@hasna/todos 0.13.7 → 0.13.9

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.7",
73
+ version: "0.13.9",
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();
@@ -15130,8 +15180,8 @@ var init_boards = __esm(() => {
15130
15180
 
15131
15181
  // src/lib/artifact-store.ts
15132
15182
  import { createHash as createHash4 } from "crypto";
15133
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync2, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
15134
- 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";
15135
15185
  import { tmpdir as tmpdir2 } from "os";
15136
15186
  function isInMemoryDb2(path) {
15137
15187
  return path === ":memory:" || path.startsWith("file::memory:");
@@ -15143,15 +15193,15 @@ function artifactStoreRoot() {
15143
15193
  return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
15144
15194
  const dbPath = getDatabasePath();
15145
15195
  if (isInMemoryDb2(dbPath))
15146
- return join5(tmpdir2(), "hasna-todos-artifacts");
15147
- return join5(dirname4(resolve7(dbPath)), "artifacts");
15196
+ return join6(tmpdir2(), "hasna-todos-artifacts");
15197
+ return join6(dirname4(resolve7(dbPath)), "artifacts");
15148
15198
  }
15149
15199
  function artifactStorePath(relativePath) {
15150
15200
  const normalized = relativePath.replace(/\\/g, "/");
15151
15201
  if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
15152
15202
  throw new Error("Invalid artifact store path");
15153
15203
  }
15154
- return join5(artifactStoreRoot(), normalized);
15204
+ return join6(artifactStoreRoot(), normalized);
15155
15205
  }
15156
15206
  function sha2562(buffer) {
15157
15207
  return createHash4("sha256").update(buffer).digest("hex");
@@ -15192,7 +15242,7 @@ function mediaTypeFor(path, textLike) {
15192
15242
  }
15193
15243
  function storeArtifactContent(input) {
15194
15244
  const sourcePath = resolve7(input.path);
15195
- if (!existsSync6(sourcePath))
15245
+ if (!existsSync7(sourcePath))
15196
15246
  return null;
15197
15247
  const sourceStat = statSync2(sourcePath);
15198
15248
  if (!sourceStat.isFile())
@@ -15209,9 +15259,9 @@ function storeArtifactContent(input) {
15209
15259
  redactionStatus = "redacted";
15210
15260
  }
15211
15261
  const storedSha = sha2562(storedBuffer);
15212
- const relativePath = join5("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
15262
+ const relativePath = join6("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
15213
15263
  const destination = artifactStorePath(relativePath);
15214
- if (!existsSync6(destination)) {
15264
+ if (!existsSync7(destination)) {
15215
15265
  mkdirSync4(dirname4(destination), { recursive: true });
15216
15266
  writeFileSync2(destination, storedBuffer);
15217
15267
  }
@@ -15271,7 +15321,7 @@ function verifyStoredArtifact(input) {
15271
15321
  };
15272
15322
  }
15273
15323
  const storedPath = artifactStorePath(store.relative_path);
15274
- if (!existsSync6(storedPath)) {
15324
+ if (!existsSync7(storedPath)) {
15275
15325
  return {
15276
15326
  id: input.id,
15277
15327
  path: input.path,
@@ -17584,8 +17634,8 @@ var exports_doctor = {};
17584
17634
  __export(exports_doctor, {
17585
17635
  runTodosDoctor: () => runTodosDoctor
17586
17636
  });
17587
- import { chmodSync, copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync5, statSync as statSync3 } from "fs";
17588
- 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";
17589
17639
  function tableExists2(db, table) {
17590
17640
  return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
17591
17641
  }
@@ -17679,7 +17729,7 @@ function findMissingProjectRoots(db) {
17679
17729
  continue;
17680
17730
  if (!row.path.startsWith("/"))
17681
17731
  continue;
17682
- if (!existsSync7(row.path))
17732
+ if (!existsSync8(row.path))
17683
17733
  missing++;
17684
17734
  }
17685
17735
  return missing;
@@ -17739,16 +17789,16 @@ function databasePermissionsAreUnsafe(dbPath) {
17739
17789
  function createBackup(dbPath) {
17740
17790
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
17741
17791
  return;
17742
- if (!existsSync7(dbPath))
17792
+ if (!existsSync8(dbPath))
17743
17793
  return;
17744
17794
  const stamp = now().replace(/[:.]/g, "-");
17745
- const backupDir = join6(dirname5(dbPath), `${basename2(dbPath)}.backup-${stamp}`);
17795
+ const backupDir = join7(dirname5(dbPath), `${basename2(dbPath)}.backup-${stamp}`);
17746
17796
  const files = [];
17747
17797
  mkdirSync5(backupDir, { recursive: true });
17748
17798
  for (const source of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
17749
- if (!existsSync7(source))
17799
+ if (!existsSync8(source))
17750
17800
  continue;
17751
- const target = join6(backupDir, basename2(source));
17801
+ const target = join7(backupDir, basename2(source));
17752
17802
  copyFileSync(source, target);
17753
17803
  files.push(target);
17754
17804
  }
@@ -18007,7 +18057,7 @@ var init_doctor = __esm(() => {
18007
18057
  });
18008
18058
 
18009
18059
  // src/server/routes.ts
18010
- 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";
18011
18061
  function parseFieldsParam(url) {
18012
18062
  const fieldsParam = url.searchParams.get("fields");
18013
18063
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -18802,7 +18852,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
18802
18852
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
18803
18853
  return null;
18804
18854
  if (path !== "/") {
18805
- const filePath = join7(ctx.dashboardDir, path);
18855
+ const filePath = join8(ctx.dashboardDir, path);
18806
18856
  const resolvedFile = resolve8(filePath);
18807
18857
  const resolvedBase = resolve8(ctx.dashboardDir);
18808
18858
  if (!resolvedFile.startsWith(resolvedBase + sep2) && resolvedFile !== resolvedBase) {
@@ -18812,7 +18862,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
18812
18862
  if (res2)
18813
18863
  return res2;
18814
18864
  }
18815
- const indexPath = join7(ctx.dashboardDir, "index.html");
18865
+ const indexPath = join8(ctx.dashboardDir, "index.html");
18816
18866
  const res = serveStaticFile2(indexPath);
18817
18867
  if (res)
18818
18868
  return res;
@@ -22191,10 +22241,10 @@ async function handleV1Request(req, url, dependencies = {}) {
22191
22241
  const released2 = await store.tasks.unlock(id);
22192
22242
  return json3({ success: released2 });
22193
22243
  }
22194
- if (body2.agent_id && principal.agent && body2.agent_id !== principal.agent && !principal.scopes.includes("todos:*")) {
22244
+ if (body2.agent_id && body2.agent_id !== principal.agent && !principal.scopes.includes("todos:*")) {
22195
22245
  return error(403, "unlock agent_id must match the authenticated agent");
22196
22246
  }
22197
- const agentId2 = principal.agent || body2.agent_id;
22247
+ const agentId2 = body2.agent_id || principal.agent;
22198
22248
  if (!agentId2)
22199
22249
  return error(403, "unlock requires an agent-bound key or force=true");
22200
22250
  const released = await store.tasks.unlock(id, agentId2);
@@ -44246,48 +44296,98 @@ var init_token_utils = __esm(() => {
44246
44296
  };
44247
44297
  });
44248
44298
 
44249
- // src/lib/creator-identity.ts
44250
- import { existsSync as existsSync8, rmSync as rmSync2 } from "fs";
44251
- import { join as join8 } from "path";
44252
- function identityFilePath() {
44253
- return join8(getTodosGlobalDir(), "identity.json");
44254
- }
44255
- function readPersistedIdentity() {
44256
- const path = identityFilePath();
44257
- if (!existsSync8(path))
44258
- return null;
44259
- const parsed = readJsonFile(path);
44260
- if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
44261
- return null;
44262
- return parsed;
44263
- }
44264
- function canonicalAgentRef(value) {
44265
- return value.trim().toLowerCase();
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");
44266
44305
  }
44267
- function isProcessBoundSource(source) {
44268
- return source === "explicit" || source === "env";
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
+ }
44269
44319
  }
44270
- function resolveWritableIdentity(explicit) {
44271
- const resolved = resolveCreatorIdentity(explicit);
44272
- if (!isProcessBoundSource(resolved.source))
44273
- return { agent_id: null, source: "none" };
44274
- return resolved;
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 };
44275
44365
  }
44276
- function resolveCreatorIdentity(explicit) {
44277
- const fromExplicit = explicit?.trim();
44278
- if (fromExplicit)
44279
- return { agent_id: canonicalAgentRef(fromExplicit), source: "explicit" };
44280
- const fromEnv = (process.env["TODOS_AGENT_ID"] || process.env["HASNA_TODOS_AGENT_ID"] || "").trim();
44281
- if (fromEnv)
44282
- return { agent_id: canonicalAgentRef(fromEnv), source: "env" };
44283
- const persisted = readPersistedIdentity();
44284
- if (persisted) {
44285
- 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
+ };
44286
44385
  }
44287
- return { agent_id: null, source: "none" };
44386
+ return { agents: cache.agents, seats: cache.seats, allowSeat, degraded: cache.degraded };
44288
44387
  }
44289
- var init_creator_identity = __esm(() => {
44290
- init_sync_utils();
44388
+ var ROSTER_TTL_MS = 15000, cache;
44389
+ var init_assignee_context = __esm(() => {
44390
+ init_assignee_validation();
44291
44391
  });
44292
44392
 
44293
44393
  // src/pr-groups/http-client.ts
@@ -45605,6 +45705,14 @@ var init_cloud_router = __esm(() => {
45605
45705
  });
45606
45706
 
45607
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
+ }
45608
45716
  function registerTaskCrudTools(server, ctx) {
45609
45717
  const { shouldRegisterTool, resolveId, formatError: formatError2, formatTask } = ctx;
45610
45718
  function mutationTaskResponse(task) {
@@ -45641,6 +45749,7 @@ function registerTaskCrudTools(server, ctx) {
45641
45749
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
45642
45750
  created_by: exports_external.string().optional().describe("Agent who FILED this task. Defaults to the ambient agent identity (todos init / TODOS_AGENT_ID)."),
45643
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."),
45644
45753
  depends_on: exports_external.array(exports_external.string()).optional().describe("Array of task IDs this task depends on"),
45645
45754
  short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if not provided, disabled if null)"),
45646
45755
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
@@ -45651,10 +45760,11 @@ function registerTaskCrudTools(server, ctx) {
45651
45760
  retry_count: exports_external.number().optional().describe("Max retry count for agent failures")
45652
45761
  }, async (params) => {
45653
45762
  try {
45654
- 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;
45655
45765
  const creator = resolveCreatorIdentity(created_by);
45656
45766
  const router = resolveWritableIdentity(created_by);
45657
- const assignee = assigned_to || (unassigned ? undefined : router.agent_id || undefined);
45767
+ const assignee = requestedAssignee || (unassigned ? undefined : router.agent_id || undefined);
45658
45768
  const cloud = getTodosCloudClient();
45659
45769
  if (cloud) {
45660
45770
  const payload = { ...rest };
@@ -45723,6 +45833,7 @@ function registerTaskCrudTools(server, ctx) {
45723
45833
  project_id: exports_external.string().optional().describe("Project ID"),
45724
45834
  task_list_id: exports_external.string().optional().describe("Task list ID"),
45725
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."),
45726
45837
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
45727
45838
  working_dir: exports_external.string().optional().describe("Working directory associated with the task"),
45728
45839
  metadata: exports_external.record(exports_external.unknown()).optional().describe("Metadata object to shallow-merge"),
@@ -45736,7 +45847,7 @@ function registerTaskCrudTools(server, ctx) {
45736
45847
  acceptance: exports_external.unknown().optional()
45737
45848
  }, async (params) => {
45738
45849
  try {
45739
- 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;
45740
45851
  const mergedMetadata = { ...metadata ?? {} };
45741
45852
  if (expectation_id !== undefined)
45742
45853
  mergedMetadata["expectation_id"] = expectation_id;
@@ -45755,8 +45866,9 @@ function registerTaskCrudTools(server, ctx) {
45755
45866
  if (acceptance !== undefined)
45756
45867
  mergedMetadata["acceptance"] = acceptance;
45757
45868
  const resolved = { ...rest, metadata: mergedMetadata };
45758
- if (assigned_to)
45759
- resolved.assigned_to = resolveAssignee(assigned_to);
45869
+ if (assigned_to) {
45870
+ resolved.assigned_to = resolveAssignee(await validateMcpAssignee(assigned_to, Boolean(params.allow_seat)));
45871
+ }
45760
45872
  if (project_id)
45761
45873
  resolved.project_id = resolveId(project_id, "projects");
45762
45874
  if (task_list_id)
@@ -45887,12 +45999,16 @@ ${task.description}` : null
45887
45999
  completed_at: exports_external.string().optional().describe("ISO timestamp for backdating completion"),
45888
46000
  deadline: exports_external.string().nullable().optional(),
45889
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."),
45890
46003
  version: exports_external.number().optional().describe("Expected version for optimistic locking")
45891
46004
  }, async (params) => {
45892
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
+ }
45893
46009
  const cloud = getTodosCloudClient();
45894
46010
  if (cloud) {
45895
- 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;
45896
46012
  const patch = { ...updates2 };
45897
46013
  if (patch.assigned_to === "")
45898
46014
  patch.assigned_to = null;
@@ -45917,7 +46033,7 @@ ${task.description}` : null
45917
46033
  return { content: [{ type: "text", text: mutationTaskResponse(updated) }] };
45918
46034
  }
45919
46035
  const resolvedId = resolveId(params.task_id);
45920
- const { task_id, version: version2, ...updates } = params;
46036
+ const { task_id, version: version2, allow_seat: _allowSeatLocal, ...updates } = params;
45921
46037
  const resolved = { ...updates };
45922
46038
  if (resolved.assigned_to === "")
45923
46039
  resolved.assigned_to = null;
@@ -45970,11 +46086,14 @@ var init_task_crud2 = __esm(() => {
45970
46086
  init_types();
45971
46087
  init_token_utils();
45972
46088
  init_creator_identity();
46089
+ init_assignee_validation();
46090
+ init_assignee_context();
46091
+ init_agents();
45973
46092
  init_cloud_router();
45974
46093
  });
45975
46094
 
45976
46095
  // src/lib/project-bootstrap.ts
45977
- 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";
45978
46097
  import { basename as basename3, dirname as dirname6, resolve as resolve9 } from "path";
45979
46098
  function safeStat(path) {
45980
46099
  try {
@@ -46008,7 +46127,7 @@ function readPackageJson(path) {
46008
46127
  if (!existsSync9(file))
46009
46128
  return null;
46010
46129
  try {
46011
- const parsed = JSON.parse(readFileSync3(file, "utf-8"));
46130
+ const parsed = JSON.parse(readFileSync4(file, "utf-8"));
46012
46131
  return parsed && typeof parsed === "object" ? parsed : null;
46013
46132
  } catch {
46014
46133
  return null;
@@ -46570,8 +46689,8 @@ var init_retention_cleanup = __esm(() => {
46570
46689
  });
46571
46690
 
46572
46691
  // src/lib/mention-resolver.ts
46573
- import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
46574
- 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";
46575
46694
  function blankResolution(parsed) {
46576
46695
  return {
46577
46696
  input: parsed.input,
@@ -46679,7 +46798,7 @@ function resolveFile(parsed, workspace) {
46679
46798
  return resolution;
46680
46799
  }
46681
46800
  if (parsed.line !== undefined) {
46682
- const lineCount = readFileSync4(absolutePath, "utf-8").split(/\r?\n/).length;
46801
+ const lineCount = readFileSync5(absolutePath, "utf-8").split(/\r?\n/).length;
46683
46802
  if (parsed.line < 1 || parsed.line > lineCount) {
46684
46803
  resolution.warnings.push(`line ${parsed.line} is outside the file range 1-${lineCount}`);
46685
46804
  return resolution;
@@ -46702,7 +46821,7 @@ function walkSourceFiles(root, current = root, files = []) {
46702
46821
  if (SKIP_DIRS.has(entry2.name))
46703
46822
  continue;
46704
46823
  }
46705
- const absolutePath = join9(current, entry2.name);
46824
+ const absolutePath = join10(current, entry2.name);
46706
46825
  if (entry2.isDirectory()) {
46707
46826
  if (!SKIP_DIRS.has(entry2.name))
46708
46827
  walkSourceFiles(root, absolutePath, files);
@@ -46732,7 +46851,7 @@ function resolveSymbol(parsed, workspace, maxMatches) {
46732
46851
  const pattern = symbolPattern(name);
46733
46852
  const matches = [];
46734
46853
  for (const file of walkSourceFiles(workspace)) {
46735
- const lines = readFileSync4(file, "utf-8").split(/\r?\n/);
46854
+ const lines = readFileSync5(file, "utf-8").split(/\r?\n/);
46736
46855
  for (let index = 0;index < lines.length; index += 1) {
46737
46856
  const line = lines[index];
46738
46857
  const found = pattern.exec(line);
@@ -50393,8 +50512,8 @@ var init_audit_ledger = __esm(() => {
50393
50512
  });
50394
50513
 
50395
50514
  // src/lib/release-compatibility.ts
50396
- import { readFileSync as readFileSync5 } from "fs";
50397
- 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";
50398
50517
  import { Database as Database2 } from "bun:sqlite";
50399
50518
  function pass(id, message, details) {
50400
50519
  return { id, status: "passed", message, details };
@@ -50406,7 +50525,7 @@ function warn(id, message, details) {
50406
50525
  return { id, status: "warning", message, details };
50407
50526
  }
50408
50527
  function readPackageJson2(root) {
50409
- return JSON.parse(readFileSync5(join10(root, "package.json"), "utf8"));
50528
+ return JSON.parse(readFileSync6(join11(root, "package.json"), "utf8"));
50410
50529
  }
50411
50530
  function sortedKeys(value) {
50412
50531
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -80301,7 +80420,7 @@ var init_agent_run_dispatcher = __esm(() => {
80301
80420
  });
80302
80421
 
80303
80422
  // src/lib/verification-providers.ts
80304
- import { existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
80423
+ import { existsSync as existsSync12, readFileSync as readFileSync7 } from "fs";
80305
80424
  function normalizeName5(name) {
80306
80425
  const normalized = name.trim().toLowerCase();
80307
80426
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -80453,7 +80572,7 @@ Timed out after ${provider.timeout_ms}ms`);
80453
80572
  };
80454
80573
  }
80455
80574
  function runCiLogProvider(input) {
80456
- 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") : "");
80457
80576
  return {
80458
80577
  status: classifyLog(text),
80459
80578
  attempts: 1,
@@ -83185,7 +83304,7 @@ var init_local_bridge = __esm(() => {
83185
83304
 
83186
83305
  // src/lib/local-backups.ts
83187
83306
  import { createHash as createHash7 } from "crypto";
83188
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
83307
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
83189
83308
  import { dirname as dirname7, resolve as resolve13 } from "path";
83190
83309
  import { mkdirSync as mkdirSync6 } from "fs";
83191
83310
  function stableJson2(value) {
@@ -83295,7 +83414,7 @@ function writeLocalBackupFile(backup, outputPath) {
83295
83414
  return path;
83296
83415
  }
83297
83416
  function readLocalBackupFile(path) {
83298
- return JSON.parse(readFileSync7(resolve13(path), "utf-8"));
83417
+ return JSON.parse(readFileSync8(resolve13(path), "utf-8"));
83299
83418
  }
83300
83419
  function verifyLocalBackup(value, options = {}, db) {
83301
83420
  const verifiedAt = options.verified_at ?? now();
@@ -84746,8 +84865,8 @@ __export(exports_local_extensions, {
84746
84865
  discoverLocalExtensions: () => discoverLocalExtensions
84747
84866
  });
84748
84867
  import { createHash as createHash10, createVerify } from "crypto";
84749
- import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
84750
- 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";
84751
84870
  function isObject3(value) {
84752
84871
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
84753
84872
  }
@@ -84828,7 +84947,7 @@ function normalizeManifest(input) {
84828
84947
  };
84829
84948
  }
84830
84949
  function parseJson(path) {
84831
- return JSON.parse(readFileSync8(path, "utf8"));
84950
+ return JSON.parse(readFileSync9(path, "utf8"));
84832
84951
  }
84833
84952
  function sha2566(bytes) {
84834
84953
  return `sha256:${createHash10("sha256").update(bytes).digest("hex")}`;
@@ -85009,10 +85128,10 @@ function inspectExtensionSource(source3) {
85009
85128
  if (!existsSync13(resolved))
85010
85129
  throw new Error(`extension source not found: ${source3}`);
85011
85130
  const stat = statSync6(resolved);
85012
- 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;
85013
85132
  if (!manifestPath)
85014
85133
  throw new Error(`extension directory ${source3} is missing todos.extension.json`);
85015
- const raw = readFileSync8(manifestPath);
85134
+ const raw = readFileSync9(manifestPath);
85016
85135
  const parsed = parseJson(manifestPath);
85017
85136
  const bundle = isObject3(parsed) && isObject3(parsed["manifest"]);
85018
85137
  const manifest = normalizeManifest(bundle ? parsed["manifest"] : parsed);
@@ -85105,15 +85224,15 @@ function projectExtensionSources(projectPath) {
85105
85224
  return [];
85106
85225
  const root = resolve14(projectPath);
85107
85226
  const candidates = [
85108
- join11(root, "todos.extension.json"),
85109
- join11(root, ".todos", "todos.extension.json")
85227
+ join12(root, "todos.extension.json"),
85228
+ join12(root, ".todos", "todos.extension.json")
85110
85229
  ];
85111
- const extensionDir = join11(root, ".todos", "extensions");
85230
+ const extensionDir = join12(root, ".todos", "extensions");
85112
85231
  if (existsSync13(extensionDir)) {
85113
85232
  for (const entry2 of readdirSync3(extensionDir)) {
85114
85233
  if (entry2.startsWith("."))
85115
85234
  continue;
85116
- const full = join11(extensionDir, entry2);
85235
+ const full = join12(extensionDir, entry2);
85117
85236
  if (statSync6(full).isDirectory() || entry2.endsWith(".json"))
85118
85237
  candidates.push(full);
85119
85238
  }
@@ -89525,9 +89644,9 @@ __export(exports_extract, {
89525
89644
  buildCodebaseIndex: () => buildCodebaseIndex,
89526
89645
  EXTRACT_TAGS: () => EXTRACT_TAGS
89527
89646
  });
89528
- 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";
89529
89648
  import { createHash as createHash12 } from "crypto";
89530
- 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";
89531
89650
  function stableHash(value) {
89532
89651
  return createHash12("sha256").update(value).digest("hex");
89533
89652
  }
@@ -89536,11 +89655,11 @@ function normalizePathForMatch(value) {
89536
89655
  }
89537
89656
  function readGitignorePatterns(basePath) {
89538
89657
  const root = statSync7(basePath).isFile() ? resolve15(basePath, "..") : basePath;
89539
- const gitignorePath = join12(root, ".gitignore");
89658
+ const gitignorePath = join13(root, ".gitignore");
89540
89659
  if (!existsSync14(gitignorePath))
89541
89660
  return [];
89542
89661
  try {
89543
- return readFileSync9(gitignorePath, "utf-8").split(`
89662
+ return readFileSync10(gitignorePath, "utf-8").split(`
89544
89663
  `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!"));
89545
89664
  } catch {
89546
89665
  return [];
@@ -89679,9 +89798,9 @@ function buildCodebaseIndex(options) {
89679
89798
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
89680
89799
  const indexed = [];
89681
89800
  for (const file of files) {
89682
- const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
89801
+ const fullPath = statSync7(basePath).isFile() ? basePath : join13(basePath, file);
89683
89802
  try {
89684
- const source3 = readFileSync9(fullPath, "utf-8");
89803
+ const source3 = readFileSync10(fullPath, "utf-8");
89685
89804
  const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
89686
89805
  indexed.push({
89687
89806
  file: relPath,
@@ -89710,9 +89829,9 @@ function extractTodos(options, db) {
89710
89829
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
89711
89830
  const allComments = [];
89712
89831
  for (const file of files) {
89713
- const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
89832
+ const fullPath = statSync7(basePath).isFile() ? basePath : join13(basePath, file);
89714
89833
  try {
89715
- const source3 = readFileSync9(fullPath, "utf-8");
89834
+ const source3 = readFileSync10(fullPath, "utf-8");
89716
89835
  const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
89717
89836
  const comments = extractFromSource(source3, relPath, tags);
89718
89837
  allComments.push(...comments);
@@ -90661,7 +90780,7 @@ __export(exports_builtin_templates, {
90661
90780
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
90662
90781
  });
90663
90782
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "fs";
90664
- import { join as join13 } from "path";
90783
+ import { join as join14 } from "path";
90665
90784
  function templateMetadata(template) {
90666
90785
  return {
90667
90786
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -90720,7 +90839,7 @@ function writeBuiltinTemplateFiles(directory) {
90720
90839
  mkdirSync7(directory, { recursive: true });
90721
90840
  const files = [];
90722
90841
  for (const entry2 of exportBuiltinTemplateFiles()) {
90723
- const path = join13(directory, entry2.filename);
90842
+ const path = join14(directory, entry2.filename);
90724
90843
  writeFileSync4(path, `${JSON.stringify(entry2.template, null, 2)}
90725
90844
  `, "utf-8");
90726
90845
  files.push(path);
@@ -91246,28 +91365,28 @@ __export(exports_environment_snapshots, {
91246
91365
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
91247
91366
  });
91248
91367
  import { createHash as createHash13 } from "crypto";
91249
- 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";
91250
91369
  import { hostname as hostname3, platform, arch } from "os";
91251
- 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";
91252
91371
  import { tmpdir as tmpdir3 } from "os";
91253
91372
  function sha2567(value) {
91254
91373
  return createHash13("sha256").update(value).digest("hex");
91255
91374
  }
91256
91375
  function fileRecord(root, relativePath) {
91257
- const path = join14(root, relativePath);
91376
+ const path = join15(root, relativePath);
91258
91377
  if (!existsSync15(path))
91259
91378
  return null;
91260
91379
  const stat = statSync8(path);
91261
91380
  if (!stat.isFile())
91262
91381
  return null;
91263
- const content = readFileSync10(path);
91382
+ const content = readFileSync11(path);
91264
91383
  return { path: relativePath, sha256: sha2567(content), size_bytes: content.length };
91265
91384
  }
91266
91385
  function manifestRecord(root, relativePath) {
91267
91386
  const base = fileRecord(root, relativePath);
91268
91387
  if (!base)
91269
91388
  return null;
91270
- const parsed = readJsonFile(join14(root, relativePath));
91389
+ const parsed = readJsonFile(join15(root, relativePath));
91271
91390
  if (!parsed)
91272
91391
  return { ...base, redacted: {} };
91273
91392
  const redacted = redactValue({
@@ -91362,8 +91481,8 @@ function commandEnv(env, includeValues) {
91362
91481
  function defaultSnapshotDir() {
91363
91482
  const dbPath = getDatabasePath();
91364
91483
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
91365
- return join14(tmpdir3(), "hasna-todos", "environment-snapshots");
91366
- return join14(dirname8(resolve16(dbPath)), "environment-snapshots");
91484
+ return join15(tmpdir3(), "hasna-todos", "environment-snapshots");
91485
+ return join15(dirname8(resolve16(dbPath)), "environment-snapshots");
91367
91486
  }
91368
91487
  function snapshotWithId(snapshot) {
91369
91488
  const digest = sha2567(JSON.stringify(snapshot)).slice(0, 24);
@@ -91410,7 +91529,7 @@ function captureEnvironmentSnapshot(input = {}) {
91410
91529
  });
91411
91530
  }
91412
91531
  function writeEnvironmentSnapshot(snapshot, outputPath) {
91413
- const path = outputPath ? resolve16(outputPath) : join14(defaultSnapshotDir(), `${snapshot.id}.json`);
91532
+ const path = outputPath ? resolve16(outputPath) : join15(defaultSnapshotDir(), `${snapshot.id}.json`);
91414
91533
  ensureDir(dirname8(path));
91415
91534
  writeJsonFile(path, snapshot);
91416
91535
  return path;
@@ -92334,26 +92453,26 @@ __export(exports_serve, {
92334
92453
  MIME_TYPES: () => MIME_TYPES
92335
92454
  });
92336
92455
  import { existsSync as existsSync16 } from "fs";
92337
- import { join as join15, dirname as dirname9, extname } from "path";
92456
+ import { join as join16, dirname as dirname9, extname } from "path";
92338
92457
  import { fileURLToPath } from "url";
92339
92458
  function resolveDashboardDir() {
92340
92459
  const candidates = [];
92341
92460
  try {
92342
92461
  const scriptDir = dirname9(fileURLToPath(import.meta.url));
92343
- candidates.push(join15(scriptDir, "..", "dashboard", "dist"));
92344
- candidates.push(join15(scriptDir, "..", "..", "dashboard", "dist"));
92462
+ candidates.push(join16(scriptDir, "..", "dashboard", "dist"));
92463
+ candidates.push(join16(scriptDir, "..", "..", "dashboard", "dist"));
92345
92464
  } catch {}
92346
92465
  if (process.argv[1]) {
92347
92466
  const mainDir = dirname9(process.argv[1]);
92348
- candidates.push(join15(mainDir, "..", "dashboard", "dist"));
92349
- candidates.push(join15(mainDir, "..", "..", "dashboard", "dist"));
92467
+ candidates.push(join16(mainDir, "..", "dashboard", "dist"));
92468
+ candidates.push(join16(mainDir, "..", "..", "dashboard", "dist"));
92350
92469
  }
92351
- candidates.push(join15(process.cwd(), "dashboard", "dist"));
92470
+ candidates.push(join16(process.cwd(), "dashboard", "dist"));
92352
92471
  for (const candidate of candidates) {
92353
92472
  if (existsSync16(candidate))
92354
92473
  return candidate;
92355
92474
  }
92356
- return join15(process.cwd(), "dashboard", "dist");
92475
+ return join16(process.cwd(), "dashboard", "dist");
92357
92476
  }
92358
92477
  function getProvidedApiKey(req) {
92359
92478
  const headerKey = req.headers.get("x-api-key");