agent-nuvira 3.3.1 → 3.3.3
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 +5 -8
- package/dist/agents/agents/runner.d.ts +9 -0
- package/dist/agents/agents/runner.d.ts.map +1 -1
- package/dist/agents/agents/runner.js +34 -12
- package/dist/agents/agents/runner.js.map +1 -1
- package/dist/agents/agents/writer-tool-calling.d.ts.map +1 -1
- package/dist/agents/agents/writer-tool-calling.js +7 -2
- package/dist/agents/agents/writer-tool-calling.js.map +1 -1
- package/dist/agents/agents/writer.d.ts.map +1 -1
- package/dist/agents/agents/writer.js +7 -1
- package/dist/agents/agents/writer.js.map +1 -1
- package/dist/agents/artifact-verification.d.ts +102 -0
- package/dist/agents/artifact-verification.d.ts.map +1 -0
- package/dist/agents/artifact-verification.js +216 -0
- package/dist/agents/artifact-verification.js.map +1 -0
- package/dist/agents/checkpoint-store.d.ts +73 -0
- package/dist/agents/checkpoint-store.d.ts.map +1 -1
- package/dist/agents/checkpoint-store.js +161 -0
- package/dist/agents/checkpoint-store.js.map +1 -1
- package/dist/agents/credential-store.d.ts +120 -0
- package/dist/agents/credential-store.d.ts.map +1 -1
- package/dist/agents/credential-store.js +358 -19
- package/dist/agents/credential-store.js.map +1 -1
- package/dist/agents/orchestrator.d.ts +10 -1
- package/dist/agents/orchestrator.d.ts.map +1 -1
- package/dist/agents/orchestrator.js +234 -75
- package/dist/agents/orchestrator.js.map +1 -1
- package/dist/agents/phase-engine.d.ts +61 -0
- package/dist/agents/phase-engine.d.ts.map +1 -1
- package/dist/agents/phase-engine.js +68 -2
- package/dist/agents/phase-engine.js.map +1 -1
- package/dist/agents/prompt-assembly.d.ts +11 -0
- package/dist/agents/prompt-assembly.d.ts.map +1 -1
- package/dist/agents/prompt-assembly.js +17 -0
- package/dist/agents/prompt-assembly.js.map +1 -1
- package/dist/agents/release-preflight.d.ts +121 -0
- package/dist/agents/release-preflight.d.ts.map +1 -0
- package/dist/agents/release-preflight.js +259 -0
- package/dist/agents/release-preflight.js.map +1 -0
- package/dist/agents/release-runner.d.ts +157 -0
- package/dist/agents/release-runner.d.ts.map +1 -0
- package/dist/agents/release-runner.js +719 -0
- package/dist/agents/release-runner.js.map +1 -0
- package/dist/agents/step-handoff.d.ts +190 -0
- package/dist/agents/step-handoff.d.ts.map +1 -0
- package/dist/agents/step-handoff.js +443 -0
- package/dist/agents/step-handoff.js.map +1 -0
- package/dist/cli/benchmark.d.ts.map +1 -1
- package/dist/cli/benchmark.js +11 -4
- package/dist/cli/benchmark.js.map +1 -1
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +25 -4
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/cli-program.d.ts.map +1 -1
- package/dist/cli/cli-program.js +3 -0
- package/dist/cli/cli-program.js.map +1 -1
- package/dist/cli/credentials.d.ts +28 -0
- package/dist/cli/credentials.d.ts.map +1 -0
- package/dist/cli/credentials.js +213 -0
- package/dist/cli/credentials.js.map +1 -0
- package/dist/cli/failover-runner.d.ts.map +1 -1
- package/dist/cli/failover-runner.js +9 -3
- package/dist/cli/failover-runner.js.map +1 -1
- package/dist/cli/loop-executor.d.ts.map +1 -1
- package/dist/cli/loop-executor.js +74 -7
- package/dist/cli/loop-executor.js.map +1 -1
- package/dist/cli/nlu.d.ts.map +1 -1
- package/dist/cli/nlu.js +8 -2
- package/dist/cli/nlu.js.map +1 -1
- package/dist/cli/publish.d.ts +22 -0
- package/dist/cli/publish.d.ts.map +1 -1
- package/dist/cli/publish.js +109 -13
- package/dist/cli/publish.js.map +1 -1
- package/dist/config/live-credentials.d.ts +57 -0
- package/dist/config/live-credentials.d.ts.map +1 -0
- package/dist/config/live-credentials.js +126 -0
- package/dist/config/live-credentials.js.map +1 -0
- package/dist/config/paths.d.ts +18 -0
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +25 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/federation/a2a-types.js +1 -1
- package/dist/federation/a2a-types.js.map +1 -1
- package/dist/inference/model-validator.d.ts +6 -1
- package/dist/inference/model-validator.d.ts.map +1 -1
- package/dist/inference/model-validator.js +7 -2
- package/dist/inference/model-validator.js.map +1 -1
- package/dist/inference/route-resolver.d.ts +116 -0
- package/dist/inference/route-resolver.d.ts.map +1 -0
- package/dist/inference/route-resolver.js +159 -0
- package/dist/inference/route-resolver.js.map +1 -0
- package/dist/learning/autonomy-policy.d.ts +18 -0
- package/dist/learning/autonomy-policy.d.ts.map +1 -1
- package/dist/learning/autonomy-policy.js +35 -0
- package/dist/learning/autonomy-policy.js.map +1 -1
- package/dist/learning/reasoning-trace.d.ts +1 -1
- package/dist/learning/reasoning-trace.d.ts.map +1 -1
- package/dist/learning/reasoning-trace.js +25 -1
- package/dist/learning/reasoning-trace.js.map +1 -1
- package/dist/learning/resilient-call.d.ts.map +1 -1
- package/dist/learning/resilient-call.js +29 -17
- package/dist/learning/resilient-call.js.map +1 -1
- package/dist/learning/skill-store.d.ts.map +1 -1
- package/dist/learning/skill-store.js +30 -9
- package/dist/learning/skill-store.js.map +1 -1
- package/dist/mcp/catalog.js +2 -3
- package/dist/mcp/catalog.js.map +1 -1
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +5 -3
- package/dist/mcp/manager.js.map +1 -1
- package/dist/skills/secret-capture.d.ts.map +1 -1
- package/dist/skills/secret-capture.js +9 -4
- package/dist/skills/secret-capture.js.map +1 -1
- package/dist/tools/credentials-tool.d.ts +24 -0
- package/dist/tools/credentials-tool.d.ts.map +1 -0
- package/dist/tools/credentials-tool.js +125 -0
- package/dist/tools/credentials-tool.js.map +1 -0
- package/dist/tools/git-tool.d.ts +25 -7
- package/dist/tools/git-tool.d.ts.map +1 -1
- package/dist/tools/git-tool.js +155 -15
- package/dist/tools/git-tool.js.map +1 -1
- package/dist/tools/loop-project-context.d.ts.map +1 -1
- package/dist/tools/loop-project-context.js +16 -0
- package/dist/tools/loop-project-context.js.map +1 -1
- package/dist/tools/loop-route-feed.d.ts +57 -0
- package/dist/tools/loop-route-feed.d.ts.map +1 -0
- package/dist/tools/loop-route-feed.js +101 -0
- package/dist/tools/loop-route-feed.js.map +1 -0
- package/dist/tools/pipeline-tool.d.ts.map +1 -1
- package/dist/tools/pipeline-tool.js +8 -2
- package/dist/tools/pipeline-tool.js.map +1 -1
- package/dist/tools/publish-tool.d.ts.map +1 -1
- package/dist/tools/publish-tool.js +76 -10
- package/dist/tools/publish-tool.js.map +1 -1
- package/dist/tools/registry.d.ts +21 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +44 -6
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/run-terminal.d.ts +9 -0
- package/dist/tools/run-terminal.d.ts.map +1 -1
- package/dist/tools/run-terminal.js +80 -2
- package/dist/tools/run-terminal.js.map +1 -1
- package/dist/tools/tool-loop.d.ts +12 -5
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +148 -0
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/tools/toolsets.js +2 -2
- package/dist/tools/toolsets.js.map +1 -1
- package/dist/workflow/registry.js +1 -1
- package/dist/workflow/registry.js.map +1 -1
- package/package.json +5 -4
- package/src/web-dashboard/public/assets/{index-CjvoBhlz.js → index-CxDj7p6i.js} +2 -2
- package/src/web-dashboard/public/assets/{index-CjvoBhlz.js.map → index-CxDj7p6i.js.map} +1 -1
- package/src/web-dashboard/public/index.html +1 -1
|
@@ -42,6 +42,32 @@ export interface PublishCredentials {
|
|
|
42
42
|
git: GitCredentials;
|
|
43
43
|
npm: NpmCredentials;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Release credentials may be STORED, not merely exported for one session.
|
|
47
|
+
*
|
|
48
|
+
* Why this exists: this store used to read only `process.env` and `.npmrc`,
|
|
49
|
+
* and `collectAll()` was the only way to mark credentials as collected. So an
|
|
50
|
+
* agent asked to release needed both tokens re-exported in its environment
|
|
51
|
+
* every session, and the `publish` TOOL — which may never prompt — could never
|
|
52
|
+
* set them up at all: it called `setupGitCredentials()` / `setupNpmAuth()`
|
|
53
|
+
* without `collectAll()`, both threw, and a single `try/catch` swallowed the
|
|
54
|
+
* pair. A tool-driven release therefore ran with NO credentials while
|
|
55
|
+
* reporting a clean pipeline.
|
|
56
|
+
*
|
|
57
|
+
* Tokens now persist in the nuvira env file (0600, resolved by
|
|
58
|
+
* `resolveNuviraEnvFile` — outside the repo, never in shell history), the SAME
|
|
59
|
+
* store the skill-secret path already writes. One place a credential lives.
|
|
60
|
+
*/
|
|
61
|
+
export declare const RELEASE_CREDENTIAL_KEYS: {
|
|
62
|
+
/** GitHub PAT / OAuth token used for HTTPS push. */
|
|
63
|
+
readonly gitToken: "GITHUB_TOKEN";
|
|
64
|
+
/** Git username paired with {@link RELEASE_CREDENTIAL_KEYS.gitToken}. */
|
|
65
|
+
readonly gitUsername: "GIT_USERNAME";
|
|
66
|
+
/** npm automation token used for `npm publish`. */
|
|
67
|
+
readonly npmToken: "NPM_TOKEN";
|
|
68
|
+
/** Optional non-default npm registry. */
|
|
69
|
+
readonly npmRegistry: "NPM_REGISTRY";
|
|
70
|
+
};
|
|
45
71
|
export declare class CredentialStore {
|
|
46
72
|
private _git;
|
|
47
73
|
private _npm;
|
|
@@ -61,6 +87,24 @@ export declare class CredentialStore {
|
|
|
61
87
|
* Does NOT prompt — call collectAll() for interactive collection.
|
|
62
88
|
*/
|
|
63
89
|
constructor();
|
|
90
|
+
/**
|
|
91
|
+
* Non-interactive initialisation.
|
|
92
|
+
*
|
|
93
|
+
* Adopts whatever was auto-detected — environment variables, the persisted
|
|
94
|
+
* store, and the project `.npmrc` — and marks the store collected, so
|
|
95
|
+
* {@link setupGitCredentials} / {@link setupNpmAuth} can run with NO TTY.
|
|
96
|
+
*
|
|
97
|
+
* This is the method the `publish` TOOL must call: a tool call may never
|
|
98
|
+
* block on a prompt, whereas `nuvira publish` on a terminal still uses
|
|
99
|
+
* `collectAll()` to ask for anything missing.
|
|
100
|
+
*/
|
|
101
|
+
initialize(): PublishCredentials;
|
|
102
|
+
/**
|
|
103
|
+
* Validate the detected credentials against the live services.
|
|
104
|
+
* Best-effort and NON-FATAL: a network hiccup reports `unknown`, never a
|
|
105
|
+
* failure, so a preflight check can advise without blocking a release.
|
|
106
|
+
*/
|
|
107
|
+
verify(): Promise<PublishVerification>;
|
|
64
108
|
/**
|
|
65
109
|
* Collect missing credentials interactively.
|
|
66
110
|
* Skips prompts for already-detected values.
|
|
@@ -99,4 +143,80 @@ export declare function checkNpmCredentials(): boolean;
|
|
|
99
143
|
* Get a human-readable summary of the current credential status.
|
|
100
144
|
*/
|
|
101
145
|
export declare function getCredentialStatus(): string;
|
|
146
|
+
/** Where a credential was found. */
|
|
147
|
+
export type CredentialOrigin = 'env' | 'stored' | 'npmrc' | 'ssh' | 'none';
|
|
148
|
+
/** One row of the release-credential status table. */
|
|
149
|
+
export interface ReleaseCredentialRow {
|
|
150
|
+
/** Env var name the credential lives under. */
|
|
151
|
+
key: string;
|
|
152
|
+
/** What the credential is for. */
|
|
153
|
+
role: 'git' | 'npm';
|
|
154
|
+
/** Whether a usable value was found anywhere. */
|
|
155
|
+
set: boolean;
|
|
156
|
+
/** Masked value — enough to identify the key, never the key itself. */
|
|
157
|
+
masked?: string;
|
|
158
|
+
/** Where the value came from. */
|
|
159
|
+
origin: CredentialOrigin;
|
|
160
|
+
}
|
|
161
|
+
/** Result of a live credential probe against GitHub / the npm registry. */
|
|
162
|
+
export interface CredentialProbe {
|
|
163
|
+
/** False when the check could not be completed (offline, timeout). */
|
|
164
|
+
checked: boolean;
|
|
165
|
+
/** True only when the service CONFIRMED the credential works. */
|
|
166
|
+
ok: boolean;
|
|
167
|
+
/** Human-readable outcome. */
|
|
168
|
+
detail: string;
|
|
169
|
+
}
|
|
170
|
+
/** Both probes from {@link CredentialStore.verify}. */
|
|
171
|
+
export interface PublishVerification {
|
|
172
|
+
git: CredentialProbe;
|
|
173
|
+
npm: CredentialProbe;
|
|
174
|
+
}
|
|
175
|
+
/** The full release-credential picture, for the CLI and the tool. */
|
|
176
|
+
export interface ReleaseCredentialStatus {
|
|
177
|
+
rows: ReleaseCredentialRow[];
|
|
178
|
+
git: {
|
|
179
|
+
canPush: boolean;
|
|
180
|
+
remoteUrl?: string;
|
|
181
|
+
repoSlug?: string;
|
|
182
|
+
origin: CredentialOrigin;
|
|
183
|
+
};
|
|
184
|
+
npm: {
|
|
185
|
+
canPublish: boolean;
|
|
186
|
+
registry: string;
|
|
187
|
+
origin: CredentialOrigin;
|
|
188
|
+
};
|
|
189
|
+
/** Path of the store the values are persisted to. */
|
|
190
|
+
storePath: string;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Describe every release credential, masked, and say whether a release could
|
|
194
|
+
* run right now. Never returns a raw secret — only {@link maskSecret} shapes.
|
|
195
|
+
*/
|
|
196
|
+
export declare function releaseCredentialStatus(): ReleaseCredentialStatus;
|
|
197
|
+
/**
|
|
198
|
+
* Persist a release credential so it survives the session.
|
|
199
|
+
*
|
|
200
|
+
* Writes to the nuvira env file (created 0600 by {@link saveEnvValue}), which
|
|
201
|
+
* lives OUTSIDE the repository — a token in a committed file is a leak, and a
|
|
202
|
+
* token in a shell export is gone at the next login.
|
|
203
|
+
*/
|
|
204
|
+
export declare function storeReleaseCredential(name: string, value: string): {
|
|
205
|
+
success: boolean;
|
|
206
|
+
path: string;
|
|
207
|
+
reason?: string;
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Forget a stored release credential.
|
|
211
|
+
* A value that was never stored is a SUCCESS — the caller's intent already
|
|
212
|
+
* holds, and reporting failure would only surface a pointless error.
|
|
213
|
+
*/
|
|
214
|
+
export declare function forgetReleaseCredential(name: string): {
|
|
215
|
+
success: boolean;
|
|
216
|
+
path: string;
|
|
217
|
+
removed: boolean;
|
|
218
|
+
reason?: string;
|
|
219
|
+
};
|
|
220
|
+
/** Whether `name` is a credential this module is willing to store. */
|
|
221
|
+
export declare function isReleaseCredentialKey(name: string): boolean;
|
|
102
222
|
//# sourceMappingURL=credential-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential-store.d.ts","sourceRoot":"","sources":["../../src/agents/credential-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"credential-store.d.ts","sourceRoot":"","sources":["../../src/agents/credential-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAmBH,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,cAAc,CAAC;IACpB,GAAG,EAAE,cAAc,CAAC;CACrB;AAID;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,uBAAuB;IAClC,oDAAoD;;IAEpD,yEAAyE;;IAEzE,mDAAmD;;IAEnD,yCAAyC;;CAEjC,CAAC;AAsIX,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,IAAI,CAAyC;IACrD,OAAO,CAAC,UAAU,CAAS;IAE3B,kCAAkC;IAClC,IAAI,GAAG,IAAI,cAAc,CAExB;IAED,kCAAkC;IAClC,IAAI,GAAG,IAAI,cAAc,CAExB;IAED,8CAA8C;IAC9C,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,wDAAwD;IACxD,IAAI,OAAO,IAAI,OAAO,CAIrB;IAED,2DAA2D;IAC3D,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;OAGG;;IA0BH;;;;;;;;;;OAUG;IACH,UAAU,IAAI,kBAAkB;IAKhC;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAoB5C;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAmI/C;;;;;;OAMG;IACH,mBAAmB,IAAI,IAAI;IAsH3B;;;;;OAKG;IACH,YAAY,IAAI,IAAI;IAyBpB;;;OAGG;IACH,OAAO,IAAI,IAAI;CAqBhB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAe5C;AAID,oCAAoC;AACpC,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAE3E,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,iDAAiD;IACjD,GAAG,EAAE,OAAO,CAAC;IACb,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,OAAO,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,EAAE,EAAE,OAAO,CAAC;IACZ,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,uDAAuD;AACvD,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,eAAe,CAAC;IACrB,GAAG,EAAE,eAAe,CAAC;CACtB;AAED,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,oBAAoB,EAAE,CAAC;IAC7B,GAAG,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAAC;IAC3F,GAAG,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAAC;IACzE,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB;AAoDD;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,uBAAuB,CA8DjE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAkBrD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,GACX;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAIvE;AAED,sEAAsE;AACtE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG5D"}
|
|
@@ -16,13 +16,60 @@
|
|
|
16
16
|
* creds.setupGitCredentials(); // Sets GIT_ASKPASS for HTTPS auth
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
|
-
import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
19
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, chmodSync } from 'node:fs';
|
|
20
|
+
import { resolveNuviraEnvFile } from '../config/paths.js';
|
|
20
21
|
import { homedir, tmpdir } from 'node:os';
|
|
21
22
|
import { join } from 'node:path';
|
|
22
23
|
import inquirer from 'inquirer';
|
|
23
24
|
import { execSync } from 'node:child_process';
|
|
24
25
|
import { logger } from '../utils/logger.js';
|
|
25
|
-
|
|
26
|
+
import { maskSecret } from '../enterprise/secrets.js';
|
|
27
|
+
import { deleteEnvValue, getEnvVarValue, loadEnvFile, saveEnvValue, } from '../skills/secret-capture.js';
|
|
28
|
+
// ─── Persisted release credentials ──────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Release credentials may be STORED, not merely exported for one session.
|
|
31
|
+
*
|
|
32
|
+
* Why this exists: this store used to read only `process.env` and `.npmrc`,
|
|
33
|
+
* and `collectAll()` was the only way to mark credentials as collected. So an
|
|
34
|
+
* agent asked to release needed both tokens re-exported in its environment
|
|
35
|
+
* every session, and the `publish` TOOL — which may never prompt — could never
|
|
36
|
+
* set them up at all: it called `setupGitCredentials()` / `setupNpmAuth()`
|
|
37
|
+
* without `collectAll()`, both threw, and a single `try/catch` swallowed the
|
|
38
|
+
* pair. A tool-driven release therefore ran with NO credentials while
|
|
39
|
+
* reporting a clean pipeline.
|
|
40
|
+
*
|
|
41
|
+
* Tokens now persist in the nuvira env file (0600, resolved by
|
|
42
|
+
* `resolveNuviraEnvFile` — outside the repo, never in shell history), the SAME
|
|
43
|
+
* store the skill-secret path already writes. One place a credential lives.
|
|
44
|
+
*/
|
|
45
|
+
export const RELEASE_CREDENTIAL_KEYS = {
|
|
46
|
+
/** GitHub PAT / OAuth token used for HTTPS push. */
|
|
47
|
+
gitToken: 'GITHUB_TOKEN',
|
|
48
|
+
/** Git username paired with {@link RELEASE_CREDENTIAL_KEYS.gitToken}. */
|
|
49
|
+
gitUsername: 'GIT_USERNAME',
|
|
50
|
+
/** npm automation token used for `npm publish`. */
|
|
51
|
+
npmToken: 'NPM_TOKEN',
|
|
52
|
+
/** Optional non-default npm registry. */
|
|
53
|
+
npmRegistry: 'NPM_REGISTRY',
|
|
54
|
+
};
|
|
55
|
+
/** Legacy/alternate GitHub token names accepted on read. */
|
|
56
|
+
const GIT_TOKEN_ALIASES = ['GITHUB_TOKEN', 'GH_TOKEN', 'GITHUB_API_KEY'];
|
|
57
|
+
/**
|
|
58
|
+
* Read a credential from the persisted store, falling back to `process.env`.
|
|
59
|
+
* Returns `undefined` when unset (never an empty string).
|
|
60
|
+
*/
|
|
61
|
+
function storedCredential(name) {
|
|
62
|
+
return getEnvVarValue(name) || undefined;
|
|
63
|
+
}
|
|
64
|
+
/** First set credential among `names`, in priority order. */
|
|
65
|
+
function firstStoredCredential(names) {
|
|
66
|
+
for (const name of names) {
|
|
67
|
+
const value = storedCredential(name);
|
|
68
|
+
if (value)
|
|
69
|
+
return { name, value };
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
26
73
|
function detectGitRemote() {
|
|
27
74
|
const creds = {};
|
|
28
75
|
try {
|
|
@@ -94,6 +141,18 @@ function detectNpmConfig() {
|
|
|
94
141
|
}
|
|
95
142
|
}
|
|
96
143
|
}
|
|
144
|
+
// Persisted store — the STORED path (nuvira env file), which is what makes a
|
|
145
|
+
// token survive across sessions without an env export.
|
|
146
|
+
if (!creds.token) {
|
|
147
|
+
const stored = getEnvVarValue(RELEASE_CREDENTIAL_KEYS.npmToken);
|
|
148
|
+
if (stored) {
|
|
149
|
+
creds.token = stored;
|
|
150
|
+
creds.configured = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const storedRegistry = getEnvVarValue(RELEASE_CREDENTIAL_KEYS.npmRegistry);
|
|
154
|
+
if (storedRegistry)
|
|
155
|
+
creds.registry = storedRegistry;
|
|
97
156
|
creds.registry = creds.registry || 'https://registry.npmjs.org/';
|
|
98
157
|
return creds;
|
|
99
158
|
}
|
|
@@ -154,12 +213,59 @@ export class CredentialStore {
|
|
|
154
213
|
if (process.env.GIT_USERNAME) {
|
|
155
214
|
this._git.username = process.env.GIT_USERNAME;
|
|
156
215
|
}
|
|
216
|
+
// …then the PERSISTED store (env file), for anything the environment did
|
|
217
|
+
// not already provide. `process.env` wins so a one-off override stays
|
|
218
|
+
// possible without editing the stored credential.
|
|
219
|
+
if (!this._git.token) {
|
|
220
|
+
const stored = firstStoredCredential(GIT_TOKEN_ALIASES);
|
|
221
|
+
if (stored)
|
|
222
|
+
this._git.token = stored.value;
|
|
223
|
+
}
|
|
224
|
+
if (!this._git.username) {
|
|
225
|
+
const storedUser = storedCredential(RELEASE_CREDENTIAL_KEYS.gitUsername);
|
|
226
|
+
if (storedUser)
|
|
227
|
+
this._git.username = storedUser;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Non-interactive initialisation.
|
|
232
|
+
*
|
|
233
|
+
* Adopts whatever was auto-detected — environment variables, the persisted
|
|
234
|
+
* store, and the project `.npmrc` — and marks the store collected, so
|
|
235
|
+
* {@link setupGitCredentials} / {@link setupNpmAuth} can run with NO TTY.
|
|
236
|
+
*
|
|
237
|
+
* This is the method the `publish` TOOL must call: a tool call may never
|
|
238
|
+
* block on a prompt, whereas `nuvira publish` on a terminal still uses
|
|
239
|
+
* `collectAll()` to ask for anything missing.
|
|
240
|
+
*/
|
|
241
|
+
initialize() {
|
|
242
|
+
this._collected = true;
|
|
243
|
+
return { git: this._git, npm: this._npm };
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Validate the detected credentials against the live services.
|
|
247
|
+
* Best-effort and NON-FATAL: a network hiccup reports `unknown`, never a
|
|
248
|
+
* failure, so a preflight check can advise without blocking a release.
|
|
249
|
+
*/
|
|
250
|
+
async verify() {
|
|
251
|
+
const result = {
|
|
252
|
+
git: { checked: false, ok: false, detail: 'no git credentials' },
|
|
253
|
+
npm: { checked: false, ok: false, detail: 'no npm token' },
|
|
254
|
+
};
|
|
255
|
+
if (this._git.token) {
|
|
256
|
+
result.git = await probeGitHubToken(this._git.token);
|
|
257
|
+
}
|
|
258
|
+
if (this._npm.token) {
|
|
259
|
+
result.npm = await probeNpmToken(this._npm.token, this._npm.registry || 'https://registry.npmjs.org/');
|
|
260
|
+
}
|
|
261
|
+
return result;
|
|
157
262
|
}
|
|
158
263
|
/**
|
|
159
264
|
* Collect missing credentials interactively.
|
|
160
265
|
* Skips prompts for already-detected values.
|
|
161
266
|
*/
|
|
162
267
|
async collectAll() {
|
|
268
|
+
// Already initialised non-interactively (tool path) — never re-prompt.
|
|
163
269
|
if (this._collected) {
|
|
164
270
|
return { git: this._git, npm: this._npm };
|
|
165
271
|
}
|
|
@@ -269,7 +375,11 @@ export class CredentialStore {
|
|
|
269
375
|
}
|
|
270
376
|
this._collected = true;
|
|
271
377
|
console.log('');
|
|
272
|
-
|
|
378
|
+
// Report where they CAME from rather than claiming "session only": tokens
|
|
379
|
+
// are routinely read from the persisted store now, and a message that says
|
|
380
|
+
// otherwise makes an operator distrust the store they just populated.
|
|
381
|
+
logger.success(' ✅ Credentials ready for this run');
|
|
382
|
+
logger.info(` Stored values are read from: ${resolveNuviraEnvFile()}`);
|
|
273
383
|
console.log('');
|
|
274
384
|
return { git: this._git, npm: this._npm };
|
|
275
385
|
}
|
|
@@ -348,8 +458,15 @@ export class CredentialStore {
|
|
|
348
458
|
// Platform-aware: `.cmd` on Windows, `.sh` on Unix. Username/token ride
|
|
349
459
|
// in env (never inlined into the script file on disk), which removes the
|
|
350
460
|
// biggest breakage vector (tokens with &, %, ^, quotes, spaces) and keeps
|
|
351
|
-
// secrets out of temp files. cmd.exe still expands %
|
|
461
|
+
// secrets out of temp files. cmd.exe still expands %NUVIRA_GIT_TOKEN% at
|
|
352
462
|
// runtime, so values containing `%` remain best-effort on Windows.
|
|
463
|
+
//
|
|
464
|
+
// The variable names here MUST match the ones exported below: the script
|
|
465
|
+
// and the env were written on different days (one read BUFF_GIT_TOKEN
|
|
466
|
+
// while the other set NUVIRA_GIT_TOKEN), so a stored `GITHUB_TOKEN`
|
|
467
|
+
// produced an askpass helper that echoed an EMPTY string — HTTPS auth
|
|
468
|
+
// silently fell back to whatever credential helper the machine had, and
|
|
469
|
+
// a machine with none simply could not push.
|
|
353
470
|
const isWindows = process.platform === 'win32';
|
|
354
471
|
const askPassPath = join(tmpdir(), isWindows ? 'buff-git-askpass.cmd' : 'buff-git-askpass.sh');
|
|
355
472
|
const username = this._git.username || process.env.USER || process.env.USERNAME || 'git';
|
|
@@ -358,16 +475,16 @@ export class CredentialStore {
|
|
|
358
475
|
? '@echo off\r\n' +
|
|
359
476
|
'echo %1 | findstr /i "Username" >nul\r\n' +
|
|
360
477
|
'if not errorlevel 1 (\r\n' +
|
|
361
|
-
' echo %
|
|
478
|
+
' echo %NUVIRA_GIT_USERNAME%\r\n' +
|
|
362
479
|
') else (\r\n' +
|
|
363
|
-
' echo %
|
|
480
|
+
' echo %NUVIRA_GIT_TOKEN%\r\n' +
|
|
364
481
|
')\r\n'
|
|
365
482
|
// Unix variant reads the values from env too — never inlines secrets
|
|
366
483
|
// into the script file on disk.
|
|
367
484
|
: '#!/bin/sh\n' +
|
|
368
485
|
'case "$1" in\n' +
|
|
369
|
-
' *Username*) echo "$
|
|
370
|
-
' *) echo "$
|
|
486
|
+
' *Username*) echo "$NUVIRA_GIT_USERNAME" ;;\n' +
|
|
487
|
+
' *) echo "$NUVIRA_GIT_TOKEN" ;;\n' +
|
|
371
488
|
'esac\n';
|
|
372
489
|
writeFileSync(askPassPath, askPassContent, 'utf-8');
|
|
373
490
|
// Values ride in env so the script text stays literal and safe.
|
|
@@ -452,18 +569,240 @@ export function checkNpmCredentials() {
|
|
|
452
569
|
* Get a human-readable summary of the current credential status.
|
|
453
570
|
*/
|
|
454
571
|
export function getCredentialStatus() {
|
|
572
|
+
const status = releaseCredentialStatus();
|
|
455
573
|
const lines = [];
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
lines.push(`
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
const npmConfig = detectNpmConfig();
|
|
465
|
-
lines.push(` 📦 npm token: ${npmToken || npmConfig.token ? '✅ Detected' : '❌ Missing'}`);
|
|
466
|
-
lines.push(` 📦 npm registry: ${npmConfig.registry || 'Not configured'}`);
|
|
574
|
+
lines.push(` 📡 Remote: ${status.git.remoteUrl || 'Not configured'}`);
|
|
575
|
+
const gitRow = status.rows.find((r) => r.role === 'git');
|
|
576
|
+
lines.push(` 🔑 Git token: ${gitRow?.set ? `✅ Detected (${gitRow.origin})` : '❌ Missing'}`);
|
|
577
|
+
const ssh = status.rows.find((r) => r.role === 'git' && r.origin === 'ssh');
|
|
578
|
+
lines.push(` 🔐 SSH key: ${ssh ? `✅ ${ssh.key}` : '❌ Not detected'}`);
|
|
579
|
+
const npmRow = status.rows.find((r) => r.role === 'npm');
|
|
580
|
+
lines.push(` 📦 npm token: ${npmRow?.set ? `✅ Detected (${npmRow.origin})` : '❌ Missing'}`);
|
|
581
|
+
lines.push(` 📦 npm registry: ${status.npm.registry || 'Not configured'}`);
|
|
467
582
|
return lines.join('\n');
|
|
468
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* Resolve one credential and report WHERE it came from.
|
|
586
|
+
*
|
|
587
|
+
* The env FILE is consulted first, by reading it directly. It cannot be found
|
|
588
|
+
* through `process.env` order alone: nuvira loads that file into `process.env`
|
|
589
|
+
* at startup, so an environment-first check labelled every STORED token as
|
|
590
|
+
* `env` — the status output claimed the store was empty while it was in fact
|
|
591
|
+
* the thing supplying the value.
|
|
592
|
+
*/
|
|
593
|
+
function resolveCredential(names, fileVars) {
|
|
594
|
+
for (const name of names) {
|
|
595
|
+
const fromFile = fileVars[name]?.trim();
|
|
596
|
+
if (fromFile)
|
|
597
|
+
return { value: fromFile, origin: 'stored', key: name };
|
|
598
|
+
}
|
|
599
|
+
for (const name of names) {
|
|
600
|
+
const fromEnv = process.env[name]?.trim();
|
|
601
|
+
if (fromEnv)
|
|
602
|
+
return { value: fromEnv, origin: 'env', key: name };
|
|
603
|
+
}
|
|
604
|
+
return { origin: 'none', key: names[0] ?? '' };
|
|
605
|
+
}
|
|
606
|
+
/** The npm token as written by `npm login` into a `.npmrc`, if any. */
|
|
607
|
+
function projectNpmrcToken() {
|
|
608
|
+
for (const candidate of [join(process.cwd(), '.npmrc'), join(homedir(), '.npmrc')]) {
|
|
609
|
+
if (!existsSync(candidate))
|
|
610
|
+
continue;
|
|
611
|
+
try {
|
|
612
|
+
const m = readFileSync(candidate, 'utf-8').match(/\/\/registry\.npmjs\.org\/:_authToken=([^\s]+)/);
|
|
613
|
+
// A `${VAR}` placeholder is not a token — resolve it (setupNpmAuth writes
|
|
614
|
+
// the placeholder form, with the real value in the environment).
|
|
615
|
+
const raw = m?.[1];
|
|
616
|
+
if (!raw)
|
|
617
|
+
continue;
|
|
618
|
+
const indirection = raw.match(/^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/);
|
|
619
|
+
if (indirection) {
|
|
620
|
+
const resolved = process.env[indirection[1]]?.trim();
|
|
621
|
+
if (resolved)
|
|
622
|
+
return resolved;
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
return raw;
|
|
626
|
+
}
|
|
627
|
+
catch {
|
|
628
|
+
// Unreadable .npmrc — try the next one.
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return '';
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Describe every release credential, masked, and say whether a release could
|
|
635
|
+
* run right now. Never returns a raw secret — only {@link maskSecret} shapes.
|
|
636
|
+
*/
|
|
637
|
+
export function releaseCredentialStatus() {
|
|
638
|
+
// Read the store ONCE — the file is re-read per call otherwise.
|
|
639
|
+
const fileVars = loadEnvFile();
|
|
640
|
+
const git = resolveCredential(GIT_TOKEN_ALIASES, fileVars);
|
|
641
|
+
const gitUser = resolveCredential([RELEASE_CREDENTIAL_KEYS.gitUsername], fileVars);
|
|
642
|
+
const npm = resolveCredential([RELEASE_CREDENTIAL_KEYS.npmToken], fileVars);
|
|
643
|
+
const npmRegistry = resolveCredential([RELEASE_CREDENTIAL_KEYS.npmRegistry], fileVars);
|
|
644
|
+
const remote = detectGitRemote();
|
|
645
|
+
// npm's own `.npmrc` is a legitimate source — `npm login` writes it, and a
|
|
646
|
+
// release that ignored it would report "no npm token" on a machine that can
|
|
647
|
+
// publish perfectly well.
|
|
648
|
+
let npmValue = npm.value;
|
|
649
|
+
let npmOrigin = npm.origin;
|
|
650
|
+
if (!npmValue) {
|
|
651
|
+
const fromNpmrc = projectNpmrcToken();
|
|
652
|
+
if (fromNpmrc) {
|
|
653
|
+
npmValue = fromNpmrc;
|
|
654
|
+
npmOrigin = 'npmrc';
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
const rows = [
|
|
658
|
+
{
|
|
659
|
+
key: git.key || RELEASE_CREDENTIAL_KEYS.gitToken,
|
|
660
|
+
role: 'git',
|
|
661
|
+
set: Boolean(git.value) || Boolean(remote.sshKeyPath),
|
|
662
|
+
masked: git.value ? maskSecret(git.value) : undefined,
|
|
663
|
+
origin: git.value ? git.origin : remote.sshKeyPath ? 'ssh' : 'none',
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
key: gitUser.key || RELEASE_CREDENTIAL_KEYS.gitUsername,
|
|
667
|
+
role: 'git',
|
|
668
|
+
set: Boolean(gitUser.value),
|
|
669
|
+
masked: gitUser.value ? maskSecret(gitUser.value) : undefined,
|
|
670
|
+
origin: gitUser.origin,
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
key: npm.key || RELEASE_CREDENTIAL_KEYS.npmToken,
|
|
674
|
+
role: 'npm',
|
|
675
|
+
set: Boolean(npmValue),
|
|
676
|
+
masked: npmValue ? maskSecret(npmValue) : undefined,
|
|
677
|
+
origin: npmOrigin,
|
|
678
|
+
},
|
|
679
|
+
];
|
|
680
|
+
return {
|
|
681
|
+
rows,
|
|
682
|
+
git: {
|
|
683
|
+
canPush: Boolean(git.value) || Boolean(remote.sshKeyPath),
|
|
684
|
+
remoteUrl: remote.remoteUrl,
|
|
685
|
+
repoSlug: remote.repoSlug,
|
|
686
|
+
origin: rows[0].origin,
|
|
687
|
+
},
|
|
688
|
+
npm: {
|
|
689
|
+
canPublish: Boolean(npmValue),
|
|
690
|
+
registry: npmRegistry.value || 'https://registry.npmjs.org/',
|
|
691
|
+
origin: npmOrigin,
|
|
692
|
+
},
|
|
693
|
+
storePath: resolveNuviraEnvFile(),
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Persist a release credential so it survives the session.
|
|
698
|
+
*
|
|
699
|
+
* Writes to the nuvira env file (created 0600 by {@link saveEnvValue}), which
|
|
700
|
+
* lives OUTSIDE the repository — a token in a committed file is a leak, and a
|
|
701
|
+
* token in a shell export is gone at the next login.
|
|
702
|
+
*/
|
|
703
|
+
export function storeReleaseCredential(name, value) {
|
|
704
|
+
const cleaned = String(value ?? '').replace(/[\r\n]/g, '').trim();
|
|
705
|
+
if (!cleaned) {
|
|
706
|
+
return { success: false, path: resolveNuviraEnvFile(), reason: 'empty-value' };
|
|
707
|
+
}
|
|
708
|
+
const result = saveEnvValue(name, cleaned);
|
|
709
|
+
if (result.success) {
|
|
710
|
+
// Owner-only. `saveEnvValue` writes with the process umask, which on a
|
|
711
|
+
// default (022) install produced a WORLD-READABLE file holding a token.
|
|
712
|
+
// Best-effort: a filesystem without POSIX permissions still stores fine.
|
|
713
|
+
try {
|
|
714
|
+
chmodSync(result.path, 0o600);
|
|
715
|
+
}
|
|
716
|
+
catch { /* best-effort — non-POSIX filesystems */ }
|
|
717
|
+
// Make it usable in THIS process too, so a release started right after the
|
|
718
|
+
// token is stored does not need a restart to pick it up.
|
|
719
|
+
process.env[name] = cleaned;
|
|
720
|
+
}
|
|
721
|
+
return { success: result.success, path: result.path, reason: result.reason };
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Forget a stored release credential.
|
|
725
|
+
* A value that was never stored is a SUCCESS — the caller's intent already
|
|
726
|
+
* holds, and reporting failure would only surface a pointless error.
|
|
727
|
+
*/
|
|
728
|
+
export function forgetReleaseCredential(name) {
|
|
729
|
+
const result = deleteEnvValue(name);
|
|
730
|
+
if (result.success)
|
|
731
|
+
delete process.env[name];
|
|
732
|
+
return result;
|
|
733
|
+
}
|
|
734
|
+
/** Whether `name` is a credential this module is willing to store. */
|
|
735
|
+
export function isReleaseCredentialKey(name) {
|
|
736
|
+
return Object.values(RELEASE_CREDENTIAL_KEYS).includes(name)
|
|
737
|
+
|| GIT_TOKEN_ALIASES.includes(name);
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Confirm a GitHub token is live. A 401 is a DEFINITIVE failure (the token was
|
|
741
|
+
* revoked or lacks scopes); anything else inconclusive — offline, timeout,
|
|
742
|
+
* rate-limit — reports `checked: false` so a preflight can warn without
|
|
743
|
+
* blocking a release on a flaky network.
|
|
744
|
+
*/
|
|
745
|
+
async function probeGitHubToken(token) {
|
|
746
|
+
try {
|
|
747
|
+
const res = await fetch('https://api.github.com/user', {
|
|
748
|
+
headers: {
|
|
749
|
+
Authorization: `Bearer ${token}`,
|
|
750
|
+
Accept: 'application/vnd.github+json',
|
|
751
|
+
'User-Agent': 'agent-nuvira',
|
|
752
|
+
},
|
|
753
|
+
signal: AbortSignal.timeout(10_000),
|
|
754
|
+
});
|
|
755
|
+
if (res.ok) {
|
|
756
|
+
const body = (await res.json().catch(() => ({})));
|
|
757
|
+
return {
|
|
758
|
+
checked: true,
|
|
759
|
+
ok: true,
|
|
760
|
+
detail: body.login ? `authenticated as ${body.login}` : 'token accepted',
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
if (res.status === 401) {
|
|
764
|
+
return { checked: true, ok: false, detail: 'token rejected (401) — revoked or wrong scopes' };
|
|
765
|
+
}
|
|
766
|
+
return { checked: true, ok: true, detail: `GitHub returned ${res.status} (token accepted)` };
|
|
767
|
+
}
|
|
768
|
+
catch (err) {
|
|
769
|
+
return {
|
|
770
|
+
checked: false,
|
|
771
|
+
ok: false,
|
|
772
|
+
detail: `could not reach GitHub: ${err instanceof Error ? err.message : String(err)}`,
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Confirm an npm token is live, by asking the registry who we are. Same
|
|
778
|
+
* conservatism as {@link probeGitHubToken}: only a definitive rejection counts.
|
|
779
|
+
*/
|
|
780
|
+
async function probeNpmToken(token, registry) {
|
|
781
|
+
const base = registry.replace(/\/+$/, '');
|
|
782
|
+
try {
|
|
783
|
+
const res = await fetch(`${base}/-/whoami`, {
|
|
784
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
785
|
+
signal: AbortSignal.timeout(10_000),
|
|
786
|
+
});
|
|
787
|
+
if (res.ok) {
|
|
788
|
+
const body = (await res.json().catch(() => ({})));
|
|
789
|
+
return {
|
|
790
|
+
checked: true,
|
|
791
|
+
ok: true,
|
|
792
|
+
detail: body.username ? `authenticated as ${body.username}` : 'token accepted',
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
if (res.status === 401 || res.status === 403) {
|
|
796
|
+
return { checked: true, ok: false, detail: `token rejected (${res.status})` };
|
|
797
|
+
}
|
|
798
|
+
return { checked: true, ok: true, detail: `registry returned ${res.status} (token accepted)` };
|
|
799
|
+
}
|
|
800
|
+
catch (err) {
|
|
801
|
+
return {
|
|
802
|
+
checked: false,
|
|
803
|
+
ok: false,
|
|
804
|
+
detail: `could not reach ${base}: ${err instanceof Error ? err.message : String(err)}`,
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
}
|
|
469
808
|
//# sourceMappingURL=credential-store.js.map
|