@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,70 @@ 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
|
+
function normalizeAppError(error) {
|
|
26
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
+
const rawHint = error instanceof Error ? error.hint : undefined;
|
|
28
|
+
return {
|
|
29
|
+
message,
|
|
30
|
+
...typeof rawHint === "string" && rawHint.trim() ? { hint: rawHint.trim() } : {}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function emitStarted(ctx, label, optimistic) {
|
|
34
|
+
ctx.emit({ type: "action.started", label, ...optimistic ? { optimistic } : {} });
|
|
35
|
+
}
|
|
36
|
+
function emitProgress(ctx, label, detail, data) {
|
|
37
|
+
ctx.emit({ type: "action.progress", label, ...detail ? { detail } : {}, ...data ? { data } : {} });
|
|
38
|
+
}
|
|
39
|
+
function emitCompleted(ctx, label, data) {
|
|
40
|
+
ctx.emit({ type: "action.completed", label, ...data ? { data } : {} });
|
|
41
|
+
}
|
|
42
|
+
function emitFailed(ctx, label, error, data) {
|
|
43
|
+
if (data)
|
|
44
|
+
patchData(ctx, data);
|
|
45
|
+
const normalized = normalizeAppError(error);
|
|
46
|
+
ctx.emit({
|
|
47
|
+
type: "action.failed",
|
|
48
|
+
label,
|
|
49
|
+
message: normalized.message,
|
|
50
|
+
...normalized.hint ? { hint: normalized.hint } : {},
|
|
51
|
+
cause: error
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function patchData(ctx, data) {
|
|
55
|
+
ctx.emit({ type: "data.patch", data });
|
|
56
|
+
}
|
|
57
|
+
function patchFooter(ctx, footer) {
|
|
58
|
+
ctx.emit({ type: "footer.patch", footer });
|
|
59
|
+
}
|
|
60
|
+
function arrayFromPayload(value) {
|
|
61
|
+
if (Array.isArray(value)) {
|
|
62
|
+
return value.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
|
|
63
|
+
}
|
|
64
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
65
|
+
const entries = value.entries;
|
|
66
|
+
if (Array.isArray(entries))
|
|
67
|
+
return arrayFromPayload(entries);
|
|
68
|
+
}
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
function stringField(record, keys, fallback = "") {
|
|
72
|
+
for (const key of keys) {
|
|
73
|
+
const value = record[key];
|
|
74
|
+
if (typeof value === "string" && value.trim())
|
|
75
|
+
return value.trim();
|
|
76
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
77
|
+
return String(value);
|
|
78
|
+
}
|
|
79
|
+
return fallback;
|
|
80
|
+
}
|
|
81
|
+
|
|
18
82
|
// packages/cli/src/runner.ts
|
|
19
83
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
20
84
|
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
@@ -192,17 +256,21 @@ function setGitHubBearerTokenForCurrentProcess(token, projectRoot) {
|
|
|
192
256
|
const scopedKey = resolve2(projectRoot ?? process.cwd());
|
|
193
257
|
scopedGitHubBearerTokens.set(scopedKey, cleanToken(token ?? undefined));
|
|
194
258
|
}
|
|
195
|
-
function
|
|
259
|
+
function readRemoteAuthState(projectRoot) {
|
|
196
260
|
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
197
261
|
if (!existsSync2(path))
|
|
198
262
|
return null;
|
|
199
263
|
try {
|
|
200
264
|
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
201
|
-
return
|
|
265
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
202
266
|
} catch {
|
|
203
267
|
return null;
|
|
204
268
|
}
|
|
205
269
|
}
|
|
270
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
271
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
272
|
+
return parsed ? cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined) : null;
|
|
273
|
+
}
|
|
206
274
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
207
275
|
const scopedKey = resolve2(projectRoot);
|
|
208
276
|
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
@@ -213,15 +281,25 @@ function readGitHubBearerTokenForRemote(projectRoot) {
|
|
|
213
281
|
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
214
282
|
}
|
|
215
283
|
function readStoredGitHubAuthToken(projectRoot) {
|
|
216
|
-
const
|
|
217
|
-
|
|
284
|
+
const parsed = readRemoteAuthState(projectRoot);
|
|
285
|
+
return parsed ? cleanToken(typeof parsed.token === "string" ? parsed.token : undefined) : null;
|
|
286
|
+
}
|
|
287
|
+
function inferRemoteServerProjectRootFromAuthState(projectRoot) {
|
|
288
|
+
const repo = readRepoConnection(projectRoot);
|
|
289
|
+
const slug = repo?.project?.trim();
|
|
290
|
+
if (!slug || !/^[^/]+\/[^/]+$/.test(slug))
|
|
218
291
|
return null;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
292
|
+
const auth = readRemoteAuthState(projectRoot);
|
|
293
|
+
const authUpdatedAt = typeof auth?.updatedAt === "string" ? Date.parse(auth.updatedAt) : Number.NaN;
|
|
294
|
+
const repoLinkedAt = typeof repo?.linkedAt === "string" ? Date.parse(repo.linkedAt) : Number.NaN;
|
|
295
|
+
if (Number.isFinite(authUpdatedAt) && Number.isFinite(repoLinkedAt) && authUpdatedAt + 1000 < repoLinkedAt)
|
|
223
296
|
return null;
|
|
224
|
-
|
|
297
|
+
const checkoutBaseDir = typeof auth?.checkoutBaseDir === "string" && auth.checkoutBaseDir.trim() ? auth.checkoutBaseDir.trim() : null;
|
|
298
|
+
if (!checkoutBaseDir)
|
|
299
|
+
return null;
|
|
300
|
+
const inferred = `${checkoutBaseDir.replace(/\/+$/, "")}/${slug}`;
|
|
301
|
+
writeRepoServerProjectRoot(projectRoot, inferred);
|
|
302
|
+
return inferred;
|
|
225
303
|
}
|
|
226
304
|
function readLocalConnectionFallbackToken(projectRoot) {
|
|
227
305
|
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
@@ -232,7 +310,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
232
310
|
if (selected?.connection.kind === "remote") {
|
|
233
311
|
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
234
312
|
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
235
|
-
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
313
|
+
const serverProjectRoot = selected.serverProjectRoot ?? inferRemoteServerProjectRootFromAuthState(projectRoot) ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
236
314
|
return {
|
|
237
315
|
baseUrl: selected.connection.baseUrl,
|
|
238
316
|
authToken,
|
|
@@ -261,7 +339,10 @@ async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken)
|
|
|
261
339
|
if (!slug)
|
|
262
340
|
return null;
|
|
263
341
|
try {
|
|
264
|
-
const
|
|
342
|
+
const url = new URL(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`);
|
|
343
|
+
if (authToken && queryAuthFallbackEnabled())
|
|
344
|
+
url.searchParams.set("rt", authToken);
|
|
345
|
+
const response = await fetch(url, {
|
|
265
346
|
headers: mergeHeaders(undefined, authToken)
|
|
266
347
|
});
|
|
267
348
|
if (!response.ok)
|
|
@@ -288,10 +369,23 @@ function appendTaskFilterParams(url, filters) {
|
|
|
288
369
|
if (filters.limit !== undefined)
|
|
289
370
|
url.searchParams.set("limit", String(filters.limit));
|
|
290
371
|
}
|
|
372
|
+
function mergeCookie(existing, name, value) {
|
|
373
|
+
const encoded = `${name}=${encodeURIComponent(value)}`;
|
|
374
|
+
if (!existing?.trim())
|
|
375
|
+
return encoded;
|
|
376
|
+
const parts = existing.split(";").map((part) => part.trim()).filter((part) => part && !part.startsWith(`${name}=`));
|
|
377
|
+
return [...parts, encoded].join("; ");
|
|
378
|
+
}
|
|
379
|
+
function queryAuthFallbackEnabled(env = process.env) {
|
|
380
|
+
return env.RIG_ENABLE_QUERY_AUTH_FALLBACK === "1" || env.RIG_QUERY_AUTH_FALLBACK === "1";
|
|
381
|
+
}
|
|
291
382
|
function mergeHeaders(headers, authToken) {
|
|
292
383
|
const merged = new Headers(headers);
|
|
293
384
|
if (authToken) {
|
|
294
|
-
|
|
385
|
+
const bearer = `Bearer ${authToken}`;
|
|
386
|
+
merged.set("authorization", bearer);
|
|
387
|
+
merged.set("x-auth", bearer);
|
|
388
|
+
merged.set("cookie", mergeCookie(merged.get("cookie"), "rig_auth", authToken));
|
|
295
389
|
}
|
|
296
390
|
return merged;
|
|
297
391
|
}
|
|
@@ -351,15 +445,34 @@ async function buildServerFailureContext(projectRoot, server) {
|
|
|
351
445
|
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
352
446
|
};
|
|
353
447
|
}
|
|
448
|
+
function isLoopbackRemoteBaseUrl(baseUrl) {
|
|
449
|
+
try {
|
|
450
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
451
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
|
|
452
|
+
} catch {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function canUseRemoteWithoutProjectRoot(pathname) {
|
|
457
|
+
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/");
|
|
458
|
+
}
|
|
354
459
|
async function requestServerJson(context, pathname, init = {}) {
|
|
355
460
|
const server = await ensureServerForCli(context.projectRoot);
|
|
461
|
+
const requestUrl = new URL(`${server.baseUrl}${pathname}`);
|
|
462
|
+
if (server.connectionKind === "remote" && !server.serverProjectRoot && !canUseRemoteWithoutProjectRoot(requestUrl.pathname) && (server.authToken || !isLoopbackRemoteBaseUrl(server.baseUrl))) {
|
|
463
|
+
const repo = readRepoConnection(context.projectRoot);
|
|
464
|
+
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." });
|
|
465
|
+
}
|
|
356
466
|
const headers = mergeHeaders(init.headers, server.authToken);
|
|
357
467
|
if (server.serverProjectRoot)
|
|
358
468
|
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
469
|
+
if (server.connectionKind === "remote" && server.authToken && queryAuthFallbackEnabled()) {
|
|
470
|
+
requestUrl.searchParams.set("rt", server.authToken);
|
|
471
|
+
}
|
|
359
472
|
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
360
473
|
let response;
|
|
361
474
|
try {
|
|
362
|
-
response = await fetch(
|
|
475
|
+
response = await fetch(requestUrl, {
|
|
363
476
|
...init,
|
|
364
477
|
headers
|
|
365
478
|
});
|
|
@@ -421,11 +534,26 @@ async function getGitHubAuthStatusViaServer(context) {
|
|
|
421
534
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
422
535
|
}
|
|
423
536
|
async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
537
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
538
|
+
const requestUrl = new URL(`${server.baseUrl}/api/github/auth/token`);
|
|
539
|
+
reportServerPhase("POST /api/github/auth/token\u2026");
|
|
540
|
+
let response;
|
|
541
|
+
try {
|
|
542
|
+
response = await fetch(requestUrl, {
|
|
543
|
+
method: "POST",
|
|
544
|
+
headers: { "content-type": "application/json" },
|
|
545
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot ?? server.serverProjectRoot ?? undefined })
|
|
546
|
+
});
|
|
547
|
+
} catch (error) {
|
|
548
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
549
|
+
throw new CliError(`Rig server auth bootstrap failed: ${error instanceof Error ? error.message : String(error)}
|
|
550
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
551
|
+
}
|
|
552
|
+
const payload = await response.json().catch(() => null);
|
|
553
|
+
if (!response.ok) {
|
|
554
|
+
const detail = payload && typeof payload === "object" && !Array.isArray(payload) ? String(payload.error ?? JSON.stringify(payload)) : `HTTP ${response.status}`;
|
|
555
|
+
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`." });
|
|
556
|
+
}
|
|
429
557
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
430
558
|
}
|
|
431
559
|
async function prepareRemoteCheckoutViaServer(context, input) {
|
|
@@ -702,72 +830,203 @@ var init__server_client = __esm(() => {
|
|
|
702
830
|
]);
|
|
703
831
|
});
|
|
704
832
|
|
|
705
|
-
// packages/cli/src/app-opentui/adapters/
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
ctx.emit({ type: "action.started", label, ...optimistic ? { optimistic } : {} });
|
|
731
|
-
}
|
|
732
|
-
function emitProgress(ctx, label, detail, data) {
|
|
733
|
-
ctx.emit({ type: "action.progress", label, ...detail ? { detail } : {}, ...data ? { data } : {} });
|
|
833
|
+
// packages/cli/src/app-opentui/adapters/inspect.ts
|
|
834
|
+
var exports_inspect = {};
|
|
835
|
+
__export(exports_inspect, {
|
|
836
|
+
loadInspectRuns: () => loadInspectRuns,
|
|
837
|
+
loadInspect: () => loadInspect,
|
|
838
|
+
INSPECT_VIEWS: () => INSPECT_VIEWS
|
|
839
|
+
});
|
|
840
|
+
import { execFile } from "child_process";
|
|
841
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
|
|
842
|
+
import { resolve as resolve3 } from "path";
|
|
843
|
+
import { promisify } from "util";
|
|
844
|
+
import { resolveTaskArtifactDirs } from "@rig/runtime/control-plane/authority-files";
|
|
845
|
+
import { readTaskArtifactPreview } from "@rig/runtime/control-plane/native/workspace-ops";
|
|
846
|
+
import { resolveHarnessPaths, resolveMonorepoRoot, runCapture } from "@rig/runtime/control-plane/native/utils";
|
|
847
|
+
function isFailureEntry(entry) {
|
|
848
|
+
const status = `${entry.status ?? entry.level ?? entry.type ?? ""}`.toLowerCase();
|
|
849
|
+
return status.includes("fail") || status.includes("error");
|
|
850
|
+
}
|
|
851
|
+
async function gitDiff(projectRoot) {
|
|
852
|
+
try {
|
|
853
|
+
const { stdout } = await execFileAsync("git", ["diff", "--no-color"], { cwd: projectRoot, maxBuffer: 8 * 1024 * 1024 });
|
|
854
|
+
return { diff: stdout };
|
|
855
|
+
} catch (error) {
|
|
856
|
+
return { diff: "", error: normalizeAppError(error).message };
|
|
857
|
+
}
|
|
734
858
|
}
|
|
735
|
-
function
|
|
736
|
-
|
|
859
|
+
function listArtifacts(projectRoot, taskId) {
|
|
860
|
+
if (!taskId)
|
|
861
|
+
return { artifacts: [], error: "run has no task id \u2014 artifacts are resolved per task" };
|
|
862
|
+
try {
|
|
863
|
+
const dirs = resolveTaskArtifactDirs(projectRoot, taskId).filter((dir) => existsSync3(dir));
|
|
864
|
+
const out = [];
|
|
865
|
+
const seen = new Set;
|
|
866
|
+
for (const dir of dirs) {
|
|
867
|
+
for (const name of readdirSync(dir)) {
|
|
868
|
+
if (seen.has(name))
|
|
869
|
+
continue;
|
|
870
|
+
seen.add(name);
|
|
871
|
+
const path = resolve3(dir, name);
|
|
872
|
+
try {
|
|
873
|
+
const stat = statSync(path);
|
|
874
|
+
out.push({ name, path, sizeBytes: stat.size, isDir: stat.isDirectory() });
|
|
875
|
+
} catch {
|
|
876
|
+
out.push({ name, path, sizeBytes: 0, isDir: false });
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
out.sort((left, right) => left.name.localeCompare(right.name));
|
|
881
|
+
return { artifacts: out };
|
|
882
|
+
} catch (error) {
|
|
883
|
+
return { artifacts: [], error: normalizeAppError(error).message };
|
|
884
|
+
}
|
|
737
885
|
}
|
|
738
|
-
function
|
|
739
|
-
if (
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
cause: error
|
|
748
|
-
});
|
|
886
|
+
function readArtifact(projectRoot, taskId, fileName) {
|
|
887
|
+
if (!taskId)
|
|
888
|
+
return;
|
|
889
|
+
try {
|
|
890
|
+
const preview = readTaskArtifactPreview(projectRoot, taskId, fileName);
|
|
891
|
+
return { name: fileName, contents: preview.contents, truncated: preview.truncated, sizeBytes: preview.sizeBytes };
|
|
892
|
+
} catch (error) {
|
|
893
|
+
return { name: fileName, contents: `Failed to read artifact: ${normalizeAppError(error).message}`, truncated: false, sizeBytes: 0 };
|
|
894
|
+
}
|
|
749
895
|
}
|
|
750
|
-
function
|
|
751
|
-
|
|
896
|
+
function loadGraph(projectRoot) {
|
|
897
|
+
try {
|
|
898
|
+
const result = runCapture(["br", "--no-db", "list", "--pretty"], resolveMonorepoRoot(projectRoot));
|
|
899
|
+
if (result.exitCode !== 0)
|
|
900
|
+
return { graph: "", error: result.stderr || result.stdout || "failed to inspect graph" };
|
|
901
|
+
return { graph: result.stdout };
|
|
902
|
+
} catch (error) {
|
|
903
|
+
return { graph: "", error: normalizeAppError(error).message };
|
|
904
|
+
}
|
|
752
905
|
}
|
|
753
|
-
function
|
|
754
|
-
|
|
906
|
+
function loadAudit(projectRoot) {
|
|
907
|
+
try {
|
|
908
|
+
const auditPath = resolve3(resolveHarnessPaths(projectRoot).logsDir, "audit.jsonl");
|
|
909
|
+
if (!existsSync3(auditPath))
|
|
910
|
+
return [];
|
|
911
|
+
return readFileSync3(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-50).flatMap((raw) => {
|
|
912
|
+
try {
|
|
913
|
+
const parsed = JSON.parse(raw);
|
|
914
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? [parsed] : [{ detail: raw }];
|
|
915
|
+
} catch {
|
|
916
|
+
return [{ detail: raw }];
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
} catch {
|
|
920
|
+
return [];
|
|
921
|
+
}
|
|
755
922
|
}
|
|
756
|
-
function
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
923
|
+
async function loadInspectRuns(ctx, options = {}) {
|
|
924
|
+
const label = "Loading runs to inspect";
|
|
925
|
+
emitStarted(ctx, label);
|
|
926
|
+
try {
|
|
927
|
+
const projectRoot = projectRootOf(ctx);
|
|
928
|
+
const { listRunsViaServer: listRunsViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
929
|
+
emitProgress(ctx, label, "listing recent runs");
|
|
930
|
+
const runs = await listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 });
|
|
931
|
+
const options_ = runs.flatMap((record) => {
|
|
932
|
+
const runId = stringField(record, ["runId", "id"]);
|
|
933
|
+
if (!runId)
|
|
934
|
+
return [];
|
|
935
|
+
const taskId = stringField(record, ["taskId"]);
|
|
936
|
+
const updatedAt = stringField(record, ["updatedAt", "createdAt"]);
|
|
937
|
+
return [{
|
|
938
|
+
runId,
|
|
939
|
+
status: stringField(record, ["status", "state"], "unknown"),
|
|
940
|
+
title: stringField(record, ["title", "taskTitle", "taskId"], "untitled run"),
|
|
941
|
+
...taskId ? { taskId } : {},
|
|
942
|
+
...updatedAt ? { updatedAt } : {}
|
|
943
|
+
}];
|
|
944
|
+
});
|
|
945
|
+
patchData(ctx, { inspectRuns: options_, lastRefreshError: undefined });
|
|
946
|
+
emitCompleted(ctx, label, { count: options_.length });
|
|
947
|
+
return options_;
|
|
948
|
+
} catch (error) {
|
|
949
|
+
emitFailed(ctx, label, error);
|
|
950
|
+
throw error;
|
|
763
951
|
}
|
|
764
|
-
return fallback;
|
|
765
952
|
}
|
|
766
|
-
function
|
|
767
|
-
const
|
|
768
|
-
|
|
953
|
+
async function loadInspect(ctx, runId) {
|
|
954
|
+
const cleanRunId = runId.trim();
|
|
955
|
+
const label = `Inspecting run ${cleanRunId.slice(0, 8)}`;
|
|
956
|
+
if (!cleanRunId) {
|
|
957
|
+
await loadInspectRuns(ctx).catch(() => []);
|
|
958
|
+
const error = new Error("Inspect requires a run id.");
|
|
959
|
+
throw error;
|
|
960
|
+
}
|
|
961
|
+
emitStarted(ctx, label);
|
|
962
|
+
try {
|
|
963
|
+
const projectRoot = projectRootOf(ctx);
|
|
964
|
+
const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunLogsViaServer: getRunLogsViaServer2, getRunTimelineViaServer: getRunTimelineViaServer2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
965
|
+
const context = { projectRoot };
|
|
966
|
+
emitProgress(ctx, label, "loading logs, timeline, diff, artifacts, graph, and audit");
|
|
967
|
+
const [runPayload, logsPayload, timelinePayload, diffResult] = await Promise.all([
|
|
968
|
+
getRunDetailsViaServer2(context, cleanRunId).catch((error) => ({ error: normalizeAppError(error).message })),
|
|
969
|
+
getRunLogsViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
|
|
970
|
+
getRunTimelineViaServer2(context, cleanRunId, { limit: 500 }).catch(() => []),
|
|
971
|
+
gitDiff(projectRoot)
|
|
972
|
+
]);
|
|
973
|
+
const run = runPayload && typeof runPayload === "object" && "run" in runPayload && runPayload.run && typeof runPayload.run === "object" ? runPayload.run : runPayload;
|
|
974
|
+
const logs = arrayFromPayload(logsPayload);
|
|
975
|
+
const timeline = arrayFromPayload(timelinePayload);
|
|
976
|
+
const taskId = stringField(run, ["taskId"]) || undefined;
|
|
977
|
+
const { artifacts, error: artifactsError } = listArtifacts(projectRoot, taskId);
|
|
978
|
+
const { graph, error: graphError } = loadGraph(projectRoot);
|
|
979
|
+
const audit = loadAudit(projectRoot);
|
|
980
|
+
const state = ctx.getState();
|
|
981
|
+
const selectedArtifact = typeof state.data?.inspectArtifact === "string" ? state.data.inspectArtifact : undefined;
|
|
982
|
+
const artifactView = selectedArtifact ? readArtifact(projectRoot, taskId, selectedArtifact) : undefined;
|
|
983
|
+
const snapshot = {
|
|
984
|
+
runId: cleanRunId,
|
|
985
|
+
status: stringField(run, ["status", "state"], "unknown"),
|
|
986
|
+
title: stringField(run, ["title", "taskId", "id"], "untitled run"),
|
|
987
|
+
logs,
|
|
988
|
+
timeline,
|
|
989
|
+
diff: diffResult.diff,
|
|
990
|
+
failures: [...logs, ...timeline].filter(isFailureEntry),
|
|
991
|
+
artifacts,
|
|
992
|
+
graph,
|
|
993
|
+
audit,
|
|
994
|
+
refreshedAt: Date.now(),
|
|
995
|
+
...taskId ? { taskId } : {},
|
|
996
|
+
...artifactView ? { artifactView } : {},
|
|
997
|
+
...diffResult.error ? { diffError: diffResult.error } : {},
|
|
998
|
+
...graphError ? { graphError } : {},
|
|
999
|
+
...artifactsError ? { artifactsError } : {}
|
|
1000
|
+
};
|
|
1001
|
+
patchData(ctx, { inspect: snapshot, lastRefreshError: undefined });
|
|
1002
|
+
emitCompleted(ctx, label, {
|
|
1003
|
+
runId: cleanRunId,
|
|
1004
|
+
logs: logs.length,
|
|
1005
|
+
timeline: timeline.length,
|
|
1006
|
+
diffBytes: snapshot.diff.length,
|
|
1007
|
+
artifacts: artifacts.length,
|
|
1008
|
+
audit: audit.length
|
|
1009
|
+
});
|
|
1010
|
+
return snapshot;
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
emitFailed(ctx, label, error, { runId: cleanRunId });
|
|
1013
|
+
throw error;
|
|
1014
|
+
}
|
|
769
1015
|
}
|
|
770
|
-
|
|
1016
|
+
var execFileAsync, INSPECT_VIEWS;
|
|
1017
|
+
var init_inspect = __esm(() => {
|
|
1018
|
+
execFileAsync = promisify(execFile);
|
|
1019
|
+
INSPECT_VIEWS = [
|
|
1020
|
+
"logs",
|
|
1021
|
+
"timeline",
|
|
1022
|
+
"artifacts",
|
|
1023
|
+
"artifact",
|
|
1024
|
+
"diff",
|
|
1025
|
+
"failures",
|
|
1026
|
+
"graph",
|
|
1027
|
+
"audit"
|
|
1028
|
+
];
|
|
1029
|
+
});
|
|
771
1030
|
// packages/cli/src/app-opentui/pi-pty-host.ts
|
|
772
1031
|
import { fileURLToPath } from "url";
|
|
773
1032
|
import { basename } from "path";
|
|
@@ -777,8 +1036,8 @@ var MIN_COLS = 40;
|
|
|
777
1036
|
var MIN_ROWS = 12;
|
|
778
1037
|
var MAX_ROWS = 300;
|
|
779
1038
|
var MAX_SNAPSHOT_LINES = 360;
|
|
780
|
-
var STYLED_SNAPSHOT_MARGIN =
|
|
781
|
-
var SNAPSHOT_DELAY_MS =
|
|
1039
|
+
var STYLED_SNAPSHOT_MARGIN = 6;
|
|
1040
|
+
var SNAPSHOT_DELAY_MS = 120;
|
|
782
1041
|
var fallbackChildScriptPath = fileURLToPath(new URL("./pi-host-child.ts", import.meta.url));
|
|
783
1042
|
var activeHost = null;
|
|
784
1043
|
function clampCols(cols) {
|
|
@@ -876,17 +1135,15 @@ function lineToStyledText(line, cols) {
|
|
|
876
1135
|
function childCommandPrefix() {
|
|
877
1136
|
const execName = basename(process.execPath).toLowerCase();
|
|
878
1137
|
const currentEntry = process.argv[1];
|
|
879
|
-
if (
|
|
880
|
-
return [process.execPath, currentEntry, "__opentui-pi-host"];
|
|
1138
|
+
if (execName === "bun" || execName === "bun.exe") {
|
|
1139
|
+
return currentEntry ? [process.execPath, currentEntry, "__opentui-pi-host"] : [process.execPath, fallbackChildScriptPath];
|
|
881
1140
|
}
|
|
882
|
-
|
|
883
|
-
return [process.execPath, "__opentui-pi-host"];
|
|
884
|
-
return [process.execPath, fallbackChildScriptPath];
|
|
1141
|
+
return [process.execPath, "__opentui-pi-host"];
|
|
885
1142
|
}
|
|
886
1143
|
function withEnv(base) {
|
|
887
1144
|
const env = {};
|
|
888
1145
|
for (const [key, value] of Object.entries(base ?? process.env)) {
|
|
889
|
-
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP")
|
|
1146
|
+
if (key === "RIG_PLAIN" || key === "RIG_CLI_PLAIN_HELP" || key === "NO_COLOR")
|
|
890
1147
|
continue;
|
|
891
1148
|
if (typeof value === "string")
|
|
892
1149
|
env[key] = value;
|
|
@@ -921,9 +1178,11 @@ class PiPtyHost {
|
|
|
921
1178
|
cols;
|
|
922
1179
|
rows;
|
|
923
1180
|
message = "starting bundled Pi";
|
|
1181
|
+
lastResizeError = null;
|
|
924
1182
|
exitCode;
|
|
925
1183
|
signal;
|
|
926
1184
|
notifyTimer = null;
|
|
1185
|
+
lastStreamKey = "";
|
|
927
1186
|
_disposed = false;
|
|
928
1187
|
constructor(options) {
|
|
929
1188
|
this.runId = options.runId;
|
|
@@ -937,9 +1196,20 @@ class PiPtyHost {
|
|
|
937
1196
|
allowProposedApi: true,
|
|
938
1197
|
cols: this.cols,
|
|
939
1198
|
rows: this.rows,
|
|
940
|
-
scrollback:
|
|
1199
|
+
scrollback: 1000
|
|
941
1200
|
});
|
|
942
1201
|
this.registerTerminalResponders();
|
|
1202
|
+
this.disposables.push(this.terminal.onWriteParsed(() => {
|
|
1203
|
+
if (this._disposed)
|
|
1204
|
+
return;
|
|
1205
|
+
const snapshot = this.createSnapshot();
|
|
1206
|
+
const key = snapshot.lines.join(`
|
|
1207
|
+
`);
|
|
1208
|
+
if (key === this.lastStreamKey)
|
|
1209
|
+
return;
|
|
1210
|
+
this.lastStreamKey = key;
|
|
1211
|
+
this.onSnapshot?.(snapshot);
|
|
1212
|
+
}));
|
|
943
1213
|
}
|
|
944
1214
|
get disposed() {
|
|
945
1215
|
return this._disposed;
|
|
@@ -951,7 +1221,7 @@ class PiPtyHost {
|
|
|
951
1221
|
if (this._disposed)
|
|
952
1222
|
throw new Error("Pi PTY host is disposed.");
|
|
953
1223
|
if (typeof Bun.Terminal !== "function") {
|
|
954
|
-
throw new Error("Bun native PTY is unavailable in this runtime.
|
|
1224
|
+
throw new Error("Bun native PTY is unavailable in this runtime. Pi host requires Bun.Terminal.");
|
|
955
1225
|
}
|
|
956
1226
|
const spawnOptions = {
|
|
957
1227
|
cwd: this.projectRoot,
|
|
@@ -975,7 +1245,7 @@ class PiPtyHost {
|
|
|
975
1245
|
this.proc = proc;
|
|
976
1246
|
this.pty = proc.terminal;
|
|
977
1247
|
this.status = "running";
|
|
978
|
-
this.message = "bundled Pi running inside
|
|
1248
|
+
this.message = "bundled Pi running inside this app";
|
|
979
1249
|
this.emitSnapshotSoon(0);
|
|
980
1250
|
proc.exited.then((exitCode) => {
|
|
981
1251
|
if (this._disposed)
|
|
@@ -1026,7 +1296,10 @@ class PiPtyHost {
|
|
|
1026
1296
|
this.terminal.resize(nextCols, nextRows);
|
|
1027
1297
|
try {
|
|
1028
1298
|
this.pty?.resize(nextCols, nextRows);
|
|
1029
|
-
|
|
1299
|
+
this.lastResizeError = null;
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
this.lastResizeError = error instanceof Error ? error.message : String(error);
|
|
1302
|
+
}
|
|
1030
1303
|
this.emitSnapshotSoon(0);
|
|
1031
1304
|
}
|
|
1032
1305
|
detach() {
|
|
@@ -1068,7 +1341,7 @@ class PiPtyHost {
|
|
|
1068
1341
|
return;
|
|
1069
1342
|
const text = this.decoder.decode(data, { stream: true });
|
|
1070
1343
|
this.respondToRawTerminalQueries(text);
|
|
1071
|
-
this.terminal.write(data
|
|
1344
|
+
this.terminal.write(data);
|
|
1072
1345
|
}
|
|
1073
1346
|
emitSnapshotSoon(delayMs = SNAPSHOT_DELAY_MS) {
|
|
1074
1347
|
if (this._disposed || this.notifyTimer)
|
|
@@ -1097,6 +1370,7 @@ class PiPtyHost {
|
|
|
1097
1370
|
lines.push("");
|
|
1098
1371
|
styledLines[lines.length - 1] = new StyledText([{ __isChunk: true, text: "" }]);
|
|
1099
1372
|
}
|
|
1373
|
+
const resolvedMessage = this.lastResizeError ? `resize degraded: ${this.lastResizeError}` : message;
|
|
1100
1374
|
return {
|
|
1101
1375
|
runId: this.runId,
|
|
1102
1376
|
status: this.status,
|
|
@@ -1104,7 +1378,8 @@ class PiPtyHost {
|
|
|
1104
1378
|
rows: this.rows,
|
|
1105
1379
|
lines,
|
|
1106
1380
|
styledLines,
|
|
1107
|
-
message,
|
|
1381
|
+
message: resolvedMessage,
|
|
1382
|
+
...this.lastResizeError ? { resizeError: this.lastResizeError } : {},
|
|
1108
1383
|
...this.exitCode !== undefined ? { exitCode: this.exitCode } : {},
|
|
1109
1384
|
...this.signal !== undefined ? { signal: this.signal } : {}
|
|
1110
1385
|
};
|
|
@@ -1165,7 +1440,7 @@ async function refreshFleet(ctx, options = {}) {
|
|
|
1165
1440
|
try {
|
|
1166
1441
|
const { listRunsViaServer: listRunsViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
1167
1442
|
const projectRoot = projectRootOf(ctx);
|
|
1168
|
-
emitProgress(ctx, label, "contacting selected
|
|
1443
|
+
emitProgress(ctx, label, "contacting selected server");
|
|
1169
1444
|
const [runs, serverLabel] = await Promise.all([
|
|
1170
1445
|
listRunsViaServer2({ projectRoot }, { limit: options.limit ?? 50 }),
|
|
1171
1446
|
resolveServerConnectionLabel2(projectRoot)
|
|
@@ -1174,7 +1449,7 @@ async function refreshFleet(ctx, options = {}) {
|
|
|
1174
1449
|
const entry = normalizeRunRecord(run);
|
|
1175
1450
|
return entry ? [entry] : [];
|
|
1176
1451
|
});
|
|
1177
|
-
patchData(ctx, { fleet: { runs: normalized, refreshedAt: new Date().toISOString() } });
|
|
1452
|
+
patchData(ctx, { fleet: { runs: normalized, refreshedAt: new Date().toISOString() }, lastRefreshError: undefined });
|
|
1178
1453
|
patchFooter(ctx, { server: serverLabel, runs: String(normalized.length) });
|
|
1179
1454
|
emitCompleted(ctx, label, { count: normalized.length });
|
|
1180
1455
|
return normalized;
|
|
@@ -1185,25 +1460,31 @@ async function refreshFleet(ctx, options = {}) {
|
|
|
1185
1460
|
}
|
|
1186
1461
|
|
|
1187
1462
|
// packages/cli/src/app-opentui/adapters/pi-attach.ts
|
|
1188
|
-
function
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1463
|
+
function currentAttachState(ctx, runId) {
|
|
1464
|
+
const value = ctx.getState().data?.piAttach;
|
|
1465
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
1466
|
+
return null;
|
|
1467
|
+
const state = value;
|
|
1468
|
+
return state.runId === runId ? state : null;
|
|
1469
|
+
}
|
|
1470
|
+
function recordStep(ctx, runId, label, emitLabel) {
|
|
1471
|
+
const now = Date.now();
|
|
1472
|
+
const previous = currentAttachState(ctx, runId);
|
|
1473
|
+
const priorSteps = previous?.steps ?? [];
|
|
1474
|
+
const settled = priorSteps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
|
|
1475
|
+
const steps = [...settled, { label, startedAtMs: now, status: "running" }];
|
|
1476
|
+
patchData(ctx, {
|
|
1477
|
+
piAttach: { ...previous ?? { runId, status: "preparing", message: label }, runId, steps }
|
|
1478
|
+
});
|
|
1479
|
+
emitProgress(ctx, emitLabel, label);
|
|
1480
|
+
}
|
|
1481
|
+
function settleSteps(ctx, runId) {
|
|
1482
|
+
const now = Date.now();
|
|
1483
|
+
const previous = currentAttachState(ctx, runId);
|
|
1484
|
+
if (!previous?.steps)
|
|
1485
|
+
return;
|
|
1486
|
+
const steps = previous.steps.map((step) => step.status === "running" ? { ...step, status: "done", endedAtMs: step.endedAtMs ?? now } : step);
|
|
1487
|
+
patchData(ctx, { piAttach: { ...previous, steps } });
|
|
1207
1488
|
}
|
|
1208
1489
|
async function preparePiAttachHandoff(ctx, runId) {
|
|
1209
1490
|
const cleanRunId = runId.trim();
|
|
@@ -1212,7 +1493,7 @@ async function preparePiAttachHandoff(ctx, runId) {
|
|
|
1212
1493
|
try {
|
|
1213
1494
|
const { getRunDetailsViaServer: getRunDetailsViaServer2, getRunPiStatusViaServer: getRunPiStatusViaServer2, resolveServerConnectionLabel: resolveServerConnectionLabel2 } = await Promise.resolve().then(() => (init__server_client(), exports__server_client));
|
|
1214
1495
|
const projectRoot = projectRootOf(ctx);
|
|
1215
|
-
|
|
1496
|
+
recordStep(ctx, cleanRunId, "connecting to server \xB7 checking run and Pi session readiness", label);
|
|
1216
1497
|
const [run, piStatus, server] = await Promise.all([
|
|
1217
1498
|
getRunDetailsViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
|
|
1218
1499
|
getRunPiStatusViaServer2({ projectRoot }, cleanRunId).catch((error) => ({ error: error instanceof Error ? error.message : String(error) })),
|
|
@@ -1223,13 +1504,21 @@ async function preparePiAttachHandoff(ctx, runId) {
|
|
|
1223
1504
|
status: "preparing",
|
|
1224
1505
|
message: "ready to enter worker Pi",
|
|
1225
1506
|
result: { run, piStatus, server },
|
|
1226
|
-
ready: true
|
|
1507
|
+
ready: true,
|
|
1508
|
+
...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
|
|
1227
1509
|
};
|
|
1228
|
-
patchData(ctx, { piAttach: state });
|
|
1510
|
+
patchData(ctx, { piAttach: state, lastRefreshError: undefined });
|
|
1229
1511
|
emitCompleted(ctx, label, { runId: cleanRunId, server });
|
|
1230
1512
|
return state;
|
|
1231
1513
|
} catch (error) {
|
|
1232
|
-
const
|
|
1514
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1515
|
+
const state = {
|
|
1516
|
+
runId: cleanRunId,
|
|
1517
|
+
status: "failed",
|
|
1518
|
+
message: reason,
|
|
1519
|
+
failureReason: reason,
|
|
1520
|
+
...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
|
|
1521
|
+
};
|
|
1233
1522
|
patchData(ctx, { piAttach: state });
|
|
1234
1523
|
emitFailed(ctx, label, error, { runId: cleanRunId });
|
|
1235
1524
|
throw error;
|
|
@@ -1244,32 +1533,52 @@ async function attachRunWithBundledPi(ctx, runId) {
|
|
|
1244
1533
|
throw error;
|
|
1245
1534
|
}
|
|
1246
1535
|
emitStarted(ctx, label, { piAttach: { runId: cleanRunId, status: "entering-pi" } });
|
|
1247
|
-
patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi
|
|
1536
|
+
patchData(ctx, { piAttach: { runId: cleanRunId, status: "entering-pi", message: "starting bundled Pi", steps: [] } });
|
|
1248
1537
|
try {
|
|
1249
|
-
|
|
1250
|
-
|
|
1538
|
+
recordStep(ctx, cleanRunId, "connecting to server and fetching run transcript", label);
|
|
1539
|
+
await preparePiAttachHandoff(ctx, cleanRunId).catch((error) => {
|
|
1540
|
+
patchData(ctx, { lastRefreshError: normalizeAppError(error).message });
|
|
1541
|
+
return null;
|
|
1542
|
+
});
|
|
1543
|
+
recordStep(ctx, cleanRunId, "spawning Bun PTY host for bundled Pi", label);
|
|
1251
1544
|
const projectRoot = projectRootOf(ctx);
|
|
1252
1545
|
const cols = typeof process.stdout.columns === "number" ? process.stdout.columns : 100;
|
|
1253
1546
|
const rows = Math.max(1, (typeof process.stdout.rows === "number" ? process.stdout.rows : 35) - 1);
|
|
1254
1547
|
const patchTerminal = (snapshot) => {
|
|
1548
|
+
const previousSteps = currentAttachState(ctx, cleanRunId)?.steps;
|
|
1549
|
+
const failed = snapshot.status === "failed";
|
|
1550
|
+
const reason = failed ? snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}` : undefined;
|
|
1255
1551
|
patchData(ctx, {
|
|
1256
1552
|
piTerminal: snapshot,
|
|
1257
1553
|
piAttach: {
|
|
1258
1554
|
runId: cleanRunId,
|
|
1259
|
-
status:
|
|
1260
|
-
message: snapshot.message ?? "bundled Pi running
|
|
1261
|
-
result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode }
|
|
1555
|
+
status: failed ? "failed" : snapshot.status === "exited" ? "returned" : "entering-pi",
|
|
1556
|
+
message: snapshot.message ?? "bundled Pi running",
|
|
1557
|
+
result: { runId: cleanRunId, status: snapshot.status, exitCode: snapshot.exitCode },
|
|
1558
|
+
...previousSteps ? { steps: previousSteps } : {},
|
|
1559
|
+
...reason ? { failureReason: reason } : {}
|
|
1262
1560
|
}
|
|
1263
1561
|
});
|
|
1264
1562
|
};
|
|
1563
|
+
recordStep(ctx, cleanRunId, "starting bundled Pi", label);
|
|
1265
1564
|
await startPiPtyHost({
|
|
1266
1565
|
runId: cleanRunId,
|
|
1267
1566
|
projectRoot,
|
|
1268
1567
|
cols,
|
|
1269
1568
|
rows,
|
|
1270
|
-
onSnapshot:
|
|
1569
|
+
onSnapshot: (snapshot) => {
|
|
1570
|
+
if (snapshot.status === "running" && snapshot.lines.some((entry) => entry.trim().length > 0)) {
|
|
1571
|
+
settleSteps(ctx, cleanRunId);
|
|
1572
|
+
}
|
|
1573
|
+
patchTerminal(snapshot);
|
|
1574
|
+
},
|
|
1271
1575
|
onExit: (snapshot) => {
|
|
1272
1576
|
patchTerminal(snapshot);
|
|
1577
|
+
if (snapshot.status === "failed") {
|
|
1578
|
+
Promise.resolve().then(() => (init_inspect(), exports_inspect)).then(({ loadInspect: loadInspect2 }) => loadInspect2(ctx, cleanRunId)).catch(() => null);
|
|
1579
|
+
emitFailed(ctx, label, new Error(snapshot.message ?? `bundled Pi exited with code ${snapshot.exitCode ?? "unknown"}`), { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1273
1582
|
emitCompleted(ctx, label, { runId: cleanRunId, result: { status: snapshot.status, exitCode: snapshot.exitCode } });
|
|
1274
1583
|
ctx.emit({ type: "scene.change", scene: "fleet", intent: { scene: "fleet", argv: ["runs"], action: { kind: "refresh", label: "Opening runs" } } });
|
|
1275
1584
|
refreshFleet(ctx).catch((error) => emitFailed(ctx, "Refresh runs", error));
|
|
@@ -1284,16 +1593,24 @@ async function attachRunWithBundledPi(ctx, runId) {
|
|
|
1284
1593
|
status: "running",
|
|
1285
1594
|
rendered: "bundled Pi embedded via Bun.Terminal + @xterm/headless"
|
|
1286
1595
|
};
|
|
1287
|
-
emitProgress(ctx, label, "bundled Pi is running
|
|
1596
|
+
emitProgress(ctx, label, "bundled Pi is running", { piAttach: { runId: cleanRunId, status: "entering-pi", message: "bundled Pi running", result: record } });
|
|
1288
1597
|
return record;
|
|
1289
1598
|
} catch (error) {
|
|
1290
|
-
|
|
1599
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1600
|
+
patchData(ctx, {
|
|
1601
|
+
piAttach: {
|
|
1602
|
+
runId: cleanRunId,
|
|
1603
|
+
status: "failed",
|
|
1604
|
+
message: reason,
|
|
1605
|
+
failureReason: reason,
|
|
1606
|
+
...currentAttachState(ctx, cleanRunId)?.steps ? { steps: currentAttachState(ctx, cleanRunId).steps } : {}
|
|
1607
|
+
}
|
|
1608
|
+
});
|
|
1291
1609
|
emitFailed(ctx, label, error, { runId: cleanRunId });
|
|
1292
1610
|
throw error;
|
|
1293
1611
|
}
|
|
1294
1612
|
}
|
|
1295
1613
|
export {
|
|
1296
1614
|
preparePiAttachHandoff,
|
|
1297
|
-
createPiAttachAdapter,
|
|
1298
1615
|
attachRunWithBundledPi
|
|
1299
1616
|
};
|