@almightygpt/core 0.9.2 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/adapters/factory.d.ts +12 -0
  2. package/dist/adapters/factory.d.ts.map +1 -0
  3. package/dist/adapters/factory.js +40 -0
  4. package/dist/adapters/factory.js.map +1 -0
  5. package/dist/auth/__tests__/keychain.test.d.ts +18 -0
  6. package/dist/auth/__tests__/keychain.test.d.ts.map +1 -0
  7. package/dist/auth/__tests__/keychain.test.js +155 -0
  8. package/dist/auth/__tests__/keychain.test.js.map +1 -0
  9. package/dist/auth/__tests__/resolver.test.d.ts +13 -0
  10. package/dist/auth/__tests__/resolver.test.d.ts.map +1 -0
  11. package/dist/auth/__tests__/resolver.test.js +182 -0
  12. package/dist/auth/__tests__/resolver.test.js.map +1 -0
  13. package/dist/auth/__tests__/validator.test.d.ts +15 -0
  14. package/dist/auth/__tests__/validator.test.d.ts.map +1 -0
  15. package/dist/auth/__tests__/validator.test.js +197 -0
  16. package/dist/auth/__tests__/validator.test.js.map +1 -0
  17. package/dist/auth/validator.js +19 -14
  18. package/dist/auth/validator.js.map +1 -1
  19. package/dist/index.d.ts +3 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +4 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/plan/run-plan-review.d.ts +40 -0
  24. package/dist/plan/run-plan-review.d.ts.map +1 -0
  25. package/dist/plan/run-plan-review.js +224 -0
  26. package/dist/plan/run-plan-review.js.map +1 -0
  27. package/dist/plan/run-plan.d.ts +42 -0
  28. package/dist/plan/run-plan.d.ts.map +1 -0
  29. package/dist/plan/run-plan.js +193 -0
  30. package/dist/plan/run-plan.js.map +1 -0
  31. package/dist/runs/types.d.ts +1 -1
  32. package/dist/runs/types.d.ts.map +1 -1
  33. package/package.json +4 -2
  34. package/src/adapters/factory.ts +45 -0
  35. package/src/auth/__tests__/keychain.test.ts +171 -0
  36. package/src/auth/__tests__/resolver.test.ts +231 -0
  37. package/src/auth/__tests__/validator.test.ts +241 -0
  38. package/src/auth/validator.ts +27 -14
  39. package/src/index.ts +13 -1
  40. package/src/plan/run-plan-review.ts +302 -0
  41. package/src/plan/run-plan.ts +247 -0
  42. package/src/runs/types.ts +3 -1
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Adapter factory — single place to map (name, provider) → concrete
3
+ * Adapter instance.
4
+ *
5
+ * Extracted from review/run-diff-review.ts during v0.10.0 (plan
6
+ * module) so the plan + review pipelines share the same factory and
7
+ * a new adapter only needs to be wired in one place.
8
+ */
9
+ import { type Adapter } from "./types.js";
10
+ export declare function makeAdapter(name: string, provider: string): Adapter;
11
+ export declare function envHintForProvider(provider: string): string;
12
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/adapters/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAM1C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAgBnE;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAW3D"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Adapter factory — single place to map (name, provider) → concrete
3
+ * Adapter instance.
4
+ *
5
+ * Extracted from review/run-diff-review.ts during v0.10.0 (plan
6
+ * module) so the plan + review pipelines share the same factory and
7
+ * a new adapter only needs to be wired in one place.
8
+ */
9
+ import { MockAdapter } from "./mock.js";
10
+ import { OpenAIAdapter } from "./openai.js";
11
+ import { ClaudeAdapter } from "./claude.js";
12
+ import { GeminiAdapter } from "./gemini.js";
13
+ export function makeAdapter(name, provider) {
14
+ switch (provider) {
15
+ case "openai":
16
+ return new OpenAIAdapter(name);
17
+ case "anthropic":
18
+ return new ClaudeAdapter(name);
19
+ case "google":
20
+ return new GeminiAdapter(name);
21
+ case "mock":
22
+ return new MockAdapter();
23
+ default:
24
+ throw new Error(`Provider "${provider}" not supported. ` +
25
+ `Use "openai", "anthropic", "google", or "mock".`);
26
+ }
27
+ }
28
+ export function envHintForProvider(provider) {
29
+ switch (provider) {
30
+ case "openai":
31
+ return "Export OPENAI_API_KEY in your environment.";
32
+ case "anthropic":
33
+ return "Export ANTHROPIC_API_KEY in your environment.";
34
+ case "google":
35
+ return "Export GOOGLE_API_KEY (or GEMINI_API_KEY) in your environment.";
36
+ default:
37
+ return "";
38
+ }
39
+ }
40
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/adapters/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAgB;IACxD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,WAAW;YACd,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,QAAQ;YACX,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,MAAM;YACT,OAAO,IAAI,WAAW,EAAE,CAAC;QAC3B;YACE,MAAM,IAAI,KAAK,CACb,aAAa,QAAQ,mBAAmB;gBACtC,iDAAiD,CACpD,CAAC;IACN,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,4CAA4C,CAAC;QACtD,KAAK,WAAW;YACd,OAAO,+CAA+C,CAAC;QACzD,KAAK,QAAQ;YACX,OAAO,gEAAgE,CAAC;QAC1E;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Keychain adapter tests.
3
+ *
4
+ * Strategy (addresses Codex's v0.8 plan-review R3 — "KeychainAdapter
5
+ * dependency injection"): we mock the dynamic import of
6
+ * `@napi-rs/keyring` itself, replacing the Entry class with a fake
7
+ * we control per test. This sidesteps needing to refactor the real
8
+ * code to accept an injected adapter — the dynamic-import boundary
9
+ * IS the seam we test against.
10
+ *
11
+ * Codex's main concern (P2 #4) was that read failures were silently
12
+ * converted into "absent". These tests prove the new behavior:
13
+ * - found → { kind: "found", key }
14
+ * - absent → { kind: "absent" }
15
+ * - throws → { kind: "error", message } (was: silently dropped)
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=keychain.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.test.d.ts","sourceRoot":"","sources":["../../../src/auth/__tests__/keychain.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Keychain adapter tests.
3
+ *
4
+ * Strategy (addresses Codex's v0.8 plan-review R3 — "KeychainAdapter
5
+ * dependency injection"): we mock the dynamic import of
6
+ * `@napi-rs/keyring` itself, replacing the Entry class with a fake
7
+ * we control per test. This sidesteps needing to refactor the real
8
+ * code to accept an injected adapter — the dynamic-import boundary
9
+ * IS the seam we test against.
10
+ *
11
+ * Codex's main concern (P2 #4) was that read failures were silently
12
+ * converted into "absent". These tests prove the new behavior:
13
+ * - found → { kind: "found", key }
14
+ * - absent → { kind: "absent" }
15
+ * - throws → { kind: "error", message } (was: silently dropped)
16
+ */
17
+ import { describe, it, expect, beforeEach, vi } from "vitest";
18
+ import { _resetKeychainCache } from "../keychain.js";
19
+ // Track the entry methods per test so we can rewire mid-suite.
20
+ let mockGetPassword;
21
+ let mockSetPassword;
22
+ let mockDeletePassword;
23
+ let constructorThrows = false;
24
+ let importThrows = false;
25
+ vi.mock("@napi-rs/keyring", () => ({
26
+ get Entry() {
27
+ if (importThrows)
28
+ throw new Error("synthetic import failure");
29
+ return class FakeEntry {
30
+ constructor(_service, _account) {
31
+ if (constructorThrows) {
32
+ throw new Error("native binding refused to initialize");
33
+ }
34
+ }
35
+ getPassword() {
36
+ return mockGetPassword();
37
+ }
38
+ setPassword(p) {
39
+ mockSetPassword(p);
40
+ }
41
+ deletePassword() {
42
+ return mockDeletePassword();
43
+ }
44
+ };
45
+ },
46
+ }));
47
+ import { getKeychain } from "../keychain.js";
48
+ beforeEach(() => {
49
+ _resetKeychainCache();
50
+ mockGetPassword = () => null;
51
+ mockSetPassword = () => { };
52
+ mockDeletePassword = () => true;
53
+ constructorThrows = false;
54
+ importThrows = false;
55
+ });
56
+ describe("keychain adapter — availability", () => {
57
+ it("reports available=true when native binding loads cleanly", async () => {
58
+ const kc = await getKeychain();
59
+ expect(kc.available).toBe(true);
60
+ expect(kc.describeBackend()).not.toContain("unavailable");
61
+ });
62
+ it("reports available=false + reason when native binding throws on init", async () => {
63
+ constructorThrows = true;
64
+ const kc = await getKeychain();
65
+ expect(kc.available).toBe(false);
66
+ expect(kc.describeBackend()).toContain("unavailable");
67
+ expect(kc.describeBackend()).toMatch(/initialize|native/i);
68
+ });
69
+ it("get on unavailable adapter returns { kind: 'absent' } (not throw)", async () => {
70
+ constructorThrows = true;
71
+ const kc = await getKeychain();
72
+ const r = await kc.get("openai");
73
+ expect(r).toEqual({ kind: "absent" });
74
+ });
75
+ it("set on unavailable adapter throws with a clear hint", async () => {
76
+ constructorThrows = true;
77
+ const kc = await getKeychain();
78
+ await expect(kc.set("openai", "x")).rejects.toThrow(/unavailable|environment/i);
79
+ });
80
+ });
81
+ describe("keychain adapter — get behavior (Codex P2 #4 regression coverage)", () => {
82
+ it("found path returns { kind: 'found', key }", async () => {
83
+ mockGetPassword = () => "stored-secret";
84
+ const kc = await getKeychain();
85
+ const r = await kc.get("anthropic");
86
+ expect(r).toEqual({ kind: "found", key: "stored-secret" });
87
+ });
88
+ it("absent path returns { kind: 'absent' } when getPassword returns null", async () => {
89
+ mockGetPassword = () => null;
90
+ const kc = await getKeychain();
91
+ const r = await kc.get("openai");
92
+ expect(r).toEqual({ kind: "absent" });
93
+ });
94
+ it("error path returns { kind: 'error', message } when getPassword throws — NOT silently absent", async () => {
95
+ mockGetPassword = () => {
96
+ throw new Error("keyring locked by OS");
97
+ };
98
+ const kc = await getKeychain();
99
+ const r = await kc.get("openai");
100
+ expect(r.kind).toBe("error");
101
+ if (r.kind === "error") {
102
+ expect(r.message).toContain("keyring locked");
103
+ }
104
+ });
105
+ it("error path preserves non-Error throws via String(err)", async () => {
106
+ mockGetPassword = () => {
107
+ throw "raw string error";
108
+ };
109
+ const kc = await getKeychain();
110
+ const r = await kc.get("openai");
111
+ expect(r.kind).toBe("error");
112
+ if (r.kind === "error") {
113
+ expect(r.message).toContain("raw string error");
114
+ }
115
+ });
116
+ });
117
+ describe("keychain adapter — set / remove", () => {
118
+ it("set forwards the key to the underlying Entry", async () => {
119
+ const writes = [];
120
+ mockSetPassword = (p) => writes.push(p);
121
+ const kc = await getKeychain();
122
+ await kc.set("google", "new-key");
123
+ expect(writes).toEqual(["new-key"]);
124
+ });
125
+ it("remove returns true when entry existed", async () => {
126
+ mockDeletePassword = () => true;
127
+ const kc = await getKeychain();
128
+ const removed = await kc.remove("openai");
129
+ expect(removed).toBe(true);
130
+ });
131
+ it("remove returns false (not throws) when entry didn't exist", async () => {
132
+ mockDeletePassword = () => {
133
+ throw new Error("not found");
134
+ };
135
+ const kc = await getKeychain();
136
+ const removed = await kc.remove("openai");
137
+ expect(removed).toBe(false);
138
+ });
139
+ });
140
+ describe("keychain adapter — caching", () => {
141
+ it("getKeychain returns the same instance across calls (singleton)", async () => {
142
+ const a = await getKeychain();
143
+ const b = await getKeychain();
144
+ expect(a).toBe(b);
145
+ });
146
+ it("_resetKeychainCache forces a fresh load on next getKeychain", async () => {
147
+ const a = await getKeychain();
148
+ _resetKeychainCache();
149
+ constructorThrows = true; // change behavior between loads
150
+ const b = await getKeychain();
151
+ expect(a.available).toBe(true);
152
+ expect(b.available).toBe(false);
153
+ });
154
+ });
155
+ //# sourceMappingURL=keychain.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.test.js","sourceRoot":"","sources":["../../../src/auth/__tests__/keychain.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Resolver tests — proves the priority chain.
3
+ *
4
+ * Codex's v0.8 security review (P1 #3) called out that without these
5
+ * tests, the resolver could silently regress and ship — because
6
+ * `npm run test` would pass vacuously.
7
+ *
8
+ * Each test isolates the keychain via vi.mock so we control its
9
+ * behavior per case. Env vars are reset in beforeEach so test order
10
+ * doesn't matter.
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=resolver.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.test.d.ts","sourceRoot":"","sources":["../../../src/auth/__tests__/resolver.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Resolver tests — proves the priority chain.
3
+ *
4
+ * Codex's v0.8 security review (P1 #3) called out that without these
5
+ * tests, the resolver could silently regress and ship — because
6
+ * `npm run test` would pass vacuously.
7
+ *
8
+ * Each test isolates the keychain via vi.mock so we control its
9
+ * behavior per case. Env vars are reset in beforeEach so test order
10
+ * doesn't matter.
11
+ */
12
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
13
+ import { resolveApiKey, requireApiKey } from "../resolver.js";
14
+ import { AuthMissingError } from "../types.js";
15
+ // Mock the keychain module before any test runs. Each test then
16
+ // overrides the mock's return for getKeychain().
17
+ vi.mock("../keychain.js", () => ({
18
+ getKeychain: vi.fn(),
19
+ }));
20
+ import { getKeychain } from "../keychain.js";
21
+ const PROVIDER_ENV_VAR = {
22
+ openai: "OPENAI_API_KEY",
23
+ anthropic: "ANTHROPIC_API_KEY",
24
+ google: "GOOGLE_API_KEY",
25
+ };
26
+ function makeKeychainStub(behavior) {
27
+ return {
28
+ available: behavior.available,
29
+ describeBackend: () => "test-backend",
30
+ get: behavior.get ?? (async () => ({ kind: "absent" })),
31
+ set: async () => { },
32
+ remove: async () => true,
33
+ };
34
+ }
35
+ describe("resolveApiKey — priority order", () => {
36
+ const savedEnv = {};
37
+ beforeEach(() => {
38
+ // Snapshot + clear all provider env vars so test inputs are
39
+ // deterministic.
40
+ for (const v of Object.values(PROVIDER_ENV_VAR)) {
41
+ savedEnv[v] = process.env[v];
42
+ delete process.env[v];
43
+ }
44
+ delete process.env.GEMINI_API_KEY;
45
+ vi.clearAllMocks();
46
+ });
47
+ afterEach(() => {
48
+ // Restore original env so we don't pollute the runner / sibling tests.
49
+ for (const [k, v] of Object.entries(savedEnv)) {
50
+ if (v === undefined)
51
+ delete process.env[k];
52
+ else
53
+ process.env[k] = v;
54
+ }
55
+ });
56
+ it("explicit param wins over env (which would otherwise win over keychain)", async () => {
57
+ process.env.OPENAI_API_KEY = "from-env";
58
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({
59
+ available: true,
60
+ get: async () => ({ kind: "found", key: "from-keychain" }),
61
+ }));
62
+ const r = await resolveApiKey("openai", { explicit: "from-explicit" });
63
+ expect(r.source).toBe("explicit");
64
+ expect(r.key).toBe("from-explicit");
65
+ });
66
+ it("env wins over keychain — prevents stale-keychain-key bug Codex flagged", async () => {
67
+ process.env.OPENAI_API_KEY = "from-env";
68
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({
69
+ available: true,
70
+ get: async () => ({ kind: "found", key: "from-keychain" }),
71
+ }));
72
+ const r = await resolveApiKey("openai");
73
+ expect(r.source).toBe("env");
74
+ expect(r.key).toBe("from-env");
75
+ expect(r.envVar).toBe("OPENAI_API_KEY");
76
+ });
77
+ it("keychain wins over missing", async () => {
78
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({
79
+ available: true,
80
+ get: async () => ({ kind: "found", key: "from-keychain" }),
81
+ }));
82
+ const r = await resolveApiKey("openai");
83
+ expect(r.source).toBe("keychain");
84
+ expect(r.key).toBe("from-keychain");
85
+ });
86
+ it("returns missing when no source has the key", async () => {
87
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({ available: true }));
88
+ const r = await resolveApiKey("openai");
89
+ expect(r.source).toBe("missing");
90
+ expect(r.key).toBeUndefined();
91
+ });
92
+ it("returns keychain_error (not missing) when keychain read fails", async () => {
93
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({
94
+ available: true,
95
+ get: async () => ({ kind: "error", message: "denied by OS" }),
96
+ }));
97
+ const r = await resolveApiKey("openai");
98
+ expect(r.source).toBe("keychain_error");
99
+ expect(r.keychainError).toBe("denied by OS");
100
+ });
101
+ it("keychain unavailability degrades to missing (not error) — backward compat", async () => {
102
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({ available: false }));
103
+ const r = await resolveApiKey("openai");
104
+ expect(r.source).toBe("missing");
105
+ });
106
+ it("skipKeychain option bypasses keychain entirely", async () => {
107
+ process.env.OPENAI_API_KEY = "from-env";
108
+ // Keychain mock would throw if called — proves skipKeychain takes effect.
109
+ vi.mocked(getKeychain).mockImplementation(() => {
110
+ throw new Error("getKeychain should not be called when skipKeychain is true");
111
+ });
112
+ const r = await resolveApiKey("openai", { skipKeychain: true });
113
+ expect(r.source).toBe("env");
114
+ });
115
+ it("Google provider checks BOTH GOOGLE_API_KEY and GEMINI_API_KEY", async () => {
116
+ delete process.env.GOOGLE_API_KEY;
117
+ process.env.GEMINI_API_KEY = "from-gemini-env";
118
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({ available: false }));
119
+ const r = await resolveApiKey("google");
120
+ expect(r.source).toBe("env");
121
+ expect(r.key).toBe("from-gemini-env");
122
+ expect(r.envVar).toBe("GEMINI_API_KEY");
123
+ });
124
+ it("Google prefers GOOGLE_API_KEY over GEMINI_API_KEY when both set", async () => {
125
+ process.env.GOOGLE_API_KEY = "primary";
126
+ process.env.GEMINI_API_KEY = "secondary";
127
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({ available: false }));
128
+ const r = await resolveApiKey("google");
129
+ expect(r.source).toBe("env");
130
+ expect(r.envVar).toBe("GOOGLE_API_KEY");
131
+ expect(r.key).toBe("primary");
132
+ });
133
+ it("empty env var is treated as not set (falls through to keychain)", async () => {
134
+ process.env.OPENAI_API_KEY = "";
135
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({
136
+ available: true,
137
+ get: async () => ({ kind: "found", key: "from-keychain" }),
138
+ }));
139
+ const r = await resolveApiKey("openai");
140
+ expect(r.source).toBe("keychain");
141
+ });
142
+ });
143
+ describe("requireApiKey — throws AuthMissingError on missing / keychain_error", () => {
144
+ const savedEnv = {};
145
+ beforeEach(() => {
146
+ for (const v of Object.values(PROVIDER_ENV_VAR)) {
147
+ savedEnv[v] = process.env[v];
148
+ delete process.env[v];
149
+ }
150
+ delete process.env.GEMINI_API_KEY;
151
+ vi.clearAllMocks();
152
+ });
153
+ afterEach(() => {
154
+ for (const [k, v] of Object.entries(savedEnv)) {
155
+ if (v === undefined)
156
+ delete process.env[k];
157
+ else
158
+ process.env[k] = v;
159
+ }
160
+ });
161
+ it("returns the key when found", async () => {
162
+ process.env.ANTHROPIC_API_KEY = "the-key";
163
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({ available: false }));
164
+ const key = await requireApiKey("anthropic");
165
+ expect(key).toBe("the-key");
166
+ });
167
+ it("throws AuthMissingError with provider + env var when missing", async () => {
168
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({ available: true }));
169
+ await expect(requireApiKey("anthropic")).rejects.toThrow(AuthMissingError);
170
+ await expect(requireApiKey("anthropic")).rejects.toThrow(/anthropic/);
171
+ await expect(requireApiKey("anthropic")).rejects.toThrow(/ANTHROPIC_API_KEY/);
172
+ });
173
+ it("throws AuthMissingError with a DIFFERENT message for keychain_error", async () => {
174
+ vi.mocked(getKeychain).mockResolvedValue(makeKeychainStub({
175
+ available: true,
176
+ get: async () => ({ kind: "error", message: "keyring locked" }),
177
+ }));
178
+ await expect(requireApiKey("anthropic")).rejects.toThrow(/keyring locked/);
179
+ await expect(requireApiKey("anthropic")).rejects.toThrow(/Keychain read failed/);
180
+ });
181
+ });
182
+ //# sourceMappingURL=resolver.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.test.js","sourceRoot":"","sources":["../../../src/auth/__tests__/resolver.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Validator tests with mocked fetch.
3
+ *
4
+ * Covers Codex's v0.8 security review demand:
5
+ * "Add a regression test that failed validation never logs or returns
6
+ * a URL containing the key."
7
+ *
8
+ * Plus the broader contract:
9
+ * - happy path → ok: true with model
10
+ * - timeout → ok: false with friendly message
11
+ * - non-OK response → normalized error with statusCode, never raw body
12
+ * - submitted key NEVER appears in error / model field for ANY provider
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=validator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.test.d.ts","sourceRoot":"","sources":["../../../src/auth/__tests__/validator.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}