@hasna/todos 0.11.82 → 0.11.84
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 +190 -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/query-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +754 -84
- package/dist/index.js +114 -4
- package/dist/mcp/index.js +271 -5
- package/dist/mcp/tools/agents.d.ts.map +1 -1
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +289 -23
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/interfaces.d.ts +58 -1
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage.js +111 -1
- package/package.json +1 -1
package/dist/storage.js
CHANGED
|
@@ -11907,7 +11907,19 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
11907
11907
|
claimNext: (agentId, filters) => claimNextTask2(agentId, filters, store),
|
|
11908
11908
|
getNext: (_agentId, filters) => getNextTask2(filters, store),
|
|
11909
11909
|
getActiveWork: (filters) => getActiveWork2(filters, store),
|
|
11910
|
-
getChangedSince: (since, filters) => getChangedSince(since, filters, store)
|
|
11910
|
+
getChangedSince: (since, filters) => getChangedSince(since, filters, store),
|
|
11911
|
+
lock: (id, agentId) => lockTask2(id, agentId, store),
|
|
11912
|
+
unlock: (id, agentId) => unlockTask2(id, agentId, store)
|
|
11913
|
+
},
|
|
11914
|
+
dependencies: {
|
|
11915
|
+
add: (taskId, dependsOn, context) => addDependency2(taskId, dependsOn, store, context),
|
|
11916
|
+
remove: (taskId, dependsOn) => removeDependency2(taskId, dependsOn, store),
|
|
11917
|
+
list: (taskId) => listDependencies(taskId, store),
|
|
11918
|
+
listAll: () => store.list("dependencies")
|
|
11919
|
+
},
|
|
11920
|
+
verifications: {
|
|
11921
|
+
add: (input, context) => addVerification(input, store, context),
|
|
11922
|
+
list: (taskId) => listVerifications(taskId, store)
|
|
11911
11923
|
},
|
|
11912
11924
|
projects: {
|
|
11913
11925
|
create: (input, context) => createProject2(input, store, context),
|
|
@@ -12362,6 +12374,104 @@ async function patchTask(task, patch, store) {
|
|
|
12362
12374
|
await store.upsert("tasks", updated);
|
|
12363
12375
|
return updated;
|
|
12364
12376
|
}
|
|
12377
|
+
var CLOUD_LOCK_EXPIRY_MINUTES = 30;
|
|
12378
|
+
function cloudLockExpired(lockedAt) {
|
|
12379
|
+
if (!lockedAt)
|
|
12380
|
+
return true;
|
|
12381
|
+
return new Date(lockedAt).getTime() + CLOUD_LOCK_EXPIRY_MINUTES * 60 * 1000 < Date.now();
|
|
12382
|
+
}
|
|
12383
|
+
function cloudLockExpiresAt(lockedAt) {
|
|
12384
|
+
return new Date(new Date(lockedAt).getTime() + CLOUD_LOCK_EXPIRY_MINUTES * 60 * 1000).toISOString();
|
|
12385
|
+
}
|
|
12386
|
+
async function lockTask2(id, agentId, store) {
|
|
12387
|
+
const task = await requireRecord("tasks", id, store);
|
|
12388
|
+
if (task.status === "completed" || task.status === "cancelled") {
|
|
12389
|
+
return { success: false, error: `Task is ${task.status} and cannot be locked` };
|
|
12390
|
+
}
|
|
12391
|
+
if (task.locked_by && task.locked_by !== agentId && !cloudLockExpired(task.locked_at)) {
|
|
12392
|
+
return { success: false, locked_by: task.locked_by, locked_at: task.locked_at ?? undefined, error: `Task is locked by ${task.locked_by}` };
|
|
12393
|
+
}
|
|
12394
|
+
const timestamp = new Date().toISOString();
|
|
12395
|
+
await patchTask(task, { locked_by: agentId, locked_at: timestamp }, store);
|
|
12396
|
+
return { success: true, locked_by: agentId, locked_at: timestamp, expires_at: cloudLockExpiresAt(timestamp) };
|
|
12397
|
+
}
|
|
12398
|
+
async function unlockTask2(id, agentId, store) {
|
|
12399
|
+
const task = await requireRecord("tasks", id, store);
|
|
12400
|
+
if (agentId && task.locked_by && task.locked_by !== agentId) {
|
|
12401
|
+
throw new Error(`Task ${id} is locked by ${task.locked_by}, not ${agentId}`);
|
|
12402
|
+
}
|
|
12403
|
+
await patchTask(task, { locked_by: null, locked_at: null }, store);
|
|
12404
|
+
return true;
|
|
12405
|
+
}
|
|
12406
|
+
function dependencyId(taskId, dependsOn) {
|
|
12407
|
+
return `${taskId}::${dependsOn}`;
|
|
12408
|
+
}
|
|
12409
|
+
async function addDependency2(taskId, dependsOn, store, context) {
|
|
12410
|
+
if (taskId === dependsOn)
|
|
12411
|
+
throw new Error("A task cannot depend on itself");
|
|
12412
|
+
if (!await store.get("tasks", taskId))
|
|
12413
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
12414
|
+
if (!await store.get("tasks", dependsOn))
|
|
12415
|
+
throw new Error(`Task not found: ${dependsOn}`);
|
|
12416
|
+
const edges = await store.list("dependencies");
|
|
12417
|
+
const adjacency = new Map;
|
|
12418
|
+
for (const edge of edges) {
|
|
12419
|
+
if (!adjacency.has(edge.task_id))
|
|
12420
|
+
adjacency.set(edge.task_id, []);
|
|
12421
|
+
adjacency.get(edge.task_id).push(edge.depends_on);
|
|
12422
|
+
}
|
|
12423
|
+
const queue = [dependsOn];
|
|
12424
|
+
const seen = new Set;
|
|
12425
|
+
while (queue.length) {
|
|
12426
|
+
const node = queue.shift();
|
|
12427
|
+
if (node === taskId)
|
|
12428
|
+
throw new Error(`Adding dependency ${taskId} -> ${dependsOn} would create a cycle`);
|
|
12429
|
+
if (seen.has(node))
|
|
12430
|
+
continue;
|
|
12431
|
+
seen.add(node);
|
|
12432
|
+
for (const next of adjacency.get(node) ?? [])
|
|
12433
|
+
queue.push(next);
|
|
12434
|
+
}
|
|
12435
|
+
const timestamp = new Date().toISOString();
|
|
12436
|
+
const record = { id: dependencyId(taskId, dependsOn), task_id: taskId, depends_on: dependsOn, created_at: timestamp, updated_at: timestamp };
|
|
12437
|
+
await store.upsert("dependencies", record, context);
|
|
12438
|
+
return { task_id: taskId, depends_on: dependsOn };
|
|
12439
|
+
}
|
|
12440
|
+
async function removeDependency2(taskId, dependsOn, store) {
|
|
12441
|
+
const existing = await store.get("dependencies", dependencyId(taskId, dependsOn));
|
|
12442
|
+
if (!existing)
|
|
12443
|
+
return false;
|
|
12444
|
+
await store.delete("dependencies", dependencyId(taskId, dependsOn));
|
|
12445
|
+
return true;
|
|
12446
|
+
}
|
|
12447
|
+
async function listDependencies(taskId, store) {
|
|
12448
|
+
const edges = await store.list("dependencies");
|
|
12449
|
+
return {
|
|
12450
|
+
dependencies: edges.filter((edge) => edge.task_id === taskId).map((edge) => ({ task_id: edge.task_id, depends_on: edge.depends_on })),
|
|
12451
|
+
blocked_by: edges.filter((edge) => edge.depends_on === taskId).map((edge) => ({ task_id: edge.task_id, depends_on: edge.depends_on }))
|
|
12452
|
+
};
|
|
12453
|
+
}
|
|
12454
|
+
async function addVerification(input, store, context) {
|
|
12455
|
+
if (!await store.get("tasks", input.task_id))
|
|
12456
|
+
throw new Error(`Task not found: ${input.task_id}`);
|
|
12457
|
+
const timestamp = new Date().toISOString();
|
|
12458
|
+
const verification = {
|
|
12459
|
+
id: randomUUID3(),
|
|
12460
|
+
task_id: input.task_id,
|
|
12461
|
+
command: input.command,
|
|
12462
|
+
status: input.status ?? "unknown",
|
|
12463
|
+
output_summary: input.output_summary ?? null,
|
|
12464
|
+
artifact_path: input.artifact_path ?? null,
|
|
12465
|
+
agent_id: input.agent_id ?? context?.agentId ?? null,
|
|
12466
|
+
run_at: timestamp,
|
|
12467
|
+
created_at: timestamp
|
|
12468
|
+
};
|
|
12469
|
+
await store.upsert("verifications", { ...verification, updated_at: timestamp }, context);
|
|
12470
|
+
return verification;
|
|
12471
|
+
}
|
|
12472
|
+
async function listVerifications(taskId, store) {
|
|
12473
|
+
return (await store.list("verifications")).filter((verification) => verification.task_id === taskId).sort((a, b) => b.run_at.localeCompare(a.run_at));
|
|
12474
|
+
}
|
|
12365
12475
|
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";
|
|
12366
12476
|
function toFilterArray(value) {
|
|
12367
12477
|
return Array.isArray(value) ? value : [value];
|