@xopcai/xopc 0.0.111 → 0.0.113
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/icons/icon-128.png +0 -0
- package/dist/browser-ext/icons/icon-16.png +0 -0
- package/dist/browser-ext/icons/icon-32.png +0 -0
- package/dist/browser-ext/icons/icon-48.png +0 -0
- 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/adapters/streaming-adapter.js +1 -1
- package/dist/extensions/telegram/src/inbound-processor.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/update-offset-store.js +1 -1
- 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/delivery-to.d.ts +3 -3
- package/dist/extensions/weixin/src/delivery-to.js.map +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/apple-touch-icon.png +0 -0
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-zlx9l5Yp.js → agent-avatar-dicebear-CBlklyNr.js} +1 -1
- package/dist/gateway/static/root/assets/agent-browser-settings-page-rGbKc2Hh.js +6 -0
- package/dist/gateway/static/root/assets/{agent-defaults-tabbed-page-Bz7JWjRM.js → agent-defaults-tabbed-page-CtwVbjOA.js} +1 -1
- package/dist/gateway/static/root/assets/agents-CCBmGcwc.js +71 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-DaQwdeCq.js → app-management-settings-panel-28E_WfMe.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-D2zv6r3P.js → appearance-settings-Do1z0XgJ.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-BIppe_jj.js → apps-page-xwCsJS15.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-DEmu8eH7.js → archive-plugin-BAFlsov_.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-B98lJoN5.js → attachment-load-V3qGPC3x.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-CL9QKmyo.js → binary-plugins-DT8NvA6W.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BOAhMAhT.js → block-editor-CdLMEcHd.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-Br8iRnXp.js → channels-settings-C044DcYk.js} +1 -1
- package/dist/gateway/static/root/assets/{config-api-0o1WYdz2.js → config-api-Bf5mjpu7.js} +1 -1
- package/dist/gateway/static/root/assets/config-reload-event-Bjh2zoKp.js +1 -0
- package/dist/gateway/static/root/assets/connectors-settings-8VEWUOTj.js +2 -0
- package/dist/gateway/static/root/assets/cron-api--pAB_rps.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-DqmNGNOy.js +3 -0
- package/dist/gateway/static/root/assets/dreaming-settings-6UZE1FSK.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-Cjx6pZVz.js → extension-debug-page-BGhE7msg.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-BSPNL90V.js → extension-page-CvpI45WG.js} +1 -1
- package/dist/gateway/static/root/assets/extension-settings-page-DAmR5FUe.js +1 -0
- package/dist/gateway/static/root/assets/{fetch-Bs7in_4F.js → fetch-NZmjSXLl.js} +1 -1
- package/dist/gateway/static/root/assets/format-cron-label-BsotZDO5.js +2 -0
- package/dist/gateway/static/root/assets/{format-interval-label-CftJ5uXu.js → format-interval-label-qhth3BMV.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-Clfck-hW.js → gateway-config-api-CaXBt-9R.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-DphpbLBc.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-BkjVJc38.js → gateway-startup-retry-Be1omjh3.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-detail-page-kqJue0sE.js → goal-detail-page-bv0VRDaf.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-1Ywnt4Cb.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-CJnzNSDo.js → goals-settings-ceEBjnfc.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-Cp7CPuIn.js → heartbeat-config-api-BzO6G4fD.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-ifSI1Wbh.js → heartbeat-settings-DCST9-du.js} +1 -1
- package/dist/gateway/static/root/assets/index-Cxm6xsps.js +122 -0
- package/dist/gateway/static/root/assets/index-b_R4tKqt.css +1 -0
- package/dist/gateway/static/root/assets/interval-presets-DJmshV8n.js +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings--MI1rYl7.js → keyboard-shortcuts-settings-9AUUlQ5Q.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-BwrVNlyW.js → logs-page-B_wrxhee.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-split-B_oc9lJe.js → markdown-split-CYYELqAr.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DLPvB5H3.js → media-plugins-BEcMR2M8.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CkT-lcgP.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-D0JDSbNu.js +2 -0
- package/dist/gateway/static/root/assets/note-detail-page-DpFV-EO6.js +2 -0
- package/dist/gateway/static/root/assets/{note-image-lightbox-rSqAgOi9.js → note-image-lightbox-DNAoPcmm.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-C9ACGEty.js → notes-page-CdkjTch8.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-Bw_N2oZi.js → preview-open-alternatives-BPLz0AIv.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-BhcWIKuM.js → remote-access-hub-DSYVAi96.js} +1 -1
- package/dist/gateway/static/root/assets/{save-bar-controls-CNP2jmw0.js → save-bar-controls-CrWwWRVK.js} +1 -1
- package/dist/gateway/static/root/assets/schedule-field-CF2sadvN.js +1 -0
- package/dist/gateway/static/root/assets/{schema-form-CDPzua9q.js → schema-form-Di5cKjYE.js} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-DzkpSDZr.js → sessions-page-CenkcVjF.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-D9sva86V.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-C9bff85B.js → settings-collapsible-section-Chh282jW.js} +1 -1
- package/dist/gateway/static/root/assets/settings-form-section-Dq-83cQd.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-DWK4Zo7s.js → settings-page-DL35U0x0.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Ctwbp9Er.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-DnutoY_S.js → share-preview-page-0FXYnSUA.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-6oRgJMpn.js → shares-settings-DIM3JNsr.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-DAlNaNzL.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-CLrHVzAt.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-qP1JDtDd.js → system-settings-panel-W1pTcojB.js} +1 -1
- package/dist/gateway/static/root/assets/use-agent-defaults-form-BVRvkK7m.js +1 -0
- package/dist/gateway/static/root/assets/{use-channel-catalog-C4mg1hjD.js → use-channel-catalog-CgJibfMM.js} +1 -1
- package/dist/gateway/static/root/assets/{use-settings-tab-guard-F6C0mpJt.js → use-settings-tab-guard-Bw_qkRQE.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-BnDDb6qO.js → voice-api-key-field-vYAO71Gn.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-player-store-B2EwIhtY.js → voice-player-store-C7lziaAx.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-page.utils-CRB7WX9l.js → workflow-page.utils-sgHjOXOm.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-2Fptr0je.js → workflows-page-DYyOTmFx.js} +1 -1
- package/dist/gateway/static/root/favicon-16x16.png +0 -0
- package/dist/gateway/static/root/favicon-32x32.png +0 -0
- package/dist/gateway/static/root/favicon-icon.svg +1 -0
- package/dist/gateway/static/root/favicon.ico +0 -0
- package/dist/gateway/static/root/favicon.png +0 -0
- package/dist/gateway/static/root/favicon.svg +24 -15
- package/dist/gateway/static/root/index.html +11 -10
- package/dist/gateway/static/root/logo-dark.svg +23 -15
- package/dist/gateway/static/root/logo.svg +23 -16
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +28 -2
- package/dist/src/agent/agent-manager.js +82 -11
- package/dist/src/agent/agent-manager.js.map +1 -1
- 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/context/workspace-state.js +1 -1
- package/dist/src/agent/context/workspace.js +1 -1
- package/dist/src/agent/goals/goal-locale.js +2 -2
- package/dist/src/agent/goals/persistent-goal-service.js +1 -1
- package/dist/src/agent/goals/post-turn.js +3 -3
- package/dist/src/agent/image/load-image-media.js +2 -2
- package/dist/src/agent/ipc/inbox.js +3 -3
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
- 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/memory-flush.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +3 -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/models/manager.js +1 -1
- package/dist/src/agent/prompt/memory/index.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/process-direct-streaming.js +9 -4
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service/reconcile-dreaming-cron.js +8 -5
- package/dist/src/agent/service/reconcile-dreaming-cron.js.map +1 -1
- package/dist/src/agent/service.d.ts +2 -1
- package/dist/src/agent/service.js +10 -5
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +4 -0
- package/dist/src/agent/session/session-config-service.d.ts +8 -0
- package/dist/src/agent/session/session-config-service.js +20 -0
- package/dist/src/agent/session/session-config-service.js.map +1 -1
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/config.js +2 -2
- package/dist/src/agent/skills/filesystem-watcher.d.ts +19 -0
- package/dist/src/agent/skills/filesystem-watcher.js +182 -0
- package/dist/src/agent/skills/filesystem-watcher.js.map +1 -0
- 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 +30 -36
- package/dist/src/agent/skills/hub-pull.js.map +1 -1
- package/dist/src/agent/skills/index.js +82 -38
- package/dist/src/agent/skills/index.js.map +1 -1
- package/dist/src/agent/skills/installer.js +2 -2
- package/dist/src/agent/skills/managed-store.d.ts +19 -0
- package/dist/src/agent/skills/managed-store.js +97 -38
- package/dist/src/agent/skills/managed-store.js.map +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manager.d.ts +16 -6
- package/dist/src/agent/skills/skill-manager.js +79 -24
- package/dist/src/agent/skills/skill-manager.js.map +1 -1
- package/dist/src/agent/skills/skill-view-path.js +1 -1
- package/dist/src/agent/skills/test-framework.js +1 -1
- package/dist/src/agent/skills/types.d.ts +11 -0
- package/dist/src/agent/skills/watcher.js +1 -1
- package/dist/src/agent/starter-agents.js +1 -1
- package/dist/src/agent/tools/cronjob-tool.d.ts +6 -3
- package/dist/src/agent/tools/cronjob-tool.js +58 -23
- package/dist/src/agent/tools/cronjob-tool.js.map +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/find.js +1 -1
- package/dist/src/agent/tools/grep.js +1 -1
- package/dist/src/agent/tools/image-generate-tool.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/workflow/catalog.js +1 -1
- package/dist/src/agent/workspace-runtime/registry.d.ts +5 -0
- package/dist/src/agent/workspace-runtime/registry.js +7 -0
- package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
- package/dist/src/ai-assist/text-assist.d.ts +42 -0
- package/dist/src/ai-assist/text-assist.js +252 -0
- package/dist/src/ai-assist/text-assist.js.map +1 -0
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/profiles/store.js +2 -2
- 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 +4 -4
- package/dist/src/browser/providers/cloakbrowser.js +5 -5
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.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/channels/plugins/types.adapters.d.ts +2 -2
- package/dist/src/chat-commands/agent-edit.js +2 -2
- package/dist/src/chat-commands/builtins/config.js +3 -3
- package/dist/src/chat-commands/builtins/system.js +11 -4
- package/dist/src/chat-commands/builtins/system.js.map +1 -1
- package/dist/src/chat-commands/context.d.ts +3 -0
- package/dist/src/chat-commands/context.js +7 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +2 -0
- package/dist/src/cli/commands/config.js +1 -1
- package/dist/src/cli/commands/cron.js +55 -16
- package/dist/src/cli/commands/cron.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/cron-health.js +2 -83
- package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- 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-create.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +2 -2
- package/dist/src/cli/commands/extension-marketplace.js +2 -2
- package/dist/src/cli/commands/extension-pack.js +2 -2
- package/dist/src/cli/commands/extension.js +3 -3
- 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 +2 -2
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/skills-test.js +1 -1
- package/dist/src/cli/templates.js +1 -1
- package/dist/src/cli/utils/gateway-client.js +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +2 -2
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/agent-typed-models.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +7 -7
- package/dist/src/config/loader.js +4 -4
- package/dist/src/config/models-json.js +2 -2
- package/dist/src/config/paths-state.js +1 -1
- package/dist/src/config/paths.js +1 -1
- package/dist/src/config/profile.js +1 -1
- package/dist/src/config/reload.js +1 -1
- package/dist/src/config/schema.d.ts +0 -2
- package/dist/src/config/schema.js +0 -3
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/config/web-patch.d.ts +0 -1
- package/dist/src/config/web-patch.js +0 -2
- package/dist/src/config/web-patch.js.map +1 -1
- package/dist/src/config/workspace-path.js +1 -1
- package/dist/src/cron/executor.js +102 -71
- package/dist/src/cron/executor.js.map +1 -1
- package/dist/src/cron/index.d.ts +1 -1
- package/dist/src/cron/index.js +3 -3
- package/dist/src/cron/run-log-store.d.ts +1 -0
- package/dist/src/cron/run-log-store.js +4 -2
- package/dist/src/cron/run-log-store.js.map +1 -1
- package/dist/src/cron/schedule.d.ts +10 -0
- package/dist/src/cron/schedule.js +85 -0
- package/dist/src/cron/schedule.js.map +1 -0
- package/dist/src/cron/service.d.ts +34 -77
- package/dist/src/cron/service.js +327 -264
- package/dist/src/cron/service.js.map +1 -1
- package/dist/src/cron/types.d.ts +80 -50
- package/dist/src/cron/validation.d.ts +358 -90
- package/dist/src/cron/validation.js +152 -132
- package/dist/src/cron/validation.js.map +1 -1
- package/dist/src/cron/workflow-run-completion.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 +2 -2
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/install.js +2 -2
- package/dist/src/extensions/loader.js +2 -2
- package/dist/src/extensions/lockfile.js +3 -3
- package/dist/src/extensions/security.js +1 -1
- package/dist/src/extensions/update.js +2 -2
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/chat-stream/mapper.d.ts +1 -0
- package/dist/src/gateway/chat-stream/mapper.js +6 -2
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +2 -0
- package/dist/src/gateway/file-path-classifier.js +2 -2
- package/dist/src/gateway/hono/lib/config-payload.d.ts +0 -1
- package/dist/src/gateway/hono/lib/config-payload.js +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/oauth-async.js +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/ai-assist.d.ts +3 -0
- package/dist/src/gateway/hono/routes/ai-assist.js +66 -0
- package/dist/src/gateway/hono/routes/ai-assist.js.map +1 -0
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/commands-skills.js +7 -0
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
- package/dist/src/gateway/hono/routes/cron.js +24 -5
- package/dist/src/gateway/hono/routes/cron.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +1 -1
- package/dist/src/gateway/hono/routes/home.js +5 -3
- package/dist/src/gateway/hono/routes/home.js.map +1 -1
- package/dist/src/gateway/hono/routes/host-fs.js +2 -2
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- 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/shares.js +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +2 -2
- package/dist/src/gateway/hono/routes/workspace.js +2 -2
- 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/config-coordinator.js +1 -1
- package/dist/src/gateway/service/marketplace-service.d.ts +22 -10
- package/dist/src/gateway/service/marketplace-service.js +46 -5
- package/dist/src/gateway/service/marketplace-service.js.map +1 -1
- package/dist/src/gateway/service.js +11 -5
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.js +1 -1
- package/dist/src/goals/goal-notifications.js +1 -1
- package/dist/src/goals/goal-workflow-judge.js +3 -3
- package/dist/src/i18n/goals-bundle.js +1 -1
- package/dist/src/i18n/index.js +1 -1
- package/dist/src/i18n/share-tool-bundle.js +2 -2
- 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 +1 -1
- 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 +2 -2
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/infra/update-runner.js +1 -1
- package/dist/src/infra/update-startup.js +1 -1
- package/dist/src/infra/write-file-atomic.js +2 -2
- package/dist/src/media/store.js +1 -1
- package/dist/src/notes/agent-context.js +1 -1
- package/dist/src/notes/service.js +1 -1
- package/dist/src/notes/store.js +2 -2
- package/dist/src/project-trust/trust-store.js +2 -2
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/index.js +1 -1
- package/dist/src/providers/model-registry.js +4 -4
- package/dist/src/session/init-session-turn.js +2 -2
- package/dist/src/session/resolve-session.js +3 -3
- package/dist/src/session/session-title.js +2 -2
- package/dist/src/session/store-reader.d.ts +5 -0
- package/dist/src/session/store-reader.js +1 -0
- package/dist/src/session/store.js +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/storage/sqlite/connection.js +1 -1
- package/dist/src/storage/sqlite/cron-job-repository.d.ts +6 -0
- package/dist/src/storage/sqlite/cron-job-repository.js +70 -0
- package/dist/src/storage/sqlite/cron-job-repository.js.map +1 -0
- package/dist/src/storage/sqlite/cron-run-repository.js +0 -1
- package/dist/src/storage/sqlite/cron-run-repository.js.map +1 -1
- package/dist/src/storage/sqlite/index.d.ts +1 -0
- package/dist/src/storage/sqlite/index.js +2 -1
- package/dist/src/storage/sqlite/memory-index-repository.js +2 -2
- package/dist/src/storage/sqlite/migrations/009_cron_jobs.sql +24 -0
- package/dist/src/storage/sqlite/migrations/discover.js +1 -1
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +4 -4
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +2 -2
- package/dist/src/storage/sqlite/schema.sql +25 -0
- package/dist/src/tui/clipboard-image.js +3 -3
- package/dist/src/tui/extension-host/load-extensions.js +1 -1
- package/dist/src/tui/theme-manager.js +1 -1
- package/dist/src/tui/tui-command-formatters.js +4 -4
- package/dist/src/tui/tui-commands.js +2 -2
- 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-startup-resources.js +1 -1
- package/dist/src/tui/tui.js +6 -6
- package/dist/src/tunnel/frpc-binary.js +4 -4
- 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/utils/logger/streams.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/draft/workflow-draft-service.js +1 -1
- package/dist/src/workflows/engine/projector.js +23 -14
- package/dist/src/workflows/engine/projector.js.map +1 -1
- package/dist/src/workflows/engine/workflow-engine.d.ts +1 -0
- package/dist/src/workflows/engine/workflow-engine.js +36 -0
- package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
- package/dist/src/workflows/index.js +1 -1
- package/dist/src/workflows/service/workflow-run-service.js +2 -2
- package/dist/src/workflows/service/workflow-session-bridge.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 +2 -3
- package/dist/gateway/static/root/assets/agent-browser-settings-page-B-0zGjt8.js +0 -6
- package/dist/gateway/static/root/assets/agents-DwDnJlXT.js +0 -71
- package/dist/gateway/static/root/assets/connectors-settings-DDvHo9in.js +0 -2
- package/dist/gateway/static/root/assets/cron-api-Dj_UZKST.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-Dv7GII9G.js +0 -1
- package/dist/gateway/static/root/assets/cron-schedule-picker-DH10KgD6.js +0 -1
- package/dist/gateway/static/root/assets/dreaming-settings-L2rgZDQl.js +0 -1
- package/dist/gateway/static/root/assets/extension-settings-page-m_UQYkVc.js +0 -1
- package/dist/gateway/static/root/assets/gateway-settings-CS177xni.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-C0FujUB5.js +0 -2
- package/dist/gateway/static/root/assets/index-COkF54xv.js +0 -121
- package/dist/gateway/static/root/assets/index-DIaSNGEO.css +0 -1
- package/dist/gateway/static/root/assets/interval-presets-B29Ss_2y.js +0 -2
- package/dist/gateway/static/root/assets/messages-Csef1aW7.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-DHGiTIvf.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-BM7zlDiT.js +0 -2
- package/dist/gateway/static/root/assets/schedule-field-BPAWui3P.js +0 -1
- package/dist/gateway/static/root/assets/schedule-summary-B-hdSRdR.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BYS-rpzn.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-BsrK7TBF.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-DJiksNu4.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-CU9i0nJV.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-ERbdWENm.js +0 -2
- package/dist/gateway/static/root/assets/use-agent-defaults-form-D3FGVLZb.js +0 -1
- package/dist/gateway/static/root/favicon-16.png +0 -0
- package/dist/gateway/static/root/favicon-32.png +0 -0
- package/dist/src/cron/persistence.d.ts +0 -62
- package/dist/src/cron/persistence.js +0 -188
- package/dist/src/cron/persistence.js.map +0 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { resolveStateDir } from "../../config/paths-state.js";
|
|
2
|
+
import { init_paths, resolveSkillsDir } from "../../config/paths.js";
|
|
3
|
+
import { createLogger } from "../../utils/logger/index.js";
|
|
4
|
+
import { init_logger } from "../../utils/logger.js";
|
|
5
|
+
import { createSkillConfigManager } from "./config.js";
|
|
6
|
+
import { isManagedSkillTransientDirName } from "./managed-store.js";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import chokidar from "chokidar";
|
|
9
|
+
//#region src/agent/skills/filesystem-watcher.ts
|
|
10
|
+
init_paths();
|
|
11
|
+
init_logger();
|
|
12
|
+
const log = createLogger("SkillFilesystemWatcher");
|
|
13
|
+
const DEFAULT_DEBOUNCE_MS = 1e3;
|
|
14
|
+
const WATCH_DEPTH = 8;
|
|
15
|
+
const IGNORED_SEGMENTS = new Set([
|
|
16
|
+
".git",
|
|
17
|
+
"node_modules",
|
|
18
|
+
"dist",
|
|
19
|
+
"build",
|
|
20
|
+
".next",
|
|
21
|
+
".nuxt",
|
|
22
|
+
".venv",
|
|
23
|
+
"venv",
|
|
24
|
+
"__pycache__",
|
|
25
|
+
".cache",
|
|
26
|
+
".mypy_cache",
|
|
27
|
+
".pytest_cache"
|
|
28
|
+
]);
|
|
29
|
+
function normalizePath(p) {
|
|
30
|
+
return path.resolve(p);
|
|
31
|
+
}
|
|
32
|
+
function pathSegments(p) {
|
|
33
|
+
return p.split(/[\\/]+/).filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
function hasIgnoredSegment(p) {
|
|
36
|
+
return pathSegments(p).some((segment) => IGNORED_SEGMENTS.has(segment) || isManagedSkillTransientDirName(segment));
|
|
37
|
+
}
|
|
38
|
+
function isSameOrInside(parent, child) {
|
|
39
|
+
const rel = path.relative(parent, child);
|
|
40
|
+
return rel === "" || !!rel && !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
41
|
+
}
|
|
42
|
+
function isWorkspaceSkillPath(workspaceRoot, candidate) {
|
|
43
|
+
const skillsRoot = path.join(workspaceRoot, "skills");
|
|
44
|
+
return candidate === workspaceRoot || isSameOrInside(skillsRoot, candidate);
|
|
45
|
+
}
|
|
46
|
+
function isWorkspaceSkillsRoot(workspaceRoot, candidate) {
|
|
47
|
+
return candidate === path.join(workspaceRoot, "skills");
|
|
48
|
+
}
|
|
49
|
+
function isSkillMdPath(candidate) {
|
|
50
|
+
return path.basename(candidate).toLowerCase() === "skill.md";
|
|
51
|
+
}
|
|
52
|
+
function readWatcherConfig() {
|
|
53
|
+
const cfg = createSkillConfigManager(resolveStateDir()).load();
|
|
54
|
+
const rawDebounce = cfg.load?.watchDebounceMs;
|
|
55
|
+
return {
|
|
56
|
+
enabled: cfg.load?.watch !== false,
|
|
57
|
+
debounceMs: typeof rawDebounce === "number" && Number.isFinite(rawDebounce) ? Math.max(0, rawDebounce) : DEFAULT_DEBOUNCE_MS
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
var SkillFilesystemWatcher = class {
|
|
61
|
+
targets = /* @__PURE__ */ new Map();
|
|
62
|
+
onChange;
|
|
63
|
+
disposed = false;
|
|
64
|
+
constructor(options) {
|
|
65
|
+
this.onChange = options.onChange;
|
|
66
|
+
this.watchGlobalSkillsDir();
|
|
67
|
+
}
|
|
68
|
+
watchGlobalSkillsDir() {
|
|
69
|
+
this.watchRoot(resolveSkillsDir(), "global");
|
|
70
|
+
}
|
|
71
|
+
watchWorkspace(workspaceDir) {
|
|
72
|
+
const resolved = normalizePath(workspaceDir);
|
|
73
|
+
if (!resolved) return;
|
|
74
|
+
this.watchRoot(resolved, "workspace");
|
|
75
|
+
}
|
|
76
|
+
refreshPrimaryWorkspace(workspaceDir) {
|
|
77
|
+
this.watchGlobalSkillsDir();
|
|
78
|
+
this.watchWorkspace(workspaceDir);
|
|
79
|
+
}
|
|
80
|
+
dispose() {
|
|
81
|
+
this.disposed = true;
|
|
82
|
+
for (const target of this.targets.values()) {
|
|
83
|
+
if (target.timer) clearTimeout(target.timer);
|
|
84
|
+
target.watcher.close().catch((err) => {
|
|
85
|
+
log.warn({
|
|
86
|
+
err,
|
|
87
|
+
root: target.root
|
|
88
|
+
}, "Skill watcher close failed");
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
this.targets.clear();
|
|
92
|
+
}
|
|
93
|
+
watchRoot(root, kind) {
|
|
94
|
+
if (this.disposed) return;
|
|
95
|
+
const { enabled, debounceMs } = readWatcherConfig();
|
|
96
|
+
const resolvedRoot = normalizePath(root);
|
|
97
|
+
const key = `${kind}:${resolvedRoot}`;
|
|
98
|
+
if (!enabled) {
|
|
99
|
+
this.unwatch(key);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (this.targets.get(key)) return;
|
|
103
|
+
const ignored = (watchPath, stats) => {
|
|
104
|
+
const candidate = normalizePath(watchPath);
|
|
105
|
+
if (candidate === resolvedRoot) return false;
|
|
106
|
+
if (hasIgnoredSegment(candidate)) return true;
|
|
107
|
+
if (kind === "workspace" && !isWorkspaceSkillPath(resolvedRoot, candidate)) return true;
|
|
108
|
+
if (kind === "workspace" && isWorkspaceSkillsRoot(resolvedRoot, candidate)) return false;
|
|
109
|
+
if (!stats) return false;
|
|
110
|
+
if (stats?.isDirectory?.()) return false;
|
|
111
|
+
return !isSkillMdPath(candidate);
|
|
112
|
+
};
|
|
113
|
+
const watcher = chokidar.watch(resolvedRoot, {
|
|
114
|
+
ignoreInitial: true,
|
|
115
|
+
followSymlinks: false,
|
|
116
|
+
depth: WATCH_DEPTH,
|
|
117
|
+
awaitWriteFinish: {
|
|
118
|
+
stabilityThreshold: debounceMs,
|
|
119
|
+
pollInterval: 100
|
|
120
|
+
},
|
|
121
|
+
ignored
|
|
122
|
+
});
|
|
123
|
+
const target = {
|
|
124
|
+
key,
|
|
125
|
+
root: resolvedRoot,
|
|
126
|
+
kind,
|
|
127
|
+
watcher
|
|
128
|
+
};
|
|
129
|
+
const schedule = (changedPath) => this.scheduleRefresh(target, changedPath);
|
|
130
|
+
watcher.on("addDir", schedule);
|
|
131
|
+
watcher.on("add", schedule);
|
|
132
|
+
watcher.on("change", schedule);
|
|
133
|
+
watcher.on("unlink", schedule);
|
|
134
|
+
watcher.on("unlinkDir", schedule);
|
|
135
|
+
watcher.on("error", (err) => {
|
|
136
|
+
log.warn({
|
|
137
|
+
err,
|
|
138
|
+
root: resolvedRoot,
|
|
139
|
+
kind
|
|
140
|
+
}, "Skill watcher error");
|
|
141
|
+
});
|
|
142
|
+
this.targets.set(key, target);
|
|
143
|
+
log.info({
|
|
144
|
+
root: resolvedRoot,
|
|
145
|
+
kind
|
|
146
|
+
}, "Watching skill filesystem");
|
|
147
|
+
}
|
|
148
|
+
unwatch(key) {
|
|
149
|
+
const target = this.targets.get(key);
|
|
150
|
+
if (!target) return;
|
|
151
|
+
if (target.timer) clearTimeout(target.timer);
|
|
152
|
+
target.watcher.close().catch((err) => {
|
|
153
|
+
log.warn({
|
|
154
|
+
err,
|
|
155
|
+
root: target.root
|
|
156
|
+
}, "Skill watcher close failed");
|
|
157
|
+
});
|
|
158
|
+
this.targets.delete(key);
|
|
159
|
+
}
|
|
160
|
+
scheduleRefresh(target, changedPath) {
|
|
161
|
+
const { enabled, debounceMs } = readWatcherConfig();
|
|
162
|
+
if (!enabled) {
|
|
163
|
+
this.unwatch(target.key);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
target.pendingPath = changedPath ?? target.pendingPath;
|
|
167
|
+
if (target.timer) clearTimeout(target.timer);
|
|
168
|
+
target.timer = setTimeout(() => {
|
|
169
|
+
target.timer = void 0;
|
|
170
|
+
const pendingPath = target.pendingPath;
|
|
171
|
+
target.pendingPath = void 0;
|
|
172
|
+
this.onChange({
|
|
173
|
+
reason: "watch",
|
|
174
|
+
changedPath: pendingPath
|
|
175
|
+
});
|
|
176
|
+
}, debounceMs);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
//#endregion
|
|
180
|
+
export { SkillFilesystemWatcher };
|
|
181
|
+
|
|
182
|
+
//# sourceMappingURL=filesystem-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-watcher.js","names":[],"sources":["../../../../src/agent/skills/filesystem-watcher.ts"],"sourcesContent":["import path from 'node:path';\n\nimport chokidar, { type FSWatcher } from 'chokidar';\n\nimport { resolveSkillsDir, resolveStateDir } from '../../config/paths.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { createSkillConfigManager } from './config.js';\nimport { isManagedSkillTransientDirName } from './managed-store.js';\n\nconst log = createLogger('SkillFilesystemWatcher');\n\nexport interface SkillFilesystemWatcherOptions {\n onChange: (event: { reason: 'watch'; changedPath?: string }) => void;\n}\n\ntype WatchTarget = {\n key: string;\n root: string;\n kind: 'global' | 'workspace';\n watcher: FSWatcher;\n timer?: NodeJS.Timeout;\n pendingPath?: string;\n};\n\nconst DEFAULT_DEBOUNCE_MS = 1000;\nconst WATCH_DEPTH = 8;\nconst IGNORED_SEGMENTS = new Set([\n '.git',\n 'node_modules',\n 'dist',\n 'build',\n '.next',\n '.nuxt',\n '.venv',\n 'venv',\n '__pycache__',\n '.cache',\n '.mypy_cache',\n '.pytest_cache',\n]);\n\nfunction normalizePath(p: string): string {\n return path.resolve(p);\n}\n\nfunction pathSegments(p: string): string[] {\n return p.split(/[\\\\/]+/).filter(Boolean);\n}\n\nfunction hasIgnoredSegment(p: string): boolean {\n return pathSegments(p).some(\n (segment) => IGNORED_SEGMENTS.has(segment) || isManagedSkillTransientDirName(segment),\n );\n}\n\nfunction isSameOrInside(parent: string, child: string): boolean {\n const rel = path.relative(parent, child);\n return rel === '' || (!!rel && !rel.startsWith('..') && !path.isAbsolute(rel));\n}\n\nfunction isWorkspaceSkillPath(workspaceRoot: string, candidate: string): boolean {\n const skillsRoot = path.join(workspaceRoot, 'skills');\n return candidate === workspaceRoot || isSameOrInside(skillsRoot, candidate);\n}\n\nfunction isWorkspaceSkillsRoot(workspaceRoot: string, candidate: string): boolean {\n return candidate === path.join(workspaceRoot, 'skills');\n}\n\nfunction isSkillMdPath(candidate: string): boolean {\n return path.basename(candidate).toLowerCase() === 'skill.md';\n}\n\nfunction readWatcherConfig(): { enabled: boolean; debounceMs: number } {\n const cfg = createSkillConfigManager(resolveStateDir()).load();\n const rawDebounce = cfg.load?.watchDebounceMs;\n return {\n enabled: cfg.load?.watch !== false,\n debounceMs:\n typeof rawDebounce === 'number' && Number.isFinite(rawDebounce)\n ? Math.max(0, rawDebounce)\n : DEFAULT_DEBOUNCE_MS,\n };\n}\n\nexport class SkillFilesystemWatcher {\n private readonly targets = new Map<string, WatchTarget>();\n private readonly onChange: SkillFilesystemWatcherOptions['onChange'];\n private disposed = false;\n\n constructor(options: SkillFilesystemWatcherOptions) {\n this.onChange = options.onChange;\n this.watchGlobalSkillsDir();\n }\n\n watchGlobalSkillsDir(): void {\n this.watchRoot(resolveSkillsDir(), 'global');\n }\n\n watchWorkspace(workspaceDir: string): void {\n const resolved = normalizePath(workspaceDir);\n if (!resolved) return;\n this.watchRoot(resolved, 'workspace');\n }\n\n refreshPrimaryWorkspace(workspaceDir: string): void {\n this.watchGlobalSkillsDir();\n this.watchWorkspace(workspaceDir);\n }\n\n dispose(): void {\n this.disposed = true;\n for (const target of this.targets.values()) {\n if (target.timer) clearTimeout(target.timer);\n void target.watcher.close().catch((err) => {\n log.warn({ err, root: target.root }, 'Skill watcher close failed');\n });\n }\n this.targets.clear();\n }\n\n private watchRoot(root: string, kind: WatchTarget['kind']): void {\n if (this.disposed) return;\n const { enabled, debounceMs } = readWatcherConfig();\n const resolvedRoot = normalizePath(root);\n const key = `${kind}:${resolvedRoot}`;\n\n if (!enabled) {\n this.unwatch(key);\n return;\n }\n\n const existing = this.targets.get(key);\n if (existing) return;\n\n const ignored = (watchPath: string, stats?: { isDirectory?: () => boolean }) => {\n const candidate = normalizePath(watchPath);\n if (candidate === resolvedRoot) return false;\n if (hasIgnoredSegment(candidate)) return true;\n\n if (kind === 'workspace' && !isWorkspaceSkillPath(resolvedRoot, candidate)) {\n return true;\n }\n\n if (kind === 'workspace' && isWorkspaceSkillsRoot(resolvedRoot, candidate)) return false;\n if (!stats) return false;\n if (stats?.isDirectory?.()) return false;\n return !isSkillMdPath(candidate);\n };\n\n const watcher = chokidar.watch(resolvedRoot, {\n ignoreInitial: true,\n followSymlinks: false,\n depth: WATCH_DEPTH,\n awaitWriteFinish: {\n stabilityThreshold: debounceMs,\n pollInterval: 100,\n },\n ignored,\n });\n\n const target: WatchTarget = { key, root: resolvedRoot, kind, watcher };\n const schedule = (changedPath?: string) => this.scheduleRefresh(target, changedPath);\n\n watcher.on('addDir', schedule);\n watcher.on('add', schedule);\n watcher.on('change', schedule);\n watcher.on('unlink', schedule);\n watcher.on('unlinkDir', schedule);\n watcher.on('error', (err) => {\n log.warn({ err, root: resolvedRoot, kind }, 'Skill watcher error');\n });\n\n this.targets.set(key, target);\n log.info({ root: resolvedRoot, kind }, 'Watching skill filesystem');\n }\n\n private unwatch(key: string): void {\n const target = this.targets.get(key);\n if (!target) return;\n if (target.timer) clearTimeout(target.timer);\n void target.watcher.close().catch((err) => {\n log.warn({ err, root: target.root }, 'Skill watcher close failed');\n });\n this.targets.delete(key);\n }\n\n private scheduleRefresh(target: WatchTarget, changedPath?: string): void {\n const { enabled, debounceMs } = readWatcherConfig();\n if (!enabled) {\n this.unwatch(target.key);\n return;\n }\n\n target.pendingPath = changedPath ?? target.pendingPath;\n if (target.timer) clearTimeout(target.timer);\n target.timer = setTimeout(() => {\n target.timer = undefined;\n const pendingPath = target.pendingPath;\n target.pendingPath = undefined;\n this.onChange({ reason: 'watch', changedPath: pendingPath });\n }, debounceMs);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { join, relative } from "node:path";
|
|
2
|
-
import { createReadStream, existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { createReadStream, existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
4
|
//#region src/agent/skills/hub-hash.ts
|
|
5
5
|
/**
|
|
6
6
|
* Deterministic content hash for a skill directory (for hub lock / drift detection).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { init_paths, resolveSkillsLockPath } from "../../config/paths.js";
|
|
2
1
|
import { init_write_file_atomic, writeTextAtomicSync } from "../../infra/write-file-atomic.js";
|
|
2
|
+
import { init_paths, resolveSkillsLockPath } from "../../config/paths.js";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
//#region src/agent/skills/hub-lock.ts
|
|
5
5
|
/**
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { init_paths, resolveSkillsDir } from "../../config/paths.js";
|
|
2
1
|
import { parseFrontmatter } from "../../markdown/frontmatter.js";
|
|
3
2
|
import { formatScanSummary, scanSkillDirectory } from "./scanner.js";
|
|
4
3
|
import { getSkillsLockEntry, recordSkillsHubInstall } from "./hub-lock.js";
|
|
5
|
-
import { installSkillFromZip, isValidSkillId } from "./managed-store.js";
|
|
4
|
+
import { installSkillFromZip, isValidSkillId, prepareManagedSkillTempDir, promoteManagedSkillTempDir } from "./managed-store.js";
|
|
6
5
|
import { computeSkillTreeHashSync } from "./hub-hash.js";
|
|
7
|
-
import { tmpdir } from "node:os";
|
|
8
|
-
import { basename, join, normalize, resolve, sep } from "node:path";
|
|
9
6
|
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { basename, join, normalize, resolve } from "node:path";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
10
9
|
import { fileURLToPath } from "node:url";
|
|
11
10
|
import { execFileSync } from "node:child_process";
|
|
12
11
|
import { fetch } from "undici";
|
|
@@ -14,7 +13,6 @@ import { fetch } from "undici";
|
|
|
14
13
|
/**
|
|
15
14
|
* CLI / hub: install or update skills from git or archive (zip / tar.gz) into ~/.xopc/skills.
|
|
16
15
|
*/
|
|
17
|
-
init_paths();
|
|
18
16
|
function classifyHubSource(raw) {
|
|
19
17
|
const s = raw.trim();
|
|
20
18
|
const lower = s.toLowerCase();
|
|
@@ -70,42 +68,38 @@ async function copySkillTreeToManaged(skillRoot, ctx) {
|
|
|
70
68
|
const targetId = ctx.skillId?.trim() || inferSkillIdFromSkillMd(skillRoot);
|
|
71
69
|
if (!targetId) throw new Error("Could not determine skill id: pass --id <name> (must match managed id rules)");
|
|
72
70
|
if (!isValidSkillId(targetId)) throw new Error(`Invalid skill id "${targetId}" (letters, digits, ._-; max 63 chars after first)`);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
force: true
|
|
71
|
+
const { destDir, tempDir } = prepareManagedSkillTempDir(targetId);
|
|
72
|
+
try {
|
|
73
|
+
if (existsSync(destDir) && !ctx.force) throw new Error(`Skill "${targetId}" already exists. Use --force to replace.`);
|
|
74
|
+
cpSync(skillRoot, tempDir, { recursive: true });
|
|
75
|
+
if (!existsSync(join(tempDir, "SKILL.md"))) throw new Error("Installed tree is missing SKILL.md");
|
|
76
|
+
await runScan(tempDir, ctx.strictScan);
|
|
77
|
+
const promoted = promoteManagedSkillTempDir({
|
|
78
|
+
skillId: targetId,
|
|
79
|
+
tempDir,
|
|
80
|
+
overwrite: ctx.force
|
|
84
81
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
const hash = computeSkillTreeHashSync(promoted.path);
|
|
83
|
+
recordSkillsHubInstall(targetId, {
|
|
84
|
+
kind: ctx.kind,
|
|
85
|
+
source: ctx.source,
|
|
86
|
+
ref: ctx.kind === "git" ? ctx.ref : void 0,
|
|
87
|
+
subpath: ctx.kind === "git" ? ctx.subpath : void 0
|
|
88
|
+
}, hash);
|
|
89
|
+
return {
|
|
90
|
+
skillId: targetId,
|
|
91
|
+
path: promoted.path,
|
|
92
|
+
contentHash: hash,
|
|
93
|
+
kind: ctx.kind,
|
|
94
|
+
source: ctx.source
|
|
95
|
+
};
|
|
96
|
+
} catch (err) {
|
|
97
|
+
rmSync(tempDir, {
|
|
89
98
|
recursive: true,
|
|
90
99
|
force: true
|
|
91
100
|
});
|
|
92
|
-
throw
|
|
101
|
+
throw err;
|
|
93
102
|
}
|
|
94
|
-
const hash = computeSkillTreeHashSync(dest);
|
|
95
|
-
recordSkillsHubInstall(targetId, {
|
|
96
|
-
kind: ctx.kind,
|
|
97
|
-
source: ctx.source,
|
|
98
|
-
ref: ctx.kind === "git" ? ctx.ref : void 0,
|
|
99
|
-
subpath: ctx.kind === "git" ? ctx.subpath : void 0
|
|
100
|
-
}, hash);
|
|
101
|
-
await runScan(dest, ctx.strictScan);
|
|
102
|
-
return {
|
|
103
|
-
skillId: targetId,
|
|
104
|
-
path: dest,
|
|
105
|
-
contentHash: hash,
|
|
106
|
-
kind: ctx.kind,
|
|
107
|
-
source: ctx.source
|
|
108
|
-
};
|
|
109
103
|
}
|
|
110
104
|
async function pullSkillFromZipBuffer(buf, sourceLabel, options) {
|
|
111
105
|
const r = installSkillFromZip(buf, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hub-pull.js","names":[],"sources":["../../../../src/agent/skills/hub-pull.ts"],"sourcesContent":["/**\n * CLI / hub: install or update skills from git or archive (zip / tar.gz) into ~/.xopc/skills.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n cpSync,\n existsSync,\n mkdtempSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { basename, join, normalize, resolve, sep } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { fileURLToPath } from 'node:url';\n\nimport { fetch } from 'undici';\n\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\nimport { resolveSkillsDir } from '../../config/paths.js';\nimport { computeSkillTreeHashSync } from './hub-hash.js';\nimport { getSkillsLockEntry, recordSkillsHubInstall } from './hub-lock.js';\nimport type { SkillHubKind } from './hub-lock.js';\nimport { installSkillFromZip, isValidSkillId } from './managed-store.js';\nimport { formatScanSummary, scanSkillDirectory } from './scanner.js';\n\nexport interface HubPullOptions {\n skillId?: string;\n ref?: string;\n subpath?: string;\n force?: boolean;\n /** When true, critical scanner findings fail the install. */\n strictScan?: boolean;\n}\n\nexport interface HubPullResult {\n skillId: string;\n path: string;\n contentHash: string;\n kind: SkillHubKind;\n source: string;\n}\n\nexport function classifyHubSource(raw: string): 'git' | 'archive' {\n const s = raw.trim();\n const lower = s.toLowerCase();\n\n try {\n if (s.startsWith('file:')) {\n const p = fileURLToPath(s);\n const pl = p.toLowerCase();\n if (pl.endsWith('.zip')) return 'archive';\n if (pl.endsWith('.tar.gz') || pl.endsWith('.tgz')) return 'archive';\n return 'git';\n }\n } catch {\n // ignore invalid file URL\n }\n\n if (/^[a-zA-Z]:[\\\\/]/.test(s) || (s.startsWith('/') && !s.startsWith('//'))) {\n if (lower.endsWith('.zip')) return 'archive';\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return 'archive';\n return 'git';\n }\n\n try {\n const u = new URL(s);\n const path = u.pathname.toLowerCase();\n if (path.endsWith('.zip')) return 'archive';\n if (path.endsWith('.tar.gz') || path.endsWith('.tgz')) return 'archive';\n } catch {\n // not a URL\n }\n\n if (lower.endsWith('.zip')) return 'archive';\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return 'archive';\n return 'git';\n}\n\nexport function findSkillRoot(repoRoot: string, subpath?: string): string {\n const sub = subpath?.trim()\n ? normalize(subpath.trim()).replace(/^[/\\\\]+/, '')\n : '';\n const base = sub ? resolve(repoRoot, sub) : resolve(repoRoot);\n if (!existsSync(base)) {\n throw new Error(`Path not found in source: ${sub || '.'}`);\n }\n if (existsSync(join(base, 'SKILL.md'))) {\n return base;\n }\n const entries = readdirSync(base, { withFileTypes: true }).filter((e) => e.isDirectory());\n if (entries.length === 1) {\n const one = join(base, entries[0].name);\n if (existsSync(join(one, 'SKILL.md'))) {\n return one;\n }\n }\n throw new Error(\n 'Could not locate SKILL.md (expected at root, under --path, or in a single child folder)',\n );\n}\n\nfunction inferSkillIdFromSkillMd(skillRoot: string): string {\n const raw = readFileSync(join(skillRoot, 'SKILL.md'), 'utf-8');\n const { frontmatter } = parseFrontmatter(raw);\n const n = String(frontmatter.name || '').trim();\n if (n && isValidSkillId(n)) {\n return n;\n }\n const dirName = basename(skillRoot);\n if (isValidSkillId(dirName)) {\n return dirName;\n }\n return '';\n}\n\nasync function runScan(dir: string, strict: boolean | undefined): Promise<void> {\n const summary = await scanSkillDirectory(dir);\n const label = basename(dir);\n if (summary.critical > 0 && strict) {\n throw new Error(\n `Security scan reported ${summary.critical} critical finding(s). ${formatScanSummary(summary, label)}`,\n );\n }\n if (summary.critical > 0 || summary.warn > 0) {\n console.warn(formatScanSummary(summary, label));\n }\n}\n\nasync function copySkillTreeToManaged(\n skillRoot: string,\n ctx: HubPullOptions & { kind: SkillHubKind; source: string },\n): Promise<HubPullResult> {\n const targetId = ctx.skillId?.trim() || inferSkillIdFromSkillMd(skillRoot);\n if (!targetId) {\n throw new Error('Could not determine skill id: pass --id <name> (must match managed id rules)');\n }\n if (!isValidSkillId(targetId)) {\n throw new Error(`Invalid skill id \"${targetId}\" (letters, digits, ._-; max 63 chars after first)`);\n }\n\n const rootDir = resolveSkillsDir();\n mkdirSync(rootDir, { recursive: true });\n const dest = join(rootDir, targetId);\n const destResolved = resolve(dest);\n const rootResolved = resolve(rootDir);\n if (!destResolved.startsWith(rootResolved + sep) && destResolved !== rootResolved) {\n throw new Error('Invalid destination path');\n }\n\n if (existsSync(dest)) {\n if (!ctx.force) {\n throw new Error(`Skill \"${targetId}\" already exists. Use --force to replace.`);\n }\n rmSync(dest, { recursive: true, force: true });\n }\n\n cpSync(skillRoot, dest, { recursive: true });\n\n if (!existsSync(join(dest, 'SKILL.md'))) {\n rmSync(dest, { recursive: true, force: true });\n throw new Error('Installed tree is missing SKILL.md');\n }\n\n const hash = computeSkillTreeHashSync(dest);\n recordSkillsHubInstall(\n targetId,\n {\n kind: ctx.kind,\n source: ctx.source,\n ref: ctx.kind === 'git' ? ctx.ref : undefined,\n subpath: ctx.kind === 'git' ? ctx.subpath : undefined,\n },\n hash,\n );\n await runScan(dest, ctx.strictScan);\n\n return {\n skillId: targetId,\n path: dest,\n contentHash: hash,\n kind: ctx.kind,\n source: ctx.source,\n };\n}\n\nasync function pullSkillFromZipBuffer(\n buf: Buffer,\n sourceLabel: string,\n options: HubPullOptions,\n): Promise<HubPullResult> {\n const r = installSkillFromZip(buf, {\n skillId: options.skillId,\n overwrite: options.force ?? false,\n });\n const hash = computeSkillTreeHashSync(r.path);\n recordSkillsHubInstall(r.skillId, { kind: 'archive', source: sourceLabel }, hash);\n await runScan(r.path, options.strictScan);\n return {\n skillId: r.skillId,\n path: r.path,\n contentHash: hash,\n kind: 'archive',\n source: sourceLabel,\n };\n}\n\nasync function pullSkillFromTarFile(\n archivePath: string,\n sourceLabel: string,\n options: HubPullOptions,\n): Promise<HubPullResult> {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-tar-'));\n try {\n execFileSync('tar', ['-xzf', archivePath, '-C', tmpRoot], {\n stdio: 'pipe',\n encoding: 'utf-8',\n });\n const skillRoot = findSkillRoot(tmpRoot);\n return await copySkillTreeToManaged(skillRoot, { ...options, kind: 'archive', source: sourceLabel });\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function pullSkillFromLocalPath(absPath: string, options: HubPullOptions): Promise<HubPullResult> {\n const resolved = resolve(absPath);\n if (!existsSync(resolved)) {\n throw new Error(`Not found: ${resolved}`);\n }\n const st = statSync(resolved);\n if (!st.isFile()) {\n throw new Error('Expected a path to a .zip or .tar.gz file (directories are not supported here)');\n }\n const lower = resolved.toLowerCase();\n if (lower.endsWith('.zip')) {\n const buf = readFileSync(resolved);\n return pullSkillFromZipBuffer(buf, resolved, options);\n }\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) {\n return pullSkillFromTarFile(resolved, resolved, options);\n }\n throw new Error('Unsupported file type (expected .zip, .tar.gz, or .tgz)');\n}\n\nasync function pullSkillFromGit(url: string, options: HubPullOptions): Promise<HubPullResult> {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-hub-'));\n const cloneDir = join(tmpRoot, 'clone');\n try {\n const args = ['clone', '--depth', '1'];\n if (options.ref?.trim()) {\n args.push('--branch', options.ref.trim());\n }\n args.push(url, cloneDir);\n try {\n execFileSync('git', args, { stdio: 'pipe', encoding: 'utf-8' });\n } catch {\n if (options.ref?.trim()) {\n execFileSync('git', ['clone', '--depth', '1', url, cloneDir], {\n stdio: 'pipe',\n encoding: 'utf-8',\n });\n } else {\n throw new Error(`git clone failed for ${url}`);\n }\n }\n const skillRoot = findSkillRoot(cloneDir, options.subpath);\n return await copySkillTreeToManaged(skillRoot, { ...options, kind: 'git', source: url });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.includes('ENOENT') || msg.includes('git clone failed')) {\n throw new Error(\n `Git operation failed (${msg}). Ensure git is installed and the repository URL is valid.`,\n );\n }\n throw e;\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n}\n\n/**\n * Install or replace a skill from a git URL, http(s) archive URL, file:// URL, or local archive path.\n */\nexport async function pullSkillFromSource(\n source: string,\n options: HubPullOptions = {},\n): Promise<HubPullResult> {\n const trimmed = source.trim();\n if (!trimmed) {\n throw new Error('Missing source URL or path');\n }\n\n if (trimmed.startsWith('file:')) {\n return pullSkillFromLocalPath(fileURLToPath(trimmed), options);\n }\n\n if (!/^[a-zA-Z][a-zA-Z+.-]*:\\/\\//.test(trimmed) && existsSync(trimmed)) {\n return pullSkillFromLocalPath(resolve(trimmed), options);\n }\n\n const kind = classifyHubSource(trimmed);\n if (kind === 'archive') {\n const res = await fetch(trimmed);\n if (!res.ok) {\n throw new Error(`HTTP ${res.status} while fetching archive`);\n }\n const buf = Buffer.from(await res.arrayBuffer());\n const lower = trimmed.toLowerCase();\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-fetch-tar-'));\n const f = join(tmpRoot, 'archive.tgz');\n try {\n mkdirSync(tmpRoot, { recursive: true });\n writeFileSync(f, buf);\n return await pullSkillFromTarFile(f, trimmed, options);\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n }\n return pullSkillFromZipBuffer(buf, trimmed, options);\n }\n\n return pullSkillFromGit(trimmed, options);\n}\n\n/** Re-install from the source recorded in ~/.xopc/skills-lock.json for this skill id. */\nexport async function updateSkillFromLock(\n skillId: string,\n options: Pick<HubPullOptions, 'strictScan'> = {},\n): Promise<HubPullResult> {\n const e = getSkillsLockEntry(skillId);\n if (!e) {\n throw new Error(\n `No hub lock entry for \"${skillId}\". Install with: xopc skills pull <git-or-archive-url>`,\n );\n }\n return pullSkillFromSource(e.source, {\n skillId,\n ref: e.ref,\n subpath: e.subpath,\n force: true,\n strictScan: options.strictScan,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hub-pull.js","names":[],"sources":["../../../../src/agent/skills/hub-pull.ts"],"sourcesContent":["/**\n * CLI / hub: install or update skills from git or archive (zip / tar.gz) into ~/.xopc/skills.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n cpSync,\n existsSync,\n mkdtempSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport { basename, join, normalize, resolve } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { fileURLToPath } from 'node:url';\n\nimport { fetch } from 'undici';\n\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\nimport { computeSkillTreeHashSync } from './hub-hash.js';\nimport { getSkillsLockEntry, recordSkillsHubInstall } from './hub-lock.js';\nimport type { SkillHubKind } from './hub-lock.js';\nimport {\n installSkillFromZip,\n isValidSkillId,\n prepareManagedSkillTempDir,\n promoteManagedSkillTempDir,\n} from './managed-store.js';\nimport { formatScanSummary, scanSkillDirectory } from './scanner.js';\n\nexport interface HubPullOptions {\n skillId?: string;\n ref?: string;\n subpath?: string;\n force?: boolean;\n /** When true, critical scanner findings fail the install. */\n strictScan?: boolean;\n}\n\nexport interface HubPullResult {\n skillId: string;\n path: string;\n contentHash: string;\n kind: SkillHubKind;\n source: string;\n}\n\nexport function classifyHubSource(raw: string): 'git' | 'archive' {\n const s = raw.trim();\n const lower = s.toLowerCase();\n\n try {\n if (s.startsWith('file:')) {\n const p = fileURLToPath(s);\n const pl = p.toLowerCase();\n if (pl.endsWith('.zip')) return 'archive';\n if (pl.endsWith('.tar.gz') || pl.endsWith('.tgz')) return 'archive';\n return 'git';\n }\n } catch {\n // ignore invalid file URL\n }\n\n if (/^[a-zA-Z]:[\\\\/]/.test(s) || (s.startsWith('/') && !s.startsWith('//'))) {\n if (lower.endsWith('.zip')) return 'archive';\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return 'archive';\n return 'git';\n }\n\n try {\n const u = new URL(s);\n const path = u.pathname.toLowerCase();\n if (path.endsWith('.zip')) return 'archive';\n if (path.endsWith('.tar.gz') || path.endsWith('.tgz')) return 'archive';\n } catch {\n // not a URL\n }\n\n if (lower.endsWith('.zip')) return 'archive';\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return 'archive';\n return 'git';\n}\n\nexport function findSkillRoot(repoRoot: string, subpath?: string): string {\n const sub = subpath?.trim()\n ? normalize(subpath.trim()).replace(/^[/\\\\]+/, '')\n : '';\n const base = sub ? resolve(repoRoot, sub) : resolve(repoRoot);\n if (!existsSync(base)) {\n throw new Error(`Path not found in source: ${sub || '.'}`);\n }\n if (existsSync(join(base, 'SKILL.md'))) {\n return base;\n }\n const entries = readdirSync(base, { withFileTypes: true }).filter((e) => e.isDirectory());\n if (entries.length === 1) {\n const one = join(base, entries[0].name);\n if (existsSync(join(one, 'SKILL.md'))) {\n return one;\n }\n }\n throw new Error(\n 'Could not locate SKILL.md (expected at root, under --path, or in a single child folder)',\n );\n}\n\nfunction inferSkillIdFromSkillMd(skillRoot: string): string {\n const raw = readFileSync(join(skillRoot, 'SKILL.md'), 'utf-8');\n const { frontmatter } = parseFrontmatter(raw);\n const n = String(frontmatter.name || '').trim();\n if (n && isValidSkillId(n)) {\n return n;\n }\n const dirName = basename(skillRoot);\n if (isValidSkillId(dirName)) {\n return dirName;\n }\n return '';\n}\n\nasync function runScan(dir: string, strict: boolean | undefined): Promise<void> {\n const summary = await scanSkillDirectory(dir);\n const label = basename(dir);\n if (summary.critical > 0 && strict) {\n throw new Error(\n `Security scan reported ${summary.critical} critical finding(s). ${formatScanSummary(summary, label)}`,\n );\n }\n if (summary.critical > 0 || summary.warn > 0) {\n console.warn(formatScanSummary(summary, label));\n }\n}\n\nasync function copySkillTreeToManaged(\n skillRoot: string,\n ctx: HubPullOptions & { kind: SkillHubKind; source: string },\n): Promise<HubPullResult> {\n const targetId = ctx.skillId?.trim() || inferSkillIdFromSkillMd(skillRoot);\n if (!targetId) {\n throw new Error('Could not determine skill id: pass --id <name> (must match managed id rules)');\n }\n if (!isValidSkillId(targetId)) {\n throw new Error(`Invalid skill id \"${targetId}\" (letters, digits, ._-; max 63 chars after first)`);\n }\n\n const { destDir, tempDir } = prepareManagedSkillTempDir(targetId);\n try {\n if (existsSync(destDir) && !ctx.force) {\n throw new Error(`Skill \"${targetId}\" already exists. Use --force to replace.`);\n }\n\n cpSync(skillRoot, tempDir, { recursive: true });\n\n if (!existsSync(join(tempDir, 'SKILL.md'))) {\n throw new Error('Installed tree is missing SKILL.md');\n }\n\n await runScan(tempDir, ctx.strictScan);\n const promoted = promoteManagedSkillTempDir({\n skillId: targetId,\n tempDir,\n overwrite: ctx.force,\n });\n const hash = computeSkillTreeHashSync(promoted.path);\n recordSkillsHubInstall(\n targetId,\n {\n kind: ctx.kind,\n source: ctx.source,\n ref: ctx.kind === 'git' ? ctx.ref : undefined,\n subpath: ctx.kind === 'git' ? ctx.subpath : undefined,\n },\n hash,\n );\n\n return {\n skillId: targetId,\n path: promoted.path,\n contentHash: hash,\n kind: ctx.kind,\n source: ctx.source,\n };\n } catch (err) {\n rmSync(tempDir, { recursive: true, force: true });\n throw err;\n }\n}\n\nasync function pullSkillFromZipBuffer(\n buf: Buffer,\n sourceLabel: string,\n options: HubPullOptions,\n): Promise<HubPullResult> {\n const r = installSkillFromZip(buf, {\n skillId: options.skillId,\n overwrite: options.force ?? false,\n });\n const hash = computeSkillTreeHashSync(r.path);\n recordSkillsHubInstall(r.skillId, { kind: 'archive', source: sourceLabel }, hash);\n await runScan(r.path, options.strictScan);\n return {\n skillId: r.skillId,\n path: r.path,\n contentHash: hash,\n kind: 'archive',\n source: sourceLabel,\n };\n}\n\nasync function pullSkillFromTarFile(\n archivePath: string,\n sourceLabel: string,\n options: HubPullOptions,\n): Promise<HubPullResult> {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-tar-'));\n try {\n execFileSync('tar', ['-xzf', archivePath, '-C', tmpRoot], {\n stdio: 'pipe',\n encoding: 'utf-8',\n });\n const skillRoot = findSkillRoot(tmpRoot);\n return await copySkillTreeToManaged(skillRoot, { ...options, kind: 'archive', source: sourceLabel });\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n}\n\nasync function pullSkillFromLocalPath(absPath: string, options: HubPullOptions): Promise<HubPullResult> {\n const resolved = resolve(absPath);\n if (!existsSync(resolved)) {\n throw new Error(`Not found: ${resolved}`);\n }\n const st = statSync(resolved);\n if (!st.isFile()) {\n throw new Error('Expected a path to a .zip or .tar.gz file (directories are not supported here)');\n }\n const lower = resolved.toLowerCase();\n if (lower.endsWith('.zip')) {\n const buf = readFileSync(resolved);\n return pullSkillFromZipBuffer(buf, resolved, options);\n }\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) {\n return pullSkillFromTarFile(resolved, resolved, options);\n }\n throw new Error('Unsupported file type (expected .zip, .tar.gz, or .tgz)');\n}\n\nasync function pullSkillFromGit(url: string, options: HubPullOptions): Promise<HubPullResult> {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-hub-'));\n const cloneDir = join(tmpRoot, 'clone');\n try {\n const args = ['clone', '--depth', '1'];\n if (options.ref?.trim()) {\n args.push('--branch', options.ref.trim());\n }\n args.push(url, cloneDir);\n try {\n execFileSync('git', args, { stdio: 'pipe', encoding: 'utf-8' });\n } catch {\n if (options.ref?.trim()) {\n execFileSync('git', ['clone', '--depth', '1', url, cloneDir], {\n stdio: 'pipe',\n encoding: 'utf-8',\n });\n } else {\n throw new Error(`git clone failed for ${url}`);\n }\n }\n const skillRoot = findSkillRoot(cloneDir, options.subpath);\n return await copySkillTreeToManaged(skillRoot, { ...options, kind: 'git', source: url });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (msg.includes('ENOENT') || msg.includes('git clone failed')) {\n throw new Error(\n `Git operation failed (${msg}). Ensure git is installed and the repository URL is valid.`,\n );\n }\n throw e;\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n}\n\n/**\n * Install or replace a skill from a git URL, http(s) archive URL, file:// URL, or local archive path.\n */\nexport async function pullSkillFromSource(\n source: string,\n options: HubPullOptions = {},\n): Promise<HubPullResult> {\n const trimmed = source.trim();\n if (!trimmed) {\n throw new Error('Missing source URL or path');\n }\n\n if (trimmed.startsWith('file:')) {\n return pullSkillFromLocalPath(fileURLToPath(trimmed), options);\n }\n\n if (!/^[a-zA-Z][a-zA-Z+.-]*:\\/\\//.test(trimmed) && existsSync(trimmed)) {\n return pullSkillFromLocalPath(resolve(trimmed), options);\n }\n\n const kind = classifyHubSource(trimmed);\n if (kind === 'archive') {\n const res = await fetch(trimmed);\n if (!res.ok) {\n throw new Error(`HTTP ${res.status} while fetching archive`);\n }\n const buf = Buffer.from(await res.arrayBuffer());\n const lower = trimmed.toLowerCase();\n if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) {\n const tmpRoot = mkdtempSync(join(tmpdir(), 'xopc-fetch-tar-'));\n const f = join(tmpRoot, 'archive.tgz');\n try {\n mkdirSync(tmpRoot, { recursive: true });\n writeFileSync(f, buf);\n return await pullSkillFromTarFile(f, trimmed, options);\n } finally {\n rmSync(tmpRoot, { recursive: true, force: true });\n }\n }\n return pullSkillFromZipBuffer(buf, trimmed, options);\n }\n\n return pullSkillFromGit(trimmed, options);\n}\n\n/** Re-install from the source recorded in ~/.xopc/skills-lock.json for this skill id. */\nexport async function updateSkillFromLock(\n skillId: string,\n options: Pick<HubPullOptions, 'strictScan'> = {},\n): Promise<HubPullResult> {\n const e = getSkillsLockEntry(skillId);\n if (!e) {\n throw new Error(\n `No hub lock entry for \"${skillId}\". Install with: xopc skills pull <git-or-archive-url>`,\n );\n }\n return pullSkillFromSource(e.source, {\n skillId,\n ref: e.ref,\n subpath: e.subpath,\n force: true,\n strictScan: options.strictScan,\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { resolveStateDir } from "../../config/paths-state.js";
|
|
2
|
+
import { init_paths } from "../../config/paths.js";
|
|
2
3
|
import { createLogger } from "../../utils/logger/index.js";
|
|
3
4
|
import { init_logger } from "../../utils/logger.js";
|
|
4
|
-
import { init_paths } from "../../config/paths.js";
|
|
5
5
|
import { parseFrontmatter } from "../../markdown/frontmatter.js";
|
|
6
6
|
import { findInstallSpec, getDefaultInstallerPreferences, hasBinary, installSkill } from "./installer.js";
|
|
7
7
|
import { applySkillEnvOverrides, createSkillConfigManager, getSkillEnvironment, isSkillEnabled, resolveSkillConfig, validateSkillConfig } from "./config.js";
|
|
@@ -11,8 +11,8 @@ import { parseRequiredEnvVarNames } from "./required-env-vars.js";
|
|
|
11
11
|
import { formatSkillsForPrompt } from "./format-skills-prompt.js";
|
|
12
12
|
import { parseSkillMetadata } from "./parse-skill-metadata.js";
|
|
13
13
|
import { SkillTestFramework, SkillTestRunner, formatTestResults, formatTestResultsJson, formatTestResultsTap } from "./test-framework.js";
|
|
14
|
-
import { basename, dirname, join, relative, sep } from "path";
|
|
15
14
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
15
|
+
import { basename, dirname, join, relative, sep } from "path";
|
|
16
16
|
//#region src/agent/skills/index.ts
|
|
17
17
|
init_paths();
|
|
18
18
|
init_logger();
|
|
@@ -68,7 +68,11 @@ function shouldIgnore(path, ignoredPaths) {
|
|
|
68
68
|
}
|
|
69
69
|
function discoverSkills(dir, source) {
|
|
70
70
|
const skills = [];
|
|
71
|
-
|
|
71
|
+
const diagnostics = [];
|
|
72
|
+
if (!existsSync(dir)) return {
|
|
73
|
+
skills,
|
|
74
|
+
diagnostics
|
|
75
|
+
};
|
|
72
76
|
function scan(currentDir, currentIgnoredPaths) {
|
|
73
77
|
try {
|
|
74
78
|
const entries = readdirSync(currentDir, { withFileTypes: true });
|
|
@@ -86,16 +90,27 @@ function discoverSkills(dir, source) {
|
|
|
86
90
|
for (const rule of subRules) subIgnoredPaths.add(`${skillRelPath}${rule}`);
|
|
87
91
|
}
|
|
88
92
|
if (existsSync(skillMdPath) && !shouldIgnore(skillRelPath, currentIgnoredPaths)) {
|
|
89
|
-
const skill = loadSkillFromFile(skillMdPath, source, dir);
|
|
93
|
+
const { skill, diagnostic } = loadSkillFromFile(skillMdPath, source, dir);
|
|
94
|
+
if (diagnostic) diagnostics.push(diagnostic);
|
|
90
95
|
if (skill) skills.push(skill);
|
|
91
96
|
}
|
|
92
97
|
scan(fullPath, subIgnoredPaths);
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
|
-
} catch {
|
|
100
|
+
} catch (err) {
|
|
101
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
102
|
+
diagnostics.push({
|
|
103
|
+
type: "error",
|
|
104
|
+
message: `Failed to scan skills directory: ${message}`,
|
|
105
|
+
path: currentDir
|
|
106
|
+
});
|
|
107
|
+
}
|
|
96
108
|
}
|
|
97
109
|
scan(dir, loadIgnoreRules(dir, dir));
|
|
98
|
-
return
|
|
110
|
+
return {
|
|
111
|
+
skills,
|
|
112
|
+
diagnostics
|
|
113
|
+
};
|
|
99
114
|
}
|
|
100
115
|
function deriveDescriptionFromMarkdown(content) {
|
|
101
116
|
let fallbackHeading = "";
|
|
@@ -118,7 +133,15 @@ function loadSkillFromFile(filePath, source, rootDir) {
|
|
|
118
133
|
const parentDirName = basename(skillDir);
|
|
119
134
|
const name = frontmatter.name || parentDirName;
|
|
120
135
|
const description = frontmatter.description?.trim() || deriveDescriptionFromMarkdown(content);
|
|
121
|
-
if (!description?.trim()) return
|
|
136
|
+
if (!description?.trim()) return {
|
|
137
|
+
skill: null,
|
|
138
|
+
diagnostic: {
|
|
139
|
+
type: "error",
|
|
140
|
+
skillName: name,
|
|
141
|
+
message: `Skill "${name}" is missing a description`,
|
|
142
|
+
path: filePath
|
|
143
|
+
}
|
|
144
|
+
};
|
|
122
145
|
let category;
|
|
123
146
|
if (rootDir) {
|
|
124
147
|
const segments = toPosixPath(relative(rootDir, skillDir)).split("/");
|
|
@@ -129,7 +152,7 @@ function loadSkillFromFile(filePath, source, rootDir) {
|
|
|
129
152
|
metadata.description ||= description.trim();
|
|
130
153
|
const toolConditions = parseSkillToolConditions(frontmatter);
|
|
131
154
|
const requiredEnvVarNames = parseRequiredEnvVarNames(frontmatter);
|
|
132
|
-
return {
|
|
155
|
+
return { skill: {
|
|
133
156
|
name,
|
|
134
157
|
description: description.trim(),
|
|
135
158
|
category,
|
|
@@ -141,38 +164,55 @@ function loadSkillFromFile(filePath, source, rootDir) {
|
|
|
141
164
|
toolConditions,
|
|
142
165
|
requiredEnvVarNames: requiredEnvVarNames.length > 0 ? requiredEnvVarNames : void 0,
|
|
143
166
|
content
|
|
167
|
+
} };
|
|
168
|
+
} catch (err) {
|
|
169
|
+
return {
|
|
170
|
+
skill: null,
|
|
171
|
+
diagnostic: {
|
|
172
|
+
type: "error",
|
|
173
|
+
message: `Failed to load skill file: ${err instanceof Error ? err.message : String(err)}`,
|
|
174
|
+
path: filePath
|
|
175
|
+
}
|
|
144
176
|
};
|
|
145
|
-
} catch {
|
|
146
|
-
return null;
|
|
147
177
|
}
|
|
148
178
|
}
|
|
149
179
|
function loadSkills(options) {
|
|
150
180
|
const { workspaceDir, builtinDir, extraDirs = [] } = options;
|
|
151
181
|
const skillMap = /* @__PURE__ */ new Map();
|
|
152
182
|
const diagnostics = [];
|
|
153
|
-
if (builtinDir)
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
183
|
+
if (builtinDir) {
|
|
184
|
+
const discovered = discoverSkills(builtinDir, "builtin");
|
|
185
|
+
diagnostics.push(...discovered.diagnostics);
|
|
186
|
+
for (const skill of discovered.skills) {
|
|
187
|
+
const existing = skillMap.get(skill.name);
|
|
188
|
+
if (existing) diagnostics.push({
|
|
189
|
+
type: "collision",
|
|
190
|
+
skillName: skill.name,
|
|
191
|
+
message: `Skill "${skill.name}" collision: ${existing.source} overrides ${skill.source}`,
|
|
192
|
+
path: skill.filePath
|
|
193
|
+
});
|
|
194
|
+
else skillMap.set(skill.name, skill);
|
|
195
|
+
}
|
|
162
196
|
}
|
|
163
197
|
const globalDirs = [options.globalDir, join(resolveStateDir(), "skills")].filter((d) => !!d && existsSync(d));
|
|
164
|
-
for (const dir of globalDirs)
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
198
|
+
for (const dir of globalDirs) {
|
|
199
|
+
const discovered = discoverSkills(dir, "global");
|
|
200
|
+
diagnostics.push(...discovered.diagnostics);
|
|
201
|
+
for (const skill of discovered.skills) {
|
|
202
|
+
const existing = skillMap.get(skill.name);
|
|
203
|
+
if (existing) diagnostics.push({
|
|
204
|
+
type: "collision",
|
|
205
|
+
skillName: skill.name,
|
|
206
|
+
message: `Skill "${skill.name}" collision: ${skill.source} overrides ${existing.source}`,
|
|
207
|
+
path: skill.filePath
|
|
208
|
+
});
|
|
209
|
+
skillMap.set(skill.name, skill);
|
|
210
|
+
}
|
|
173
211
|
}
|
|
174
212
|
if (workspaceDir) {
|
|
175
|
-
const
|
|
213
|
+
const discovered = discoverSkills(join(workspaceDir, "skills"), "workspace");
|
|
214
|
+
diagnostics.push(...discovered.diagnostics);
|
|
215
|
+
const workspaceSkills = discovered.skills;
|
|
176
216
|
for (const skill of workspaceSkills) {
|
|
177
217
|
const existing = skillMap.get(skill.name);
|
|
178
218
|
if (existing) diagnostics.push({
|
|
@@ -184,15 +224,19 @@ function loadSkills(options) {
|
|
|
184
224
|
skillMap.set(skill.name, skill);
|
|
185
225
|
}
|
|
186
226
|
}
|
|
187
|
-
for (const extraDir of extraDirs) if (existsSync(extraDir))
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
227
|
+
for (const extraDir of extraDirs) if (existsSync(extraDir)) {
|
|
228
|
+
const discovered = discoverSkills(extraDir, "global");
|
|
229
|
+
diagnostics.push(...discovered.diagnostics);
|
|
230
|
+
for (const skill of discovered.skills) {
|
|
231
|
+
const existing = skillMap.get(skill.name);
|
|
232
|
+
if (existing) diagnostics.push({
|
|
233
|
+
type: "collision",
|
|
234
|
+
skillName: skill.name,
|
|
235
|
+
message: `Skill "${skill.name}" collision: ${existing.source} overrides ${skill.source}`,
|
|
236
|
+
path: skill.filePath
|
|
237
|
+
});
|
|
238
|
+
else skillMap.set(skill.name, skill);
|
|
239
|
+
}
|
|
196
240
|
}
|
|
197
241
|
const skillsConfig = createSkillConfigManager(resolveStateDir()).load();
|
|
198
242
|
const merged = Array.from(skillMap.values());
|