@h-rig/cli 0.0.6-alpha.21 → 0.0.6-alpha.22
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 +332 -120
- package/dist/src/commands/_operator-view.js +235 -0
- package/dist/src/commands/run.js +237 -111
- package/dist/src/commands/task-run-driver.js +92 -7
- package/dist/src/commands/task.js +236 -109
- package/dist/src/commands.js +332 -120
- package/dist/src/index.js +332 -120
- package/package.json +6 -5
- package/dist/src/commands/_pi-session.js +0 -253
package/dist/src/index.js
CHANGED
|
@@ -6590,6 +6590,50 @@ async function promptForTaskSelection(question) {
|
|
|
6590
6590
|
|
|
6591
6591
|
// packages/cli/src/commands/_operator-view.ts
|
|
6592
6592
|
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
6593
|
+
var CANONICAL_STAGES2 = [
|
|
6594
|
+
"Connect",
|
|
6595
|
+
"GitHub/task sync",
|
|
6596
|
+
"Prepare workspace",
|
|
6597
|
+
"Launch Pi",
|
|
6598
|
+
"Plan",
|
|
6599
|
+
"Implement",
|
|
6600
|
+
"Validate",
|
|
6601
|
+
"Commit",
|
|
6602
|
+
"Open PR",
|
|
6603
|
+
"Review/CI",
|
|
6604
|
+
"Merge",
|
|
6605
|
+
"Complete"
|
|
6606
|
+
];
|
|
6607
|
+
var GREEN = "\x1B[32m";
|
|
6608
|
+
var BLUE = "\x1B[34m";
|
|
6609
|
+
var MAGENTA = "\x1B[35m";
|
|
6610
|
+
var YELLOW = "\x1B[33m";
|
|
6611
|
+
var RED = "\x1B[31m";
|
|
6612
|
+
var DIM = "\x1B[2m";
|
|
6613
|
+
var BOLD = "\x1B[1m";
|
|
6614
|
+
var RESET = "\x1B[0m";
|
|
6615
|
+
async function loadPiTuiRuntime() {
|
|
6616
|
+
try {
|
|
6617
|
+
return await import("@earendil-works/pi-tui");
|
|
6618
|
+
} catch {
|
|
6619
|
+
const base = new URL("../../../pi/packages/tui/src/", import.meta.url);
|
|
6620
|
+
const [tui, input, terminal, keys, utils] = await Promise.all([
|
|
6621
|
+
import(new URL("tui.ts", base).href),
|
|
6622
|
+
import(new URL("components/input.ts", base).href),
|
|
6623
|
+
import(new URL("terminal.ts", base).href),
|
|
6624
|
+
import(new URL("keys.ts", base).href),
|
|
6625
|
+
import(new URL("utils.ts", base).href)
|
|
6626
|
+
]);
|
|
6627
|
+
return {
|
|
6628
|
+
Container: tui.Container,
|
|
6629
|
+
TUI: tui.TUI,
|
|
6630
|
+
Input: input.Input,
|
|
6631
|
+
ProcessTerminal: terminal.ProcessTerminal,
|
|
6632
|
+
matchesKey: keys.matchesKey,
|
|
6633
|
+
truncateToWidth: utils.truncateToWidth
|
|
6634
|
+
};
|
|
6635
|
+
}
|
|
6636
|
+
}
|
|
6593
6637
|
function runStatusFromPayload(payload) {
|
|
6594
6638
|
const run = payload.run && typeof payload.run === "object" && !Array.isArray(payload.run) ? payload.run : payload;
|
|
6595
6639
|
return String(run.status ?? "unknown").toLowerCase();
|
|
@@ -6628,12 +6672,201 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
6628
6672
|
const timelineCursor = typeof timelinePage.nextCursor === "string" ? timelinePage.nextCursor : options.timelineCursor ?? null;
|
|
6629
6673
|
return { run, logs, timeline, timelineCursor, rendered: renderOperatorSnapshot({ run, logs, timeline }) };
|
|
6630
6674
|
}
|
|
6675
|
+
function unwrapRun(runPayload) {
|
|
6676
|
+
return runPayload.run && typeof runPayload.run === "object" && !Array.isArray(runPayload.run) ? runPayload.run : runPayload;
|
|
6677
|
+
}
|
|
6678
|
+
function logDetail2(log3) {
|
|
6679
|
+
return typeof log3.detail === "string" ? log3.detail.trim() : "";
|
|
6680
|
+
}
|
|
6681
|
+
function logTitle(log3) {
|
|
6682
|
+
return typeof log3.title === "string" ? log3.title.trim() : "";
|
|
6683
|
+
}
|
|
6684
|
+
function renderAssistantTextFromTimeline(entries) {
|
|
6685
|
+
const assistant = entries.filter((entry) => entry.type === "assistant_message" && typeof entry.text === "string").at(-1);
|
|
6686
|
+
const text2 = typeof assistant?.text === "string" ? assistant.text.trimEnd() : "";
|
|
6687
|
+
if (!text2)
|
|
6688
|
+
return [];
|
|
6689
|
+
return [`${BLUE}${BOLD}Remote Pi assistant${RESET}`, ...text2.split(/\r?\n/).slice(-18)];
|
|
6690
|
+
}
|
|
6691
|
+
function renderToolLines(entries) {
|
|
6692
|
+
return entries.filter((entry) => entry.type === "tool_execution_start" || entry.type === "tool_execution_update" || entry.type === "tool_execution_end" || entry.type === "mcp_tool_call").slice(-8).map((entry) => `${DIM}[tool]${RESET} ${String(entry.toolName ?? entry.name ?? entry.title ?? entry.type)} ${String(entry.status ?? entry.state ?? "")}`.trim());
|
|
6693
|
+
}
|
|
6694
|
+
function renderStageLines(logs) {
|
|
6695
|
+
const latestByStage = new Map;
|
|
6696
|
+
for (const log3 of logs) {
|
|
6697
|
+
const title = logTitle(log3).toLowerCase();
|
|
6698
|
+
const stageName = String(log3.stage ?? "").toLowerCase();
|
|
6699
|
+
const stage = CANONICAL_STAGES2.find((candidate) => candidate.toLowerCase() === title || candidate.toLowerCase() === stageName);
|
|
6700
|
+
if (stage)
|
|
6701
|
+
latestByStage.set(stage, log3);
|
|
6702
|
+
}
|
|
6703
|
+
return CANONICAL_STAGES2.map((stage) => {
|
|
6704
|
+
const log3 = latestByStage.get(stage);
|
|
6705
|
+
const status = String(log3?.status ?? "pending");
|
|
6706
|
+
const detail = log3 ? logDetail2(log3) : "";
|
|
6707
|
+
const color = status === "completed" ? GREEN : status === "failed" || status === "rejected" ? RED : status === "pending" ? DIM : YELLOW;
|
|
6708
|
+
const mark = status === "completed" ? "\u2713" : status === "pending" ? "\xB7" : status === "failed" ? "\u2717" : "\u25B6";
|
|
6709
|
+
return `${color}${mark} ${stage}${RESET}${detail ? ` ${DIM}\u2014 ${detail.slice(0, 140)}${RESET}` : ""}`;
|
|
6710
|
+
});
|
|
6711
|
+
}
|
|
6712
|
+
function renderEventLines(logs) {
|
|
6713
|
+
return logs.filter((log3) => !CANONICAL_STAGES2.some((stage) => stage.toLowerCase() === logTitle(log3).toLowerCase())).slice(-12).flatMap((log3) => {
|
|
6714
|
+
const title = logTitle(log3) || "Rig event";
|
|
6715
|
+
const detail = logDetail2(log3);
|
|
6716
|
+
if (!detail)
|
|
6717
|
+
return [];
|
|
6718
|
+
const tone = String(log3.tone ?? "");
|
|
6719
|
+
const color = tone === "error" ? RED : tone === "tool" ? MAGENTA : DIM;
|
|
6720
|
+
return [`${color}[${title}]${RESET} ${detail.slice(0, 220)}`];
|
|
6721
|
+
});
|
|
6722
|
+
}
|
|
6723
|
+
|
|
6724
|
+
class RigRunComponent {
|
|
6725
|
+
truncateToWidth;
|
|
6726
|
+
snapshot = { run: {}, logs: [], timeline: [] };
|
|
6727
|
+
localEvents = [];
|
|
6728
|
+
constructor(truncateToWidth) {
|
|
6729
|
+
this.truncateToWidth = truncateToWidth;
|
|
6730
|
+
}
|
|
6731
|
+
update(snapshot) {
|
|
6732
|
+
this.snapshot = snapshot;
|
|
6733
|
+
}
|
|
6734
|
+
addLocalEvent(message2) {
|
|
6735
|
+
this.localEvents.push(`${new Date().toLocaleTimeString()} ${message2}`);
|
|
6736
|
+
this.localEvents = this.localEvents.slice(-8);
|
|
6737
|
+
}
|
|
6738
|
+
invalidate() {}
|
|
6739
|
+
render(width) {
|
|
6740
|
+
const run = unwrapRun(this.snapshot.run);
|
|
6741
|
+
const runId = String(run.runId ?? run.id ?? "run");
|
|
6742
|
+
const status = String(run.status ?? "unknown");
|
|
6743
|
+
const worker = typeof run.worktreePath === "string" && run.worktreePath.trim() ? run.worktreePath.trim() : typeof run.projectRoot === "string" && run.projectRoot.trim() ? run.projectRoot.trim() : "worker workspace pending";
|
|
6744
|
+
const lines = [
|
|
6745
|
+
`${BOLD}Rig Pi frontend${RESET} ${DIM}(local Pi TUI \u2192 Rig server \u2192 worker Pi backend)${RESET}`,
|
|
6746
|
+
`${BOLD}${runId}${RESET} \xB7 ${status} \xB7 ${DIM}${worker}${RESET}`,
|
|
6747
|
+
"",
|
|
6748
|
+
`${BOLD}Rig flow${RESET}`,
|
|
6749
|
+
...renderStageLines(this.snapshot.logs ?? []),
|
|
6750
|
+
"",
|
|
6751
|
+
...renderAssistantTextFromTimeline(this.snapshot.timeline ?? []),
|
|
6752
|
+
...renderToolLines(this.snapshot.timeline ?? []),
|
|
6753
|
+
"",
|
|
6754
|
+
`${BOLD}Rig / Pi events${RESET}`,
|
|
6755
|
+
...renderEventLines(this.snapshot.logs ?? []),
|
|
6756
|
+
...this.localEvents.map((event) => `${GREEN}[frontend]${RESET} ${event}`),
|
|
6757
|
+
""
|
|
6758
|
+
];
|
|
6759
|
+
return lines.slice(-42).map((line) => this.truncateToWidth(line, Math.max(10, width)));
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6762
|
+
|
|
6763
|
+
class RigInputComponent {
|
|
6764
|
+
matchesKey;
|
|
6765
|
+
truncateToWidth;
|
|
6766
|
+
input;
|
|
6767
|
+
status = "Type text, /skill:..., or remote Pi slash commands. Local: /stop /detach.";
|
|
6768
|
+
constructor(InputCtor, matchesKey, truncateToWidth, onSubmit, onEscape) {
|
|
6769
|
+
this.matchesKey = matchesKey;
|
|
6770
|
+
this.truncateToWidth = truncateToWidth;
|
|
6771
|
+
this.input = new InputCtor;
|
|
6772
|
+
this.input.onSubmit = (value) => {
|
|
6773
|
+
const text2 = value.trim();
|
|
6774
|
+
this.input.setValue("");
|
|
6775
|
+
if (text2)
|
|
6776
|
+
Promise.resolve(onSubmit(text2));
|
|
6777
|
+
};
|
|
6778
|
+
this.input.onEscape = onEscape;
|
|
6779
|
+
}
|
|
6780
|
+
handleInput(data) {
|
|
6781
|
+
if (this.matchesKey(data, "ctrl+d")) {
|
|
6782
|
+
this.input.onEscape?.();
|
|
6783
|
+
return;
|
|
6784
|
+
}
|
|
6785
|
+
this.input.handleInput?.(data);
|
|
6786
|
+
}
|
|
6787
|
+
setStatus(status) {
|
|
6788
|
+
this.status = status;
|
|
6789
|
+
}
|
|
6790
|
+
invalidate() {
|
|
6791
|
+
this.input.invalidate();
|
|
6792
|
+
}
|
|
6793
|
+
render(width) {
|
|
6794
|
+
return [
|
|
6795
|
+
`${DIM}${this.truncateToWidth(this.status, Math.max(10, width))}${RESET}`,
|
|
6796
|
+
`${GREEN}${BOLD}You \u2192 worker Pi:${RESET}`,
|
|
6797
|
+
...this.input.render(width)
|
|
6798
|
+
];
|
|
6799
|
+
}
|
|
6800
|
+
}
|
|
6801
|
+
async function attachRunPiTuiFrontend(context, input) {
|
|
6802
|
+
const piTui = await loadPiTuiRuntime();
|
|
6803
|
+
const terminal = new piTui.ProcessTerminal;
|
|
6804
|
+
const tui = new piTui.TUI(terminal);
|
|
6805
|
+
const root = new piTui.Container;
|
|
6806
|
+
const runView = new RigRunComponent(piTui.truncateToWidth);
|
|
6807
|
+
let detached = false;
|
|
6808
|
+
let steered = input.steered === true;
|
|
6809
|
+
let latest = await readOperatorSnapshot(context, input.runId);
|
|
6810
|
+
let timelineCursor = latest.timelineCursor;
|
|
6811
|
+
runView.update(latest);
|
|
6812
|
+
if (steered)
|
|
6813
|
+
runView.addLocalEvent("initial message queued to worker Pi.");
|
|
6814
|
+
const stop = () => {
|
|
6815
|
+
detached = true;
|
|
6816
|
+
tui.stop();
|
|
6817
|
+
};
|
|
6818
|
+
const inputView = new RigInputComponent(piTui.Input, piTui.matchesKey, piTui.truncateToWidth, async (line) => {
|
|
6819
|
+
if (line === "/detach" || line === "/quit" || line === "/q") {
|
|
6820
|
+
runView.addLocalEvent("detached from run.");
|
|
6821
|
+
stop();
|
|
6822
|
+
return;
|
|
6823
|
+
}
|
|
6824
|
+
if (line === "/stop") {
|
|
6825
|
+
await stopRunViaServer(context, input.runId);
|
|
6826
|
+
runView.addLocalEvent("stop requested.");
|
|
6827
|
+
stop();
|
|
6828
|
+
return;
|
|
6829
|
+
}
|
|
6830
|
+
await steerRunViaServer(context, input.runId, line);
|
|
6831
|
+
steered = true;
|
|
6832
|
+
runView.addLocalEvent(`queued to worker Pi: ${line.slice(0, 160)}`);
|
|
6833
|
+
tui.requestRender();
|
|
6834
|
+
}, stop);
|
|
6835
|
+
root.addChild(runView);
|
|
6836
|
+
root.addChild(inputView);
|
|
6837
|
+
tui.addChild(root);
|
|
6838
|
+
tui.setFocus(inputView.input);
|
|
6839
|
+
tui.start();
|
|
6840
|
+
tui.requestRender(true);
|
|
6841
|
+
const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 1000));
|
|
6842
|
+
try {
|
|
6843
|
+
while (!detached && !TERMINAL_RUN_STATUSES.has(runStatusFromPayload(latest.run))) {
|
|
6844
|
+
await Bun.sleep(pollMs);
|
|
6845
|
+
latest = await readOperatorSnapshot(context, input.runId, { timelineCursor });
|
|
6846
|
+
timelineCursor = latest.timelineCursor;
|
|
6847
|
+
runView.update(latest);
|
|
6848
|
+
inputView.setStatus(`Remote worker ${runStatusFromPayload(latest.run)}. Input is forwarded to worker Pi; /stop /detach are local controls.`);
|
|
6849
|
+
tui.requestRender();
|
|
6850
|
+
}
|
|
6851
|
+
} finally {
|
|
6852
|
+
if (!detached)
|
|
6853
|
+
tui.stop();
|
|
6854
|
+
}
|
|
6855
|
+
return { ...latest, timelineCursor, steered, detached, rendered: renderOperatorSnapshot(latest) };
|
|
6856
|
+
}
|
|
6631
6857
|
async function attachRunOperatorView(context, input) {
|
|
6632
6858
|
let steered = false;
|
|
6633
6859
|
if (input.message?.trim()) {
|
|
6634
6860
|
await steerRunViaServer(context, input.runId, input.message.trim());
|
|
6635
6861
|
steered = true;
|
|
6636
6862
|
}
|
|
6863
|
+
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
6864
|
+
return attachRunPiTuiFrontend(context, {
|
|
6865
|
+
runId: input.runId,
|
|
6866
|
+
pollMs: input.pollMs,
|
|
6867
|
+
steered
|
|
6868
|
+
});
|
|
6869
|
+
}
|
|
6637
6870
|
const surface = createOperatorSurface({ interactive: input.interactive !== false });
|
|
6638
6871
|
let snapshot = await readOperatorSnapshot(context, input.runId);
|
|
6639
6872
|
if (context.outputMode === "text") {
|
|
@@ -6774,92 +7007,6 @@ function formatSubmittedRun(input) {
|
|
|
6774
7007
|
`);
|
|
6775
7008
|
}
|
|
6776
7009
|
|
|
6777
|
-
// packages/cli/src/commands/_pi-session.ts
|
|
6778
|
-
import { spawn as spawn2 } from "child_process";
|
|
6779
|
-
function buildPiRigSessionEnv(input) {
|
|
6780
|
-
return {
|
|
6781
|
-
RIG_PROJECT_ROOT: input.projectRoot,
|
|
6782
|
-
PROJECT_RIG_ROOT: input.projectRoot,
|
|
6783
|
-
RIG_RUN_ID: input.runId,
|
|
6784
|
-
RIG_SERVER_RUN_ID: input.runId,
|
|
6785
|
-
RIG_RUNTIME_ADAPTER: "pi",
|
|
6786
|
-
RIG_SERVER_URL: input.serverUrl,
|
|
6787
|
-
RIG_SERVER_BASE_URL: input.serverUrl,
|
|
6788
|
-
RIG_STEERING_POLL_MS: process.env.RIG_STEERING_POLL_MS?.trim() || "1000",
|
|
6789
|
-
RIG_PI_OPERATOR_SESSION: "1",
|
|
6790
|
-
...input.taskId ? { RIG_TASK_ID: input.taskId } : {},
|
|
6791
|
-
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {}
|
|
6792
|
-
};
|
|
6793
|
-
}
|
|
6794
|
-
function shellBinary(name) {
|
|
6795
|
-
const explicit = process.env.RIG_PI_BINARY?.trim();
|
|
6796
|
-
if (explicit)
|
|
6797
|
-
return explicit;
|
|
6798
|
-
return Bun.which(name) || name;
|
|
6799
|
-
}
|
|
6800
|
-
function buildPiRigSessionCommand(input) {
|
|
6801
|
-
const configuredExtension = input.extensionSource ?? process.env.RIG_PI_RIG_EXTENSION_SOURCE?.trim();
|
|
6802
|
-
const extensionSource = configuredExtension && configuredExtension.length > 0 ? configuredExtension : resolvePiRigPackageSource(input.projectRoot);
|
|
6803
|
-
const initialCommand = `/rig attach ${input.runId}`;
|
|
6804
|
-
return [
|
|
6805
|
-
shellBinary("pi"),
|
|
6806
|
-
"--no-extensions",
|
|
6807
|
-
"--extension",
|
|
6808
|
-
extensionSource,
|
|
6809
|
-
initialCommand
|
|
6810
|
-
];
|
|
6811
|
-
}
|
|
6812
|
-
async function launchPiRigSession(context, input) {
|
|
6813
|
-
if (context.outputMode !== "text" || !process.stdin.isTTY || !process.stdout.isTTY) {
|
|
6814
|
-
return { launched: false, exitCode: null, command: [] };
|
|
6815
|
-
}
|
|
6816
|
-
if (process.env.RIG_DISABLE_PI_LAUNCH === "1") {
|
|
6817
|
-
return { launched: false, exitCode: null, command: [] };
|
|
6818
|
-
}
|
|
6819
|
-
const server = await ensureServerForCli(context.projectRoot);
|
|
6820
|
-
const command = buildPiRigSessionCommand({ ...input, projectRoot: context.projectRoot });
|
|
6821
|
-
const env = {
|
|
6822
|
-
...process.env,
|
|
6823
|
-
...buildPiRigSessionEnv({
|
|
6824
|
-
projectRoot: context.projectRoot,
|
|
6825
|
-
runId: input.runId,
|
|
6826
|
-
taskId: input.taskId,
|
|
6827
|
-
serverUrl: server.baseUrl,
|
|
6828
|
-
authToken: server.authToken
|
|
6829
|
-
})
|
|
6830
|
-
};
|
|
6831
|
-
process.stdout.write(`Launching Pi for Rig run ${input.runId}\u2026
|
|
6832
|
-
`);
|
|
6833
|
-
process.stdout.write(`Pi command: ${formatCommand(command)}
|
|
6834
|
-
`);
|
|
6835
|
-
const launchedAt = Date.now();
|
|
6836
|
-
const child = spawn2(command[0], command.slice(1), {
|
|
6837
|
-
cwd: context.projectRoot,
|
|
6838
|
-
env,
|
|
6839
|
-
stdio: "inherit"
|
|
6840
|
-
});
|
|
6841
|
-
const launchError = await new Promise((resolve20) => {
|
|
6842
|
-
child.once("error", (error) => {
|
|
6843
|
-
resolve20({ error: error.message });
|
|
6844
|
-
});
|
|
6845
|
-
child.once("close", (code) => resolve20({ code }));
|
|
6846
|
-
});
|
|
6847
|
-
if ("error" in launchError) {
|
|
6848
|
-
process.stderr.write(`Failed to launch Pi; falling back to Rig attach view: ${launchError.error}
|
|
6849
|
-
`);
|
|
6850
|
-
return { launched: false, exitCode: null, command, error: launchError.error };
|
|
6851
|
-
}
|
|
6852
|
-
const exitCode = launchError.code;
|
|
6853
|
-
const elapsedMs = Date.now() - launchedAt;
|
|
6854
|
-
if (typeof exitCode === "number" && exitCode !== 0 && elapsedMs < 5000) {
|
|
6855
|
-
const error = `Pi exited during startup with code ${exitCode}.`;
|
|
6856
|
-
process.stderr.write(`${error} Falling back to Rig attach view.
|
|
6857
|
-
`);
|
|
6858
|
-
return { launched: false, exitCode, command, error };
|
|
6859
|
-
}
|
|
6860
|
-
return { launched: true, exitCode, command };
|
|
6861
|
-
}
|
|
6862
|
-
|
|
6863
7010
|
// packages/cli/src/commands/run.ts
|
|
6864
7011
|
function normalizeRemoteRunDetails(payload) {
|
|
6865
7012
|
const run = payload.run;
|
|
@@ -7084,20 +7231,13 @@ async function executeRun(context, args) {
|
|
|
7084
7231
|
throw new CliError2("run attach requires a run id.", 2);
|
|
7085
7232
|
}
|
|
7086
7233
|
let steered = false;
|
|
7087
|
-
|
|
7088
|
-
if (shouldTryPiAttach && messageOption.value?.trim()) {
|
|
7234
|
+
if (messageOption.value?.trim()) {
|
|
7089
7235
|
await steerRunViaServer(context, runId, messageOption.value.trim());
|
|
7090
7236
|
steered = true;
|
|
7091
7237
|
}
|
|
7092
|
-
if (shouldTryPiAttach) {
|
|
7093
|
-
const piSession = await launchPiRigSession(context, { runId });
|
|
7094
|
-
if (piSession.launched) {
|
|
7095
|
-
return { ok: true, group: "run", command, details: { runId, steered, mode: "pi", ...piSession } };
|
|
7096
|
-
}
|
|
7097
|
-
}
|
|
7098
7238
|
const attached = await attachRunOperatorView(context, {
|
|
7099
7239
|
runId,
|
|
7100
|
-
message:
|
|
7240
|
+
message: null,
|
|
7101
7241
|
once: once.value,
|
|
7102
7242
|
follow: follow.value,
|
|
7103
7243
|
pollMs: parsePositiveInt(pollMs.value, "--poll-ms", 2000)
|
|
@@ -7824,20 +7964,7 @@ async function executeTask(context, args, options) {
|
|
|
7824
7964
|
let attachDetails = null;
|
|
7825
7965
|
if (!detachResult.value && context.outputMode === "text") {
|
|
7826
7966
|
console.log(formatSubmittedRun({ runId: submitted.runId, task: selectedTask ? summarizeTask(selectedTask) : null }));
|
|
7827
|
-
|
|
7828
|
-
const piSession = await launchPiRigSession(context, {
|
|
7829
|
-
runId: submitted.runId,
|
|
7830
|
-
taskId: selectedTaskId,
|
|
7831
|
-
title: titleResult.value ?? readTaskString(selectedTask ?? {}, "title"),
|
|
7832
|
-
runtimeAdapter
|
|
7833
|
-
});
|
|
7834
|
-
attachDetails = { mode: "pi", ...piSession };
|
|
7835
|
-
if (!piSession.launched) {
|
|
7836
|
-
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7837
|
-
}
|
|
7838
|
-
} else {
|
|
7839
|
-
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7840
|
-
}
|
|
7967
|
+
attachDetails = await attachRunOperatorView(context, { runId: submitted.runId, follow: true });
|
|
7841
7968
|
} else if (context.outputMode === "text") {
|
|
7842
7969
|
console.log(formatSubmittedRun({ runId: submitted.runId, task: selectedTask ? summarizeTask(selectedTask) : null }));
|
|
7843
7970
|
}
|
|
@@ -7924,7 +8051,7 @@ async function executeTask(context, args, options) {
|
|
|
7924
8051
|
// packages/cli/src/commands/task-run-driver.ts
|
|
7925
8052
|
import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync10, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7926
8053
|
import { resolve as resolve21 } from "path";
|
|
7927
|
-
import { spawn as
|
|
8054
|
+
import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
|
|
7928
8055
|
import { createInterface as createLineInterface } from "readline";
|
|
7929
8056
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
7930
8057
|
import {
|
|
@@ -7981,6 +8108,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
7981
8108
|
RIG_SERVER_RUN_ID: input.runId,
|
|
7982
8109
|
RIG_TASK_ID: input.taskId,
|
|
7983
8110
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
8111
|
+
RIG_STEERING_POLL_MS: "0",
|
|
7984
8112
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
7985
8113
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
7986
8114
|
...githubBridgeEnv(githubToken)
|
|
@@ -8152,7 +8280,7 @@ async function runCheckedCommand(command, args, cwd, label = "git") {
|
|
|
8152
8280
|
}
|
|
8153
8281
|
function createCommandRunner(binary) {
|
|
8154
8282
|
return async (args, options) => {
|
|
8155
|
-
const child =
|
|
8283
|
+
const child = spawn2(binary, [...args], {
|
|
8156
8284
|
cwd: options?.cwd,
|
|
8157
8285
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8158
8286
|
});
|
|
@@ -8576,6 +8704,69 @@ function appendAssistantTimelineFromRecord(input) {
|
|
|
8576
8704
|
}
|
|
8577
8705
|
return nextAssistantText;
|
|
8578
8706
|
}
|
|
8707
|
+
function appendPiRpcProtocolLogFromRecord(input) {
|
|
8708
|
+
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
8709
|
+
if (type === "response") {
|
|
8710
|
+
const command = typeof input.record.command === "string" ? input.record.command : "rpc";
|
|
8711
|
+
const success = input.record.success !== false;
|
|
8712
|
+
if (success && command !== "prompt" && command !== "steer" && command !== "follow_up" && command !== "set_session_name") {
|
|
8713
|
+
return true;
|
|
8714
|
+
}
|
|
8715
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
8716
|
+
id: input.nextRunLogId(),
|
|
8717
|
+
title: success ? "Pi RPC response" : "Pi RPC error",
|
|
8718
|
+
detail: success ? `${command}: accepted` : `${command}: ${String(input.record.error ?? "failed")}`,
|
|
8719
|
+
tone: success ? "tool" : "error",
|
|
8720
|
+
status: input.status,
|
|
8721
|
+
payload: input.record,
|
|
8722
|
+
createdAt: new Date().toISOString()
|
|
8723
|
+
});
|
|
8724
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: success ? "Pi RPC response" : "Pi RPC error" });
|
|
8725
|
+
return true;
|
|
8726
|
+
}
|
|
8727
|
+
if (type !== "extension_ui_request")
|
|
8728
|
+
return false;
|
|
8729
|
+
const method = typeof input.record.method === "string" ? input.record.method : "ui";
|
|
8730
|
+
let title = "Pi UI event";
|
|
8731
|
+
let detail = method;
|
|
8732
|
+
let tone = "info";
|
|
8733
|
+
if (method === "notify") {
|
|
8734
|
+
title = "Pi notification";
|
|
8735
|
+
detail = String(input.record.message ?? "");
|
|
8736
|
+
tone = input.record.notifyType === "error" ? "error" : "info";
|
|
8737
|
+
} else if (method === "setStatus") {
|
|
8738
|
+
title = "Pi UI status";
|
|
8739
|
+
detail = `${String(input.record.statusKey ?? "status")}: ${String(input.record.statusText ?? "cleared")}`;
|
|
8740
|
+
tone = "tool";
|
|
8741
|
+
} else if (method === "setWidget") {
|
|
8742
|
+
title = "Pi UI widget";
|
|
8743
|
+
const lines = Array.isArray(input.record.widgetLines) ? input.record.widgetLines.map((line) => String(line)).join(" | ") : "cleared";
|
|
8744
|
+
detail = `${String(input.record.widgetKey ?? "widget")}: ${lines}`.slice(0, 500);
|
|
8745
|
+
tone = "tool";
|
|
8746
|
+
} else if (method === "setTitle") {
|
|
8747
|
+
title = "Pi UI title";
|
|
8748
|
+
detail = String(input.record.title ?? "");
|
|
8749
|
+
tone = "tool";
|
|
8750
|
+
} else if (method === "set_editor_text") {
|
|
8751
|
+
title = "Pi editor update";
|
|
8752
|
+
detail = String(input.record.text ?? "").slice(0, 500);
|
|
8753
|
+
tone = "tool";
|
|
8754
|
+
} else {
|
|
8755
|
+
title = "Pi UI request";
|
|
8756
|
+
detail = `${method}: ${String(input.record.title ?? input.record.message ?? "")}`.trim();
|
|
8757
|
+
}
|
|
8758
|
+
appendRunLog(input.projectRoot, input.runId, {
|
|
8759
|
+
id: input.nextRunLogId(),
|
|
8760
|
+
title,
|
|
8761
|
+
detail,
|
|
8762
|
+
tone,
|
|
8763
|
+
status: input.status,
|
|
8764
|
+
payload: input.record,
|
|
8765
|
+
createdAt: new Date().toISOString()
|
|
8766
|
+
});
|
|
8767
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
8768
|
+
return true;
|
|
8769
|
+
}
|
|
8579
8770
|
function appendPiToolTimelineFromRecord(input) {
|
|
8580
8771
|
const type = typeof input.record.type === "string" ? input.record.type : "";
|
|
8581
8772
|
if (type !== "tool_execution_start" && type !== "tool_execution_update" && type !== "tool_execution_end")
|
|
@@ -8594,7 +8785,7 @@ function appendPiToolTimelineFromRecord(input) {
|
|
|
8594
8785
|
}
|
|
8595
8786
|
function isNonRenderablePiProtocolRecord(record) {
|
|
8596
8787
|
const type = typeof record.type === "string" ? record.type : "";
|
|
8597
|
-
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");
|
|
8788
|
+
return type === "agent_start" || type === "agent_end" || 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");
|
|
8598
8789
|
}
|
|
8599
8790
|
function appendToolTimelineFromLog(input) {
|
|
8600
8791
|
const title = typeof input.log.title === "string" ? input.log.title : "";
|
|
@@ -8757,11 +8948,8 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8757
8948
|
...input.model ? ["--model", input.model] : [],
|
|
8758
8949
|
"--prompt"
|
|
8759
8950
|
] : input.runtimeAdapter === "pi" ? [
|
|
8760
|
-
"--print",
|
|
8761
|
-
"--verbose",
|
|
8762
8951
|
"--mode",
|
|
8763
|
-
"
|
|
8764
|
-
"--no-session",
|
|
8952
|
+
"rpc",
|
|
8765
8953
|
...input.model ? ["--model", input.model] : []
|
|
8766
8954
|
] : [
|
|
8767
8955
|
"--print",
|
|
@@ -8858,7 +9046,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8858
9046
|
projectRoot: context.projectRoot,
|
|
8859
9047
|
runId: input.runId,
|
|
8860
9048
|
stage,
|
|
8861
|
-
detail: stage === "Launch Pi" ? "Pi runtime bridge starting with pi-rig environment." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running." : null,
|
|
9049
|
+
detail: stage === "Launch Pi" ? "Pi RPC runtime bridge starting with pi-rig environment." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running." : null,
|
|
8862
9050
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
8863
9051
|
});
|
|
8864
9052
|
}
|
|
@@ -8951,6 +9139,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8951
9139
|
detail: detail ?? "Verifier review is running."
|
|
8952
9140
|
});
|
|
8953
9141
|
};
|
|
9142
|
+
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
8954
9143
|
const handleWrapperEvent = (rawPayload) => {
|
|
8955
9144
|
let event = null;
|
|
8956
9145
|
try {
|
|
@@ -9085,9 +9274,23 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9085
9274
|
}
|
|
9086
9275
|
return true;
|
|
9087
9276
|
}
|
|
9277
|
+
if (event.type === "pi.rpc.prompt.sent" || event.type === "pi.rpc.steering.delivered" || event.type === "pi.rpc.steering.poll.failed" || event.type === "pi.rpc.extension_ui.cancelled") {
|
|
9278
|
+
const title = event.type === "pi.rpc.prompt.sent" ? "Delivered initial prompt to worker Pi" : event.type === "pi.rpc.steering.delivered" ? "Delivered steering to worker Pi" : event.type === "pi.rpc.steering.poll.failed" ? "Worker Pi steering poll failed" : "Pi RPC UI request auto-cancelled";
|
|
9279
|
+
const detail = event.type === "pi.rpc.prompt.sent" ? `${String(payload.kind ?? "prompt")} prompt (${String(payload.bytes ?? "unknown")} bytes)` : event.type === "pi.rpc.steering.delivered" ? `${String(payload.actor ?? "operator")}: ${String(payload.message ?? "")}`.slice(0, 500) : event.type === "pi.rpc.steering.poll.failed" ? String(payload.error ?? "steering poll failed") : `${String(payload.method ?? "ui")}: ${String(payload.reason ?? "noninteractive worker session")}`;
|
|
9280
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
9281
|
+
id: nextRunLogId(),
|
|
9282
|
+
title,
|
|
9283
|
+
detail,
|
|
9284
|
+
tone: event.type === "pi.rpc.steering.poll.failed" ? "error" : "info",
|
|
9285
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
9286
|
+
payload,
|
|
9287
|
+
createdAt: new Date().toISOString()
|
|
9288
|
+
});
|
|
9289
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
9290
|
+
return true;
|
|
9291
|
+
}
|
|
9088
9292
|
return false;
|
|
9089
9293
|
};
|
|
9090
|
-
const nextRunLogId = createRunLogIdFactory(input.runId);
|
|
9091
9294
|
const handleAgentStdoutLine = (line) => {
|
|
9092
9295
|
const trimmed = line.trim();
|
|
9093
9296
|
if (!trimmed)
|
|
@@ -9121,6 +9324,15 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9121
9324
|
try {
|
|
9122
9325
|
const record = JSON.parse(trimmed);
|
|
9123
9326
|
const liveLogStatus = reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running";
|
|
9327
|
+
if (input.runtimeAdapter === "pi" && appendPiRpcProtocolLogFromRecord({
|
|
9328
|
+
projectRoot: context.projectRoot,
|
|
9329
|
+
runId: input.runId,
|
|
9330
|
+
record,
|
|
9331
|
+
status: liveLogStatus,
|
|
9332
|
+
nextRunLogId
|
|
9333
|
+
})) {
|
|
9334
|
+
return;
|
|
9335
|
+
}
|
|
9124
9336
|
if (input.runtimeAdapter === "pi" && appendPiToolTimelineFromRecord({ projectRoot: context.projectRoot, runId: input.runId, record })) {
|
|
9125
9337
|
emitServerRunEvent({ type: "timeline", runId: input.runId });
|
|
9126
9338
|
return;
|
|
@@ -9266,7 +9478,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9266
9478
|
}
|
|
9267
9479
|
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
9268
9480
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
9269
|
-
const child =
|
|
9481
|
+
const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
9270
9482
|
cwd: context.projectRoot,
|
|
9271
9483
|
env: childEnv,
|
|
9272
9484
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -9613,7 +9825,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
9613
9825
|
});
|
|
9614
9826
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Steering Pi from PR feedback" });
|
|
9615
9827
|
const feedbackCommand = buildHostAgentCommand(message2);
|
|
9616
|
-
const child =
|
|
9828
|
+
const child = spawn2(feedbackCommand[0], feedbackCommand.slice(1), {
|
|
9617
9829
|
cwd: latestRuntimeWorkspace ?? context.projectRoot,
|
|
9618
9830
|
env: childEnv,
|
|
9619
9831
|
stdio: ["pipe", "pipe", "pipe"]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/cli",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Rig package",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -23,10 +23,11 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@clack/prompts": "^1.2.0",
|
|
26
|
-
"@
|
|
27
|
-
"@rig/
|
|
28
|
-
"@rig/
|
|
29
|
-
"@rig/
|
|
26
|
+
"@earendil-works/pi-tui": "^0.75.5",
|
|
27
|
+
"@rig/core": "npm:@h-rig/core@0.0.6-alpha.22",
|
|
28
|
+
"@rig/runtime": "npm:@h-rig/runtime@0.0.6-alpha.22",
|
|
29
|
+
"@rig/client": "npm:@h-rig/client@0.0.6-alpha.22",
|
|
30
|
+
"@rig/server": "npm:@h-rig/server@0.0.6-alpha.22",
|
|
30
31
|
"picocolors": "^1.1.1"
|
|
31
32
|
}
|
|
32
33
|
}
|