@agentuity/cli 0.0.22 → 0.0.24
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/dist/cmd/dev/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cmd/dev/index.ts +174 -16
- package/src/tui.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cmd/dev/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cmd/dev/index.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,OAAO,mCA8TlB,CAAC"}
|
package/package.json
CHANGED
package/src/cmd/dev/index.ts
CHANGED
|
@@ -41,13 +41,18 @@ export const command = createCommand({
|
|
|
41
41
|
const agentuityDir = resolve(rootDir, '.agentuity');
|
|
42
42
|
const appPath = resolve(agentuityDir, 'app.js');
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// Watch directories instead of files to survive atomic replacements (sed -i, cp)
|
|
45
|
+
const watches = [rootDir];
|
|
45
46
|
const watchers: FSWatcher[] = [];
|
|
46
47
|
let failures = 0;
|
|
47
48
|
let running = false;
|
|
48
49
|
let pid = 0;
|
|
49
50
|
let failed = false;
|
|
50
51
|
let devServer: Bun.Subprocess | undefined;
|
|
52
|
+
let exitPromise: Promise<number> | undefined;
|
|
53
|
+
let restarting = false;
|
|
54
|
+
let shuttingDownForRestart = false;
|
|
55
|
+
let pendingRestart = false;
|
|
51
56
|
|
|
52
57
|
function failure(msg: string) {
|
|
53
58
|
failed = true;
|
|
@@ -60,23 +65,42 @@ export const command = createCommand({
|
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
const kill = () => {
|
|
68
|
+
const kill = async () => {
|
|
69
|
+
if (!running || !devServer) {
|
|
70
|
+
logger.trace('kill() called but server not running');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
logger.trace('Killing dev server (pid: %d)', pid);
|
|
75
|
+
shuttingDownForRestart = true;
|
|
64
76
|
running = false;
|
|
65
77
|
try {
|
|
66
78
|
// Kill the process group (negative PID kills entire group)
|
|
67
79
|
process.kill(-pid, 'SIGTERM');
|
|
80
|
+
logger.trace('Sent SIGTERM to process group');
|
|
68
81
|
} catch {
|
|
69
82
|
// Fallback: kill the direct process
|
|
70
83
|
try {
|
|
71
84
|
if (devServer) {
|
|
72
85
|
devServer.kill();
|
|
86
|
+
logger.trace('Killed dev server process directly');
|
|
73
87
|
}
|
|
74
88
|
} catch {
|
|
75
89
|
// Ignore if already dead
|
|
90
|
+
logger.trace('Process already dead');
|
|
76
91
|
}
|
|
77
|
-
} finally {
|
|
78
|
-
devServer = undefined;
|
|
79
92
|
}
|
|
93
|
+
|
|
94
|
+
// Wait for the server to actually exit
|
|
95
|
+
if (exitPromise) {
|
|
96
|
+
logger.trace('Waiting for dev server to exit...');
|
|
97
|
+
await exitPromise;
|
|
98
|
+
logger.trace('Dev server exited');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
devServer = undefined;
|
|
102
|
+
exitPromise = undefined;
|
|
103
|
+
shuttingDownForRestart = false;
|
|
80
104
|
};
|
|
81
105
|
|
|
82
106
|
// Handle signals to ensure entire process tree is killed
|
|
@@ -95,11 +119,26 @@ export const command = createCommand({
|
|
|
95
119
|
process.on('SIGTERM', cleanup);
|
|
96
120
|
|
|
97
121
|
async function restart() {
|
|
122
|
+
// Queue restart if already restarting
|
|
123
|
+
if (restarting) {
|
|
124
|
+
logger.trace('Restart already in progress, queuing another restart');
|
|
125
|
+
pendingRestart = true;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
logger.trace('restart() called, restarting=%s, running=%s', restarting, running);
|
|
130
|
+
restarting = true;
|
|
131
|
+
pendingRestart = false;
|
|
132
|
+
failed = false;
|
|
98
133
|
try {
|
|
99
134
|
if (running) {
|
|
135
|
+
logger.trace('Server is running, killing before restart');
|
|
100
136
|
tui.info('Restarting on file change');
|
|
101
|
-
kill();
|
|
102
|
-
|
|
137
|
+
await kill();
|
|
138
|
+
logger.trace('Server killed, continuing with restart');
|
|
139
|
+
// Continue with restart after kill completes
|
|
140
|
+
} else {
|
|
141
|
+
logger.trace('Initial server start');
|
|
103
142
|
}
|
|
104
143
|
await Promise.all([
|
|
105
144
|
tui.runCommand({
|
|
@@ -108,7 +147,7 @@ export const command = createCommand({
|
|
|
108
147
|
cwd: rootDir,
|
|
109
148
|
clearOnSuccess: true,
|
|
110
149
|
truncate: false,
|
|
111
|
-
maxLinesOutput:
|
|
150
|
+
maxLinesOutput: 2,
|
|
112
151
|
maxLinesOnFailure: 15,
|
|
113
152
|
}),
|
|
114
153
|
tui.spinner('Building project', async () => {
|
|
@@ -132,9 +171,10 @@ export const command = createCommand({
|
|
|
132
171
|
return;
|
|
133
172
|
}
|
|
134
173
|
|
|
174
|
+
logger.trace('Starting dev server: %s', appPath);
|
|
135
175
|
// Use shell to run in a process group for proper cleanup
|
|
136
176
|
// The 'exec' ensures the shell is replaced by the actual process
|
|
137
|
-
|
|
177
|
+
devServer = Bun.spawn(['sh', '-c', `exec bun run "${appPath}"`], {
|
|
138
178
|
cwd: rootDir,
|
|
139
179
|
stdout: 'inherit',
|
|
140
180
|
stderr: 'inherit',
|
|
@@ -144,25 +184,143 @@ export const command = createCommand({
|
|
|
144
184
|
running = true;
|
|
145
185
|
failed = false;
|
|
146
186
|
pid = devServer.pid;
|
|
187
|
+
exitPromise = devServer.exited;
|
|
188
|
+
logger.trace('Dev server started (pid: %d)', pid);
|
|
147
189
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
190
|
+
// Attach non-blocking exit handler
|
|
191
|
+
exitPromise
|
|
192
|
+
.then((exitCode) => {
|
|
193
|
+
logger.trace(
|
|
194
|
+
'Dev server exited with code %d (shuttingDownForRestart=%s)',
|
|
195
|
+
exitCode,
|
|
196
|
+
shuttingDownForRestart
|
|
197
|
+
);
|
|
198
|
+
running = false;
|
|
199
|
+
devServer = undefined;
|
|
200
|
+
exitPromise = undefined;
|
|
201
|
+
// Only exit the CLI if this is a clean exit AND not a restart
|
|
202
|
+
if (exitCode === 0 && !shuttingDownForRestart) {
|
|
203
|
+
logger.trace('Clean exit, stopping CLI');
|
|
204
|
+
process.exit(exitCode);
|
|
205
|
+
}
|
|
206
|
+
// Non-zero exit codes are treated as restartable failures
|
|
207
|
+
})
|
|
208
|
+
.catch((error) => {
|
|
209
|
+
logger.trace(
|
|
210
|
+
'Dev server exit error (shuttingDownForRestart=%s): %s',
|
|
211
|
+
shuttingDownForRestart,
|
|
212
|
+
error
|
|
213
|
+
);
|
|
214
|
+
running = false;
|
|
215
|
+
devServer = undefined;
|
|
216
|
+
exitPromise = undefined;
|
|
217
|
+
if (!shuttingDownForRestart) {
|
|
218
|
+
if (error instanceof Error) {
|
|
219
|
+
failure(`Dev server failed: ${error.message}`);
|
|
220
|
+
} else {
|
|
221
|
+
failure('Dev server failed');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
152
225
|
} catch (error) {
|
|
153
226
|
if (error instanceof Error) {
|
|
154
227
|
failure(`Dev server failed: ${error.message}`);
|
|
155
228
|
} else {
|
|
156
229
|
failure('Dev server failed');
|
|
157
230
|
}
|
|
158
|
-
} finally {
|
|
159
231
|
running = false;
|
|
232
|
+
devServer = undefined;
|
|
233
|
+
} finally {
|
|
234
|
+
const hadPendingRestart = pendingRestart;
|
|
235
|
+
restarting = false;
|
|
236
|
+
pendingRestart = false;
|
|
237
|
+
logger.trace(
|
|
238
|
+
'restart() completed, restarting=%s, hadPendingRestart=%s',
|
|
239
|
+
restarting,
|
|
240
|
+
hadPendingRestart
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
// If another restart was queued while we were restarting, trigger it now
|
|
244
|
+
if (hadPendingRestart) {
|
|
245
|
+
logger.trace('Triggering queued restart');
|
|
246
|
+
setImmediate(restart);
|
|
247
|
+
}
|
|
160
248
|
}
|
|
161
249
|
}
|
|
250
|
+
|
|
251
|
+
logger.trace('Starting initial build and server');
|
|
162
252
|
await restart();
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
253
|
+
logger.trace('Initial restart completed, setting up watchers');
|
|
254
|
+
|
|
255
|
+
// Patterns to ignore (generated files that change during build)
|
|
256
|
+
const ignorePatterns = [
|
|
257
|
+
/\.generated\.(js|ts|d\.ts)$/,
|
|
258
|
+
/registry\.generated\.ts$/,
|
|
259
|
+
/types\.generated\.d\.ts$/,
|
|
260
|
+
/client\.generated\.js$/,
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
logger.trace('Setting up file watchers for: %s', watches.join(', '));
|
|
264
|
+
for (const watchDir of watches) {
|
|
265
|
+
try {
|
|
266
|
+
logger.trace('Setting up watcher for %s', watchDir);
|
|
267
|
+
const watcher = watch(watchDir, { recursive: true }, (eventType, changedFile) => {
|
|
268
|
+
const absPath = changedFile ? join(watchDir, changedFile) : watchDir;
|
|
269
|
+
|
|
270
|
+
// Ignore node_modules folder
|
|
271
|
+
if (absPath.includes('node_modules')) {
|
|
272
|
+
logger.trace(
|
|
273
|
+
'File change ignored (node_modules): %s (event: %s, file: %s)',
|
|
274
|
+
watchDir,
|
|
275
|
+
eventType,
|
|
276
|
+
changedFile
|
|
277
|
+
);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Ignore changes in .agentuity directory (build output)
|
|
282
|
+
if (absPath.startsWith(agentuityDir)) {
|
|
283
|
+
logger.trace(
|
|
284
|
+
'File change ignored (.agentuity dir): %s (event: %s, file: %s)',
|
|
285
|
+
watchDir,
|
|
286
|
+
eventType,
|
|
287
|
+
changedFile
|
|
288
|
+
);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Ignore generated files to prevent restart loops
|
|
293
|
+
if (changedFile) {
|
|
294
|
+
for (const pattern of ignorePatterns) {
|
|
295
|
+
if (pattern.test(changedFile)) {
|
|
296
|
+
logger.trace(
|
|
297
|
+
'File change ignored (generated file): %s (event: %s, file: %s)',
|
|
298
|
+
watchDir,
|
|
299
|
+
eventType,
|
|
300
|
+
changedFile
|
|
301
|
+
);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
logger.trace(
|
|
308
|
+
'File change detected: %s (event: %s, file: %s)',
|
|
309
|
+
absPath,
|
|
310
|
+
eventType,
|
|
311
|
+
changedFile
|
|
312
|
+
);
|
|
313
|
+
restart();
|
|
314
|
+
});
|
|
315
|
+
watchers.push(watcher);
|
|
316
|
+
logger.trace('✓ Watcher added for %s', watchDir);
|
|
317
|
+
} catch (error) {
|
|
318
|
+
logger.error('Failed to setup watcher for %s: %s', watchDir, error);
|
|
319
|
+
}
|
|
166
320
|
}
|
|
321
|
+
logger.debug('Dev server watching for changes');
|
|
322
|
+
|
|
323
|
+
// Keep the handler alive indefinitely
|
|
324
|
+
await new Promise(() => {});
|
|
167
325
|
},
|
|
168
326
|
});
|
package/src/tui.ts
CHANGED
|
@@ -890,7 +890,7 @@ export async function runCommand(options: CommandRunnerOptions): Promise<number>
|
|
|
890
890
|
for (const line of lines) {
|
|
891
891
|
if (line.trim()) {
|
|
892
892
|
allOutputLines.push(line);
|
|
893
|
-
renderOutput(
|
|
893
|
+
renderOutput(maxLinesOutput); // Show last N lines while streaming
|
|
894
894
|
}
|
|
895
895
|
}
|
|
896
896
|
}
|