- Auto-connect WebSocket on page load in service console - Settings modal for editing sgclaw_config.json (API key, base URL, model, skills dir, etc.) - UpdateConfig/ConfigUpdated protocol messages for remote config save - save_to_path() for SgClawSettings serialization - ConfigUpdated handler in sg_claw_client binary - Protocol serialization tests for new message types - HTML test assertions for auto-connect and settings UI - Additional pending changes: deterministic submit, org units, lineloss xlsx export, browser script tool, and docs 🤖 Generated with [Qoder][https://qoder.com]
3.5 KiB
Rust-Side Lineloss XLSX Export
Problem
collect_lineloss.js runs on a remote page (http://20.76.57.61:18080/gsllys).
The script successfully queries API data (12 rows), but cannot call
http://localhost:13313/.../faultDetailsExportXLSX because the browser blocks
cross-origin requests from a remote page to localhost.
The original scene architecture had a local scene page acting as a proxy, but skill mode has no local page -- so export is architecturally impossible from the browser side.
Decision
Move XLSX generation to the Rust side. JS only collects data; Rust generates
the .xlsx file locally after receiving the artifact.
Report log (setReportLog) is deferred to a later iteration.
Design
JS Changes (collect_lineloss.js)
- Remove
exportWorkbook()call andwriteReportLog()call - Return artifact with
rowsarray andcolumn_defsarray - Status is
okwhen rows > 0,emptywhen rows == 0,error/blockedunchanged
Artifact shape:
{
"type": "report-artifact",
"report_name": "tq-lineloss-report",
"status": "ok",
"org": { "label": "...", "code": "..." },
"period": { "mode": "month", "value": "2026-03" },
"column_defs": [["ORG_NAME","供电单位"], ["YGDL","累计供电量"], ...],
"rows": [
{"ORG_NAME":"xxx", "YGDL":"12345.67", ...}
],
"counts": { "rows": 12 }
}
Rust Changes
New file: src/compat/lineloss_xlsx_export.rs
Generates a standard .xlsx file using zip crate + OpenXML XML strings.
Follows the pattern established in openxml_office_tool.rs.
Public API:
pub struct LinelossExportRequest {
pub column_defs: Vec<(String, String)>, // (key, chinese_header)
pub rows: Vec<Map<String, Value>>,
pub sheet_name: String,
pub output_path: PathBuf,
}
pub fn export_lineloss_xlsx(request: &LinelossExportRequest) -> anyhow::Result<PathBuf>;
Internals:
- Build header row from
column_defs[*].1(chinese names) - Build data rows by looking up
column_defs[*].0keys in each row map - Generate
worksheet_xmlwith inline string cells - Package with standard OpenXML boilerplate (content_types, rels, workbook)
- Write to
output_path
Modified: src/compat/deterministic_submit.rs
In execute_deterministic_submit_with_browser_backend (and the non-backend variant):
let output = execute_browser_script_skill_raw_output_with_browser_backend(...)?;
let artifact = parse_lineloss_artifact(&output);
if artifact has rows > 0 && column_defs present:
let export_path = workspace_root/out/tq-lineloss-{timestamp}.xlsx
export_lineloss_xlsx(LinelossExportRequest { ... })?
// attach export_path to outcome summary
Ok(summarize_lineloss_output_with_export(&output, export_path))
Modified: src/compat/mod.rs
Add pub mod lineloss_xlsx_export;
Output Path
{workspace_root}/out/tq-lineloss-{org_label}-{period}-{timestamp_nanos}.xlsx
Error Handling
- XLSX generation failure: outcome status =
partial, reason =xlsx_export_failed - Artifact parse failure: fall through to existing
summarize_lineloss_output
Files Changed
| File | Change Type |
|---|---|
collect_lineloss.js |
Modify: remove export/log calls, add rows+column_defs to artifact |
src/compat/lineloss_xlsx_export.rs |
New: XLSX generation |
src/compat/deterministic_submit.rs |
Modify: post-process artifact, call XLSX export |
src/compat/mod.rs |
Modify: register new module |
Requires Recompilation
Yes. Rust code changes require cargo build.