@dmrlzz/davecode 0.0.0-stage → 0.3.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 +33 -2
- package/bin/davecode.js +2 -0
- package/dist/ChatApp-FO5SABXB.js +893 -0
- package/dist/ChatApp-FO5SABXB.js.map +1 -0
- package/dist/RunnerView-DLVR7C22.js +18 -0
- package/dist/RunnerView-DLVR7C22.js.map +1 -0
- package/dist/accounts-MTMGYKEP.js +724 -0
- package/dist/accounts-MTMGYKEP.js.map +1 -0
- package/dist/budget-RBHCEDPP.js +115 -0
- package/dist/budget-RBHCEDPP.js.map +1 -0
- package/dist/chat-ZDXX3N6X.js +95 -0
- package/dist/chat-ZDXX3N6X.js.map +1 -0
- package/dist/chunk-23KTE7IK.js +114 -0
- package/dist/chunk-23KTE7IK.js.map +1 -0
- package/dist/chunk-7XVXEO7U.js +23 -0
- package/dist/chunk-7XVXEO7U.js.map +1 -0
- package/dist/chunk-BOLFK3BH.js +20 -0
- package/dist/chunk-BOLFK3BH.js.map +1 -0
- package/dist/chunk-C7FDRA3U.js +9087 -0
- package/dist/chunk-C7FDRA3U.js.map +1 -0
- package/dist/chunk-DYQ56FBT.js +47 -0
- package/dist/chunk-DYQ56FBT.js.map +1 -0
- package/dist/chunk-FRFSJMN4.js +79 -0
- package/dist/chunk-FRFSJMN4.js.map +1 -0
- package/dist/chunk-IC3TDONV.js +12 -0
- package/dist/chunk-IC3TDONV.js.map +1 -0
- package/dist/chunk-IVZHKUCD.js +180 -0
- package/dist/chunk-IVZHKUCD.js.map +1 -0
- package/dist/chunk-MSLLH5XP.js +213 -0
- package/dist/chunk-MSLLH5XP.js.map +1 -0
- package/dist/chunk-OXRMGR72.js +20 -0
- package/dist/chunk-OXRMGR72.js.map +1 -0
- package/dist/chunk-OYEYNDOO.js +200 -0
- package/dist/chunk-OYEYNDOO.js.map +1 -0
- package/dist/chunk-PIIJ3RSI.js +1610 -0
- package/dist/chunk-PIIJ3RSI.js.map +1 -0
- package/dist/chunk-SF5ZEAP4.js +105 -0
- package/dist/chunk-SF5ZEAP4.js.map +1 -0
- package/dist/chunk-VVCXGQMY.js +107 -0
- package/dist/chunk-VVCXGQMY.js.map +1 -0
- package/dist/config-FRMY5OHG.js +158 -0
- package/dist/config-FRMY5OHG.js.map +1 -0
- package/dist/dashboard/assets/Accounts-BR3ZfVk4.js +1 -0
- package/dist/dashboard/assets/Badge-B-qzNRrZ.js +55 -0
- package/dist/dashboard/assets/Callout-B9g_wPDi.js +1 -0
- package/dist/dashboard/assets/Field-BfabPEcY.js +1 -0
- package/dist/dashboard/assets/Goals-BsV-6m94.js +1 -0
- package/dist/dashboard/assets/Library-CEA9P1m-.js +1 -0
- package/dist/dashboard/assets/Markdown-ClRsddnB.js +69 -0
- package/dist/dashboard/assets/Panel-B9tSgX6l.js +1 -0
- package/dist/dashboard/assets/RoutesPage-Dh-BJ3ir.js +4 -0
- package/dist/dashboard/assets/Runner-C_e9l5l2.js +1 -0
- package/dist/dashboard/assets/Settings-C6Yc1Gjg.js +1 -0
- package/dist/dashboard/assets/Switch-CUpOJjUT.js +1 -0
- package/dist/dashboard/assets/Tasks-CTku0rR3.js +2 -0
- package/dist/dashboard/assets/Traffic-CE6wZSMr.js +1 -0
- package/dist/dashboard/assets/Workspace-CxFxvkA6.js +1 -0
- package/dist/dashboard/assets/arrow-up-DL3CuFOO.js +1 -0
- package/dist/dashboard/assets/forms-D6UIYgC5.js +1 -0
- package/dist/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/dashboard/assets/index-1iH716Xy.css +2 -0
- package/dist/dashboard/assets/index-BMWr9EKo.js +104 -0
- package/dist/dashboard/assets/task-errors-CE4oEr8H.js +1 -0
- package/dist/dashboard/assets/trash-CWqeZ7eg.js +1 -0
- package/dist/dashboard/favicon.svg +7 -0
- package/dist/dashboard/github.svg +1 -0
- package/dist/dashboard/index.html +31 -0
- package/dist/dist-ZPXEVBEC.js +357 -0
- package/dist/dist-ZPXEVBEC.js.map +1 -0
- package/dist/doctor-7RUD4D3A.js +279 -0
- package/dist/doctor-7RUD4D3A.js.map +1 -0
- package/dist/goal-JFRNPBSD.js +302 -0
- package/dist/goal-JFRNPBSD.js.map +1 -0
- package/dist/index.js +381 -0
- package/dist/index.js.map +1 -0
- package/dist/init-NFYNO3A3.js +194 -0
- package/dist/init-NFYNO3A3.js.map +1 -0
- package/dist/prompts-L37J72YW.js +215 -0
- package/dist/prompts-L37J72YW.js.map +1 -0
- package/dist/run-ARUPXFFB.js +376 -0
- package/dist/run-ARUPXFFB.js.map +1 -0
- package/dist/runner-state-QFU3LJVJ.js +17 -0
- package/dist/runner-state-QFU3LJVJ.js.map +1 -0
- package/dist/start-CFPOVIAR.js +285 -0
- package/dist/start-CFPOVIAR.js.map +1 -0
- package/dist/status-E4EACVE2.js +25 -0
- package/dist/status-E4EACVE2.js.map +1 -0
- package/dist/tasks-ZIK6BVEX.js +184 -0
- package/dist/tasks-ZIK6BVEX.js.map +1 -0
- package/package.json +68 -4
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
themeFor
|
|
3
|
+
} from "./chunk-VVCXGQMY.js";
|
|
4
|
+
|
|
5
|
+
// src/context.ts
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { join, resolve } from "path";
|
|
8
|
+
function resolveHome(env, flag, cwd = process.cwd()) {
|
|
9
|
+
if (flag) return resolve(cwd, flag);
|
|
10
|
+
return env.DAVECODE_HOME ? resolve(cwd, env.DAVECODE_HOME) : join(homedir(), ".davecode");
|
|
11
|
+
}
|
|
12
|
+
function createContext(options, io, base) {
|
|
13
|
+
const home = resolveHome(base.env, options.home, base.cwd);
|
|
14
|
+
const env = { ...base.env, DAVECODE_HOME: home };
|
|
15
|
+
const json = options.json === true;
|
|
16
|
+
const theme = themeFor(env, io.stdout, json ? false : options.color);
|
|
17
|
+
const errTheme = themeFor(env, io.stderr, options.color);
|
|
18
|
+
return {
|
|
19
|
+
env,
|
|
20
|
+
home,
|
|
21
|
+
cwd: base.cwd,
|
|
22
|
+
json,
|
|
23
|
+
theme,
|
|
24
|
+
errTheme,
|
|
25
|
+
io,
|
|
26
|
+
interactive: io.stdin.isTTY === true && io.stdout.isTTY === true && !json,
|
|
27
|
+
columns: io.stdout.columns && io.stdout.columns > 0 ? io.stdout.columns : 80,
|
|
28
|
+
out: (line = "") => {
|
|
29
|
+
io.stdout.write(`${line}
|
|
30
|
+
`);
|
|
31
|
+
},
|
|
32
|
+
err: (line = "") => {
|
|
33
|
+
io.stderr.write(`${line}
|
|
34
|
+
`);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function printJson(ctx, value) {
|
|
39
|
+
ctx.io.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
40
|
+
`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
createContext,
|
|
45
|
+
printJson
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=chunk-DYQ56FBT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { type Theme, themeFor } from './ui/theme';\n\n/** Minimal writable surface the CLI prints to (process streams in production, buffers in tests). */\nexport interface OutStream {\n write(chunk: string): unknown;\n isTTY?: boolean;\n columns?: number;\n}\n\nexport interface InStream {\n isTTY?: boolean;\n}\n\nexport interface CliIO {\n stdout: OutStream;\n stderr: OutStream;\n stdin: InStream;\n}\n\n/** Options shared by every command (`--home`, `--json`, `--no-color`). */\nexport interface GlobalOptions {\n home?: string;\n json?: boolean;\n color?: boolean;\n}\n\n/** Everything a command needs; built once per invocation from the global flags. */\nexport interface CliContext {\n /** Effective environment (`DAVECODE_HOME` reflects `--home`). */\n env: NodeJS.ProcessEnv;\n /** Resolved DaveCode home. */\n home: string;\n cwd: string;\n json: boolean;\n theme: Theme;\n /** Theme for stderr (it can be a TTY while stdout is piped). */\n errTheme: Theme;\n io: CliIO;\n /** Both stdin and stdout are terminals and `--json` is off. */\n interactive: boolean;\n /** Terminal width (80 when unknown). */\n columns: number;\n /** Print a line to stdout. */\n out(line?: string): void;\n /** Print a line to stderr. */\n err(line?: string): void;\n}\n\nexport function resolveHome(env: NodeJS.ProcessEnv, flag?: string, cwd = process.cwd()): string {\n if (flag) return resolve(cwd, flag);\n return env.DAVECODE_HOME ? resolve(cwd, env.DAVECODE_HOME) : join(homedir(), '.davecode');\n}\n\nexport function createContext(\n options: GlobalOptions,\n io: CliIO,\n base: { env: NodeJS.ProcessEnv; cwd: string },\n): CliContext {\n const home = resolveHome(base.env, options.home, base.cwd);\n const env = { ...base.env, DAVECODE_HOME: home };\n const json = options.json === true;\n const theme = themeFor(env, io.stdout, json ? false : options.color);\n const errTheme = themeFor(env, io.stderr, options.color);\n return {\n env,\n home,\n cwd: base.cwd,\n json,\n theme,\n errTheme,\n io,\n interactive: io.stdin.isTTY === true && io.stdout.isTTY === true && !json,\n columns: io.stdout.columns && io.stdout.columns > 0 ? io.stdout.columns : 80,\n out: (line = '') => {\n io.stdout.write(`${line}\\n`);\n },\n err: (line = '') => {\n io.stderr.write(`${line}\\n`);\n },\n };\n}\n\n/** Print a value as pretty JSON on stdout (the `--json` contract). */\nexport function printJson(ctx: CliContext, value: unknown): void {\n ctx.io.stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n"],"mappings":";;;;;AAAA,SAAS,eAAe;AACxB,SAAS,MAAM,eAAe;AAiDvB,SAAS,YAAY,KAAwB,MAAe,MAAM,QAAQ,IAAI,GAAW;AAC9F,MAAI,KAAM,QAAO,QAAQ,KAAK,IAAI;AAClC,SAAO,IAAI,gBAAgB,QAAQ,KAAK,IAAI,aAAa,IAAI,KAAK,QAAQ,GAAG,WAAW;AAC1F;AAEO,SAAS,cACd,SACA,IACA,MACY;AACZ,QAAM,OAAO,YAAY,KAAK,KAAK,QAAQ,MAAM,KAAK,GAAG;AACzD,QAAM,MAAM,EAAE,GAAG,KAAK,KAAK,eAAe,KAAK;AAC/C,QAAM,OAAO,QAAQ,SAAS;AAC9B,QAAM,QAAQ,SAAS,KAAK,GAAG,QAAQ,OAAO,QAAQ,QAAQ,KAAK;AACnE,QAAM,WAAW,SAAS,KAAK,GAAG,QAAQ,QAAQ,KAAK;AACvD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,KAAK,KAAK;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,GAAG,MAAM,UAAU,QAAQ,GAAG,OAAO,UAAU,QAAQ,CAAC;AAAA,IACrE,SAAS,GAAG,OAAO,WAAW,GAAG,OAAO,UAAU,IAAI,GAAG,OAAO,UAAU;AAAA,IAC1E,KAAK,CAAC,OAAO,OAAO;AAClB,SAAG,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAAA,IAC7B;AAAA,IACA,KAAK,CAAC,OAAO,OAAO;AAClB,SAAG,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAAA,IAC7B;AAAA,EACF;AACF;AAGO,SAAS,UAAU,KAAiB,OAAsB;AAC/D,MAAI,GAAG,OAAO,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,CAAI;AAC3D;","names":[]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/tui/runner-state.ts
|
|
2
|
+
var LOG_LIMIT = 200;
|
|
3
|
+
function initialRunnerState(status = { state: "idle" }, tasks = []) {
|
|
4
|
+
return {
|
|
5
|
+
status,
|
|
6
|
+
tasks: Object.fromEntries(
|
|
7
|
+
tasks.map((t) => [
|
|
8
|
+
t.id,
|
|
9
|
+
{
|
|
10
|
+
id: t.id,
|
|
11
|
+
title: t.title,
|
|
12
|
+
status: t.status,
|
|
13
|
+
...t.attempts ? { attempts: t.attempts } : {}
|
|
14
|
+
}
|
|
15
|
+
])
|
|
16
|
+
),
|
|
17
|
+
logs: [],
|
|
18
|
+
done: { success: 0, failed: 0 }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function runnerReducer(state, event) {
|
|
22
|
+
switch (event.type) {
|
|
23
|
+
case "runner.status":
|
|
24
|
+
return { ...state, status: event.status };
|
|
25
|
+
case "runner.log": {
|
|
26
|
+
const line = { ts: event.ts, level: event.level, message: event.message };
|
|
27
|
+
if (event.taskId) line.taskId = event.taskId;
|
|
28
|
+
return { ...state, logs: [...state.logs, line].slice(-LOG_LIMIT) };
|
|
29
|
+
}
|
|
30
|
+
case "task.updated": {
|
|
31
|
+
const { task } = event;
|
|
32
|
+
const previous = state.tasks[task.id];
|
|
33
|
+
const done = { ...state.done };
|
|
34
|
+
if (previous?.status !== task.status) {
|
|
35
|
+
if (task.status === "SUCCESS") done.success++;
|
|
36
|
+
if (task.status === "FAILED") done.failed++;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
...state,
|
|
40
|
+
done,
|
|
41
|
+
tasks: {
|
|
42
|
+
...state.tasks,
|
|
43
|
+
[task.id]: {
|
|
44
|
+
id: task.id,
|
|
45
|
+
title: task.title,
|
|
46
|
+
status: task.status,
|
|
47
|
+
...task.attempts ? { attempts: task.attempts } : {}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
default:
|
|
53
|
+
return state;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
var PIPELINE = [
|
|
57
|
+
"selecting",
|
|
58
|
+
"preparing",
|
|
59
|
+
"implementing",
|
|
60
|
+
"validating",
|
|
61
|
+
"merging"
|
|
62
|
+
];
|
|
63
|
+
function pipelineIndex(state) {
|
|
64
|
+
if (state === "repairing") return PIPELINE.indexOf("validating");
|
|
65
|
+
return PIPELINE.indexOf(state);
|
|
66
|
+
}
|
|
67
|
+
function isTerminal(state) {
|
|
68
|
+
return state === "stopped" || state === "error";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
LOG_LIMIT,
|
|
73
|
+
initialRunnerState,
|
|
74
|
+
runnerReducer,
|
|
75
|
+
PIPELINE,
|
|
76
|
+
pipelineIndex,
|
|
77
|
+
isTerminal
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=chunk-FRFSJMN4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/runner-state.ts"],"sourcesContent":["import type { DaveEvent, LogLevel, RunnerState, RunnerStatus, TaskNode } from '@davecode/core';\n\n/** Pure state behind the runner view, folded from `runner.*` and `task.updated` events. */\n\nexport interface LogLine {\n ts: number;\n level: LogLevel;\n message: string;\n taskId?: string;\n}\n\nexport interface RunnerViewState {\n status: RunnerStatus;\n /** Latest known version of each task (from `task.updated` or the initial graph). */\n tasks: Record<string, Pick<TaskNode, 'id' | 'title' | 'status' | 'attempts'>>;\n logs: LogLine[];\n /** Tasks finished in this session. */\n done: { success: number; failed: number };\n}\n\nexport const LOG_LIMIT = 200;\n\nexport function initialRunnerState(\n status: RunnerStatus = { state: 'idle' },\n tasks: TaskNode[] = [],\n): RunnerViewState {\n return {\n status,\n tasks: Object.fromEntries(\n tasks.map((t) => [\n t.id,\n {\n id: t.id,\n title: t.title,\n status: t.status,\n ...(t.attempts ? { attempts: t.attempts } : {}),\n },\n ]),\n ),\n logs: [],\n done: { success: 0, failed: 0 },\n };\n}\n\nexport function runnerReducer(state: RunnerViewState, event: DaveEvent): RunnerViewState {\n switch (event.type) {\n case 'runner.status':\n return { ...state, status: event.status };\n case 'runner.log': {\n const line: LogLine = { ts: event.ts, level: event.level, message: event.message };\n if (event.taskId) line.taskId = event.taskId;\n return { ...state, logs: [...state.logs, line].slice(-LOG_LIMIT) };\n }\n case 'task.updated': {\n const { task } = event;\n const previous = state.tasks[task.id];\n const done = { ...state.done };\n if (previous?.status !== task.status) {\n if (task.status === 'SUCCESS') done.success++;\n if (task.status === 'FAILED') done.failed++;\n }\n return {\n ...state,\n done,\n tasks: {\n ...state.tasks,\n [task.id]: {\n id: task.id,\n title: task.title,\n status: task.status,\n ...(task.attempts ? { attempts: task.attempts } : {}),\n },\n },\n };\n }\n default:\n return state;\n }\n}\n\n/** The happy path of the state machine, as drawn in the view. */\nexport const PIPELINE: readonly RunnerState[] = [\n 'selecting',\n 'preparing',\n 'implementing',\n 'validating',\n 'merging',\n];\n\n/** Index of `state` on the pipeline (`repairing` sits on `validating`), or -1 off-pipeline. */\nexport function pipelineIndex(state: RunnerState): number {\n if (state === 'repairing') return PIPELINE.indexOf('validating');\n return PIPELINE.indexOf(state);\n}\n\n/** True once the runner has come to rest after a stop or a fatal error. */\nexport function isTerminal(state: RunnerState): boolean {\n return state === 'stopped' || state === 'error';\n}\n"],"mappings":";AAoBO,IAAM,YAAY;AAElB,SAAS,mBACd,SAAuB,EAAE,OAAO,OAAO,GACvC,QAAoB,CAAC,GACJ;AACjB,SAAO;AAAA,IACL;AAAA,IACA,OAAO,OAAO;AAAA,MACZ,MAAM,IAAI,CAAC,MAAM;AAAA,QACf,EAAE;AAAA,QACF;AAAA,UACE,IAAI,EAAE;AAAA,UACN,OAAO,EAAE;AAAA,UACT,QAAQ,EAAE;AAAA,UACV,GAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,IAAI,CAAC;AAAA,QAC/C;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,MAAM,CAAC;AAAA,IACP,MAAM,EAAE,SAAS,GAAG,QAAQ,EAAE;AAAA,EAChC;AACF;AAEO,SAAS,cAAc,OAAwB,OAAmC;AACvF,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,MAAM,OAAO;AAAA,IAC1C,KAAK,cAAc;AACjB,YAAM,OAAgB,EAAE,IAAI,MAAM,IAAI,OAAO,MAAM,OAAO,SAAS,MAAM,QAAQ;AACjF,UAAI,MAAM,OAAQ,MAAK,SAAS,MAAM;AACtC,aAAO,EAAE,GAAG,OAAO,MAAM,CAAC,GAAG,MAAM,MAAM,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE;AAAA,IACnE;AAAA,IACA,KAAK,gBAAgB;AACnB,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,WAAW,MAAM,MAAM,KAAK,EAAE;AACpC,YAAM,OAAO,EAAE,GAAG,MAAM,KAAK;AAC7B,UAAI,UAAU,WAAW,KAAK,QAAQ;AACpC,YAAI,KAAK,WAAW,UAAW,MAAK;AACpC,YAAI,KAAK,WAAW,SAAU,MAAK;AAAA,MACrC;AACA,aAAO;AAAA,QACL,GAAG;AAAA,QACH;AAAA,QACA,OAAO;AAAA,UACL,GAAG,MAAM;AAAA,UACT,CAAC,KAAK,EAAE,GAAG;AAAA,YACT,IAAI,KAAK;AAAA,YACT,OAAO,KAAK;AAAA,YACZ,QAAQ,KAAK;AAAA,YACb,GAAI,KAAK,WAAW,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,UACrD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA;AACE,aAAO;AAAA,EACX;AACF;AAGO,IAAM,WAAmC;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,cAAc,OAA4B;AACxD,MAAI,UAAU,YAAa,QAAO,SAAS,QAAQ,YAAY;AAC/D,SAAO,SAAS,QAAQ,KAAK;AAC/B;AAGO,SAAS,WAAW,OAA6B;AACtD,SAAO,UAAU,aAAa,UAAU;AAC1C;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/prompter.ts"],"sourcesContent":["/**\n * Interactive questions, decoupled from how they are drawn. Production uses the Ink prompts in\n * `tui/prompts.tsx`; tests script the answers.\n */\n\nexport interface Choice<T extends string> {\n value: T;\n label: string;\n hint?: string;\n}\n\nexport interface TextOptions {\n initial?: string;\n placeholder?: string;\n /** Return an error message to reject the value. */\n validate?: (value: string) => string | undefined;\n}\n\nexport interface SecretOptions {\n /** Allow an empty answer (e.g. an optional API key). */\n optional?: boolean;\n}\n\nexport interface Prompter {\n select<T extends string>(message: string, choices: Choice<T>[], initial?: T): Promise<T>;\n text(message: string, options?: TextOptions): Promise<string>;\n /** Masked input; the value is never echoed. */\n secret(message: string, options?: SecretOptions): Promise<string>;\n confirm(message: string, initial?: boolean): Promise<boolean>;\n}\n\n/** Thrown when the user aborts a prompt (Esc / Ctrl+C). */\nexport class PromptCancelledError extends Error {\n constructor() {\n super('Cancelled');\n this.name = 'PromptCancelledError';\n }\n}\n\n/** A prompter that replays queued answers in order (tests and scripted flows). */\nexport function scriptedPrompter(answers: Array<string | boolean>): Prompter & {\n asked: string[];\n} {\n const queue = [...answers];\n const asked: string[] = [];\n const next = (message: string) => {\n asked.push(message);\n if (queue.length === 0) throw new Error(`No scripted answer for: ${message}`);\n return queue.shift()!;\n };\n return {\n asked,\n async select<T extends string>(message: string, choices: Choice<T>[]) {\n const answer = String(next(message));\n const choice = choices.find((c) => c.value === answer);\n if (!choice) throw new Error(`Scripted answer ${answer} is not a choice for: ${message}`);\n return choice.value;\n },\n async text(message: string, options: TextOptions = {}) {\n const answer = String(next(message));\n const value = answer === '' ? (options.initial ?? '') : answer;\n const problem = options.validate?.(value);\n if (problem) throw new Error(`Scripted answer rejected for ${message}: ${problem}`);\n return value;\n },\n async secret(message: string) {\n return String(next(message));\n },\n async confirm(message: string) {\n return Boolean(next(message));\n },\n };\n}\n"],"mappings":";AAgCO,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,cAAc;AACZ,UAAM,WAAW;AACjB,SAAK,OAAO;AAAA,EACd;AACF;","names":[]}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
accountCreateSchema,
|
|
3
|
+
accountPatchSchema,
|
|
4
|
+
findGateway
|
|
5
|
+
} from "./chunk-PIIJ3RSI.js";
|
|
6
|
+
import {
|
|
7
|
+
CliError
|
|
8
|
+
} from "./chunk-7XVXEO7U.js";
|
|
9
|
+
import {
|
|
10
|
+
createEngine
|
|
11
|
+
} from "./chunk-C7FDRA3U.js";
|
|
12
|
+
|
|
13
|
+
// src/lib/accounts-backend.ts
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
function invalid(error) {
|
|
16
|
+
return new CliError(`Invalid account: ${z.prettifyError(error)}`);
|
|
17
|
+
}
|
|
18
|
+
var LocalAccounts = class {
|
|
19
|
+
constructor(engine) {
|
|
20
|
+
this.engine = engine;
|
|
21
|
+
}
|
|
22
|
+
engine;
|
|
23
|
+
kind = "local";
|
|
24
|
+
get config() {
|
|
25
|
+
return this.engine.config;
|
|
26
|
+
}
|
|
27
|
+
async list() {
|
|
28
|
+
return this.engine.accounts.list();
|
|
29
|
+
}
|
|
30
|
+
async get(id) {
|
|
31
|
+
return this.engine.accounts.get(id);
|
|
32
|
+
}
|
|
33
|
+
async create(input) {
|
|
34
|
+
const parsed = accountCreateSchema.safeParse(input);
|
|
35
|
+
if (!parsed.success) throw invalid(parsed.error);
|
|
36
|
+
const { secret, ...fields } = parsed.data;
|
|
37
|
+
const { accounts, keyring, audit, db } = this.engine;
|
|
38
|
+
return db.transaction(() => {
|
|
39
|
+
const created = accounts.create(fields);
|
|
40
|
+
if (secret !== void 0) keyring.set(created.id, secret);
|
|
41
|
+
audit.record({
|
|
42
|
+
actor: "cli",
|
|
43
|
+
action: "account.create",
|
|
44
|
+
target: created.id,
|
|
45
|
+
details: { provider: created.provider, label: created.label, hasSecret: !!secret }
|
|
46
|
+
});
|
|
47
|
+
return created;
|
|
48
|
+
})();
|
|
49
|
+
}
|
|
50
|
+
async update(id, patch) {
|
|
51
|
+
const parsed = accountPatchSchema.safeParse(patch);
|
|
52
|
+
if (!parsed.success) throw invalid(parsed.error);
|
|
53
|
+
const existing = this.engine.accounts.get(id);
|
|
54
|
+
if (!existing) throw notFound(id);
|
|
55
|
+
const { secret, ...fields } = parsed.data;
|
|
56
|
+
const update = { ...fields };
|
|
57
|
+
const enabled = fields.enabled ?? existing.enabled;
|
|
58
|
+
if (!enabled) {
|
|
59
|
+
update.status = "disabled";
|
|
60
|
+
} else if (fields.enabled === true || secret !== void 0) {
|
|
61
|
+
update.status = "active";
|
|
62
|
+
update.cooldownUntil = null;
|
|
63
|
+
update.lastError = null;
|
|
64
|
+
}
|
|
65
|
+
const { accounts, keyring, audit, db } = this.engine;
|
|
66
|
+
return db.transaction(() => {
|
|
67
|
+
const updated = accounts.update(id, update);
|
|
68
|
+
if (secret !== void 0) keyring.set(id, secret);
|
|
69
|
+
audit.record({
|
|
70
|
+
actor: "cli",
|
|
71
|
+
action: "account.update",
|
|
72
|
+
target: id,
|
|
73
|
+
details: { fields: Object.keys(fields), secretChanged: secret !== void 0 }
|
|
74
|
+
});
|
|
75
|
+
return updated;
|
|
76
|
+
})();
|
|
77
|
+
}
|
|
78
|
+
async remove(id) {
|
|
79
|
+
const { accounts, sandboxes, chromium, audit } = this.engine;
|
|
80
|
+
if (!accounts.delete(id)) throw notFound(id);
|
|
81
|
+
for (const cleanup of [() => sandboxes.remove(id), () => chromium.remove(id)]) {
|
|
82
|
+
try {
|
|
83
|
+
cleanup();
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
audit.record({ actor: "cli", action: "account.delete", target: id });
|
|
88
|
+
}
|
|
89
|
+
/** Quota windows are rebuilt from state.db; provider-reported limits live in the gateway only. */
|
|
90
|
+
async budget() {
|
|
91
|
+
return this.engine.budget.snapshot();
|
|
92
|
+
}
|
|
93
|
+
async connection(id) {
|
|
94
|
+
const account = this.engine.accounts.get(id);
|
|
95
|
+
if (!account) throw notFound(id);
|
|
96
|
+
return this.engine.connections.check(account);
|
|
97
|
+
}
|
|
98
|
+
close() {
|
|
99
|
+
this.engine.close();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var GatewayAccounts = class {
|
|
103
|
+
constructor(client, config) {
|
|
104
|
+
this.client = client;
|
|
105
|
+
this.config = config;
|
|
106
|
+
}
|
|
107
|
+
client;
|
|
108
|
+
config;
|
|
109
|
+
kind = "gateway";
|
|
110
|
+
async list() {
|
|
111
|
+
return (await this.client.get("/api/accounts")).accounts;
|
|
112
|
+
}
|
|
113
|
+
async get(id) {
|
|
114
|
+
return (await this.list()).find((a) => a.id === id);
|
|
115
|
+
}
|
|
116
|
+
async create(input) {
|
|
117
|
+
return (await this.client.post("/api/accounts", input)).account;
|
|
118
|
+
}
|
|
119
|
+
async update(id, patch) {
|
|
120
|
+
return (await this.client.patch(
|
|
121
|
+
`/api/accounts/${encodeURIComponent(id)}`,
|
|
122
|
+
patch
|
|
123
|
+
)).account;
|
|
124
|
+
}
|
|
125
|
+
async remove(id) {
|
|
126
|
+
await this.client.delete(`/api/accounts/${encodeURIComponent(id)}`);
|
|
127
|
+
}
|
|
128
|
+
async budget() {
|
|
129
|
+
return (await this.client.get("/api/budget")).budget;
|
|
130
|
+
}
|
|
131
|
+
async connection(id) {
|
|
132
|
+
return (await this.client.get(
|
|
133
|
+
`/api/accounts/${encodeURIComponent(id)}/connection`
|
|
134
|
+
)).connection;
|
|
135
|
+
}
|
|
136
|
+
close() {
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
function notFound(id) {
|
|
140
|
+
return new CliError(`No account with id ${JSON.stringify(id)}`, {
|
|
141
|
+
hint: "List accounts with `davecode accounts list`."
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async function openAccounts(options) {
|
|
145
|
+
const engine = createEngine({
|
|
146
|
+
home: options.home,
|
|
147
|
+
env: options.env,
|
|
148
|
+
...options.projectRoot ? { projectRoot: options.projectRoot } : {}
|
|
149
|
+
});
|
|
150
|
+
if (!options.local) {
|
|
151
|
+
const gateway = await findGateway(engine.config).catch(() => void 0);
|
|
152
|
+
if (gateway) {
|
|
153
|
+
const config = engine.config;
|
|
154
|
+
engine.close();
|
|
155
|
+
return new GatewayAccounts(gateway.client, config);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return new LocalAccounts(engine);
|
|
159
|
+
}
|
|
160
|
+
async function resolveAccount(backend, ref) {
|
|
161
|
+
const accounts = await backend.list();
|
|
162
|
+
const exact = accounts.find((a) => a.id === ref);
|
|
163
|
+
if (exact) return exact;
|
|
164
|
+
const matches = accounts.filter(
|
|
165
|
+
(a) => a.label.toLowerCase() === ref.toLowerCase() || a.id.startsWith(ref)
|
|
166
|
+
);
|
|
167
|
+
if (matches.length === 1) return matches[0];
|
|
168
|
+
if (matches.length > 1) {
|
|
169
|
+
throw new CliError(`${JSON.stringify(ref)} matches ${matches.length} accounts`, {
|
|
170
|
+
hint: `Use the full id: ${matches.map((a) => a.id).join(", ")}`
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
throw notFound(ref);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export {
|
|
177
|
+
openAccounts,
|
|
178
|
+
resolveAccount
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=chunk-IVZHKUCD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/accounts-backend.ts"],"sourcesContent":["import {\n type Account,\n type AccountConnection,\n type AccountUpdateInput,\n type BudgetSnapshot,\n createEngine,\n type DaveConfig,\n type Engine,\n type ProviderKind,\n type QuotaLimits,\n type SubscriptionInfo,\n} from '@davecode/core';\nimport { accountCreateSchema, accountPatchSchema } from '@davecode/server';\nimport { z } from 'zod';\nimport { findGateway, type GatewayClient } from '../client';\nimport { CliError } from '../errors';\n\n/** Body of `POST /api/accounts` (secret is write-only). */\nexport interface AccountCreate {\n provider: ProviderKind;\n label: string;\n enabled?: boolean;\n priority?: number;\n weight?: number;\n limits?: QuotaLimits;\n config?: Record<string, unknown>;\n subscription?: SubscriptionInfo;\n secret?: string;\n}\n\nexport type AccountPatch = Partial<Omit<AccountCreate, 'provider' | 'subscription'>> & {\n /** Replaces the subscription details; `null` clears them. */\n subscription?: SubscriptionInfo | null;\n};\n\n/**\n * Account operations against a running gateway (so the dashboard sees the change live) or, when\n * none is running, directly against `state.db` with the same validation and audit trail.\n */\nexport interface AccountsBackend {\n readonly kind: 'gateway' | 'local';\n readonly config: DaveConfig;\n list(): Promise<Account[]>;\n get(id: string): Promise<Account | undefined>;\n create(input: AccountCreate): Promise<Account>;\n update(id: string, patch: AccountPatch): Promise<Account>;\n remove(id: string): Promise<void>;\n /** Subscription details and remaining capacity per account. */\n budget(): Promise<BudgetSnapshot>;\n /** Whether the account's API key or vendor CLI login is in place. */\n connection(id: string): Promise<AccountConnection>;\n close(): void;\n}\n\nfunction invalid(error: z.ZodError): CliError {\n return new CliError(`Invalid account: ${z.prettifyError(error)}`);\n}\n\nexport class LocalAccounts implements AccountsBackend {\n readonly kind = 'local' as const;\n\n constructor(readonly engine: Engine) {}\n\n get config(): DaveConfig {\n return this.engine.config;\n }\n\n async list(): Promise<Account[]> {\n return this.engine.accounts.list();\n }\n\n async get(id: string): Promise<Account | undefined> {\n return this.engine.accounts.get(id);\n }\n\n async create(input: AccountCreate): Promise<Account> {\n const parsed = accountCreateSchema.safeParse(input);\n if (!parsed.success) throw invalid(parsed.error);\n const { secret, ...fields } = parsed.data;\n const { accounts, keyring, audit, db } = this.engine;\n return db.transaction(() => {\n const created = accounts.create(fields);\n if (secret !== undefined) keyring.set(created.id, secret);\n audit.record({\n actor: 'cli',\n action: 'account.create',\n target: created.id,\n details: { provider: created.provider, label: created.label, hasSecret: !!secret },\n });\n return created;\n })();\n }\n\n async update(id: string, patch: AccountPatch): Promise<Account> {\n const parsed = accountPatchSchema.safeParse(patch);\n if (!parsed.success) throw invalid(parsed.error);\n const existing = this.engine.accounts.get(id);\n if (!existing) throw notFound(id);\n const { secret, ...fields } = parsed.data;\n const update: AccountUpdateInput = { ...fields };\n const enabled = fields.enabled ?? existing.enabled;\n if (!enabled) {\n update.status = 'disabled';\n } else if (fields.enabled === true || secret !== undefined) {\n update.status = 'active';\n update.cooldownUntil = null;\n update.lastError = null;\n }\n const { accounts, keyring, audit, db } = this.engine;\n return db.transaction(() => {\n const updated = accounts.update(id, update)!;\n if (secret !== undefined) keyring.set(id, secret);\n audit.record({\n actor: 'cli',\n action: 'account.update',\n target: id,\n details: { fields: Object.keys(fields), secretChanged: secret !== undefined },\n });\n return updated;\n })();\n }\n\n async remove(id: string): Promise<void> {\n const { accounts, sandboxes, chromium, audit } = this.engine;\n if (!accounts.delete(id)) throw notFound(id);\n for (const cleanup of [() => sandboxes.remove(id), () => chromium.remove(id)]) {\n try {\n cleanup();\n } catch {\n // A leftover directory is harmless; the account row is gone.\n }\n }\n audit.record({ actor: 'cli', action: 'account.delete', target: id });\n }\n\n /** Quota windows are rebuilt from state.db; provider-reported limits live in the gateway only. */\n async budget(): Promise<BudgetSnapshot> {\n return this.engine.budget.snapshot();\n }\n\n async connection(id: string): Promise<AccountConnection> {\n const account = this.engine.accounts.get(id);\n if (!account) throw notFound(id);\n return this.engine.connections.check(account);\n }\n\n close(): void {\n this.engine.close();\n }\n}\n\nexport class GatewayAccounts implements AccountsBackend {\n readonly kind = 'gateway' as const;\n\n constructor(\n readonly client: GatewayClient,\n readonly config: DaveConfig,\n ) {}\n\n async list(): Promise<Account[]> {\n return (await this.client.get<{ accounts: Account[] }>('/api/accounts')).accounts;\n }\n\n async get(id: string): Promise<Account | undefined> {\n return (await this.list()).find((a) => a.id === id);\n }\n\n async create(input: AccountCreate): Promise<Account> {\n return (await this.client.post<{ account: Account }>('/api/accounts', input)).account;\n }\n\n async update(id: string, patch: AccountPatch): Promise<Account> {\n return (\n await this.client.patch<{ account: Account }>(\n `/api/accounts/${encodeURIComponent(id)}`,\n patch,\n )\n ).account;\n }\n\n async remove(id: string): Promise<void> {\n await this.client.delete(`/api/accounts/${encodeURIComponent(id)}`);\n }\n\n async budget(): Promise<BudgetSnapshot> {\n return (await this.client.get<{ budget: BudgetSnapshot }>('/api/budget')).budget;\n }\n\n async connection(id: string): Promise<AccountConnection> {\n return (\n await this.client.get<{ connection: AccountConnection }>(\n `/api/accounts/${encodeURIComponent(id)}/connection`,\n )\n ).connection;\n }\n\n close(): void {}\n}\n\nfunction notFound(id: string): CliError {\n return new CliError(`No account with id ${JSON.stringify(id)}`, {\n hint: 'List accounts with `davecode accounts list`.',\n });\n}\n\n/** Prefer the running gateway; fall back to the local database. */\nexport async function openAccounts(options: {\n home: string;\n env: NodeJS.ProcessEnv;\n projectRoot?: string;\n /** Skip the gateway probe (tests, or `--local`). */\n local?: boolean;\n}): Promise<AccountsBackend> {\n const engine = createEngine({\n home: options.home,\n env: options.env,\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n });\n if (!options.local) {\n const gateway = await findGateway(engine.config).catch(() => undefined);\n if (gateway) {\n const config = engine.config;\n engine.close();\n return new GatewayAccounts(gateway.client, config);\n }\n }\n return new LocalAccounts(engine);\n}\n\n/** Resolve an account by exact id, or by unique label/id prefix for convenience. */\nexport async function resolveAccount(backend: AccountsBackend, ref: string): Promise<Account> {\n const accounts = await backend.list();\n const exact = accounts.find((a) => a.id === ref);\n if (exact) return exact;\n const matches = accounts.filter(\n (a) => a.label.toLowerCase() === ref.toLowerCase() || a.id.startsWith(ref),\n );\n if (matches.length === 1) return matches[0]!;\n if (matches.length > 1) {\n throw new CliError(`${JSON.stringify(ref)} matches ${matches.length} accounts`, {\n hint: `Use the full id: ${matches.map((a) => a.id).join(', ')}`,\n });\n }\n throw notFound(ref);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bar,
|
|
3
|
+
formatDuration,
|
|
4
|
+
formatTokens,
|
|
5
|
+
levelStyle,
|
|
6
|
+
percent,
|
|
7
|
+
table,
|
|
8
|
+
truncate
|
|
9
|
+
} from "./chunk-SF5ZEAP4.js";
|
|
10
|
+
import {
|
|
11
|
+
printJson
|
|
12
|
+
} from "./chunk-DYQ56FBT.js";
|
|
13
|
+
import {
|
|
14
|
+
detectProjectRoot,
|
|
15
|
+
findGateway,
|
|
16
|
+
targetFor
|
|
17
|
+
} from "./chunk-PIIJ3RSI.js";
|
|
18
|
+
import {
|
|
19
|
+
EXIT
|
|
20
|
+
} from "./chunk-7XVXEO7U.js";
|
|
21
|
+
import {
|
|
22
|
+
QUOTA_WINDOWS,
|
|
23
|
+
createEngine,
|
|
24
|
+
loadConfig
|
|
25
|
+
} from "./chunk-C7FDRA3U.js";
|
|
26
|
+
|
|
27
|
+
// src/commands/status.ts
|
|
28
|
+
function emptyUsage(accountId) {
|
|
29
|
+
const windows = Object.fromEntries(
|
|
30
|
+
QUOTA_WINDOWS.map((w) => [w, { window: w, tokens: 0, requests: 0, utilization: 0 }])
|
|
31
|
+
);
|
|
32
|
+
return { accountId, windows };
|
|
33
|
+
}
|
|
34
|
+
async function fromGateway(client, health, home) {
|
|
35
|
+
const [{ accounts }, { usage }, runner] = await Promise.all([
|
|
36
|
+
client.get("/api/accounts"),
|
|
37
|
+
client.get("/api/usage"),
|
|
38
|
+
client.get("/api/runner").catch(() => void 0)
|
|
39
|
+
]);
|
|
40
|
+
const byId = new Map(usage.map((u) => [u.accountId, u]));
|
|
41
|
+
return {
|
|
42
|
+
running: true,
|
|
43
|
+
source: "gateway",
|
|
44
|
+
url: client.baseUrl,
|
|
45
|
+
version: health.version,
|
|
46
|
+
uptimeSec: health.uptimeSec,
|
|
47
|
+
experimental: health.experimental,
|
|
48
|
+
...runner ? { runner: runner.status } : {},
|
|
49
|
+
home,
|
|
50
|
+
accounts: accounts.map((account) => ({
|
|
51
|
+
account,
|
|
52
|
+
usage: byId.get(account.id) ?? emptyUsage(account.id)
|
|
53
|
+
}))
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function localStatus(ctx, projectRoot) {
|
|
57
|
+
const engine = createEngine({
|
|
58
|
+
home: ctx.home,
|
|
59
|
+
env: ctx.env,
|
|
60
|
+
...projectRoot ? { projectRoot } : {}
|
|
61
|
+
});
|
|
62
|
+
try {
|
|
63
|
+
return {
|
|
64
|
+
running: false,
|
|
65
|
+
source: "local",
|
|
66
|
+
url: targetFor(engine.config).baseUrl,
|
|
67
|
+
experimental: { ...engine.config.experimental },
|
|
68
|
+
home: engine.home,
|
|
69
|
+
accounts: engine.accounts.list().map((account) => ({
|
|
70
|
+
account,
|
|
71
|
+
usage: engine.tracker.usageFor(account)
|
|
72
|
+
}))
|
|
73
|
+
};
|
|
74
|
+
} finally {
|
|
75
|
+
engine.close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function gatherStatus(ctx) {
|
|
79
|
+
const projectRoot = await detectProjectRoot(ctx.cwd, ctx.home);
|
|
80
|
+
const config = loadConfig({
|
|
81
|
+
home: ctx.home,
|
|
82
|
+
env: ctx.env,
|
|
83
|
+
...projectRoot ? { projectRoot } : {}
|
|
84
|
+
});
|
|
85
|
+
const gateway = await findGateway(config);
|
|
86
|
+
if (gateway) return fromGateway(gateway.client, gateway.health, ctx.home);
|
|
87
|
+
return localStatus(ctx, projectRoot);
|
|
88
|
+
}
|
|
89
|
+
function statusLabel(theme, account) {
|
|
90
|
+
switch (account.status) {
|
|
91
|
+
case "active":
|
|
92
|
+
return theme.ok("active");
|
|
93
|
+
case "cooldown":
|
|
94
|
+
return theme.warn("cooldown");
|
|
95
|
+
case "error":
|
|
96
|
+
return theme.error("error");
|
|
97
|
+
default:
|
|
98
|
+
return theme.dim("disabled");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function windowCell(theme, usage, width) {
|
|
102
|
+
const limited = usage.tokenLimit !== void 0 || usage.requestLimit !== void 0;
|
|
103
|
+
if (!limited) {
|
|
104
|
+
return `${bar(theme, 0, width, false)} ${theme.dim(formatTokens(usage.tokens))}`;
|
|
105
|
+
}
|
|
106
|
+
return `${bar(theme, usage.utilization, width)} ${levelStyle(
|
|
107
|
+
theme,
|
|
108
|
+
usage.utilization
|
|
109
|
+
)(percent(usage.utilization))}`;
|
|
110
|
+
}
|
|
111
|
+
function renderAccounts(theme, rows, columns) {
|
|
112
|
+
const barWidth = columns >= 110 ? 10 : columns >= 90 ? 8 : 5;
|
|
113
|
+
const cols = [
|
|
114
|
+
{ header: "ACCOUNT" },
|
|
115
|
+
{ header: "ID", drop: 4 },
|
|
116
|
+
{ header: "PROVIDER", drop: 2 },
|
|
117
|
+
{ header: "STATUS" },
|
|
118
|
+
{ header: "PRI", align: "right", drop: 5 },
|
|
119
|
+
{ header: "1M", drop: 3 },
|
|
120
|
+
{ header: "5H" },
|
|
121
|
+
{ header: "24H", drop: 1 }
|
|
122
|
+
];
|
|
123
|
+
const body = rows.map(({ account, usage }) => [
|
|
124
|
+
truncate(account.label, 28),
|
|
125
|
+
theme.dim(account.id),
|
|
126
|
+
account.provider,
|
|
127
|
+
statusLabel(theme, account),
|
|
128
|
+
String(account.priority),
|
|
129
|
+
windowCell(theme, usage.windows["1m"], barWidth),
|
|
130
|
+
windowCell(theme, usage.windows["5h"], barWidth),
|
|
131
|
+
windowCell(theme, usage.windows["24h"], barWidth)
|
|
132
|
+
]);
|
|
133
|
+
return table(theme, cols, body, columns);
|
|
134
|
+
}
|
|
135
|
+
function renderStatus(theme, report, columns = 100) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
const name = theme.bold(theme.accent("DaveCode"));
|
|
138
|
+
if (report.running) {
|
|
139
|
+
const meta = [
|
|
140
|
+
report.version ? `v${report.version}` : void 0,
|
|
141
|
+
report.uptimeSec !== void 0 ? `up ${formatDuration(report.uptimeSec)}` : void 0
|
|
142
|
+
].filter(Boolean);
|
|
143
|
+
lines.push(
|
|
144
|
+
`${name} ${theme.ok(`${theme.glyph.dot} running`)} ${theme.accent(report.url)} ${theme.dim(meta.join(" \xB7 "))}`
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
lines.push(
|
|
148
|
+
`${name} ${theme.dim(`${theme.glyph.pending} not running`)} ${theme.dim(`local state from ${report.home}`)}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
if (report.runner && report.runner.state !== "idle") {
|
|
152
|
+
lines.push(
|
|
153
|
+
`${theme.dim("runner")} ${report.runner.state}${report.runner.taskId ? theme.dim(` \xB7 ${report.runner.taskId}`) : ""}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
lines.push("");
|
|
157
|
+
if (report.accounts.length === 0) {
|
|
158
|
+
lines.push(theme.dim("No accounts yet."));
|
|
159
|
+
lines.push(`Add one with ${theme.accent("davecode accounts add")}.`);
|
|
160
|
+
} else {
|
|
161
|
+
lines.push(...renderAccounts(theme, report.accounts, columns));
|
|
162
|
+
const troubled = report.accounts.filter(
|
|
163
|
+
({ account }) => account.lastError && account.status !== "active"
|
|
164
|
+
);
|
|
165
|
+
if (troubled.length > 0) lines.push("");
|
|
166
|
+
for (const { account } of troubled) {
|
|
167
|
+
lines.push(
|
|
168
|
+
`${theme.warn(theme.glyph.warn)} ${account.label}: ${theme.dim(truncate(account.lastError ?? "", Math.max(20, columns - account.label.length - 4)))}`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const flags = Object.entries(report.experimental).filter(([, on]) => on).map(([flag]) => `experimental.${flag}`);
|
|
173
|
+
if (flags.length > 0) {
|
|
174
|
+
lines.push("");
|
|
175
|
+
lines.push(
|
|
176
|
+
theme.warn(`${theme.glyph.warn} ${flags.join(", ")} enabled (may violate provider ToS)`)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
if (!report.running) {
|
|
180
|
+
lines.push("");
|
|
181
|
+
lines.push(theme.dim(`Start the gateway with ${"`davecode start`"} for live data.`));
|
|
182
|
+
}
|
|
183
|
+
return lines;
|
|
184
|
+
}
|
|
185
|
+
async function statusCommand(ctx) {
|
|
186
|
+
const report = await gatherStatus(ctx);
|
|
187
|
+
if (ctx.json) {
|
|
188
|
+
printJson(ctx, {
|
|
189
|
+
running: report.running,
|
|
190
|
+
source: report.source,
|
|
191
|
+
url: report.url,
|
|
192
|
+
version: report.version ?? null,
|
|
193
|
+
uptimeSec: report.uptimeSec ?? null,
|
|
194
|
+
experimental: report.experimental,
|
|
195
|
+
runner: report.runner ?? null,
|
|
196
|
+
accounts: report.accounts.map(({ account, usage }) => ({ ...account, usage }))
|
|
197
|
+
});
|
|
198
|
+
return EXIT.ok;
|
|
199
|
+
}
|
|
200
|
+
for (const line of renderStatus(ctx.theme, report, ctx.columns)) ctx.out(line);
|
|
201
|
+
return EXIT.ok;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export {
|
|
205
|
+
emptyUsage,
|
|
206
|
+
localStatus,
|
|
207
|
+
gatherStatus,
|
|
208
|
+
windowCell,
|
|
209
|
+
renderAccounts,
|
|
210
|
+
renderStatus,
|
|
211
|
+
statusCommand
|
|
212
|
+
};
|
|
213
|
+
//# sourceMappingURL=chunk-MSLLH5XP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/status.ts"],"sourcesContent":["import {\n type Account,\n type AccountUsage,\n createEngine,\n type DaveConfig,\n loadConfig,\n QUOTA_WINDOWS,\n type QuotaWindow,\n type RunnerStatus,\n type WindowUsage,\n} from '@davecode/core';\nimport { findGateway, type GatewayClient, type HealthResponse, targetFor } from '../client';\nimport { type CliContext, printJson } from '../context';\nimport { EXIT } from '../errors';\nimport { detectProjectRoot } from '../runtime';\nimport {\n bar,\n type Column,\n formatDuration,\n formatTokens,\n levelStyle,\n percent,\n table,\n truncate,\n} from '../ui/format';\nimport type { Theme } from '../ui/theme';\n\nexport interface AccountRow {\n account: Account;\n usage: AccountUsage;\n}\n\nexport interface StatusReport {\n running: boolean;\n /** Where the account data came from. */\n source: 'gateway' | 'local';\n url: string;\n version?: string;\n uptimeSec?: number;\n experimental: DaveConfig['experimental'];\n runner?: RunnerStatus;\n home: string;\n accounts: AccountRow[];\n}\n\nexport function emptyUsage(accountId: string): AccountUsage {\n const windows = Object.fromEntries(\n QUOTA_WINDOWS.map((w) => [w, { window: w, tokens: 0, requests: 0, utilization: 0 }]),\n ) as Record<QuotaWindow, WindowUsage>;\n return { accountId, windows };\n}\n\nasync function fromGateway(\n client: GatewayClient,\n health: HealthResponse,\n home: string,\n): Promise<StatusReport> {\n const [{ accounts }, { usage }, runner] = await Promise.all([\n client.get<{ accounts: Account[] }>('/api/accounts'),\n client.get<{ usage: AccountUsage[] }>('/api/usage'),\n client.get<{ status: RunnerStatus }>('/api/runner').catch(() => undefined),\n ]);\n const byId = new Map(usage.map((u) => [u.accountId, u]));\n return {\n running: true,\n source: 'gateway',\n url: client.baseUrl,\n version: health.version,\n uptimeSec: health.uptimeSec,\n experimental: health.experimental,\n ...(runner ? { runner: runner.status } : {}),\n home,\n accounts: accounts.map((account) => ({\n account,\n usage: byId.get(account.id) ?? emptyUsage(account.id),\n })),\n };\n}\n\n/** Read accounts and quota windows straight from `state.db` (no gateway needed). */\nexport function localStatus(\n ctx: Pick<CliContext, 'home' | 'env'>,\n projectRoot?: string,\n): StatusReport {\n const engine = createEngine({\n home: ctx.home,\n env: ctx.env,\n ...(projectRoot ? { projectRoot } : {}),\n });\n try {\n return {\n running: false,\n source: 'local',\n url: targetFor(engine.config).baseUrl,\n experimental: { ...engine.config.experimental },\n home: engine.home,\n accounts: engine.accounts.list().map((account) => ({\n account,\n usage: engine.tracker.usageFor(account),\n })),\n };\n } finally {\n engine.close();\n }\n}\n\nexport async function gatherStatus(ctx: CliContext): Promise<StatusReport> {\n const projectRoot = await detectProjectRoot(ctx.cwd, ctx.home);\n const config = loadConfig({\n home: ctx.home,\n env: ctx.env,\n ...(projectRoot ? { projectRoot } : {}),\n });\n const gateway = await findGateway(config);\n if (gateway) return fromGateway(gateway.client, gateway.health, ctx.home);\n return localStatus(ctx, projectRoot);\n}\n\n// ---------------------------------------------------------------------------\n// Rendering\n// ---------------------------------------------------------------------------\n\nfunction statusLabel(theme: Theme, account: Account): string {\n switch (account.status) {\n case 'active':\n return theme.ok('active');\n case 'cooldown':\n return theme.warn('cooldown');\n case 'error':\n return theme.error('error');\n default:\n return theme.dim('disabled');\n }\n}\n\n/** `████░░░░ 23%` for limited windows, a dim track plus raw tokens when unlimited. */\nexport function windowCell(theme: Theme, usage: WindowUsage, width: number): string {\n const limited = usage.tokenLimit !== undefined || usage.requestLimit !== undefined;\n if (!limited) {\n return `${bar(theme, 0, width, false)} ${theme.dim(formatTokens(usage.tokens))}`;\n }\n return `${bar(theme, usage.utilization, width)} ${levelStyle(\n theme,\n usage.utilization,\n )(percent(usage.utilization))}`;\n}\n\nexport function renderAccounts(theme: Theme, rows: AccountRow[], columns: number): string[] {\n const barWidth = columns >= 110 ? 10 : columns >= 90 ? 8 : 5;\n const cols: Column[] = [\n { header: 'ACCOUNT' },\n { header: 'ID', drop: 4 },\n { header: 'PROVIDER', drop: 2 },\n { header: 'STATUS' },\n { header: 'PRI', align: 'right', drop: 5 },\n { header: '1M', drop: 3 },\n { header: '5H' },\n { header: '24H', drop: 1 },\n ];\n const body = rows.map(({ account, usage }) => [\n truncate(account.label, 28),\n theme.dim(account.id),\n account.provider,\n statusLabel(theme, account),\n String(account.priority),\n windowCell(theme, usage.windows['1m'], barWidth),\n windowCell(theme, usage.windows['5h'], barWidth),\n windowCell(theme, usage.windows['24h'], barWidth),\n ]);\n return table(theme, cols, body, columns);\n}\n\nexport function renderStatus(theme: Theme, report: StatusReport, columns = 100): string[] {\n const lines: string[] = [];\n const name = theme.bold(theme.accent('DaveCode'));\n if (report.running) {\n const meta = [\n report.version ? `v${report.version}` : undefined,\n report.uptimeSec !== undefined ? `up ${formatDuration(report.uptimeSec)}` : undefined,\n ].filter(Boolean);\n lines.push(\n `${name} ${theme.ok(`${theme.glyph.dot} running`)} ${theme.accent(report.url)} ${theme.dim(meta.join(' · '))}`,\n );\n } else {\n lines.push(\n `${name} ${theme.dim(`${theme.glyph.pending} not running`)} ${theme.dim(`local state from ${report.home}`)}`,\n );\n }\n if (report.runner && report.runner.state !== 'idle') {\n lines.push(\n `${theme.dim('runner')} ${report.runner.state}${report.runner.taskId ? theme.dim(` · ${report.runner.taskId}`) : ''}`,\n );\n }\n lines.push('');\n\n if (report.accounts.length === 0) {\n lines.push(theme.dim('No accounts yet.'));\n lines.push(`Add one with ${theme.accent('davecode accounts add')}.`);\n } else {\n lines.push(...renderAccounts(theme, report.accounts, columns));\n const troubled = report.accounts.filter(\n ({ account }) => account.lastError && account.status !== 'active',\n );\n if (troubled.length > 0) lines.push('');\n for (const { account } of troubled) {\n lines.push(\n `${theme.warn(theme.glyph.warn)} ${account.label}: ${theme.dim(truncate(account.lastError ?? '', Math.max(20, columns - account.label.length - 4)))}`,\n );\n }\n }\n\n const flags = Object.entries(report.experimental)\n .filter(([, on]) => on)\n .map(([flag]) => `experimental.${flag}`);\n if (flags.length > 0) {\n lines.push('');\n lines.push(\n theme.warn(`${theme.glyph.warn} ${flags.join(', ')} enabled (may violate provider ToS)`),\n );\n }\n if (!report.running) {\n lines.push('');\n lines.push(theme.dim(`Start the gateway with ${'`davecode start`'} for live data.`));\n }\n return lines;\n}\n\nexport async function statusCommand(ctx: CliContext): Promise<number> {\n const report = await gatherStatus(ctx);\n if (ctx.json) {\n printJson(ctx, {\n running: report.running,\n source: report.source,\n url: report.url,\n version: report.version ?? null,\n uptimeSec: report.uptimeSec ?? null,\n experimental: report.experimental,\n runner: report.runner ?? null,\n accounts: report.accounts.map(({ account, usage }) => ({ ...account, usage })),\n });\n return EXIT.ok;\n }\n for (const line of renderStatus(ctx.theme, report, ctx.columns)) ctx.out(line);\n return EXIT.ok;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PLAIN_THEME
|
|
3
|
+
} from "./chunk-VVCXGQMY.js";
|
|
4
|
+
|
|
5
|
+
// src/tui/theme-context.tsx
|
|
6
|
+
import { createContext, useContext } from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var ThemeContext = createContext(PLAIN_THEME);
|
|
9
|
+
function ThemeProvider({ theme, children }) {
|
|
10
|
+
return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: theme, children });
|
|
11
|
+
}
|
|
12
|
+
function useTheme() {
|
|
13
|
+
return useContext(ThemeContext);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
ThemeProvider,
|
|
18
|
+
useTheme
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=chunk-OXRMGR72.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/theme-context.tsx"],"sourcesContent":["import { createContext, type ReactNode, useContext } from 'react';\nimport { PLAIN_THEME, type Theme } from '../ui/theme';\n\nconst ThemeContext = createContext<Theme>(PLAIN_THEME);\n\nexport function ThemeProvider({ theme, children }: { theme: Theme; children?: ReactNode }) {\n return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme(): Theme {\n return useContext(ThemeContext);\n}\n"],"mappings":";;;;;AAAA,SAAS,eAA+B,kBAAkB;AAMjD;AAHT,IAAM,eAAe,cAAqB,WAAW;AAE9C,SAAS,cAAc,EAAE,OAAO,SAAS,GAA2C;AACzF,SAAO,oBAAC,aAAa,UAAb,EAAsB,OAAO,OAAQ,UAAS;AACxD;AAEO,SAAS,WAAkB;AAChC,SAAO,WAAW,YAAY;AAChC;","names":[]}
|