@tokenfactory/acc-runner 0.42.0 → 0.44.0
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 +52 -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/doctor.d.ts.map +1 -1
- package/dist/doctor.js +13 -3
- package/dist/doctor.js.map +1 -1
- package/dist/engines/account-probe.d.ts +24 -0
- package/dist/engines/account-probe.d.ts.map +1 -1
- package/dist/engines/account-probe.js +18 -1
- package/dist/engines/account-probe.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/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/machine-capabilities/index.d.ts +21 -0
- package/dist/machine-capabilities/index.d.ts.map +1 -0
- package/dist/machine-capabilities/index.js +21 -0
- package/dist/machine-capabilities/index.js.map +1 -0
- package/dist/machine-capabilities/probe.d.ts +61 -0
- package/dist/machine-capabilities/probe.d.ts.map +1 -0
- package/dist/machine-capabilities/probe.js +243 -0
- package/dist/machine-capabilities/probe.js.map +1 -0
- package/dist/machine-capabilities/redact.d.ts +42 -0
- package/dist/machine-capabilities/redact.d.ts.map +1 -0
- package/dist/machine-capabilities/redact.js +160 -0
- package/dist/machine-capabilities/redact.js.map +1 -0
- package/dist/machine-capabilities/report.d.ts +107 -0
- package/dist/machine-capabilities/report.d.ts.map +1 -0
- package/dist/machine-capabilities/report.js +211 -0
- package/dist/machine-capabilities/report.js.map +1 -0
- package/dist/machine-capabilities/types.d.ts +115 -0
- package/dist/machine-capabilities/types.d.ts.map +1 -0
- package/dist/machine-capabilities/types.js +11 -0
- package/dist/machine-capabilities/types.js.map +1 -0
- 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/migration-seq-guard.d.ts +78 -0
- package/dist/migration-seq-guard.d.ts.map +1 -0
- package/dist/migration-seq-guard.js +304 -0
- package/dist/migration-seq-guard.js.map +1 -0
- package/dist/node-version.d.ts +132 -0
- package/dist/node-version.d.ts.map +1 -0
- package/dist/node-version.js +318 -0
- package/dist/node-version.js.map +1 -0
- package/dist/prepr-gate.d.ts +120 -0
- package/dist/prepr-gate.d.ts.map +1 -1
- package/dist/prepr-gate.js +202 -3
- package/dist/prepr-gate.js.map +1 -1
- 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 +51 -1
- package/dist/provider-auth.d.ts.map +1 -1
- package/dist/provider-auth.js +83 -2
- package/dist/provider-auth.js.map +1 -1
- package/dist/runtime/review-chunking.d.ts +101 -0
- package/dist/runtime/review-chunking.d.ts.map +1 -0
- package/dist/runtime/review-chunking.js +192 -0
- package/dist/runtime/review-chunking.js.map +1 -0
- package/dist/runtime/review-diff.d.ts +30 -0
- package/dist/runtime/review-diff.d.ts.map +1 -0
- package/dist/runtime/review-diff.js +59 -0
- package/dist/runtime/review-diff.js.map +1 -0
- package/dist/runtime/reviewer.d.ts +40 -19
- package/dist/runtime/reviewer.d.ts.map +1 -1
- package/dist/runtime/reviewer.js +283 -68
- package/dist/runtime/reviewer.js.map +1 -1
- package/dist/serve-scope/command.d.ts +12 -0
- package/dist/serve-scope/command.d.ts.map +1 -0
- package/dist/serve-scope/command.js +89 -0
- package/dist/serve-scope/command.js.map +1 -0
- package/dist/serve-scope/index.d.ts +130 -0
- package/dist/serve-scope/index.d.ts.map +1 -0
- package/dist/serve-scope/index.js +148 -0
- package/dist/serve-scope/index.js.map +1 -0
- package/dist/task-runner.d.ts +22 -2
- package/dist/task-runner.d.ts.map +1 -1
- package/dist/task-runner.js +142 -25
- package/dist/task-runner.js.map +1 -1
- package/dist/watch.d.ts +21 -0
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +109 -1
- package/dist/watch.js.map +1 -1
- package/package.json +1 -1
|
@@ -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,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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LOCAL-REPO-CLI — `acc repo add-local <path>` and `acc repo list-local`.
|
|
3
|
+
*
|
|
4
|
+
* The plumbing around ./index.ts's pure rules: probe the directory, call
|
|
5
|
+
* acc.register_local_repo under the operator's OWN session, and write the
|
|
6
|
+
* resulting slug→path pair into this machine's config.json.
|
|
7
|
+
*
|
|
8
|
+
* WHY THE PERSON LANE AND NOT THE RUNNER LANE
|
|
9
|
+
* ----------------------------------------------------------------------------
|
|
10
|
+
* The runner session is an ACC-signed JWT that impersonates a machine's owner.
|
|
11
|
+
* It would authenticate fine, and using it would be wrong: the row being written
|
|
12
|
+
* is "THIS human grants THEMSELVES access to a repo on their disk", and the
|
|
13
|
+
* server refuses a null auth.uid() on that path for exactly that reason. So this
|
|
14
|
+
* command runs on the generalized device flow's person credential (`acc login`),
|
|
15
|
+
* the same one `acc chat` and `acc task list` use, and the machine identity is
|
|
16
|
+
* computed locally rather than taken from a session — a person may register a
|
|
17
|
+
* repo before they have ever enrolled this host as a runner.
|
|
18
|
+
*
|
|
19
|
+
* ORDER MATTERS: the server write happens FIRST, the local config write second.
|
|
20
|
+
* A local map entry pointing at a repo the server does not know is a silently
|
|
21
|
+
* dead directory reference; a server registration whose local path failed to
|
|
22
|
+
* save is a legible error the operator can fix by re-running (both writes are
|
|
23
|
+
* idempotent upserts).
|
|
24
|
+
*/
|
|
25
|
+
import { execFileSync } from "node:child_process";
|
|
26
|
+
import fs from "node:fs";
|
|
27
|
+
import os from "node:os";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import chalk from "chalk";
|
|
30
|
+
import { loadLocalReposFromConfigFile, userConfigFilePath, writeLocalRepoToConfigFile, } from "../config.js";
|
|
31
|
+
import { machineIdentity } from "../login.js";
|
|
32
|
+
import { userClient } from "../user-cli/client.js";
|
|
33
|
+
import { isMigrationPending, localRepoDisplayName, planLocalRepoRegistration, } from "./index.js";
|
|
34
|
+
/** `~/code/app` → `/Users/me/code/app`. Absolute paths pass through. */
|
|
35
|
+
function expandHome(p) {
|
|
36
|
+
const trimmed = p.trim();
|
|
37
|
+
if (trimmed === "~")
|
|
38
|
+
return os.homedir();
|
|
39
|
+
if (trimmed.startsWith("~/"))
|
|
40
|
+
return path.resolve(os.homedir(), trimmed.slice(2));
|
|
41
|
+
return path.resolve(trimmed);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Probe a candidate directory. Every question is answered from the filesystem or
|
|
45
|
+
* a single read-only `git` call, and a failing `git` is an answer ("no origin"),
|
|
46
|
+
* never an exception — planLocalRepoRegistration decides what is fatal.
|
|
47
|
+
*/
|
|
48
|
+
export function probeLocalRepo(rawPath, nameOverride) {
|
|
49
|
+
const abs = expandHome(rawPath);
|
|
50
|
+
let isDirectory = false;
|
|
51
|
+
try {
|
|
52
|
+
isDirectory = fs.statSync(abs).isDirectory();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
isDirectory = false;
|
|
56
|
+
}
|
|
57
|
+
// A `.git` FILE (not directory) is a worktree or submodule — still a git repo,
|
|
58
|
+
// so existsSync rather than an isDirectory check.
|
|
59
|
+
const isGitRepo = isDirectory && fs.existsSync(path.join(abs, ".git"));
|
|
60
|
+
let originUrl = null;
|
|
61
|
+
if (isGitRepo) {
|
|
62
|
+
try {
|
|
63
|
+
const out = execFileSync("git", ["-C", abs, "remote", "get-url", "origin"], {
|
|
64
|
+
encoding: "utf8",
|
|
65
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
66
|
+
});
|
|
67
|
+
originUrl = out.trim() || null;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
originUrl = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { path: abs, isDirectory, isGitRepo, originUrl, nameOverride: nameOverride ?? null };
|
|
74
|
+
}
|
|
75
|
+
export async function addLocalRepoCommand(repoPath, opts = {}) {
|
|
76
|
+
const out = opts.out ?? ((line) => console.log(line));
|
|
77
|
+
const plan = planLocalRepoRegistration(probeLocalRepo(repoPath, opts.name));
|
|
78
|
+
const machine = machineIdentity();
|
|
79
|
+
const sb = await userClient();
|
|
80
|
+
const { data, error } = await sb.rpc("register_local_repo", {
|
|
81
|
+
p_name: plan.name,
|
|
82
|
+
p_machine: machine,
|
|
83
|
+
p_project_slug: opts.project?.trim() || null,
|
|
84
|
+
});
|
|
85
|
+
if (error) {
|
|
86
|
+
if (isMigrationPending(error)) {
|
|
87
|
+
throw new Error("local repos are unavailable — migration 0318 has not been applied to this " +
|
|
88
|
+
"environment yet. Until it is, attach repos through the GitHub App path.");
|
|
89
|
+
}
|
|
90
|
+
throw new Error(`could not register ${plan.name}: ${error.message}`);
|
|
91
|
+
}
|
|
92
|
+
const result = data;
|
|
93
|
+
if (!result?.repo) {
|
|
94
|
+
throw new Error("register_local_repo returned no repo slug — nothing was recorded locally");
|
|
95
|
+
}
|
|
96
|
+
const written = writeLocalRepoToConfigFile(result.repo, plan.path);
|
|
97
|
+
out(chalk.green(`✓ registered ${result.repo}`));
|
|
98
|
+
out(` path ${plan.path} ${chalk.dim("(this machine only)")}`);
|
|
99
|
+
out(` machine ${machine}`);
|
|
100
|
+
out(` access ${result.level} · source ${result.source} · you only`);
|
|
101
|
+
if (result.project_slug) {
|
|
102
|
+
out(` project ${result.project_slug}`);
|
|
103
|
+
}
|
|
104
|
+
out(chalk.dim(` recorded in ${written.path}`));
|
|
105
|
+
for (const warning of plan.warnings) {
|
|
106
|
+
out(chalk.yellow(` ! ${warning}`));
|
|
107
|
+
}
|
|
108
|
+
if (written.addedToServedRepos) {
|
|
109
|
+
out(chalk.yellow(" ! Added the slug to this machine's served-repo scope. Restart `acc watch` " +
|
|
110
|
+
"so it re-advertises its capabilities."));
|
|
111
|
+
}
|
|
112
|
+
out("");
|
|
113
|
+
out("Next: run `acc watch` on this machine to execute tasks for this repo.");
|
|
114
|
+
if (!result.project_slug) {
|
|
115
|
+
out(chalk.dim("It is now selectable in the project-create repo step, or attach it to an " +
|
|
116
|
+
"existing project with --project <slug>."));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* What this machine believes it serves locally, and whether the server agrees.
|
|
121
|
+
* The two can disagree in both directions — a config entry whose grant was
|
|
122
|
+
* revoked, or a grant registered from another machine — and an operator
|
|
123
|
+
* debugging "why is my task not running" needs to see which side is missing.
|
|
124
|
+
*/
|
|
125
|
+
export async function listLocalReposCommand(opts = {}) {
|
|
126
|
+
const out = opts.out ?? ((line) => console.log(line));
|
|
127
|
+
const local = loadLocalReposFromConfigFile();
|
|
128
|
+
let granted = [];
|
|
129
|
+
let grantError = null;
|
|
130
|
+
try {
|
|
131
|
+
const sb = await userClient();
|
|
132
|
+
// RLS scopes acc.user_repo_access to auth.uid(), so no user predicate is
|
|
133
|
+
// needed — and none should be added: the caller's own rows are the answer.
|
|
134
|
+
const { data, error } = await sb
|
|
135
|
+
.from("user_repo_access")
|
|
136
|
+
.select("repo, level, source")
|
|
137
|
+
.eq("source", "cli");
|
|
138
|
+
if (error)
|
|
139
|
+
throw new Error(error.message);
|
|
140
|
+
granted = (data ?? [])
|
|
141
|
+
.map((r) => r.repo)
|
|
142
|
+
.filter((r) => typeof r === "string");
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// Offline / signed out is not a reason to refuse to print the local half.
|
|
146
|
+
grantError = err.message;
|
|
147
|
+
}
|
|
148
|
+
if (opts.json) {
|
|
149
|
+
out(JSON.stringify({
|
|
150
|
+
config_path: userConfigFilePath(),
|
|
151
|
+
local_repos: local,
|
|
152
|
+
granted_repos: granted,
|
|
153
|
+
grant_error: grantError,
|
|
154
|
+
}, null, 2));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const slugs = [...new Set([...Object.keys(local), ...granted.map((r) => r.toLowerCase())])].sort();
|
|
158
|
+
if (slugs.length === 0) {
|
|
159
|
+
out("No local repos registered on this machine.");
|
|
160
|
+
out(chalk.dim("Add one with `acc repo add-local <path>`."));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
for (const slug of slugs) {
|
|
164
|
+
const dir = local[slug];
|
|
165
|
+
const isGranted = granted.some((r) => r.toLowerCase() === slug);
|
|
166
|
+
const state = !dir
|
|
167
|
+
? chalk.yellow("registered on another machine — no local path here")
|
|
168
|
+
: !isGranted && !grantError
|
|
169
|
+
? chalk.yellow("no server grant — re-run `acc repo add-local`")
|
|
170
|
+
: chalk.dim(dir);
|
|
171
|
+
out(` ${localRepoDisplayName(slug).padEnd(24)} ${slug}`);
|
|
172
|
+
out(` ${" ".repeat(24)} ${state}`);
|
|
173
|
+
}
|
|
174
|
+
if (grantError) {
|
|
175
|
+
out(chalk.yellow(`! could not read your server grants: ${grantError}`));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/local-repo/command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LOCAL-REPO-CLI — the decidable half of `acc repo add-local`.
|
|
3
|
+
*
|
|
4
|
+
* Everything here is pure: given a path on disk and a few probe results, decide
|
|
5
|
+
* what to register, what to refuse, and what to warn about. The plumbing that
|
|
6
|
+
* needs a keychain, a network and a filesystem lives in ./command.ts, the same
|
|
7
|
+
* split serve-scope uses — so every rule below is unit-testable without either.
|
|
8
|
+
*
|
|
9
|
+
* WHAT A "LOCAL REPO" IS, AND WHAT IT IS NOT
|
|
10
|
+
* ----------------------------------------------------------------------------
|
|
11
|
+
* It is a git working copy on THIS machine that ACC may run tasks in, attached
|
|
12
|
+
* to a project without the GitHub App being installed anywhere. That is the
|
|
13
|
+
* whole of it. In particular it is NOT:
|
|
14
|
+
*
|
|
15
|
+
* * a mirror of a GitHub repo ACC will keep in sync — nothing here clones,
|
|
16
|
+
* fetches or pushes on the server's behalf;
|
|
17
|
+
* * a repo other people on the team can work. The grant the server records is
|
|
18
|
+
* scoped to the registering user, and the path is known only to this
|
|
19
|
+
* machine, so a teammate's runner has neither the right nor the directory;
|
|
20
|
+
* * a way to skip a GitHub write grant on a repo that IS on GitHub. The
|
|
21
|
+
* server derives the slug from the caller's user id into a reserved
|
|
22
|
+
* `local.…` namespace precisely so this cannot be that.
|
|
23
|
+
*
|
|
24
|
+
* WHY THE PATH NEVER LEAVES THE MACHINE
|
|
25
|
+
* ----------------------------------------------------------------------------
|
|
26
|
+
* The server needs to know "machine M serves repo R" to route a task; it does
|
|
27
|
+
* not need to know that R is at /Users/priya/code/side-project. Sending the path
|
|
28
|
+
* would put a filesystem layout — usernames, project names, directory
|
|
29
|
+
* structure — into a shared database for no routing benefit, so the path stays
|
|
30
|
+
* in this machine's own config.json (`local-repos`) and the runner resolves it
|
|
31
|
+
* at task time. See docs/acc/LOCAL_REPO_CLI_SETUP.md.
|
|
32
|
+
*/
|
|
33
|
+
/** The reserved namespace the server mints local slugs into (acc.local_repo_slug). */
|
|
34
|
+
export declare const LOCAL_REPO_PREFIX = "local.";
|
|
35
|
+
/**
|
|
36
|
+
* Is this slug in the reserved local/CLI namespace? Mirrors
|
|
37
|
+
* acc.is_local_repo_slug (migration 0318) and src/lib/local-repo.ts. A GitHub
|
|
38
|
+
* owner segment can never contain a '.', so the two namespaces cannot collide.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isLocalRepoSlug(repo: string | null | undefined): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* The human-facing name of a local repo — the part after the '/', without the
|
|
43
|
+
* per-user hash fragment that makes the slug unique. `local.9f2c1a/my-app` reads
|
|
44
|
+
* as "my-app (local)" in a list; the full slug is still what gets matched.
|
|
45
|
+
*/
|
|
46
|
+
export declare function localRepoDisplayName(repo: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Slugify a repo NAME the way acc.local_repo_slug does server-side, so the CLI
|
|
49
|
+
* can predict (and preview) the slug it is about to be handed. Kept in sync
|
|
50
|
+
* deliberately — the server is authoritative and the CLI stores whatever comes
|
|
51
|
+
* back, so a drift here is a cosmetic preview bug, never a wrong write.
|
|
52
|
+
*/
|
|
53
|
+
export declare function sanitizeLocalRepoName(raw: string): string;
|
|
54
|
+
/** What ./command.ts probed about the directory the operator pointed at. */
|
|
55
|
+
export interface LocalRepoProbe {
|
|
56
|
+
/** The resolved absolute path. */
|
|
57
|
+
path: string;
|
|
58
|
+
/** Does the path exist and is it a directory? */
|
|
59
|
+
isDirectory: boolean;
|
|
60
|
+
/** Is it a git repository (a `.git` entry at the root)? */
|
|
61
|
+
isGitRepo: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* `git remote get-url origin`, or null when there is no origin. Not a
|
|
64
|
+
* requirement — it only decides which warning the operator gets.
|
|
65
|
+
*/
|
|
66
|
+
originUrl: string | null;
|
|
67
|
+
/** Explicit `--name`, when the operator overrode the directory name. */
|
|
68
|
+
nameOverride?: string | null;
|
|
69
|
+
}
|
|
70
|
+
export interface LocalRepoPlan {
|
|
71
|
+
/** The name to send; the server derives the slug from it. */
|
|
72
|
+
name: string;
|
|
73
|
+
/** The absolute path to record in config.json. */
|
|
74
|
+
path: string;
|
|
75
|
+
/** The slug the server is expected to mint, for the pre-flight preview. */
|
|
76
|
+
previewSlug: string;
|
|
77
|
+
/**
|
|
78
|
+
* Advisory notes to print before the write. A plan always registers — these
|
|
79
|
+
* are the things an operator would rather learn now than at task time.
|
|
80
|
+
*/
|
|
81
|
+
warnings: string[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Decide what to register for a probed directory, or throw with the reason it
|
|
85
|
+
* cannot be registered.
|
|
86
|
+
*
|
|
87
|
+
* Refusals are limited to the two facts that make registration meaningless: the
|
|
88
|
+
* path is not a directory, or it is not a git repository (the runner's whole
|
|
89
|
+
* task loop is `git` — branch, commit, diff — so a plain folder would fail at
|
|
90
|
+
* the first step rather than at a legible place).
|
|
91
|
+
*
|
|
92
|
+
* A missing `origin` is NOT a refusal. Running tasks in a local working copy is
|
|
93
|
+
* useful on its own; it is opening a PR at the end that needs a remote, and the
|
|
94
|
+
* warning says exactly that instead of pre-emptively blocking the register.
|
|
95
|
+
*/
|
|
96
|
+
export declare function planLocalRepoRegistration(probe: LocalRepoProbe): LocalRepoPlan;
|
|
97
|
+
/** The shape acc.register_local_repo returns (migration 0318 §7). */
|
|
98
|
+
export interface RegisterLocalRepoResult {
|
|
99
|
+
repo: string;
|
|
100
|
+
machine: string;
|
|
101
|
+
org_id: string;
|
|
102
|
+
assignment_id: string | null;
|
|
103
|
+
project_id: string | null;
|
|
104
|
+
project_slug: string | null;
|
|
105
|
+
level: string;
|
|
106
|
+
source: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Is this error the "migration 0318 has not been applied here yet" shape?
|
|
110
|
+
* HALT-before-apply means every environment has a window where this command
|
|
111
|
+
* ships and the RPC does not exist; "run psql" and "something is broken" have
|
|
112
|
+
* different remedies, so the command says which. Mirrors serve-scope's
|
|
113
|
+
* isMigrationPending.
|
|
114
|
+
*/
|
|
115
|
+
export declare function isMigrationPending(err: {
|
|
116
|
+
message?: string;
|
|
117
|
+
code?: string;
|
|
118
|
+
} | null): boolean;
|
|
119
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/local-repo/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAKxE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;IACrB,2DAA2D;IAC3D,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,cAAc,GAAG,aAAa,CAuC9E;AAYD,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,OAAO,CAY3F"}
|