@h-rig/cli 0.0.6-alpha.82 → 0.0.6-alpha.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/build-rig-binaries.js +40 -8
- package/dist/bin/rig.js +23402 -14577
- package/dist/src/app/board.js +217 -41
- package/dist/src/app-opentui/adapters/command.d.ts +2 -0
- package/dist/src/app-opentui/adapters/command.js +329 -0
- package/dist/src/app-opentui/adapters/common.js +2 -2
- package/dist/src/app-opentui/adapters/doctor.d.ts +0 -2
- package/dist/src/app-opentui/adapters/doctor.js +64 -39
- package/dist/src/app-opentui/adapters/family.d.ts +62 -0
- package/dist/src/app-opentui/adapters/family.js +14305 -0
- package/dist/src/app-opentui/adapters/fleet.d.ts +0 -2
- package/dist/src/app-opentui/adapters/fleet.js +90 -60
- package/dist/src/app-opentui/adapters/inbox.d.ts +12 -2
- package/dist/src/app-opentui/adapters/inbox.js +137 -78
- package/dist/src/app-opentui/adapters/init.d.ts +0 -2
- package/dist/src/app-opentui/adapters/init.js +85 -47
- package/dist/src/app-opentui/adapters/inspect.d.ts +52 -0
- package/dist/src/app-opentui/adapters/inspect.js +1024 -0
- package/dist/src/app-opentui/adapters/pi-attach.d.ts +15 -6
- package/dist/src/app-opentui/adapters/pi-attach.js +442 -125
- package/dist/src/app-opentui/adapters/pi.d.ts +23 -0
- package/dist/src/app-opentui/adapters/pi.js +363 -0
- package/dist/src/app-opentui/adapters/plugin.d.ts +84 -0
- package/dist/src/app-opentui/adapters/plugin.js +544 -0
- package/dist/src/app-opentui/adapters/repo.d.ts +37 -0
- package/dist/src/app-opentui/adapters/repo.js +186 -0
- package/dist/src/app-opentui/adapters/run-detail.d.ts +9 -2
- package/dist/src/app-opentui/adapters/run-detail.js +180 -63
- package/dist/src/app-opentui/adapters/server.d.ts +10 -2
- package/dist/src/app-opentui/adapters/server.js +191 -45
- package/dist/src/app-opentui/adapters/tasks.d.ts +11 -2
- package/dist/src/app-opentui/adapters/tasks.js +1123 -143
- package/dist/src/app-opentui/adapters/workspace.d.ts +49 -0
- package/dist/src/app-opentui/adapters/workspace.js +333 -0
- package/dist/src/app-opentui/autocomplete.d.ts +20 -0
- package/dist/src/app-opentui/autocomplete.js +576 -0
- package/dist/src/app-opentui/bootstrap.d.ts +1 -6
- package/dist/src/app-opentui/bootstrap.js +25252 -16474
- package/dist/src/app-opentui/command-palette.d.ts +3 -0
- package/dist/src/app-opentui/command-palette.js +1010 -0
- package/dist/src/app-opentui/command-pty-host.d.ts +62 -0
- package/dist/src/app-opentui/command-pty-host.js +248 -0
- package/dist/src/app-opentui/drone.js +8 -6
- package/dist/src/app-opentui/events.js +1 -1
- package/dist/src/app-opentui/fleet-stats.d.ts +32 -0
- package/dist/src/app-opentui/fleet-stats.js +114 -0
- package/dist/src/app-opentui/focus-manager.d.ts +14 -0
- package/dist/src/app-opentui/focus-manager.js +24 -0
- package/dist/src/app-opentui/index.js +5431 -2797
- package/dist/src/app-opentui/intent.js +179 -50
- package/dist/src/app-opentui/keymap.d.ts +21 -0
- package/dist/src/app-opentui/keymap.js +1748 -0
- package/dist/src/app-opentui/launch-routing.d.ts +16 -0
- package/dist/src/app-opentui/launch-routing.js +55 -0
- package/dist/src/app-opentui/layout.d.ts +7 -0
- package/dist/src/app-opentui/layout.js +13 -6
- package/dist/src/app-opentui/list-search.d.ts +24 -0
- package/dist/src/app-opentui/list-search.js +88 -1
- package/dist/src/app-opentui/pi-host-child.js +99 -17
- package/dist/src/app-opentui/pi-pty-host.d.ts +14 -0
- package/dist/src/app-opentui/pi-pty-host.js +30 -14
- package/dist/src/app-opentui/react/App.d.ts +9 -0
- package/dist/src/app-opentui/react/App.js +5144 -0
- package/dist/src/app-opentui/react/Backdrop.d.ts +5 -0
- package/dist/src/app-opentui/react/Backdrop.js +1834 -0
- package/dist/src/app-opentui/react/ChromeHost.d.ts +5 -0
- package/dist/src/app-opentui/react/ChromeHost.js +2942 -0
- package/dist/src/app-opentui/react/SceneFrameView.d.ts +7 -0
- package/dist/src/app-opentui/react/SceneFrameView.js +529 -0
- package/dist/src/app-opentui/react/context.d.ts +17 -0
- package/dist/src/app-opentui/react/context.js +37 -0
- package/dist/src/app-opentui/react/launch.d.ts +2 -0
- package/dist/src/app-opentui/react/launch.js +5743 -0
- package/dist/src/app-opentui/react/nav.d.ts +18 -0
- package/dist/src/app-opentui/react/nav.js +54 -0
- package/dist/src/app-opentui/react/scroll.d.ts +12 -0
- package/dist/src/app-opentui/react/scroll.js +21 -0
- package/dist/src/app-opentui/react/syntax.d.ts +2 -0
- package/dist/src/app-opentui/react/syntax.js +64 -0
- package/dist/src/app-opentui/registry.js +20428 -4828
- package/dist/src/app-opentui/render/ascii-fleet.d.ts +15 -0
- package/dist/src/app-opentui/render/ascii-fleet.js +82 -0
- package/dist/src/app-opentui/render/constants.d.ts +31 -0
- package/dist/src/app-opentui/render/constants.js +66 -0
- package/dist/src/app-opentui/render/graphics.d.ts +2 -1
- package/dist/src/app-opentui/render/graphics.js +228 -46
- package/dist/src/app-opentui/render/image-visual-layer-worker.js +469 -930
- package/dist/src/app-opentui/render/image-visual-layer.d.ts +25 -10
- package/dist/src/app-opentui/render/image-visual-layer.js +448 -329
- package/dist/src/app-opentui/render/native-host.d.ts +37 -0
- package/dist/src/app-opentui/render/native-host.js +179 -0
- package/dist/src/app-opentui/render/panel-layout.d.ts +38 -0
- package/dist/src/app-opentui/render/panel-layout.js +48 -0
- package/dist/src/app-opentui/render/panels.d.ts +2 -0
- package/dist/src/app-opentui/render/panels.js +78 -38
- package/dist/src/app-opentui/render/preloader.d.ts +10 -0
- package/dist/src/app-opentui/render/preloader.js +165 -0
- package/dist/src/app-opentui/render/scene.d.ts +53 -1
- package/dist/src/app-opentui/render/scene.js +195 -6
- package/dist/src/app-opentui/render/text.d.ts +7 -1
- package/dist/src/app-opentui/render/text.js +15 -8
- package/dist/src/app-opentui/render/type-bar.d.ts +2 -1
- package/dist/src/app-opentui/render/type-bar.js +113 -39
- package/dist/src/app-opentui/runtime-resources.d.ts +16 -0
- package/dist/src/app-opentui/runtime-resources.js +62 -0
- package/dist/src/app-opentui/runtime.d.ts +44 -1
- package/dist/src/app-opentui/runtime.js +5415 -2738
- package/dist/src/app-opentui/scenes/command.d.ts +3 -0
- package/dist/src/app-opentui/scenes/command.js +117 -0
- package/dist/src/app-opentui/scenes/doctor.d.ts +2 -1
- package/dist/src/app-opentui/scenes/doctor.js +221 -17
- package/dist/src/app-opentui/scenes/error.js +60 -20
- package/dist/src/app-opentui/scenes/family-domains/agent.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/agent.js +348 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/browser.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/dist.js +243 -0
- package/dist/src/app-opentui/scenes/family-domains/git.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/git.js +195 -0
- package/dist/src/app-opentui/scenes/family-domains/github.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/github.js +274 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/harness.js +152 -0
- package/dist/src/app-opentui/scenes/family-domains/index.d.ts +4 -0
- package/dist/src/app-opentui/scenes/family-domains/index.js +1679 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.d.ts +76 -0
- package/dist/src/app-opentui/scenes/family-domains/kit.js +305 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/profile.js +212 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/queue.js +146 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/remote.js +518 -0
- package/dist/src/app-opentui/scenes/family-domains/review.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/review.js +280 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/setup.js +267 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.d.ts +2 -0
- package/dist/src/app-opentui/scenes/family-domains/stats.js +370 -0
- package/dist/src/app-opentui/scenes/family.d.ts +3 -0
- package/dist/src/app-opentui/scenes/family.js +2144 -0
- package/dist/src/app-opentui/scenes/fleet.js +552 -43
- package/dist/src/app-opentui/scenes/handoff.js +342 -35
- package/dist/src/app-opentui/scenes/help.js +640 -56
- package/dist/src/app-opentui/scenes/inbox.d.ts +2 -1
- package/dist/src/app-opentui/scenes/inbox.js +329 -21
- package/dist/src/app-opentui/scenes/init.d.ts +2 -1
- package/dist/src/app-opentui/scenes/init.js +120 -34
- package/dist/src/app-opentui/scenes/inspect.d.ts +3 -0
- package/dist/src/app-opentui/scenes/inspect.js +793 -0
- package/dist/src/app-opentui/scenes/main.d.ts +2 -1
- package/dist/src/app-opentui/scenes/main.js +264 -29
- package/dist/src/app-opentui/scenes/pi.d.ts +3 -0
- package/dist/src/app-opentui/scenes/pi.js +508 -0
- package/dist/src/app-opentui/scenes/plugin.d.ts +3 -0
- package/dist/src/app-opentui/scenes/plugin.js +486 -0
- package/dist/src/app-opentui/scenes/repo.d.ts +3 -0
- package/dist/src/app-opentui/scenes/repo.js +424 -0
- package/dist/src/app-opentui/scenes/run-detail.d.ts +2 -1
- package/dist/src/app-opentui/scenes/run-detail.js +362 -35
- package/dist/src/app-opentui/scenes/server.d.ts +2 -1
- package/dist/src/app-opentui/scenes/server.js +243 -26
- package/dist/src/app-opentui/scenes/tasks.js +518 -41
- package/dist/src/app-opentui/scenes/workspace.d.ts +3 -0
- package/dist/src/app-opentui/scenes/workspace.js +426 -0
- package/dist/src/app-opentui/selectable.d.ts +19 -0
- package/dist/src/app-opentui/selectable.js +79 -0
- package/dist/src/app-opentui/state.js +129 -36
- package/dist/src/app-opentui/surface-catalog.d.ts +20 -0
- package/dist/src/app-opentui/surface-catalog.js +540 -0
- package/dist/src/app-opentui/terminal-capabilities.d.ts +7 -0
- package/dist/src/app-opentui/terminal-capabilities.js +15 -0
- package/dist/src/app-opentui/theme.d.ts +28 -6
- package/dist/src/app-opentui/theme.js +61 -8
- package/dist/src/app-opentui/types.d.ts +130 -5
- package/dist/src/commands/_authority-runs.d.ts +1 -1
- package/dist/src/commands/_authority-runs.js +2 -12
- package/dist/src/commands/_doctor-checks.js +62 -13
- package/dist/src/commands/_help-catalog.js +95 -15
- package/dist/src/commands/_operator-view.js +97 -15
- package/dist/src/commands/_pi-frontend.d.ts +2 -0
- package/dist/src/commands/_pi-frontend.js +97 -13
- package/dist/src/commands/_preflight.js +64 -14
- package/dist/src/commands/_server-client.js +82 -18
- package/dist/src/commands/_server-events.d.ts +26 -0
- package/dist/src/commands/_server-events.js +310 -0
- package/dist/src/commands/_snapshot-upload.js +62 -13
- package/dist/src/commands/agent.js +2 -12
- package/dist/src/commands/connect.js +7 -1
- package/dist/src/commands/doctor.js +62 -13
- package/dist/src/commands/github.js +144 -23
- package/dist/src/commands/inbox.js +62 -13
- package/dist/src/commands/init.js +82 -18
- package/dist/src/commands/inspect.js +62 -13
- package/dist/src/commands/run.js +100 -15
- package/dist/src/commands/server.js +71 -26
- package/dist/src/commands/setup.js +62 -13
- package/dist/src/commands/stats.js +157 -28
- package/dist/src/commands/task-run-driver.js +64 -25
- package/dist/src/commands/task.js +196 -43
- package/dist/src/commands.js +419 -123
- package/dist/src/index.js +426 -130
- package/package.json +11 -10
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.d.ts +0 -2
- package/dist/src/app-opentui/render/image-visual-layer-native-canvas.js +0 -1484
|
@@ -15,6 +15,87 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
17
|
|
|
18
|
+
// packages/cli/src/app-opentui/adapters/common.ts
|
|
19
|
+
function projectRootOf(ctx) {
|
|
20
|
+
const root = ctx.rig?.projectRoot ?? ctx.projectRoot ?? ctx.getState().projectRoot;
|
|
21
|
+
if (typeof root === "string" && root.trim())
|
|
22
|
+
return root;
|
|
23
|
+
throw new Error("App adapter requires a projectRoot.");
|
|
24
|
+
}
|
|
25
|
+
async function runtimeOf(ctx) {
|
|
26
|
+
if (ctx.rig && "runId" in ctx.rig && ctx.ensureRuntime === undefined) {
|
|
27
|
+
return ctx.rig;
|
|
28
|
+
}
|
|
29
|
+
if (ctx.ensureRuntime)
|
|
30
|
+
return ctx.ensureRuntime();
|
|
31
|
+
throw new Error("App adapter requires ensureRuntime() before this action can run.");
|
|
32
|
+
}
|
|
33
|
+
function normalizeAppError(error) {
|
|
34
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
35
|
+
const rawHint = error instanceof Error ? error.hint : undefined;
|
|
36
|
+
return {
|
|
37
|
+
message,
|
|
38
|
+
...typeof rawHint === "string" && rawHint.trim() ? { hint: rawHint.trim() } : {}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function emitStarted(ctx, label, optimistic) {
|
|
42
|
+
ctx.emit({ type: "action.started", label, ...optimistic ? { optimistic } : {} });
|
|
43
|
+
}
|
|
44
|
+
function emitProgress(ctx, label, detail, data) {
|
|
45
|
+
ctx.emit({ type: "action.progress", label, ...detail ? { detail } : {}, ...data ? { data } : {} });
|
|
46
|
+
}
|
|
47
|
+
function emitCompleted(ctx, label, data) {
|
|
48
|
+
ctx.emit({ type: "action.completed", label, ...data ? { data } : {} });
|
|
49
|
+
}
|
|
50
|
+
function emitFailed(ctx, label, error, data) {
|
|
51
|
+
if (data)
|
|
52
|
+
patchData(ctx, data);
|
|
53
|
+
const normalized = normalizeAppError(error);
|
|
54
|
+
ctx.emit({
|
|
55
|
+
type: "action.failed",
|
|
56
|
+
label,
|
|
57
|
+
message: normalized.message,
|
|
58
|
+
...normalized.hint ? { hint: normalized.hint } : {},
|
|
59
|
+
cause: error
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function patchData(ctx, data) {
|
|
63
|
+
ctx.emit({ type: "data.patch", data });
|
|
64
|
+
}
|
|
65
|
+
function patchFooter(ctx, footer) {
|
|
66
|
+
ctx.emit({ type: "footer.patch", footer });
|
|
67
|
+
}
|
|
68
|
+
function arrayFromPayload(value) {
|
|
69
|
+
if (Array.isArray(value)) {
|
|
70
|
+
return value.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
|
|
71
|
+
}
|
|
72
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
73
|
+
const entries = value.entries;
|
|
74
|
+
if (Array.isArray(entries))
|
|
75
|
+
return arrayFromPayload(entries);
|
|
76
|
+
}
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
function stringField(record, keys, fallback = "") {
|
|
80
|
+
for (const key of keys) {
|
|
81
|
+
const value = record[key];
|
|
82
|
+
if (typeof value === "string" && value.trim())
|
|
83
|
+
return value.trim();
|
|
84
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
85
|
+
return String(value);
|
|
86
|
+
}
|
|
87
|
+
return fallback;
|
|
88
|
+
}
|
|
89
|
+
function compactRecord(record, keys) {
|
|
90
|
+
const out = {};
|
|
91
|
+
for (const key of keys) {
|
|
92
|
+
const value = record[key];
|
|
93
|
+
if (value !== undefined && value !== null && value !== "")
|
|
94
|
+
out[key] = value;
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
18
99
|
// packages/cli/src/runner.ts
|
|
19
100
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
20
101
|
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
@@ -237,17 +318,21 @@ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
|
237
318
|
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
238
319
|
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
239
320
|
}
|
|
240
|
-
function
|
|
321
|
+
function readRemoteAuthState(projectRoot) {
|
|
241
322
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
242
323
|
if (!existsSync2(path))
|
|
243
324
|
return null;
|
|
244
325
|
try {
|
|
245
326
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
246
|
-
return
|
|
327
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
247
328
|
} catch {
|
|
248
329
|
return null;
|
|
249
330
|
}
|
|
250
331
|
}
|
|
332
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
333
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
334
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
335
|
+
}
|
|
251
336
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
252
337
|
const scopedKey = resolve2(projectRoot);
|
|
253
338
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -258,15 +343,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
258
343
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
259
344
|
}
|
|
260
345
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
261
|
-
const
|
|
262
|
-
|
|
346
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
347
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
348
|
+
}
|
|
349
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
350
|
+
const repo = readRepoConnection(projectRoot);
|
|
351
|
+
const slug = repo?.project?.trim();
|
|
352
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
263
353
|
return null;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
354
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
355
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
356
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
357
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
268
358
|
return null;
|
|
269
|
-
|
|
359
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
360
|
+
if (!checkoutBaseDir)
|
|
361
|
+
return null;
|
|
362
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
363
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
364
|
+
return inferred;
|
|
270
365
|
}
|
|
271
366
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
272
367
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -277,7 +372,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
277
372
|
if (selected?.connection.kind === "remote") {
|
|
278
373
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
279
374
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
280
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
375
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
281
376
|
return {
|
|
282
377
|
baseUrl: selected.connection.baseUrl,
|
|
283
378
|
authToken,
|
|
@@ -306,7 +401,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
306
401
|
if (!slug)
|
|
307
402
|
return null;
|
|
308
403
|
try {
|
|
309
|
-
const
|
|
404
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
405
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
406
|
+
url.searchParams.set("rt", authToken);
|
|
407
|
+
const response = await fetch(url, {
|
|
310
408
|
headers: mergeHeaders(undefined, authToken)
|
|
311
409
|
});
|
|
312
410
|
if (!response.ok)
|
|
@@ -333,10 +431,23 @@ function appendTaskFilterParams(url, filters) {
|
|
|
333
431
|
if (filters.limit !== undefined)
|
|
334
432
|
url.searchParams.set("limit", String(filters.limit));
|
|
335
433
|
}
|
|
434
|
+
function mergeCookie(existing, name, value) {
|
|
435
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
436
|
+
if (!existing?.trim())
|
|
437
|
+
return encoded;
|
|
438
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
439
|
+
return [...parts, encoded].join("; ");
|
|
440
|
+
}
|
|
441
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
442
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
443
|
+
}
|
|
336
444
|
function mergeHeaders(headers, authToken) {
|
|
337
445
|
const merged = new Headers(headers);
|
|
338
446
|
if (authToken) {
|
|
339
|
-
|
|
447
|
+
const bearer = `Bearer ${authToken}`;
|
|
448
|
+
merged.set("authorization", bearer);
|
|
449
|
+
merged.set("x-auth", bearer);
|
|
450
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
340
451
|
}
|
|
341
452
|
return merged;
|
|
342
453
|
}
|
|
@@ -396,15 +507,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
396
507
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
397
508
|
};
|
|
398
509
|
}
|
|
510
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
511
|
+
try {
|
|
512
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
513
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
514
|
+
} catch {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
519
|
+
return pathname === "/health" || pathname === "/api/health" || pathname === "/api/server/status" || pathname === "/api/server/project-root" || pathname.startsWith("/api/github/auth/") || pathname === "/api/projects" || pathname.startsWith("/api/projects/");
|
|
520
|
+
}
|
|
399
521
|
async function requestServerJson(context, pathname, init = {}) {
|
|
400
522
|
const server = await ensureServerForCli(context.projectRoot);
|
|
523
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
524
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
525
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
526
|
+
throw new CliError(`Remote server is selected for ${repo?.project ?? "this repo"}, but this checkout has no server-host project root link.`, 1, { hint: "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to repair the remote project link before task/run commands." });
|
|
527
|
+
}
|
|
401
528
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
402
529
|
if (server.serverProjectRoot)
|
|
403
530
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
531
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
532
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
533
|
+
}
|
|
404
534
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
405
535
|
let response;
|
|
406
536
|
try {
|
|
407
|
-
response = await fetch(
|
|
537
|
+
response = await fetch(requestUrl, {
|
|
408
538
|
...init,
|
|
409
539
|
headers
|
|
410
540
|
});
|
|
@@ -466,11 +596,26 @@ async function getGitHubAuthStatusViaServer(context) {
|
|
|
466
596
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
467
597
|
}
|
|
468
598
|
async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
599
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
600
|
+
const requestUrl = new URL(`${server.baseUrl}/api/github/auth/token`);
|
|
601
|
+
reportServerPhase("POST /api/github/auth/token\u2026");
|
|
602
|
+
let response;
|
|
603
|
+
try {
|
|
604
|
+
response = await fetch(requestUrl, {
|
|
605
|
+
method: "POST",
|
|
606
|
+
headers: { "content-type": "application/json" },
|
|
607
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot ?? server.serverProjectRoot ?? undefined })
|
|
608
|
+
});
|
|
609
|
+
} catch (error) {
|
|
610
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
611
|
+
throw new CliError(`Rig server auth bootstrap failed: ${error instanceof Error ? error.message : String(error)}
|
|
612
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
613
|
+
}
|
|
614
|
+
const payload = await response.json().catch(() => null);
|
|
615
|
+
if (!response.ok) {
|
|
616
|
+
const detail = payload && typeof payload === "object" && !Array.isArray(payload) ? String(payload.error ?? JSON.stringify(payload)) : `HTTP ${response.status}`;
|
|
617
|
+
throw new CliError(`Rig server auth bootstrap failed (${response.status}): ${detail}`, 1, { hint: "Re-run `rig github auth import-gh`, or check the selected server with `rig server status`." });
|
|
618
|
+
}
|
|
474
619
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
475
620
|
}
|
|
476
621
|
async function prepareRemoteCheckoutViaServer(context, input) {
|
|
@@ -978,18 +1123,8 @@ import {
|
|
|
978
1123
|
readJsonlFile,
|
|
979
1124
|
writeAuthorityRunRecord
|
|
980
1125
|
} from "@rig/runtime/control-plane/authority-files";
|
|
981
|
-
function normalizeRuntimeAdapter(
|
|
982
|
-
|
|
983
|
-
if (!normalized) {
|
|
984
|
-
return "pi";
|
|
985
|
-
}
|
|
986
|
-
if (normalized === "codex" || normalized === "codex-cli" || normalized === "codex-app-server" || normalized === "gpt-codex") {
|
|
987
|
-
return "codex";
|
|
988
|
-
}
|
|
989
|
-
if (normalized === "pi" || normalized === "rig-pi" || normalized === "@rig/pi") {
|
|
990
|
-
return "pi";
|
|
991
|
-
}
|
|
992
|
-
return "claude-code";
|
|
1126
|
+
function normalizeRuntimeAdapter(_value) {
|
|
1127
|
+
return "pi";
|
|
993
1128
|
}
|
|
994
1129
|
var init__authority_runs = __esm(() => {
|
|
995
1130
|
init__paths();
|
|
@@ -1107,7 +1242,8 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
1107
1242
|
}
|
|
1108
1243
|
}
|
|
1109
1244
|
const repo = readRepoConnection(context.projectRoot);
|
|
1110
|
-
|
|
1245
|
+
const remoteWithoutProjectLink = selectedServer?.connectionKind === "remote" && !repo?.project;
|
|
1246
|
+
checks.push(repo ? preflightCheck("project-link", "project linked to selected server", repo.project ? "pass" : remoteWithoutProjectLink ? "fail" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : " (missing project link)"}`, remoteWithoutProjectLink ? "Run `rig init --repair` or `rig init --yes --repo owner/repo --server remote` to restore the remote project link." : "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to selected server", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
|
|
1111
1247
|
try {
|
|
1112
1248
|
const auth = await request("/api/github/auth/status");
|
|
1113
1249
|
checks.push(isAuthenticated(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
|
|
@@ -1610,6 +1746,23 @@ function buildOperatorPiEnv(input) {
|
|
|
1610
1746
|
...input.serverProjectRoot ? { RIG_PROJECT_ROOT: input.serverProjectRoot } : {}
|
|
1611
1747
|
};
|
|
1612
1748
|
}
|
|
1749
|
+
function shouldRequireOperatorTranscript(status) {
|
|
1750
|
+
return typeof status === "string" && TERMINAL_RUN_STATUSES.has(status.trim().toLowerCase());
|
|
1751
|
+
}
|
|
1752
|
+
function missingOperatorTranscriptMessage(runId, status) {
|
|
1753
|
+
const shortRun = runId.trim().slice(0, 8) || "unknown";
|
|
1754
|
+
const statusText = typeof status === "string" && status.trim() ? status.trim() : "terminal";
|
|
1755
|
+
return `Run ${shortRun} is ${statusText}, but no worker Pi session transcript is recorded for this run. It likely failed before Pi started or predates full-session capture. Use \`rig run show ${runId}\` and \`rig run logs ${runId}\` for the lifecycle details.`;
|
|
1756
|
+
}
|
|
1757
|
+
function statusFromRunDetails(run) {
|
|
1758
|
+
if (!run || typeof run !== "object" || Array.isArray(run))
|
|
1759
|
+
return;
|
|
1760
|
+
const record = run;
|
|
1761
|
+
if (typeof record.status === "string")
|
|
1762
|
+
return record.status;
|
|
1763
|
+
const nested = record.run;
|
|
1764
|
+
return nested && typeof nested === "object" && !Array.isArray(nested) ? nested.status : undefined;
|
|
1765
|
+
}
|
|
1613
1766
|
async function prepareOperatorConsole(context, runId, tempSessionDir) {
|
|
1614
1767
|
const server = await ensureServerForCli(context.projectRoot);
|
|
1615
1768
|
const localCwd = join(homedir2(), ".rig", "drones", runId.slice(0, 8));
|
|
@@ -1639,6 +1792,13 @@ async function prepareOperatorConsole(context, runId, tempSessionDir) {
|
|
|
1639
1792
|
sessionFileArg = ["--session", localSessionPath];
|
|
1640
1793
|
}
|
|
1641
1794
|
} catch {}
|
|
1795
|
+
if (sessionFileArg.length === 0) {
|
|
1796
|
+
const run = await getRunDetailsViaServer(context, runId).catch(() => null);
|
|
1797
|
+
const status = statusFromRunDetails(run);
|
|
1798
|
+
if (shouldRequireOperatorTranscript(status)) {
|
|
1799
|
+
throw new OperatorTranscriptUnavailableError(missingOperatorTranscriptMessage(runId, status));
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1642
1802
|
return { server, sessionFileArg };
|
|
1643
1803
|
}
|
|
1644
1804
|
function trustDroneCwd(localCwd) {
|
|
@@ -1682,6 +1842,7 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
1682
1842
|
let detached = false;
|
|
1683
1843
|
try {
|
|
1684
1844
|
await runPiMain([
|
|
1845
|
+
"--offline",
|
|
1685
1846
|
"--no-extensions",
|
|
1686
1847
|
"--no-skills",
|
|
1687
1848
|
"--no-prompt-templates",
|
|
@@ -1709,10 +1870,17 @@ async function attachRunBundledPiFrontend(context, input) {
|
|
|
1709
1870
|
rendered: "stock Pi operator console with the pi-rig extension"
|
|
1710
1871
|
};
|
|
1711
1872
|
}
|
|
1712
|
-
var RIG_PI_THEME;
|
|
1873
|
+
var TERMINAL_RUN_STATUSES, OperatorTranscriptUnavailableError, RIG_PI_THEME;
|
|
1713
1874
|
var init__pi_frontend = __esm(() => {
|
|
1714
1875
|
init__server_client();
|
|
1715
1876
|
init__async_ui();
|
|
1877
|
+
TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
1878
|
+
OperatorTranscriptUnavailableError = class OperatorTranscriptUnavailableError extends Error {
|
|
1879
|
+
constructor(message2) {
|
|
1880
|
+
super(message2);
|
|
1881
|
+
this.name = "OperatorTranscriptUnavailableError";
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1716
1884
|
RIG_PI_THEME = {
|
|
1717
1885
|
$schema: "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
|
1718
1886
|
name: "rig",
|
|
@@ -1867,7 +2035,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
1867
2035
|
}
|
|
1868
2036
|
const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 2000));
|
|
1869
2037
|
let timelineCursor = snapshot.timelineCursor;
|
|
1870
|
-
while (!detached && !
|
|
2038
|
+
while (!detached && !TERMINAL_RUN_STATUSES2.has(runStatusFromPayload(snapshot.run))) {
|
|
1871
2039
|
await Bun.sleep(pollMs);
|
|
1872
2040
|
snapshot = await readOperatorSnapshot(context, input.runId, { timelineCursor });
|
|
1873
2041
|
timelineCursor = snapshot.timelineCursor;
|
|
@@ -1879,13 +2047,13 @@ async function attachRunOperatorView(context, input) {
|
|
|
1879
2047
|
}
|
|
1880
2048
|
return { ...snapshot, steered, detached };
|
|
1881
2049
|
}
|
|
1882
|
-
var
|
|
2050
|
+
var TERMINAL_RUN_STATUSES2;
|
|
1883
2051
|
var init__operator_view = __esm(() => {
|
|
1884
2052
|
init__server_client();
|
|
1885
2053
|
init__operator_surface();
|
|
1886
2054
|
init__pi_frontend();
|
|
1887
2055
|
init__async_ui();
|
|
1888
|
-
|
|
2056
|
+
TERMINAL_RUN_STATUSES2 = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
1889
2057
|
});
|
|
1890
2058
|
|
|
1891
2059
|
// packages/cli/src/commands/_cli-format.ts
|
|
@@ -2415,30 +2583,62 @@ var init__help_catalog = __esm(() => {
|
|
|
2415
2583
|
}
|
|
2416
2584
|
];
|
|
2417
2585
|
ADVANCED_GROUPS = [
|
|
2418
|
-
{
|
|
2419
|
-
|
|
2586
|
+
{
|
|
2587
|
+
name: "setup",
|
|
2588
|
+
summary: "Bootstrap/check local setup.",
|
|
2589
|
+
usage: ["rig setup <bootstrap|check|preflight>"],
|
|
2590
|
+
commands: [
|
|
2591
|
+
{ command: "bootstrap", description: "Bootstrap local setup dependencies.", primary: true },
|
|
2592
|
+
{ command: "check", description: "Check local setup state (toolchain, deps, config).", primary: true },
|
|
2593
|
+
{ command: "preflight", description: "Run preflight checks before a run.", primary: true }
|
|
2594
|
+
]
|
|
2595
|
+
},
|
|
2596
|
+
{
|
|
2597
|
+
name: "profile",
|
|
2598
|
+
summary: "Runtime profile/model defaults.",
|
|
2599
|
+
usage: ["rig profile <show|set>"],
|
|
2600
|
+
commands: [
|
|
2601
|
+
{ command: "show", description: "Show the active execution profile.", primary: true },
|
|
2602
|
+
{ command: "set [--model <model>] [--runtime <runtime>] [--plugin <plugin>]", description: "Set model/runtime/plugin profile defaults.", primary: true }
|
|
2603
|
+
]
|
|
2604
|
+
},
|
|
2420
2605
|
{
|
|
2421
2606
|
name: "review",
|
|
2422
2607
|
summary: "Inspect or change completion review gate policy.",
|
|
2423
2608
|
usage: ["rig review <show|set>"],
|
|
2424
2609
|
commands: [
|
|
2425
2610
|
{ command: "show", description: "Show current review gate settings." },
|
|
2426
|
-
{ command: "set <off|advisory|required> [--provider
|
|
2611
|
+
{ command: "set <off|advisory|required> [--provider <provider>]", description: "Change review strictness/provider (e.g. --provider greptile)." }
|
|
2427
2612
|
],
|
|
2428
2613
|
examples: ["rig review show", "rig review set required --provider greptile"],
|
|
2429
2614
|
next: ["Use `rig inbox approvals` for blocked run handoffs."]
|
|
2430
2615
|
},
|
|
2431
2616
|
{
|
|
2432
2617
|
name: "browser",
|
|
2433
|
-
summary: "Browser
|
|
2434
|
-
usage: ["rig browser <help|explain|demo|
|
|
2618
|
+
summary: "Browser workstation actions for browser-required tasks: launch, check, CDP probe, profile reset, and app-specific smokes.",
|
|
2619
|
+
usage: ["rig browser <help|explain|demo|hp-next|cdp-probe|profile-persistence|profile-lock-check|smoke-test> [options]"],
|
|
2435
2620
|
commands: [
|
|
2436
|
-
{ command: "help", description: "Rich browser command help (canonical: `rig browser help`)." },
|
|
2437
|
-
{ command: "explain", description: "Explain the browser-required task contract." },
|
|
2438
|
-
{ command: "demo", description: "Run
|
|
2439
|
-
{ command: "
|
|
2440
|
-
{ command: "hp-next
|
|
2441
|
-
|
|
2621
|
+
{ command: "help", description: "Rich browser command help (canonical: `rig browser help`).", primary: true },
|
|
2622
|
+
{ command: "explain", description: "Explain the browser-required task contract and runtime helper commands.", primary: true },
|
|
2623
|
+
{ command: "demo [--port <n>] [--profile <name>] [--state-dir <path>] [--target-url <url>] [--keep-open] [--no-build]", description: "Run a guided real browser/agent demo flow.", primary: true },
|
|
2624
|
+
{ command: "hp-next dev", description: "Start hp-next Rig Browser in dev/watch mode.", primary: true },
|
|
2625
|
+
{ command: "hp-next start", description: "Build/start the hp-next browser workstation without watch mode.", primary: true },
|
|
2626
|
+
{ command: "hp-next check", description: "Validate the hp-next CDP endpoint on the configured port.", primary: true },
|
|
2627
|
+
{ command: "hp-next e2e", description: "Run hp-next browser e2e smoke checks through Rig Browser.", primary: true },
|
|
2628
|
+
{ command: "hp-next reset", description: "Reset the hp-next browser profile when stale browser state is suspected.", primary: true },
|
|
2629
|
+
{ command: "cdp-probe", description: "Run an isolated CDP attach/evaluate/layout probe." },
|
|
2630
|
+
{ command: "profile-persistence", description: "Verify a named browser profile persists browser state." },
|
|
2631
|
+
{ command: "profile-lock-check", description: "Verify concurrent launches reject the same profile." },
|
|
2632
|
+
{ command: "smoke-test", description: "Run the browser package smoke test." }
|
|
2633
|
+
],
|
|
2634
|
+
examples: [
|
|
2635
|
+
"rig browser help",
|
|
2636
|
+
"rig browser hp-next check",
|
|
2637
|
+
"rig browser hp-next e2e",
|
|
2638
|
+
"rig browser cdp-probe",
|
|
2639
|
+
"rig browser profile-lock-check"
|
|
2640
|
+
],
|
|
2641
|
+
next: ["Inside a task run, use rig-browser-launch, rig-browser-check, and rig-browser-attach-info from the worker PATH."]
|
|
2442
2642
|
},
|
|
2443
2643
|
{
|
|
2444
2644
|
name: "pi",
|
|
@@ -2453,12 +2653,60 @@ var init__help_catalog = __esm(() => {
|
|
|
2453
2653
|
examples: ["rig pi search subagents", "rig pi add pi-subagents", "rig pi list"],
|
|
2454
2654
|
next: ["Config-managed extensions: declare `runtime: { pi: { packages: [...] } }` in rig.config.ts \u2014 workers pick them up automatically."]
|
|
2455
2655
|
},
|
|
2456
|
-
{
|
|
2457
|
-
|
|
2656
|
+
{
|
|
2657
|
+
name: "queue",
|
|
2658
|
+
summary: "Run task queues locally.",
|
|
2659
|
+
usage: ["rig queue run [--workers <n>] [--max-tasks <n>] [--action validate|verify|pipeline] [--isolation off|worktree] [--no-runtime-reuse] [--fail-fast] [--skip-project-sync]"],
|
|
2660
|
+
commands: [
|
|
2661
|
+
{ command: "run [--workers <n>] [--max-tasks <n>] [--action validate|verify|pipeline] [--isolation off|worktree] [--no-runtime-reuse] [--fail-fast] [--skip-project-sync]", description: "Process queue work: drain matching tasks with N workers, optional per-task action and isolation.", primary: true }
|
|
2662
|
+
]
|
|
2663
|
+
},
|
|
2664
|
+
{
|
|
2665
|
+
name: "agent",
|
|
2666
|
+
summary: "Runtime agent workspace helpers.",
|
|
2667
|
+
usage: ["rig agent <list|prepare|run|cleanup>"],
|
|
2668
|
+
commands: [
|
|
2669
|
+
{ command: "list", description: "List prepared agent runtimes.", primary: true },
|
|
2670
|
+
{ command: "prepare [--id <id>] [--mode <mode>] [--task <task>]", description: "Prepare an isolated agent runtime workspace.", primary: true },
|
|
2671
|
+
{ command: "run [--id <id>] [--mode <mode>] [--task <task>] [--skip-project-sync]", description: "Prepare (if needed) and run an agent in its workspace.", primary: true },
|
|
2672
|
+
{ command: "cleanup [--all] [--id <id>]", description: "Remove prepared agent workspaces.", primary: true }
|
|
2673
|
+
]
|
|
2674
|
+
},
|
|
2458
2675
|
{ name: "inspector", summary: "Event stream and drift scanners.", usage: ["rig inspector <stream|scan-upstream-drift>"], commands: [{ command: "stream", description: "Stream events." }] },
|
|
2459
|
-
{
|
|
2676
|
+
{
|
|
2677
|
+
name: "dist",
|
|
2678
|
+
summary: "Build/install packaged Rig CLI.",
|
|
2679
|
+
usage: ["rig dist <build|install|doctor|rebuild-agent>"],
|
|
2680
|
+
commands: [
|
|
2681
|
+
{ command: "build [--output-dir <dir>]", description: "Build the distributable Rig CLI.", primary: true },
|
|
2682
|
+
{ command: "install [--scope <scope>] [--path <path>]", description: "Install the built CLI to a scope/path.", primary: true },
|
|
2683
|
+
{ command: "doctor", description: "Diagnose the dist toolchain and install state.", primary: true },
|
|
2684
|
+
{ command: "rebuild-agent", description: "Rebuild the agent runtime image, pruning stale images.", primary: true }
|
|
2685
|
+
]
|
|
2686
|
+
},
|
|
2460
2687
|
{ name: "workspace", summary: "Workspace topology/service helpers.", usage: ["rig workspace <summary|topology|remote-hosts>"], commands: [{ command: "summary", description: "Show workspace summary." }] },
|
|
2461
|
-
{
|
|
2688
|
+
{
|
|
2689
|
+
name: "remote",
|
|
2690
|
+
summary: "Compatibility remote orchestration controls.",
|
|
2691
|
+
usage: ["rig remote <status|tasks|watch|pause|resume|continue|stop|refresh|add-iterations|remove-iterations|endpoint ...>"],
|
|
2692
|
+
commands: [
|
|
2693
|
+
{ command: "status [--remote <alias>]", description: "Show the remote orchestration state.", primary: true },
|
|
2694
|
+
{ command: "tasks [--remote <alias>]", description: "List the remote's tracked tasks.", primary: true },
|
|
2695
|
+
{ command: "watch [--remote <alias>] [--seconds <n>] [--event <type>]", description: "Stream remote orchestration events.", primary: true },
|
|
2696
|
+
{ command: "pause [--remote <alias>]", description: "Pause the running remote orchestration.", primary: true },
|
|
2697
|
+
{ command: "resume [--remote <alias>] [--max-workers <n>] [--max-iterations <n>] [--direct-merge]", description: "Resume the remote with optional tuning.", primary: true },
|
|
2698
|
+
{ command: "continue [--remote <alias>]", description: "Continue a paused remote orchestration.", primary: true },
|
|
2699
|
+
{ command: "stop [--remote <alias>]", description: "Stop the remote orchestration.", primary: true },
|
|
2700
|
+
{ command: "refresh [--remote <alias>]", description: "Refresh remote state.", primary: true },
|
|
2701
|
+
{ command: "add-iterations [--count <n>] [--remote <alias>]", description: "Grant the remote more iterations.", primary: true },
|
|
2702
|
+
{ command: "remove-iterations [--count <n>] [--remote <alias>]", description: "Reduce the remote's iteration budget.", primary: true },
|
|
2703
|
+
{ command: "endpoint list", description: "List configured remote endpoints.", primary: true },
|
|
2704
|
+
{ command: "endpoint add --alias <alias> --host <host> --port <n> [--token <token>]", description: "Register a remote endpoint.", primary: true },
|
|
2705
|
+
{ command: "endpoint remove --alias <alias>", description: "Remove a remote endpoint.", primary: true },
|
|
2706
|
+
{ command: "endpoint test [--alias <alias>]", description: "Test connectivity to a remote endpoint.", primary: true },
|
|
2707
|
+
{ command: "endpoint doctor", description: "Diagnose remote endpoint configuration.", primary: true }
|
|
2708
|
+
]
|
|
2709
|
+
},
|
|
2462
2710
|
{ name: "git", summary: "Pass through to Rig git-flow helper.", usage: ["rig git <args...>"], commands: [{ command: "<args...>", description: "Advanced git flow operations." }] },
|
|
2463
2711
|
{ name: "harness", summary: "Pass through to runtime harness CLI.", usage: ["rig harness <args...>"], commands: [{ command: "<args...>", description: "Advanced harness operations." }] },
|
|
2464
2712
|
{ name: "test", summary: "Project test wrappers.", usage: ["rig test <unit|e2e|all>"], commands: [{ command: "all", description: "Run configured project tests." }] }
|
|
@@ -2990,116 +3238,794 @@ var init_task = __esm(() => {
|
|
|
2990
3238
|
init__connection_state();
|
|
2991
3239
|
});
|
|
2992
3240
|
|
|
2993
|
-
// packages/cli/src/app-opentui/
|
|
2994
|
-
import {
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
3241
|
+
// packages/cli/src/app-opentui/pi-pty-host.ts
|
|
3242
|
+
import { fileURLToPath } from "url";
|
|
3243
|
+
import { basename } from "path";
|
|
3244
|
+
import { RGBA, StyledText, TextAttributes } from "@opentui/core";
|
|
3245
|
+
import { Terminal as XtermTerminal } from "@xterm/headless";
|
|
3246
|
+
function clampCols(cols) {
|
|
3247
|
+
return Math.max(MIN_COLS, Math.trunc(cols || 100));
|
|
3248
|
+
}
|
|
3249
|
+
function clampRows(rows) {
|
|
3250
|
+
return Math.max(MIN_ROWS, Math.min(MAX_ROWS, Math.trunc(rows || 35)));
|
|
3251
|
+
}
|
|
3252
|
+
function rgbaFromXtermColor(mode, value) {
|
|
3253
|
+
if (mode === 1 || mode === 2 || mode === XTERM_COLOR_MODE_INDEXED_16 || mode === XTERM_COLOR_MODE_INDEXED_256) {
|
|
3254
|
+
return RGBA.fromIndex(value);
|
|
3255
|
+
}
|
|
3256
|
+
if (mode === 3 || mode === XTERM_COLOR_MODE_RGB) {
|
|
3257
|
+
return RGBA.fromInts(value >> 16 & 255, value >> 8 & 255, value & 255);
|
|
3258
|
+
}
|
|
3259
|
+
return;
|
|
3260
|
+
}
|
|
3261
|
+
function textAttributesFromCell(cell) {
|
|
3262
|
+
let attributes = TextAttributes.NONE;
|
|
3263
|
+
if (cell.isBold())
|
|
3264
|
+
attributes |= TextAttributes.BOLD;
|
|
3265
|
+
if (cell.isDim())
|
|
3266
|
+
attributes |= TextAttributes.DIM;
|
|
3267
|
+
if (cell.isItalic())
|
|
3268
|
+
attributes |= TextAttributes.ITALIC;
|
|
3269
|
+
if (cell.isUnderline())
|
|
3270
|
+
attributes |= TextAttributes.UNDERLINE;
|
|
3271
|
+
if (cell.isBlink())
|
|
3272
|
+
attributes |= TextAttributes.BLINK;
|
|
3273
|
+
if (cell.isInverse())
|
|
3274
|
+
attributes |= TextAttributes.INVERSE;
|
|
3275
|
+
if (cell.isInvisible())
|
|
3276
|
+
attributes |= TextAttributes.HIDDEN;
|
|
3277
|
+
if (cell.isStrikethrough())
|
|
3278
|
+
attributes |= TextAttributes.STRIKETHROUGH;
|
|
3279
|
+
return attributes;
|
|
3280
|
+
}
|
|
3281
|
+
function sameRgba(a, b) {
|
|
3282
|
+
if (!a && !b)
|
|
3283
|
+
return true;
|
|
3284
|
+
return Boolean(a && b && a.equals(b));
|
|
3285
|
+
}
|
|
3286
|
+
function sameStyle(a, b) {
|
|
3287
|
+
return sameRgba(a.fg, b.fg) && sameRgba(a.bg, b.bg) && (a.attributes ?? 0) === (b.attributes ?? 0);
|
|
3288
|
+
}
|
|
3289
|
+
function styleFromCell(cell) {
|
|
2998
3290
|
return {
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
emit: (event) => runtime.emit(event),
|
|
3003
|
-
getState: () => runtime.getState()
|
|
3291
|
+
fg: rgbaFromXtermColor(cell.getFgColorMode(), cell.getFgColor()),
|
|
3292
|
+
bg: rgbaFromXtermColor(cell.getBgColorMode(), cell.getBgColor()),
|
|
3293
|
+
attributes: textAttributesFromCell(cell)
|
|
3004
3294
|
};
|
|
3005
3295
|
}
|
|
3006
|
-
function
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3296
|
+
function lineToStyledText(line, cols) {
|
|
3297
|
+
if (!line)
|
|
3298
|
+
return new StyledText([{ __isChunk: true, text: "" }]);
|
|
3299
|
+
const chunks = [];
|
|
3300
|
+
let run = "";
|
|
3301
|
+
let runStyle = null;
|
|
3302
|
+
const flush = () => {
|
|
3303
|
+
if (!run)
|
|
3304
|
+
return;
|
|
3305
|
+
chunks.push({
|
|
3306
|
+
__isChunk: true,
|
|
3307
|
+
text: run,
|
|
3308
|
+
...runStyle?.fg ? { fg: runStyle.fg } : {},
|
|
3309
|
+
...runStyle?.bg ? { bg: runStyle.bg } : {},
|
|
3310
|
+
...(runStyle?.attributes ?? 0) !== 0 ? { attributes: runStyle.attributes } : {}
|
|
3311
|
+
});
|
|
3312
|
+
run = "";
|
|
3313
|
+
};
|
|
3314
|
+
for (let index = 0;index < cols; index += 1) {
|
|
3315
|
+
const cell = line.getCell(index);
|
|
3316
|
+
if (!cell) {
|
|
3317
|
+
if (runStyle !== null)
|
|
3318
|
+
flush();
|
|
3319
|
+
runStyle = null;
|
|
3320
|
+
run += " ";
|
|
3321
|
+
continue;
|
|
3322
|
+
}
|
|
3323
|
+
if (cell.getWidth() === 0)
|
|
3324
|
+
continue;
|
|
3325
|
+
const style = styleFromCell(cell);
|
|
3326
|
+
if (!runStyle || !sameStyle(runStyle, style)) {
|
|
3327
|
+
flush();
|
|
3328
|
+
runStyle = style;
|
|
3329
|
+
}
|
|
3330
|
+
run += cell.getChars() || " ";
|
|
3015
3331
|
}
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3332
|
+
flush();
|
|
3333
|
+
return new StyledText(chunks.length > 0 ? chunks : [{ __isChunk: true, text: "" }]);
|
|
3334
|
+
}
|
|
3335
|
+
function childCommandPrefix() {
|
|
3336
|
+
const execName = basename(process.execPath).toLowerCase();
|
|
3337
|
+
const currentEntry = process.argv[1];
|
|
3338
|
+
if (execName === "bun" || execName === "bun.exe") {
|
|
3339
|
+
return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
|
|
3340
|
+
}
|
|
3341
|
+
return [process.execPath, "__opentui-pi-host"];
|
|
3019
3342
|
}
|
|
3020
|
-
function
|
|
3021
|
-
const
|
|
3022
|
-
const
|
|
3343
|
+
function withEnv(base) {
|
|
3344
|
+
const env = {};
|
|
3345
|
+
for (const [key, value] of Object.entries(base ?? process.env)) {
|
|
3346
|
+
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
|
|
3347
|
+
continue;
|
|
3348
|
+
if (typeof value === "string")
|
|
3349
|
+
env[key] = value;
|
|
3350
|
+
}
|
|
3023
3351
|
return {
|
|
3024
|
-
|
|
3025
|
-
|
|
3352
|
+
...env,
|
|
3353
|
+
TERM: "xterm-256color",
|
|
3354
|
+
COLORTERM: "truecolor",
|
|
3355
|
+
FORCE_COLOR: env.FORCE_COLOR ?? "1",
|
|
3356
|
+
RIG_OPENTUI_PI_HOST: "1"
|
|
3026
3357
|
};
|
|
3027
3358
|
}
|
|
3028
|
-
function
|
|
3029
|
-
|
|
3359
|
+
async function startPiPtyHost(options) {
|
|
3360
|
+
activeHost?.dispose("replace");
|
|
3361
|
+
const host = new PiPtyHost(options);
|
|
3362
|
+
activeHost = host;
|
|
3363
|
+
await host.start();
|
|
3364
|
+
return host;
|
|
3030
3365
|
}
|
|
3031
|
-
|
|
3032
|
-
|
|
3366
|
+
|
|
3367
|
+
class PiPtyHost {
|
|
3368
|
+
runId;
|
|
3369
|
+
projectRoot;
|
|
3370
|
+
onSnapshot;
|
|
3371
|
+
onExit;
|
|
3372
|
+
onError;
|
|
3373
|
+
terminal;
|
|
3374
|
+
disposables = [];
|
|
3375
|
+
decoder = new TextDecoder("utf-8");
|
|
3376
|
+
proc = null;
|
|
3377
|
+
pty = null;
|
|
3378
|
+
status = "starting";
|
|
3379
|
+
cols;
|
|
3380
|
+
rows;
|
|
3381
|
+
message = "starting bundled Pi";
|
|
3382
|
+
lastResizeError = null;
|
|
3383
|
+
exitCode;
|
|
3384
|
+
signal;
|
|
3385
|
+
notifyTimer = null;
|
|
3386
|
+
lastStreamKey = "";
|
|
3387
|
+
_disposed = false;
|
|
3388
|
+
constructor(options) {
|
|
3389
|
+
this.runId = options.runId;
|
|
3390
|
+
this.projectRoot = options.projectRoot;
|
|
3391
|
+
this.cols = clampCols(options.cols);
|
|
3392
|
+
this.rows = clampRows(options.rows);
|
|
3393
|
+
this.onSnapshot = options.onSnapshot;
|
|
3394
|
+
this.onExit = options.onExit;
|
|
3395
|
+
this.onError = options.onError;
|
|
3396
|
+
this.terminal = new XtermTerminal({
|
|
3397
|
+
allowProposedApi: true,
|
|
3398
|
+
cols: this.cols,
|
|
3399
|
+
rows: this.rows,
|
|
3400
|
+
scrollback: 1000
|
|
3401
|
+
});
|
|
3402
|
+
this.registerTerminalResponders();
|
|
3403
|
+
this.disposables.push(this.terminal.onWriteParsed(() => {
|
|
3404
|
+
if (this._disposed)
|
|
3405
|
+
return;
|
|
3406
|
+
const snapshot = this.createSnapshot();
|
|
3407
|
+
const key = snapshot.lines.join(`
|
|
3408
|
+
`);
|
|
3409
|
+
if (key === this.lastStreamKey)
|
|
3410
|
+
return;
|
|
3411
|
+
this.lastStreamKey = key;
|
|
3412
|
+
this.onSnapshot?.(snapshot);
|
|
3413
|
+
}));
|
|
3414
|
+
}
|
|
3415
|
+
get disposed() {
|
|
3416
|
+
return this._disposed;
|
|
3417
|
+
}
|
|
3418
|
+
get snapshot() {
|
|
3419
|
+
return this.createSnapshot();
|
|
3420
|
+
}
|
|
3421
|
+
async start() {
|
|
3422
|
+
if (this._disposed)
|
|
3423
|
+
throw new Error("Pi PTY host is disposed.");
|
|
3424
|
+
if (typeof Bun.Terminal !== "function") {
|
|
3425
|
+
throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
|
|
3426
|
+
}
|
|
3427
|
+
const spawnOptions = {
|
|
3428
|
+
cwd: this.projectRoot,
|
|
3429
|
+
env: withEnv(process.env),
|
|
3430
|
+
terminal: {
|
|
3431
|
+
cols: this.cols,
|
|
3432
|
+
rows: this.rows,
|
|
3433
|
+
name: "xterm-256color",
|
|
3434
|
+
data: (_terminal, data) => this.handlePtyData(data)
|
|
3435
|
+
}
|
|
3436
|
+
};
|
|
3437
|
+
const proc = Bun.spawn([
|
|
3438
|
+
...childCommandPrefix(),
|
|
3439
|
+
"--run-id",
|
|
3440
|
+
this.runId,
|
|
3441
|
+
"--project-root",
|
|
3442
|
+
this.projectRoot
|
|
3443
|
+
], spawnOptions);
|
|
3444
|
+
if (!proc.terminal)
|
|
3445
|
+
throw new Error("Bun did not attach a terminal to the bundled Pi child process.");
|
|
3446
|
+
this.proc = proc;
|
|
3447
|
+
this.pty = proc.terminal;
|
|
3448
|
+
this.status = "running";
|
|
3449
|
+
this.message = "bundled Pi running inside this app";
|
|
3450
|
+
this.emitSnapshotSoon(0);
|
|
3451
|
+
proc.exited.then((exitCode) => {
|
|
3452
|
+
if (this._disposed)
|
|
3453
|
+
return;
|
|
3454
|
+
this.status = exitCode === 0 ? "exited" : "failed";
|
|
3455
|
+
this.exitCode = exitCode;
|
|
3456
|
+
this.signal = null;
|
|
3457
|
+
this.message = exitCode === 0 ? "bundled Pi exited" : `bundled Pi exited with code ${exitCode}`;
|
|
3458
|
+
const snapshot = this.createSnapshot();
|
|
3459
|
+
this.onSnapshot?.(snapshot);
|
|
3460
|
+
this.onExit?.(snapshot);
|
|
3461
|
+
if (activeHost === this)
|
|
3462
|
+
activeHost = null;
|
|
3463
|
+
this.dispose("exit", { kill: false, notify: false });
|
|
3464
|
+
}).catch((error) => {
|
|
3465
|
+
if (this._disposed)
|
|
3466
|
+
return;
|
|
3467
|
+
this.status = "failed";
|
|
3468
|
+
this.message = error instanceof Error ? error.message : String(error);
|
|
3469
|
+
const snapshot = this.createSnapshot();
|
|
3470
|
+
this.onSnapshot?.(snapshot);
|
|
3471
|
+
this.onError?.(error, snapshot);
|
|
3472
|
+
if (activeHost === this)
|
|
3473
|
+
activeHost = null;
|
|
3474
|
+
this.dispose("error", { kill: false, notify: false });
|
|
3475
|
+
});
|
|
3476
|
+
}
|
|
3477
|
+
write(data) {
|
|
3478
|
+
if (this._disposed || !this.pty)
|
|
3479
|
+
return;
|
|
3480
|
+
try {
|
|
3481
|
+
this.pty.write(data);
|
|
3482
|
+
} catch (error) {
|
|
3483
|
+
this.status = "failed";
|
|
3484
|
+
this.message = error instanceof Error ? error.message : String(error);
|
|
3485
|
+
const snapshot = this.createSnapshot();
|
|
3486
|
+
this.onSnapshot?.(snapshot);
|
|
3487
|
+
this.onError?.(error, snapshot);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
resize(cols, rows) {
|
|
3491
|
+
const nextCols = clampCols(cols);
|
|
3492
|
+
const nextRows = clampRows(rows);
|
|
3493
|
+
if (nextCols === this.cols && nextRows === this.rows)
|
|
3494
|
+
return;
|
|
3495
|
+
this.cols = nextCols;
|
|
3496
|
+
this.rows = nextRows;
|
|
3497
|
+
this.terminal.resize(nextCols, nextRows);
|
|
3498
|
+
try {
|
|
3499
|
+
this.pty?.resize(nextCols, nextRows);
|
|
3500
|
+
this.lastResizeError = null;
|
|
3501
|
+
} catch (error) {
|
|
3502
|
+
this.lastResizeError = error instanceof Error ? error.message : String(error);
|
|
3503
|
+
}
|
|
3504
|
+
this.emitSnapshotSoon(0);
|
|
3505
|
+
}
|
|
3506
|
+
detach() {
|
|
3507
|
+
this.dispose("detach");
|
|
3508
|
+
return this.createSnapshot("detached from bundled Pi");
|
|
3509
|
+
}
|
|
3510
|
+
dispose(reason = "dispose", options = {}) {
|
|
3511
|
+
if (this._disposed)
|
|
3512
|
+
return;
|
|
3513
|
+
this._disposed = true;
|
|
3514
|
+
if (this.notifyTimer)
|
|
3515
|
+
clearTimeout(this.notifyTimer);
|
|
3516
|
+
this.notifyTimer = null;
|
|
3517
|
+
for (const disposable of this.disposables.splice(0)) {
|
|
3518
|
+
try {
|
|
3519
|
+
disposable.dispose();
|
|
3520
|
+
} catch {}
|
|
3521
|
+
}
|
|
3522
|
+
if (options.kill !== false) {
|
|
3523
|
+
try {
|
|
3524
|
+
this.proc?.kill("SIGTERM");
|
|
3525
|
+
} catch {}
|
|
3526
|
+
}
|
|
3527
|
+
try {
|
|
3528
|
+
this.pty?.close();
|
|
3529
|
+
} catch {}
|
|
3530
|
+
try {
|
|
3531
|
+
this.terminal.dispose();
|
|
3532
|
+
} catch {}
|
|
3533
|
+
if (activeHost === this)
|
|
3534
|
+
activeHost = null;
|
|
3535
|
+
if (options.notify) {
|
|
3536
|
+
this.message = reason;
|
|
3537
|
+
this.onSnapshot?.(this.createSnapshot(reason));
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
handlePtyData(data) {
|
|
3541
|
+
if (this._disposed)
|
|
3542
|
+
return;
|
|
3543
|
+
const text = this.decoder.decode(data, { stream: true });
|
|
3544
|
+
this.respondToRawTerminalQueries(text);
|
|
3545
|
+
this.terminal.write(data);
|
|
3546
|
+
}
|
|
3547
|
+
emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
|
|
3548
|
+
if (this._disposed || this.notifyTimer)
|
|
3549
|
+
return;
|
|
3550
|
+
this.notifyTimer = setTimeout(() => {
|
|
3551
|
+
this.notifyTimer = null;
|
|
3552
|
+
if (this._disposed)
|
|
3553
|
+
return;
|
|
3554
|
+
this.onSnapshot?.(this.createSnapshot());
|
|
3555
|
+
}, delayMs);
|
|
3556
|
+
}
|
|
3557
|
+
createSnapshot(message2 = this.message) {
|
|
3558
|
+
const buffer = this.terminal.buffer.active;
|
|
3559
|
+
const end = buffer.length;
|
|
3560
|
+
const start = Math.max(0, end - MAX_SNAPSHOT_LINES);
|
|
3561
|
+
const styledStart = Math.max(start, end - this.rows - STYLED_SNAPSHOT_MARGIN);
|
|
3562
|
+
const lines = [];
|
|
3563
|
+
const styledLines = [];
|
|
3564
|
+
for (let row = start;row < end; row += 1) {
|
|
3565
|
+
const line = buffer.getLine(row);
|
|
3566
|
+
lines.push((line?.translateToString(false) ?? "").slice(0, this.cols));
|
|
3567
|
+
if (row >= styledStart)
|
|
3568
|
+
styledLines[lines.length - 1] = lineToStyledText(line, this.cols);
|
|
3569
|
+
}
|
|
3570
|
+
while (lines.length < this.rows) {
|
|
3571
|
+
lines.push("");
|
|
3572
|
+
styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
|
|
3573
|
+
}
|
|
3574
|
+
const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message2;
|
|
3575
|
+
return {
|
|
3576
|
+
runId: this.runId,
|
|
3577
|
+
status: this.status,
|
|
3578
|
+
cols: this.cols,
|
|
3579
|
+
rows: this.rows,
|
|
3580
|
+
lines,
|
|
3581
|
+
styledLines,
|
|
3582
|
+
message: resolvedMessage,
|
|
3583
|
+
...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
|
|
3584
|
+
...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
|
|
3585
|
+
...this.signal !== undefined ? { signal: this.signal } : {}
|
|
3586
|
+
};
|
|
3587
|
+
}
|
|
3588
|
+
registerTerminalResponders() {
|
|
3589
|
+
this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "c" }, (params) => {
|
|
3590
|
+
if (params.length === 0 || params[0] === 0)
|
|
3591
|
+
this.write("\x1B[?62;22c");
|
|
3592
|
+
return false;
|
|
3593
|
+
}));
|
|
3594
|
+
this.disposables.push(this.terminal.parser.registerCsiHandler({ prefix: ">", final: "c" }, (params) => {
|
|
3595
|
+
if (params.length === 0 || params[0] === 0)
|
|
3596
|
+
this.write("\x1B[>0;0;0c");
|
|
3597
|
+
return false;
|
|
3598
|
+
}));
|
|
3599
|
+
this.disposables.push(this.terminal.parser.registerCsiHandler({ final: "n" }, (params) => {
|
|
3600
|
+
if (params[0] === 5) {
|
|
3601
|
+
this.write("\x1B[0n");
|
|
3602
|
+
} else if (params[0] === 6) {
|
|
3603
|
+
const row = this.terminal.buffer.active.cursorY + 1;
|
|
3604
|
+
const col = this.terminal.buffer.active.cursorX + 1;
|
|
3605
|
+
this.write(`\x1B[${row};${col}R`);
|
|
3606
|
+
}
|
|
3607
|
+
return false;
|
|
3608
|
+
}));
|
|
3609
|
+
}
|
|
3610
|
+
respondToRawTerminalQueries(text) {
|
|
3611
|
+
if (!text)
|
|
3612
|
+
return;
|
|
3613
|
+
const decrqm = /\x1b\[\?(\d+)\$p/g;
|
|
3614
|
+
let match;
|
|
3615
|
+
while ((match = decrqm.exec(text)) !== null) {
|
|
3616
|
+
this.write(`\x1B[?${match[1]};2$y`);
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3033
3619
|
}
|
|
3034
|
-
|
|
3035
|
-
|
|
3620
|
+
var MIN_COLS = 40, MIN_ROWS = 12, MAX_ROWS = 300, MAX_SNAPSHOT_LINES = 360, STYLED_SNAPSHOT_MARGIN = 6, SNAPSHOT_DELAY_MS = 120, fallbackChildScriptPath, activeHost = null, XTERM_COLOR_MODE_INDEXED_16 = 16777216, XTERM_COLOR_MODE_INDEXED_256 = 33554432, XTERM_COLOR_MODE_RGB = 50331648;
|
|
3621
|
+
var init_pi_pty_host = __esm(() => {
|
|
3622
|
+
fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
|
|
3623
|
+
});
|
|
3624
|
+
|
|
3625
|
+
// packages/cli/src/app-opentui/adapters/fleet.ts
|
|
3626
|
+
function normalizeRunRecord(record) {
|
|
3627
|
+
const runId = stringField(record, ["runId", "id"]);
|
|
3628
|
+
if (!runId)
|
|
3629
|
+
return null;
|
|
3630
|
+
const title = stringField(record, ["title", "taskTitle", "taskId"], "untitled run");
|
|
3631
|
+
const taskId3 = stringField(record, ["taskId"]);
|
|
3632
|
+
const updatedAt = stringField(record, ["updatedAt", "createdAt"]);
|
|
3633
|
+
return {
|
|
3634
|
+
runId,
|
|
3635
|
+
status: stringField(record, ["status", "state"], "unknown"),
|
|
3636
|
+
title,
|
|
3637
|
+
raw: record,
|
|
3638
|
+
...taskId3 ? { taskId: taskId3 } : {},
|
|
3639
|
+
...updatedAt ? { updatedAt } : {}
|
|
3640
|
+
};
|
|
3036
3641
|
}
|
|
3037
|
-
function
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
label,
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3642
|
+
async function refreshFleet(ctx, options = {}) {
|
|
3643
|
+
const label = "Refreshing fleet";
|
|
3644
|
+
emitStarted(ctx, label);
|
|
3645
|
+
try {
|
|
3646
|
+
const { listRunsViaServer: listRunsViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
3647
|
+
const projectRoot = projectRootOf(ctx);
|
|
3648
|
+
emitProgress(ctx, label, "contacting selected server");
|
|
3649
|
+
const [runs, serverLabel] = await Promise.all([
|
|
3650
|
+
listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 }),
|
|
3651
|
+
resolveServerConnectionLabel2(projectRoot)
|
|
3652
|
+
]);
|
|
3653
|
+
const normalized = runs.flatMap((run) => {
|
|
3654
|
+
const entry = normalizeRunRecord(run);
|
|
3655
|
+
return entry ? [entry] : [];
|
|
3656
|
+
});
|
|
3657
|
+
patchData(ctx, { fleet: { runs: normalized, refreshedAt: new Date().toISOString() }, lastRefreshError: undefined });
|
|
3658
|
+
patchFooter(ctx, { server: serverLabel, runs: String(normalized.length) });
|
|
3659
|
+
emitCompleted(ctx, label, { count: normalized.length });
|
|
3660
|
+
return normalized;
|
|
3661
|
+
} catch (error) {
|
|
3662
|
+
emitFailed(ctx, label, error);
|
|
3663
|
+
throw error;
|
|
3664
|
+
}
|
|
3048
3665
|
}
|
|
3049
|
-
|
|
3050
|
-
|
|
3666
|
+
var init_fleet = () => {};
|
|
3667
|
+
|
|
3668
|
+
// packages/cli/src/app-opentui/adapters/inspect.ts
|
|
3669
|
+
var exports_inspect = {};
|
|
3670
|
+
__export(exports_inspect, {
|
|
3671
|
+
loadInspectRuns: () => loadInspectRuns,
|
|
3672
|
+
loadInspect: () => loadInspect,
|
|
3673
|
+
INSPECT_VIEWS: () => INSPECT_VIEWS
|
|
3674
|
+
});
|
|
3675
|
+
import { execFile } from "child_process";
|
|
3676
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync, statSync } from "fs";
|
|
3677
|
+
import { resolve as resolve4 } from "path";
|
|
3678
|
+
import { promisify } from "util";
|
|
3679
|
+
import { resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
3680
|
+
import { readTaskArtifactPreview } from "@rig/runtime/control-plane/native/workspace-ops";
|
|
3681
|
+
import { resolveHarnessPaths, resolveMonorepoRoot as resolveMonorepoRoot2, runCapture } from "@rig/runtime/control-plane/native/utils";
|
|
3682
|
+
function isFailureEntry(entry) {
|
|
3683
|
+
const status = `${entry.status ?? entry.level ?? entry.type ?? ""}`.toLowerCase();
|
|
3684
|
+
return status.includes("fail") || status.includes("error");
|
|
3685
|
+
}
|
|
3686
|
+
async function gitDiff(projectRoot) {
|
|
3687
|
+
try {
|
|
3688
|
+
const { stdout } = await execFileAsync("git", ["diff", "--no-color"], { cwd: projectRoot, maxBuffer: 8 * 1024 * 1024 });
|
|
3689
|
+
return { diff: stdout };
|
|
3690
|
+
} catch (error) {
|
|
3691
|
+
return { diff: "", error: normalizeAppError(error).message };
|
|
3692
|
+
}
|
|
3051
3693
|
}
|
|
3052
|
-
function
|
|
3053
|
-
|
|
3694
|
+
function listArtifacts(projectRoot, taskId3) {
|
|
3695
|
+
if (!taskId3)
|
|
3696
|
+
return { artifacts: [], error: "run has no task id \u2014 artifacts are resolved per task" };
|
|
3697
|
+
try {
|
|
3698
|
+
const dirs = resolveTaskArtifactDirs(projectRoot, taskId3).filter((dir) => existsSync4(dir));
|
|
3699
|
+
const out = [];
|
|
3700
|
+
const seen = new Set;
|
|
3701
|
+
for (const dir of dirs) {
|
|
3702
|
+
for (const name of readdirSync(dir)) {
|
|
3703
|
+
if (seen.has(name))
|
|
3704
|
+
continue;
|
|
3705
|
+
seen.add(name);
|
|
3706
|
+
const path = resolve4(dir, name);
|
|
3707
|
+
try {
|
|
3708
|
+
const stat = statSync(path);
|
|
3709
|
+
out.push({ name, path, sizeBytes: stat.size, isDir: stat.isDirectory() });
|
|
3710
|
+
} catch {
|
|
3711
|
+
out.push({ name, path, sizeBytes: 0, isDir: false });
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
out.sort((left, right) => left.name.localeCompare(right.name));
|
|
3716
|
+
return { artifacts: out };
|
|
3717
|
+
} catch (error) {
|
|
3718
|
+
return { artifacts: [], error: normalizeAppError(error).message };
|
|
3719
|
+
}
|
|
3054
3720
|
}
|
|
3055
|
-
function
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3721
|
+
function readArtifact(projectRoot, taskId3, fileName) {
|
|
3722
|
+
if (!taskId3)
|
|
3723
|
+
return;
|
|
3724
|
+
try {
|
|
3725
|
+
const preview = readTaskArtifactPreview(projectRoot, taskId3, fileName);
|
|
3726
|
+
return { name: fileName, contents: preview.contents, truncated: preview.truncated, sizeBytes: preview.sizeBytes };
|
|
3727
|
+
} catch (error) {
|
|
3728
|
+
return { name: fileName, contents: `Failed to read artifact: ${normalizeAppError(error).message}`, truncated: false, sizeBytes: 0 };
|
|
3062
3729
|
}
|
|
3063
|
-
return fallback;
|
|
3064
3730
|
}
|
|
3065
|
-
function
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3731
|
+
function loadGraph(projectRoot) {
|
|
3732
|
+
try {
|
|
3733
|
+
const result = runCapture(["br", "--no-db", "list", "--pretty"], resolveMonorepoRoot2(projectRoot));
|
|
3734
|
+
if (result.exitCode !== 0)
|
|
3735
|
+
return { graph: "", error: result.stderr || result.stdout || "failed to inspect graph" };
|
|
3736
|
+
return { graph: result.stdout };
|
|
3737
|
+
} catch (error) {
|
|
3738
|
+
return { graph: "", error: normalizeAppError(error).message };
|
|
3071
3739
|
}
|
|
3072
|
-
return out;
|
|
3073
3740
|
}
|
|
3074
|
-
function
|
|
3075
|
-
|
|
3076
|
-
|
|
3741
|
+
function loadAudit(projectRoot) {
|
|
3742
|
+
try {
|
|
3743
|
+
const auditPath = resolve4(resolveHarnessPaths(projectRoot).logsDir, "audit.jsonl");
|
|
3744
|
+
if (!existsSync4(auditPath))
|
|
3745
|
+
return [];
|
|
3746
|
+
return readFileSync5(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-50).flatMap((raw) => {
|
|
3747
|
+
try {
|
|
3748
|
+
const parsed = JSON.parse(raw);
|
|
3749
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? [parsed] : [{ detail: raw }];
|
|
3750
|
+
} catch {
|
|
3751
|
+
return [{ detail: raw }];
|
|
3752
|
+
}
|
|
3753
|
+
});
|
|
3754
|
+
} catch {
|
|
3755
|
+
return [];
|
|
3756
|
+
}
|
|
3077
3757
|
}
|
|
3758
|
+
async function loadInspectRuns(ctx, options = {}) {
|
|
3759
|
+
const label = "Loading runs to inspect";
|
|
3760
|
+
emitStarted(ctx, label);
|
|
3761
|
+
try {
|
|
3762
|
+
const projectRoot = projectRootOf(ctx);
|
|
3763
|
+
const { listRunsViaServer: listRunsViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
3764
|
+
emitProgress(ctx, label, "listing recent runs");
|
|
3765
|
+
const runs = await listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 });
|
|
3766
|
+
const options_ = runs.flatMap((record) => {
|
|
3767
|
+
const runId = stringField(record, ["runId", "id"]);
|
|
3768
|
+
if (!runId)
|
|
3769
|
+
return [];
|
|
3770
|
+
const taskId3 = stringField(record, ["taskId"]);
|
|
3771
|
+
const updatedAt = stringField(record, ["updatedAt", "createdAt"]);
|
|
3772
|
+
return [{
|
|
3773
|
+
runId,
|
|
3774
|
+
status: stringField(record, ["status", "state"], "unknown"),
|
|
3775
|
+
title: stringField(record, ["title", "taskTitle", "taskId"], "untitled run"),
|
|
3776
|
+
...taskId3 ? { taskId: taskId3 } : {},
|
|
3777
|
+
...updatedAt ? { updatedAt } : {}
|
|
3778
|
+
}];
|
|
3779
|
+
});
|
|
3780
|
+
patchData(ctx, { inspectRuns: options_, lastRefreshError: undefined });
|
|
3781
|
+
emitCompleted(ctx, label, { count: options_.length });
|
|
3782
|
+
return options_;
|
|
3783
|
+
} catch (error) {
|
|
3784
|
+
emitFailed(ctx, label, error);
|
|
3785
|
+
throw error;
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
async function loadInspect(ctx, runId) {
|
|
3789
|
+
const cleanRunId = runId.trim();
|
|
3790
|
+
const label = `Inspecting run ${cleanRunId.slice(0, 8)}`;
|
|
3791
|
+
if (!cleanRunId) {
|
|
3792
|
+
await loadInspectRuns(ctx).catch(() => []);
|
|
3793
|
+
const error = new Error("Inspect requires a run id.");
|
|
3794
|
+
throw error;
|
|
3795
|
+
}
|
|
3796
|
+
emitStarted(ctx, label);
|
|
3797
|
+
try {
|
|
3798
|
+
const projectRoot = projectRootOf(ctx);
|
|
3799
|
+
const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunLogsViaServer: getRunLogsViaServer2, getRunTimelineViaServer: getRunTimelineViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
3800
|
+
const context = { projectRoot };
|
|
3801
|
+
emitProgress(ctx, label, "loading logs, timeline, diff, artifacts, graph, and audit");
|
|
3802
|
+
const [runPayload, logsPayload, timelinePayload, diffResult] = await Promise.all([
|
|
3803
|
+
getRunDetailsViaServer2(context, cleanRunId).catch((error) => ({ error: normalizeAppError(error).message })),
|
|
3804
|
+
getRunLogsViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
|
|
3805
|
+
getRunTimelineViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
|
|
3806
|
+
gitDiff(projectRoot)
|
|
3807
|
+
]);
|
|
3808
|
+
const run = runPayload && typeof runPayload === "object" && "run" in runPayload && runPayload.run && typeof runPayload.run === "object" ? runPayload.run : runPayload;
|
|
3809
|
+
const logs = arrayFromPayload(logsPayload);
|
|
3810
|
+
const timeline = arrayFromPayload(timelinePayload);
|
|
3811
|
+
const taskId3 = stringField(run, ["taskId"]) || undefined;
|
|
3812
|
+
const { artifacts, error: artifactsError } = listArtifacts(projectRoot, taskId3);
|
|
3813
|
+
const { graph, error: graphError } = loadGraph(projectRoot);
|
|
3814
|
+
const audit = loadAudit(projectRoot);
|
|
3815
|
+
const state = ctx.getState();
|
|
3816
|
+
const selectedArtifact = typeof state.data?.inspectArtifact === "string" ? state.data.inspectArtifact : undefined;
|
|
3817
|
+
const artifactView = selectedArtifact ? readArtifact(projectRoot, taskId3, selectedArtifact) : undefined;
|
|
3818
|
+
const snapshot = {
|
|
3819
|
+
runId: cleanRunId,
|
|
3820
|
+
status: stringField(run, ["status", "state"], "unknown"),
|
|
3821
|
+
title: stringField(run, ["title", "taskId", "id"], "untitled run"),
|
|
3822
|
+
logs,
|
|
3823
|
+
timeline,
|
|
3824
|
+
diff: diffResult.diff,
|
|
3825
|
+
failures: [...logs, ...timeline].filter(isFailureEntry),
|
|
3826
|
+
artifacts,
|
|
3827
|
+
graph,
|
|
3828
|
+
audit,
|
|
3829
|
+
refreshedAt: Date.now(),
|
|
3830
|
+
...taskId3 ? { taskId: taskId3 } : {},
|
|
3831
|
+
...artifactView ? { artifactView } : {},
|
|
3832
|
+
...diffResult.error ? { diffError: diffResult.error } : {},
|
|
3833
|
+
...graphError ? { graphError } : {},
|
|
3834
|
+
...artifactsError ? { artifactsError } : {}
|
|
3835
|
+
};
|
|
3836
|
+
patchData(ctx, { inspect: snapshot, lastRefreshError: undefined });
|
|
3837
|
+
emitCompleted(ctx, label, {
|
|
3838
|
+
runId: cleanRunId,
|
|
3839
|
+
logs: logs.length,
|
|
3840
|
+
timeline: timeline.length,
|
|
3841
|
+
diffBytes: snapshot.diff.length,
|
|
3842
|
+
artifacts: artifacts.length,
|
|
3843
|
+
audit: audit.length
|
|
3844
|
+
});
|
|
3845
|
+
return snapshot;
|
|
3846
|
+
} catch (error) {
|
|
3847
|
+
emitFailed(ctx, label, error, { runId: cleanRunId });
|
|
3848
|
+
throw error;
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
var execFileAsync, INSPECT_VIEWS;
|
|
3852
|
+
var init_inspect = __esm(() => {
|
|
3853
|
+
execFileAsync = promisify(execFile);
|
|
3854
|
+
INSPECT_VIEWS = [
|
|
3855
|
+
"logs",
|
|
3856
|
+
"timeline",
|
|
3857
|
+
"artifacts",
|
|
3858
|
+
"artifact",
|
|
3859
|
+
"diff",
|
|
3860
|
+
"failures",
|
|
3861
|
+
"graph",
|
|
3862
|
+
"audit"
|
|
3863
|
+
];
|
|
3864
|
+
});
|
|
3078
3865
|
|
|
3079
|
-
// packages/cli/src/app-opentui/adapters/
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3866
|
+
// packages/cli/src/app-opentui/adapters/pi-attach.ts
|
|
3867
|
+
var exports_pi_attach = {};
|
|
3868
|
+
__export(exports_pi_attach, {
|
|
3869
|
+
preparePiAttachHandoff: () => preparePiAttachHandoff,
|
|
3870
|
+
attachRunWithBundledPi: () => attachRunWithBundledPi
|
|
3871
|
+
});
|
|
3872
|
+
function currentAttachState(ctx, runId) {
|
|
3873
|
+
const value = ctx.getState().data?.piAttach;
|
|
3874
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3875
|
+
return null;
|
|
3876
|
+
const state = value;
|
|
3877
|
+
return state.runId === runId ? state : null;
|
|
3878
|
+
}
|
|
3879
|
+
function recordStep(ctx, runId, label, emitLabel) {
|
|
3880
|
+
const now = Date.now();
|
|
3881
|
+
const previous = currentAttachState(ctx, runId);
|
|
3882
|
+
const priorSteps = previous?.steps ?? [];
|
|
3883
|
+
const settled = priorSteps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
|
|
3884
|
+
const steps = [...settled, { label, startedAtMs: now, status: "running" }];
|
|
3885
|
+
patchData(ctx, {
|
|
3886
|
+
piAttach: { ...previous ?? { runId, status: "preparing", message: label }, runId, steps }
|
|
3887
|
+
});
|
|
3888
|
+
emitProgress(ctx, emitLabel, label);
|
|
3889
|
+
}
|
|
3890
|
+
function settleSteps(ctx, runId) {
|
|
3891
|
+
const now = Date.now();
|
|
3892
|
+
const previous = currentAttachState(ctx, runId);
|
|
3893
|
+
if (!previous?.steps)
|
|
3894
|
+
return;
|
|
3895
|
+
const steps = previous.steps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
|
|
3896
|
+
patchData(ctx, { piAttach: { ...previous, steps } });
|
|
3897
|
+
}
|
|
3898
|
+
async function preparePiAttachHandoff(ctx, runId) {
|
|
3899
|
+
const cleanRunId = runId.trim();
|
|
3900
|
+
const label = `Preparing Pi ${cleanRunId.slice(0, 8)}`;
|
|
3901
|
+
emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "preparing" } });
|
|
3902
|
+
try {
|
|
3903
|
+
const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunPiStatusViaServer: getRunPiStatusViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
3904
|
+
const projectRoot = projectRootOf(ctx);
|
|
3905
|
+
recordStep(ctx, cleanRunId, "connecting to server \xB7 checking run and Pi session readiness", label);
|
|
3906
|
+
const [run, piStatus, server] = await Promise.all([
|
|
3907
|
+
getRunDetailsViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
|
|
3908
|
+
getRunPiStatusViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
|
|
3909
|
+
resolveServerConnectionLabel2(projectRoot)
|
|
3910
|
+
]);
|
|
3911
|
+
const state = {
|
|
3912
|
+
runId: cleanRunId,
|
|
3913
|
+
status: "preparing",
|
|
3914
|
+
message: "ready to enter worker Pi",
|
|
3915
|
+
result: { run, piStatus, server },
|
|
3916
|
+
ready: true,
|
|
3917
|
+
...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
|
|
3918
|
+
};
|
|
3919
|
+
patchData(ctx, { piAttach: state, lastRefreshError: undefined });
|
|
3920
|
+
emitCompleted(ctx, label, { runId: cleanRunId, server });
|
|
3921
|
+
return state;
|
|
3922
|
+
} catch (error) {
|
|
3923
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
3924
|
+
const state = {
|
|
3925
|
+
runId: cleanRunId,
|
|
3926
|
+
status: "failed",
|
|
3927
|
+
message: reason,
|
|
3928
|
+
failureReason: reason,
|
|
3929
|
+
...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
|
|
3930
|
+
};
|
|
3931
|
+
patchData(ctx, { piAttach: state });
|
|
3932
|
+
emitFailed(ctx, label, error, { runId: cleanRunId });
|
|
3933
|
+
throw error;
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
async function attachRunWithBundledPi(ctx, runId) {
|
|
3937
|
+
const cleanRunId = runId.trim();
|
|
3938
|
+
const label = `Entering Pi ${cleanRunId.slice(0, 8)}`;
|
|
3939
|
+
if (!cleanRunId) {
|
|
3940
|
+
const error = new Error("Attach requires a run id.");
|
|
3941
|
+
emitFailed(ctx, label, error);
|
|
3942
|
+
throw error;
|
|
3943
|
+
}
|
|
3944
|
+
emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
|
|
3945
|
+
patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi", steps: [] } });
|
|
3946
|
+
try {
|
|
3947
|
+
recordStep(ctx, cleanRunId, "connecting to server and fetching run transcript", label);
|
|
3948
|
+
await preparePiAttachHandoff(ctx, cleanRunId).catch((error) => {
|
|
3949
|
+
patchData(ctx, { lastRefreshError: normalizeAppError(error).message });
|
|
3950
|
+
return null;
|
|
3951
|
+
});
|
|
3952
|
+
recordStep(ctx, cleanRunId, "spawning Bun PTY host for bundled Pi", label);
|
|
3953
|
+
const projectRoot = projectRootOf(ctx);
|
|
3954
|
+
const cols = typeof process.stdout.columns === "number" ? process.stdout.columns : 100;
|
|
3955
|
+
const rows = Math.max(1, (typeof process.stdout.rows === "number" ? process.stdout.rows : 35) - 1);
|
|
3956
|
+
const patchTerminal = (snapshot) => {
|
|
3957
|
+
const previousSteps = currentAttachState(ctx, cleanRunId)?.steps;
|
|
3958
|
+
const failed = snapshot.status === "failed";
|
|
3959
|
+
const reason = failed ? snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}` : undefined;
|
|
3960
|
+
patchData(ctx, {
|
|
3961
|
+
piTerminal: snapshot,
|
|
3962
|
+
piAttach: {
|
|
3963
|
+
runId: cleanRunId,
|
|
3964
|
+
status: failed ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
|
|
3965
|
+
message: snapshot.message ?? "bundled Pi running",
|
|
3966
|
+
result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode },
|
|
3967
|
+
...previousSteps ? { steps: previousSteps } : {},
|
|
3968
|
+
...reason ? { failureReason: reason } : {}
|
|
3969
|
+
}
|
|
3970
|
+
});
|
|
3971
|
+
};
|
|
3972
|
+
recordStep(ctx, cleanRunId, "starting bundled Pi", label);
|
|
3973
|
+
await startPiPtyHost({
|
|
3974
|
+
runId: cleanRunId,
|
|
3975
|
+
projectRoot,
|
|
3976
|
+
cols,
|
|
3977
|
+
rows,
|
|
3978
|
+
onSnapshot: (snapshot) => {
|
|
3979
|
+
if (snapshot.status === "running" && snapshot.lines.some((entry) => entry.trim().length > 0)) {
|
|
3980
|
+
settleSteps(ctx, cleanRunId);
|
|
3981
|
+
}
|
|
3982
|
+
patchTerminal(snapshot);
|
|
3983
|
+
},
|
|
3984
|
+
onExit: (snapshot) => {
|
|
3985
|
+
patchTerminal(snapshot);
|
|
3986
|
+
if (snapshot.status === "failed") {
|
|
3987
|
+
Promise.resolve().then(() => (init_inspect(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
|
|
3988
|
+
emitFailed(ctx, label, new Error(snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}`), { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
emitCompleted(ctx, label, { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
|
|
3992
|
+
ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
|
|
3993
|
+
refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
|
|
3994
|
+
},
|
|
3995
|
+
onError: (error, snapshot) => {
|
|
3996
|
+
patchTerminal(snapshot);
|
|
3997
|
+
emitFailed(ctx, label, error, { runId: cleanRunId });
|
|
3094
3998
|
}
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3999
|
+
});
|
|
4000
|
+
const record = {
|
|
4001
|
+
runId: cleanRunId,
|
|
4002
|
+
status: "running",
|
|
4003
|
+
rendered: "bundled Pi embedded via Bun.Terminal + @xterm/headless"
|
|
4004
|
+
};
|
|
4005
|
+
emitProgress(ctx, label, "bundled Pi is running", { piAttach: { runId: cleanRunId, status: "entering-pi", message: "bundled Pi running", result: record } });
|
|
4006
|
+
return record;
|
|
4007
|
+
} catch (error) {
|
|
4008
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
4009
|
+
patchData(ctx, {
|
|
4010
|
+
piAttach: {
|
|
4011
|
+
runId: cleanRunId,
|
|
4012
|
+
status: "failed",
|
|
4013
|
+
message: reason,
|
|
4014
|
+
failureReason: reason,
|
|
4015
|
+
...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
|
|
3098
4016
|
}
|
|
3099
|
-
|
|
3100
|
-
}
|
|
3101
|
-
|
|
4017
|
+
});
|
|
4018
|
+
emitFailed(ctx, label, error, { runId: cleanRunId });
|
|
4019
|
+
throw error;
|
|
4020
|
+
}
|
|
3102
4021
|
}
|
|
4022
|
+
var init_pi_attach = __esm(() => {
|
|
4023
|
+
init_pi_pty_host();
|
|
4024
|
+
init_fleet();
|
|
4025
|
+
});
|
|
4026
|
+
|
|
4027
|
+
// packages/cli/src/app-opentui/adapters/tasks.ts
|
|
4028
|
+
import { randomUUID } from "crypto";
|
|
3103
4029
|
function rawRecord(record, key) {
|
|
3104
4030
|
const direct = record[key];
|
|
3105
4031
|
if (direct !== undefined && direct !== null)
|
|
@@ -3150,6 +4076,30 @@ function labelsFor(record) {
|
|
|
3150
4076
|
function assigneesFor(record) {
|
|
3151
4077
|
return stringList(rawRecord(record, "assignees"));
|
|
3152
4078
|
}
|
|
4079
|
+
function activeRunForRecord(record) {
|
|
4080
|
+
const value = rawRecord(record, "activeRun");
|
|
4081
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
4082
|
+
return;
|
|
4083
|
+
const activeRun = value;
|
|
4084
|
+
const runId = optionalString(activeRun, ["runId", "id"]);
|
|
4085
|
+
if (!runId)
|
|
4086
|
+
return;
|
|
4087
|
+
const status = optionalString(activeRun, ["status", "state"]);
|
|
4088
|
+
const stage = optionalString(activeRun, ["stage", "phase"]);
|
|
4089
|
+
return {
|
|
4090
|
+
runId,
|
|
4091
|
+
...status ? { status } : {},
|
|
4092
|
+
...stage ? { stage } : {}
|
|
4093
|
+
};
|
|
4094
|
+
}
|
|
4095
|
+
function activeRunIdFromTaskLike(task) {
|
|
4096
|
+
return activeRunForRecord(task)?.runId;
|
|
4097
|
+
}
|
|
4098
|
+
function activeRunIdFromTaskRunError(error) {
|
|
4099
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
4100
|
+
const match = message2.match(/already has an active (?:Rig )?run:\s*([^\s"'}]+)/i);
|
|
4101
|
+
return match?.[1]?.replace(/[),.;]+$/g, "") ?? null;
|
|
4102
|
+
}
|
|
3153
4103
|
function normalizeTaskRecord(record) {
|
|
3154
4104
|
const id = stringField(record, ["id", "taskId", "number"]);
|
|
3155
4105
|
if (!id)
|
|
@@ -3157,6 +4107,7 @@ function normalizeTaskRecord(record) {
|
|
|
3157
4107
|
const priority = optionalNumber(record, ["priority", "rank", "score"]);
|
|
3158
4108
|
const updatedAt = optionalString(record, ["updatedAt", "updated_at", "modifiedAt"]);
|
|
3159
4109
|
const createdAt = optionalString(record, ["createdAt", "created_at"]);
|
|
4110
|
+
const activeRun = activeRunForRecord(record);
|
|
3160
4111
|
return {
|
|
3161
4112
|
id,
|
|
3162
4113
|
status: stringField(record, ["status", "state"], "unknown"),
|
|
@@ -3166,6 +4117,7 @@ function normalizeTaskRecord(record) {
|
|
|
3166
4117
|
...createdAt ? { createdAt } : {},
|
|
3167
4118
|
labels: labelsFor(record),
|
|
3168
4119
|
assignees: assigneesFor(record),
|
|
4120
|
+
...activeRun ? { activeRun } : {},
|
|
3169
4121
|
raw: record
|
|
3170
4122
|
};
|
|
3171
4123
|
}
|
|
@@ -3393,7 +4345,7 @@ async function submitTaskRun(ctx, input = {}) {
|
|
|
3393
4345
|
try {
|
|
3394
4346
|
const { submitTaskRunViaServer: submitTaskRunViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
3395
4347
|
const defaults = await loadRunDefaults(projectRoot);
|
|
3396
|
-
emitProgress(ctx, label, "submitting run to selected
|
|
4348
|
+
emitProgress(ctx, label, "submitting run to selected server", { runId, taskId: taskId3 ?? null });
|
|
3397
4349
|
const submitted = await submitTaskRunViaServer2({ projectRoot }, {
|
|
3398
4350
|
runId,
|
|
3399
4351
|
taskId: taskId3 || undefined,
|
|
@@ -3413,14 +4365,41 @@ async function submitTaskRun(ctx, input = {}) {
|
|
|
3413
4365
|
latestSubmittedRun: { runId: submitted.runId, taskId: taskId3 ?? null },
|
|
3414
4366
|
runDetail: { runId: submitted.runId, status: "submitted", title: optimisticTitle, run: submitted, logs: [], timeline: [], timelineCursor: null }
|
|
3415
4367
|
});
|
|
3416
|
-
patchFooter(ctx, { run: `${submitted.runId.slice(0, 8)} submitted` });
|
|
4368
|
+
patchFooter(ctx, { run: `${submitted.runId.slice(0, 8)} ${input.detach ? "submitted" : "attaching"}` });
|
|
4369
|
+
if (!input.detach) {
|
|
4370
|
+
ctx.emit({
|
|
4371
|
+
type: "scene.change",
|
|
4372
|
+
scene: "handoff",
|
|
4373
|
+
intent: { scene: "handoff", argv: ["attach", submitted.runId], action: { kind: "run-attach", payload: { runId: submitted.runId }, label: `Attach Pi ${submitted.runId.slice(0, 8)}` } }
|
|
4374
|
+
});
|
|
4375
|
+
const { attachRunWithBundledPi: attachRunWithBundledPi2 } = await Promise.resolve().then(() => (init_pi_attach(), exports_pi_attach));
|
|
4376
|
+
await attachRunWithBundledPi2(ctx, submitted.runId);
|
|
4377
|
+
}
|
|
3417
4378
|
return submitted;
|
|
3418
4379
|
} catch (error) {
|
|
4380
|
+
const activeRunId = activeRunIdFromTaskRunError(error);
|
|
4381
|
+
if (activeRunId) {
|
|
4382
|
+
emitProgress(ctx, label, `task already has active run ${activeRunId.slice(0, 8)}; attaching Pi`, {
|
|
4383
|
+
selectedRunId: activeRunId,
|
|
4384
|
+
dispatchingRun: null,
|
|
4385
|
+
latestSubmittedRun: { runId: activeRunId, taskId: taskId3 ?? null }
|
|
4386
|
+
});
|
|
4387
|
+
patchData(ctx, { selectedRunId: activeRunId, dispatchingRun: null });
|
|
4388
|
+
patchFooter(ctx, { run: `${activeRunId.slice(0, 8)} active` });
|
|
4389
|
+
ctx.emit({
|
|
4390
|
+
type: "scene.change",
|
|
4391
|
+
scene: "handoff",
|
|
4392
|
+
intent: { scene: "handoff", argv: ["attach", activeRunId], action: { kind: "run-attach", payload: { runId: activeRunId }, label: `Attach Pi ${activeRunId.slice(0, 8)}` } }
|
|
4393
|
+
});
|
|
4394
|
+
const { attachRunWithBundledPi: attachRunWithBundledPi2 } = await Promise.resolve().then(() => (init_pi_attach(), exports_pi_attach));
|
|
4395
|
+
await attachRunWithBundledPi2(ctx, activeRunId);
|
|
4396
|
+
return { runId: activeRunId };
|
|
4397
|
+
}
|
|
3419
4398
|
emitFailed(ctx, label, error, { runId, taskId: taskId3 ?? null, revert: { dispatchingRun: null } });
|
|
3420
4399
|
throw error;
|
|
3421
4400
|
}
|
|
3422
4401
|
}
|
|
3423
|
-
async function submitNextTaskRun(ctx, filters = {}) {
|
|
4402
|
+
async function submitNextTaskRun(ctx, filters = {}, options = {}) {
|
|
3424
4403
|
const label = "Selecting next task";
|
|
3425
4404
|
emitStarted(ctx, label, { dispatch: { status: "selecting-next" } });
|
|
3426
4405
|
try {
|
|
@@ -3436,7 +4415,7 @@ async function submitNextTaskRun(ctx, filters = {}) {
|
|
|
3436
4415
|
throw error;
|
|
3437
4416
|
}
|
|
3438
4417
|
emitCompleted(ctx, label, { taskId: task.id, count: selected.count });
|
|
3439
|
-
const submitted = await submitTaskRun(ctx, { taskId: task.id, title: task.title });
|
|
4418
|
+
const submitted = await submitTaskRun(ctx, { taskId: task.id, title: task.title, detach: options.detach });
|
|
3440
4419
|
return { ...submitted, task };
|
|
3441
4420
|
} catch (error) {
|
|
3442
4421
|
emitFailed(ctx, label, error);
|
|
@@ -3450,7 +4429,7 @@ async function loadTaskPreview(ctx, taskId3) {
|
|
|
3450
4429
|
const { getWorkspaceTaskViaServer: getWorkspaceTaskViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
3451
4430
|
const task = await getWorkspaceTaskViaServer2({ projectRoot: projectRootOf(ctx) }, taskId3);
|
|
3452
4431
|
const normalized = task ? normalizeTaskRecord(task) : null;
|
|
3453
|
-
patchData(ctx, { selectedTaskId: taskId3, taskPreview: normalized, taskPreviewRaw: task ? compactRecord(task, ["id", "title", "status", "body", "url"]) : null });
|
|
4432
|
+
patchData(ctx, { selectedTaskId: taskId3, taskDetailId: taskId3, taskPreview: normalized, taskPreviewRaw: task ? compactRecord(task, ["id", "title", "status", "body", "url"]) : null });
|
|
3454
4433
|
emitCompleted(ctx, label, { taskId: taskId3, found: Boolean(normalized) });
|
|
3455
4434
|
return normalized;
|
|
3456
4435
|
} catch (error) {
|
|
@@ -3468,5 +4447,6 @@ export {
|
|
|
3468
4447
|
normalizeTaskRecord,
|
|
3469
4448
|
loadTaskPreview,
|
|
3470
4449
|
ensureTaskRuntime,
|
|
3471
|
-
|
|
4450
|
+
activeRunIdFromTaskRunError,
|
|
4451
|
+
activeRunIdFromTaskLike
|
|
3472
4452
|
};
|