zelari-code 0.1.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/LICENSE +21 -0
- package/README.md +156 -0
- package/bin/zelari-code.js +64 -0
- package/dist/agents/advancedTools.js +115 -0
- package/dist/agents/advancedTools.js.map +1 -0
- package/dist/agents/councilApi.js +375 -0
- package/dist/agents/councilApi.js.map +1 -0
- package/dist/agents/councilDirectives.js +100 -0
- package/dist/agents/councilDirectives.js.map +1 -0
- package/dist/agents/promoteMember.js +210 -0
- package/dist/agents/promoteMember.js.map +1 -0
- package/dist/agents/promptModules.js +139 -0
- package/dist/agents/promptModules.js.map +1 -0
- package/dist/agents/roles.js +281 -0
- package/dist/agents/roles.js.map +1 -0
- package/dist/agents/skills/builtin/debugging.js +248 -0
- package/dist/agents/skills/builtin/debugging.js.map +1 -0
- package/dist/agents/skills/builtin/docs.js +301 -0
- package/dist/agents/skills/builtin/docs.js.map +1 -0
- package/dist/agents/skills/builtin/git-ops.js +301 -0
- package/dist/agents/skills/builtin/git-ops.js.map +1 -0
- package/dist/agents/skills/builtin/planning.js +335 -0
- package/dist/agents/skills/builtin/planning.js.map +1 -0
- package/dist/agents/skills/builtin/refactoring.js +261 -0
- package/dist/agents/skills/builtin/refactoring.js.map +1 -0
- package/dist/agents/skills/builtin/review.js +302 -0
- package/dist/agents/skills/builtin/review.js.map +1 -0
- package/dist/agents/skills/builtin/testing.js +245 -0
- package/dist/agents/skills/builtin/testing.js.map +1 -0
- package/dist/agents/skills.js +272 -0
- package/dist/agents/skills.js.map +1 -0
- package/dist/agents/systemPromptBuilder.js +151 -0
- package/dist/agents/systemPromptBuilder.js.map +1 -0
- package/dist/agents/toolSchemas.js +249 -0
- package/dist/agents/toolSchemas.js.map +1 -0
- package/dist/agents/tools.js +196 -0
- package/dist/agents/tools.js.map +1 -0
- package/dist/agents/vaultTools.js +134 -0
- package/dist/agents/vaultTools.js.map +1 -0
- package/dist/cli/app.js +1823 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/cli/branchManager.js +280 -0
- package/dist/cli/branchManager.js.map +1 -0
- package/dist/cli/compaction.js +63 -0
- package/dist/cli/compaction.js.map +1 -0
- package/dist/cli/components/ChatStream.js +23 -0
- package/dist/cli/components/ChatStream.js.map +1 -0
- package/dist/cli/components/CollapsibleToolOutput.js +38 -0
- package/dist/cli/components/CollapsibleToolOutput.js.map +1 -0
- package/dist/cli/components/Header.js +31 -0
- package/dist/cli/components/Header.js.map +1 -0
- package/dist/cli/components/InputBar.js +9 -0
- package/dist/cli/components/InputBar.js.map +1 -0
- package/dist/cli/components/Sidebar.js +17 -0
- package/dist/cli/components/Sidebar.js.map +1 -0
- package/dist/cli/councilCost.js +81 -0
- package/dist/cli/councilCost.js.map +1 -0
- package/dist/cli/councilDispatcher.js +56 -0
- package/dist/cli/councilDispatcher.js.map +1 -0
- package/dist/cli/councilFeedback.js +164 -0
- package/dist/cli/councilFeedback.js.map +1 -0
- package/dist/cli/crossProviderFailover.js +83 -0
- package/dist/cli/crossProviderFailover.js.map +1 -0
- package/dist/cli/gitOps.js +96 -0
- package/dist/cli/gitOps.js.map +1 -0
- package/dist/cli/grokOAuth.js +375 -0
- package/dist/cli/grokOAuth.js.map +1 -0
- package/dist/cli/keyStore.js +263 -0
- package/dist/cli/keyStore.js.map +1 -0
- package/dist/cli/keyValidator.js +83 -0
- package/dist/cli/keyValidator.js.map +1 -0
- package/dist/cli/main.bundled.js +21428 -0
- package/dist/cli/main.bundled.js.map +7 -0
- package/dist/cli/main.js +72 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/metrics.js +131 -0
- package/dist/cli/metrics.js.map +1 -0
- package/dist/cli/modelDiscovery.js +234 -0
- package/dist/cli/modelDiscovery.js.map +1 -0
- package/dist/cli/modelPricing.js +112 -0
- package/dist/cli/modelPricing.js.map +1 -0
- package/dist/cli/oauthCallbackServer.js +207 -0
- package/dist/cli/oauthCallbackServer.js.map +1 -0
- package/dist/cli/provider/openai-compatible.js +236 -0
- package/dist/cli/provider/openai-compatible.js.map +1 -0
- package/dist/cli/providerConfig.js +210 -0
- package/dist/cli/providerConfig.js.map +1 -0
- package/dist/cli/providerFailover.js +90 -0
- package/dist/cli/providerFailover.js.map +1 -0
- package/dist/cli/refreshRegistry.js +77 -0
- package/dist/cli/refreshRegistry.js.map +1 -0
- package/dist/cli/safety/auditLogger.js +111 -0
- package/dist/cli/safety/auditLogger.js.map +1 -0
- package/dist/cli/safety/sandboxPath.js +62 -0
- package/dist/cli/safety/sandboxPath.js.map +1 -0
- package/dist/cli/safety/shellBlocklist.js +71 -0
- package/dist/cli/safety/shellBlocklist.js.map +1 -0
- package/dist/cli/sessionManager.js +149 -0
- package/dist/cli/sessionManager.js.map +1 -0
- package/dist/cli/skillCache.js +205 -0
- package/dist/cli/skillCache.js.map +1 -0
- package/dist/cli/skillHistory.js +178 -0
- package/dist/cli/skillHistory.js.map +1 -0
- package/dist/cli/skillSuggest.js +132 -0
- package/dist/cli/skillSuggest.js.map +1 -0
- package/dist/cli/slashCommands.js +431 -0
- package/dist/cli/slashCommands.js.map +1 -0
- package/dist/cli/toolRegistry.js +187 -0
- package/dist/cli/toolRegistry.js.map +1 -0
- package/dist/cli/updater.js +157 -0
- package/dist/cli/updater.js.map +1 -0
- package/dist/main/core/AgentHarness.js +345 -0
- package/dist/main/core/AgentHarness.js.map +1 -0
- package/dist/main/core/providerStream.js +99 -0
- package/dist/main/core/providerStream.js.map +1 -0
- package/dist/main/core/sessionJsonl.js +99 -0
- package/dist/main/core/sessionJsonl.js.map +1 -0
- package/dist/main/core/tools/auditLog.js +26 -0
- package/dist/main/core/tools/auditLog.js.map +1 -0
- package/dist/main/core/tools/builtin/filesystem.js +110 -0
- package/dist/main/core/tools/builtin/filesystem.js.map +1 -0
- package/dist/main/core/tools/builtin/search.js +56 -0
- package/dist/main/core/tools/builtin/search.js.map +1 -0
- package/dist/main/core/tools/builtin/shell.js +51 -0
- package/dist/main/core/tools/builtin/shell.js.map +1 -0
- package/dist/main/core/tools/registry.js +71 -0
- package/dist/main/core/tools/registry.js.map +1 -0
- package/dist/main/core/tools/toolTypes.js +8 -0
- package/dist/main/core/tools/toolTypes.js.map +1 -0
- package/dist/main/core/tools/zodBridge.js +48 -0
- package/dist/main/core/tools/zodBridge.js.map +1 -0
- package/dist/shared/eventBus.js +104 -0
- package/dist/shared/eventBus.js.map +1 -0
- package/dist/shared/events.js +73 -0
- package/dist/shared/events.js.map +1 -0
- package/dist/shared/index.js +8 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/types/context.js +3 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/knowledge.js +2 -0
- package/dist/types/knowledge.js.map +1 -0
- package/dist/types/systemTypes.js +10 -0
- package/dist/types/systemTypes.js.map +1 -0
- package/package.json +63 -0
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* zelari-code — CLI coding agent on top of AnathemaBrain.
|
|
4
|
+
* Phase 14 Task 14.3 + 14.4: multi-panel TUI + slash command wiring.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { render } from 'ink';
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
import { App } from './app.js';
|
|
10
|
+
import { getMetricsLogger } from './metrics.js';
|
|
11
|
+
export const VERSION = '0.1.0';
|
|
12
|
+
/**
|
|
13
|
+
* Silent background update check (Task N.6, v3-N).
|
|
14
|
+
*
|
|
15
|
+
* Runs ~3s after startup. If a newer version exists on npm, prints a
|
|
16
|
+
* one-line hint to stderr (so it doesn't pollute the TUI). Failures
|
|
17
|
+
* are swallowed silently — registry outages must NEVER block the CLI.
|
|
18
|
+
*
|
|
19
|
+
* Disabled in dev mode (`ANATHEMA_DEV=1`) to avoid noise during local
|
|
20
|
+
* development where the bundled version is the source repo.
|
|
21
|
+
*/
|
|
22
|
+
async function backgroundUpdateCheck() {
|
|
23
|
+
if (process.env.ANATHEMA_DEV === '1')
|
|
24
|
+
return;
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
26
|
+
try {
|
|
27
|
+
const { checkForUpdate } = await import('./updater.js');
|
|
28
|
+
const info = await checkForUpdate();
|
|
29
|
+
if (info.updateAvailable && !info.error) {
|
|
30
|
+
// eslint-disable-next-line no-console
|
|
31
|
+
console.error(`[zelari-coder] 🆕 v${info.latestVersion} available (current: v${info.currentVersion}). ` +
|
|
32
|
+
`Run \`zelari-coder\` then \`/update --yes\` to upgrade.`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Swallow — network failures, malformed responses, etc.
|
|
37
|
+
// The CLI is fully usable without update awareness.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function shutdown() {
|
|
41
|
+
// Flush the process-wide MetricsLogger (Task G.3.3, carryover from v3-B
|
|
42
|
+
// B.5.2). The chat session in `app.tsx` writes via fire-and-forget
|
|
43
|
+
// queue — if we just `process.exit(0)` on SIGINT, the last few records
|
|
44
|
+
// (often the most interesting: agent_end + tool_execution_end) never
|
|
45
|
+
// land in `~/.tmp/anathema-coder/metrics.jsonl`. Awaiting `flush()`
|
|
46
|
+
// before exit guarantees the file is fully written.
|
|
47
|
+
try {
|
|
48
|
+
await getMetricsLogger().flush();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Best-effort — never block shutdown on a metrics write error.
|
|
52
|
+
}
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
function main() {
|
|
56
|
+
const { waitUntilExit, unmount } = render(React.createElement(App));
|
|
57
|
+
process.on('SIGINT', () => {
|
|
58
|
+
unmount();
|
|
59
|
+
void shutdown();
|
|
60
|
+
});
|
|
61
|
+
process.on('SIGTERM', () => {
|
|
62
|
+
unmount();
|
|
63
|
+
void shutdown();
|
|
64
|
+
});
|
|
65
|
+
// Fire-and-forget — the CLI works regardless of the update check result.
|
|
66
|
+
void backgroundUpdateCheck();
|
|
67
|
+
waitUntilExit().then(() => {
|
|
68
|
+
void shutdown();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
main();
|
|
72
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B;;;;;;;;;GASG;AACH,KAAK,UAAU,qBAAqB;IAClC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG;QAAE,OAAO;IAC7C,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAC1D,IAAI,CAAC;QACH,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,MAAM,cAAc,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACxC,sCAAsC;YACtC,OAAO,CAAC,KAAK,CACX,sBAAsB,IAAI,CAAC,aAAa,yBAAyB,IAAI,CAAC,cAAc,KAAK;gBACvF,yDAAyD,CAC5D,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,wDAAwD;QACxD,oDAAoD;IACtD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,QAAQ;IACrB,wEAAwE;IACxE,mEAAmE;IACnE,uEAAuE;IACvE,qEAAqE;IACrE,oEAAoE;IACpE,oDAAoD;IACpD,IAAI,CAAC;QACH,MAAM,gBAAgB,EAAE,CAAC,KAAK,EAAE,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,+DAA+D;IACjE,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,IAAI;IACX,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAEpE,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxB,OAAO,EAAE,CAAC;QACV,KAAK,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACzB,OAAO,EAAE,CAAC;QACV,KAAK,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,yEAAyE;IACzE,KAAK,qBAAqB,EAAE,CAAC;IAE7B,aAAa,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QACxB,KAAK,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* metrics — JSONL metrics logger (Task B.5).
|
|
3
|
+
*
|
|
4
|
+
* Append-only NDJSON writer for runtime telemetry. One event per line,
|
|
5
|
+
* flushed via fire-and-forget queue (matches auditLogger pattern from
|
|
6
|
+
* v3-A).
|
|
7
|
+
*
|
|
8
|
+
* Schema (per record):
|
|
9
|
+
* {
|
|
10
|
+
* ts: number, // epoch ms
|
|
11
|
+
* kind: 'run' | 'message' | 'error',
|
|
12
|
+
* sessionId?: string,
|
|
13
|
+
* provider?: string,
|
|
14
|
+
* model?: string,
|
|
15
|
+
* latencyMs?: number,
|
|
16
|
+
* tokens?: number,
|
|
17
|
+
* costUsd?: number,
|
|
18
|
+
* ok?: boolean,
|
|
19
|
+
* error?: string,
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* Storage: `~/.tmp/anathema-coder/metrics.jsonl` (override via
|
|
23
|
+
* `ANATHEMA_METRICS_FILE` env var, useful for tests).
|
|
24
|
+
*
|
|
25
|
+
* Rotation: when the file exceeds 10MB, it's renamed to
|
|
26
|
+
* `metrics.1.jsonl` (existing `.1.jsonl` is overwritten). Only one
|
|
27
|
+
* rotation step — older data is dropped. This matches typical
|
|
28
|
+
* logrotate setups.
|
|
29
|
+
*/
|
|
30
|
+
import { promises as fs, existsSync, statSync, renameSync, appendFileSync, mkdirSync } from 'node:fs';
|
|
31
|
+
import path from 'node:path';
|
|
32
|
+
import os from 'node:os';
|
|
33
|
+
export const METRICS_ROTATE_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
34
|
+
export class MetricsLogger {
|
|
35
|
+
file;
|
|
36
|
+
writeQueue = Promise.resolve();
|
|
37
|
+
constructor(file) {
|
|
38
|
+
this.file = file ?? process.env.ANATHEMA_METRICS_FILE
|
|
39
|
+
?? path.join(os.homedir(), '.tmp', 'zelari-code', 'metrics.jsonl');
|
|
40
|
+
mkdirSync(path.dirname(this.file), { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
/** Fire-and-forget record append. */
|
|
43
|
+
record(rec) {
|
|
44
|
+
const full = { ts: rec.ts ?? Date.now(), ...rec };
|
|
45
|
+
this.writeQueue = this.writeQueue.then(() => this.append(full));
|
|
46
|
+
void this.writeQueue.catch(() => { });
|
|
47
|
+
}
|
|
48
|
+
/** Append a single record synchronously (file I/O). */
|
|
49
|
+
append(rec) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
try {
|
|
52
|
+
this.maybeRotate();
|
|
53
|
+
const line = JSON.stringify(rec) + '\n';
|
|
54
|
+
appendFileSync(this.file, line, { encoding: 'utf-8' });
|
|
55
|
+
resolve();
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
reject(err);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Force the write queue to flush — used by tests + on graceful shutdown.
|
|
64
|
+
* Returns the underlying promise so callers can await it.
|
|
65
|
+
*/
|
|
66
|
+
async flush() {
|
|
67
|
+
await this.writeQueue;
|
|
68
|
+
}
|
|
69
|
+
/** If the file is over the rotation threshold, rotate it. */
|
|
70
|
+
maybeRotate() {
|
|
71
|
+
if (!existsSync(this.file))
|
|
72
|
+
return;
|
|
73
|
+
try {
|
|
74
|
+
const stat = statSync(this.file);
|
|
75
|
+
if (stat.size >= METRICS_ROTATE_BYTES) {
|
|
76
|
+
const rotated = this.file.replace(/\.jsonl$/, '.1.jsonl');
|
|
77
|
+
renameSync(this.file, rotated);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Best-effort.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Convenience helper for tests: read all records from a metrics file. */
|
|
86
|
+
export async function readMetrics(file) {
|
|
87
|
+
let raw = '';
|
|
88
|
+
try {
|
|
89
|
+
raw = await fs.readFile(file, 'utf-8');
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
const out = [];
|
|
95
|
+
for (const line of raw.split('\n')) {
|
|
96
|
+
if (!line.trim())
|
|
97
|
+
continue;
|
|
98
|
+
try {
|
|
99
|
+
out.push(JSON.parse(line));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Skip malformed lines.
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Process-wide singleton MetricsLogger (Task G.3.3). Multiple consumers
|
|
109
|
+
* (the chat session in `app.tsx`, the shutdown handler in `main.ts`)
|
|
110
|
+
* need to coordinate on a single writer so SIGINT/SIGTERM can flush
|
|
111
|
+
* the pending fire-and-forget queue before `process.exit(0)` discards
|
|
112
|
+
* anything still in memory.
|
|
113
|
+
*
|
|
114
|
+
* Lazy-instantiated on first access. Respects the same env override as
|
|
115
|
+
* the constructor (`ANATHEMA_METRICS_FILE`), so tests can point it at
|
|
116
|
+
* a temp file before any consumer imports it.
|
|
117
|
+
*
|
|
118
|
+
* @see docs/plans/2026-06-29-anathema-coder-v3-G.md (Task G.3.3)
|
|
119
|
+
*/
|
|
120
|
+
let _singleton = null;
|
|
121
|
+
export function getMetricsLogger() {
|
|
122
|
+
if (!_singleton) {
|
|
123
|
+
_singleton = new MetricsLogger();
|
|
124
|
+
}
|
|
125
|
+
return _singleton;
|
|
126
|
+
}
|
|
127
|
+
/** Reset the singleton (test-only). Forces a fresh instance on next getMetricsLogger(). */
|
|
128
|
+
export function resetMetricsLogger() {
|
|
129
|
+
_singleton = null;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/cli/metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACtG,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ;AAqB9D,MAAM,OAAO,aAAa;IACP,IAAI,CAAS;IACtB,UAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEtD,YAAY,IAAa;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB;eAChD,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,eAAe,CAAC,CAAC;QACrE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,qCAAqC;IACrC,MAAM,CAAC,GAAgD;QACrD,MAAM,IAAI,GAAkB,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC;QACjE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAChE,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,GAAiC,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,uDAAuD;IAC/C,MAAM,CAAC,GAAkB;QAC/B,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC3C,IAAI,CAAC;gBACH,IAAI,CAAC,WAAW,EAAE,CAAC;gBACnB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;gBACxC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;gBACvD,OAAO,EAAE,CAAC;YACZ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,UAAU,CAAC;IACxB,CAAC;IAED,6DAA6D;IACrD,WAAW;QACjB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO;QACnC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,IAAI,CAAC,IAAI,IAAI,oBAAoB,EAAE,CAAC;gBACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBAC1D,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;IACH,CAAC;CACF;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAC3B,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,IAAI,UAAU,GAAyB,IAAI,CAAC;AAC5C,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,UAAU,GAAG,IAAI,aAAa,EAAE,CAAC;IACnC,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,kBAAkB;IAChC,UAAU,GAAG,IAAI,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modelDiscovery — auto-discover available models for each provider.
|
|
3
|
+
*
|
|
4
|
+
* Triggers:
|
|
5
|
+
* - After `/login <provider> <key>` succeeds (keyStore write)
|
|
6
|
+
* - After `/login grok` (OAuth) succeeds
|
|
7
|
+
* - On user request via `/model refresh` or `/models refresh`
|
|
8
|
+
* - On startup if cache is older than `staleAfterMs` (default 6h)
|
|
9
|
+
*
|
|
10
|
+
* Why dynamic discovery? Providers (xAI, Z.AI, MiniMax, OpenAI) add/remove
|
|
11
|
+
* models frequently — hardcoding a list in `providerDefaults` makes the user
|
|
12
|
+
* see stale choices. After auth we hit the provider's `/v1/models` endpoint
|
|
13
|
+
* and cache the IDs in `~/.tmp/anathema-coder/models.json`.
|
|
14
|
+
*
|
|
15
|
+
* Pure node:fs + fetch — no Electron deps, browser-importable for jsdom tests.
|
|
16
|
+
* Env override: `ANATHEMA_MODELS_FILE` (useful for tests + CI).
|
|
17
|
+
*
|
|
18
|
+
* @see docs/plans/2026-06-30-anathema-coder-v3-U.md
|
|
19
|
+
*/
|
|
20
|
+
import { promises as fs, existsSync, readFileSync } from 'node:fs';
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Provider endpoint map
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/** Default base URL per provider for `/v1/models` discovery. */
|
|
27
|
+
const PROVIDER_BASE_URLS = {
|
|
28
|
+
'grok': 'https://api.x.ai/v1',
|
|
29
|
+
'glm': 'https://api.z.ai/api/paas/v4',
|
|
30
|
+
'minimax': 'https://api.minimaxi.chat/v1',
|
|
31
|
+
'openai-compatible': 'https://api.openai.com/v1',
|
|
32
|
+
};
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Cache file location
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
function defaultModelsFilePath() {
|
|
37
|
+
return process.env.ANATHEMA_MODELS_FILE
|
|
38
|
+
?? path.join(homedir(), '.tmp', 'anathema-coder', 'models.json');
|
|
39
|
+
}
|
|
40
|
+
export function getModelsFilePath() {
|
|
41
|
+
return defaultModelsFilePath();
|
|
42
|
+
}
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Sync read (used at app start)
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
/** Read the on-disk models registry. Returns empty object if file missing/corrupt. */
|
|
47
|
+
export function loadModelsRegistry(file = getModelsFilePath()) {
|
|
48
|
+
if (!existsSync(file))
|
|
49
|
+
return {};
|
|
50
|
+
try {
|
|
51
|
+
const raw = readFileSync(file, 'utf-8');
|
|
52
|
+
const parsed = JSON.parse(raw);
|
|
53
|
+
if (!parsed || typeof parsed !== 'object')
|
|
54
|
+
return {};
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Read cached models for a single provider. Returns undefined if not cached. */
|
|
62
|
+
export function getCachedModels(provider, file = getModelsFilePath()) {
|
|
63
|
+
const registry = loadModelsRegistry(file);
|
|
64
|
+
return registry[provider];
|
|
65
|
+
}
|
|
66
|
+
/** Returns true if the cache exists and is older than `maxAgeMs`. */
|
|
67
|
+
export function isModelsCacheStale(provider, maxAgeMs = 6 * 60 * 60 * 1000, // 6 hours
|
|
68
|
+
file = getModelsFilePath(), now = Date.now()) {
|
|
69
|
+
const entry = getCachedModels(provider, file);
|
|
70
|
+
if (!entry)
|
|
71
|
+
return true;
|
|
72
|
+
return (now - entry.fetchedAt) > maxAgeMs;
|
|
73
|
+
}
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Async write (atomic: write to .tmp, rename) — serialized via in-memory mutex
|
|
76
|
+
// to avoid concurrent discoveries clobbering each other (race on rename).
|
|
77
|
+
// Also wraps the read-modify-write cycle so two parallel calls don't both
|
|
78
|
+
// observe an empty registry and then clobber each other.
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
let writeChain = Promise.resolve();
|
|
81
|
+
async function withRegistryLock(fn) {
|
|
82
|
+
const next = writeChain.then(async () => fn());
|
|
83
|
+
writeChain = next.catch(() => undefined);
|
|
84
|
+
return next;
|
|
85
|
+
}
|
|
86
|
+
async function readModifyWriteRegistry(mutator, file = getModelsFilePath()) {
|
|
87
|
+
return withRegistryLock(async () => {
|
|
88
|
+
// Read latest on-disk (other concurrent calls may have written since
|
|
89
|
+
// we last read).
|
|
90
|
+
const onDisk = loadModelsRegistry(file);
|
|
91
|
+
const merged = mutator(onDisk);
|
|
92
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
93
|
+
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
94
|
+
await fs.writeFile(tmp, JSON.stringify(merged, null, 2), 'utf-8');
|
|
95
|
+
await fs.rename(tmp, file);
|
|
96
|
+
return merged;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Auth resolution
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
/** Synchronous auth resolution — reads keyStore via dynamic import to keep this module browser-friendly. */
|
|
103
|
+
async function resolveAuthToken(provider, options) {
|
|
104
|
+
if (options.authToken)
|
|
105
|
+
return options.authToken;
|
|
106
|
+
// Late import: keyStore uses node:fs/promises, no Electron deps.
|
|
107
|
+
const { resolveApiKeyWithMeta, getOAuthToken } = await import('./keyStore.js');
|
|
108
|
+
if (provider === 'grok') {
|
|
109
|
+
const oauth = getOAuthToken('grok');
|
|
110
|
+
if (oauth?.apiKey)
|
|
111
|
+
return oauth.apiKey;
|
|
112
|
+
}
|
|
113
|
+
const resolved = await resolveApiKeyWithMeta(provider);
|
|
114
|
+
return resolved?.apiKey;
|
|
115
|
+
}
|
|
116
|
+
function parseOpenAIModelsResponse(json, baseUrl) {
|
|
117
|
+
if (!json || typeof json !== 'object')
|
|
118
|
+
return [];
|
|
119
|
+
const obj = json;
|
|
120
|
+
if (!Array.isArray(obj.data))
|
|
121
|
+
return [];
|
|
122
|
+
const models = [];
|
|
123
|
+
for (const m of obj.data) {
|
|
124
|
+
if (!m || typeof m.id !== 'string')
|
|
125
|
+
continue;
|
|
126
|
+
const out = { id: m.id };
|
|
127
|
+
if (typeof m.created === 'number')
|
|
128
|
+
out.created = m.created;
|
|
129
|
+
if (typeof m.owned_by === 'string')
|
|
130
|
+
out.ownedBy = m.owned_by;
|
|
131
|
+
// Optional context length (some providers include it as extra field)
|
|
132
|
+
const ctx = m.context_length;
|
|
133
|
+
if (typeof ctx === 'number')
|
|
134
|
+
out.contextLength = ctx;
|
|
135
|
+
models.push(out);
|
|
136
|
+
}
|
|
137
|
+
models.sort((a, b) => a.id.localeCompare(b.id));
|
|
138
|
+
return models;
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Main: discoverModelsForProvider
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
/**
|
|
144
|
+
* Fetch available models for a provider and persist the result to disk.
|
|
145
|
+
* Throws on network errors but never corrupts the on-disk cache.
|
|
146
|
+
*/
|
|
147
|
+
export async function discoverModelsForProvider(provider, options = {}) {
|
|
148
|
+
const baseUrl = options.baseUrl ?? PROVIDER_BASE_URLS[provider];
|
|
149
|
+
const url = `${baseUrl.replace(/\/$/, '')}/models`;
|
|
150
|
+
const authToken = await resolveAuthToken(provider, options);
|
|
151
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
152
|
+
let response;
|
|
153
|
+
try {
|
|
154
|
+
const headers = { Accept: 'application/json' };
|
|
155
|
+
if (authToken)
|
|
156
|
+
headers.Authorization = `Bearer ${authToken}`;
|
|
157
|
+
response = await fetchImpl(url, { method: 'GET', headers });
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
throw new ModelDiscoveryError(`Network error contacting ${url}: ${err instanceof Error ? err.message : String(err)}`, 'network_error');
|
|
161
|
+
}
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
const body = await response.text().catch(() => '');
|
|
164
|
+
throw new ModelDiscoveryError(`HTTP ${response.status} from ${url}: ${body.slice(0, 200)}`, `http_${response.status}`);
|
|
165
|
+
}
|
|
166
|
+
let json;
|
|
167
|
+
try {
|
|
168
|
+
json = await response.json();
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
throw new ModelDiscoveryError(`Invalid JSON from ${url}: ${err instanceof Error ? err.message : String(err)}`, 'invalid_json');
|
|
172
|
+
}
|
|
173
|
+
const models = parseOpenAIModelsResponse(json, baseUrl);
|
|
174
|
+
if (models.length === 0) {
|
|
175
|
+
throw new ModelDiscoveryError(`Provider ${provider} returned 0 models — refusing to overwrite cache`, 'empty_response');
|
|
176
|
+
}
|
|
177
|
+
const entry = {
|
|
178
|
+
models,
|
|
179
|
+
fetchedAt: Date.now(),
|
|
180
|
+
baseUrl,
|
|
181
|
+
};
|
|
182
|
+
if (!options.skipCacheWrite) {
|
|
183
|
+
const file = getModelsFilePath();
|
|
184
|
+
await readModifyWriteRegistry((current) => {
|
|
185
|
+
current[provider] = entry;
|
|
186
|
+
return current;
|
|
187
|
+
}, file);
|
|
188
|
+
}
|
|
189
|
+
return entry;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Fire-and-forget variant: kicks off discovery in the background, logs errors
|
|
193
|
+
* to the `onError` callback instead of throwing. Used by `/login` handlers
|
|
194
|
+
* that must not block the user response on a slow `/v1/models` call.
|
|
195
|
+
*/
|
|
196
|
+
export function discoverModelsInBackground(provider, options = {}) {
|
|
197
|
+
discoverModelsForProvider(provider, options).catch((err) => {
|
|
198
|
+
if (err instanceof ModelDiscoveryError && options.onError) {
|
|
199
|
+
options.onError(err);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
// Error
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
export class ModelDiscoveryError extends Error {
|
|
207
|
+
code;
|
|
208
|
+
constructor(message, code) {
|
|
209
|
+
super(message);
|
|
210
|
+
this.code = code;
|
|
211
|
+
this.name = 'ModelDiscoveryError';
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Suggestion helper: filter /model tab-completion against discovered models
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
/** Returns the discovered model ids for a provider, or undefined if no cache. */
|
|
218
|
+
export function getDiscoveredModelIds(provider, file = getModelsFilePath()) {
|
|
219
|
+
const entry = getCachedModels(provider, file);
|
|
220
|
+
return entry?.models.map((m) => m.id);
|
|
221
|
+
}
|
|
222
|
+
/** Returns the "best" default model id: discovered (most recent) → providerDefaults fallback. */
|
|
223
|
+
export function pickDefaultModel(provider, providerDefaults, file = getModelsFilePath()) {
|
|
224
|
+
const cached = getCachedModels(provider, file);
|
|
225
|
+
if (cached && cached.models.length > 0) {
|
|
226
|
+
// Prefer the first model alphabetically that contains "latest" or matches the default name
|
|
227
|
+
const preferred = cached.models.find((m) => /latest|default|chat/i.test(m.id));
|
|
228
|
+
if (preferred)
|
|
229
|
+
return preferred.id;
|
|
230
|
+
return cached.models[0]?.id;
|
|
231
|
+
}
|
|
232
|
+
return providerDefaults[provider];
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=modelDiscovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelDiscovery.js","sourceRoot":"","sources":["../../src/cli/modelDiscovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modelPricing — USD cost calculator for LLM tokens.
|
|
3
|
+
*
|
|
4
|
+
* Pricing table covers the providers wired in `keyStore.PROVIDERS`. Costs
|
|
5
|
+
* are in **USD per 1M tokens** (industry standard). Numbers are conservative
|
|
6
|
+
* defaults — the user can override per-model via env vars (see below).
|
|
7
|
+
*
|
|
8
|
+
* Env overrides (1M-token rate):
|
|
9
|
+
* - ANATHEMA_PRICE_GROK4
|
|
10
|
+
* - ANATHEMA_PRICE_GROK4_FAST
|
|
11
|
+
* - ANATHEMA_PRICE_GLM46
|
|
12
|
+
* - ANATHEMA_PRICE_MINIMAX_M25
|
|
13
|
+
* - ANATHEMA_PRICE_DEFAULT (fallback for unknown models)
|
|
14
|
+
*
|
|
15
|
+
* Costs returned are precise to 6 decimals. Round to cents at the UI layer.
|
|
16
|
+
*/
|
|
17
|
+
/** Pricing table (USD per 1M tokens) — conservative public list prices. */
|
|
18
|
+
const PRICES_PER_MILLION = {
|
|
19
|
+
// xAI Grok
|
|
20
|
+
'grok-4': { input: 3, output: 15 },
|
|
21
|
+
'grok-4-fast': { input: 0.20, output: 0.50 },
|
|
22
|
+
'grok-3': { input: 3, output: 15 },
|
|
23
|
+
'grok-3-mini': { input: 0.30, output: 0.50 },
|
|
24
|
+
'grok-2-vision': { input: 2, output: 10 },
|
|
25
|
+
// GLM / Z.AI
|
|
26
|
+
'glm-4.6': { input: 0.60, output: 2.20 },
|
|
27
|
+
'glm-4.5': { input: 0.50, output: 2.00 },
|
|
28
|
+
'glm-4.5-air': { input: 0.10, output: 0.60 },
|
|
29
|
+
'glm-z1': { input: 0.50, output: 2.00 },
|
|
30
|
+
// MiniMax
|
|
31
|
+
'MiniMax-M2.5': { input: 0.20, output: 1.10 },
|
|
32
|
+
'MiniMax-M2': { input: 0.20, output: 1.10 },
|
|
33
|
+
'MiniMax-M2-her': { input: 0.30, output: 1.20 },
|
|
34
|
+
// OpenAI (for openai-compatible fallback)
|
|
35
|
+
'gpt-4o': { input: 2.50, output: 10 },
|
|
36
|
+
'gpt-4o-mini': { input: 0.15, output: 0.60 },
|
|
37
|
+
'gpt-4-turbo': { input: 10, output: 30 },
|
|
38
|
+
'o1-preview': { input: 15, output: 60 },
|
|
39
|
+
'o1-mini': { input: 3, output: 12 },
|
|
40
|
+
};
|
|
41
|
+
/** Default rate for unknown models — assume a mid-tier provider. */
|
|
42
|
+
const DEFAULT_RATE = { input: 1.0, output: 3.0 };
|
|
43
|
+
/** Read an env-var override for a given model, if present. */
|
|
44
|
+
function envOverride(model) {
|
|
45
|
+
const key = `ANATHEMA_PRICE_${model.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;
|
|
46
|
+
const raw = process.env[key] ?? process.env.ANATHEMA_PRICE_DEFAULT;
|
|
47
|
+
return parseRateOverride(raw);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Parse a price-override string in the format `INPUT[/OUTPUT]`.
|
|
51
|
+
* Exported for testing (env-var-based tests are subject to platform
|
|
52
|
+
* content filters that rewrite numeric literals in test files).
|
|
53
|
+
*
|
|
54
|
+
* @returns null if `raw` is undefined/empty or if either number is invalid.
|
|
55
|
+
*/
|
|
56
|
+
export function parseRateOverride(raw) {
|
|
57
|
+
if (!raw)
|
|
58
|
+
return null;
|
|
59
|
+
const [inStr, outStr] = raw.split('/');
|
|
60
|
+
const input = Number.parseFloat(inStr);
|
|
61
|
+
const output = outStr !== undefined ? Number.parseFloat(outStr) : input;
|
|
62
|
+
if (!Number.isFinite(input) || !Number.isFinite(output))
|
|
63
|
+
return null;
|
|
64
|
+
return { input, output };
|
|
65
|
+
}
|
|
66
|
+
/** Look up pricing for a model. Falls back to DEFAULT_RATE. */
|
|
67
|
+
export function getModelRate(model) {
|
|
68
|
+
if (!model)
|
|
69
|
+
return DEFAULT_RATE;
|
|
70
|
+
return envOverride(model)
|
|
71
|
+
?? PRICES_PER_MILLION[model]
|
|
72
|
+
?? DEFAULT_RATE;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Compute the USD cost for a given number of prompt + completion tokens.
|
|
76
|
+
* Rates are per 1M tokens, so we divide the token count by 1_000_000.
|
|
77
|
+
*
|
|
78
|
+
* @returns cost in USD (0 if either token count is negative or NaN).
|
|
79
|
+
*/
|
|
80
|
+
export function calculateCost(model, promptTokens, completionTokens) {
|
|
81
|
+
if (!Number.isFinite(promptTokens) || promptTokens < 0)
|
|
82
|
+
return 0;
|
|
83
|
+
if (!Number.isFinite(completionTokens) || completionTokens < 0)
|
|
84
|
+
return 0;
|
|
85
|
+
const rate = getModelRate(model);
|
|
86
|
+
const inputCost = (promptTokens / 1_000_000) * rate.input;
|
|
87
|
+
const outputCost = (completionTokens / 1_000_000) * rate.output;
|
|
88
|
+
return Number((inputCost + outputCost).toFixed(6));
|
|
89
|
+
}
|
|
90
|
+
/** Format a USD cost as `$0.0234` style (4 decimals). */
|
|
91
|
+
export function formatCost(usd) {
|
|
92
|
+
if (!Number.isFinite(usd) || usd < 0)
|
|
93
|
+
return '$0.0000';
|
|
94
|
+
if (usd === 0)
|
|
95
|
+
return '$0.0000';
|
|
96
|
+
if (usd < 0.0001)
|
|
97
|
+
return '<$0.0001';
|
|
98
|
+
return `$${usd.toFixed(4)}`;
|
|
99
|
+
}
|
|
100
|
+
/** Format a token count compactly: `1.2k`, `3.4M`, etc. */
|
|
101
|
+
export function formatTokens(tokens) {
|
|
102
|
+
if (!Number.isFinite(tokens) || tokens < 0)
|
|
103
|
+
return '0';
|
|
104
|
+
if (tokens < 1_000)
|
|
105
|
+
return `${tokens}`;
|
|
106
|
+
if (tokens < 1_000_000)
|
|
107
|
+
return `${(tokens / 1_000).toFixed(1)}k`;
|
|
108
|
+
if (tokens < 1_000_000_000)
|
|
109
|
+
return `${(tokens / 1_000_000).toFixed(2)}M`;
|
|
110
|
+
return `${(tokens / 1_000_000_000).toFixed(2)}B`;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=modelPricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelPricing.js","sourceRoot":"","sources":["../../src/cli/modelPricing.ts"],"names":[],"mappings":"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"}
|