@qvac/harness 0.0.0 → 0.1.1
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.md +198 -0
- package/NOTICE.md +13 -0
- package/README.md +68 -0
- package/SECURITY.md +6 -0
- package/dist/harness.bundle +48339 -0
- package/dist/harness.cjs +18 -0
- package/dist/harness.d.ts +8 -0
- package/dist/prebuilds-manifest.json +379 -0
- package/dist-lib/index.d.ts +240 -0
- package/dist-lib/index.js +1063 -0
- package/dist-lib/lib/audio-transcode-bare.d.ts +2 -0
- package/dist-lib/lib/audio-transcode-bare.js +145 -0
- package/dist-lib/lib/audio-transcode-node.d.ts +2 -0
- package/dist-lib/lib/audio-transcode-node.js +6 -0
- package/dist-lib/lib/capabilities/audio-resample.d.ts +14 -0
- package/dist-lib/lib/capabilities/audio-resample.js +72 -0
- package/dist-lib/lib/capabilities/completion.d.ts +288 -0
- package/dist-lib/lib/capabilities/completion.js +1077 -0
- package/dist-lib/lib/capabilities/download.d.ts +46 -0
- package/dist-lib/lib/capabilities/download.js +124 -0
- package/dist-lib/lib/capabilities/embed.d.ts +20 -0
- package/dist-lib/lib/capabilities/embed.js +13 -0
- package/dist-lib/lib/capabilities/kv-cache.d.ts +13 -0
- package/dist-lib/lib/capabilities/kv-cache.js +21 -0
- package/dist-lib/lib/capabilities/prompt-prefix.d.ts +6 -0
- package/dist-lib/lib/capabilities/prompt-prefix.js +10 -0
- package/dist-lib/lib/capabilities/speech-lexicon.d.ts +5 -0
- package/dist-lib/lib/capabilities/speech-lexicon.js +32 -0
- package/dist-lib/lib/capabilities/speech-text.d.ts +6 -0
- package/dist-lib/lib/capabilities/speech-text.js +79 -0
- package/dist-lib/lib/capabilities/tool-names.d.ts +3 -0
- package/dist-lib/lib/capabilities/tool-names.js +3 -0
- package/dist-lib/lib/capabilities/tool-selection.d.ts +43 -0
- package/dist-lib/lib/capabilities/tool-selection.js +60 -0
- package/dist-lib/lib/capabilities/tool-turns.d.ts +9 -0
- package/dist-lib/lib/capabilities/tool-turns.js +101 -0
- package/dist-lib/lib/capabilities/usage-breakdown.d.ts +27 -0
- package/dist-lib/lib/capabilities/usage-breakdown.js +44 -0
- package/dist-lib/lib/capabilities/vision-history.d.ts +3 -0
- package/dist-lib/lib/capabilities/vision-history.js +21 -0
- package/dist-lib/lib/capabilities/voice.d.ts +78 -0
- package/dist-lib/lib/capabilities/voice.js +189 -0
- package/dist-lib/lib/connect.d.ts +76 -0
- package/dist-lib/lib/connect.js +789 -0
- package/dist-lib/lib/context-window.d.ts +7 -0
- package/dist-lib/lib/context-window.js +90 -0
- package/dist-lib/lib/contract-types.d.ts +210 -0
- package/dist-lib/lib/contract-types.js +1 -0
- package/dist-lib/lib/diagram-repair.d.ts +21 -0
- package/dist-lib/lib/diagram-repair.js +58 -0
- package/dist-lib/lib/dns-lookup.d.ts +1 -0
- package/dist-lib/lib/dns-lookup.js +13 -0
- package/dist-lib/lib/error-fields.d.ts +2 -0
- package/dist-lib/lib/error-fields.js +8 -0
- package/dist-lib/lib/fetch-node.d.ts +1 -0
- package/dist-lib/lib/fetch-node.js +1 -0
- package/dist-lib/lib/hardware/capability-probe.d.ts +9 -0
- package/dist-lib/lib/hardware/capability-probe.js +99 -0
- package/dist-lib/lib/hardware/gpu-classify.d.ts +2 -0
- package/dist-lib/lib/hardware/gpu-classify.js +32 -0
- package/dist-lib/lib/hardware/gpu-probe-addon.d.ts +2 -0
- package/dist-lib/lib/hardware/gpu-probe-addon.js +42 -0
- package/dist-lib/lib/hardware/gpu-stats.bare.d.ts +1 -0
- package/dist-lib/lib/hardware/gpu-stats.bare.js +24 -0
- package/dist-lib/lib/hardware/gpu-stats.stub.d.ts +1 -0
- package/dist-lib/lib/hardware/gpu-stats.stub.js +3 -0
- package/dist-lib/lib/hardware/memory-budget.d.ts +8 -0
- package/dist-lib/lib/hardware/memory-budget.js +1 -0
- package/dist-lib/lib/hardware/parsers.d.ts +7 -0
- package/dist-lib/lib/hardware/parsers.js +41 -0
- package/dist-lib/lib/headers-bare.d.ts +1 -0
- package/dist-lib/lib/headers-bare.js +1 -0
- package/dist-lib/lib/headers-node.d.ts +4 -0
- package/dist-lib/lib/headers-node.js +1 -0
- package/dist-lib/lib/helpers/buffer-burst.d.ts +1 -0
- package/dist-lib/lib/helpers/buffer-burst.js +20 -0
- package/dist-lib/lib/helpers/control-tokens.d.ts +7 -0
- package/dist-lib/lib/helpers/control-tokens.js +32 -0
- package/dist-lib/lib/helpers/join-guard.d.ts +6 -0
- package/dist-lib/lib/helpers/join-guard.js +46 -0
- package/dist-lib/lib/helpers/markup-stream-filter.d.ts +5 -0
- package/dist-lib/lib/helpers/markup-stream-filter.js +75 -0
- package/dist-lib/lib/helpers/race-abort.d.ts +5 -0
- package/dist-lib/lib/helpers/race-abort.js +18 -0
- package/dist-lib/lib/helpers/retry-memo.d.ts +5 -0
- package/dist-lib/lib/helpers/retry-memo.js +12 -0
- package/dist-lib/lib/helpers/run-outcome.d.ts +33 -0
- package/dist-lib/lib/helpers/run-outcome.js +204 -0
- package/dist-lib/lib/helpers/spawn-path.d.ts +5 -0
- package/dist-lib/lib/helpers/spawn-path.js +57 -0
- package/dist-lib/lib/helpers/text-coalescer.d.ts +15 -0
- package/dist-lib/lib/helpers/text-coalescer.js +34 -0
- package/dist-lib/lib/helpers/thinking.d.ts +8 -0
- package/dist-lib/lib/helpers/thinking.js +95 -0
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +20 -0
- package/dist-lib/lib/helpers/tool-call-markup.js +201 -0
- package/dist-lib/lib/helpers/tool-intent.d.ts +7 -0
- package/dist-lib/lib/helpers/tool-intent.js +21 -0
- package/dist-lib/lib/helpers/tool-label.d.ts +8 -0
- package/dist-lib/lib/helpers/tool-label.js +131 -0
- package/dist-lib/lib/hooks/built-in/arg-repair.d.ts +5 -0
- package/dist-lib/lib/hooks/built-in/arg-repair.js +88 -0
- package/dist-lib/lib/hooks/built-in/egress.d.ts +5 -0
- package/dist-lib/lib/hooks/built-in/egress.js +29 -0
- package/dist-lib/lib/hooks/built-in/index.d.ts +3 -0
- package/dist-lib/lib/hooks/built-in/index.js +3 -0
- package/dist-lib/lib/hooks/built-in/skill-catalog.d.ts +10 -0
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +17 -0
- package/dist-lib/lib/hooks/pipeline.d.ts +20 -0
- package/dist-lib/lib/hooks/pipeline.js +49 -0
- package/dist-lib/lib/hooks/registry.d.ts +2 -0
- package/dist-lib/lib/hooks/registry.js +6 -0
- package/dist-lib/lib/hooks/run.d.ts +5 -0
- package/dist-lib/lib/hooks/run.js +83 -0
- package/dist-lib/lib/hooks/types.d.ts +57 -0
- package/dist-lib/lib/hooks/types.js +1 -0
- package/dist-lib/lib/log.d.ts +5 -0
- package/dist-lib/lib/log.js +37 -0
- package/dist-lib/lib/model-download-progress.d.ts +22 -0
- package/dist-lib/lib/model-download-progress.js +95 -0
- package/dist-lib/lib/models/context.d.ts +5 -0
- package/dist-lib/lib/models/context.js +12 -0
- package/dist-lib/lib/models/embed-config.d.ts +14 -0
- package/dist-lib/lib/models/embed-config.js +31 -0
- package/dist-lib/lib/models/gpu-selection.d.ts +3 -0
- package/dist-lib/lib/models/gpu-selection.js +26 -0
- package/dist-lib/lib/models/kv-cache.d.ts +4 -0
- package/dist-lib/lib/models/kv-cache.js +6 -0
- package/dist-lib/lib/models/llm-presets.d.ts +10 -0
- package/dist-lib/lib/models/llm-presets.js +58 -0
- package/dist-lib/lib/models/model-catalog-constants.d.ts +29 -0
- package/dist-lib/lib/models/model-catalog-constants.js +103 -0
- package/dist-lib/lib/models/model-catalog.d.ts +125 -0
- package/dist-lib/lib/models/model-catalog.js +371 -0
- package/dist-lib/lib/models/model-delete.d.ts +18 -0
- package/dist-lib/lib/models/model-delete.js +47 -0
- package/dist-lib/lib/models/model-handle.d.ts +18 -0
- package/dist-lib/lib/models/model-handle.js +33 -0
- package/dist-lib/lib/models/model-list.d.ts +42 -0
- package/dist-lib/lib/models/model-list.js +123 -0
- package/dist-lib/lib/models/model-pool.d.ts +116 -0
- package/dist-lib/lib/models/model-pool.js +356 -0
- package/dist-lib/lib/models/model-source.d.ts +6 -0
- package/dist-lib/lib/models/model-source.js +21 -0
- package/dist-lib/lib/models/music-catalog.d.ts +12 -0
- package/dist-lib/lib/models/music-catalog.js +26 -0
- package/dist-lib/lib/models/opencl-cache.d.ts +5 -0
- package/dist-lib/lib/models/opencl-cache.js +50 -0
- package/dist-lib/lib/models/sdk-faults.d.ts +21 -0
- package/dist-lib/lib/models/sdk-faults.js +66 -0
- package/dist-lib/lib/models/tool-dialect.d.ts +2 -0
- package/dist-lib/lib/models/tool-dialect.js +19 -0
- package/dist-lib/lib/models/tts-model-default.d.ts +3 -0
- package/dist-lib/lib/models/tts-model-default.js +13 -0
- package/dist-lib/lib/no-redirect-fetch-bare.d.ts +15 -0
- package/dist-lib/lib/no-redirect-fetch-bare.js +59 -0
- package/dist-lib/lib/no-redirect-fetch-impl.d.ts +39 -0
- package/dist-lib/lib/no-redirect-fetch-impl.js +80 -0
- package/dist-lib/lib/no-redirect-fetch-node.d.ts +4 -0
- package/dist-lib/lib/no-redirect-fetch-node.js +52 -0
- package/dist-lib/lib/prompt.d.ts +22 -0
- package/dist-lib/lib/prompt.js +77 -0
- package/dist-lib/lib/queue.d.ts +9 -0
- package/dist-lib/lib/queue.js +48 -0
- package/dist-lib/lib/resolution-detector.d.ts +5 -0
- package/dist-lib/lib/resolution-detector.js +100 -0
- package/dist-lib/lib/rpc-session.d.ts +8 -0
- package/dist-lib/lib/rpc-session.js +20 -0
- package/dist-lib/lib/sdk-config-loader.node.d.ts +1 -0
- package/dist-lib/lib/sdk-config-loader.node.js +1 -0
- package/dist-lib/lib/sdk-loader.d.ts +30 -0
- package/dist-lib/lib/sdk-loader.js +53 -0
- package/dist-lib/lib/sdk-storage.d.ts +8 -0
- package/dist-lib/lib/sdk-storage.js +24 -0
- package/dist-lib/lib/serve.d.ts +3 -0
- package/dist-lib/lib/serve.js +612 -0
- package/dist-lib/lib/spawn.d.ts +62 -0
- package/dist-lib/lib/spawn.js +71 -0
- package/dist-lib/lib/staging.d.ts +4 -0
- package/dist-lib/lib/staging.js +28 -0
- package/dist-lib/lib/storage.d.ts +18 -0
- package/dist-lib/lib/storage.js +26 -0
- package/dist-lib/lib/tmp-path.d.ts +7 -0
- package/dist-lib/lib/tmp-path.js +17 -0
- package/dist-lib/lib/tool-call-display.d.ts +21 -0
- package/dist-lib/lib/tool-call-display.js +1 -0
- package/dist-lib/lib/tools/catalog.d.ts +13 -0
- package/dist-lib/lib/tools/catalog.js +13 -0
- package/dist-lib/lib/tools/create-documents.d.ts +3 -0
- package/dist-lib/lib/tools/create-documents.js +87 -0
- package/dist-lib/lib/tools/edit-documents.d.ts +3 -0
- package/dist-lib/lib/tools/edit-documents.js +122 -0
- package/dist-lib/lib/tools/edit-image.d.ts +25 -0
- package/dist-lib/lib/tools/edit-image.js +271 -0
- package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +27 -0
- package/dist-lib/lib/tools/exec/adapters/adapter.js +1 -0
- package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +26 -0
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +160 -0
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +7 -0
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +290 -0
- package/dist-lib/lib/tools/exec/adapters/python/loader.d.ts +11 -0
- package/dist-lib/lib/tools/exec/adapters/python/loader.js +94 -0
- package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +47 -0
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +85 -0
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +13 -0
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +129 -0
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +19 -0
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +107 -0
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +75 -0
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +1094 -0
- package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +75 -0
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +273 -0
- package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +38 -0
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +102 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +7 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +188 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/entry.d.ts +1 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +105 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +38 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +41 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-bare.d.ts +2 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-bare.js +30 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +13 -0
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +45 -0
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +12 -0
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +20 -0
- package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +8 -0
- package/dist-lib/lib/tools/exec/obsidian/cli-process.js +74 -0
- package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +18 -0
- package/dist-lib/lib/tools/exec/obsidian/cli.js +153 -0
- package/dist-lib/lib/tools/exec/obsidian/command.d.ts +5 -0
- package/dist-lib/lib/tools/exec/obsidian/command.js +18 -0
- package/dist-lib/lib/tools/exec/obsidian/guidance.d.ts +5 -0
- package/dist-lib/lib/tools/exec/obsidian/guidance.js +18 -0
- package/dist-lib/lib/tools/exec/obsidian/note-name.d.ts +1 -0
- package/dist-lib/lib/tools/exec/obsidian/note-name.js +6 -0
- package/dist-lib/lib/tools/exec/obsidian/output.d.ts +1 -0
- package/dist-lib/lib/tools/exec/obsidian/output.js +31 -0
- package/dist-lib/lib/tools/exec/obsidian/route.d.ts +19 -0
- package/dist-lib/lib/tools/exec/obsidian/route.js +85 -0
- package/dist-lib/lib/tools/exec/obsidian/safe-path.d.ts +3 -0
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +56 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-create.d.ts +1 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +40 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-fs.d.ts +3 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-fs.js +57 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +20 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +171 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-read.d.ts +11 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +119 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-write.d.ts +7 -0
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +134 -0
- package/dist-lib/lib/tools/exec/runner.d.ts +62 -0
- package/dist-lib/lib/tools/exec/runner.js +87 -0
- package/dist-lib/lib/tools/exec/scope.d.ts +3 -0
- package/dist-lib/lib/tools/exec/scope.js +97 -0
- package/dist-lib/lib/tools/exec/shell-argv.d.ts +2 -0
- package/dist-lib/lib/tools/exec/shell-argv.js +48 -0
- package/dist-lib/lib/tools/exec.d.ts +29 -0
- package/dist-lib/lib/tools/exec.js +459 -0
- package/dist-lib/lib/tools/file-glob.d.ts +15 -0
- package/dist-lib/lib/tools/file-glob.js +112 -0
- package/dist-lib/lib/tools/fs/drive.d.ts +26 -0
- package/dist-lib/lib/tools/fs/drive.js +1 -0
- package/dist-lib/lib/tools/fs/glob.d.ts +1 -0
- package/dist-lib/lib/tools/fs/glob.js +34 -0
- package/dist-lib/lib/tools/fs/local.d.ts +2 -0
- package/dist-lib/lib/tools/fs/local.js +29 -0
- package/dist-lib/lib/tools/fs/read.d.ts +2 -0
- package/dist-lib/lib/tools/fs/read.js +17 -0
- package/dist-lib/lib/tools/fs/remote-protocol.d.ts +40 -0
- package/dist-lib/lib/tools/fs/remote-protocol.js +1 -0
- package/dist-lib/lib/tools/fs/remote.d.ts +17 -0
- package/dist-lib/lib/tools/fs/remote.js +131 -0
- package/dist-lib/lib/tools/fs/scope.d.ts +37 -0
- package/dist-lib/lib/tools/fs/scope.js +130 -0
- package/dist-lib/lib/tools/fs/walk.d.ts +15 -0
- package/dist-lib/lib/tools/fs/walk.js +38 -0
- package/dist-lib/lib/tools/fs/write.d.ts +4 -0
- package/dist-lib/lib/tools/fs/write.js +5 -0
- package/dist-lib/lib/tools/generate-image.d.ts +111 -0
- package/dist-lib/lib/tools/generate-image.js +369 -0
- package/dist-lib/lib/tools/generate-music.d.ts +124 -0
- package/dist-lib/lib/tools/generate-music.js +522 -0
- package/dist-lib/lib/tools/grep.d.ts +5 -0
- package/dist-lib/lib/tools/grep.js +158 -0
- package/dist-lib/lib/tools/http-methods.d.ts +1 -0
- package/dist-lib/lib/tools/http-methods.js +1 -0
- package/dist-lib/lib/tools/http-request/encode-fields.d.ts +2 -0
- package/dist-lib/lib/tools/http-request/encode-fields.js +69 -0
- package/dist-lib/lib/tools/http-request/truncate-response.d.ts +1 -0
- package/dist-lib/lib/tools/http-request/truncate-response.js +83 -0
- package/dist-lib/lib/tools/http-request/utils.d.ts +27 -0
- package/dist-lib/lib/tools/http-request/utils.js +84 -0
- package/dist-lib/lib/tools/http-request.d.ts +20 -0
- package/dist-lib/lib/tools/http-request.js +177 -0
- package/dist-lib/lib/tools/mcp/utils.d.ts +47 -0
- package/dist-lib/lib/tools/mcp/utils.js +172 -0
- package/dist-lib/lib/tools/mcp-call.d.ts +14 -0
- package/dist-lib/lib/tools/mcp-call.js +155 -0
- package/dist-lib/lib/tools/model-args.d.ts +1 -0
- package/dist-lib/lib/tools/model-args.js +4 -0
- package/dist-lib/lib/tools/operations/builders.d.ts +17 -0
- package/dist-lib/lib/tools/operations/builders.js +179 -0
- package/dist-lib/lib/tools/operations/compile.d.ts +7 -0
- package/dist-lib/lib/tools/operations/compile.js +92 -0
- package/dist-lib/lib/tools/operations/gmail-message.d.ts +15 -0
- package/dist-lib/lib/tools/operations/gmail-message.js +62 -0
- package/dist-lib/lib/tools/operations/manifest.d.ts +60 -0
- package/dist-lib/lib/tools/operations/manifest.js +50 -0
- package/dist-lib/lib/tools/outbound-guard.d.ts +10 -0
- package/dist-lib/lib/tools/outbound-guard.js +127 -0
- package/dist-lib/lib/tools/private-ip.d.ts +4 -0
- package/dist-lib/lib/tools/private-ip.js +126 -0
- package/dist-lib/lib/tools/rag-search.d.ts +2 -0
- package/dist-lib/lib/tools/rag-search.js +98 -0
- package/dist-lib/lib/tools/read-files.d.ts +5 -0
- package/dist-lib/lib/tools/read-files.js +153 -0
- package/dist-lib/lib/tools/skill.d.ts +10 -0
- package/dist-lib/lib/tools/skill.js +123 -0
- package/dist-lib/lib/tools/skills/auth-resolver.d.ts +43 -0
- package/dist-lib/lib/tools/skills/auth-resolver.js +121 -0
- package/dist-lib/lib/tools/skills/bin-check.d.ts +9 -0
- package/dist-lib/lib/tools/skills/bin-check.js +56 -0
- package/dist-lib/lib/tools/skills/bundled-dir.d.ts +2 -0
- package/dist-lib/lib/tools/skills/bundled-dir.js +117 -0
- package/dist-lib/lib/tools/skills/bundled-hash.d.ts +4 -0
- package/dist-lib/lib/tools/skills/bundled-hash.js +12 -0
- package/dist-lib/lib/tools/skills/catalog.d.ts +60 -0
- package/dist-lib/lib/tools/skills/catalog.js +203 -0
- package/dist-lib/lib/tools/skills/cli-argv.d.ts +9 -0
- package/dist-lib/lib/tools/skills/cli-argv.js +147 -0
- package/dist-lib/lib/tools/skills/cli-schema-check.d.ts +3 -0
- package/dist-lib/lib/tools/skills/cli-schema-check.js +62 -0
- package/dist-lib/lib/tools/skills/cli-schema-load.d.ts +4 -0
- package/dist-lib/lib/tools/skills/cli-schema-load.js +90 -0
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +30 -0
- package/dist-lib/lib/tools/skills/cli-schema-types.js +1 -0
- package/dist-lib/lib/tools/skills/cli-schema.d.ts +7 -0
- package/dist-lib/lib/tools/skills/cli-schema.js +35 -0
- package/dist-lib/lib/tools/skills/credential-check.d.ts +20 -0
- package/dist-lib/lib/tools/skills/credential-check.js +94 -0
- package/dist-lib/lib/tools/skills/credential-grants.d.ts +9 -0
- package/dist-lib/lib/tools/skills/credential-grants.js +25 -0
- package/dist-lib/lib/tools/skills/credential-health.d.ts +8 -0
- package/dist-lib/lib/tools/skills/credential-health.js +51 -0
- package/dist-lib/lib/tools/skills/credential-slots.d.ts +7 -0
- package/dist-lib/lib/tools/skills/credential-slots.js +8 -0
- package/dist-lib/lib/tools/skills/frontmatter-parser.d.ts +23 -0
- package/dist-lib/lib/tools/skills/frontmatter-parser.js +68 -0
- package/dist-lib/lib/tools/skills/index.d.ts +5 -0
- package/dist-lib/lib/tools/skills/index.js +5 -0
- package/dist-lib/lib/tools/skills/manifest-parse.d.ts +12 -0
- package/dist-lib/lib/tools/skills/manifest-parse.js +199 -0
- package/dist-lib/lib/tools/skills/manifest.d.ts +47 -0
- package/dist-lib/lib/tools/skills/manifest.js +1 -0
- package/dist-lib/lib/tools/skills/mention-name.d.ts +1 -0
- package/dist-lib/lib/tools/skills/mention-name.js +4 -0
- package/dist-lib/lib/tools/skills/mention.d.ts +6 -0
- package/dist-lib/lib/tools/skills/mention.js +42 -0
- package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +16 -0
- package/dist-lib/lib/tools/skills/obsidian-readiness.js +95 -0
- package/dist-lib/lib/tools/skills/overlay-crud.d.ts +18 -0
- package/dist-lib/lib/tools/skills/overlay-crud.js +98 -0
- package/dist-lib/lib/tools/skills/overlay-dir.d.ts +2 -0
- package/dist-lib/lib/tools/skills/overlay-dir.js +22 -0
- package/dist-lib/lib/tools/skills/overlay-watch.d.ts +13 -0
- package/dist-lib/lib/tools/skills/overlay-watch.js +44 -0
- package/dist-lib/lib/tools/skills/scope-key.d.ts +7 -0
- package/dist-lib/lib/tools/skills/scope-key.js +7 -0
- package/dist-lib/lib/tools/skills/setup-routes.d.ts +4 -0
- package/dist-lib/lib/tools/skills/setup-routes.js +14 -0
- package/dist-lib/lib/tools/skills/skill-settings.d.ts +16 -0
- package/dist-lib/lib/tools/skills/skill-settings.js +33 -0
- package/dist-lib/lib/tools/skills/skill-state-constants.d.ts +12 -0
- package/dist-lib/lib/tools/skills/skill-state-constants.js +14 -0
- package/dist-lib/lib/tools/skills/skill-state.d.ts +40 -0
- package/dist-lib/lib/tools/skills/skill-state.js +117 -0
- package/dist-lib/lib/tools/skills/tool-grants.d.ts +5 -0
- package/dist-lib/lib/tools/skills/tool-grants.js +14 -0
- package/dist-lib/lib/tools/toolsets.d.ts +8 -0
- package/dist-lib/lib/tools/toolsets.js +20 -0
- package/dist-lib/lib/tools/url-guard.d.ts +8 -0
- package/dist-lib/lib/tools/url-guard.js +40 -0
- package/dist-lib/lib/tools/web-fetch.d.ts +8 -0
- package/dist-lib/lib/tools/web-fetch.js +302 -0
- package/dist-lib/lib/tools/web-search.d.ts +6 -0
- package/dist-lib/lib/tools/web-search.js +55 -0
- package/dist-lib/lib/transport.d.ts +12 -0
- package/dist-lib/lib/transport.js +59 -0
- package/dist-lib/lib/user-agent.d.ts +1 -0
- package/dist-lib/lib/user-agent.js +1 -0
- package/dist-lib/lib/validating-lookup.d.ts +17 -0
- package/dist-lib/lib/validating-lookup.js +25 -0
- package/dist-lib/lib/wav-encode.d.ts +5 -0
- package/dist-lib/lib/wav-encode.js +24 -0
- package/dist-lib/lib/web-fetch/embedded-state.d.ts +1 -0
- package/dist-lib/lib/web-fetch/embedded-state.js +131 -0
- package/dist-lib/lib/web-fetch/extract.d.ts +6 -0
- package/dist-lib/lib/web-fetch/extract.js +65 -0
- package/dist-lib/lib/web-fetch/images.d.ts +3 -0
- package/dist-lib/lib/web-fetch/images.js +43 -0
- package/dist-lib/lib/web-fetch/video.d.ts +5 -0
- package/dist-lib/lib/web-fetch/video.js +42 -0
- package/dist-lib/lib/web-search/engine-registry.d.ts +7 -0
- package/dist-lib/lib/web-search/engine-registry.js +17 -0
- package/dist-lib/lib/web-search/engines/aol.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/aol.js +81 -0
- package/dist-lib/lib/web-search/engines/bing.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/bing.js +111 -0
- package/dist-lib/lib/web-search/engines/brave.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/brave.js +59 -0
- package/dist-lib/lib/web-search/engines/duckduckgo.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/duckduckgo.js +112 -0
- package/dist-lib/lib/web-search/engines/google.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/google.js +92 -0
- package/dist-lib/lib/web-search/engines/wikipedia.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/wikipedia.js +37 -0
- package/dist-lib/lib/web-search/engines/yahoo.d.ts +3 -0
- package/dist-lib/lib/web-search/engines/yahoo.js +81 -0
- package/dist-lib/lib/web-search/fetch-html.d.ts +25 -0
- package/dist-lib/lib/web-search/fetch-html.js +97 -0
- package/dist-lib/lib/web-search/format-results.d.ts +3 -0
- package/dist-lib/lib/web-search/format-results.js +14 -0
- package/dist-lib/lib/web-search/html-helpers.d.ts +3 -0
- package/dist-lib/lib/web-search/html-helpers.js +24 -0
- package/dist-lib/lib/web-search/merge-results.d.ts +2 -0
- package/dist-lib/lib/web-search/merge-results.js +53 -0
- package/dist-lib/lib/web-search/orchestrator.d.ts +14 -0
- package/dist-lib/lib/web-search/orchestrator.js +50 -0
- package/dist-lib/lib/web-search/types.d.ts +34 -0
- package/dist-lib/lib/web-search/types.js +1 -0
- package/dist-lib/lib/web-search/url-helpers.d.ts +1 -0
- package/dist-lib/lib/web-search/url-helpers.js +11 -0
- package/dist-lib/lib/web-search/url-normalize.d.ts +1 -0
- package/dist-lib/lib/web-search/url-normalize.js +25 -0
- package/dist-lib/lib/web-search/user-agents.d.ts +3 -0
- package/dist-lib/lib/web-search/user-agents.js +3 -0
- package/dist-lib/lib/wire.d.ts +50 -0
- package/dist-lib/lib/wire.js +145 -0
- package/dist-lib/schema/baseline.d.ts +8 -0
- package/dist-lib/schema/baseline.js +35 -0
- package/dist-lib/schema/build.d.ts +1 -0
- package/dist-lib/schema/build.js +39 -0
- package/dist-lib/schema/generate.d.ts +20 -0
- package/dist-lib/schema/generate.js +149 -0
- package/dist-lib/schema/harness.d.ts +2 -0
- package/dist-lib/schema/harness.js +745 -0
- package/dist-lib/schema/release-tags.d.ts +5 -0
- package/dist-lib/schema/release-tags.js +58 -0
- package/dist-lib/spec/bind.d.ts +2 -0
- package/dist-lib/spec/bind.js +124 -0
- package/dist-lib/spec/calls.d.ts +3 -0
- package/dist-lib/spec/calls.js +41 -0
- package/dist-lib/spec/hrpc/hrpc.json +356 -0
- package/dist-lib/spec/hrpc/index.d.ts +3 -0
- package/dist-lib/spec/hrpc/index.js +463 -0
- package/dist-lib/spec/hrpc/messages.d.ts +4 -0
- package/dist-lib/spec/hrpc/messages.js +3102 -0
- package/dist-lib/spec/hyperschema/index.js +3102 -0
- package/dist-lib/spec/hyperschema/schema.json +2290 -0
- package/dist-lib/spec/hyperschema/types.d.ts +554 -0
- package/dist-lib/spec/index.d.ts +8 -0
- package/dist-lib/spec/index.js +1 -0
- package/package.json +418 -1
- package/wheels/cffi-2.0.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
- package/wheels/cryptography-47.0.0-cp314-abi3-pyemscripten_2026_0_wasm32.whl +0 -0
- package/wheels/defusedxml-0.7.1-py2.py3-none-any.whl +0 -0
- package/wheels/et_xmlfile-2.0.0-py3-none-any.whl +0 -0
- package/wheels/fonttools-4.62.1-py3-none-any.whl +0 -0
- package/wheels/fpdf2-2.8.8-py3-none-any.whl +0 -0
- package/wheels/lxml-6.0.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
- package/wheels/manifest.json +155 -0
- package/wheels/micropip-0.11.1-py3-none-any.whl +0 -0
- package/wheels/openpyxl-3.1.5-py2.py3-none-any.whl +0 -0
- package/wheels/pillow-12.2.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
- package/wheels/pycparser-3.0-py3-none-any.whl +0 -0
- package/wheels/pypdf-6.15.0-py3-none-any.whl +0 -0
- package/wheels/python_docx-1.2.0-py3-none-any.whl +0 -0
- package/wheels/python_pptx-1.0.2-py3-none-any.whl +0 -0
- package/wheels/six-1.17.0-py2.py3-none-any.whl +0 -0
- package/wheels/typing_extensions-4.15.0-py3-none-any.whl +0 -0
- package/wheels/xlsxwriter-3.2.9-py3-none-any.whl +0 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { readLines } from "./fs/read.js";
|
|
2
|
+
import { isDriveReadTruncated } from "./fs/remote.js";
|
|
3
|
+
import { canonicalDenyRoots, defaultRealpath, resolveKey } from "./fs/scope.js";
|
|
4
|
+
export const DEFAULT_READ_CHARS = 8192;
|
|
5
|
+
export const MAX_READ_CHARS = 65_536;
|
|
6
|
+
function lineIndex(value) {
|
|
7
|
+
if (value === undefined || value === null)
|
|
8
|
+
return undefined;
|
|
9
|
+
const n = Number(value);
|
|
10
|
+
if (!Number.isInteger(n) || n < 1)
|
|
11
|
+
return null;
|
|
12
|
+
return n;
|
|
13
|
+
}
|
|
14
|
+
function fileRequests(raw) {
|
|
15
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
16
|
+
return null;
|
|
17
|
+
const files = [];
|
|
18
|
+
for (const entry of raw) {
|
|
19
|
+
const record = entry;
|
|
20
|
+
if (typeof record?.path !== 'string' || !record.path.trim())
|
|
21
|
+
return null;
|
|
22
|
+
const startLine = lineIndex(record.startLine);
|
|
23
|
+
const endLine = lineIndex(record.endLine);
|
|
24
|
+
if (startLine === null || endLine === null)
|
|
25
|
+
return null;
|
|
26
|
+
files.push({
|
|
27
|
+
path: record.path,
|
|
28
|
+
...(startLine !== undefined ? { startLine } : {}),
|
|
29
|
+
...(endLine !== undefined ? { endLine } : {})
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return files;
|
|
33
|
+
}
|
|
34
|
+
async function slice(drive, key, request, maxChars) {
|
|
35
|
+
const first = request.startLine ?? 1;
|
|
36
|
+
const last = request.endLine ?? Infinity;
|
|
37
|
+
const kept = [];
|
|
38
|
+
let length = 0;
|
|
39
|
+
let index = 0;
|
|
40
|
+
let capped = false;
|
|
41
|
+
try {
|
|
42
|
+
for await (const line of readLines(drive, key)) {
|
|
43
|
+
if (++index < first)
|
|
44
|
+
continue;
|
|
45
|
+
if (index > last)
|
|
46
|
+
break;
|
|
47
|
+
kept.push(line);
|
|
48
|
+
length += line.length + (kept.length > 1 ? 1 : 0);
|
|
49
|
+
if (length > maxChars)
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (!isDriveReadTruncated(err))
|
|
55
|
+
throw err;
|
|
56
|
+
capped = true;
|
|
57
|
+
}
|
|
58
|
+
const text = kept.join('\n');
|
|
59
|
+
return text.length > maxChars || capped
|
|
60
|
+
? { content: text.slice(0, maxChars), truncated: true }
|
|
61
|
+
: { content: text };
|
|
62
|
+
}
|
|
63
|
+
export function createReadFilesTool({ realpath = defaultRealpath, deniedRoots } = {}) {
|
|
64
|
+
return {
|
|
65
|
+
name: 'read_files',
|
|
66
|
+
toolset: 'files',
|
|
67
|
+
description: 'Read the contents of one or more files inside the granted directories. ' +
|
|
68
|
+
'Optionally slice by 1-indexed line range. Failures are reported per file.',
|
|
69
|
+
parameters: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
files: {
|
|
73
|
+
type: 'array',
|
|
74
|
+
description: 'Files to read',
|
|
75
|
+
items: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
path: { type: 'string', description: 'File path' },
|
|
79
|
+
startLine: { type: 'number', description: 'Optional 1-indexed first line' },
|
|
80
|
+
endLine: { type: 'number', description: 'Optional 1-indexed last line (inclusive)' }
|
|
81
|
+
},
|
|
82
|
+
required: ['path']
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
maxChars: {
|
|
86
|
+
type: 'number',
|
|
87
|
+
description: `Optional per-file cap in characters (default ${DEFAULT_READ_CHARS}, max ${MAX_READ_CHARS})`
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
required: ['files']
|
|
91
|
+
},
|
|
92
|
+
effect: () => 'read',
|
|
93
|
+
autoApprove: true,
|
|
94
|
+
rootsRequired: true,
|
|
95
|
+
validate(rawArgs) {
|
|
96
|
+
if (!fileRequests(rawArgs.files)) {
|
|
97
|
+
return 'files must be a non-empty array of { path } with optional positive integer startLine/endLine';
|
|
98
|
+
}
|
|
99
|
+
const maxChars = rawArgs.maxChars;
|
|
100
|
+
if (maxChars !== undefined && maxChars !== null) {
|
|
101
|
+
const n = Number(maxChars);
|
|
102
|
+
if (!Number.isInteger(n) || n <= 0 || n > MAX_READ_CHARS) {
|
|
103
|
+
return `maxChars must be a positive integer ≤ ${MAX_READ_CHARS}`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
},
|
|
108
|
+
resource(args) {
|
|
109
|
+
const files = fileRequests(args.files);
|
|
110
|
+
if (!files)
|
|
111
|
+
return null;
|
|
112
|
+
const label = files
|
|
113
|
+
.map((f) => f.path)
|
|
114
|
+
.slice(0, 3)
|
|
115
|
+
.join(', ');
|
|
116
|
+
return {
|
|
117
|
+
kind: 'file',
|
|
118
|
+
label: files.length > 3 ? `${label} (+${files.length - 3} more)` : label
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
async execute(rawArgs, ctx) {
|
|
122
|
+
const roots = ctx?.fsRoots ?? [];
|
|
123
|
+
const requests = fileRequests(rawArgs.files);
|
|
124
|
+
if (!requests)
|
|
125
|
+
return { error: 'files must be a non-empty array of { path }' };
|
|
126
|
+
const maxChars = rawArgs.maxChars ? Number(rawArgs.maxChars) : DEFAULT_READ_CHARS;
|
|
127
|
+
const files = [];
|
|
128
|
+
const failed = [];
|
|
129
|
+
const deny = await canonicalDenyRoots(deniedRoots, realpath);
|
|
130
|
+
for (const request of requests) {
|
|
131
|
+
const resolved = await resolveKey(request.path, roots, realpath, deny);
|
|
132
|
+
if ('error' in resolved) {
|
|
133
|
+
failed.push({ path: request.path, error: resolved.error });
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
if ((await resolved.root.drive.entry(resolved.key)) === null) {
|
|
138
|
+
failed.push({ path: request.path, error: `no such file: ${request.path}` });
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
files.push({
|
|
142
|
+
path: request.path,
|
|
143
|
+
...(await slice(resolved.root.drive, resolved.key, request, maxChars))
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
failed.push({ path: request.path, error: err.message });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { files, ...(failed.length ? { failed } : {}) };
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../capabilities/completion.ts';
|
|
2
|
+
import { type CredentialStoreFn } from './skills/auth-resolver.ts';
|
|
3
|
+
export type ReadSkillFn = (name: string, file: string) => Promise<string> | string;
|
|
4
|
+
export interface SkillToolOptions {
|
|
5
|
+
skillsDir?: string;
|
|
6
|
+
overlayDir?: string | null;
|
|
7
|
+
read?: ReadSkillFn;
|
|
8
|
+
credentialStore?: CredentialStoreFn;
|
|
9
|
+
}
|
|
10
|
+
export declare function createSkillTool({ skillsDir, overlayDir, read, credentialStore }?: SkillToolOptions): ToolDefinition;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import b4a from 'b4a';
|
|
3
|
+
import Localdrive from 'localdrive';
|
|
4
|
+
import { OBSIDIAN_VAULT_MODE_NOTE } from "./exec/obsidian/guidance.js";
|
|
5
|
+
import { unquoteArg } from "./model-args.js";
|
|
6
|
+
import { credentialRecord } from "./skills/auth-resolver.js";
|
|
7
|
+
import { resolveSkillsOverlayDir } from "./skills/overlay-dir.js";
|
|
8
|
+
import { parseSkillFrontMatter } from "./skills/frontmatter-parser.js";
|
|
9
|
+
import { parseToolGrant } from "./skills/tool-grants.js";
|
|
10
|
+
import { shellEscapeArg } from "./exec/shell-argv.js";
|
|
11
|
+
import { isObsidianVaultPath, OBSIDIAN_SKILL_NAME, OBSIDIAN_VAULT_PATH_CREDENTIAL_KEY } from "./skills/obsidian-readiness.js";
|
|
12
|
+
const MAIN_FILE = 'SKILL.md';
|
|
13
|
+
function resolveSkillDir(root, name) {
|
|
14
|
+
if (!root)
|
|
15
|
+
return null;
|
|
16
|
+
return path.resolve(root, name);
|
|
17
|
+
}
|
|
18
|
+
function toolNote(tools) {
|
|
19
|
+
if (!tools?.length)
|
|
20
|
+
return null;
|
|
21
|
+
const grants = [];
|
|
22
|
+
for (const entry of tools) {
|
|
23
|
+
const { name } = parseToolGrant(entry);
|
|
24
|
+
if (!grants.includes(name))
|
|
25
|
+
grants.push(name);
|
|
26
|
+
}
|
|
27
|
+
return [
|
|
28
|
+
`You can now call: ${grants.join(', ')}.`,
|
|
29
|
+
'Do this work yourself with those tools in this turn — never tell the user to install ' +
|
|
30
|
+
'anything, open another app, or run code themselves.'
|
|
31
|
+
].join(' ');
|
|
32
|
+
}
|
|
33
|
+
async function listSkillFiles(drives, name) {
|
|
34
|
+
const files = new Set();
|
|
35
|
+
for (const drive of drives) {
|
|
36
|
+
if (!drive)
|
|
37
|
+
continue;
|
|
38
|
+
try {
|
|
39
|
+
for await (const entry of drive.list(`/${name}`)) {
|
|
40
|
+
const rel = String(entry.key).slice(name.length + 2);
|
|
41
|
+
if (rel && rel !== 'SKILL.md')
|
|
42
|
+
files.add(rel);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return [...files].sort();
|
|
49
|
+
}
|
|
50
|
+
async function vaultModeNote(name, credentialStore, ctx) {
|
|
51
|
+
if (name !== OBSIDIAN_SKILL_NAME || !credentialStore)
|
|
52
|
+
return null;
|
|
53
|
+
const record = credentialRecord(await credentialStore(OBSIDIAN_VAULT_PATH_CREDENTIAL_KEY, { ...ctx, skills: [name] }));
|
|
54
|
+
const token = record?.token?.trim();
|
|
55
|
+
if (!token || !isObsidianVaultPath(token))
|
|
56
|
+
return null;
|
|
57
|
+
return OBSIDIAN_VAULT_MODE_NOTE;
|
|
58
|
+
}
|
|
59
|
+
export function createSkillTool({ skillsDir, overlayDir = resolveSkillsOverlayDir(), read, credentialStore } = {}) {
|
|
60
|
+
const baseDrive = skillsDir ? new Localdrive(skillsDir) : null;
|
|
61
|
+
const overlayDrive = overlayDir ? new Localdrive(overlayDir) : null;
|
|
62
|
+
async function resolveSkill(name, file) {
|
|
63
|
+
if (read)
|
|
64
|
+
return { raw: await read(name, file), root: skillsDir };
|
|
65
|
+
for (const [drive, root] of [
|
|
66
|
+
[overlayDrive, overlayDir],
|
|
67
|
+
[baseDrive, skillsDir]
|
|
68
|
+
]) {
|
|
69
|
+
const buf = drive ? await drive.get(`/${name}/${file}`) : null;
|
|
70
|
+
if (buf)
|
|
71
|
+
return { raw: b4a.toString(buf, 'utf8'), root: root ?? undefined };
|
|
72
|
+
}
|
|
73
|
+
throw new Error(`ENOENT: ${name}/${file}`);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
name: 'skill',
|
|
77
|
+
toolset: null,
|
|
78
|
+
description: 'Load the instructions for a named skill. Call this FIRST, before any other tool, whenever a listed skill covers the capability you are about to use. When loaded instructions point to another file in the skill, call this again with `name` and `file` to load it before doing the work it covers.',
|
|
79
|
+
parameters: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
properties: {
|
|
82
|
+
name: { type: 'string', description: "The skill's name" },
|
|
83
|
+
file: {
|
|
84
|
+
type: 'string',
|
|
85
|
+
description: "Path of a file inside the skill to load, exactly as the skill's instructions name it (e.g. 'references/edit.md'). Omit to load the main instructions."
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
required: ['name']
|
|
89
|
+
},
|
|
90
|
+
effect: () => 'read',
|
|
91
|
+
autoApprove: true,
|
|
92
|
+
async execute(args, ctx) {
|
|
93
|
+
const name = unquoteArg(args.name ?? args.skill_name ?? args.skill);
|
|
94
|
+
const fileArg = args.file ?? args.path ?? args.filename;
|
|
95
|
+
const file = fileArg === undefined || fileArg === null ? MAIN_FILE : unquoteArg(fileArg);
|
|
96
|
+
if (file.startsWith('/') || file.includes('\\') || file.split('/').includes('..')) {
|
|
97
|
+
return { error: `file not found in skill ${name}: ${file}` };
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const { raw, root } = await resolveSkill(name, file === '' ? MAIN_FILE : file);
|
|
101
|
+
const { body, meta } = parseSkillFrontMatter(raw);
|
|
102
|
+
const dir = resolveSkillDir(root, name);
|
|
103
|
+
const content = dir ? body.replaceAll('{{SKILL_DIR}}', shellEscapeArg(dir)) : body;
|
|
104
|
+
const notes = [
|
|
105
|
+
toolNote(meta.tools),
|
|
106
|
+
await vaultModeNote(name, credentialStore, ctx)
|
|
107
|
+
].filter(Boolean);
|
|
108
|
+
return {
|
|
109
|
+
name,
|
|
110
|
+
file: file === MAIN_FILE || file === '' ? undefined : file,
|
|
111
|
+
content: notes.length ? [content.trim(), ...notes].join('\n\n') : content
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
if (file === MAIN_FILE)
|
|
116
|
+
return { error: `skill not found: ${name}` };
|
|
117
|
+
const available = await listSkillFiles([overlayDrive, baseDrive], name);
|
|
118
|
+
const listing = available.length ? ` — files this skill has: ${available.join(', ')}` : '';
|
|
119
|
+
return { error: `file not found in skill ${name}: ${file}${listing}` };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AbortSignal as BareAbortSignal } from 'bare-abort-controller';
|
|
2
|
+
import type { ToolExecuteContext } from '../../capabilities/completion.ts';
|
|
3
|
+
import { type ParseResult } from '../http-request/utils.ts';
|
|
4
|
+
export interface HttpRequestAuth {
|
|
5
|
+
type: 'bearer';
|
|
6
|
+
tokenCredentialKey: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CredentialRecord {
|
|
9
|
+
readonly token: string;
|
|
10
|
+
readonly expiresAt?: number;
|
|
11
|
+
}
|
|
12
|
+
export type CredentialStoreValue = CredentialRecord | string | null;
|
|
13
|
+
export type CredentialStoreFn = (key: string, ctx?: ToolExecuteContext) => Promise<CredentialStoreValue> | CredentialStoreValue;
|
|
14
|
+
export declare function credentialRecord(value: CredentialStoreValue): CredentialRecord | null;
|
|
15
|
+
export declare function isCredentialExpired(record: CredentialRecord, now?: number): boolean;
|
|
16
|
+
export type CredentialAllowList = Record<string, string | string[]>;
|
|
17
|
+
export declare function urlAllowed(url: URL, patterns: readonly string[]): boolean;
|
|
18
|
+
export declare function isCredentialUrlAllowed(target: string, patterns: readonly string[]): boolean;
|
|
19
|
+
export type CredentialAllowListFn = (ctx?: ToolExecuteContext) => CredentialAllowList;
|
|
20
|
+
export type AuthOutcome = {
|
|
21
|
+
readonly token: string;
|
|
22
|
+
} | {
|
|
23
|
+
readonly error: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function parseAuth(v: unknown): ParseResult<HttpRequestAuth>;
|
|
26
|
+
export declare function resolveAuth(auth: HttpRequestAuth, url: URL, credentialStore: CredentialStoreFn | undefined, credentialAllowList: CredentialAllowList | undefined, ctx: ToolExecuteContext | undefined): Promise<AuthOutcome>;
|
|
27
|
+
export declare function resolveAutoAuth(url: URL, credentialStore: CredentialStoreFn | undefined, credentialAllowList: CredentialAllowList | undefined, ctx: ToolExecuteContext | undefined): Promise<{
|
|
28
|
+
token: string;
|
|
29
|
+
key: string;
|
|
30
|
+
} | {
|
|
31
|
+
error: string;
|
|
32
|
+
} | null>;
|
|
33
|
+
export type BearerOutcome = {
|
|
34
|
+
readonly header: string | null;
|
|
35
|
+
readonly credentialKey?: string;
|
|
36
|
+
} | {
|
|
37
|
+
readonly error: string;
|
|
38
|
+
};
|
|
39
|
+
export declare function resolveBearerHeader(auth: HttpRequestAuth | undefined, url: URL, credentialStore: CredentialStoreFn | undefined, credentialAllowList: CredentialAllowList | undefined, ctx: ToolExecuteContext | undefined, signal: BareAbortSignal): Promise<BearerOutcome>;
|
|
40
|
+
export declare function resolveRedirectBearerHeader(auth: HttpRequestAuth | undefined, url: URL, credentialStore: CredentialStoreFn | undefined, credentialAllowList: CredentialAllowList | undefined, ctx: ToolExecuteContext | undefined, signal: BareAbortSignal): Promise<{
|
|
41
|
+
readonly header: string | null;
|
|
42
|
+
readonly credentialKey?: string;
|
|
43
|
+
}>;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { raceAbort } from "../../helpers/race-abort.js";
|
|
2
|
+
import { parseObjectArg } from "../http-request/utils.js";
|
|
3
|
+
export function credentialRecord(value) {
|
|
4
|
+
if (typeof value === 'string')
|
|
5
|
+
return value ? { token: value } : null;
|
|
6
|
+
return value?.token ? value : null;
|
|
7
|
+
}
|
|
8
|
+
export function isCredentialExpired(record, now = Date.now()) {
|
|
9
|
+
return typeof record.expiresAt === 'number' && now + 60_000 >= record.expiresAt;
|
|
10
|
+
}
|
|
11
|
+
function pathAllowed(target, prefix) {
|
|
12
|
+
if (target === prefix)
|
|
13
|
+
return true;
|
|
14
|
+
return target.startsWith(prefix.endsWith('/') ? prefix : `${prefix}/`);
|
|
15
|
+
}
|
|
16
|
+
function matchesPattern(url, pattern) {
|
|
17
|
+
const glob = pattern.endsWith('*');
|
|
18
|
+
const raw = glob ? pattern.slice(0, -1) : pattern;
|
|
19
|
+
let base;
|
|
20
|
+
try {
|
|
21
|
+
base = new URL(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return url.href.startsWith(raw);
|
|
25
|
+
}
|
|
26
|
+
if (url.protocol !== base.protocol || url.host !== base.host)
|
|
27
|
+
return false;
|
|
28
|
+
return glob ? url.pathname.startsWith(base.pathname) : pathAllowed(url.pathname, base.pathname);
|
|
29
|
+
}
|
|
30
|
+
export function urlAllowed(url, patterns) {
|
|
31
|
+
return patterns.some((p) => matchesPattern(url, p));
|
|
32
|
+
}
|
|
33
|
+
export function isCredentialUrlAllowed(target, patterns) {
|
|
34
|
+
try {
|
|
35
|
+
return urlAllowed(new URL(target), patterns);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function parseAuth(v) {
|
|
42
|
+
const parsed = parseObjectArg(v, 'auth');
|
|
43
|
+
if (!parsed.ok)
|
|
44
|
+
return parsed;
|
|
45
|
+
if (!parsed.value)
|
|
46
|
+
return { ok: true, value: undefined };
|
|
47
|
+
const { type, tokenCredentialKey } = parsed.value;
|
|
48
|
+
if (type !== undefined && type !== 'bearer') {
|
|
49
|
+
return { ok: false, error: `unsupported auth type: ${String(type)}` };
|
|
50
|
+
}
|
|
51
|
+
const key = typeof tokenCredentialKey === 'string' && tokenCredentialKey.length > 0
|
|
52
|
+
? tokenCredentialKey
|
|
53
|
+
: null;
|
|
54
|
+
if (key === null) {
|
|
55
|
+
const blank = tokenCredentialKey === undefined || tokenCredentialKey === null || tokenCredentialKey === '';
|
|
56
|
+
if (!blank) {
|
|
57
|
+
return { ok: false, error: 'auth.tokenCredentialKey must be a non-empty string' };
|
|
58
|
+
}
|
|
59
|
+
return { ok: true, value: undefined };
|
|
60
|
+
}
|
|
61
|
+
return { ok: true, value: { type: 'bearer', tokenCredentialKey: key } };
|
|
62
|
+
}
|
|
63
|
+
export async function resolveAuth(auth, url, credentialStore, credentialAllowList, ctx) {
|
|
64
|
+
const key = auth.tokenCredentialKey;
|
|
65
|
+
if (!credentialStore)
|
|
66
|
+
return { error: 'credentials not configured' };
|
|
67
|
+
const entry = credentialAllowList?.[key];
|
|
68
|
+
if (!entry) {
|
|
69
|
+
return {
|
|
70
|
+
error: `credential ${key} is not available here — load the skill that declares it first`
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const patterns = Array.isArray(entry) ? entry : [entry];
|
|
74
|
+
if (!urlAllowed(url, patterns)) {
|
|
75
|
+
return { error: `credential ${key} is not allowed for URL ${url.href}` };
|
|
76
|
+
}
|
|
77
|
+
const record = credentialRecord(await credentialStore(key, ctx));
|
|
78
|
+
if (!record)
|
|
79
|
+
return { error: `unknown credential: ${key}` };
|
|
80
|
+
return { token: record.token };
|
|
81
|
+
}
|
|
82
|
+
export async function resolveAutoAuth(url, credentialStore, credentialAllowList, ctx) {
|
|
83
|
+
if (!credentialStore || !credentialAllowList)
|
|
84
|
+
return null;
|
|
85
|
+
let selected = null;
|
|
86
|
+
for (const [key, entry] of Object.entries(credentialAllowList)) {
|
|
87
|
+
const patterns = Array.isArray(entry) ? entry : [entry];
|
|
88
|
+
if (!urlAllowed(url, patterns))
|
|
89
|
+
continue;
|
|
90
|
+
const record = credentialRecord(await credentialStore(key, ctx));
|
|
91
|
+
if (!record)
|
|
92
|
+
continue;
|
|
93
|
+
if (selected) {
|
|
94
|
+
return { error: 'multiple credentials match this URL; set auth.tokenCredentialKey' };
|
|
95
|
+
}
|
|
96
|
+
selected = { token: record.token, key };
|
|
97
|
+
}
|
|
98
|
+
return selected;
|
|
99
|
+
}
|
|
100
|
+
export async function resolveBearerHeader(auth, url, credentialStore, credentialAllowList, ctx, signal) {
|
|
101
|
+
if (auth) {
|
|
102
|
+
const resolved = await raceAbort(resolveAuth(auth, url, credentialStore, credentialAllowList, ctx), signal);
|
|
103
|
+
if ('error' in resolved)
|
|
104
|
+
return { error: resolved.error };
|
|
105
|
+
return { header: `Bearer ${resolved.token}`, credentialKey: auth.tokenCredentialKey };
|
|
106
|
+
}
|
|
107
|
+
const resolved = await raceAbort(resolveAutoAuth(url, credentialStore, credentialAllowList, ctx), signal);
|
|
108
|
+
if (resolved && 'error' in resolved)
|
|
109
|
+
return resolved;
|
|
110
|
+
return resolved
|
|
111
|
+
? { header: `Bearer ${resolved.token}`, credentialKey: resolved.key }
|
|
112
|
+
: { header: null };
|
|
113
|
+
}
|
|
114
|
+
export async function resolveRedirectBearerHeader(auth, url, credentialStore, credentialAllowList, ctx, signal) {
|
|
115
|
+
if (!auth)
|
|
116
|
+
return { header: null };
|
|
117
|
+
const resolved = await raceAbort(resolveAuth(auth, url, credentialStore, credentialAllowList, ctx), signal);
|
|
118
|
+
return 'error' in resolved
|
|
119
|
+
? { header: null }
|
|
120
|
+
: { header: `Bearer ${resolved.token}`, credentialKey: auth.tokenCredentialKey };
|
|
121
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SkillRequires } from './manifest.ts';
|
|
2
|
+
export type BinVersionState = 'ok' | 'too-old' | 'unknown';
|
|
3
|
+
export declare function binOnPath(bin: string, platform?: string): boolean;
|
|
4
|
+
export declare function missingBins(bins: readonly string[], platform?: string, probe?: (bin: string, platform?: string) => boolean): string[];
|
|
5
|
+
export declare function probeBinVersions(requires: SkillRequires | undefined, opts: {
|
|
6
|
+
platform?: string;
|
|
7
|
+
run: (command: string) => Promise<string | null>;
|
|
8
|
+
binPresent?: (bin: string) => boolean | Promise<boolean>;
|
|
9
|
+
}): Promise<Map<string, BinVersionState>>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import process from 'process';
|
|
4
|
+
import { withHomebrewPath } from "../../helpers/spawn-path.js";
|
|
5
|
+
const SAFE_BIN_NAME = /^[A-Za-z0-9._-]+$/;
|
|
6
|
+
function envPath() {
|
|
7
|
+
return process.env.PATH ?? process.env.Path ?? '';
|
|
8
|
+
}
|
|
9
|
+
function existsFile(p) {
|
|
10
|
+
try {
|
|
11
|
+
return fs.existsSync(p);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function binOnPath(bin, platform) {
|
|
18
|
+
if (!SAFE_BIN_NAME.test(bin))
|
|
19
|
+
return false;
|
|
20
|
+
const searchPath = withHomebrewPath(envPath(), platform ?? process.platform) ?? '';
|
|
21
|
+
const dirs = searchPath.split(path.delimiter).filter((d) => d.length > 0);
|
|
22
|
+
const names = platform === 'win32' ? [bin, `${bin}.exe`, `${bin}.cmd`, `${bin}.bat`] : [bin];
|
|
23
|
+
return dirs.some((dir) => names.some((name) => existsFile(path.join(dir, name))));
|
|
24
|
+
}
|
|
25
|
+
export function missingBins(bins, platform, probe = binOnPath) {
|
|
26
|
+
return bins.filter((bin) => !probe(bin, platform));
|
|
27
|
+
}
|
|
28
|
+
function parseVersion(text) {
|
|
29
|
+
const m = text.match(/(\d+)\.(\d+)(?:\.(\d+))?/);
|
|
30
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : null;
|
|
31
|
+
}
|
|
32
|
+
function isBelow(found, min) {
|
|
33
|
+
for (let i = 0; i < Math.max(found.length, min.length); i++) {
|
|
34
|
+
const f = found[i] ?? 0;
|
|
35
|
+
const m = min[i] ?? 0;
|
|
36
|
+
if (f !== m)
|
|
37
|
+
return f < m;
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
export async function probeBinVersions(requires, opts) {
|
|
42
|
+
const states = new Map();
|
|
43
|
+
const mins = requires?.binMinVersions;
|
|
44
|
+
if (!mins)
|
|
45
|
+
return states;
|
|
46
|
+
const binPresent = opts.binPresent ?? ((bin) => binOnPath(bin, opts.platform));
|
|
47
|
+
for (const [bin, spec] of Object.entries(mins)) {
|
|
48
|
+
if (!spec.command || !(await binPresent(bin)))
|
|
49
|
+
continue;
|
|
50
|
+
const output = await opts.run(spec.command);
|
|
51
|
+
const found = output === null ? null : parseVersion(output);
|
|
52
|
+
const min = parseVersion(spec.min);
|
|
53
|
+
states.set(bin, found && min ? (isBelow(found, min) ? 'too-old' : 'ok') : 'unknown');
|
|
54
|
+
}
|
|
55
|
+
return states;
|
|
56
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import process from 'process';
|
|
5
|
+
import { userTmpPath } from "../../tmp-path.js";
|
|
6
|
+
import { hashBundledSkills } from "./bundled-hash.js";
|
|
7
|
+
import { SKILLS as BUNDLED_SKILLS } from '@qvac/skills/bundled';
|
|
8
|
+
import { SKILLS_HASH as BUNDLED_SKILLS_HASH } from '@qvac/skills/hash';
|
|
9
|
+
let inflight = null;
|
|
10
|
+
export function ensureBundledSkillsDir() {
|
|
11
|
+
inflight ??= materialize().finally(() => {
|
|
12
|
+
inflight = null;
|
|
13
|
+
});
|
|
14
|
+
return inflight;
|
|
15
|
+
}
|
|
16
|
+
async function materialize() {
|
|
17
|
+
const dir = userTmpPath('qvac-harness-skills-' + BUNDLED_SKILLS_HASH);
|
|
18
|
+
if (await validTree(dir))
|
|
19
|
+
return dir;
|
|
20
|
+
const staging = `${dir}-staging-${process.pid}-${randomSuffix()}`;
|
|
21
|
+
await fs.promises.mkdir(staging, { recursive: true });
|
|
22
|
+
if (os.platform() !== 'win32')
|
|
23
|
+
await fs.promises.chmod(staging, 0o700);
|
|
24
|
+
try {
|
|
25
|
+
await writeTree(staging);
|
|
26
|
+
if (!(await validTree(staging))) {
|
|
27
|
+
throw new Error(`bundled skills staging dir ${staging} failed validation`);
|
|
28
|
+
}
|
|
29
|
+
if (await tryRename(staging, dir))
|
|
30
|
+
return dir;
|
|
31
|
+
if (await validTree(dir))
|
|
32
|
+
return dir;
|
|
33
|
+
const trash = `${dir}.trash-${process.pid}-${randomSuffix()}`;
|
|
34
|
+
try {
|
|
35
|
+
await fs.promises.rename(dir, trash);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (await validTree(dir))
|
|
39
|
+
return dir;
|
|
40
|
+
throw occupiedError(dir, err);
|
|
41
|
+
}
|
|
42
|
+
void fs.promises.rm(trash, { recursive: true, force: true });
|
|
43
|
+
if (await tryRename(staging, dir))
|
|
44
|
+
return dir;
|
|
45
|
+
if (await validTree(dir))
|
|
46
|
+
return dir;
|
|
47
|
+
throw tamperError(dir);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await fs.promises.rm(staging, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function tamperError(dir) {
|
|
54
|
+
return new Error(`bundled skills dir ${dir} failed validation after a rewrite — ` +
|
|
55
|
+
'another process is tampering with it; remove it and restart');
|
|
56
|
+
}
|
|
57
|
+
function occupiedError(dir, cause) {
|
|
58
|
+
const why = cause instanceof Error ? ` (${cause.message})` : '';
|
|
59
|
+
return new Error(`bundled skills dir ${dir} could not be read or replaced${why} — remove it and restart`);
|
|
60
|
+
}
|
|
61
|
+
export function randomSuffix() {
|
|
62
|
+
return Math.random().toString(36).slice(2, 10);
|
|
63
|
+
}
|
|
64
|
+
async function tryRename(from, to) {
|
|
65
|
+
try {
|
|
66
|
+
await fs.promises.rename(from, to);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function writeTree(dir) {
|
|
74
|
+
for (const [relPath, content] of Object.entries(BUNDLED_SKILLS)) {
|
|
75
|
+
const target = path.join(dir, relPath);
|
|
76
|
+
await fs.promises.mkdir(path.dirname(target), { recursive: true });
|
|
77
|
+
await fs.promises.writeFile(target, content);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function validTree(dir) {
|
|
81
|
+
try {
|
|
82
|
+
if (os.platform() !== 'win32') {
|
|
83
|
+
const stat = await fs.promises.lstat(dir);
|
|
84
|
+
if (!stat.isDirectory() || (stat.mode & 0o077) !== 0)
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const files = await collect(dir, '');
|
|
88
|
+
if (files === null)
|
|
89
|
+
return false;
|
|
90
|
+
return hashBundledSkills(files) === BUNDLED_SKILLS_HASH;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function collect(dir, prefix) {
|
|
97
|
+
const files = [];
|
|
98
|
+
for (const entry of await fs.promises.readdir(dir, { withFileTypes: true })) {
|
|
99
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
100
|
+
if (entry.isDirectory()) {
|
|
101
|
+
const nested = await collect(path.join(dir, entry.name), rel);
|
|
102
|
+
if (nested === null)
|
|
103
|
+
return null;
|
|
104
|
+
files.push(...nested);
|
|
105
|
+
}
|
|
106
|
+
else if (entry.isFile()) {
|
|
107
|
+
files.push({
|
|
108
|
+
path: rel,
|
|
109
|
+
content: await fs.promises.readFile(path.join(dir, entry.name), 'utf8')
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return files;
|
|
117
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
export function hashBundledSkills(files) {
|
|
3
|
+
const hash = crypto.createHash('sha256');
|
|
4
|
+
const sorted = [...files].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
5
|
+
for (const file of sorted) {
|
|
6
|
+
hash.update(file.path);
|
|
7
|
+
hash.update('\0');
|
|
8
|
+
hash.update(file.content);
|
|
9
|
+
hash.update('\0');
|
|
10
|
+
}
|
|
11
|
+
return hash.digest('hex').slice(0, 16);
|
|
12
|
+
}
|