@h-rig/cli 0.0.6-alpha.1 → 0.0.6-alpha.100

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.
Files changed (120) hide show
  1. package/README.md +18 -19
  2. package/dist/bin/build-rig-binaries.d.ts +2 -0
  3. package/dist/bin/build-rig-binaries.js +22 -10
  4. package/dist/bin/rig.d.ts +72 -0
  5. package/dist/bin/rig.js +15742 -7203
  6. package/dist/config/rig-default-config.yml +5 -0
  7. package/dist/src/app/drone-ui.d.ts +34 -0
  8. package/dist/src/app/drone-ui.js +278 -0
  9. package/dist/src/commands/_async-ui.d.ts +10 -0
  10. package/dist/src/commands/_async-ui.js +121 -0
  11. package/dist/src/commands/_cli-format.d.ts +56 -0
  12. package/dist/src/commands/_cli-format.js +319 -0
  13. package/dist/src/commands/_config-env.d.ts +18 -0
  14. package/dist/src/commands/_config-env.js +22 -0
  15. package/dist/src/commands/_connection-state.d.ts +54 -0
  16. package/dist/src/commands/_connection-state.js +75 -11
  17. package/dist/src/commands/_doctor-checks.d.ts +46 -0
  18. package/dist/src/commands/_doctor-checks.js +95 -244
  19. package/dist/src/commands/_help-catalog.d.ts +29 -0
  20. package/dist/src/commands/_help-catalog.js +146 -0
  21. package/dist/src/commands/_inprocess-services.d.ts +33 -0
  22. package/dist/src/commands/_inprocess-services.js +102 -0
  23. package/dist/src/commands/_json-output.d.ts +11 -0
  24. package/dist/src/commands/_json-output.js +60 -0
  25. package/dist/src/commands/_lazy-reconcile.d.ts +34 -0
  26. package/dist/src/commands/_lazy-reconcile.js +102 -0
  27. package/dist/src/commands/_parsers.d.ts +15 -0
  28. package/dist/src/commands/_parsers.js +18 -11
  29. package/dist/src/commands/_paths.d.ts +11 -0
  30. package/dist/src/commands/_paths.js +1 -1
  31. package/dist/src/commands/_pi-frontend.d.ts +35 -0
  32. package/dist/src/commands/_pi-frontend.js +64 -0
  33. package/dist/src/commands/_pi-install.d.ts +42 -0
  34. package/dist/src/commands/_pi-install.js +19 -36
  35. package/dist/src/commands/_policy.d.ts +8 -0
  36. package/dist/src/commands/_policy.js +67 -19
  37. package/dist/src/commands/_probes.d.ts +1 -0
  38. package/dist/src/commands/_run-bridge.d.ts +114 -0
  39. package/dist/src/commands/_run-bridge.js +387 -0
  40. package/dist/src/commands/_run-diagnostics.d.ts +9 -0
  41. package/dist/src/commands/_run-diagnostics.js +51 -0
  42. package/dist/src/commands/_run-driver-helpers.d.ts +26 -0
  43. package/dist/src/commands/_run-driver-helpers.js +79 -230
  44. package/dist/src/commands/_run-projection.d.ts +50 -0
  45. package/dist/src/commands/_run-projection.js +349 -0
  46. package/dist/src/commands/_run-subcommands.d.ts +3 -0
  47. package/dist/src/commands/_run-subcommands.js +31 -0
  48. package/dist/src/commands/_spinner.d.ts +25 -0
  49. package/dist/src/commands/_spinner.js +65 -0
  50. package/dist/src/commands/agent.d.ts +3 -0
  51. package/dist/src/commands/agent.js +8690 -235
  52. package/dist/src/commands/config.d.ts +3 -0
  53. package/dist/src/commands/config.js +181 -0
  54. package/dist/src/commands/dist.d.ts +28 -0
  55. package/dist/src/commands/dist.js +44 -29
  56. package/dist/src/commands/doctor.d.ts +3 -0
  57. package/dist/src/commands/doctor.js +214 -245
  58. package/dist/src/commands/github.d.ts +3 -0
  59. package/dist/src/commands/github.js +237 -171
  60. package/dist/src/commands/inbox.d.ts +28 -0
  61. package/dist/src/commands/inbox.js +530 -111
  62. package/dist/src/commands/init.d.ts +64 -0
  63. package/dist/src/commands/init.js +941 -654
  64. package/dist/src/commands/inspect.d.ts +20 -0
  65. package/dist/src/commands/inspect.js +709 -119
  66. package/dist/src/commands/pi.d.ts +3 -0
  67. package/dist/src/commands/pi.js +177 -0
  68. package/dist/src/commands/plugin.d.ts +16 -0
  69. package/dist/src/commands/plugin.js +576 -29
  70. package/dist/src/commands/profile-and-review.d.ts +4 -0
  71. package/dist/src/commands/profile-and-review.js +112 -67
  72. package/dist/src/commands/queue.d.ts +3 -0
  73. package/dist/src/commands/queue.js +20 -20
  74. package/dist/src/commands/remote.d.ts +3 -0
  75. package/dist/src/commands/remote.js +883 -50
  76. package/dist/src/commands/repo-git-harness.d.ts +5 -0
  77. package/dist/src/commands/repo-git-harness.js +78 -28
  78. package/dist/src/commands/run.d.ts +21 -0
  79. package/dist/src/commands/run.js +17732 -618
  80. package/dist/src/commands/server.d.ts +3 -0
  81. package/dist/src/commands/server.js +166 -285
  82. package/dist/src/commands/setup.d.ts +16 -0
  83. package/dist/src/commands/setup.js +230 -267
  84. package/dist/src/commands/stats.d.ts +15 -0
  85. package/dist/src/commands/stats.js +632 -0
  86. package/dist/src/commands/task-run-driver.d.ts +94 -0
  87. package/dist/src/commands/task-run-driver.js +120 -1856
  88. package/dist/src/commands/task.d.ts +35 -0
  89. package/dist/src/commands/task.js +676 -1183
  90. package/dist/src/commands/test.d.ts +3 -0
  91. package/dist/src/commands/test.js +15 -8
  92. package/dist/src/commands/triage.d.ts +11 -0
  93. package/dist/src/commands/triage.js +227 -0
  94. package/dist/src/commands/workspace.d.ts +3 -0
  95. package/dist/src/commands/workspace.js +18 -11
  96. package/dist/src/commands.d.ts +13 -0
  97. package/dist/src/commands.js +16760 -7779
  98. package/dist/src/index.d.ts +4 -0
  99. package/dist/src/index.js +16880 -8223
  100. package/dist/src/launcher.d.ts +61 -0
  101. package/dist/src/launcher.js +81 -13
  102. package/dist/src/operator-cli.d.ts +2 -0
  103. package/dist/src/operator-cli.js +17867 -0
  104. package/dist/src/operator-entry.d.ts +1 -0
  105. package/dist/src/operator-entry.js +3 -0
  106. package/dist/src/runner.d.ts +47 -0
  107. package/dist/src/runner.js +19 -24
  108. package/dist/src/withMutedConsole.d.ts +2 -0
  109. package/package.json +21 -7
  110. package/dist/src/commands/_authority-runs.js +0 -110
  111. package/dist/src/commands/_operator-view.js +0 -322
  112. package/dist/src/commands/_preflight.js +0 -460
  113. package/dist/src/commands/_server-client.js +0 -364
  114. package/dist/src/commands/_snapshot-upload.js +0 -313
  115. package/dist/src/commands/_task-picker.js +0 -48
  116. package/dist/src/commands/browser.js +0 -890
  117. package/dist/src/commands/connect.js +0 -180
  118. package/dist/src/commands/inspector.js +0 -256
  119. package/dist/src/commands/task-report-bug.js +0 -1083
  120. package/dist/src/report-bug.js +0 -260
@@ -0,0 +1,387 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // packages/cli/src/commands/_run-bridge.ts
5
+ import { randomUUID } from "crypto";
6
+ import { spawn as nodeSpawn } from "child_process";
7
+ import { existsSync, readdirSync, readFileSync } from "fs";
8
+ import { dirname, resolve } from "path";
9
+ import { fileURLToPath } from "url";
10
+ import { CUSTOM_TYPE_FOR, sessionIdFromSessionFile } from "@rig/contracts";
11
+ import { createPluginHost } from "@rig/core";
12
+ import { loadConfig } from "@rig/core/load-config";
13
+ import { resolveOwnerNamespaceKey, resolveRegistryBaseUrl, resolveRegistrySecret, resolveSelectedRemote } from "@rig/runtime/control-plane/remote";
14
+ import { listAgentRuntimes } from "@rig/runtime/control-plane/runtime/isolation";
15
+ import { createRegistryClient } from "@rig/relay-registry";
16
+ import {
17
+ listActiveCollabSessions as ompListActiveCollabSessions
18
+ } from "@oh-my-pi/pi-coding-agent/collab/api";
19
+ var DEFAULT_ATTACH_TIMEOUT_MS = 15000;
20
+ var DEFAULT_ATTACH_POLL_INTERVAL_MS = 250;
21
+ function stringField(value) {
22
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
23
+ }
24
+ function githubUserId(value) {
25
+ if (typeof value === "number" && Number.isInteger(value))
26
+ return String(value);
27
+ return stringField(value);
28
+ }
29
+ function readJsonRecord(path) {
30
+ if (!existsSync(path))
31
+ return null;
32
+ try {
33
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
34
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+ function publicIdentityEnv(projectRoot) {
40
+ const auth = readJsonRecord(resolve(projectRoot, ".rig", "state", "github-auth.json"));
41
+ const connection = readJsonRecord(resolve(projectRoot, ".rig", "state", "connection.json"));
42
+ const values = {};
43
+ const selectedRepo = stringField(auth?.selectedRepo) ?? stringField(connection?.project);
44
+ const userId = githubUserId(auth?.userId);
45
+ const login = stringField(auth?.login);
46
+ const namespaceKey = stringField(auth?.userNamespaceKey) ?? resolveOwnerNamespaceKey(projectRoot);
47
+ if (selectedRepo)
48
+ values.RIG_SELECTED_REPO = selectedRepo;
49
+ if (userId)
50
+ values.RIG_GITHUB_USER_ID = userId;
51
+ if (login)
52
+ values.RIG_GITHUB_LOGIN = login;
53
+ if (namespaceKey)
54
+ values.RIG_GITHUB_NAMESPACE_KEY = namespaceKey;
55
+ return values;
56
+ }
57
+ function taskText(value) {
58
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
59
+ }
60
+ function taskUrl(record) {
61
+ const metadata = record;
62
+ return taskText(metadata.url) ?? taskText(metadata.html_url) ?? taskText(metadata.webUrl);
63
+ }
64
+ function taskBody(record) {
65
+ const metadata = record;
66
+ return taskText(metadata.body) ?? taskText(metadata.description);
67
+ }
68
+ function taskTitle(record) {
69
+ const metadata = record;
70
+ return taskText(metadata.title) ?? taskText(metadata.name) ?? record.id;
71
+ }
72
+ function toExtensionTask(record, sourceKind) {
73
+ return {
74
+ id: record.id,
75
+ title: taskTitle(record),
76
+ status: typeof record.status === "string" ? record.status : null,
77
+ source: sourceKind,
78
+ url: taskUrl(record),
79
+ body: taskBody(record)
80
+ };
81
+ }
82
+ async function loadConfiguredTaskSource(projectRoot) {
83
+ const normalizedRoot = resolve(projectRoot);
84
+ const config = await loadConfig(normalizedRoot);
85
+ const pluginHost = createPluginHost(config.plugins);
86
+ const taskSourceFactory = pluginHost.resolveTaskSourceFactoryByKind(config.taskSource.kind);
87
+ if (!taskSourceFactory) {
88
+ const kinds = pluginHost.listExecutableTaskSources().map((entry) => entry.kind).join(", ") || "none";
89
+ throw new Error(`No task source factory registered for kind "${config.taskSource.kind}". Registered kinds: ${kinds}.`);
90
+ }
91
+ return {
92
+ kind: config.taskSource.kind,
93
+ source: taskSourceFactory.factory(config.taskSource, { projectRoot: normalizedRoot })
94
+ };
95
+ }
96
+ async function listTasksFromSource(projectRoot, deps = {}) {
97
+ const { kind, source } = await (deps.loadTaskSource ?? loadConfiguredTaskSource)(projectRoot);
98
+ return (await source.list()).map((task) => toExtensionTask(task, kind));
99
+ }
100
+ async function getTaskFromSource(projectRoot, taskId, deps = {}) {
101
+ const tasks = await listTasksFromSource(projectRoot, deps);
102
+ return tasks.find((task) => task.id === taskId) ?? null;
103
+ }
104
+ async function readSelectedTarget(projectRoot, env = process.env) {
105
+ const config = await loadConfig(resolve(projectRoot));
106
+ const selected = resolveSelectedRemote(projectRoot, env);
107
+ return {
108
+ alias: selected?.alias ?? "local",
109
+ kind: selected ? "remote" : "local",
110
+ projectRoot,
111
+ status: "ready",
112
+ taskSource: config.taskSource.kind
113
+ };
114
+ }
115
+ function runningFromCompiledBinary() {
116
+ return import.meta.url.includes("$bunfs");
117
+ }
118
+ function resolveRigRunBin() {
119
+ if (runningFromCompiledBinary())
120
+ return process.execPath;
121
+ return resolve(dirname(fileURLToPath(import.meta.url)), "../../../rig-host/bin/rig-run.ts");
122
+ }
123
+ async function defaultRunRunProcess(input) {
124
+ const { runRunProcess } = await import("@rig/rig-host");
125
+ return await runRunProcess(input);
126
+ }
127
+ function shellQuote(value) {
128
+ return `'${value.replace(/'/g, "'\\''")}'`;
129
+ }
130
+ function readProjectSlug(projectRoot) {
131
+ const connection = readJsonRecord(resolve(projectRoot, ".rig", "state", "connection.json"));
132
+ return stringField(connection?.project) ?? null;
133
+ }
134
+ function resolveProjectOriginUrl(projectRoot) {
135
+ const slug = readProjectSlug(projectRoot);
136
+ return slug && /^[^/]+\/[^/]+$/.test(slug) ? `https://github.com/${slug}.git` : null;
137
+ }
138
+ async function spawnRunProcess(input, deps = {}) {
139
+ const runId = (deps.uuid ?? randomUUID)();
140
+ const env = deps.env ?? process.env;
141
+ const identityEnv = publicIdentityEnv(input.projectRoot);
142
+ const selected = resolveSelectedRemote(input.projectRoot, env);
143
+ const sshTarget = selected?.sshTarget ?? env.RIG_SSH_TARGET?.trim() ?? env.RIG_REMOTE_ALIAS?.trim();
144
+ if (sshTarget) {
145
+ const namespaceKey = identityEnv.RIG_GITHUB_NAMESPACE_KEY ?? resolveOwnerNamespaceKey(input.projectRoot, env) ?? "";
146
+ const sh = shellQuote;
147
+ const originUrl = resolveProjectOriginUrl(input.projectRoot);
148
+ const repoName = (readProjectSlug(input.projectRoot)?.split("/").pop() ?? "project").replace(/[^A-Za-z0-9._-]/g, "-");
149
+ const checkoutExpr = selected?.checkout ? sh(selected.checkout) : `"$HOME/.rig/checkouts/${repoName}"`;
150
+ const identityAssignments = Object.entries(identityEnv).map(([k, v]) => `${k}=${sh(v)}`);
151
+ if (namespaceKey && !identityEnv.RIG_GITHUB_NAMESPACE_KEY)
152
+ identityAssignments.push(`RIG_GITHUB_NAMESPACE_KEY=${sh(namespaceKey)}`);
153
+ const runInvocation = [
154
+ `RIG_RUN_ID=${sh(runId)}`,
155
+ `RIG_TASK_ID=${sh(input.taskId)}`,
156
+ ...identityAssignments,
157
+ selected?.registryBaseUrl ? `RIG_REGISTRY_URL=${sh(selected.registryBaseUrl)}` : "",
158
+ `rig run start --task ${sh(input.taskId)}`
159
+ ].filter(Boolean).join(" ");
160
+ const provisionAndRun = [
161
+ "set -e",
162
+ `CHECKOUT=${checkoutExpr}`,
163
+ originUrl ? `[ -d "$CHECKOUT/.git" ] || git clone ${sh(originUrl)} "$CHECKOUT"` : `[ -d "$CHECKOUT/.git" ] || { echo "rig: remote checkout $CHECKOUT missing and no origin to clone" >&2; exit 1; }`,
164
+ `cd "$CHECKOUT"`,
165
+ "git fetch origin --prune --quiet || true",
166
+ runInvocation
167
+ ].join("; ");
168
+ const child = (deps.spawn ?? nodeSpawn)("ssh", [sshTarget, `bash -lc ${sh(provisionAndRun)}`], {
169
+ cwd: input.projectRoot,
170
+ env: { ...env, ...identityEnv },
171
+ detached: true,
172
+ stdio: "ignore"
173
+ });
174
+ child.unref();
175
+ return { runId };
176
+ }
177
+ await (deps.runRunProcess ?? defaultRunRunProcess)({
178
+ projectRoot: input.projectRoot,
179
+ taskId: input.taskId,
180
+ runId,
181
+ title: input.title,
182
+ rigRunBin: (deps.resolveRigRunBin ?? resolveRigRunBin)()
183
+ });
184
+ return { runId };
185
+ }
186
+ function buildRemoteControlCommand(runId, control, ctx) {
187
+ const verb = control.kind === "stop" ? `rig run stop ${shellQuote(runId)}${control.reason.trim() ? ` --reason ${shellQuote(control.reason)}` : ""}` : `rig run steer ${shellQuote(runId)} --message ${shellQuote(control.message)}`;
188
+ return [
189
+ ctx.checkout ? `cd ${shellQuote(ctx.checkout)} &&` : "",
190
+ ctx.namespaceKey ? `RIG_GITHUB_NAMESPACE_KEY=${shellQuote(ctx.namespaceKey)}` : "",
191
+ ctx.registryBaseUrl ? `RIG_REGISTRY_URL=${shellQuote(ctx.registryBaseUrl)}` : "",
192
+ verb
193
+ ].filter(Boolean).join(" ");
194
+ }
195
+ async function deliverRemoteRunControl(projectRoot, runId, control, deps = {}) {
196
+ const env = deps.env ?? process.env;
197
+ const selected = resolveSelectedRemote(projectRoot, env);
198
+ const sshTarget = selected?.sshTarget ?? env.RIG_SSH_TARGET?.trim() ?? env.RIG_REMOTE_ALIAS?.trim();
199
+ if (!sshTarget) {
200
+ throw new Error(`Run ${runId} is remote but no SSH target is configured (set one with \`rig server use <alias>\`).`);
201
+ }
202
+ const identityEnv = publicIdentityEnv(projectRoot);
203
+ const namespaceKey = identityEnv.RIG_GITHUB_NAMESPACE_KEY ?? resolveOwnerNamespaceKey(projectRoot, env) ?? "";
204
+ const remoteCommand = buildRemoteControlCommand(runId, control, {
205
+ checkout: selected?.checkout ?? null,
206
+ registryBaseUrl: selected?.registryBaseUrl ?? null,
207
+ namespaceKey
208
+ });
209
+ await new Promise((resolveDone, rejectDone) => {
210
+ const child = (deps.spawn ?? nodeSpawn)("ssh", [sshTarget, `bash -lc ${shellQuote(remoteCommand)}`], {
211
+ cwd: projectRoot,
212
+ env: { ...env, ...identityEnv },
213
+ stdio: ["ignore", "ignore", "pipe"]
214
+ });
215
+ let stderr = "";
216
+ child.stderr?.on("data", (chunk) => {
217
+ stderr += String(chunk);
218
+ });
219
+ child.on("error", rejectDone);
220
+ child.on("close", (code) => {
221
+ if (code === 0) {
222
+ resolveDone();
223
+ return;
224
+ }
225
+ const detail = stderr.trim();
226
+ rejectDone(new Error(`remote \`rig run ${control.kind}\` exited ${code ?? "null"}${detail ? `: ${detail}` : ""}`));
227
+ });
228
+ });
229
+ }
230
+ function registrySecret(env = process.env) {
231
+ return resolveRegistrySecret(process.cwd(), env) ?? null;
232
+ }
233
+ function registryBaseUrl(env = process.env) {
234
+ return resolveRegistryBaseUrl(process.cwd(), env);
235
+ }
236
+ function collabFromRegistryEntry(entry) {
237
+ return {
238
+ sessionId: entry.roomId,
239
+ sessionPath: "",
240
+ cwd: "",
241
+ title: entry.title,
242
+ joinLink: entry.joinLink,
243
+ webLink: entry.webLink,
244
+ relayUrl: entry.relayUrl,
245
+ owner: entry.owner,
246
+ selectedRepo: entry.repo,
247
+ startedAt: entry.startedAt,
248
+ updatedAt: entry.heartbeatAt,
249
+ ...entry.pid === undefined ? {} : { pid: entry.pid },
250
+ stale: entry.stale
251
+ };
252
+ }
253
+ function isRecord(value) {
254
+ return typeof value === "object" && value !== null;
255
+ }
256
+ function readLocalRunCollab(runtime) {
257
+ if (!runtime.sessionDir || !existsSync(runtime.sessionDir))
258
+ return null;
259
+ let files;
260
+ try {
261
+ files = readdirSync(runtime.sessionDir).filter((f) => f.endsWith(".jsonl")).sort().reverse();
262
+ } catch {
263
+ return null;
264
+ }
265
+ for (const file of files) {
266
+ let lines;
267
+ try {
268
+ lines = readFileSync(resolve(runtime.sessionDir, file), "utf8").split(`
269
+ `);
270
+ } catch {
271
+ continue;
272
+ }
273
+ let host = null;
274
+ for (const line of lines) {
275
+ if (!line.includes("collab-host-started"))
276
+ continue;
277
+ let entry;
278
+ try {
279
+ entry = JSON.parse(line);
280
+ } catch {
281
+ continue;
282
+ }
283
+ if (!isRecord(entry) || entry.customType !== CUSTOM_TYPE_FOR["timeline-entry"] || !isRecord(entry.data))
284
+ continue;
285
+ const payload = isRecord(entry.data.payload) ? entry.data.payload : entry.data;
286
+ if (payload.type !== "collab-host-started")
287
+ continue;
288
+ host = {
289
+ roomId: typeof payload.roomId === "string" ? payload.roomId : undefined,
290
+ joinLink: typeof payload.joinLink === "string" ? payload.joinLink : undefined,
291
+ webLink: typeof payload.webLink === "string" ? payload.webLink : undefined,
292
+ relayUrl: typeof payload.relayUrl === "string" ? payload.relayUrl : undefined,
293
+ at: typeof entry.data.at === "string" ? entry.data.at : undefined
294
+ };
295
+ }
296
+ if (!host?.joinLink)
297
+ continue;
298
+ const at = host.at ?? "";
299
+ const sessionId = host.roomId ?? sessionIdFromSessionFile(resolve(runtime.sessionDir, file)) ?? runtime.id;
300
+ return {
301
+ sessionId,
302
+ sessionPath: resolve(runtime.sessionDir, file),
303
+ cwd: runtime.workspaceDir,
304
+ joinLink: host.joinLink,
305
+ webLink: host.webLink ?? "",
306
+ relayUrl: host.relayUrl ?? "",
307
+ title: runtime.taskId ? `[${runtime.taskId}] Rig run ${sessionId}` : `Rig run ${sessionId}`,
308
+ startedAt: at,
309
+ updatedAt: at,
310
+ stale: false
311
+ };
312
+ }
313
+ return null;
314
+ }
315
+ async function listLocalRunCollabSessions(projectRoot) {
316
+ let runtimes;
317
+ try {
318
+ runtimes = await listAgentRuntimes(projectRoot);
319
+ } catch {
320
+ return [];
321
+ }
322
+ const out = [];
323
+ for (const runtime of runtimes) {
324
+ const collab = readLocalRunCollab(runtime);
325
+ if (collab)
326
+ out.push(collab);
327
+ }
328
+ return out;
329
+ }
330
+ async function listActiveRunCollab(filter) {
331
+ return defaultListActiveCollabSessions(filter);
332
+ }
333
+ async function defaultListActiveCollabSessions(filter) {
334
+ const projectRoot = process.cwd();
335
+ const local = await listLocalRunCollabSessions(projectRoot);
336
+ const namespaceKey = filter.namespaceKey ?? resolveOwnerNamespaceKey(projectRoot);
337
+ const secret = registrySecret();
338
+ const remote = namespaceKey && secret ? (await createRegistryClient({ baseUrl: registryBaseUrl(), namespaceKey, secret }).listRoomsByOwner(filter)).map(collabFromRegistryEntry) : await ompListActiveCollabSessions(filter);
339
+ const seen = new Set(local.map((c) => c.sessionId));
340
+ return [...local, ...remote.filter((c) => !seen.has(c.sessionId))];
341
+ }
342
+ function sleep(ms) {
343
+ const { promise, resolve: resolveSleep } = Promise.withResolvers();
344
+ setTimeout(resolveSleep, ms);
345
+ return promise;
346
+ }
347
+ function matchesRun(collab, runId, taskId) {
348
+ if (collab.sessionId === runId)
349
+ return true;
350
+ if (runId && collab.title?.includes(runId))
351
+ return true;
352
+ return Boolean(taskId && collab.title?.includes(taskId));
353
+ }
354
+ async function attachViaRelay(input, deps = {}) {
355
+ const timeoutMs = input.timeoutMs ?? DEFAULT_ATTACH_TIMEOUT_MS;
356
+ const pollIntervalMs = input.pollIntervalMs ?? DEFAULT_ATTACH_POLL_INTERVAL_MS;
357
+ const listActive = deps.listActiveCollabSessions ?? defaultListActiveCollabSessions;
358
+ const now = deps.now ?? Date.now;
359
+ const pause = deps.sleep ?? sleep;
360
+ const deadline = now() + timeoutMs;
361
+ do {
362
+ const live = await listActive(input.identityFilter);
363
+ const match = live.find((collab) => matchesRun(collab, input.runId, input.taskId) && !collab.stale);
364
+ if (match) {
365
+ return {
366
+ sessionPath: match.sessionPath || null,
367
+ joinLink: match.joinLink || null,
368
+ collabSessionId: match.sessionId
369
+ };
370
+ }
371
+ if (now() >= deadline)
372
+ break;
373
+ await pause(pollIntervalMs);
374
+ } while (now() <= deadline);
375
+ return null;
376
+ }
377
+ export {
378
+ spawnRunProcess,
379
+ resolveRigRunBin,
380
+ readSelectedTarget,
381
+ listTasksFromSource,
382
+ listActiveRunCollab,
383
+ getTaskFromSource,
384
+ deliverRemoteRunControl,
385
+ buildRemoteControlCommand,
386
+ attachViaRelay
387
+ };
@@ -0,0 +1,9 @@
1
+ import type { RunJournalProjection } from "@rig/contracts";
2
+ /**
3
+ * Extract the most useful human-facing failure diagnostic from the folded run journal.
4
+ *
5
+ * The live projection deliberately does not retain log/timeline lines, so this mirrors the
6
+ * deleted server-side categorization using only fields that survive folding: record fields,
7
+ * status reasons, closeout details, and reducer anomalies.
8
+ */
9
+ export declare function summarizeUsefulRunError(projection: RunJournalProjection): string | null;
@@ -0,0 +1,51 @@
1
+ // @bun
2
+ // packages/cli/src/commands/_run-diagnostics.ts
3
+ function normalizeString(value) {
4
+ if (typeof value !== "string")
5
+ return null;
6
+ const normalized = value.replace(/\s+/g, " ").trim();
7
+ return normalized.length > 0 ? normalized : null;
8
+ }
9
+ function isGenericRunFailure(value) {
10
+ const text = normalizeString(value);
11
+ return Boolean(text && /^Task run failed \([^)]*\)$/i.test(text));
12
+ }
13
+ function appendCandidate(candidates, value) {
14
+ const text = normalizeString(value);
15
+ if (text && !candidates.includes(text))
16
+ candidates.push(text);
17
+ }
18
+ function categorizeUsefulRunError(lines) {
19
+ const taskSourceFailure = lines.find((line) => /failed to update task source/i.test(line));
20
+ if (taskSourceFailure)
21
+ return `Task source update failed: ${taskSourceFailure}`;
22
+ const moduleFailure = lines.find((line) => /cannot find module/i.test(line));
23
+ if (moduleFailure)
24
+ return `Runtime module resolution failed: ${moduleFailure}`;
25
+ const providerFailure = lines.find((line) => /no api key found|unauthorized|authentication failed|invalid api key/i.test(line));
26
+ if (providerFailure)
27
+ return `Provider authentication failed: ${providerFailure}`;
28
+ return null;
29
+ }
30
+ function summarizeUsefulRunError(projection) {
31
+ if (projection.status !== "failed")
32
+ return null;
33
+ const candidates = [];
34
+ for (const anomaly of projection.anomalies) {
35
+ appendCandidate(candidates, `Journal anomaly (${anomaly.kind}): ${anomaly.detail}`);
36
+ }
37
+ for (const phase of projection.closeoutPhases) {
38
+ if (phase.outcome === "failed")
39
+ appendCandidate(candidates, phase.detail);
40
+ }
41
+ for (const entry of projection.statusHistory) {
42
+ appendCandidate(candidates, entry.reason);
43
+ }
44
+ appendCandidate(candidates, projection.record.statusDetail);
45
+ appendCandidate(candidates, projection.record.errorText);
46
+ const nonGeneric = candidates.filter((candidate) => !isGenericRunFailure(candidate));
47
+ return categorizeUsefulRunError(nonGeneric) ?? nonGeneric.at(-1) ?? normalizeString(projection.record.errorText);
48
+ }
49
+ export {
50
+ summarizeUsefulRunError
51
+ };
@@ -0,0 +1,26 @@
1
+ export type SourceTaskContract = {
2
+ id: string;
3
+ title?: string | null;
4
+ description?: string | null;
5
+ body?: string | null;
6
+ acceptanceCriteria?: string | null;
7
+ acceptance_criteria?: string | null;
8
+ sourceIssueId?: string | null;
9
+ status?: string | null;
10
+ issueType?: string | null;
11
+ role?: string | null;
12
+ validation?: string[];
13
+ validators?: string[];
14
+ labels?: string[];
15
+ scope?: string[];
16
+ };
17
+ export declare function buildRunPrompt(input: {
18
+ projectRoot: string;
19
+ taskId?: string | undefined;
20
+ fallbackTitle?: string | undefined;
21
+ fallbackDescription?: string | undefined;
22
+ fallbackAcceptanceCriteria?: string | undefined;
23
+ sourceTask?: SourceTaskContract | null;
24
+ initialPrompt?: string;
25
+ runtimeAdapter: "claude-code" | "codex" | "pi";
26
+ }): string;