@hasna/todos 0.11.66 → 0.11.68
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 +2 -2
- 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/storage-commands.d.ts +5 -0
- package/dist/cli/commands/storage-commands.d.ts.map +1 -1
- package/dist/cli/index.js +8811 -2900
- package/dist/contracts.js +301 -71
- 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.js +628 -160
- 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-panel.d.ts +8 -0
- package/dist/lib/project-panel.d.ts.map +1 -0
- 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/mcp/index.js +343 -104
- package/dist/registry.js +301 -71
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +366 -127
- 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 +569 -94
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -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,6 +4942,7 @@ 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
4948
|
// src/lib/task-route-contract.ts
|
|
@@ -4975,6 +5146,8 @@ function readMachineLocalPath(project) {
|
|
|
4975
5146
|
}
|
|
4976
5147
|
}
|
|
4977
5148
|
async function emitSharedTaskEvent(input) {
|
|
5149
|
+
if (!shouldEmitSharedTaskEvents(input.databasePath))
|
|
5150
|
+
return;
|
|
4978
5151
|
const data = taskEventData(input.task, input.data);
|
|
4979
5152
|
await new EventsClient().emit({
|
|
4980
5153
|
source: SOURCE,
|
|
@@ -4998,6 +5171,7 @@ var init_shared_events = __esm(() => {
|
|
|
4998
5171
|
init_database();
|
|
4999
5172
|
init_projects();
|
|
5000
5173
|
init_task_lists();
|
|
5174
|
+
init_event_emission_safety();
|
|
5001
5175
|
});
|
|
5002
5176
|
|
|
5003
5177
|
// src/lib/secret-redaction.ts
|
|
@@ -5246,8 +5420,9 @@ function logTaskChange(taskId, action, field, oldValue, newValue, agentId, db) {
|
|
|
5246
5420
|
const d = db || getDatabase();
|
|
5247
5421
|
const id = uuid();
|
|
5248
5422
|
const timestamp = now();
|
|
5249
|
-
|
|
5250
|
-
|
|
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]);
|
|
5251
5426
|
try {
|
|
5252
5427
|
const { logActivity: logActivity2 } = (init_activity_audit(), __toCommonJS(exports_activity_audit));
|
|
5253
5428
|
logActivity2({
|
|
@@ -5260,7 +5435,7 @@ function logTaskChange(taskId, action, field, oldValue, newValue, agentId, db) {
|
|
|
5260
5435
|
actor_id: agentId ?? undefined
|
|
5261
5436
|
}, d);
|
|
5262
5437
|
} catch {}
|
|
5263
|
-
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 };
|
|
5264
5439
|
}
|
|
5265
5440
|
function getTaskHistory(taskId, db) {
|
|
5266
5441
|
const d = db || getDatabase();
|
|
@@ -5272,6 +5447,7 @@ function getRecentActivity(limit = 50, db) {
|
|
|
5272
5447
|
}
|
|
5273
5448
|
var init_audit = __esm(() => {
|
|
5274
5449
|
init_database();
|
|
5450
|
+
init_storage_tombstones();
|
|
5275
5451
|
});
|
|
5276
5452
|
|
|
5277
5453
|
// src/db/webhooks.ts
|
|
@@ -5537,13 +5713,14 @@ function createTask(input, db) {
|
|
|
5537
5713
|
const d = db || getDatabase();
|
|
5538
5714
|
const timestamp = now();
|
|
5539
5715
|
const tags = input.tags || [];
|
|
5716
|
+
const machineId = currentStorageMachineId(d);
|
|
5540
5717
|
const assignedBy = input.assigned_by || input.agent_id;
|
|
5541
5718
|
const assignedFromProject = input.assigned_from_project || null;
|
|
5542
5719
|
let id = uuid();
|
|
5543
5720
|
for (let attempt = 0;attempt < 3; attempt++) {
|
|
5544
5721
|
try {
|
|
5545
|
-
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)
|
|
5546
|
-
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, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5547
5724
|
id,
|
|
5548
5725
|
null,
|
|
5549
5726
|
input.project_id || null,
|
|
@@ -5580,7 +5757,8 @@ function createTask(input, db) {
|
|
|
5580
5757
|
input.spawned_from_session || null,
|
|
5581
5758
|
assignedBy || null,
|
|
5582
5759
|
assignedFromProject || null,
|
|
5583
|
-
input.task_type || null
|
|
5760
|
+
input.task_type || null,
|
|
5761
|
+
machineId
|
|
5584
5762
|
]);
|
|
5585
5763
|
break;
|
|
5586
5764
|
} catch (e) {
|
|
@@ -5596,9 +5774,10 @@ function createTask(input, db) {
|
|
|
5596
5774
|
}
|
|
5597
5775
|
const task = getTask(id, d);
|
|
5598
5776
|
const payload = taskEventData(task);
|
|
5777
|
+
const databasePath = databasePathFromDatabase(d);
|
|
5599
5778
|
dispatchWebhook2("task.created", payload, d).catch(() => {});
|
|
5600
|
-
emitLocalEventHooksQuiet({ type: "task.created", payload });
|
|
5601
|
-
emitSharedTaskEventQuiet({ type: "task.created", task });
|
|
5779
|
+
emitLocalEventHooksQuiet({ type: "task.created", payload, databasePath });
|
|
5780
|
+
emitSharedTaskEventQuiet({ type: "task.created", task, databasePath });
|
|
5602
5781
|
return task;
|
|
5603
5782
|
}
|
|
5604
5783
|
function getTask(id, db) {
|
|
@@ -6016,29 +6195,39 @@ function updateTask(id, input, db) {
|
|
|
6016
6195
|
approved_by: input.approved_by ?? task.approved_by,
|
|
6017
6196
|
approved_at: input.approved_by ? timestamp : task.approved_at
|
|
6018
6197
|
};
|
|
6198
|
+
const databasePath = databasePathFromDatabase(d);
|
|
6019
6199
|
if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to) {
|
|
6020
6200
|
const payload = taskEventData(updatedTask, { assigned_to: input.assigned_to, old_assigned_to: task.assigned_to });
|
|
6021
6201
|
dispatchWebhook2("task.assigned", payload, d).catch(() => {});
|
|
6022
|
-
emitLocalEventHooksQuiet({ type: "task.assigned", payload });
|
|
6023
|
-
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 });
|
|
6024
6204
|
}
|
|
6025
6205
|
if (input.status !== undefined && input.status !== task.status) {
|
|
6026
6206
|
const payload = taskEventData(updatedTask, { old_status: task.status, new_status: input.status });
|
|
6027
6207
|
dispatchWebhook2("task.status_changed", payload, d).catch(() => {});
|
|
6028
|
-
emitLocalEventHooksQuiet({ type: "task.status_changed", payload });
|
|
6029
|
-
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 });
|
|
6030
6210
|
}
|
|
6031
6211
|
if (input.approved_by !== undefined) {
|
|
6032
|
-
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 });
|
|
6033
6213
|
}
|
|
6034
6214
|
const updatePayload = taskEventData(updatedTask);
|
|
6035
6215
|
dispatchWebhook2("task.updated", updatePayload, d).catch(() => {});
|
|
6036
|
-
emitLocalEventHooksQuiet({ type: "task.updated", payload: updatePayload });
|
|
6037
|
-
emitSharedTaskEventQuiet({ type: "task.updated", task: updatedTask });
|
|
6216
|
+
emitLocalEventHooksQuiet({ type: "task.updated", payload: updatePayload, databasePath });
|
|
6217
|
+
emitSharedTaskEventQuiet({ type: "task.updated", task: updatedTask, databasePath });
|
|
6038
6218
|
return updatedTask;
|
|
6039
6219
|
}
|
|
6040
6220
|
function deleteTask(id, db) {
|
|
6041
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);
|
|
6042
6231
|
const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
|
|
6043
6232
|
return result.changes > 0;
|
|
6044
6233
|
}
|
|
@@ -6046,11 +6235,13 @@ var init_task_crud = __esm(() => {
|
|
|
6046
6235
|
init_types();
|
|
6047
6236
|
init_database();
|
|
6048
6237
|
init_completion_guard();
|
|
6238
|
+
init_event_emission_safety();
|
|
6049
6239
|
init_event_hooks();
|
|
6050
6240
|
init_shared_events();
|
|
6051
6241
|
init_audit();
|
|
6052
6242
|
init_webhooks();
|
|
6053
6243
|
init_checklists();
|
|
6244
|
+
init_storage_tombstones();
|
|
6054
6245
|
});
|
|
6055
6246
|
|
|
6056
6247
|
// src/lib/recurrence.ts
|
|
@@ -6199,8 +6390,9 @@ function resolveTemplateId(id, d) {
|
|
|
6199
6390
|
function createTemplate(input, db) {
|
|
6200
6391
|
const d = db || getDatabase();
|
|
6201
6392
|
const id = uuid();
|
|
6202
|
-
|
|
6203
|
-
|
|
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
6204
6396
|
id,
|
|
6205
6397
|
input.name,
|
|
6206
6398
|
input.title_pattern,
|
|
@@ -6211,7 +6403,8 @@ function createTemplate(input, db) {
|
|
|
6211
6403
|
input.project_id || null,
|
|
6212
6404
|
input.plan_id || null,
|
|
6213
6405
|
JSON.stringify(input.metadata || {}),
|
|
6214
|
-
now()
|
|
6406
|
+
now(),
|
|
6407
|
+
machineId
|
|
6215
6408
|
]);
|
|
6216
6409
|
if (input.tasks && input.tasks.length > 0) {
|
|
6217
6410
|
addTemplateTasks(id, input.tasks, d);
|
|
@@ -6235,6 +6428,15 @@ function deleteTemplate(id, db) {
|
|
|
6235
6428
|
const resolved = resolveTemplateId(id, d);
|
|
6236
6429
|
if (!resolved)
|
|
6237
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);
|
|
6238
6440
|
return d.run("DELETE FROM task_templates WHERE id = ?", [resolved]).changes > 0;
|
|
6239
6441
|
}
|
|
6240
6442
|
function updateTemplate(id, updates, db) {
|
|
@@ -6604,6 +6806,7 @@ function previewTemplate(templateId, variables, db) {
|
|
|
6604
6806
|
var init_templates = __esm(() => {
|
|
6605
6807
|
init_database();
|
|
6606
6808
|
init_tasks();
|
|
6809
|
+
init_storage_tombstones();
|
|
6607
6810
|
});
|
|
6608
6811
|
|
|
6609
6812
|
// src/db/task-graph.ts
|
|
@@ -6770,6 +6973,7 @@ function getBlockingDeps(id, db) {
|
|
|
6770
6973
|
}
|
|
6771
6974
|
function startTask(id, agentId, db) {
|
|
6772
6975
|
const d = db || getDatabase();
|
|
6976
|
+
const databasePath = databasePathFromDatabase(d);
|
|
6773
6977
|
const task = getTask(id, d);
|
|
6774
6978
|
if (!task)
|
|
6775
6979
|
throw new TaskNotFoundError(id);
|
|
@@ -6784,7 +6988,8 @@ function startTask(id, agentId, db) {
|
|
|
6784
6988
|
agent_id: agentId,
|
|
6785
6989
|
title: task.title,
|
|
6786
6990
|
blockers: blocking.map((b) => ({ id: b.id, short_id: b.short_id, title: b.title, status: b.status }))
|
|
6787
|
-
}
|
|
6991
|
+
},
|
|
6992
|
+
databasePath
|
|
6788
6993
|
});
|
|
6789
6994
|
throw new Error(`Task is blocked by ${blocking.length} unfinished dependency(ies): ${blockerIds}`);
|
|
6790
6995
|
}
|
|
@@ -6806,12 +7011,13 @@ function startTask(id, agentId, db) {
|
|
|
6806
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 };
|
|
6807
7012
|
const payload = taskEventData(startedTask, { agent_id: agentId });
|
|
6808
7013
|
dispatchWebhook2("task.started", payload, d).catch(() => {});
|
|
6809
|
-
emitLocalEventHooksQuiet({ type: "task.started", payload });
|
|
6810
|
-
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 });
|
|
6811
7016
|
return startedTask;
|
|
6812
7017
|
}
|
|
6813
7018
|
function completeTask(id, agentId, db, options) {
|
|
6814
7019
|
const d = db || getDatabase();
|
|
7020
|
+
const databasePath = databasePathFromDatabase(d);
|
|
6815
7021
|
const task = getTask(id, d);
|
|
6816
7022
|
if (!task)
|
|
6817
7023
|
throw new TaskNotFoundError(id);
|
|
@@ -6857,8 +7063,8 @@ function completeTask(id, agentId, db, options) {
|
|
|
6857
7063
|
};
|
|
6858
7064
|
const completionPayload = taskEventData(completedTaskForEvent, { agent_id: agentId, completed_at: timestamp });
|
|
6859
7065
|
dispatchWebhook2("task.completed", completionPayload, d).catch(() => {});
|
|
6860
|
-
emitLocalEventHooksQuiet({ type: "task.completed", payload: completionPayload });
|
|
6861
|
-
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 });
|
|
6862
7068
|
let spawnedTask = null;
|
|
6863
7069
|
if (task.recurrence_rule && !options?.skip_recurrence) {
|
|
6864
7070
|
spawnedTask = spawnNextRecurrence(task, d, timestamp);
|
|
@@ -6902,9 +7108,9 @@ function completeTask(id, agentId, db, options) {
|
|
|
6902
7108
|
const depTask = getTask(dep.id, d);
|
|
6903
7109
|
const payload = depTask ? taskEventData(depTask, { unblocked_by: id }) : { id: dep.id, unblocked_by: id, title: dep.title };
|
|
6904
7110
|
dispatchWebhook2("task.unblocked", payload, d).catch(() => {});
|
|
6905
|
-
emitLocalEventHooksQuiet({ type: "task.unblocked", payload });
|
|
7111
|
+
emitLocalEventHooksQuiet({ type: "task.unblocked", payload, databasePath });
|
|
6906
7112
|
if (depTask)
|
|
6907
|
-
emitSharedTaskEventQuiet({ type: "task.unblocked", task: depTask, data: { unblocked_by: id } });
|
|
7113
|
+
emitSharedTaskEventQuiet({ type: "task.unblocked", task: depTask, data: { unblocked_by: id }, databasePath });
|
|
6908
7114
|
}
|
|
6909
7115
|
}
|
|
6910
7116
|
return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, confidence, version: task.version + 1, updated_at: timestamp, metadata: meta };
|
|
@@ -7074,6 +7280,7 @@ function getTasksChangedSince(since, filters, db) {
|
|
|
7074
7280
|
}
|
|
7075
7281
|
function failTask(id, agentId, reason, options, db) {
|
|
7076
7282
|
const d = db || getDatabase();
|
|
7283
|
+
const databasePath = databasePathFromDatabase(d);
|
|
7077
7284
|
const task = getTask(id, d);
|
|
7078
7285
|
if (!task)
|
|
7079
7286
|
throw new TaskNotFoundError(id);
|
|
@@ -7102,8 +7309,8 @@ function failTask(id, agentId, reason, options, db) {
|
|
|
7102
7309
|
logTaskChange(id, "fail", "status", task.status, "failed", agentId || null, d);
|
|
7103
7310
|
const failurePayload = taskEventData(failedTask, { reason, error_code: options?.error_code, agent_id: agentId });
|
|
7104
7311
|
dispatchWebhook2("task.failed", failurePayload, d).catch(() => {});
|
|
7105
|
-
emitLocalEventHooksQuiet({ type: "task.failed", payload: failurePayload });
|
|
7106
|
-
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 });
|
|
7107
7314
|
let retryTask;
|
|
7108
7315
|
if (options?.retry) {
|
|
7109
7316
|
const retryCount = (task.retry_count || 0) + 1;
|
|
@@ -7163,6 +7370,7 @@ function getStaleTasks(staleQuery = 30, filters, db) {
|
|
|
7163
7370
|
}
|
|
7164
7371
|
function stealTask(agentId, opts, db) {
|
|
7165
7372
|
const d = db || getDatabase();
|
|
7373
|
+
const databasePath = databasePathFromDatabase(d);
|
|
7166
7374
|
const staleMinutes = opts?.stale_minutes ?? 30;
|
|
7167
7375
|
const staleTasks = getStaleTasks(staleMinutes, { project_id: opts?.project_id, task_list_id: opts?.task_list_id }, d);
|
|
7168
7376
|
if (staleTasks.length === 0)
|
|
@@ -7181,8 +7389,8 @@ function stealTask(agentId, opts, db) {
|
|
|
7181
7389
|
const stolenTask = { ...target, assigned_to: agentId, locked_by: agentId, locked_at: timestamp, updated_at: timestamp, version: target.version + 1 };
|
|
7182
7390
|
const payload = taskEventData(stolenTask, { agent_id: agentId, stolen_from: target.assigned_to });
|
|
7183
7391
|
dispatchWebhook2("task.assigned", payload, d).catch(() => {});
|
|
7184
|
-
emitLocalEventHooksQuiet({ type: "task.assigned", payload });
|
|
7185
|
-
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 });
|
|
7186
7394
|
return stolenTask;
|
|
7187
7395
|
}
|
|
7188
7396
|
function claimOrSteal(agentId, filters, db) {
|
|
@@ -7230,6 +7438,7 @@ var init_task_lifecycle = __esm(() => {
|
|
|
7230
7438
|
init_types();
|
|
7231
7439
|
init_database();
|
|
7232
7440
|
init_completion_guard();
|
|
7441
|
+
init_event_emission_safety();
|
|
7233
7442
|
init_event_hooks();
|
|
7234
7443
|
init_shared_events();
|
|
7235
7444
|
init_audit();
|
|
@@ -7937,8 +8146,9 @@ function createPlan(input, db) {
|
|
|
7937
8146
|
const d = db || getDatabase();
|
|
7938
8147
|
const id = uuid();
|
|
7939
8148
|
const timestamp = now();
|
|
7940
|
-
|
|
7941
|
-
|
|
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
7942
8152
|
id,
|
|
7943
8153
|
input.project_id || null,
|
|
7944
8154
|
input.task_list_id || null,
|
|
@@ -7947,7 +8157,8 @@ function createPlan(input, db) {
|
|
|
7947
8157
|
input.description || null,
|
|
7948
8158
|
input.status || "active",
|
|
7949
8159
|
timestamp,
|
|
7950
|
-
timestamp
|
|
8160
|
+
timestamp,
|
|
8161
|
+
machineId
|
|
7951
8162
|
]);
|
|
7952
8163
|
return getPlan(id, d);
|
|
7953
8164
|
}
|
|
@@ -7995,19 +8206,30 @@ function updatePlan(id, input, db) {
|
|
|
7995
8206
|
const updated = getPlan(id, d);
|
|
7996
8207
|
emitLocalEventHooksQuiet({
|
|
7997
8208
|
type: "plan.updated",
|
|
7998
|
-
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)
|
|
7999
8211
|
});
|
|
8000
8212
|
return updated;
|
|
8001
8213
|
}
|
|
8002
8214
|
function deletePlan(id, db) {
|
|
8003
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);
|
|
8004
8224
|
const result = d.run("DELETE FROM plans WHERE id = ?", [id]);
|
|
8005
8225
|
return result.changes > 0;
|
|
8006
8226
|
}
|
|
8007
8227
|
var init_plans = __esm(() => {
|
|
8008
8228
|
init_types();
|
|
8229
|
+
init_event_emission_safety();
|
|
8009
8230
|
init_event_hooks();
|
|
8010
8231
|
init_database();
|
|
8232
|
+
init_storage_tombstones();
|
|
8011
8233
|
});
|
|
8012
8234
|
|
|
8013
8235
|
// src/db/boards.ts
|
|
@@ -8414,20 +8636,20 @@ var init_boards = __esm(() => {
|
|
|
8414
8636
|
// src/lib/artifact-store.ts
|
|
8415
8637
|
import { createHash as createHash3 } from "crypto";
|
|
8416
8638
|
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
8417
|
-
import { basename, dirname as dirname5, join as join6, resolve as
|
|
8418
|
-
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";
|
|
8419
8641
|
function isInMemoryDb2(path) {
|
|
8420
8642
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
8421
8643
|
}
|
|
8422
8644
|
function artifactStoreRoot() {
|
|
8423
8645
|
if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
|
|
8424
|
-
return
|
|
8646
|
+
return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
|
|
8425
8647
|
if (process.env["TODOS_ARTIFACTS_DIR"])
|
|
8426
|
-
return
|
|
8648
|
+
return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
8427
8649
|
const dbPath = getDatabasePath();
|
|
8428
8650
|
if (isInMemoryDb2(dbPath))
|
|
8429
|
-
return join6(
|
|
8430
|
-
return join6(dirname5(
|
|
8651
|
+
return join6(tmpdir2(), "hasna-todos-artifacts");
|
|
8652
|
+
return join6(dirname5(resolve7(dbPath)), "artifacts");
|
|
8431
8653
|
}
|
|
8432
8654
|
function artifactStorePath(relativePath) {
|
|
8433
8655
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
@@ -8474,7 +8696,7 @@ function mediaTypeFor(path, textLike) {
|
|
|
8474
8696
|
return "application/octet-stream";
|
|
8475
8697
|
}
|
|
8476
8698
|
function storeArtifactContent(input) {
|
|
8477
|
-
const sourcePath =
|
|
8699
|
+
const sourcePath = resolve7(input.path);
|
|
8478
8700
|
if (!existsSync7(sourcePath))
|
|
8479
8701
|
return null;
|
|
8480
8702
|
const sourceStat = statSync2(sourcePath);
|
|
@@ -9171,7 +9393,11 @@ function startTaskRun(input, db) {
|
|
|
9171
9393
|
}, d);
|
|
9172
9394
|
}
|
|
9173
9395
|
const run = getTaskRun(id, d);
|
|
9174
|
-
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
|
+
});
|
|
9175
9401
|
return run;
|
|
9176
9402
|
}
|
|
9177
9403
|
function beginTaskRunTransaction(input, db) {
|
|
@@ -9440,7 +9666,8 @@ function finishTaskRun(input, db) {
|
|
|
9440
9666
|
const updated = getTaskRun(run.id, d);
|
|
9441
9667
|
emitLocalEventHooksQuiet({
|
|
9442
9668
|
type: `run.${input.status}`,
|
|
9443
|
-
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)
|
|
9444
9671
|
});
|
|
9445
9672
|
return updated;
|
|
9446
9673
|
}
|
|
@@ -9524,6 +9751,7 @@ function getTaskRunLedger(runId, db) {
|
|
|
9524
9751
|
var LOOP_RUN_TRANSACTION_SCHEMA_VERSION = "todos.loop_run_transaction.v1";
|
|
9525
9752
|
var init_task_runs = __esm(() => {
|
|
9526
9753
|
init_artifact_store();
|
|
9754
|
+
init_event_emission_safety();
|
|
9527
9755
|
init_event_hooks();
|
|
9528
9756
|
init_redaction();
|
|
9529
9757
|
init_types();
|
|
@@ -10410,6 +10638,7 @@ function rowToAgent(row) {
|
|
|
10410
10638
|
}
|
|
10411
10639
|
function registerAgent(input, db) {
|
|
10412
10640
|
const d = db || getDatabase();
|
|
10641
|
+
const machineId = currentStorageMachineId(d);
|
|
10413
10642
|
const existingNames = d.query("SELECT name FROM agents").all().map((row) => row.name);
|
|
10414
10643
|
const normalizedName = validateAgentName(input.name, existingNames);
|
|
10415
10644
|
const existing = getAgentByName(normalizedName, d);
|
|
@@ -10450,14 +10679,18 @@ function registerAgent(input, db) {
|
|
|
10450
10679
|
updates.push("active_project_id = ?");
|
|
10451
10680
|
params.push(input.project_id);
|
|
10452
10681
|
}
|
|
10682
|
+
if (!existing.machine_id && machineId) {
|
|
10683
|
+
updates.push("machine_id = ?");
|
|
10684
|
+
params.push(machineId);
|
|
10685
|
+
}
|
|
10453
10686
|
params.push(existing.id);
|
|
10454
10687
|
d.run(`UPDATE agents SET ${updates.join(", ")} WHERE id = ?`, params);
|
|
10455
10688
|
return getAgent(existing.id, d);
|
|
10456
10689
|
}
|
|
10457
10690
|
const id = shortUuid();
|
|
10458
10691
|
const timestamp = now();
|
|
10459
|
-
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)
|
|
10460
|
-
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
10461
10694
|
id,
|
|
10462
10695
|
normalizedName,
|
|
10463
10696
|
input.description || null,
|
|
@@ -10473,7 +10706,8 @@ function registerAgent(input, db) {
|
|
|
10473
10706
|
timestamp,
|
|
10474
10707
|
input.session_id || null,
|
|
10475
10708
|
input.working_dir || null,
|
|
10476
|
-
input.project_id && input.session_id ? input.project_id : null
|
|
10709
|
+
input.project_id && input.session_id ? input.project_id : null,
|
|
10710
|
+
machineId
|
|
10477
10711
|
]);
|
|
10478
10712
|
return getAgent(id, d);
|
|
10479
10713
|
}
|
|
@@ -10656,6 +10890,7 @@ function getCapableAgents(capabilities, opts, db) {
|
|
|
10656
10890
|
}
|
|
10657
10891
|
var init_agents = __esm(() => {
|
|
10658
10892
|
init_database();
|
|
10893
|
+
init_storage_tombstones();
|
|
10659
10894
|
init_agent_names();
|
|
10660
10895
|
});
|
|
10661
10896
|
|
|
@@ -11191,7 +11426,7 @@ var init_doctor = __esm(() => {
|
|
|
11191
11426
|
});
|
|
11192
11427
|
|
|
11193
11428
|
// src/server/routes.ts
|
|
11194
|
-
import { join as join8, resolve as
|
|
11429
|
+
import { join as join8, resolve as resolve8, sep as sep2 } from "path";
|
|
11195
11430
|
function parseFieldsParam(url) {
|
|
11196
11431
|
const fieldsParam = url.searchParams.get("fields");
|
|
11197
11432
|
return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
|
|
@@ -11929,9 +12164,9 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
|
|
|
11929
12164
|
return null;
|
|
11930
12165
|
if (path !== "/") {
|
|
11931
12166
|
const filePath = join8(ctx.dashboardDir, path);
|
|
11932
|
-
const resolvedFile =
|
|
11933
|
-
const resolvedBase =
|
|
11934
|
-
if (!resolvedFile.startsWith(resolvedBase +
|
|
12167
|
+
const resolvedFile = resolve8(filePath);
|
|
12168
|
+
const resolvedBase = resolve8(ctx.dashboardDir);
|
|
12169
|
+
if (!resolvedFile.startsWith(resolvedBase + sep2) && resolvedFile !== resolvedBase) {
|
|
11935
12170
|
return json2({ error: "Forbidden" }, 403);
|
|
11936
12171
|
}
|
|
11937
12172
|
const res2 = serveStaticFile2(filePath);
|
|
@@ -15330,7 +15565,7 @@ class JSONSchemaGenerator {
|
|
|
15330
15565
|
if (val === undefined) {
|
|
15331
15566
|
if (this.unrepresentable === "throw") {
|
|
15332
15567
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
15333
|
-
}
|
|
15568
|
+
}
|
|
15334
15569
|
} else if (typeof val === "bigint") {
|
|
15335
15570
|
if (this.unrepresentable === "throw") {
|
|
15336
15571
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -17696,9 +17931,9 @@ data:
|
|
|
17696
17931
|
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
17697
17932
|
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
17698
17933
|
if (this._enableJsonResponse) {
|
|
17699
|
-
return new Promise((
|
|
17934
|
+
return new Promise((resolve9) => {
|
|
17700
17935
|
this._streamMapping.set(streamId, {
|
|
17701
|
-
resolveJson:
|
|
17936
|
+
resolveJson: resolve9,
|
|
17702
17937
|
cleanup: () => {
|
|
17703
17938
|
this._streamMapping.delete(streamId);
|
|
17704
17939
|
}
|
|
@@ -23998,7 +24233,7 @@ class Protocol {
|
|
|
23998
24233
|
return;
|
|
23999
24234
|
}
|
|
24000
24235
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
24001
|
-
await new Promise((
|
|
24236
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
24002
24237
|
options?.signal?.throwIfAborted();
|
|
24003
24238
|
}
|
|
24004
24239
|
} catch (error2) {
|
|
@@ -24010,7 +24245,7 @@ class Protocol {
|
|
|
24010
24245
|
}
|
|
24011
24246
|
request(request, resultSchema, options) {
|
|
24012
24247
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
24013
|
-
return new Promise((
|
|
24248
|
+
return new Promise((resolve9, reject) => {
|
|
24014
24249
|
const earlyReject = (error2) => {
|
|
24015
24250
|
reject(error2);
|
|
24016
24251
|
};
|
|
@@ -24088,7 +24323,7 @@ class Protocol {
|
|
|
24088
24323
|
if (!parseResult.success) {
|
|
24089
24324
|
reject(parseResult.error);
|
|
24090
24325
|
} else {
|
|
24091
|
-
|
|
24326
|
+
resolve9(parseResult.data);
|
|
24092
24327
|
}
|
|
24093
24328
|
} catch (error2) {
|
|
24094
24329
|
reject(error2);
|
|
@@ -24279,12 +24514,12 @@ class Protocol {
|
|
|
24279
24514
|
interval = task.pollInterval;
|
|
24280
24515
|
}
|
|
24281
24516
|
} catch {}
|
|
24282
|
-
return new Promise((
|
|
24517
|
+
return new Promise((resolve9, reject) => {
|
|
24283
24518
|
if (signal.aborted) {
|
|
24284
24519
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
24285
24520
|
return;
|
|
24286
24521
|
}
|
|
24287
|
-
const timeoutId = setTimeout(
|
|
24522
|
+
const timeoutId = setTimeout(resolve9, interval);
|
|
24288
24523
|
signal.addEventListener("abort", () => {
|
|
24289
24524
|
clearTimeout(timeoutId);
|
|
24290
24525
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -27269,7 +27504,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
27269
27504
|
const schOrFunc = root.refs[ref];
|
|
27270
27505
|
if (schOrFunc)
|
|
27271
27506
|
return schOrFunc;
|
|
27272
|
-
let _sch =
|
|
27507
|
+
let _sch = resolve9.call(this, root, ref);
|
|
27273
27508
|
if (_sch === undefined) {
|
|
27274
27509
|
const schema = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
|
|
27275
27510
|
const { schemaId } = this.opts;
|
|
@@ -27296,7 +27531,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
27296
27531
|
function sameSchemaEnv(s1, s2) {
|
|
27297
27532
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
27298
27533
|
}
|
|
27299
|
-
function
|
|
27534
|
+
function resolve9(root, ref) {
|
|
27300
27535
|
let sch;
|
|
27301
27536
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
27302
27537
|
ref = sch;
|
|
@@ -27826,7 +28061,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
27826
28061
|
}
|
|
27827
28062
|
return uri;
|
|
27828
28063
|
}
|
|
27829
|
-
function
|
|
28064
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
27830
28065
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
27831
28066
|
const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
27832
28067
|
schemelessOptions.skipEscape = true;
|
|
@@ -28054,7 +28289,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
28054
28289
|
var fastUri = {
|
|
28055
28290
|
SCHEMES,
|
|
28056
28291
|
normalize,
|
|
28057
|
-
resolve:
|
|
28292
|
+
resolve: resolve9,
|
|
28058
28293
|
resolveComponent,
|
|
28059
28294
|
equal,
|
|
28060
28295
|
serialize,
|
|
@@ -31580,7 +31815,7 @@ class McpServer {
|
|
|
31580
31815
|
let task = createTaskResult.task;
|
|
31581
31816
|
const pollInterval = task.pollInterval ?? 5000;
|
|
31582
31817
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
31583
|
-
await new Promise((
|
|
31818
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
|
|
31584
31819
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
31585
31820
|
if (!updatedTask) {
|
|
31586
31821
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -32204,12 +32439,12 @@ class StdioServerTransport {
|
|
|
32204
32439
|
this.onclose?.();
|
|
32205
32440
|
}
|
|
32206
32441
|
send(message) {
|
|
32207
|
-
return new Promise((
|
|
32442
|
+
return new Promise((resolve9) => {
|
|
32208
32443
|
const json2 = serializeMessage(message);
|
|
32209
32444
|
if (this._stdout.write(json2)) {
|
|
32210
|
-
|
|
32445
|
+
resolve9();
|
|
32211
32446
|
} else {
|
|
32212
|
-
this._stdout.once("drain",
|
|
32447
|
+
this._stdout.once("drain", resolve9);
|
|
32213
32448
|
}
|
|
32214
32449
|
});
|
|
32215
32450
|
}
|
|
@@ -33616,7 +33851,7 @@ var init_task_crud2 = __esm(() => {
|
|
|
33616
33851
|
|
|
33617
33852
|
// src/lib/project-bootstrap.ts
|
|
33618
33853
|
import { existsSync as existsSync9, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
33619
|
-
import { basename as basename3, dirname as dirname7, resolve as
|
|
33854
|
+
import { basename as basename3, dirname as dirname7, resolve as resolve9 } from "path";
|
|
33620
33855
|
function safeStat(path) {
|
|
33621
33856
|
try {
|
|
33622
33857
|
return statSync4(path);
|
|
@@ -33625,7 +33860,7 @@ function safeStat(path) {
|
|
|
33625
33860
|
}
|
|
33626
33861
|
}
|
|
33627
33862
|
function canonicalPath(input) {
|
|
33628
|
-
const resolved =
|
|
33863
|
+
const resolved = resolve9(input);
|
|
33629
33864
|
const stats = safeStat(resolved);
|
|
33630
33865
|
if (stats?.isFile())
|
|
33631
33866
|
return dirname7(resolved);
|
|
@@ -33634,7 +33869,7 @@ function canonicalPath(input) {
|
|
|
33634
33869
|
function findUp(start, marker) {
|
|
33635
33870
|
let current = canonicalPath(start);
|
|
33636
33871
|
while (true) {
|
|
33637
|
-
if (existsSync9(
|
|
33872
|
+
if (existsSync9(resolve9(current, marker)))
|
|
33638
33873
|
return current;
|
|
33639
33874
|
const parent = dirname7(current);
|
|
33640
33875
|
if (parent === current)
|
|
@@ -33645,7 +33880,7 @@ function findUp(start, marker) {
|
|
|
33645
33880
|
function readPackageJson(path) {
|
|
33646
33881
|
if (!path)
|
|
33647
33882
|
return null;
|
|
33648
|
-
const file =
|
|
33883
|
+
const file = resolve9(path, "package.json");
|
|
33649
33884
|
if (!existsSync9(file))
|
|
33650
33885
|
return null;
|
|
33651
33886
|
try {
|
|
@@ -33668,7 +33903,7 @@ function workspaceMarker(root, rootPackage) {
|
|
|
33668
33903
|
if (rootPackage?.workspaces)
|
|
33669
33904
|
markers.push("package.json#workspaces");
|
|
33670
33905
|
for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
|
|
33671
|
-
if (existsSync9(
|
|
33906
|
+
if (existsSync9(resolve9(root, marker)))
|
|
33672
33907
|
markers.push(marker);
|
|
33673
33908
|
}
|
|
33674
33909
|
const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
|
|
@@ -34212,7 +34447,7 @@ var init_retention_cleanup = __esm(() => {
|
|
|
34212
34447
|
|
|
34213
34448
|
// src/lib/mention-resolver.ts
|
|
34214
34449
|
import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
|
|
34215
|
-
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";
|
|
34216
34451
|
function blankResolution(parsed) {
|
|
34217
34452
|
return {
|
|
34218
34453
|
input: parsed.input,
|
|
@@ -34235,11 +34470,11 @@ function backlink(kind, key, label, target = key) {
|
|
|
34235
34470
|
return { kind, key, label, target };
|
|
34236
34471
|
}
|
|
34237
34472
|
function normalizeWorkspace(workspace) {
|
|
34238
|
-
return
|
|
34473
|
+
return resolve10(workspace || process.cwd());
|
|
34239
34474
|
}
|
|
34240
34475
|
function isInside(root, absolutePath) {
|
|
34241
34476
|
const rel = relative3(root, absolutePath);
|
|
34242
|
-
return rel === "" || !rel.startsWith("..") && !rel.includes(`..${
|
|
34477
|
+
return rel === "" || !rel.startsWith("..") && !rel.includes(`..${sep3}`) && !isAbsolute(rel);
|
|
34243
34478
|
}
|
|
34244
34479
|
function normalizeRelativePath(value) {
|
|
34245
34480
|
const normalized = value.trim().replace(/^\.?\//, "").replace(/\\/g, "/");
|
|
@@ -34303,7 +34538,7 @@ function resolveFile(parsed, workspace) {
|
|
|
34303
34538
|
resolution.warnings.push("path is empty or escapes the workspace");
|
|
34304
34539
|
return resolution;
|
|
34305
34540
|
}
|
|
34306
|
-
const absolutePath =
|
|
34541
|
+
const absolutePath = resolve10(workspace, relPath);
|
|
34307
34542
|
if (!isInside(workspace, absolutePath)) {
|
|
34308
34543
|
resolution.path = relPath;
|
|
34309
34544
|
resolution.warnings.push("path escapes the workspace");
|
|
@@ -34648,9 +34883,9 @@ var init_mention_resolver = __esm(() => {
|
|
|
34648
34883
|
});
|
|
34649
34884
|
|
|
34650
34885
|
// src/lib/policy-packs.ts
|
|
34651
|
-
import { relative as relative4, resolve as
|
|
34886
|
+
import { relative as relative4, resolve as resolve11 } from "path";
|
|
34652
34887
|
function normalizePath3(path) {
|
|
34653
|
-
return
|
|
34888
|
+
return resolve11(path);
|
|
34654
34889
|
}
|
|
34655
34890
|
function unique4(values) {
|
|
34656
34891
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -34705,7 +34940,7 @@ function commandMatches(commands, pattern) {
|
|
|
34705
34940
|
}
|
|
34706
34941
|
function pathMatches(paths, pattern, root) {
|
|
34707
34942
|
return paths.filter((path) => {
|
|
34708
|
-
const candidate = path.startsWith("/") ? path :
|
|
34943
|
+
const candidate = path.startsWith("/") ? path : resolve11(root, path);
|
|
34709
34944
|
if (!isPathInside3(root, candidate))
|
|
34710
34945
|
return matchesPattern3(path, pattern);
|
|
34711
34946
|
return matchesPattern3(path, pattern) || matchesPattern3(relative4(root, candidate), pattern);
|
|
@@ -35052,7 +35287,8 @@ function logApprovalEvent(taskId, action, gate, agentId, db) {
|
|
|
35052
35287
|
if (action === "approved" || action === "rejected" || action === "expired") {
|
|
35053
35288
|
emitLocalEventHooksQuiet({
|
|
35054
35289
|
type: "approval.decided",
|
|
35055
|
-
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)
|
|
35056
35292
|
});
|
|
35057
35293
|
}
|
|
35058
35294
|
}
|
|
@@ -35192,6 +35428,7 @@ var init_approval_gates = __esm(() => {
|
|
|
35192
35428
|
init_task_runs();
|
|
35193
35429
|
init_tasks();
|
|
35194
35430
|
init_types();
|
|
35431
|
+
init_event_emission_safety();
|
|
35195
35432
|
init_event_hooks();
|
|
35196
35433
|
});
|
|
35197
35434
|
|
|
@@ -37758,7 +37995,7 @@ function canonicalize(value) {
|
|
|
37758
37995
|
function hash(value) {
|
|
37759
37996
|
return createHash5("sha256").update(value).digest("hex");
|
|
37760
37997
|
}
|
|
37761
|
-
function
|
|
37998
|
+
function parsePayload2(value) {
|
|
37762
37999
|
if (!value)
|
|
37763
38000
|
return {};
|
|
37764
38001
|
try {
|
|
@@ -37857,7 +38094,7 @@ function taskScopedRows(db, scope) {
|
|
|
37857
38094
|
FROM handoffs h
|
|
37858
38095
|
WHERE h.project_id IS NULL OR h.project_id = COALESCE(?, h.project_id)
|
|
37859
38096
|
`).all(scope.project_id ?? null).filter((row) => {
|
|
37860
|
-
const payload =
|
|
38097
|
+
const payload = parsePayload2(row.payload_json);
|
|
37861
38098
|
const taskRefs = parseStringArray(payload["task_ids"]);
|
|
37862
38099
|
const runRefs = parseStringArray(payload["run_ids"]);
|
|
37863
38100
|
if (scope.project_id && row.project_id !== scope.project_id)
|
|
@@ -37888,7 +38125,7 @@ function toLedgerEntries(rows) {
|
|
|
37888
38125
|
});
|
|
37889
38126
|
let previous = LOCAL_AUDIT_LEDGER_INITIAL_HASH;
|
|
37890
38127
|
return ordered.map((row, index) => {
|
|
37891
|
-
const payload =
|
|
38128
|
+
const payload = parsePayload2(row.payload_json);
|
|
37892
38129
|
const payloadHash = hash(canonicalize({ source: row.source, source_id: row.source_id, created_at: row.created_at, payload }));
|
|
37893
38130
|
const chainHash = hash(`${previous}
|
|
37894
38131
|
${payloadHash}`);
|
|
@@ -38030,7 +38267,7 @@ var init_audit_ledger = __esm(() => {
|
|
|
38030
38267
|
|
|
38031
38268
|
// src/lib/release-compatibility.ts
|
|
38032
38269
|
import { readFileSync as readFileSync6 } from "fs";
|
|
38033
|
-
import { join as join10, resolve as
|
|
38270
|
+
import { join as join10, resolve as resolve12 } from "path";
|
|
38034
38271
|
import { Database as Database2 } from "bun:sqlite";
|
|
38035
38272
|
function pass(id, message, details) {
|
|
38036
38273
|
return { id, status: "passed", message, details };
|
|
@@ -38138,7 +38375,7 @@ function checkChangelog() {
|
|
|
38138
38375
|
];
|
|
38139
38376
|
}
|
|
38140
38377
|
function createReleaseCompatibilityReport(options = {}) {
|
|
38141
|
-
const root =
|
|
38378
|
+
const root = resolve12(options.root ?? process.cwd());
|
|
38142
38379
|
const packageJson = readPackageJson2(root);
|
|
38143
38380
|
const simulatedLevels = options.simulated_levels ?? defaultSimulationLevels();
|
|
38144
38381
|
const checks4 = [
|
|
@@ -58415,7 +58652,7 @@ var require_to_json_schema = __commonJS((exports) => {
|
|
|
58415
58652
|
if (val === undefined) {
|
|
58416
58653
|
if (this.unrepresentable === "throw") {
|
|
58417
58654
|
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
58418
|
-
}
|
|
58655
|
+
}
|
|
58419
58656
|
} else if (typeof val === "bigint") {
|
|
58420
58657
|
if (this.unrepresentable === "throw") {
|
|
58421
58658
|
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
@@ -65057,7 +65294,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
65057
65294
|
return;
|
|
65058
65295
|
}
|
|
65059
65296
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
65060
|
-
await new Promise((
|
|
65297
|
+
await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
|
|
65061
65298
|
options?.signal?.throwIfAborted();
|
|
65062
65299
|
}
|
|
65063
65300
|
} catch (error2) {
|
|
@@ -65069,7 +65306,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
65069
65306
|
}
|
|
65070
65307
|
request(request, resultSchema, options) {
|
|
65071
65308
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
65072
|
-
return new Promise((
|
|
65309
|
+
return new Promise((resolve13, reject) => {
|
|
65073
65310
|
const earlyReject = (error2) => {
|
|
65074
65311
|
reject(error2);
|
|
65075
65312
|
};
|
|
@@ -65147,7 +65384,7 @@ var require_protocol = __commonJS((exports) => {
|
|
|
65147
65384
|
if (!parseResult.success) {
|
|
65148
65385
|
reject(parseResult.error);
|
|
65149
65386
|
} else {
|
|
65150
|
-
|
|
65387
|
+
resolve13(parseResult.data);
|
|
65151
65388
|
}
|
|
65152
65389
|
} catch (error2) {
|
|
65153
65390
|
reject(error2);
|
|
@@ -65338,12 +65575,12 @@ var require_protocol = __commonJS((exports) => {
|
|
|
65338
65575
|
interval = task.pollInterval;
|
|
65339
65576
|
}
|
|
65340
65577
|
} catch {}
|
|
65341
|
-
return new Promise((
|
|
65578
|
+
return new Promise((resolve13, reject) => {
|
|
65342
65579
|
if (signal.aborted) {
|
|
65343
65580
|
reject(new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, "Request cancelled"));
|
|
65344
65581
|
return;
|
|
65345
65582
|
}
|
|
65346
|
-
const timeoutId = setTimeout(
|
|
65583
|
+
const timeoutId = setTimeout(resolve13, interval);
|
|
65347
65584
|
signal.addEventListener("abort", () => {
|
|
65348
65585
|
clearTimeout(timeoutId);
|
|
65349
65586
|
reject(new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -66489,7 +66726,7 @@ var require_mcp = __commonJS((exports) => {
|
|
|
66489
66726
|
let task = createTaskResult.task;
|
|
66490
66727
|
const pollInterval = task.pollInterval ?? 5000;
|
|
66491
66728
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
66492
|
-
await new Promise((
|
|
66729
|
+
await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
|
|
66493
66730
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
66494
66731
|
if (!updatedTask) {
|
|
66495
66732
|
throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -67898,7 +68135,7 @@ function classifyLog(text) {
|
|
|
67898
68135
|
async function sleep2(ms) {
|
|
67899
68136
|
if (ms <= 0)
|
|
67900
68137
|
return;
|
|
67901
|
-
await new Promise((
|
|
68138
|
+
await new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
67902
68139
|
}
|
|
67903
68140
|
async function runCommandProvider(provider, input) {
|
|
67904
68141
|
const commandTemplate = input.command || provider.command;
|
|
@@ -70627,7 +70864,7 @@ var init_local_bridge = __esm(() => {
|
|
|
70627
70864
|
// src/lib/local-backups.ts
|
|
70628
70865
|
import { createHash as createHash6 } from "crypto";
|
|
70629
70866
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
|
|
70630
|
-
import { dirname as dirname8, resolve as
|
|
70867
|
+
import { dirname as dirname8, resolve as resolve13 } from "path";
|
|
70631
70868
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
70632
70869
|
function stableJson(value) {
|
|
70633
70870
|
if (value === null || typeof value !== "object")
|
|
@@ -70729,14 +70966,14 @@ function createLocalBackup(options = {}, db) {
|
|
|
70729
70966
|
return backup;
|
|
70730
70967
|
}
|
|
70731
70968
|
function writeLocalBackupFile(backup, outputPath) {
|
|
70732
|
-
const path =
|
|
70969
|
+
const path = resolve13(outputPath);
|
|
70733
70970
|
mkdirSync6(dirname8(path), { recursive: true });
|
|
70734
70971
|
writeFileSync3(path, `${JSON.stringify(backup, null, 2)}
|
|
70735
70972
|
`);
|
|
70736
70973
|
return path;
|
|
70737
70974
|
}
|
|
70738
70975
|
function readLocalBackupFile(path) {
|
|
70739
|
-
return JSON.parse(readFileSync8(
|
|
70976
|
+
return JSON.parse(readFileSync8(resolve13(path), "utf-8"));
|
|
70740
70977
|
}
|
|
70741
70978
|
function verifyLocalBackup(value, options = {}, db) {
|
|
70742
70979
|
const verifiedAt = options.verified_at ?? now();
|
|
@@ -72186,7 +72423,7 @@ __export(exports_local_extensions, {
|
|
|
72186
72423
|
});
|
|
72187
72424
|
import { createHash as createHash9, createVerify } from "crypto";
|
|
72188
72425
|
import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
|
|
72189
|
-
import { basename as basename5, join as join11, resolve as
|
|
72426
|
+
import { basename as basename5, join as join11, resolve as resolve14 } from "path";
|
|
72190
72427
|
function isObject3(value) {
|
|
72191
72428
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
72192
72429
|
}
|
|
@@ -72444,7 +72681,7 @@ function verifyExtensionSignature(input) {
|
|
|
72444
72681
|
return verifier.verify(input.public_key, decodeSignature(input.signature));
|
|
72445
72682
|
}
|
|
72446
72683
|
function inspectExtensionSource(source3) {
|
|
72447
|
-
const resolved =
|
|
72684
|
+
const resolved = resolve14(source3);
|
|
72448
72685
|
if (!existsSync13(resolved))
|
|
72449
72686
|
throw new Error(`extension source not found: ${source3}`);
|
|
72450
72687
|
const stat = statSync6(resolved);
|
|
@@ -72542,7 +72779,7 @@ function testExtensionCompatibility(sourceOrManifest) {
|
|
|
72542
72779
|
function projectExtensionSources(projectPath) {
|
|
72543
72780
|
if (!projectPath)
|
|
72544
72781
|
return [];
|
|
72545
|
-
const root =
|
|
72782
|
+
const root = resolve14(projectPath);
|
|
72546
72783
|
const candidates = [
|
|
72547
72784
|
join11(root, "todos.extension.json"),
|
|
72548
72785
|
join11(root, ".todos", "todos.extension.json")
|
|
@@ -72561,7 +72798,7 @@ function projectExtensionSources(projectPath) {
|
|
|
72561
72798
|
}
|
|
72562
72799
|
function discoverLocalExtensions(options = {}) {
|
|
72563
72800
|
const config2 = loadConfig();
|
|
72564
|
-
const projectPath = options.project_path ?
|
|
72801
|
+
const projectPath = options.project_path ? resolve14(options.project_path) : null;
|
|
72565
72802
|
const configuredSources = [
|
|
72566
72803
|
...config2.extension_sources || [],
|
|
72567
72804
|
...projectPath ? config2.project_overrides?.[projectPath]?.extension_sources || [] : []
|
|
@@ -72569,7 +72806,7 @@ function discoverLocalExtensions(options = {}) {
|
|
|
72569
72806
|
const sources = Array.from(new Set([
|
|
72570
72807
|
...configuredSources,
|
|
72571
72808
|
...projectExtensionSources(projectPath || undefined)
|
|
72572
|
-
])).map((source3) => projectPath && !source3.startsWith("/") ?
|
|
72809
|
+
])).map((source3) => projectPath && !source3.startsWith("/") ? resolve14(projectPath, source3) : resolve14(source3));
|
|
72573
72810
|
const warnings = [];
|
|
72574
72811
|
const discovered = [];
|
|
72575
72812
|
for (const source3 of sources) {
|
|
@@ -75519,7 +75756,8 @@ function writeQueue(task2, queue, actor, action, db) {
|
|
|
75519
75756
|
logTaskChange(task2.id, `review_queue.${action}`, "review_queue", null, JSON.stringify(queue), actor, d);
|
|
75520
75757
|
emitLocalEventHooksQuiet({
|
|
75521
75758
|
type: `review.${action}`,
|
|
75522
|
-
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)
|
|
75523
75761
|
});
|
|
75524
75762
|
return itemFromTask(taskOrThrow2(task2.id, d), queue, d);
|
|
75525
75763
|
}
|
|
@@ -75823,6 +76061,7 @@ var init_review_queues = __esm(() => {
|
|
|
75823
76061
|
init_database();
|
|
75824
76062
|
init_tasks();
|
|
75825
76063
|
init_config();
|
|
76064
|
+
init_event_emission_safety();
|
|
75826
76065
|
init_event_hooks();
|
|
75827
76066
|
init_task_contracts();
|
|
75828
76067
|
STATES = new Set(["requested", "claimed", "approved", "changes_requested", "returned", "reopened"]);
|
|
@@ -76978,7 +77217,7 @@ __export(exports_extract, {
|
|
|
76978
77217
|
});
|
|
76979
77218
|
import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync7 } from "fs";
|
|
76980
77219
|
import { createHash as createHash11 } from "crypto";
|
|
76981
|
-
import { relative as relative5, resolve as
|
|
77220
|
+
import { relative as relative5, resolve as resolve15, join as join12 } from "path";
|
|
76982
77221
|
function stableHash(value) {
|
|
76983
77222
|
return createHash11("sha256").update(value).digest("hex");
|
|
76984
77223
|
}
|
|
@@ -76986,7 +77225,7 @@ function normalizePathForMatch(value) {
|
|
|
76986
77225
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
76987
77226
|
}
|
|
76988
77227
|
function readGitignorePatterns(basePath) {
|
|
76989
|
-
const root = statSync7(basePath).isFile() ?
|
|
77228
|
+
const root = statSync7(basePath).isFile() ? resolve15(basePath, "..") : basePath;
|
|
76990
77229
|
const gitignorePath = join12(root, ".gitignore");
|
|
76991
77230
|
if (!existsSync14(gitignorePath))
|
|
76992
77231
|
return [];
|
|
@@ -77122,7 +77361,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
|
|
|
77122
77361
|
return files.sort();
|
|
77123
77362
|
}
|
|
77124
77363
|
function buildCodebaseIndex(options) {
|
|
77125
|
-
const basePath =
|
|
77364
|
+
const basePath = resolve15(options.path);
|
|
77126
77365
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
77127
77366
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
77128
77367
|
const excludes = options.exclude || [];
|
|
@@ -77133,7 +77372,7 @@ function buildCodebaseIndex(options) {
|
|
|
77133
77372
|
const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
|
|
77134
77373
|
try {
|
|
77135
77374
|
const source3 = readFileSync10(fullPath, "utf-8");
|
|
77136
|
-
const relPath = statSync7(basePath).isFile() ? relative5(
|
|
77375
|
+
const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
|
|
77137
77376
|
indexed.push({
|
|
77138
77377
|
file: relPath,
|
|
77139
77378
|
checksum: stableHash(source3).slice(0, 24),
|
|
@@ -77153,7 +77392,7 @@ function buildCodebaseIndex(options) {
|
|
|
77153
77392
|
};
|
|
77154
77393
|
}
|
|
77155
77394
|
function extractTodos(options, db) {
|
|
77156
|
-
const basePath =
|
|
77395
|
+
const basePath = resolve15(options.path);
|
|
77157
77396
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
77158
77397
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
77159
77398
|
const excludes = options.exclude || [];
|
|
@@ -77164,7 +77403,7 @@ function extractTodos(options, db) {
|
|
|
77164
77403
|
const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
|
|
77165
77404
|
try {
|
|
77166
77405
|
const source3 = readFileSync10(fullPath, "utf-8");
|
|
77167
|
-
const relPath = statSync7(basePath).isFile() ? relative5(
|
|
77406
|
+
const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
|
|
77168
77407
|
const comments = extractFromSource(source3, relPath, tags);
|
|
77169
77408
|
allComments.push(...comments);
|
|
77170
77409
|
} catch {}
|
|
@@ -77258,7 +77497,7 @@ async function watchSourceTodos(options, onRun) {
|
|
|
77258
77497
|
const interval = Math.max(100, options.interval_ms || 2000);
|
|
77259
77498
|
const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
|
|
77260
77499
|
const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
|
|
77261
|
-
const root =
|
|
77500
|
+
const root = resolve15(options.path);
|
|
77262
77501
|
const runs = [];
|
|
77263
77502
|
let previous = new Map;
|
|
77264
77503
|
for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
|
|
@@ -78641,8 +78880,8 @@ __export(exports_environment_snapshots, {
|
|
|
78641
78880
|
import { createHash as createHash12 } from "crypto";
|
|
78642
78881
|
import { existsSync as existsSync15, readFileSync as readFileSync11, statSync as statSync8 } from "fs";
|
|
78643
78882
|
import { hostname as hostname3, platform, arch } from "os";
|
|
78644
|
-
import { dirname as dirname9, join as join14, resolve as
|
|
78645
|
-
import { tmpdir as
|
|
78883
|
+
import { dirname as dirname9, join as join14, resolve as resolve16 } from "path";
|
|
78884
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
78646
78885
|
function sha2566(value) {
|
|
78647
78886
|
return createHash12("sha256").update(value).digest("hex");
|
|
78648
78887
|
}
|
|
@@ -78755,15 +78994,15 @@ function commandEnv(env, includeValues) {
|
|
|
78755
78994
|
function defaultSnapshotDir() {
|
|
78756
78995
|
const dbPath = getDatabasePath();
|
|
78757
78996
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
78758
|
-
return join14(
|
|
78759
|
-
return join14(dirname9(
|
|
78997
|
+
return join14(tmpdir3(), "hasna-todos", "environment-snapshots");
|
|
78998
|
+
return join14(dirname9(resolve16(dbPath)), "environment-snapshots");
|
|
78760
78999
|
}
|
|
78761
79000
|
function snapshotWithId(snapshot) {
|
|
78762
79001
|
const digest = sha2566(JSON.stringify(snapshot)).slice(0, 24);
|
|
78763
79002
|
return { id: `env_${digest}`, ...snapshot };
|
|
78764
79003
|
}
|
|
78765
79004
|
function captureEnvironmentSnapshot(input = {}) {
|
|
78766
|
-
const root =
|
|
79005
|
+
const root = resolve16(input.root || process.cwd());
|
|
78767
79006
|
const env = input.env || process.env;
|
|
78768
79007
|
const warnings = [];
|
|
78769
79008
|
const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
|
|
@@ -78803,13 +79042,13 @@ function captureEnvironmentSnapshot(input = {}) {
|
|
|
78803
79042
|
});
|
|
78804
79043
|
}
|
|
78805
79044
|
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
78806
|
-
const path = outputPath ?
|
|
79045
|
+
const path = outputPath ? resolve16(outputPath) : join14(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
78807
79046
|
ensureDir2(dirname9(path));
|
|
78808
79047
|
writeJsonFile(path, snapshot);
|
|
78809
79048
|
return path;
|
|
78810
79049
|
}
|
|
78811
79050
|
function readEnvironmentSnapshot(path) {
|
|
78812
|
-
const snapshot = readJsonFile(
|
|
79051
|
+
const snapshot = readJsonFile(resolve16(path));
|
|
78813
79052
|
if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
|
|
78814
79053
|
throw new Error(`Invalid environment snapshot: ${path}`);
|
|
78815
79054
|
}
|