archondev 1.1.0 → 1.2.0
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/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 +1 -1
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
import {
|
|
2
|
+
keyManager
|
|
3
|
+
} from "./chunk-SMR7JQK6.js";
|
|
4
|
+
import {
|
|
5
|
+
getApiUrl,
|
|
6
|
+
getAuthToken,
|
|
7
|
+
loadConfig
|
|
8
|
+
} from "./chunk-WCCBJSNI.js";
|
|
9
|
+
|
|
10
|
+
// src/cli/preferences.ts
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import ora from "ora";
|
|
13
|
+
import readline from "readline";
|
|
14
|
+
|
|
15
|
+
// src/core/models/registry.json
|
|
16
|
+
var registry_default = {
|
|
17
|
+
lastUpdated: "2026-01-21T00:00:00Z",
|
|
18
|
+
version: "1.0.0",
|
|
19
|
+
providers: {
|
|
20
|
+
anthropic: {
|
|
21
|
+
displayName: "Anthropic",
|
|
22
|
+
fast: [
|
|
23
|
+
{
|
|
24
|
+
id: "claude-3-haiku-20240307",
|
|
25
|
+
name: "Claude 3 Haiku",
|
|
26
|
+
active: true,
|
|
27
|
+
costPer1kInput: 25e-5,
|
|
28
|
+
costPer1kOutput: 125e-5
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
thinking: [
|
|
32
|
+
{
|
|
33
|
+
id: "claude-3-5-sonnet-20241022",
|
|
34
|
+
name: "Claude 3.5 Sonnet",
|
|
35
|
+
active: true,
|
|
36
|
+
default: true,
|
|
37
|
+
costPer1kInput: 3e-3,
|
|
38
|
+
costPer1kOutput: 0.015
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "claude-3-opus-20240229",
|
|
42
|
+
name: "Claude 3 Opus",
|
|
43
|
+
active: true,
|
|
44
|
+
costPer1kInput: 0.015,
|
|
45
|
+
costPer1kOutput: 0.075
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
openai: {
|
|
50
|
+
displayName: "OpenAI",
|
|
51
|
+
fast: [
|
|
52
|
+
{
|
|
53
|
+
id: "gpt-4o-mini",
|
|
54
|
+
name: "GPT-4o Mini",
|
|
55
|
+
active: true,
|
|
56
|
+
costPer1kInput: 15e-5,
|
|
57
|
+
costPer1kOutput: 6e-4
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
thinking: [
|
|
61
|
+
{
|
|
62
|
+
id: "gpt-4o",
|
|
63
|
+
name: "GPT-4o",
|
|
64
|
+
active: true,
|
|
65
|
+
default: true,
|
|
66
|
+
costPer1kInput: 5e-3,
|
|
67
|
+
costPer1kOutput: 0.015
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "o1-preview",
|
|
71
|
+
name: "o1 Preview",
|
|
72
|
+
active: true,
|
|
73
|
+
costPer1kInput: 0.015,
|
|
74
|
+
costPer1kOutput: 0.06
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "o1-mini",
|
|
78
|
+
name: "o1 Mini",
|
|
79
|
+
active: true,
|
|
80
|
+
costPer1kInput: 3e-3,
|
|
81
|
+
costPer1kOutput: 0.012
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
google: {
|
|
86
|
+
displayName: "Google",
|
|
87
|
+
fast: [
|
|
88
|
+
{
|
|
89
|
+
id: "gemini-1.5-flash",
|
|
90
|
+
name: "Gemini 1.5 Flash",
|
|
91
|
+
active: true,
|
|
92
|
+
costPer1kInput: 75e-6,
|
|
93
|
+
costPer1kOutput: 3e-4
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
thinking: [
|
|
97
|
+
{
|
|
98
|
+
id: "gemini-1.5-pro",
|
|
99
|
+
name: "Gemini 1.5 Pro",
|
|
100
|
+
active: true,
|
|
101
|
+
default: true,
|
|
102
|
+
costPer1kInput: 125e-5,
|
|
103
|
+
costPer1kOutput: 5e-3
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "gemini-2.0-flash-thinking-exp",
|
|
107
|
+
name: "Gemini 2.0 Flash Thinking",
|
|
108
|
+
active: true,
|
|
109
|
+
costPer1kInput: 0,
|
|
110
|
+
costPer1kOutput: 0
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
defaults: {
|
|
116
|
+
fast: "claude-3-haiku-20240307",
|
|
117
|
+
thinking: "claude-3-5-sonnet-20241022",
|
|
118
|
+
primaryAdversarial: "claude-3-5-sonnet-20241022",
|
|
119
|
+
secondaryAdversarial: "gpt-4o"
|
|
120
|
+
},
|
|
121
|
+
freeModels: [
|
|
122
|
+
"claude-3-haiku-20240307",
|
|
123
|
+
"gpt-4o-mini",
|
|
124
|
+
"gemini-1.5-flash"
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// src/core/models/index.ts
|
|
129
|
+
var registry = registry_default;
|
|
130
|
+
function getAllActiveModels() {
|
|
131
|
+
const models = [];
|
|
132
|
+
for (const [provider, data] of Object.entries(registry.providers)) {
|
|
133
|
+
for (const model of data.fast.filter((m) => m.active)) {
|
|
134
|
+
models.push({
|
|
135
|
+
provider,
|
|
136
|
+
modelId: model.id,
|
|
137
|
+
modelName: model.name,
|
|
138
|
+
category: "fast"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
for (const model of data.thinking.filter((m) => m.active)) {
|
|
142
|
+
models.push({
|
|
143
|
+
provider,
|
|
144
|
+
modelId: model.id,
|
|
145
|
+
modelName: model.name,
|
|
146
|
+
category: "thinking"
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return models;
|
|
151
|
+
}
|
|
152
|
+
function getDefaultModel(category) {
|
|
153
|
+
return registry.defaults[category];
|
|
154
|
+
}
|
|
155
|
+
function getDefaultAdversarialModels() {
|
|
156
|
+
return {
|
|
157
|
+
primary: registry.defaults.primaryAdversarial,
|
|
158
|
+
secondary: registry.defaults.secondaryAdversarial
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function findModel(modelId) {
|
|
162
|
+
for (const [provider, data] of Object.entries(registry.providers)) {
|
|
163
|
+
for (const model of data.fast) {
|
|
164
|
+
if (model.id === modelId) {
|
|
165
|
+
return { ...model, provider, category: "fast" };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const model of data.thinking) {
|
|
169
|
+
if (model.id === modelId) {
|
|
170
|
+
return { ...model, provider, category: "thinking" };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
function getModelProvider(modelId) {
|
|
177
|
+
const model = findModel(modelId);
|
|
178
|
+
return model?.provider ?? null;
|
|
179
|
+
}
|
|
180
|
+
function isValidModel(modelId) {
|
|
181
|
+
const model = findModel(modelId);
|
|
182
|
+
return model !== null && model.active;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/cli/preferences.ts
|
|
186
|
+
var PREF_KEYS = ["fast-model", "thinking-model", "primary-adversarial", "secondary-adversarial"];
|
|
187
|
+
function prefKeyToDbColumn(key) {
|
|
188
|
+
const map = {
|
|
189
|
+
"fast-model": "pref_fast_model",
|
|
190
|
+
"thinking-model": "pref_thinking_model",
|
|
191
|
+
"primary-adversarial": "pref_primary_adversarial",
|
|
192
|
+
"secondary-adversarial": "pref_secondary_adversarial"
|
|
193
|
+
};
|
|
194
|
+
return map[key];
|
|
195
|
+
}
|
|
196
|
+
function isPrefKey(key) {
|
|
197
|
+
return PREF_KEYS.includes(key);
|
|
198
|
+
}
|
|
199
|
+
async function fetchPreferences() {
|
|
200
|
+
const config = await loadConfig();
|
|
201
|
+
const authToken = getAuthToken(config);
|
|
202
|
+
if (!authToken) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const apiUrl = getApiUrl(config);
|
|
206
|
+
try {
|
|
207
|
+
const response = await fetch(`${apiUrl}/api/preferences`, {
|
|
208
|
+
headers: {
|
|
209
|
+
"Authorization": `Bearer ${authToken}`
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
if (!response.ok) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
const data = await response.json();
|
|
216
|
+
return {
|
|
217
|
+
fastModel: data.pref_fast_model,
|
|
218
|
+
thinkingModel: data.pref_thinking_model,
|
|
219
|
+
primaryAdversarial: data.pref_primary_adversarial,
|
|
220
|
+
secondaryAdversarial: data.pref_secondary_adversarial
|
|
221
|
+
};
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function updatePreference(key, value) {
|
|
227
|
+
const config = await loadConfig();
|
|
228
|
+
const authToken = getAuthToken(config);
|
|
229
|
+
if (!authToken) {
|
|
230
|
+
return { success: false, error: "Not logged in. Run: archon login" };
|
|
231
|
+
}
|
|
232
|
+
const apiUrl = getApiUrl(config);
|
|
233
|
+
const column = prefKeyToDbColumn(key);
|
|
234
|
+
try {
|
|
235
|
+
const response = await fetch(`${apiUrl}/api/preferences`, {
|
|
236
|
+
method: "PATCH",
|
|
237
|
+
headers: {
|
|
238
|
+
"Authorization": `Bearer ${authToken}`,
|
|
239
|
+
"Content-Type": "application/json"
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify({ [column]: value })
|
|
242
|
+
});
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
const text = await response.text();
|
|
245
|
+
return { success: false, error: text };
|
|
246
|
+
}
|
|
247
|
+
return { success: true };
|
|
248
|
+
} catch (err) {
|
|
249
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
250
|
+
return { success: false, error: message };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async function getUserProviders() {
|
|
254
|
+
return keyManager.listProviders();
|
|
255
|
+
}
|
|
256
|
+
function formatModelDisplay(modelId, defaultId) {
|
|
257
|
+
if (!modelId) {
|
|
258
|
+
const model2 = findModel(defaultId);
|
|
259
|
+
return chalk.dim(`${model2?.name ?? defaultId} (default)`);
|
|
260
|
+
}
|
|
261
|
+
const model = findModel(modelId);
|
|
262
|
+
return model?.name ?? modelId;
|
|
263
|
+
}
|
|
264
|
+
async function showPreferences() {
|
|
265
|
+
const spinner = ora("Loading preferences...").start();
|
|
266
|
+
const prefs = await fetchPreferences();
|
|
267
|
+
const defaults = getDefaultAdversarialModels();
|
|
268
|
+
spinner.stop();
|
|
269
|
+
if (!prefs) {
|
|
270
|
+
console.log(chalk.yellow("Not logged in or unable to fetch preferences."));
|
|
271
|
+
console.log(chalk.dim("Using system defaults:"));
|
|
272
|
+
console.log();
|
|
273
|
+
console.log(` ${chalk.blue("Fast Model:")} ${formatModelDisplay(null, getDefaultModel("fast"))}`);
|
|
274
|
+
console.log(` ${chalk.blue("Thinking Model:")} ${formatModelDisplay(null, getDefaultModel("thinking"))}`);
|
|
275
|
+
console.log(` ${chalk.blue("Primary Adversarial:")} ${formatModelDisplay(null, defaults.primary)}`);
|
|
276
|
+
console.log(` ${chalk.blue("Secondary Adversarial:")} ${formatModelDisplay(null, defaults.secondary)}`);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
console.log(chalk.green("Model Preferences:"));
|
|
280
|
+
console.log();
|
|
281
|
+
console.log(` ${chalk.blue("Fast Model:")} ${formatModelDisplay(prefs.fastModel, getDefaultModel("fast"))}`);
|
|
282
|
+
console.log(` ${chalk.blue("Thinking Model:")} ${formatModelDisplay(prefs.thinkingModel, getDefaultModel("thinking"))}`);
|
|
283
|
+
console.log(` ${chalk.blue("Primary Adversarial:")} ${formatModelDisplay(prefs.primaryAdversarial, defaults.primary)}`);
|
|
284
|
+
console.log(` ${chalk.blue("Secondary Adversarial:")} ${formatModelDisplay(prefs.secondaryAdversarial, defaults.secondary)}`);
|
|
285
|
+
console.log();
|
|
286
|
+
const providers = await getUserProviders();
|
|
287
|
+
if (providers.length > 0) {
|
|
288
|
+
console.log(chalk.dim(`Available providers: ${providers.join(", ")}`));
|
|
289
|
+
} else {
|
|
290
|
+
console.log(chalk.dim("No API keys configured. Run: archon keys add <provider>"));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async function setPreference(key, modelId) {
|
|
294
|
+
if (!isPrefKey(key)) {
|
|
295
|
+
console.error(chalk.red(`Invalid preference key: ${key}`));
|
|
296
|
+
console.log(chalk.dim(`Valid keys: ${PREF_KEYS.join(", ")}`));
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
if (!isValidModel(modelId)) {
|
|
300
|
+
console.error(chalk.red(`Invalid or inactive model: ${modelId}`));
|
|
301
|
+
console.log(chalk.dim("Run: archon models to see available models"));
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
const model = findModel(modelId);
|
|
305
|
+
if (!model) {
|
|
306
|
+
console.error(chalk.red(`Model not found: ${modelId}`));
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
const providers = await getUserProviders();
|
|
310
|
+
const modelProvider = getModelProvider(modelId);
|
|
311
|
+
if (modelProvider && !providers.includes(modelProvider)) {
|
|
312
|
+
console.error(chalk.red(`You don't have an API key for ${modelProvider}`));
|
|
313
|
+
console.log(chalk.dim(`Add one with: archon keys add ${modelProvider}`));
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
if (key === "fast-model" && model.category !== "fast") {
|
|
317
|
+
console.log(chalk.yellow(`Note: ${model.name} is a ${model.category} model, typically used for complex tasks.`));
|
|
318
|
+
}
|
|
319
|
+
if (key === "thinking-model" && model.category !== "thinking") {
|
|
320
|
+
console.log(chalk.yellow(`Note: ${model.name} is a ${model.category} model, typically used for quick tasks.`));
|
|
321
|
+
}
|
|
322
|
+
const spinner = ora(`Setting ${key} to ${model.name}...`).start();
|
|
323
|
+
const result = await updatePreference(key, modelId);
|
|
324
|
+
if (!result.success) {
|
|
325
|
+
spinner.fail(chalk.red(result.error ?? "Failed to update preference"));
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
spinner.succeed(chalk.green(`${key} set to ${model.name}`));
|
|
329
|
+
}
|
|
330
|
+
async function resetPreferences() {
|
|
331
|
+
const spinner = ora("Resetting preferences to defaults...").start();
|
|
332
|
+
const results = await Promise.all([
|
|
333
|
+
updatePreference("fast-model", null),
|
|
334
|
+
updatePreference("thinking-model", null),
|
|
335
|
+
updatePreference("primary-adversarial", null),
|
|
336
|
+
updatePreference("secondary-adversarial", null)
|
|
337
|
+
]);
|
|
338
|
+
const failed = results.filter((r) => !r.success);
|
|
339
|
+
if (failed.length > 0) {
|
|
340
|
+
spinner.fail(chalk.red("Failed to reset some preferences"));
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
spinner.succeed(chalk.green("Preferences reset to system defaults"));
|
|
344
|
+
}
|
|
345
|
+
async function listModels() {
|
|
346
|
+
const models = getAllActiveModels();
|
|
347
|
+
const providers = await getUserProviders();
|
|
348
|
+
console.log(chalk.green("Available Models:"));
|
|
349
|
+
console.log();
|
|
350
|
+
const byProvider = {};
|
|
351
|
+
for (const model of models) {
|
|
352
|
+
const key = model.provider;
|
|
353
|
+
if (!byProvider[key]) {
|
|
354
|
+
byProvider[key] = [];
|
|
355
|
+
}
|
|
356
|
+
byProvider[key].push(model);
|
|
357
|
+
}
|
|
358
|
+
for (const [provider, providerModels] of Object.entries(byProvider)) {
|
|
359
|
+
const hasKey = providers.includes(provider);
|
|
360
|
+
const status = hasKey ? chalk.green("\u2713") : chalk.dim("\u25CB");
|
|
361
|
+
console.log(`${status} ${chalk.blue(provider.toUpperCase())}`);
|
|
362
|
+
for (const model of providerModels ?? []) {
|
|
363
|
+
const categoryLabel = model.category === "fast" ? chalk.cyan("[fast]") : chalk.magenta("[thinking]");
|
|
364
|
+
console.log(` ${model.modelId} ${categoryLabel}`);
|
|
365
|
+
console.log(chalk.dim(` ${model.modelName}`));
|
|
366
|
+
}
|
|
367
|
+
console.log();
|
|
368
|
+
}
|
|
369
|
+
if (providers.length === 0) {
|
|
370
|
+
console.log(chalk.dim("Add API keys to use models: archon keys add <provider>"));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
async function fetchUserProfile() {
|
|
374
|
+
const config = await loadConfig();
|
|
375
|
+
const authToken = getAuthToken(config);
|
|
376
|
+
if (!authToken) return null;
|
|
377
|
+
const apiUrl = getApiUrl(config);
|
|
378
|
+
try {
|
|
379
|
+
const response = await fetch(`${apiUrl}/api/preferences`, {
|
|
380
|
+
headers: { "Authorization": `Bearer ${authToken}` }
|
|
381
|
+
});
|
|
382
|
+
if (!response.ok) return null;
|
|
383
|
+
return await response.json();
|
|
384
|
+
} catch {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function fetchUsageSummary() {
|
|
389
|
+
const config = await loadConfig();
|
|
390
|
+
const authToken = getAuthToken(config);
|
|
391
|
+
if (!authToken) return null;
|
|
392
|
+
const apiUrl = getApiUrl(config);
|
|
393
|
+
try {
|
|
394
|
+
const response = await fetch(`${apiUrl}/api/usage`, {
|
|
395
|
+
headers: { "Authorization": `Bearer ${authToken}` }
|
|
396
|
+
});
|
|
397
|
+
if (!response.ok) return null;
|
|
398
|
+
return await response.json();
|
|
399
|
+
} catch {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async function updateUserTier(tier) {
|
|
404
|
+
const config = await loadConfig();
|
|
405
|
+
const authToken = getAuthToken(config);
|
|
406
|
+
if (!authToken) {
|
|
407
|
+
return { success: false, error: "Not logged in" };
|
|
408
|
+
}
|
|
409
|
+
const apiUrl = getApiUrl(config);
|
|
410
|
+
try {
|
|
411
|
+
const response = await fetch(`${apiUrl}/api/preferences`, {
|
|
412
|
+
method: "PATCH",
|
|
413
|
+
headers: {
|
|
414
|
+
"Authorization": `Bearer ${authToken}`,
|
|
415
|
+
"Content-Type": "application/json"
|
|
416
|
+
},
|
|
417
|
+
body: JSON.stringify({ tier })
|
|
418
|
+
});
|
|
419
|
+
if (!response.ok) {
|
|
420
|
+
const text = await response.text();
|
|
421
|
+
return { success: false, error: text };
|
|
422
|
+
}
|
|
423
|
+
return { success: true };
|
|
424
|
+
} catch (err) {
|
|
425
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
426
|
+
return { success: false, error: message };
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
function prompt(question) {
|
|
430
|
+
return new Promise((resolve) => {
|
|
431
|
+
const rl = readline.createInterface({
|
|
432
|
+
input: process.stdin,
|
|
433
|
+
output: process.stdout
|
|
434
|
+
});
|
|
435
|
+
rl.question(`${chalk.cyan("?")} ${question}: `, (answer) => {
|
|
436
|
+
rl.close();
|
|
437
|
+
resolve(answer);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function formatCost(dollars) {
|
|
442
|
+
if (dollars < 0.01) {
|
|
443
|
+
return `$${dollars.toFixed(4)}`;
|
|
444
|
+
}
|
|
445
|
+
return `$${dollars.toFixed(2)}`;
|
|
446
|
+
}
|
|
447
|
+
function formatTokens(tokens) {
|
|
448
|
+
if (tokens >= 1e6) {
|
|
449
|
+
return `${(tokens / 1e6).toFixed(2)}M`;
|
|
450
|
+
}
|
|
451
|
+
if (tokens >= 1e3) {
|
|
452
|
+
return `${(tokens / 1e3).toFixed(1)}K`;
|
|
453
|
+
}
|
|
454
|
+
return tokens.toString();
|
|
455
|
+
}
|
|
456
|
+
async function interactiveSettings() {
|
|
457
|
+
console.log(chalk.blue("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
458
|
+
console.log(chalk.bold.white(" Settings & Preferences"));
|
|
459
|
+
console.log(chalk.blue("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
460
|
+
const spinner = ora("Loading settings...").start();
|
|
461
|
+
const [profile, usage, providers] = await Promise.all([
|
|
462
|
+
fetchUserProfile(),
|
|
463
|
+
fetchUsageSummary(),
|
|
464
|
+
getUserProviders()
|
|
465
|
+
]);
|
|
466
|
+
spinner.stop();
|
|
467
|
+
if (!profile) {
|
|
468
|
+
console.log(chalk.yellow("Not logged in. Run: archon login\n"));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
await displayCurrentSettings(profile, usage, providers);
|
|
472
|
+
console.log(chalk.bold("\nOptions:\n"));
|
|
473
|
+
console.log(` ${chalk.cyan("1")}) Change billing mode (BYOK / Credits)`);
|
|
474
|
+
console.log(` ${chalk.cyan("2")}) Set default models`);
|
|
475
|
+
console.log(` ${chalk.cyan("3")}) Manage API keys`);
|
|
476
|
+
console.log(` ${chalk.cyan("4")}) View usage details`);
|
|
477
|
+
console.log(` ${chalk.cyan("5")}) Reset to defaults`);
|
|
478
|
+
console.log(` ${chalk.cyan("b")}) Back to project`);
|
|
479
|
+
console.log();
|
|
480
|
+
const choice = await prompt("Enter choice");
|
|
481
|
+
switch (choice.toLowerCase()) {
|
|
482
|
+
case "1":
|
|
483
|
+
await changeBillingMode(profile.tier);
|
|
484
|
+
await interactiveSettings();
|
|
485
|
+
break;
|
|
486
|
+
case "2":
|
|
487
|
+
await setDefaultModelsMenu(profile, providers);
|
|
488
|
+
await interactiveSettings();
|
|
489
|
+
break;
|
|
490
|
+
case "3":
|
|
491
|
+
await manageApiKeys();
|
|
492
|
+
await interactiveSettings();
|
|
493
|
+
break;
|
|
494
|
+
case "4":
|
|
495
|
+
await viewUsageDetails();
|
|
496
|
+
await interactiveSettings();
|
|
497
|
+
break;
|
|
498
|
+
case "5":
|
|
499
|
+
await resetPreferences();
|
|
500
|
+
await interactiveSettings();
|
|
501
|
+
break;
|
|
502
|
+
case "b":
|
|
503
|
+
console.log(chalk.dim("\nReturning to project...\n"));
|
|
504
|
+
return;
|
|
505
|
+
default:
|
|
506
|
+
console.log(chalk.yellow("Invalid choice."));
|
|
507
|
+
await interactiveSettings();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
async function displayCurrentSettings(profile, usage, providers) {
|
|
511
|
+
const defaults = getDefaultAdversarialModels();
|
|
512
|
+
const tierDisplay = profile.tier === "BYOK" ? chalk.cyan("BYOK (Bring Your Own Key)") : profile.tier === "CREDITS" ? chalk.green("Credits (Pay via ArchonDev)") : chalk.dim("Free Tier");
|
|
513
|
+
console.log(chalk.blue("Billing Mode:"), tierDisplay);
|
|
514
|
+
if (providers.length > 0) {
|
|
515
|
+
console.log(chalk.blue("API Keys:"), providers.map((p) => chalk.green(p)).join(", "));
|
|
516
|
+
} else {
|
|
517
|
+
console.log(chalk.blue("API Keys:"), chalk.dim("None configured"));
|
|
518
|
+
}
|
|
519
|
+
if (usage) {
|
|
520
|
+
console.log();
|
|
521
|
+
console.log(chalk.blue("Current Period Usage:"));
|
|
522
|
+
console.log(` Tokens: ${chalk.white(formatTokens(usage.totalInputTokens))} in / ${chalk.white(formatTokens(usage.totalOutputTokens))} out`);
|
|
523
|
+
console.log(` Estimated Cost: ${chalk.white(formatCost(usage.totalBaseCost))}`);
|
|
524
|
+
if (profile.tier === "BYOK") {
|
|
525
|
+
console.log(chalk.dim(" (You pay your provider directly)"));
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
console.log();
|
|
529
|
+
console.log(chalk.blue("Default Models:"));
|
|
530
|
+
console.log(` Fast: ${formatModelDisplay(profile.pref_fast_model, getDefaultModel("fast"))}`);
|
|
531
|
+
console.log(` Thinking: ${formatModelDisplay(profile.pref_thinking_model, getDefaultModel("thinking"))}`);
|
|
532
|
+
console.log(` Primary: ${formatModelDisplay(profile.pref_primary_adversarial, defaults.primary)}`);
|
|
533
|
+
console.log(` Secondary: ${formatModelDisplay(profile.pref_secondary_adversarial, defaults.secondary)}`);
|
|
534
|
+
}
|
|
535
|
+
async function changeBillingMode(currentTier) {
|
|
536
|
+
console.log(chalk.bold("\n\u2501\u2501\u2501 Billing Mode \u2501\u2501\u2501\n"));
|
|
537
|
+
console.log(chalk.dim("Choose how you want to pay for AI usage:\n"));
|
|
538
|
+
const byokSelected = currentTier === "BYOK";
|
|
539
|
+
const creditsSelected = currentTier === "CREDITS";
|
|
540
|
+
console.log(` ${chalk.cyan("1")}) ${byokSelected ? chalk.green("\u25CF ") : "\u25CB "}BYOK - Bring Your Own Key`);
|
|
541
|
+
console.log(chalk.dim(" Use your own API keys. You pay providers directly."));
|
|
542
|
+
console.log(chalk.dim(" We track usage so you can see approximate costs.\n"));
|
|
543
|
+
console.log(` ${chalk.cyan("2")}) ${creditsSelected ? chalk.green("\u25CF ") : "\u25CB "}Credits - Pay via ArchonDev`);
|
|
544
|
+
console.log(chalk.dim(" We handle API access. Pay only for what you use."));
|
|
545
|
+
console.log(chalk.dim(" 10% service fee applied.\n"));
|
|
546
|
+
console.log(` ${chalk.cyan("b")}) Back`);
|
|
547
|
+
console.log();
|
|
548
|
+
const choice = await prompt("Enter choice");
|
|
549
|
+
if (choice === "1" && !byokSelected) {
|
|
550
|
+
const spinner = ora("Switching to BYOK...").start();
|
|
551
|
+
const result = await updateUserTier("BYOK");
|
|
552
|
+
if (result.success) {
|
|
553
|
+
spinner.succeed(chalk.green("Switched to BYOK mode"));
|
|
554
|
+
console.log(chalk.dim("Add API keys with: archon keys add <provider>"));
|
|
555
|
+
} else {
|
|
556
|
+
spinner.fail(chalk.red(result.error ?? "Failed to update"));
|
|
557
|
+
}
|
|
558
|
+
} else if (choice === "2" && !creditsSelected) {
|
|
559
|
+
const spinner = ora("Switching to Credits...").start();
|
|
560
|
+
const result = await updateUserTier("CREDITS");
|
|
561
|
+
if (result.success) {
|
|
562
|
+
spinner.succeed(chalk.green("Switched to Credits mode"));
|
|
563
|
+
} else {
|
|
564
|
+
spinner.fail(chalk.red(result.error ?? "Failed to update"));
|
|
565
|
+
}
|
|
566
|
+
} else if (choice.toLowerCase() !== "b") {
|
|
567
|
+
if (choice === "1" && byokSelected || choice === "2" && creditsSelected) {
|
|
568
|
+
console.log(chalk.dim("Already using this billing mode."));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async function setDefaultModelsMenu(profile, providers) {
|
|
573
|
+
console.log(chalk.bold("\n\u2501\u2501\u2501 Default Models \u2501\u2501\u2501\n"));
|
|
574
|
+
const defaults = getDefaultAdversarialModels();
|
|
575
|
+
console.log(` ${chalk.cyan("1")}) Fast Model: ${formatModelDisplay(profile.pref_fast_model, getDefaultModel("fast"))}`);
|
|
576
|
+
console.log(` ${chalk.cyan("2")}) Thinking Model: ${formatModelDisplay(profile.pref_thinking_model, getDefaultModel("thinking"))}`);
|
|
577
|
+
console.log(` ${chalk.cyan("3")}) Primary: ${formatModelDisplay(profile.pref_primary_adversarial, defaults.primary)}`);
|
|
578
|
+
console.log(` ${chalk.cyan("4")}) Secondary: ${formatModelDisplay(profile.pref_secondary_adversarial, defaults.secondary)}`);
|
|
579
|
+
console.log();
|
|
580
|
+
console.log(` ${chalk.cyan("b")}) Back`);
|
|
581
|
+
console.log();
|
|
582
|
+
const choice = await prompt("Which model to change?");
|
|
583
|
+
const keyMap = {
|
|
584
|
+
"1": "fast-model",
|
|
585
|
+
"2": "thinking-model",
|
|
586
|
+
"3": "primary-adversarial",
|
|
587
|
+
"4": "secondary-adversarial"
|
|
588
|
+
};
|
|
589
|
+
if (choice.toLowerCase() === "b") {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const prefKey = keyMap[choice];
|
|
593
|
+
if (!prefKey) {
|
|
594
|
+
console.log(chalk.yellow("Invalid choice."));
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const models = getAllActiveModels();
|
|
598
|
+
const availableModels = models.filter(
|
|
599
|
+
(m) => providers.includes(m.provider) || profile.tier === "CREDITS"
|
|
600
|
+
);
|
|
601
|
+
if (availableModels.length === 0) {
|
|
602
|
+
console.log(chalk.yellow("\nNo models available. Add API keys or switch to Credits mode."));
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
console.log(chalk.bold("\nAvailable models:\n"));
|
|
606
|
+
availableModels.forEach((m, i) => {
|
|
607
|
+
const categoryLabel = m.category === "fast" ? chalk.cyan("[fast]") : chalk.magenta("[thinking]");
|
|
608
|
+
console.log(` ${chalk.cyan(String(i + 1))}) ${m.name} ${categoryLabel}`);
|
|
609
|
+
});
|
|
610
|
+
console.log();
|
|
611
|
+
const modelChoice = await prompt("Select model number");
|
|
612
|
+
const modelIndex = parseInt(modelChoice, 10) - 1;
|
|
613
|
+
if (modelIndex >= 0 && modelIndex < availableModels.length) {
|
|
614
|
+
const selectedModel = availableModels[modelIndex];
|
|
615
|
+
if (selectedModel) {
|
|
616
|
+
await setPreference(prefKey, selectedModel.modelId);
|
|
617
|
+
}
|
|
618
|
+
} else {
|
|
619
|
+
console.log(chalk.yellow("Invalid selection."));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
async function manageApiKeys() {
|
|
623
|
+
console.log(chalk.bold("\n\u2501\u2501\u2501 API Keys \u2501\u2501\u2501\n"));
|
|
624
|
+
const providers = await getUserProviders();
|
|
625
|
+
if (providers.length > 0) {
|
|
626
|
+
console.log(chalk.blue("Configured providers:"));
|
|
627
|
+
providers.forEach((p) => console.log(` ${chalk.green("\u2713")} ${p}`));
|
|
628
|
+
} else {
|
|
629
|
+
console.log(chalk.dim("No API keys configured."));
|
|
630
|
+
}
|
|
631
|
+
console.log();
|
|
632
|
+
console.log(` ${chalk.cyan("1")}) Add Anthropic key`);
|
|
633
|
+
console.log(` ${chalk.cyan("2")}) Add OpenAI key`);
|
|
634
|
+
console.log(` ${chalk.cyan("3")}) Add Google key`);
|
|
635
|
+
console.log(` ${chalk.cyan("4")}) Remove a key`);
|
|
636
|
+
console.log(` ${chalk.cyan("b")}) Back`);
|
|
637
|
+
console.log();
|
|
638
|
+
const choice = await prompt("Enter choice");
|
|
639
|
+
const providerMap = {
|
|
640
|
+
"1": "anthropic",
|
|
641
|
+
"2": "openai",
|
|
642
|
+
"3": "google"
|
|
643
|
+
};
|
|
644
|
+
if (choice.toLowerCase() === "b") {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (choice === "4") {
|
|
648
|
+
if (providers.length === 0) {
|
|
649
|
+
console.log(chalk.yellow("No keys to remove."));
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
console.log("\nWhich provider to remove?");
|
|
653
|
+
providers.forEach((p, i) => console.log(` ${chalk.cyan(String(i + 1))}) ${p}`));
|
|
654
|
+
const removeChoice = await prompt("Select");
|
|
655
|
+
const removeIndex = parseInt(removeChoice, 10) - 1;
|
|
656
|
+
if (removeIndex >= 0 && removeIndex < providers.length) {
|
|
657
|
+
const providerToRemove = providers[removeIndex];
|
|
658
|
+
if (providerToRemove) {
|
|
659
|
+
const { removeKey } = await import("./keys-EL3FUM5O.js");
|
|
660
|
+
await removeKey(providerToRemove);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
const provider = providerMap[choice];
|
|
666
|
+
if (provider) {
|
|
667
|
+
const { addKey } = await import("./keys-EL3FUM5O.js");
|
|
668
|
+
await addKey(provider);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
async function viewUsageDetails() {
|
|
672
|
+
console.log(chalk.bold("\n\u2501\u2501\u2501 Usage Details \u2501\u2501\u2501\n"));
|
|
673
|
+
const spinner = ora("Loading usage data...").start();
|
|
674
|
+
const config = await loadConfig();
|
|
675
|
+
const authToken = getAuthToken(config);
|
|
676
|
+
if (!authToken) {
|
|
677
|
+
spinner.fail("Not logged in");
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
const apiUrl = getApiUrl(config);
|
|
681
|
+
try {
|
|
682
|
+
const response = await fetch(`${apiUrl}/api/usage`, {
|
|
683
|
+
headers: { "Authorization": `Bearer ${authToken}` }
|
|
684
|
+
});
|
|
685
|
+
spinner.stop();
|
|
686
|
+
if (!response.ok) {
|
|
687
|
+
console.log(chalk.yellow("Unable to fetch usage data."));
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
const data = await response.json();
|
|
691
|
+
console.log(chalk.blue("Current Billing Period:"));
|
|
692
|
+
console.log(` ${data.periodStart} to ${data.periodEnd}`);
|
|
693
|
+
console.log();
|
|
694
|
+
console.log(chalk.blue("Token Usage:"));
|
|
695
|
+
console.log(` Input: ${formatTokens(data.totalInputTokens)} tokens`);
|
|
696
|
+
console.log(` Output: ${formatTokens(data.totalOutputTokens)} tokens`);
|
|
697
|
+
console.log(` Total: ${formatTokens(data.totalInputTokens + data.totalOutputTokens)} tokens`);
|
|
698
|
+
console.log();
|
|
699
|
+
console.log(chalk.blue("Cost:"));
|
|
700
|
+
console.log(` Base Cost: ${formatCost(data.totalBaseCost)}`);
|
|
701
|
+
if (data.tier === "CREDITS") {
|
|
702
|
+
const withFee = data.totalBaseCost * 1.1;
|
|
703
|
+
console.log(` With 10% Fee: ${formatCost(withFee)}`);
|
|
704
|
+
} else if (data.tier === "BYOK") {
|
|
705
|
+
console.log(chalk.dim(" (Paid directly to your provider)"));
|
|
706
|
+
}
|
|
707
|
+
if (data.byModel && Object.keys(data.byModel).length > 0) {
|
|
708
|
+
console.log();
|
|
709
|
+
console.log(chalk.blue("By Model:"));
|
|
710
|
+
for (const [model, stats] of Object.entries(data.byModel)) {
|
|
711
|
+
const modelInfo = findModel(model);
|
|
712
|
+
const name = modelInfo?.name ?? model;
|
|
713
|
+
console.log(` ${name}: ${formatTokens(stats.inputTokens + stats.outputTokens)} tokens (${formatCost(stats.cost)})`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
if (data.byOperation && Object.keys(data.byOperation).length > 0) {
|
|
717
|
+
console.log();
|
|
718
|
+
console.log(chalk.blue("By Operation:"));
|
|
719
|
+
for (const [op, stats] of Object.entries(data.byOperation)) {
|
|
720
|
+
console.log(` ${op}: ${formatTokens(stats.tokenCount)} tokens (${formatCost(stats.cost)})`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
} catch {
|
|
724
|
+
spinner.stop();
|
|
725
|
+
console.log(chalk.yellow("Error fetching usage data."));
|
|
726
|
+
}
|
|
727
|
+
console.log();
|
|
728
|
+
await prompt("Press Enter to continue");
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export {
|
|
732
|
+
showPreferences,
|
|
733
|
+
setPreference,
|
|
734
|
+
resetPreferences,
|
|
735
|
+
listModels,
|
|
736
|
+
interactiveSettings
|
|
737
|
+
};
|