@hasna/todos 0.11.82 → 0.11.83

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.
@@ -21,7 +21,7 @@
21
21
  * subnet routing — pure API-client path per the locked architecture.
22
22
  */
23
23
  import { type HasnaStorageClient } from "@hasna/contracts/client/storage";
24
- import type { Agent, Plan, Project, Task, TaskComment, TaskFilter } from "../types/index.js";
24
+ import type { Agent, Plan, Project, RegisterAgentInput, Task, TaskComment, TaskDependency, TaskFilter } from "../types/index.js";
25
25
  type Env = Record<string, string | undefined>;
26
26
  /**
27
27
  * Resolve the todos cloud storage client from the environment. Returns a ready
@@ -86,5 +86,69 @@ export declare function cloudAddComment(client: HasnaStorageClient, taskId: stri
86
86
  * client (which previously loaded every matching task over HTTP just to count).
87
87
  */
88
88
  export declare function cloudCountTasks(client: HasnaStorageClient, filter?: TaskFilter): Promise<number>;
89
+ /**
90
+ * Register (or renew) an agent in the shared cloud roster (`POST /v1/agents`).
91
+ * This is the fix for the agent-identity misroute: `todos init` and the MCP
92
+ * `register_agent` tool historically wrote the agent to LOCAL sqlite even on a
93
+ * flipped machine, so the cloud `/v1/agents` roster never saw it. Routing through
94
+ * here writes the agent to the shared dataset with the bearer key. A name that is
95
+ * already actively held by another session comes back as HTTP 409, which the
96
+ * transport throws — surfaced to the caller as a conflict error (parity with the
97
+ * local conflict path) rather than a silent duplicate.
98
+ */
99
+ export declare function cloudRegisterAgent(client: HasnaStorageClient, input: RegisterAgentInput): Promise<Agent>;
100
+ /** Result of a cloud lock/unlock action (mirrors the local `LockResult` shape). */
101
+ export interface CloudLockResult {
102
+ success: boolean;
103
+ locked_by?: string;
104
+ locked_at?: string;
105
+ expires_at?: string;
106
+ error?: string;
107
+ }
108
+ /**
109
+ * Acquire an exclusive lock on a cloud task (`POST /v1/tasks/:id/lock`). Locking is
110
+ * a task-field operation (`locked_by`/`locked_at`) resolved server-side against the
111
+ * shared dataset so a flipped machine coordinates on the SAME lock as every other
112
+ * agent — the previous local-sqlite lookup 404'd cloud tasks ("Task not found").
113
+ */
114
+ export declare function cloudLockTask(client: HasnaStorageClient, id: string, agentId: string): Promise<CloudLockResult>;
115
+ /** Release a lock on a cloud task (`POST /v1/tasks/:id/unlock`). */
116
+ export declare function cloudUnlockTask(client: HasnaStorageClient, id: string, agentId?: string): Promise<boolean>;
117
+ /** A task's dependency edges from the cloud (`GET /v1/tasks/:id/dependencies`). */
118
+ export interface CloudTaskDependencies {
119
+ dependencies: TaskDependency[];
120
+ blocked_by: TaskDependency[];
121
+ }
122
+ /** List a cloud task's dependency edges (`GET /v1/tasks/:id/dependencies`). */
123
+ export declare function cloudGetDependencies(client: HasnaStorageClient, id: string): Promise<CloudTaskDependencies>;
124
+ /** Add a dependency edge to a cloud task (`POST /v1/tasks/:id/dependencies`). */
125
+ export declare function cloudAddDependency(client: HasnaStorageClient, id: string, dependsOn: string): Promise<TaskDependency>;
126
+ /** Remove a dependency edge from a cloud task (`DELETE /v1/tasks/:id/dependencies/:dep`). */
127
+ export declare function cloudRemoveDependency(client: HasnaStorageClient, id: string, dependsOn: string): Promise<boolean>;
128
+ /** A verification record returned by the cloud. */
129
+ export interface CloudTaskVerification {
130
+ id: string;
131
+ task_id: string;
132
+ command: string;
133
+ status: "passed" | "failed" | "unknown";
134
+ output_summary: string | null;
135
+ artifact_path: string | null;
136
+ agent_id: string | null;
137
+ run_at: string;
138
+ created_at: string;
139
+ }
140
+ /**
141
+ * Record a verification command + result against a cloud task
142
+ * (`POST /v1/tasks/:id/verifications`). The previous local path wrote the row to
143
+ * this machine's sqlite where the cloud task does not exist, tripping a FOREIGN
144
+ * KEY constraint; routing to the shared store attaches it to the real task.
145
+ */
146
+ export declare function cloudRecordVerification(client: HasnaStorageClient, id: string, input: {
147
+ command: string;
148
+ status?: string;
149
+ output_summary?: string;
150
+ artifact_path?: string;
151
+ agent_id?: string;
152
+ }): Promise<CloudTaskVerification>;
89
153
  export {};
90
154
  //# sourceMappingURL=cloud-router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-router.d.ts","sourceRoot":"","sources":["../../src/cli/cloud-router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cloud-router.d.ts","sourceRoot":"","sources":["../../src/cli/cloud-router.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"agent-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/agent-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QAmZrD"}
1
+ {"version":3,"file":"agent-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/agent-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QA0ZrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-hooks-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/mcp-hooks-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0MzC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,QAqpCxD"}
1
+ {"version":3,"file":"mcp-hooks-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/mcp-hooks-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2MzC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,QAmqCxD"}
@@ -1 +1 @@
1
- {"version":3,"file":"project-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/project-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqJzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QA05BvD"}
1
+ {"version":3,"file":"project-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/project-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqJzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAg8BvD"}
@@ -1 +1 @@
1
- {"version":3,"file":"task-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/task-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwMzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAglCpD"}
1
+ {"version":3,"file":"task-commands.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/task-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0MzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QAqlCpD"}
package/dist/cli/index.js CHANGED
@@ -8580,6 +8580,53 @@ async function cloudCountTasks(client, filter = {}) {
8580
8580
  const tasks = await cloudListTasks(client, rest);
8581
8581
  return tasks.length;
8582
8582
  }
8583
+ async function cloudRegisterAgent(client, input) {
8584
+ const raw = await client.transport.post("/agents", input);
8585
+ if (raw && typeof raw === "object" && "agent" in raw) {
8586
+ return raw.agent;
8587
+ }
8588
+ return raw;
8589
+ }
8590
+ async function cloudLockTask(client, id, agentId) {
8591
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/lock`, { agent_id: agentId });
8592
+ if (raw && typeof raw === "object" && "result" in raw) {
8593
+ return raw.result;
8594
+ }
8595
+ return raw ?? { success: true };
8596
+ }
8597
+ async function cloudUnlockTask(client, id, agentId) {
8598
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/unlock`, agentId ? { agent_id: agentId } : {});
8599
+ if (raw && typeof raw === "object" && "success" in raw) {
8600
+ return Boolean(raw.success);
8601
+ }
8602
+ return true;
8603
+ }
8604
+ async function cloudGetDependencies(client, id) {
8605
+ const raw = await client.transport.get(`/tasks/${encodeURIComponent(id)}/dependencies`);
8606
+ const env = raw ?? {};
8607
+ return { dependencies: env.dependencies ?? [], blocked_by: env.blocked_by ?? [] };
8608
+ }
8609
+ async function cloudAddDependency(client, id, dependsOn) {
8610
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/dependencies`, { depends_on: dependsOn });
8611
+ if (raw && typeof raw === "object" && "dependency" in raw) {
8612
+ return raw.dependency;
8613
+ }
8614
+ return raw;
8615
+ }
8616
+ async function cloudRemoveDependency(client, id, dependsOn) {
8617
+ const raw = await client.transport.del(`/tasks/${encodeURIComponent(id)}/dependencies/${encodeURIComponent(dependsOn)}`);
8618
+ if (raw && typeof raw === "object" && "removed" in raw) {
8619
+ return Boolean(raw.removed);
8620
+ }
8621
+ return true;
8622
+ }
8623
+ async function cloudRecordVerification(client, id, input) {
8624
+ const raw = await client.transport.post(`/tasks/${encodeURIComponent(id)}/verifications`, input);
8625
+ if (raw && typeof raw === "object" && "verification" in raw) {
8626
+ return raw.verification;
8627
+ }
8628
+ return raw;
8629
+ }
8583
8630
  var _cache;
8584
8631
  var init_cloud_router = __esm(() => {
8585
8632
  init_storage();
@@ -20886,13 +20933,14 @@ ${chalk2.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
20886
20933
  console.log(formatTaskLine(task2));
20887
20934
  }
20888
20935
  });
20889
- program2.command("lock <id>").description("Acquire exclusive lock on a task").action((id) => {
20936
+ program2.command("lock <id>").description("Acquire exclusive lock on a task").action(async (id) => {
20890
20937
  const globalOpts = program2.opts();
20891
20938
  const agentId = globalOpts.agent || "cli";
20939
+ const cloud = getTodosCloudClient();
20892
20940
  const resolvedId = resolveTaskId(id);
20893
20941
  let result;
20894
20942
  try {
20895
- result = lockTask(resolvedId, agentId);
20943
+ result = cloud ? await cloudLockTask(cloud, resolvedId, agentId) : lockTask(resolvedId, agentId);
20896
20944
  } catch (e) {
20897
20945
  handleError(e);
20898
20946
  }
@@ -20905,11 +20953,15 @@ ${chalk2.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
20905
20953
  process.exit(1);
20906
20954
  }
20907
20955
  });
20908
- program2.command("unlock <id>").description("Release lock on a task").action((id) => {
20956
+ program2.command("unlock <id>").description("Release lock on a task").action(async (id) => {
20909
20957
  const globalOpts = program2.opts();
20958
+ const cloud = getTodosCloudClient();
20910
20959
  const resolvedId = resolveTaskId(id);
20911
20960
  try {
20912
- unlockTask(resolvedId, globalOpts.agent);
20961
+ if (cloud)
20962
+ await cloudUnlockTask(cloud, resolvedId, globalOpts.agent);
20963
+ else
20964
+ unlockTask(resolvedId, globalOpts.agent);
20913
20965
  } catch (e) {
20914
20966
  handleError(e);
20915
20967
  }
@@ -31573,6 +31625,49 @@ function registerProjectCommands(program2) {
31573
31625
  });
31574
31626
  program2.command("deps <id>").description("Manage task dependencies").option("--needs <dep-id>", "Add dependency (this task needs dep-id)").option("--remove <dep-id>", "Remove dependency").option("--graph", "Show the dependency graph instead of direct edges").option("--direction <direction>", "Graph direction: up, down, or both", "both").action(async (id, opts) => {
31575
31627
  const globalOpts = program2.opts();
31628
+ const cloud = getTodosCloudClient();
31629
+ if (cloud) {
31630
+ const cloudId = resolveTaskId(id);
31631
+ if (opts.needs) {
31632
+ try {
31633
+ const dep = await cloudAddDependency(cloud, cloudId, resolveTaskId(opts.needs));
31634
+ if (globalOpts.json)
31635
+ output(dep, true);
31636
+ else
31637
+ console.log(chalk4.green("Dependency added."));
31638
+ } catch (e) {
31639
+ handleError(e);
31640
+ }
31641
+ return;
31642
+ }
31643
+ if (opts.remove) {
31644
+ const removed = await cloudRemoveDependency(cloud, cloudId, resolveTaskId(opts.remove));
31645
+ if (globalOpts.json)
31646
+ output({ removed }, true);
31647
+ else
31648
+ console.log(removed ? chalk4.green("Dependency removed.") : chalk4.red("Dependency not found."));
31649
+ return;
31650
+ }
31651
+ const edges = await cloudGetDependencies(cloud, cloudId);
31652
+ if (globalOpts.json) {
31653
+ output(edges, true);
31654
+ return;
31655
+ }
31656
+ if (edges.dependencies.length > 0) {
31657
+ console.log(chalk4.bold("Depends on:"));
31658
+ for (const dep of edges.dependencies)
31659
+ console.log(` ${chalk4.cyan(dep.depends_on)}`);
31660
+ }
31661
+ if (edges.blocked_by.length > 0) {
31662
+ console.log(chalk4.bold("Blocks:"));
31663
+ for (const b of edges.blocked_by)
31664
+ console.log(` ${chalk4.cyan(b.task_id)}`);
31665
+ }
31666
+ if (edges.dependencies.length === 0 && edges.blocked_by.length === 0) {
31667
+ console.log(chalk4.dim("No dependencies."));
31668
+ }
31669
+ return;
31670
+ }
31576
31671
  const { addDependency: addDependency2, removeDependency: removeDependency2, getTaskGraph: getTaskGraph2, getTaskWithRelations: getTaskWithRelations2 } = await Promise.resolve().then(() => (init_tasks(), exports_tasks));
31577
31672
  const resolvedId = resolveTaskId(id);
31578
31673
  if (opts.needs) {
@@ -32789,8 +32884,9 @@ function registerAgentCommands(program2) {
32789
32884
  program2.command("init <name>").description("Register an agents and get a short UUID").option("-d, --description <text>", "Agent description").action(async (name, opts) => {
32790
32885
  const globalOpts = program2.opts();
32791
32886
  try {
32792
- const { registerAgent: registerAgent2, isAgentConflict: isAgentConflict2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
32793
- const result = registerAgent2({ name, description: opts.description });
32887
+ const cloud = getTodosCloudClient();
32888
+ const result = cloud ? await cloudRegisterAgent(cloud, { name, description: opts.description }) : (await Promise.resolve().then(() => (init_agents(), exports_agents))).registerAgent({ name, description: opts.description });
32889
+ const { isAgentConflict: isAgentConflict2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
32794
32890
  if (isAgentConflict2(result)) {
32795
32891
  console.error(chalk5.red("CONFLICT:"), result.message);
32796
32892
  process.exit(1);
@@ -38364,7 +38460,18 @@ function createPostgresTodosStorageAdapter(options) {
38364
38460
  claimNext: (agentId, filters) => claimNextTask2(agentId, filters, store),
38365
38461
  getNext: (_agentId, filters) => getNextTask2(filters, store),
38366
38462
  getActiveWork: (filters) => getActiveWork2(filters, store),
38367
- getChangedSince: (since, filters) => getChangedSince(since, filters, store)
38463
+ getChangedSince: (since, filters) => getChangedSince(since, filters, store),
38464
+ lock: (id, agentId) => lockTask2(id, agentId, store),
38465
+ unlock: (id, agentId) => unlockTask2(id, agentId, store)
38466
+ },
38467
+ dependencies: {
38468
+ add: (taskId, dependsOn, context) => addDependency2(taskId, dependsOn, store, context),
38469
+ remove: (taskId, dependsOn) => removeDependency2(taskId, dependsOn, store),
38470
+ list: (taskId) => listDependencies(taskId, store)
38471
+ },
38472
+ verifications: {
38473
+ add: (input, context) => addVerification(input, store, context),
38474
+ list: (taskId) => listVerifications(taskId, store)
38368
38475
  },
38369
38476
  projects: {
38370
38477
  create: (input, context) => createProject2(input, store, context),
@@ -38819,6 +38926,103 @@ async function patchTask(task, patch, store) {
38819
38926
  await store.upsert("tasks", updated);
38820
38927
  return updated;
38821
38928
  }
38929
+ function cloudLockExpired(lockedAt) {
38930
+ if (!lockedAt)
38931
+ return true;
38932
+ return new Date(lockedAt).getTime() + CLOUD_LOCK_EXPIRY_MINUTES * 60 * 1000 < Date.now();
38933
+ }
38934
+ function cloudLockExpiresAt(lockedAt) {
38935
+ return new Date(new Date(lockedAt).getTime() + CLOUD_LOCK_EXPIRY_MINUTES * 60 * 1000).toISOString();
38936
+ }
38937
+ async function lockTask2(id, agentId, store) {
38938
+ const task = await requireRecord("tasks", id, store);
38939
+ if (task.status === "completed" || task.status === "cancelled") {
38940
+ return { success: false, error: `Task is ${task.status} and cannot be locked` };
38941
+ }
38942
+ if (task.locked_by && task.locked_by !== agentId && !cloudLockExpired(task.locked_at)) {
38943
+ return { success: false, locked_by: task.locked_by, locked_at: task.locked_at ?? undefined, error: `Task is locked by ${task.locked_by}` };
38944
+ }
38945
+ const timestamp = new Date().toISOString();
38946
+ await patchTask(task, { locked_by: agentId, locked_at: timestamp }, store);
38947
+ return { success: true, locked_by: agentId, locked_at: timestamp, expires_at: cloudLockExpiresAt(timestamp) };
38948
+ }
38949
+ async function unlockTask2(id, agentId, store) {
38950
+ const task = await requireRecord("tasks", id, store);
38951
+ if (agentId && task.locked_by && task.locked_by !== agentId) {
38952
+ throw new Error(`Task ${id} is locked by ${task.locked_by}, not ${agentId}`);
38953
+ }
38954
+ await patchTask(task, { locked_by: null, locked_at: null }, store);
38955
+ return true;
38956
+ }
38957
+ function dependencyId(taskId, dependsOn) {
38958
+ return `${taskId}::${dependsOn}`;
38959
+ }
38960
+ async function addDependency2(taskId, dependsOn, store, context) {
38961
+ if (taskId === dependsOn)
38962
+ throw new Error("A task cannot depend on itself");
38963
+ if (!await store.get("tasks", taskId))
38964
+ throw new Error(`Task not found: ${taskId}`);
38965
+ if (!await store.get("tasks", dependsOn))
38966
+ throw new Error(`Task not found: ${dependsOn}`);
38967
+ const edges = await store.list("dependencies");
38968
+ const adjacency = new Map;
38969
+ for (const edge of edges) {
38970
+ if (!adjacency.has(edge.task_id))
38971
+ adjacency.set(edge.task_id, []);
38972
+ adjacency.get(edge.task_id).push(edge.depends_on);
38973
+ }
38974
+ const queue = [dependsOn];
38975
+ const seen = new Set;
38976
+ while (queue.length) {
38977
+ const node = queue.shift();
38978
+ if (node === taskId)
38979
+ throw new Error(`Adding dependency ${taskId} -> ${dependsOn} would create a cycle`);
38980
+ if (seen.has(node))
38981
+ continue;
38982
+ seen.add(node);
38983
+ for (const next of adjacency.get(node) ?? [])
38984
+ queue.push(next);
38985
+ }
38986
+ const timestamp = new Date().toISOString();
38987
+ const record = { id: dependencyId(taskId, dependsOn), task_id: taskId, depends_on: dependsOn, created_at: timestamp, updated_at: timestamp };
38988
+ await store.upsert("dependencies", record, context);
38989
+ return { task_id: taskId, depends_on: dependsOn };
38990
+ }
38991
+ async function removeDependency2(taskId, dependsOn, store) {
38992
+ const existing = await store.get("dependencies", dependencyId(taskId, dependsOn));
38993
+ if (!existing)
38994
+ return false;
38995
+ await store.delete("dependencies", dependencyId(taskId, dependsOn));
38996
+ return true;
38997
+ }
38998
+ async function listDependencies(taskId, store) {
38999
+ const edges = await store.list("dependencies");
39000
+ return {
39001
+ dependencies: edges.filter((edge) => edge.task_id === taskId).map((edge) => ({ task_id: edge.task_id, depends_on: edge.depends_on })),
39002
+ blocked_by: edges.filter((edge) => edge.depends_on === taskId).map((edge) => ({ task_id: edge.task_id, depends_on: edge.depends_on }))
39003
+ };
39004
+ }
39005
+ async function addVerification(input, store, context) {
39006
+ if (!await store.get("tasks", input.task_id))
39007
+ throw new Error(`Task not found: ${input.task_id}`);
39008
+ const timestamp = new Date().toISOString();
39009
+ const verification = {
39010
+ id: randomUUID3(),
39011
+ task_id: input.task_id,
39012
+ command: input.command,
39013
+ status: input.status ?? "unknown",
39014
+ output_summary: input.output_summary ?? null,
39015
+ artifact_path: input.artifact_path ?? null,
39016
+ agent_id: input.agent_id ?? context?.agentId ?? null,
39017
+ run_at: timestamp,
39018
+ created_at: timestamp
39019
+ };
39020
+ await store.upsert("verifications", { ...verification, updated_at: timestamp }, context);
39021
+ return verification;
39022
+ }
39023
+ async function listVerifications(taskId, store) {
39024
+ return (await store.list("verifications")).filter((verification) => verification.task_id === taskId).sort((a, b) => b.run_at.localeCompare(a.run_at));
39025
+ }
38822
39026
  function toFilterArray(value) {
38823
39027
  return Array.isArray(value) ? value : [value];
38824
39028
  }
@@ -39192,7 +39396,7 @@ function compareClock(left, right) {
39192
39396
  function numberValue2(value) {
39193
39397
  return typeof value === "number" && Number.isSafeInteger(value) ? value : null;
39194
39398
  }
39195
- var TASK_ORDER_BY = "ORDER BY CASE payload->>'priority' WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC, payload->>'created_at' ASC, payload->>'id' ASC";
39399
+ var CLOUD_LOCK_EXPIRY_MINUTES = 30, TASK_ORDER_BY = "ORDER BY CASE payload->>'priority' WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC, payload->>'created_at' ASC, payload->>'id' ASC";
39196
39400
  var init_postgres_adapter = () => {};
39197
39401
 
39198
39402
  // src/server/cloud.ts
@@ -39690,6 +39894,7 @@ async function handleV1Request(req, url) {
39690
39894
  const resource = segments[1];
39691
39895
  const id = segments[2];
39692
39896
  const action = segments[3];
39897
+ const subId = segments[4];
39693
39898
  try {
39694
39899
  if (resource === "tasks") {
39695
39900
  if (id === "exists" && !action) {
@@ -39765,6 +39970,89 @@ async function handleV1Request(req, url) {
39765
39970
  }
39766
39971
  return error(405, `method ${method} not allowed on /v1/tasks/:id/comments`);
39767
39972
  }
39973
+ if (action === "lock" || action === "unlock") {
39974
+ if (method !== "POST")
39975
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/${action}`);
39976
+ if (typeof store.tasks.lock !== "function" || typeof store.tasks.unlock !== "function") {
39977
+ return error(501, "task locking is not supported by this storage backend");
39978
+ }
39979
+ const body2 = await readJson(req) ?? {};
39980
+ if (!await store.tasks.get(id))
39981
+ return error(404, "task not found");
39982
+ if (action === "lock") {
39983
+ const agentId2 = body2.agent_id || principal.agent || "todos-serve";
39984
+ return json2({ result: await store.tasks.lock(id, agentId2) });
39985
+ }
39986
+ const released = await store.tasks.unlock(id, body2.agent_id || principal.agent || undefined);
39987
+ return json2({ success: released });
39988
+ }
39989
+ if (action === "dependencies") {
39990
+ if (!store.dependencies)
39991
+ return error(501, "dependencies are not supported by this storage backend");
39992
+ if (method === "GET") {
39993
+ if (!await store.tasks.get(id))
39994
+ return error(404, "task not found");
39995
+ const edges = await store.dependencies.list(id);
39996
+ return json2(edges);
39997
+ }
39998
+ if (method === "POST") {
39999
+ const body2 = await readJson(req) ?? {};
40000
+ if (typeof body2.depends_on !== "string" || !body2.depends_on.trim()) {
40001
+ return error(400, "depends_on is required");
40002
+ }
40003
+ try {
40004
+ const dependency = await store.dependencies.add(id, body2.depends_on, contextFromPrincipal(principal));
40005
+ return json2({ dependency }, 201);
40006
+ } catch (e) {
40007
+ const msg = e.message || "";
40008
+ if (msg.includes("not found"))
40009
+ return error(404, msg);
40010
+ if (msg.includes("cycle") || msg.includes("itself"))
40011
+ return error(409, msg);
40012
+ throw e;
40013
+ }
40014
+ }
40015
+ if (method === "DELETE") {
40016
+ if (!subId)
40017
+ return error(400, "dependency target id is required (/v1/tasks/:id/dependencies/:dep)");
40018
+ const removed = await store.dependencies.remove(id, subId);
40019
+ return json2({ removed });
40020
+ }
40021
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/dependencies`);
40022
+ }
40023
+ if (action === "verifications") {
40024
+ if (!store.verifications)
40025
+ return error(501, "verifications are not supported by this storage backend");
40026
+ if (method === "GET") {
40027
+ if (!await store.tasks.get(id))
40028
+ return error(404, "task not found");
40029
+ const verifications = await store.verifications.list(id);
40030
+ return json2({ verifications, count: verifications.length });
40031
+ }
40032
+ if (method === "POST") {
40033
+ const body2 = await readJson(req) ?? {};
40034
+ if (typeof body2.command !== "string" || !body2.command.trim()) {
40035
+ return error(400, "command is required");
40036
+ }
40037
+ try {
40038
+ const verification = await store.verifications.add({
40039
+ task_id: id,
40040
+ command: body2.command,
40041
+ status: body2.status,
40042
+ output_summary: body2.output_summary,
40043
+ artifact_path: body2.artifact_path,
40044
+ agent_id: body2.agent_id
40045
+ }, contextFromPrincipal(principal, body2));
40046
+ return json2({ verification }, 201);
40047
+ } catch (e) {
40048
+ const msg = e.message || "";
40049
+ if (msg.includes("not found"))
40050
+ return error(404, msg);
40051
+ throw e;
40052
+ }
40053
+ }
40054
+ return error(405, `method ${method} not allowed on /v1/tasks/:id/verifications`);
40055
+ }
39768
40056
  const body = await readJson(req) ?? {};
39769
40057
  const agentId = body.agent_id || principal.agent || "todos-serve";
39770
40058
  if (action === "start" && method === "POST") {
@@ -39869,10 +40157,13 @@ async function handleV1Request(req, url) {
39869
40157
  }
39870
40158
  if (!id && method === "POST") {
39871
40159
  const body = await readJson(req);
39872
- if (!body || typeof body.name !== "string")
40160
+ if (!body || typeof body.name !== "string" || !body.name.trim())
39873
40161
  return error(400, "name is required");
39874
- const agent = await store.agents.register(body, contextFromPrincipal(principal));
39875
- return json2({ agent }, 201);
40162
+ const result = await store.agents.register(body, contextFromPrincipal(principal));
40163
+ if (result && typeof result === "object" && "conflict" in result) {
40164
+ return error(409, result.message ?? "agent name conflict", { conflict: true });
40165
+ }
40166
+ return json2({ agent: result }, 201);
39876
40167
  }
39877
40168
  if (id && method === "GET") {
39878
40169
  const agent = await store.agents.get(id);
@@ -62399,6 +62690,33 @@ function registerAgentTools(server, { shouldRegisterTool, resolveId, formatError
62399
62690
  force: exports_external3.boolean().optional().describe("Force takeover of an active agent's name. Use with caution \u2014 only when you know the previous session is dead.")
62400
62691
  }, async ({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force }) => {
62401
62692
  try {
62693
+ const cloud = getTodosCloudClient();
62694
+ if (cloud) {
62695
+ const agent2 = await cloudRegisterAgent(cloud, {
62696
+ name,
62697
+ description,
62698
+ role,
62699
+ title,
62700
+ level,
62701
+ permissions,
62702
+ capabilities,
62703
+ session_id,
62704
+ working_dir,
62705
+ force
62706
+ });
62707
+ return {
62708
+ content: [{
62709
+ type: "text",
62710
+ text: `Agent registered:
62711
+ ID: ${agent2.id}
62712
+ Name: ${agent2.name}${agent2.description ? `
62713
+ Description: ${agent2.description}` : ""}
62714
+ Session: ${agent2.session_id ?? "unbound"}
62715
+ Created: ${agent2.created_at}
62716
+ Last seen: ${agent2.last_seen_at}`
62717
+ }]
62718
+ };
62719
+ }
62402
62720
  const pool = getAgentPoolForProject(working_dir);
62403
62721
  const result = registerAgent({ name, description, role, title, level, permissions, capabilities, session_id, working_dir, force, pool: pool || undefined });
62404
62722
  if (isAgentConflict(result)) {
@@ -71258,25 +71576,34 @@ Commands:`));
71258
71576
  });
71259
71577
  program2.command("record-verification <task-id> <command>").description("Record a verification command and result for a task").option("--status <status>", "Verification status: passed, failed, or unknown", "unknown").option("--summary <text>", "Short output summary").option("--artifact <path>", "Artifact or log path").option("--agent <name>", "Agent that ran the command").action(async (taskId, command, opts) => {
71260
71578
  const globalOpts = program2.opts();
71261
- const resolvedId = resolveTaskId(taskId);
71262
71579
  if (opts.status !== "passed" && opts.status !== "failed" && opts.status !== "unknown") {
71263
71580
  console.error(chalk8.red("--status must be passed, failed, or unknown"));
71264
71581
  process.exit(1);
71265
71582
  }
71266
- const { addTaskVerification: addTaskVerification2 } = await Promise.resolve().then(() => (init_task_commits(), exports_task_commits));
71267
- const verification = addTaskVerification2({
71268
- task_id: resolvedId,
71269
- command,
71270
- status: opts.status,
71271
- output_summary: opts.summary,
71272
- artifact_path: opts.artifact,
71273
- agent_id: opts.agent
71274
- });
71275
- if (globalOpts.json) {
71276
- output(verification, true);
71277
- return;
71583
+ try {
71584
+ const cloud = getTodosCloudClient();
71585
+ const verification = cloud ? await cloudRecordVerification(cloud, resolveTaskId(taskId), {
71586
+ command,
71587
+ status: opts.status,
71588
+ output_summary: opts.summary,
71589
+ artifact_path: opts.artifact,
71590
+ agent_id: opts.agent
71591
+ }) : (await Promise.resolve().then(() => (init_task_commits(), exports_task_commits))).addTaskVerification({
71592
+ task_id: resolveTaskId(taskId),
71593
+ command,
71594
+ status: opts.status,
71595
+ output_summary: opts.summary,
71596
+ artifact_path: opts.artifact,
71597
+ agent_id: opts.agent
71598
+ });
71599
+ if (globalOpts.json) {
71600
+ output(verification, true);
71601
+ return;
71602
+ }
71603
+ console.log(chalk8.green(`Recorded ${verification.status} verification for task ${taskId}`));
71604
+ } catch (e) {
71605
+ handleError(e);
71278
71606
  }
71279
- console.log(chalk8.green(`Recorded ${verification.status} verification for task ${taskId}`));
71280
71607
  });
71281
71608
  program2.command("trace <task-id>").description("Show local git refs, commits, changed files, and verification commands for a task").action(async (taskId) => {
71282
71609
  const globalOpts = program2.opts();
@@ -71982,6 +72309,7 @@ var HOME2;
71982
72309
  var init_mcp_hooks_commands = __esm(() => {
71983
72310
  init_tasks();
71984
72311
  init_helpers();
72312
+ init_cloud_router();
71985
72313
  HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
71986
72314
  });
71987
72315