@basou/cli 0.9.0 → 0.10.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/index.js +128 -101
- package/dist/index.js.map +1 -1
- package/dist/program.js +128 -101
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/program.js
CHANGED
|
@@ -8,7 +8,8 @@ import { join } from "path";
|
|
|
8
8
|
import {
|
|
9
9
|
ApprovalSchema,
|
|
10
10
|
ApprovalStatusSchema,
|
|
11
|
-
|
|
11
|
+
acquireLock,
|
|
12
|
+
appendChainedEventLocked,
|
|
12
13
|
assertBasouRootSafe,
|
|
13
14
|
basouPaths,
|
|
14
15
|
enumerateApprovals,
|
|
@@ -320,55 +321,63 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
320
321
|
if (approval.status !== "pending") {
|
|
321
322
|
throw new Error(`Approval status mismatch: pending YAML has status=${approval.status}`);
|
|
322
323
|
}
|
|
323
|
-
const sessionDir = join(paths.sessions, approval.session_id);
|
|
324
|
-
for await (const ev of replayEvents(sessionDir, {
|
|
325
|
-
onWarning: (w) => printReplayWarning(w, approval.session_id)
|
|
326
|
-
})) {
|
|
327
|
-
if (isApprovalEvent(ev) && ev.approval_id === approval.id && (ev.type === "approval_approved" || ev.type === "approval_rejected" || ev.type === "approval_expired")) {
|
|
328
|
-
throw new Error(`Approval already resolved (per events.jsonl): ${idInput}`);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
324
|
const now = /* @__PURE__ */ new Date();
|
|
332
|
-
if (isLazyExpired(approval, now)) {
|
|
333
|
-
throw new Error(`Approval already expired: ${idInput}`);
|
|
334
|
-
}
|
|
335
|
-
let sessionStatus = null;
|
|
336
|
-
try {
|
|
337
|
-
sessionStatus = (await readSessionYaml(paths, approval.session_id)).session.status;
|
|
338
|
-
} catch {
|
|
339
|
-
sessionStatus = null;
|
|
340
|
-
}
|
|
341
|
-
if (sessionStatus === "imported") {
|
|
342
|
-
throw new Error(`Cannot resolve an approval for an imported session: ${idInput}`);
|
|
343
|
-
}
|
|
344
325
|
const occurredAt = now.toISOString();
|
|
345
326
|
const eventId = prefixedUlid("evt");
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
327
|
+
const sessionLock = await acquireLock(paths, "session", approval.session_id);
|
|
328
|
+
try {
|
|
329
|
+
const sessionDir = join(paths.sessions, approval.session_id);
|
|
330
|
+
for await (const ev of replayEvents(sessionDir, {
|
|
331
|
+
onWarning: (w) => printReplayWarning(w, approval.session_id)
|
|
332
|
+
})) {
|
|
333
|
+
if (isApprovalEvent(ev) && ev.approval_id === approval.id && (ev.type === "approval_approved" || ev.type === "approval_rejected" || ev.type === "approval_expired")) {
|
|
334
|
+
throw new Error(`Approval already resolved (per events.jsonl): ${idInput}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (isLazyExpired(approval, now)) {
|
|
338
|
+
throw new Error(`Approval already expired: ${idInput}`);
|
|
339
|
+
}
|
|
340
|
+
let sessionStatus = null;
|
|
341
|
+
try {
|
|
342
|
+
sessionStatus = (await readSessionYaml(paths, approval.session_id)).session.status;
|
|
343
|
+
} catch {
|
|
344
|
+
sessionStatus = null;
|
|
345
|
+
}
|
|
346
|
+
const attachable = sessionStatus === "initialized" || sessionStatus === "running" || sessionStatus === "waiting_approval";
|
|
347
|
+
if (sessionStatus !== null && !attachable) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
`Cannot resolve an approval for a session that is not active (status=${sessionStatus}): ${idInput}`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
if (decision === "approve") {
|
|
353
|
+
const note = options.note ?? null;
|
|
354
|
+
await appendChainedEventLocked(paths, approval.session_id, {
|
|
355
|
+
schema_version: "0.1.0",
|
|
356
|
+
id: eventId,
|
|
357
|
+
session_id: approval.session_id,
|
|
358
|
+
occurred_at: occurredAt,
|
|
359
|
+
source: "local-cli",
|
|
360
|
+
type: "approval_approved",
|
|
361
|
+
approval_id: approval.id,
|
|
362
|
+
resolver: "local-cli",
|
|
363
|
+
note
|
|
364
|
+
});
|
|
365
|
+
} else {
|
|
366
|
+
const reason = options.reason;
|
|
367
|
+
await appendChainedEventLocked(paths, approval.session_id, {
|
|
368
|
+
schema_version: "0.1.0",
|
|
369
|
+
id: eventId,
|
|
370
|
+
session_id: approval.session_id,
|
|
371
|
+
occurred_at: occurredAt,
|
|
372
|
+
source: "local-cli",
|
|
373
|
+
type: "approval_rejected",
|
|
374
|
+
approval_id: approval.id,
|
|
375
|
+
resolver: "local-cli",
|
|
376
|
+
reason
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
} finally {
|
|
380
|
+
await sessionLock.release();
|
|
372
381
|
}
|
|
373
382
|
const resolvedApproval = decision === "approve" ? {
|
|
374
383
|
...approval,
|
|
@@ -625,7 +634,7 @@ function printNoApprovals(options) {
|
|
|
625
634
|
|
|
626
635
|
// src/commands/decision.ts
|
|
627
636
|
import {
|
|
628
|
-
acquireLock,
|
|
637
|
+
acquireLock as acquireLock2,
|
|
629
638
|
appendEventToExistingSession,
|
|
630
639
|
assertBasouRootSafe as assertBasouRootSafe2,
|
|
631
640
|
basouPaths as basouPaths2,
|
|
@@ -690,7 +699,7 @@ async function doRunDecisionRecord(options, ctx) {
|
|
|
690
699
|
if (options.session !== void 0) {
|
|
691
700
|
const sessionId = await resolveSessionId(paths, options.session);
|
|
692
701
|
const sesId = sessionId;
|
|
693
|
-
const sessionLock = await
|
|
702
|
+
const sessionLock = await acquireLock2(paths, "session", sesId);
|
|
694
703
|
let result;
|
|
695
704
|
try {
|
|
696
705
|
result = await appendEventToExistingSession({
|
|
@@ -954,10 +963,12 @@ import { mkdir } from "fs/promises";
|
|
|
954
963
|
import { homedir } from "os";
|
|
955
964
|
import { join as join2 } from "path";
|
|
956
965
|
import {
|
|
966
|
+
acquireLock as acquireLock3,
|
|
957
967
|
assertBasouRootSafe as assertBasouRootSafe4,
|
|
958
968
|
basouPaths as basouPaths4,
|
|
959
969
|
ChildProcessRunner,
|
|
960
|
-
|
|
970
|
+
appendChainedEvent as coreAppendChainedEvent,
|
|
971
|
+
finalizeSessionYaml,
|
|
961
972
|
getSnapshot,
|
|
962
973
|
overwriteYamlFile,
|
|
963
974
|
parseDuration,
|
|
@@ -983,7 +994,6 @@ function registerExecCommand(program) {
|
|
|
983
994
|
async function runExec(command, args, options, ctx = {}) {
|
|
984
995
|
const runner = ctx.runner ?? new ChildProcessRunner();
|
|
985
996
|
const now = ctx.now ?? (() => /* @__PURE__ */ new Date());
|
|
986
|
-
const appendEvent2 = ctx.appendEvent ?? coreAppendEvent;
|
|
987
997
|
const cwd = options.cwd ?? process.cwd();
|
|
988
998
|
const timeout_ms = options.timeout !== void 0 ? parseDuration(options.timeout) : void 0;
|
|
989
999
|
const repoRoot = await resolveRepositoryRootForExec(cwd);
|
|
@@ -993,6 +1003,9 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
993
1003
|
const sessionId = prefixedUlid3("ses");
|
|
994
1004
|
const sessionDir = join2(paths.sessions, sessionId);
|
|
995
1005
|
await mkdir(sessionDir, { recursive: true });
|
|
1006
|
+
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
1007
|
+
await coreAppendChainedEvent(paths, sessionId, event);
|
|
1008
|
+
});
|
|
996
1009
|
const startedAt = now().toISOString();
|
|
997
1010
|
const sessionYamlPath = join2(sessionDir, "session.yaml");
|
|
998
1011
|
const session = buildInitialSession({
|
|
@@ -1004,7 +1017,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1004
1017
|
startedAt
|
|
1005
1018
|
});
|
|
1006
1019
|
await writeYamlFile(sessionYamlPath, session);
|
|
1007
|
-
await
|
|
1020
|
+
await appendEvent(sessionDir, {
|
|
1008
1021
|
schema_version: "0.1.0",
|
|
1009
1022
|
type: "session_started",
|
|
1010
1023
|
id: prefixedUlid3("evt"),
|
|
@@ -1013,10 +1026,10 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1013
1026
|
source: "terminal-recording"
|
|
1014
1027
|
});
|
|
1015
1028
|
if (options.snapshot !== false) {
|
|
1016
|
-
await tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now,
|
|
1029
|
+
await tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent);
|
|
1017
1030
|
}
|
|
1018
1031
|
const runningAt = now().toISOString();
|
|
1019
|
-
await
|
|
1032
|
+
await appendEvent(sessionDir, {
|
|
1020
1033
|
schema_version: "0.1.0",
|
|
1021
1034
|
type: "session_status_changed",
|
|
1022
1035
|
id: prefixedUlid3("evt"),
|
|
@@ -1026,9 +1039,14 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1026
1039
|
from: "initialized",
|
|
1027
1040
|
to: "running"
|
|
1028
1041
|
});
|
|
1029
|
-
await
|
|
1030
|
-
|
|
1031
|
-
|
|
1042
|
+
const runningLock = await acquireLock3(paths, "session", sessionId);
|
|
1043
|
+
try {
|
|
1044
|
+
await mutateSessionYaml(sessionYamlPath, (s) => {
|
|
1045
|
+
s.session.status = "running";
|
|
1046
|
+
});
|
|
1047
|
+
} finally {
|
|
1048
|
+
await runningLock.release();
|
|
1049
|
+
}
|
|
1032
1050
|
const controller = new AbortController();
|
|
1033
1051
|
let signalReceived = null;
|
|
1034
1052
|
let activeChild = null;
|
|
@@ -1064,7 +1082,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1064
1082
|
}
|
|
1065
1083
|
});
|
|
1066
1084
|
} catch (spawnError) {
|
|
1067
|
-
await finalizeSessionAsFailed(
|
|
1085
|
+
await finalizeSessionAsFailed(paths, sessionDir, sessionId, appendEvent, {
|
|
1068
1086
|
command,
|
|
1069
1087
|
args,
|
|
1070
1088
|
cwd,
|
|
@@ -1080,7 +1098,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1080
1098
|
activeChild = null;
|
|
1081
1099
|
}
|
|
1082
1100
|
const endedAt = now().toISOString();
|
|
1083
|
-
await
|
|
1101
|
+
await appendEvent(sessionDir, {
|
|
1084
1102
|
schema_version: "0.1.0",
|
|
1085
1103
|
type: "command_executed",
|
|
1086
1104
|
id: prefixedUlid3("evt"),
|
|
@@ -1096,10 +1114,10 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1096
1114
|
duration_ms: result.duration_ms
|
|
1097
1115
|
});
|
|
1098
1116
|
if (options.snapshot !== false) {
|
|
1099
|
-
await tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now,
|
|
1117
|
+
await tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent);
|
|
1100
1118
|
}
|
|
1101
1119
|
const finalStatus = decideFinalStatus(result, signalReceived);
|
|
1102
|
-
await
|
|
1120
|
+
await appendEvent(sessionDir, {
|
|
1103
1121
|
schema_version: "0.1.0",
|
|
1104
1122
|
type: "session_status_changed",
|
|
1105
1123
|
id: prefixedUlid3("evt"),
|
|
@@ -1109,7 +1127,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1109
1127
|
from: "running",
|
|
1110
1128
|
to: finalStatus
|
|
1111
1129
|
});
|
|
1112
|
-
await
|
|
1130
|
+
await appendEvent(sessionDir, {
|
|
1113
1131
|
schema_version: "0.1.0",
|
|
1114
1132
|
type: "session_ended",
|
|
1115
1133
|
id: prefixedUlid3("evt"),
|
|
@@ -1118,7 +1136,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
1118
1136
|
source: "terminal-recording",
|
|
1119
1137
|
...result.exit_code !== null ? { exit_code: result.exit_code } : {}
|
|
1120
1138
|
});
|
|
1121
|
-
await
|
|
1139
|
+
await finalizeSessionYaml(paths, sessionId, (s) => {
|
|
1122
1140
|
s.session.status = finalStatus;
|
|
1123
1141
|
s.session.ended_at = endedAt;
|
|
1124
1142
|
s.session.invocation.exit_code = result.exit_code;
|
|
@@ -1148,7 +1166,7 @@ function signalToExitCode(sig) {
|
|
|
1148
1166
|
const num = SIGNUM_MAP[sig] ?? 1;
|
|
1149
1167
|
return 128 + num;
|
|
1150
1168
|
}
|
|
1151
|
-
async function tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now,
|
|
1169
|
+
async function tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent) {
|
|
1152
1170
|
let snapshot;
|
|
1153
1171
|
try {
|
|
1154
1172
|
snapshot = await getSnapshot(repoRoot);
|
|
@@ -1156,7 +1174,7 @@ async function tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, append
|
|
|
1156
1174
|
console.warn(normalizeGitSnapshotSkipMessage(error));
|
|
1157
1175
|
return;
|
|
1158
1176
|
}
|
|
1159
|
-
await
|
|
1177
|
+
await appendEvent(sessionDir, {
|
|
1160
1178
|
schema_version: "0.1.0",
|
|
1161
1179
|
type: "git_snapshot",
|
|
1162
1180
|
id: prefixedUlid3("evt"),
|
|
@@ -1212,8 +1230,8 @@ async function mutateSessionYaml(filePath, mutator) {
|
|
|
1212
1230
|
const validated = SessionSchema.parse(parsed);
|
|
1213
1231
|
await overwriteYamlFile(filePath, validated);
|
|
1214
1232
|
}
|
|
1215
|
-
async function finalizeSessionAsFailed(
|
|
1216
|
-
await
|
|
1233
|
+
async function finalizeSessionAsFailed(paths, sessionDir, sessionId, appendEvent, ctx) {
|
|
1234
|
+
await appendEvent(sessionDir, {
|
|
1217
1235
|
schema_version: "0.1.0",
|
|
1218
1236
|
type: "command_executed",
|
|
1219
1237
|
id: prefixedUlid3("evt"),
|
|
@@ -1228,7 +1246,7 @@ async function finalizeSessionAsFailed(sessionDir, sessionYamlPath, sessionId, a
|
|
|
1228
1246
|
...ctx.signalReceived !== null ? { received_signal: ctx.signalReceived } : {},
|
|
1229
1247
|
duration_ms: 0
|
|
1230
1248
|
});
|
|
1231
|
-
await
|
|
1249
|
+
await appendEvent(sessionDir, {
|
|
1232
1250
|
schema_version: "0.1.0",
|
|
1233
1251
|
type: "session_status_changed",
|
|
1234
1252
|
id: prefixedUlid3("evt"),
|
|
@@ -1238,7 +1256,7 @@ async function finalizeSessionAsFailed(sessionDir, sessionYamlPath, sessionId, a
|
|
|
1238
1256
|
from: "running",
|
|
1239
1257
|
to: "failed"
|
|
1240
1258
|
});
|
|
1241
|
-
await
|
|
1259
|
+
await appendEvent(sessionDir, {
|
|
1242
1260
|
schema_version: "0.1.0",
|
|
1243
1261
|
type: "session_ended",
|
|
1244
1262
|
id: prefixedUlid3("evt"),
|
|
@@ -1246,7 +1264,7 @@ async function finalizeSessionAsFailed(sessionDir, sessionYamlPath, sessionId, a
|
|
|
1246
1264
|
occurred_at: ctx.occurredAt,
|
|
1247
1265
|
source: "terminal-recording"
|
|
1248
1266
|
});
|
|
1249
|
-
await
|
|
1267
|
+
await finalizeSessionYaml(paths, sessionId, (s) => {
|
|
1250
1268
|
s.session.status = "failed";
|
|
1251
1269
|
s.session.ended_at = ctx.occurredAt;
|
|
1252
1270
|
s.session.invocation.exit_code = null;
|
|
@@ -2398,11 +2416,13 @@ import { mkdir as mkdir2 } from "fs/promises";
|
|
|
2398
2416
|
import { homedir as homedir4 } from "os";
|
|
2399
2417
|
import { join as join5 } from "path";
|
|
2400
2418
|
import {
|
|
2419
|
+
acquireLock as acquireLock4,
|
|
2401
2420
|
assertBasouRootSafe as assertBasouRootSafe8,
|
|
2402
2421
|
basouPaths as basouPaths8,
|
|
2403
2422
|
ChildProcessRunner as ChildProcessRunner2,
|
|
2404
2423
|
claudeCodeAdapterMetadata,
|
|
2405
|
-
|
|
2424
|
+
appendChainedEvent as coreAppendChainedEvent2,
|
|
2425
|
+
finalizeSessionYaml as finalizeSessionYaml2,
|
|
2406
2426
|
getDiff,
|
|
2407
2427
|
getSnapshot as getSnapshot2,
|
|
2408
2428
|
overwriteYamlFile as overwriteYamlFile2,
|
|
@@ -2438,7 +2458,6 @@ function registerRunCommand(program, ctx = {}) {
|
|
|
2438
2458
|
async function runClaudeCode(args, options, ctx = {}) {
|
|
2439
2459
|
const runner = ctx.runner ?? new ChildProcessRunner2();
|
|
2440
2460
|
const now = ctx.now ?? (() => /* @__PURE__ */ new Date());
|
|
2441
|
-
const appendEvent2 = ctx.appendEvent ?? coreAppendEvent2;
|
|
2442
2461
|
const resolveCommand = ctx.resolveCommand ?? resolveClaudeCodeCommand;
|
|
2443
2462
|
const getDiffFn = ctx.getDiff ?? getDiff;
|
|
2444
2463
|
const { command } = await resolveCommand();
|
|
@@ -2450,6 +2469,9 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2450
2469
|
const sessionId = prefixedUlid4("ses");
|
|
2451
2470
|
const sessionDir = join5(paths.sessions, sessionId);
|
|
2452
2471
|
await mkdir2(sessionDir, { recursive: true });
|
|
2472
|
+
const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
|
|
2473
|
+
await coreAppendChainedEvent2(paths, sessionId, event);
|
|
2474
|
+
});
|
|
2453
2475
|
const startedAt = now().toISOString();
|
|
2454
2476
|
const sessionYamlPath = join5(sessionDir, "session.yaml");
|
|
2455
2477
|
const session = buildInitialSession2({
|
|
@@ -2461,7 +2483,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2461
2483
|
startedAt
|
|
2462
2484
|
});
|
|
2463
2485
|
await writeYamlFile2(sessionYamlPath, session);
|
|
2464
|
-
await
|
|
2486
|
+
await appendEvent(sessionDir, {
|
|
2465
2487
|
schema_version: "0.1.0",
|
|
2466
2488
|
type: "session_started",
|
|
2467
2489
|
id: prefixedUlid4("evt"),
|
|
@@ -2471,10 +2493,10 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2471
2493
|
});
|
|
2472
2494
|
let preSnapshot = null;
|
|
2473
2495
|
if (options.snapshot !== false) {
|
|
2474
|
-
preSnapshot = await tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now,
|
|
2496
|
+
preSnapshot = await tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent);
|
|
2475
2497
|
}
|
|
2476
2498
|
const runningAt = now().toISOString();
|
|
2477
|
-
await
|
|
2499
|
+
await appendEvent(sessionDir, {
|
|
2478
2500
|
schema_version: "0.1.0",
|
|
2479
2501
|
type: "session_status_changed",
|
|
2480
2502
|
id: prefixedUlid4("evt"),
|
|
@@ -2484,9 +2506,14 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2484
2506
|
from: "initialized",
|
|
2485
2507
|
to: "running"
|
|
2486
2508
|
});
|
|
2487
|
-
await
|
|
2488
|
-
|
|
2489
|
-
|
|
2509
|
+
const runningLock = await acquireLock4(paths, "session", sessionId);
|
|
2510
|
+
try {
|
|
2511
|
+
await mutateSessionYaml2(sessionYamlPath, (s) => {
|
|
2512
|
+
s.session.status = "running";
|
|
2513
|
+
});
|
|
2514
|
+
} finally {
|
|
2515
|
+
await runningLock.release();
|
|
2516
|
+
}
|
|
2490
2517
|
const controller = new AbortController();
|
|
2491
2518
|
let signalReceived = null;
|
|
2492
2519
|
let activeChild = null;
|
|
@@ -2521,7 +2548,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2521
2548
|
}
|
|
2522
2549
|
});
|
|
2523
2550
|
} catch (spawnError) {
|
|
2524
|
-
await finalizeSessionAsFailed2(
|
|
2551
|
+
await finalizeSessionAsFailed2(paths, sessionDir, sessionId, appendEvent, {
|
|
2525
2552
|
command,
|
|
2526
2553
|
args,
|
|
2527
2554
|
cwd: repoRoot,
|
|
@@ -2537,7 +2564,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2537
2564
|
activeChild = null;
|
|
2538
2565
|
}
|
|
2539
2566
|
const endedAt = now().toISOString();
|
|
2540
|
-
await
|
|
2567
|
+
await appendEvent(sessionDir, {
|
|
2541
2568
|
schema_version: "0.1.0",
|
|
2542
2569
|
type: "command_executed",
|
|
2543
2570
|
id: prefixedUlid4("evt"),
|
|
@@ -2554,7 +2581,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2554
2581
|
});
|
|
2555
2582
|
let postSnapshot = null;
|
|
2556
2583
|
if (options.snapshot !== false) {
|
|
2557
|
-
postSnapshot = await tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now,
|
|
2584
|
+
postSnapshot = await tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent);
|
|
2558
2585
|
}
|
|
2559
2586
|
let diff = null;
|
|
2560
2587
|
if (preSnapshot !== null && postSnapshot !== null) {
|
|
@@ -2565,7 +2592,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2565
2592
|
preSnapshot.head,
|
|
2566
2593
|
postSnapshot.head,
|
|
2567
2594
|
now().toISOString(),
|
|
2568
|
-
|
|
2595
|
+
appendEvent,
|
|
2569
2596
|
getDiffFn
|
|
2570
2597
|
);
|
|
2571
2598
|
}
|
|
@@ -2575,7 +2602,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2575
2602
|
homedir: homedir4()
|
|
2576
2603
|
}).sanitized;
|
|
2577
2604
|
const finalStatus = decideFinalStatus2(result, signalReceived);
|
|
2578
|
-
await
|
|
2605
|
+
await appendEvent(sessionDir, {
|
|
2579
2606
|
schema_version: "0.1.0",
|
|
2580
2607
|
type: "session_status_changed",
|
|
2581
2608
|
id: prefixedUlid4("evt"),
|
|
@@ -2585,7 +2612,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2585
2612
|
from: "running",
|
|
2586
2613
|
to: finalStatus
|
|
2587
2614
|
});
|
|
2588
|
-
await
|
|
2615
|
+
await appendEvent(sessionDir, {
|
|
2589
2616
|
schema_version: "0.1.0",
|
|
2590
2617
|
type: "session_ended",
|
|
2591
2618
|
id: prefixedUlid4("evt"),
|
|
@@ -2594,7 +2621,7 @@ async function runClaudeCode(args, options, ctx = {}) {
|
|
|
2594
2621
|
source: claudeCodeAdapterMetadata.kind,
|
|
2595
2622
|
...result.exit_code !== null ? { exit_code: result.exit_code } : {}
|
|
2596
2623
|
});
|
|
2597
|
-
await
|
|
2624
|
+
await finalizeSessionYaml2(paths, sessionId, (s) => {
|
|
2598
2625
|
s.session.status = finalStatus;
|
|
2599
2626
|
s.session.ended_at = endedAt;
|
|
2600
2627
|
s.session.invocation.exit_code = result.exit_code;
|
|
@@ -2623,7 +2650,7 @@ function signalToExitCode2(sig) {
|
|
|
2623
2650
|
const num = SIGNUM_MAP2[sig] ?? 1;
|
|
2624
2651
|
return 128 + num;
|
|
2625
2652
|
}
|
|
2626
|
-
async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now,
|
|
2653
|
+
async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent) {
|
|
2627
2654
|
let snapshot;
|
|
2628
2655
|
try {
|
|
2629
2656
|
snapshot = await getSnapshot2(repoRoot);
|
|
@@ -2631,7 +2658,7 @@ async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appen
|
|
|
2631
2658
|
console.warn(normalizeGitSnapshotSkipMessage2(error));
|
|
2632
2659
|
return null;
|
|
2633
2660
|
}
|
|
2634
|
-
await
|
|
2661
|
+
await appendEvent(sessionDir, {
|
|
2635
2662
|
schema_version: "0.1.0",
|
|
2636
2663
|
type: "git_snapshot",
|
|
2637
2664
|
id: prefixedUlid4("evt"),
|
|
@@ -2642,7 +2669,7 @@ async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appen
|
|
|
2642
2669
|
});
|
|
2643
2670
|
return snapshot;
|
|
2644
2671
|
}
|
|
2645
|
-
async function tryAppendFileChangedEvents(sessionDir, sessionId, repoRoot, baseRef, headRef, occurredAt,
|
|
2672
|
+
async function tryAppendFileChangedEvents(sessionDir, sessionId, repoRoot, baseRef, headRef, occurredAt, appendEvent, getDiffFn) {
|
|
2646
2673
|
let diff;
|
|
2647
2674
|
try {
|
|
2648
2675
|
diff = await getDiffFn(repoRoot, baseRef, headRef);
|
|
@@ -2651,7 +2678,7 @@ async function tryAppendFileChangedEvents(sessionDir, sessionId, repoRoot, baseR
|
|
|
2651
2678
|
return null;
|
|
2652
2679
|
}
|
|
2653
2680
|
for (const change of diff.changed_files) {
|
|
2654
|
-
await
|
|
2681
|
+
await appendEvent(sessionDir, {
|
|
2655
2682
|
schema_version: "0.1.0",
|
|
2656
2683
|
type: "file_changed",
|
|
2657
2684
|
id: prefixedUlid4("evt"),
|
|
@@ -2734,8 +2761,8 @@ async function mutateSessionYaml2(filePath, mutator) {
|
|
|
2734
2761
|
const validated = SessionSchema2.parse(parsed);
|
|
2735
2762
|
await overwriteYamlFile2(filePath, validated);
|
|
2736
2763
|
}
|
|
2737
|
-
async function finalizeSessionAsFailed2(
|
|
2738
|
-
await
|
|
2764
|
+
async function finalizeSessionAsFailed2(paths, sessionDir, sessionId, appendEvent, ctx) {
|
|
2765
|
+
await appendEvent(sessionDir, {
|
|
2739
2766
|
schema_version: "0.1.0",
|
|
2740
2767
|
type: "command_executed",
|
|
2741
2768
|
id: prefixedUlid4("evt"),
|
|
@@ -2750,7 +2777,7 @@ async function finalizeSessionAsFailed2(sessionDir, sessionYamlPath, sessionId,
|
|
|
2750
2777
|
...ctx.signalReceived !== null ? { received_signal: ctx.signalReceived } : {},
|
|
2751
2778
|
duration_ms: 0
|
|
2752
2779
|
});
|
|
2753
|
-
await
|
|
2780
|
+
await appendEvent(sessionDir, {
|
|
2754
2781
|
schema_version: "0.1.0",
|
|
2755
2782
|
type: "session_status_changed",
|
|
2756
2783
|
id: prefixedUlid4("evt"),
|
|
@@ -2760,7 +2787,7 @@ async function finalizeSessionAsFailed2(sessionDir, sessionYamlPath, sessionId,
|
|
|
2760
2787
|
from: "running",
|
|
2761
2788
|
to: "failed"
|
|
2762
2789
|
});
|
|
2763
|
-
await
|
|
2790
|
+
await appendEvent(sessionDir, {
|
|
2764
2791
|
schema_version: "0.1.0",
|
|
2765
2792
|
type: "session_ended",
|
|
2766
2793
|
id: prefixedUlid4("evt"),
|
|
@@ -2768,7 +2795,7 @@ async function finalizeSessionAsFailed2(sessionDir, sessionYamlPath, sessionId,
|
|
|
2768
2795
|
occurred_at: ctx.occurredAt,
|
|
2769
2796
|
source: claudeCodeAdapterMetadata.kind
|
|
2770
2797
|
});
|
|
2771
|
-
await
|
|
2798
|
+
await finalizeSessionYaml2(paths, sessionId, (s) => {
|
|
2772
2799
|
s.session.status = "failed";
|
|
2773
2800
|
s.session.ended_at = ctx.occurredAt;
|
|
2774
2801
|
s.session.invocation.exit_code = null;
|
|
@@ -2791,7 +2818,7 @@ async function resolveRepositoryRootForRun(cwd) {
|
|
|
2791
2818
|
import { readFile as readFile2 } from "fs/promises";
|
|
2792
2819
|
import { basename as basename3, isAbsolute, join as join6, relative as relative2 } from "path";
|
|
2793
2820
|
import {
|
|
2794
|
-
acquireLock as
|
|
2821
|
+
acquireLock as acquireLock5,
|
|
2795
2822
|
appendEventToExistingSession as appendEventToExistingSession2,
|
|
2796
2823
|
assertBasouRootSafe as assertBasouRootSafe9,
|
|
2797
2824
|
basouPaths as basouPaths9,
|
|
@@ -3341,7 +3368,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
|
|
|
3341
3368
|
}
|
|
3342
3369
|
const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3343
3370
|
const sesId = sessionId;
|
|
3344
|
-
const sessionLock = await
|
|
3371
|
+
const sessionLock = await acquireLock5(paths, "session", sesId);
|
|
3345
3372
|
let result;
|
|
3346
3373
|
try {
|
|
3347
3374
|
result = await appendEventToExistingSession2({
|
|
@@ -4812,9 +4839,7 @@ import {
|
|
|
4812
4839
|
verifyEventsChain
|
|
4813
4840
|
} from "@basou/core";
|
|
4814
4841
|
function registerVerifyCommand(program) {
|
|
4815
|
-
program.command("verify").description(
|
|
4816
|
-
"Verify the tamper-evidence hash chain of imported sessions' event logs (read-only)"
|
|
4817
|
-
).option("--session <id>", "Verify a single session (unique id prefix accepted)").option("--all", "Verify every session (the default when --session is omitted)").option("--json", "Output the verdicts as JSON").option("-v, --verbose", "Show error causes").action(async (opts) => {
|
|
4842
|
+
program.command("verify").description("Verify the tamper-evidence hash chain of sessions' event logs (read-only)").option("--session <id>", "Verify a single session (unique id prefix accepted)").option("--all", "Verify every session (the default when --session is omitted)").option("--json", "Output the verdicts as JSON").option("-v, --verbose", "Show error causes").action(async (opts) => {
|
|
4818
4843
|
await runVerify(opts);
|
|
4819
4844
|
});
|
|
4820
4845
|
}
|
|
@@ -4855,7 +4880,7 @@ async function doRunVerify(options, ctx) {
|
|
|
4855
4880
|
}
|
|
4856
4881
|
const tally = (status) => rows.filter((r) => r.status === status).length;
|
|
4857
4882
|
console.log(
|
|
4858
|
-
`Sessions: ${rows.length} total \u2014 ${tally("verified")} verified, ${tally("unchained")} unchained, ${tally("empty")} empty, ${tally("incomplete")} incomplete, ${tamperedCount} tampered`
|
|
4883
|
+
`Sessions: ${rows.length} total \u2014 ${tally("verified")} verified, ${tally("unchained")} unchained, ${tally("empty")} empty, ${tally("incomplete")} incomplete, ${tally("in_progress")} in_progress, ${tamperedCount} tampered`
|
|
4859
4884
|
);
|
|
4860
4885
|
}
|
|
4861
4886
|
if (tamperedCount > 0) {
|
|
@@ -4870,8 +4895,10 @@ function renderVerdict(row) {
|
|
|
4870
4895
|
return row.line !== void 0 ? `TAMPERED (${row.reason} at line ${row.line})` : `TAMPERED (${row.reason})`;
|
|
4871
4896
|
case "incomplete":
|
|
4872
4897
|
return "incomplete (session.yaml missing; re-import to repair)";
|
|
4898
|
+
case "in_progress":
|
|
4899
|
+
return `in_progress (${row.event_count} events; live session, anchor written at finalize)`;
|
|
4873
4900
|
case "unchained":
|
|
4874
|
-
return "unchained (
|
|
4901
|
+
return "unchained (session created before event-log chaining)";
|
|
4875
4902
|
case "empty":
|
|
4876
4903
|
return "empty";
|
|
4877
4904
|
}
|