@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.
- package/dist/cli/cloud-router.d.ts +65 -1
- package/dist/cli/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/agent-commands.d.ts.map +1 -1
- package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
- package/dist/cli/commands/project-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +353 -25
- package/dist/index.js +113 -4
- package/dist/mcp/index.js +234 -5
- package/dist/mcp/tools/agents.d.ts.map +1 -1
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +252 -23
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/interfaces.d.ts +52 -1
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage.js +110 -1
- package/package.json +1 -1
|
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAChG,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
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,
|
|
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;
|
|
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,
|
|
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;
|
|
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
|
-
|
|
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
|
|
32793
|
-
const result =
|
|
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
|
|
39875
|
-
|
|
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
|
-
|
|
71267
|
-
|
|
71268
|
-
|
|
71269
|
-
|
|
71270
|
-
|
|
71271
|
-
|
|
71272
|
-
|
|
71273
|
-
|
|
71274
|
-
|
|
71275
|
-
|
|
71276
|
-
|
|
71277
|
-
|
|
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
|
|