@h-rig/cli 0.0.6-alpha.22 → 0.0.6-alpha.24
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 +951 -494
- package/dist/src/commands/_operator-view.js +644 -228
- package/dist/src/commands/_pi-frontend.js +843 -0
- package/dist/src/commands/_pi-worker-bridge-extension.js +761 -0
- package/dist/src/commands/_preflight.js +1 -81
- package/dist/src/commands/_server-client.js +80 -1
- package/dist/src/commands/run.js +644 -228
- package/dist/src/commands/task-run-driver.js +51 -5
- package/dist/src/commands/task.js +648 -312
- package/dist/src/commands.js +951 -494
- package/dist/src/index.js +951 -494
- package/package.json +6 -6
package/dist/src/commands.js
CHANGED
|
@@ -2747,6 +2747,66 @@ async function steerRunViaServer(context, runId, message) {
|
|
|
2747
2747
|
});
|
|
2748
2748
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
2749
2749
|
}
|
|
2750
|
+
async function getRunPiSessionViaServer(context, runId) {
|
|
2751
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi`);
|
|
2752
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2753
|
+
}
|
|
2754
|
+
async function getRunPiMessagesViaServer(context, runId) {
|
|
2755
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/messages`);
|
|
2756
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { messages: [] };
|
|
2757
|
+
}
|
|
2758
|
+
async function getRunPiStatusViaServer(context, runId) {
|
|
2759
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/status`);
|
|
2760
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2761
|
+
}
|
|
2762
|
+
async function getRunPiCommandsViaServer(context, runId) {
|
|
2763
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands`);
|
|
2764
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { commands: [] };
|
|
2765
|
+
}
|
|
2766
|
+
async function sendRunPiPromptViaServer(context, runId, text2, streamingBehavior) {
|
|
2767
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/prompt`, {
|
|
2768
|
+
method: "POST",
|
|
2769
|
+
headers: { "content-type": "application/json" },
|
|
2770
|
+
body: JSON.stringify({ text: text2, streamingBehavior })
|
|
2771
|
+
});
|
|
2772
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
2773
|
+
}
|
|
2774
|
+
async function sendRunPiShellViaServer(context, runId, text2) {
|
|
2775
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/shell`, {
|
|
2776
|
+
method: "POST",
|
|
2777
|
+
headers: { "content-type": "application/json" },
|
|
2778
|
+
body: JSON.stringify({ text: text2 })
|
|
2779
|
+
});
|
|
2780
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
2781
|
+
}
|
|
2782
|
+
async function runRunPiCommandViaServer(context, runId, text2) {
|
|
2783
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands/run`, {
|
|
2784
|
+
method: "POST",
|
|
2785
|
+
headers: { "content-type": "application/json" },
|
|
2786
|
+
body: JSON.stringify({ text: text2 })
|
|
2787
|
+
});
|
|
2788
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
|
|
2789
|
+
}
|
|
2790
|
+
async function respondRunPiExtensionUiViaServer(context, runId, requestId, valueOrCancel) {
|
|
2791
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/extension-ui/respond`, {
|
|
2792
|
+
method: "POST",
|
|
2793
|
+
headers: { "content-type": "application/json" },
|
|
2794
|
+
body: JSON.stringify({ requestId, ...valueOrCancel })
|
|
2795
|
+
});
|
|
2796
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
2797
|
+
}
|
|
2798
|
+
async function abortRunPiViaServer(context, runId) {
|
|
2799
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/abort`, { method: "POST" });
|
|
2800
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { aborted: true };
|
|
2801
|
+
}
|
|
2802
|
+
async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
2803
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
2804
|
+
const url = new URL(`${server.baseUrl.replace(/\/+$/, "")}/api/runs/${encodeURIComponent(runId)}/pi/events`);
|
|
2805
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
2806
|
+
if (server.authToken)
|
|
2807
|
+
url.searchParams.set("token", server.authToken);
|
|
2808
|
+
return url.toString();
|
|
2809
|
+
}
|
|
2750
2810
|
async function submitTaskRunViaServer(context, input) {
|
|
2751
2811
|
const isTaskRun = Boolean(input.taskId);
|
|
2752
2812
|
const endpoint = isTaskRun ? "/api/runs/task" : "/api/runs/adhoc";
|
|
@@ -2779,183 +2839,6 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2779
2839
|
return { runId };
|
|
2780
2840
|
}
|
|
2781
2841
|
|
|
2782
|
-
// packages/cli/src/commands/_pi-install.ts
|
|
2783
|
-
import { existsSync as existsSync5, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2784
|
-
import { homedir as homedir3 } from "os";
|
|
2785
|
-
import { resolve as resolve9 } from "path";
|
|
2786
|
-
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
2787
|
-
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2788
|
-
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2789
|
-
export { default } from '@rig/pi-rig';
|
|
2790
|
-
`;
|
|
2791
|
-
async function defaultCommandRunner(command, options = {}) {
|
|
2792
|
-
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
2793
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
2794
|
-
new Response(proc.stdout).text(),
|
|
2795
|
-
new Response(proc.stderr).text(),
|
|
2796
|
-
proc.exited
|
|
2797
|
-
]);
|
|
2798
|
-
return { exitCode, stdout, stderr };
|
|
2799
|
-
}
|
|
2800
|
-
function resolvePiRigExtensionPath(homeDir) {
|
|
2801
|
-
return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
2802
|
-
}
|
|
2803
|
-
function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
|
|
2804
|
-
const localPackage = resolve9(projectRoot, "packages", "pi-rig");
|
|
2805
|
-
if (exists(resolve9(localPackage, "package.json")))
|
|
2806
|
-
return localPackage;
|
|
2807
|
-
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
2808
|
-
}
|
|
2809
|
-
function resolvePiHomeDir(inputHomeDir) {
|
|
2810
|
-
return inputHomeDir ?? process.env.RIG_PI_HOME_DIR?.trim() ?? homedir3();
|
|
2811
|
-
}
|
|
2812
|
-
function piListContainsPiRig(output) {
|
|
2813
|
-
return output.split(/\r?\n/).some((line) => {
|
|
2814
|
-
const normalized = line.trim();
|
|
2815
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
2816
|
-
});
|
|
2817
|
-
}
|
|
2818
|
-
async function safeRun(runner, command, options) {
|
|
2819
|
-
try {
|
|
2820
|
-
return await runner(command, options);
|
|
2821
|
-
} catch (error) {
|
|
2822
|
-
return { exitCode: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
2825
|
-
function splitInstallCommand(value) {
|
|
2826
|
-
return value.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((part) => part.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
2827
|
-
}
|
|
2828
|
-
async function ensurePiBinaryAvailable(input) {
|
|
2829
|
-
const current = await safeRun(input.runner, ["pi", "--version"]);
|
|
2830
|
-
if (current.exitCode === 0) {
|
|
2831
|
-
const updateCommand = process.env.RIG_PI_UPDATE_COMMAND?.trim();
|
|
2832
|
-
if (updateCommand) {
|
|
2833
|
-
const parts2 = splitInstallCommand(updateCommand);
|
|
2834
|
-
if (parts2.length > 0)
|
|
2835
|
-
await safeRun(input.runner, parts2, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
2836
|
-
}
|
|
2837
|
-
return { ok: true, detail: (current.stdout || current.stderr).trim() || undefined };
|
|
2838
|
-
}
|
|
2839
|
-
const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi@latest install";
|
|
2840
|
-
const parts = splitInstallCommand(installCommand);
|
|
2841
|
-
if (parts.length === 0) {
|
|
2842
|
-
return { ok: false, error: (current.stderr || current.stdout).trim() || "pi --version failed" };
|
|
2843
|
-
}
|
|
2844
|
-
const install = await safeRun(input.runner, parts, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
2845
|
-
if (install.exitCode !== 0) {
|
|
2846
|
-
return { ok: false, installedOrUpdated: true, error: (install.stderr || install.stdout).trim() || `Pi install command failed (${install.exitCode})` };
|
|
2847
|
-
}
|
|
2848
|
-
const next = await safeRun(input.runner, ["pi", "--version"]);
|
|
2849
|
-
return {
|
|
2850
|
-
ok: next.exitCode === 0,
|
|
2851
|
-
installedOrUpdated: true,
|
|
2852
|
-
detail: (next.stdout || next.stderr).trim() || undefined,
|
|
2853
|
-
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
2854
|
-
};
|
|
2855
|
-
}
|
|
2856
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
|
|
2857
|
-
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
2858
|
-
const indexPath = resolve9(extensionPath, "index.ts");
|
|
2859
|
-
if (!exists(indexPath))
|
|
2860
|
-
return;
|
|
2861
|
-
try {
|
|
2862
|
-
const content = readFileSync4(indexPath, "utf8");
|
|
2863
|
-
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
2864
|
-
rmSync3(extensionPath, { recursive: true, force: true });
|
|
2865
|
-
}
|
|
2866
|
-
} catch {}
|
|
2867
|
-
}
|
|
2868
|
-
async function checkPiRigInstall(input = {}) {
|
|
2869
|
-
const home = resolvePiHomeDir(input.homeDir);
|
|
2870
|
-
const extensionPath = resolvePiRigExtensionPath(home);
|
|
2871
|
-
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
2872
|
-
return {
|
|
2873
|
-
extensionPath,
|
|
2874
|
-
pi: { ok: true, label: "pi", detail: "fake-pi" },
|
|
2875
|
-
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
2876
|
-
};
|
|
2877
|
-
}
|
|
2878
|
-
const exists = input.exists ?? existsSync5;
|
|
2879
|
-
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
2880
|
-
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
2881
|
-
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
2882
|
-
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
2883
|
-
${piListResult.stderr}`);
|
|
2884
|
-
const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
|
|
2885
|
-
const hasPiRig = listedPiRig;
|
|
2886
|
-
return {
|
|
2887
|
-
extensionPath,
|
|
2888
|
-
pi: {
|
|
2889
|
-
ok: piResult.exitCode === 0,
|
|
2890
|
-
label: "pi",
|
|
2891
|
-
detail: (piResult.stdout || piResult.stderr).trim() || undefined,
|
|
2892
|
-
hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
|
|
2893
|
-
},
|
|
2894
|
-
piRig: {
|
|
2895
|
-
ok: hasPiRig,
|
|
2896
|
-
label: "pi-rig global extension",
|
|
2897
|
-
detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
|
|
2898
|
-
hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
|
|
2899
|
-
}
|
|
2900
|
-
};
|
|
2901
|
-
}
|
|
2902
|
-
async function ensurePiRigInstalled(input) {
|
|
2903
|
-
const home = resolvePiHomeDir(input.homeDir);
|
|
2904
|
-
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
2905
|
-
const status2 = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
|
|
2906
|
-
return { ...status2, installedPath: status2.extensionPath };
|
|
2907
|
-
}
|
|
2908
|
-
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
2909
|
-
const piAvailable = await ensurePiBinaryAvailable({ runner, projectRoot: input.projectRoot });
|
|
2910
|
-
const status = piAvailable.ok ? await checkPiRigInstall({ homeDir: home, commandRunner: runner }) : {
|
|
2911
|
-
extensionPath: resolvePiRigExtensionPath(home),
|
|
2912
|
-
pi: { ok: false, label: "pi", detail: piAvailable.error, hint: "Install/update Pi with RIG_PI_INSTALL_COMMAND or install Pi manually." },
|
|
2913
|
-
piRig: { ok: false, label: "pi-rig global extension", hint: "Pi is required before pi-rig can be installed." }
|
|
2914
|
-
};
|
|
2915
|
-
if (!piAvailable.ok) {
|
|
2916
|
-
throw new Error(`Pi install/update failed: ${piAvailable.error ?? "pi unavailable"}`);
|
|
2917
|
-
}
|
|
2918
|
-
const packageSource = resolvePiRigPackageSource(input.projectRoot);
|
|
2919
|
-
removeManagedLegacyPiRigBridge(home);
|
|
2920
|
-
const install = await runner(["pi", "install", packageSource], { cwd: input.projectRoot });
|
|
2921
|
-
if (install.exitCode !== 0) {
|
|
2922
|
-
throw new Error(`pi-rig install failed: ${(install.stderr || install.stdout).trim() || `exit ${install.exitCode}`}`);
|
|
2923
|
-
}
|
|
2924
|
-
const next = await checkPiRigInstall({ homeDir: home, commandRunner: runner });
|
|
2925
|
-
return { ...next, installedPath: packageSource };
|
|
2926
|
-
}
|
|
2927
|
-
async function ensureRemotePiRigInstalled(input) {
|
|
2928
|
-
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
2929
|
-
method: "POST",
|
|
2930
|
-
headers: { "content-type": "application/json" },
|
|
2931
|
-
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
2932
|
-
});
|
|
2933
|
-
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2934
|
-
const piOk = record.piOk === true || record.ok === true;
|
|
2935
|
-
const piRigOk = record.piRigOk === true || record.installed === true || record.ok === true;
|
|
2936
|
-
const extensionPath = typeof record.extensionPath === "string" ? record.extensionPath : "remote:~/.pi/agent/extensions/pi-rig";
|
|
2937
|
-
return {
|
|
2938
|
-
remote: true,
|
|
2939
|
-
extensionPath,
|
|
2940
|
-
pi: {
|
|
2941
|
-
ok: piOk,
|
|
2942
|
-
label: "pi",
|
|
2943
|
-
detail: typeof record.piVersion === "string" ? record.piVersion : undefined,
|
|
2944
|
-
hint: piOk ? undefined : "Install/update Pi on the selected remote Rig server."
|
|
2945
|
-
},
|
|
2946
|
-
piRig: {
|
|
2947
|
-
ok: piRigOk,
|
|
2948
|
-
label: "pi-rig global extension",
|
|
2949
|
-
detail: extensionPath,
|
|
2950
|
-
hint: piRigOk ? undefined : "Install/enable pi-rig on the selected remote Rig server."
|
|
2951
|
-
}
|
|
2952
|
-
};
|
|
2953
|
-
}
|
|
2954
|
-
async function buildPiSetupChecks(input = {}) {
|
|
2955
|
-
const status = await checkPiRigInstall(input);
|
|
2956
|
-
return [status.pi, status.piRig];
|
|
2957
|
-
}
|
|
2958
|
-
|
|
2959
2842
|
// packages/cli/src/commands/_preflight.ts
|
|
2960
2843
|
function preflightCheck(id, label, status, detail, remediation) {
|
|
2961
2844
|
return {
|
|
@@ -3113,14 +2996,7 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
3113
2996
|
}
|
|
3114
2997
|
}
|
|
3115
2998
|
if ((options.runtimeAdapter ?? "pi") === "pi") {
|
|
3116
|
-
|
|
3117
|
-
ok: false,
|
|
3118
|
-
label: "pi/pi-rig checks",
|
|
3119
|
-
hint: message(error)
|
|
3120
|
-
}]);
|
|
3121
|
-
for (const pi of piChecks) {
|
|
3122
|
-
checks.push(preflightCheck(pi.label === "pi" ? "pi" : "pi-rig", pi.label, pi.ok ? "pass" : "fail", pi.detail, pi.hint ?? (pi.ok ? undefined : "Run `rig init --yes` to install/update Pi and enable pi-rig.")));
|
|
3123
|
-
}
|
|
2999
|
+
checks.push(preflightCheck("runtime", "worker Pi SDK session daemon", "pass", selectedServer?.connectionKind === "remote" ? "remote worker-owned runtime" : "bundled server-owned runtime"));
|
|
3124
3000
|
} else {
|
|
3125
3001
|
checks.push(preflightCheck("runtime", "runtime adapter", "pass", options.runtimeAdapter));
|
|
3126
3002
|
}
|
|
@@ -3236,7 +3112,7 @@ async function executeQueue(context, args) {
|
|
|
3236
3112
|
}
|
|
3237
3113
|
|
|
3238
3114
|
// packages/cli/src/commands/agent.ts
|
|
3239
|
-
import { resolve as
|
|
3115
|
+
import { resolve as resolve10 } from "path";
|
|
3240
3116
|
import {
|
|
3241
3117
|
agentId,
|
|
3242
3118
|
cleanupAgentRuntime,
|
|
@@ -3246,8 +3122,8 @@ import {
|
|
|
3246
3122
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3247
3123
|
|
|
3248
3124
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3249
|
-
import { existsSync as
|
|
3250
|
-
import { resolve as
|
|
3125
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3126
|
+
import { resolve as resolve9 } from "path";
|
|
3251
3127
|
import {
|
|
3252
3128
|
readAuthorityRun,
|
|
3253
3129
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3269,8 +3145,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3269
3145
|
return "claude-code";
|
|
3270
3146
|
}
|
|
3271
3147
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3272
|
-
const issuesPath =
|
|
3273
|
-
if (!
|
|
3148
|
+
const issuesPath = resolve9(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3149
|
+
if (!existsSync5(issuesPath)) {
|
|
3274
3150
|
return null;
|
|
3275
3151
|
}
|
|
3276
3152
|
let latest = null;
|
|
@@ -3338,7 +3214,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3338
3214
|
} else if ("errorText" in next) {
|
|
3339
3215
|
delete next.errorText;
|
|
3340
3216
|
}
|
|
3341
|
-
writeJsonFile3(
|
|
3217
|
+
writeJsonFile3(resolve9(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3342
3218
|
return next;
|
|
3343
3219
|
}
|
|
3344
3220
|
|
|
@@ -3459,10 +3335,10 @@ async function executeAgent(context, args) {
|
|
|
3459
3335
|
status: "running",
|
|
3460
3336
|
startedAt: createdAt,
|
|
3461
3337
|
worktreePath: runtime.workspaceDir,
|
|
3462
|
-
artifactRoot:
|
|
3338
|
+
artifactRoot: resolve10(runtime.workspaceDir, "artifacts", taskId),
|
|
3463
3339
|
logRoot: runtime.logsDir,
|
|
3464
|
-
sessionPath:
|
|
3465
|
-
sessionLogPath:
|
|
3340
|
+
sessionPath: resolve10(runtime.sessionDir, "session.json"),
|
|
3341
|
+
sessionLogPath: resolve10(runtime.logsDir, "agent-stdout.log"),
|
|
3466
3342
|
pid: process.pid
|
|
3467
3343
|
});
|
|
3468
3344
|
const result = await runInAgentRuntime({
|
|
@@ -3482,10 +3358,10 @@ async function executeAgent(context, args) {
|
|
|
3482
3358
|
startedAt: createdAt,
|
|
3483
3359
|
completedAt: failedAt,
|
|
3484
3360
|
worktreePath: runtime.workspaceDir,
|
|
3485
|
-
artifactRoot:
|
|
3361
|
+
artifactRoot: resolve10(runtime.workspaceDir, "artifacts", taskId),
|
|
3486
3362
|
logRoot: runtime.logsDir,
|
|
3487
|
-
sessionPath:
|
|
3488
|
-
sessionLogPath:
|
|
3363
|
+
sessionPath: resolve10(runtime.sessionDir, "session.json"),
|
|
3364
|
+
sessionLogPath: resolve10(runtime.logsDir, "agent-stdout.log"),
|
|
3489
3365
|
pid: process.pid,
|
|
3490
3366
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3491
3367
|
});
|
|
@@ -3502,10 +3378,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3502
3378
|
startedAt: createdAt,
|
|
3503
3379
|
completedAt,
|
|
3504
3380
|
worktreePath: runtime.workspaceDir,
|
|
3505
|
-
artifactRoot:
|
|
3381
|
+
artifactRoot: resolve10(runtime.workspaceDir, "artifacts", taskId),
|
|
3506
3382
|
logRoot: runtime.logsDir,
|
|
3507
|
-
sessionPath:
|
|
3508
|
-
sessionLogPath:
|
|
3383
|
+
sessionPath: resolve10(runtime.sessionDir, "session.json"),
|
|
3384
|
+
sessionLogPath: resolve10(runtime.logsDir, "agent-stdout.log"),
|
|
3509
3385
|
pid: process.pid
|
|
3510
3386
|
});
|
|
3511
3387
|
return {
|
|
@@ -3579,17 +3455,17 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3579
3455
|
import {
|
|
3580
3456
|
chmodSync,
|
|
3581
3457
|
copyFileSync as copyFileSync2,
|
|
3582
|
-
existsSync as
|
|
3458
|
+
existsSync as existsSync6,
|
|
3583
3459
|
mkdirSync as mkdirSync5,
|
|
3584
3460
|
readdirSync,
|
|
3585
3461
|
readlinkSync,
|
|
3586
|
-
rmSync as
|
|
3462
|
+
rmSync as rmSync3,
|
|
3587
3463
|
statSync,
|
|
3588
3464
|
symlinkSync,
|
|
3589
3465
|
unlinkSync
|
|
3590
3466
|
} from "fs";
|
|
3591
|
-
import { homedir as
|
|
3592
|
-
import { resolve as
|
|
3467
|
+
import { homedir as homedir3 } from "os";
|
|
3468
|
+
import { resolve as resolve11 } from "path";
|
|
3593
3469
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3594
3470
|
import {
|
|
3595
3471
|
computeRuntimeImageFingerprint,
|
|
@@ -3608,13 +3484,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3608
3484
|
|
|
3609
3485
|
// packages/cli/src/commands/dist.ts
|
|
3610
3486
|
function collectRigValidatorBuildTargets(input) {
|
|
3611
|
-
const validatorsRoot =
|
|
3612
|
-
if (!
|
|
3487
|
+
const validatorsRoot = resolve11(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3488
|
+
if (!existsSync6(validatorsRoot))
|
|
3613
3489
|
return [];
|
|
3614
3490
|
const targets = [];
|
|
3615
3491
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3616
3492
|
for (const category of categories) {
|
|
3617
|
-
const categoryDir =
|
|
3493
|
+
const categoryDir = resolve11(validatorsRoot, category.name);
|
|
3618
3494
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3619
3495
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3620
3496
|
continue;
|
|
@@ -3623,7 +3499,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3623
3499
|
continue;
|
|
3624
3500
|
targets.push({
|
|
3625
3501
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3626
|
-
dest:
|
|
3502
|
+
dest: resolve11(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3627
3503
|
cwd: input.hostProjectRoot
|
|
3628
3504
|
});
|
|
3629
3505
|
}
|
|
@@ -3632,16 +3508,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3632
3508
|
}
|
|
3633
3509
|
async function findLatestDistBinary(projectRoot) {
|
|
3634
3510
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3635
|
-
if (!
|
|
3511
|
+
if (!existsSync6(distRoot)) {
|
|
3636
3512
|
return null;
|
|
3637
3513
|
}
|
|
3638
3514
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3639
3515
|
name: entry.name,
|
|
3640
|
-
mtimeMs: statSync(
|
|
3516
|
+
mtimeMs: statSync(resolve11(distRoot, entry.name)).mtimeMs
|
|
3641
3517
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3642
3518
|
for (const { name } of entries) {
|
|
3643
|
-
const candidate =
|
|
3644
|
-
if (
|
|
3519
|
+
const candidate = resolve11(distRoot, name, "bin", "rig");
|
|
3520
|
+
if (existsSync6(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3645
3521
|
return candidate;
|
|
3646
3522
|
}
|
|
3647
3523
|
}
|
|
@@ -3653,7 +3529,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3653
3529
|
async function runDistDoctor(projectRoot) {
|
|
3654
3530
|
const bunPath = Bun.which("bun");
|
|
3655
3531
|
const rigPath = Bun.which("rig");
|
|
3656
|
-
const userBinDir =
|
|
3532
|
+
const userBinDir = resolve11(homedir3(), ".local/bin");
|
|
3657
3533
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3658
3534
|
let rigRunnable = false;
|
|
3659
3535
|
if (rigPath) {
|
|
@@ -3701,15 +3577,15 @@ async function executeDist(context, args) {
|
|
|
3701
3577
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3702
3578
|
let buildDir = null;
|
|
3703
3579
|
if (!source) {
|
|
3704
|
-
buildDir =
|
|
3580
|
+
buildDir = resolve11(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3705
3581
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3706
|
-
source =
|
|
3582
|
+
source = resolve11(buildDir, "bin", "rig");
|
|
3707
3583
|
}
|
|
3708
|
-
if (!
|
|
3584
|
+
if (!existsSync6(source)) {
|
|
3709
3585
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3710
3586
|
}
|
|
3711
|
-
const installedPath =
|
|
3712
|
-
if (
|
|
3587
|
+
const installedPath = resolve11(installDir, "rig");
|
|
3588
|
+
if (existsSync6(installedPath)) {
|
|
3713
3589
|
unlinkSync(installedPath);
|
|
3714
3590
|
}
|
|
3715
3591
|
copyFileSync2(source, installedPath);
|
|
@@ -3751,22 +3627,22 @@ async function executeDist(context, args) {
|
|
|
3751
3627
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3752
3628
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3753
3629
|
const currentId = computeRuntimeImageId(fp);
|
|
3754
|
-
const imagesDir =
|
|
3630
|
+
const imagesDir = resolve11(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3755
3631
|
mkdirSync5(imagesDir, { recursive: true });
|
|
3756
3632
|
let pruned = 0;
|
|
3757
3633
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3758
3634
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3759
|
-
|
|
3635
|
+
rmSync3(resolve11(imagesDir, entry.name), { recursive: true, force: true });
|
|
3760
3636
|
pruned++;
|
|
3761
3637
|
}
|
|
3762
3638
|
}
|
|
3763
3639
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3764
3640
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3765
3641
|
}
|
|
3766
|
-
const imageDir =
|
|
3767
|
-
mkdirSync5(
|
|
3768
|
-
mkdirSync5(
|
|
3769
|
-
mkdirSync5(
|
|
3642
|
+
const imageDir = resolve11(imagesDir, currentId);
|
|
3643
|
+
mkdirSync5(resolve11(imageDir, "bin/hooks"), { recursive: true });
|
|
3644
|
+
mkdirSync5(resolve11(imageDir, "bin/plugins"), { recursive: true });
|
|
3645
|
+
mkdirSync5(resolve11(imageDir, "bin/validators"), { recursive: true });
|
|
3770
3646
|
const hookNames = [
|
|
3771
3647
|
"scope-guard",
|
|
3772
3648
|
"import-guard",
|
|
@@ -3780,25 +3656,25 @@ async function executeDist(context, args) {
|
|
|
3780
3656
|
];
|
|
3781
3657
|
const targets = [];
|
|
3782
3658
|
const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
|
|
3783
|
-
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest:
|
|
3784
|
-
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest:
|
|
3659
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve11(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3660
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve11(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3785
3661
|
for (const hookName of hookNames) {
|
|
3786
3662
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3787
|
-
targets.push({ source: src, dest:
|
|
3788
|
-
targets.push({ source: src, dest:
|
|
3663
|
+
targets.push({ source: src, dest: resolve11(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3664
|
+
targets.push({ source: src, dest: resolve11(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3789
3665
|
}
|
|
3790
|
-
const pluginsDir =
|
|
3791
|
-
const binPluginsDir =
|
|
3792
|
-
const validatorsRoot =
|
|
3793
|
-
const binValidatorsDir =
|
|
3666
|
+
const pluginsDir = resolve11(context.projectRoot, "rig/plugins");
|
|
3667
|
+
const binPluginsDir = resolve11(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3668
|
+
const validatorsRoot = resolve11(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3669
|
+
const binValidatorsDir = resolve11(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3794
3670
|
mkdirSync5(binPluginsDir, { recursive: true });
|
|
3795
3671
|
mkdirSync5(binValidatorsDir, { recursive: true });
|
|
3796
|
-
if (
|
|
3672
|
+
if (existsSync6(pluginsDir)) {
|
|
3797
3673
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3798
3674
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3799
3675
|
if (!m)
|
|
3800
3676
|
continue;
|
|
3801
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3677
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve11(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3802
3678
|
}
|
|
3803
3679
|
}
|
|
3804
3680
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3809,17 +3685,17 @@ async function executeDist(context, args) {
|
|
|
3809
3685
|
const isValidator = dest.includes("/bin/validators/");
|
|
3810
3686
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3811
3687
|
}
|
|
3812
|
-
if (
|
|
3688
|
+
if (existsSync6(pluginsDir)) {
|
|
3813
3689
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3814
3690
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3815
3691
|
if (!m)
|
|
3816
3692
|
continue;
|
|
3817
3693
|
const pluginName = m[1];
|
|
3818
|
-
const imageBin =
|
|
3694
|
+
const imageBin = resolve11(imageDir, `bin/plugins/${pluginName}`);
|
|
3819
3695
|
if (!pluginName)
|
|
3820
3696
|
continue;
|
|
3821
|
-
const symlinkPath =
|
|
3822
|
-
if (
|
|
3697
|
+
const symlinkPath = resolve11(binPluginsDir, pluginName);
|
|
3698
|
+
if (existsSync6(imageBin)) {
|
|
3823
3699
|
try {
|
|
3824
3700
|
unlinkSync(symlinkPath);
|
|
3825
3701
|
} catch {}
|
|
@@ -3827,10 +3703,10 @@ async function executeDist(context, args) {
|
|
|
3827
3703
|
}
|
|
3828
3704
|
}
|
|
3829
3705
|
}
|
|
3830
|
-
if (
|
|
3706
|
+
if (existsSync6(validatorsRoot)) {
|
|
3831
3707
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3832
3708
|
for (const category of categories) {
|
|
3833
|
-
const categoryDir =
|
|
3709
|
+
const categoryDir = resolve11(validatorsRoot, category.name);
|
|
3834
3710
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3835
3711
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3836
3712
|
continue;
|
|
@@ -3838,9 +3714,9 @@ async function executeDist(context, args) {
|
|
|
3838
3714
|
if (!check || check === "index" || check === "shared")
|
|
3839
3715
|
continue;
|
|
3840
3716
|
const validatorName = `${category.name}-${check}`;
|
|
3841
|
-
const imageBin =
|
|
3842
|
-
const symlinkPath =
|
|
3843
|
-
if (
|
|
3717
|
+
const imageBin = resolve11(imageDir, `bin/validators/${validatorName}`);
|
|
3718
|
+
const symlinkPath = resolve11(binValidatorsDir, validatorName);
|
|
3719
|
+
if (existsSync6(imageBin)) {
|
|
3844
3720
|
try {
|
|
3845
3721
|
unlinkSync(symlinkPath);
|
|
3846
3722
|
} catch {}
|
|
@@ -3849,18 +3725,18 @@ async function executeDist(context, args) {
|
|
|
3849
3725
|
}
|
|
3850
3726
|
}
|
|
3851
3727
|
}
|
|
3852
|
-
const agentsDir =
|
|
3853
|
-
if (
|
|
3728
|
+
const agentsDir = resolve11(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3729
|
+
if (existsSync6(agentsDir)) {
|
|
3854
3730
|
let relinkCount = 0;
|
|
3855
3731
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3856
3732
|
if (!agentEntry.isDirectory())
|
|
3857
3733
|
continue;
|
|
3858
|
-
const agentBinDir =
|
|
3859
|
-
if (!
|
|
3734
|
+
const agentBinDir = resolve11(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
3735
|
+
if (!existsSync6(agentBinDir))
|
|
3860
3736
|
continue;
|
|
3861
3737
|
const walkDir = (dir) => {
|
|
3862
3738
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3863
|
-
const fullPath =
|
|
3739
|
+
const fullPath = resolve11(dir, entry.name);
|
|
3864
3740
|
if (entry.isDirectory()) {
|
|
3865
3741
|
walkDir(fullPath);
|
|
3866
3742
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -3894,7 +3770,7 @@ async function executeDist(context, args) {
|
|
|
3894
3770
|
|
|
3895
3771
|
// packages/cli/src/commands/inbox.ts
|
|
3896
3772
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
3897
|
-
import { resolve as
|
|
3773
|
+
import { resolve as resolve12 } from "path";
|
|
3898
3774
|
import {
|
|
3899
3775
|
listAuthorityRuns,
|
|
3900
3776
|
readJsonlFile as readJsonlFile3,
|
|
@@ -3911,7 +3787,7 @@ async function executeInbox(context, args) {
|
|
|
3911
3787
|
pending = task.rest;
|
|
3912
3788
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
3913
3789
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
3914
|
-
const approvals = runs.flatMap((entry) => readJsonlFile3(
|
|
3790
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve12(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
3915
3791
|
runId: entry.runId,
|
|
3916
3792
|
record
|
|
3917
3793
|
})));
|
|
@@ -3939,7 +3815,7 @@ async function executeInbox(context, args) {
|
|
|
3939
3815
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
3940
3816
|
throw new CliError2("decision must be approve or reject.");
|
|
3941
3817
|
}
|
|
3942
|
-
const approvalsPath =
|
|
3818
|
+
const approvalsPath = resolve12(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
3943
3819
|
const approvals = readJsonlFile3(approvalsPath);
|
|
3944
3820
|
const resolvedAt = new Date().toISOString();
|
|
3945
3821
|
const next = approvals.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", decision: decision.value, note: note3.value ?? null, resolvedAt } : entry);
|
|
@@ -3956,7 +3832,7 @@ async function executeInbox(context, args) {
|
|
|
3956
3832
|
pending = task.rest;
|
|
3957
3833
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
3958
3834
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
3959
|
-
const requests = runs.flatMap((entry) => readJsonlFile3(
|
|
3835
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve12(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
3960
3836
|
runId: entry.runId,
|
|
3961
3837
|
record
|
|
3962
3838
|
})));
|
|
@@ -3998,7 +3874,7 @@ async function executeInbox(context, args) {
|
|
|
3998
3874
|
const [key, ...restValue] = entry.split("=");
|
|
3999
3875
|
return [key, restValue.join("=")];
|
|
4000
3876
|
}));
|
|
4001
|
-
const requestsPath =
|
|
3877
|
+
const requestsPath = resolve12(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
4002
3878
|
const requests = readJsonlFile3(requestsPath);
|
|
4003
3879
|
const resolvedAt = new Date().toISOString();
|
|
4004
3880
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -4007,17 +3883,194 @@ async function executeInbox(context, args) {
|
|
|
4007
3883
|
`, "utf8");
|
|
4008
3884
|
return { ok: true, group: "inbox", command, details: { runId: run.value, requestId: request.value, answers: parsedAnswers } };
|
|
4009
3885
|
}
|
|
4010
|
-
default:
|
|
4011
|
-
throw new CliError2(`Unknown inbox command: ${command}`);
|
|
4012
|
-
}
|
|
3886
|
+
default:
|
|
3887
|
+
throw new CliError2(`Unknown inbox command: ${command}`);
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
// packages/cli/src/commands/init.ts
|
|
3892
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
3893
|
+
import { spawnSync } from "child_process";
|
|
3894
|
+
import { resolve as resolve16 } from "path";
|
|
3895
|
+
import { buildRigInitConfigSource } from "@rig/core";
|
|
3896
|
+
import { listGitHubProjects as listGitHubProjectsDirect, resolveProjectStatusField as resolveProjectStatusFieldDirect } from "@rig/server";
|
|
3897
|
+
|
|
3898
|
+
// packages/cli/src/commands/_pi-install.ts
|
|
3899
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, rmSync as rmSync4 } from "fs";
|
|
3900
|
+
import { homedir as homedir4 } from "os";
|
|
3901
|
+
import { resolve as resolve13 } from "path";
|
|
3902
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
3903
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
3904
|
+
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
3905
|
+
export { default } from '@rig/pi-rig';
|
|
3906
|
+
`;
|
|
3907
|
+
async function defaultCommandRunner(command, options = {}) {
|
|
3908
|
+
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
3909
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
3910
|
+
new Response(proc.stdout).text(),
|
|
3911
|
+
new Response(proc.stderr).text(),
|
|
3912
|
+
proc.exited
|
|
3913
|
+
]);
|
|
3914
|
+
return { exitCode, stdout, stderr };
|
|
3915
|
+
}
|
|
3916
|
+
function resolvePiRigExtensionPath(homeDir) {
|
|
3917
|
+
return resolve13(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
3918
|
+
}
|
|
3919
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync7) {
|
|
3920
|
+
const localPackage = resolve13(projectRoot, "packages", "pi-rig");
|
|
3921
|
+
if (exists(resolve13(localPackage, "package.json")))
|
|
3922
|
+
return localPackage;
|
|
3923
|
+
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
3924
|
+
}
|
|
3925
|
+
function resolvePiHomeDir(inputHomeDir) {
|
|
3926
|
+
return inputHomeDir ?? process.env.RIG_PI_HOME_DIR?.trim() ?? homedir4();
|
|
3927
|
+
}
|
|
3928
|
+
function piListContainsPiRig(output) {
|
|
3929
|
+
return output.split(/\r?\n/).some((line) => {
|
|
3930
|
+
const normalized = line.trim();
|
|
3931
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
3932
|
+
});
|
|
3933
|
+
}
|
|
3934
|
+
async function safeRun(runner, command, options) {
|
|
3935
|
+
try {
|
|
3936
|
+
return await runner(command, options);
|
|
3937
|
+
} catch (error) {
|
|
3938
|
+
return { exitCode: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
function splitInstallCommand(value) {
|
|
3942
|
+
return value.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((part) => part.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
3943
|
+
}
|
|
3944
|
+
async function ensurePiBinaryAvailable(input) {
|
|
3945
|
+
const current = await safeRun(input.runner, ["pi", "--version"]);
|
|
3946
|
+
if (current.exitCode === 0) {
|
|
3947
|
+
const updateCommand = process.env.RIG_PI_UPDATE_COMMAND?.trim();
|
|
3948
|
+
if (updateCommand) {
|
|
3949
|
+
const parts2 = splitInstallCommand(updateCommand);
|
|
3950
|
+
if (parts2.length > 0)
|
|
3951
|
+
await safeRun(input.runner, parts2, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
3952
|
+
}
|
|
3953
|
+
return { ok: true, detail: (current.stdout || current.stderr).trim() || undefined };
|
|
3954
|
+
}
|
|
3955
|
+
const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi@latest install";
|
|
3956
|
+
const parts = splitInstallCommand(installCommand);
|
|
3957
|
+
if (parts.length === 0) {
|
|
3958
|
+
return { ok: false, error: (current.stderr || current.stdout).trim() || "pi --version failed" };
|
|
3959
|
+
}
|
|
3960
|
+
const install = await safeRun(input.runner, parts, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
3961
|
+
if (install.exitCode !== 0) {
|
|
3962
|
+
return { ok: false, installedOrUpdated: true, error: (install.stderr || install.stdout).trim() || `Pi install command failed (${install.exitCode})` };
|
|
3963
|
+
}
|
|
3964
|
+
const next = await safeRun(input.runner, ["pi", "--version"]);
|
|
3965
|
+
return {
|
|
3966
|
+
ok: next.exitCode === 0,
|
|
3967
|
+
installedOrUpdated: true,
|
|
3968
|
+
detail: (next.stdout || next.stderr).trim() || undefined,
|
|
3969
|
+
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
3970
|
+
};
|
|
3971
|
+
}
|
|
3972
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync7) {
|
|
3973
|
+
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
3974
|
+
const indexPath = resolve13(extensionPath, "index.ts");
|
|
3975
|
+
if (!exists(indexPath))
|
|
3976
|
+
return;
|
|
3977
|
+
try {
|
|
3978
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
3979
|
+
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
3980
|
+
rmSync4(extensionPath, { recursive: true, force: true });
|
|
3981
|
+
}
|
|
3982
|
+
} catch {}
|
|
3983
|
+
}
|
|
3984
|
+
async function checkPiRigInstall(input = {}) {
|
|
3985
|
+
const home = resolvePiHomeDir(input.homeDir);
|
|
3986
|
+
const extensionPath = resolvePiRigExtensionPath(home);
|
|
3987
|
+
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
3988
|
+
return {
|
|
3989
|
+
extensionPath,
|
|
3990
|
+
pi: { ok: true, label: "pi", detail: "fake-pi" },
|
|
3991
|
+
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
const exists = input.exists ?? existsSync7;
|
|
3995
|
+
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3996
|
+
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
3997
|
+
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
3998
|
+
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
3999
|
+
${piListResult.stderr}`);
|
|
4000
|
+
const legacyBridge = exists(resolve13(extensionPath, "index.ts"));
|
|
4001
|
+
const hasPiRig = listedPiRig;
|
|
4002
|
+
return {
|
|
4003
|
+
extensionPath,
|
|
4004
|
+
pi: {
|
|
4005
|
+
ok: piResult.exitCode === 0,
|
|
4006
|
+
label: "pi",
|
|
4007
|
+
detail: (piResult.stdout || piResult.stderr).trim() || undefined,
|
|
4008
|
+
hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
|
|
4009
|
+
},
|
|
4010
|
+
piRig: {
|
|
4011
|
+
ok: hasPiRig,
|
|
4012
|
+
label: "pi-rig global extension",
|
|
4013
|
+
detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
|
|
4014
|
+
hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
|
|
4015
|
+
}
|
|
4016
|
+
};
|
|
4017
|
+
}
|
|
4018
|
+
async function ensurePiRigInstalled(input) {
|
|
4019
|
+
const home = resolvePiHomeDir(input.homeDir);
|
|
4020
|
+
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
4021
|
+
const status2 = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
|
|
4022
|
+
return { ...status2, installedPath: status2.extensionPath };
|
|
4023
|
+
}
|
|
4024
|
+
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
4025
|
+
const piAvailable = await ensurePiBinaryAvailable({ runner, projectRoot: input.projectRoot });
|
|
4026
|
+
const status = piAvailable.ok ? await checkPiRigInstall({ homeDir: home, commandRunner: runner }) : {
|
|
4027
|
+
extensionPath: resolvePiRigExtensionPath(home),
|
|
4028
|
+
pi: { ok: false, label: "pi", detail: piAvailable.error, hint: "Install/update Pi with RIG_PI_INSTALL_COMMAND or install Pi manually." },
|
|
4029
|
+
piRig: { ok: false, label: "pi-rig global extension", hint: "Pi is required before pi-rig can be installed." }
|
|
4030
|
+
};
|
|
4031
|
+
if (!piAvailable.ok) {
|
|
4032
|
+
throw new Error(`Pi install/update failed: ${piAvailable.error ?? "pi unavailable"}`);
|
|
4033
|
+
}
|
|
4034
|
+
const packageSource = resolvePiRigPackageSource(input.projectRoot);
|
|
4035
|
+
removeManagedLegacyPiRigBridge(home);
|
|
4036
|
+
const install = await runner(["pi", "install", packageSource], { cwd: input.projectRoot });
|
|
4037
|
+
if (install.exitCode !== 0) {
|
|
4038
|
+
throw new Error(`pi-rig install failed: ${(install.stderr || install.stdout).trim() || `exit ${install.exitCode}`}`);
|
|
4039
|
+
}
|
|
4040
|
+
const next = await checkPiRigInstall({ homeDir: home, commandRunner: runner });
|
|
4041
|
+
return { ...next, installedPath: packageSource };
|
|
4042
|
+
}
|
|
4043
|
+
async function ensureRemotePiRigInstalled(input) {
|
|
4044
|
+
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
4045
|
+
method: "POST",
|
|
4046
|
+
headers: { "content-type": "application/json" },
|
|
4047
|
+
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
4048
|
+
});
|
|
4049
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4050
|
+
const piOk = record.piOk === true || record.ok === true;
|
|
4051
|
+
const piRigOk = record.piRigOk === true || record.installed === true || record.ok === true;
|
|
4052
|
+
const extensionPath = typeof record.extensionPath === "string" ? record.extensionPath : "remote:~/.pi/agent/extensions/pi-rig";
|
|
4053
|
+
return {
|
|
4054
|
+
remote: true,
|
|
4055
|
+
extensionPath,
|
|
4056
|
+
pi: {
|
|
4057
|
+
ok: piOk,
|
|
4058
|
+
label: "pi",
|
|
4059
|
+
detail: typeof record.piVersion === "string" ? record.piVersion : undefined,
|
|
4060
|
+
hint: piOk ? undefined : "Install/update Pi on the selected remote Rig server."
|
|
4061
|
+
},
|
|
4062
|
+
piRig: {
|
|
4063
|
+
ok: piRigOk,
|
|
4064
|
+
label: "pi-rig global extension",
|
|
4065
|
+
detail: extensionPath,
|
|
4066
|
+
hint: piRigOk ? undefined : "Install/enable pi-rig on the selected remote Rig server."
|
|
4067
|
+
}
|
|
4068
|
+
};
|
|
4069
|
+
}
|
|
4070
|
+
async function buildPiSetupChecks(input = {}) {
|
|
4071
|
+
const status = await checkPiRigInstall(input);
|
|
4072
|
+
return [status.pi, status.piRig];
|
|
4013
4073
|
}
|
|
4014
|
-
|
|
4015
|
-
// packages/cli/src/commands/init.ts
|
|
4016
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4017
|
-
import { spawnSync } from "child_process";
|
|
4018
|
-
import { resolve as resolve16 } from "path";
|
|
4019
|
-
import { buildRigInitConfigSource } from "@rig/core";
|
|
4020
|
-
import { listGitHubProjects as listGitHubProjectsDirect, resolveProjectStatusField as resolveProjectStatusFieldDirect } from "@rig/server";
|
|
4021
4074
|
|
|
4022
4075
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
4023
4076
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
@@ -6385,52 +6438,593 @@ async function promptForTaskSelection(question) {
|
|
|
6385
6438
|
}
|
|
6386
6439
|
}
|
|
6387
6440
|
|
|
6388
|
-
// packages/cli/src/commands/
|
|
6441
|
+
// packages/cli/src/commands/_pi-frontend.ts
|
|
6442
|
+
import { mkdtempSync, rmSync as rmSync5 } from "fs";
|
|
6443
|
+
import { tmpdir } from "os";
|
|
6444
|
+
import { join as join2 } from "path";
|
|
6445
|
+
import { main as runPiMain } from "@earendil-works/pi-coding-agent";
|
|
6446
|
+
|
|
6447
|
+
// packages/cli/src/commands/_pi-worker-bridge-extension.ts
|
|
6389
6448
|
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
6390
|
-
var
|
|
6391
|
-
|
|
6392
|
-
"
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
"
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
"
|
|
6400
|
-
|
|
6401
|
-
"Merge",
|
|
6402
|
-
"Complete"
|
|
6403
|
-
];
|
|
6404
|
-
var GREEN = "\x1B[32m";
|
|
6405
|
-
var BLUE = "\x1B[34m";
|
|
6406
|
-
var MAGENTA = "\x1B[35m";
|
|
6407
|
-
var YELLOW = "\x1B[33m";
|
|
6408
|
-
var RED = "\x1B[31m";
|
|
6409
|
-
var DIM = "\x1B[2m";
|
|
6410
|
-
var BOLD = "\x1B[1m";
|
|
6411
|
-
var RESET = "\x1B[0m";
|
|
6412
|
-
async function loadPiTuiRuntime() {
|
|
6449
|
+
var MAX_TRANSCRIPT_LINES = 120;
|
|
6450
|
+
function recordOf(value) {
|
|
6451
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6452
|
+
}
|
|
6453
|
+
function asText(value) {
|
|
6454
|
+
if (typeof value === "string")
|
|
6455
|
+
return value;
|
|
6456
|
+
if (value === null || value === undefined)
|
|
6457
|
+
return "";
|
|
6458
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
6459
|
+
return String(value);
|
|
6413
6460
|
try {
|
|
6414
|
-
return
|
|
6461
|
+
return JSON.stringify(value);
|
|
6415
6462
|
} catch {
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6463
|
+
return String(value);
|
|
6464
|
+
}
|
|
6465
|
+
}
|
|
6466
|
+
function textFromContent(content) {
|
|
6467
|
+
if (typeof content === "string")
|
|
6468
|
+
return content;
|
|
6469
|
+
if (!Array.isArray(content))
|
|
6470
|
+
return asText(content);
|
|
6471
|
+
return content.flatMap((part) => {
|
|
6472
|
+
const item = recordOf(part);
|
|
6473
|
+
if (!item)
|
|
6474
|
+
return [];
|
|
6475
|
+
if (typeof item.text === "string")
|
|
6476
|
+
return [item.text];
|
|
6477
|
+
if (typeof item.content === "string")
|
|
6478
|
+
return [item.content];
|
|
6479
|
+
if (item.type === "toolCall")
|
|
6480
|
+
return [`\u23FA ${String(item.name ?? "tool")} ${asText(item.arguments ?? "")}`.trim()];
|
|
6481
|
+
if (item.type === "toolResult")
|
|
6482
|
+
return [`\u21B3 ${asText(item.content ?? item.result ?? "")}`.trim()];
|
|
6483
|
+
return [];
|
|
6484
|
+
}).join(`
|
|
6485
|
+
`);
|
|
6486
|
+
}
|
|
6487
|
+
function appendTranscript(state, label, text2) {
|
|
6488
|
+
const trimmed = text2.trimEnd();
|
|
6489
|
+
if (!trimmed)
|
|
6490
|
+
return;
|
|
6491
|
+
const lines = trimmed.split(/\r?\n/);
|
|
6492
|
+
state.transcript.push(`${label}: ${lines[0] ?? ""}`);
|
|
6493
|
+
for (const line of lines.slice(1))
|
|
6494
|
+
state.transcript.push(` ${line}`);
|
|
6495
|
+
if (state.transcript.length > MAX_TRANSCRIPT_LINES) {
|
|
6496
|
+
state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
|
|
6497
|
+
}
|
|
6498
|
+
}
|
|
6499
|
+
function nativePiUi(ctx) {
|
|
6500
|
+
const ui = ctx.ui;
|
|
6501
|
+
return typeof ui.emitSessionEvent === "function" && typeof ui.appendSessionMessages === "function" ? ui : null;
|
|
6502
|
+
}
|
|
6503
|
+
function syncNativeDisplayCwd(ctx, state) {
|
|
6504
|
+
const ui = nativePiUi(ctx);
|
|
6505
|
+
if (ui?.setDisplayCwd && state.cwd)
|
|
6506
|
+
ui.setDisplayCwd(state.cwd);
|
|
6507
|
+
}
|
|
6508
|
+
function parseExtensionUiRequest(value) {
|
|
6509
|
+
const request = recordOf(value) ?? {};
|
|
6510
|
+
const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
|
|
6511
|
+
const method = String(request.method ?? request.type ?? "input");
|
|
6512
|
+
const prompt = asText(request.prompt ?? request.message ?? request.title ?? method);
|
|
6513
|
+
const rawOptions = Array.isArray(request.options) ? request.options : Array.isArray(request.items) ? request.items : [];
|
|
6514
|
+
const options = rawOptions.map((option) => {
|
|
6515
|
+
const record = recordOf(option);
|
|
6516
|
+
return record ? asText(record.label ?? record.value ?? record.name ?? option) : asText(option);
|
|
6517
|
+
}).filter(Boolean);
|
|
6518
|
+
return { requestId, method, prompt, options };
|
|
6519
|
+
}
|
|
6520
|
+
function renderBridgeWidget(state) {
|
|
6521
|
+
const statusParts = [
|
|
6522
|
+
state.wsConnected ? "live WS" : "WS pending",
|
|
6523
|
+
state.status,
|
|
6524
|
+
state.model,
|
|
6525
|
+
state.cwd
|
|
6526
|
+
].filter(Boolean);
|
|
6527
|
+
const lines = [`Worker Pi daemon bridge \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
6528
|
+
if (state.activity)
|
|
6529
|
+
lines.push(state.activity);
|
|
6530
|
+
if (state.commands.length > 0) {
|
|
6531
|
+
lines.push(`Worker commands: ${state.commands.slice(0, 10).join(", ")}${state.commands.length > 10 ? ", \u2026" : ""}`);
|
|
6532
|
+
}
|
|
6533
|
+
lines.push("");
|
|
6534
|
+
if (state.transcript.length > 0) {
|
|
6535
|
+
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
6536
|
+
} else {
|
|
6537
|
+
lines.push("Waiting for worker Pi daemon transcript\u2026");
|
|
6538
|
+
}
|
|
6539
|
+
if (state.pendingUi) {
|
|
6540
|
+
lines.push("");
|
|
6541
|
+
lines.push(`Extension UI request \xB7 ${state.pendingUi.method}`);
|
|
6542
|
+
lines.push(state.pendingUi.prompt);
|
|
6543
|
+
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
6544
|
+
lines.push("Reply in the Pi editor. /cancel cancels this request.");
|
|
6545
|
+
}
|
|
6546
|
+
return lines;
|
|
6547
|
+
}
|
|
6548
|
+
function updatePiUi(ctx, state) {
|
|
6549
|
+
ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
|
|
6550
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
|
|
6551
|
+
syncNativeDisplayCwd(ctx, state);
|
|
6552
|
+
if (state.nativeStream && nativePiUi(ctx)) {
|
|
6553
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
6554
|
+
return;
|
|
6555
|
+
}
|
|
6556
|
+
ctx.ui.setWorkingVisible(false);
|
|
6557
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", [`Worker Pi daemon bridge \xB7 degraded widget transcript`, ...renderBridgeWidget(state)], { placement: "aboveEditor" });
|
|
6558
|
+
}
|
|
6559
|
+
function applyStatus(state, payload) {
|
|
6560
|
+
const status = recordOf(payload.status) ?? payload;
|
|
6561
|
+
state.streaming = status.isStreaming === true || status.isCompacting === true || status.isBashRunning === true;
|
|
6562
|
+
state.cwd = typeof status.cwd === "string" ? status.cwd : state.cwd;
|
|
6563
|
+
state.model = typeof status.model === "string" ? status.model : state.model;
|
|
6564
|
+
const pending = typeof status.pendingMessageCount === "number" ? status.pendingMessageCount : 0;
|
|
6565
|
+
state.status = `${state.streaming ? "streaming" : "idle"}${pending ? ` \xB7 ${pending} queued` : ""}`;
|
|
6566
|
+
}
|
|
6567
|
+
function applyMessage(state, message2) {
|
|
6568
|
+
const record = recordOf(message2);
|
|
6569
|
+
if (!record)
|
|
6570
|
+
return;
|
|
6571
|
+
const role = String(record.role ?? "system");
|
|
6572
|
+
const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
|
|
6573
|
+
appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
|
|
6574
|
+
}
|
|
6575
|
+
function applyPiEvent(ctx, state, eventValue) {
|
|
6576
|
+
const event = recordOf(eventValue);
|
|
6577
|
+
if (!event)
|
|
6578
|
+
return;
|
|
6579
|
+
const type = String(event.type ?? "event");
|
|
6580
|
+
if (type === "agent_start") {
|
|
6581
|
+
state.streaming = true;
|
|
6582
|
+
state.status = "streaming";
|
|
6583
|
+
} else if (type === "agent_end") {
|
|
6584
|
+
state.streaming = false;
|
|
6585
|
+
state.status = "idle";
|
|
6586
|
+
} else if (type === "queue_update") {
|
|
6587
|
+
const steering = Array.isArray(event.steering) ? event.steering.length : 0;
|
|
6588
|
+
const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
|
|
6589
|
+
state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
|
|
6590
|
+
}
|
|
6591
|
+
const native = nativePiUi(ctx);
|
|
6592
|
+
if (state.nativeStream && native?.emitSessionEvent) {
|
|
6593
|
+
native.emitSessionEvent(eventValue);
|
|
6594
|
+
return;
|
|
6595
|
+
}
|
|
6596
|
+
if (type === "agent_end") {
|
|
6597
|
+
appendTranscript(state, "System", "Agent turn complete.");
|
|
6598
|
+
return;
|
|
6599
|
+
}
|
|
6600
|
+
if (type === "message_start" || type === "message_end" || type === "turn_end") {
|
|
6601
|
+
applyMessage(state, event.message);
|
|
6602
|
+
return;
|
|
6603
|
+
}
|
|
6604
|
+
if (type === "message_update") {
|
|
6605
|
+
const assistantEvent = recordOf(event.assistantMessageEvent);
|
|
6606
|
+
const delta = typeof assistantEvent?.delta === "string" ? assistantEvent.delta : typeof assistantEvent?.text === "string" ? assistantEvent.text : "";
|
|
6607
|
+
if (delta)
|
|
6608
|
+
appendTranscript(state, assistantEvent?.type === "thinking_delta" ? "Thinking" : "Pi", delta);
|
|
6609
|
+
return;
|
|
6610
|
+
}
|
|
6611
|
+
if (type === "tool_execution_start") {
|
|
6612
|
+
appendTranscript(state, "Tool", `${String(event.toolName ?? "tool")} ${asText(event.args ?? "")}`.trim());
|
|
6613
|
+
return;
|
|
6614
|
+
}
|
|
6615
|
+
if (type === "tool_execution_update") {
|
|
6616
|
+
appendTranscript(state, "Tool", asText(event.partialResult ?? ""));
|
|
6617
|
+
return;
|
|
6618
|
+
}
|
|
6619
|
+
if (type === "tool_execution_end") {
|
|
6620
|
+
appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
|
|
6621
|
+
}
|
|
6622
|
+
}
|
|
6623
|
+
function firstPendingShell(state) {
|
|
6624
|
+
return state.pendingShells[0];
|
|
6625
|
+
}
|
|
6626
|
+
function finishPendingShell(state, shell, result) {
|
|
6627
|
+
const index = state.pendingShells.indexOf(shell);
|
|
6628
|
+
if (index !== -1)
|
|
6629
|
+
state.pendingShells.splice(index, 1);
|
|
6630
|
+
shell.resolve(result);
|
|
6631
|
+
}
|
|
6632
|
+
function failPendingShell(state, shell, error) {
|
|
6633
|
+
const index = state.pendingShells.indexOf(shell);
|
|
6634
|
+
if (index !== -1)
|
|
6635
|
+
state.pendingShells.splice(index, 1);
|
|
6636
|
+
shell.reject(error);
|
|
6637
|
+
}
|
|
6638
|
+
function applyUiEvent(state, value) {
|
|
6639
|
+
const event = recordOf(value);
|
|
6640
|
+
if (!event)
|
|
6641
|
+
return;
|
|
6642
|
+
const type = String(event.type ?? "ui");
|
|
6643
|
+
if (type === "shell.chunk") {
|
|
6644
|
+
const pending = firstPendingShell(state);
|
|
6645
|
+
const chunk = asText(event.chunk);
|
|
6646
|
+
if (pending) {
|
|
6647
|
+
pending.sawChunk = true;
|
|
6648
|
+
pending.onData(Buffer.from(chunk));
|
|
6649
|
+
} else {
|
|
6650
|
+
appendTranscript(state, "Tool", chunk);
|
|
6651
|
+
}
|
|
6652
|
+
return;
|
|
6653
|
+
}
|
|
6654
|
+
if (type === "shell.end") {
|
|
6655
|
+
const pending = firstPendingShell(state);
|
|
6656
|
+
const output = asText(event.output ?? "");
|
|
6657
|
+
const exitCode = typeof event.exitCode === "number" ? event.exitCode : event.isError === true ? 1 : 0;
|
|
6658
|
+
if (pending) {
|
|
6659
|
+
if (output && !pending.sawChunk)
|
|
6660
|
+
pending.onData(Buffer.from(output));
|
|
6661
|
+
finishPendingShell(state, pending, { exitCode });
|
|
6662
|
+
} else {
|
|
6663
|
+
appendTranscript(state, event.isError === true ? "Error" : "Tool", output || `exit ${String(exitCode)}`);
|
|
6664
|
+
}
|
|
6665
|
+
return;
|
|
6666
|
+
}
|
|
6667
|
+
if (type === "shell.start") {
|
|
6668
|
+
if (!firstPendingShell(state))
|
|
6669
|
+
appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
|
|
6670
|
+
return;
|
|
6671
|
+
}
|
|
6672
|
+
appendTranscript(state, "System", `${type}: ${asText(event)}`);
|
|
6673
|
+
}
|
|
6674
|
+
function applyEnvelope(ctx, state, envelopeValue) {
|
|
6675
|
+
const envelope = recordOf(envelopeValue);
|
|
6676
|
+
if (!envelope)
|
|
6677
|
+
return;
|
|
6678
|
+
const type = String(envelope.type ?? "");
|
|
6679
|
+
if (type === "ready") {
|
|
6680
|
+
const metadata = recordOf(envelope.metadata);
|
|
6681
|
+
state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
|
|
6682
|
+
state.status = "worker Pi daemon ready";
|
|
6683
|
+
if (!state.nativeStream)
|
|
6684
|
+
appendTranscript(state, "System", "Connected to worker Pi daemon.");
|
|
6685
|
+
} else if (type === "status.update") {
|
|
6686
|
+
applyStatus(state, envelope);
|
|
6687
|
+
} else if (type === "activity.update") {
|
|
6688
|
+
const activity = recordOf(envelope.activity);
|
|
6689
|
+
state.activity = [activity?.label, activity?.detail].map(asText).filter(Boolean).join(" \u2014 ");
|
|
6690
|
+
} else if (type === "extension_ui_request") {
|
|
6691
|
+
state.pendingUi = parseExtensionUiRequest(envelope.request);
|
|
6692
|
+
appendTranscript(state, "System", `Extension UI request: ${state.pendingUi.prompt}`);
|
|
6693
|
+
} else if (type === "pi.ui_event") {
|
|
6694
|
+
applyUiEvent(state, envelope.event);
|
|
6695
|
+
} else if (type === "pi.event") {
|
|
6696
|
+
applyPiEvent(ctx, state, envelope.event);
|
|
6697
|
+
} else if (type === "error") {
|
|
6698
|
+
appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
|
|
6699
|
+
}
|
|
6700
|
+
syncNativeDisplayCwd(ctx, state);
|
|
6701
|
+
}
|
|
6702
|
+
async function waitForWorkerReady(options, ctx, state) {
|
|
6703
|
+
while (true) {
|
|
6704
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => ({
|
|
6705
|
+
ready: false,
|
|
6706
|
+
status: error instanceof Error ? error.message : String(error),
|
|
6707
|
+
retryAfterMs: 1000
|
|
6708
|
+
}));
|
|
6709
|
+
if (session.ready === false) {
|
|
6710
|
+
const status = String(session.status ?? "starting");
|
|
6711
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
6712
|
+
updatePiUi(ctx, state);
|
|
6713
|
+
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
6714
|
+
appendTranscript(state, "Error", `Run ended before worker Pi daemon became ready: ${status}`);
|
|
6715
|
+
return false;
|
|
6716
|
+
}
|
|
6717
|
+
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
6718
|
+
continue;
|
|
6719
|
+
}
|
|
6720
|
+
const sessionRecord = recordOf(session) ?? {};
|
|
6721
|
+
applyEnvelope(ctx, state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
|
|
6722
|
+
updatePiUi(ctx, state);
|
|
6723
|
+
return true;
|
|
6724
|
+
}
|
|
6725
|
+
}
|
|
6726
|
+
function parseWsPayload(message2) {
|
|
6727
|
+
if (typeof message2.data === "string")
|
|
6728
|
+
return JSON.parse(message2.data);
|
|
6729
|
+
return JSON.parse(Buffer.from(message2.data).toString("utf8"));
|
|
6730
|
+
}
|
|
6731
|
+
async function connectWorkerStream(options, ctx, state) {
|
|
6732
|
+
const ready = await waitForWorkerReady(options, ctx, state);
|
|
6733
|
+
if (!ready)
|
|
6734
|
+
return;
|
|
6735
|
+
let catchupDone = false;
|
|
6736
|
+
const buffered = [];
|
|
6737
|
+
const wsUrl = await buildRunPiEventsWebSocketUrl(options.context, options.runId);
|
|
6738
|
+
const socket = new WebSocket(wsUrl);
|
|
6739
|
+
const closePromise = new Promise((resolve19) => {
|
|
6740
|
+
socket.onopen = () => {
|
|
6741
|
+
state.wsConnected = true;
|
|
6742
|
+
state.status = "live worker Pi WebSocket connected";
|
|
6743
|
+
updatePiUi(ctx, state);
|
|
6744
|
+
};
|
|
6745
|
+
socket.onmessage = (message2) => {
|
|
6746
|
+
try {
|
|
6747
|
+
const payload = parseWsPayload(message2);
|
|
6748
|
+
if (!catchupDone)
|
|
6749
|
+
buffered.push(payload);
|
|
6750
|
+
else {
|
|
6751
|
+
applyEnvelope(ctx, state, payload);
|
|
6752
|
+
updatePiUi(ctx, state);
|
|
6753
|
+
}
|
|
6754
|
+
} catch (error) {
|
|
6755
|
+
appendTranscript(state, "Error", `Unparseable worker Pi event: ${error instanceof Error ? error.message : String(error)}`);
|
|
6756
|
+
updatePiUi(ctx, state);
|
|
6757
|
+
}
|
|
6758
|
+
};
|
|
6759
|
+
socket.onerror = () => socket.close();
|
|
6760
|
+
socket.onclose = () => {
|
|
6761
|
+
state.wsConnected = false;
|
|
6762
|
+
state.status = "worker Pi WebSocket disconnected";
|
|
6763
|
+
updatePiUi(ctx, state);
|
|
6764
|
+
resolve19();
|
|
6765
|
+
};
|
|
6766
|
+
});
|
|
6767
|
+
try {
|
|
6768
|
+
const [messagesPayload, statusPayload, commandsPayload] = await Promise.all([
|
|
6769
|
+
getRunPiMessagesViaServer(options.context, options.runId),
|
|
6770
|
+
getRunPiStatusViaServer(options.context, options.runId),
|
|
6771
|
+
getRunPiCommandsViaServer(options.context, options.runId)
|
|
6423
6772
|
]);
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6773
|
+
const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
|
|
6774
|
+
const native = nativePiUi(ctx);
|
|
6775
|
+
if (state.nativeStream && native?.appendSessionMessages)
|
|
6776
|
+
native.appendSessionMessages(messages);
|
|
6777
|
+
else
|
|
6778
|
+
for (const message2 of messages)
|
|
6779
|
+
applyMessage(state, message2);
|
|
6780
|
+
applyStatus(state, statusPayload);
|
|
6781
|
+
const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
|
|
6782
|
+
state.commands = commands.flatMap((command) => {
|
|
6783
|
+
const record = recordOf(command);
|
|
6784
|
+
return typeof record?.name === "string" ? [`/${record.name}`] : [];
|
|
6785
|
+
});
|
|
6786
|
+
catchupDone = true;
|
|
6787
|
+
for (const payload of buffered.splice(0))
|
|
6788
|
+
applyEnvelope(ctx, state, payload);
|
|
6789
|
+
updatePiUi(ctx, state);
|
|
6790
|
+
} catch (error) {
|
|
6791
|
+
appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6792
|
+
catchupDone = true;
|
|
6793
|
+
updatePiUi(ctx, state);
|
|
6794
|
+
}
|
|
6795
|
+
await closePromise;
|
|
6796
|
+
}
|
|
6797
|
+
function createRemoteBashOperations(options, state, excludeFromContext) {
|
|
6798
|
+
return {
|
|
6799
|
+
exec(command, _cwd, execOptions) {
|
|
6800
|
+
return new Promise((resolve19, reject) => {
|
|
6801
|
+
const pending = {
|
|
6802
|
+
command,
|
|
6803
|
+
onData: execOptions.onData,
|
|
6804
|
+
resolve: resolve19,
|
|
6805
|
+
reject,
|
|
6806
|
+
sawChunk: false
|
|
6807
|
+
};
|
|
6808
|
+
const cleanup = () => {
|
|
6809
|
+
execOptions.signal?.removeEventListener("abort", onAbort);
|
|
6810
|
+
if (timer)
|
|
6811
|
+
clearTimeout(timer);
|
|
6812
|
+
};
|
|
6813
|
+
const onAbort = () => {
|
|
6814
|
+
cleanup();
|
|
6815
|
+
failPendingShell(state, pending, new Error("Remote worker shell command aborted locally."));
|
|
6816
|
+
};
|
|
6817
|
+
const timeoutMs = typeof execOptions.timeout === "number" && execOptions.timeout > 0 ? execOptions.timeout : 0;
|
|
6818
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
6819
|
+
cleanup();
|
|
6820
|
+
failPendingShell(state, pending, new Error(`Remote worker shell command timed out after ${timeoutMs}ms.`));
|
|
6821
|
+
}, timeoutMs) : null;
|
|
6822
|
+
const wrappedResolve = pending.resolve;
|
|
6823
|
+
const wrappedReject = pending.reject;
|
|
6824
|
+
pending.resolve = (result) => {
|
|
6825
|
+
cleanup();
|
|
6826
|
+
wrappedResolve(result);
|
|
6827
|
+
};
|
|
6828
|
+
pending.reject = (error) => {
|
|
6829
|
+
cleanup();
|
|
6830
|
+
wrappedReject(error);
|
|
6831
|
+
};
|
|
6832
|
+
execOptions.signal?.addEventListener("abort", onAbort, { once: true });
|
|
6833
|
+
state.pendingShells.push(pending);
|
|
6834
|
+
sendRunPiShellViaServer(options.context, options.runId, `${excludeFromContext ? "!!" : "!"}${command}`).catch((error) => {
|
|
6835
|
+
cleanup();
|
|
6836
|
+
failPendingShell(state, pending, error instanceof Error ? error : new Error(String(error)));
|
|
6837
|
+
});
|
|
6838
|
+
});
|
|
6839
|
+
}
|
|
6840
|
+
};
|
|
6841
|
+
}
|
|
6842
|
+
async function answerPendingUi(options, state, line) {
|
|
6843
|
+
const pending = state.pendingUi;
|
|
6844
|
+
if (!pending)
|
|
6845
|
+
return false;
|
|
6846
|
+
if (line === "/cancel") {
|
|
6847
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { cancelled: true });
|
|
6848
|
+
} else if (pending.method === "confirm") {
|
|
6849
|
+
const confirmed = /^(y|yes|true|1)$/i.test(line);
|
|
6850
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: confirmed, confirmed });
|
|
6851
|
+
} else if (pending.options.length > 0 && /^\d+$/.test(line)) {
|
|
6852
|
+
const selected = pending.options[Math.max(0, Number(line) - 1)] ?? line;
|
|
6853
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: selected });
|
|
6854
|
+
} else {
|
|
6855
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: line });
|
|
6856
|
+
}
|
|
6857
|
+
appendTranscript(state, "System", `Responded to extension UI request ${pending.requestId}.`);
|
|
6858
|
+
state.pendingUi = null;
|
|
6859
|
+
return true;
|
|
6860
|
+
}
|
|
6861
|
+
async function routeInput(options, ctx, state, line) {
|
|
6862
|
+
const text2 = line.trim();
|
|
6863
|
+
if (!text2)
|
|
6864
|
+
return;
|
|
6865
|
+
if (await answerPendingUi(options, state, text2)) {
|
|
6866
|
+
updatePiUi(ctx, state);
|
|
6867
|
+
return;
|
|
6868
|
+
}
|
|
6869
|
+
if (text2 === "/detach" || text2 === "/quit" || text2 === "/q") {
|
|
6870
|
+
appendTranscript(state, "System", "Detached locally; worker Pi daemon continues.");
|
|
6871
|
+
updatePiUi(ctx, state);
|
|
6872
|
+
ctx.shutdown();
|
|
6873
|
+
return;
|
|
6874
|
+
}
|
|
6875
|
+
if (text2 === "/stop") {
|
|
6876
|
+
await abortRunPiViaServer(options.context, options.runId);
|
|
6877
|
+
appendTranscript(state, "System", "Stop requested for worker Pi daemon.");
|
|
6878
|
+
updatePiUi(ctx, state);
|
|
6879
|
+
ctx.shutdown();
|
|
6880
|
+
return;
|
|
6881
|
+
}
|
|
6882
|
+
if (text2.startsWith("!")) {
|
|
6883
|
+
appendTranscript(state, "You", text2);
|
|
6884
|
+
await sendRunPiShellViaServer(options.context, options.runId, text2);
|
|
6885
|
+
} else if (text2.startsWith("/")) {
|
|
6886
|
+
appendTranscript(state, "You", text2);
|
|
6887
|
+
const result = await runRunPiCommandViaServer(options.context, options.runId, text2);
|
|
6888
|
+
const message2 = typeof result.message === "string" ? result.message : "worker command accepted";
|
|
6889
|
+
appendTranscript(state, "System", message2);
|
|
6890
|
+
} else {
|
|
6891
|
+
appendTranscript(state, "You", text2);
|
|
6892
|
+
await sendRunPiPromptViaServer(options.context, options.runId, text2, state.streaming ? "steer" : undefined);
|
|
6893
|
+
}
|
|
6894
|
+
updatePiUi(ctx, state);
|
|
6895
|
+
}
|
|
6896
|
+
function createRigWorkerPiBridgeExtension(options) {
|
|
6897
|
+
return (pi) => {
|
|
6898
|
+
const state = {
|
|
6899
|
+
transcript: [],
|
|
6900
|
+
status: "starting worker Pi daemon bridge",
|
|
6901
|
+
activity: "",
|
|
6902
|
+
cwd: "",
|
|
6903
|
+
model: "",
|
|
6904
|
+
commands: [],
|
|
6905
|
+
streaming: false,
|
|
6906
|
+
pendingUi: null,
|
|
6907
|
+
pendingShells: [],
|
|
6908
|
+
wsConnected: false,
|
|
6909
|
+
nativeStream: false
|
|
6431
6910
|
};
|
|
6911
|
+
if (options.initialMessageSent)
|
|
6912
|
+
appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
|
|
6913
|
+
let nativePiUiContextAvailable = false;
|
|
6914
|
+
pi.on("user_bash", (event) => {
|
|
6915
|
+
state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
|
|
6916
|
+
return { operations: createRemoteBashOperations(options, state, event.excludeFromContext === true) };
|
|
6917
|
+
});
|
|
6918
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
6919
|
+
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
6920
|
+
state.nativeStream = nativePiUiContextAvailable;
|
|
6921
|
+
updatePiUi(ctx, state);
|
|
6922
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker Pi native stream bridge loaded" : "Rig worker Pi bridge extension loaded (degraded widget fallback)", "info");
|
|
6923
|
+
ctx.ui.onTerminalInput((data) => {
|
|
6924
|
+
if (data.includes("\x04")) {
|
|
6925
|
+
ctx.shutdown();
|
|
6926
|
+
return { consume: true };
|
|
6927
|
+
}
|
|
6928
|
+
if (!data.includes("\r") && !data.includes(`
|
|
6929
|
+
`))
|
|
6930
|
+
return;
|
|
6931
|
+
const inlineText = data.replace(/[\r\n]+/g, "").trim();
|
|
6932
|
+
const editorText = ctx.ui.getEditorText().trim();
|
|
6933
|
+
const text2 = [editorText, inlineText].filter(Boolean).join(" ").trim();
|
|
6934
|
+
if (!text2)
|
|
6935
|
+
return;
|
|
6936
|
+
if (text2.startsWith("!"))
|
|
6937
|
+
return;
|
|
6938
|
+
ctx.ui.setEditorText("");
|
|
6939
|
+
routeInput(options, ctx, state, text2).catch((error) => {
|
|
6940
|
+
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
6941
|
+
updatePiUi(ctx, state);
|
|
6942
|
+
});
|
|
6943
|
+
return { consume: true };
|
|
6944
|
+
});
|
|
6945
|
+
connectWorkerStream(options, ctx, state).catch((error) => {
|
|
6946
|
+
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
6947
|
+
updatePiUi(ctx, state);
|
|
6948
|
+
});
|
|
6949
|
+
});
|
|
6950
|
+
pi.on("session_shutdown", () => {});
|
|
6951
|
+
};
|
|
6952
|
+
}
|
|
6953
|
+
|
|
6954
|
+
// packages/cli/src/commands/_pi-frontend.ts
|
|
6955
|
+
function setTemporaryEnv(updates) {
|
|
6956
|
+
const previous = new Map;
|
|
6957
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
6958
|
+
previous.set(key, process.env[key]);
|
|
6959
|
+
process.env[key] = value;
|
|
6960
|
+
}
|
|
6961
|
+
return () => {
|
|
6962
|
+
for (const [key, value] of previous) {
|
|
6963
|
+
if (value === undefined)
|
|
6964
|
+
delete process.env[key];
|
|
6965
|
+
else
|
|
6966
|
+
process.env[key] = value;
|
|
6967
|
+
}
|
|
6968
|
+
};
|
|
6969
|
+
}
|
|
6970
|
+
async function attachRunBundledPiFrontend(context, input) {
|
|
6971
|
+
const tempRoot = mkdtempSync(join2(tmpdir(), "rig-pi-frontend-"));
|
|
6972
|
+
const cwd = join2(tempRoot, "workspace");
|
|
6973
|
+
const agentDir = join2(tempRoot, "agent");
|
|
6974
|
+
const sessionDir = join2(tempRoot, "sessions");
|
|
6975
|
+
const previousCwd = process.cwd();
|
|
6976
|
+
const restoreEnv = setTemporaryEnv({
|
|
6977
|
+
PI_CODING_AGENT_DIR: agentDir,
|
|
6978
|
+
PI_CODING_AGENT_SESSION_DIR: sessionDir,
|
|
6979
|
+
PI_OFFLINE: "1",
|
|
6980
|
+
PI_SKIP_VERSION_CHECK: "1"
|
|
6981
|
+
});
|
|
6982
|
+
let detached = false;
|
|
6983
|
+
try {
|
|
6984
|
+
await Bun.$`mkdir -p ${cwd} ${agentDir} ${sessionDir}`.quiet();
|
|
6985
|
+
process.chdir(cwd);
|
|
6986
|
+
await runPiMain([
|
|
6987
|
+
"--offline",
|
|
6988
|
+
"--no-session",
|
|
6989
|
+
"--no-tools",
|
|
6990
|
+
"--no-builtin-tools",
|
|
6991
|
+
"--no-skills",
|
|
6992
|
+
"--no-prompt-templates",
|
|
6993
|
+
"--no-themes",
|
|
6994
|
+
"--no-context-files",
|
|
6995
|
+
"--no-approve"
|
|
6996
|
+
], {
|
|
6997
|
+
extensionFactories: [
|
|
6998
|
+
createRigWorkerPiBridgeExtension({
|
|
6999
|
+
context,
|
|
7000
|
+
runId: input.runId,
|
|
7001
|
+
initialMessageSent: input.steered === true
|
|
7002
|
+
})
|
|
7003
|
+
]
|
|
7004
|
+
});
|
|
7005
|
+
detached = true;
|
|
7006
|
+
} finally {
|
|
7007
|
+
process.chdir(previousCwd);
|
|
7008
|
+
restoreEnv();
|
|
7009
|
+
rmSync5(tempRoot, { recursive: true, force: true });
|
|
6432
7010
|
}
|
|
7011
|
+
let run = { runId: input.runId, status: "unknown" };
|
|
7012
|
+
try {
|
|
7013
|
+
run = await getRunDetailsViaServer(context, input.runId);
|
|
7014
|
+
} catch {}
|
|
7015
|
+
return {
|
|
7016
|
+
run,
|
|
7017
|
+
logs: [],
|
|
7018
|
+
timeline: [],
|
|
7019
|
+
timelineCursor: null,
|
|
7020
|
+
steered: input.steered === true,
|
|
7021
|
+
detached,
|
|
7022
|
+
rendered: "actual bundled Pi frontend hosted Rig worker Pi bridge extension"
|
|
7023
|
+
};
|
|
6433
7024
|
}
|
|
7025
|
+
|
|
7026
|
+
// packages/cli/src/commands/_operator-view.ts
|
|
7027
|
+
var TERMINAL_RUN_STATUSES2 = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
6434
7028
|
function runStatusFromPayload(payload) {
|
|
6435
7029
|
const run = payload.run && typeof payload.run === "object" && !Array.isArray(payload.run) ? payload.run : payload;
|
|
6436
7030
|
return String(run.status ?? "unknown").toLowerCase();
|
|
@@ -6469,198 +7063,15 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
6469
7063
|
const timelineCursor = typeof timelinePage.nextCursor === "string" ? timelinePage.nextCursor : options.timelineCursor ?? null;
|
|
6470
7064
|
return { run, logs, timeline, timelineCursor, rendered: renderOperatorSnapshot({ run, logs, timeline }) };
|
|
6471
7065
|
}
|
|
6472
|
-
function unwrapRun(runPayload) {
|
|
6473
|
-
return runPayload.run && typeof runPayload.run === "object" && !Array.isArray(runPayload.run) ? runPayload.run : runPayload;
|
|
6474
|
-
}
|
|
6475
|
-
function logDetail2(log3) {
|
|
6476
|
-
return typeof log3.detail === "string" ? log3.detail.trim() : "";
|
|
6477
|
-
}
|
|
6478
|
-
function logTitle(log3) {
|
|
6479
|
-
return typeof log3.title === "string" ? log3.title.trim() : "";
|
|
6480
|
-
}
|
|
6481
|
-
function renderAssistantTextFromTimeline(entries) {
|
|
6482
|
-
const assistant = entries.filter((entry) => entry.type === "assistant_message" && typeof entry.text === "string").at(-1);
|
|
6483
|
-
const text2 = typeof assistant?.text === "string" ? assistant.text.trimEnd() : "";
|
|
6484
|
-
if (!text2)
|
|
6485
|
-
return [];
|
|
6486
|
-
return [`${BLUE}${BOLD}Remote Pi assistant${RESET}`, ...text2.split(/\r?\n/).slice(-18)];
|
|
6487
|
-
}
|
|
6488
|
-
function renderToolLines(entries) {
|
|
6489
|
-
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());
|
|
6490
|
-
}
|
|
6491
|
-
function renderStageLines(logs) {
|
|
6492
|
-
const latestByStage = new Map;
|
|
6493
|
-
for (const log3 of logs) {
|
|
6494
|
-
const title = logTitle(log3).toLowerCase();
|
|
6495
|
-
const stageName = String(log3.stage ?? "").toLowerCase();
|
|
6496
|
-
const stage = CANONICAL_STAGES2.find((candidate) => candidate.toLowerCase() === title || candidate.toLowerCase() === stageName);
|
|
6497
|
-
if (stage)
|
|
6498
|
-
latestByStage.set(stage, log3);
|
|
6499
|
-
}
|
|
6500
|
-
return CANONICAL_STAGES2.map((stage) => {
|
|
6501
|
-
const log3 = latestByStage.get(stage);
|
|
6502
|
-
const status = String(log3?.status ?? "pending");
|
|
6503
|
-
const detail = log3 ? logDetail2(log3) : "";
|
|
6504
|
-
const color = status === "completed" ? GREEN : status === "failed" || status === "rejected" ? RED : status === "pending" ? DIM : YELLOW;
|
|
6505
|
-
const mark = status === "completed" ? "\u2713" : status === "pending" ? "\xB7" : status === "failed" ? "\u2717" : "\u25B6";
|
|
6506
|
-
return `${color}${mark} ${stage}${RESET}${detail ? ` ${DIM}\u2014 ${detail.slice(0, 140)}${RESET}` : ""}`;
|
|
6507
|
-
});
|
|
6508
|
-
}
|
|
6509
|
-
function renderEventLines(logs) {
|
|
6510
|
-
return logs.filter((log3) => !CANONICAL_STAGES2.some((stage) => stage.toLowerCase() === logTitle(log3).toLowerCase())).slice(-12).flatMap((log3) => {
|
|
6511
|
-
const title = logTitle(log3) || "Rig event";
|
|
6512
|
-
const detail = logDetail2(log3);
|
|
6513
|
-
if (!detail)
|
|
6514
|
-
return [];
|
|
6515
|
-
const tone = String(log3.tone ?? "");
|
|
6516
|
-
const color = tone === "error" ? RED : tone === "tool" ? MAGENTA : DIM;
|
|
6517
|
-
return [`${color}[${title}]${RESET} ${detail.slice(0, 220)}`];
|
|
6518
|
-
});
|
|
6519
|
-
}
|
|
6520
|
-
|
|
6521
|
-
class RigRunComponent {
|
|
6522
|
-
truncateToWidth;
|
|
6523
|
-
snapshot = { run: {}, logs: [], timeline: [] };
|
|
6524
|
-
localEvents = [];
|
|
6525
|
-
constructor(truncateToWidth) {
|
|
6526
|
-
this.truncateToWidth = truncateToWidth;
|
|
6527
|
-
}
|
|
6528
|
-
update(snapshot) {
|
|
6529
|
-
this.snapshot = snapshot;
|
|
6530
|
-
}
|
|
6531
|
-
addLocalEvent(message2) {
|
|
6532
|
-
this.localEvents.push(`${new Date().toLocaleTimeString()} ${message2}`);
|
|
6533
|
-
this.localEvents = this.localEvents.slice(-8);
|
|
6534
|
-
}
|
|
6535
|
-
invalidate() {}
|
|
6536
|
-
render(width) {
|
|
6537
|
-
const run = unwrapRun(this.snapshot.run);
|
|
6538
|
-
const runId = String(run.runId ?? run.id ?? "run");
|
|
6539
|
-
const status = String(run.status ?? "unknown");
|
|
6540
|
-
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";
|
|
6541
|
-
const lines = [
|
|
6542
|
-
`${BOLD}Rig Pi frontend${RESET} ${DIM}(local Pi TUI \u2192 Rig server \u2192 worker Pi backend)${RESET}`,
|
|
6543
|
-
`${BOLD}${runId}${RESET} \xB7 ${status} \xB7 ${DIM}${worker}${RESET}`,
|
|
6544
|
-
"",
|
|
6545
|
-
`${BOLD}Rig flow${RESET}`,
|
|
6546
|
-
...renderStageLines(this.snapshot.logs ?? []),
|
|
6547
|
-
"",
|
|
6548
|
-
...renderAssistantTextFromTimeline(this.snapshot.timeline ?? []),
|
|
6549
|
-
...renderToolLines(this.snapshot.timeline ?? []),
|
|
6550
|
-
"",
|
|
6551
|
-
`${BOLD}Rig / Pi events${RESET}`,
|
|
6552
|
-
...renderEventLines(this.snapshot.logs ?? []),
|
|
6553
|
-
...this.localEvents.map((event) => `${GREEN}[frontend]${RESET} ${event}`),
|
|
6554
|
-
""
|
|
6555
|
-
];
|
|
6556
|
-
return lines.slice(-42).map((line) => this.truncateToWidth(line, Math.max(10, width)));
|
|
6557
|
-
}
|
|
6558
|
-
}
|
|
6559
|
-
|
|
6560
|
-
class RigInputComponent {
|
|
6561
|
-
matchesKey;
|
|
6562
|
-
truncateToWidth;
|
|
6563
|
-
input;
|
|
6564
|
-
status = "Type text, /skill:..., or remote Pi slash commands. Local: /stop /detach.";
|
|
6565
|
-
constructor(InputCtor, matchesKey, truncateToWidth, onSubmit, onEscape) {
|
|
6566
|
-
this.matchesKey = matchesKey;
|
|
6567
|
-
this.truncateToWidth = truncateToWidth;
|
|
6568
|
-
this.input = new InputCtor;
|
|
6569
|
-
this.input.onSubmit = (value) => {
|
|
6570
|
-
const text2 = value.trim();
|
|
6571
|
-
this.input.setValue("");
|
|
6572
|
-
if (text2)
|
|
6573
|
-
Promise.resolve(onSubmit(text2));
|
|
6574
|
-
};
|
|
6575
|
-
this.input.onEscape = onEscape;
|
|
6576
|
-
}
|
|
6577
|
-
handleInput(data) {
|
|
6578
|
-
if (this.matchesKey(data, "ctrl+d")) {
|
|
6579
|
-
this.input.onEscape?.();
|
|
6580
|
-
return;
|
|
6581
|
-
}
|
|
6582
|
-
this.input.handleInput?.(data);
|
|
6583
|
-
}
|
|
6584
|
-
setStatus(status) {
|
|
6585
|
-
this.status = status;
|
|
6586
|
-
}
|
|
6587
|
-
invalidate() {
|
|
6588
|
-
this.input.invalidate();
|
|
6589
|
-
}
|
|
6590
|
-
render(width) {
|
|
6591
|
-
return [
|
|
6592
|
-
`${DIM}${this.truncateToWidth(this.status, Math.max(10, width))}${RESET}`,
|
|
6593
|
-
`${GREEN}${BOLD}You \u2192 worker Pi:${RESET}`,
|
|
6594
|
-
...this.input.render(width)
|
|
6595
|
-
];
|
|
6596
|
-
}
|
|
6597
|
-
}
|
|
6598
|
-
async function attachRunPiTuiFrontend(context, input) {
|
|
6599
|
-
const piTui = await loadPiTuiRuntime();
|
|
6600
|
-
const terminal = new piTui.ProcessTerminal;
|
|
6601
|
-
const tui = new piTui.TUI(terminal);
|
|
6602
|
-
const root = new piTui.Container;
|
|
6603
|
-
const runView = new RigRunComponent(piTui.truncateToWidth);
|
|
6604
|
-
let detached = false;
|
|
6605
|
-
let steered = input.steered === true;
|
|
6606
|
-
let latest = await readOperatorSnapshot(context, input.runId);
|
|
6607
|
-
let timelineCursor = latest.timelineCursor;
|
|
6608
|
-
runView.update(latest);
|
|
6609
|
-
if (steered)
|
|
6610
|
-
runView.addLocalEvent("initial message queued to worker Pi.");
|
|
6611
|
-
const stop = () => {
|
|
6612
|
-
detached = true;
|
|
6613
|
-
tui.stop();
|
|
6614
|
-
};
|
|
6615
|
-
const inputView = new RigInputComponent(piTui.Input, piTui.matchesKey, piTui.truncateToWidth, async (line) => {
|
|
6616
|
-
if (line === "/detach" || line === "/quit" || line === "/q") {
|
|
6617
|
-
runView.addLocalEvent("detached from run.");
|
|
6618
|
-
stop();
|
|
6619
|
-
return;
|
|
6620
|
-
}
|
|
6621
|
-
if (line === "/stop") {
|
|
6622
|
-
await stopRunViaServer(context, input.runId);
|
|
6623
|
-
runView.addLocalEvent("stop requested.");
|
|
6624
|
-
stop();
|
|
6625
|
-
return;
|
|
6626
|
-
}
|
|
6627
|
-
await steerRunViaServer(context, input.runId, line);
|
|
6628
|
-
steered = true;
|
|
6629
|
-
runView.addLocalEvent(`queued to worker Pi: ${line.slice(0, 160)}`);
|
|
6630
|
-
tui.requestRender();
|
|
6631
|
-
}, stop);
|
|
6632
|
-
root.addChild(runView);
|
|
6633
|
-
root.addChild(inputView);
|
|
6634
|
-
tui.addChild(root);
|
|
6635
|
-
tui.setFocus(inputView.input);
|
|
6636
|
-
tui.start();
|
|
6637
|
-
tui.requestRender(true);
|
|
6638
|
-
const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 1000));
|
|
6639
|
-
try {
|
|
6640
|
-
while (!detached && !TERMINAL_RUN_STATUSES.has(runStatusFromPayload(latest.run))) {
|
|
6641
|
-
await Bun.sleep(pollMs);
|
|
6642
|
-
latest = await readOperatorSnapshot(context, input.runId, { timelineCursor });
|
|
6643
|
-
timelineCursor = latest.timelineCursor;
|
|
6644
|
-
runView.update(latest);
|
|
6645
|
-
inputView.setStatus(`Remote worker ${runStatusFromPayload(latest.run)}. Input is forwarded to worker Pi; /stop /detach are local controls.`);
|
|
6646
|
-
tui.requestRender();
|
|
6647
|
-
}
|
|
6648
|
-
} finally {
|
|
6649
|
-
if (!detached)
|
|
6650
|
-
tui.stop();
|
|
6651
|
-
}
|
|
6652
|
-
return { ...latest, timelineCursor, steered, detached, rendered: renderOperatorSnapshot(latest) };
|
|
6653
|
-
}
|
|
6654
7066
|
async function attachRunOperatorView(context, input) {
|
|
6655
7067
|
let steered = false;
|
|
6656
7068
|
if (input.message?.trim()) {
|
|
6657
|
-
await steerRunViaServer(context, input.runId, input.message.trim());
|
|
7069
|
+
await sendRunPiPromptViaServer(context, input.runId, input.message.trim(), "steer").catch(() => steerRunViaServer(context, input.runId, input.message.trim()));
|
|
6658
7070
|
steered = true;
|
|
6659
7071
|
}
|
|
6660
7072
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
6661
|
-
return
|
|
7073
|
+
return attachRunBundledPiFrontend(context, {
|
|
6662
7074
|
runId: input.runId,
|
|
6663
|
-
pollMs: input.pollMs,
|
|
6664
7075
|
steered
|
|
6665
7076
|
});
|
|
6666
7077
|
}
|
|
@@ -6671,7 +7082,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
6671
7082
|
surface.renderTimeline(snapshot.timeline);
|
|
6672
7083
|
surface.renderLogs(snapshot.logs);
|
|
6673
7084
|
if (steered)
|
|
6674
|
-
surface.info("
|
|
7085
|
+
surface.info("Message submitted to worker Pi.");
|
|
6675
7086
|
}
|
|
6676
7087
|
let detached = false;
|
|
6677
7088
|
let commandInput = null;
|
|
@@ -6690,7 +7101,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
6690
7101
|
}
|
|
6691
7102
|
const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 2000));
|
|
6692
7103
|
let timelineCursor = snapshot.timelineCursor;
|
|
6693
|
-
while (!detached && !
|
|
7104
|
+
while (!detached && !TERMINAL_RUN_STATUSES2.has(runStatusFromPayload(snapshot.run))) {
|
|
6694
7105
|
await Bun.sleep(pollMs);
|
|
6695
7106
|
snapshot = await readOperatorSnapshot(context, input.runId, { timelineCursor });
|
|
6696
7107
|
timelineCursor = snapshot.timelineCursor;
|
|
@@ -8745,8 +9156,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8745
9156
|
...input.model ? ["--model", input.model] : [],
|
|
8746
9157
|
"--prompt"
|
|
8747
9158
|
] : input.runtimeAdapter === "pi" ? [
|
|
8748
|
-
"
|
|
8749
|
-
"rpc",
|
|
9159
|
+
"__rig_pi_session_daemon__",
|
|
8750
9160
|
...input.model ? ["--model", input.model] : []
|
|
8751
9161
|
] : [
|
|
8752
9162
|
"--print",
|
|
@@ -8843,7 +9253,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8843
9253
|
projectRoot: context.projectRoot,
|
|
8844
9254
|
runId: input.runId,
|
|
8845
9255
|
stage,
|
|
8846
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
9256
|
+
detail: stage === "Launch Pi" ? "Worker Pi SDK session daemon starting; local frontend will attach over Rig-proxied WebSocket." : stage === "Plan" ? `${planningClassification.planningRequired ? "recorded" : "skipped"} (${planningClassification.reason}; size=${planningClassification.size}; risk=${planningClassification.risk})` : stage === "Implement" ? "Pi implementation pass is running in the worker runtime." : null,
|
|
8847
9257
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
8848
9258
|
});
|
|
8849
9259
|
}
|
|
@@ -8884,6 +9294,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8884
9294
|
let latestProviderCommand = null;
|
|
8885
9295
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
8886
9296
|
let snapshotSidecarPromise = null;
|
|
9297
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
8887
9298
|
let dirtyBaselineApplied = false;
|
|
8888
9299
|
const childEnv = {
|
|
8889
9300
|
...process.env,
|
|
@@ -8952,6 +9363,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8952
9363
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
8953
9364
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
8954
9365
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
9366
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
8955
9367
|
latestRuntimeBranch = runtimeId;
|
|
8956
9368
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
8957
9369
|
runtimeId: runtimeId ?? null,
|
|
@@ -8989,7 +9401,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
8989
9401
|
});
|
|
8990
9402
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
8991
9403
|
}
|
|
8992
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
9404
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
8993
9405
|
snapshotSidecarPromise = (async () => {
|
|
8994
9406
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
8995
9407
|
projectRoot: context.projectRoot,
|
|
@@ -9071,6 +9483,51 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9071
9483
|
}
|
|
9072
9484
|
return true;
|
|
9073
9485
|
}
|
|
9486
|
+
if (event.type === "pi.sessiond.starting" || event.type === "pi.sessiond.ready" || event.type === "pi.session.event_stream.connected" || event.type === "pi.prompt.sent" || event.type === "pi.prompt.waiting" || event.type === "pi.session.agent_end" || event.type === "pi.session.error") {
|
|
9487
|
+
const title = event.type === "pi.sessiond.starting" ? "Starting worker Pi session daemon" : event.type === "pi.sessiond.ready" ? "Worker Pi daemon ready" : event.type === "pi.session.event_stream.connected" ? "Worker Pi event stream connected" : event.type === "pi.prompt.sent" ? "Delivered initial prompt to worker Pi" : event.type === "pi.prompt.waiting" ? "Worker Pi prompt waiting" : event.type === "pi.session.agent_end" ? "Worker Pi turn complete" : "Worker Pi session error";
|
|
9488
|
+
const detail = event.type === "pi.sessiond.ready" ? "Daemon accepted control connection; waiting for SDK session metadata." : event.type === "pi.sessiond.starting" ? String(payload.workspaceDir ?? "worker runtime") : event.type === "pi.prompt.sent" ? `${String(payload.bytes ?? "unknown")} prompt bytes sent.` : event.type === "pi.prompt.waiting" ? String(payload.reason ?? "empty prompt") : event.type === "pi.session.error" ? String(payload.message ?? "session error") : String(payload.sessionId ?? payload.runId ?? "worker Pi session");
|
|
9489
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
9490
|
+
id: nextRunLogId(),
|
|
9491
|
+
title,
|
|
9492
|
+
detail,
|
|
9493
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
9494
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
9495
|
+
payload: {
|
|
9496
|
+
eventType: event.type,
|
|
9497
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
9498
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
9499
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
9500
|
+
},
|
|
9501
|
+
createdAt: new Date().toISOString()
|
|
9502
|
+
});
|
|
9503
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
9504
|
+
return true;
|
|
9505
|
+
}
|
|
9506
|
+
if (event.type === "pi.session.ready") {
|
|
9507
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
9508
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
9509
|
+
if (privateMetadata) {
|
|
9510
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
9511
|
+
piSession: publicMetadata,
|
|
9512
|
+
piSessionPrivate: privateMetadata
|
|
9513
|
+
});
|
|
9514
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
9515
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
9516
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
9517
|
+
title: "Worker Pi session ready",
|
|
9518
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
9519
|
+
tone: "info",
|
|
9520
|
+
status: "running",
|
|
9521
|
+
payload: {
|
|
9522
|
+
sessionId: sessionId || null,
|
|
9523
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
9524
|
+
},
|
|
9525
|
+
createdAt: new Date().toISOString()
|
|
9526
|
+
});
|
|
9527
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
9528
|
+
}
|
|
9529
|
+
return true;
|
|
9530
|
+
}
|
|
9074
9531
|
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") {
|
|
9075
9532
|
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";
|
|
9076
9533
|
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")}`;
|
|
@@ -9602,7 +10059,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
9602
10059
|
startedAt,
|
|
9603
10060
|
finishedAt: requestedAt
|
|
9604
10061
|
});
|
|
9605
|
-
|
|
10062
|
+
process.exit(0);
|
|
9606
10063
|
}
|
|
9607
10064
|
const runPiPrFeedbackFix = async (message2) => {
|
|
9608
10065
|
appendPiStageLog({
|