@brutalsystems/muster 3.3.1 → 3.3.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/dist/reap.js CHANGED
@@ -125,8 +125,10 @@ async function stopSession(home, socket, launchId) {
125
125
  });
126
126
  try {
127
127
  const entry = (await new Registry(home).all()).find((e) => e.launchId === launchId);
128
+ // By launch id: since --resume, the durable id may name a newer launch of
129
+ // the same session, which this check was not armed for.
128
130
  if (entry)
129
- await muster.stop(entry.id);
131
+ await muster.stop(entry.launchId);
130
132
  }
131
133
  finally {
132
134
  await muster.close();
package/dist/registry.js CHANGED
@@ -45,6 +45,21 @@ export class IdempotencyKeyReuse extends Error {
45
45
  */
46
46
  const UNOWNED_LOCK_MS = 60_000;
47
47
  let self;
48
+ /** Statuses whose process is over: such an entry no longer holds its id. */
49
+ const FINISHED = ["exited", "failed", "stopped"];
50
+ /**
51
+ * The entry a durable id names. One id can have several since `--resume`: the
52
+ * launches that ran a session before, now finished, and the one running it
53
+ * now. The live one is meant, else the newest; two live ones cannot arise,
54
+ * because `update` refuses a second live holder of an id.
55
+ */
56
+ export function currentEntry(entries) {
57
+ const live = entries.filter((e) => !FINISHED.includes(e.status));
58
+ if (live.length > 1)
59
+ throw new Error("Ambiguous durable id; update by launchId");
60
+ return (live[0] ??
61
+ [...entries].sort((a, b) => b.createdAt.localeCompare(a.createdAt))[0]);
62
+ }
48
63
  export class Registry {
49
64
  home;
50
65
  lockWait;
@@ -300,16 +315,16 @@ export class Registry {
300
315
  async update(id, patch) {
301
316
  return this.transaction(async (entries) => {
302
317
  const byLaunch = entries.find((e) => e.launchId === id);
303
- const durable = entries.filter((e) => e.id === id);
304
- if (!byLaunch && durable.length > 1)
305
- throw new Error("Ambiguous durable id; update by launchId");
306
- const entry = byLaunch ?? durable[0];
318
+ const entry = byLaunch ?? currentEntry(entries.filter((e) => e.id === id));
307
319
  if (!entry)
308
320
  throw new Error(`Unknown launch ${id}`);
309
321
  if ("requester" in patch)
310
322
  throw new Error("requester is recorded once and cannot be updated");
311
323
  if (patch.id &&
312
- entries.some((e) => e !== entry && e.runtime === entry.runtime && e.id === patch.id))
324
+ entries.some((e) => e !== entry &&
325
+ e.runtime === entry.runtime &&
326
+ e.id === patch.id &&
327
+ !FINISHED.includes(e.status)))
313
328
  throw new Error("Durable identity already registered");
314
329
  Object.assign(entry, patch);
315
330
  return structuredClone(entry);
package/dist/run.js CHANGED
@@ -11,7 +11,7 @@ import { assertEnforcementMeetsMinimum, assertLevelWithinCeiling, assertMayAct,
11
11
  import { IdentityError, readIdentity, } from "./identity-store.js";
12
12
  import { resolveCredential } from "./identity-token.js";
13
13
  import { copyIdentity, identityEnv } from "./identity-copy.js";
14
- import { Registry } from "./registry.js";
14
+ import { currentEntry, Registry } from "./registry.js";
15
15
  import { LaunchLog } from "./log.js";
16
16
  import { hosts, selectHost } from "./hosts/index.js";
17
17
  import { AGENTS } from "./agents.js";
@@ -1185,11 +1185,21 @@ export class Muster {
1185
1185
  }
1186
1186
  async find(id) {
1187
1187
  const all = await this.registry.all();
1188
+ // A launch id names exactly one launch, so a caller that holds one (the
1189
+ // reaper) can never reach a later launch of the same resumed session.
1190
+ const launch = all.find((e) => e.launchId === id);
1191
+ if (launch)
1192
+ return launch;
1188
1193
  const exact = all.filter((e) => e.id === id);
1189
1194
  if (exact.length === 1)
1190
1195
  return exact[0];
1191
- if (exact.length > 1)
1192
- throw new Error(`Ambiguous durable id: ${exact.map((e) => e.runtime + ":" + e.id).join(", ")}`);
1196
+ // The same session under two runtimes is ambiguous. Under one, it was
1197
+ // resumed: the launch running it now, else the latest, is the one meant.
1198
+ if (exact.length > 1) {
1199
+ if (new Set(exact.map((e) => e.runtime)).size > 1)
1200
+ throw new Error(`Ambiguous durable id: ${exact.map((e) => e.runtime + ":" + e.id).join(", ")}`);
1201
+ return currentEntry(exact);
1202
+ }
1193
1203
  const peers = all.filter((e) => e.kind === "session" && e.status === "running" && e.peer);
1194
1204
  const named = peers.map((e) => ({
1195
1205
  runtime: runtimeName(e.runtime),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brutalsystems/muster",
3
- "version": "3.3.1",
3
+ "version": "3.3.2",
4
4
  "description": "Launch instructed, reachable local agent sessions",
5
5
  "type": "module",
6
6
  "engines": {