@hasna/todos 0.13.6 → 0.13.8

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.
@@ -70,7 +70,7 @@ var package_default;
70
70
  var init_package = __esm(() => {
71
71
  package_default = {
72
72
  name: "@hasna/todos",
73
- version: "0.13.6",
73
+ version: "0.13.8",
74
74
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
75
75
  type: "module",
76
76
  main: "dist/index.js",
@@ -11956,6 +11956,50 @@ var init_checklists = __esm(() => {
11956
11956
  init_database();
11957
11957
  });
11958
11958
 
11959
+ // src/lib/creator-identity.ts
11960
+ import { existsSync as existsSync6, rmSync } from "fs";
11961
+ import { join as join5 } from "path";
11962
+ function identityFilePath() {
11963
+ return join5(getTodosGlobalDir(), "identity.json");
11964
+ }
11965
+ function readPersistedIdentity() {
11966
+ const path = identityFilePath();
11967
+ if (!existsSync6(path))
11968
+ return null;
11969
+ const parsed = readJsonFile(path);
11970
+ if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
11971
+ return null;
11972
+ return parsed;
11973
+ }
11974
+ function canonicalAgentRef(value) {
11975
+ return value.trim().toLowerCase();
11976
+ }
11977
+ function isProcessBoundSource(source) {
11978
+ return source === "explicit" || source === "env";
11979
+ }
11980
+ function resolveWritableIdentity(explicit) {
11981
+ const resolved = resolveCreatorIdentity(explicit);
11982
+ if (!isProcessBoundSource(resolved.source))
11983
+ return { agent_id: null, source: "none" };
11984
+ return resolved;
11985
+ }
11986
+ function resolveCreatorIdentity(explicit) {
11987
+ const fromExplicit = explicit?.trim();
11988
+ if (fromExplicit)
11989
+ return { agent_id: canonicalAgentRef(fromExplicit), source: "explicit" };
11990
+ const fromEnv = (process.env["TODOS_AGENT_ID"] || process.env["HASNA_TODOS_AGENT_ID"] || "").trim();
11991
+ if (fromEnv)
11992
+ return { agent_id: canonicalAgentRef(fromEnv), source: "env" };
11993
+ const persisted = readPersistedIdentity();
11994
+ if (persisted) {
11995
+ return { agent_id: canonicalAgentRef(persisted.agent_name || persisted.agent_id), source: "persisted" };
11996
+ }
11997
+ return { agent_id: null, source: "none" };
11998
+ }
11999
+ var init_creator_identity = __esm(() => {
12000
+ init_sync_utils();
12001
+ });
12002
+
11959
12003
  // src/lib/recurrence.ts
11960
12004
  function parseRecurrenceRule(rule) {
11961
12005
  const normalized = rule.trim().toLowerCase();
@@ -12682,6 +12726,11 @@ __export(exports_task_lifecycle, {
12682
12726
  claimOrSteal: () => claimOrSteal,
12683
12727
  claimNextTask: () => claimNextTask2
12684
12728
  });
12729
+ function sameHolder(stored, incoming) {
12730
+ if (!stored || !incoming)
12731
+ return false;
12732
+ return canonicalAgentRef(stored) === canonicalAgentRef(incoming);
12733
+ }
12685
12734
  function lockExpiresAt(lockedAt) {
12686
12735
  if (!lockedAt)
12687
12736
  return null;
@@ -12732,13 +12781,13 @@ function startTask2(id, agentId, db) {
12732
12781
  const cutoff = lockExpiryCutoff();
12733
12782
  const timestamp2 = now();
12734
12783
  const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, started_at = COALESCE(started_at, ?), version = version + 1, updated_at = ?
12735
- WHERE id = ? AND status IN ('pending', 'in_progress') AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp2, timestamp2, timestamp2, id, agentId, cutoff]);
12784
+ WHERE id = ? AND status IN ('pending', 'in_progress') AND (locked_by IS NULL OR LOWER(TRIM(locked_by)) = LOWER(TRIM(?)) OR locked_at < ?)`, [agentId, agentId, timestamp2, timestamp2, timestamp2, id, agentId, cutoff]);
12736
12785
  if (result.changes === 0) {
12737
12786
  const current = getTask(id, d);
12738
12787
  if (!current)
12739
12788
  throw new TaskNotFoundError(id);
12740
12789
  assertStartable(current, agentId);
12741
- if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
12790
+ if (current.locked_by && !sameHolder(current.locked_by, agentId) && !isLockExpired(current.locked_at)) {
12742
12791
  throw new LockError(id, current.locked_by);
12743
12792
  }
12744
12793
  throw new Error(`Task ${id} could not be started because it changed during claim`);
@@ -12763,7 +12812,7 @@ function completeTask2(id, agentId, db, options) {
12763
12812
  if (task.status === "cancelled") {
12764
12813
  throw new Error(`Task ${id} is cancelled and cannot be completed`);
12765
12814
  }
12766
- if (agentId && task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
12815
+ if (agentId && task.locked_by && !sameHolder(task.locked_by, agentId) && !isLockExpired(task.locked_at)) {
12767
12816
  throw new LockError(id, task.locked_by);
12768
12817
  }
12769
12818
  checkCompletionGuard(task, agentId || null, d);
@@ -12885,16 +12934,16 @@ function lockTask2(id, agentId, db) {
12885
12934
  error: `Task is ${task.status} and cannot be locked`
12886
12935
  };
12887
12936
  }
12888
- if (task.locked_by === agentId && !isLockExpired(task.locked_at)) {
12937
+ if (sameHolder(task.locked_by, agentId) && !isLockExpired(task.locked_at)) {
12889
12938
  const timestamp3 = now();
12890
- d.run(`UPDATE tasks SET locked_at = ?, updated_at = ?, version = version + 1 WHERE id = ? AND locked_by = ?`, [timestamp3, timestamp3, id, agentId]);
12939
+ d.run(`UPDATE tasks SET locked_at = ?, updated_at = ?, version = version + 1 WHERE id = ? AND LOWER(TRIM(locked_by)) = LOWER(TRIM(?))`, [timestamp3, timestamp3, id, agentId]);
12891
12940
  logTaskChange2(id, "lock_renew", "locked_by", agentId, agentId, agentId, d);
12892
12941
  return { success: true, locked_by: agentId, locked_at: timestamp3, expires_at: lockExpiresAt(timestamp3) };
12893
12942
  }
12894
12943
  const cutoff = lockExpiryCutoff();
12895
12944
  const timestamp2 = now();
12896
12945
  const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
12897
- WHERE id = ? AND status NOT IN ('completed', 'cancelled') AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp2, timestamp2, id, agentId, cutoff]);
12946
+ WHERE id = ? AND status NOT IN ('completed', 'cancelled') AND (locked_by IS NULL OR LOWER(TRIM(locked_by)) = LOWER(TRIM(?)) OR locked_at < ?)`, [agentId, timestamp2, timestamp2, id, agentId, cutoff]);
12898
12947
  if (result.changes === 0) {
12899
12948
  const current = getTask(id, d);
12900
12949
  if (!current)
@@ -12926,7 +12975,7 @@ function unlockTask2(id, agentId, db) {
12926
12975
  const task = getTask(id, d);
12927
12976
  if (!task)
12928
12977
  throw new TaskNotFoundError(id);
12929
- if (agentId && task.locked_by && task.locked_by !== agentId) {
12978
+ if (agentId && task.locked_by && !sameHolder(task.locked_by, agentId)) {
12930
12979
  throw new LockError(id, task.locked_by);
12931
12980
  }
12932
12981
  const timestamp2 = now();
@@ -13219,6 +13268,7 @@ var MAX_SPAWN_DEPTH = 10;
13219
13268
  var init_task_lifecycle = __esm(() => {
13220
13269
  init_types();
13221
13270
  init_database();
13271
+ init_creator_identity();
13222
13272
  init_completion_guard();
13223
13273
  init_event_emission_safety();
13224
13274
  init_event_hooks();
@@ -13674,6 +13724,10 @@ function updateTask2(id, input, db) {
13674
13724
  sets.push("description = ?");
13675
13725
  params.push(input.description);
13676
13726
  }
13727
+ if (input.agent_id !== undefined) {
13728
+ sets.push("agent_id = ?");
13729
+ params.push(input.agent_id);
13730
+ }
13677
13731
  if (input.status !== undefined) {
13678
13732
  if (input.status === "completed") {
13679
13733
  checkCompletionGuard(task, task.assigned_to || task.agent_id || null, d);
@@ -15126,8 +15180,8 @@ var init_boards = __esm(() => {
15126
15180
 
15127
15181
  // src/lib/artifact-store.ts
15128
15182
  import { createHash as createHash4 } from "crypto";
15129
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync2, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
15130
- import { basename, dirname as dirname4, join as join5, resolve as resolve7 } from "path";
15183
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
15184
+ import { basename, dirname as dirname4, join as join6, resolve as resolve7 } from "path";
15131
15185
  import { tmpdir as tmpdir2 } from "os";
15132
15186
  function isInMemoryDb2(path) {
15133
15187
  return path === ":memory:" || path.startsWith("file::memory:");
@@ -15139,15 +15193,15 @@ function artifactStoreRoot() {
15139
15193
  return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
15140
15194
  const dbPath = getDatabasePath();
15141
15195
  if (isInMemoryDb2(dbPath))
15142
- return join5(tmpdir2(), "hasna-todos-artifacts");
15143
- return join5(dirname4(resolve7(dbPath)), "artifacts");
15196
+ return join6(tmpdir2(), "hasna-todos-artifacts");
15197
+ return join6(dirname4(resolve7(dbPath)), "artifacts");
15144
15198
  }
15145
15199
  function artifactStorePath(relativePath) {
15146
15200
  const normalized = relativePath.replace(/\\/g, "/");
15147
15201
  if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
15148
15202
  throw new Error("Invalid artifact store path");
15149
15203
  }
15150
- return join5(artifactStoreRoot(), normalized);
15204
+ return join6(artifactStoreRoot(), normalized);
15151
15205
  }
15152
15206
  function sha2562(buffer) {
15153
15207
  return createHash4("sha256").update(buffer).digest("hex");
@@ -15188,7 +15242,7 @@ function mediaTypeFor(path, textLike) {
15188
15242
  }
15189
15243
  function storeArtifactContent(input) {
15190
15244
  const sourcePath = resolve7(input.path);
15191
- if (!existsSync6(sourcePath))
15245
+ if (!existsSync7(sourcePath))
15192
15246
  return null;
15193
15247
  const sourceStat = statSync2(sourcePath);
15194
15248
  if (!sourceStat.isFile())
@@ -15205,9 +15259,9 @@ function storeArtifactContent(input) {
15205
15259
  redactionStatus = "redacted";
15206
15260
  }
15207
15261
  const storedSha = sha2562(storedBuffer);
15208
- const relativePath = join5("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
15262
+ const relativePath = join6("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
15209
15263
  const destination = artifactStorePath(relativePath);
15210
- if (!existsSync6(destination)) {
15264
+ if (!existsSync7(destination)) {
15211
15265
  mkdirSync4(dirname4(destination), { recursive: true });
15212
15266
  writeFileSync2(destination, storedBuffer);
15213
15267
  }
@@ -15267,7 +15321,7 @@ function verifyStoredArtifact(input) {
15267
15321
  };
15268
15322
  }
15269
15323
  const storedPath = artifactStorePath(store.relative_path);
15270
- if (!existsSync6(storedPath)) {
15324
+ if (!existsSync7(storedPath)) {
15271
15325
  return {
15272
15326
  id: input.id,
15273
15327
  path: input.path,
@@ -17580,8 +17634,8 @@ var exports_doctor = {};
17580
17634
  __export(exports_doctor, {
17581
17635
  runTodosDoctor: () => runTodosDoctor
17582
17636
  });
17583
- import { chmodSync, copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync5, statSync as statSync3 } from "fs";
17584
- import { basename as basename2, dirname as dirname5, join as join6 } from "path";
17637
+ import { chmodSync, copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync5, statSync as statSync3 } from "fs";
17638
+ import { basename as basename2, dirname as dirname5, join as join7 } from "path";
17585
17639
  function tableExists2(db, table) {
17586
17640
  return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
17587
17641
  }
@@ -17675,7 +17729,7 @@ function findMissingProjectRoots(db) {
17675
17729
  continue;
17676
17730
  if (!row.path.startsWith("/"))
17677
17731
  continue;
17678
- if (!existsSync7(row.path))
17732
+ if (!existsSync8(row.path))
17679
17733
  missing++;
17680
17734
  }
17681
17735
  return missing;
@@ -17735,16 +17789,16 @@ function databasePermissionsAreUnsafe(dbPath) {
17735
17789
  function createBackup(dbPath) {
17736
17790
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
17737
17791
  return;
17738
- if (!existsSync7(dbPath))
17792
+ if (!existsSync8(dbPath))
17739
17793
  return;
17740
17794
  const stamp = now().replace(/[:.]/g, "-");
17741
- const backupDir = join6(dirname5(dbPath), `${basename2(dbPath)}.backup-${stamp}`);
17795
+ const backupDir = join7(dirname5(dbPath), `${basename2(dbPath)}.backup-${stamp}`);
17742
17796
  const files = [];
17743
17797
  mkdirSync5(backupDir, { recursive: true });
17744
17798
  for (const source of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
17745
- if (!existsSync7(source))
17799
+ if (!existsSync8(source))
17746
17800
  continue;
17747
- const target = join6(backupDir, basename2(source));
17801
+ const target = join7(backupDir, basename2(source));
17748
17802
  copyFileSync(source, target);
17749
17803
  files.push(target);
17750
17804
  }
@@ -18003,7 +18057,7 @@ var init_doctor = __esm(() => {
18003
18057
  });
18004
18058
 
18005
18059
  // src/server/routes.ts
18006
- import { join as join7, resolve as resolve8, sep as sep2 } from "path";
18060
+ import { join as join8, resolve as resolve8, sep as sep2 } from "path";
18007
18061
  function parseFieldsParam(url) {
18008
18062
  const fieldsParam = url.searchParams.get("fields");
18009
18063
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -18798,7 +18852,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
18798
18852
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
18799
18853
  return null;
18800
18854
  if (path !== "/") {
18801
- const filePath = join7(ctx.dashboardDir, path);
18855
+ const filePath = join8(ctx.dashboardDir, path);
18802
18856
  const resolvedFile = resolve8(filePath);
18803
18857
  const resolvedBase = resolve8(ctx.dashboardDir);
18804
18858
  if (!resolvedFile.startsWith(resolvedBase + sep2) && resolvedFile !== resolvedBase) {
@@ -18808,7 +18862,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
18808
18862
  if (res2)
18809
18863
  return res2;
18810
18864
  }
18811
- const indexPath = join7(ctx.dashboardDir, "index.html");
18865
+ const indexPath = join8(ctx.dashboardDir, "index.html");
18812
18866
  const res = serveStaticFile2(indexPath);
18813
18867
  if (res)
18814
18868
  return res;
@@ -44242,39 +44296,98 @@ var init_token_utils = __esm(() => {
44242
44296
  };
44243
44297
  });
44244
44298
 
44245
- // src/lib/creator-identity.ts
44246
- import { existsSync as existsSync8, rmSync as rmSync2 } from "fs";
44247
- import { join as join8 } from "path";
44248
- function identityFilePath() {
44249
- return join8(getTodosGlobalDir(), "identity.json");
44299
+ // src/lib/assignee-validation.ts
44300
+ import { readFileSync as readFileSync3 } from "fs";
44301
+ import { homedir as homedir2 } from "os";
44302
+ import { join as join9 } from "path";
44303
+ function defaultSeatRosterPath() {
44304
+ return process.env["TODOS_SEAT_ROSTER_PATH"] || join9(homedir2(), ".hasna", "identities", "hasna-seats.roster.json");
44250
44305
  }
44251
- function readPersistedIdentity() {
44252
- const path = identityFilePath();
44253
- if (!existsSync8(path))
44254
- return null;
44255
- const parsed = readJsonFile(path);
44256
- if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
44257
- return null;
44258
- return parsed;
44306
+ function loadSeatSlugs(path = defaultSeatRosterPath()) {
44307
+ try {
44308
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
44309
+ const slugs = new Set;
44310
+ for (const agent of parsed.agents ?? []) {
44311
+ if (typeof agent?.slug === "string" && agent.slug.trim()) {
44312
+ slugs.add(normalizeAgentNameInput(agent.slug));
44313
+ }
44314
+ }
44315
+ return slugs;
44316
+ } catch {
44317
+ return new Set;
44318
+ }
44259
44319
  }
44260
- function canonicalAgentRef(value) {
44261
- return value.trim().toLowerCase();
44320
+ function validateAssignee(input, ctx) {
44321
+ const raw = input.trim();
44322
+ const normalized = normalizeAgentNameInput(raw);
44323
+ if (!normalized) {
44324
+ return {
44325
+ ok: true,
44326
+ assignee: raw,
44327
+ isSeat: false,
44328
+ warning: "Assignee is empty. Use --unassigned to file this deliberately with no owner."
44329
+ };
44330
+ }
44331
+ const byId = ctx.agents.find((a) => a.id.toLowerCase() === normalized);
44332
+ const byName = ctx.agents.filter((a) => normalizeAgentNameInput(a.name) === normalized);
44333
+ const effectiveName = byId ? normalizeAgentNameInput(byId.name) : normalized;
44334
+ if (ctx.seats.has(effectiveName)) {
44335
+ if (ctx.allowSeat) {
44336
+ return { ok: true, assignee: byId ? byId.name : raw, agentId: byId?.id, isSeat: true };
44337
+ }
44338
+ return {
44339
+ ok: false,
44340
+ reason: "seat",
44341
+ message: `'${raw}' is a durable SEAT, and a task assigned to a seat is assigned to nobody \u2014 no session is watching that queue. ` + `Assign a specific agent, use --unassigned to file it with no owner on purpose, or pass --assign-seat if filing at the seat is what you mean.`
44342
+ };
44343
+ }
44344
+ if (byId) {
44345
+ return { ok: true, assignee: byId.name, agentId: byId.id, isSeat: false };
44346
+ }
44347
+ if (byName.length === 0) {
44348
+ return {
44349
+ ok: true,
44350
+ assignee: raw,
44351
+ isSeat: false,
44352
+ warning: `No agent named '${raw}' is registered yet. If that is a typo the task is now routed to nobody \u2014 ` + `check with 'todos agents'.`
44353
+ };
44354
+ }
44355
+ if (byName.length > 1) {
44356
+ const ids = byName.map((a) => a.id).sort();
44357
+ return {
44358
+ ok: false,
44359
+ reason: "ambiguous",
44360
+ message: `'${raw}' names ${byName.length} registered agents (${ids.join(", ")}), so assigning by name would pick one at random \u2014 ` + `which is how a task lands on a stranger. Pass the agent ID instead.`,
44361
+ candidates: byName
44362
+ };
44363
+ }
44364
+ return { ok: true, assignee: byName[0].name, agentId: byName[0].id, isSeat: false };
44262
44365
  }
44263
- function resolveCreatorIdentity(explicit) {
44264
- const fromExplicit = explicit?.trim();
44265
- if (fromExplicit)
44266
- return { agent_id: canonicalAgentRef(fromExplicit), source: "explicit" };
44267
- const fromEnv = (process.env["TODOS_AGENT_ID"] || process.env["HASNA_TODOS_AGENT_ID"] || "").trim();
44268
- if (fromEnv)
44269
- return { agent_id: canonicalAgentRef(fromEnv), source: "env" };
44270
- const persisted = readPersistedIdentity();
44271
- if (persisted) {
44272
- return { agent_id: canonicalAgentRef(persisted.agent_name || persisted.agent_id), source: "persisted" };
44366
+ var init_assignee_validation = () => {};
44367
+
44368
+ // src/lib/assignee-context.ts
44369
+ async function loadAssigneeContext(listAgentsFn, allowSeat, nowMs = Date.now()) {
44370
+ if (!cache || nowMs - cache.at >= ROSTER_TTL_MS) {
44371
+ let agents;
44372
+ let degraded = false;
44373
+ try {
44374
+ agents = await listAgentsFn();
44375
+ } catch {
44376
+ agents = [];
44377
+ degraded = true;
44378
+ }
44379
+ cache = {
44380
+ at: nowMs,
44381
+ agents: agents.map((a) => ({ id: a.id, name: a.name })),
44382
+ seats: loadSeatSlugs(),
44383
+ degraded
44384
+ };
44273
44385
  }
44274
- return { agent_id: null, source: "none" };
44386
+ return { agents: cache.agents, seats: cache.seats, allowSeat, degraded: cache.degraded };
44275
44387
  }
44276
- var init_creator_identity = __esm(() => {
44277
- init_sync_utils();
44388
+ var ROSTER_TTL_MS = 15000, cache;
44389
+ var init_assignee_context = __esm(() => {
44390
+ init_assignee_validation();
44278
44391
  });
44279
44392
 
44280
44393
  // src/pr-groups/http-client.ts
@@ -45592,6 +45705,14 @@ var init_cloud_router = __esm(() => {
45592
45705
  });
45593
45706
 
45594
45707
  // src/mcp/tools/task-crud.ts
45708
+ async function validateMcpAssignee(value, allowSeat) {
45709
+ const cloud = getTodosCloudClient();
45710
+ const ctx = await loadAssigneeContext(() => cloud ? cloudListAgents(cloud) : listAgents(), allowSeat);
45711
+ const verdict = validateAssignee(value, ctx);
45712
+ if (!verdict.ok)
45713
+ throw new Error(`Cannot assign to '${value}'. ${verdict.message}`);
45714
+ return verdict.assignee;
45715
+ }
45595
45716
  function registerTaskCrudTools(server, ctx) {
45596
45717
  const { shouldRegisterTool, resolveId, formatError: formatError2, formatTask } = ctx;
45597
45718
  function mutationTaskResponse(task) {
@@ -45628,6 +45749,7 @@ function registerTaskCrudTools(server, ctx) {
45628
45749
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
45629
45750
  created_by: exports_external.string().optional().describe("Agent who FILED this task. Defaults to the ambient agent identity (todos init / TODOS_AGENT_ID)."),
45630
45751
  unassigned: exports_external.boolean().optional().describe("Deliberately file with no assignee. Without it, the task defaults to the filer."),
45752
+ allow_seat: exports_external.boolean().optional().describe("Allow assigned_to to name a durable seat. A seat queue has no session watching it, so this must be deliberate."),
45631
45753
  depends_on: exports_external.array(exports_external.string()).optional().describe("Array of task IDs this task depends on"),
45632
45754
  short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if not provided, disabled if null)"),
45633
45755
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
@@ -45638,16 +45760,18 @@ function registerTaskCrudTools(server, ctx) {
45638
45760
  retry_count: exports_external.number().optional().describe("Max retry count for agent failures")
45639
45761
  }, async (params) => {
45640
45762
  try {
45641
- const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, created_by, unassigned, ...rest } = params;
45763
+ const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, created_by, unassigned, allow_seat, ...rest } = params;
45764
+ const requestedAssignee = assigned_to ? await validateMcpAssignee(assigned_to, Boolean(allow_seat)) : undefined;
45642
45765
  const creator = resolveCreatorIdentity(created_by);
45643
- const assignee = assigned_to || (unassigned ? undefined : creator.agent_id || undefined);
45766
+ const router = resolveWritableIdentity(created_by);
45767
+ const assignee = requestedAssignee || (unassigned ? undefined : router.agent_id || undefined);
45644
45768
  const cloud = getTodosCloudClient();
45645
45769
  if (cloud) {
45646
45770
  const payload = { ...rest };
45647
- if (creator.agent_id) {
45771
+ if (creator.agent_id)
45648
45772
  payload.created_by = creator.agent_id;
45649
- payload.agent_id = payload.agent_id ?? creator.agent_id;
45650
- }
45773
+ if (router.agent_id)
45774
+ payload.agent_id = payload.agent_id ?? router.agent_id;
45651
45775
  if (assignee)
45652
45776
  payload.assigned_to = assignee;
45653
45777
  if (project_id)
@@ -45670,10 +45794,10 @@ function registerTaskCrudTools(server, ctx) {
45670
45794
  return { content: [{ type: "text", text: mutationTaskResponse(created) }] };
45671
45795
  }
45672
45796
  const resolved = { ...rest };
45673
- if (creator.agent_id) {
45797
+ if (creator.agent_id)
45674
45798
  resolved.created_by = creator.agent_id;
45675
- resolved.agent_id = resolved.agent_id ?? creator.agent_id;
45676
- }
45799
+ if (router.agent_id)
45800
+ resolved.agent_id = resolved.agent_id ?? router.agent_id;
45677
45801
  if (assignee)
45678
45802
  resolved.assigned_to = resolveAssignee(assignee);
45679
45803
  if (project_id)
@@ -45709,6 +45833,7 @@ function registerTaskCrudTools(server, ctx) {
45709
45833
  project_id: exports_external.string().optional().describe("Project ID"),
45710
45834
  task_list_id: exports_external.string().optional().describe("Task list ID"),
45711
45835
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
45836
+ allow_seat: exports_external.boolean().optional().describe("Allow assigned_to to name a durable seat. A seat queue has no session watching it, so this must be deliberate."),
45712
45837
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
45713
45838
  working_dir: exports_external.string().optional().describe("Working directory associated with the task"),
45714
45839
  metadata: exports_external.record(exports_external.unknown()).optional().describe("Metadata object to shallow-merge"),
@@ -45722,7 +45847,7 @@ function registerTaskCrudTools(server, ctx) {
45722
45847
  acceptance: exports_external.unknown().optional()
45723
45848
  }, async (params) => {
45724
45849
  try {
45725
- const { assigned_to, project_id, task_list_id, metadata, expectation_id, expectation_fingerprint, evidence_paths, origin_loop_id, origin_run_id, expected, observed, acceptance, ...rest } = params;
45850
+ const { assigned_to, allow_seat: _allowSeatUpsert, project_id, task_list_id, metadata, expectation_id, expectation_fingerprint, evidence_paths, origin_loop_id, origin_run_id, expected, observed, acceptance, ...rest } = params;
45726
45851
  const mergedMetadata = { ...metadata ?? {} };
45727
45852
  if (expectation_id !== undefined)
45728
45853
  mergedMetadata["expectation_id"] = expectation_id;
@@ -45741,8 +45866,9 @@ function registerTaskCrudTools(server, ctx) {
45741
45866
  if (acceptance !== undefined)
45742
45867
  mergedMetadata["acceptance"] = acceptance;
45743
45868
  const resolved = { ...rest, metadata: mergedMetadata };
45744
- if (assigned_to)
45745
- resolved.assigned_to = resolveAssignee(assigned_to);
45869
+ if (assigned_to) {
45870
+ resolved.assigned_to = resolveAssignee(await validateMcpAssignee(assigned_to, Boolean(params.allow_seat)));
45871
+ }
45746
45872
  if (project_id)
45747
45873
  resolved.project_id = resolveId(project_id, "projects");
45748
45874
  if (task_list_id)
@@ -45873,12 +45999,16 @@ ${task.description}` : null
45873
45999
  completed_at: exports_external.string().optional().describe("ISO timestamp for backdating completion"),
45874
46000
  deadline: exports_external.string().nullable().optional(),
45875
46001
  retry_count: exports_external.number().optional(),
46002
+ allow_seat: exports_external.boolean().optional().describe("Allow assigned_to to name a durable seat. A seat queue has no session watching it, so this must be deliberate."),
45876
46003
  version: exports_external.number().optional().describe("Expected version for optimistic locking")
45877
46004
  }, async (params) => {
45878
46005
  try {
46006
+ if (typeof params.assigned_to === "string" && params.assigned_to !== "") {
46007
+ params.assigned_to = await validateMcpAssignee(params.assigned_to, Boolean(params.allow_seat));
46008
+ }
45879
46009
  const cloud = getTodosCloudClient();
45880
46010
  if (cloud) {
45881
- const { task_id: task_id2, version: version3, estimate, deadline, ...updates2 } = params;
46011
+ const { task_id: task_id2, version: version3, estimate, deadline, allow_seat: _allowSeatCloud, ...updates2 } = params;
45882
46012
  const patch = { ...updates2 };
45883
46013
  if (patch.assigned_to === "")
45884
46014
  patch.assigned_to = null;
@@ -45903,7 +46033,7 @@ ${task.description}` : null
45903
46033
  return { content: [{ type: "text", text: mutationTaskResponse(updated) }] };
45904
46034
  }
45905
46035
  const resolvedId = resolveId(params.task_id);
45906
- const { task_id, version: version2, ...updates } = params;
46036
+ const { task_id, version: version2, allow_seat: _allowSeatLocal, ...updates } = params;
45907
46037
  const resolved = { ...updates };
45908
46038
  if (resolved.assigned_to === "")
45909
46039
  resolved.assigned_to = null;
@@ -45956,11 +46086,14 @@ var init_task_crud2 = __esm(() => {
45956
46086
  init_types();
45957
46087
  init_token_utils();
45958
46088
  init_creator_identity();
46089
+ init_assignee_validation();
46090
+ init_assignee_context();
46091
+ init_agents();
45959
46092
  init_cloud_router();
45960
46093
  });
45961
46094
 
45962
46095
  // src/lib/project-bootstrap.ts
45963
- import { existsSync as existsSync9, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
46096
+ import { existsSync as existsSync9, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
45964
46097
  import { basename as basename3, dirname as dirname6, resolve as resolve9 } from "path";
45965
46098
  function safeStat(path) {
45966
46099
  try {
@@ -45994,7 +46127,7 @@ function readPackageJson(path) {
45994
46127
  if (!existsSync9(file))
45995
46128
  return null;
45996
46129
  try {
45997
- const parsed = JSON.parse(readFileSync3(file, "utf-8"));
46130
+ const parsed = JSON.parse(readFileSync4(file, "utf-8"));
45998
46131
  return parsed && typeof parsed === "object" ? parsed : null;
45999
46132
  } catch {
46000
46133
  return null;
@@ -46556,8 +46689,8 @@ var init_retention_cleanup = __esm(() => {
46556
46689
  });
46557
46690
 
46558
46691
  // src/lib/mention-resolver.ts
46559
- import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
46560
- import { basename as basename4, isAbsolute, join as join9, relative as relative3, resolve as resolve10, sep as sep3 } from "path";
46692
+ import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
46693
+ import { basename as basename4, isAbsolute, join as join10, relative as relative3, resolve as resolve10, sep as sep3 } from "path";
46561
46694
  function blankResolution(parsed) {
46562
46695
  return {
46563
46696
  input: parsed.input,
@@ -46665,7 +46798,7 @@ function resolveFile(parsed, workspace) {
46665
46798
  return resolution;
46666
46799
  }
46667
46800
  if (parsed.line !== undefined) {
46668
- const lineCount = readFileSync4(absolutePath, "utf-8").split(/\r?\n/).length;
46801
+ const lineCount = readFileSync5(absolutePath, "utf-8").split(/\r?\n/).length;
46669
46802
  if (parsed.line < 1 || parsed.line > lineCount) {
46670
46803
  resolution.warnings.push(`line ${parsed.line} is outside the file range 1-${lineCount}`);
46671
46804
  return resolution;
@@ -46688,7 +46821,7 @@ function walkSourceFiles(root, current = root, files = []) {
46688
46821
  if (SKIP_DIRS.has(entry2.name))
46689
46822
  continue;
46690
46823
  }
46691
- const absolutePath = join9(current, entry2.name);
46824
+ const absolutePath = join10(current, entry2.name);
46692
46825
  if (entry2.isDirectory()) {
46693
46826
  if (!SKIP_DIRS.has(entry2.name))
46694
46827
  walkSourceFiles(root, absolutePath, files);
@@ -46718,7 +46851,7 @@ function resolveSymbol(parsed, workspace, maxMatches) {
46718
46851
  const pattern = symbolPattern(name);
46719
46852
  const matches = [];
46720
46853
  for (const file of walkSourceFiles(workspace)) {
46721
- const lines = readFileSync4(file, "utf-8").split(/\r?\n/);
46854
+ const lines = readFileSync5(file, "utf-8").split(/\r?\n/);
46722
46855
  for (let index = 0;index < lines.length; index += 1) {
46723
46856
  const line = lines[index];
46724
46857
  const found = pattern.exec(line);
@@ -50379,8 +50512,8 @@ var init_audit_ledger = __esm(() => {
50379
50512
  });
50380
50513
 
50381
50514
  // src/lib/release-compatibility.ts
50382
- import { readFileSync as readFileSync5 } from "fs";
50383
- import { join as join10, resolve as resolve12 } from "path";
50515
+ import { readFileSync as readFileSync6 } from "fs";
50516
+ import { join as join11, resolve as resolve12 } from "path";
50384
50517
  import { Database as Database2 } from "bun:sqlite";
50385
50518
  function pass(id, message, details) {
50386
50519
  return { id, status: "passed", message, details };
@@ -50392,7 +50525,7 @@ function warn(id, message, details) {
50392
50525
  return { id, status: "warning", message, details };
50393
50526
  }
50394
50527
  function readPackageJson2(root) {
50395
- return JSON.parse(readFileSync5(join10(root, "package.json"), "utf8"));
50528
+ return JSON.parse(readFileSync6(join11(root, "package.json"), "utf8"));
50396
50529
  }
50397
50530
  function sortedKeys(value) {
50398
50531
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -80287,7 +80420,7 @@ var init_agent_run_dispatcher = __esm(() => {
80287
80420
  });
80288
80421
 
80289
80422
  // src/lib/verification-providers.ts
80290
- import { existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
80423
+ import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
80291
80424
  function normalizeName5(name) {
80292
80425
  const normalized = name.trim().toLowerCase();
80293
80426
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -80439,7 +80572,7 @@ Timed out after ${provider.timeout_ms}ms`);
80439
80572
  };
80440
80573
  }
80441
80574
  function runCiLogProvider(input) {
80442
- const text = input.log_text ?? (input.log_path && existsSync12(input.log_path) ? readFileSync6(input.log_path, "utf-8") : "");
80575
+ const text = input.log_text ?? (input.log_path && existsSync12(input.log_path) ? readFileSync7(input.log_path, "utf-8") : "");
80443
80576
  return {
80444
80577
  status: classifyLog(text),
80445
80578
  attempts: 1,
@@ -83171,7 +83304,7 @@ var init_local_bridge = __esm(() => {
83171
83304
 
83172
83305
  // src/lib/local-backups.ts
83173
83306
  import { createHash as createHash7 } from "crypto";
83174
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
83307
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
83175
83308
  import { dirname as dirname7, resolve as resolve13 } from "path";
83176
83309
  import { mkdirSync as mkdirSync6 } from "fs";
83177
83310
  function stableJson2(value) {
@@ -83281,7 +83414,7 @@ function writeLocalBackupFile(backup, outputPath) {
83281
83414
  return path;
83282
83415
  }
83283
83416
  function readLocalBackupFile(path) {
83284
- return JSON.parse(readFileSync7(resolve13(path), "utf-8"));
83417
+ return JSON.parse(readFileSync8(resolve13(path), "utf-8"));
83285
83418
  }
83286
83419
  function verifyLocalBackup(value, options = {}, db) {
83287
83420
  const verifiedAt = options.verified_at ?? now();
@@ -84732,8 +84865,8 @@ __export(exports_local_extensions, {
84732
84865
  discoverLocalExtensions: () => discoverLocalExtensions
84733
84866
  });
84734
84867
  import { createHash as createHash10, createVerify } from "crypto";
84735
- import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
84736
- import { basename as basename5, join as join11, resolve as resolve14 } from "path";
84868
+ import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
84869
+ import { basename as basename5, join as join12, resolve as resolve14 } from "path";
84737
84870
  function isObject3(value) {
84738
84871
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
84739
84872
  }
@@ -84814,7 +84947,7 @@ function normalizeManifest(input) {
84814
84947
  };
84815
84948
  }
84816
84949
  function parseJson(path) {
84817
- return JSON.parse(readFileSync8(path, "utf8"));
84950
+ return JSON.parse(readFileSync9(path, "utf8"));
84818
84951
  }
84819
84952
  function sha2566(bytes) {
84820
84953
  return `sha256:${createHash10("sha256").update(bytes).digest("hex")}`;
@@ -84995,10 +85128,10 @@ function inspectExtensionSource(source3) {
84995
85128
  if (!existsSync13(resolved))
84996
85129
  throw new Error(`extension source not found: ${source3}`);
84997
85130
  const stat = statSync6(resolved);
84998
- const manifestPath = stat.isDirectory() ? [join11(resolved, "todos.extension.json"), join11(resolved, "extension.json")].find(existsSync13) : resolved;
85131
+ const manifestPath = stat.isDirectory() ? [join12(resolved, "todos.extension.json"), join12(resolved, "extension.json")].find(existsSync13) : resolved;
84999
85132
  if (!manifestPath)
85000
85133
  throw new Error(`extension directory ${source3} is missing todos.extension.json`);
85001
- const raw = readFileSync8(manifestPath);
85134
+ const raw = readFileSync9(manifestPath);
85002
85135
  const parsed = parseJson(manifestPath);
85003
85136
  const bundle = isObject3(parsed) && isObject3(parsed["manifest"]);
85004
85137
  const manifest = normalizeManifest(bundle ? parsed["manifest"] : parsed);
@@ -85091,15 +85224,15 @@ function projectExtensionSources(projectPath) {
85091
85224
  return [];
85092
85225
  const root = resolve14(projectPath);
85093
85226
  const candidates = [
85094
- join11(root, "todos.extension.json"),
85095
- join11(root, ".todos", "todos.extension.json")
85227
+ join12(root, "todos.extension.json"),
85228
+ join12(root, ".todos", "todos.extension.json")
85096
85229
  ];
85097
- const extensionDir = join11(root, ".todos", "extensions");
85230
+ const extensionDir = join12(root, ".todos", "extensions");
85098
85231
  if (existsSync13(extensionDir)) {
85099
85232
  for (const entry2 of readdirSync3(extensionDir)) {
85100
85233
  if (entry2.startsWith("."))
85101
85234
  continue;
85102
- const full = join11(extensionDir, entry2);
85235
+ const full = join12(extensionDir, entry2);
85103
85236
  if (statSync6(full).isDirectory() || entry2.endsWith(".json"))
85104
85237
  candidates.push(full);
85105
85238
  }
@@ -89511,9 +89644,9 @@ __export(exports_extract, {
89511
89644
  buildCodebaseIndex: () => buildCodebaseIndex,
89512
89645
  EXTRACT_TAGS: () => EXTRACT_TAGS
89513
89646
  });
89514
- import { existsSync as existsSync14, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
89647
+ import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync7 } from "fs";
89515
89648
  import { createHash as createHash12 } from "crypto";
89516
- import { relative as relative5, resolve as resolve15, join as join12 } from "path";
89649
+ import { relative as relative5, resolve as resolve15, join as join13 } from "path";
89517
89650
  function stableHash(value) {
89518
89651
  return createHash12("sha256").update(value).digest("hex");
89519
89652
  }
@@ -89522,11 +89655,11 @@ function normalizePathForMatch(value) {
89522
89655
  }
89523
89656
  function readGitignorePatterns(basePath) {
89524
89657
  const root = statSync7(basePath).isFile() ? resolve15(basePath, "..") : basePath;
89525
- const gitignorePath = join12(root, ".gitignore");
89658
+ const gitignorePath = join13(root, ".gitignore");
89526
89659
  if (!existsSync14(gitignorePath))
89527
89660
  return [];
89528
89661
  try {
89529
- return readFileSync9(gitignorePath, "utf-8").split(`
89662
+ return readFileSync10(gitignorePath, "utf-8").split(`
89530
89663
  `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!"));
89531
89664
  } catch {
89532
89665
  return [];
@@ -89665,9 +89798,9 @@ function buildCodebaseIndex(options) {
89665
89798
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
89666
89799
  const indexed = [];
89667
89800
  for (const file of files) {
89668
- const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
89801
+ const fullPath = statSync7(basePath).isFile() ? basePath : join13(basePath, file);
89669
89802
  try {
89670
- const source3 = readFileSync9(fullPath, "utf-8");
89803
+ const source3 = readFileSync10(fullPath, "utf-8");
89671
89804
  const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
89672
89805
  indexed.push({
89673
89806
  file: relPath,
@@ -89696,9 +89829,9 @@ function extractTodos(options, db) {
89696
89829
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
89697
89830
  const allComments = [];
89698
89831
  for (const file of files) {
89699
- const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
89832
+ const fullPath = statSync7(basePath).isFile() ? basePath : join13(basePath, file);
89700
89833
  try {
89701
- const source3 = readFileSync9(fullPath, "utf-8");
89834
+ const source3 = readFileSync10(fullPath, "utf-8");
89702
89835
  const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
89703
89836
  const comments = extractFromSource(source3, relPath, tags);
89704
89837
  allComments.push(...comments);
@@ -90647,7 +90780,7 @@ __export(exports_builtin_templates, {
90647
90780
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
90648
90781
  });
90649
90782
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "fs";
90650
- import { join as join13 } from "path";
90783
+ import { join as join14 } from "path";
90651
90784
  function templateMetadata(template) {
90652
90785
  return {
90653
90786
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -90706,7 +90839,7 @@ function writeBuiltinTemplateFiles(directory) {
90706
90839
  mkdirSync7(directory, { recursive: true });
90707
90840
  const files = [];
90708
90841
  for (const entry2 of exportBuiltinTemplateFiles()) {
90709
- const path = join13(directory, entry2.filename);
90842
+ const path = join14(directory, entry2.filename);
90710
90843
  writeFileSync4(path, `${JSON.stringify(entry2.template, null, 2)}
90711
90844
  `, "utf-8");
90712
90845
  files.push(path);
@@ -91232,28 +91365,28 @@ __export(exports_environment_snapshots, {
91232
91365
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
91233
91366
  });
91234
91367
  import { createHash as createHash13 } from "crypto";
91235
- import { existsSync as existsSync15, readFileSync as readFileSync10, statSync as statSync8 } from "fs";
91368
+ import { existsSync as existsSync15, readFileSync as readFileSync11, statSync as statSync8 } from "fs";
91236
91369
  import { hostname as hostname3, platform, arch } from "os";
91237
- import { dirname as dirname8, join as join14, resolve as resolve16 } from "path";
91370
+ import { dirname as dirname8, join as join15, resolve as resolve16 } from "path";
91238
91371
  import { tmpdir as tmpdir3 } from "os";
91239
91372
  function sha2567(value) {
91240
91373
  return createHash13("sha256").update(value).digest("hex");
91241
91374
  }
91242
91375
  function fileRecord(root, relativePath) {
91243
- const path = join14(root, relativePath);
91376
+ const path = join15(root, relativePath);
91244
91377
  if (!existsSync15(path))
91245
91378
  return null;
91246
91379
  const stat = statSync8(path);
91247
91380
  if (!stat.isFile())
91248
91381
  return null;
91249
- const content = readFileSync10(path);
91382
+ const content = readFileSync11(path);
91250
91383
  return { path: relativePath, sha256: sha2567(content), size_bytes: content.length };
91251
91384
  }
91252
91385
  function manifestRecord(root, relativePath) {
91253
91386
  const base = fileRecord(root, relativePath);
91254
91387
  if (!base)
91255
91388
  return null;
91256
- const parsed = readJsonFile(join14(root, relativePath));
91389
+ const parsed = readJsonFile(join15(root, relativePath));
91257
91390
  if (!parsed)
91258
91391
  return { ...base, redacted: {} };
91259
91392
  const redacted = redactValue({
@@ -91348,8 +91481,8 @@ function commandEnv(env, includeValues) {
91348
91481
  function defaultSnapshotDir() {
91349
91482
  const dbPath = getDatabasePath();
91350
91483
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
91351
- return join14(tmpdir3(), "hasna-todos", "environment-snapshots");
91352
- return join14(dirname8(resolve16(dbPath)), "environment-snapshots");
91484
+ return join15(tmpdir3(), "hasna-todos", "environment-snapshots");
91485
+ return join15(dirname8(resolve16(dbPath)), "environment-snapshots");
91353
91486
  }
91354
91487
  function snapshotWithId(snapshot) {
91355
91488
  const digest = sha2567(JSON.stringify(snapshot)).slice(0, 24);
@@ -91396,7 +91529,7 @@ function captureEnvironmentSnapshot(input = {}) {
91396
91529
  });
91397
91530
  }
91398
91531
  function writeEnvironmentSnapshot(snapshot, outputPath) {
91399
- const path = outputPath ? resolve16(outputPath) : join14(defaultSnapshotDir(), `${snapshot.id}.json`);
91532
+ const path = outputPath ? resolve16(outputPath) : join15(defaultSnapshotDir(), `${snapshot.id}.json`);
91400
91533
  ensureDir(dirname8(path));
91401
91534
  writeJsonFile(path, snapshot);
91402
91535
  return path;
@@ -92320,26 +92453,26 @@ __export(exports_serve, {
92320
92453
  MIME_TYPES: () => MIME_TYPES
92321
92454
  });
92322
92455
  import { existsSync as existsSync16 } from "fs";
92323
- import { join as join15, dirname as dirname9, extname } from "path";
92456
+ import { join as join16, dirname as dirname9, extname } from "path";
92324
92457
  import { fileURLToPath } from "url";
92325
92458
  function resolveDashboardDir() {
92326
92459
  const candidates = [];
92327
92460
  try {
92328
92461
  const scriptDir = dirname9(fileURLToPath(import.meta.url));
92329
- candidates.push(join15(scriptDir, "..", "dashboard", "dist"));
92330
- candidates.push(join15(scriptDir, "..", "..", "dashboard", "dist"));
92462
+ candidates.push(join16(scriptDir, "..", "dashboard", "dist"));
92463
+ candidates.push(join16(scriptDir, "..", "..", "dashboard", "dist"));
92331
92464
  } catch {}
92332
92465
  if (process.argv[1]) {
92333
92466
  const mainDir = dirname9(process.argv[1]);
92334
- candidates.push(join15(mainDir, "..", "dashboard", "dist"));
92335
- candidates.push(join15(mainDir, "..", "..", "dashboard", "dist"));
92467
+ candidates.push(join16(mainDir, "..", "dashboard", "dist"));
92468
+ candidates.push(join16(mainDir, "..", "..", "dashboard", "dist"));
92336
92469
  }
92337
- candidates.push(join15(process.cwd(), "dashboard", "dist"));
92470
+ candidates.push(join16(process.cwd(), "dashboard", "dist"));
92338
92471
  for (const candidate of candidates) {
92339
92472
  if (existsSync16(candidate))
92340
92473
  return candidate;
92341
92474
  }
92342
- return join15(process.cwd(), "dashboard", "dist");
92475
+ return join16(process.cwd(), "dashboard", "dist");
92343
92476
  }
92344
92477
  function getProvidedApiKey(req) {
92345
92478
  const headerKey = req.headers.get("x-api-key");