@vibe-agent-toolkit/utils 0.2.0-rc.1 → 0.2.0-rc.11
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/README.md +69 -23
- package/dist/asset-reference.js +3 -11
- package/dist/asset-reference.js.map +1 -1
- package/dist/compare-code-units.d.ts +28 -0
- package/dist/compare-code-units.d.ts.map +1 -0
- package/dist/compare-code-units.js +33 -0
- package/dist/compare-code-units.js.map +1 -0
- package/dist/crawl-timing.d.ts +657 -0
- package/dist/crawl-timing.d.ts.map +1 -0
- package/dist/crawl-timing.js +765 -0
- package/dist/crawl-timing.js.map +1 -0
- package/dist/crawl.d.ts +2 -4
- package/dist/crawl.d.ts.map +1 -1
- package/dist/crawl.js +2 -4
- package/dist/crawl.js.map +1 -1
- package/dist/dirent-kind.d.ts +83 -0
- package/dist/dirent-kind.d.ts.map +1 -0
- package/dist/dirent-kind.js +129 -0
- package/dist/dirent-kind.js.map +1 -0
- package/dist/entrypoint.d.ts +54 -0
- package/dist/entrypoint.d.ts.map +1 -0
- package/dist/entrypoint.js +71 -0
- package/dist/entrypoint.js.map +1 -0
- package/dist/env-flag.d.ts +57 -0
- package/dist/env-flag.d.ts.map +1 -0
- package/dist/env-flag.js +68 -0
- package/dist/env-flag.js.map +1 -0
- package/dist/errors/errno.d.ts +51 -0
- package/dist/errors/errno.d.ts.map +1 -0
- package/dist/errors/errno.js +112 -0
- package/dist/errors/errno.js.map +1 -0
- package/dist/errors/vat-error.d.ts +60 -0
- package/dist/errors/vat-error.d.ts.map +1 -0
- package/dist/errors/vat-error.js +86 -0
- package/dist/errors/vat-error.js.map +1 -0
- package/dist/file-crawler.d.ts +60 -0
- package/dist/file-crawler.d.ts.map +1 -1
- package/dist/file-crawler.js +133 -29
- package/dist/file-crawler.js.map +1 -1
- package/dist/fs/file-hash.d.ts.map +1 -1
- package/dist/fs/file-hash.js +0 -1
- package/dist/fs/file-hash.js.map +1 -1
- package/dist/fs-utils.d.ts +422 -191
- package/dist/fs-utils.d.ts.map +1 -1
- package/dist/fs-utils.js +534 -204
- package/dist/fs-utils.js.map +1 -1
- package/dist/fs.d.ts +6 -2
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +10 -7
- package/dist/fs.js.map +1 -1
- package/dist/git-run.d.ts +167 -0
- package/dist/git-run.d.ts.map +1 -0
- package/dist/git-run.js +154 -0
- package/dist/git-run.js.map +1 -0
- package/dist/git-snapshot.d.ts +199 -0
- package/dist/git-snapshot.d.ts.map +1 -0
- package/dist/git-snapshot.js +250 -0
- package/dist/git-snapshot.js.map +1 -0
- package/dist/git-tracker.d.ts +166 -3
- package/dist/git-tracker.d.ts.map +1 -1
- package/dist/git-tracker.js +250 -22
- package/dist/git-tracker.js.map +1 -1
- package/dist/git-utils.d.ts +84 -1
- package/dist/git-utils.d.ts.map +1 -1
- package/dist/git-utils.js +208 -82
- package/dist/git-utils.js.map +1 -1
- package/dist/git.d.ts +13 -4
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +12 -4
- package/dist/git.js.map +1 -1
- package/dist/gitignore-checker.d.ts.map +1 -1
- package/dist/gitignore-checker.js +15 -6
- package/dist/gitignore-checker.js.map +1 -1
- package/dist/glob/glob-pattern.js +5 -5
- package/dist/glob/glob-pattern.js.map +1 -1
- package/dist/index.d.ts +32 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -50
- package/dist/index.js.map +1 -1
- package/dist/listing-refusal.d.ts +112 -0
- package/dist/listing-refusal.d.ts.map +1 -0
- package/dist/listing-refusal.js +105 -0
- package/dist/listing-refusal.js.map +1 -0
- package/dist/path-containment.d.ts +79 -0
- package/dist/path-containment.d.ts.map +1 -0
- package/dist/path-containment.js +128 -0
- package/dist/path-containment.js.map +1 -0
- package/dist/path-core.d.ts +113 -18
- package/dist/path-core.d.ts.map +1 -1
- package/dist/path-core.js +139 -23
- package/dist/path-core.js.map +1 -1
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +42 -47
- package/dist/path-utils.js.map +1 -1
- package/dist/path.d.ts +1 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +1 -1
- package/dist/path.js.map +1 -1
- package/dist/process.d.ts +7 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +7 -0
- package/dist/process.js.map +1 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +15 -9
- package/dist/project-utils.js.map +1 -1
- package/dist/project.d.ts +7 -5
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +7 -5
- package/dist/project.js.map +1 -1
- package/dist/safe-exec.d.ts +40 -11
- package/dist/safe-exec.d.ts.map +1 -1
- package/dist/safe-exec.js +111 -25
- package/dist/safe-exec.js.map +1 -1
- package/dist/skill-targets.d.ts +2 -2
- package/dist/skill-targets.js +2 -2
- package/dist/skill-test/auth-resolver.d.ts +10 -2
- package/dist/skill-test/auth-resolver.d.ts.map +1 -1
- package/dist/skill-test/auth-resolver.js +26 -11
- package/dist/skill-test/auth-resolver.js.map +1 -1
- package/dist/skill-test/index.d.ts +1 -1
- package/dist/skill-test/index.d.ts.map +1 -1
- package/dist/skill-test/index.js +1 -1
- package/dist/skill-test/index.js.map +1 -1
- package/dist/skill-test/spawn-claude.d.ts +30 -1
- package/dist/skill-test/spawn-claude.d.ts.map +1 -1
- package/dist/skill-test/spawn-claude.js +71 -5
- package/dist/skill-test/spawn-claude.js.map +1 -1
- package/dist/skill-test/transcript.d.ts +41 -2
- package/dist/skill-test/transcript.d.ts.map +1 -1
- package/dist/skill-test/transcript.js +55 -10
- package/dist/skill-test/transcript.js.map +1 -1
- package/dist/spawn-hardened.js +0 -1
- package/dist/spawn-hardened.js.map +1 -1
- package/dist/stdio-blocking.js +13 -14
- package/dist/stdio-blocking.js.map +1 -1
- package/dist/test-helpers.d.ts +135 -100
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +224 -146
- package/dist/test-helpers.js.map +1 -1
- package/dist/testing/executables.d.ts +32 -0
- package/dist/testing/executables.d.ts.map +1 -0
- package/dist/testing/executables.js +70 -0
- package/dist/testing/executables.js.map +1 -0
- package/dist/testing/hostile-tree.d.ts +79 -0
- package/dist/testing/hostile-tree.d.ts.map +1 -0
- package/dist/testing/hostile-tree.js +164 -0
- package/dist/testing/hostile-tree.js.map +1 -0
- package/dist/testing/platform-gates.d.ts +31 -0
- package/dist/testing/platform-gates.d.ts.map +1 -0
- package/dist/testing/platform-gates.js +31 -0
- package/dist/testing/platform-gates.js.map +1 -0
- package/dist/testing/temp-dir.d.ts +255 -0
- package/dist/testing/temp-dir.d.ts.map +1 -0
- package/dist/testing/temp-dir.js +363 -0
- package/dist/testing/temp-dir.js.map +1 -0
- package/dist/testing.d.ts +94 -3
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +119 -3
- package/dist/testing.js.map +1 -1
- package/dist/text-content.d.ts +226 -0
- package/dist/text-content.d.ts.map +1 -0
- package/dist/text-content.js +418 -0
- package/dist/text-content.js.map +1 -0
- package/dist/text-file.d.ts +84 -0
- package/dist/text-file.d.ts.map +1 -0
- package/dist/text-file.js +118 -0
- package/dist/text-file.js.map +1 -0
- package/dist/text.d.ts +16 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +16 -0
- package/dist/text.js.map +1 -0
- package/dist/timing-dump.d.ts +136 -0
- package/dist/timing-dump.d.ts.map +1 -0
- package/dist/timing-dump.js +229 -0
- package/dist/timing-dump.js.map +1 -0
- package/dist/windows-shell.d.ts +0 -1
- package/dist/windows-shell.d.ts.map +1 -1
- package/dist/windows-shell.js +0 -1
- package/dist/windows-shell.js.map +1 -1
- package/dist/zod-introspection.js +2 -2
- package/dist/zod-introspection.js.map +1 -1
- package/eslint/README.md +223 -49
- package/eslint/index.cjs +111 -97
- package/eslint/index.d.cts +16 -6
- package/eslint/rules/callee-name.cjs +23 -0
- package/eslint/rules/commands-import-boundary.cjs +148 -0
- package/eslint/rules/dead-import.cjs +1 -1
- package/eslint/rules/dirent-type-needs-symlink-check.cjs +282 -0
- package/eslint/rules/eslint-rule-factory.cjs +5 -2
- package/eslint/rules/exempt-path-matcher.cjs +2 -1
- package/eslint/rules/explicit-zod-strictness.cjs +120 -0
- package/eslint/rules/no-bare-dynamic-import-path.cjs +5 -1
- package/eslint/rules/no-bare-executable-spawn.cjs +70 -0
- package/eslint/rules/no-bare-symlink-in-tests.cjs +213 -0
- package/eslint/rules/no-blind-catch.cjs +171 -0
- package/eslint/rules/no-child-process-execSync.cjs +8 -0
- package/eslint/rules/no-decaying-referent.cjs +217 -0
- package/eslint/rules/no-dotdot-containment.cjs +160 -0
- package/eslint/rules/no-file-url-string-concat.cjs +4 -1
- package/eslint/rules/no-fragile-entrypoint-guard.cjs +228 -0
- package/eslint/rules/no-fs-mkdirSync.cjs +8 -0
- package/eslint/rules/no-fs-promises-cp.cjs +7 -0
- package/eslint/rules/no-fs-realpathSync.cjs +8 -0
- package/eslint/rules/no-hardcoded-path-split.cjs +29 -18
- package/eslint/rules/no-io-in-unit-tier.cjs +129 -0
- package/eslint/rules/no-literal-process-exit.cjs +230 -0
- package/eslint/rules/no-manual-path-normalize.cjs +136 -107
- package/eslint/rules/no-os-tmpdir.cjs +8 -0
- package/eslint/rules/no-path-operations-in-comparisons.cjs +7 -2
- package/eslint/rules/no-path-sep-in-strings.cjs +7 -2
- package/eslint/rules/no-path-startswith.cjs +7 -2
- package/eslint/rules/no-process-exit-in-phase.cjs +127 -0
- package/eslint/rules/no-raw-node-path.cjs +563 -0
- package/eslint/rules/no-raw-text-decode.cjs +305 -0
- package/eslint/rules/no-registry-count-pin.cjs +255 -0
- package/eslint/rules/no-self-package-import.cjs +168 -0
- package/eslint/rules/no-test-scoped-functions.cjs +10 -3
- package/eslint/rules/no-unix-shell-commands.cjs +6 -2
- package/eslint/rules/no-unsafe-root-join.cjs +23 -2
- package/eslint/rules/no-url-pathname-for-fs.cjs +5 -1
- package/eslint/rules/no-version-literal.cjs +151 -0
- package/eslint/rules/prefer-startswith-over-regex.cjs +9 -0
- package/eslint/rules/require-justified-skip.cjs +8 -2
- package/eslint/rules/safe-import.cjs +1 -1
- package/package.json +16 -16
- package/dist/link-auth/build-headers.d.ts +0 -34
- package/dist/link-auth/build-headers.d.ts.map +0 -1
- package/dist/link-auth/build-headers.js +0 -58
- package/dist/link-auth/build-headers.js.map +0 -1
- package/dist/link-auth/expand-macro.d.ts +0 -38
- package/dist/link-auth/expand-macro.d.ts.map +0 -1
- package/dist/link-auth/expand-macro.js +0 -133
- package/dist/link-auth/expand-macro.js.map +0 -1
- package/dist/link-auth/macros.yaml +0 -50
- package/dist/link-auth/resolve-token.d.ts +0 -83
- package/dist/link-auth/resolve-token.d.ts.map +0 -1
- package/dist/link-auth/resolve-token.js +0 -101
- package/dist/link-auth/resolve-token.js.map +0 -1
- package/dist/link-auth/resolve.d.ts +0 -102
- package/dist/link-auth/resolve.d.ts.map +0 -1
- package/dist/link-auth/resolve.js +0 -66
- package/dist/link-auth/resolve.js.map +0 -1
- package/dist/link-auth/rewrite.d.ts +0 -52
- package/dist/link-auth/rewrite.d.ts.map +0 -1
- package/dist/link-auth/rewrite.js +0 -102
- package/dist/link-auth/rewrite.js.map +0 -1
- package/dist/link-auth/select-provider.d.ts +0 -30
- package/dist/link-auth/select-provider.d.ts.map +0 -1
- package/dist/link-auth/select-provider.js +0 -55
- package/dist/link-auth/select-provider.js.map +0 -1
- package/dist/link-auth/template.d.ts +0 -40
- package/dist/link-auth/template.d.ts.map +0 -1
- package/dist/link-auth/template.js +0 -89
- package/dist/link-auth/template.js.map +0 -1
- package/dist/link-auth/transforms.d.ts +0 -46
- package/dist/link-auth/transforms.d.ts.map +0 -1
- package/dist/link-auth/transforms.js +0 -52
- package/dist/link-auth/transforms.js.map +0 -1
- package/dist/template-entry.d.ts +0 -10
- package/dist/template-entry.d.ts.map +0 -1
- package/dist/template-entry.js +0 -10
- package/dist/template-entry.js.map +0 -1
- package/dist/template.d.ts +0 -7
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -18
- package/dist/template.js.map +0 -1
- package/eslint/rules/no-path-join.cjs +0 -14
- package/eslint/rules/no-path-relative.cjs +0 -14
- package/eslint/rules/no-path-resolve.cjs +0 -14
- package/eslint/rules/path-function-rule-factory.cjs +0 -496
package/dist/safe-exec.js
CHANGED
|
@@ -1,21 +1,55 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import which from 'which';
|
|
3
|
+
import { isPathAbsentError } from './errors/errno.js';
|
|
4
|
+
import { VatError } from './errors/vat-error.js';
|
|
5
|
+
import { runGit } from './git-run.js';
|
|
3
6
|
import { buildWindowsShellLine, resolveShellCommandToken, shouldUseShell, } from './windows-shell.js';
|
|
4
7
|
/**
|
|
5
8
|
* Error thrown when command execution fails
|
|
6
9
|
*/
|
|
7
|
-
export class CommandExecutionError extends
|
|
10
|
+
export class CommandExecutionError extends VatError {
|
|
8
11
|
status;
|
|
9
12
|
stdout;
|
|
10
13
|
stderr;
|
|
11
14
|
constructor(message, status, stdout, stderr) {
|
|
12
|
-
super(message);
|
|
13
|
-
this.name = 'CommandExecutionError';
|
|
15
|
+
super('COMMAND_EXECUTION', message);
|
|
14
16
|
this.status = status;
|
|
15
17
|
this.stdout = stdout;
|
|
16
18
|
this.stderr = stderr;
|
|
17
19
|
}
|
|
18
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Refuse `git` here, and say where it belongs.
|
|
23
|
+
*
|
|
24
|
+
* "Safe" in this module's name has always meant *shell-injection*-safe: PATH
|
|
25
|
+
* resolved once, no shell interpreter. It says nothing about the environment,
|
|
26
|
+
* and `git` is the one command for which the environment silently decides which
|
|
27
|
+
* repository the answer describes — `GIT_DIR` and `GIT_INDEX_FILE`, exported
|
|
28
|
+
* into every child of a `git` hook, override the `cwd` passed here. The failure
|
|
29
|
+
* is a well-formed answer about the wrong repository at exit 0, so no caller
|
|
30
|
+
* discovers it by testing.
|
|
31
|
+
*
|
|
32
|
+
* A refusal rather than a silent scrub, because the two meanings are not
|
|
33
|
+
* interchangeable: only the caller knows whether it means the path it passed or
|
|
34
|
+
* the repository it stands in, and guessing on their behalf would be wrong half
|
|
35
|
+
* the time in a way nothing reports. {@link runGit} makes them say.
|
|
36
|
+
*
|
|
37
|
+
* @param command - The command about to be spawned
|
|
38
|
+
* @throws {Error} When the command is git
|
|
39
|
+
*/
|
|
40
|
+
function isGitCommand(command) {
|
|
41
|
+
const name = command.toLowerCase().replace(/\.(exe|cmd)$/, '');
|
|
42
|
+
return name === 'git' || name.endsWith('/git') || name.endsWith(String.raw `\git`);
|
|
43
|
+
}
|
|
44
|
+
function refuseGit(command) {
|
|
45
|
+
if (!isGitCommand(command))
|
|
46
|
+
return;
|
|
47
|
+
throw new Error('Use runGit() from @vibe-agent-toolkit/utils/git to run git, not safeExecSync/safeExecResult.\n' +
|
|
48
|
+
" runGit(args, { cwd }) — about a path you were handed (scrubs the inherited\n" +
|
|
49
|
+
' GIT_DIR/GIT_INDEX_FILE that would override cwd)\n' +
|
|
50
|
+
' runGit(args, { ambient: true }) — about the repository this process stands in\n' +
|
|
51
|
+
'Inside a git hook the difference decides which repository you read — or write.');
|
|
52
|
+
}
|
|
19
53
|
/**
|
|
20
54
|
* Resolve command, build spawn options, and execute via spawnSync.
|
|
21
55
|
*
|
|
@@ -31,6 +65,8 @@ export class CommandExecutionError extends Error {
|
|
|
31
65
|
* value. Passing the raw command here used to break silently on `C:\Program Files\…`.
|
|
32
66
|
*/
|
|
33
67
|
function resolveAndSpawn(command, args, options) {
|
|
68
|
+
if (options.allowGit !== true)
|
|
69
|
+
refuseGit(command);
|
|
34
70
|
const commandPath = which.sync(command);
|
|
35
71
|
const useShell = shouldUseShell(commandPath);
|
|
36
72
|
const spawnOptions = {
|
|
@@ -46,7 +82,6 @@ function resolveAndSpawn(command, args, options) {
|
|
|
46
82
|
return spawnSync(commandPath, args, spawnOptions);
|
|
47
83
|
}
|
|
48
84
|
const shellLine = buildWindowsShellLine(resolveShellCommandToken(command, commandPath), args);
|
|
49
|
-
// eslint-disable-next-line sonarjs/os-command -- Windows DEP0190 workaround: command resolved via which.sync(); args are per-arg shell-quoted via windowsShellQuote()
|
|
50
85
|
return spawnSync(shellLine, { ...spawnOptions, shell: true });
|
|
51
86
|
}
|
|
52
87
|
/**
|
|
@@ -56,7 +91,11 @@ function resolveAndSpawn(command, args, options) {
|
|
|
56
91
|
* - Resolves PATH once using pure Node.js (which package)
|
|
57
92
|
* - Executes with absolute path and shell: false
|
|
58
93
|
* - No shell interpreter = no command injection risk
|
|
59
|
-
* - Supports custom env vars
|
|
94
|
+
* - Supports custom env vars
|
|
95
|
+
*
|
|
96
|
+
* ⛔ **Not for `git`** — it throws. "Safe" here means shell-injection-safe and
|
|
97
|
+
* says nothing about `GIT_DIR`/`GIT_INDEX_FILE`, which silently decide which
|
|
98
|
+
* repository a git command answers about. Use `runGit()`.
|
|
60
99
|
*
|
|
61
100
|
* @param command - Command name (e.g., 'git', 'gitleaks', 'node')
|
|
62
101
|
* @param args - Array of arguments
|
|
@@ -69,14 +108,11 @@ function resolveAndSpawn(command, args, options) {
|
|
|
69
108
|
* safeExecSync('gitleaks', ['--version'], { stdio: 'ignore' });
|
|
70
109
|
*
|
|
71
110
|
* @example
|
|
72
|
-
* // Git with custom env
|
|
73
|
-
* safeExecSync('git', ['add', '--all'], {
|
|
74
|
-
* env: { ...process.env, GIT_INDEX_FILE: tempFile }
|
|
75
|
-
* });
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
111
|
* // Get output as string
|
|
79
112
|
* const version = safeExecSync('node', ['--version'], { encoding: 'utf8' });
|
|
113
|
+
*
|
|
114
|
+
* @throws {Error} When `command` is git — use `runGit()` instead, which owns the
|
|
115
|
+
* inherited-environment problem this helper cannot see
|
|
80
116
|
*/
|
|
81
117
|
export function safeExecSync(command, args = [], options = {}) {
|
|
82
118
|
const result = resolveAndSpawn(command, args, options);
|
|
@@ -102,14 +138,22 @@ export function safeExecSync(command, args = [], options = {}) {
|
|
|
102
138
|
* @returns Detailed execution result
|
|
103
139
|
*
|
|
104
140
|
* @example
|
|
105
|
-
* const result = safeExecResult('
|
|
141
|
+
* const result = safeExecResult('gh', ['pr', 'list']);
|
|
106
142
|
* if (result.success) {
|
|
107
143
|
* console.log(result.stdout.toString());
|
|
108
144
|
* } else {
|
|
109
145
|
* console.error(`Failed: ${result.stderr.toString()}`);
|
|
110
146
|
* }
|
|
147
|
+
*
|
|
148
|
+
* @throws {Error} When `command` is git — use `runGit()` instead
|
|
111
149
|
*/
|
|
112
150
|
export function safeExecResult(command, args = [], options = {}) {
|
|
151
|
+
// Deliberately OUTSIDE the try: this is a programming error to fix at the call
|
|
152
|
+
// site, not a command that failed. Folded into the result it would read as
|
|
153
|
+
// "git is unavailable" and callers that treat a failure as information — most
|
|
154
|
+
// of them — would carry on silently.
|
|
155
|
+
if (options.allowGit !== true)
|
|
156
|
+
refuseGit(command);
|
|
113
157
|
try {
|
|
114
158
|
const result = resolveAndSpawn(command, args, options);
|
|
115
159
|
const status = result.status ?? -1;
|
|
@@ -148,12 +192,61 @@ export function safeExecResult(command, args = [], options = {}) {
|
|
|
148
192
|
* }
|
|
149
193
|
*/
|
|
150
194
|
export function isToolAvailable(toolName) {
|
|
195
|
+
if (isGitCommand(toolName))
|
|
196
|
+
return probeVersion(toolName, '--version') !== null;
|
|
151
197
|
try {
|
|
152
198
|
safeExecSync(toolName, ['--version'], { stdio: 'ignore' });
|
|
153
199
|
return true;
|
|
154
200
|
}
|
|
155
|
-
catch {
|
|
156
|
-
|
|
201
|
+
catch (error) {
|
|
202
|
+
if (isToolUnusable(error))
|
|
203
|
+
return false;
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Whether a failed version probe means the tool is not usable — the two
|
|
209
|
+
* failures every caller reads as "not installed": the binary is not on PATH
|
|
210
|
+
* (`which` and `spawn` both say `ENOENT`), or it ran and exited non-zero (a
|
|
211
|
+
* binary that does not answer `--version`). The OS refusing to execute a file
|
|
212
|
+
* `which` had just found (`EACCES`), a spawn that could not start for any other
|
|
213
|
+
* reason, or a bug in this module are none of those; `vat doctor` reporting
|
|
214
|
+
* "not installed" for an `EACCES` sends the adopter to reinstall a tool that is
|
|
215
|
+
* there, so those stay loud.
|
|
216
|
+
*/
|
|
217
|
+
function isToolUnusable(error) {
|
|
218
|
+
return isPathAbsentError(error) || error instanceof CommandExecutionError;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Ask a binary its version, routing `git` around {@link refuseGit}.
|
|
222
|
+
*
|
|
223
|
+
* A version probe names the *executable*, never a repository, so the refusal
|
|
224
|
+
* would be a false negative here — and a silent one, because both callers below
|
|
225
|
+
* report "not installed" for a null. `vat doctor` did exactly that: correct
|
|
226
|
+
* `git`, reported missing. {@link runGit} is the sanctioned path regardless.
|
|
227
|
+
*
|
|
228
|
+
* @param toolName - Binary to ask
|
|
229
|
+
* @param versionArg - Argument that makes it print its version
|
|
230
|
+
* @returns Trimmed version string, or null when the tool is genuinely absent
|
|
231
|
+
*/
|
|
232
|
+
function probeVersion(toolName, versionArg) {
|
|
233
|
+
if (isGitCommand(toolName)) {
|
|
234
|
+
// The scrub is a no-op for a command that reads nothing; keeping the safe
|
|
235
|
+
// default means no call site here has to reason about it.
|
|
236
|
+
const result = runGit([versionArg]);
|
|
237
|
+
return result.ok ? result.stdout : null;
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
const version = safeExecSync(toolName, [versionArg], {
|
|
241
|
+
encoding: 'utf8',
|
|
242
|
+
stdio: 'pipe',
|
|
243
|
+
});
|
|
244
|
+
return version.trim();
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
if (isToolUnusable(error))
|
|
248
|
+
return null;
|
|
249
|
+
throw error;
|
|
157
250
|
}
|
|
158
251
|
}
|
|
159
252
|
/**
|
|
@@ -172,16 +265,7 @@ export function isToolAvailable(toolName) {
|
|
|
172
265
|
* console.log(gitVersion); // "git version 2.39.2"
|
|
173
266
|
*/
|
|
174
267
|
export function getToolVersion(toolName, versionArg = '--version') {
|
|
175
|
-
|
|
176
|
-
const version = safeExecSync(toolName, [versionArg], {
|
|
177
|
-
encoding: 'utf8',
|
|
178
|
-
stdio: 'pipe',
|
|
179
|
-
});
|
|
180
|
-
return version.trim();
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
return null;
|
|
184
|
-
}
|
|
268
|
+
return probeVersion(toolName, versionArg);
|
|
185
269
|
}
|
|
186
270
|
/**
|
|
187
271
|
* Shell syntax character sets for hyper-efficient single-pass detection
|
|
@@ -301,9 +385,11 @@ export function hasShellSyntax(commandString) {
|
|
|
301
385
|
* @example
|
|
302
386
|
* ```typescript
|
|
303
387
|
* // ✅ Simple commands (these work)
|
|
304
|
-
* safeExecFromString('git status');
|
|
305
388
|
* safeExecFromString('pnpm test --watch');
|
|
306
389
|
* safeExecFromString('gh pr view 123');
|
|
390
|
+
*
|
|
391
|
+
* // ❌ git is refused here too — the string form is not a back door around
|
|
392
|
+
* // the chokepoint. Use runGit(['status'], { cwd }).
|
|
307
393
|
* ```
|
|
308
394
|
*
|
|
309
395
|
* @example
|
package/dist/safe-exec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-exec.js","sourceRoot":"","sources":["../src/safe-exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAyB,MAAM,oBAAoB,CAAC;AAEtE,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,cAAc,GACf,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"safe-exec.js","sourceRoot":"","sources":["../src/safe-exec.ts"],"names":[],"mappings":"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"}
|
package/dist/skill-targets.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ interface SkillTargetPaths {
|
|
|
21
21
|
*
|
|
22
22
|
* @vendor-claim reviewed=2026-07-30 verify=Each of the seven platforms' own published docs for where it reads skills from (claude, codex, copilot, gemini, cursor, windsurf, agents)
|
|
23
23
|
*
|
|
24
|
-
*
|
|
24
|
+
* At the last review (the `reviewed=` date above): all fourteen paths
|
|
25
25
|
* re-read from the first-party sources listed below, all fourteen unchanged.
|
|
26
26
|
* Update this table when platforms change theirs — and re-read those sources when
|
|
27
27
|
* you do, because nothing here is verified at build or test time. A stale entry
|
|
@@ -40,7 +40,7 @@ interface SkillTargetPaths {
|
|
|
40
40
|
* a change-detector masquerading as verification, and friction against exactly the
|
|
41
41
|
* correction the clock above exists to produce.)
|
|
42
42
|
*
|
|
43
|
-
* Sources, one per target, re-read
|
|
43
|
+
* Sources, one per target, re-read at that review:
|
|
44
44
|
* - claude https://code.claude.com/docs/en/skills
|
|
45
45
|
* (docs.claude.com/en/docs/claude-code/skills now 301s here)
|
|
46
46
|
* - codex https://developers.openai.com/codex/skills/
|
package/dist/skill-targets.js
CHANGED
|
@@ -40,7 +40,7 @@ const AGENTS_SKILLS_PATH = '.agents/skills';
|
|
|
40
40
|
*
|
|
41
41
|
* @vendor-claim reviewed=2026-07-30 verify=Each of the seven platforms' own published docs for where it reads skills from (claude, codex, copilot, gemini, cursor, windsurf, agents)
|
|
42
42
|
*
|
|
43
|
-
*
|
|
43
|
+
* At the last review (the `reviewed=` date above): all fourteen paths
|
|
44
44
|
* re-read from the first-party sources listed below, all fourteen unchanged.
|
|
45
45
|
* Update this table when platforms change theirs — and re-read those sources when
|
|
46
46
|
* you do, because nothing here is verified at build or test time. A stale entry
|
|
@@ -59,7 +59,7 @@ const AGENTS_SKILLS_PATH = '.agents/skills';
|
|
|
59
59
|
* a change-detector masquerading as verification, and friction against exactly the
|
|
60
60
|
* correction the clock above exists to produce.)
|
|
61
61
|
*
|
|
62
|
-
* Sources, one per target, re-read
|
|
62
|
+
* Sources, one per target, re-read at that review:
|
|
63
63
|
* - claude https://code.claude.com/docs/en/skills
|
|
64
64
|
* (docs.claude.com/en/docs/claude-code/skills now 301s here)
|
|
65
65
|
* - codex https://developers.openai.com/codex/skills/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VatError } from '../errors/vat-error.js';
|
|
1
2
|
export type AuthMode = 'inherit' | 'subscription' | 'api-key' | 'auto';
|
|
2
3
|
export type AuthMechanism = 'subscription' | 'api-key';
|
|
3
4
|
export interface AuthStatusResult {
|
|
@@ -21,8 +22,8 @@ export interface ResolvedAuth {
|
|
|
21
22
|
apiKeySource?: string;
|
|
22
23
|
}
|
|
23
24
|
/** A preflight auth failure — maps to exit code 2. */
|
|
24
|
-
export declare class AuthPreflightError extends
|
|
25
|
-
readonly
|
|
25
|
+
export declare class AuthPreflightError extends VatError {
|
|
26
|
+
readonly reason: "preflight";
|
|
26
27
|
constructor(message: string);
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
@@ -30,5 +31,12 @@ export declare class AuthPreflightError extends Error {
|
|
|
30
31
|
* given env: returns in ~150ms, exit 0, spends NO tokens. NOT a `-p` call.
|
|
31
32
|
*/
|
|
32
33
|
export declare function probeAuthStatus(env: NodeJS.ProcessEnv): AuthStatusResult | null;
|
|
34
|
+
/**
|
|
35
|
+
* Read what `claude auth status --json` printed. `null` means "not the JSON
|
|
36
|
+
* object the flag promises" — an older CLI answering in prose, or a bare
|
|
37
|
+
* literal — and only that: a failure inside this reader is a bug, not "not
|
|
38
|
+
* logged in", and `resolveAuth` would otherwise turn it into a preflight exit.
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseAuthStatus(stdout: string): AuthStatusResult | null;
|
|
33
41
|
export declare function resolveAuth(opts: ResolveAuthOptions): ResolvedAuth;
|
|
34
42
|
//# sourceMappingURL=auth-resolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-resolver.d.ts","sourceRoot":"","sources":["../../src/skill-test/auth-resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-resolver.d.ts","sourceRoot":"","sources":["../../src/skill-test/auth-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAKlD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,GAAG,MAAM,CAAC;AACvE,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,gBAAgB,GAAG,IAAI,CAAC;AAElF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC;IAC7B,uEAAuE;IACvE,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,kBAAkB,EAAE,aAAa,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,sDAAsD;AACtD,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,QAAQ,CAAC,MAAM,EAAG,WAAW,CAAU;gBAC3B,OAAO,EAAE,MAAM;CAG5B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,gBAAgB,GAAG,IAAI,CAI/E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAevE;AAgBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,YAAY,CA+BlE"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { VatError } from '../errors/vat-error.js';
|
|
1
2
|
import { safeExecResult } from '../safe-exec.js';
|
|
2
3
|
import { buildForwardedEnv } from './env-scrub.js';
|
|
3
4
|
/** A preflight auth failure — maps to exit code 2. */
|
|
4
|
-
export class AuthPreflightError extends
|
|
5
|
-
|
|
5
|
+
export class AuthPreflightError extends VatError {
|
|
6
|
+
reason = 'preflight';
|
|
6
7
|
constructor(message) {
|
|
7
|
-
super(message);
|
|
8
|
-
this.name = 'AuthPreflightError';
|
|
8
|
+
super('AUTH_PREFLIGHT', message);
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
@@ -16,17 +16,32 @@ export function probeAuthStatus(env) {
|
|
|
16
16
|
const res = safeExecResult('claude', ['auth', 'status', '--json'], { env, encoding: 'utf8', stdio: 'pipe' });
|
|
17
17
|
if (!res.success)
|
|
18
18
|
return null;
|
|
19
|
+
return parseAuthStatus(res.stdout.toString());
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read what `claude auth status --json` printed. `null` means "not the JSON
|
|
23
|
+
* object the flag promises" — an older CLI answering in prose, or a bare
|
|
24
|
+
* literal — and only that: a failure inside this reader is a bug, not "not
|
|
25
|
+
* logged in", and `resolveAuth` would otherwise turn it into a preflight exit.
|
|
26
|
+
*/
|
|
27
|
+
export function parseAuthStatus(stdout) {
|
|
28
|
+
let parsed;
|
|
19
29
|
try {
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
loggedIn: parsed['loggedIn'] === true,
|
|
23
|
-
...(typeof parsed['authMethod'] === 'string' ? { authMethod: parsed['authMethod'] } : {}),
|
|
24
|
-
...(typeof parsed['apiKeySource'] === 'string' ? { apiKeySource: parsed['apiKeySource'] } : {}),
|
|
25
|
-
};
|
|
30
|
+
parsed = JSON.parse(stdout);
|
|
26
31
|
}
|
|
27
|
-
catch {
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (!(error instanceof SyntaxError))
|
|
34
|
+
throw error;
|
|
28
35
|
return null;
|
|
29
36
|
}
|
|
37
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
38
|
+
return null;
|
|
39
|
+
const record = parsed;
|
|
40
|
+
return {
|
|
41
|
+
loggedIn: record['loggedIn'] === true,
|
|
42
|
+
...(typeof record['authMethod'] === 'string' ? { authMethod: record['authMethod'] } : {}),
|
|
43
|
+
...(typeof record['apiKeySource'] === 'string' ? { apiKeySource: record['apiKeySource'] } : {}),
|
|
44
|
+
};
|
|
30
45
|
}
|
|
31
46
|
function hasInferenceKey(env) {
|
|
32
47
|
return Boolean(env['ANTHROPIC_API_KEY'] ?? env['ANTHROPIC_AUTH_TOKEN']);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-resolver.js","sourceRoot":"","sources":["../../src/skill-test/auth-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AA6BnD,sDAAsD;AACtD,MAAM,OAAO,kBAAmB,SAAQ,
|
|
1
|
+
{"version":3,"file":"auth-resolver.js","sourceRoot":"","sources":["../../src/skill-test/auth-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { applyDeclaredEnv, buildForwardedEnv, formatForwardedEnvLine, protectedEnvNames, type DeclaredEnvInput, type DeclaredEnvResult, type ForwardEnvOptions, } from './env-scrub.js';
|
|
1
|
+
export { applyDeclaredEnv, buildForwardedEnv, formatForwardedEnvLine, isProtectedName, protectedEnvNames, type DeclaredEnvInput, type DeclaredEnvResult, type ForwardEnvOptions, } from './env-scrub.js';
|
|
2
2
|
export { AuthPreflightError, probeAuthStatus, resolveAuth, type AuthMechanism, type AuthMode, type AuthStatusProbe, type AuthStatusResult, type ResolveAuthOptions, type ResolvedAuth, } from './auth-resolver.js';
|
|
3
3
|
export { assembleClaudeArgs, killAllActiveClaudeChildren, spawnHeadlessClaude, type ClaudeSpawnArgs, type SpawnHeadlessOptions, type SpawnResult, } from './spawn-claude.js';
|
|
4
4
|
export { detectInvocationFromTranscript, parseStreamJsonTranscript, type ParsedTranscript, } from './transcript.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/skill-test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/skill-test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,KAAK,gBAAgB,GACtB,MAAM,iBAAiB,CAAC"}
|
package/dist/skill-test/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { applyDeclaredEnv, buildForwardedEnv, formatForwardedEnvLine, protectedEnvNames, } from './env-scrub.js';
|
|
1
|
+
export { applyDeclaredEnv, buildForwardedEnv, formatForwardedEnvLine, isProtectedName, protectedEnvNames, } from './env-scrub.js';
|
|
2
2
|
export { AuthPreflightError, probeAuthStatus, resolveAuth, } from './auth-resolver.js';
|
|
3
3
|
export { assembleClaudeArgs, killAllActiveClaudeChildren, spawnHeadlessClaude, } from './spawn-claude.js';
|
|
4
4
|
export { detectInvocationFromTranscript, parseStreamJsonTranscript, } from './transcript.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/skill-test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,GAIlB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,WAAW,GAOZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,mBAAmB,GAIpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,GAE1B,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/skill-test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,GAIlB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,WAAW,GAOZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,2BAA2B,EAC3B,mBAAmB,GAIpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,GAE1B,MAAM,iBAAiB,CAAC"}
|
|
@@ -11,6 +11,34 @@ export interface ClaudeSpawnArgs {
|
|
|
11
11
|
* `--prompt-file` flag — the prompt is fed to the child's stdin by
|
|
12
12
|
* {@link spawnHeadlessClaude} instead. `--setting-sources ""` suppresses
|
|
13
13
|
* user/project settings; built-in skills remain (§5).
|
|
14
|
+
*
|
|
15
|
+
* `--no-session-persistence` is load-bearing for the harness's integrity model,
|
|
16
|
+
* not a tidiness flag. Claude Code otherwise writes every headless session to
|
|
17
|
+
* `$CLAUDE_CONFIG_DIR/projects/<cwd-slug>/<uuid>.jsonl` — plaintext, mode 0600,
|
|
18
|
+
* retained indefinitely — and `CLAUDE_CONFIG_DIR` and `HOME` are both forwarded
|
|
19
|
+
* to the child (they have to be: that is where auth lives). That file falsifies
|
|
20
|
+
* three of the harness's stated guarantees at once:
|
|
21
|
+
*
|
|
22
|
+
* - the per-run grading NONCE, which {@link SpawnHeadlessOptions.prompt} keeps
|
|
23
|
+
* off disk precisely so untrusted skill code cannot read it back and forge a
|
|
24
|
+
* passing grading.json — the grader's own session file contains it verbatim;
|
|
25
|
+
* - the eval ANSWER KEY (`expected_output`), which `eval-suite-isolation.ts`
|
|
26
|
+
* exists solely to keep off the executor's filesystem;
|
|
27
|
+
* - the `--baseline` control, which could read the treatment arm's entire
|
|
28
|
+
* session by listing `projects/` for its slug.
|
|
29
|
+
*
|
|
30
|
+
* And it leaks ACROSS runs: a transcript from one run is still readable weeks
|
|
31
|
+
* later by the next run's executor, so no per-run randomness helps.
|
|
32
|
+
*
|
|
33
|
+
* VERIFIED on macOS with claude 2.x, both directions: without the flag one
|
|
34
|
+
* `.jsonl` lands under the config dir per spawn; with it, zero, and the session
|
|
35
|
+
* still authenticates. It is `--print`-only, which is exactly this spawn's shape.
|
|
36
|
+
*
|
|
37
|
+
* 🪤 An ephemeral per-spawn `CLAUDE_CONFIG_DIR` — the obvious alternative — does
|
|
38
|
+
* NOT work: on macOS the subscription credential is a Keychain item whose service
|
|
39
|
+
* name is derived from the config-dir PATH, so a fresh dir reports "Not logged
|
|
40
|
+
* in" even with the real `.claude.json` copied in (verified). Materializing the
|
|
41
|
+
* token into a dir the executor can read would be strictly worse than the leak.
|
|
14
42
|
*/
|
|
15
43
|
export declare function assembleClaudeArgs(opts: ClaudeSpawnArgs): string[];
|
|
16
44
|
export interface SpawnResult {
|
|
@@ -25,7 +53,8 @@ export interface SpawnResult {
|
|
|
25
53
|
* orphaned MCP subprocesses that a direct `child.kill()` would leave running.
|
|
26
54
|
*
|
|
27
55
|
* - Guards an undefined pid (child never spawned — nothing to kill).
|
|
28
|
-
* -
|
|
56
|
+
* - Absorbs the throw that means the group is already gone (see
|
|
57
|
+
* {@link isProcessGroupGone}); anything else stays loud.
|
|
29
58
|
* - Windows has no POSIX process groups: fall back to `taskkill /T /F`, which
|
|
30
59
|
* terminates the whole process tree.
|
|
31
60
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-claude.d.ts","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED
|
|
1
|
+
{"version":3,"file":"spawn-claude.d.ts","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,EAAE,CAoBlE;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GAAG,IAAI,CAkBzE;AAgCD;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAKlD;AAED,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IAC3D;;;;;;;;;OASG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CA8G1F"}
|
|
@@ -7,6 +7,34 @@ import { spawnHardened } from '../spawn-hardened.js';
|
|
|
7
7
|
* `--prompt-file` flag — the prompt is fed to the child's stdin by
|
|
8
8
|
* {@link spawnHeadlessClaude} instead. `--setting-sources ""` suppresses
|
|
9
9
|
* user/project settings; built-in skills remain (§5).
|
|
10
|
+
*
|
|
11
|
+
* `--no-session-persistence` is load-bearing for the harness's integrity model,
|
|
12
|
+
* not a tidiness flag. Claude Code otherwise writes every headless session to
|
|
13
|
+
* `$CLAUDE_CONFIG_DIR/projects/<cwd-slug>/<uuid>.jsonl` — plaintext, mode 0600,
|
|
14
|
+
* retained indefinitely — and `CLAUDE_CONFIG_DIR` and `HOME` are both forwarded
|
|
15
|
+
* to the child (they have to be: that is where auth lives). That file falsifies
|
|
16
|
+
* three of the harness's stated guarantees at once:
|
|
17
|
+
*
|
|
18
|
+
* - the per-run grading NONCE, which {@link SpawnHeadlessOptions.prompt} keeps
|
|
19
|
+
* off disk precisely so untrusted skill code cannot read it back and forge a
|
|
20
|
+
* passing grading.json — the grader's own session file contains it verbatim;
|
|
21
|
+
* - the eval ANSWER KEY (`expected_output`), which `eval-suite-isolation.ts`
|
|
22
|
+
* exists solely to keep off the executor's filesystem;
|
|
23
|
+
* - the `--baseline` control, which could read the treatment arm's entire
|
|
24
|
+
* session by listing `projects/` for its slug.
|
|
25
|
+
*
|
|
26
|
+
* And it leaks ACROSS runs: a transcript from one run is still readable weeks
|
|
27
|
+
* later by the next run's executor, so no per-run randomness helps.
|
|
28
|
+
*
|
|
29
|
+
* VERIFIED on macOS with claude 2.x, both directions: without the flag one
|
|
30
|
+
* `.jsonl` lands under the config dir per spawn; with it, zero, and the session
|
|
31
|
+
* still authenticates. It is `--print`-only, which is exactly this spawn's shape.
|
|
32
|
+
*
|
|
33
|
+
* 🪤 An ephemeral per-spawn `CLAUDE_CONFIG_DIR` — the obvious alternative — does
|
|
34
|
+
* NOT work: on macOS the subscription credential is a Keychain item whose service
|
|
35
|
+
* name is derived from the config-dir PATH, so a fresh dir reports "Not logged
|
|
36
|
+
* in" even with the real `.claude.json` copied in (verified). Materializing the
|
|
37
|
+
* token into a dir the executor can read would be strictly worse than the leak.
|
|
10
38
|
*/
|
|
11
39
|
export function assembleClaudeArgs(opts) {
|
|
12
40
|
const args = [
|
|
@@ -17,6 +45,7 @@ export function assembleClaudeArgs(opts) {
|
|
|
17
45
|
'--output-format', 'stream-json',
|
|
18
46
|
'--verbose',
|
|
19
47
|
'--setting-sources', '',
|
|
48
|
+
'--no-session-persistence',
|
|
20
49
|
'--permission-mode', 'bypassPermissions',
|
|
21
50
|
'--add-dir', opts.sandboxDir,
|
|
22
51
|
];
|
|
@@ -38,7 +67,8 @@ export function assembleClaudeArgs(opts) {
|
|
|
38
67
|
* orphaned MCP subprocesses that a direct `child.kill()` would leave running.
|
|
39
68
|
*
|
|
40
69
|
* - Guards an undefined pid (child never spawned — nothing to kill).
|
|
41
|
-
* -
|
|
70
|
+
* - Absorbs the throw that means the group is already gone (see
|
|
71
|
+
* {@link isProcessGroupGone}); anything else stays loud.
|
|
42
72
|
* - Windows has no POSIX process groups: fall back to `taskkill /T /F`, which
|
|
43
73
|
* terminates the whole process tree.
|
|
44
74
|
*/
|
|
@@ -56,10 +86,26 @@ export function killProcessTree(child) {
|
|
|
56
86
|
process.kill(-pid, 'SIGKILL');
|
|
57
87
|
}
|
|
58
88
|
}
|
|
59
|
-
catch {
|
|
60
|
-
//
|
|
89
|
+
catch (error) {
|
|
90
|
+
// A reaper that says nothing about a tree it did not reap is the
|
|
91
|
+
// orphan-billing defect the registry exists to prevent, so only "already
|
|
92
|
+
// gone" is absorbed. A bug here — the one other thing `process.kill` on our
|
|
93
|
+
// own child's group can throw — stays loud.
|
|
94
|
+
if (!isProcessGroupGone(error))
|
|
95
|
+
throw error;
|
|
61
96
|
}
|
|
62
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Whether `process.kill(-pid)` failed because the group is already gone.
|
|
100
|
+
*
|
|
101
|
+
* `ESRCH`: no such process group. `EPERM`: the group id has been recycled to a
|
|
102
|
+
* process this uid may not signal — which, for a group WE created and would
|
|
103
|
+
* have permission over, also means every process of ours in it has exited.
|
|
104
|
+
* Both are the outcome the kill was after.
|
|
105
|
+
*/
|
|
106
|
+
function isProcessGroupGone(error) {
|
|
107
|
+
return error instanceof Error && 'code' in error && (error.code === 'ESRCH' || error.code === 'EPERM');
|
|
108
|
+
}
|
|
63
109
|
/**
|
|
64
110
|
* Registry of currently in-flight `claude` child processes, keyed by the
|
|
65
111
|
* `ChildProcess` object itself. A child is registered immediately after
|
|
@@ -119,6 +165,24 @@ export async function spawnHeadlessClaude(opts) {
|
|
|
119
165
|
// Register immediately after spawn so the child is reapable by
|
|
120
166
|
// killAllActiveClaudeChildren() for the entire time it can be in flight.
|
|
121
167
|
activeClaudeChildren.add(child);
|
|
168
|
+
// Decode BOTH output streams as UTF-8 at the stream, not per chunk.
|
|
169
|
+
//
|
|
170
|
+
// A pipe hands the reader whatever bytes have arrived (typically capped at
|
|
171
|
+
// 64 KiB) with no regard for character boundaries, so `chunk.toString()` on
|
|
172
|
+
// each Buffer independently mangles any multi-byte character that straddles a
|
|
173
|
+
// boundary into replacement characters on both sides of the split. That
|
|
174
|
+
// corruption lands INSIDE a stream-json line, and it costs the harness
|
|
175
|
+
// evidence two ways: the line can stop parsing, silently deleting the tool
|
|
176
|
+
// call in it (see ParsedTranscript.malformedLineCount), or — worse, because
|
|
177
|
+
// nothing reports it — the line still parses with a mangled VALUE, so a
|
|
178
|
+
// contamination needle no longer matches the path the arm actually reached.
|
|
179
|
+
//
|
|
180
|
+
// `setEncoding` runs the stream's own StringDecoder, which holds a partial
|
|
181
|
+
// sequence back until the rest arrives AND flushes whatever is left at EOF, so
|
|
182
|
+
// a genuinely truncated tail still surfaces (as U+FFFD) rather than vanishing.
|
|
183
|
+
// Set before any listener is attached, so no chunk is ever seen undecoded.
|
|
184
|
+
child.stdout.setEncoding('utf8');
|
|
185
|
+
child.stderr.setEncoding('utf8');
|
|
122
186
|
let timedOut = false;
|
|
123
187
|
let stalled = false;
|
|
124
188
|
// Feed the prompt to the child via stdin (claude 2.x has no --prompt-file).
|
|
@@ -170,8 +234,10 @@ export async function spawnHeadlessClaude(opts) {
|
|
|
170
234
|
// child's real exit status).
|
|
171
235
|
child.stdin.on('error', () => { });
|
|
172
236
|
promptStream.pipe(child.stdin);
|
|
173
|
-
|
|
174
|
-
|
|
237
|
+
// `d` is already a string: setEncoding('utf8') above put the stream's
|
|
238
|
+
// StringDecoder in front of these listeners. Never call `.toString()` here.
|
|
239
|
+
child.stdout.on('data', (d) => { opts.onStdout?.(d); });
|
|
240
|
+
child.stderr.on('data', (d) => { opts.onStderr?.(d); });
|
|
175
241
|
// Reap on 'error' too: clearAllTimers() unregisters the child, so if 'error'
|
|
176
242
|
// ever fires while the process is actually alive we would otherwise drop it
|
|
177
243
|
// from the reap set without killing it (the exact orphan this file guards
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-claude.js","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA+C,MAAM,oBAAoB,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAiB,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAUrD
|
|
1
|
+
{"version":3,"file":"spawn-claude.js","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"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"}
|