archondev 1.1.0 → 1.2.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.
- package/README.md +1 -1
- package/dist/bug-DXLBBW3U.js +1 -1
- package/dist/{chunk-4NZPAEKS.js → chunk-EDP55FCI.js} +23 -416
- package/dist/{chunk-QPYDPMUV.js → chunk-I4ZVNLNO.js} +1 -1
- package/dist/chunk-IMZN36GC.js +159 -0
- package/dist/{chunk-BL5TX2UW.js → chunk-PK3OQVBG.js} +1 -1
- package/dist/chunk-SMR7JQK6.js +399 -0
- package/dist/chunk-UG2ZZ7CM.js +737 -0
- package/dist/{execute-55VINPV5.js → execute-LYID2ODD.js} +4 -3
- package/dist/index.js +91 -584
- package/dist/keys-EL3FUM5O.js +15 -0
- package/dist/{list-LKYYAGSN.js → list-VXMVEIL5.js} +4 -3
- package/dist/{plan-MWUUJV3W.js → plan-7VSFESVD.js} +3 -2
- package/dist/preferences-PL2ON5VY.js +17 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ Copy governance files into any project. Works with your existing AI tools (Curso
|
|
|
57
57
|
| `archon credits budget` | Set monthly budget and alerts |
|
|
58
58
|
| `archon keys add <provider>` | Add your own API key — BYOK (Bring Your Own Key) |
|
|
59
59
|
| `archon keys list` | Show configured API keys by provider |
|
|
60
|
-
| `archon preferences` |
|
|
60
|
+
| `archon preferences` | Interactive settings menu (billing, models, keys, usage) |
|
|
61
61
|
| `archon models` | List available AI models |
|
|
62
62
|
|
|
63
63
|
## Pricing
|
package/dist/bug-DXLBBW3U.js
CHANGED
|
@@ -6,19 +6,22 @@ import {
|
|
|
6
6
|
createAtom,
|
|
7
7
|
validateAtom
|
|
8
8
|
} from "./chunk-MOZHC2GX.js";
|
|
9
|
-
import {
|
|
10
|
-
isAuthenticated
|
|
11
|
-
} from "./chunk-WCCBJSNI.js";
|
|
12
9
|
import {
|
|
13
10
|
AnthropicClient,
|
|
14
11
|
getDefaultModel
|
|
15
12
|
} from "./chunk-A7QU6JC6.js";
|
|
13
|
+
import {
|
|
14
|
+
KeyManager
|
|
15
|
+
} from "./chunk-SMR7JQK6.js";
|
|
16
|
+
import {
|
|
17
|
+
isAuthenticated
|
|
18
|
+
} from "./chunk-WCCBJSNI.js";
|
|
16
19
|
|
|
17
20
|
// src/cli/plan.ts
|
|
18
21
|
import chalk from "chalk";
|
|
19
|
-
import { existsSync
|
|
20
|
-
import { readFile
|
|
21
|
-
import { join
|
|
22
|
+
import { existsSync } from "fs";
|
|
23
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
24
|
+
import { join } from "path";
|
|
22
25
|
import { createInterface } from "readline";
|
|
23
26
|
|
|
24
27
|
// src/agents/sentinel.ts
|
|
@@ -277,400 +280,6 @@ var AdversarialPlanner = class {
|
|
|
277
280
|
}
|
|
278
281
|
};
|
|
279
282
|
|
|
280
|
-
// src/core/keys/manager.ts
|
|
281
|
-
import { homedir as homedir2 } from "os";
|
|
282
|
-
import { join } from "path";
|
|
283
|
-
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
284
|
-
import { existsSync } from "fs";
|
|
285
|
-
|
|
286
|
-
// src/core/keys/encryption.ts
|
|
287
|
-
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
|
|
288
|
-
import { homedir, hostname, userInfo } from "os";
|
|
289
|
-
var ALGORITHM = "aes-256-gcm";
|
|
290
|
-
var IV_LENGTH = 16;
|
|
291
|
-
var AUTH_TAG_LENGTH = 16;
|
|
292
|
-
var SALT_LENGTH = 32;
|
|
293
|
-
var KEY_LENGTH = 32;
|
|
294
|
-
function getDerivedKey(salt) {
|
|
295
|
-
const machineId = `${hostname()}-${userInfo().username}-${homedir()}`;
|
|
296
|
-
return scryptSync(machineId, salt, KEY_LENGTH);
|
|
297
|
-
}
|
|
298
|
-
function encrypt(plaintext) {
|
|
299
|
-
const salt = randomBytes(SALT_LENGTH);
|
|
300
|
-
const key = getDerivedKey(salt);
|
|
301
|
-
const iv = randomBytes(IV_LENGTH);
|
|
302
|
-
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
303
|
-
let encrypted = cipher.update(plaintext, "utf8", "hex");
|
|
304
|
-
encrypted += cipher.final("hex");
|
|
305
|
-
const authTag = cipher.getAuthTag();
|
|
306
|
-
const combined = Buffer.concat([
|
|
307
|
-
salt,
|
|
308
|
-
iv,
|
|
309
|
-
authTag,
|
|
310
|
-
Buffer.from(encrypted, "hex")
|
|
311
|
-
]);
|
|
312
|
-
return combined.toString("base64");
|
|
313
|
-
}
|
|
314
|
-
function decrypt(encryptedData) {
|
|
315
|
-
const combined = Buffer.from(encryptedData, "base64");
|
|
316
|
-
const salt = combined.subarray(0, SALT_LENGTH);
|
|
317
|
-
const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
|
|
318
|
-
const authTag = combined.subarray(
|
|
319
|
-
SALT_LENGTH + IV_LENGTH,
|
|
320
|
-
SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH
|
|
321
|
-
);
|
|
322
|
-
const encrypted = combined.subarray(SALT_LENGTH + IV_LENGTH + AUTH_TAG_LENGTH);
|
|
323
|
-
const key = getDerivedKey(salt);
|
|
324
|
-
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
|
325
|
-
decipher.setAuthTag(authTag);
|
|
326
|
-
let decrypted = decipher.update(encrypted.toString("hex"), "hex", "utf8");
|
|
327
|
-
decrypted += decipher.final("utf8");
|
|
328
|
-
return decrypted;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// src/core/keys/manager.ts
|
|
332
|
-
var CONFIG_DIR = join(homedir2(), ".archon");
|
|
333
|
-
var KEYS_FILE = join(CONFIG_DIR, "keys.json");
|
|
334
|
-
var CURRENT_VERSION = 2;
|
|
335
|
-
var KeyManager = class {
|
|
336
|
-
async ensureConfigDir() {
|
|
337
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
338
|
-
await mkdir(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
async loadKeys() {
|
|
342
|
-
try {
|
|
343
|
-
if (!existsSync(KEYS_FILE)) {
|
|
344
|
-
return { keys: [], version: CURRENT_VERSION };
|
|
345
|
-
}
|
|
346
|
-
const content = await readFile(KEYS_FILE, "utf-8");
|
|
347
|
-
const data = JSON.parse(content);
|
|
348
|
-
if (data.version === 1 && !Array.isArray(data.keys)) {
|
|
349
|
-
const oldKeys = data.keys;
|
|
350
|
-
const newKeys = Object.values(oldKeys).map((k) => ({
|
|
351
|
-
...k,
|
|
352
|
-
label: k.label ?? "default",
|
|
353
|
-
isPrimary: k.isPrimary ?? true
|
|
354
|
-
}));
|
|
355
|
-
const migrated = { keys: newKeys, version: CURRENT_VERSION };
|
|
356
|
-
await this.saveKeys(migrated);
|
|
357
|
-
return migrated;
|
|
358
|
-
}
|
|
359
|
-
return data;
|
|
360
|
-
} catch {
|
|
361
|
-
return { keys: [], version: CURRENT_VERSION };
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
async saveKeys(stored) {
|
|
365
|
-
await this.ensureConfigDir();
|
|
366
|
-
const content = JSON.stringify(stored, null, 2);
|
|
367
|
-
await writeFile(KEYS_FILE, content, { mode: 384 });
|
|
368
|
-
await chmod(KEYS_FILE, 384);
|
|
369
|
-
}
|
|
370
|
-
async addKey(provider, apiKey, label = "default") {
|
|
371
|
-
try {
|
|
372
|
-
const stored = await this.loadKeys();
|
|
373
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
374
|
-
const existingIndex = stored.keys.findIndex((k) => k.provider === provider && k.label === label);
|
|
375
|
-
const providerKeys = stored.keys.filter((k) => k.provider === provider);
|
|
376
|
-
const isPrimary = providerKeys.length === 0 || existingIndex >= 0 && stored.keys[existingIndex]?.isPrimary === true;
|
|
377
|
-
const newKey = {
|
|
378
|
-
provider,
|
|
379
|
-
encryptedKey: encrypt(apiKey),
|
|
380
|
-
label,
|
|
381
|
-
isPrimary,
|
|
382
|
-
createdAt: now,
|
|
383
|
-
updatedAt: now
|
|
384
|
-
};
|
|
385
|
-
if (existingIndex >= 0) {
|
|
386
|
-
const existing = stored.keys[existingIndex];
|
|
387
|
-
if (existing) {
|
|
388
|
-
newKey.createdAt = existing.createdAt;
|
|
389
|
-
newKey.isPrimary = existing.isPrimary;
|
|
390
|
-
}
|
|
391
|
-
stored.keys[existingIndex] = newKey;
|
|
392
|
-
} else {
|
|
393
|
-
stored.keys.push(newKey);
|
|
394
|
-
}
|
|
395
|
-
await this.saveKeys(stored);
|
|
396
|
-
return { success: true };
|
|
397
|
-
} catch (error) {
|
|
398
|
-
return {
|
|
399
|
-
success: false,
|
|
400
|
-
error: `Failed to save key: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
async getKey(provider, label) {
|
|
405
|
-
try {
|
|
406
|
-
const stored = await this.loadKeys();
|
|
407
|
-
let keyConfig;
|
|
408
|
-
if (label) {
|
|
409
|
-
keyConfig = stored.keys.find((k) => k.provider === provider && k.label === label);
|
|
410
|
-
} else {
|
|
411
|
-
keyConfig = stored.keys.find((k) => k.provider === provider && k.isPrimary);
|
|
412
|
-
if (!keyConfig) {
|
|
413
|
-
keyConfig = stored.keys.find((k) => k.provider === provider);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
if (!keyConfig) {
|
|
417
|
-
return null;
|
|
418
|
-
}
|
|
419
|
-
return decrypt(keyConfig.encryptedKey);
|
|
420
|
-
} catch {
|
|
421
|
-
return null;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
async removeKey(provider, label) {
|
|
425
|
-
try {
|
|
426
|
-
const stored = await this.loadKeys();
|
|
427
|
-
let index;
|
|
428
|
-
if (label) {
|
|
429
|
-
index = stored.keys.findIndex((k) => k.provider === provider && k.label === label);
|
|
430
|
-
} else {
|
|
431
|
-
index = stored.keys.findIndex((k) => k.provider === provider && k.isPrimary);
|
|
432
|
-
if (index < 0) {
|
|
433
|
-
index = stored.keys.findIndex((k) => k.provider === provider);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
if (index < 0) {
|
|
437
|
-
const labelMsg = label ? ` with label "${label}"` : "";
|
|
438
|
-
return {
|
|
439
|
-
success: false,
|
|
440
|
-
error: `No API key found for provider: ${provider}${labelMsg}`
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
const removedKey = stored.keys[index];
|
|
444
|
-
stored.keys.splice(index, 1);
|
|
445
|
-
if (removedKey?.isPrimary) {
|
|
446
|
-
const nextKey = stored.keys.find((k) => k.provider === provider);
|
|
447
|
-
if (nextKey) {
|
|
448
|
-
nextKey.isPrimary = true;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
await this.saveKeys(stored);
|
|
452
|
-
return { success: true };
|
|
453
|
-
} catch (error) {
|
|
454
|
-
return {
|
|
455
|
-
success: false,
|
|
456
|
-
error: `Failed to remove key: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
async setPrimary(provider, label) {
|
|
461
|
-
try {
|
|
462
|
-
const stored = await this.loadKeys();
|
|
463
|
-
const targetKey = stored.keys.find((k) => k.provider === provider && k.label === label);
|
|
464
|
-
if (!targetKey) {
|
|
465
|
-
return {
|
|
466
|
-
success: false,
|
|
467
|
-
error: `No key found for ${provider} with label "${label}"`
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
for (const key of stored.keys) {
|
|
471
|
-
if (key.provider === provider) {
|
|
472
|
-
key.isPrimary = key.label === label;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
await this.saveKeys(stored);
|
|
476
|
-
return { success: true };
|
|
477
|
-
} catch (error) {
|
|
478
|
-
return {
|
|
479
|
-
success: false,
|
|
480
|
-
error: `Failed to set primary: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
async listProviders() {
|
|
485
|
-
try {
|
|
486
|
-
const stored = await this.loadKeys();
|
|
487
|
-
const providers = /* @__PURE__ */ new Set();
|
|
488
|
-
for (const key of stored.keys) {
|
|
489
|
-
providers.add(key.provider);
|
|
490
|
-
}
|
|
491
|
-
return Array.from(providers);
|
|
492
|
-
} catch {
|
|
493
|
-
return [];
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
async listKeys(provider) {
|
|
497
|
-
try {
|
|
498
|
-
const stored = await this.loadKeys();
|
|
499
|
-
if (provider) {
|
|
500
|
-
return stored.keys.filter((k) => k.provider === provider);
|
|
501
|
-
}
|
|
502
|
-
return stored.keys;
|
|
503
|
-
} catch {
|
|
504
|
-
return [];
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
async hasAnyKey() {
|
|
508
|
-
const stored = await this.loadKeys();
|
|
509
|
-
return stored.keys.length > 0;
|
|
510
|
-
}
|
|
511
|
-
async getKeyConfig(provider, label) {
|
|
512
|
-
try {
|
|
513
|
-
const stored = await this.loadKeys();
|
|
514
|
-
if (label) {
|
|
515
|
-
return stored.keys.find((k) => k.provider === provider && k.label === label) ?? null;
|
|
516
|
-
}
|
|
517
|
-
return stored.keys.find((k) => k.provider === provider && k.isPrimary) ?? stored.keys.find((k) => k.provider === provider) ?? null;
|
|
518
|
-
} catch {
|
|
519
|
-
return null;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
async getAdversarialStatus() {
|
|
523
|
-
const providers = await this.listProviders();
|
|
524
|
-
const enabled = providers.length >= 2;
|
|
525
|
-
if (enabled) {
|
|
526
|
-
return {
|
|
527
|
-
enabled: true,
|
|
528
|
-
providerCount: providers.length,
|
|
529
|
-
providers,
|
|
530
|
-
message: `Adversarial features enabled (${providers.length} providers: ${providers.join(", ")})`
|
|
531
|
-
};
|
|
532
|
-
} else if (providers.length === 1) {
|
|
533
|
-
return {
|
|
534
|
-
enabled: false,
|
|
535
|
-
providerCount: 1,
|
|
536
|
-
providers,
|
|
537
|
-
message: `Adversarial features require keys from 2+ providers. Add another provider to unlock.`
|
|
538
|
-
};
|
|
539
|
-
} else {
|
|
540
|
-
return {
|
|
541
|
-
enabled: false,
|
|
542
|
-
providerCount: 0,
|
|
543
|
-
providers: [],
|
|
544
|
-
message: `No API keys configured. Add keys from 2+ providers to enable adversarial features.`
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
var keyManager = new KeyManager();
|
|
550
|
-
|
|
551
|
-
// src/core/keys/validator.ts
|
|
552
|
-
var KeyValidator = class {
|
|
553
|
-
async validateKey(provider, apiKey) {
|
|
554
|
-
switch (provider) {
|
|
555
|
-
case "anthropic":
|
|
556
|
-
return this.validateAnthropicKey(apiKey);
|
|
557
|
-
case "openai":
|
|
558
|
-
return this.validateOpenAIKey(apiKey);
|
|
559
|
-
case "google":
|
|
560
|
-
return this.validateGoogleKey(apiKey);
|
|
561
|
-
default:
|
|
562
|
-
return { valid: false, error: `Unknown provider: ${provider}`, provider };
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
async validateAnthropicKey(apiKey) {
|
|
566
|
-
try {
|
|
567
|
-
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
568
|
-
method: "POST",
|
|
569
|
-
headers: {
|
|
570
|
-
"Content-Type": "application/json",
|
|
571
|
-
"x-api-key": apiKey,
|
|
572
|
-
"anthropic-version": "2023-06-01"
|
|
573
|
-
},
|
|
574
|
-
body: JSON.stringify({
|
|
575
|
-
model: "claude-3-haiku-20240307",
|
|
576
|
-
max_tokens: 1,
|
|
577
|
-
messages: [{ role: "user", content: "Hi" }]
|
|
578
|
-
})
|
|
579
|
-
});
|
|
580
|
-
if (response.status === 401) {
|
|
581
|
-
return {
|
|
582
|
-
valid: false,
|
|
583
|
-
error: "Invalid API key. Please check your Anthropic API key.",
|
|
584
|
-
provider: "anthropic"
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
if (response.status === 400) {
|
|
588
|
-
const data = await response.json();
|
|
589
|
-
if (data.error?.message?.includes("credit")) {
|
|
590
|
-
return {
|
|
591
|
-
valid: false,
|
|
592
|
-
error: "API key valid but no credits. Please add credits to your Anthropic account.",
|
|
593
|
-
provider: "anthropic"
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
if (response.ok || response.status === 429 || response.status === 404) {
|
|
598
|
-
return { valid: true, provider: "anthropic" };
|
|
599
|
-
}
|
|
600
|
-
return {
|
|
601
|
-
valid: true,
|
|
602
|
-
provider: "anthropic"
|
|
603
|
-
};
|
|
604
|
-
} catch (error) {
|
|
605
|
-
return {
|
|
606
|
-
valid: false,
|
|
607
|
-
error: `Connection error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
608
|
-
provider: "anthropic"
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
async validateOpenAIKey(apiKey) {
|
|
613
|
-
try {
|
|
614
|
-
const response = await fetch("https://api.openai.com/v1/models", {
|
|
615
|
-
method: "GET",
|
|
616
|
-
headers: {
|
|
617
|
-
Authorization: `Bearer ${apiKey}`
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
if (response.status === 401) {
|
|
621
|
-
return {
|
|
622
|
-
valid: false,
|
|
623
|
-
error: "Invalid API key. Please check your OpenAI API key.",
|
|
624
|
-
provider: "openai"
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
if (response.ok) {
|
|
628
|
-
return { valid: true, provider: "openai" };
|
|
629
|
-
}
|
|
630
|
-
return {
|
|
631
|
-
valid: false,
|
|
632
|
-
error: `Validation failed with status ${response.status}`,
|
|
633
|
-
provider: "openai"
|
|
634
|
-
};
|
|
635
|
-
} catch (error) {
|
|
636
|
-
return {
|
|
637
|
-
valid: false,
|
|
638
|
-
error: `Connection error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
639
|
-
provider: "openai"
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
async validateGoogleKey(apiKey) {
|
|
644
|
-
try {
|
|
645
|
-
const response = await fetch(
|
|
646
|
-
`https://generativelanguage.googleapis.com/v1/models?key=${apiKey}`,
|
|
647
|
-
{ method: "GET" }
|
|
648
|
-
);
|
|
649
|
-
if (response.status === 400 || response.status === 401 || response.status === 403) {
|
|
650
|
-
return {
|
|
651
|
-
valid: false,
|
|
652
|
-
error: "Invalid API key. Please check your Google AI API key.",
|
|
653
|
-
provider: "google"
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
if (response.ok) {
|
|
657
|
-
return { valid: true, provider: "google" };
|
|
658
|
-
}
|
|
659
|
-
return {
|
|
660
|
-
valid: false,
|
|
661
|
-
error: `Validation failed with status ${response.status}`,
|
|
662
|
-
provider: "google"
|
|
663
|
-
};
|
|
664
|
-
} catch (error) {
|
|
665
|
-
return {
|
|
666
|
-
valid: false,
|
|
667
|
-
error: `Connection error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
668
|
-
provider: "google"
|
|
669
|
-
};
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
|
|
674
283
|
// src/cli/plan.ts
|
|
675
284
|
var ATOMS_DIR = ".archon/atoms";
|
|
676
285
|
function createPrompt() {
|
|
@@ -692,8 +301,8 @@ async function plan(description, options) {
|
|
|
692
301
|
console.log(chalk.yellow('Not authenticated. Run "archon login" first.'));
|
|
693
302
|
console.log(chalk.dim("For local development, you can continue without authentication."));
|
|
694
303
|
}
|
|
695
|
-
const archPath =
|
|
696
|
-
if (!
|
|
304
|
+
const archPath = join(process.cwd(), "ARCHITECTURE.md");
|
|
305
|
+
if (!existsSync(archPath)) {
|
|
697
306
|
console.error(chalk.red('ARCHITECTURE.md not found. Run "archon init" first.'));
|
|
698
307
|
process.exit(1);
|
|
699
308
|
}
|
|
@@ -724,8 +333,8 @@ Atom created: ${atom.externalId}`));
|
|
|
724
333
|
console.log(chalk.dim(`Acceptance Criteria: ${atom.acceptanceCriteria.length} items`));
|
|
725
334
|
let apiKey = process.env["ANTHROPIC_API_KEY"];
|
|
726
335
|
if (!apiKey) {
|
|
727
|
-
const
|
|
728
|
-
const storedKey = await
|
|
336
|
+
const keyManager = new KeyManager();
|
|
337
|
+
const storedKey = await keyManager.getKey("anthropic");
|
|
729
338
|
if (storedKey) {
|
|
730
339
|
apiKey = storedKey;
|
|
731
340
|
}
|
|
@@ -835,24 +444,24 @@ function displayPlan(plan2) {
|
|
|
835
444
|
console.log(chalk.bold("\nComplexity:"), plan2.estimated_complexity);
|
|
836
445
|
}
|
|
837
446
|
async function saveAtom(atom) {
|
|
838
|
-
const atomsDir =
|
|
839
|
-
if (!
|
|
840
|
-
await
|
|
447
|
+
const atomsDir = join(process.cwd(), ATOMS_DIR);
|
|
448
|
+
if (!existsSync(atomsDir)) {
|
|
449
|
+
await mkdir(atomsDir, { recursive: true });
|
|
841
450
|
}
|
|
842
|
-
const atomFile =
|
|
843
|
-
await
|
|
451
|
+
const atomFile = join(atomsDir, `${atom.externalId}.json`);
|
|
452
|
+
await writeFile(atomFile, JSON.stringify(atom, null, 2));
|
|
844
453
|
}
|
|
845
454
|
async function loadAtom(atomId) {
|
|
846
|
-
const atomFile =
|
|
847
|
-
if (!
|
|
455
|
+
const atomFile = join(process.cwd(), ATOMS_DIR, `${atomId}.json`);
|
|
456
|
+
if (!existsSync(atomFile)) {
|
|
848
457
|
return null;
|
|
849
458
|
}
|
|
850
|
-
const content = await
|
|
459
|
+
const content = await readFile(atomFile, "utf-8");
|
|
851
460
|
return JSON.parse(content);
|
|
852
461
|
}
|
|
853
462
|
async function listLocalAtoms() {
|
|
854
|
-
const atomsDir =
|
|
855
|
-
if (!
|
|
463
|
+
const atomsDir = join(process.cwd(), ATOMS_DIR);
|
|
464
|
+
if (!existsSync(atomsDir)) {
|
|
856
465
|
return [];
|
|
857
466
|
}
|
|
858
467
|
const { readdir } = await import("fs/promises");
|
|
@@ -870,8 +479,6 @@ async function listLocalAtoms() {
|
|
|
870
479
|
}
|
|
871
480
|
|
|
872
481
|
export {
|
|
873
|
-
keyManager,
|
|
874
|
-
KeyValidator,
|
|
875
482
|
plan,
|
|
876
483
|
loadAtom,
|
|
877
484
|
listLocalAtoms
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KeyValidator,
|
|
3
|
+
keyManager
|
|
4
|
+
} from "./chunk-SMR7JQK6.js";
|
|
5
|
+
import {
|
|
6
|
+
loadConfig,
|
|
7
|
+
saveConfig
|
|
8
|
+
} from "./chunk-WCCBJSNI.js";
|
|
9
|
+
|
|
10
|
+
// src/cli/keys.ts
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import ora from "ora";
|
|
13
|
+
import { createInterface } from "readline";
|
|
14
|
+
var VALID_PROVIDERS = ["anthropic", "openai", "google"];
|
|
15
|
+
function isValidProvider(provider) {
|
|
16
|
+
return VALID_PROVIDERS.includes(provider);
|
|
17
|
+
}
|
|
18
|
+
async function promptForKey(provider) {
|
|
19
|
+
const rl = createInterface({
|
|
20
|
+
input: process.stdin,
|
|
21
|
+
output: process.stdout
|
|
22
|
+
});
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
25
|
+
rl.question(`Enter your ${providerName} API key: `, (answer) => {
|
|
26
|
+
rl.close();
|
|
27
|
+
resolve(answer.trim());
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function addKey(provider, options = {}) {
|
|
32
|
+
if (!isValidProvider(provider)) {
|
|
33
|
+
console.error(chalk.red(`Invalid provider: ${provider}`));
|
|
34
|
+
console.log(chalk.dim(`Valid providers: ${VALID_PROVIDERS.join(", ")}`));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
const label = options.label ?? "default";
|
|
38
|
+
const apiKey = await promptForKey(provider);
|
|
39
|
+
if (!apiKey) {
|
|
40
|
+
console.error(chalk.red("API key cannot be empty"));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const spinner = ora("Validating API key...").start();
|
|
44
|
+
const validator = new KeyValidator();
|
|
45
|
+
const validationResult = await validator.validateKey(provider, apiKey);
|
|
46
|
+
if (!validationResult.valid) {
|
|
47
|
+
spinner.fail(chalk.red(`API key validation failed: ${validationResult.error}`));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
spinner.text = "Encrypting and storing API key...";
|
|
51
|
+
const result = await keyManager.addKey(provider, apiKey, label);
|
|
52
|
+
if (!result.success) {
|
|
53
|
+
spinner.fail(chalk.red(`Failed to store key: ${result.error}`));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const config = await loadConfig();
|
|
57
|
+
if (config.tier === "FREE") {
|
|
58
|
+
await saveConfig({ ...config, tier: "BYOK" });
|
|
59
|
+
spinner.succeed(chalk.green(`API key for ${provider} (${label}) added successfully!`));
|
|
60
|
+
console.log(chalk.blue("Your tier has been upgraded to BYOK (Bring Your Own Key)"));
|
|
61
|
+
console.log(chalk.dim("You now have access to all models and unlimited atoms."));
|
|
62
|
+
} else {
|
|
63
|
+
spinner.succeed(chalk.green(`API key for ${provider} (${label}) added successfully!`));
|
|
64
|
+
}
|
|
65
|
+
const adversarialStatus = await keyManager.getAdversarialStatus();
|
|
66
|
+
if (adversarialStatus.enabled) {
|
|
67
|
+
console.log(chalk.green(`\u2713 ${adversarialStatus.message}`));
|
|
68
|
+
} else {
|
|
69
|
+
console.log(chalk.yellow(`\u25CB ${adversarialStatus.message}`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function listKeys() {
|
|
73
|
+
const spinner = ora("Loading configured keys...").start();
|
|
74
|
+
const keys = await keyManager.listKeys();
|
|
75
|
+
spinner.stop();
|
|
76
|
+
if (keys.length === 0) {
|
|
77
|
+
console.log(chalk.yellow("No API keys configured"));
|
|
78
|
+
console.log(chalk.dim("Run `archon keys add <provider>` to add a key"));
|
|
79
|
+
console.log(chalk.dim(`Available providers: ${VALID_PROVIDERS.join(", ")}`));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
console.log(chalk.green("Configured API keys:"));
|
|
83
|
+
console.log();
|
|
84
|
+
const byProvider = {};
|
|
85
|
+
for (const key of keys) {
|
|
86
|
+
if (!byProvider[key.provider]) {
|
|
87
|
+
byProvider[key.provider] = [];
|
|
88
|
+
}
|
|
89
|
+
byProvider[key.provider]?.push(key);
|
|
90
|
+
}
|
|
91
|
+
for (const [provider, providerKeys] of Object.entries(byProvider)) {
|
|
92
|
+
console.log(chalk.blue(` ${provider.toUpperCase()}`));
|
|
93
|
+
for (const key of providerKeys ?? []) {
|
|
94
|
+
const addedDate = new Date(key.createdAt).toLocaleDateString();
|
|
95
|
+
const primaryBadge = key.isPrimary ? chalk.green(" [primary]") : "";
|
|
96
|
+
console.log(` \u2022 ${key.label}${primaryBadge} ${chalk.dim(`(added ${addedDate})`)}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
console.log();
|
|
100
|
+
const adversarialStatus = await keyManager.getAdversarialStatus();
|
|
101
|
+
if (adversarialStatus.enabled) {
|
|
102
|
+
console.log(chalk.green(`\u2713 ${adversarialStatus.message}`));
|
|
103
|
+
} else {
|
|
104
|
+
console.log(chalk.yellow(`\u25CB ${adversarialStatus.message}`));
|
|
105
|
+
}
|
|
106
|
+
console.log();
|
|
107
|
+
console.log(chalk.dim("Note: API keys are stored locally with encryption"));
|
|
108
|
+
}
|
|
109
|
+
async function removeKey(provider, options = {}) {
|
|
110
|
+
if (!isValidProvider(provider)) {
|
|
111
|
+
console.error(chalk.red(`Invalid provider: ${provider}`));
|
|
112
|
+
console.log(chalk.dim(`Valid providers: ${VALID_PROVIDERS.join(", ")}`));
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const label = options.label;
|
|
116
|
+
const labelMsg = label ? ` (${label})` : "";
|
|
117
|
+
const spinner = ora(`Removing API key for ${provider}${labelMsg}...`).start();
|
|
118
|
+
const result = await keyManager.removeKey(provider, label);
|
|
119
|
+
if (!result.success) {
|
|
120
|
+
spinner.fail(chalk.red(result.error ?? "Failed to remove key"));
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const hasRemainingKeys = await keyManager.hasAnyKey();
|
|
124
|
+
if (!hasRemainingKeys) {
|
|
125
|
+
const config = await loadConfig();
|
|
126
|
+
if (config.tier === "BYOK") {
|
|
127
|
+
await saveConfig({ ...config, tier: "FREE" });
|
|
128
|
+
spinner.succeed(chalk.green(`API key for ${provider}${labelMsg} removed`));
|
|
129
|
+
console.log(chalk.yellow("No API keys remaining. Tier reverted to FREE."));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
spinner.succeed(chalk.green(`API key for ${provider}${labelMsg} removed`));
|
|
134
|
+
const adversarialStatus = await keyManager.getAdversarialStatus();
|
|
135
|
+
if (!adversarialStatus.enabled && adversarialStatus.providerCount > 0) {
|
|
136
|
+
console.log(chalk.yellow(`\u25CB ${adversarialStatus.message}`));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function setPrimaryKey(provider, label) {
|
|
140
|
+
if (!isValidProvider(provider)) {
|
|
141
|
+
console.error(chalk.red(`Invalid provider: ${provider}`));
|
|
142
|
+
console.log(chalk.dim(`Valid providers: ${VALID_PROVIDERS.join(", ")}`));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
const spinner = ora(`Setting primary key for ${provider} to "${label}"...`).start();
|
|
146
|
+
const result = await keyManager.setPrimary(provider, label);
|
|
147
|
+
if (!result.success) {
|
|
148
|
+
spinner.fail(chalk.red(result.error ?? "Failed to set primary key"));
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
spinner.succeed(chalk.green(`Primary key for ${provider} set to "${label}"`));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export {
|
|
155
|
+
addKey,
|
|
156
|
+
listKeys,
|
|
157
|
+
removeKey,
|
|
158
|
+
setPrimaryKey
|
|
159
|
+
};
|