@xopcai/xopc 0.0.90 → 0.0.92
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/README.md +36 -12
- package/README.zh-CN.md +36 -12
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/feishu/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/api/api.js +2 -2
- package/dist/extensions/weixin/src/auth/accounts.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js +1 -1
- package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +41 -0
- package/dist/gateway/static/root/assets/agents-uwPn7ZW9.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-CWKdhSPU.js +1 -0
- package/dist/gateway/static/root/assets/{attachment-preview-renderer-CpyoFbs4.js → attachment-preview-renderer-DBAxQXb-.js} +2 -2
- package/dist/gateway/static/root/assets/{attachment-process-heavy-CqVriadb.js → attachment-process-heavy-Csq3TrrP.js} +4 -4
- package/dist/gateway/static/root/assets/channels-settings-hEhW7Mbk.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-BrtH2VzC.js → channels-status-swr-XzddfJW2.js} +1 -1
- package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +1 -0
- package/dist/gateway/static/root/assets/{cron-api-CyqbgfHM.js → cron-api--I8LJ44S.js} +1 -1
- package/dist/gateway/static/root/assets/cron-page-B0kvgZGR.js +1 -0
- package/dist/gateway/static/root/assets/dist-CYgHMQO0.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-D6Ak0STa.js → extension-debug-page-6cRP0nA9.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Q0P3d6DW.js → extension-page-DpwIkspI.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-CL55LwU_.js → extension-settings-page-DYbnQUxH.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-Dqa9iTWl.js → fetch-DTN0w7rV.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-HUR6JElP.js → field-primitives-CslW6HwD.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-config-api-2UiKevxG.js +1 -0
- package/dist/gateway/static/root/assets/index-BUKUv7QW.css +1 -0
- package/dist/gateway/static/root/assets/{index-BYcGfwcE.js → index-DnevRVa6.js} +63 -59
- package/dist/gateway/static/root/assets/logs-page-sOP4TXJ4.js +1 -0
- package/dist/gateway/static/root/assets/note-detail-page-B91pLkEI.css +1 -0
- package/dist/gateway/static/root/assets/note-detail-page-DvW2qg4i.js +179 -0
- package/dist/gateway/static/root/assets/note-time-BEiibLJv.js +1 -0
- package/dist/gateway/static/root/assets/notes-page-BFQaquHU.js +1 -0
- package/dist/gateway/static/root/assets/{pdf-BnEvgIXZ.js → pdf-epILhEOn.js} +1 -1
- package/dist/gateway/static/root/assets/preload-helper-zJ_50EbN.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-CptjDKAX.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BctKqHcf.js +2 -0
- package/dist/gateway/static/root/assets/{settings-form-section-a0qGVOlr.js → settings-form-section-QJh5ruel.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-V3p-hISB.js +2 -0
- package/dist/gateway/static/root/assets/share-preview-page-DBsvvbmD.js +2 -0
- package/dist/gateway/static/root/assets/skills-page-q2zPUJAR.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-C0Ehmdo5.js → theme-store-ht5iswWS.js} +1 -1
- package/dist/gateway/static/root/assets/toast-z0toXu32.js +1 -0
- package/dist/gateway/static/root/assets/url-CWWpfkq1.js +3 -0
- package/dist/gateway/static/root/assets/{utils-DRQryzdn.js → utils-DhPv9xoB.js} +1 -1
- package/dist/gateway/static/root/assets/vendor-codemirror-DYoKfS8f.js +45 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-DLSKUipa.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-page.utils-DnG8JBhV.js → workflow-page.utils-CJqnPWkW.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-DRRQ1A0l.js +27 -0
- package/dist/gateway/static/root/index.html +10 -8
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +7 -7
- package/dist/src/agent/agent-scope.js +1 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/goals/goal-run-store.js +4 -4
- package/dist/src/agent/goals/persistent-goal-service.js +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/image/load-image-media.js +2 -2
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +2 -2
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-config.d.ts +2 -9
- package/dist/src/agent/mcp/bundle-mcp-config.js +10 -34
- package/dist/src/agent/mcp/bundle-mcp-config.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-policy.js +2 -2
- package/dist/src/agent/mcp/bundle-mcp-policy.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +5 -5
- package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
- package/dist/src/agent/mcp/index.js +2 -2
- package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
- package/dist/src/agent/memory/dreaming/events.js +1 -1
- package/dist/src/agent/memory/dreaming/last-run.js +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
- package/dist/src/agent/memory/dreaming/preview.js +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
- package/dist/src/agent/memory/dreaming/utils.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
- package/dist/src/agent/reply/post-compaction-context.js +1 -1
- package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
- package/dist/src/agent/sandbox/path-policy.js +2 -2
- package/dist/src/agent/service/build-direct-message-content.js +1 -1
- package/dist/src/agent/service.js +4 -4
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/config.js +1 -1
- package/dist/src/agent/skills/hub-hash.js +2 -2
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +2 -2
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/dreaming-tool.js +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/workflow-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/agent/workflow/catalog.js +1 -1
- package/dist/src/auth/credentials.js +3 -3
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/browser/cache-dir-policy.js +1 -1
- package/dist/src/browser/cdp-local-launcher.js +2 -2
- package/dist/src/browser/providers/browser-ext-install.js +3 -3
- package/dist/src/browser/providers/cloakbrowser.js +4 -4
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/outbound/persist-store.js +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +1 -1
- package/dist/src/channels/pairing/pairing-store.js +2 -2
- package/dist/src/chat-commands/agent-edit.js +2 -2
- package/dist/src/chat-commands/builtins/config.js +2 -2
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/command-catalog.js +0 -4
- package/dist/src/cli/command-catalog.js.map +1 -1
- package/dist/src/cli/command-loaders.js +1 -2
- package/dist/src/cli/command-loaders.js.map +1 -1
- package/dist/src/cli/command-manifest.js +0 -4
- package/dist/src/cli/command-manifest.js.map +1 -1
- package/dist/src/cli/commands/config.js +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +2 -2
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/logs.js +1 -1
- package/dist/src/cli/commands/image.js +1 -1
- package/dist/src/cli/commands/init.js +4 -4
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +2 -2
- package/dist/src/commands/agents.config.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.d.ts +0 -1
- package/dist/src/config/index.js +6 -7
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +2 -2
- package/dist/src/config/paths-state.js +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/schema.d.ts +36 -6
- package/dist/src/config/schema.js +13 -11
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/workspace-path.js +1 -1
- package/dist/src/connectors/builtin-catalog.d.ts +2 -0
- package/dist/src/connectors/builtin-catalog.js +152 -0
- package/dist/src/connectors/builtin-catalog.js.map +1 -0
- package/dist/src/connectors/catalog.d.ts +5 -0
- package/dist/src/connectors/catalog.js +13 -0
- package/dist/src/connectors/catalog.js.map +1 -0
- package/dist/src/connectors/health.d.ts +3 -0
- package/dist/src/connectors/health.js +61 -0
- package/dist/src/connectors/health.js.map +1 -0
- package/dist/src/connectors/install.d.ts +5 -0
- package/dist/src/connectors/install.js +46 -0
- package/dist/src/connectors/install.js.map +1 -0
- package/dist/src/connectors/instances.d.ts +4 -0
- package/dist/src/connectors/instances.js +43 -0
- package/dist/src/connectors/instances.js.map +1 -0
- package/dist/src/connectors/materialize.d.ts +9 -0
- package/dist/src/connectors/materialize.js +76 -0
- package/dist/src/connectors/materialize.js.map +1 -0
- package/dist/src/connectors/oauth.d.ts +22 -0
- package/dist/src/connectors/oauth.js +99 -0
- package/dist/src/connectors/oauth.js.map +1 -0
- package/dist/src/connectors/providers.d.ts +9 -0
- package/dist/src/connectors/providers.js +20 -0
- package/dist/src/connectors/providers.js.map +1 -0
- package/dist/src/connectors/secret-store.d.ts +7 -0
- package/dist/src/connectors/secret-store.js +47 -0
- package/dist/src/connectors/secret-store.js.map +1 -0
- package/dist/src/connectors/types.d.ts +102 -0
- package/dist/src/connectors/types.js +1 -0
- package/dist/src/connectors/usage.d.ts +6 -0
- package/dist/src/connectors/usage.js +63 -0
- package/dist/src/connectors/usage.js.map +1 -0
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/daemon/constants.js +1 -1
- package/dist/src/daemon/install-plan.js +2 -2
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/schtasks.js +2 -2
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/extensions/bundle-mcp.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.js +1 -1
- package/dist/src/extensions/lockfile.js +2 -2
- package/dist/src/extensions/update.js +1 -1
- package/dist/src/gateway/agents-admin.js +3 -3
- package/dist/src/gateway/file-path-classifier.js +2 -2
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -0
- package/dist/src/gateway/hono/lib/config-payload.js +2 -1
- package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
- package/dist/src/gateway/hono/lib/extension-store.js +2 -2
- package/dist/src/gateway/hono/lib/static-ui.js +2 -2
- package/dist/src/gateway/hono/middleware/auth.js +1 -2
- package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +2 -2
- package/dist/src/gateway/hono/routes/config-patch/gateway.js +12 -0
- package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
- package/dist/src/gateway/hono/routes/connectors.d.ts +3 -0
- package/dist/src/gateway/hono/routes/connectors.js +177 -0
- package/dist/src/gateway/hono/routes/connectors.js.map +1 -0
- package/dist/src/gateway/hono/routes/dreaming.js +1 -1
- package/dist/src/gateway/hono/routes/home.d.ts +12 -0
- package/dist/src/gateway/hono/routes/home.js +50 -0
- package/dist/src/gateway/hono/routes/home.js.map +1 -0
- package/dist/src/gateway/hono/routes/host-fs.js +2 -2
- package/dist/src/gateway/hono/routes/lazy-bundles.js +20 -4
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/notes.d.ts +3 -0
- package/dist/src/gateway/hono/routes/notes.js +305 -0
- package/dist/src/gateway/hono/routes/notes.js.map +1 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/shares.js +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +4 -4
- package/dist/src/gateway/lock.js +3 -3
- package/dist/src/gateway/ports.js +1 -1
- package/dist/src/gateway/service/agent-runner.js +2 -2
- package/dist/src/gateway/service/marketplace-service.js +2 -2
- package/dist/src/gateway/service.d.ts +3 -0
- package/dist/src/gateway/service.js +11 -1
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.js +1 -1
- package/dist/src/gateway/workspace-ripgrep.d.ts +6 -0
- package/dist/src/gateway/workspace-ripgrep.js +62 -11
- package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/brew.js +1 -1
- package/dist/src/infra/package-json.js +1 -1
- package/dist/src/infra/package-update-steps.js +1 -1
- package/dist/src/infra/path-env.js +2 -2
- package/dist/src/infra/restart.js +2 -2
- package/dist/src/infra/stable-node-path.js +1 -1
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/infra/update-global.js +1 -1
- package/dist/src/infra/update-lock.js +3 -3
- package/dist/src/infra/update-runner.js +1 -1
- package/dist/src/infra/update-startup.js +2 -2
- package/dist/src/infra/write-file-atomic.js +2 -2
- package/dist/src/mcp/channel-bridge.js +1 -1
- package/dist/src/mcp/channel-bridge.js.map +1 -1
- package/dist/src/notes/attachment-ref.d.ts +9 -0
- package/dist/src/notes/attachment-ref.js +27 -0
- package/dist/src/notes/attachment-ref.js.map +1 -0
- package/dist/src/notes/index.d.ts +4 -0
- package/dist/src/notes/index.js +4 -0
- package/dist/src/notes/note-attachment-sync.d.ts +7 -0
- package/dist/src/notes/note-attachment-sync.js +46 -0
- package/dist/src/notes/note-attachment-sync.js.map +1 -0
- package/dist/src/notes/note-index-meta.d.ts +14 -0
- package/dist/src/notes/note-index-meta.js +87 -0
- package/dist/src/notes/note-index-meta.js.map +1 -0
- package/dist/src/notes/paths.d.ts +5 -0
- package/dist/src/notes/paths.js +23 -0
- package/dist/src/notes/paths.js.map +1 -0
- package/dist/src/notes/service.d.ts +53 -0
- package/dist/src/notes/service.js +373 -0
- package/dist/src/notes/service.js.map +1 -0
- package/dist/src/notes/store.d.ts +34 -0
- package/dist/src/notes/store.js +342 -0
- package/dist/src/notes/store.js.map +1 -0
- package/dist/src/notes/types.d.ts +199 -0
- package/dist/src/notes/types.js +1 -0
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/session/config-store.js +2 -2
- package/dist/src/session/init-session-turn.js +2 -2
- package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
- package/dist/src/session/parity/sessions-json-file.js +1 -1
- package/dist/src/session/parity/transcript-file-lock.js +2 -2
- package/dist/src/session/parity/transcript-paths.js +1 -1
- package/dist/src/session/resolve-session.js +4 -4
- package/dist/src/session/search-index-cache.js +1 -1
- package/dist/src/session/search-index.js +1 -1
- package/dist/src/session/session-title.js +2 -2
- package/dist/src/session/store.d.ts +2 -0
- package/dist/src/session/store.js +27 -7
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/share/share-auto.js +2 -2
- package/dist/src/share/share-store.js +3 -3
- package/dist/src/share/share-thumbnail.js +2 -2
- package/dist/src/share/share-zip.js +1 -1
- package/dist/src/share/site-share-store.js +3 -3
- package/dist/src/share/site-static-serve.js +1 -1
- package/dist/src/tui/clipboard-image.js +3 -3
- package/dist/src/tui/theme-manager.js +1 -1
- package/dist/src/tui/tui-keybindings-file.js +1 -1
- package/dist/src/tui/tui-scoped-models.js +2 -2
- package/dist/src/tui/tui-settings.js +1 -1
- package/dist/src/tui/tui.js +3 -3
- package/dist/src/tunnel/frpc-binary.js +3 -3
- package/dist/src/tunnel/frpc-config.js +1 -1
- package/dist/src/tunnel/frpc-extract.js +1 -1
- package/dist/src/tunnel/tunnel-state.js +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/dist/src/workflows/store/event-store.js +1 -1
- package/dist/src/workflows/store/run-store.js +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-cPvvYLXo.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-Bk1_P5FJ.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-CZoeQwHz.js +0 -1
- package/dist/gateway/static/root/assets/cron-dreaming-jobs-Ip703-qM.js +0 -2
- package/dist/gateway/static/root/assets/cron-page-BpLdiQN8.js +0 -1
- package/dist/gateway/static/root/assets/dist-BTWC-BTN.js +0 -45
- package/dist/gateway/static/root/assets/dist-BpAiK86n.js +0 -1
- package/dist/gateway/static/root/assets/eye-DAfL1U7M.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-config-api-DusckjUX.js +0 -1
- package/dist/gateway/static/root/assets/index-V7MQ7834.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-_HcZ2fgK.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-iezSMjho.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-C9_nYQwM.js +0 -3
- package/dist/gateway/static/root/assets/share-preview-page-DExl7CJy.js +0 -2
- package/dist/gateway/static/root/assets/skills-page-BlgGD93t.js +0 -2
- package/dist/gateway/static/root/assets/url-fxyYANfA.js +0 -3
- package/dist/gateway/static/root/assets/vendor-codemirror-D0yxdRpg.js +0 -58
- package/dist/gateway/static/root/assets/voice-api-key-field-D0viACE2.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-BvMobnJP.js +0 -27
- package/dist/src/cli/commands/mcp.d.ts +0 -4
- package/dist/src/cli/commands/mcp.js +0 -85
- package/dist/src/cli/commands/mcp.js.map +0 -1
- package/dist/src/config/mcp-config.d.ts +0 -34
- package/dist/src/config/mcp-config.js +0 -116
- package/dist/src/config/mcp-config.js.map +0 -1
- package/dist/src/gateway/hono/routes/mcp.d.ts +0 -3
- package/dist/src/gateway/hono/routes/mcp.js +0 -99
- package/dist/src/gateway/hono/routes/mcp.js.map +0 -1
|
@@ -8,28 +8,66 @@ const log = createLogger("WorkspaceRipgrep");
|
|
|
8
8
|
function isEnoent(err) {
|
|
9
9
|
return err !== null && typeof err === "object" && err.code === "ENOENT";
|
|
10
10
|
}
|
|
11
|
+
/** Ripgrep binaries inside `app.asar` are not executable (spawn throws ENOTDIR). */
|
|
12
|
+
function isAsarBundledPath(filePath) {
|
|
13
|
+
return filePath.includes(".asar");
|
|
14
|
+
}
|
|
15
|
+
/** True when `filePath` is a real on-disk executable candidate (not inside asar). */
|
|
16
|
+
function isRunnableRipgrepPath(filePath) {
|
|
17
|
+
const trimmed = filePath.trim();
|
|
18
|
+
if (!trimmed || isAsarBundledPath(trimmed)) return false;
|
|
19
|
+
return existsSync(trimmed);
|
|
20
|
+
}
|
|
11
21
|
let cachedRipgrepBin;
|
|
22
|
+
/** @internal Test-only — clears memoized ripgrep path between cases. */
|
|
23
|
+
function resetRipgrepBinaryCacheForTests() {
|
|
24
|
+
cachedRipgrepBin = void 0;
|
|
25
|
+
}
|
|
12
26
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
27
|
+
* Resolve ripgrep binary:
|
|
28
|
+
* 1. `XOPC_RIPGREP_BIN` (Electron extraResources `bin/rg`)
|
|
29
|
+
* 2. `@vscode/ripgrep` postinstall path (dev / CLI)
|
|
30
|
+
* 3. `rg` on PATH
|
|
15
31
|
*/
|
|
16
32
|
async function resolveRipgrepBinary() {
|
|
17
33
|
if (cachedRipgrepBin) return cachedRipgrepBin;
|
|
34
|
+
const envBin = process.env.XOPC_RIPGREP_BIN?.trim();
|
|
35
|
+
if (envBin && isRunnableRipgrepPath(envBin)) {
|
|
36
|
+
cachedRipgrepBin = envBin;
|
|
37
|
+
return envBin;
|
|
38
|
+
}
|
|
18
39
|
let bin = "rg";
|
|
19
40
|
try {
|
|
20
41
|
const { rgPath } = await import("@vscode/ripgrep");
|
|
21
|
-
if (typeof rgPath === "string" && rgPath.length > 0
|
|
22
|
-
else if (
|
|
42
|
+
if (typeof rgPath === "string" && rgPath.length > 0) if (isRunnableRipgrepPath(rgPath)) bin = rgPath;
|
|
43
|
+
else if (isAsarBundledPath(rgPath)) log.debug({ rgPath }, "@vscode/ripgrep path is inside app.asar; will try rg on PATH or XOPC_RIPGREP_BIN");
|
|
44
|
+
else log.debug({ rgPath }, "@vscode/ripgrep binary not on disk; will try rg on PATH");
|
|
23
45
|
} catch {}
|
|
24
46
|
cachedRipgrepBin = bin;
|
|
25
47
|
return bin;
|
|
26
48
|
}
|
|
49
|
+
function spawnRipgrep(executable, args, options, context) {
|
|
50
|
+
try {
|
|
51
|
+
return spawn(executable, args, {
|
|
52
|
+
shell: false,
|
|
53
|
+
...options
|
|
54
|
+
});
|
|
55
|
+
} catch (err) {
|
|
56
|
+
const em = err instanceof Error ? err.message : String(err);
|
|
57
|
+
log.warn({
|
|
58
|
+
err,
|
|
59
|
+
...context,
|
|
60
|
+
rg: executable
|
|
61
|
+
}, `ripgrep ${context.phase}: spawn failed: ${em}`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
27
65
|
/** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */
|
|
28
66
|
function runRipgrepInDirectory(query, dirAbsPath) {
|
|
29
67
|
return (async () => {
|
|
30
68
|
const rgExecutable = await resolveRipgrepBinary();
|
|
31
69
|
return await new Promise((resolve) => {
|
|
32
|
-
const rg =
|
|
70
|
+
const rg = spawnRipgrep(rgExecutable, [
|
|
33
71
|
"--json",
|
|
34
72
|
"--smart-case",
|
|
35
73
|
"--max-count",
|
|
@@ -40,7 +78,15 @@ function runRipgrepInDirectory(query, dirAbsPath) {
|
|
|
40
78
|
"*.txt",
|
|
41
79
|
query,
|
|
42
80
|
dirAbsPath
|
|
43
|
-
], {
|
|
81
|
+
], {}, {
|
|
82
|
+
phase: "in-directory",
|
|
83
|
+
dir: dirAbsPath,
|
|
84
|
+
rg: rgExecutable
|
|
85
|
+
});
|
|
86
|
+
if (!rg) {
|
|
87
|
+
resolve([]);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
44
90
|
const results = [];
|
|
45
91
|
let buffer = "";
|
|
46
92
|
rg.stdout.on("data", (data) => {
|
|
@@ -93,17 +139,22 @@ function runRipgrepListFiles(dirAbsPath) {
|
|
|
93
139
|
return (async () => {
|
|
94
140
|
const rgExecutable = await resolveRipgrepBinary();
|
|
95
141
|
return await new Promise((resolve) => {
|
|
96
|
-
const rg =
|
|
142
|
+
const rg = spawnRipgrep(rgExecutable, [
|
|
97
143
|
"--files",
|
|
98
144
|
"--glob",
|
|
99
145
|
"!**/node_modules/**",
|
|
100
146
|
"--glob",
|
|
101
147
|
"!.git/**",
|
|
102
148
|
"."
|
|
103
|
-
], {
|
|
104
|
-
|
|
105
|
-
|
|
149
|
+
], { cwd: dirAbsPath }, {
|
|
150
|
+
phase: "--files",
|
|
151
|
+
dir: dirAbsPath,
|
|
152
|
+
rg: rgExecutable
|
|
106
153
|
});
|
|
154
|
+
if (!rg) {
|
|
155
|
+
resolve([]);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
107
158
|
const lines = [];
|
|
108
159
|
let buffer = "";
|
|
109
160
|
rg.stdout.on("data", (data) => {
|
|
@@ -141,6 +192,6 @@ function runRipgrepListFiles(dirAbsPath) {
|
|
|
141
192
|
})();
|
|
142
193
|
}
|
|
143
194
|
//#endregion
|
|
144
|
-
export { runRipgrepInDirectory, runRipgrepListFiles };
|
|
195
|
+
export { isAsarBundledPath, isRunnableRipgrepPath, resetRipgrepBinaryCacheForTests, runRipgrepInDirectory, runRipgrepListFiles };
|
|
145
196
|
|
|
146
197
|
//# sourceMappingURL=workspace-ripgrep.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-ripgrep.js","names":[],"sources":["../../../src/gateway/workspace-ripgrep.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\n\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('WorkspaceRipgrep');\n\nfunction isEnoent(err: unknown): boolean {\n return err !== null && typeof err === 'object' && (err as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nlet cachedRipgrepBin: string | undefined;\n\n/**\n * Prefer `@vscode/ripgrep` when its postinstall placed `bin/rg`; otherwise use `rg` on PATH.\n * (Bundled path can be ENOENT if postinstall was skipped or the binary was never downloaded.)\n */\nasync function resolveRipgrepBinary(): Promise<string> {\n if (cachedRipgrepBin) return cachedRipgrepBin;\n let bin = 'rg';\n try {\n const { rgPath } = await import('@vscode/ripgrep');\n if (typeof rgPath === 'string' && rgPath.length > 0 && existsSync(rgPath)) {\n bin = rgPath;\n } else if (typeof rgPath === 'string' && rgPath.length > 0) {\n log.debug({ rgPath }, '@vscode/ripgrep binary not on disk; will try rg on PATH');\n }\n } catch {\n // pnpm may skip @vscode/ripgrep postinstall; package dir can be missing.\n }\n cachedRipgrepBin = bin;\n return bin;\n}\n\nexport interface WorkspaceSearchHit {\n filePath: string;\n lineNumber: number;\n lineContent: string;\n matchStart: number;\n matchEnd: number;\n}\n\n/** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */\nexport function runRipgrepInDirectory(query: string, dirAbsPath: string): Promise<WorkspaceSearchHit[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<WorkspaceSearchHit[]>((resolve) => {\n const args = [\n '--json',\n '--smart-case',\n '--max-count',\n '50',\n '--glob',\n '*.md',\n '--glob',\n '*.txt',\n query,\n dirAbsPath,\n ];\n\n const rg = spawn(rgExecutable, args, { shell: false });\n const results: WorkspaceSearchHit[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const parsed = JSON.parse(line) as {\n type?: string;\n data?: {\n path?: { text?: string };\n lines?: { text?: string };\n line_number?: number;\n submatches?: Array<{ start?: number; end?: number }>;\n };\n };\n if (parsed.type === 'match' && parsed.data) {\n const d = parsed.data;\n const pathText = d.path?.text ?? '';\n const lineContent = d.lines?.text ?? '';\n const lineNumber = d.line_number ?? 0;\n const sm = d.submatches?.[0];\n results.push({\n filePath: pathText,\n lineNumber,\n lineContent: lineContent.trimEnd(),\n matchStart: sm?.start ?? 0,\n matchEnd: sm?.end ?? 0,\n });\n }\n } catch {\n /* skip */\n }\n }\n });\n\n rg.on('close', () => resolve(results));\n rg.on('error', (err) => {\n if (!isEnoent(err)) {\n log.warn({ err, query, dir: dirAbsPath, rg: rgExecutable }, 'ripgrep in-directory: spawn failed');\n } else {\n log.debug({ dir: dirAbsPath, rg: rgExecutable }, 'ripgrep not on PATH; skipping in-directory search');\n }\n resolve([]);\n });\n });\n })();\n}\n\n/**\n * List workspace-relative file paths via ripgrep `--files` (respects .gitignore; fast on large trees).\n * Returns POSIX paths relative to `dirAbsPath`.\n */\nexport function runRipgrepListFiles(dirAbsPath: string): Promise<string[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<string[]>((resolve) => {\n const args = ['--files', '--glob', '!**/node_modules/**', '--glob', '!.git/**', '.'];\n const rg = spawn(rgExecutable, args, { shell: false, cwd: dirAbsPath });\n const lines: string[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const parts = buffer.split(/\\r?\\n/);\n buffer = parts.pop() ?? '';\n for (const line of parts) {\n const t = line.trim();\n if (t) lines.push(t.replace(/\\\\/g, '/'));\n }\n });\n\n rg.on('close', (code) => {\n const tail = buffer.trim();\n if (tail) lines.push(tail.replace(/\\\\/g, '/'));\n if (code !== 0 && lines.length === 0) {\n log.debug({ code, cwd: dirAbsPath, rg: rgExecutable }, 'ripgrep --files: non-zero exit, no output (using fs fallback if any)');\n }\n resolve(lines);\n });\n rg.on('error', (err) => {\n if (isEnoent(err)) {\n log.debug(\n { cwd: dirAbsPath, rg: rgExecutable },\n 'ripgrep binary not found; workspace file search will use fs fallback when needed',\n );\n } else {\n log.warn({ err, cwd: dirAbsPath, rg: rgExecutable }, 'ripgrep --files failed to start');\n }\n resolve([]);\n });\n });\n })();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"workspace-ripgrep.js","names":[],"sources":["../../../src/gateway/workspace-ripgrep.ts"],"sourcesContent":["import { type ChildProcess, spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\n\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('WorkspaceRipgrep');\n\nfunction isEnoent(err: unknown): boolean {\n return err !== null && typeof err === 'object' && (err as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\n/** Ripgrep binaries inside `app.asar` are not executable (spawn throws ENOTDIR). */\nexport function isAsarBundledPath(filePath: string): boolean {\n return filePath.includes('.asar');\n}\n\n/** True when `filePath` is a real on-disk executable candidate (not inside asar). */\nexport function isRunnableRipgrepPath(filePath: string): boolean {\n const trimmed = filePath.trim();\n if (!trimmed || isAsarBundledPath(trimmed)) return false;\n return existsSync(trimmed);\n}\n\nlet cachedRipgrepBin: string | undefined;\n\n/** @internal Test-only — clears memoized ripgrep path between cases. */\nexport function resetRipgrepBinaryCacheForTests(): void {\n cachedRipgrepBin = undefined;\n}\n\n/**\n * Resolve ripgrep binary:\n * 1. `XOPC_RIPGREP_BIN` (Electron extraResources `bin/rg`)\n * 2. `@vscode/ripgrep` postinstall path (dev / CLI)\n * 3. `rg` on PATH\n */\nasync function resolveRipgrepBinary(): Promise<string> {\n if (cachedRipgrepBin) return cachedRipgrepBin;\n\n const envBin = process.env.XOPC_RIPGREP_BIN?.trim();\n if (envBin && isRunnableRipgrepPath(envBin)) {\n cachedRipgrepBin = envBin;\n return envBin;\n }\n\n let bin = 'rg';\n try {\n const { rgPath } = await import('@vscode/ripgrep');\n if (typeof rgPath === 'string' && rgPath.length > 0) {\n if (isRunnableRipgrepPath(rgPath)) {\n bin = rgPath;\n } else if (isAsarBundledPath(rgPath)) {\n log.debug({ rgPath }, '@vscode/ripgrep path is inside app.asar; will try rg on PATH or XOPC_RIPGREP_BIN');\n } else {\n log.debug({ rgPath }, '@vscode/ripgrep binary not on disk; will try rg on PATH');\n }\n }\n } catch {\n // pnpm may skip @vscode/ripgrep postinstall; package dir can be missing.\n }\n cachedRipgrepBin = bin;\n return bin;\n}\n\nfunction spawnRipgrep(\n executable: string,\n args: string[],\n options: { cwd?: string },\n context: { phase: string; dir?: string; rg: string },\n): ChildProcess | null {\n try {\n return spawn(executable, args, { shell: false, ...options });\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, ...context, rg: executable }, `ripgrep ${context.phase}: spawn failed: ${em}`);\n return null;\n }\n}\n\nexport interface WorkspaceSearchHit {\n filePath: string;\n lineNumber: number;\n lineContent: string;\n matchStart: number;\n matchEnd: number;\n}\n\n/** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */\nexport function runRipgrepInDirectory(query: string, dirAbsPath: string): Promise<WorkspaceSearchHit[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<WorkspaceSearchHit[]>((resolve) => {\n const args = [\n '--json',\n '--smart-case',\n '--max-count',\n '50',\n '--glob',\n '*.md',\n '--glob',\n '*.txt',\n query,\n dirAbsPath,\n ];\n\n const rg = spawnRipgrep(rgExecutable, args, {}, {\n phase: 'in-directory',\n dir: dirAbsPath,\n rg: rgExecutable,\n });\n if (!rg) {\n resolve([]);\n return;\n }\n const results: WorkspaceSearchHit[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const parsed = JSON.parse(line) as {\n type?: string;\n data?: {\n path?: { text?: string };\n lines?: { text?: string };\n line_number?: number;\n submatches?: Array<{ start?: number; end?: number }>;\n };\n };\n if (parsed.type === 'match' && parsed.data) {\n const d = parsed.data;\n const pathText = d.path?.text ?? '';\n const lineContent = d.lines?.text ?? '';\n const lineNumber = d.line_number ?? 0;\n const sm = d.submatches?.[0];\n results.push({\n filePath: pathText,\n lineNumber,\n lineContent: lineContent.trimEnd(),\n matchStart: sm?.start ?? 0,\n matchEnd: sm?.end ?? 0,\n });\n }\n } catch {\n /* skip */\n }\n }\n });\n\n rg.on('close', () => resolve(results));\n rg.on('error', (err) => {\n if (!isEnoent(err)) {\n log.warn({ err, query, dir: dirAbsPath, rg: rgExecutable }, 'ripgrep in-directory: spawn failed');\n } else {\n log.debug({ dir: dirAbsPath, rg: rgExecutable }, 'ripgrep not on PATH; skipping in-directory search');\n }\n resolve([]);\n });\n });\n })();\n}\n\n/**\n * List workspace-relative file paths via ripgrep `--files` (respects .gitignore; fast on large trees).\n * Returns POSIX paths relative to `dirAbsPath`.\n */\nexport function runRipgrepListFiles(dirAbsPath: string): Promise<string[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<string[]>((resolve) => {\n const args = ['--files', '--glob', '!**/node_modules/**', '--glob', '!.git/**', '.'];\n const rg = spawnRipgrep(rgExecutable, args, { cwd: dirAbsPath }, {\n phase: '--files',\n dir: dirAbsPath,\n rg: rgExecutable,\n });\n if (!rg) {\n resolve([]);\n return;\n }\n const lines: string[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const parts = buffer.split(/\\r?\\n/);\n buffer = parts.pop() ?? '';\n for (const line of parts) {\n const t = line.trim();\n if (t) lines.push(t.replace(/\\\\/g, '/'));\n }\n });\n\n rg.on('close', (code) => {\n const tail = buffer.trim();\n if (tail) lines.push(tail.replace(/\\\\/g, '/'));\n if (code !== 0 && lines.length === 0) {\n log.debug({ code, cwd: dirAbsPath, rg: rgExecutable }, 'ripgrep --files: non-zero exit, no output (using fs fallback if any)');\n }\n resolve(lines);\n });\n rg.on('error', (err) => {\n if (isEnoent(err)) {\n log.debug(\n { cwd: dirAbsPath, rg: rgExecutable },\n 'ripgrep binary not found; workspace file search will use fs fallback when needed',\n );\n } else {\n log.warn({ err, cwd: dirAbsPath, rg: rgExecutable }, 'ripgrep --files failed to start');\n }\n resolve([]);\n });\n });\n })();\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_ACK_MAX_CHARS, HEARTBEAT_OK, NO_REPLY, shouldSilence, stripHeartbeatToken } from "./tokens.js";
|
|
2
|
+
import { appendCronEventLines } from "./event-prompt.js";
|
|
2
3
|
import { isWithinActiveHours } from "./active-hours.js";
|
|
3
4
|
import { isHeartbeatContentEmpty } from "./content-check.js";
|
|
4
|
-
import { appendCronEventLines } from "./event-prompt.js";
|
|
5
5
|
import { createHeartbeatWake } from "./wake.js";
|
|
6
6
|
export { DEFAULT_ACK_MAX_CHARS, HEARTBEAT_OK, NO_REPLY, appendCronEventLines, createHeartbeatWake, isHeartbeatContentEmpty, isWithinActiveHours, shouldSilence, stripHeartbeatToken };
|
package/dist/src/infra/brew.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readPackageVersion } from "./package-json.js";
|
|
2
2
|
import { XOPC_PACKAGE_NAME, collectInstalledGlobalPackageErrors, globalInstallArgs, globalInstallFallbackArgs, resolveExpectedInstalledVersionFromSpec, resolveGlobalInstallTarget, resolveNpmGlobalPrefixLayoutFromGlobalRoot, resolveNpmGlobalPrefixLayoutFromPrefix } from "./update-global.js";
|
|
3
|
-
import fs from "node:fs/promises";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
5
|
//#region src/infra/package-update-steps.ts
|
|
6
6
|
function formatError(err) {
|
|
7
7
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveBrewPathDirs } from "./brew.js";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
2
|
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import fs from "node:fs";
|
|
5
5
|
//#region src/infra/path-env.ts
|
|
6
6
|
function isTruthyEnvValue(value) {
|
|
7
7
|
if (!value) return false;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { init_paths_state, resolveStateDir } from "../config/paths-state.js";
|
|
1
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
2
3
|
import { init_logger } from "../utils/logger.js";
|
|
3
|
-
import { init_paths_state, resolveStateDir } from "../config/paths-state.js";
|
|
4
|
-
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
4
|
import path from "node:path";
|
|
5
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
//#region src/infra/restart.ts
|
|
7
7
|
/**
|
|
8
8
|
* Gateway restart coordination — OpenClaw-aligned SIGUSR1 authorization and restart intent.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
|
|
2
2
|
import { channelToNpmTag } from "./update-channels.js";
|
|
3
|
-
import { access, readFile } from "node:fs/promises";
|
|
4
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { access, readFile } from "node:fs/promises";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
//#region src/infra/update-check.ts
|
|
7
7
|
init_package_version();
|
|
@@ -2,9 +2,9 @@ import { resolveExecPathBinPrepend } from "./path-env.js";
|
|
|
2
2
|
import { readPackageVersion } from "./package-json.js";
|
|
3
3
|
import { applyPathPrepend } from "./path-prepend.js";
|
|
4
4
|
import { createDefaultCommandRunner } from "./run-command.js";
|
|
5
|
+
import path from "node:path";
|
|
5
6
|
import fs from "node:fs";
|
|
6
7
|
import fs$1 from "node:fs/promises";
|
|
7
|
-
import path from "node:path";
|
|
8
8
|
//#region src/infra/update-global.ts
|
|
9
9
|
const XOPC_PACKAGE_NAME = "@xopcai/xopc";
|
|
10
10
|
const GLOBAL_RENAME_PREFIX = ".";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { init_paths_state, resolveUpdateLockPath } from "../config/paths-state.js";
|
|
2
2
|
import { createLogger } from "../utils/logger/index.js";
|
|
3
3
|
import { init_logger } from "../utils/logger.js";
|
|
4
|
-
import {
|
|
5
|
-
import { mkdir, readFile, unlink } from "node:fs/promises";
|
|
4
|
+
import { init_write_file_atomic, writeTextAtomic } from "./write-file-atomic.js";
|
|
6
5
|
import { dirname } from "node:path";
|
|
6
|
+
import { mkdir, readFile, unlink } from "node:fs/promises";
|
|
7
7
|
//#region src/infra/update-lock.ts
|
|
8
8
|
init_write_file_atomic();
|
|
9
9
|
init_paths_state();
|
|
@@ -8,8 +8,8 @@ import { runGlobalPackageUpdateSteps } from "./package-update-steps.js";
|
|
|
8
8
|
import { resolveStableNodePath } from "./stable-node-path.js";
|
|
9
9
|
import { trimLogTail } from "./update-log.js";
|
|
10
10
|
import { maybeRestartGatewayAfterUpdate } from "./update-restart.js";
|
|
11
|
-
import fs from "node:fs/promises";
|
|
12
11
|
import path from "node:path";
|
|
12
|
+
import fs from "node:fs/promises";
|
|
13
13
|
//#region src/infra/update-runner.ts
|
|
14
14
|
const DEFAULT_TIMEOUT_MS = 2700 * 1e3;
|
|
15
15
|
const MAX_LOG_CHARS = 8e3;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
|
|
2
|
-
import {
|
|
2
|
+
import { init_paths_state, resolveUpdateCheckStatePath } from "../config/paths-state.js";
|
|
3
3
|
import { createLogger } from "../utils/logger/index.js";
|
|
4
4
|
import { init_logger } from "../utils/logger.js";
|
|
5
|
-
import {
|
|
5
|
+
import { init_write_file_atomic, writeTextAtomic } from "./write-file-atomic.js";
|
|
6
6
|
import { acquireUpdateLock } from "./update-lock.js";
|
|
7
7
|
import { normalizeUpdateChannel } from "./update-channels.js";
|
|
8
8
|
import { compareSemver, detectInstallKind, resolveNpmChannelTag, resolvePackageRoot } from "./update-check.js";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
import { chmodSync, closeSync, copyFileSync, fsyncSync, mkdirSync, openSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
4
5
|
import { chmod, copyFile, mkdir, open, rename, rm } from "node:fs/promises";
|
|
5
|
-
import path from "node:path";
|
|
6
6
|
//#region src/infra/write-file-atomic.ts
|
|
7
7
|
/**
|
|
8
8
|
* Durable single-file writes: temp file → fsync → rename to target.
|
|
@@ -149,7 +149,7 @@ var XopcChannelBridge = class {
|
|
|
149
149
|
return Array.from(this.pendingApprovals.values());
|
|
150
150
|
}
|
|
151
151
|
async respondToApproval(params) {
|
|
152
|
-
return this.client.postJson("/api/
|
|
152
|
+
return this.client.postJson("/api/connectors/approvals/respond", params);
|
|
153
153
|
}
|
|
154
154
|
async handleClaudePermissionRequest(_params) {}
|
|
155
155
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-bridge.js","names":[],"sources":["../../../src/mcp/channel-bridge.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { loadConfig } from '../config/loader.js';\nimport type {\n ApprovalDecision,\n ApprovalKind,\n ClaudeChannelMode,\n ConversationDescriptor,\n PendingApproval,\n QueueEvent,\n SessionRow,\n WaitFilter,\n} from './channel-shared.js';\nimport { toConversation } from './channel-shared.js';\nimport {\n createGatewayHttpClientFromConfig,\n resolveGatewayHttpBaseUrl,\n type GatewayHttpClient,\n} from './gateway-http-client.js';\nimport { loadUndiciRuntimeDeps } from '../infra/undici-fetch.js';\n\nconst QUEUE_LIMIT = 1000;\n\nexport class XopcChannelBridge {\n private client: GatewayHttpClient | null = null;\n private readonly queue: QueueEvent[] = [];\n private readonly pendingApprovals = new Map<string, PendingApproval>();\n private cursor = 0;\n private closed = false;\n private eventsAbort: AbortController | null = null;\n\n constructor(\n private readonly cfg: Config,\n private readonly params: {\n gatewayUrl?: string;\n gatewayToken?: string;\n claudeChannelMode: ClaudeChannelMode;\n verbose: boolean;\n },\n ) {}\n\n setServer(_server: unknown): void {\n void _server;\n }\n\n async start(): Promise<void> {\n this.client = createGatewayHttpClientFromConfig({\n config: this.cfg,\n gatewayUrl: this.params.gatewayUrl,\n gatewayToken: this.params.gatewayToken,\n });\n this.connectEvents();\n }\n\n async close(): Promise<void> {\n this.closed = true;\n this.eventsAbort?.abort();\n this.eventsAbort = null;\n this.queue.length = 0;\n this.pendingApprovals.clear();\n }\n\n private connectEvents(): void {\n if (this.closed) {\n return;\n }\n this.eventsAbort?.abort();\n const abort = new AbortController();\n this.eventsAbort = abort;\n const baseUrl = resolveGatewayHttpBaseUrl(this.cfg, this.params.gatewayUrl);\n void this.runEventsLoop(baseUrl, this.params.gatewayToken, abort.signal);\n }\n\n private async runEventsLoop(\n baseUrl: string,\n token: string | undefined,\n signal: AbortSignal,\n ): Promise<void> {\n const url = new URL(`${baseUrl}/api/events`);\n if (token?.trim()) {\n url.searchParams.set('token', token.trim());\n }\n try {\n const res = await loadUndiciRuntimeDeps().fetch(url.toString(), {\n headers: { Accept: 'text/event-stream' },\n signal,\n });\n if (!res.ok || !res.body) {\n return;\n }\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n while (!this.closed && !signal.aborted) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n buffer += decoder.decode(value, { stream: true });\n let splitAt = buffer.indexOf('\\n\\n');\n while (splitAt >= 0) {\n this.handleSseChunk(buffer.slice(0, splitAt));\n buffer = buffer.slice(splitAt + 2);\n splitAt = buffer.indexOf('\\n\\n');\n }\n }\n } catch {\n // Best-effort reconnect while bridge stays open.\n }\n if (!this.closed && !signal.aborted) {\n await new Promise((resolve) => setTimeout(resolve, 2000));\n if (!this.closed) {\n this.connectEvents();\n }\n }\n }\n\n private handleSseChunk(chunk: string): void {\n let eventName = 'message';\n let data = '';\n for (const line of chunk.split('\\n')) {\n if (line.startsWith('event:')) {\n eventName = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n data += line.slice(5).trim();\n }\n }\n if (!data) {\n return;\n }\n try {\n const parsed = JSON.parse(data) as Record<string, unknown>;\n this.enqueueFromBroadcast({ type: eventName, ...parsed });\n } catch {\n this.enqueueFromBroadcast({ type: eventName, raw: data });\n }\n }\n\n private enqueueFromBroadcast(data: Record<string, unknown>): void {\n const type = String(data.type ?? data.event ?? '');\n if (!type) {\n return;\n }\n if (type.includes('message') || type.includes('session')) {\n this.pushEvent({\n cursor: ++this.cursor,\n type: 'message',\n sessionKey: String(data.sessionKey ?? data.key ?? ''),\n raw: data,\n });\n }\n }\n\n private pushEvent(event: QueueEvent): void {\n this.queue.push(event);\n while (this.queue.length > QUEUE_LIMIT) {\n this.queue.shift();\n }\n }\n\n async listConversations(args: {\n limit?: number;\n search?: string;\n channel?: string;\n }): Promise<ConversationDescriptor[]> {\n const client = this.client!;\n const query = new URLSearchParams();\n if (args.limit) query.set('limit', String(args.limit));\n if (args.search) query.set('search', args.search);\n if (args.channel) query.set('channel', args.channel);\n const qs = query.toString();\n const rows = await client.getJson<SessionRow[] | { sessions?: SessionRow[] }>(\n `/api/sessions${qs ? `?${qs}` : ''}`,\n );\n const sessions = Array.isArray(rows) ? rows : (rows.sessions ?? []);\n return sessions\n .map((row) => toConversation(row))\n .filter((c): c is ConversationDescriptor => c !== null);\n }\n\n async getConversation(sessionKey: string): Promise<ConversationDescriptor | null> {\n const client = this.client!;\n try {\n const row = await client.getJson<SessionRow>(`/api/sessions/${encodeURIComponent(sessionKey)}`);\n return toConversation(row);\n } catch {\n return null;\n }\n }\n\n async readMessages(\n sessionKey: string,\n limit: number,\n ): Promise<Array<Record<string, unknown>>> {\n const client = this.client!;\n const payload = await client.getJson<{ messages?: Array<Record<string, unknown>> }>(\n `/api/sessions/${encodeURIComponent(sessionKey)}/messages?limit=${limit}`,\n );\n return payload.messages ?? [];\n }\n\n pollEvents(\n filter: WaitFilter,\n limit: number,\n ): { events: QueueEvent[]; nextCursor: number } {\n const events = this.queue\n .filter((e) => e.cursor > filter.afterCursor)\n .filter((e) => !filter.sessionKey || ('sessionKey' in e && e.sessionKey === filter.sessionKey))\n .slice(0, limit);\n const nextCursor = events.length > 0 ? events[events.length - 1]!.cursor : filter.afterCursor;\n return { events, nextCursor };\n }\n\n async waitForEvent(filter: WaitFilter, timeoutMs: number): Promise<QueueEvent | null> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline && !this.closed) {\n const polled = this.pollEvents(filter, 1);\n if (polled.events.length > 0) {\n return polled.events[0] ?? null;\n }\n await new Promise((r) => setTimeout(r, 250));\n }\n return null;\n }\n\n async sendMessage(params: { sessionKey: string; text: string }): Promise<Record<string, unknown>> {\n const client = this.client!;\n return client.postJson('/api/agent', {\n sessionKey: params.sessionKey,\n message: params.text,\n });\n }\n\n listPendingApprovals(): PendingApproval[] {\n return Array.from(this.pendingApprovals.values());\n }\n\n async respondToApproval(params: {\n kind: ApprovalKind;\n id: string;\n decision: ApprovalDecision;\n }): Promise<Record<string, unknown>> {\n const client = this.client!;\n return client.postJson('/api/mcp/approvals/respond', params);\n }\n\n async handleClaudePermissionRequest(_params: {\n requestId: string;\n toolName: string;\n description: string;\n inputPreview: string;\n }): Promise<void> {\n void _params;\n }\n}\n\n// `serveXopcChannelMcp` lazy wrapper removed — it dynamically imported\n// `./channel-server.js`, forming a bridge ↔ server ↔ tools ↔ bridge cycle.\n// Callers (currently only `cli/commands/mcp.ts`) should import\n// `serveXopcChannelMcpImpl` directly from `./channel-server.js`.\n\nexport function loadMcpServeConfig(): Config {\n return loadConfig();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"channel-bridge.js","names":[],"sources":["../../../src/mcp/channel-bridge.ts"],"sourcesContent":["import type { Config } from '../config/schema.js';\nimport { loadConfig } from '../config/loader.js';\nimport type {\n ApprovalDecision,\n ApprovalKind,\n ClaudeChannelMode,\n ConversationDescriptor,\n PendingApproval,\n QueueEvent,\n SessionRow,\n WaitFilter,\n} from './channel-shared.js';\nimport { toConversation } from './channel-shared.js';\nimport {\n createGatewayHttpClientFromConfig,\n resolveGatewayHttpBaseUrl,\n type GatewayHttpClient,\n} from './gateway-http-client.js';\nimport { loadUndiciRuntimeDeps } from '../infra/undici-fetch.js';\n\nconst QUEUE_LIMIT = 1000;\n\nexport class XopcChannelBridge {\n private client: GatewayHttpClient | null = null;\n private readonly queue: QueueEvent[] = [];\n private readonly pendingApprovals = new Map<string, PendingApproval>();\n private cursor = 0;\n private closed = false;\n private eventsAbort: AbortController | null = null;\n\n constructor(\n private readonly cfg: Config,\n private readonly params: {\n gatewayUrl?: string;\n gatewayToken?: string;\n claudeChannelMode: ClaudeChannelMode;\n verbose: boolean;\n },\n ) {}\n\n setServer(_server: unknown): void {\n void _server;\n }\n\n async start(): Promise<void> {\n this.client = createGatewayHttpClientFromConfig({\n config: this.cfg,\n gatewayUrl: this.params.gatewayUrl,\n gatewayToken: this.params.gatewayToken,\n });\n this.connectEvents();\n }\n\n async close(): Promise<void> {\n this.closed = true;\n this.eventsAbort?.abort();\n this.eventsAbort = null;\n this.queue.length = 0;\n this.pendingApprovals.clear();\n }\n\n private connectEvents(): void {\n if (this.closed) {\n return;\n }\n this.eventsAbort?.abort();\n const abort = new AbortController();\n this.eventsAbort = abort;\n const baseUrl = resolveGatewayHttpBaseUrl(this.cfg, this.params.gatewayUrl);\n void this.runEventsLoop(baseUrl, this.params.gatewayToken, abort.signal);\n }\n\n private async runEventsLoop(\n baseUrl: string,\n token: string | undefined,\n signal: AbortSignal,\n ): Promise<void> {\n const url = new URL(`${baseUrl}/api/events`);\n if (token?.trim()) {\n url.searchParams.set('token', token.trim());\n }\n try {\n const res = await loadUndiciRuntimeDeps().fetch(url.toString(), {\n headers: { Accept: 'text/event-stream' },\n signal,\n });\n if (!res.ok || !res.body) {\n return;\n }\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n while (!this.closed && !signal.aborted) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n buffer += decoder.decode(value, { stream: true });\n let splitAt = buffer.indexOf('\\n\\n');\n while (splitAt >= 0) {\n this.handleSseChunk(buffer.slice(0, splitAt));\n buffer = buffer.slice(splitAt + 2);\n splitAt = buffer.indexOf('\\n\\n');\n }\n }\n } catch {\n // Best-effort reconnect while bridge stays open.\n }\n if (!this.closed && !signal.aborted) {\n await new Promise((resolve) => setTimeout(resolve, 2000));\n if (!this.closed) {\n this.connectEvents();\n }\n }\n }\n\n private handleSseChunk(chunk: string): void {\n let eventName = 'message';\n let data = '';\n for (const line of chunk.split('\\n')) {\n if (line.startsWith('event:')) {\n eventName = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n data += line.slice(5).trim();\n }\n }\n if (!data) {\n return;\n }\n try {\n const parsed = JSON.parse(data) as Record<string, unknown>;\n this.enqueueFromBroadcast({ type: eventName, ...parsed });\n } catch {\n this.enqueueFromBroadcast({ type: eventName, raw: data });\n }\n }\n\n private enqueueFromBroadcast(data: Record<string, unknown>): void {\n const type = String(data.type ?? data.event ?? '');\n if (!type) {\n return;\n }\n if (type.includes('message') || type.includes('session')) {\n this.pushEvent({\n cursor: ++this.cursor,\n type: 'message',\n sessionKey: String(data.sessionKey ?? data.key ?? ''),\n raw: data,\n });\n }\n }\n\n private pushEvent(event: QueueEvent): void {\n this.queue.push(event);\n while (this.queue.length > QUEUE_LIMIT) {\n this.queue.shift();\n }\n }\n\n async listConversations(args: {\n limit?: number;\n search?: string;\n channel?: string;\n }): Promise<ConversationDescriptor[]> {\n const client = this.client!;\n const query = new URLSearchParams();\n if (args.limit) query.set('limit', String(args.limit));\n if (args.search) query.set('search', args.search);\n if (args.channel) query.set('channel', args.channel);\n const qs = query.toString();\n const rows = await client.getJson<SessionRow[] | { sessions?: SessionRow[] }>(\n `/api/sessions${qs ? `?${qs}` : ''}`,\n );\n const sessions = Array.isArray(rows) ? rows : (rows.sessions ?? []);\n return sessions\n .map((row) => toConversation(row))\n .filter((c): c is ConversationDescriptor => c !== null);\n }\n\n async getConversation(sessionKey: string): Promise<ConversationDescriptor | null> {\n const client = this.client!;\n try {\n const row = await client.getJson<SessionRow>(`/api/sessions/${encodeURIComponent(sessionKey)}`);\n return toConversation(row);\n } catch {\n return null;\n }\n }\n\n async readMessages(\n sessionKey: string,\n limit: number,\n ): Promise<Array<Record<string, unknown>>> {\n const client = this.client!;\n const payload = await client.getJson<{ messages?: Array<Record<string, unknown>> }>(\n `/api/sessions/${encodeURIComponent(sessionKey)}/messages?limit=${limit}`,\n );\n return payload.messages ?? [];\n }\n\n pollEvents(\n filter: WaitFilter,\n limit: number,\n ): { events: QueueEvent[]; nextCursor: number } {\n const events = this.queue\n .filter((e) => e.cursor > filter.afterCursor)\n .filter((e) => !filter.sessionKey || ('sessionKey' in e && e.sessionKey === filter.sessionKey))\n .slice(0, limit);\n const nextCursor = events.length > 0 ? events[events.length - 1]!.cursor : filter.afterCursor;\n return { events, nextCursor };\n }\n\n async waitForEvent(filter: WaitFilter, timeoutMs: number): Promise<QueueEvent | null> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline && !this.closed) {\n const polled = this.pollEvents(filter, 1);\n if (polled.events.length > 0) {\n return polled.events[0] ?? null;\n }\n await new Promise((r) => setTimeout(r, 250));\n }\n return null;\n }\n\n async sendMessage(params: { sessionKey: string; text: string }): Promise<Record<string, unknown>> {\n const client = this.client!;\n return client.postJson('/api/agent', {\n sessionKey: params.sessionKey,\n message: params.text,\n });\n }\n\n listPendingApprovals(): PendingApproval[] {\n return Array.from(this.pendingApprovals.values());\n }\n\n async respondToApproval(params: {\n kind: ApprovalKind;\n id: string;\n decision: ApprovalDecision;\n }): Promise<Record<string, unknown>> {\n const client = this.client!;\n return client.postJson('/api/connectors/approvals/respond', params);\n }\n\n async handleClaudePermissionRequest(_params: {\n requestId: string;\n toolName: string;\n description: string;\n inputPreview: string;\n }): Promise<void> {\n void _params;\n }\n}\n\nexport function loadMcpServeConfig(): Config {\n return loadConfig();\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Canonical persisted reference: xopc-attachment://notes/{noteId}/{attachmentId} */
|
|
2
|
+
export declare const NOTE_ATTACHMENT_SCHEME = "xopc-attachment";
|
|
3
|
+
export type ParsedNoteAttachmentRef = {
|
|
4
|
+
noteId: string;
|
|
5
|
+
attachmentId: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function buildNoteAttachmentRef(noteId: string, attachmentId: string): string;
|
|
8
|
+
export declare function parseNoteAttachmentTarget(target: string, expectedNoteId?: string): ParsedNoteAttachmentRef | null;
|
|
9
|
+
export declare function attachmentIdFromTarget(target: string, noteId: string): string | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/notes/attachment-ref.ts
|
|
2
|
+
/** Canonical persisted reference: xopc-attachment://notes/{noteId}/{attachmentId} */
|
|
3
|
+
const NOTE_ATTACHMENT_SCHEME = "xopc-attachment";
|
|
4
|
+
const CANONICAL_REF = /^xopc-attachment:\/\/notes\/([^/]+)\/([^/?#]+)$/i;
|
|
5
|
+
function buildNoteAttachmentRef(noteId, attachmentId) {
|
|
6
|
+
return `${NOTE_ATTACHMENT_SCHEME}://notes/${encodeURIComponent(noteId)}/${encodeURIComponent(attachmentId)}`;
|
|
7
|
+
}
|
|
8
|
+
function parseNoteAttachmentTarget(target, expectedNoteId) {
|
|
9
|
+
const trimmed = target.trim();
|
|
10
|
+
if (!trimmed) return null;
|
|
11
|
+
const canonical = trimmed.split("?")[0].match(CANONICAL_REF);
|
|
12
|
+
if (!canonical) return null;
|
|
13
|
+
const noteId = decodeURIComponent(canonical[1]);
|
|
14
|
+
const attachmentId = decodeURIComponent(canonical[2]);
|
|
15
|
+
if (expectedNoteId && noteId !== expectedNoteId) return null;
|
|
16
|
+
return {
|
|
17
|
+
noteId,
|
|
18
|
+
attachmentId
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function attachmentIdFromTarget(target, noteId) {
|
|
22
|
+
return parseNoteAttachmentTarget(target, noteId)?.attachmentId;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { NOTE_ATTACHMENT_SCHEME, attachmentIdFromTarget, buildNoteAttachmentRef, parseNoteAttachmentTarget };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=attachment-ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attachment-ref.js","names":[],"sources":["../../../src/notes/attachment-ref.ts"],"sourcesContent":["/** Canonical persisted reference: xopc-attachment://notes/{noteId}/{attachmentId} */\nexport const NOTE_ATTACHMENT_SCHEME = 'xopc-attachment';\n\nconst CANONICAL_REF =\n /^xopc-attachment:\\/\\/notes\\/([^/]+)\\/([^/?#]+)$/i;\n\nexport type ParsedNoteAttachmentRef = {\n noteId: string;\n attachmentId: string;\n};\n\nexport function buildNoteAttachmentRef(noteId: string, attachmentId: string): string {\n return `${NOTE_ATTACHMENT_SCHEME}://notes/${encodeURIComponent(noteId)}/${encodeURIComponent(attachmentId)}`;\n}\n\nexport function parseNoteAttachmentTarget(\n target: string,\n expectedNoteId?: string,\n): ParsedNoteAttachmentRef | null {\n const trimmed = target.trim();\n if (!trimmed) return null;\n\n const canonical = trimmed.split('?')[0].match(CANONICAL_REF);\n if (!canonical) return null;\n\n const noteId = decodeURIComponent(canonical[1]);\n const attachmentId = decodeURIComponent(canonical[2]);\n if (expectedNoteId && noteId !== expectedNoteId) return null;\n return { noteId, attachmentId };\n}\n\nexport function attachmentIdFromTarget(target: string, noteId: string): string | undefined {\n return parseNoteAttachmentTarget(target, noteId)?.attachmentId;\n}\n"],"mappings":";;AACA,MAAa,yBAAyB;AAEtC,MAAM,gBACJ;AAOF,SAAgB,uBAAuB,QAAgB,cAA8B;AACnF,QAAO,GAAG,uBAAuB,WAAW,mBAAmB,OAAO,CAAC,GAAG,mBAAmB,aAAa;;AAG5G,SAAgB,0BACd,QACA,gBACgC;CAChC,MAAM,UAAU,OAAO,MAAM;AAC7B,KAAI,CAAC,QAAS,QAAO;CAErB,MAAM,YAAY,QAAQ,MAAM,IAAI,CAAC,GAAG,MAAM,cAAc;AAC5D,KAAI,CAAC,UAAW,QAAO;CAEvB,MAAM,SAAS,mBAAmB,UAAU,GAAG;CAC/C,MAAM,eAAe,mBAAmB,UAAU,GAAG;AACrD,KAAI,kBAAkB,WAAW,eAAgB,QAAO;AACxD,QAAO;EAAE;EAAQ;EAAc;;AAGjC,SAAgB,uBAAuB,QAAgB,QAAoC;AACzF,QAAO,0BAA0B,QAAQ,OAAO,EAAE"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { NotesStore } from './store.js';
|
|
2
|
+
export { NotesService } from './service.js';
|
|
3
|
+
export { resolveNotesDir, resolveNotesIndexPath, resolveNoteItemPath, resolveNoteMediaDir, resolveNoteHistoryDir } from './paths.js';
|
|
4
|
+
export type { Note, NoteKind, NoteStatus, NoteAttachment, NoteAiMeta, NoteAiDeepMeta, NoteIndexEntry, NoteSnapshot, NoteSnapshotEntry, NotesIndexFile, NotesListQuery, CaptureSource, CaptureChannel, CreateNoteParams, SnapshotTrigger, NoteTaskMeta, NoteGroup, } from './types.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { resolveNoteHistoryDir, resolveNoteItemPath, resolveNoteMediaDir, resolveNotesDir, resolveNotesIndexPath } from "./paths.js";
|
|
2
|
+
import { NotesStore } from "./store.js";
|
|
3
|
+
import { NotesService } from "./service.js";
|
|
4
|
+
export { NotesService, NotesStore, resolveNoteHistoryDir, resolveNoteItemPath, resolveNoteMediaDir, resolveNotesDir, resolveNotesIndexPath };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Note, NoteAttachment } from './types.js';
|
|
2
|
+
/** Attachment ids referenced in note markdown, blocks, or plain text. */
|
|
3
|
+
export declare function collectReferencedAttachmentIds(note: Pick<Note, 'id' | 'text' | 'blocks'>): Set<string>;
|
|
4
|
+
export declare function partitionAttachmentsByReference(note: Pick<Note, 'id' | 'text' | 'blocks' | 'attachments'>): {
|
|
5
|
+
kept: NoteAttachment[];
|
|
6
|
+
removed: NoteAttachment[];
|
|
7
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { attachmentIdFromTarget } from "./attachment-ref.js";
|
|
2
|
+
import { notePlainText } from "./note-index-meta.js";
|
|
3
|
+
//#region src/notes/note-attachment-sync.ts
|
|
4
|
+
const MARKDOWN_IMAGE = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
5
|
+
const MARKDOWN_LINK = /(?<!!)\[([^\]]*)\]\(([^)]+)\)/g;
|
|
6
|
+
const BARE_ATTACHMENT_REF = /xopc-attachment:\/\/notes\/([^/]+)\/([^/?#"'\s)]+)/gi;
|
|
7
|
+
/** Attachment ids referenced in note markdown, blocks, or plain text. */
|
|
8
|
+
function collectReferencedAttachmentIds(note) {
|
|
9
|
+
const ids = /* @__PURE__ */ new Set();
|
|
10
|
+
for (const block of note.blocks ?? []) if (block.type === "image") ids.add(block.attachmentId);
|
|
11
|
+
const source = notePlainText(note);
|
|
12
|
+
if (!source.trim()) return ids;
|
|
13
|
+
for (const match of source.matchAll(MARKDOWN_IMAGE)) {
|
|
14
|
+
const target = match[2];
|
|
15
|
+
if (typeof target !== "string") continue;
|
|
16
|
+
const attachmentId = attachmentIdFromTarget(target, note.id);
|
|
17
|
+
if (attachmentId) ids.add(attachmentId);
|
|
18
|
+
}
|
|
19
|
+
for (const match of source.matchAll(MARKDOWN_LINK)) {
|
|
20
|
+
const target = match[2];
|
|
21
|
+
if (typeof target !== "string") continue;
|
|
22
|
+
const attachmentId = attachmentIdFromTarget(target, note.id);
|
|
23
|
+
if (attachmentId) ids.add(attachmentId);
|
|
24
|
+
}
|
|
25
|
+
for (const match of source.matchAll(BARE_ATTACHMENT_REF)) {
|
|
26
|
+
if (decodeURIComponent(match[1]) !== note.id) continue;
|
|
27
|
+
ids.add(decodeURIComponent(match[2]));
|
|
28
|
+
}
|
|
29
|
+
return ids;
|
|
30
|
+
}
|
|
31
|
+
function partitionAttachmentsByReference(note) {
|
|
32
|
+
const referenced = collectReferencedAttachmentIds(note);
|
|
33
|
+
const attachments = note.attachments ?? [];
|
|
34
|
+
const kept = [];
|
|
35
|
+
const removed = [];
|
|
36
|
+
for (const attachment of attachments) if (referenced.has(attachment.id)) kept.push(attachment);
|
|
37
|
+
else removed.push(attachment);
|
|
38
|
+
return {
|
|
39
|
+
kept,
|
|
40
|
+
removed
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { collectReferencedAttachmentIds, partitionAttachmentsByReference };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=note-attachment-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"note-attachment-sync.js","names":[],"sources":["../../../src/notes/note-attachment-sync.ts"],"sourcesContent":["import type { Note, NoteAttachment } from './types.js';\nimport { attachmentIdFromTarget } from './attachment-ref.js';\nimport { notePlainText } from './note-index-meta.js';\n\nconst MARKDOWN_IMAGE = /!\\[([^\\]]*)\\]\\(([^)]+)\\)/g;\nconst MARKDOWN_LINK = /(?<!!)\\[([^\\]]*)\\]\\(([^)]+)\\)/g;\nconst BARE_ATTACHMENT_REF = /xopc-attachment:\\/\\/notes\\/([^/]+)\\/([^/?#\"'\\s)]+)/gi;\n\n/** Attachment ids referenced in note markdown, blocks, or plain text. */\nexport function collectReferencedAttachmentIds(\n note: Pick<Note, 'id' | 'text' | 'blocks'>,\n): Set<string> {\n const ids = new Set<string>();\n\n for (const block of note.blocks ?? []) {\n if (block.type === 'image') ids.add(block.attachmentId);\n }\n\n const source = notePlainText(note);\n if (!source.trim()) return ids;\n\n for (const match of source.matchAll(MARKDOWN_IMAGE)) {\n const target = match[2];\n if (typeof target !== 'string') continue;\n const attachmentId = attachmentIdFromTarget(target, note.id);\n if (attachmentId) ids.add(attachmentId);\n }\n\n for (const match of source.matchAll(MARKDOWN_LINK)) {\n const target = match[2];\n if (typeof target !== 'string') continue;\n const attachmentId = attachmentIdFromTarget(target, note.id);\n if (attachmentId) ids.add(attachmentId);\n }\n\n for (const match of source.matchAll(BARE_ATTACHMENT_REF)) {\n const noteId = decodeURIComponent(match[1]);\n if (noteId !== note.id) continue;\n ids.add(decodeURIComponent(match[2]));\n }\n\n return ids;\n}\n\nexport function partitionAttachmentsByReference(\n note: Pick<Note, 'id' | 'text' | 'blocks' | 'attachments'>,\n): { kept: NoteAttachment[]; removed: NoteAttachment[] } {\n const referenced = collectReferencedAttachmentIds(note);\n const attachments = note.attachments ?? [];\n const kept: NoteAttachment[] = [];\n const removed: NoteAttachment[] = [];\n\n for (const attachment of attachments) {\n if (referenced.has(attachment.id)) {\n kept.push(attachment);\n } else {\n removed.push(attachment);\n }\n }\n\n return { kept, removed };\n}\n"],"mappings":";;;AAIA,MAAM,iBAAiB;AACvB,MAAM,gBAAgB;AACtB,MAAM,sBAAsB;;AAG5B,SAAgB,+BACd,MACa;CACb,MAAM,sBAAM,IAAI,KAAa;AAE7B,MAAK,MAAM,SAAS,KAAK,UAAU,EAAE,CACnC,KAAI,MAAM,SAAS,QAAS,KAAI,IAAI,MAAM,aAAa;CAGzD,MAAM,SAAS,cAAc,KAAK;AAClC,KAAI,CAAC,OAAO,MAAM,CAAE,QAAO;AAE3B,MAAK,MAAM,SAAS,OAAO,SAAS,eAAe,EAAE;EACnD,MAAM,SAAS,MAAM;AACrB,MAAI,OAAO,WAAW,SAAU;EAChC,MAAM,eAAe,uBAAuB,QAAQ,KAAK,GAAG;AAC5D,MAAI,aAAc,KAAI,IAAI,aAAa;;AAGzC,MAAK,MAAM,SAAS,OAAO,SAAS,cAAc,EAAE;EAClD,MAAM,SAAS,MAAM;AACrB,MAAI,OAAO,WAAW,SAAU;EAChC,MAAM,eAAe,uBAAuB,QAAQ,KAAK,GAAG;AAC5D,MAAI,aAAc,KAAI,IAAI,aAAa;;AAGzC,MAAK,MAAM,SAAS,OAAO,SAAS,oBAAoB,EAAE;AAExD,MADe,mBAAmB,MAAM,GAC9B,KAAK,KAAK,GAAI;AACxB,MAAI,IAAI,mBAAmB,MAAM,GAAG,CAAC;;AAGvC,QAAO;;AAGT,SAAgB,gCACd,MACuD;CACvD,MAAM,aAAa,+BAA+B,KAAK;CACvD,MAAM,cAAc,KAAK,eAAe,EAAE;CAC1C,MAAM,OAAyB,EAAE;CACjC,MAAM,UAA4B,EAAE;AAEpC,MAAK,MAAM,cAAc,YACvB,KAAI,WAAW,IAAI,WAAW,GAAG,CAC/B,MAAK,KAAK,WAAW;KAErB,SAAQ,KAAK,WAAW;AAI5B,QAAO;EAAE;EAAM;EAAS"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Note } from './types.js';
|
|
2
|
+
export { parseNoteAttachmentTarget, buildNoteAttachmentRef } from './attachment-ref.js';
|
|
3
|
+
export declare function notePlainText(note: Pick<Note, 'id' | 'text' | 'blocks'>): string;
|
|
4
|
+
export declare function stripMediaFromPlainText(text: string): string;
|
|
5
|
+
export declare function extractAttachmentFileNames(note: Pick<Note, 'attachments'>): string[] | undefined;
|
|
6
|
+
export declare function extractCoverAttachmentId(note: Note): string | undefined;
|
|
7
|
+
export declare function buildNoteSnippet(note: Pick<Note, 'id' | 'text' | 'blocks'>): string | undefined;
|
|
8
|
+
export declare function buildNoteIndexMeta(note: Note): {
|
|
9
|
+
snippet?: string;
|
|
10
|
+
coverAttachmentId?: string;
|
|
11
|
+
voiceAttachmentId?: string;
|
|
12
|
+
voiceDurationSec?: number;
|
|
13
|
+
attachmentNames?: string[];
|
|
14
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { attachmentIdFromTarget, buildNoteAttachmentRef, parseNoteAttachmentTarget } from "./attachment-ref.js";
|
|
2
|
+
//#region src/notes/note-index-meta.ts
|
|
3
|
+
const SNIPPET_LENGTH = 100;
|
|
4
|
+
const MARKDOWN_IMAGE = /!\[([^\]]*)\]\(([^)]+)\)/g;
|
|
5
|
+
const MARKDOWN_LINK = /(?<!!)\[([^\]]*)\]\(([^)]+)\)/g;
|
|
6
|
+
const BARE_ATTACHMENT_REF = /xopc-attachment:\/\/notes\/[^/]+\/[^/?#"'\s)]+/gi;
|
|
7
|
+
function notePlainText(note) {
|
|
8
|
+
if (note.text?.trim()) return note.text;
|
|
9
|
+
return note.blocks?.map((block) => {
|
|
10
|
+
if (block.type === "divider") return "";
|
|
11
|
+
if (block.type === "todo") return block.text;
|
|
12
|
+
if (block.type === "image") return `})`;
|
|
13
|
+
return block.text;
|
|
14
|
+
}).join(" ") ?? "";
|
|
15
|
+
}
|
|
16
|
+
function isNoteMediaTarget(target) {
|
|
17
|
+
return parseNoteAttachmentTarget(target) !== null;
|
|
18
|
+
}
|
|
19
|
+
function stripMediaFromPlainText(text) {
|
|
20
|
+
let result = text.replace(MARKDOWN_IMAGE, " ");
|
|
21
|
+
result = result.replace(MARKDOWN_LINK, (full, _alt, target) => {
|
|
22
|
+
if (typeof target !== "string") return full;
|
|
23
|
+
return isNoteMediaTarget(target) ? " " : full;
|
|
24
|
+
});
|
|
25
|
+
result = result.replace(BARE_ATTACHMENT_REF, " ");
|
|
26
|
+
return result.replace(/\s+/g, " ").trim();
|
|
27
|
+
}
|
|
28
|
+
function extractAttachmentFileNames(note) {
|
|
29
|
+
const names = note.attachments?.map((item) => item.fileName.trim().toLowerCase()).filter(Boolean);
|
|
30
|
+
if (!names?.length) return void 0;
|
|
31
|
+
return [...new Set(names)];
|
|
32
|
+
}
|
|
33
|
+
function extractCoverAttachmentId(note) {
|
|
34
|
+
for (const block of note.blocks ?? []) {
|
|
35
|
+
if (block.type !== "image") continue;
|
|
36
|
+
const attachment = note.attachments?.find((item) => item.id === block.attachmentId);
|
|
37
|
+
if (!attachment || attachment.type === "image") return block.attachmentId;
|
|
38
|
+
}
|
|
39
|
+
const text = notePlainText(note);
|
|
40
|
+
if (!text.trim()) return void 0;
|
|
41
|
+
for (const match of text.matchAll(MARKDOWN_IMAGE)) {
|
|
42
|
+
const target = match[2];
|
|
43
|
+
if (typeof target !== "string") continue;
|
|
44
|
+
const attachmentId = attachmentIdFromTarget(target, note.id);
|
|
45
|
+
if (!attachmentId) continue;
|
|
46
|
+
const attachment = note.attachments?.find((item) => item.id === attachmentId);
|
|
47
|
+
if (!attachment || attachment.type === "image") return attachmentId;
|
|
48
|
+
}
|
|
49
|
+
for (const match of text.matchAll(BARE_ATTACHMENT_REF)) {
|
|
50
|
+
const parsed = parseNoteAttachmentTarget(match[0], note.id);
|
|
51
|
+
if (!parsed) continue;
|
|
52
|
+
const attachment = note.attachments?.find((item) => item.id === parsed.attachmentId);
|
|
53
|
+
if (!attachment || attachment.type === "image") return parsed.attachmentId;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function buildNoteSnippet(note) {
|
|
57
|
+
const text = notePlainText(note);
|
|
58
|
+
if (!text.trim()) return void 0;
|
|
59
|
+
const clean = stripMediaFromPlainText(text);
|
|
60
|
+
if (!clean) return void 0;
|
|
61
|
+
return clean.length > SNIPPET_LENGTH ? `${clean.slice(0, SNIPPET_LENGTH)}…` : clean;
|
|
62
|
+
}
|
|
63
|
+
function extractVoiceAttachment(note) {
|
|
64
|
+
const audio = note.attachments?.find((a) => a.type === "audio");
|
|
65
|
+
if (!audio) return {};
|
|
66
|
+
return {
|
|
67
|
+
voiceAttachmentId: audio.id,
|
|
68
|
+
voiceDurationSec: audio.duration
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function buildNoteIndexMeta(note) {
|
|
72
|
+
const coverAttachmentId = extractCoverAttachmentId(note);
|
|
73
|
+
const snippet = buildNoteSnippet(note);
|
|
74
|
+
const attachmentNames = extractAttachmentFileNames(note);
|
|
75
|
+
const { voiceAttachmentId, voiceDurationSec } = extractVoiceAttachment(note);
|
|
76
|
+
return {
|
|
77
|
+
snippet,
|
|
78
|
+
coverAttachmentId,
|
|
79
|
+
voiceAttachmentId,
|
|
80
|
+
voiceDurationSec,
|
|
81
|
+
attachmentNames
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
export { buildNoteAttachmentRef, buildNoteIndexMeta, buildNoteSnippet, extractAttachmentFileNames, extractCoverAttachmentId, notePlainText, parseNoteAttachmentTarget, stripMediaFromPlainText };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=note-index-meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"note-index-meta.js","names":[],"sources":["../../../src/notes/note-index-meta.ts"],"sourcesContent":["import type { Note } from './types.js';\nimport {\n attachmentIdFromTarget,\n buildNoteAttachmentRef,\n parseNoteAttachmentTarget,\n} from './attachment-ref.js';\n\nconst SNIPPET_LENGTH = 100;\nconst MARKDOWN_IMAGE = /!\\[([^\\]]*)\\]\\(([^)]+)\\)/g;\nconst MARKDOWN_LINK = /(?<!!)\\[([^\\]]*)\\]\\(([^)]+)\\)/g;\nconst BARE_ATTACHMENT_REF = /xopc-attachment:\\/\\/notes\\/[^/]+\\/[^/?#\"'\\s)]+/gi;\n\nexport { parseNoteAttachmentTarget, buildNoteAttachmentRef } from './attachment-ref.js';\n\nexport function notePlainText(note: Pick<Note, 'id' | 'text' | 'blocks'>): string {\n if (note.text?.trim()) return note.text;\n return (\n note.blocks\n ?.map((block) => {\n if (block.type === 'divider') return '';\n if (block.type === 'todo') return block.text;\n if (block.type === 'image') {\n const alt = block.alt ?? '';\n return `})`;\n }\n return block.text;\n })\n .join(' ') ?? ''\n );\n}\n\nfunction isNoteMediaTarget(target: string): boolean {\n return parseNoteAttachmentTarget(target) !== null;\n}\n\nexport function stripMediaFromPlainText(text: string): string {\n let result = text.replace(MARKDOWN_IMAGE, ' ');\n result = result.replace(MARKDOWN_LINK, (full, _alt, target) => {\n if (typeof target !== 'string') return full;\n return isNoteMediaTarget(target) ? ' ' : full;\n });\n result = result.replace(BARE_ATTACHMENT_REF, ' ');\n return result.replace(/\\s+/g, ' ').trim();\n}\n\nexport function extractAttachmentFileNames(note: Pick<Note, 'attachments'>): string[] | undefined {\n const names = note.attachments?.map((item) => item.fileName.trim().toLowerCase()).filter(Boolean);\n if (!names?.length) return undefined;\n return [...new Set(names)];\n}\n\nexport function extractCoverAttachmentId(note: Note): string | undefined {\n for (const block of note.blocks ?? []) {\n if (block.type !== 'image') continue;\n const attachment = note.attachments?.find((item) => item.id === block.attachmentId);\n if (!attachment || attachment.type === 'image') return block.attachmentId;\n }\n\n const text = notePlainText(note);\n if (!text.trim()) return undefined;\n\n for (const match of text.matchAll(MARKDOWN_IMAGE)) {\n const target = match[2];\n if (typeof target !== 'string') continue;\n const attachmentId = attachmentIdFromTarget(target, note.id);\n if (!attachmentId) continue;\n const attachment = note.attachments?.find((item) => item.id === attachmentId);\n if (!attachment || attachment.type === 'image') return attachmentId;\n }\n\n for (const match of text.matchAll(BARE_ATTACHMENT_REF)) {\n const parsed = parseNoteAttachmentTarget(match[0], note.id);\n if (!parsed) continue;\n const attachment = note.attachments?.find((item) => item.id === parsed.attachmentId);\n if (!attachment || attachment.type === 'image') return parsed.attachmentId;\n }\n\n return undefined;\n}\n\nexport function buildNoteSnippet(note: Pick<Note, 'id' | 'text' | 'blocks'>): string | undefined {\n const text = notePlainText(note);\n if (!text.trim()) return undefined;\n const clean = stripMediaFromPlainText(text);\n if (!clean) return undefined;\n return clean.length > SNIPPET_LENGTH ? `${clean.slice(0, SNIPPET_LENGTH)}…` : clean;\n}\n\nfunction extractVoiceAttachment(note: Note): { voiceAttachmentId?: string; voiceDurationSec?: number } {\n const audio = note.attachments?.find((a) => a.type === 'audio');\n if (!audio) return {};\n return { voiceAttachmentId: audio.id, voiceDurationSec: audio.duration };\n}\n\nexport function buildNoteIndexMeta(note: Note): {\n snippet?: string;\n coverAttachmentId?: string;\n voiceAttachmentId?: string;\n voiceDurationSec?: number;\n attachmentNames?: string[];\n} {\n const coverAttachmentId = extractCoverAttachmentId(note);\n const snippet = buildNoteSnippet(note);\n const attachmentNames = extractAttachmentFileNames(note);\n const { voiceAttachmentId, voiceDurationSec } = extractVoiceAttachment(note);\n return {\n snippet,\n coverAttachmentId,\n voiceAttachmentId,\n voiceDurationSec,\n attachmentNames,\n };\n}\n"],"mappings":"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"}
|