@xyit/wellspring 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +853 -2
- package/dist/agent/pending-asks.d.ts +20 -0
- package/dist/agent/pending-asks.js +70 -0
- package/dist/agent/pending-asks.js.map +1 -0
- package/dist/agent/task-registry.d.ts +14 -0
- package/dist/agent/task-registry.js +80 -0
- package/dist/agent/task-registry.js.map +1 -0
- package/dist/agent/tool-context.d.ts +30 -0
- package/dist/agent/tool-context.js +10 -0
- package/dist/agent/tool-context.js.map +1 -0
- package/dist/agent/usage.d.ts +40 -0
- package/dist/agent/usage.js +122 -0
- package/dist/agent/usage.js.map +1 -0
- package/dist/api/host/agent.d.ts +165 -0
- package/dist/api/host/agent.js +5 -0
- package/dist/api/host/agent.js.map +1 -0
- package/dist/api/host/events.d.ts +2 -0
- package/dist/api/host/events.js +6 -0
- package/dist/api/host/events.js.map +1 -0
- package/dist/api/host/exec.d.ts +36 -0
- package/dist/api/host/exec.js +10 -0
- package/dist/api/host/exec.js.map +1 -0
- package/dist/api/host/fs.d.ts +24 -0
- package/dist/api/host/fs.js +7 -0
- package/dist/api/host/fs.js.map +1 -0
- package/dist/api/host/hooks.d.ts +210 -0
- package/dist/api/host/hooks.js +401 -0
- package/dist/api/host/hooks.js.map +1 -0
- package/dist/api/host/host.d.ts +36 -0
- package/dist/api/host/host.js +53 -0
- package/dist/api/host/host.js.map +1 -0
- package/dist/api/host/http.d.ts +58 -0
- package/dist/api/host/http.js +6 -0
- package/dist/api/host/http.js.map +1 -0
- package/dist/api/host/index.d.ts +15 -0
- package/dist/api/host/index.js +20 -0
- package/dist/api/host/index.js.map +1 -0
- package/dist/api/host/models.d.ts +65 -0
- package/dist/api/host/models.js +8 -0
- package/dist/api/host/models.js.map +1 -0
- package/dist/api/host/network.d.ts +22 -0
- package/dist/api/host/network.js +7 -0
- package/dist/api/host/network.js.map +1 -0
- package/dist/api/host/plugin.d.ts +138 -0
- package/dist/api/host/plugin.js +6 -0
- package/dist/api/host/plugin.js.map +1 -0
- package/dist/api/host/plugins.d.ts +49 -0
- package/dist/api/host/plugins.js +6 -0
- package/dist/api/host/plugins.js.map +1 -0
- package/dist/api/host/pty.d.ts +55 -0
- package/dist/api/host/pty.js +21 -0
- package/dist/api/host/pty.js.map +1 -0
- package/dist/api/host/session.d.ts +5 -0
- package/dist/api/host/session.js +52 -0
- package/dist/api/host/session.js.map +1 -0
- package/dist/api/host/version.d.ts +2 -0
- package/dist/api/host/version.js +3 -0
- package/dist/api/host/version.js.map +1 -0
- package/dist/api/host/workspace.d.ts +38 -0
- package/dist/api/host/workspace.js +9 -0
- package/dist/api/host/workspace.js.map +1 -0
- package/dist/api/plugin/http-client.d.ts +42 -0
- package/dist/api/plugin/http-client.js +364 -0
- package/dist/api/plugin/http-client.js.map +1 -0
- package/dist/api/plugin/http-protocol.d.ts +152 -0
- package/dist/api/plugin/http-protocol.js +49 -0
- package/dist/api/plugin/http-protocol.js.map +1 -0
- package/dist/api/result/result.d.ts +16 -0
- package/dist/api/result/result.js +43 -0
- package/dist/api/result/result.js.map +1 -0
- package/dist/commands/agent-turn.d.ts +63 -0
- package/dist/commands/agent-turn.js +687 -0
- package/dist/commands/agent-turn.js.map +1 -0
- package/dist/commands/ask-prompt.d.ts +6 -0
- package/dist/commands/ask-prompt.js +49 -0
- package/dist/commands/ask-prompt.js.map +1 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +283 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/conversation.d.ts +51 -0
- package/dist/commands/conversation.js +630 -0
- package/dist/commands/conversation.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +77 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/exec-approval.d.ts +3 -0
- package/dist/commands/exec-approval.js +80 -0
- package/dist/commands/exec-approval.js.map +1 -0
- package/dist/commands/help.d.ts +2 -0
- package/dist/commands/help.js +35 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/key.d.ts +2 -0
- package/dist/commands/key.js +226 -0
- package/dist/commands/key.js.map +1 -0
- package/dist/commands/plugin.d.ts +2 -0
- package/dist/commands/plugin.js +190 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/session.d.ts +2 -0
- package/dist/commands/session.js +167 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/shared.d.ts +35 -0
- package/dist/commands/shared.js +66 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/start.d.ts +2 -0
- package/dist/commands/start.js +75 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/stop.d.ts +2 -0
- package/dist/commands/stop.js +24 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/version.d.ts +2 -0
- package/dist/commands/version.js +33 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/config/codes.d.ts +13 -0
- package/dist/config/codes.js +15 -0
- package/dist/config/codes.js.map +1 -0
- package/dist/config/credentials.d.ts +67 -0
- package/dist/config/credentials.js +214 -0
- package/dist/config/credentials.js.map +1 -0
- package/dist/config/defaults.d.ts +5 -0
- package/dist/config/defaults.js +31 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.js +10 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +10 -0
- package/dist/config/loader.js +68 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/providers.d.ts +30 -0
- package/dist/config/providers.js +97 -0
- package/dist/config/providers.js.map +1 -0
- package/dist/config/types.d.ts +75 -0
- package/dist/config/types.js +4 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validate.d.ts +4 -0
- package/dist/config/validate.js +391 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/display/markdown.d.ts +7 -0
- package/dist/display/markdown.js +116 -0
- package/dist/display/markdown.js.map +1 -0
- package/dist/display/tool-label.d.ts +6 -0
- package/dist/display/tool-label.js +81 -0
- package/dist/display/tool-label.js.map +1 -0
- package/dist/event/event.d.ts +27 -0
- package/dist/event/event.js +152 -0
- package/dist/event/event.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +308 -0
- package/dist/index.js.map +1 -0
- package/dist/input/codes.d.ts +15 -0
- package/dist/input/codes.js +20 -0
- package/dist/input/codes.js.map +1 -0
- package/dist/input/commands.d.ts +10 -0
- package/dist/input/commands.js +65 -0
- package/dist/input/commands.js.map +1 -0
- package/dist/input/help.d.ts +8 -0
- package/dist/input/help.js +51 -0
- package/dist/input/help.js.map +1 -0
- package/dist/input/index.d.ts +6 -0
- package/dist/input/index.js +10 -0
- package/dist/input/index.js.map +1 -0
- package/dist/input/parse-options.d.ts +11 -0
- package/dist/input/parse-options.js +51 -0
- package/dist/input/parse-options.js.map +1 -0
- package/dist/input/parse.d.ts +3 -0
- package/dist/input/parse.js +119 -0
- package/dist/input/parse.js.map +1 -0
- package/dist/input/types.d.ts +17 -0
- package/dist/input/types.js +2 -0
- package/dist/input/types.js.map +1 -0
- package/dist/logger/elog4js.d.ts +65 -0
- package/dist/logger/elog4js.js +305 -0
- package/dist/logger/elog4js.js.map +1 -0
- package/dist/logger/logger.d.ts +37 -0
- package/dist/logger/logger.js +196 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/network/index.d.ts +2 -0
- package/dist/network/index.js +6 -0
- package/dist/network/index.js.map +1 -0
- package/dist/network/openai.d.ts +117 -0
- package/dist/network/openai.js +443 -0
- package/dist/network/openai.js.map +1 -0
- package/dist/network/proxy.d.ts +9 -0
- package/dist/network/proxy.js +72 -0
- package/dist/network/proxy.js.map +1 -0
- package/dist/network/web.d.ts +34 -0
- package/dist/network/web.js +152 -0
- package/dist/network/web.js.map +1 -0
- package/dist/platform/clipboard.d.ts +15 -0
- package/dist/platform/clipboard.js +82 -0
- package/dist/platform/clipboard.js.map +1 -0
- package/dist/platform/exec.d.ts +28 -0
- package/dist/platform/exec.js +95 -0
- package/dist/platform/exec.js.map +1 -0
- package/dist/platform/file.d.ts +38 -0
- package/dist/platform/file.js +197 -0
- package/dist/platform/file.js.map +1 -0
- package/dist/platform/index.d.ts +7 -0
- package/dist/platform/index.js +12 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/notification.d.ts +14 -0
- package/dist/platform/notification.js +82 -0
- package/dist/platform/notification.js.map +1 -0
- package/dist/platform/open-external.d.ts +10 -0
- package/dist/platform/open-external.js +105 -0
- package/dist/platform/open-external.js.map +1 -0
- package/dist/platform/os.d.ts +20 -0
- package/dist/platform/os.js +86 -0
- package/dist/platform/os.js.map +1 -0
- package/dist/platform/path.d.ts +32 -0
- package/dist/platform/path.js +154 -0
- package/dist/platform/path.js.map +1 -0
- package/dist/plugin/access-guard.d.ts +1 -0
- package/dist/plugin/access-guard.js +40 -0
- package/dist/plugin/access-guard.js.map +1 -0
- package/dist/plugin/agent-service.d.ts +25 -0
- package/dist/plugin/agent-service.js +809 -0
- package/dist/plugin/agent-service.js.map +1 -0
- package/dist/plugin/agent-tools.d.ts +12 -0
- package/dist/plugin/agent-tools.js +31 -0
- package/dist/plugin/agent-tools.js.map +1 -0
- package/dist/plugin/capabilities.d.ts +26 -0
- package/dist/plugin/capabilities.js +192 -0
- package/dist/plugin/capabilities.js.map +1 -0
- package/dist/plugin/console-guard.d.ts +2 -0
- package/dist/plugin/console-guard.js +52 -0
- package/dist/plugin/console-guard.js.map +1 -0
- package/dist/plugin/data-access.d.ts +15 -0
- package/dist/plugin/data-access.js +57 -0
- package/dist/plugin/data-access.js.map +1 -0
- package/dist/plugin/fs.d.ts +3 -0
- package/dist/plugin/fs.js +93 -0
- package/dist/plugin/fs.js.map +1 -0
- package/dist/plugin/host-alias.d.ts +2 -0
- package/dist/plugin/host-alias.js +57 -0
- package/dist/plugin/host-alias.js.map +1 -0
- package/dist/plugin/index.d.ts +7 -0
- package/dist/plugin/index.js +11 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/install.d.ts +17 -0
- package/dist/plugin/install.js +163 -0
- package/dist/plugin/install.js.map +1 -0
- package/dist/plugin/loader.d.ts +45 -0
- package/dist/plugin/loader.js +739 -0
- package/dist/plugin/loader.js.map +1 -0
- package/dist/plugin/manage.d.ts +31 -0
- package/dist/plugin/manage.js +271 -0
- package/dist/plugin/manage.js.map +1 -0
- package/dist/plugin/models-service.d.ts +15 -0
- package/dist/plugin/models-service.js +317 -0
- package/dist/plugin/models-service.js.map +1 -0
- package/dist/plugin/network-service.d.ts +10 -0
- package/dist/plugin/network-service.js +87 -0
- package/dist/plugin/network-service.js.map +1 -0
- package/dist/plugin/package-manager.d.ts +46 -0
- package/dist/plugin/package-manager.js +148 -0
- package/dist/plugin/package-manager.js.map +1 -0
- package/dist/plugin/paths.d.ts +6 -0
- package/dist/plugin/paths.js +15 -0
- package/dist/plugin/paths.js.map +1 -0
- package/dist/plugin/registry.d.ts +17 -0
- package/dist/plugin/registry.js +82 -0
- package/dist/plugin/registry.js.map +1 -0
- package/dist/plugin/runtime.d.ts +9 -0
- package/dist/plugin/runtime.js +43 -0
- package/dist/plugin/runtime.js.map +1 -0
- package/dist/plugin/scaffold.d.ts +13 -0
- package/dist/plugin/scaffold.js +132 -0
- package/dist/plugin/scaffold.js.map +1 -0
- package/dist/plugin/state.d.ts +6 -0
- package/dist/plugin/state.js +38 -0
- package/dist/plugin/state.js.map +1 -0
- package/dist/plugin/zip.d.ts +7 -0
- package/dist/plugin/zip.js +138 -0
- package/dist/plugin/zip.js.map +1 -0
- package/dist/sandbox/audit.d.ts +12 -0
- package/dist/sandbox/audit.js +20 -0
- package/dist/sandbox/audit.js.map +1 -0
- package/dist/sandbox/backends/bwrap.d.ts +28 -0
- package/dist/sandbox/backends/bwrap.js +197 -0
- package/dist/sandbox/backends/bwrap.js.map +1 -0
- package/dist/sandbox/backends/direct.d.ts +7 -0
- package/dist/sandbox/backends/direct.js +32 -0
- package/dist/sandbox/backends/direct.js.map +1 -0
- package/dist/sandbox/bridge-state.d.ts +13 -0
- package/dist/sandbox/bridge-state.js +12 -0
- package/dist/sandbox/bridge-state.js.map +1 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.js +6 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/manager.d.ts +8 -0
- package/dist/sandbox/manager.js +325 -0
- package/dist/sandbox/manager.js.map +1 -0
- package/dist/sandbox/memory-limit.d.ts +44 -0
- package/dist/sandbox/memory-limit.js +193 -0
- package/dist/sandbox/memory-limit.js.map +1 -0
- package/dist/sandbox/plugin-client.d.ts +80 -0
- package/dist/sandbox/plugin-client.js +1601 -0
- package/dist/sandbox/plugin-client.js.map +1 -0
- package/dist/sandbox/plugin-host.d.ts +1 -0
- package/dist/sandbox/plugin-host.js +1001 -0
- package/dist/sandbox/plugin-host.js.map +1 -0
- package/dist/sandbox/protocol.d.ts +43 -0
- package/dist/sandbox/protocol.js +96 -0
- package/dist/sandbox/protocol.js.map +1 -0
- package/dist/sandbox/rpc-timeouts.d.ts +28 -0
- package/dist/sandbox/rpc-timeouts.js +15 -0
- package/dist/sandbox/rpc-timeouts.js.map +1 -0
- package/dist/sandbox/supervisor.d.ts +31 -0
- package/dist/sandbox/supervisor.js +114 -0
- package/dist/sandbox/supervisor.js.map +1 -0
- package/dist/sandbox/types.d.ts +124 -0
- package/dist/sandbox/types.js +17 -0
- package/dist/sandbox/types.js.map +1 -0
- package/dist/service/control.d.ts +32 -0
- package/dist/service/control.js +117 -0
- package/dist/service/control.js.map +1 -0
- package/dist/session/compress.d.ts +3 -0
- package/dist/session/compress.js +42 -0
- package/dist/session/compress.js.map +1 -0
- package/dist/session/index.d.ts +4 -0
- package/dist/session/index.js +8 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/registry.d.ts +4 -0
- package/dist/session/registry.js +12 -0
- package/dist/session/registry.js.map +1 -0
- package/dist/session/replay.d.ts +10 -0
- package/dist/session/replay.js +109 -0
- package/dist/session/replay.js.map +1 -0
- package/dist/session/session.d.ts +40 -0
- package/dist/session/session.js +108 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/store.d.ts +58 -0
- package/dist/session/store.js +382 -0
- package/dist/session/store.js.map +1 -0
- package/dist/session/types.d.ts +56 -0
- package/dist/session/types.js +3 -0
- package/dist/session/types.js.map +1 -0
- package/dist/workspace/index.d.ts +7 -0
- package/dist/workspace/index.js +17 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/registry.d.ts +25 -0
- package/dist/workspace/registry.js +293 -0
- package/dist/workspace/registry.js.map +1 -0
- package/package.json +73 -5
- package/scripts/build-node-pty.mjs +55 -0
- package/scripts/rewrite-dts.mjs +36 -0
- package/scripts/run-tests.mjs +41 -0
- package/scripts/verify-sandbox.ts +98 -0
- package/src/agent/pending-asks.ts +88 -0
- package/src/agent/task-registry.ts +108 -0
- package/src/agent/tool-context.ts +47 -0
- package/src/agent/usage.ts +170 -0
- package/src/api/host/agent.ts +162 -0
- package/src/api/host/events.ts +7 -0
- package/src/api/host/exec.ts +47 -0
- package/src/api/host/fs.ts +33 -0
- package/src/api/host/hooks.ts +635 -0
- package/src/api/host/host.ts +99 -0
- package/src/api/host/http.ts +79 -0
- package/src/api/host/index.ts +20 -0
- package/src/api/host/models.ts +81 -0
- package/src/api/host/network.ts +32 -0
- package/src/api/host/plugin.ts +158 -0
- package/src/api/host/plugins.ts +59 -0
- package/src/api/host/pty.ts +73 -0
- package/src/api/host/session.ts +61 -0
- package/src/api/host/version.ts +2 -0
- package/src/api/host/workspace.ts +51 -0
- package/src/api/plugin/http-client.ts +492 -0
- package/src/api/plugin/http-protocol.ts +231 -0
- package/src/api/result/result.ts +52 -0
- package/src/commands/agent-turn.ts +817 -0
- package/src/commands/ask-prompt.ts +52 -0
- package/src/commands/config.ts +306 -0
- package/src/commands/conversation.ts +777 -0
- package/src/commands/doctor.ts +92 -0
- package/src/commands/exec-approval.ts +91 -0
- package/src/commands/help.ts +41 -0
- package/src/commands/key.ts +281 -0
- package/src/commands/plugin.ts +222 -0
- package/src/commands/session.ts +179 -0
- package/src/commands/shared.ts +103 -0
- package/src/commands/start.ts +88 -0
- package/src/commands/stop.ts +24 -0
- package/src/commands/version.ts +40 -0
- package/src/config/codes.ts +17 -0
- package/src/config/credentials.ts +302 -0
- package/src/config/defaults.ts +32 -0
- package/src/config/index.ts +10 -0
- package/src/config/loader.ts +80 -0
- package/src/config/providers.ts +133 -0
- package/src/config/types.ts +88 -0
- package/src/config/validate.ts +458 -0
- package/src/display/markdown.ts +137 -0
- package/src/display/tool-label.ts +87 -0
- package/src/event/event.ts +171 -0
- package/src/index.ts +344 -0
- package/src/input/codes.ts +21 -0
- package/src/input/commands.ts +76 -0
- package/src/input/help.ts +60 -0
- package/src/input/index.ts +10 -0
- package/src/input/parse-options.ts +66 -0
- package/src/input/parse.ts +138 -0
- package/src/input/types.ts +19 -0
- package/src/logger/elog4js.ts +366 -0
- package/src/logger/logger.ts +251 -0
- package/src/network/index.ts +6 -0
- package/src/network/openai.ts +571 -0
- package/src/network/proxy.ts +80 -0
- package/src/network/web.ts +206 -0
- package/src/platform/clipboard.ts +107 -0
- package/src/platform/exec.ts +128 -0
- package/src/platform/file.ts +235 -0
- package/src/platform/index.ts +12 -0
- package/src/platform/notification.ts +108 -0
- package/src/platform/open-external.ts +132 -0
- package/src/platform/os.ts +101 -0
- package/src/platform/path.ts +214 -0
- package/src/plugin/access-guard.ts +46 -0
- package/src/plugin/agent-service.ts +969 -0
- package/src/plugin/agent-tools.ts +43 -0
- package/src/plugin/capabilities.ts +240 -0
- package/src/plugin/console-guard.ts +66 -0
- package/src/plugin/data-access.ts +70 -0
- package/src/plugin/fs.ts +109 -0
- package/src/plugin/host-alias.ts +61 -0
- package/src/plugin/index.ts +11 -0
- package/src/plugin/install.ts +213 -0
- package/src/plugin/loader.ts +952 -0
- package/src/plugin/manage.ts +338 -0
- package/src/plugin/models-service.ts +383 -0
- package/src/plugin/network-service.ts +102 -0
- package/src/plugin/package-manager.ts +214 -0
- package/src/plugin/paths.ts +19 -0
- package/src/plugin/registry.ts +103 -0
- package/src/plugin/runtime.ts +57 -0
- package/src/plugin/scaffold.ts +158 -0
- package/src/plugin/state.ts +52 -0
- package/src/plugin/zip.ts +156 -0
- package/src/sandbox/audit.ts +38 -0
- package/src/sandbox/backends/bwrap.ts +251 -0
- package/src/sandbox/backends/direct.ts +43 -0
- package/src/sandbox/bridge-state.ts +29 -0
- package/src/sandbox/index.ts +5 -0
- package/src/sandbox/manager.ts +406 -0
- package/src/sandbox/memory-limit.ts +245 -0
- package/src/sandbox/plugin-client.ts +1881 -0
- package/src/sandbox/plugin-host.ts +1282 -0
- package/src/sandbox/protocol.ts +142 -0
- package/src/sandbox/pty-bridge.py +154 -0
- package/src/sandbox/rpc-timeouts.ts +43 -0
- package/src/sandbox/supervisor.ts +172 -0
- package/src/sandbox/types.ts +149 -0
- package/src/service/control.ts +155 -0
- package/src/session/compress.ts +47 -0
- package/src/session/index.ts +8 -0
- package/src/session/registry.ts +17 -0
- package/src/session/replay.ts +132 -0
- package/src/session/session.ts +134 -0
- package/src/session/store.ts +468 -0
- package/src/session/types.ts +61 -0
- package/src/workspace/index.ts +21 -0
- package/src/workspace/registry.ts +356 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// 插件 Agent 工具注册表
|
|
2
|
+
|
|
3
|
+
import type { AgentTool } from "../api/host/plugin.ts"
|
|
4
|
+
|
|
5
|
+
const TOOL_NAME = /^[a-z][a-z0-9_-]{1,63}$/
|
|
6
|
+
|
|
7
|
+
export interface RegisteredAgentTool {
|
|
8
|
+
readonly pluginId: string
|
|
9
|
+
readonly tool: AgentTool
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class AgentToolRegistry {
|
|
13
|
+
readonly #tools = new Map<string, RegisteredAgentTool>()
|
|
14
|
+
|
|
15
|
+
register(pluginId: string, tool: AgentTool): void {
|
|
16
|
+
if (!TOOL_NAME.test(tool.name)) {
|
|
17
|
+
throw new Error(`Agent 工具名不合法:${tool.name}`)
|
|
18
|
+
}
|
|
19
|
+
if (this.#tools.has(tool.name)) {
|
|
20
|
+
throw new Error(`Agent 工具重名:${tool.name}`)
|
|
21
|
+
}
|
|
22
|
+
if (typeof tool.call !== "function") {
|
|
23
|
+
throw new Error(`Agent 工具缺少 call:${tool.name}`)
|
|
24
|
+
}
|
|
25
|
+
this.#tools.set(tool.name, { pluginId, tool })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
list(): readonly RegisteredAgentTool[] {
|
|
29
|
+
return [...this.#tools.values()]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get(name: string): RegisteredAgentTool | undefined {
|
|
33
|
+
return this.#tools.get(name)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
removePlugin(pluginId: string): void {
|
|
37
|
+
for (const [name, record] of this.#tools) {
|
|
38
|
+
if (record.pluginId === pluginId) {
|
|
39
|
+
this.#tools.delete(name)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// 插件可用的宿主能力:sandbox 代码执行与网络工具
|
|
2
|
+
|
|
3
|
+
import type { Logger } from "pino"
|
|
4
|
+
|
|
5
|
+
import { loadSettings } from "../config/loader.ts"
|
|
6
|
+
import type { AppSetting } from "../config/types.ts"
|
|
7
|
+
import { credentialsFile, resolveApiKeySync } from "../config/credentials.ts"
|
|
8
|
+
import {
|
|
9
|
+
WEB_CODES,
|
|
10
|
+
createDeepSeekWebClient,
|
|
11
|
+
type WebFetchResult,
|
|
12
|
+
type WebSearchResult,
|
|
13
|
+
} from "../network/web.ts"
|
|
14
|
+
import { createProxyFetch } from "../network/proxy.ts"
|
|
15
|
+
import type { ResolvedPaths } from "../platform/path.ts"
|
|
16
|
+
import { SandboxManager } from "../sandbox/manager.ts"
|
|
17
|
+
import type { AgentCodeRequest, AgentCodeResult, StopMode } from "../sandbox/types.ts"
|
|
18
|
+
import { getAgentToolContext, type AgentToolContext } from "../agent/tool-context.ts"
|
|
19
|
+
import {
|
|
20
|
+
queryTask,
|
|
21
|
+
registerBackgroundTask,
|
|
22
|
+
stopTask,
|
|
23
|
+
type AgentTaskInfo,
|
|
24
|
+
} from "../agent/task-registry.ts"
|
|
25
|
+
|
|
26
|
+
export interface HostCapabilities {
|
|
27
|
+
executeCode(
|
|
28
|
+
input: Omit<AgentCodeRequest, "workspaceDir">,
|
|
29
|
+
context?: AgentToolContext,
|
|
30
|
+
): Promise<AgentCodeResult>
|
|
31
|
+
startBackgroundCode(
|
|
32
|
+
input: Omit<AgentCodeRequest, "workspaceDir">,
|
|
33
|
+
context?: AgentToolContext,
|
|
34
|
+
): Promise<{ taskId: string; sandboxId: string }>
|
|
35
|
+
queryTask(taskId?: string): AgentTaskInfo | null
|
|
36
|
+
stopTask(taskId: string | undefined, mode: StopMode): boolean
|
|
37
|
+
webSearch(query: string, count?: number): Promise<readonly WebSearchResult[]>
|
|
38
|
+
webFetch(url: string): Promise<WebFetchResult>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface HostCapabilitiesOptions {
|
|
42
|
+
readonly config: AppSetting
|
|
43
|
+
readonly paths: ResolvedPaths
|
|
44
|
+
readonly logger: Logger
|
|
45
|
+
/** 配置文件路径;提供时每次联网工具调用都重读,网页里改的代理不需要重启 */
|
|
46
|
+
readonly settingsFile?: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const ACCESS_RANK: Readonly<Record<string, number>> = {
|
|
50
|
+
"read-only": 0,
|
|
51
|
+
"workspace-write": 1,
|
|
52
|
+
"danger-full-access": 2,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createHostCapabilities(options: HostCapabilitiesOptions): HostCapabilities {
|
|
56
|
+
let manager: SandboxManager | null = null
|
|
57
|
+
let web: ReturnType<typeof createDeepSeekWebClient> | null = null
|
|
58
|
+
let webCacheKey: string | null = null
|
|
59
|
+
|
|
60
|
+
/** 联网工具用默认供应商(OpenAI/DeepSeek)的 Key:环境变量 > credentials.json */
|
|
61
|
+
const resolveWebApiKey = (): string | null =>
|
|
62
|
+
resolveApiKeySync(credentialsFile(options.paths.configDir))?.apiKey ?? null
|
|
63
|
+
|
|
64
|
+
/** 配置文件可能存在网页/CLI 运行中修改(Key、Base URL、代理):每次调用重读 */
|
|
65
|
+
const currentConfig = async (): Promise<AppSetting> => {
|
|
66
|
+
if (options.settingsFile === undefined) return options.config
|
|
67
|
+
try {
|
|
68
|
+
const loaded = await loadSettings(options.settingsFile)
|
|
69
|
+
return loaded.getData() ?? options.config
|
|
70
|
+
} catch {
|
|
71
|
+
return options.config
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Key / 代理 / Base URL 都可能在运行中变化,缓存按这些字段失效 */
|
|
76
|
+
const webClient = async (): Promise<ReturnType<typeof createDeepSeekWebClient>> => {
|
|
77
|
+
const config = await currentConfig()
|
|
78
|
+
const apiKey = resolveWebApiKey()
|
|
79
|
+
const proxy = config.network.proxy
|
|
80
|
+
const cacheKey = [
|
|
81
|
+
apiKey ?? "",
|
|
82
|
+
config.openai.baseUrl ?? "",
|
|
83
|
+
String(config.openai.timeoutMs),
|
|
84
|
+
proxy?.url ?? "",
|
|
85
|
+
(proxy?.noProxy ?? []).join(","),
|
|
86
|
+
].join("|")
|
|
87
|
+
if (web === null || webCacheKey !== cacheKey) {
|
|
88
|
+
webCacheKey = cacheKey
|
|
89
|
+
web = createDeepSeekWebClient({
|
|
90
|
+
baseUrl: config.openai.baseUrl,
|
|
91
|
+
timeoutMs: config.openai.timeoutMs,
|
|
92
|
+
fetch: createProxyFetch(proxy) as typeof fetch | undefined,
|
|
93
|
+
...(apiKey === null ? {} : { apiKey }),
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
return web
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const webError = (code: string | null, action: string): Error => {
|
|
100
|
+
if (code === WEB_CODES.missingApiKey) {
|
|
101
|
+
return new Error(
|
|
102
|
+
"web.missing-api-key:联网工具缺少 API Key。设置环境变量 OPENAI_API_KEY,或运行 wellspring key set(网页:左下角「供应商」里填写该供应商的 Key)",
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
return new Error(code ?? `web.${action}-failed`)
|
|
106
|
+
}
|
|
107
|
+
const sandbox = (): SandboxManager => {
|
|
108
|
+
manager ??= new SandboxManager({
|
|
109
|
+
settings: options.config.sandbox,
|
|
110
|
+
paths: options.paths,
|
|
111
|
+
logger: options.logger,
|
|
112
|
+
})
|
|
113
|
+
return manager
|
|
114
|
+
}
|
|
115
|
+
const approve = async (
|
|
116
|
+
input: Omit<AgentCodeRequest, "workspaceDir">,
|
|
117
|
+
context: AgentToolContext,
|
|
118
|
+
/** 调用方是 startAgentCode(后台任务)而不是 runAgentCode */
|
|
119
|
+
background = false,
|
|
120
|
+
): Promise<{ access: NonNullable<AgentCodeRequest["access"]>; cwd: string }> => {
|
|
121
|
+
const current = context.permission
|
|
122
|
+
const requested = input.access ?? current
|
|
123
|
+
const cwd = input.cwd ?? context.workspaceDir
|
|
124
|
+
if ((ACCESS_RANK[requested] ?? 0) > (ACCESS_RANK[current] ?? 0)) {
|
|
125
|
+
const argv = input.argv ?? []
|
|
126
|
+
const stdin = typeof input.stdin === "string" ? input.stdin.trim() : ""
|
|
127
|
+
/*
|
|
128
|
+
* 参数逐个列出,而不是把 command 和 argv 拼成一行:
|
|
129
|
+
* 「bash -lc 'curl … | sh'」里危险的是参数边界,拼行会看不出引号归属。
|
|
130
|
+
*/
|
|
131
|
+
context.elog.action("请求执行命令", [
|
|
132
|
+
{ label: "command", value: input.command, code: true },
|
|
133
|
+
...argv.map((arg, index) => ({
|
|
134
|
+
label: `参数 ${index + 1}`,
|
|
135
|
+
value: arg,
|
|
136
|
+
code: true,
|
|
137
|
+
})),
|
|
138
|
+
{ label: "工作目录", value: cwd },
|
|
139
|
+
{ label: "权限", value: `${current} → ${requested}` },
|
|
140
|
+
...(input.network === true ? [{ label: "网络", value: "请求联网" }] : []),
|
|
141
|
+
...(background ? [{ label: "运行方式", value: "后台执行" }] : []),
|
|
142
|
+
...(typeof input.timeoutMs === "number"
|
|
143
|
+
? [{ label: "超时", value: `${Math.round(input.timeoutMs / 1000)} 秒` }]
|
|
144
|
+
: []),
|
|
145
|
+
...(stdin === ""
|
|
146
|
+
? []
|
|
147
|
+
: [
|
|
148
|
+
{
|
|
149
|
+
label: "标准输入",
|
|
150
|
+
value: stdin.length > 200 ? `${stdin.slice(0, 200)}…` : stdin,
|
|
151
|
+
code: true,
|
|
152
|
+
},
|
|
153
|
+
]),
|
|
154
|
+
])
|
|
155
|
+
let allowed = context.allowExec
|
|
156
|
+
if (!allowed && context.approveExec !== undefined) {
|
|
157
|
+
allowed = await context.approveExec({
|
|
158
|
+
command: input.command,
|
|
159
|
+
argv: input.argv ?? [],
|
|
160
|
+
cwd,
|
|
161
|
+
currentPermission: current,
|
|
162
|
+
requestedPermission: requested,
|
|
163
|
+
justification: input.justification,
|
|
164
|
+
...(input.network === true ? { network: true } : {}),
|
|
165
|
+
...(background ? { background: true } : {}),
|
|
166
|
+
...(typeof input.stdin === "string" && input.stdin !== ""
|
|
167
|
+
? { stdin: input.stdin }
|
|
168
|
+
: {}),
|
|
169
|
+
...(typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {}),
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
if (!allowed) {
|
|
173
|
+
throw new Error("工具执行被拒绝")
|
|
174
|
+
}
|
|
175
|
+
context.elog.toolResult("已允许提权")
|
|
176
|
+
}
|
|
177
|
+
return { access: requested, cwd }
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
async executeCode(input, explicitContext) {
|
|
181
|
+
const context = explicitContext ?? getAgentToolContext()
|
|
182
|
+
if (context === null) {
|
|
183
|
+
throw new Error("executeCode 只能在 Agent 工具调用中使用")
|
|
184
|
+
}
|
|
185
|
+
const { access, cwd } = await approve(input, context)
|
|
186
|
+
const result = await sandbox().runAgentCode({
|
|
187
|
+
...input,
|
|
188
|
+
cwd,
|
|
189
|
+
workspaceDir: context.workspaceDir,
|
|
190
|
+
access,
|
|
191
|
+
})
|
|
192
|
+
if (result.exitCode !== 0) {
|
|
193
|
+
context.elog.toolResult(`exit=${String(result.exitCode)}`)
|
|
194
|
+
}
|
|
195
|
+
return result
|
|
196
|
+
},
|
|
197
|
+
async startBackgroundCode(input, explicitContext) {
|
|
198
|
+
const context = explicitContext ?? getAgentToolContext()
|
|
199
|
+
if (context === null) {
|
|
200
|
+
throw new Error("startBackgroundCode 只能在 Agent 工具调用中使用")
|
|
201
|
+
}
|
|
202
|
+
const { access, cwd } = await approve(input, context, true)
|
|
203
|
+
const handle = await sandbox().startAgentCode({
|
|
204
|
+
...input,
|
|
205
|
+
cwd,
|
|
206
|
+
workspaceDir: context.workspaceDir,
|
|
207
|
+
access,
|
|
208
|
+
})
|
|
209
|
+
const info = registerBackgroundTask(handle)
|
|
210
|
+
context.elog.toolResult(`后台任务 ${info.taskId}`)
|
|
211
|
+
return { taskId: info.taskId, sandboxId: info.sandboxId }
|
|
212
|
+
},
|
|
213
|
+
queryTask(taskId?: string) {
|
|
214
|
+
return queryTask(taskId)
|
|
215
|
+
},
|
|
216
|
+
stopTask(taskId: string | undefined, mode: StopMode) {
|
|
217
|
+
return stopTask(taskId, mode)
|
|
218
|
+
},
|
|
219
|
+
async webSearch(query, count) {
|
|
220
|
+
const client = await webClient()
|
|
221
|
+
const result = await client.search(query, count)
|
|
222
|
+
if (!result.isSuccess()) {
|
|
223
|
+
throw webError(result.getCode(), "search")
|
|
224
|
+
}
|
|
225
|
+
return result.getData() ?? []
|
|
226
|
+
},
|
|
227
|
+
async webFetch(url) {
|
|
228
|
+
const client = await webClient()
|
|
229
|
+
const result = await client.fetch(url)
|
|
230
|
+
if (!result.isSuccess()) {
|
|
231
|
+
throw webError(result.getCode(), "fetch")
|
|
232
|
+
}
|
|
233
|
+
const page = result.getData()
|
|
234
|
+
if (page === null) {
|
|
235
|
+
throw webError(null, "fetch")
|
|
236
|
+
}
|
|
237
|
+
return page
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 插件 console 拦截。
|
|
3
|
+
* 插件内 console.* 不直接写终端:先记录一条 error,再按原方法级别输出原始消息。
|
|
4
|
+
* 拦截只覆盖插件 import / register / run 期间,结束后恢复。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { format } from "node:util"
|
|
8
|
+
|
|
9
|
+
import type { Logger } from "pino"
|
|
10
|
+
|
|
11
|
+
const METHODS = ["log", "info", "debug", "warn", "error", "trace", "dir", "table"] as const
|
|
12
|
+
|
|
13
|
+
type GuardedMethod = (typeof METHODS)[number]
|
|
14
|
+
|
|
15
|
+
const LEVEL_OF: Readonly<Record<GuardedMethod, "info" | "debug" | "warn" | "error" | "trace">> = {
|
|
16
|
+
log: "info",
|
|
17
|
+
info: "info",
|
|
18
|
+
debug: "debug",
|
|
19
|
+
warn: "warn",
|
|
20
|
+
error: "error",
|
|
21
|
+
trace: "trace",
|
|
22
|
+
dir: "info",
|
|
23
|
+
table: "info",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function withConsoleGuard<T>(
|
|
27
|
+
pluginName: string,
|
|
28
|
+
logger: Logger,
|
|
29
|
+
task: () => T | Promise<T>,
|
|
30
|
+
): Promise<T> {
|
|
31
|
+
const target = console as unknown as Record<string, (...args: unknown[]) => void>
|
|
32
|
+
const originals = new Map<string, (...args: unknown[]) => void>()
|
|
33
|
+
|
|
34
|
+
const emit = (level: "info" | "debug" | "warn" | "error" | "trace", args: unknown[]): void => {
|
|
35
|
+
logger.error(`插件 [${pluginName}] 使用了不推荐的 console logger !`)
|
|
36
|
+
const text = `${pluginName} ${format(...args)}`
|
|
37
|
+
logger[level](text)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const method of METHODS) {
|
|
41
|
+
const original = target[method]
|
|
42
|
+
if (typeof original !== "function") {
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
originals.set(method, original)
|
|
46
|
+
target[method] = (...args: unknown[]): void => {
|
|
47
|
+
emit(LEVEL_OF[method], args)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const originalAssert = console.assert
|
|
52
|
+
console.assert = (condition: unknown, ...args: unknown[]): void => {
|
|
53
|
+
if (!condition) {
|
|
54
|
+
emit("error", args)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
return await task()
|
|
60
|
+
} finally {
|
|
61
|
+
for (const [method, original] of originals) {
|
|
62
|
+
target[method] = original
|
|
63
|
+
}
|
|
64
|
+
console.assert = originalAssert
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// 插件数据访问:依赖关系链上的插件可互访 data 目录
|
|
2
|
+
|
|
3
|
+
import type { HostFileApi } from "../api/host/fs.ts"
|
|
4
|
+
import type { ResolvedPaths } from "../platform/path.ts"
|
|
5
|
+
import { createPluginFileApi } from "./fs.ts"
|
|
6
|
+
import { ensurePluginDataDir } from "./runtime.ts"
|
|
7
|
+
|
|
8
|
+
interface PluginNode {
|
|
9
|
+
readonly id: string
|
|
10
|
+
readonly deps: readonly string[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class PluginDataService {
|
|
14
|
+
readonly #paths: ResolvedPaths
|
|
15
|
+
readonly #edges = new Map<string, Set<string>>()
|
|
16
|
+
|
|
17
|
+
constructor(paths: ResolvedPaths) {
|
|
18
|
+
this.#paths = paths
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 建立无向依赖图;A 依赖 B 后双方都可访问对方 data */
|
|
22
|
+
setPlugins(plugins: readonly PluginNode[]): void {
|
|
23
|
+
this.#edges.clear()
|
|
24
|
+
for (const plugin of plugins) {
|
|
25
|
+
this.#edges.set(plugin.id, this.#edges.get(plugin.id) ?? new Set())
|
|
26
|
+
for (const dep of plugin.deps) {
|
|
27
|
+
this.#link(plugin.id, dep)
|
|
28
|
+
this.#link(dep, plugin.id)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#link(left: string, right: string): void {
|
|
34
|
+
const set = this.#edges.get(left) ?? new Set<string>()
|
|
35
|
+
set.add(right)
|
|
36
|
+
this.#edges.set(left, set)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
canAccess(from: string, to: string): boolean {
|
|
40
|
+
if (from === to) {
|
|
41
|
+
return true
|
|
42
|
+
}
|
|
43
|
+
const seen = new Set<string>([from])
|
|
44
|
+
const queue = [from]
|
|
45
|
+
while (queue.length > 0) {
|
|
46
|
+
const current = queue.shift()
|
|
47
|
+
if (current === undefined) {
|
|
48
|
+
break
|
|
49
|
+
}
|
|
50
|
+
for (const next of this.#edges.get(current) ?? []) {
|
|
51
|
+
if (next === to) {
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
if (!seen.has(next)) {
|
|
55
|
+
seen.add(next)
|
|
56
|
+
queue.push(next)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async open(from: string, to: string): Promise<HostFileApi | null> {
|
|
64
|
+
if (!this.canAccess(from, to)) {
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
67
|
+
const root = await ensurePluginDataDir(to, this.#paths)
|
|
68
|
+
return createPluginFileApi(root)
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/plugin/fs.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* HostFileApi 实现:根目录内的相对路径访问。
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { readdir } from "node:fs/promises"
|
|
6
|
+
import { isAbsolute, resolve, sep } from "node:path"
|
|
7
|
+
|
|
8
|
+
import type { HostFileApi, HostFileEntry, HostFileStat, HostFileType } from "../api/host/fs.ts"
|
|
9
|
+
import {
|
|
10
|
+
ensureDir,
|
|
11
|
+
readBytesIfExists,
|
|
12
|
+
readJsonIfExists,
|
|
13
|
+
readTextIfExists,
|
|
14
|
+
removePath,
|
|
15
|
+
statIfExists,
|
|
16
|
+
writeBytesAtomic,
|
|
17
|
+
writeJsonAtomic,
|
|
18
|
+
writeTextAtomic,
|
|
19
|
+
} from "../platform/file.ts"
|
|
20
|
+
|
|
21
|
+
function safePath(root: string, relative: string): string {
|
|
22
|
+
if (typeof relative !== "string" || relative.includes("\0")) {
|
|
23
|
+
throw new Error("文件路径不合法")
|
|
24
|
+
}
|
|
25
|
+
if (isAbsolute(relative)) {
|
|
26
|
+
throw new Error(`不允许绝对路径:${relative}`)
|
|
27
|
+
}
|
|
28
|
+
const target = resolve(root, relative)
|
|
29
|
+
if (target !== root && !target.startsWith(`${root}${sep}`)) {
|
|
30
|
+
throw new Error(`路径越界:${relative}`)
|
|
31
|
+
}
|
|
32
|
+
return target
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function fileTypeOf(isFile: boolean, isDirectory: boolean): HostFileType {
|
|
36
|
+
if (isFile) {
|
|
37
|
+
return "file"
|
|
38
|
+
}
|
|
39
|
+
if (isDirectory) {
|
|
40
|
+
return "directory"
|
|
41
|
+
}
|
|
42
|
+
return "other"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 为某个根目录创建 HostFileApi;root 需已存在或首次写入时自动创建 */
|
|
46
|
+
export function createPluginFileApi(root: string): HostFileApi {
|
|
47
|
+
const base = resolve(root)
|
|
48
|
+
|
|
49
|
+
const api: HostFileApi = {
|
|
50
|
+
root: base,
|
|
51
|
+
async readText(path) {
|
|
52
|
+
return await readTextIfExists(safePath(base, path))
|
|
53
|
+
},
|
|
54
|
+
async readBytes(path) {
|
|
55
|
+
const bytes = await readBytesIfExists(safePath(base, path))
|
|
56
|
+
return bytes === null ? null : new Uint8Array(bytes)
|
|
57
|
+
},
|
|
58
|
+
async readJson<T = unknown>(path: string) {
|
|
59
|
+
return await readJsonIfExists<T>(safePath(base, path))
|
|
60
|
+
},
|
|
61
|
+
async writeText(path, text) {
|
|
62
|
+
await writeTextAtomic(safePath(base, path), text)
|
|
63
|
+
},
|
|
64
|
+
async writeBytes(path, bytes) {
|
|
65
|
+
await writeBytesAtomic(safePath(base, path), bytes)
|
|
66
|
+
},
|
|
67
|
+
async writeJson(path, value) {
|
|
68
|
+
await writeJsonAtomic(safePath(base, path), value)
|
|
69
|
+
},
|
|
70
|
+
async list(path = "") {
|
|
71
|
+
const dir = safePath(base, path)
|
|
72
|
+
const entries = await readdir(dir, { withFileTypes: true })
|
|
73
|
+
const result: HostFileEntry[] = []
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
const info = await statIfExists(resolve(dir, entry.name))
|
|
76
|
+
result.push({
|
|
77
|
+
name: entry.name,
|
|
78
|
+
type: fileTypeOf(entry.isFile(), entry.isDirectory()),
|
|
79
|
+
size: info?.size ?? 0,
|
|
80
|
+
mtimeMs: info?.mtimeMs ?? 0,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
result.sort((left, right) => left.name.localeCompare(right.name))
|
|
84
|
+
return result
|
|
85
|
+
},
|
|
86
|
+
async stat(path) {
|
|
87
|
+
const info = await statIfExists(safePath(base, path))
|
|
88
|
+
if (info === null) {
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
const stat: HostFileStat = {
|
|
92
|
+
type: fileTypeOf(info.isFile(), info.isDirectory()),
|
|
93
|
+
size: info.size,
|
|
94
|
+
mtimeMs: info.mtimeMs,
|
|
95
|
+
}
|
|
96
|
+
return stat
|
|
97
|
+
},
|
|
98
|
+
async mkdir(path) {
|
|
99
|
+
await ensureDir(safePath(base, path))
|
|
100
|
+
},
|
|
101
|
+
async remove(path) {
|
|
102
|
+
await removePath(safePath(base, path))
|
|
103
|
+
},
|
|
104
|
+
async exists(path) {
|
|
105
|
+
return (await statIfExists(safePath(base, path))) !== null
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
return api
|
|
109
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 插件侧模块别名。
|
|
3
|
+
* 插件不在 wellspring 包的解析路径内,用 Node 的 resolve hook 把
|
|
4
|
+
* "wellspring" / "wellspring/..." 映射到本仓库的 src 目录,
|
|
5
|
+
* 这样插件可以 import { onMessage, getHost } from "wellspring/api/host/index.ts"。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { statSync } from "node:fs"
|
|
9
|
+
import { registerHooks } from "node:module"
|
|
10
|
+
import { join, resolve } from "node:path"
|
|
11
|
+
import { pathToFileURL } from "node:url"
|
|
12
|
+
|
|
13
|
+
const PROJECT_ROOT = resolve(import.meta.dirname, "../..")
|
|
14
|
+
const SOURCE_ROOT = join(PROJECT_ROOT, "src")
|
|
15
|
+
const PREFIX = "wellspring/"
|
|
16
|
+
|
|
17
|
+
let installed = false
|
|
18
|
+
|
|
19
|
+
function existingFile(path: string): string | null {
|
|
20
|
+
try {
|
|
21
|
+
const info = statSync(path)
|
|
22
|
+
return info.isFile() ? path : null
|
|
23
|
+
} catch {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveHostTarget(specifier: string): string | null {
|
|
29
|
+
if (specifier === "wellspring") {
|
|
30
|
+
return existingFile(join(SOURCE_ROOT, "api", "host", "index.ts"))
|
|
31
|
+
}
|
|
32
|
+
if (!specifier.startsWith(PREFIX)) {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
const rest = specifier.slice(PREFIX.length)
|
|
36
|
+
if (rest === "" || rest.startsWith("..") || rest.startsWith("/") || rest.includes("\\")) {
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
const direct = existingFile(join(SOURCE_ROOT, rest))
|
|
40
|
+
if (direct !== null) {
|
|
41
|
+
return direct
|
|
42
|
+
}
|
|
43
|
+
return existingFile(join(SOURCE_ROOT, rest, "index.ts"))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 注册一次即可;在加载插件前调用 */
|
|
47
|
+
export function ensureHostAlias(): void {
|
|
48
|
+
if (installed) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
installed = true
|
|
52
|
+
registerHooks({
|
|
53
|
+
resolve(specifier, context, nextResolve) {
|
|
54
|
+
const target = resolveHostTarget(specifier)
|
|
55
|
+
if (target !== null) {
|
|
56
|
+
return { url: pathToFileURL(target).href, shortCircuit: true }
|
|
57
|
+
}
|
|
58
|
+
return nextResolve(specifier, context)
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
}
|