@runfusion/fusion 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +67600 -65202
- package/dist/client/assets/{AgentDetailView-CJIxNRq-.js → AgentDetailView-sy6Hg1Xr.js} +3 -3
- package/dist/client/assets/{AgentsView-BS17exn3.js → AgentsView-DpEpW88a.js} +3 -3
- package/dist/client/assets/ChatView-BUt3C4cf.js +1 -0
- package/dist/client/assets/{DevServerView-qMPpnXRb.js → DevServerView-BOyWtQJM.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-CTwgv9LY.js → DirectoryPicker-BJyEEso5.js} +1 -1
- package/dist/client/assets/{DocumentsView-DOz1KFGN.js → DocumentsView-Dx0M1YHw.js} +1 -1
- package/dist/client/assets/{InsightsView-CHZTJUic.js → InsightsView-BtXdAo7D.js} +1 -1
- package/dist/client/assets/MemoryView-DiajLXby.css +1 -0
- package/dist/client/assets/MemoryView-kKPuqmwf.js +2 -0
- package/dist/client/assets/{NodesView-BtGNRj2z.js → NodesView-B_ZwUORz.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-D9Ye2Vak.js → PiExtensionsManager-Db0EGr0Q.js} +3 -3
- package/dist/client/assets/{PluginManager-LeHp0jJ_.js → PluginManager-mOwWndfg.js} +1 -1
- package/dist/client/assets/{RoadmapsView-C413ISVU.js → RoadmapsView-DbUzBLeF.js} +1 -1
- package/dist/client/assets/SettingsModal-Bs_lNs5B.js +31 -0
- package/dist/client/assets/SettingsModal-G0ESQXRD.css +1 -0
- package/dist/client/assets/{SettingsModal-olTBmYJs.js → SettingsModal-TRJu_mTn.js} +1 -1
- package/dist/client/assets/{SetupWizardModal-WdaR2eQQ.js → SetupWizardModal-D1bmCQrf.js} +1 -1
- package/dist/client/assets/{SkillsView-BcE57w8i.js → SkillsView-Dzzpd5Md.js} +1 -1
- package/dist/client/assets/{folder-open-Ec4hU1xL.js → folder-open-BcuByk6U.js} +1 -1
- package/dist/client/assets/index-BipedNj4.css +1 -0
- package/dist/client/assets/index-k2c4LrUr.js +616 -0
- package/dist/client/assets/{upload-BksRDuGJ.js → upload-BzNbXYEj.js} +1 -1
- package/dist/client/assets/{users-EFU4n9Qr.js → users-BvIqhSXp.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -0
- package/dist/extension.js +2165 -885
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/pi-claude-cli/src/provider.ts +0 -1
- package/package.json +5 -5
- package/dist/client/assets/ChatView-BUlq3WNJ.js +0 -1
- package/dist/client/assets/MemoryView-DhinauGs.css +0 -1
- package/dist/client/assets/MemoryView-V0QdeO3e.js +0 -2
- package/dist/client/assets/SettingsModal--vWmKBpT.css +0 -1
- package/dist/client/assets/SettingsModal-BZLL2xAP.js +0 -31
- package/dist/client/assets/index-CCYdhck-.js +0 -616
- package/dist/client/assets/index-lJ5WOmO9.css +0 -1
package/dist/extension.js
CHANGED
|
@@ -193,6 +193,40 @@ var init_settings_schema = __esm({
|
|
|
193
193
|
missionHealthCheckIntervalMs: 3e5,
|
|
194
194
|
agentPrompts: void 0,
|
|
195
195
|
promptOverrides: void 0,
|
|
196
|
+
remoteAccess: {
|
|
197
|
+
activeProvider: null,
|
|
198
|
+
providers: {
|
|
199
|
+
tailscale: {
|
|
200
|
+
enabled: false,
|
|
201
|
+
hostname: "",
|
|
202
|
+
targetPort: 0,
|
|
203
|
+
acceptRoutes: false
|
|
204
|
+
},
|
|
205
|
+
cloudflare: {
|
|
206
|
+
enabled: false,
|
|
207
|
+
quickTunnel: false,
|
|
208
|
+
tunnelName: "",
|
|
209
|
+
tunnelToken: null,
|
|
210
|
+
ingressUrl: ""
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
tokenStrategy: {
|
|
214
|
+
persistent: {
|
|
215
|
+
enabled: true,
|
|
216
|
+
token: null
|
|
217
|
+
},
|
|
218
|
+
shortLived: {
|
|
219
|
+
enabled: false,
|
|
220
|
+
ttlMs: 9e5,
|
|
221
|
+
maxTtlMs: 864e5
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
lifecycle: {
|
|
225
|
+
rememberLastRunning: false,
|
|
226
|
+
wasRunningOnShutdown: false,
|
|
227
|
+
lastRunningProvider: null
|
|
228
|
+
}
|
|
229
|
+
},
|
|
196
230
|
reflectionEnabled: false,
|
|
197
231
|
reflectionIntervalMs: 36e5,
|
|
198
232
|
reflectionAfterTask: true,
|
|
@@ -354,18 +388,18 @@ Call \`task_done()\` to signal completion.
|
|
|
354
388
|
},
|
|
355
389
|
"triage-welcome": {
|
|
356
390
|
key: "triage-welcome",
|
|
357
|
-
name: "
|
|
391
|
+
name: "Planning Welcome",
|
|
358
392
|
roles: ["triage"],
|
|
359
|
-
description: "Introductory section for the
|
|
393
|
+
description: "Introductory section for the planning agent",
|
|
360
394
|
defaultContent: `You are a task specification agent for "fn", an AI-orchestrated task board.
|
|
361
395
|
|
|
362
396
|
Your job: take a rough task description and produce a fully specified PROMPT.md that another AI agent can execute autonomously in a fresh context with zero memory of this conversation.`
|
|
363
397
|
},
|
|
364
398
|
"triage-context": {
|
|
365
399
|
key: "triage-context",
|
|
366
|
-
name: "
|
|
400
|
+
name: "Planning Context",
|
|
367
401
|
roles: ["triage"],
|
|
368
|
-
description: "Context-gathering instructions for
|
|
402
|
+
description: "Context-gathering instructions for planning",
|
|
369
403
|
defaultContent: `## What you receive
|
|
370
404
|
- A raw task title and optional description (the user's rough idea)
|
|
371
405
|
- Access to the project's files so you can understand context`
|
|
@@ -1054,7 +1088,7 @@ Output Requirements:
|
|
|
1054
1088
|
}
|
|
1055
1089
|
};
|
|
1056
1090
|
COLUMN_LABELS = {
|
|
1057
|
-
triage: "
|
|
1091
|
+
triage: "Planning",
|
|
1058
1092
|
todo: "Todo",
|
|
1059
1093
|
"in-progress": "In Progress",
|
|
1060
1094
|
"in-review": "In Review",
|
|
@@ -1062,7 +1096,7 @@ Output Requirements:
|
|
|
1062
1096
|
archived: "Archived"
|
|
1063
1097
|
};
|
|
1064
1098
|
COLUMN_DESCRIPTIONS = {
|
|
1065
|
-
triage: "Raw ideas \u2014 AI will
|
|
1099
|
+
triage: "Raw ideas \u2014 AI will plan these",
|
|
1066
1100
|
todo: "Specified and ready to start",
|
|
1067
1101
|
"in-progress": "AI is working on this in a worktree",
|
|
1068
1102
|
"in-review": "Complete \u2014 ready to merge",
|
|
@@ -2188,7 +2222,7 @@ var init_db = __esm({
|
|
|
2188
2222
|
"../core/src/db.ts"() {
|
|
2189
2223
|
"use strict";
|
|
2190
2224
|
init_types();
|
|
2191
|
-
SCHEMA_VERSION =
|
|
2225
|
+
SCHEMA_VERSION = 47;
|
|
2192
2226
|
SCHEMA_SQL = `
|
|
2193
2227
|
-- Tasks table with JSON columns for nested data
|
|
2194
2228
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
@@ -3626,6 +3660,14 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
3626
3660
|
this.db.exec("CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder)");
|
|
3627
3661
|
});
|
|
3628
3662
|
}
|
|
3663
|
+
if (version < 47) {
|
|
3664
|
+
this.applyMigration(47, () => {
|
|
3665
|
+
if (this.hasTable("tasks") && this.hasColumn("tasks", "status")) {
|
|
3666
|
+
this.db.exec("UPDATE tasks SET status = 'planning' WHERE status = 'specifying'");
|
|
3667
|
+
this.db.exec("UPDATE tasks SET status = 'needs-replan' WHERE status = 'needs-respecify'");
|
|
3668
|
+
}
|
|
3669
|
+
});
|
|
3670
|
+
}
|
|
3629
3671
|
}
|
|
3630
3672
|
/**
|
|
3631
3673
|
* Run a single migration step inside a transaction and bump the version.
|
|
@@ -17106,10 +17148,10 @@ var init_central_core = __esm({
|
|
|
17106
17148
|
*/
|
|
17107
17149
|
async generateProjectName(projectPath) {
|
|
17108
17150
|
try {
|
|
17109
|
-
const { execFile:
|
|
17110
|
-
const { promisify:
|
|
17111
|
-
const
|
|
17112
|
-
const { stdout } = await
|
|
17151
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
17152
|
+
const { promisify: promisify13 } = await import("node:util");
|
|
17153
|
+
const execFileAsync4 = promisify13(execFile6);
|
|
17154
|
+
const { stdout } = await execFileAsync4(
|
|
17113
17155
|
"git",
|
|
17114
17156
|
["remote", "get-url", "origin"],
|
|
17115
17157
|
{ cwd: projectPath, timeout: 5e3 }
|
|
@@ -17594,8 +17636,8 @@ function hasProjectDbFile(dir, folderName, dbName) {
|
|
|
17594
17636
|
if (!existsSync8(projectDir)) return false;
|
|
17595
17637
|
if (!existsSync8(dbPath)) return false;
|
|
17596
17638
|
try {
|
|
17597
|
-
const
|
|
17598
|
-
return
|
|
17639
|
+
const stat8 = statSync2(dbPath);
|
|
17640
|
+
return stat8.isFile() && stat8.size > 0;
|
|
17599
17641
|
} catch {
|
|
17600
17642
|
return false;
|
|
17601
17643
|
}
|
|
@@ -17722,10 +17764,10 @@ var init_migration = __esm({
|
|
|
17722
17764
|
return basename3(projectPath);
|
|
17723
17765
|
}
|
|
17724
17766
|
try {
|
|
17725
|
-
const { execFile:
|
|
17726
|
-
const { promisify:
|
|
17727
|
-
const
|
|
17728
|
-
const { stdout } = await
|
|
17767
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
17768
|
+
const { promisify: promisify13 } = await import("node:util");
|
|
17769
|
+
const execFileAsync4 = promisify13(execFile6);
|
|
17770
|
+
const { stdout } = await execFileAsync4(
|
|
17729
17771
|
"git",
|
|
17730
17772
|
["remote", "get-url", "origin"],
|
|
17731
17773
|
{ cwd: projectPath, timeout: 1e3 }
|
|
@@ -28431,13 +28473,13 @@ async function searchWithQmd(rootDir, options) {
|
|
|
28431
28473
|
const command = "qmd";
|
|
28432
28474
|
const limit = Math.max(1, Math.min(options.limit ?? 5, 20));
|
|
28433
28475
|
try {
|
|
28434
|
-
const { execFile:
|
|
28435
|
-
const { promisify:
|
|
28436
|
-
const
|
|
28437
|
-
await ensureQmdProjectMemoryCollection(rootDir,
|
|
28476
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
28477
|
+
const { promisify: promisify13 } = await import("node:util");
|
|
28478
|
+
const execFileAsync4 = promisify13(execFile6);
|
|
28479
|
+
await ensureQmdProjectMemoryCollection(rootDir, execFileAsync4);
|
|
28438
28480
|
scheduleQmdProjectMemoryRefresh(rootDir);
|
|
28439
28481
|
const args = buildQmdSearchArgs(rootDir, options);
|
|
28440
|
-
const { stdout } = await
|
|
28482
|
+
const { stdout } = await execFileAsync4(command, args, {
|
|
28441
28483
|
cwd: rootDir,
|
|
28442
28484
|
timeout: 4e3,
|
|
28443
28485
|
maxBuffer: 1024 * 1024
|
|
@@ -28462,12 +28504,12 @@ async function searchWithQmd(rootDir, options) {
|
|
|
28462
28504
|
return [];
|
|
28463
28505
|
}
|
|
28464
28506
|
}
|
|
28465
|
-
async function ensureQmdProjectMemoryCollection(rootDir,
|
|
28507
|
+
async function ensureQmdProjectMemoryCollection(rootDir, execFileAsync4) {
|
|
28466
28508
|
const collectionName = qmdMemoryCollectionName(rootDir);
|
|
28467
28509
|
const memoryDir = memoryWorkspacePath(rootDir);
|
|
28468
28510
|
await mkdir6(memoryDir, { recursive: true });
|
|
28469
28511
|
try {
|
|
28470
|
-
await
|
|
28512
|
+
await execFileAsync4("qmd", buildQmdCollectionAddArgs(rootDir), {
|
|
28471
28513
|
cwd: rootDir,
|
|
28472
28514
|
timeout: 4e3,
|
|
28473
28515
|
maxBuffer: 512 * 1024
|
|
@@ -28483,9 +28525,9 @@ ${stderr}`)) {
|
|
|
28483
28525
|
return collectionName;
|
|
28484
28526
|
}
|
|
28485
28527
|
async function getDefaultExecFileAsync() {
|
|
28486
|
-
const { execFile:
|
|
28487
|
-
const { promisify:
|
|
28488
|
-
return
|
|
28528
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
28529
|
+
const { promisify: promisify13 } = await import("node:util");
|
|
28530
|
+
return promisify13(execFile6);
|
|
28489
28531
|
}
|
|
28490
28532
|
async function refreshQmdProjectMemoryIndex(rootDir, options) {
|
|
28491
28533
|
const key = resolve5(rootDir);
|
|
@@ -28500,14 +28542,14 @@ async function refreshQmdProjectMemoryIndex(rootDir, options) {
|
|
|
28500
28542
|
}
|
|
28501
28543
|
}
|
|
28502
28544
|
const promise = (async () => {
|
|
28503
|
-
const
|
|
28504
|
-
await ensureQmdProjectMemoryCollection(rootDir,
|
|
28505
|
-
await
|
|
28545
|
+
const execFileAsync4 = options?.execFileAsync ?? await getDefaultExecFileAsync();
|
|
28546
|
+
await ensureQmdProjectMemoryCollection(rootDir, execFileAsync4);
|
|
28547
|
+
await execFileAsync4("qmd", ["update"], {
|
|
28506
28548
|
cwd: rootDir,
|
|
28507
28549
|
timeout: 3e4,
|
|
28508
28550
|
maxBuffer: 1024 * 1024
|
|
28509
28551
|
});
|
|
28510
|
-
await
|
|
28552
|
+
await execFileAsync4("qmd", ["embed"], {
|
|
28511
28553
|
cwd: rootDir,
|
|
28512
28554
|
timeout: 12e4,
|
|
28513
28555
|
maxBuffer: 1024 * 1024
|
|
@@ -28532,8 +28574,8 @@ function scheduleQmdProjectMemoryRefresh(rootDir) {
|
|
|
28532
28574
|
}
|
|
28533
28575
|
async function isQmdAvailable() {
|
|
28534
28576
|
try {
|
|
28535
|
-
const
|
|
28536
|
-
await
|
|
28577
|
+
const execFileAsync4 = await getDefaultExecFileAsync();
|
|
28578
|
+
await execFileAsync4("qmd", ["--help"], {
|
|
28537
28579
|
timeout: 3e3,
|
|
28538
28580
|
maxBuffer: 128 * 1024
|
|
28539
28581
|
});
|
|
@@ -28543,12 +28585,12 @@ async function isQmdAvailable() {
|
|
|
28543
28585
|
}
|
|
28544
28586
|
}
|
|
28545
28587
|
async function installQmd(options) {
|
|
28546
|
-
const
|
|
28588
|
+
const execFileAsync4 = options?.execFileAsync ?? await getDefaultExecFileAsync();
|
|
28547
28589
|
const [command, ...args] = QMD_INSTALL_COMMAND.split(" ");
|
|
28548
28590
|
if (!command || args.length === 0) {
|
|
28549
28591
|
throw new MemoryBackendError("BACKEND_UNAVAILABLE", "qmd install command is not configured", "qmd");
|
|
28550
28592
|
}
|
|
28551
|
-
await
|
|
28593
|
+
await execFileAsync4(command, args, {
|
|
28552
28594
|
timeout: 12e4,
|
|
28553
28595
|
maxBuffer: 1024 * 1024
|
|
28554
28596
|
});
|
|
@@ -29354,6 +29396,29 @@ function canonicalizeSettings(settings) {
|
|
|
29354
29396
|
}
|
|
29355
29397
|
return base;
|
|
29356
29398
|
}
|
|
29399
|
+
function isPlainObject(value) {
|
|
29400
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29401
|
+
}
|
|
29402
|
+
function deepMergeWithNullDelete(existingValue, patchValue) {
|
|
29403
|
+
const merged = isPlainObject(existingValue) ? { ...existingValue } : {};
|
|
29404
|
+
for (const [key, value] of Object.entries(patchValue)) {
|
|
29405
|
+
if (value === null) {
|
|
29406
|
+
delete merged[key];
|
|
29407
|
+
continue;
|
|
29408
|
+
}
|
|
29409
|
+
if (isPlainObject(value)) {
|
|
29410
|
+
const nested = deepMergeWithNullDelete(merged[key], value);
|
|
29411
|
+
if (nested === void 0) {
|
|
29412
|
+
delete merged[key];
|
|
29413
|
+
} else {
|
|
29414
|
+
merged[key] = nested;
|
|
29415
|
+
}
|
|
29416
|
+
continue;
|
|
29417
|
+
}
|
|
29418
|
+
merged[key] = value;
|
|
29419
|
+
}
|
|
29420
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
29421
|
+
}
|
|
29357
29422
|
var TASK_ACTIVITY_LOG_ENTRY_LIMIT, TASK_ACTIVITY_LOG_OUTCOME_LIMIT, ARCHIVE_AGENT_LOG_SNAPSHOT_LIMIT, ARCHIVE_AGENT_LOG_SNIPPET_LIMIT, storeLog, TaskHasDependentsError, TaskStore;
|
|
29358
29423
|
var init_store = __esm({
|
|
29359
29424
|
"../core/src/store.ts"() {
|
|
@@ -30524,6 +30589,21 @@ ${recentText}` : void 0
|
|
|
30524
30589
|
projectPatch["promptOverrides"] = mergedMap;
|
|
30525
30590
|
}
|
|
30526
30591
|
}
|
|
30592
|
+
const incomingRemoteAccess = projectPatch["remoteAccess"];
|
|
30593
|
+
if (incomingRemoteAccess === null) {
|
|
30594
|
+
delete config.settings["remoteAccess"];
|
|
30595
|
+
delete projectPatch["remoteAccess"];
|
|
30596
|
+
} else if (isPlainObject(incomingRemoteAccess)) {
|
|
30597
|
+
const existingRemoteAccess = config.settings["remoteAccess"];
|
|
30598
|
+
const mergedRemoteAccess = deepMergeWithNullDelete(existingRemoteAccess, incomingRemoteAccess);
|
|
30599
|
+
if (mergedRemoteAccess === void 0) {
|
|
30600
|
+
delete config.settings["remoteAccess"];
|
|
30601
|
+
delete projectPatch["remoteAccess"];
|
|
30602
|
+
} else {
|
|
30603
|
+
config.settings["remoteAccess"] = mergedRemoteAccess;
|
|
30604
|
+
projectPatch["remoteAccess"] = mergedRemoteAccess;
|
|
30605
|
+
}
|
|
30606
|
+
}
|
|
30527
30607
|
for (const key of Object.keys(projectPatch)) {
|
|
30528
30608
|
if (projectPatch[key] === null) {
|
|
30529
30609
|
delete config.settings[key];
|
|
@@ -31914,8 +31994,8 @@ ${task.description}
|
|
|
31914
31994
|
if (this.isWatching) this.taskCache.delete(id);
|
|
31915
31995
|
const dir = this.taskDir(id);
|
|
31916
31996
|
if (existsSync12(dir)) {
|
|
31917
|
-
const { rm:
|
|
31918
|
-
await
|
|
31997
|
+
const { rm: rm4 } = await import("node:fs/promises");
|
|
31998
|
+
await rm4(dir, { recursive: true });
|
|
31919
31999
|
}
|
|
31920
32000
|
for (const dependentTask of rewrittenDependents) {
|
|
31921
32001
|
this.emit("task:updated", dependentTask);
|
|
@@ -32250,8 +32330,8 @@ ${task.description}
|
|
|
32250
32330
|
this.archiveDb.upsert(entry);
|
|
32251
32331
|
this.db.prepare("DELETE FROM tasks WHERE id = ?").run(id);
|
|
32252
32332
|
this.db.bumpLastModified();
|
|
32253
|
-
const { rm:
|
|
32254
|
-
await
|
|
32333
|
+
const { rm: rm4 } = await import("node:fs/promises");
|
|
32334
|
+
await rm4(dir, { recursive: true, force: true });
|
|
32255
32335
|
if (this.isWatching) {
|
|
32256
32336
|
this.taskCache.delete(id);
|
|
32257
32337
|
}
|
|
@@ -32758,7 +32838,7 @@ ${task.description}
|
|
|
32758
32838
|
let invalidatedStatus = false;
|
|
32759
32839
|
try {
|
|
32760
32840
|
await this.updateTask(id, {
|
|
32761
|
-
status: "needs-
|
|
32841
|
+
status: "needs-replan"
|
|
32762
32842
|
});
|
|
32763
32843
|
invalidatedStatus = true;
|
|
32764
32844
|
} catch (err) {
|
|
@@ -32766,7 +32846,7 @@ ${task.description}
|
|
|
32766
32846
|
...commentContextBase,
|
|
32767
32847
|
phase: "addComment:awaiting-approval-invalidation",
|
|
32768
32848
|
stage: "status-update",
|
|
32769
|
-
nextStatus: "needs-
|
|
32849
|
+
nextStatus: "needs-replan",
|
|
32770
32850
|
error: err instanceof Error ? err.message : String(err)
|
|
32771
32851
|
});
|
|
32772
32852
|
}
|
|
@@ -32783,7 +32863,7 @@ ${task.description}
|
|
|
32783
32863
|
...commentContextBase,
|
|
32784
32864
|
phase: "addComment:awaiting-approval-invalidation",
|
|
32785
32865
|
stage: "post-invalidation-log-entry",
|
|
32786
|
-
nextStatus: "needs-
|
|
32866
|
+
nextStatus: "needs-replan",
|
|
32787
32867
|
error: err instanceof Error ? err.message : String(err)
|
|
32788
32868
|
});
|
|
32789
32869
|
}
|
|
@@ -33203,14 +33283,14 @@ ${task.description}
|
|
|
33203
33283
|
if (rows.length === 0) {
|
|
33204
33284
|
return;
|
|
33205
33285
|
}
|
|
33206
|
-
const { rm:
|
|
33286
|
+
const { rm: rm4 } = await import("node:fs/promises");
|
|
33207
33287
|
for (const row of rows) {
|
|
33208
33288
|
const task = this.rowToTask(row);
|
|
33209
33289
|
const archivedAt = task.columnMovedAt ?? task.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
33210
33290
|
const entry = await this.taskToArchiveEntry(task, archivedAt);
|
|
33211
33291
|
this.archiveDb.upsert(entry);
|
|
33212
33292
|
this.db.prepare("DELETE FROM tasks WHERE id = ?").run(task.id);
|
|
33213
|
-
await
|
|
33293
|
+
await rm4(this.taskDir(task.id), { recursive: true, force: true });
|
|
33214
33294
|
if (this.isWatching) {
|
|
33215
33295
|
this.taskCache.delete(task.id);
|
|
33216
33296
|
}
|
|
@@ -33233,8 +33313,8 @@ ${task.description}
|
|
|
33233
33313
|
this.archiveDb.upsert(entry);
|
|
33234
33314
|
this.db.prepare("DELETE FROM tasks WHERE id = ?").run(task.id);
|
|
33235
33315
|
this.db.bumpLastModified();
|
|
33236
|
-
const { rm:
|
|
33237
|
-
await
|
|
33316
|
+
const { rm: rm4 } = await import("node:fs/promises");
|
|
33317
|
+
await rm4(dir, { recursive: true, force: true });
|
|
33238
33318
|
if (this.isWatching) {
|
|
33239
33319
|
this.taskCache.delete(task.id);
|
|
33240
33320
|
}
|
|
@@ -36345,6 +36425,47 @@ async function writeMemoryAudit(rootDir, content) {
|
|
|
36345
36425
|
}
|
|
36346
36426
|
await writeFile8(filePath, content, "utf-8");
|
|
36347
36427
|
}
|
|
36428
|
+
async function readMemoryAuditState(rootDir) {
|
|
36429
|
+
const filePath = join18(rootDir, MEMORY_AUDIT_STATE_PATH);
|
|
36430
|
+
if (!existsSync15(filePath)) {
|
|
36431
|
+
return null;
|
|
36432
|
+
}
|
|
36433
|
+
try {
|
|
36434
|
+
const raw = await readFile10(filePath, "utf-8");
|
|
36435
|
+
const parsed = JSON.parse(raw);
|
|
36436
|
+
const extraction = isValidExtractionMetadata(parsed.extraction) ? parsed.extraction : void 0;
|
|
36437
|
+
const pruning = isValidPruneOutcome(parsed.pruning) ? parsed.pruning : void 0;
|
|
36438
|
+
return {
|
|
36439
|
+
extraction,
|
|
36440
|
+
pruning,
|
|
36441
|
+
updatedAt: typeof parsed.updatedAt === "string" && parsed.updatedAt.trim() ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString()
|
|
36442
|
+
};
|
|
36443
|
+
} catch {
|
|
36444
|
+
return null;
|
|
36445
|
+
}
|
|
36446
|
+
}
|
|
36447
|
+
async function writeMemoryAuditState(rootDir, state) {
|
|
36448
|
+
const filePath = join18(rootDir, MEMORY_AUDIT_STATE_PATH);
|
|
36449
|
+
const dir = join18(rootDir, ".fusion");
|
|
36450
|
+
if (!existsSync15(dir)) {
|
|
36451
|
+
await mkdir9(dir, { recursive: true });
|
|
36452
|
+
}
|
|
36453
|
+
await writeFile8(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
36454
|
+
}
|
|
36455
|
+
function isValidExtractionMetadata(value) {
|
|
36456
|
+
if (!value || typeof value !== "object") {
|
|
36457
|
+
return false;
|
|
36458
|
+
}
|
|
36459
|
+
const candidate = value;
|
|
36460
|
+
return typeof candidate.runAt === "string" && typeof candidate.success === "boolean" && typeof candidate.insightCount === "number" && typeof candidate.duplicateCount === "number" && typeof candidate.skippedCount === "number" && typeof candidate.summary === "string" && (candidate.error === void 0 || typeof candidate.error === "string");
|
|
36461
|
+
}
|
|
36462
|
+
function isValidPruneOutcome(value) {
|
|
36463
|
+
if (!value || typeof value !== "object") {
|
|
36464
|
+
return false;
|
|
36465
|
+
}
|
|
36466
|
+
const candidate = value;
|
|
36467
|
+
return typeof candidate.applied === "boolean" && typeof candidate.reason === "string" && typeof candidate.sizeDelta === "number" && typeof candidate.originalSize === "number" && typeof candidate.newSize === "number";
|
|
36468
|
+
}
|
|
36348
36469
|
function buildInsightExtractionPrompt(workingMemory, existingInsights) {
|
|
36349
36470
|
const existingSection = existingInsights ? `
|
|
36350
36471
|
## Existing Insights (already captured \u2014 do not duplicate)
|
|
@@ -36799,6 +36920,9 @@ function countInsightsInMarkdown(markdown) {
|
|
|
36799
36920
|
async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
36800
36921
|
const checks = [];
|
|
36801
36922
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
36923
|
+
const persistedState = lastExtraction === void 0 || pruningOutcome === void 0 ? await readMemoryAuditState(rootDir) : null;
|
|
36924
|
+
const effectiveExtraction = lastExtraction ?? persistedState?.extraction;
|
|
36925
|
+
const effectivePruning = pruningOutcome ?? persistedState?.pruning;
|
|
36802
36926
|
const workingMemoryPath = join18(rootDir, MEMORY_WORKING_PATH);
|
|
36803
36927
|
const workingMemoryExists = existsSync15(workingMemoryPath);
|
|
36804
36928
|
let workingMemorySize = 0;
|
|
@@ -36917,20 +37041,20 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
36917
37041
|
details: totalInsights > 0 ? `Contains ${totalInsights} insights across categories: patterns=${categoryCounts.pattern}, principles=${categoryCounts.principle}, conventions=${categoryCounts.convention}, pitfalls=${categoryCounts.pitfall}, context=${categoryCounts.context}` : "No insights extracted yet"
|
|
36918
37042
|
});
|
|
36919
37043
|
}
|
|
36920
|
-
if (
|
|
36921
|
-
const extractionAge = Date.now() - new Date(
|
|
37044
|
+
if (effectiveExtraction) {
|
|
37045
|
+
const extractionAge = Date.now() - new Date(effectiveExtraction.runAt).getTime();
|
|
36922
37046
|
const oneWeekMs = 7 * 24 * 60 * 60 * 1e3;
|
|
36923
37047
|
checks.push({
|
|
36924
37048
|
id: "recent-extraction",
|
|
36925
37049
|
name: "Recent extraction activity",
|
|
36926
|
-
passed:
|
|
36927
|
-
details:
|
|
37050
|
+
passed: effectiveExtraction.success && extractionAge < oneWeekMs,
|
|
37051
|
+
details: effectiveExtraction.success ? `Last successful extraction ${formatTimeAgo(effectiveExtraction.runAt)} (${effectiveExtraction.insightCount} insights, ${effectiveExtraction.duplicateCount} duplicates skipped)` : `Last extraction failed: ${effectiveExtraction.error || "Unknown error"}`
|
|
36928
37052
|
});
|
|
36929
37053
|
checks.push({
|
|
36930
37054
|
id: "extraction-summary",
|
|
36931
37055
|
name: "Extraction produces meaningful summaries",
|
|
36932
|
-
passed:
|
|
36933
|
-
details:
|
|
37056
|
+
passed: effectiveExtraction.success && effectiveExtraction.summary.length > 10,
|
|
37057
|
+
details: effectiveExtraction.success ? `Summary: "${effectiveExtraction.summary.slice(0, 100)}${effectiveExtraction.summary.length > 100 ? "..." : ""}"` : "No meaningful summary available"
|
|
36934
37058
|
});
|
|
36935
37059
|
} else {
|
|
36936
37060
|
checks.push({
|
|
@@ -36940,12 +37064,12 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
36940
37064
|
details: "No extraction runs recorded"
|
|
36941
37065
|
});
|
|
36942
37066
|
}
|
|
36943
|
-
if (
|
|
37067
|
+
if (effectivePruning) {
|
|
36944
37068
|
checks.push({
|
|
36945
37069
|
id: "pruning-applied",
|
|
36946
37070
|
name: "Memory pruning outcome",
|
|
36947
|
-
passed:
|
|
36948
|
-
details:
|
|
37071
|
+
passed: effectivePruning.applied,
|
|
37072
|
+
details: effectivePruning.applied ? `Pruning applied: ${effectivePruning.originalSize} \u2192 ${effectivePruning.newSize} chars (${effectivePruning.sizeDelta >= 0 ? "+" : ""}${effectivePruning.sizeDelta} chars)` : `Pruning skipped: ${effectivePruning.reason}`
|
|
36949
37073
|
});
|
|
36950
37074
|
}
|
|
36951
37075
|
const failedChecks = checks.filter((c) => !c.passed);
|
|
@@ -36972,14 +37096,14 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
36972
37096
|
categories: categoryCounts,
|
|
36973
37097
|
lastUpdated
|
|
36974
37098
|
},
|
|
36975
|
-
extraction:
|
|
36976
|
-
runAt:
|
|
36977
|
-
success:
|
|
36978
|
-
insightCount:
|
|
36979
|
-
duplicateCount:
|
|
36980
|
-
skippedCount:
|
|
36981
|
-
summary:
|
|
36982
|
-
error:
|
|
37099
|
+
extraction: effectiveExtraction ? {
|
|
37100
|
+
runAt: effectiveExtraction.runAt,
|
|
37101
|
+
success: effectiveExtraction.success,
|
|
37102
|
+
insightCount: effectiveExtraction.insightCount,
|
|
37103
|
+
duplicateCount: effectiveExtraction.duplicateCount,
|
|
37104
|
+
skippedCount: effectiveExtraction.skippedCount,
|
|
37105
|
+
summary: effectiveExtraction.summary,
|
|
37106
|
+
error: effectiveExtraction.error
|
|
36983
37107
|
} : {
|
|
36984
37108
|
runAt: "",
|
|
36985
37109
|
success: false,
|
|
@@ -36988,7 +37112,7 @@ async function generateMemoryAudit(rootDir, lastExtraction, pruningOutcome) {
|
|
|
36988
37112
|
skippedCount: 0,
|
|
36989
37113
|
summary: "No extraction runs recorded"
|
|
36990
37114
|
},
|
|
36991
|
-
pruning:
|
|
37115
|
+
pruning: effectivePruning ?? {
|
|
36992
37116
|
applied: false,
|
|
36993
37117
|
reason: "No pruning run recorded",
|
|
36994
37118
|
sizeDelta: 0,
|
|
@@ -37129,6 +37253,17 @@ async function processAndAuditInsightExtraction(rootDir, input) {
|
|
|
37129
37253
|
newSize: currentMemory.length
|
|
37130
37254
|
};
|
|
37131
37255
|
}
|
|
37256
|
+
try {
|
|
37257
|
+
await writeMemoryAuditState(rootDir, {
|
|
37258
|
+
extraction: extractionInfo,
|
|
37259
|
+
pruning: pruneOutcome,
|
|
37260
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
37261
|
+
});
|
|
37262
|
+
} catch (err) {
|
|
37263
|
+
console.error(
|
|
37264
|
+
`[memory-audit] Failed to persist audit state: ${err instanceof Error ? err.message : String(err)}`
|
|
37265
|
+
);
|
|
37266
|
+
}
|
|
37132
37267
|
const auditReport = await generateMemoryAudit(rootDir, extractionInfo, pruneOutcome);
|
|
37133
37268
|
try {
|
|
37134
37269
|
const auditMarkdown = renderMemoryAuditMarkdown(auditReport);
|
|
@@ -37140,13 +37275,14 @@ async function processAndAuditInsightExtraction(rootDir, input) {
|
|
|
37140
37275
|
}
|
|
37141
37276
|
return auditReport;
|
|
37142
37277
|
}
|
|
37143
|
-
var MEMORY_WORKING_PATH, MEMORY_INSIGHTS_PATH, MEMORY_AUDIT_PATH, DEFAULT_INSIGHT_SCHEDULE, DEFAULT_MIN_INTERVAL_MS, MIN_INSIGHT_GROWTH_CHARS, INSIGHT_EXTRACTION_SCHEDULE_NAME, REQUIRED_MEMORY_SECTIONS;
|
|
37278
|
+
var MEMORY_WORKING_PATH, MEMORY_INSIGHTS_PATH, MEMORY_AUDIT_PATH, MEMORY_AUDIT_STATE_PATH, DEFAULT_INSIGHT_SCHEDULE, DEFAULT_MIN_INTERVAL_MS, MIN_INSIGHT_GROWTH_CHARS, INSIGHT_EXTRACTION_SCHEDULE_NAME, REQUIRED_MEMORY_SECTIONS;
|
|
37144
37279
|
var init_memory_insights = __esm({
|
|
37145
37280
|
"../core/src/memory-insights.ts"() {
|
|
37146
37281
|
"use strict";
|
|
37147
37282
|
MEMORY_WORKING_PATH = ".fusion/memory/MEMORY.md";
|
|
37148
37283
|
MEMORY_INSIGHTS_PATH = ".fusion/memory-insights.md";
|
|
37149
37284
|
MEMORY_AUDIT_PATH = ".fusion/memory-audit.md";
|
|
37285
|
+
MEMORY_AUDIT_STATE_PATH = ".fusion/memory-audit-state.json";
|
|
37150
37286
|
DEFAULT_INSIGHT_SCHEDULE = "0 2 * * *";
|
|
37151
37287
|
DEFAULT_MIN_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
37152
37288
|
MIN_INSIGHT_GROWTH_CHARS = 1e3;
|
|
@@ -38324,15 +38460,15 @@ var require_fd_slicer = __commonJS({
|
|
|
38324
38460
|
var Writable = stream.Writable;
|
|
38325
38461
|
var PassThrough = stream.PassThrough;
|
|
38326
38462
|
var Pend = require_pend();
|
|
38327
|
-
var
|
|
38463
|
+
var EventEmitter26 = __require("events").EventEmitter;
|
|
38328
38464
|
exports.createFromBuffer = createFromBuffer;
|
|
38329
38465
|
exports.createFromFd = createFromFd;
|
|
38330
38466
|
exports.BufferSlicer = BufferSlicer;
|
|
38331
38467
|
exports.FdSlicer = FdSlicer;
|
|
38332
|
-
util.inherits(FdSlicer,
|
|
38468
|
+
util.inherits(FdSlicer, EventEmitter26);
|
|
38333
38469
|
function FdSlicer(fd, options) {
|
|
38334
38470
|
options = options || {};
|
|
38335
|
-
|
|
38471
|
+
EventEmitter26.call(this);
|
|
38336
38472
|
this.fd = fd;
|
|
38337
38473
|
this.pend = new Pend();
|
|
38338
38474
|
this.pend.max = 1;
|
|
@@ -38476,9 +38612,9 @@ var require_fd_slicer = __commonJS({
|
|
|
38476
38612
|
this.destroyed = true;
|
|
38477
38613
|
this.context.unref();
|
|
38478
38614
|
};
|
|
38479
|
-
util.inherits(BufferSlicer,
|
|
38615
|
+
util.inherits(BufferSlicer, EventEmitter26);
|
|
38480
38616
|
function BufferSlicer(buffer, options) {
|
|
38481
|
-
|
|
38617
|
+
EventEmitter26.call(this);
|
|
38482
38618
|
options = options || {};
|
|
38483
38619
|
this.refCount = 0;
|
|
38484
38620
|
this.buffer = buffer;
|
|
@@ -38890,7 +39026,7 @@ var require_yauzl = __commonJS({
|
|
|
38890
39026
|
var fd_slicer = require_fd_slicer();
|
|
38891
39027
|
var crc32 = require_buffer_crc32();
|
|
38892
39028
|
var util = __require("util");
|
|
38893
|
-
var
|
|
39029
|
+
var EventEmitter26 = __require("events").EventEmitter;
|
|
38894
39030
|
var Transform = __require("stream").Transform;
|
|
38895
39031
|
var PassThrough = __require("stream").PassThrough;
|
|
38896
39032
|
var Writable = __require("stream").Writable;
|
|
@@ -39022,10 +39158,10 @@ var require_yauzl = __commonJS({
|
|
|
39022
39158
|
callback(new Error("end of central directory record signature not found"));
|
|
39023
39159
|
});
|
|
39024
39160
|
}
|
|
39025
|
-
util.inherits(ZipFile,
|
|
39161
|
+
util.inherits(ZipFile, EventEmitter26);
|
|
39026
39162
|
function ZipFile(reader, centralDirectoryOffset, fileSize, entryCount, comment, autoClose, lazyEntries, decodeStrings, validateEntrySizes, strictFileNames) {
|
|
39027
39163
|
var self = this;
|
|
39028
|
-
|
|
39164
|
+
EventEmitter26.call(self);
|
|
39029
39165
|
self.reader = reader;
|
|
39030
39166
|
self.reader.on("error", function(err) {
|
|
39031
39167
|
emitError(self, err);
|
|
@@ -39386,9 +39522,9 @@ var require_yauzl = __commonJS({
|
|
|
39386
39522
|
}
|
|
39387
39523
|
cb();
|
|
39388
39524
|
};
|
|
39389
|
-
util.inherits(RandomAccessReader,
|
|
39525
|
+
util.inherits(RandomAccessReader, EventEmitter26);
|
|
39390
39526
|
function RandomAccessReader() {
|
|
39391
|
-
|
|
39527
|
+
EventEmitter26.call(this);
|
|
39392
39528
|
this.refCount = 0;
|
|
39393
39529
|
}
|
|
39394
39530
|
RandomAccessReader.prototype.ref = function() {
|
|
@@ -39519,11 +39655,11 @@ var require_extract_zip = __commonJS({
|
|
|
39519
39655
|
var { createWriteStream, promises: fs } = __require("fs");
|
|
39520
39656
|
var getStream = require_get_stream();
|
|
39521
39657
|
var path = __require("path");
|
|
39522
|
-
var { promisify:
|
|
39658
|
+
var { promisify: promisify13 } = __require("util");
|
|
39523
39659
|
var stream = __require("stream");
|
|
39524
39660
|
var yauzl = require_yauzl();
|
|
39525
|
-
var openZip =
|
|
39526
|
-
var pipeline =
|
|
39661
|
+
var openZip = promisify13(yauzl.open);
|
|
39662
|
+
var pipeline = promisify13(stream.pipeline);
|
|
39527
39663
|
var Extractor = class {
|
|
39528
39664
|
constructor(zipPath, opts) {
|
|
39529
39665
|
this.zipPath = zipPath;
|
|
@@ -39605,7 +39741,7 @@ var require_extract_zip = __commonJS({
|
|
|
39605
39741
|
await fs.mkdir(destDir, mkdirOptions);
|
|
39606
39742
|
if (isDir) return;
|
|
39607
39743
|
debug("opening read stream", dest);
|
|
39608
|
-
const readStream = await
|
|
39744
|
+
const readStream = await promisify13(this.zipfile.openReadStream.bind(this.zipfile))(entry);
|
|
39609
39745
|
if (symlink) {
|
|
39610
39746
|
const link = await getStream(readStream);
|
|
39611
39747
|
debug("creating symlink", link, dest);
|
|
@@ -47266,12 +47402,12 @@ function resolveExtractionRoot(tempDir) {
|
|
|
47266
47402
|
return tempDir;
|
|
47267
47403
|
}
|
|
47268
47404
|
async function extractTarArchive(archivePath, outputDir) {
|
|
47269
|
-
const [{ execFile:
|
|
47405
|
+
const [{ execFile: execFile6 }, { promisify: promisify13 }] = await Promise.all([
|
|
47270
47406
|
import("node:child_process"),
|
|
47271
47407
|
import("node:util")
|
|
47272
47408
|
]);
|
|
47273
|
-
const
|
|
47274
|
-
await
|
|
47409
|
+
const execFileAsync4 = promisify13(execFile6);
|
|
47410
|
+
await execFileAsync4("tar", ["xzf", archivePath, "-C", outputDir]);
|
|
47275
47411
|
}
|
|
47276
47412
|
async function parseCompanyArchive(archivePath) {
|
|
47277
47413
|
const resolvedArchivePath = resolve8(archivePath);
|
|
@@ -48451,7 +48587,7 @@ ${stack}` : message2;
|
|
|
48451
48587
|
}
|
|
48452
48588
|
return { message, detail: message };
|
|
48453
48589
|
}
|
|
48454
|
-
var LOG_LEVEL_MARKER_PREFIX2, LOG_LEVEL_MARKER_SUFFIX2, schedulerLog, executorLog, triageLog, piLog, extensionsLog, mergerLog, worktreePoolLog, reviewerLog, prMonitorLog, runtimeLog, ipcLog, projectManagerLog, hybridExecutorLog, autopilotLog, heartbeatLog, remoteNodeLog, nodeHealthMonitorLog, peerExchangeLog;
|
|
48590
|
+
var LOG_LEVEL_MARKER_PREFIX2, LOG_LEVEL_MARKER_SUFFIX2, schedulerLog, executorLog, triageLog, piLog, extensionsLog, mergerLog, worktreePoolLog, reviewerLog, prMonitorLog, runtimeLog, ipcLog, projectManagerLog, hybridExecutorLog, autopilotLog, heartbeatLog, remoteNodeLog, remoteTunnelLog, nodeHealthMonitorLog, peerExchangeLog;
|
|
48455
48591
|
var init_logger2 = __esm({
|
|
48456
48592
|
"../engine/src/logger.ts"() {
|
|
48457
48593
|
"use strict";
|
|
@@ -48473,6 +48609,7 @@ var init_logger2 = __esm({
|
|
|
48473
48609
|
autopilotLog = createLogger2("autopilot");
|
|
48474
48610
|
heartbeatLog = createLogger2("heartbeat");
|
|
48475
48611
|
remoteNodeLog = createLogger2("remote-node");
|
|
48612
|
+
remoteTunnelLog = createLogger2("remote-tunnel");
|
|
48476
48613
|
nodeHealthMonitorLog = createLogger2("node-health-monitor");
|
|
48477
48614
|
peerExchangeLog = createLogger2("peer-exchange");
|
|
48478
48615
|
}
|
|
@@ -48809,11 +48946,11 @@ async function refreshAgentMemoryQmdIndex(rootDir, agentMemory) {
|
|
|
48809
48946
|
return;
|
|
48810
48947
|
}
|
|
48811
48948
|
const promise = (async () => {
|
|
48812
|
-
const { execFile:
|
|
48813
|
-
const { promisify:
|
|
48814
|
-
const
|
|
48949
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
48950
|
+
const { promisify: promisify13 } = await import("node:util");
|
|
48951
|
+
const execFileAsync4 = promisify13(execFile6);
|
|
48815
48952
|
try {
|
|
48816
|
-
await
|
|
48953
|
+
await execFileAsync4("qmd", buildQmdAgentMemoryCollectionAddArgs(rootDir, agentMemory.agentId), {
|
|
48817
48954
|
cwd: rootDir,
|
|
48818
48955
|
timeout: 4e3,
|
|
48819
48956
|
maxBuffer: 512 * 1024
|
|
@@ -48826,8 +48963,8 @@ ${stderr}`)) {
|
|
|
48826
48963
|
throw error;
|
|
48827
48964
|
}
|
|
48828
48965
|
}
|
|
48829
|
-
await
|
|
48830
|
-
await
|
|
48966
|
+
await execFileAsync4("qmd", ["update"], { cwd: rootDir, timeout: 3e4, maxBuffer: 1024 * 1024 });
|
|
48967
|
+
await execFileAsync4("qmd", ["embed"], { cwd: rootDir, timeout: 12e4, maxBuffer: 1024 * 1024 });
|
|
48831
48968
|
})();
|
|
48832
48969
|
agentQmdRefreshState.set(key, { lastStartedAt: now, inFlight: promise });
|
|
48833
48970
|
try {
|
|
@@ -48848,10 +48985,10 @@ async function searchAgentMemoryWithQmd(rootDir, agentMemory, query, limit) {
|
|
|
48848
48985
|
}
|
|
48849
48986
|
try {
|
|
48850
48987
|
await refreshAgentMemoryQmdIndex(rootDir, agentMemory);
|
|
48851
|
-
const { execFile:
|
|
48852
|
-
const { promisify:
|
|
48853
|
-
const
|
|
48854
|
-
const { stdout } = await
|
|
48988
|
+
const { execFile: execFile6 } = await import("node:child_process");
|
|
48989
|
+
const { promisify: promisify13 } = await import("node:util");
|
|
48990
|
+
const execFileAsync4 = promisify13(execFile6);
|
|
48991
|
+
const { stdout } = await execFileAsync4("qmd", buildQmdAgentMemorySearchArgs(rootDir, agentMemory.agentId, query, limit), {
|
|
48855
48992
|
cwd: rootDir,
|
|
48856
48993
|
timeout: 4e3,
|
|
48857
48994
|
maxBuffer: 1024 * 1024
|
|
@@ -54044,14 +54181,14 @@ function rethrowIfMergeAborted(error) {
|
|
|
54044
54181
|
throw error;
|
|
54045
54182
|
}
|
|
54046
54183
|
}
|
|
54047
|
-
async function runDeterministicVerification(store, rootDir, taskId, testCommand,
|
|
54184
|
+
async function runDeterministicVerification(store, rootDir, taskId, testCommand, buildCommand2, testSource, buildSource, signal) {
|
|
54048
54185
|
const result = { allPassed: true };
|
|
54049
|
-
if (!testCommand && !
|
|
54186
|
+
if (!testCommand && !buildCommand2) {
|
|
54050
54187
|
mergerLog.log(`${taskId}: no verification commands configured \u2014 skipping`);
|
|
54051
54188
|
return result;
|
|
54052
54189
|
}
|
|
54053
54190
|
const normalizedTestCommand = testCommand?.trim();
|
|
54054
|
-
const normalizedBuildCommand =
|
|
54191
|
+
const normalizedBuildCommand = buildCommand2?.trim();
|
|
54055
54192
|
const hasTestCommand = !!normalizedTestCommand;
|
|
54056
54193
|
const hasBuildCommand = !!normalizedBuildCommand;
|
|
54057
54194
|
const testSourceLabel = testSource === "inferred" ? " [inferred]" : "";
|
|
@@ -55545,7 +55682,7 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
55545
55682
|
result,
|
|
55546
55683
|
settings,
|
|
55547
55684
|
testCommand,
|
|
55548
|
-
buildCommand,
|
|
55685
|
+
buildCommand: buildCommand2,
|
|
55549
55686
|
testSource,
|
|
55550
55687
|
buildSource
|
|
55551
55688
|
} = params;
|
|
@@ -55614,14 +55751,14 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
55614
55751
|
);
|
|
55615
55752
|
mergerLog.log(`${taskId}: committed after auto-resolving all conflicts`);
|
|
55616
55753
|
}
|
|
55617
|
-
if (testCommand ||
|
|
55754
|
+
if (testCommand || buildCommand2) {
|
|
55618
55755
|
throwIfAborted(options.signal, taskId);
|
|
55619
55756
|
await runDeterministicVerification(
|
|
55620
55757
|
store,
|
|
55621
55758
|
rootDir,
|
|
55622
55759
|
taskId,
|
|
55623
55760
|
testCommand,
|
|
55624
|
-
|
|
55761
|
+
buildCommand2,
|
|
55625
55762
|
testSource,
|
|
55626
55763
|
buildSource,
|
|
55627
55764
|
options.signal
|
|
@@ -55637,14 +55774,14 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
55637
55774
|
).trim() === "0";
|
|
55638
55775
|
if (squashIsEmpty) {
|
|
55639
55776
|
mergerLog.log(`${taskId}: squash merge staged nothing \u2014 already merged`);
|
|
55640
|
-
if (testCommand ||
|
|
55777
|
+
if (testCommand || buildCommand2) {
|
|
55641
55778
|
throwIfAborted(options.signal, taskId);
|
|
55642
55779
|
await runDeterministicVerification(
|
|
55643
55780
|
store,
|
|
55644
55781
|
rootDir,
|
|
55645
55782
|
taskId,
|
|
55646
55783
|
testCommand,
|
|
55647
|
-
|
|
55784
|
+
buildCommand2,
|
|
55648
55785
|
testSource,
|
|
55649
55786
|
buildSource,
|
|
55650
55787
|
options.signal
|
|
@@ -55664,14 +55801,14 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
55664
55801
|
).trim() === "0";
|
|
55665
55802
|
if (squashIsEmpty) {
|
|
55666
55803
|
mergerLog.log(`${taskId}: squash merge staged nothing \u2014 already merged`);
|
|
55667
|
-
if (testCommand ||
|
|
55804
|
+
if (testCommand || buildCommand2) {
|
|
55668
55805
|
throwIfAborted(options.signal, taskId);
|
|
55669
55806
|
await runDeterministicVerification(
|
|
55670
55807
|
store,
|
|
55671
55808
|
rootDir,
|
|
55672
55809
|
taskId,
|
|
55673
55810
|
testCommand,
|
|
55674
|
-
|
|
55811
|
+
buildCommand2,
|
|
55675
55812
|
testSource,
|
|
55676
55813
|
buildSource,
|
|
55677
55814
|
options.signal
|
|
@@ -55691,7 +55828,7 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
55691
55828
|
return false;
|
|
55692
55829
|
}
|
|
55693
55830
|
}
|
|
55694
|
-
if (
|
|
55831
|
+
if (buildCommand2) {
|
|
55695
55832
|
throwIfAborted(options.signal, taskId);
|
|
55696
55833
|
const stagedFiles = await getStagedFiles(rootDir);
|
|
55697
55834
|
if (shouldSyncDependenciesForMerge(stagedFiles, hasInstallState(rootDir))) {
|
|
@@ -55712,7 +55849,7 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
55712
55849
|
simplifiedContext: attemptNum === 2,
|
|
55713
55850
|
options,
|
|
55714
55851
|
testCommand,
|
|
55715
|
-
buildCommand
|
|
55852
|
+
buildCommand: buildCommand2
|
|
55716
55853
|
});
|
|
55717
55854
|
if (!agentResult.success) {
|
|
55718
55855
|
const errorMessage = agentResult.error || "Build verification failed";
|
|
@@ -55727,14 +55864,14 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
55727
55864
|
}
|
|
55728
55865
|
throw new Error(`Build verification failed for ${taskId}: ${errorMessage}`);
|
|
55729
55866
|
}
|
|
55730
|
-
if (testCommand ||
|
|
55867
|
+
if (testCommand || buildCommand2) {
|
|
55731
55868
|
throwIfAborted(options.signal, taskId);
|
|
55732
55869
|
await runDeterministicVerification(
|
|
55733
55870
|
store,
|
|
55734
55871
|
rootDir,
|
|
55735
55872
|
taskId,
|
|
55736
55873
|
testCommand,
|
|
55737
|
-
|
|
55874
|
+
buildCommand2,
|
|
55738
55875
|
testSource,
|
|
55739
55876
|
buildSource,
|
|
55740
55877
|
options.signal
|
|
@@ -55762,7 +55899,7 @@ async function executeMergeAttempt(params, aiTracker) {
|
|
|
55762
55899
|
}
|
|
55763
55900
|
}
|
|
55764
55901
|
async function attemptWithTheirsStrategy(params) {
|
|
55765
|
-
const { rootDir, branch, commitLog, includeTaskId, taskId, store, settings, testCommand, buildCommand, testSource, buildSource } = params;
|
|
55902
|
+
const { rootDir, branch, commitLog, includeTaskId, taskId, store, settings, testCommand, buildCommand: buildCommand2, testSource, buildSource } = params;
|
|
55766
55903
|
mergerLog.log(`${taskId}: attempting merge with -X theirs strategy`);
|
|
55767
55904
|
try {
|
|
55768
55905
|
throwIfAborted(params.options.signal, taskId);
|
|
@@ -55782,14 +55919,14 @@ async function attemptWithTheirsStrategy(params) {
|
|
|
55782
55919
|
encoding: "utf-8"
|
|
55783
55920
|
}).trim();
|
|
55784
55921
|
if (staged === "0") {
|
|
55785
|
-
if (testCommand ||
|
|
55922
|
+
if (testCommand || buildCommand2) {
|
|
55786
55923
|
throwIfAborted(params.options.signal, taskId);
|
|
55787
55924
|
await runDeterministicVerification(
|
|
55788
55925
|
store,
|
|
55789
55926
|
rootDir,
|
|
55790
55927
|
taskId,
|
|
55791
55928
|
testCommand,
|
|
55792
|
-
|
|
55929
|
+
buildCommand2,
|
|
55793
55930
|
testSource,
|
|
55794
55931
|
buildSource,
|
|
55795
55932
|
params.options.signal
|
|
@@ -55806,14 +55943,14 @@ async function attemptWithTheirsStrategy(params) {
|
|
|
55806
55943
|
{ cwd: rootDir }
|
|
55807
55944
|
);
|
|
55808
55945
|
mergerLog.log(`${taskId}: committed with -X theirs auto-resolution`);
|
|
55809
|
-
if (testCommand ||
|
|
55946
|
+
if (testCommand || buildCommand2) {
|
|
55810
55947
|
throwIfAborted(params.options.signal, taskId);
|
|
55811
55948
|
await runDeterministicVerification(
|
|
55812
55949
|
store,
|
|
55813
55950
|
rootDir,
|
|
55814
55951
|
taskId,
|
|
55815
55952
|
testCommand,
|
|
55816
|
-
|
|
55953
|
+
buildCommand2,
|
|
55817
55954
|
testSource,
|
|
55818
55955
|
buildSource,
|
|
55819
55956
|
params.options.signal
|
|
@@ -55841,7 +55978,7 @@ async function runAiAgentForCommit(params) {
|
|
|
55841
55978
|
simplifiedContext,
|
|
55842
55979
|
options,
|
|
55843
55980
|
testCommand,
|
|
55844
|
-
buildCommand
|
|
55981
|
+
buildCommand: buildCommand2
|
|
55845
55982
|
} = params;
|
|
55846
55983
|
const settings = await store.getSettings();
|
|
55847
55984
|
let buildFailed = false;
|
|
@@ -55930,7 +56067,7 @@ async function runAiAgentForCommit(params) {
|
|
|
55930
56067
|
hasConflicts,
|
|
55931
56068
|
simplifiedContext,
|
|
55932
56069
|
testCommand,
|
|
55933
|
-
buildCommand,
|
|
56070
|
+
buildCommand: buildCommand2,
|
|
55934
56071
|
authorArg
|
|
55935
56072
|
});
|
|
55936
56073
|
mergerLog.log(`${taskId}: starting fresh merge agent session`);
|
|
@@ -55962,7 +56099,7 @@ async function runAiAgentForCommit(params) {
|
|
|
55962
56099
|
simplifiedContext: true,
|
|
55963
56100
|
// Also skip detailed context
|
|
55964
56101
|
testCommand,
|
|
55965
|
-
buildCommand,
|
|
56102
|
+
buildCommand: buildCommand2,
|
|
55966
56103
|
authorArg
|
|
55967
56104
|
});
|
|
55968
56105
|
try {
|
|
@@ -55998,7 +56135,7 @@ async function runAiAgentForCommit(params) {
|
|
|
55998
56135
|
encoding: "utf-8"
|
|
55999
56136
|
}).trim();
|
|
56000
56137
|
if (staged !== "0") {
|
|
56001
|
-
if (!
|
|
56138
|
+
if (!buildCommand2) {
|
|
56002
56139
|
throwIfAborted(options.signal, taskId);
|
|
56003
56140
|
mergerLog.log("Agent didn't commit \u2014 committing with fallback message");
|
|
56004
56141
|
const escapedLog = commitLog.replace(/"/g, '\\"');
|
|
@@ -56025,7 +56162,7 @@ async function runAiAgentForCommit(params) {
|
|
|
56025
56162
|
}
|
|
56026
56163
|
}
|
|
56027
56164
|
function buildMergePrompt(params) {
|
|
56028
|
-
const { taskId, branch, commitLog, diffStat, hasConflicts, simplifiedContext, testCommand, buildCommand, authorArg } = params;
|
|
56165
|
+
const { taskId, branch, commitLog, diffStat, hasConflicts, simplifiedContext, testCommand, buildCommand: buildCommand2, authorArg } = params;
|
|
56029
56166
|
const truncatedCommitLog = truncateWithEllipsis(commitLog, MERGE_COMMIT_LOG_MAX_CHARS);
|
|
56030
56167
|
const truncatedDiffStat = truncateWithEllipsis(diffStat, MERGE_DIFF_STAT_MAX_CHARS);
|
|
56031
56168
|
const parts = [
|
|
@@ -56073,11 +56210,11 @@ function buildMergePrompt(params) {
|
|
|
56073
56210
|
"If it exits non-zero, call `fn_report_build_failure` with the concrete error output and stop without committing."
|
|
56074
56211
|
);
|
|
56075
56212
|
}
|
|
56076
|
-
if (
|
|
56213
|
+
if (buildCommand2) {
|
|
56077
56214
|
parts.push(
|
|
56078
56215
|
"",
|
|
56079
56216
|
"## Build command",
|
|
56080
|
-
`Build command: \`${
|
|
56217
|
+
`Build command: \`${buildCommand2}\``,
|
|
56081
56218
|
"",
|
|
56082
56219
|
"This command is mandatory before commit.",
|
|
56083
56220
|
"Run it with the bash tool in the current worktree and inspect the actual exit code.",
|
|
@@ -58487,6 +58624,8 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
58487
58624
|
loopRecoveryState = /* @__PURE__ */ new Map();
|
|
58488
58625
|
/** Spawned child agent IDs per parent task ID. Used for lifecycle tracking. */
|
|
58489
58626
|
spawnedAgents = /* @__PURE__ */ new Map();
|
|
58627
|
+
/** Per-task baseline of agent cumulative token counters used for delta persistence. */
|
|
58628
|
+
tokenUsageBaselines = /* @__PURE__ */ new Map();
|
|
58490
58629
|
async finalizeAlreadyReviewedTask(taskId) {
|
|
58491
58630
|
const latestTask = await this.store.getTask(taskId);
|
|
58492
58631
|
if (!latestTask || latestTask.column !== "in-review") {
|
|
@@ -58602,6 +58741,65 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
58602
58741
|
async getTaskCompletionBlocker(task) {
|
|
58603
58742
|
return getTaskCompletionBlockerForStore(this.store, task);
|
|
58604
58743
|
}
|
|
58744
|
+
async initializeTokenUsageBaseline(taskId, task) {
|
|
58745
|
+
if (!this.options.agentStore) return;
|
|
58746
|
+
const currentTask = task ?? await this.store.getTask(taskId);
|
|
58747
|
+
const assignedAgentId = currentTask.assignedAgentId?.trim();
|
|
58748
|
+
if (!assignedAgentId) return;
|
|
58749
|
+
const existing = this.tokenUsageBaselines.get(taskId);
|
|
58750
|
+
if (existing?.agentId === assignedAgentId) return;
|
|
58751
|
+
const agent = await this.options.agentStore.getAgent(assignedAgentId);
|
|
58752
|
+
if (!agent) return;
|
|
58753
|
+
this.tokenUsageBaselines.set(taskId, {
|
|
58754
|
+
agentId: assignedAgentId,
|
|
58755
|
+
inputTokens: agent.totalInputTokens ?? 0,
|
|
58756
|
+
outputTokens: agent.totalOutputTokens ?? 0
|
|
58757
|
+
});
|
|
58758
|
+
}
|
|
58759
|
+
async persistTokenUsage(taskId) {
|
|
58760
|
+
if (!this.options.agentStore) return;
|
|
58761
|
+
const task = await this.store.getTask(taskId);
|
|
58762
|
+
const assignedAgentId = task.assignedAgentId?.trim();
|
|
58763
|
+
if (!assignedAgentId) return;
|
|
58764
|
+
const agent = await this.options.agentStore.getAgent(assignedAgentId);
|
|
58765
|
+
if (!agent) return;
|
|
58766
|
+
const currentInputTokens = agent.totalInputTokens ?? 0;
|
|
58767
|
+
const currentOutputTokens = agent.totalOutputTokens ?? 0;
|
|
58768
|
+
const baseline = this.tokenUsageBaselines.get(taskId);
|
|
58769
|
+
if (!baseline || baseline.agentId !== assignedAgentId) {
|
|
58770
|
+
this.tokenUsageBaselines.set(taskId, {
|
|
58771
|
+
agentId: assignedAgentId,
|
|
58772
|
+
inputTokens: currentInputTokens,
|
|
58773
|
+
outputTokens: currentOutputTokens
|
|
58774
|
+
});
|
|
58775
|
+
return;
|
|
58776
|
+
}
|
|
58777
|
+
const inputDelta = Math.max(0, currentInputTokens - baseline.inputTokens);
|
|
58778
|
+
const outputDelta = Math.max(0, currentOutputTokens - baseline.outputTokens);
|
|
58779
|
+
this.tokenUsageBaselines.set(taskId, {
|
|
58780
|
+
agentId: assignedAgentId,
|
|
58781
|
+
inputTokens: currentInputTokens,
|
|
58782
|
+
outputTokens: currentOutputTokens
|
|
58783
|
+
});
|
|
58784
|
+
if (inputDelta === 0 && outputDelta === 0 && !task.tokenUsage) {
|
|
58785
|
+
return;
|
|
58786
|
+
}
|
|
58787
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
58788
|
+
const mergedInputTokens = (task.tokenUsage?.inputTokens ?? 0) + inputDelta;
|
|
58789
|
+
const mergedOutputTokens = (task.tokenUsage?.outputTokens ?? 0) + outputDelta;
|
|
58790
|
+
const cachedTokens = task.tokenUsage?.cachedTokens ?? 0;
|
|
58791
|
+
const totalTokens = mergedInputTokens + mergedOutputTokens + cachedTokens;
|
|
58792
|
+
await this.store.updateTask(taskId, {
|
|
58793
|
+
tokenUsage: {
|
|
58794
|
+
inputTokens: mergedInputTokens,
|
|
58795
|
+
outputTokens: mergedOutputTokens,
|
|
58796
|
+
cachedTokens,
|
|
58797
|
+
totalTokens,
|
|
58798
|
+
firstUsedAt: task.tokenUsage?.firstUsedAt ?? now,
|
|
58799
|
+
lastUsedAt: now
|
|
58800
|
+
}
|
|
58801
|
+
});
|
|
58802
|
+
}
|
|
58605
58803
|
/**
|
|
58606
58804
|
* Execute a review handoff: move the task to in-review column with
|
|
58607
58805
|
* awaiting-user-review status, assign the requesting user, and dispose
|
|
@@ -58624,6 +58822,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
58624
58822
|
},
|
|
58625
58823
|
this.currentRunContext
|
|
58626
58824
|
);
|
|
58825
|
+
await this.persistTokenUsage(task.id);
|
|
58627
58826
|
await this.store.moveTask(task.id, "in-review");
|
|
58628
58827
|
if (this.activeSessions.has(task.id)) {
|
|
58629
58828
|
const { session: activeSession } = this.activeSessions.get(task.id);
|
|
@@ -58662,6 +58861,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
58662
58861
|
executorLog.log(`${task.id}: fast mode \u2014 skipping workflow steps on auto-recovery`);
|
|
58663
58862
|
}
|
|
58664
58863
|
}
|
|
58864
|
+
await this.persistTokenUsage(task.id);
|
|
58665
58865
|
await this.store.moveTask(task.id, "in-review");
|
|
58666
58866
|
await this.store.logEntry(task.id, "Auto-recovered: task work was complete but stuck in in-progress \u2014 moved to in-review");
|
|
58667
58867
|
executorLog.log(`\u2713 ${task.id} auto-recovered completed task \u2192 in-review`);
|
|
@@ -59041,6 +59241,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59041
59241
|
this.options.onStart?.(task, worktreePath);
|
|
59042
59242
|
const detail = await this.store.getTask(task.id);
|
|
59043
59243
|
executorLog.log(`${task.id}: fetched task detail (${detail.steps.length} steps, prompt length=${detail.prompt?.length ?? 0})`);
|
|
59244
|
+
await this.initializeTokenUsageBaseline(task.id, detail);
|
|
59044
59245
|
if (detail.steps.length === 0) {
|
|
59045
59246
|
const steps = await this.store.parseStepsFromPrompt(task.id);
|
|
59046
59247
|
if (steps.length > 0) {
|
|
@@ -59088,6 +59289,9 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59088
59289
|
} catch (err) {
|
|
59089
59290
|
executorLog.warn(`${task.id}: failed to update step ${stepIndex} status: ${err}`);
|
|
59090
59291
|
}
|
|
59292
|
+
this.persistTokenUsage(task.id).catch((err) => {
|
|
59293
|
+
executorLog.warn(`${task.id}: failed to persist token usage on step ${stepIndex} complete: ${err}`);
|
|
59294
|
+
});
|
|
59091
59295
|
}
|
|
59092
59296
|
});
|
|
59093
59297
|
this.activeStepExecutors.set(task.id, stepExecutor);
|
|
@@ -59137,6 +59341,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59137
59341
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
59138
59342
|
}
|
|
59139
59343
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
59344
|
+
await this.persistTokenUsage(task.id);
|
|
59140
59345
|
await this.store.moveTask(task.id, "in-review");
|
|
59141
59346
|
await audit.database({ type: "task:move", target: task.id, metadata: { to: "in-review" } });
|
|
59142
59347
|
executorLog.log(`\u2713 ${task.id} completed (step-session) \u2192 in-review`);
|
|
@@ -59145,6 +59350,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59145
59350
|
const failedSteps = results.filter((r) => !r.success);
|
|
59146
59351
|
const errorSummary = failedSteps.map((r) => `Step ${r.stepIndex}: ${r.error || "unknown error"}`).join("; ");
|
|
59147
59352
|
await this.store.updateTask(task.id, { status: "failed", error: errorSummary });
|
|
59353
|
+
await this.persistTokenUsage(task.id);
|
|
59148
59354
|
await this.store.moveTask(task.id, "in-review");
|
|
59149
59355
|
executorLog.log(`\u2717 ${task.id} step-session failed \u2192 in-review: ${errorSummary}`);
|
|
59150
59356
|
this.options.onError?.(task, new Error(errorSummary));
|
|
@@ -59221,6 +59427,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59221
59427
|
recoveryRetryCount: null,
|
|
59222
59428
|
nextRecoveryAt: null
|
|
59223
59429
|
});
|
|
59430
|
+
await this.persistTokenUsage(task.id);
|
|
59224
59431
|
await this.store.moveTask(task.id, "in-review");
|
|
59225
59432
|
executorLog.log(`\u2717 ${task.id} transient retries exhausted \u2192 in-review`);
|
|
59226
59433
|
this.options.onError?.(task, err instanceof Error ? err : new Error(errorMessage));
|
|
@@ -59228,6 +59435,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59228
59435
|
executorLog.error(`\u2717 ${task.id} step-session execution failed:`, errorDetail);
|
|
59229
59436
|
await this.store.logEntry(task.id, `Step-session execution failed: ${errorMessage}`, errorStack ?? errorDetail, this.currentRunContext);
|
|
59230
59437
|
await this.store.updateTask(task.id, { status: "failed", error: errorMessage });
|
|
59438
|
+
await this.persistTokenUsage(task.id);
|
|
59231
59439
|
await this.store.moveTask(task.id, "in-review");
|
|
59232
59440
|
executorLog.log(`\u2717 ${task.id} step-session execution failed \u2192 in-review`);
|
|
59233
59441
|
this.options.onError?.(task, err instanceof Error ? err : new Error(errorMessage));
|
|
@@ -59462,6 +59670,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59462
59670
|
if (await this.shouldFinalizeCompletedTask(task.id, taskDone)) {
|
|
59463
59671
|
executorLog.log(`${task.id} paused after completion (graceful session exit) \u2014 finalizing to in-review`);
|
|
59464
59672
|
await this.store.logEntry(task.id, "Execution paused after completion \u2014 finalizing to in-review");
|
|
59673
|
+
await this.persistTokenUsage(task.id);
|
|
59465
59674
|
await this.store.moveTask(task.id, "in-review");
|
|
59466
59675
|
this.options.onComplete?.(task);
|
|
59467
59676
|
} else {
|
|
@@ -59511,6 +59720,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59511
59720
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
59512
59721
|
}
|
|
59513
59722
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
59723
|
+
await this.persistTokenUsage(task.id);
|
|
59514
59724
|
await this.store.moveTask(task.id, "in-review");
|
|
59515
59725
|
executorLog.log(`\u2713 ${task.id} completed \u2192 in-review`);
|
|
59516
59726
|
this.options.onComplete?.(task);
|
|
@@ -59607,6 +59817,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59607
59817
|
await this.store.logEntry(task.id, "Fast mode \u2014 pre-merge workflow steps skipped", void 0, this.currentRunContext);
|
|
59608
59818
|
}
|
|
59609
59819
|
await this.store.updateTask(task.id, { workflowStepRetries: void 0, taskDoneRetryCount: null });
|
|
59820
|
+
await this.persistTokenUsage(task.id);
|
|
59610
59821
|
await this.store.moveTask(task.id, "in-review");
|
|
59611
59822
|
executorLog.log(`\u2713 ${task.id} completed on retry \u2192 in-review`);
|
|
59612
59823
|
this.options.onComplete?.(task);
|
|
@@ -59631,6 +59842,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59631
59842
|
} else {
|
|
59632
59843
|
await this.store.updateTask(task.id, { status: "failed", error: errorMessage });
|
|
59633
59844
|
await this.store.logEntry(task.id, `${errorMessage} \u2014 moved to in-review for inspection`, void 0, this.currentRunContext);
|
|
59845
|
+
await this.persistTokenUsage(task.id);
|
|
59634
59846
|
await this.store.moveTask(task.id, "in-review");
|
|
59635
59847
|
executorLog.log(`\u2717 ${task.id} failed after ${MAX_TASK_DONE_SESSION_RETRIES} retries \u2014 no fn_task_done \u2192 in-review`);
|
|
59636
59848
|
}
|
|
@@ -59694,6 +59906,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59694
59906
|
if (await this.shouldFinalizeCompletedTask(task.id, taskDone)) {
|
|
59695
59907
|
executorLog.log(`${task.id} paused after completion \u2014 finalizing to in-review`);
|
|
59696
59908
|
await this.store.logEntry(task.id, "Execution paused after completion \u2014 finalizing to in-review", void 0, this.currentRunContext);
|
|
59909
|
+
await this.persistTokenUsage(task.id);
|
|
59697
59910
|
await this.store.moveTask(task.id, "in-review");
|
|
59698
59911
|
this.options.onComplete?.(task);
|
|
59699
59912
|
} else {
|
|
@@ -59819,6 +60032,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59819
60032
|
recoveryRetryCount: null,
|
|
59820
60033
|
nextRecoveryAt: null
|
|
59821
60034
|
});
|
|
60035
|
+
await this.persistTokenUsage(task.id);
|
|
59822
60036
|
await this.store.moveTask(task.id, "in-review");
|
|
59823
60037
|
executorLog.log(`\u2717 ${task.id} transient retries exhausted \u2192 in-review`);
|
|
59824
60038
|
this.options.onError?.(task, err instanceof Error ? err : new Error(errorMessage));
|
|
@@ -59827,6 +60041,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59827
60041
|
executorLog.error(`\u2717 ${task.id} execution failed:`, errorDetail);
|
|
59828
60042
|
await this.store.logEntry(task.id, `Execution failed: ${errorMessage}`, errorStack ?? errorDetail, this.currentRunContext);
|
|
59829
60043
|
await this.store.updateTask(task.id, { status: "failed", error: errorMessage });
|
|
60044
|
+
await this.persistTokenUsage(task.id);
|
|
59830
60045
|
await this.store.moveTask(task.id, "in-review");
|
|
59831
60046
|
executorLog.log(`\u2717 ${task.id} execution failed \u2192 in-review`);
|
|
59832
60047
|
this.options.onError?.(task, err instanceof Error ? err : new Error(errorMessage));
|
|
@@ -59840,6 +60055,7 @@ Lint, tests, and typecheck are also hard quality gates:
|
|
|
59840
60055
|
executorLog.warn(`terminateAllChildren failed for ${task.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
59841
60056
|
}
|
|
59842
60057
|
this.loopRecoveryState.delete(task.id);
|
|
60058
|
+
this.tokenUsageBaselines.delete(task.id);
|
|
59843
60059
|
if (stuckRequeue === true) {
|
|
59844
60060
|
try {
|
|
59845
60061
|
const latestTask = await this.store.getTask(task.id);
|
|
@@ -70580,8 +70796,571 @@ var init_project_manager = __esm({
|
|
|
70580
70796
|
}
|
|
70581
70797
|
});
|
|
70582
70798
|
|
|
70799
|
+
// ../engine/src/remote-access/provider-adapters.ts
|
|
70800
|
+
import { accessSync, constants as fsConstants } from "node:fs";
|
|
70801
|
+
function isAbsoluteOrPathLike(input) {
|
|
70802
|
+
return input.startsWith("/") || input.startsWith("./") || input.startsWith("../");
|
|
70803
|
+
}
|
|
70804
|
+
function assertNonEmpty(value, label) {
|
|
70805
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
70806
|
+
throw new Error(`invalid_config:${label} must be a non-empty string`);
|
|
70807
|
+
}
|
|
70808
|
+
}
|
|
70809
|
+
function ensureNumberInRange(value, label) {
|
|
70810
|
+
if (value === void 0) {
|
|
70811
|
+
return void 0;
|
|
70812
|
+
}
|
|
70813
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
70814
|
+
throw new Error(`invalid_config:${label} must be a positive number`);
|
|
70815
|
+
}
|
|
70816
|
+
return Math.floor(value);
|
|
70817
|
+
}
|
|
70818
|
+
function collectSensitiveValues(config) {
|
|
70819
|
+
const values = /* @__PURE__ */ new Set();
|
|
70820
|
+
const env = config.env ?? {};
|
|
70821
|
+
const tokenEnvVar = config.tokenEnvVar;
|
|
70822
|
+
if (typeof tokenEnvVar === "string" && tokenEnvVar.trim().length > 0) {
|
|
70823
|
+
const tokenValue = env[tokenEnvVar] ?? process.env[tokenEnvVar];
|
|
70824
|
+
if (!tokenValue) {
|
|
70825
|
+
throw new Error(`invalid_config:missing credential in env var ${tokenEnvVar}`);
|
|
70826
|
+
}
|
|
70827
|
+
values.add(tokenValue);
|
|
70828
|
+
}
|
|
70829
|
+
for (const envName of config.sensitiveEnvVars ?? []) {
|
|
70830
|
+
const envValue = env[envName] ?? process.env[envName];
|
|
70831
|
+
if (envValue) {
|
|
70832
|
+
values.add(envValue);
|
|
70833
|
+
}
|
|
70834
|
+
}
|
|
70835
|
+
return [...values].sort((a, b) => b.length - a.length);
|
|
70836
|
+
}
|
|
70837
|
+
function redactValue(input, sensitiveValues) {
|
|
70838
|
+
let redacted = input;
|
|
70839
|
+
for (const value of sensitiveValues) {
|
|
70840
|
+
redacted = redacted.split(value).join("[REDACTED]");
|
|
70841
|
+
}
|
|
70842
|
+
return redacted;
|
|
70843
|
+
}
|
|
70844
|
+
function redactArgs(args, sensitiveValues) {
|
|
70845
|
+
return args.map((arg) => redactValue(arg, sensitiveValues));
|
|
70846
|
+
}
|
|
70847
|
+
function buildCommand(config) {
|
|
70848
|
+
const command = config.executablePath.trim();
|
|
70849
|
+
const args = [...config.args];
|
|
70850
|
+
const env = {
|
|
70851
|
+
...process.env,
|
|
70852
|
+
...config.env ?? {}
|
|
70853
|
+
};
|
|
70854
|
+
const sensitiveValues = collectSensitiveValues(config);
|
|
70855
|
+
const redactedPreview = [command, ...redactArgs(args, sensitiveValues)].join(" ").trim();
|
|
70856
|
+
return {
|
|
70857
|
+
provider: config.provider,
|
|
70858
|
+
command,
|
|
70859
|
+
args,
|
|
70860
|
+
cwd: config.cwd,
|
|
70861
|
+
env,
|
|
70862
|
+
redactedPreview,
|
|
70863
|
+
sensitiveValues,
|
|
70864
|
+
readinessTimeoutMs: config.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS,
|
|
70865
|
+
stopTimeoutMs: config.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS
|
|
70866
|
+
};
|
|
70867
|
+
}
|
|
70868
|
+
function parseCommonReadiness(line) {
|
|
70869
|
+
const normalized = line.trim();
|
|
70870
|
+
if (!normalized) {
|
|
70871
|
+
return null;
|
|
70872
|
+
}
|
|
70873
|
+
const urlMatch = normalized.match(URL_PATTERN);
|
|
70874
|
+
const url = urlMatch?.[1];
|
|
70875
|
+
if (/\b(connected|ready|available|started|serving)\b/i.test(normalized)) {
|
|
70876
|
+
return { ready: true, url };
|
|
70877
|
+
}
|
|
70878
|
+
if (url && /\b(trycloudflare|tailscale|ts\.net|serve|funnel)\b/i.test(normalized)) {
|
|
70879
|
+
return { ready: true, url };
|
|
70880
|
+
}
|
|
70881
|
+
return null;
|
|
70882
|
+
}
|
|
70883
|
+
function validateBaseConfig(config, provider) {
|
|
70884
|
+
if (config.provider !== provider) {
|
|
70885
|
+
throw new Error(`invalid_config:config provider ${config.provider} does not match ${provider}`);
|
|
70886
|
+
}
|
|
70887
|
+
assertNonEmpty(config.executablePath, "executablePath");
|
|
70888
|
+
if (!Array.isArray(config.args)) {
|
|
70889
|
+
throw new Error("invalid_config:args must be an array");
|
|
70890
|
+
}
|
|
70891
|
+
for (const [index, arg] of config.args.entries()) {
|
|
70892
|
+
assertNonEmpty(arg, `args[${index}]`);
|
|
70893
|
+
}
|
|
70894
|
+
if (config.cwd !== void 0) {
|
|
70895
|
+
assertNonEmpty(config.cwd, "cwd");
|
|
70896
|
+
}
|
|
70897
|
+
if (config.tokenEnvVar !== void 0) {
|
|
70898
|
+
assertNonEmpty(config.tokenEnvVar, "tokenEnvVar");
|
|
70899
|
+
}
|
|
70900
|
+
ensureNumberInRange(config.readinessTimeoutMs, "readinessTimeoutMs");
|
|
70901
|
+
ensureNumberInRange(config.stopTimeoutMs, "stopTimeoutMs");
|
|
70902
|
+
}
|
|
70903
|
+
function getTunnelProviderAdapter(provider) {
|
|
70904
|
+
return ADAPTERS[provider];
|
|
70905
|
+
}
|
|
70906
|
+
function redactTunnelText(input, sensitiveValues) {
|
|
70907
|
+
return redactValue(input, sensitiveValues);
|
|
70908
|
+
}
|
|
70909
|
+
var DEFAULT_READINESS_TIMEOUT_MS, DEFAULT_STOP_TIMEOUT_MS, URL_PATTERN, tailscaleAdapter, cloudflareAdapter, ADAPTERS;
|
|
70910
|
+
var init_provider_adapters = __esm({
|
|
70911
|
+
"../engine/src/remote-access/provider-adapters.ts"() {
|
|
70912
|
+
"use strict";
|
|
70913
|
+
DEFAULT_READINESS_TIMEOUT_MS = 2e4;
|
|
70914
|
+
DEFAULT_STOP_TIMEOUT_MS = 5e3;
|
|
70915
|
+
URL_PATTERN = /(https?:\/\/[^\s]+)/i;
|
|
70916
|
+
tailscaleAdapter = {
|
|
70917
|
+
provider: "tailscale",
|
|
70918
|
+
validateConfig(config) {
|
|
70919
|
+
validateBaseConfig(config, "tailscale");
|
|
70920
|
+
},
|
|
70921
|
+
buildCommand(config) {
|
|
70922
|
+
this.validateConfig(config);
|
|
70923
|
+
return buildCommand(config);
|
|
70924
|
+
},
|
|
70925
|
+
parseReadiness(line, _stream) {
|
|
70926
|
+
return parseCommonReadiness(line);
|
|
70927
|
+
}
|
|
70928
|
+
};
|
|
70929
|
+
cloudflareAdapter = {
|
|
70930
|
+
provider: "cloudflare",
|
|
70931
|
+
validateConfig(config) {
|
|
70932
|
+
validateBaseConfig(config, "cloudflare");
|
|
70933
|
+
if (config.provider === "cloudflare" && config.quickTunnel === true) {
|
|
70934
|
+
return;
|
|
70935
|
+
}
|
|
70936
|
+
if ("credentialsPath" in config && config.credentialsPath !== void 0) {
|
|
70937
|
+
assertNonEmpty(config.credentialsPath, "credentialsPath");
|
|
70938
|
+
if (isAbsoluteOrPathLike(config.credentialsPath)) {
|
|
70939
|
+
accessSync(config.credentialsPath, fsConstants.R_OK);
|
|
70940
|
+
}
|
|
70941
|
+
}
|
|
70942
|
+
},
|
|
70943
|
+
buildCommand(config) {
|
|
70944
|
+
this.validateConfig(config);
|
|
70945
|
+
return buildCommand(config);
|
|
70946
|
+
},
|
|
70947
|
+
parseReadiness(line, _stream) {
|
|
70948
|
+
return parseCommonReadiness(line);
|
|
70949
|
+
}
|
|
70950
|
+
};
|
|
70951
|
+
ADAPTERS = {
|
|
70952
|
+
tailscale: tailscaleAdapter,
|
|
70953
|
+
cloudflare: cloudflareAdapter
|
|
70954
|
+
};
|
|
70955
|
+
}
|
|
70956
|
+
});
|
|
70957
|
+
|
|
70958
|
+
// ../engine/src/remote-access/tunnel-process-manager.ts
|
|
70959
|
+
import { EventEmitter as EventEmitter18 } from "node:events";
|
|
70960
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
70961
|
+
function nowIso() {
|
|
70962
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
70963
|
+
}
|
|
70964
|
+
function normalizeError(input) {
|
|
70965
|
+
if (input instanceof Error) {
|
|
70966
|
+
return input;
|
|
70967
|
+
}
|
|
70968
|
+
return new Error(String(input));
|
|
70969
|
+
}
|
|
70970
|
+
function maskSensitive(message, processHandle) {
|
|
70971
|
+
if (!processHandle) {
|
|
70972
|
+
return message;
|
|
70973
|
+
}
|
|
70974
|
+
return redactTunnelText(message, processHandle.command.sensitiveValues);
|
|
70975
|
+
}
|
|
70976
|
+
function killManagedProcess(child, signal) {
|
|
70977
|
+
if (typeof child.pid !== "number") {
|
|
70978
|
+
return;
|
|
70979
|
+
}
|
|
70980
|
+
if (process.platform !== "win32") {
|
|
70981
|
+
try {
|
|
70982
|
+
process.kill(-child.pid, signal);
|
|
70983
|
+
return;
|
|
70984
|
+
} catch {
|
|
70985
|
+
}
|
|
70986
|
+
}
|
|
70987
|
+
try {
|
|
70988
|
+
process.kill(child.pid, signal);
|
|
70989
|
+
} catch {
|
|
70990
|
+
}
|
|
70991
|
+
}
|
|
70992
|
+
function toStateError(code, err) {
|
|
70993
|
+
const normalized = normalizeError(err);
|
|
70994
|
+
return {
|
|
70995
|
+
code,
|
|
70996
|
+
message: normalized.message,
|
|
70997
|
+
at: nowIso()
|
|
70998
|
+
};
|
|
70999
|
+
}
|
|
71000
|
+
var DEFAULT_MAX_LOG_ENTRIES, DEFAULT_STOP_TIMEOUT_MS2, LineBuffer, TunnelProcessManager;
|
|
71001
|
+
var init_tunnel_process_manager = __esm({
|
|
71002
|
+
"../engine/src/remote-access/tunnel-process-manager.ts"() {
|
|
71003
|
+
"use strict";
|
|
71004
|
+
init_logger2();
|
|
71005
|
+
init_provider_adapters();
|
|
71006
|
+
DEFAULT_MAX_LOG_ENTRIES = 400;
|
|
71007
|
+
DEFAULT_STOP_TIMEOUT_MS2 = 5e3;
|
|
71008
|
+
LineBuffer = class {
|
|
71009
|
+
pending = "";
|
|
71010
|
+
push(chunk) {
|
|
71011
|
+
this.pending += chunk;
|
|
71012
|
+
const lines = this.pending.split(/\r?\n/);
|
|
71013
|
+
this.pending = lines.pop() ?? "";
|
|
71014
|
+
return lines.map((line) => line.trim()).filter(Boolean);
|
|
71015
|
+
}
|
|
71016
|
+
flush() {
|
|
71017
|
+
const tail = this.pending.trim();
|
|
71018
|
+
this.pending = "";
|
|
71019
|
+
return tail ? [tail] : [];
|
|
71020
|
+
}
|
|
71021
|
+
};
|
|
71022
|
+
TunnelProcessManager = class extends EventEmitter18 {
|
|
71023
|
+
maxLogEntries;
|
|
71024
|
+
defaultStopTimeoutMs;
|
|
71025
|
+
spawnImpl;
|
|
71026
|
+
status = {
|
|
71027
|
+
provider: null,
|
|
71028
|
+
state: "stopped",
|
|
71029
|
+
pid: null,
|
|
71030
|
+
startedAt: null,
|
|
71031
|
+
stoppedAt: null,
|
|
71032
|
+
url: null,
|
|
71033
|
+
lastError: null
|
|
71034
|
+
};
|
|
71035
|
+
logs = [];
|
|
71036
|
+
statusListeners = /* @__PURE__ */ new Set();
|
|
71037
|
+
logListeners = /* @__PURE__ */ new Set();
|
|
71038
|
+
processHandle = null;
|
|
71039
|
+
readinessTimer = null;
|
|
71040
|
+
stopTimer = null;
|
|
71041
|
+
operationChain = Promise.resolve();
|
|
71042
|
+
expectedStop = false;
|
|
71043
|
+
activeStopPromise = null;
|
|
71044
|
+
constructor(options = {}) {
|
|
71045
|
+
super();
|
|
71046
|
+
this.maxLogEntries = options.maxLogEntries ?? DEFAULT_MAX_LOG_ENTRIES;
|
|
71047
|
+
this.defaultStopTimeoutMs = options.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS2;
|
|
71048
|
+
this.spawnImpl = options.spawnImpl ?? spawn2;
|
|
71049
|
+
}
|
|
71050
|
+
getStatus() {
|
|
71051
|
+
return { ...this.status, lastError: this.status.lastError ? { ...this.status.lastError } : null };
|
|
71052
|
+
}
|
|
71053
|
+
subscribeStatus(listener) {
|
|
71054
|
+
this.statusListeners.add(listener);
|
|
71055
|
+
listener(this.getStatus());
|
|
71056
|
+
return () => {
|
|
71057
|
+
this.statusListeners.delete(listener);
|
|
71058
|
+
};
|
|
71059
|
+
}
|
|
71060
|
+
subscribeLogs(listener) {
|
|
71061
|
+
this.logListeners.add(listener);
|
|
71062
|
+
return () => {
|
|
71063
|
+
this.logListeners.delete(listener);
|
|
71064
|
+
};
|
|
71065
|
+
}
|
|
71066
|
+
async start(provider, config) {
|
|
71067
|
+
return this.runExclusive(async () => {
|
|
71068
|
+
if (this.processHandle || this.status.state === "starting" || this.status.state === "running") {
|
|
71069
|
+
throw new Error("already_running:tunnel process is already active");
|
|
71070
|
+
}
|
|
71071
|
+
await this.startInternal(provider, config);
|
|
71072
|
+
});
|
|
71073
|
+
}
|
|
71074
|
+
async stop() {
|
|
71075
|
+
return this.runExclusive(async () => {
|
|
71076
|
+
await this.stopInternal();
|
|
71077
|
+
});
|
|
71078
|
+
}
|
|
71079
|
+
async switchProvider(target, config) {
|
|
71080
|
+
return this.runExclusive(async () => {
|
|
71081
|
+
const previousProvider = this.status.provider;
|
|
71082
|
+
if (this.processHandle) {
|
|
71083
|
+
await this.stopInternal();
|
|
71084
|
+
}
|
|
71085
|
+
try {
|
|
71086
|
+
await this.startInternal(target, config);
|
|
71087
|
+
} catch (error) {
|
|
71088
|
+
const stateError = toStateError("switch_failed", error);
|
|
71089
|
+
const redactedMessage = this.redactForProviderConfig(target, config, stateError.message);
|
|
71090
|
+
this.updateStatus({
|
|
71091
|
+
provider: target,
|
|
71092
|
+
state: "failed",
|
|
71093
|
+
pid: null,
|
|
71094
|
+
startedAt: null,
|
|
71095
|
+
stoppedAt: nowIso(),
|
|
71096
|
+
url: null,
|
|
71097
|
+
lastError: {
|
|
71098
|
+
...stateError,
|
|
71099
|
+
message: redactedMessage
|
|
71100
|
+
}
|
|
71101
|
+
});
|
|
71102
|
+
this.emitLog("error", "manager", `Provider switch failed (${previousProvider ?? "none"} -> ${target}): ${redactedMessage}`);
|
|
71103
|
+
throw error;
|
|
71104
|
+
}
|
|
71105
|
+
});
|
|
71106
|
+
}
|
|
71107
|
+
redactForProviderConfig(provider, config, message) {
|
|
71108
|
+
try {
|
|
71109
|
+
const adapter = getTunnelProviderAdapter(provider);
|
|
71110
|
+
const command = adapter.buildCommand(config);
|
|
71111
|
+
return redactTunnelText(message, command.sensitiveValues);
|
|
71112
|
+
} catch {
|
|
71113
|
+
return message;
|
|
71114
|
+
}
|
|
71115
|
+
}
|
|
71116
|
+
async runExclusive(operation) {
|
|
71117
|
+
const next = this.operationChain.then(operation);
|
|
71118
|
+
this.operationChain = next.catch(() => void 0);
|
|
71119
|
+
return next;
|
|
71120
|
+
}
|
|
71121
|
+
async startInternal(provider, config) {
|
|
71122
|
+
const adapter = getTunnelProviderAdapter(provider);
|
|
71123
|
+
if (config.provider !== provider) {
|
|
71124
|
+
throw new Error(`invalid_config:provider mismatch (${config.provider} vs ${provider})`);
|
|
71125
|
+
}
|
|
71126
|
+
try {
|
|
71127
|
+
adapter.validateConfig(config);
|
|
71128
|
+
} catch (error) {
|
|
71129
|
+
const stateError = toStateError("invalid_config", error);
|
|
71130
|
+
this.updateStatus({
|
|
71131
|
+
provider,
|
|
71132
|
+
state: "failed",
|
|
71133
|
+
pid: null,
|
|
71134
|
+
startedAt: null,
|
|
71135
|
+
stoppedAt: nowIso(),
|
|
71136
|
+
url: null,
|
|
71137
|
+
lastError: stateError
|
|
71138
|
+
});
|
|
71139
|
+
this.emitLog("error", "manager", `Configuration validation failed for ${provider}: ${stateError.message}`);
|
|
71140
|
+
throw error;
|
|
71141
|
+
}
|
|
71142
|
+
const command = adapter.buildCommand(config);
|
|
71143
|
+
this.updateStatus({
|
|
71144
|
+
provider,
|
|
71145
|
+
state: "starting",
|
|
71146
|
+
pid: null,
|
|
71147
|
+
startedAt: nowIso(),
|
|
71148
|
+
stoppedAt: null,
|
|
71149
|
+
url: null,
|
|
71150
|
+
lastError: null
|
|
71151
|
+
});
|
|
71152
|
+
this.emitLog("info", "manager", `Starting ${provider} tunnel: ${command.redactedPreview}`);
|
|
71153
|
+
const child = this.spawnImpl(command.command, command.args, {
|
|
71154
|
+
cwd: command.cwd,
|
|
71155
|
+
env: command.env,
|
|
71156
|
+
detached: process.platform !== "win32",
|
|
71157
|
+
shell: false,
|
|
71158
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
71159
|
+
});
|
|
71160
|
+
this.processHandle = {
|
|
71161
|
+
provider,
|
|
71162
|
+
child,
|
|
71163
|
+
command
|
|
71164
|
+
};
|
|
71165
|
+
this.expectedStop = false;
|
|
71166
|
+
this.updateStatus({ pid: child.pid ?? null });
|
|
71167
|
+
const stdoutBuffer = new LineBuffer();
|
|
71168
|
+
const stderrBuffer = new LineBuffer();
|
|
71169
|
+
const attachStream = (stream, source, buffer) => {
|
|
71170
|
+
stream?.on("data", (chunk) => {
|
|
71171
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
71172
|
+
for (const line of buffer.push(text)) {
|
|
71173
|
+
this.handleOutputLine(source, line);
|
|
71174
|
+
}
|
|
71175
|
+
});
|
|
71176
|
+
};
|
|
71177
|
+
attachStream(child.stdout, "stdout", stdoutBuffer);
|
|
71178
|
+
attachStream(child.stderr, "stderr", stderrBuffer);
|
|
71179
|
+
child.once("error", (error) => {
|
|
71180
|
+
const maskedMessage = maskSensitive(normalizeError(error).message, this.processHandle);
|
|
71181
|
+
this.emitLog("error", "manager", `Spawn failure for ${provider}: ${maskedMessage}`);
|
|
71182
|
+
this.handleUnexpectedExit("start_failed", `Spawn failure: ${maskedMessage}`);
|
|
71183
|
+
});
|
|
71184
|
+
child.once("close", (code, signal) => {
|
|
71185
|
+
for (const line of stdoutBuffer.flush()) {
|
|
71186
|
+
this.handleOutputLine("stdout", line);
|
|
71187
|
+
}
|
|
71188
|
+
for (const line of stderrBuffer.flush()) {
|
|
71189
|
+
this.handleOutputLine("stderr", line);
|
|
71190
|
+
}
|
|
71191
|
+
const reason = signal ? `signal ${signal}` : `exit code ${code ?? 0}`;
|
|
71192
|
+
if (this.expectedStop) {
|
|
71193
|
+
this.emitLog("info", "manager", `Tunnel process stopped (${reason})`);
|
|
71194
|
+
this.finalizeStoppedState();
|
|
71195
|
+
return;
|
|
71196
|
+
}
|
|
71197
|
+
this.emitLog("error", "manager", `Tunnel process exited unexpectedly (${reason})`);
|
|
71198
|
+
this.handleUnexpectedExit("process_exit", `Process exited unexpectedly (${reason})`);
|
|
71199
|
+
});
|
|
71200
|
+
this.readinessTimer = setTimeout(() => {
|
|
71201
|
+
if (this.status.state === "starting" && this.processHandle?.provider === provider) {
|
|
71202
|
+
this.emitLog("error", "manager", `Readiness timed out after ${command.readinessTimeoutMs}ms`);
|
|
71203
|
+
this.handleUnexpectedExit("readiness_timeout", `Tunnel readiness timeout after ${command.readinessTimeoutMs}ms`);
|
|
71204
|
+
}
|
|
71205
|
+
}, command.readinessTimeoutMs);
|
|
71206
|
+
this.readinessTimer.unref?.();
|
|
71207
|
+
}
|
|
71208
|
+
async stopInternal() {
|
|
71209
|
+
if (!this.processHandle) {
|
|
71210
|
+
this.updateStatus({
|
|
71211
|
+
provider: null,
|
|
71212
|
+
state: "stopped",
|
|
71213
|
+
pid: null,
|
|
71214
|
+
stoppedAt: nowIso(),
|
|
71215
|
+
url: null
|
|
71216
|
+
});
|
|
71217
|
+
return;
|
|
71218
|
+
}
|
|
71219
|
+
if (this.activeStopPromise) {
|
|
71220
|
+
await this.activeStopPromise;
|
|
71221
|
+
return;
|
|
71222
|
+
}
|
|
71223
|
+
const currentHandle = this.processHandle;
|
|
71224
|
+
const stopTimeoutMs = currentHandle.command.stopTimeoutMs || this.defaultStopTimeoutMs;
|
|
71225
|
+
this.expectedStop = true;
|
|
71226
|
+
this.updateStatus({
|
|
71227
|
+
state: "stopping",
|
|
71228
|
+
provider: currentHandle.provider,
|
|
71229
|
+
pid: currentHandle.child.pid ?? null,
|
|
71230
|
+
lastError: null
|
|
71231
|
+
});
|
|
71232
|
+
this.emitLog("info", "manager", `Stopping ${currentHandle.provider} tunnel (pid=${currentHandle.child.pid ?? "n/a"})`);
|
|
71233
|
+
this.activeStopPromise = new Promise((resolve16) => {
|
|
71234
|
+
const onClose = () => {
|
|
71235
|
+
currentHandle.child.removeListener("close", onClose);
|
|
71236
|
+
resolve16();
|
|
71237
|
+
};
|
|
71238
|
+
currentHandle.child.once("close", onClose);
|
|
71239
|
+
killManagedProcess(currentHandle.child, "SIGTERM");
|
|
71240
|
+
this.stopTimer = setTimeout(() => {
|
|
71241
|
+
if (this.processHandle === currentHandle) {
|
|
71242
|
+
this.emitLog("warn", "manager", `Graceful stop timed out after ${stopTimeoutMs}ms, sending SIGKILL`);
|
|
71243
|
+
killManagedProcess(currentHandle.child, "SIGKILL");
|
|
71244
|
+
}
|
|
71245
|
+
}, stopTimeoutMs);
|
|
71246
|
+
this.stopTimer.unref?.();
|
|
71247
|
+
}).finally(() => {
|
|
71248
|
+
this.activeStopPromise = null;
|
|
71249
|
+
if (this.stopTimer) {
|
|
71250
|
+
clearTimeout(this.stopTimer);
|
|
71251
|
+
this.stopTimer = null;
|
|
71252
|
+
}
|
|
71253
|
+
});
|
|
71254
|
+
await this.activeStopPromise;
|
|
71255
|
+
}
|
|
71256
|
+
handleOutputLine(source, rawLine) {
|
|
71257
|
+
const processHandle = this.processHandle;
|
|
71258
|
+
const maskedLine = maskSensitive(rawLine, processHandle);
|
|
71259
|
+
this.emitLog("info", source, maskedLine);
|
|
71260
|
+
if (!processHandle || this.status.state !== "starting") {
|
|
71261
|
+
return;
|
|
71262
|
+
}
|
|
71263
|
+
const adapter = getTunnelProviderAdapter(processHandle.provider);
|
|
71264
|
+
const readiness = adapter.parseReadiness(maskedLine, source);
|
|
71265
|
+
if (!readiness?.ready) {
|
|
71266
|
+
return;
|
|
71267
|
+
}
|
|
71268
|
+
this.clearReadinessTimer();
|
|
71269
|
+
this.updateStatus({
|
|
71270
|
+
state: "running",
|
|
71271
|
+
provider: processHandle.provider,
|
|
71272
|
+
pid: processHandle.child.pid ?? null,
|
|
71273
|
+
url: readiness.url ?? this.status.url,
|
|
71274
|
+
startedAt: this.status.startedAt ?? nowIso(),
|
|
71275
|
+
lastError: null
|
|
71276
|
+
});
|
|
71277
|
+
this.emitLog("info", "manager", `${processHandle.provider} tunnel is running`);
|
|
71278
|
+
}
|
|
71279
|
+
handleUnexpectedExit(code, message) {
|
|
71280
|
+
this.clearReadinessTimer();
|
|
71281
|
+
if (this.stopTimer) {
|
|
71282
|
+
clearTimeout(this.stopTimer);
|
|
71283
|
+
this.stopTimer = null;
|
|
71284
|
+
}
|
|
71285
|
+
this.expectedStop = false;
|
|
71286
|
+
const provider = this.processHandle?.provider ?? this.status.provider;
|
|
71287
|
+
this.processHandle = null;
|
|
71288
|
+
this.updateStatus({
|
|
71289
|
+
provider,
|
|
71290
|
+
state: "failed",
|
|
71291
|
+
pid: null,
|
|
71292
|
+
stoppedAt: nowIso(),
|
|
71293
|
+
url: null,
|
|
71294
|
+
lastError: {
|
|
71295
|
+
code,
|
|
71296
|
+
message,
|
|
71297
|
+
at: nowIso()
|
|
71298
|
+
}
|
|
71299
|
+
});
|
|
71300
|
+
}
|
|
71301
|
+
finalizeStoppedState() {
|
|
71302
|
+
this.clearReadinessTimer();
|
|
71303
|
+
if (this.stopTimer) {
|
|
71304
|
+
clearTimeout(this.stopTimer);
|
|
71305
|
+
this.stopTimer = null;
|
|
71306
|
+
}
|
|
71307
|
+
this.expectedStop = false;
|
|
71308
|
+
this.processHandle = null;
|
|
71309
|
+
this.updateStatus({
|
|
71310
|
+
provider: null,
|
|
71311
|
+
state: "stopped",
|
|
71312
|
+
pid: null,
|
|
71313
|
+
stoppedAt: nowIso(),
|
|
71314
|
+
url: null,
|
|
71315
|
+
lastError: null
|
|
71316
|
+
});
|
|
71317
|
+
}
|
|
71318
|
+
clearReadinessTimer() {
|
|
71319
|
+
if (this.readinessTimer) {
|
|
71320
|
+
clearTimeout(this.readinessTimer);
|
|
71321
|
+
this.readinessTimer = null;
|
|
71322
|
+
}
|
|
71323
|
+
}
|
|
71324
|
+
updateStatus(patch) {
|
|
71325
|
+
this.status = {
|
|
71326
|
+
...this.status,
|
|
71327
|
+
...patch,
|
|
71328
|
+
lastError: patch.lastError === void 0 ? this.status.lastError : patch.lastError
|
|
71329
|
+
};
|
|
71330
|
+
const snapshot = this.getStatus();
|
|
71331
|
+
for (const listener of this.statusListeners) {
|
|
71332
|
+
listener(snapshot);
|
|
71333
|
+
}
|
|
71334
|
+
this.emit("status", snapshot);
|
|
71335
|
+
}
|
|
71336
|
+
emitLog(level, source, message) {
|
|
71337
|
+
const safeMessage = maskSensitive(message, this.processHandle);
|
|
71338
|
+
const entry = {
|
|
71339
|
+
timestamp: nowIso(),
|
|
71340
|
+
provider: this.status.provider,
|
|
71341
|
+
level,
|
|
71342
|
+
source,
|
|
71343
|
+
message: safeMessage
|
|
71344
|
+
};
|
|
71345
|
+
this.logs.push(entry);
|
|
71346
|
+
if (this.logs.length > this.maxLogEntries) {
|
|
71347
|
+
this.logs.splice(0, this.logs.length - this.maxLogEntries);
|
|
71348
|
+
}
|
|
71349
|
+
const logMethod = level === "error" ? "error" : level === "warn" ? "warn" : "log";
|
|
71350
|
+
remoteTunnelLog[logMethod](safeMessage);
|
|
71351
|
+
for (const listener of this.logListeners) {
|
|
71352
|
+
listener(entry);
|
|
71353
|
+
}
|
|
71354
|
+
this.emit("log", entry);
|
|
71355
|
+
}
|
|
71356
|
+
};
|
|
71357
|
+
}
|
|
71358
|
+
});
|
|
71359
|
+
|
|
70583
71360
|
// ../engine/src/project-engine.ts
|
|
70584
|
-
|
|
71361
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
71362
|
+
import { promisify as promisify10 } from "node:util";
|
|
71363
|
+
var execFileAsync, isRemoteActive, ProjectEngine;
|
|
70585
71364
|
var init_project_engine = __esm({
|
|
70586
71365
|
"../engine/src/project-engine.ts"() {
|
|
70587
71366
|
"use strict";
|
|
@@ -70593,6 +71372,9 @@ var init_project_engine = __esm({
|
|
|
70593
71372
|
init_merger();
|
|
70594
71373
|
init_concurrency();
|
|
70595
71374
|
init_logger2();
|
|
71375
|
+
init_tunnel_process_manager();
|
|
71376
|
+
execFileAsync = promisify10(execFile3);
|
|
71377
|
+
isRemoteActive = (ra) => ra?.activeProvider != null && (ra.providers[ra.activeProvider]?.enabled ?? false);
|
|
70596
71378
|
ProjectEngine = class _ProjectEngine {
|
|
70597
71379
|
constructor(config, centralCore, options = {}) {
|
|
70598
71380
|
this.config = config;
|
|
@@ -70606,6 +71388,13 @@ var init_project_engine = __esm({
|
|
|
70606
71388
|
notifier;
|
|
70607
71389
|
cronRunner;
|
|
70608
71390
|
automationStore;
|
|
71391
|
+
remoteTunnelManager;
|
|
71392
|
+
remoteTunnelRestoreDiagnostics = {
|
|
71393
|
+
outcome: "skipped",
|
|
71394
|
+
reason: "not_attempted",
|
|
71395
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
71396
|
+
provider: null
|
|
71397
|
+
};
|
|
70609
71398
|
// ── Auto-merge state ──
|
|
70610
71399
|
mergeQueue = [];
|
|
70611
71400
|
mergeActive = /* @__PURE__ */ new Set();
|
|
@@ -70633,6 +71422,14 @@ var init_project_engine = __esm({
|
|
|
70633
71422
|
await this.runtime.start();
|
|
70634
71423
|
const store = this.runtime.getTaskStore();
|
|
70635
71424
|
const cwd = this.config.workingDirectory;
|
|
71425
|
+
this.remoteTunnelManager = new TunnelProcessManager();
|
|
71426
|
+
try {
|
|
71427
|
+
await this.restoreRemoteTunnelIfNeeded(store);
|
|
71428
|
+
} catch (error) {
|
|
71429
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71430
|
+
this.setRestoreDiagnostics("failed", "restore_start_failed", null, message);
|
|
71431
|
+
runtimeLog.warn(`Remote tunnel restore evaluation failed (continuing startup): ${message}`);
|
|
71432
|
+
}
|
|
70636
71433
|
this.prMonitor = new PrMonitor();
|
|
70637
71434
|
this.prCommentHandler = new PrCommentHandler(store);
|
|
70638
71435
|
this.prMonitor.onNewComments(
|
|
@@ -70732,6 +71529,30 @@ var init_project_engine = __esm({
|
|
|
70732
71529
|
}
|
|
70733
71530
|
this.notifier?.stop();
|
|
70734
71531
|
this.cronRunner?.stop();
|
|
71532
|
+
const tunnelManager = this.remoteTunnelManager;
|
|
71533
|
+
this.remoteTunnelManager = void 0;
|
|
71534
|
+
if (tunnelManager) {
|
|
71535
|
+
let shutdownStore = null;
|
|
71536
|
+
try {
|
|
71537
|
+
shutdownStore = this.runtime.getTaskStore();
|
|
71538
|
+
} catch {
|
|
71539
|
+
shutdownStore = null;
|
|
71540
|
+
}
|
|
71541
|
+
if (shutdownStore) {
|
|
71542
|
+
try {
|
|
71543
|
+
await this.persistShutdownRemoteLifecycle(shutdownStore, tunnelManager.getStatus());
|
|
71544
|
+
} catch (error) {
|
|
71545
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71546
|
+
runtimeLog.warn(`Failed to persist remote lifecycle shutdown markers: ${message}`);
|
|
71547
|
+
}
|
|
71548
|
+
}
|
|
71549
|
+
try {
|
|
71550
|
+
await tunnelManager.stop();
|
|
71551
|
+
} catch (error) {
|
|
71552
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71553
|
+
runtimeLog.warn(`Tunnel process manager stop failed (continuing shutdown): ${message}`);
|
|
71554
|
+
}
|
|
71555
|
+
}
|
|
70735
71556
|
await this.runtime.stop();
|
|
70736
71557
|
runtimeLog.log(`ProjectEngine stopped for ${this.config.projectId}`);
|
|
70737
71558
|
}
|
|
@@ -70776,6 +71597,71 @@ var init_project_engine = __esm({
|
|
|
70776
71597
|
getRoutineStore() {
|
|
70777
71598
|
return this.runtime.getRoutineStore();
|
|
70778
71599
|
}
|
|
71600
|
+
/** Get the remote tunnel manager (available after start()). */
|
|
71601
|
+
getRemoteTunnelManager() {
|
|
71602
|
+
return this.remoteTunnelManager;
|
|
71603
|
+
}
|
|
71604
|
+
getRemoteTunnelRestoreDiagnostics() {
|
|
71605
|
+
return { ...this.remoteTunnelRestoreDiagnostics };
|
|
71606
|
+
}
|
|
71607
|
+
async startRemoteTunnel() {
|
|
71608
|
+
const manager = this.remoteTunnelManager;
|
|
71609
|
+
if (!manager) {
|
|
71610
|
+
throw new Error("remote_tunnel_unavailable:remote tunnel manager is not initialized");
|
|
71611
|
+
}
|
|
71612
|
+
const store = this.runtime.getTaskStore();
|
|
71613
|
+
const settings = await store.getSettings();
|
|
71614
|
+
const remoteAccess = settings.remoteAccess;
|
|
71615
|
+
if (!remoteAccess || !isRemoteActive(remoteAccess)) {
|
|
71616
|
+
throw new Error("invalid_config:no remote access provider enabled");
|
|
71617
|
+
}
|
|
71618
|
+
const provider = remoteAccess.activeProvider;
|
|
71619
|
+
if (!provider) {
|
|
71620
|
+
throw new Error("invalid_config:no active remote provider configured");
|
|
71621
|
+
}
|
|
71622
|
+
const lifecycle = await this.evaluateRemoteLifecycle(settings, provider);
|
|
71623
|
+
if (!lifecycle.config) {
|
|
71624
|
+
throw new Error(`${lifecycle.reason ?? "invalid_config"}:${lifecycle.message ?? "remote provider prerequisites are not met"}`);
|
|
71625
|
+
}
|
|
71626
|
+
const current = manager.getStatus();
|
|
71627
|
+
if (current.state === "running" && current.provider === provider) {
|
|
71628
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71629
|
+
...remoteAccess.lifecycle,
|
|
71630
|
+
wasRunningOnShutdown: true,
|
|
71631
|
+
lastRunningProvider: provider
|
|
71632
|
+
});
|
|
71633
|
+
return manager.getStatus();
|
|
71634
|
+
}
|
|
71635
|
+
if (current.state === "running" && current.provider && current.provider !== provider) {
|
|
71636
|
+
await manager.switchProvider(provider, lifecycle.config);
|
|
71637
|
+
} else {
|
|
71638
|
+
await manager.start(provider, lifecycle.config);
|
|
71639
|
+
}
|
|
71640
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71641
|
+
...remoteAccess.lifecycle,
|
|
71642
|
+
wasRunningOnShutdown: true,
|
|
71643
|
+
lastRunningProvider: provider
|
|
71644
|
+
});
|
|
71645
|
+
return manager.getStatus();
|
|
71646
|
+
}
|
|
71647
|
+
async stopRemoteTunnel() {
|
|
71648
|
+
const manager = this.remoteTunnelManager;
|
|
71649
|
+
if (!manager) {
|
|
71650
|
+
throw new Error("remote_tunnel_unavailable:remote tunnel manager is not initialized");
|
|
71651
|
+
}
|
|
71652
|
+
await manager.stop();
|
|
71653
|
+
const store = this.runtime.getTaskStore();
|
|
71654
|
+
const settings = await store.getSettings();
|
|
71655
|
+
const remoteAccess = settings.remoteAccess;
|
|
71656
|
+
if (remoteAccess) {
|
|
71657
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71658
|
+
...remoteAccess.lifecycle,
|
|
71659
|
+
wasRunningOnShutdown: false,
|
|
71660
|
+
lastRunningProvider: null
|
|
71661
|
+
});
|
|
71662
|
+
}
|
|
71663
|
+
return manager.getStatus();
|
|
71664
|
+
}
|
|
70779
71665
|
/** Get the RoutineRunner (if initialized). */
|
|
70780
71666
|
getRoutineRunner() {
|
|
70781
71667
|
return this.runtime.getRoutineRunner();
|
|
@@ -70810,6 +71696,183 @@ var init_project_engine = __esm({
|
|
|
70810
71696
|
this.internalEnqueueMerge(taskId);
|
|
70811
71697
|
});
|
|
70812
71698
|
}
|
|
71699
|
+
setRestoreDiagnostics(outcome, reason, provider, message) {
|
|
71700
|
+
this.remoteTunnelRestoreDiagnostics = {
|
|
71701
|
+
outcome,
|
|
71702
|
+
reason,
|
|
71703
|
+
provider,
|
|
71704
|
+
message,
|
|
71705
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
71706
|
+
};
|
|
71707
|
+
}
|
|
71708
|
+
async restoreRemoteTunnelIfNeeded(store) {
|
|
71709
|
+
const manager = this.remoteTunnelManager;
|
|
71710
|
+
if (!manager) {
|
|
71711
|
+
return;
|
|
71712
|
+
}
|
|
71713
|
+
const settings = await store.getSettings();
|
|
71714
|
+
const remoteAccess = settings.remoteAccess;
|
|
71715
|
+
if (!remoteAccess || !isRemoteActive(remoteAccess)) {
|
|
71716
|
+
this.setRestoreDiagnostics("skipped", "remote_access_disabled", null);
|
|
71717
|
+
return;
|
|
71718
|
+
}
|
|
71719
|
+
const lifecycle = remoteAccess.lifecycle;
|
|
71720
|
+
if (!lifecycle.rememberLastRunning) {
|
|
71721
|
+
this.setRestoreDiagnostics("skipped", "remember_last_running_disabled", null);
|
|
71722
|
+
if (lifecycle.wasRunningOnShutdown || lifecycle.lastRunningProvider) {
|
|
71723
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71724
|
+
...lifecycle,
|
|
71725
|
+
wasRunningOnShutdown: false,
|
|
71726
|
+
lastRunningProvider: null
|
|
71727
|
+
});
|
|
71728
|
+
}
|
|
71729
|
+
return;
|
|
71730
|
+
}
|
|
71731
|
+
if (!lifecycle.wasRunningOnShutdown) {
|
|
71732
|
+
this.setRestoreDiagnostics("skipped", "no_prior_running_marker", null);
|
|
71733
|
+
return;
|
|
71734
|
+
}
|
|
71735
|
+
const provider = lifecycle.lastRunningProvider ?? remoteAccess.activeProvider;
|
|
71736
|
+
if (!provider) {
|
|
71737
|
+
this.setRestoreDiagnostics("skipped", "provider_missing", null);
|
|
71738
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71739
|
+
...lifecycle,
|
|
71740
|
+
wasRunningOnShutdown: false,
|
|
71741
|
+
lastRunningProvider: null
|
|
71742
|
+
});
|
|
71743
|
+
return;
|
|
71744
|
+
}
|
|
71745
|
+
const evaluation = await this.evaluateRemoteLifecycle(settings, provider);
|
|
71746
|
+
if (!evaluation.config) {
|
|
71747
|
+
this.setRestoreDiagnostics("skipped", evaluation.reason ?? "provider_not_configured", provider, evaluation.message);
|
|
71748
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71749
|
+
...lifecycle,
|
|
71750
|
+
wasRunningOnShutdown: false,
|
|
71751
|
+
lastRunningProvider: null
|
|
71752
|
+
});
|
|
71753
|
+
return;
|
|
71754
|
+
}
|
|
71755
|
+
try {
|
|
71756
|
+
await manager.start(provider, evaluation.config);
|
|
71757
|
+
this.setRestoreDiagnostics("applied", "restore_started", provider);
|
|
71758
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71759
|
+
...lifecycle,
|
|
71760
|
+
wasRunningOnShutdown: true,
|
|
71761
|
+
lastRunningProvider: provider
|
|
71762
|
+
}, provider);
|
|
71763
|
+
} catch (error) {
|
|
71764
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71765
|
+
this.setRestoreDiagnostics("failed", "restore_start_failed", provider, message);
|
|
71766
|
+
runtimeLog.warn(`Remote tunnel restore failed for ${provider}: ${message}`);
|
|
71767
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71768
|
+
...lifecycle,
|
|
71769
|
+
wasRunningOnShutdown: false,
|
|
71770
|
+
lastRunningProvider: null
|
|
71771
|
+
});
|
|
71772
|
+
}
|
|
71773
|
+
}
|
|
71774
|
+
async persistShutdownRemoteLifecycle(store, status) {
|
|
71775
|
+
const settings = await store.getSettings();
|
|
71776
|
+
const remoteAccess = settings.remoteAccess;
|
|
71777
|
+
if (!remoteAccess) {
|
|
71778
|
+
return;
|
|
71779
|
+
}
|
|
71780
|
+
const shouldRememberRunning = (status.state === "running" || status.state === "starting" || status.state === "stopping") && status.provider !== null;
|
|
71781
|
+
await this.writeRemoteLifecycleState(store, remoteAccess, {
|
|
71782
|
+
...remoteAccess.lifecycle,
|
|
71783
|
+
wasRunningOnShutdown: shouldRememberRunning,
|
|
71784
|
+
lastRunningProvider: shouldRememberRunning ? status.provider : null
|
|
71785
|
+
}, shouldRememberRunning ? status.provider : remoteAccess.activeProvider);
|
|
71786
|
+
}
|
|
71787
|
+
async writeRemoteLifecycleState(store, remoteAccess, lifecycle, activeProviderOverride) {
|
|
71788
|
+
await store.updateSettings({
|
|
71789
|
+
remoteAccess: {
|
|
71790
|
+
...remoteAccess,
|
|
71791
|
+
activeProvider: activeProviderOverride === void 0 ? remoteAccess.activeProvider : activeProviderOverride,
|
|
71792
|
+
lifecycle
|
|
71793
|
+
}
|
|
71794
|
+
});
|
|
71795
|
+
}
|
|
71796
|
+
async evaluateRemoteLifecycle(settings, provider) {
|
|
71797
|
+
const remoteAccess = settings.remoteAccess;
|
|
71798
|
+
if (!remoteAccess || !isRemoteActive(remoteAccess)) {
|
|
71799
|
+
return { provider, reason: "remote_access_disabled", message: "No remote provider is enabled" };
|
|
71800
|
+
}
|
|
71801
|
+
if (provider === "tailscale") {
|
|
71802
|
+
const tailscale = remoteAccess.providers.tailscale;
|
|
71803
|
+
if (!tailscale.enabled) {
|
|
71804
|
+
return { provider, reason: "provider_not_enabled", message: "Tailscale provider is disabled" };
|
|
71805
|
+
}
|
|
71806
|
+
if (!tailscale.hostname?.trim() || !Number.isFinite(tailscale.targetPort) || tailscale.targetPort <= 0) {
|
|
71807
|
+
return { provider, reason: "provider_not_configured", message: "Tailscale hostname and target port must be configured" };
|
|
71808
|
+
}
|
|
71809
|
+
const executable2 = await this.checkExecutableAvailable("tailscale");
|
|
71810
|
+
if (!executable2.available) {
|
|
71811
|
+
return { provider, reason: "runtime_prerequisite_missing", message: executable2.message };
|
|
71812
|
+
}
|
|
71813
|
+
return {
|
|
71814
|
+
provider,
|
|
71815
|
+
config: {
|
|
71816
|
+
provider: "tailscale",
|
|
71817
|
+
executablePath: "tailscale",
|
|
71818
|
+
args: ["funnel", String(Math.floor(tailscale.targetPort))]
|
|
71819
|
+
}
|
|
71820
|
+
};
|
|
71821
|
+
}
|
|
71822
|
+
const cloudflare = remoteAccess.providers.cloudflare;
|
|
71823
|
+
if (!cloudflare.enabled) {
|
|
71824
|
+
return { provider, reason: "provider_not_enabled", message: "Cloudflare provider is disabled" };
|
|
71825
|
+
}
|
|
71826
|
+
if (cloudflare.quickTunnel === true) {
|
|
71827
|
+
const executable2 = await this.checkExecutableAvailable("cloudflared");
|
|
71828
|
+
if (!executable2.available) {
|
|
71829
|
+
return { provider, reason: "runtime_prerequisite_missing", message: executable2.message };
|
|
71830
|
+
}
|
|
71831
|
+
return {
|
|
71832
|
+
provider,
|
|
71833
|
+
config: {
|
|
71834
|
+
provider: "cloudflare",
|
|
71835
|
+
quickTunnel: true,
|
|
71836
|
+
executablePath: "cloudflared",
|
|
71837
|
+
args: ["tunnel", "--url", "http://localhost:4040"]
|
|
71838
|
+
}
|
|
71839
|
+
};
|
|
71840
|
+
}
|
|
71841
|
+
if (!cloudflare.tunnelName?.trim() || !cloudflare.ingressUrl?.trim()) {
|
|
71842
|
+
return { provider, reason: "provider_not_configured", message: "Cloudflare tunnel name and ingress URL must be configured" };
|
|
71843
|
+
}
|
|
71844
|
+
if (!cloudflare.tunnelToken?.trim()) {
|
|
71845
|
+
return { provider, reason: "provider_not_configured", message: "Cloudflare tunnel token is required" };
|
|
71846
|
+
}
|
|
71847
|
+
const executable = await this.checkExecutableAvailable("cloudflared");
|
|
71848
|
+
if (!executable.available) {
|
|
71849
|
+
return { provider, reason: "runtime_prerequisite_missing", message: executable.message };
|
|
71850
|
+
}
|
|
71851
|
+
return {
|
|
71852
|
+
provider,
|
|
71853
|
+
config: {
|
|
71854
|
+
provider: "cloudflare",
|
|
71855
|
+
executablePath: "cloudflared",
|
|
71856
|
+
args: ["tunnel", "--no-autoupdate", "run", cloudflare.tunnelName.trim()],
|
|
71857
|
+
tokenEnvVar: "TUNNEL_TOKEN",
|
|
71858
|
+
env: {
|
|
71859
|
+
TUNNEL_TOKEN: cloudflare.tunnelToken
|
|
71860
|
+
}
|
|
71861
|
+
}
|
|
71862
|
+
};
|
|
71863
|
+
}
|
|
71864
|
+
async checkExecutableAvailable(command) {
|
|
71865
|
+
const checker = process.platform === "win32" ? "where" : "which";
|
|
71866
|
+
try {
|
|
71867
|
+
await execFileAsync(checker, [command]);
|
|
71868
|
+
return { available: true };
|
|
71869
|
+
} catch {
|
|
71870
|
+
return {
|
|
71871
|
+
available: false,
|
|
71872
|
+
message: `${command} is not available on PATH`
|
|
71873
|
+
};
|
|
71874
|
+
}
|
|
71875
|
+
}
|
|
70813
71876
|
// ── Merge eligibility helpers (richer logic from dashboard.ts) ──
|
|
70814
71877
|
/**
|
|
70815
71878
|
* True when a retry-exhausted task in "in-review" has a verification buffer
|
|
@@ -71422,6 +72485,15 @@ var init_peer_exchange_service = __esm({
|
|
|
71422
72485
|
}
|
|
71423
72486
|
});
|
|
71424
72487
|
|
|
72488
|
+
// ../engine/src/remote-access/index.ts
|
|
72489
|
+
var init_remote_access = __esm({
|
|
72490
|
+
"../engine/src/remote-access/index.ts"() {
|
|
72491
|
+
"use strict";
|
|
72492
|
+
init_provider_adapters();
|
|
72493
|
+
init_tunnel_process_manager();
|
|
72494
|
+
}
|
|
72495
|
+
});
|
|
72496
|
+
|
|
71425
72497
|
// ../engine/src/index.ts
|
|
71426
72498
|
var init_src2 = __esm({
|
|
71427
72499
|
"../engine/src/index.ts"() {
|
|
@@ -71462,6 +72534,7 @@ var init_src2 = __esm({
|
|
|
71462
72534
|
init_project_engine_manager();
|
|
71463
72535
|
init_node_health_monitor();
|
|
71464
72536
|
init_peer_exchange_service();
|
|
72537
|
+
init_remote_access();
|
|
71465
72538
|
init_remote_node_client();
|
|
71466
72539
|
init_remote_node_runtime();
|
|
71467
72540
|
init_step_session_executor();
|
|
@@ -71615,7 +72688,7 @@ var init_ai_session_diagnostics = __esm({
|
|
|
71615
72688
|
|
|
71616
72689
|
// ../dashboard/src/planning.ts
|
|
71617
72690
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
71618
|
-
import { EventEmitter as
|
|
72691
|
+
import { EventEmitter as EventEmitter19 } from "node:events";
|
|
71619
72692
|
async function initEngine2() {
|
|
71620
72693
|
try {
|
|
71621
72694
|
const engineModule = "@fusion/engine";
|
|
@@ -72449,7 +73522,7 @@ For completion:
|
|
|
72449
73522
|
process.on("beforeExit", () => {
|
|
72450
73523
|
clearInterval(cleanupInterval2);
|
|
72451
73524
|
});
|
|
72452
|
-
PlanningStreamManager = class extends
|
|
73525
|
+
PlanningStreamManager = class extends EventEmitter19 {
|
|
72453
73526
|
constructor(bufferSize = 100) {
|
|
72454
73527
|
super();
|
|
72455
73528
|
this.bufferSize = bufferSize;
|
|
@@ -72561,10 +73634,756 @@ For completion:
|
|
|
72561
73634
|
}
|
|
72562
73635
|
});
|
|
72563
73636
|
|
|
72564
|
-
// ../dashboard/src/
|
|
72565
|
-
|
|
72566
|
-
|
|
73637
|
+
// ../dashboard/src/terminal.ts
|
|
73638
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
73639
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
73640
|
+
import { EventEmitter as EventEmitter20 } from "node:events";
|
|
73641
|
+
function extractBaseCommand(command) {
|
|
73642
|
+
let trimmed = command.trim();
|
|
73643
|
+
while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(trimmed)) {
|
|
73644
|
+
const match2 = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*=(?:[^\s]*|"[^"]*"|'[^']*'))\s*/);
|
|
73645
|
+
if (match2) {
|
|
73646
|
+
trimmed = trimmed.slice(match2[0].length).trim();
|
|
73647
|
+
} else {
|
|
73648
|
+
break;
|
|
73649
|
+
}
|
|
73650
|
+
}
|
|
73651
|
+
if (trimmed.startsWith("sudo ")) {
|
|
73652
|
+
trimmed = trimmed.slice(5).trim();
|
|
73653
|
+
}
|
|
73654
|
+
const match = trimmed.match(/^([A-Za-z0-9._-]+)/);
|
|
73655
|
+
return match ? match[1].toLowerCase() : null;
|
|
73656
|
+
}
|
|
73657
|
+
function validateCommand(command) {
|
|
73658
|
+
for (const pattern of SUBSTITUTION_PATTERNS) {
|
|
73659
|
+
if (pattern.test(command)) {
|
|
73660
|
+
return { valid: false, error: "Command substitution is not allowed" };
|
|
73661
|
+
}
|
|
73662
|
+
}
|
|
73663
|
+
if (/[\0\r\n]/.test(command)) {
|
|
73664
|
+
return { valid: false, error: "Command contains invalid control characters" };
|
|
73665
|
+
}
|
|
73666
|
+
for (const pattern of BLOCKED_PATTERNS) {
|
|
73667
|
+
if (pattern.test(command)) {
|
|
73668
|
+
return { valid: false, error: "Command contains dangerous patterns and is not allowed" };
|
|
73669
|
+
}
|
|
73670
|
+
}
|
|
73671
|
+
const segments = command.split(CHAIN_OPERATORS);
|
|
73672
|
+
for (const raw of segments) {
|
|
73673
|
+
const segment = raw.trim();
|
|
73674
|
+
if (segment.length === 0) continue;
|
|
73675
|
+
const baseCommand = extractBaseCommand(segment);
|
|
73676
|
+
if (!baseCommand) {
|
|
73677
|
+
return { valid: false, error: "Could not parse command" };
|
|
73678
|
+
}
|
|
73679
|
+
if (!ALLOWED_COMMANDS.has(baseCommand)) {
|
|
73680
|
+
return {
|
|
73681
|
+
valid: false,
|
|
73682
|
+
error: `Command '${baseCommand}' is not in the allowed command list. Allowed commands: ${Array.from(ALLOWED_COMMANDS).sort().join(", ")}`
|
|
73683
|
+
};
|
|
73684
|
+
}
|
|
73685
|
+
}
|
|
73686
|
+
return { valid: true };
|
|
73687
|
+
}
|
|
73688
|
+
var ALLOWED_COMMANDS, BLOCKED_PATTERNS, SUBSTITUTION_PATTERNS, CHAIN_OPERATORS, TerminalSessionManager, terminalSessionManager;
|
|
73689
|
+
var init_terminal = __esm({
|
|
73690
|
+
"../dashboard/src/terminal.ts"() {
|
|
73691
|
+
"use strict";
|
|
73692
|
+
ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
73693
|
+
// Version control
|
|
73694
|
+
"git",
|
|
73695
|
+
// Package managers
|
|
73696
|
+
"npm",
|
|
73697
|
+
"pnpm",
|
|
73698
|
+
"yarn",
|
|
73699
|
+
"bun",
|
|
73700
|
+
// File operations
|
|
73701
|
+
"ls",
|
|
73702
|
+
"cat",
|
|
73703
|
+
"echo",
|
|
73704
|
+
"pwd",
|
|
73705
|
+
"cd",
|
|
73706
|
+
"mkdir",
|
|
73707
|
+
"touch",
|
|
73708
|
+
"cp",
|
|
73709
|
+
"mv",
|
|
73710
|
+
"rm",
|
|
73711
|
+
"head",
|
|
73712
|
+
"tail",
|
|
73713
|
+
"less",
|
|
73714
|
+
"more",
|
|
73715
|
+
"find",
|
|
73716
|
+
"grep",
|
|
73717
|
+
// System info
|
|
73718
|
+
"clear",
|
|
73719
|
+
"which",
|
|
73720
|
+
"whoami",
|
|
73721
|
+
"uname",
|
|
73722
|
+
"date",
|
|
73723
|
+
// Node/JS
|
|
73724
|
+
"node",
|
|
73725
|
+
"npx",
|
|
73726
|
+
"tsx",
|
|
73727
|
+
// Python
|
|
73728
|
+
"python",
|
|
73729
|
+
"python3",
|
|
73730
|
+
"pip",
|
|
73731
|
+
"pip3",
|
|
73732
|
+
// Network
|
|
73733
|
+
"curl",
|
|
73734
|
+
"wget",
|
|
73735
|
+
// Build tools
|
|
73736
|
+
"make",
|
|
73737
|
+
"cmake",
|
|
73738
|
+
// Process management
|
|
73739
|
+
"ps",
|
|
73740
|
+
"top",
|
|
73741
|
+
"htop",
|
|
73742
|
+
"kill",
|
|
73743
|
+
"pkill",
|
|
73744
|
+
// Shell builtins that are safe
|
|
73745
|
+
"source",
|
|
73746
|
+
".",
|
|
73747
|
+
"export",
|
|
73748
|
+
"env",
|
|
73749
|
+
"printenv",
|
|
73750
|
+
"alias",
|
|
73751
|
+
// Editors (for viewing)
|
|
73752
|
+
"code",
|
|
73753
|
+
// VS Code CLI
|
|
73754
|
+
"vim",
|
|
73755
|
+
"vi",
|
|
73756
|
+
"nano"
|
|
73757
|
+
]);
|
|
73758
|
+
BLOCKED_PATTERNS = [
|
|
73759
|
+
// System destruction
|
|
73760
|
+
/rm\s+(-[rf]+\s+)?\/\s*$/i,
|
|
73761
|
+
// rm -rf / or rm /
|
|
73762
|
+
/rm\s+(-[rf]+\s+)?\/\*/i,
|
|
73763
|
+
// rm -rf /*
|
|
73764
|
+
/>\s*\/dev\/sda/i,
|
|
73765
|
+
// Direct disk write
|
|
73766
|
+
/:\(\)\s*\{\s*:\s*\|:\s*&\s*\};\s*:/i,
|
|
73767
|
+
// Fork bomb
|
|
73768
|
+
/mkfs\.\w+\s+/i,
|
|
73769
|
+
// Filesystem formatting
|
|
73770
|
+
/dd\s+if=/i,
|
|
73771
|
+
// dd with input file
|
|
73772
|
+
/\.\s*\/dev\/null/i
|
|
73773
|
+
// Sourcing /dev/null tricks
|
|
73774
|
+
];
|
|
73775
|
+
SUBSTITUTION_PATTERNS = [
|
|
73776
|
+
/\$\(/,
|
|
73777
|
+
// $(...) — command substitution
|
|
73778
|
+
/`/,
|
|
73779
|
+
// `...` — backtick command substitution
|
|
73780
|
+
/<\(/,
|
|
73781
|
+
// <(...) — process substitution (bash)
|
|
73782
|
+
/>\(/
|
|
73783
|
+
// >(...) — process substitution (bash)
|
|
73784
|
+
];
|
|
73785
|
+
CHAIN_OPERATORS = /&&|\|\||;|(?<!\|)\|(?!\|)/;
|
|
73786
|
+
TerminalSessionManager = class extends EventEmitter20 {
|
|
73787
|
+
sessions = /* @__PURE__ */ new Map();
|
|
73788
|
+
defaultTimeout = 3e4;
|
|
73789
|
+
// 30 seconds
|
|
73790
|
+
/**
|
|
73791
|
+
* Creates a new terminal session and spawns the command.
|
|
73792
|
+
* Returns the session ID for tracking.
|
|
73793
|
+
*/
|
|
73794
|
+
createSession(command, cwd) {
|
|
73795
|
+
const validation = validateCommand(command);
|
|
73796
|
+
if (!validation.valid) {
|
|
73797
|
+
return { sessionId: "", error: validation.error };
|
|
73798
|
+
}
|
|
73799
|
+
const sessionId = randomUUID11();
|
|
73800
|
+
const childProcess = spawn3(command, [], {
|
|
73801
|
+
cwd,
|
|
73802
|
+
shell: true,
|
|
73803
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
73804
|
+
env: { ...process.env, FORCE_COLOR: "1", TERM: "xterm-256color" }
|
|
73805
|
+
});
|
|
73806
|
+
const session = {
|
|
73807
|
+
id: sessionId,
|
|
73808
|
+
command,
|
|
73809
|
+
process: childProcess,
|
|
73810
|
+
startTime: /* @__PURE__ */ new Date(),
|
|
73811
|
+
output: [],
|
|
73812
|
+
exitCode: null,
|
|
73813
|
+
killed: false
|
|
73814
|
+
};
|
|
73815
|
+
this.sessions.set(sessionId, session);
|
|
73816
|
+
childProcess.stdout?.on("data", (data) => {
|
|
73817
|
+
const chunk = data.toString("utf-8");
|
|
73818
|
+
session.output.push(chunk);
|
|
73819
|
+
this.emit("output", { sessionId, type: "stdout", data: chunk });
|
|
73820
|
+
});
|
|
73821
|
+
childProcess.stderr?.on("data", (data) => {
|
|
73822
|
+
const chunk = data.toString("utf-8");
|
|
73823
|
+
session.output.push(chunk);
|
|
73824
|
+
this.emit("output", { sessionId, type: "stderr", data: chunk });
|
|
73825
|
+
});
|
|
73826
|
+
childProcess.on("exit", (code) => {
|
|
73827
|
+
session.exitCode = code ?? 0;
|
|
73828
|
+
this.emit("output", {
|
|
73829
|
+
sessionId,
|
|
73830
|
+
type: "exit",
|
|
73831
|
+
data: `Process exited with code ${code ?? 0}`,
|
|
73832
|
+
exitCode: code ?? 0
|
|
73833
|
+
});
|
|
73834
|
+
setTimeout(() => this.cleanupSession(sessionId), 5e3);
|
|
73835
|
+
});
|
|
73836
|
+
childProcess.on("error", (err) => {
|
|
73837
|
+
const errorMsg = err.message;
|
|
73838
|
+
session.output.push(errorMsg);
|
|
73839
|
+
session.exitCode = 127;
|
|
73840
|
+
this.emit("output", { sessionId, type: "stderr", data: errorMsg });
|
|
73841
|
+
this.emit("output", {
|
|
73842
|
+
sessionId,
|
|
73843
|
+
type: "exit",
|
|
73844
|
+
data: `Process failed: ${errorMsg}`,
|
|
73845
|
+
exitCode: 127
|
|
73846
|
+
});
|
|
73847
|
+
setTimeout(() => this.cleanupSession(sessionId), 5e3);
|
|
73848
|
+
});
|
|
73849
|
+
const timeout = setTimeout(() => {
|
|
73850
|
+
if (!session.exitCode && !session.killed) {
|
|
73851
|
+
this.killSession(sessionId, "SIGTERM");
|
|
73852
|
+
this.emit("output", {
|
|
73853
|
+
sessionId,
|
|
73854
|
+
type: "stderr",
|
|
73855
|
+
data: "\n[Command timed out after 30 seconds]\n"
|
|
73856
|
+
});
|
|
73857
|
+
}
|
|
73858
|
+
}, this.defaultTimeout);
|
|
73859
|
+
childProcess.on("exit", () => clearTimeout(timeout));
|
|
73860
|
+
return { sessionId };
|
|
73861
|
+
}
|
|
73862
|
+
/**
|
|
73863
|
+
* Gets a session by ID.
|
|
73864
|
+
*/
|
|
73865
|
+
getSession(sessionId) {
|
|
73866
|
+
return this.sessions.get(sessionId);
|
|
73867
|
+
}
|
|
73868
|
+
/**
|
|
73869
|
+
* Kills a running session's process.
|
|
73870
|
+
* Returns true if killed, false if not found or already exited.
|
|
73871
|
+
*/
|
|
73872
|
+
killSession(sessionId, signal = "SIGTERM") {
|
|
73873
|
+
const session = this.sessions.get(sessionId);
|
|
73874
|
+
if (!session || session.exitCode !== null || session.killed) {
|
|
73875
|
+
return false;
|
|
73876
|
+
}
|
|
73877
|
+
session.killed = true;
|
|
73878
|
+
try {
|
|
73879
|
+
if (session.process.pid) {
|
|
73880
|
+
process.kill(-session.process.pid, signal);
|
|
73881
|
+
}
|
|
73882
|
+
} catch {
|
|
73883
|
+
session.process.kill(signal);
|
|
73884
|
+
}
|
|
73885
|
+
return true;
|
|
73886
|
+
}
|
|
73887
|
+
/**
|
|
73888
|
+
* Cleans up a session from memory.
|
|
73889
|
+
*/
|
|
73890
|
+
cleanupSession(sessionId) {
|
|
73891
|
+
const session = this.sessions.get(sessionId);
|
|
73892
|
+
if (!session) return false;
|
|
73893
|
+
if (session.exitCode === null && !session.killed) {
|
|
73894
|
+
this.killSession(sessionId, "SIGKILL");
|
|
73895
|
+
}
|
|
73896
|
+
this.sessions.delete(sessionId);
|
|
73897
|
+
return true;
|
|
73898
|
+
}
|
|
73899
|
+
/**
|
|
73900
|
+
* Lists all active sessions.
|
|
73901
|
+
*/
|
|
73902
|
+
listSessions() {
|
|
73903
|
+
return Array.from(this.sessions.values()).map((s) => ({
|
|
73904
|
+
id: s.id,
|
|
73905
|
+
command: s.command,
|
|
73906
|
+
running: s.exitCode === null && !s.killed,
|
|
73907
|
+
startTime: s.startTime
|
|
73908
|
+
}));
|
|
73909
|
+
}
|
|
73910
|
+
/**
|
|
73911
|
+
* Cleans up all sessions (useful for shutdown).
|
|
73912
|
+
*/
|
|
73913
|
+
cleanupAll() {
|
|
73914
|
+
for (const [sessionId] of this.sessions) {
|
|
73915
|
+
this.cleanupSession(sessionId);
|
|
73916
|
+
}
|
|
73917
|
+
}
|
|
73918
|
+
};
|
|
73919
|
+
terminalSessionManager = new TerminalSessionManager();
|
|
73920
|
+
}
|
|
73921
|
+
});
|
|
73922
|
+
|
|
73923
|
+
// ../dashboard/src/terminal-service.ts
|
|
73924
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
73925
|
+
var isBunBinary, require2;
|
|
73926
|
+
var init_terminal_service = __esm({
|
|
73927
|
+
"../dashboard/src/terminal-service.ts"() {
|
|
72567
73928
|
"use strict";
|
|
73929
|
+
isBunBinary = typeof Bun !== "undefined" && !!Bun.embeddedFiles;
|
|
73930
|
+
require2 = createRequire2(import.meta.url);
|
|
73931
|
+
}
|
|
73932
|
+
});
|
|
73933
|
+
|
|
73934
|
+
// ../dashboard/src/github-webhooks.ts
|
|
73935
|
+
var init_github_webhooks = __esm({
|
|
73936
|
+
"../dashboard/src/github-webhooks.ts"() {
|
|
73937
|
+
"use strict";
|
|
73938
|
+
}
|
|
73939
|
+
});
|
|
73940
|
+
|
|
73941
|
+
// ../dashboard/src/ai-session-store.ts
|
|
73942
|
+
var MAX_THINKING_BYTES, SESSION_CLEANUP_DEFAULT_MAX_AGE_MS, SESSION_CLEANUP_INTERVAL_MS, diagnostics2;
|
|
73943
|
+
var init_ai_session_store = __esm({
|
|
73944
|
+
"../dashboard/src/ai-session-store.ts"() {
|
|
73945
|
+
"use strict";
|
|
73946
|
+
init_ai_session_diagnostics();
|
|
73947
|
+
MAX_THINKING_BYTES = 50 * 1024;
|
|
73948
|
+
SESSION_CLEANUP_DEFAULT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
73949
|
+
SESSION_CLEANUP_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
73950
|
+
diagnostics2 = createSessionDiagnostics("ai-session-store");
|
|
73951
|
+
}
|
|
73952
|
+
});
|
|
73953
|
+
|
|
73954
|
+
// ../dashboard/src/subtask-breakdown.ts
|
|
73955
|
+
import { EventEmitter as EventEmitter21 } from "node:events";
|
|
73956
|
+
function cleanupInMemorySubtaskSession(sessionId) {
|
|
73957
|
+
const session = sessions2.get(sessionId);
|
|
73958
|
+
if (!session) {
|
|
73959
|
+
return false;
|
|
73960
|
+
}
|
|
73961
|
+
try {
|
|
73962
|
+
session.agent?.session?.dispose?.();
|
|
73963
|
+
} catch {
|
|
73964
|
+
}
|
|
73965
|
+
subtaskStreamManager.cleanupSession(sessionId);
|
|
73966
|
+
sessions2.delete(sessionId);
|
|
73967
|
+
return true;
|
|
73968
|
+
}
|
|
73969
|
+
function cleanupExpiredSessions2() {
|
|
73970
|
+
const now = Date.now();
|
|
73971
|
+
for (const [id, session] of sessions2) {
|
|
73972
|
+
if (now - session.updatedAt.getTime() > SESSION_TTL_MS2) {
|
|
73973
|
+
cleanupInMemorySubtaskSession(id);
|
|
73974
|
+
}
|
|
73975
|
+
}
|
|
73976
|
+
}
|
|
73977
|
+
var diagnostics3, SESSION_TTL_MS2, CLEANUP_INTERVAL_MS3, sessions2, cleanupInterval3, SubtaskStreamManager, subtaskStreamManager;
|
|
73978
|
+
var init_subtask_breakdown = __esm({
|
|
73979
|
+
"../dashboard/src/subtask-breakdown.ts"() {
|
|
73980
|
+
"use strict";
|
|
73981
|
+
init_src();
|
|
73982
|
+
init_sse_buffer();
|
|
73983
|
+
init_ai_session_diagnostics();
|
|
73984
|
+
diagnostics3 = createSessionDiagnostics("subtask-breakdown");
|
|
73985
|
+
SESSION_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
|
|
73986
|
+
CLEANUP_INTERVAL_MS3 = 5 * 60 * 1e3;
|
|
73987
|
+
sessions2 = /* @__PURE__ */ new Map();
|
|
73988
|
+
cleanupInterval3 = setInterval(cleanupExpiredSessions2, CLEANUP_INTERVAL_MS3);
|
|
73989
|
+
cleanupInterval3.unref?.();
|
|
73990
|
+
process.on("beforeExit", () => {
|
|
73991
|
+
clearInterval(cleanupInterval3);
|
|
73992
|
+
});
|
|
73993
|
+
SubtaskStreamManager = class extends EventEmitter21 {
|
|
73994
|
+
constructor(bufferSize = 100) {
|
|
73995
|
+
super();
|
|
73996
|
+
this.bufferSize = bufferSize;
|
|
73997
|
+
}
|
|
73998
|
+
sessions = /* @__PURE__ */ new Map();
|
|
73999
|
+
buffers = /* @__PURE__ */ new Map();
|
|
74000
|
+
subscribe(sessionId, callback) {
|
|
74001
|
+
if (!this.sessions.has(sessionId)) {
|
|
74002
|
+
this.sessions.set(sessionId, /* @__PURE__ */ new Set());
|
|
74003
|
+
}
|
|
74004
|
+
const callbacks = this.sessions.get(sessionId);
|
|
74005
|
+
callbacks.add(callback);
|
|
74006
|
+
return () => {
|
|
74007
|
+
callbacks.delete(callback);
|
|
74008
|
+
if (callbacks.size === 0) {
|
|
74009
|
+
this.sessions.delete(sessionId);
|
|
74010
|
+
}
|
|
74011
|
+
};
|
|
74012
|
+
}
|
|
74013
|
+
getBuffer(sessionId) {
|
|
74014
|
+
let buffer = this.buffers.get(sessionId);
|
|
74015
|
+
if (!buffer) {
|
|
74016
|
+
buffer = new SessionEventBuffer(this.bufferSize);
|
|
74017
|
+
this.buffers.set(sessionId, buffer);
|
|
74018
|
+
}
|
|
74019
|
+
return buffer;
|
|
74020
|
+
}
|
|
74021
|
+
broadcast(sessionId, event) {
|
|
74022
|
+
const serialized = JSON.stringify(event.data ?? {});
|
|
74023
|
+
const eventData = typeof serialized === "string" ? serialized : "{}";
|
|
74024
|
+
const eventId = this.getBuffer(sessionId).push(event.type, eventData);
|
|
74025
|
+
const callbacks = this.sessions.get(sessionId);
|
|
74026
|
+
if (!callbacks) return eventId;
|
|
74027
|
+
for (const callback of callbacks) {
|
|
74028
|
+
try {
|
|
74029
|
+
callback(event, eventId);
|
|
74030
|
+
} catch {
|
|
74031
|
+
}
|
|
74032
|
+
}
|
|
74033
|
+
return eventId;
|
|
74034
|
+
}
|
|
74035
|
+
getBufferedEvents(sessionId, sinceId) {
|
|
74036
|
+
const buffer = this.buffers.get(sessionId);
|
|
74037
|
+
if (!buffer) return [];
|
|
74038
|
+
return buffer.getEventsSince(sinceId);
|
|
74039
|
+
}
|
|
74040
|
+
cleanupSession(sessionId) {
|
|
74041
|
+
this.sessions.delete(sessionId);
|
|
74042
|
+
this.buffers.delete(sessionId);
|
|
74043
|
+
}
|
|
74044
|
+
reset() {
|
|
74045
|
+
this.sessions.clear();
|
|
74046
|
+
this.buffers.clear();
|
|
74047
|
+
this.removeAllListeners();
|
|
74048
|
+
}
|
|
74049
|
+
};
|
|
74050
|
+
subtaskStreamManager = new SubtaskStreamManager();
|
|
74051
|
+
}
|
|
74052
|
+
});
|
|
74053
|
+
|
|
74054
|
+
// ../dashboard/src/mission-interview.ts
|
|
74055
|
+
import { EventEmitter as EventEmitter22 } from "node:events";
|
|
74056
|
+
function cleanupInMemoryMissionSession(sessionId) {
|
|
74057
|
+
const session = sessions3.get(sessionId);
|
|
74058
|
+
if (!session) {
|
|
74059
|
+
return false;
|
|
74060
|
+
}
|
|
74061
|
+
if (session.agent) {
|
|
74062
|
+
try {
|
|
74063
|
+
session.agent.session.dispose?.();
|
|
74064
|
+
} catch {
|
|
74065
|
+
}
|
|
74066
|
+
session.agent = void 0;
|
|
74067
|
+
}
|
|
74068
|
+
missionInterviewStreamManager.cleanupSession(sessionId);
|
|
74069
|
+
sessions3.delete(sessionId);
|
|
74070
|
+
return true;
|
|
74071
|
+
}
|
|
74072
|
+
function cleanupExpiredSessions3() {
|
|
74073
|
+
const now = Date.now();
|
|
74074
|
+
for (const [id, session] of sessions3) {
|
|
74075
|
+
if (now - session.updatedAt.getTime() > SESSION_TTL_MS3) {
|
|
74076
|
+
cleanupInMemoryMissionSession(id);
|
|
74077
|
+
}
|
|
74078
|
+
}
|
|
74079
|
+
for (const [ip, entry] of rateLimits3) {
|
|
74080
|
+
if (now - entry.firstRequestAt.getTime() > RATE_LIMIT_WINDOW_MS3) {
|
|
74081
|
+
rateLimits3.delete(ip);
|
|
74082
|
+
}
|
|
74083
|
+
}
|
|
74084
|
+
}
|
|
74085
|
+
var diagnostics4, SESSION_TTL_MS3, CLEANUP_INTERVAL_MS4, RATE_LIMIT_WINDOW_MS3, sessions3, rateLimits3, cleanupInterval4, MissionInterviewStreamManager, missionInterviewStreamManager;
|
|
74086
|
+
var init_mission_interview = __esm({
|
|
74087
|
+
"../dashboard/src/mission-interview.ts"() {
|
|
74088
|
+
"use strict";
|
|
74089
|
+
init_src();
|
|
74090
|
+
init_sse_buffer();
|
|
74091
|
+
init_ai_session_diagnostics();
|
|
74092
|
+
diagnostics4 = createSessionDiagnostics("mission-interview");
|
|
74093
|
+
SESSION_TTL_MS3 = 7 * 24 * 60 * 60 * 1e3;
|
|
74094
|
+
CLEANUP_INTERVAL_MS4 = 5 * 60 * 1e3;
|
|
74095
|
+
RATE_LIMIT_WINDOW_MS3 = 60 * 60 * 1e3;
|
|
74096
|
+
sessions3 = /* @__PURE__ */ new Map();
|
|
74097
|
+
rateLimits3 = /* @__PURE__ */ new Map();
|
|
74098
|
+
cleanupInterval4 = setInterval(cleanupExpiredSessions3, CLEANUP_INTERVAL_MS4);
|
|
74099
|
+
cleanupInterval4.unref?.();
|
|
74100
|
+
process.on("beforeExit", () => clearInterval(cleanupInterval4));
|
|
74101
|
+
MissionInterviewStreamManager = class extends EventEmitter22 {
|
|
74102
|
+
constructor(bufferSize = 100) {
|
|
74103
|
+
super();
|
|
74104
|
+
this.bufferSize = bufferSize;
|
|
74105
|
+
}
|
|
74106
|
+
sessions = /* @__PURE__ */ new Map();
|
|
74107
|
+
buffers = /* @__PURE__ */ new Map();
|
|
74108
|
+
subscribe(sessionId, callback) {
|
|
74109
|
+
if (!this.sessions.has(sessionId)) {
|
|
74110
|
+
this.sessions.set(sessionId, /* @__PURE__ */ new Set());
|
|
74111
|
+
}
|
|
74112
|
+
const callbacks = this.sessions.get(sessionId);
|
|
74113
|
+
callbacks.add(callback);
|
|
74114
|
+
return () => {
|
|
74115
|
+
callbacks.delete(callback);
|
|
74116
|
+
if (callbacks.size === 0) {
|
|
74117
|
+
this.sessions.delete(sessionId);
|
|
74118
|
+
}
|
|
74119
|
+
};
|
|
74120
|
+
}
|
|
74121
|
+
getBuffer(sessionId) {
|
|
74122
|
+
let buffer = this.buffers.get(sessionId);
|
|
74123
|
+
if (!buffer) {
|
|
74124
|
+
buffer = new SessionEventBuffer(this.bufferSize);
|
|
74125
|
+
this.buffers.set(sessionId, buffer);
|
|
74126
|
+
}
|
|
74127
|
+
return buffer;
|
|
74128
|
+
}
|
|
74129
|
+
broadcast(sessionId, event) {
|
|
74130
|
+
const serialized = JSON.stringify(event.data ?? {});
|
|
74131
|
+
const eventData = typeof serialized === "string" ? serialized : "{}";
|
|
74132
|
+
const eventId = this.getBuffer(sessionId).push(event.type, eventData);
|
|
74133
|
+
const callbacks = this.sessions.get(sessionId);
|
|
74134
|
+
if (!callbacks) return eventId;
|
|
74135
|
+
for (const callback of callbacks) {
|
|
74136
|
+
nonfatal(
|
|
74137
|
+
() => callback(event, eventId),
|
|
74138
|
+
diagnostics4,
|
|
74139
|
+
"Error broadcasting to client",
|
|
74140
|
+
{ sessionId, operation: "broadcast" }
|
|
74141
|
+
);
|
|
74142
|
+
}
|
|
74143
|
+
return eventId;
|
|
74144
|
+
}
|
|
74145
|
+
getBufferedEvents(sessionId, sinceId) {
|
|
74146
|
+
const buffer = this.buffers.get(sessionId);
|
|
74147
|
+
if (!buffer) return [];
|
|
74148
|
+
return buffer.getEventsSince(sinceId);
|
|
74149
|
+
}
|
|
74150
|
+
hasSubscribers(sessionId) {
|
|
74151
|
+
const callbacks = this.sessions.get(sessionId);
|
|
74152
|
+
return callbacks !== void 0 && callbacks.size > 0;
|
|
74153
|
+
}
|
|
74154
|
+
cleanupSession(sessionId) {
|
|
74155
|
+
this.sessions.delete(sessionId);
|
|
74156
|
+
this.buffers.delete(sessionId);
|
|
74157
|
+
}
|
|
74158
|
+
reset() {
|
|
74159
|
+
this.sessions.clear();
|
|
74160
|
+
this.buffers.clear();
|
|
74161
|
+
this.removeAllListeners();
|
|
74162
|
+
}
|
|
74163
|
+
};
|
|
74164
|
+
missionInterviewStreamManager = new MissionInterviewStreamManager();
|
|
74165
|
+
}
|
|
74166
|
+
});
|
|
74167
|
+
|
|
74168
|
+
// ../dashboard/src/milestone-slice-interview.ts
|
|
74169
|
+
import { EventEmitter as EventEmitter23 } from "node:events";
|
|
74170
|
+
function cleanupInMemorySession2(sessionId) {
|
|
74171
|
+
const session = sessions4.get(sessionId);
|
|
74172
|
+
if (!session) {
|
|
74173
|
+
return false;
|
|
74174
|
+
}
|
|
74175
|
+
if (session.agent) {
|
|
74176
|
+
try {
|
|
74177
|
+
session.agent.session.dispose?.();
|
|
74178
|
+
} catch {
|
|
74179
|
+
}
|
|
74180
|
+
session.agent = void 0;
|
|
74181
|
+
}
|
|
74182
|
+
milestoneSliceInterviewStreamManager.cleanupSession(sessionId);
|
|
74183
|
+
sessions4.delete(sessionId);
|
|
74184
|
+
return true;
|
|
74185
|
+
}
|
|
74186
|
+
function cleanupExpiredSessions4() {
|
|
74187
|
+
const now = Date.now();
|
|
74188
|
+
for (const [id, session] of sessions4) {
|
|
74189
|
+
if (now - session.updatedAt.getTime() > SESSION_TTL_MS4) {
|
|
74190
|
+
cleanupInMemorySession2(id);
|
|
74191
|
+
}
|
|
74192
|
+
}
|
|
74193
|
+
for (const [ip, entry] of rateLimits4) {
|
|
74194
|
+
if (now - entry.firstRequestAt.getTime() > RATE_LIMIT_WINDOW_MS4) {
|
|
74195
|
+
rateLimits4.delete(ip);
|
|
74196
|
+
}
|
|
74197
|
+
}
|
|
74198
|
+
}
|
|
74199
|
+
var diagnostics5, SESSION_TTL_MS4, CLEANUP_INTERVAL_MS5, RATE_LIMIT_WINDOW_MS4, sessions4, rateLimits4, cleanupInterval5, MilestoneSliceInterviewStreamManager, milestoneSliceInterviewStreamManager;
|
|
74200
|
+
var init_milestone_slice_interview = __esm({
|
|
74201
|
+
"../dashboard/src/milestone-slice-interview.ts"() {
|
|
74202
|
+
"use strict";
|
|
74203
|
+
init_sse_buffer();
|
|
74204
|
+
init_mission_interview();
|
|
74205
|
+
init_ai_session_diagnostics();
|
|
74206
|
+
init_mission_interview();
|
|
74207
|
+
diagnostics5 = createSessionDiagnostics("milestone-slice-interview");
|
|
74208
|
+
SESSION_TTL_MS4 = 7 * 24 * 60 * 60 * 1e3;
|
|
74209
|
+
CLEANUP_INTERVAL_MS5 = 5 * 60 * 1e3;
|
|
74210
|
+
RATE_LIMIT_WINDOW_MS4 = 60 * 60 * 1e3;
|
|
74211
|
+
sessions4 = /* @__PURE__ */ new Map();
|
|
74212
|
+
rateLimits4 = /* @__PURE__ */ new Map();
|
|
74213
|
+
cleanupInterval5 = setInterval(cleanupExpiredSessions4, CLEANUP_INTERVAL_MS5);
|
|
74214
|
+
cleanupInterval5.unref?.();
|
|
74215
|
+
process.on("beforeExit", () => clearInterval(cleanupInterval5));
|
|
74216
|
+
MilestoneSliceInterviewStreamManager = class extends EventEmitter23 {
|
|
74217
|
+
constructor(bufferSize = 100) {
|
|
74218
|
+
super();
|
|
74219
|
+
this.bufferSize = bufferSize;
|
|
74220
|
+
}
|
|
74221
|
+
sessions = /* @__PURE__ */ new Map();
|
|
74222
|
+
buffers = /* @__PURE__ */ new Map();
|
|
74223
|
+
subscribe(sessionId, callback) {
|
|
74224
|
+
if (!this.sessions.has(sessionId)) {
|
|
74225
|
+
this.sessions.set(sessionId, /* @__PURE__ */ new Set());
|
|
74226
|
+
}
|
|
74227
|
+
const callbacks = this.sessions.get(sessionId);
|
|
74228
|
+
callbacks.add(callback);
|
|
74229
|
+
return () => {
|
|
74230
|
+
callbacks.delete(callback);
|
|
74231
|
+
if (callbacks.size === 0) {
|
|
74232
|
+
this.sessions.delete(sessionId);
|
|
74233
|
+
}
|
|
74234
|
+
};
|
|
74235
|
+
}
|
|
74236
|
+
getBuffer(sessionId) {
|
|
74237
|
+
let buffer = this.buffers.get(sessionId);
|
|
74238
|
+
if (!buffer) {
|
|
74239
|
+
buffer = new SessionEventBuffer(this.bufferSize);
|
|
74240
|
+
this.buffers.set(sessionId, buffer);
|
|
74241
|
+
}
|
|
74242
|
+
return buffer;
|
|
74243
|
+
}
|
|
74244
|
+
broadcast(sessionId, event) {
|
|
74245
|
+
const serialized = JSON.stringify(event.data ?? {});
|
|
74246
|
+
const eventData = typeof serialized === "string" ? serialized : "{}";
|
|
74247
|
+
const eventId = this.getBuffer(sessionId).push(event.type, eventData);
|
|
74248
|
+
const callbacks = this.sessions.get(sessionId);
|
|
74249
|
+
if (!callbacks) return eventId;
|
|
74250
|
+
for (const callback of callbacks) {
|
|
74251
|
+
nonfatal(
|
|
74252
|
+
() => callback(event, eventId),
|
|
74253
|
+
diagnostics5,
|
|
74254
|
+
"Error broadcasting to client",
|
|
74255
|
+
{ sessionId, operation: "broadcast" }
|
|
74256
|
+
);
|
|
74257
|
+
}
|
|
74258
|
+
return eventId;
|
|
74259
|
+
}
|
|
74260
|
+
getBufferedEvents(sessionId, sinceId) {
|
|
74261
|
+
const buffer = this.buffers.get(sessionId);
|
|
74262
|
+
if (!buffer) return [];
|
|
74263
|
+
return buffer.getEventsSince(sinceId);
|
|
74264
|
+
}
|
|
74265
|
+
hasSubscribers(sessionId) {
|
|
74266
|
+
const callbacks = this.sessions.get(sessionId);
|
|
74267
|
+
return callbacks !== void 0 && callbacks.size > 0;
|
|
74268
|
+
}
|
|
74269
|
+
cleanupSession(sessionId) {
|
|
74270
|
+
this.sessions.delete(sessionId);
|
|
74271
|
+
this.buffers.delete(sessionId);
|
|
74272
|
+
}
|
|
74273
|
+
reset() {
|
|
74274
|
+
this.sessions.clear();
|
|
74275
|
+
this.buffers.clear();
|
|
74276
|
+
this.removeAllListeners();
|
|
74277
|
+
}
|
|
74278
|
+
};
|
|
74279
|
+
milestoneSliceInterviewStreamManager = new MilestoneSliceInterviewStreamManager();
|
|
74280
|
+
}
|
|
74281
|
+
});
|
|
74282
|
+
|
|
74283
|
+
// ../dashboard/src/runtime-logger.ts
|
|
74284
|
+
var init_runtime_logger = __esm({
|
|
74285
|
+
"../dashboard/src/runtime-logger.ts"() {
|
|
74286
|
+
"use strict";
|
|
74287
|
+
}
|
|
74288
|
+
});
|
|
74289
|
+
|
|
74290
|
+
// ../dashboard/src/api-error.ts
|
|
74291
|
+
var init_api_error = __esm({
|
|
74292
|
+
"../dashboard/src/api-error.ts"() {
|
|
74293
|
+
"use strict";
|
|
74294
|
+
init_runtime_logger();
|
|
74295
|
+
}
|
|
74296
|
+
});
|
|
74297
|
+
|
|
74298
|
+
// ../dashboard/src/plugin-routes.ts
|
|
74299
|
+
import { Router } from "express";
|
|
74300
|
+
var init_plugin_routes = __esm({
|
|
74301
|
+
"../dashboard/src/plugin-routes.ts"() {
|
|
74302
|
+
"use strict";
|
|
74303
|
+
init_src();
|
|
74304
|
+
init_api_error();
|
|
74305
|
+
}
|
|
74306
|
+
});
|
|
74307
|
+
|
|
74308
|
+
// ../dashboard/src/project-store-resolver.ts
|
|
74309
|
+
var init_project_store_resolver = __esm({
|
|
74310
|
+
"../dashboard/src/project-store-resolver.ts"() {
|
|
74311
|
+
"use strict";
|
|
74312
|
+
}
|
|
74313
|
+
});
|
|
74314
|
+
|
|
74315
|
+
// ../dashboard/src/routes/context.ts
|
|
74316
|
+
import { Router as Router2 } from "express";
|
|
74317
|
+
var init_context = __esm({
|
|
74318
|
+
"../dashboard/src/routes/context.ts"() {
|
|
74319
|
+
"use strict";
|
|
74320
|
+
init_api_error();
|
|
74321
|
+
init_project_store_resolver();
|
|
74322
|
+
init_runtime_logger();
|
|
74323
|
+
}
|
|
74324
|
+
});
|
|
74325
|
+
|
|
74326
|
+
// ../dashboard/src/routes/register-task-workflow-routes.ts
|
|
74327
|
+
var init_register_task_workflow_routes = __esm({
|
|
74328
|
+
"../dashboard/src/routes/register-task-workflow-routes.ts"() {
|
|
74329
|
+
"use strict";
|
|
74330
|
+
init_src();
|
|
74331
|
+
init_api_error();
|
|
74332
|
+
}
|
|
74333
|
+
});
|
|
74334
|
+
|
|
74335
|
+
// ../dashboard/src/routes/register-planning-subtask-routes.ts
|
|
74336
|
+
var init_register_planning_subtask_routes = __esm({
|
|
74337
|
+
"../dashboard/src/routes/register-planning-subtask-routes.ts"() {
|
|
74338
|
+
"use strict";
|
|
74339
|
+
init_api_error();
|
|
74340
|
+
init_sse_buffer();
|
|
74341
|
+
}
|
|
74342
|
+
});
|
|
74343
|
+
|
|
74344
|
+
// ../dashboard/src/rate-limit.ts
|
|
74345
|
+
var init_rate_limit = __esm({
|
|
74346
|
+
"../dashboard/src/rate-limit.ts"() {
|
|
74347
|
+
"use strict";
|
|
74348
|
+
init_api_error();
|
|
74349
|
+
}
|
|
74350
|
+
});
|
|
74351
|
+
|
|
74352
|
+
// ../dashboard/src/routes/register-chat-routes.ts
|
|
74353
|
+
var init_register_chat_routes = __esm({
|
|
74354
|
+
"../dashboard/src/routes/register-chat-routes.ts"() {
|
|
74355
|
+
"use strict";
|
|
74356
|
+
init_api_error();
|
|
74357
|
+
init_rate_limit();
|
|
74358
|
+
init_sse_buffer();
|
|
74359
|
+
}
|
|
74360
|
+
});
|
|
74361
|
+
|
|
74362
|
+
// ../dashboard/src/remote-auth.ts
|
|
74363
|
+
var init_remote_auth = __esm({
|
|
74364
|
+
"../dashboard/src/remote-auth.ts"() {
|
|
74365
|
+
"use strict";
|
|
74366
|
+
}
|
|
74367
|
+
});
|
|
74368
|
+
|
|
74369
|
+
// ../dashboard/src/routes/register-settings-memory-routes.ts
|
|
74370
|
+
var init_register_settings_memory_routes = __esm({
|
|
74371
|
+
"../dashboard/src/routes/register-settings-memory-routes.ts"() {
|
|
74372
|
+
"use strict";
|
|
74373
|
+
init_src();
|
|
74374
|
+
init_api_error();
|
|
74375
|
+
init_remote_auth();
|
|
74376
|
+
init_project_store_resolver();
|
|
74377
|
+
}
|
|
74378
|
+
});
|
|
74379
|
+
|
|
74380
|
+
// ../dashboard/src/routes/register-messaging-scripts.ts
|
|
74381
|
+
var init_register_messaging_scripts = __esm({
|
|
74382
|
+
"../dashboard/src/routes/register-messaging-scripts.ts"() {
|
|
74383
|
+
"use strict";
|
|
74384
|
+
init_src();
|
|
74385
|
+
init_api_error();
|
|
74386
|
+
init_terminal_service();
|
|
72568
74387
|
}
|
|
72569
74388
|
});
|
|
72570
74389
|
|
|
@@ -74048,7 +75867,7 @@ var init_github = __esm({
|
|
|
74048
75867
|
});
|
|
74049
75868
|
|
|
74050
75869
|
// ../dashboard/src/github-poll.ts
|
|
74051
|
-
import { EventEmitter as
|
|
75870
|
+
import { EventEmitter as EventEmitter24 } from "node:events";
|
|
74052
75871
|
function toAlias(type, number) {
|
|
74053
75872
|
return `${type}_${number}`;
|
|
74054
75873
|
}
|
|
@@ -74094,7 +75913,7 @@ var init_github_poll = __esm({
|
|
|
74094
75913
|
}
|
|
74095
75914
|
};
|
|
74096
75915
|
githubRateLimiter = new GitHubRateLimiter();
|
|
74097
|
-
GitHubPollingService = class extends
|
|
75916
|
+
GitHubPollingService = class extends EventEmitter24 {
|
|
74098
75917
|
watches = /* @__PURE__ */ new Map();
|
|
74099
75918
|
rateLimiter;
|
|
74100
75919
|
pollingIntervalMs;
|
|
@@ -74338,309 +76157,82 @@ var init_github_poll = __esm({
|
|
|
74338
76157
|
}
|
|
74339
76158
|
});
|
|
74340
76159
|
|
|
74341
|
-
// ../dashboard/src/
|
|
74342
|
-
import {
|
|
74343
|
-
import {
|
|
74344
|
-
|
|
74345
|
-
|
|
74346
|
-
|
|
74347
|
-
|
|
74348
|
-
|
|
74349
|
-
if (match2) {
|
|
74350
|
-
trimmed = trimmed.slice(match2[0].length).trim();
|
|
74351
|
-
} else {
|
|
74352
|
-
break;
|
|
74353
|
-
}
|
|
74354
|
-
}
|
|
74355
|
-
if (trimmed.startsWith("sudo ")) {
|
|
74356
|
-
trimmed = trimmed.slice(5).trim();
|
|
74357
|
-
}
|
|
74358
|
-
const match = trimmed.match(/^([A-Za-z0-9._-]+)/);
|
|
74359
|
-
return match ? match[1].toLowerCase() : null;
|
|
74360
|
-
}
|
|
74361
|
-
function validateCommand(command) {
|
|
74362
|
-
for (const pattern of SUBSTITUTION_PATTERNS) {
|
|
74363
|
-
if (pattern.test(command)) {
|
|
74364
|
-
return { valid: false, error: "Command substitution is not allowed" };
|
|
74365
|
-
}
|
|
74366
|
-
}
|
|
74367
|
-
if (/[\0\r\n]/.test(command)) {
|
|
74368
|
-
return { valid: false, error: "Command contains invalid control characters" };
|
|
76160
|
+
// ../dashboard/src/routes/resolve-diff-base.ts
|
|
76161
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
76162
|
+
import { promisify as promisify11 } from "node:util";
|
|
76163
|
+
var execFileAsync2;
|
|
76164
|
+
var init_resolve_diff_base = __esm({
|
|
76165
|
+
"../dashboard/src/routes/resolve-diff-base.ts"() {
|
|
76166
|
+
"use strict";
|
|
76167
|
+
execFileAsync2 = promisify11(execFile4);
|
|
74369
76168
|
}
|
|
74370
|
-
|
|
74371
|
-
|
|
74372
|
-
|
|
74373
|
-
|
|
76169
|
+
});
|
|
76170
|
+
|
|
76171
|
+
// ../dashboard/src/routes/register-git-github.ts
|
|
76172
|
+
var init_register_git_github = __esm({
|
|
76173
|
+
"../dashboard/src/routes/register-git-github.ts"() {
|
|
76174
|
+
"use strict";
|
|
76175
|
+
init_src();
|
|
76176
|
+
init_api_error();
|
|
76177
|
+
init_github();
|
|
76178
|
+
init_github_poll();
|
|
76179
|
+
init_github_webhooks();
|
|
76180
|
+
init_resolve_diff_base();
|
|
74374
76181
|
}
|
|
74375
|
-
|
|
74376
|
-
|
|
74377
|
-
|
|
74378
|
-
|
|
74379
|
-
|
|
74380
|
-
|
|
74381
|
-
|
|
74382
|
-
|
|
74383
|
-
if (!ALLOWED_COMMANDS.has(baseCommand)) {
|
|
74384
|
-
return {
|
|
74385
|
-
valid: false,
|
|
74386
|
-
error: `Command '${baseCommand}' is not in the allowed command list. Allowed commands: ${Array.from(ALLOWED_COMMANDS).sort().join(", ")}`
|
|
74387
|
-
};
|
|
74388
|
-
}
|
|
76182
|
+
});
|
|
76183
|
+
|
|
76184
|
+
// ../dashboard/src/file-service.ts
|
|
76185
|
+
var MAX_FILE_SIZE;
|
|
76186
|
+
var init_file_service = __esm({
|
|
76187
|
+
"../dashboard/src/file-service.ts"() {
|
|
76188
|
+
"use strict";
|
|
76189
|
+
MAX_FILE_SIZE = 1024 * 1024;
|
|
74389
76190
|
}
|
|
74390
|
-
|
|
74391
|
-
|
|
74392
|
-
|
|
74393
|
-
var
|
|
74394
|
-
"../dashboard/src/
|
|
76191
|
+
});
|
|
76192
|
+
|
|
76193
|
+
// ../dashboard/src/routes/register-file-workspace-routes.ts
|
|
76194
|
+
var init_register_file_workspace_routes = __esm({
|
|
76195
|
+
"../dashboard/src/routes/register-file-workspace-routes.ts"() {
|
|
74395
76196
|
"use strict";
|
|
74396
|
-
|
|
74397
|
-
|
|
74398
|
-
"git",
|
|
74399
|
-
// Package managers
|
|
74400
|
-
"npm",
|
|
74401
|
-
"pnpm",
|
|
74402
|
-
"yarn",
|
|
74403
|
-
"bun",
|
|
74404
|
-
// File operations
|
|
74405
|
-
"ls",
|
|
74406
|
-
"cat",
|
|
74407
|
-
"echo",
|
|
74408
|
-
"pwd",
|
|
74409
|
-
"cd",
|
|
74410
|
-
"mkdir",
|
|
74411
|
-
"touch",
|
|
74412
|
-
"cp",
|
|
74413
|
-
"mv",
|
|
74414
|
-
"rm",
|
|
74415
|
-
"head",
|
|
74416
|
-
"tail",
|
|
74417
|
-
"less",
|
|
74418
|
-
"more",
|
|
74419
|
-
"find",
|
|
74420
|
-
"grep",
|
|
74421
|
-
// System info
|
|
74422
|
-
"clear",
|
|
74423
|
-
"which",
|
|
74424
|
-
"whoami",
|
|
74425
|
-
"uname",
|
|
74426
|
-
"date",
|
|
74427
|
-
// Node/JS
|
|
74428
|
-
"node",
|
|
74429
|
-
"npx",
|
|
74430
|
-
"tsx",
|
|
74431
|
-
// Python
|
|
74432
|
-
"python",
|
|
74433
|
-
"python3",
|
|
74434
|
-
"pip",
|
|
74435
|
-
"pip3",
|
|
74436
|
-
// Network
|
|
74437
|
-
"curl",
|
|
74438
|
-
"wget",
|
|
74439
|
-
// Build tools
|
|
74440
|
-
"make",
|
|
74441
|
-
"cmake",
|
|
74442
|
-
// Process management
|
|
74443
|
-
"ps",
|
|
74444
|
-
"top",
|
|
74445
|
-
"htop",
|
|
74446
|
-
"kill",
|
|
74447
|
-
"pkill",
|
|
74448
|
-
// Shell builtins that are safe
|
|
74449
|
-
"source",
|
|
74450
|
-
".",
|
|
74451
|
-
"export",
|
|
74452
|
-
"env",
|
|
74453
|
-
"printenv",
|
|
74454
|
-
"alias",
|
|
74455
|
-
// Editors (for viewing)
|
|
74456
|
-
"code",
|
|
74457
|
-
// VS Code CLI
|
|
74458
|
-
"vim",
|
|
74459
|
-
"vi",
|
|
74460
|
-
"nano"
|
|
74461
|
-
]);
|
|
74462
|
-
BLOCKED_PATTERNS = [
|
|
74463
|
-
// System destruction
|
|
74464
|
-
/rm\s+(-[rf]+\s+)?\/\s*$/i,
|
|
74465
|
-
// rm -rf / or rm /
|
|
74466
|
-
/rm\s+(-[rf]+\s+)?\/\*/i,
|
|
74467
|
-
// rm -rf /*
|
|
74468
|
-
/>\s*\/dev\/sda/i,
|
|
74469
|
-
// Direct disk write
|
|
74470
|
-
/:\(\)\s*\{\s*:\s*\|:\s*&\s*\};\s*:/i,
|
|
74471
|
-
// Fork bomb
|
|
74472
|
-
/mkfs\.\w+\s+/i,
|
|
74473
|
-
// Filesystem formatting
|
|
74474
|
-
/dd\s+if=/i,
|
|
74475
|
-
// dd with input file
|
|
74476
|
-
/\.\s*\/dev\/null/i
|
|
74477
|
-
// Sourcing /dev/null tricks
|
|
74478
|
-
];
|
|
74479
|
-
SUBSTITUTION_PATTERNS = [
|
|
74480
|
-
/\$\(/,
|
|
74481
|
-
// $(...) — command substitution
|
|
74482
|
-
/`/,
|
|
74483
|
-
// `...` — backtick command substitution
|
|
74484
|
-
/<\(/,
|
|
74485
|
-
// <(...) — process substitution (bash)
|
|
74486
|
-
/>\(/
|
|
74487
|
-
// >(...) — process substitution (bash)
|
|
74488
|
-
];
|
|
74489
|
-
CHAIN_OPERATORS = /&&|\|\||;|(?<!\|)\|(?!\|)/;
|
|
74490
|
-
TerminalSessionManager = class extends EventEmitter20 {
|
|
74491
|
-
sessions = /* @__PURE__ */ new Map();
|
|
74492
|
-
defaultTimeout = 3e4;
|
|
74493
|
-
// 30 seconds
|
|
74494
|
-
/**
|
|
74495
|
-
* Creates a new terminal session and spawns the command.
|
|
74496
|
-
* Returns the session ID for tracking.
|
|
74497
|
-
*/
|
|
74498
|
-
createSession(command, cwd) {
|
|
74499
|
-
const validation = validateCommand(command);
|
|
74500
|
-
if (!validation.valid) {
|
|
74501
|
-
return { sessionId: "", error: validation.error };
|
|
74502
|
-
}
|
|
74503
|
-
const sessionId = randomUUID11();
|
|
74504
|
-
const childProcess = spawn2(command, [], {
|
|
74505
|
-
cwd,
|
|
74506
|
-
shell: true,
|
|
74507
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
74508
|
-
env: { ...process.env, FORCE_COLOR: "1", TERM: "xterm-256color" }
|
|
74509
|
-
});
|
|
74510
|
-
const session = {
|
|
74511
|
-
id: sessionId,
|
|
74512
|
-
command,
|
|
74513
|
-
process: childProcess,
|
|
74514
|
-
startTime: /* @__PURE__ */ new Date(),
|
|
74515
|
-
output: [],
|
|
74516
|
-
exitCode: null,
|
|
74517
|
-
killed: false
|
|
74518
|
-
};
|
|
74519
|
-
this.sessions.set(sessionId, session);
|
|
74520
|
-
childProcess.stdout?.on("data", (data) => {
|
|
74521
|
-
const chunk = data.toString("utf-8");
|
|
74522
|
-
session.output.push(chunk);
|
|
74523
|
-
this.emit("output", { sessionId, type: "stdout", data: chunk });
|
|
74524
|
-
});
|
|
74525
|
-
childProcess.stderr?.on("data", (data) => {
|
|
74526
|
-
const chunk = data.toString("utf-8");
|
|
74527
|
-
session.output.push(chunk);
|
|
74528
|
-
this.emit("output", { sessionId, type: "stderr", data: chunk });
|
|
74529
|
-
});
|
|
74530
|
-
childProcess.on("exit", (code) => {
|
|
74531
|
-
session.exitCode = code ?? 0;
|
|
74532
|
-
this.emit("output", {
|
|
74533
|
-
sessionId,
|
|
74534
|
-
type: "exit",
|
|
74535
|
-
data: `Process exited with code ${code ?? 0}`,
|
|
74536
|
-
exitCode: code ?? 0
|
|
74537
|
-
});
|
|
74538
|
-
setTimeout(() => this.cleanupSession(sessionId), 5e3);
|
|
74539
|
-
});
|
|
74540
|
-
childProcess.on("error", (err) => {
|
|
74541
|
-
const errorMsg = err.message;
|
|
74542
|
-
session.output.push(errorMsg);
|
|
74543
|
-
session.exitCode = 127;
|
|
74544
|
-
this.emit("output", { sessionId, type: "stderr", data: errorMsg });
|
|
74545
|
-
this.emit("output", {
|
|
74546
|
-
sessionId,
|
|
74547
|
-
type: "exit",
|
|
74548
|
-
data: `Process failed: ${errorMsg}`,
|
|
74549
|
-
exitCode: 127
|
|
74550
|
-
});
|
|
74551
|
-
setTimeout(() => this.cleanupSession(sessionId), 5e3);
|
|
74552
|
-
});
|
|
74553
|
-
const timeout = setTimeout(() => {
|
|
74554
|
-
if (!session.exitCode && !session.killed) {
|
|
74555
|
-
this.killSession(sessionId, "SIGTERM");
|
|
74556
|
-
this.emit("output", {
|
|
74557
|
-
sessionId,
|
|
74558
|
-
type: "stderr",
|
|
74559
|
-
data: "\n[Command timed out after 30 seconds]\n"
|
|
74560
|
-
});
|
|
74561
|
-
}
|
|
74562
|
-
}, this.defaultTimeout);
|
|
74563
|
-
childProcess.on("exit", () => clearTimeout(timeout));
|
|
74564
|
-
return { sessionId };
|
|
74565
|
-
}
|
|
74566
|
-
/**
|
|
74567
|
-
* Gets a session by ID.
|
|
74568
|
-
*/
|
|
74569
|
-
getSession(sessionId) {
|
|
74570
|
-
return this.sessions.get(sessionId);
|
|
74571
|
-
}
|
|
74572
|
-
/**
|
|
74573
|
-
* Kills a running session's process.
|
|
74574
|
-
* Returns true if killed, false if not found or already exited.
|
|
74575
|
-
*/
|
|
74576
|
-
killSession(sessionId, signal = "SIGTERM") {
|
|
74577
|
-
const session = this.sessions.get(sessionId);
|
|
74578
|
-
if (!session || session.exitCode !== null || session.killed) {
|
|
74579
|
-
return false;
|
|
74580
|
-
}
|
|
74581
|
-
session.killed = true;
|
|
74582
|
-
try {
|
|
74583
|
-
if (session.process.pid) {
|
|
74584
|
-
process.kill(-session.process.pid, signal);
|
|
74585
|
-
}
|
|
74586
|
-
} catch {
|
|
74587
|
-
session.process.kill(signal);
|
|
74588
|
-
}
|
|
74589
|
-
return true;
|
|
74590
|
-
}
|
|
74591
|
-
/**
|
|
74592
|
-
* Cleans up a session from memory.
|
|
74593
|
-
*/
|
|
74594
|
-
cleanupSession(sessionId) {
|
|
74595
|
-
const session = this.sessions.get(sessionId);
|
|
74596
|
-
if (!session) return false;
|
|
74597
|
-
if (session.exitCode === null && !session.killed) {
|
|
74598
|
-
this.killSession(sessionId, "SIGKILL");
|
|
74599
|
-
}
|
|
74600
|
-
this.sessions.delete(sessionId);
|
|
74601
|
-
return true;
|
|
74602
|
-
}
|
|
74603
|
-
/**
|
|
74604
|
-
* Lists all active sessions.
|
|
74605
|
-
*/
|
|
74606
|
-
listSessions() {
|
|
74607
|
-
return Array.from(this.sessions.values()).map((s) => ({
|
|
74608
|
-
id: s.id,
|
|
74609
|
-
command: s.command,
|
|
74610
|
-
running: s.exitCode === null && !s.killed,
|
|
74611
|
-
startTime: s.startTime
|
|
74612
|
-
}));
|
|
74613
|
-
}
|
|
74614
|
-
/**
|
|
74615
|
-
* Cleans up all sessions (useful for shutdown).
|
|
74616
|
-
*/
|
|
74617
|
-
cleanupAll() {
|
|
74618
|
-
for (const [sessionId] of this.sessions) {
|
|
74619
|
-
this.cleanupSession(sessionId);
|
|
74620
|
-
}
|
|
74621
|
-
}
|
|
74622
|
-
};
|
|
74623
|
-
terminalSessionManager = new TerminalSessionManager();
|
|
76197
|
+
init_api_error();
|
|
76198
|
+
init_file_service();
|
|
74624
76199
|
}
|
|
74625
76200
|
});
|
|
74626
76201
|
|
|
74627
|
-
// ../dashboard/src/
|
|
74628
|
-
|
|
74629
|
-
|
|
74630
|
-
var init_terminal_service = __esm({
|
|
74631
|
-
"../dashboard/src/terminal-service.ts"() {
|
|
76202
|
+
// ../dashboard/src/routes/register-agents-projects-nodes.ts
|
|
76203
|
+
var init_register_agents_projects_nodes = __esm({
|
|
76204
|
+
"../dashboard/src/routes/register-agents-projects-nodes.ts"() {
|
|
74632
76205
|
"use strict";
|
|
74633
|
-
isBunBinary = typeof Bun !== "undefined" && !!Bun.embeddedFiles;
|
|
74634
|
-
require2 = createRequire2(import.meta.url);
|
|
74635
76206
|
}
|
|
74636
76207
|
});
|
|
74637
76208
|
|
|
74638
|
-
// ../dashboard/src/
|
|
74639
|
-
|
|
74640
|
-
|
|
74641
|
-
|
|
76209
|
+
// ../dashboard/src/routes/register-project-routes.ts
|
|
76210
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
76211
|
+
import * as fsPromises from "node:fs/promises";
|
|
76212
|
+
import { promisify as promisify12 } from "node:util";
|
|
76213
|
+
var access3, stat6, mkdir12, readdir8, rm2, execFileAsync3;
|
|
76214
|
+
var init_register_project_routes = __esm({
|
|
76215
|
+
"../dashboard/src/routes/register-project-routes.ts"() {
|
|
74642
76216
|
"use strict";
|
|
74643
|
-
|
|
76217
|
+
init_src();
|
|
76218
|
+
init_api_error();
|
|
76219
|
+
init_project_store_resolver();
|
|
76220
|
+
({
|
|
76221
|
+
access: access3,
|
|
76222
|
+
stat: stat6,
|
|
76223
|
+
mkdir: mkdir12,
|
|
76224
|
+
readdir: readdir8,
|
|
76225
|
+
rm: rm2
|
|
76226
|
+
} = fsPromises);
|
|
76227
|
+
execFileAsync3 = promisify12(execFile5);
|
|
76228
|
+
}
|
|
76229
|
+
});
|
|
76230
|
+
|
|
76231
|
+
// ../dashboard/src/routes/register-node-routes.ts
|
|
76232
|
+
var init_register_node_routes = __esm({
|
|
76233
|
+
"../dashboard/src/routes/register-node-routes.ts"() {
|
|
76234
|
+
"use strict";
|
|
76235
|
+
init_api_error();
|
|
74644
76236
|
}
|
|
74645
76237
|
});
|
|
74646
76238
|
|
|
@@ -74651,138 +76243,72 @@ var init_auth_paths = __esm({
|
|
|
74651
76243
|
}
|
|
74652
76244
|
});
|
|
74653
76245
|
|
|
74654
|
-
// ../dashboard/src/
|
|
74655
|
-
var
|
|
74656
|
-
"../dashboard/src/
|
|
76246
|
+
// ../dashboard/src/routes/register-settings-sync-helpers.ts
|
|
76247
|
+
var init_register_settings_sync_helpers = __esm({
|
|
76248
|
+
"../dashboard/src/routes/register-settings-sync-helpers.ts"() {
|
|
74657
76249
|
"use strict";
|
|
76250
|
+
init_api_error();
|
|
74658
76251
|
init_auth_paths();
|
|
74659
76252
|
}
|
|
74660
76253
|
});
|
|
74661
76254
|
|
|
74662
|
-
// ../dashboard/src/
|
|
74663
|
-
var
|
|
74664
|
-
"../dashboard/src/
|
|
76255
|
+
// ../dashboard/src/routes/register-settings-sync-routes.ts
|
|
76256
|
+
var init_register_settings_sync_routes = __esm({
|
|
76257
|
+
"../dashboard/src/routes/register-settings-sync-routes.ts"() {
|
|
74665
76258
|
"use strict";
|
|
76259
|
+
init_api_error();
|
|
76260
|
+
init_auth_paths();
|
|
76261
|
+
init_register_settings_sync_helpers();
|
|
74666
76262
|
}
|
|
74667
76263
|
});
|
|
74668
76264
|
|
|
74669
|
-
// ../dashboard/src/
|
|
74670
|
-
var
|
|
74671
|
-
"../dashboard/src/
|
|
76265
|
+
// ../dashboard/src/routes/register-mesh-routes.ts
|
|
76266
|
+
var init_register_mesh_routes = __esm({
|
|
76267
|
+
"../dashboard/src/routes/register-mesh-routes.ts"() {
|
|
74672
76268
|
"use strict";
|
|
76269
|
+
init_api_error();
|
|
74673
76270
|
}
|
|
74674
76271
|
});
|
|
74675
76272
|
|
|
74676
|
-
// ../dashboard/src/
|
|
74677
|
-
var
|
|
74678
|
-
|
|
74679
|
-
"../dashboard/src/ai-session-store.ts"() {
|
|
76273
|
+
// ../dashboard/src/routes/register-discovery-routes.ts
|
|
76274
|
+
var init_register_discovery_routes = __esm({
|
|
76275
|
+
"../dashboard/src/routes/register-discovery-routes.ts"() {
|
|
74680
76276
|
"use strict";
|
|
74681
|
-
|
|
74682
|
-
MAX_THINKING_BYTES = 50 * 1024;
|
|
74683
|
-
SESSION_CLEANUP_DEFAULT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
74684
|
-
SESSION_CLEANUP_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
74685
|
-
diagnostics2 = createSessionDiagnostics("ai-session-store");
|
|
76277
|
+
init_api_error();
|
|
74686
76278
|
}
|
|
74687
76279
|
});
|
|
74688
76280
|
|
|
74689
|
-
// ../dashboard/src/
|
|
74690
|
-
|
|
74691
|
-
|
|
74692
|
-
|
|
74693
|
-
|
|
74694
|
-
|
|
76281
|
+
// ../dashboard/src/routes/register-settings-sync-inbound-routes.ts
|
|
76282
|
+
var init_register_settings_sync_inbound_routes = __esm({
|
|
76283
|
+
"../dashboard/src/routes/register-settings-sync-inbound-routes.ts"() {
|
|
76284
|
+
"use strict";
|
|
76285
|
+
init_api_error();
|
|
76286
|
+
init_auth_paths();
|
|
76287
|
+
init_register_settings_sync_helpers();
|
|
74695
76288
|
}
|
|
74696
|
-
|
|
74697
|
-
|
|
74698
|
-
|
|
76289
|
+
});
|
|
76290
|
+
|
|
76291
|
+
// ../dashboard/src/routes/register-agent-core-routes.ts
|
|
76292
|
+
var init_register_agent_core_routes = __esm({
|
|
76293
|
+
"../dashboard/src/routes/register-agent-core-routes.ts"() {
|
|
76294
|
+
"use strict";
|
|
76295
|
+
init_api_error();
|
|
74699
76296
|
}
|
|
74700
|
-
|
|
74701
|
-
|
|
74702
|
-
|
|
74703
|
-
|
|
74704
|
-
|
|
74705
|
-
|
|
74706
|
-
|
|
74707
|
-
if (now - session.updatedAt.getTime() > SESSION_TTL_MS2) {
|
|
74708
|
-
cleanupInMemorySubtaskSession(id);
|
|
74709
|
-
}
|
|
76297
|
+
});
|
|
76298
|
+
|
|
76299
|
+
// ../dashboard/src/routes/register-agent-runtime-routes.ts
|
|
76300
|
+
var init_register_agent_runtime_routes = __esm({
|
|
76301
|
+
"../dashboard/src/routes/register-agent-runtime-routes.ts"() {
|
|
76302
|
+
"use strict";
|
|
76303
|
+
init_api_error();
|
|
74710
76304
|
}
|
|
74711
|
-
}
|
|
74712
|
-
|
|
74713
|
-
|
|
74714
|
-
|
|
76305
|
+
});
|
|
76306
|
+
|
|
76307
|
+
// ../dashboard/src/routes/register-agent-reflection-rating-routes.ts
|
|
76308
|
+
var init_register_agent_reflection_rating_routes = __esm({
|
|
76309
|
+
"../dashboard/src/routes/register-agent-reflection-rating-routes.ts"() {
|
|
74715
76310
|
"use strict";
|
|
74716
|
-
|
|
74717
|
-
init_sse_buffer();
|
|
74718
|
-
init_ai_session_diagnostics();
|
|
74719
|
-
diagnostics3 = createSessionDiagnostics("subtask-breakdown");
|
|
74720
|
-
SESSION_TTL_MS2 = 7 * 24 * 60 * 60 * 1e3;
|
|
74721
|
-
CLEANUP_INTERVAL_MS3 = 5 * 60 * 1e3;
|
|
74722
|
-
sessions2 = /* @__PURE__ */ new Map();
|
|
74723
|
-
cleanupInterval3 = setInterval(cleanupExpiredSessions2, CLEANUP_INTERVAL_MS3);
|
|
74724
|
-
cleanupInterval3.unref?.();
|
|
74725
|
-
process.on("beforeExit", () => {
|
|
74726
|
-
clearInterval(cleanupInterval3);
|
|
74727
|
-
});
|
|
74728
|
-
SubtaskStreamManager = class extends EventEmitter21 {
|
|
74729
|
-
constructor(bufferSize = 100) {
|
|
74730
|
-
super();
|
|
74731
|
-
this.bufferSize = bufferSize;
|
|
74732
|
-
}
|
|
74733
|
-
sessions = /* @__PURE__ */ new Map();
|
|
74734
|
-
buffers = /* @__PURE__ */ new Map();
|
|
74735
|
-
subscribe(sessionId, callback) {
|
|
74736
|
-
if (!this.sessions.has(sessionId)) {
|
|
74737
|
-
this.sessions.set(sessionId, /* @__PURE__ */ new Set());
|
|
74738
|
-
}
|
|
74739
|
-
const callbacks = this.sessions.get(sessionId);
|
|
74740
|
-
callbacks.add(callback);
|
|
74741
|
-
return () => {
|
|
74742
|
-
callbacks.delete(callback);
|
|
74743
|
-
if (callbacks.size === 0) {
|
|
74744
|
-
this.sessions.delete(sessionId);
|
|
74745
|
-
}
|
|
74746
|
-
};
|
|
74747
|
-
}
|
|
74748
|
-
getBuffer(sessionId) {
|
|
74749
|
-
let buffer = this.buffers.get(sessionId);
|
|
74750
|
-
if (!buffer) {
|
|
74751
|
-
buffer = new SessionEventBuffer(this.bufferSize);
|
|
74752
|
-
this.buffers.set(sessionId, buffer);
|
|
74753
|
-
}
|
|
74754
|
-
return buffer;
|
|
74755
|
-
}
|
|
74756
|
-
broadcast(sessionId, event) {
|
|
74757
|
-
const serialized = JSON.stringify(event.data ?? {});
|
|
74758
|
-
const eventData = typeof serialized === "string" ? serialized : "{}";
|
|
74759
|
-
const eventId = this.getBuffer(sessionId).push(event.type, eventData);
|
|
74760
|
-
const callbacks = this.sessions.get(sessionId);
|
|
74761
|
-
if (!callbacks) return eventId;
|
|
74762
|
-
for (const callback of callbacks) {
|
|
74763
|
-
try {
|
|
74764
|
-
callback(event, eventId);
|
|
74765
|
-
} catch {
|
|
74766
|
-
}
|
|
74767
|
-
}
|
|
74768
|
-
return eventId;
|
|
74769
|
-
}
|
|
74770
|
-
getBufferedEvents(sessionId, sinceId) {
|
|
74771
|
-
const buffer = this.buffers.get(sessionId);
|
|
74772
|
-
if (!buffer) return [];
|
|
74773
|
-
return buffer.getEventsSince(sinceId);
|
|
74774
|
-
}
|
|
74775
|
-
cleanupSession(sessionId) {
|
|
74776
|
-
this.sessions.delete(sessionId);
|
|
74777
|
-
this.buffers.delete(sessionId);
|
|
74778
|
-
}
|
|
74779
|
-
reset() {
|
|
74780
|
-
this.sessions.clear();
|
|
74781
|
-
this.buffers.clear();
|
|
74782
|
-
this.removeAllListeners();
|
|
74783
|
-
}
|
|
74784
|
-
};
|
|
74785
|
-
subtaskStreamManager = new SubtaskStreamManager();
|
|
76311
|
+
init_api_error();
|
|
74786
76312
|
}
|
|
74787
76313
|
});
|
|
74788
76314
|
|
|
@@ -74798,33 +76324,33 @@ async function initPromptCatalog() {
|
|
|
74798
76324
|
promptCatalogReady = true;
|
|
74799
76325
|
}
|
|
74800
76326
|
}
|
|
74801
|
-
function
|
|
76327
|
+
function cleanupExpiredSessions5() {
|
|
74802
76328
|
const now = Date.now();
|
|
74803
76329
|
let cleanedSessions = 0;
|
|
74804
76330
|
let cleanedRateLimits = 0;
|
|
74805
|
-
for (const [id, session] of
|
|
74806
|
-
if (now - session.updatedAt.getTime() >
|
|
74807
|
-
|
|
76331
|
+
for (const [id, session] of sessions5) {
|
|
76332
|
+
if (now - session.updatedAt.getTime() > SESSION_TTL_MS5) {
|
|
76333
|
+
sessions5.delete(id);
|
|
74808
76334
|
cleanedSessions++;
|
|
74809
76335
|
}
|
|
74810
76336
|
}
|
|
74811
|
-
for (const [ip, entry] of
|
|
74812
|
-
if (now - entry.firstRequestAt.getTime() >
|
|
74813
|
-
|
|
76337
|
+
for (const [ip, entry] of rateLimits5) {
|
|
76338
|
+
if (now - entry.firstRequestAt.getTime() > RATE_LIMIT_WINDOW_MS5) {
|
|
76339
|
+
rateLimits5.delete(ip);
|
|
74814
76340
|
cleanedRateLimits++;
|
|
74815
76341
|
}
|
|
74816
76342
|
}
|
|
74817
76343
|
if (cleanedSessions > 0 || cleanedRateLimits > 0) {
|
|
74818
|
-
|
|
76344
|
+
diagnostics6.info("Cleanup completed", {
|
|
74819
76345
|
cleanedSessions,
|
|
74820
76346
|
cleanedRateLimits,
|
|
74821
|
-
ttlMs:
|
|
74822
|
-
rateLimitWindowMs:
|
|
76347
|
+
ttlMs: SESSION_TTL_MS5,
|
|
76348
|
+
rateLimitWindowMs: RATE_LIMIT_WINDOW_MS5,
|
|
74823
76349
|
operation: "cleanup-expired"
|
|
74824
76350
|
});
|
|
74825
76351
|
}
|
|
74826
76352
|
}
|
|
74827
|
-
var resolvePrompt2, promptCatalogReady, promptCatalogReadyPromise,
|
|
76353
|
+
var resolvePrompt2, promptCatalogReady, promptCatalogReadyPromise, SESSION_TTL_MS5, CLEANUP_INTERVAL_MS6, RATE_LIMIT_WINDOW_MS5, sessions5, rateLimits5, diagnostics6, cleanupInterval6;
|
|
74828
76354
|
var init_agent_generation = __esm({
|
|
74829
76355
|
"../dashboard/src/agent-generation.ts"() {
|
|
74830
76356
|
"use strict";
|
|
@@ -74832,357 +76358,97 @@ var init_agent_generation = __esm({
|
|
|
74832
76358
|
resolvePrompt2 = () => "";
|
|
74833
76359
|
promptCatalogReady = false;
|
|
74834
76360
|
promptCatalogReadyPromise = initPromptCatalog();
|
|
74835
|
-
|
|
74836
|
-
CLEANUP_INTERVAL_MS4 = 5 * 60 * 1e3;
|
|
74837
|
-
RATE_LIMIT_WINDOW_MS3 = 60 * 60 * 1e3;
|
|
74838
|
-
sessions3 = /* @__PURE__ */ new Map();
|
|
74839
|
-
rateLimits3 = /* @__PURE__ */ new Map();
|
|
74840
|
-
diagnostics4 = createSessionDiagnostics("agent-generation");
|
|
74841
|
-
cleanupInterval4 = setInterval(cleanupExpiredSessions3, CLEANUP_INTERVAL_MS4);
|
|
74842
|
-
cleanupInterval4.unref?.();
|
|
74843
|
-
process.on("beforeExit", () => {
|
|
74844
|
-
clearInterval(cleanupInterval4);
|
|
74845
|
-
});
|
|
74846
|
-
}
|
|
74847
|
-
});
|
|
74848
|
-
|
|
74849
|
-
// ../dashboard/src/mission-interview.ts
|
|
74850
|
-
import { EventEmitter as EventEmitter22 } from "node:events";
|
|
74851
|
-
function cleanupInMemoryMissionSession(sessionId) {
|
|
74852
|
-
const session = sessions4.get(sessionId);
|
|
74853
|
-
if (!session) {
|
|
74854
|
-
return false;
|
|
74855
|
-
}
|
|
74856
|
-
if (session.agent) {
|
|
74857
|
-
try {
|
|
74858
|
-
session.agent.session.dispose?.();
|
|
74859
|
-
} catch {
|
|
74860
|
-
}
|
|
74861
|
-
session.agent = void 0;
|
|
74862
|
-
}
|
|
74863
|
-
missionInterviewStreamManager.cleanupSession(sessionId);
|
|
74864
|
-
sessions4.delete(sessionId);
|
|
74865
|
-
return true;
|
|
74866
|
-
}
|
|
74867
|
-
function cleanupExpiredSessions4() {
|
|
74868
|
-
const now = Date.now();
|
|
74869
|
-
for (const [id, session] of sessions4) {
|
|
74870
|
-
if (now - session.updatedAt.getTime() > SESSION_TTL_MS4) {
|
|
74871
|
-
cleanupInMemoryMissionSession(id);
|
|
74872
|
-
}
|
|
74873
|
-
}
|
|
74874
|
-
for (const [ip, entry] of rateLimits4) {
|
|
74875
|
-
if (now - entry.firstRequestAt.getTime() > RATE_LIMIT_WINDOW_MS4) {
|
|
74876
|
-
rateLimits4.delete(ip);
|
|
74877
|
-
}
|
|
74878
|
-
}
|
|
74879
|
-
}
|
|
74880
|
-
var diagnostics5, SESSION_TTL_MS4, CLEANUP_INTERVAL_MS5, RATE_LIMIT_WINDOW_MS4, sessions4, rateLimits4, cleanupInterval5, MissionInterviewStreamManager, missionInterviewStreamManager;
|
|
74881
|
-
var init_mission_interview = __esm({
|
|
74882
|
-
"../dashboard/src/mission-interview.ts"() {
|
|
74883
|
-
"use strict";
|
|
74884
|
-
init_src();
|
|
74885
|
-
init_sse_buffer();
|
|
74886
|
-
init_ai_session_diagnostics();
|
|
74887
|
-
diagnostics5 = createSessionDiagnostics("mission-interview");
|
|
74888
|
-
SESSION_TTL_MS4 = 7 * 24 * 60 * 60 * 1e3;
|
|
74889
|
-
CLEANUP_INTERVAL_MS5 = 5 * 60 * 1e3;
|
|
74890
|
-
RATE_LIMIT_WINDOW_MS4 = 60 * 60 * 1e3;
|
|
74891
|
-
sessions4 = /* @__PURE__ */ new Map();
|
|
74892
|
-
rateLimits4 = /* @__PURE__ */ new Map();
|
|
74893
|
-
cleanupInterval5 = setInterval(cleanupExpiredSessions4, CLEANUP_INTERVAL_MS5);
|
|
74894
|
-
cleanupInterval5.unref?.();
|
|
74895
|
-
process.on("beforeExit", () => clearInterval(cleanupInterval5));
|
|
74896
|
-
MissionInterviewStreamManager = class extends EventEmitter22 {
|
|
74897
|
-
constructor(bufferSize = 100) {
|
|
74898
|
-
super();
|
|
74899
|
-
this.bufferSize = bufferSize;
|
|
74900
|
-
}
|
|
74901
|
-
sessions = /* @__PURE__ */ new Map();
|
|
74902
|
-
buffers = /* @__PURE__ */ new Map();
|
|
74903
|
-
subscribe(sessionId, callback) {
|
|
74904
|
-
if (!this.sessions.has(sessionId)) {
|
|
74905
|
-
this.sessions.set(sessionId, /* @__PURE__ */ new Set());
|
|
74906
|
-
}
|
|
74907
|
-
const callbacks = this.sessions.get(sessionId);
|
|
74908
|
-
callbacks.add(callback);
|
|
74909
|
-
return () => {
|
|
74910
|
-
callbacks.delete(callback);
|
|
74911
|
-
if (callbacks.size === 0) {
|
|
74912
|
-
this.sessions.delete(sessionId);
|
|
74913
|
-
}
|
|
74914
|
-
};
|
|
74915
|
-
}
|
|
74916
|
-
getBuffer(sessionId) {
|
|
74917
|
-
let buffer = this.buffers.get(sessionId);
|
|
74918
|
-
if (!buffer) {
|
|
74919
|
-
buffer = new SessionEventBuffer(this.bufferSize);
|
|
74920
|
-
this.buffers.set(sessionId, buffer);
|
|
74921
|
-
}
|
|
74922
|
-
return buffer;
|
|
74923
|
-
}
|
|
74924
|
-
broadcast(sessionId, event) {
|
|
74925
|
-
const serialized = JSON.stringify(event.data ?? {});
|
|
74926
|
-
const eventData = typeof serialized === "string" ? serialized : "{}";
|
|
74927
|
-
const eventId = this.getBuffer(sessionId).push(event.type, eventData);
|
|
74928
|
-
const callbacks = this.sessions.get(sessionId);
|
|
74929
|
-
if (!callbacks) return eventId;
|
|
74930
|
-
for (const callback of callbacks) {
|
|
74931
|
-
nonfatal(
|
|
74932
|
-
() => callback(event, eventId),
|
|
74933
|
-
diagnostics5,
|
|
74934
|
-
"Error broadcasting to client",
|
|
74935
|
-
{ sessionId, operation: "broadcast" }
|
|
74936
|
-
);
|
|
74937
|
-
}
|
|
74938
|
-
return eventId;
|
|
74939
|
-
}
|
|
74940
|
-
getBufferedEvents(sessionId, sinceId) {
|
|
74941
|
-
const buffer = this.buffers.get(sessionId);
|
|
74942
|
-
if (!buffer) return [];
|
|
74943
|
-
return buffer.getEventsSince(sinceId);
|
|
74944
|
-
}
|
|
74945
|
-
hasSubscribers(sessionId) {
|
|
74946
|
-
const callbacks = this.sessions.get(sessionId);
|
|
74947
|
-
return callbacks !== void 0 && callbacks.size > 0;
|
|
74948
|
-
}
|
|
74949
|
-
cleanupSession(sessionId) {
|
|
74950
|
-
this.sessions.delete(sessionId);
|
|
74951
|
-
this.buffers.delete(sessionId);
|
|
74952
|
-
}
|
|
74953
|
-
reset() {
|
|
74954
|
-
this.sessions.clear();
|
|
74955
|
-
this.buffers.clear();
|
|
74956
|
-
this.removeAllListeners();
|
|
74957
|
-
}
|
|
74958
|
-
};
|
|
74959
|
-
missionInterviewStreamManager = new MissionInterviewStreamManager();
|
|
74960
|
-
}
|
|
74961
|
-
});
|
|
74962
|
-
|
|
74963
|
-
// ../dashboard/src/milestone-slice-interview.ts
|
|
74964
|
-
import { EventEmitter as EventEmitter23 } from "node:events";
|
|
74965
|
-
function cleanupInMemorySession2(sessionId) {
|
|
74966
|
-
const session = sessions5.get(sessionId);
|
|
74967
|
-
if (!session) {
|
|
74968
|
-
return false;
|
|
74969
|
-
}
|
|
74970
|
-
if (session.agent) {
|
|
74971
|
-
try {
|
|
74972
|
-
session.agent.session.dispose?.();
|
|
74973
|
-
} catch {
|
|
74974
|
-
}
|
|
74975
|
-
session.agent = void 0;
|
|
74976
|
-
}
|
|
74977
|
-
milestoneSliceInterviewStreamManager.cleanupSession(sessionId);
|
|
74978
|
-
sessions5.delete(sessionId);
|
|
74979
|
-
return true;
|
|
74980
|
-
}
|
|
74981
|
-
function cleanupExpiredSessions5() {
|
|
74982
|
-
const now = Date.now();
|
|
74983
|
-
for (const [id, session] of sessions5) {
|
|
74984
|
-
if (now - session.updatedAt.getTime() > SESSION_TTL_MS5) {
|
|
74985
|
-
cleanupInMemorySession2(id);
|
|
74986
|
-
}
|
|
74987
|
-
}
|
|
74988
|
-
for (const [ip, entry] of rateLimits5) {
|
|
74989
|
-
if (now - entry.firstRequestAt.getTime() > RATE_LIMIT_WINDOW_MS5) {
|
|
74990
|
-
rateLimits5.delete(ip);
|
|
74991
|
-
}
|
|
74992
|
-
}
|
|
74993
|
-
}
|
|
74994
|
-
var diagnostics6, SESSION_TTL_MS5, CLEANUP_INTERVAL_MS6, RATE_LIMIT_WINDOW_MS5, sessions5, rateLimits5, cleanupInterval6, MilestoneSliceInterviewStreamManager, milestoneSliceInterviewStreamManager;
|
|
74995
|
-
var init_milestone_slice_interview = __esm({
|
|
74996
|
-
"../dashboard/src/milestone-slice-interview.ts"() {
|
|
74997
|
-
"use strict";
|
|
74998
|
-
init_sse_buffer();
|
|
74999
|
-
init_mission_interview();
|
|
75000
|
-
init_ai_session_diagnostics();
|
|
75001
|
-
init_mission_interview();
|
|
75002
|
-
diagnostics6 = createSessionDiagnostics("milestone-slice-interview");
|
|
75003
|
-
SESSION_TTL_MS5 = 7 * 24 * 60 * 60 * 1e3;
|
|
76361
|
+
SESSION_TTL_MS5 = 30 * 60 * 1e3;
|
|
75004
76362
|
CLEANUP_INTERVAL_MS6 = 5 * 60 * 1e3;
|
|
75005
76363
|
RATE_LIMIT_WINDOW_MS5 = 60 * 60 * 1e3;
|
|
75006
76364
|
sessions5 = /* @__PURE__ */ new Map();
|
|
75007
76365
|
rateLimits5 = /* @__PURE__ */ new Map();
|
|
76366
|
+
diagnostics6 = createSessionDiagnostics("agent-generation");
|
|
75008
76367
|
cleanupInterval6 = setInterval(cleanupExpiredSessions5, CLEANUP_INTERVAL_MS6);
|
|
75009
76368
|
cleanupInterval6.unref?.();
|
|
75010
|
-
process.on("beforeExit", () =>
|
|
75011
|
-
|
|
75012
|
-
|
|
75013
|
-
super();
|
|
75014
|
-
this.bufferSize = bufferSize;
|
|
75015
|
-
}
|
|
75016
|
-
sessions = /* @__PURE__ */ new Map();
|
|
75017
|
-
buffers = /* @__PURE__ */ new Map();
|
|
75018
|
-
subscribe(sessionId, callback) {
|
|
75019
|
-
if (!this.sessions.has(sessionId)) {
|
|
75020
|
-
this.sessions.set(sessionId, /* @__PURE__ */ new Set());
|
|
75021
|
-
}
|
|
75022
|
-
const callbacks = this.sessions.get(sessionId);
|
|
75023
|
-
callbacks.add(callback);
|
|
75024
|
-
return () => {
|
|
75025
|
-
callbacks.delete(callback);
|
|
75026
|
-
if (callbacks.size === 0) {
|
|
75027
|
-
this.sessions.delete(sessionId);
|
|
75028
|
-
}
|
|
75029
|
-
};
|
|
75030
|
-
}
|
|
75031
|
-
getBuffer(sessionId) {
|
|
75032
|
-
let buffer = this.buffers.get(sessionId);
|
|
75033
|
-
if (!buffer) {
|
|
75034
|
-
buffer = new SessionEventBuffer(this.bufferSize);
|
|
75035
|
-
this.buffers.set(sessionId, buffer);
|
|
75036
|
-
}
|
|
75037
|
-
return buffer;
|
|
75038
|
-
}
|
|
75039
|
-
broadcast(sessionId, event) {
|
|
75040
|
-
const serialized = JSON.stringify(event.data ?? {});
|
|
75041
|
-
const eventData = typeof serialized === "string" ? serialized : "{}";
|
|
75042
|
-
const eventId = this.getBuffer(sessionId).push(event.type, eventData);
|
|
75043
|
-
const callbacks = this.sessions.get(sessionId);
|
|
75044
|
-
if (!callbacks) return eventId;
|
|
75045
|
-
for (const callback of callbacks) {
|
|
75046
|
-
nonfatal(
|
|
75047
|
-
() => callback(event, eventId),
|
|
75048
|
-
diagnostics6,
|
|
75049
|
-
"Error broadcasting to client",
|
|
75050
|
-
{ sessionId, operation: "broadcast" }
|
|
75051
|
-
);
|
|
75052
|
-
}
|
|
75053
|
-
return eventId;
|
|
75054
|
-
}
|
|
75055
|
-
getBufferedEvents(sessionId, sinceId) {
|
|
75056
|
-
const buffer = this.buffers.get(sessionId);
|
|
75057
|
-
if (!buffer) return [];
|
|
75058
|
-
return buffer.getEventsSince(sinceId);
|
|
75059
|
-
}
|
|
75060
|
-
hasSubscribers(sessionId) {
|
|
75061
|
-
const callbacks = this.sessions.get(sessionId);
|
|
75062
|
-
return callbacks !== void 0 && callbacks.size > 0;
|
|
75063
|
-
}
|
|
75064
|
-
cleanupSession(sessionId) {
|
|
75065
|
-
this.sessions.delete(sessionId);
|
|
75066
|
-
this.buffers.delete(sessionId);
|
|
75067
|
-
}
|
|
75068
|
-
reset() {
|
|
75069
|
-
this.sessions.clear();
|
|
75070
|
-
this.buffers.clear();
|
|
75071
|
-
this.removeAllListeners();
|
|
75072
|
-
}
|
|
75073
|
-
};
|
|
75074
|
-
milestoneSliceInterviewStreamManager = new MilestoneSliceInterviewStreamManager();
|
|
75075
|
-
}
|
|
75076
|
-
});
|
|
75077
|
-
|
|
75078
|
-
// ../dashboard/src/runtime-logger.ts
|
|
75079
|
-
var init_runtime_logger = __esm({
|
|
75080
|
-
"../dashboard/src/runtime-logger.ts"() {
|
|
75081
|
-
"use strict";
|
|
75082
|
-
}
|
|
75083
|
-
});
|
|
75084
|
-
|
|
75085
|
-
// ../dashboard/src/api-error.ts
|
|
75086
|
-
var init_api_error = __esm({
|
|
75087
|
-
"../dashboard/src/api-error.ts"() {
|
|
75088
|
-
"use strict";
|
|
75089
|
-
init_runtime_logger();
|
|
75090
|
-
}
|
|
75091
|
-
});
|
|
75092
|
-
|
|
75093
|
-
// ../dashboard/src/rate-limit.ts
|
|
75094
|
-
var init_rate_limit = __esm({
|
|
75095
|
-
"../dashboard/src/rate-limit.ts"() {
|
|
75096
|
-
"use strict";
|
|
75097
|
-
init_api_error();
|
|
76369
|
+
process.on("beforeExit", () => {
|
|
76370
|
+
clearInterval(cleanupInterval6);
|
|
76371
|
+
});
|
|
75098
76372
|
}
|
|
75099
76373
|
});
|
|
75100
76374
|
|
|
75101
|
-
// ../dashboard/src/
|
|
75102
|
-
import
|
|
75103
|
-
var
|
|
75104
|
-
|
|
76375
|
+
// ../dashboard/src/routes/register-agent-import-export-generation-routes.ts
|
|
76376
|
+
import * as fsPromises2 from "node:fs/promises";
|
|
76377
|
+
var mkdtemp, access4, stat7, mkdir13, rm3, fsWriteFile;
|
|
76378
|
+
var init_register_agent_import_export_generation_routes = __esm({
|
|
76379
|
+
"../dashboard/src/routes/register-agent-import-export-generation-routes.ts"() {
|
|
75105
76380
|
"use strict";
|
|
75106
|
-
init_src();
|
|
75107
76381
|
init_api_error();
|
|
76382
|
+
init_ai_session_diagnostics();
|
|
76383
|
+
init_agent_generation();
|
|
76384
|
+
({ mkdtemp, access: access4, stat: stat7, mkdir: mkdir13, rm: rm3, writeFile: fsWriteFile } = fsPromises2);
|
|
75108
76385
|
}
|
|
75109
76386
|
});
|
|
75110
76387
|
|
|
75111
|
-
// ../dashboard/src/routes/
|
|
75112
|
-
|
|
75113
|
-
|
|
75114
|
-
"../dashboard/src/routes/context.ts"() {
|
|
76388
|
+
// ../dashboard/src/routes/register-agent-skills-routes.ts
|
|
76389
|
+
var init_register_agent_skills_routes = __esm({
|
|
76390
|
+
"../dashboard/src/routes/register-agent-skills-routes.ts"() {
|
|
75115
76391
|
"use strict";
|
|
75116
76392
|
init_api_error();
|
|
75117
|
-
init_project_store_resolver();
|
|
75118
|
-
init_runtime_logger();
|
|
75119
76393
|
}
|
|
75120
76394
|
});
|
|
75121
76395
|
|
|
75122
|
-
// ../dashboard/src/routes/register-
|
|
75123
|
-
var
|
|
75124
|
-
"../dashboard/src/routes/register-
|
|
75125
|
-
"use strict";
|
|
75126
|
-
}
|
|
75127
|
-
});
|
|
75128
|
-
|
|
75129
|
-
// ../dashboard/src/routes/register-planning-chat.ts
|
|
75130
|
-
var init_register_planning_chat = __esm({
|
|
75131
|
-
"../dashboard/src/routes/register-planning-chat.ts"() {
|
|
75132
|
-
"use strict";
|
|
75133
|
-
}
|
|
75134
|
-
});
|
|
75135
|
-
|
|
75136
|
-
// ../dashboard/src/routes/register-settings-memory.ts
|
|
75137
|
-
var init_register_settings_memory = __esm({
|
|
75138
|
-
"../dashboard/src/routes/register-settings-memory.ts"() {
|
|
76396
|
+
// ../dashboard/src/routes/register-plugins-automation.ts
|
|
76397
|
+
var init_register_plugins_automation = __esm({
|
|
76398
|
+
"../dashboard/src/routes/register-plugins-automation.ts"() {
|
|
75139
76399
|
"use strict";
|
|
75140
76400
|
}
|
|
75141
76401
|
});
|
|
75142
76402
|
|
|
75143
|
-
// ../dashboard/src/routes/register-
|
|
75144
|
-
var
|
|
75145
|
-
"../dashboard/src/routes/register-
|
|
76403
|
+
// ../dashboard/src/routes/register-proxy.ts
|
|
76404
|
+
var init_register_proxy = __esm({
|
|
76405
|
+
"../dashboard/src/routes/register-proxy.ts"() {
|
|
75146
76406
|
"use strict";
|
|
75147
|
-
init_src();
|
|
75148
76407
|
init_api_error();
|
|
75149
|
-
init_terminal_service();
|
|
75150
76408
|
}
|
|
75151
76409
|
});
|
|
75152
76410
|
|
|
75153
|
-
// ../dashboard/src/routes/register-
|
|
75154
|
-
var
|
|
75155
|
-
"../dashboard/src/routes/register-
|
|
76411
|
+
// ../dashboard/src/routes/register-model-routes.ts
|
|
76412
|
+
var init_register_model_routes = __esm({
|
|
76413
|
+
"../dashboard/src/routes/register-model-routes.ts"() {
|
|
75156
76414
|
"use strict";
|
|
76415
|
+
init_api_error();
|
|
75157
76416
|
}
|
|
75158
76417
|
});
|
|
75159
76418
|
|
|
75160
|
-
// ../dashboard/src/
|
|
75161
|
-
var
|
|
75162
|
-
"../dashboard/src/
|
|
76419
|
+
// ../dashboard/src/usage.ts
|
|
76420
|
+
var init_usage = __esm({
|
|
76421
|
+
"../dashboard/src/usage.ts"() {
|
|
75163
76422
|
"use strict";
|
|
76423
|
+
init_auth_paths();
|
|
75164
76424
|
}
|
|
75165
76425
|
});
|
|
75166
76426
|
|
|
75167
|
-
// ../dashboard/src/routes/register-
|
|
75168
|
-
var
|
|
75169
|
-
"../dashboard/src/routes/register-
|
|
76427
|
+
// ../dashboard/src/routes/register-usage-routes.ts
|
|
76428
|
+
var init_register_usage_routes = __esm({
|
|
76429
|
+
"../dashboard/src/routes/register-usage-routes.ts"() {
|
|
75170
76430
|
"use strict";
|
|
76431
|
+
init_api_error();
|
|
76432
|
+
init_usage();
|
|
75171
76433
|
}
|
|
75172
76434
|
});
|
|
75173
76435
|
|
|
75174
|
-
// ../dashboard/src/
|
|
75175
|
-
var
|
|
75176
|
-
"../dashboard/src/
|
|
76436
|
+
// ../dashboard/src/claude-cli-probe.ts
|
|
76437
|
+
var init_claude_cli_probe = __esm({
|
|
76438
|
+
"../dashboard/src/claude-cli-probe.ts"() {
|
|
75177
76439
|
"use strict";
|
|
75178
76440
|
}
|
|
75179
76441
|
});
|
|
75180
76442
|
|
|
75181
|
-
// ../dashboard/src/routes/register-
|
|
75182
|
-
var
|
|
75183
|
-
"../dashboard/src/routes/register-
|
|
76443
|
+
// ../dashboard/src/routes/register-auth-routes.ts
|
|
76444
|
+
var init_register_auth_routes = __esm({
|
|
76445
|
+
"../dashboard/src/routes/register-auth-routes.ts"() {
|
|
75184
76446
|
"use strict";
|
|
76447
|
+
init_src();
|
|
76448
|
+
init_claude_cli_probe();
|
|
75185
76449
|
init_api_error();
|
|
76450
|
+
init_usage();
|
|
76451
|
+
init_project_store_resolver();
|
|
75186
76452
|
}
|
|
75187
76453
|
});
|
|
75188
76454
|
|
|
@@ -75280,11 +76546,25 @@ var init_register_integrated_routers = __esm({
|
|
|
75280
76546
|
}
|
|
75281
76547
|
});
|
|
75282
76548
|
|
|
76549
|
+
// ../dashboard/src/routes/register-terminal-routes.ts
|
|
76550
|
+
var init_register_terminal_routes = __esm({
|
|
76551
|
+
"../dashboard/src/routes/register-terminal-routes.ts"() {
|
|
76552
|
+
"use strict";
|
|
76553
|
+
init_api_error();
|
|
76554
|
+
}
|
|
76555
|
+
});
|
|
76556
|
+
|
|
76557
|
+
// ../dashboard/src/routes/register-session-diff-routes.ts
|
|
76558
|
+
var init_register_session_diff_routes = __esm({
|
|
76559
|
+
"../dashboard/src/routes/register-session-diff-routes.ts"() {
|
|
76560
|
+
"use strict";
|
|
76561
|
+
init_api_error();
|
|
76562
|
+
init_resolve_diff_base();
|
|
76563
|
+
}
|
|
76564
|
+
});
|
|
76565
|
+
|
|
75283
76566
|
// ../dashboard/src/routes.ts
|
|
75284
76567
|
import multer from "multer";
|
|
75285
|
-
import * as fsPromises from "node:fs/promises";
|
|
75286
|
-
import { execFile as execFile3 } from "node:child_process";
|
|
75287
|
-
import { promisify as promisify10 } from "node:util";
|
|
75288
76568
|
async function initPromptOverrides() {
|
|
75289
76569
|
if (promptOverridesReady) return;
|
|
75290
76570
|
try {
|
|
@@ -75296,60 +76576,59 @@ async function initPromptOverrides() {
|
|
|
75296
76576
|
promptOverridesReady = true;
|
|
75297
76577
|
}
|
|
75298
76578
|
}
|
|
75299
|
-
var
|
|
76579
|
+
var upload, resolveWorkflowStepRefinePrompt, promptOverridesReady, DEFAULT_WORKFLOW_STEP_REFINE_PROMPT;
|
|
75300
76580
|
var init_routes = __esm({
|
|
75301
76581
|
"../dashboard/src/routes.ts"() {
|
|
75302
76582
|
"use strict";
|
|
75303
76583
|
init_src();
|
|
75304
|
-
init_claude_cli_probe();
|
|
75305
|
-
init_github();
|
|
75306
|
-
init_github_poll();
|
|
75307
76584
|
init_terminal();
|
|
75308
76585
|
init_terminal_service();
|
|
75309
|
-
init_file_service();
|
|
75310
|
-
init_usage();
|
|
75311
76586
|
init_github_webhooks();
|
|
75312
|
-
init_project_store_resolver();
|
|
75313
76587
|
init_ai_session_store();
|
|
75314
76588
|
init_planning();
|
|
75315
76589
|
init_subtask_breakdown();
|
|
75316
|
-
init_agent_generation();
|
|
75317
76590
|
init_mission_interview();
|
|
75318
76591
|
init_milestone_slice_interview();
|
|
75319
76592
|
init_sse_buffer();
|
|
75320
76593
|
init_api_error();
|
|
75321
|
-
init_rate_limit();
|
|
75322
76594
|
init_plugin_routes();
|
|
75323
|
-
init_auth_paths();
|
|
75324
|
-
init_runtime_logger();
|
|
75325
76595
|
init_ai_session_diagnostics();
|
|
75326
76596
|
init_context();
|
|
75327
|
-
|
|
75328
|
-
|
|
75329
|
-
|
|
76597
|
+
init_register_task_workflow_routes();
|
|
76598
|
+
init_register_planning_subtask_routes();
|
|
76599
|
+
init_register_chat_routes();
|
|
76600
|
+
init_register_settings_memory_routes();
|
|
75330
76601
|
init_register_messaging_scripts();
|
|
75331
76602
|
init_register_git_github();
|
|
75332
|
-
|
|
76603
|
+
init_register_file_workspace_routes();
|
|
75333
76604
|
init_register_agents_projects_nodes();
|
|
76605
|
+
init_register_project_routes();
|
|
76606
|
+
init_register_node_routes();
|
|
76607
|
+
init_register_settings_sync_routes();
|
|
76608
|
+
init_register_mesh_routes();
|
|
76609
|
+
init_register_discovery_routes();
|
|
76610
|
+
init_register_settings_sync_inbound_routes();
|
|
76611
|
+
init_register_agent_core_routes();
|
|
76612
|
+
init_register_agent_runtime_routes();
|
|
76613
|
+
init_register_agent_reflection_rating_routes();
|
|
76614
|
+
init_register_agent_import_export_generation_routes();
|
|
76615
|
+
init_register_agent_skills_routes();
|
|
75334
76616
|
init_register_plugins_automation();
|
|
75335
76617
|
init_register_proxy();
|
|
76618
|
+
init_register_model_routes();
|
|
76619
|
+
init_register_usage_routes();
|
|
76620
|
+
init_register_auth_routes();
|
|
75336
76621
|
init_register_integrated_routers();
|
|
75337
|
-
(
|
|
75338
|
-
|
|
75339
|
-
|
|
75340
|
-
|
|
75341
|
-
|
|
75342
|
-
readdir: readdir8,
|
|
75343
|
-
rm: rm2,
|
|
75344
|
-
readFile: fsReadFile,
|
|
75345
|
-
writeFile: fsWriteFile
|
|
75346
|
-
} = fsPromises);
|
|
76622
|
+
init_register_terminal_routes();
|
|
76623
|
+
init_register_session_diff_routes();
|
|
76624
|
+
init_resolve_diff_base();
|
|
76625
|
+
init_register_git_github();
|
|
76626
|
+
init_resolve_diff_base();
|
|
75347
76627
|
upload = multer({
|
|
75348
76628
|
storage: multer.memoryStorage(),
|
|
75349
76629
|
limits: { fileSize: 5 * 1024 * 1024 }
|
|
75350
76630
|
// 5MB
|
|
75351
76631
|
});
|
|
75352
|
-
execFileAsync = promisify10(execFile3);
|
|
75353
76632
|
resolveWorkflowStepRefinePrompt = () => DEFAULT_WORKFLOW_STEP_REFINE_PROMPT;
|
|
75354
76633
|
promptOverridesReady = false;
|
|
75355
76634
|
initPromptOverrides();
|
|
@@ -77577,7 +78856,7 @@ var require_extension = __commonJS({
|
|
|
77577
78856
|
var require_websocket = __commonJS({
|
|
77578
78857
|
"../../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket.js"(exports, module) {
|
|
77579
78858
|
"use strict";
|
|
77580
|
-
var
|
|
78859
|
+
var EventEmitter26 = __require("events");
|
|
77581
78860
|
var https = __require("https");
|
|
77582
78861
|
var http = __require("http");
|
|
77583
78862
|
var net = __require("net");
|
|
@@ -77609,7 +78888,7 @@ var require_websocket = __commonJS({
|
|
|
77609
78888
|
var protocolVersions = [8, 13];
|
|
77610
78889
|
var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
|
|
77611
78890
|
var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
77612
|
-
var WebSocket2 = class _WebSocket extends
|
|
78891
|
+
var WebSocket2 = class _WebSocket extends EventEmitter26 {
|
|
77613
78892
|
/**
|
|
77614
78893
|
* Create a new `WebSocket`.
|
|
77615
78894
|
*
|
|
@@ -78606,7 +79885,7 @@ var require_subprotocol = __commonJS({
|
|
|
78606
79885
|
var require_websocket_server = __commonJS({
|
|
78607
79886
|
"../../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket-server.js"(exports, module) {
|
|
78608
79887
|
"use strict";
|
|
78609
|
-
var
|
|
79888
|
+
var EventEmitter26 = __require("events");
|
|
78610
79889
|
var http = __require("http");
|
|
78611
79890
|
var { Duplex } = __require("stream");
|
|
78612
79891
|
var { createHash: createHash5 } = __require("crypto");
|
|
@@ -78619,7 +79898,7 @@ var require_websocket_server = __commonJS({
|
|
|
78619
79898
|
var RUNNING = 0;
|
|
78620
79899
|
var CLOSING = 1;
|
|
78621
79900
|
var CLOSED = 2;
|
|
78622
|
-
var WebSocketServer2 = class extends
|
|
79901
|
+
var WebSocketServer2 = class extends EventEmitter26 {
|
|
78623
79902
|
/**
|
|
78624
79903
|
* Create a `WebSocketServer` instance.
|
|
78625
79904
|
*
|
|
@@ -79035,7 +80314,7 @@ var init_terminal_websocket_diagnostics = __esm({
|
|
|
79035
80314
|
});
|
|
79036
80315
|
|
|
79037
80316
|
// ../dashboard/src/chat.ts
|
|
79038
|
-
import { EventEmitter as
|
|
80317
|
+
import { EventEmitter as EventEmitter25 } from "node:events";
|
|
79039
80318
|
var defaultDiagnostics, _diagnostics, diagnostics7, RATE_LIMIT_WINDOW_MS6, MAX_REFERENCED_FILE_SIZE, ChatStreamManager, chatStreamManager;
|
|
79040
80319
|
var init_chat = __esm({
|
|
79041
80320
|
"../dashboard/src/chat.ts"() {
|
|
@@ -79067,7 +80346,7 @@ var init_chat = __esm({
|
|
|
79067
80346
|
};
|
|
79068
80347
|
RATE_LIMIT_WINDOW_MS6 = 60 * 1e3;
|
|
79069
80348
|
MAX_REFERENCED_FILE_SIZE = 50 * 1024;
|
|
79070
|
-
ChatStreamManager = class extends
|
|
80349
|
+
ChatStreamManager = class extends EventEmitter25 {
|
|
79071
80350
|
constructor(bufferSize = 100) {
|
|
79072
80351
|
super();
|
|
79073
80352
|
this.bufferSize = bufferSize;
|
|
@@ -79202,6 +80481,7 @@ var init_server = __esm({
|
|
|
79202
80481
|
init_chat();
|
|
79203
80482
|
init_dev_server_routes();
|
|
79204
80483
|
init_auth_middleware();
|
|
80484
|
+
init_remote_auth();
|
|
79205
80485
|
__dirname = dirname8(fileURLToPath2(import.meta.url));
|
|
79206
80486
|
MIN_AI_SESSION_TTL_MS = 10 * 60 * 1e3;
|
|
79207
80487
|
MAX_AI_SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -80617,7 +81897,7 @@ __export(skills_exports, {
|
|
|
80617
81897
|
runSkillsSearch: () => runSkillsSearch,
|
|
80618
81898
|
searchSkills: () => searchSkills
|
|
80619
81899
|
});
|
|
80620
|
-
import { spawn as
|
|
81900
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
80621
81901
|
async function searchSkills(query, limit = 10) {
|
|
80622
81902
|
const url = `${SKILLS_API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=${limit}`;
|
|
80623
81903
|
try {
|
|
@@ -80695,7 +81975,7 @@ async function runSkillsInstall(args, options) {
|
|
|
80695
81975
|
npxArgs.push("--skill", options.skill);
|
|
80696
81976
|
}
|
|
80697
81977
|
npxArgs.push("-y", "-a", "pi");
|
|
80698
|
-
const child =
|
|
81978
|
+
const child = spawn4("npx", npxArgs, {
|
|
80699
81979
|
cwd: process.cwd(),
|
|
80700
81980
|
stdio: "inherit"
|
|
80701
81981
|
});
|
|
@@ -80731,7 +82011,7 @@ import { StringEnum } from "@mariozechner/pi-ai";
|
|
|
80731
82011
|
import { resolve as resolve15, basename as basename8, extname as extname2, join as join36 } from "node:path";
|
|
80732
82012
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
80733
82013
|
import { existsSync as existsSync30 } from "node:fs";
|
|
80734
|
-
import { spawn as
|
|
82014
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
80735
82015
|
var MIME_TYPES2 = {
|
|
80736
82016
|
".png": "image/png",
|
|
80737
82017
|
".jpg": "image/jpeg",
|
|
@@ -82132,7 +83412,7 @@ Status: ${updated.status}`
|
|
|
82132
83412
|
npxArgs.push("--skill", params.skill);
|
|
82133
83413
|
}
|
|
82134
83414
|
npxArgs.push("-y", "-a", "pi");
|
|
82135
|
-
const child =
|
|
83415
|
+
const child = spawn5("npx", npxArgs, {
|
|
82136
83416
|
cwd: resolveProjectRoot(ctx.cwd),
|
|
82137
83417
|
stdio: "pipe"
|
|
82138
83418
|
});
|
|
@@ -82213,7 +83493,7 @@ Status: ${updated.status}`
|
|
|
82213
83493
|
return;
|
|
82214
83494
|
}
|
|
82215
83495
|
const port = trimmed ? parseInt(trimmed, 10) || 4040 : 4040;
|
|
82216
|
-
const child =
|
|
83496
|
+
const child = spawn5("fn", ["dashboard", "--port", String(port)], {
|
|
82217
83497
|
cwd: resolveProjectRoot(ctx.cwd),
|
|
82218
83498
|
stdio: ["ignore", "pipe", "pipe"],
|
|
82219
83499
|
detached: false,
|