@vibe-agent-toolkit/utils 0.1.39-rc.9 → 0.1.40-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/fs/file-hash.d.ts +17 -0
  2. package/dist/fs/file-hash.d.ts.map +1 -0
  3. package/dist/fs/file-hash.js +23 -0
  4. package/dist/fs/file-hash.js.map +1 -0
  5. package/dist/glob/glob-pattern.d.ts +65 -0
  6. package/dist/glob/glob-pattern.d.ts.map +1 -0
  7. package/dist/glob/glob-pattern.js +133 -0
  8. package/dist/glob/glob-pattern.js.map +1 -0
  9. package/dist/index.d.ts +4 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +7 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/link-auth/resolve-token.d.ts +33 -1
  14. package/dist/link-auth/resolve-token.d.ts.map +1 -1
  15. package/dist/link-auth/resolve-token.js +33 -4
  16. package/dist/link-auth/resolve-token.js.map +1 -1
  17. package/dist/link-auth/resolve.d.ts +44 -0
  18. package/dist/link-auth/resolve.d.ts.map +1 -1
  19. package/dist/link-auth/resolve.js +12 -1
  20. package/dist/link-auth/resolve.js.map +1 -1
  21. package/dist/path-utils.d.ts +30 -0
  22. package/dist/path-utils.d.ts.map +1 -1
  23. package/dist/path-utils.js +34 -0
  24. package/dist/path-utils.js.map +1 -1
  25. package/dist/skill-test/env-scrub.d.ts +58 -0
  26. package/dist/skill-test/env-scrub.d.ts.map +1 -1
  27. package/dist/skill-test/env-scrub.js +148 -2
  28. package/dist/skill-test/env-scrub.js.map +1 -1
  29. package/dist/skill-test/index.d.ts +1 -1
  30. package/dist/skill-test/index.d.ts.map +1 -1
  31. package/dist/skill-test/index.js +1 -1
  32. package/dist/skill-test/index.js.map +1 -1
  33. package/dist/skill-test/spawn-claude.d.ts +34 -9
  34. package/dist/skill-test/spawn-claude.d.ts.map +1 -1
  35. package/dist/skill-test/spawn-claude.js +60 -14
  36. package/dist/skill-test/spawn-claude.js.map +1 -1
  37. package/dist/yaml/surgical-yaml.d.ts +71 -0
  38. package/dist/yaml/surgical-yaml.d.ts.map +1 -0
  39. package/dist/yaml/surgical-yaml.js +314 -0
  40. package/dist/yaml/surgical-yaml.js.map +1 -0
  41. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Compute the SHA-256 hash of a file's raw bytes.
3
+ *
4
+ * Reads the file synchronously and returns the full lowercase hex digest (64
5
+ * characters). The hash is computed over the raw byte content, so it is stable
6
+ * across platforms for identical file content and changes whenever the content
7
+ * changes.
8
+ *
9
+ * @param path - Absolute (or relative) path to the file to hash.
10
+ * @returns Lowercase hex SHA-256 digest string (64 characters).
11
+ *
12
+ * @example
13
+ * const hash = fileContentHash('/path/to/file.txt');
14
+ * // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
15
+ */
16
+ export declare function fileContentHash(path: string): string;
17
+ //# sourceMappingURL=file-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../../src/fs/file-hash.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIpD"}
@@ -0,0 +1,23 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ /**
4
+ * Compute the SHA-256 hash of a file's raw bytes.
5
+ *
6
+ * Reads the file synchronously and returns the full lowercase hex digest (64
7
+ * characters). The hash is computed over the raw byte content, so it is stable
8
+ * across platforms for identical file content and changes whenever the content
9
+ * changes.
10
+ *
11
+ * @param path - Absolute (or relative) path to the file to hash.
12
+ * @returns Lowercase hex SHA-256 digest string (64 characters).
13
+ *
14
+ * @example
15
+ * const hash = fileContentHash('/path/to/file.txt');
16
+ * // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
17
+ */
18
+ export function fileContentHash(path) {
19
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- path is caller-supplied; callers are responsible for path safety
20
+ const bytes = readFileSync(path);
21
+ return createHash('sha256').update(bytes).digest('hex');
22
+ }
23
+ //# sourceMappingURL=file-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-hash.js","sourceRoot":"","sources":["../../src/fs/file-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,uIAAuI;IACvI,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Pure string helpers for glob pattern analysis.
3
+ *
4
+ * No filesystem access. These are building blocks for higher-level glob
5
+ * expansion utilities. Glob patterns always use forward slashes by convention —
6
+ * this module treats `/` as the only path separator (never `\`).
7
+ *
8
+ * ### Escape semantics
9
+ * A backslash immediately preceding a metachar (`*`, `?`, `[`) neutralises it.
10
+ * Any other backslash is left as-is. The algorithm scans left-to-right and
11
+ * sets an `escaped` flag when it sees `\`; the *next* character is skipped for
12
+ * magic detection if `escaped` is true.
13
+ */
14
+ /**
15
+ * Returns `true` iff `source` contains at least one unescaped glob metachar
16
+ * (`*`, `?`, `[`). A backslash immediately before a metachar escapes it.
17
+ *
18
+ * @example
19
+ * isGlob('a/b/*.mjs') // true — unescaped *
20
+ * isGlob('packs/**\/*') // true — unescaped **
21
+ * isGlob('x?.txt') // true — unescaped ?
22
+ * isGlob('files[1].txt') // true — unescaped [
23
+ * isGlob('foo\\*.txt') // false — * is backslash-escaped
24
+ * isGlob('foo/bar.txt') // false — no metachar
25
+ */
26
+ export declare function isGlob(source: string): boolean;
27
+ /**
28
+ * The longest leading path prefix of `pattern` that contains no glob magic —
29
+ * i.e. the static directory base suitable as a `cwd` for a glob runner.
30
+ *
31
+ * Algorithm:
32
+ * 1. Split pattern on `/` (glob patterns always use forward slashes).
33
+ * 2. Accumulate segments until (and **excluding**) the first magic segment.
34
+ * 3. Join accumulated segments with `/`.
35
+ * 4. If the very first segment is magic → return `'.'`.
36
+ * 5. If the joined base is empty but the pattern is absolute (leading `/`,
37
+ * first real segment magic, e.g. `'/*.mjs'`) → return the root `'/'`.
38
+ * 6. If NO segment is magic (not a glob at all) → return the whole pattern.
39
+ *
40
+ * Output always uses forward slashes (pure string ops, no `node:path`).
41
+ *
42
+ * @example
43
+ * staticGlobBase('modules/packs/**\/*') // 'modules/packs'
44
+ * staticGlobBase('a/b/*.mjs') // 'a/b'
45
+ * staticGlobBase('*.mjs') // '.'
46
+ * staticGlobBase('/*.mjs') // '/' (absolute root, not '')
47
+ * staticGlobBase('../mycli/dist/*.mjs') // '../mycli/dist'
48
+ * staticGlobBase('foo/bar.txt') // 'foo/bar.txt'
49
+ */
50
+ export declare function staticGlobBase(pattern: string): string;
51
+ /**
52
+ * The sub-pattern remaining after stripping the static base, with no leading
53
+ * `/`. This is the pattern to pass to a glob runner with `cwd` set to the base.
54
+ *
55
+ * Only meaningful when `isGlob(pattern)` is true — for a non-glob input the
56
+ * return value is `''` (the whole pattern was consumed as the base).
57
+ *
58
+ * @example
59
+ * globMagicRemainder('modules/packs/**\/*') // '**\/*'
60
+ * globMagicRemainder('a/b/*.mjs') // '*.mjs'
61
+ * globMagicRemainder('*.mjs') // '*.mjs'
62
+ * globMagicRemainder('../mycli/dist/*.mjs') // '*.mjs'
63
+ */
64
+ export declare function globMagicRemainder(pattern: string): string;
65
+ //# sourceMappingURL=glob-pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob-pattern.d.ts","sourceRoot":"","sources":["../../src/glob/glob-pattern.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAUH;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAgB9C;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgCtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkB1D"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Pure string helpers for glob pattern analysis.
3
+ *
4
+ * No filesystem access. These are building blocks for higher-level glob
5
+ * expansion utilities. Glob patterns always use forward slashes by convention —
6
+ * this module treats `/` as the only path separator (never `\`).
7
+ *
8
+ * ### Escape semantics
9
+ * A backslash immediately preceding a metachar (`*`, `?`, `[`) neutralises it.
10
+ * Any other backslash is left as-is. The algorithm scans left-to-right and
11
+ * sets an `escaped` flag when it sees `\`; the *next* character is skipped for
12
+ * magic detection if `escaped` is true.
13
+ */
14
+ import { toForwardSlash } from '../path-utils.js';
15
+ /** The glob metacharacters recognised by this module. */
16
+ const MAGIC_CHARS = new Set(['*', '?', '[']);
17
+ /** Sentinel returned by {@link staticGlobBase} when the first segment is magic. */
18
+ const DOT = '.';
19
+ /**
20
+ * Returns `true` iff `source` contains at least one unescaped glob metachar
21
+ * (`*`, `?`, `[`). A backslash immediately before a metachar escapes it.
22
+ *
23
+ * @example
24
+ * isGlob('a/b/*.mjs') // true — unescaped *
25
+ * isGlob('packs/**\/*') // true — unescaped **
26
+ * isGlob('x?.txt') // true — unescaped ?
27
+ * isGlob('files[1].txt') // true — unescaped [
28
+ * isGlob('foo\\*.txt') // false — * is backslash-escaped
29
+ * isGlob('foo/bar.txt') // false — no metachar
30
+ */
31
+ export function isGlob(source) {
32
+ let escaped = false;
33
+ for (const ch of source) {
34
+ if (escaped) {
35
+ escaped = false;
36
+ continue;
37
+ }
38
+ if (ch === '\\') {
39
+ escaped = true;
40
+ continue;
41
+ }
42
+ if (MAGIC_CHARS.has(ch)) {
43
+ return true;
44
+ }
45
+ }
46
+ return false;
47
+ }
48
+ /**
49
+ * The longest leading path prefix of `pattern` that contains no glob magic —
50
+ * i.e. the static directory base suitable as a `cwd` for a glob runner.
51
+ *
52
+ * Algorithm:
53
+ * 1. Split pattern on `/` (glob patterns always use forward slashes).
54
+ * 2. Accumulate segments until (and **excluding**) the first magic segment.
55
+ * 3. Join accumulated segments with `/`.
56
+ * 4. If the very first segment is magic → return `'.'`.
57
+ * 5. If the joined base is empty but the pattern is absolute (leading `/`,
58
+ * first real segment magic, e.g. `'/*.mjs'`) → return the root `'/'`.
59
+ * 6. If NO segment is magic (not a glob at all) → return the whole pattern.
60
+ *
61
+ * Output always uses forward slashes (pure string ops, no `node:path`).
62
+ *
63
+ * @example
64
+ * staticGlobBase('modules/packs/**\/*') // 'modules/packs'
65
+ * staticGlobBase('a/b/*.mjs') // 'a/b'
66
+ * staticGlobBase('*.mjs') // '.'
67
+ * staticGlobBase('/*.mjs') // '/' (absolute root, not '')
68
+ * staticGlobBase('../mycli/dist/*.mjs') // '../mycli/dist'
69
+ * staticGlobBase('foo/bar.txt') // 'foo/bar.txt'
70
+ */
71
+ export function staticGlobBase(pattern) {
72
+ // Glob patterns are always forward-slash; toForwardSlash() satisfies the
73
+ // no-hardcoded-path-split lint rule while being a no-op in practice.
74
+ const normalized = toForwardSlash(pattern);
75
+ const segments = normalized.split('/');
76
+ const staticSegments = [];
77
+ for (const segment of segments) {
78
+ if (isGlob(segment)) {
79
+ break;
80
+ }
81
+ staticSegments.push(segment);
82
+ }
83
+ if (staticSegments.length === 0) {
84
+ return DOT;
85
+ }
86
+ if (staticSegments.length === segments.length) {
87
+ // No magic found — return the whole (normalized) pattern unchanged.
88
+ return normalized;
89
+ }
90
+ const joined = staticSegments.join('/');
91
+ if (joined === '') {
92
+ // Every static segment was empty — the only static prefix is a leading
93
+ // slash (e.g. '/*.mjs' → segments ['', '*.mjs']). A glob runner handed
94
+ // cwd:'' misbehaves; the correct base is the absolute root '/' for an
95
+ // absolute pattern, '.' otherwise (defensive — relative-first-magic is
96
+ // already handled by the length-0 guard above).
97
+ return normalized.startsWith('/') ? '/' : DOT;
98
+ }
99
+ return joined;
100
+ }
101
+ /**
102
+ * The sub-pattern remaining after stripping the static base, with no leading
103
+ * `/`. This is the pattern to pass to a glob runner with `cwd` set to the base.
104
+ *
105
+ * Only meaningful when `isGlob(pattern)` is true — for a non-glob input the
106
+ * return value is `''` (the whole pattern was consumed as the base).
107
+ *
108
+ * @example
109
+ * globMagicRemainder('modules/packs/**\/*') // '**\/*'
110
+ * globMagicRemainder('a/b/*.mjs') // '*.mjs'
111
+ * globMagicRemainder('*.mjs') // '*.mjs'
112
+ * globMagicRemainder('../mycli/dist/*.mjs') // '*.mjs'
113
+ */
114
+ export function globMagicRemainder(pattern) {
115
+ const forward = toForwardSlash(pattern);
116
+ const base = staticGlobBase(pattern);
117
+ if (base === DOT) {
118
+ // First segment was magic — the remainder is the full (forward-slash) pattern.
119
+ return forward;
120
+ }
121
+ if (base === forward) {
122
+ // No magic at all — degenerate case: remainder is empty.
123
+ return '';
124
+ }
125
+ if (base === '/') {
126
+ // Absolute-root base ('/*.mjs'): the leading slash IS the base, so strip
127
+ // just that one char (there is no separator between base and remainder).
128
+ return forward.slice(1);
129
+ }
130
+ // Strip the base prefix and the trailing '/' separator.
131
+ return forward.slice(base.length + 1);
132
+ }
133
+ //# sourceMappingURL=glob-pattern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob-pattern.js","sourceRoot":"","sources":["../../src/glob/glob-pattern.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,yDAAyD;AACzD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAE7C,mFAAmF;AACnF,MAAM,GAAG,GAAG,GAAG,CAAC;AAEhB;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,MAAM,CAAC,MAAc;IACnC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,KAAK,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACpB,MAAM;QACR,CAAC;QACD,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,cAAc,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC9C,oEAAoE;QACpE,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAClB,uEAAuE;QACvE,uEAAuE;QACvE,sEAAsE;QACtE,uEAAuE;QACvE,gDAAgD;QAChD,OAAO,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAChD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QACjB,+EAA+E;QAC/E,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,yDAAyD;QACzD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QACjB,yEAAyE;QACzE,yEAAyE;QACzE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,wDAAwD;IACxD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACxC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -21,7 +21,10 @@ export * from './skill-targets.js';
21
21
  export { type LinkAuthConfig, type Provider, type ProviderAuth, type ProviderCheck, resolveAuthenticatedUrl, type ResolveOutcome, } from './link-auth/resolve.js';
22
22
  export type { ProviderMatch } from './link-auth/select-provider.js';
23
23
  export type { RewriteRule } from './link-auth/rewrite.js';
24
- export type { TokenSource } from './link-auth/resolve-token.js';
24
+ export { defaultRunCommand, type TokenSource } from './link-auth/resolve-token.js';
25
25
  export { expandMacro, UnknownMacroError } from './link-auth/expand-macro.js';
26
26
  export * from './skill-test/index.js';
27
+ export * from './glob/glob-pattern.js';
28
+ export * from './fs/file-hash.js';
29
+ export * from './yaml/surgical-yaml.js';
27
30
  //# sourceMappingURL=index.d.ts.map
@@ -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,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"}
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,OAAO,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAG7E,cAAc,uBAAuB,CAAC;AAGtC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -36,7 +36,14 @@ export * from './skill-targets.js';
36
36
  // linkAuth pure engine — public API only (issue #113).
37
37
  // Internal helpers (rewrite, build-headers, etc.) stay module-private.
38
38
  export { resolveAuthenticatedUrl, } from './link-auth/resolve.js';
39
+ export { defaultRunCommand } from './link-auth/resolve-token.js';
39
40
  export { expandMacro, UnknownMacroError } from './link-auth/expand-macro.js';
40
41
  // Skill testing utilities (environment management for headless agent runs)
41
42
  export * from './skill-test/index.js';
43
+ // Glob pattern helpers (isGlob, staticGlobBase, globMagicRemainder)
44
+ export * from './glob/glob-pattern.js';
45
+ // Filesystem hashing (sha256 of raw file bytes)
46
+ export * from './fs/file-hash.js';
47
+ // Byte-surgical YAML value updater (replace/insert without reflowing the doc)
48
+ export * from './yaml/surgical-yaml.js';
42
49
  //# 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,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"}
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;AAGhC,OAAO,EAAE,iBAAiB,EAAoB,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAE7E,2EAA2E;AAC3E,cAAc,uBAAuB,CAAC;AAEtC,oEAAoE;AACpE,cAAc,wBAAwB,CAAC;AAEvC,gDAAgD;AAChD,cAAc,mBAAmB,CAAC;AAElC,8EAA8E;AAC9E,cAAc,yBAAyB,CAAC"}
@@ -9,9 +9,13 @@
9
9
  * argv. **Not** passed through a shell — operators (`|`, `&&`, `$(...)`)
10
10
  * become literal argv elements, per the design's §6.1 sharp-edge note.
11
11
  *
12
+ * Command sources can be disabled at runtime with `VAT_LINKAUTH_ALLOW_COMMAND=0`
13
+ * (or by passing `allowCommand: false` in deps). Useful in security-sensitive
14
+ * environments where arbitrary command execution is undesirable.
15
+ *
12
16
  * Returns `undefined` if every source fails or yields an empty/whitespace
13
17
  * value — the caller's `resolveAuthenticatedUrl` translates that to the
14
- * `unverified` outcome (LINK_AUTH_UNVERIFIED in slice 2).
18
+ * `unverified` outcome (surfaced as `LINK_AUTH_UNVERIFIED` by the validator).
15
19
  *
16
20
  * Per design issue #113 §4 (token vocabulary) and §6.1 (command execution,
17
21
  * `safeExecSync`-backed, `shell: false`).
@@ -37,7 +41,35 @@ export interface TokenResolutionDeps {
37
41
  success: boolean;
38
42
  stdout: string;
39
43
  };
44
+ /**
45
+ * Whether `{ command: ... }` sources are allowed. Defaults to
46
+ * `process.env['VAT_LINKAUTH_ALLOW_COMMAND'] !== '0'`. Set to `false` (or
47
+ * export `VAT_LINKAUTH_ALLOW_COMMAND=0`) to skip all command sources and rely
48
+ * solely on env-var sources — useful in locked-down CI or security reviews.
49
+ */
50
+ readonly allowCommand: boolean;
40
51
  }
52
+ /**
53
+ * Return a copy of the given env with all `GIT_*` keys removed. Case-insensitive
54
+ * on the key so Windows env vars (which are case-insensitive at the OS level,
55
+ * though `process.env` preserves original case) can't sneak through as e.g.
56
+ * `Git_Dir`.
57
+ *
58
+ * Exported for unit testing and for callers assembling their own `runCommand`
59
+ * who want the exact same scrub `defaultRunCommand` applies.
60
+ *
61
+ * Rationale: `vat resources validate` is often invoked from git pre-commit
62
+ * hooks, which pre-set `GIT_DIR` / `GIT_WORK_TREE` / `GIT_INDEX_FILE`. These
63
+ * poison any nested tool that shells out to git, notably `gh auth token`.
64
+ */
65
+ export declare function scrubGitEnv(source: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
66
+ /**
67
+ * Default `runCommand` implementation — exported so callers that want to
68
+ * memoize per-validate-run can wrap it without duplicating the spawn logic.
69
+ * Forwards to `safeExecResult` (no shell, argv-based), with `GIT_*` vars
70
+ * stripped from the child env — see {@link scrubGitEnv}.
71
+ */
72
+ export declare const defaultRunCommand: TokenResolutionDeps['runCommand'];
41
73
  /**
42
74
  * Resolve a token from an ordered list of sources.
43
75
  *
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-token.d.ts","sourceRoot":"","sources":["../../src/link-auth/resolve-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAEtG,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAEjD;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CACxF;AAWD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,IAAI,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAClC,MAAM,GAAG,SAAS,CASpB"}
1
+ {"version":3,"file":"resolve-token.d.ts","sourceRoot":"","sources":["../../src/link-auth/resolve-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAEtG,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAEjD;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAEvF;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAIxE;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,mBAAmB,CAAC,YAAY,CAO/D,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,IAAI,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAClC,MAAM,GAAG,SAAS,CAWpB"}
@@ -9,21 +9,47 @@
9
9
  * argv. **Not** passed through a shell — operators (`|`, `&&`, `$(...)`)
10
10
  * become literal argv elements, per the design's §6.1 sharp-edge note.
11
11
  *
12
+ * Command sources can be disabled at runtime with `VAT_LINKAUTH_ALLOW_COMMAND=0`
13
+ * (or by passing `allowCommand: false` in deps). Useful in security-sensitive
14
+ * environments where arbitrary command execution is undesirable.
15
+ *
12
16
  * Returns `undefined` if every source fails or yields an empty/whitespace
13
17
  * value — the caller's `resolveAuthenticatedUrl` translates that to the
14
- * `unverified` outcome (LINK_AUTH_UNVERIFIED in slice 2).
18
+ * `unverified` outcome (surfaced as `LINK_AUTH_UNVERIFIED` by the validator).
15
19
  *
16
20
  * Per design issue #113 §4 (token vocabulary) and §6.1 (command execution,
17
21
  * `safeExecSync`-backed, `shell: false`).
18
22
  */
19
23
  import { safeExecResult } from '../safe-exec.js';
20
- const DEFAULT_RUN_COMMAND = (argv) => {
24
+ /**
25
+ * Return a copy of the given env with all `GIT_*` keys removed. Case-insensitive
26
+ * on the key so Windows env vars (which are case-insensitive at the OS level,
27
+ * though `process.env` preserves original case) can't sneak through as e.g.
28
+ * `Git_Dir`.
29
+ *
30
+ * Exported for unit testing and for callers assembling their own `runCommand`
31
+ * who want the exact same scrub `defaultRunCommand` applies.
32
+ *
33
+ * Rationale: `vat resources validate` is often invoked from git pre-commit
34
+ * hooks, which pre-set `GIT_DIR` / `GIT_WORK_TREE` / `GIT_INDEX_FILE`. These
35
+ * poison any nested tool that shells out to git, notably `gh auth token`.
36
+ */
37
+ export function scrubGitEnv(source) {
38
+ return Object.fromEntries(Object.entries(source).filter(([k]) => !k.toUpperCase().startsWith('GIT_')));
39
+ }
40
+ /**
41
+ * Default `runCommand` implementation — exported so callers that want to
42
+ * memoize per-validate-run can wrap it without duplicating the spawn logic.
43
+ * Forwards to `safeExecResult` (no shell, argv-based), with `GIT_*` vars
44
+ * stripped from the child env — see {@link scrubGitEnv}.
45
+ */
46
+ export const defaultRunCommand = (argv) => {
21
47
  if (argv.length === 0)
22
48
  return { success: false, stdout: '' };
23
49
  const [bin, ...args] = argv;
24
50
  if (bin === undefined)
25
51
  return { success: false, stdout: '' };
26
- const result = safeExecResult(bin, [...args], { encoding: 'utf8' });
52
+ const result = safeExecResult(bin, [...args], { encoding: 'utf8', env: scrubGitEnv(process.env) });
27
53
  const stdout = typeof result.stdout === 'string' ? result.stdout : result.stdout.toString('utf8');
28
54
  return { success: result.success, stdout };
29
55
  };
@@ -36,8 +62,11 @@ const DEFAULT_RUN_COMMAND = (argv) => {
36
62
  */
37
63
  export function resolveToken(sources, deps) {
38
64
  const env = deps?.env ?? process.env;
39
- const runCommand = deps?.runCommand ?? DEFAULT_RUN_COMMAND;
65
+ const runCommand = deps?.runCommand ?? defaultRunCommand;
66
+ const allowCommand = deps?.allowCommand ?? (process.env['VAT_LINKAUTH_ALLOW_COMMAND'] !== '0');
40
67
  for (const source of sources) {
68
+ if (!allowCommand && 'command' in source)
69
+ continue;
41
70
  const value = tryResolveSource(source, env, runCommand);
42
71
  if (value !== undefined && value.length > 0)
43
72
  return value;
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-token.js","sourceRoot":"","sources":["../../src/link-auth/resolve-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAoBjD,MAAM,mBAAmB,GAAsC,CAAC,IAAI,EAAE,EAAE;IACtE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC7D,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC7D,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClG,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7C,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,OAA+B,EAC/B,IAAmC;IAEnC,MAAM,GAAG,GAAG,IAAI,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,EAAE,UAAU,IAAI,mBAAmB,CAAC;IAE3D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QACxD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAC5D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,gBAAgB,CACvB,MAAmB,EACnB,GAAuC,EACvC,UAA6C;IAE7C,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;QACpB,qEAAqE;QACrE,4CAA4C;QAC5C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QACtD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAExC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACtC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACrC,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;AACpD,CAAC;AAED,SAAS,MAAM,CAAC,OAAmC;IACjD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3C,6EAA6E;IAC7E,2EAA2E;IAC3E,2BAA2B;IAC3B,OAAQ,OAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"resolve-token.js","sourceRoot":"","sources":["../../src/link-auth/resolve-token.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,19 @@ import { type ProviderMatch } from './select-provider.js';
24
24
  export interface ProviderAuth {
25
25
  readonly headers: Record<string, string>;
26
26
  }
27
+ /**
28
+ * Optional content-fetch header overrides (design issue #113 §6.2).
29
+ *
30
+ * Health-check and content retrieval often need different `Accept` (or other)
31
+ * headers. The canonical example: GitHub's `application/vnd.github+json`
32
+ * returns 200 for any size but omits bytes >1 MiB, while
33
+ * `application/vnd.github.raw` streams the bytes inline. The provider declares
34
+ * `auth.headers` for health-check and an optional `fetch.headers` for content
35
+ * retrieval. Both are templated against the same context (URL captures + token).
36
+ */
37
+ export interface ProviderFetch {
38
+ readonly headers: Record<string, string>;
39
+ }
27
40
  export interface ProviderCheck {
28
41
  readonly method: 'GET' | 'HEAD';
29
42
  readonly aliveStatus: readonly number[];
@@ -33,15 +46,46 @@ export interface Provider {
33
46
  readonly match: ProviderMatch;
34
47
  readonly rewrite: readonly RewriteRule[];
35
48
  readonly auth: ProviderAuth;
49
+ /**
50
+ * Optional — present when a provider needs different headers for content
51
+ * retrieval than for health-check. Absent for hosts where one header set
52
+ * does both jobs.
53
+ */
54
+ readonly fetch?: ProviderFetch;
36
55
  readonly token: readonly TokenSource[];
37
56
  readonly check: ProviderCheck;
38
57
  }
39
58
  export interface LinkAuthConfig {
40
59
  readonly providers: readonly Provider[];
60
+ /**
61
+ * Optional content-cache config (consumed by the slice-3 content-fetch
62
+ * primitive, not by the engine itself). The engine stays stateless; this
63
+ * field rides along on the config object so the primitive doesn't need a
64
+ * second source of truth.
65
+ */
66
+ readonly cache?: {
67
+ readonly ttlMinutes?: number;
68
+ };
41
69
  }
42
70
  export type ResolveOutcome = {
43
71
  readonly fetchUrl: string;
44
72
  readonly headers: Record<string, string>;
73
+ /**
74
+ * Expanded fetch-mode headers, only present when the provider declared
75
+ * a `fetch` block. Templated against the same context as `headers`
76
+ * (URL captures + resolved token), so callers do not need to re-resolve
77
+ * the token to send these. Per §6.2 — content-fetch consumers send
78
+ * these instead of (or merged over) `headers` for the request body.
79
+ */
80
+ readonly fetchHeaders?: Record<string, string>;
81
+ /**
82
+ * The matched provider's `check` block, passed through so the post-fetch
83
+ * classifier (in `packages/resources`) can route status codes to outcomes
84
+ * without re-running `selectProvider`. Reading this from the engine —
85
+ * rather than asking the validator to re-derive it — keeps the
86
+ * provider-match decision in exactly one place.
87
+ */
88
+ readonly check: ProviderCheck;
45
89
  } | {
46
90
  readonly outcome: 'unsupported';
47
91
  } | {
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/link-auth/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,sBAAsB,CAAC;AAE1E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,WAAW,GAAG,MAAM,CAAC;CAChD;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;CAGzC;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACvE;IAAE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;CAAE,GACnC;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAClC,cAAc,CAyBhB"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/link-auth/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,sBAAsB,CAAC;AAE1E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,WAAW,GAAG,MAAM,CAAC;CAChD;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE;QACf,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;CACH;AAED,MAAM,MAAM,cAAc,GACtB;IACE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;CAC/B,GACD;IAAE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA;CAAE,GACnC;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,EACtB,IAAI,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAClC,cAAc,CAqChB"}
@@ -50,6 +50,17 @@ export function resolveAuthenticatedUrl(url, config, deps) {
50
50
  Object.assign(headerContext, rewrite.context);
51
51
  headerContext['token'] = token;
52
52
  const headers = buildHeaders(provider.auth.headers, headerContext);
53
- return { fetchUrl: rewrite.rewrittenUrl, headers };
53
+ // Expand fetch.headers against the same context so the resolved token wins
54
+ // over any URL-captured "token" group here too — the precedence discipline
55
+ // applies to both header sets, not just auth.headers.
56
+ const fetchHeaders = provider.fetch === undefined
57
+ ? undefined
58
+ : buildHeaders(provider.fetch.headers, headerContext);
59
+ return {
60
+ fetchUrl: rewrite.rewrittenUrl,
61
+ headers,
62
+ ...(fetchHeaders === undefined ? {} : { fetchHeaders }),
63
+ check: provider.check,
64
+ };
54
65
  }
55
66
  //# sourceMappingURL=resolve.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/link-auth/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,YAAY,GAGb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAoB,UAAU,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAsB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA+B1E;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,GAAW,EACX,MAAsB,EACtB,IAAmC;IAEnC,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAE9D,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAExD,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjD,wHAAwH;IACxH,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,YAAY;YACrB,MAAM,EAAE,2EAA2E;SACpF,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;IACpE,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,aAAa,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;IAE/B,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACnE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/link-auth/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,YAAY,GAGb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAoB,UAAU,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAsB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA6E1E;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,GAAW,EACX,MAAsB,EACtB,IAAmC;IAEnC,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IACvD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAE9D,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;IAExD,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjD,wHAAwH;IACxH,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,YAAY;YACrB,MAAM,EAAE,2EAA2E;SACpF,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;IACpE,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9C,aAAa,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;IAE/B,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACnE,2EAA2E;IAC3E,2EAA2E;IAC3E,sDAAsD;IACtD,MAAM,YAAY,GAChB,QAAQ,CAAC,KAAK,KAAK,SAAS;QAC1B,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAC1D,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,YAAY;QAC9B,OAAO;QACP,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;QACvD,KAAK,EAAE,QAAQ,CAAC,KAAK;KACtB,CAAC;AACJ,CAAC"}
@@ -106,6 +106,36 @@ export declare function mkdirSyncReal(dirPath: string, options?: Parameters<type
106
106
  * isAbsolutePath('C:/Windows') // true (Windows)
107
107
  */
108
108
  export declare function isAbsolutePath(p: string): boolean;
109
+ /**
110
+ * True if `p` is absolute on ANY platform — a POSIX root path (`/etc`), a
111
+ * Windows drive-letter path (`C:\…` or `C:/…`), or a UNC path (`\\host\share`).
112
+ *
113
+ * Unlike {@link isAbsolutePath} (host-platform only), this is host-independent,
114
+ * so config-containment checks reject Windows-absolute paths even when run on
115
+ * POSIX CI, and vice versa. Used to keep config-supplied relative paths (skill
116
+ * `files:` dest) from escaping their anchor directory (zip-slip class).
117
+ *
118
+ * @example
119
+ * isAbsoluteAnyPlatform('/etc/passwd') // true (POSIX)
120
+ * isAbsoluteAnyPlatform('C:\\Users') // true (Windows drive)
121
+ * isAbsoluteAnyPlatform('scripts/cli') // false (relative)
122
+ */
123
+ export declare function isAbsoluteAnyPlatform(p: string): boolean;
124
+ /**
125
+ * True if `p` contains a `..` parent-directory traversal segment.
126
+ *
127
+ * Forward-slash-normalized, then inspects each `/`-delimited segment — so a
128
+ * `..` is caught regardless of the original OS separator. A containment guard
129
+ * for config-supplied relative paths (skill `files:` dest values, glob magic
130
+ * remainders) that must never climb above their anchor directory.
131
+ *
132
+ * @example
133
+ * hasParentTraversalSegment('a/../b') // true
134
+ * hasParentTraversalSegment('a/b/c') // false
135
+ * hasParentTraversalSegment('..\\evil') // true (backslash normalized)
136
+ * hasParentTraversalSegment('a..b/c') // false (".." must be a whole segment)
137
+ */
138
+ export declare function hasParentTraversalSegment(p: string): boolean;
109
139
  /**
110
140
  * Convert a relative path to absolute
111
141
  *
@@ -1 +1 @@
1
- {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../src/path-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,MAAM,SAAS,CAAC;AAKlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,aAAa,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CA+BxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAezC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,GACxC,MAAM,CAiBR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAMhE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,QAAQ;IACnB,6DAA6D;8BAC9C,MAAM,EAAE,KAAG,MAAM;IAIhC,gEAAgE;iCAC9C,MAAM,EAAE,KAAG,MAAM;IAInC,iEAAiE;8BAClD,MAAM,MAAM,MAAM,KAAG,MAAM;IAI1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;mCACiB,MAAM,eAAe,MAAM,EAAE,KAAG,MAAM;CAuClD,CAAC;AAEX;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAQ1F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAGhF"}
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../src/path-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,MAAM,SAAS,CAAC;AAKlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,aAAa,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CA+BxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAezC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,GACxC,MAAM,CAiBR;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAMhE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,QAAQ;IACnB,6DAA6D;8BAC9C,MAAM,EAAE,KAAG,MAAM;IAIhC,gEAAgE;iCAC9C,MAAM,EAAE,KAAG,MAAM;IAInC,iEAAiE;8BAClD,MAAM,MAAM,MAAM,KAAG,MAAM;IAI1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;mCACiB,MAAM,eAAe,MAAM,EAAE,KAAG,MAAM;CAuClD,CAAC;AAEX;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAQ1F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAGhF"}
@@ -179,6 +179,40 @@ export function mkdirSyncReal(dirPath, options) {
179
179
  export function isAbsolutePath(p) {
180
180
  return path.isAbsolute(p);
181
181
  }
182
+ /**
183
+ * True if `p` is absolute on ANY platform — a POSIX root path (`/etc`), a
184
+ * Windows drive-letter path (`C:\…` or `C:/…`), or a UNC path (`\\host\share`).
185
+ *
186
+ * Unlike {@link isAbsolutePath} (host-platform only), this is host-independent,
187
+ * so config-containment checks reject Windows-absolute paths even when run on
188
+ * POSIX CI, and vice versa. Used to keep config-supplied relative paths (skill
189
+ * `files:` dest) from escaping their anchor directory (zip-slip class).
190
+ *
191
+ * @example
192
+ * isAbsoluteAnyPlatform('/etc/passwd') // true (POSIX)
193
+ * isAbsoluteAnyPlatform('C:\\Users') // true (Windows drive)
194
+ * isAbsoluteAnyPlatform('scripts/cli') // false (relative)
195
+ */
196
+ export function isAbsoluteAnyPlatform(p) {
197
+ return path.posix.isAbsolute(p) || path.win32.isAbsolute(p);
198
+ }
199
+ /**
200
+ * True if `p` contains a `..` parent-directory traversal segment.
201
+ *
202
+ * Forward-slash-normalized, then inspects each `/`-delimited segment — so a
203
+ * `..` is caught regardless of the original OS separator. A containment guard
204
+ * for config-supplied relative paths (skill `files:` dest values, glob magic
205
+ * remainders) that must never climb above their anchor directory.
206
+ *
207
+ * @example
208
+ * hasParentTraversalSegment('a/../b') // true
209
+ * hasParentTraversalSegment('a/b/c') // false
210
+ * hasParentTraversalSegment('..\\evil') // true (backslash normalized)
211
+ * hasParentTraversalSegment('a..b/c') // false (".." must be a whole segment)
212
+ */
213
+ export function hasParentTraversalSegment(p) {
214
+ return toForwardSlash(p).split('/').includes('..');
215
+ }
182
216
  /**
183
217
  * Convert a relative path to absolute
184
218
  *