@vibe-agent-toolkit/utils 0.1.39-rc.6 → 0.1.39-rc.8
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/dist/git-url.d.ts +43 -0
- package/dist/git-url.d.ts.map +1 -0
- package/dist/git-url.js +135 -0
- package/dist/git-url.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/skill-test/auth-resolver.d.ts +34 -0
- package/dist/skill-test/auth-resolver.d.ts.map +1 -0
- package/dist/skill-test/auth-resolver.js +68 -0
- package/dist/skill-test/auth-resolver.js.map +1 -0
- package/dist/skill-test/env-scrub.d.ts +14 -0
- package/dist/skill-test/env-scrub.d.ts.map +1 -0
- package/dist/skill-test/env-scrub.js +35 -0
- package/dist/skill-test/env-scrub.js.map +1 -0
- package/dist/skill-test/index.d.ts +4 -0
- package/dist/skill-test/index.d.ts.map +1 -0
- package/dist/skill-test/index.js +4 -0
- package/dist/skill-test/index.js.map +1 -0
- package/dist/skill-test/spawn-claude.d.ts +45 -0
- package/dist/skill-test/spawn-claude.d.ts.map +1 -0
- package/dist/skill-test/spawn-claude.js +92 -0
- package/dist/skill-test/spawn-claude.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsed representation of a git URL accepted by `vat audit`.
|
|
3
|
+
*
|
|
4
|
+
* - `cloneUrl` is the URL passed to `git clone` (after stripping ref/subpath
|
|
5
|
+
* fragments and after expanding GitHub shorthand to a full HTTPS URL).
|
|
6
|
+
* - `ref` is an optional branch or tag name (deep commit SHAs are not
|
|
7
|
+
* guaranteed to work with shallow clone — see design spec for details).
|
|
8
|
+
* - `subpath` is an optional subdirectory within the cloned repo to audit.
|
|
9
|
+
*/
|
|
10
|
+
export interface ParsedGitUrl {
|
|
11
|
+
cloneUrl: string;
|
|
12
|
+
ref?: string;
|
|
13
|
+
subpath?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse a string into a {@link ParsedGitUrl}.
|
|
17
|
+
*
|
|
18
|
+
* Accepted forms:
|
|
19
|
+
* - `https://host/owner/repo.git`
|
|
20
|
+
* - `https://host/owner/repo.git#ref`
|
|
21
|
+
* - `https://host/owner/repo.git#ref:subpath`
|
|
22
|
+
* - `https://github.com/owner/repo/tree/<ref>/<subpath>` (GitHub web URL)
|
|
23
|
+
* - `owner/repo` (GitHub shorthand → expanded to HTTPS)
|
|
24
|
+
* - `git@host:owner/repo.git`
|
|
25
|
+
* - `ssh://git@host/owner/repo.git`
|
|
26
|
+
*
|
|
27
|
+
* Throws on malformed input.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseGitUrl(input: string): ParsedGitUrl;
|
|
30
|
+
/**
|
|
31
|
+
* Detect whether a string should be treated as a git URL (for the polymorphic
|
|
32
|
+
* `[git-url-or-path]` audit argument). True for:
|
|
33
|
+
* - http(s):// URLs
|
|
34
|
+
* - ssh:// URLs
|
|
35
|
+
* - file:// URLs (used by integration tests against local bare repos)
|
|
36
|
+
* - git@host:path scp-style URLs
|
|
37
|
+
* - GitHub shorthand `owner/repo` (strict — no extensions, no extra slashes)
|
|
38
|
+
*
|
|
39
|
+
* Everything else (including relative paths like `./foo/bar`, absolute paths,
|
|
40
|
+
* and multi-segment paths like `foo/bar/baz`) is treated as a filesystem path.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isGitUrl(input: string): boolean;
|
|
43
|
+
//# sourceMappingURL=git-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-url.d.ts","sourceRoot":"","sources":["../src/git-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAgEvD;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAmB/C"}
|
package/dist/git-url.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split a URL with an optional `#ref[:subpath]` fragment into the base URL
|
|
3
|
+
* and fragment components. Keeps the fragment-handling logic local so the
|
|
4
|
+
* per-form regexes can stay simple and anchored.
|
|
5
|
+
*/
|
|
6
|
+
function splitFragment(input) {
|
|
7
|
+
const hashIndex = input.indexOf('#');
|
|
8
|
+
if (hashIndex === -1) {
|
|
9
|
+
return { base: input };
|
|
10
|
+
}
|
|
11
|
+
const base = input.slice(0, hashIndex);
|
|
12
|
+
const fragment = input.slice(hashIndex + 1);
|
|
13
|
+
const colonIndex = fragment.indexOf(':');
|
|
14
|
+
if (colonIndex === -1) {
|
|
15
|
+
return { base, ref: fragment };
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
base,
|
|
19
|
+
ref: fragment.slice(0, colonIndex),
|
|
20
|
+
subpath: fragment.slice(colonIndex + 1),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parse a string into a {@link ParsedGitUrl}.
|
|
25
|
+
*
|
|
26
|
+
* Accepted forms:
|
|
27
|
+
* - `https://host/owner/repo.git`
|
|
28
|
+
* - `https://host/owner/repo.git#ref`
|
|
29
|
+
* - `https://host/owner/repo.git#ref:subpath`
|
|
30
|
+
* - `https://github.com/owner/repo/tree/<ref>/<subpath>` (GitHub web URL)
|
|
31
|
+
* - `owner/repo` (GitHub shorthand → expanded to HTTPS)
|
|
32
|
+
* - `git@host:owner/repo.git`
|
|
33
|
+
* - `ssh://git@host/owner/repo.git`
|
|
34
|
+
*
|
|
35
|
+
* Throws on malformed input.
|
|
36
|
+
*/
|
|
37
|
+
export function parseGitUrl(input) {
|
|
38
|
+
const trimmed = input.trim();
|
|
39
|
+
if (trimmed === '') {
|
|
40
|
+
throw new Error(`Invalid git URL or path: <empty>.`);
|
|
41
|
+
}
|
|
42
|
+
// file:// form: file:///path/to/repo[#ref[:subpath]] — used primarily by
|
|
43
|
+
// integration tests that clone a local bare repo. `git clone` accepts
|
|
44
|
+
// file:// natively.
|
|
45
|
+
if (trimmed.startsWith('file://')) {
|
|
46
|
+
const { base, ref, subpath } = splitFragment(trimmed);
|
|
47
|
+
return buildParsed(base, ref, subpath);
|
|
48
|
+
}
|
|
49
|
+
// HTTPS .git form: https://host/path.git[#ref[:subpath]]
|
|
50
|
+
if (/^https?:\/\//.test(trimmed)) {
|
|
51
|
+
const { base, ref, subpath } = splitFragment(trimmed);
|
|
52
|
+
if (base.endsWith('.git')) {
|
|
53
|
+
return buildParsed(base, ref, subpath);
|
|
54
|
+
}
|
|
55
|
+
// GitHub web URL: https://github.com/owner/repo/tree/<ref>[/<subpath>]
|
|
56
|
+
const ghWeb =
|
|
57
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- Anchored ^...$ with bounded character classes; the only variable-length group is the trailing subpath. Safe from ReDoS.
|
|
58
|
+
/^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/tree\/([^/]+)(?:\/(.+))?$/.exec(trimmed);
|
|
59
|
+
if (ghWeb) {
|
|
60
|
+
const owner = ghWeb[1] ?? '';
|
|
61
|
+
const repo = ghWeb[2] ?? '';
|
|
62
|
+
const webRef = ghWeb[3];
|
|
63
|
+
const webSubpath = ghWeb[4];
|
|
64
|
+
return buildParsed(`https://github.com/${owner}/${repo}.git`, webRef, webSubpath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// SSH ssh:// form: ssh://git@host/path[#ref[:subpath]]
|
|
68
|
+
if (trimmed.startsWith('ssh://')) {
|
|
69
|
+
const { base, ref, subpath } = splitFragment(trimmed);
|
|
70
|
+
return buildParsed(base, ref, subpath);
|
|
71
|
+
}
|
|
72
|
+
// SSH scp-like form: git@host:owner/repo.git[#ref[:subpath]]
|
|
73
|
+
// Anchored, no alternation with nested quantifiers — safe from ReDoS.
|
|
74
|
+
if (/^[^@\s]+@[^:\s]+:[^#\s]+/.test(trimmed)) {
|
|
75
|
+
const { base, ref, subpath } = splitFragment(trimmed);
|
|
76
|
+
return buildParsed(base, ref, subpath);
|
|
77
|
+
}
|
|
78
|
+
// GitHub shorthand: owner/repo[#ref[:subpath]] (single slash in base)
|
|
79
|
+
const { base, ref, subpath } = splitFragment(trimmed);
|
|
80
|
+
const shorthand = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(base);
|
|
81
|
+
if (shorthand) {
|
|
82
|
+
const owner = shorthand[1] ?? '';
|
|
83
|
+
const repo = shorthand[2] ?? '';
|
|
84
|
+
return buildParsed(`https://github.com/${owner}/${repo}.git`, ref, subpath);
|
|
85
|
+
}
|
|
86
|
+
throw new Error(`Invalid git URL or path: ${input}. Accepted forms: ` +
|
|
87
|
+
`https://<host>/<owner>/<repo>.git, ` +
|
|
88
|
+
`git@<host>:<owner>/<repo>.git, ` +
|
|
89
|
+
`<owner>/<repo>, or a local filesystem path.`);
|
|
90
|
+
}
|
|
91
|
+
function buildParsed(cloneUrl, ref, subpath) {
|
|
92
|
+
const result = { cloneUrl };
|
|
93
|
+
if (ref !== undefined && ref !== '')
|
|
94
|
+
result.ref = ref;
|
|
95
|
+
if (subpath !== undefined && subpath !== '')
|
|
96
|
+
result.subpath = subpath;
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Detect whether a string should be treated as a git URL (for the polymorphic
|
|
101
|
+
* `[git-url-or-path]` audit argument). True for:
|
|
102
|
+
* - http(s):// URLs
|
|
103
|
+
* - ssh:// URLs
|
|
104
|
+
* - file:// URLs (used by integration tests against local bare repos)
|
|
105
|
+
* - git@host:path scp-style URLs
|
|
106
|
+
* - GitHub shorthand `owner/repo` (strict — no extensions, no extra slashes)
|
|
107
|
+
*
|
|
108
|
+
* Everything else (including relative paths like `./foo/bar`, absolute paths,
|
|
109
|
+
* and multi-segment paths like `foo/bar/baz`) is treated as a filesystem path.
|
|
110
|
+
*/
|
|
111
|
+
export function isGitUrl(input) {
|
|
112
|
+
const trimmed = input.trim();
|
|
113
|
+
if (trimmed === '')
|
|
114
|
+
return false;
|
|
115
|
+
if (/^https?:\/\//.test(trimmed))
|
|
116
|
+
return true;
|
|
117
|
+
if (trimmed.startsWith('ssh://'))
|
|
118
|
+
return true;
|
|
119
|
+
if (trimmed.startsWith('file://'))
|
|
120
|
+
return true;
|
|
121
|
+
// Match the same scp-style pattern parseGitUrl uses, including the
|
|
122
|
+
// requirement of a non-empty path segment after the colon. Without the
|
|
123
|
+
// trailing `[^#\s]+`, `isGitUrl` would accept inputs like `foo@host:`
|
|
124
|
+
// that parseGitUrl then rejects with a less-helpful error.
|
|
125
|
+
if (/^[^@\s]+@[^:\s]+:[^#\s]+/.test(trimmed))
|
|
126
|
+
return true;
|
|
127
|
+
// Strict GitHub shorthand: exactly two segments, no extension on second,
|
|
128
|
+
// no path separators beyond the single /. Strip any `#ref[:subpath]`
|
|
129
|
+
// fragment first so `owner/repo#main` and `owner/repo#main:sub` are
|
|
130
|
+
// recognized — `parseGitUrl` handles fragments uniformly across forms.
|
|
131
|
+
const hashIndex = trimmed.indexOf('#');
|
|
132
|
+
const base = hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex);
|
|
133
|
+
return /^[A-Za-z0-9_-]+\/[A-Za-z0-9_-]+$/.test(base);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=git-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-url.js","sourceRoot":"","sources":["../src/git-url.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACzB,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;IACjC,CAAC;IACD,OAAO;QACL,IAAI;QACJ,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;QAClC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;KACxC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,yEAAyE;IACzE,sEAAsE;IACtE,oBAAoB;IACpB,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACtD,OAAO,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,yDAAyD;IACzD,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACjC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QACzC,CAAC;QAED,uEAAuE;QACvE,MAAM,KAAK;QACT,mLAAmL;QACnL,yFAAyF,CAAC,IAAI,CAC5F,OAAO,CACR,CAAC;QACJ,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC5B,OAAO,WAAW,CAAC,sBAAsB,KAAK,IAAI,IAAI,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QACpF,CAAC;IACH,CAAC;IAED,uDAAuD;IACvD,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACtD,OAAO,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,6DAA6D;IAC7D,sEAAsE;IACtE,IAAI,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACtD,OAAO,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,sEAAsE;IACtE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,wCAAwC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChC,OAAO,WAAW,CAAC,sBAAsB,KAAK,IAAI,IAAI,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,IAAI,KAAK,CACb,4BAA4B,KAAK,oBAAoB;QACnD,qCAAqC;QACrC,iCAAiC;QACjC,6CAA6C,CAChD,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB,EAAE,GAAY,EAAE,OAAgB;IACnE,MAAM,MAAM,GAAiB,EAAE,QAAQ,EAAE,CAAC;IAC1C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;IACtD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,EAAE;QAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IACtE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,mEAAmE;IACnE,uEAAuE;IACvE,sEAAsE;IACtE,2DAA2D;IAC3D,IAAI,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAE1D,yEAAyE;IACzE,qEAAqE;IACrE,oEAAoE;IACpE,uEAAuE;IACvE,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtE,OAAO,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './asset-reference.js';
|
|
|
10
10
|
export * from './fs-utils.js';
|
|
11
11
|
export * from './file-crawler.js';
|
|
12
12
|
export * from './gitignore-checker.js';
|
|
13
|
+
export * from './git-url.js';
|
|
13
14
|
export * from './git-utils.js';
|
|
14
15
|
export * from './project-utils.js';
|
|
15
16
|
export * from './git-tracker.js';
|
|
@@ -22,4 +23,5 @@ export type { ProviderMatch } from './link-auth/select-provider.js';
|
|
|
22
23
|
export type { RewriteRule } from './link-auth/rewrite.js';
|
|
23
24
|
export type { TokenSource } from './link-auth/resolve-token.js';
|
|
24
25
|
export { expandMacro, UnknownMacroError } from './link-auth/expand-macro.js';
|
|
26
|
+
export * from './skill-test/index.js';
|
|
25
27
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,gBAAgB,CAAC;AAG/B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,eAAe,CAAC;AAG9B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAI/B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,eAAe,CAAC;AAG9B,cAAc,oBAAoB,CAAC;AAInC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,uBAAuB,EACvB,KAAK,cAAc,GACpB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,gBAAgB,CAAC;AAG/B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,eAAe,CAAC;AAG9B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,cAAc,CAAC;AAG7B,cAAc,gBAAgB,CAAC;AAI/B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,eAAe,CAAC;AAG9B,cAAc,oBAAoB,CAAC;AAInC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,uBAAuB,EACvB,KAAK,cAAc,GACpB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAG7E,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,8 @@ export * from './fs-utils.js';
|
|
|
16
16
|
export * from './file-crawler.js';
|
|
17
17
|
// Git ignore checking
|
|
18
18
|
export * from './gitignore-checker.js';
|
|
19
|
+
// Git URL parsing (parse/detect git URLs, GitHub shorthand, SSH forms)
|
|
20
|
+
export * from './git-url.js';
|
|
19
21
|
// Git utilities (using git commands directly)
|
|
20
22
|
export * from './git-utils.js';
|
|
21
23
|
// Project root discovery (canonical: config → git → null).
|
|
@@ -35,4 +37,6 @@ export * from './skill-targets.js';
|
|
|
35
37
|
// Internal helpers (rewrite, build-headers, etc.) stay module-private.
|
|
36
38
|
export { resolveAuthenticatedUrl, } from './link-auth/resolve.js';
|
|
37
39
|
export { expandMacro, UnknownMacroError } from './link-auth/expand-macro.js';
|
|
40
|
+
// Skill testing utilities (environment management for headless agent runs)
|
|
41
|
+
export * from './skill-test/index.js';
|
|
38
42
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8DAA8D;AAC9D,cAAc,gBAAgB,CAAC;AAE/B,gCAAgC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,2DAA2D;AAC3D,cAAc,sBAAsB,CAAC;AAErC,uBAAuB;AACvB,cAAc,eAAe,CAAC;AAE9B,wCAAwC;AACxC,cAAc,mBAAmB,CAAC;AAElC,sBAAsB;AACtB,cAAc,wBAAwB,CAAC;AAEvC,8CAA8C;AAC9C,cAAc,gBAAgB,CAAC;AAE/B,2DAA2D;AAC3D,iEAAiE;AACjE,cAAc,oBAAoB,CAAC;AAEnC,yDAAyD;AACzD,cAAc,kBAAkB,CAAC;AAEjC,oDAAoD;AACpD,cAAc,mBAAmB,CAAC;AAElC,4CAA4C;AAC5C,cAAc,wBAAwB,CAAC;AAEvC,2DAA2D;AAC3D,cAAc,eAAe,CAAC;AAE9B,oEAAoE;AACpE,cAAc,oBAAoB,CAAC;AAEnC,uDAAuD;AACvD,uEAAuE;AACvE,OAAO,EAKL,uBAAuB,GAExB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8DAA8D;AAC9D,cAAc,gBAAgB,CAAC;AAE/B,gCAAgC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,2DAA2D;AAC3D,cAAc,sBAAsB,CAAC;AAErC,uBAAuB;AACvB,cAAc,eAAe,CAAC;AAE9B,wCAAwC;AACxC,cAAc,mBAAmB,CAAC;AAElC,sBAAsB;AACtB,cAAc,wBAAwB,CAAC;AAEvC,uEAAuE;AACvE,cAAc,cAAc,CAAC;AAE7B,8CAA8C;AAC9C,cAAc,gBAAgB,CAAC;AAE/B,2DAA2D;AAC3D,iEAAiE;AACjE,cAAc,oBAAoB,CAAC;AAEnC,yDAAyD;AACzD,cAAc,kBAAkB,CAAC;AAEjC,oDAAoD;AACpD,cAAc,mBAAmB,CAAC;AAElC,4CAA4C;AAC5C,cAAc,wBAAwB,CAAC;AAEvC,2DAA2D;AAC3D,cAAc,eAAe,CAAC;AAE9B,oEAAoE;AACpE,cAAc,oBAAoB,CAAC;AAEnC,uDAAuD;AACvD,uEAAuE;AACvE,OAAO,EAKL,uBAAuB,GAExB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAE7E,2EAA2E;AAC3E,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type AuthMode = 'inherit' | 'subscription' | 'api-key' | 'auto';
|
|
2
|
+
export type AuthMechanism = 'subscription' | 'api-key';
|
|
3
|
+
export interface AuthStatusResult {
|
|
4
|
+
loggedIn: boolean;
|
|
5
|
+
authMethod?: string;
|
|
6
|
+
apiKeySource?: string;
|
|
7
|
+
}
|
|
8
|
+
export type AuthStatusProbe = (env: NodeJS.ProcessEnv) => AuthStatusResult | null;
|
|
9
|
+
export interface ResolveAuthOptions {
|
|
10
|
+
mode: AuthMode;
|
|
11
|
+
requireAuth?: AuthMechanism;
|
|
12
|
+
sourceEnv: NodeJS.ProcessEnv;
|
|
13
|
+
/** Injected in tests; defaults to the real CLI probe in production. */
|
|
14
|
+
probe?: AuthStatusProbe;
|
|
15
|
+
modelVars?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface ResolvedAuth {
|
|
18
|
+
forwardedEnv: NodeJS.ProcessEnv;
|
|
19
|
+
effectiveMechanism: AuthMechanism;
|
|
20
|
+
authMethod?: string;
|
|
21
|
+
apiKeySource?: string;
|
|
22
|
+
}
|
|
23
|
+
/** A preflight auth failure — maps to exit code 2. */
|
|
24
|
+
export declare class AuthPreflightError extends Error {
|
|
25
|
+
readonly exitCode: 2;
|
|
26
|
+
constructor(message: string);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Token-free auth probe (spec §8/§10). Runs `claude auth status --json` in the
|
|
30
|
+
* given env: returns in ~150ms, exit 0, spends NO tokens. NOT a `-p` call.
|
|
31
|
+
*/
|
|
32
|
+
export declare function probeAuthStatus(env: NodeJS.ProcessEnv): AuthStatusResult | null;
|
|
33
|
+
export declare function resolveAuth(opts: ResolveAuthOptions): ResolvedAuth;
|
|
34
|
+
//# sourceMappingURL=auth-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-resolver.d.ts","sourceRoot":"","sources":["../../src/skill-test/auth-resolver.ts"],"names":[],"mappings":"AAIA,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,KAAK;IAC3C,QAAQ,CAAC,QAAQ,EAAG,CAAC,CAAU;gBACnB,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,gBAAgB,GAAG,IAAI,CAa/E;AAgBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,YAAY,CA+BlE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { safeExecResult } from '../safe-exec.js';
|
|
2
|
+
import { buildForwardedEnv } from './env-scrub.js';
|
|
3
|
+
/** A preflight auth failure — maps to exit code 2. */
|
|
4
|
+
export class AuthPreflightError extends Error {
|
|
5
|
+
exitCode = 2;
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'AuthPreflightError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Token-free auth probe (spec §8/§10). Runs `claude auth status --json` in the
|
|
13
|
+
* given env: returns in ~150ms, exit 0, spends NO tokens. NOT a `-p` call.
|
|
14
|
+
*/
|
|
15
|
+
export function probeAuthStatus(env) {
|
|
16
|
+
const res = safeExecResult('claude', ['auth', 'status', '--json'], { env, encoding: 'utf8', stdio: 'pipe' });
|
|
17
|
+
if (!res.success)
|
|
18
|
+
return null;
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(res.stdout.toString());
|
|
21
|
+
return {
|
|
22
|
+
loggedIn: parsed['loggedIn'] === true,
|
|
23
|
+
...(typeof parsed['authMethod'] === 'string' ? { authMethod: parsed['authMethod'] } : {}),
|
|
24
|
+
...(typeof parsed['apiKeySource'] === 'string' ? { apiKeySource: parsed['apiKeySource'] } : {}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function hasInferenceKey(env) {
|
|
32
|
+
return Boolean(env['ANTHROPIC_API_KEY'] ?? env['ANTHROPIC_AUTH_TOKEN']);
|
|
33
|
+
}
|
|
34
|
+
/** Decide whether to scrub the inference key, given mode + a subscription-present probe. */
|
|
35
|
+
function decideScrub(mode, sourceEnv, probe) {
|
|
36
|
+
if (mode === 'subscription')
|
|
37
|
+
return true;
|
|
38
|
+
if (mode === 'api-key' || mode === 'auto')
|
|
39
|
+
return false;
|
|
40
|
+
// inherit: scrub the key only if a subscription is actually logged in.
|
|
41
|
+
const scrubbed = buildForwardedEnv(sourceEnv, { scrubInferenceKey: true });
|
|
42
|
+
const status = probe(scrubbed);
|
|
43
|
+
return status?.loggedIn === true;
|
|
44
|
+
}
|
|
45
|
+
export function resolveAuth(opts) {
|
|
46
|
+
const probe = opts.probe ?? probeAuthStatus;
|
|
47
|
+
const baseOpts = opts.modelVars ? { modelVars: opts.modelVars } : {};
|
|
48
|
+
if (opts.mode === 'api-key' && !hasInferenceKey(opts.sourceEnv)) {
|
|
49
|
+
throw new AuthPreflightError('--auth api-key requires ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN to be set.');
|
|
50
|
+
}
|
|
51
|
+
const scrubInferenceKey = decideScrub(opts.mode, opts.sourceEnv, probe);
|
|
52
|
+
const forwardedEnv = buildForwardedEnv(opts.sourceEnv, { scrubInferenceKey, ...baseOpts });
|
|
53
|
+
const status = probe(forwardedEnv);
|
|
54
|
+
if (opts.mode === 'subscription' && status?.loggedIn !== true) {
|
|
55
|
+
throw new AuthPreflightError('--auth subscription requires a logged-in subscription (claude auth status reports not logged in).');
|
|
56
|
+
}
|
|
57
|
+
const effectiveMechanism = !scrubInferenceKey && hasInferenceKey(forwardedEnv) ? 'api-key' : 'subscription';
|
|
58
|
+
if (opts.requireAuth && opts.requireAuth !== effectiveMechanism) {
|
|
59
|
+
throw new AuthPreflightError(`--require-auth ${opts.requireAuth} but the effective mechanism is ${effectiveMechanism}. Refusing to spend tokens.`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
forwardedEnv,
|
|
63
|
+
effectiveMechanism,
|
|
64
|
+
...(status?.authMethod ? { authMethod: status.authMethod } : {}),
|
|
65
|
+
...(status?.apiKeySource ? { apiKeySource: status.apiKeySource } : {}),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=auth-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-resolver.js","sourceRoot":"","sources":["../../src/skill-test/auth-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the forwarded environment for the headless `claude` child using a
|
|
3
|
+
* STRICT exact-name allowlist (spec §14). We never forward ANTHROPIC_* by
|
|
4
|
+
* prefix — that would silently leak ANTHROPIC_ADMIN_API_KEY, proxy overrides,
|
|
5
|
+
* etc. into untrusted skill code. Anything not enumerated is dropped.
|
|
6
|
+
*/
|
|
7
|
+
export interface ForwardEnvOptions {
|
|
8
|
+
/** True under --auth subscription (and inherit-with-subscription): drop the inference key so the child uses OAuth. */
|
|
9
|
+
scrubInferenceKey: boolean;
|
|
10
|
+
/** Additional exact model var names the run needs (e.g. ANTHROPIC_MODEL). */
|
|
11
|
+
modelVars?: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function buildForwardedEnv(source: NodeJS.ProcessEnv, opts: ForwardEnvOptions): NodeJS.ProcessEnv;
|
|
14
|
+
//# sourceMappingURL=env-scrub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-scrub.d.ts","sourceRoot":"","sources":["../../src/skill-test/env-scrub.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,iBAAiB;IAChC,sHAAsH;IACtH,iBAAiB,EAAE,OAAO,CAAC;IAC3B,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAWD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,UAAU,EACzB,IAAI,EAAE,iBAAiB,GACtB,MAAM,CAAC,UAAU,CAuBnB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the forwarded environment for the headless `claude` child using a
|
|
3
|
+
* STRICT exact-name allowlist (spec §14). We never forward ANTHROPIC_* by
|
|
4
|
+
* prefix — that would silently leak ANTHROPIC_ADMIN_API_KEY, proxy overrides,
|
|
5
|
+
* etc. into untrusted skill code. Anything not enumerated is dropped.
|
|
6
|
+
*/
|
|
7
|
+
/** Process-essential vars that must survive for the child to even start. */
|
|
8
|
+
const PROCESS_ESSENTIALS = ['PATH', 'Path', 'HOME', 'USERPROFILE', 'SystemRoot', 'TEMP', 'TMP', 'TMPDIR', 'LANG', 'LC_ALL'];
|
|
9
|
+
/** The only auth/config vars ever forwarded. */
|
|
10
|
+
const AUTH_CONFIG_ALLOWLIST = ['CLAUDE_CONFIG_DIR'];
|
|
11
|
+
/** The active inference credential candidates (forwarded unless scrubbed). */
|
|
12
|
+
const INFERENCE_CREDENTIALS = ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN'];
|
|
13
|
+
export function buildForwardedEnv(source, opts) {
|
|
14
|
+
const out = {};
|
|
15
|
+
const allow = new Set([
|
|
16
|
+
...PROCESS_ESSENTIALS,
|
|
17
|
+
...AUTH_CONFIG_ALLOWLIST,
|
|
18
|
+
...(opts.modelVars ?? []),
|
|
19
|
+
]);
|
|
20
|
+
if (!opts.scrubInferenceKey) {
|
|
21
|
+
for (const name of INFERENCE_CREDENTIALS)
|
|
22
|
+
allow.add(name);
|
|
23
|
+
}
|
|
24
|
+
for (const name of allow) {
|
|
25
|
+
const value = source[name];
|
|
26
|
+
if (value !== undefined)
|
|
27
|
+
out[name] = value;
|
|
28
|
+
}
|
|
29
|
+
// ANTHROPIC_ADMIN_API_KEY and CLAUDECODE/CLAUDE_CODE_* are never in `allow`,
|
|
30
|
+
// so they are dropped by construction. No explicit delete needed — but assert
|
|
31
|
+
// the invariant defensively so a future allowlist edit can't reintroduce it.
|
|
32
|
+
delete out['ANTHROPIC_ADMIN_API_KEY'];
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=env-scrub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-scrub.js","sourceRoot":"","sources":["../../src/skill-test/env-scrub.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,4EAA4E;AAC5E,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAU,CAAC;AAErI,gDAAgD;AAChD,MAAM,qBAAqB,GAAG,CAAC,mBAAmB,CAAU,CAAC;AAE7D,8EAA8E;AAC9E,MAAM,qBAAqB,GAAG,CAAC,mBAAmB,EAAE,sBAAsB,CAAU,CAAC;AAErF,MAAM,UAAU,iBAAiB,CAC/B,MAAyB,EACzB,IAAuB;IAEvB,MAAM,GAAG,GAAsB,EAAE,CAAC;IAElC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAS;QAC5B,GAAG,kBAAkB;QACrB,GAAG,qBAAqB;QACxB,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;KAC1B,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC5B,KAAK,MAAM,IAAI,IAAI,qBAAqB;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,KAAK,KAAK,SAAS;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;IAC7C,CAAC;IAED,6EAA6E;IAC7E,8EAA8E;IAC9E,6EAA6E;IAC7E,OAAO,GAAG,CAAC,yBAAyB,CAAC,CAAC;IAEtC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { buildForwardedEnv, type ForwardEnvOptions } from './env-scrub.js';
|
|
2
|
+
export { AuthPreflightError, probeAuthStatus, resolveAuth, type AuthMechanism, type AuthMode, type AuthStatusProbe, type AuthStatusResult, type ResolveAuthOptions, type ResolvedAuth, } from './auth-resolver.js';
|
|
3
|
+
export { assembleClaudeArgs, spawnHeadlessClaude, type ClaudeSpawnArgs, type SpawnHeadlessOptions, type SpawnResult, } from './spawn-claude.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/skill-test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC3E,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,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,WAAW,GACjB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/skill-test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA0B,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,WAAW,GAOZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GAIpB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface ClaudeSpawnArgs {
|
|
2
|
+
pluginDirs: string[];
|
|
3
|
+
sandboxDir: string;
|
|
4
|
+
model?: string;
|
|
5
|
+
maxTurns?: number;
|
|
6
|
+
maxBudgetUsd?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Pure assembly of the headless `claude -p` argv (spec §9). The prompt is never
|
|
10
|
+
* inlined as an argv string (Windows cmd-quoting safety) and claude 2.x has no
|
|
11
|
+
* `--prompt-file` flag — the prompt is fed to the child's stdin by
|
|
12
|
+
* {@link spawnHeadlessClaude} instead. `--setting-sources ""` suppresses
|
|
13
|
+
* user/project settings; built-in skills remain (§5).
|
|
14
|
+
*/
|
|
15
|
+
export declare function assembleClaudeArgs(opts: ClaudeSpawnArgs): string[];
|
|
16
|
+
export interface SpawnResult {
|
|
17
|
+
status: number;
|
|
18
|
+
timedOut: boolean;
|
|
19
|
+
stalled: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface SpawnHeadlessOptions extends ClaudeSpawnArgs {
|
|
22
|
+
/**
|
|
23
|
+
* Path to the experimenter-prompt file. Its contents are streamed to the
|
|
24
|
+
* child's stdin (claude 2.x reads the `-p` prompt from stdin; there is no
|
|
25
|
+
* `--prompt-file` flag). Kept off argv so the prompt is never inlined
|
|
26
|
+
* (Windows cmd-quoting safety).
|
|
27
|
+
*/
|
|
28
|
+
promptFile: string;
|
|
29
|
+
cwd: string;
|
|
30
|
+
env: NodeJS.ProcessEnv;
|
|
31
|
+
timeoutMs: number;
|
|
32
|
+
/** If set, SIGKILL the child if no stdout/stderr output is received for this many ms. */
|
|
33
|
+
stallMs?: number;
|
|
34
|
+
onStdout?: (chunk: string) => void;
|
|
35
|
+
onStderr?: (chunk: string) => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Spawn the headless `claude`. The prompt file is streamed to the child's stdin
|
|
39
|
+
* so the session reads its prompt and then sees EOF (it cannot block on input,
|
|
40
|
+
* §16). Wall-clock kill on timeout. If `stallMs` is set, a resettable stall
|
|
41
|
+
* watchdog kills the child when no output is received for that duration
|
|
42
|
+
* (stalled: true in result).
|
|
43
|
+
*/
|
|
44
|
+
export declare function spawnHeadlessClaude(opts: SpawnHeadlessOptions): Promise<SpawnResult>;
|
|
45
|
+
//# sourceMappingURL=spawn-claude.d.ts.map
|
|
@@ -0,0 +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;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,EAAE,CAmBlE;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,oBAAqB,SAAQ,eAAe;IAC3D;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,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;CACpC;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CAsD1F"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import which from 'which';
|
|
4
|
+
/**
|
|
5
|
+
* Pure assembly of the headless `claude -p` argv (spec §9). The prompt is never
|
|
6
|
+
* inlined as an argv string (Windows cmd-quoting safety) and claude 2.x has no
|
|
7
|
+
* `--prompt-file` flag — the prompt is fed to the child's stdin by
|
|
8
|
+
* {@link spawnHeadlessClaude} instead. `--setting-sources ""` suppresses
|
|
9
|
+
* user/project settings; built-in skills remain (§5).
|
|
10
|
+
*/
|
|
11
|
+
export function assembleClaudeArgs(opts) {
|
|
12
|
+
const args = [
|
|
13
|
+
'-p',
|
|
14
|
+
// claude 2.x rejects `-p --output-format stream-json` unless --verbose is
|
|
15
|
+
// also passed ("--output-format=stream-json requires --verbose"). The
|
|
16
|
+
// streamed JSON is piped to stderr for progress visibility.
|
|
17
|
+
'--output-format', 'stream-json',
|
|
18
|
+
'--verbose',
|
|
19
|
+
'--setting-sources', '',
|
|
20
|
+
'--permission-mode', 'bypassPermissions',
|
|
21
|
+
'--add-dir', opts.sandboxDir,
|
|
22
|
+
];
|
|
23
|
+
for (const dir of opts.pluginDirs) {
|
|
24
|
+
args.push('--plugin-dir', dir);
|
|
25
|
+
}
|
|
26
|
+
if (opts.model !== undefined)
|
|
27
|
+
args.push('--model', opts.model);
|
|
28
|
+
if (opts.maxTurns !== undefined)
|
|
29
|
+
args.push('--max-turns', String(opts.maxTurns));
|
|
30
|
+
if (opts.maxBudgetUsd !== undefined)
|
|
31
|
+
args.push('--max-budget-usd', String(opts.maxBudgetUsd));
|
|
32
|
+
return args;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Spawn the headless `claude`. The prompt file is streamed to the child's stdin
|
|
36
|
+
* so the session reads its prompt and then sees EOF (it cannot block on input,
|
|
37
|
+
* §16). Wall-clock kill on timeout. If `stallMs` is set, a resettable stall
|
|
38
|
+
* watchdog kills the child when no output is received for that duration
|
|
39
|
+
* (stalled: true in result).
|
|
40
|
+
*/
|
|
41
|
+
export async function spawnHeadlessClaude(opts) {
|
|
42
|
+
const bin = which.sync('claude');
|
|
43
|
+
const args = assembleClaudeArgs(opts);
|
|
44
|
+
return await new Promise((resolve, reject) => {
|
|
45
|
+
const child = spawn(bin, args, { cwd: opts.cwd, env: opts.env, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
46
|
+
let timedOut = false;
|
|
47
|
+
let stalled = false;
|
|
48
|
+
const wallTimer = setTimeout(() => {
|
|
49
|
+
timedOut = true;
|
|
50
|
+
child.kill('SIGKILL');
|
|
51
|
+
}, opts.timeoutMs);
|
|
52
|
+
// Stall watchdog: reset on every stdout/stderr chunk; fire if silent for stallMs.
|
|
53
|
+
let stallTimer;
|
|
54
|
+
if (opts.stallMs !== undefined) {
|
|
55
|
+
const stallMs = opts.stallMs;
|
|
56
|
+
const resetStallTimer = () => {
|
|
57
|
+
if (stallTimer !== undefined)
|
|
58
|
+
clearTimeout(stallTimer);
|
|
59
|
+
stallTimer = setTimeout(() => {
|
|
60
|
+
stalled = true;
|
|
61
|
+
child.kill('SIGKILL');
|
|
62
|
+
}, stallMs);
|
|
63
|
+
};
|
|
64
|
+
resetStallTimer();
|
|
65
|
+
child.stdout.on('data', () => { resetStallTimer(); });
|
|
66
|
+
child.stderr.on('data', () => { resetStallTimer(); });
|
|
67
|
+
}
|
|
68
|
+
const clearAllTimers = () => {
|
|
69
|
+
clearTimeout(wallTimer);
|
|
70
|
+
if (stallTimer !== undefined)
|
|
71
|
+
clearTimeout(stallTimer);
|
|
72
|
+
};
|
|
73
|
+
// Feed the prompt to the child via stdin (claude 2.x has no --prompt-file).
|
|
74
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- our own derived prompt path
|
|
75
|
+
const promptStream = createReadStream(opts.promptFile);
|
|
76
|
+
promptStream.on('error', err => {
|
|
77
|
+
clearAllTimers();
|
|
78
|
+
child.kill('SIGKILL');
|
|
79
|
+
reject(err);
|
|
80
|
+
});
|
|
81
|
+
// Swallow EPIPE: if the child exits before consuming all stdin, the write
|
|
82
|
+
// end errors — that is not a harness failure (the close handler reports the
|
|
83
|
+
// child's real exit status).
|
|
84
|
+
child.stdin.on('error', () => { });
|
|
85
|
+
promptStream.pipe(child.stdin);
|
|
86
|
+
child.stdout.on('data', (d) => { opts.onStdout?.(d.toString()); });
|
|
87
|
+
child.stderr.on('data', (d) => { opts.onStderr?.(d.toString()); });
|
|
88
|
+
child.on('error', err => { clearAllTimers(); reject(err); });
|
|
89
|
+
child.on('close', code => { clearAllTimers(); resolve({ status: code ?? -1, timedOut, stalled }); });
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=spawn-claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn-claude.js","sourceRoot":"","sources":["../../src/skill-test/spawn-claude.ts"],"names":[],"mappings":"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"}
|