sentinelayer-cli 0.30.1 → 0.30.2
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/package.json +1 -1
- package/src/commands/session.js +57 -31
- package/src/session/file-locks.js +12 -0
package/package.json
CHANGED
package/src/commands/session.js
CHANGED
|
@@ -42,7 +42,9 @@ import {
|
|
|
42
42
|
import { listRuntimeRuns } from "../session/runtime-bridge.js";
|
|
43
43
|
import {
|
|
44
44
|
listFileLocks,
|
|
45
|
+
lockFile,
|
|
45
46
|
releaseFileLocksForAgent,
|
|
47
|
+
unlockFile,
|
|
46
48
|
} from "../session/file-locks.js";
|
|
47
49
|
import {
|
|
48
50
|
injectSessionGuides,
|
|
@@ -2014,30 +2016,6 @@ export function buildSessionLockDirective(verb, file, intent = "") {
|
|
|
2014
2016
|
return normalizedIntent ? `lock: ${normalizedFile} - ${normalizedIntent}` : `lock: ${normalizedFile}`;
|
|
2015
2017
|
}
|
|
2016
2018
|
|
|
2017
|
-
// Posts a coordination directive (e.g. "lock: <file> - <intent>") as a session
|
|
2018
|
-
// message so the session daemon processes it into the authoritative file-lock
|
|
2019
|
-
// registry. Used by `session lock`/`unlock` as ergonomic sugar over the
|
|
2020
|
-
// say-convention; locks are advisory + daemon-enforced with TTL auto-release,
|
|
2021
|
-
// so this is a best-effort post.
|
|
2022
|
-
async function postSessionDirectiveMessage(sessionId, message, {
|
|
2023
|
-
agentId,
|
|
2024
|
-
targetPath = process.cwd(),
|
|
2025
|
-
} = {}) {
|
|
2026
|
-
const clientMessageId = `cli-${randomUUID()}`;
|
|
2027
|
-
const agent = await resolveSessionAgentEnvelope(sessionId, agentId, { targetPath });
|
|
2028
|
-
await ensureSessionSayAgentRegistered(sessionId, agent, { targetPath });
|
|
2029
|
-
const event = createAgentEvent({
|
|
2030
|
-
event: "session_message",
|
|
2031
|
-
agent,
|
|
2032
|
-
sessionId,
|
|
2033
|
-
payload: { message, channel: "session", clientMessageId },
|
|
2034
|
-
});
|
|
2035
|
-
event.eventId = clientMessageId;
|
|
2036
|
-
event.idempotencyToken = clientMessageId;
|
|
2037
|
-
const result = await syncSessionEventToApi(sessionId, event, { targetPath });
|
|
2038
|
-
return { event, result };
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2041
2019
|
async function runWithConcurrency(items = [], concurrency = 1, worker = async () => null) {
|
|
2042
2020
|
const normalizedItems = Array.isArray(items) ? items : [];
|
|
2043
2021
|
const normalizedConcurrency = Math.max(
|
|
@@ -3672,7 +3650,7 @@ export function registerSessionCommand(program) {
|
|
|
3672
3650
|
return payload;
|
|
3673
3651
|
});
|
|
3674
3652
|
|
|
3675
|
-
const
|
|
3653
|
+
const runFileLockCommand = async (verb, sessionId, files, options, command) => {
|
|
3676
3654
|
const normalizedSessionId = normalizeString(sessionId);
|
|
3677
3655
|
if (!normalizedSessionId) {
|
|
3678
3656
|
throw new Error("session id is required.");
|
|
@@ -3698,20 +3676,68 @@ export function registerSessionCommand(program) {
|
|
|
3698
3676
|
);
|
|
3699
3677
|
}
|
|
3700
3678
|
const intent = normalizeString(options.intent);
|
|
3679
|
+
const results = [];
|
|
3701
3680
|
const processed = [];
|
|
3681
|
+
const skipped = [];
|
|
3682
|
+
const failed = [];
|
|
3702
3683
|
for (const file of fileList) {
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3684
|
+
if (verb === "lock") {
|
|
3685
|
+
const result = await lockFile(normalizedSessionId, identity.agentId, file, {
|
|
3686
|
+
intent,
|
|
3687
|
+
targetPath,
|
|
3688
|
+
awaitRemoteSync: true,
|
|
3689
|
+
});
|
|
3690
|
+
const record = {
|
|
3691
|
+
file: result.file || file,
|
|
3692
|
+
locked: Boolean(result.locked),
|
|
3693
|
+
reason: result.reason || (result.locked ? "locked" : "held_by_other_agent"),
|
|
3694
|
+
heldBy: result.heldBy || null,
|
|
3695
|
+
since: result.since || null,
|
|
3696
|
+
};
|
|
3697
|
+
results.push(record);
|
|
3698
|
+
if (result.locked) {
|
|
3699
|
+
processed.push(record.file);
|
|
3700
|
+
} else {
|
|
3701
|
+
failed.push(record);
|
|
3702
|
+
}
|
|
3703
|
+
continue;
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
const result = await unlockFile(normalizedSessionId, identity.agentId, file, {
|
|
3707
|
+
reason: intent || "manual_release",
|
|
3706
3708
|
targetPath,
|
|
3709
|
+
awaitRemoteSync: true,
|
|
3707
3710
|
});
|
|
3708
|
-
|
|
3711
|
+
const record = {
|
|
3712
|
+
file: result.file || file,
|
|
3713
|
+
unlocked: Boolean(result.unlocked),
|
|
3714
|
+
reason: result.reason || (result.unlocked ? "unlocked" : "not_locked"),
|
|
3715
|
+
heldBy: result.heldBy || null,
|
|
3716
|
+
since: result.since || null,
|
|
3717
|
+
};
|
|
3718
|
+
results.push(record);
|
|
3719
|
+
if (result.unlocked) {
|
|
3720
|
+
processed.push(record.file);
|
|
3721
|
+
} else if (record.reason === "not_locked") {
|
|
3722
|
+
skipped.push(record);
|
|
3723
|
+
} else {
|
|
3724
|
+
failed.push(record);
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
if (failed.length > 0) {
|
|
3729
|
+
const summary = failed
|
|
3730
|
+
.map((item) => `${item.file}${item.heldBy ? ` held by ${item.heldBy}` : ""}`)
|
|
3731
|
+
.join(", ");
|
|
3732
|
+
throw new Error(`session ${verb} failed for ${summary}`);
|
|
3709
3733
|
}
|
|
3710
3734
|
const payload = {
|
|
3711
3735
|
command: `session ${verb}`,
|
|
3712
3736
|
sessionId: normalizedSessionId,
|
|
3713
3737
|
agentId: identity.agentId,
|
|
3714
3738
|
files: processed,
|
|
3739
|
+
results,
|
|
3740
|
+
skipped,
|
|
3715
3741
|
};
|
|
3716
3742
|
if (shouldEmitJson(options, command)) {
|
|
3717
3743
|
console.log(JSON.stringify(payload, null, 2));
|
|
@@ -3735,7 +3761,7 @@ export function registerSessionCommand(program) {
|
|
|
3735
3761
|
.option("--path <path>", "Workspace path for the session", ".")
|
|
3736
3762
|
.option("--json", "Emit machine-readable output")
|
|
3737
3763
|
.action(async (sessionId, files, options, command) => {
|
|
3738
|
-
await
|
|
3764
|
+
await runFileLockCommand("lock", sessionId, files, options, command);
|
|
3739
3765
|
});
|
|
3740
3766
|
|
|
3741
3767
|
session
|
|
@@ -3746,7 +3772,7 @@ export function registerSessionCommand(program) {
|
|
|
3746
3772
|
.option("--path <path>", "Workspace path for the session", ".")
|
|
3747
3773
|
.option("--json", "Emit machine-readable output")
|
|
3748
3774
|
.action(async (sessionId, files, options, command) => {
|
|
3749
|
-
await
|
|
3775
|
+
await runFileLockCommand("unlock", sessionId, files, options, command);
|
|
3750
3776
|
});
|
|
3751
3777
|
|
|
3752
3778
|
session
|
|
@@ -260,6 +260,8 @@ async function appendLockEvent(
|
|
|
260
260
|
{
|
|
261
261
|
targetPath = process.cwd(),
|
|
262
262
|
nowIso = new Date().toISOString(),
|
|
263
|
+
syncRemote = true,
|
|
264
|
+
awaitRemoteSync = false,
|
|
263
265
|
} = {}
|
|
264
266
|
) {
|
|
265
267
|
return appendToStream(
|
|
@@ -273,6 +275,8 @@ async function appendLockEvent(
|
|
|
273
275
|
}),
|
|
274
276
|
{
|
|
275
277
|
targetPath,
|
|
278
|
+
syncRemote,
|
|
279
|
+
awaitRemoteSync,
|
|
276
280
|
}
|
|
277
281
|
);
|
|
278
282
|
}
|
|
@@ -371,6 +375,8 @@ export async function lockFile(
|
|
|
371
375
|
ttlSeconds = DEFAULT_FILE_LOCK_TTL_SECONDS,
|
|
372
376
|
targetPath = process.cwd(),
|
|
373
377
|
nowIso = new Date().toISOString(),
|
|
378
|
+
syncRemote = true,
|
|
379
|
+
awaitRemoteSync = false,
|
|
374
380
|
} = {}
|
|
375
381
|
) {
|
|
376
382
|
const normalizedAgentId = normalizeAgentId(agentId);
|
|
@@ -437,6 +443,8 @@ export async function lockFile(
|
|
|
437
443
|
{
|
|
438
444
|
targetPath,
|
|
439
445
|
nowIso: normalizedNow,
|
|
446
|
+
syncRemote,
|
|
447
|
+
awaitRemoteSync,
|
|
440
448
|
}
|
|
441
449
|
);
|
|
442
450
|
return {
|
|
@@ -462,6 +470,8 @@ export async function unlockFile(
|
|
|
462
470
|
targetPath = process.cwd(),
|
|
463
471
|
nowIso = new Date().toISOString(),
|
|
464
472
|
actorAgentId = null,
|
|
473
|
+
syncRemote = true,
|
|
474
|
+
awaitRemoteSync = false,
|
|
465
475
|
} = {}
|
|
466
476
|
) {
|
|
467
477
|
const normalizedAgentId = normalizeAgentId(agentId);
|
|
@@ -527,6 +537,8 @@ export async function unlockFile(
|
|
|
527
537
|
{
|
|
528
538
|
targetPath,
|
|
529
539
|
nowIso: normalizedNow,
|
|
540
|
+
syncRemote,
|
|
541
|
+
awaitRemoteSync,
|
|
530
542
|
}
|
|
531
543
|
);
|
|
532
544
|
|