@staff0rd/assist 0.358.0 → 0.360.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/dist/commands/sessions/web/bundle.js +86 -86
- package/dist/index.js +189 -142
- 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.360.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -21975,6 +21975,187 @@ function recordSessionUsage(sessions, claudeSessionId, totalIn, totalOut) {
|
|
|
21975
21975
|
}
|
|
21976
21976
|
}
|
|
21977
21977
|
|
|
21978
|
+
// src/commands/sessions/daemon/assistResumeArgs.ts
|
|
21979
|
+
function assistResumeArgs(session) {
|
|
21980
|
+
const args = ["assist", ...session.assistArgs];
|
|
21981
|
+
return session.claudeSessionId ? [...args, "--resume-session", session.claudeSessionId] : args;
|
|
21982
|
+
}
|
|
21983
|
+
|
|
21984
|
+
// src/commands/sessions/daemon/watchActivity.ts
|
|
21985
|
+
import { existsSync as existsSync53, mkdirSync as mkdirSync23, watch } from "fs";
|
|
21986
|
+
import { dirname as dirname26 } from "path";
|
|
21987
|
+
|
|
21988
|
+
// src/commands/sessions/daemon/applyReviewPause.ts
|
|
21989
|
+
function applyReviewPause(session, activity2) {
|
|
21990
|
+
const inReview = activity2.kind === "backlog" && activity2.phase !== void 0 && activity2.phase === activity2.totalPhases;
|
|
21991
|
+
if (inReview && !session.reviewStarted) {
|
|
21992
|
+
session.autoAdvance = false;
|
|
21993
|
+
session.reviewStarted = true;
|
|
21994
|
+
} else if (!inReview && session.reviewStarted) {
|
|
21995
|
+
session.reviewStarted = false;
|
|
21996
|
+
}
|
|
21997
|
+
}
|
|
21998
|
+
|
|
21999
|
+
// src/shared/db/getPhaseActiveMs.ts
|
|
22000
|
+
import { and as and17, eq as eq36 } from "drizzle-orm";
|
|
22001
|
+
async function getPhaseActiveMs(db, itemId, phaseIdx) {
|
|
22002
|
+
const [row] = await db.select({ activeMs: phaseUsage.activeMs }).from(phaseUsage).where(
|
|
22003
|
+
and17(eq36(phaseUsage.itemId, itemId), eq36(phaseUsage.phaseIdx, phaseIdx))
|
|
22004
|
+
);
|
|
22005
|
+
return row?.activeMs ?? 0;
|
|
22006
|
+
}
|
|
22007
|
+
|
|
22008
|
+
// src/commands/sessions/daemon/loadPhaseActiveMs.ts
|
|
22009
|
+
async function loadPhaseActiveMs(itemId, phaseIdx) {
|
|
22010
|
+
try {
|
|
22011
|
+
if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url)
|
|
22012
|
+
return null;
|
|
22013
|
+
return await getPhaseActiveMs(await getDb(), itemId, phaseIdx);
|
|
22014
|
+
} catch (error) {
|
|
22015
|
+
daemonLog(`phase-active-ms load failed: ${String(error)}`);
|
|
22016
|
+
return null;
|
|
22017
|
+
}
|
|
22018
|
+
}
|
|
22019
|
+
|
|
22020
|
+
// src/commands/sessions/daemon/seedRunningMsFromUsage.ts
|
|
22021
|
+
async function seedRunningMsFromUsage(session, notify2) {
|
|
22022
|
+
if (session.usageSeeded) return;
|
|
22023
|
+
const phase = sessionBacklogPhase(session);
|
|
22024
|
+
if (!phase) return;
|
|
22025
|
+
session.usageSeeded = true;
|
|
22026
|
+
const priorMs = await loadPhaseActiveMs(phase.itemId, phase.phaseIdx);
|
|
22027
|
+
if (priorMs == null || priorMs <= session.runningMs) return;
|
|
22028
|
+
session.runningMs = priorMs;
|
|
22029
|
+
daemonLog(
|
|
22030
|
+
`session ${session.id} seeded runningMs=${priorMs} from item ${phase.itemId} phase ${phase.phaseIdx} recorded usage`
|
|
22031
|
+
);
|
|
22032
|
+
notify2();
|
|
22033
|
+
}
|
|
22034
|
+
|
|
22035
|
+
// src/commands/sessions/daemon/watchActivity.ts
|
|
22036
|
+
var DEBOUNCE_MS = 50;
|
|
22037
|
+
function watchActivity(session, notify2) {
|
|
22038
|
+
if (session.commandType !== "assist" || !session.cwd) return;
|
|
22039
|
+
const path57 = activityPath(session.id);
|
|
22040
|
+
const dir = dirname26(path57);
|
|
22041
|
+
try {
|
|
22042
|
+
mkdirSync23(dir, { recursive: true });
|
|
22043
|
+
} catch {
|
|
22044
|
+
return;
|
|
22045
|
+
}
|
|
22046
|
+
reconcileActivity(session.id, session.activity);
|
|
22047
|
+
let timer = null;
|
|
22048
|
+
const read = () => {
|
|
22049
|
+
timer = null;
|
|
22050
|
+
const activity2 = readActivity(path57);
|
|
22051
|
+
if (!activity2) return;
|
|
22052
|
+
session.activity = activity2;
|
|
22053
|
+
if (activity2.claudeSessionId)
|
|
22054
|
+
session.claudeSessionId = activity2.claudeSessionId;
|
|
22055
|
+
applyReviewPause(session, activity2);
|
|
22056
|
+
void seedRunningMsFromUsage(session, notify2);
|
|
22057
|
+
notify2();
|
|
22058
|
+
};
|
|
22059
|
+
session.activityWatcher = watch(dir, (_event, filename) => {
|
|
22060
|
+
if (filename && !path57.endsWith(filename)) return;
|
|
22061
|
+
if (timer) clearTimeout(timer);
|
|
22062
|
+
timer = setTimeout(read, DEBOUNCE_MS);
|
|
22063
|
+
});
|
|
22064
|
+
if (existsSync53(path57)) read();
|
|
22065
|
+
}
|
|
22066
|
+
function refreshActivity(session) {
|
|
22067
|
+
if (session.commandType !== "assist" || !session.cwd) return;
|
|
22068
|
+
const activity2 = readActivity(activityPath(session.id));
|
|
22069
|
+
if (!activity2) return;
|
|
22070
|
+
session.activity = activity2;
|
|
22071
|
+
if (activity2.claudeSessionId)
|
|
22072
|
+
session.claudeSessionId = activity2.claudeSessionId;
|
|
22073
|
+
}
|
|
22074
|
+
|
|
22075
|
+
// src/commands/sessions/daemon/wirePtyEvents.ts
|
|
22076
|
+
var MAX_SCROLLBACK = 256 * 1024;
|
|
22077
|
+
function appendScrollback(session, data) {
|
|
22078
|
+
session.scrollback += data;
|
|
22079
|
+
if (session.scrollback.length > MAX_SCROLLBACK) {
|
|
22080
|
+
session.scrollback = session.scrollback.slice(-MAX_SCROLLBACK);
|
|
22081
|
+
}
|
|
22082
|
+
}
|
|
22083
|
+
function wirePtyEvents(session, clients, onStatusChange) {
|
|
22084
|
+
if (!session.pty) return;
|
|
22085
|
+
session.pty.onData((data) => {
|
|
22086
|
+
appendScrollback(session, data);
|
|
22087
|
+
noteOutputForEscInterrupt(session, onStatusChange);
|
|
22088
|
+
broadcast(clients, { type: "output", sessionId: session.id, data });
|
|
22089
|
+
});
|
|
22090
|
+
session.pty.onExit(({ exitCode }) => {
|
|
22091
|
+
refreshActivity(session);
|
|
22092
|
+
const failedResume = session.restored === true && exitCode !== 0 && session.scrollback.length === 0;
|
|
22093
|
+
if (failedResume) {
|
|
22094
|
+
session.error = `resume process exited with code ${exitCode} before producing any output`;
|
|
22095
|
+
daemonLog(
|
|
22096
|
+
`could not resume restored session "${session.name}" (id ${session.id}): ${session.error}`
|
|
22097
|
+
);
|
|
22098
|
+
onStatusChange(session, "error", exitCode);
|
|
22099
|
+
return;
|
|
22100
|
+
}
|
|
22101
|
+
const priorStatus = session.status;
|
|
22102
|
+
const unexpected = priorStatus === "waiting" || exitCode !== 0;
|
|
22103
|
+
daemonLog(
|
|
22104
|
+
`session ${session.id} ("${session.name}") pty exited with code ${exitCode} from status "${priorStatus}" (${session.scrollback.length} bytes of output) \u2014 ${unexpected ? "unexpected exit, process died mid-session" : "expected completion"}; marking done`
|
|
22105
|
+
);
|
|
22106
|
+
onStatusChange(session, "done", exitCode);
|
|
22107
|
+
});
|
|
22108
|
+
}
|
|
22109
|
+
|
|
22110
|
+
// src/commands/sessions/daemon/respawnSession.ts
|
|
22111
|
+
function respawnSession(session, respawn, status2, clients, onStatusChange) {
|
|
22112
|
+
session.scrollback = "";
|
|
22113
|
+
session.startedAt = Date.now();
|
|
22114
|
+
session.runningMs = 0;
|
|
22115
|
+
session.runningSince = null;
|
|
22116
|
+
setStatus2(session, status2);
|
|
22117
|
+
session.restored = void 0;
|
|
22118
|
+
session.pty = respawn();
|
|
22119
|
+
broadcast(clients, { type: "clear", sessionId: session.id });
|
|
22120
|
+
wirePtyEvents(session, clients, onStatusChange);
|
|
22121
|
+
}
|
|
22122
|
+
|
|
22123
|
+
// src/commands/sessions/daemon/restartSession.ts
|
|
22124
|
+
function restartSession(session, clients, onStatusChange) {
|
|
22125
|
+
const plan2 = respawnPlan(session);
|
|
22126
|
+
if (!plan2) return false;
|
|
22127
|
+
session.pty?.kill();
|
|
22128
|
+
respawnSession(session, plan2.spawn, plan2.status, clients, onStatusChange);
|
|
22129
|
+
const via = session.claudeSessionId ? `resume ${session.claudeSessionId}` : "fresh";
|
|
22130
|
+
daemonLog(`session ${session.id} restarted (${via}): ${session.name}`);
|
|
22131
|
+
return true;
|
|
22132
|
+
}
|
|
22133
|
+
function respawnPlan(session) {
|
|
22134
|
+
const { commandType, claudeSessionId, cwd, assistArgs } = session;
|
|
22135
|
+
if (commandType === "claude")
|
|
22136
|
+
return claudeSessionId ? {
|
|
22137
|
+
spawn: () => spawnClaude2({
|
|
22138
|
+
resumeSessionId: claudeSessionId,
|
|
22139
|
+
cwd,
|
|
22140
|
+
sessionId: session.id
|
|
22141
|
+
}),
|
|
22142
|
+
status: "waiting"
|
|
22143
|
+
} : null;
|
|
22144
|
+
if (commandType === "assist" && assistArgs) {
|
|
22145
|
+
const idle = session.status === "waiting";
|
|
22146
|
+
return {
|
|
22147
|
+
spawn: () => spawnPty(
|
|
22148
|
+
assistResumeArgs({ assistArgs, claudeSessionId }),
|
|
22149
|
+
cwd,
|
|
22150
|
+
session.id,
|
|
22151
|
+
idle ? { ASSIST_RESUME_IDLE: "1" } : void 0
|
|
22152
|
+
),
|
|
22153
|
+
status: idle ? "waiting" : "running"
|
|
22154
|
+
};
|
|
22155
|
+
}
|
|
22156
|
+
return null;
|
|
22157
|
+
}
|
|
22158
|
+
|
|
21978
22159
|
// src/commands/sessions/daemon/restoreBase.ts
|
|
21979
22160
|
function restoreBase(id2, persisted) {
|
|
21980
22161
|
return {
|
|
@@ -22005,12 +22186,6 @@ function errorSession(id2, persisted, error) {
|
|
|
22005
22186
|
};
|
|
22006
22187
|
}
|
|
22007
22188
|
|
|
22008
|
-
// src/commands/sessions/daemon/assistResumeArgs.ts
|
|
22009
|
-
function assistResumeArgs(persisted) {
|
|
22010
|
-
const args = ["assist", ...persisted.assistArgs];
|
|
22011
|
-
return persisted.claudeSessionId ? [...args, "--resume-session", persisted.claudeSessionId] : args;
|
|
22012
|
-
}
|
|
22013
|
-
|
|
22014
22189
|
// src/commands/sessions/daemon/runningSession.ts
|
|
22015
22190
|
function runningSession(base, persisted, pty2) {
|
|
22016
22191
|
return restoredSession(base, persisted, pty2, "running");
|
|
@@ -22194,146 +22369,12 @@ function resumeSession(id2, sessionId, cwd, name) {
|
|
|
22194
22369
|
};
|
|
22195
22370
|
}
|
|
22196
22371
|
|
|
22197
|
-
// src/commands/sessions/daemon/watchActivity.ts
|
|
22198
|
-
import { existsSync as existsSync53, mkdirSync as mkdirSync23, watch } from "fs";
|
|
22199
|
-
import { dirname as dirname26 } from "path";
|
|
22200
|
-
|
|
22201
|
-
// src/commands/sessions/daemon/applyReviewPause.ts
|
|
22202
|
-
function applyReviewPause(session, activity2) {
|
|
22203
|
-
const inReview = activity2.kind === "backlog" && activity2.phase !== void 0 && activity2.phase === activity2.totalPhases;
|
|
22204
|
-
if (inReview && !session.reviewStarted) {
|
|
22205
|
-
session.autoAdvance = false;
|
|
22206
|
-
session.reviewStarted = true;
|
|
22207
|
-
} else if (!inReview && session.reviewStarted) {
|
|
22208
|
-
session.reviewStarted = false;
|
|
22209
|
-
}
|
|
22210
|
-
}
|
|
22211
|
-
|
|
22212
|
-
// src/shared/db/getPhaseActiveMs.ts
|
|
22213
|
-
import { and as and17, eq as eq36 } from "drizzle-orm";
|
|
22214
|
-
async function getPhaseActiveMs(db, itemId, phaseIdx) {
|
|
22215
|
-
const [row] = await db.select({ activeMs: phaseUsage.activeMs }).from(phaseUsage).where(
|
|
22216
|
-
and17(eq36(phaseUsage.itemId, itemId), eq36(phaseUsage.phaseIdx, phaseIdx))
|
|
22217
|
-
);
|
|
22218
|
-
return row?.activeMs ?? 0;
|
|
22219
|
-
}
|
|
22220
|
-
|
|
22221
|
-
// src/commands/sessions/daemon/loadPhaseActiveMs.ts
|
|
22222
|
-
async function loadPhaseActiveMs(itemId, phaseIdx) {
|
|
22223
|
-
try {
|
|
22224
|
-
if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url)
|
|
22225
|
-
return null;
|
|
22226
|
-
return await getPhaseActiveMs(await getDb(), itemId, phaseIdx);
|
|
22227
|
-
} catch (error) {
|
|
22228
|
-
daemonLog(`phase-active-ms load failed: ${String(error)}`);
|
|
22229
|
-
return null;
|
|
22230
|
-
}
|
|
22231
|
-
}
|
|
22232
|
-
|
|
22233
|
-
// src/commands/sessions/daemon/seedRunningMsFromUsage.ts
|
|
22234
|
-
async function seedRunningMsFromUsage(session, notify2) {
|
|
22235
|
-
if (session.usageSeeded) return;
|
|
22236
|
-
const phase = sessionBacklogPhase(session);
|
|
22237
|
-
if (!phase) return;
|
|
22238
|
-
session.usageSeeded = true;
|
|
22239
|
-
const priorMs = await loadPhaseActiveMs(phase.itemId, phase.phaseIdx);
|
|
22240
|
-
if (priorMs == null || priorMs <= session.runningMs) return;
|
|
22241
|
-
session.runningMs = priorMs;
|
|
22242
|
-
daemonLog(
|
|
22243
|
-
`session ${session.id} seeded runningMs=${priorMs} from item ${phase.itemId} phase ${phase.phaseIdx} recorded usage`
|
|
22244
|
-
);
|
|
22245
|
-
notify2();
|
|
22246
|
-
}
|
|
22247
|
-
|
|
22248
|
-
// src/commands/sessions/daemon/watchActivity.ts
|
|
22249
|
-
var DEBOUNCE_MS = 50;
|
|
22250
|
-
function watchActivity(session, notify2) {
|
|
22251
|
-
if (session.commandType !== "assist" || !session.cwd) return;
|
|
22252
|
-
const path57 = activityPath(session.id);
|
|
22253
|
-
const dir = dirname26(path57);
|
|
22254
|
-
try {
|
|
22255
|
-
mkdirSync23(dir, { recursive: true });
|
|
22256
|
-
} catch {
|
|
22257
|
-
return;
|
|
22258
|
-
}
|
|
22259
|
-
reconcileActivity(session.id, session.activity);
|
|
22260
|
-
let timer = null;
|
|
22261
|
-
const read = () => {
|
|
22262
|
-
timer = null;
|
|
22263
|
-
const activity2 = readActivity(path57);
|
|
22264
|
-
if (!activity2) return;
|
|
22265
|
-
session.activity = activity2;
|
|
22266
|
-
if (activity2.claudeSessionId)
|
|
22267
|
-
session.claudeSessionId = activity2.claudeSessionId;
|
|
22268
|
-
applyReviewPause(session, activity2);
|
|
22269
|
-
void seedRunningMsFromUsage(session, notify2);
|
|
22270
|
-
notify2();
|
|
22271
|
-
};
|
|
22272
|
-
session.activityWatcher = watch(dir, (_event, filename) => {
|
|
22273
|
-
if (filename && !path57.endsWith(filename)) return;
|
|
22274
|
-
if (timer) clearTimeout(timer);
|
|
22275
|
-
timer = setTimeout(read, DEBOUNCE_MS);
|
|
22276
|
-
});
|
|
22277
|
-
if (existsSync53(path57)) read();
|
|
22278
|
-
}
|
|
22279
|
-
function refreshActivity(session) {
|
|
22280
|
-
if (session.commandType !== "assist" || !session.cwd) return;
|
|
22281
|
-
const activity2 = readActivity(activityPath(session.id));
|
|
22282
|
-
if (!activity2) return;
|
|
22283
|
-
session.activity = activity2;
|
|
22284
|
-
if (activity2.claudeSessionId)
|
|
22285
|
-
session.claudeSessionId = activity2.claudeSessionId;
|
|
22286
|
-
}
|
|
22287
|
-
|
|
22288
|
-
// src/commands/sessions/daemon/wirePtyEvents.ts
|
|
22289
|
-
var MAX_SCROLLBACK = 256 * 1024;
|
|
22290
|
-
function appendScrollback(session, data) {
|
|
22291
|
-
session.scrollback += data;
|
|
22292
|
-
if (session.scrollback.length > MAX_SCROLLBACK) {
|
|
22293
|
-
session.scrollback = session.scrollback.slice(-MAX_SCROLLBACK);
|
|
22294
|
-
}
|
|
22295
|
-
}
|
|
22296
|
-
function wirePtyEvents(session, clients, onStatusChange) {
|
|
22297
|
-
if (!session.pty) return;
|
|
22298
|
-
session.pty.onData((data) => {
|
|
22299
|
-
appendScrollback(session, data);
|
|
22300
|
-
noteOutputForEscInterrupt(session, onStatusChange);
|
|
22301
|
-
broadcast(clients, { type: "output", sessionId: session.id, data });
|
|
22302
|
-
});
|
|
22303
|
-
session.pty.onExit(({ exitCode }) => {
|
|
22304
|
-
refreshActivity(session);
|
|
22305
|
-
const failedResume = session.restored === true && exitCode !== 0 && session.scrollback.length === 0;
|
|
22306
|
-
if (failedResume) {
|
|
22307
|
-
session.error = `resume process exited with code ${exitCode} before producing any output`;
|
|
22308
|
-
daemonLog(
|
|
22309
|
-
`could not resume restored session "${session.name}" (id ${session.id}): ${session.error}`
|
|
22310
|
-
);
|
|
22311
|
-
onStatusChange(session, "error", exitCode);
|
|
22312
|
-
return;
|
|
22313
|
-
}
|
|
22314
|
-
const priorStatus = session.status;
|
|
22315
|
-
const unexpected = priorStatus === "waiting" || exitCode !== 0;
|
|
22316
|
-
daemonLog(
|
|
22317
|
-
`session ${session.id} ("${session.name}") pty exited with code ${exitCode} from status "${priorStatus}" (${session.scrollback.length} bytes of output) \u2014 ${unexpected ? "unexpected exit, process died mid-session" : "expected completion"}; marking done`
|
|
22318
|
-
);
|
|
22319
|
-
onStatusChange(session, "done", exitCode);
|
|
22320
|
-
});
|
|
22321
|
-
}
|
|
22322
|
-
|
|
22323
22372
|
// src/commands/sessions/daemon/retrySession.ts
|
|
22324
22373
|
function retrySession(session, clients, onStatusChange) {
|
|
22325
22374
|
const respawn = respawnThunk(session);
|
|
22326
22375
|
if (!respawn) return false;
|
|
22327
22376
|
if (session.status !== "done") session.pty?.kill();
|
|
22328
|
-
session
|
|
22329
|
-
session.startedAt = Date.now();
|
|
22330
|
-
session.runningMs = 0;
|
|
22331
|
-
session.runningSince = null;
|
|
22332
|
-
setStatus2(session, "running");
|
|
22333
|
-
session.restored = void 0;
|
|
22334
|
-
session.pty = respawn();
|
|
22335
|
-
broadcast(clients, { type: "clear", sessionId: session.id });
|
|
22336
|
-
wirePtyEvents(session, clients, onStatusChange);
|
|
22377
|
+
respawnSession(session, respawn, "running", clients, onStatusChange);
|
|
22337
22378
|
daemonLog(`session ${session.id} retried: ${session.name}`);
|
|
22338
22379
|
return true;
|
|
22339
22380
|
}
|
|
@@ -23116,6 +23157,11 @@ var SessionManager = class {
|
|
|
23116
23157
|
const s = this.sessions.get(id2);
|
|
23117
23158
|
if (s && retrySession(s, this.clients, this.onStatusChange)) this.notify();
|
|
23118
23159
|
}
|
|
23160
|
+
restart(id2) {
|
|
23161
|
+
const s = this.sessions.get(id2);
|
|
23162
|
+
if (s && restartSession(s, this.clients, this.onStatusChange))
|
|
23163
|
+
this.notify();
|
|
23164
|
+
}
|
|
23119
23165
|
dismissSession = (id2) => {
|
|
23120
23166
|
if (dismissSession(this.sessions, id2)) this.notify();
|
|
23121
23167
|
};
|
|
@@ -23534,6 +23580,7 @@ var messageHandlers = {
|
|
|
23534
23580
|
(_client, m, d) => m.resizeSession(d.sessionId, d.cols, d.rows)
|
|
23535
23581
|
),
|
|
23536
23582
|
retry: routed((_client, m, d) => m.retrySession(d.sessionId)),
|
|
23583
|
+
restart: routed((_client, m, d) => m.restart(d.sessionId)),
|
|
23537
23584
|
dismiss: routed((_client, m, d) => m.dismissSession(d.sessionId)),
|
|
23538
23585
|
"set-autorun": routed(
|
|
23539
23586
|
(_client, m, d) => m.setAutoRun(d.sessionId, d.enabled)
|