@tokenfactory/acc-runner 0.43.0 → 0.44.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.
- package/README.md +75 -3
- package/dist/companion-run.d.ts.map +1 -1
- package/dist/companion-run.js +20 -0
- package/dist/companion-run.js.map +1 -1
- package/dist/config.d.ts +72 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +117 -3
- package/dist/config.js.map +1 -1
- package/dist/cost-pricing.d.ts +45 -0
- package/dist/cost-pricing.d.ts.map +1 -1
- package/dist/cost-pricing.js +57 -0
- package/dist/cost-pricing.js.map +1 -1
- package/dist/doctor.d.ts +4 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +32 -3
- package/dist/doctor.js.map +1 -1
- package/dist/engines/registry.d.ts +17 -6
- package/dist/engines/registry.d.ts.map +1 -1
- package/dist/engines/registry.js +38 -13
- package/dist/engines/registry.js.map +1 -1
- package/dist/git-auth/github-token-source.d.ts +54 -0
- package/dist/git-auth/github-token-source.d.ts.map +1 -0
- package/dist/git-auth/github-token-source.js +44 -0
- package/dist/git-auth/github-token-source.js.map +1 -0
- package/dist/git-auth/index.d.ts +4 -0
- package/dist/git-auth/index.d.ts.map +1 -1
- package/dist/git-auth/index.js +4 -0
- package/dist/git-auth/index.js.map +1 -1
- package/dist/git-auth/installation-token.d.ts +95 -0
- package/dist/git-auth/installation-token.d.ts.map +1 -0
- package/dist/git-auth/installation-token.js +192 -0
- package/dist/git-auth/installation-token.js.map +1 -0
- package/dist/instance.d.ts +30 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +51 -0
- package/dist/instance.js.map +1 -0
- package/dist/keychain.d.ts +4 -8
- package/dist/keychain.d.ts.map +1 -1
- package/dist/keychain.js +10 -4
- package/dist/keychain.js.map +1 -1
- package/dist/local-repo/command.d.ts +28 -0
- package/dist/local-repo/command.d.ts.map +1 -0
- package/dist/local-repo/command.js +178 -0
- package/dist/local-repo/command.js.map +1 -0
- package/dist/local-repo/index.d.ts +119 -0
- package/dist/local-repo/index.d.ts.map +1 -0
- package/dist/local-repo/index.js +141 -0
- package/dist/local-repo/index.js.map +1 -0
- package/dist/login.d.ts +4 -0
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +6 -1
- package/dist/login.js.map +1 -1
- package/dist/mcp-spawn.d.ts +19 -0
- package/dist/mcp-spawn.d.ts.map +1 -1
- package/dist/mcp-spawn.js +52 -26
- package/dist/mcp-spawn.js.map +1 -1
- package/dist/memory-context.d.ts +106 -0
- package/dist/memory-context.d.ts.map +1 -0
- package/dist/memory-context.js +220 -0
- package/dist/memory-context.js.map +1 -0
- package/dist/memory-curation.d.ts +172 -0
- package/dist/memory-curation.d.ts.map +1 -0
- package/dist/memory-curation.js +277 -0
- package/dist/memory-curation.js.map +1 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +34 -0
- package/dist/program.js.map +1 -1
- package/dist/provider-auth.d.ts +70 -1
- package/dist/provider-auth.d.ts.map +1 -1
- package/dist/provider-auth.js +106 -2
- package/dist/provider-auth.js.map +1 -1
- package/dist/runtime/cache-dir.d.ts.map +1 -1
- package/dist/runtime/cache-dir.js +8 -3
- package/dist/runtime/cache-dir.js.map +1 -1
- package/dist/runtime/decision-provenance.d.ts +63 -0
- package/dist/runtime/decision-provenance.d.ts.map +1 -0
- package/dist/runtime/decision-provenance.js +107 -0
- package/dist/runtime/decision-provenance.js.map +1 -0
- package/dist/runtime/prompt-substitute.d.ts +100 -0
- package/dist/runtime/prompt-substitute.d.ts.map +1 -0
- package/dist/runtime/prompt-substitute.js +124 -0
- package/dist/runtime/prompt-substitute.js.map +1 -0
- package/dist/runtime/review-cost-cap.d.ts +167 -0
- package/dist/runtime/review-cost-cap.d.ts.map +1 -0
- package/dist/runtime/review-cost-cap.js +182 -0
- package/dist/runtime/review-cost-cap.js.map +1 -0
- package/dist/runtime/reviewer-abstain.d.ts +101 -0
- package/dist/runtime/reviewer-abstain.d.ts.map +1 -0
- package/dist/runtime/reviewer-abstain.js +138 -0
- package/dist/runtime/reviewer-abstain.js.map +1 -0
- package/dist/runtime/reviewer.d.ts +75 -0
- package/dist/runtime/reviewer.d.ts.map +1 -1
- package/dist/runtime/reviewer.js +235 -81
- package/dist/runtime/reviewer.js.map +1 -1
- package/dist/runtime/singleton.d.ts.map +1 -1
- package/dist/runtime/singleton.js +9 -1
- package/dist/runtime/singleton.js.map +1 -1
- package/dist/serving-auth/index.d.ts +79 -0
- package/dist/serving-auth/index.d.ts.map +1 -0
- package/dist/serving-auth/index.js +169 -0
- package/dist/serving-auth/index.js.map +1 -0
- package/dist/task-runner.d.ts +19 -2
- package/dist/task-runner.d.ts.map +1 -1
- package/dist/task-runner.js +110 -28
- package/dist/task-runner.js.map +1 -1
- package/dist/watch.d.ts +13 -0
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +128 -20
- package/dist/watch.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const GITHUB_TOKEN_SOURCE_ENV = "ACC_RUNNER_GITHUB_TOKEN_SOURCE";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve which source GH_TOKEN should come from. Defaults to "env" (today's
|
|
4
|
+
* behaviour, unchanged) so existing desktop runners and containers with
|
|
5
|
+
* GH_TOKEN baked into their launch env see no change.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveGithubTokenSource(env = process.env) {
|
|
8
|
+
const v = (env[GITHUB_TOKEN_SOURCE_ENV] ?? "").trim().toLowerCase();
|
|
9
|
+
return v === "installation-token" || v === "installation_token" ? "installation-token" : "env";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The repo(s) an installation-token mint should be scoped to for this
|
|
13
|
+
* container: the runner's declared served-repo scope (RunnerConfig.repos)
|
|
14
|
+
* when non-empty, else the single configured target repo — the same
|
|
15
|
+
* fallback the pre-multi-repo single-repo default already uses everywhere
|
|
16
|
+
* else in the runner (an undeclared scope means "this one repo", not
|
|
17
|
+
* "every repo the App can see").
|
|
18
|
+
*/
|
|
19
|
+
export function effectiveInstallationTokenRepos(cfg) {
|
|
20
|
+
return cfg.repos && cfg.repos.length > 0 ? cfg.repos : [cfg.targetRepo];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Startup/refresh-time env hydration: when ACC_RUNNER_GITHUB_TOKEN_SOURCE is
|
|
24
|
+
* "installation-token", mints (or serves the cached) short-lived, repo-scoped
|
|
25
|
+
* token from `provider` and returns `base` with GH_TOKEN set to it;
|
|
26
|
+
* otherwise returns `base` unchanged. Unlike hydrateAnthropicApiKey, an
|
|
27
|
+
* existing GH_TOKEN in `base` is deliberately OVERWRITTEN when opted in — the
|
|
28
|
+
* whole point is to replace the shared PAT a container's launch env may
|
|
29
|
+
* already carry, not defer to it. Never mutates `base`.
|
|
30
|
+
*
|
|
31
|
+
* Callers invoke this once at startup and again on a refresh timer (the
|
|
32
|
+
* provider's own cache means a call before the refresh margin is a cheap
|
|
33
|
+
* no-op mint).
|
|
34
|
+
*/
|
|
35
|
+
export async function hydrateGithubToken(base = process.env, provider) {
|
|
36
|
+
if (resolveGithubTokenSource(base) !== "installation-token")
|
|
37
|
+
return base;
|
|
38
|
+
if (!provider) {
|
|
39
|
+
throw new Error(`${GITHUB_TOKEN_SOURCE_ENV}=installation-token requires an InstallationTokenProvider at startup`);
|
|
40
|
+
}
|
|
41
|
+
const token = await provider.getToken();
|
|
42
|
+
return { ...base, GH_TOKEN: token };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=github-token-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-token-source.js","sourceRoot":"","sources":["../../src/git-auth/github-token-source.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,MAAM,uBAAuB,GAAG,gCAAyC,CAAC;AAEjF;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpE,OAAO,CAAC,KAAK,oBAAoB,IAAI,CAAC,KAAK,oBAAoB,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,KAAK,CAAC;AACjG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,+BAA+B,CAAC,GAG/C;IACC,OAAO,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAA0B,OAAO,CAAC,GAAG,EACrC,QAA0C;IAE1C,IAAI,wBAAwB,CAAC,IAAI,CAAC,KAAK,oBAAoB;QAAE,OAAO,IAAI,CAAC;IACzE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,GAAG,uBAAuB,sEAAsE,CACjG,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACxC,OAAO,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACtC,CAAC"}
|
package/dist/git-auth/index.d.ts
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
export { classifyGitFetchError, isGitAuthFailure, GIT_AUTH_FAILURE_PATTERNS, type GitFetchFailureClass, } from "./classify.js";
|
|
8
8
|
export { makeGitAuthProbe, maskGitCredentials, type GitAuthProbe, type GitAuthProbeResult, type DefaultProbeOptions, } from "./probe.js";
|
|
9
9
|
export { GitAuthController, DEFAULT_GIT_AUTH_FAILURE_THRESHOLD, type GitAuthControllerDeps, type GitAuthDegradedInfo, type GitAuthRecoveredInfo, type GitAuthSnapshot, } from "./controller.js";
|
|
10
|
+
/** NS-RUN-3: per-container GitHub App installation tokens (replaces a shared GH_TOKEN PAT). */
|
|
11
|
+
export { InstallationTokenProvider, InstallationTokenScopeError, isWithinRequestedScope, makeHttpInstallationTokenFetcher, type InstallationTokenFetcher, type InstallationTokenProviderOptions, type InstallationTokenResult, } from "./installation-token.js";
|
|
12
|
+
/** NS-RUN-3: where a running container's GH_TOKEN comes from (env vs. minted). */
|
|
13
|
+
export { GITHUB_TOKEN_SOURCE_ENV, resolveGithubTokenSource, effectiveInstallationTokenRepos, hydrateGithubToken, type GithubTokenSource, } from "./github-token-source.js";
|
|
10
14
|
/** Runner-emitted audit verbs. The alerts tick (api/_lib/alerts-git-auth.ts) */
|
|
11
15
|
/** scans these to derive currently-degraded machines and page the operator. */
|
|
12
16
|
export declare const RUNNER_GIT_AUTH_FAILED_VERB = "runner.git_auth_failed";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/git-auth/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,yBAAyB,EACzB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,kCAAkC,EAClC,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/git-auth/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,yBAAyB,EACzB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,kCAAkC,EAClC,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;AACzB,+FAA+F;AAC/F,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,sBAAsB,EACtB,gCAAgC,EAChC,KAAK,wBAAwB,EAC7B,KAAK,gCAAgC,EACrC,KAAK,uBAAuB,GAC7B,MAAM,yBAAyB,CAAC;AACjC,kFAAkF;AAClF,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,+BAA+B,EAC/B,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,0BAA0B,CAAC;AAElC,gFAAgF;AAChF,+EAA+E;AAC/E,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AACpE,eAAO,MAAM,8BAA8B,8BAA8B,CAAC"}
|
package/dist/git-auth/index.js
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
export { classifyGitFetchError, isGitAuthFailure, GIT_AUTH_FAILURE_PATTERNS, } from "./classify.js";
|
|
8
8
|
export { makeGitAuthProbe, maskGitCredentials, } from "./probe.js";
|
|
9
9
|
export { GitAuthController, DEFAULT_GIT_AUTH_FAILURE_THRESHOLD, } from "./controller.js";
|
|
10
|
+
/** NS-RUN-3: per-container GitHub App installation tokens (replaces a shared GH_TOKEN PAT). */
|
|
11
|
+
export { InstallationTokenProvider, InstallationTokenScopeError, isWithinRequestedScope, makeHttpInstallationTokenFetcher, } from "./installation-token.js";
|
|
12
|
+
/** NS-RUN-3: where a running container's GH_TOKEN comes from (env vs. minted). */
|
|
13
|
+
export { GITHUB_TOKEN_SOURCE_ENV, resolveGithubTokenSource, effectiveInstallationTokenRepos, hydrateGithubToken, } from "./github-token-source.js";
|
|
10
14
|
/** Runner-emitted audit verbs. The alerts tick (api/_lib/alerts-git-auth.ts) */
|
|
11
15
|
/** scans these to derive currently-degraded machines and page the operator. */
|
|
12
16
|
export const RUNNER_GIT_AUTH_FAILED_VERB = "runner.git_auth_failed";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/git-auth/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,yBAAyB,GAE1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GAInB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,kCAAkC,GAKnC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/git-auth/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,yBAAyB,GAE1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GAInB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,kCAAkC,GAKnC,MAAM,iBAAiB,CAAC;AACzB,+FAA+F;AAC/F,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,sBAAsB,EACtB,gCAAgC,GAIjC,MAAM,yBAAyB,CAAC;AACjC,kFAAkF;AAClF,OAAO,EACL,uBAAuB,EACvB,wBAAwB,EACxB,+BAA+B,EAC/B,kBAAkB,GAEnB,MAAM,0BAA0B,CAAC;AAElC,gFAAgF;AAChF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AACpE,MAAM,CAAC,MAAM,8BAA8B,GAAG,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** One minted installation token plus the metadata needed to cache/refresh it. */
|
|
2
|
+
export interface InstallationTokenResult {
|
|
3
|
+
token: string;
|
|
4
|
+
/** ISO-8601 expiry timestamp (GitHub installation tokens live ~1h). */
|
|
5
|
+
expiresAt: string;
|
|
6
|
+
/** The repos this token is ACTUALLY scoped to, as returned by the mint. */
|
|
7
|
+
repos: string[];
|
|
8
|
+
}
|
|
9
|
+
/** Injectable seam. Production wires this via `makeHttpInstallationTokenFetcher`; tests pass a stub. */
|
|
10
|
+
export type InstallationTokenFetcher = (repos: string[], runnerId: string) => Promise<InstallationTokenResult>;
|
|
11
|
+
/**
|
|
12
|
+
* Thrown when a mint hands back a token scoped to repos beyond what the
|
|
13
|
+
* runner requested. This is the client-side half of "token scope limited to
|
|
14
|
+
* the repos the runner serves" (AC3): even a server that returns an
|
|
15
|
+
* over-broad grant cannot make this runner USE the excess scope, because the
|
|
16
|
+
* provider refuses the token outright rather than caching/serving it.
|
|
17
|
+
*/
|
|
18
|
+
export declare class InstallationTokenScopeError extends Error {
|
|
19
|
+
readonly requested: string[];
|
|
20
|
+
readonly granted: string[];
|
|
21
|
+
constructor(requested: string[], granted: string[]);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* True when `granted` is a NON-EMPTY set and every entry is present in
|
|
25
|
+
* `requested` (case-insensitive).
|
|
26
|
+
*
|
|
27
|
+
* AC5: an empty grant is NOT treated as in-scope. A mint that hands back zero
|
|
28
|
+
* repos proves nothing about what it actually scoped the token to — vacuously
|
|
29
|
+
* satisfying a subset check would let a malformed or field-less response
|
|
30
|
+
* through as if it were a valid (if narrow) grant. Fail closed instead: no
|
|
31
|
+
* verifiable scope means the token is rejected, same as an over-broad one.
|
|
32
|
+
*/
|
|
33
|
+
export declare function isWithinRequestedScope(requested: string[], granted: string[]): boolean;
|
|
34
|
+
export interface InstallationTokenProviderOptions {
|
|
35
|
+
/**
|
|
36
|
+
* The repo(s) ("owner/name") this container is configured to serve.
|
|
37
|
+
* Required and non-empty — a provider with no configured repos can never
|
|
38
|
+
* mint a token, closing off the "one credential, every repo" shape a
|
|
39
|
+
* shared PAT had.
|
|
40
|
+
*/
|
|
41
|
+
repos: string[];
|
|
42
|
+
fetcher: InstallationTokenFetcher;
|
|
43
|
+
runnerId: string;
|
|
44
|
+
/** Refresh this many ms before the cached token's stated expiry. Default 5 min. */
|
|
45
|
+
refreshMarginMs?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Runner-side cache/refresh for a single installation token, scoped to the
|
|
49
|
+
* repo(s) this container serves. In-memory only — like `EnvTokenProvider`
|
|
50
|
+
* (token-provider.ts), an ephemeral container must never write a credential
|
|
51
|
+
* to its writable layer.
|
|
52
|
+
*/
|
|
53
|
+
export declare class InstallationTokenProvider {
|
|
54
|
+
private readonly repos;
|
|
55
|
+
private readonly fetcher;
|
|
56
|
+
private readonly runnerId;
|
|
57
|
+
private readonly refreshMarginMs;
|
|
58
|
+
private cached;
|
|
59
|
+
private inflight;
|
|
60
|
+
constructor(opts: InstallationTokenProviderOptions);
|
|
61
|
+
/** Repos this provider is configured to serve (defensive copy). */
|
|
62
|
+
reposServed(): string[];
|
|
63
|
+
/** True when the cached token is still fresh past the refresh margin. */
|
|
64
|
+
private fresh;
|
|
65
|
+
/**
|
|
66
|
+
* Return a valid installation token, minting or refreshing as needed.
|
|
67
|
+
* Concurrent callers share one in-flight mint (mirrors the server-side
|
|
68
|
+
* single-flight in `api/_lib/github-app.ts`) so a burst of git operations
|
|
69
|
+
* at container boot doesn't fire N redundant mints. A rejected mint clears
|
|
70
|
+
* the in-flight slot so the NEXT call retries rather than replaying the
|
|
71
|
+
* same rejection forever.
|
|
72
|
+
*/
|
|
73
|
+
getToken(): Promise<string>;
|
|
74
|
+
private mint;
|
|
75
|
+
/** Diagnostics-safe snapshot — NEVER includes the token itself. */
|
|
76
|
+
snapshot(): {
|
|
77
|
+
hasToken: boolean;
|
|
78
|
+
expiresAt: string | null;
|
|
79
|
+
repos: string[];
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build an HTTP-backed `InstallationTokenFetcher` against the (not-yet-shipped)
|
|
84
|
+
* `/api/runner/github-installation-token` endpoint. Mirrors
|
|
85
|
+
* `secrets/inject.ts`'s `makeHttpSecretFetcher`: the runner authenticates
|
|
86
|
+
* with its own bearer JWT, the server does the privileged GitHub App
|
|
87
|
+
* JWT-sign + installation-token exchange (`api/_lib/github-app.ts`), and only
|
|
88
|
+
* the resulting short-lived, repo-scoped token crosses the wire back.
|
|
89
|
+
*/
|
|
90
|
+
export declare function makeHttpInstallationTokenFetcher(opts: {
|
|
91
|
+
publicUrl: string;
|
|
92
|
+
jwt: string;
|
|
93
|
+
fetchImpl?: typeof fetch;
|
|
94
|
+
}): InstallationTokenFetcher;
|
|
95
|
+
//# sourceMappingURL=installation-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installation-token.d.ts","sourceRoot":"","sources":["../../src/git-auth/installation-token.ts"],"names":[],"mappings":"AAqCA,kFAAkF;AAClF,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wGAAwG;AACxG,MAAM,MAAM,wBAAwB,GAAG,CACrC,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEtC;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBAEf,SAAS,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;CAUnD;AAMD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAItF;AAKD,MAAM,WAAW,gCAAgC;IAC/C;;;;;OAKG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,wBAAwB,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,MAAM,CAAwC;IACtD,OAAO,CAAC,QAAQ,CAAiD;gBAErD,IAAI,EAAE,gCAAgC;IAclD,mEAAmE;IACnE,WAAW,IAAI,MAAM,EAAE;IAIvB,yEAAyE;IACzE,OAAO,CAAC,KAAK;IAKb;;;;;;;OAOG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;YAWnB,IAAI;IASlB,mEAAmE;IACnE,QAAQ,IAAI;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE;CAO7E;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,EAAE;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,wBAAwB,CA4C3B"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NS-RUN-3: per-container GitHub App installation tokens.
|
|
3
|
+
*
|
|
4
|
+
* Headless/container runners have historically shared one long-lived GH_TOKEN
|
|
5
|
+
* PAT across every node in the fleet (see docs/acc/RUNNER_AUTOSCALE.md §2,
|
|
6
|
+
* `ACC_AUTOSCALE_RUNNER_ENV_JSON`) — a single credential whose blast radius is
|
|
7
|
+
* the whole fleet's repo access, that never expires until an operator rotates
|
|
8
|
+
* it by hand, and that cannot be scoped tighter than whatever repos the PAT
|
|
9
|
+
* itself carries.
|
|
10
|
+
*
|
|
11
|
+
* This module is the RUNNER-SIDE client for replacing that PAT with
|
|
12
|
+
* short-lived (~1h) GitHub App installation tokens, scoped to only the
|
|
13
|
+
* repo(s) a given container is configured to serve. The privileged half of
|
|
14
|
+
* the mint — signing a JWT with the GitHub App's private key and exchanging
|
|
15
|
+
* it for an installation access token — already exists server-side in
|
|
16
|
+
* `api/_lib/github-app.ts` (`getInstallationTokenForRepo`); this module never
|
|
17
|
+
* holds that private key, only the short-lived bearer it fetches over HTTPS
|
|
18
|
+
* using the runner's OWN control-plane JWT — the same "runner authenticates,
|
|
19
|
+
* server does the privileged work" shape `secrets/inject.ts`'s
|
|
20
|
+
* `{{secret:NAME}}` broker already uses for tool-call secrets.
|
|
21
|
+
*
|
|
22
|
+
* Scope enforcement is defense-in-depth on BOTH sides: the request always
|
|
23
|
+
* names the exact repos this runner serves (never "all installs"), and this
|
|
24
|
+
* client additionally REFUSES to use a minted token whose granted repos
|
|
25
|
+
* exceed what it asked for (`InstallationTokenScopeError`) — a
|
|
26
|
+
* misconfigured or compromised server response cannot hand this container a
|
|
27
|
+
* broader credential than it was provisioned for.
|
|
28
|
+
*
|
|
29
|
+
* Wiring note (tracked as a follow-up, out of this module's file scope): the
|
|
30
|
+
* HTTP endpoint this fetcher targets (`/api/runner/github-installation-token`)
|
|
31
|
+
* does not exist yet, and neither `packages/acc-runner/docker/entrypoint.sh`
|
|
32
|
+
* nor `api/scheduled/runner-autoscale.ts` have been pointed at this provider
|
|
33
|
+
* in place of a shared `GH_TOKEN`. This module is the tested client primitive
|
|
34
|
+
* that follow-up work wires in.
|
|
35
|
+
*/
|
|
36
|
+
import { maskGitCredentials } from "./probe.js";
|
|
37
|
+
/**
|
|
38
|
+
* Thrown when a mint hands back a token scoped to repos beyond what the
|
|
39
|
+
* runner requested. This is the client-side half of "token scope limited to
|
|
40
|
+
* the repos the runner serves" (AC3): even a server that returns an
|
|
41
|
+
* over-broad grant cannot make this runner USE the excess scope, because the
|
|
42
|
+
* provider refuses the token outright rather than caching/serving it.
|
|
43
|
+
*/
|
|
44
|
+
export class InstallationTokenScopeError extends Error {
|
|
45
|
+
requested;
|
|
46
|
+
granted;
|
|
47
|
+
constructor(requested, granted) {
|
|
48
|
+
super(`installation token scope exceeds request: requested [${requested.join(", ")}], granted [${granted.join(", ")}]`);
|
|
49
|
+
this.name = "InstallationTokenScopeError";
|
|
50
|
+
this.requested = requested;
|
|
51
|
+
this.granted = granted;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function normaliseRepo(repo) {
|
|
55
|
+
return repo.trim().toLowerCase();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* True when `granted` is a NON-EMPTY set and every entry is present in
|
|
59
|
+
* `requested` (case-insensitive).
|
|
60
|
+
*
|
|
61
|
+
* AC5: an empty grant is NOT treated as in-scope. A mint that hands back zero
|
|
62
|
+
* repos proves nothing about what it actually scoped the token to — vacuously
|
|
63
|
+
* satisfying a subset check would let a malformed or field-less response
|
|
64
|
+
* through as if it were a valid (if narrow) grant. Fail closed instead: no
|
|
65
|
+
* verifiable scope means the token is rejected, same as an over-broad one.
|
|
66
|
+
*/
|
|
67
|
+
export function isWithinRequestedScope(requested, granted) {
|
|
68
|
+
if (granted.length === 0)
|
|
69
|
+
return false;
|
|
70
|
+
const allowed = new Set(requested.map(normaliseRepo));
|
|
71
|
+
return granted.every((r) => allowed.has(normaliseRepo(r)));
|
|
72
|
+
}
|
|
73
|
+
/** Default refresh margin: mint a new token once the cached one is this close to expiry. Mirrors api/_lib/github-app.ts's server-side cache. */
|
|
74
|
+
const DEFAULT_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
75
|
+
/**
|
|
76
|
+
* Runner-side cache/refresh for a single installation token, scoped to the
|
|
77
|
+
* repo(s) this container serves. In-memory only — like `EnvTokenProvider`
|
|
78
|
+
* (token-provider.ts), an ephemeral container must never write a credential
|
|
79
|
+
* to its writable layer.
|
|
80
|
+
*/
|
|
81
|
+
export class InstallationTokenProvider {
|
|
82
|
+
repos;
|
|
83
|
+
fetcher;
|
|
84
|
+
runnerId;
|
|
85
|
+
refreshMarginMs;
|
|
86
|
+
cached = null;
|
|
87
|
+
inflight = null;
|
|
88
|
+
constructor(opts) {
|
|
89
|
+
const repos = (opts.repos ?? []).map((r) => r.trim()).filter(Boolean);
|
|
90
|
+
if (repos.length === 0) {
|
|
91
|
+
throw new Error("InstallationTokenProvider requires at least one repo — a container must be " +
|
|
92
|
+
"scoped to the repo(s) it serves, never minted a token for every install");
|
|
93
|
+
}
|
|
94
|
+
this.repos = repos;
|
|
95
|
+
this.fetcher = opts.fetcher;
|
|
96
|
+
this.runnerId = opts.runnerId;
|
|
97
|
+
this.refreshMarginMs = Math.max(0, opts.refreshMarginMs ?? DEFAULT_REFRESH_MARGIN_MS);
|
|
98
|
+
}
|
|
99
|
+
/** Repos this provider is configured to serve (defensive copy). */
|
|
100
|
+
reposServed() {
|
|
101
|
+
return [...this.repos];
|
|
102
|
+
}
|
|
103
|
+
/** True when the cached token is still fresh past the refresh margin. */
|
|
104
|
+
fresh() {
|
|
105
|
+
if (!this.cached)
|
|
106
|
+
return false;
|
|
107
|
+
return new Date(this.cached.expiresAt).getTime() - Date.now() > this.refreshMarginMs;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Return a valid installation token, minting or refreshing as needed.
|
|
111
|
+
* Concurrent callers share one in-flight mint (mirrors the server-side
|
|
112
|
+
* single-flight in `api/_lib/github-app.ts`) so a burst of git operations
|
|
113
|
+
* at container boot doesn't fire N redundant mints. A rejected mint clears
|
|
114
|
+
* the in-flight slot so the NEXT call retries rather than replaying the
|
|
115
|
+
* same rejection forever.
|
|
116
|
+
*/
|
|
117
|
+
async getToken() {
|
|
118
|
+
if (this.fresh())
|
|
119
|
+
return this.cached.token;
|
|
120
|
+
if (this.inflight)
|
|
121
|
+
return (await this.inflight).token;
|
|
122
|
+
const mint = this.mint().finally(() => {
|
|
123
|
+
this.inflight = null;
|
|
124
|
+
});
|
|
125
|
+
this.inflight = mint;
|
|
126
|
+
return (await mint).token;
|
|
127
|
+
}
|
|
128
|
+
async mint() {
|
|
129
|
+
const result = await this.fetcher(this.repos, this.runnerId);
|
|
130
|
+
if (!isWithinRequestedScope(this.repos, result.repos)) {
|
|
131
|
+
throw new InstallationTokenScopeError(this.repos, result.repos);
|
|
132
|
+
}
|
|
133
|
+
this.cached = result;
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
/** Diagnostics-safe snapshot — NEVER includes the token itself. */
|
|
137
|
+
snapshot() {
|
|
138
|
+
return {
|
|
139
|
+
hasToken: this.cached !== null,
|
|
140
|
+
expiresAt: this.cached?.expiresAt ?? null,
|
|
141
|
+
repos: this.repos,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Build an HTTP-backed `InstallationTokenFetcher` against the (not-yet-shipped)
|
|
147
|
+
* `/api/runner/github-installation-token` endpoint. Mirrors
|
|
148
|
+
* `secrets/inject.ts`'s `makeHttpSecretFetcher`: the runner authenticates
|
|
149
|
+
* with its own bearer JWT, the server does the privileged GitHub App
|
|
150
|
+
* JWT-sign + installation-token exchange (`api/_lib/github-app.ts`), and only
|
|
151
|
+
* the resulting short-lived, repo-scoped token crosses the wire back.
|
|
152
|
+
*/
|
|
153
|
+
export function makeHttpInstallationTokenFetcher(opts) {
|
|
154
|
+
const f = opts.fetchImpl ?? fetch;
|
|
155
|
+
const endpoint = `${opts.publicUrl.replace(/\/$/, "")}/api/runner/github-installation-token`;
|
|
156
|
+
return async (repos, runnerId) => {
|
|
157
|
+
const res = await f(endpoint, {
|
|
158
|
+
method: "POST",
|
|
159
|
+
headers: {
|
|
160
|
+
Authorization: `Bearer ${opts.jwt}`,
|
|
161
|
+
"Content-Type": "application/json",
|
|
162
|
+
},
|
|
163
|
+
body: JSON.stringify({ repos, runner_id: runnerId }),
|
|
164
|
+
});
|
|
165
|
+
if (!res.ok) {
|
|
166
|
+
// Masked defensively: the endpoint contract is that the body never
|
|
167
|
+
// carries token material, but a broken/aliased endpoint could echo
|
|
168
|
+
// request state back — never trust an error body unmasked.
|
|
169
|
+
const text = await res.text().catch(() => "");
|
|
170
|
+
throw new Error(`github-installation-token HTTP ${res.status}: ${maskGitCredentials(text || res.statusText)}`);
|
|
171
|
+
}
|
|
172
|
+
const data = (await res.json());
|
|
173
|
+
if (typeof data.token !== "string" || typeof data.expires_at !== "string") {
|
|
174
|
+
throw new Error("github-installation-token response missing token/expires_at");
|
|
175
|
+
}
|
|
176
|
+
// AC5: a field-less or empty `repos` is rejected here, not silently
|
|
177
|
+
// trusted back to whatever we asked for — an over-broad or malformed
|
|
178
|
+
// server response must never be papered over by re-stating the request
|
|
179
|
+
// as if it were the grant. isWithinRequestedScope() in the caller
|
|
180
|
+
// additionally refuses an empty grant, so this is defense-in-depth: the
|
|
181
|
+
// fetcher itself never fabricates a scope the server didn't state.
|
|
182
|
+
if (!Array.isArray(data.repos) || data.repos.length === 0) {
|
|
183
|
+
throw new Error("github-installation-token response missing/empty repos (no fallback to the requested repos)");
|
|
184
|
+
}
|
|
185
|
+
const grantedRepos = data.repos.filter((r) => typeof r === "string");
|
|
186
|
+
if (grantedRepos.length !== data.repos.length) {
|
|
187
|
+
throw new Error("github-installation-token response repos contains a non-string entry");
|
|
188
|
+
}
|
|
189
|
+
return { token: data.token, expiresAt: data.expires_at, repos: grantedRepos };
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=installation-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installation-token.js","sourceRoot":"","sources":["../../src/git-auth/installation-token.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RUNNER-NAMED-INSTANCES-1: named runner instances. Keychain mode stores ONE
|
|
3
|
+
* runner session per OS user, and `acc.register_runner` dedups identity on
|
|
4
|
+
* (bound_user_id, machine) — so a second runner on one Mac silently became a
|
|
5
|
+
* duplicate of the first (incident 2026-08-27). A name fixes that client-side:
|
|
6
|
+
* folded into the keychain account, machine string, cache root and singleton
|
|
7
|
+
* key, the server dedup mints each name its own stable runner id. Unnamed,
|
|
8
|
+
* every resolver below returns its historic value byte-for-byte (frozen).
|
|
9
|
+
*/
|
|
10
|
+
/** Env var carrying the active name (set by `--instance <name>`). */
|
|
11
|
+
export declare const INSTANCE_ENV = "ACC_RUNNER_INSTANCE";
|
|
12
|
+
/** Keychain account slot for the unnamed instance. FROZEN. */
|
|
13
|
+
export declare const DEFAULT_INSTANCE_ACCOUNT = "default";
|
|
14
|
+
/** Narrow on purpose: the name lands in a path, a keychain account and a
|
|
15
|
+
* server-side machine string — nothing escapable/quotable is allowed. */
|
|
16
|
+
export declare const INSTANCE_NAME_RE: RegExp;
|
|
17
|
+
export declare function isValidInstanceName(name: string): boolean;
|
|
18
|
+
/** Throws on an invalid name; returns the trimmed name otherwise. */
|
|
19
|
+
export declare function assertInstanceName(name: string): string;
|
|
20
|
+
/** Active name or null. Throws on an invalid env value — a typo must not
|
|
21
|
+
* fall back to the default slot and re-create the duplicate incident. */
|
|
22
|
+
export declare function resolveInstance(env?: NodeJS.ProcessEnv): string | null;
|
|
23
|
+
/** Publish `--instance <name>` into the env so every resolver (including in
|
|
24
|
+
* spawned children) agrees. No-op without the flag. */
|
|
25
|
+
export declare function applyInstanceOption(name: string | undefined): string | null;
|
|
26
|
+
/** Keychain account: "default" unnamed, else "instance:<name>". */
|
|
27
|
+
export declare function instanceKeychainAccount(env?: NodeJS.ProcessEnv): string;
|
|
28
|
+
/** Machine-string suffix: "" unnamed, else "#<name>". */
|
|
29
|
+
export declare function instanceMachineSuffix(env?: NodeJS.ProcessEnv): string;
|
|
30
|
+
//# sourceMappingURL=instance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,qEAAqE;AACrE,eAAO,MAAM,YAAY,wBAAwB,CAAC;AAElD,8DAA8D;AAC9D,eAAO,MAAM,wBAAwB,YAAY,CAAC;AAElD;yEACyE;AACzE,eAAO,MAAM,gBAAgB,QAA8B,CAAC;AAE5D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED;yEACyE;AACzE,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,IAAI,CAGnF;AAED;uDACuD;AACvD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAG3E;AAED,mEAAmE;AACnE,wBAAgB,uBAAuB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAGvE;AAED,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAGrE"}
|
package/dist/instance.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RUNNER-NAMED-INSTANCES-1: named runner instances. Keychain mode stores ONE
|
|
3
|
+
* runner session per OS user, and `acc.register_runner` dedups identity on
|
|
4
|
+
* (bound_user_id, machine) — so a second runner on one Mac silently became a
|
|
5
|
+
* duplicate of the first (incident 2026-08-27). A name fixes that client-side:
|
|
6
|
+
* folded into the keychain account, machine string, cache root and singleton
|
|
7
|
+
* key, the server dedup mints each name its own stable runner id. Unnamed,
|
|
8
|
+
* every resolver below returns its historic value byte-for-byte (frozen).
|
|
9
|
+
*/
|
|
10
|
+
/** Env var carrying the active name (set by `--instance <name>`). */
|
|
11
|
+
export const INSTANCE_ENV = "ACC_RUNNER_INSTANCE";
|
|
12
|
+
/** Keychain account slot for the unnamed instance. FROZEN. */
|
|
13
|
+
export const DEFAULT_INSTANCE_ACCOUNT = "default";
|
|
14
|
+
/** Narrow on purpose: the name lands in a path, a keychain account and a
|
|
15
|
+
* server-side machine string — nothing escapable/quotable is allowed. */
|
|
16
|
+
export const INSTANCE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
17
|
+
export function isValidInstanceName(name) {
|
|
18
|
+
return INSTANCE_NAME_RE.test(name);
|
|
19
|
+
}
|
|
20
|
+
/** Throws on an invalid name; returns the trimmed name otherwise. */
|
|
21
|
+
export function assertInstanceName(name) {
|
|
22
|
+
const n = name.trim();
|
|
23
|
+
if (isValidInstanceName(n))
|
|
24
|
+
return n;
|
|
25
|
+
throw new Error(`Invalid --instance name "${name}": must match ${INSTANCE_NAME_RE.source} ` +
|
|
26
|
+
"(lowercase letters, digits and dashes, starting alphanumeric, max 32 chars).");
|
|
27
|
+
}
|
|
28
|
+
/** Active name or null. Throws on an invalid env value — a typo must not
|
|
29
|
+
* fall back to the default slot and re-create the duplicate incident. */
|
|
30
|
+
export function resolveInstance(env = process.env) {
|
|
31
|
+
const raw = env[INSTANCE_ENV]?.trim();
|
|
32
|
+
return raw ? assertInstanceName(raw) : null;
|
|
33
|
+
}
|
|
34
|
+
/** Publish `--instance <name>` into the env so every resolver (including in
|
|
35
|
+
* spawned children) agrees. No-op without the flag. */
|
|
36
|
+
export function applyInstanceOption(name) {
|
|
37
|
+
if (name !== undefined)
|
|
38
|
+
process.env[INSTANCE_ENV] = assertInstanceName(name);
|
|
39
|
+
return resolveInstance();
|
|
40
|
+
}
|
|
41
|
+
/** Keychain account: "default" unnamed, else "instance:<name>". */
|
|
42
|
+
export function instanceKeychainAccount(env) {
|
|
43
|
+
const name = resolveInstance(env);
|
|
44
|
+
return name ? `instance:${name}` : DEFAULT_INSTANCE_ACCOUNT;
|
|
45
|
+
}
|
|
46
|
+
/** Machine-string suffix: "" unnamed, else "#<name>". */
|
|
47
|
+
export function instanceMachineSuffix(env) {
|
|
48
|
+
const name = resolveInstance(env);
|
|
49
|
+
return name ? `#${name}` : "";
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=instance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,YAAY,GAAG,qBAAqB,CAAC;AAElD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,wBAAwB,GAAG,SAAS,CAAC;AAElD;yEACyE;AACzE,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAE5D,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACtB,IAAI,mBAAmB,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACrC,MAAM,IAAI,KAAK,CACb,4BAA4B,IAAI,iBAAiB,gBAAgB,CAAC,MAAM,GAAG;QACzE,8EAA8E,CACjF,CAAC;AACJ,CAAC;AAED;yEACyE;AACzE,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC;IACtC,OAAO,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED;uDACuD;AACvD,MAAM,UAAU,mBAAmB,CAAC,IAAwB;IAC1D,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7E,OAAO,eAAe,EAAE,CAAC;AAC3B,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,uBAAuB,CAAC,GAAuB;IAC7D,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,wBAAwB,CAAC;AAC9D,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,qBAAqB,CAAC,GAAuB;IAC3D,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChC,CAAC"}
|
package/dist/keychain.d.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OS-keychain wrapper around `keytar`. The CLI never persists a JWT to
|
|
3
|
-
* disk in plaintext — keychain is the only writable surface.
|
|
4
|
-
*
|
|
5
|
-
* keytar is a native module; we lazy-import so commands that don't need
|
|
6
|
-
* credentials (`--version`, `--help`) still work on systems where the
|
|
7
|
-
* binding has not built (`linux` headless CI without libsecret, etc).
|
|
8
|
-
*/
|
|
9
1
|
import { type StoredSession } from "./types.js";
|
|
2
|
+
/** RUNNER-NAMED-INSTANCES-1: the session slot — frozen "default" unless a
|
|
3
|
+
* named instance is active. Resolved per call (not at module load) so login /
|
|
4
|
+
* watch / logout / doctor can never disagree about it. */
|
|
5
|
+
export declare function sessionAccount(): string;
|
|
10
6
|
type KeytarLike = {
|
|
11
7
|
setPassword(service: string, account: string, password: string): Promise<void>;
|
|
12
8
|
getPassword(service: string, account: string): Promise<string | null>;
|
package/dist/keychain.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"keychain.d.ts","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"AASA,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzE;;0DAE0D;AAC1D,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,KAAK,UAAU,GAAG;IAChB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACtE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpE,CAAC;AAYF,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvE;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAUjE;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CAGrD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAG7E;AAED,wBAAsB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAWpE;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGjE;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,IAAI,CAEzD"}
|
package/dist/keychain.js
CHANGED
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
* credentials (`--version`, `--help`) still work on systems where the
|
|
7
7
|
* binding has not built (`linux` headless CI without libsecret, etc).
|
|
8
8
|
*/
|
|
9
|
+
import { instanceKeychainAccount } from "./instance.js";
|
|
9
10
|
import { RUNNER_KEYCHAIN_SERVICE } from "./types.js";
|
|
10
|
-
|
|
11
|
+
/** RUNNER-NAMED-INSTANCES-1: the session slot — frozen "default" unless a
|
|
12
|
+
* named instance is active. Resolved per call (not at module load) so login /
|
|
13
|
+
* watch / logout / doctor can never disagree about it. */
|
|
14
|
+
export function sessionAccount() {
|
|
15
|
+
return instanceKeychainAccount();
|
|
16
|
+
}
|
|
11
17
|
let cached = null;
|
|
12
18
|
async function getKeytar() {
|
|
13
19
|
if (cached)
|
|
@@ -19,11 +25,11 @@ async function getKeytar() {
|
|
|
19
25
|
}
|
|
20
26
|
export async function saveSession(session) {
|
|
21
27
|
const k = await getKeytar();
|
|
22
|
-
await k.setPassword(RUNNER_KEYCHAIN_SERVICE,
|
|
28
|
+
await k.setPassword(RUNNER_KEYCHAIN_SERVICE, sessionAccount(), JSON.stringify(session));
|
|
23
29
|
}
|
|
24
30
|
export async function loadSession() {
|
|
25
31
|
const k = await getKeytar();
|
|
26
|
-
const raw = await k.getPassword(RUNNER_KEYCHAIN_SERVICE,
|
|
32
|
+
const raw = await k.getPassword(RUNNER_KEYCHAIN_SERVICE, sessionAccount());
|
|
27
33
|
if (!raw)
|
|
28
34
|
return null;
|
|
29
35
|
try {
|
|
@@ -36,7 +42,7 @@ export async function loadSession() {
|
|
|
36
42
|
}
|
|
37
43
|
export async function clearSession() {
|
|
38
44
|
const k = await getKeytar();
|
|
39
|
-
return k.deletePassword(RUNNER_KEYCHAIN_SERVICE,
|
|
45
|
+
return k.deletePassword(RUNNER_KEYCHAIN_SERVICE, sessionAccount());
|
|
40
46
|
}
|
|
41
47
|
/**
|
|
42
48
|
* AX-G1: a SECOND entry in the same keychain service, under a different account.
|
package/dist/keychain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,uBAAuB,EAAsB,MAAM,YAAY,CAAC;AAEzE,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAsB,MAAM,YAAY,CAAC;AAEzE;;0DAE0D;AAC1D,MAAM,UAAU,cAAc;IAC5B,OAAO,uBAAuB,EAAE,CAAC;AACnC,CAAC;AAQD,IAAI,MAAM,GAAsB,IAAI,CAAC;AAErC,KAAK,UAAU,SAAS;IACtB,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,qEAAqE;IACrE,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAqD,CAAC;IACzF,MAAM,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAe,CAAC;IAC5C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAsB;IACtD,MAAM,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC;IAC5B,MAAM,CAAC,CAAC,WAAW,CAAC,uBAAuB,EAAE,cAAc,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,WAAW,CAAC,uBAAuB,EAAE,cAAc,EAAE,CAAC,CAAC;IAC3E,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkB,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;QACnE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC;IAC5B,OAAO,CAAC,CAAC,cAAc,CAAC,uBAAuB,EAAE,cAAc,EAAE,CAAC,CAAC;AACrE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAe,EAAE,KAAc;IAC5D,MAAM,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC;IAC5B,MAAM,CAAC,CAAC,WAAW,CAAC,uBAAuB,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,OAAe;IAC/C,MAAM,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,WAAW,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC;IAClE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,qBAAqB;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAe;IAC7C,MAAM,CAAC,GAAG,MAAM,SAAS,EAAE,CAAC;IAC5B,OAAO,CAAC,CAAC,cAAc,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,IAAuB;IACjD,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type LocalRepoProbe } from "./index.js";
|
|
2
|
+
export interface AddLocalRepoOptions {
|
|
3
|
+
/** Override the repo name (defaults to the directory name). */
|
|
4
|
+
name?: string;
|
|
5
|
+
/** Attach the repo to this project slug as part of the registration. */
|
|
6
|
+
project?: string;
|
|
7
|
+
/** Test seam. */
|
|
8
|
+
out?: (line: string) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Probe a candidate directory. Every question is answered from the filesystem or
|
|
12
|
+
* a single read-only `git` call, and a failing `git` is an answer ("no origin"),
|
|
13
|
+
* never an exception — planLocalRepoRegistration decides what is fatal.
|
|
14
|
+
*/
|
|
15
|
+
export declare function probeLocalRepo(rawPath: string, nameOverride?: string | null): LocalRepoProbe;
|
|
16
|
+
export declare function addLocalRepoCommand(repoPath: string, opts?: AddLocalRepoOptions): Promise<void>;
|
|
17
|
+
export interface ListLocalReposOptions {
|
|
18
|
+
json?: boolean;
|
|
19
|
+
out?: (line: string) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What this machine believes it serves locally, and whether the server agrees.
|
|
23
|
+
* The two can disagree in both directions — a config entry whose grant was
|
|
24
|
+
* revoked, or a grant registered from another machine — and an operator
|
|
25
|
+
* debugging "why is my task not running" needs to see which side is missing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function listLocalReposCommand(opts?: ListLocalReposOptions): Promise<void>;
|
|
28
|
+
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/local-repo/command.ts"],"names":[],"mappings":"AAoCA,OAAO,EAIL,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAUD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CA0B5F;AAED,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAyDf;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,IAAI,CAAC,CA2Df"}
|