@xopcai/xopc 0.0.86 → 0.0.87
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/adapters/cli-login.js +3 -3
- package/dist/extensions/feishu/src/adapters/cli-login.js.map +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.d.ts +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.js +1 -1
- package/dist/extensions/telegram/src/delivery-chat-id.js.map +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +1 -0
- package/dist/extensions/telegram/src/routing-integration.js.map +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js +2 -2
- package/dist/extensions/weixin/src/__tests__/workflow-progress.test.js.map +1 -1
- package/dist/extensions/weixin/src/api/api.js +2 -2
- package/dist/extensions/weixin/src/api/api.js.map +1 -1
- package/dist/extensions/weixin/src/auth/accounts.js +12 -12
- package/dist/extensions/weixin/src/auth/accounts.js.map +1 -1
- package/dist/extensions/weixin/src/delivery-to.js +2 -2
- package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +5 -5
- package/dist/extensions/weixin/src/messaging/debug-mode.js.map +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +11 -11
- package/dist/extensions/weixin/src/messaging/inbound.js.map +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +4 -4
- package/dist/extensions/weixin/src/storage/sync-buf.js.map +1 -1
- package/dist/extensions/weixin/src/workflow-progress.d.ts +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js.map +1 -1
- package/dist/gateway/static/root/assets/{agents-mS3_HpRI.js → agents-BEAbXpuP.js} +6 -6
- package/dist/gateway/static/root/assets/{apps-page-DrfytjOb.js → apps-page-Dg8R-Szf.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-BG6b9KrW.js → channels-settings-yohw9YSu.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-status-swr-Bs5kMCMI.js → channels-status-swr-BSHqqCF1.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-api-BuVcZ5zR.js → cron-api-0h_QT8U3.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-BMrloeFH.js → cron-page-BkfKFfFk.js} +1 -1
- package/dist/gateway/static/root/assets/{dist-CKU1OOTf.js → dist-Cmjp2APP.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-BdW_46sN.js → extension-debug-page-CFa9z_1N.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DW47KI82.js → extension-page-BI8eaTPq.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-B-W4x2xP.js → extension-settings-page-x4BB7q1X.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-B2MYHbWg.js → fetch-DRqwef_Q.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-DPG-oJmx.js → field-primitives-BiNHBo2Y.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-C8dNts9i.js → heartbeat-config-api-ZRb8qhuz.js} +1 -1
- package/dist/gateway/static/root/assets/{index-BmVYculr.js → index-Cu7bKuUi.js} +96 -94
- package/dist/gateway/static/root/assets/index-a5gWIdZQ.css +1 -0
- package/dist/gateway/static/root/assets/{logs-page-sTsVWz0X.js → logs-page-BFZ8GgCv.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-FaG_Vlkb.js → sessions-page-CD7AfB-2.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DuvRQW--.js → settings-form-section-DiqqVs6m.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-Bet1OerL.js → settings-page-BBOjEQW3.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-BtG2kLDh.js → share-preview-page-n1Gprylk.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-DhUO235y.js → skills-page-CcN_gj--.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-DryYl3qD.js → theme-store-CZOh1nT3.js} +1 -1
- package/dist/gateway/static/root/assets/url-Dd8Q7kZZ.js +3 -0
- package/dist/gateway/static/root/assets/{utils-BY7bU1DT.js → utils-CkWBfxs4.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-CGEydndO.js → voice-api-key-field-O6awz9hi.js} +1 -1
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-scope.d.ts +4 -0
- package/dist/src/agent/agent-scope.js +53 -10
- package/dist/src/agent/agent-scope.js.map +1 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js +2 -1
- package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
- package/dist/src/agent/embedded/session-tool-result-guard.js +2 -1
- package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
- package/dist/src/agent/embedded/tool-result-truncation.js +2 -1
- package/dist/src/agent/embedded/tool-result-truncation.js.map +1 -1
- package/dist/src/agent/fallback/candidates.js +2 -2
- package/dist/src/agent/fallback/candidates.js.map +1 -1
- package/dist/src/agent/goals/persistent-goal-apis.d.ts +0 -2
- package/dist/src/agent/goals/persistent-goal-service.js +0 -1
- package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
- package/dist/src/agent/image/generation/normalization.js +2 -12
- package/dist/src/agent/image/generation/normalization.js.map +1 -1
- package/dist/src/agent/image/generation/provider-registry.d.ts +4 -8
- package/dist/src/agent/image/generation/provider-registry.js.map +1 -1
- package/dist/src/agent/image/generation/runtime.d.ts +2 -2
- package/dist/src/agent/image/generation/runtime.js.map +1 -1
- package/dist/src/agent/image/generation/types.d.ts +0 -18
- package/dist/src/agent/image/image-helpers.js +6 -1
- package/dist/src/agent/image/image-helpers.js.map +1 -1
- package/dist/src/agent/image/index.d.ts +1 -1
- package/dist/src/agent/inbound/inbound-loop.d.ts +5 -0
- package/dist/src/agent/inbound/inbound-loop.js +41 -10
- package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +4 -0
- package/dist/src/agent/inbound/turn-dispatcher.js +6 -4
- package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-names.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-names.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +2 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +2 -1
- package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +2 -1
- package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
- package/dist/src/agent/media-generation/runtime-shared.js +2 -9
- package/dist/src/agent/media-generation/runtime-shared.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +6 -0
- package/dist/src/agent/messaging/command-handler.js +5 -0
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/prompt/safety.d.ts +0 -7
- package/dist/src/agent/prompt/safety.js +1 -20
- package/dist/src/agent/prompt/safety.js.map +1 -1
- package/dist/src/agent/service/build-direct-message-content.js +1 -1
- package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
- package/dist/src/agent/service/direct-turn-helpers.d.ts +3 -1
- package/dist/src/agent/service/direct-turn-helpers.js +6 -1
- package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
- package/dist/src/agent/service/process-direct-one-shot.d.ts +4 -0
- package/dist/src/agent/service/process-direct-one-shot.js +15 -2
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.d.ts +4 -0
- package/dist/src/agent/service/process-direct-streaming.js +34 -4
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service/webchat-tts.js +1 -1
- package/dist/src/agent/service/webchat-tts.js.map +1 -1
- package/dist/src/agent/service.d.ts +8 -0
- package/dist/src/agent/service.js +21 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/tools/create-share-tool.js +27 -20
- package/dist/src/agent/tools/create-share-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/index.d.ts +0 -1
- package/dist/src/agent/tools/index.js +4 -5
- package/dist/src/agent/tools/shell.js +0 -13
- package/dist/src/agent/tools/shell.js.map +1 -1
- package/dist/src/agent/tools/workflow-tool.js +7 -1
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/workflow/channel-capability.d.ts +3 -3
- package/dist/src/agent/workflow/lint.d.ts +38 -0
- package/dist/src/agent/workflow/lint.js +74 -0
- package/dist/src/agent/workflow/lint.js.map +1 -0
- package/dist/src/agent/workflow/parser.js +4 -1
- package/dist/src/agent/workflow/parser.js.map +1 -1
- package/dist/src/agent/workflow/runtime.d.ts +3 -0
- package/dist/src/agent/workflow/runtime.js +76 -3
- package/dist/src/agent/workflow/runtime.js.map +1 -1
- package/dist/src/agent/workflow/types.d.ts +3 -1
- package/dist/src/browser/index.js +4 -4
- package/dist/src/browser/manager.d.ts +1 -3
- package/dist/src/browser/manager.js +0 -6
- package/dist/src/browser/manager.js.map +1 -1
- package/dist/src/browser/providers/browser-ext-install.d.ts +4 -4
- package/dist/src/browser/providers/browser-ext-install.js +38 -85
- package/dist/src/browser/providers/browser-ext-install.js.map +1 -1
- package/dist/src/browser/providers/cloakbrowser.d.ts +0 -5
- package/dist/src/browser/providers/cloakbrowser.js +2 -55
- package/dist/src/browser/providers/cloakbrowser.js.map +1 -1
- package/dist/src/channels/attachments/voice-stt-webchat.js +10 -8
- package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +9 -9
- package/dist/src/channels/pairing/allow-from-file.js.map +1 -1
- package/dist/src/channels/pairing/pairing-store.js +6 -6
- package/dist/src/channels/pairing/pairing-store.js.map +1 -1
- package/dist/src/chat-commands/builtins/session.js +1 -1
- package/dist/src/chat-commands/builtins/session.js.map +1 -1
- package/dist/src/chat-commands/builtins/tts.js +2 -2
- package/dist/src/chat-commands/builtins/tts.js.map +1 -1
- package/dist/src/chat-commands/context.d.ts +3 -0
- package/dist/src/chat-commands/context.js +21 -3
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/session-key.d.ts +4 -37
- package/dist/src/chat-commands/session-key.js +49 -85
- package/dist/src/chat-commands/session-key.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +2 -0
- package/dist/src/cli/commands/agent/interactive.js +2 -2
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent/sessions.js +2 -2
- package/dist/src/cli/commands/agent/sessions.js.map +1 -1
- package/dist/src/cli/commands/agent.js +4 -5
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/channels.js +1 -5
- package/dist/src/cli/commands/channels.js.map +1 -1
- package/dist/src/cli/commands/gateway/lifecycle-core.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle-core.js.map +1 -1
- package/dist/src/cli/commands/gateway/logs.d.ts +9 -0
- package/dist/src/cli/commands/gateway/logs.js +50 -17
- package/dist/src/cli/commands/gateway/logs.js.map +1 -1
- package/dist/src/cli/commands/image.js +22 -21
- package/dist/src/cli/commands/image.js.map +1 -1
- package/dist/src/cli/commands/session/utils.js +2 -2
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/update.js +26 -46
- package/dist/src/cli/commands/update.js.map +1 -1
- package/dist/src/cli/utils/session.d.ts +0 -5
- package/dist/src/cli/utils/session.js +1 -6
- package/dist/src/cli/utils/session.js.map +1 -1
- package/dist/src/commands/agents.config.js +1 -1
- package/dist/src/commands/agents.config.js.map +1 -1
- package/dist/src/config/agent-profile.js +5 -27
- package/dist/src/config/agent-profile.js.map +1 -1
- package/dist/src/config/index.js +2 -2
- package/dist/src/config/model-input.js +2 -5
- package/dist/src/config/model-input.js.map +1 -1
- package/dist/src/config/schema.d.ts +201 -217
- package/dist/src/config/schema.js +54 -39
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/workspace-path-helpers.d.ts +1 -2
- package/dist/src/config/workspace-path-helpers.js.map +1 -1
- package/dist/src/daemon/install-plan.js +25 -1
- package/dist/src/daemon/install-plan.js.map +1 -1
- package/dist/src/daemon/launchd.d.ts +8 -0
- package/dist/src/daemon/launchd.js +5 -12
- package/dist/src/daemon/launchd.js.map +1 -1
- package/dist/src/daemon/schtasks.d.ts +25 -0
- package/dist/src/daemon/schtasks.js +166 -46
- package/dist/src/daemon/schtasks.js.map +1 -1
- package/dist/src/daemon/service.js +5 -4
- package/dist/src/daemon/service.js.map +1 -1
- package/dist/src/daemon/systemd.d.ts +6 -0
- package/dist/src/daemon/systemd.js +18 -3
- package/dist/src/daemon/systemd.js.map +1 -1
- package/dist/src/extensions/activation-context.js +0 -1
- package/dist/src/extensions/activation-context.js.map +1 -1
- package/dist/src/extensions/normalize-manifest.js +0 -1
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/types/manifest.d.ts +0 -2
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +1 -0
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +10 -2
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/heartbeat/service.js +2 -2
- package/dist/src/gateway/heartbeat/service.js.map +1 -1
- package/dist/src/gateway/hono/app.js +1 -1
- package/dist/src/gateway/hono/lib/agent-model.d.ts +18 -10
- package/dist/src/gateway/hono/lib/agent-model.js +24 -35
- package/dist/src/gateway/hono/lib/agent-model.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/lib/safe-voice-config.js +14 -53
- package/dist/src/gateway/hono/lib/safe-voice-config.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/agents.js +17 -5
- package/dist/src/gateway/hono/routes/config-patch/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/channels.js +0 -11
- package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
- package/dist/src/gateway/hono/routes/goals.js +1 -1
- package/dist/src/gateway/hono/routes/goals.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +28 -7
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/shares.js +14 -12
- package/dist/src/gateway/hono/routes/shares.js.map +1 -1
- package/dist/src/gateway/hono/routes/tunnel.js +1 -1
- package/dist/src/gateway/hono/routes/update.js +4 -2
- package/dist/src/gateway/hono/routes/update.js.map +1 -1
- package/dist/src/gateway/hono/sse.js +16 -33
- package/dist/src/gateway/hono/sse.js.map +1 -1
- package/dist/src/gateway/lock.js +10 -10
- package/dist/src/gateway/lock.js.map +1 -1
- package/dist/src/gateway/ports.js +6 -6
- package/dist/src/gateway/ports.js.map +1 -1
- package/dist/src/gateway/resolve-webchat-session-key.d.ts +19 -0
- package/dist/src/gateway/resolve-webchat-session-key.js +46 -0
- package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -0
- package/dist/src/gateway/service/run-gateway-agent.js +27 -11
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service/sessions-api.d.ts +3 -0
- package/dist/src/gateway/service/sessions-api.js +8 -0
- package/dist/src/gateway/service/sessions-api.js.map +1 -1
- package/dist/src/gateway/service.d.ts +0 -2
- package/dist/src/gateway/service.js +2 -7
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/session-reset-service.d.ts +20 -0
- package/dist/src/gateway/session-reset-service.js +54 -0
- package/dist/src/gateway/session-reset-service.js.map +1 -0
- package/dist/src/gateway/startup-readiness.d.ts +1 -1
- package/dist/src/gateway/startup-readiness.js +1 -0
- package/dist/src/gateway/startup-readiness.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/gateway-processes.js +2 -2
- package/dist/src/infra/gateway-processes.js.map +1 -1
- package/dist/src/infra/run-command.d.ts +16 -0
- package/dist/src/infra/run-command.js +67 -0
- package/dist/src/infra/run-command.js.map +1 -0
- package/dist/src/infra/update-global.d.ts +45 -0
- package/dist/src/infra/update-global.js +224 -0
- package/dist/src/infra/update-global.js.map +1 -0
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/mcp/channel-shared.js +2 -1
- package/dist/src/mcp/channel-shared.js.map +1 -1
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/auth-runtime/auth-profile-store.js.map +1 -1
- package/dist/src/providers/auth-runtime/resolve-auth.js +1 -12
- package/dist/src/providers/auth-runtime/resolve-auth.js.map +1 -1
- package/dist/src/providers/auth-runtime/types.d.ts +6 -12
- package/dist/src/routing/agent-session-key.d.ts +58 -0
- package/dist/src/routing/agent-session-key.js +164 -0
- package/dist/src/routing/agent-session-key.js.map +1 -0
- package/dist/src/routing/index.d.ts +1 -1
- package/dist/src/routing/index.js +4 -2
- package/dist/src/routing/index.js.map +1 -1
- package/dist/src/routing/resolve-route.d.ts +15 -0
- package/dist/src/routing/resolve-route.js +41 -20
- package/dist/src/routing/resolve-route.js.map +1 -1
- package/dist/src/routing/resolve-tui-session-key.d.ts +25 -0
- package/dist/src/routing/resolve-tui-session-key.js +54 -0
- package/dist/src/routing/resolve-tui-session-key.js.map +1 -0
- package/dist/src/routing/session-key-utils.d.ts +24 -0
- package/dist/src/routing/session-key-utils.js +92 -0
- package/dist/src/routing/session-key-utils.js.map +1 -0
- package/dist/src/routing/session-key.d.ts +19 -49
- package/dist/src/routing/session-key.js +143 -116
- package/dist/src/routing/session-key.js.map +1 -1
- package/dist/src/session/index.d.ts +6 -0
- package/dist/src/session/index.js +7 -1
- package/dist/src/session/init-session-turn.d.ts +30 -0
- package/dist/src/session/init-session-turn.js +102 -0
- package/dist/src/session/init-session-turn.js.map +1 -0
- package/dist/src/session/lifecycle-timestamps.d.ts +8 -0
- package/dist/src/session/lifecycle-timestamps.js +16 -0
- package/dist/src/session/lifecycle-timestamps.js.map +1 -0
- package/dist/src/session/manager.d.ts +7 -1
- package/dist/src/session/manager.js +8 -1
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/parity/transcript-paths.js +2 -2
- package/dist/src/session/parity/transcript-paths.js.map +1 -1
- package/dist/src/session/parity/xopc-session-disk-entry.d.ts +6 -0
- package/dist/src/session/reset-policy.d.ts +32 -0
- package/dist/src/session/reset-policy.js +65 -0
- package/dist/src/session/reset-policy.js.map +1 -0
- package/dist/src/session/reset-triggers.d.ts +20 -0
- package/dist/src/session/reset-triggers.js +63 -0
- package/dist/src/session/reset-triggers.js.map +1 -0
- package/dist/src/session/reset-type.d.ts +12 -0
- package/dist/src/session/reset-type.js +25 -0
- package/dist/src/session/reset-type.js.map +1 -0
- package/dist/src/session/resolve-session.d.ts +30 -0
- package/dist/src/session/resolve-session.js +93 -0
- package/dist/src/session/resolve-session.js.map +1 -0
- package/dist/src/session/session-title.js +3 -2
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/session/store.d.ts +11 -4
- package/dist/src/session/store.js +57 -6
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/session/transcript-events.js +2 -1
- package/dist/src/session/transcript-events.js.map +1 -1
- package/dist/src/share/share-url.d.ts +33 -0
- package/dist/src/share/share-url.js +56 -14
- package/dist/src/share/share-url.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.js +4 -9
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.js +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/components/chat-log.js +3 -3
- package/dist/src/tui/components/chat-log.js.map +1 -1
- package/dist/src/tui/theme.d.ts +0 -2
- package/dist/src/tui/theme.js +1 -3
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/tui-commands.d.ts +3 -0
- package/dist/src/tui/tui-commands.js +45 -10
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-keybindings-file.js +1 -21
- package/dist/src/tui/tui-keybindings-file.js.map +1 -1
- package/dist/src/tui/tui-session-actions.d.ts +28 -0
- package/dist/src/tui/tui-session-actions.js +88 -0
- package/dist/src/tui/tui-session-actions.js.map +1 -0
- package/dist/src/tui/tui.js +52 -47
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/utils/string-coerce.d.ts +2 -0
- package/dist/src/utils/string-coerce.js +10 -1
- package/dist/src/utils/string-coerce.js.map +1 -1
- package/dist/src/voice/stt/config-slice.d.ts +2 -5
- package/dist/src/voice/stt/config-slice.js +5 -26
- package/dist/src/voice/stt/config-slice.js.map +1 -1
- package/dist/src/voice/stt/types.d.ts +1 -18
- package/dist/src/voice/stt/types.js +4 -2
- package/dist/src/voice/stt/types.js.map +1 -1
- package/dist/src/voice/tts/config-slice.d.ts +3 -7
- package/dist/src/voice/tts/config-slice.js +7 -38
- package/dist/src/voice/tts/config-slice.js.map +1 -1
- package/dist/src/voice/tts/merge-config.js +2 -48
- package/dist/src/voice/tts/merge-config.js.map +1 -1
- package/dist/src/voice/tts/providers/alibaba-speech.js +1 -1
- package/dist/src/voice/tts/providers/alibaba-speech.js.map +1 -1
- package/dist/src/voice/tts/types.d.ts +1 -29
- package/dist/src/voice/tts/types.js +19 -17
- package/dist/src/voice/tts/types.js.map +1 -1
- package/package.json +1 -4
- package/dist/gateway/static/root/assets/index-ew_2L2We.css +0 -1
- package/dist/gateway/static/root/assets/url-BwNL6Rgk.js +0 -3
- package/dist/src/agent/tools/browser-legacy-tools.d.ts +0 -17
- package/dist/src/agent/tools/browser-legacy-tools.js +0 -766
- package/dist/src/agent/tools/browser-legacy-tools.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-path-helpers.js","names":[],"sources":["../../../src/config/workspace-path-helpers.ts"],"sourcesContent":["/**\n * Workspace-path domain helpers that need both `Config` (from `./schema.js`)\n * and agent-scope helpers (from `../agent/agent-scope.js`).\n *\n * Lives in this file rather than `./schema.js` because schema.ts is the leaf\n * type module — letting it import agent-scope creates a circular cycle\n * (`schema → agent-scope → schema`). Callers import via the `./index.js
|
|
1
|
+
{"version":3,"file":"workspace-path-helpers.js","names":[],"sources":["../../../src/config/workspace-path-helpers.ts"],"sourcesContent":["/**\n * Workspace-path domain helpers that need both `Config` (from `./schema.js`)\n * and agent-scope helpers (from `../agent/agent-scope.js`).\n *\n * Lives in this file rather than `./schema.js` because schema.ts is the leaf\n * type module — letting it import agent-scope creates a circular cycle\n * (`schema → agent-scope → schema`). Callers import via the `./index.js` barrel.\n */\n\nimport { getDefaultWorkspacePath } from '../agent/agent-scope.js';\nimport type { Config } from './schema.js';\n\n/**\n * Default agent's resolved Markdown workspace root\n * (`resolveAgentWorkspaceDir` for the default agent id).\n */\nexport function getWorkspacePath(config: Config): string {\n return getDefaultWorkspacePath(config);\n}\n"],"mappings":";;kBASkE;;;;;AAOlE,SAAgB,iBAAiB,QAAwB;AACvD,QAAO,wBAAwB,OAAO"}
|
|
@@ -34,8 +34,32 @@ function isSEABinary() {
|
|
|
34
34
|
function resolveEntryPoint() {
|
|
35
35
|
if (isSEABinary()) return process.execPath;
|
|
36
36
|
const thisDir = path.dirname(new URL(import.meta.url).pathname);
|
|
37
|
+
const sourceEntryPoint = path.join(thisDir, "..", "cli", "bin.ts");
|
|
38
|
+
if (existsSync(sourceEntryPoint)) return sourceEntryPoint;
|
|
37
39
|
return path.join(thisDir, "..", "cli", "bin.js");
|
|
38
40
|
}
|
|
41
|
+
function resolveServiceNodeArgs() {
|
|
42
|
+
const serviceNodeArgs = [];
|
|
43
|
+
const skipValueFlags = new Set([
|
|
44
|
+
"-e",
|
|
45
|
+
"--eval",
|
|
46
|
+
"-p",
|
|
47
|
+
"--print",
|
|
48
|
+
"--test-name-pattern"
|
|
49
|
+
]);
|
|
50
|
+
for (let index = 0; index < process.execArgv.length; index += 1) {
|
|
51
|
+
const arg = process.execArgv[index];
|
|
52
|
+
if (!arg) continue;
|
|
53
|
+
if (arg.startsWith("--inspect") || arg.startsWith("--debug")) continue;
|
|
54
|
+
if (skipValueFlags.has(arg)) {
|
|
55
|
+
index += 1;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if ([...skipValueFlags].some((flag) => arg.startsWith(`${flag}=`))) continue;
|
|
59
|
+
serviceNodeArgs.push(arg);
|
|
60
|
+
}
|
|
61
|
+
return serviceNodeArgs;
|
|
62
|
+
}
|
|
39
63
|
function buildGatewayInstallPlan(params) {
|
|
40
64
|
const configPath = resolveDefaultConfigPath();
|
|
41
65
|
const workspace = resolveDefaultWorkspace();
|
|
@@ -49,7 +73,7 @@ function buildGatewayInstallPlan(params) {
|
|
|
49
73
|
params.port.toString()
|
|
50
74
|
];
|
|
51
75
|
else {
|
|
52
|
-
const nodeArgs =
|
|
76
|
+
const nodeArgs = resolveServiceNodeArgs();
|
|
53
77
|
const entryPoint = resolveEntryPoint();
|
|
54
78
|
programArguments = [
|
|
55
79
|
process.execPath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-plan.js","names":[],"sources":["../../../src/daemon/install-plan.ts"],"sourcesContent":["/**\n * Install Plan Builder - Build gateway installation configuration\n *\n * Aligned with OpenClaw: adds XOPC_SERVICE_VERSION, StandardOut/ErrorPath,\n * supports binary runtime detection, and --foreground arg passing.\n */\n\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { homedir } from 'node:os';\nimport { createLogger } from '../utils/logger.js';\nimport { SERVICE_VERSION_ENV_KEY, formatGatewayServiceDescription } from './constants.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport type { GatewayServiceInstallArgs, GatewayServiceEnv } from './types.js';\n\nconst log = createLogger('InstallPlan');\n\nexport interface InstallPlan {\n programArguments: string[];\n workingDirectory: string;\n environment: Record<string, string>;\n description: string;\n}\n\n// ─── Path Resolution ───\n\nfunction resolveDefaultConfigPath(): string {\n const envConfig = process.env.XOPC_CONFIG || process.env.XOPC_CONFIG_PATH;\n if (envConfig) return envConfig;\n return path.join(homedir(), '.xopc', 'xopc.json');\n}\n\nfunction resolveDefaultWorkspace(): string {\n const envWorkspace = process.env.XOPC_WORKSPACE;\n if (envWorkspace) return envWorkspace;\n return path.join(homedir(), '.xopc', 'workspace');\n}\n\nfunction resolveLogDir(): string {\n return path.join(homedir(), '.xopc', 'logs');\n}\n\n// ─── Binary Detection ───\n\nfunction isSEABinary(): boolean {\n // Node.js SEA (Single Executable Application) detection\n return !!(process as NodeJS.Process & { pkg?: unknown }).pkg ||\n process.execPath.endsWith('xopc') ||\n process.execPath.endsWith('xopc.exe');\n}\n\nfunction resolveEntryPoint(): string {\n if (isSEABinary()) {\n return process.execPath;\n }\n // Must match package.json \"bin\"
|
|
1
|
+
{"version":3,"file":"install-plan.js","names":[],"sources":["../../../src/daemon/install-plan.ts"],"sourcesContent":["/**\n * Install Plan Builder - Build gateway installation configuration\n *\n * Aligned with OpenClaw: adds XOPC_SERVICE_VERSION, StandardOut/ErrorPath,\n * supports binary runtime detection, and --foreground arg passing.\n */\n\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { homedir } from 'node:os';\nimport { createLogger } from '../utils/logger.js';\nimport { SERVICE_VERSION_ENV_KEY, formatGatewayServiceDescription } from './constants.js';\nimport { PACKAGE_VERSION } from '../package-version.js';\nimport type { GatewayServiceInstallArgs, GatewayServiceEnv } from './types.js';\n\nconst log = createLogger('InstallPlan');\n\nexport interface InstallPlan {\n programArguments: string[];\n workingDirectory: string;\n environment: Record<string, string>;\n description: string;\n}\n\n// ─── Path Resolution ───\n\nfunction resolveDefaultConfigPath(): string {\n const envConfig = process.env.XOPC_CONFIG || process.env.XOPC_CONFIG_PATH;\n if (envConfig) return envConfig;\n return path.join(homedir(), '.xopc', 'xopc.json');\n}\n\nfunction resolveDefaultWorkspace(): string {\n const envWorkspace = process.env.XOPC_WORKSPACE;\n if (envWorkspace) return envWorkspace;\n return path.join(homedir(), '.xopc', 'workspace');\n}\n\nfunction resolveLogDir(): string {\n return path.join(homedir(), '.xopc', 'logs');\n}\n\n// ─── Binary Detection ───\n\nfunction isSEABinary(): boolean {\n // Node.js SEA (Single Executable Application) detection\n return !!(process as NodeJS.Process & { pkg?: unknown }).pkg ||\n process.execPath.endsWith('xopc') ||\n process.execPath.endsWith('xopc.exe');\n}\n\nfunction resolveEntryPoint(): string {\n if (isSEABinary()) {\n return process.execPath;\n }\n // Must match package.json \"bin\" in built packages, while source/dev runs use bin.ts.\n // index.js only exports runCli and exits immediately when executed directly.\n const thisDir = path.dirname(new URL(import.meta.url).pathname);\n const sourceEntryPoint = path.join(thisDir, '..', 'cli', 'bin.ts');\n if (existsSync(sourceEntryPoint)) {\n return sourceEntryPoint;\n }\n return path.join(thisDir, '..', 'cli', 'bin.js');\n}\n\nfunction resolveServiceNodeArgs(): string[] {\n const serviceNodeArgs: string[] = [];\n const skipValueFlags = new Set(['-e', '--eval', '-p', '--print', '--test-name-pattern']);\n\n for (let index = 0; index < process.execArgv.length; index += 1) {\n const arg = process.execArgv[index];\n if (!arg) continue;\n if (arg.startsWith('--inspect') || arg.startsWith('--debug')) continue;\n if (skipValueFlags.has(arg)) {\n index += 1;\n continue;\n }\n if ([...skipValueFlags].some((flag) => arg.startsWith(`${flag}=`))) continue;\n serviceNodeArgs.push(arg);\n }\n\n return serviceNodeArgs;\n}\n\n// ─── Plan Builder ───\n\nexport function buildGatewayInstallPlan(params: {\n port: number;\n bind?: import('../config/schema.js').GatewayBindMode;\n token?: string;\n env?: GatewayServiceEnv;\n runtime?: 'node' | 'binary';\n version?: string;\n}): InstallPlan {\n const configPath = resolveDefaultConfigPath();\n const workspace = resolveDefaultWorkspace();\n const version = params.version || PACKAGE_VERSION;\n\n // Determine program + arguments\n let programArguments: string[];\n\n if (params.runtime === 'binary' || isSEABinary()) {\n // SEA binary: direct execution\n const binaryPath = process.execPath;\n programArguments = [binaryPath, 'gateway', '--foreground', '--port', params.port.toString()];\n } else {\n // Node.js runtime\n const nodeArgs = resolveServiceNodeArgs();\n const entryPoint = resolveEntryPoint();\n programArguments = [\n process.execPath,\n ...nodeArgs,\n entryPoint,\n 'gateway',\n '--foreground',\n '--port', params.port.toString(),\n ];\n }\n\n if (params.bind && params.bind !== 'loopback') {\n programArguments.push('--bind', params.bind);\n }\n\n // Build environment\n const environment: Record<string, string> = {\n XOPC_CONFIG: configPath,\n XOPC_WORKSPACE: workspace,\n XOPC_LOG_LEVEL: process.env.XOPC_LOG_LEVEL || 'info',\n XOPC_LOG_FILE: 'true',\n XOPC_LOG_CONSOLE: 'false',\n XOPC_LOG_DIR: resolveLogDir(),\n [SERVICE_VERSION_ENV_KEY]: version,\n XOPC_SERVICE_MARKER: '1',\n };\n\n if (params.token) {\n environment.XOPC_GATEWAY_TOKEN = params.token;\n }\n\n // Copy relevant API key env vars\n const relevantEnvVars = [\n 'OPENAI_API_KEY',\n 'ANTHROPIC_API_KEY',\n 'GOOGLE_API_KEY',\n 'BRAVE_API_KEY',\n 'DASHSCOPE_API_KEY',\n 'XOPC_LOG_RETENTION_DAYS',\n ];\n\n for (const key of relevantEnvVars) {\n if (process.env[key]) {\n environment[key] = process.env[key]!;\n }\n }\n\n // Add custom env from params (non-XOPC keys only; XOPC keys already handled)\n if (params.env) {\n for (const [key, value] of Object.entries(params.env)) {\n if (value !== undefined && !key.startsWith('XOPC_')) {\n environment[key] = value;\n }\n }\n }\n\n // Service description\n const profile = params.env?.XOPC_PROFILE?.trim() || undefined;\n const description = formatGatewayServiceDescription({ profile, version });\n\n log.info({\n programArguments: programArguments.slice(0, 3),\n envKeys: Object.keys(environment),\n version,\n }, 'Built gateway install plan');\n\n return {\n programArguments,\n workingDirectory: path.dirname(configPath),\n environment,\n description,\n };\n}\n\n// ─── Install Args Builder ───\n\nexport function buildGatewayInstallArgs(params: {\n port: number;\n bind?: import('../config/schema.js').GatewayBindMode;\n token?: string;\n env?: GatewayServiceEnv;\n runtime?: 'node' | 'binary';\n version?: string;\n}): GatewayServiceInstallArgs {\n const plan = buildGatewayInstallPlan(params);\n\n return {\n env: params.env || process.env,\n programArguments: plan.programArguments,\n workingDirectory: plan.workingDirectory,\n environment: plan.environment,\n description: plan.description,\n };\n}\n\n// ─── Validation ───\n\n/** Check if the program in an install plan still exists on disk */\nexport function validateInstallPlanProgram(programArguments: string[]): boolean {\n if (programArguments.length === 0) return false;\n const program = programArguments[0];\n return existsSync(program);\n}\n"],"mappings":"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"}
|
|
@@ -10,5 +10,13 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { GatewayService, GatewayServiceEnv } from './types.js';
|
|
12
12
|
export declare function resolveLaunchAgentPlistPath(env?: GatewayServiceEnv): string;
|
|
13
|
+
export declare function buildLaunchAgentPlist(params: {
|
|
14
|
+
label: string;
|
|
15
|
+
programArguments: string[];
|
|
16
|
+
workingDirectory?: string;
|
|
17
|
+
environment: Record<string, string>;
|
|
18
|
+
stdoutPath?: string;
|
|
19
|
+
stderrPath?: string;
|
|
20
|
+
}): string;
|
|
13
21
|
export declare function isLaunchdAvailable(): boolean;
|
|
14
22
|
export declare const launchdService: GatewayService;
|
|
@@ -71,10 +71,7 @@ ${envDict}
|
|
|
71
71
|
plist += ` <key>RunAtLoad</key>
|
|
72
72
|
<true/>
|
|
73
73
|
<key>KeepAlive</key>
|
|
74
|
-
<
|
|
75
|
-
<key>SuccessfulExit</key>
|
|
76
|
-
<false/>
|
|
77
|
-
</dict>
|
|
74
|
+
<true/>
|
|
78
75
|
<key>ThrottleInterval</key>
|
|
79
76
|
<integer>10</integer>
|
|
80
77
|
<key>ExitTimeOut</key>
|
|
@@ -220,15 +217,11 @@ const launchdService = {
|
|
|
220
217
|
log.info("LaunchAgent stopped and disabled (plist removed)");
|
|
221
218
|
} else {
|
|
222
219
|
try {
|
|
223
|
-
await launchctlExec([
|
|
224
|
-
"kill",
|
|
225
|
-
"SIGTERM",
|
|
226
|
-
serviceTarget
|
|
227
|
-
]);
|
|
220
|
+
await launchctlExec(["bootout", serviceTarget]);
|
|
228
221
|
} catch {
|
|
229
|
-
log.debug("LaunchAgent
|
|
222
|
+
log.debug("LaunchAgent bootout failed (may not be loaded)");
|
|
230
223
|
}
|
|
231
|
-
log.info("LaunchAgent
|
|
224
|
+
log.info("LaunchAgent stopped and unloaded");
|
|
232
225
|
}
|
|
233
226
|
},
|
|
234
227
|
async restart(args) {
|
|
@@ -300,6 +293,6 @@ const launchdService = {
|
|
|
300
293
|
}
|
|
301
294
|
};
|
|
302
295
|
//#endregion
|
|
303
|
-
export { isLaunchdAvailable, launchdService, resolveLaunchAgentPlistPath };
|
|
296
|
+
export { buildLaunchAgentPlist, isLaunchdAvailable, launchdService, resolveLaunchAgentPlistPath };
|
|
304
297
|
|
|
305
298
|
//# sourceMappingURL=launchd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launchd.js","names":["resolvePlistPathFromConstants"],"sources":["../../../src/daemon/launchd.ts"],"sourcesContent":["/**\n * LaunchAgent Service - macOS user service management\n *\n * Aligned with OpenClaw launchd implementation:\n * - KeepAlive with SuccessfulExit=false\n * - ThrottleInterval for restart throttling\n * - ExitTimeOut for graceful shutdown\n * - launchctl bootstrap/bootout for modern service management\n * - launchctl kickstart -k for restart\n */\n\nimport { writeFile, mkdir, readFile, rm } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { spawn, spawnSync } from 'node:child_process';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { createLogger } from '../utils/logger.js';\nimport {\n resolveGatewayLaunchAgentLabel,\n resolveLaunchAgentPlistPath as resolvePlistPathFromConstants,\n LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS,\n LAUNCH_AGENT_EXIT_TIMEOUT_SECONDS,\n} from './constants.js';\nimport type {\n GatewayService,\n GatewayServiceInstallArgs,\n GatewayServiceControlArgs,\n GatewayServiceEnvArgs,\n GatewayServiceRuntime,\n GatewayServiceCommandConfig,\n GatewayServiceEnv,\n GatewayServiceRestartResult,\n} from './types.js';\n\nconst log = createLogger('LaunchdService');\n\n// ─── Domain / Path Resolution ───\n\nfunction resolveGuiDomain(): string {\n const uid = typeof process.getuid === 'function' ? process.getuid() : 501;\n return `gui/${uid}`;\n}\n\nfunction resolveProfileFromEnv(env?: GatewayServiceEnv): string | undefined {\n return env?.XOPC_PROFILE?.trim() || undefined;\n}\n\nexport function resolveLaunchAgentPlistPath(env?: GatewayServiceEnv): string {\n return resolvePlistPathFromConstants(resolveProfileFromEnv(env));\n}\n\nfunction resolveLabelFromEnv(env?: GatewayServiceEnv): string {\n return resolveGatewayLaunchAgentLabel(resolveProfileFromEnv(env));\n}\n\nfunction resolveServiceTarget(env?: GatewayServiceEnv): string {\n return `${resolveGuiDomain()}/${resolveLabelFromEnv(env)}`;\n}\n\nfunction resolveLogDir(): string {\n return path.join(os.homedir(), '.xopc', 'logs');\n}\n\n// ─── Plist Generation ───\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction buildLaunchAgentPlist(params: {\n label: string;\n programArguments: string[];\n workingDirectory?: string;\n environment: Record<string, string>;\n stdoutPath?: string;\n stderrPath?: string;\n}): string {\n const envDict = Object.entries(params.environment)\n .map(([k, v]) => ` <key>${k}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n const programArgs = params.programArguments\n .map((arg) => ` <string>${escapeXml(arg)}</string>`)\n .join('\\n');\n\n let plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${escapeXml(params.label)}</string>\n <key>ProgramArguments</key>\n <array>\n${programArgs}\n </array>\n`;\n\n if (params.workingDirectory) {\n plist += ` <key>WorkingDirectory</key>\n <string>${escapeXml(params.workingDirectory)}</string>\n`;\n }\n\n if (Object.keys(params.environment).length > 0) {\n plist += ` <key>EnvironmentVariables</key>\n <dict>\n${envDict}\n </dict>\n`;\n }\n\n if (params.stdoutPath) {\n plist += ` <key>StandardOutPath</key>\n <string>${escapeXml(params.stdoutPath)}</string>\n`;\n }\n\n if (params.stderrPath) {\n plist += ` <key>StandardErrorPath</key>\n <string>${escapeXml(params.stderrPath)}</string>\n`;\n }\n\n plist += ` <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n <key>ThrottleInterval</key>\n <integer>${LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS}</integer>\n <key>ExitTimeOut</key>\n <integer>${LAUNCH_AGENT_EXIT_TIMEOUT_SECONDS}</integer>\n <key>ProcessType</key>\n <string>Interactive</string>\n</dict>\n</plist>`;\n\n return plist;\n}\n\n// ─── launchctl Execution ───\n\ninterface LaunchctlResult {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\nasync function launchctl(args: string[]): Promise<LaunchctlResult> {\n return new Promise<LaunchctlResult>((resolve, reject) => {\n const child = spawn('launchctl', args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout?.on('data', (data) => { stdout += data.toString(); });\n child.stderr?.on('data', (data) => { stderr += data.toString(); });\n\n child.on('close', (code) => {\n resolve({ stdout, stderr, exitCode: code });\n });\n child.on('error', (err) => {\n reject(new Error(`launchctl spawn failed: ${err.message}`));\n });\n });\n}\n\nasync function launchctlExec(args: string[]): Promise<string> {\n const result = await launchctl(args);\n if (result.stderr.trim()) {\n log.debug({ stderr: result.stderr.trim(), args }, 'launchctl stderr');\n }\n return result.stdout;\n}\n\n// ─── Availability Check ───\n\nexport function isLaunchdAvailable(): boolean {\n if (process.platform !== 'darwin') return false;\n try {\n const result = spawnSync('launchctl', ['version'], {\n stdio: ['ignore', 'pipe', 'ignore'],\n timeout: 3000,\n });\n return result.status === 0;\n } catch {\n return false;\n }\n}\n\n// ─── Plist Parsing ───\n\nfunction parsePlistProgramArguments(content: string): string[] {\n const argsMatch = content.match(\n /<key>ProgramArguments<\\/key>\\s*<array>([\\s\\S]*?)<\\/array>/,\n );\n if (!argsMatch) return [];\n\n const programArgs: string[] = [];\n const stringMatches = argsMatch[1].matchAll(/<string>([\\s\\S]*?)<\\/string>/g);\n for (const m of stringMatches) {\n programArgs.push(unescapeXml(m[1]));\n }\n return programArgs;\n}\n\nfunction parsePlistEnvironment(content: string): Record<string, string> {\n const envMatch = content.match(\n /<key>EnvironmentVariables<\\/key>\\s*<dict>([\\s\\S]*?)<\\/dict>/,\n );\n if (!envMatch) return {};\n\n const environment: Record<string, string> = {};\n const pairs = envMatch[1].matchAll(\n /<key>([\\s\\S]*?)<\\/key>\\s*<string>([\\s\\S]*?)<\\/string>/g,\n );\n for (const pair of pairs) {\n environment[unescapeXml(pair[1])] = unescapeXml(pair[2]);\n }\n return environment;\n}\n\nfunction parsePlistStringValue(content: string, key: string): string | undefined {\n const regex = new RegExp(`<key>${key}</key>\\\\s*<string>([\\\\s\\\\S]*?)</string>`);\n const match = content.match(regex);\n return match ? unescapeXml(match[1]) : undefined;\n}\n\nfunction unescapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n}\n\n// ─── Service Implementation ───\n\nexport const launchdService: GatewayService = {\n label: resolveGatewayLaunchAgentLabel(),\n loadedText: 'LaunchAgent (loaded)',\n notLoadedText: 'LaunchAgent (not loaded)',\n\n async install(args: GatewayServiceInstallArgs): Promise<void> {\n const label = resolveLabelFromEnv(args.env);\n const plistPath = resolveLaunchAgentPlistPath(args.env);\n const logDir = resolveLogDir();\n\n // Ensure directories exist\n await mkdir(path.dirname(plistPath), { recursive: true });\n await mkdir(logDir, { recursive: true });\n\n // Build environment\n const environment: Record<string, string> = {};\n if (args.environment) {\n Object.assign(environment, args.environment);\n }\n\n // Build plist\n const plist = buildLaunchAgentPlist({\n label,\n programArguments: args.programArguments,\n workingDirectory: args.workingDirectory,\n environment,\n stdoutPath: path.join(logDir, 'gateway.log'),\n stderrPath: path.join(logDir, 'gateway.err.log'),\n });\n\n // Write plist file\n await writeFile(plistPath, plist, 'utf8');\n args.stdout?.write(`Written: ${plistPath}\\n`);\n\n // Bootstrap the service\n const domain = resolveGuiDomain();\n const result = await launchctl(['bootstrap', domain, plistPath]);\n if (result.exitCode !== 0 && result.exitCode !== 37) {\n // exit 37 = already loaded, acceptable\n const detail = result.stderr.trim() || result.stdout.trim();\n if (detail) {\n log.warn({ detail, exitCode: result.exitCode }, 'launchctl bootstrap warning');\n }\n }\n\n log.info({ label, plistPath }, 'LaunchAgent installed and bootstrapped');\n },\n\n async uninstall(args: GatewayServiceControlArgs): Promise<void> {\n const serviceTarget = resolveServiceTarget(args.env);\n const plistPath = resolveLaunchAgentPlistPath(args.env);\n\n // Bootout the service (stops + unloads)\n try {\n await launchctlExec(['bootout', serviceTarget]);\n } catch {\n // Ignore if not loaded\n }\n\n // Remove plist file\n if (existsSync(plistPath)) {\n await rm(plistPath);\n args.stdout?.write(`Removed: ${plistPath}\\n`);\n }\n\n log.info({ plistPath }, 'LaunchAgent uninstalled');\n },\n\n async stop(args: GatewayServiceControlArgs): Promise<void> {\n const serviceTarget = resolveServiceTarget(args.env);\n\n if (args.disable) {\n // Disable + bootout: service won't respawn\n const plistPath = resolveLaunchAgentPlistPath(args.env);\n try {\n await launchctlExec(['bootout', serviceTarget]);\n } catch {\n // Ignore\n }\n if (existsSync(plistPath)) {\n await rm(plistPath);\n }\n log.info('LaunchAgent stopped and disabled (plist removed)');\n } else {\n // Send SIGTERM via launchctl kill\n try {\n await launchctlExec(['kill', 'SIGTERM', serviceTarget]);\n } catch {\n // Service might not be running\n log.debug('LaunchAgent kill SIGTERM failed (may not be running)');\n }\n log.info('LaunchAgent stop signal sent');\n }\n },\n\n async restart(args: GatewayServiceControlArgs): Promise<GatewayServiceRestartResult> {\n const serviceTarget = resolveServiceTarget(args.env);\n\n // Use kickstart -k for reliable restart (kills current + starts new)\n const result = await launchctl(['kickstart', '-k', serviceTarget]);\n\n if (result.exitCode === 0) {\n log.info('LaunchAgent restarted via kickstart');\n return { outcome: 'restarted' };\n }\n\n // Fallback: bootout + bootstrap\n const plistPath = resolveLaunchAgentPlistPath(args.env);\n const domain = resolveGuiDomain();\n\n try {\n await launchctlExec(['bootout', serviceTarget]);\n } catch {\n // Ignore\n }\n\n const bootstrapResult = await launchctl(['bootstrap', domain, plistPath]);\n if (bootstrapResult.exitCode === 0 || bootstrapResult.exitCode === 37) {\n log.info('LaunchAgent restarted via bootout+bootstrap');\n return { outcome: 'restarted' };\n }\n\n throw new Error(\n `Failed to restart LaunchAgent: ${bootstrapResult.stderr.trim() || 'unknown error'}`,\n );\n },\n\n async isLoaded(args: GatewayServiceEnvArgs): Promise<boolean> {\n const serviceTarget = resolveServiceTarget(args.env);\n const result = await launchctl(['print', serviceTarget]);\n return result.exitCode === 0;\n },\n\n async readRuntime(env?: GatewayServiceEnv): Promise<GatewayServiceRuntime> {\n const serviceTarget = resolveServiceTarget(env);\n\n try {\n const result = await launchctl(['print', serviceTarget]);\n if (result.exitCode !== 0) {\n return { status: 'stopped' };\n }\n\n const output = result.stdout;\n\n // Parse PID\n let pid: number | undefined;\n const pidMatch = output.match(/pid\\s*=\\s*(\\d+)/);\n if (pidMatch) {\n const parsed = parseInt(pidMatch[1], 10);\n if (parsed > 0) pid = parsed;\n }\n\n // Parse last exit status\n let lastExitStatus: number | undefined;\n const exitMatch = output.match(/last exit code\\s*=\\s*(\\d+)/i);\n if (exitMatch) {\n lastExitStatus = parseInt(exitMatch[1], 10);\n }\n\n // Determine status from PID presence\n const status = pid ? 'running' : 'stopped';\n\n return { status, pid, lastExitStatus };\n } catch {\n return { status: 'unknown' };\n }\n },\n\n async readCommand(env?: GatewayServiceEnv): Promise<GatewayServiceCommandConfig | null> {\n const plistPath = resolveLaunchAgentPlistPath(env);\n if (!existsSync(plistPath)) return null;\n\n const content = await readFile(plistPath, 'utf8');\n\n const programArguments = parsePlistProgramArguments(content);\n if (programArguments.length === 0) return null;\n\n const environment = parsePlistEnvironment(content);\n const workingDirectory = parsePlistStringValue(content, 'WorkingDirectory');\n\n return {\n programArguments,\n workingDirectory,\n environment,\n sourcePath: plistPath,\n };\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"launchd.js","names":["resolvePlistPathFromConstants"],"sources":["../../../src/daemon/launchd.ts"],"sourcesContent":["/**\n * LaunchAgent Service - macOS user service management\n *\n * Aligned with OpenClaw launchd implementation:\n * - KeepAlive with SuccessfulExit=false\n * - ThrottleInterval for restart throttling\n * - ExitTimeOut for graceful shutdown\n * - launchctl bootstrap/bootout for modern service management\n * - launchctl kickstart -k for restart\n */\n\nimport { writeFile, mkdir, readFile, rm } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { spawn, spawnSync } from 'node:child_process';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { createLogger } from '../utils/logger.js';\nimport {\n resolveGatewayLaunchAgentLabel,\n resolveLaunchAgentPlistPath as resolvePlistPathFromConstants,\n LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS,\n LAUNCH_AGENT_EXIT_TIMEOUT_SECONDS,\n} from './constants.js';\nimport type {\n GatewayService,\n GatewayServiceInstallArgs,\n GatewayServiceControlArgs,\n GatewayServiceEnvArgs,\n GatewayServiceRuntime,\n GatewayServiceCommandConfig,\n GatewayServiceEnv,\n GatewayServiceRestartResult,\n} from './types.js';\n\nconst log = createLogger('LaunchdService');\n\n// ─── Domain / Path Resolution ───\n\nfunction resolveGuiDomain(): string {\n const uid = typeof process.getuid === 'function' ? process.getuid() : 501;\n return `gui/${uid}`;\n}\n\nfunction resolveProfileFromEnv(env?: GatewayServiceEnv): string | undefined {\n return env?.XOPC_PROFILE?.trim() || undefined;\n}\n\nexport function resolveLaunchAgentPlistPath(env?: GatewayServiceEnv): string {\n return resolvePlistPathFromConstants(resolveProfileFromEnv(env));\n}\n\nfunction resolveLabelFromEnv(env?: GatewayServiceEnv): string {\n return resolveGatewayLaunchAgentLabel(resolveProfileFromEnv(env));\n}\n\nfunction resolveServiceTarget(env?: GatewayServiceEnv): string {\n return `${resolveGuiDomain()}/${resolveLabelFromEnv(env)}`;\n}\n\nfunction resolveLogDir(): string {\n return path.join(os.homedir(), '.xopc', 'logs');\n}\n\n// ─── Plist Generation ───\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nexport function buildLaunchAgentPlist(params: {\n label: string;\n programArguments: string[];\n workingDirectory?: string;\n environment: Record<string, string>;\n stdoutPath?: string;\n stderrPath?: string;\n}): string {\n const envDict = Object.entries(params.environment)\n .map(([k, v]) => ` <key>${k}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n const programArgs = params.programArguments\n .map((arg) => ` <string>${escapeXml(arg)}</string>`)\n .join('\\n');\n\n let plist = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${escapeXml(params.label)}</string>\n <key>ProgramArguments</key>\n <array>\n${programArgs}\n </array>\n`;\n\n if (params.workingDirectory) {\n plist += ` <key>WorkingDirectory</key>\n <string>${escapeXml(params.workingDirectory)}</string>\n`;\n }\n\n if (Object.keys(params.environment).length > 0) {\n plist += ` <key>EnvironmentVariables</key>\n <dict>\n${envDict}\n </dict>\n`;\n }\n\n if (params.stdoutPath) {\n plist += ` <key>StandardOutPath</key>\n <string>${escapeXml(params.stdoutPath)}</string>\n`;\n }\n\n if (params.stderrPath) {\n plist += ` <key>StandardErrorPath</key>\n <string>${escapeXml(params.stderrPath)}</string>\n`;\n }\n\n plist += ` <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <true/>\n <key>ThrottleInterval</key>\n <integer>${LAUNCH_AGENT_THROTTLE_INTERVAL_SECONDS}</integer>\n <key>ExitTimeOut</key>\n <integer>${LAUNCH_AGENT_EXIT_TIMEOUT_SECONDS}</integer>\n <key>ProcessType</key>\n <string>Interactive</string>\n</dict>\n</plist>`;\n\n return plist;\n}\n\n// ─── launchctl Execution ───\n\ninterface LaunchctlResult {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\nasync function launchctl(args: string[]): Promise<LaunchctlResult> {\n return new Promise<LaunchctlResult>((resolve, reject) => {\n const child = spawn('launchctl', args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout?.on('data', (data) => { stdout += data.toString(); });\n child.stderr?.on('data', (data) => { stderr += data.toString(); });\n\n child.on('close', (code) => {\n resolve({ stdout, stderr, exitCode: code });\n });\n child.on('error', (err) => {\n reject(new Error(`launchctl spawn failed: ${err.message}`));\n });\n });\n}\n\nasync function launchctlExec(args: string[]): Promise<string> {\n const result = await launchctl(args);\n if (result.stderr.trim()) {\n log.debug({ stderr: result.stderr.trim(), args }, 'launchctl stderr');\n }\n return result.stdout;\n}\n\n// ─── Availability Check ───\n\nexport function isLaunchdAvailable(): boolean {\n if (process.platform !== 'darwin') return false;\n try {\n const result = spawnSync('launchctl', ['version'], {\n stdio: ['ignore', 'pipe', 'ignore'],\n timeout: 3000,\n });\n return result.status === 0;\n } catch {\n return false;\n }\n}\n\n// ─── Plist Parsing ───\n\nfunction parsePlistProgramArguments(content: string): string[] {\n const argsMatch = content.match(\n /<key>ProgramArguments<\\/key>\\s*<array>([\\s\\S]*?)<\\/array>/,\n );\n if (!argsMatch) return [];\n\n const programArgs: string[] = [];\n const stringMatches = argsMatch[1].matchAll(/<string>([\\s\\S]*?)<\\/string>/g);\n for (const m of stringMatches) {\n programArgs.push(unescapeXml(m[1]));\n }\n return programArgs;\n}\n\nfunction parsePlistEnvironment(content: string): Record<string, string> {\n const envMatch = content.match(\n /<key>EnvironmentVariables<\\/key>\\s*<dict>([\\s\\S]*?)<\\/dict>/,\n );\n if (!envMatch) return {};\n\n const environment: Record<string, string> = {};\n const pairs = envMatch[1].matchAll(\n /<key>([\\s\\S]*?)<\\/key>\\s*<string>([\\s\\S]*?)<\\/string>/g,\n );\n for (const pair of pairs) {\n environment[unescapeXml(pair[1])] = unescapeXml(pair[2]);\n }\n return environment;\n}\n\nfunction parsePlistStringValue(content: string, key: string): string | undefined {\n const regex = new RegExp(`<key>${key}</key>\\\\s*<string>([\\\\s\\\\S]*?)</string>`);\n const match = content.match(regex);\n return match ? unescapeXml(match[1]) : undefined;\n}\n\nfunction unescapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n}\n\n// ─── Service Implementation ───\n\nexport const launchdService: GatewayService = {\n label: resolveGatewayLaunchAgentLabel(),\n loadedText: 'LaunchAgent (loaded)',\n notLoadedText: 'LaunchAgent (not loaded)',\n\n async install(args: GatewayServiceInstallArgs): Promise<void> {\n const label = resolveLabelFromEnv(args.env);\n const plistPath = resolveLaunchAgentPlistPath(args.env);\n const logDir = resolveLogDir();\n\n // Ensure directories exist\n await mkdir(path.dirname(plistPath), { recursive: true });\n await mkdir(logDir, { recursive: true });\n\n // Build environment\n const environment: Record<string, string> = {};\n if (args.environment) {\n Object.assign(environment, args.environment);\n }\n\n // Build plist\n const plist = buildLaunchAgentPlist({\n label,\n programArguments: args.programArguments,\n workingDirectory: args.workingDirectory,\n environment,\n stdoutPath: path.join(logDir, 'gateway.log'),\n stderrPath: path.join(logDir, 'gateway.err.log'),\n });\n\n // Write plist file\n await writeFile(plistPath, plist, 'utf8');\n args.stdout?.write(`Written: ${plistPath}\\n`);\n\n // Bootstrap the service\n const domain = resolveGuiDomain();\n const result = await launchctl(['bootstrap', domain, plistPath]);\n if (result.exitCode !== 0 && result.exitCode !== 37) {\n // exit 37 = already loaded, acceptable\n const detail = result.stderr.trim() || result.stdout.trim();\n if (detail) {\n log.warn({ detail, exitCode: result.exitCode }, 'launchctl bootstrap warning');\n }\n }\n\n log.info({ label, plistPath }, 'LaunchAgent installed and bootstrapped');\n },\n\n async uninstall(args: GatewayServiceControlArgs): Promise<void> {\n const serviceTarget = resolveServiceTarget(args.env);\n const plistPath = resolveLaunchAgentPlistPath(args.env);\n\n // Bootout the service (stops + unloads)\n try {\n await launchctlExec(['bootout', serviceTarget]);\n } catch {\n // Ignore if not loaded\n }\n\n // Remove plist file\n if (existsSync(plistPath)) {\n await rm(plistPath);\n args.stdout?.write(`Removed: ${plistPath}\\n`);\n }\n\n log.info({ plistPath }, 'LaunchAgent uninstalled');\n },\n\n async stop(args: GatewayServiceControlArgs): Promise<void> {\n const serviceTarget = resolveServiceTarget(args.env);\n\n if (args.disable) {\n // Disable + bootout: service won't respawn\n const plistPath = resolveLaunchAgentPlistPath(args.env);\n try {\n await launchctlExec(['bootout', serviceTarget]);\n } catch {\n // Ignore\n }\n if (existsSync(plistPath)) {\n await rm(plistPath);\n }\n log.info('LaunchAgent stopped and disabled (plist removed)');\n } else {\n try {\n await launchctlExec(['bootout', serviceTarget]);\n } catch {\n // Service might not be running or loaded.\n log.debug('LaunchAgent bootout failed (may not be loaded)');\n }\n log.info('LaunchAgent stopped and unloaded');\n }\n },\n\n async restart(args: GatewayServiceControlArgs): Promise<GatewayServiceRestartResult> {\n const serviceTarget = resolveServiceTarget(args.env);\n\n // Use kickstart -k for reliable restart (kills current + starts new)\n const result = await launchctl(['kickstart', '-k', serviceTarget]);\n\n if (result.exitCode === 0) {\n log.info('LaunchAgent restarted via kickstart');\n return { outcome: 'restarted' };\n }\n\n // Fallback: bootout + bootstrap\n const plistPath = resolveLaunchAgentPlistPath(args.env);\n const domain = resolveGuiDomain();\n\n try {\n await launchctlExec(['bootout', serviceTarget]);\n } catch {\n // Ignore\n }\n\n const bootstrapResult = await launchctl(['bootstrap', domain, plistPath]);\n if (bootstrapResult.exitCode === 0 || bootstrapResult.exitCode === 37) {\n log.info('LaunchAgent restarted via bootout+bootstrap');\n return { outcome: 'restarted' };\n }\n\n throw new Error(\n `Failed to restart LaunchAgent: ${bootstrapResult.stderr.trim() || 'unknown error'}`,\n );\n },\n\n async isLoaded(args: GatewayServiceEnvArgs): Promise<boolean> {\n const serviceTarget = resolveServiceTarget(args.env);\n const result = await launchctl(['print', serviceTarget]);\n return result.exitCode === 0;\n },\n\n async readRuntime(env?: GatewayServiceEnv): Promise<GatewayServiceRuntime> {\n const serviceTarget = resolveServiceTarget(env);\n\n try {\n const result = await launchctl(['print', serviceTarget]);\n if (result.exitCode !== 0) {\n return { status: 'stopped' };\n }\n\n const output = result.stdout;\n\n // Parse PID\n let pid: number | undefined;\n const pidMatch = output.match(/pid\\s*=\\s*(\\d+)/);\n if (pidMatch) {\n const parsed = parseInt(pidMatch[1], 10);\n if (parsed > 0) pid = parsed;\n }\n\n // Parse last exit status\n let lastExitStatus: number | undefined;\n const exitMatch = output.match(/last exit code\\s*=\\s*(\\d+)/i);\n if (exitMatch) {\n lastExitStatus = parseInt(exitMatch[1], 10);\n }\n\n // Determine status from PID presence\n const status = pid ? 'running' : 'stopped';\n\n return { status, pid, lastExitStatus };\n } catch {\n return { status: 'unknown' };\n }\n },\n\n async readCommand(env?: GatewayServiceEnv): Promise<GatewayServiceCommandConfig | null> {\n const plistPath = resolveLaunchAgentPlistPath(env);\n if (!existsSync(plistPath)) return null;\n\n const content = await readFile(plistPath, 'utf8');\n\n const programArguments = parsePlistProgramArguments(content);\n if (programArguments.length === 0) return null;\n\n const environment = parsePlistEnvironment(content);\n const workingDirectory = parsePlistStringValue(content, 'WorkingDirectory');\n\n return {\n programArguments,\n workingDirectory,\n environment,\n sourcePath: plistPath,\n };\n },\n};\n"],"mappings":"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"}
|
|
@@ -7,5 +7,30 @@
|
|
|
7
7
|
* - Proper start/stop/restart lifecycle
|
|
8
8
|
*/
|
|
9
9
|
import type { GatewayService } from './types.js';
|
|
10
|
+
declare function resolveTaskCommandSidecarPath(taskName: string): string;
|
|
11
|
+
declare function resolveTaskWrapperPath(taskName: string): string;
|
|
12
|
+
declare function resolveTaskXmlPath(taskName: string): string;
|
|
13
|
+
declare function escapeCmdSetValue(value: string): string;
|
|
14
|
+
declare function quoteCmdArg(value: string): string;
|
|
15
|
+
declare function buildTaskWrapperContent(params: {
|
|
16
|
+
programArguments: string[];
|
|
17
|
+
workingDirectory?: string;
|
|
18
|
+
environment?: Record<string, string>;
|
|
19
|
+
}): string;
|
|
20
|
+
declare function buildTaskXml(params: {
|
|
21
|
+
description: string;
|
|
22
|
+
wrapperPath: string;
|
|
23
|
+
workingDirectory?: string;
|
|
24
|
+
}): string;
|
|
25
|
+
export declare const schtasksTestInternals: {
|
|
26
|
+
buildTaskWrapperContent: typeof buildTaskWrapperContent;
|
|
27
|
+
buildTaskXml: typeof buildTaskXml;
|
|
28
|
+
escapeCmdSetValue: typeof escapeCmdSetValue;
|
|
29
|
+
quoteCmdArg: typeof quoteCmdArg;
|
|
30
|
+
resolveTaskCommandSidecarPath: typeof resolveTaskCommandSidecarPath;
|
|
31
|
+
resolveTaskWrapperPath: typeof resolveTaskWrapperPath;
|
|
32
|
+
resolveTaskXmlPath: typeof resolveTaskXmlPath;
|
|
33
|
+
};
|
|
10
34
|
export declare function isSchtasksAvailable(): boolean;
|
|
11
35
|
export declare const schtasksService: GatewayService;
|
|
36
|
+
export {};
|
|
@@ -24,14 +24,11 @@ function resolveTaskName(env) {
|
|
|
24
24
|
}
|
|
25
25
|
async function schtasks(args) {
|
|
26
26
|
return new Promise((resolve, reject) => {
|
|
27
|
-
const child = spawn("schtasks", args, {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
],
|
|
33
|
-
shell: true
|
|
34
|
-
});
|
|
27
|
+
const child = spawn("schtasks", args, { stdio: [
|
|
28
|
+
"ignore",
|
|
29
|
+
"pipe",
|
|
30
|
+
"pipe"
|
|
31
|
+
] });
|
|
35
32
|
let stdout = "";
|
|
36
33
|
let stderr = "";
|
|
37
34
|
child.stdout?.on("data", (data) => {
|
|
@@ -60,9 +57,87 @@ async function schtasksExec(args) {
|
|
|
60
57
|
}
|
|
61
58
|
return result.stdout;
|
|
62
59
|
}
|
|
60
|
+
function resolveTaskDaemonDir() {
|
|
61
|
+
return path.join(os.homedir(), ".xopc", "daemon");
|
|
62
|
+
}
|
|
63
63
|
function resolveTaskEnvSidecarPath(taskName) {
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
return path.join(resolveTaskDaemonDir(), `${taskName}.env.json`);
|
|
65
|
+
}
|
|
66
|
+
function resolveTaskCommandSidecarPath(taskName) {
|
|
67
|
+
return path.join(resolveTaskDaemonDir(), `${taskName}.command.json`);
|
|
68
|
+
}
|
|
69
|
+
function resolveTaskWrapperPath(taskName) {
|
|
70
|
+
return path.join(resolveTaskDaemonDir(), `${taskName}.cmd`);
|
|
71
|
+
}
|
|
72
|
+
function resolveTaskXmlPath(taskName) {
|
|
73
|
+
return path.join(resolveTaskDaemonDir(), `${taskName}.xml`);
|
|
74
|
+
}
|
|
75
|
+
function escapeCmdSetValue(value) {
|
|
76
|
+
return value.replace(/\^/g, "^^").replace(/%/g, "%%").replace(/"/g, "^\"").replace(/&/g, "^&").replace(/</g, "^<").replace(/>/g, "^>").replace(/\|/g, "^|");
|
|
77
|
+
}
|
|
78
|
+
function quoteCmdArg(value) {
|
|
79
|
+
return `"${value.replace(/"/g, "\\\"")}"`;
|
|
80
|
+
}
|
|
81
|
+
function escapeXmlValue(value) {
|
|
82
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
83
|
+
}
|
|
84
|
+
function buildTaskWrapperContent(params) {
|
|
85
|
+
const lines = ["@echo off"];
|
|
86
|
+
for (const [key, value] of Object.entries(params.environment ?? {})) lines.push(`set "${key}=${escapeCmdSetValue(value)}"`);
|
|
87
|
+
if (params.workingDirectory) lines.push(`cd /d ${quoteCmdArg(params.workingDirectory)}`);
|
|
88
|
+
lines.push(params.programArguments.map(quoteCmdArg).join(" "));
|
|
89
|
+
return `${lines.join("\r\n")}\r\n`;
|
|
90
|
+
}
|
|
91
|
+
function buildTaskXml(params) {
|
|
92
|
+
const escapedDescription = escapeXmlValue(params.description);
|
|
93
|
+
const escapedWrapperPath = escapeXmlValue(params.wrapperPath);
|
|
94
|
+
const escapedWorkingDirectory = params.workingDirectory ? escapeXmlValue(params.workingDirectory) : "";
|
|
95
|
+
return `<?xml version="1.0" encoding="UTF-16"?>
|
|
96
|
+
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
97
|
+
<RegistrationInfo>
|
|
98
|
+
<Description>${escapedDescription}</Description>
|
|
99
|
+
</RegistrationInfo>
|
|
100
|
+
<Triggers>
|
|
101
|
+
<LogonTrigger>
|
|
102
|
+
<Enabled>true</Enabled>
|
|
103
|
+
</LogonTrigger>
|
|
104
|
+
</Triggers>
|
|
105
|
+
<Principals>
|
|
106
|
+
<Principal id="Author">
|
|
107
|
+
<LogonType>InteractiveToken</LogonType>
|
|
108
|
+
<RunLevel>LeastPrivilege</RunLevel>
|
|
109
|
+
</Principal>
|
|
110
|
+
</Principals>
|
|
111
|
+
<Settings>
|
|
112
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
113
|
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
114
|
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
115
|
+
<AllowHardTerminate>true</AllowHardTerminate>
|
|
116
|
+
<StartWhenAvailable>true</StartWhenAvailable>
|
|
117
|
+
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
118
|
+
<IdleSettings>
|
|
119
|
+
<StopOnIdleEnd>false</StopOnIdleEnd>
|
|
120
|
+
<RestartOnIdle>false</RestartOnIdle>
|
|
121
|
+
</IdleSettings>
|
|
122
|
+
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
123
|
+
<Enabled>true</Enabled>
|
|
124
|
+
<Hidden>false</Hidden>
|
|
125
|
+
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
126
|
+
<WakeToRun>false</WakeToRun>
|
|
127
|
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
128
|
+
<Priority>7</Priority>
|
|
129
|
+
<RestartOnFailure>
|
|
130
|
+
<Interval>PT1M</Interval>
|
|
131
|
+
<Count>999</Count>
|
|
132
|
+
</RestartOnFailure>
|
|
133
|
+
</Settings>
|
|
134
|
+
<Actions Context="Author">
|
|
135
|
+
<Exec>
|
|
136
|
+
<Command>${escapedWrapperPath}</Command>${escapedWorkingDirectory ? `\n <WorkingDirectory>${escapedWorkingDirectory}</WorkingDirectory>` : ""}
|
|
137
|
+
</Exec>
|
|
138
|
+
</Actions>
|
|
139
|
+
</Task>
|
|
140
|
+
`;
|
|
66
141
|
}
|
|
67
142
|
function writeTaskEnvSidecar(taskName, environment) {
|
|
68
143
|
const sidecarPath = resolveTaskEnvSidecarPath(taskName);
|
|
@@ -84,12 +159,72 @@ function readTaskEnvSidecar(taskName) {
|
|
|
84
159
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
85
160
|
} catch {}
|
|
86
161
|
}
|
|
87
|
-
function
|
|
88
|
-
const sidecarPath =
|
|
162
|
+
function writeTaskCommandSidecar(taskName, args) {
|
|
163
|
+
const sidecarPath = resolveTaskCommandSidecarPath(taskName);
|
|
89
164
|
try {
|
|
90
|
-
|
|
165
|
+
writeFileSync(sidecarPath, JSON.stringify({
|
|
166
|
+
programArguments: args.programArguments,
|
|
167
|
+
workingDirectory: args.workingDirectory
|
|
168
|
+
}, null, 2), "utf8");
|
|
169
|
+
} catch (err) {
|
|
170
|
+
log.warn({
|
|
171
|
+
err,
|
|
172
|
+
sidecarPath
|
|
173
|
+
}, "Failed to write task command sidecar");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function readTaskCommandSidecar(taskName) {
|
|
177
|
+
const sidecarPath = resolveTaskCommandSidecarPath(taskName);
|
|
178
|
+
try {
|
|
179
|
+
const raw = readFileSync(sidecarPath, "utf8");
|
|
180
|
+
const parsed = JSON.parse(raw);
|
|
181
|
+
if (parsed && typeof parsed === "object" && Array.isArray(parsed.programArguments) && parsed.programArguments.every((arg) => typeof arg === "string")) return {
|
|
182
|
+
programArguments: parsed.programArguments,
|
|
183
|
+
workingDirectory: typeof parsed.workingDirectory === "string" ? parsed.workingDirectory : void 0
|
|
184
|
+
};
|
|
91
185
|
} catch {}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
function writeTaskSupportFiles(taskName, args) {
|
|
189
|
+
const wrapperPath = resolveTaskWrapperPath(taskName);
|
|
190
|
+
const xmlPath = resolveTaskXmlPath(taskName);
|
|
191
|
+
mkdirSync(resolveTaskDaemonDir(), { recursive: true });
|
|
192
|
+
writeFileSync(wrapperPath, buildTaskWrapperContent({
|
|
193
|
+
programArguments: args.programArguments,
|
|
194
|
+
workingDirectory: args.workingDirectory,
|
|
195
|
+
environment: args.environment
|
|
196
|
+
}), "utf8");
|
|
197
|
+
writeFileSync(xmlPath, buildTaskXml({
|
|
198
|
+
description: args.description || "xopc Gateway Service",
|
|
199
|
+
wrapperPath,
|
|
200
|
+
workingDirectory: args.workingDirectory
|
|
201
|
+
}), "utf16le");
|
|
202
|
+
writeTaskEnvSidecar(taskName, args.environment ?? {});
|
|
203
|
+
writeTaskCommandSidecar(taskName, args);
|
|
204
|
+
return {
|
|
205
|
+
wrapperPath,
|
|
206
|
+
xmlPath
|
|
207
|
+
};
|
|
92
208
|
}
|
|
209
|
+
function removeTaskSupportFiles(taskName) {
|
|
210
|
+
for (const filePath of [
|
|
211
|
+
resolveTaskEnvSidecarPath(taskName),
|
|
212
|
+
resolveTaskCommandSidecarPath(taskName),
|
|
213
|
+
resolveTaskWrapperPath(taskName),
|
|
214
|
+
resolveTaskXmlPath(taskName)
|
|
215
|
+
]) try {
|
|
216
|
+
rmSync(filePath, { force: true });
|
|
217
|
+
} catch {}
|
|
218
|
+
}
|
|
219
|
+
const schtasksTestInternals = {
|
|
220
|
+
buildTaskWrapperContent,
|
|
221
|
+
buildTaskXml,
|
|
222
|
+
escapeCmdSetValue,
|
|
223
|
+
quoteCmdArg,
|
|
224
|
+
resolveTaskCommandSidecarPath,
|
|
225
|
+
resolveTaskWrapperPath,
|
|
226
|
+
resolveTaskXmlPath
|
|
227
|
+
};
|
|
93
228
|
function isSchtasksAvailable() {
|
|
94
229
|
if (process.platform !== "win32") return false;
|
|
95
230
|
try {
|
|
@@ -99,7 +234,6 @@ function isSchtasksAvailable() {
|
|
|
99
234
|
"ignore",
|
|
100
235
|
"ignore"
|
|
101
236
|
],
|
|
102
|
-
shell: true,
|
|
103
237
|
timeout: 3e3
|
|
104
238
|
}).status === 0;
|
|
105
239
|
} catch {
|
|
@@ -112,8 +246,6 @@ const schtasksService = {
|
|
|
112
246
|
notLoadedText: "Scheduled Task (not installed)",
|
|
113
247
|
async install(args) {
|
|
114
248
|
const taskName = resolveTaskName(args.env);
|
|
115
|
-
const program = args.programArguments[0];
|
|
116
|
-
const programArgs = args.programArguments.slice(1).join(" ");
|
|
117
249
|
try {
|
|
118
250
|
await schtasks([
|
|
119
251
|
"/delete",
|
|
@@ -122,23 +254,22 @@ const schtasksService = {
|
|
|
122
254
|
"/f"
|
|
123
255
|
]);
|
|
124
256
|
} catch {}
|
|
257
|
+
const { wrapperPath, xmlPath } = writeTaskSupportFiles(taskName, args);
|
|
125
258
|
await schtasksExec([
|
|
126
259
|
"/create",
|
|
127
260
|
"/tn",
|
|
128
261
|
taskName,
|
|
129
|
-
"/
|
|
130
|
-
|
|
131
|
-
"/sc",
|
|
132
|
-
"ONLOGON",
|
|
133
|
-
"/rl",
|
|
134
|
-
"LIMITED",
|
|
262
|
+
"/xml",
|
|
263
|
+
xmlPath,
|
|
135
264
|
"/f"
|
|
136
265
|
]);
|
|
137
|
-
if (args.environment && Object.keys(args.environment).length > 0) writeTaskEnvSidecar(taskName, args.environment);
|
|
138
266
|
args.stdout?.write(`Created scheduled task: ${taskName}\n`);
|
|
139
|
-
args.stdout?.write(`
|
|
140
|
-
args.stdout?.write(`
|
|
141
|
-
log.info({
|
|
267
|
+
args.stdout?.write(` Wrapper: ${wrapperPath}\n`);
|
|
268
|
+
args.stdout?.write(` Command: ${args.programArguments.join(" ")}\n`);
|
|
269
|
+
log.info({
|
|
270
|
+
taskName,
|
|
271
|
+
wrapperPath
|
|
272
|
+
}, "Scheduled task installed");
|
|
142
273
|
},
|
|
143
274
|
async uninstall(args) {
|
|
144
275
|
const taskName = resolveTaskName(args.env);
|
|
@@ -160,7 +291,7 @@ const schtasksService = {
|
|
|
160
291
|
} catch (err) {
|
|
161
292
|
log.debug({ err }, "Uninstall task not found");
|
|
162
293
|
}
|
|
163
|
-
|
|
294
|
+
removeTaskSupportFiles(taskName);
|
|
164
295
|
log.info({ taskName }, "Scheduled task uninstalled");
|
|
165
296
|
},
|
|
166
297
|
async stop(args) {
|
|
@@ -243,31 +374,20 @@ const schtasksService = {
|
|
|
243
374
|
async readCommand(env) {
|
|
244
375
|
const taskName = resolveTaskName(env);
|
|
245
376
|
try {
|
|
246
|
-
|
|
377
|
+
if ((await schtasks([
|
|
247
378
|
"/query",
|
|
248
379
|
"/tn",
|
|
249
380
|
taskName,
|
|
250
381
|
"/fo",
|
|
251
382
|
"list",
|
|
252
383
|
"/v"
|
|
253
|
-
]);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const taskRunMatch = output.match(/Task To Run:\s*(.+)/i);
|
|
257
|
-
const workDirMatch = output.match(/Start In:\s*(.+)/i);
|
|
258
|
-
if (!taskRunMatch) return null;
|
|
259
|
-
const taskRun = taskRunMatch[1].trim();
|
|
260
|
-
let programArguments;
|
|
261
|
-
if (taskRun.startsWith("\"")) {
|
|
262
|
-
const closeQuote = taskRun.indexOf("\"", 1);
|
|
263
|
-
if (closeQuote > 0) programArguments = [taskRun.slice(1, closeQuote), ...taskRun.slice(closeQuote + 1).trim().split(/\s+/).filter(Boolean)];
|
|
264
|
-
else programArguments = taskRun.split(/\s+/);
|
|
265
|
-
} else programArguments = taskRun.split(/\s+/);
|
|
266
|
-
const environment = readTaskEnvSidecar(taskName);
|
|
384
|
+
])).exitCode !== 0) return null;
|
|
385
|
+
const commandSidecar = readTaskCommandSidecar(taskName);
|
|
386
|
+
if (!commandSidecar) return null;
|
|
267
387
|
return {
|
|
268
|
-
programArguments,
|
|
269
|
-
workingDirectory:
|
|
270
|
-
environment
|
|
388
|
+
programArguments: commandSidecar.programArguments,
|
|
389
|
+
workingDirectory: commandSidecar.workingDirectory,
|
|
390
|
+
environment: readTaskEnvSidecar(taskName)
|
|
271
391
|
};
|
|
272
392
|
} catch {
|
|
273
393
|
return null;
|
|
@@ -275,6 +395,6 @@ const schtasksService = {
|
|
|
275
395
|
}
|
|
276
396
|
};
|
|
277
397
|
//#endregion
|
|
278
|
-
export { isSchtasksAvailable, schtasksService };
|
|
398
|
+
export { isSchtasksAvailable, schtasksService, schtasksTestInternals };
|
|
279
399
|
|
|
280
400
|
//# sourceMappingURL=schtasks.js.map
|