nightytidy 0.2.7 → 0.2.9
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/package.json +1 -1
- package/src/agent/cli-bridge.js +29 -7
- package/src/agent/index.js +20 -3
package/package.json
CHANGED
package/src/agent/cli-bridge.js
CHANGED
|
@@ -76,7 +76,7 @@ export class CliBridge {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
_run(args, onOutput, opts = {}) {
|
|
79
|
-
return new Promise((resolve
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
80
|
const binPath = path.resolve(import.meta.dirname, '../../bin/nightytidy.js');
|
|
81
81
|
const proc = spawn('node', [binPath, ...args], {
|
|
82
82
|
cwd: this.projectDir,
|
|
@@ -87,15 +87,40 @@ export class CliBridge {
|
|
|
87
87
|
let stdout = '';
|
|
88
88
|
let stderr = '';
|
|
89
89
|
let killed = false;
|
|
90
|
+
let settled = false;
|
|
91
|
+
|
|
92
|
+
const settle = (result) => {
|
|
93
|
+
if (settled) return;
|
|
94
|
+
settled = true;
|
|
95
|
+
if (timer) clearTimeout(timer);
|
|
96
|
+
if (killTimer) clearTimeout(killTimer);
|
|
97
|
+
resolve(result);
|
|
98
|
+
};
|
|
90
99
|
|
|
91
100
|
// Timeout — kill the process if it takes too long
|
|
92
101
|
let timer = null;
|
|
102
|
+
let killTimer = null;
|
|
93
103
|
if (opts.timeout) {
|
|
94
104
|
timer = setTimeout(() => {
|
|
95
105
|
killed = true;
|
|
96
106
|
const timeoutSec = Math.round(opts.timeout / 1000);
|
|
97
107
|
warn(`CLI process timed out after ${timeoutSec}s: ${args.join(' ')}`);
|
|
98
108
|
this.kill();
|
|
109
|
+
// On Windows, taskkill is fire-and-forget — the 'close' event may
|
|
110
|
+
// never fire. Force-resolve after 5s to prevent the agent from
|
|
111
|
+
// hanging forever.
|
|
112
|
+
killTimer = setTimeout(() => {
|
|
113
|
+
warn(`CLI process did not exit within 5s after kill — force-resolving`);
|
|
114
|
+
this.activeProcess = null;
|
|
115
|
+
settle({
|
|
116
|
+
success: false,
|
|
117
|
+
exitCode: -1,
|
|
118
|
+
stdout,
|
|
119
|
+
stderr: `Process timed out after ${timeoutSec}s — Claude Code may be unavailable`,
|
|
120
|
+
parsed: CliBridge.parseOutput(stdout),
|
|
121
|
+
timedOut: true,
|
|
122
|
+
});
|
|
123
|
+
}, 5000);
|
|
99
124
|
}, opts.timeout);
|
|
100
125
|
}
|
|
101
126
|
|
|
@@ -127,26 +152,23 @@ export class CliBridge {
|
|
|
127
152
|
});
|
|
128
153
|
|
|
129
154
|
proc.on('close', (code) => {
|
|
130
|
-
if (timer) clearTimeout(timer);
|
|
131
155
|
this.activeProcess = null;
|
|
132
|
-
|
|
133
|
-
resolve({
|
|
156
|
+
settle({
|
|
134
157
|
success: code === 0 && !killed,
|
|
135
158
|
exitCode: code,
|
|
136
159
|
stdout,
|
|
137
160
|
stderr: killed
|
|
138
161
|
? `Process timed out after ${Math.round(opts.timeout / 1000)}s — Claude Code may be unavailable`
|
|
139
162
|
: stderr,
|
|
140
|
-
parsed,
|
|
163
|
+
parsed: CliBridge.parseOutput(stdout),
|
|
141
164
|
timedOut: killed,
|
|
142
165
|
});
|
|
143
166
|
});
|
|
144
167
|
|
|
145
168
|
proc.on('error', (err) => {
|
|
146
|
-
if (timer) clearTimeout(timer);
|
|
147
169
|
this.activeProcess = null;
|
|
148
170
|
logError(`CLI process error: ${err.message}`);
|
|
149
|
-
|
|
171
|
+
settle({
|
|
150
172
|
success: false,
|
|
151
173
|
exitCode: -1,
|
|
152
174
|
stdout,
|
package/src/agent/index.js
CHANGED
|
@@ -372,6 +372,7 @@ export async function startAgent() {
|
|
|
372
372
|
run: { id: interrupted.id },
|
|
373
373
|
}, []);
|
|
374
374
|
reply({ type: 'interrupted-discarded', runId: interrupted.id });
|
|
375
|
+
if (!runQueue.getCurrent()) processQueue();
|
|
375
376
|
break;
|
|
376
377
|
}
|
|
377
378
|
|
|
@@ -717,7 +718,8 @@ export async function startAgent() {
|
|
|
717
718
|
|
|
718
719
|
stopHeartbeat();
|
|
719
720
|
info(` Finishing run (report + merge)...`);
|
|
720
|
-
await bridge.finishRun();
|
|
721
|
+
const finishResult = await bridge.finishRun();
|
|
722
|
+
const reportMarkdown = finishResult?.parsed?.reportContent || null;
|
|
721
723
|
projectManager.updateProject(run.projectId, { lastRunAt: Date.now() });
|
|
722
724
|
|
|
723
725
|
const elapsedMs = Date.now() - run.startedAt;
|
|
@@ -733,6 +735,7 @@ export async function startAgent() {
|
|
|
733
735
|
project: project.name,
|
|
734
736
|
projectId: project.id,
|
|
735
737
|
run: { id: run.id, totalSteps, completedSteps: run.steps.length, elapsedMs: Date.now() - run.startedAt },
|
|
738
|
+
reportMarkdown,
|
|
736
739
|
}, project.webhooks);
|
|
737
740
|
|
|
738
741
|
activeBridge = null;
|
|
@@ -927,7 +930,8 @@ export async function startAgent() {
|
|
|
927
930
|
// Finish the run
|
|
928
931
|
stopHeartbeat();
|
|
929
932
|
info(` Finishing resumed run (report + merge)...`);
|
|
930
|
-
await bridge.finishRun();
|
|
933
|
+
const finishResult = await bridge.finishRun();
|
|
934
|
+
const reportMarkdown = finishResult?.parsed?.reportContent || null;
|
|
931
935
|
projectManager.updateProject(interrupted.projectId, { lastRunAt: Date.now() });
|
|
932
936
|
|
|
933
937
|
wsServer.broadcast({ type: 'run-completed', runId: interrupted.id, results: {} });
|
|
@@ -936,6 +940,7 @@ export async function startAgent() {
|
|
|
936
940
|
dispatchWithQueue('run_completed', {
|
|
937
941
|
project: project.name, projectId: project.id,
|
|
938
942
|
run: { id: interrupted.id, totalSteps: interrupted.steps.length, completedSteps: runProgress.completedCount, elapsedMs: Date.now() - interrupted.startedAt },
|
|
943
|
+
reportMarkdown,
|
|
939
944
|
}, project.webhooks);
|
|
940
945
|
|
|
941
946
|
activeBridge = null;
|
|
@@ -956,11 +961,13 @@ export async function startAgent() {
|
|
|
956
961
|
interrupted.status = 'running';
|
|
957
962
|
runQueue._save();
|
|
958
963
|
|
|
964
|
+
let finishResult = null;
|
|
959
965
|
try {
|
|
960
|
-
await bridge.finishRun();
|
|
966
|
+
finishResult = await bridge.finishRun();
|
|
961
967
|
} catch (err) {
|
|
962
968
|
warn(` finishRun failed: ${err.message}`);
|
|
963
969
|
}
|
|
970
|
+
const reportMarkdown = finishResult?.parsed?.reportContent || null;
|
|
964
971
|
|
|
965
972
|
projectManager.updateProject(interrupted.projectId, { lastRunAt: Date.now() });
|
|
966
973
|
wsServer.broadcast({ type: 'run-completed', runId: interrupted.id, status: 'completed', results: {} });
|
|
@@ -969,6 +976,7 @@ export async function startAgent() {
|
|
|
969
976
|
dispatchWithQueue('run_completed', {
|
|
970
977
|
project: project.name, projectId: project.id,
|
|
971
978
|
run: { id: interrupted.id, totalSteps: interrupted.steps.length, completedSteps: interrupted.lastProgress?.completedCount || 0, elapsedMs: Date.now() - interrupted.startedAt },
|
|
979
|
+
reportMarkdown,
|
|
972
980
|
}, project.webhooks);
|
|
973
981
|
|
|
974
982
|
activeBridge = null;
|
|
@@ -1099,6 +1107,15 @@ export async function startAgent() {
|
|
|
1099
1107
|
runQueue.markInterrupted({ completedCount: 0, failedCount: 0, totalCost: 0, stepList: [], currentStepNum: null });
|
|
1100
1108
|
}
|
|
1101
1109
|
|
|
1110
|
+
// Process any queued runs left from a previous session
|
|
1111
|
+
if (!runQueue.getInterrupted() && !runQueue.getCurrent()) {
|
|
1112
|
+
const pending = runQueue.getQueue();
|
|
1113
|
+
if (pending.length > 0) {
|
|
1114
|
+
info(`Found ${pending.length} queued run(s) — starting queue processing`);
|
|
1115
|
+
processQueue();
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1102
1119
|
// Print startup info
|
|
1103
1120
|
console.log(`\nNightyTidy Agent v${AGENT_VERSION}`);
|
|
1104
1121
|
console.log(`WebSocket: ws://127.0.0.1:${actualPort}`);
|