@h-rig/cli 0.0.6-alpha.17 → 0.0.6-alpha.19
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/rig.js +192 -35
- package/dist/src/commands/_operator-surface.js +48 -1
- package/dist/src/commands/_operator-view.js +48 -1
- package/dist/src/commands/_server-client.js +9 -0
- package/dist/src/commands/init.js +74 -23
- package/dist/src/commands/run.js +91 -12
- package/dist/src/commands/task-run-driver.js +27 -0
- package/dist/src/commands/task.js +48 -1
- package/dist/src/commands.js +192 -35
- package/dist/src/index.js +192 -35
- package/package.json +5 -5
package/dist/bin/rig.js
CHANGED
|
@@ -2886,6 +2886,14 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
2886
2886
|
}
|
|
2887
2887
|
throw new CliError2(`Rig server did not switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no status")}).`, 1);
|
|
2888
2888
|
}
|
|
2889
|
+
async function listRunsViaServer(context, options = {}) {
|
|
2890
|
+
const url = new URL("http://rig.local/api/runs");
|
|
2891
|
+
if (options.limit !== undefined)
|
|
2892
|
+
url.searchParams.set("limit", String(options.limit));
|
|
2893
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
2894
|
+
const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
|
|
2895
|
+
return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
|
|
2896
|
+
}
|
|
2889
2897
|
async function getRunDetailsViaServer(context, runId) {
|
|
2890
2898
|
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}`);
|
|
2891
2899
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
@@ -4529,6 +4537,7 @@ function countDoctorFailures(checks) {
|
|
|
4529
4537
|
|
|
4530
4538
|
// packages/cli/src/commands/init.ts
|
|
4531
4539
|
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4540
|
+
var DEFAULT_REMOTE_RIG_URL = "https://where.rig-does.work";
|
|
4532
4541
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4533
4542
|
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4534
4543
|
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
@@ -4633,6 +4642,19 @@ function readGhAuthToken() {
|
|
|
4633
4642
|
}
|
|
4634
4643
|
return result.stdout.trim();
|
|
4635
4644
|
}
|
|
4645
|
+
function refreshGhProjectScopesAndReadToken() {
|
|
4646
|
+
const result = spawnSync("gh", ["auth", "refresh", "--scopes", "read:project"], {
|
|
4647
|
+
encoding: "utf8",
|
|
4648
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
4649
|
+
});
|
|
4650
|
+
if (result.status !== 0)
|
|
4651
|
+
return null;
|
|
4652
|
+
try {
|
|
4653
|
+
return readGhAuthToken();
|
|
4654
|
+
} catch {
|
|
4655
|
+
return null;
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4636
4658
|
async function loadClackPrompts() {
|
|
4637
4659
|
return await import("@clack/prompts");
|
|
4638
4660
|
}
|
|
@@ -4728,12 +4750,27 @@ async function promptManualProjectStatusMapping(prompts) {
|
|
|
4728
4750
|
}
|
|
4729
4751
|
return statuses;
|
|
4730
4752
|
}
|
|
4731
|
-
|
|
4753
|
+
function projectScopeError(value) {
|
|
4754
|
+
const text2 = typeof value === "string" ? value : JSON.stringify(value ?? "");
|
|
4755
|
+
return /INSUFFICIENT_SCOPES|read:project|required scopes/i.test(text2);
|
|
4756
|
+
}
|
|
4757
|
+
function optionName(option) {
|
|
4758
|
+
return String(option.name ?? option.label ?? option.id ?? "").trim();
|
|
4759
|
+
}
|
|
4760
|
+
function autoProjectStatusValue(options, key, label) {
|
|
4761
|
+
const candidates = [DEFAULT_PROJECT_STATUS_OPTIONS[key], label].filter((value) => Boolean(value)).map((value) => value.trim().toLowerCase());
|
|
4762
|
+
const match = options.find((option) => candidates.includes(optionName(option).toLowerCase()));
|
|
4763
|
+
if (!match)
|
|
4764
|
+
return null;
|
|
4765
|
+
return String(match.id ?? match.name);
|
|
4766
|
+
}
|
|
4767
|
+
async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken, refreshProjectToken) {
|
|
4732
4768
|
const projectChoice = await promptSelect(prompts, {
|
|
4733
4769
|
message: "GitHub Projects status sync",
|
|
4770
|
+
initialValue: "select",
|
|
4734
4771
|
options: [
|
|
4735
|
-
{ value: "off", label: "Off" },
|
|
4736
4772
|
{ value: "select", label: "Select accessible ProjectV2" },
|
|
4773
|
+
{ value: "off", label: "Off" },
|
|
4737
4774
|
{ value: "manual", label: "Enter ProjectV2 ids manually" }
|
|
4738
4775
|
]
|
|
4739
4776
|
});
|
|
@@ -4749,16 +4786,22 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
|
|
|
4749
4786
|
const owner = repoOwnerFromSlug(repoSlug);
|
|
4750
4787
|
if (!owner)
|
|
4751
4788
|
throw new CliError2(`Cannot derive GitHub owner from repo slug ${repoSlug}.`, 1);
|
|
4752
|
-
|
|
4753
|
-
|
|
4789
|
+
let activeToken = githubToken?.trim() || null;
|
|
4790
|
+
let projectsPayload = await listGitHubProjectsForInit(context, owner, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
|
|
4791
|
+
let projects = recordArray(projectsPayload, "projects");
|
|
4792
|
+
if (projects.length === 0 && projectScopeError(projectsPayload.error) && refreshProjectToken) {
|
|
4793
|
+
prompts.outro?.("GitHub token is missing read:project; refreshing gh auth scopes and retrying Projects.");
|
|
4794
|
+
const refreshedToken = refreshProjectToken();
|
|
4795
|
+
if (refreshedToken) {
|
|
4796
|
+
activeToken = refreshedToken;
|
|
4797
|
+
projectsPayload = await listGitHubProjectsForInit(context, owner, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error), projects: [] }));
|
|
4798
|
+
projects = recordArray(projectsPayload, "projects");
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4754
4801
|
if (projects.length === 0) {
|
|
4755
|
-
const error = typeof projectsPayload.error === "string" ? ` (${projectsPayload.error})` : "";
|
|
4756
|
-
prompts.outro?.(`No accessible GitHub Projects were returned${error};
|
|
4757
|
-
return {
|
|
4758
|
-
githubProject: await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }),
|
|
4759
|
-
githubProjectStatusField: await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" }),
|
|
4760
|
-
githubProjectStatuses: await promptManualProjectStatusMapping(prompts)
|
|
4761
|
-
};
|
|
4802
|
+
const error = typeof projectsPayload.error === "string" ? ` (${String(projectsPayload.error).replace(/\s+/g, " ").slice(0, 240)})` : "";
|
|
4803
|
+
prompts.outro?.(`No accessible GitHub Projects were returned${error}; continuing with GitHub Projects status sync off.`);
|
|
4804
|
+
return { githubProject: "off", ...activeToken ? { githubToken: activeToken } : {} };
|
|
4762
4805
|
}
|
|
4763
4806
|
const selectedProjectId = await promptSelect(prompts, {
|
|
4764
4807
|
message: "GitHub ProjectV2 project",
|
|
@@ -4772,23 +4815,34 @@ async function promptGitHubProjectConfig(context, prompts, repoSlug, githubToken
|
|
|
4772
4815
|
]
|
|
4773
4816
|
});
|
|
4774
4817
|
const projectId = selectedProjectId === "manual" ? await promptRequiredText(prompts, { message: "GitHub ProjectV2 id", placeholder: "PVT_..." }) : selectedProjectId;
|
|
4775
|
-
const fieldPayload = await getGitHubProjectStatusFieldForInit(context, projectId,
|
|
4818
|
+
const fieldPayload = await getGitHubProjectStatusFieldForInit(context, projectId, activeToken).catch((error) => ({ ok: false, error: error instanceof Error ? error.message : String(error) }));
|
|
4776
4819
|
const fieldPayloadRecord = fieldPayload && typeof fieldPayload === "object" && !Array.isArray(fieldPayload) ? fieldPayload : {};
|
|
4777
4820
|
const rawField = fieldPayloadRecord.field;
|
|
4778
4821
|
const field = rawField && typeof rawField === "object" && !Array.isArray(rawField) ? rawField : null;
|
|
4779
4822
|
const fieldId = typeof field?.id === "string" && field.id.trim() ? field.id : await promptRequiredText(prompts, { message: "Project Status field id", placeholder: "field_status" });
|
|
4780
4823
|
const options = Array.isArray(field?.options) ? field.options.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry))) : [];
|
|
4781
4824
|
if (options.length === 0) {
|
|
4782
|
-
return {
|
|
4825
|
+
return {
|
|
4826
|
+
githubProject: projectId,
|
|
4827
|
+
githubProjectStatusField: fieldId,
|
|
4828
|
+
githubProjectStatuses: await promptManualProjectStatusMapping(prompts),
|
|
4829
|
+
...activeToken ? { githubToken: activeToken } : {}
|
|
4830
|
+
};
|
|
4783
4831
|
}
|
|
4784
4832
|
const statuses = {};
|
|
4785
4833
|
for (const [key, label] of Object.entries(PROJECT_STATUS_PROMPTS)) {
|
|
4786
|
-
|
|
4834
|
+
const auto = autoProjectStatusValue(options, key, label);
|
|
4835
|
+
statuses[key] = auto ?? await promptSelect(prompts, {
|
|
4787
4836
|
message: `Project status option for ${label}`,
|
|
4788
|
-
options: options.map((option) => ({ value: String(option.id ?? option.name), label:
|
|
4837
|
+
options: options.map((option) => ({ value: String(option.id ?? option.name), label: optionName(option) }))
|
|
4789
4838
|
});
|
|
4790
4839
|
}
|
|
4791
|
-
return {
|
|
4840
|
+
return {
|
|
4841
|
+
githubProject: projectId,
|
|
4842
|
+
githubProjectStatusField: fieldId,
|
|
4843
|
+
githubProjectStatuses: Object.keys(statuses).length > 0 ? statuses : undefined,
|
|
4844
|
+
...activeToken ? { githubToken: activeToken } : {}
|
|
4845
|
+
};
|
|
4792
4846
|
}
|
|
4793
4847
|
function sleep2(ms) {
|
|
4794
4848
|
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
@@ -5119,12 +5173,13 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
5119
5173
|
});
|
|
5120
5174
|
const serverChoice = await promptSelect(prompts, {
|
|
5121
5175
|
message: "Rig server",
|
|
5176
|
+
initialValue: "remote",
|
|
5122
5177
|
options: [
|
|
5123
|
-
{ value: "
|
|
5124
|
-
{ value: "
|
|
5178
|
+
{ value: "remote", label: "Remote server", hint: "connect to an HTTPS Rig server" },
|
|
5179
|
+
{ value: "local", label: "Local server", hint: "run on this machine" }
|
|
5125
5180
|
]
|
|
5126
5181
|
});
|
|
5127
|
-
const remoteUrl = serverChoice === "remote" ? await promptRequiredText(prompts, { message: "Remote Rig server URL", placeholder:
|
|
5182
|
+
const remoteUrl = serverChoice === "remote" ? await promptRequiredText(prompts, { message: "Remote Rig server URL", placeholder: DEFAULT_REMOTE_RIG_URL, initialValue: DEFAULT_REMOTE_RIG_URL }) : undefined;
|
|
5128
5183
|
let remoteCheckout;
|
|
5129
5184
|
if (serverChoice === "remote") {
|
|
5130
5185
|
const checkout = await promptSelect(prompts, {
|
|
@@ -5156,31 +5211,35 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
5156
5211
|
{ value: "skip", label: "Skip for now" }
|
|
5157
5212
|
]
|
|
5158
5213
|
});
|
|
5214
|
+
let remoteGhTokenConfirmed = false;
|
|
5159
5215
|
if (serverChoice === "remote" && authMethod === "gh") {
|
|
5160
5216
|
if (!prompts.confirm)
|
|
5161
5217
|
throw new CliError2("Remote gh-token import requires explicit confirmation.", 1);
|
|
5162
5218
|
const confirmed = await prompts.confirm({
|
|
5163
5219
|
message: `This sends a GitHub token from this machine to ${remoteUrl}. Continue?`,
|
|
5164
|
-
initialValue:
|
|
5220
|
+
initialValue: true
|
|
5165
5221
|
});
|
|
5166
5222
|
if (prompts.isCancel(confirmed) || confirmed !== true) {
|
|
5167
5223
|
throw new CliError2("Remote gh-token import cancelled.", 1);
|
|
5168
5224
|
}
|
|
5225
|
+
remoteGhTokenConfirmed = true;
|
|
5169
5226
|
}
|
|
5170
5227
|
const githubToken = authMethod === "token" ? await promptRequiredText(prompts, { message: "GitHub token", placeholder: "ghp_..." }) : authMethod === "gh" ? readGhAuthToken() : undefined;
|
|
5171
|
-
const projectConfig = await promptGitHubProjectConfig(context, prompts, repoSlug, githubToken);
|
|
5228
|
+
const projectConfig = await promptGitHubProjectConfig(context, prompts, repoSlug, githubToken, authMethod === "gh" ? refreshGhProjectScopesAndReadToken : undefined);
|
|
5229
|
+
const effectiveGithubToken = projectConfig.githubToken ?? githubToken;
|
|
5172
5230
|
const result = await runControlPlaneInit(context, {
|
|
5173
5231
|
server: serverChoice,
|
|
5174
5232
|
remoteUrl,
|
|
5175
5233
|
repoSlug,
|
|
5176
|
-
githubToken,
|
|
5234
|
+
githubToken: effectiveGithubToken,
|
|
5177
5235
|
githubAuthMethod: authMethod,
|
|
5178
5236
|
githubProject: projectConfig.githubProject,
|
|
5179
5237
|
githubProjectStatusField: projectConfig.githubProjectStatusField,
|
|
5180
5238
|
githubProjectStatuses: projectConfig.githubProjectStatuses,
|
|
5181
5239
|
remoteCheckout,
|
|
5182
5240
|
repair,
|
|
5183
|
-
privateStateOnly
|
|
5241
|
+
privateStateOnly,
|
|
5242
|
+
yes: remoteGhTokenConfirmed || undefined
|
|
5184
5243
|
});
|
|
5185
5244
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
5186
5245
|
const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
|
|
@@ -6355,6 +6414,42 @@ var CANONICAL_STAGES = [
|
|
|
6355
6414
|
function logDetail(log3) {
|
|
6356
6415
|
return typeof log3.detail === "string" ? log3.detail.trim() : "";
|
|
6357
6416
|
}
|
|
6417
|
+
function parseProviderProtocolLog(title, detail) {
|
|
6418
|
+
if (title.trim().toLowerCase() !== "agent output")
|
|
6419
|
+
return null;
|
|
6420
|
+
if (!detail.startsWith("{") || !detail.endsWith("}"))
|
|
6421
|
+
return null;
|
|
6422
|
+
try {
|
|
6423
|
+
const record = JSON.parse(detail);
|
|
6424
|
+
if (!record || typeof record !== "object" || Array.isArray(record))
|
|
6425
|
+
return null;
|
|
6426
|
+
const type = record.type;
|
|
6427
|
+
return typeof type === "string" && [
|
|
6428
|
+
"assistant",
|
|
6429
|
+
"message_start",
|
|
6430
|
+
"message_update",
|
|
6431
|
+
"message_end",
|
|
6432
|
+
"stream_event",
|
|
6433
|
+
"tool_result",
|
|
6434
|
+
"tool_execution_start",
|
|
6435
|
+
"tool_execution_update",
|
|
6436
|
+
"tool_execution_end",
|
|
6437
|
+
"turn_start",
|
|
6438
|
+
"turn_end"
|
|
6439
|
+
].includes(type) ? record : null;
|
|
6440
|
+
} catch {
|
|
6441
|
+
return null;
|
|
6442
|
+
}
|
|
6443
|
+
}
|
|
6444
|
+
function renderProviderProtocolLog(record) {
|
|
6445
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
6446
|
+
if (type === "tool_execution_start" || type === "tool_execution_update" || type === "tool_execution_end") {
|
|
6447
|
+
const toolName = String(record.toolName ?? record.name ?? "tool");
|
|
6448
|
+
const status = type === "tool_execution_start" ? "started" : type === "tool_execution_end" ? record.isError === true || record.result && typeof record.result === "object" && !Array.isArray(record.result) && record.result.isError === true ? "failed" : "completed" : "running";
|
|
6449
|
+
return `[Pi tool] ${toolName} ${status}`;
|
|
6450
|
+
}
|
|
6451
|
+
return null;
|
|
6452
|
+
}
|
|
6358
6453
|
function entryId(entry, fallback) {
|
|
6359
6454
|
return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
|
|
6360
6455
|
}
|
|
@@ -6399,12 +6494,16 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
6399
6494
|
if (entry.type === "assistant_message" && typeof entry.text === "string") {
|
|
6400
6495
|
const text2 = entry.text;
|
|
6401
6496
|
const previousText = assistantTextById.get(id) ?? "";
|
|
6497
|
+
if (!previousText && text2.trim()) {
|
|
6498
|
+
writeLine("[Pi assistant]");
|
|
6499
|
+
}
|
|
6402
6500
|
if (text2.startsWith(previousText)) {
|
|
6403
6501
|
const delta = text2.slice(previousText.length);
|
|
6404
6502
|
if (delta)
|
|
6405
6503
|
output.write(delta);
|
|
6406
6504
|
} else if (text2.trim() && text2 !== previousText) {
|
|
6407
|
-
|
|
6505
|
+
if (previousText)
|
|
6506
|
+
writeLine(`
|
|
6408
6507
|
[Pi assistant]`);
|
|
6409
6508
|
output.write(text2);
|
|
6410
6509
|
}
|
|
@@ -6435,6 +6534,13 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
6435
6534
|
const detail = logDetail(entry);
|
|
6436
6535
|
if (!detail)
|
|
6437
6536
|
continue;
|
|
6537
|
+
const protocolRecord = parseProviderProtocolLog(title, detail);
|
|
6538
|
+
if (protocolRecord) {
|
|
6539
|
+
const protocolLine = renderProviderProtocolLog(protocolRecord);
|
|
6540
|
+
if (protocolLine)
|
|
6541
|
+
writeLine(protocolLine);
|
|
6542
|
+
continue;
|
|
6543
|
+
}
|
|
6438
6544
|
writeLine(`[${title || "Rig log"}] ${detail}`);
|
|
6439
6545
|
}
|
|
6440
6546
|
}
|
|
@@ -6582,6 +6688,28 @@ function normalizeRemoteRunDetails(payload) {
|
|
|
6582
6688
|
...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
|
|
6583
6689
|
};
|
|
6584
6690
|
}
|
|
6691
|
+
var REMOTE_TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged"]);
|
|
6692
|
+
function isRemoteConnectionSelected(projectRoot) {
|
|
6693
|
+
return resolveSelectedConnection(projectRoot)?.connection.kind === "remote";
|
|
6694
|
+
}
|
|
6695
|
+
async function listRunsForSelectedConnection(context, options = {}) {
|
|
6696
|
+
if (isRemoteConnectionSelected(context.projectRoot)) {
|
|
6697
|
+
return { runs: await listRunsViaServer(context, options), source: "server" };
|
|
6698
|
+
}
|
|
6699
|
+
return { runs: listAuthorityRuns3(context.projectRoot), source: "local" };
|
|
6700
|
+
}
|
|
6701
|
+
function runStringField(run, key, fallback = "") {
|
|
6702
|
+
const value = run[key];
|
|
6703
|
+
return typeof value === "string" && value.trim() ? value : fallback;
|
|
6704
|
+
}
|
|
6705
|
+
function runDisplayTitle(run) {
|
|
6706
|
+
return runStringField(run, "title", runStringField(run, "taskId", "(untitled)"));
|
|
6707
|
+
}
|
|
6708
|
+
function buildServerRunStatus(runs) {
|
|
6709
|
+
const activeRuns = runs.filter((run) => !REMOTE_TERMINAL_RUN_STATUSES.has(runStringField(run, "status").toLowerCase()));
|
|
6710
|
+
const recentRuns = runs.filter((run) => REMOTE_TERMINAL_RUN_STATUSES.has(runStringField(run, "status").toLowerCase()));
|
|
6711
|
+
return { activeRuns, recentRuns, runs };
|
|
6712
|
+
}
|
|
6585
6713
|
function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
|
|
6586
6714
|
if (noEpicPrompt) {
|
|
6587
6715
|
return false;
|
|
@@ -6647,17 +6775,17 @@ async function executeRun(context, args) {
|
|
|
6647
6775
|
switch (command) {
|
|
6648
6776
|
case "list": {
|
|
6649
6777
|
requireNoExtraArgs(rest, "bun run rig run list");
|
|
6650
|
-
const runs =
|
|
6778
|
+
const { runs, source } = await listRunsForSelectedConnection(context, { limit: 100 });
|
|
6651
6779
|
if (context.outputMode === "text") {
|
|
6652
6780
|
if (runs.length === 0) {
|
|
6653
|
-
console.log("No runs recorded in .rig/runs.");
|
|
6781
|
+
console.log(source === "server" ? "No runs recorded on the selected Rig server." : "No runs recorded in .rig/runs.");
|
|
6654
6782
|
} else {
|
|
6655
6783
|
for (const run of runs) {
|
|
6656
|
-
console.log(`- ${run
|
|
6784
|
+
console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runDisplayTitle(run)}`);
|
|
6657
6785
|
}
|
|
6658
6786
|
}
|
|
6659
6787
|
}
|
|
6660
|
-
return { ok: true, group: "run", command, details: { runs } };
|
|
6788
|
+
return { ok: true, group: "run", command, details: { runs, source } };
|
|
6661
6789
|
}
|
|
6662
6790
|
case "delete": {
|
|
6663
6791
|
let pending = rest;
|
|
@@ -6794,17 +6922,19 @@ async function executeRun(context, args) {
|
|
|
6794
6922
|
}
|
|
6795
6923
|
return { ok: true, group: "run", command };
|
|
6796
6924
|
}
|
|
6797
|
-
const summary = runStatus(context.projectRoot, runtimeContext);
|
|
6925
|
+
const summary = isRemoteConnectionSelected(context.projectRoot) ? buildServerRunStatus(await listRunsViaServer(context, { limit: 100 })) : runStatus(context.projectRoot, runtimeContext);
|
|
6926
|
+
const activeRuns = Array.isArray(summary.activeRuns) ? summary.activeRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
6927
|
+
const recentRuns = Array.isArray(summary.recentRuns) ? summary.recentRuns.filter((run) => Boolean(run && typeof run === "object" && !Array.isArray(run))) : [];
|
|
6798
6928
|
if (context.outputMode === "text") {
|
|
6799
|
-
console.log(`Active runs: ${
|
|
6800
|
-
for (const run of
|
|
6801
|
-
console.log(`- ${run
|
|
6929
|
+
console.log(`Active runs: ${activeRuns.length}`);
|
|
6930
|
+
for (const run of activeRuns) {
|
|
6931
|
+
console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runStringField(run, "taskId", runDisplayTitle(run))}`);
|
|
6802
6932
|
}
|
|
6803
|
-
if (
|
|
6933
|
+
if (recentRuns.length > 0) {
|
|
6804
6934
|
console.log("");
|
|
6805
6935
|
console.log("Recent runs:");
|
|
6806
|
-
for (const run of
|
|
6807
|
-
console.log(`- ${run
|
|
6936
|
+
for (const run of recentRuns) {
|
|
6937
|
+
console.log(`- ${runStringField(run, "runId", "(unknown-run)")} \xB7 ${runStringField(run, "status", "unknown")} \xB7 ${runStringField(run, "taskId", runDisplayTitle(run))}`);
|
|
6808
6938
|
}
|
|
6809
6939
|
}
|
|
6810
6940
|
}
|
|
@@ -8240,6 +8370,26 @@ function appendAssistantTimelineFromRecord(input) {
|
|
|
8240
8370
|
}
|
|
8241
8371
|
return nextAssistantText;
|
|
8242
8372
|
}
|
|
8373
|
+
function appendPiToolTimelineFromRecord(input) {
|
|
8374
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
8375
|
+
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
8376
|
+
return false;
|
|
8377
|
+
const toolCallId = typeof input.record.toolCallId === "string" && input.record.toolCallId.trim() ? input.record.toolCallId.trim() : `${Date.now()}`;
|
|
8378
|
+
const toolName = typeof input.record.toolName === "string" && input.record.toolName.trim() ? input.record.toolName.trim() : "tool";
|
|
8379
|
+
const result = input.record.result && typeof input.record.result === "object" && !Array.isArray(input.record.result) ? input.record.result : null;
|
|
8380
|
+
appendRunTimeline(input.projectRoot, input.runId, {
|
|
8381
|
+
id: `tool:${toolCallId}:${type}`,
|
|
8382
|
+
type,
|
|
8383
|
+
toolName,
|
|
8384
|
+
status: type === "tool_execution_end" ? input.record.isError === true || result?.isError === true ? "failed" : "completed" : "running",
|
|
8385
|
+
createdAt: new Date().toISOString()
|
|
8386
|
+
});
|
|
8387
|
+
return true;
|
|
8388
|
+
}
|
|
8389
|
+
function isNonRenderablePiProtocolRecord(record) {
|
|
8390
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
8391
|
+
return type === "message_start" || type === "message_end" || type === "turn_start" || type === "turn_end" || type === "tool_result" || type === "message_update" && (!record.assistantMessageEvent || typeof record.assistantMessageEvent !== "object" || Array.isArray(record.assistantMessageEvent) || record.assistantMessageEvent.type !== "text_delta");
|
|
8392
|
+
}
|
|
8243
8393
|
function appendToolTimelineFromLog(input) {
|
|
8244
8394
|
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
8245
8395
|
if (title !== "Tool activity")
|
|
@@ -8765,6 +8915,10 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8765
8915
|
try {
|
|
8766
8916
|
const record = JSON.parse(trimmed);
|
|
8767
8917
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
8918
|
+
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
8919
|
+
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
8920
|
+
return;
|
|
8921
|
+
}
|
|
8768
8922
|
const providerLogs = input.runtimeAdapter === "codex" ? buildCodexLogsFromRecord({
|
|
8769
8923
|
runId: input.runId,
|
|
8770
8924
|
record,
|
|
@@ -8836,6 +8990,9 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8836
8990
|
return;
|
|
8837
8991
|
}
|
|
8838
8992
|
}
|
|
8993
|
+
if (input.runtimeAdapter === "pi" && isNonRenderablePiProtocolRecord(record)) {
|
|
8994
|
+
return;
|
|
8995
|
+
}
|
|
8839
8996
|
if (record.type === "assistant") {
|
|
8840
8997
|
const message2 = record.message && typeof record.message === "object" ? record.message : record;
|
|
8841
8998
|
const content = Array.isArray(message2.content) ? message2.content : [];
|
|
@@ -19,6 +19,42 @@ var CANONICAL_STAGES = [
|
|
|
19
19
|
function logDetail(log) {
|
|
20
20
|
return typeof log.detail === "string" ? log.detail.trim() : "";
|
|
21
21
|
}
|
|
22
|
+
function parseProviderProtocolLog(title, detail) {
|
|
23
|
+
if (title.trim().toLowerCase() !== "agent output")
|
|
24
|
+
return null;
|
|
25
|
+
if (!detail.startsWith("{") || !detail.endsWith("}"))
|
|
26
|
+
return null;
|
|
27
|
+
try {
|
|
28
|
+
const record = JSON.parse(detail);
|
|
29
|
+
if (!record || typeof record !== "object" || Array.isArray(record))
|
|
30
|
+
return null;
|
|
31
|
+
const type = record.type;
|
|
32
|
+
return typeof type === "string" && [
|
|
33
|
+
"assistant",
|
|
34
|
+
"message_start",
|
|
35
|
+
"message_update",
|
|
36
|
+
"message_end",
|
|
37
|
+
"stream_event",
|
|
38
|
+
"tool_result",
|
|
39
|
+
"tool_execution_start",
|
|
40
|
+
"tool_execution_update",
|
|
41
|
+
"tool_execution_end",
|
|
42
|
+
"turn_start",
|
|
43
|
+
"turn_end"
|
|
44
|
+
].includes(type) ? record : null;
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function renderProviderProtocolLog(record) {
|
|
50
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
51
|
+
if (type === "tool_execution_start" || type === "tool_execution_update" || type === "tool_execution_end") {
|
|
52
|
+
const toolName = String(record.toolName ?? record.name ?? "tool");
|
|
53
|
+
const status = type === "tool_execution_start" ? "started" : type === "tool_execution_end" ? record.isError === true || record.result && typeof record.result === "object" && !Array.isArray(record.result) && record.result.isError === true ? "failed" : "completed" : "running";
|
|
54
|
+
return `[Pi tool] ${toolName} ${status}`;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
22
58
|
function entryId(entry, fallback) {
|
|
23
59
|
return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
|
|
24
60
|
}
|
|
@@ -63,12 +99,16 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
63
99
|
if (entry.type === "assistant_message" && typeof entry.text === "string") {
|
|
64
100
|
const text = entry.text;
|
|
65
101
|
const previousText = assistantTextById.get(id) ?? "";
|
|
102
|
+
if (!previousText && text.trim()) {
|
|
103
|
+
writeLine("[Pi assistant]");
|
|
104
|
+
}
|
|
66
105
|
if (text.startsWith(previousText)) {
|
|
67
106
|
const delta = text.slice(previousText.length);
|
|
68
107
|
if (delta)
|
|
69
108
|
output.write(delta);
|
|
70
109
|
} else if (text.trim() && text !== previousText) {
|
|
71
|
-
|
|
110
|
+
if (previousText)
|
|
111
|
+
writeLine(`
|
|
72
112
|
[Pi assistant]`);
|
|
73
113
|
output.write(text);
|
|
74
114
|
}
|
|
@@ -99,6 +139,13 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
99
139
|
const detail = logDetail(entry);
|
|
100
140
|
if (!detail)
|
|
101
141
|
continue;
|
|
142
|
+
const protocolRecord = parseProviderProtocolLog(title, detail);
|
|
143
|
+
if (protocolRecord) {
|
|
144
|
+
const protocolLine = renderProviderProtocolLog(protocolRecord);
|
|
145
|
+
if (protocolLine)
|
|
146
|
+
writeLine(protocolLine);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
102
149
|
writeLine(`[${title || "Rig log"}] ${detail}`);
|
|
103
150
|
}
|
|
104
151
|
}
|
|
@@ -247,6 +247,42 @@ var CANONICAL_STAGES = [
|
|
|
247
247
|
function logDetail(log) {
|
|
248
248
|
return typeof log.detail === "string" ? log.detail.trim() : "";
|
|
249
249
|
}
|
|
250
|
+
function parseProviderProtocolLog(title, detail) {
|
|
251
|
+
if (title.trim().toLowerCase() !== "agent output")
|
|
252
|
+
return null;
|
|
253
|
+
if (!detail.startsWith("{") || !detail.endsWith("}"))
|
|
254
|
+
return null;
|
|
255
|
+
try {
|
|
256
|
+
const record = JSON.parse(detail);
|
|
257
|
+
if (!record || typeof record !== "object" || Array.isArray(record))
|
|
258
|
+
return null;
|
|
259
|
+
const type = record.type;
|
|
260
|
+
return typeof type === "string" && [
|
|
261
|
+
"assistant",
|
|
262
|
+
"message_start",
|
|
263
|
+
"message_update",
|
|
264
|
+
"message_end",
|
|
265
|
+
"stream_event",
|
|
266
|
+
"tool_result",
|
|
267
|
+
"tool_execution_start",
|
|
268
|
+
"tool_execution_update",
|
|
269
|
+
"tool_execution_end",
|
|
270
|
+
"turn_start",
|
|
271
|
+
"turn_end"
|
|
272
|
+
].includes(type) ? record : null;
|
|
273
|
+
} catch {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function renderProviderProtocolLog(record) {
|
|
278
|
+
const type = typeof record.type === "string" ? record.type : "";
|
|
279
|
+
if (type === "tool_execution_start" || type === "tool_execution_update" || type === "tool_execution_end") {
|
|
280
|
+
const toolName = String(record.toolName ?? record.name ?? "tool");
|
|
281
|
+
const status = type === "tool_execution_start" ? "started" : type === "tool_execution_end" ? record.isError === true || record.result && typeof record.result === "object" && !Array.isArray(record.result) && record.result.isError === true ? "failed" : "completed" : "running";
|
|
282
|
+
return `[Pi tool] ${toolName} ${status}`;
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
250
286
|
function entryId(entry, fallback) {
|
|
251
287
|
return typeof entry.id === "string" && entry.id.trim() ? entry.id : fallback;
|
|
252
288
|
}
|
|
@@ -291,12 +327,16 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
291
327
|
if (entry.type === "assistant_message" && typeof entry.text === "string") {
|
|
292
328
|
const text = entry.text;
|
|
293
329
|
const previousText = assistantTextById.get(id) ?? "";
|
|
330
|
+
if (!previousText && text.trim()) {
|
|
331
|
+
writeLine("[Pi assistant]");
|
|
332
|
+
}
|
|
294
333
|
if (text.startsWith(previousText)) {
|
|
295
334
|
const delta = text.slice(previousText.length);
|
|
296
335
|
if (delta)
|
|
297
336
|
output.write(delta);
|
|
298
337
|
} else if (text.trim() && text !== previousText) {
|
|
299
|
-
|
|
338
|
+
if (previousText)
|
|
339
|
+
writeLine(`
|
|
300
340
|
[Pi assistant]`);
|
|
301
341
|
output.write(text);
|
|
302
342
|
}
|
|
@@ -327,6 +367,13 @@ function createPiRunStreamRenderer(output = process.stdout) {
|
|
|
327
367
|
const detail = logDetail(entry);
|
|
328
368
|
if (!detail)
|
|
329
369
|
continue;
|
|
370
|
+
const protocolRecord = parseProviderProtocolLog(title, detail);
|
|
371
|
+
if (protocolRecord) {
|
|
372
|
+
const protocolLine = renderProviderProtocolLog(protocolRecord);
|
|
373
|
+
if (protocolLine)
|
|
374
|
+
writeLine(protocolLine);
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
330
377
|
writeLine(`[${title || "Rig log"}] ${detail}`);
|
|
331
378
|
}
|
|
332
379
|
}
|
|
@@ -310,6 +310,14 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
310
310
|
}
|
|
311
311
|
throw new CliError2(`Rig server did not switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no status")}).`, 1);
|
|
312
312
|
}
|
|
313
|
+
async function listRunsViaServer(context, options = {}) {
|
|
314
|
+
const url = new URL("http://rig.local/api/runs");
|
|
315
|
+
if (options.limit !== undefined)
|
|
316
|
+
url.searchParams.set("limit", String(options.limit));
|
|
317
|
+
const payload = await requestServerJson(context, `${url.pathname}${url.search}`);
|
|
318
|
+
const runs = Array.isArray(payload) ? payload : payload && typeof payload === "object" && !Array.isArray(payload) && Array.isArray(payload.runs) ? payload.runs : [];
|
|
319
|
+
return runs.filter((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry)));
|
|
320
|
+
}
|
|
313
321
|
async function getRunDetailsViaServer(context, runId) {
|
|
314
322
|
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}`);
|
|
315
323
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
@@ -414,6 +422,7 @@ export {
|
|
|
414
422
|
prepareRemoteCheckoutViaServer,
|
|
415
423
|
postGitHubTokenViaServer,
|
|
416
424
|
listWorkspaceTasksViaServer,
|
|
425
|
+
listRunsViaServer,
|
|
417
426
|
listGitHubProjectsViaServer,
|
|
418
427
|
getWorkspaceTaskViaServer,
|
|
419
428
|
getRunTimelineViaServer,
|