@useorgx/openclaw-plugin 0.7.3 → 0.7.5
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/dashboard/dist/assets/{CZaT3ob_.js → 77gGFBt6.js} +1 -1
- package/dashboard/dist/assets/77gGFBt6.js.br +0 -0
- package/dashboard/dist/assets/77gGFBt6.js.gz +0 -0
- package/dashboard/dist/assets/BBpTN_SR.js +1 -0
- package/dashboard/dist/assets/BBpTN_SR.js.br +0 -0
- package/dashboard/dist/assets/BBpTN_SR.js.gz +0 -0
- package/dashboard/dist/assets/BTAEErUY.js +1 -0
- package/dashboard/dist/assets/BTAEErUY.js.br +0 -0
- package/dashboard/dist/assets/BTAEErUY.js.gz +0 -0
- package/dashboard/dist/assets/BVShoyjA.js +1 -0
- package/dashboard/dist/assets/BVShoyjA.js.br +0 -0
- package/dashboard/dist/assets/BVShoyjA.js.gz +0 -0
- package/dashboard/dist/assets/{CC63EwFD.js → BgcAY5rE.js} +1 -1
- package/dashboard/dist/assets/BgcAY5rE.js.br +0 -0
- package/dashboard/dist/assets/BgcAY5rE.js.gz +0 -0
- package/dashboard/dist/assets/{rttbDbEx.js → C-PAoJF-.js} +1 -1
- package/dashboard/dist/assets/C-PAoJF-.js.br +0 -0
- package/dashboard/dist/assets/C-PAoJF-.js.gz +0 -0
- package/dashboard/dist/assets/C0nA-iUG.js +1 -0
- package/dashboard/dist/assets/C0nA-iUG.js.br +0 -0
- package/dashboard/dist/assets/C0nA-iUG.js.gz +0 -0
- package/dashboard/dist/assets/C6GO-FKy.js +1 -0
- package/dashboard/dist/assets/C6GO-FKy.js.br +0 -0
- package/dashboard/dist/assets/C6GO-FKy.js.gz +0 -0
- package/dashboard/dist/assets/CFwPph5U.js +1 -0
- package/dashboard/dist/assets/CFwPph5U.js.br +0 -0
- package/dashboard/dist/assets/CFwPph5U.js.gz +0 -0
- package/dashboard/dist/assets/CPjsbbgZ.js +212 -0
- package/dashboard/dist/assets/CPjsbbgZ.js.br +0 -0
- package/dashboard/dist/assets/CPjsbbgZ.js.gz +0 -0
- package/dashboard/dist/assets/{IUexzymk.js → CSr2ZnTV.js} +1 -1
- package/dashboard/dist/assets/CSr2ZnTV.js.br +0 -0
- package/dashboard/dist/assets/CSr2ZnTV.js.gz +0 -0
- package/dashboard/dist/assets/CgQDT6yL.js +1 -0
- package/dashboard/dist/assets/CgQDT6yL.js.br +0 -0
- package/dashboard/dist/assets/CgQDT6yL.js.gz +0 -0
- package/dashboard/dist/assets/{B6wPWJ35.js → CnitK1MX.js} +1 -1
- package/dashboard/dist/assets/CnitK1MX.js.br +0 -0
- package/dashboard/dist/assets/CnitK1MX.js.gz +0 -0
- package/dashboard/dist/assets/{CgaottFX.js → D7DHFX0D.js} +1 -1
- package/dashboard/dist/assets/D7DHFX0D.js.br +0 -0
- package/dashboard/dist/assets/D7DHFX0D.js.gz +0 -0
- package/dashboard/dist/assets/DEip7uko.js +1 -0
- package/dashboard/dist/assets/DEip7uko.js.br +0 -0
- package/dashboard/dist/assets/DEip7uko.js.gz +0 -0
- package/dashboard/dist/assets/DHUSLc01.css +1 -0
- package/dashboard/dist/assets/DHUSLc01.css.br +0 -0
- package/dashboard/dist/assets/DHUSLc01.css.gz +0 -0
- package/dashboard/dist/assets/{B5zYRHc3.js → DOFL9l8s.js} +1 -1
- package/dashboard/dist/assets/DOFL9l8s.js.br +0 -0
- package/dashboard/dist/assets/DOFL9l8s.js.gz +0 -0
- package/dashboard/dist/assets/{8dksYiq4.js → DpuQm1oF.js} +1 -1
- package/dashboard/dist/assets/DpuQm1oF.js.br +0 -0
- package/dashboard/dist/assets/DpuQm1oF.js.gz +0 -0
- package/dashboard/dist/assets/{D8JNX8kq.js → DxKG5zy8.js} +2 -2
- package/dashboard/dist/assets/DxKG5zy8.js.br +0 -0
- package/dashboard/dist/assets/DxKG5zy8.js.gz +0 -0
- package/dashboard/dist/assets/tcEHYcbW.js +1 -0
- package/dashboard/dist/assets/tcEHYcbW.js.br +0 -0
- package/dashboard/dist/assets/tcEHYcbW.js.gz +0 -0
- package/dashboard/dist/index.html +2 -2
- package/dashboard/dist/index.html.br +0 -0
- package/dashboard/dist/index.html.gz +0 -0
- package/dist/http/helpers/auto-continue-engine.js +29 -11
- package/dist/http/helpers/autopilot-slice-utils.js +7 -10
- package/dist/http/helpers/openclaw-cli.js +2 -2
- package/dist/http/index.js +161 -4
- package/dist/http/routes/live-legacy.d.ts +2 -0
- package/dist/http/routes/live-legacy.js +186 -3
- package/dist/http/routes/live-misc.d.ts +1 -0
- package/dist/http/routes/live-misc.js +76 -0
- package/dist/http/routes/mission-control-read.d.ts +4 -0
- package/dist/http/routes/mission-control-read.js +114 -63
- package/dist/http/routes/usage.js +15 -3
- package/dist/json-utils.js +5 -1
- package/dist/openclaw-settings.js +3 -2
- package/dist/sync/outbox-replay.js +17 -8
- package/package.json +6 -1
- package/dashboard/dist/assets/6mILZQ2a.js +0 -1
- package/dashboard/dist/assets/6mILZQ2a.js.br +0 -0
- package/dashboard/dist/assets/6mILZQ2a.js.gz +0 -0
- package/dashboard/dist/assets/8dksYiq4.js.br +0 -0
- package/dashboard/dist/assets/8dksYiq4.js.gz +0 -0
- package/dashboard/dist/assets/B5zYRHc3.js.br +0 -0
- package/dashboard/dist/assets/B5zYRHc3.js.gz +0 -0
- package/dashboard/dist/assets/B6wPWJ35.js.br +0 -0
- package/dashboard/dist/assets/B6wPWJ35.js.gz +0 -0
- package/dashboard/dist/assets/BWEwjt1W.js +0 -1
- package/dashboard/dist/assets/BWEwjt1W.js.br +0 -0
- package/dashboard/dist/assets/BWEwjt1W.js.gz +0 -0
- package/dashboard/dist/assets/BzRbDCAD.css +0 -1
- package/dashboard/dist/assets/BzRbDCAD.css.br +0 -0
- package/dashboard/dist/assets/BzRbDCAD.css.gz +0 -0
- package/dashboard/dist/assets/C8uM3AX8.js +0 -1
- package/dashboard/dist/assets/C8uM3AX8.js.br +0 -0
- package/dashboard/dist/assets/C8uM3AX8.js.gz +0 -0
- package/dashboard/dist/assets/C9jy61eu.js +0 -212
- package/dashboard/dist/assets/C9jy61eu.js.br +0 -0
- package/dashboard/dist/assets/C9jy61eu.js.gz +0 -0
- package/dashboard/dist/assets/CC63EwFD.js.br +0 -0
- package/dashboard/dist/assets/CC63EwFD.js.gz +0 -0
- package/dashboard/dist/assets/CZaT3ob_.js.br +0 -0
- package/dashboard/dist/assets/CZaT3ob_.js.gz +0 -0
- package/dashboard/dist/assets/CgaottFX.js.br +0 -0
- package/dashboard/dist/assets/CgaottFX.js.gz +0 -0
- package/dashboard/dist/assets/CzCxAZlW.js +0 -1
- package/dashboard/dist/assets/CzCxAZlW.js.br +0 -0
- package/dashboard/dist/assets/CzCxAZlW.js.gz +0 -0
- package/dashboard/dist/assets/D3iMTYEj.js +0 -1
- package/dashboard/dist/assets/D3iMTYEj.js.br +0 -0
- package/dashboard/dist/assets/D3iMTYEj.js.gz +0 -0
- package/dashboard/dist/assets/D8JNX8kq.js.br +0 -0
- package/dashboard/dist/assets/D8JNX8kq.js.gz +0 -0
- package/dashboard/dist/assets/DnA8dpj6.js +0 -1
- package/dashboard/dist/assets/DnA8dpj6.js.br +0 -0
- package/dashboard/dist/assets/DnA8dpj6.js.gz +0 -0
- package/dashboard/dist/assets/IUexzymk.js.br +0 -0
- package/dashboard/dist/assets/IUexzymk.js.gz +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js +0 -1
- package/dashboard/dist/assets/ic2FaMnh.js.br +0 -0
- package/dashboard/dist/assets/ic2FaMnh.js.gz +0 -0
- package/dashboard/dist/assets/rttbDbEx.js.br +0 -0
- package/dashboard/dist/assets/rttbDbEx.js.gz +0 -0
package/dist/http/index.js
CHANGED
|
@@ -18,10 +18,12 @@
|
|
|
18
18
|
* /orgx/api/runs/:id/actions/:action → run control action
|
|
19
19
|
*/
|
|
20
20
|
import { readFileSync, existsSync, readdirSync, statSync, openSync, readSync, closeSync, } from "node:fs";
|
|
21
|
+
import { execFile } from "node:child_process";
|
|
21
22
|
import { homedir } from "node:os";
|
|
22
23
|
import { join, dirname, extname, normalize, resolve, relative, sep } from "node:path";
|
|
23
24
|
import { fileURLToPath } from "node:url";
|
|
24
25
|
import { randomUUID } from "node:crypto";
|
|
26
|
+
import { promisify } from "node:util";
|
|
25
27
|
import { readNextUpQueuePins, removeNextUpQueuePin, setNextUpQueuePinOrder, suppressNextUpQueueItem, upsertNextUpQueuePin, } from "../next-up-queue-store.js";
|
|
26
28
|
import { formatStatus, formatAgents, formatActivity, formatInitiatives, getOnboardingState, } from "../dashboard-api.js";
|
|
27
29
|
import { loadLocalOpenClawSnapshot, loadLocalTurnDetail, toLocalLiveActivity, toLocalLiveAgents, toLocalLiveInitiatives, toLocalSessionTree, } from "../local-openclaw.js";
|
|
@@ -1219,6 +1221,8 @@ const IMMUTABLE_FILE_CACHE = new Map();
|
|
|
1219
1221
|
const IMMUTABLE_FILE_CACHE_MAX = 128;
|
|
1220
1222
|
const FILE_PREVIEW_MAX_BYTES = 1_000_000;
|
|
1221
1223
|
const FILE_PREVIEW_MAX_DIR_ENTRIES = 300;
|
|
1224
|
+
const AUTOPILOT_LOGS_DIR = join(homedir(), ".config", "useorgx", "openclaw-plugin", "autopilot-logs");
|
|
1225
|
+
const execFileAsync = promisify(execFile);
|
|
1222
1226
|
function sendJson(res, status, data) {
|
|
1223
1227
|
const body = JSON.stringify(data);
|
|
1224
1228
|
res.writeHead(status, {
|
|
@@ -1270,6 +1274,50 @@ function resolveFilesystemOpenPath(rawPath) {
|
|
|
1270
1274
|
}
|
|
1271
1275
|
return resolve(process.cwd(), value);
|
|
1272
1276
|
}
|
|
1277
|
+
function resolveAutopilotLogCandidates(runId) {
|
|
1278
|
+
const sanitizedRunId = runId.trim().replaceAll(/[\\/]/g, "");
|
|
1279
|
+
if (!sanitizedRunId)
|
|
1280
|
+
return [];
|
|
1281
|
+
const directCandidates = [
|
|
1282
|
+
join(AUTOPILOT_LOGS_DIR, `${sanitizedRunId}.log`),
|
|
1283
|
+
join(AUTOPILOT_LOGS_DIR, `${sanitizedRunId}.output.json`),
|
|
1284
|
+
];
|
|
1285
|
+
if (!existsSync(AUTOPILOT_LOGS_DIR)) {
|
|
1286
|
+
return directCandidates;
|
|
1287
|
+
}
|
|
1288
|
+
const discovered = readdirSync(AUTOPILOT_LOGS_DIR)
|
|
1289
|
+
.filter((entry) => entry.startsWith(`${sanitizedRunId}.`))
|
|
1290
|
+
.map((entry) => join(AUTOPILOT_LOGS_DIR, entry))
|
|
1291
|
+
.filter((entry) => entry.endsWith(".log") || entry.endsWith(".output.json"));
|
|
1292
|
+
return dedupeStrings([...directCandidates, ...discovered]);
|
|
1293
|
+
}
|
|
1294
|
+
async function openPathInTerminal(pathname) {
|
|
1295
|
+
const resolvedPath = resolve(pathname);
|
|
1296
|
+
if (process.platform === "darwin") {
|
|
1297
|
+
const escapedPath = resolvedPath
|
|
1298
|
+
.replaceAll("\\", "\\\\")
|
|
1299
|
+
.replaceAll('"', '\\"');
|
|
1300
|
+
await execFileAsync("osascript", [
|
|
1301
|
+
"-e",
|
|
1302
|
+
'tell application "Terminal" to activate',
|
|
1303
|
+
"-e",
|
|
1304
|
+
`tell application "Terminal" to do script "tail -f \\"${escapedPath}\\""`,
|
|
1305
|
+
]);
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
if (process.platform === "win32") {
|
|
1309
|
+
await execFileAsync("cmd", ["/c", "start", "", resolvedPath]);
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
try {
|
|
1313
|
+
await execFileAsync("gnome-terminal", ["--", "tail", "-f", resolvedPath]);
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
catch {
|
|
1317
|
+
// Fallback to generic opener when no terminal app is available.
|
|
1318
|
+
}
|
|
1319
|
+
await execFileAsync("xdg-open", [resolvedPath]);
|
|
1320
|
+
}
|
|
1273
1321
|
function readFilePreview(pathname, totalBytes) {
|
|
1274
1322
|
if (totalBytes <= 0) {
|
|
1275
1323
|
return { previewBuffer: Buffer.alloc(0), truncated: false };
|
|
@@ -3328,6 +3376,7 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3328
3376
|
loadLocalOpenClawSnapshot,
|
|
3329
3377
|
toLocalLiveAgents,
|
|
3330
3378
|
toLocalLiveInitiatives,
|
|
3379
|
+
buildMissionControlGraph: (initiativeId) => buildMissionControlGraph(client, initiativeId),
|
|
3331
3380
|
localInitiativeStatusOverrides,
|
|
3332
3381
|
mapDecisionEntity,
|
|
3333
3382
|
sendJson,
|
|
@@ -3369,6 +3418,8 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3369
3418
|
readFilePreview,
|
|
3370
3419
|
filePreviewMaxBytes: FILE_PREVIEW_MAX_BYTES,
|
|
3371
3420
|
filePreviewMaxDirEntries: FILE_PREVIEW_MAX_DIR_ENTRIES,
|
|
3421
|
+
resolveAutopilotLogCandidates,
|
|
3422
|
+
openPathInTerminal,
|
|
3372
3423
|
securityHeaders: SECURITY_HEADERS,
|
|
3373
3424
|
corsHeaders: CORS_HEADERS,
|
|
3374
3425
|
config: {
|
|
@@ -3509,10 +3560,116 @@ export function createHttpHandler(config, client, getSnapshot, onboarding, diagn
|
|
|
3509
3560
|
}
|
|
3510
3561
|
return [];
|
|
3511
3562
|
},
|
|
3512
|
-
getBlockerEvents: () => {
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3563
|
+
getBlockerEvents: (workspaceId) => {
|
|
3564
|
+
const normalizedWorkspaceId = (workspaceId ?? "").trim();
|
|
3565
|
+
const scopedKeys = normalizedWorkspaceId
|
|
3566
|
+
? [
|
|
3567
|
+
`live-snapshot:${normalizedWorkspaceId}`,
|
|
3568
|
+
`live-snapshot-v2:${normalizedWorkspaceId}`,
|
|
3569
|
+
`dashboard-bundle:${normalizedWorkspaceId}`,
|
|
3570
|
+
]
|
|
3571
|
+
: [];
|
|
3572
|
+
const fallbackKeys = ["live-snapshot", "dashboard-bundle", "live-snapshot-v2"];
|
|
3573
|
+
const keys = [...scopedKeys, ...fallbackKeys];
|
|
3574
|
+
const seen = new Set();
|
|
3575
|
+
const mapped = [];
|
|
3576
|
+
const deriveFailureType = (eventNameRaw, actionTypeRaw) => {
|
|
3577
|
+
const eventName = (eventNameRaw ?? "").trim().toLowerCase();
|
|
3578
|
+
const actionType = (actionTypeRaw ?? "").trim().toLowerCase();
|
|
3579
|
+
const signature = `${eventName} ${actionType}`;
|
|
3580
|
+
if (!signature.trim())
|
|
3581
|
+
return null;
|
|
3582
|
+
if (signature.includes("status_updates_buffered"))
|
|
3583
|
+
return "status_updates_buffered";
|
|
3584
|
+
if (signature.includes("question_answer_failed"))
|
|
3585
|
+
return "question_answer_failed";
|
|
3586
|
+
if (signature.includes("spawn_guard_rate_limited"))
|
|
3587
|
+
return "spawn_guard_rate_limited";
|
|
3588
|
+
if (signature.includes("spawn_guard_blocked"))
|
|
3589
|
+
return "spawn_guard_blocked";
|
|
3590
|
+
if (signature.includes("mcp_handshake_failure") || signature.includes("mcp_handshake")) {
|
|
3591
|
+
return "mcp_handshake_failure";
|
|
3592
|
+
}
|
|
3593
|
+
if (signature.includes("worker_exit_no_output") ||
|
|
3594
|
+
signature.includes("worker_exit_without_structured_output") ||
|
|
3595
|
+
signature.includes("no_structured_output")) {
|
|
3596
|
+
return "worker_exit_no_output";
|
|
3597
|
+
}
|
|
3598
|
+
if (signature.includes("workspace_conflict"))
|
|
3599
|
+
return "workspace_conflict";
|
|
3600
|
+
if (signature.includes("budget_exhausted"))
|
|
3601
|
+
return "budget_exhausted";
|
|
3602
|
+
if (signature.includes("stale_blocked_workstream"))
|
|
3603
|
+
return "stale_blocked_workstream";
|
|
3604
|
+
return null;
|
|
3605
|
+
};
|
|
3606
|
+
try {
|
|
3607
|
+
for (const key of keys) {
|
|
3608
|
+
const cached = readSnapshotResponseCache(key);
|
|
3609
|
+
if (!cached || typeof cached !== "object")
|
|
3610
|
+
continue;
|
|
3611
|
+
const activityRaw = cached.activity;
|
|
3612
|
+
if (!Array.isArray(activityRaw))
|
|
3613
|
+
continue;
|
|
3614
|
+
for (const entry of activityRaw) {
|
|
3615
|
+
if (!entry || typeof entry !== "object")
|
|
3616
|
+
continue;
|
|
3617
|
+
const activityRecord = entry;
|
|
3618
|
+
const metadataRecord = activityRecord.metadata && typeof activityRecord.metadata === "object"
|
|
3619
|
+
? activityRecord.metadata
|
|
3620
|
+
: {};
|
|
3621
|
+
const failureType = deriveFailureType(pickString(metadataRecord, ["event", "event_name"]), pickString(metadataRecord, ["action_type", "actionType"]));
|
|
3622
|
+
if (!failureType)
|
|
3623
|
+
continue;
|
|
3624
|
+
const runId = pickString(metadataRecord, ["run_id", "source_run_id"]) ?? pickString(activityRecord, ["runId"]);
|
|
3625
|
+
const workstreamId = pickString(metadataRecord, ["workstream_id", "source_stream_id"]) ??
|
|
3626
|
+
pickString(activityRecord, ["workstreamId"]);
|
|
3627
|
+
const taskId = pickString(metadataRecord, ["task_id"]) ?? pickString(activityRecord, ["taskId"]);
|
|
3628
|
+
const initiativeId = pickString(metadataRecord, ["initiative_id"]) ??
|
|
3629
|
+
pickString(activityRecord, ["initiativeId"]);
|
|
3630
|
+
const timestamp = pickString(activityRecord, ["timestamp"]) ??
|
|
3631
|
+
pickString(metadataRecord, ["timestamp", "created_at"]);
|
|
3632
|
+
const dedupeId = pickString(activityRecord, ["id"]) ??
|
|
3633
|
+
`${failureType}:${initiativeId ?? "unknown"}:${workstreamId ?? "unknown"}:${runId ?? "unknown"}:${timestamp ?? "unknown"}`;
|
|
3634
|
+
if (seen.has(dedupeId))
|
|
3635
|
+
continue;
|
|
3636
|
+
seen.add(dedupeId);
|
|
3637
|
+
mapped.push({
|
|
3638
|
+
id: dedupeId,
|
|
3639
|
+
failureType,
|
|
3640
|
+
reason: pickString(metadataRecord, ["error", "reason", "message"]) ??
|
|
3641
|
+
pickString(activityRecord, ["description", "summary", "title"]),
|
|
3642
|
+
provider: pickString(metadataRecord, ["provider"]),
|
|
3643
|
+
initiativeId,
|
|
3644
|
+
initiativeTitle: pickString(metadataRecord, ["initiative_title"]) ??
|
|
3645
|
+
pickString(activityRecord, ["initiativeTitle"]),
|
|
3646
|
+
workstreamId,
|
|
3647
|
+
workstreamTitle: pickString(metadataRecord, ["workstream_title"]) ??
|
|
3648
|
+
pickString(activityRecord, ["workstreamTitle"]),
|
|
3649
|
+
taskId,
|
|
3650
|
+
taskTitle: pickString(metadataRecord, ["task_title"]) ?? pickString(activityRecord, ["taskTitle"]),
|
|
3651
|
+
agentId: pickString(metadataRecord, ["agent_id", "executor_agent_id"]) ??
|
|
3652
|
+
pickString(activityRecord, ["executorAgentId", "agentId"]),
|
|
3653
|
+
domain: pickString(metadataRecord, ["domain", "executor_domain"]),
|
|
3654
|
+
sourceSystem: pickString(metadataRecord, ["source_system", "source"]) ?? "openclaw",
|
|
3655
|
+
runId,
|
|
3656
|
+
logPath: pickString(metadataRecord, ["log_path"]),
|
|
3657
|
+
outputPath: pickString(metadataRecord, ["output_path"]),
|
|
3658
|
+
metadata: metadataRecord,
|
|
3659
|
+
timestamp: timestamp ?? undefined,
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
catch {
|
|
3665
|
+
// best effort
|
|
3666
|
+
}
|
|
3667
|
+
mapped.sort((a, b) => {
|
|
3668
|
+
const aTs = Date.parse(String(a.timestamp ?? "")) || 0;
|
|
3669
|
+
const bTs = Date.parse(String(b.timestamp ?? "")) || 0;
|
|
3670
|
+
return bTs - aTs;
|
|
3671
|
+
});
|
|
3672
|
+
return mapped;
|
|
3516
3673
|
},
|
|
3517
3674
|
resolveDecisionAction: async (decisionId, action, note, optionId) => {
|
|
3518
3675
|
try {
|
|
@@ -101,6 +101,8 @@ type RegisterLiveLegacyRoutesDeps<TRes extends RouteResLike> = {
|
|
|
101
101
|
};
|
|
102
102
|
filePreviewMaxBytes: number;
|
|
103
103
|
filePreviewMaxDirEntries: number;
|
|
104
|
+
resolveAutopilotLogCandidates?: (runId: string) => string[];
|
|
105
|
+
openPathInTerminal?: (path: string) => Promise<void>;
|
|
104
106
|
securityHeaders: Record<string, string>;
|
|
105
107
|
corsHeaders: Record<string, string>;
|
|
106
108
|
config: {
|
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
export function registerLiveLegacyRoutes(router, deps) {
|
|
2
|
+
function toContextBundle(value) {
|
|
3
|
+
return {
|
|
4
|
+
agents: value.agents ?? {},
|
|
5
|
+
runs: value.runs ?? {},
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function parseWorkspaceScope(query) {
|
|
9
|
+
const candidates = [
|
|
10
|
+
query.get("workspace_id"),
|
|
11
|
+
query.get("workspaceId"),
|
|
12
|
+
query.get("command_center_id"),
|
|
13
|
+
query.get("commandCenterId"),
|
|
14
|
+
query.get("center"),
|
|
15
|
+
query.get("project_id"),
|
|
16
|
+
query.get("projectId"),
|
|
17
|
+
];
|
|
18
|
+
for (const candidate of candidates) {
|
|
19
|
+
if (typeof candidate !== "string")
|
|
20
|
+
continue;
|
|
21
|
+
const normalized = candidate.trim();
|
|
22
|
+
if (!normalized || normalized.toLowerCase() === "all")
|
|
23
|
+
continue;
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
2
28
|
const sendDeprecated = (res, endpoint, replacement) => {
|
|
3
29
|
deps.sendJson(res, 410, {
|
|
4
30
|
error: `${endpoint} is deprecated`,
|
|
@@ -14,9 +40,69 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
14
40
|
router.add("GET", "live/sessions", async ({ query, res }) => renderLiveSessions(query, res), "Legacy live sessions endpoint");
|
|
15
41
|
router.add("HEAD", "live/sessions", async ({ query, res }) => renderLiveSessions(query, res), "Legacy live sessions endpoint (HEAD)");
|
|
16
42
|
async function renderLiveActivityPage(query, res) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
43
|
+
const run = query.get("run");
|
|
44
|
+
const since = query.get("since");
|
|
45
|
+
const until = query.get("until");
|
|
46
|
+
const cursor = query.get("cursor");
|
|
47
|
+
const projectId = parseWorkspaceScope(query);
|
|
48
|
+
const limitRaw = query.get("limit") ? Number(query.get("limit")) : undefined;
|
|
49
|
+
const limit = Number.isFinite(limitRaw)
|
|
50
|
+
? Math.max(1, Math.floor(Number(limitRaw)))
|
|
51
|
+
: 200;
|
|
52
|
+
let page = deps.listActivityPage({
|
|
53
|
+
limit,
|
|
54
|
+
runId: run,
|
|
55
|
+
since,
|
|
56
|
+
until,
|
|
57
|
+
cursor,
|
|
58
|
+
});
|
|
59
|
+
{
|
|
60
|
+
const ctx = toContextBundle(deps.readAgentContexts());
|
|
61
|
+
page = {
|
|
62
|
+
...page,
|
|
63
|
+
activities: deps.applyAgentContextsToActivity(page.activities, ctx),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const warmKey = `${projectId ?? ""}::${run ?? ""}::${since ?? ""}::${until ?? ""}`;
|
|
67
|
+
const lastWarmAt = deps.activityWarmByKey.get(warmKey) ?? 0;
|
|
68
|
+
const shouldWarm = Date.now() - lastWarmAt > deps.activityWarmThrottleMs &&
|
|
69
|
+
(cursor === null || cursor === "" || page.activities.length < limit);
|
|
70
|
+
if (shouldWarm) {
|
|
71
|
+
deps.activityWarmByKey.set(warmKey, Date.now());
|
|
72
|
+
try {
|
|
73
|
+
const warmLimit = Math.max(800, Math.min(6_000, limit * 10));
|
|
74
|
+
const data = await deps.getLiveActivity({
|
|
75
|
+
run,
|
|
76
|
+
since,
|
|
77
|
+
projectId,
|
|
78
|
+
limit: warmLimit,
|
|
79
|
+
});
|
|
80
|
+
const remote = Array.isArray(data.activities) ? data.activities : [];
|
|
81
|
+
{
|
|
82
|
+
const ctx = toContextBundle(deps.readAgentContexts());
|
|
83
|
+
const withContexts = deps.applyAgentContextsToActivity(remote, ctx);
|
|
84
|
+
deps.appendActivityItems(withContexts);
|
|
85
|
+
}
|
|
86
|
+
page = deps.listActivityPage({
|
|
87
|
+
limit,
|
|
88
|
+
runId: run,
|
|
89
|
+
since,
|
|
90
|
+
until,
|
|
91
|
+
cursor,
|
|
92
|
+
});
|
|
93
|
+
{
|
|
94
|
+
const ctx = toContextBundle(deps.readAgentContexts());
|
|
95
|
+
page = {
|
|
96
|
+
...page,
|
|
97
|
+
activities: deps.applyAgentContextsToActivity(page.activities, ctx),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// best effort
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
deps.sendJson(res, 200, page);
|
|
20
106
|
}
|
|
21
107
|
router.add("GET", "live/activity/page", async ({ query, res }) => renderLiveActivityPage(query, res), "Paginated live activity");
|
|
22
108
|
router.add("HEAD", "live/activity/page", async ({ query, res }) => renderLiveActivityPage(query, res), "Paginated live activity (HEAD)");
|
|
@@ -101,6 +187,64 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
101
187
|
router.add("*", "live/filesystem/open", ({ res }) => {
|
|
102
188
|
deps.sendJson(res, 405, { error: "Use GET /orgx/api/live/filesystem/open?path=..." });
|
|
103
189
|
}, "Reject unsupported methods for live/filesystem/open");
|
|
190
|
+
router.add("POST", "live/terminal/open", async ({ body, res }) => {
|
|
191
|
+
const payload = body && typeof body === "object" && !Array.isArray(body)
|
|
192
|
+
? body
|
|
193
|
+
: {};
|
|
194
|
+
const runId = typeof payload.runId === "string" ? payload.runId.trim() : "";
|
|
195
|
+
const rawPath = typeof payload.path === "string" ? payload.path.trim() : "";
|
|
196
|
+
if (!runId && !rawPath) {
|
|
197
|
+
deps.sendJson(res, 400, {
|
|
198
|
+
ok: false,
|
|
199
|
+
error: "runId or path is required",
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
let resolvedPath = "";
|
|
204
|
+
if (rawPath) {
|
|
205
|
+
resolvedPath = deps.resolveFilesystemOpenPath(rawPath);
|
|
206
|
+
}
|
|
207
|
+
else if (runId) {
|
|
208
|
+
const candidates = deps.resolveAutopilotLogCandidates
|
|
209
|
+
? deps.resolveAutopilotLogCandidates(runId)
|
|
210
|
+
: [];
|
|
211
|
+
resolvedPath =
|
|
212
|
+
candidates.find((candidate) => deps.existsSync(candidate)) ?? "";
|
|
213
|
+
}
|
|
214
|
+
if (!resolvedPath || !deps.existsSync(resolvedPath)) {
|
|
215
|
+
deps.sendJson(res, 404, {
|
|
216
|
+
ok: false,
|
|
217
|
+
error: "Terminal target not found",
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (!deps.openPathInTerminal) {
|
|
222
|
+
deps.sendJson(res, 501, {
|
|
223
|
+
ok: false,
|
|
224
|
+
error: "Terminal open is unavailable in this runtime.",
|
|
225
|
+
});
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
await deps.openPathInTerminal(resolvedPath);
|
|
230
|
+
deps.sendJson(res, 200, {
|
|
231
|
+
ok: true,
|
|
232
|
+
path: resolvedPath,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
deps.sendJson(res, 500, {
|
|
237
|
+
ok: false,
|
|
238
|
+
error: deps.safeErrorMessage(err),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}, "Open run logs in local terminal");
|
|
242
|
+
router.add("*", "live/terminal/open", ({ res }) => {
|
|
243
|
+
deps.sendJson(res, 405, {
|
|
244
|
+
ok: false,
|
|
245
|
+
error: "Use POST /orgx/api/live/terminal/open",
|
|
246
|
+
});
|
|
247
|
+
}, "Reject unsupported methods for live/terminal/open");
|
|
104
248
|
async function renderLiveStream(query, req, res) {
|
|
105
249
|
sendDeprecated(res, "/orgx/api/live/stream", "/orgx/api/live/snapshot-v2");
|
|
106
250
|
void query;
|
|
@@ -109,4 +253,43 @@ export function registerLiveLegacyRoutes(router, deps) {
|
|
|
109
253
|
}
|
|
110
254
|
router.add("GET", "live/stream", async ({ query, req, res }) => renderLiveStream(query, req, res), "Proxy live SSE stream");
|
|
111
255
|
router.add("HEAD", "live/stream", async ({ query, req, res }) => renderLiveStream(query, req, res), "Proxy live SSE stream (HEAD)");
|
|
256
|
+
router.add("POST", "live/terminal/open", async ({ body, res }) => {
|
|
257
|
+
try {
|
|
258
|
+
const payload = (typeof body === "string" ? JSON.parse(body) : body);
|
|
259
|
+
const runId = payload?.runId;
|
|
260
|
+
if (!runId) {
|
|
261
|
+
deps.sendJson(res, 400, { error: "runId is required" });
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const os = await import("os");
|
|
265
|
+
const cp = await import("child_process");
|
|
266
|
+
const path = await import("path");
|
|
267
|
+
const homedir = os.homedir();
|
|
268
|
+
const logPath = path.join(homedir, ".config", "useorgx", "openclaw-plugin", "autopilot-logs", `${runId}.log`);
|
|
269
|
+
if (!deps.existsSync(logPath)) {
|
|
270
|
+
deps.sendJson(res, 404, { error: `Log file not found: ${logPath}` });
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const shellPath = logPath.replaceAll("'", "'\\''");
|
|
274
|
+
let command = "";
|
|
275
|
+
if (os.platform() === "darwin") {
|
|
276
|
+
command = `osascript -e 'tell app "Terminal" to do script "tail -f \\"${shellPath}\\""'`;
|
|
277
|
+
}
|
|
278
|
+
else if (os.platform() === "win32") {
|
|
279
|
+
command = `start cmd.exe /k "tail -f \\"${shellPath}\\""`;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
command = `gnome-terminal -- bash -c "tail -f \\"${shellPath}\\"; exec bash"`;
|
|
283
|
+
}
|
|
284
|
+
cp.exec(command, (error) => {
|
|
285
|
+
if (error) {
|
|
286
|
+
console.error("Failed to open terminal:", error);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
deps.sendJson(res, 200, { success: true });
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
deps.sendJson(res, 500, { error: deps.safeErrorMessage(err) });
|
|
293
|
+
}
|
|
294
|
+
}, "Open run session in native terminal");
|
|
112
295
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveWorkspaceScope, workspaceScopeFromHeaders, } from "../helpers/workspace-scope.js";
|
|
2
|
+
import { summarizeTaskStatuses } from "../../reporting/rollups.js";
|
|
2
3
|
function asRecord(input) {
|
|
3
4
|
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
4
5
|
return null;
|
|
@@ -423,4 +424,79 @@ export function registerLiveMiscRoutes(router, deps) {
|
|
|
423
424
|
}
|
|
424
425
|
router.add("GET", "handoffs", async ({ res }) => renderHandoffs(res), "Get handoffs");
|
|
425
426
|
router.add("HEAD", "handoffs", async ({ res }) => renderHandoffs(res), "Get handoffs (HEAD)");
|
|
427
|
+
router.add("GET", "live/initiatives/summary", async ({ query, res, req }) => {
|
|
428
|
+
try {
|
|
429
|
+
const scope = resolveWorkspaceScope(query, workspaceScopeFromHeaders(req?.headers), { allowProjectScope: false });
|
|
430
|
+
if (scope.error) {
|
|
431
|
+
deps.sendJson(res, 400, { error: scope.error });
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const workspaceId = scope.workspaceId;
|
|
435
|
+
const projectInitiatives = await resolveProjectInitiativeSet(workspaceId);
|
|
436
|
+
// Load initiatives
|
|
437
|
+
const local = deps.toLocalLiveInitiatives(await deps.loadLocalOpenClawSnapshot(240));
|
|
438
|
+
let initiatives = local.initiatives;
|
|
439
|
+
if (projectInitiatives) {
|
|
440
|
+
initiatives = initiatives.filter((item) => projectInitiatives.has(item.id));
|
|
441
|
+
}
|
|
442
|
+
const perInitiative = [];
|
|
443
|
+
let totalTasks = 0;
|
|
444
|
+
let doneTasks = 0;
|
|
445
|
+
let blockedCount = 0;
|
|
446
|
+
let activeCount = 0;
|
|
447
|
+
let totalActiveAgents = 0;
|
|
448
|
+
for (const initiative of initiatives) {
|
|
449
|
+
totalActiveAgents += initiative.activeAgents;
|
|
450
|
+
// Try to get task statuses from graph
|
|
451
|
+
let taskStatuses = [];
|
|
452
|
+
if (deps.buildMissionControlGraph) {
|
|
453
|
+
try {
|
|
454
|
+
const graphRaw = await deps.buildMissionControlGraph(initiative.id);
|
|
455
|
+
const graph = asRecord(graphRaw);
|
|
456
|
+
const nodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
457
|
+
for (const nodeEntry of nodes) {
|
|
458
|
+
const node = asRecord(nodeEntry);
|
|
459
|
+
if (!node)
|
|
460
|
+
continue;
|
|
461
|
+
const type = pickStringFromRecord(node, ["type"]);
|
|
462
|
+
if (type !== "task")
|
|
463
|
+
continue;
|
|
464
|
+
const status = pickStringFromRecord(node, ["status"]) ?? "todo";
|
|
465
|
+
taskStatuses.push(status);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
// Graph unavailable — fall back to empty counts
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const counts = summarizeTaskStatuses(taskStatuses);
|
|
473
|
+
const progressPct = counts.total > 0 ? Math.round((counts.done / counts.total) * 100) : 0;
|
|
474
|
+
perInitiative.push({
|
|
475
|
+
id: initiative.id,
|
|
476
|
+
taskCounts: counts,
|
|
477
|
+
progressPct,
|
|
478
|
+
});
|
|
479
|
+
totalTasks += counts.total;
|
|
480
|
+
doneTasks += counts.done;
|
|
481
|
+
blockedCount += counts.blocked;
|
|
482
|
+
activeCount += counts.active;
|
|
483
|
+
}
|
|
484
|
+
const completionPercent = totalTasks > 0 ? Math.round((doneTasks / totalTasks) * 100) : 0;
|
|
485
|
+
deps.sendJson(res, 200, {
|
|
486
|
+
initiatives: perInitiative,
|
|
487
|
+
aggregate: {
|
|
488
|
+
totalTasks,
|
|
489
|
+
doneTasks,
|
|
490
|
+
blockedCount,
|
|
491
|
+
activeAgents: totalActiveAgents,
|
|
492
|
+
completionPercent,
|
|
493
|
+
},
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
deps.sendJson(res, 500, {
|
|
498
|
+
error: deps.safeErrorMessage(err),
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}, "Get live initiatives summary");
|
|
426
502
|
}
|
|
@@ -41,6 +41,10 @@ type NextUpQueueItem = {
|
|
|
41
41
|
pinnedRank?: number | null;
|
|
42
42
|
compositeScore?: number;
|
|
43
43
|
scoringTier?: "urgent" | "ready" | "waiting" | "deferred";
|
|
44
|
+
objectiveScore?: number;
|
|
45
|
+
roiPerToken?: number;
|
|
46
|
+
expectedTokens?: number;
|
|
47
|
+
expectedValueUsd?: number;
|
|
44
48
|
updatedAt?: string | null;
|
|
45
49
|
};
|
|
46
50
|
type SliceRunnerAgent = {
|