u-foo 2.4.5 → 2.4.6
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/README.md +1 -3
- package/modules/context/README.md +2 -2
- package/package.json +3 -7
- package/scripts/postinstall.js +1 -21
- package/src/app/chat/commandExecutor.js +11 -6
- package/src/app/chat/daemonCoordinator.js +4 -0
- package/src/app/chat/daemonReconnect.js +17 -7
- package/src/app/cli/features/skills.js +0 -15
- package/src/app/cli/run.js +1 -1
- package/src/code/README.md +4 -4
- package/src/code/cli.js +1 -1
- package/src/code/launcher/ucode.js +18 -45
- package/src/code/skills/loader.js +0 -13
- package/src/coordination/context/doctor.js +2 -4
- package/src/online/server.js +1 -1
- package/src/runtime/daemon/restart.js +293 -0
- package/src/runtime/daemon/run.js +31 -37
- package/src/ui/MIGRATION.md +8 -10
- package/src/ui/ink/ChatApp.js +11 -5
- package/bin/ucode-core.js +0 -15
- package/bin/ufoo +0 -71
- package/scripts/chat-app-smoke.js +0 -30
- package/scripts/global-chat-switch-benchmark.js +0 -406
- package/scripts/ink-demo.js +0 -23
- package/scripts/ink-smoke.js +0 -30
- package/scripts/ucode-app-smoke.js +0 -36
- /package/{modules/bus/SKILLS → SKILLS}/ubus/SKILL.md +0 -0
- /package/{modules/context/SKILLS → SKILLS}/uctx/SKILL.md +0 -0
- /package/{modules/online/SKILLS → SKILLS}/ufoo-online/SKILL.md +0 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_STOP_TIMEOUT_MS = 5000;
|
|
4
|
+
const DEFAULT_START_TIMEOUT_MS = 5000;
|
|
5
|
+
const DEFAULT_POLL_MS = 100;
|
|
6
|
+
|
|
7
|
+
function normalizePositiveInt(value, fallback) {
|
|
8
|
+
const n = Number(value);
|
|
9
|
+
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
10
|
+
return Math.trunc(n);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function waitAttempts(timeoutMs, pollMs) {
|
|
14
|
+
return Math.max(1, Math.ceil(timeoutMs / pollMs));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function defaultSleep(ms) {
|
|
18
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function daemonState(isRunning, projectRoot) {
|
|
22
|
+
if (typeof isRunning !== "function") return null;
|
|
23
|
+
return Boolean(isRunning(projectRoot));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function waitForDaemonState({
|
|
27
|
+
projectRoot,
|
|
28
|
+
isRunning,
|
|
29
|
+
desired,
|
|
30
|
+
timeoutMs,
|
|
31
|
+
pollMs,
|
|
32
|
+
sleep = defaultSleep,
|
|
33
|
+
}) {
|
|
34
|
+
if (typeof isRunning !== "function") return true;
|
|
35
|
+
const attempts = waitAttempts(timeoutMs, pollMs);
|
|
36
|
+
for (let i = 0; i <= attempts; i += 1) {
|
|
37
|
+
if (daemonState(isRunning, projectRoot) === desired) return true;
|
|
38
|
+
if (i >= attempts) break;
|
|
39
|
+
// eslint-disable-next-line no-await-in-loop
|
|
40
|
+
await sleep(pollMs);
|
|
41
|
+
}
|
|
42
|
+
return daemonState(isRunning, projectRoot) === desired;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function waitForDaemonStateSync({
|
|
46
|
+
projectRoot,
|
|
47
|
+
isRunning,
|
|
48
|
+
desired,
|
|
49
|
+
timeoutMs,
|
|
50
|
+
pollMs,
|
|
51
|
+
sleepSync,
|
|
52
|
+
}) {
|
|
53
|
+
if (typeof isRunning !== "function") return true;
|
|
54
|
+
const attempts = waitAttempts(timeoutMs, pollMs);
|
|
55
|
+
for (let i = 0; i <= attempts; i += 1) {
|
|
56
|
+
if (daemonState(isRunning, projectRoot) === desired) return true;
|
|
57
|
+
if (i >= attempts) break;
|
|
58
|
+
if (typeof sleepSync === "function") sleepSync(pollMs);
|
|
59
|
+
}
|
|
60
|
+
return daemonState(isRunning, projectRoot) === desired;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function createResult(overrides = {}) {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
stopped: false,
|
|
67
|
+
started: false,
|
|
68
|
+
connected: false,
|
|
69
|
+
stopOk: false,
|
|
70
|
+
startOk: false,
|
|
71
|
+
error: null,
|
|
72
|
+
...overrides,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function restartDaemonLifecycle(options = {}) {
|
|
77
|
+
const {
|
|
78
|
+
projectRoot,
|
|
79
|
+
isRunning,
|
|
80
|
+
stopDaemon,
|
|
81
|
+
startDaemon,
|
|
82
|
+
stopOptions,
|
|
83
|
+
startOptions,
|
|
84
|
+
connect,
|
|
85
|
+
requestStatus,
|
|
86
|
+
stopTimeoutMs = DEFAULT_STOP_TIMEOUT_MS,
|
|
87
|
+
startTimeoutMs = DEFAULT_START_TIMEOUT_MS,
|
|
88
|
+
pollMs = DEFAULT_POLL_MS,
|
|
89
|
+
sleep = defaultSleep,
|
|
90
|
+
} = options;
|
|
91
|
+
|
|
92
|
+
if (!projectRoot) return createResult({ error: "missing_project_root" });
|
|
93
|
+
if (typeof startDaemon !== "function") return createResult({ error: "missing_start_daemon" });
|
|
94
|
+
|
|
95
|
+
let stopOk = true;
|
|
96
|
+
let stopError = null;
|
|
97
|
+
if (typeof stopDaemon === "function") {
|
|
98
|
+
try {
|
|
99
|
+
stopOk = Boolean(stopOptions === undefined
|
|
100
|
+
? await stopDaemon(projectRoot)
|
|
101
|
+
: await stopDaemon(projectRoot, stopOptions));
|
|
102
|
+
} catch (err) {
|
|
103
|
+
stopOk = false;
|
|
104
|
+
stopError = err;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const stopped = await waitForDaemonState({
|
|
109
|
+
projectRoot,
|
|
110
|
+
isRunning,
|
|
111
|
+
desired: false,
|
|
112
|
+
timeoutMs: normalizePositiveInt(stopTimeoutMs, DEFAULT_STOP_TIMEOUT_MS),
|
|
113
|
+
pollMs: normalizePositiveInt(pollMs, DEFAULT_POLL_MS),
|
|
114
|
+
sleep,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (!stopped) {
|
|
118
|
+
return createResult({
|
|
119
|
+
stopped: false,
|
|
120
|
+
stopOk,
|
|
121
|
+
error: "failed_to_stop",
|
|
122
|
+
stopError,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let startOk = true;
|
|
127
|
+
let startError = null;
|
|
128
|
+
try {
|
|
129
|
+
if (startOptions === undefined) {
|
|
130
|
+
await startDaemon(projectRoot);
|
|
131
|
+
} else {
|
|
132
|
+
await startDaemon(projectRoot, startOptions);
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
startOk = false;
|
|
136
|
+
startError = err;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!startOk) {
|
|
140
|
+
return createResult({
|
|
141
|
+
stopped: true,
|
|
142
|
+
stopOk,
|
|
143
|
+
startOk: false,
|
|
144
|
+
error: "failed_to_start",
|
|
145
|
+
stopError,
|
|
146
|
+
startError,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const started = await waitForDaemonState({
|
|
151
|
+
projectRoot,
|
|
152
|
+
isRunning,
|
|
153
|
+
desired: true,
|
|
154
|
+
timeoutMs: normalizePositiveInt(startTimeoutMs, DEFAULT_START_TIMEOUT_MS),
|
|
155
|
+
pollMs: normalizePositiveInt(pollMs, DEFAULT_POLL_MS),
|
|
156
|
+
sleep,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
let connected = started;
|
|
160
|
+
let connectError = null;
|
|
161
|
+
if (typeof connect === "function") {
|
|
162
|
+
try {
|
|
163
|
+
connected = Boolean(await connect(projectRoot));
|
|
164
|
+
} catch (err) {
|
|
165
|
+
connected = false;
|
|
166
|
+
connectError = err;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (connected && typeof requestStatus === "function") {
|
|
171
|
+
try {
|
|
172
|
+
requestStatus(projectRoot);
|
|
173
|
+
} catch {
|
|
174
|
+
// Status refresh is best-effort after the lifecycle itself succeeds.
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const ok = typeof connect === "function" ? connected : started;
|
|
179
|
+
return createResult({
|
|
180
|
+
ok,
|
|
181
|
+
stopped: true,
|
|
182
|
+
started,
|
|
183
|
+
connected,
|
|
184
|
+
stopOk,
|
|
185
|
+
startOk: true,
|
|
186
|
+
error: ok ? null : (started ? "failed_to_connect" : "failed_to_start"),
|
|
187
|
+
stopError,
|
|
188
|
+
startError,
|
|
189
|
+
connectError,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function restartDaemonLifecycleSync(options = {}) {
|
|
194
|
+
const {
|
|
195
|
+
projectRoot,
|
|
196
|
+
isRunning,
|
|
197
|
+
stopDaemon,
|
|
198
|
+
startDaemon,
|
|
199
|
+
stopOptions,
|
|
200
|
+
startOptions,
|
|
201
|
+
stopTimeoutMs = DEFAULT_STOP_TIMEOUT_MS,
|
|
202
|
+
startTimeoutMs = DEFAULT_START_TIMEOUT_MS,
|
|
203
|
+
pollMs = DEFAULT_POLL_MS,
|
|
204
|
+
sleepSync,
|
|
205
|
+
} = options;
|
|
206
|
+
|
|
207
|
+
if (!projectRoot) return createResult({ error: "missing_project_root" });
|
|
208
|
+
if (typeof startDaemon !== "function") return createResult({ error: "missing_start_daemon" });
|
|
209
|
+
|
|
210
|
+
let stopOk = true;
|
|
211
|
+
let stopError = null;
|
|
212
|
+
if (typeof stopDaemon === "function") {
|
|
213
|
+
try {
|
|
214
|
+
stopOk = Boolean(stopOptions === undefined
|
|
215
|
+
? stopDaemon(projectRoot)
|
|
216
|
+
: stopDaemon(projectRoot, stopOptions));
|
|
217
|
+
} catch (err) {
|
|
218
|
+
stopOk = false;
|
|
219
|
+
stopError = err;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const stopped = waitForDaemonStateSync({
|
|
224
|
+
projectRoot,
|
|
225
|
+
isRunning,
|
|
226
|
+
desired: false,
|
|
227
|
+
timeoutMs: normalizePositiveInt(stopTimeoutMs, DEFAULT_STOP_TIMEOUT_MS),
|
|
228
|
+
pollMs: normalizePositiveInt(pollMs, DEFAULT_POLL_MS),
|
|
229
|
+
sleepSync,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (!stopped) {
|
|
233
|
+
return createResult({
|
|
234
|
+
stopped: false,
|
|
235
|
+
stopOk,
|
|
236
|
+
error: "failed_to_stop",
|
|
237
|
+
stopError,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let startOk = true;
|
|
242
|
+
let startError = null;
|
|
243
|
+
try {
|
|
244
|
+
if (startOptions === undefined) {
|
|
245
|
+
startDaemon(projectRoot);
|
|
246
|
+
} else {
|
|
247
|
+
startDaemon(projectRoot, startOptions);
|
|
248
|
+
}
|
|
249
|
+
} catch (err) {
|
|
250
|
+
startOk = false;
|
|
251
|
+
startError = err;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!startOk) {
|
|
255
|
+
return createResult({
|
|
256
|
+
stopped: true,
|
|
257
|
+
stopOk,
|
|
258
|
+
startOk: false,
|
|
259
|
+
error: "failed_to_start",
|
|
260
|
+
stopError,
|
|
261
|
+
startError,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const started = waitForDaemonStateSync({
|
|
266
|
+
projectRoot,
|
|
267
|
+
isRunning,
|
|
268
|
+
desired: true,
|
|
269
|
+
timeoutMs: normalizePositiveInt(startTimeoutMs, DEFAULT_START_TIMEOUT_MS),
|
|
270
|
+
pollMs: normalizePositiveInt(pollMs, DEFAULT_POLL_MS),
|
|
271
|
+
sleepSync,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return createResult({
|
|
275
|
+
ok: started,
|
|
276
|
+
stopped: true,
|
|
277
|
+
started,
|
|
278
|
+
connected: false,
|
|
279
|
+
stopOk,
|
|
280
|
+
startOk: true,
|
|
281
|
+
error: started ? null : "failed_to_start",
|
|
282
|
+
stopError,
|
|
283
|
+
startError,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
module.exports = {
|
|
288
|
+
DEFAULT_STOP_TIMEOUT_MS,
|
|
289
|
+
DEFAULT_START_TIMEOUT_MS,
|
|
290
|
+
DEFAULT_POLL_MS,
|
|
291
|
+
restartDaemonLifecycle,
|
|
292
|
+
restartDaemonLifecycleSync,
|
|
293
|
+
};
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
const path = require("path");
|
|
2
2
|
const { startDaemon, stopDaemon, isRunning } = require("./index");
|
|
3
|
+
const { restartDaemonLifecycleSync } = require("./restart");
|
|
3
4
|
const { loadConfig, defaultAgentModelForProvider } = require("../../config");
|
|
4
5
|
const { resolveNodeExecutable } = require("../process/nodeExecutable");
|
|
5
6
|
|
|
7
|
+
function spawnDaemonStart(projectRoot) {
|
|
8
|
+
const { spawn } = require("child_process");
|
|
9
|
+
const child = spawn(resolveNodeExecutable(), [path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
|
|
10
|
+
detached: true,
|
|
11
|
+
stdio: "ignore",
|
|
12
|
+
env: { ...process.env, UFOO_DAEMON_CHILD: "1" },
|
|
13
|
+
cwd: projectRoot,
|
|
14
|
+
});
|
|
15
|
+
child.unref();
|
|
16
|
+
return child;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function sleepSync(ms) {
|
|
20
|
+
require("child_process").spawnSync("sleep", [String(ms / 1000)]);
|
|
21
|
+
}
|
|
22
|
+
|
|
6
23
|
function runDaemonCli(argv) {
|
|
7
24
|
const cmd = argv[1] || "start";
|
|
8
25
|
const projectRoot = process.cwd();
|
|
@@ -19,14 +36,7 @@ function runDaemonCli(argv) {
|
|
|
19
36
|
if (cmd === "start" || cmd === "--start") {
|
|
20
37
|
if (isRunning(projectRoot)) return;
|
|
21
38
|
if (!process.env.UFOO_DAEMON_CHILD) {
|
|
22
|
-
|
|
23
|
-
const child = spawn(resolveNodeExecutable(), [path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
|
|
24
|
-
detached: true,
|
|
25
|
-
stdio: "ignore",
|
|
26
|
-
env: { ...process.env, UFOO_DAEMON_CHILD: "1" },
|
|
27
|
-
cwd: projectRoot,
|
|
28
|
-
});
|
|
29
|
-
child.unref();
|
|
39
|
+
spawnDaemonStart(projectRoot);
|
|
30
40
|
return;
|
|
31
41
|
}
|
|
32
42
|
startDaemon({ projectRoot, provider, model, resumeMode });
|
|
@@ -39,35 +49,19 @@ function runDaemonCli(argv) {
|
|
|
39
49
|
return;
|
|
40
50
|
}
|
|
41
51
|
if (cmd === "restart" || cmd === "--restart") {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
// Start fresh daemon
|
|
57
|
-
if (!process.env.UFOO_DAEMON_CHILD) {
|
|
58
|
-
const { spawn } = require("child_process");
|
|
59
|
-
const childEnv = { ...process.env, UFOO_DAEMON_CHILD: "1" };
|
|
60
|
-
const child = spawn(resolveNodeExecutable(), [path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
|
|
61
|
-
detached: true,
|
|
62
|
-
stdio: "ignore",
|
|
63
|
-
env: childEnv,
|
|
64
|
-
cwd: projectRoot,
|
|
65
|
-
});
|
|
66
|
-
child.unref();
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
// Manual restart does not auto-resume; crash-recovery is handled on next auto start with stale lock detection.
|
|
70
|
-
startDaemon({ projectRoot, provider, model, resumeMode: "none" });
|
|
52
|
+
const result = restartDaemonLifecycleSync({
|
|
53
|
+
projectRoot,
|
|
54
|
+
isRunning,
|
|
55
|
+
stopDaemon,
|
|
56
|
+
startDaemon: () => {
|
|
57
|
+
if (!process.env.UFOO_DAEMON_CHILD) return spawnDaemonStart(projectRoot);
|
|
58
|
+
// Manual restart does not auto-resume; crash-recovery is handled on next auto start with stale lock detection.
|
|
59
|
+
return startDaemon({ projectRoot, provider, model, resumeMode: "none" });
|
|
60
|
+
},
|
|
61
|
+
stopOptions: { source: process.env.UFOO_DAEMON_STOP_SOURCE || `daemon-cli:${cmd} pid=${process.pid}` },
|
|
62
|
+
sleepSync,
|
|
63
|
+
});
|
|
64
|
+
if (!result.ok) process.exitCode = 1;
|
|
71
65
|
return;
|
|
72
66
|
}
|
|
73
67
|
if (cmd === "status" || cmd === "--status") {
|
package/src/ui/MIGRATION.md
CHANGED
|
@@ -105,9 +105,8 @@ layout, hooks, and proper isolation of pure logic from rendering.
|
|
|
105
105
|
|
|
106
106
|
### Smoke
|
|
107
107
|
|
|
108
|
-
- [ ] `
|
|
109
|
-
- [ ] `npx jest --silent`
|
|
110
|
-
every ink suite passes.
|
|
108
|
+
- [ ] `npx jest --runTestsByPath test/unit/ui/UcodeApp.test.js test/unit/code/ucodeTui.test.js --runInBand` passes.
|
|
109
|
+
- [ ] `npx jest --silent` passes; every ink suite stays green.
|
|
111
110
|
|
|
112
111
|
## Decision log
|
|
113
112
|
|
|
@@ -118,9 +117,9 @@ layout, hooks, and proper isolation of pure logic from rendering.
|
|
|
118
117
|
avoids a build step. We can revisit if any single component grows
|
|
119
118
|
past ~600 lines and readability suffers.
|
|
120
119
|
- **Don't enable `--experimental-vm-modules` for jest.** The risk of
|
|
121
|
-
surprise ESM behaviour across the existing
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
surprise ESM behaviour across the existing test suite is too high.
|
|
121
|
+
Component logic is exercised by pure-function tests; real TTY render
|
|
122
|
+
coverage remains a manual smoke step.
|
|
124
123
|
- **Codex isn't a useful reference.** Its TUI is a Rust ratatui app
|
|
125
124
|
(`codex-rs/tui`), not React-based. The architectural principle worth
|
|
126
125
|
borrowing is its hard split between TUI and core protocol.
|
|
@@ -307,10 +306,9 @@ and `shouldEchoCommandInChat(text)` are pure.
|
|
|
307
306
|
the same as ucode.
|
|
308
307
|
|
|
309
308
|
### Smoke
|
|
310
|
-
- [ ] `
|
|
311
|
-
- [ ] `
|
|
312
|
-
- [ ] `npx jest --silent`
|
|
313
|
-
only; every ink suite passes.
|
|
309
|
+
- [ ] `npx jest --runTestsByPath test/unit/ui/ChatApp.test.js test/unit/ui/chatReducer.test.js --runInBand` passes.
|
|
310
|
+
- [ ] `npx jest --runTestsByPath test/unit/ui/UcodeApp.test.js test/unit/code/ucodeTui.test.js --runInBand` passes.
|
|
311
|
+
- [ ] `npx jest --silent` passes; every ink suite stays green.
|
|
314
312
|
|
|
315
313
|
## P4 close-out
|
|
316
314
|
|
package/src/ui/ink/ChatApp.js
CHANGED
|
@@ -23,6 +23,7 @@ const fmt = require("../format");
|
|
|
23
23
|
const { createMultilineInput } = require("./MultilineInput");
|
|
24
24
|
const { createDashboardBar } = require("./DashboardBar");
|
|
25
25
|
const { reducer, createInitialState } = require("./chatReducer");
|
|
26
|
+
const { restartDaemonLifecycle } = require("../../runtime/daemon/restart");
|
|
26
27
|
|
|
27
28
|
function bootstrapEnvironment(projectRoot, options = {}) {
|
|
28
29
|
// Ensure ufoo dirs exist and that we have a stable subscriber ID.
|
|
@@ -1813,11 +1814,15 @@ function createChatApp({ React, ink, props, interactive = true }) {
|
|
|
1813
1814
|
return;
|
|
1814
1815
|
}
|
|
1815
1816
|
try { if (conn && typeof conn.close === "function") conn.close(); } catch { /* ignore */ }
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1817
|
+
await restartDaemonLifecycle({
|
|
1818
|
+
projectRoot: targetRoot,
|
|
1819
|
+
isRunning: props.env && typeof props.env.isRunning === "function" ? props.env.isRunning : null,
|
|
1820
|
+
stopDaemon: (daemonRoot) => transportStopDaemon(daemonRoot, { source: "ink-command:/daemon restart" }),
|
|
1821
|
+
startDaemon: (daemonRoot) => transportStartDaemon(daemonRoot),
|
|
1822
|
+
connect: targetRoot === (currentProjectRootRef.current || props.projectRoot) && conn && typeof conn.connect === "function"
|
|
1823
|
+
? () => conn.connect()
|
|
1824
|
+
: null,
|
|
1825
|
+
});
|
|
1821
1826
|
},
|
|
1822
1827
|
send: (req) => { try { if (conn && typeof conn.send === "function") conn.send(req); } catch { /* ignore */ } },
|
|
1823
1828
|
requestStatus: requestDaemonStatus,
|
|
@@ -3591,6 +3596,7 @@ async function runChatInk(projectRoot, options = {}) {
|
|
|
3591
3596
|
daemonConnection,
|
|
3592
3597
|
stopDaemon,
|
|
3593
3598
|
startDaemon,
|
|
3599
|
+
isDaemonRunning: env.isRunning,
|
|
3594
3600
|
logMessage: () => {},
|
|
3595
3601
|
queueStatusLine: () => {},
|
|
3596
3602
|
resolveStatusLine: () => {},
|
package/bin/ucode-core.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { runUcodeCoreCli } = require("../src/code/cli");
|
|
4
|
-
|
|
5
|
-
(async () => {
|
|
6
|
-
const argv = process.argv.slice(2);
|
|
7
|
-
const result = await runUcodeCoreCli({ argv, projectRoot: process.cwd() });
|
|
8
|
-
if (result && typeof result.output === "string" && result.output) {
|
|
9
|
-
process.stdout.write(result.output);
|
|
10
|
-
}
|
|
11
|
-
process.exit(typeof result.exitCode === "number" ? result.exitCode : 0);
|
|
12
|
-
})().catch((err) => {
|
|
13
|
-
process.stderr.write(`${err && err.message ? err.message : "ucode-core failed"}\n`);
|
|
14
|
-
process.exit(1);
|
|
15
|
-
});
|
package/bin/ufoo
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
-
|
|
6
|
-
usage() {
|
|
7
|
-
cat <<'USAGE'
|
|
8
|
-
ufoo
|
|
9
|
-
|
|
10
|
-
Monorepo layout:
|
|
11
|
-
~/.ufoo/ (clone of ufoo)
|
|
12
|
-
modules/ai-context/ (protocol module)
|
|
13
|
-
modules/ai-resources/ (optional resources)
|
|
14
|
-
|
|
15
|
-
Usage:
|
|
16
|
-
ufoo doctor
|
|
17
|
-
ufoo status
|
|
18
|
-
ufoo daemon --start|--stop|--status
|
|
19
|
-
ufoo chat
|
|
20
|
-
ufoo init [--modules context[,resources]] [--project <dir>]
|
|
21
|
-
ufoo bus <...> # JS bus implementation (project-local data)
|
|
22
|
-
ufoo ctx <...> # JS context commands (doctor|lint|decisions)
|
|
23
|
-
ufoo skills list
|
|
24
|
-
ufoo skills install <name|all> [--codex|--agents|--target <dir>]
|
|
25
|
-
|
|
26
|
-
Notes:
|
|
27
|
-
- `init` creates/updates <project>/.ai-context and injects CLAUDE.md/AGENTS.md blocks.
|
|
28
|
-
- For Codex notifications, prefer `ufoo bus alert` / `ufoo bus listen` (no IME issues).
|
|
29
|
-
USAGE
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
cmd="${1:-}"
|
|
33
|
-
shift || true
|
|
34
|
-
|
|
35
|
-
case "$cmd" in
|
|
36
|
-
""|--help|-h)
|
|
37
|
-
exec node "$repo_root/bin/ufoo.js" chat
|
|
38
|
-
;;
|
|
39
|
-
doctor)
|
|
40
|
-
exec node "$repo_root/bin/ufoo.js" doctor "$@"
|
|
41
|
-
;;
|
|
42
|
-
status)
|
|
43
|
-
exec node "$repo_root/bin/ufoo.js" status "$@"
|
|
44
|
-
;;
|
|
45
|
-
daemon)
|
|
46
|
-
exec node "$repo_root/bin/ufoo.js" daemon "$@"
|
|
47
|
-
;;
|
|
48
|
-
chat)
|
|
49
|
-
exec node "$repo_root/bin/ufoo.js" chat
|
|
50
|
-
;;
|
|
51
|
-
init)
|
|
52
|
-
exec node "$repo_root/bin/ufoo.js" init "$@"
|
|
53
|
-
;;
|
|
54
|
-
bus)
|
|
55
|
-
# Project-local bus commands. Must be run from within the target project.
|
|
56
|
-
exec node "$repo_root/bin/ufoo.js" bus "$@"
|
|
57
|
-
;;
|
|
58
|
-
ctx)
|
|
59
|
-
exec node "$repo_root/bin/ufoo.js" ctx "$@"
|
|
60
|
-
;;
|
|
61
|
-
skills)
|
|
62
|
-
sub="${1:-}"
|
|
63
|
-
shift || true
|
|
64
|
-
exec node "$repo_root/bin/ufoo.js" skills "$sub" "$@"
|
|
65
|
-
;;
|
|
66
|
-
*)
|
|
67
|
-
echo "Unknown command: $cmd" >&2
|
|
68
|
-
usage >&2
|
|
69
|
-
exit 1
|
|
70
|
-
;;
|
|
71
|
-
esac
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Headless mount test for the ChatApp shell. Boots the ink TUI with stub
|
|
5
|
-
* props (no daemon, no real bootstrap) and checks the component tree
|
|
6
|
-
* renders without throwing. Used for CI parity with ucode-app-smoke.js.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const { runInk } = require("../src/ui/runInk");
|
|
10
|
-
const { createChatApp } = require("../src/ui/ink/ChatApp");
|
|
11
|
-
|
|
12
|
-
(async () => {
|
|
13
|
-
const props = {
|
|
14
|
-
activeProjectRoot: process.cwd(),
|
|
15
|
-
globalMode: false,
|
|
16
|
-
globalScope: "project",
|
|
17
|
-
};
|
|
18
|
-
const handle = await runInk((React, ink) => {
|
|
19
|
-
const ChatApp = createChatApp({ React, ink, props, interactive: false });
|
|
20
|
-
return React.createElement(ChatApp);
|
|
21
|
-
}, { stdout: process.stdout, stderr: process.stderr });
|
|
22
|
-
await new Promise((r) => setTimeout(r, 80));
|
|
23
|
-
handle.unmount();
|
|
24
|
-
await handle.waitUntilExit().catch(() => undefined);
|
|
25
|
-
process.stdout.write("\nchat-app-smoke: ok\n");
|
|
26
|
-
process.exit(0);
|
|
27
|
-
})().catch((err) => {
|
|
28
|
-
process.stderr.write(`chat-app-smoke: failed: ${err && err.stack || err}\n`);
|
|
29
|
-
process.exit(1);
|
|
30
|
-
});
|