@qvac/harness 0.3.3 → 0.5.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/CHANGELOG.md +700 -126
- package/README.md +156 -0
- package/dist/harness.bundle +71 -103
- package/dist/prebuilds-manifest.json +0 -8
- package/dist-lib/index.d.ts +42 -40
- package/dist-lib/index.js +307 -117
- package/dist-lib/lib/audio-transcode-bare.d.ts +2 -2
- package/dist-lib/lib/audio-transcode-bare.js +28 -18
- package/dist-lib/lib/audio-transcode-node.d.ts +2 -2
- package/dist-lib/lib/capabilities/audio-resample.d.ts +7 -6
- package/dist-lib/lib/capabilities/audio-resample.js +30 -18
- package/dist-lib/lib/capabilities/completion.d.ts +20 -19
- package/dist-lib/lib/capabilities/completion.js +194 -135
- package/dist-lib/lib/capabilities/download.d.ts +6 -6
- package/dist-lib/lib/capabilities/download.js +15 -8
- package/dist-lib/lib/capabilities/embed.js +2 -1
- package/dist-lib/lib/capabilities/enhance.d.ts +6 -1
- package/dist-lib/lib/capabilities/enhance.js +7 -4
- package/dist-lib/lib/capabilities/invoke.js +7 -4
- package/dist-lib/lib/capabilities/kv-cache.js +2 -1
- package/dist-lib/lib/capabilities/preview-model-source.d.ts +69 -0
- package/dist-lib/lib/capabilities/preview-model-source.js +439 -0
- package/dist-lib/lib/capabilities/speech-text.js +5 -4
- package/dist-lib/lib/capabilities/tool-selection.js +10 -9
- package/dist-lib/lib/capabilities/tool-turns.js +30 -16
- package/dist-lib/lib/capabilities/usage-breakdown.js +2 -1
- package/dist-lib/lib/capabilities/voice.d.ts +5 -1
- package/dist-lib/lib/capabilities/voice.js +22 -12
- package/dist-lib/lib/connect.d.ts +15 -37
- package/dist-lib/lib/connect.js +161 -112
- package/dist-lib/lib/context-window.js +25 -21
- package/dist-lib/lib/contract-types.d.ts +26 -20
- package/dist-lib/lib/diagram-repair.d.ts +4 -4
- package/dist-lib/lib/diagram-repair.js +6 -3
- package/dist-lib/lib/docx/index.d.ts +3 -2
- package/dist-lib/lib/{error-fields.d.ts → guards.d.ts} +1 -0
- package/dist-lib/lib/{error-fields.js → guards.js} +3 -0
- package/dist-lib/lib/hardware/capability-probe.js +20 -10
- package/dist-lib/lib/hardware/gpu-classify.js +6 -3
- package/dist-lib/lib/hardware/gpu-probe-addon.js +8 -4
- package/dist-lib/lib/hardware/gpu-probe.js +4 -2
- package/dist-lib/lib/hardware/gpu-stats.native.cjs +8 -9
- package/dist-lib/lib/hardware/parsers.js +8 -4
- package/dist-lib/lib/helpers/buffer-burst.js +4 -2
- package/dist-lib/lib/helpers/control-tokens.js +4 -2
- package/dist-lib/lib/helpers/join-guard.d.ts +2 -2
- package/dist-lib/lib/helpers/join-guard.js +4 -2
- package/dist-lib/lib/helpers/load-stall.js +4 -2
- package/dist-lib/lib/helpers/markup-stream-filter.d.ts +2 -2
- package/dist-lib/lib/helpers/markup-stream-filter.js +6 -3
- package/dist-lib/lib/helpers/race-abort.js +2 -1
- package/dist-lib/lib/helpers/race-timeout.d.ts +5 -0
- package/dist-lib/lib/helpers/race-timeout.js +14 -0
- package/dist-lib/lib/helpers/run-outcome.d.ts +6 -8
- package/dist-lib/lib/helpers/run-outcome.js +36 -20
- package/dist-lib/lib/helpers/spawn-path.js +6 -3
- package/dist-lib/lib/helpers/text-coalescer.js +6 -3
- package/dist-lib/lib/helpers/thinking.js +38 -45
- package/dist-lib/lib/helpers/tool-call-markup.d.ts +9 -7
- package/dist-lib/lib/helpers/tool-call-markup.js +36 -20
- package/dist-lib/lib/helpers/tool-intent.d.ts +3 -2
- package/dist-lib/lib/helpers/tool-intent.js +4 -2
- package/dist-lib/lib/helpers/tool-label.js +18 -9
- package/dist-lib/lib/hooks/built-in/arg-repair.js +23 -13
- package/dist-lib/lib/hooks/built-in/egress.js +2 -1
- package/dist-lib/lib/hooks/built-in/skill-catalog.js +2 -1
- package/dist-lib/lib/hooks/pipeline.d.ts +5 -4
- package/dist-lib/lib/hooks/pipeline.js +6 -3
- package/dist-lib/lib/hooks/run.js +12 -8
- package/dist-lib/lib/model-download-progress.js +10 -8
- package/dist-lib/lib/models/context.d.ts +1 -1
- package/dist-lib/lib/models/context.js +5 -1
- package/dist-lib/lib/models/custom-model-cache.d.ts +13 -0
- package/dist-lib/lib/models/custom-model-cache.js +70 -0
- package/dist-lib/lib/models/gguf-header.d.ts +31 -0
- package/dist-lib/lib/models/gguf-header.js +230 -0
- package/dist-lib/lib/models/idle-unload.d.ts +14 -0
- package/dist-lib/lib/models/idle-unload.js +51 -0
- package/dist-lib/lib/models/kv-cache.d.ts +3 -2
- package/dist-lib/lib/models/kv-cache.js +4 -3
- package/dist-lib/lib/models/llm-presets.d.ts +2 -2
- package/dist-lib/lib/models/llm-presets.js +4 -2
- package/dist-lib/lib/models/model-catalog-constants.js +3 -2
- package/dist-lib/lib/models/model-catalog.d.ts +19 -21
- package/dist-lib/lib/models/model-catalog.js +66 -33
- package/dist-lib/lib/models/model-delete.d.ts +4 -1
- package/dist-lib/lib/models/model-delete.js +30 -15
- package/dist-lib/lib/models/model-handle.js +2 -1
- package/dist-lib/lib/models/model-list.d.ts +14 -3
- package/dist-lib/lib/models/model-list.js +33 -6
- package/dist-lib/lib/models/model-pool.d.ts +44 -10
- package/dist-lib/lib/models/model-pool.js +47 -42
- package/dist-lib/lib/models/model-source.js +11 -6
- package/dist-lib/lib/models/music-catalog.d.ts +2 -2
- package/dist-lib/lib/models/nmt-catalog.d.ts +4 -3
- package/dist-lib/lib/models/nmt-catalog.js +10 -5
- package/dist-lib/lib/models/opencl-cache.d.ts +6 -1
- package/dist-lib/lib/models/opencl-cache.js +5 -3
- package/dist-lib/lib/models/sdk-faults.d.ts +11 -8
- package/dist-lib/lib/models/sdk-faults.js +9 -6
- package/dist-lib/lib/models/shards.d.ts +11 -0
- package/dist-lib/lib/models/shards.js +15 -0
- package/dist-lib/lib/models/tool-dialect.js +10 -5
- package/dist-lib/lib/no-redirect-fetch-bare.d.ts +3 -3
- package/dist-lib/lib/no-redirect-fetch-bare.js +1 -1
- package/dist-lib/lib/no-redirect-fetch-impl.js +6 -3
- package/dist-lib/lib/no-redirect-fetch-node.d.ts +1 -1
- package/dist-lib/lib/no-redirect-fetch-node.js +9 -9
- package/dist-lib/lib/office/index.d.ts +3 -2
- package/dist-lib/lib/office/python.d.ts +3 -2
- package/dist-lib/lib/office/python.js +2 -1
- package/dist-lib/lib/prompt.d.ts +5 -4
- package/dist-lib/lib/prompt.js +8 -4
- package/dist-lib/lib/queue.js +10 -5
- package/dist-lib/lib/resolution-detector.js +21 -11
- package/dist-lib/lib/rpc-session.js +2 -1
- package/dist-lib/lib/sdk-loader.js +2 -1
- package/dist-lib/lib/sdk-storage.js +4 -2
- package/dist-lib/lib/serve.js +128 -68
- package/dist-lib/lib/spawn.js +7 -4
- package/dist-lib/lib/tmp-path.d.ts +7 -6
- package/dist-lib/lib/tool-call-display.d.ts +2 -2
- package/dist-lib/lib/tools/create-documents.js +18 -10
- package/dist-lib/lib/tools/edit-documents.js +23 -13
- package/dist-lib/lib/tools/edit-image.d.ts +2 -2
- package/dist-lib/lib/tools/edit-image.js +60 -36
- package/dist-lib/lib/tools/exec/adapters/adapter.d.ts +10 -10
- package/dist-lib/lib/tools/exec/adapters/python/attachments.d.ts +17 -11
- package/dist-lib/lib/tools/exec/adapters/python/attachments.js +53 -40
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +60 -32
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.d.ts +33 -22
- package/dist-lib/lib/tools/exec/adapters/python/guarded-fetch.js +58 -29
- package/dist-lib/lib/tools/exec/adapters/python/interrupt-timer.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/loader.js +23 -12
- package/dist-lib/lib/tools/exec/adapters/python/memfs.d.ts +13 -11
- package/dist-lib/lib/tools/exec/adapters/python/memfs.js +10 -5
- package/dist-lib/lib/tools/exec/adapters/python/net-shim.js +3 -1
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.d.ts +6 -4
- package/dist-lib/lib/tools/exec/adapters/python/output-policy.js +20 -10
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/pyodide.js +14 -7
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +41 -34
- package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +316 -166
- package/dist-lib/lib/tools/exec/adapters/python/run-session.d.ts +9 -8
- package/dist-lib/lib/tools/exec/adapters/python/run-session.js +56 -28
- package/dist-lib/lib/tools/exec/adapters/python/wheels.d.ts +8 -10
- package/dist-lib/lib/tools/exec/adapters/python/wheels.js +22 -11
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.d.ts +3 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/client.js +41 -26
- package/dist-lib/lib/tools/exec/adapters/python/worker/entry.js +27 -18
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.d.ts +2 -2
- package/dist-lib/lib/tools/exec/adapters/python/worker/protocol.js +14 -13
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.d.ts +4 -4
- package/dist-lib/lib/tools/exec/adapters/python/worker/spawn-node.js +10 -5
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.d.ts +4 -4
- package/dist-lib/lib/tools/exec/adapters/shell/shell-adapter.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/cli-process.d.ts +6 -1
- package/dist-lib/lib/tools/exec/obsidian/cli-process.js +8 -5
- package/dist-lib/lib/tools/exec/obsidian/cli.d.ts +6 -6
- package/dist-lib/lib/tools/exec/obsidian/cli.js +4 -4
- package/dist-lib/lib/tools/exec/obsidian/command.d.ts +3 -2
- package/dist-lib/lib/tools/exec/obsidian/command.js +6 -3
- package/dist-lib/lib/tools/exec/obsidian/output.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/route.d.ts +4 -6
- package/dist-lib/lib/tools/exec/obsidian/route.js +17 -11
- package/dist-lib/lib/tools/exec/obsidian/safe-path.js +2 -1
- package/dist-lib/lib/tools/exec/obsidian/vault-create.js +15 -7
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.d.ts +2 -2
- package/dist-lib/lib/tools/exec/obsidian/vault-paths.js +20 -10
- package/dist-lib/lib/tools/exec/obsidian/vault-read.js +20 -13
- package/dist-lib/lib/tools/exec/obsidian/vault-write.js +24 -12
- package/dist-lib/lib/tools/exec/runner.d.ts +14 -14
- package/dist-lib/lib/tools/exec/runner.js +5 -3
- package/dist-lib/lib/tools/exec/scope.js +52 -28
- package/dist-lib/lib/tools/exec/shell-argv.js +31 -18
- package/dist-lib/lib/tools/exec/shell-guidance.d.ts +6 -1
- package/dist-lib/lib/tools/exec/shell-guidance.js +35 -20
- package/dist-lib/lib/tools/exec/video-tools.js +2 -2
- package/dist-lib/lib/tools/exec.d.ts +6 -8
- package/dist-lib/lib/tools/exec.js +113 -54
- package/dist-lib/lib/tools/file-glob.d.ts +4 -5
- package/dist-lib/lib/tools/file-glob.js +16 -11
- package/dist-lib/lib/tools/fs/glob.js +13 -18
- package/dist-lib/lib/tools/fs/remote-protocol.d.ts +10 -10
- package/dist-lib/lib/tools/fs/remote.d.ts +3 -2
- package/dist-lib/lib/tools/fs/remote.js +13 -7
- package/dist-lib/lib/tools/fs/scope.d.ts +8 -3
- package/dist-lib/lib/tools/fs/scope.js +20 -12
- package/dist-lib/lib/tools/fs/walk.js +6 -3
- package/dist-lib/lib/tools/fs/write.d.ts +3 -2
- package/dist-lib/lib/tools/generate-image.d.ts +11 -9
- package/dist-lib/lib/tools/generate-image.js +73 -37
- package/dist-lib/lib/tools/generate-music.d.ts +22 -11
- package/dist-lib/lib/tools/generate-music.js +79 -48
- package/dist-lib/lib/tools/grep.js +66 -41
- package/dist-lib/lib/tools/http-request/encode-fields.js +18 -9
- package/dist-lib/lib/tools/http-request/{utils.js → request.js} +20 -11
- package/dist-lib/lib/tools/http-request/truncate-response.js +57 -46
- package/dist-lib/lib/tools/http-request.d.ts +1 -1
- package/dist-lib/lib/tools/http-request.js +33 -18
- package/dist-lib/lib/tools/mcp/protocol.d.ts +53 -0
- package/dist-lib/lib/tools/mcp/{utils.js → protocol.js} +45 -23
- package/dist-lib/lib/tools/mcp-call.d.ts +1 -1
- package/dist-lib/lib/tools/mcp-call.js +37 -16
- package/dist-lib/lib/tools/operations/builders.js +10 -6
- package/dist-lib/lib/tools/operations/compile.js +20 -10
- package/dist-lib/lib/tools/operations/decoders.js +12 -6
- package/dist-lib/lib/tools/operations/gmail-message.js +3 -2
- package/dist-lib/lib/tools/outbound-guard.d.ts +4 -3
- package/dist-lib/lib/tools/outbound-guard.js +14 -8
- package/dist-lib/lib/tools/private-ip.js +8 -7
- package/dist-lib/lib/tools/rag-search.js +14 -7
- package/dist-lib/lib/tools/read-files.js +23 -20
- package/dist-lib/lib/tools/skill.js +19 -10
- package/dist-lib/lib/tools/skills/auth-resolver.d.ts +11 -18
- package/dist-lib/lib/tools/skills/auth-resolver.js +30 -18
- package/dist-lib/lib/tools/skills/bin-check.d.ts +3 -2
- package/dist-lib/lib/tools/skills/bin-check.js +10 -3
- package/dist-lib/lib/tools/skills/bundled-dir.js +20 -10
- package/dist-lib/lib/tools/skills/bundled-hash.d.ts +3 -2
- package/dist-lib/lib/tools/skills/bundled-hash.js +6 -1
- package/dist-lib/lib/tools/skills/catalog.d.ts +3 -2
- package/dist-lib/lib/tools/skills/catalog.js +33 -28
- package/dist-lib/lib/tools/skills/cli-argv.d.ts +3 -2
- package/dist-lib/lib/tools/skills/cli-argv.js +55 -52
- package/dist-lib/lib/tools/skills/cli-schema-check.js +21 -10
- package/dist-lib/lib/tools/skills/cli-schema-load.js +28 -20
- package/dist-lib/lib/tools/skills/cli-schema-types.d.ts +6 -6
- package/dist-lib/lib/tools/skills/cli-schema.js +2 -1
- package/dist-lib/lib/tools/skills/credential-check.d.ts +6 -3
- package/dist-lib/lib/tools/skills/credential-check.js +24 -12
- package/dist-lib/lib/tools/skills/credential-grants.js +1 -6
- package/dist-lib/lib/tools/skills/credential-health.d.ts +5 -1
- package/dist-lib/lib/tools/skills/credential-health.js +5 -3
- package/dist-lib/lib/tools/skills/frontmatter-parser.js +10 -5
- package/dist-lib/lib/tools/skills/manifest-parse.js +84 -64
- package/dist-lib/lib/tools/skills/mention.js +7 -6
- package/dist-lib/lib/tools/skills/obsidian-readiness.d.ts +2 -2
- package/dist-lib/lib/tools/skills/obsidian-readiness.js +11 -3
- package/dist-lib/lib/tools/skills/overlay-crud.d.ts +6 -4
- package/dist-lib/lib/tools/skills/overlay-crud.js +12 -6
- package/dist-lib/lib/tools/skills/overlay-dir.js +4 -2
- package/dist-lib/lib/tools/skills/overlay-watch.d.ts +3 -2
- package/dist-lib/lib/tools/skills/overlay-watch.js +8 -4
- package/dist-lib/lib/tools/skills/setup-routes.js +1 -3
- package/dist-lib/lib/tools/skills/skill-settings.js +4 -1
- package/dist-lib/lib/tools/skills/skill-state.d.ts +6 -11
- package/dist-lib/lib/tools/skills/skill-state.js +48 -29
- package/dist-lib/lib/tools/skills/tool-grants.js +6 -3
- package/dist-lib/lib/tools/toolsets.js +2 -1
- package/dist-lib/lib/tools/translate.d.ts +4 -5
- package/dist-lib/lib/tools/translate.js +31 -14
- package/dist-lib/lib/tools/url-guard.js +2 -1
- package/dist-lib/lib/tools/weather-lookup/place.js +8 -4
- package/dist-lib/lib/tools/weather-lookup/report.d.ts +7 -1
- package/dist-lib/lib/tools/weather-lookup/report.js +46 -9
- package/dist-lib/lib/tools/weather-lookup.js +18 -12
- package/dist-lib/lib/tools/web-fetch.js +47 -24
- package/dist-lib/lib/tools/web-search.js +8 -3
- package/dist-lib/lib/validating-lookup.d.ts +4 -4
- package/dist-lib/lib/wav-encode.d.ts +1 -1
- package/dist-lib/lib/wav-encode.js +11 -10
- package/dist-lib/lib/web-fetch/embedded-state.js +24 -12
- package/dist-lib/lib/web-fetch/extract.js +4 -2
- package/dist-lib/lib/web-fetch/images.js +8 -4
- package/dist-lib/lib/web-fetch/video.d.ts +2 -2
- package/dist-lib/lib/web-fetch/video.js +8 -4
- package/dist-lib/lib/web-search/engine-registry.d.ts +2 -2
- package/dist-lib/lib/web-search/engines/aol.js +1 -1
- package/dist-lib/lib/web-search/engines/bing.js +1 -1
- package/dist-lib/lib/web-search/engines/brave.js +4 -1
- package/dist-lib/lib/web-search/engines/duckduckgo.js +3 -2
- package/dist-lib/lib/web-search/engines/google.js +7 -3
- package/dist-lib/lib/web-search/engines/wikipedia.js +5 -4
- package/dist-lib/lib/web-search/engines/yahoo.js +1 -1
- package/dist-lib/lib/web-search/fetch-html.d.ts +5 -4
- package/dist-lib/lib/web-search/fetch-html.js +13 -7
- package/dist-lib/lib/web-search/orchestrator.d.ts +9 -5
- package/dist-lib/lib/web-search/orchestrator.js +3 -3
- package/dist-lib/lib/wire.d.ts +3 -2
- package/dist-lib/lib/wire.js +23 -15
- package/dist-lib/schema/baseline.js +10 -5
- package/dist-lib/schema/build.js +2 -1
- package/dist-lib/schema/harness.d.ts +15 -2
- package/dist-lib/schema/harness.js +55 -2
- package/dist-lib/schema/release-tags.d.ts +3 -2
- package/dist-lib/schema/release-tags.js +6 -3
- package/dist-lib/spec/bind.js +6 -0
- package/dist-lib/spec/calls.js +2 -0
- package/dist-lib/spec/hrpc/hrpc.json +26 -0
- package/dist-lib/spec/hrpc/index.js +27 -3
- package/dist-lib/spec/hrpc/messages.js +207 -8
- package/dist-lib/spec/hyperschema/index.js +207 -8
- package/dist-lib/spec/hyperschema/schema.json +165 -1
- package/dist-lib/spec/hyperschema/types.d.ts +42 -1
- package/package.json +13 -14
- package/worker/harness-sidecar.mjs +16 -22
- package/worker/stow-entry.mjs +7 -4
- package/dist-lib/lib/tools/mcp/utils.d.ts +0 -47
- /package/dist-lib/lib/tools/http-request/{utils.d.ts → request.d.ts} +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { raceAbort } from '../../helpers/race-abort.js';
|
|
2
|
-
import { parseObjectArg } from '../http-request/
|
|
2
|
+
import { parseObjectArg } from '../http-request/request.js';
|
|
3
3
|
export function credentialRecord(value) {
|
|
4
|
-
if (typeof value === 'string')
|
|
4
|
+
if (typeof value === 'string') {
|
|
5
5
|
return value ? { token: value } : null;
|
|
6
|
+
}
|
|
6
7
|
return value?.token ? value : null;
|
|
7
8
|
}
|
|
8
9
|
export function isCredentialExpired(record, now = Date.now()) {
|
|
@@ -23,8 +24,9 @@ function matchesPattern(url, pattern) {
|
|
|
23
24
|
catch {
|
|
24
25
|
return url.href.startsWith(raw);
|
|
25
26
|
}
|
|
26
|
-
if (url.protocol !== base.protocol || url.host !== base.host)
|
|
27
|
+
if (url.protocol !== base.protocol || url.host !== base.host) {
|
|
27
28
|
return false;
|
|
29
|
+
}
|
|
28
30
|
return glob ? url.pathname.startsWith(base.pathname) : pathAllowed(url.pathname, base.pathname);
|
|
29
31
|
}
|
|
30
32
|
export function urlAllowed(url, patterns) {
|
|
@@ -40,10 +42,12 @@ export function isCredentialUrlAllowed(target, patterns) {
|
|
|
40
42
|
}
|
|
41
43
|
export function parseAuth(v) {
|
|
42
44
|
const parsed = parseObjectArg(v, 'auth');
|
|
43
|
-
if (!parsed.ok)
|
|
45
|
+
if (!parsed.ok) {
|
|
44
46
|
return parsed;
|
|
45
|
-
|
|
47
|
+
}
|
|
48
|
+
if (!parsed.value) {
|
|
46
49
|
return { ok: true, value: undefined };
|
|
50
|
+
}
|
|
47
51
|
const { type, tokenCredentialKey } = parsed.value;
|
|
48
52
|
if (type !== undefined && type !== 'bearer') {
|
|
49
53
|
return { ok: false, error: `unsupported auth type: ${String(type)}` };
|
|
@@ -69,8 +73,9 @@ async function connectedCredentials(keys, credentialStore, ctx) {
|
|
|
69
73
|
const connected = [];
|
|
70
74
|
for (const key of keys) {
|
|
71
75
|
const record = credentialRecord(await credentialStore(key, ctx));
|
|
72
|
-
if (record)
|
|
76
|
+
if (record) {
|
|
73
77
|
connected.push({ token: record.token, key });
|
|
78
|
+
}
|
|
74
79
|
}
|
|
75
80
|
return connected;
|
|
76
81
|
}
|
|
@@ -79,7 +84,7 @@ function ambiguous(keys) {
|
|
|
79
84
|
error: `multiple credentials match this URL; set auth.tokenCredentialKey to one of ${keys.join(', ')}`
|
|
80
85
|
};
|
|
81
86
|
}
|
|
82
|
-
async function resolveGuessedKey(key, url, credentialStore, credentialAllowList, ctx) {
|
|
87
|
+
async function resolveGuessedKey(key, url, { credentialStore, credentialAllowList = {}, ctx }) {
|
|
83
88
|
const granted = grantedKeys(url, credentialAllowList);
|
|
84
89
|
if (granted.length === 0) {
|
|
85
90
|
return {
|
|
@@ -92,14 +97,16 @@ async function resolveGuessedKey(key, url, credentialStore, credentialAllowList,
|
|
|
92
97
|
error: `unknown credential key ${key}; no connected credential for ${url.host} (${granted.join(', ')}). Tell the user to connect it in Skills; do not retry`
|
|
93
98
|
};
|
|
94
99
|
}
|
|
95
|
-
if (rest.length > 0)
|
|
100
|
+
if (rest.length > 0) {
|
|
96
101
|
return ambiguous([first, ...rest].map((c) => c.key));
|
|
102
|
+
}
|
|
97
103
|
return first;
|
|
98
104
|
}
|
|
99
|
-
async function resolveDeclaredKey(key, url, credentialStore, credentialAllowList, ctx) {
|
|
105
|
+
async function resolveDeclaredKey(key, url, { credentialStore, credentialAllowList, ctx }) {
|
|
100
106
|
const entry = credentialAllowList?.[key];
|
|
101
|
-
if (!entry)
|
|
107
|
+
if (!entry) {
|
|
102
108
|
return null;
|
|
109
|
+
}
|
|
103
110
|
const patterns = Array.isArray(entry) ? entry : [entry];
|
|
104
111
|
if (!urlAllowed(url, patterns)) {
|
|
105
112
|
return { error: `credential ${key} is not allowed for URL ${url.href}` };
|
|
@@ -114,37 +121,42 @@ async function resolveDeclaredKey(key, url, credentialStore, credentialAllowList
|
|
|
114
121
|
}
|
|
115
122
|
export async function resolveAuth(auth, url, credentialStore, credentialAllowList, ctx) {
|
|
116
123
|
const key = auth.tokenCredentialKey;
|
|
117
|
-
if (!credentialStore)
|
|
124
|
+
if (!credentialStore) {
|
|
118
125
|
return { error: 'credentials not configured' };
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
}
|
|
127
|
+
const access = { credentialStore, credentialAllowList, ctx };
|
|
128
|
+
return (await resolveDeclaredKey(key, url, access)) ?? resolveGuessedKey(key, url, access);
|
|
121
129
|
}
|
|
122
130
|
export async function resolveAutoAuth(url, credentialStore, credentialAllowList, ctx) {
|
|
123
131
|
if (!credentialStore || !credentialAllowList)
|
|
124
132
|
return null;
|
|
125
133
|
const connected = await connectedCredentials(grantedKeys(url, credentialAllowList), credentialStore, ctx);
|
|
126
|
-
if (connected.length > 1)
|
|
134
|
+
if (connected.length > 1) {
|
|
127
135
|
return ambiguous(connected.map((c) => c.key));
|
|
136
|
+
}
|
|
128
137
|
return connected[0] ?? null;
|
|
129
138
|
}
|
|
130
139
|
export async function resolveBearerHeader(auth, url, credentialStore, credentialAllowList, ctx, signal) {
|
|
131
140
|
if (auth) {
|
|
132
141
|
const resolved = await raceAbort(resolveAuth(auth, url, credentialStore, credentialAllowList, ctx), signal);
|
|
133
|
-
if ('error' in resolved)
|
|
142
|
+
if ('error' in resolved) {
|
|
134
143
|
return { error: resolved.error };
|
|
144
|
+
}
|
|
135
145
|
return { header: `Bearer ${resolved.token}`, credentialKey: resolved.key };
|
|
136
146
|
}
|
|
137
147
|
const resolved = await raceAbort(resolveAutoAuth(url, credentialStore, credentialAllowList, ctx), signal);
|
|
138
|
-
if (resolved && 'error' in resolved)
|
|
148
|
+
if (resolved && 'error' in resolved) {
|
|
139
149
|
return resolved;
|
|
150
|
+
}
|
|
140
151
|
return resolved
|
|
141
152
|
? { header: `Bearer ${resolved.token}`, credentialKey: resolved.key }
|
|
142
153
|
: { header: null };
|
|
143
154
|
}
|
|
144
155
|
export async function resolveRedirectBearerHeader(auth, url, credentialStore, credentialAllowList, ctx, signal) {
|
|
145
|
-
if (!auth || !credentialStore)
|
|
156
|
+
if (!auth || !credentialStore) {
|
|
146
157
|
return { header: null };
|
|
147
|
-
|
|
158
|
+
}
|
|
159
|
+
const resolved = await raceAbort(resolveDeclaredKey(auth.tokenCredentialKey, url, { credentialStore, credentialAllowList, ctx }), signal);
|
|
148
160
|
return !resolved || 'error' in resolved
|
|
149
161
|
? { header: null }
|
|
150
162
|
: { header: `Bearer ${resolved.token}`, credentialKey: resolved.key };
|
|
@@ -2,8 +2,9 @@ import type { SkillRequires } from './manifest.ts';
|
|
|
2
2
|
export type BinVersionState = 'ok' | 'too-old' | 'unknown';
|
|
3
3
|
export declare function binOnPath(bin: string, platform?: string): boolean;
|
|
4
4
|
export declare function missingBins(bins: readonly string[], platform?: string, probe?: (bin: string, platform?: string) => boolean): string[];
|
|
5
|
-
export
|
|
5
|
+
export interface ProbeBinVersionsOptions {
|
|
6
6
|
platform?: string;
|
|
7
7
|
run: (command: string) => Promise<string | null>;
|
|
8
8
|
binPresent?: (bin: string) => boolean | Promise<boolean>;
|
|
9
|
-
}
|
|
9
|
+
}
|
|
10
|
+
export declare function probeBinVersions(requires: SkillRequires | undefined, opts: ProbeBinVersionsOptions): Promise<Map<string, BinVersionState>>;
|
|
@@ -29,20 +29,27 @@ function parseVersion(text) {
|
|
|
29
29
|
const m = text.match(/(\d+)\.(\d+)(?:\.(\d+))?/);
|
|
30
30
|
return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : null;
|
|
31
31
|
}
|
|
32
|
+
function versionState(found, min) {
|
|
33
|
+
if (!found || !min)
|
|
34
|
+
return 'unknown';
|
|
35
|
+
return isBelow(found, min) ? 'too-old' : 'ok';
|
|
36
|
+
}
|
|
32
37
|
function isBelow(found, min) {
|
|
33
38
|
for (let i = 0; i < Math.max(found.length, min.length); i++) {
|
|
34
39
|
const f = found[i] ?? 0;
|
|
35
40
|
const m = min[i] ?? 0;
|
|
36
|
-
if (f !== m)
|
|
41
|
+
if (f !== m) {
|
|
37
42
|
return f < m;
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
45
|
return false;
|
|
40
46
|
}
|
|
41
47
|
export async function probeBinVersions(requires, opts) {
|
|
42
48
|
const states = new Map();
|
|
43
49
|
const mins = requires?.binMinVersions;
|
|
44
|
-
if (!mins)
|
|
50
|
+
if (!mins) {
|
|
45
51
|
return states;
|
|
52
|
+
}
|
|
46
53
|
const binPresent = opts.binPresent ?? ((bin) => binOnPath(bin, opts.platform));
|
|
47
54
|
for (const [bin, spec] of Object.entries(mins)) {
|
|
48
55
|
if (!spec.command || !(await binPresent(bin)))
|
|
@@ -50,7 +57,7 @@ export async function probeBinVersions(requires, opts) {
|
|
|
50
57
|
const output = await opts.run(spec.command);
|
|
51
58
|
const found = output === null ? null : parseVersion(output);
|
|
52
59
|
const min = parseVersion(spec.min);
|
|
53
|
-
states.set(bin,
|
|
60
|
+
states.set(bin, versionState(found, min));
|
|
54
61
|
}
|
|
55
62
|
return states;
|
|
56
63
|
}
|
|
@@ -15,35 +15,42 @@ export function ensureBundledSkillsDir() {
|
|
|
15
15
|
}
|
|
16
16
|
async function materialize() {
|
|
17
17
|
const dir = userTmpPath('qvac-harness-skills-' + BUNDLED_SKILLS_HASH);
|
|
18
|
-
if (await validTree(dir))
|
|
18
|
+
if (await validTree(dir)) {
|
|
19
19
|
return dir;
|
|
20
|
+
}
|
|
20
21
|
const staging = `${dir}-staging-${process.pid}-${randomSuffix()}`;
|
|
21
22
|
await fs.promises.mkdir(staging, { recursive: true });
|
|
22
|
-
if (os.platform() !== 'win32')
|
|
23
|
+
if (os.platform() !== 'win32') {
|
|
23
24
|
await fs.promises.chmod(staging, 0o700);
|
|
25
|
+
}
|
|
24
26
|
try {
|
|
25
27
|
await writeTree(staging);
|
|
26
28
|
if (!(await validTree(staging))) {
|
|
27
29
|
throw new Error(`bundled skills staging dir ${staging} failed validation`);
|
|
28
30
|
}
|
|
29
|
-
if (await tryRename(staging, dir))
|
|
31
|
+
if (await tryRename(staging, dir)) {
|
|
30
32
|
return dir;
|
|
31
|
-
|
|
33
|
+
}
|
|
34
|
+
if (await validTree(dir)) {
|
|
32
35
|
return dir;
|
|
36
|
+
}
|
|
33
37
|
const trash = `${dir}.trash-${process.pid}-${randomSuffix()}`;
|
|
34
38
|
try {
|
|
35
39
|
await fs.promises.rename(dir, trash);
|
|
36
40
|
}
|
|
37
41
|
catch (err) {
|
|
38
|
-
if (await validTree(dir))
|
|
42
|
+
if (await validTree(dir)) {
|
|
39
43
|
return dir;
|
|
44
|
+
}
|
|
40
45
|
throw occupiedError(dir, err);
|
|
41
46
|
}
|
|
42
47
|
void fs.promises.rm(trash, { recursive: true, force: true });
|
|
43
|
-
if (await tryRename(staging, dir))
|
|
48
|
+
if (await tryRename(staging, dir)) {
|
|
44
49
|
return dir;
|
|
45
|
-
|
|
50
|
+
}
|
|
51
|
+
if (await validTree(dir)) {
|
|
46
52
|
return dir;
|
|
53
|
+
}
|
|
47
54
|
throw tamperError(dir);
|
|
48
55
|
}
|
|
49
56
|
finally {
|
|
@@ -81,12 +88,14 @@ async function validTree(dir) {
|
|
|
81
88
|
try {
|
|
82
89
|
if (os.platform() !== 'win32') {
|
|
83
90
|
const stat = await fs.promises.lstat(dir);
|
|
84
|
-
if (!stat.isDirectory() || (stat.mode & 0o077) !== 0)
|
|
91
|
+
if (!stat.isDirectory() || (stat.mode & 0o077) !== 0) {
|
|
85
92
|
return false;
|
|
93
|
+
}
|
|
86
94
|
}
|
|
87
95
|
const files = await collect(dir, '');
|
|
88
|
-
if (files === null)
|
|
96
|
+
if (files === null) {
|
|
89
97
|
return false;
|
|
98
|
+
}
|
|
90
99
|
return hashBundledSkills(files) === BUNDLED_SKILLS_HASH;
|
|
91
100
|
}
|
|
92
101
|
catch {
|
|
@@ -99,8 +108,9 @@ async function collect(dir, prefix) {
|
|
|
99
108
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
100
109
|
if (entry.isDirectory()) {
|
|
101
110
|
const nested = await collect(path.join(dir, entry.name), rel);
|
|
102
|
-
if (nested === null)
|
|
111
|
+
if (nested === null) {
|
|
103
112
|
return null;
|
|
113
|
+
}
|
|
104
114
|
files.push(...nested);
|
|
105
115
|
}
|
|
106
116
|
else if (entry.isFile()) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
2
|
export function hashBundledSkills(files) {
|
|
3
3
|
const hash = crypto.createHash('sha256');
|
|
4
|
-
const sorted = [...files].sort(
|
|
4
|
+
const sorted = [...files].sort(byPath);
|
|
5
5
|
for (const file of sorted) {
|
|
6
6
|
hash.update(file.path);
|
|
7
7
|
hash.update('\0');
|
|
@@ -10,3 +10,8 @@ export function hashBundledSkills(files) {
|
|
|
10
10
|
}
|
|
11
11
|
return hash.digest('hex').slice(0, 16);
|
|
12
12
|
}
|
|
13
|
+
function byPath(a, b) {
|
|
14
|
+
if (a.path < b.path)
|
|
15
|
+
return -1;
|
|
16
|
+
return a.path > b.path ? 1 : 0;
|
|
17
|
+
}
|
|
@@ -46,10 +46,11 @@ export interface ResolveSkillStatusOptions {
|
|
|
46
46
|
skipCredentialProbe?: boolean;
|
|
47
47
|
fetchImpl?: CredentialCheckFetch;
|
|
48
48
|
}
|
|
49
|
-
export
|
|
49
|
+
export interface CredentialProbeEligibilityOptions {
|
|
50
50
|
platform?: string | undefined;
|
|
51
51
|
installedTools?: ReadonlySet<string> | undefined;
|
|
52
|
-
}
|
|
52
|
+
}
|
|
53
|
+
export declare function skillsEligibleForCredentialProbe(catalog: readonly SkillCatalogEntry[], { platform, installedTools }?: CredentialProbeEligibilityOptions): SkillCatalogEntry[];
|
|
53
54
|
export declare function resolveSkillStatuses(catalog: readonly SkillCatalogEntry[], { installedTools, credentialStore, settingsStore, platform, agentId, activeProfile, binVersions, refresh, skipCredentialProbe, fetchImpl }?: ResolveSkillStatusOptions): Promise<ResolvedSkillCatalogEntry[]>;
|
|
54
55
|
export interface LoadSkillCatalogOptions {
|
|
55
56
|
platform?: string | null;
|
|
@@ -35,7 +35,7 @@ export function resolveSkillStatuses(catalog, { installedTools, credentialStore,
|
|
|
35
35
|
const probeCatalog = skillsEligibleForCredentialProbe(catalog, { platform, installedTools });
|
|
36
36
|
const checked = skipCredentialProbe
|
|
37
37
|
? Promise.resolve()
|
|
38
|
-
: validateCredentialChecks(probeCatalog, credentialStore, agentId, fetchImpl,
|
|
38
|
+
: validateCredentialChecks(probeCatalog, { credentialStore, agentId, fetchImpl, refresh });
|
|
39
39
|
return checked.then(() => Promise.all(catalog.map(async (entry) => withSetup(entry, installedTools && !isToolSupported(entry, installedTools)
|
|
40
40
|
? SKILL_STATE.unsupported
|
|
41
41
|
: await resolveSkillState(entry, {
|
|
@@ -52,9 +52,9 @@ function withSetup(entry, state) {
|
|
|
52
52
|
const setup = deriveSetup(entry, state);
|
|
53
53
|
return {
|
|
54
54
|
...rest,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
requires: requires ? stripCredentialChecks(requires) : undefined,
|
|
56
|
+
install,
|
|
57
|
+
setup,
|
|
58
58
|
status: state
|
|
59
59
|
};
|
|
60
60
|
}
|
|
@@ -82,8 +82,8 @@ function mergeOverlay(base, overlay) {
|
|
|
82
82
|
credentials: base.credentials,
|
|
83
83
|
allowList: base.allowList,
|
|
84
84
|
platform: base.platform,
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
operations: base.operations,
|
|
86
|
+
mcpReads: base.mcpReads
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
const OVERLAY_DEFAULT_PLATFORMS = ['darwin', 'linux', 'win32', 'ios', 'android'];
|
|
@@ -99,9 +99,11 @@ async function loadRoot(skillsDir, origin) {
|
|
|
99
99
|
}
|
|
100
100
|
const loaded = await Promise.all(dirs.map((dir) => loadEntry(skillsDir, dir, origin)));
|
|
101
101
|
const byName = new Map();
|
|
102
|
-
for (const entry of loaded)
|
|
103
|
-
if (entry)
|
|
102
|
+
for (const entry of loaded) {
|
|
103
|
+
if (entry) {
|
|
104
104
|
byName.set(entry.name, entry);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
105
107
|
return byName;
|
|
106
108
|
}
|
|
107
109
|
export function mcpReadMethods(catalog) {
|
|
@@ -113,11 +115,13 @@ export function mcpReadMethods(catalog) {
|
|
|
113
115
|
continue;
|
|
114
116
|
for (const url of entry.allowList) {
|
|
115
117
|
const host = hostnameOf(url);
|
|
116
|
-
if (!host)
|
|
118
|
+
if (!host) {
|
|
117
119
|
continue;
|
|
120
|
+
}
|
|
118
121
|
const methods = byHost.get(host) ?? new Set();
|
|
119
|
-
for (const method of entry.mcpReads)
|
|
122
|
+
for (const method of entry.mcpReads) {
|
|
120
123
|
methods.add(method);
|
|
124
|
+
}
|
|
121
125
|
byHost.set(host, methods);
|
|
122
126
|
}
|
|
123
127
|
}
|
|
@@ -134,6 +138,11 @@ function hostnameOf(url) {
|
|
|
134
138
|
export function detectPlatform() {
|
|
135
139
|
return platform();
|
|
136
140
|
}
|
|
141
|
+
function platformsOf(declared, origin) {
|
|
142
|
+
if (declared && declared.length > 0)
|
|
143
|
+
return declared;
|
|
144
|
+
return origin === 'overlay' ? OVERLAY_DEFAULT_PLATFORMS : [];
|
|
145
|
+
}
|
|
137
146
|
async function loadEntry(skillsDir, dir, origin) {
|
|
138
147
|
try {
|
|
139
148
|
const skillDir = path.join(skillsDir, dir);
|
|
@@ -143,31 +152,27 @@ async function loadEntry(skillsDir, dir, origin) {
|
|
|
143
152
|
const cli = await loadSkillCli(skillDir, tools);
|
|
144
153
|
const referenceFiles = await loadReferenceFiles(skillDir);
|
|
145
154
|
const operations = origin === 'overlay' ? [] : await loadOperations(skillDir, dir);
|
|
146
|
-
const platform = meta.platform
|
|
147
|
-
? meta.platform
|
|
148
|
-
: origin === 'overlay'
|
|
149
|
-
? OVERLAY_DEFAULT_PLATFORMS
|
|
150
|
-
: [];
|
|
155
|
+
const platform = platformsOf(meta.platform, origin);
|
|
151
156
|
return {
|
|
152
157
|
name: dir,
|
|
153
158
|
description: meta.description ?? '',
|
|
154
|
-
|
|
155
|
-
|
|
159
|
+
aliases: meta.aliases,
|
|
160
|
+
preloadOnName: meta.preloadOnName === false ? false : undefined,
|
|
156
161
|
tools,
|
|
157
162
|
credentials: meta.credentials ?? [],
|
|
158
163
|
allowList: meta.allowList ?? [],
|
|
159
|
-
|
|
164
|
+
mcpReads: meta.mcpReads,
|
|
160
165
|
platform,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
requires: meta.requires,
|
|
167
|
+
install: meta.install,
|
|
168
|
+
setup: meta.setup,
|
|
169
|
+
cliValidator: cli ? buildCliValidator(cli) : undefined,
|
|
170
|
+
stub: meta.stub,
|
|
171
|
+
minCapabilityProfile: meta.minCapabilityProfile || undefined,
|
|
172
|
+
emoji: meta.emoji || undefined,
|
|
173
|
+
referenceFiles: referenceFiles.length ? referenceFiles : undefined,
|
|
174
|
+
operations: operations.length ? operations : undefined,
|
|
175
|
+
origin
|
|
171
176
|
};
|
|
172
177
|
}
|
|
173
178
|
catch {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { JsonSchema } from './cli-schema-types.ts';
|
|
2
|
-
export
|
|
2
|
+
export interface CommandWalk {
|
|
3
3
|
node: JsonSchema;
|
|
4
4
|
pathSegs: string[];
|
|
5
5
|
index: number;
|
|
6
|
-
}
|
|
6
|
+
}
|
|
7
|
+
export declare function walkCommands(argv: readonly string[], schema: JsonSchema): CommandWalk;
|
|
7
8
|
export declare function argvToObject(argv: readonly string[], schema: JsonSchema): Record<string, unknown> | string;
|
|
8
9
|
export declare function objectProps(schema: JsonSchema): Readonly<Record<string, JsonSchema>>;
|
|
9
10
|
export declare function isObjectSchema(schema: JsonSchema): boolean;
|
|
@@ -4,11 +4,13 @@ export function walkCommands(argv, schema) {
|
|
|
4
4
|
let i = 0;
|
|
5
5
|
while (i < argv.length) {
|
|
6
6
|
const tok = argv[i] ?? '';
|
|
7
|
-
if (isParamToken(tok))
|
|
7
|
+
if (isParamToken(tok)) {
|
|
8
8
|
break;
|
|
9
|
+
}
|
|
9
10
|
const child = objectProps(node)[tok];
|
|
10
|
-
if (!child || !isObjectSchema(child))
|
|
11
|
+
if (!child || !isObjectSchema(child)) {
|
|
11
12
|
break;
|
|
13
|
+
}
|
|
12
14
|
pathSegs.push(tok);
|
|
13
15
|
node = child;
|
|
14
16
|
i++;
|
|
@@ -18,13 +20,10 @@ export function walkCommands(argv, schema) {
|
|
|
18
20
|
export function argvToObject(argv, schema) {
|
|
19
21
|
const { node, pathSegs, index } = walkCommands(argv, schema);
|
|
20
22
|
const leaf = parseParams(argv.slice(index), node);
|
|
21
|
-
if (typeof leaf === 'string')
|
|
23
|
+
if (typeof leaf === 'string') {
|
|
22
24
|
return leaf;
|
|
23
|
-
let out = leaf;
|
|
24
|
-
for (let p = pathSegs.length - 1; p >= 0; p--) {
|
|
25
|
-
out = { [pathSegs[p]]: out };
|
|
26
25
|
}
|
|
27
|
-
return
|
|
26
|
+
return pathSegs.reduceRight((inner, seg) => ({ [seg]: inner }), leaf);
|
|
28
27
|
}
|
|
29
28
|
export function objectProps(schema) {
|
|
30
29
|
return schema.properties ?? {};
|
|
@@ -37,13 +36,25 @@ function isParamToken(tok) {
|
|
|
37
36
|
return false;
|
|
38
37
|
if (tok.startsWith('-'))
|
|
39
38
|
return true;
|
|
40
|
-
if (tok.includes('='))
|
|
39
|
+
if (tok.includes('=')) {
|
|
41
40
|
return true;
|
|
41
|
+
}
|
|
42
42
|
return false;
|
|
43
43
|
}
|
|
44
|
+
function flagParts(tok) {
|
|
45
|
+
if (!tok.startsWith('--')) {
|
|
46
|
+
return { key: tok.slice(1), inline: undefined };
|
|
47
|
+
}
|
|
48
|
+
const body = tok.slice(2);
|
|
49
|
+
const ieq = body.indexOf('=');
|
|
50
|
+
if (ieq < 0) {
|
|
51
|
+
return { key: body, inline: undefined };
|
|
52
|
+
}
|
|
53
|
+
return { key: body.slice(0, ieq), inline: body.slice(ieq + 1) };
|
|
54
|
+
}
|
|
44
55
|
function parseParams(tokens, schema) {
|
|
45
56
|
const props = objectProps(schema);
|
|
46
|
-
const
|
|
57
|
+
const propSchemas = new Map(Object.entries(props));
|
|
47
58
|
const boolKeys = new Set(Object.entries(props)
|
|
48
59
|
.filter(([, s]) => s.type === 'boolean')
|
|
49
60
|
.map(([k]) => k));
|
|
@@ -59,67 +70,58 @@ function parseParams(tokens, schema) {
|
|
|
59
70
|
const eq = tok.indexOf('=');
|
|
60
71
|
if (eq > 0 && !tok.startsWith('-')) {
|
|
61
72
|
const key = tok.slice(0, eq);
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
const keySchema = propSchemas.get(key);
|
|
74
|
+
if (keySchema !== undefined) {
|
|
75
|
+
out[key] = coerce(tok.slice(eq + 1), keySchema);
|
|
64
76
|
i++;
|
|
65
77
|
continue;
|
|
66
78
|
}
|
|
67
|
-
if (!canAbsorb)
|
|
79
|
+
if (!canAbsorb) {
|
|
68
80
|
return `unknown parameter "${key}"`;
|
|
69
|
-
}
|
|
70
|
-
if (tok.startsWith('-')) {
|
|
71
|
-
let key;
|
|
72
|
-
let inline;
|
|
73
|
-
if (tok.startsWith('--')) {
|
|
74
|
-
const body = tok.slice(2);
|
|
75
|
-
const ieq = body.indexOf('=');
|
|
76
|
-
if (ieq >= 0) {
|
|
77
|
-
key = body.slice(0, ieq);
|
|
78
|
-
inline = body.slice(ieq + 1);
|
|
79
|
-
}
|
|
80
|
-
else
|
|
81
|
-
key = body;
|
|
82
81
|
}
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
}
|
|
83
|
+
const flag = tok.startsWith('-') ? flagParts(tok) : null;
|
|
84
|
+
const flagSchema = flag ? propSchemas.get(flag.key) : undefined;
|
|
85
|
+
if (flag && flagSchema !== undefined) {
|
|
86
|
+
const { key, inline } = flag;
|
|
87
|
+
if (boolKeys.has(key)) {
|
|
88
|
+
out[key] = inline !== undefined ? inline !== 'false' && inline !== '0' : true;
|
|
89
|
+
i++;
|
|
90
|
+
continue;
|
|
85
91
|
}
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
i++;
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (inline !== undefined) {
|
|
93
|
-
out[key] = coerce(inline, props[key]);
|
|
94
|
-
i++;
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
const next = tokens[i + 1];
|
|
98
|
-
if (next === undefined || next.startsWith('-')) {
|
|
99
|
-
return `flag --${key} requires a value`;
|
|
100
|
-
}
|
|
101
|
-
out[key] = coerce(next, props[key]);
|
|
102
|
-
i += 2;
|
|
92
|
+
if (inline !== undefined) {
|
|
93
|
+
out[key] = coerce(inline, flagSchema);
|
|
94
|
+
i++;
|
|
103
95
|
continue;
|
|
104
96
|
}
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
const next = tokens[i + 1];
|
|
98
|
+
if (next === undefined || next.startsWith('-')) {
|
|
99
|
+
return `flag --${key} requires a value`;
|
|
100
|
+
}
|
|
101
|
+
out[key] = coerce(next, flagSchema);
|
|
102
|
+
i += 2;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (flag && !canAbsorb) {
|
|
106
|
+
return `unknown flag "--${flag.key}"`;
|
|
107
107
|
}
|
|
108
108
|
if (boolKeys.has(tok)) {
|
|
109
109
|
out[tok] = true;
|
|
110
110
|
i++;
|
|
111
111
|
continue;
|
|
112
112
|
}
|
|
113
|
-
if (
|
|
113
|
+
if (propSchemas.get(tok)?.type === 'string' && !canAbsorb) {
|
|
114
114
|
out[tok] = '';
|
|
115
115
|
i++;
|
|
116
116
|
continue;
|
|
117
117
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
const name = positionals[posIdx];
|
|
119
|
+
if (name !== undefined) {
|
|
120
|
+
const nameSchema = propSchemas.get(name);
|
|
121
|
+
if (nameSchema === undefined) {
|
|
121
122
|
return `x-positionals names unknown property "${name}"`;
|
|
122
|
-
|
|
123
|
+
}
|
|
124
|
+
out[name] = coerce(tok, nameSchema);
|
|
123
125
|
posIdx++;
|
|
124
126
|
i++;
|
|
125
127
|
continue;
|
|
@@ -131,8 +133,9 @@ function parseParams(tokens, schema) {
|
|
|
131
133
|
}
|
|
132
134
|
return `unexpected argument "${tok}"`;
|
|
133
135
|
}
|
|
134
|
-
if (restName && restVals.length > 0)
|
|
136
|
+
if (restName && restVals.length > 0) {
|
|
135
137
|
out[restName] = restVals;
|
|
138
|
+
}
|
|
136
139
|
return out;
|
|
137
140
|
}
|
|
138
141
|
function coerce(value, schema) {
|