@outcomeeng/spx 0.6.10 → 0.6.12
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/cli.js +1211 -342
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -289,9 +289,6 @@ async function resolveAgentResumeScopeContext(options) {
|
|
|
289
289
|
return { match: (core) => core.branch === target, claudeDirAccepts: () => true };
|
|
290
290
|
}
|
|
291
291
|
const invocationRoot = await options.resolveWorktreeRoot(options.invocationDir);
|
|
292
|
-
if (invocationRoot === null) {
|
|
293
|
-
return null;
|
|
294
|
-
}
|
|
295
292
|
const projectPrefix = claudeProjectDirName(invocationRoot);
|
|
296
293
|
return {
|
|
297
294
|
match: (core) => isPathInsideOrEqual(invocationRoot, core.cwd),
|
|
@@ -326,11 +323,11 @@ function renderAgentResumeJson(candidates) {
|
|
|
326
323
|
}
|
|
327
324
|
async function recentStoreFiles(paths, fs8, nowMs) {
|
|
328
325
|
const stats = await mapWithConcurrency(paths, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (path7) => {
|
|
329
|
-
const
|
|
330
|
-
if (
|
|
326
|
+
const stat9 = await fs8.stat(path7).catch(() => null);
|
|
327
|
+
if (stat9 === null || !isRecentAgentSessionMtime(stat9.mtimeMs, nowMs)) {
|
|
331
328
|
return null;
|
|
332
329
|
}
|
|
333
|
-
return { path: path7, modifiedAtMs:
|
|
330
|
+
return { path: path7, modifiedAtMs: stat9.mtimeMs };
|
|
334
331
|
});
|
|
335
332
|
return stats.filter((file) => file !== null).sort((left, right) => right.modifiedAtMs - left.modifiedAtMs || left.path.localeCompare(right.path));
|
|
336
333
|
}
|
|
@@ -529,6 +526,195 @@ async function mapWithConcurrency(items, concurrency, mapper) {
|
|
|
529
526
|
return results;
|
|
530
527
|
}
|
|
531
528
|
|
|
529
|
+
// src/domains/session/types.ts
|
|
530
|
+
var SESSION_PRIORITY = {
|
|
531
|
+
HIGH: "high",
|
|
532
|
+
MEDIUM: "medium",
|
|
533
|
+
LOW: "low"
|
|
534
|
+
};
|
|
535
|
+
var SESSION_STATUSES = ["todo", "doing", "archive"];
|
|
536
|
+
var DEFAULT_LIST_STATUSES = ["doing", "todo"];
|
|
537
|
+
var SESSION_FILE_ENCODING = "utf-8";
|
|
538
|
+
var SESSION_FILE_ERROR_CODE = {
|
|
539
|
+
NOT_FOUND: "ENOENT"
|
|
540
|
+
};
|
|
541
|
+
var SESSION_OUTPUT_MARKER = {
|
|
542
|
+
HANDOFF_ID: "HANDOFF_ID",
|
|
543
|
+
PICKUP_ID: "PICKUP_ID",
|
|
544
|
+
SESSION_FILE: "SESSION_FILE"
|
|
545
|
+
};
|
|
546
|
+
function formatSessionOutputMarker(marker, value) {
|
|
547
|
+
return `<${marker}>${value}</${marker}>`;
|
|
548
|
+
}
|
|
549
|
+
var CLAIMABLE_STATUS = SESSION_STATUSES[0];
|
|
550
|
+
var PRIORITY_ORDER = {
|
|
551
|
+
[SESSION_PRIORITY.HIGH]: 0,
|
|
552
|
+
[SESSION_PRIORITY.MEDIUM]: 1,
|
|
553
|
+
[SESSION_PRIORITY.LOW]: 2
|
|
554
|
+
};
|
|
555
|
+
var DEFAULT_PRIORITY = SESSION_PRIORITY.MEDIUM;
|
|
556
|
+
var SESSION_FRONT_MATTER = {
|
|
557
|
+
PRIORITY: "priority",
|
|
558
|
+
GIT_REF: "git_ref",
|
|
559
|
+
GOAL: "goal",
|
|
560
|
+
NEXT_STEP: "next_step",
|
|
561
|
+
CREATED_AT: "created_at",
|
|
562
|
+
AGENT_SESSION_ID: "agent_session_id",
|
|
563
|
+
SPECS: "specs",
|
|
564
|
+
FILES: "files"
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/domains/agent/search.ts
|
|
568
|
+
var AGENT_SEARCH_DEFAULT_LIMIT = 20;
|
|
569
|
+
var AGENT_SEARCH_MATCH_REASON = {
|
|
570
|
+
ALL: "all",
|
|
571
|
+
PICKUP_ID: "pickup-id",
|
|
572
|
+
CONTAINS: "contains",
|
|
573
|
+
SESSION_ID: "session-id",
|
|
574
|
+
AGENT: "agent",
|
|
575
|
+
BRANCH: "branch"
|
|
576
|
+
};
|
|
577
|
+
function pickupIdSearchLiteral(pickupId) {
|
|
578
|
+
return formatSessionOutputMarker(SESSION_OUTPUT_MARKER.PICKUP_ID, pickupId);
|
|
579
|
+
}
|
|
580
|
+
function agentSearchQueryFromOptions(options) {
|
|
581
|
+
const contentNeedles = [];
|
|
582
|
+
if (options.pickupId !== void 0) {
|
|
583
|
+
contentNeedles.push({
|
|
584
|
+
reason: AGENT_SEARCH_MATCH_REASON.PICKUP_ID,
|
|
585
|
+
value: pickupIdSearchLiteral(options.pickupId)
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (options.contains !== void 0) {
|
|
589
|
+
contentNeedles.push({ reason: AGENT_SEARCH_MATCH_REASON.CONTAINS, value: options.contains });
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
contentNeedles,
|
|
593
|
+
sessionId: options.sessionId ?? null,
|
|
594
|
+
branch: options.branch ?? null,
|
|
595
|
+
agent: options.agent ?? null,
|
|
596
|
+
includeAll: options.all === true,
|
|
597
|
+
limit: options.limit ?? AGENT_SEARCH_DEFAULT_LIMIT
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
async function searchAgentSessions(options) {
|
|
601
|
+
const selectedAgents = options.query.agent === null ? [AGENT_SESSION_KIND.CODEX, AGENT_SESSION_KIND.CLAUDE_CODE] : [options.query.agent];
|
|
602
|
+
const perAgent = await Promise.all(
|
|
603
|
+
selectedAgents.map((agent) => searchAgentStore(agent, options))
|
|
604
|
+
);
|
|
605
|
+
return perAgent.flat().sort(compareSearchResults).slice(0, Math.max(0, options.query.limit));
|
|
606
|
+
}
|
|
607
|
+
function renderAgentSearchJson(results) {
|
|
608
|
+
return JSON.stringify(results, null, 2);
|
|
609
|
+
}
|
|
610
|
+
function renderAgentSearchList(results) {
|
|
611
|
+
if (results.length === 0) {
|
|
612
|
+
return "No matching agent sessions found.";
|
|
613
|
+
}
|
|
614
|
+
return results.map((result) => {
|
|
615
|
+
const updatedAt = result.updatedAt ?? new Date(result.modifiedAtMs).toISOString();
|
|
616
|
+
return `${updatedAt} ${AGENT_SESSION_LABEL[result.agent]} ${result.sessionId} ${result.cwd}`;
|
|
617
|
+
}).join("\n");
|
|
618
|
+
}
|
|
619
|
+
async function searchAgentStore(agent, options) {
|
|
620
|
+
const paths = agent === AGENT_SESSION_KIND.CODEX ? await collectJsonlFiles(codexSessionStoreDir(options.homeDir), options.fs) : await claudeTranscriptFiles(
|
|
621
|
+
claudeCodeSessionStoreDir(options.homeDir),
|
|
622
|
+
options.fs,
|
|
623
|
+
claudeDirAcceptsProductScope(options.productScopeRoot)
|
|
624
|
+
);
|
|
625
|
+
const files = await storeFiles(paths, options.fs, options.nowMs, options.query.includeAll);
|
|
626
|
+
const parser = agent === AGENT_SESSION_KIND.CODEX ? parseCodexHead : parseClaudeHead;
|
|
627
|
+
return collectMatchingSessions(agent, files, options, parser);
|
|
628
|
+
}
|
|
629
|
+
function claudeDirAcceptsProductScope(productScopeRoot) {
|
|
630
|
+
const projectPrefix = claudeProjectDirName(productScopeRoot);
|
|
631
|
+
return (dirName) => dirName === projectPrefix || dirName.startsWith(`${projectPrefix}${CLAUDE_PROJECT_ENCODED_SEPARATOR}`);
|
|
632
|
+
}
|
|
633
|
+
async function collectMatchingSessions(agent, files, options, parseHead) {
|
|
634
|
+
const results = [];
|
|
635
|
+
const seen = /* @__PURE__ */ new Set();
|
|
636
|
+
for (const file of files) {
|
|
637
|
+
const head = await options.fs.readHead(file.path, AGENT_RESUME_LIMITS.METADATA_HEAD_BYTES).catch(() => null);
|
|
638
|
+
if (head === null) continue;
|
|
639
|
+
const core = parseHead(head);
|
|
640
|
+
if (core === null || !core.interactive || seen.has(core.sessionId)) continue;
|
|
641
|
+
if (!coreMatchesProductScope(core, options.productScopeRoot)) continue;
|
|
642
|
+
const matches = await matchReasons(agent, core, file.path, options);
|
|
643
|
+
if (matches.length === 0) continue;
|
|
644
|
+
seen.add(core.sessionId);
|
|
645
|
+
results.push({
|
|
646
|
+
agent,
|
|
647
|
+
sessionId: core.sessionId,
|
|
648
|
+
cwd: core.cwd,
|
|
649
|
+
sourcePath: file.path,
|
|
650
|
+
modifiedAtMs: file.modifiedAtMs,
|
|
651
|
+
updatedAt: core.updatedAt,
|
|
652
|
+
branch: core.branch,
|
|
653
|
+
matches
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
return results;
|
|
657
|
+
}
|
|
658
|
+
async function matchReasons(agent, core, path7, options) {
|
|
659
|
+
if (!hasSearchSelector(options.query)) {
|
|
660
|
+
return [AGENT_SEARCH_MATCH_REASON.ALL];
|
|
661
|
+
}
|
|
662
|
+
const metadataMatches = metadataMatchReasons(agent, core, options.query);
|
|
663
|
+
const contentMatches = await contentMatchReasons(path7, options);
|
|
664
|
+
if (metadataMatches === null || contentMatches === null) {
|
|
665
|
+
return [];
|
|
666
|
+
}
|
|
667
|
+
return [...metadataMatches, ...contentMatches];
|
|
668
|
+
}
|
|
669
|
+
function hasSearchSelector(query) {
|
|
670
|
+
return query.contentNeedles.length > 0 || query.sessionId !== null || query.branch !== null || query.agent !== null;
|
|
671
|
+
}
|
|
672
|
+
function metadataMatchReasons(agent, core, query) {
|
|
673
|
+
const matches = [];
|
|
674
|
+
if (query.agent !== null) {
|
|
675
|
+
if (agent !== query.agent) return null;
|
|
676
|
+
matches.push(AGENT_SEARCH_MATCH_REASON.AGENT);
|
|
677
|
+
}
|
|
678
|
+
if (query.sessionId !== null && core.sessionId === query.sessionId) {
|
|
679
|
+
matches.push(AGENT_SEARCH_MATCH_REASON.SESSION_ID);
|
|
680
|
+
} else if (query.sessionId !== null) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
if (query.branch !== null) {
|
|
684
|
+
if (core.branch !== query.branch) return null;
|
|
685
|
+
matches.push(AGENT_SEARCH_MATCH_REASON.BRANCH);
|
|
686
|
+
}
|
|
687
|
+
return matches;
|
|
688
|
+
}
|
|
689
|
+
async function contentMatchReasons(path7, options) {
|
|
690
|
+
if (options.query.contentNeedles.length === 0) {
|
|
691
|
+
return [];
|
|
692
|
+
}
|
|
693
|
+
const content = await options.fs.readText(path7).catch(() => null);
|
|
694
|
+
return content === null ? null : matchingContentNeedles(content, options.query.contentNeedles);
|
|
695
|
+
}
|
|
696
|
+
function matchingContentNeedles(content, needles) {
|
|
697
|
+
const matches = needles.filter((needle) => content.includes(needle.value)).map((needle) => needle.reason);
|
|
698
|
+
return matches.length === needles.length ? matches : null;
|
|
699
|
+
}
|
|
700
|
+
function coreMatchesProductScope(core, productScopeRoot) {
|
|
701
|
+
return isPathInsideOrEqual(productScopeRoot, core.cwd);
|
|
702
|
+
}
|
|
703
|
+
async function storeFiles(paths, fs8, nowMs, includeAll) {
|
|
704
|
+
const files = await mapWithConcurrency(paths, AGENT_RESUME_LIMITS.READ_CONCURRENCY, async (path7) => {
|
|
705
|
+
const stat9 = await fs8.stat(path7).catch(() => null);
|
|
706
|
+
if (stat9 === null) return null;
|
|
707
|
+
if (!includeAll && !isRecentAgentSessionMtime(stat9.mtimeMs, nowMs)) return null;
|
|
708
|
+
return { path: path7, modifiedAtMs: stat9.mtimeMs };
|
|
709
|
+
});
|
|
710
|
+
return files.filter((file) => file !== null).sort((left, right) => right.modifiedAtMs - left.modifiedAtMs || left.path.localeCompare(right.path));
|
|
711
|
+
}
|
|
712
|
+
function compareSearchResults(left, right) {
|
|
713
|
+
const modifiedDiff = right.modifiedAtMs - left.modifiedAtMs;
|
|
714
|
+
if (modifiedDiff !== 0) return modifiedDiff;
|
|
715
|
+
return `${left.agent}:${left.sessionId}`.localeCompare(`${right.agent}:${right.sessionId}`);
|
|
716
|
+
}
|
|
717
|
+
|
|
532
718
|
// src/git/root.ts
|
|
533
719
|
import { execa } from "execa";
|
|
534
720
|
import { basename, dirname, isAbsolute as isAbsolute2, join, resolve as resolve4 } from "path";
|
|
@@ -917,9 +1103,9 @@ var defaultAgentResumeCommandDeps = {
|
|
|
917
1103
|
fs: nodeAgentSessionFileSystem,
|
|
918
1104
|
homeDir: homedir,
|
|
919
1105
|
nowMs: Date.now,
|
|
920
|
-
resolveWorktreeRoot: async (cwd) => {
|
|
1106
|
+
resolveWorktreeRoot: async (cwd, fallbackWorktreeRoot) => {
|
|
921
1107
|
const result = await detectWorktreeProductRoot(cwd);
|
|
922
|
-
return result.isGitRepo ? result.productDir :
|
|
1108
|
+
return result.isGitRepo ? result.productDir : fallbackWorktreeRoot;
|
|
923
1109
|
}
|
|
924
1110
|
};
|
|
925
1111
|
async function loadAgentResumeCandidates(options) {
|
|
@@ -930,7 +1116,7 @@ async function loadAgentResumeCandidates(options) {
|
|
|
930
1116
|
nowMs: deps.nowMs(),
|
|
931
1117
|
scope: options.scope,
|
|
932
1118
|
fs: deps.fs,
|
|
933
|
-
resolveWorktreeRoot: deps.resolveWorktreeRoot
|
|
1119
|
+
resolveWorktreeRoot: (cwd) => deps.resolveWorktreeRoot(cwd, options.fallbackWorktreeRoot)
|
|
934
1120
|
});
|
|
935
1121
|
}
|
|
936
1122
|
async function listAgentResumeSessions(options) {
|
|
@@ -940,6 +1126,63 @@ async function jsonAgentResumeSessions(options) {
|
|
|
940
1126
|
return renderAgentResumeJson(await loadAgentResumeCandidates(options));
|
|
941
1127
|
}
|
|
942
1128
|
|
|
1129
|
+
// src/commands/agent/search.ts
|
|
1130
|
+
import { open as open2, readdir as readdir2, readFile, stat as stat2 } from "fs/promises";
|
|
1131
|
+
import { homedir as homedir2 } from "os";
|
|
1132
|
+
var nodeAgentSearchFileSystem = {
|
|
1133
|
+
async readDir(path7) {
|
|
1134
|
+
const entries = await readdir2(path7, { withFileTypes: true });
|
|
1135
|
+
return entries.map((entry) => ({
|
|
1136
|
+
name: entry.name,
|
|
1137
|
+
isDirectory: entry.isDirectory(),
|
|
1138
|
+
isFile: entry.isFile()
|
|
1139
|
+
}));
|
|
1140
|
+
},
|
|
1141
|
+
async readHead(path7, maxBytes) {
|
|
1142
|
+
const handle = await open2(path7, "r");
|
|
1143
|
+
try {
|
|
1144
|
+
const buffer = Buffer.alloc(maxBytes);
|
|
1145
|
+
const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
|
|
1146
|
+
return buffer.toString(AGENT_SESSION_STORE.TEXT_ENCODING, 0, bytesRead);
|
|
1147
|
+
} finally {
|
|
1148
|
+
await handle.close();
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
async readText(path7) {
|
|
1152
|
+
return readFile(path7, AGENT_SESSION_STORE.TEXT_ENCODING);
|
|
1153
|
+
},
|
|
1154
|
+
async stat(path7) {
|
|
1155
|
+
const result = await stat2(path7);
|
|
1156
|
+
return { mtimeMs: result.mtimeMs };
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
var defaultAgentSearchCommandDeps = {
|
|
1160
|
+
fs: nodeAgentSearchFileSystem,
|
|
1161
|
+
homeDir: homedir2,
|
|
1162
|
+
nowMs: Date.now,
|
|
1163
|
+
resolveProductScopeRoot: resolveAgentSearchProductScopeRoot
|
|
1164
|
+
};
|
|
1165
|
+
async function resolveAgentSearchProductScopeRoot(cwd, fallbackProductScopeRoot, gitDeps = defaultGitDependencies) {
|
|
1166
|
+
const result = await detectWorktreeProductRoot(cwd, gitDeps);
|
|
1167
|
+
return result.isGitRepo ? result.productDir : fallbackProductScopeRoot;
|
|
1168
|
+
}
|
|
1169
|
+
async function loadAgentSearchResults(options) {
|
|
1170
|
+
const deps = options.deps ?? defaultAgentSearchCommandDeps;
|
|
1171
|
+
return searchAgentSessions({
|
|
1172
|
+
homeDir: deps.homeDir(),
|
|
1173
|
+
nowMs: deps.nowMs(),
|
|
1174
|
+
productScopeRoot: await deps.resolveProductScopeRoot(options.cwd, options.fallbackProductScopeRoot),
|
|
1175
|
+
fs: deps.fs,
|
|
1176
|
+
query: options.query
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
async function listAgentSearchSessions(options) {
|
|
1180
|
+
return renderAgentSearchList(await loadAgentSearchResults(options));
|
|
1181
|
+
}
|
|
1182
|
+
async function jsonAgentSearchSessions(options) {
|
|
1183
|
+
return renderAgentSearchJson(await loadAgentSearchResults(options));
|
|
1184
|
+
}
|
|
1185
|
+
|
|
943
1186
|
// src/lib/process-lifecycle/exit-codes.ts
|
|
944
1187
|
var SIGINT_EXIT_CODE = 130;
|
|
945
1188
|
var SIGTERM_EXIT_CODE = 143;
|
|
@@ -1118,6 +1361,50 @@ function spawnManagedSubprocess(runner, command, args, options) {
|
|
|
1118
1361
|
});
|
|
1119
1362
|
}
|
|
1120
1363
|
|
|
1364
|
+
// src/lib/sanitize-cli-argument.ts
|
|
1365
|
+
var MAX_CLI_ARGUMENT_DISPLAY_LENGTH = 120;
|
|
1366
|
+
var ELLIPSIS_TOKEN = "...";
|
|
1367
|
+
var SENTINEL_UNDEFINED = "<undefined>";
|
|
1368
|
+
var SENTINEL_NULL = "<null>";
|
|
1369
|
+
var SENTINEL_EMPTY = "<empty>";
|
|
1370
|
+
var CONTROL_CHAR_UPPER_BOUND = 31;
|
|
1371
|
+
var DEL_CHAR_CODE = 127;
|
|
1372
|
+
var HEX_RADIX = 16;
|
|
1373
|
+
var HEX_PAD = 2;
|
|
1374
|
+
function formatHexEscape(code) {
|
|
1375
|
+
return String.raw`\x${code.toString(HEX_RADIX).padStart(HEX_PAD, "0")}`;
|
|
1376
|
+
}
|
|
1377
|
+
function nonStringSentinel(type) {
|
|
1378
|
+
return `<non-string:${type}>`;
|
|
1379
|
+
}
|
|
1380
|
+
function sanitizeCliArgument(input) {
|
|
1381
|
+
return truncate(escapeCliArgument(input));
|
|
1382
|
+
}
|
|
1383
|
+
function escapeCliArgument(input) {
|
|
1384
|
+
if (input === void 0) return SENTINEL_UNDEFINED;
|
|
1385
|
+
if (input === null) return SENTINEL_NULL;
|
|
1386
|
+
if (typeof input !== "string") return nonStringSentinel(typeof input);
|
|
1387
|
+
if (input.length === 0) return SENTINEL_EMPTY;
|
|
1388
|
+
return escapeControlCharacters(input);
|
|
1389
|
+
}
|
|
1390
|
+
function escapeControlCharacters(value) {
|
|
1391
|
+
let out = "";
|
|
1392
|
+
for (const char of value) {
|
|
1393
|
+
const code = char.codePointAt(0);
|
|
1394
|
+
if (code === void 0) continue;
|
|
1395
|
+
if (code <= CONTROL_CHAR_UPPER_BOUND || code === DEL_CHAR_CODE) {
|
|
1396
|
+
out += formatHexEscape(code);
|
|
1397
|
+
} else {
|
|
1398
|
+
out += char;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
return out;
|
|
1402
|
+
}
|
|
1403
|
+
function truncate(value) {
|
|
1404
|
+
if (value.length <= MAX_CLI_ARGUMENT_DISPLAY_LENGTH) return value;
|
|
1405
|
+
return value.slice(0, MAX_CLI_ARGUMENT_DISPLAY_LENGTH - ELLIPSIS_TOKEN.length) + ELLIPSIS_TOKEN;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1121
1408
|
// src/interfaces/cli/foreground-launch.ts
|
|
1122
1409
|
var FOREGROUND_LAUNCH_STDIO = "inherit";
|
|
1123
1410
|
var LAUNCH_FAILURE_STATUS = 1;
|
|
@@ -1240,14 +1527,26 @@ async function runAgentResumePicker(candidates, renderPicker = render) {
|
|
|
1240
1527
|
var AGENT_CLI = {
|
|
1241
1528
|
commandName: "agent",
|
|
1242
1529
|
resumeCommandName: "resume",
|
|
1530
|
+
searchCommandName: "search",
|
|
1243
1531
|
flags: {
|
|
1244
1532
|
latest: "--latest",
|
|
1245
1533
|
list: "--list",
|
|
1246
1534
|
json: "--json",
|
|
1247
|
-
branch: "--branch"
|
|
1535
|
+
branch: "--branch",
|
|
1536
|
+
all: "--all",
|
|
1537
|
+
pickupId: "--pickup-id",
|
|
1538
|
+
contains: "--contains",
|
|
1539
|
+
sessionId: "--session-id",
|
|
1540
|
+
agent: "--agent",
|
|
1541
|
+
limit: "--limit"
|
|
1248
1542
|
},
|
|
1249
1543
|
optionArgs: {
|
|
1250
|
-
branch: "--branch <name>"
|
|
1544
|
+
branch: "--branch <name>",
|
|
1545
|
+
pickupId: "--pickup-id <id>",
|
|
1546
|
+
contains: "--contains <literal>",
|
|
1547
|
+
sessionId: "--session-id <id>",
|
|
1548
|
+
agent: "--agent <kind>",
|
|
1549
|
+
limit: "--limit <count>"
|
|
1251
1550
|
}
|
|
1252
1551
|
};
|
|
1253
1552
|
var AGENT_CLI_EXIT = {
|
|
@@ -1265,6 +1564,7 @@ var DEFAULT_AGENT_CLI_DEPENDENCIES = {
|
|
|
1265
1564
|
);
|
|
1266
1565
|
}
|
|
1267
1566
|
};
|
|
1567
|
+
var POSITIVE_DECIMAL_INTEGER_PATTERN = /^[1-9][0-9]*$/;
|
|
1268
1568
|
function writeOutput(invocation, output) {
|
|
1269
1569
|
invocation.io.writeStdout(`${output}
|
|
1270
1570
|
`);
|
|
@@ -1281,6 +1581,33 @@ function handleError(invocation, error) {
|
|
|
1281
1581
|
function resumeScopeFromOptions(options) {
|
|
1282
1582
|
return options.branch === void 0 ? worktreeResumeScope() : branchResumeScope(options.branch);
|
|
1283
1583
|
}
|
|
1584
|
+
function parseSearchLimit(value) {
|
|
1585
|
+
if (!POSITIVE_DECIMAL_INTEGER_PATTERN.test(value)) {
|
|
1586
|
+
throw new Error(`agent search limit must be a positive integer: ${sanitizeCliArgument(value)}`);
|
|
1587
|
+
}
|
|
1588
|
+
const parsed = Number.parseInt(value, 10);
|
|
1589
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
1590
|
+
throw new Error(`agent search limit must be a positive integer: ${sanitizeCliArgument(value)}`);
|
|
1591
|
+
}
|
|
1592
|
+
return parsed;
|
|
1593
|
+
}
|
|
1594
|
+
function parseSearchAgentKind(value) {
|
|
1595
|
+
if (value === AGENT_SESSION_KIND.CODEX || value === AGENT_SESSION_KIND.CLAUDE_CODE) {
|
|
1596
|
+
return value;
|
|
1597
|
+
}
|
|
1598
|
+
throw new Error(`agent search kind must be codex or claude-code: ${sanitizeCliArgument(value)}`);
|
|
1599
|
+
}
|
|
1600
|
+
function searchQueryFromOptions(options) {
|
|
1601
|
+
return {
|
|
1602
|
+
pickupId: options.pickupId,
|
|
1603
|
+
contains: options.contains,
|
|
1604
|
+
sessionId: options.sessionId,
|
|
1605
|
+
branch: options.branch,
|
|
1606
|
+
agent: options.agent === void 0 ? void 0 : parseSearchAgentKind(options.agent),
|
|
1607
|
+
all: options.all,
|
|
1608
|
+
limit: options.limit === void 0 ? AGENT_SEARCH_DEFAULT_LIMIT : parseSearchLimit(options.limit)
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1284
1611
|
async function dispatchInteractiveResume(mode, commandOptions, deps, invocation) {
|
|
1285
1612
|
const candidates = await loadAgentResumeCandidates(commandOptions);
|
|
1286
1613
|
if (candidates.length === 0) {
|
|
@@ -1307,12 +1634,14 @@ function createAgentDomain(deps = {}) {
|
|
|
1307
1634
|
let requestedExitCode = AGENT_CLI_EXIT.SUCCESS;
|
|
1308
1635
|
try {
|
|
1309
1636
|
const mode = resolveAgentResumeMode(options);
|
|
1637
|
+
const productContext = invocation.resolveProductContext();
|
|
1310
1638
|
if (mode === AGENT_RESUME_MODE.PICK && !resolvedDeps.isInteractiveTerminal()) {
|
|
1311
1639
|
writeError(invocation, AGENT_RESUME_TEXT.INTERACTIVE_REQUIRED);
|
|
1312
1640
|
requestedExitCode = AGENT_CLI_EXIT.FAILURE;
|
|
1313
1641
|
} else {
|
|
1314
1642
|
const commandOptions = {
|
|
1315
|
-
cwd:
|
|
1643
|
+
cwd: productContext.effectiveInvocationDir,
|
|
1644
|
+
fallbackWorktreeRoot: productContext.productDir,
|
|
1316
1645
|
scope: resumeScopeFromOptions(options),
|
|
1317
1646
|
deps: resolvedDeps.resumeDeps
|
|
1318
1647
|
};
|
|
@@ -1331,6 +1660,21 @@ function createAgentDomain(deps = {}) {
|
|
|
1331
1660
|
}
|
|
1332
1661
|
return invocation.io.exit(requestedExitCode);
|
|
1333
1662
|
});
|
|
1663
|
+
agentCmd.command(AGENT_CLI.searchCommandName).description("Search Codex and Claude Code agent session transcripts for this product").option(AGENT_CLI.optionArgs.pickupId, "Search for an exact SPX pickup marker").option(AGENT_CLI.optionArgs.contains, "Search transcript content for a literal string").option(AGENT_CLI.optionArgs.sessionId, "Search for an agent session id").option(AGENT_CLI.optionArgs.branch, "Search for sessions started on the named branch").option(AGENT_CLI.optionArgs.agent, "Search only one agent kind").option(AGENT_CLI.flags.all, "Include sessions outside the recent-session window").option(AGENT_CLI.optionArgs.limit, "Maximum number of results").option(AGENT_CLI.flags.json, "Print matching sessions as JSON").action(async (options) => {
|
|
1664
|
+
try {
|
|
1665
|
+
const productContext = invocation.resolveProductContext();
|
|
1666
|
+
const commandOptions = {
|
|
1667
|
+
cwd: productContext.effectiveInvocationDir,
|
|
1668
|
+
fallbackProductScopeRoot: productContext.productDir,
|
|
1669
|
+
query: agentSearchQueryFromOptions(searchQueryFromOptions(options)),
|
|
1670
|
+
deps: resolvedDeps.searchDeps
|
|
1671
|
+
};
|
|
1672
|
+
const output = options.json === true ? await jsonAgentSearchSessions(commandOptions) : await listAgentSearchSessions(commandOptions);
|
|
1673
|
+
writeOutput(invocation, output);
|
|
1674
|
+
} catch (error) {
|
|
1675
|
+
handleError(invocation, error);
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1334
1678
|
}
|
|
1335
1679
|
};
|
|
1336
1680
|
}
|
|
@@ -1443,6 +1787,7 @@ import {
|
|
|
1443
1787
|
mkdir as nodeMkdir,
|
|
1444
1788
|
open as nodeOpen,
|
|
1445
1789
|
readdir as nodeReaddir,
|
|
1790
|
+
rename as nodeRename,
|
|
1446
1791
|
rm as nodeRm
|
|
1447
1792
|
} from "fs/promises";
|
|
1448
1793
|
import { dirname as dirname2, join as join2 } from "path";
|
|
@@ -1536,6 +1881,9 @@ var defaultFileSystem = {
|
|
|
1536
1881
|
},
|
|
1537
1882
|
readdir: nodeReaddir,
|
|
1538
1883
|
lstat: nodeLstat,
|
|
1884
|
+
rename: async (from, to) => {
|
|
1885
|
+
await nodeRename(from, to);
|
|
1886
|
+
},
|
|
1539
1887
|
rm: async (path7, options) => {
|
|
1540
1888
|
await nodeRm(path7, options);
|
|
1541
1889
|
}
|
|
@@ -1628,8 +1976,8 @@ function formatRunTimestamp(date) {
|
|
|
1628
1976
|
);
|
|
1629
1977
|
return `${year}-${month}-${day}${RUN_TIMESTAMP_SEPARATOR}${hours}-${minutes}-${seconds}-${milliseconds}`;
|
|
1630
1978
|
}
|
|
1631
|
-
function generateRunId(
|
|
1632
|
-
return
|
|
1979
|
+
function generateRunId(randomBytes3 = nodeRandomBytes) {
|
|
1980
|
+
return randomBytes3(STATE_STORE_RUN_TOKEN.ID_BYTES).toString(HEX_ENCODING);
|
|
1633
1981
|
}
|
|
1634
1982
|
function createStateStoreRunToken(options) {
|
|
1635
1983
|
const startedAt = formatRunTimestamp(options.date);
|
|
@@ -1651,7 +1999,7 @@ async function createJsonlRunFile(scopeDir, domainName, options = {}) {
|
|
|
1651
1999
|
if (!domainRunsDir.ok) return domainRunsDir;
|
|
1652
2000
|
const maxAttempts = options.maxAttempts ?? RUN_FILE_CREATE_ATTEMPTS;
|
|
1653
2001
|
const startedDate = (options.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
1654
|
-
const
|
|
2002
|
+
const randomBytes3 = options.randomBytes ?? nodeRandomBytes;
|
|
1655
2003
|
try {
|
|
1656
2004
|
await fs8.mkdir(domainRunsDir.value, { recursive: true });
|
|
1657
2005
|
} catch (error) {
|
|
@@ -1661,7 +2009,7 @@ async function createJsonlRunFile(scopeDir, domainName, options = {}) {
|
|
|
1661
2009
|
};
|
|
1662
2010
|
}
|
|
1663
2011
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
1664
|
-
const token = createStateStoreRunToken({ date: startedDate, randomBytes:
|
|
2012
|
+
const token = createStateStoreRunToken({ date: startedDate, randomBytes: randomBytes3 });
|
|
1665
2013
|
const name = runFileName(token.runToken);
|
|
1666
2014
|
const path7 = join2(domainRunsDir.value, name);
|
|
1667
2015
|
try {
|
|
@@ -2184,8 +2532,8 @@ import path2 from "path";
|
|
|
2184
2532
|
// src/lib/atomic-file-write.ts
|
|
2185
2533
|
var TEMP_TOKEN_BYTES = 8;
|
|
2186
2534
|
var TEMP_SUFFIX = ".tmp";
|
|
2187
|
-
function atomicWriteTempPath(targetPath,
|
|
2188
|
-
const token =
|
|
2535
|
+
function atomicWriteTempPath(targetPath, randomBytes3) {
|
|
2536
|
+
const token = randomBytes3(TEMP_TOKEN_BYTES).toString("hex");
|
|
2189
2537
|
return `${targetPath}.${token}${TEMP_SUFFIX}`;
|
|
2190
2538
|
}
|
|
2191
2539
|
async function writeFileAtomic(targetPath, content, options) {
|
|
@@ -2545,14 +2893,14 @@ async function compactRetrieveCommand(options) {
|
|
|
2545
2893
|
}
|
|
2546
2894
|
|
|
2547
2895
|
// src/commands/compact/store.ts
|
|
2548
|
-
import { readFile } from "fs/promises";
|
|
2896
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
2549
2897
|
var UTF8_ENCODING = "utf8";
|
|
2550
2898
|
async function compactStoreCommand(options) {
|
|
2551
2899
|
const sessionToken = resolveCompactSessionToken(options.sessionId, options.env ?? process.env);
|
|
2552
2900
|
if (sessionToken === void 0) return 1;
|
|
2553
2901
|
let transcript;
|
|
2554
2902
|
try {
|
|
2555
|
-
transcript = await
|
|
2903
|
+
transcript = await readFile2(options.transcript, UTF8_ENCODING);
|
|
2556
2904
|
} catch {
|
|
2557
2905
|
return 1;
|
|
2558
2906
|
}
|
|
@@ -2608,7 +2956,7 @@ var compactDomain = {
|
|
|
2608
2956
|
};
|
|
2609
2957
|
|
|
2610
2958
|
// src/config/index.ts
|
|
2611
|
-
import { readFile as
|
|
2959
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
2612
2960
|
import { join as join4 } from "path";
|
|
2613
2961
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
2614
2962
|
import { parse as parseYaml, parseDocument as parseYamlDocument, stringify as stringifyYaml } from "yaml";
|
|
@@ -2879,37 +3227,37 @@ function validateAgents(raw) {
|
|
|
2879
3227
|
}
|
|
2880
3228
|
return { ok: true, value: agents };
|
|
2881
3229
|
}
|
|
2882
|
-
function validateAgentConfig(path7, raw,
|
|
3230
|
+
function validateAgentConfig(path7, raw, defaults6) {
|
|
2883
3231
|
if (!isRecord2(raw)) return { ok: false, error: `${path7} must be an object` };
|
|
2884
3232
|
const unknown = rejectUnknownFields(path7, raw, HARNESS_ENVIRONMENT_AGENT_CONFIG_ALLOWED_FIELDS);
|
|
2885
3233
|
if (!unknown.ok) return unknown;
|
|
2886
3234
|
const enabledRaw = raw[HARNESS_ENVIRONMENT_CONFIG_FIELDS.ENABLED];
|
|
2887
|
-
const enabled = enabledRaw === void 0 ? { ok: true, value:
|
|
3235
|
+
const enabled = enabledRaw === void 0 ? { ok: true, value: defaults6.enabled } : validateBoolean(`${path7}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.ENABLED}`, enabledRaw);
|
|
2888
3236
|
if (!enabled.ok) return enabled;
|
|
2889
3237
|
const hooksRaw = raw[HARNESS_ENVIRONMENT_CONFIG_FIELDS.HOOKS];
|
|
2890
|
-
const hooks = hooksRaw === void 0 ? { ok: true, value:
|
|
3238
|
+
const hooks = hooksRaw === void 0 ? { ok: true, value: defaults6.hooks } : validateAgentHooks(`${path7}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.HOOKS}`, hooksRaw, defaults6.hooks);
|
|
2891
3239
|
if (!hooks.ok) return hooks;
|
|
2892
3240
|
return { ok: true, value: { enabled: enabled.value, hooks: hooks.value } };
|
|
2893
3241
|
}
|
|
2894
|
-
function validateAgentHooks(path7, raw,
|
|
3242
|
+
function validateAgentHooks(path7, raw, defaults6) {
|
|
2895
3243
|
if (!isRecord2(raw)) return { ok: false, error: `${path7} must be an object` };
|
|
2896
3244
|
const unknown = rejectUnknownFields(path7, raw, HARNESS_ENVIRONMENT_AGENT_HOOKS_ALLOWED_FIELDS);
|
|
2897
3245
|
if (!unknown.ok) return unknown;
|
|
2898
3246
|
const sessionStartRaw = raw[HARNESS_ENVIRONMENT_CONFIG_FIELDS.SESSION_START];
|
|
2899
|
-
const sessionStart = sessionStartRaw === void 0 ? { ok: true, value:
|
|
3247
|
+
const sessionStart = sessionStartRaw === void 0 ? { ok: true, value: defaults6.sessionStart } : validateSessionStartHooks(
|
|
2900
3248
|
`${path7}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.SESSION_START}`,
|
|
2901
3249
|
sessionStartRaw,
|
|
2902
|
-
|
|
3250
|
+
defaults6.sessionStart
|
|
2903
3251
|
);
|
|
2904
3252
|
if (!sessionStart.ok) return sessionStart;
|
|
2905
3253
|
return { ok: true, value: { sessionStart: sessionStart.value } };
|
|
2906
3254
|
}
|
|
2907
|
-
function validateSessionStartHooks(path7, raw,
|
|
3255
|
+
function validateSessionStartHooks(path7, raw, defaults6) {
|
|
2908
3256
|
if (!isRecord2(raw)) return { ok: false, error: `${path7} must be an object` };
|
|
2909
3257
|
const unknown = rejectUnknownFields(path7, raw, HARNESS_ENVIRONMENT_SESSION_START_HOOKS_ALLOWED_FIELDS);
|
|
2910
3258
|
if (!unknown.ok) return unknown;
|
|
2911
3259
|
const compactStdoutRaw = raw[HARNESS_ENVIRONMENT_CONFIG_FIELDS.COMPACT_STDOUT];
|
|
2912
|
-
const compactStdout = compactStdoutRaw === void 0 ? { ok: true, value:
|
|
3260
|
+
const compactStdout = compactStdoutRaw === void 0 ? { ok: true, value: defaults6.compactStdout } : validateBoolean(`${path7}.${HARNESS_ENVIRONMENT_CONFIG_FIELDS.COMPACT_STDOUT}`, compactStdoutRaw);
|
|
2913
3261
|
if (!compactStdout.ok) return compactStdout;
|
|
2914
3262
|
return { ok: true, value: { compactStdout: compactStdout.value } };
|
|
2915
3263
|
}
|
|
@@ -2982,8 +3330,8 @@ function validatePluginBootstrap(raw) {
|
|
|
2982
3330
|
}
|
|
2983
3331
|
};
|
|
2984
3332
|
}
|
|
2985
|
-
function validateEntryArray(path7, raw, validateEntry,
|
|
2986
|
-
if (raw === void 0) return { ok: true, value:
|
|
3333
|
+
function validateEntryArray(path7, raw, validateEntry, defaults6) {
|
|
3334
|
+
if (raw === void 0) return { ok: true, value: defaults6 };
|
|
2987
3335
|
if (!Array.isArray(raw)) return { ok: false, error: `${path7} must be an array` };
|
|
2988
3336
|
const entries = [];
|
|
2989
3337
|
for (const [index, entry] of raw.entries()) {
|
|
@@ -3274,6 +3622,36 @@ var diagnoseConfigDescriptor = {
|
|
|
3274
3622
|
validate: validate2
|
|
3275
3623
|
};
|
|
3276
3624
|
|
|
3625
|
+
// src/lib/agent-run-journal/config.ts
|
|
3626
|
+
var RUNTIME_SECTION = "runtime";
|
|
3627
|
+
var RUNTIME_CONFIG_FIELDS = {
|
|
3628
|
+
EVENT_NAMESPACE: "eventNamespace"
|
|
3629
|
+
};
|
|
3630
|
+
var RUNTIME_EVENT_NAMESPACE_DEFAULT = "sh.spx";
|
|
3631
|
+
var defaults2 = { eventNamespace: RUNTIME_EVENT_NAMESPACE_DEFAULT };
|
|
3632
|
+
function validate3(value) {
|
|
3633
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3634
|
+
return { ok: false, error: `${RUNTIME_SECTION} section must be an object` };
|
|
3635
|
+
}
|
|
3636
|
+
const candidate = value;
|
|
3637
|
+
const raw = candidate[RUNTIME_CONFIG_FIELDS.EVENT_NAMESPACE];
|
|
3638
|
+
if (raw === void 0) {
|
|
3639
|
+
return { ok: true, value: { eventNamespace: RUNTIME_EVENT_NAMESPACE_DEFAULT } };
|
|
3640
|
+
}
|
|
3641
|
+
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
3642
|
+
return {
|
|
3643
|
+
ok: false,
|
|
3644
|
+
error: `${RUNTIME_SECTION}.${RUNTIME_CONFIG_FIELDS.EVENT_NAMESPACE} must be a non-empty string`
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
return { ok: true, value: { eventNamespace: raw } };
|
|
3648
|
+
}
|
|
3649
|
+
var runtimeConfigDescriptor = {
|
|
3650
|
+
section: RUNTIME_SECTION,
|
|
3651
|
+
defaults: defaults2,
|
|
3652
|
+
validate: validate3
|
|
3653
|
+
};
|
|
3654
|
+
|
|
3277
3655
|
// src/lib/file-inclusion/adapters/eslint.ts
|
|
3278
3656
|
function eslintAdapter(scope2, config) {
|
|
3279
3657
|
return scope2.excluded.flatMap((entry) => [config.ignoreFlag, entry.path]);
|
|
@@ -3334,7 +3712,7 @@ var DEFAULT_SCOPE_CONFIG = {};
|
|
|
3334
3712
|
var DEFAULT_TOOLS_CONFIG = Object.fromEntries(
|
|
3335
3713
|
Object.entries(TOOL_DEFAULT_FLAGS).map(([name, flag]) => [name, { ignoreFlag: flag }])
|
|
3336
3714
|
);
|
|
3337
|
-
var
|
|
3715
|
+
var defaults3 = {
|
|
3338
3716
|
scope: DEFAULT_SCOPE_CONFIG,
|
|
3339
3717
|
tools: DEFAULT_TOOLS_CONFIG
|
|
3340
3718
|
};
|
|
@@ -3406,7 +3784,7 @@ function validateTools(raw) {
|
|
|
3406
3784
|
}
|
|
3407
3785
|
return { ok: true, value: tools };
|
|
3408
3786
|
}
|
|
3409
|
-
function
|
|
3787
|
+
function validate4(value) {
|
|
3410
3788
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3411
3789
|
return { ok: false, error: `${FILE_INCLUSION_SECTION} section must be an object` };
|
|
3412
3790
|
}
|
|
@@ -3433,8 +3811,8 @@ function validate3(value) {
|
|
|
3433
3811
|
}
|
|
3434
3812
|
var fileInclusionConfigDescriptor = {
|
|
3435
3813
|
section: FILE_INCLUSION_SECTION,
|
|
3436
|
-
defaults:
|
|
3437
|
-
validate:
|
|
3814
|
+
defaults: defaults3,
|
|
3815
|
+
validate: validate4
|
|
3438
3816
|
};
|
|
3439
3817
|
|
|
3440
3818
|
// src/config/source-roots.ts
|
|
@@ -3457,7 +3835,7 @@ var PRECOMMIT_DEFAULTS = {
|
|
|
3457
3835
|
sourceDirs: TEST_RELEVANT_SOURCE_ROOT_PREFIXES,
|
|
3458
3836
|
testPattern: ".test.ts"
|
|
3459
3837
|
};
|
|
3460
|
-
function
|
|
3838
|
+
function validate5(value) {
|
|
3461
3839
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3462
3840
|
return { ok: false, error: `${PRECOMMIT_SECTION} section must be an object` };
|
|
3463
3841
|
}
|
|
@@ -3487,7 +3865,7 @@ function validate4(value) {
|
|
|
3487
3865
|
var precommitConfigDescriptor = {
|
|
3488
3866
|
section: PRECOMMIT_SECTION,
|
|
3489
3867
|
defaults: PRECOMMIT_DEFAULTS,
|
|
3490
|
-
validate:
|
|
3868
|
+
validate: validate5
|
|
3491
3869
|
};
|
|
3492
3870
|
|
|
3493
3871
|
// src/lib/spec-tree/config.ts
|
|
@@ -3678,7 +4056,7 @@ function buildConfigFromKindNames(kindNames) {
|
|
|
3678
4056
|
const entries = kindNames.map((kind) => [kind, KIND_REGISTRY[kind]]);
|
|
3679
4057
|
return { kinds: Object.fromEntries(entries) };
|
|
3680
4058
|
}
|
|
3681
|
-
function
|
|
4059
|
+
function validate6(value) {
|
|
3682
4060
|
if (typeof value !== "object" || value === null) {
|
|
3683
4061
|
return { ok: false, error: `${SPEC_TREE_SECTION} section must be an object` };
|
|
3684
4062
|
}
|
|
@@ -3777,7 +4155,7 @@ function validateKindDefinitionMap(kindEntries) {
|
|
|
3777
4155
|
var specTreeConfigDescriptor = {
|
|
3778
4156
|
section: SPEC_TREE_SECTION,
|
|
3779
4157
|
defaults: buildDefaults(),
|
|
3780
|
-
validate:
|
|
4158
|
+
validate: validate6
|
|
3781
4159
|
};
|
|
3782
4160
|
|
|
3783
4161
|
// src/config/primitives/path-filter.ts
|
|
@@ -3850,7 +4228,7 @@ var TESTING_CONFIG_FIELDS = {
|
|
|
3850
4228
|
PASSING_SCOPE: "passingScope"
|
|
3851
4229
|
};
|
|
3852
4230
|
var DEFAULT_PASSING_SCOPE = resolveDefaultPassingScope();
|
|
3853
|
-
var
|
|
4231
|
+
var defaults4 = {
|
|
3854
4232
|
passingScope: DEFAULT_PASSING_SCOPE
|
|
3855
4233
|
};
|
|
3856
4234
|
function resolveDefaultPassingScope() {
|
|
@@ -3863,14 +4241,14 @@ function resolveDefaultPassingScope() {
|
|
|
3863
4241
|
}
|
|
3864
4242
|
return result.value;
|
|
3865
4243
|
}
|
|
3866
|
-
function
|
|
4244
|
+
function validate7(value) {
|
|
3867
4245
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3868
4246
|
return { ok: false, error: `${TESTING_SECTION} section must be an object` };
|
|
3869
4247
|
}
|
|
3870
4248
|
const candidate = value;
|
|
3871
4249
|
const passingScopeRaw = candidate[TESTING_CONFIG_FIELDS.PASSING_SCOPE];
|
|
3872
4250
|
if (passingScopeRaw === void 0) {
|
|
3873
|
-
return { ok: true, value:
|
|
4251
|
+
return { ok: true, value: defaults4 };
|
|
3874
4252
|
}
|
|
3875
4253
|
const passingScopeResult = validatePathFilterConfig(
|
|
3876
4254
|
passingScopeRaw,
|
|
@@ -3886,8 +4264,8 @@ function validate6(value) {
|
|
|
3886
4264
|
}
|
|
3887
4265
|
var testingConfigDescriptor = {
|
|
3888
4266
|
section: TESTING_SECTION,
|
|
3889
|
-
defaults:
|
|
3890
|
-
validate:
|
|
4267
|
+
defaults: defaults4,
|
|
4268
|
+
validate: validate7
|
|
3891
4269
|
};
|
|
3892
4270
|
|
|
3893
4271
|
// src/validation/literal/config.ts
|
|
@@ -3951,7 +4329,7 @@ var LITERAL_DEFAULTS = {
|
|
|
3951
4329
|
function isNonNegativeInteger(value) {
|
|
3952
4330
|
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
3953
4331
|
}
|
|
3954
|
-
function
|
|
4332
|
+
function validate8(value) {
|
|
3955
4333
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
3956
4334
|
return { ok: false, error: `${LITERAL_SECTION} section must be an object` };
|
|
3957
4335
|
}
|
|
@@ -4015,7 +4393,7 @@ function isStringArray(value) {
|
|
|
4015
4393
|
var literalConfigDescriptor = {
|
|
4016
4394
|
section: LITERAL_SECTION,
|
|
4017
4395
|
defaults: LITERAL_DEFAULTS,
|
|
4018
|
-
validate:
|
|
4396
|
+
validate: validate8
|
|
4019
4397
|
};
|
|
4020
4398
|
|
|
4021
4399
|
// src/validation/config/descriptor.ts
|
|
@@ -4034,7 +4412,7 @@ var VALIDATION_PATH_TOOL_SUBSECTIONS = {
|
|
|
4034
4412
|
LITERAL: "literal",
|
|
4035
4413
|
FORMATTING: "formatting"
|
|
4036
4414
|
};
|
|
4037
|
-
var
|
|
4415
|
+
var defaults5 = {
|
|
4038
4416
|
paths: {},
|
|
4039
4417
|
literal: {
|
|
4040
4418
|
enabled: true,
|
|
@@ -4074,7 +4452,7 @@ function validateLiteral(raw) {
|
|
|
4074
4452
|
};
|
|
4075
4453
|
}
|
|
4076
4454
|
const candidate = raw;
|
|
4077
|
-
const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ??
|
|
4455
|
+
const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ?? defaults5.literal.enabled;
|
|
4078
4456
|
if (typeof enabledRaw !== "boolean") {
|
|
4079
4457
|
return {
|
|
4080
4458
|
ok: false,
|
|
@@ -4099,7 +4477,7 @@ function validateKnip(raw) {
|
|
|
4099
4477
|
};
|
|
4100
4478
|
}
|
|
4101
4479
|
const candidate = raw;
|
|
4102
|
-
const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ??
|
|
4480
|
+
const enabledRaw = candidate[VALIDATION_ENABLED_FIELD] ?? defaults5.knip.enabled;
|
|
4103
4481
|
if (typeof enabledRaw !== "boolean") {
|
|
4104
4482
|
return {
|
|
4105
4483
|
ok: false,
|
|
@@ -4108,7 +4486,7 @@ function validateKnip(raw) {
|
|
|
4108
4486
|
}
|
|
4109
4487
|
return { ok: true, value: { enabled: enabledRaw } };
|
|
4110
4488
|
}
|
|
4111
|
-
function
|
|
4489
|
+
function validate9(value) {
|
|
4112
4490
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4113
4491
|
return { ok: false, error: `${VALIDATION_SECTION} section must be an object` };
|
|
4114
4492
|
}
|
|
@@ -4126,8 +4504,8 @@ function validate8(value) {
|
|
|
4126
4504
|
}
|
|
4127
4505
|
var validationConfigDescriptor = {
|
|
4128
4506
|
section: VALIDATION_SECTION,
|
|
4129
|
-
defaults:
|
|
4130
|
-
validate:
|
|
4507
|
+
defaults: defaults5,
|
|
4508
|
+
validate: validate9
|
|
4131
4509
|
};
|
|
4132
4510
|
|
|
4133
4511
|
// src/config/registry.ts
|
|
@@ -4138,15 +4516,16 @@ var productionRegistry = [
|
|
|
4138
4516
|
fileInclusionConfigDescriptor,
|
|
4139
4517
|
precommitConfigDescriptor,
|
|
4140
4518
|
harnessEnvironmentConfigDescriptor,
|
|
4141
|
-
diagnoseConfigDescriptor
|
|
4519
|
+
diagnoseConfigDescriptor,
|
|
4520
|
+
runtimeConfigDescriptor
|
|
4142
4521
|
];
|
|
4143
4522
|
|
|
4144
4523
|
// src/config/descriptor-digest.ts
|
|
4145
4524
|
import { createHash as createHash3 } from "crypto";
|
|
4146
4525
|
var DEFAULT_DESCRIPTOR_PATH = "descriptor section";
|
|
4147
|
-
var
|
|
4526
|
+
var DESCRIPTOR_DIGEST_SHA256_ALGORITHM = "sha256";
|
|
4148
4527
|
var UTF8_ENCODING2 = "utf8";
|
|
4149
|
-
var
|
|
4528
|
+
var DESCRIPTOR_DIGEST_HEX_ENCODING = "hex";
|
|
4150
4529
|
function canonicalDescriptorJson(value, path7 = DEFAULT_DESCRIPTOR_PATH) {
|
|
4151
4530
|
const normalized = normalizeDescriptorJsonValue(value, path7, /* @__PURE__ */ new WeakSet());
|
|
4152
4531
|
if (!normalized.ok) return normalized;
|
|
@@ -4155,7 +4534,7 @@ function canonicalDescriptorJson(value, path7 = DEFAULT_DESCRIPTOR_PATH) {
|
|
|
4155
4534
|
function digestDescriptorSection(value, path7 = DEFAULT_DESCRIPTOR_PATH) {
|
|
4156
4535
|
const canonical = canonicalDescriptorJson(value, path7);
|
|
4157
4536
|
if (!canonical.ok) return canonical;
|
|
4158
|
-
const sha256 = createHash3(
|
|
4537
|
+
const sha256 = createHash3(DESCRIPTOR_DIGEST_SHA256_ALGORITHM).update(Buffer.from(canonical.value, UTF8_ENCODING2)).digest(DESCRIPTOR_DIGEST_HEX_ENCODING);
|
|
4159
4538
|
return {
|
|
4160
4539
|
ok: true,
|
|
4161
4540
|
value: {
|
|
@@ -4269,7 +4648,7 @@ async function readProductConfigFile(productDir) {
|
|
|
4269
4648
|
const path7 = join4(productDir, filename);
|
|
4270
4649
|
let raw;
|
|
4271
4650
|
try {
|
|
4272
|
-
raw = await
|
|
4651
|
+
raw = await readFile3(path7, "utf8");
|
|
4273
4652
|
} catch (error) {
|
|
4274
4653
|
if (isFileNotFound(error)) continue;
|
|
4275
4654
|
return { ok: false, error: `failed to read ${filename}: ${toMessage(error)}` };
|
|
@@ -4553,7 +4932,7 @@ var configDomain = {
|
|
|
4553
4932
|
};
|
|
4554
4933
|
|
|
4555
4934
|
// src/interfaces/cli/diagnose.ts
|
|
4556
|
-
import { readFile as
|
|
4935
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
4557
4936
|
import { Option } from "commander";
|
|
4558
4937
|
|
|
4559
4938
|
// src/domains/diagnose/types.ts
|
|
@@ -5898,7 +6277,7 @@ import {
|
|
|
5898
6277
|
mkdir as nodeMkdir2,
|
|
5899
6278
|
readFile as nodeReadFile,
|
|
5900
6279
|
readlink as nodeReadlink,
|
|
5901
|
-
rename as
|
|
6280
|
+
rename as nodeRename2,
|
|
5902
6281
|
rm as nodeRm2,
|
|
5903
6282
|
symlink as nodeSymlink,
|
|
5904
6283
|
writeFile as nodeWriteFile
|
|
@@ -5910,7 +6289,7 @@ var defaultOccupancyFileSystem = {
|
|
|
5910
6289
|
writeFile: async (path7, data) => {
|
|
5911
6290
|
await nodeWriteFile(path7, data);
|
|
5912
6291
|
},
|
|
5913
|
-
rename:
|
|
6292
|
+
rename: nodeRename2,
|
|
5914
6293
|
symlink: nodeSymlink,
|
|
5915
6294
|
readlink: nodeReadlink,
|
|
5916
6295
|
readFile: nodeReadFile,
|
|
@@ -6251,50 +6630,6 @@ var defaultSpxReachabilityProbe = {
|
|
|
6251
6630
|
}
|
|
6252
6631
|
};
|
|
6253
6632
|
|
|
6254
|
-
// src/lib/sanitize-cli-argument.ts
|
|
6255
|
-
var MAX_CLI_ARGUMENT_DISPLAY_LENGTH = 120;
|
|
6256
|
-
var ELLIPSIS_TOKEN = "...";
|
|
6257
|
-
var SENTINEL_UNDEFINED = "<undefined>";
|
|
6258
|
-
var SENTINEL_NULL = "<null>";
|
|
6259
|
-
var SENTINEL_EMPTY = "<empty>";
|
|
6260
|
-
var CONTROL_CHAR_UPPER_BOUND = 31;
|
|
6261
|
-
var DEL_CHAR_CODE = 127;
|
|
6262
|
-
var HEX_RADIX = 16;
|
|
6263
|
-
var HEX_PAD = 2;
|
|
6264
|
-
function formatHexEscape(code) {
|
|
6265
|
-
return String.raw`\x${code.toString(HEX_RADIX).padStart(HEX_PAD, "0")}`;
|
|
6266
|
-
}
|
|
6267
|
-
function nonStringSentinel(type) {
|
|
6268
|
-
return `<non-string:${type}>`;
|
|
6269
|
-
}
|
|
6270
|
-
function sanitizeCliArgument(input) {
|
|
6271
|
-
return truncate(escapeCliArgument(input));
|
|
6272
|
-
}
|
|
6273
|
-
function escapeCliArgument(input) {
|
|
6274
|
-
if (input === void 0) return SENTINEL_UNDEFINED;
|
|
6275
|
-
if (input === null) return SENTINEL_NULL;
|
|
6276
|
-
if (typeof input !== "string") return nonStringSentinel(typeof input);
|
|
6277
|
-
if (input.length === 0) return SENTINEL_EMPTY;
|
|
6278
|
-
return escapeControlCharacters(input);
|
|
6279
|
-
}
|
|
6280
|
-
function escapeControlCharacters(value) {
|
|
6281
|
-
let out = "";
|
|
6282
|
-
for (const char of value) {
|
|
6283
|
-
const code = char.codePointAt(0);
|
|
6284
|
-
if (code === void 0) continue;
|
|
6285
|
-
if (code <= CONTROL_CHAR_UPPER_BOUND || code === DEL_CHAR_CODE) {
|
|
6286
|
-
out += formatHexEscape(code);
|
|
6287
|
-
} else {
|
|
6288
|
-
out += char;
|
|
6289
|
-
}
|
|
6290
|
-
}
|
|
6291
|
-
return out;
|
|
6292
|
-
}
|
|
6293
|
-
function truncate(value) {
|
|
6294
|
-
if (value.length <= MAX_CLI_ARGUMENT_DISPLAY_LENGTH) return value;
|
|
6295
|
-
return value.slice(0, MAX_CLI_ARGUMENT_DISPLAY_LENGTH - ELLIPSIS_TOKEN.length) + ELLIPSIS_TOKEN;
|
|
6296
|
-
}
|
|
6297
|
-
|
|
6298
6633
|
// src/interfaces/cli/diagnose.ts
|
|
6299
6634
|
var DIAGNOSE_CLI = {
|
|
6300
6635
|
COMMAND: "diagnose",
|
|
@@ -6341,7 +6676,7 @@ var diagnoseDomain = {
|
|
|
6341
6676
|
isTty: Boolean(process.stdout.isTTY)
|
|
6342
6677
|
}),
|
|
6343
6678
|
registry: defaultRegistry(),
|
|
6344
|
-
fs: { readFile: (path7) =>
|
|
6679
|
+
fs: { readFile: (path7) => readFile4(path7, "utf8") }
|
|
6345
6680
|
});
|
|
6346
6681
|
if (!result.ok) {
|
|
6347
6682
|
handleError2(result.error, invocation.io);
|
|
@@ -6845,9 +7180,9 @@ var JOURNAL_RUN_STATE_INCOMPLETE_REASON = {
|
|
|
6845
7180
|
};
|
|
6846
7181
|
var JOURNAL_RUN_EVENT = {
|
|
6847
7182
|
SOURCE: "/spx/journal",
|
|
6848
|
-
STARTED_TYPE:
|
|
6849
|
-
PROGRESS_TYPE:
|
|
6850
|
-
COMPLETED_TYPE:
|
|
7183
|
+
STARTED_TYPE: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.journal.run.started`,
|
|
7184
|
+
PROGRESS_TYPE: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.journal.run.progress`,
|
|
7185
|
+
COMPLETED_TYPE: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.journal.run.completed`
|
|
6851
7186
|
};
|
|
6852
7187
|
var JOURNAL_RUN_STATE_FIELDS = {
|
|
6853
7188
|
BRANCH_NAME: "branchName",
|
|
@@ -6866,13 +7201,18 @@ var JOURNAL_RUN_STATE_FIELDS = {
|
|
|
6866
7201
|
STATUS: "status"
|
|
6867
7202
|
};
|
|
6868
7203
|
function foldJournalRunState(events, sealed) {
|
|
6869
|
-
if (!sealed)
|
|
7204
|
+
if (!sealed) {
|
|
7205
|
+
return { ok: false, reason: JOURNAL_RUN_STATE_INCOMPLETE_REASON.UNSEALED };
|
|
7206
|
+
}
|
|
6870
7207
|
let completed;
|
|
6871
7208
|
for (const event of events) {
|
|
6872
7209
|
if (event.type === JOURNAL_RUN_EVENT.COMPLETED_TYPE) completed = event;
|
|
6873
7210
|
}
|
|
6874
7211
|
if (completed === void 0) {
|
|
6875
|
-
return {
|
|
7212
|
+
return {
|
|
7213
|
+
ok: false,
|
|
7214
|
+
reason: JOURNAL_RUN_STATE_INCOMPLETE_REASON.MISSING_STATE
|
|
7215
|
+
};
|
|
6876
7216
|
}
|
|
6877
7217
|
const validated = validateJournalRunState(completed.data);
|
|
6878
7218
|
if (!validated.ok) {
|
|
@@ -6885,13 +7225,17 @@ function foldJournalRunState(events, sealed) {
|
|
|
6885
7225
|
return validated;
|
|
6886
7226
|
}
|
|
6887
7227
|
function isJournalRunStateStatus(value) {
|
|
6888
|
-
return typeof value === "string" && Object.values(JOURNAL_RUN_STATE_STATUS).includes(
|
|
7228
|
+
return typeof value === "string" && Object.values(JOURNAL_RUN_STATE_STATUS).includes(
|
|
7229
|
+
value
|
|
7230
|
+
);
|
|
6889
7231
|
}
|
|
6890
7232
|
function isJournalTargetKind(value) {
|
|
6891
7233
|
return typeof value === "string" && Object.values(JOURNAL_TARGET_KIND).includes(value);
|
|
6892
7234
|
}
|
|
6893
7235
|
function validateJournalRunState(value) {
|
|
6894
|
-
if (!isRecord5(value))
|
|
7236
|
+
if (!isRecord5(value)) {
|
|
7237
|
+
return { ok: false, error: "journal run state must be an object" };
|
|
7238
|
+
}
|
|
6895
7239
|
const identity = readRunStateIdentity(value);
|
|
6896
7240
|
if (!identity.ok) return identity;
|
|
6897
7241
|
const body = readRunStateBody(value);
|
|
@@ -6903,9 +7247,15 @@ function readRunStateIdentity(value) {
|
|
|
6903
7247
|
if (!branchName.ok) return branchName;
|
|
6904
7248
|
const branchSlug = readString(value, JOURNAL_RUN_STATE_FIELDS.BRANCH_SLUG);
|
|
6905
7249
|
if (!branchSlug.ok) return branchSlug;
|
|
6906
|
-
const targetKind = readTargetKind(
|
|
7250
|
+
const targetKind = readTargetKind(
|
|
7251
|
+
value,
|
|
7252
|
+
JOURNAL_RUN_STATE_FIELDS.TARGET_KIND
|
|
7253
|
+
);
|
|
6907
7254
|
if (!targetKind.ok) return targetKind;
|
|
6908
|
-
const pullRequestNumber = readOptionalNonNegativeInteger(
|
|
7255
|
+
const pullRequestNumber = readOptionalNonNegativeInteger(
|
|
7256
|
+
value,
|
|
7257
|
+
JOURNAL_RUN_STATE_FIELDS.PULL_REQUEST_NUMBER
|
|
7258
|
+
);
|
|
6909
7259
|
if (!pullRequestNumber.ok) return pullRequestNumber;
|
|
6910
7260
|
const headSha = readString(value, JOURNAL_RUN_STATE_FIELDS.HEAD_SHA);
|
|
6911
7261
|
if (!headSha.ok) return headSha;
|
|
@@ -6927,9 +7277,15 @@ function readRunStateIdentity(value) {
|
|
|
6927
7277
|
};
|
|
6928
7278
|
}
|
|
6929
7279
|
function readRunStateBody(value) {
|
|
6930
|
-
const configDigest = readString(
|
|
7280
|
+
const configDigest = readString(
|
|
7281
|
+
value,
|
|
7282
|
+
JOURNAL_RUN_STATE_FIELDS.CONFIG_DIGEST
|
|
7283
|
+
);
|
|
6931
7284
|
if (!configDigest.ok) return configDigest;
|
|
6932
|
-
const participants = readStringArray(
|
|
7285
|
+
const participants = readStringArray(
|
|
7286
|
+
value,
|
|
7287
|
+
JOURNAL_RUN_STATE_FIELDS.PARTICIPANTS
|
|
7288
|
+
);
|
|
6933
7289
|
if (!participants.ok) return participants;
|
|
6934
7290
|
const scope2 = readPathFilter(value, JOURNAL_RUN_STATE_FIELDS.SCOPE);
|
|
6935
7291
|
if (!scope2.ok) return scope2;
|
|
@@ -6937,7 +7293,10 @@ function readRunStateBody(value) {
|
|
|
6937
7293
|
if (!startedAt.ok) return startedAt;
|
|
6938
7294
|
const completedAt = readString(value, JOURNAL_RUN_STATE_FIELDS.COMPLETED_AT);
|
|
6939
7295
|
if (!completedAt.ok) return completedAt;
|
|
6940
|
-
const outputPaths = readStringArray(
|
|
7296
|
+
const outputPaths = readStringArray(
|
|
7297
|
+
value,
|
|
7298
|
+
JOURNAL_RUN_STATE_FIELDS.OUTPUT_PATHS
|
|
7299
|
+
);
|
|
6941
7300
|
if (!outputPaths.ok) return outputPaths;
|
|
6942
7301
|
const status = readStatus(value, JOURNAL_RUN_STATE_FIELDS.STATUS);
|
|
6943
7302
|
if (!status.ok) return status;
|
|
@@ -6966,7 +7325,10 @@ function readOptionalString(value, field) {
|
|
|
6966
7325
|
function readOptionalNonNegativeInteger(value, field) {
|
|
6967
7326
|
const raw = value[field];
|
|
6968
7327
|
if (raw === void 0) return { ok: true, value: void 0 };
|
|
6969
|
-
return typeof raw === "number" && Number.isInteger(raw) && raw >= 0 ? { ok: true, value: raw } : {
|
|
7328
|
+
return typeof raw === "number" && Number.isInteger(raw) && raw >= 0 ? { ok: true, value: raw } : {
|
|
7329
|
+
ok: false,
|
|
7330
|
+
error: `${field} must be a non-negative integer when present`
|
|
7331
|
+
};
|
|
6970
7332
|
}
|
|
6971
7333
|
function readStringArray(value, field) {
|
|
6972
7334
|
const raw = value[field];
|
|
@@ -7208,6 +7570,7 @@ function createJournal(backend, identity) {
|
|
|
7208
7570
|
|
|
7209
7571
|
// src/lib/appendable-journal-store/index.ts
|
|
7210
7572
|
var SEAL_MARKER_SUFFIX = ".sealed";
|
|
7573
|
+
var APPENDABLE_JOURNAL_SEAL_MARKER_CONTENT = "";
|
|
7211
7574
|
var LINE_SEPARATOR3 = "\n";
|
|
7212
7575
|
function appendableJournalSealMarkerPath(runFilePath) {
|
|
7213
7576
|
return `${runFilePath}${SEAL_MARKER_SUFFIX}`;
|
|
@@ -7247,7 +7610,7 @@ function createAppendableJournalStore(options) {
|
|
|
7247
7610
|
readAll,
|
|
7248
7611
|
async seal() {
|
|
7249
7612
|
await fs8.mkdir(dirname5(sealMarkerPath), { recursive: true });
|
|
7250
|
-
await fs8.writeFile(sealMarkerPath,
|
|
7613
|
+
await fs8.writeFile(sealMarkerPath, APPENDABLE_JOURNAL_SEAL_MARKER_CONTENT);
|
|
7251
7614
|
},
|
|
7252
7615
|
async isSealed() {
|
|
7253
7616
|
return await readFileOrUndefined(fs8, sealMarkerPath) !== void 0;
|
|
@@ -7658,6 +8021,18 @@ async function readSealedJournalRunSet(scope2, options = {}) {
|
|
|
7658
8021
|
}))
|
|
7659
8022
|
};
|
|
7660
8023
|
}
|
|
8024
|
+
async function findJournalRunBranchSlugs(scope2, options = {}) {
|
|
8025
|
+
const fs8 = options.fs ?? defaultFileSystem;
|
|
8026
|
+
const branches = await branchSlugs(scope2, fs8);
|
|
8027
|
+
if (!branches.ok) return branches;
|
|
8028
|
+
const matches = [];
|
|
8029
|
+
for (const branchSlug of branches.value) {
|
|
8030
|
+
const runFilePath = bindRunFilePath({ ...scope2, branchSlug });
|
|
8031
|
+
if (!runFilePath.ok) return runFilePath;
|
|
8032
|
+
if (await runFileExists(fs8, runFilePath.value)) matches.push(branchSlug);
|
|
8033
|
+
}
|
|
8034
|
+
return { ok: true, value: matches };
|
|
8035
|
+
}
|
|
7661
8036
|
async function appendJournalEvent(ref, input, sink, options = {}) {
|
|
7662
8037
|
const bound = await bindJournal(ref, options.fs);
|
|
7663
8038
|
if (!bound.ok) return bound;
|
|
@@ -7692,6 +8067,20 @@ async function sealJournalRun(ref, options = {}) {
|
|
|
7692
8067
|
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.SEAL_FAILED}: ${toMessage(error)}` };
|
|
7693
8068
|
}
|
|
7694
8069
|
}
|
|
8070
|
+
async function isJournalRunSealed(ref, options = {}) {
|
|
8071
|
+
const runFilePath = bindRunFilePath(ref);
|
|
8072
|
+
if (!runFilePath.ok) return runFilePath;
|
|
8073
|
+
const fs8 = options.fs ?? defaultFileSystem;
|
|
8074
|
+
if (!await runFileExists(fs8, runFilePath.value)) {
|
|
8075
|
+
return { ok: false, error: JOURNAL_RUNTIME_ERROR.RUN_NOT_FOUND };
|
|
8076
|
+
}
|
|
8077
|
+
const store = createAppendableJournalStore({ runFilePath: runFilePath.value, fs: fs8 });
|
|
8078
|
+
try {
|
|
8079
|
+
return { ok: true, value: await store.isSealed() };
|
|
8080
|
+
} catch (error) {
|
|
8081
|
+
return { ok: false, error: `${JOURNAL_RUNTIME_ERROR.READ_FAILED}: ${toMessage(error)}` };
|
|
8082
|
+
}
|
|
8083
|
+
}
|
|
7695
8084
|
async function renderJournalRun(ref, projection, options = {}) {
|
|
7696
8085
|
const bound = await bindJournal(ref, options.fs);
|
|
7697
8086
|
if (!bound.ok) return bound;
|
|
@@ -7736,6 +8125,7 @@ var JOURNAL_CLI_ERROR = {
|
|
|
7736
8125
|
INVALID_READ_SET_EVENT_LIMIT: "journal read-set event limit must be a positive whole integer",
|
|
7737
8126
|
INVALID_SEALED_FILTER: "journal list sealed filter is not registered",
|
|
7738
8127
|
INVALID_TERMINAL_STATE_FILTER: "journal list terminal-state filter is not registered",
|
|
8128
|
+
RUN_TOKEN_AMBIGUOUS: "journal run token matches multiple branch scopes; rerun with --branch-slug",
|
|
7739
8129
|
OPEN_HYDRATION_FAILED: "journal open failed to hydrate the pull request's prior runs"
|
|
7740
8130
|
};
|
|
7741
8131
|
var CURSOR_PATTERN = /^\d+$/;
|
|
@@ -7858,6 +8248,34 @@ function verbOptions(deps) {
|
|
|
7858
8248
|
function runRef(context, runToken) {
|
|
7859
8249
|
return { productDir: context.productDir, branchSlug: context.branchSlug, type: context.type, runToken };
|
|
7860
8250
|
}
|
|
8251
|
+
async function inspectionRunRef(scope2, deps) {
|
|
8252
|
+
const context = await resolveJournalRunContext(scope2, deps);
|
|
8253
|
+
if (!context.ok) return context;
|
|
8254
|
+
if (scope2.branchSlug !== void 0) return { ok: true, value: runRef(context.value, scope2.runToken) };
|
|
8255
|
+
const branches = await findJournalRunBranchSlugs(
|
|
8256
|
+
{
|
|
8257
|
+
productDir: context.value.productDir,
|
|
8258
|
+
type: context.value.type,
|
|
8259
|
+
runToken: scope2.runToken
|
|
8260
|
+
},
|
|
8261
|
+
verbOptions(deps)
|
|
8262
|
+
);
|
|
8263
|
+
if (!branches.ok) return branches;
|
|
8264
|
+
if (branches.value.length === 1) {
|
|
8265
|
+
const branchSlug = branches.value[0];
|
|
8266
|
+
return {
|
|
8267
|
+
ok: true,
|
|
8268
|
+
value: {
|
|
8269
|
+
productDir: context.value.productDir,
|
|
8270
|
+
branchSlug,
|
|
8271
|
+
type: context.value.type,
|
|
8272
|
+
runToken: scope2.runToken
|
|
8273
|
+
}
|
|
8274
|
+
};
|
|
8275
|
+
}
|
|
8276
|
+
if (branches.value.length > 1) return { ok: false, error: JOURNAL_CLI_ERROR.RUN_TOKEN_AMBIGUOUS };
|
|
8277
|
+
return { ok: true, value: runRef(context.value, scope2.runToken) };
|
|
8278
|
+
}
|
|
7861
8279
|
function okResult(output) {
|
|
7862
8280
|
return { exitCode: JOURNAL_CLI_EXIT_CODE.OK, output };
|
|
7863
8281
|
}
|
|
@@ -7974,9 +8392,9 @@ async function journalAppendCommand(scope2, input, binding, deps = {}) {
|
|
|
7974
8392
|
async function journalReadCommand(scope2, fromCursor, deps = {}) {
|
|
7975
8393
|
const cursor = parseJournalCursor(fromCursor);
|
|
7976
8394
|
if (!cursor.ok) return errorResult(cursor.error);
|
|
7977
|
-
const
|
|
7978
|
-
if (!
|
|
7979
|
-
const events = await readJournalEvents(
|
|
8395
|
+
const ref = await inspectionRunRef(scope2, deps);
|
|
8396
|
+
if (!ref.ok) return errorResult(ref.error);
|
|
8397
|
+
const events = await readJournalEvents(ref.value, cursor.value, verbOptions(deps));
|
|
7980
8398
|
if (!events.ok) return errorResult(events.error);
|
|
7981
8399
|
return okResult(JSON.stringify(events.value));
|
|
7982
8400
|
}
|
|
@@ -8025,10 +8443,10 @@ async function journalSealCommand(scope2, deps = {}) {
|
|
|
8025
8443
|
return okResult(JSON.stringify({ sealed: true }));
|
|
8026
8444
|
}
|
|
8027
8445
|
async function journalRenderCommand(scope2, deps = {}) {
|
|
8028
|
-
const
|
|
8029
|
-
if (!
|
|
8446
|
+
const ref = await inspectionRunRef(scope2, deps);
|
|
8447
|
+
if (!ref.ok) return errorResult(ref.error);
|
|
8030
8448
|
const rendered = await renderJournalRun(
|
|
8031
|
-
|
|
8449
|
+
ref.value,
|
|
8032
8450
|
(events) => [...events],
|
|
8033
8451
|
verbOptions(deps)
|
|
8034
8452
|
);
|
|
@@ -8354,7 +8772,7 @@ function sessionOptionToken(option) {
|
|
|
8354
8772
|
}
|
|
8355
8773
|
|
|
8356
8774
|
// src/commands/session/archive.ts
|
|
8357
|
-
import { mkdir, rename, stat as
|
|
8775
|
+
import { mkdir, rename, stat as stat3 } from "fs/promises";
|
|
8358
8776
|
import { dirname as dirname7, join as join8 } from "path";
|
|
8359
8777
|
|
|
8360
8778
|
// src/domains/session/archive.ts
|
|
@@ -8667,7 +9085,7 @@ var SessionAlreadyArchivedError = class extends Error {
|
|
|
8667
9085
|
};
|
|
8668
9086
|
async function fileExists(path7) {
|
|
8669
9087
|
try {
|
|
8670
|
-
const stats = await
|
|
9088
|
+
const stats = await stat3(path7);
|
|
8671
9089
|
return stats.isFile();
|
|
8672
9090
|
} catch {
|
|
8673
9091
|
return false;
|
|
@@ -8709,7 +9127,7 @@ async function archiveCommand(options) {
|
|
|
8709
9127
|
}
|
|
8710
9128
|
|
|
8711
9129
|
// src/commands/session/delete.ts
|
|
8712
|
-
import { stat as
|
|
9130
|
+
import { stat as stat4, unlink } from "fs/promises";
|
|
8713
9131
|
|
|
8714
9132
|
// src/domains/session/delete.ts
|
|
8715
9133
|
function resolveDeletePath(sessionId, existingPaths) {
|
|
@@ -8882,44 +9300,6 @@ function parseSessionId(id) {
|
|
|
8882
9300
|
return date;
|
|
8883
9301
|
}
|
|
8884
9302
|
|
|
8885
|
-
// src/domains/session/types.ts
|
|
8886
|
-
var SESSION_PRIORITY = {
|
|
8887
|
-
HIGH: "high",
|
|
8888
|
-
MEDIUM: "medium",
|
|
8889
|
-
LOW: "low"
|
|
8890
|
-
};
|
|
8891
|
-
var SESSION_STATUSES = ["todo", "doing", "archive"];
|
|
8892
|
-
var DEFAULT_LIST_STATUSES = ["doing", "todo"];
|
|
8893
|
-
var SESSION_FILE_ENCODING = "utf-8";
|
|
8894
|
-
var SESSION_FILE_ERROR_CODE = {
|
|
8895
|
-
NOT_FOUND: "ENOENT"
|
|
8896
|
-
};
|
|
8897
|
-
var SESSION_OUTPUT_MARKER = {
|
|
8898
|
-
HANDOFF_ID: "HANDOFF_ID",
|
|
8899
|
-
PICKUP_ID: "PICKUP_ID",
|
|
8900
|
-
SESSION_FILE: "SESSION_FILE"
|
|
8901
|
-
};
|
|
8902
|
-
function formatSessionOutputMarker(marker, value) {
|
|
8903
|
-
return `<${marker}>${value}</${marker}>`;
|
|
8904
|
-
}
|
|
8905
|
-
var CLAIMABLE_STATUS = SESSION_STATUSES[0];
|
|
8906
|
-
var PRIORITY_ORDER = {
|
|
8907
|
-
[SESSION_PRIORITY.HIGH]: 0,
|
|
8908
|
-
[SESSION_PRIORITY.MEDIUM]: 1,
|
|
8909
|
-
[SESSION_PRIORITY.LOW]: 2
|
|
8910
|
-
};
|
|
8911
|
-
var DEFAULT_PRIORITY = SESSION_PRIORITY.MEDIUM;
|
|
8912
|
-
var SESSION_FRONT_MATTER = {
|
|
8913
|
-
PRIORITY: "priority",
|
|
8914
|
-
GIT_REF: "git_ref",
|
|
8915
|
-
GOAL: "goal",
|
|
8916
|
-
NEXT_STEP: "next_step",
|
|
8917
|
-
CREATED_AT: "created_at",
|
|
8918
|
-
AGENT_SESSION_ID: "agent_session_id",
|
|
8919
|
-
SPECS: "specs",
|
|
8920
|
-
FILES: "files"
|
|
8921
|
-
};
|
|
8922
|
-
|
|
8923
9303
|
// src/domains/session/list.ts
|
|
8924
9304
|
var FRONT_MATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n(?:---|\.\.\.)\r?\n?/;
|
|
8925
9305
|
var SESSION_PRIORITY_VALUES = Object.values(SESSION_PRIORITY);
|
|
@@ -9158,7 +9538,7 @@ async function findExistingPaths(paths) {
|
|
|
9158
9538
|
const existing = [];
|
|
9159
9539
|
for (const path7 of paths) {
|
|
9160
9540
|
try {
|
|
9161
|
-
const stats = await
|
|
9541
|
+
const stats = await stat4(path7);
|
|
9162
9542
|
if (stats.isFile()) {
|
|
9163
9543
|
existing.push(path7);
|
|
9164
9544
|
}
|
|
@@ -9180,7 +9560,7 @@ async function deleteCommand(options) {
|
|
|
9180
9560
|
}
|
|
9181
9561
|
|
|
9182
9562
|
// src/commands/session/handoff.ts
|
|
9183
|
-
import { mkdir as mkdir2, stat as
|
|
9563
|
+
import { mkdir as mkdir2, stat as stat5, writeFile } from "fs/promises";
|
|
9184
9564
|
import { join as join10, resolve as resolve6 } from "path";
|
|
9185
9565
|
import { stringify as stringifyYaml3 } from "yaml";
|
|
9186
9566
|
|
|
@@ -9431,7 +9811,7 @@ async function rejectDirectoryInjectionEntries(entries, cwd) {
|
|
|
9431
9811
|
if (entry.length === 0) continue;
|
|
9432
9812
|
let entryIsDirectory = false;
|
|
9433
9813
|
try {
|
|
9434
|
-
entryIsDirectory = (await
|
|
9814
|
+
entryIsDirectory = (await stat5(resolve6(cwd, entry))).isDirectory();
|
|
9435
9815
|
} catch {
|
|
9436
9816
|
continue;
|
|
9437
9817
|
}
|
|
@@ -9489,7 +9869,7 @@ ${formatSessionOutputMarker(SESSION_OUTPUT_MARKER.SESSION_FILE, absolutePath)}`;
|
|
|
9489
9869
|
}
|
|
9490
9870
|
|
|
9491
9871
|
// src/commands/session/list.ts
|
|
9492
|
-
import { readdir as
|
|
9872
|
+
import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
|
|
9493
9873
|
import { join as join11 } from "path";
|
|
9494
9874
|
var SESSION_LIST_FORMAT = {
|
|
9495
9875
|
TEXT: "text",
|
|
@@ -9498,13 +9878,13 @@ var SESSION_LIST_FORMAT = {
|
|
|
9498
9878
|
var SESSION_LIST_EMPTY_TEXT = "(no sessions)";
|
|
9499
9879
|
async function loadSessionsFromDir(dir, status) {
|
|
9500
9880
|
try {
|
|
9501
|
-
const files = await
|
|
9881
|
+
const files = await readdir3(dir);
|
|
9502
9882
|
const sessions = [];
|
|
9503
9883
|
for (const file of files) {
|
|
9504
9884
|
if (!file.endsWith(".md")) continue;
|
|
9505
9885
|
const id = file.replace(".md", "");
|
|
9506
9886
|
const filePath = join11(dir, file);
|
|
9507
|
-
const content = await
|
|
9887
|
+
const content = await readFile5(filePath, SESSION_FILE_ENCODING);
|
|
9508
9888
|
const metadata = parseSessionMetadata(content);
|
|
9509
9889
|
sessions.push({
|
|
9510
9890
|
id,
|
|
@@ -9567,7 +9947,7 @@ async function listCommand(options) {
|
|
|
9567
9947
|
}
|
|
9568
9948
|
|
|
9569
9949
|
// src/commands/session/pickup.ts
|
|
9570
|
-
import { mkdir as mkdir3, readdir as
|
|
9950
|
+
import { mkdir as mkdir3, readdir as readdir4, readFile as readFile6, rename as rename2 } from "fs/promises";
|
|
9571
9951
|
import { join as join12, resolve as resolve7 } from "path";
|
|
9572
9952
|
|
|
9573
9953
|
// src/domains/session/pickup.ts
|
|
@@ -9607,8 +9987,8 @@ function selectBestSession(sessions) {
|
|
|
9607
9987
|
var PICKUP_TARGET_STATUS = SESSION_STATUSES[1];
|
|
9608
9988
|
var PICKUP_DEPS = {
|
|
9609
9989
|
mkdir: mkdir3,
|
|
9610
|
-
readdir:
|
|
9611
|
-
readFile:
|
|
9990
|
+
readdir: readdir4,
|
|
9991
|
+
readFile: readFile6,
|
|
9612
9992
|
rename: rename2
|
|
9613
9993
|
};
|
|
9614
9994
|
async function loadTodoSessions(config) {
|
|
@@ -9717,7 +10097,7 @@ async function loadPickCandidates(options) {
|
|
|
9717
10097
|
}
|
|
9718
10098
|
|
|
9719
10099
|
// src/commands/session/prune.ts
|
|
9720
|
-
import { readdir as
|
|
10100
|
+
import { readdir as readdir5, readFile as readFile7, unlink as unlink2 } from "fs/promises";
|
|
9721
10101
|
import { join as join13 } from "path";
|
|
9722
10102
|
|
|
9723
10103
|
// src/domains/session/prune.ts
|
|
@@ -9762,13 +10142,13 @@ function validatePruneOptions(options) {
|
|
|
9762
10142
|
}
|
|
9763
10143
|
async function loadArchiveSessions(config) {
|
|
9764
10144
|
try {
|
|
9765
|
-
const files = await
|
|
10145
|
+
const files = await readdir5(config.archiveDir);
|
|
9766
10146
|
const sessions = [];
|
|
9767
10147
|
for (const file of files) {
|
|
9768
10148
|
if (!file.endsWith(".md")) continue;
|
|
9769
10149
|
const id = file.replace(".md", "");
|
|
9770
10150
|
const filePath = join13(config.archiveDir, file);
|
|
9771
|
-
const content = await
|
|
10151
|
+
const content = await readFile7(filePath, SESSION_FILE_ENCODING);
|
|
9772
10152
|
const metadata = parseSessionMetadata(content);
|
|
9773
10153
|
sessions.push({
|
|
9774
10154
|
id,
|
|
@@ -9817,7 +10197,7 @@ async function pruneCommand(options) {
|
|
|
9817
10197
|
}
|
|
9818
10198
|
|
|
9819
10199
|
// src/commands/session/release.ts
|
|
9820
|
-
import { readdir as
|
|
10200
|
+
import { readdir as readdir6, rename as rename3 } from "fs/promises";
|
|
9821
10201
|
|
|
9822
10202
|
// src/domains/session/release.ts
|
|
9823
10203
|
function buildReleasePaths(sessionId, config) {
|
|
@@ -9848,7 +10228,7 @@ var SESSION_RELEASE_OUTPUT = {
|
|
|
9848
10228
|
};
|
|
9849
10229
|
async function loadDoingSessions(config) {
|
|
9850
10230
|
try {
|
|
9851
|
-
const files = await
|
|
10231
|
+
const files = await readdir6(config.doingDir);
|
|
9852
10232
|
return files.filter((file) => file.endsWith(".md")).map((file) => ({ id: file.replace(".md", "") }));
|
|
9853
10233
|
} catch (error) {
|
|
9854
10234
|
if (error instanceof Error && "code" in error && error.code === SESSION_FILE_ERROR_CODE.NOT_FOUND) {
|
|
@@ -9885,12 +10265,12 @@ async function releaseCommand(options) {
|
|
|
9885
10265
|
}
|
|
9886
10266
|
|
|
9887
10267
|
// src/commands/session/show.ts
|
|
9888
|
-
import { readFile as
|
|
10268
|
+
import { readFile as readFile8, stat as stat6 } from "fs/promises";
|
|
9889
10269
|
async function findExistingPath(paths, _config) {
|
|
9890
10270
|
for (let i = 0; i < paths.length; i++) {
|
|
9891
10271
|
const filePath = paths[i];
|
|
9892
10272
|
try {
|
|
9893
|
-
const stats = await
|
|
10273
|
+
const stats = await stat6(filePath);
|
|
9894
10274
|
if (stats.isFile()) {
|
|
9895
10275
|
return { path: filePath, status: SEARCH_ORDER[i] };
|
|
9896
10276
|
}
|
|
@@ -9905,7 +10285,7 @@ async function resolveSession(sessionId, config) {
|
|
|
9905
10285
|
if (!found) {
|
|
9906
10286
|
throw new SessionNotFoundError(sessionId);
|
|
9907
10287
|
}
|
|
9908
|
-
const content = await
|
|
10288
|
+
const content = await readFile8(found.path, SESSION_FILE_ENCODING);
|
|
9909
10289
|
return { status: found.status, path: found.path, content };
|
|
9910
10290
|
}
|
|
9911
10291
|
async function showSingle(sessionId, config) {
|
|
@@ -10569,7 +10949,7 @@ var sessionDomain = {
|
|
|
10569
10949
|
};
|
|
10570
10950
|
|
|
10571
10951
|
// src/lib/spec-tree/index.ts
|
|
10572
|
-
import { readdir as
|
|
10952
|
+
import { readdir as readdir7, readFile as readFile9 } from "fs/promises";
|
|
10573
10953
|
import { join as join14 } from "path";
|
|
10574
10954
|
var SPEC_TREE_FIELD_KEY = {
|
|
10575
10955
|
VERSION: "version",
|
|
@@ -10654,7 +11034,7 @@ function createFilesystemSpecTreeSource(options) {
|
|
|
10654
11034
|
if (ref.path === void 0) {
|
|
10655
11035
|
throw new Error("Filesystem source refs require a path");
|
|
10656
11036
|
}
|
|
10657
|
-
return
|
|
11037
|
+
return readFile9(join14(options.productDir, ref.path), SPEC_TREE_TEXT_ENCODING);
|
|
10658
11038
|
}
|
|
10659
11039
|
};
|
|
10660
11040
|
}
|
|
@@ -10945,7 +11325,7 @@ async function* readFilesystemSourceEntries(productDir, registry, schemaVersions
|
|
|
10945
11325
|
async function* walkFilesystemDirectory(context) {
|
|
10946
11326
|
let entries;
|
|
10947
11327
|
try {
|
|
10948
|
-
entries = await
|
|
11328
|
+
entries = await readdir7(context.absolutePath, { withFileTypes: true });
|
|
10949
11329
|
} catch (error) {
|
|
10950
11330
|
if (isFileNotFound2(error)) return;
|
|
10951
11331
|
throw error;
|
|
@@ -11117,7 +11497,7 @@ function formatNextNode(node) {
|
|
|
11117
11497
|
}
|
|
11118
11498
|
|
|
11119
11499
|
// src/commands/test/discovery.ts
|
|
11120
|
-
import { readdir as
|
|
11500
|
+
import { readdir as readdir8 } from "fs/promises";
|
|
11121
11501
|
import { join as join15, relative as relative2, sep as sep2 } from "path";
|
|
11122
11502
|
var TESTS_DIRECTORY_NAME = SPEC_TREE_EVIDENCE_FILE.DIRECTORY_NAME;
|
|
11123
11503
|
var SPEC_ROOT_DIRECTORY = SPEC_TREE_CONFIG.ROOT_DIRECTORY;
|
|
@@ -11131,7 +11511,7 @@ async function discoverTestFiles(productDir) {
|
|
|
11131
11511
|
async function collectTestFiles(directory, insideTestsDir, found) {
|
|
11132
11512
|
let entries;
|
|
11133
11513
|
try {
|
|
11134
|
-
entries = await
|
|
11514
|
+
entries = await readdir8(directory, { withFileTypes: true });
|
|
11135
11515
|
} catch (error) {
|
|
11136
11516
|
if (hasErrorCode2(error, ERROR_CODE_NOT_FOUND2)) return;
|
|
11137
11517
|
throw error;
|
|
@@ -11380,8 +11760,8 @@ var PRODUCT_INPUT_DIGEST_FIELDS = {
|
|
|
11380
11760
|
DESCRIPTOR_ID: "descriptorId",
|
|
11381
11761
|
DIGEST: "digest"
|
|
11382
11762
|
};
|
|
11383
|
-
var
|
|
11384
|
-
var
|
|
11763
|
+
var SHA256_ALGORITHM2 = "sha256";
|
|
11764
|
+
var HEX_ENCODING2 = "hex";
|
|
11385
11765
|
var SEGMENT_SEPARATOR = "-";
|
|
11386
11766
|
var defaultFileSystem2 = defaultFileSystem;
|
|
11387
11767
|
function formatTestRunTimestamp(date) {
|
|
@@ -11657,7 +12037,7 @@ function isRecord6(value) {
|
|
|
11657
12037
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11658
12038
|
}
|
|
11659
12039
|
function sha256Hex2(value) {
|
|
11660
|
-
return createHash4(
|
|
12040
|
+
return createHash4(SHA256_ALGORITHM2).update(value).digest(HEX_ENCODING2);
|
|
11661
12041
|
}
|
|
11662
12042
|
var UNKNOWN_ERROR_MESSAGE = "unknown error";
|
|
11663
12043
|
function toErrorMessage3(error) {
|
|
@@ -12542,7 +12922,7 @@ function createNodeStatusProvider(productDir) {
|
|
|
12542
12922
|
}
|
|
12543
12923
|
|
|
12544
12924
|
// src/lib/node-status/update.ts
|
|
12545
|
-
import { mkdir as mkdir4, readdir as
|
|
12925
|
+
import { mkdir as mkdir4, readdir as readdir9, rm, writeFile as writeFile2 } from "fs/promises";
|
|
12546
12926
|
import { dirname as dirname8, join as join21 } from "path";
|
|
12547
12927
|
|
|
12548
12928
|
// src/git/tracked-paths.ts
|
|
@@ -12666,7 +13046,7 @@ async function collectNodeStatusFiles(directory) {
|
|
|
12666
13046
|
}
|
|
12667
13047
|
async function readDirectoryEntries(directory) {
|
|
12668
13048
|
try {
|
|
12669
|
-
return await
|
|
13049
|
+
return await readdir9(directory, { withFileTypes: true });
|
|
12670
13050
|
} catch (error) {
|
|
12671
13051
|
if (isNodeError3(error) && error.code === "ENOENT") return [];
|
|
12672
13052
|
throw error;
|
|
@@ -13275,7 +13655,7 @@ var testingRegistry = {
|
|
|
13275
13655
|
|
|
13276
13656
|
// src/interfaces/cli/test-runner-deps.ts
|
|
13277
13657
|
import { createWriteStream } from "fs";
|
|
13278
|
-
import { mkdtemp, readFile as
|
|
13658
|
+
import { mkdtemp, readFile as readFile10 } from "fs/promises";
|
|
13279
13659
|
import { tmpdir } from "os";
|
|
13280
13660
|
import { join as join23 } from "path";
|
|
13281
13661
|
import { finished } from "stream/promises";
|
|
@@ -13325,7 +13705,7 @@ function createRunnerDepsFor(productDir, outStream = process.stdout) {
|
|
|
13325
13705
|
}
|
|
13326
13706
|
function createRelatedDepsFor(productDir) {
|
|
13327
13707
|
const runCommand = createRelatedCommandRunner(productDir);
|
|
13328
|
-
return () => ({ runCommand, readFile: (path7) =>
|
|
13708
|
+
return () => ({ runCommand, readFile: (path7) => readFile10(join23(productDir, path7), "utf8") });
|
|
13329
13709
|
}
|
|
13330
13710
|
function artifactFileName(index, suffix) {
|
|
13331
13711
|
return `${index.toString(ARTIFACT_INDEX_RADIX).padStart(ARTIFACT_INDEX_WIDTH, "0")}-${suffix}`;
|
|
@@ -17025,11 +17405,11 @@ function formatLintResult(result, quiet, durationMs) {
|
|
|
17025
17405
|
}
|
|
17026
17406
|
|
|
17027
17407
|
// src/validation/literal/index.ts
|
|
17028
|
-
import { readFile as
|
|
17408
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
17029
17409
|
import { isAbsolute as isAbsolute9, relative as relative7, resolve as resolve10 } from "path";
|
|
17030
17410
|
|
|
17031
17411
|
// src/lib/file-inclusion/pipeline.ts
|
|
17032
|
-
import { readdir as
|
|
17412
|
+
import { readdir as readdir10, readFile as readFile11, stat as stat7 } from "fs/promises";
|
|
17033
17413
|
import { join as join33, relative as relative6, sep as sep3 } from "path";
|
|
17034
17414
|
|
|
17035
17415
|
// src/lib/file-inclusion/ignore-source.ts
|
|
@@ -17335,7 +17715,7 @@ function isNodeError4(err) {
|
|
|
17335
17715
|
}
|
|
17336
17716
|
async function isDirectory(absolutePath) {
|
|
17337
17717
|
try {
|
|
17338
|
-
return (await
|
|
17718
|
+
return (await stat7(absolutePath)).isDirectory();
|
|
17339
17719
|
} catch (err) {
|
|
17340
17720
|
if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
|
|
17341
17721
|
return false;
|
|
@@ -17345,7 +17725,7 @@ async function isDirectory(absolutePath) {
|
|
|
17345
17725
|
}
|
|
17346
17726
|
async function isGitdirPointerFile(absolutePath) {
|
|
17347
17727
|
try {
|
|
17348
|
-
const content = await
|
|
17728
|
+
const content = await readFile11(absolutePath, "utf8");
|
|
17349
17729
|
return content.startsWith(GITDIR_POINTER_PREFIX);
|
|
17350
17730
|
} catch (err) {
|
|
17351
17731
|
if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
|
|
@@ -17356,7 +17736,7 @@ async function isGitdirPointerFile(absolutePath) {
|
|
|
17356
17736
|
}
|
|
17357
17737
|
async function readDirectoryEntries2(absoluteDir) {
|
|
17358
17738
|
try {
|
|
17359
|
-
return await
|
|
17739
|
+
return await readdir10(absoluteDir, { withFileTypes: true });
|
|
17360
17740
|
} catch (err) {
|
|
17361
17741
|
if (isNodeError4(err) && (err.code === "ENOENT" || err.code === "ENOTDIR")) {
|
|
17362
17742
|
return [];
|
|
@@ -17969,7 +18349,7 @@ async function validateLiteralReuse(input) {
|
|
|
17969
18349
|
}
|
|
17970
18350
|
async function readSafe(path7) {
|
|
17971
18351
|
try {
|
|
17972
|
-
return await
|
|
18352
|
+
return await readFile12(path7, "utf8");
|
|
17973
18353
|
} catch (err) {
|
|
17974
18354
|
if (typeof err === "object" && err !== null && "code" in err) {
|
|
17975
18355
|
const code = err.code;
|
|
@@ -19115,13 +19495,13 @@ async function persistVerificationContext(scope2, document, options = {}) {
|
|
|
19115
19495
|
const existing = await readExistingContext(fs8, contextPath.value);
|
|
19116
19496
|
if (!existing.ok) return existing;
|
|
19117
19497
|
if (existing.value === document.canonicalJson) {
|
|
19118
|
-
return { ok: true, value: { digest: document.digest, contextPath: contextPath.value } };
|
|
19498
|
+
return { ok: true, value: { digest: document.digest, contextPath: contextPath.value, created: false } };
|
|
19119
19499
|
}
|
|
19120
19500
|
return { ok: false, error: VERIFICATION_CONTEXT_RUNTIME_ERROR.CONTENT_MISMATCH };
|
|
19121
19501
|
}
|
|
19122
19502
|
return { ok: false, error: `${VERIFICATION_CONTEXT_RUNTIME_ERROR.WRITE_FAILED}: ${toMessage(error)}` };
|
|
19123
19503
|
}
|
|
19124
|
-
return { ok: true, value: { digest: document.digest, contextPath: contextPath.value } };
|
|
19504
|
+
return { ok: true, value: { digest: document.digest, contextPath: contextPath.value, created: true } };
|
|
19125
19505
|
}
|
|
19126
19506
|
async function readExistingContext(fs8, contextPath) {
|
|
19127
19507
|
try {
|
|
@@ -19264,9 +19644,10 @@ var verificationContextDomain = {
|
|
|
19264
19644
|
};
|
|
19265
19645
|
|
|
19266
19646
|
// src/interfaces/cli/verify.ts
|
|
19267
|
-
import { readFile as
|
|
19647
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
19268
19648
|
|
|
19269
19649
|
// src/commands/verify/cli.ts
|
|
19650
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
19270
19651
|
import { dirname as dirname12 } from "path";
|
|
19271
19652
|
|
|
19272
19653
|
// src/domains/verify/verify.ts
|
|
@@ -19279,18 +19660,35 @@ var VERIFY_VERB = {
|
|
|
19279
19660
|
START: "start",
|
|
19280
19661
|
INPUT: "input",
|
|
19281
19662
|
APPEND_SCOPE: "append-scope",
|
|
19282
|
-
APPEND_FINDING: "append-finding"
|
|
19663
|
+
APPEND_FINDING: "append-finding",
|
|
19664
|
+
FINISH: "finish",
|
|
19665
|
+
STATUS: "status",
|
|
19666
|
+
RENDER: "render"
|
|
19283
19667
|
};
|
|
19668
|
+
var VERIFY_LIFECYCLE_ACTION = {
|
|
19669
|
+
SCOPE_ADD: "scope add",
|
|
19670
|
+
FINDING_ADD: "finding add",
|
|
19671
|
+
FINISH: VERIFY_VERB.FINISH
|
|
19672
|
+
};
|
|
19673
|
+
var UNSEALED_NEXT_ACTIONS = [
|
|
19674
|
+
VERIFY_LIFECYCLE_ACTION.SCOPE_ADD,
|
|
19675
|
+
VERIFY_LIFECYCLE_ACTION.FINDING_ADD,
|
|
19676
|
+
VERIFY_LIFECYCLE_ACTION.FINISH
|
|
19677
|
+
];
|
|
19284
19678
|
var VERIFY_VERIFICATION_TYPE = {
|
|
19285
19679
|
REVIEW: "review"
|
|
19286
19680
|
};
|
|
19681
|
+
var VERIFY_VERIFICATION_TYPES = new Set(Object.values(VERIFY_VERIFICATION_TYPE));
|
|
19682
|
+
function isVerifyVerificationType(value) {
|
|
19683
|
+
return VERIFY_VERIFICATION_TYPES.has(value);
|
|
19684
|
+
}
|
|
19287
19685
|
var REVIEW_FINDING_DISPOSITION = {
|
|
19288
19686
|
BLOCKING: "BLOCKING",
|
|
19289
19687
|
DEBT: "DEBT"
|
|
19290
19688
|
};
|
|
19291
19689
|
var VERIFY_APPEND_EVENT_TYPE = {
|
|
19292
|
-
SCOPE:
|
|
19293
|
-
FINDING:
|
|
19690
|
+
SCOPE: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.verify.scope`,
|
|
19691
|
+
FINDING: `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.verify.finding`
|
|
19294
19692
|
};
|
|
19295
19693
|
var VERIFY_EVENT_SOURCE = "/spx/verify";
|
|
19296
19694
|
var VERIFY_APPEND_EVENT_FIELD = {
|
|
@@ -19308,7 +19706,9 @@ var VERIFY_SCOPE_ERROR = {
|
|
|
19308
19706
|
var VERIFY_INPUT_DIGEST_PATH = "verify run input";
|
|
19309
19707
|
function parseChangesetScope(scope2) {
|
|
19310
19708
|
const separatorIndex = scope2.indexOf(VERIFY_SCOPE_SEPARATOR);
|
|
19311
|
-
if (separatorIndex < 0)
|
|
19709
|
+
if (separatorIndex < 0) {
|
|
19710
|
+
return { ok: false, error: VERIFY_SCOPE_ERROR.MALFORMED_CHANGESET };
|
|
19711
|
+
}
|
|
19312
19712
|
const base = scope2.slice(0, separatorIndex);
|
|
19313
19713
|
const head = scope2.slice(separatorIndex + VERIFY_SCOPE_SEPARATOR.length);
|
|
19314
19714
|
if (base.length === 0 || head.length === 0 || head.includes(VERIFY_SCOPE_SEPARATOR)) {
|
|
@@ -19328,7 +19728,10 @@ function buildRunLocator(parts) {
|
|
|
19328
19728
|
};
|
|
19329
19729
|
}
|
|
19330
19730
|
function digestRunInput(source, content) {
|
|
19331
|
-
const digest = digestDescriptorSection(
|
|
19731
|
+
const digest = digestDescriptorSection(
|
|
19732
|
+
{ source, content },
|
|
19733
|
+
VERIFY_INPUT_DIGEST_PATH
|
|
19734
|
+
);
|
|
19332
19735
|
if (!digest.ok) return digest;
|
|
19333
19736
|
return { ok: true, value: digest.value.sha256 };
|
|
19334
19737
|
}
|
|
@@ -19348,7 +19751,10 @@ function verifyInputRecordPath(scope2) {
|
|
|
19348
19751
|
if (!token.ok) return token;
|
|
19349
19752
|
return {
|
|
19350
19753
|
ok: true,
|
|
19351
|
-
value: join36(
|
|
19754
|
+
value: join36(
|
|
19755
|
+
runs.value,
|
|
19756
|
+
`${VERIFY_INPUT_RECORD.PREFIX}${token.value}${VERIFY_INPUT_RECORD.SUFFIX}`
|
|
19757
|
+
)
|
|
19352
19758
|
};
|
|
19353
19759
|
}
|
|
19354
19760
|
var VERIFY_APPEND_ATTEMPT = 1;
|
|
@@ -19397,6 +19803,58 @@ function buildAppendEvent(args) {
|
|
|
19397
19803
|
}
|
|
19398
19804
|
};
|
|
19399
19805
|
}
|
|
19806
|
+
var VERIFY_TERMINAL_EVENT_TYPE = `${RUNTIME_EVENT_NAMESPACE_DEFAULT}.verify.terminal`;
|
|
19807
|
+
var VERIFY_TERMINAL_EVENT_FIELD = {
|
|
19808
|
+
TERMINAL_STATUS: "terminalStatus"
|
|
19809
|
+
};
|
|
19810
|
+
var VERIFY_TERMINAL_EVENT_ID_PREFIX = "verify-terminal-";
|
|
19811
|
+
function isVerifyTerminalStatus(value) {
|
|
19812
|
+
return isJournalRunStateStatus(value);
|
|
19813
|
+
}
|
|
19814
|
+
function buildTerminalEvent(args) {
|
|
19815
|
+
return {
|
|
19816
|
+
id: `${VERIFY_TERMINAL_EVENT_ID_PREFIX}${args.runToken}`,
|
|
19817
|
+
source: VERIFY_EVENT_SOURCE,
|
|
19818
|
+
type: VERIFY_TERMINAL_EVENT_TYPE,
|
|
19819
|
+
time: args.at.toISOString(),
|
|
19820
|
+
attempt: VERIFY_APPEND_ATTEMPT,
|
|
19821
|
+
data: {
|
|
19822
|
+
[VERIFY_TERMINAL_EVENT_FIELD.TERMINAL_STATUS]: args.terminalStatus
|
|
19823
|
+
}
|
|
19824
|
+
};
|
|
19825
|
+
}
|
|
19826
|
+
var VERIFY_NO_EVENTS_SEQUENCE = 0;
|
|
19827
|
+
function findTerminalEvent(events) {
|
|
19828
|
+
return events.find((event) => event.type === VERIFY_TERMINAL_EVENT_TYPE);
|
|
19829
|
+
}
|
|
19830
|
+
function terminalStatusOf(event) {
|
|
19831
|
+
if (event === void 0 || !isJsonRecord(event.data)) return void 0;
|
|
19832
|
+
const status = event.data[VERIFY_TERMINAL_EVENT_FIELD.TERMINAL_STATUS];
|
|
19833
|
+
return typeof status === "string" ? status : void 0;
|
|
19834
|
+
}
|
|
19835
|
+
function countVerifyFindings(events) {
|
|
19836
|
+
return events.filter(
|
|
19837
|
+
(event) => event.type === VERIFY_APPEND_EVENT_TYPE.FINDING
|
|
19838
|
+
).length;
|
|
19839
|
+
}
|
|
19840
|
+
function lastSequenceOf(events) {
|
|
19841
|
+
return events.reduce(
|
|
19842
|
+
(max, event) => event.seq > max ? event.seq : max,
|
|
19843
|
+
VERIFY_NO_EVENTS_SEQUENCE
|
|
19844
|
+
);
|
|
19845
|
+
}
|
|
19846
|
+
function projectVerifyRun(events) {
|
|
19847
|
+
const terminal = findTerminalEvent(events);
|
|
19848
|
+
const terminalStatus = terminalStatusOf(terminal);
|
|
19849
|
+
const sealed = terminal !== void 0;
|
|
19850
|
+
return {
|
|
19851
|
+
sealed,
|
|
19852
|
+
...terminalStatus === void 0 ? {} : { terminalStatus },
|
|
19853
|
+
findingCount: countVerifyFindings(events),
|
|
19854
|
+
lastSequence: lastSequenceOf(events),
|
|
19855
|
+
nextActions: sealed ? [] : UNSEALED_NEXT_ACTIONS
|
|
19856
|
+
};
|
|
19857
|
+
}
|
|
19400
19858
|
|
|
19401
19859
|
// src/commands/verify/cli.ts
|
|
19402
19860
|
var VERIFY_CLI_EXIT_CODE = {
|
|
@@ -19407,19 +19865,40 @@ var VERIFY_CLI_ENV = {
|
|
|
19407
19865
|
BRANCH: SPX_VERIFY_ENV.BRANCH
|
|
19408
19866
|
};
|
|
19409
19867
|
var VERIFY_CLI_ERROR = {
|
|
19410
|
-
INPUT_REQUIRED: "spx
|
|
19411
|
-
RUN_REQUIRED: "spx
|
|
19412
|
-
RUN_NOT_FOUND: "spx
|
|
19413
|
-
|
|
19414
|
-
|
|
19415
|
-
|
|
19416
|
-
|
|
19417
|
-
|
|
19418
|
-
|
|
19419
|
-
|
|
19420
|
-
|
|
19421
|
-
|
|
19422
|
-
|
|
19868
|
+
INPUT_REQUIRED: "spx verification run start requires --input <input-source>",
|
|
19869
|
+
RUN_REQUIRED: "spx verification run existing-run commands require an explicit --run <run-token>",
|
|
19870
|
+
RUN_NOT_FOUND: "spx verification run could not locate the requested run",
|
|
19871
|
+
RUN_SELECTOR_MISMATCH: "spx verification run selector does not match the recorded run",
|
|
19872
|
+
CHANGED_SCOPE_FAILED: "spx verification run could not derive the changeset changed-file scope",
|
|
19873
|
+
INPUT_PERSIST_FAILED: "spx verification run could not persist the recorded run input",
|
|
19874
|
+
INPUT_READ_FAILED: "spx verification run could not read the recorded run input",
|
|
19875
|
+
PAYLOAD_REQUIRED: "spx verification run evidence-add commands require --payload <payload-source>",
|
|
19876
|
+
IDEMPOTENCY_KEY_REQUIRED: "spx verification run evidence-add commands require --idempotency-key <key>",
|
|
19877
|
+
PAYLOAD_READ_FAILED: "spx verification run could not read the evidence payload",
|
|
19878
|
+
PAYLOAD_INVALID: "spx verification run evidence payload is not valid JSON",
|
|
19879
|
+
RUN_FINISHED: "spx verification run cannot add evidence to a finished run",
|
|
19880
|
+
FINDING_INVALID: "spx verification run finding add payload failed verification-type validation",
|
|
19881
|
+
UNSUPPORTED_VERIFICATION_TYPE: "spx verification run verification type is not registered",
|
|
19882
|
+
APPEND_FAILED: "spx verification run could not append the evidence event",
|
|
19883
|
+
TERMINAL_STATUS_REQUIRED: "spx verification run finish requires --terminal-status <status>",
|
|
19884
|
+
TERMINAL_STATUS_INVALID: "spx verification run finish requires a terminal status in the journal terminal-status vocabulary",
|
|
19885
|
+
FINISH_FAILED: "spx verification run could not record terminal completion",
|
|
19886
|
+
SEAL_FAILED: "spx verification run could not seal the run journal",
|
|
19887
|
+
STATUS_FAILED: "spx verification run could not read the run status",
|
|
19888
|
+
RENDER_FAILED: "spx verification run could not render the run projection"
|
|
19889
|
+
};
|
|
19890
|
+
var VERIFY_RUN_NOT_FOUND_DIAGNOSTIC_FIELD = {
|
|
19891
|
+
RUN: "run=",
|
|
19892
|
+
VERIFICATION_TYPE: "verification-type=",
|
|
19893
|
+
SCOPE_TYPE: "scope-type=",
|
|
19894
|
+
SCOPE: "scope=",
|
|
19895
|
+
BACKEND: "backend=",
|
|
19896
|
+
NAMESPACE: "namespace=",
|
|
19897
|
+
TARGET: "target="
|
|
19898
|
+
};
|
|
19899
|
+
var VERIFY_START_ROLLBACK_ARTIFACT = {
|
|
19900
|
+
CONTEXT_FILE: "verification context file",
|
|
19901
|
+
RUN_FILE: "journal run file"
|
|
19423
19902
|
};
|
|
19424
19903
|
function okResult3(output) {
|
|
19425
19904
|
return { exitCode: VERIFY_CLI_EXIT_CODE.OK, output };
|
|
@@ -19478,73 +19957,131 @@ async function persistInputRecord(runScope2, record6, deps) {
|
|
|
19478
19957
|
const fs8 = deps.fs ?? defaultFileSystem;
|
|
19479
19958
|
try {
|
|
19480
19959
|
await fs8.mkdir(dirname12(path7.value), { recursive: true });
|
|
19481
|
-
await
|
|
19960
|
+
await writeFileAtomic(path7.value, JSON.stringify(record6), { fs: fs8, randomBytes: randomBytes2 });
|
|
19482
19961
|
return { ok: true, value: void 0 };
|
|
19483
19962
|
} catch (error) {
|
|
19484
19963
|
return { ok: false, error: `${VERIFY_CLI_ERROR.INPUT_PERSIST_FAILED}: ${toMessage2(error)}` };
|
|
19485
19964
|
}
|
|
19486
19965
|
}
|
|
19966
|
+
async function readStartInputContent(source, deps) {
|
|
19967
|
+
try {
|
|
19968
|
+
return { ok: true, value: await deps.readInputSource(source) };
|
|
19969
|
+
} catch (error) {
|
|
19970
|
+
return { ok: false, error: `${VERIFY_CLI_ERROR.INPUT_READ_FAILED}: ${toMessage2(error)}` };
|
|
19971
|
+
}
|
|
19972
|
+
}
|
|
19973
|
+
async function removeStartedRunArtifact(path7, label, deps) {
|
|
19974
|
+
const fs8 = deps.fs ?? defaultFileSystem;
|
|
19975
|
+
try {
|
|
19976
|
+
await fs8.rm(path7, { force: true });
|
|
19977
|
+
return { ok: true, value: void 0 };
|
|
19978
|
+
} catch (error) {
|
|
19979
|
+
return {
|
|
19980
|
+
ok: false,
|
|
19981
|
+
error: `${VERIFY_CLI_ERROR.INPUT_PERSIST_FAILED}: rollback failed for ${label}: ${toMessage2(error)}`
|
|
19982
|
+
};
|
|
19983
|
+
}
|
|
19984
|
+
}
|
|
19985
|
+
async function removeStartedRunArtifacts(artifacts, deps) {
|
|
19986
|
+
const rollbackErrors = [];
|
|
19987
|
+
if (artifacts.contextPath !== void 0) {
|
|
19988
|
+
const contextRollback = await removeStartedRunArtifact(
|
|
19989
|
+
artifacts.contextPath,
|
|
19990
|
+
VERIFY_START_ROLLBACK_ARTIFACT.CONTEXT_FILE,
|
|
19991
|
+
deps
|
|
19992
|
+
);
|
|
19993
|
+
if (!contextRollback.ok) rollbackErrors.push(contextRollback.error);
|
|
19994
|
+
}
|
|
19995
|
+
if (artifacts.runFile !== void 0) {
|
|
19996
|
+
const runRollback = await removeStartedRunArtifact(
|
|
19997
|
+
artifacts.runFile,
|
|
19998
|
+
VERIFY_START_ROLLBACK_ARTIFACT.RUN_FILE,
|
|
19999
|
+
deps
|
|
20000
|
+
);
|
|
20001
|
+
if (!runRollback.ok) rollbackErrors.push(runRollback.error);
|
|
20002
|
+
}
|
|
20003
|
+
if (rollbackErrors.length > 0) return { ok: false, error: rollbackErrors.join("; ") };
|
|
20004
|
+
return { ok: true, value: void 0 };
|
|
20005
|
+
}
|
|
20006
|
+
function isStoredRecordedInput(value) {
|
|
20007
|
+
if (typeof value !== "object" || value === null) return false;
|
|
20008
|
+
const record6 = value;
|
|
20009
|
+
return typeof record6.scopeIdentity === "string" && typeof record6.scopeType === "string" && typeof record6.source === "string" && typeof record6.digest === "string" && typeof record6.content === "string";
|
|
20010
|
+
}
|
|
20011
|
+
function parseRecordedInput(content) {
|
|
20012
|
+
try {
|
|
20013
|
+
const parsed = JSON.parse(content);
|
|
20014
|
+
if (!isStoredRecordedInput(parsed)) {
|
|
20015
|
+
return { ok: false, error: `${VERIFY_CLI_ERROR.INPUT_READ_FAILED}: recorded input is missing required fields` };
|
|
20016
|
+
}
|
|
20017
|
+
return { ok: true, value: parsed };
|
|
20018
|
+
} catch (error) {
|
|
20019
|
+
return { ok: false, error: `${VERIFY_CLI_ERROR.INPUT_READ_FAILED}: ${toMessage2(error)}` };
|
|
20020
|
+
}
|
|
20021
|
+
}
|
|
19487
20022
|
async function readInputRecordAt(path7, deps) {
|
|
19488
20023
|
const fs8 = deps.fs ?? defaultFileSystem;
|
|
20024
|
+
let content;
|
|
19489
20025
|
try {
|
|
19490
|
-
|
|
19491
|
-
return { ok: true, value: JSON.parse(content) };
|
|
20026
|
+
content = await fs8.readFile(path7, STATE_STORE_TEXT_ENCODING);
|
|
19492
20027
|
} catch (error) {
|
|
19493
20028
|
if (hasErrorCode(error, ERROR_CODE_NOT_FOUND)) return { ok: true, value: void 0 };
|
|
19494
20029
|
return { ok: false, error: `${VERIFY_CLI_ERROR.INPUT_READ_FAILED}: ${toMessage2(error)}` };
|
|
19495
20030
|
}
|
|
20031
|
+
const parsed = parseRecordedInput(content);
|
|
20032
|
+
if (!parsed.ok) return parsed;
|
|
20033
|
+
return { ok: true, value: parsed.value };
|
|
19496
20034
|
}
|
|
19497
|
-
function
|
|
20035
|
+
function verifyRunLocatorDiagnostic(summary, context) {
|
|
19498
20036
|
return [
|
|
19499
|
-
|
|
19500
|
-
|
|
19501
|
-
|
|
19502
|
-
|
|
19503
|
-
|
|
19504
|
-
|
|
19505
|
-
|
|
19506
|
-
|
|
20037
|
+
summary,
|
|
20038
|
+
`${VERIFY_RUN_NOT_FOUND_DIAGNOSTIC_FIELD.RUN}${context.runToken}`,
|
|
20039
|
+
`${VERIFY_RUN_NOT_FOUND_DIAGNOSTIC_FIELD.VERIFICATION_TYPE}${context.verificationType}`,
|
|
20040
|
+
`${VERIFY_RUN_NOT_FOUND_DIAGNOSTIC_FIELD.SCOPE_TYPE}${context.scopeType}`,
|
|
20041
|
+
`${VERIFY_RUN_NOT_FOUND_DIAGNOSTIC_FIELD.SCOPE}${context.scopeIdentity}`,
|
|
20042
|
+
`${VERIFY_RUN_NOT_FOUND_DIAGNOSTIC_FIELD.BACKEND}${context.backendIdentity}`,
|
|
20043
|
+
`${VERIFY_RUN_NOT_FOUND_DIAGNOSTIC_FIELD.NAMESPACE}${context.storageNamespace}`,
|
|
20044
|
+
`${VERIFY_RUN_NOT_FOUND_DIAGNOSTIC_FIELD.TARGET}${context.searchedTarget}`
|
|
19507
20045
|
].join(" ");
|
|
19508
20046
|
}
|
|
19509
|
-
|
|
19510
|
-
|
|
19511
|
-
|
|
19512
|
-
|
|
19513
|
-
|
|
19514
|
-
const resolved = await resolveVerifyScope(deps);
|
|
19515
|
-
if (!resolved.ok) return errorResult3(resolved.error);
|
|
19516
|
-
const inputContent = await deps.readInputSource(options.input);
|
|
19517
|
-
const inputDigest = digestRunInput(options.input, inputContent);
|
|
19518
|
-
if (!inputDigest.ok) return errorResult3(inputDigest.error);
|
|
19519
|
-
const context = await verificationContextCreateCommand(
|
|
19520
|
-
{
|
|
19521
|
-
subject: VERIFICATION_CONTEXT_SUBJECT_KIND.CHANGESET,
|
|
19522
|
-
base: scope2.value.base,
|
|
19523
|
-
head: scope2.value.head,
|
|
19524
|
-
predicate: options.verificationType,
|
|
19525
|
-
workflow: options.verificationType
|
|
19526
|
-
},
|
|
19527
|
-
forwardDeps(deps)
|
|
19528
|
-
);
|
|
19529
|
-
if (context.exitCode !== VERIFY_CLI_EXIT_CODE.OK) return errorResult3(context.output);
|
|
19530
|
-
const contextDigest = JSON.parse(context.output).digest;
|
|
20047
|
+
function verifyRunNotFoundDiagnostic(context) {
|
|
20048
|
+
return verifyRunLocatorDiagnostic(VERIFY_CLI_ERROR.RUN_NOT_FOUND, context);
|
|
20049
|
+
}
|
|
20050
|
+
async function completeVerifyStartCommand(args) {
|
|
20051
|
+
const { options, deps } = args;
|
|
19531
20052
|
const opened = await journalOpenCommand(
|
|
19532
|
-
{ type: options.verificationType, branchSlug:
|
|
20053
|
+
{ type: options.verificationType, branchSlug: args.branchSlug },
|
|
19533
20054
|
forwardDeps(deps)
|
|
19534
20055
|
);
|
|
19535
|
-
if (opened.exitCode !== VERIFY_CLI_EXIT_CODE.OK)
|
|
20056
|
+
if (opened.exitCode !== VERIFY_CLI_EXIT_CODE.OK) {
|
|
20057
|
+
const rollback = await removeStartedRunArtifacts(
|
|
20058
|
+
{ ...args.contextCreated ? { contextPath: args.contextPath } : {} },
|
|
20059
|
+
deps
|
|
20060
|
+
);
|
|
20061
|
+
return errorResult3(rollback.ok ? opened.output : `${opened.output}; ${rollback.error}`);
|
|
20062
|
+
}
|
|
19536
20063
|
const { runToken, runFile } = JSON.parse(opened.output);
|
|
19537
20064
|
const runScope2 = {
|
|
19538
|
-
productDir:
|
|
19539
|
-
branchSlug:
|
|
20065
|
+
productDir: args.productDir,
|
|
20066
|
+
branchSlug: args.branchSlug,
|
|
19540
20067
|
type: options.verificationType,
|
|
19541
20068
|
runToken
|
|
19542
20069
|
};
|
|
19543
|
-
const recorded = {
|
|
20070
|
+
const recorded = {
|
|
20071
|
+
scopeIdentity: options.scope,
|
|
20072
|
+
scopeType: options.scopeType,
|
|
20073
|
+
source: options.input,
|
|
20074
|
+
digest: args.inputDigest,
|
|
20075
|
+
content: args.inputContent
|
|
20076
|
+
};
|
|
19544
20077
|
const persisted = await persistInputRecord(runScope2, recorded, deps);
|
|
19545
|
-
if (!persisted.ok)
|
|
19546
|
-
|
|
19547
|
-
|
|
20078
|
+
if (!persisted.ok) {
|
|
20079
|
+
const rollback = await removeStartedRunArtifacts(
|
|
20080
|
+
{ ...args.contextCreated ? { contextPath: args.contextPath } : {}, runFile },
|
|
20081
|
+
deps
|
|
20082
|
+
);
|
|
20083
|
+
return errorResult3(rollback.ok ? persisted.error : `${persisted.error}; ${rollback.error}`);
|
|
20084
|
+
}
|
|
19548
20085
|
const namespace = verifyRunsDir(runScope2);
|
|
19549
20086
|
if (!namespace.ok) return errorResult3(namespace.error);
|
|
19550
20087
|
const locator = buildRunLocator({
|
|
@@ -19552,52 +20089,69 @@ async function verifyStartCommand(options, deps) {
|
|
|
19552
20089
|
verificationType: options.verificationType,
|
|
19553
20090
|
scopeType: options.scopeType,
|
|
19554
20091
|
scopeIdentity: options.scope,
|
|
19555
|
-
backendIdentity:
|
|
20092
|
+
backendIdentity: args.backendIdentity,
|
|
19556
20093
|
storageNamespace: namespace.value,
|
|
19557
20094
|
runTarget: runFile
|
|
19558
20095
|
});
|
|
19559
20096
|
const report2 = {
|
|
19560
20097
|
runToken,
|
|
19561
|
-
contextDigest,
|
|
19562
|
-
changedScope: changedScope
|
|
19563
|
-
input: { source: options.input, digest: inputDigest
|
|
20098
|
+
contextDigest: args.contextDigest,
|
|
20099
|
+
changedScope: args.changedScope,
|
|
20100
|
+
input: { source: options.input, digest: args.inputDigest },
|
|
19564
20101
|
locator
|
|
19565
20102
|
};
|
|
19566
20103
|
return okResult3(JSON.stringify(report2));
|
|
19567
20104
|
}
|
|
19568
|
-
async function
|
|
19569
|
-
if (options.
|
|
20105
|
+
async function verifyStartCommand(options, deps) {
|
|
20106
|
+
if (!isVerifyVerificationType(options.verificationType)) {
|
|
20107
|
+
return errorResult3(VERIFY_CLI_ERROR.UNSUPPORTED_VERIFICATION_TYPE);
|
|
20108
|
+
}
|
|
20109
|
+
if (options.scopeType !== VERIFY_SCOPE_TYPE.CHANGESET) return errorResult3(VERIFY_SCOPE_ERROR.UNSUPPORTED_SCOPE_TYPE);
|
|
20110
|
+
if (options.input.trim().length === 0) return errorResult3(VERIFY_CLI_ERROR.INPUT_REQUIRED);
|
|
20111
|
+
const scope2 = parseChangesetScope(options.scope);
|
|
20112
|
+
if (!scope2.ok) return errorResult3(scope2.error);
|
|
19570
20113
|
const resolved = await resolveVerifyScope(deps);
|
|
19571
20114
|
if (!resolved.ok) return errorResult3(resolved.error);
|
|
19572
|
-
const
|
|
20115
|
+
const inputContent = await readStartInputContent(options.input, deps);
|
|
20116
|
+
if (!inputContent.ok) return errorResult3(inputContent.error);
|
|
20117
|
+
const inputDigest = digestRunInput(options.input, inputContent.value);
|
|
20118
|
+
if (!inputDigest.ok) return errorResult3(inputDigest.error);
|
|
20119
|
+
const changedScope = await resolveChangedScope(scope2.value, deps);
|
|
20120
|
+
if (!changedScope.ok) return errorResult3(changedScope.error);
|
|
20121
|
+
const context = await verificationContextCreateCommand(
|
|
20122
|
+
{
|
|
20123
|
+
subject: VERIFICATION_CONTEXT_SUBJECT_KIND.CHANGESET,
|
|
20124
|
+
base: scope2.value.base,
|
|
20125
|
+
head: scope2.value.head,
|
|
20126
|
+
predicate: options.verificationType,
|
|
20127
|
+
workflow: options.verificationType
|
|
20128
|
+
},
|
|
20129
|
+
forwardDeps(deps)
|
|
20130
|
+
);
|
|
20131
|
+
if (context.exitCode !== VERIFY_CLI_EXIT_CODE.OK) return errorResult3(context.output);
|
|
20132
|
+
const { digest: contextDigest, contextPath, created: contextCreated } = JSON.parse(context.output);
|
|
20133
|
+
return completeVerifyStartCommand({
|
|
20134
|
+
options,
|
|
20135
|
+
deps,
|
|
19573
20136
|
productDir: resolved.value.productDir,
|
|
19574
20137
|
branchSlug: resolved.value.branchSlug,
|
|
19575
|
-
|
|
19576
|
-
|
|
19577
|
-
|
|
19578
|
-
|
|
19579
|
-
|
|
19580
|
-
|
|
19581
|
-
|
|
19582
|
-
|
|
19583
|
-
|
|
19584
|
-
|
|
19585
|
-
|
|
19586
|
-
|
|
19587
|
-
|
|
19588
|
-
verificationType: options.verificationType,
|
|
19589
|
-
scopeType: options.scopeType,
|
|
19590
|
-
scopeIdentity: options.scope,
|
|
19591
|
-
backendIdentity: resolved.value.backendIdentity,
|
|
19592
|
-
storageNamespace: namespace.value,
|
|
19593
|
-
searchedTarget: path7.value
|
|
19594
|
-
})
|
|
19595
|
-
);
|
|
19596
|
-
}
|
|
20138
|
+
backendIdentity: resolved.value.backendIdentity,
|
|
20139
|
+
changedScope: changedScope.value,
|
|
20140
|
+
inputDigest: inputDigest.value,
|
|
20141
|
+
inputContent: inputContent.value,
|
|
20142
|
+
contextDigest,
|
|
20143
|
+
contextPath,
|
|
20144
|
+
contextCreated
|
|
20145
|
+
});
|
|
20146
|
+
}
|
|
20147
|
+
async function verifyInputCommand(options, deps) {
|
|
20148
|
+
const run = await resolveExistingRun(options, deps);
|
|
20149
|
+
if (!run.ok) return errorResult3(run.error);
|
|
20150
|
+
const record6 = run.value.recordedInput;
|
|
19597
20151
|
const report2 = {
|
|
19598
|
-
source: record6.
|
|
19599
|
-
digest: record6.
|
|
19600
|
-
content: record6.
|
|
20152
|
+
source: record6.source,
|
|
20153
|
+
digest: record6.digest,
|
|
20154
|
+
content: record6.content
|
|
19601
20155
|
};
|
|
19602
20156
|
return okResult3(JSON.stringify(report2));
|
|
19603
20157
|
}
|
|
@@ -19606,12 +20160,36 @@ async function readRunJournalEvents(scope2, deps) {
|
|
|
19606
20160
|
if (read.exitCode !== VERIFY_CLI_EXIT_CODE.OK) return { ok: false, error: read.output };
|
|
19607
20161
|
return { ok: true, value: JSON.parse(read.output) };
|
|
19608
20162
|
}
|
|
20163
|
+
async function readAppendExistingEvents(options, deps, journalScope, backendIdentity, namespace) {
|
|
20164
|
+
const existingEvents = await readRunJournalEvents(journalScope, deps);
|
|
20165
|
+
if (!existingEvents.ok) {
|
|
20166
|
+
if (existingEvents.error === JOURNAL_RUNTIME_ERROR.RUN_NOT_FOUND) {
|
|
20167
|
+
return {
|
|
20168
|
+
ok: false,
|
|
20169
|
+
error: appendRunNotFoundDiagnostic(options, backendIdentity, namespace)
|
|
20170
|
+
};
|
|
20171
|
+
}
|
|
20172
|
+
return { ok: false, error: `${VERIFY_CLI_ERROR.APPEND_FAILED}: ${existingEvents.error}` };
|
|
20173
|
+
}
|
|
20174
|
+
if (findTerminalEvent(existingEvents.value) !== void 0) {
|
|
20175
|
+
return { ok: false, error: VERIFY_CLI_ERROR.RUN_FINISHED };
|
|
20176
|
+
}
|
|
20177
|
+
return existingEvents;
|
|
20178
|
+
}
|
|
19609
20179
|
async function prepareAppend(options, deps) {
|
|
19610
20180
|
if (options.run.trim().length === 0) return { ok: false, error: VERIFY_CLI_ERROR.RUN_REQUIRED };
|
|
20181
|
+
if (!isVerifyVerificationType(options.verificationType)) {
|
|
20182
|
+
return { ok: false, error: VERIFY_CLI_ERROR.UNSUPPORTED_VERIFICATION_TYPE };
|
|
20183
|
+
}
|
|
19611
20184
|
if (options.payload.trim().length === 0) return { ok: false, error: VERIFY_CLI_ERROR.PAYLOAD_REQUIRED };
|
|
19612
20185
|
if (options.idempotencyKey.trim().length === 0) {
|
|
19613
20186
|
return { ok: false, error: VERIFY_CLI_ERROR.IDEMPOTENCY_KEY_REQUIRED };
|
|
19614
20187
|
}
|
|
20188
|
+
if (options.scopeType !== VERIFY_SCOPE_TYPE.CHANGESET) {
|
|
20189
|
+
return { ok: false, error: VERIFY_SCOPE_ERROR.UNSUPPORTED_SCOPE_TYPE };
|
|
20190
|
+
}
|
|
20191
|
+
const scope2 = parseChangesetScope(options.scope);
|
|
20192
|
+
if (!scope2.ok) return scope2;
|
|
19615
20193
|
const readPayload = deps.readPayloadSource;
|
|
19616
20194
|
const binding = deps.journalBinding;
|
|
19617
20195
|
if (readPayload === void 0 || binding === void 0) return { ok: false, error: VERIFY_CLI_ERROR.APPEND_FAILED };
|
|
@@ -19625,25 +20203,53 @@ async function prepareAppend(options, deps) {
|
|
|
19625
20203
|
};
|
|
19626
20204
|
const namespace = verifyRunsDir(runScope2);
|
|
19627
20205
|
if (!namespace.ok) return namespace;
|
|
20206
|
+
const journalScope = {
|
|
20207
|
+
type: options.verificationType,
|
|
20208
|
+
runToken: options.run,
|
|
20209
|
+
branchSlug: resolved.value.branchSlug
|
|
20210
|
+
};
|
|
20211
|
+
const existingEvents = await readAppendExistingEvents(
|
|
20212
|
+
options,
|
|
20213
|
+
deps,
|
|
20214
|
+
journalScope,
|
|
20215
|
+
resolved.value.backendIdentity,
|
|
20216
|
+
namespace.value
|
|
20217
|
+
);
|
|
20218
|
+
if (!existingEvents.ok) return existingEvents;
|
|
19628
20219
|
const inputPath = verifyInputRecordPath(runScope2);
|
|
19629
20220
|
if (!inputPath.ok) return inputPath;
|
|
19630
20221
|
const inputRecord = await readInputRecordAt(inputPath.value, deps);
|
|
19631
20222
|
if (!inputRecord.ok) return inputRecord;
|
|
19632
20223
|
if (inputRecord.value === void 0) {
|
|
19633
|
-
return {
|
|
20224
|
+
return {
|
|
20225
|
+
ok: false,
|
|
20226
|
+
error: appendRunNotFoundDiagnostic(options, resolved.value.backendIdentity, namespace.value, inputPath.value)
|
|
20227
|
+
};
|
|
20228
|
+
}
|
|
20229
|
+
if (!recordedSelectorMatches(inputRecord.value, options)) {
|
|
20230
|
+
return {
|
|
20231
|
+
ok: false,
|
|
20232
|
+
error: appendRunSelectorMismatchDiagnostic(
|
|
20233
|
+
options,
|
|
20234
|
+
resolved.value.backendIdentity,
|
|
20235
|
+
namespace.value,
|
|
20236
|
+
inputPath.value
|
|
20237
|
+
)
|
|
20238
|
+
};
|
|
19634
20239
|
}
|
|
19635
20240
|
return {
|
|
19636
20241
|
ok: true,
|
|
19637
20242
|
value: {
|
|
19638
20243
|
readPayload,
|
|
19639
20244
|
binding,
|
|
19640
|
-
journalScope
|
|
20245
|
+
journalScope,
|
|
19641
20246
|
namespace: namespace.value,
|
|
19642
|
-
backendIdentity: resolved.value.backendIdentity
|
|
20247
|
+
backendIdentity: resolved.value.backendIdentity,
|
|
20248
|
+
existingEvents: existingEvents.value
|
|
19643
20249
|
}
|
|
19644
20250
|
};
|
|
19645
20251
|
}
|
|
19646
|
-
function appendRunNotFoundDiagnostic(options, backendIdentity, namespace) {
|
|
20252
|
+
function appendRunNotFoundDiagnostic(options, backendIdentity, namespace, searchedTarget = namespace) {
|
|
19647
20253
|
return verifyRunNotFoundDiagnostic({
|
|
19648
20254
|
runToken: options.run,
|
|
19649
20255
|
verificationType: options.verificationType,
|
|
@@ -19651,7 +20257,18 @@ function appendRunNotFoundDiagnostic(options, backendIdentity, namespace) {
|
|
|
19651
20257
|
scopeIdentity: options.scope,
|
|
19652
20258
|
backendIdentity,
|
|
19653
20259
|
storageNamespace: namespace,
|
|
19654
|
-
searchedTarget
|
|
20260
|
+
searchedTarget
|
|
20261
|
+
});
|
|
20262
|
+
}
|
|
20263
|
+
function appendRunSelectorMismatchDiagnostic(options, backendIdentity, namespace, searchedTarget) {
|
|
20264
|
+
return verifyRunLocatorDiagnostic(VERIFY_CLI_ERROR.RUN_SELECTOR_MISMATCH, {
|
|
20265
|
+
runToken: options.run,
|
|
20266
|
+
verificationType: options.verificationType,
|
|
20267
|
+
scopeType: options.scopeType,
|
|
20268
|
+
scopeIdentity: options.scope,
|
|
20269
|
+
backendIdentity,
|
|
20270
|
+
storageNamespace: namespace,
|
|
20271
|
+
searchedTarget
|
|
19655
20272
|
});
|
|
19656
20273
|
}
|
|
19657
20274
|
function validateAppendFinding(verb, verificationType, payload) {
|
|
@@ -19667,16 +20284,9 @@ function appendEventType(verb) {
|
|
|
19667
20284
|
async function verifyAppend(options, deps, verb) {
|
|
19668
20285
|
const prepared = await prepareAppend(options, deps);
|
|
19669
20286
|
if (!prepared.ok) return errorResult3(prepared.error);
|
|
19670
|
-
const { readPayload, binding, journalScope,
|
|
20287
|
+
const { readPayload, binding, journalScope, existingEvents } = prepared.value;
|
|
19671
20288
|
const eventType = appendEventType(verb);
|
|
19672
|
-
const
|
|
19673
|
-
if (!before.ok) {
|
|
19674
|
-
if (before.error === JOURNAL_RUNTIME_ERROR.RUN_NOT_FOUND) {
|
|
19675
|
-
return errorResult3(appendRunNotFoundDiagnostic(options, backendIdentity, namespace));
|
|
19676
|
-
}
|
|
19677
|
-
return errorResult3(`${VERIFY_CLI_ERROR.APPEND_FAILED}: ${before.error}`);
|
|
19678
|
-
}
|
|
19679
|
-
const existing = findAppendedSequence(before.value, options.idempotencyKey, eventType);
|
|
20289
|
+
const existing = findAppendedSequence(existingEvents, options.idempotencyKey, eventType);
|
|
19680
20290
|
if (existing !== void 0) {
|
|
19681
20291
|
const report3 = { sequence: existing, idempotent: true };
|
|
19682
20292
|
return okResult3(JSON.stringify(report3));
|
|
@@ -19714,24 +20324,270 @@ async function verifyAppendScopeCommand(options, deps) {
|
|
|
19714
20324
|
async function verifyAppendFindingCommand(options, deps) {
|
|
19715
20325
|
return verifyAppend(options, deps, VERIFY_VERB.APPEND_FINDING);
|
|
19716
20326
|
}
|
|
20327
|
+
function existingRunNotFound(run, options) {
|
|
20328
|
+
return verifyRunNotFoundDiagnostic({
|
|
20329
|
+
runToken: run.runToken,
|
|
20330
|
+
verificationType: options.verificationType,
|
|
20331
|
+
scopeType: options.scopeType,
|
|
20332
|
+
scopeIdentity: options.scope,
|
|
20333
|
+
backendIdentity: run.backendIdentity,
|
|
20334
|
+
storageNamespace: run.namespace,
|
|
20335
|
+
searchedTarget: run.inputRecordPath
|
|
20336
|
+
});
|
|
20337
|
+
}
|
|
20338
|
+
function existingRunSelectorMismatch(run, options) {
|
|
20339
|
+
return verifyRunLocatorDiagnostic(VERIFY_CLI_ERROR.RUN_SELECTOR_MISMATCH, {
|
|
20340
|
+
runToken: run.runToken,
|
|
20341
|
+
verificationType: options.verificationType,
|
|
20342
|
+
scopeType: options.scopeType,
|
|
20343
|
+
scopeIdentity: options.scope,
|
|
20344
|
+
backendIdentity: run.backendIdentity,
|
|
20345
|
+
storageNamespace: run.namespace,
|
|
20346
|
+
searchedTarget: run.inputRecordPath
|
|
20347
|
+
});
|
|
20348
|
+
}
|
|
20349
|
+
function recordedSelectorMatches(record6, options) {
|
|
20350
|
+
return record6.scopeType === options.scopeType && record6.scopeIdentity === options.scope;
|
|
20351
|
+
}
|
|
20352
|
+
async function readExistingRecordedInput(run, deps) {
|
|
20353
|
+
return readInputRecordAt(run.inputRecordPath, deps);
|
|
20354
|
+
}
|
|
20355
|
+
async function resolveExistingRunAddress(options, deps) {
|
|
20356
|
+
if (!isVerifyVerificationType(options.verificationType)) {
|
|
20357
|
+
return { ok: false, error: VERIFY_CLI_ERROR.UNSUPPORTED_VERIFICATION_TYPE };
|
|
20358
|
+
}
|
|
20359
|
+
if (options.scopeType !== VERIFY_SCOPE_TYPE.CHANGESET) {
|
|
20360
|
+
return { ok: false, error: VERIFY_SCOPE_ERROR.UNSUPPORTED_SCOPE_TYPE };
|
|
20361
|
+
}
|
|
20362
|
+
const scope2 = parseChangesetScope(options.scope);
|
|
20363
|
+
if (!scope2.ok) return scope2;
|
|
20364
|
+
if (options.run.trim().length === 0) return { ok: false, error: VERIFY_CLI_ERROR.RUN_REQUIRED };
|
|
20365
|
+
const resolved = await resolveVerifyScope(deps);
|
|
20366
|
+
if (!resolved.ok) return resolved;
|
|
20367
|
+
const runScope2 = {
|
|
20368
|
+
productDir: resolved.value.productDir,
|
|
20369
|
+
branchSlug: resolved.value.branchSlug,
|
|
20370
|
+
type: options.verificationType,
|
|
20371
|
+
runToken: options.run
|
|
20372
|
+
};
|
|
20373
|
+
const namespace = verifyRunsDir(runScope2);
|
|
20374
|
+
if (!namespace.ok) return namespace;
|
|
20375
|
+
const inputPath = verifyInputRecordPath(runScope2);
|
|
20376
|
+
if (!inputPath.ok) return inputPath;
|
|
20377
|
+
return {
|
|
20378
|
+
ok: true,
|
|
20379
|
+
value: {
|
|
20380
|
+
productDir: resolved.value.productDir,
|
|
20381
|
+
runToken: options.run,
|
|
20382
|
+
journalScope: { type: options.verificationType, runToken: options.run, branchSlug: resolved.value.branchSlug },
|
|
20383
|
+
namespace: namespace.value,
|
|
20384
|
+
backendIdentity: resolved.value.backendIdentity,
|
|
20385
|
+
inputRecordPath: inputPath.value
|
|
20386
|
+
}
|
|
20387
|
+
};
|
|
20388
|
+
}
|
|
20389
|
+
async function resolveExistingRun(options, deps) {
|
|
20390
|
+
const address = await resolveExistingRunAddress(options, deps);
|
|
20391
|
+
if (!address.ok) return address;
|
|
20392
|
+
const inputRecord = await readExistingRecordedInput(address.value, deps);
|
|
20393
|
+
if (!inputRecord.ok) return inputRecord;
|
|
20394
|
+
if (inputRecord.value === void 0) {
|
|
20395
|
+
return { ok: false, error: existingRunNotFound(address.value, options) };
|
|
20396
|
+
}
|
|
20397
|
+
if (!recordedSelectorMatches(inputRecord.value, options)) {
|
|
20398
|
+
return { ok: false, error: existingRunSelectorMismatch(address.value, options) };
|
|
20399
|
+
}
|
|
20400
|
+
const run = { ...address.value, recordedInput: inputRecord.value };
|
|
20401
|
+
return { ok: true, value: run };
|
|
20402
|
+
}
|
|
20403
|
+
function isRecordedInputReadFailure(error) {
|
|
20404
|
+
return error.startsWith(VERIFY_CLI_ERROR.INPUT_READ_FAILED);
|
|
20405
|
+
}
|
|
20406
|
+
async function readRecordedInputForProjection(run, options, events, deps) {
|
|
20407
|
+
const terminal = findTerminalEvent(events);
|
|
20408
|
+
const inputRecord = await readExistingRecordedInput(run, deps);
|
|
20409
|
+
if (!inputRecord.ok) {
|
|
20410
|
+
if (terminal !== void 0 && isRecordedInputReadFailure(inputRecord.error)) {
|
|
20411
|
+
return { ok: true, value: void 0 };
|
|
20412
|
+
}
|
|
20413
|
+
return inputRecord;
|
|
20414
|
+
}
|
|
20415
|
+
if (inputRecord.value === void 0) return { ok: true, value: void 0 };
|
|
20416
|
+
if (!recordedSelectorMatches(inputRecord.value, options)) {
|
|
20417
|
+
return { ok: false, error: existingRunSelectorMismatch(run, options) };
|
|
20418
|
+
}
|
|
20419
|
+
return { ok: true, value: inputRecord.value };
|
|
20420
|
+
}
|
|
20421
|
+
function verifyFinishReport(runToken, projection) {
|
|
20422
|
+
return {
|
|
20423
|
+
runToken,
|
|
20424
|
+
...projection.terminalStatus === void 0 ? {} : { terminalStatus: projection.terminalStatus },
|
|
20425
|
+
sealed: projection.sealed,
|
|
20426
|
+
findingCount: projection.findingCount,
|
|
20427
|
+
lastSequence: projection.lastSequence
|
|
20428
|
+
};
|
|
20429
|
+
}
|
|
20430
|
+
async function isJournalPhysicallySealed(run, deps) {
|
|
20431
|
+
return isJournalRunSealed(
|
|
20432
|
+
{ ...run.journalScope, productDir: run.productDir },
|
|
20433
|
+
{ ...deps.fs === void 0 ? {} : { fs: deps.fs } }
|
|
20434
|
+
);
|
|
20435
|
+
}
|
|
20436
|
+
async function sealExistingRun(run, deps) {
|
|
20437
|
+
const sealed = await journalSealCommand(run.journalScope, forwardDeps(deps));
|
|
20438
|
+
if (sealed.exitCode !== VERIFY_CLI_EXIT_CODE.OK) {
|
|
20439
|
+
return errorResult3(`${VERIFY_CLI_ERROR.SEAL_FAILED}: ${sealed.output}`);
|
|
20440
|
+
}
|
|
20441
|
+
return void 0;
|
|
20442
|
+
}
|
|
20443
|
+
function finishReadFailure(run, options, error) {
|
|
20444
|
+
if (error === JOURNAL_RUNTIME_ERROR.RUN_NOT_FOUND) {
|
|
20445
|
+
return errorResult3(existingRunNotFound(run, options));
|
|
20446
|
+
}
|
|
20447
|
+
return errorResult3(`${VERIFY_CLI_ERROR.FINISH_FAILED}: ${error}`);
|
|
20448
|
+
}
|
|
20449
|
+
async function finishProjectionResult(run, deps) {
|
|
20450
|
+
const events = await readRunJournalEvents(run.journalScope, deps);
|
|
20451
|
+
if (!events.ok) return errorResult3(`${VERIFY_CLI_ERROR.FINISH_FAILED}: ${events.error}`);
|
|
20452
|
+
return okResult3(JSON.stringify(verifyFinishReport(run.runToken, projectVerifyRun(events.value))));
|
|
20453
|
+
}
|
|
20454
|
+
async function retrySealForTerminalRun(run, deps) {
|
|
20455
|
+
const physicallySealed = await isJournalPhysicallySealed(run, deps);
|
|
20456
|
+
if (!physicallySealed.ok) return void 0;
|
|
20457
|
+
if (physicallySealed.value) return void 0;
|
|
20458
|
+
return sealExistingRun(run, deps);
|
|
20459
|
+
}
|
|
20460
|
+
async function verifyFinishCommand(options, deps) {
|
|
20461
|
+
if (options.terminalStatus.trim().length === 0) return errorResult3(VERIFY_CLI_ERROR.TERMINAL_STATUS_REQUIRED);
|
|
20462
|
+
if (!isVerifyTerminalStatus(options.terminalStatus)) return errorResult3(VERIFY_CLI_ERROR.TERMINAL_STATUS_INVALID);
|
|
20463
|
+
const run = await resolveExistingRunAddress(options, deps);
|
|
20464
|
+
if (!run.ok) return errorResult3(run.error);
|
|
20465
|
+
const before = await readRunJournalEvents(run.value.journalScope, deps);
|
|
20466
|
+
if (!before.ok) {
|
|
20467
|
+
return finishReadFailure(run.value, options, before.error);
|
|
20468
|
+
}
|
|
20469
|
+
const inputRecord = await readRecordedInputForProjection(run.value, options, before.value, deps);
|
|
20470
|
+
if (!inputRecord.ok) return errorResult3(inputRecord.error);
|
|
20471
|
+
if (findTerminalEvent(before.value) !== void 0) {
|
|
20472
|
+
const retrySeal = await retrySealForTerminalRun(run.value, deps);
|
|
20473
|
+
return retrySeal ?? finishProjectionResult(run.value, deps);
|
|
20474
|
+
}
|
|
20475
|
+
if (inputRecord.value === void 0) {
|
|
20476
|
+
return errorResult3(existingRunNotFound(run.value, options));
|
|
20477
|
+
}
|
|
20478
|
+
const binding = deps.journalBinding;
|
|
20479
|
+
if (binding === void 0) return errorResult3(VERIFY_CLI_ERROR.FINISH_FAILED);
|
|
20480
|
+
const event = buildTerminalEvent({
|
|
20481
|
+
runToken: run.value.runToken,
|
|
20482
|
+
terminalStatus: options.terminalStatus,
|
|
20483
|
+
at: deps.now?.() ?? /* @__PURE__ */ new Date()
|
|
20484
|
+
});
|
|
20485
|
+
const appended = await journalAppendCommand(run.value.journalScope, event, binding, forwardDeps(deps));
|
|
20486
|
+
if (appended.exitCode !== VERIFY_CLI_EXIT_CODE.OK) {
|
|
20487
|
+
return errorResult3(`${VERIFY_CLI_ERROR.FINISH_FAILED}: ${appended.output}`);
|
|
20488
|
+
}
|
|
20489
|
+
const sealResult = await sealExistingRun(run.value, deps);
|
|
20490
|
+
if (sealResult !== void 0) return sealResult;
|
|
20491
|
+
return finishProjectionResult(run.value, deps);
|
|
20492
|
+
}
|
|
20493
|
+
async function verifyStatusCommand(options, deps) {
|
|
20494
|
+
const run = await resolveExistingRunAddress(options, deps);
|
|
20495
|
+
if (!run.ok) return errorResult3(run.error);
|
|
20496
|
+
const events = await readRunJournalEvents(run.value.journalScope, deps);
|
|
20497
|
+
if (!events.ok) {
|
|
20498
|
+
if (events.error === JOURNAL_RUNTIME_ERROR.RUN_NOT_FOUND) {
|
|
20499
|
+
return errorResult3(existingRunNotFound(run.value, options));
|
|
20500
|
+
}
|
|
20501
|
+
return errorResult3(`${VERIFY_CLI_ERROR.STATUS_FAILED}: ${events.error}`);
|
|
20502
|
+
}
|
|
20503
|
+
const inputRecord = await readRecordedInputForProjection(run.value, options, events.value, deps);
|
|
20504
|
+
if (!inputRecord.ok) return errorResult3(inputRecord.error);
|
|
20505
|
+
if (inputRecord.value === void 0 && findTerminalEvent(events.value) === void 0) {
|
|
20506
|
+
return errorResult3(existingRunNotFound(run.value, options));
|
|
20507
|
+
}
|
|
20508
|
+
const projection = projectVerifyRun(events.value);
|
|
20509
|
+
const report2 = {
|
|
20510
|
+
runToken: run.value.runToken,
|
|
20511
|
+
verificationType: options.verificationType,
|
|
20512
|
+
scopeType: options.scopeType,
|
|
20513
|
+
sealed: projection.sealed,
|
|
20514
|
+
lastSequence: projection.lastSequence,
|
|
20515
|
+
...projection.terminalStatus === void 0 ? {} : { terminalStatus: projection.terminalStatus },
|
|
20516
|
+
findingCount: projection.findingCount,
|
|
20517
|
+
nextActions: projection.nextActions
|
|
20518
|
+
};
|
|
20519
|
+
return okResult3(JSON.stringify(report2));
|
|
20520
|
+
}
|
|
20521
|
+
async function verifyRenderCommand(options, deps) {
|
|
20522
|
+
const run = await resolveExistingRunAddress(options, deps);
|
|
20523
|
+
if (!run.ok) return errorResult3(run.error);
|
|
20524
|
+
const events = await readRunJournalEvents(run.value.journalScope, deps);
|
|
20525
|
+
if (!events.ok) {
|
|
20526
|
+
if (events.error === JOURNAL_RUNTIME_ERROR.RUN_NOT_FOUND) {
|
|
20527
|
+
return errorResult3(existingRunNotFound(run.value, options));
|
|
20528
|
+
}
|
|
20529
|
+
return errorResult3(`${VERIFY_CLI_ERROR.RENDER_FAILED}: ${events.error}`);
|
|
20530
|
+
}
|
|
20531
|
+
const inputRecord = await readRecordedInputForProjection(run.value, options, events.value, deps);
|
|
20532
|
+
if (!inputRecord.ok) return errorResult3(inputRecord.error);
|
|
20533
|
+
if (inputRecord.value === void 0 && findTerminalEvent(events.value) === void 0) {
|
|
20534
|
+
return errorResult3(existingRunNotFound(run.value, options));
|
|
20535
|
+
}
|
|
20536
|
+
const projection = projectVerifyRun(events.value);
|
|
20537
|
+
const report2 = {
|
|
20538
|
+
runToken: run.value.runToken,
|
|
20539
|
+
findingCount: projection.findingCount,
|
|
20540
|
+
sealed: projection.sealed,
|
|
20541
|
+
...projection.terminalStatus === void 0 ? {} : { terminalStatus: projection.terminalStatus },
|
|
20542
|
+
events: events.value
|
|
20543
|
+
};
|
|
20544
|
+
return okResult3(JSON.stringify(report2));
|
|
20545
|
+
}
|
|
19717
20546
|
|
|
19718
20547
|
// src/interfaces/cli/verify.ts
|
|
20548
|
+
var VERIFICATION_RUN_CLI_SURFACE = {
|
|
20549
|
+
addCommandName: "add",
|
|
20550
|
+
findingResourceCommandName: "finding",
|
|
20551
|
+
forbiddenRootCommandName: "verify",
|
|
20552
|
+
forbiddenRunHelpTerms: ["Append", "append"],
|
|
20553
|
+
forbiddenRunCommandNames: ["journal", "event", "append-scope", "append-finding"],
|
|
20554
|
+
rootCommandName: "verification",
|
|
20555
|
+
runCommandName: "run",
|
|
20556
|
+
scopeResourceCommandName: "scope"
|
|
20557
|
+
};
|
|
19719
20558
|
var VERIFY_CLI = {
|
|
19720
|
-
|
|
20559
|
+
addCommandName: VERIFICATION_RUN_CLI_SURFACE.addCommandName,
|
|
20560
|
+
commandName: VERIFICATION_RUN_CLI_SURFACE.rootCommandName,
|
|
19721
20561
|
description: "Record and replay a typed verification run",
|
|
19722
20562
|
startCommandName: VERIFY_VERB.START,
|
|
19723
20563
|
inputCommandName: VERIFY_VERB.INPUT,
|
|
19724
|
-
|
|
19725
|
-
|
|
20564
|
+
findingCommandName: VERIFICATION_RUN_CLI_SURFACE.findingResourceCommandName,
|
|
20565
|
+
runCommandName: VERIFICATION_RUN_CLI_SURFACE.runCommandName,
|
|
20566
|
+
scopeCommandName: VERIFICATION_RUN_CLI_SURFACE.scopeResourceCommandName,
|
|
20567
|
+
finishCommandName: VERIFY_VERB.FINISH,
|
|
20568
|
+
statusCommandName: VERIFY_VERB.STATUS,
|
|
20569
|
+
renderCommandName: VERIFY_VERB.RENDER,
|
|
19726
20570
|
verificationTypeOption: "--verification-type <type>",
|
|
19727
20571
|
scopeTypeOption: "--scope-type <scope-type>",
|
|
19728
20572
|
scopeOption: "--scope <base>..<head>",
|
|
19729
20573
|
inputOption: "--input <input-source>",
|
|
19730
20574
|
runOption: "--run <token>",
|
|
19731
20575
|
payloadOption: "--payload <payload-source>",
|
|
19732
|
-
|
|
20576
|
+
payloadOptionDescription: "Evidence payload source; stdin or a file path",
|
|
20577
|
+
idempotencyKeyOption: "--idempotency-key <key>",
|
|
20578
|
+
idempotencyKeyOptionDescription: "Caller-supplied idempotency key for the evidence add",
|
|
20579
|
+
terminalStatusOption: "--terminal-status <status>"
|
|
19733
20580
|
};
|
|
19734
20581
|
var CLI_SOURCE_ENCODING = "utf8";
|
|
20582
|
+
var DEFAULT_VERIFY_CLI_HANDLERS = {
|
|
20583
|
+
appendFinding: verifyAppendFindingCommand,
|
|
20584
|
+
appendScope: verifyAppendScopeCommand,
|
|
20585
|
+
finish: verifyFinishCommand,
|
|
20586
|
+
input: verifyInputCommand,
|
|
20587
|
+
render: verifyRenderCommand,
|
|
20588
|
+
start: verifyStartCommand,
|
|
20589
|
+
status: verifyStatusCommand
|
|
20590
|
+
};
|
|
19735
20591
|
async function readStdinText() {
|
|
19736
20592
|
const chunks = [];
|
|
19737
20593
|
for await (const chunk of process.stdin) {
|
|
@@ -19741,35 +20597,48 @@ async function readStdinText() {
|
|
|
19741
20597
|
}
|
|
19742
20598
|
async function readCliSource(source) {
|
|
19743
20599
|
if (source === VERIFY_INPUT_SOURCE.STDIN) return readStdinText();
|
|
19744
|
-
return
|
|
20600
|
+
return readFile13(source, CLI_SOURCE_ENCODING);
|
|
19745
20601
|
}
|
|
19746
20602
|
var verifyDomain = {
|
|
19747
20603
|
name: VERIFY_CLI.commandName,
|
|
19748
20604
|
description: VERIFY_CLI.description,
|
|
19749
|
-
register: (program, invocation) =>
|
|
19750
|
-
|
|
19751
|
-
|
|
19752
|
-
|
|
19753
|
-
|
|
19754
|
-
|
|
19755
|
-
|
|
19756
|
-
|
|
19757
|
-
|
|
19758
|
-
|
|
19759
|
-
|
|
19760
|
-
|
|
19761
|
-
|
|
19762
|
-
|
|
19763
|
-
|
|
19764
|
-
|
|
19765
|
-
|
|
19766
|
-
|
|
19767
|
-
|
|
19768
|
-
|
|
19769
|
-
|
|
19770
|
-
|
|
19771
|
-
}
|
|
19772
|
-
|
|
20605
|
+
register: (program, invocation) => registerVerifyCommands(program, invocation)
|
|
20606
|
+
};
|
|
20607
|
+
function registerVerifyCommands(program, invocation, handlers = DEFAULT_VERIFY_CLI_HANDLERS) {
|
|
20608
|
+
const deps = () => ({
|
|
20609
|
+
cwd: invocation.resolveEffectiveInvocationDir(),
|
|
20610
|
+
readInputSource: readCliSource,
|
|
20611
|
+
readPayloadSource: readCliSource,
|
|
20612
|
+
// The append verbs write a single structured JSON result to stdout, so the run's event
|
|
20613
|
+
// stream goes to stderr under the local backend rather than sharing the result channel.
|
|
20614
|
+
journalBinding: createJournalStreamBinding(invocation.io, stderrStreamSink(invocation.io))
|
|
20615
|
+
});
|
|
20616
|
+
const command = program.command(VERIFY_CLI.commandName).description(VERIFY_CLI.description);
|
|
20617
|
+
const runCommand = command.command(VERIFY_CLI.runCommandName).description("Manage a typed verification run lifecycle");
|
|
20618
|
+
runCommand.command(VERIFY_CLI.startCommandName).description("Start a changeset-scoped verification run and report its run locator").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.inputOption, "Verification input source; stdin or a file path").action(async (options) => {
|
|
20619
|
+
reportCliResult(await handlers.start(options, deps()), invocation.io);
|
|
20620
|
+
});
|
|
20621
|
+
runCommand.command(VERIFY_CLI.inputCommandName).description("Replay the verification input recorded at start").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").action(async (options) => {
|
|
20622
|
+
reportCliResult(await handlers.input(options, deps()), invocation.io);
|
|
20623
|
+
});
|
|
20624
|
+
const scopeCommand = runCommand.command(VERIFY_CLI.scopeCommandName).description("Manage inspected scope evidence for a started verification run");
|
|
20625
|
+
scopeCommand.command(VERIFY_CLI.addCommandName).description("Record the inspected scope for a started run").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").requiredOption(VERIFY_CLI.payloadOption, VERIFY_CLI.payloadOptionDescription).requiredOption(VERIFY_CLI.idempotencyKeyOption, VERIFY_CLI.idempotencyKeyOptionDescription).action(async (options) => {
|
|
20626
|
+
reportCliResult(await handlers.appendScope(options, deps()), invocation.io);
|
|
20627
|
+
});
|
|
20628
|
+
const findingCommand = runCommand.command(VERIFY_CLI.findingCommandName).description("Manage finding evidence for a started verification run");
|
|
20629
|
+
findingCommand.command(VERIFY_CLI.addCommandName).description("Record a validated verification finding for a started run").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").requiredOption(VERIFY_CLI.payloadOption, VERIFY_CLI.payloadOptionDescription).requiredOption(VERIFY_CLI.idempotencyKeyOption, VERIFY_CLI.idempotencyKeyOptionDescription).action(async (options) => {
|
|
20630
|
+
reportCliResult(await handlers.appendFinding(options, deps()), invocation.io);
|
|
20631
|
+
});
|
|
20632
|
+
runCommand.command(VERIFY_CLI.finishCommandName).description("Record terminal completion, seal the run journal, and report its terminal projection").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").requiredOption(VERIFY_CLI.terminalStatusOption, "Terminal status recorded before sealing").action(async (options) => {
|
|
20633
|
+
reportCliResult(await handlers.finish(options, deps()), invocation.io);
|
|
20634
|
+
});
|
|
20635
|
+
runCommand.command(VERIFY_CLI.statusCommandName).description("Report the run's resumable status projected from its journal history").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").action(async (options) => {
|
|
20636
|
+
reportCliResult(await handlers.status(options, deps()), invocation.io);
|
|
20637
|
+
});
|
|
20638
|
+
runCommand.command(VERIFY_CLI.renderCommandName).description("Render the run's journal projection with its authoritative finding count").requiredOption(VERIFY_CLI.verificationTypeOption, "Verification type recorded for the run").requiredOption(VERIFY_CLI.scopeTypeOption, "Scope type; changeset").requiredOption(VERIFY_CLI.scopeOption, "Changeset scope as <base>..<head>").requiredOption(VERIFY_CLI.runOption, "Run token reported by start").action(async (options) => {
|
|
20639
|
+
reportCliResult(await handlers.render(options, deps()), invocation.io);
|
|
20640
|
+
});
|
|
20641
|
+
}
|
|
19773
20642
|
|
|
19774
20643
|
// src/interfaces/cli/worktree.ts
|
|
19775
20644
|
import { randomBytes as nodeRandomBytes4 } from "crypto";
|
|
@@ -19927,11 +20796,11 @@ function renderTextStatusChild(record6) {
|
|
|
19927
20796
|
}
|
|
19928
20797
|
|
|
19929
20798
|
// src/lib/worktree-path-info.ts
|
|
19930
|
-
import { stat as
|
|
20799
|
+
import { stat as stat8 } from "fs/promises";
|
|
19931
20800
|
var defaultWorktreePathInfo = {
|
|
19932
20801
|
isExistingNonDirectory: async (path7) => {
|
|
19933
20802
|
try {
|
|
19934
|
-
const pathStats = await
|
|
20803
|
+
const pathStats = await stat8(path7);
|
|
19935
20804
|
return !pathStats.isDirectory();
|
|
19936
20805
|
} catch {
|
|
19937
20806
|
return false;
|