@h-rig/run-worker 0.0.6-alpha.135 → 0.0.6-alpha.137
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/src/autohost.d.ts +2 -3
- package/dist/src/autohost.js +157 -40
- package/dist/src/extension.js +157 -40
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +164 -40
- package/dist/src/panel-plugin.d.ts +20 -0
- package/dist/src/panel-plugin.js +94 -0
- package/package.json +8 -6
package/dist/src/autohost.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext, AgentEndEvent } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
-
import { type RigRunTimelineEntry, type RunInboxResolutionSentinel, type RunSessionCustomEntry } from "@rig/contracts";
|
|
3
|
-
import { projectRunFromSession } from "@rig/runtime/control-plane/run-session-projection";
|
|
2
|
+
import { type RigRunTimelineEntry, type RunInboxResolutionSentinel, type RunJournalProjection, type RunSessionCustomEntry } from "@rig/contracts";
|
|
4
3
|
import { updateRunTaskSourceLifecycle } from "@rig/runtime/control-plane/tasks/source-lifecycle";
|
|
5
4
|
import { type RegistryRunProjection } from "@rig/relay-registry";
|
|
6
5
|
export interface RunProcessWorkerHooks {
|
|
@@ -23,7 +22,7 @@ export declare function reflectStoppedRunTaskSource(input: {
|
|
|
23
22
|
}): Promise<boolean>;
|
|
24
23
|
export declare function registryRunProjection(input: {
|
|
25
24
|
readonly runId: string;
|
|
26
|
-
readonly folded:
|
|
25
|
+
readonly folded: RunJournalProjection;
|
|
27
26
|
readonly entries: readonly RunSessionCustomEntry[];
|
|
28
27
|
readonly title: string;
|
|
29
28
|
readonly joinLink?: string | null;
|
package/dist/src/autohost.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/run-worker/src/autohost.ts
|
|
3
|
-
import {
|
|
3
|
+
import { runPipelineCloseout } from "@rig/bundle-default-lifecycle/pipeline-closeout";
|
|
4
|
+
import { adoptPlacementKernel } from "@rig/composition/kernel-entrypoint";
|
|
4
5
|
import { latestTimelineEntriesFromCustomEntries, parseInboxResolutionSentinel, parsePauseSentinel, parseResumeSentinel, parseStopSentinel, sessionIdFromSessionFile } from "@rig/contracts";
|
|
5
6
|
import { Duration, Effect, Fiber, Stream } from "effect";
|
|
6
7
|
import { createEnvCloseoutRunners } from "@rig/runtime/control-plane/native/closeout-runners";
|
|
@@ -12,7 +13,6 @@ import { resolveOwnerNamespaceKey } from "@rig/runtime/control-plane/remote-conf
|
|
|
12
13
|
import { resolveRigIdentity } from "@rig/runtime/control-plane/identity";
|
|
13
14
|
import { connectWorkerProjection, createRegistryClient } from "@rig/relay-registry";
|
|
14
15
|
import { coerceRegistryStatus } from "@rig/relay-registry/schema";
|
|
15
|
-
import { bootDefaultKernelIntoProcess } from "@rig/kernel/boot-default";
|
|
16
16
|
|
|
17
17
|
// packages/run-worker/src/journal.ts
|
|
18
18
|
import { createJournalSessionProvider } from "@rig/kernel/journal-session-provider";
|
|
@@ -108,6 +108,91 @@ function startRunProcessStallMonitor(opts) {
|
|
|
108
108
|
return () => clearInterval(timer);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// packages/run-worker/src/panel-plugin.ts
|
|
112
|
+
import { createProjectPluginHost, projectPluginResolutionWarningMessages } from "@rig/composition/project-plugins";
|
|
113
|
+
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels";
|
|
114
|
+
var RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
|
|
115
|
+
var RIG_CAPABILITY_PANEL_SLOT = "capability";
|
|
116
|
+
var RIG_SUPERVISOR_PANEL_ID = "supervisor";
|
|
117
|
+
var PANEL_PRODUCER_TIMEOUT_MS = 2000;
|
|
118
|
+
var RUN_SUPERVISOR_PANEL_REGISTRATION = {
|
|
119
|
+
id: RIG_SUPERVISOR_PANEL_ID,
|
|
120
|
+
slot: RIG_CAPABILITY_PANEL_SLOT,
|
|
121
|
+
title: "Supervisor",
|
|
122
|
+
capabilityId: "run.supervisor",
|
|
123
|
+
description: "Live run status, closeout progress, and operator stop control."
|
|
124
|
+
};
|
|
125
|
+
function buildSupervisorPanelPayload(context) {
|
|
126
|
+
const status = context.folded.status ?? "unknown";
|
|
127
|
+
const taskId = context.folded.record.taskId ?? context.taskIdAtStart;
|
|
128
|
+
const operatorActive = status === "running" || status === "validating" || status === "closing-out" || status === "needs-attention";
|
|
129
|
+
return {
|
|
130
|
+
status,
|
|
131
|
+
currentTask: taskId ? { id: taskId, title: context.runDisplayTitle } : null,
|
|
132
|
+
processed: context.folded.closeoutPhases.length,
|
|
133
|
+
succeeded: context.folded.closeoutPhases.filter((phase) => phase.outcome === "completed").length,
|
|
134
|
+
failed: context.folded.closeoutPhases.filter((phase) => phase.outcome === "failed").length,
|
|
135
|
+
skipped: 0,
|
|
136
|
+
plannedOrder: taskId ? [{ id: taskId, title: context.runDisplayTitle, status }] : [],
|
|
137
|
+
idleReason: operatorActive ? null : status,
|
|
138
|
+
stopActionId: operatorActive ? RIG_RUN_STOP_PANEL_ACTION : null,
|
|
139
|
+
closures: []
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
var RUN_SUPERVISOR_PANEL_PRODUCER = {
|
|
143
|
+
...RUN_SUPERVISOR_PANEL_REGISTRATION,
|
|
144
|
+
produce(context) {
|
|
145
|
+
return buildSupervisorPanelPayload(context);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
async function produceWorkerPanelPayload(producer, context) {
|
|
149
|
+
if (!producer.produce)
|
|
150
|
+
return;
|
|
151
|
+
let timeout;
|
|
152
|
+
try {
|
|
153
|
+
return await Promise.race([
|
|
154
|
+
Promise.resolve(producer.produce(context)),
|
|
155
|
+
new Promise((resolve2) => {
|
|
156
|
+
timeout = setTimeout(() => resolve2(undefined), PANEL_PRODUCER_TIMEOUT_MS);
|
|
157
|
+
})
|
|
158
|
+
]);
|
|
159
|
+
} finally {
|
|
160
|
+
clearTimeout(timeout);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
var RUN_WORKER_PANEL_PLUGIN = {
|
|
164
|
+
name: "@rig/run-worker-panels",
|
|
165
|
+
version: "0.0.0-alpha.1",
|
|
166
|
+
contributes: {
|
|
167
|
+
panels: [RUN_SUPERVISOR_PANEL_REGISTRATION]
|
|
168
|
+
},
|
|
169
|
+
__runtime: {
|
|
170
|
+
panels: [RUN_SUPERVISOR_PANEL_PRODUCER]
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
async function loadWorkerPanelRegistry(projectRoot) {
|
|
174
|
+
try {
|
|
175
|
+
const { host, resolved } = await createProjectPluginHost(projectRoot, {
|
|
176
|
+
mode: "compatibility-default-injection",
|
|
177
|
+
surfaceName: "run-worker-panel-registry",
|
|
178
|
+
surfaceExtras: [RUN_WORKER_PANEL_PLUGIN]
|
|
179
|
+
});
|
|
180
|
+
for (const message of projectPluginResolutionWarningMessages(resolved)) {
|
|
181
|
+
console.warn(`[rig-run] ${message}`);
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
registrations: host.listPanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT),
|
|
185
|
+
producers: host.listExecutablePanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT)
|
|
186
|
+
};
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(`[rig-run] panel-registry-build-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
189
|
+
return {
|
|
190
|
+
registrations: [RUN_SUPERVISOR_PANEL_REGISTRATION],
|
|
191
|
+
producers: [RUN_SUPERVISOR_PANEL_PRODUCER]
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
111
196
|
// packages/run-worker/src/utils.ts
|
|
112
197
|
import { createWorkflowStatusChanged, RIG_WORKFLOW_STATUS_CHANGED } from "@rig/contracts";
|
|
113
198
|
import { resolveRegistryBaseUrl, resolveRelayUrl } from "@rig/runtime/control-plane/remote-config";
|
|
@@ -130,8 +215,6 @@ function rigRelayUrl() {
|
|
|
130
215
|
}
|
|
131
216
|
|
|
132
217
|
// packages/run-worker/src/autohost.ts
|
|
133
|
-
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels";
|
|
134
|
-
var RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
|
|
135
218
|
function syntheticRegistryOwner(namespaceKey) {
|
|
136
219
|
const githubUserId = namespaceKey.startsWith("gh:") ? namespaceKey.slice(3) : namespaceKey;
|
|
137
220
|
return {
|
|
@@ -228,7 +311,16 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
228
311
|
const envRunId = process.env.RIG_RUN_ID?.trim();
|
|
229
312
|
if (!envRunId)
|
|
230
313
|
throw new Error("RIG_RUN_ID is required when RIG_RUN_PROCESS=1");
|
|
231
|
-
const
|
|
314
|
+
const sessionManagerRunId = typeof ctx.sessionManager.getSessionId === "function" ? ctx.sessionManager.getSessionId().trim() : "";
|
|
315
|
+
const runId = sessionIdFromSessionFile(ctx.sessionManager.getSessionFile()) || sessionManagerRunId;
|
|
316
|
+
if (!runId)
|
|
317
|
+
throw new Error("OMP session id is required when RIG_RUN_PROCESS=1; RIG_RUN_ID is only a dispatch handle.");
|
|
318
|
+
const projectRoot = process.env.PROJECT_RIG_ROOT ?? process.cwd();
|
|
319
|
+
const journal = await createRunJournal(ctx.sessionManager, runId);
|
|
320
|
+
await adoptPlacementKernel(projectRoot, {
|
|
321
|
+
entrypoint: "run-worker",
|
|
322
|
+
...journal ? { journal: journal.kernel } : {}
|
|
323
|
+
});
|
|
232
324
|
if (!ctx.hasUI)
|
|
233
325
|
return null;
|
|
234
326
|
const collab = ctx.collab;
|
|
@@ -236,12 +328,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
236
328
|
if (!startHost)
|
|
237
329
|
throw new Error("OMP collab host facade is unavailable for Rig run process.");
|
|
238
330
|
const identity = resolveRigIdentity(ctx);
|
|
239
|
-
const
|
|
240
|
-
await bootDefaultKernelIntoProcess({
|
|
241
|
-
entrypoint: "run-worker",
|
|
242
|
-
...journal ? { journal: journal.kernel } : {}
|
|
243
|
-
});
|
|
244
|
-
const projectRoot = process.env.PROJECT_RIG_ROOT ?? process.cwd();
|
|
331
|
+
const panelRegistry = await loadWorkerPanelRegistry(projectRoot);
|
|
245
332
|
let runProjection = projectRunFromSession(customEntries(ctx.sessionManager.getBranch()), runId);
|
|
246
333
|
const taskIdAtStart = process.env.RIG_TASK_ID?.trim() || runProjection.record.taskId;
|
|
247
334
|
const runDisplayTitle = (() => {
|
|
@@ -301,30 +388,43 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
301
388
|
});
|
|
302
389
|
};
|
|
303
390
|
let lastRigPanelsSnapshotJson = "";
|
|
304
|
-
const publishRigPanelsSnapshot = () => {
|
|
391
|
+
const publishRigPanelsSnapshot = async () => {
|
|
305
392
|
const appendCustomMessageEntry = ctx.sessionManager.appendCustomMessageEntry?.bind(ctx.sessionManager);
|
|
306
393
|
if (!appendCustomMessageEntry)
|
|
307
394
|
return;
|
|
308
395
|
const { folded } = currentProjectionParts();
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
396
|
+
const producerContext = {
|
|
397
|
+
projectRoot,
|
|
398
|
+
runId,
|
|
399
|
+
folded,
|
|
400
|
+
taskIdAtStart,
|
|
401
|
+
runDisplayTitle
|
|
402
|
+
};
|
|
403
|
+
const payloadEntries = await Promise.all(panelRegistry.producers.map(async (producer) => {
|
|
404
|
+
try {
|
|
405
|
+
const payload = await produceWorkerPanelPayload(producer, producerContext);
|
|
406
|
+
return payload === undefined ? null : [producer.id, payload];
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error(`[rig-run] panel-producer-failed panel=${producer.id} ${error instanceof Error ? error.message : String(error)}`);
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
}));
|
|
412
|
+
const payloads = Object.fromEntries(payloadEntries.filter((entry) => entry !== null));
|
|
413
|
+
const registrations = panelRegistry.registrations.map((panel) => ({
|
|
414
|
+
id: panel.id,
|
|
415
|
+
slot: panel.slot,
|
|
416
|
+
title: panel.title,
|
|
417
|
+
capabilityId: panel.capabilityId,
|
|
418
|
+
description: panel.description,
|
|
419
|
+
badge: panel.badge,
|
|
420
|
+
disabled: panel.disabled
|
|
421
|
+
}));
|
|
422
|
+
const activePanel = registrations.find((panel) => panel.id === RIG_SUPERVISOR_PANEL_ID)?.id ?? registrations.find((panel) => !panel.disabled)?.id ?? registrations[0]?.id ?? null;
|
|
312
423
|
const frameBody = {
|
|
313
424
|
kind: "snapshot",
|
|
314
|
-
activePanel
|
|
315
|
-
registrations
|
|
316
|
-
|
|
317
|
-
status,
|
|
318
|
-
currentTask: taskId ? { id: taskId, title: runDisplayTitle } : null,
|
|
319
|
-
processed: folded.closeoutPhases.length,
|
|
320
|
-
succeeded: folded.closeoutPhases.filter((phase) => phase.outcome === "completed").length,
|
|
321
|
-
failed: folded.closeoutPhases.filter((phase) => phase.outcome === "failed").length,
|
|
322
|
-
skipped: 0,
|
|
323
|
-
plannedOrder: taskId ? [{ id: taskId, title: runDisplayTitle, status }] : [],
|
|
324
|
-
idleReason: operatorActive ? null : status,
|
|
325
|
-
stopActionId: operatorActive ? RIG_RUN_STOP_PANEL_ACTION : null,
|
|
326
|
-
closures: []
|
|
327
|
-
}
|
|
425
|
+
activePanel,
|
|
426
|
+
registrations,
|
|
427
|
+
payloads
|
|
328
428
|
};
|
|
329
429
|
const serialized = JSON.stringify(frameBody);
|
|
330
430
|
if (serialized === lastRigPanelsSnapshotJson)
|
|
@@ -332,6 +432,11 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
332
432
|
lastRigPanelsSnapshotJson = serialized;
|
|
333
433
|
appendCustomMessageEntry(RIG_PANELS_CUSTOM_MESSAGE_TYPE, "", false, { ...frameBody, updatedAt: new Date().toISOString() }, "agent");
|
|
334
434
|
};
|
|
435
|
+
const publishRigPanelsSnapshotBestEffort = () => {
|
|
436
|
+
publishRigPanelsSnapshot().catch((error) => {
|
|
437
|
+
console.error(`[rig-run] panel-snapshot-publish-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
438
|
+
});
|
|
439
|
+
};
|
|
335
440
|
const pushWorkerProjection = (links = runRegistryLinks) => {
|
|
336
441
|
if (!workerProjectionConnection)
|
|
337
442
|
return;
|
|
@@ -339,12 +444,12 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
339
444
|
};
|
|
340
445
|
const publishRunProjection = async (status) => {
|
|
341
446
|
const projection = buildCurrentRegistryProjection();
|
|
342
|
-
publishRigPanelsSnapshot();
|
|
343
447
|
if (workerProjectionConnection)
|
|
344
448
|
workerProjectionConnection.push(projection);
|
|
345
449
|
if (runRegistry && runRegistryRoomId) {
|
|
346
450
|
await runRegistry.heartbeatRoom(runRegistryRoomId, status, projection).catch((err) => console.error(`[rig-run] registry-heartbeat-failed ${err instanceof Error ? err.message : String(err)}`));
|
|
347
451
|
}
|
|
452
|
+
publishRigPanelsSnapshotBestEffort();
|
|
348
453
|
};
|
|
349
454
|
const stopRunRegistry = (opts = {}) => {
|
|
350
455
|
if (runRegistryHeartbeat) {
|
|
@@ -460,10 +565,14 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
460
565
|
relayUrl: rigRelayUrl(),
|
|
461
566
|
onPanelAction: handlePanelAction
|
|
462
567
|
});
|
|
568
|
+
const roomId = projection.sessionId?.trim() || runId;
|
|
569
|
+
if (roomId !== runId) {
|
|
570
|
+
throw new Error(`Collab host session id mismatch: expected ${runId}, got ${roomId}`);
|
|
571
|
+
}
|
|
463
572
|
runRegistryLinks = { joinLink: projection.joinLink, webLink: projection.webLink, relayUrl: projection.relayUrl ?? rigRelayUrl() };
|
|
464
573
|
const timeline = {
|
|
465
574
|
type: "collab-host-started",
|
|
466
|
-
roomId
|
|
575
|
+
roomId,
|
|
467
576
|
joinLink: projection.joinLink,
|
|
468
577
|
webLink: projection.webLink,
|
|
469
578
|
relayUrl: projection.relayUrl
|
|
@@ -471,13 +580,13 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
471
580
|
journal?.appendTimeline(timeline);
|
|
472
581
|
console.log(`[rig-run] collab-host-started joinLink=${projection.joinLink || "(empty)"} relayUrl=${projection.relayUrl || "(empty)"}`);
|
|
473
582
|
ctx.ui.notify("Rig run collab host started.", "info");
|
|
474
|
-
|
|
583
|
+
publishRigPanelsSnapshotBestEffort();
|
|
475
584
|
if (runRegistry) {
|
|
476
585
|
try {
|
|
477
|
-
runRegistryRoomId =
|
|
586
|
+
runRegistryRoomId = roomId;
|
|
478
587
|
const initialProjection = buildCurrentRegistryProjection();
|
|
479
588
|
await runRegistry.registerRoom({
|
|
480
|
-
roomId
|
|
589
|
+
roomId,
|
|
481
590
|
owner: runRegistryOwner,
|
|
482
591
|
repo: runRegistryRepo,
|
|
483
592
|
title: runDisplayTitle,
|
|
@@ -491,15 +600,15 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
491
600
|
pid: process.pid,
|
|
492
601
|
projection: initialProjection
|
|
493
602
|
});
|
|
494
|
-
workerProjectionConnection = connectWorkerProjection({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace, runId:
|
|
603
|
+
workerProjectionConnection = connectWorkerProjection({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace, runId: roomId });
|
|
495
604
|
workerProjectionConnection.ready.catch((err) => console.error(`[rig-run] worker-projection-degraded (heartbeat remains authoritative): ${err instanceof Error ? err.message : String(err)}`));
|
|
496
605
|
pushWorkerProjection();
|
|
497
606
|
workerProjectionFiber = Effect.runFork(localRunChanges(projectRoot).pipe(Stream.debounce(Duration.millis(500)), Stream.runForEach(() => Effect.sync(() => pushWorkerProjection()))));
|
|
498
|
-
journal?.appendTimeline({ type: "registry-registered", roomId
|
|
499
|
-
console.log(`[rig-run] registry-registered roomId=${
|
|
607
|
+
journal?.appendTimeline({ type: "registry-registered", roomId });
|
|
608
|
+
console.log(`[rig-run] registry-registered roomId=${roomId}`);
|
|
500
609
|
runRegistryHeartbeat = setInterval(() => {
|
|
501
610
|
const heartbeatProjection = buildCurrentRegistryProjection();
|
|
502
|
-
runRegistry.heartbeatRoom(
|
|
611
|
+
runRegistry.heartbeatRoom(roomId, coerceRegistryStatus(heartbeatProjection.status, "running"), heartbeatProjection).catch((err) => console.error(`[rig-run] registry-heartbeat-failed ${err instanceof Error ? err.message : String(err)}`));
|
|
503
612
|
}, 15000);
|
|
504
613
|
if (typeof runRegistryHeartbeat.unref === "function")
|
|
505
614
|
runRegistryHeartbeat.unref();
|
|
@@ -508,15 +617,22 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
508
617
|
ctx.ui.notify("Rig run could not register to the discovery registry; it may not appear in Runs.", "warning");
|
|
509
618
|
}
|
|
510
619
|
}
|
|
620
|
+
return true;
|
|
511
621
|
} catch (error) {
|
|
622
|
+
if (error instanceof Error && error.message.startsWith("Collab host session id mismatch:")) {
|
|
623
|
+
publishFatalTerminal(error);
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
512
626
|
console.error(`[rig-run] collab-host-start-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
513
627
|
ctx.ui.notify("Rig run collab host could not reach the relay; session remains local.", "warning");
|
|
628
|
+
return true;
|
|
514
629
|
}
|
|
515
630
|
};
|
|
516
631
|
journal?.appendTimeline({ type: "stage", stage: "Connect", status: "running", detail: "run process session_start" });
|
|
517
632
|
journal?.appendTimeline({ type: "stage", stage: "Prepare", status: "completed", detail: "run process prepared" });
|
|
518
633
|
journal?.appendStatus("running", { actor: { kind: "agent" }, reason: "run process session started", force: true });
|
|
519
|
-
await startRunCollabHost()
|
|
634
|
+
if (!await startRunCollabHost())
|
|
635
|
+
return null;
|
|
520
636
|
if (taskIdAtStart) {
|
|
521
637
|
journal?.appendTimeline({ type: "stage", stage: "GitHub-sync", status: "running", detail: "reflecting rig:running to task source" });
|
|
522
638
|
try {
|
|
@@ -616,7 +732,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
616
732
|
const { command, gitCommand } = createEnvCloseoutRunners(process.env);
|
|
617
733
|
let closeoutStatusAdvanced = false;
|
|
618
734
|
try {
|
|
619
|
-
await
|
|
735
|
+
await runPipelineCloseout({
|
|
620
736
|
projectRoot,
|
|
621
737
|
runId,
|
|
622
738
|
taskId,
|
|
@@ -631,6 +747,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
631
747
|
journal?.appendStatus("validating", { actor: { kind: "agent" }, reason: "validating task before closeout", force: true });
|
|
632
748
|
await publishRunProjection("validating");
|
|
633
749
|
},
|
|
750
|
+
kernelJournal: journal?.kernel ?? null,
|
|
634
751
|
journalPhase: async (phase, outcome, detail) => {
|
|
635
752
|
journal?.appendCloseoutPhase({ phase, outcome, detail: detail ?? null });
|
|
636
753
|
if (!closeoutStatusAdvanced && phase !== "queued" && outcome === "started") {
|
package/dist/src/extension.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/run-worker/src/autohost.ts
|
|
3
|
-
import {
|
|
3
|
+
import { runPipelineCloseout } from "@rig/bundle-default-lifecycle/pipeline-closeout";
|
|
4
|
+
import { adoptPlacementKernel } from "@rig/composition/kernel-entrypoint";
|
|
4
5
|
import { latestTimelineEntriesFromCustomEntries, parseInboxResolutionSentinel, parsePauseSentinel, parseResumeSentinel, parseStopSentinel, sessionIdFromSessionFile } from "@rig/contracts";
|
|
5
6
|
import { Duration, Effect, Fiber, Stream } from "effect";
|
|
6
7
|
import { createEnvCloseoutRunners } from "@rig/runtime/control-plane/native/closeout-runners";
|
|
@@ -12,7 +13,6 @@ import { resolveOwnerNamespaceKey } from "@rig/runtime/control-plane/remote-conf
|
|
|
12
13
|
import { resolveRigIdentity } from "@rig/runtime/control-plane/identity";
|
|
13
14
|
import { connectWorkerProjection, createRegistryClient } from "@rig/relay-registry";
|
|
14
15
|
import { coerceRegistryStatus } from "@rig/relay-registry/schema";
|
|
15
|
-
import { bootDefaultKernelIntoProcess } from "@rig/kernel/boot-default";
|
|
16
16
|
|
|
17
17
|
// packages/run-worker/src/journal.ts
|
|
18
18
|
import { createJournalSessionProvider } from "@rig/kernel/journal-session-provider";
|
|
@@ -108,6 +108,91 @@ function startRunProcessStallMonitor(opts) {
|
|
|
108
108
|
return () => clearInterval(timer);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// packages/run-worker/src/panel-plugin.ts
|
|
112
|
+
import { createProjectPluginHost, projectPluginResolutionWarningMessages } from "@rig/composition/project-plugins";
|
|
113
|
+
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels";
|
|
114
|
+
var RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
|
|
115
|
+
var RIG_CAPABILITY_PANEL_SLOT = "capability";
|
|
116
|
+
var RIG_SUPERVISOR_PANEL_ID = "supervisor";
|
|
117
|
+
var PANEL_PRODUCER_TIMEOUT_MS = 2000;
|
|
118
|
+
var RUN_SUPERVISOR_PANEL_REGISTRATION = {
|
|
119
|
+
id: RIG_SUPERVISOR_PANEL_ID,
|
|
120
|
+
slot: RIG_CAPABILITY_PANEL_SLOT,
|
|
121
|
+
title: "Supervisor",
|
|
122
|
+
capabilityId: "run.supervisor",
|
|
123
|
+
description: "Live run status, closeout progress, and operator stop control."
|
|
124
|
+
};
|
|
125
|
+
function buildSupervisorPanelPayload(context) {
|
|
126
|
+
const status = context.folded.status ?? "unknown";
|
|
127
|
+
const taskId = context.folded.record.taskId ?? context.taskIdAtStart;
|
|
128
|
+
const operatorActive = status === "running" || status === "validating" || status === "closing-out" || status === "needs-attention";
|
|
129
|
+
return {
|
|
130
|
+
status,
|
|
131
|
+
currentTask: taskId ? { id: taskId, title: context.runDisplayTitle } : null,
|
|
132
|
+
processed: context.folded.closeoutPhases.length,
|
|
133
|
+
succeeded: context.folded.closeoutPhases.filter((phase) => phase.outcome === "completed").length,
|
|
134
|
+
failed: context.folded.closeoutPhases.filter((phase) => phase.outcome === "failed").length,
|
|
135
|
+
skipped: 0,
|
|
136
|
+
plannedOrder: taskId ? [{ id: taskId, title: context.runDisplayTitle, status }] : [],
|
|
137
|
+
idleReason: operatorActive ? null : status,
|
|
138
|
+
stopActionId: operatorActive ? RIG_RUN_STOP_PANEL_ACTION : null,
|
|
139
|
+
closures: []
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
var RUN_SUPERVISOR_PANEL_PRODUCER = {
|
|
143
|
+
...RUN_SUPERVISOR_PANEL_REGISTRATION,
|
|
144
|
+
produce(context) {
|
|
145
|
+
return buildSupervisorPanelPayload(context);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
async function produceWorkerPanelPayload(producer, context) {
|
|
149
|
+
if (!producer.produce)
|
|
150
|
+
return;
|
|
151
|
+
let timeout;
|
|
152
|
+
try {
|
|
153
|
+
return await Promise.race([
|
|
154
|
+
Promise.resolve(producer.produce(context)),
|
|
155
|
+
new Promise((resolve2) => {
|
|
156
|
+
timeout = setTimeout(() => resolve2(undefined), PANEL_PRODUCER_TIMEOUT_MS);
|
|
157
|
+
})
|
|
158
|
+
]);
|
|
159
|
+
} finally {
|
|
160
|
+
clearTimeout(timeout);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
var RUN_WORKER_PANEL_PLUGIN = {
|
|
164
|
+
name: "@rig/run-worker-panels",
|
|
165
|
+
version: "0.0.0-alpha.1",
|
|
166
|
+
contributes: {
|
|
167
|
+
panels: [RUN_SUPERVISOR_PANEL_REGISTRATION]
|
|
168
|
+
},
|
|
169
|
+
__runtime: {
|
|
170
|
+
panels: [RUN_SUPERVISOR_PANEL_PRODUCER]
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
async function loadWorkerPanelRegistry(projectRoot) {
|
|
174
|
+
try {
|
|
175
|
+
const { host, resolved } = await createProjectPluginHost(projectRoot, {
|
|
176
|
+
mode: "compatibility-default-injection",
|
|
177
|
+
surfaceName: "run-worker-panel-registry",
|
|
178
|
+
surfaceExtras: [RUN_WORKER_PANEL_PLUGIN]
|
|
179
|
+
});
|
|
180
|
+
for (const message of projectPluginResolutionWarningMessages(resolved)) {
|
|
181
|
+
console.warn(`[rig-run] ${message}`);
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
registrations: host.listPanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT),
|
|
185
|
+
producers: host.listExecutablePanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT)
|
|
186
|
+
};
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(`[rig-run] panel-registry-build-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
189
|
+
return {
|
|
190
|
+
registrations: [RUN_SUPERVISOR_PANEL_REGISTRATION],
|
|
191
|
+
producers: [RUN_SUPERVISOR_PANEL_PRODUCER]
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
111
196
|
// packages/run-worker/src/utils.ts
|
|
112
197
|
import { createWorkflowStatusChanged, RIG_WORKFLOW_STATUS_CHANGED } from "@rig/contracts";
|
|
113
198
|
import { resolveRegistryBaseUrl, resolveRelayUrl } from "@rig/runtime/control-plane/remote-config";
|
|
@@ -130,8 +215,6 @@ function rigRelayUrl() {
|
|
|
130
215
|
}
|
|
131
216
|
|
|
132
217
|
// packages/run-worker/src/autohost.ts
|
|
133
|
-
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels";
|
|
134
|
-
var RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
|
|
135
218
|
function syntheticRegistryOwner(namespaceKey) {
|
|
136
219
|
const githubUserId = namespaceKey.startsWith("gh:") ? namespaceKey.slice(3) : namespaceKey;
|
|
137
220
|
return {
|
|
@@ -228,7 +311,16 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
228
311
|
const envRunId = process.env.RIG_RUN_ID?.trim();
|
|
229
312
|
if (!envRunId)
|
|
230
313
|
throw new Error("RIG_RUN_ID is required when RIG_RUN_PROCESS=1");
|
|
231
|
-
const
|
|
314
|
+
const sessionManagerRunId = typeof ctx.sessionManager.getSessionId === "function" ? ctx.sessionManager.getSessionId().trim() : "";
|
|
315
|
+
const runId = sessionIdFromSessionFile(ctx.sessionManager.getSessionFile()) || sessionManagerRunId;
|
|
316
|
+
if (!runId)
|
|
317
|
+
throw new Error("OMP session id is required when RIG_RUN_PROCESS=1; RIG_RUN_ID is only a dispatch handle.");
|
|
318
|
+
const projectRoot = process.env.PROJECT_RIG_ROOT ?? process.cwd();
|
|
319
|
+
const journal = await createRunJournal(ctx.sessionManager, runId);
|
|
320
|
+
await adoptPlacementKernel(projectRoot, {
|
|
321
|
+
entrypoint: "run-worker",
|
|
322
|
+
...journal ? { journal: journal.kernel } : {}
|
|
323
|
+
});
|
|
232
324
|
if (!ctx.hasUI)
|
|
233
325
|
return null;
|
|
234
326
|
const collab = ctx.collab;
|
|
@@ -236,12 +328,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
236
328
|
if (!startHost)
|
|
237
329
|
throw new Error("OMP collab host facade is unavailable for Rig run process.");
|
|
238
330
|
const identity = resolveRigIdentity(ctx);
|
|
239
|
-
const
|
|
240
|
-
await bootDefaultKernelIntoProcess({
|
|
241
|
-
entrypoint: "run-worker",
|
|
242
|
-
...journal ? { journal: journal.kernel } : {}
|
|
243
|
-
});
|
|
244
|
-
const projectRoot = process.env.PROJECT_RIG_ROOT ?? process.cwd();
|
|
331
|
+
const panelRegistry = await loadWorkerPanelRegistry(projectRoot);
|
|
245
332
|
let runProjection = projectRunFromSession(customEntries(ctx.sessionManager.getBranch()), runId);
|
|
246
333
|
const taskIdAtStart = process.env.RIG_TASK_ID?.trim() || runProjection.record.taskId;
|
|
247
334
|
const runDisplayTitle = (() => {
|
|
@@ -301,30 +388,43 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
301
388
|
});
|
|
302
389
|
};
|
|
303
390
|
let lastRigPanelsSnapshotJson = "";
|
|
304
|
-
const publishRigPanelsSnapshot = () => {
|
|
391
|
+
const publishRigPanelsSnapshot = async () => {
|
|
305
392
|
const appendCustomMessageEntry = ctx.sessionManager.appendCustomMessageEntry?.bind(ctx.sessionManager);
|
|
306
393
|
if (!appendCustomMessageEntry)
|
|
307
394
|
return;
|
|
308
395
|
const { folded } = currentProjectionParts();
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
396
|
+
const producerContext = {
|
|
397
|
+
projectRoot,
|
|
398
|
+
runId,
|
|
399
|
+
folded,
|
|
400
|
+
taskIdAtStart,
|
|
401
|
+
runDisplayTitle
|
|
402
|
+
};
|
|
403
|
+
const payloadEntries = await Promise.all(panelRegistry.producers.map(async (producer) => {
|
|
404
|
+
try {
|
|
405
|
+
const payload = await produceWorkerPanelPayload(producer, producerContext);
|
|
406
|
+
return payload === undefined ? null : [producer.id, payload];
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error(`[rig-run] panel-producer-failed panel=${producer.id} ${error instanceof Error ? error.message : String(error)}`);
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
}));
|
|
412
|
+
const payloads = Object.fromEntries(payloadEntries.filter((entry) => entry !== null));
|
|
413
|
+
const registrations = panelRegistry.registrations.map((panel) => ({
|
|
414
|
+
id: panel.id,
|
|
415
|
+
slot: panel.slot,
|
|
416
|
+
title: panel.title,
|
|
417
|
+
capabilityId: panel.capabilityId,
|
|
418
|
+
description: panel.description,
|
|
419
|
+
badge: panel.badge,
|
|
420
|
+
disabled: panel.disabled
|
|
421
|
+
}));
|
|
422
|
+
const activePanel = registrations.find((panel) => panel.id === RIG_SUPERVISOR_PANEL_ID)?.id ?? registrations.find((panel) => !panel.disabled)?.id ?? registrations[0]?.id ?? null;
|
|
312
423
|
const frameBody = {
|
|
313
424
|
kind: "snapshot",
|
|
314
|
-
activePanel
|
|
315
|
-
registrations
|
|
316
|
-
|
|
317
|
-
status,
|
|
318
|
-
currentTask: taskId ? { id: taskId, title: runDisplayTitle } : null,
|
|
319
|
-
processed: folded.closeoutPhases.length,
|
|
320
|
-
succeeded: folded.closeoutPhases.filter((phase) => phase.outcome === "completed").length,
|
|
321
|
-
failed: folded.closeoutPhases.filter((phase) => phase.outcome === "failed").length,
|
|
322
|
-
skipped: 0,
|
|
323
|
-
plannedOrder: taskId ? [{ id: taskId, title: runDisplayTitle, status }] : [],
|
|
324
|
-
idleReason: operatorActive ? null : status,
|
|
325
|
-
stopActionId: operatorActive ? RIG_RUN_STOP_PANEL_ACTION : null,
|
|
326
|
-
closures: []
|
|
327
|
-
}
|
|
425
|
+
activePanel,
|
|
426
|
+
registrations,
|
|
427
|
+
payloads
|
|
328
428
|
};
|
|
329
429
|
const serialized = JSON.stringify(frameBody);
|
|
330
430
|
if (serialized === lastRigPanelsSnapshotJson)
|
|
@@ -332,6 +432,11 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
332
432
|
lastRigPanelsSnapshotJson = serialized;
|
|
333
433
|
appendCustomMessageEntry(RIG_PANELS_CUSTOM_MESSAGE_TYPE, "", false, { ...frameBody, updatedAt: new Date().toISOString() }, "agent");
|
|
334
434
|
};
|
|
435
|
+
const publishRigPanelsSnapshotBestEffort = () => {
|
|
436
|
+
publishRigPanelsSnapshot().catch((error) => {
|
|
437
|
+
console.error(`[rig-run] panel-snapshot-publish-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
438
|
+
});
|
|
439
|
+
};
|
|
335
440
|
const pushWorkerProjection = (links = runRegistryLinks) => {
|
|
336
441
|
if (!workerProjectionConnection)
|
|
337
442
|
return;
|
|
@@ -339,12 +444,12 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
339
444
|
};
|
|
340
445
|
const publishRunProjection = async (status) => {
|
|
341
446
|
const projection = buildCurrentRegistryProjection();
|
|
342
|
-
publishRigPanelsSnapshot();
|
|
343
447
|
if (workerProjectionConnection)
|
|
344
448
|
workerProjectionConnection.push(projection);
|
|
345
449
|
if (runRegistry && runRegistryRoomId) {
|
|
346
450
|
await runRegistry.heartbeatRoom(runRegistryRoomId, status, projection).catch((err) => console.error(`[rig-run] registry-heartbeat-failed ${err instanceof Error ? err.message : String(err)}`));
|
|
347
451
|
}
|
|
452
|
+
publishRigPanelsSnapshotBestEffort();
|
|
348
453
|
};
|
|
349
454
|
const stopRunRegistry = (opts = {}) => {
|
|
350
455
|
if (runRegistryHeartbeat) {
|
|
@@ -460,10 +565,14 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
460
565
|
relayUrl: rigRelayUrl(),
|
|
461
566
|
onPanelAction: handlePanelAction
|
|
462
567
|
});
|
|
568
|
+
const roomId = projection.sessionId?.trim() || runId;
|
|
569
|
+
if (roomId !== runId) {
|
|
570
|
+
throw new Error(`Collab host session id mismatch: expected ${runId}, got ${roomId}`);
|
|
571
|
+
}
|
|
463
572
|
runRegistryLinks = { joinLink: projection.joinLink, webLink: projection.webLink, relayUrl: projection.relayUrl ?? rigRelayUrl() };
|
|
464
573
|
const timeline = {
|
|
465
574
|
type: "collab-host-started",
|
|
466
|
-
roomId
|
|
575
|
+
roomId,
|
|
467
576
|
joinLink: projection.joinLink,
|
|
468
577
|
webLink: projection.webLink,
|
|
469
578
|
relayUrl: projection.relayUrl
|
|
@@ -471,13 +580,13 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
471
580
|
journal?.appendTimeline(timeline);
|
|
472
581
|
console.log(`[rig-run] collab-host-started joinLink=${projection.joinLink || "(empty)"} relayUrl=${projection.relayUrl || "(empty)"}`);
|
|
473
582
|
ctx.ui.notify("Rig run collab host started.", "info");
|
|
474
|
-
|
|
583
|
+
publishRigPanelsSnapshotBestEffort();
|
|
475
584
|
if (runRegistry) {
|
|
476
585
|
try {
|
|
477
|
-
runRegistryRoomId =
|
|
586
|
+
runRegistryRoomId = roomId;
|
|
478
587
|
const initialProjection = buildCurrentRegistryProjection();
|
|
479
588
|
await runRegistry.registerRoom({
|
|
480
|
-
roomId
|
|
589
|
+
roomId,
|
|
481
590
|
owner: runRegistryOwner,
|
|
482
591
|
repo: runRegistryRepo,
|
|
483
592
|
title: runDisplayTitle,
|
|
@@ -491,15 +600,15 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
491
600
|
pid: process.pid,
|
|
492
601
|
projection: initialProjection
|
|
493
602
|
});
|
|
494
|
-
workerProjectionConnection = connectWorkerProjection({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace, runId:
|
|
603
|
+
workerProjectionConnection = connectWorkerProjection({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace, runId: roomId });
|
|
495
604
|
workerProjectionConnection.ready.catch((err) => console.error(`[rig-run] worker-projection-degraded (heartbeat remains authoritative): ${err instanceof Error ? err.message : String(err)}`));
|
|
496
605
|
pushWorkerProjection();
|
|
497
606
|
workerProjectionFiber = Effect.runFork(localRunChanges(projectRoot).pipe(Stream.debounce(Duration.millis(500)), Stream.runForEach(() => Effect.sync(() => pushWorkerProjection()))));
|
|
498
|
-
journal?.appendTimeline({ type: "registry-registered", roomId
|
|
499
|
-
console.log(`[rig-run] registry-registered roomId=${
|
|
607
|
+
journal?.appendTimeline({ type: "registry-registered", roomId });
|
|
608
|
+
console.log(`[rig-run] registry-registered roomId=${roomId}`);
|
|
500
609
|
runRegistryHeartbeat = setInterval(() => {
|
|
501
610
|
const heartbeatProjection = buildCurrentRegistryProjection();
|
|
502
|
-
runRegistry.heartbeatRoom(
|
|
611
|
+
runRegistry.heartbeatRoom(roomId, coerceRegistryStatus(heartbeatProjection.status, "running"), heartbeatProjection).catch((err) => console.error(`[rig-run] registry-heartbeat-failed ${err instanceof Error ? err.message : String(err)}`));
|
|
503
612
|
}, 15000);
|
|
504
613
|
if (typeof runRegistryHeartbeat.unref === "function")
|
|
505
614
|
runRegistryHeartbeat.unref();
|
|
@@ -508,15 +617,22 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
508
617
|
ctx.ui.notify("Rig run could not register to the discovery registry; it may not appear in Runs.", "warning");
|
|
509
618
|
}
|
|
510
619
|
}
|
|
620
|
+
return true;
|
|
511
621
|
} catch (error) {
|
|
622
|
+
if (error instanceof Error && error.message.startsWith("Collab host session id mismatch:")) {
|
|
623
|
+
publishFatalTerminal(error);
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
512
626
|
console.error(`[rig-run] collab-host-start-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
513
627
|
ctx.ui.notify("Rig run collab host could not reach the relay; session remains local.", "warning");
|
|
628
|
+
return true;
|
|
514
629
|
}
|
|
515
630
|
};
|
|
516
631
|
journal?.appendTimeline({ type: "stage", stage: "Connect", status: "running", detail: "run process session_start" });
|
|
517
632
|
journal?.appendTimeline({ type: "stage", stage: "Prepare", status: "completed", detail: "run process prepared" });
|
|
518
633
|
journal?.appendStatus("running", { actor: { kind: "agent" }, reason: "run process session started", force: true });
|
|
519
|
-
await startRunCollabHost()
|
|
634
|
+
if (!await startRunCollabHost())
|
|
635
|
+
return null;
|
|
520
636
|
if (taskIdAtStart) {
|
|
521
637
|
journal?.appendTimeline({ type: "stage", stage: "GitHub-sync", status: "running", detail: "reflecting rig:running to task source" });
|
|
522
638
|
try {
|
|
@@ -616,7 +732,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
616
732
|
const { command, gitCommand } = createEnvCloseoutRunners(process.env);
|
|
617
733
|
let closeoutStatusAdvanced = false;
|
|
618
734
|
try {
|
|
619
|
-
await
|
|
735
|
+
await runPipelineCloseout({
|
|
620
736
|
projectRoot,
|
|
621
737
|
runId,
|
|
622
738
|
taskId,
|
|
@@ -631,6 +747,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
631
747
|
journal?.appendStatus("validating", { actor: { kind: "agent" }, reason: "validating task before closeout", force: true });
|
|
632
748
|
await publishRunProjection("validating");
|
|
633
749
|
},
|
|
750
|
+
kernelJournal: journal?.kernel ?? null,
|
|
634
751
|
journalPhase: async (phase, outcome, detail) => {
|
|
635
752
|
journal?.appendCloseoutPhase({ phase, outcome, detail: detail ?? null });
|
|
636
753
|
if (!closeoutStatusAdvanced && phase !== "queued" && outcome === "started") {
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/run-worker/src/autohost.ts
|
|
3
|
-
import {
|
|
3
|
+
import { runPipelineCloseout } from "@rig/bundle-default-lifecycle/pipeline-closeout";
|
|
4
|
+
import { adoptPlacementKernel } from "@rig/composition/kernel-entrypoint";
|
|
4
5
|
import { latestTimelineEntriesFromCustomEntries, parseInboxResolutionSentinel, parsePauseSentinel, parseResumeSentinel, parseStopSentinel, sessionIdFromSessionFile } from "@rig/contracts";
|
|
5
6
|
import { Duration, Effect, Fiber, Stream } from "effect";
|
|
6
7
|
import { createEnvCloseoutRunners } from "@rig/runtime/control-plane/native/closeout-runners";
|
|
@@ -12,7 +13,6 @@ import { resolveOwnerNamespaceKey } from "@rig/runtime/control-plane/remote-conf
|
|
|
12
13
|
import { resolveRigIdentity } from "@rig/runtime/control-plane/identity";
|
|
13
14
|
import { connectWorkerProjection, createRegistryClient } from "@rig/relay-registry";
|
|
14
15
|
import { coerceRegistryStatus } from "@rig/relay-registry/schema";
|
|
15
|
-
import { bootDefaultKernelIntoProcess } from "@rig/kernel/boot-default";
|
|
16
16
|
|
|
17
17
|
// packages/run-worker/src/journal.ts
|
|
18
18
|
import { createJournalSessionProvider } from "@rig/kernel/journal-session-provider";
|
|
@@ -108,6 +108,91 @@ function startRunProcessStallMonitor(opts) {
|
|
|
108
108
|
return () => clearInterval(timer);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// packages/run-worker/src/panel-plugin.ts
|
|
112
|
+
import { createProjectPluginHost, projectPluginResolutionWarningMessages } from "@rig/composition/project-plugins";
|
|
113
|
+
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels";
|
|
114
|
+
var RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
|
|
115
|
+
var RIG_CAPABILITY_PANEL_SLOT = "capability";
|
|
116
|
+
var RIG_SUPERVISOR_PANEL_ID = "supervisor";
|
|
117
|
+
var PANEL_PRODUCER_TIMEOUT_MS = 2000;
|
|
118
|
+
var RUN_SUPERVISOR_PANEL_REGISTRATION = {
|
|
119
|
+
id: RIG_SUPERVISOR_PANEL_ID,
|
|
120
|
+
slot: RIG_CAPABILITY_PANEL_SLOT,
|
|
121
|
+
title: "Supervisor",
|
|
122
|
+
capabilityId: "run.supervisor",
|
|
123
|
+
description: "Live run status, closeout progress, and operator stop control."
|
|
124
|
+
};
|
|
125
|
+
function buildSupervisorPanelPayload(context) {
|
|
126
|
+
const status = context.folded.status ?? "unknown";
|
|
127
|
+
const taskId = context.folded.record.taskId ?? context.taskIdAtStart;
|
|
128
|
+
const operatorActive = status === "running" || status === "validating" || status === "closing-out" || status === "needs-attention";
|
|
129
|
+
return {
|
|
130
|
+
status,
|
|
131
|
+
currentTask: taskId ? { id: taskId, title: context.runDisplayTitle } : null,
|
|
132
|
+
processed: context.folded.closeoutPhases.length,
|
|
133
|
+
succeeded: context.folded.closeoutPhases.filter((phase) => phase.outcome === "completed").length,
|
|
134
|
+
failed: context.folded.closeoutPhases.filter((phase) => phase.outcome === "failed").length,
|
|
135
|
+
skipped: 0,
|
|
136
|
+
plannedOrder: taskId ? [{ id: taskId, title: context.runDisplayTitle, status }] : [],
|
|
137
|
+
idleReason: operatorActive ? null : status,
|
|
138
|
+
stopActionId: operatorActive ? RIG_RUN_STOP_PANEL_ACTION : null,
|
|
139
|
+
closures: []
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
var RUN_SUPERVISOR_PANEL_PRODUCER = {
|
|
143
|
+
...RUN_SUPERVISOR_PANEL_REGISTRATION,
|
|
144
|
+
produce(context) {
|
|
145
|
+
return buildSupervisorPanelPayload(context);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
async function produceWorkerPanelPayload(producer, context) {
|
|
149
|
+
if (!producer.produce)
|
|
150
|
+
return;
|
|
151
|
+
let timeout;
|
|
152
|
+
try {
|
|
153
|
+
return await Promise.race([
|
|
154
|
+
Promise.resolve(producer.produce(context)),
|
|
155
|
+
new Promise((resolve2) => {
|
|
156
|
+
timeout = setTimeout(() => resolve2(undefined), PANEL_PRODUCER_TIMEOUT_MS);
|
|
157
|
+
})
|
|
158
|
+
]);
|
|
159
|
+
} finally {
|
|
160
|
+
clearTimeout(timeout);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
var RUN_WORKER_PANEL_PLUGIN = {
|
|
164
|
+
name: "@rig/run-worker-panels",
|
|
165
|
+
version: "0.0.0-alpha.1",
|
|
166
|
+
contributes: {
|
|
167
|
+
panels: [RUN_SUPERVISOR_PANEL_REGISTRATION]
|
|
168
|
+
},
|
|
169
|
+
__runtime: {
|
|
170
|
+
panels: [RUN_SUPERVISOR_PANEL_PRODUCER]
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
async function loadWorkerPanelRegistry(projectRoot) {
|
|
174
|
+
try {
|
|
175
|
+
const { host, resolved } = await createProjectPluginHost(projectRoot, {
|
|
176
|
+
mode: "compatibility-default-injection",
|
|
177
|
+
surfaceName: "run-worker-panel-registry",
|
|
178
|
+
surfaceExtras: [RUN_WORKER_PANEL_PLUGIN]
|
|
179
|
+
});
|
|
180
|
+
for (const message of projectPluginResolutionWarningMessages(resolved)) {
|
|
181
|
+
console.warn(`[rig-run] ${message}`);
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
registrations: host.listPanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT),
|
|
185
|
+
producers: host.listExecutablePanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT)
|
|
186
|
+
};
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(`[rig-run] panel-registry-build-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
189
|
+
return {
|
|
190
|
+
registrations: [RUN_SUPERVISOR_PANEL_REGISTRATION],
|
|
191
|
+
producers: [RUN_SUPERVISOR_PANEL_PRODUCER]
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
111
196
|
// packages/run-worker/src/utils.ts
|
|
112
197
|
import { createWorkflowStatusChanged, RIG_WORKFLOW_STATUS_CHANGED } from "@rig/contracts";
|
|
113
198
|
import { resolveRegistryBaseUrl, resolveRelayUrl } from "@rig/runtime/control-plane/remote-config";
|
|
@@ -130,8 +215,6 @@ function rigRelayUrl() {
|
|
|
130
215
|
}
|
|
131
216
|
|
|
132
217
|
// packages/run-worker/src/autohost.ts
|
|
133
|
-
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels";
|
|
134
|
-
var RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
|
|
135
218
|
function syntheticRegistryOwner(namespaceKey) {
|
|
136
219
|
const githubUserId = namespaceKey.startsWith("gh:") ? namespaceKey.slice(3) : namespaceKey;
|
|
137
220
|
return {
|
|
@@ -228,7 +311,16 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
228
311
|
const envRunId = process.env.RIG_RUN_ID?.trim();
|
|
229
312
|
if (!envRunId)
|
|
230
313
|
throw new Error("RIG_RUN_ID is required when RIG_RUN_PROCESS=1");
|
|
231
|
-
const
|
|
314
|
+
const sessionManagerRunId = typeof ctx.sessionManager.getSessionId === "function" ? ctx.sessionManager.getSessionId().trim() : "";
|
|
315
|
+
const runId = sessionIdFromSessionFile(ctx.sessionManager.getSessionFile()) || sessionManagerRunId;
|
|
316
|
+
if (!runId)
|
|
317
|
+
throw new Error("OMP session id is required when RIG_RUN_PROCESS=1; RIG_RUN_ID is only a dispatch handle.");
|
|
318
|
+
const projectRoot = process.env.PROJECT_RIG_ROOT ?? process.cwd();
|
|
319
|
+
const journal = await createRunJournal(ctx.sessionManager, runId);
|
|
320
|
+
await adoptPlacementKernel(projectRoot, {
|
|
321
|
+
entrypoint: "run-worker",
|
|
322
|
+
...journal ? { journal: journal.kernel } : {}
|
|
323
|
+
});
|
|
232
324
|
if (!ctx.hasUI)
|
|
233
325
|
return null;
|
|
234
326
|
const collab = ctx.collab;
|
|
@@ -236,12 +328,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
236
328
|
if (!startHost)
|
|
237
329
|
throw new Error("OMP collab host facade is unavailable for Rig run process.");
|
|
238
330
|
const identity = resolveRigIdentity(ctx);
|
|
239
|
-
const
|
|
240
|
-
await bootDefaultKernelIntoProcess({
|
|
241
|
-
entrypoint: "run-worker",
|
|
242
|
-
...journal ? { journal: journal.kernel } : {}
|
|
243
|
-
});
|
|
244
|
-
const projectRoot = process.env.PROJECT_RIG_ROOT ?? process.cwd();
|
|
331
|
+
const panelRegistry = await loadWorkerPanelRegistry(projectRoot);
|
|
245
332
|
let runProjection = projectRunFromSession(customEntries(ctx.sessionManager.getBranch()), runId);
|
|
246
333
|
const taskIdAtStart = process.env.RIG_TASK_ID?.trim() || runProjection.record.taskId;
|
|
247
334
|
const runDisplayTitle = (() => {
|
|
@@ -301,30 +388,43 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
301
388
|
});
|
|
302
389
|
};
|
|
303
390
|
let lastRigPanelsSnapshotJson = "";
|
|
304
|
-
const publishRigPanelsSnapshot = () => {
|
|
391
|
+
const publishRigPanelsSnapshot = async () => {
|
|
305
392
|
const appendCustomMessageEntry = ctx.sessionManager.appendCustomMessageEntry?.bind(ctx.sessionManager);
|
|
306
393
|
if (!appendCustomMessageEntry)
|
|
307
394
|
return;
|
|
308
395
|
const { folded } = currentProjectionParts();
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
396
|
+
const producerContext = {
|
|
397
|
+
projectRoot,
|
|
398
|
+
runId,
|
|
399
|
+
folded,
|
|
400
|
+
taskIdAtStart,
|
|
401
|
+
runDisplayTitle
|
|
402
|
+
};
|
|
403
|
+
const payloadEntries = await Promise.all(panelRegistry.producers.map(async (producer) => {
|
|
404
|
+
try {
|
|
405
|
+
const payload = await produceWorkerPanelPayload(producer, producerContext);
|
|
406
|
+
return payload === undefined ? null : [producer.id, payload];
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error(`[rig-run] panel-producer-failed panel=${producer.id} ${error instanceof Error ? error.message : String(error)}`);
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
}));
|
|
412
|
+
const payloads = Object.fromEntries(payloadEntries.filter((entry) => entry !== null));
|
|
413
|
+
const registrations = panelRegistry.registrations.map((panel) => ({
|
|
414
|
+
id: panel.id,
|
|
415
|
+
slot: panel.slot,
|
|
416
|
+
title: panel.title,
|
|
417
|
+
capabilityId: panel.capabilityId,
|
|
418
|
+
description: panel.description,
|
|
419
|
+
badge: panel.badge,
|
|
420
|
+
disabled: panel.disabled
|
|
421
|
+
}));
|
|
422
|
+
const activePanel = registrations.find((panel) => panel.id === RIG_SUPERVISOR_PANEL_ID)?.id ?? registrations.find((panel) => !panel.disabled)?.id ?? registrations[0]?.id ?? null;
|
|
312
423
|
const frameBody = {
|
|
313
424
|
kind: "snapshot",
|
|
314
|
-
activePanel
|
|
315
|
-
registrations
|
|
316
|
-
|
|
317
|
-
status,
|
|
318
|
-
currentTask: taskId ? { id: taskId, title: runDisplayTitle } : null,
|
|
319
|
-
processed: folded.closeoutPhases.length,
|
|
320
|
-
succeeded: folded.closeoutPhases.filter((phase) => phase.outcome === "completed").length,
|
|
321
|
-
failed: folded.closeoutPhases.filter((phase) => phase.outcome === "failed").length,
|
|
322
|
-
skipped: 0,
|
|
323
|
-
plannedOrder: taskId ? [{ id: taskId, title: runDisplayTitle, status }] : [],
|
|
324
|
-
idleReason: operatorActive ? null : status,
|
|
325
|
-
stopActionId: operatorActive ? RIG_RUN_STOP_PANEL_ACTION : null,
|
|
326
|
-
closures: []
|
|
327
|
-
}
|
|
425
|
+
activePanel,
|
|
426
|
+
registrations,
|
|
427
|
+
payloads
|
|
328
428
|
};
|
|
329
429
|
const serialized = JSON.stringify(frameBody);
|
|
330
430
|
if (serialized === lastRigPanelsSnapshotJson)
|
|
@@ -332,6 +432,11 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
332
432
|
lastRigPanelsSnapshotJson = serialized;
|
|
333
433
|
appendCustomMessageEntry(RIG_PANELS_CUSTOM_MESSAGE_TYPE, "", false, { ...frameBody, updatedAt: new Date().toISOString() }, "agent");
|
|
334
434
|
};
|
|
435
|
+
const publishRigPanelsSnapshotBestEffort = () => {
|
|
436
|
+
publishRigPanelsSnapshot().catch((error) => {
|
|
437
|
+
console.error(`[rig-run] panel-snapshot-publish-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
438
|
+
});
|
|
439
|
+
};
|
|
335
440
|
const pushWorkerProjection = (links = runRegistryLinks) => {
|
|
336
441
|
if (!workerProjectionConnection)
|
|
337
442
|
return;
|
|
@@ -339,12 +444,12 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
339
444
|
};
|
|
340
445
|
const publishRunProjection = async (status) => {
|
|
341
446
|
const projection = buildCurrentRegistryProjection();
|
|
342
|
-
publishRigPanelsSnapshot();
|
|
343
447
|
if (workerProjectionConnection)
|
|
344
448
|
workerProjectionConnection.push(projection);
|
|
345
449
|
if (runRegistry && runRegistryRoomId) {
|
|
346
450
|
await runRegistry.heartbeatRoom(runRegistryRoomId, status, projection).catch((err) => console.error(`[rig-run] registry-heartbeat-failed ${err instanceof Error ? err.message : String(err)}`));
|
|
347
451
|
}
|
|
452
|
+
publishRigPanelsSnapshotBestEffort();
|
|
348
453
|
};
|
|
349
454
|
const stopRunRegistry = (opts = {}) => {
|
|
350
455
|
if (runRegistryHeartbeat) {
|
|
@@ -460,10 +565,14 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
460
565
|
relayUrl: rigRelayUrl(),
|
|
461
566
|
onPanelAction: handlePanelAction
|
|
462
567
|
});
|
|
568
|
+
const roomId = projection.sessionId?.trim() || runId;
|
|
569
|
+
if (roomId !== runId) {
|
|
570
|
+
throw new Error(`Collab host session id mismatch: expected ${runId}, got ${roomId}`);
|
|
571
|
+
}
|
|
463
572
|
runRegistryLinks = { joinLink: projection.joinLink, webLink: projection.webLink, relayUrl: projection.relayUrl ?? rigRelayUrl() };
|
|
464
573
|
const timeline = {
|
|
465
574
|
type: "collab-host-started",
|
|
466
|
-
roomId
|
|
575
|
+
roomId,
|
|
467
576
|
joinLink: projection.joinLink,
|
|
468
577
|
webLink: projection.webLink,
|
|
469
578
|
relayUrl: projection.relayUrl
|
|
@@ -471,13 +580,13 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
471
580
|
journal?.appendTimeline(timeline);
|
|
472
581
|
console.log(`[rig-run] collab-host-started joinLink=${projection.joinLink || "(empty)"} relayUrl=${projection.relayUrl || "(empty)"}`);
|
|
473
582
|
ctx.ui.notify("Rig run collab host started.", "info");
|
|
474
|
-
|
|
583
|
+
publishRigPanelsSnapshotBestEffort();
|
|
475
584
|
if (runRegistry) {
|
|
476
585
|
try {
|
|
477
|
-
runRegistryRoomId =
|
|
586
|
+
runRegistryRoomId = roomId;
|
|
478
587
|
const initialProjection = buildCurrentRegistryProjection();
|
|
479
588
|
await runRegistry.registerRoom({
|
|
480
|
-
roomId
|
|
589
|
+
roomId,
|
|
481
590
|
owner: runRegistryOwner,
|
|
482
591
|
repo: runRegistryRepo,
|
|
483
592
|
title: runDisplayTitle,
|
|
@@ -491,15 +600,15 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
491
600
|
pid: process.pid,
|
|
492
601
|
projection: initialProjection
|
|
493
602
|
});
|
|
494
|
-
workerProjectionConnection = connectWorkerProjection({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace, runId:
|
|
603
|
+
workerProjectionConnection = connectWorkerProjection({ baseUrl: registryBaseUrl(), namespaceKey: runRegistryNamespace, runId: roomId });
|
|
495
604
|
workerProjectionConnection.ready.catch((err) => console.error(`[rig-run] worker-projection-degraded (heartbeat remains authoritative): ${err instanceof Error ? err.message : String(err)}`));
|
|
496
605
|
pushWorkerProjection();
|
|
497
606
|
workerProjectionFiber = Effect.runFork(localRunChanges(projectRoot).pipe(Stream.debounce(Duration.millis(500)), Stream.runForEach(() => Effect.sync(() => pushWorkerProjection()))));
|
|
498
|
-
journal?.appendTimeline({ type: "registry-registered", roomId
|
|
499
|
-
console.log(`[rig-run] registry-registered roomId=${
|
|
607
|
+
journal?.appendTimeline({ type: "registry-registered", roomId });
|
|
608
|
+
console.log(`[rig-run] registry-registered roomId=${roomId}`);
|
|
500
609
|
runRegistryHeartbeat = setInterval(() => {
|
|
501
610
|
const heartbeatProjection = buildCurrentRegistryProjection();
|
|
502
|
-
runRegistry.heartbeatRoom(
|
|
611
|
+
runRegistry.heartbeatRoom(roomId, coerceRegistryStatus(heartbeatProjection.status, "running"), heartbeatProjection).catch((err) => console.error(`[rig-run] registry-heartbeat-failed ${err instanceof Error ? err.message : String(err)}`));
|
|
503
612
|
}, 15000);
|
|
504
613
|
if (typeof runRegistryHeartbeat.unref === "function")
|
|
505
614
|
runRegistryHeartbeat.unref();
|
|
@@ -508,15 +617,22 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
508
617
|
ctx.ui.notify("Rig run could not register to the discovery registry; it may not appear in Runs.", "warning");
|
|
509
618
|
}
|
|
510
619
|
}
|
|
620
|
+
return true;
|
|
511
621
|
} catch (error) {
|
|
622
|
+
if (error instanceof Error && error.message.startsWith("Collab host session id mismatch:")) {
|
|
623
|
+
publishFatalTerminal(error);
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
512
626
|
console.error(`[rig-run] collab-host-start-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
513
627
|
ctx.ui.notify("Rig run collab host could not reach the relay; session remains local.", "warning");
|
|
628
|
+
return true;
|
|
514
629
|
}
|
|
515
630
|
};
|
|
516
631
|
journal?.appendTimeline({ type: "stage", stage: "Connect", status: "running", detail: "run process session_start" });
|
|
517
632
|
journal?.appendTimeline({ type: "stage", stage: "Prepare", status: "completed", detail: "run process prepared" });
|
|
518
633
|
journal?.appendStatus("running", { actor: { kind: "agent" }, reason: "run process session started", force: true });
|
|
519
|
-
await startRunCollabHost()
|
|
634
|
+
if (!await startRunCollabHost())
|
|
635
|
+
return null;
|
|
520
636
|
if (taskIdAtStart) {
|
|
521
637
|
journal?.appendTimeline({ type: "stage", stage: "GitHub-sync", status: "running", detail: "reflecting rig:running to task source" });
|
|
522
638
|
try {
|
|
@@ -616,7 +732,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
616
732
|
const { command, gitCommand } = createEnvCloseoutRunners(process.env);
|
|
617
733
|
let closeoutStatusAdvanced = false;
|
|
618
734
|
try {
|
|
619
|
-
await
|
|
735
|
+
await runPipelineCloseout({
|
|
620
736
|
projectRoot,
|
|
621
737
|
runId,
|
|
622
738
|
taskId,
|
|
@@ -631,6 +747,7 @@ async function maybeStartRunProcessAutohost(api, ctx) {
|
|
|
631
747
|
journal?.appendStatus("validating", { actor: { kind: "agent" }, reason: "validating task before closeout", force: true });
|
|
632
748
|
await publishRunProjection("validating");
|
|
633
749
|
},
|
|
750
|
+
kernelJournal: journal?.kernel ?? null,
|
|
634
751
|
journalPhase: async (phase, outcome, detail) => {
|
|
635
752
|
journal?.appendCloseoutPhase({ phase, outcome, detail: detail ?? null });
|
|
636
753
|
if (!closeoutStatusAdvanced && phase !== "queued" && outcome === "started") {
|
|
@@ -722,8 +839,10 @@ export {
|
|
|
722
839
|
startRunProcessStallMonitor,
|
|
723
840
|
registryRunProjection,
|
|
724
841
|
reflectStoppedRunTaskSource,
|
|
842
|
+
produceWorkerPanelPayload,
|
|
725
843
|
maybeStartSpikeAutohost,
|
|
726
844
|
maybeStartRunProcessAutohost,
|
|
845
|
+
loadWorkerPanelRegistry,
|
|
727
846
|
dispatchRunNotifications,
|
|
728
847
|
detectRunControlText,
|
|
729
848
|
rigWorkerExtension as default,
|
|
@@ -733,8 +852,13 @@ export {
|
|
|
733
852
|
__rigRunWorkerTest,
|
|
734
853
|
__rigExtensionTest,
|
|
735
854
|
TRACKED_RUN_STALL_MS,
|
|
855
|
+
RUN_WORKER_PANEL_PLUGIN,
|
|
736
856
|
RUN_PROCESS_STEER_TIMEOUT_MS,
|
|
737
857
|
RUN_PROCESS_STALL_SWEEP_MS,
|
|
738
858
|
RUN_PROCESS_STALL_DETAIL,
|
|
859
|
+
RIG_SUPERVISOR_PANEL_ID,
|
|
860
|
+
RIG_RUN_STOP_PANEL_ACTION,
|
|
861
|
+
RIG_PANELS_CUSTOM_MESSAGE_TYPE,
|
|
862
|
+
RIG_CAPABILITY_PANEL_SLOT,
|
|
739
863
|
REGISTRY_PROJECTION_TIMELINE_LIMIT
|
|
740
864
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PanelRegistration, RunJournalProjection } from "@rig/contracts";
|
|
2
|
+
import type { RigPluginWithRuntime, RuntimePanelProducer } from "@rig/core";
|
|
3
|
+
export declare const RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels";
|
|
4
|
+
export declare const RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
|
|
5
|
+
export declare const RIG_CAPABILITY_PANEL_SLOT = "capability";
|
|
6
|
+
export declare const RIG_SUPERVISOR_PANEL_ID = "supervisor";
|
|
7
|
+
export interface WorkerPanelProducerContext {
|
|
8
|
+
readonly projectRoot: string;
|
|
9
|
+
readonly runId: string;
|
|
10
|
+
readonly folded: RunJournalProjection;
|
|
11
|
+
readonly taskIdAtStart?: string | null;
|
|
12
|
+
readonly runDisplayTitle: string;
|
|
13
|
+
}
|
|
14
|
+
export interface WorkerPanelRegistrySnapshot {
|
|
15
|
+
readonly registrations: readonly PanelRegistration[];
|
|
16
|
+
readonly producers: readonly RuntimePanelProducer[];
|
|
17
|
+
}
|
|
18
|
+
export declare function produceWorkerPanelPayload(producer: RuntimePanelProducer, context: WorkerPanelProducerContext): Promise<unknown>;
|
|
19
|
+
export declare const RUN_WORKER_PANEL_PLUGIN: RigPluginWithRuntime;
|
|
20
|
+
export declare function loadWorkerPanelRegistry(projectRoot: string): Promise<WorkerPanelRegistrySnapshot>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/run-worker/src/panel-plugin.ts
|
|
3
|
+
import { createProjectPluginHost, projectPluginResolutionWarningMessages } from "@rig/composition/project-plugins";
|
|
4
|
+
var RIG_PANELS_CUSTOM_MESSAGE_TYPE = "rig-panels";
|
|
5
|
+
var RIG_RUN_STOP_PANEL_ACTION = "rig-run:stop";
|
|
6
|
+
var RIG_CAPABILITY_PANEL_SLOT = "capability";
|
|
7
|
+
var RIG_SUPERVISOR_PANEL_ID = "supervisor";
|
|
8
|
+
var PANEL_PRODUCER_TIMEOUT_MS = 2000;
|
|
9
|
+
var RUN_SUPERVISOR_PANEL_REGISTRATION = {
|
|
10
|
+
id: RIG_SUPERVISOR_PANEL_ID,
|
|
11
|
+
slot: RIG_CAPABILITY_PANEL_SLOT,
|
|
12
|
+
title: "Supervisor",
|
|
13
|
+
capabilityId: "run.supervisor",
|
|
14
|
+
description: "Live run status, closeout progress, and operator stop control."
|
|
15
|
+
};
|
|
16
|
+
function buildSupervisorPanelPayload(context) {
|
|
17
|
+
const status = context.folded.status ?? "unknown";
|
|
18
|
+
const taskId = context.folded.record.taskId ?? context.taskIdAtStart;
|
|
19
|
+
const operatorActive = status === "running" || status === "validating" || status === "closing-out" || status === "needs-attention";
|
|
20
|
+
return {
|
|
21
|
+
status,
|
|
22
|
+
currentTask: taskId ? { id: taskId, title: context.runDisplayTitle } : null,
|
|
23
|
+
processed: context.folded.closeoutPhases.length,
|
|
24
|
+
succeeded: context.folded.closeoutPhases.filter((phase) => phase.outcome === "completed").length,
|
|
25
|
+
failed: context.folded.closeoutPhases.filter((phase) => phase.outcome === "failed").length,
|
|
26
|
+
skipped: 0,
|
|
27
|
+
plannedOrder: taskId ? [{ id: taskId, title: context.runDisplayTitle, status }] : [],
|
|
28
|
+
idleReason: operatorActive ? null : status,
|
|
29
|
+
stopActionId: operatorActive ? RIG_RUN_STOP_PANEL_ACTION : null,
|
|
30
|
+
closures: []
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
var RUN_SUPERVISOR_PANEL_PRODUCER = {
|
|
34
|
+
...RUN_SUPERVISOR_PANEL_REGISTRATION,
|
|
35
|
+
produce(context) {
|
|
36
|
+
return buildSupervisorPanelPayload(context);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
async function produceWorkerPanelPayload(producer, context) {
|
|
40
|
+
if (!producer.produce)
|
|
41
|
+
return;
|
|
42
|
+
let timeout;
|
|
43
|
+
try {
|
|
44
|
+
return await Promise.race([
|
|
45
|
+
Promise.resolve(producer.produce(context)),
|
|
46
|
+
new Promise((resolve) => {
|
|
47
|
+
timeout = setTimeout(() => resolve(undefined), PANEL_PRODUCER_TIMEOUT_MS);
|
|
48
|
+
})
|
|
49
|
+
]);
|
|
50
|
+
} finally {
|
|
51
|
+
clearTimeout(timeout);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
var RUN_WORKER_PANEL_PLUGIN = {
|
|
55
|
+
name: "@rig/run-worker-panels",
|
|
56
|
+
version: "0.0.0-alpha.1",
|
|
57
|
+
contributes: {
|
|
58
|
+
panels: [RUN_SUPERVISOR_PANEL_REGISTRATION]
|
|
59
|
+
},
|
|
60
|
+
__runtime: {
|
|
61
|
+
panels: [RUN_SUPERVISOR_PANEL_PRODUCER]
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
async function loadWorkerPanelRegistry(projectRoot) {
|
|
65
|
+
try {
|
|
66
|
+
const { host, resolved } = await createProjectPluginHost(projectRoot, {
|
|
67
|
+
mode: "compatibility-default-injection",
|
|
68
|
+
surfaceName: "run-worker-panel-registry",
|
|
69
|
+
surfaceExtras: [RUN_WORKER_PANEL_PLUGIN]
|
|
70
|
+
});
|
|
71
|
+
for (const message of projectPluginResolutionWarningMessages(resolved)) {
|
|
72
|
+
console.warn(`[rig-run] ${message}`);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
registrations: host.listPanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT),
|
|
76
|
+
producers: host.listExecutablePanels().filter((registration) => registration.slot === RIG_CAPABILITY_PANEL_SLOT)
|
|
77
|
+
};
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(`[rig-run] panel-registry-build-failed ${error instanceof Error ? error.message : String(error)}`);
|
|
80
|
+
return {
|
|
81
|
+
registrations: [RUN_SUPERVISOR_PANEL_REGISTRATION],
|
|
82
|
+
producers: [RUN_SUPERVISOR_PANEL_PRODUCER]
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
produceWorkerPanelPayload,
|
|
88
|
+
loadWorkerPanelRegistry,
|
|
89
|
+
RUN_WORKER_PANEL_PLUGIN,
|
|
90
|
+
RIG_SUPERVISOR_PANEL_ID,
|
|
91
|
+
RIG_RUN_STOP_PANEL_ACTION,
|
|
92
|
+
RIG_PANELS_CUSTOM_MESSAGE_TYPE,
|
|
93
|
+
RIG_CAPABILITY_PANEL_SLOT
|
|
94
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/run-worker",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.137",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Rig package",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -26,12 +26,14 @@
|
|
|
26
26
|
]
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@rig/bundle-default-lifecycle": "npm:@h-rig/bundle-default-lifecycle@0.0.6-alpha.135",
|
|
30
|
-
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.135",
|
|
31
|
-
"@rig/kernel": "npm:@h-rig/kernel@0.0.6-alpha.135",
|
|
32
|
-
"@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.135",
|
|
33
|
-
"@rig/relay-registry": "npm:@h-rig/relay-registry@0.0.6-alpha.135",
|
|
34
29
|
"@oh-my-pi/pi-coding-agent": "16.0.4",
|
|
30
|
+
"@rig/bundle-default-lifecycle": "npm:@h-rig/bundle-default-lifecycle@0.0.6-alpha.137",
|
|
31
|
+
"@rig/composition": "npm:@h-rig/composition@0.0.6-alpha.137",
|
|
32
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.137",
|
|
33
|
+
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.137",
|
|
34
|
+
"@rig/kernel": "npm:@h-rig/kernel@0.0.6-alpha.137",
|
|
35
|
+
"@rig/relay-registry": "npm:@h-rig/relay-registry@0.0.6-alpha.137",
|
|
36
|
+
"@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.137",
|
|
35
37
|
"effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@8881a9b"
|
|
36
38
|
}
|
|
37
39
|
}
|