@basou/cli 0.57.1 → 0.59.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 +77 -18
- package/dist/index.js.map +1 -1
- package/dist/program.js +77 -18
- package/dist/program.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -95,6 +95,8 @@ function printSessionSkip(sid, reason) {
|
|
|
95
95
|
const short = shortSessionId(sid);
|
|
96
96
|
if (reason === "events_jsonl_unreadable") {
|
|
97
97
|
console.error(`Warning: skipped suspect check for ${short}: events.jsonl unreadable`);
|
|
98
|
+
} else if (reason === "session_dir_not_directory") {
|
|
99
|
+
console.error(`Skipped ${sid}: ${reason}`);
|
|
98
100
|
} else {
|
|
99
101
|
console.error(`Skipped ${short}: ${reason}`);
|
|
100
102
|
}
|
|
@@ -111,6 +113,9 @@ function printSessionListSkip(sid, reason) {
|
|
|
111
113
|
case "events_jsonl_unreadable":
|
|
112
114
|
console.error(`Warning: skipped suspect check for ${short}: events.jsonl unreadable`);
|
|
113
115
|
break;
|
|
116
|
+
case "session_dir_not_directory":
|
|
117
|
+
console.error(`Skipped ${sid}: not a directory (a symlink or a file is not followed)`);
|
|
118
|
+
break;
|
|
114
119
|
}
|
|
115
120
|
}
|
|
116
121
|
function printTaskSkip(taskId, reason) {
|
|
@@ -131,10 +136,12 @@ import {
|
|
|
131
136
|
acquireLock,
|
|
132
137
|
appendChainedEventLocked,
|
|
133
138
|
assertBasouRootSafe,
|
|
139
|
+
assertSessionDirSafe,
|
|
134
140
|
basouPaths,
|
|
135
141
|
EVENT_SCHEMA_VERSION,
|
|
136
142
|
enumerateApprovals,
|
|
137
143
|
findErrorCode,
|
|
144
|
+
inspectSessionEntry,
|
|
138
145
|
isLazyExpired,
|
|
139
146
|
LOCAL_CLI_EVENT_SOURCE,
|
|
140
147
|
linkYamlFile,
|
|
@@ -257,13 +264,20 @@ async function doRunApprovalShow(idInput, options, ctx) {
|
|
|
257
264
|
if (loaded === null) {
|
|
258
265
|
throw new Error(`Approval not found: ${idInput}`);
|
|
259
266
|
}
|
|
260
|
-
const
|
|
267
|
+
const sessionId = loaded.approval.session_id;
|
|
268
|
+
const entry = await inspectSessionEntry(paths, sessionId);
|
|
261
269
|
const relatedEvents = [];
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
270
|
+
if (entry === "symlink" || entry === "not_a_directory") {
|
|
271
|
+
console.error(
|
|
272
|
+
`Warning: session ${sessionId} is not a directory (a symlink or a file is not followed); its events are not shown`
|
|
273
|
+
);
|
|
274
|
+
} else {
|
|
275
|
+
for await (const ev of replayEvents(join(paths.sessions, sessionId), {
|
|
276
|
+
onWarning: (w) => printReplayWarning(w, sessionId)
|
|
277
|
+
})) {
|
|
278
|
+
if (isApprovalEvent(ev) && ev.approval_id === id) {
|
|
279
|
+
relatedEvents.push(ev);
|
|
280
|
+
}
|
|
267
281
|
}
|
|
268
282
|
}
|
|
269
283
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -342,6 +356,7 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
|
|
|
342
356
|
const eventId = prefixedUlid("evt");
|
|
343
357
|
const sessionLock = await acquireLock(paths, "session", approval.session_id);
|
|
344
358
|
try {
|
|
359
|
+
await assertSessionDirSafe(paths, approval.session_id);
|
|
345
360
|
const sessionDir = join(paths.sessions, approval.session_id);
|
|
346
361
|
for await (const ev of replayEvents(sessionDir, {
|
|
347
362
|
onWarning: (w) => printReplayWarning(w, approval.session_id)
|
|
@@ -2062,6 +2077,7 @@ import { join as join6 } from "path";
|
|
|
2062
2077
|
import {
|
|
2063
2078
|
acquireLock as acquireLock3,
|
|
2064
2079
|
assertBasouRootSafe as assertBasouRootSafe4,
|
|
2080
|
+
assertSessionStoreSafe,
|
|
2065
2081
|
basouPaths as basouPaths6,
|
|
2066
2082
|
ChildProcessRunner,
|
|
2067
2083
|
appendChainedEvent as coreAppendChainedEvent,
|
|
@@ -2100,6 +2116,7 @@ async function runExec(command, args, options, ctx = {}) {
|
|
|
2100
2116
|
const paths = basouPaths6(repoRoot);
|
|
2101
2117
|
await assertBasouRootSafe4(paths.root);
|
|
2102
2118
|
const manifest = await readManifest3(paths);
|
|
2119
|
+
await assertSessionStoreSafe(paths);
|
|
2103
2120
|
const sessionId = prefixedUlid3("ses");
|
|
2104
2121
|
const sessionDir = join6(paths.sessions, sessionId);
|
|
2105
2122
|
await mkdir(sessionDir, { recursive: true });
|
|
@@ -2914,6 +2931,7 @@ import { createInterface } from "readline";
|
|
|
2914
2931
|
import {
|
|
2915
2932
|
AGENT_INFRA_DIRS as AGENT_INFRA_DIRS2,
|
|
2916
2933
|
assertBasouRootSafe as assertBasouRootSafe6,
|
|
2934
|
+
assertSessionStoreSafe as assertSessionStoreSafe2,
|
|
2917
2935
|
basouPaths as basouPaths8,
|
|
2918
2936
|
CLAUDE_IMPORT_SOURCE,
|
|
2919
2937
|
CODEX_IMPORT_SOURCE,
|
|
@@ -2921,7 +2939,7 @@ import {
|
|
|
2921
2939
|
claudeTranscriptToImportPayload,
|
|
2922
2940
|
codexRolloutToImportPayload,
|
|
2923
2941
|
displayPath as displayPath2,
|
|
2924
|
-
|
|
2942
|
+
enumerateSessionEntries,
|
|
2925
2943
|
findErrorCode as findErrorCode5,
|
|
2926
2944
|
importSessionFromJson,
|
|
2927
2945
|
observedFilesOf,
|
|
@@ -3085,7 +3103,12 @@ async function resolveImportTarget(ctx) {
|
|
|
3085
3103
|
return { repositoryRoot, paths, manifest };
|
|
3086
3104
|
}
|
|
3087
3105
|
async function importDerivedSessions(paths, manifest, options, sourceKind, candidates, projectPaths, boundaryDeclared) {
|
|
3088
|
-
|
|
3106
|
+
await assertSessionStoreSafe2(paths);
|
|
3107
|
+
const { byExternalId: existingByExternalId, notDirectories } = await loadExistingByExternalId(
|
|
3108
|
+
paths,
|
|
3109
|
+
sourceKind
|
|
3110
|
+
);
|
|
3111
|
+
let notDirectoriesWarned = false;
|
|
3089
3112
|
const seenThisRun = /* @__PURE__ */ new Set();
|
|
3090
3113
|
const crossProjectCheck = boundaryDeclared;
|
|
3091
3114
|
const crossProject = [];
|
|
@@ -3177,6 +3200,13 @@ async function importDerivedSessions(paths, manifest, options, sourceKind, candi
|
|
|
3177
3200
|
}
|
|
3178
3201
|
counts.replaced++;
|
|
3179
3202
|
}
|
|
3203
|
+
if (priors.length === 0 && notDirectories.length > 0 && !notDirectoriesWarned) {
|
|
3204
|
+
notDirectoriesWarned = true;
|
|
3205
|
+
const one = notDirectories.length === 1;
|
|
3206
|
+
console.error(
|
|
3207
|
+
`Import: ${notDirectories.join(", ")} ${one ? "is not a directory" : "are not directories"} (a symlink or a file is not followed), so ${one ? "it was" : "they were"} not checked for an earlier import; a session imported there may be imported again`
|
|
3208
|
+
);
|
|
3209
|
+
}
|
|
3180
3210
|
const result = await importSessionFromJson(paths, manifest, payload, {
|
|
3181
3211
|
dryRun: options.dryRun === true
|
|
3182
3212
|
});
|
|
@@ -3253,10 +3283,11 @@ async function loadExistingByExternalId(paths, sourceKind) {
|
|
|
3253
3283
|
else list.push(prior);
|
|
3254
3284
|
};
|
|
3255
3285
|
let sessionIds;
|
|
3286
|
+
let notDirectories;
|
|
3256
3287
|
try {
|
|
3257
|
-
sessionIds = await
|
|
3288
|
+
({ dirs: sessionIds, notDirectories } = await enumerateSessionEntries(paths));
|
|
3258
3289
|
} catch {
|
|
3259
|
-
return byExternalId;
|
|
3290
|
+
return { byExternalId, notDirectories: [] };
|
|
3260
3291
|
}
|
|
3261
3292
|
for (const sessionId of sessionIds) {
|
|
3262
3293
|
let session;
|
|
@@ -3277,7 +3308,7 @@ async function loadExistingByExternalId(paths, sourceKind) {
|
|
|
3277
3308
|
const match = typeof label === "string" ? label.match(/^claude-code import (\S+)$/) : null;
|
|
3278
3309
|
if (match?.[1] !== void 0) add(match[1], prior);
|
|
3279
3310
|
}
|
|
3280
|
-
return byExternalId;
|
|
3311
|
+
return { byExternalId, notDirectories };
|
|
3281
3312
|
}
|
|
3282
3313
|
async function selectTranscriptFiles(projectsRoot, projectPaths, options) {
|
|
3283
3314
|
if (options.session !== void 0) {
|
|
@@ -9557,6 +9588,7 @@ import { join as join14 } from "path";
|
|
|
9557
9588
|
import {
|
|
9558
9589
|
acquireLock as acquireLock5,
|
|
9559
9590
|
assertBasouRootSafe as assertBasouRootSafe12,
|
|
9591
|
+
assertSessionStoreSafe as assertSessionStoreSafe3,
|
|
9560
9592
|
basouPaths as basouPaths17,
|
|
9561
9593
|
ChildProcessRunner as ChildProcessRunner2,
|
|
9562
9594
|
claudeCodeAdapterMetadata,
|
|
@@ -9632,6 +9664,7 @@ async function runTrackedTool(args, options, ctx, adapter) {
|
|
|
9632
9664
|
const paths = basouPaths17(repoRoot);
|
|
9633
9665
|
await assertBasouRootSafe12(paths.root);
|
|
9634
9666
|
const manifest = await readManifest10(paths);
|
|
9667
|
+
await assertSessionStoreSafe3(paths);
|
|
9635
9668
|
const sessionId = prefixedUlid4("ses");
|
|
9636
9669
|
const sessionDir = join14(paths.sessions, sessionId);
|
|
9637
9670
|
await mkdir2(sessionDir, { recursive: true });
|
|
@@ -10021,7 +10054,7 @@ import {
|
|
|
10021
10054
|
basouPaths as basouPaths18,
|
|
10022
10055
|
displayPath as displayPath3,
|
|
10023
10056
|
EVENT_SCHEMA_VERSION as EVENT_SCHEMA_VERSION6,
|
|
10024
|
-
|
|
10057
|
+
enumerateSessionEntries as enumerateSessionEntries2,
|
|
10025
10058
|
findErrorCode as findErrorCode12,
|
|
10026
10059
|
importSessionFromJson as importSessionFromJson2,
|
|
10027
10060
|
LOCAL_CLI_EVENT_SOURCE as LOCAL_CLI_EVENT_SOURCE4,
|
|
@@ -10653,7 +10686,7 @@ async function doRunSessionRechain(options, ctx) {
|
|
|
10653
10686
|
const repositoryRoot = await resolveRepositoryRootForSession(cwd, "rechain");
|
|
10654
10687
|
const paths = basouPaths18(repositoryRoot);
|
|
10655
10688
|
await assertWorkspaceInitialized11(paths.root);
|
|
10656
|
-
const sessionIds = options.session !== void 0 ? [await resolveSessionId3(paths, options.session)] : await
|
|
10689
|
+
const sessionIds = options.session !== void 0 ? [await resolveSessionId3(paths, options.session)] : await enumerateAllSessionNames(paths);
|
|
10657
10690
|
const dryRun = options.dryRun === true;
|
|
10658
10691
|
const rows = [];
|
|
10659
10692
|
for (const sessionId of sessionIds) {
|
|
@@ -10674,7 +10707,9 @@ async function doRunSessionRechain(options, ctx) {
|
|
|
10674
10707
|
rows.push({ session_id: sessionId, status: "skipped", reason: outcome.reason });
|
|
10675
10708
|
}
|
|
10676
10709
|
}
|
|
10677
|
-
const tamperedCount = rows.filter(
|
|
10710
|
+
const tamperedCount = rows.filter(
|
|
10711
|
+
(r) => r.reason === "tampered" || r.reason === "symlink" || r.reason === "not_a_directory"
|
|
10712
|
+
).length;
|
|
10678
10713
|
const errorCount = rows.filter((r) => r.status === "error").length;
|
|
10679
10714
|
if (options.json === true) {
|
|
10680
10715
|
console.log(JSON.stringify(rows, null, 2));
|
|
@@ -10697,11 +10732,19 @@ function renderRechainRow(row, dryRun) {
|
|
|
10697
10732
|
case "rechained":
|
|
10698
10733
|
return `${dryRun ? "would rechain" : "rechained"} (${row.event_count} events)`;
|
|
10699
10734
|
case "skipped":
|
|
10700
|
-
|
|
10735
|
+
if (row.reason === "tampered") return "skipped (TAMPERED \u2014 inspect with 'basou verify')";
|
|
10736
|
+
if (row.reason === "symlink" || row.reason === "not_a_directory") {
|
|
10737
|
+
return `skipped (${row.reason === "symlink" ? "SYMLINK" : "NOT A DIRECTORY"} \u2014 not followed; inspect with 'basou verify')`;
|
|
10738
|
+
}
|
|
10739
|
+
return `skipped (${row.reason})`;
|
|
10701
10740
|
case "error":
|
|
10702
10741
|
return `error (${row.message})`;
|
|
10703
10742
|
}
|
|
10704
10743
|
}
|
|
10744
|
+
async function enumerateAllSessionNames(paths) {
|
|
10745
|
+
const { dirs, notDirectories } = await enumerateSessionEntries2(paths);
|
|
10746
|
+
return [...dirs, ...notDirectories].sort();
|
|
10747
|
+
}
|
|
10705
10748
|
|
|
10706
10749
|
// src/commands/stats.ts
|
|
10707
10750
|
import {
|
|
@@ -12049,7 +12092,7 @@ function maxLen3(values, floor) {
|
|
|
12049
12092
|
import {
|
|
12050
12093
|
assertBasouRootSafe as assertBasouRootSafe17,
|
|
12051
12094
|
basouPaths as basouPaths22,
|
|
12052
|
-
|
|
12095
|
+
enumerateSessionEntries as enumerateSessionEntries3,
|
|
12053
12096
|
findErrorCode as findErrorCode16,
|
|
12054
12097
|
resolveRepositoryRoot as resolveRepositoryRoot12,
|
|
12055
12098
|
resolveSessionId as resolveSessionId5,
|
|
@@ -12083,7 +12126,7 @@ async function doRunVerify(options, ctx) {
|
|
|
12083
12126
|
const repositoryRoot = await resolveRepositoryRootForVerify(cwd);
|
|
12084
12127
|
const paths = basouPaths22(repositoryRoot);
|
|
12085
12128
|
await assertWorkspaceInitialized14(paths.root);
|
|
12086
|
-
const sessionIds = options.session !== void 0 ? [await resolveSessionId5(paths, options.session)] : await
|
|
12129
|
+
const sessionIds = options.session !== void 0 ? [await resolveSessionId5(paths, options.session, { allowNotDirectory: true })] : await enumerateAllSessionNames2(paths);
|
|
12087
12130
|
const rows = [];
|
|
12088
12131
|
for (const sessionId of sessionIds) {
|
|
12089
12132
|
const verdict = await verifyEventsChain(paths, sessionId);
|
|
@@ -12130,6 +12173,10 @@ function renderVerdict(row) {
|
|
|
12130
12173
|
return "empty";
|
|
12131
12174
|
}
|
|
12132
12175
|
}
|
|
12176
|
+
async function enumerateAllSessionNames2(paths) {
|
|
12177
|
+
const { dirs, notDirectories } = await enumerateSessionEntries3(paths);
|
|
12178
|
+
return [...dirs, ...notDirectories].sort();
|
|
12179
|
+
}
|
|
12133
12180
|
async function resolveRepositoryRootForVerify(cwd) {
|
|
12134
12181
|
try {
|
|
12135
12182
|
return await resolveRepositoryRoot12(cwd);
|
|
@@ -12667,6 +12714,7 @@ import {
|
|
|
12667
12714
|
computeWorkStats as computeWorkStats2,
|
|
12668
12715
|
enumerateApprovals as enumerateApprovals2,
|
|
12669
12716
|
findErrorCode as findErrorCode17,
|
|
12717
|
+
inspectSessionEntry as inspectSessionEntry2,
|
|
12670
12718
|
isLazyExpired as isLazyExpired2,
|
|
12671
12719
|
loadApproval as loadApproval2,
|
|
12672
12720
|
loadSessionEntries as loadSessionEntries4,
|
|
@@ -12678,6 +12726,7 @@ import {
|
|
|
12678
12726
|
readTaskFile as readTaskFile2,
|
|
12679
12727
|
renderDecisions as renderDecisions3,
|
|
12680
12728
|
renderHandoff as renderHandoff3,
|
|
12729
|
+
SessionIdSchema,
|
|
12681
12730
|
summarizeOrientation,
|
|
12682
12731
|
tryRemoteUrl
|
|
12683
12732
|
} from "@basou/core";
|
|
@@ -13741,6 +13790,16 @@ async function sessionsList(ws, nowProvider) {
|
|
|
13741
13790
|
return { sessions };
|
|
13742
13791
|
}
|
|
13743
13792
|
async function sessionDetail(ws, sessionId) {
|
|
13793
|
+
if (!SessionIdSchema.safeParse(sessionId).success) {
|
|
13794
|
+
throw new HttpError(404, "Session not found");
|
|
13795
|
+
}
|
|
13796
|
+
const entry = await inspectSessionEntry2(ws.paths, sessionId);
|
|
13797
|
+
if (entry === "symlink" || entry === "not_a_directory") {
|
|
13798
|
+
throw new HttpError(
|
|
13799
|
+
404,
|
|
13800
|
+
`Session ${sessionId} is not a directory; a symlink or a file there is not followed`
|
|
13801
|
+
);
|
|
13802
|
+
}
|
|
13744
13803
|
let session;
|
|
13745
13804
|
try {
|
|
13746
13805
|
session = await readSessionYaml3(ws.paths, sessionId);
|
|
@@ -14123,7 +14182,7 @@ async function assertWorkspaceInitialized15(basouRoot) {
|
|
|
14123
14182
|
function readBuildStamp() {
|
|
14124
14183
|
if (false) return void 0;
|
|
14125
14184
|
try {
|
|
14126
|
-
return JSON.parse('{"version":"0.
|
|
14185
|
+
return JSON.parse('{"version":"0.59.0","commit":"431fe03","committedAt":"2026-10-01T20:32:13+09:00"}');
|
|
14127
14186
|
} catch {
|
|
14128
14187
|
return void 0;
|
|
14129
14188
|
}
|