@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,279 @@
|
|
|
1
|
+
import {
|
|
2
|
+
padEnd
|
|
3
|
+
} from "./chunk-SF5ZEAP4.js";
|
|
4
|
+
import {
|
|
5
|
+
printJson
|
|
6
|
+
} from "./chunk-QA3O6RA2.js";
|
|
7
|
+
import {
|
|
8
|
+
GatewayClient,
|
|
9
|
+
connectHost,
|
|
10
|
+
detectProjectRoot,
|
|
11
|
+
resolveDashboardDir,
|
|
12
|
+
which
|
|
13
|
+
} from "./chunk-DZ2FDABF.js";
|
|
14
|
+
import {
|
|
15
|
+
EXIT
|
|
16
|
+
} from "./chunk-7XVXEO7U.js";
|
|
17
|
+
import {
|
|
18
|
+
loadConfig,
|
|
19
|
+
openDatabase
|
|
20
|
+
} from "./chunk-3Q2KCXLN.js";
|
|
21
|
+
import "./chunk-MC7RIPL7.js";
|
|
22
|
+
|
|
23
|
+
// src/commands/doctor.ts
|
|
24
|
+
import { existsSync } from "fs";
|
|
25
|
+
import { mkdir, rm, writeFile } from "fs/promises";
|
|
26
|
+
import { createServer } from "net";
|
|
27
|
+
import { join } from "path";
|
|
28
|
+
var MIN_NODE = [22, 12];
|
|
29
|
+
function nodeVersionOk(version) {
|
|
30
|
+
const [major = 0, minor = 0] = version.replace(/^v/, "").split(".").map(Number);
|
|
31
|
+
return major > MIN_NODE[0] || major === MIN_NODE[0] && minor >= MIN_NODE[1];
|
|
32
|
+
}
|
|
33
|
+
async function runDoctor(input, probes) {
|
|
34
|
+
const results = [];
|
|
35
|
+
const add = (r) => results.push(r);
|
|
36
|
+
add(
|
|
37
|
+
nodeVersionOk(probes.nodeVersion) ? { id: "node", label: "Node.js", status: "ok", detail: probes.nodeVersion } : {
|
|
38
|
+
id: "node",
|
|
39
|
+
label: "Node.js",
|
|
40
|
+
status: "fail",
|
|
41
|
+
detail: `${probes.nodeVersion} (needs ${MIN_NODE.join(".")}+)`,
|
|
42
|
+
fix: "Install Node.js 22.12 or newer: https://nodejs.org"
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
let config;
|
|
46
|
+
try {
|
|
47
|
+
config = probes.loadConfig();
|
|
48
|
+
add({ id: "config", label: "Config", status: "ok", detail: "valid" });
|
|
49
|
+
} catch (err) {
|
|
50
|
+
add({
|
|
51
|
+
id: "config",
|
|
52
|
+
label: "Config",
|
|
53
|
+
status: "fail",
|
|
54
|
+
detail: err.message.split("\n").slice(0, 3).join(" "),
|
|
55
|
+
fix: "Fix the file named above; `davecode config path` lists every config location."
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
add({ id: "sqlite", label: "better-sqlite3", status: "ok", detail: await probes.loadSqlite() });
|
|
60
|
+
} catch (err) {
|
|
61
|
+
add({
|
|
62
|
+
id: "sqlite",
|
|
63
|
+
label: "better-sqlite3",
|
|
64
|
+
status: "fail",
|
|
65
|
+
detail: `cannot load: ${err.message.split("\n")[0]}`,
|
|
66
|
+
fix: "Rebuild the native module: pnpm rebuild better-sqlite3 (needs a C++ toolchain if no prebuilt binary matches your Node version)."
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
await probes.checkWritable(input.home);
|
|
71
|
+
add({ id: "home", label: "Home directory", status: "ok", detail: `${input.home} is writable` });
|
|
72
|
+
} catch (err) {
|
|
73
|
+
add({
|
|
74
|
+
id: "home",
|
|
75
|
+
label: "Home directory",
|
|
76
|
+
status: "fail",
|
|
77
|
+
detail: `${input.home} is not writable (${err.code ?? err.message})`,
|
|
78
|
+
fix: "Fix the permissions or point DaveCode elsewhere with --home <dir> / DAVECODE_HOME."
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const keyPath = join(input.home, "master.key");
|
|
82
|
+
if (input.env.DAVECODE_MASTER_KEY) {
|
|
83
|
+
add({
|
|
84
|
+
id: "master-key",
|
|
85
|
+
label: "Master key",
|
|
86
|
+
status: "ok",
|
|
87
|
+
detail: "from DAVECODE_MASTER_KEY"
|
|
88
|
+
});
|
|
89
|
+
} else if (probes.exists(keyPath)) {
|
|
90
|
+
add({ id: "master-key", label: "Master key", status: "ok", detail: keyPath });
|
|
91
|
+
} else {
|
|
92
|
+
add({
|
|
93
|
+
id: "master-key",
|
|
94
|
+
label: "Master key",
|
|
95
|
+
status: "warn",
|
|
96
|
+
detail: "not created yet",
|
|
97
|
+
fix: "It is generated on first use (davecode start or accounts add). Back it up: without it stored secrets cannot be decrypted."
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (config) {
|
|
101
|
+
const { host, port, authToken } = config.server;
|
|
102
|
+
const baseUrl = `http://${connectHost(host)}:${port}`;
|
|
103
|
+
if (!await probes.portInUse(host, port)) {
|
|
104
|
+
add({ id: "port", label: "Gateway port", status: "ok", detail: `${host}:${port} is free` });
|
|
105
|
+
} else {
|
|
106
|
+
const version = await probes.gatewayVersion(baseUrl, authToken);
|
|
107
|
+
add(
|
|
108
|
+
version ? {
|
|
109
|
+
id: "port",
|
|
110
|
+
label: "Gateway port",
|
|
111
|
+
status: "ok",
|
|
112
|
+
detail: `DaveCode ${version} is already running at ${baseUrl}`
|
|
113
|
+
} : {
|
|
114
|
+
id: "port",
|
|
115
|
+
label: "Gateway port",
|
|
116
|
+
status: "fail",
|
|
117
|
+
detail: `${host}:${port} is used by another program`,
|
|
118
|
+
fix: `Stop it, or use another port: davecode start --port ${port + 1} (or DAVECODE_PORT).`
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const tools = [
|
|
124
|
+
{
|
|
125
|
+
cmd: "git",
|
|
126
|
+
label: "git",
|
|
127
|
+
required: true,
|
|
128
|
+
why: "needed by the autonomous runner (task branches and merges)",
|
|
129
|
+
fix: "Install git: https://git-scm.com/downloads"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
cmd: "claude",
|
|
133
|
+
label: "Claude Code CLI",
|
|
134
|
+
required: false,
|
|
135
|
+
why: "only for claude-cli accounts",
|
|
136
|
+
fix: "npm install -g @anthropic-ai/claude-code"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
cmd: "codex",
|
|
140
|
+
label: "Codex CLI",
|
|
141
|
+
required: false,
|
|
142
|
+
why: "only for codex-cli accounts",
|
|
143
|
+
fix: "npm install -g @openai/codex"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
cmd: "gemini",
|
|
147
|
+
label: "Gemini CLI",
|
|
148
|
+
required: false,
|
|
149
|
+
why: "only for gemini-cli accounts",
|
|
150
|
+
fix: "npm install -g @google/gemini-cli"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
cmd: "gh",
|
|
154
|
+
label: "GitHub CLI",
|
|
155
|
+
required: false,
|
|
156
|
+
why: "optional, for opening pull requests",
|
|
157
|
+
fix: "Install gh: https://cli.github.com"
|
|
158
|
+
}
|
|
159
|
+
];
|
|
160
|
+
for (const tool of tools) {
|
|
161
|
+
const path = probes.which(tool.cmd);
|
|
162
|
+
add(
|
|
163
|
+
path ? { id: tool.cmd, label: tool.label, status: "ok", detail: path } : {
|
|
164
|
+
id: tool.cmd,
|
|
165
|
+
label: tool.label,
|
|
166
|
+
status: tool.required ? "fail" : "warn",
|
|
167
|
+
detail: `not found on PATH (${tool.why})`,
|
|
168
|
+
fix: tool.fix
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
const dashboard = probes.dashboardDir();
|
|
173
|
+
add(
|
|
174
|
+
dashboard ? { id: "dashboard", label: "Dashboard build", status: "ok", detail: dashboard } : {
|
|
175
|
+
id: "dashboard",
|
|
176
|
+
label: "Dashboard build",
|
|
177
|
+
status: "warn",
|
|
178
|
+
detail: "not built (the gateway still works without it)",
|
|
179
|
+
fix: "pnpm --filter @davecode/ui build"
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
return results;
|
|
183
|
+
}
|
|
184
|
+
function portInUse(host, port) {
|
|
185
|
+
return new Promise((resolve) => {
|
|
186
|
+
const server = createServer();
|
|
187
|
+
server.once("error", (err) => {
|
|
188
|
+
resolve(err.code === "EADDRINUSE" || err.code === "EACCES");
|
|
189
|
+
});
|
|
190
|
+
server.listen({ host, port, exclusive: true }, () => {
|
|
191
|
+
server.close(() => resolve(false));
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
function defaultProbes(ctx, projectRoot) {
|
|
196
|
+
return {
|
|
197
|
+
nodeVersion: process.version,
|
|
198
|
+
loadConfig: () => loadConfig({ home: ctx.home, env: ctx.env, ...projectRoot ? { projectRoot } : {} }),
|
|
199
|
+
async loadSqlite() {
|
|
200
|
+
const db = openDatabase(":memory:");
|
|
201
|
+
try {
|
|
202
|
+
const row = db.prepare("select sqlite_version() as v").get();
|
|
203
|
+
return `loads (SQLite ${row.v})`;
|
|
204
|
+
} finally {
|
|
205
|
+
db.close();
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
async checkWritable(dir) {
|
|
209
|
+
await mkdir(dir, { recursive: true });
|
|
210
|
+
const probe = join(dir, `.doctor-${process.pid}.tmp`);
|
|
211
|
+
await writeFile(probe, "ok");
|
|
212
|
+
await rm(probe, { force: true });
|
|
213
|
+
},
|
|
214
|
+
exists: existsSync,
|
|
215
|
+
portInUse,
|
|
216
|
+
async gatewayVersion(baseUrl, token) {
|
|
217
|
+
const client = new GatewayClient({ baseUrl, ...token ? { token } : {} });
|
|
218
|
+
try {
|
|
219
|
+
return (await client.health(800))?.version;
|
|
220
|
+
} catch {
|
|
221
|
+
return void 0;
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
which: (cmd) => which(cmd, ctx.env),
|
|
225
|
+
async reachable(url) {
|
|
226
|
+
try {
|
|
227
|
+
await fetch(url, { signal: AbortSignal.timeout(800) });
|
|
228
|
+
return true;
|
|
229
|
+
} catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
dashboardDir: () => resolveDashboardDir(ctx.env)
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function renderDoctor(ctx, results) {
|
|
237
|
+
const { theme } = ctx;
|
|
238
|
+
const lines = [theme.bold(theme.accent("DaveCode doctor")), ""];
|
|
239
|
+
const width = Math.max(...results.map((r) => r.label.length)) + 2;
|
|
240
|
+
for (const r of results) {
|
|
241
|
+
const mark = r.status === "ok" ? theme.ok(theme.glyph.ok) : r.status === "warn" ? theme.warn(theme.glyph.warn) : theme.error(theme.glyph.fail);
|
|
242
|
+
const detail = r.status === "ok" ? theme.dim(r.detail) : r.detail;
|
|
243
|
+
lines.push(` ${mark} ${padEnd(r.label, width)} ${detail}`);
|
|
244
|
+
if (r.fix && r.status !== "ok")
|
|
245
|
+
lines.push(` ${" ".repeat(width)}${theme.dim(`fix: ${r.fix}`)}`);
|
|
246
|
+
}
|
|
247
|
+
const count = (s) => results.filter((r) => r.status === s).length;
|
|
248
|
+
const fails = count("fail");
|
|
249
|
+
const warns = count("warn");
|
|
250
|
+
lines.push("");
|
|
251
|
+
lines.push(
|
|
252
|
+
[
|
|
253
|
+
theme.ok(`${count("ok")} ok`),
|
|
254
|
+
warns ? theme.warn(`${warns} warning${warns === 1 ? "" : "s"}`) : `${warns} warnings`,
|
|
255
|
+
fails ? theme.error(`${fails} failure${fails === 1 ? "" : "s"}`) : "0 failures"
|
|
256
|
+
].join(theme.dim(" \xB7 "))
|
|
257
|
+
);
|
|
258
|
+
return lines;
|
|
259
|
+
}
|
|
260
|
+
async function doctorCommand(ctx, probes) {
|
|
261
|
+
const projectRoot = await detectProjectRoot(ctx.cwd, ctx.home);
|
|
262
|
+
const merged = { ...defaultProbes(ctx, projectRoot), ...probes };
|
|
263
|
+
const results = await runDoctor({ home: ctx.home, env: ctx.env }, merged);
|
|
264
|
+
const failed = results.some((r) => r.status === "fail");
|
|
265
|
+
if (ctx.json) {
|
|
266
|
+
printJson(ctx, { ok: !failed, checks: results });
|
|
267
|
+
} else {
|
|
268
|
+
for (const line of renderDoctor(ctx, results)) ctx.out(line);
|
|
269
|
+
}
|
|
270
|
+
return failed ? EXIT.failure : EXIT.ok;
|
|
271
|
+
}
|
|
272
|
+
export {
|
|
273
|
+
defaultProbes,
|
|
274
|
+
doctorCommand,
|
|
275
|
+
nodeVersionOk,
|
|
276
|
+
renderDoctor,
|
|
277
|
+
runDoctor
|
|
278
|
+
};
|
|
279
|
+
//# sourceMappingURL=doctor-MQYK44S2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/doctor.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, rm, writeFile } from 'node:fs/promises';\nimport { createServer } from 'node:net';\nimport { join } from 'node:path';\nimport { type DaveConfig, loadConfig, openDatabase } from '@davecode/core';\nimport { connectHost, GatewayClient } from '../client';\nimport { type CliContext, printJson } from '../context';\nimport { EXIT } from '../errors';\nimport { which } from '../lib/proc';\nimport { detectProjectRoot, resolveDashboardDir } from '../runtime';\nimport { padEnd } from '../ui/format';\n\nexport type CheckStatus = 'ok' | 'warn' | 'fail';\n\nexport interface CheckResult {\n id: string;\n label: string;\n status: CheckStatus;\n detail: string;\n fix?: string;\n}\n\n/** Everything doctor touches outside its own logic, injectable for tests. */\nexport interface DoctorProbes {\n nodeVersion: string;\n loadConfig(): DaveConfig;\n /** Open an in-memory database; resolves with a detail string. */\n loadSqlite(): Promise<string>;\n /** Throws when `dir` cannot be created or written. */\n checkWritable(dir: string): Promise<void>;\n exists(path: string): boolean;\n portInUse(host: string, port: number): Promise<boolean>;\n /** Version of the DaveCode gateway answering on `baseUrl`, if any. */\n gatewayVersion(baseUrl: string, token?: string): Promise<string | undefined>;\n which(command: string): string | undefined;\n /** True when `url` answers with any HTTP response within the timeout. */\n reachable(url: string): Promise<boolean>;\n dashboardDir(): string | undefined;\n}\n\nexport interface DoctorInput {\n home: string;\n env: NodeJS.ProcessEnv;\n}\n\nconst MIN_NODE: [number, number] = [22, 12];\n\nexport function nodeVersionOk(version: string): boolean {\n const [major = 0, minor = 0] = version.replace(/^v/, '').split('.').map(Number);\n return major > MIN_NODE[0] || (major === MIN_NODE[0] && minor >= MIN_NODE[1]);\n}\n\nexport async function runDoctor(input: DoctorInput, probes: DoctorProbes): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n const add = (r: CheckResult) => results.push(r);\n\n add(\n nodeVersionOk(probes.nodeVersion)\n ? { id: 'node', label: 'Node.js', status: 'ok', detail: probes.nodeVersion }\n : {\n id: 'node',\n label: 'Node.js',\n status: 'fail',\n detail: `${probes.nodeVersion} (needs ${MIN_NODE.join('.')}+)`,\n fix: 'Install Node.js 22.12 or newer: https://nodejs.org',\n },\n );\n\n let config: DaveConfig | undefined;\n try {\n config = probes.loadConfig();\n add({ id: 'config', label: 'Config', status: 'ok', detail: 'valid' });\n } catch (err) {\n add({\n id: 'config',\n label: 'Config',\n status: 'fail',\n detail: (err as Error).message.split('\\n').slice(0, 3).join(' '),\n fix: 'Fix the file named above; `davecode config path` lists every config location.',\n });\n }\n\n try {\n add({ id: 'sqlite', label: 'better-sqlite3', status: 'ok', detail: await probes.loadSqlite() });\n } catch (err) {\n add({\n id: 'sqlite',\n label: 'better-sqlite3',\n status: 'fail',\n detail: `cannot load: ${(err as Error).message.split('\\n')[0]}`,\n fix: 'Rebuild the native module: pnpm rebuild better-sqlite3 (needs a C++ toolchain if no prebuilt binary matches your Node version).',\n });\n }\n\n try {\n await probes.checkWritable(input.home);\n add({ id: 'home', label: 'Home directory', status: 'ok', detail: `${input.home} is writable` });\n } catch (err) {\n add({\n id: 'home',\n label: 'Home directory',\n status: 'fail',\n detail: `${input.home} is not writable (${(err as NodeJS.ErrnoException).code ?? (err as Error).message})`,\n fix: 'Fix the permissions or point DaveCode elsewhere with --home <dir> / DAVECODE_HOME.',\n });\n }\n\n const keyPath = join(input.home, 'master.key');\n if (input.env.DAVECODE_MASTER_KEY) {\n add({\n id: 'master-key',\n label: 'Master key',\n status: 'ok',\n detail: 'from DAVECODE_MASTER_KEY',\n });\n } else if (probes.exists(keyPath)) {\n add({ id: 'master-key', label: 'Master key', status: 'ok', detail: keyPath });\n } else {\n add({\n id: 'master-key',\n label: 'Master key',\n status: 'warn',\n detail: 'not created yet',\n fix: 'It is generated on first use (davecode start or accounts add). Back it up: without it stored secrets cannot be decrypted.',\n });\n }\n\n if (config) {\n const { host, port, authToken } = config.server;\n const baseUrl = `http://${connectHost(host)}:${port}`;\n if (!(await probes.portInUse(host, port))) {\n add({ id: 'port', label: 'Gateway port', status: 'ok', detail: `${host}:${port} is free` });\n } else {\n const version = await probes.gatewayVersion(baseUrl, authToken);\n add(\n version\n ? {\n id: 'port',\n label: 'Gateway port',\n status: 'ok',\n detail: `DaveCode ${version} is already running at ${baseUrl}`,\n }\n : {\n id: 'port',\n label: 'Gateway port',\n status: 'fail',\n detail: `${host}:${port} is used by another program`,\n fix: `Stop it, or use another port: davecode start --port ${port + 1} (or DAVECODE_PORT).`,\n },\n );\n }\n }\n\n const tools: Array<{ cmd: string; label: string; required: boolean; why: string; fix: string }> =\n [\n {\n cmd: 'git',\n label: 'git',\n required: true,\n why: 'needed by the autonomous runner (task branches and merges)',\n fix: 'Install git: https://git-scm.com/downloads',\n },\n {\n cmd: 'claude',\n label: 'Claude Code CLI',\n required: false,\n why: 'only for claude-cli accounts',\n fix: 'npm install -g @anthropic-ai/claude-code',\n },\n {\n cmd: 'codex',\n label: 'Codex CLI',\n required: false,\n why: 'only for codex-cli accounts',\n fix: 'npm install -g @openai/codex',\n },\n {\n cmd: 'gemini',\n label: 'Gemini CLI',\n required: false,\n why: 'only for gemini-cli accounts',\n fix: 'npm install -g @google/gemini-cli',\n },\n {\n cmd: 'gh',\n label: 'GitHub CLI',\n required: false,\n why: 'optional, for opening pull requests',\n fix: 'Install gh: https://cli.github.com',\n },\n ];\n for (const tool of tools) {\n const path = probes.which(tool.cmd);\n add(\n path\n ? { id: tool.cmd, label: tool.label, status: 'ok', detail: path }\n : {\n id: tool.cmd,\n label: tool.label,\n status: tool.required ? 'fail' : 'warn',\n detail: `not found on PATH (${tool.why})`,\n fix: tool.fix,\n },\n );\n }\n\n const dashboard = probes.dashboardDir();\n add(\n dashboard\n ? { id: 'dashboard', label: 'Dashboard build', status: 'ok', detail: dashboard }\n : {\n id: 'dashboard',\n label: 'Dashboard build',\n status: 'warn',\n detail: 'not built (the gateway still works without it)',\n fix: 'pnpm --filter @davecode/ui build',\n },\n );\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Real probes\n// ---------------------------------------------------------------------------\n\nfunction portInUse(host: string, port: number): Promise<boolean> {\n return new Promise((resolve) => {\n const server = createServer();\n server.once('error', (err: NodeJS.ErrnoException) => {\n resolve(err.code === 'EADDRINUSE' || err.code === 'EACCES');\n });\n server.listen({ host, port, exclusive: true }, () => {\n server.close(() => resolve(false));\n });\n });\n}\n\nexport function defaultProbes(\n ctx: Pick<CliContext, 'home' | 'env'>,\n projectRoot?: string,\n): DoctorProbes {\n return {\n nodeVersion: process.version,\n loadConfig: () =>\n loadConfig({ home: ctx.home, env: ctx.env, ...(projectRoot ? { projectRoot } : {}) }),\n async loadSqlite() {\n const db = openDatabase(':memory:');\n try {\n const row = db.prepare('select sqlite_version() as v').get() as { v: string };\n return `loads (SQLite ${row.v})`;\n } finally {\n db.close();\n }\n },\n async checkWritable(dir) {\n await mkdir(dir, { recursive: true });\n const probe = join(dir, `.doctor-${process.pid}.tmp`);\n await writeFile(probe, 'ok');\n await rm(probe, { force: true });\n },\n exists: existsSync,\n portInUse,\n async gatewayVersion(baseUrl, token) {\n const client = new GatewayClient({ baseUrl, ...(token ? { token } : {}) });\n try {\n return (await client.health(800))?.version;\n } catch {\n return undefined;\n }\n },\n which: (cmd) => which(cmd, ctx.env),\n async reachable(url) {\n try {\n await fetch(url, { signal: AbortSignal.timeout(800) });\n return true;\n } catch {\n return false;\n }\n },\n dashboardDir: () => resolveDashboardDir(ctx.env),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Command\n// ---------------------------------------------------------------------------\n\nexport function renderDoctor(ctx: Pick<CliContext, 'theme'>, results: CheckResult[]): string[] {\n const { theme } = ctx;\n const lines = [theme.bold(theme.accent('DaveCode doctor')), ''];\n const width = Math.max(...results.map((r) => r.label.length)) + 2;\n for (const r of results) {\n const mark =\n r.status === 'ok'\n ? theme.ok(theme.glyph.ok)\n : r.status === 'warn'\n ? theme.warn(theme.glyph.warn)\n : theme.error(theme.glyph.fail);\n const detail = r.status === 'ok' ? theme.dim(r.detail) : r.detail;\n lines.push(` ${mark} ${padEnd(r.label, width)} ${detail}`);\n if (r.fix && r.status !== 'ok')\n lines.push(` ${' '.repeat(width)}${theme.dim(`fix: ${r.fix}`)}`);\n }\n const count = (s: CheckStatus) => results.filter((r) => r.status === s).length;\n const fails = count('fail');\n const warns = count('warn');\n lines.push('');\n lines.push(\n [\n theme.ok(`${count('ok')} ok`),\n warns ? theme.warn(`${warns} warning${warns === 1 ? '' : 's'}`) : `${warns} warnings`,\n fails ? theme.error(`${fails} failure${fails === 1 ? '' : 's'}`) : '0 failures',\n ].join(theme.dim(' · ')),\n );\n return lines;\n}\n\nexport async function doctorCommand(\n ctx: CliContext,\n probes?: Partial<DoctorProbes>,\n): Promise<number> {\n const projectRoot = await detectProjectRoot(ctx.cwd, ctx.home);\n const merged: DoctorProbes = { ...defaultProbes(ctx, projectRoot), ...probes };\n const results = await runDoctor({ home: ctx.home, env: ctx.env }, merged);\n const failed = results.some((r) => r.status === 'fail');\n if (ctx.json) {\n printJson(ctx, { ok: !failed, checks: results });\n } else {\n for (const line of renderDoctor(ctx, results)) ctx.out(line);\n }\n return failed ? EXIT.failure : EXIT.ok;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatDuration
|
|
3
|
+
} from "./chunk-SF5ZEAP4.js";
|
|
4
|
+
import {
|
|
5
|
+
printJson
|
|
6
|
+
} from "./chunk-QA3O6RA2.js";
|
|
7
|
+
import {
|
|
8
|
+
GatewayError,
|
|
9
|
+
detectProjectRoot,
|
|
10
|
+
findGateway,
|
|
11
|
+
wireProject
|
|
12
|
+
} from "./chunk-DZ2FDABF.js";
|
|
13
|
+
import {
|
|
14
|
+
CliError,
|
|
15
|
+
EXIT
|
|
16
|
+
} from "./chunk-7XVXEO7U.js";
|
|
17
|
+
import {
|
|
18
|
+
GoalStore,
|
|
19
|
+
createEngine,
|
|
20
|
+
isTerminalGoal,
|
|
21
|
+
loadConfig
|
|
22
|
+
} from "./chunk-3Q2KCXLN.js";
|
|
23
|
+
import "./chunk-MC7RIPL7.js";
|
|
24
|
+
|
|
25
|
+
// src/commands/goal.ts
|
|
26
|
+
import { existsSync } from "fs";
|
|
27
|
+
import { resolve } from "path";
|
|
28
|
+
async function projectRoot(ctx, project) {
|
|
29
|
+
const root = project ? resolve(ctx.cwd, project) : await detectProjectRoot(ctx.cwd, ctx.home);
|
|
30
|
+
if (!root || !existsSync(root)) {
|
|
31
|
+
throw new CliError("Not inside a project", {
|
|
32
|
+
hint: "Run goal mode inside a repository with a project brain (`git init` and `davecode init`)."
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return root;
|
|
36
|
+
}
|
|
37
|
+
function interruptSignal(deps) {
|
|
38
|
+
if (deps.interrupt) return { promise: deps.interrupt(), dispose: () => {
|
|
39
|
+
} };
|
|
40
|
+
let dispose = () => {
|
|
41
|
+
};
|
|
42
|
+
const promise = new Promise((done) => {
|
|
43
|
+
const handler = () => {
|
|
44
|
+
dispose();
|
|
45
|
+
done();
|
|
46
|
+
};
|
|
47
|
+
dispose = () => {
|
|
48
|
+
process.off("SIGINT", handler);
|
|
49
|
+
process.off("SIGTERM", handler);
|
|
50
|
+
};
|
|
51
|
+
process.on("SIGINT", handler);
|
|
52
|
+
process.on("SIGTERM", handler);
|
|
53
|
+
});
|
|
54
|
+
return { promise, dispose };
|
|
55
|
+
}
|
|
56
|
+
function explainGoalError(err) {
|
|
57
|
+
const code = err instanceof GatewayError ? err.code : err?.code;
|
|
58
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
59
|
+
if (code === "busy") {
|
|
60
|
+
return new CliError(message, {
|
|
61
|
+
hint: "Wait for the runner or the other goal to finish, or stop it from the dashboard."
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (code === "goals_unavailable") {
|
|
65
|
+
return new CliError("The running gateway has no project to run goals in", {
|
|
66
|
+
hint: "Restart `davecode start` inside this repository, or stop it so `davecode goal` runs in-process."
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return err;
|
|
70
|
+
}
|
|
71
|
+
var STATUS_GLYPH = {
|
|
72
|
+
SUCCESS: (t) => t.ok(t.glyph.success),
|
|
73
|
+
FAILED: (t) => t.error(t.glyph.failed),
|
|
74
|
+
IN_PROGRESS: (t) => t.accent(t.glyph.running),
|
|
75
|
+
PENDING: (t) => t.dim(t.glyph.pending)
|
|
76
|
+
};
|
|
77
|
+
function printEvent(ctx, goalId, event) {
|
|
78
|
+
if (event.type !== "goal.log" || event.goalId !== goalId || event.level === "debug") return;
|
|
79
|
+
const theme = ctx.errTheme;
|
|
80
|
+
const time = new Date(event.ts).toTimeString().slice(0, 8);
|
|
81
|
+
const who = event.role ? theme.accent(`[${event.role}]`) : theme.dim("[goal]");
|
|
82
|
+
const message = event.level === "error" ? theme.error(event.message) : event.level === "warn" ? theme.warn(event.message) : event.message;
|
|
83
|
+
ctx.err(`${theme.dim(time)} ${who} ${message}`);
|
|
84
|
+
}
|
|
85
|
+
function describeGoal(theme, goal) {
|
|
86
|
+
const lines = [];
|
|
87
|
+
const colour = goal.status === "succeeded" || goal.status === "planned" ? theme.ok : goal.status === "cancelled" || goal.status === "budget_exhausted" ? theme.warn : goal.status === "failed" ? theme.error : theme.accent;
|
|
88
|
+
lines.push(
|
|
89
|
+
`${theme.bold(goal.id)} ${colour(goal.status)} ${theme.dim(`round ${goal.round}/${goal.limits.maxRounds}`)}`
|
|
90
|
+
);
|
|
91
|
+
lines.push(` ${goal.objective}`);
|
|
92
|
+
lines.push(
|
|
93
|
+
theme.dim(
|
|
94
|
+
` tokens ${goal.limits.tokensUsed.toLocaleString("en-US")} / ${goal.limits.maxTokens.toLocaleString("en-US")} \xB7 tasks ${goal.tasks.length} / ${goal.limits.maxTasks}`
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
if (goal.tasks.length > 0) lines.push("");
|
|
98
|
+
for (const task of goal.tasks) {
|
|
99
|
+
const glyph = STATUS_GLYPH[task.status](theme);
|
|
100
|
+
lines.push(
|
|
101
|
+
`${glyph} ${theme.bold(task.id)} ${theme.dim(`${task.role}\xB7${task.kind}`)} ${task.title}`
|
|
102
|
+
);
|
|
103
|
+
if (task.artifact) lines.push(theme.dim(` ${task.artifact}`));
|
|
104
|
+
else if (task.status === "FAILED" && task.notes) {
|
|
105
|
+
lines.push(theme.dim(` ${task.notes.split("\n")[0]?.slice(0, 160) ?? ""}`));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (goal.summary) lines.push("", goal.summary.trim());
|
|
109
|
+
if (goal.lastError) lines.push("", theme.error(goal.lastError));
|
|
110
|
+
return lines;
|
|
111
|
+
}
|
|
112
|
+
function goalExitCode(goal) {
|
|
113
|
+
if (goal.status === "succeeded" || goal.status === "planned") return EXIT.ok;
|
|
114
|
+
if (goal.status === "cancelled") return 130;
|
|
115
|
+
return EXIT.failure;
|
|
116
|
+
}
|
|
117
|
+
function gatewayBackend(client, onEvent) {
|
|
118
|
+
const controller = new AbortController();
|
|
119
|
+
const finished = /* @__PURE__ */ new Map();
|
|
120
|
+
const pump = (async () => {
|
|
121
|
+
try {
|
|
122
|
+
for await (const event of client.events(controller.signal)) {
|
|
123
|
+
onEvent(event);
|
|
124
|
+
if (event.type === "goal.updated" && isTerminalGoal(event.goal)) {
|
|
125
|
+
finished.get(event.goal.id)?.(event.goal);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
})();
|
|
131
|
+
const backend = {
|
|
132
|
+
start: async (input) => (await client.post("/api/goals", input)).goal,
|
|
133
|
+
cancel: async (id) => (await client.post(`/api/goals/${id}/cancel`, void 0, {
|
|
134
|
+
timeoutMs: 10 * 6e4
|
|
135
|
+
})).goal,
|
|
136
|
+
wait: (id) => new Promise((resolveGoal) => {
|
|
137
|
+
finished.set(id, resolveGoal);
|
|
138
|
+
void client.get(`/api/goals/${id}`).then(({ goal }) => {
|
|
139
|
+
if (isTerminalGoal(goal)) resolveGoal(goal);
|
|
140
|
+
}).catch(() => void 0);
|
|
141
|
+
})
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
backend,
|
|
145
|
+
close: async () => {
|
|
146
|
+
controller.abort();
|
|
147
|
+
await pump;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async function goalCommand(ctx, words, opts, deps = {}) {
|
|
152
|
+
const objective = words.join(" ").trim();
|
|
153
|
+
if (!objective)
|
|
154
|
+
throw new CliError("Describe the objective", {
|
|
155
|
+
hint: 'davecode goal "Launch a landing page for my app"'
|
|
156
|
+
});
|
|
157
|
+
const root = await projectRoot(ctx, opts.project);
|
|
158
|
+
const input = {
|
|
159
|
+
objective,
|
|
160
|
+
...opts.maxRounds !== void 0 ? { maxRounds: opts.maxRounds } : {},
|
|
161
|
+
...opts.maxTasks !== void 0 ? { maxTasks: opts.maxTasks } : {},
|
|
162
|
+
...opts.maxTokens !== void 0 ? { maxTokens: opts.maxTokens } : {},
|
|
163
|
+
...opts.dryRun ? { dryRun: true } : {}
|
|
164
|
+
};
|
|
165
|
+
const config = deps.engine?.config ?? loadConfig({ home: ctx.home, env: ctx.env, projectRoot: root });
|
|
166
|
+
const gateway = deps.noGateway ? void 0 : await findGateway(config).catch(() => void 0);
|
|
167
|
+
let goalId;
|
|
168
|
+
const onEvent = (event) => {
|
|
169
|
+
if (goalId) printEvent(ctx, goalId, event);
|
|
170
|
+
};
|
|
171
|
+
let engine;
|
|
172
|
+
let backend;
|
|
173
|
+
let close;
|
|
174
|
+
if (gateway) {
|
|
175
|
+
const remote = gatewayBackend(gateway.client, onEvent);
|
|
176
|
+
backend = remote.backend;
|
|
177
|
+
close = remote.close;
|
|
178
|
+
if (!ctx.json) ctx.err(ctx.errTheme.dim(`Using the gateway at ${gateway.client.baseUrl}`));
|
|
179
|
+
} else {
|
|
180
|
+
engine = deps.engine ?? createEngine({ home: ctx.home, env: ctx.env, projectRoot: root });
|
|
181
|
+
const { goals } = wireProject(engine, root, { env: ctx.env });
|
|
182
|
+
const unsubscribe = engine.events.subscribe(onEvent);
|
|
183
|
+
backend = {
|
|
184
|
+
start: (body) => goals.start(body),
|
|
185
|
+
cancel: (id) => goals.cancel(id),
|
|
186
|
+
wait: async (id) => {
|
|
187
|
+
const goal = await goals.wait(id);
|
|
188
|
+
if (!goal) throw new CliError(`Goal ${id} disappeared`);
|
|
189
|
+
return goal;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const owned = !deps.engine;
|
|
193
|
+
const used = engine;
|
|
194
|
+
close = async () => {
|
|
195
|
+
unsubscribe();
|
|
196
|
+
if (owned) used.close();
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const interrupt = interruptSignal(deps);
|
|
200
|
+
const started = Date.now();
|
|
201
|
+
try {
|
|
202
|
+
let goal;
|
|
203
|
+
try {
|
|
204
|
+
goal = await backend.start(input);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
throw explainGoalError(err);
|
|
207
|
+
}
|
|
208
|
+
goalId = goal.id;
|
|
209
|
+
if (!ctx.json) {
|
|
210
|
+
ctx.err(
|
|
211
|
+
ctx.errTheme.dim(
|
|
212
|
+
`Goal ${goal.id} started${input.dryRun ? " (dry run: plan only)" : ""}. Ctrl+C cancels.`
|
|
213
|
+
)
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
let cancelling = false;
|
|
217
|
+
void interrupt.promise.then(() => {
|
|
218
|
+
cancelling = true;
|
|
219
|
+
if (!ctx.json) ctx.err(ctx.errTheme.dim("Cancelling\u2026"));
|
|
220
|
+
void backend.cancel(goal.id).catch(() => void 0);
|
|
221
|
+
});
|
|
222
|
+
goal = await backend.wait(goal.id);
|
|
223
|
+
if (ctx.json) printJson(ctx, { goal, cancelled: cancelling });
|
|
224
|
+
else {
|
|
225
|
+
ctx.out();
|
|
226
|
+
for (const line of describeGoal(ctx.theme, goal)) ctx.out(line);
|
|
227
|
+
ctx.out(ctx.theme.dim(` took ${formatDuration((Date.now() - started) / 1e3)}`));
|
|
228
|
+
if (goal.tasks.some((t) => t.artifact)) {
|
|
229
|
+
ctx.out(ctx.theme.dim(` artifacts in ${root}`));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return goalExitCode(goal);
|
|
233
|
+
} finally {
|
|
234
|
+
interrupt.dispose();
|
|
235
|
+
await close();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async function goalsList(ctx, opts) {
|
|
239
|
+
const root = await projectRoot(ctx, opts.project);
|
|
240
|
+
const goals = await new GoalStore(root).list();
|
|
241
|
+
if (ctx.json) {
|
|
242
|
+
printJson(ctx, { goals });
|
|
243
|
+
return EXIT.ok;
|
|
244
|
+
}
|
|
245
|
+
if (goals.length === 0) {
|
|
246
|
+
ctx.out(ctx.theme.dim('No goals yet. Start one with `davecode goal "<objective>"`.'));
|
|
247
|
+
return EXIT.ok;
|
|
248
|
+
}
|
|
249
|
+
for (const goal of goals) {
|
|
250
|
+
const done = goal.tasks.filter((t) => t.status === "SUCCESS").length;
|
|
251
|
+
ctx.out(
|
|
252
|
+
`${ctx.theme.bold(goal.id)} ${goal.status.padEnd(16)} ${ctx.theme.dim(`${done}/${goal.tasks.length} tasks \xB7 ${goal.createdAt.slice(0, 16).replace("T", " ")}`)} ${goal.objective.slice(0, 80)}`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return EXIT.ok;
|
|
256
|
+
}
|
|
257
|
+
async function goalsShow(ctx, id, opts) {
|
|
258
|
+
const root = await projectRoot(ctx, opts.project);
|
|
259
|
+
const goal = await new GoalStore(root).load(id);
|
|
260
|
+
if (!goal)
|
|
261
|
+
throw new CliError(`Unknown goal "${id}"`, { hint: "See `davecode goals` for the ids." });
|
|
262
|
+
if (ctx.json) printJson(ctx, { goal });
|
|
263
|
+
else for (const line of describeGoal(ctx.theme, goal)) ctx.out(line);
|
|
264
|
+
return EXIT.ok;
|
|
265
|
+
}
|
|
266
|
+
async function goalsCancel(ctx, id, opts, deps = {}) {
|
|
267
|
+
const root = await projectRoot(ctx, opts.project);
|
|
268
|
+
const config = deps.engine?.config ?? loadConfig({ home: ctx.home, env: ctx.env, projectRoot: root });
|
|
269
|
+
const gateway = deps.noGateway ? void 0 : await findGateway(config).catch(() => void 0);
|
|
270
|
+
let goal;
|
|
271
|
+
if (gateway) {
|
|
272
|
+
goal = (await gateway.client.post(`/api/goals/${id}/cancel`, void 0, {
|
|
273
|
+
timeoutMs: 10 * 6e4
|
|
274
|
+
})).goal;
|
|
275
|
+
} else {
|
|
276
|
+
const store = new GoalStore(root);
|
|
277
|
+
const stored = await store.load(id);
|
|
278
|
+
if (!stored)
|
|
279
|
+
throw new CliError(`Unknown goal "${id}"`, { hint: "See `davecode goals` for the ids." });
|
|
280
|
+
goal = stored;
|
|
281
|
+
if (!isTerminalGoal(goal)) {
|
|
282
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
283
|
+
goal.status = "cancelled";
|
|
284
|
+
goal.updatedAt = stamp;
|
|
285
|
+
goal.finishedAt = stamp;
|
|
286
|
+
delete goal.activeTaskId;
|
|
287
|
+
await store.save(goal);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (ctx.json) printJson(ctx, { goal });
|
|
291
|
+
else ctx.out(`${ctx.theme.bold(goal.id)} ${goal.status}`);
|
|
292
|
+
return EXIT.ok;
|
|
293
|
+
}
|
|
294
|
+
export {
|
|
295
|
+
describeGoal,
|
|
296
|
+
goalCommand,
|
|
297
|
+
goalExitCode,
|
|
298
|
+
goalsCancel,
|
|
299
|
+
goalsList,
|
|
300
|
+
goalsShow
|
|
301
|
+
};
|
|
302
|
+
//# sourceMappingURL=goal-6NUX4AIT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/goal.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport {\n createEngine,\n type DaveEvent,\n type Engine,\n type Goal,\n GoalStore,\n isTerminalGoal,\n loadConfig,\n} from '@davecode/core';\nimport { findGateway, type GatewayClient, GatewayError } from '../client';\nimport { type CliContext, printJson } from '../context';\nimport { CliError, EXIT } from '../errors';\nimport { detectProjectRoot, wireProject } from '../runtime';\nimport { formatDuration } from '../ui/format';\nimport type { Theme } from '../ui/theme';\n\nexport interface GoalOptions {\n maxRounds?: number;\n maxTasks?: number;\n maxTokens?: number;\n dryRun?: boolean;\n project?: string;\n}\n\n/** Goal control used by the command: in-process `GoalService` or the gateway's `/api/goals`. */\ninterface GoalBackend {\n start(input: GoalStartBody): Promise<Goal>;\n cancel(id: string): Promise<Goal>;\n /** Resolves with the finished goal. */\n wait(id: string): Promise<Goal>;\n}\n\ninterface GoalStartBody {\n objective: string;\n maxRounds?: number;\n maxTasks?: number;\n maxTokens?: number;\n dryRun?: boolean;\n}\n\n/** Test seams. */\nexport interface GoalDeps {\n engine?: Engine;\n /** Resolves when the user asks to stop (default: SIGINT/SIGTERM). */\n interrupt?: () => Promise<void>;\n noGateway?: boolean;\n}\n\nasync function projectRoot(ctx: CliContext, project?: string): Promise<string> {\n const root = project ? resolve(ctx.cwd, project) : await detectProjectRoot(ctx.cwd, ctx.home);\n if (!root || !existsSync(root)) {\n throw new CliError('Not inside a project', {\n hint: 'Run goal mode inside a repository with a project brain (`git init` and `davecode init`).',\n });\n }\n return root;\n}\n\nfunction interruptSignal(deps: GoalDeps): { promise: Promise<void>; dispose(): void } {\n if (deps.interrupt) return { promise: deps.interrupt(), dispose: () => {} };\n let dispose = () => {};\n const promise = new Promise<void>((done) => {\n const handler = () => {\n dispose();\n done();\n };\n dispose = () => {\n process.off('SIGINT', handler);\n process.off('SIGTERM', handler);\n };\n process.on('SIGINT', handler);\n process.on('SIGTERM', handler);\n });\n return { promise, dispose };\n}\n\nfunction explainGoalError(err: unknown): unknown {\n const code =\n err instanceof GatewayError ? err.code : (err as { code?: unknown } | undefined)?.code;\n const message = err instanceof Error ? err.message : String(err);\n if (code === 'busy') {\n return new CliError(message, {\n hint: 'Wait for the runner or the other goal to finish, or stop it from the dashboard.',\n });\n }\n if (code === 'goals_unavailable') {\n return new CliError('The running gateway has no project to run goals in', {\n hint: 'Restart `davecode start` inside this repository, or stop it so `davecode goal` runs in-process.',\n });\n }\n return err;\n}\n\n// ---------------------------------------------------------------------------\n// Printing\n// ---------------------------------------------------------------------------\n\nconst STATUS_GLYPH: Record<Goal['tasks'][number]['status'], (t: Theme) => string> = {\n SUCCESS: (t) => t.ok(t.glyph.success),\n FAILED: (t) => t.error(t.glyph.failed),\n IN_PROGRESS: (t) => t.accent(t.glyph.running),\n PENDING: (t) => t.dim(t.glyph.pending),\n};\n\nfunction printEvent(ctx: CliContext, goalId: string, event: DaveEvent): void {\n if (event.type !== 'goal.log' || event.goalId !== goalId || event.level === 'debug') return;\n const theme = ctx.errTheme;\n const time = new Date(event.ts).toTimeString().slice(0, 8);\n const who = event.role ? theme.accent(`[${event.role}]`) : theme.dim('[goal]');\n const message =\n event.level === 'error'\n ? theme.error(event.message)\n : event.level === 'warn'\n ? theme.warn(event.message)\n : event.message;\n ctx.err(`${theme.dim(time)} ${who} ${message}`);\n}\n\nexport function describeGoal(theme: Theme, goal: Goal): string[] {\n const lines: string[] = [];\n const colour =\n goal.status === 'succeeded' || goal.status === 'planned'\n ? theme.ok\n : goal.status === 'cancelled' || goal.status === 'budget_exhausted'\n ? theme.warn\n : goal.status === 'failed'\n ? theme.error\n : theme.accent;\n lines.push(\n `${theme.bold(goal.id)} ${colour(goal.status)} ${theme.dim(`round ${goal.round}/${goal.limits.maxRounds}`)}`,\n );\n lines.push(` ${goal.objective}`);\n lines.push(\n theme.dim(\n ` tokens ${goal.limits.tokensUsed.toLocaleString('en-US')} / ${goal.limits.maxTokens.toLocaleString('en-US')} · tasks ${goal.tasks.length} / ${goal.limits.maxTasks}`,\n ),\n );\n if (goal.tasks.length > 0) lines.push('');\n for (const task of goal.tasks) {\n const glyph = STATUS_GLYPH[task.status](theme);\n lines.push(\n `${glyph} ${theme.bold(task.id)} ${theme.dim(`${task.role}·${task.kind}`)} ${task.title}`,\n );\n if (task.artifact) lines.push(theme.dim(` ${task.artifact}`));\n else if (task.status === 'FAILED' && task.notes) {\n lines.push(theme.dim(` ${task.notes.split('\\n')[0]?.slice(0, 160) ?? ''}`));\n }\n }\n if (goal.summary) lines.push('', goal.summary.trim());\n if (goal.lastError) lines.push('', theme.error(goal.lastError));\n return lines;\n}\n\nexport function goalExitCode(goal: Goal): number {\n if (goal.status === 'succeeded' || goal.status === 'planned') return EXIT.ok;\n if (goal.status === 'cancelled') return 130;\n return EXIT.failure;\n}\n\n// ---------------------------------------------------------------------------\n// Backends\n// ---------------------------------------------------------------------------\n\nfunction gatewayBackend(\n client: GatewayClient,\n onEvent: (e: DaveEvent) => void,\n): {\n backend: GoalBackend;\n close(): Promise<void>;\n} {\n const controller = new AbortController();\n const finished = new Map<string, (goal: Goal) => void>();\n const pump = (async () => {\n try {\n for await (const event of client.events(controller.signal)) {\n onEvent(event);\n if (event.type === 'goal.updated' && isTerminalGoal(event.goal)) {\n finished.get(event.goal.id)?.(event.goal);\n }\n }\n } catch {\n // aborted or disconnected\n }\n })();\n const backend: GoalBackend = {\n start: async (input) => (await client.post<{ goal: Goal }>('/api/goals', input)).goal,\n cancel: async (id) =>\n (\n await client.post<{ goal: Goal }>(`/api/goals/${id}/cancel`, undefined, {\n timeoutMs: 10 * 60_000,\n })\n ).goal,\n wait: (id) =>\n new Promise<Goal>((resolveGoal) => {\n finished.set(id, resolveGoal);\n // The goal may have finished before the listener was registered.\n void client\n .get<{ goal: Goal }>(`/api/goals/${id}`)\n .then(({ goal }) => {\n if (isTerminalGoal(goal)) resolveGoal(goal);\n })\n .catch(() => undefined);\n }),\n };\n return {\n backend,\n close: async () => {\n controller.abort();\n await pump;\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Commands\n// ---------------------------------------------------------------------------\n\n/** `davecode goal \"<objective>\"`: plan and run a goal with the agent team, following its log. */\nexport async function goalCommand(\n ctx: CliContext,\n words: string[],\n opts: GoalOptions,\n deps: GoalDeps = {},\n): Promise<number> {\n const objective = words.join(' ').trim();\n if (!objective)\n throw new CliError('Describe the objective', {\n hint: 'davecode goal \"Launch a landing page for my app\"',\n });\n const root = await projectRoot(ctx, opts.project);\n const input: GoalStartBody = {\n objective,\n ...(opts.maxRounds !== undefined ? { maxRounds: opts.maxRounds } : {}),\n ...(opts.maxTasks !== undefined ? { maxTasks: opts.maxTasks } : {}),\n ...(opts.maxTokens !== undefined ? { maxTokens: opts.maxTokens } : {}),\n ...(opts.dryRun ? { dryRun: true } : {}),\n };\n\n const config =\n deps.engine?.config ?? loadConfig({ home: ctx.home, env: ctx.env, projectRoot: root });\n const gateway = deps.noGateway ? undefined : await findGateway(config).catch(() => undefined);\n let goalId: string | undefined;\n const onEvent = (event: DaveEvent) => {\n if (goalId) printEvent(ctx, goalId, event);\n };\n\n let engine: Engine | undefined;\n let backend: GoalBackend;\n let close: () => Promise<void>;\n if (gateway) {\n const remote = gatewayBackend(gateway.client, onEvent);\n backend = remote.backend;\n close = remote.close;\n if (!ctx.json) ctx.err(ctx.errTheme.dim(`Using the gateway at ${gateway.client.baseUrl}`));\n } else {\n engine = deps.engine ?? createEngine({ home: ctx.home, env: ctx.env, projectRoot: root });\n const { goals } = wireProject(engine, root, { env: ctx.env });\n const unsubscribe = engine.events.subscribe(onEvent);\n backend = {\n start: (body) => goals.start(body),\n cancel: (id) => goals.cancel(id),\n wait: async (id) => {\n const goal = await goals.wait(id);\n if (!goal) throw new CliError(`Goal ${id} disappeared`);\n return goal;\n },\n };\n const owned = !deps.engine;\n const used = engine;\n close = async () => {\n unsubscribe();\n if (owned) used.close();\n };\n }\n\n const interrupt = interruptSignal(deps);\n const started = Date.now();\n try {\n let goal: Goal;\n try {\n goal = await backend.start(input);\n } catch (err) {\n throw explainGoalError(err);\n }\n goalId = goal.id;\n if (!ctx.json) {\n ctx.err(\n ctx.errTheme.dim(\n `Goal ${goal.id} started${input.dryRun ? ' (dry run: plan only)' : ''}. Ctrl+C cancels.`,\n ),\n );\n }\n let cancelling = false;\n void interrupt.promise.then(() => {\n cancelling = true;\n if (!ctx.json) ctx.err(ctx.errTheme.dim('Cancelling…'));\n void backend.cancel(goal.id).catch(() => undefined);\n });\n goal = await backend.wait(goal.id);\n if (ctx.json) printJson(ctx, { goal, cancelled: cancelling });\n else {\n ctx.out();\n for (const line of describeGoal(ctx.theme, goal)) ctx.out(line);\n ctx.out(ctx.theme.dim(` took ${formatDuration((Date.now() - started) / 1000)}`));\n if (goal.tasks.some((t) => t.artifact)) {\n ctx.out(ctx.theme.dim(` artifacts in ${root}`));\n }\n }\n return goalExitCode(goal);\n } finally {\n interrupt.dispose();\n await close();\n }\n}\n\n/** `davecode goals`: goals of this repository, newest first (read from `.davecode/goals`). */\nexport async function goalsList(ctx: CliContext, opts: { project?: string }): Promise<number> {\n const root = await projectRoot(ctx, opts.project);\n const goals = await new GoalStore(root).list();\n if (ctx.json) {\n printJson(ctx, { goals });\n return EXIT.ok;\n }\n if (goals.length === 0) {\n ctx.out(ctx.theme.dim('No goals yet. Start one with `davecode goal \"<objective>\"`.'));\n return EXIT.ok;\n }\n for (const goal of goals) {\n const done = goal.tasks.filter((t) => t.status === 'SUCCESS').length;\n ctx.out(\n `${ctx.theme.bold(goal.id)} ${goal.status.padEnd(16)} ${ctx.theme.dim(`${done}/${goal.tasks.length} tasks · ${goal.createdAt.slice(0, 16).replace('T', ' ')}`)} ${goal.objective.slice(0, 80)}`,\n );\n }\n return EXIT.ok;\n}\n\n/** `davecode goals show <id>`. */\nexport async function goalsShow(\n ctx: CliContext,\n id: string,\n opts: { project?: string },\n): Promise<number> {\n const root = await projectRoot(ctx, opts.project);\n const goal = await new GoalStore(root).load(id);\n if (!goal)\n throw new CliError(`Unknown goal \"${id}\"`, { hint: 'See `davecode goals` for the ids.' });\n if (ctx.json) printJson(ctx, { goal });\n else for (const line of describeGoal(ctx.theme, goal)) ctx.out(line);\n return EXIT.ok;\n}\n\n/** `davecode goals cancel <id>`: through the gateway when one runs it, else marks it cancelled. */\nexport async function goalsCancel(\n ctx: CliContext,\n id: string,\n opts: { project?: string },\n deps: GoalDeps = {},\n): Promise<number> {\n const root = await projectRoot(ctx, opts.project);\n const config =\n deps.engine?.config ?? loadConfig({ home: ctx.home, env: ctx.env, projectRoot: root });\n const gateway = deps.noGateway ? undefined : await findGateway(config).catch(() => undefined);\n let goal: Goal;\n if (gateway) {\n goal = (\n await gateway.client.post<{ goal: Goal }>(`/api/goals/${id}/cancel`, undefined, {\n timeoutMs: 10 * 60_000,\n })\n ).goal;\n } else {\n const store = new GoalStore(root);\n const stored = await store.load(id);\n if (!stored)\n throw new CliError(`Unknown goal \"${id}\"`, { hint: 'See `davecode goals` for the ids.' });\n goal = stored;\n if (!isTerminalGoal(goal)) {\n const stamp = new Date().toISOString();\n goal.status = 'cancelled';\n goal.updatedAt = stamp;\n goal.finishedAt = stamp;\n delete goal.activeTaskId;\n await store.save(goal);\n }\n }\n if (ctx.json) printJson(ctx, { goal });\n else ctx.out(`${ctx.theme.bold(goal.id)} ${goal.status}`);\n return EXIT.ok;\n}\n"],"mappings":"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","names":[]}
|