@h-rig/cli 0.0.6-alpha.90 → 0.0.6-alpha.91
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/README.md +18 -19
- package/dist/bin/build-rig-binaries.js +22 -10
- package/dist/bin/rig.d.ts +71 -1
- package/dist/bin/rig.js +15078 -11169
- package/dist/config/rig-default-config.yml +5 -0
- package/dist/src/app/drone-ui.d.ts +11 -14
- package/dist/src/app/drone-ui.js +70 -86
- package/dist/src/commands/_async-ui.d.ts +1 -4
- package/dist/src/commands/_async-ui.js +9 -111
- package/dist/src/commands/_cli-format.d.ts +16 -9
- package/dist/src/commands/_cli-format.js +167 -295
- package/dist/src/commands/_connection-state.d.ts +11 -1
- package/dist/src/commands/_connection-state.js +50 -5
- package/dist/src/commands/_doctor-checks.d.ts +0 -6
- package/dist/src/commands/_doctor-checks.js +79 -382
- package/dist/src/commands/_help-catalog.d.ts +1 -1
- package/dist/src/commands/_help-catalog.js +217 -157
- package/dist/src/commands/_inprocess-services.d.ts +33 -0
- package/dist/src/commands/_inprocess-services.js +102 -0
- package/dist/src/commands/_json-output.js +4 -0
- package/dist/src/commands/_lazy-reconcile.d.ts +34 -0
- package/dist/src/commands/_lazy-reconcile.js +102 -0
- package/dist/src/commands/_paths.js +1 -1
- package/dist/src/commands/_pi-frontend.d.ts +18 -10
- package/dist/src/commands/_pi-frontend.js +37 -715
- package/dist/src/commands/_pi-install.js +18 -36
- package/dist/src/commands/_policy.d.ts +1 -1
- package/dist/src/commands/_policy.js +56 -15
- package/dist/src/commands/_run-bridge.d.ts +114 -0
- package/dist/src/commands/_run-bridge.js +387 -0
- package/dist/src/commands/_run-diagnostics.d.ts +9 -0
- package/dist/src/commands/_run-diagnostics.js +51 -0
- package/dist/src/commands/_run-driver-helpers.d.ts +8 -81
- package/dist/src/commands/_run-driver-helpers.js +79 -283
- package/dist/src/commands/_run-projection.d.ts +50 -0
- package/dist/src/commands/_run-projection.js +349 -0
- package/dist/src/commands/_run-subcommands.d.ts +3 -0
- package/dist/src/commands/_run-subcommands.js +31 -0
- package/dist/src/commands/_spinner.js +1 -1
- package/dist/src/commands/agent.d.ts +1 -1
- package/dist/src/commands/agent.js +8559 -239
- package/dist/src/commands/dist.d.ts +1 -1
- package/dist/src/commands/dist.js +27 -19
- package/dist/src/commands/doctor.d.ts +1 -1
- package/dist/src/commands/doctor.js +93 -475
- package/dist/src/commands/github.d.ts +1 -1
- package/dist/src/commands/github.js +113 -387
- package/dist/src/commands/inbox.d.ts +22 -24
- package/dist/src/commands/inbox.js +420 -691
- package/dist/src/commands/init.d.ts +6 -16
- package/dist/src/commands/init.js +334 -971
- package/dist/src/commands/inspect.d.ts +19 -2
- package/dist/src/commands/inspect.js +644 -610
- package/dist/src/commands/pi.d.ts +1 -1
- package/dist/src/commands/plugin.d.ts +1 -1
- package/dist/src/commands/plugin.js +486 -7
- package/dist/src/commands/profile-and-review.d.ts +1 -1
- package/dist/src/commands/profile-and-review.js +94 -56
- package/dist/src/commands/queue.js +1 -21
- package/dist/src/commands/remote.d.ts +1 -1
- package/dist/src/commands/remote.js +837 -14
- package/dist/src/commands/repo-git-harness.d.ts +1 -1
- package/dist/src/commands/repo-git-harness.js +57 -14
- package/dist/src/commands/run.d.ts +20 -2
- package/dist/src/commands/run.js +17579 -1759
- package/dist/src/commands/server.d.ts +2 -6
- package/dist/src/commands/server.js +141 -723
- package/dist/src/commands/setup.d.ts +1 -1
- package/dist/src/commands/setup.js +102 -484
- package/dist/src/commands/stats.d.ts +13 -10
- package/dist/src/commands/stats.js +689 -761
- package/dist/src/commands/task-run-driver.d.ts +50 -88
- package/dist/src/commands/task-run-driver.js +116 -2717
- package/dist/src/commands/task.d.ts +34 -13
- package/dist/src/commands/task.js +668 -2523
- package/dist/src/commands/test.d.ts +1 -1
- package/dist/src/commands/triage.d.ts +11 -0
- package/dist/src/commands/triage.js +227 -0
- package/dist/src/commands/workspace.d.ts +1 -1
- package/dist/src/commands.d.ts +0 -16
- package/dist/src/commands.js +16657 -12250
- package/dist/src/index.js +16528 -12497
- package/dist/src/launcher.js +4 -0
- package/dist/src/operator-cli.d.ts +2 -0
- package/dist/src/operator-cli.js +17837 -0
- package/dist/src/operator-entry.d.ts +1 -0
- package/dist/src/operator-entry.js +3 -0
- package/package.json +18 -12
- package/dist/src/app/board.d.ts +0 -23
- package/dist/src/app/board.js +0 -1786
- package/dist/src/app/theme.d.ts +0 -47
- package/dist/src/app/theme.js +0 -150
- package/dist/src/commands/_authority-runs.d.ts +0 -22
- package/dist/src/commands/_authority-runs.js +0 -110
- package/dist/src/commands/_operator-surface.d.ts +0 -34
- package/dist/src/commands/_operator-surface.js +0 -220
- package/dist/src/commands/_operator-view.d.ts +0 -30
- package/dist/src/commands/_operator-view.js +0 -1070
- package/dist/src/commands/_preflight.d.ts +0 -22
- package/dist/src/commands/_preflight.js +0 -540
- package/dist/src/commands/_run-replay.d.ts +0 -24
- package/dist/src/commands/_run-replay.js +0 -142
- package/dist/src/commands/_server-client.d.ts +0 -186
- package/dist/src/commands/_server-client.js +0 -681
- package/dist/src/commands/_snapshot-upload.d.ts +0 -39
- package/dist/src/commands/_snapshot-upload.js +0 -455
- package/dist/src/commands/_task-picker.d.ts +0 -9
- package/dist/src/commands/_task-picker.js +0 -201
- package/dist/src/commands/browser.d.ts +0 -65
- package/dist/src/commands/browser.js +0 -1173
- package/dist/src/commands/connect.d.ts +0 -7
- package/dist/src/commands/connect.js +0 -419
- package/dist/src/commands/inspector.d.ts +0 -3
- package/dist/src/commands/inspector.js +0 -263
- package/dist/src/commands/task-report-bug.d.ts +0 -19
- package/dist/src/commands/task-report-bug.js +0 -1281
- package/dist/src/report-bug.d.ts +0 -44
- 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 resolve(dirname(process.execPath), "rig-run");
|
|
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_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_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
|
+
};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { type AuthorityRunPatch, type AuthorityRunRecord } from "@rig/runtime/control-plane/authority-files";
|
|
2
|
-
import type { RunStatus } from "@rig/contracts";
|
|
3
1
|
export type SourceTaskContract = {
|
|
4
2
|
id: string;
|
|
5
3
|
title?: string | null;
|
|
@@ -11,89 +9,18 @@ export type SourceTaskContract = {
|
|
|
11
9
|
status?: string | null;
|
|
12
10
|
issueType?: string | null;
|
|
13
11
|
role?: string | null;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
labels?: readonly string[];
|
|
12
|
+
validation?: string[];
|
|
13
|
+
validators?: string[];
|
|
14
|
+
labels?: string[];
|
|
15
|
+
scope?: string[];
|
|
19
16
|
};
|
|
20
|
-
export declare function patchAuthorityRun(projectRoot: string, runId: string, patch: AuthorityRunPatch): AuthorityRunRecord;
|
|
21
|
-
/**
|
|
22
|
-
* THE status mutation on the CLI driver side: transition-checked and
|
|
23
|
-
* journaled as a status-changed event (see @rig/contracts run-journal).
|
|
24
|
-
*/
|
|
25
|
-
export declare function setRunStatusOrFail(projectRoot: string, runId: string, to: RunStatus, options?: {
|
|
26
|
-
reason?: string | null;
|
|
27
|
-
errorText?: string | null;
|
|
28
|
-
}): AuthorityRunRecord;
|
|
29
|
-
export declare function touchAuthorityRun(projectRoot: string, runId: string): void;
|
|
30
|
-
export declare function appendRunTimeline(projectRoot: string, runId: string, value: Record<string, unknown>): void;
|
|
31
|
-
export declare function appendRunLog(projectRoot: string, runId: string, value: Record<string, unknown>): void;
|
|
32
|
-
type RunActionInput = {
|
|
33
|
-
id: string;
|
|
34
|
-
actionType: string;
|
|
35
|
-
title: string;
|
|
36
|
-
detail?: string | null;
|
|
37
|
-
state: "running" | "completed" | "failed";
|
|
38
|
-
startedAt: string;
|
|
39
|
-
completedAt?: string | null;
|
|
40
|
-
payload?: Record<string, unknown>;
|
|
41
|
-
};
|
|
42
|
-
export declare function appendRunAction(projectRoot: string, runId: string, value: RunActionInput): void;
|
|
43
|
-
export type RunActionHandle = {
|
|
44
|
-
startedAt: string;
|
|
45
|
-
complete: (detail?: string | null, payload?: Record<string, unknown>) => void;
|
|
46
|
-
fail: (detail: string, payload?: Record<string, unknown>) => void;
|
|
47
|
-
};
|
|
48
|
-
export declare function startRunAction(projectRoot: string, runId: string, input: {
|
|
49
|
-
actionId: string;
|
|
50
|
-
actionType: string;
|
|
51
|
-
title: string;
|
|
52
|
-
detail?: string | null;
|
|
53
|
-
payload?: Record<string, unknown>;
|
|
54
|
-
}): RunActionHandle;
|
|
55
|
-
export type RunEvent = {
|
|
56
|
-
type: "status";
|
|
57
|
-
runId: string;
|
|
58
|
-
status: string;
|
|
59
|
-
detail?: string | null;
|
|
60
|
-
sessionId?: string | null;
|
|
61
|
-
} | {
|
|
62
|
-
type: "timeline";
|
|
63
|
-
runId: string;
|
|
64
|
-
} | {
|
|
65
|
-
type: "log";
|
|
66
|
-
runId: string;
|
|
67
|
-
title?: string;
|
|
68
|
-
} | {
|
|
69
|
-
type: "completed";
|
|
70
|
-
runId: string;
|
|
71
|
-
} | {
|
|
72
|
-
type: "failed";
|
|
73
|
-
runId: string;
|
|
74
|
-
error: string;
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* Route emitServerRunEvent through the run.jsonl lifecycle log + doorbell in
|
|
78
|
-
* addition to stdout. Called once at the start of executeRigOwnedTaskRun.
|
|
79
|
-
*/
|
|
80
|
-
export declare function configureRunEventSink(projectRoot: string): void;
|
|
81
|
-
/**
|
|
82
|
-
* Print a parseable status line to stdout that the server's spawn-and-watch
|
|
83
|
-
* path reads to update its own state. Format: `__RIG_RUN_EVENT__<json>`.
|
|
84
|
-
*
|
|
85
|
-
* When `configureRunEventSink` has run, the same event is also appended to
|
|
86
|
-
* the run's `run.jsonl` lifecycle log and the server doorbell is rung.
|
|
87
|
-
*/
|
|
88
|
-
export declare function emitServerRunEvent(event: RunEvent): void;
|
|
89
17
|
export declare function buildRunPrompt(input: {
|
|
90
18
|
projectRoot: string;
|
|
91
|
-
taskId?: string;
|
|
92
|
-
fallbackTitle?: string |
|
|
93
|
-
fallbackDescription?: string |
|
|
94
|
-
fallbackAcceptanceCriteria?: string |
|
|
19
|
+
taskId?: string | undefined;
|
|
20
|
+
fallbackTitle?: string | undefined;
|
|
21
|
+
fallbackDescription?: string | undefined;
|
|
22
|
+
fallbackAcceptanceCriteria?: string | undefined;
|
|
95
23
|
sourceTask?: SourceTaskContract | null;
|
|
96
24
|
initialPrompt?: string;
|
|
97
25
|
runtimeAdapter: "claude-code" | "codex" | "pi";
|
|
98
26
|
}): string;
|
|
99
|
-
export {};
|