@h-rig/cli 0.0.6-alpha.34 → 0.0.6-alpha.36

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.
@@ -112,6 +112,7 @@ var PRIMARY_GROUPS = [
112
112
  { command: "show <id>|--run <id> [--raw]", description: "Show a human run summary; --raw prints the full payload.", primary: true },
113
113
  { command: "attach <run-id>|--run <id> [--follow]", description: "Attach to the run; --follow launches native bundled Pi for live Pi runs.", primary: true },
114
114
  { command: "stop [<run-id>|--run <id>]", description: "Request stop for one run or local active runs.", primary: true },
115
+ { command: "steer <run-id> --message <text>", description: "Queue a steering message into a live worker without attaching." },
115
116
  { command: "timeline --run <id> [--follow]", description: "Stream raw run timeline events." },
116
117
  { command: "resume", description: "Resume the most recent interrupted local run." },
117
118
  { command: "restart", description: "Restart the most recent local run from a clean runtime." },
@@ -213,6 +214,19 @@ var ADVANCED_GROUPS = [
213
214
  { command: "hp-next <dev|check|e2e|reset>", description: "Drive the hp-next browser test harness." }
214
215
  ]
215
216
  },
217
+ {
218
+ name: "pi",
219
+ summary: "Manage Pi extension packages for this project (community extensions from npm/git).",
220
+ usage: ["rig pi <list|add|remove|search> [args]"],
221
+ commands: [
222
+ { command: "list", description: "Show project and user Pi extension packages." },
223
+ { command: "add <source>", description: "Add an npm/git Pi extension to .pi/settings.json (auto-installs at next session)." },
224
+ { command: "remove <source>", description: "Remove an operator-added Pi extension." },
225
+ { command: "search [term]", description: "Discover Pi extension packages on the npm registry." }
226
+ ],
227
+ examples: ["rig pi search subagents", "rig pi add pi-subagents", "rig pi list"],
228
+ next: ["Config-managed extensions: declare `runtime: { pi: { packages: [...] } }` in rig.config.ts \u2014 workers pick them up automatically."]
229
+ },
216
230
  {
217
231
  name: "plugin",
218
232
  summary: "Plugin listing, validation, and plugin-contributed commands.",
@@ -802,7 +802,38 @@ function parseWsPayload(message) {
802
802
  return JSON.parse(message.data);
803
803
  return JSON.parse(Buffer.from(message.data).toString("utf8"));
804
804
  }
805
- async function connectWorkerStream(options, ctx, state) {
805
+ var BRIDGE_LOCAL_COMMANDS = new Set(["detach", "quit", "q", "stop"]);
806
+ function registerDaemonCommandsNatively(pi, options, ctx, state, commands, registered) {
807
+ for (const command of commands) {
808
+ const record = recordOf(command);
809
+ const name = typeof record?.name === "string" ? record.name : "";
810
+ if (!name || registered.has(name) || BRIDGE_LOCAL_COMMANDS.has(name))
811
+ continue;
812
+ registered.add(name);
813
+ const description = typeof record?.description === "string" ? record.description : undefined;
814
+ const source = typeof record?.source === "string" ? record.source : "worker";
815
+ try {
816
+ pi.registerCommand(name, {
817
+ description: `[worker ${source}] ${description ?? ""}`.trim(),
818
+ handler: async (args) => {
819
+ const text = `/${name}${args ? ` ${args}` : ""}`;
820
+ appendTranscript(state, "You", text);
821
+ try {
822
+ const result = await runRunPiCommandViaServer(options.context, options.runId, text);
823
+ const message = typeof result.message === "string" ? result.message : "worker command accepted";
824
+ appendTranscript(state, "System", message);
825
+ if (state.nativeStream)
826
+ ctx.ui.notify(message, "info");
827
+ } catch (error) {
828
+ reportBridgeError(ctx, state, error instanceof Error ? error.message : String(error));
829
+ }
830
+ updatePiUi(ctx, state);
831
+ }
832
+ });
833
+ } catch {}
834
+ }
835
+ }
836
+ async function connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands) {
806
837
  const ready = await waitForWorkerReady(options, ctx, state);
807
838
  if (!ready)
808
839
  return;
@@ -857,6 +888,7 @@ async function connectWorkerStream(options, ctx, state) {
857
888
  const record = recordOf(command);
858
889
  return typeof record?.name === "string" ? [`/${record.name}`] : [];
859
890
  });
891
+ registerDaemonCommandsNatively(pi, options, ctx, state, commands, registeredDaemonCommands);
860
892
  catchupDone = true;
861
893
  for (const payload of buffered.splice(0))
862
894
  applyEnvelope(ctx, state, payload);
@@ -984,6 +1016,7 @@ function createRigWorkerPiBridgeExtension(options) {
984
1016
  };
985
1017
  if (options.initialMessageSent)
986
1018
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
1019
+ const registeredDaemonCommands = new Set;
987
1020
  let nativePiUiContextAvailable = false;
988
1021
  pi.on("user_bash", (event) => {
989
1022
  state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
@@ -1016,7 +1049,7 @@ function createRigWorkerPiBridgeExtension(options) {
1016
1049
  });
1017
1050
  return { consume: true };
1018
1051
  });
1019
- connectWorkerStream(options, ctx, state).catch((error) => {
1052
+ connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands).catch((error) => {
1020
1053
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
1021
1054
  updatePiUi(ctx, state);
1022
1055
  });
@@ -1063,8 +1096,6 @@ async function attachRunBundledPiFrontend(context, input) {
1063
1096
  "--no-tools",
1064
1097
  "--no-builtin-tools",
1065
1098
  "--no-skills",
1066
- "--no-prompt-templates",
1067
- "--no-themes",
1068
1099
  "--no-context-files",
1069
1100
  "--no-approve"
1070
1101
  ], {
@@ -576,7 +576,38 @@ function parseWsPayload(message) {
576
576
  return JSON.parse(message.data);
577
577
  return JSON.parse(Buffer.from(message.data).toString("utf8"));
578
578
  }
579
- async function connectWorkerStream(options, ctx, state) {
579
+ var BRIDGE_LOCAL_COMMANDS = new Set(["detach", "quit", "q", "stop"]);
580
+ function registerDaemonCommandsNatively(pi, options, ctx, state, commands, registered) {
581
+ for (const command of commands) {
582
+ const record = recordOf(command);
583
+ const name = typeof record?.name === "string" ? record.name : "";
584
+ if (!name || registered.has(name) || BRIDGE_LOCAL_COMMANDS.has(name))
585
+ continue;
586
+ registered.add(name);
587
+ const description = typeof record?.description === "string" ? record.description : undefined;
588
+ const source = typeof record?.source === "string" ? record.source : "worker";
589
+ try {
590
+ pi.registerCommand(name, {
591
+ description: `[worker ${source}] ${description ?? ""}`.trim(),
592
+ handler: async (args) => {
593
+ const text = `/${name}${args ? ` ${args}` : ""}`;
594
+ appendTranscript(state, "You", text);
595
+ try {
596
+ const result = await runRunPiCommandViaServer(options.context, options.runId, text);
597
+ const message = typeof result.message === "string" ? result.message : "worker command accepted";
598
+ appendTranscript(state, "System", message);
599
+ if (state.nativeStream)
600
+ ctx.ui.notify(message, "info");
601
+ } catch (error) {
602
+ reportBridgeError(ctx, state, error instanceof Error ? error.message : String(error));
603
+ }
604
+ updatePiUi(ctx, state);
605
+ }
606
+ });
607
+ } catch {}
608
+ }
609
+ }
610
+ async function connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands) {
580
611
  const ready = await waitForWorkerReady(options, ctx, state);
581
612
  if (!ready)
582
613
  return;
@@ -631,6 +662,7 @@ async function connectWorkerStream(options, ctx, state) {
631
662
  const record = recordOf(command);
632
663
  return typeof record?.name === "string" ? [`/${record.name}`] : [];
633
664
  });
665
+ registerDaemonCommandsNatively(pi, options, ctx, state, commands, registeredDaemonCommands);
634
666
  catchupDone = true;
635
667
  for (const payload of buffered.splice(0))
636
668
  applyEnvelope(ctx, state, payload);
@@ -758,6 +790,7 @@ function createRigWorkerPiBridgeExtension(options) {
758
790
  };
759
791
  if (options.initialMessageSent)
760
792
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
793
+ const registeredDaemonCommands = new Set;
761
794
  let nativePiUiContextAvailable = false;
762
795
  pi.on("user_bash", (event) => {
763
796
  state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
@@ -790,7 +823,7 @@ function createRigWorkerPiBridgeExtension(options) {
790
823
  });
791
824
  return { consume: true };
792
825
  });
793
- connectWorkerStream(options, ctx, state).catch((error) => {
826
+ connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands).catch((error) => {
794
827
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
795
828
  updatePiUi(ctx, state);
796
829
  });
@@ -837,8 +870,6 @@ async function attachRunBundledPiFrontend(context, input) {
837
870
  "--no-tools",
838
871
  "--no-builtin-tools",
839
872
  "--no-skills",
840
- "--no-prompt-templates",
841
- "--no-themes",
842
873
  "--no-context-files",
843
874
  "--no-approve"
844
875
  ], {
@@ -566,7 +566,38 @@ function parseWsPayload(message) {
566
566
  return JSON.parse(message.data);
567
567
  return JSON.parse(Buffer.from(message.data).toString("utf8"));
568
568
  }
569
- async function connectWorkerStream(options, ctx, state) {
569
+ var BRIDGE_LOCAL_COMMANDS = new Set(["detach", "quit", "q", "stop"]);
570
+ function registerDaemonCommandsNatively(pi, options, ctx, state, commands, registered) {
571
+ for (const command of commands) {
572
+ const record = recordOf(command);
573
+ const name = typeof record?.name === "string" ? record.name : "";
574
+ if (!name || registered.has(name) || BRIDGE_LOCAL_COMMANDS.has(name))
575
+ continue;
576
+ registered.add(name);
577
+ const description = typeof record?.description === "string" ? record.description : undefined;
578
+ const source = typeof record?.source === "string" ? record.source : "worker";
579
+ try {
580
+ pi.registerCommand(name, {
581
+ description: `[worker ${source}] ${description ?? ""}`.trim(),
582
+ handler: async (args) => {
583
+ const text = `/${name}${args ? ` ${args}` : ""}`;
584
+ appendTranscript(state, "You", text);
585
+ try {
586
+ const result = await runRunPiCommandViaServer(options.context, options.runId, text);
587
+ const message = typeof result.message === "string" ? result.message : "worker command accepted";
588
+ appendTranscript(state, "System", message);
589
+ if (state.nativeStream)
590
+ ctx.ui.notify(message, "info");
591
+ } catch (error) {
592
+ reportBridgeError(ctx, state, error instanceof Error ? error.message : String(error));
593
+ }
594
+ updatePiUi(ctx, state);
595
+ }
596
+ });
597
+ } catch {}
598
+ }
599
+ }
600
+ async function connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands) {
570
601
  const ready = await waitForWorkerReady(options, ctx, state);
571
602
  if (!ready)
572
603
  return;
@@ -621,6 +652,7 @@ async function connectWorkerStream(options, ctx, state) {
621
652
  const record = recordOf(command);
622
653
  return typeof record?.name === "string" ? [`/${record.name}`] : [];
623
654
  });
655
+ registerDaemonCommandsNatively(pi, options, ctx, state, commands, registeredDaemonCommands);
624
656
  catchupDone = true;
625
657
  for (const payload of buffered.splice(0))
626
658
  applyEnvelope(ctx, state, payload);
@@ -748,6 +780,7 @@ function createRigWorkerPiBridgeExtension(options) {
748
780
  };
749
781
  if (options.initialMessageSent)
750
782
  appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
783
+ const registeredDaemonCommands = new Set;
751
784
  let nativePiUiContextAvailable = false;
752
785
  pi.on("user_bash", (event) => {
753
786
  state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
@@ -780,7 +813,7 @@ function createRigWorkerPiBridgeExtension(options) {
780
813
  });
781
814
  return { consume: true };
782
815
  });
783
- connectWorkerStream(options, ctx, state).catch((error) => {
816
+ connectWorkerStream(options, pi, ctx, state, registeredDaemonCommands).catch((error) => {
784
817
  appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
785
818
  updatePiUi(ctx, state);
786
819
  });
@@ -416,14 +416,6 @@ async function runRunPiCommandViaServer(context, runId, text) {
416
416
  });
417
417
  return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
418
418
  }
419
- async function respondRunPiCommandViaServer(context, runId, requestId, value) {
420
- const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands/respond`, {
421
- method: "POST",
422
- headers: { "content-type": "application/json" },
423
- body: JSON.stringify({ requestId, value })
424
- });
425
- return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
426
- }
427
419
  async function respondRunPiExtensionUiViaServer(context, runId, requestId, valueOrCancel) {
428
420
  const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/extension-ui/respond`, {
429
421
  method: "POST",
@@ -487,7 +479,6 @@ export {
487
479
  selectNextWorkspaceTaskViaServer,
488
480
  runRunPiCommandViaServer,
489
481
  respondRunPiExtensionUiViaServer,
490
- respondRunPiCommandViaServer,
491
482
  requestServerJson,
492
483
  registerProjectViaServer,
493
484
  prepareRemoteCheckoutViaServer,
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  // packages/cli/src/commands/inspect.ts
3
- import { existsSync, readFileSync } from "fs";
4
- import { resolve } from "path";
3
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
4
+ import { resolve as resolve3 } from "path";
5
5
 
6
6
  // packages/cli/src/runner.ts
7
7
  import { EventBus } from "@rig/runtime/control-plane/runtime/events";
@@ -53,6 +53,205 @@ import {
53
53
  import { changedFilesForTask } from "@rig/runtime/control-plane/native/task-ops";
54
54
  import { resolveHarnessPaths, resolveMonorepoRoot, runCapture } from "@rig/runtime/control-plane/native/utils";
55
55
  import { readTaskArtifactPreview } from "@rig/runtime/control-plane/native/workspace-ops";
56
+
57
+ // packages/cli/src/commands/_server-client.ts
58
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
59
+ import { resolve as resolve2 } from "path";
60
+ import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
61
+
62
+ // packages/cli/src/commands/_connection-state.ts
63
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
64
+ import { homedir } from "os";
65
+ import { dirname, resolve } from "path";
66
+ function resolveGlobalConnectionsPath(env = process.env) {
67
+ const explicit = env.RIG_CONNECTIONS_FILE?.trim();
68
+ if (explicit)
69
+ return resolve(explicit);
70
+ const stateDir = env.RIG_GLOBAL_STATE_DIR?.trim();
71
+ if (stateDir)
72
+ return resolve(stateDir, "connections.json");
73
+ return resolve(homedir(), ".rig", "connections.json");
74
+ }
75
+ function resolveRepoConnectionPath(projectRoot) {
76
+ return resolve(projectRoot, ".rig", "state", "connection.json");
77
+ }
78
+ function readJsonFile(path) {
79
+ if (!existsSync(path))
80
+ return null;
81
+ try {
82
+ return JSON.parse(readFileSync(path, "utf8"));
83
+ } catch (error) {
84
+ throw new CliError2(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1);
85
+ }
86
+ }
87
+ function normalizeConnection(value) {
88
+ if (!value || typeof value !== "object" || Array.isArray(value))
89
+ return null;
90
+ const record = value;
91
+ if (record.kind === "local")
92
+ return { kind: "local", mode: "auto" };
93
+ if (record.kind === "remote" && typeof record.baseUrl === "string" && record.baseUrl.trim()) {
94
+ const baseUrl = record.baseUrl.trim().replace(/\/+$/, "");
95
+ return { kind: "remote", baseUrl };
96
+ }
97
+ return null;
98
+ }
99
+ function readGlobalConnections(options = {}) {
100
+ const path = resolveGlobalConnectionsPath(options.env ?? process.env);
101
+ const payload = readJsonFile(path);
102
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
103
+ return { connections: {} };
104
+ }
105
+ const rawConnections = payload.connections;
106
+ const connections = {};
107
+ if (rawConnections && typeof rawConnections === "object" && !Array.isArray(rawConnections)) {
108
+ for (const [alias, raw] of Object.entries(rawConnections)) {
109
+ const connection = normalizeConnection(raw);
110
+ if (connection)
111
+ connections[alias] = connection;
112
+ }
113
+ }
114
+ return { connections };
115
+ }
116
+ function readRepoConnection(projectRoot) {
117
+ const payload = readJsonFile(resolveRepoConnectionPath(projectRoot));
118
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
119
+ return null;
120
+ const record = payload;
121
+ const selected = typeof record.selected === "string" ? record.selected.trim() : "";
122
+ if (!selected)
123
+ return null;
124
+ return {
125
+ selected,
126
+ project: typeof record.project === "string" ? record.project : undefined,
127
+ linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
128
+ };
129
+ }
130
+ function resolveSelectedConnection(projectRoot, options = {}) {
131
+ const repo = readRepoConnection(projectRoot);
132
+ if (!repo)
133
+ return null;
134
+ if (repo.selected === "local")
135
+ return { alias: "local", connection: { kind: "local", mode: "auto" } };
136
+ const global = readGlobalConnections(options);
137
+ const connection = global.connections[repo.selected];
138
+ if (!connection) {
139
+ throw new CliError2(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
140
+ }
141
+ return { alias: repo.selected, connection };
142
+ }
143
+
144
+ // packages/cli/src/commands/_server-client.ts
145
+ var scopedGitHubBearerTokens = new Map;
146
+ function cleanToken(value) {
147
+ const trimmed = value?.trim();
148
+ return trimmed ? trimmed : null;
149
+ }
150
+ function readPrivateRemoteSessionToken(projectRoot) {
151
+ const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
152
+ if (!existsSync2(path))
153
+ return null;
154
+ try {
155
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
156
+ return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+ function readGitHubBearerTokenForRemote(projectRoot) {
162
+ const scopedKey = resolve2(projectRoot);
163
+ if (scopedGitHubBearerTokens.has(scopedKey))
164
+ return scopedGitHubBearerTokens.get(scopedKey) ?? null;
165
+ const privateSession = readPrivateRemoteSessionToken(projectRoot);
166
+ if (privateSession)
167
+ return privateSession;
168
+ return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
169
+ }
170
+ async function ensureServerForCli(projectRoot) {
171
+ try {
172
+ const selected = resolveSelectedConnection(projectRoot);
173
+ if (selected?.connection.kind === "remote") {
174
+ return {
175
+ baseUrl: selected.connection.baseUrl,
176
+ authToken: readGitHubBearerTokenForRemote(projectRoot),
177
+ connectionKind: "remote"
178
+ };
179
+ }
180
+ const connection = await ensureLocalRigServerConnection(projectRoot);
181
+ return {
182
+ baseUrl: connection.baseUrl,
183
+ authToken: connection.authToken,
184
+ connectionKind: "local"
185
+ };
186
+ } catch (error) {
187
+ if (error instanceof Error) {
188
+ throw new CliError2(error.message, 1);
189
+ }
190
+ throw error;
191
+ }
192
+ }
193
+ function mergeHeaders(headers, authToken) {
194
+ const merged = new Headers(headers);
195
+ if (authToken) {
196
+ merged.set("authorization", `Bearer ${authToken}`);
197
+ }
198
+ return merged;
199
+ }
200
+ function diagnosticMessage(payload) {
201
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
202
+ return null;
203
+ const record = payload;
204
+ const diagnostics = Array.isArray(record.diagnostics) ? record.diagnostics : [];
205
+ const messages = diagnostics.flatMap((entry) => {
206
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
207
+ return [];
208
+ const diagnostic = entry;
209
+ const kind = typeof diagnostic.kind === "string" ? diagnostic.kind : "task-source";
210
+ const message = typeof diagnostic.message === "string" ? diagnostic.message : null;
211
+ return message ? [`${kind}: ${message}`] : [];
212
+ });
213
+ return messages.length > 0 ? messages.join("; ") : null;
214
+ }
215
+ async function requestServerJson(context, pathname, init = {}) {
216
+ const server = await ensureServerForCli(context.projectRoot);
217
+ const response = await fetch(`${server.baseUrl}${pathname}`, {
218
+ ...init,
219
+ headers: mergeHeaders(init.headers, server.authToken)
220
+ });
221
+ const text = await response.text();
222
+ const payload = text.trim().length > 0 ? (() => {
223
+ try {
224
+ return JSON.parse(text);
225
+ } catch {
226
+ return null;
227
+ }
228
+ })() : null;
229
+ if (!response.ok) {
230
+ const diagnostics = diagnosticMessage(payload);
231
+ const detail = diagnostics ?? (text || response.statusText);
232
+ throw new CliError2(`Rig server request failed (${response.status}): ${detail}`, 1);
233
+ }
234
+ return payload;
235
+ }
236
+ async function listRunsViaServer(context, options = {}) {
237
+ const url = new URL("http://rig.local/api/runs");
238
+ if (options.limit !== undefined)
239
+ url.searchParams.set("limit", String(options.limit));
240
+ const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
241
+ const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
242
+ return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
243
+ }
244
+ async function getRunLogsViaServer(context, runId, options = {}) {
245
+ const url = new URL(`http://rig.local/api/runs/${encodeURIComponent(runId)}/logs`);
246
+ if (options.limit !== undefined)
247
+ url.searchParams.set("limit", String(options.limit));
248
+ if (options.cursor)
249
+ url.searchParams.set("cursor", options.cursor);
250
+ const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
251
+ return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { entries: [] };
252
+ }
253
+
254
+ // packages/cli/src/commands/inspect.ts
56
255
  async function executeInspect(context, args) {
57
256
  const [command = "failures", ...rest] = args;
58
257
  switch (command) {
@@ -62,10 +261,27 @@ async function executeInspect(context, args) {
62
261
  const requiredTask = requireTask(task, "rig inspect logs --task <task-id>");
63
262
  const latestRun = listAuthorityRuns(context.projectRoot).map((entry) => readAuthorityRun(context.projectRoot, entry.runId)).filter((run) => Boolean(run)).filter((run) => run.taskId === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
64
263
  if (!latestRun) {
65
- throw new CliError2(`No runs found for ${requiredTask}.`);
264
+ const serverRuns = await listRunsViaServer(context, { limit: 200 }).catch(() => []);
265
+ const serverRun = serverRuns.filter((run) => String(run.taskId ?? "") === requiredTask).sort((left, right) => String(right.updatedAt ?? "").localeCompare(String(left.updatedAt ?? "")))[0];
266
+ if (!serverRun || typeof serverRun.runId !== "string") {
267
+ throw new CliError2(`No runs found for ${requiredTask} (local or on the selected server).`);
268
+ }
269
+ const page = await getRunLogsViaServer(context, serverRun.runId, { limit: 500 });
270
+ const entries = Array.isArray(page.entries) ? page.entries : [];
271
+ if (context.outputMode === "text") {
272
+ for (const entry of entries) {
273
+ const record = entry && typeof entry === "object" ? entry : {};
274
+ const title = String(record.title ?? "");
275
+ const detail = String(record.detail ?? "");
276
+ console.log([title, detail].filter(Boolean).join(" \u2014 "));
277
+ }
278
+ if (entries.length === 0)
279
+ console.log(`(no log entries for run ${serverRun.runId})`);
280
+ }
281
+ return { ok: true, group: "inspect", command, details: { task: requiredTask, runId: serverRun.runId, source: "server", entries } };
66
282
  }
67
- const logsPath = resolve(resolveAuthorityRunDir(context.projectRoot, latestRun.runId), "logs.jsonl");
68
- if (!existsSync(logsPath)) {
283
+ const logsPath = resolve3(resolveAuthorityRunDir(context.projectRoot, latestRun.runId), "logs.jsonl");
284
+ if (!existsSync3(logsPath)) {
69
285
  throw new CliError2(`No logs found for run ${latestRun.runId}.`);
70
286
  }
71
287
  await context.runCommand(["cat", logsPath]);
@@ -75,7 +291,7 @@ async function executeInspect(context, args) {
75
291
  const { value: task, rest: remaining } = takeOption(rest, "--task");
76
292
  requireNoExtraArgs(remaining, "rig inspect artifacts --task <task-id>");
77
293
  const requiredTask = requireTask(task, "rig inspect artifacts --task <task-id>");
78
- const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync(path));
294
+ const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync3(path));
79
295
  if (!artifactRoot) {
80
296
  throw new CliError2(`No artifacts found for ${requiredTask}.`);
81
297
  }
@@ -132,10 +348,10 @@ async function executeInspect(context, args) {
132
348
  case "failures": {
133
349
  requireNoExtraArgs(rest, "rig inspect failures");
134
350
  const failed = resolveHarnessPaths(context.projectRoot).failedApproachesPath;
135
- if (!existsSync(failed)) {
351
+ if (!existsSync3(failed)) {
136
352
  console.log("No failures recorded.");
137
353
  } else {
138
- process.stdout.write(readFileSync(failed, "utf-8"));
354
+ process.stdout.write(readFileSync3(failed, "utf-8"));
139
355
  }
140
356
  return { ok: true, group: "inspect", command };
141
357
  }
@@ -152,11 +368,11 @@ async function executeInspect(context, args) {
152
368
  return { ok: true, group: "inspect", command };
153
369
  case "audit": {
154
370
  requireNoExtraArgs(rest, "rig inspect audit");
155
- const auditPath = resolve(resolveHarnessPaths(context.projectRoot).logsDir, "audit.jsonl");
156
- if (!existsSync(auditPath)) {
371
+ const auditPath = resolve3(resolveHarnessPaths(context.projectRoot).logsDir, "audit.jsonl");
372
+ if (!existsSync3(auditPath)) {
157
373
  console.log("No audit log found.");
158
374
  } else {
159
- const lines = readFileSync(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
375
+ const lines = readFileSync3(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
160
376
  for (const line of lines) {
161
377
  console.log(line);
162
378
  }