@asterxsk/kiln 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +170 -170
- package/agent/AGENTS.md +67 -67
- package/agent/README.md +5 -5
- package/agent/extensions/AGENTS.md +68 -68
- package/agent/extensions/ask-user/index.ts +418 -418
- package/agent/extensions/ask-user/package-lock.json +769 -769
- package/agent/extensions/ask-user/package.json +19 -19
- package/agent/extensions/ask-user/prompt.ts +45 -45
- package/agent/extensions/ask-user/tsconfig.json +7 -7
- package/agent/extensions/background-terminals/docs/implementation-guide.md +942 -942
- package/agent/extensions/background-terminals/index.ts +627 -627
- package/agent/extensions/background-terminals/manager.test.ts +735 -735
- package/agent/extensions/background-terminals/output.test.ts +109 -109
- package/agent/extensions/background-terminals/package-lock.json +769 -769
- package/agent/extensions/background-terminals/package.json +17 -17
- package/agent/extensions/background-terminals/prompt.test.ts +125 -125
- package/agent/extensions/background-terminals/ps.test.ts +82 -82
- package/agent/extensions/background-terminals/result-delivery.test.ts +44 -44
- package/agent/extensions/background-terminals/src/domain.ts +87 -87
- package/agent/extensions/background-terminals/src/manager.ts +907 -907
- package/agent/extensions/background-terminals/src/output.ts +84 -84
- package/agent/extensions/background-terminals/src/prompt.ts +142 -142
- package/agent/extensions/background-terminals/src/result-delivery.ts +27 -27
- package/agent/extensions/background-terminals/src/runtime.ts +36 -36
- package/agent/extensions/background-terminals/src/ui/output-view.ts +79 -79
- package/agent/extensions/background-terminals/src/ui/ps.ts +621 -621
- package/agent/extensions/background-terminals/tsconfig.json +7 -7
- package/agent/extensions/file-search/index.spec.ts +443 -443
- package/agent/extensions/file-search/index.ts +459 -459
- package/agent/extensions/file-search/package-lock.json +2253 -2253
- package/agent/extensions/file-search/package.json +23 -23
- package/agent/extensions/file-search/src/args.ts +122 -122
- package/agent/extensions/file-search/src/binaries.ts +422 -422
- package/agent/extensions/file-search/src/output.ts +126 -126
- package/agent/extensions/file-search/src/process.ts +146 -146
- package/agent/extensions/file-search/src/prompt.ts +52 -52
- package/agent/extensions/file-search/tsconfig.json +7 -7
- package/agent/extensions/modelconf/PLAN.md +915 -915
- package/agent/extensions/modelconf/index.ts +296 -296
- package/agent/extensions/modelconf/src/ui/ModelConfView.ts +1101 -1101
- package/agent/extensions/pi-web-access/CHANGELOG.md +690 -690
- package/agent/extensions/pi-web-access/LICENSE +21 -21
- package/agent/extensions/pi-web-access/README.md +470 -470
- package/agent/extensions/pi-web-access/SECURITY.md +5 -5
- package/agent/extensions/pi-web-access/activity.ts +101 -101
- package/agent/extensions/pi-web-access/auth-fetch.ts +148 -148
- package/agent/extensions/pi-web-access/brightdata-unlocker.ts +272 -272
- package/agent/extensions/pi-web-access/chrome-cookies.ts +669 -669
- package/agent/extensions/pi-web-access/content-find.ts +139 -139
- package/agent/extensions/pi-web-access/credential-source.ts +191 -191
- package/agent/extensions/pi-web-access/data-uri-sanitize.ts +406 -406
- package/agent/extensions/pi-web-access/datalab-pdf-extract.ts +568 -568
- package/agent/extensions/pi-web-access/declared-web-links.ts +173 -173
- package/agent/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -496
- package/agent/extensions/pi-web-access/evidence/contract-probe.mjs +140 -140
- package/agent/extensions/pi-web-access/exa.ts +526 -526
- package/agent/extensions/pi-web-access/extract.ts +1196 -1196
- package/agent/extensions/pi-web-access/feature-config.ts +29 -29
- package/agent/extensions/pi-web-access/fetch-params.ts +111 -111
- package/agent/extensions/pi-web-access/gemini-adc.ts +298 -298
- package/agent/extensions/pi-web-access/gemini-api.ts +353 -353
- package/agent/extensions/pi-web-access/gemini-pdf-extract.ts +108 -108
- package/agent/extensions/pi-web-access/gemini-url-context.ts +128 -128
- package/agent/extensions/pi-web-access/gemini-web-config.ts +101 -101
- package/agent/extensions/pi-web-access/gemini-web.ts +487 -487
- package/agent/extensions/pi-web-access/github-api.ts +197 -197
- package/agent/extensions/pi-web-access/github-extract.ts +746 -746
- package/agent/extensions/pi-web-access/github-issue-pr.ts +700 -700
- package/agent/extensions/pi-web-access/index.ts +1737 -1737
- package/agent/extensions/pi-web-access/package-lock.json +5808 -5808
- package/agent/extensions/pi-web-access/package.json +64 -64
- package/agent/extensions/pi-web-access/page-query.ts +96 -96
- package/agent/extensions/pi-web-access/pdf-extract.ts +409 -409
- package/agent/extensions/pi-web-access/promise-try.d.ts +7 -7
- package/agent/extensions/pi-web-access/query-rewrite.ts +51 -51
- package/agent/extensions/pi-web-access/render-search-error.ts +170 -170
- package/agent/extensions/pi-web-access/rsc-extract.ts +338 -338
- package/agent/extensions/pi-web-access/source-check.ts +282 -282
- package/agent/extensions/pi-web-access/ssrf-protection.ts +526 -526
- package/agent/extensions/pi-web-access/storage.ts +521 -521
- package/agent/extensions/pi-web-access/summary-model-scope.ts +125 -125
- package/agent/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -208
- package/agent/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -840
- package/agent/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -441
- package/agent/extensions/pi-web-access/test/config-path.test.mjs +283 -283
- package/agent/extensions/pi-web-access/test/content-find.test.mjs +25 -25
- package/agent/extensions/pi-web-access/test/credential-source.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -210
- package/agent/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -552
- package/agent/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -212
- package/agent/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -40
- package/agent/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -334
- package/agent/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -95
- package/agent/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -53
- package/agent/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -92
- package/agent/extensions/pi-web-access/test/fetch-params.test.mjs +86 -86
- package/agent/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -34
- package/agent/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -173
- package/agent/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -257
- package/agent/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -170
- package/agent/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -133
- package/agent/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -178
- package/agent/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -148
- package/agent/extensions/pi-web-access/test/get-search-content.test.mjs +223 -223
- package/agent/extensions/pi-web-access/test/github-extract.test.mjs +378 -378
- package/agent/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -565
- package/agent/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -99
- package/agent/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -50
- package/agent/extensions/pi-web-access/test/page-query.test.mjs +51 -51
- package/agent/extensions/pi-web-access/test/pdf-config.test.mjs +140 -140
- package/agent/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -500
- package/agent/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -286
- package/agent/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -102
- package/agent/extensions/pi-web-access/test/search-error-render.test.mjs +152 -152
- package/agent/extensions/pi-web-access/test/search-providers.test.mjs +274 -274
- package/agent/extensions/pi-web-access/test/source-check.test.mjs +179 -179
- package/agent/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -205
- package/agent/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -456
- package/agent/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -182
- package/agent/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -64
- package/agent/extensions/pi-web-access/tsconfig.json +11 -11
- package/agent/extensions/pi-web-access/utils.ts +451 -451
- package/agent/extensions/pi-web-access/video-extract.ts +392 -392
- package/agent/extensions/pi-web-access/youtube-extract.ts +328 -328
- package/agent/extensions/shared/activity-status.ts +31 -31
- package/agent/extensions/shared/child-session.test.ts +270 -270
- package/agent/extensions/shared/child-session.ts +148 -148
- package/agent/extensions/shared/context-utilization.test.ts +48 -48
- package/agent/extensions/shared/context-utilization.ts +47 -47
- package/agent/extensions/shared/dashboard-state.ts +99 -99
- package/agent/extensions/shared/tool-call-timeout.test.ts +117 -117
- package/agent/extensions/shared/tool-call-timeout.ts +104 -104
- package/agent/extensions/subagents/by-the-way.test.ts +29 -29
- package/agent/extensions/subagents/claude.test.ts +119 -119
- package/agent/extensions/subagents/codex.test.ts +102 -102
- package/agent/extensions/subagents/context-usage.test.ts +107 -107
- package/agent/extensions/subagents/docs/design-plan.md +568 -568
- package/agent/extensions/subagents/docs/effect-v4-extension-guide.md +354 -354
- package/agent/extensions/subagents/docs/effect-v4-notes.md +571 -571
- package/agent/extensions/subagents/index.ts +779 -779
- package/agent/extensions/subagents/manager.test.ts +276 -276
- package/agent/extensions/subagents/package-lock.json +2244 -2244
- package/agent/extensions/subagents/package.json +19 -19
- package/agent/extensions/subagents/result-delivery.test.ts +27 -27
- package/agent/extensions/subagents/src/backend.ts +73 -73
- package/agent/extensions/subagents/src/backends/claude.ts +701 -701
- package/agent/extensions/subagents/src/backends/codex.ts +1060 -1060
- package/agent/extensions/subagents/src/backends/pi.ts +575 -575
- package/agent/extensions/subagents/src/backends/stub.ts +300 -300
- package/agent/extensions/subagents/src/by-the-way.ts +21 -21
- package/agent/extensions/subagents/src/domain.ts +253 -253
- package/agent/extensions/subagents/src/format.ts +74 -74
- package/agent/extensions/subagents/src/manager.ts +736 -736
- package/agent/extensions/subagents/src/prompt.ts +92 -92
- package/agent/extensions/subagents/src/result-delivery.ts +20 -20
- package/agent/extensions/subagents/src/runtime.ts +53 -53
- package/agent/extensions/subagents/src/ui/takeover.ts +583 -583
- package/agent/extensions/subagents/src/ui/transcript.ts +201 -201
- package/agent/extensions/subagents/takeover.test.ts +29 -29
- package/agent/extensions/subagents/tsconfig.json +7 -7
- package/agent/extensions/todo/AGENTS.md +38 -38
- package/agent/extensions/todo/LICENSE +21 -21
- package/agent/extensions/todo/config.ts +55 -55
- package/agent/extensions/todo/index.ts +151 -151
- package/agent/extensions/todo/locales/de.json +17 -17
- package/agent/extensions/todo/locales/en.json +15 -15
- package/agent/extensions/todo/locales/es.json +17 -17
- package/agent/extensions/todo/locales/fr.json +17 -17
- package/agent/extensions/todo/locales/pt-BR.json +17 -17
- package/agent/extensions/todo/locales/pt.json +17 -17
- package/agent/extensions/todo/locales/ru.json +17 -17
- package/agent/extensions/todo/locales/uk.json +17 -17
- package/agent/extensions/todo/locales/zh.json +17 -17
- package/agent/extensions/todo/package-lock.json +3358 -3358
- package/agent/extensions/todo/package.json +67 -67
- package/agent/extensions/todo/state/i18n-bridge.ts +64 -64
- package/agent/extensions/todo/state/invariants.ts +20 -20
- package/agent/extensions/todo/state/replay.ts +38 -38
- package/agent/extensions/todo/state/selectors.ts +107 -107
- package/agent/extensions/todo/state/state-reducer.ts +326 -326
- package/agent/extensions/todo/state/state.ts +18 -18
- package/agent/extensions/todo/state/store.ts +82 -82
- package/agent/extensions/todo/state/task-graph.ts +57 -57
- package/agent/extensions/todo/todo-overlay.ts +200 -200
- package/agent/extensions/todo/todo.ts +155 -155
- package/agent/extensions/todo/tool/response-envelope.ts +109 -109
- package/agent/extensions/todo/tool/types.ts +206 -206
- package/agent/extensions/todo/view/format.ts +177 -177
- package/agent/install.ps1 +637 -527
- package/agent/install.sh +620 -511
- package/agent/keybindings.json +7 -7
- package/bin/kiln.js +124 -11
- package/package.json +8 -2
- package/agent/extensions/taste/index.ts +0 -443
- package/agent/extensions/taste/install.ps1 +0 -23
- package/agent/extensions/taste/install.sh +0 -21
- /package/agent/extensions/{status line → statusline}/index.ts +0 -0
- /package/agent/extensions/{status line → statusline}/install.ps1 +0 -0
- /package/agent/extensions/{status line → statusline}/install.sh +0 -0
|
@@ -1,118 +1,118 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { test } from "node:test";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
CredentialResolutionError,
|
|
6
|
-
hasCredentialSource,
|
|
7
|
-
resolveCredential,
|
|
8
|
-
} from "../credential-source.ts";
|
|
9
|
-
|
|
10
|
-
const fake = value => async () => ({ stdout: value });
|
|
11
|
-
|
|
12
|
-
function options(configuredValue, environmentValue) {
|
|
13
|
-
return { provider: "Synthetic", configuredValue, environmentValue };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
test("literal and legacy environment credentials preserve existing precedence", async () => {
|
|
17
|
-
assert.equal(await resolveCredential(options("literal-value", "environment-value")), "environment-value");
|
|
18
|
-
assert.equal(await resolveCredential(options("literal-value", undefined)), "literal-value");
|
|
19
|
-
assert.equal(await resolveCredential(options(undefined, "environment-value")), "environment-value");
|
|
20
|
-
assert.equal(await resolveCredential(options(undefined, undefined)), null);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("explicit environment sources use only the named variable", async () => {
|
|
24
|
-
assert.equal(await resolveCredential({
|
|
25
|
-
...options("${SCOPED_SYNTHETIC_KEY}", "stale-legacy-value"),
|
|
26
|
-
environment: { SCOPED_SYNTHETIC_KEY: "scoped-value" },
|
|
27
|
-
}), "scoped-value");
|
|
28
|
-
await assert.rejects(
|
|
29
|
-
resolveCredential({
|
|
30
|
-
...options("$SCOPED_SYNTHETIC_KEY", "stale-legacy-value"),
|
|
31
|
-
environment: {},
|
|
32
|
-
}),
|
|
33
|
-
err => err instanceof CredentialResolutionError && err.category === "environment-empty",
|
|
34
|
-
);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("command sources override stale values, remain lazy, and rotate per resolution", async () => {
|
|
38
|
-
let calls = 0;
|
|
39
|
-
const runCommand = async (command, runOptions) => {
|
|
40
|
-
calls += 1;
|
|
41
|
-
assert.equal(command, "/trusted/read synthetic");
|
|
42
|
-
assert.deepEqual(runOptions.environment, {
|
|
43
|
-
HOME: "/synthetic/home",
|
|
44
|
-
PATH: "/usr/bin:/bin",
|
|
45
|
-
OP_SESSION_my: "synthetic-password-manager-session",
|
|
46
|
-
});
|
|
47
|
-
return { stdout: calls === 1 ? "first-value\n" : "second-value\n" };
|
|
48
|
-
};
|
|
49
|
-
const commandOptions = {
|
|
50
|
-
...options("!/trusted/read synthetic", "stale-environment-value"),
|
|
51
|
-
runCommand,
|
|
52
|
-
environment: {
|
|
53
|
-
HOME: "/synthetic/home",
|
|
54
|
-
PATH: "/usr/bin:/bin",
|
|
55
|
-
EXA_API_KEY: "stale-provider-key-must-not-reach-command",
|
|
56
|
-
OP_SESSION_my: "synthetic-password-manager-session",
|
|
57
|
-
OP_SERVICE_ACCOUNT_TOKEN: "must-not-reach-command",
|
|
58
|
-
NODE_OPTIONS: "--require=untrusted.js",
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
assert.equal(hasCredentialSource(commandOptions), true);
|
|
63
|
-
assert.equal(calls, 0);
|
|
64
|
-
assert.equal(await resolveCredential(commandOptions), "first-value");
|
|
65
|
-
assert.equal(await resolveCredential(commandOptions), "second-value");
|
|
66
|
-
assert.equal(calls, 2);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("command output must be one non-empty bounded value", async () => {
|
|
70
|
-
for (const [stdout, category] of [
|
|
71
|
-
["", "command-empty"],
|
|
72
|
-
["one\ntwo\n", "command-invalid-output"],
|
|
73
|
-
["x".repeat(16_385), "command-output-too-large"],
|
|
74
|
-
]) {
|
|
75
|
-
await assert.rejects(
|
|
76
|
-
resolveCredential({ ...options("!ignored", undefined), runCommand: fake(stdout) }),
|
|
77
|
-
err => err instanceof CredentialResolutionError && err.category === category,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("command failures are categorized and redact command output", async () => {
|
|
83
|
-
const secret = "SYNTHETIC_SECRET_MUST_NOT_ESCAPE";
|
|
84
|
-
for (const [failure, category] of [
|
|
85
|
-
[Object.assign(new Error(`spawn failed ${secret}`), { code: "ENOENT", stderr: secret }), "command-failed"],
|
|
86
|
-
[Object.assign(new Error(`timed out ${secret}`), { killed: true, stderr: secret }), "command-timeout"],
|
|
87
|
-
[Object.assign(new Error(`too large ${secret}`), { code: "ERR_CHILD_PROCESS_STDIO_MAXBUFFER", stderr: secret }), "command-output-too-large"],
|
|
88
|
-
]) {
|
|
89
|
-
await assert.rejects(
|
|
90
|
-
resolveCredential({
|
|
91
|
-
...options("!/trusted/read synthetic", "stale-value"),
|
|
92
|
-
runCommand: async () => { throw failure; },
|
|
93
|
-
}),
|
|
94
|
-
err => {
|
|
95
|
-
assert.equal(err instanceof CredentialResolutionError, true);
|
|
96
|
-
assert.equal(err.category, category);
|
|
97
|
-
assert.equal(err.message.includes(secret), false);
|
|
98
|
-
assert.equal(err.message.includes("/trusted/read"), false);
|
|
99
|
-
return true;
|
|
100
|
-
},
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("escaped source prefixes remain literal and override legacy environment values", async () => {
|
|
106
|
-
assert.equal(await resolveCredential(options("$$OPENAI_API_KEY", "legacy-value")), "$OPENAI_API_KEY");
|
|
107
|
-
assert.equal(await resolveCredential(options("$!literal-command", "legacy-value")), "!literal-command");
|
|
108
|
-
assert.equal(hasCredentialSource(options("$$OPENAI_API_KEY", undefined)), true);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test("malformed explicit sources fail closed instead of becoming literals", async () => {
|
|
112
|
-
for (const source of ["!", "$BAD-NAME", "${UNCLOSED"]) {
|
|
113
|
-
await assert.rejects(
|
|
114
|
-
resolveCredential(options(source, "stale-value")),
|
|
115
|
-
err => err instanceof CredentialResolutionError && err.category === "invalid-source",
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CredentialResolutionError,
|
|
6
|
+
hasCredentialSource,
|
|
7
|
+
resolveCredential,
|
|
8
|
+
} from "../credential-source.ts";
|
|
9
|
+
|
|
10
|
+
const fake = value => async () => ({ stdout: value });
|
|
11
|
+
|
|
12
|
+
function options(configuredValue, environmentValue) {
|
|
13
|
+
return { provider: "Synthetic", configuredValue, environmentValue };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test("literal and legacy environment credentials preserve existing precedence", async () => {
|
|
17
|
+
assert.equal(await resolveCredential(options("literal-value", "environment-value")), "environment-value");
|
|
18
|
+
assert.equal(await resolveCredential(options("literal-value", undefined)), "literal-value");
|
|
19
|
+
assert.equal(await resolveCredential(options(undefined, "environment-value")), "environment-value");
|
|
20
|
+
assert.equal(await resolveCredential(options(undefined, undefined)), null);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("explicit environment sources use only the named variable", async () => {
|
|
24
|
+
assert.equal(await resolveCredential({
|
|
25
|
+
...options("${SCOPED_SYNTHETIC_KEY}", "stale-legacy-value"),
|
|
26
|
+
environment: { SCOPED_SYNTHETIC_KEY: "scoped-value" },
|
|
27
|
+
}), "scoped-value");
|
|
28
|
+
await assert.rejects(
|
|
29
|
+
resolveCredential({
|
|
30
|
+
...options("$SCOPED_SYNTHETIC_KEY", "stale-legacy-value"),
|
|
31
|
+
environment: {},
|
|
32
|
+
}),
|
|
33
|
+
err => err instanceof CredentialResolutionError && err.category === "environment-empty",
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("command sources override stale values, remain lazy, and rotate per resolution", async () => {
|
|
38
|
+
let calls = 0;
|
|
39
|
+
const runCommand = async (command, runOptions) => {
|
|
40
|
+
calls += 1;
|
|
41
|
+
assert.equal(command, "/trusted/read synthetic");
|
|
42
|
+
assert.deepEqual(runOptions.environment, {
|
|
43
|
+
HOME: "/synthetic/home",
|
|
44
|
+
PATH: "/usr/bin:/bin",
|
|
45
|
+
OP_SESSION_my: "synthetic-password-manager-session",
|
|
46
|
+
});
|
|
47
|
+
return { stdout: calls === 1 ? "first-value\n" : "second-value\n" };
|
|
48
|
+
};
|
|
49
|
+
const commandOptions = {
|
|
50
|
+
...options("!/trusted/read synthetic", "stale-environment-value"),
|
|
51
|
+
runCommand,
|
|
52
|
+
environment: {
|
|
53
|
+
HOME: "/synthetic/home",
|
|
54
|
+
PATH: "/usr/bin:/bin",
|
|
55
|
+
EXA_API_KEY: "stale-provider-key-must-not-reach-command",
|
|
56
|
+
OP_SESSION_my: "synthetic-password-manager-session",
|
|
57
|
+
OP_SERVICE_ACCOUNT_TOKEN: "must-not-reach-command",
|
|
58
|
+
NODE_OPTIONS: "--require=untrusted.js",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
assert.equal(hasCredentialSource(commandOptions), true);
|
|
63
|
+
assert.equal(calls, 0);
|
|
64
|
+
assert.equal(await resolveCredential(commandOptions), "first-value");
|
|
65
|
+
assert.equal(await resolveCredential(commandOptions), "second-value");
|
|
66
|
+
assert.equal(calls, 2);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("command output must be one non-empty bounded value", async () => {
|
|
70
|
+
for (const [stdout, category] of [
|
|
71
|
+
["", "command-empty"],
|
|
72
|
+
["one\ntwo\n", "command-invalid-output"],
|
|
73
|
+
["x".repeat(16_385), "command-output-too-large"],
|
|
74
|
+
]) {
|
|
75
|
+
await assert.rejects(
|
|
76
|
+
resolveCredential({ ...options("!ignored", undefined), runCommand: fake(stdout) }),
|
|
77
|
+
err => err instanceof CredentialResolutionError && err.category === category,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("command failures are categorized and redact command output", async () => {
|
|
83
|
+
const secret = "SYNTHETIC_SECRET_MUST_NOT_ESCAPE";
|
|
84
|
+
for (const [failure, category] of [
|
|
85
|
+
[Object.assign(new Error(`spawn failed ${secret}`), { code: "ENOENT", stderr: secret }), "command-failed"],
|
|
86
|
+
[Object.assign(new Error(`timed out ${secret}`), { killed: true, stderr: secret }), "command-timeout"],
|
|
87
|
+
[Object.assign(new Error(`too large ${secret}`), { code: "ERR_CHILD_PROCESS_STDIO_MAXBUFFER", stderr: secret }), "command-output-too-large"],
|
|
88
|
+
]) {
|
|
89
|
+
await assert.rejects(
|
|
90
|
+
resolveCredential({
|
|
91
|
+
...options("!/trusted/read synthetic", "stale-value"),
|
|
92
|
+
runCommand: async () => { throw failure; },
|
|
93
|
+
}),
|
|
94
|
+
err => {
|
|
95
|
+
assert.equal(err instanceof CredentialResolutionError, true);
|
|
96
|
+
assert.equal(err.category, category);
|
|
97
|
+
assert.equal(err.message.includes(secret), false);
|
|
98
|
+
assert.equal(err.message.includes("/trusted/read"), false);
|
|
99
|
+
return true;
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("escaped source prefixes remain literal and override legacy environment values", async () => {
|
|
106
|
+
assert.equal(await resolveCredential(options("$$OPENAI_API_KEY", "legacy-value")), "$OPENAI_API_KEY");
|
|
107
|
+
assert.equal(await resolveCredential(options("$!literal-command", "legacy-value")), "!literal-command");
|
|
108
|
+
assert.equal(hasCredentialSource(options("$$OPENAI_API_KEY", undefined)), true);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("malformed explicit sources fail closed instead of becoming literals", async () => {
|
|
112
|
+
for (const source of ["!", "$BAD-NAME", "${UNCLOSED"]) {
|
|
113
|
+
await assert.rejects(
|
|
114
|
+
resolveCredential(options(source, "stale-value")),
|
|
115
|
+
err => err instanceof CredentialResolutionError && err.category === "invalid-source",
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
});
|