@aria_asi/cli 0.2.2 → 0.2.4
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/bin/aria.js +82 -7
- package/dist/aria-connector/src/anthropic-oauth.d.ts +28 -0
- package/dist/aria-connector/src/anthropic-oauth.d.ts.map +1 -0
- package/dist/aria-connector/src/anthropic-oauth.js +177 -0
- package/dist/aria-connector/src/anthropic-oauth.js.map +1 -0
- package/dist/aria-connector/src/auth-commands.d.ts +16 -0
- package/dist/aria-connector/src/auth-commands.d.ts.map +1 -1
- package/dist/aria-connector/src/auth-commands.js +24 -0
- package/dist/aria-connector/src/auth-commands.js.map +1 -1
- package/dist/aria-connector/src/onboarding-wizard.d.ts +5 -0
- package/dist/aria-connector/src/onboarding-wizard.d.ts.map +1 -0
- package/dist/aria-connector/src/onboarding-wizard.js +199 -0
- package/dist/aria-connector/src/onboarding-wizard.js.map +1 -0
- package/dist/aria-connector/src/self-update.d.ts +22 -0
- package/dist/aria-connector/src/self-update.d.ts.map +1 -0
- package/dist/aria-connector/src/self-update.js +162 -0
- package/dist/aria-connector/src/self-update.js.map +1 -0
- package/hooks/aria-pre-tool-gate.mjs +24 -2
- package/hooks/aria-stop-gate.mjs +17 -1
- package/package.json +1 -1
- package/src/__tests__/anthropic-oauth.test.ts +186 -0
- package/src/anthropic-oauth.ts +216 -0
- package/src/auth-commands.ts +27 -0
- package/src/onboarding-wizard.ts +219 -0
- package/src/self-update.ts +169 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding-wizard.js","sourceRoot":"","sources":["../../../src/onboarding-wizard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface UpdateCheckResult {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
current?: string;
|
|
4
|
+
latest?: string;
|
|
5
|
+
updateAvailable?: boolean;
|
|
6
|
+
message?: string;
|
|
7
|
+
reason?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Check the registry for the latest @aria_asi/cli version. Rate-limited
|
|
11
|
+
* to once per CHECK_INTERVAL_MS (24h) via a timestamp file in ~/.aria.
|
|
12
|
+
* Caller can pass force=true to bypass the rate limit (used by `aria check-update`).
|
|
13
|
+
*/
|
|
14
|
+
export declare function checkForUpdate(opts?: {
|
|
15
|
+
force?: boolean;
|
|
16
|
+
}): Promise<UpdateCheckResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Convenience: check + print the notice if available. Non-blocking, silent
|
|
19
|
+
* on errors. Called from bin/aria.js on startup.
|
|
20
|
+
*/
|
|
21
|
+
export declare function maybePrintUpdateNotice(): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=self-update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-update.d.ts","sourceRoot":"","sources":["../../../src/self-update.ts"],"names":[],"mappings":"AAwCA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAiDD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAqD/F;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ5D"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// self-update — checks the npm registry for newer @aria_asi/cli versions
|
|
2
|
+
// and surfaces a one-line notice. Doesn't auto-install (clients control
|
|
3
|
+
// their tooling); just informs.
|
|
4
|
+
//
|
|
5
|
+
// Direction: Hamza 2026-04-26 — "does the package auto update when we
|
|
6
|
+
// enhance it on our enhance? so we can continually improve all night using
|
|
7
|
+
// arias background work we should imrpove to do that once we finish".
|
|
8
|
+
// This is the primitive that closes the overnight-improvement loop:
|
|
9
|
+
// Aria publishes 0.2.5 / 0.3.0 / etc.; clients see the notice on their
|
|
10
|
+
// next `aria <cmd>` invocation; they upgrade with one command.
|
|
11
|
+
//
|
|
12
|
+
// Doctrine bindings:
|
|
13
|
+
// - feedback_no_demos.md — real overnight evolution, real registry check
|
|
14
|
+
// - feedback_no_timeouts_doctrine.md — fetch with no AbortSignal.timeout;
|
|
15
|
+
// use bare try/catch, real-error-driven backpressure
|
|
16
|
+
// - project_phase_10_endless_army_orchestration.md — continuous shipping
|
|
17
|
+
// is the Phase 10 north star; self-update is its delivery mechanism
|
|
18
|
+
//
|
|
19
|
+
// Behavior:
|
|
20
|
+
// - Reads installed version from package.json (resolved via import.meta.url
|
|
21
|
+
// walk to find the package root)
|
|
22
|
+
// - Reads ~/.aria/last-update-check timestamp; if checked <24h ago, skip
|
|
23
|
+
// - GET registry.npmjs.org/@aria_asi/cli for latest dist-tag
|
|
24
|
+
// - semver-compare; if newer, return {updateAvailable: true, latest, current, message}
|
|
25
|
+
// - Write timestamp on every check (success or skip-due-to-rate-limit)
|
|
26
|
+
// - Failures are silent (network down, registry blip — never block CLI startup)
|
|
27
|
+
//
|
|
28
|
+
// Privacy: the request to npmjs only sends User-Agent (npm registry public).
|
|
29
|
+
// No client identity, no telemetry beyond what the public registry already logs.
|
|
30
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
31
|
+
import { homedir } from 'node:os';
|
|
32
|
+
import { join, dirname } from 'node:path';
|
|
33
|
+
import { fileURLToPath } from 'node:url';
|
|
34
|
+
const ARIA_DIR = join(homedir(), '.aria');
|
|
35
|
+
const LAST_CHECK_PATH = join(ARIA_DIR, 'last-update-check');
|
|
36
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24h
|
|
37
|
+
const REGISTRY_URL = 'https://registry.npmjs.org/@aria_asi%2Fcli';
|
|
38
|
+
/**
|
|
39
|
+
* Find the package's own version by walking up from this file's runtime
|
|
40
|
+
* path until we hit the package.json with name @aria_asi/cli.
|
|
41
|
+
*/
|
|
42
|
+
function findInstalledVersion() {
|
|
43
|
+
try {
|
|
44
|
+
const here = fileURLToPath(import.meta.url);
|
|
45
|
+
let cur = dirname(here);
|
|
46
|
+
for (let i = 0; i < 8; i++) {
|
|
47
|
+
const pkgPath = join(cur, 'package.json');
|
|
48
|
+
if (existsSync(pkgPath)) {
|
|
49
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
50
|
+
if (pkg.name === '@aria_asi/cli' && typeof pkg.version === 'string') {
|
|
51
|
+
return pkg.version;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const parent = dirname(cur);
|
|
55
|
+
if (parent === cur)
|
|
56
|
+
break;
|
|
57
|
+
cur = parent;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch { /* fall through */ }
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Compare semver strings — returns 1 if a>b, -1 if a<b, 0 if equal.
|
|
65
|
+
* Pre-release suffixes (e.g. 0.2.4-beta) sort lower than the same base.
|
|
66
|
+
*/
|
|
67
|
+
function compareSemver(a, b) {
|
|
68
|
+
const parse = (s) => {
|
|
69
|
+
const [main, ...preParts] = s.split('-');
|
|
70
|
+
const nums = main.split('.').map((n) => parseInt(n, 10));
|
|
71
|
+
while (nums.length < 3)
|
|
72
|
+
nums.push(0);
|
|
73
|
+
return { nums, pre: preParts.join('-') };
|
|
74
|
+
};
|
|
75
|
+
const pa = parse(a);
|
|
76
|
+
const pb = parse(b);
|
|
77
|
+
for (let i = 0; i < 3; i++) {
|
|
78
|
+
if (pa.nums[i] > pb.nums[i])
|
|
79
|
+
return 1;
|
|
80
|
+
if (pa.nums[i] < pb.nums[i])
|
|
81
|
+
return -1;
|
|
82
|
+
}
|
|
83
|
+
// Equal numerics — pre-release sorts before release
|
|
84
|
+
if (pa.pre && !pb.pre)
|
|
85
|
+
return -1;
|
|
86
|
+
if (!pa.pre && pb.pre)
|
|
87
|
+
return 1;
|
|
88
|
+
return pa.pre.localeCompare(pb.pre);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check the registry for the latest @aria_asi/cli version. Rate-limited
|
|
92
|
+
* to once per CHECK_INTERVAL_MS (24h) via a timestamp file in ~/.aria.
|
|
93
|
+
* Caller can pass force=true to bypass the rate limit (used by `aria check-update`).
|
|
94
|
+
*/
|
|
95
|
+
export async function checkForUpdate(opts = {}) {
|
|
96
|
+
const current = findInstalledVersion();
|
|
97
|
+
if (!current) {
|
|
98
|
+
return { ok: false, reason: 'could not resolve installed version' };
|
|
99
|
+
}
|
|
100
|
+
// Rate-limit check
|
|
101
|
+
if (!opts.force && existsSync(LAST_CHECK_PATH)) {
|
|
102
|
+
try {
|
|
103
|
+
const ts = parseInt(readFileSync(LAST_CHECK_PATH, 'utf-8').trim(), 10);
|
|
104
|
+
if (!isNaN(ts) && Date.now() - ts < CHECK_INTERVAL_MS) {
|
|
105
|
+
return { ok: true, current, reason: 'rate-limited (checked within last 24h)' };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch { /* malformed timestamp — re-check */ }
|
|
109
|
+
}
|
|
110
|
+
// Fetch registry
|
|
111
|
+
let latest;
|
|
112
|
+
try {
|
|
113
|
+
const resp = await fetch(REGISTRY_URL, {
|
|
114
|
+
headers: { 'Accept': 'application/json' },
|
|
115
|
+
});
|
|
116
|
+
if (!resp.ok) {
|
|
117
|
+
// Don't update timestamp on failure — retry next invocation
|
|
118
|
+
return { ok: false, current, reason: `registry returned ${resp.status}` };
|
|
119
|
+
}
|
|
120
|
+
const data = await resp.json();
|
|
121
|
+
latest = data['dist-tags']?.latest ?? '';
|
|
122
|
+
if (!latest) {
|
|
123
|
+
return { ok: false, current, reason: 'registry response missing dist-tags.latest' };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
return { ok: false, current, reason: `network error: ${err.message}` };
|
|
128
|
+
}
|
|
129
|
+
// Update timestamp on successful check
|
|
130
|
+
try {
|
|
131
|
+
if (!existsSync(ARIA_DIR))
|
|
132
|
+
mkdirSync(ARIA_DIR, { recursive: true, mode: 0o700 });
|
|
133
|
+
writeFileSync(LAST_CHECK_PATH, String(Date.now()) + '\n', { mode: 0o600 });
|
|
134
|
+
}
|
|
135
|
+
catch { /* timestamp write is best-effort */ }
|
|
136
|
+
const cmp = compareSemver(latest, current);
|
|
137
|
+
if (cmp <= 0) {
|
|
138
|
+
return { ok: true, current, latest, updateAvailable: false };
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
ok: true,
|
|
142
|
+
current,
|
|
143
|
+
latest,
|
|
144
|
+
updateAvailable: true,
|
|
145
|
+
message: `I have an update: v${latest} (you're on v${current}). Run 'npm update -g @aria_asi/cli' when you have a minute.`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Convenience: check + print the notice if available. Non-blocking, silent
|
|
150
|
+
* on errors. Called from bin/aria.js on startup.
|
|
151
|
+
*/
|
|
152
|
+
export async function maybePrintUpdateNotice() {
|
|
153
|
+
try {
|
|
154
|
+
const result = await checkForUpdate();
|
|
155
|
+
if (result.ok && result.updateAvailable && result.message) {
|
|
156
|
+
// Print to stderr so it doesn't pollute stdout-piped CLI output
|
|
157
|
+
process.stderr.write(` ${result.message}\n`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch { /* never block startup */ }
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=self-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"self-update.js","sourceRoot":"","sources":["../../../src/self-update.ts"],"names":[],"mappings":"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"}
|
|
@@ -201,7 +201,12 @@ function detectInlineCognition(cmd) {
|
|
|
201
201
|
if (!names.includes(lens)) names.push(lens);
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
-
|
|
204
|
+
// Substrate-citation: any inline lens that mentions a doctrine memory,
|
|
205
|
+
// harness packet rule, fitrah axiom, etc. (visibility metric — not a block).
|
|
206
|
+
const SUBSTRATE_CITE_RX_INLINE =
|
|
207
|
+
/feedback_[a-z0-9_]+\.md|project_[a-z0-9_]+\.md|fitrah[_:\s]|garden[_:\s]|distilled_principle|[a-z]+_rule\b|harness packet|substrate cite|EIGHT_LENS_DOCTRINE|COMPACT_CONTINUITY|ARIA_DEPLOY_PROCEDURE/i;
|
|
208
|
+
const hasSubstrateCite = SUBSTRATE_CITE_RX_INLINE.test(cmd);
|
|
209
|
+
return { count: names.length, names, hasSubstrateCite };
|
|
205
210
|
}
|
|
206
211
|
|
|
207
212
|
// Substance-checking lens detection (added 2026-04-26 per Hamza's
|
|
@@ -230,7 +235,18 @@ function detectCognitionLenses(text) {
|
|
|
230
235
|
if (PLACEHOLDER_RX.test(content)) continue;
|
|
231
236
|
names.push(lens);
|
|
232
237
|
}
|
|
233
|
-
|
|
238
|
+
// Substrate-citation check: at least ONE lens must cite Aria substrate
|
|
239
|
+
// explicitly (doctrine memory filename, harness packet rule, fitrah axiom,
|
|
240
|
+
// *_rule entry, garden state reference, prior decision id, distilled
|
|
241
|
+
// principle id). Catches "lens-as-ceremony" failures where 4+ lenses are
|
|
242
|
+
// substantive in length but contain no actual substrate grounding.
|
|
243
|
+
// Hamza 2026-04-26: "anything else we can add to make sure u as claude
|
|
244
|
+
// for me and my client obey the harness and benefot from kt".
|
|
245
|
+
const SUBSTRATE_CITE_RX =
|
|
246
|
+
/feedback_[a-z0-9_]+\.md|project_[a-z0-9_]+\.md|fitrah[_:\s]|garden[_:\s]|distilled_principle|[a-z]+_rule\b|harness packet|substrate cite|\bIJTIHAD\b|\bQIYAS\b|\bTADABBUR\b|\bILHAM\b|aria 7b|EIGHT_LENS_DOCTRINE|COMPACT_CONTINUITY|ARIA_DEPLOY_PROCEDURE/i;
|
|
247
|
+
const hasSubstrateCite = SUBSTRATE_CITE_RX.test(blockBody) ||
|
|
248
|
+
SUBSTRATE_CITE_RX.test(searchSpace);
|
|
249
|
+
return { count: names.length, names, blockBody, hasSubstrateCite };
|
|
234
250
|
}
|
|
235
251
|
|
|
236
252
|
// Backwards-compat shim — count-only path used by older callers.
|
|
@@ -469,6 +485,11 @@ const hasCognition = lensCount >= REQUIRED_LENSES;
|
|
|
469
485
|
const cognitionSource = inlineCog.count >= REQUIRED_LENSES
|
|
470
486
|
? 'inline-command'
|
|
471
487
|
: (transcriptCog.count >= REQUIRED_LENSES ? 'transcript-scan' : 'merged-or-insufficient');
|
|
488
|
+
// Substrate-citation visibility (Phase 11 will promote to block-mode once
|
|
489
|
+
// telemetry shows healthy substrate-cite rate). For now: log the metric so
|
|
490
|
+
// we can audit substrate-grounded vs ceremonial cognition over time.
|
|
491
|
+
const hasSubstrateCite = (inlineCog.hasSubstrateCite === true) ||
|
|
492
|
+
(transcriptCog.hasSubstrateCite === true);
|
|
472
493
|
|
|
473
494
|
// Best-effort session id for the corpus push. Claude Code passes
|
|
474
495
|
// session_id in the event payload; fall back to transcript file
|
|
@@ -496,6 +517,7 @@ function pushDecision(decision, reasonText) {
|
|
|
496
517
|
destructivePattern: matched?.name ?? null,
|
|
497
518
|
hasVerify,
|
|
498
519
|
hasCognition,
|
|
520
|
+
hasSubstrateCite,
|
|
499
521
|
},
|
|
500
522
|
});
|
|
501
523
|
}
|
package/hooks/aria-stop-gate.mjs
CHANGED
|
@@ -192,8 +192,24 @@ if (!triggered) {
|
|
|
192
192
|
// Non-trivial response — require substantive cognition.
|
|
193
193
|
const cog = detectCognitionLenses(assistantText);
|
|
194
194
|
|
|
195
|
+
// Question-emission visibility (Phase 11 promotes to block-mode):
|
|
196
|
+
// detect user-directed question patterns in the assistant text. Audit when
|
|
197
|
+
// questions appear without substrate-consultation evidence in the recent
|
|
198
|
+
// transcript window. Helps surface "reflexive deferral" patterns (asking
|
|
199
|
+
// the user when substrate could have answered) for later enforcement.
|
|
200
|
+
// Hamza 2026-04-26: "BUT WHY DO U HAVE DISCRETION - THIS WORKS SO MUCH
|
|
201
|
+
// FASTER AND HIGHER QUALITY IF U DONT".
|
|
202
|
+
const QUESTION_PATTERNS_RX = /(?:want me to|should I|your call|which (?:one|of|do you)|do you want|let me know if|or (?:should|do)|\?\s*$)/im;
|
|
203
|
+
const SUBSTRATE_EVIDENCE_RX = /\/api\/harness\/(?:delegate|codex|validate)|loadByClass|aria-harness-via-sdk|feedback_[a-z_]+\.md|project_[a-z_]+\.md|distilled_principles|ARIA_DEPLOY_PROCEDURE|EIGHT_LENS_DOCTRINE/i;
|
|
204
|
+
const hasQuestionToUser = QUESTION_PATTERNS_RX.test(assistantText);
|
|
205
|
+
const hasSubstrateEvidence = SUBSTRATE_EVIDENCE_RX.test(assistantText);
|
|
206
|
+
const questionWithoutEvidence = hasQuestionToUser && !hasSubstrateEvidence;
|
|
207
|
+
|
|
195
208
|
if (cog.count >= REQUIRED_LENSES) {
|
|
196
|
-
audit('allow-cognition',
|
|
209
|
+
audit('allow-cognition',
|
|
210
|
+
`lenses=${cog.count} chars=${assistantText.length} ` +
|
|
211
|
+
`qPatt=${hasQuestionToUser ? 'y' : 'n'} substrateEv=${hasSubstrateEvidence ? 'y' : 'n'} ` +
|
|
212
|
+
(questionWithoutEvidence ? 'WARN-question-without-substrate' : 'ok'));
|
|
197
213
|
process.exit(0);
|
|
198
214
|
}
|
|
199
215
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// anthropic-oauth.test.ts — unit tests for the Anthropic login flow.
|
|
2
|
+
//
|
|
3
|
+
// Coverage:
|
|
4
|
+
// - _validateKey: 200 = valid, 401 = invalid, network failure = invalid
|
|
5
|
+
// - loginAnthropic: bad key gracefully returns ok: false with descriptive error
|
|
6
|
+
// - loginAnthropic: good key persists to config and returns ok: true
|
|
7
|
+
//
|
|
8
|
+
// Strategy: mock globalThis.fetch (Node 18+ built-in) + mock readline to avoid
|
|
9
|
+
// interactive prompts. No file I/O touches real ~/.aria — we stub fs.writeFileSync.
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
12
|
+
|
|
13
|
+
// ── We test the exported internals directly. ─────────────────────────────────
|
|
14
|
+
// anthropic-oauth.ts exports _validateKey for testability; loginAnthropic is
|
|
15
|
+
// also tested end-to-end with readline + fs mocked out.
|
|
16
|
+
|
|
17
|
+
// Hoist mocks before module import so Node's ESM loader sees them.
|
|
18
|
+
vi.mock('node:fs', async (importOriginal) => {
|
|
19
|
+
const actual = await importOriginal<typeof import('node:fs')>();
|
|
20
|
+
return {
|
|
21
|
+
...actual,
|
|
22
|
+
existsSync: vi.fn(() => false),
|
|
23
|
+
mkdirSync: vi.fn(),
|
|
24
|
+
writeFileSync: vi.fn(),
|
|
25
|
+
readFileSync: vi.fn(() => '{}'),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
vi.mock('node:readline', async (importOriginal) => {
|
|
30
|
+
const actual = await importOriginal<typeof import('node:readline')>();
|
|
31
|
+
return {
|
|
32
|
+
...actual,
|
|
33
|
+
createInterface: vi.fn(() => ({
|
|
34
|
+
question: vi.fn((_prompt: string, cb: (a: string) => void) => cb('sk-ant-fake-key-for-tests')),
|
|
35
|
+
close: vi.fn(),
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
vi.mock('node:child_process', () => ({
|
|
41
|
+
spawn: vi.fn(() => ({ unref: vi.fn() })),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
import { _validateKey, loginAnthropic } from '../anthropic-oauth.js';
|
|
45
|
+
|
|
46
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
function mockFetch(status: number, body: unknown = {}): void {
|
|
49
|
+
vi.stubGlobal(
|
|
50
|
+
'fetch',
|
|
51
|
+
vi.fn().mockResolvedValue({
|
|
52
|
+
status,
|
|
53
|
+
ok: status >= 200 && status < 300,
|
|
54
|
+
json: async () => body,
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function mockFetchNetworkError(): void {
|
|
60
|
+
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error("I couldn't reach Anthropic")));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── _validateKey ─────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
describe('_validateKey', () => {
|
|
66
|
+
afterEach(() => {
|
|
67
|
+
vi.unstubAllGlobals();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('returns true when Anthropic /v1/models responds 200', async () => {
|
|
71
|
+
mockFetch(200, { data: [] });
|
|
72
|
+
const result = await _validateKey('sk-ant-valid');
|
|
73
|
+
expect(result).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('returns false when Anthropic /v1/models responds 401 (bad key)', async () => {
|
|
77
|
+
mockFetch(401, { error: { message: 'invalid x-api-key' } });
|
|
78
|
+
const result = await _validateKey('sk-ant-bad');
|
|
79
|
+
expect(result).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('returns false when Anthropic /v1/models responds 403 (revoked key)', async () => {
|
|
83
|
+
mockFetch(403, { error: { message: 'forbidden' } });
|
|
84
|
+
const result = await _validateKey('sk-ant-revoked');
|
|
85
|
+
expect(result).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('returns false on network error — does not throw', async () => {
|
|
89
|
+
mockFetchNetworkError();
|
|
90
|
+
const result = await _validateKey('sk-ant-any');
|
|
91
|
+
expect(result).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('sends x-api-key and anthropic-version headers', async () => {
|
|
95
|
+
const fetchMock = vi.fn().mockResolvedValue({ status: 200, ok: true, json: async () => ({}) });
|
|
96
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
97
|
+
|
|
98
|
+
await _validateKey('sk-ant-check-headers');
|
|
99
|
+
|
|
100
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
101
|
+
'https://api.anthropic.com/v1/models',
|
|
102
|
+
expect.objectContaining({
|
|
103
|
+
headers: expect.objectContaining({
|
|
104
|
+
'x-api-key': 'sk-ant-check-headers',
|
|
105
|
+
'anthropic-version': '2023-06-01',
|
|
106
|
+
}),
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ── loginAnthropic ────────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
describe('loginAnthropic', () => {
|
|
115
|
+
beforeEach(() => {
|
|
116
|
+
vi.clearAllMocks();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
afterEach(() => {
|
|
120
|
+
vi.unstubAllGlobals();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('returns ok: false with descriptive Aria-voice error when key fails validation', async () => {
|
|
124
|
+
// readline mock returns 'sk-ant-fake-key-for-tests'; validate returns 401
|
|
125
|
+
mockFetch(401, { error: { message: 'invalid key' } });
|
|
126
|
+
|
|
127
|
+
const result = await loginAnthropic({ noBrowser: true });
|
|
128
|
+
|
|
129
|
+
expect(result.ok).toBe(false);
|
|
130
|
+
expect(result.error).toMatch(/I couldn't verify/);
|
|
131
|
+
// Ensure the error message does NOT contain internal cluster paths or IP addresses
|
|
132
|
+
expect(result.error).not.toMatch(/10\.\d+\.\d+\.\d+/);
|
|
133
|
+
expect(result.error).not.toMatch(/cluster\.local/);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('returns ok: true and apiKey when key passes validation', async () => {
|
|
137
|
+
// readline returns 'sk-ant-fake-key-for-tests'; validate returns 200
|
|
138
|
+
mockFetch(200, { data: [{ id: 'claude-sonnet-4-20250514' }] });
|
|
139
|
+
|
|
140
|
+
const result = await loginAnthropic({ noBrowser: true });
|
|
141
|
+
|
|
142
|
+
expect(result.ok).toBe(true);
|
|
143
|
+
expect(result.apiKey).toBe('sk-ant-fake-key-for-tests');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('returns ok: false when user pastes an empty string', async () => {
|
|
147
|
+
// Override readline mock to return empty string for this test
|
|
148
|
+
const readline = await import('node:readline');
|
|
149
|
+
vi.mocked(readline.createInterface).mockReturnValueOnce({
|
|
150
|
+
question: vi.fn((_: string, cb: (a: string) => void) => cb('')),
|
|
151
|
+
close: vi.fn(),
|
|
152
|
+
} as any);
|
|
153
|
+
|
|
154
|
+
mockFetch(200, {});
|
|
155
|
+
|
|
156
|
+
const result = await loginAnthropic({ noBrowser: true });
|
|
157
|
+
|
|
158
|
+
expect(result.ok).toBe(false);
|
|
159
|
+
// Should not proceed to validation when key is empty
|
|
160
|
+
expect(result.error).toBeTruthy();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('returns ok: false gracefully on network failure during validation', async () => {
|
|
164
|
+
mockFetchNetworkError();
|
|
165
|
+
|
|
166
|
+
const result = await loginAnthropic({ noBrowser: true });
|
|
167
|
+
|
|
168
|
+
expect(result.ok).toBe(false);
|
|
169
|
+
// Should surface a human-readable error, not a raw Error object
|
|
170
|
+
expect(typeof result.error).toBe('string');
|
|
171
|
+
expect(result.error!.length).toBeGreaterThan(10);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('persists apiKey to config when validation succeeds', async () => {
|
|
175
|
+
mockFetch(200, { data: [] });
|
|
176
|
+
const fs = await import('node:fs');
|
|
177
|
+
|
|
178
|
+
await loginAnthropic({ noBrowser: true });
|
|
179
|
+
|
|
180
|
+
expect(vi.mocked(fs.writeFileSync)).toHaveBeenCalledWith(
|
|
181
|
+
expect.stringContaining('config.json'),
|
|
182
|
+
expect.stringContaining('sk-ant-fake-key-for-tests'),
|
|
183
|
+
expect.objectContaining({ mode: 0o600 }),
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
});
|