@hasna/todos 0.11.65 → 0.11.67
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/README.md +78 -49
- package/dashboard/dist/assets/{index-aJefI7kh.js → index-DVotjwab.js} +1 -1
- package/dashboard/dist/index.html +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/storage-commands.d.ts +5 -0
- package/dist/cli/commands/storage-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/index.js +1034 -231
- package/dist/contracts.js +409 -129
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/db/audit.d.ts.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/plans.d.ts.map +1 -1
- package/dist/db/projects.d.ts.map +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/storage-tombstones.d.ts +26 -0
- package/dist/db/storage-tombstones.d.ts.map +1 -0
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/db/task-lifecycle.d.ts.map +1 -1
- package/dist/db/task-lists.d.ts.map +1 -1
- package/dist/db/task-runs.d.ts.map +1 -1
- package/dist/db/templates.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +928 -219
- package/dist/lib/approval-gates.d.ts.map +1 -1
- package/dist/lib/event-emission-safety.d.ts +9 -0
- package/dist/lib/event-emission-safety.d.ts.map +1 -0
- package/dist/lib/event-hooks.d.ts +1 -0
- package/dist/lib/event-hooks.d.ts.map +1 -1
- package/dist/lib/feature-manifest.d.ts.map +1 -1
- package/dist/lib/project-bootstrap.d.ts +1 -0
- package/dist/lib/project-bootstrap.d.ts.map +1 -1
- package/dist/lib/review-queues.d.ts.map +1 -1
- package/dist/lib/shared-events.d.ts +1 -0
- package/dist/lib/shared-events.d.ts.map +1 -1
- package/dist/lib/task-route-contract.d.ts +33 -0
- package/dist/lib/task-route-contract.d.ts.map +1 -0
- package/dist/lib/task-routing.d.ts +55 -0
- package/dist/lib/task-routing.d.ts.map +1 -0
- package/dist/mcp/index.js +459 -162
- package/dist/registry.js +409 -129
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +482 -185
- package/dist/storage/interfaces.d.ts +20 -0
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage/postgres-sync.d.ts +6 -1
- package/dist/storage/postgres-sync.d.ts.map +1 -1
- package/dist/storage/sqlite-snapshot.d.ts.map +1 -1
- package/dist/storage.js +677 -152
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -1232,6 +1232,22 @@ var init_migrations = __esm(() => {
|
|
|
1232
1232
|
CREATE INDEX IF NOT EXISTS idx_task_findings_source ON task_findings(source);
|
|
1233
1233
|
CREATE INDEX IF NOT EXISTS idx_task_findings_fingerprint ON task_findings(fingerprint);
|
|
1234
1234
|
INSERT OR IGNORE INTO _migrations (id) VALUES (62);
|
|
1235
|
+
`,
|
|
1236
|
+
`
|
|
1237
|
+
CREATE TABLE IF NOT EXISTS storage_tombstones (
|
|
1238
|
+
id TEXT PRIMARY KEY,
|
|
1239
|
+
object_type TEXT NOT NULL,
|
|
1240
|
+
object_id TEXT NOT NULL,
|
|
1241
|
+
deleted_at TEXT NOT NULL,
|
|
1242
|
+
updated_at TEXT NOT NULL,
|
|
1243
|
+
source_machine_id TEXT,
|
|
1244
|
+
payload TEXT,
|
|
1245
|
+
version INTEGER,
|
|
1246
|
+
UNIQUE(object_type, object_id)
|
|
1247
|
+
);
|
|
1248
|
+
CREATE INDEX IF NOT EXISTS idx_storage_tombstones_object ON storage_tombstones(object_type, object_id);
|
|
1249
|
+
CREATE INDEX IF NOT EXISTS idx_storage_tombstones_updated ON storage_tombstones(updated_at);
|
|
1250
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (63);
|
|
1235
1251
|
`
|
|
1236
1252
|
];
|
|
1237
1253
|
});
|
|
@@ -1753,6 +1769,20 @@ function ensureSchema(db) {
|
|
|
1753
1769
|
)`);
|
|
1754
1770
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_machine_paths_project ON project_machine_paths(project_id)");
|
|
1755
1771
|
ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_machine_paths_machine ON project_machine_paths(machine_id)");
|
|
1772
|
+
ensureTable("storage_tombstones", `
|
|
1773
|
+
CREATE TABLE storage_tombstones (
|
|
1774
|
+
id TEXT PRIMARY KEY,
|
|
1775
|
+
object_type TEXT NOT NULL,
|
|
1776
|
+
object_id TEXT NOT NULL,
|
|
1777
|
+
deleted_at TEXT NOT NULL,
|
|
1778
|
+
updated_at TEXT NOT NULL,
|
|
1779
|
+
source_machine_id TEXT,
|
|
1780
|
+
payload TEXT,
|
|
1781
|
+
version INTEGER,
|
|
1782
|
+
UNIQUE(object_type, object_id)
|
|
1783
|
+
)`);
|
|
1784
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_storage_tombstones_object ON storage_tombstones(object_type, object_id)");
|
|
1785
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_storage_tombstones_updated ON storage_tombstones(updated_at)");
|
|
1756
1786
|
ensureTable("machines", `
|
|
1757
1787
|
CREATE TABLE machines (
|
|
1758
1788
|
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, hostname TEXT, platform TEXT,
|
|
@@ -3077,6 +3107,65 @@ var init_config = __esm(() => {
|
|
|
3077
3107
|
};
|
|
3078
3108
|
});
|
|
3079
3109
|
|
|
3110
|
+
// src/db/storage-tombstones.ts
|
|
3111
|
+
function recordStorageTombstone(input, db) {
|
|
3112
|
+
const d = db ?? getDatabase();
|
|
3113
|
+
const deletedAt = input.deleted_at ?? now();
|
|
3114
|
+
const machineId = input.source_machine_id ?? currentStorageMachineId(d);
|
|
3115
|
+
d.run(`INSERT INTO storage_tombstones (
|
|
3116
|
+
id, object_type, object_id, deleted_at, updated_at, source_machine_id, payload, version
|
|
3117
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3118
|
+
ON CONFLICT(object_type, object_id) DO UPDATE SET
|
|
3119
|
+
deleted_at = excluded.deleted_at,
|
|
3120
|
+
updated_at = excluded.updated_at,
|
|
3121
|
+
source_machine_id = excluded.source_machine_id,
|
|
3122
|
+
payload = excluded.payload,
|
|
3123
|
+
version = excluded.version
|
|
3124
|
+
WHERE storage_tombstones.updated_at IS NULL OR storage_tombstones.updated_at <= excluded.updated_at`, [
|
|
3125
|
+
uuid(),
|
|
3126
|
+
input.object_type,
|
|
3127
|
+
input.object_id,
|
|
3128
|
+
deletedAt,
|
|
3129
|
+
deletedAt,
|
|
3130
|
+
machineId,
|
|
3131
|
+
input.payload ? JSON.stringify(input.payload) : null,
|
|
3132
|
+
input.version ?? null
|
|
3133
|
+
]);
|
|
3134
|
+
return getStorageTombstone(input.object_type, input.object_id, d);
|
|
3135
|
+
}
|
|
3136
|
+
function getStorageTombstone(objectType, objectId, db) {
|
|
3137
|
+
const d = db ?? getDatabase();
|
|
3138
|
+
const row = d.query("SELECT * FROM storage_tombstones WHERE object_type = ? AND object_id = ?").get(objectType, objectId);
|
|
3139
|
+
return row ? rowToStorageTombstone(row) : null;
|
|
3140
|
+
}
|
|
3141
|
+
function rowToStorageTombstone(row) {
|
|
3142
|
+
return {
|
|
3143
|
+
...row,
|
|
3144
|
+
payload: parsePayload(row.payload)
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
function parsePayload(value) {
|
|
3148
|
+
if (!value)
|
|
3149
|
+
return null;
|
|
3150
|
+
try {
|
|
3151
|
+
const parsed = JSON.parse(value);
|
|
3152
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
3153
|
+
} catch {
|
|
3154
|
+
return null;
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
function currentStorageMachineId(db) {
|
|
3158
|
+
try {
|
|
3159
|
+
return getMachineId(db);
|
|
3160
|
+
} catch {
|
|
3161
|
+
return null;
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
var init_storage_tombstones = __esm(() => {
|
|
3165
|
+
init_database();
|
|
3166
|
+
init_machines();
|
|
3167
|
+
});
|
|
3168
|
+
|
|
3080
3169
|
// src/db/projects.ts
|
|
3081
3170
|
var exports_projects = {};
|
|
3082
3171
|
__export(exports_projects, {
|
|
@@ -3128,8 +3217,9 @@ function createProject(input, db) {
|
|
|
3128
3217
|
const timestamp = now();
|
|
3129
3218
|
const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
|
|
3130
3219
|
const taskPrefix = input.task_prefix || generatePrefix(input.name, d);
|
|
3131
|
-
|
|
3132
|
-
|
|
3220
|
+
const machineId = currentStorageMachineId(d);
|
|
3221
|
+
d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, created_at, updated_at, machine_id)
|
|
3222
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, timestamp, timestamp, machineId]);
|
|
3133
3223
|
return getProject(id, d);
|
|
3134
3224
|
}
|
|
3135
3225
|
function getProject(id, db) {
|
|
@@ -3208,6 +3298,14 @@ function renameProject(id, input, db) {
|
|
|
3208
3298
|
}
|
|
3209
3299
|
function deleteProject(id, db) {
|
|
3210
3300
|
const d = db || getDatabase();
|
|
3301
|
+
const project = getProject(id, d);
|
|
3302
|
+
if (!project)
|
|
3303
|
+
return false;
|
|
3304
|
+
recordStorageTombstone({
|
|
3305
|
+
object_type: "projects",
|
|
3306
|
+
object_id: id,
|
|
3307
|
+
payload: project
|
|
3308
|
+
}, d);
|
|
3211
3309
|
const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
3212
3310
|
return result.changes > 0;
|
|
3213
3311
|
}
|
|
@@ -3312,6 +3410,14 @@ function listMachineLocalPaths(projectId, db) {
|
|
|
3312
3410
|
function removeMachineLocalPath(projectId, machineId, db) {
|
|
3313
3411
|
const d = db || getDatabase();
|
|
3314
3412
|
const mid = machineId ?? getMachineId(d);
|
|
3413
|
+
const existing = d.query("SELECT * FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, mid);
|
|
3414
|
+
if (!existing)
|
|
3415
|
+
return false;
|
|
3416
|
+
recordStorageTombstone({
|
|
3417
|
+
object_type: "project_machine_paths",
|
|
3418
|
+
object_id: existing.id,
|
|
3419
|
+
payload: existing
|
|
3420
|
+
}, d);
|
|
3315
3421
|
const result = d.run("DELETE FROM project_machine_paths WHERE project_id = ? AND machine_id = ?", [projectId, mid]);
|
|
3316
3422
|
return result.changes > 0;
|
|
3317
3423
|
}
|
|
@@ -3319,6 +3425,7 @@ var init_projects = __esm(() => {
|
|
|
3319
3425
|
init_types();
|
|
3320
3426
|
init_database();
|
|
3321
3427
|
init_machines();
|
|
3428
|
+
init_storage_tombstones();
|
|
3322
3429
|
});
|
|
3323
3430
|
|
|
3324
3431
|
// src/lib/completion-guard.ts
|
|
@@ -3370,6 +3477,57 @@ var init_completion_guard = __esm(() => {
|
|
|
3370
3477
|
init_projects();
|
|
3371
3478
|
});
|
|
3372
3479
|
|
|
3480
|
+
// src/lib/event-emission-safety.ts
|
|
3481
|
+
import { tmpdir } from "os";
|
|
3482
|
+
import { resolve as resolve3, sep } from "path";
|
|
3483
|
+
function envFlag(name) {
|
|
3484
|
+
const value = process.env[name];
|
|
3485
|
+
return value === "1" || value === "true" || value === "yes";
|
|
3486
|
+
}
|
|
3487
|
+
function isUnder(parent, child) {
|
|
3488
|
+
const normalizedParent = resolve3(parent);
|
|
3489
|
+
const normalizedChild = resolve3(child);
|
|
3490
|
+
return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep}`);
|
|
3491
|
+
}
|
|
3492
|
+
function databasePathFromDatabase(db) {
|
|
3493
|
+
const filename = db?.filename;
|
|
3494
|
+
return typeof filename === "string" && filename.trim() ? filename : undefined;
|
|
3495
|
+
}
|
|
3496
|
+
function isEphemeralTodosDatabase(dbPath) {
|
|
3497
|
+
const resolvedPath = dbPath ?? getDatabasePath();
|
|
3498
|
+
if (resolvedPath === ":memory:" || resolvedPath.startsWith("file::memory:"))
|
|
3499
|
+
return true;
|
|
3500
|
+
return isUnder(tmpdir(), resolvedPath);
|
|
3501
|
+
}
|
|
3502
|
+
function hasExplicitSharedEventsStore() {
|
|
3503
|
+
return Boolean(process.env["HASNA_EVENTS_DIR"] || process.env["HASNA_EVENTS_HOME"]);
|
|
3504
|
+
}
|
|
3505
|
+
function usesIsolatedTodosHome() {
|
|
3506
|
+
return isUnder(tmpdir(), getTodosGlobalDir());
|
|
3507
|
+
}
|
|
3508
|
+
function shouldEmitSharedTaskEvents(dbPath) {
|
|
3509
|
+
if (envFlag("TODOS_DISABLE_SHARED_EVENTS"))
|
|
3510
|
+
return false;
|
|
3511
|
+
if (envFlag("TODOS_ALLOW_EPHEMERAL_SHARED_EVENTS"))
|
|
3512
|
+
return true;
|
|
3513
|
+
if (!isEphemeralTodosDatabase(dbPath))
|
|
3514
|
+
return true;
|
|
3515
|
+
return hasExplicitSharedEventsStore();
|
|
3516
|
+
}
|
|
3517
|
+
function shouldDeliverLocalLifecycleHooks(dbPath) {
|
|
3518
|
+
if (envFlag("TODOS_DISABLE_LOCAL_EVENT_HOOKS"))
|
|
3519
|
+
return false;
|
|
3520
|
+
if (envFlag("TODOS_ALLOW_EPHEMERAL_LOCAL_EVENT_HOOKS"))
|
|
3521
|
+
return true;
|
|
3522
|
+
if (!isEphemeralTodosDatabase(dbPath))
|
|
3523
|
+
return true;
|
|
3524
|
+
return usesIsolatedTodosHome();
|
|
3525
|
+
}
|
|
3526
|
+
var init_event_emission_safety = __esm(() => {
|
|
3527
|
+
init_database();
|
|
3528
|
+
init_sync_utils();
|
|
3529
|
+
});
|
|
3530
|
+
|
|
3373
3531
|
// src/lib/redaction.ts
|
|
3374
3532
|
function unique(values) {
|
|
3375
3533
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -3470,9 +3628,9 @@ var init_redaction = __esm(() => {
|
|
|
3470
3628
|
});
|
|
3471
3629
|
|
|
3472
3630
|
// src/lib/workspace-trust.ts
|
|
3473
|
-
import { relative, resolve as
|
|
3631
|
+
import { relative, resolve as resolve4 } from "path";
|
|
3474
3632
|
function normalizePath(path) {
|
|
3475
|
-
return
|
|
3633
|
+
return resolve4(path);
|
|
3476
3634
|
}
|
|
3477
3635
|
function unique2(values) {
|
|
3478
3636
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -3654,9 +3812,9 @@ var init_workspace_trust = __esm(() => {
|
|
|
3654
3812
|
});
|
|
3655
3813
|
|
|
3656
3814
|
// src/lib/runner-sandbox.ts
|
|
3657
|
-
import { relative as relative2, resolve as
|
|
3815
|
+
import { relative as relative2, resolve as resolve5 } from "path";
|
|
3658
3816
|
function normalizePath2(path) {
|
|
3659
|
-
return
|
|
3817
|
+
return resolve5(path);
|
|
3660
3818
|
}
|
|
3661
3819
|
function unique3(values) {
|
|
3662
3820
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -3846,7 +4004,7 @@ var init_runner_sandbox = __esm(() => {
|
|
|
3846
4004
|
// src/lib/event-hooks.ts
|
|
3847
4005
|
import { createHash as createHash2, randomUUID } from "crypto";
|
|
3848
4006
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
3849
|
-
import { dirname as dirname4, resolve as
|
|
4007
|
+
import { dirname as dirname4, resolve as resolve6 } from "path";
|
|
3850
4008
|
import { createConnection } from "net";
|
|
3851
4009
|
function safeName(name) {
|
|
3852
4010
|
const trimmed = name.trim();
|
|
@@ -3984,7 +4142,7 @@ async function deliverHook(hook, envelope) {
|
|
|
3984
4142
|
if (hook.target === "stdout") {
|
|
3985
4143
|
output = line.trim();
|
|
3986
4144
|
} else if (hook.target === "file") {
|
|
3987
|
-
const filePath =
|
|
4145
|
+
const filePath = resolve6(hook.file_path);
|
|
3988
4146
|
mkdirSync3(dirname4(filePath), { recursive: true });
|
|
3989
4147
|
appendFileSync(filePath, line);
|
|
3990
4148
|
} else if (hook.target === "socket") {
|
|
@@ -4059,6 +4217,8 @@ async function emitLocalEventHooks(input) {
|
|
|
4059
4217
|
return Promise.all(hooks.map((hook) => deliverHook(hook, envelope)));
|
|
4060
4218
|
}
|
|
4061
4219
|
function emitLocalEventHooksQuiet(input) {
|
|
4220
|
+
if (!shouldDeliverLocalLifecycleHooks(input.databasePath))
|
|
4221
|
+
return;
|
|
4062
4222
|
emitLocalEventHooks(input).catch(() => {});
|
|
4063
4223
|
}
|
|
4064
4224
|
async function testLocalEventHook(name, input) {
|
|
@@ -4072,6 +4232,7 @@ var init_event_hooks = __esm(() => {
|
|
|
4072
4232
|
init_redaction();
|
|
4073
4233
|
init_runner_sandbox();
|
|
4074
4234
|
init_config();
|
|
4235
|
+
init_event_emission_safety();
|
|
4075
4236
|
LOCAL_EVENT_TYPES = [
|
|
4076
4237
|
"task.created",
|
|
4077
4238
|
"task.assigned",
|
|
@@ -4404,7 +4565,7 @@ async function dispatchCommand(event, channel) {
|
|
|
4404
4565
|
HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
|
|
4405
4566
|
HASNA_EVENT_JSON: eventJson
|
|
4406
4567
|
};
|
|
4407
|
-
return new Promise((
|
|
4568
|
+
return new Promise((resolve7) => {
|
|
4408
4569
|
const child = spawn(channel.command.command, channel.command.args ?? [], {
|
|
4409
4570
|
cwd: channel.command.cwd,
|
|
4410
4571
|
env,
|
|
@@ -4422,7 +4583,7 @@ async function dispatchCommand(event, channel) {
|
|
|
4422
4583
|
});
|
|
4423
4584
|
child.on("error", (error) => {
|
|
4424
4585
|
clearTimeout(timeout);
|
|
4425
|
-
|
|
4586
|
+
resolve7({
|
|
4426
4587
|
attempt: 1,
|
|
4427
4588
|
status: "failed",
|
|
4428
4589
|
startedAt,
|
|
@@ -4435,7 +4596,7 @@ async function dispatchCommand(event, channel) {
|
|
|
4435
4596
|
child.on("close", (code, signal) => {
|
|
4436
4597
|
clearTimeout(timeout);
|
|
4437
4598
|
const success = code === 0;
|
|
4438
|
-
|
|
4599
|
+
resolve7({
|
|
4439
4600
|
attempt: 1,
|
|
4440
4601
|
status: success ? "success" : "failed",
|
|
4441
4602
|
startedAt,
|
|
@@ -4702,14 +4863,15 @@ function createTaskList(input, db) {
|
|
|
4702
4863
|
const id = uuid();
|
|
4703
4864
|
const timestamp = now();
|
|
4704
4865
|
const slug = input.slug || slugify(input.name);
|
|
4866
|
+
const machineId = currentStorageMachineId(d);
|
|
4705
4867
|
if (!input.project_id) {
|
|
4706
4868
|
const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
|
|
4707
4869
|
if (existing) {
|
|
4708
4870
|
throw new Error(`Standalone task list with slug "${slug}" already exists`);
|
|
4709
4871
|
}
|
|
4710
4872
|
}
|
|
4711
|
-
d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
|
|
4712
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
|
|
4873
|
+
d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at, machine_id)
|
|
4874
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp, machineId]);
|
|
4713
4875
|
return getTaskList(id, d);
|
|
4714
4876
|
}
|
|
4715
4877
|
function getTaskList(id, db) {
|
|
@@ -4759,6 +4921,14 @@ function updateTaskList(id, input, db) {
|
|
|
4759
4921
|
}
|
|
4760
4922
|
function deleteTaskList(id, db) {
|
|
4761
4923
|
const d = db || getDatabase();
|
|
4924
|
+
const list = getTaskList(id, d);
|
|
4925
|
+
if (!list)
|
|
4926
|
+
return false;
|
|
4927
|
+
recordStorageTombstone({
|
|
4928
|
+
object_type: "task_lists",
|
|
4929
|
+
object_id: id,
|
|
4930
|
+
payload: list
|
|
4931
|
+
}, d);
|
|
4762
4932
|
return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
|
|
4763
4933
|
}
|
|
4764
4934
|
function ensureTaskList(name, slug, projectId, db) {
|
|
@@ -4772,40 +4942,10 @@ var init_task_lists = __esm(() => {
|
|
|
4772
4942
|
init_types();
|
|
4773
4943
|
init_database();
|
|
4774
4944
|
init_projects();
|
|
4945
|
+
init_storage_tombstones();
|
|
4775
4946
|
});
|
|
4776
4947
|
|
|
4777
|
-
// src/lib/
|
|
4778
|
-
function taskEventData(task, extra = {}) {
|
|
4779
|
-
return {
|
|
4780
|
-
id: task.id,
|
|
4781
|
-
task_id: task.id,
|
|
4782
|
-
short_id: task.short_id,
|
|
4783
|
-
title: task.title,
|
|
4784
|
-
description: task.description,
|
|
4785
|
-
status: task.status,
|
|
4786
|
-
priority: task.priority,
|
|
4787
|
-
project_id: task.project_id,
|
|
4788
|
-
parent_id: task.parent_id,
|
|
4789
|
-
plan_id: task.plan_id,
|
|
4790
|
-
task_list_id: task.task_list_id,
|
|
4791
|
-
agent_id: task.agent_id,
|
|
4792
|
-
assigned_to: task.assigned_to,
|
|
4793
|
-
session_id: task.session_id,
|
|
4794
|
-
working_dir: task.working_dir,
|
|
4795
|
-
tags: task.tags,
|
|
4796
|
-
metadata: task.metadata,
|
|
4797
|
-
version: task.version,
|
|
4798
|
-
created_at: task.created_at,
|
|
4799
|
-
updated_at: task.updated_at,
|
|
4800
|
-
started_at: task.started_at,
|
|
4801
|
-
completed_at: task.completed_at,
|
|
4802
|
-
due_at: task.due_at,
|
|
4803
|
-
requires_approval: task.requires_approval,
|
|
4804
|
-
approved_by: task.approved_by,
|
|
4805
|
-
approved_at: task.approved_at,
|
|
4806
|
-
...extra
|
|
4807
|
-
};
|
|
4808
|
-
}
|
|
4948
|
+
// src/lib/task-route-contract.ts
|
|
4809
4949
|
function booleanField(value) {
|
|
4810
4950
|
if (typeof value === "boolean")
|
|
4811
4951
|
return value;
|
|
@@ -4827,21 +4967,31 @@ function booleanField(value) {
|
|
|
4827
4967
|
function objectField(value) {
|
|
4828
4968
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
4829
4969
|
}
|
|
4830
|
-
function
|
|
4970
|
+
function collectBooleans(records, keys) {
|
|
4971
|
+
const values = [];
|
|
4831
4972
|
for (const record of records) {
|
|
4973
|
+
if (!record)
|
|
4974
|
+
continue;
|
|
4832
4975
|
for (const key of keys) {
|
|
4833
4976
|
const value = booleanField(record[key]);
|
|
4834
4977
|
if (value !== undefined)
|
|
4835
|
-
|
|
4978
|
+
values.push(value);
|
|
4836
4979
|
}
|
|
4837
4980
|
}
|
|
4838
|
-
return;
|
|
4981
|
+
return values;
|
|
4839
4982
|
}
|
|
4840
|
-
function
|
|
4841
|
-
const
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4983
|
+
function mergedBoolean(records, keys, trueWins) {
|
|
4984
|
+
const values = collectBooleans(records, keys);
|
|
4985
|
+
if (values.length === 0)
|
|
4986
|
+
return;
|
|
4987
|
+
if (trueWins)
|
|
4988
|
+
return values.some(Boolean);
|
|
4989
|
+
return values.includes(false) ? false : true;
|
|
4990
|
+
}
|
|
4991
|
+
function routingAutomationMetadata(task, taskList) {
|
|
4992
|
+
const taskAutomation = objectField(task.metadata.automation);
|
|
4993
|
+
const taskListAutomation = taskList ? objectField(taskList.metadata.automation) : undefined;
|
|
4994
|
+
const records = [task.metadata, taskAutomation, taskList?.metadata, taskListAutomation];
|
|
4845
4995
|
const result = {};
|
|
4846
4996
|
const aliases = [
|
|
4847
4997
|
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
@@ -4852,7 +5002,7 @@ function routingAutomationMetadata(task) {
|
|
|
4852
5002
|
["approval_required", ["approval_required", "approvalRequired"]]
|
|
4853
5003
|
];
|
|
4854
5004
|
for (const [canonical, keys] of aliases) {
|
|
4855
|
-
const value =
|
|
5005
|
+
const value = mergedBoolean(records, keys, canonical !== "allowed");
|
|
4856
5006
|
if (value !== undefined)
|
|
4857
5007
|
result[canonical] = value;
|
|
4858
5008
|
}
|
|
@@ -4860,23 +5010,88 @@ function routingAutomationMetadata(task) {
|
|
|
4860
5010
|
result.requires_approval = true;
|
|
4861
5011
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
4862
5012
|
}
|
|
5013
|
+
function routeEnabledForTask(task, taskList) {
|
|
5014
|
+
const explicit = booleanField(task.metadata.route_enabled);
|
|
5015
|
+
if (explicit !== undefined)
|
|
5016
|
+
return explicit;
|
|
5017
|
+
if (task.tags.includes("auto:route") || task.tags.includes("route:enabled"))
|
|
5018
|
+
return true;
|
|
5019
|
+
const taskListDefault = taskList ? booleanField(taskList.metadata.route_enabled) : undefined;
|
|
5020
|
+
if (taskListDefault !== undefined)
|
|
5021
|
+
return taskListDefault;
|
|
5022
|
+
return;
|
|
5023
|
+
}
|
|
5024
|
+
function stringField(value) {
|
|
5025
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
5026
|
+
}
|
|
5027
|
+
function workflowPointersFromMetadata(metadata) {
|
|
5028
|
+
const nested = objectField(metadata.workflow_invocation) ?? objectField(metadata.workflow) ?? {};
|
|
5029
|
+
return {
|
|
5030
|
+
current_workflow_invocation_id: stringField(metadata.current_workflow_invocation_id) ?? stringField(nested.current_workflow_invocation_id) ?? stringField(nested.invocation_id),
|
|
5031
|
+
current_run_id: stringField(metadata.current_run_id) ?? stringField(nested.current_run_id) ?? stringField(nested.run_id),
|
|
5032
|
+
latest_manifest_path: stringField(metadata.latest_manifest_path) ?? stringField(nested.latest_manifest_path) ?? stringField(nested.manifest_path),
|
|
5033
|
+
latest_evaluation_path: stringField(metadata.latest_evaluation_path) ?? stringField(nested.latest_evaluation_path) ?? stringField(nested.evaluation_path),
|
|
5034
|
+
workflow_state: stringField(metadata.workflow_state) ?? stringField(nested.workflow_state) ?? stringField(nested.state)
|
|
5035
|
+
};
|
|
5036
|
+
}
|
|
5037
|
+
function classifyProjectKind(path) {
|
|
5038
|
+
return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
|
|
5039
|
+
}
|
|
5040
|
+
function isWorktreePath(path) {
|
|
5041
|
+
return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
|
|
5042
|
+
}
|
|
5043
|
+
function inferRootProjectId(project) {
|
|
5044
|
+
return isWorktreePath(project.path) ? null : project.id;
|
|
5045
|
+
}
|
|
5046
|
+
var TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION = "todos.task_route_state.v1";
|
|
5047
|
+
|
|
5048
|
+
// src/lib/shared-events.ts
|
|
5049
|
+
function taskEventData(task, extra = {}) {
|
|
5050
|
+
return {
|
|
5051
|
+
id: task.id,
|
|
5052
|
+
task_id: task.id,
|
|
5053
|
+
short_id: task.short_id,
|
|
5054
|
+
title: task.title,
|
|
5055
|
+
description: task.description,
|
|
5056
|
+
status: task.status,
|
|
5057
|
+
priority: task.priority,
|
|
5058
|
+
project_id: task.project_id,
|
|
5059
|
+
parent_id: task.parent_id,
|
|
5060
|
+
plan_id: task.plan_id,
|
|
5061
|
+
task_list_id: task.task_list_id,
|
|
5062
|
+
agent_id: task.agent_id,
|
|
5063
|
+
assigned_to: task.assigned_to,
|
|
5064
|
+
session_id: task.session_id,
|
|
5065
|
+
working_dir: task.working_dir,
|
|
5066
|
+
tags: task.tags,
|
|
5067
|
+
metadata: task.metadata,
|
|
5068
|
+
version: task.version,
|
|
5069
|
+
created_at: task.created_at,
|
|
5070
|
+
updated_at: task.updated_at,
|
|
5071
|
+
started_at: task.started_at,
|
|
5072
|
+
completed_at: task.completed_at,
|
|
5073
|
+
due_at: task.due_at,
|
|
5074
|
+
requires_approval: task.requires_approval,
|
|
5075
|
+
approved_by: task.approved_by,
|
|
5076
|
+
approved_at: task.approved_at,
|
|
5077
|
+
...extra
|
|
5078
|
+
};
|
|
5079
|
+
}
|
|
4863
5080
|
function taskEventMetadata(task) {
|
|
4864
5081
|
const metadata = {
|
|
4865
5082
|
package: "@hasna/todos",
|
|
4866
5083
|
todos_event_schema_version: 1,
|
|
5084
|
+
route_state_schema_version: TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION,
|
|
4867
5085
|
task_id: task.id,
|
|
4868
5086
|
task_short_id: task.short_id,
|
|
4869
5087
|
project_id: task.project_id,
|
|
4870
5088
|
task_list_id: task.task_list_id,
|
|
4871
5089
|
working_dir: task.working_dir
|
|
4872
5090
|
};
|
|
4873
|
-
const
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
const automation = routingAutomationMetadata(task);
|
|
4878
|
-
if (automation) {
|
|
4879
|
-
metadata.automation = automation;
|
|
5091
|
+
const pointers = workflowPointersFromMetadata(task.metadata);
|
|
5092
|
+
for (const [key, value] of Object.entries(pointers)) {
|
|
5093
|
+
if (value)
|
|
5094
|
+
metadata[key] = value;
|
|
4880
5095
|
}
|
|
4881
5096
|
try {
|
|
4882
5097
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
@@ -4905,18 +5120,20 @@ function taskEventMetadata(task) {
|
|
|
4905
5120
|
metadata.task_list_project_id = taskList.project_id;
|
|
4906
5121
|
metadata.task_list_is_project_default = Boolean(project?.task_list_id && taskList.slug === project.task_list_id);
|
|
4907
5122
|
}
|
|
5123
|
+
const routeEnabled = routeEnabledForTask(task, taskList);
|
|
5124
|
+
if (routeEnabled !== undefined) {
|
|
5125
|
+
metadata.route_enabled = routeEnabled;
|
|
5126
|
+
}
|
|
5127
|
+
const automation = routingAutomationMetadata(task, taskList);
|
|
5128
|
+
if (automation) {
|
|
5129
|
+
metadata.automation = automation;
|
|
5130
|
+
metadata.route_blocked_by_no_auto = automation.no_auto === true;
|
|
5131
|
+
metadata.route_blocked_by_manual = automation.manual === true || automation.manual_required === true;
|
|
5132
|
+
metadata.route_blocked_by_approval = (automation.requires_approval === true || automation.approval_required === true) && !task.approved_by;
|
|
5133
|
+
}
|
|
4908
5134
|
} catch {}
|
|
4909
5135
|
return metadata;
|
|
4910
5136
|
}
|
|
4911
|
-
function classifyProjectKind(path) {
|
|
4912
|
-
return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
|
|
4913
|
-
}
|
|
4914
|
-
function isWorktreePath(path) {
|
|
4915
|
-
return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
|
|
4916
|
-
}
|
|
4917
|
-
function inferRootProjectId(project) {
|
|
4918
|
-
return isWorktreePath(project.path) ? null : project.id;
|
|
4919
|
-
}
|
|
4920
5137
|
function readMachineLocalPath(project) {
|
|
4921
5138
|
const machineId = process.env["TODOS_MACHINE_ID"];
|
|
4922
5139
|
if (!machineId)
|
|
@@ -4929,6 +5146,8 @@ function readMachineLocalPath(project) {
|
|
|
4929
5146
|
}
|
|
4930
5147
|
}
|
|
4931
5148
|
async function emitSharedTaskEvent(input) {
|
|
5149
|
+
if (!shouldEmitSharedTaskEvents(input.databasePath))
|
|
5150
|
+
return;
|
|
4932
5151
|
const data = taskEventData(input.task, input.data);
|
|
4933
5152
|
await new EventsClient().emit({
|
|
4934
5153
|
source: SOURCE,
|
|
@@ -4952,6 +5171,7 @@ var init_shared_events = __esm(() => {
|
|
|
4952
5171
|
init_database();
|
|
4953
5172
|
init_projects();
|
|
4954
5173
|
init_task_lists();
|
|
5174
|
+
init_event_emission_safety();
|
|
4955
5175
|
});
|
|
4956
5176
|
|
|
4957
5177
|
// src/lib/secret-redaction.ts
|
|
@@ -5200,8 +5420,9 @@ function logTaskChange(taskId, action, field, oldValue, newValue, agentId, db) {
|
|
|
5200
5420
|
const d = db || getDatabase();
|
|
5201
5421
|
const id = uuid();
|
|
5202
5422
|
const timestamp = now();
|
|
5203
|
-
|
|
5204
|
-
|
|
5423
|
+
const machineId = currentStorageMachineId(d);
|
|
5424
|
+
d.run(`INSERT INTO task_history (id, task_id, action, field, old_value, new_value, agent_id, created_at, machine_id)
|
|
5425
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, taskId, action, field || null, oldValue ?? null, newValue ?? null, agentId || null, timestamp, machineId]);
|
|
5205
5426
|
try {
|
|
5206
5427
|
const { logActivity: logActivity2 } = (init_activity_audit(), __toCommonJS(exports_activity_audit));
|
|
5207
5428
|
logActivity2({
|
|
@@ -5214,7 +5435,7 @@ function logTaskChange(taskId, action, field, oldValue, newValue, agentId, db) {
|
|
|
5214
5435
|
actor_id: agentId ?? undefined
|
|
5215
5436
|
}, d);
|
|
5216
5437
|
} catch {}
|
|
5217
|
-
return { id, task_id: taskId, action, field: field || null, old_value: oldValue ?? null, new_value: newValue ?? null, agent_id: agentId || null, created_at: timestamp };
|
|
5438
|
+
return { id, task_id: taskId, action, field: field || null, old_value: oldValue ?? null, new_value: newValue ?? null, agent_id: agentId || null, created_at: timestamp, machine_id: machineId };
|
|
5218
5439
|
}
|
|
5219
5440
|
function getTaskHistory(taskId, db) {
|
|
5220
5441
|
const d = db || getDatabase();
|
|
@@ -5226,6 +5447,7 @@ function getRecentActivity(limit = 50, db) {
|
|
|
5226
5447
|
}
|
|
5227
5448
|
var init_audit = __esm(() => {
|
|
5228
5449
|
init_database();
|
|
5450
|
+
init_storage_tombstones();
|
|
5229
5451
|
});
|
|
5230
5452
|
|
|
5231
5453
|
// src/db/webhooks.ts
|
|
@@ -5491,13 +5713,14 @@ function createTask(input, db) {
|
|
|
5491
5713
|
const d = db || getDatabase();
|
|
5492
5714
|
const timestamp = now();
|
|
5493
5715
|
const tags = input.tags || [];
|
|
5716
|
+
const machineId = currentStorageMachineId(d);
|
|
5494
5717
|
const assignedBy = input.assigned_by || input.agent_id;
|
|
5495
5718
|
const assignedFromProject = input.assigned_from_project || null;
|
|
5496
5719
|
let id = uuid();
|
|
5497
5720
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
5498
5721
|
try {
|
|
5499
|
-
d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, sla_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type)
|
|
5500
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5722
|
+
d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, sla_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type, machine_id)
|
|
5723
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5501
5724
|
id,
|
|
5502
5725
|
null,
|
|
5503
5726
|
input.project_id || null,
|
|
@@ -5534,7 +5757,8 @@ function createTask(input, db) {
|
|
|
5534
5757
|
input.spawned_from_session || null,
|
|
5535
5758
|
assignedBy || null,
|
|
5536
5759
|
assignedFromProject || null,
|
|
5537
|
-
input.task_type || null
|
|
5760
|
+
input.task_type || null,
|
|
5761
|
+
machineId
|
|
5538
5762
|
]);
|
|
5539
5763
|
break;
|
|
5540
5764
|
} catch (e) {
|
|
@@ -5550,9 +5774,10 @@ function createTask(input, db) {
|
|
|
5550
5774
|
}
|
|
5551
5775
|
const task = getTask(id, d);
|
|
5552
5776
|
const payload = taskEventData(task);
|
|
5777
|
+
const databasePath = databasePathFromDatabase(d);
|
|
5553
5778
|
dispatchWebhook2("task.created", payload, d).catch(() => {});
|
|
5554
|
-
emitLocalEventHooksQuiet({ type: "task.created", payload });
|
|
5555
|
-
emitSharedTaskEventQuiet({ type: "task.created", task });
|
|
5779
|
+
emitLocalEventHooksQuiet({ type: "task.created", payload, databasePath });
|
|
5780
|
+
emitSharedTaskEventQuiet({ type: "task.created", task, databasePath });
|
|
5556
5781
|
return task;
|
|
5557
5782
|
}
|
|
5558
5783
|
function getTask(id, db) {
|
|
@@ -5970,29 +6195,39 @@ function updateTask(id, input, db) {
|
|
|
5970
6195
|
approved_by: input.approved_by ?? task.approved_by,
|
|
5971
6196
|
approved_at: input.approved_by ? timestamp : task.approved_at
|
|
5972
6197
|
};
|
|
6198
|
+
const databasePath = databasePathFromDatabase(d);
|
|
5973
6199
|
if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to) {
|
|
5974
6200
|
const payload = taskEventData(updatedTask, { assigned_to: input.assigned_to, old_assigned_to: task.assigned_to });
|
|
5975
6201
|
dispatchWebhook2("task.assigned", payload, d).catch(() => {});
|
|
5976
|
-
emitLocalEventHooksQuiet({ type: "task.assigned", payload });
|
|
5977
|
-
emitSharedTaskEventQuiet({ type: "task.assigned", task: updatedTask, data: { old_assigned_to: task.assigned_to } });
|
|
6202
|
+
emitLocalEventHooksQuiet({ type: "task.assigned", payload, databasePath });
|
|
6203
|
+
emitSharedTaskEventQuiet({ type: "task.assigned", task: updatedTask, data: { old_assigned_to: task.assigned_to }, databasePath });
|
|
5978
6204
|
}
|
|
5979
6205
|
if (input.status !== undefined && input.status !== task.status) {
|
|
5980
6206
|
const payload = taskEventData(updatedTask, { old_status: task.status, new_status: input.status });
|
|
5981
6207
|
dispatchWebhook2("task.status_changed", payload, d).catch(() => {});
|
|
5982
|
-
emitLocalEventHooksQuiet({ type: "task.status_changed", payload });
|
|
5983
|
-
emitSharedTaskEventQuiet({ type: "task.status_changed", task: updatedTask, data: { old_status: task.status, new_status: input.status } });
|
|
6208
|
+
emitLocalEventHooksQuiet({ type: "task.status_changed", payload, databasePath });
|
|
6209
|
+
emitSharedTaskEventQuiet({ type: "task.status_changed", task: updatedTask, data: { old_status: task.status, new_status: input.status }, databasePath });
|
|
5984
6210
|
}
|
|
5985
6211
|
if (input.approved_by !== undefined) {
|
|
5986
|
-
emitLocalEventHooksQuiet({ type: "approval.decided", payload: { id, approved_by: input.approved_by, title: task.title } });
|
|
6212
|
+
emitLocalEventHooksQuiet({ type: "approval.decided", payload: { id, approved_by: input.approved_by, title: task.title }, databasePath });
|
|
5987
6213
|
}
|
|
5988
6214
|
const updatePayload = taskEventData(updatedTask);
|
|
5989
6215
|
dispatchWebhook2("task.updated", updatePayload, d).catch(() => {});
|
|
5990
|
-
emitLocalEventHooksQuiet({ type: "task.updated", payload: updatePayload });
|
|
5991
|
-
emitSharedTaskEventQuiet({ type: "task.updated", task: updatedTask });
|
|
6216
|
+
emitLocalEventHooksQuiet({ type: "task.updated", payload: updatePayload, databasePath });
|
|
6217
|
+
emitSharedTaskEventQuiet({ type: "task.updated", task: updatedTask, databasePath });
|
|
5992
6218
|
return updatedTask;
|
|
5993
6219
|
}
|
|
5994
6220
|
function deleteTask(id, db) {
|
|
5995
6221
|
const d = db || getDatabase();
|
|
6222
|
+
const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
|
|
6223
|
+
if (!row)
|
|
6224
|
+
return false;
|
|
6225
|
+
recordStorageTombstone({
|
|
6226
|
+
object_type: "tasks",
|
|
6227
|
+
object_id: id,
|
|
6228
|
+
payload: rowToTask(row),
|
|
6229
|
+
version: row.version
|
|
6230
|
+
}, d);
|
|
5996
6231
|
const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
|
|
5997
6232
|
return result.changes > 0;
|
|
5998
6233
|
}
|
|
@@ -6000,11 +6235,13 @@ var init_task_crud = __esm(() => {
|
|
|
6000
6235
|
init_types();
|
|
6001
6236
|
init_database();
|
|
6002
6237
|
init_completion_guard();
|
|
6238
|
+
init_event_emission_safety();
|
|
6003
6239
|
init_event_hooks();
|
|
6004
6240
|
init_shared_events();
|
|
6005
6241
|
init_audit();
|
|
6006
6242
|
init_webhooks();
|
|
6007
6243
|
init_checklists();
|
|
6244
|
+
init_storage_tombstones();
|
|
6008
6245
|
});
|
|
6009
6246
|
|
|
6010
6247
|
// src/lib/recurrence.ts
|
|
@@ -6153,8 +6390,9 @@ function resolveTemplateId(id, d) {
|
|
|
6153
6390
|
function createTemplate(input, db) {
|
|
6154
6391
|
const d = db || getDatabase();
|
|
6155
6392
|
const id = uuid();
|
|
6156
|
-
|
|
6157
|
-
|
|
6393
|
+
const machineId = currentStorageMachineId(d);
|
|
6394
|
+
d.run(`INSERT INTO task_templates (id, name, title_pattern, description, priority, tags, variables, project_id, plan_id, metadata, created_at, machine_id)
|
|
6395
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
6158
6396
|
id,
|
|
6159
6397
|
input.name,
|
|
6160
6398
|
input.title_pattern,
|
|
@@ -6165,7 +6403,8 @@ function createTemplate(input, db) {
|
|
|
6165
6403
|
input.project_id || null,
|
|
6166
6404
|
input.plan_id || null,
|
|
6167
6405
|
JSON.stringify(input.metadata || {}),
|
|
6168
|
-
now()
|
|
6406
|
+
now(),
|
|
6407
|
+
machineId
|
|
6169
6408
|
]);
|
|
6170
6409
|
if (input.tasks && input.tasks.length > 0) {
|
|
6171
6410
|
addTemplateTasks(id, input.tasks, d);
|
|
@@ -6189,6 +6428,15 @@ function deleteTemplate(id, db) {
|
|
|
6189
6428
|
const resolved = resolveTemplateId(id, d);
|
|
6190
6429
|
if (!resolved)
|
|
6191
6430
|
return false;
|
|
6431
|
+
const template = getTemplate(resolved, d);
|
|
6432
|
+
if (!template)
|
|
6433
|
+
return false;
|
|
6434
|
+
recordStorageTombstone({
|
|
6435
|
+
object_type: "templates",
|
|
6436
|
+
object_id: resolved,
|
|
6437
|
+
payload: template,
|
|
6438
|
+
version: template.version
|
|
6439
|
+
}, d);
|
|
6192
6440
|
return d.run("DELETE FROM task_templates WHERE id = ?", [resolved]).changes > 0;
|
|
6193
6441
|
}
|
|
6194
6442
|
function updateTemplate(id, updates, db) {
|
|
@@ -6558,6 +6806,7 @@ function previewTemplate(templateId, variables, db) {
|
|
|
6558
6806
|
var init_templates = __esm(() => {
|
|
6559
6807
|
init_database();
|
|
6560
6808
|
init_tasks();
|
|
6809
|
+
init_storage_tombstones();
|
|
6561
6810
|
});
|
|
6562
6811
|
|
|
6563
6812
|
// src/db/task-graph.ts
|
|
@@ -6724,6 +6973,7 @@ function getBlockingDeps(id, db) {
|
|
|
6724
6973
|
}
|
|
6725
6974
|
function startTask(id, agentId, db) {
|
|
6726
6975
|
const d = db || getDatabase();
|
|
6976
|
+
const databasePath = databasePathFromDatabase(d);
|
|
6727
6977
|
const task = getTask(id, d);
|
|
6728
6978
|
if (!task)
|
|
6729
6979
|
throw new TaskNotFoundError(id);
|
|
@@ -6738,7 +6988,8 @@ function startTask(id, agentId, db) {
|
|
|
6738
6988
|
agent_id: agentId,
|
|
6739
6989
|
title: task.title,
|
|
6740
6990
|
blockers: blocking.map((b) => ({ id: b.id, short_id: b.short_id, title: b.title, status: b.status }))
|
|
6741
|
-
}
|
|
6991
|
+
},
|
|
6992
|
+
databasePath
|
|
6742
6993
|
});
|
|
6743
6994
|
throw new Error(`Task is blocked by ${blocking.length} unfinished dependency(ies): ${blockerIds}`);
|
|
6744
6995
|
}
|
|
@@ -6760,12 +7011,13 @@ function startTask(id, agentId, db) {
|
|
|
6760
7011
|
const startedTask = { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, started_at: task.started_at || timestamp, version: task.version + 1, updated_at: timestamp };
|
|
6761
7012
|
const payload = taskEventData(startedTask, { agent_id: agentId });
|
|
6762
7013
|
dispatchWebhook2("task.started", payload, d).catch(() => {});
|
|
6763
|
-
emitLocalEventHooksQuiet({ type: "task.started", payload });
|
|
6764
|
-
emitSharedTaskEventQuiet({ type: "task.started", task: startedTask, data: { agent_id: agentId } });
|
|
7014
|
+
emitLocalEventHooksQuiet({ type: "task.started", payload, databasePath });
|
|
7015
|
+
emitSharedTaskEventQuiet({ type: "task.started", task: startedTask, data: { agent_id: agentId }, databasePath });
|
|
6765
7016
|
return startedTask;
|
|
6766
7017
|
}
|
|
6767
7018
|
function completeTask(id, agentId, db, options) {
|
|
6768
7019
|
const d = db || getDatabase();
|
|
7020
|
+
const databasePath = databasePathFromDatabase(d);
|
|
6769
7021
|
const task = getTask(id, d);
|
|
6770
7022
|
if (!task)
|
|
6771
7023
|
throw new TaskNotFoundError(id);
|
|
@@ -6811,8 +7063,8 @@ function completeTask(id, agentId, db, options) {
|
|
|
6811
7063
|
};
|
|
6812
7064
|
const completionPayload = taskEventData(completedTaskForEvent, { agent_id: agentId, completed_at: timestamp });
|
|
6813
7065
|
dispatchWebhook2("task.completed", completionPayload, d).catch(() => {});
|
|
6814
|
-
emitLocalEventHooksQuiet({ type: "task.completed", payload: completionPayload });
|
|
6815
|
-
emitSharedTaskEventQuiet({ type: "task.completed", task: completedTaskForEvent, data: { agent_id: agentId, completed_at: timestamp } });
|
|
7066
|
+
emitLocalEventHooksQuiet({ type: "task.completed", payload: completionPayload, databasePath });
|
|
7067
|
+
emitSharedTaskEventQuiet({ type: "task.completed", task: completedTaskForEvent, data: { agent_id: agentId, completed_at: timestamp }, databasePath });
|
|
6816
7068
|
let spawnedTask = null;
|
|
6817
7069
|
if (task.recurrence_rule && !options?.skip_recurrence) {
|
|
6818
7070
|
spawnedTask = spawnNextRecurrence(task, d, timestamp);
|
|
@@ -6856,9 +7108,9 @@ function completeTask(id, agentId, db, options) {
|
|
|
6856
7108
|
const depTask = getTask(dep.id, d);
|
|
6857
7109
|
const payload = depTask ? taskEventData(depTask, { unblocked_by: id }) : { id: dep.id, unblocked_by: id, title: dep.title };
|
|
6858
7110
|
dispatchWebhook2("task.unblocked", payload, d).catch(() => {});
|
|
6859
|
-
emitLocalEventHooksQuiet({ type: "task.unblocked", payload });
|
|
7111
|
+
emitLocalEventHooksQuiet({ type: "task.unblocked", payload, databasePath });
|
|
6860
7112
|
if (depTask)
|
|
6861
|
-
emitSharedTaskEventQuiet({ type: "task.unblocked", task: depTask, data: { unblocked_by: id } });
|
|
7113
|
+
emitSharedTaskEventQuiet({ type: "task.unblocked", task: depTask, data: { unblocked_by: id }, databasePath });
|
|
6862
7114
|
}
|
|
6863
7115
|
}
|
|
6864
7116
|
return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, confidence, version: task.version + 1, updated_at: timestamp, metadata: meta };
|
|
@@ -7028,6 +7280,7 @@ function getTasksChangedSince(since, filters, db) {
|
|
|
7028
7280
|
}
|
|
7029
7281
|
function failTask(id, agentId, reason, options, db) {
|
|
7030
7282
|
const d = db || getDatabase();
|
|
7283
|
+
const databasePath = databasePathFromDatabase(d);
|
|
7031
7284
|
const task = getTask(id, d);
|
|
7032
7285
|
if (!task)
|
|
7033
7286
|
throw new TaskNotFoundError(id);
|
|
@@ -7056,8 +7309,8 @@ function failTask(id, agentId, reason, options, db) {
|
|
|
7056
7309
|
logTaskChange(id, "fail", "status", task.status, "failed", agentId || null, d);
|
|
7057
7310
|
const failurePayload = taskEventData(failedTask, { reason, error_code: options?.error_code, agent_id: agentId });
|
|
7058
7311
|
dispatchWebhook2("task.failed", failurePayload, d).catch(() => {});
|
|
7059
|
-
emitLocalEventHooksQuiet({ type: "task.failed", payload: failurePayload });
|
|
7060
|
-
emitSharedTaskEventQuiet({ type: "task.failed", task: failedTask, data: { reason, error_code: options?.error_code, agent_id: agentId }, severity: "warning" });
|
|
7312
|
+
emitLocalEventHooksQuiet({ type: "task.failed", payload: failurePayload, databasePath });
|
|
7313
|
+
emitSharedTaskEventQuiet({ type: "task.failed", task: failedTask, data: { reason, error_code: options?.error_code, agent_id: agentId }, severity: "warning", databasePath });
|
|
7061
7314
|
let retryTask;
|
|
7062
7315
|
if (options?.retry) {
|
|
7063
7316
|
const retryCount = (task.retry_count || 0) + 1;
|
|
@@ -7117,6 +7370,7 @@ function getStaleTasks(staleQuery = 30, filters, db) {
|
|
|
7117
7370
|
}
|
|
7118
7371
|
function stealTask(agentId, opts, db) {
|
|
7119
7372
|
const d = db || getDatabase();
|
|
7373
|
+
const databasePath = databasePathFromDatabase(d);
|
|
7120
7374
|
const staleMinutes = opts?.stale_minutes ?? 30;
|
|
7121
7375
|
const staleTasks = getStaleTasks(staleMinutes, { project_id: opts?.project_id, task_list_id: opts?.task_list_id }, d);
|
|
7122
7376
|
if (staleTasks.length === 0)
|
|
@@ -7135,8 +7389,8 @@ function stealTask(agentId, opts, db) {
|
|
|
7135
7389
|
const stolenTask = { ...target, assigned_to: agentId, locked_by: agentId, locked_at: timestamp, updated_at: timestamp, version: target.version + 1 };
|
|
7136
7390
|
const payload = taskEventData(stolenTask, { agent_id: agentId, stolen_from: target.assigned_to });
|
|
7137
7391
|
dispatchWebhook2("task.assigned", payload, d).catch(() => {});
|
|
7138
|
-
emitLocalEventHooksQuiet({ type: "task.assigned", payload });
|
|
7139
|
-
emitSharedTaskEventQuiet({ type: "task.assigned", task: stolenTask, data: { agent_id: agentId, stolen_from: target.assigned_to } });
|
|
7392
|
+
emitLocalEventHooksQuiet({ type: "task.assigned", payload, databasePath });
|
|
7393
|
+
emitSharedTaskEventQuiet({ type: "task.assigned", task: stolenTask, data: { agent_id: agentId, stolen_from: target.assigned_to }, databasePath });
|
|
7140
7394
|
return stolenTask;
|
|
7141
7395
|
}
|
|
7142
7396
|
function claimOrSteal(agentId, filters, db) {
|
|
@@ -7184,6 +7438,7 @@ var init_task_lifecycle = __esm(() => {
|
|
|
7184
7438
|
init_types();
|
|
7185
7439
|
init_database();
|
|
7186
7440
|
init_completion_guard();
|
|
7441
|
+
init_event_emission_safety();
|
|
7187
7442
|
init_event_hooks();
|
|
7188
7443
|
init_shared_events();
|
|
7189
7444
|
init_audit();
|
|
@@ -7891,8 +8146,9 @@ function createPlan(input, db) {
|
|
|
7891
8146
|
const d = db || getDatabase();
|
|
7892
8147
|
const id = uuid();
|
|
7893
8148
|
const timestamp = now();
|
|
7894
|
-
|
|
7895
|
-
|
|
8149
|
+
const machineId = currentStorageMachineId(d);
|
|
8150
|
+
d.run(`INSERT INTO plans (id, project_id, task_list_id, agent_id, name, description, status, created_at, updated_at, machine_id)
|
|
8151
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
7896
8152
|
id,
|
|
7897
8153
|
input.project_id || null,
|
|
7898
8154
|
input.task_list_id || null,
|
|
@@ -7901,7 +8157,8 @@ function createPlan(input, db) {
|
|
|
7901
8157
|
input.description || null,
|
|
7902
8158
|
input.status || "active",
|
|
7903
8159
|
timestamp,
|
|
7904
|
-
timestamp
|
|
8160
|
+
timestamp,
|
|
8161
|
+
machineId
|
|
7905
8162
|
]);
|
|
7906
8163
|
return getPlan(id, d);
|
|
7907
8164
|
}
|
|
@@ -7949,19 +8206,30 @@ function updatePlan(id, input, db) {
|
|
|
7949
8206
|
const updated = getPlan(id, d);
|
|
7950
8207
|
emitLocalEventHooksQuiet({
|
|
7951
8208
|
type: "plan.updated",
|
|
7952
|
-
payload: { id, old_status: plan.status, new_status: updated.status, name: updated.name, project_id: updated.project_id }
|
|
8209
|
+
payload: { id, old_status: plan.status, new_status: updated.status, name: updated.name, project_id: updated.project_id },
|
|
8210
|
+
databasePath: databasePathFromDatabase(d)
|
|
7953
8211
|
});
|
|
7954
8212
|
return updated;
|
|
7955
8213
|
}
|
|
7956
8214
|
function deletePlan(id, db) {
|
|
7957
8215
|
const d = db || getDatabase();
|
|
8216
|
+
const plan = getPlan(id, d);
|
|
8217
|
+
if (!plan)
|
|
8218
|
+
return false;
|
|
8219
|
+
recordStorageTombstone({
|
|
8220
|
+
object_type: "plans",
|
|
8221
|
+
object_id: id,
|
|
8222
|
+
payload: plan
|
|
8223
|
+
}, d);
|
|
7958
8224
|
const result = d.run("DELETE FROM plans WHERE id = ?", [id]);
|
|
7959
8225
|
return result.changes > 0;
|
|
7960
8226
|
}
|
|
7961
8227
|
var init_plans = __esm(() => {
|
|
7962
8228
|
init_types();
|
|
8229
|
+
init_event_emission_safety();
|
|
7963
8230
|
init_event_hooks();
|
|
7964
8231
|
init_database();
|
|
8232
|
+
init_storage_tombstones();
|
|
7965
8233
|
});
|
|
7966
8234
|
|
|
7967
8235
|
// src/db/boards.ts
|
|
@@ -8368,20 +8636,20 @@ var init_boards = __esm(() => {
|
|
|
8368
8636
|
// src/lib/artifact-store.ts
|
|
8369
8637
|
import { createHash as createHash3 } from "crypto";
|
|
8370
8638
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
8371
|
-
import { basename, dirname as dirname5, join as join6, resolve as
|
|
8372
|
-
import { tmpdir } from "os";
|
|
8639
|
+
import { basename, dirname as dirname5, join as join6, resolve as resolve7 } from "path";
|
|
8640
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
8373
8641
|
function isInMemoryDb2(path) {
|
|
8374
8642
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
8375
8643
|
}
|
|
8376
8644
|
function artifactStoreRoot() {
|
|
8377
8645
|
if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
|
|
8378
|
-
return
|
|
8646
|
+
return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
|
|
8379
8647
|
if (process.env["TODOS_ARTIFACTS_DIR"])
|
|
8380
|
-
return
|
|
8648
|
+
return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
8381
8649
|
const dbPath = getDatabasePath();
|
|
8382
8650
|
if (isInMemoryDb2(dbPath))
|
|
8383
|
-
return join6(
|
|
8384
|
-
return join6(dirname5(
|
|
8651
|
+
return join6(tmpdir2(), "hasna-todos-artifacts");
|
|
8652
|
+
return join6(dirname5(resolve7(dbPath)), "artifacts");
|
|
8385
8653
|
}
|
|
8386
8654
|
function artifactStorePath(relativePath) {
|
|
8387
8655
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
@@ -8428,7 +8696,7 @@ function mediaTypeFor(path, textLike) {
|
|
|
8428
8696
|
return "application/octet-stream";
|
|
8429
8697
|
}
|
|
8430
8698
|
function storeArtifactContent(input) {
|
|
8431
|
-
const sourcePath =
|
|
8699
|
+
const sourcePath = resolve7(input.path);
|
|
8432
8700
|
if (!existsSync7(sourcePath))
|
|
8433
8701
|
return null;
|
|
8434
8702
|
const sourceStat = statSync2(sourcePath);
|
|
@@ -9125,7 +9393,11 @@ function startTaskRun(input, db) {
|
|
|
9125
9393
|
}, d);
|
|
9126
9394
|
}
|
|
9127
9395
|
const run = getTaskRun(id, d);
|
|
9128
|
-
emitLocalEventHooksQuiet({
|
|
9396
|
+
emitLocalEventHooksQuiet({
|
|
9397
|
+
type: "run.started",
|
|
9398
|
+
payload: { id: run.id, task_id: run.task_id, agent_id: run.agent_id, title: run.title },
|
|
9399
|
+
databasePath: databasePathFromDatabase(d)
|
|
9400
|
+
});
|
|
9129
9401
|
return run;
|
|
9130
9402
|
}
|
|
9131
9403
|
function beginTaskRunTransaction(input, db) {
|
|
@@ -9394,7 +9666,8 @@ function finishTaskRun(input, db) {
|
|
|
9394
9666
|
const updated = getTaskRun(run.id, d);
|
|
9395
9667
|
emitLocalEventHooksQuiet({
|
|
9396
9668
|
type: `run.${input.status}`,
|
|
9397
|
-
payload: { id: updated.id, task_id: updated.task_id, agent_id: updated.agent_id, status: updated.status, summary: updated.summary, completed_at: timestamp }
|
|
9669
|
+
payload: { id: updated.id, task_id: updated.task_id, agent_id: updated.agent_id, status: updated.status, summary: updated.summary, completed_at: timestamp },
|
|
9670
|
+
databasePath: databasePathFromDatabase(d)
|
|
9398
9671
|
});
|
|
9399
9672
|
return updated;
|
|
9400
9673
|
}
|
|
@@ -9478,6 +9751,7 @@ function getTaskRunLedger(runId, db) {
|
|
|
9478
9751
|
var LOOP_RUN_TRANSACTION_SCHEMA_VERSION = "todos.loop_run_transaction.v1";
|
|
9479
9752
|
var init_task_runs = __esm(() => {
|
|
9480
9753
|
init_artifact_store();
|
|
9754
|
+
init_event_emission_safety();
|
|
9481
9755
|
init_event_hooks();
|
|
9482
9756
|
init_redaction();
|
|
9483
9757
|
init_types();
|
|
@@ -10364,6 +10638,7 @@ function rowToAgent(row) {
|
|
|
10364
10638
|
}
|
|
10365
10639
|
function registerAgent(input, db) {
|
|
10366
10640
|
const d = db || getDatabase();
|
|
10641
|
+
const machineId = currentStorageMachineId(d);
|
|
10367
10642
|
const existingNames = d.query("SELECT name FROM agents").all().map((row) => row.name);
|
|
10368
10643
|
const normalizedName = validateAgentName(input.name, existingNames);
|
|
10369
10644
|
const existing = getAgentByName(normalizedName, d);
|
|
@@ -10404,14 +10679,18 @@ function registerAgent(input, db) {
|
|
|
10404
10679
|
updates.push("active_project_id = ?");
|
|
10405
10680
|
params.push(input.project_id);
|
|
10406
10681
|
}
|
|
10682
|
+
if (!existing.machine_id && machineId) {
|
|
10683
|
+
updates.push("machine_id = ?");
|
|
10684
|
+
params.push(machineId);
|
|
10685
|
+
}
|
|
10407
10686
|
params.push(existing.id);
|
|
10408
10687
|
d.run(`UPDATE agents SET ${updates.join(", ")} WHERE id = ?`, params);
|
|
10409
10688
|
return getAgent(existing.id, d);
|
|
10410
10689
|
}
|
|
10411
10690
|
const id = shortUuid();
|
|
10412
10691
|
const timestamp = now();
|
|
10413
|
-
d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, capabilities, reports_to, org_id, metadata, created_at, last_seen_at, session_id, working_dir, active_project_id)
|
|
10414
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
10692
|
+
d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, capabilities, reports_to, org_id, metadata, created_at, last_seen_at, session_id, working_dir, active_project_id, machine_id)
|
|
10693
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
10415
10694
|
id,
|
|
10416
10695
|
normalizedName,
|
|
10417
10696
|
input.description || null,
|
|
@@ -10427,7 +10706,8 @@ function registerAgent(input, db) {
|
|
|
10427
10706
|
timestamp,
|
|
10428
10707
|
input.session_id || null,
|
|
10429
10708
|
input.working_dir || null,
|
|
10430
|
-
input.project_id && input.session_id ? input.project_id : null
|
|
10709
|
+
input.project_id && input.session_id ? input.project_id : null,
|
|
10710
|
+
machineId
|
|
10431
10711
|
]);
|
|
10432
10712
|
return getAgent(id, d);
|
|
10433
10713
|
}
|
|
@@ -10610,6 +10890,7 @@ function getCapableAgents(capabilities, opts, db) {
|
|
|
10610
10890
|
}
|
|
10611
10891
|
var init_agents = __esm(() => {
|
|
10612
10892
|
init_database();
|
|
10893
|
+
init_storage_tombstones();
|
|
10613
10894
|
init_agent_names();
|
|
10614
10895
|
});
|
|
10615
10896
|
|
|
@@ -11145,7 +11426,7 @@ var init_doctor = __esm(() => {
|
|
|
11145
11426
|
});
|
|
11146
11427
|
|
|
11147
11428
|
// src/server/routes.ts
|
|
11148
|
-
import { join as join8, resolve as
|
|
11429
|
+
import { join as join8, resolve as resolve8, sep as sep2 } from "path";
|
|
11149
11430
|
function parseFieldsParam(url) {
|
|
11150
11431
|
const fieldsParam = url.searchParams.get("fields");
|
|
11151
11432
|
return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
|
|
@@ -11883,9 +12164,9 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
|
|
|
11883
12164
|
return null;
|
|
11884
12165
|
if (path !== "/") {
|
|
11885
12166
|
const filePath = join8(ctx.dashboardDir, path);
|
|
11886
|
-
const resolvedFile =
|
|
11887
|
-
const resolvedBase =
|
|
11888
|
-
if (!resolvedFile.startsWith(resolvedBase +
|
|
12167
|
+
const resolvedFile = resolve8(filePath);
|
|
12168
|
+
const resolvedBase = resolve8(ctx.dashboardDir);
|
|
12169
|
+
if (!resolvedFile.startsWith(resolvedBase + sep2) && resolvedFile !== resolvedBase) {
|
|
11889
12170
|
return json2({ error: "Forbidden" }, 403);
|
|
11890
12171
|
}
|
|
11891
12172
|
const res2 = serveStaticFile2(filePath);
|
|
@@ -15284,7 +15565,7 @@ class JSONSchemaGenerator {
|
|
|
15284
15565
|
if (val === undefined) {
|
|
15285
15566
|
if (this.unrepresentable === "throw") {
|
|
15286
15567
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
15287
|
-
}
|
|
15568
|
+
}
|
|
15288
15569
|
} else if (typeof val === "bigint") {
|
|
15289
15570
|
if (this.unrepresentable === "throw") {
|
|
15290
15571
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -17650,9 +17931,9 @@ data:
|
|
|
17650
17931
|
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
17651
17932
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
17652
17933
|
if (this._enableJsonResponse) {
|
|
17653
|
-
return new Promise((
|
|
17934
|
+
return new Promise((resolve9) => {
|
|
17654
17935
|
this._streamMapping.set(streamId, {
|
|
17655
|
-
resolveJson:
|
|
17936
|
+
resolveJson: resolve9,
|
|
17656
17937
|
cleanup: () => {
|
|
17657
17938
|
this._streamMapping.delete(streamId);
|
|
17658
17939
|
}
|
|
@@ -23952,7 +24233,7 @@ class Protocol {
|
|
|
23952
24233
|
return;
|
|
23953
24234
|
}
|
|
23954
24235
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
23955
|
-
await new Promise((
|
|
24236
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
23956
24237
|
options?.signal?.throwIfAborted();
|
|
23957
24238
|
}
|
|
23958
24239
|
} catch (error2) {
|
|
@@ -23964,7 +24245,7 @@ class Protocol {
|
|
|
23964
24245
|
}
|
|
23965
24246
|
request(request, resultSchema, options) {
|
|
23966
24247
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
23967
|
-
return new Promise((
|
|
24248
|
+
return new Promise((resolve9, reject) => {
|
|
23968
24249
|
const earlyReject = (error2) => {
|
|
23969
24250
|
reject(error2);
|
|
23970
24251
|
};
|
|
@@ -24042,7 +24323,7 @@ class Protocol {
|
|
|
24042
24323
|
if (!parseResult.success) {
|
|
24043
24324
|
reject(parseResult.error);
|
|
24044
24325
|
} else {
|
|
24045
|
-
|
|
24326
|
+
resolve9(parseResult.data);
|
|
24046
24327
|
}
|
|
24047
24328
|
} catch (error2) {
|
|
24048
24329
|
reject(error2);
|
|
@@ -24233,12 +24514,12 @@ class Protocol {
|
|
|
24233
24514
|
interval = task.pollInterval;
|
|
24234
24515
|
}
|
|
24235
24516
|
} catch {}
|
|
24236
|
-
return new Promise((
|
|
24517
|
+
return new Promise((resolve9, reject) => {
|
|
24237
24518
|
if (signal.aborted) {
|
|
24238
24519
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
24239
24520
|
return;
|
|
24240
24521
|
}
|
|
24241
|
-
const timeoutId = setTimeout(
|
|
24522
|
+
const timeoutId = setTimeout(resolve9, interval);
|
|
24242
24523
|
signal.addEventListener("abort", () => {
|
|
24243
24524
|
clearTimeout(timeoutId);
|
|
24244
24525
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -27223,7 +27504,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
27223
27504
|
const schOrFunc = root.refs[ref];
|
|
27224
27505
|
if (schOrFunc)
|
|
27225
27506
|
return schOrFunc;
|
|
27226
|
-
let _sch =
|
|
27507
|
+
let _sch = resolve9.call(this, root, ref);
|
|
27227
27508
|
if (_sch === undefined) {
|
|
27228
27509
|
const schema = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
|
|
27229
27510
|
const { schemaId } = this.opts;
|
|
@@ -27250,7 +27531,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
27250
27531
|
function sameSchemaEnv(s1, s2) {
|
|
27251
27532
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
27252
27533
|
}
|
|
27253
|
-
function
|
|
27534
|
+
function resolve9(root, ref) {
|
|
27254
27535
|
let sch;
|
|
27255
27536
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
27256
27537
|
ref = sch;
|
|
@@ -27780,7 +28061,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
27780
28061
|
}
|
|
27781
28062
|
return uri;
|
|
27782
28063
|
}
|
|
27783
|
-
function
|
|
28064
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
27784
28065
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
27785
28066
|
const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
27786
28067
|
schemelessOptions.skipEscape = true;
|
|
@@ -28008,7 +28289,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
28008
28289
|
var fastUri = {
|
|
28009
28290
|
SCHEMES,
|
|
28010
28291
|
normalize,
|
|
28011
|
-
resolve:
|
|
28292
|
+
resolve: resolve9,
|
|
28012
28293
|
resolveComponent,
|
|
28013
28294
|
equal,
|
|
28014
28295
|
serialize,
|
|
@@ -31534,7 +31815,7 @@ class McpServer {
|
|
|
31534
31815
|
let task = createTaskResult.task;
|
|
31535
31816
|
const pollInterval = task.pollInterval ?? 5000;
|
|
31536
31817
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
31537
|
-
await new Promise((
|
|
31818
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
31538
31819
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
31539
31820
|
if (!updatedTask) {
|
|
31540
31821
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -32158,12 +32439,12 @@ class StdioServerTransport {
|
|
|
32158
32439
|
this.onclose?.();
|
|
32159
32440
|
}
|
|
32160
32441
|
send(message) {
|
|
32161
|
-
return new Promise((
|
|
32442
|
+
return new Promise((resolve9) => {
|
|
32162
32443
|
const json2 = serializeMessage(message);
|
|
32163
32444
|
if (this._stdout.write(json2)) {
|
|
32164
|
-
|
|
32445
|
+
resolve9();
|
|
32165
32446
|
} else {
|
|
32166
|
-
this._stdout.once("drain",
|
|
32447
|
+
this._stdout.once("drain", resolve9);
|
|
32167
32448
|
}
|
|
32168
32449
|
});
|
|
32169
32450
|
}
|
|
@@ -33570,7 +33851,7 @@ var init_task_crud2 = __esm(() => {
|
|
|
33570
33851
|
|
|
33571
33852
|
// src/lib/project-bootstrap.ts
|
|
33572
33853
|
import { existsSync as existsSync9, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
33573
|
-
import { basename as basename3, dirname as dirname7, resolve as
|
|
33854
|
+
import { basename as basename3, dirname as dirname7, resolve as resolve9 } from "path";
|
|
33574
33855
|
function safeStat(path) {
|
|
33575
33856
|
try {
|
|
33576
33857
|
return statSync4(path);
|
|
@@ -33579,7 +33860,7 @@ function safeStat(path) {
|
|
|
33579
33860
|
}
|
|
33580
33861
|
}
|
|
33581
33862
|
function canonicalPath(input) {
|
|
33582
|
-
const resolved =
|
|
33863
|
+
const resolved = resolve9(input);
|
|
33583
33864
|
const stats = safeStat(resolved);
|
|
33584
33865
|
if (stats?.isFile())
|
|
33585
33866
|
return dirname7(resolved);
|
|
@@ -33588,7 +33869,7 @@ function canonicalPath(input) {
|
|
|
33588
33869
|
function findUp(start, marker) {
|
|
33589
33870
|
let current = canonicalPath(start);
|
|
33590
33871
|
while (true) {
|
|
33591
|
-
if (existsSync9(
|
|
33872
|
+
if (existsSync9(resolve9(current, marker)))
|
|
33592
33873
|
return current;
|
|
33593
33874
|
const parent = dirname7(current);
|
|
33594
33875
|
if (parent === current)
|
|
@@ -33599,7 +33880,7 @@ function findUp(start, marker) {
|
|
|
33599
33880
|
function readPackageJson(path) {
|
|
33600
33881
|
if (!path)
|
|
33601
33882
|
return null;
|
|
33602
|
-
const file =
|
|
33883
|
+
const file = resolve9(path, "package.json");
|
|
33603
33884
|
if (!existsSync9(file))
|
|
33604
33885
|
return null;
|
|
33605
33886
|
try {
|
|
@@ -33622,7 +33903,7 @@ function workspaceMarker(root, rootPackage) {
|
|
|
33622
33903
|
if (rootPackage?.workspaces)
|
|
33623
33904
|
markers.push("package.json#workspaces");
|
|
33624
33905
|
for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
|
|
33625
|
-
if (existsSync9(
|
|
33906
|
+
if (existsSync9(resolve9(root, marker)))
|
|
33626
33907
|
markers.push(marker);
|
|
33627
33908
|
}
|
|
33628
33909
|
const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
|
|
@@ -33684,7 +33965,19 @@ function bootstrapProject(options = {}, db) {
|
|
|
33684
33965
|
}
|
|
33685
33966
|
setMachineLocalPath(project.id, discovery.projectPath, d);
|
|
33686
33967
|
const beforeTaskList = d.query("SELECT id FROM task_lists WHERE project_id = ? AND slug = ?").get(project.id, taskListSlug);
|
|
33687
|
-
|
|
33968
|
+
let taskList = ensureTaskList(`${project.name} Tasks`, taskListSlug, project.id, d);
|
|
33969
|
+
if (options.routeEnabled && taskList.metadata.route_enabled !== true) {
|
|
33970
|
+
taskList = updateTaskList(taskList.id, {
|
|
33971
|
+
metadata: {
|
|
33972
|
+
...taskList.metadata,
|
|
33973
|
+
route_enabled: true,
|
|
33974
|
+
automation: {
|
|
33975
|
+
...taskList.metadata.automation && typeof taskList.metadata.automation === "object" && !Array.isArray(taskList.metadata.automation) ? taskList.metadata.automation : {},
|
|
33976
|
+
no_auto: false
|
|
33977
|
+
}
|
|
33978
|
+
}
|
|
33979
|
+
}, d);
|
|
33980
|
+
}
|
|
33688
33981
|
const createdSources = [];
|
|
33689
33982
|
for (const source of [
|
|
33690
33983
|
addSourceOnce(project.id, "local", "Project root", discovery.projectPath, { role: "project-root" }, d),
|
|
@@ -34154,7 +34447,7 @@ var init_retention_cleanup = __esm(() => {
|
|
|
34154
34447
|
|
|
34155
34448
|
// src/lib/mention-resolver.ts
|
|
34156
34449
|
import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
|
|
34157
|
-
import { basename as basename4, isAbsolute, join as join9, relative as relative3, resolve as
|
|
34450
|
+
import { basename as basename4, isAbsolute, join as join9, relative as relative3, resolve as resolve10, sep as sep3 } from "path";
|
|
34158
34451
|
function blankResolution(parsed) {
|
|
34159
34452
|
return {
|
|
34160
34453
|
input: parsed.input,
|
|
@@ -34177,11 +34470,11 @@ function backlink(kind, key, label, target = key) {
|
|
|
34177
34470
|
return { kind, key, label, target };
|
|
34178
34471
|
}
|
|
34179
34472
|
function normalizeWorkspace(workspace) {
|
|
34180
|
-
return
|
|
34473
|
+
return resolve10(workspace || process.cwd());
|
|
34181
34474
|
}
|
|
34182
34475
|
function isInside(root, absolutePath) {
|
|
34183
34476
|
const rel = relative3(root, absolutePath);
|
|
34184
|
-
return rel === "" || !rel.startsWith("..") && !rel.includes(`..${
|
|
34477
|
+
return rel === "" || !rel.startsWith("..") && !rel.includes(`..${sep3}`) && !isAbsolute(rel);
|
|
34185
34478
|
}
|
|
34186
34479
|
function normalizeRelativePath(value) {
|
|
34187
34480
|
const normalized = value.trim().replace(/^\.?\//, "").replace(/\\/g, "/");
|
|
@@ -34245,7 +34538,7 @@ function resolveFile(parsed, workspace) {
|
|
|
34245
34538
|
resolution.warnings.push("path is empty or escapes the workspace");
|
|
34246
34539
|
return resolution;
|
|
34247
34540
|
}
|
|
34248
|
-
const absolutePath =
|
|
34541
|
+
const absolutePath = resolve10(workspace, relPath);
|
|
34249
34542
|
if (!isInside(workspace, absolutePath)) {
|
|
34250
34543
|
resolution.path = relPath;
|
|
34251
34544
|
resolution.warnings.push("path escapes the workspace");
|
|
@@ -34590,9 +34883,9 @@ var init_mention_resolver = __esm(() => {
|
|
|
34590
34883
|
});
|
|
34591
34884
|
|
|
34592
34885
|
// src/lib/policy-packs.ts
|
|
34593
|
-
import { relative as relative4, resolve as
|
|
34886
|
+
import { relative as relative4, resolve as resolve11 } from "path";
|
|
34594
34887
|
function normalizePath3(path) {
|
|
34595
|
-
return
|
|
34888
|
+
return resolve11(path);
|
|
34596
34889
|
}
|
|
34597
34890
|
function unique4(values) {
|
|
34598
34891
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -34647,7 +34940,7 @@ function commandMatches(commands, pattern) {
|
|
|
34647
34940
|
}
|
|
34648
34941
|
function pathMatches(paths, pattern, root) {
|
|
34649
34942
|
return paths.filter((path) => {
|
|
34650
|
-
const candidate = path.startsWith("/") ? path :
|
|
34943
|
+
const candidate = path.startsWith("/") ? path : resolve11(root, path);
|
|
34651
34944
|
if (!isPathInside3(root, candidate))
|
|
34652
34945
|
return matchesPattern3(path, pattern);
|
|
34653
34946
|
return matchesPattern3(path, pattern) || matchesPattern3(relative4(root, candidate), pattern);
|
|
@@ -34994,7 +35287,8 @@ function logApprovalEvent(taskId, action, gate, agentId, db) {
|
|
|
34994
35287
|
if (action === "approved" || action === "rejected" || action === "expired") {
|
|
34995
35288
|
emitLocalEventHooksQuiet({
|
|
34996
35289
|
type: "approval.decided",
|
|
34997
|
-
payload: { task_id: taskId, gate: gate.gate, status: gate.status, reviewer: gate.reviewer, run_id: gate.run_id }
|
|
35290
|
+
payload: { task_id: taskId, gate: gate.gate, status: gate.status, reviewer: gate.reviewer, run_id: gate.run_id },
|
|
35291
|
+
databasePath: databasePathFromDatabase(db)
|
|
34998
35292
|
});
|
|
34999
35293
|
}
|
|
35000
35294
|
}
|
|
@@ -35134,6 +35428,7 @@ var init_approval_gates = __esm(() => {
|
|
|
35134
35428
|
init_task_runs();
|
|
35135
35429
|
init_tasks();
|
|
35136
35430
|
init_types();
|
|
35431
|
+
init_event_emission_safety();
|
|
35137
35432
|
init_event_hooks();
|
|
35138
35433
|
});
|
|
35139
35434
|
|
|
@@ -37700,7 +37995,7 @@ function canonicalize(value) {
|
|
|
37700
37995
|
function hash(value) {
|
|
37701
37996
|
return createHash5("sha256").update(value).digest("hex");
|
|
37702
37997
|
}
|
|
37703
|
-
function
|
|
37998
|
+
function parsePayload2(value) {
|
|
37704
37999
|
if (!value)
|
|
37705
38000
|
return {};
|
|
37706
38001
|
try {
|
|
@@ -37799,7 +38094,7 @@ function taskScopedRows(db, scope) {
|
|
|
37799
38094
|
FROM handoffs h
|
|
37800
38095
|
WHERE h.project_id IS NULL OR h.project_id = COALESCE(?, h.project_id)
|
|
37801
38096
|
`).all(scope.project_id ?? null).filter((row) => {
|
|
37802
|
-
const payload =
|
|
38097
|
+
const payload = parsePayload2(row.payload_json);
|
|
37803
38098
|
const taskRefs = parseStringArray(payload["task_ids"]);
|
|
37804
38099
|
const runRefs = parseStringArray(payload["run_ids"]);
|
|
37805
38100
|
if (scope.project_id && row.project_id !== scope.project_id)
|
|
@@ -37830,7 +38125,7 @@ function toLedgerEntries(rows) {
|
|
|
37830
38125
|
});
|
|
37831
38126
|
let previous = LOCAL_AUDIT_LEDGER_INITIAL_HASH;
|
|
37832
38127
|
return ordered.map((row, index) => {
|
|
37833
|
-
const payload =
|
|
38128
|
+
const payload = parsePayload2(row.payload_json);
|
|
37834
38129
|
const payloadHash = hash(canonicalize({ source: row.source, source_id: row.source_id, created_at: row.created_at, payload }));
|
|
37835
38130
|
const chainHash = hash(`${previous}
|
|
37836
38131
|
${payloadHash}`);
|
|
@@ -37972,7 +38267,7 @@ var init_audit_ledger = __esm(() => {
|
|
|
37972
38267
|
|
|
37973
38268
|
// src/lib/release-compatibility.ts
|
|
37974
38269
|
import { readFileSync as readFileSync6 } from "fs";
|
|
37975
|
-
import { join as join10, resolve as
|
|
38270
|
+
import { join as join10, resolve as resolve12 } from "path";
|
|
37976
38271
|
import { Database as Database2 } from "bun:sqlite";
|
|
37977
38272
|
function pass(id, message, details) {
|
|
37978
38273
|
return { id, status: "passed", message, details };
|
|
@@ -38080,7 +38375,7 @@ function checkChangelog() {
|
|
|
38080
38375
|
];
|
|
38081
38376
|
}
|
|
38082
38377
|
function createReleaseCompatibilityReport(options = {}) {
|
|
38083
|
-
const root =
|
|
38378
|
+
const root = resolve12(options.root ?? process.cwd());
|
|
38084
38379
|
const packageJson = readPackageJson2(root);
|
|
38085
38380
|
const simulatedLevels = options.simulated_levels ?? defaultSimulationLevels();
|
|
38086
38381
|
const checks4 = [
|
|
@@ -58357,7 +58652,7 @@ var require_to_json_schema = __commonJS((exports) => {
|
|
|
58357
58652
|
if (val === undefined) {
|
|
58358
58653
|
if (this.unrepresentable === "throw") {
|
|
58359
58654
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
58360
|
-
}
|
|
58655
|
+
}
|
|
58361
58656
|
} else if (typeof val === "bigint") {
|
|
58362
58657
|
if (this.unrepresentable === "throw") {
|
|
58363
58658
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -64999,7 +65294,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
64999
65294
|
return;
|
|
65000
65295
|
}
|
|
65001
65296
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
65002
|
-
await new Promise((
|
|
65297
|
+
await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
|
|
65003
65298
|
options?.signal?.throwIfAborted();
|
|
65004
65299
|
}
|
|
65005
65300
|
} catch (error2) {
|
|
@@ -65011,7 +65306,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
65011
65306
|
}
|
|
65012
65307
|
request(request, resultSchema, options) {
|
|
65013
65308
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
65014
|
-
return new Promise((
|
|
65309
|
+
return new Promise((resolve13, reject) => {
|
|
65015
65310
|
const earlyReject = (error2) => {
|
|
65016
65311
|
reject(error2);
|
|
65017
65312
|
};
|
|
@@ -65089,7 +65384,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
65089
65384
|
if (!parseResult.success) {
|
|
65090
65385
|
reject(parseResult.error);
|
|
65091
65386
|
} else {
|
|
65092
|
-
|
|
65387
|
+
resolve13(parseResult.data);
|
|
65093
65388
|
}
|
|
65094
65389
|
} catch (error2) {
|
|
65095
65390
|
reject(error2);
|
|
@@ -65280,12 +65575,12 @@ var require_protocol = __commonJS((exports) => {
|
|
|
65280
65575
|
interval = task.pollInterval;
|
|
65281
65576
|
}
|
|
65282
65577
|
} catch {}
|
|
65283
|
-
return new Promise((
|
|
65578
|
+
return new Promise((resolve13, reject) => {
|
|
65284
65579
|
if (signal.aborted) {
|
|
65285
65580
|
reject(new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, "Request cancelled"));
|
|
65286
65581
|
return;
|
|
65287
65582
|
}
|
|
65288
|
-
const timeoutId = setTimeout(
|
|
65583
|
+
const timeoutId = setTimeout(resolve13, interval);
|
|
65289
65584
|
signal.addEventListener("abort", () => {
|
|
65290
65585
|
clearTimeout(timeoutId);
|
|
65291
65586
|
reject(new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -66431,7 +66726,7 @@ var require_mcp = __commonJS((exports) => {
|
|
|
66431
66726
|
let task = createTaskResult.task;
|
|
66432
66727
|
const pollInterval = task.pollInterval ?? 5000;
|
|
66433
66728
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
66434
|
-
await new Promise((
|
|
66729
|
+
await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
|
|
66435
66730
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
66436
66731
|
if (!updatedTask) {
|
|
66437
66732
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -67840,7 +68135,7 @@ function classifyLog(text) {
|
|
|
67840
68135
|
async function sleep2(ms) {
|
|
67841
68136
|
if (ms <= 0)
|
|
67842
68137
|
return;
|
|
67843
|
-
await new Promise((
|
|
68138
|
+
await new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
67844
68139
|
}
|
|
67845
68140
|
async function runCommandProvider(provider, input) {
|
|
67846
68141
|
const commandTemplate = input.command || provider.command;
|
|
@@ -70569,7 +70864,7 @@ var init_local_bridge = __esm(() => {
|
|
|
70569
70864
|
// src/lib/local-backups.ts
|
|
70570
70865
|
import { createHash as createHash6 } from "crypto";
|
|
70571
70866
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
|
|
70572
|
-
import { dirname as dirname8, resolve as
|
|
70867
|
+
import { dirname as dirname8, resolve as resolve13 } from "path";
|
|
70573
70868
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
70574
70869
|
function stableJson(value) {
|
|
70575
70870
|
if (value === null || typeof value !== "object")
|
|
@@ -70671,14 +70966,14 @@ function createLocalBackup(options = {}, db) {
|
|
|
70671
70966
|
return backup;
|
|
70672
70967
|
}
|
|
70673
70968
|
function writeLocalBackupFile(backup, outputPath) {
|
|
70674
|
-
const path =
|
|
70969
|
+
const path = resolve13(outputPath);
|
|
70675
70970
|
mkdirSync6(dirname8(path), { recursive: true });
|
|
70676
70971
|
writeFileSync3(path, `${JSON.stringify(backup, null, 2)}
|
|
70677
70972
|
`);
|
|
70678
70973
|
return path;
|
|
70679
70974
|
}
|
|
70680
70975
|
function readLocalBackupFile(path) {
|
|
70681
|
-
return JSON.parse(readFileSync8(
|
|
70976
|
+
return JSON.parse(readFileSync8(resolve13(path), "utf-8"));
|
|
70682
70977
|
}
|
|
70683
70978
|
function verifyLocalBackup(value, options = {}, db) {
|
|
70684
70979
|
const verifiedAt = options.verified_at ?? now();
|
|
@@ -72128,7 +72423,7 @@ __export(exports_local_extensions, {
|
|
|
72128
72423
|
});
|
|
72129
72424
|
import { createHash as createHash9, createVerify } from "crypto";
|
|
72130
72425
|
import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
|
|
72131
|
-
import { basename as basename5, join as join11, resolve as
|
|
72426
|
+
import { basename as basename5, join as join11, resolve as resolve14 } from "path";
|
|
72132
72427
|
function isObject3(value) {
|
|
72133
72428
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
72134
72429
|
}
|
|
@@ -72386,7 +72681,7 @@ function verifyExtensionSignature(input) {
|
|
|
72386
72681
|
return verifier.verify(input.public_key, decodeSignature(input.signature));
|
|
72387
72682
|
}
|
|
72388
72683
|
function inspectExtensionSource(source3) {
|
|
72389
|
-
const resolved =
|
|
72684
|
+
const resolved = resolve14(source3);
|
|
72390
72685
|
if (!existsSync13(resolved))
|
|
72391
72686
|
throw new Error(`extension source not found: ${source3}`);
|
|
72392
72687
|
const stat = statSync6(resolved);
|
|
@@ -72484,7 +72779,7 @@ function testExtensionCompatibility(sourceOrManifest) {
|
|
|
72484
72779
|
function projectExtensionSources(projectPath) {
|
|
72485
72780
|
if (!projectPath)
|
|
72486
72781
|
return [];
|
|
72487
|
-
const root =
|
|
72782
|
+
const root = resolve14(projectPath);
|
|
72488
72783
|
const candidates = [
|
|
72489
72784
|
join11(root, "todos.extension.json"),
|
|
72490
72785
|
join11(root, ".todos", "todos.extension.json")
|
|
@@ -72503,7 +72798,7 @@ function projectExtensionSources(projectPath) {
|
|
|
72503
72798
|
}
|
|
72504
72799
|
function discoverLocalExtensions(options = {}) {
|
|
72505
72800
|
const config2 = loadConfig();
|
|
72506
|
-
const projectPath = options.project_path ?
|
|
72801
|
+
const projectPath = options.project_path ? resolve14(options.project_path) : null;
|
|
72507
72802
|
const configuredSources = [
|
|
72508
72803
|
...config2.extension_sources || [],
|
|
72509
72804
|
...projectPath ? config2.project_overrides?.[projectPath]?.extension_sources || [] : []
|
|
@@ -72511,7 +72806,7 @@ function discoverLocalExtensions(options = {}) {
|
|
|
72511
72806
|
const sources = Array.from(new Set([
|
|
72512
72807
|
...configuredSources,
|
|
72513
72808
|
...projectExtensionSources(projectPath || undefined)
|
|
72514
|
-
])).map((source3) => projectPath && !source3.startsWith("/") ?
|
|
72809
|
+
])).map((source3) => projectPath && !source3.startsWith("/") ? resolve14(projectPath, source3) : resolve14(source3));
|
|
72515
72810
|
const warnings = [];
|
|
72516
72811
|
const discovered = [];
|
|
72517
72812
|
for (const source3 of sources) {
|
|
@@ -75461,7 +75756,8 @@ function writeQueue(task2, queue, actor, action, db) {
|
|
|
75461
75756
|
logTaskChange(task2.id, `review_queue.${action}`, "review_queue", null, JSON.stringify(queue), actor, d);
|
|
75462
75757
|
emitLocalEventHooksQuiet({
|
|
75463
75758
|
type: `review.${action}`,
|
|
75464
|
-
payload: { task_id: task2.id, queue: queue.queue, state: queue.state, reviewer: queue.reviewer, claimed_by: queue.claimed_by }
|
|
75759
|
+
payload: { task_id: task2.id, queue: queue.queue, state: queue.state, reviewer: queue.reviewer, claimed_by: queue.claimed_by },
|
|
75760
|
+
databasePath: databasePathFromDatabase(d)
|
|
75465
75761
|
});
|
|
75466
75762
|
return itemFromTask(taskOrThrow2(task2.id, d), queue, d);
|
|
75467
75763
|
}
|
|
@@ -75765,6 +76061,7 @@ var init_review_queues = __esm(() => {
|
|
|
75765
76061
|
init_database();
|
|
75766
76062
|
init_tasks();
|
|
75767
76063
|
init_config();
|
|
76064
|
+
init_event_emission_safety();
|
|
75768
76065
|
init_event_hooks();
|
|
75769
76066
|
init_task_contracts();
|
|
75770
76067
|
STATES = new Set(["requested", "claimed", "approved", "changes_requested", "returned", "reopened"]);
|
|
@@ -76920,7 +77217,7 @@ __export(exports_extract, {
|
|
|
76920
77217
|
});
|
|
76921
77218
|
import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync7 } from "fs";
|
|
76922
77219
|
import { createHash as createHash11 } from "crypto";
|
|
76923
|
-
import { relative as relative5, resolve as
|
|
77220
|
+
import { relative as relative5, resolve as resolve15, join as join12 } from "path";
|
|
76924
77221
|
function stableHash(value) {
|
|
76925
77222
|
return createHash11("sha256").update(value).digest("hex");
|
|
76926
77223
|
}
|
|
@@ -76928,7 +77225,7 @@ function normalizePathForMatch(value) {
|
|
|
76928
77225
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
76929
77226
|
}
|
|
76930
77227
|
function readGitignorePatterns(basePath) {
|
|
76931
|
-
const root = statSync7(basePath).isFile() ?
|
|
77228
|
+
const root = statSync7(basePath).isFile() ? resolve15(basePath, "..") : basePath;
|
|
76932
77229
|
const gitignorePath = join12(root, ".gitignore");
|
|
76933
77230
|
if (!existsSync14(gitignorePath))
|
|
76934
77231
|
return [];
|
|
@@ -77064,7 +77361,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
|
|
|
77064
77361
|
return files.sort();
|
|
77065
77362
|
}
|
|
77066
77363
|
function buildCodebaseIndex(options) {
|
|
77067
|
-
const basePath =
|
|
77364
|
+
const basePath = resolve15(options.path);
|
|
77068
77365
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
77069
77366
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
77070
77367
|
const excludes = options.exclude || [];
|
|
@@ -77075,7 +77372,7 @@ function buildCodebaseIndex(options) {
|
|
|
77075
77372
|
const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
|
|
77076
77373
|
try {
|
|
77077
77374
|
const source3 = readFileSync10(fullPath, "utf-8");
|
|
77078
|
-
const relPath = statSync7(basePath).isFile() ? relative5(
|
|
77375
|
+
const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
|
|
77079
77376
|
indexed.push({
|
|
77080
77377
|
file: relPath,
|
|
77081
77378
|
checksum: stableHash(source3).slice(0, 24),
|
|
@@ -77095,7 +77392,7 @@ function buildCodebaseIndex(options) {
|
|
|
77095
77392
|
};
|
|
77096
77393
|
}
|
|
77097
77394
|
function extractTodos(options, db) {
|
|
77098
|
-
const basePath =
|
|
77395
|
+
const basePath = resolve15(options.path);
|
|
77099
77396
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
77100
77397
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
77101
77398
|
const excludes = options.exclude || [];
|
|
@@ -77106,7 +77403,7 @@ function extractTodos(options, db) {
|
|
|
77106
77403
|
const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
|
|
77107
77404
|
try {
|
|
77108
77405
|
const source3 = readFileSync10(fullPath, "utf-8");
|
|
77109
|
-
const relPath = statSync7(basePath).isFile() ? relative5(
|
|
77406
|
+
const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
|
|
77110
77407
|
const comments = extractFromSource(source3, relPath, tags);
|
|
77111
77408
|
allComments.push(...comments);
|
|
77112
77409
|
} catch {}
|
|
@@ -77200,7 +77497,7 @@ async function watchSourceTodos(options, onRun) {
|
|
|
77200
77497
|
const interval = Math.max(100, options.interval_ms || 2000);
|
|
77201
77498
|
const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
|
|
77202
77499
|
const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
|
|
77203
|
-
const root =
|
|
77500
|
+
const root = resolve15(options.path);
|
|
77204
77501
|
const runs = [];
|
|
77205
77502
|
let previous = new Map;
|
|
77206
77503
|
for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
|
|
@@ -78583,8 +78880,8 @@ __export(exports_environment_snapshots, {
|
|
|
78583
78880
|
import { createHash as createHash12 } from "crypto";
|
|
78584
78881
|
import { existsSync as existsSync15, readFileSync as readFileSync11, statSync as statSync8 } from "fs";
|
|
78585
78882
|
import { hostname as hostname3, platform, arch } from "os";
|
|
78586
|
-
import { dirname as dirname9, join as join14, resolve as
|
|
78587
|
-
import { tmpdir as
|
|
78883
|
+
import { dirname as dirname9, join as join14, resolve as resolve16 } from "path";
|
|
78884
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
78588
78885
|
function sha2566(value) {
|
|
78589
78886
|
return createHash12("sha256").update(value).digest("hex");
|
|
78590
78887
|
}
|
|
@@ -78697,15 +78994,15 @@ function commandEnv(env, includeValues) {
|
|
|
78697
78994
|
function defaultSnapshotDir() {
|
|
78698
78995
|
const dbPath = getDatabasePath();
|
|
78699
78996
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
78700
|
-
return join14(
|
|
78701
|
-
return join14(dirname9(
|
|
78997
|
+
return join14(tmpdir3(), "hasna-todos", "environment-snapshots");
|
|
78998
|
+
return join14(dirname9(resolve16(dbPath)), "environment-snapshots");
|
|
78702
78999
|
}
|
|
78703
79000
|
function snapshotWithId(snapshot) {
|
|
78704
79001
|
const digest = sha2566(JSON.stringify(snapshot)).slice(0, 24);
|
|
78705
79002
|
return { id: `env_${digest}`, ...snapshot };
|
|
78706
79003
|
}
|
|
78707
79004
|
function captureEnvironmentSnapshot(input = {}) {
|
|
78708
|
-
const root =
|
|
79005
|
+
const root = resolve16(input.root || process.cwd());
|
|
78709
79006
|
const env = input.env || process.env;
|
|
78710
79007
|
const warnings = [];
|
|
78711
79008
|
const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
|
|
@@ -78745,13 +79042,13 @@ function captureEnvironmentSnapshot(input = {}) {
|
|
|
78745
79042
|
});
|
|
78746
79043
|
}
|
|
78747
79044
|
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
78748
|
-
const path = outputPath ?
|
|
79045
|
+
const path = outputPath ? resolve16(outputPath) : join14(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
78749
79046
|
ensureDir2(dirname9(path));
|
|
78750
79047
|
writeJsonFile(path, snapshot);
|
|
78751
79048
|
return path;
|
|
78752
79049
|
}
|
|
78753
79050
|
function readEnvironmentSnapshot(path) {
|
|
78754
|
-
const snapshot = readJsonFile(
|
|
79051
|
+
const snapshot = readJsonFile(resolve16(path));
|
|
78755
79052
|
if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
|
|
78756
79053
|
throw new Error(`Invalid environment snapshot: ${path}`);
|
|
78757
79054
|
}
|