@vibe-agent-toolkit/utils 0.1.38 → 0.1.39-rc.10
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 +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/link-auth/build-headers.d.ts +34 -0
- package/dist/link-auth/build-headers.d.ts.map +1 -0
- package/dist/link-auth/build-headers.js +58 -0
- package/dist/link-auth/build-headers.js.map +1 -0
- package/dist/link-auth/expand-macro.d.ts +38 -0
- package/dist/link-auth/expand-macro.d.ts.map +1 -0
- package/dist/link-auth/expand-macro.js +131 -0
- package/dist/link-auth/expand-macro.js.map +1 -0
- package/dist/link-auth/macros.yaml +50 -0
- package/dist/link-auth/resolve-token.d.ts +49 -0
- package/dist/link-auth/resolve-token.d.ts.map +1 -0
- package/dist/link-auth/resolve-token.js +72 -0
- package/dist/link-auth/resolve-token.js.map +1 -0
- package/dist/link-auth/resolve.d.ts +58 -0
- package/dist/link-auth/resolve.d.ts.map +1 -0
- package/dist/link-auth/resolve.js +55 -0
- package/dist/link-auth/resolve.js.map +1 -0
- package/dist/link-auth/rewrite.d.ts +52 -0
- package/dist/link-auth/rewrite.d.ts.map +1 -0
- package/dist/link-auth/rewrite.js +102 -0
- package/dist/link-auth/rewrite.js.map +1 -0
- package/dist/link-auth/select-provider.d.ts +30 -0
- package/dist/link-auth/select-provider.d.ts.map +1 -0
- package/dist/link-auth/select-provider.js +55 -0
- package/dist/link-auth/select-provider.js.map +1 -0
- package/dist/link-auth/template.d.ts +40 -0
- package/dist/link-auth/template.d.ts.map +1 -0
- package/dist/link-auth/template.js +89 -0
- package/dist/link-auth/template.js.map +1 -0
- package/dist/link-auth/transforms.d.ts +46 -0
- package/dist/link-auth/transforms.d.ts.map +1 -0
- package/dist/link-auth/transforms.js +52 -0
- package/dist/link-auth/transforms.js.map +1 -0
- package/dist/path-utils.d.ts +35 -3
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +63 -3
- package/dist/path-utils.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 +61 -0
- package/dist/skill-test/env-scrub.d.ts.map +1 -0
- package/dist/skill-test/env-scrub.js +112 -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 +4 -3
|
@@ -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":"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"}
|
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';
|
|
@@ -17,4 +18,10 @@ export * from './test-helpers.js';
|
|
|
17
18
|
export * from './zod-introspection.js';
|
|
18
19
|
export * from './template.js';
|
|
19
20
|
export * from './skill-targets.js';
|
|
21
|
+
export { type LinkAuthConfig, type Provider, type ProviderAuth, type ProviderCheck, resolveAuthenticatedUrl, type ResolveOutcome, } from './link-auth/resolve.js';
|
|
22
|
+
export type { ProviderMatch } from './link-auth/select-provider.js';
|
|
23
|
+
export type { RewriteRule } from './link-auth/rewrite.js';
|
|
24
|
+
export type { TokenSource } from './link-auth/resolve-token.js';
|
|
25
|
+
export { expandMacro, UnknownMacroError } from './link-auth/expand-macro.js';
|
|
26
|
+
export * from './skill-test/index.js';
|
|
20
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"}
|
|
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).
|
|
@@ -31,4 +33,10 @@ export * from './zod-introspection.js';
|
|
|
31
33
|
export * from './template.js';
|
|
32
34
|
// Skill target resolution (cross-platform flat skill install paths)
|
|
33
35
|
export * from './skill-targets.js';
|
|
36
|
+
// linkAuth pure engine — public API only (issue #113).
|
|
37
|
+
// Internal helpers (rewrite, build-headers, etc.) stay module-private.
|
|
38
|
+
export { resolveAuthenticatedUrl, } from './link-auth/resolve.js';
|
|
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';
|
|
34
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"}
|
|
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
|
+
/**
|
|
2
|
+
* Render auth-header templates with rendered values, and a structural
|
|
3
|
+
* redaction helper for serialization.
|
|
4
|
+
*
|
|
5
|
+
* `buildHeaders` renders each header value template against a context that
|
|
6
|
+
* carries `${token}` plus any named captures / vars from the rewrite step.
|
|
7
|
+
* `redactHeaders` masks `Authorization` values for any caller that needs to
|
|
8
|
+
* serialize headers into logs, errors, or cache entries — the design's §8
|
|
9
|
+
* "tokens never leak" claim depends on every such site routing through this.
|
|
10
|
+
*
|
|
11
|
+
* Per design issue #113 §4 (auth.headers vocabulary) and §8 (redaction is
|
|
12
|
+
* structural; Authorization values never appear in serialized output).
|
|
13
|
+
*/
|
|
14
|
+
export declare const REDACTED_VALUE = "<redacted>";
|
|
15
|
+
/**
|
|
16
|
+
* Render a map of header templates into a map of concrete header values.
|
|
17
|
+
*
|
|
18
|
+
* @throws {TemplateMissingVarError} if a header template references an
|
|
19
|
+
* unknown context key
|
|
20
|
+
* @throws {TemplateSyntaxError} from a malformed template expression
|
|
21
|
+
* @throws {UnknownTransformError} from a template calling an unknown transform
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildHeaders(templates: Record<string, string>, context: Record<string, string>): Record<string, string>;
|
|
24
|
+
/**
|
|
25
|
+
* Return a copy of `headers` with sensitive values replaced by `REDACTED_VALUE`.
|
|
26
|
+
* Header-name matching is case-insensitive but exact (no prefix matching) — a
|
|
27
|
+
* header like `X-Authorization-Foo` is NOT considered sensitive.
|
|
28
|
+
*
|
|
29
|
+
* **Input must be a plain key-value object.** A `Headers` instance (Web Fetch
|
|
30
|
+
* API) or a `Map` yields `[]` from `Object.entries` and would silently no-op
|
|
31
|
+
* redaction — converting to a plain object is the caller's responsibility.
|
|
32
|
+
*/
|
|
33
|
+
export declare function redactHeaders(headers: Record<string, string>): Record<string, string>;
|
|
34
|
+
//# sourceMappingURL=build-headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-headers.d.ts","sourceRoot":"","sources":["../../src/link-auth/build-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,eAAO,MAAM,cAAc,eAAe,CAAC;AAc3C;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMrF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render auth-header templates with rendered values, and a structural
|
|
3
|
+
* redaction helper for serialization.
|
|
4
|
+
*
|
|
5
|
+
* `buildHeaders` renders each header value template against a context that
|
|
6
|
+
* carries `${token}` plus any named captures / vars from the rewrite step.
|
|
7
|
+
* `redactHeaders` masks `Authorization` values for any caller that needs to
|
|
8
|
+
* serialize headers into logs, errors, or cache entries — the design's §8
|
|
9
|
+
* "tokens never leak" claim depends on every such site routing through this.
|
|
10
|
+
*
|
|
11
|
+
* Per design issue #113 §4 (auth.headers vocabulary) and §8 (redaction is
|
|
12
|
+
* structural; Authorization values never appear in serialized output).
|
|
13
|
+
*/
|
|
14
|
+
import { renderTemplate } from './template.js';
|
|
15
|
+
export const REDACTED_VALUE = '<redacted>';
|
|
16
|
+
/**
|
|
17
|
+
* Header names whose values must be masked when serialized.
|
|
18
|
+
*
|
|
19
|
+
* v1 ships only `authorization` because that is the only secret-bearing
|
|
20
|
+
* header the current macros emit. **Omission is the security risk** — any
|
|
21
|
+
* future macro that emits a header carrying a secret (`Cookie`,
|
|
22
|
+
* `Proxy-Authorization`, `X-API-Key`, custom bearer-style headers) must add
|
|
23
|
+
* that name here, or the token will silently leak through serialization.
|
|
24
|
+
* Extending the set is not the dangerous edit; forgetting to extend it is.
|
|
25
|
+
*/
|
|
26
|
+
const SENSITIVE_HEADER_NAMES = new Set(['authorization']);
|
|
27
|
+
/**
|
|
28
|
+
* Render a map of header templates into a map of concrete header values.
|
|
29
|
+
*
|
|
30
|
+
* @throws {TemplateMissingVarError} if a header template references an
|
|
31
|
+
* unknown context key
|
|
32
|
+
* @throws {TemplateSyntaxError} from a malformed template expression
|
|
33
|
+
* @throws {UnknownTransformError} from a template calling an unknown transform
|
|
34
|
+
*/
|
|
35
|
+
export function buildHeaders(templates, context) {
|
|
36
|
+
const headers = Object.create(null);
|
|
37
|
+
for (const [name, template] of Object.entries(templates)) {
|
|
38
|
+
headers[name] = renderTemplate(template, context);
|
|
39
|
+
}
|
|
40
|
+
return headers;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Return a copy of `headers` with sensitive values replaced by `REDACTED_VALUE`.
|
|
44
|
+
* Header-name matching is case-insensitive but exact (no prefix matching) — a
|
|
45
|
+
* header like `X-Authorization-Foo` is NOT considered sensitive.
|
|
46
|
+
*
|
|
47
|
+
* **Input must be a plain key-value object.** A `Headers` instance (Web Fetch
|
|
48
|
+
* API) or a `Map` yields `[]` from `Object.entries` and would silently no-op
|
|
49
|
+
* redaction — converting to a plain object is the caller's responsibility.
|
|
50
|
+
*/
|
|
51
|
+
export function redactHeaders(headers) {
|
|
52
|
+
const redacted = Object.create(null);
|
|
53
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
54
|
+
redacted[name] = SENSITIVE_HEADER_NAMES.has(name.toLowerCase()) ? REDACTED_VALUE : value;
|
|
55
|
+
}
|
|
56
|
+
return redacted;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=build-headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-headers.js","sourceRoot":"","sources":["../../src/link-auth/build-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC;AAE3C;;;;;;;;;GASG;AACH,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;AAE/E;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAiC,EACjC,OAA+B;IAE/B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;IAC9D,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,OAAO,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,OAA+B;IAC3D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;IAC/D,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC;IAC3F,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Macro loader + expander.
|
|
3
|
+
*
|
|
4
|
+
* Loads the bundled `macros.yaml` once at module init and exposes
|
|
5
|
+
* `expandMacro(name, overrides?)` which deep-merges the named macro with any
|
|
6
|
+
* adopter overrides. The merge is "adopter wins": objects merge recursively,
|
|
7
|
+
* arrays and primitives are replaced wholesale (no element-wise array merge).
|
|
8
|
+
*
|
|
9
|
+
* The shipped macro file is at `link-auth/macros.yaml`, copied into the dist
|
|
10
|
+
* tree by `packages/dev-tools/src/copy-yaml-assets.ts` during build so the
|
|
11
|
+
* runtime `fs.readFileSync(new URL('./macros.yaml', import.meta.url))`
|
|
12
|
+
* resolves in both source-mode (vitest) and built-mode (dist).
|
|
13
|
+
*
|
|
14
|
+
* Per design issue #113 §5 (macros are config, not a privileged code path).
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Thrown when a `use: <name>` references a macro not in the shipped set.
|
|
18
|
+
* Message lists the available macros so a typo surfaces clearly.
|
|
19
|
+
*/
|
|
20
|
+
export declare class UnknownMacroError extends Error {
|
|
21
|
+
constructor(name: string, available: readonly string[]);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Look up a macro by name and deep-merge optional adopter overrides on top.
|
|
25
|
+
*
|
|
26
|
+
* Merge semantics:
|
|
27
|
+
* - Plain objects merge recursively (sibling keys preserved).
|
|
28
|
+
* - Arrays are replaced wholesale (override's array wins; no concat).
|
|
29
|
+
* - Primitives are replaced.
|
|
30
|
+
* - `undefined` in an override is treated as "not provided" (base wins).
|
|
31
|
+
*
|
|
32
|
+
* The returned object and all nested plain objects use null prototypes so
|
|
33
|
+
* `__proto__` / `constructor` keys can never poison consumers.
|
|
34
|
+
*
|
|
35
|
+
* @throws {UnknownMacroError} if `name` is not in the shipped macro set
|
|
36
|
+
*/
|
|
37
|
+
export declare function expandMacro(name: string, overrides?: Record<string, unknown>): Record<string, unknown>;
|
|
38
|
+
//# sourceMappingURL=expand-macro.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expand-macro.d.ts","sourceRoot":"","sources":["../../src/link-auth/expand-macro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAwCH;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE;CAIvD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Macro loader + expander.
|
|
3
|
+
*
|
|
4
|
+
* Loads the bundled `macros.yaml` once at module init and exposes
|
|
5
|
+
* `expandMacro(name, overrides?)` which deep-merges the named macro with any
|
|
6
|
+
* adopter overrides. The merge is "adopter wins": objects merge recursively,
|
|
7
|
+
* arrays and primitives are replaced wholesale (no element-wise array merge).
|
|
8
|
+
*
|
|
9
|
+
* The shipped macro file is at `link-auth/macros.yaml`, copied into the dist
|
|
10
|
+
* tree by `packages/dev-tools/src/copy-yaml-assets.ts` during build so the
|
|
11
|
+
* runtime `fs.readFileSync(new URL('./macros.yaml', import.meta.url))`
|
|
12
|
+
* resolves in both source-mode (vitest) and built-mode (dist).
|
|
13
|
+
*
|
|
14
|
+
* Per design issue #113 §5 (macros are config, not a privileged code path).
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync } from 'node:fs';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { parse as parseYaml } from 'yaml';
|
|
19
|
+
const macrosPath = fileURLToPath(new URL('./macros.yaml', import.meta.url));
|
|
20
|
+
let macrosCache;
|
|
21
|
+
/**
|
|
22
|
+
* Load macros lazily on first use rather than at module init.
|
|
23
|
+
*
|
|
24
|
+
* Why lazy: vitest tests elsewhere in the repo mock `node:fs` (replacing
|
|
25
|
+
* `readFileSync` with `vi.fn()` that returns `undefined`). If we eagerly read
|
|
26
|
+
* at module load, any unrelated test that imports `@vibe-agent-toolkit/utils`
|
|
27
|
+
* and mocks fs crashes inside `parseYaml(undefined)` — module-init code runs
|
|
28
|
+
* unconditionally, before the mock-setup intent reaches our file.
|
|
29
|
+
*
|
|
30
|
+
* Lazy load makes module import side-effect-free; only callers of
|
|
31
|
+
* `expandMacro` pay the fs cost.
|
|
32
|
+
*/
|
|
33
|
+
function getMacros() {
|
|
34
|
+
if (macrosCache !== undefined)
|
|
35
|
+
return macrosCache;
|
|
36
|
+
// Path is derived from `import.meta.url`, not user input — points at the
|
|
37
|
+
// shipped macros.yaml asset next to this module in both src and dist trees.
|
|
38
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
39
|
+
const macrosFileContent = readFileSync(macrosPath, 'utf8');
|
|
40
|
+
const parsed = parseYaml(macrosFileContent);
|
|
41
|
+
if (!isPlainObject(parsed)) {
|
|
42
|
+
throw new Error('macros.yaml: expected top-level object mapping macro name → provider config.');
|
|
43
|
+
}
|
|
44
|
+
macrosCache = freezeMacros(parsed);
|
|
45
|
+
return macrosCache;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Thrown when a `use: <name>` references a macro not in the shipped set.
|
|
49
|
+
* Message lists the available macros so a typo surfaces clearly.
|
|
50
|
+
*/
|
|
51
|
+
export class UnknownMacroError extends Error {
|
|
52
|
+
constructor(name, available) {
|
|
53
|
+
super(`Unknown macro "${name}". Available: ${available.join(', ')}.`);
|
|
54
|
+
this.name = 'UnknownMacroError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Look up a macro by name and deep-merge optional adopter overrides on top.
|
|
59
|
+
*
|
|
60
|
+
* Merge semantics:
|
|
61
|
+
* - Plain objects merge recursively (sibling keys preserved).
|
|
62
|
+
* - Arrays are replaced wholesale (override's array wins; no concat).
|
|
63
|
+
* - Primitives are replaced.
|
|
64
|
+
* - `undefined` in an override is treated as "not provided" (base wins).
|
|
65
|
+
*
|
|
66
|
+
* The returned object and all nested plain objects use null prototypes so
|
|
67
|
+
* `__proto__` / `constructor` keys can never poison consumers.
|
|
68
|
+
*
|
|
69
|
+
* @throws {UnknownMacroError} if `name` is not in the shipped macro set
|
|
70
|
+
*/
|
|
71
|
+
export function expandMacro(name, overrides) {
|
|
72
|
+
const macros = getMacros();
|
|
73
|
+
const base = macros[name];
|
|
74
|
+
if (base === undefined) {
|
|
75
|
+
throw new UnknownMacroError(name, Object.keys(macros));
|
|
76
|
+
}
|
|
77
|
+
if (overrides === undefined) {
|
|
78
|
+
return cloneWithNullProto(base);
|
|
79
|
+
}
|
|
80
|
+
const merged = deepMerge(base, overrides);
|
|
81
|
+
// Top-level result is guaranteed object here because base is.
|
|
82
|
+
return merged;
|
|
83
|
+
}
|
|
84
|
+
function deepMerge(base, override) {
|
|
85
|
+
if (override === undefined)
|
|
86
|
+
return base;
|
|
87
|
+
if (!isPlainObject(base) || !isPlainObject(override))
|
|
88
|
+
return cloneValue(override);
|
|
89
|
+
const result = Object.create(null);
|
|
90
|
+
for (const [key, value] of Object.entries(base)) {
|
|
91
|
+
result[key] = cloneValue(value);
|
|
92
|
+
}
|
|
93
|
+
for (const [key, value] of Object.entries(override)) {
|
|
94
|
+
const existing = Object.hasOwn(result, key) ? result[key] : undefined;
|
|
95
|
+
if (isPlainObject(existing) && isPlainObject(value)) {
|
|
96
|
+
result[key] = deepMerge(existing, value);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
result[key] = cloneValue(value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
function cloneValue(value) {
|
|
105
|
+
if (Array.isArray(value))
|
|
106
|
+
return value.map(cloneValue);
|
|
107
|
+
if (isPlainObject(value))
|
|
108
|
+
return cloneWithNullProto(value);
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
function cloneWithNullProto(obj) {
|
|
112
|
+
const cloned = Object.create(null);
|
|
113
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
114
|
+
cloned[key] = cloneValue(value);
|
|
115
|
+
}
|
|
116
|
+
return cloned;
|
|
117
|
+
}
|
|
118
|
+
function freezeMacros(parsed) {
|
|
119
|
+
const frozen = Object.create(null);
|
|
120
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
121
|
+
if (!isPlainObject(value)) {
|
|
122
|
+
throw new Error(`macros.yaml: macro "${name}" is not an object.`);
|
|
123
|
+
}
|
|
124
|
+
frozen[name] = cloneWithNullProto(value);
|
|
125
|
+
}
|
|
126
|
+
return frozen;
|
|
127
|
+
}
|
|
128
|
+
function isPlainObject(v) {
|
|
129
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=expand-macro.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expand-macro.js","sourceRoot":"","sources":["../../src/link-auth/expand-macro.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# linkAuth provider macros — shipped defaults referenced by `use: <name>` in
|
|
2
|
+
# adopter `vibe-agent-toolkit.config.yaml` files.
|
|
3
|
+
#
|
|
4
|
+
# Each entry is a full provider config in the §4 vocabulary (match, rewrite,
|
|
5
|
+
# auth, token, check). Adopters reference a macro by name and may deep-merge
|
|
6
|
+
# overrides on top — see design issue #113 §5.
|
|
7
|
+
#
|
|
8
|
+
# Adding a new host is intended to be a config-only PR: append an entry here
|
|
9
|
+
# and add unit tests. Only `vocabulary` changes (e.g. a new transform) require
|
|
10
|
+
# engine code.
|
|
11
|
+
|
|
12
|
+
github:
|
|
13
|
+
match:
|
|
14
|
+
host: github.com
|
|
15
|
+
rewrite:
|
|
16
|
+
- when: '^https://github\.com/(?<owner>[^/]+)/(?<repo>[^/]+)/(?:blob|tree)/(?<ref>[^/]+)/(?<path>.+)$'
|
|
17
|
+
to: 'https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${ref}'
|
|
18
|
+
auth:
|
|
19
|
+
headers:
|
|
20
|
+
Authorization: 'Bearer ${token}'
|
|
21
|
+
Accept: application/vnd.github+json
|
|
22
|
+
token:
|
|
23
|
+
- command: gh auth token
|
|
24
|
+
- env: GITHUB_TOKEN
|
|
25
|
+
check:
|
|
26
|
+
method: GET
|
|
27
|
+
aliveStatus: [200]
|
|
28
|
+
notFoundMeaning: ambiguous
|
|
29
|
+
|
|
30
|
+
sharepoint:
|
|
31
|
+
match:
|
|
32
|
+
host: '*.sharepoint.com'
|
|
33
|
+
excludeHost:
|
|
34
|
+
- '*-my.sharepoint.com'
|
|
35
|
+
rewrite:
|
|
36
|
+
- when: '^(?<u>https://.+)$'
|
|
37
|
+
vars:
|
|
38
|
+
shareId: 'u!${base64url(u)}'
|
|
39
|
+
to: 'https://graph.microsoft.com/v1.0/shares/${shareId}/driveItem'
|
|
40
|
+
auth:
|
|
41
|
+
headers:
|
|
42
|
+
Authorization: 'Bearer ${token}'
|
|
43
|
+
# SharePoint has no zero-config token source (design §5.1 note): a SharePoint-
|
|
44
|
+
# scoped token requires an Entra app registration; the stock `az` token does
|
|
45
|
+
# not work. Adopter must supply their own token command.
|
|
46
|
+
token: []
|
|
47
|
+
check:
|
|
48
|
+
method: GET
|
|
49
|
+
aliveStatus: [200]
|
|
50
|
+
notFoundMeaning: dead
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token resolution — ordered, first-non-empty-wins.
|
|
3
|
+
*
|
|
4
|
+
* Iterates a provider's `token` source list and returns the first source that
|
|
5
|
+
* yields a non-empty value. Two source shapes:
|
|
6
|
+
* - `{ env: "NAME" }` — read from process env (no trimming)
|
|
7
|
+
* - `{ command: argv }` — run a command, return trimmed stdout
|
|
8
|
+
* - `{ command: "gh auth token" }` — convenience: whitespace-tokenized into
|
|
9
|
+
* argv. **Not** passed through a shell — operators (`|`, `&&`, `$(...)`)
|
|
10
|
+
* become literal argv elements, per the design's §6.1 sharp-edge note.
|
|
11
|
+
*
|
|
12
|
+
* Returns `undefined` if every source fails or yields an empty/whitespace
|
|
13
|
+
* value — the caller's `resolveAuthenticatedUrl` translates that to the
|
|
14
|
+
* `unverified` outcome (LINK_AUTH_UNVERIFIED in slice 2).
|
|
15
|
+
*
|
|
16
|
+
* Per design issue #113 §4 (token vocabulary) and §6.1 (command execution,
|
|
17
|
+
* `safeExecSync`-backed, `shell: false`).
|
|
18
|
+
*/
|
|
19
|
+
export type TokenSource = {
|
|
20
|
+
readonly env: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly command: string | readonly string[];
|
|
23
|
+
};
|
|
24
|
+
export interface TokenResolutionDeps {
|
|
25
|
+
/**
|
|
26
|
+
* Environment lookup map. Defaults to `process.env`. Injectable for tests so
|
|
27
|
+
* unit tests don't depend on ambient environment state.
|
|
28
|
+
*/
|
|
29
|
+
readonly env: Record<string, string | undefined>;
|
|
30
|
+
/**
|
|
31
|
+
* Command runner. Defaults to `safeExecResult`-wrapped invocation. Injectable
|
|
32
|
+
* for tests. Receives argv; returns `success` + `stdout`. Should NOT throw
|
|
33
|
+
* for normal exec failures (return `success: false` instead). Throws are
|
|
34
|
+
* propagated by `resolveToken` — they indicate operator-level bugs.
|
|
35
|
+
*/
|
|
36
|
+
readonly runCommand: (argv: readonly string[]) => {
|
|
37
|
+
success: boolean;
|
|
38
|
+
stdout: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a token from an ordered list of sources.
|
|
43
|
+
*
|
|
44
|
+
* @returns the first non-empty value, or `undefined` if every source failed.
|
|
45
|
+
* @throws whatever the injected `runCommand` throws (operator-level bug; not
|
|
46
|
+
* swallowed). Standard `safeExecResult` does not throw under normal use.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveToken(sources: readonly TokenSource[], deps?: Partial<TokenResolutionDeps>): string | undefined;
|
|
49
|
+
//# sourceMappingURL=resolve-token.d.ts.map
|
|
@@ -0,0 +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"}
|