@staff0rd/assist 0.776.0 → 0.777.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/README.md +1 -1
- package/claude/skills/review-ci/scripts/review.mjs +21 -21
- package/dist/commands/sessions/web/bundle.js +481 -481
- package/dist/index.js +252 -81
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.777.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -1936,6 +1936,7 @@ var daemonPaths = {
|
|
|
1936
1936
|
socket: process.platform === "win32" ? perUserWindowsPipe() : join9(DAEMON_DIR, "daemon.sock"),
|
|
1937
1937
|
log: join9(DAEMON_DIR, "daemon.log"),
|
|
1938
1938
|
pid: join9(DAEMON_DIR, "daemon.pid"),
|
|
1939
|
+
autoUpdatePaused: join9(DAEMON_DIR, "auto-update-paused"),
|
|
1939
1940
|
spawnLock: join9(DAEMON_DIR, "spawn.lock"),
|
|
1940
1941
|
hooksSettings: join9(DAEMON_DIR, "hooks-settings.json")
|
|
1941
1942
|
};
|
|
@@ -11055,7 +11056,7 @@ var defaultDeps = {
|
|
|
11055
11056
|
httpsPort: tailnetHttpsPort
|
|
11056
11057
|
};
|
|
11057
11058
|
async function ensureTailscaleServe(port, deps2 = {}) {
|
|
11058
|
-
const { enabled, status: status2, serveStatus, serve, sleep:
|
|
11059
|
+
const { enabled, status: status2, serveStatus, serve, sleep: sleep2, httpsPort } = {
|
|
11059
11060
|
...defaultDeps,
|
|
11060
11061
|
...deps2
|
|
11061
11062
|
};
|
|
@@ -11070,7 +11071,7 @@ async function ensureTailscaleServe(port, deps2 = {}) {
|
|
|
11070
11071
|
return `serving ${plan2.target} at ${plan2.url}`;
|
|
11071
11072
|
} catch (error) {
|
|
11072
11073
|
if (attempt >= SERVE_WRITE_ATTEMPTS) throw error;
|
|
11073
|
-
await
|
|
11074
|
+
await sleep2(attempt * 500 + Math.random() * 500);
|
|
11074
11075
|
}
|
|
11075
11076
|
}
|
|
11076
11077
|
}
|
|
@@ -17221,6 +17222,33 @@ async function updates(_req, res) {
|
|
|
17221
17222
|
respondJson(res, 200, status2);
|
|
17222
17223
|
}
|
|
17223
17224
|
|
|
17225
|
+
// src/commands/sessions/shared/AutoUpdateAction.ts
|
|
17226
|
+
var AUTO_UPDATE_ACTIONS = ["pause", "resume", "check"];
|
|
17227
|
+
|
|
17228
|
+
// src/commands/sessions/web/updates/updatesControl.ts
|
|
17229
|
+
async function updatesControl(req, res) {
|
|
17230
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
17231
|
+
const action = url.searchParams.get("action");
|
|
17232
|
+
if (!AUTO_UPDATE_ACTIONS.includes(action)) {
|
|
17233
|
+
respondJson(res, 400, { error: "Invalid action" });
|
|
17234
|
+
return;
|
|
17235
|
+
}
|
|
17236
|
+
console.log(`auto-update ${action} ${linkedRequester(req)}`);
|
|
17237
|
+
const reply2 = await requestDaemonReply(
|
|
17238
|
+
{ type: "auto-update-control", action },
|
|
17239
|
+
"auto-update-control"
|
|
17240
|
+
);
|
|
17241
|
+
if (!reply2) {
|
|
17242
|
+
respondJson(res, 503, { error: "Daemon not reachable" });
|
|
17243
|
+
return;
|
|
17244
|
+
}
|
|
17245
|
+
if (!reply2.ok) {
|
|
17246
|
+
respondJson(res, 409, { error: reply2.error ?? `${action} failed` });
|
|
17247
|
+
return;
|
|
17248
|
+
}
|
|
17249
|
+
respondJson(res, 200, { ok: true });
|
|
17250
|
+
}
|
|
17251
|
+
|
|
17224
17252
|
// src/commands/sessions/web/readRequestBuffer.ts
|
|
17225
17253
|
async function readRequestBuffer(req, limit) {
|
|
17226
17254
|
const chunks = [];
|
|
@@ -17494,7 +17522,8 @@ var routes2 = {
|
|
|
17494
17522
|
"POST /api/config/set": setConfig,
|
|
17495
17523
|
"POST /api/config/unset": unsetConfig,
|
|
17496
17524
|
"GET /api/review/synthesis": getReviewSynthesis,
|
|
17497
|
-
"GET /api/updates": updates
|
|
17525
|
+
"GET /api/updates": updates,
|
|
17526
|
+
"POST /api/updates/control": updatesControl
|
|
17498
17527
|
};
|
|
17499
17528
|
var handleRequest = createFallbackHandler(
|
|
17500
17529
|
routes2,
|
|
@@ -40010,15 +40039,20 @@ function gitFailureReason(error) {
|
|
|
40010
40039
|
return message3.trim() || "git failed without reporting a reason";
|
|
40011
40040
|
}
|
|
40012
40041
|
|
|
40013
|
-
// src/commands/watch/
|
|
40042
|
+
// src/commands/watch/gitMarkerPath.ts
|
|
40014
40043
|
import { resolve as resolve25 } from "path";
|
|
40015
|
-
function
|
|
40044
|
+
function gitMarkerPath(name, cwd) {
|
|
40016
40045
|
return resolve25(
|
|
40017
40046
|
cwd ?? process.cwd(),
|
|
40018
|
-
runGit3(["rev-parse", "--git-path",
|
|
40047
|
+
runGit3(["rev-parse", "--git-path", name], cwd)
|
|
40019
40048
|
);
|
|
40020
40049
|
}
|
|
40021
40050
|
|
|
40051
|
+
// src/commands/watch/simulatedDivergencePath.ts
|
|
40052
|
+
function simulatedDivergencePath(cwd) {
|
|
40053
|
+
return gitMarkerPath("assist-simulate-divergence", cwd);
|
|
40054
|
+
}
|
|
40055
|
+
|
|
40022
40056
|
// src/commands/watch/simulateDivergence.ts
|
|
40023
40057
|
function simulateDivergence() {
|
|
40024
40058
|
try {
|
|
@@ -40526,11 +40560,10 @@ ${buildWatchReport(from)}`);
|
|
|
40526
40560
|
process.exit(0);
|
|
40527
40561
|
}
|
|
40528
40562
|
|
|
40529
|
-
// src/commands/watch/
|
|
40563
|
+
// src/commands/watch/consumeMarker.ts
|
|
40530
40564
|
import { existsSync as existsSync81, rmSync as rmSync8 } from "fs";
|
|
40531
|
-
function
|
|
40565
|
+
function consumeMarker(path106) {
|
|
40532
40566
|
try {
|
|
40533
|
-
const path106 = simulatedDivergencePath(cwd);
|
|
40534
40567
|
if (!existsSync81(path106)) return false;
|
|
40535
40568
|
rmSync8(path106);
|
|
40536
40569
|
return true;
|
|
@@ -40539,6 +40572,15 @@ function consumeSimulatedDivergence(cwd) {
|
|
|
40539
40572
|
}
|
|
40540
40573
|
}
|
|
40541
40574
|
|
|
40575
|
+
// src/commands/watch/consumeSimulatedDivergence.ts
|
|
40576
|
+
function consumeSimulatedDivergence(cwd) {
|
|
40577
|
+
try {
|
|
40578
|
+
return consumeMarker(simulatedDivergencePath(cwd));
|
|
40579
|
+
} catch {
|
|
40580
|
+
return false;
|
|
40581
|
+
}
|
|
40582
|
+
}
|
|
40583
|
+
|
|
40542
40584
|
// src/commands/watch/fetchQuietly.ts
|
|
40543
40585
|
import { execFileSync as execFileSync21 } from "child_process";
|
|
40544
40586
|
var MIN_FETCH_TIMEOUT_MS = 6e4;
|
|
@@ -40573,26 +40615,33 @@ function readMovement(cwd) {
|
|
|
40573
40615
|
}
|
|
40574
40616
|
|
|
40575
40617
|
// src/commands/watch/pollForMovement.ts
|
|
40618
|
+
var CONTROL_POLL_MS = 1e3;
|
|
40576
40619
|
function pollForMovement(options2) {
|
|
40577
|
-
const { upstream, intervalMs, timeoutMs, timeout, cwd } = options2;
|
|
40620
|
+
const { upstream, intervalMs, timeoutMs, timeout, cwd, control } = options2;
|
|
40578
40621
|
return new Promise((resolve28) => {
|
|
40579
40622
|
let settled = false;
|
|
40580
40623
|
const finish2 = (outcome) => {
|
|
40581
40624
|
if (settled) return;
|
|
40582
40625
|
settled = true;
|
|
40583
40626
|
clearInterval(ticker);
|
|
40627
|
+
clearInterval(controller);
|
|
40584
40628
|
clearTimeout(deadline);
|
|
40585
40629
|
process.off("SIGINT", onInterrupt);
|
|
40586
40630
|
resolve28(outcome);
|
|
40587
40631
|
};
|
|
40588
40632
|
const onInterrupt = () => finish2({ kind: "interrupted" });
|
|
40589
|
-
const
|
|
40633
|
+
const check2 = () => {
|
|
40590
40634
|
if (consumeSimulatedDivergence(cwd))
|
|
40591
40635
|
return finish2({ kind: "simulated-divergence", upstream });
|
|
40592
40636
|
fetchQuietly(cwd, intervalMs);
|
|
40593
40637
|
const found = readMovement(cwd);
|
|
40594
40638
|
if (found) finish2({ kind: "moved", upstream, ...found });
|
|
40595
|
-
}
|
|
40639
|
+
};
|
|
40640
|
+
const ticker = setInterval(check2, intervalMs);
|
|
40641
|
+
const controller = control && setInterval(() => {
|
|
40642
|
+
if (consumeMarker(control.stop)) return finish2({ kind: "interrupted" });
|
|
40643
|
+
if (consumeMarker(control.check)) check2();
|
|
40644
|
+
}, CONTROL_POLL_MS);
|
|
40596
40645
|
const deadline = timeoutMs === void 0 ? void 0 : setTimeout(
|
|
40597
40646
|
() => finish2({ kind: "timeout", upstream, timeout }),
|
|
40598
40647
|
timeoutMs
|
|
@@ -40601,6 +40650,18 @@ function pollForMovement(options2) {
|
|
|
40601
40650
|
});
|
|
40602
40651
|
}
|
|
40603
40652
|
|
|
40653
|
+
// src/commands/watch/watchControlPaths.ts
|
|
40654
|
+
function watchControlPaths(cwd) {
|
|
40655
|
+
try {
|
|
40656
|
+
return {
|
|
40657
|
+
check: gitMarkerPath("assist-watch-check", cwd),
|
|
40658
|
+
stop: gitMarkerPath("assist-watch-stop", cwd)
|
|
40659
|
+
};
|
|
40660
|
+
} catch {
|
|
40661
|
+
return void 0;
|
|
40662
|
+
}
|
|
40663
|
+
}
|
|
40664
|
+
|
|
40604
40665
|
// src/commands/watch/waitForUpstream.ts
|
|
40605
40666
|
function waitForUpstream(options2) {
|
|
40606
40667
|
const { intervalMs, timeoutMs, timeout, cwd, onStart } = options2;
|
|
@@ -40616,10 +40677,20 @@ function waitForUpstream(options2) {
|
|
|
40616
40677
|
onStart?.(upstream);
|
|
40617
40678
|
if (consumeSimulatedDivergence(cwd))
|
|
40618
40679
|
return Promise.resolve({ kind: "simulated-divergence", upstream });
|
|
40680
|
+
const control = watchControlPaths(cwd);
|
|
40619
40681
|
fetchQuietly(cwd, intervalMs);
|
|
40682
|
+
if (control && consumeMarker(control.stop))
|
|
40683
|
+
return Promise.resolve({ kind: "interrupted" });
|
|
40620
40684
|
const moved = readMovement(cwd);
|
|
40621
40685
|
if (moved) return Promise.resolve({ kind: "moved", upstream, ...moved });
|
|
40622
|
-
return pollForMovement({
|
|
40686
|
+
return pollForMovement({
|
|
40687
|
+
upstream,
|
|
40688
|
+
intervalMs,
|
|
40689
|
+
timeoutMs,
|
|
40690
|
+
timeout,
|
|
40691
|
+
cwd,
|
|
40692
|
+
control
|
|
40693
|
+
});
|
|
40623
40694
|
}
|
|
40624
40695
|
|
|
40625
40696
|
// src/commands/watch/watchWait.ts
|
|
@@ -42136,6 +42207,39 @@ function createRunSession(id, { runName, runArgs, cwd, meta }) {
|
|
|
42136
42207
|
};
|
|
42137
42208
|
}
|
|
42138
42209
|
|
|
42210
|
+
// src/commands/sessions/daemon/autoUpdate/autoUpdateControl.ts
|
|
42211
|
+
import { existsSync as existsSync86, rmSync as rmSync9, writeFileSync as writeFileSync54 } from "fs";
|
|
42212
|
+
var paused;
|
|
42213
|
+
var wake;
|
|
42214
|
+
function persist2(value) {
|
|
42215
|
+
try {
|
|
42216
|
+
if (value) writeFileSync54(daemonPaths.autoUpdatePaused, "");
|
|
42217
|
+
else rmSync9(daemonPaths.autoUpdatePaused, { force: true });
|
|
42218
|
+
} catch {
|
|
42219
|
+
}
|
|
42220
|
+
}
|
|
42221
|
+
var autoUpdateControl = {
|
|
42222
|
+
paused: () => {
|
|
42223
|
+
paused ??= existsSync86(daemonPaths.autoUpdatePaused);
|
|
42224
|
+
return paused;
|
|
42225
|
+
},
|
|
42226
|
+
setPaused: (value) => {
|
|
42227
|
+
paused = value;
|
|
42228
|
+
persist2(value);
|
|
42229
|
+
wake?.();
|
|
42230
|
+
},
|
|
42231
|
+
sleep: (ms) => new Promise((resolve28) => {
|
|
42232
|
+
const done2 = () => {
|
|
42233
|
+
clearTimeout(timer);
|
|
42234
|
+
if (wake === done2) wake = void 0;
|
|
42235
|
+
resolve28();
|
|
42236
|
+
};
|
|
42237
|
+
const timer = setTimeout(done2, ms);
|
|
42238
|
+
wake = done2;
|
|
42239
|
+
}),
|
|
42240
|
+
wake: () => wake?.()
|
|
42241
|
+
};
|
|
42242
|
+
|
|
42139
42243
|
// src/commands/sessions/daemon/autoUpdate/autoUpdateEnabled.ts
|
|
42140
42244
|
function autoUpdateEnabled(installDir) {
|
|
42141
42245
|
try {
|
|
@@ -42166,6 +42270,14 @@ var autoUpdateState = {
|
|
|
42166
42270
|
}
|
|
42167
42271
|
};
|
|
42168
42272
|
|
|
42273
|
+
// src/commands/sessions/daemon/autoUpdate/backOff.ts
|
|
42274
|
+
var RETRY_AFTER_FAILURE_MS = 5 * 60 * 1e3;
|
|
42275
|
+
async function backOff(reason4, deps2) {
|
|
42276
|
+
deps2.enter("retrying", { reason: reason4 });
|
|
42277
|
+
deps2.note(`${reason4}; retrying in ${RETRY_AFTER_FAILURE_MS / 6e4}m`);
|
|
42278
|
+
await deps2.sleep(RETRY_AFTER_FAILURE_MS);
|
|
42279
|
+
}
|
|
42280
|
+
|
|
42169
42281
|
// src/commands/sessions/daemon/autoUpdate/holdForEscalation.ts
|
|
42170
42282
|
var ESCALATION_POLL_MS = 5e3;
|
|
42171
42283
|
async function holdForEscalation(sessionId, deps2) {
|
|
@@ -42186,50 +42298,47 @@ async function runLapCapturingTail(deps2) {
|
|
|
42186
42298
|
return { end, tail };
|
|
42187
42299
|
}
|
|
42188
42300
|
|
|
42189
|
-
// src/commands/sessions/daemon/autoUpdate/
|
|
42301
|
+
// src/commands/sessions/daemon/autoUpdate/settleLap.ts
|
|
42190
42302
|
var DIVERGED_EXIT_CODE = 3;
|
|
42191
|
-
|
|
42303
|
+
async function settleLap(lap, { end, tail }, deps2) {
|
|
42304
|
+
const how = end.signal ? `killed by ${end.signal}` : `exited ${end.code}`;
|
|
42305
|
+
if (end.code === DIVERGED_EXIT_CODE) {
|
|
42306
|
+
const held = deps2.escalate(tail);
|
|
42307
|
+
deps2.note(`lap ${lap} ${how} (divergence); escalated to session ${held}`);
|
|
42308
|
+
return held;
|
|
42309
|
+
}
|
|
42310
|
+
if (deps2.paused()) deps2.note(`lap ${lap} ${how}; paused`);
|
|
42311
|
+
else if (decideLap(end, false).kind === "relaunch")
|
|
42312
|
+
deps2.note(`lap ${lap} ${how}; relaunching`);
|
|
42313
|
+
else await backOff(`lap ${lap} ${how}`, deps2);
|
|
42314
|
+
return void 0;
|
|
42315
|
+
}
|
|
42316
|
+
|
|
42317
|
+
// src/commands/sessions/daemon/autoUpdate/runAutoUpdateLoop.ts
|
|
42318
|
+
var PAUSE_POLL_MS = 60 * 1e3;
|
|
42192
42319
|
async function runAutoUpdateLoop(deps2, maxLaps = Number.POSITIVE_INFINITY) {
|
|
42193
42320
|
let held = deps2.liveEscalation();
|
|
42194
42321
|
for (let lap = 1; lap <= maxLaps; lap++) {
|
|
42195
42322
|
if (held) await holdForEscalation(held, deps2);
|
|
42196
42323
|
held = void 0;
|
|
42324
|
+
while (deps2.paused()) await deps2.sleep(PAUSE_POLL_MS);
|
|
42197
42325
|
deps2.enter("waiting");
|
|
42198
42326
|
deps2.note(
|
|
42199
42327
|
`lap ${lap} at ${(/* @__PURE__ */ new Date()).toLocaleString()}: assist ${WATCH_LAP_ARGS.join(" ")}`
|
|
42200
42328
|
);
|
|
42201
|
-
let result;
|
|
42202
42329
|
try {
|
|
42203
|
-
|
|
42330
|
+
held = await settleLap(lap, await runLapCapturingTail(deps2), deps2);
|
|
42204
42331
|
} catch (error) {
|
|
42205
42332
|
await backOff(
|
|
42206
42333
|
`lap ${lap} could not start (${error instanceof Error ? error.message : String(error)})`,
|
|
42207
42334
|
deps2
|
|
42208
42335
|
);
|
|
42209
|
-
continue;
|
|
42210
42336
|
}
|
|
42211
|
-
const { end, tail } = result;
|
|
42212
|
-
const how = end.signal ? `killed by ${end.signal}` : `exited ${end.code}`;
|
|
42213
|
-
if (end.code === DIVERGED_EXIT_CODE) {
|
|
42214
|
-
held = deps2.escalate(tail);
|
|
42215
|
-
deps2.note(`lap ${lap} ${how} (divergence); escalated to session ${held}`);
|
|
42216
|
-
continue;
|
|
42217
|
-
}
|
|
42218
|
-
if (decideLap(end, false).kind === "relaunch") {
|
|
42219
|
-
deps2.note(`lap ${lap} ${how}; relaunching`);
|
|
42220
|
-
continue;
|
|
42221
|
-
}
|
|
42222
|
-
await backOff(`lap ${lap} ${how}`, deps2);
|
|
42223
42337
|
}
|
|
42224
42338
|
}
|
|
42225
|
-
async function backOff(reason4, deps2) {
|
|
42226
|
-
deps2.enter("retrying", { reason: reason4 });
|
|
42227
|
-
deps2.note(`${reason4}; retrying in ${RETRY_AFTER_FAILURE_MS / 6e4}m`);
|
|
42228
|
-
await deps2.sleep(RETRY_AFTER_FAILURE_MS);
|
|
42229
|
-
}
|
|
42230
42339
|
|
|
42231
42340
|
// src/commands/sessions/daemon/autoUpdate/trimWatcherLog.ts
|
|
42232
|
-
import { readFileSync as readFileSync65, writeFileSync as
|
|
42341
|
+
import { readFileSync as readFileSync65, writeFileSync as writeFileSync55 } from "fs";
|
|
42233
42342
|
var KEEP_TAIL = 256 * 1024;
|
|
42234
42343
|
var MAX_LOG_SIZE = 4 * 1024 * 1024;
|
|
42235
42344
|
function trimWatcherLog(cwd) {
|
|
@@ -42241,20 +42350,10 @@ function trimWatcherLog(cwd) {
|
|
|
42241
42350
|
return;
|
|
42242
42351
|
}
|
|
42243
42352
|
if (log2.length <= MAX_LOG_SIZE) return;
|
|
42244
|
-
|
|
42353
|
+
writeFileSync55(path106, log2.slice(-KEEP_TAIL));
|
|
42245
42354
|
daemonLog(`auto-update: log ${path106} trimmed to its last ${KEEP_TAIL} chars`);
|
|
42246
42355
|
}
|
|
42247
42356
|
|
|
42248
|
-
// src/commands/sessions/daemon/autoUpdate/loopDeps.ts
|
|
42249
|
-
import { setTimeout as sleep2 } from "timers/promises";
|
|
42250
|
-
|
|
42251
|
-
// src/commands/sessions/daemon/watcherNote.ts
|
|
42252
|
-
function watcherNote(text17) {
|
|
42253
|
-
return `\r
|
|
42254
|
-
\x1B[2m\u2500\u2500 ${text17} \u2500\u2500\x1B[0m\r
|
|
42255
|
-
`;
|
|
42256
|
-
}
|
|
42257
|
-
|
|
42258
42357
|
// src/commands/sessions/daemon/worktree/appendWatcherLog.ts
|
|
42259
42358
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync34 } from "fs";
|
|
42260
42359
|
import { dirname as dirname42 } from "path";
|
|
@@ -42424,9 +42523,30 @@ function lineRecorder(onLine) {
|
|
|
42424
42523
|
};
|
|
42425
42524
|
}
|
|
42426
42525
|
|
|
42526
|
+
// src/commands/sessions/daemon/watcherNote.ts
|
|
42527
|
+
function watcherNote(text17) {
|
|
42528
|
+
return `\r
|
|
42529
|
+
\x1B[2m\u2500\u2500 ${text17} \u2500\u2500\x1B[0m\r
|
|
42530
|
+
`;
|
|
42531
|
+
}
|
|
42532
|
+
|
|
42533
|
+
// src/commands/sessions/daemon/autoUpdate/noteAutoUpdate.ts
|
|
42534
|
+
function noteAutoUpdate(installDir, text17) {
|
|
42535
|
+
appendWatcherLog(installDir, watcherNote(text17));
|
|
42536
|
+
daemonLog(`auto-update: ${text17}`);
|
|
42537
|
+
}
|
|
42538
|
+
|
|
42427
42539
|
// src/commands/sessions/daemon/autoUpdate/runUpdateLap.ts
|
|
42428
42540
|
import { spawn as spawn11 } from "child_process";
|
|
42541
|
+
import { rmSync as rmSync10 } from "fs";
|
|
42542
|
+
function clearLapSignals(cwd) {
|
|
42543
|
+
const control = watchControlPaths(cwd);
|
|
42544
|
+
if (!control) return;
|
|
42545
|
+
for (const path106 of [control.check, control.stop])
|
|
42546
|
+
rmSync10(path106, { force: true });
|
|
42547
|
+
}
|
|
42429
42548
|
function runUpdateLap(cwd, onOutput) {
|
|
42549
|
+
clearLapSignals(cwd);
|
|
42430
42550
|
return new Promise((resolve28, reject) => {
|
|
42431
42551
|
const { ASSIST_ACTIVITY_ID: _activity, ...env } = process.env;
|
|
42432
42552
|
const child = spawn11(
|
|
@@ -42466,10 +42586,7 @@ function loopDeps(installDir, ctx) {
|
|
|
42466
42586
|
appendWatcherLog(installDir, text17);
|
|
42467
42587
|
logLine(text17);
|
|
42468
42588
|
},
|
|
42469
|
-
note: (text17) =>
|
|
42470
|
-
appendWatcherLog(installDir, watcherNote(text17));
|
|
42471
|
-
daemonLog(`auto-update: ${text17}`);
|
|
42472
|
-
},
|
|
42589
|
+
note: (text17) => noteAutoUpdate(installDir, text17),
|
|
42473
42590
|
enter: autoUpdateState.enter,
|
|
42474
42591
|
liveEscalation: () => liveEscalationIn(ctx().sessions, clone)?.id,
|
|
42475
42592
|
escalate: (output) => escalateDivergence(ctx(), installDir, output),
|
|
@@ -42477,7 +42594,8 @@ function loopDeps(installDir, ctx) {
|
|
|
42477
42594
|
const status2 = ctx().sessions.get(sessionId)?.status;
|
|
42478
42595
|
return status2 !== void 0 && status2 !== "done" && status2 !== "error";
|
|
42479
42596
|
},
|
|
42480
|
-
|
|
42597
|
+
paused: autoUpdateControl.paused,
|
|
42598
|
+
sleep: autoUpdateControl.sleep
|
|
42481
42599
|
};
|
|
42482
42600
|
}
|
|
42483
42601
|
|
|
@@ -42498,7 +42616,9 @@ function startAutoUpdate(ctx) {
|
|
|
42498
42616
|
return;
|
|
42499
42617
|
}
|
|
42500
42618
|
trimWatcherLog(installDir);
|
|
42501
|
-
daemonLog(
|
|
42619
|
+
daemonLog(
|
|
42620
|
+
autoUpdateControl.paused() ? `auto-update: paused for ${installDir}; resume it from the Updates page` : `auto-update: pulling and building ${installDir} as origin moves`
|
|
42621
|
+
);
|
|
42502
42622
|
void runAutoUpdateLoop(loopDeps(installDir, ctx)).catch(
|
|
42503
42623
|
(error) => daemonLog(
|
|
42504
42624
|
`auto-update: loop stopped: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -42527,11 +42647,11 @@ function setStatus2(session, newStatus) {
|
|
|
42527
42647
|
}
|
|
42528
42648
|
|
|
42529
42649
|
// src/commands/sessions/daemon/worktree/reapWorktree.ts
|
|
42530
|
-
import { existsSync as
|
|
42650
|
+
import { existsSync as existsSync88 } from "fs";
|
|
42531
42651
|
import { basename as basename30 } from "path";
|
|
42532
42652
|
|
|
42533
42653
|
// src/commands/sessions/daemon/worktree/deleteStrandedTree.ts
|
|
42534
|
-
import { existsSync as
|
|
42654
|
+
import { existsSync as existsSync87 } from "fs";
|
|
42535
42655
|
import { join as join103 } from "path";
|
|
42536
42656
|
|
|
42537
42657
|
// src/commands/sessions/daemon/worktree/deleteTreeDirectly.ts
|
|
@@ -42601,7 +42721,7 @@ async function deleteStrandedTree(clone, worktreePath, cause) {
|
|
|
42601
42721
|
);
|
|
42602
42722
|
}
|
|
42603
42723
|
function strandedReason(worktreePath, cause) {
|
|
42604
|
-
if (!
|
|
42724
|
+
if (!existsSync87(join103(worktreePath, ".git")))
|
|
42605
42725
|
return "its .git link is already gone";
|
|
42606
42726
|
if (/not a working tree|not a git repository/i.test(reason2(cause)))
|
|
42607
42727
|
return "git no longer recognises it as a working tree";
|
|
@@ -42653,7 +42773,7 @@ function reason3(error) {
|
|
|
42653
42773
|
|
|
42654
42774
|
// src/commands/sessions/daemon/worktree/reapWorktree.ts
|
|
42655
42775
|
async function reapWorktree(worktreePath, force = false) {
|
|
42656
|
-
if (!
|
|
42776
|
+
if (!existsSync88(worktreePath)) {
|
|
42657
42777
|
forgetWorktree(worktreePath);
|
|
42658
42778
|
daemonLog(
|
|
42659
42779
|
`worktree ${worktreePath} already gone; its record was forgotten`
|
|
@@ -42678,7 +42798,7 @@ async function reapWorktree(worktreePath, force = false) {
|
|
|
42678
42798
|
}
|
|
42679
42799
|
function owningClone(worktreePath) {
|
|
42680
42800
|
const recorded = worktreeAttributionIncludingReaped(worktreePath)?.clone;
|
|
42681
|
-
if (recorded &&
|
|
42801
|
+
if (recorded && existsSync88(recorded)) return recorded;
|
|
42682
42802
|
const detected = mainWorktree(worktreePath);
|
|
42683
42803
|
if (detected) return detected;
|
|
42684
42804
|
daemonLog(
|
|
@@ -42881,12 +43001,12 @@ async function closeBlockReason(sessionId, tree) {
|
|
|
42881
43001
|
}
|
|
42882
43002
|
|
|
42883
43003
|
// src/commands/sessions/daemon/worktree/watchGitState.ts
|
|
42884
|
-
import { existsSync as
|
|
43004
|
+
import { existsSync as existsSync89, watch } from "fs";
|
|
42885
43005
|
var DEBOUNCE_MS = 500;
|
|
42886
43006
|
var POLL_MS2 = 3e4;
|
|
42887
43007
|
function watchGitState(cwd, onChange) {
|
|
42888
43008
|
const common = gitCommonDir(cwd);
|
|
42889
|
-
if (!common || !
|
|
43009
|
+
if (!common || !existsSync89(common)) return void 0;
|
|
42890
43010
|
const watchers = [
|
|
42891
43011
|
watchGitDir(common, onChange),
|
|
42892
43012
|
pollGitState(cwd, onChange)
|
|
@@ -43433,10 +43553,10 @@ function emitSessionOutput(session, clients, data) {
|
|
|
43433
43553
|
}
|
|
43434
43554
|
|
|
43435
43555
|
// src/commands/sessions/daemon/exitReason.ts
|
|
43436
|
-
import { existsSync as
|
|
43556
|
+
import { existsSync as existsSync90 } from "fs";
|
|
43437
43557
|
import { resolve as resolve27 } from "path";
|
|
43438
43558
|
function exitDetail(session) {
|
|
43439
|
-
if (session.cwd && !
|
|
43559
|
+
if (session.cwd && !existsSync90(session.cwd))
|
|
43440
43560
|
return `working directory ${session.cwd} no longer exists`;
|
|
43441
43561
|
return missingRunConfigCwd(session);
|
|
43442
43562
|
}
|
|
@@ -43450,7 +43570,7 @@ function missingRunConfigCwd(session) {
|
|
|
43450
43570
|
const config = resolveRunConfig(session.runName, dir);
|
|
43451
43571
|
if (!config?.cwd) return void 0;
|
|
43452
43572
|
const configured = resolve27(runConfigBaseDirFrom(dir), config.cwd);
|
|
43453
|
-
if (
|
|
43573
|
+
if (existsSync90(configured)) return void 0;
|
|
43454
43574
|
return `run config "${config.name}": cwd ${configured} does not exist`;
|
|
43455
43575
|
}
|
|
43456
43576
|
|
|
@@ -43498,7 +43618,7 @@ function handleRefusedRun(session, exitCode, onStatusChange) {
|
|
|
43498
43618
|
}
|
|
43499
43619
|
|
|
43500
43620
|
// src/commands/sessions/daemon/watchActivity.ts
|
|
43501
|
-
import { existsSync as
|
|
43621
|
+
import { existsSync as existsSync91, mkdirSync as mkdirSync35, watch as watch2 } from "fs";
|
|
43502
43622
|
import { dirname as dirname43 } from "path";
|
|
43503
43623
|
|
|
43504
43624
|
// src/commands/sessions/daemon/applyActivityToSession.ts
|
|
@@ -43584,7 +43704,7 @@ function watchActivity(session, notify2, onClaudeSessionId) {
|
|
|
43584
43704
|
if (timer) clearTimeout(timer);
|
|
43585
43705
|
timer = setTimeout(read3, DEBOUNCE_MS2);
|
|
43586
43706
|
});
|
|
43587
|
-
if (
|
|
43707
|
+
if (existsSync91(path106)) read3();
|
|
43588
43708
|
}
|
|
43589
43709
|
function refreshActivity(session) {
|
|
43590
43710
|
if (session.commandType !== "assist" || !session.cwd) return;
|
|
@@ -45575,7 +45695,7 @@ function rearmStoppedSessions(sessions, notify2) {
|
|
|
45575
45695
|
}
|
|
45576
45696
|
|
|
45577
45697
|
// src/commands/sessions/daemon/worktree/reconcileWorktreesOnRestore.ts
|
|
45578
|
-
import { existsSync as
|
|
45698
|
+
import { existsSync as existsSync94 } from "fs";
|
|
45579
45699
|
import { basename as basename32 } from "path";
|
|
45580
45700
|
|
|
45581
45701
|
// src/commands/sessions/daemon/worktree/accountedTrees.ts
|
|
@@ -45596,9 +45716,9 @@ function accountedTrees(sessions) {
|
|
|
45596
45716
|
}
|
|
45597
45717
|
|
|
45598
45718
|
// src/commands/sessions/daemon/worktree/reclaimVanishedWorktrees.ts
|
|
45599
|
-
import { existsSync as
|
|
45719
|
+
import { existsSync as existsSync93 } from "fs";
|
|
45600
45720
|
async function reclaimVanishedWorktrees(clone, paths) {
|
|
45601
|
-
if (!
|
|
45721
|
+
if (!existsSync93(clone)) {
|
|
45602
45722
|
for (const { path: path106 } of paths) forgetWorktree(path106);
|
|
45603
45723
|
daemonLog(
|
|
45604
45724
|
`clone ${clone} is gone; forgot ${paths.length} worktree record(s) it owned`
|
|
@@ -45764,7 +45884,7 @@ async function recoverOrphanedWorktrees(sessions, spawnWith, notify2) {
|
|
|
45764
45884
|
);
|
|
45765
45885
|
continue;
|
|
45766
45886
|
}
|
|
45767
|
-
if (!
|
|
45887
|
+
if (!existsSync94(path106)) {
|
|
45768
45888
|
logVanishedTree(sessions, path106);
|
|
45769
45889
|
vanished.set(clone, [
|
|
45770
45890
|
...vanished.get(clone) ?? [],
|
|
@@ -46809,7 +46929,7 @@ function spawnAssistInTree(ctx, assistArgs, cwd, meta, context) {
|
|
|
46809
46929
|
}
|
|
46810
46930
|
|
|
46811
46931
|
// src/commands/sessions/daemon/worktree/resumeInTree.ts
|
|
46812
|
-
import { existsSync as
|
|
46932
|
+
import { existsSync as existsSync97 } from "fs";
|
|
46813
46933
|
|
|
46814
46934
|
// src/commands/sessions/daemon/resumeSession.ts
|
|
46815
46935
|
function resumeSession(id, sessionId, cwd, name, holdPty, harness) {
|
|
@@ -46840,10 +46960,10 @@ function resumeSession(id, sessionId, cwd, name, holdPty, harness) {
|
|
|
46840
46960
|
}
|
|
46841
46961
|
|
|
46842
46962
|
// src/commands/sessions/daemon/worktree/resumeInReplacementTree.ts
|
|
46843
|
-
import { existsSync as
|
|
46963
|
+
import { existsSync as existsSync96 } from "fs";
|
|
46844
46964
|
|
|
46845
46965
|
// src/commands/sessions/daemon/worktree/carryTranscriptToTree.ts
|
|
46846
|
-
import { copyFileSync as copyFileSync5, existsSync as
|
|
46966
|
+
import { copyFileSync as copyFileSync5, existsSync as existsSync95, mkdirSync as mkdirSync37 } from "fs";
|
|
46847
46967
|
import { join as join105 } from "path";
|
|
46848
46968
|
function carryTranscriptToTree(claudeSessionId, fromCwd, toCwd) {
|
|
46849
46969
|
const dir = projectDirForCwd(toCwd);
|
|
@@ -46854,7 +46974,7 @@ function carryTranscriptToTree(claudeSessionId, fromCwd, toCwd) {
|
|
|
46854
46974
|
return;
|
|
46855
46975
|
}
|
|
46856
46976
|
const dest = join105(dir, `${claudeSessionId}.jsonl`);
|
|
46857
|
-
if (
|
|
46977
|
+
if (existsSync95(dest)) {
|
|
46858
46978
|
daemonLog(`transcript ${claudeSessionId} already present in ${dir}`);
|
|
46859
46979
|
return;
|
|
46860
46980
|
}
|
|
@@ -46905,7 +47025,7 @@ function resumeInReplacementTree(ctx, claudeSessionId, missingCwd, name, harness
|
|
|
46905
47025
|
}
|
|
46906
47026
|
function cloneForReapedTree(missingCwd) {
|
|
46907
47027
|
const clone = worktreeAttributionIncludingReaped(missingCwd)?.clone;
|
|
46908
|
-
if (!clone || !
|
|
47028
|
+
if (!clone || !existsSync96(clone))
|
|
46909
47029
|
throw new Error(
|
|
46910
47030
|
`working directory no longer exists and no clone is recorded to re-allocate from: ${missingCwd}`
|
|
46911
47031
|
);
|
|
@@ -46914,7 +47034,7 @@ function cloneForReapedTree(missingCwd) {
|
|
|
46914
47034
|
|
|
46915
47035
|
// src/commands/sessions/daemon/worktree/resumeInTree.ts
|
|
46916
47036
|
function resumeInTree(ctx, sessionId, cwd, name, harness) {
|
|
46917
|
-
if (!
|
|
47037
|
+
if (!existsSync97(cwd))
|
|
46918
47038
|
return resumeInReplacementTree(ctx, sessionId, cwd, name, harness);
|
|
46919
47039
|
const id = ctx.spawnWith(
|
|
46920
47040
|
(sid) => resumeSession(sid, sessionId, cwd, name, void 0, harness)
|
|
@@ -47107,10 +47227,60 @@ function autoUpdateReply() {
|
|
|
47107
47227
|
type: "auto-update",
|
|
47108
47228
|
version: ASSIST_VERSION,
|
|
47109
47229
|
startCommit: autoUpdateState.startCommit(),
|
|
47110
|
-
loop: autoUpdateState.loop()
|
|
47230
|
+
loop: { ...autoUpdateState.loop(), paused: autoUpdateControl.paused() }
|
|
47111
47231
|
};
|
|
47112
47232
|
}
|
|
47113
47233
|
|
|
47234
|
+
// src/commands/sessions/daemon/autoUpdate/applyAutoUpdateAction.ts
|
|
47235
|
+
import { rmSync as rmSync11, writeFileSync as writeFileSync56 } from "fs";
|
|
47236
|
+
function signalLap(installDir, marker2) {
|
|
47237
|
+
const control = watchControlPaths(installDir);
|
|
47238
|
+
if (control) writeFileSync56(control[marker2], "");
|
|
47239
|
+
}
|
|
47240
|
+
function applyAutoUpdateAction(action) {
|
|
47241
|
+
const installDir = getInstallDir();
|
|
47242
|
+
const loop2 = autoUpdateState.loop();
|
|
47243
|
+
if (action === "resume") {
|
|
47244
|
+
if (!autoUpdateControl.paused()) return void 0;
|
|
47245
|
+
const control = watchControlPaths(installDir);
|
|
47246
|
+
if (control) rmSync11(control.stop, { force: true });
|
|
47247
|
+
autoUpdateControl.setPaused(false);
|
|
47248
|
+
noteAutoUpdate(installDir, "resumed from Updates");
|
|
47249
|
+
return void 0;
|
|
47250
|
+
}
|
|
47251
|
+
if (loop2.phase === "off")
|
|
47252
|
+
return `auto-update is off (${loop2.reason ?? "not running"})`;
|
|
47253
|
+
if (action === "pause") {
|
|
47254
|
+
if (autoUpdateControl.paused()) return void 0;
|
|
47255
|
+
autoUpdateControl.setPaused(true);
|
|
47256
|
+
signalLap(installDir, "stop");
|
|
47257
|
+
noteAutoUpdate(installDir, "paused from Updates");
|
|
47258
|
+
return void 0;
|
|
47259
|
+
}
|
|
47260
|
+
if (autoUpdateControl.paused())
|
|
47261
|
+
return "auto-update is paused; resume it first";
|
|
47262
|
+
signalLap(installDir, "check");
|
|
47263
|
+
autoUpdateControl.wake();
|
|
47264
|
+
noteAutoUpdate(installDir, "check requested from Updates");
|
|
47265
|
+
return void 0;
|
|
47266
|
+
}
|
|
47267
|
+
|
|
47268
|
+
// src/commands/sessions/daemon/autoUpdate/handleAutoUpdateControl.ts
|
|
47269
|
+
function tryAction(action) {
|
|
47270
|
+
if (!AUTO_UPDATE_ACTIONS.includes(action))
|
|
47271
|
+
return `unknown auto-update action ${String(action)}`;
|
|
47272
|
+
try {
|
|
47273
|
+
return applyAutoUpdateAction(action);
|
|
47274
|
+
} catch (error) {
|
|
47275
|
+
return error instanceof Error ? error.message : String(error);
|
|
47276
|
+
}
|
|
47277
|
+
}
|
|
47278
|
+
var handleAutoUpdateControl = (client, _m, d) => {
|
|
47279
|
+
const error = tryAction(d.action);
|
|
47280
|
+
daemonLog(`auto-update-control ${String(d.action)}: ${error ?? "ok"}`);
|
|
47281
|
+
sendTo(client, { type: "auto-update-control", ok: !error, error });
|
|
47282
|
+
};
|
|
47283
|
+
|
|
47114
47284
|
// src/commands/sessions/daemon/handleHelloRequest.ts
|
|
47115
47285
|
var handleHelloRequest = (client, m, d) => {
|
|
47116
47286
|
sendTo(client, buildHello());
|
|
@@ -47637,6 +47807,7 @@ var messageHandlers = {
|
|
|
47637
47807
|
output: handleOutputRequest,
|
|
47638
47808
|
nodes: (client, m) => m.links.sendNodes(client),
|
|
47639
47809
|
"auto-update": (client) => sendTo(client, autoUpdateReply()),
|
|
47810
|
+
"auto-update-control": handleAutoUpdateControl,
|
|
47640
47811
|
"reload-links": (client, m) => {
|
|
47641
47812
|
daemonLog("reload-links received");
|
|
47642
47813
|
m.links.reload();
|
|
@@ -47753,7 +47924,7 @@ function handleConnection(socket, manager) {
|
|
|
47753
47924
|
}
|
|
47754
47925
|
|
|
47755
47926
|
// src/commands/sessions/daemon/onListening.ts
|
|
47756
|
-
import { unlinkSync as unlinkSync22, writeFileSync as
|
|
47927
|
+
import { unlinkSync as unlinkSync22, writeFileSync as writeFileSync57 } from "fs";
|
|
47757
47928
|
|
|
47758
47929
|
// src/commands/sessions/daemon/startPidFileWatchdog.ts
|
|
47759
47930
|
import { readFileSync as readFileSync66 } from "fs";
|
|
@@ -47775,7 +47946,7 @@ function ownsPidFile() {
|
|
|
47775
47946
|
|
|
47776
47947
|
// src/commands/sessions/daemon/onListening.ts
|
|
47777
47948
|
function onListening(manager, checkAutoExit) {
|
|
47778
|
-
|
|
47949
|
+
writeFileSync57(daemonPaths.pid, String(process.pid));
|
|
47779
47950
|
startPidFileWatchdog(() => {
|
|
47780
47951
|
daemonLog("lost daemon.pid ownership; shutting down sessions and exiting");
|
|
47781
47952
|
void manager.flushActiveMs().finally(() => {
|
|
@@ -48161,14 +48332,14 @@ async function linkStatus(name, query = queryNodes) {
|
|
|
48161
48332
|
return (await query())?.links.find((l) => l.name === name);
|
|
48162
48333
|
}
|
|
48163
48334
|
async function awaitLinkReturn(name, options2, deps2 = {}) {
|
|
48164
|
-
const { query = queryNodes, sleep:
|
|
48335
|
+
const { query = queryNodes, sleep: sleep2 = wait, now = Date.now } = deps2;
|
|
48165
48336
|
const deadline = now() + options2.timeoutMs;
|
|
48166
48337
|
let sawDown = options2.sawDown;
|
|
48167
48338
|
while (now() < deadline) {
|
|
48168
48339
|
const link3 = await linkStatus(name, query);
|
|
48169
48340
|
if (link3?.state !== "connected") sawDown = true;
|
|
48170
48341
|
else if (sawDown) return link3;
|
|
48171
|
-
await
|
|
48342
|
+
await sleep2(POLL_MS4);
|
|
48172
48343
|
}
|
|
48173
48344
|
return void 0;
|
|
48174
48345
|
}
|