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/index.js
CHANGED
|
@@ -1,1222 +1,1222 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Loxia Autopilot One - Main Application Entry Point
|
|
3
|
-
*
|
|
4
|
-
* Purpose:
|
|
5
|
-
* - Initialize all system components
|
|
6
|
-
* - Setup dependency injection
|
|
7
|
-
* - Start interface handlers
|
|
8
|
-
* - Handle graceful shutdown
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { exec } from 'child_process';
|
|
13
|
-
import { createLogger } from './utilities/logger.js';
|
|
14
|
-
import { resolveModuleFilename } from './utilities/esmCjsPath.js';
|
|
15
|
-
import { createConfigManager } from './utilities/configManager.js';
|
|
16
|
-
import Orchestrator from './core/orchestrator.js';
|
|
17
|
-
import AgentPool from './core/agentPool.js';
|
|
18
|
-
import MessageProcessor from './core/messageProcessor.js';
|
|
19
|
-
import AgentScheduler from './core/agentScheduler.js';
|
|
20
|
-
import ContextManager from './core/contextManager.js';
|
|
21
|
-
import StateManager from './core/stateManager.js';
|
|
22
|
-
import AIService from './services/aiService.js';
|
|
23
|
-
import { EmbeddingService } from './services/embeddings/embeddingService.js';
|
|
24
|
-
import { getUserDataPaths } from './utilities/userDataDir.js';
|
|
25
|
-
import BudgetService from './services/budgetService.js';
|
|
26
|
-
import ErrorHandler from './services/errorHandler.js';
|
|
27
|
-
import BenchmarkService from './services/benchmarkService.js';
|
|
28
|
-
import ModelRouterService from './services/modelRouterService.js';
|
|
29
|
-
import ModelsService from './services/modelsService.js';
|
|
30
|
-
import ApiKeyManager, { registerApiKeyManager } from './services/apiKeyManager.js';
|
|
31
|
-
import { getCredentialVault } from './services/credentialVault.js';
|
|
32
|
-
import FileAttachmentService from './services/fileAttachmentService.js';
|
|
33
|
-
import { ToolsRegistry } from './tools/baseTool.js';
|
|
34
|
-
import AgentDelayTool from './tools/agentDelayTool.js';
|
|
35
|
-
import TerminalTool from './tools/terminalTool.js';
|
|
36
|
-
import FileSystemTool from './tools/fileSystemTool.js';
|
|
37
|
-
import JobDoneTool from './tools/jobDoneTool.js';
|
|
38
|
-
import AgentCommunicationTool from './tools/agentCommunicationTool.js';
|
|
39
|
-
import TaskManagerTool from './tools/taskManagerTool.js';
|
|
40
|
-
import ImportAnalyzerTool from './tools/importAnalyzerTool.js';
|
|
41
|
-
import DependencyResolverTool from './tools/dependencyResolverTool.js';
|
|
42
|
-
import ImageTool from './tools/imageTool.js';
|
|
43
|
-
import VideoTool from './tools/videoTool.js';
|
|
44
|
-
import StaticAnalysisTool from './tools/staticAnalysisTool.js';
|
|
45
|
-
import CloneDetectionTool from './tools/cloneDetectionTool.js';
|
|
46
|
-
import FileTreeTool from './tools/fileTreeTool.js';
|
|
47
|
-
import FileContentReplaceTool from './tools/fileContentReplaceTool.js';
|
|
48
|
-
import SeekTool from './tools/seekTool.js';
|
|
49
|
-
import WebTool from './tools/webTool.js';
|
|
50
|
-
import FoundryWebSearchTool from './tools/foundryWebSearchTool.js';
|
|
51
|
-
import VisualEditorTool from './tools/visualEditorTool.js';
|
|
52
|
-
import PdfTool from './tools/pdfTool.js';
|
|
53
|
-
import HelpTool from './tools/helpTool.js';
|
|
54
|
-
import DocxTool from './tools/docxTool.js';
|
|
55
|
-
import ExcelTool from './tools/excelTool.js';
|
|
56
|
-
import OfficeTool from './tools/officeTool.js';
|
|
57
|
-
import MemoryTool from './tools/memoryTool.js';
|
|
58
|
-
import ComposioTool from './tools/composioTool.js';
|
|
59
|
-
import SkillsTool from './tools/skillsTool.js';
|
|
60
|
-
import VisionTool from './tools/visionTool.js';
|
|
61
|
-
import UserPromptTool from './tools/userPromptTool.js';
|
|
62
|
-
import CodeMapTool from './tools/codeMapTool.js';
|
|
63
|
-
import WhatsAppTool from './tools/whatsappTool.js';
|
|
64
|
-
import PlatformControlTool from './tools/platformControlTool.js';
|
|
65
|
-
import { DesktopTool } from './tools/desktop/DesktopTool.js';
|
|
66
|
-
import ScheduleService from './services/scheduleService.js';
|
|
67
|
-
import AsyncToolManager from './tools/asyncToolManager.js';
|
|
68
|
-
import WebServer from './interfaces/webServer.js';
|
|
69
|
-
|
|
70
|
-
import {
|
|
71
|
-
SYSTEM_VERSION,
|
|
72
|
-
INTERFACE_TYPES
|
|
73
|
-
} from './utilities/constants.js';
|
|
74
|
-
import {
|
|
75
|
-
getUserDataDir,
|
|
76
|
-
ensureUserDataDirs,
|
|
77
|
-
migrateFromOldLocation,
|
|
78
|
-
getLegacyDataPaths
|
|
79
|
-
} from './utilities/userDataDir.js';
|
|
80
|
-
|
|
81
|
-
class LoxiaApplication {
|
|
82
|
-
constructor() {
|
|
83
|
-
this.logger = null;
|
|
84
|
-
this.config = null;
|
|
85
|
-
this.orchestrator = null;
|
|
86
|
-
this.interfaces = new Map();
|
|
87
|
-
this.isShuttingDown = false;
|
|
88
|
-
|
|
89
|
-
// Bind shutdown handler
|
|
90
|
-
this.shutdown = this.shutdown.bind(this);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Initialize the application
|
|
95
|
-
* @param {Object} options - Initialization options
|
|
96
|
-
* @returns {Promise<void>}
|
|
97
|
-
*/
|
|
98
|
-
async initialize(options = {}) {
|
|
99
|
-
try {
|
|
100
|
-
console.log(`🚀 Starting Loxia Autopilot One v${SYSTEM_VERSION}`);
|
|
101
|
-
|
|
102
|
-
// Initialize configuration
|
|
103
|
-
await this.initializeConfig(options);
|
|
104
|
-
|
|
105
|
-
// Initialize logging
|
|
106
|
-
await this.initializeLogging();
|
|
107
|
-
|
|
108
|
-
this.logger.info('Loxia Autopilot One starting up', {
|
|
109
|
-
version: SYSTEM_VERSION,
|
|
110
|
-
nodeVersion: process.version,
|
|
111
|
-
platform: process.platform,
|
|
112
|
-
projectDir: options.projectDir || process.cwd()
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// IMPORTANT: Initialize persistent user data directory and migrate legacy data
|
|
116
|
-
// This ensures user data (agents, conversations, settings) survives npm updates
|
|
117
|
-
await this.initializeUserDataDirectory();
|
|
118
|
-
|
|
119
|
-
// Initialize core components
|
|
120
|
-
await this.initializeCoreComponents();
|
|
121
|
-
|
|
122
|
-
// Initialize tools
|
|
123
|
-
await this.initializeTools();
|
|
124
|
-
|
|
125
|
-
this.logger.info('Starting interface initialization...');
|
|
126
|
-
|
|
127
|
-
// Initialize interfaces
|
|
128
|
-
await this.initializeInterfaces(options);
|
|
129
|
-
|
|
130
|
-
this.logger.info('Interface initialization completed');
|
|
131
|
-
|
|
132
|
-
// Setup shutdown handlers
|
|
133
|
-
this.setupShutdownHandlers();
|
|
134
|
-
|
|
135
|
-
this.logger.info('Loxia Autopilot One startup complete');
|
|
136
|
-
console.log('✅ Loxia Autopilot One is ready!');
|
|
137
|
-
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error('❌ Failed to initialize Loxia Autopilot One:', error.message);
|
|
140
|
-
if (this.logger) {
|
|
141
|
-
this.logger.error('Application initialization failed', {
|
|
142
|
-
error: error.message,
|
|
143
|
-
stack: error.stack
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Initialize configuration management
|
|
152
|
-
* @private
|
|
153
|
-
*/
|
|
154
|
-
async initializeConfig(options) {
|
|
155
|
-
const __filename = resolveModuleFilename(import.meta.url);
|
|
156
|
-
const __dirname = path.dirname(__filename);
|
|
157
|
-
|
|
158
|
-
const configPaths = [
|
|
159
|
-
path.join(__dirname, '../config/default.json'),
|
|
160
|
-
...(options.configPaths || [])
|
|
161
|
-
];
|
|
162
|
-
|
|
163
|
-
this.configManager = createConfigManager({
|
|
164
|
-
configPaths,
|
|
165
|
-
envPrefix: 'LOXIA'
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
this.config = await this.configManager.loadConfig();
|
|
169
|
-
|
|
170
|
-
// Enable config watching if requested
|
|
171
|
-
if (options.watchConfig) {
|
|
172
|
-
await this.configManager.watchConfig(true);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Initialize logging system
|
|
178
|
-
* @private
|
|
179
|
-
*/
|
|
180
|
-
async initializeLogging() {
|
|
181
|
-
const loggingConfig = this.config.logging || {};
|
|
182
|
-
|
|
183
|
-
this.logger = createLogger({
|
|
184
|
-
level: loggingConfig.level || 'info',
|
|
185
|
-
outputs: loggingConfig.outputs || ['console'],
|
|
186
|
-
colors: loggingConfig.colors !== false,
|
|
187
|
-
timestamp: loggingConfig.timestamp !== false,
|
|
188
|
-
logFile: loggingConfig.logFile,
|
|
189
|
-
maxFileSize: loggingConfig.maxFileSize,
|
|
190
|
-
maxFiles: loggingConfig.maxFiles
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
await this.logger.initialize();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Initialize persistent user data directory and migrate legacy data
|
|
198
|
-
* This ensures user data survives npm package updates
|
|
199
|
-
* @private
|
|
200
|
-
*/
|
|
201
|
-
async initializeUserDataDirectory() {
|
|
202
|
-
try {
|
|
203
|
-
// Create user data directory structure
|
|
204
|
-
|
|
205
|
-
const userDataDir = getUserDataDir();
|
|
206
|
-
|
|
207
|
-
this.logger.info('User data directory initialized', {
|
|
208
|
-
location: userDataDir,
|
|
209
|
-
platform: process.platform
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
// Check for legacy data and migrate if needed
|
|
213
|
-
const legacyPaths = getLegacyDataPaths();
|
|
214
|
-
for (const legacyPath of legacyPaths) {
|
|
215
|
-
try {
|
|
216
|
-
const result = await migrateFromOldLocation(legacyPath, {
|
|
217
|
-
dryRun: false,
|
|
218
|
-
logger: this.logger
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
if (result.migrated.length > 0) {
|
|
222
|
-
this.logger.info('Successfully migrated data from legacy location', {
|
|
223
|
-
from: legacyPath,
|
|
224
|
-
migratedCount: result.migrated.length
|
|
225
|
-
});
|
|
226
|
-
console.log(`📦 Migrated ${result.migrated.length} items from legacy location to ${userDataDir}`);
|
|
227
|
-
}
|
|
228
|
-
} catch (migrationError) {
|
|
229
|
-
// Non-fatal: log and continue
|
|
230
|
-
this.logger.warn('Failed to migrate from legacy location', {
|
|
231
|
-
legacyPath,
|
|
232
|
-
error: migrationError.message
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
} catch (error) {
|
|
238
|
-
this.logger.error('Failed to initialize user data directory', {
|
|
239
|
-
error: error.message
|
|
240
|
-
});
|
|
241
|
-
throw error;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Initialize core system components
|
|
247
|
-
* @private
|
|
248
|
-
*/
|
|
249
|
-
async initializeCoreComponents() {
|
|
250
|
-
this.logger.info('Initializing core components...');
|
|
251
|
-
|
|
252
|
-
// State Manager
|
|
253
|
-
this.stateManager = new StateManager(this.config, this.logger);
|
|
254
|
-
|
|
255
|
-
// File Attachment Service
|
|
256
|
-
this.fileAttachmentService = new FileAttachmentService(this.config, this.logger);
|
|
257
|
-
await this.fileAttachmentService.initialize();
|
|
258
|
-
|
|
259
|
-
// Context Manager
|
|
260
|
-
this.contextManager = new ContextManager(this.config, this.logger);
|
|
261
|
-
|
|
262
|
-
// Tools Registry and Async Tool Manager
|
|
263
|
-
this.toolsRegistry = new ToolsRegistry(this.logger);
|
|
264
|
-
this.asyncToolManager = new AsyncToolManager(this.config, this.logger);
|
|
265
|
-
|
|
266
|
-
// Agent Pool (with tools registry for prompt enhancement)
|
|
267
|
-
this.agentPool = new AgentPool(
|
|
268
|
-
this.config,
|
|
269
|
-
this.logger,
|
|
270
|
-
this.stateManager,
|
|
271
|
-
this.contextManager,
|
|
272
|
-
this.toolsRegistry
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
// Initialize Budget Service and Error Handler
|
|
276
|
-
this.budgetService = new BudgetService(this.config, this.logger);
|
|
277
|
-
this.errorHandler = new ErrorHandler(this.config, this.logger);
|
|
278
|
-
|
|
279
|
-
// API Key Manager
|
|
280
|
-
this.apiKeyManager = new ApiKeyManager(this.logger);
|
|
281
|
-
await this.apiKeyManager.initialize(); // Load persisted keys
|
|
282
|
-
// Register the singleton so cross-cutting services (composioService,
|
|
283
|
-
// etc.) can resolve vendor keys without explicit injection.
|
|
284
|
-
registerApiKeyManager(this.apiKeyManager);
|
|
285
|
-
|
|
286
|
-
// Credential Vault for secure website credential management
|
|
287
|
-
this.credentialVault = getCredentialVault(this.logger);
|
|
288
|
-
await this.credentialVault.initialize(); // Load persisted credentials
|
|
289
|
-
|
|
290
|
-
// WhatsApp Service (optional — requires whatsapp-web.js)
|
|
291
|
-
try {
|
|
292
|
-
const { default: WhatsAppService } = await import('./services/whatsappService.js');
|
|
293
|
-
this.whatsappService = new WhatsAppService(this.logger);
|
|
294
|
-
this.logger.info('WhatsApp service initialized');
|
|
295
|
-
} catch (e) {
|
|
296
|
-
this.whatsappService = null;
|
|
297
|
-
this.logger.info('WhatsApp service unavailable (whatsapp-web.js not installed)', { error: e.message });
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Telegram Service (optional — requires node-telegram-bot-api)
|
|
301
|
-
try {
|
|
302
|
-
const { getTelegramService } = await import('./services/telegramService.js');
|
|
303
|
-
this.telegramService = getTelegramService(this.logger);
|
|
304
|
-
this.logger.info('Telegram service initialized');
|
|
305
|
-
} catch (e) {
|
|
306
|
-
this.telegramService = null;
|
|
307
|
-
this.logger.info('Telegram service unavailable', { error: e.message });
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Discord Service (optional — requires discord.js)
|
|
311
|
-
try {
|
|
312
|
-
const { getDiscordService } = await import('./services/discordService.js');
|
|
313
|
-
this.discordService = getDiscordService(this.logger);
|
|
314
|
-
this.logger.info('Discord service initialized');
|
|
315
|
-
} catch (e) {
|
|
316
|
-
this.discordService = null;
|
|
317
|
-
this.logger.info('Discord service unavailable', { error: e.message });
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Schedule Service
|
|
321
|
-
this.scheduleService = new ScheduleService(this.logger);
|
|
322
|
-
await this.scheduleService.initialize();
|
|
323
|
-
|
|
324
|
-
// AI Service
|
|
325
|
-
this.aiService = new AIService(
|
|
326
|
-
this.config,
|
|
327
|
-
this.logger,
|
|
328
|
-
this.budgetService,
|
|
329
|
-
this.errorHandler
|
|
330
|
-
);
|
|
331
|
-
|
|
332
|
-
// Set API Key Manager reference in AI Service
|
|
333
|
-
this.aiService.setApiKeyManager(this.apiKeyManager);
|
|
334
|
-
|
|
335
|
-
// Set Agent Pool reference in AI Service
|
|
336
|
-
this.aiService.setAgentPool(this.agentPool);
|
|
337
|
-
|
|
338
|
-
// Embedding Service — opt-in semantic retrieval layer. Boots in the
|
|
339
|
-
// disabled state by default; loadPersistedConfig() picks up whatever
|
|
340
|
-
// the user previously chose in Settings.
|
|
341
|
-
const stateDir = getUserDataPaths().state;
|
|
342
|
-
this.embeddingService = new EmbeddingService({
|
|
343
|
-
// Start with the in-memory default (disabled). The settings file
|
|
344
|
-
// (if present) will override this on the next line.
|
|
345
|
-
config: this.config?.embeddings || {},
|
|
346
|
-
apiKeyManager: this.apiKeyManager,
|
|
347
|
-
baseUrl: this.config?.backend?.baseUrl || this.aiService.baseUrl,
|
|
348
|
-
stateDir,
|
|
349
|
-
logger: this.logger,
|
|
350
|
-
});
|
|
351
|
-
await this.embeddingService.loadPersistedConfig();
|
|
352
|
-
this.aiService.setEmbeddingService(this.embeddingService);
|
|
353
|
-
this.logger.info('[Loxia] Embedding service ready', {
|
|
354
|
-
provider: this.embeddingService.config.provider,
|
|
355
|
-
enabled: this.embeddingService.isEnabled,
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
// Initialize Model Routing Services
|
|
359
|
-
this.benchmarkService = new BenchmarkService(this.config, this.logger);
|
|
360
|
-
this.modelsService = new ModelsService(this.config, this.logger);
|
|
361
|
-
this.modelRouterService = new ModelRouterService(
|
|
362
|
-
this.config,
|
|
363
|
-
this.logger,
|
|
364
|
-
this.benchmarkService,
|
|
365
|
-
this.aiService
|
|
366
|
-
);
|
|
367
|
-
|
|
368
|
-
// Set API Key Manager reference in ModelsService
|
|
369
|
-
this.modelsService.setApiKeyManager(this.apiKeyManager);
|
|
370
|
-
|
|
371
|
-
// Set ModelsService reference in AI Service (for model suggestions on errors)
|
|
372
|
-
this.aiService.setModelsService(this.modelsService);
|
|
373
|
-
|
|
374
|
-
// Set ModelsService reference in BudgetService (for dynamic pricing lookup)
|
|
375
|
-
this.budgetService.setModelsService(this.modelsService);
|
|
376
|
-
|
|
377
|
-
// Initialize services
|
|
378
|
-
await this.benchmarkService.initialize();
|
|
379
|
-
await this.modelsService.initialize();
|
|
380
|
-
|
|
381
|
-
// Message Processor
|
|
382
|
-
this.messageProcessor = new MessageProcessor(
|
|
383
|
-
this.config,
|
|
384
|
-
this.logger,
|
|
385
|
-
this.toolsRegistry,
|
|
386
|
-
this.agentPool,
|
|
387
|
-
this.contextManager,
|
|
388
|
-
this.aiService,
|
|
389
|
-
this.modelRouterService,
|
|
390
|
-
this.modelsService
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
// Agent Scheduler - NEW ARCHITECTURE
|
|
394
|
-
this.agentScheduler = new AgentScheduler(
|
|
395
|
-
this.agentPool,
|
|
396
|
-
this.messageProcessor,
|
|
397
|
-
this.aiService,
|
|
398
|
-
this.logger,
|
|
399
|
-
null, // webSocketManager will be set later
|
|
400
|
-
this.modelRouterService,
|
|
401
|
-
this.modelsService
|
|
402
|
-
);
|
|
403
|
-
|
|
404
|
-
// Note: Scheduler will be started after WebSocketManager is initialized
|
|
405
|
-
|
|
406
|
-
// Orchestrator
|
|
407
|
-
this.orchestrator = new Orchestrator(
|
|
408
|
-
this.config,
|
|
409
|
-
this.logger,
|
|
410
|
-
this.agentPool,
|
|
411
|
-
this.messageProcessor,
|
|
412
|
-
this.aiService,
|
|
413
|
-
this.stateManager
|
|
414
|
-
);
|
|
415
|
-
|
|
416
|
-
// Expose toolsRegistry on the orchestrator so downstream wiring (e.g.
|
|
417
|
-
// webServer's flowExecutor init at webServer.js:574 → jobDoneTool
|
|
418
|
-
// setFlowExecutor) can reach it through the orchestrator handle. Without
|
|
419
|
-
// this, `this.orchestrator?.toolsRegistry` is undefined at wiring time
|
|
420
|
-
// and JobDoneTool's flow-contract validation (Phase 8) silently no-ops
|
|
421
|
-
// for the entire process lifetime.
|
|
422
|
-
this.orchestrator.toolsRegistry = this.toolsRegistry;
|
|
423
|
-
|
|
424
|
-
// Set cross-references between components
|
|
425
|
-
this.messageProcessor.orchestrator = this.orchestrator;
|
|
426
|
-
this.messageProcessor.setScheduler(this.agentScheduler);
|
|
427
|
-
this.agentPool.setMessageProcessor(this.messageProcessor);
|
|
428
|
-
this.agentPool.setScheduler(this.agentScheduler);
|
|
429
|
-
this.agentPool.setFileAttachmentService(this.fileAttachmentService);
|
|
430
|
-
|
|
431
|
-
// Attach FileAttachmentService to orchestrator for webServer access
|
|
432
|
-
this.orchestrator.fileAttachmentService = this.fileAttachmentService;
|
|
433
|
-
|
|
434
|
-
// Wire ScheduleService dependencies
|
|
435
|
-
this.scheduleService.setAgentPool(this.agentPool);
|
|
436
|
-
this.scheduleService.setMessageProcessor(this.messageProcessor);
|
|
437
|
-
this.scheduleService.setOrchestrator(this.orchestrator);
|
|
438
|
-
|
|
439
|
-
this.logger.info('Core components initialized');
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Initialize tools system
|
|
444
|
-
* @private
|
|
445
|
-
*/
|
|
446
|
-
async initializeTools() {
|
|
447
|
-
this.logger.info('Initializing tools...');
|
|
448
|
-
|
|
449
|
-
// Register Agent Delay Tool
|
|
450
|
-
await this.toolsRegistry.registerTool(AgentDelayTool);
|
|
451
|
-
|
|
452
|
-
// Register Terminal Tool
|
|
453
|
-
await this.toolsRegistry.registerTool(TerminalTool);
|
|
454
|
-
|
|
455
|
-
// Register File System Tool
|
|
456
|
-
await this.toolsRegistry.registerTool(FileSystemTool);
|
|
457
|
-
|
|
458
|
-
// Register Job Done Tool
|
|
459
|
-
await this.toolsRegistry.registerTool(JobDoneTool);
|
|
460
|
-
|
|
461
|
-
// Register Agent Communication Tool
|
|
462
|
-
await this.toolsRegistry.registerTool(AgentCommunicationTool);
|
|
463
|
-
|
|
464
|
-
// Register Task Manager Tool
|
|
465
|
-
await this.toolsRegistry.registerTool(TaskManagerTool);
|
|
466
|
-
|
|
467
|
-
// Register Import Analyzer Tool
|
|
468
|
-
await this.toolsRegistry.registerTool(ImportAnalyzerTool);
|
|
469
|
-
|
|
470
|
-
// Register Dependency Resolver Tool
|
|
471
|
-
await this.toolsRegistry.registerTool(DependencyResolverTool);
|
|
472
|
-
|
|
473
|
-
// Register Image Generation Tool
|
|
474
|
-
await this.toolsRegistry.registerTool(ImageTool);
|
|
475
|
-
|
|
476
|
-
// Register Video Generation Tool (Sora 2)
|
|
477
|
-
await this.toolsRegistry.registerTool(VideoTool);
|
|
478
|
-
|
|
479
|
-
// Register Static Analysis Tool
|
|
480
|
-
await this.toolsRegistry.registerTool(StaticAnalysisTool);
|
|
481
|
-
|
|
482
|
-
// Register Clone Detection Tool
|
|
483
|
-
await this.toolsRegistry.registerTool(CloneDetectionTool);
|
|
484
|
-
|
|
485
|
-
// Register File Tree Tool
|
|
486
|
-
await this.toolsRegistry.registerTool(FileTreeTool);
|
|
487
|
-
|
|
488
|
-
// Register File Content Replace Tool
|
|
489
|
-
await this.toolsRegistry.registerTool(FileContentReplaceTool);
|
|
490
|
-
|
|
491
|
-
// Register Seek Tool
|
|
492
|
-
await this.toolsRegistry.registerTool(SeekTool);
|
|
493
|
-
|
|
494
|
-
// Register Web Tool
|
|
495
|
-
await this.toolsRegistry.registerTool(WebTool);
|
|
496
|
-
|
|
497
|
-
// Register Foundry Web Search Tool (grounded, with citations).
|
|
498
|
-
// Distinct from WebTool: WebTool drives a real browser (puppeteer)
|
|
499
|
-
// for raw results / authenticated sites; FoundryWebSearchTool
|
|
500
|
-
// calls /llm/web-search which uses Azure AI Foundry's
|
|
501
|
-
// web_search_preview tool — single round-trip, synthesized answer
|
|
502
|
-
// with citation URLs. See docs/FOUNDRY_WEB_SEARCH_PROVISIONING.md
|
|
503
|
-
// on the backend side; the tool returns a clear "not provisioned"
|
|
504
|
-
// error until that's done.
|
|
505
|
-
await this.toolsRegistry.registerTool(FoundryWebSearchTool);
|
|
506
|
-
|
|
507
|
-
// Register Visual Editor Tool
|
|
508
|
-
await this.toolsRegistry.registerTool(VisualEditorTool);
|
|
509
|
-
|
|
510
|
-
// Register PDF Tool
|
|
511
|
-
await this.toolsRegistry.registerTool(PdfTool);
|
|
512
|
-
|
|
513
|
-
// Register Help Tool (two-layer tool description system)
|
|
514
|
-
await this.toolsRegistry.registerTool(HelpTool);
|
|
515
|
-
|
|
516
|
-
// Register Document (DOCX) Tool — legacy; superseded by OfficeTool.
|
|
517
|
-
// Kept for one release cycle so existing agent configs that reference
|
|
518
|
-
// toolId "doc" continue to work.
|
|
519
|
-
await this.toolsRegistry.registerTool(DocxTool);
|
|
520
|
-
|
|
521
|
-
// Register Spreadsheet (Excel) Tool — legacy; superseded by OfficeTool.
|
|
522
|
-
await this.toolsRegistry.registerTool(ExcelTool);
|
|
523
|
-
|
|
524
|
-
// Register Office Tool — consolidated doc/sheet/pres surface
|
|
525
|
-
// (Word + Excel + PowerPoint create/read).
|
|
526
|
-
await this.toolsRegistry.registerTool(OfficeTool);
|
|
527
|
-
|
|
528
|
-
// Register Memory Tool
|
|
529
|
-
await this.toolsRegistry.registerTool(MemoryTool);
|
|
530
|
-
await this.toolsRegistry.registerTool(SkillsTool);
|
|
531
|
-
await this.toolsRegistry.registerTool(VisionTool);
|
|
532
|
-
|
|
533
|
-
// Register Composio integration tool (off by default — agents
|
|
534
|
-
// must have the `composio` capability AND the operator must set
|
|
535
|
-
// COMPOSIO_API_KEY for it to do anything).
|
|
536
|
-
await this.toolsRegistry.registerTool(ComposioTool);
|
|
537
|
-
|
|
538
|
-
// Register User Prompt Tool
|
|
539
|
-
await this.toolsRegistry.registerTool(UserPromptTool);
|
|
540
|
-
|
|
541
|
-
// Register Code Map Tool
|
|
542
|
-
await this.toolsRegistry.registerTool(CodeMapTool);
|
|
543
|
-
|
|
544
|
-
// Register WhatsApp Tool
|
|
545
|
-
await this.toolsRegistry.registerTool(WhatsAppTool);
|
|
546
|
-
|
|
547
|
-
// Register Platform Control Tool — agent-facing platform control
|
|
548
|
-
// (currently: scheduled tasks; per-agent permission, default DISABLED).
|
|
549
|
-
await this.toolsRegistry.registerTool(PlatformControlTool);
|
|
550
|
-
|
|
551
|
-
// Register Desktop Tool — keyboard/mouse/screen/window control with
|
|
552
|
-
// visual grounding (Kimi K2.6 by default). Per-agent allowlist with
|
|
553
|
-
// an EMPTY default, so no agent can drive the desktop until the
|
|
554
|
-
// operator explicitly opts in via toolConfig.desktop.allowedActions.
|
|
555
|
-
// Global kill switch: LOXIA_DESKTOP_TOOL_DISABLED=1.
|
|
556
|
-
await this.toolsRegistry.registerTool(DesktopTool);
|
|
557
|
-
|
|
558
|
-
// widget-module: remove this block if the module is deleted.
|
|
559
|
-
// Registers the WidgetTool for agent-facing custom-UI rendering.
|
|
560
|
-
// Honors LOXIA_DISABLE_WIDGETS=1 env flag for a zero-source kill switch.
|
|
561
|
-
const widgetModule = await import('./modules/widget/index.js');
|
|
562
|
-
if (!widgetModule.isDisabled()) {
|
|
563
|
-
await this.toolsRegistry.registerTool(widgetModule.WidgetTool);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// Set ToolsRegistry dependency for HelpTool (two-layer tool description system)
|
|
567
|
-
const helpTool = this.toolsRegistry.getTool('help');
|
|
568
|
-
if (helpTool && typeof helpTool.setToolsRegistry === 'function') {
|
|
569
|
-
helpTool.setToolsRegistry(this.toolsRegistry);
|
|
570
|
-
this.logger.info('ToolsRegistry set for Help Tool');
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Set AgentPool dependency for AgentDelayTool
|
|
574
|
-
const agentDelayTool = this.toolsRegistry.getTool('agentdelay');
|
|
575
|
-
if (agentDelayTool && typeof agentDelayTool.setAgentPool === 'function') {
|
|
576
|
-
agentDelayTool.setAgentPool(this.agentPool);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// Wire ScheduleService into PlatformControlTool. Without this, the
|
|
580
|
-
// tool reports "ScheduleService is not available" for any feature
|
|
581
|
-
// action — useful as a defensive default but not what we want here.
|
|
582
|
-
const platformControlTool = this.toolsRegistry.getTool('platformcontrol');
|
|
583
|
-
if (platformControlTool) {
|
|
584
|
-
if (typeof platformControlTool.setScheduleService === 'function') {
|
|
585
|
-
platformControlTool.setScheduleService(this.scheduleService);
|
|
586
|
-
}
|
|
587
|
-
// Agent + team CRUD requires AgentPool, StateManager, MemoryService.
|
|
588
|
-
if (typeof platformControlTool.setAgentPool === 'function') {
|
|
589
|
-
platformControlTool.setAgentPool(this.agentPool);
|
|
590
|
-
}
|
|
591
|
-
if (typeof platformControlTool.setStateManager === 'function') {
|
|
592
|
-
platformControlTool.setStateManager(this.stateManager);
|
|
593
|
-
}
|
|
594
|
-
// Memory tool's team-pool actions (publish/search/unpublish) need
|
|
595
|
-
// StateManager for the team-membership ACL check. The wiring is
|
|
596
|
-
// optional: when StateManager isn't attached, team-scoped actions
|
|
597
|
-
// refuse cleanly rather than defaulting open.
|
|
598
|
-
const memoryTool = this.toolsRegistry?.getTool?.('memory');
|
|
599
|
-
if (memoryTool && typeof memoryTool.setStateManager === 'function') {
|
|
600
|
-
memoryTool.setStateManager(this.stateManager);
|
|
601
|
-
}
|
|
602
|
-
if (typeof platformControlTool.setMemoryService === 'function') {
|
|
603
|
-
const { getMemoryService } = await import('./services/memoryService.js');
|
|
604
|
-
platformControlTool.setMemoryService(getMemoryService(this.logger));
|
|
605
|
-
}
|
|
606
|
-
// Flow CRUD + execution requires the FlowExecutor (created later in
|
|
607
|
-
// the boot sequence by the webServer). The webServer init also
|
|
608
|
-
// calls setFlowExecutor on this tool when it spins up — so this
|
|
609
|
-
// call is the one that fires when the executor is already alive
|
|
610
|
-
// (rare during startup), and the webServer call covers the normal
|
|
611
|
-
// path. Both are idempotent.
|
|
612
|
-
if (typeof platformControlTool.setFlowExecutor === 'function' && this.flowExecutor) {
|
|
613
|
-
platformControlTool.setFlowExecutor(this.flowExecutor);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// Set AgentPool dependency for JobDoneTool
|
|
618
|
-
const jobDoneTool = this.toolsRegistry.getTool('jobdone');
|
|
619
|
-
if (jobDoneTool && typeof jobDoneTool.setAgentPool === 'function') {
|
|
620
|
-
jobDoneTool.setAgentPool(this.agentPool);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// Set AgentPool and Scheduler dependencies for TaskManagerTool
|
|
624
|
-
const taskManagerTool = this.toolsRegistry.getTool('taskmanager');
|
|
625
|
-
if (taskManagerTool && typeof taskManagerTool.setAgentPool === 'function') {
|
|
626
|
-
taskManagerTool.setAgentPool(this.agentPool);
|
|
627
|
-
}
|
|
628
|
-
if (taskManagerTool && typeof taskManagerTool.setScheduler === 'function') {
|
|
629
|
-
taskManagerTool.setScheduler(this.scheduler);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Note: AgentCommunicationTool receives agentPool through execution context
|
|
633
|
-
// No need to set it directly as it's passed in the context parameter
|
|
634
|
-
const agentCommTool = this.toolsRegistry.getTool('agentcommunication');
|
|
635
|
-
if (agentCommTool) {
|
|
636
|
-
this.logger.info('Agent Communication Tool registered successfully');
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// Set AIService dependency for ImageTool
|
|
640
|
-
const imageTool = this.toolsRegistry.getTool('image-gen');
|
|
641
|
-
if (imageTool && typeof imageTool.setAIService === 'function') {
|
|
642
|
-
imageTool.setAIService(this.aiService);
|
|
643
|
-
this.logger.info('AIService set for Image Generation Tool');
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// Set AgentPool dependency for ImageTool (for conversation history persistence)
|
|
647
|
-
if (imageTool && typeof imageTool.setAgentPool === 'function') {
|
|
648
|
-
imageTool.setAgentPool(this.agentPool);
|
|
649
|
-
this.logger.info('AgentPool set for Image Generation Tool');
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Set dependencies for VisionTool
|
|
653
|
-
const visionTool = this.toolsRegistry.getTool('vision');
|
|
654
|
-
if (visionTool && typeof visionTool.setAIService === 'function') {
|
|
655
|
-
visionTool.setAIService(this.aiService);
|
|
656
|
-
if (this.modelsService) visionTool.setModelsService(this.modelsService);
|
|
657
|
-
if (this.agentPool) visionTool.setAgentPool(this.agentPool);
|
|
658
|
-
this.logger.info('Dependencies set for Vision Tool');
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// Video generation (Sora 2)
|
|
662
|
-
const videoTool = this.toolsRegistry.getTool('video-gen');
|
|
663
|
-
if (videoTool && typeof videoTool.setAIService === 'function') {
|
|
664
|
-
videoTool.setAIService(this.aiService);
|
|
665
|
-
this.logger.info('AIService set for Video Generation Tool');
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// Foundry web search — uses /llm/web-search backed by Azure AI
|
|
669
|
-
// Foundry + Grounding with Bing Search. Calls aiService.foundryWebSearch
|
|
670
|
-
// for the auth + URL plumbing.
|
|
671
|
-
const foundryWebTool = this.toolsRegistry.getTool('foundry_web_search');
|
|
672
|
-
if (foundryWebTool && typeof foundryWebTool.setAIService === 'function') {
|
|
673
|
-
foundryWebTool.setAIService(this.aiService);
|
|
674
|
-
this.logger.info('AIService set for Foundry Web Search Tool');
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// Desktop tool — needs aiService for proxy-mode grounding auth
|
|
678
|
-
// (baseUrl + per-session Loxia key from apiKeyManager).
|
|
679
|
-
const desktopTool = this.toolsRegistry.getTool('desktop');
|
|
680
|
-
if (desktopTool && typeof desktopTool.setAIService === 'function') {
|
|
681
|
-
desktopTool.setAIService(this.aiService);
|
|
682
|
-
this.logger.info('AIService set for Desktop Tool');
|
|
683
|
-
}
|
|
684
|
-
if (videoTool && typeof videoTool.setAgentPool === 'function') {
|
|
685
|
-
videoTool.setAgentPool(this.agentPool);
|
|
686
|
-
this.logger.info('AgentPool set for Video Generation Tool');
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// Set WhatsApp Service dependency for WhatsApp Tool
|
|
690
|
-
const whatsappTool = this.toolsRegistry.getTool('whatsapp');
|
|
691
|
-
if (whatsappTool && typeof whatsappTool.setWhatsAppService === 'function') {
|
|
692
|
-
whatsappTool.setWhatsAppService(this.whatsappService);
|
|
693
|
-
this.logger.info('WhatsAppService set for WhatsApp Tool');
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
// ─── Catch-all aiService wiring ────────────────────────────────
|
|
697
|
-
//
|
|
698
|
-
// Several tools read `this.aiService.getEmbeddingService()` for their
|
|
699
|
-
// semantic-search / find-action paths (memory, agentcommunication,
|
|
700
|
-
// taskmanager, skills, composio) but have no `setAIService` method
|
|
701
|
-
// and were never wired by the explicit calls above. Production silently
|
|
702
|
-
// returned `this.aiService === undefined` → `_getIndexer()` returned
|
|
703
|
-
// null → every embedding-backed action surfaced "Embeddings are not
|
|
704
|
-
// enabled" even when embeddings WERE enabled (the user just hit this
|
|
705
|
-
// on composio.find-action; auto-recall in agentScheduler uses a
|
|
706
|
-
// different code path so it kept working, masking the wider gap).
|
|
707
|
-
//
|
|
708
|
-
// We sweep every registered tool here, AFTER the explicit per-tool
|
|
709
|
-
// wiring above. Tools that already have aiService set (image, vision,
|
|
710
|
-
// video, foundry-web-search, desktop) are skipped; everyone else
|
|
711
|
-
// gets it now. The assignment is harmless for tools that ignore the
|
|
712
|
-
// property.
|
|
713
|
-
{
|
|
714
|
-
let wired = 0;
|
|
715
|
-
for (const tool of this.toolsRegistry.tools.values()) {
|
|
716
|
-
if (!tool.aiService) {
|
|
717
|
-
tool.aiService = this.aiService;
|
|
718
|
-
wired++;
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
this.logger.info('AIService wired onto remaining tools', { count: wired });
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
const toolCapabilities = this.toolsRegistry.getToolCapabilities();
|
|
725
|
-
this.logger.info('Tools initialized', {
|
|
726
|
-
toolCount: Object.keys(toolCapabilities).length,
|
|
727
|
-
enabledTools: Object.keys(toolCapabilities).filter(id => toolCapabilities[id].capabilities.enabled),
|
|
728
|
-
registeredTools: this.toolsRegistry.listTools()
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
// Log tool descriptions for debugging
|
|
732
|
-
if (this.logger.level === 'debug') {
|
|
733
|
-
for (const [toolId, tool] of Object.entries(toolCapabilities)) {
|
|
734
|
-
this.logger.debug(`Tool ${toolId} capabilities`, tool.capabilities);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
/**
|
|
740
|
-
* Initialize interface handlers
|
|
741
|
-
* @private
|
|
742
|
-
*/
|
|
743
|
-
async initializeInterfaces(
|
|
744
|
-
this.logger.info('Initializing interfaces...');
|
|
745
|
-
|
|
746
|
-
const interfaceConfig = this.config.interfaces || {};
|
|
747
|
-
const uiMode = process.env.LOXIA_UI_MODE || 'cli'; // Default to old CLI
|
|
748
|
-
|
|
749
|
-
// CLI Interface - Load old readline CLI (unless terminal UI mode is specified)
|
|
750
|
-
// NOTE: Terminal UI mode runs as a separate WebSocket client, not in the main process
|
|
751
|
-
if (interfaceConfig.cli?.enabled !== false && uiMode !== 'terminal') {
|
|
752
|
-
// Use old CLI (readline-based)
|
|
753
|
-
this.logger.info('Loading CLI (readline)...');
|
|
754
|
-
const { default: CLIInterface } = await import('./interfaces/cli.js');
|
|
755
|
-
const cliInterface = new CLIInterface(
|
|
756
|
-
this.orchestrator,
|
|
757
|
-
this.logger,
|
|
758
|
-
interfaceConfig.cli || {}
|
|
759
|
-
);
|
|
760
|
-
|
|
761
|
-
await cliInterface.initialize();
|
|
762
|
-
this.interfaces.set(INTERFACE_TYPES.CLI, cliInterface);
|
|
763
|
-
|
|
764
|
-
this.logger.info('CLI interface initialized');
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
// If terminal UI mode, skip CLI - Terminal UI will connect as WebSocket client
|
|
768
|
-
if (uiMode === 'terminal') {
|
|
769
|
-
this.logger.info('Terminal UI mode: Server-only startup (Terminal UI will connect as WebSocket client)');
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// Web Interface - now implemented
|
|
773
|
-
if (interfaceConfig.web?.enabled !== false) {
|
|
774
|
-
// Read port from environment variables (set by CLI) or use config defaults
|
|
775
|
-
const webPort = parseInt(process.env.LOXIA_PORT || process.env.PORT, 10) || 8080;
|
|
776
|
-
// Use env var, then config, then 0.0.0.0 (accept connections from all interfaces)
|
|
777
|
-
const webHost = process.env.LOXIA_HOST || interfaceConfig.web?.host || '0.0.0.0';
|
|
778
|
-
|
|
779
|
-
const webConfig = {
|
|
780
|
-
...interfaceConfig.web,
|
|
781
|
-
port: webPort,
|
|
782
|
-
host: webHost,
|
|
783
|
-
backend: this.config.backend
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
const webServer = new WebServer(
|
|
787
|
-
this.orchestrator,
|
|
788
|
-
this.logger,
|
|
789
|
-
webConfig
|
|
790
|
-
);
|
|
791
|
-
|
|
792
|
-
// Pass toolsRegistry to webServer for the /api/tools endpoint
|
|
793
|
-
webServer.toolsRegistry = this.toolsRegistry;
|
|
794
|
-
|
|
795
|
-
// Set API Key Manager reference in Web Server
|
|
796
|
-
webServer.setApiKeyManager(this.apiKeyManager);
|
|
797
|
-
|
|
798
|
-
// Embedding service handle so the /api/embeddings/* routes can
|
|
799
|
-
// read config + telemetry, hot-swap providers, etc.
|
|
800
|
-
webServer.embeddingService = this.embeddingService;
|
|
801
|
-
|
|
802
|
-
// Set Credential Vault reference in Web Server
|
|
803
|
-
webServer.setCredentialVault(this.credentialVault);
|
|
804
|
-
|
|
805
|
-
// Set WhatsApp Service reference in Web Server
|
|
806
|
-
if (this.whatsappService) {
|
|
807
|
-
webServer.setWhatsAppService(this.whatsappService);
|
|
808
|
-
this.whatsappService.setWebSocketManager(webServer);
|
|
809
|
-
// Auto-reconnect if a previous session exists (non-blocking)
|
|
810
|
-
this.whatsappService.autoReconnect().catch(e =>
|
|
811
|
-
this.logger.warn('WhatsApp auto-reconnect failed', { error: e.message })
|
|
812
|
-
);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
// Set Telegram Service references
|
|
816
|
-
if (this.telegramService) {
|
|
817
|
-
webServer.setTelegramService(this.telegramService);
|
|
818
|
-
this.telegramService.setOrchestrator(this.orchestrator);
|
|
819
|
-
this.telegramService.setAgentPool(this.agentPool);
|
|
820
|
-
this.telegramService.setWebSocketManager(webServer);
|
|
821
|
-
if (this.flowExecutor) this.telegramService.setFlowExecutor(this.flowExecutor);
|
|
822
|
-
// Backend URL + platform API key are needed for the voice-note
|
|
823
|
-
// → /llm/transcribe path. Without these, voice messages return
|
|
824
|
-
// a friendly "voice not configured" message and the user is
|
|
825
|
-
// told to type instead. Both come from the same sources the
|
|
826
|
-
// rest of the CLI's /api/* proxy code uses.
|
|
827
|
-
const backendBaseUrl = this.config?.backend?.baseUrl
|
|
828
|
-
|| webServer?.config?.backend?.baseUrl
|
|
829
|
-
|| null;
|
|
830
|
-
if (backendBaseUrl) this.telegramService.setBackendBaseUrl(backendBaseUrl);
|
|
831
|
-
// Pass a getter (not a snapshotted value) so the key is
|
|
832
|
-
// re-resolved on every transcribe call. This lets the user
|
|
833
|
-
// sign in AFTER boot — voice notes start working immediately
|
|
834
|
-
// without restarting the server.
|
|
835
|
-
this.telegramService.setPlatformApiKey(
|
|
836
|
-
() => this.apiKeyManager?.getKeysForRequest?.(null, { platformProvided: true })?.loxiaApiKey || null
|
|
837
|
-
);
|
|
838
|
-
// Let the scheduler query bridge-state per agent so it can inject
|
|
839
|
-
// the <external> routing guidance into system prompts only when the
|
|
840
|
-
// agent is actually addressable from Telegram.
|
|
841
|
-
this.agentScheduler?.setTelegramService?.(this.telegramService);
|
|
842
|
-
// Auto-connect if token exists in config (non-blocking)
|
|
843
|
-
this.telegramService.autoConnect().catch(e =>
|
|
844
|
-
this.logger.warn('Telegram auto-connect failed', { error: e.message })
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// Set Discord Service references
|
|
849
|
-
if (this.discordService) {
|
|
850
|
-
webServer.setDiscordService(this.discordService);
|
|
851
|
-
this.discordService.setOrchestrator(this.orchestrator);
|
|
852
|
-
this.discordService.setAgentPool(this.agentPool);
|
|
853
|
-
this.discordService.setWebSocketManager(webServer);
|
|
854
|
-
if (this.flowExecutor) this.discordService.setFlowExecutor(this.flowExecutor);
|
|
855
|
-
// Same bridge-awareness hook as Telegram above — the scheduler
|
|
856
|
-
// only appends <external> guidance when `isAgentBridged(agentId)`
|
|
857
|
-
// returns true for this channel.
|
|
858
|
-
this.agentScheduler?.setDiscordService?.(this.discordService);
|
|
859
|
-
// Auto-connect if token exists in config (non-blocking)
|
|
860
|
-
this.discordService.autoConnect().catch(e =>
|
|
861
|
-
this.logger.warn('Discord auto-connect failed', { error: e.message })
|
|
862
|
-
);
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
await webServer.initialize();
|
|
866
|
-
this.interfaces.set(INTERFACE_TYPES.WEB, webServer);
|
|
867
|
-
|
|
868
|
-
// Attach WebServer to orchestrator for MessageProcessor broadcasting
|
|
869
|
-
this.orchestrator.webServer = webServer;
|
|
870
|
-
|
|
871
|
-
// Connect MessageProcessor to WebServer for real-time updates
|
|
872
|
-
this.messageProcessor.setWebSocketManager(webServer);
|
|
873
|
-
|
|
874
|
-
// Connect AgentScheduler to WebServer for real-time updates
|
|
875
|
-
this.agentScheduler.webSocketManager = webServer;
|
|
876
|
-
|
|
877
|
-
// Start the scheduler now that WebSocketManager is available
|
|
878
|
-
this.agentScheduler.start();
|
|
879
|
-
this.logger.info('Agent Scheduler started with WebSocket integration');
|
|
880
|
-
|
|
881
|
-
// Wire ScheduleService to WebServer and FlowExecutor
|
|
882
|
-
webServer.setScheduleService(this.scheduleService);
|
|
883
|
-
this.scheduleService.setWebSocketManager(webServer);
|
|
884
|
-
if (webServer.flowExecutor) {
|
|
885
|
-
this.scheduleService.setFlowExecutor(webServer.flowExecutor);
|
|
886
|
-
}
|
|
887
|
-
this.scheduleService.start();
|
|
888
|
-
this.logger.info('ScheduleService started with WebSocket integration');
|
|
889
|
-
|
|
890
|
-
// Set global reference for tools that need to broadcast
|
|
891
|
-
global.loxiaWebServer = webServer;
|
|
892
|
-
|
|
893
|
-
// Connect JobDoneTool to WebServer for broadcasting mode changes
|
|
894
|
-
const jobDoneTool = this.toolsRegistry.getTool('jobdone');
|
|
895
|
-
if (jobDoneTool && typeof jobDoneTool.setWebSocketManager === 'function') {
|
|
896
|
-
jobDoneTool.setWebSocketManager(webServer);
|
|
897
|
-
this.logger.info('WebSocketManager set for JobDone Tool');
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// Connect UserPromptTool to WebServer and AgentPool for user prompting
|
|
901
|
-
const userPromptTool = this.toolsRegistry.getTool('userprompt');
|
|
902
|
-
if (userPromptTool) {
|
|
903
|
-
if (typeof userPromptTool.setWebSocketManager === 'function') {
|
|
904
|
-
userPromptTool.setWebSocketManager(webServer);
|
|
905
|
-
}
|
|
906
|
-
if (typeof userPromptTool.setAgentPool === 'function') {
|
|
907
|
-
userPromptTool.setAgentPool(this.agentPool);
|
|
908
|
-
}
|
|
909
|
-
this.logger.info('Dependencies set for UserPrompt Tool');
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
const status = webServer.getStatus();
|
|
913
|
-
this.logger.info('Web interface initialized', { url: status.url });
|
|
914
|
-
console.log(`🌐 Server running at ${status.url}`);
|
|
915
|
-
console.log(`📱 Web UI available at: ${status.url}`);
|
|
916
|
-
|
|
917
|
-
// Auto-open browser (skip when running inside Electron — it creates its own window)
|
|
918
|
-
if (!process.env.LOXIA_ELECTRON) {
|
|
919
|
-
const url = status.url.replace('0.0.0.0', 'localhost');
|
|
920
|
-
const platform = process.platform;
|
|
921
|
-
const openCmd = platform === 'win32' ? `start "" "${url}"`
|
|
922
|
-
: platform === 'darwin' ? `open "${url}"`
|
|
923
|
-
: `xdg-open "${url}"`;
|
|
924
|
-
exec(openCmd, (err) => {
|
|
925
|
-
if (err) this.logger.debug('Could not auto-open browser', { error: err.message });
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
// VSCode Extension Interface (placeholder)
|
|
931
|
-
if (interfaceConfig.vscode?.enabled === true) {
|
|
932
|
-
this.logger.info('VSCode interface configured but not implemented yet');
|
|
933
|
-
// TODO: Initialize VSCode extension interface
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
/**
|
|
938
|
-
* Setup graceful shutdown handlers
|
|
939
|
-
* @private
|
|
940
|
-
*/
|
|
941
|
-
setupShutdownHandlers() {
|
|
942
|
-
// Handle SIGINT (Ctrl+C)
|
|
943
|
-
process.on('SIGINT', async () => {
|
|
944
|
-
console.log('\n📋 Received SIGINT, shutting down gracefully...');
|
|
945
|
-
await this.shutdown();
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
// Handle SIGTERM
|
|
949
|
-
process.on('SIGTERM', async () => {
|
|
950
|
-
console.log('\n📋 Received SIGTERM, shutting down gracefully...');
|
|
951
|
-
await this.shutdown();
|
|
952
|
-
});
|
|
953
|
-
|
|
954
|
-
// Handle uncaught exceptions
|
|
955
|
-
process.on('uncaughtException', async (error) => {
|
|
956
|
-
console.error('❌ Uncaught exception:', error);
|
|
957
|
-
if (this.logger) {
|
|
958
|
-
this.logger.error('Uncaught exception', {
|
|
959
|
-
error: error.message,
|
|
960
|
-
stack: error.stack
|
|
961
|
-
});
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
await this.shutdown();
|
|
965
|
-
process.exit(1);
|
|
966
|
-
});
|
|
967
|
-
|
|
968
|
-
// Handle unhandled promise rejections
|
|
969
|
-
process.on('unhandledRejection', async (reason, promise) => {
|
|
970
|
-
const reasonMessage = reason?.message || String(reason);
|
|
971
|
-
|
|
972
|
-
// List of known non-critical rejections that shouldn't crash the server
|
|
973
|
-
const nonCriticalPatterns = [
|
|
974
|
-
'Credential request cancelled',
|
|
975
|
-
'Credential request timed out',
|
|
976
|
-
'Target closed',
|
|
977
|
-
'Session closed',
|
|
978
|
-
'Protocol error',
|
|
979
|
-
'Navigation timeout',
|
|
980
|
-
'net::ERR_',
|
|
981
|
-
'Requesting main frame too early',
|
|
982
|
-
'Connection closed',
|
|
983
|
-
// AI/model errors should NOT crash the server
|
|
984
|
-
'HTTP 4', // 400, 401, 403, 404, 429, etc.
|
|
985
|
-
'HTTP 5', // 500, 502, 503, etc.
|
|
986
|
-
'circuit breaker',
|
|
987
|
-
'Rate limit',
|
|
988
|
-
'Insufficient credits',
|
|
989
|
-
'not suitable for chat',
|
|
990
|
-
'No API key configured',
|
|
991
|
-
'Message content is empty',
|
|
992
|
-
'Backend returned malformed',
|
|
993
|
-
'stream generator',
|
|
994
|
-
'No response choices',
|
|
995
|
-
'model error',
|
|
996
|
-
'isModelError',
|
|
997
|
-
'Service temporarily unavailable',
|
|
998
|
-
'Failed to fetch models',
|
|
999
|
-
'ECONNREFUSED',
|
|
1000
|
-
'ETIMEDOUT',
|
|
1001
|
-
'ECONNRESET',
|
|
1002
|
-
'fetch failed',
|
|
1003
|
-
'AbortError',
|
|
1004
|
-
'The operation was aborted'
|
|
1005
|
-
];
|
|
1006
|
-
|
|
1007
|
-
const isNonCritical = nonCriticalPatterns.some(pattern =>
|
|
1008
|
-
reasonMessage.includes(pattern)
|
|
1009
|
-
);
|
|
1010
|
-
|
|
1011
|
-
if (isNonCritical) {
|
|
1012
|
-
console.warn('⚠️ Non-critical promise rejection (server continues):', reasonMessage);
|
|
1013
|
-
if (this.logger) {
|
|
1014
|
-
this.logger.warn('Non-critical promise rejection', {
|
|
1015
|
-
reason: reasonMessage,
|
|
1016
|
-
promise: promise.toString()
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
return; // Don't crash for non-critical errors
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
console.error('❌ Unhandled promise rejection:', reason);
|
|
1023
|
-
if (this.logger) {
|
|
1024
|
-
this.logger.error('Unhandled promise rejection', {
|
|
1025
|
-
reason: reasonMessage,
|
|
1026
|
-
promise: promise.toString()
|
|
1027
|
-
});
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
await this.shutdown();
|
|
1031
|
-
process.exit(1);
|
|
1032
|
-
});
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
/**
|
|
1036
|
-
* Gracefully shutdown the application
|
|
1037
|
-
* @returns {Promise<void>}
|
|
1038
|
-
*/
|
|
1039
|
-
async shutdown() {
|
|
1040
|
-
if (this.isShuttingDown) {
|
|
1041
|
-
return;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
this.isShuttingDown = true;
|
|
1045
|
-
|
|
1046
|
-
// Force-exit safety net: if graceful shutdown hangs, kill the process after 10s
|
|
1047
|
-
const forceExitTimer = setTimeout(() => {
|
|
1048
|
-
console.error('⚠️ Graceful shutdown timed out after 10s — forcing exit');
|
|
1049
|
-
process.exit(1);
|
|
1050
|
-
}, 10000);
|
|
1051
|
-
forceExitTimer.unref(); // Don't let this timer keep the process alive on its own
|
|
1052
|
-
|
|
1053
|
-
try {
|
|
1054
|
-
console.log('🛑 Shutting down Loxia Autopilot One...');
|
|
1055
|
-
|
|
1056
|
-
if (this.logger) {
|
|
1057
|
-
this.logger.info('Application shutdown initiated');
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
// Stop schedule service first
|
|
1061
|
-
if (this.scheduleService) {
|
|
1062
|
-
this.scheduleService.stop();
|
|
1063
|
-
this.logger?.info('Schedule service stopped');
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
// Stop agent scheduler (prevents new work from starting mid-shutdown)
|
|
1067
|
-
if (this.agentScheduler) {
|
|
1068
|
-
this.agentScheduler.stop();
|
|
1069
|
-
this.logger?.info('Agent scheduler stopped');
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
// Cancel any pending model fetch retries
|
|
1073
|
-
if (this.modelsService?._cancelRetry) {
|
|
1074
|
-
this.modelsService._cancelRetry();
|
|
1075
|
-
this.logger?.info('Models service retries cancelled');
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
// Close Puppeteer browser (webTool) — it holds DevTools ports
|
|
1079
|
-
if (this.toolsRegistry) {
|
|
1080
|
-
for (const toolId of ['web']) {
|
|
1081
|
-
try {
|
|
1082
|
-
const tool = this.toolsRegistry.getTool(toolId);
|
|
1083
|
-
if (tool?.cleanup) {
|
|
1084
|
-
await tool.cleanup();
|
|
1085
|
-
this.logger?.info(`${toolId} tool cleanup complete`);
|
|
1086
|
-
}
|
|
1087
|
-
} catch (error) {
|
|
1088
|
-
this.logger?.warn(`Failed to cleanup ${toolId} tool`, { error: error.message });
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
// Kill ALL running terminal processes across all agents
|
|
1094
|
-
if (this.toolsRegistry) {
|
|
1095
|
-
try {
|
|
1096
|
-
const terminalTool = this.toolsRegistry.getTool('terminal');
|
|
1097
|
-
if (terminalTool?.commandTracker) {
|
|
1098
|
-
let killed = 0;
|
|
1099
|
-
for (const [
|
|
1100
|
-
if (cmdInfo.process && cmdInfo.state === 'RUNNING') {
|
|
1101
|
-
try {
|
|
1102
|
-
cmdInfo.process.kill('SIGTERM');
|
|
1103
|
-
killed++;
|
|
1104
|
-
} catch { /* already dead */ }
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
if (killed > 0) {
|
|
1108
|
-
this.logger?.info(`Killed ${killed} running terminal process(es) on shutdown`);
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
} catch (error) {
|
|
1112
|
-
this.logger?.warn('Failed to cleanup terminal processes', { error: error.message });
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
// Shutdown interfaces (web server, visual editor, WS connections)
|
|
1117
|
-
for (const [type, interface_] of this.interfaces) {
|
|
1118
|
-
try {
|
|
1119
|
-
if (interface_.shutdown) {
|
|
1120
|
-
await interface_.shutdown();
|
|
1121
|
-
}
|
|
1122
|
-
this.logger?.info(`${type} interface shutdown complete`);
|
|
1123
|
-
} catch (error) {
|
|
1124
|
-
console.error(`Failed to shutdown ${type} interface:`, error.message);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
// Shutdown async tool manager
|
|
1129
|
-
if (this.asyncToolManager) {
|
|
1130
|
-
await this.asyncToolManager.shutdown();
|
|
1131
|
-
this.logger?.info('Async tool manager shutdown complete');
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
// Shutdown orchestrator (persists agent states)
|
|
1135
|
-
if (this.orchestrator) {
|
|
1136
|
-
await this.orchestrator.shutdown();
|
|
1137
|
-
this.logger?.info('Orchestrator shutdown complete');
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
// Cleanup configuration manager
|
|
1141
|
-
if (this.configManager) {
|
|
1142
|
-
this.configManager.cleanup();
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
// Close logger
|
|
1146
|
-
if (this.logger) {
|
|
1147
|
-
await this.logger.close();
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
console.log('✅ Loxia Autopilot One shutdown complete');
|
|
1151
|
-
|
|
1152
|
-
} catch (error) {
|
|
1153
|
-
console.error('❌ Error during shutdown:', error.message);
|
|
1154
|
-
} finally {
|
|
1155
|
-
process.exit(0);
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
/**
|
|
1160
|
-
* Get application status
|
|
1161
|
-
* @returns {Object} Application status
|
|
1162
|
-
*/
|
|
1163
|
-
getStatus() {
|
|
1164
|
-
return {
|
|
1165
|
-
version: SYSTEM_VERSION,
|
|
1166
|
-
uptime: process.uptime(),
|
|
1167
|
-
memoryUsage: process.memoryUsage(),
|
|
1168
|
-
interfaces: Array.from(this.interfaces.keys()),
|
|
1169
|
-
isShuttingDown: this.isShuttingDown
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
/**
|
|
1175
|
-
* Main application entry point
|
|
1176
|
-
*/
|
|
1177
|
-
async function main() {
|
|
1178
|
-
const app = new LoxiaApplication();
|
|
1179
|
-
|
|
1180
|
-
// Parse command line arguments
|
|
1181
|
-
const args = process.argv.slice(2);
|
|
1182
|
-
const options = {
|
|
1183
|
-
projectDir: process.cwd(),
|
|
1184
|
-
watchConfig: args.includes('--watch-config'),
|
|
1185
|
-
configPaths: []
|
|
1186
|
-
};
|
|
1187
|
-
|
|
1188
|
-
// Parse --port and --host from argv so they work when running index.js directly
|
|
1189
|
-
// (bin/cli.js sets these as env vars, but npm start / node src/index.js bypasses cli.js)
|
|
1190
|
-
for (let i = 0; i < args.length; i++) {
|
|
1191
|
-
if (args[i] === '--port' && args[i + 1]) {
|
|
1192
|
-
process.env.LOXIA_PORT = args[i + 1];
|
|
1193
|
-
}
|
|
1194
|
-
if (args[i] === '--host' && args[i + 1]) {
|
|
1195
|
-
process.env.LOXIA_HOST = args[i + 1];
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
// Look for custom config file
|
|
1200
|
-
const configIndex = args.indexOf('--config');
|
|
1201
|
-
if (configIndex !== -1 && args[configIndex + 1]) {
|
|
1202
|
-
options.configPaths.push(args[configIndex + 1]);
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
await app.initialize(options);
|
|
1206
|
-
|
|
1207
|
-
// Keep the application running
|
|
1208
|
-
return app;
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
// Start the application if this file is run directly.
|
|
1212
|
-
const __filename = resolveModuleFilename(import.meta.url);
|
|
1213
|
-
if (process.argv[1] === __filename) {
|
|
1214
|
-
console.log('🚀 Starting Loxia Autopilot One...');
|
|
1215
|
-
main().catch(error => {
|
|
1216
|
-
console.error('❌ Failed to start Loxia Autopilot One:', error.message);
|
|
1217
|
-
console.error('Stack trace:', error.stack);
|
|
1218
|
-
process.exit(1);
|
|
1219
|
-
});
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Loxia Autopilot One - Main Application Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Purpose:
|
|
5
|
+
* - Initialize all system components
|
|
6
|
+
* - Setup dependency injection
|
|
7
|
+
* - Start interface handlers
|
|
8
|
+
* - Handle graceful shutdown
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { exec } from 'child_process';
|
|
13
|
+
import { createLogger } from './utilities/logger.js';
|
|
14
|
+
import { resolveModuleFilename } from './utilities/esmCjsPath.js';
|
|
15
|
+
import { createConfigManager } from './utilities/configManager.js';
|
|
16
|
+
import Orchestrator from './core/orchestrator.js';
|
|
17
|
+
import AgentPool from './core/agentPool.js';
|
|
18
|
+
import MessageProcessor from './core/messageProcessor.js';
|
|
19
|
+
import AgentScheduler from './core/agentScheduler.js';
|
|
20
|
+
import ContextManager from './core/contextManager.js';
|
|
21
|
+
import StateManager from './core/stateManager.js';
|
|
22
|
+
import AIService from './services/aiService.js';
|
|
23
|
+
import { EmbeddingService } from './services/embeddings/embeddingService.js';
|
|
24
|
+
import { getUserDataPaths } from './utilities/userDataDir.js';
|
|
25
|
+
import BudgetService from './services/budgetService.js';
|
|
26
|
+
import ErrorHandler from './services/errorHandler.js';
|
|
27
|
+
import BenchmarkService from './services/benchmarkService.js';
|
|
28
|
+
import ModelRouterService from './services/modelRouterService.js';
|
|
29
|
+
import ModelsService from './services/modelsService.js';
|
|
30
|
+
import ApiKeyManager, { registerApiKeyManager } from './services/apiKeyManager.js';
|
|
31
|
+
import { getCredentialVault } from './services/credentialVault.js';
|
|
32
|
+
import FileAttachmentService from './services/fileAttachmentService.js';
|
|
33
|
+
import { ToolsRegistry } from './tools/baseTool.js';
|
|
34
|
+
import AgentDelayTool from './tools/agentDelayTool.js';
|
|
35
|
+
import TerminalTool from './tools/terminalTool.js';
|
|
36
|
+
import FileSystemTool from './tools/fileSystemTool.js';
|
|
37
|
+
import JobDoneTool from './tools/jobDoneTool.js';
|
|
38
|
+
import AgentCommunicationTool from './tools/agentCommunicationTool.js';
|
|
39
|
+
import TaskManagerTool from './tools/taskManagerTool.js';
|
|
40
|
+
import ImportAnalyzerTool from './tools/importAnalyzerTool.js';
|
|
41
|
+
import DependencyResolverTool from './tools/dependencyResolverTool.js';
|
|
42
|
+
import ImageTool from './tools/imageTool.js';
|
|
43
|
+
import VideoTool from './tools/videoTool.js';
|
|
44
|
+
import StaticAnalysisTool from './tools/staticAnalysisTool.js';
|
|
45
|
+
import CloneDetectionTool from './tools/cloneDetectionTool.js';
|
|
46
|
+
import FileTreeTool from './tools/fileTreeTool.js';
|
|
47
|
+
import FileContentReplaceTool from './tools/fileContentReplaceTool.js';
|
|
48
|
+
import SeekTool from './tools/seekTool.js';
|
|
49
|
+
import WebTool from './tools/webTool.js';
|
|
50
|
+
import FoundryWebSearchTool from './tools/foundryWebSearchTool.js';
|
|
51
|
+
import VisualEditorTool from './tools/visualEditorTool.js';
|
|
52
|
+
import PdfTool from './tools/pdfTool.js';
|
|
53
|
+
import HelpTool from './tools/helpTool.js';
|
|
54
|
+
import DocxTool from './tools/docxTool.js';
|
|
55
|
+
import ExcelTool from './tools/excelTool.js';
|
|
56
|
+
import OfficeTool from './tools/officeTool.js';
|
|
57
|
+
import MemoryTool from './tools/memoryTool.js';
|
|
58
|
+
import ComposioTool from './tools/composioTool.js';
|
|
59
|
+
import SkillsTool from './tools/skillsTool.js';
|
|
60
|
+
import VisionTool from './tools/visionTool.js';
|
|
61
|
+
import UserPromptTool from './tools/userPromptTool.js';
|
|
62
|
+
import CodeMapTool from './tools/codeMapTool.js';
|
|
63
|
+
import WhatsAppTool from './tools/whatsappTool.js';
|
|
64
|
+
import PlatformControlTool from './tools/platformControlTool.js';
|
|
65
|
+
import { DesktopTool } from './tools/desktop/DesktopTool.js';
|
|
66
|
+
import ScheduleService from './services/scheduleService.js';
|
|
67
|
+
import AsyncToolManager from './tools/asyncToolManager.js';
|
|
68
|
+
import WebServer from './interfaces/webServer.js';
|
|
69
|
+
|
|
70
|
+
import {
|
|
71
|
+
SYSTEM_VERSION,
|
|
72
|
+
INTERFACE_TYPES
|
|
73
|
+
} from './utilities/constants.js';
|
|
74
|
+
import {
|
|
75
|
+
getUserDataDir,
|
|
76
|
+
ensureUserDataDirs,
|
|
77
|
+
migrateFromOldLocation,
|
|
78
|
+
getLegacyDataPaths
|
|
79
|
+
} from './utilities/userDataDir.js';
|
|
80
|
+
|
|
81
|
+
class LoxiaApplication {
|
|
82
|
+
constructor() {
|
|
83
|
+
this.logger = null;
|
|
84
|
+
this.config = null;
|
|
85
|
+
this.orchestrator = null;
|
|
86
|
+
this.interfaces = new Map();
|
|
87
|
+
this.isShuttingDown = false;
|
|
88
|
+
|
|
89
|
+
// Bind shutdown handler
|
|
90
|
+
this.shutdown = this.shutdown.bind(this);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Initialize the application
|
|
95
|
+
* @param {Object} options - Initialization options
|
|
96
|
+
* @returns {Promise<void>}
|
|
97
|
+
*/
|
|
98
|
+
async initialize(options = {}) {
|
|
99
|
+
try {
|
|
100
|
+
console.log(`🚀 Starting Loxia Autopilot One v${SYSTEM_VERSION}`);
|
|
101
|
+
|
|
102
|
+
// Initialize configuration
|
|
103
|
+
await this.initializeConfig(options);
|
|
104
|
+
|
|
105
|
+
// Initialize logging
|
|
106
|
+
await this.initializeLogging();
|
|
107
|
+
|
|
108
|
+
this.logger.info('Loxia Autopilot One starting up', {
|
|
109
|
+
version: SYSTEM_VERSION,
|
|
110
|
+
nodeVersion: process.version,
|
|
111
|
+
platform: process.platform,
|
|
112
|
+
projectDir: options.projectDir || process.cwd()
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// IMPORTANT: Initialize persistent user data directory and migrate legacy data
|
|
116
|
+
// This ensures user data (agents, conversations, settings) survives npm updates
|
|
117
|
+
await this.initializeUserDataDirectory();
|
|
118
|
+
|
|
119
|
+
// Initialize core components
|
|
120
|
+
await this.initializeCoreComponents();
|
|
121
|
+
|
|
122
|
+
// Initialize tools
|
|
123
|
+
await this.initializeTools();
|
|
124
|
+
|
|
125
|
+
this.logger.info('Starting interface initialization...');
|
|
126
|
+
|
|
127
|
+
// Initialize interfaces
|
|
128
|
+
await this.initializeInterfaces(options);
|
|
129
|
+
|
|
130
|
+
this.logger.info('Interface initialization completed');
|
|
131
|
+
|
|
132
|
+
// Setup shutdown handlers
|
|
133
|
+
this.setupShutdownHandlers();
|
|
134
|
+
|
|
135
|
+
this.logger.info('Loxia Autopilot One startup complete');
|
|
136
|
+
console.log('✅ Loxia Autopilot One is ready!');
|
|
137
|
+
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error('❌ Failed to initialize Loxia Autopilot One:', error.message);
|
|
140
|
+
if (this.logger) {
|
|
141
|
+
this.logger.error('Application initialization failed', {
|
|
142
|
+
error: error.message,
|
|
143
|
+
stack: error.stack
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Initialize configuration management
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
async initializeConfig(options) {
|
|
155
|
+
const __filename = resolveModuleFilename(import.meta.url);
|
|
156
|
+
const __dirname = path.dirname(__filename);
|
|
157
|
+
|
|
158
|
+
const configPaths = [
|
|
159
|
+
path.join(__dirname, '../config/default.json'),
|
|
160
|
+
...(options.configPaths || [])
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
this.configManager = createConfigManager({
|
|
164
|
+
configPaths,
|
|
165
|
+
envPrefix: 'LOXIA'
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
this.config = await this.configManager.loadConfig();
|
|
169
|
+
|
|
170
|
+
// Enable config watching if requested
|
|
171
|
+
if (options.watchConfig) {
|
|
172
|
+
await this.configManager.watchConfig(true);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Initialize logging system
|
|
178
|
+
* @private
|
|
179
|
+
*/
|
|
180
|
+
async initializeLogging() {
|
|
181
|
+
const loggingConfig = this.config.logging || {};
|
|
182
|
+
|
|
183
|
+
this.logger = createLogger({
|
|
184
|
+
level: loggingConfig.level || 'info',
|
|
185
|
+
outputs: loggingConfig.outputs || ['console'],
|
|
186
|
+
colors: loggingConfig.colors !== false,
|
|
187
|
+
timestamp: loggingConfig.timestamp !== false,
|
|
188
|
+
logFile: loggingConfig.logFile,
|
|
189
|
+
maxFileSize: loggingConfig.maxFileSize,
|
|
190
|
+
maxFiles: loggingConfig.maxFiles
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await this.logger.initialize();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Initialize persistent user data directory and migrate legacy data
|
|
198
|
+
* This ensures user data survives npm package updates
|
|
199
|
+
* @private
|
|
200
|
+
*/
|
|
201
|
+
async initializeUserDataDirectory() {
|
|
202
|
+
try {
|
|
203
|
+
// Create user data directory structure
|
|
204
|
+
await ensureUserDataDirs();
|
|
205
|
+
const userDataDir = getUserDataDir();
|
|
206
|
+
|
|
207
|
+
this.logger.info('User data directory initialized', {
|
|
208
|
+
location: userDataDir,
|
|
209
|
+
platform: process.platform
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Check for legacy data and migrate if needed
|
|
213
|
+
const legacyPaths = getLegacyDataPaths();
|
|
214
|
+
for (const legacyPath of legacyPaths) {
|
|
215
|
+
try {
|
|
216
|
+
const result = await migrateFromOldLocation(legacyPath, {
|
|
217
|
+
dryRun: false,
|
|
218
|
+
logger: this.logger
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
if (result.migrated.length > 0) {
|
|
222
|
+
this.logger.info('Successfully migrated data from legacy location', {
|
|
223
|
+
from: legacyPath,
|
|
224
|
+
migratedCount: result.migrated.length
|
|
225
|
+
});
|
|
226
|
+
console.log(`📦 Migrated ${result.migrated.length} items from legacy location to ${userDataDir}`);
|
|
227
|
+
}
|
|
228
|
+
} catch (migrationError) {
|
|
229
|
+
// Non-fatal: log and continue
|
|
230
|
+
this.logger.warn('Failed to migrate from legacy location', {
|
|
231
|
+
legacyPath,
|
|
232
|
+
error: migrationError.message
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
} catch (error) {
|
|
238
|
+
this.logger.error('Failed to initialize user data directory', {
|
|
239
|
+
error: error.message
|
|
240
|
+
});
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Initialize core system components
|
|
247
|
+
* @private
|
|
248
|
+
*/
|
|
249
|
+
async initializeCoreComponents() {
|
|
250
|
+
this.logger.info('Initializing core components...');
|
|
251
|
+
|
|
252
|
+
// State Manager
|
|
253
|
+
this.stateManager = new StateManager(this.config, this.logger);
|
|
254
|
+
|
|
255
|
+
// File Attachment Service
|
|
256
|
+
this.fileAttachmentService = new FileAttachmentService(this.config, this.logger);
|
|
257
|
+
await this.fileAttachmentService.initialize();
|
|
258
|
+
|
|
259
|
+
// Context Manager
|
|
260
|
+
this.contextManager = new ContextManager(this.config, this.logger);
|
|
261
|
+
|
|
262
|
+
// Tools Registry and Async Tool Manager
|
|
263
|
+
this.toolsRegistry = new ToolsRegistry(this.logger);
|
|
264
|
+
this.asyncToolManager = new AsyncToolManager(this.config, this.logger);
|
|
265
|
+
|
|
266
|
+
// Agent Pool (with tools registry for prompt enhancement)
|
|
267
|
+
this.agentPool = new AgentPool(
|
|
268
|
+
this.config,
|
|
269
|
+
this.logger,
|
|
270
|
+
this.stateManager,
|
|
271
|
+
this.contextManager,
|
|
272
|
+
this.toolsRegistry
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// Initialize Budget Service and Error Handler
|
|
276
|
+
this.budgetService = new BudgetService(this.config, this.logger);
|
|
277
|
+
this.errorHandler = new ErrorHandler(this.config, this.logger);
|
|
278
|
+
|
|
279
|
+
// API Key Manager
|
|
280
|
+
this.apiKeyManager = new ApiKeyManager(this.logger);
|
|
281
|
+
await this.apiKeyManager.initialize(); // Load persisted keys
|
|
282
|
+
// Register the singleton so cross-cutting services (composioService,
|
|
283
|
+
// etc.) can resolve vendor keys without explicit injection.
|
|
284
|
+
registerApiKeyManager(this.apiKeyManager);
|
|
285
|
+
|
|
286
|
+
// Credential Vault for secure website credential management
|
|
287
|
+
this.credentialVault = getCredentialVault(this.logger);
|
|
288
|
+
await this.credentialVault.initialize(); // Load persisted credentials
|
|
289
|
+
|
|
290
|
+
// WhatsApp Service (optional — requires whatsapp-web.js)
|
|
291
|
+
try {
|
|
292
|
+
const { default: WhatsAppService } = await import('./services/whatsappService.js');
|
|
293
|
+
this.whatsappService = new WhatsAppService(this.logger);
|
|
294
|
+
this.logger.info('WhatsApp service initialized');
|
|
295
|
+
} catch (e) {
|
|
296
|
+
this.whatsappService = null;
|
|
297
|
+
this.logger.info('WhatsApp service unavailable (whatsapp-web.js not installed)', { error: e.message });
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Telegram Service (optional — requires node-telegram-bot-api)
|
|
301
|
+
try {
|
|
302
|
+
const { getTelegramService } = await import('./services/telegramService.js');
|
|
303
|
+
this.telegramService = getTelegramService(this.logger);
|
|
304
|
+
this.logger.info('Telegram service initialized');
|
|
305
|
+
} catch (e) {
|
|
306
|
+
this.telegramService = null;
|
|
307
|
+
this.logger.info('Telegram service unavailable', { error: e.message });
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Discord Service (optional — requires discord.js)
|
|
311
|
+
try {
|
|
312
|
+
const { getDiscordService } = await import('./services/discordService.js');
|
|
313
|
+
this.discordService = getDiscordService(this.logger);
|
|
314
|
+
this.logger.info('Discord service initialized');
|
|
315
|
+
} catch (e) {
|
|
316
|
+
this.discordService = null;
|
|
317
|
+
this.logger.info('Discord service unavailable', { error: e.message });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Schedule Service
|
|
321
|
+
this.scheduleService = new ScheduleService(this.logger);
|
|
322
|
+
await this.scheduleService.initialize();
|
|
323
|
+
|
|
324
|
+
// AI Service
|
|
325
|
+
this.aiService = new AIService(
|
|
326
|
+
this.config,
|
|
327
|
+
this.logger,
|
|
328
|
+
this.budgetService,
|
|
329
|
+
this.errorHandler
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
// Set API Key Manager reference in AI Service
|
|
333
|
+
this.aiService.setApiKeyManager(this.apiKeyManager);
|
|
334
|
+
|
|
335
|
+
// Set Agent Pool reference in AI Service
|
|
336
|
+
this.aiService.setAgentPool(this.agentPool);
|
|
337
|
+
|
|
338
|
+
// Embedding Service — opt-in semantic retrieval layer. Boots in the
|
|
339
|
+
// disabled state by default; loadPersistedConfig() picks up whatever
|
|
340
|
+
// the user previously chose in Settings.
|
|
341
|
+
const stateDir = getUserDataPaths().state;
|
|
342
|
+
this.embeddingService = new EmbeddingService({
|
|
343
|
+
// Start with the in-memory default (disabled). The settings file
|
|
344
|
+
// (if present) will override this on the next line.
|
|
345
|
+
config: this.config?.embeddings || {},
|
|
346
|
+
apiKeyManager: this.apiKeyManager,
|
|
347
|
+
baseUrl: this.config?.backend?.baseUrl || this.aiService.baseUrl,
|
|
348
|
+
stateDir,
|
|
349
|
+
logger: this.logger,
|
|
350
|
+
});
|
|
351
|
+
await this.embeddingService.loadPersistedConfig();
|
|
352
|
+
this.aiService.setEmbeddingService(this.embeddingService);
|
|
353
|
+
this.logger.info('[Loxia] Embedding service ready', {
|
|
354
|
+
provider: this.embeddingService.config.provider,
|
|
355
|
+
enabled: this.embeddingService.isEnabled,
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Initialize Model Routing Services
|
|
359
|
+
this.benchmarkService = new BenchmarkService(this.config, this.logger);
|
|
360
|
+
this.modelsService = new ModelsService(this.config, this.logger);
|
|
361
|
+
this.modelRouterService = new ModelRouterService(
|
|
362
|
+
this.config,
|
|
363
|
+
this.logger,
|
|
364
|
+
this.benchmarkService,
|
|
365
|
+
this.aiService
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
// Set API Key Manager reference in ModelsService
|
|
369
|
+
this.modelsService.setApiKeyManager(this.apiKeyManager);
|
|
370
|
+
|
|
371
|
+
// Set ModelsService reference in AI Service (for model suggestions on errors)
|
|
372
|
+
this.aiService.setModelsService(this.modelsService);
|
|
373
|
+
|
|
374
|
+
// Set ModelsService reference in BudgetService (for dynamic pricing lookup)
|
|
375
|
+
this.budgetService.setModelsService(this.modelsService);
|
|
376
|
+
|
|
377
|
+
// Initialize services
|
|
378
|
+
await this.benchmarkService.initialize();
|
|
379
|
+
await this.modelsService.initialize();
|
|
380
|
+
|
|
381
|
+
// Message Processor
|
|
382
|
+
this.messageProcessor = new MessageProcessor(
|
|
383
|
+
this.config,
|
|
384
|
+
this.logger,
|
|
385
|
+
this.toolsRegistry,
|
|
386
|
+
this.agentPool,
|
|
387
|
+
this.contextManager,
|
|
388
|
+
this.aiService,
|
|
389
|
+
this.modelRouterService,
|
|
390
|
+
this.modelsService
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
// Agent Scheduler - NEW ARCHITECTURE
|
|
394
|
+
this.agentScheduler = new AgentScheduler(
|
|
395
|
+
this.agentPool,
|
|
396
|
+
this.messageProcessor,
|
|
397
|
+
this.aiService,
|
|
398
|
+
this.logger,
|
|
399
|
+
null, // webSocketManager will be set later
|
|
400
|
+
this.modelRouterService,
|
|
401
|
+
this.modelsService
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
// Note: Scheduler will be started after WebSocketManager is initialized
|
|
405
|
+
|
|
406
|
+
// Orchestrator
|
|
407
|
+
this.orchestrator = new Orchestrator(
|
|
408
|
+
this.config,
|
|
409
|
+
this.logger,
|
|
410
|
+
this.agentPool,
|
|
411
|
+
this.messageProcessor,
|
|
412
|
+
this.aiService,
|
|
413
|
+
this.stateManager
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
// Expose toolsRegistry on the orchestrator so downstream wiring (e.g.
|
|
417
|
+
// webServer's flowExecutor init at webServer.js:574 → jobDoneTool
|
|
418
|
+
// setFlowExecutor) can reach it through the orchestrator handle. Without
|
|
419
|
+
// this, `this.orchestrator?.toolsRegistry` is undefined at wiring time
|
|
420
|
+
// and JobDoneTool's flow-contract validation (Phase 8) silently no-ops
|
|
421
|
+
// for the entire process lifetime.
|
|
422
|
+
this.orchestrator.toolsRegistry = this.toolsRegistry;
|
|
423
|
+
|
|
424
|
+
// Set cross-references between components
|
|
425
|
+
this.messageProcessor.orchestrator = this.orchestrator;
|
|
426
|
+
this.messageProcessor.setScheduler(this.agentScheduler);
|
|
427
|
+
this.agentPool.setMessageProcessor(this.messageProcessor);
|
|
428
|
+
this.agentPool.setScheduler(this.agentScheduler);
|
|
429
|
+
this.agentPool.setFileAttachmentService(this.fileAttachmentService);
|
|
430
|
+
|
|
431
|
+
// Attach FileAttachmentService to orchestrator for webServer access
|
|
432
|
+
this.orchestrator.fileAttachmentService = this.fileAttachmentService;
|
|
433
|
+
|
|
434
|
+
// Wire ScheduleService dependencies
|
|
435
|
+
this.scheduleService.setAgentPool(this.agentPool);
|
|
436
|
+
this.scheduleService.setMessageProcessor(this.messageProcessor);
|
|
437
|
+
this.scheduleService.setOrchestrator(this.orchestrator);
|
|
438
|
+
|
|
439
|
+
this.logger.info('Core components initialized');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Initialize tools system
|
|
444
|
+
* @private
|
|
445
|
+
*/
|
|
446
|
+
async initializeTools() {
|
|
447
|
+
this.logger.info('Initializing tools...');
|
|
448
|
+
|
|
449
|
+
// Register Agent Delay Tool
|
|
450
|
+
await this.toolsRegistry.registerTool(AgentDelayTool);
|
|
451
|
+
|
|
452
|
+
// Register Terminal Tool
|
|
453
|
+
await this.toolsRegistry.registerTool(TerminalTool);
|
|
454
|
+
|
|
455
|
+
// Register File System Tool
|
|
456
|
+
await this.toolsRegistry.registerTool(FileSystemTool);
|
|
457
|
+
|
|
458
|
+
// Register Job Done Tool
|
|
459
|
+
await this.toolsRegistry.registerTool(JobDoneTool);
|
|
460
|
+
|
|
461
|
+
// Register Agent Communication Tool
|
|
462
|
+
await this.toolsRegistry.registerTool(AgentCommunicationTool);
|
|
463
|
+
|
|
464
|
+
// Register Task Manager Tool
|
|
465
|
+
await this.toolsRegistry.registerTool(TaskManagerTool);
|
|
466
|
+
|
|
467
|
+
// Register Import Analyzer Tool
|
|
468
|
+
await this.toolsRegistry.registerTool(ImportAnalyzerTool);
|
|
469
|
+
|
|
470
|
+
// Register Dependency Resolver Tool
|
|
471
|
+
await this.toolsRegistry.registerTool(DependencyResolverTool);
|
|
472
|
+
|
|
473
|
+
// Register Image Generation Tool
|
|
474
|
+
await this.toolsRegistry.registerTool(ImageTool);
|
|
475
|
+
|
|
476
|
+
// Register Video Generation Tool (Sora 2)
|
|
477
|
+
await this.toolsRegistry.registerTool(VideoTool);
|
|
478
|
+
|
|
479
|
+
// Register Static Analysis Tool
|
|
480
|
+
await this.toolsRegistry.registerTool(StaticAnalysisTool);
|
|
481
|
+
|
|
482
|
+
// Register Clone Detection Tool
|
|
483
|
+
await this.toolsRegistry.registerTool(CloneDetectionTool);
|
|
484
|
+
|
|
485
|
+
// Register File Tree Tool
|
|
486
|
+
await this.toolsRegistry.registerTool(FileTreeTool);
|
|
487
|
+
|
|
488
|
+
// Register File Content Replace Tool
|
|
489
|
+
await this.toolsRegistry.registerTool(FileContentReplaceTool);
|
|
490
|
+
|
|
491
|
+
// Register Seek Tool
|
|
492
|
+
await this.toolsRegistry.registerTool(SeekTool);
|
|
493
|
+
|
|
494
|
+
// Register Web Tool
|
|
495
|
+
await this.toolsRegistry.registerTool(WebTool);
|
|
496
|
+
|
|
497
|
+
// Register Foundry Web Search Tool (grounded, with citations).
|
|
498
|
+
// Distinct from WebTool: WebTool drives a real browser (puppeteer)
|
|
499
|
+
// for raw results / authenticated sites; FoundryWebSearchTool
|
|
500
|
+
// calls /llm/web-search which uses Azure AI Foundry's
|
|
501
|
+
// web_search_preview tool — single round-trip, synthesized answer
|
|
502
|
+
// with citation URLs. See docs/FOUNDRY_WEB_SEARCH_PROVISIONING.md
|
|
503
|
+
// on the backend side; the tool returns a clear "not provisioned"
|
|
504
|
+
// error until that's done.
|
|
505
|
+
await this.toolsRegistry.registerTool(FoundryWebSearchTool);
|
|
506
|
+
|
|
507
|
+
// Register Visual Editor Tool
|
|
508
|
+
await this.toolsRegistry.registerTool(VisualEditorTool);
|
|
509
|
+
|
|
510
|
+
// Register PDF Tool
|
|
511
|
+
await this.toolsRegistry.registerTool(PdfTool);
|
|
512
|
+
|
|
513
|
+
// Register Help Tool (two-layer tool description system)
|
|
514
|
+
await this.toolsRegistry.registerTool(HelpTool);
|
|
515
|
+
|
|
516
|
+
// Register Document (DOCX) Tool — legacy; superseded by OfficeTool.
|
|
517
|
+
// Kept for one release cycle so existing agent configs that reference
|
|
518
|
+
// toolId "doc" continue to work.
|
|
519
|
+
await this.toolsRegistry.registerTool(DocxTool);
|
|
520
|
+
|
|
521
|
+
// Register Spreadsheet (Excel) Tool — legacy; superseded by OfficeTool.
|
|
522
|
+
await this.toolsRegistry.registerTool(ExcelTool);
|
|
523
|
+
|
|
524
|
+
// Register Office Tool — consolidated doc/sheet/pres surface
|
|
525
|
+
// (Word + Excel + PowerPoint create/read).
|
|
526
|
+
await this.toolsRegistry.registerTool(OfficeTool);
|
|
527
|
+
|
|
528
|
+
// Register Memory Tool
|
|
529
|
+
await this.toolsRegistry.registerTool(MemoryTool);
|
|
530
|
+
await this.toolsRegistry.registerTool(SkillsTool);
|
|
531
|
+
await this.toolsRegistry.registerTool(VisionTool);
|
|
532
|
+
|
|
533
|
+
// Register Composio integration tool (off by default — agents
|
|
534
|
+
// must have the `composio` capability AND the operator must set
|
|
535
|
+
// COMPOSIO_API_KEY for it to do anything).
|
|
536
|
+
await this.toolsRegistry.registerTool(ComposioTool);
|
|
537
|
+
|
|
538
|
+
// Register User Prompt Tool
|
|
539
|
+
await this.toolsRegistry.registerTool(UserPromptTool);
|
|
540
|
+
|
|
541
|
+
// Register Code Map Tool
|
|
542
|
+
await this.toolsRegistry.registerTool(CodeMapTool);
|
|
543
|
+
|
|
544
|
+
// Register WhatsApp Tool
|
|
545
|
+
await this.toolsRegistry.registerTool(WhatsAppTool);
|
|
546
|
+
|
|
547
|
+
// Register Platform Control Tool — agent-facing platform control
|
|
548
|
+
// (currently: scheduled tasks; per-agent permission, default DISABLED).
|
|
549
|
+
await this.toolsRegistry.registerTool(PlatformControlTool);
|
|
550
|
+
|
|
551
|
+
// Register Desktop Tool — keyboard/mouse/screen/window control with
|
|
552
|
+
// visual grounding (Kimi K2.6 by default). Per-agent allowlist with
|
|
553
|
+
// an EMPTY default, so no agent can drive the desktop until the
|
|
554
|
+
// operator explicitly opts in via toolConfig.desktop.allowedActions.
|
|
555
|
+
// Global kill switch: LOXIA_DESKTOP_TOOL_DISABLED=1.
|
|
556
|
+
await this.toolsRegistry.registerTool(DesktopTool);
|
|
557
|
+
|
|
558
|
+
// widget-module: remove this block if the module is deleted.
|
|
559
|
+
// Registers the WidgetTool for agent-facing custom-UI rendering.
|
|
560
|
+
// Honors LOXIA_DISABLE_WIDGETS=1 env flag for a zero-source kill switch.
|
|
561
|
+
const widgetModule = await import('./modules/widget/index.js');
|
|
562
|
+
if (!widgetModule.isDisabled()) {
|
|
563
|
+
await this.toolsRegistry.registerTool(widgetModule.WidgetTool);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Set ToolsRegistry dependency for HelpTool (two-layer tool description system)
|
|
567
|
+
const helpTool = this.toolsRegistry.getTool('help');
|
|
568
|
+
if (helpTool && typeof helpTool.setToolsRegistry === 'function') {
|
|
569
|
+
helpTool.setToolsRegistry(this.toolsRegistry);
|
|
570
|
+
this.logger.info('ToolsRegistry set for Help Tool');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Set AgentPool dependency for AgentDelayTool
|
|
574
|
+
const agentDelayTool = this.toolsRegistry.getTool('agentdelay');
|
|
575
|
+
if (agentDelayTool && typeof agentDelayTool.setAgentPool === 'function') {
|
|
576
|
+
agentDelayTool.setAgentPool(this.agentPool);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Wire ScheduleService into PlatformControlTool. Without this, the
|
|
580
|
+
// tool reports "ScheduleService is not available" for any feature
|
|
581
|
+
// action — useful as a defensive default but not what we want here.
|
|
582
|
+
const platformControlTool = this.toolsRegistry.getTool('platformcontrol');
|
|
583
|
+
if (platformControlTool) {
|
|
584
|
+
if (typeof platformControlTool.setScheduleService === 'function') {
|
|
585
|
+
platformControlTool.setScheduleService(this.scheduleService);
|
|
586
|
+
}
|
|
587
|
+
// Agent + team CRUD requires AgentPool, StateManager, MemoryService.
|
|
588
|
+
if (typeof platformControlTool.setAgentPool === 'function') {
|
|
589
|
+
platformControlTool.setAgentPool(this.agentPool);
|
|
590
|
+
}
|
|
591
|
+
if (typeof platformControlTool.setStateManager === 'function') {
|
|
592
|
+
platformControlTool.setStateManager(this.stateManager);
|
|
593
|
+
}
|
|
594
|
+
// Memory tool's team-pool actions (publish/search/unpublish) need
|
|
595
|
+
// StateManager for the team-membership ACL check. The wiring is
|
|
596
|
+
// optional: when StateManager isn't attached, team-scoped actions
|
|
597
|
+
// refuse cleanly rather than defaulting open.
|
|
598
|
+
const memoryTool = this.toolsRegistry?.getTool?.('memory');
|
|
599
|
+
if (memoryTool && typeof memoryTool.setStateManager === 'function') {
|
|
600
|
+
memoryTool.setStateManager(this.stateManager);
|
|
601
|
+
}
|
|
602
|
+
if (typeof platformControlTool.setMemoryService === 'function') {
|
|
603
|
+
const { getMemoryService } = await import('./services/memoryService.js');
|
|
604
|
+
platformControlTool.setMemoryService(getMemoryService(this.logger));
|
|
605
|
+
}
|
|
606
|
+
// Flow CRUD + execution requires the FlowExecutor (created later in
|
|
607
|
+
// the boot sequence by the webServer). The webServer init also
|
|
608
|
+
// calls setFlowExecutor on this tool when it spins up — so this
|
|
609
|
+
// call is the one that fires when the executor is already alive
|
|
610
|
+
// (rare during startup), and the webServer call covers the normal
|
|
611
|
+
// path. Both are idempotent.
|
|
612
|
+
if (typeof platformControlTool.setFlowExecutor === 'function' && this.flowExecutor) {
|
|
613
|
+
platformControlTool.setFlowExecutor(this.flowExecutor);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Set AgentPool dependency for JobDoneTool
|
|
618
|
+
const jobDoneTool = this.toolsRegistry.getTool('jobdone');
|
|
619
|
+
if (jobDoneTool && typeof jobDoneTool.setAgentPool === 'function') {
|
|
620
|
+
jobDoneTool.setAgentPool(this.agentPool);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Set AgentPool and Scheduler dependencies for TaskManagerTool
|
|
624
|
+
const taskManagerTool = this.toolsRegistry.getTool('taskmanager');
|
|
625
|
+
if (taskManagerTool && typeof taskManagerTool.setAgentPool === 'function') {
|
|
626
|
+
taskManagerTool.setAgentPool(this.agentPool);
|
|
627
|
+
}
|
|
628
|
+
if (taskManagerTool && typeof taskManagerTool.setScheduler === 'function') {
|
|
629
|
+
taskManagerTool.setScheduler(this.scheduler);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Note: AgentCommunicationTool receives agentPool through execution context
|
|
633
|
+
// No need to set it directly as it's passed in the context parameter
|
|
634
|
+
const agentCommTool = this.toolsRegistry.getTool('agentcommunication');
|
|
635
|
+
if (agentCommTool) {
|
|
636
|
+
this.logger.info('Agent Communication Tool registered successfully');
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Set AIService dependency for ImageTool
|
|
640
|
+
const imageTool = this.toolsRegistry.getTool('image-gen');
|
|
641
|
+
if (imageTool && typeof imageTool.setAIService === 'function') {
|
|
642
|
+
imageTool.setAIService(this.aiService);
|
|
643
|
+
this.logger.info('AIService set for Image Generation Tool');
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Set AgentPool dependency for ImageTool (for conversation history persistence)
|
|
647
|
+
if (imageTool && typeof imageTool.setAgentPool === 'function') {
|
|
648
|
+
imageTool.setAgentPool(this.agentPool);
|
|
649
|
+
this.logger.info('AgentPool set for Image Generation Tool');
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Set dependencies for VisionTool
|
|
653
|
+
const visionTool = this.toolsRegistry.getTool('vision');
|
|
654
|
+
if (visionTool && typeof visionTool.setAIService === 'function') {
|
|
655
|
+
visionTool.setAIService(this.aiService);
|
|
656
|
+
if (this.modelsService) visionTool.setModelsService(this.modelsService);
|
|
657
|
+
if (this.agentPool) visionTool.setAgentPool(this.agentPool);
|
|
658
|
+
this.logger.info('Dependencies set for Vision Tool');
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Video generation (Sora 2)
|
|
662
|
+
const videoTool = this.toolsRegistry.getTool('video-gen');
|
|
663
|
+
if (videoTool && typeof videoTool.setAIService === 'function') {
|
|
664
|
+
videoTool.setAIService(this.aiService);
|
|
665
|
+
this.logger.info('AIService set for Video Generation Tool');
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Foundry web search — uses /llm/web-search backed by Azure AI
|
|
669
|
+
// Foundry + Grounding with Bing Search. Calls aiService.foundryWebSearch
|
|
670
|
+
// for the auth + URL plumbing.
|
|
671
|
+
const foundryWebTool = this.toolsRegistry.getTool('foundry_web_search');
|
|
672
|
+
if (foundryWebTool && typeof foundryWebTool.setAIService === 'function') {
|
|
673
|
+
foundryWebTool.setAIService(this.aiService);
|
|
674
|
+
this.logger.info('AIService set for Foundry Web Search Tool');
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// Desktop tool — needs aiService for proxy-mode grounding auth
|
|
678
|
+
// (baseUrl + per-session Loxia key from apiKeyManager).
|
|
679
|
+
const desktopTool = this.toolsRegistry.getTool('desktop');
|
|
680
|
+
if (desktopTool && typeof desktopTool.setAIService === 'function') {
|
|
681
|
+
desktopTool.setAIService(this.aiService);
|
|
682
|
+
this.logger.info('AIService set for Desktop Tool');
|
|
683
|
+
}
|
|
684
|
+
if (videoTool && typeof videoTool.setAgentPool === 'function') {
|
|
685
|
+
videoTool.setAgentPool(this.agentPool);
|
|
686
|
+
this.logger.info('AgentPool set for Video Generation Tool');
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Set WhatsApp Service dependency for WhatsApp Tool
|
|
690
|
+
const whatsappTool = this.toolsRegistry.getTool('whatsapp');
|
|
691
|
+
if (whatsappTool && typeof whatsappTool.setWhatsAppService === 'function') {
|
|
692
|
+
whatsappTool.setWhatsAppService(this.whatsappService);
|
|
693
|
+
this.logger.info('WhatsAppService set for WhatsApp Tool');
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// ─── Catch-all aiService wiring ────────────────────────────────
|
|
697
|
+
//
|
|
698
|
+
// Several tools read `this.aiService.getEmbeddingService()` for their
|
|
699
|
+
// semantic-search / find-action paths (memory, agentcommunication,
|
|
700
|
+
// taskmanager, skills, composio) but have no `setAIService` method
|
|
701
|
+
// and were never wired by the explicit calls above. Production silently
|
|
702
|
+
// returned `this.aiService === undefined` → `_getIndexer()` returned
|
|
703
|
+
// null → every embedding-backed action surfaced "Embeddings are not
|
|
704
|
+
// enabled" even when embeddings WERE enabled (the user just hit this
|
|
705
|
+
// on composio.find-action; auto-recall in agentScheduler uses a
|
|
706
|
+
// different code path so it kept working, masking the wider gap).
|
|
707
|
+
//
|
|
708
|
+
// We sweep every registered tool here, AFTER the explicit per-tool
|
|
709
|
+
// wiring above. Tools that already have aiService set (image, vision,
|
|
710
|
+
// video, foundry-web-search, desktop) are skipped; everyone else
|
|
711
|
+
// gets it now. The assignment is harmless for tools that ignore the
|
|
712
|
+
// property.
|
|
713
|
+
{
|
|
714
|
+
let wired = 0;
|
|
715
|
+
for (const tool of this.toolsRegistry.tools.values()) {
|
|
716
|
+
if (!tool.aiService) {
|
|
717
|
+
tool.aiService = this.aiService;
|
|
718
|
+
wired++;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
this.logger.info('AIService wired onto remaining tools', { count: wired });
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const toolCapabilities = this.toolsRegistry.getToolCapabilities();
|
|
725
|
+
this.logger.info('Tools initialized', {
|
|
726
|
+
toolCount: Object.keys(toolCapabilities).length,
|
|
727
|
+
enabledTools: Object.keys(toolCapabilities).filter(id => toolCapabilities[id].capabilities.enabled),
|
|
728
|
+
registeredTools: this.toolsRegistry.listTools()
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
// Log tool descriptions for debugging
|
|
732
|
+
if (this.logger.level === 'debug') {
|
|
733
|
+
for (const [toolId, tool] of Object.entries(toolCapabilities)) {
|
|
734
|
+
this.logger.debug(`Tool ${toolId} capabilities`, tool.capabilities);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Initialize interface handlers
|
|
741
|
+
* @private
|
|
742
|
+
*/
|
|
743
|
+
async initializeInterfaces() {
|
|
744
|
+
this.logger.info('Initializing interfaces...');
|
|
745
|
+
|
|
746
|
+
const interfaceConfig = this.config.interfaces || {};
|
|
747
|
+
const uiMode = process.env.LOXIA_UI_MODE || 'cli'; // Default to old CLI
|
|
748
|
+
|
|
749
|
+
// CLI Interface - Load old readline CLI (unless terminal UI mode is specified)
|
|
750
|
+
// NOTE: Terminal UI mode runs as a separate WebSocket client, not in the main process
|
|
751
|
+
if (interfaceConfig.cli?.enabled !== false && uiMode !== 'terminal') {
|
|
752
|
+
// Use old CLI (readline-based)
|
|
753
|
+
this.logger.info('Loading CLI (readline)...');
|
|
754
|
+
const { default: CLIInterface } = await import('./interfaces/cli.js');
|
|
755
|
+
const cliInterface = new CLIInterface(
|
|
756
|
+
this.orchestrator,
|
|
757
|
+
this.logger,
|
|
758
|
+
interfaceConfig.cli || {}
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
await cliInterface.initialize();
|
|
762
|
+
this.interfaces.set(INTERFACE_TYPES.CLI, cliInterface);
|
|
763
|
+
|
|
764
|
+
this.logger.info('CLI interface initialized');
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// If terminal UI mode, skip CLI - Terminal UI will connect as WebSocket client
|
|
768
|
+
if (uiMode === 'terminal') {
|
|
769
|
+
this.logger.info('Terminal UI mode: Server-only startup (Terminal UI will connect as WebSocket client)');
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Web Interface - now implemented
|
|
773
|
+
if (interfaceConfig.web?.enabled !== false) {
|
|
774
|
+
// Read port from environment variables (set by CLI) or use config defaults
|
|
775
|
+
const webPort = parseInt(process.env.LOXIA_PORT || process.env.PORT, 10) || 8080;
|
|
776
|
+
// Use env var, then config, then 0.0.0.0 (accept connections from all interfaces)
|
|
777
|
+
const webHost = process.env.LOXIA_HOST || interfaceConfig.web?.host || '0.0.0.0';
|
|
778
|
+
|
|
779
|
+
const webConfig = {
|
|
780
|
+
...interfaceConfig.web,
|
|
781
|
+
port: webPort,
|
|
782
|
+
host: webHost,
|
|
783
|
+
backend: this.config.backend
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
const webServer = new WebServer(
|
|
787
|
+
this.orchestrator,
|
|
788
|
+
this.logger,
|
|
789
|
+
webConfig
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
// Pass toolsRegistry to webServer for the /api/tools endpoint
|
|
793
|
+
webServer.toolsRegistry = this.toolsRegistry;
|
|
794
|
+
|
|
795
|
+
// Set API Key Manager reference in Web Server
|
|
796
|
+
webServer.setApiKeyManager(this.apiKeyManager);
|
|
797
|
+
|
|
798
|
+
// Embedding service handle so the /api/embeddings/* routes can
|
|
799
|
+
// read config + telemetry, hot-swap providers, etc.
|
|
800
|
+
webServer.embeddingService = this.embeddingService;
|
|
801
|
+
|
|
802
|
+
// Set Credential Vault reference in Web Server
|
|
803
|
+
webServer.setCredentialVault(this.credentialVault);
|
|
804
|
+
|
|
805
|
+
// Set WhatsApp Service reference in Web Server
|
|
806
|
+
if (this.whatsappService) {
|
|
807
|
+
webServer.setWhatsAppService(this.whatsappService);
|
|
808
|
+
this.whatsappService.setWebSocketManager(webServer);
|
|
809
|
+
// Auto-reconnect if a previous session exists (non-blocking)
|
|
810
|
+
this.whatsappService.autoReconnect().catch(e =>
|
|
811
|
+
this.logger.warn('WhatsApp auto-reconnect failed', { error: e.message })
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Set Telegram Service references
|
|
816
|
+
if (this.telegramService) {
|
|
817
|
+
webServer.setTelegramService(this.telegramService);
|
|
818
|
+
this.telegramService.setOrchestrator(this.orchestrator);
|
|
819
|
+
this.telegramService.setAgentPool(this.agentPool);
|
|
820
|
+
this.telegramService.setWebSocketManager(webServer);
|
|
821
|
+
if (this.flowExecutor) this.telegramService.setFlowExecutor(this.flowExecutor);
|
|
822
|
+
// Backend URL + platform API key are needed for the voice-note
|
|
823
|
+
// → /llm/transcribe path. Without these, voice messages return
|
|
824
|
+
// a friendly "voice not configured" message and the user is
|
|
825
|
+
// told to type instead. Both come from the same sources the
|
|
826
|
+
// rest of the CLI's /api/* proxy code uses.
|
|
827
|
+
const backendBaseUrl = this.config?.backend?.baseUrl
|
|
828
|
+
|| webServer?.config?.backend?.baseUrl
|
|
829
|
+
|| null;
|
|
830
|
+
if (backendBaseUrl) this.telegramService.setBackendBaseUrl(backendBaseUrl);
|
|
831
|
+
// Pass a getter (not a snapshotted value) so the key is
|
|
832
|
+
// re-resolved on every transcribe call. This lets the user
|
|
833
|
+
// sign in AFTER boot — voice notes start working immediately
|
|
834
|
+
// without restarting the server.
|
|
835
|
+
this.telegramService.setPlatformApiKey(
|
|
836
|
+
() => this.apiKeyManager?.getKeysForRequest?.(null, { platformProvided: true })?.loxiaApiKey || null
|
|
837
|
+
);
|
|
838
|
+
// Let the scheduler query bridge-state per agent so it can inject
|
|
839
|
+
// the <external> routing guidance into system prompts only when the
|
|
840
|
+
// agent is actually addressable from Telegram.
|
|
841
|
+
this.agentScheduler?.setTelegramService?.(this.telegramService);
|
|
842
|
+
// Auto-connect if token exists in config (non-blocking)
|
|
843
|
+
this.telegramService.autoConnect().catch(e =>
|
|
844
|
+
this.logger.warn('Telegram auto-connect failed', { error: e.message })
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Set Discord Service references
|
|
849
|
+
if (this.discordService) {
|
|
850
|
+
webServer.setDiscordService(this.discordService);
|
|
851
|
+
this.discordService.setOrchestrator(this.orchestrator);
|
|
852
|
+
this.discordService.setAgentPool(this.agentPool);
|
|
853
|
+
this.discordService.setWebSocketManager(webServer);
|
|
854
|
+
if (this.flowExecutor) this.discordService.setFlowExecutor(this.flowExecutor);
|
|
855
|
+
// Same bridge-awareness hook as Telegram above — the scheduler
|
|
856
|
+
// only appends <external> guidance when `isAgentBridged(agentId)`
|
|
857
|
+
// returns true for this channel.
|
|
858
|
+
this.agentScheduler?.setDiscordService?.(this.discordService);
|
|
859
|
+
// Auto-connect if token exists in config (non-blocking)
|
|
860
|
+
this.discordService.autoConnect().catch(e =>
|
|
861
|
+
this.logger.warn('Discord auto-connect failed', { error: e.message })
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
await webServer.initialize();
|
|
866
|
+
this.interfaces.set(INTERFACE_TYPES.WEB, webServer);
|
|
867
|
+
|
|
868
|
+
// Attach WebServer to orchestrator for MessageProcessor broadcasting
|
|
869
|
+
this.orchestrator.webServer = webServer;
|
|
870
|
+
|
|
871
|
+
// Connect MessageProcessor to WebServer for real-time updates
|
|
872
|
+
this.messageProcessor.setWebSocketManager(webServer);
|
|
873
|
+
|
|
874
|
+
// Connect AgentScheduler to WebServer for real-time updates
|
|
875
|
+
this.agentScheduler.webSocketManager = webServer;
|
|
876
|
+
|
|
877
|
+
// Start the scheduler now that WebSocketManager is available
|
|
878
|
+
this.agentScheduler.start();
|
|
879
|
+
this.logger.info('Agent Scheduler started with WebSocket integration');
|
|
880
|
+
|
|
881
|
+
// Wire ScheduleService to WebServer and FlowExecutor
|
|
882
|
+
webServer.setScheduleService(this.scheduleService);
|
|
883
|
+
this.scheduleService.setWebSocketManager(webServer);
|
|
884
|
+
if (webServer.flowExecutor) {
|
|
885
|
+
this.scheduleService.setFlowExecutor(webServer.flowExecutor);
|
|
886
|
+
}
|
|
887
|
+
this.scheduleService.start();
|
|
888
|
+
this.logger.info('ScheduleService started with WebSocket integration');
|
|
889
|
+
|
|
890
|
+
// Set global reference for tools that need to broadcast
|
|
891
|
+
global.loxiaWebServer = webServer;
|
|
892
|
+
|
|
893
|
+
// Connect JobDoneTool to WebServer for broadcasting mode changes
|
|
894
|
+
const jobDoneTool = this.toolsRegistry.getTool('jobdone');
|
|
895
|
+
if (jobDoneTool && typeof jobDoneTool.setWebSocketManager === 'function') {
|
|
896
|
+
jobDoneTool.setWebSocketManager(webServer);
|
|
897
|
+
this.logger.info('WebSocketManager set for JobDone Tool');
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Connect UserPromptTool to WebServer and AgentPool for user prompting
|
|
901
|
+
const userPromptTool = this.toolsRegistry.getTool('userprompt');
|
|
902
|
+
if (userPromptTool) {
|
|
903
|
+
if (typeof userPromptTool.setWebSocketManager === 'function') {
|
|
904
|
+
userPromptTool.setWebSocketManager(webServer);
|
|
905
|
+
}
|
|
906
|
+
if (typeof userPromptTool.setAgentPool === 'function') {
|
|
907
|
+
userPromptTool.setAgentPool(this.agentPool);
|
|
908
|
+
}
|
|
909
|
+
this.logger.info('Dependencies set for UserPrompt Tool');
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
const status = webServer.getStatus();
|
|
913
|
+
this.logger.info('Web interface initialized', { url: status.url });
|
|
914
|
+
console.log(`🌐 Server running at ${status.url}`);
|
|
915
|
+
console.log(`📱 Web UI available at: ${status.url}`);
|
|
916
|
+
|
|
917
|
+
// Auto-open browser (skip when running inside Electron — it creates its own window)
|
|
918
|
+
if (!process.env.LOXIA_ELECTRON) {
|
|
919
|
+
const url = status.url.replace('0.0.0.0', 'localhost');
|
|
920
|
+
const platform = process.platform;
|
|
921
|
+
const openCmd = platform === 'win32' ? `start "" "${url}"`
|
|
922
|
+
: platform === 'darwin' ? `open "${url}"`
|
|
923
|
+
: `xdg-open "${url}"`;
|
|
924
|
+
exec(openCmd, (err) => {
|
|
925
|
+
if (err) this.logger.debug('Could not auto-open browser', { error: err.message });
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// VSCode Extension Interface (placeholder)
|
|
931
|
+
if (interfaceConfig.vscode?.enabled === true) {
|
|
932
|
+
this.logger.info('VSCode interface configured but not implemented yet');
|
|
933
|
+
// TODO: Initialize VSCode extension interface
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Setup graceful shutdown handlers
|
|
939
|
+
* @private
|
|
940
|
+
*/
|
|
941
|
+
setupShutdownHandlers() {
|
|
942
|
+
// Handle SIGINT (Ctrl+C)
|
|
943
|
+
process.on('SIGINT', async () => {
|
|
944
|
+
console.log('\n📋 Received SIGINT, shutting down gracefully...');
|
|
945
|
+
await this.shutdown();
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
// Handle SIGTERM
|
|
949
|
+
process.on('SIGTERM', async () => {
|
|
950
|
+
console.log('\n📋 Received SIGTERM, shutting down gracefully...');
|
|
951
|
+
await this.shutdown();
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
// Handle uncaught exceptions
|
|
955
|
+
process.on('uncaughtException', async (error) => {
|
|
956
|
+
console.error('❌ Uncaught exception:', error);
|
|
957
|
+
if (this.logger) {
|
|
958
|
+
this.logger.error('Uncaught exception', {
|
|
959
|
+
error: error.message,
|
|
960
|
+
stack: error.stack
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
await this.shutdown();
|
|
965
|
+
process.exit(1);
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
// Handle unhandled promise rejections
|
|
969
|
+
process.on('unhandledRejection', async (reason, promise) => {
|
|
970
|
+
const reasonMessage = reason?.message || String(reason);
|
|
971
|
+
|
|
972
|
+
// List of known non-critical rejections that shouldn't crash the server
|
|
973
|
+
const nonCriticalPatterns = [
|
|
974
|
+
'Credential request cancelled',
|
|
975
|
+
'Credential request timed out',
|
|
976
|
+
'Target closed',
|
|
977
|
+
'Session closed',
|
|
978
|
+
'Protocol error',
|
|
979
|
+
'Navigation timeout',
|
|
980
|
+
'net::ERR_',
|
|
981
|
+
'Requesting main frame too early',
|
|
982
|
+
'Connection closed',
|
|
983
|
+
// AI/model errors should NOT crash the server
|
|
984
|
+
'HTTP 4', // 400, 401, 403, 404, 429, etc.
|
|
985
|
+
'HTTP 5', // 500, 502, 503, etc.
|
|
986
|
+
'circuit breaker',
|
|
987
|
+
'Rate limit',
|
|
988
|
+
'Insufficient credits',
|
|
989
|
+
'not suitable for chat',
|
|
990
|
+
'No API key configured',
|
|
991
|
+
'Message content is empty',
|
|
992
|
+
'Backend returned malformed',
|
|
993
|
+
'stream generator',
|
|
994
|
+
'No response choices',
|
|
995
|
+
'model error',
|
|
996
|
+
'isModelError',
|
|
997
|
+
'Service temporarily unavailable',
|
|
998
|
+
'Failed to fetch models',
|
|
999
|
+
'ECONNREFUSED',
|
|
1000
|
+
'ETIMEDOUT',
|
|
1001
|
+
'ECONNRESET',
|
|
1002
|
+
'fetch failed',
|
|
1003
|
+
'AbortError',
|
|
1004
|
+
'The operation was aborted'
|
|
1005
|
+
];
|
|
1006
|
+
|
|
1007
|
+
const isNonCritical = nonCriticalPatterns.some(pattern =>
|
|
1008
|
+
reasonMessage.includes(pattern)
|
|
1009
|
+
);
|
|
1010
|
+
|
|
1011
|
+
if (isNonCritical) {
|
|
1012
|
+
console.warn('⚠️ Non-critical promise rejection (server continues):', reasonMessage);
|
|
1013
|
+
if (this.logger) {
|
|
1014
|
+
this.logger.warn('Non-critical promise rejection', {
|
|
1015
|
+
reason: reasonMessage,
|
|
1016
|
+
promise: promise.toString()
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
return; // Don't crash for non-critical errors
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
console.error('❌ Unhandled promise rejection:', reason);
|
|
1023
|
+
if (this.logger) {
|
|
1024
|
+
this.logger.error('Unhandled promise rejection', {
|
|
1025
|
+
reason: reasonMessage,
|
|
1026
|
+
promise: promise.toString()
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
await this.shutdown();
|
|
1031
|
+
process.exit(1);
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Gracefully shutdown the application
|
|
1037
|
+
* @returns {Promise<void>}
|
|
1038
|
+
*/
|
|
1039
|
+
async shutdown() {
|
|
1040
|
+
if (this.isShuttingDown) {
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
this.isShuttingDown = true;
|
|
1045
|
+
|
|
1046
|
+
// Force-exit safety net: if graceful shutdown hangs, kill the process after 10s
|
|
1047
|
+
const forceExitTimer = setTimeout(() => {
|
|
1048
|
+
console.error('⚠️ Graceful shutdown timed out after 10s — forcing exit');
|
|
1049
|
+
process.exit(1);
|
|
1050
|
+
}, 10000);
|
|
1051
|
+
forceExitTimer.unref(); // Don't let this timer keep the process alive on its own
|
|
1052
|
+
|
|
1053
|
+
try {
|
|
1054
|
+
console.log('🛑 Shutting down Loxia Autopilot One...');
|
|
1055
|
+
|
|
1056
|
+
if (this.logger) {
|
|
1057
|
+
this.logger.info('Application shutdown initiated');
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Stop schedule service first
|
|
1061
|
+
if (this.scheduleService) {
|
|
1062
|
+
this.scheduleService.stop();
|
|
1063
|
+
this.logger?.info('Schedule service stopped');
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Stop agent scheduler (prevents new work from starting mid-shutdown)
|
|
1067
|
+
if (this.agentScheduler) {
|
|
1068
|
+
this.agentScheduler.stop();
|
|
1069
|
+
this.logger?.info('Agent scheduler stopped');
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// Cancel any pending model fetch retries
|
|
1073
|
+
if (this.modelsService?._cancelRetry) {
|
|
1074
|
+
this.modelsService._cancelRetry();
|
|
1075
|
+
this.logger?.info('Models service retries cancelled');
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
// Close Puppeteer browser (webTool) — it holds DevTools ports
|
|
1079
|
+
if (this.toolsRegistry) {
|
|
1080
|
+
for (const toolId of ['web']) {
|
|
1081
|
+
try {
|
|
1082
|
+
const tool = this.toolsRegistry.getTool(toolId);
|
|
1083
|
+
if (tool?.cleanup) {
|
|
1084
|
+
await tool.cleanup();
|
|
1085
|
+
this.logger?.info(`${toolId} tool cleanup complete`);
|
|
1086
|
+
}
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
this.logger?.warn(`Failed to cleanup ${toolId} tool`, { error: error.message });
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// Kill ALL running terminal processes across all agents
|
|
1094
|
+
if (this.toolsRegistry) {
|
|
1095
|
+
try {
|
|
1096
|
+
const terminalTool = this.toolsRegistry.getTool('terminal');
|
|
1097
|
+
if (terminalTool?.commandTracker) {
|
|
1098
|
+
let killed = 0;
|
|
1099
|
+
for (const [, cmdInfo] of terminalTool.commandTracker) {
|
|
1100
|
+
if (cmdInfo.process && cmdInfo.state === 'RUNNING') {
|
|
1101
|
+
try {
|
|
1102
|
+
cmdInfo.process.kill('SIGTERM');
|
|
1103
|
+
killed++;
|
|
1104
|
+
} catch { /* already dead */ }
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
if (killed > 0) {
|
|
1108
|
+
this.logger?.info(`Killed ${killed} running terminal process(es) on shutdown`);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
this.logger?.warn('Failed to cleanup terminal processes', { error: error.message });
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// Shutdown interfaces (web server, visual editor, WS connections)
|
|
1117
|
+
for (const [type, interface_] of this.interfaces) {
|
|
1118
|
+
try {
|
|
1119
|
+
if (interface_.shutdown) {
|
|
1120
|
+
await interface_.shutdown();
|
|
1121
|
+
}
|
|
1122
|
+
this.logger?.info(`${type} interface shutdown complete`);
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
console.error(`Failed to shutdown ${type} interface:`, error.message);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// Shutdown async tool manager
|
|
1129
|
+
if (this.asyncToolManager) {
|
|
1130
|
+
await this.asyncToolManager.shutdown();
|
|
1131
|
+
this.logger?.info('Async tool manager shutdown complete');
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// Shutdown orchestrator (persists agent states)
|
|
1135
|
+
if (this.orchestrator) {
|
|
1136
|
+
await this.orchestrator.shutdown();
|
|
1137
|
+
this.logger?.info('Orchestrator shutdown complete');
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// Cleanup configuration manager
|
|
1141
|
+
if (this.configManager) {
|
|
1142
|
+
this.configManager.cleanup();
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// Close logger
|
|
1146
|
+
if (this.logger) {
|
|
1147
|
+
await this.logger.close();
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
console.log('✅ Loxia Autopilot One shutdown complete');
|
|
1151
|
+
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
console.error('❌ Error during shutdown:', error.message);
|
|
1154
|
+
} finally {
|
|
1155
|
+
process.exit(0);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Get application status
|
|
1161
|
+
* @returns {Object} Application status
|
|
1162
|
+
*/
|
|
1163
|
+
getStatus() {
|
|
1164
|
+
return {
|
|
1165
|
+
version: SYSTEM_VERSION,
|
|
1166
|
+
uptime: process.uptime(),
|
|
1167
|
+
memoryUsage: process.memoryUsage(),
|
|
1168
|
+
interfaces: Array.from(this.interfaces.keys()),
|
|
1169
|
+
isShuttingDown: this.isShuttingDown
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* Main application entry point
|
|
1176
|
+
*/
|
|
1177
|
+
async function main() {
|
|
1178
|
+
const app = new LoxiaApplication();
|
|
1179
|
+
|
|
1180
|
+
// Parse command line arguments
|
|
1181
|
+
const args = process.argv.slice(2);
|
|
1182
|
+
const options = {
|
|
1183
|
+
projectDir: process.cwd(),
|
|
1184
|
+
watchConfig: args.includes('--watch-config'),
|
|
1185
|
+
configPaths: []
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
// Parse --port and --host from argv so they work when running index.js directly
|
|
1189
|
+
// (bin/cli.js sets these as env vars, but npm start / node src/index.js bypasses cli.js)
|
|
1190
|
+
for (let i = 0; i < args.length; i++) {
|
|
1191
|
+
if (args[i] === '--port' && args[i + 1]) {
|
|
1192
|
+
process.env.LOXIA_PORT = args[i + 1];
|
|
1193
|
+
}
|
|
1194
|
+
if (args[i] === '--host' && args[i + 1]) {
|
|
1195
|
+
process.env.LOXIA_HOST = args[i + 1];
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// Look for custom config file
|
|
1200
|
+
const configIndex = args.indexOf('--config');
|
|
1201
|
+
if (configIndex !== -1 && args[configIndex + 1]) {
|
|
1202
|
+
options.configPaths.push(args[configIndex + 1]);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
await app.initialize(options);
|
|
1206
|
+
|
|
1207
|
+
// Keep the application running
|
|
1208
|
+
return app;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// Start the application if this file is run directly.
|
|
1212
|
+
const __filename = resolveModuleFilename(import.meta.url);
|
|
1213
|
+
if (process.argv[1] === __filename) {
|
|
1214
|
+
console.log('🚀 Starting Loxia Autopilot One...');
|
|
1215
|
+
main().catch(error => {
|
|
1216
|
+
console.error('❌ Failed to start Loxia Autopilot One:', error.message);
|
|
1217
|
+
console.error('Stack trace:', error.stack);
|
|
1218
|
+
process.exit(1);
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
1222
|
export { LoxiaApplication, main };
|