onbuzz 4.9.13 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node_modules/glob/README.md +31 -5
- package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
- package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
- package/node_modules/glob/dist/commonjs/glob.js +2 -1
- package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
- package/node_modules/glob/dist/commonjs/index.min.js +3 -3
- package/node_modules/glob/dist/commonjs/index.min.js.map +4 -4
- package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
- package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
- package/node_modules/glob/dist/commonjs/pattern.js +4 -0
- package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
- package/node_modules/glob/dist/esm/glob.d.ts +8 -0
- package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
- package/node_modules/glob/dist/esm/glob.js +2 -1
- package/node_modules/glob/dist/esm/glob.js.map +1 -1
- package/node_modules/glob/dist/esm/index.min.js +3 -3
- package/node_modules/glob/dist/esm/index.min.js.map +4 -4
- package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
- package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
- package/node_modules/glob/dist/esm/pattern.js +4 -0
- package/node_modules/glob/dist/esm/pattern.js.map +1 -1
- package/node_modules/{@isaacs → glob/node_modules}/balanced-match/README.md +7 -10
- package/node_modules/{@isaacs → glob/node_modules}/balanced-match/package.json +7 -18
- package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/README.md +3 -6
- package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.js +6 -4
- package/node_modules/glob/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -0
- package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.js +6 -4
- package/node_modules/glob/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
- package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/package.json +11 -7
- package/node_modules/glob/node_modules/minimatch/README.md +76 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +4 -2
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +309 -55
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +4 -4
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +81 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +232 -134
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +8 -8
- package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +4 -2
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +309 -55
- package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
- package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +4 -4
- package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +81 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +232 -134
- package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -1
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +8 -8
- package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
- package/node_modules/glob/node_modules/minimatch/package.json +17 -11
- package/node_modules/glob/package.json +10 -13
- package/node_modules/minipass/LICENSE.md +55 -0
- package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
- package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/minipass/dist/commonjs/index.js +13 -3
- package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
- package/node_modules/minipass/dist/esm/index.d.ts +12 -16
- package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
- package/node_modules/minipass/dist/esm/index.js +3 -1
- package/node_modules/minipass/dist/esm/index.js.map +1 -1
- package/node_modules/minipass/package.json +9 -14
- package/node_modules/path-scurry/node_modules/lru-cache/README.md +96 -10
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts +1400 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.cjs.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +109 -32
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +334 -197
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +4 -4
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts +1400 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts +1400 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.mjs.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +109 -32
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +333 -196
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +1 -1
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +4 -4
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts +1400 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js.map +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts +12 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
- package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js.map +1 -0
- package/node_modules/path-scurry/node_modules/lru-cache/package.json +71 -18
- package/node_modules/path-scurry/package.json +8 -24
- package/package.json +1 -1
- package/scripts/debug-balance-probe.mjs +35 -35
- package/scripts/push-image.sh +43 -43
- package/scripts/setup-acr.sh +65 -65
- package/scripts/verify-optional-deps.js +96 -1
- package/src/__tests__/composioCliFlags.test.js +239 -239
- package/src/analyzers/CSSAnalyzer.js +298 -297
- package/src/analyzers/ConfigValidator.js +691 -690
- package/src/analyzers/ESLintAnalyzer.js +320 -320
- package/src/analyzers/JavaScriptAnalyzer.js +260 -261
- package/src/analyzers/PrettierFormatter.js +246 -247
- package/src/analyzers/PythonAnalyzer.js +283 -283
- package/src/analyzers/SecurityAnalyzer.js +729 -729
- package/src/analyzers/SparrowAnalyzer.js +341 -341
- package/src/analyzers/TypeScriptAnalyzer.js +247 -247
- package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -41
- package/src/analyzers/__tests__/ConfigValidator.test.js +362 -362
- package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -40
- package/src/analyzers/__tests__/PythonAnalyzer.test.js +205 -208
- package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -303
- package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -187
- package/src/analyzers/codeCloneDetector/analyzer.js +344 -344
- package/src/analyzers/codeCloneDetector/detector.js +250 -250
- package/src/analyzers/codeCloneDetector/index.js +194 -192
- package/src/analyzers/codeCloneDetector/parser.js +199 -199
- package/src/core/__tests__/agentPool.test.js +866 -866
- package/src/core/__tests__/agentPoolAutoResume.test.js +209 -209
- package/src/core/__tests__/agentPoolWakeOnMessage.test.js +315 -315
- package/src/core/__tests__/agentScheduler.emptyResponseChatStall.test.js +213 -213
- package/src/core/__tests__/agentScheduler.errorCategorisation.test.js +246 -246
- package/src/core/__tests__/agentScheduler.firstChunkTimeout.test.js +138 -138
- package/src/core/__tests__/agentScheduler.modeTransitions.test.js +233 -233
- package/src/core/__tests__/agentScheduler.nativePromptPick.test.js +319 -319
- package/src/core/__tests__/agentScheduler.taskLifecycleInstruction.test.js +78 -78
- package/src/core/__tests__/agentScheduler.visualizer.test.js +258 -258
- package/src/core/__tests__/flowCheckpointStore.test.js +140 -140
- package/src/core/__tests__/flowEndToEnd.test.js +565 -565
- package/src/core/__tests__/flowFieldMapping.test.js +188 -189
- package/src/core/__tests__/flowLintClientMirror.test.js +96 -98
- package/src/core/__tests__/flowSavePayload.test.js +170 -169
- package/src/core/__tests__/flowTemplates.test.js +311 -311
- package/src/core/__tests__/flowVersionStore.test.js +123 -123
- package/src/core/__tests__/messageProcessor.test.js +669 -669
- package/src/core/__tests__/stateManager.test.js +0 -1
- package/src/core/agentPool.js +2474 -2475
- package/src/core/agentScheduler.js +1 -4
- package/src/core/contextManager.js +708 -708
- package/src/core/flowExecutor.js +1510 -1510
- package/src/core/flowFieldMapping.js +136 -138
- package/src/core/messageProcessor.js +953 -954
- package/src/core/orchestrator.js +593 -595
- package/src/core/stateManager.js +1765 -1752
- package/src/index.js +1221 -1221
- package/src/interfaces/__tests__/archivedAgentDelete.test.js +207 -207
- package/src/interfaces/__tests__/bulkAgentRoute.test.js +361 -361
- package/src/interfaces/__tests__/imageServing.test.js +228 -228
- package/src/interfaces/__tests__/remoteSessionAuth.test.js +308 -308
- package/src/interfaces/__tests__/videoJobsRoutes.test.js +178 -179
- package/src/interfaces/__tests__/webServer.marketplace.test.js +629 -629
- package/src/interfaces/schedulerRoutes.js +50 -50
- package/src/interfaces/terminal/__tests__/smoke/connection.test.js +341 -350
- package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -156
- package/src/interfaces/terminal/__tests__/smoke/imports.test.js +325 -330
- package/src/interfaces/terminal/__tests__/smoke/tools.test.js +385 -388
- package/src/interfaces/terminal/api/session.js +265 -266
- package/src/interfaces/terminal/api/websocket.js +496 -497
- package/src/interfaces/terminal/components/AgentCreator.js +691 -705
- package/src/interfaces/terminal/components/AgentEditor.js +676 -678
- package/src/interfaces/terminal/components/AgentSwitcher.js +331 -330
- package/src/interfaces/terminal/components/ErrorPanel.js +263 -264
- package/src/interfaces/terminal/components/Header.js +28 -28
- package/src/interfaces/terminal/components/Layout.js +598 -603
- package/src/interfaces/terminal/components/MessageList.js +280 -281
- package/src/interfaces/terminal/components/SettingsPanel.js +410 -415
- package/src/interfaces/terminal/components/StatusBar.js +2 -0
- package/src/interfaces/terminal/index.js +168 -168
- package/src/interfaces/terminal/state/useAgentControl.js +496 -496
- package/src/interfaces/terminal/state/useAgents.js +537 -537
- package/src/interfaces/terminal/state/useMessages.js +629 -630
- package/src/interfaces/terminal/state/useTools.js +554 -554
- package/src/interfaces/terminal/utils/debugLogger.js +44 -44
- package/src/interfaces/terminal/utils/settingsStorage.js +232 -232
- package/src/interfaces/webServer.js +7578 -7579
- package/src/interfaces/webServer.js.bak +7046 -7046
- package/src/modules/fileExplorer/__tests__/zipDownload.test.js +237 -237
- package/src/modules/fileExplorer/controller.js +470 -469
- package/src/modules/fileExplorer/routes.js +285 -286
- package/src/modules/widget/__tests__/isDisabled.test.js +41 -41
- package/src/modules/widget/__tests__/routes.test.js +677 -678
- package/src/modules/widget/__tests__/runtime.test.js +401 -401
- package/src/modules/widget/__tests__/versioning.test.js +309 -309
- package/src/modules/widget/__tests__/webComponentRuntime.test.js +565 -565
- package/src/modules/widget/__tests__/widgetTool.test.js +316 -316
- package/src/modules/widget/routes.js +435 -435
- package/src/modules/widget/runtime/bundle.js +640 -640
- package/src/modules/widget/runtime/webComponentBundle.js +470 -470
- package/src/modules/widget/schema.js +182 -181
- package/src/modules/widget/widgetTool.js +1389 -1389
- package/src/services/__tests__/agentActivityService.test.js +401 -402
- package/src/services/__tests__/benchmarkService.test.js +184 -184
- package/src/services/__tests__/contextInjectionService.test.js +246 -246
- package/src/services/__tests__/conversationQuery.test.js +721 -723
- package/src/services/__tests__/credentialVault.test.js +469 -469
- package/src/services/__tests__/discordService.integration.test.js +638 -639
- package/src/services/__tests__/flowContextService.test.js +590 -590
- package/src/services/__tests__/memoryService.test.js +1 -1
- package/src/services/__tests__/messageSource.test.js +380 -380
- package/src/services/__tests__/modelRouterNaming.test.js +111 -111
- package/src/services/__tests__/projectDetector.test.js +34 -34
- package/src/services/__tests__/promptService.test.js +242 -242
- package/src/services/__tests__/telegramService.test.js +941 -941
- package/src/services/__tests__/tokenCountingService.test.js +48 -48
- package/src/services/agentActivityService.js +419 -420
- package/src/services/aiService.js +2997 -3001
- package/src/services/apiKeyManager.js +359 -359
- package/src/services/benchmarkService.js +196 -196
- package/src/services/codebaseKnowledgeService.js +2 -2
- package/src/services/composioService.js +738 -738
- package/src/services/conversationCompactionService.js +1258 -1257
- package/src/services/credentialVault.js +685 -685
- package/src/services/discordService.js +792 -793
- package/src/services/embeddings/__tests__/azureCustomProvider.test.js +232 -232
- package/src/services/embeddings/__tests__/embeddingService.test.js +417 -417
- package/src/services/embeddings/__tests__/localProvider.test.js +263 -263
- package/src/services/embeddings/autoRecall.js +218 -219
- package/src/services/embeddings/indexers/__tests__/agentIndexer.test.js +232 -232
- package/src/services/embeddings/indexers/__tests__/memoryIndexer.test.js +418 -418
- package/src/services/embeddings/indexers/__tests__/reminisceIndexer.test.js +356 -357
- package/src/services/embeddings/indexers/__tests__/skillsIndexer.test.js +145 -145
- package/src/services/embeddings/indexers/__tests__/taskIndexer.test.js +146 -146
- package/src/services/embeddings/indexers/composioIndexer.js +279 -279
- package/src/services/embeddings/providerInterface.js +206 -206
- package/src/services/embeddings/providers/localProvider.js +11 -7
- package/src/services/embeddings/providers/openaiProvider.js +101 -101
- package/src/services/embeddings/vectorStore/inMemoryJsonStore.js +356 -356
- package/src/services/errorHandler.js +809 -809
- package/src/services/flowContextService.js +586 -586
- package/src/services/grounding/MockAdapter.js +125 -125
- package/src/services/modelRouterService.js +26 -31
- package/src/services/modelsService.js +322 -322
- package/src/services/ollamaService.js +452 -452
- package/src/services/projectDetector.js +403 -404
- package/src/services/promptService.js +418 -418
- package/src/services/qualityInspector.js +795 -795
- package/src/services/scheduleService.js +726 -726
- package/src/services/serviceRegistry.js +386 -386
- package/src/services/telegrafBot.js +174 -174
- package/src/services/telegramService.js +1972 -1972
- package/src/services/visualEditorBridge.js +1033 -1033
- package/src/services/visualEditorServer.js +1769 -1774
- package/src/services/whatsappService.js +667 -668
- package/src/tools/__tests__/agentCommunicationTool.findAgent.test.js +226 -226
- package/src/tools/__tests__/agentCommunicationTool.test.js +3 -3
- package/src/tools/__tests__/agentDelayTool.test.js +342 -342
- package/src/tools/__tests__/baseTool.test.js +3 -3
- package/src/tools/__tests__/codeMapTool.test.js +915 -915
- package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -309
- package/src/tools/__tests__/fileTreeTool.test.js +274 -274
- package/src/tools/__tests__/filesystemTool.test.js +815 -815
- package/src/tools/__tests__/foundryWebSearchTool.test.js +252 -252
- package/src/tools/__tests__/imageTool.validator.test.js +194 -194
- package/src/tools/__tests__/jobDoneTool.test.js +580 -581
- package/src/tools/__tests__/memoryTool.forgetStale.test.js +272 -272
- package/src/tools/__tests__/memoryTool.reminisce.test.js +2 -2
- package/src/tools/__tests__/memoryTool.reminisceSemanticSearch.test.js +301 -301
- package/src/tools/__tests__/memoryTool.semanticSearch.test.js +405 -405
- package/src/tools/__tests__/memoryTool.teamPool.test.js +293 -293
- package/src/tools/__tests__/memoryTool.test.js +1 -1
- package/src/tools/__tests__/seekTool.test.js +282 -282
- package/src/tools/__tests__/skillsTool.search.test.js +164 -164
- package/src/tools/__tests__/skillsTool.test.js +226 -226
- package/src/tools/__tests__/staticAnalysisTool.test.js +509 -509
- package/src/tools/__tests__/taskManagerTool.discipline.test.js +137 -137
- package/src/tools/__tests__/taskManagerTool.search.test.js +143 -143
- package/src/tools/__tests__/taskManagerTool.test.js +866 -866
- package/src/tools/__tests__/terminalTool.test.js +448 -448
- package/src/tools/__tests__/toolShapeForgiveness.test.js +259 -260
- package/src/tools/__tests__/userPromptTool.test.js +297 -297
- package/src/tools/__tests__/videoTool.jobs.test.js +147 -147
- package/src/tools/__tests__/webTool.e2e.test.js +609 -603
- package/src/tools/__tests__/webTool.unit.test.js +195 -195
- package/src/tools/__tests__/webTool.visionModel.test.js +75 -75
- package/src/tools/agentCommunicationTool.js +8 -10
- package/src/tools/agentDelayTool.js +496 -497
- package/src/tools/asyncToolManager.js +602 -603
- package/src/tools/baseTool.js +12 -11
- package/src/tools/cloneDetectionTool.js +576 -581
- package/src/tools/codeMapTool.js +0 -6
- package/src/tools/composioTool.js +617 -617
- package/src/tools/dependencyResolverTool.js +1211 -1212
- package/src/tools/desktop/DesktopTool.js +629 -638
- package/src/tools/desktop/__tests__/DesktopTool.e2e.test.js +306 -306
- package/src/tools/desktop/__tests__/DesktopTool.test.js +507 -507
- package/src/tools/desktop/__tests__/osController.test.js +364 -364
- package/src/tools/desktop/osController.js +491 -491
- package/src/tools/docxTool.js +623 -623
- package/src/tools/excelTool.js +636 -636
- package/src/tools/fileContentReplaceTool.js +5 -7
- package/src/tools/fileSystemTool.js +12 -19
- package/src/tools/fileTreeTool.js +840 -840
- package/src/tools/foundryWebSearchTool.js +273 -273
- package/src/tools/helpTool.js +198 -198
- package/src/tools/imageTool.js +1397 -1397
- package/src/tools/importAnalyzerTool.js +1056 -1056
- package/src/tools/jobDoneTool.js +495 -495
- package/src/tools/memoryTool.js +1 -1
- package/src/tools/office/pres/__tests__/presSystem.test.js +365 -365
- package/src/tools/office/pres/archetypes/agenda.js +61 -61
- package/src/tools/office/pres/archetypes/bentoGrid.js +218 -219
- package/src/tools/office/pres/archetypes/bigStat.js +140 -142
- package/src/tools/office/pres/archetypes/closing.js +70 -70
- package/src/tools/office/pres/archetypes/hero.js +70 -70
- package/src/tools/office/pres/archetypes/productHero.js +93 -94
- package/src/tools/office/pres/archetypes/table.js +73 -74
- package/src/tools/office/pres/backgrounds/orb.js +66 -66
- package/src/tools/office/pres/components.js +422 -423
- package/src/tools/officeTool.js +441 -441
- package/src/tools/pdfTool.js +625 -627
- package/src/tools/platformControlTool.js +1081 -1081
- package/src/tools/seekTool.js +917 -918
- package/src/tools/skillsTool.js +1 -1
- package/src/tools/staticAnalysisTool.js +2143 -2146
- package/src/tools/taskManagerTool.js +3324 -3324
- package/src/tools/terminalTool.js +2615 -2618
- package/src/tools/videoTool.js +1303 -1303
- package/src/tools/visionTool.js +508 -508
- package/src/tools/visualEditorTool.js +1289 -1290
- package/src/tools/webTool.js +3368 -3368
- package/src/tools/whatsappTool.js +464 -464
- package/src/types/__tests__/agent.test.js +499 -499
- package/src/types/__tests__/contextReference.test.js +606 -606
- package/src/types/__tests__/conversation.test.js +555 -555
- package/src/types/__tests__/toolCommand.test.js +584 -584
- package/src/types/contextReference.js +974 -971
- package/src/types/conversation.js +729 -729
- package/src/types/toolCommand.js +746 -746
- package/src/utilities/__tests__/attachmentValidator.test.js +80 -80
- package/src/utilities/__tests__/auditReport.test.js +328 -328
- package/src/utilities/__tests__/directoryAccessManager.test.js +388 -388
- package/src/utilities/__tests__/jsonRepair.test.js +103 -104
- package/src/utilities/__tests__/modeTransitionReasons.test.js +105 -105
- package/src/utilities/__tests__/platformUtils.test.js +80 -87
- package/src/utilities/__tests__/structuredFileValidator.test.js +261 -263
- package/src/utilities/__tests__/toolConstants.test.js +92 -94
- package/src/utilities/__tests__/useIsTouchDevice.detect.test.js +114 -114
- package/src/utilities/__tests__/webUiUtilSync.test.js +117 -117
- package/src/utilities/attachmentValidator.js +284 -288
- package/src/utilities/authCache.js.backup-1779570472481 +121 -121
- package/src/utilities/browserStealth.js +631 -630
- package/src/utilities/configManager.js +616 -617
- package/src/utilities/directoryAccessManager.js +564 -565
- package/src/utilities/fileProcessor.js +308 -307
- package/src/utilities/humanBehavior.js +454 -453
- package/src/utilities/logger.js +479 -479
- package/src/utilities/structuredFileValidator.js +696 -699
- package/src/utilities/tagParser.js +5 -10
- package/src/utilities/userDataDir.js +308 -308
- package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +0 -1
- package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +0 -1
- package/node_modules/minipass/LICENSE +0 -15
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/LICENSE.md +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/package.json +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/package.json +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/LICENSE +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/package.json +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/package.json +0 -0
package/src/tools/excelTool.js
CHANGED
|
@@ -1,636 +1,636 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spreadsheet (Excel) Tool - Read and create Excel files
|
|
3
|
-
*
|
|
4
|
-
* Purpose:
|
|
5
|
-
* - Get spreadsheet metadata (sheet names, row/column counts)
|
|
6
|
-
* - Read data from specific sheets and ranges
|
|
7
|
-
* - Create Excel workbooks with formatting, formulas, and styling
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { BaseTool } from './baseTool.js';
|
|
11
|
-
import TagParser from '../utilities/tagParser.js';
|
|
12
|
-
import fs from 'fs/promises';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
|
|
15
|
-
// Lazy-loaded dependency
|
|
16
|
-
let ExcelJS = null;
|
|
17
|
-
|
|
18
|
-
class ExcelTool extends BaseTool {
|
|
19
|
-
constructor(config = {}, logger = null) {
|
|
20
|
-
super(config, logger);
|
|
21
|
-
this.id = 'spreadsheet';
|
|
22
|
-
this.name = 'Spreadsheet Tool';
|
|
23
|
-
this.description = 'Read and create Excel (XLSX) spreadsheets';
|
|
24
|
-
this.version = '1.0.0';
|
|
25
|
-
this.requiresProject = false;
|
|
26
|
-
this.isAsync = false;
|
|
27
|
-
this.excelLoaded = false;
|
|
28
|
-
this.excelError = null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Lazily load ExcelJS module
|
|
33
|
-
* @returns {Promise<boolean>}
|
|
34
|
-
*/
|
|
35
|
-
async loadExcelJS() {
|
|
36
|
-
if (this.excelLoaded) return true;
|
|
37
|
-
if (this.excelError) return false;
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const mod = await import('exceljs');
|
|
41
|
-
ExcelJS = mod.default || mod;
|
|
42
|
-
this.excelLoaded = true;
|
|
43
|
-
return true;
|
|
44
|
-
} catch (error) {
|
|
45
|
-
this.excelError = error.message;
|
|
46
|
-
this.logger?.error('Failed to load exceljs module', { error: error.message });
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Get tool description for LLM consumption
|
|
53
|
-
* @returns {string}
|
|
54
|
-
*/
|
|
55
|
-
getDescription() {
|
|
56
|
-
return `
|
|
57
|
-
Spreadsheet Tool: Read and create Excel (XLSX) spreadsheets.
|
|
58
|
-
|
|
59
|
-
USAGE:
|
|
60
|
-
\`\`\`json
|
|
61
|
-
{
|
|
62
|
-
"toolId": "spreadsheet",
|
|
63
|
-
"actions": [{
|
|
64
|
-
"action": "get-info",
|
|
65
|
-
"filePath": "data/report.xlsx"
|
|
66
|
-
}]
|
|
67
|
-
}
|
|
68
|
-
\`\`\`
|
|
69
|
-
|
|
70
|
-
ACTIONS:
|
|
71
|
-
|
|
72
|
-
1. **get-info** - Get spreadsheet metadata
|
|
73
|
-
- filePath: Path to Excel file (required)
|
|
74
|
-
- Returns: sheet names, row counts, column counts per sheet
|
|
75
|
-
|
|
76
|
-
2. **read** - Read data from a sheet
|
|
77
|
-
- filePath: Path to Excel file (required)
|
|
78
|
-
- sheetName: Sheet name (optional, defaults to first sheet)
|
|
79
|
-
- startRow: Start row number, 1-indexed (optional, default: 1)
|
|
80
|
-
- endRow: End row number, inclusive (optional, default: all rows)
|
|
81
|
-
- includeFormulas: Return formulas instead of values (optional, default: false)
|
|
82
|
-
|
|
83
|
-
3. **create** - Create a new Excel workbook
|
|
84
|
-
- outputPath: Output file path (required)
|
|
85
|
-
- content: Workbook content object (required):
|
|
86
|
-
- sheets: Array of sheet definitions:
|
|
87
|
-
- name: Sheet name
|
|
88
|
-
- columns: [{ header: "Name", key: "name", width: 20 }]
|
|
89
|
-
- rows: [{ name: "John", age: 30 }] or [["John", 30]]
|
|
90
|
-
- headerStyle: { bold: true, fill: "#4472C4", fontColor: "#FFFFFF" }
|
|
91
|
-
- freezeRow: Freeze panes at this row (optional)
|
|
92
|
-
- autoFilter: Enable auto-filter on headers (optional, default: false)
|
|
93
|
-
- formulas: [{ cell: "C2", formula: "=A2+B2" }]
|
|
94
|
-
|
|
95
|
-
EXAMPLES:
|
|
96
|
-
|
|
97
|
-
1. Get spreadsheet info:
|
|
98
|
-
\`\`\`json
|
|
99
|
-
{
|
|
100
|
-
"toolId": "spreadsheet",
|
|
101
|
-
"actions": [{
|
|
102
|
-
"action": "get-info",
|
|
103
|
-
"filePath": "data/sales.xlsx"
|
|
104
|
-
}]
|
|
105
|
-
}
|
|
106
|
-
\`\`\`
|
|
107
|
-
|
|
108
|
-
2. Read data from a specific sheet:
|
|
109
|
-
\`\`\`json
|
|
110
|
-
{
|
|
111
|
-
"toolId": "spreadsheet",
|
|
112
|
-
"actions": [{
|
|
113
|
-
"action": "read",
|
|
114
|
-
"filePath": "data/sales.xlsx",
|
|
115
|
-
"sheetName": "Q1 Sales",
|
|
116
|
-
"startRow": 1,
|
|
117
|
-
"endRow": 50
|
|
118
|
-
}]
|
|
119
|
-
}
|
|
120
|
-
\`\`\`
|
|
121
|
-
|
|
122
|
-
3. Create a spreadsheet with formatting:
|
|
123
|
-
\`\`\`json
|
|
124
|
-
{
|
|
125
|
-
"toolId": "spreadsheet",
|
|
126
|
-
"actions": [{
|
|
127
|
-
"action": "create",
|
|
128
|
-
"outputPath": "output/report.xlsx",
|
|
129
|
-
"content": {
|
|
130
|
-
"sheets": [{
|
|
131
|
-
"name": "Sales Data",
|
|
132
|
-
"columns": [
|
|
133
|
-
{ "header": "Product", "key": "product", "width": 25 },
|
|
134
|
-
{ "header": "Quantity", "key": "qty", "width": 12 },
|
|
135
|
-
{ "header": "Price", "key": "price", "width": 12 },
|
|
136
|
-
{ "header": "Total", "key": "total", "width": 15 }
|
|
137
|
-
],
|
|
138
|
-
"rows": [
|
|
139
|
-
{ "product": "Widget A", "qty": 100, "price": 9.99 },
|
|
140
|
-
{ "product": "Widget B", "qty": 50, "price": 19.99 }
|
|
141
|
-
],
|
|
142
|
-
"headerStyle": { "bold": true, "fill": "#4472C4", "fontColor": "#FFFFFF" },
|
|
143
|
-
"freezeRow": 1,
|
|
144
|
-
"autoFilter": true,
|
|
145
|
-
"formulas": [
|
|
146
|
-
{ "cell": "D2", "formula": "=B2*C2" },
|
|
147
|
-
{ "cell": "D3", "formula": "=B3*C3" }
|
|
148
|
-
]
|
|
149
|
-
}]
|
|
150
|
-
}
|
|
151
|
-
}]
|
|
152
|
-
}
|
|
153
|
-
\`\`\`
|
|
154
|
-
|
|
155
|
-
NOTES:
|
|
156
|
-
- Row numbers are 1-indexed (first row is 1, typically the header)
|
|
157
|
-
- Values starting with "=" in rows are treated as formulas
|
|
158
|
-
- Column widths are in approximate character widths
|
|
159
|
-
- Fill colors should be hex codes without # (or with #, both are accepted)
|
|
160
|
-
- Multiple sheets can be created in a single workbook
|
|
161
|
-
`.trim();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Parse parameters from tool command content
|
|
166
|
-
* @param {string} content
|
|
167
|
-
* @returns {Object}
|
|
168
|
-
*/
|
|
169
|
-
parseParameters(content) {
|
|
170
|
-
try {
|
|
171
|
-
const actionMatches = TagParser.extractContent(content, 'action');
|
|
172
|
-
const filePathMatches = TagParser.extractContent(content, 'filePath');
|
|
173
|
-
const sheetNameMatches = TagParser.extractContent(content, 'sheetName');
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
actions: [{
|
|
177
|
-
action: actionMatches.length > 0 ? actionMatches[0].trim() : 'get-info',
|
|
178
|
-
filePath: filePathMatches.length > 0 ? filePathMatches[0].trim() : '',
|
|
179
|
-
sheetName: sheetNameMatches.length > 0 ? sheetNameMatches[0].trim() : undefined
|
|
180
|
-
}]
|
|
181
|
-
};
|
|
182
|
-
} catch (error) {
|
|
183
|
-
throw new Error(`Failed to parse Spreadsheet tool parameters: ${error.message}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Get supported actions
|
|
189
|
-
* @returns {Array<string>}
|
|
190
|
-
*/
|
|
191
|
-
getSupportedActions() {
|
|
192
|
-
return ['get-info', 'read', 'create'];
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Execute spreadsheet tool action
|
|
197
|
-
* @param {Object} params
|
|
198
|
-
* @param {Object} context
|
|
199
|
-
* @returns {Promise<Object>}
|
|
200
|
-
*/
|
|
201
|
-
async execute(params, context) {
|
|
202
|
-
const { actions } = params;
|
|
203
|
-
|
|
204
|
-
if (!actions || actions.length === 0) {
|
|
205
|
-
return {
|
|
206
|
-
success: false,
|
|
207
|
-
error: 'No actions provided',
|
|
208
|
-
output: 'Please specify an action (get-info, read, or create)'
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const action = actions[0];
|
|
213
|
-
|
|
214
|
-
// Handle create action (no existing file required)
|
|
215
|
-
if (action.action === 'create') {
|
|
216
|
-
try {
|
|
217
|
-
return await this.createSpreadsheet(action, context);
|
|
218
|
-
} catch (error) {
|
|
219
|
-
this.logger?.error('Excel creation error', { error: error.message });
|
|
220
|
-
return {
|
|
221
|
-
success: false,
|
|
222
|
-
error: error.message,
|
|
223
|
-
output: `Failed to create spreadsheet: ${error.message}`
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Read-oriented actions require file path
|
|
229
|
-
const { projectDir } = context;
|
|
230
|
-
let filePath = action.filePath;
|
|
231
|
-
|
|
232
|
-
if (!filePath) {
|
|
233
|
-
return {
|
|
234
|
-
success: false,
|
|
235
|
-
error: 'File path is required',
|
|
236
|
-
output: 'Please provide a filePath parameter'
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (!path.isAbsolute(filePath)) {
|
|
241
|
-
filePath = path.resolve(projectDir || process.cwd(), filePath);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Check file exists
|
|
245
|
-
try {
|
|
246
|
-
await fs.access(filePath);
|
|
247
|
-
} catch {
|
|
248
|
-
return {
|
|
249
|
-
success: false,
|
|
250
|
-
error: `File not found: ${filePath}`,
|
|
251
|
-
output: `The Excel file does not exist: ${filePath}`
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Check extension
|
|
256
|
-
const ext = filePath.toLowerCase();
|
|
257
|
-
if (!ext.endsWith('.xlsx') && !ext.endsWith('.xls') && !ext.endsWith('.csv')) {
|
|
258
|
-
return {
|
|
259
|
-
success: false,
|
|
260
|
-
error: 'Not a spreadsheet file',
|
|
261
|
-
output: `The file must have a .xlsx, .xls, or .csv extension: ${filePath}`
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Load ExcelJS
|
|
266
|
-
const loaded = await this.loadExcelJS();
|
|
267
|
-
if (!loaded) {
|
|
268
|
-
return {
|
|
269
|
-
success: false,
|
|
270
|
-
error: 'ExcelJS module not available',
|
|
271
|
-
output: `Spreadsheet module could not be loaded: ${this.excelError}`
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
try {
|
|
276
|
-
switch (action.action) {
|
|
277
|
-
case 'get-info':
|
|
278
|
-
return await this.getInfo(filePath);
|
|
279
|
-
case 'read':
|
|
280
|
-
return await this.readSheet(filePath, action);
|
|
281
|
-
default:
|
|
282
|
-
return {
|
|
283
|
-
success: false,
|
|
284
|
-
error: `Unknown action: ${action.action}`,
|
|
285
|
-
output: 'Supported actions: get-info, read, create'
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
} catch (error) {
|
|
289
|
-
this.logger?.error('Excel tool error', { action: action.action, filePath, error: error.message });
|
|
290
|
-
return {
|
|
291
|
-
success: false,
|
|
292
|
-
error: error.message,
|
|
293
|
-
output: `Failed to process spreadsheet: ${error.message}`
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Get spreadsheet info
|
|
300
|
-
* @param {string} filePath
|
|
301
|
-
* @returns {Promise<Object>}
|
|
302
|
-
*/
|
|
303
|
-
async getInfo(filePath) {
|
|
304
|
-
const workbook = new ExcelJS.Workbook();
|
|
305
|
-
|
|
306
|
-
if (filePath.toLowerCase().endsWith('.csv')) {
|
|
307
|
-
await workbook.csv.readFile(filePath);
|
|
308
|
-
} else {
|
|
309
|
-
await workbook.xlsx.readFile(filePath);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const sheets = [];
|
|
313
|
-
workbook.eachSheet((worksheet) => {
|
|
314
|
-
sheets.push({
|
|
315
|
-
name: worksheet.name,
|
|
316
|
-
rowCount: worksheet.rowCount,
|
|
317
|
-
columnCount: worksheet.columnCount,
|
|
318
|
-
actualRowCount: worksheet.actualRowCount
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
let output = `Spreadsheet Info: ${path.basename(filePath)}\n`;
|
|
323
|
-
output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
324
|
-
output += `Sheets: ${sheets.length}\n\n`;
|
|
325
|
-
|
|
326
|
-
for (const sheet of sheets) {
|
|
327
|
-
output += `📊 ${sheet.name}\n`;
|
|
328
|
-
output += ` Rows: ${sheet.actualRowCount || sheet.rowCount}\n`;
|
|
329
|
-
output += ` Columns: ${sheet.columnCount}\n\n`;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
success: true,
|
|
334
|
-
action: 'get-info',
|
|
335
|
-
filePath,
|
|
336
|
-
sheets,
|
|
337
|
-
output,
|
|
338
|
-
message: `Spreadsheet has ${sheets.length} sheet(s)`
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Read data from a sheet
|
|
344
|
-
* @param {string} filePath
|
|
345
|
-
* @param {Object} action - Action parameters
|
|
346
|
-
* @returns {Promise<Object>}
|
|
347
|
-
*/
|
|
348
|
-
async readSheet(filePath, action) {
|
|
349
|
-
const { sheetName, startRow = 1, endRow, includeFormulas = false } = action;
|
|
350
|
-
|
|
351
|
-
const workbook = new ExcelJS.Workbook();
|
|
352
|
-
|
|
353
|
-
if (filePath.toLowerCase().endsWith('.csv')) {
|
|
354
|
-
await workbook.csv.readFile(filePath);
|
|
355
|
-
} else {
|
|
356
|
-
await workbook.xlsx.readFile(filePath);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Get the requested sheet
|
|
360
|
-
let worksheet;
|
|
361
|
-
if (sheetName) {
|
|
362
|
-
worksheet = workbook.getWorksheet(sheetName);
|
|
363
|
-
if (!worksheet) {
|
|
364
|
-
const available = [];
|
|
365
|
-
workbook.eachSheet(ws => available.push(ws.name));
|
|
366
|
-
return {
|
|
367
|
-
success: false,
|
|
368
|
-
error: `Sheet not found: ${sheetName}`,
|
|
369
|
-
output: `Sheet "${sheetName}" not found. Available sheets: ${available.join(', ')}`
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
} else {
|
|
373
|
-
worksheet = workbook.worksheets[0];
|
|
374
|
-
if (!worksheet) {
|
|
375
|
-
return {
|
|
376
|
-
success: false,
|
|
377
|
-
error: 'No sheets in workbook',
|
|
378
|
-
output: 'The workbook has no sheets'
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
const totalRows = worksheet.actualRowCount || worksheet.rowCount;
|
|
384
|
-
const effectiveEnd = endRow ? Math.min(endRow, totalRows) : totalRows;
|
|
385
|
-
const effectiveStart = Math.max(1, startRow);
|
|
386
|
-
|
|
387
|
-
const rows = [];
|
|
388
|
-
for (let r = effectiveStart; r <= effectiveEnd; r++) {
|
|
389
|
-
const row = worksheet.getRow(r);
|
|
390
|
-
const rowData = [];
|
|
391
|
-
|
|
392
|
-
for (let c = 1; c <= worksheet.columnCount; c++) {
|
|
393
|
-
const cell = row.getCell(c);
|
|
394
|
-
if (includeFormulas && cell.formula) {
|
|
395
|
-
rowData.push(`=${cell.formula}`);
|
|
396
|
-
} else {
|
|
397
|
-
rowData.push(cell.value !== null && cell.value !== undefined ? cell.value : '');
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
rows.push(rowData);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Build output
|
|
405
|
-
let output = `Sheet: ${worksheet.name} (rows ${effectiveStart}-${effectiveEnd} of ${totalRows})\n`;
|
|
406
|
-
output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
|
|
407
|
-
|
|
408
|
-
// Format as text table
|
|
409
|
-
for (let i = 0; i < rows.length; i++) {
|
|
410
|
-
const rowNum = effectiveStart + i;
|
|
411
|
-
const values = rows[i].map(v => {
|
|
412
|
-
if (v === null || v === undefined) return '';
|
|
413
|
-
if (typeof v === 'object' && v.result !== undefined) return String(v.result);
|
|
414
|
-
return String(v);
|
|
415
|
-
});
|
|
416
|
-
output += `Row ${rowNum}: ${values.join(' | ')}\n`;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
return {
|
|
420
|
-
success: true,
|
|
421
|
-
action: 'read',
|
|
422
|
-
filePath,
|
|
423
|
-
sheetName: worksheet.name,
|
|
424
|
-
startRow: effectiveStart,
|
|
425
|
-
endRow: effectiveEnd,
|
|
426
|
-
totalRows,
|
|
427
|
-
rowCount: rows.length,
|
|
428
|
-
rows,
|
|
429
|
-
output,
|
|
430
|
-
message: `Read ${rows.length} rows from "${worksheet.name}"`
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Create an Excel workbook
|
|
436
|
-
* @param {Object} action - Action parameters
|
|
437
|
-
* @param {Object} context - Execution context
|
|
438
|
-
* @returns {Promise<Object>}
|
|
439
|
-
*/
|
|
440
|
-
async createSpreadsheet(action, context) {
|
|
441
|
-
const { projectDir } = context;
|
|
442
|
-
const { outputPath, content } = action;
|
|
443
|
-
|
|
444
|
-
if (!outputPath) {
|
|
445
|
-
return {
|
|
446
|
-
success: false,
|
|
447
|
-
error: 'Output path is required',
|
|
448
|
-
output: 'Please provide an outputPath parameter'
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (!content || !content.sheets || !Array.isArray(content.sheets)) {
|
|
453
|
-
return {
|
|
454
|
-
success: false,
|
|
455
|
-
error: 'Content with sheets array is required',
|
|
456
|
-
output: 'Please provide content with a "sheets" array'
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// Resolve output path
|
|
461
|
-
let resolvedPath = outputPath;
|
|
462
|
-
if (!path.isAbsolute(resolvedPath)) {
|
|
463
|
-
resolvedPath = path.resolve(projectDir || process.cwd(), resolvedPath);
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Security: prevent path traversal
|
|
467
|
-
const baseDir = projectDir || process.cwd();
|
|
468
|
-
const normalizedPath = path.normalize(resolvedPath);
|
|
469
|
-
if (!normalizedPath.startsWith(path.normalize(baseDir))) {
|
|
470
|
-
return {
|
|
471
|
-
success: false,
|
|
472
|
-
error: 'Path traversal detected',
|
|
473
|
-
output: 'Output path must be within the project directory'
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Ensure output directory exists
|
|
478
|
-
const outputDir = path.dirname(resolvedPath);
|
|
479
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
480
|
-
|
|
481
|
-
// Ensure .xlsx extension
|
|
482
|
-
if (!resolvedPath.toLowerCase().endsWith('.xlsx')) {
|
|
483
|
-
resolvedPath += '.xlsx';
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Load ExcelJS
|
|
487
|
-
const loaded = await this.loadExcelJS();
|
|
488
|
-
if (!loaded) {
|
|
489
|
-
return {
|
|
490
|
-
success: false,
|
|
491
|
-
error: 'ExcelJS module not available',
|
|
492
|
-
output: `Spreadsheet module could not be loaded: ${this.excelError}`
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
const workbook = new ExcelJS.Workbook();
|
|
497
|
-
workbook.creator = 'Loxia Agent';
|
|
498
|
-
workbook.created = new Date();
|
|
499
|
-
|
|
500
|
-
for (const sheetDef of content.sheets) {
|
|
501
|
-
const worksheet = workbook.addWorksheet(sheetDef.name || 'Sheet');
|
|
502
|
-
|
|
503
|
-
// Set columns
|
|
504
|
-
if (sheetDef.columns && Array.isArray(sheetDef.columns)) {
|
|
505
|
-
worksheet.columns = sheetDef.columns.map(col => ({
|
|
506
|
-
header: col.header || '',
|
|
507
|
-
key: col.key || col.header?.toLowerCase().replace(/\s+/g, '_') || '',
|
|
508
|
-
width: col.width || 15
|
|
509
|
-
}));
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Apply header styling
|
|
513
|
-
if (sheetDef.headerStyle && worksheet.columns?.length > 0) {
|
|
514
|
-
const headerRow = worksheet.getRow(1);
|
|
515
|
-
const style = sheetDef.headerStyle;
|
|
516
|
-
|
|
517
|
-
headerRow.eachCell((cell) => {
|
|
518
|
-
if (style.bold) {
|
|
519
|
-
cell.font = { ...cell.font, bold: true };
|
|
520
|
-
}
|
|
521
|
-
if (style.fontColor) {
|
|
522
|
-
const color = style.fontColor.replace('#', '');
|
|
523
|
-
cell.font = { ...cell.font, color: { argb: `FF${color}` } };
|
|
524
|
-
}
|
|
525
|
-
if (style.fill) {
|
|
526
|
-
const fillColor = style.fill.replace('#', '');
|
|
527
|
-
cell.fill = {
|
|
528
|
-
type: 'pattern',
|
|
529
|
-
pattern: 'solid',
|
|
530
|
-
fgColor: { argb: `FF${fillColor}` }
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Add rows
|
|
537
|
-
if (sheetDef.rows && Array.isArray(sheetDef.rows)) {
|
|
538
|
-
for (const row of sheetDef.rows) {
|
|
539
|
-
if (Array.isArray(row)) {
|
|
540
|
-
// Array format: [val1, val2, ...]
|
|
541
|
-
const addedRow = worksheet.addRow(row);
|
|
542
|
-
// Check for formula values
|
|
543
|
-
row.forEach((val, i) => {
|
|
544
|
-
if (typeof val === 'string' && val.startsWith('=')) {
|
|
545
|
-
addedRow.getCell(i + 1).value = { formula: val.substring(1) };
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
} else if (typeof row === 'object') {
|
|
549
|
-
// Object format: { key: value }
|
|
550
|
-
const addedRow = worksheet.addRow(row);
|
|
551
|
-
// Check for formula values in object
|
|
552
|
-
for (const [key, val] of Object.entries(row)) {
|
|
553
|
-
if (typeof val === 'string' && val.startsWith('=')) {
|
|
554
|
-
const colIndex = worksheet.columns.findIndex(c => c.key === key);
|
|
555
|
-
if (colIndex >= 0) {
|
|
556
|
-
addedRow.getCell(colIndex + 1).value = { formula: val.substring(1) };
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Apply formulas
|
|
565
|
-
if (sheetDef.formulas && Array.isArray(sheetDef.formulas)) {
|
|
566
|
-
for (const formulaDef of sheetDef.formulas) {
|
|
567
|
-
if (formulaDef.cell && formulaDef.formula) {
|
|
568
|
-
const cell = worksheet.getCell(formulaDef.cell);
|
|
569
|
-
const formula = formulaDef.formula.startsWith('=')
|
|
570
|
-
? formulaDef.formula.substring(1)
|
|
571
|
-
: formulaDef.formula;
|
|
572
|
-
cell.value = { formula };
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// Freeze panes
|
|
578
|
-
if (sheetDef.freezeRow) {
|
|
579
|
-
worksheet.views = [{
|
|
580
|
-
state: 'frozen',
|
|
581
|
-
ySplit: sheetDef.freezeRow
|
|
582
|
-
}];
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Auto-filter
|
|
586
|
-
if (sheetDef.autoFilter && worksheet.columns?.length > 0) {
|
|
587
|
-
const lastCol = worksheet.columnCount;
|
|
588
|
-
const lastColLetter = this._getColumnLetter(lastCol);
|
|
589
|
-
worksheet.autoFilter = {
|
|
590
|
-
from: 'A1',
|
|
591
|
-
to: `${lastColLetter}1`
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// Write file
|
|
597
|
-
await workbook.xlsx.writeFile(resolvedPath);
|
|
598
|
-
|
|
599
|
-
const stats = await fs.stat(resolvedPath);
|
|
600
|
-
|
|
601
|
-
const sheetNames = content.sheets.map(s => s.name || 'Sheet');
|
|
602
|
-
const output = `Spreadsheet created successfully!\n` +
|
|
603
|
-
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n` +
|
|
604
|
-
`File: ${resolvedPath}\n` +
|
|
605
|
-
`Size: ${(stats.size / 1024).toFixed(1)} KB\n` +
|
|
606
|
-
`Sheets: ${sheetNames.join(', ')}`;
|
|
607
|
-
|
|
608
|
-
return {
|
|
609
|
-
success: true,
|
|
610
|
-
action: 'create',
|
|
611
|
-
outputPath: resolvedPath,
|
|
612
|
-
fileSize: stats.size,
|
|
613
|
-
sheets: sheetNames,
|
|
614
|
-
output,
|
|
615
|
-
message: `Spreadsheet created: ${resolvedPath} (${sheetNames.length} sheet(s))`
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
/**
|
|
620
|
-
* Convert column number to letter (1=A, 2=B, ..., 27=AA)
|
|
621
|
-
* @param {number} num
|
|
622
|
-
* @returns {string}
|
|
623
|
-
* @private
|
|
624
|
-
*/
|
|
625
|
-
_getColumnLetter(num) {
|
|
626
|
-
let letter = '';
|
|
627
|
-
while (num > 0) {
|
|
628
|
-
const remainder = (num - 1) % 26;
|
|
629
|
-
letter = String.fromCharCode(65 + remainder) + letter;
|
|
630
|
-
num = Math.floor((num - 1) / 26);
|
|
631
|
-
}
|
|
632
|
-
return letter;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
export default ExcelTool;
|
|
1
|
+
/**
|
|
2
|
+
* Spreadsheet (Excel) Tool - Read and create Excel files
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Get spreadsheet metadata (sheet names, row/column counts)
|
|
6
|
+
* - Read data from specific sheets and ranges
|
|
7
|
+
* - Create Excel workbooks with formatting, formulas, and styling
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BaseTool } from './baseTool.js';
|
|
11
|
+
import TagParser from '../utilities/tagParser.js';
|
|
12
|
+
import fs from 'fs/promises';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
|
|
15
|
+
// Lazy-loaded dependency
|
|
16
|
+
let ExcelJS = null;
|
|
17
|
+
|
|
18
|
+
class ExcelTool extends BaseTool {
|
|
19
|
+
constructor(config = {}, logger = null) {
|
|
20
|
+
super(config, logger);
|
|
21
|
+
this.id = 'spreadsheet';
|
|
22
|
+
this.name = 'Spreadsheet Tool';
|
|
23
|
+
this.description = 'Read and create Excel (XLSX) spreadsheets';
|
|
24
|
+
this.version = '1.0.0';
|
|
25
|
+
this.requiresProject = false;
|
|
26
|
+
this.isAsync = false;
|
|
27
|
+
this.excelLoaded = false;
|
|
28
|
+
this.excelError = null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Lazily load ExcelJS module
|
|
33
|
+
* @returns {Promise<boolean>}
|
|
34
|
+
*/
|
|
35
|
+
async loadExcelJS() {
|
|
36
|
+
if (this.excelLoaded) return true;
|
|
37
|
+
if (this.excelError) return false;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const mod = await import('exceljs');
|
|
41
|
+
ExcelJS = mod.default || mod;
|
|
42
|
+
this.excelLoaded = true;
|
|
43
|
+
return true;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
this.excelError = error.message;
|
|
46
|
+
this.logger?.error('Failed to load exceljs module', { error: error.message });
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get tool description for LLM consumption
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
55
|
+
getDescription() {
|
|
56
|
+
return `
|
|
57
|
+
Spreadsheet Tool: Read and create Excel (XLSX) spreadsheets.
|
|
58
|
+
|
|
59
|
+
USAGE:
|
|
60
|
+
\`\`\`json
|
|
61
|
+
{
|
|
62
|
+
"toolId": "spreadsheet",
|
|
63
|
+
"actions": [{
|
|
64
|
+
"action": "get-info",
|
|
65
|
+
"filePath": "data/report.xlsx"
|
|
66
|
+
}]
|
|
67
|
+
}
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
ACTIONS:
|
|
71
|
+
|
|
72
|
+
1. **get-info** - Get spreadsheet metadata
|
|
73
|
+
- filePath: Path to Excel file (required)
|
|
74
|
+
- Returns: sheet names, row counts, column counts per sheet
|
|
75
|
+
|
|
76
|
+
2. **read** - Read data from a sheet
|
|
77
|
+
- filePath: Path to Excel file (required)
|
|
78
|
+
- sheetName: Sheet name (optional, defaults to first sheet)
|
|
79
|
+
- startRow: Start row number, 1-indexed (optional, default: 1)
|
|
80
|
+
- endRow: End row number, inclusive (optional, default: all rows)
|
|
81
|
+
- includeFormulas: Return formulas instead of values (optional, default: false)
|
|
82
|
+
|
|
83
|
+
3. **create** - Create a new Excel workbook
|
|
84
|
+
- outputPath: Output file path (required)
|
|
85
|
+
- content: Workbook content object (required):
|
|
86
|
+
- sheets: Array of sheet definitions:
|
|
87
|
+
- name: Sheet name
|
|
88
|
+
- columns: [{ header: "Name", key: "name", width: 20 }]
|
|
89
|
+
- rows: [{ name: "John", age: 30 }] or [["John", 30]]
|
|
90
|
+
- headerStyle: { bold: true, fill: "#4472C4", fontColor: "#FFFFFF" }
|
|
91
|
+
- freezeRow: Freeze panes at this row (optional)
|
|
92
|
+
- autoFilter: Enable auto-filter on headers (optional, default: false)
|
|
93
|
+
- formulas: [{ cell: "C2", formula: "=A2+B2" }]
|
|
94
|
+
|
|
95
|
+
EXAMPLES:
|
|
96
|
+
|
|
97
|
+
1. Get spreadsheet info:
|
|
98
|
+
\`\`\`json
|
|
99
|
+
{
|
|
100
|
+
"toolId": "spreadsheet",
|
|
101
|
+
"actions": [{
|
|
102
|
+
"action": "get-info",
|
|
103
|
+
"filePath": "data/sales.xlsx"
|
|
104
|
+
}]
|
|
105
|
+
}
|
|
106
|
+
\`\`\`
|
|
107
|
+
|
|
108
|
+
2. Read data from a specific sheet:
|
|
109
|
+
\`\`\`json
|
|
110
|
+
{
|
|
111
|
+
"toolId": "spreadsheet",
|
|
112
|
+
"actions": [{
|
|
113
|
+
"action": "read",
|
|
114
|
+
"filePath": "data/sales.xlsx",
|
|
115
|
+
"sheetName": "Q1 Sales",
|
|
116
|
+
"startRow": 1,
|
|
117
|
+
"endRow": 50
|
|
118
|
+
}]
|
|
119
|
+
}
|
|
120
|
+
\`\`\`
|
|
121
|
+
|
|
122
|
+
3. Create a spreadsheet with formatting:
|
|
123
|
+
\`\`\`json
|
|
124
|
+
{
|
|
125
|
+
"toolId": "spreadsheet",
|
|
126
|
+
"actions": [{
|
|
127
|
+
"action": "create",
|
|
128
|
+
"outputPath": "output/report.xlsx",
|
|
129
|
+
"content": {
|
|
130
|
+
"sheets": [{
|
|
131
|
+
"name": "Sales Data",
|
|
132
|
+
"columns": [
|
|
133
|
+
{ "header": "Product", "key": "product", "width": 25 },
|
|
134
|
+
{ "header": "Quantity", "key": "qty", "width": 12 },
|
|
135
|
+
{ "header": "Price", "key": "price", "width": 12 },
|
|
136
|
+
{ "header": "Total", "key": "total", "width": 15 }
|
|
137
|
+
],
|
|
138
|
+
"rows": [
|
|
139
|
+
{ "product": "Widget A", "qty": 100, "price": 9.99 },
|
|
140
|
+
{ "product": "Widget B", "qty": 50, "price": 19.99 }
|
|
141
|
+
],
|
|
142
|
+
"headerStyle": { "bold": true, "fill": "#4472C4", "fontColor": "#FFFFFF" },
|
|
143
|
+
"freezeRow": 1,
|
|
144
|
+
"autoFilter": true,
|
|
145
|
+
"formulas": [
|
|
146
|
+
{ "cell": "D2", "formula": "=B2*C2" },
|
|
147
|
+
{ "cell": "D3", "formula": "=B3*C3" }
|
|
148
|
+
]
|
|
149
|
+
}]
|
|
150
|
+
}
|
|
151
|
+
}]
|
|
152
|
+
}
|
|
153
|
+
\`\`\`
|
|
154
|
+
|
|
155
|
+
NOTES:
|
|
156
|
+
- Row numbers are 1-indexed (first row is 1, typically the header)
|
|
157
|
+
- Values starting with "=" in rows are treated as formulas
|
|
158
|
+
- Column widths are in approximate character widths
|
|
159
|
+
- Fill colors should be hex codes without # (or with #, both are accepted)
|
|
160
|
+
- Multiple sheets can be created in a single workbook
|
|
161
|
+
`.trim();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Parse parameters from tool command content
|
|
166
|
+
* @param {string} content
|
|
167
|
+
* @returns {Object}
|
|
168
|
+
*/
|
|
169
|
+
parseParameters(content) {
|
|
170
|
+
try {
|
|
171
|
+
const actionMatches = TagParser.extractContent(content, 'action');
|
|
172
|
+
const filePathMatches = TagParser.extractContent(content, 'filePath');
|
|
173
|
+
const sheetNameMatches = TagParser.extractContent(content, 'sheetName');
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
actions: [{
|
|
177
|
+
action: actionMatches.length > 0 ? actionMatches[0].trim() : 'get-info',
|
|
178
|
+
filePath: filePathMatches.length > 0 ? filePathMatches[0].trim() : '',
|
|
179
|
+
sheetName: sheetNameMatches.length > 0 ? sheetNameMatches[0].trim() : undefined
|
|
180
|
+
}]
|
|
181
|
+
};
|
|
182
|
+
} catch (error) {
|
|
183
|
+
throw new Error(`Failed to parse Spreadsheet tool parameters: ${error.message}`, { cause: error });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get supported actions
|
|
189
|
+
* @returns {Array<string>}
|
|
190
|
+
*/
|
|
191
|
+
getSupportedActions() {
|
|
192
|
+
return ['get-info', 'read', 'create'];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Execute spreadsheet tool action
|
|
197
|
+
* @param {Object} params
|
|
198
|
+
* @param {Object} context
|
|
199
|
+
* @returns {Promise<Object>}
|
|
200
|
+
*/
|
|
201
|
+
async execute(params, context) {
|
|
202
|
+
const { actions } = params;
|
|
203
|
+
|
|
204
|
+
if (!actions || actions.length === 0) {
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
error: 'No actions provided',
|
|
208
|
+
output: 'Please specify an action (get-info, read, or create)'
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const action = actions[0];
|
|
213
|
+
|
|
214
|
+
// Handle create action (no existing file required)
|
|
215
|
+
if (action.action === 'create') {
|
|
216
|
+
try {
|
|
217
|
+
return await this.createSpreadsheet(action, context);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
this.logger?.error('Excel creation error', { error: error.message });
|
|
220
|
+
return {
|
|
221
|
+
success: false,
|
|
222
|
+
error: error.message,
|
|
223
|
+
output: `Failed to create spreadsheet: ${error.message}`
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Read-oriented actions require file path
|
|
229
|
+
const { projectDir } = context;
|
|
230
|
+
let filePath = action.filePath;
|
|
231
|
+
|
|
232
|
+
if (!filePath) {
|
|
233
|
+
return {
|
|
234
|
+
success: false,
|
|
235
|
+
error: 'File path is required',
|
|
236
|
+
output: 'Please provide a filePath parameter'
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!path.isAbsolute(filePath)) {
|
|
241
|
+
filePath = path.resolve(projectDir || process.cwd(), filePath);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Check file exists
|
|
245
|
+
try {
|
|
246
|
+
await fs.access(filePath);
|
|
247
|
+
} catch {
|
|
248
|
+
return {
|
|
249
|
+
success: false,
|
|
250
|
+
error: `File not found: ${filePath}`,
|
|
251
|
+
output: `The Excel file does not exist: ${filePath}`
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Check extension
|
|
256
|
+
const ext = filePath.toLowerCase();
|
|
257
|
+
if (!ext.endsWith('.xlsx') && !ext.endsWith('.xls') && !ext.endsWith('.csv')) {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
error: 'Not a spreadsheet file',
|
|
261
|
+
output: `The file must have a .xlsx, .xls, or .csv extension: ${filePath}`
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Load ExcelJS
|
|
266
|
+
const loaded = await this.loadExcelJS();
|
|
267
|
+
if (!loaded) {
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: 'ExcelJS module not available',
|
|
271
|
+
output: `Spreadsheet module could not be loaded: ${this.excelError}`
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
switch (action.action) {
|
|
277
|
+
case 'get-info':
|
|
278
|
+
return await this.getInfo(filePath);
|
|
279
|
+
case 'read':
|
|
280
|
+
return await this.readSheet(filePath, action);
|
|
281
|
+
default:
|
|
282
|
+
return {
|
|
283
|
+
success: false,
|
|
284
|
+
error: `Unknown action: ${action.action}`,
|
|
285
|
+
output: 'Supported actions: get-info, read, create'
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
} catch (error) {
|
|
289
|
+
this.logger?.error('Excel tool error', { action: action.action, filePath, error: error.message });
|
|
290
|
+
return {
|
|
291
|
+
success: false,
|
|
292
|
+
error: error.message,
|
|
293
|
+
output: `Failed to process spreadsheet: ${error.message}`
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Get spreadsheet info
|
|
300
|
+
* @param {string} filePath
|
|
301
|
+
* @returns {Promise<Object>}
|
|
302
|
+
*/
|
|
303
|
+
async getInfo(filePath) {
|
|
304
|
+
const workbook = new ExcelJS.Workbook();
|
|
305
|
+
|
|
306
|
+
if (filePath.toLowerCase().endsWith('.csv')) {
|
|
307
|
+
await workbook.csv.readFile(filePath);
|
|
308
|
+
} else {
|
|
309
|
+
await workbook.xlsx.readFile(filePath);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const sheets = [];
|
|
313
|
+
workbook.eachSheet((worksheet) => {
|
|
314
|
+
sheets.push({
|
|
315
|
+
name: worksheet.name,
|
|
316
|
+
rowCount: worksheet.rowCount,
|
|
317
|
+
columnCount: worksheet.columnCount,
|
|
318
|
+
actualRowCount: worksheet.actualRowCount
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
let output = `Spreadsheet Info: ${path.basename(filePath)}\n`;
|
|
323
|
+
output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
324
|
+
output += `Sheets: ${sheets.length}\n\n`;
|
|
325
|
+
|
|
326
|
+
for (const sheet of sheets) {
|
|
327
|
+
output += `📊 ${sheet.name}\n`;
|
|
328
|
+
output += ` Rows: ${sheet.actualRowCount || sheet.rowCount}\n`;
|
|
329
|
+
output += ` Columns: ${sheet.columnCount}\n\n`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
success: true,
|
|
334
|
+
action: 'get-info',
|
|
335
|
+
filePath,
|
|
336
|
+
sheets,
|
|
337
|
+
output,
|
|
338
|
+
message: `Spreadsheet has ${sheets.length} sheet(s)`
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Read data from a sheet
|
|
344
|
+
* @param {string} filePath
|
|
345
|
+
* @param {Object} action - Action parameters
|
|
346
|
+
* @returns {Promise<Object>}
|
|
347
|
+
*/
|
|
348
|
+
async readSheet(filePath, action) {
|
|
349
|
+
const { sheetName, startRow = 1, endRow, includeFormulas = false } = action;
|
|
350
|
+
|
|
351
|
+
const workbook = new ExcelJS.Workbook();
|
|
352
|
+
|
|
353
|
+
if (filePath.toLowerCase().endsWith('.csv')) {
|
|
354
|
+
await workbook.csv.readFile(filePath);
|
|
355
|
+
} else {
|
|
356
|
+
await workbook.xlsx.readFile(filePath);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Get the requested sheet
|
|
360
|
+
let worksheet;
|
|
361
|
+
if (sheetName) {
|
|
362
|
+
worksheet = workbook.getWorksheet(sheetName);
|
|
363
|
+
if (!worksheet) {
|
|
364
|
+
const available = [];
|
|
365
|
+
workbook.eachSheet(ws => available.push(ws.name));
|
|
366
|
+
return {
|
|
367
|
+
success: false,
|
|
368
|
+
error: `Sheet not found: ${sheetName}`,
|
|
369
|
+
output: `Sheet "${sheetName}" not found. Available sheets: ${available.join(', ')}`
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
worksheet = workbook.worksheets[0];
|
|
374
|
+
if (!worksheet) {
|
|
375
|
+
return {
|
|
376
|
+
success: false,
|
|
377
|
+
error: 'No sheets in workbook',
|
|
378
|
+
output: 'The workbook has no sheets'
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const totalRows = worksheet.actualRowCount || worksheet.rowCount;
|
|
384
|
+
const effectiveEnd = endRow ? Math.min(endRow, totalRows) : totalRows;
|
|
385
|
+
const effectiveStart = Math.max(1, startRow);
|
|
386
|
+
|
|
387
|
+
const rows = [];
|
|
388
|
+
for (let r = effectiveStart; r <= effectiveEnd; r++) {
|
|
389
|
+
const row = worksheet.getRow(r);
|
|
390
|
+
const rowData = [];
|
|
391
|
+
|
|
392
|
+
for (let c = 1; c <= worksheet.columnCount; c++) {
|
|
393
|
+
const cell = row.getCell(c);
|
|
394
|
+
if (includeFormulas && cell.formula) {
|
|
395
|
+
rowData.push(`=${cell.formula}`);
|
|
396
|
+
} else {
|
|
397
|
+
rowData.push(cell.value !== null && cell.value !== undefined ? cell.value : '');
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
rows.push(rowData);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Build output
|
|
405
|
+
let output = `Sheet: ${worksheet.name} (rows ${effectiveStart}-${effectiveEnd} of ${totalRows})\n`;
|
|
406
|
+
output += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n`;
|
|
407
|
+
|
|
408
|
+
// Format as text table
|
|
409
|
+
for (let i = 0; i < rows.length; i++) {
|
|
410
|
+
const rowNum = effectiveStart + i;
|
|
411
|
+
const values = rows[i].map(v => {
|
|
412
|
+
if (v === null || v === undefined) return '';
|
|
413
|
+
if (typeof v === 'object' && v.result !== undefined) return String(v.result);
|
|
414
|
+
return String(v);
|
|
415
|
+
});
|
|
416
|
+
output += `Row ${rowNum}: ${values.join(' | ')}\n`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
success: true,
|
|
421
|
+
action: 'read',
|
|
422
|
+
filePath,
|
|
423
|
+
sheetName: worksheet.name,
|
|
424
|
+
startRow: effectiveStart,
|
|
425
|
+
endRow: effectiveEnd,
|
|
426
|
+
totalRows,
|
|
427
|
+
rowCount: rows.length,
|
|
428
|
+
rows,
|
|
429
|
+
output,
|
|
430
|
+
message: `Read ${rows.length} rows from "${worksheet.name}"`
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Create an Excel workbook
|
|
436
|
+
* @param {Object} action - Action parameters
|
|
437
|
+
* @param {Object} context - Execution context
|
|
438
|
+
* @returns {Promise<Object>}
|
|
439
|
+
*/
|
|
440
|
+
async createSpreadsheet(action, context) {
|
|
441
|
+
const { projectDir } = context;
|
|
442
|
+
const { outputPath, content } = action;
|
|
443
|
+
|
|
444
|
+
if (!outputPath) {
|
|
445
|
+
return {
|
|
446
|
+
success: false,
|
|
447
|
+
error: 'Output path is required',
|
|
448
|
+
output: 'Please provide an outputPath parameter'
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (!content || !content.sheets || !Array.isArray(content.sheets)) {
|
|
453
|
+
return {
|
|
454
|
+
success: false,
|
|
455
|
+
error: 'Content with sheets array is required',
|
|
456
|
+
output: 'Please provide content with a "sheets" array'
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Resolve output path
|
|
461
|
+
let resolvedPath = outputPath;
|
|
462
|
+
if (!path.isAbsolute(resolvedPath)) {
|
|
463
|
+
resolvedPath = path.resolve(projectDir || process.cwd(), resolvedPath);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Security: prevent path traversal
|
|
467
|
+
const baseDir = projectDir || process.cwd();
|
|
468
|
+
const normalizedPath = path.normalize(resolvedPath);
|
|
469
|
+
if (!normalizedPath.startsWith(path.normalize(baseDir))) {
|
|
470
|
+
return {
|
|
471
|
+
success: false,
|
|
472
|
+
error: 'Path traversal detected',
|
|
473
|
+
output: 'Output path must be within the project directory'
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Ensure output directory exists
|
|
478
|
+
const outputDir = path.dirname(resolvedPath);
|
|
479
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
480
|
+
|
|
481
|
+
// Ensure .xlsx extension
|
|
482
|
+
if (!resolvedPath.toLowerCase().endsWith('.xlsx')) {
|
|
483
|
+
resolvedPath += '.xlsx';
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Load ExcelJS
|
|
487
|
+
const loaded = await this.loadExcelJS();
|
|
488
|
+
if (!loaded) {
|
|
489
|
+
return {
|
|
490
|
+
success: false,
|
|
491
|
+
error: 'ExcelJS module not available',
|
|
492
|
+
output: `Spreadsheet module could not be loaded: ${this.excelError}`
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const workbook = new ExcelJS.Workbook();
|
|
497
|
+
workbook.creator = 'Loxia Agent';
|
|
498
|
+
workbook.created = new Date();
|
|
499
|
+
|
|
500
|
+
for (const sheetDef of content.sheets) {
|
|
501
|
+
const worksheet = workbook.addWorksheet(sheetDef.name || 'Sheet');
|
|
502
|
+
|
|
503
|
+
// Set columns
|
|
504
|
+
if (sheetDef.columns && Array.isArray(sheetDef.columns)) {
|
|
505
|
+
worksheet.columns = sheetDef.columns.map(col => ({
|
|
506
|
+
header: col.header || '',
|
|
507
|
+
key: col.key || col.header?.toLowerCase().replace(/\s+/g, '_') || '',
|
|
508
|
+
width: col.width || 15
|
|
509
|
+
}));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Apply header styling
|
|
513
|
+
if (sheetDef.headerStyle && worksheet.columns?.length > 0) {
|
|
514
|
+
const headerRow = worksheet.getRow(1);
|
|
515
|
+
const style = sheetDef.headerStyle;
|
|
516
|
+
|
|
517
|
+
headerRow.eachCell((cell) => {
|
|
518
|
+
if (style.bold) {
|
|
519
|
+
cell.font = { ...cell.font, bold: true };
|
|
520
|
+
}
|
|
521
|
+
if (style.fontColor) {
|
|
522
|
+
const color = style.fontColor.replace('#', '');
|
|
523
|
+
cell.font = { ...cell.font, color: { argb: `FF${color}` } };
|
|
524
|
+
}
|
|
525
|
+
if (style.fill) {
|
|
526
|
+
const fillColor = style.fill.replace('#', '');
|
|
527
|
+
cell.fill = {
|
|
528
|
+
type: 'pattern',
|
|
529
|
+
pattern: 'solid',
|
|
530
|
+
fgColor: { argb: `FF${fillColor}` }
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Add rows
|
|
537
|
+
if (sheetDef.rows && Array.isArray(sheetDef.rows)) {
|
|
538
|
+
for (const row of sheetDef.rows) {
|
|
539
|
+
if (Array.isArray(row)) {
|
|
540
|
+
// Array format: [val1, val2, ...]
|
|
541
|
+
const addedRow = worksheet.addRow(row);
|
|
542
|
+
// Check for formula values
|
|
543
|
+
row.forEach((val, i) => {
|
|
544
|
+
if (typeof val === 'string' && val.startsWith('=')) {
|
|
545
|
+
addedRow.getCell(i + 1).value = { formula: val.substring(1) };
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
} else if (typeof row === 'object') {
|
|
549
|
+
// Object format: { key: value }
|
|
550
|
+
const addedRow = worksheet.addRow(row);
|
|
551
|
+
// Check for formula values in object
|
|
552
|
+
for (const [key, val] of Object.entries(row)) {
|
|
553
|
+
if (typeof val === 'string' && val.startsWith('=')) {
|
|
554
|
+
const colIndex = worksheet.columns.findIndex(c => c.key === key);
|
|
555
|
+
if (colIndex >= 0) {
|
|
556
|
+
addedRow.getCell(colIndex + 1).value = { formula: val.substring(1) };
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Apply formulas
|
|
565
|
+
if (sheetDef.formulas && Array.isArray(sheetDef.formulas)) {
|
|
566
|
+
for (const formulaDef of sheetDef.formulas) {
|
|
567
|
+
if (formulaDef.cell && formulaDef.formula) {
|
|
568
|
+
const cell = worksheet.getCell(formulaDef.cell);
|
|
569
|
+
const formula = formulaDef.formula.startsWith('=')
|
|
570
|
+
? formulaDef.formula.substring(1)
|
|
571
|
+
: formulaDef.formula;
|
|
572
|
+
cell.value = { formula };
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Freeze panes
|
|
578
|
+
if (sheetDef.freezeRow) {
|
|
579
|
+
worksheet.views = [{
|
|
580
|
+
state: 'frozen',
|
|
581
|
+
ySplit: sheetDef.freezeRow
|
|
582
|
+
}];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Auto-filter
|
|
586
|
+
if (sheetDef.autoFilter && worksheet.columns?.length > 0) {
|
|
587
|
+
const lastCol = worksheet.columnCount;
|
|
588
|
+
const lastColLetter = this._getColumnLetter(lastCol);
|
|
589
|
+
worksheet.autoFilter = {
|
|
590
|
+
from: 'A1',
|
|
591
|
+
to: `${lastColLetter}1`
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Write file
|
|
597
|
+
await workbook.xlsx.writeFile(resolvedPath);
|
|
598
|
+
|
|
599
|
+
const stats = await fs.stat(resolvedPath);
|
|
600
|
+
|
|
601
|
+
const sheetNames = content.sheets.map(s => s.name || 'Sheet');
|
|
602
|
+
const output = `Spreadsheet created successfully!\n` +
|
|
603
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n` +
|
|
604
|
+
`File: ${resolvedPath}\n` +
|
|
605
|
+
`Size: ${(stats.size / 1024).toFixed(1)} KB\n` +
|
|
606
|
+
`Sheets: ${sheetNames.join(', ')}`;
|
|
607
|
+
|
|
608
|
+
return {
|
|
609
|
+
success: true,
|
|
610
|
+
action: 'create',
|
|
611
|
+
outputPath: resolvedPath,
|
|
612
|
+
fileSize: stats.size,
|
|
613
|
+
sheets: sheetNames,
|
|
614
|
+
output,
|
|
615
|
+
message: `Spreadsheet created: ${resolvedPath} (${sheetNames.length} sheet(s))`
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Convert column number to letter (1=A, 2=B, ..., 27=AA)
|
|
621
|
+
* @param {number} num
|
|
622
|
+
* @returns {string}
|
|
623
|
+
* @private
|
|
624
|
+
*/
|
|
625
|
+
_getColumnLetter(num) {
|
|
626
|
+
let letter = '';
|
|
627
|
+
while (num > 0) {
|
|
628
|
+
const remainder = (num - 1) % 26;
|
|
629
|
+
letter = String.fromCharCode(65 + remainder) + letter;
|
|
630
|
+
num = Math.floor((num - 1) / 26);
|
|
631
|
+
}
|
|
632
|
+
return letter;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
export default ExcelTool;
|