@dmrlzz/davecode 0.0.0-stage → 0.4.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 +34 -2
- package/bin/davecode.js +2 -0
- package/dist/ChatApp-BO7QMBWD.js +1045 -0
- package/dist/ChatApp-BO7QMBWD.js.map +1 -0
- package/dist/RunnerView-A7JSPUW5.js +18 -0
- package/dist/RunnerView-A7JSPUW5.js.map +1 -0
- package/dist/account-UGNLJPQ4.js +23 -0
- package/dist/account-UGNLJPQ4.js.map +1 -0
- package/dist/accounts-NM7IWP53.js +37 -0
- package/dist/accounts-NM7IWP53.js.map +1 -0
- package/dist/budget-INAW6FXD.js +115 -0
- package/dist/budget-INAW6FXD.js.map +1 -0
- package/dist/chat-U4QJQ7M6.js +359 -0
- package/dist/chat-U4QJQ7M6.js.map +1 -0
- package/dist/chunk-3Q2KCXLN.js +9608 -0
- package/dist/chunk-3Q2KCXLN.js.map +1 -0
- package/dist/chunk-5LKOJBUL.js +20 -0
- package/dist/chunk-5LKOJBUL.js.map +1 -0
- package/dist/chunk-7XVXEO7U.js +23 -0
- package/dist/chunk-7XVXEO7U.js.map +1 -0
- package/dist/chunk-CWBVNS3L.js +731 -0
- package/dist/chunk-CWBVNS3L.js.map +1 -0
- package/dist/chunk-DZ2FDABF.js +1614 -0
- package/dist/chunk-DZ2FDABF.js.map +1 -0
- package/dist/chunk-FRFSJMN4.js +79 -0
- package/dist/chunk-FRFSJMN4.js.map +1 -0
- package/dist/chunk-GE22VBCY.js +200 -0
- package/dist/chunk-GE22VBCY.js.map +1 -0
- package/dist/chunk-GHDOUII5.js +144 -0
- package/dist/chunk-GHDOUII5.js.map +1 -0
- package/dist/chunk-GZJ6JSO7.js +213 -0
- package/dist/chunk-GZJ6JSO7.js.map +1 -0
- package/dist/chunk-IC3TDONV.js +12 -0
- package/dist/chunk-IC3TDONV.js.map +1 -0
- package/dist/chunk-MC7RIPL7.js +116 -0
- package/dist/chunk-MC7RIPL7.js.map +1 -0
- package/dist/chunk-MOK5D3XJ.js +114 -0
- package/dist/chunk-MOK5D3XJ.js.map +1 -0
- package/dist/chunk-PGPXE6LF.js +20 -0
- package/dist/chunk-PGPXE6LF.js.map +1 -0
- package/dist/chunk-QA3O6RA2.js +81 -0
- package/dist/chunk-QA3O6RA2.js.map +1 -0
- package/dist/chunk-ROIUTNQV.js +20 -0
- package/dist/chunk-ROIUTNQV.js.map +1 -0
- package/dist/chunk-SF5ZEAP4.js +105 -0
- package/dist/chunk-SF5ZEAP4.js.map +1 -0
- package/dist/chunk-Z6NQDJEN.js +180 -0
- package/dist/chunk-Z6NQDJEN.js.map +1 -0
- package/dist/config-YEAPBMBX.js +158 -0
- package/dist/config-YEAPBMBX.js.map +1 -0
- package/dist/dashboard/assets/Accounts-CeVyX4a3.js +1 -0
- package/dist/dashboard/assets/Badge-CGY6DgGx.js +55 -0
- package/dist/dashboard/assets/Callout-DauCk_cW.js +1 -0
- package/dist/dashboard/assets/Field-OXMnT9Tb.js +1 -0
- package/dist/dashboard/assets/Goals-DH-KDh8M.js +1 -0
- package/dist/dashboard/assets/Library-C15szLjZ.js +1 -0
- package/dist/dashboard/assets/Markdown-Xhao3sgL.js +69 -0
- package/dist/dashboard/assets/Panel-Du40pM7n.js +1 -0
- package/dist/dashboard/assets/RoutesPage-Cj1OelGO.js +4 -0
- package/dist/dashboard/assets/Runner-BK1-ZF7h.js +1 -0
- package/dist/dashboard/assets/Settings-C1nzQQhe.js +1 -0
- package/dist/dashboard/assets/Switch-bwtIJ00H.js +1 -0
- package/dist/dashboard/assets/Tasks-x4s8IiPG.js +2 -0
- package/dist/dashboard/assets/Traffic-CV7TaTid.js +1 -0
- package/dist/dashboard/assets/Workspace-DAL-MBc0.js +1 -0
- package/dist/dashboard/assets/arrow-up-goM9r-I9.js +1 -0
- package/dist/dashboard/assets/forms-DtpfI65-.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-BCi-Vuvh.js +104 -0
- package/dist/dashboard/assets/index-CcJFhc98.css +2 -0
- package/dist/dashboard/assets/task-errors-B2pgjUGN.js +1 -0
- package/dist/dashboard/assets/trash-BVojETtF.js +1 -0
- package/dist/dashboard/favicon.svg +1 -0
- package/dist/dashboard/github.svg +1 -0
- package/dist/dashboard/index.html +31 -0
- package/dist/dist-RRQGUBL3.js +379 -0
- package/dist/dist-RRQGUBL3.js.map +1 -0
- package/dist/doctor-MQYK44S2.js +279 -0
- package/dist/doctor-MQYK44S2.js.map +1 -0
- package/dist/goal-6NUX4AIT.js +302 -0
- package/dist/goal-6NUX4AIT.js.map +1 -0
- package/dist/index.js +399 -0
- package/dist/index.js.map +1 -0
- package/dist/init-AXYIWAB3.js +194 -0
- package/dist/init-AXYIWAB3.js.map +1 -0
- package/dist/prompts-TZ7UC42M.js +219 -0
- package/dist/prompts-TZ7UC42M.js.map +1 -0
- package/dist/run-QRTLHFB3.js +376 -0
- package/dist/run-QRTLHFB3.js.map +1 -0
- package/dist/runner-state-QFU3LJVJ.js +17 -0
- package/dist/runner-state-QFU3LJVJ.js.map +1 -0
- package/dist/runtime.js +18 -0
- package/dist/runtime.js.map +1 -0
- package/dist/start-O5I5TSO2.js +285 -0
- package/dist/start-O5I5TSO2.js.map +1 -0
- package/dist/status-LEA3RVUX.js +25 -0
- package/dist/status-LEA3RVUX.js.map +1 -0
- package/dist/tasks-XOTVICVZ.js +184 -0
- package/dist/tasks-XOTVICVZ.js.map +1 -0
- package/package.json +73 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/RunnerView.tsx"],"sourcesContent":["import type { DaveEvent, RunnerState } from '@davecode/core';\nimport { Box, Text, useApp, useInput, useWindowSize } from 'ink';\nimport { useEffect, useReducer, useRef, useState } from 'react';\nimport { bar, truncate } from '../ui/format';\nimport type { Theme } from '../ui/theme';\nimport { Spinner } from './components/Spinner';\nimport {\n isTerminal,\n type LogLine,\n PIPELINE,\n pipelineIndex,\n type RunnerViewState,\n runnerReducer,\n} from './runner-state';\nimport { ThemeProvider, useTheme } from './theme-context';\n\nconst IDLE_STATES: readonly RunnerState[] = ['idle', 'paused', 'stopped', 'error'];\n\n/** `selecting → preparing → implementing → …` with the current step highlighted. */\nexport function pipelineText(theme: Theme, state: RunnerState, repairCycle?: number): string {\n const current = pipelineIndex(state);\n const arrow = theme.dim(` ${theme.glyph.arrow} `);\n return PIPELINE.map((step, i) => {\n let label: string = step;\n if (step === 'validating' && state === 'repairing') {\n label = `repairing${repairCycle ? ` #${repairCycle}` : ''}`;\n }\n if (current === -1) return theme.dim(label);\n if (i < current) return theme.ok(label);\n if (i === current) return theme.bold(theme.accent(label));\n return theme.dim(label);\n }).join(arrow);\n}\n\nfunction logColor(theme: Theme, line: LogLine): (text: string) => string {\n if (line.level === 'error') return theme.error;\n if (line.level === 'warn') return theme.warn;\n if (line.level === 'debug') return theme.dim;\n return (t) => t;\n}\n\nexport function logText(theme: Theme, line: LogLine, width: number): string {\n const time = new Date(line.ts).toTimeString().slice(0, 8);\n const prefix = `${time} `;\n return `${theme.dim(prefix)}${logColor(theme, line)(truncate(line.message.replace(/\\s*\\n\\s*/g, ' '), Math.max(10, width - prefix.length)))}`;\n}\n\nexport interface RunnerViewProps {\n state: RunnerViewState;\n maxRepairCycles: number;\n projectName: string;\n width: number;\n /** Number of log lines to show. */\n logLines: number;\n notice?: string;\n}\n\n/** Stateless runner dashboard: state machine position, current task, repair cycle, log tail. */\nexport function RunnerView({\n state,\n maxRepairCycles,\n projectName,\n width,\n logLines,\n notice,\n}: RunnerViewProps) {\n const theme = useTheme();\n const { status } = state;\n const task = status.taskId ? state.tasks[status.taskId] : undefined;\n const busy = !IDLE_STATES.includes(status.state);\n const cycle = status.repairCycle ?? 0;\n const stateColor =\n status.state === 'error'\n ? theme.ink.error\n : status.state === 'paused' || status.state === 'stopped'\n ? theme.ink.warn\n : theme.ink.accent;\n const rule = theme.dim(theme.glyph.rule.repeat(Math.max(10, width)));\n const tail = state.logs.slice(-logLines);\n\n return (\n <Box flexDirection=\"column\" width={width}>\n <Box justifyContent=\"space-between\">\n <Text>\n <Text bold color={theme.ink.accent}>\n DaveCode runner\n </Text>\n <Text dimColor={theme.ink.dim}> · {projectName}</Text>\n </Text>\n <Text dimColor={theme.ink.dim}>\n {state.done.success} done · {state.done.failed} failed\n </Text>\n </Box>\n <Box marginTop={1}>\n {busy ? <Spinner /> : <Text color={stateColor}>{theme.glyph.dot}</Text>}\n <Text bold color={stateColor}>\n {' '}\n {status.state}\n </Text>\n {status.startedAt ? (\n <Text dimColor={theme.ink.dim}> since {status.startedAt.slice(11, 19)}</Text>\n ) : null}\n </Box>\n <Text wrap=\"truncate-end\">{pipelineText(theme, status.state, status.repairCycle)}</Text>\n <Box marginTop={1} flexDirection=\"column\">\n <Text wrap=\"truncate-end\">\n <Text dimColor={theme.ink.dim}>{'task '}</Text>\n {task ? (\n <Text>\n <Text bold>{task.id}</Text> {task.title}\n {task.attempts ? (\n <Text dimColor={theme.ink.dim}> · attempt {task.attempts}</Text>\n ) : null}\n </Text>\n ) : status.taskId ? (\n <Text bold>{status.taskId}</Text>\n ) : (\n <Text dimColor={theme.ink.dim}>none</Text>\n )}\n </Text>\n <Text>\n <Text dimColor={theme.ink.dim}>{'repair '}</Text>\n {bar(\n theme,\n maxRepairCycles > 0 ? cycle / maxRepairCycles : 0,\n Math.max(1, maxRepairCycles * 2),\n )}{' '}\n {cycle}/{maxRepairCycles}\n </Text>\n {status.lastError ? (\n <Text color={theme.ink.error} wrap=\"truncate-end\">\n {theme.glyph.fail} {status.lastError}\n </Text>\n ) : null}\n </Box>\n <Box marginTop={1} flexDirection=\"column\">\n <Text>{rule}</Text>\n {tail.length === 0 ? (\n <Text dimColor={theme.ink.dim}>waiting for runner output…</Text>\n ) : null}\n {tail.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: log lines are append-only\n <Text key={`${line.ts}-${i}`} wrap=\"truncate-end\">\n {logText(theme, line, width)}\n </Text>\n ))}\n <Text>{rule}</Text>\n </Box>\n <Text dimColor={theme.ink.dim}>{notice ?? 'q or Ctrl+C stop safely · p pause/resume'}</Text>\n </Box>\n );\n}\n\nexport interface RunnerAppProps {\n theme: Theme;\n initial: RunnerViewState;\n /** Subscribe to events (in-process bus or `/api/events`); returns an unsubscribe function. */\n subscribe: (onEvent: (event: DaveEvent) => void) => () => void;\n maxRepairCycles: number;\n projectName: string;\n stop: () => Promise<void>;\n pause?: () => void | Promise<void>;\n resume?: () => void | Promise<void>;\n}\n\nfunction RunnerScreen(props: RunnerAppProps) {\n const { exit } = useApp();\n const { columns, rows } = useWindowSize();\n const [state, dispatch] = useReducer(runnerReducer, props.initial);\n const [notice, setNotice] = useState<string | undefined>();\n const stopping = useRef(false);\n const sawActive = useRef(!isTerminal(props.initial.status.state));\n\n useEffect(() => props.subscribe(dispatch), [props.subscribe]);\n\n // Leave once the runner comes to rest on its own (fatal error) or after our stop().\n useEffect(() => {\n if (!isTerminal(state.status.state)) {\n sawActive.current = true;\n return;\n }\n if (sawActive.current || stopping.current) {\n const timer = setTimeout(() => exit(), 50);\n return () => clearTimeout(timer);\n }\n }, [state.status.state, exit]);\n\n const requestStop = () => {\n if (stopping.current) {\n exit();\n return;\n }\n stopping.current = true;\n setNotice('Stopping safely (the task goes back to PENDING)… press again to quit now');\n props.stop().then(\n () => exit(),\n (err: unknown) => {\n setNotice(`stop failed: ${err instanceof Error ? err.message : String(err)}`);\n setTimeout(() => exit(), 500);\n },\n );\n };\n\n useInput((input, key) => {\n if ((key.ctrl && input === 'c') || input === 'q') return requestStop();\n if (input === 'p') {\n if (state.status.state === 'paused') void props.resume?.();\n else void props.pause?.();\n }\n });\n\n const width = Math.max(30, columns);\n const logLines = Math.max(3, Math.min(30, rows - 14));\n return (\n <RunnerView\n state={state}\n maxRepairCycles={props.maxRepairCycles}\n projectName={props.projectName}\n width={width}\n logLines={logLines}\n {...(notice ? { notice } : {})}\n />\n );\n}\n\n/** Live runner view. Render with `exitOnCtrlC: false` so Ctrl+C stops the runner safely. */\nexport function RunnerApp(props: RunnerAppProps) {\n return (\n <ThemeProvider theme={props.theme}>\n <RunnerScreen {...props} />\n </ThemeProvider>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CliError,
|
|
3
|
+
EXIT
|
|
4
|
+
} from "./chunk-7XVXEO7U.js";
|
|
5
|
+
import {
|
|
6
|
+
AccountClient,
|
|
7
|
+
AccountStore,
|
|
8
|
+
accountService,
|
|
9
|
+
startAccountLogin
|
|
10
|
+
} from "./chunk-3Q2KCXLN.js";
|
|
11
|
+
|
|
12
|
+
// src/lib/browser.ts
|
|
13
|
+
import { spawn } from "child_process";
|
|
14
|
+
var detached = (command, args) => new Promise((resolve) => {
|
|
15
|
+
const child = spawn(command, args, { detached: true, stdio: "ignore", windowsHide: true });
|
|
16
|
+
child.once("error", () => resolve(false));
|
|
17
|
+
child.once("spawn", () => {
|
|
18
|
+
child.unref();
|
|
19
|
+
resolve(true);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
async function openBrowser(url, platform = process.platform, launch = detached) {
|
|
23
|
+
const parsed = new URL(url);
|
|
24
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false;
|
|
25
|
+
const target = parsed.toString();
|
|
26
|
+
if (platform === "win32") return launch("rundll32", ["url.dll,FileProtocolHandler", target]);
|
|
27
|
+
if (platform === "darwin") return launch("open", [target]);
|
|
28
|
+
return launch("xdg-open", [target]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/commands/account.ts
|
|
32
|
+
function serviceFor(ctx, deps) {
|
|
33
|
+
return deps.service ?? accountService(ctx.env);
|
|
34
|
+
}
|
|
35
|
+
function clientFor(service, deps) {
|
|
36
|
+
return new AccountClient(service, deps.fetch ? { fetch: deps.fetch } : {});
|
|
37
|
+
}
|
|
38
|
+
function describeUser(session) {
|
|
39
|
+
return session.user.email ?? session.user.name ?? session.user.id;
|
|
40
|
+
}
|
|
41
|
+
async function currentAccount(ctx, deps = {}) {
|
|
42
|
+
const service = serviceFor(ctx, deps);
|
|
43
|
+
if (!service) return void 0;
|
|
44
|
+
return new AccountStore(ctx.home, ctx.env).current(clientFor(service, deps));
|
|
45
|
+
}
|
|
46
|
+
function accountsEnabled(ctx, deps = {}) {
|
|
47
|
+
return serviceFor(ctx, deps) !== void 0;
|
|
48
|
+
}
|
|
49
|
+
async function signIn(ctx, deps = {}, announce) {
|
|
50
|
+
const service = serviceFor(ctx, deps);
|
|
51
|
+
if (!service) {
|
|
52
|
+
throw new CliError("DaveCode accounts are not available in this build");
|
|
53
|
+
}
|
|
54
|
+
const { theme } = ctx;
|
|
55
|
+
const login = await startAccountLogin({ client: clientFor(service, deps) });
|
|
56
|
+
const opened = await (deps.open ?? openBrowser)(login.url).catch(() => false);
|
|
57
|
+
if (announce) announce(login.url, opened);
|
|
58
|
+
else {
|
|
59
|
+
ctx.out(
|
|
60
|
+
opened ? `${theme.accent(theme.glyph.arrow)} Opened your browser to sign in.` : `${theme.accent(theme.glyph.arrow)} Open this link to sign in:`
|
|
61
|
+
);
|
|
62
|
+
ctx.out(theme.dim(` ${login.url}`));
|
|
63
|
+
ctx.out(theme.dim(" Waiting for you to finish in the browser. Ctrl+C cancels."));
|
|
64
|
+
}
|
|
65
|
+
const cancel = () => void login.close();
|
|
66
|
+
process.once("SIGINT", cancel);
|
|
67
|
+
try {
|
|
68
|
+
const session = await login.session;
|
|
69
|
+
new AccountStore(ctx.home, ctx.env).save(session);
|
|
70
|
+
return session;
|
|
71
|
+
} finally {
|
|
72
|
+
process.off("SIGINT", cancel);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function loginCommand(ctx, deps = {}) {
|
|
76
|
+
const session = await signIn(ctx, deps);
|
|
77
|
+
ctx.out(
|
|
78
|
+
`${ctx.theme.ok(ctx.theme.glyph.ok)} Signed in as ${ctx.theme.bold(describeUser(session))}`
|
|
79
|
+
);
|
|
80
|
+
return EXIT.ok;
|
|
81
|
+
}
|
|
82
|
+
async function logoutCommand(ctx, deps = {}) {
|
|
83
|
+
const store = new AccountStore(ctx.home, ctx.env);
|
|
84
|
+
const session = store.load();
|
|
85
|
+
const service = serviceFor(ctx, deps);
|
|
86
|
+
if (session && service) await clientFor(service, deps).signOut(session.accessToken);
|
|
87
|
+
store.clear();
|
|
88
|
+
ctx.out(session ? "Signed out." : "You were not signed in.");
|
|
89
|
+
return EXIT.ok;
|
|
90
|
+
}
|
|
91
|
+
async function accountCommand(ctx, deps = {}) {
|
|
92
|
+
const session = await currentAccount(ctx, deps);
|
|
93
|
+
if (ctx.json) {
|
|
94
|
+
ctx.out(JSON.stringify({ signedIn: Boolean(session), user: session?.user ?? null }));
|
|
95
|
+
return session ? EXIT.ok : EXIT.failure;
|
|
96
|
+
}
|
|
97
|
+
if (!session) {
|
|
98
|
+
ctx.out(`Not signed in. Run ${ctx.theme.accent("davecode login")}.`);
|
|
99
|
+
return EXIT.failure;
|
|
100
|
+
}
|
|
101
|
+
const via = session.user.provider ? ctx.theme.dim(` (${session.user.provider})`) : "";
|
|
102
|
+
ctx.out(`Signed in as ${ctx.theme.bold(describeUser(session))}${via}`);
|
|
103
|
+
return EXIT.ok;
|
|
104
|
+
}
|
|
105
|
+
function chatAccountActions(ctx, deps = {}) {
|
|
106
|
+
if (!accountsEnabled(ctx, deps)) return void 0;
|
|
107
|
+
return {
|
|
108
|
+
status: async () => {
|
|
109
|
+
const session = await currentAccount(ctx, deps);
|
|
110
|
+
return session ? `Signed in as ${describeUser(session)}` : "Not signed in. Type /login.";
|
|
111
|
+
},
|
|
112
|
+
login: async (notify) => {
|
|
113
|
+
const session = await signIn(
|
|
114
|
+
ctx,
|
|
115
|
+
deps,
|
|
116
|
+
(url, opened) => notify(
|
|
117
|
+
`${opened ? "Opened your browser to sign in." : "Open this link to sign in:"}
|
|
118
|
+
${url}`
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
return `Signed in as ${describeUser(session)}`;
|
|
122
|
+
},
|
|
123
|
+
logout: async () => {
|
|
124
|
+
const store = new AccountStore(ctx.home, ctx.env);
|
|
125
|
+
const session = store.load();
|
|
126
|
+
const service = serviceFor(ctx, deps);
|
|
127
|
+
if (session && service) await clientFor(service, deps).signOut(session.accessToken);
|
|
128
|
+
store.clear();
|
|
129
|
+
return session ? "Signed out. Type /login to sign in again." : "You were not signed in.";
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export {
|
|
135
|
+
describeUser,
|
|
136
|
+
currentAccount,
|
|
137
|
+
accountsEnabled,
|
|
138
|
+
signIn,
|
|
139
|
+
loginCommand,
|
|
140
|
+
logoutCommand,
|
|
141
|
+
accountCommand,
|
|
142
|
+
chatAccountActions
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=chunk-GHDOUII5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/browser.ts","../src/commands/account.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\n\nexport type Launcher = (command: string, args: string[]) => Promise<boolean>;\n\nconst detached: Launcher = (command, args) =>\n new Promise((resolve) => {\n const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsHide: true });\n child.once('error', () => resolve(false));\n child.once('spawn', () => {\n child.unref();\n resolve(true);\n });\n });\n\n/**\n * Open an http(s) URL in the default browser. Returns false when nothing could be launched, so\n * the caller can print the URL instead.\n */\nexport async function openBrowser(\n url: string,\n platform: NodeJS.Platform = process.platform,\n launch: Launcher = detached,\n): Promise<boolean> {\n const parsed = new URL(url);\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return false;\n const target = parsed.toString();\n // No shell is involved, so the URL is never interpreted as a command.\n if (platform === 'win32') return launch('rundll32', ['url.dll,FileProtocolHandler', target]);\n if (platform === 'darwin') return launch('open', [target]);\n return launch('xdg-open', [target]);\n}\n","import {\n AccountClient,\n type AccountServiceConfig,\n type AccountSession,\n AccountStore,\n accountService,\n startAccountLogin,\n} from '@davecode/core';\nimport type { CliContext } from '../context';\nimport { CliError, EXIT } from '../errors';\nimport { openBrowser } from '../lib/browser';\n\nexport interface AccountDeps {\n service?: AccountServiceConfig;\n fetch?: typeof fetch;\n open?: (url: string) => Promise<boolean>;\n}\n\nfunction serviceFor(ctx: CliContext, deps: AccountDeps): AccountServiceConfig | undefined {\n return deps.service ?? accountService(ctx.env);\n}\n\nfunction clientFor(service: AccountServiceConfig, deps: AccountDeps): AccountClient {\n return new AccountClient(service, deps.fetch ? { fetch: deps.fetch } : {});\n}\n\nexport function describeUser(session: AccountSession): string {\n return session.user.email ?? session.user.name ?? session.user.id;\n}\n\n/** The signed-in session (refreshed when needed), or undefined. */\nexport async function currentAccount(\n ctx: CliContext,\n deps: AccountDeps = {},\n): Promise<AccountSession | undefined> {\n const service = serviceFor(ctx, deps);\n if (!service) return undefined;\n return new AccountStore(ctx.home, ctx.env).current(clientFor(service, deps));\n}\n\n/** Whether this build signs in to DaveCode accounts at all (it needs a configured service). */\nexport function accountsEnabled(ctx: CliContext, deps: AccountDeps = {}): boolean {\n return serviceFor(ctx, deps) !== undefined;\n}\n\n/**\n * Open the browser sign-in page and wait for it to finish. Prints the URL too, for machines\n * where no browser can be launched (SSH, containers).\n */\nexport async function signIn(\n ctx: CliContext,\n deps: AccountDeps = {},\n announce?: (url: string, opened: boolean) => void,\n): Promise<AccountSession> {\n const service = serviceFor(ctx, deps);\n if (!service) {\n throw new CliError('DaveCode accounts are not available in this build');\n }\n const { theme } = ctx;\n const login = await startAccountLogin({ client: clientFor(service, deps) });\n const opened = await (deps.open ?? openBrowser)(login.url).catch(() => false);\n if (announce) announce(login.url, opened);\n else {\n ctx.out(\n opened\n ? `${theme.accent(theme.glyph.arrow)} Opened your browser to sign in.`\n : `${theme.accent(theme.glyph.arrow)} Open this link to sign in:`,\n );\n ctx.out(theme.dim(` ${login.url}`));\n ctx.out(theme.dim(' Waiting for you to finish in the browser. Ctrl+C cancels.'));\n }\n const cancel = () => void login.close();\n process.once('SIGINT', cancel);\n try {\n const session = await login.session;\n new AccountStore(ctx.home, ctx.env).save(session);\n return session;\n } finally {\n process.off('SIGINT', cancel);\n }\n}\n\nexport async function loginCommand(ctx: CliContext, deps: AccountDeps = {}): Promise<number> {\n const session = await signIn(ctx, deps);\n ctx.out(\n `${ctx.theme.ok(ctx.theme.glyph.ok)} Signed in as ${ctx.theme.bold(describeUser(session))}`,\n );\n return EXIT.ok;\n}\n\nexport async function logoutCommand(ctx: CliContext, deps: AccountDeps = {}): Promise<number> {\n const store = new AccountStore(ctx.home, ctx.env);\n const session = store.load();\n const service = serviceFor(ctx, deps);\n if (session && service) await clientFor(service, deps).signOut(session.accessToken);\n store.clear();\n ctx.out(session ? 'Signed out.' : 'You were not signed in.');\n return EXIT.ok;\n}\n\nexport async function accountCommand(ctx: CliContext, deps: AccountDeps = {}): Promise<number> {\n const session = await currentAccount(ctx, deps);\n if (ctx.json) {\n ctx.out(JSON.stringify({ signedIn: Boolean(session), user: session?.user ?? null }));\n return session ? EXIT.ok : EXIT.failure;\n }\n if (!session) {\n ctx.out(`Not signed in. Run ${ctx.theme.accent('davecode login')}.`);\n return EXIT.failure;\n }\n const via = session.user.provider ? ctx.theme.dim(` (${session.user.provider})`) : '';\n ctx.out(`Signed in as ${ctx.theme.bold(describeUser(session))}${via}`);\n return EXIT.ok;\n}\n\n/** Account actions for the chat's /login, /logout and /account (they return text, never print). */\nexport interface ChatAccountActions {\n status(): Promise<string>;\n login(notify: (text: string) => void): Promise<string>;\n logout(): Promise<string>;\n}\n\nexport function chatAccountActions(\n ctx: CliContext,\n deps: AccountDeps = {},\n): ChatAccountActions | undefined {\n if (!accountsEnabled(ctx, deps)) return undefined;\n return {\n status: async () => {\n const session = await currentAccount(ctx, deps);\n return session ? `Signed in as ${describeUser(session)}` : 'Not signed in. Type /login.';\n },\n login: async (notify) => {\n const session = await signIn(ctx, deps, (url, opened) =>\n notify(\n `${opened ? 'Opened your browser to sign in.' : 'Open this link to sign in:'}\\n${url}`,\n ),\n );\n return `Signed in as ${describeUser(session)}`;\n },\n logout: async () => {\n const store = new AccountStore(ctx.home, ctx.env);\n const session = store.load();\n const service = serviceFor(ctx, deps);\n if (session && service) await clientFor(service, deps).signOut(session.accessToken);\n store.clear();\n return session ? 'Signed out. Type /login to sign in again.' : 'You were not signed in.';\n },\n };\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-QA3O6RA2.js";
|
|
13
|
+
import {
|
|
14
|
+
detectProjectRoot,
|
|
15
|
+
findGateway,
|
|
16
|
+
targetFor
|
|
17
|
+
} from "./chunk-DZ2FDABF.js";
|
|
18
|
+
import {
|
|
19
|
+
EXIT
|
|
20
|
+
} from "./chunk-7XVXEO7U.js";
|
|
21
|
+
import {
|
|
22
|
+
QUOTA_WINDOWS,
|
|
23
|
+
createEngine,
|
|
24
|
+
loadConfig
|
|
25
|
+
} from "./chunk-3Q2KCXLN.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-GZJ6JSO7.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 @@
|
|
|
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,116 @@
|
|
|
1
|
+
// src/ui/theme.ts
|
|
2
|
+
function detectColor(env, stream, flag = void 0) {
|
|
3
|
+
if (flag === false) return false;
|
|
4
|
+
if (env.NO_COLOR !== void 0 && env.NO_COLOR !== "") return false;
|
|
5
|
+
const force = env.FORCE_COLOR;
|
|
6
|
+
if (force !== void 0) return !["0", "false"].includes(force.trim().toLowerCase());
|
|
7
|
+
if (env.TERM === "dumb") return false;
|
|
8
|
+
return stream.isTTY === true;
|
|
9
|
+
}
|
|
10
|
+
function detectUnicode(env, platform = process.platform) {
|
|
11
|
+
if (env.DAVECODE_ASCII === "1") return false;
|
|
12
|
+
if (platform !== "win32") return env.TERM !== "linux";
|
|
13
|
+
return Boolean(env.WT_SESSION) || Boolean(env.TERMINUS_SUBLIME) || env.ConEmuTask === "{cmd::Cmder}" || env.TERM_PROGRAM === "vscode" || env.TERM_PROGRAM === "Terminus-Sublime" || env.TERM === "xterm-256color" || env.TERM === "alacritty" || env.TERMINAL_EMULATOR === "JetBrains-JediTerm" || Boolean(env.CI);
|
|
14
|
+
}
|
|
15
|
+
var UNICODE_GLYPHS = {
|
|
16
|
+
ok: "\u2713",
|
|
17
|
+
fail: "\u2717",
|
|
18
|
+
warn: "!",
|
|
19
|
+
info: "i",
|
|
20
|
+
bullet: "\u2022",
|
|
21
|
+
arrow: "\u2192",
|
|
22
|
+
dot: "\u25CF",
|
|
23
|
+
ellipsis: "\u2026",
|
|
24
|
+
prompt: "\u203A",
|
|
25
|
+
pending: "\u25CB",
|
|
26
|
+
running: "\u25D0",
|
|
27
|
+
success: "\u25CF",
|
|
28
|
+
failed: "\u2717",
|
|
29
|
+
blocked: "\u25CC",
|
|
30
|
+
tee: "\u251C\u2500",
|
|
31
|
+
elbow: "\u2514\u2500",
|
|
32
|
+
pipe: "\u2502 ",
|
|
33
|
+
barFull: "\u2588",
|
|
34
|
+
barEmpty: "\u2591",
|
|
35
|
+
rule: "\u2500"
|
|
36
|
+
};
|
|
37
|
+
var ASCII_GLYPHS = {
|
|
38
|
+
ok: "ok",
|
|
39
|
+
fail: "x",
|
|
40
|
+
warn: "!",
|
|
41
|
+
info: "i",
|
|
42
|
+
bullet: "*",
|
|
43
|
+
arrow: "->",
|
|
44
|
+
dot: "*",
|
|
45
|
+
ellipsis: "...",
|
|
46
|
+
prompt: ">",
|
|
47
|
+
pending: "o",
|
|
48
|
+
running: "~",
|
|
49
|
+
success: "*",
|
|
50
|
+
failed: "x",
|
|
51
|
+
blocked: ".",
|
|
52
|
+
tee: "|-",
|
|
53
|
+
elbow: "`-",
|
|
54
|
+
pipe: "| ",
|
|
55
|
+
barFull: "#",
|
|
56
|
+
barEmpty: ".",
|
|
57
|
+
rule: "-"
|
|
58
|
+
};
|
|
59
|
+
function sgr(open, close) {
|
|
60
|
+
return (text) => text === "" ? text : `\x1B[${open}m${text}\x1B[${close}m`;
|
|
61
|
+
}
|
|
62
|
+
var identity = (text) => text;
|
|
63
|
+
var THEME_MODES = ["dark", "light", "auto"];
|
|
64
|
+
var EMPHASIS = {
|
|
65
|
+
dark: { open: "1;97", ink: "whiteBright" },
|
|
66
|
+
light: { open: "1;30", ink: "black" },
|
|
67
|
+
auto: { open: "1", ink: void 0 }
|
|
68
|
+
};
|
|
69
|
+
function createTheme(options) {
|
|
70
|
+
const { color, unicode, mode = "auto" } = options;
|
|
71
|
+
const glyph = unicode ? UNICODE_GLYPHS : ASCII_GLYPHS;
|
|
72
|
+
if (!color) {
|
|
73
|
+
return {
|
|
74
|
+
color,
|
|
75
|
+
unicode,
|
|
76
|
+
glyph,
|
|
77
|
+
accent: identity,
|
|
78
|
+
ok: identity,
|
|
79
|
+
warn: identity,
|
|
80
|
+
error: identity,
|
|
81
|
+
dim: identity,
|
|
82
|
+
bold: identity,
|
|
83
|
+
ink: { accent: void 0, ok: void 0, warn: void 0, error: void 0, dim: false }
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
color,
|
|
88
|
+
unicode,
|
|
89
|
+
glyph,
|
|
90
|
+
// Monochrome by design: emphasis is bold black or white, never a hue. Colour is reserved
|
|
91
|
+
// for state (ok, warn, error).
|
|
92
|
+
accent: sgr(EMPHASIS[mode].open, "22;39"),
|
|
93
|
+
ok: sgr("32", "39"),
|
|
94
|
+
warn: sgr("33", "39"),
|
|
95
|
+
error: sgr("31", "39"),
|
|
96
|
+
dim: sgr("2", "22"),
|
|
97
|
+
bold: sgr("1", "22"),
|
|
98
|
+
ink: { accent: EMPHASIS[mode].ink, ok: "green", warn: "yellow", error: "red", dim: true }
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function themeFor(env, stream, colorFlag, platform = process.platform, mode = "auto") {
|
|
102
|
+
return createTheme({
|
|
103
|
+
color: detectColor(env, stream, colorFlag),
|
|
104
|
+
unicode: detectUnicode(env, platform),
|
|
105
|
+
mode
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
var PLAIN_THEME = createTheme({ color: false, unicode: true });
|
|
109
|
+
|
|
110
|
+
export {
|
|
111
|
+
THEME_MODES,
|
|
112
|
+
createTheme,
|
|
113
|
+
themeFor,
|
|
114
|
+
PLAIN_THEME
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=chunk-MC7RIPL7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/theme.ts"],"sourcesContent":["/**\n * Terminal styling: one accent colour plus semantic colours, with plain-text fallbacks for\n * `NO_COLOR`, `--no-color`, non-TTY output and terminals without Unicode glyphs.\n */\n\nexport interface ColorStream {\n isTTY?: boolean;\n}\n\n/**\n * Colour is on for TTYs unless `NO_COLOR` is set (any value) or `--no-color` was passed.\n * `FORCE_COLOR` (anything but `0`/`false`) turns it on even when piped.\n */\nexport function detectColor(\n env: NodeJS.ProcessEnv,\n stream: ColorStream,\n flag: boolean | undefined = undefined,\n): boolean {\n if (flag === false) return false;\n if (env.NO_COLOR !== undefined && env.NO_COLOR !== '') return false;\n const force = env.FORCE_COLOR;\n if (force !== undefined) return !['0', 'false'].includes(force.trim().toLowerCase());\n if (env.TERM === 'dumb') return false;\n return stream.isTTY === true;\n}\n\n/**\n * Heuristic from `is-unicode-supported`: every non-Windows terminal except the Linux console,\n * and the Windows terminals known to render box-drawing and braille glyphs.\n */\nexport function detectUnicode(\n env: NodeJS.ProcessEnv,\n platform: NodeJS.Platform = process.platform,\n): boolean {\n if (env.DAVECODE_ASCII === '1') return false;\n if (platform !== 'win32') return env.TERM !== 'linux';\n return (\n Boolean(env.WT_SESSION) ||\n Boolean(env.TERMINUS_SUBLIME) ||\n env.ConEmuTask === '{cmd::Cmder}' ||\n env.TERM_PROGRAM === 'vscode' ||\n env.TERM_PROGRAM === 'Terminus-Sublime' ||\n env.TERM === 'xterm-256color' ||\n env.TERM === 'alacritty' ||\n env.TERMINAL_EMULATOR === 'JetBrains-JediTerm' ||\n Boolean(env.CI)\n );\n}\n\nexport interface Glyphs {\n ok: string;\n fail: string;\n warn: string;\n info: string;\n bullet: string;\n arrow: string;\n dot: string;\n ellipsis: string;\n prompt: string;\n /** Task statuses. */\n pending: string;\n running: string;\n success: string;\n failed: string;\n blocked: string;\n /** Tree connectors (`tee` = has siblings below, `elbow` = last child). */\n tee: string;\n elbow: string;\n pipe: string;\n /** Quota bars. */\n barFull: string;\n barEmpty: string;\n /** Horizontal rule. */\n rule: string;\n}\n\nconst UNICODE_GLYPHS: Glyphs = {\n ok: '✓',\n fail: '✗',\n warn: '!',\n info: 'i',\n bullet: '•',\n arrow: '→',\n dot: '●',\n ellipsis: '…',\n prompt: '›',\n pending: '○',\n running: '◐',\n success: '●',\n failed: '✗',\n blocked: '◌',\n tee: '├─',\n elbow: '└─',\n pipe: '│ ',\n barFull: '█',\n barEmpty: '░',\n rule: '─',\n};\n\nconst ASCII_GLYPHS: Glyphs = {\n ok: 'ok',\n fail: 'x',\n warn: '!',\n info: 'i',\n bullet: '*',\n arrow: '->',\n dot: '*',\n ellipsis: '...',\n prompt: '>',\n pending: 'o',\n running: '~',\n success: '*',\n failed: 'x',\n blocked: '.',\n tee: '|-',\n elbow: '`-',\n pipe: '| ',\n barFull: '#',\n barEmpty: '.',\n rule: '-',\n};\n\ntype Style = (text: string) => string;\n\nexport interface Theme {\n color: boolean;\n unicode: boolean;\n glyph: Glyphs;\n /** Emphasis for headings, ids and the active element (bold, not a hue). */\n accent: Style;\n ok: Style;\n warn: Style;\n error: Style;\n /** Secondary information. */\n dim: Style;\n bold: Style;\n /** Ink `<Text color>` values (undefined when colour is off). */\n ink: {\n accent: string | undefined;\n ok: string | undefined;\n warn: string | undefined;\n error: string | undefined;\n dim: boolean;\n };\n}\n\nfunction sgr(open: string, close: string): Style {\n return (text) => (text === '' ? text : `\\u001b[${open}m${text}\\u001b[${close}m`);\n}\n\nconst identity: Style = (text) => text;\n\n/**\n * `dark` and `light` pin emphasis to bright white or black; `auto` uses the terminal's own\n * foreground, which reads on any background.\n */\nexport type ThemeMode = 'dark' | 'light' | 'auto';\nexport const THEME_MODES: readonly ThemeMode[] = ['dark', 'light', 'auto'];\n\nconst EMPHASIS: Record<ThemeMode, { open: string; ink: string | undefined }> = {\n dark: { open: '1;97', ink: 'whiteBright' },\n light: { open: '1;30', ink: 'black' },\n auto: { open: '1', ink: undefined },\n};\n\nexport function createTheme(options: {\n color: boolean;\n unicode: boolean;\n mode?: ThemeMode;\n}): Theme {\n const { color, unicode, mode = 'auto' } = options;\n const glyph = unicode ? UNICODE_GLYPHS : ASCII_GLYPHS;\n if (!color) {\n return {\n color,\n unicode,\n glyph,\n accent: identity,\n ok: identity,\n warn: identity,\n error: identity,\n dim: identity,\n bold: identity,\n ink: { accent: undefined, ok: undefined, warn: undefined, error: undefined, dim: false },\n };\n }\n return {\n color,\n unicode,\n glyph,\n // Monochrome by design: emphasis is bold black or white, never a hue. Colour is reserved\n // for state (ok, warn, error).\n accent: sgr(EMPHASIS[mode].open, '22;39'),\n ok: sgr('32', '39'),\n warn: sgr('33', '39'),\n error: sgr('31', '39'),\n dim: sgr('2', '22'),\n bold: sgr('1', '22'),\n ink: { accent: EMPHASIS[mode].ink, ok: 'green', warn: 'yellow', error: 'red', dim: true },\n };\n}\n\n/** Theme for a stream given the environment and the `--color/--no-color` flag. */\nexport function themeFor(\n env: NodeJS.ProcessEnv,\n stream: ColorStream,\n colorFlag?: boolean,\n platform: NodeJS.Platform = process.platform,\n mode: ThemeMode = 'auto',\n): Theme {\n return createTheme({\n color: detectColor(env, stream, colorFlag),\n unicode: detectUnicode(env, platform),\n mode,\n });\n}\n\n/** A theme with no styling, for tests and `--json`. */\nexport const PLAIN_THEME: Theme = createTheme({ color: false, unicode: true });\n"],"mappings":"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","names":[]}
|