vskill 0.5.104 → 0.5.106

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 (86) hide show
  1. package/README.md +123 -2
  2. package/agents.json +1 -1
  3. package/dist/agents/agents-registry.d.ts +0 -1
  4. package/dist/agents/agents-registry.js +10 -30
  5. package/dist/agents/agents-registry.js.map +1 -1
  6. package/dist/api/client.d.ts +21 -0
  7. package/dist/api/client.js +39 -5
  8. package/dist/api/client.js.map +1 -1
  9. package/dist/commands/diff.d.ts +2 -5
  10. package/dist/commands/diff.js +82 -117
  11. package/dist/commands/diff.js.map +1 -1
  12. package/dist/commands/eval/serve.js +4 -0
  13. package/dist/commands/eval/serve.js.map +1 -1
  14. package/dist/commands/keys.d.ts +14 -0
  15. package/dist/commands/keys.js +166 -0
  16. package/dist/commands/keys.js.map +1 -0
  17. package/dist/commands/masked-stdin.d.ts +14 -0
  18. package/dist/commands/masked-stdin.js +79 -0
  19. package/dist/commands/masked-stdin.js.map +1 -0
  20. package/dist/commands/skill.d.ts +3 -29
  21. package/dist/commands/skill.js +6 -32
  22. package/dist/commands/skill.js.map +1 -1
  23. package/dist/eval/anthropic-catalog.d.ts +49 -0
  24. package/dist/eval/anthropic-catalog.js +238 -0
  25. package/dist/eval/anthropic-catalog.js.map +1 -0
  26. package/dist/eval/llm.d.ts +1 -1
  27. package/dist/eval/llm.js +68 -10
  28. package/dist/eval/llm.js.map +1 -1
  29. package/dist/eval/mcp-detector.js +24 -3
  30. package/dist/eval/mcp-detector.js.map +1 -1
  31. package/dist/eval/model-resolver.d.ts +39 -0
  32. package/dist/eval/model-resolver.js +94 -0
  33. package/dist/eval/model-resolver.js.map +1 -0
  34. package/dist/eval/pricing.js +42 -16
  35. package/dist/eval/pricing.js.map +1 -1
  36. package/dist/eval-server/api-routes.d.ts +9 -0
  37. package/dist/eval-server/api-routes.js +241 -73
  38. package/dist/eval-server/api-routes.js.map +1 -1
  39. package/dist/eval-server/authoring-routes.js +21 -1
  40. package/dist/eval-server/authoring-routes.js.map +1 -1
  41. package/dist/eval-server/boot-preflight.js +8 -10
  42. package/dist/eval-server/boot-preflight.js.map +1 -1
  43. package/dist/eval-server/darwin-migrator.d.ts +57 -0
  44. package/dist/eval-server/darwin-migrator.js +169 -0
  45. package/dist/eval-server/darwin-migrator.js.map +1 -0
  46. package/dist/eval-server/eval-server.d.ts +1 -0
  47. package/dist/eval-server/eval-server.js +21 -0
  48. package/dist/eval-server/eval-server.js.map +1 -1
  49. package/dist/eval-server/integration-routes.js +7 -0
  50. package/dist/eval-server/integration-routes.js.map +1 -1
  51. package/dist/eval-server/platform-proxy.d.ts +18 -0
  52. package/dist/eval-server/platform-proxy.js +153 -0
  53. package/dist/eval-server/platform-proxy.js.map +1 -0
  54. package/dist/eval-server/providers.d.ts +7 -12
  55. package/dist/eval-server/providers.js +13 -15
  56. package/dist/eval-server/providers.js.map +1 -1
  57. package/dist/eval-server/settings-store.d.ts +31 -26
  58. package/dist/eval-server/settings-store.js +246 -143
  59. package/dist/eval-server/settings-store.js.map +1 -1
  60. package/dist/eval-server/skill-create-routes.js +18 -0
  61. package/dist/eval-server/skill-create-routes.js.map +1 -1
  62. package/dist/eval-server/skill-name-resolver.d.ts +35 -0
  63. package/dist/eval-server/skill-name-resolver.js +146 -0
  64. package/dist/eval-server/skill-name-resolver.js.map +1 -0
  65. package/dist/eval-ui/assets/{CommandPalette-DiPALzlG.js → CommandPalette-COqdrmRl.js} +1 -1
  66. package/dist/eval-ui/assets/CreateSkillPage-C3IjO8es.js +12 -0
  67. package/dist/eval-ui/assets/UpdateDropdown-DnKKMBBN.js +1 -0
  68. package/dist/eval-ui/assets/index-Dmja1p3A.css +1 -0
  69. package/dist/eval-ui/assets/index-KIcQ5e5a.js +102 -0
  70. package/dist/eval-ui/index.html +2 -2
  71. package/dist/first-run-onboarding.d.ts +19 -0
  72. package/dist/first-run-onboarding.js +104 -0
  73. package/dist/first-run-onboarding.js.map +1 -0
  74. package/dist/index.js +18 -0
  75. package/dist/index.js.map +1 -1
  76. package/dist/installer/canonical.js +12 -13
  77. package/dist/installer/canonical.js.map +1 -1
  78. package/dist/utils/resolve-binary.js +7 -8
  79. package/dist/utils/resolve-binary.js.map +1 -1
  80. package/package.json +5 -2
  81. package/dist/eval-ui/assets/CreateSkillPage-BMrFELep.js +0 -12
  82. package/dist/eval-ui/assets/UpdateDropdown-Bj8kZzuR.js +0 -1
  83. package/dist/eval-ui/assets/_shimNode-D3bBqrAh.js +0 -1
  84. package/dist/eval-ui/assets/index-BSPDkfZG.js +0 -102
  85. package/dist/eval-ui/assets/index-C0Gc_4KC.css +0 -1
  86. package/dist/eval-ui/assets/resolve-binary-DIxhrZ6O.js +0 -2
@@ -1,39 +1,44 @@
1
- import * as childProcess from "node:child_process";
2
- declare const realSpawn: typeof childProcess.spawn;
3
- export type Provider = "anthropic" | "openrouter";
4
- export type Tier = "browser" | "keychain";
1
+ import * as nodeFs from "node:fs";
2
+ import { type ProviderId } from "./providers.js";
3
+ export type Provider = ProviderId;
4
+ export type { ProviderId } from "./providers.js";
5
5
  export interface KeyMetadata {
6
6
  stored: boolean;
7
7
  updatedAt: string | null;
8
- tier: Tier;
9
8
  }
10
9
  export interface ListKeysResponse {
11
10
  anthropic: KeyMetadata;
11
+ openai: KeyMetadata;
12
12
  openrouter: KeyMetadata;
13
13
  }
14
- export declare class UnsupportedTierError extends Error {
15
- constructor(message: string);
16
- }
17
- export declare function redactKey(key: string): string;
18
- type Logger = {
14
+ export interface Logger {
19
15
  warn: (msg: string) => void;
20
16
  error: (msg: string) => void;
21
- };
22
- export declare function _setLogger(l: Logger): void;
23
- export declare function _setSpawn(s: typeof realSpawn): void;
24
- export declare function _setPlatformOverride(p: NodeJS.Platform | null): void;
25
- export declare function resetSettingsStore(): void;
26
- export declare function getTier(): Tier;
27
- export declare function setTier(tier: Tier): Promise<void>;
28
- export declare function saveKey(provider: Provider, key: string, tier?: Tier): Promise<{
17
+ }
18
+ interface FsFacade {
19
+ writeFileSync: typeof nodeFs.writeFileSync;
20
+ renameSync: typeof nodeFs.renameSync;
21
+ chmodSync: typeof nodeFs.chmodSync;
22
+ readFileSync: typeof nodeFs.readFileSync;
23
+ existsSync: typeof nodeFs.existsSync;
24
+ unlinkSync: typeof nodeFs.unlinkSync;
25
+ mkdirSync: typeof nodeFs.mkdirSync;
26
+ statSync: typeof nodeFs.statSync;
27
+ }
28
+ export declare function getKeysFilePath(): string;
29
+ export declare function redactKey(key: string): string;
30
+ export declare function saveKey(provider: ProviderId, key: string): Promise<{
29
31
  updatedAt: string;
30
- tier: Tier;
31
32
  }>;
32
- export declare function readKey(provider: Provider): string | null;
33
- /** Synchronous accessor used by detectAvailableProviders safe, same semantics. */
34
- export declare function readKeySync(provider: Provider): string | null;
35
- /** Has-key predicate exposed for synchronous availability checks. */
36
- export declare function hasKeySync(provider: Provider): boolean;
37
- export declare function removeKey(provider: Provider): Promise<void>;
33
+ export declare function readKey(provider: ProviderId): string | null;
34
+ export declare function readKeySync(provider: ProviderId): string | null;
35
+ export declare function hasKeySync(provider: ProviderId): boolean;
36
+ export declare function removeKey(provider: ProviderId): Promise<void>;
38
37
  export declare function listKeys(): ListKeysResponse;
39
- export {};
38
+ export declare function mergeStoredKeysIntoEnv(): void;
39
+ export interface ResetOptions {
40
+ logger?: Logger;
41
+ fs?: FsFacade;
42
+ configDir?: string | null;
43
+ }
44
+ export declare function resetSettingsStore(opts?: ResetOptions): void;
@@ -1,195 +1,298 @@
1
1
  // ---------------------------------------------------------------------------
2
- // settings-store.ts — Tiered API key storage for vSkill Studio (0682 / US-004).
2
+ // settings-store.ts — File-backed credential store for vSkill Studio.
3
3
  //
4
- // Two tiers:
5
- // - "browser" (default, all platforms) in-memory map keyed by provider.
6
- // The browser mirrors the write into localStorage so a page
7
- // reload does not require re-entry.
8
- // - "keychain" (Darwin opt-in) — shells out to the `security` binary to
9
- // store under `vskill-<provider>`. No native-module dep.
4
+ // Single on-disk file at `<configDir>/keys.env` (default configDir:
5
+ // `~/.vskill`, overridable via VSKILL_CONFIG_DIR). KEY=VALUE dotenv format.
6
+ // Atomic write via temp-file + rename; POSIX writes explicit 0600.
10
7
  //
11
8
  // Contract:
12
- // - Keys NEVER appear in any log, error, toast, network URL, or stdout.
13
- // The only redacted form ever emitted is `****<last-4>` via `redactKey`.
14
- // - `listKeys()` returns metadata only (stored boolean, updatedAt, tier).
15
- // - `removeKey()` is idempotent.
16
- // - Tier switch (setTier) wipes the old tier's entry and re-saves into the
17
- // new tier so the live state stays coherent.
9
+ // - Raw keys NEVER appear in any log, error message, toast, or stdout.
10
+ // Only `redactKey()` output (`****<last-4>`) may be emitted.
11
+ // - `readKey()` consults `process.env` FIRST, then the raw-key `memoryMap`.
12
+ // After `mergeStoredKeysIntoEnv()` the `memoryMap` is cleared; subsequent
13
+ // reads hit process.env directly, minimizing plaintext dwell time.
14
+ // - `listKeys()` returns { stored, updatedAt } metadata only. A parallel
15
+ // `metadataMap` holds `{ updatedAt }` and SURVIVES merge, so listKeys()
16
+ // stays truthful across a boot cycle without keeping key bytes around.
17
+ // - `removeKey()` is idempotent; clears memoryMap, metadataMap, and the
18
+ // process.env entry (post-merge, env is where the live key lives).
19
+ // - Malformed lines in keys.env never crash parse — they are skipped with
20
+ // exactly ONE aggregated warning.
18
21
  //
19
- // Tests inject a fake logger + a fake spawn factory so the Darwin path is
20
- // exercisable on any host. See __tests__/settings-store.test.ts.
21
- // ---------------------------------------------------------------------------
22
- import * as childProcess from "node:child_process";
23
- // Lazy accessor some test files mock `node:child_process` without re-
24
- // exporting `spawn`. Reading through the namespace at call time avoids
25
- // touching the mock at module-load time, so those tests keep passing.
26
- const realSpawn = ((...args) => childProcess.spawn(...args));
27
- export class UnsupportedTierError extends Error {
28
- constructor(message) {
29
- super(message);
30
- this.name = "UnsupportedTierError";
31
- }
32
- }
33
- export function redactKey(key) {
34
- if (!key || key.length <= 4)
35
- return "****";
36
- return "****" + key.slice(-4);
37
- }
38
- const store = new Map();
39
- let currentTier = "browser";
40
- let logger = {
22
+ // Tests inject `{logger, fs, configDir}` via `resetSettingsStore(opts)`.
23
+ // ---------------------------------------------------------------------------
24
+ import * as nodeFs from "node:fs";
25
+ import * as os from "node:os";
26
+ import * as path from "node:path";
27
+ import { PROVIDERS, } from "./providers.js";
28
+ // ---------------------------------------------------------------------------
29
+ // Module-level state. Tests reset via `resetSettingsStore()`.
30
+ // ---------------------------------------------------------------------------
31
+ const defaultLogger = {
41
32
  warn: (m) => console.warn(m),
42
33
  error: (m) => console.error(m),
43
34
  };
44
- let spawn = realSpawn;
45
- let platformOverride = null;
46
- export function _setLogger(l) {
47
- logger = l;
35
+ // Wrappers read through the nodeFs namespace at CALL time so test files that
36
+ // mock `node:fs` without exporting every member (e.g. comparison-sse-events
37
+ // which only mocks existsSync/readFileSync/writeFileSync) don't fail at
38
+ // module-load time. Direct property access happens only inside the call.
39
+ const defaultFs = {
40
+ writeFileSync: (...args) => nodeFs.writeFileSync(...args),
41
+ renameSync: (...args) => nodeFs.renameSync(...args),
42
+ chmodSync: (...args) => nodeFs.chmodSync(...args),
43
+ readFileSync: ((...args) => nodeFs.readFileSync(...args)),
44
+ existsSync: (...args) => nodeFs.existsSync(...args),
45
+ unlinkSync: (...args) => nodeFs.unlinkSync(...args),
46
+ mkdirSync: ((...args) => nodeFs.mkdirSync(...args)),
47
+ statSync: ((...args) => nodeFs.statSync(...args)),
48
+ };
49
+ let logger = defaultLogger;
50
+ let fsImpl = defaultFs;
51
+ let configDirOverride = null;
52
+ const memoryMap = new Map();
53
+ // metadataMap retains `{ updatedAt }` after mergeStoredKeysIntoEnv() clears
54
+ // the raw-key memoryMap. Keeps listKeys() truthful across a boot cycle without
55
+ // extending plaintext dwell time.
56
+ const metadataMap = new Map();
57
+ let loaded = false;
58
+ // ---------------------------------------------------------------------------
59
+ // Path resolution
60
+ // ---------------------------------------------------------------------------
61
+ function resolveConfigDir() {
62
+ if (configDirOverride)
63
+ return configDirOverride;
64
+ const fromEnv = process.env.VSKILL_CONFIG_DIR;
65
+ if (fromEnv && fromEnv.length > 0)
66
+ return fromEnv;
67
+ return path.join(os.homedir(), ".vskill");
48
68
  }
49
- export function _setSpawn(s) {
50
- spawn = s;
69
+ export function getKeysFilePath() {
70
+ return path.join(resolveConfigDir(), "keys.env");
51
71
  }
52
- export function _setPlatformOverride(p) {
53
- platformOverride = p;
72
+ // ---------------------------------------------------------------------------
73
+ // Redaction
74
+ // ---------------------------------------------------------------------------
75
+ export function redactKey(key) {
76
+ if (!key || key.length <= 4)
77
+ return "****";
78
+ return "****" + key.slice(-4);
54
79
  }
55
- export function resetSettingsStore() {
56
- store.clear();
57
- currentTier = "browser";
58
- platformOverride = null;
59
- logger = {
60
- warn: (m) => console.warn(m),
61
- error: (m) => console.error(m),
62
- };
63
- spawn = realSpawn;
80
+ // ---------------------------------------------------------------------------
81
+ // Parser — lenient, single aggregated warning
82
+ // ---------------------------------------------------------------------------
83
+ function parseFile(contents) {
84
+ const entries = new Map();
85
+ let malformedCount = 0;
86
+ // Strip BOM.
87
+ const stripped = contents.replace(/^\uFEFF/, "");
88
+ const lines = stripped.split(/\r?\n/);
89
+ const envToId = new Map(PROVIDERS.map((p) => [p.envVarName, p.id]));
90
+ for (const rawLine of lines) {
91
+ const line = rawLine.trim();
92
+ if (line.length === 0)
93
+ continue;
94
+ if (line.startsWith("#"))
95
+ continue;
96
+ try {
97
+ const eq = line.indexOf("=");
98
+ if (eq <= 0) {
99
+ malformedCount++;
100
+ continue;
101
+ }
102
+ const name = line.slice(0, eq).trim();
103
+ const value = line.slice(eq + 1).trim();
104
+ if (name.length === 0 || value.length === 0) {
105
+ malformedCount++;
106
+ continue;
107
+ }
108
+ const pid = envToId.get(name);
109
+ if (!pid) {
110
+ // Unknown env var name — skip silently (not "malformed", just
111
+ // unrelated). Do NOT count toward malformedCount.
112
+ continue;
113
+ }
114
+ entries.set(pid, { key: value, updatedAt: new Date().toISOString() });
115
+ }
116
+ catch {
117
+ malformedCount++;
118
+ }
119
+ }
120
+ return { entries, malformedCount };
64
121
  }
65
- function getPlatform() {
66
- return platformOverride ?? process.platform;
122
+ function loadIfNeeded() {
123
+ if (loaded)
124
+ return;
125
+ loaded = true;
126
+ const filePath = getKeysFilePath();
127
+ if (!fsImpl.existsSync(filePath))
128
+ return;
129
+ try {
130
+ const contents = fsImpl.readFileSync(filePath, "utf8");
131
+ const { entries, malformedCount } = parseFile(contents);
132
+ for (const [pid, entry] of entries)
133
+ memoryMap.set(pid, entry);
134
+ if (malformedCount > 0) {
135
+ logger.warn(`[settings-store] ${malformedCount} malformed line(s) in ${filePath} were skipped`);
136
+ }
137
+ }
138
+ catch (err) {
139
+ logger.warn(`[settings-store] failed to read ${filePath}: ${err.message}`);
140
+ }
67
141
  }
68
142
  // ---------------------------------------------------------------------------
69
- // Tier management
143
+ // Serialization
70
144
  // ---------------------------------------------------------------------------
71
- export function getTier() {
72
- return currentTier;
145
+ function serialize(entries) {
146
+ const lines = [
147
+ "# vskill credentials — do not commit. Managed by `vskill keys` or Skill Studio Settings.",
148
+ ];
149
+ for (const p of PROVIDERS) {
150
+ const entry = entries.get(p.id);
151
+ if (entry)
152
+ lines.push(`${p.envVarName}=${entry.key}`);
153
+ }
154
+ return lines.join("\n") + "\n";
73
155
  }
74
- export async function setTier(tier) {
75
- if (tier === "keychain" && getPlatform() !== "darwin") {
76
- throw new UnsupportedTierError("macOS Keychain tier is only supported on Darwin");
156
+ function atomicWrite(contents) {
157
+ const filePath = getKeysFilePath();
158
+ const dir = path.dirname(filePath);
159
+ fsImpl.mkdirSync(dir, { recursive: true });
160
+ const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}`;
161
+ try {
162
+ fsImpl.writeFileSync(tmpPath, contents, "utf8");
163
+ if (process.platform !== "win32") {
164
+ try {
165
+ fsImpl.chmodSync(tmpPath, 0o600);
166
+ }
167
+ catch {
168
+ /* non-fatal; rename will still proceed */
169
+ }
170
+ }
171
+ fsImpl.renameSync(tmpPath, filePath);
77
172
  }
78
- if (tier === currentTier)
79
- return;
80
- // Move every stored key into the new tier.
81
- const entries = Array.from(store.entries());
82
- for (const [provider, entry] of entries) {
83
- if (tier === "keychain") {
84
- await keychainAdd(provider, entry.key);
85
- store.set(provider, { ...entry, tier });
173
+ catch (err) {
174
+ // Clean up tmp if present.
175
+ try {
176
+ if (fsImpl.existsSync(tmpPath))
177
+ fsImpl.unlinkSync(tmpPath);
86
178
  }
87
- else {
88
- // Switching browser → keychain → browser is also supported; clear the
89
- // keychain entry so it doesn't linger.
90
- await keychainDelete(provider).catch(() => { });
91
- store.set(provider, { ...entry, tier });
179
+ catch {
180
+ /* swallow */
92
181
  }
182
+ throw err;
93
183
  }
94
- currentTier = tier;
95
184
  }
96
185
  // ---------------------------------------------------------------------------
97
186
  // Public API — save / read / remove / list
98
187
  // ---------------------------------------------------------------------------
99
- export async function saveKey(provider, key, tier = currentTier) {
188
+ export async function saveKey(provider, key) {
100
189
  if (!key || typeof key !== "string" || key.trim().length === 0) {
101
190
  throw new Error("key must be non-empty string");
102
191
  }
103
- if (tier === "keychain" && getPlatform() !== "darwin") {
104
- throw new UnsupportedTierError("macOS Keychain tier is only supported on Darwin");
105
- }
192
+ loadIfNeeded();
193
+ // Build the full entry set: current memory snapshot + new entry.
106
194
  const updatedAt = new Date().toISOString();
195
+ const next = new Map(memoryMap);
196
+ next.set(provider, { key, updatedAt });
107
197
  try {
108
- if (tier === "keychain") {
109
- await keychainAdd(provider, key);
110
- }
111
- store.set(provider, { key, updatedAt, tier });
112
- currentTier = tier;
113
- return { updatedAt, tier };
198
+ atomicWrite(serialize(next));
114
199
  }
115
200
  catch (err) {
116
- // NEVER log the raw key only the redacted form.
117
- logger.error(`[settings-store] saveKey(${provider}, ${redactKey(key)}) failed: ${err.message}`);
118
- throw err;
201
+ // NEVER include the raw key in the error message.
202
+ const redacted = redactKey(key);
203
+ logger.error(`[settings-store] saveKey(${provider}, ${redacted}) failed: ${err.message}`);
204
+ throw new Error(`saveKey(${provider}, ${redacted}) failed: ${err.message}`);
119
205
  }
206
+ memoryMap.set(provider, { key, updatedAt });
207
+ metadataMap.set(provider, { updatedAt });
208
+ return { updatedAt };
120
209
  }
121
210
  export function readKey(provider) {
122
- return store.get(provider)?.key ?? null;
211
+ loadIfNeeded();
212
+ const descriptor = PROVIDERS.find((p) => p.id === provider);
213
+ if (descriptor) {
214
+ const fromEnv = process.env[descriptor.envVarName];
215
+ if (fromEnv && fromEnv.length > 0)
216
+ return fromEnv;
217
+ }
218
+ return memoryMap.get(provider)?.key ?? null;
123
219
  }
124
- /** Synchronous accessor used by detectAvailableProviders — safe, same semantics. */
125
220
  export function readKeySync(provider) {
126
221
  return readKey(provider);
127
222
  }
128
- /** Has-key predicate exposed for synchronous availability checks. */
129
223
  export function hasKeySync(provider) {
130
- return store.has(provider);
224
+ return readKey(provider) !== null;
131
225
  }
132
226
  export async function removeKey(provider) {
133
- const entry = store.get(provider);
134
- if (!entry)
227
+ loadIfNeeded();
228
+ // Idempotent: short-circuit only if neither map has the provider AND the
229
+ // on-disk file won't have it either. Post-merge, memoryMap is empty but
230
+ // metadataMap is the source of truth for "was it stored?".
231
+ if (!memoryMap.has(provider) && !metadataMap.has(provider))
135
232
  return;
136
- if (entry.tier === "keychain") {
137
- try {
138
- await keychainDelete(provider);
139
- }
140
- catch (err) {
141
- logger.warn(`[settings-store] keychain remove for ${provider} failed: ${err.message}`);
142
- }
233
+ const next = new Map(memoryMap);
234
+ next.delete(provider);
235
+ try {
236
+ atomicWrite(serialize(next));
143
237
  }
144
- store.delete(provider);
238
+ catch (err) {
239
+ logger.error(`[settings-store] removeKey(${provider}) failed: ${err.message}`);
240
+ throw err;
241
+ }
242
+ memoryMap.delete(provider);
243
+ metadataMap.delete(provider);
244
+ // Clear the process.env copy too — post-merge, this is where the live key
245
+ // lives. Without this, a "remove" followed by a readKey() would still
246
+ // resolve the removed provider from process.env.
247
+ const descriptor = PROVIDERS.find((p) => p.id === provider);
248
+ if (descriptor)
249
+ delete process.env[descriptor.envVarName];
145
250
  }
146
251
  export function listKeys() {
252
+ loadIfNeeded();
147
253
  const read = (p) => {
148
- const e = store.get(p);
149
- return e
150
- ? { stored: true, updatedAt: e.updatedAt, tier: e.tier }
151
- : { stored: false, updatedAt: null, tier: currentTier };
254
+ // memoryMap wins if present (freshest), else fall back to retained
255
+ // metadata from a prior mergeStoredKeysIntoEnv() call.
256
+ const live = memoryMap.get(p);
257
+ if (live)
258
+ return { stored: true, updatedAt: live.updatedAt };
259
+ const meta = metadataMap.get(p);
260
+ if (meta)
261
+ return { stored: true, updatedAt: meta.updatedAt };
262
+ return { stored: false, updatedAt: null };
263
+ };
264
+ return {
265
+ anthropic: read("anthropic"),
266
+ openai: read("openai"),
267
+ openrouter: read("openrouter"),
152
268
  };
153
- return { anthropic: read("anthropic"), openrouter: read("openrouter") };
154
269
  }
155
270
  // ---------------------------------------------------------------------------
156
- // Darwin Keychain bridge via `security` binary.
157
- // ---------------------------------------------------------------------------
158
- function keychainAdd(provider, key) {
159
- return new Promise((resolve, reject) => {
160
- const args = [
161
- "add-generic-password",
162
- "-s",
163
- `vskill-${provider}`,
164
- "-a",
165
- "vskill-user",
166
- "-w",
167
- key,
168
- "-U",
169
- ];
170
- const opts = { stdio: ["ignore", "pipe", "pipe"] };
171
- const child = spawn("security", args, opts);
172
- let stderr = "";
173
- child.stderr?.on("data", (d) => (stderr += String(d)));
174
- child.on("error", (err) => reject(err));
175
- child.on("close", (code) => {
176
- if (code === 0)
177
- resolve();
178
- else
179
- reject(new Error(`security add-generic-password exited ${code}: ${stderr.slice(0, 200)}`));
180
- });
181
- });
271
+ // Boot-time env merge
272
+ // ---------------------------------------------------------------------------
273
+ export function mergeStoredKeysIntoEnv() {
274
+ loadIfNeeded();
275
+ for (const p of PROVIDERS) {
276
+ const entry = memoryMap.get(p.id);
277
+ if (!entry)
278
+ continue;
279
+ // Nullish coalescing: real env vars always win.
280
+ if (process.env[p.envVarName] === undefined) {
281
+ process.env[p.envVarName] = entry.key;
282
+ }
283
+ // Retain metadata so listKeys() can report `stored: true` post-merge
284
+ // without keeping the raw key plaintext around.
285
+ metadataMap.set(p.id, { updatedAt: entry.updatedAt });
286
+ }
287
+ // Shrink plaintext dwell time.
288
+ memoryMap.clear();
182
289
  }
183
- function keychainDelete(provider) {
184
- return new Promise((resolve, reject) => {
185
- const child = spawn("security", ["delete-generic-password", "-s", `vskill-${provider}`, "-a", "vskill-user"], { stdio: ["ignore", "pipe", "pipe"] });
186
- child.on("error", (err) => reject(err));
187
- child.on("close", (code) => {
188
- if (code === 0 || code === 44)
189
- resolve(); // 44 = not found, idempotent
190
- else
191
- resolve(); // be lenient — delete must not block removeKey
192
- });
193
- });
290
+ export function resetSettingsStore(opts = {}) {
291
+ memoryMap.clear();
292
+ metadataMap.clear();
293
+ loaded = false;
294
+ logger = opts.logger ?? defaultLogger;
295
+ fsImpl = opts.fs ?? defaultFs;
296
+ configDirOverride = opts.configDir ?? null;
194
297
  }
195
298
  //# sourceMappingURL=settings-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings-store.js","sourceRoot":"","sources":["../../src/eval-server/settings-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"settings-store.js","sourceRoot":"","sources":["../../src/eval-server/settings-store.ts"],"names":[],"mappings":"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"}
@@ -1030,6 +1030,24 @@ export function registerSkillCreateRoutes(router, root) {
1030
1030
  if (ac.signal.aborted)
1031
1031
  return;
1032
1032
  const result = attachSuggestedPlugin(parsed, root);
1033
+ // 0711: emit provenance as a SEPARATE SSE event before `done` so the
1034
+ // GenerateSkillResult schema stays clean (parity tests assert on the
1035
+ // legacy shape). The UI's useCreateSkill listens for the `provenance`
1036
+ // event and stamps aiMeta. Best-effort — never blocks generation.
1037
+ if (wantsSSE && (resolved.provider === "anthropic" || resolved.provider === "claude-cli")) {
1038
+ try {
1039
+ const { resolveAnthropicModel } = await import("../eval/model-resolver.js");
1040
+ const r = resolveAnthropicModel(resolved.model);
1041
+ sendSSE(res, "provenance", {
1042
+ resolvedModelId: r.resolvedId || null,
1043
+ snapshotDate: r.snapshotDate,
1044
+ source: r.source,
1045
+ });
1046
+ }
1047
+ catch {
1048
+ /* provenance is best-effort; never block generation */
1049
+ }
1050
+ }
1033
1051
  if (wantsSSE)
1034
1052
  sendSSEDone(res, result);
1035
1053
  else