@dmrlzz/davecode 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +33 -2
- package/bin/davecode.js +2 -0
- package/dist/ChatApp-FO5SABXB.js +893 -0
- package/dist/ChatApp-FO5SABXB.js.map +1 -0
- package/dist/RunnerView-DLVR7C22.js +18 -0
- package/dist/RunnerView-DLVR7C22.js.map +1 -0
- package/dist/accounts-MTMGYKEP.js +724 -0
- package/dist/accounts-MTMGYKEP.js.map +1 -0
- package/dist/budget-RBHCEDPP.js +115 -0
- package/dist/budget-RBHCEDPP.js.map +1 -0
- package/dist/chat-ZDXX3N6X.js +95 -0
- package/dist/chat-ZDXX3N6X.js.map +1 -0
- package/dist/chunk-23KTE7IK.js +114 -0
- package/dist/chunk-23KTE7IK.js.map +1 -0
- package/dist/chunk-7XVXEO7U.js +23 -0
- package/dist/chunk-7XVXEO7U.js.map +1 -0
- package/dist/chunk-BOLFK3BH.js +20 -0
- package/dist/chunk-BOLFK3BH.js.map +1 -0
- package/dist/chunk-C7FDRA3U.js +9087 -0
- package/dist/chunk-C7FDRA3U.js.map +1 -0
- package/dist/chunk-DYQ56FBT.js +47 -0
- package/dist/chunk-DYQ56FBT.js.map +1 -0
- package/dist/chunk-FRFSJMN4.js +79 -0
- package/dist/chunk-FRFSJMN4.js.map +1 -0
- package/dist/chunk-IC3TDONV.js +12 -0
- package/dist/chunk-IC3TDONV.js.map +1 -0
- package/dist/chunk-IVZHKUCD.js +180 -0
- package/dist/chunk-IVZHKUCD.js.map +1 -0
- package/dist/chunk-MSLLH5XP.js +213 -0
- package/dist/chunk-MSLLH5XP.js.map +1 -0
- package/dist/chunk-OXRMGR72.js +20 -0
- package/dist/chunk-OXRMGR72.js.map +1 -0
- package/dist/chunk-OYEYNDOO.js +200 -0
- package/dist/chunk-OYEYNDOO.js.map +1 -0
- package/dist/chunk-PIIJ3RSI.js +1610 -0
- package/dist/chunk-PIIJ3RSI.js.map +1 -0
- package/dist/chunk-SF5ZEAP4.js +105 -0
- package/dist/chunk-SF5ZEAP4.js.map +1 -0
- package/dist/chunk-VVCXGQMY.js +107 -0
- package/dist/chunk-VVCXGQMY.js.map +1 -0
- package/dist/config-FRMY5OHG.js +158 -0
- package/dist/config-FRMY5OHG.js.map +1 -0
- package/dist/dashboard/assets/Accounts-BR3ZfVk4.js +1 -0
- package/dist/dashboard/assets/Badge-B-qzNRrZ.js +55 -0
- package/dist/dashboard/assets/Callout-B9g_wPDi.js +1 -0
- package/dist/dashboard/assets/Field-BfabPEcY.js +1 -0
- package/dist/dashboard/assets/Goals-BsV-6m94.js +1 -0
- package/dist/dashboard/assets/Library-CEA9P1m-.js +1 -0
- package/dist/dashboard/assets/Markdown-ClRsddnB.js +69 -0
- package/dist/dashboard/assets/Panel-B9tSgX6l.js +1 -0
- package/dist/dashboard/assets/RoutesPage-Dh-BJ3ir.js +4 -0
- package/dist/dashboard/assets/Runner-C_e9l5l2.js +1 -0
- package/dist/dashboard/assets/Settings-C6Yc1Gjg.js +1 -0
- package/dist/dashboard/assets/Switch-CUpOJjUT.js +1 -0
- package/dist/dashboard/assets/Tasks-CTku0rR3.js +2 -0
- package/dist/dashboard/assets/Traffic-CE6wZSMr.js +1 -0
- package/dist/dashboard/assets/Workspace-CxFxvkA6.js +1 -0
- package/dist/dashboard/assets/arrow-up-DL3CuFOO.js +1 -0
- package/dist/dashboard/assets/forms-D6UIYgC5.js +1 -0
- package/dist/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/dashboard/assets/index-1iH716Xy.css +2 -0
- package/dist/dashboard/assets/index-BMWr9EKo.js +104 -0
- package/dist/dashboard/assets/task-errors-CE4oEr8H.js +1 -0
- package/dist/dashboard/assets/trash-CWqeZ7eg.js +1 -0
- package/dist/dashboard/favicon.svg +7 -0
- package/dist/dashboard/github.svg +1 -0
- package/dist/dashboard/index.html +31 -0
- package/dist/dist-ZPXEVBEC.js +357 -0
- package/dist/dist-ZPXEVBEC.js.map +1 -0
- package/dist/doctor-7RUD4D3A.js +279 -0
- package/dist/doctor-7RUD4D3A.js.map +1 -0
- package/dist/goal-JFRNPBSD.js +302 -0
- package/dist/goal-JFRNPBSD.js.map +1 -0
- package/dist/index.js +381 -0
- package/dist/index.js.map +1 -0
- package/dist/init-NFYNO3A3.js +194 -0
- package/dist/init-NFYNO3A3.js.map +1 -0
- package/dist/prompts-L37J72YW.js +215 -0
- package/dist/prompts-L37J72YW.js.map +1 -0
- package/dist/run-ARUPXFFB.js +376 -0
- package/dist/run-ARUPXFFB.js.map +1 -0
- package/dist/runner-state-QFU3LJVJ.js +17 -0
- package/dist/runner-state-QFU3LJVJ.js.map +1 -0
- package/dist/start-CFPOVIAR.js +285 -0
- package/dist/start-CFPOVIAR.js.map +1 -0
- package/dist/status-E4EACVE2.js +25 -0
- package/dist/status-E4EACVE2.js.map +1 -0
- package/dist/tasks-ZIK6BVEX.js +184 -0
- package/dist/tasks-ZIK6BVEX.js.map +1 -0
- package/package.json +68 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/init.ts","../src/lib/validate-detect.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { basename, join, relative } from 'node:path';\nimport { configSchema, ProjectBrain, projectPaths } from '@davecode/core';\nimport { type CliContext, printJson } from '../context';\nimport { CliError, EXIT } from '../errors';\nimport { PromptCancelledError, type Prompter } from '../lib/prompter';\nimport {\n detectPackageManager,\n detectValidateCommands,\n readPackageJson,\n starterConfig,\n} from '../lib/validate-detect';\nimport { detectProjectRoot } from '../runtime';\n\nexport interface InitOptions {\n name?: string;\n /** `--config` writes the starter config without asking; `--no-config` skips it. */\n config?: boolean;\n}\n\nexport interface InitDeps {\n prompter?: Prompter;\n}\n\nconst LOCK_IGNORE = '.davecode/.lock';\n\n/** Make sure `.davecode/.lock` is git-ignored. Returns true when `.gitignore` changed. */\nexport function ensureLockIgnored(root: string): boolean {\n const path = join(root, '.gitignore');\n const current = existsSync(path) ? readFileSync(path, 'utf8') : '';\n const lines = current.split(/\\r?\\n/).map((l) => l.trim());\n if (lines.includes(LOCK_IGNORE) || lines.includes('.davecode/') || lines.includes('.davecode')) {\n return false;\n }\n const prefix = current === '' || current.endsWith('\\n') ? current : `${current}\\n`;\n writeFileSync(\n path,\n `${prefix}${current === '' ? '' : '\\n'}# DaveCode runtime lock\\n${LOCK_IGNORE}\\n`,\n );\n return true;\n}\n\nexport async function initCommand(\n ctx: CliContext,\n opts: InitOptions,\n deps: InitDeps = {},\n): Promise<number> {\n const root = (await detectProjectRoot(ctx.cwd, ctx.home)) ?? ctx.cwd;\n const paths = projectPaths(root);\n const pkg = readPackageJson(root);\n const name = opts.name?.trim() || pkg?.name || basename(root);\n\n const before = {\n state: existsSync(paths.state),\n architecture: existsSync(paths.architecture),\n taskGraph: existsSync(paths.taskGraph),\n };\n await ProjectBrain.init(root, { name });\n const created = Object.entries(before)\n .filter(([, existed]) => !existed)\n .map(([file]) => file);\n\n // Starter config with validation commands detected from package.json.\n const pm = detectPackageManager(root, pkg);\n const validate = detectValidateCommands(pkg, pm);\n let configWritten = false;\n let configSkipped: string | undefined;\n if (existsSync(paths.config)) {\n configSkipped = 'exists';\n } else if (opts.config === false) {\n configSkipped = 'declined';\n } else {\n let write = opts.config === true;\n if (!write && ctx.interactive) {\n const prompter =\n deps.prompter ??\n (await import('../tui/prompts')).createInkPrompter({\n stdout: ctx.io.stdout,\n stdin: ctx.io.stdin,\n theme: ctx.theme,\n });\n const summary = Object.entries(validate)\n .map(([k, v]) => `${k}: ${v}`)\n .join(', ');\n try {\n write = await prompter.confirm(\n summary\n ? `Write .davecode/config.json with runner.validate (${summary})?`\n : 'Write a starter .davecode/config.json (no lint/typecheck/test scripts found)?',\n true,\n );\n } catch (err) {\n if (!(err instanceof PromptCancelledError)) throw err;\n }\n }\n if (write) {\n const content = starterConfig(validate);\n const check = configSchema.safeParse(content);\n if (!check.success) throw new CliError('Generated config is invalid (please report a bug)');\n writeFileSync(paths.config, `${JSON.stringify(content, null, 2)}\\n`);\n configWritten = true;\n } else {\n configSkipped = ctx.interactive ? 'declined' : 'non-interactive';\n }\n }\n\n const ignoreUpdated = existsSync(join(root, '.git')) ? ensureLockIgnored(root) : false;\n\n if (ctx.json) {\n printJson(ctx, {\n root,\n name,\n created,\n config: configWritten ? paths.config : null,\n validate,\n gitignoreUpdated: ignoreUpdated,\n });\n return EXIT.ok;\n }\n\n const { theme } = ctx;\n const rel = (p: string) => relative(ctx.cwd, p) || '.';\n ctx.out(\n `${theme.ok(theme.glyph.ok)} Project brain ready for ${theme.bold(name)} ${theme.dim(rel(paths.dir))}`,\n );\n const files: Array<[string, string, boolean]> = [\n ['STATE.md', paths.state, before.state],\n ['ARCHITECTURE.md', paths.architecture, before.architecture],\n ['TASK_GRAPH.json', paths.taskGraph, before.taskGraph],\n ];\n for (const [label, , existed] of files) {\n ctx.out(` ${existed ? theme.dim('kept ') : theme.accent('created')} ${label}`);\n }\n if (configWritten) {\n ctx.out(` ${theme.accent('created')} config.json`);\n for (const [kind, command] of Object.entries(validate)) {\n ctx.out(theme.dim(` runner.validate.${kind} = ${command}`));\n }\n } else if (configSkipped === 'exists') {\n ctx.out(` ${theme.dim('kept ')} config.json`);\n } else if (configSkipped === 'non-interactive') {\n ctx.out(theme.dim(' config.json not written (pass --config to create a starter config)'));\n }\n if (ignoreUpdated)\n ctx.out(` ${theme.accent('updated')} .gitignore ${theme.dim(`(${LOCK_IGNORE})`)}`);\n\n ctx.out();\n ctx.out(theme.dim('Next:'));\n ctx.out(` ${theme.accent('davecode tasks add setup \"Describe the first task\"')}`);\n ctx.out(` ${theme.accent('davecode start')}`);\n return EXIT.ok;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'pnpm' | 'yarn' | 'bun' | 'npm';\n\nexport interface PackageJson {\n name?: string;\n packageManager?: string;\n scripts?: Record<string, string>;\n}\n\nexport function readPackageJson(root: string): PackageJson | undefined {\n try {\n const value: unknown = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));\n return value && typeof value === 'object' ? (value as PackageJson) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** From `packageManager` in package.json, then lockfiles; npm otherwise. */\nexport function detectPackageManager(root: string, pkg?: PackageJson): PackageManager {\n const declared = pkg?.packageManager?.split('@')[0];\n if (declared === 'pnpm' || declared === 'yarn' || declared === 'bun' || declared === 'npm') {\n return declared;\n }\n if (existsSync(join(root, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(root, 'yarn.lock'))) return 'yarn';\n if (existsSync(join(root, 'bun.lock')) || existsSync(join(root, 'bun.lockb'))) return 'bun';\n return 'npm';\n}\n\nconst CANDIDATES = {\n lint: ['lint', 'lint:check', 'check:lint'],\n typecheck: ['typecheck', 'type-check', 'check-types', 'check:types', 'types', 'tsc'],\n test: ['test', 'test:unit', 'test:ci'],\n} as const;\n\n/** npm's placeholder `test` script, which always fails. */\nconst NPM_PLACEHOLDER = /no test specified/i;\n\nexport function runScript(pm: PackageManager, script: string): string {\n if (script === 'test') return `${pm} test`;\n switch (pm) {\n case 'npm':\n return `npm run ${script}`;\n case 'bun':\n return `bun run ${script}`;\n default:\n return `${pm} ${script}`;\n }\n}\n\nexport interface ValidateCommands {\n lint?: string;\n typecheck?: string;\n test?: string;\n}\n\n/** `runner.validate` commands for the lint/typecheck/test scripts a package.json defines. */\nexport function detectValidateCommands(\n pkg: PackageJson | undefined,\n pm: PackageManager,\n): ValidateCommands {\n const scripts = pkg?.scripts ?? {};\n const found: ValidateCommands = {};\n for (const [kind, names] of Object.entries(CANDIDATES) as Array<\n [keyof ValidateCommands, readonly string[]]\n >) {\n const script = names.find((name) => {\n const body = scripts[name];\n return typeof body === 'string' && body.trim() !== '' && !NPM_PLACEHOLDER.test(body);\n });\n if (script) found[kind] = runScript(pm, script);\n }\n return found;\n}\n\n/** Starter `.davecode/config.json` content. */\nexport function starterConfig(validate: ValidateCommands): Record<string, unknown> {\n return { runner: { validate, judge: { kind: 'none' } } };\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","join","existsSync","readFileSync"]}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PromptCancelledError
|
|
3
|
+
} from "./chunk-IC3TDONV.js";
|
|
4
|
+
import {
|
|
5
|
+
ThemeProvider,
|
|
6
|
+
useTheme
|
|
7
|
+
} from "./chunk-OXRMGR72.js";
|
|
8
|
+
import "./chunk-VVCXGQMY.js";
|
|
9
|
+
|
|
10
|
+
// src/tui/prompts.tsx
|
|
11
|
+
import { Box, render, Text, useInput } from "ink";
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
function sanitizeLine(input) {
|
|
15
|
+
return input.replace(/[\u0000-\u001f\u007f]/g, "");
|
|
16
|
+
}
|
|
17
|
+
function Question({ message, answer }) {
|
|
18
|
+
const theme = useTheme();
|
|
19
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
20
|
+
/* @__PURE__ */ jsx(Text, { color: answer === void 0 ? theme.ink.accent : theme.ink.ok, children: answer === void 0 ? "?" : theme.glyph.ok }),
|
|
21
|
+
" ",
|
|
22
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: message }),
|
|
23
|
+
answer !== void 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: theme.ink.dim, children: [
|
|
24
|
+
" ",
|
|
25
|
+
answer
|
|
26
|
+
] }) : null
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
29
|
+
function SelectPrompt({
|
|
30
|
+
message,
|
|
31
|
+
choices,
|
|
32
|
+
initial,
|
|
33
|
+
done,
|
|
34
|
+
cancel
|
|
35
|
+
}) {
|
|
36
|
+
const theme = useTheme();
|
|
37
|
+
const start = Math.max(
|
|
38
|
+
0,
|
|
39
|
+
choices.findIndex((c) => c.value === initial)
|
|
40
|
+
);
|
|
41
|
+
const [index, setIndex] = useState(start);
|
|
42
|
+
const [chosen, setChosen] = useState();
|
|
43
|
+
useInput((input, key) => {
|
|
44
|
+
if (chosen) return;
|
|
45
|
+
if (key.escape || key.ctrl && input === "c") return cancel();
|
|
46
|
+
if (key.upArrow || input === "k") setIndex((i) => (i - 1 + choices.length) % choices.length);
|
|
47
|
+
else if (key.downArrow || input === "j") setIndex((i) => (i + 1) % choices.length);
|
|
48
|
+
else if (key.return) {
|
|
49
|
+
const choice = choices[index];
|
|
50
|
+
setChosen(choice);
|
|
51
|
+
done(choice.value);
|
|
52
|
+
} else if (/^[1-9]$/.test(input) && Number(input) <= choices.length) {
|
|
53
|
+
setIndex(Number(input) - 1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
if (chosen) return /* @__PURE__ */ jsx(Question, { message, answer: chosen.label });
|
|
57
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
58
|
+
/* @__PURE__ */ jsx(Question, { message }),
|
|
59
|
+
choices.map((choice, i) => {
|
|
60
|
+
const active = i === index;
|
|
61
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
62
|
+
/* @__PURE__ */ jsxs(Text, { color: active ? theme.ink.accent : void 0, children: [
|
|
63
|
+
active ? ` ${theme.glyph.prompt} ` : " ",
|
|
64
|
+
choice.label
|
|
65
|
+
] }),
|
|
66
|
+
choice.hint ? /* @__PURE__ */ jsxs(Text, { dimColor: theme.ink.dim, children: [
|
|
67
|
+
" ",
|
|
68
|
+
choice.hint
|
|
69
|
+
] }) : null
|
|
70
|
+
] }, choice.value);
|
|
71
|
+
}),
|
|
72
|
+
/* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: " \u2191/\u2193 to move, Enter to select, Esc to cancel" })
|
|
73
|
+
] });
|
|
74
|
+
}
|
|
75
|
+
function TextPrompt({
|
|
76
|
+
message,
|
|
77
|
+
initial = "",
|
|
78
|
+
placeholder,
|
|
79
|
+
validate,
|
|
80
|
+
mask,
|
|
81
|
+
optional,
|
|
82
|
+
done,
|
|
83
|
+
cancel
|
|
84
|
+
}) {
|
|
85
|
+
const theme = useTheme();
|
|
86
|
+
const [value, setValue] = useState(initial);
|
|
87
|
+
const [error, setError] = useState();
|
|
88
|
+
const [submitted, setSubmitted] = useState(false);
|
|
89
|
+
useInput((input, key) => {
|
|
90
|
+
if (submitted) return;
|
|
91
|
+
if (key.escape || key.ctrl && input === "c") return cancel();
|
|
92
|
+
if (key.return) {
|
|
93
|
+
if (mask && !optional && value.trim() === "") {
|
|
94
|
+
setError("a value is required");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const problem = validate?.(value);
|
|
98
|
+
if (problem) {
|
|
99
|
+
setError(problem);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
setSubmitted(true);
|
|
103
|
+
done(value);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (key.backspace || key.delete) {
|
|
107
|
+
setValue((v) => [...v].slice(0, -1).join(""));
|
|
108
|
+
} else if (key.ctrl && input === "u") {
|
|
109
|
+
setValue("");
|
|
110
|
+
} else if (!key.ctrl && !key.meta) {
|
|
111
|
+
const text = sanitizeLine(input);
|
|
112
|
+
if (text) setValue((v) => v + text);
|
|
113
|
+
}
|
|
114
|
+
setError(void 0);
|
|
115
|
+
});
|
|
116
|
+
const shown = mask ? "\u2022".repeat(Math.min([...value].length, 32)) : value;
|
|
117
|
+
if (submitted) {
|
|
118
|
+
const answer = mask ? value ? "set" : "skipped" : value || "(empty)";
|
|
119
|
+
return /* @__PURE__ */ jsx(Question, { message, answer });
|
|
120
|
+
}
|
|
121
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
122
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
123
|
+
/* @__PURE__ */ jsx(Text, { color: theme.ink.accent, children: "?" }),
|
|
124
|
+
" ",
|
|
125
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: message }),
|
|
126
|
+
" ",
|
|
127
|
+
shown ? /* @__PURE__ */ jsx(Text, { children: shown }) : /* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: placeholder ?? "" }),
|
|
128
|
+
/* @__PURE__ */ jsx(Text, { inverse: true, children: " " })
|
|
129
|
+
] }),
|
|
130
|
+
error ? /* @__PURE__ */ jsxs(Text, { color: theme.ink.error, children: [
|
|
131
|
+
" ",
|
|
132
|
+
error
|
|
133
|
+
] }) : null
|
|
134
|
+
] });
|
|
135
|
+
}
|
|
136
|
+
function ConfirmPrompt({
|
|
137
|
+
message,
|
|
138
|
+
initial,
|
|
139
|
+
done,
|
|
140
|
+
cancel
|
|
141
|
+
}) {
|
|
142
|
+
const theme = useTheme();
|
|
143
|
+
const [answer, setAnswer] = useState();
|
|
144
|
+
useInput((input, key) => {
|
|
145
|
+
if (answer !== void 0) return;
|
|
146
|
+
if (key.escape || key.ctrl && input === "c") return cancel();
|
|
147
|
+
let value;
|
|
148
|
+
if (key.return) value = initial;
|
|
149
|
+
else if (/^y$/i.test(input)) value = true;
|
|
150
|
+
else if (/^n$/i.test(input)) value = false;
|
|
151
|
+
if (value === void 0) return;
|
|
152
|
+
setAnswer(value);
|
|
153
|
+
done(value);
|
|
154
|
+
});
|
|
155
|
+
if (answer !== void 0) return /* @__PURE__ */ jsx(Question, { message, answer: answer ? "yes" : "no" });
|
|
156
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
157
|
+
/* @__PURE__ */ jsx(Text, { color: theme.ink.accent, children: "?" }),
|
|
158
|
+
" ",
|
|
159
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: message }),
|
|
160
|
+
" ",
|
|
161
|
+
/* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: initial ? "(Y/n)" : "(y/N)" })
|
|
162
|
+
] });
|
|
163
|
+
}
|
|
164
|
+
function createInkPrompter(options) {
|
|
165
|
+
const ask = (element) => new Promise((resolve, reject) => {
|
|
166
|
+
let result;
|
|
167
|
+
const instance = render(
|
|
168
|
+
/* @__PURE__ */ jsx(ThemeProvider, { theme: options.theme, children: element({
|
|
169
|
+
done: (value) => {
|
|
170
|
+
result = { ok: true, value };
|
|
171
|
+
setImmediate(() => instance.unmount());
|
|
172
|
+
},
|
|
173
|
+
cancel: () => {
|
|
174
|
+
result = { ok: false };
|
|
175
|
+
setImmediate(() => instance.unmount());
|
|
176
|
+
}
|
|
177
|
+
}) }),
|
|
178
|
+
{
|
|
179
|
+
stdout: options.stdout,
|
|
180
|
+
stdin: options.stdin,
|
|
181
|
+
exitOnCtrlC: false,
|
|
182
|
+
patchConsole: false
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
instance.waitUntilExit().then(
|
|
186
|
+
() => {
|
|
187
|
+
if (result?.ok) resolve(result.value);
|
|
188
|
+
else reject(new PromptCancelledError());
|
|
189
|
+
},
|
|
190
|
+
(err) => reject(err)
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
select: (message, choices, initial) => ask((s) => /* @__PURE__ */ jsx(
|
|
195
|
+
SelectPrompt,
|
|
196
|
+
{
|
|
197
|
+
message,
|
|
198
|
+
choices,
|
|
199
|
+
...initial !== void 0 ? { initial } : {},
|
|
200
|
+
...s
|
|
201
|
+
}
|
|
202
|
+
)),
|
|
203
|
+
text: (message, opts = {}) => ask((s) => /* @__PURE__ */ jsx(TextPrompt, { message, ...opts, ...s })),
|
|
204
|
+
secret: (message, opts = {}) => ask((s) => /* @__PURE__ */ jsx(TextPrompt, { message, mask: true, ...opts, ...s })),
|
|
205
|
+
confirm: (message, initial = false) => ask((s) => /* @__PURE__ */ jsx(ConfirmPrompt, { message, initial, ...s }))
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
export {
|
|
209
|
+
ConfirmPrompt,
|
|
210
|
+
SelectPrompt,
|
|
211
|
+
TextPrompt,
|
|
212
|
+
createInkPrompter,
|
|
213
|
+
sanitizeLine
|
|
214
|
+
};
|
|
215
|
+
//# sourceMappingURL=prompts-L37J72YW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/prompts.tsx"],"sourcesContent":["import { Box, render, Text, useInput } from 'ink';\nimport { type ReactElement, useState } from 'react';\nimport type { InStream, OutStream } from '../context';\nimport {\n type Choice,\n PromptCancelledError,\n type Prompter,\n type SecretOptions,\n type TextOptions,\n} from '../lib/prompter';\nimport type { Theme } from '../ui/theme';\nimport { ThemeProvider, useTheme } from './theme-context';\n\n/** Drop line breaks and control characters from typed or pasted single-line input. */\nexport function sanitizeLine(input: string): string {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: stripping control characters\n return input.replace(/[\\u0000-\\u001f\\u007f]/g, '');\n}\n\ninterface Settle<T> {\n done: (value: T) => void;\n cancel: () => void;\n}\n\nfunction Question({ message, answer }: { message: string; answer?: string }) {\n const theme = useTheme();\n return (\n <Text>\n <Text color={answer === undefined ? theme.ink.accent : theme.ink.ok}>\n {answer === undefined ? '?' : theme.glyph.ok}\n </Text>{' '}\n <Text bold>{message}</Text>\n {answer !== undefined ? <Text dimColor={theme.ink.dim}> {answer}</Text> : null}\n </Text>\n );\n}\n\nexport function SelectPrompt<T extends string>({\n message,\n choices,\n initial,\n done,\n cancel,\n}: { message: string; choices: Choice<T>[]; initial?: T } & Settle<T>) {\n const theme = useTheme();\n const start = Math.max(\n 0,\n choices.findIndex((c) => c.value === initial),\n );\n const [index, setIndex] = useState(start);\n const [chosen, setChosen] = useState<Choice<T> | undefined>();\n\n useInput((input, key) => {\n if (chosen) return;\n if (key.escape || (key.ctrl && input === 'c')) return cancel();\n if (key.upArrow || input === 'k') setIndex((i) => (i - 1 + choices.length) % choices.length);\n else if (key.downArrow || input === 'j') setIndex((i) => (i + 1) % choices.length);\n else if (key.return) {\n const choice = choices[index]!;\n setChosen(choice);\n done(choice.value);\n } else if (/^[1-9]$/.test(input) && Number(input) <= choices.length) {\n setIndex(Number(input) - 1);\n }\n });\n\n if (chosen) return <Question message={message} answer={chosen.label} />;\n return (\n <Box flexDirection=\"column\">\n <Question message={message} />\n {choices.map((choice, i) => {\n const active = i === index;\n return (\n <Text key={choice.value}>\n <Text color={active ? theme.ink.accent : undefined}>\n {active ? ` ${theme.glyph.prompt} ` : ' '}\n {choice.label}\n </Text>\n {choice.hint ? <Text dimColor={theme.ink.dim}> {choice.hint}</Text> : null}\n </Text>\n );\n })}\n <Text dimColor={theme.ink.dim}> ↑/↓ to move, Enter to select, Esc to cancel</Text>\n </Box>\n );\n}\n\nexport function TextPrompt({\n message,\n initial = '',\n placeholder,\n validate,\n mask,\n optional,\n done,\n cancel,\n}: TextOptions & SecretOptions & { message: string; mask?: boolean } & Settle<string>) {\n const theme = useTheme();\n const [value, setValue] = useState(initial);\n const [error, setError] = useState<string | undefined>();\n const [submitted, setSubmitted] = useState(false);\n\n useInput((input, key) => {\n if (submitted) return;\n if (key.escape || (key.ctrl && input === 'c')) return cancel();\n if (key.return) {\n if (mask && !optional && value.trim() === '') {\n setError('a value is required');\n return;\n }\n const problem = validate?.(value);\n if (problem) {\n setError(problem);\n return;\n }\n setSubmitted(true);\n done(value);\n return;\n }\n if (key.backspace || key.delete) {\n setValue((v) => [...v].slice(0, -1).join(''));\n } else if (key.ctrl && input === 'u') {\n setValue('');\n } else if (!key.ctrl && !key.meta) {\n const text = sanitizeLine(input);\n if (text) setValue((v) => v + text);\n }\n setError(undefined);\n });\n\n const shown = mask ? '•'.repeat(Math.min([...value].length, 32)) : value;\n if (submitted) {\n const answer = mask ? (value ? 'set' : 'skipped') : value || '(empty)';\n return <Question message={message} answer={answer} />;\n }\n return (\n <Box flexDirection=\"column\">\n <Text>\n <Text color={theme.ink.accent}>?</Text> <Text bold>{message}</Text>{' '}\n {shown ? <Text>{shown}</Text> : <Text dimColor={theme.ink.dim}>{placeholder ?? ''}</Text>}\n <Text inverse> </Text>\n </Text>\n {error ? (\n <Text color={theme.ink.error}>\n {' '}\n {error}\n </Text>\n ) : null}\n </Box>\n );\n}\n\nexport function ConfirmPrompt({\n message,\n initial,\n done,\n cancel,\n}: { message: string; initial: boolean } & Settle<boolean>) {\n const theme = useTheme();\n const [answer, setAnswer] = useState<boolean | undefined>();\n useInput((input, key) => {\n if (answer !== undefined) return;\n if (key.escape || (key.ctrl && input === 'c')) return cancel();\n let value: boolean | undefined;\n if (key.return) value = initial;\n else if (/^y$/i.test(input)) value = true;\n else if (/^n$/i.test(input)) value = false;\n if (value === undefined) return;\n setAnswer(value);\n done(value);\n });\n if (answer !== undefined) return <Question message={message} answer={answer ? 'yes' : 'no'} />;\n return (\n <Text>\n <Text color={theme.ink.accent}>?</Text> <Text bold>{message}</Text>{' '}\n <Text dimColor={theme.ink.dim}>{initial ? '(Y/n)' : '(y/N)'}</Text>\n </Text>\n );\n}\n\nexport interface InkPrompterOptions {\n stdout: OutStream;\n stdin: InStream;\n theme: Theme;\n}\n\n/** Ink-backed prompter: each question is a short-lived Ink app that leaves its answer behind. */\nexport function createInkPrompter(options: InkPrompterOptions): Prompter {\n const ask = <T,>(element: (settle: Settle<T>) => ReactElement): Promise<T> =>\n new Promise<T>((resolve, reject) => {\n let result: { ok: true; value: T } | { ok: false } | undefined;\n const instance = render(\n <ThemeProvider theme={options.theme}>\n {element({\n done: (value) => {\n result = { ok: true, value };\n setImmediate(() => instance.unmount());\n },\n cancel: () => {\n result = { ok: false };\n setImmediate(() => instance.unmount());\n },\n })}\n </ThemeProvider>,\n {\n stdout: options.stdout as NodeJS.WriteStream,\n stdin: options.stdin as NodeJS.ReadStream,\n exitOnCtrlC: false,\n patchConsole: false,\n },\n );\n instance.waitUntilExit().then(\n () => {\n if (result?.ok) resolve(result.value);\n else reject(new PromptCancelledError());\n },\n (err: unknown) => reject(err),\n );\n });\n\n return {\n select: (message, choices, initial) =>\n ask((s) => (\n <SelectPrompt\n message={message}\n choices={choices}\n {...(initial !== undefined ? { initial } : {})}\n {...s}\n />\n )),\n text: (message, opts = {}) => ask((s) => <TextPrompt message={message} {...opts} {...s} />),\n secret: (message, opts = {}) =>\n ask((s) => <TextPrompt message={message} mask {...opts} {...s} />),\n confirm: (message, initial = false) =>\n ask((s) => <ConfirmPrompt message={message} initial={initial} {...s} />),\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logText
|
|
3
|
+
} from "./chunk-OYEYNDOO.js";
|
|
4
|
+
import {
|
|
5
|
+
initialRunnerState,
|
|
6
|
+
isTerminal
|
|
7
|
+
} from "./chunk-FRFSJMN4.js";
|
|
8
|
+
import "./chunk-BOLFK3BH.js";
|
|
9
|
+
import "./chunk-OXRMGR72.js";
|
|
10
|
+
import {
|
|
11
|
+
formatDuration
|
|
12
|
+
} from "./chunk-SF5ZEAP4.js";
|
|
13
|
+
import {
|
|
14
|
+
printJson
|
|
15
|
+
} from "./chunk-DYQ56FBT.js";
|
|
16
|
+
import "./chunk-VVCXGQMY.js";
|
|
17
|
+
import {
|
|
18
|
+
GatewayError,
|
|
19
|
+
detectProjectRoot,
|
|
20
|
+
findGateway,
|
|
21
|
+
wireProject
|
|
22
|
+
} from "./chunk-PIIJ3RSI.js";
|
|
23
|
+
import {
|
|
24
|
+
CliError,
|
|
25
|
+
EXIT
|
|
26
|
+
} from "./chunk-7XVXEO7U.js";
|
|
27
|
+
import {
|
|
28
|
+
ProjectBrain,
|
|
29
|
+
RunnerError,
|
|
30
|
+
createEngine,
|
|
31
|
+
loadConfig
|
|
32
|
+
} from "./chunk-C7FDRA3U.js";
|
|
33
|
+
|
|
34
|
+
// src/commands/run.ts
|
|
35
|
+
import { existsSync } from "fs";
|
|
36
|
+
import { basename, resolve } from "path";
|
|
37
|
+
var HINTS = {
|
|
38
|
+
not_a_repo: () => ({
|
|
39
|
+
message: "This project is not inside a git repository",
|
|
40
|
+
hint: "The runner works on task branches: run `git init` and make a first commit."
|
|
41
|
+
}),
|
|
42
|
+
no_brain: () => ({
|
|
43
|
+
message: "No project brain here",
|
|
44
|
+
hint: "Run `davecode init`, then add tasks with `davecode tasks add`."
|
|
45
|
+
}),
|
|
46
|
+
no_base_branch: (config) => ({
|
|
47
|
+
message: `The base branch "${config.runner.baseBranch}" does not exist`,
|
|
48
|
+
hint: "Create it, or set runner.baseBranch in .davecode/config.json."
|
|
49
|
+
}),
|
|
50
|
+
dirty_worktree: () => ({
|
|
51
|
+
hint: "Commit or stash your changes, then run again (the runner never works on top of uncommitted edits)."
|
|
52
|
+
}),
|
|
53
|
+
invalid_graph: () => ({
|
|
54
|
+
hint: "Fix .davecode/TASK_GRAPH.json; `davecode tasks` shows the problem."
|
|
55
|
+
}),
|
|
56
|
+
task_not_found: () => ({
|
|
57
|
+
hint: "See `davecode tasks` for the task ids."
|
|
58
|
+
}),
|
|
59
|
+
task_not_runnable: () => ({
|
|
60
|
+
hint: "Only PENDING tasks can run: reopen it with `davecode tasks status <id> PENDING`."
|
|
61
|
+
}),
|
|
62
|
+
task_blocked: () => ({
|
|
63
|
+
hint: "Run its dependencies first (`davecode tasks` shows what each task waits on)."
|
|
64
|
+
}),
|
|
65
|
+
busy: () => ({
|
|
66
|
+
hint: "Stop the runner or the running goal (dashboard, `davecode goals cancel <id>` or the other `davecode run`) first."
|
|
67
|
+
})
|
|
68
|
+
};
|
|
69
|
+
var CODE_PATTERNS = [
|
|
70
|
+
["not_a_repo", /not inside a git repository/i],
|
|
71
|
+
["no_brain", /no project brain/i],
|
|
72
|
+
["no_base_branch", /base branch .* does not exist/i],
|
|
73
|
+
["dirty_worktree", /uncommitted changes/i],
|
|
74
|
+
["busy", /already running|working tree is busy/i],
|
|
75
|
+
["invalid_graph", /task graph/i]
|
|
76
|
+
];
|
|
77
|
+
function explainRunnerError(err, config) {
|
|
78
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
79
|
+
let code;
|
|
80
|
+
if (err instanceof RunnerError) code = err.code;
|
|
81
|
+
else if (err instanceof GatewayError && err.status === 409) {
|
|
82
|
+
code = err.code in HINTS ? err.code : CODE_PATTERNS.find(([, re]) => re.test(message))?.[0];
|
|
83
|
+
}
|
|
84
|
+
if (!code) return err;
|
|
85
|
+
const friendly = HINTS[code](config);
|
|
86
|
+
return new CliError(friendly.message ?? message, { hint: friendly.hint });
|
|
87
|
+
}
|
|
88
|
+
function signalWaiter() {
|
|
89
|
+
let dispose = () => {
|
|
90
|
+
};
|
|
91
|
+
const promise = new Promise((resolveSignal) => {
|
|
92
|
+
const done = () => {
|
|
93
|
+
dispose();
|
|
94
|
+
resolveSignal();
|
|
95
|
+
};
|
|
96
|
+
dispose = () => {
|
|
97
|
+
process.off("SIGINT", done);
|
|
98
|
+
process.off("SIGTERM", done);
|
|
99
|
+
};
|
|
100
|
+
process.on("SIGINT", done);
|
|
101
|
+
process.on("SIGTERM", done);
|
|
102
|
+
});
|
|
103
|
+
return { promise, dispose };
|
|
104
|
+
}
|
|
105
|
+
function interruptFor(deps) {
|
|
106
|
+
return deps.interrupt ? { promise: deps.interrupt(), dispose: () => {
|
|
107
|
+
} } : signalWaiter();
|
|
108
|
+
}
|
|
109
|
+
function describeResult(ctx, result) {
|
|
110
|
+
const { theme } = ctx;
|
|
111
|
+
const task = result.task ? `${theme.bold(result.task.id)} ${result.task.title}` : "";
|
|
112
|
+
const lines = [];
|
|
113
|
+
switch (result.outcome) {
|
|
114
|
+
case "success":
|
|
115
|
+
lines.push(`${theme.ok(theme.glyph.ok)} ${task} ${theme.ok("succeeded")}`);
|
|
116
|
+
if (result.delivery?.mode === "pr" && result.delivery.url) {
|
|
117
|
+
lines.push(` pull request ${theme.accent(result.delivery.url)}`);
|
|
118
|
+
} else if (result.delivery) lines.push(theme.dim(" merged into the base branch"));
|
|
119
|
+
break;
|
|
120
|
+
case "failed":
|
|
121
|
+
lines.push(`${theme.error(theme.glyph.fail)} ${task} ${theme.error("failed")}`);
|
|
122
|
+
if (result.branch) lines.push(theme.dim(` work kept on ${result.branch}`));
|
|
123
|
+
break;
|
|
124
|
+
case "idle":
|
|
125
|
+
lines.push(theme.dim("Nothing to do: no task is ready (see `davecode tasks`)."));
|
|
126
|
+
break;
|
|
127
|
+
case "stopped":
|
|
128
|
+
lines.push(theme.warn(`${theme.glyph.warn} Stopped; the task is back to PENDING.`));
|
|
129
|
+
if (result.branch) lines.push(theme.dim(` partial work kept on ${result.branch}`));
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
lines.push(
|
|
133
|
+
`${theme.error(theme.glyph.fail)} Runner error: ${result.error ?? "unknown error"}`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (result.outcome === "success" || result.outcome === "failed") {
|
|
137
|
+
lines.push(theme.dim(` repair cycles ${result.repairCycles}`));
|
|
138
|
+
if (result.validation && !result.validation.ok) {
|
|
139
|
+
const failing = result.validation.steps.filter((s) => !s.ok).map((s) => s.name);
|
|
140
|
+
if (failing.length > 0) lines.push(theme.dim(` failing checks: ${failing.join(", ")}`));
|
|
141
|
+
}
|
|
142
|
+
if (result.verdict) {
|
|
143
|
+
lines.push(
|
|
144
|
+
theme.dim(
|
|
145
|
+
` judge ${result.verdict.pass ? "passed" : "rejected"}${result.verdict.confidence !== void 0 ? ` (confidence ${result.verdict.confidence.toFixed(2)})` : ""}`
|
|
146
|
+
)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (result.summary) lines.push("", result.summary.trim());
|
|
150
|
+
}
|
|
151
|
+
return lines;
|
|
152
|
+
}
|
|
153
|
+
function exitCodeFor(result) {
|
|
154
|
+
if (result.outcome === "success" || result.outcome === "idle") return EXIT.ok;
|
|
155
|
+
if (result.outcome === "stopped") return 130;
|
|
156
|
+
return EXIT.failure;
|
|
157
|
+
}
|
|
158
|
+
function printEvent(ctx, event, toStderr) {
|
|
159
|
+
const write = toStderr ? ctx.err : ctx.out;
|
|
160
|
+
const theme = toStderr ? ctx.errTheme : ctx.theme;
|
|
161
|
+
if (event.type === "runner.log" && event.level !== "debug") {
|
|
162
|
+
write(
|
|
163
|
+
logText(theme, { ts: event.ts, level: event.level, message: event.message }, ctx.columns)
|
|
164
|
+
);
|
|
165
|
+
} else if (event.type === "runner.status") {
|
|
166
|
+
const s = event.status;
|
|
167
|
+
write(
|
|
168
|
+
theme.dim(
|
|
169
|
+
`${new Date(event.ts).toTimeString().slice(0, 8)} ${theme.glyph.arrow} ${s.state}${s.taskId ? ` \xB7 ${s.taskId}` : ""}${s.repairCycle ? ` \xB7 repair ${s.repairCycle}` : ""}`
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function runCommand(ctx, opts, deps = {}) {
|
|
175
|
+
const root = opts.project ? resolve(ctx.cwd, opts.project) : await detectProjectRoot(ctx.cwd, ctx.home);
|
|
176
|
+
if (!root || !existsSync(root)) {
|
|
177
|
+
throw new CliError("Not inside a project", {
|
|
178
|
+
hint: "Run `davecode run` inside a git repository with a project brain (`davecode init`)."
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
const config = deps.engine?.config ?? loadConfig({ home: ctx.home, env: ctx.env, projectRoot: root });
|
|
182
|
+
const gateway = deps.noGateway ? void 0 : await findGateway(config).catch(() => void 0);
|
|
183
|
+
if (gateway) {
|
|
184
|
+
const status = await gateway.client.get("/api/runner").then((r) => r.status).catch(() => void 0);
|
|
185
|
+
if (status && !["idle", "stopped", "error"].includes(status.state)) {
|
|
186
|
+
throw new CliError(`The gateway's runner is already ${status.state}`, {
|
|
187
|
+
hint: `Watch it in the dashboard at ${gateway.client.baseUrl}/ or stop it there first.`
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (!opts.once) return driveGateway(ctx, gateway.client, config, root, opts.task);
|
|
191
|
+
}
|
|
192
|
+
const engine = deps.engine ?? createEngine({ home: ctx.home, env: ctx.env, projectRoot: root });
|
|
193
|
+
try {
|
|
194
|
+
const { runner } = (deps.wire ?? ((e, r) => wireProject(e, r, { env: ctx.env })))(engine, root);
|
|
195
|
+
const graph = await new ProjectBrain(root).readGraph().catch(() => ({ version: 1, tasks: [] }));
|
|
196
|
+
return opts.once ? await runOnce(ctx, engine, runner, config, deps, opts.task) : await runContinuous(
|
|
197
|
+
ctx,
|
|
198
|
+
engine,
|
|
199
|
+
runner,
|
|
200
|
+
config,
|
|
201
|
+
basename(root),
|
|
202
|
+
graph.tasks,
|
|
203
|
+
deps,
|
|
204
|
+
opts.task
|
|
205
|
+
);
|
|
206
|
+
} finally {
|
|
207
|
+
if (!deps.engine) engine.close();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function runOnce(ctx, engine, runner, config, deps, taskId) {
|
|
211
|
+
const unsubscribe = engine.events.subscribe((event) => printEvent(ctx, event, ctx.json));
|
|
212
|
+
let interrupted = false;
|
|
213
|
+
const interrupt = interruptFor(deps);
|
|
214
|
+
void interrupt.promise.then(() => {
|
|
215
|
+
interrupted = true;
|
|
216
|
+
if (!ctx.json) ctx.err(ctx.errTheme.dim("Stopping safely\u2026"));
|
|
217
|
+
void runner.stop();
|
|
218
|
+
});
|
|
219
|
+
const started = Date.now();
|
|
220
|
+
let result;
|
|
221
|
+
try {
|
|
222
|
+
result = await runner.runOnce(taskId === void 0 ? {} : { taskId });
|
|
223
|
+
} catch (err) {
|
|
224
|
+
throw explainRunnerError(err, config);
|
|
225
|
+
} finally {
|
|
226
|
+
unsubscribe();
|
|
227
|
+
interrupt.dispose();
|
|
228
|
+
}
|
|
229
|
+
if (ctx.json) {
|
|
230
|
+
printJson(ctx, { ...result, interrupted });
|
|
231
|
+
} else {
|
|
232
|
+
ctx.out();
|
|
233
|
+
for (const line of describeResult(ctx, result)) ctx.out(line);
|
|
234
|
+
ctx.out(ctx.theme.dim(` took ${formatDuration((Date.now() - started) / 1e3)}`));
|
|
235
|
+
}
|
|
236
|
+
return exitCodeFor(result);
|
|
237
|
+
}
|
|
238
|
+
async function runContinuous(ctx, engine, runner, config, projectName, tasks, deps, taskId) {
|
|
239
|
+
const early = [];
|
|
240
|
+
const unsubscribeEarly = engine.events.subscribe((e) => early.push(e));
|
|
241
|
+
try {
|
|
242
|
+
await runner.start(taskId === void 0 ? {} : { taskId });
|
|
243
|
+
} catch (err) {
|
|
244
|
+
unsubscribeEarly();
|
|
245
|
+
throw explainRunnerError(err, config);
|
|
246
|
+
}
|
|
247
|
+
if (!ctx.interactive) {
|
|
248
|
+
unsubscribeEarly();
|
|
249
|
+
for (const event of early) printEvent(ctx, event, false);
|
|
250
|
+
const unsubscribe = engine.events.subscribe((event) => printEvent(ctx, event, false));
|
|
251
|
+
const finished = new Promise((resolveDone) => {
|
|
252
|
+
const timer = setInterval(() => {
|
|
253
|
+
if (!runner.running) {
|
|
254
|
+
clearInterval(timer);
|
|
255
|
+
resolveDone();
|
|
256
|
+
}
|
|
257
|
+
}, 250);
|
|
258
|
+
});
|
|
259
|
+
const interrupt = interruptFor(deps);
|
|
260
|
+
await Promise.race([interrupt.promise, finished]);
|
|
261
|
+
interrupt.dispose();
|
|
262
|
+
if (runner.running) {
|
|
263
|
+
ctx.out(ctx.theme.dim("Stopping safely\u2026"));
|
|
264
|
+
await runner.stop();
|
|
265
|
+
}
|
|
266
|
+
unsubscribe();
|
|
267
|
+
} else {
|
|
268
|
+
const [{ render }, { createElement }, { RunnerApp }] = await Promise.all([
|
|
269
|
+
import("ink"),
|
|
270
|
+
import("react"),
|
|
271
|
+
import("./RunnerView-DLVR7C22.js")
|
|
272
|
+
]);
|
|
273
|
+
let initial = initialRunnerState(runner.status(), tasks);
|
|
274
|
+
const { runnerReducer } = await import("./runner-state-QFU3LJVJ.js");
|
|
275
|
+
for (const event of early) initial = runnerReducer(initial, event);
|
|
276
|
+
unsubscribeEarly();
|
|
277
|
+
const instance = render(
|
|
278
|
+
createElement(RunnerApp, {
|
|
279
|
+
theme: ctx.theme,
|
|
280
|
+
initial,
|
|
281
|
+
subscribe: (onEvent) => engine.events.subscribe(onEvent),
|
|
282
|
+
maxRepairCycles: config.runner.maxRepairCycles,
|
|
283
|
+
projectName,
|
|
284
|
+
stop: () => runner.stop(),
|
|
285
|
+
pause: () => runner.pause(),
|
|
286
|
+
resume: () => runner.resume()
|
|
287
|
+
}),
|
|
288
|
+
{ stdout: ctx.io.stdout, exitOnCtrlC: false }
|
|
289
|
+
);
|
|
290
|
+
await instance.waitUntilExit();
|
|
291
|
+
if (runner.running) await runner.stop();
|
|
292
|
+
}
|
|
293
|
+
const final = runner.status();
|
|
294
|
+
if (final.state === "error") {
|
|
295
|
+
ctx.err(ctx.errTheme.error(`Runner error: ${final.lastError ?? "unknown error"}`));
|
|
296
|
+
return EXIT.failure;
|
|
297
|
+
}
|
|
298
|
+
return EXIT.ok;
|
|
299
|
+
}
|
|
300
|
+
async function driveGateway(ctx, client, config, root, taskId) {
|
|
301
|
+
try {
|
|
302
|
+
await client.post("/api/runner/start", taskId === void 0 ? void 0 : { taskId });
|
|
303
|
+
} catch (err) {
|
|
304
|
+
if (err instanceof GatewayError && err.status === 501) {
|
|
305
|
+
throw new CliError("The running gateway has no project brain to work on", {
|
|
306
|
+
hint: "Restart `davecode start` inside this repository, or stop it so `davecode run` can work in-process."
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
throw explainRunnerError(err, config);
|
|
310
|
+
}
|
|
311
|
+
const stop = async () => {
|
|
312
|
+
await client.post("/api/runner/stop", void 0, { timeoutMs: 10 * 6e4 });
|
|
313
|
+
};
|
|
314
|
+
const controller = new AbortController();
|
|
315
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
316
|
+
let latest = { state: "selecting" };
|
|
317
|
+
const pump = (async () => {
|
|
318
|
+
try {
|
|
319
|
+
for await (const event of client.events(controller.signal)) {
|
|
320
|
+
if (event.type === "runner.status") latest = event.status;
|
|
321
|
+
for (const listener of listeners) listener(event);
|
|
322
|
+
}
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
})();
|
|
326
|
+
try {
|
|
327
|
+
if (!ctx.interactive) {
|
|
328
|
+
const print = (e) => printEvent(ctx, e, false);
|
|
329
|
+
listeners.add(print);
|
|
330
|
+
await signalWaiter().promise;
|
|
331
|
+
ctx.out(ctx.theme.dim("Stopping safely\u2026"));
|
|
332
|
+
await stop();
|
|
333
|
+
} else {
|
|
334
|
+
const [{ render }, { createElement }, { RunnerApp }, { tasks }] = await Promise.all([
|
|
335
|
+
import("ink"),
|
|
336
|
+
import("react"),
|
|
337
|
+
import("./RunnerView-DLVR7C22.js"),
|
|
338
|
+
client.get("/api/tasks").then((r) => ({ tasks: r.graph.tasks })).catch(() => ({ tasks: [] }))
|
|
339
|
+
]);
|
|
340
|
+
const status = await client.get("/api/runner").then((r) => r.status).catch(() => latest);
|
|
341
|
+
const instance = render(
|
|
342
|
+
createElement(RunnerApp, {
|
|
343
|
+
theme: ctx.theme,
|
|
344
|
+
initial: initialRunnerState(status, tasks),
|
|
345
|
+
subscribe: (onEvent) => {
|
|
346
|
+
listeners.add(onEvent);
|
|
347
|
+
return () => listeners.delete(onEvent);
|
|
348
|
+
},
|
|
349
|
+
maxRepairCycles: config.runner.maxRepairCycles,
|
|
350
|
+
projectName: basename(root),
|
|
351
|
+
stop,
|
|
352
|
+
pause: async () => {
|
|
353
|
+
await client.post("/api/runner/pause");
|
|
354
|
+
},
|
|
355
|
+
resume: async () => {
|
|
356
|
+
await client.post("/api/runner/start");
|
|
357
|
+
}
|
|
358
|
+
}),
|
|
359
|
+
{ stdout: ctx.io.stdout, exitOnCtrlC: false }
|
|
360
|
+
);
|
|
361
|
+
await instance.waitUntilExit();
|
|
362
|
+
if (!isTerminal(latest.state)) await stop().catch(() => void 0);
|
|
363
|
+
}
|
|
364
|
+
} finally {
|
|
365
|
+
controller.abort();
|
|
366
|
+
await pump;
|
|
367
|
+
}
|
|
368
|
+
return latest.state === "error" ? EXIT.failure : EXIT.ok;
|
|
369
|
+
}
|
|
370
|
+
export {
|
|
371
|
+
describeResult,
|
|
372
|
+
exitCodeFor,
|
|
373
|
+
explainRunnerError,
|
|
374
|
+
runCommand
|
|
375
|
+
};
|
|
376
|
+
//# sourceMappingURL=run-ARUPXFFB.js.map
|