@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/index.js
CHANGED
|
@@ -2950,6 +2950,66 @@ async function steerRunViaServer(context, runId, message) {
|
|
|
2950
2950
|
});
|
|
2951
2951
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { ok: true };
|
|
2952
2952
|
}
|
|
2953
|
+
async function getRunPiSessionViaServer(context, runId) {
|
|
2954
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi`);
|
|
2955
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2956
|
+
}
|
|
2957
|
+
async function getRunPiMessagesViaServer(context, runId) {
|
|
2958
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/messages`);
|
|
2959
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { messages: [] };
|
|
2960
|
+
}
|
|
2961
|
+
async function getRunPiStatusViaServer(context, runId) {
|
|
2962
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/status`);
|
|
2963
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2964
|
+
}
|
|
2965
|
+
async function getRunPiCommandsViaServer(context, runId) {
|
|
2966
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands`);
|
|
2967
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { commands: [] };
|
|
2968
|
+
}
|
|
2969
|
+
async function sendRunPiPromptViaServer(context, runId, text2, streamingBehavior) {
|
|
2970
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/prompt`, {
|
|
2971
|
+
method: "POST",
|
|
2972
|
+
headers: { "content-type": "application/json" },
|
|
2973
|
+
body: JSON.stringify({ text: text2, streamingBehavior })
|
|
2974
|
+
});
|
|
2975
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
2976
|
+
}
|
|
2977
|
+
async function sendRunPiShellViaServer(context, runId, text2) {
|
|
2978
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/shell`, {
|
|
2979
|
+
method: "POST",
|
|
2980
|
+
headers: { "content-type": "application/json" },
|
|
2981
|
+
body: JSON.stringify({ text: text2 })
|
|
2982
|
+
});
|
|
2983
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
2984
|
+
}
|
|
2985
|
+
async function runRunPiCommandViaServer(context, runId, text2) {
|
|
2986
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/commands/run`, {
|
|
2987
|
+
method: "POST",
|
|
2988
|
+
headers: { "content-type": "application/json" },
|
|
2989
|
+
body: JSON.stringify({ text: text2 })
|
|
2990
|
+
});
|
|
2991
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { type: "done" };
|
|
2992
|
+
}
|
|
2993
|
+
async function respondRunPiExtensionUiViaServer(context, runId, requestId, valueOrCancel) {
|
|
2994
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/extension-ui/respond`, {
|
|
2995
|
+
method: "POST",
|
|
2996
|
+
headers: { "content-type": "application/json" },
|
|
2997
|
+
body: JSON.stringify({ requestId, ...valueOrCancel })
|
|
2998
|
+
});
|
|
2999
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { accepted: true };
|
|
3000
|
+
}
|
|
3001
|
+
async function abortRunPiViaServer(context, runId) {
|
|
3002
|
+
const payload = await requestServerJson(context, `/api/runs/${encodeURIComponent(runId)}/pi/abort`, { method: "POST" });
|
|
3003
|
+
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : { aborted: true };
|
|
3004
|
+
}
|
|
3005
|
+
async function buildRunPiEventsWebSocketUrl(context, runId) {
|
|
3006
|
+
const server = await ensureServerForCli(context.projectRoot);
|
|
3007
|
+
const url = new URL(`${server.baseUrl.replace(/\/+$/, "")}/api/runs/${encodeURIComponent(runId)}/pi/events`);
|
|
3008
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
3009
|
+
if (server.authToken)
|
|
3010
|
+
url.searchParams.set("token", server.authToken);
|
|
3011
|
+
return url.toString();
|
|
3012
|
+
}
|
|
2953
3013
|
async function submitTaskRunViaServer(context, input) {
|
|
2954
3014
|
const isTaskRun = Boolean(input.taskId);
|
|
2955
3015
|
const endpoint = isTaskRun ? "/api/runs/task" : "/api/runs/adhoc";
|
|
@@ -2982,183 +3042,6 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2982
3042
|
return { runId };
|
|
2983
3043
|
}
|
|
2984
3044
|
|
|
2985
|
-
// packages/cli/src/commands/_pi-install.ts
|
|
2986
|
-
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2987
|
-
import { homedir as homedir3 } from "os";
|
|
2988
|
-
import { resolve as resolve10 } from "path";
|
|
2989
|
-
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
2990
|
-
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2991
|
-
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2992
|
-
export { default } from '@rig/pi-rig';
|
|
2993
|
-
`;
|
|
2994
|
-
async function defaultCommandRunner(command, options = {}) {
|
|
2995
|
-
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
2996
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
2997
|
-
new Response(proc.stdout).text(),
|
|
2998
|
-
new Response(proc.stderr).text(),
|
|
2999
|
-
proc.exited
|
|
3000
|
-
]);
|
|
3001
|
-
return { exitCode, stdout, stderr };
|
|
3002
|
-
}
|
|
3003
|
-
function resolvePiRigExtensionPath(homeDir) {
|
|
3004
|
-
return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
3005
|
-
}
|
|
3006
|
-
function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
|
|
3007
|
-
const localPackage = resolve10(projectRoot, "packages", "pi-rig");
|
|
3008
|
-
if (exists(resolve10(localPackage, "package.json")))
|
|
3009
|
-
return localPackage;
|
|
3010
|
-
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
3011
|
-
}
|
|
3012
|
-
function resolvePiHomeDir(inputHomeDir) {
|
|
3013
|
-
return inputHomeDir ?? process.env.RIG_PI_HOME_DIR?.trim() ?? homedir3();
|
|
3014
|
-
}
|
|
3015
|
-
function piListContainsPiRig(output) {
|
|
3016
|
-
return output.split(/\r?\n/).some((line) => {
|
|
3017
|
-
const normalized = line.trim();
|
|
3018
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
3019
|
-
});
|
|
3020
|
-
}
|
|
3021
|
-
async function safeRun(runner, command, options) {
|
|
3022
|
-
try {
|
|
3023
|
-
return await runner(command, options);
|
|
3024
|
-
} catch (error) {
|
|
3025
|
-
return { exitCode: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
3026
|
-
}
|
|
3027
|
-
}
|
|
3028
|
-
function splitInstallCommand(value) {
|
|
3029
|
-
return value.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((part) => part.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
3030
|
-
}
|
|
3031
|
-
async function ensurePiBinaryAvailable(input) {
|
|
3032
|
-
const current = await safeRun(input.runner, ["pi", "--version"]);
|
|
3033
|
-
if (current.exitCode === 0) {
|
|
3034
|
-
const updateCommand = process.env.RIG_PI_UPDATE_COMMAND?.trim();
|
|
3035
|
-
if (updateCommand) {
|
|
3036
|
-
const parts2 = splitInstallCommand(updateCommand);
|
|
3037
|
-
if (parts2.length > 0)
|
|
3038
|
-
await safeRun(input.runner, parts2, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
3039
|
-
}
|
|
3040
|
-
return { ok: true, detail: (current.stdout || current.stderr).trim() || undefined };
|
|
3041
|
-
}
|
|
3042
|
-
const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi@latest install";
|
|
3043
|
-
const parts = splitInstallCommand(installCommand);
|
|
3044
|
-
if (parts.length === 0) {
|
|
3045
|
-
return { ok: false, error: (current.stderr || current.stdout).trim() || "pi --version failed" };
|
|
3046
|
-
}
|
|
3047
|
-
const install = await safeRun(input.runner, parts, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
3048
|
-
if (install.exitCode !== 0) {
|
|
3049
|
-
return { ok: false, installedOrUpdated: true, error: (install.stderr || install.stdout).trim() || `Pi install command failed (${install.exitCode})` };
|
|
3050
|
-
}
|
|
3051
|
-
const next = await safeRun(input.runner, ["pi", "--version"]);
|
|
3052
|
-
return {
|
|
3053
|
-
ok: next.exitCode === 0,
|
|
3054
|
-
installedOrUpdated: true,
|
|
3055
|
-
detail: (next.stdout || next.stderr).trim() || undefined,
|
|
3056
|
-
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
3057
|
-
};
|
|
3058
|
-
}
|
|
3059
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
|
|
3060
|
-
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
3061
|
-
const indexPath = resolve10(extensionPath, "index.ts");
|
|
3062
|
-
if (!exists(indexPath))
|
|
3063
|
-
return;
|
|
3064
|
-
try {
|
|
3065
|
-
const content = readFileSync4(indexPath, "utf8");
|
|
3066
|
-
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
3067
|
-
rmSync3(extensionPath, { recursive: true, force: true });
|
|
3068
|
-
}
|
|
3069
|
-
} catch {}
|
|
3070
|
-
}
|
|
3071
|
-
async function checkPiRigInstall(input = {}) {
|
|
3072
|
-
const home = resolvePiHomeDir(input.homeDir);
|
|
3073
|
-
const extensionPath = resolvePiRigExtensionPath(home);
|
|
3074
|
-
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
3075
|
-
return {
|
|
3076
|
-
extensionPath,
|
|
3077
|
-
pi: { ok: true, label: "pi", detail: "fake-pi" },
|
|
3078
|
-
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
3079
|
-
};
|
|
3080
|
-
}
|
|
3081
|
-
const exists = input.exists ?? existsSync6;
|
|
3082
|
-
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3083
|
-
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
3084
|
-
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
3085
|
-
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
3086
|
-
${piListResult.stderr}`);
|
|
3087
|
-
const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
|
|
3088
|
-
const hasPiRig = listedPiRig;
|
|
3089
|
-
return {
|
|
3090
|
-
extensionPath,
|
|
3091
|
-
pi: {
|
|
3092
|
-
ok: piResult.exitCode === 0,
|
|
3093
|
-
label: "pi",
|
|
3094
|
-
detail: (piResult.stdout || piResult.stderr).trim() || undefined,
|
|
3095
|
-
hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
|
|
3096
|
-
},
|
|
3097
|
-
piRig: {
|
|
3098
|
-
ok: hasPiRig,
|
|
3099
|
-
label: "pi-rig global extension",
|
|
3100
|
-
detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
|
|
3101
|
-
hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
|
|
3102
|
-
}
|
|
3103
|
-
};
|
|
3104
|
-
}
|
|
3105
|
-
async function ensurePiRigInstalled(input) {
|
|
3106
|
-
const home = resolvePiHomeDir(input.homeDir);
|
|
3107
|
-
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
3108
|
-
const status2 = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
|
|
3109
|
-
return { ...status2, installedPath: status2.extensionPath };
|
|
3110
|
-
}
|
|
3111
|
-
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3112
|
-
const piAvailable = await ensurePiBinaryAvailable({ runner, projectRoot: input.projectRoot });
|
|
3113
|
-
const status = piAvailable.ok ? await checkPiRigInstall({ homeDir: home, commandRunner: runner }) : {
|
|
3114
|
-
extensionPath: resolvePiRigExtensionPath(home),
|
|
3115
|
-
pi: { ok: false, label: "pi", detail: piAvailable.error, hint: "Install/update Pi with RIG_PI_INSTALL_COMMAND or install Pi manually." },
|
|
3116
|
-
piRig: { ok: false, label: "pi-rig global extension", hint: "Pi is required before pi-rig can be installed." }
|
|
3117
|
-
};
|
|
3118
|
-
if (!piAvailable.ok) {
|
|
3119
|
-
throw new Error(`Pi install/update failed: ${piAvailable.error ?? "pi unavailable"}`);
|
|
3120
|
-
}
|
|
3121
|
-
const packageSource = resolvePiRigPackageSource(input.projectRoot);
|
|
3122
|
-
removeManagedLegacyPiRigBridge(home);
|
|
3123
|
-
const install = await runner(["pi", "install", packageSource], { cwd: input.projectRoot });
|
|
3124
|
-
if (install.exitCode !== 0) {
|
|
3125
|
-
throw new Error(`pi-rig install failed: ${(install.stderr || install.stdout).trim() || `exit ${install.exitCode}`}`);
|
|
3126
|
-
}
|
|
3127
|
-
const next = await checkPiRigInstall({ homeDir: home, commandRunner: runner });
|
|
3128
|
-
return { ...next, installedPath: packageSource };
|
|
3129
|
-
}
|
|
3130
|
-
async function ensureRemotePiRigInstalled(input) {
|
|
3131
|
-
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
3132
|
-
method: "POST",
|
|
3133
|
-
headers: { "content-type": "application/json" },
|
|
3134
|
-
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
3135
|
-
});
|
|
3136
|
-
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
3137
|
-
const piOk = record.piOk === true || record.ok === true;
|
|
3138
|
-
const piRigOk = record.piRigOk === true || record.installed === true || record.ok === true;
|
|
3139
|
-
const extensionPath = typeof record.extensionPath === "string" ? record.extensionPath : "remote:~/.pi/agent/extensions/pi-rig";
|
|
3140
|
-
return {
|
|
3141
|
-
remote: true,
|
|
3142
|
-
extensionPath,
|
|
3143
|
-
pi: {
|
|
3144
|
-
ok: piOk,
|
|
3145
|
-
label: "pi",
|
|
3146
|
-
detail: typeof record.piVersion === "string" ? record.piVersion : undefined,
|
|
3147
|
-
hint: piOk ? undefined : "Install/update Pi on the selected remote Rig server."
|
|
3148
|
-
},
|
|
3149
|
-
piRig: {
|
|
3150
|
-
ok: piRigOk,
|
|
3151
|
-
label: "pi-rig global extension",
|
|
3152
|
-
detail: extensionPath,
|
|
3153
|
-
hint: piRigOk ? undefined : "Install/enable pi-rig on the selected remote Rig server."
|
|
3154
|
-
}
|
|
3155
|
-
};
|
|
3156
|
-
}
|
|
3157
|
-
async function buildPiSetupChecks(input = {}) {
|
|
3158
|
-
const status = await checkPiRigInstall(input);
|
|
3159
|
-
return [status.pi, status.piRig];
|
|
3160
|
-
}
|
|
3161
|
-
|
|
3162
3045
|
// packages/cli/src/commands/_preflight.ts
|
|
3163
3046
|
function preflightCheck(id, label, status, detail, remediation) {
|
|
3164
3047
|
return {
|
|
@@ -3316,14 +3199,7 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
3316
3199
|
}
|
|
3317
3200
|
}
|
|
3318
3201
|
if ((options.runtimeAdapter ?? "pi") === "pi") {
|
|
3319
|
-
|
|
3320
|
-
ok: false,
|
|
3321
|
-
label: "pi/pi-rig checks",
|
|
3322
|
-
hint: message(error)
|
|
3323
|
-
}]);
|
|
3324
|
-
for (const pi of piChecks) {
|
|
3325
|
-
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.")));
|
|
3326
|
-
}
|
|
3202
|
+
checks.push(preflightCheck("runtime", "worker Pi SDK session daemon", "pass", selectedServer?.connectionKind === "remote" ? "remote worker-owned runtime" : "bundled server-owned runtime"));
|
|
3327
3203
|
} else {
|
|
3328
3204
|
checks.push(preflightCheck("runtime", "runtime adapter", "pass", options.runtimeAdapter));
|
|
3329
3205
|
}
|
|
@@ -3439,7 +3315,7 @@ async function executeQueue(context, args) {
|
|
|
3439
3315
|
}
|
|
3440
3316
|
|
|
3441
3317
|
// packages/cli/src/commands/agent.ts
|
|
3442
|
-
import { resolve as
|
|
3318
|
+
import { resolve as resolve11 } from "path";
|
|
3443
3319
|
import {
|
|
3444
3320
|
agentId,
|
|
3445
3321
|
cleanupAgentRuntime,
|
|
@@ -3449,8 +3325,8 @@ import {
|
|
|
3449
3325
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3450
3326
|
|
|
3451
3327
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3452
|
-
import { existsSync as
|
|
3453
|
-
import { resolve as
|
|
3328
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3329
|
+
import { resolve as resolve10 } from "path";
|
|
3454
3330
|
import {
|
|
3455
3331
|
readAuthorityRun,
|
|
3456
3332
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3472,8 +3348,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3472
3348
|
return "claude-code";
|
|
3473
3349
|
}
|
|
3474
3350
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3475
|
-
const issuesPath =
|
|
3476
|
-
if (!
|
|
3351
|
+
const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3352
|
+
if (!existsSync6(issuesPath)) {
|
|
3477
3353
|
return null;
|
|
3478
3354
|
}
|
|
3479
3355
|
let latest = null;
|
|
@@ -3541,7 +3417,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3541
3417
|
} else if ("errorText" in next) {
|
|
3542
3418
|
delete next.errorText;
|
|
3543
3419
|
}
|
|
3544
|
-
writeJsonFile3(
|
|
3420
|
+
writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3545
3421
|
return next;
|
|
3546
3422
|
}
|
|
3547
3423
|
|
|
@@ -3662,10 +3538,10 @@ async function executeAgent(context, args) {
|
|
|
3662
3538
|
status: "running",
|
|
3663
3539
|
startedAt: createdAt,
|
|
3664
3540
|
worktreePath: runtime.workspaceDir,
|
|
3665
|
-
artifactRoot:
|
|
3541
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3666
3542
|
logRoot: runtime.logsDir,
|
|
3667
|
-
sessionPath:
|
|
3668
|
-
sessionLogPath:
|
|
3543
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3544
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3669
3545
|
pid: process.pid
|
|
3670
3546
|
});
|
|
3671
3547
|
const result = await runInAgentRuntime({
|
|
@@ -3685,10 +3561,10 @@ async function executeAgent(context, args) {
|
|
|
3685
3561
|
startedAt: createdAt,
|
|
3686
3562
|
completedAt: failedAt,
|
|
3687
3563
|
worktreePath: runtime.workspaceDir,
|
|
3688
|
-
artifactRoot:
|
|
3564
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3689
3565
|
logRoot: runtime.logsDir,
|
|
3690
|
-
sessionPath:
|
|
3691
|
-
sessionLogPath:
|
|
3566
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3567
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3692
3568
|
pid: process.pid,
|
|
3693
3569
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3694
3570
|
});
|
|
@@ -3705,10 +3581,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3705
3581
|
startedAt: createdAt,
|
|
3706
3582
|
completedAt,
|
|
3707
3583
|
worktreePath: runtime.workspaceDir,
|
|
3708
|
-
artifactRoot:
|
|
3584
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3709
3585
|
logRoot: runtime.logsDir,
|
|
3710
|
-
sessionPath:
|
|
3711
|
-
sessionLogPath:
|
|
3586
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3587
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3712
3588
|
pid: process.pid
|
|
3713
3589
|
});
|
|
3714
3590
|
return {
|
|
@@ -3782,17 +3658,17 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3782
3658
|
import {
|
|
3783
3659
|
chmodSync,
|
|
3784
3660
|
copyFileSync as copyFileSync2,
|
|
3785
|
-
existsSync as
|
|
3661
|
+
existsSync as existsSync7,
|
|
3786
3662
|
mkdirSync as mkdirSync6,
|
|
3787
3663
|
readdirSync,
|
|
3788
3664
|
readlinkSync,
|
|
3789
|
-
rmSync as
|
|
3665
|
+
rmSync as rmSync3,
|
|
3790
3666
|
statSync,
|
|
3791
3667
|
symlinkSync,
|
|
3792
3668
|
unlinkSync
|
|
3793
3669
|
} from "fs";
|
|
3794
|
-
import { homedir as
|
|
3795
|
-
import { resolve as
|
|
3670
|
+
import { homedir as homedir3 } from "os";
|
|
3671
|
+
import { resolve as resolve12 } from "path";
|
|
3796
3672
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3797
3673
|
import {
|
|
3798
3674
|
computeRuntimeImageFingerprint,
|
|
@@ -3811,13 +3687,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3811
3687
|
|
|
3812
3688
|
// packages/cli/src/commands/dist.ts
|
|
3813
3689
|
function collectRigValidatorBuildTargets(input) {
|
|
3814
|
-
const validatorsRoot =
|
|
3815
|
-
if (!
|
|
3690
|
+
const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3691
|
+
if (!existsSync7(validatorsRoot))
|
|
3816
3692
|
return [];
|
|
3817
3693
|
const targets = [];
|
|
3818
3694
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3819
3695
|
for (const category of categories) {
|
|
3820
|
-
const categoryDir =
|
|
3696
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
3821
3697
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3822
3698
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3823
3699
|
continue;
|
|
@@ -3826,7 +3702,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3826
3702
|
continue;
|
|
3827
3703
|
targets.push({
|
|
3828
3704
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3829
|
-
dest:
|
|
3705
|
+
dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3830
3706
|
cwd: input.hostProjectRoot
|
|
3831
3707
|
});
|
|
3832
3708
|
}
|
|
@@ -3835,16 +3711,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3835
3711
|
}
|
|
3836
3712
|
async function findLatestDistBinary(projectRoot) {
|
|
3837
3713
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3838
|
-
if (!
|
|
3714
|
+
if (!existsSync7(distRoot)) {
|
|
3839
3715
|
return null;
|
|
3840
3716
|
}
|
|
3841
3717
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3842
3718
|
name: entry.name,
|
|
3843
|
-
mtimeMs: statSync(
|
|
3719
|
+
mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
|
|
3844
3720
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3845
3721
|
for (const { name } of entries) {
|
|
3846
|
-
const candidate =
|
|
3847
|
-
if (
|
|
3722
|
+
const candidate = resolve12(distRoot, name, "bin", "rig");
|
|
3723
|
+
if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3848
3724
|
return candidate;
|
|
3849
3725
|
}
|
|
3850
3726
|
}
|
|
@@ -3856,7 +3732,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3856
3732
|
async function runDistDoctor(projectRoot) {
|
|
3857
3733
|
const bunPath = Bun.which("bun");
|
|
3858
3734
|
const rigPath = Bun.which("rig");
|
|
3859
|
-
const userBinDir =
|
|
3735
|
+
const userBinDir = resolve12(homedir3(), ".local/bin");
|
|
3860
3736
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3861
3737
|
let rigRunnable = false;
|
|
3862
3738
|
if (rigPath) {
|
|
@@ -3904,15 +3780,15 @@ async function executeDist(context, args) {
|
|
|
3904
3780
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3905
3781
|
let buildDir = null;
|
|
3906
3782
|
if (!source) {
|
|
3907
|
-
buildDir =
|
|
3783
|
+
buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3908
3784
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3909
|
-
source =
|
|
3785
|
+
source = resolve12(buildDir, "bin", "rig");
|
|
3910
3786
|
}
|
|
3911
|
-
if (!
|
|
3787
|
+
if (!existsSync7(source)) {
|
|
3912
3788
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3913
3789
|
}
|
|
3914
|
-
const installedPath =
|
|
3915
|
-
if (
|
|
3790
|
+
const installedPath = resolve12(installDir, "rig");
|
|
3791
|
+
if (existsSync7(installedPath)) {
|
|
3916
3792
|
unlinkSync(installedPath);
|
|
3917
3793
|
}
|
|
3918
3794
|
copyFileSync2(source, installedPath);
|
|
@@ -3954,22 +3830,22 @@ async function executeDist(context, args) {
|
|
|
3954
3830
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3955
3831
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3956
3832
|
const currentId = computeRuntimeImageId(fp);
|
|
3957
|
-
const imagesDir =
|
|
3833
|
+
const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3958
3834
|
mkdirSync6(imagesDir, { recursive: true });
|
|
3959
3835
|
let pruned = 0;
|
|
3960
3836
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3961
3837
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3962
|
-
|
|
3838
|
+
rmSync3(resolve12(imagesDir, entry.name), { recursive: true, force: true });
|
|
3963
3839
|
pruned++;
|
|
3964
3840
|
}
|
|
3965
3841
|
}
|
|
3966
3842
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3967
3843
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3968
3844
|
}
|
|
3969
|
-
const imageDir =
|
|
3970
|
-
mkdirSync6(
|
|
3971
|
-
mkdirSync6(
|
|
3972
|
-
mkdirSync6(
|
|
3845
|
+
const imageDir = resolve12(imagesDir, currentId);
|
|
3846
|
+
mkdirSync6(resolve12(imageDir, "bin/hooks"), { recursive: true });
|
|
3847
|
+
mkdirSync6(resolve12(imageDir, "bin/plugins"), { recursive: true });
|
|
3848
|
+
mkdirSync6(resolve12(imageDir, "bin/validators"), { recursive: true });
|
|
3973
3849
|
const hookNames = [
|
|
3974
3850
|
"scope-guard",
|
|
3975
3851
|
"import-guard",
|
|
@@ -3983,25 +3859,25 @@ async function executeDist(context, args) {
|
|
|
3983
3859
|
];
|
|
3984
3860
|
const targets = [];
|
|
3985
3861
|
const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
|
|
3986
|
-
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest:
|
|
3987
|
-
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest:
|
|
3862
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3863
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3988
3864
|
for (const hookName of hookNames) {
|
|
3989
3865
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3990
|
-
targets.push({ source: src, dest:
|
|
3991
|
-
targets.push({ source: src, dest:
|
|
3866
|
+
targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3867
|
+
targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3992
3868
|
}
|
|
3993
|
-
const pluginsDir =
|
|
3994
|
-
const binPluginsDir =
|
|
3995
|
-
const validatorsRoot =
|
|
3996
|
-
const binValidatorsDir =
|
|
3869
|
+
const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
|
|
3870
|
+
const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3871
|
+
const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3872
|
+
const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3997
3873
|
mkdirSync6(binPluginsDir, { recursive: true });
|
|
3998
3874
|
mkdirSync6(binValidatorsDir, { recursive: true });
|
|
3999
|
-
if (
|
|
3875
|
+
if (existsSync7(pluginsDir)) {
|
|
4000
3876
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
4001
3877
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
4002
3878
|
if (!m)
|
|
4003
3879
|
continue;
|
|
4004
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3880
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
4005
3881
|
}
|
|
4006
3882
|
}
|
|
4007
3883
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -4012,17 +3888,17 @@ async function executeDist(context, args) {
|
|
|
4012
3888
|
const isValidator = dest.includes("/bin/validators/");
|
|
4013
3889
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
4014
3890
|
}
|
|
4015
|
-
if (
|
|
3891
|
+
if (existsSync7(pluginsDir)) {
|
|
4016
3892
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
4017
3893
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
4018
3894
|
if (!m)
|
|
4019
3895
|
continue;
|
|
4020
3896
|
const pluginName = m[1];
|
|
4021
|
-
const imageBin =
|
|
3897
|
+
const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
|
|
4022
3898
|
if (!pluginName)
|
|
4023
3899
|
continue;
|
|
4024
|
-
const symlinkPath =
|
|
4025
|
-
if (
|
|
3900
|
+
const symlinkPath = resolve12(binPluginsDir, pluginName);
|
|
3901
|
+
if (existsSync7(imageBin)) {
|
|
4026
3902
|
try {
|
|
4027
3903
|
unlinkSync(symlinkPath);
|
|
4028
3904
|
} catch {}
|
|
@@ -4030,10 +3906,10 @@ async function executeDist(context, args) {
|
|
|
4030
3906
|
}
|
|
4031
3907
|
}
|
|
4032
3908
|
}
|
|
4033
|
-
if (
|
|
3909
|
+
if (existsSync7(validatorsRoot)) {
|
|
4034
3910
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
4035
3911
|
for (const category of categories) {
|
|
4036
|
-
const categoryDir =
|
|
3912
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
4037
3913
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
4038
3914
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
4039
3915
|
continue;
|
|
@@ -4041,9 +3917,9 @@ async function executeDist(context, args) {
|
|
|
4041
3917
|
if (!check || check === "index" || check === "shared")
|
|
4042
3918
|
continue;
|
|
4043
3919
|
const validatorName = `${category.name}-${check}`;
|
|
4044
|
-
const imageBin =
|
|
4045
|
-
const symlinkPath =
|
|
4046
|
-
if (
|
|
3920
|
+
const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
|
|
3921
|
+
const symlinkPath = resolve12(binValidatorsDir, validatorName);
|
|
3922
|
+
if (existsSync7(imageBin)) {
|
|
4047
3923
|
try {
|
|
4048
3924
|
unlinkSync(symlinkPath);
|
|
4049
3925
|
} catch {}
|
|
@@ -4052,18 +3928,18 @@ async function executeDist(context, args) {
|
|
|
4052
3928
|
}
|
|
4053
3929
|
}
|
|
4054
3930
|
}
|
|
4055
|
-
const agentsDir =
|
|
4056
|
-
if (
|
|
3931
|
+
const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3932
|
+
if (existsSync7(agentsDir)) {
|
|
4057
3933
|
let relinkCount = 0;
|
|
4058
3934
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
4059
3935
|
if (!agentEntry.isDirectory())
|
|
4060
3936
|
continue;
|
|
4061
|
-
const agentBinDir =
|
|
4062
|
-
if (!
|
|
3937
|
+
const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
3938
|
+
if (!existsSync7(agentBinDir))
|
|
4063
3939
|
continue;
|
|
4064
3940
|
const walkDir = (dir) => {
|
|
4065
3941
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
4066
|
-
const fullPath =
|
|
3942
|
+
const fullPath = resolve12(dir, entry.name);
|
|
4067
3943
|
if (entry.isDirectory()) {
|
|
4068
3944
|
walkDir(fullPath);
|
|
4069
3945
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -4097,7 +3973,7 @@ async function executeDist(context, args) {
|
|
|
4097
3973
|
|
|
4098
3974
|
// packages/cli/src/commands/inbox.ts
|
|
4099
3975
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
4100
|
-
import { resolve as
|
|
3976
|
+
import { resolve as resolve13 } from "path";
|
|
4101
3977
|
import {
|
|
4102
3978
|
listAuthorityRuns,
|
|
4103
3979
|
readJsonlFile as readJsonlFile3,
|
|
@@ -4114,7 +3990,7 @@ async function executeInbox(context, args) {
|
|
|
4114
3990
|
pending = task.rest;
|
|
4115
3991
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
4116
3992
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
4117
|
-
const approvals = runs.flatMap((entry) => readJsonlFile3(
|
|
3993
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
4118
3994
|
runId: entry.runId,
|
|
4119
3995
|
record
|
|
4120
3996
|
})));
|
|
@@ -4142,7 +4018,7 @@ async function executeInbox(context, args) {
|
|
|
4142
4018
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4143
4019
|
throw new CliError2("decision must be approve or reject.");
|
|
4144
4020
|
}
|
|
4145
|
-
const approvalsPath =
|
|
4021
|
+
const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
4146
4022
|
const approvals = readJsonlFile3(approvalsPath);
|
|
4147
4023
|
const resolvedAt = new Date().toISOString();
|
|
4148
4024
|
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);
|
|
@@ -4159,7 +4035,7 @@ async function executeInbox(context, args) {
|
|
|
4159
4035
|
pending = task.rest;
|
|
4160
4036
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
4161
4037
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
4162
|
-
const requests = runs.flatMap((entry) => readJsonlFile3(
|
|
4038
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
4163
4039
|
runId: entry.runId,
|
|
4164
4040
|
record
|
|
4165
4041
|
})));
|
|
@@ -4201,7 +4077,7 @@ async function executeInbox(context, args) {
|
|
|
4201
4077
|
const [key, ...restValue] = entry.split("=");
|
|
4202
4078
|
return [key, restValue.join("=")];
|
|
4203
4079
|
}));
|
|
4204
|
-
const requestsPath =
|
|
4080
|
+
const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
4205
4081
|
const requests = readJsonlFile3(requestsPath);
|
|
4206
4082
|
const resolvedAt = new Date().toISOString();
|
|
4207
4083
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -4210,17 +4086,194 @@ async function executeInbox(context, args) {
|
|
|
4210
4086
|
`, "utf8");
|
|
4211
4087
|
return { ok: true, group: "inbox", command, details: { runId: run.value, requestId: request.value, answers: parsedAnswers } };
|
|
4212
4088
|
}
|
|
4213
|
-
default:
|
|
4214
|
-
throw new CliError2(`Unknown inbox command: ${command}`);
|
|
4215
|
-
}
|
|
4089
|
+
default:
|
|
4090
|
+
throw new CliError2(`Unknown inbox command: ${command}`);
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
// packages/cli/src/commands/init.ts
|
|
4095
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4096
|
+
import { spawnSync } from "child_process";
|
|
4097
|
+
import { resolve as resolve17 } from "path";
|
|
4098
|
+
import { buildRigInitConfigSource } from "@rig/core";
|
|
4099
|
+
import { listGitHubProjects as listGitHubProjectsDirect, resolveProjectStatusField as resolveProjectStatusFieldDirect } from "@rig/server";
|
|
4100
|
+
|
|
4101
|
+
// packages/cli/src/commands/_pi-install.ts
|
|
4102
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, rmSync as rmSync4 } from "fs";
|
|
4103
|
+
import { homedir as homedir4 } from "os";
|
|
4104
|
+
import { resolve as resolve14 } from "path";
|
|
4105
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
4106
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
4107
|
+
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
4108
|
+
export { default } from '@rig/pi-rig';
|
|
4109
|
+
`;
|
|
4110
|
+
async function defaultCommandRunner(command, options = {}) {
|
|
4111
|
+
const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
|
|
4112
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
4113
|
+
new Response(proc.stdout).text(),
|
|
4114
|
+
new Response(proc.stderr).text(),
|
|
4115
|
+
proc.exited
|
|
4116
|
+
]);
|
|
4117
|
+
return { exitCode, stdout, stderr };
|
|
4118
|
+
}
|
|
4119
|
+
function resolvePiRigExtensionPath(homeDir) {
|
|
4120
|
+
return resolve14(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
4121
|
+
}
|
|
4122
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync8) {
|
|
4123
|
+
const localPackage = resolve14(projectRoot, "packages", "pi-rig");
|
|
4124
|
+
if (exists(resolve14(localPackage, "package.json")))
|
|
4125
|
+
return localPackage;
|
|
4126
|
+
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
4127
|
+
}
|
|
4128
|
+
function resolvePiHomeDir(inputHomeDir) {
|
|
4129
|
+
return inputHomeDir ?? process.env.RIG_PI_HOME_DIR?.trim() ?? homedir4();
|
|
4130
|
+
}
|
|
4131
|
+
function piListContainsPiRig(output) {
|
|
4132
|
+
return output.split(/\r?\n/).some((line) => {
|
|
4133
|
+
const normalized = line.trim();
|
|
4134
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
4135
|
+
});
|
|
4136
|
+
}
|
|
4137
|
+
async function safeRun(runner, command, options) {
|
|
4138
|
+
try {
|
|
4139
|
+
return await runner(command, options);
|
|
4140
|
+
} catch (error) {
|
|
4141
|
+
return { exitCode: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
function splitInstallCommand(value) {
|
|
4145
|
+
return value.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((part) => part.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
4146
|
+
}
|
|
4147
|
+
async function ensurePiBinaryAvailable(input) {
|
|
4148
|
+
const current = await safeRun(input.runner, ["pi", "--version"]);
|
|
4149
|
+
if (current.exitCode === 0) {
|
|
4150
|
+
const updateCommand = process.env.RIG_PI_UPDATE_COMMAND?.trim();
|
|
4151
|
+
if (updateCommand) {
|
|
4152
|
+
const parts2 = splitInstallCommand(updateCommand);
|
|
4153
|
+
if (parts2.length > 0)
|
|
4154
|
+
await safeRun(input.runner, parts2, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
4155
|
+
}
|
|
4156
|
+
return { ok: true, detail: (current.stdout || current.stderr).trim() || undefined };
|
|
4157
|
+
}
|
|
4158
|
+
const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi@latest install";
|
|
4159
|
+
const parts = splitInstallCommand(installCommand);
|
|
4160
|
+
if (parts.length === 0) {
|
|
4161
|
+
return { ok: false, error: (current.stderr || current.stdout).trim() || "pi --version failed" };
|
|
4162
|
+
}
|
|
4163
|
+
const install = await safeRun(input.runner, parts, input.projectRoot ? { cwd: input.projectRoot } : undefined);
|
|
4164
|
+
if (install.exitCode !== 0) {
|
|
4165
|
+
return { ok: false, installedOrUpdated: true, error: (install.stderr || install.stdout).trim() || `Pi install command failed (${install.exitCode})` };
|
|
4166
|
+
}
|
|
4167
|
+
const next = await safeRun(input.runner, ["pi", "--version"]);
|
|
4168
|
+
return {
|
|
4169
|
+
ok: next.exitCode === 0,
|
|
4170
|
+
installedOrUpdated: true,
|
|
4171
|
+
detail: (next.stdout || next.stderr).trim() || undefined,
|
|
4172
|
+
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
4173
|
+
};
|
|
4174
|
+
}
|
|
4175
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync8) {
|
|
4176
|
+
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
4177
|
+
const indexPath = resolve14(extensionPath, "index.ts");
|
|
4178
|
+
if (!exists(indexPath))
|
|
4179
|
+
return;
|
|
4180
|
+
try {
|
|
4181
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
4182
|
+
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
4183
|
+
rmSync4(extensionPath, { recursive: true, force: true });
|
|
4184
|
+
}
|
|
4185
|
+
} catch {}
|
|
4186
|
+
}
|
|
4187
|
+
async function checkPiRigInstall(input = {}) {
|
|
4188
|
+
const home = resolvePiHomeDir(input.homeDir);
|
|
4189
|
+
const extensionPath = resolvePiRigExtensionPath(home);
|
|
4190
|
+
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
4191
|
+
return {
|
|
4192
|
+
extensionPath,
|
|
4193
|
+
pi: { ok: true, label: "pi", detail: "fake-pi" },
|
|
4194
|
+
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
const exists = input.exists ?? existsSync8;
|
|
4198
|
+
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
4199
|
+
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
4200
|
+
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
4201
|
+
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
4202
|
+
${piListResult.stderr}`);
|
|
4203
|
+
const legacyBridge = exists(resolve14(extensionPath, "index.ts"));
|
|
4204
|
+
const hasPiRig = listedPiRig;
|
|
4205
|
+
return {
|
|
4206
|
+
extensionPath,
|
|
4207
|
+
pi: {
|
|
4208
|
+
ok: piResult.exitCode === 0,
|
|
4209
|
+
label: "pi",
|
|
4210
|
+
detail: (piResult.stdout || piResult.stderr).trim() || undefined,
|
|
4211
|
+
hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
|
|
4212
|
+
},
|
|
4213
|
+
piRig: {
|
|
4214
|
+
ok: hasPiRig,
|
|
4215
|
+
label: "pi-rig global extension",
|
|
4216
|
+
detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
|
|
4217
|
+
hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
|
|
4218
|
+
}
|
|
4219
|
+
};
|
|
4220
|
+
}
|
|
4221
|
+
async function ensurePiRigInstalled(input) {
|
|
4222
|
+
const home = resolvePiHomeDir(input.homeDir);
|
|
4223
|
+
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
4224
|
+
const status2 = await checkPiRigInstall({ homeDir: home, commandRunner: input.commandRunner });
|
|
4225
|
+
return { ...status2, installedPath: status2.extensionPath };
|
|
4226
|
+
}
|
|
4227
|
+
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
4228
|
+
const piAvailable = await ensurePiBinaryAvailable({ runner, projectRoot: input.projectRoot });
|
|
4229
|
+
const status = piAvailable.ok ? await checkPiRigInstall({ homeDir: home, commandRunner: runner }) : {
|
|
4230
|
+
extensionPath: resolvePiRigExtensionPath(home),
|
|
4231
|
+
pi: { ok: false, label: "pi", detail: piAvailable.error, hint: "Install/update Pi with RIG_PI_INSTALL_COMMAND or install Pi manually." },
|
|
4232
|
+
piRig: { ok: false, label: "pi-rig global extension", hint: "Pi is required before pi-rig can be installed." }
|
|
4233
|
+
};
|
|
4234
|
+
if (!piAvailable.ok) {
|
|
4235
|
+
throw new Error(`Pi install/update failed: ${piAvailable.error ?? "pi unavailable"}`);
|
|
4236
|
+
}
|
|
4237
|
+
const packageSource = resolvePiRigPackageSource(input.projectRoot);
|
|
4238
|
+
removeManagedLegacyPiRigBridge(home);
|
|
4239
|
+
const install = await runner(["pi", "install", packageSource], { cwd: input.projectRoot });
|
|
4240
|
+
if (install.exitCode !== 0) {
|
|
4241
|
+
throw new Error(`pi-rig install failed: ${(install.stderr || install.stdout).trim() || `exit ${install.exitCode}`}`);
|
|
4242
|
+
}
|
|
4243
|
+
const next = await checkPiRigInstall({ homeDir: home, commandRunner: runner });
|
|
4244
|
+
return { ...next, installedPath: packageSource };
|
|
4245
|
+
}
|
|
4246
|
+
async function ensureRemotePiRigInstalled(input) {
|
|
4247
|
+
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
4248
|
+
method: "POST",
|
|
4249
|
+
headers: { "content-type": "application/json" },
|
|
4250
|
+
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
4251
|
+
});
|
|
4252
|
+
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4253
|
+
const piOk = record.piOk === true || record.ok === true;
|
|
4254
|
+
const piRigOk = record.piRigOk === true || record.installed === true || record.ok === true;
|
|
4255
|
+
const extensionPath = typeof record.extensionPath === "string" ? record.extensionPath : "remote:~/.pi/agent/extensions/pi-rig";
|
|
4256
|
+
return {
|
|
4257
|
+
remote: true,
|
|
4258
|
+
extensionPath,
|
|
4259
|
+
pi: {
|
|
4260
|
+
ok: piOk,
|
|
4261
|
+
label: "pi",
|
|
4262
|
+
detail: typeof record.piVersion === "string" ? record.piVersion : undefined,
|
|
4263
|
+
hint: piOk ? undefined : "Install/update Pi on the selected remote Rig server."
|
|
4264
|
+
},
|
|
4265
|
+
piRig: {
|
|
4266
|
+
ok: piRigOk,
|
|
4267
|
+
label: "pi-rig global extension",
|
|
4268
|
+
detail: extensionPath,
|
|
4269
|
+
hint: piRigOk ? undefined : "Install/enable pi-rig on the selected remote Rig server."
|
|
4270
|
+
}
|
|
4271
|
+
};
|
|
4272
|
+
}
|
|
4273
|
+
async function buildPiSetupChecks(input = {}) {
|
|
4274
|
+
const status = await checkPiRigInstall(input);
|
|
4275
|
+
return [status.pi, status.piRig];
|
|
4216
4276
|
}
|
|
4217
|
-
|
|
4218
|
-
// packages/cli/src/commands/init.ts
|
|
4219
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4220
|
-
import { spawnSync } from "child_process";
|
|
4221
|
-
import { resolve as resolve17 } from "path";
|
|
4222
|
-
import { buildRigInitConfigSource } from "@rig/core";
|
|
4223
|
-
import { listGitHubProjects as listGitHubProjectsDirect, resolveProjectStatusField as resolveProjectStatusFieldDirect } from "@rig/server";
|
|
4224
4277
|
|
|
4225
4278
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
4226
4279
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
@@ -6588,52 +6641,593 @@ async function promptForTaskSelection(question) {
|
|
|
6588
6641
|
}
|
|
6589
6642
|
}
|
|
6590
6643
|
|
|
6591
|
-
// packages/cli/src/commands/
|
|
6644
|
+
// packages/cli/src/commands/_pi-frontend.ts
|
|
6645
|
+
import { mkdtempSync, rmSync as rmSync5 } from "fs";
|
|
6646
|
+
import { tmpdir } from "os";
|
|
6647
|
+
import { join as join2 } from "path";
|
|
6648
|
+
import { main as runPiMain } from "@earendil-works/pi-coding-agent";
|
|
6649
|
+
|
|
6650
|
+
// packages/cli/src/commands/_pi-worker-bridge-extension.ts
|
|
6592
6651
|
var TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
6593
|
-
var
|
|
6594
|
-
|
|
6595
|
-
"
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
"
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
"
|
|
6603
|
-
|
|
6604
|
-
"Merge",
|
|
6605
|
-
"Complete"
|
|
6606
|
-
];
|
|
6607
|
-
var GREEN = "\x1B[32m";
|
|
6608
|
-
var BLUE = "\x1B[34m";
|
|
6609
|
-
var MAGENTA = "\x1B[35m";
|
|
6610
|
-
var YELLOW = "\x1B[33m";
|
|
6611
|
-
var RED = "\x1B[31m";
|
|
6612
|
-
var DIM = "\x1B[2m";
|
|
6613
|
-
var BOLD = "\x1B[1m";
|
|
6614
|
-
var RESET = "\x1B[0m";
|
|
6615
|
-
async function loadPiTuiRuntime() {
|
|
6652
|
+
var MAX_TRANSCRIPT_LINES = 120;
|
|
6653
|
+
function recordOf(value) {
|
|
6654
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6655
|
+
}
|
|
6656
|
+
function asText(value) {
|
|
6657
|
+
if (typeof value === "string")
|
|
6658
|
+
return value;
|
|
6659
|
+
if (value === null || value === undefined)
|
|
6660
|
+
return "";
|
|
6661
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
6662
|
+
return String(value);
|
|
6616
6663
|
try {
|
|
6617
|
-
return
|
|
6664
|
+
return JSON.stringify(value);
|
|
6618
6665
|
} catch {
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6666
|
+
return String(value);
|
|
6667
|
+
}
|
|
6668
|
+
}
|
|
6669
|
+
function textFromContent(content) {
|
|
6670
|
+
if (typeof content === "string")
|
|
6671
|
+
return content;
|
|
6672
|
+
if (!Array.isArray(content))
|
|
6673
|
+
return asText(content);
|
|
6674
|
+
return content.flatMap((part) => {
|
|
6675
|
+
const item = recordOf(part);
|
|
6676
|
+
if (!item)
|
|
6677
|
+
return [];
|
|
6678
|
+
if (typeof item.text === "string")
|
|
6679
|
+
return [item.text];
|
|
6680
|
+
if (typeof item.content === "string")
|
|
6681
|
+
return [item.content];
|
|
6682
|
+
if (item.type === "toolCall")
|
|
6683
|
+
return [`\u23FA ${String(item.name ?? "tool")} ${asText(item.arguments ?? "")}`.trim()];
|
|
6684
|
+
if (item.type === "toolResult")
|
|
6685
|
+
return [`\u21B3 ${asText(item.content ?? item.result ?? "")}`.trim()];
|
|
6686
|
+
return [];
|
|
6687
|
+
}).join(`
|
|
6688
|
+
`);
|
|
6689
|
+
}
|
|
6690
|
+
function appendTranscript(state, label, text2) {
|
|
6691
|
+
const trimmed = text2.trimEnd();
|
|
6692
|
+
if (!trimmed)
|
|
6693
|
+
return;
|
|
6694
|
+
const lines = trimmed.split(/\r?\n/);
|
|
6695
|
+
state.transcript.push(`${label}: ${lines[0] ?? ""}`);
|
|
6696
|
+
for (const line of lines.slice(1))
|
|
6697
|
+
state.transcript.push(` ${line}`);
|
|
6698
|
+
if (state.transcript.length > MAX_TRANSCRIPT_LINES) {
|
|
6699
|
+
state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
|
|
6700
|
+
}
|
|
6701
|
+
}
|
|
6702
|
+
function nativePiUi(ctx) {
|
|
6703
|
+
const ui = ctx.ui;
|
|
6704
|
+
return typeof ui.emitSessionEvent === "function" && typeof ui.appendSessionMessages === "function" ? ui : null;
|
|
6705
|
+
}
|
|
6706
|
+
function syncNativeDisplayCwd(ctx, state) {
|
|
6707
|
+
const ui = nativePiUi(ctx);
|
|
6708
|
+
if (ui?.setDisplayCwd && state.cwd)
|
|
6709
|
+
ui.setDisplayCwd(state.cwd);
|
|
6710
|
+
}
|
|
6711
|
+
function parseExtensionUiRequest(value) {
|
|
6712
|
+
const request = recordOf(value) ?? {};
|
|
6713
|
+
const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
|
|
6714
|
+
const method = String(request.method ?? request.type ?? "input");
|
|
6715
|
+
const prompt = asText(request.prompt ?? request.message ?? request.title ?? method);
|
|
6716
|
+
const rawOptions = Array.isArray(request.options) ? request.options : Array.isArray(request.items) ? request.items : [];
|
|
6717
|
+
const options = rawOptions.map((option) => {
|
|
6718
|
+
const record = recordOf(option);
|
|
6719
|
+
return record ? asText(record.label ?? record.value ?? record.name ?? option) : asText(option);
|
|
6720
|
+
}).filter(Boolean);
|
|
6721
|
+
return { requestId, method, prompt, options };
|
|
6722
|
+
}
|
|
6723
|
+
function renderBridgeWidget(state) {
|
|
6724
|
+
const statusParts = [
|
|
6725
|
+
state.wsConnected ? "live WS" : "WS pending",
|
|
6726
|
+
state.status,
|
|
6727
|
+
state.model,
|
|
6728
|
+
state.cwd
|
|
6729
|
+
].filter(Boolean);
|
|
6730
|
+
const lines = [`Worker Pi daemon bridge \xB7 ${statusParts.join(" \xB7 ")}`];
|
|
6731
|
+
if (state.activity)
|
|
6732
|
+
lines.push(state.activity);
|
|
6733
|
+
if (state.commands.length > 0) {
|
|
6734
|
+
lines.push(`Worker commands: ${state.commands.slice(0, 10).join(", ")}${state.commands.length > 10 ? ", \u2026" : ""}`);
|
|
6735
|
+
}
|
|
6736
|
+
lines.push("");
|
|
6737
|
+
if (state.transcript.length > 0) {
|
|
6738
|
+
lines.push(...state.transcript.slice(-MAX_TRANSCRIPT_LINES));
|
|
6739
|
+
} else {
|
|
6740
|
+
lines.push("Waiting for worker Pi daemon transcript\u2026");
|
|
6741
|
+
}
|
|
6742
|
+
if (state.pendingUi) {
|
|
6743
|
+
lines.push("");
|
|
6744
|
+
lines.push(`Extension UI request \xB7 ${state.pendingUi.method}`);
|
|
6745
|
+
lines.push(state.pendingUi.prompt);
|
|
6746
|
+
state.pendingUi.options.forEach((option, index) => lines.push(`${index + 1}. ${option}`));
|
|
6747
|
+
lines.push("Reply in the Pi editor. /cancel cancels this request.");
|
|
6748
|
+
}
|
|
6749
|
+
return lines;
|
|
6750
|
+
}
|
|
6751
|
+
function updatePiUi(ctx, state) {
|
|
6752
|
+
ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
|
|
6753
|
+
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
|
|
6754
|
+
syncNativeDisplayCwd(ctx, state);
|
|
6755
|
+
if (state.nativeStream && nativePiUi(ctx)) {
|
|
6756
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
6757
|
+
return;
|
|
6758
|
+
}
|
|
6759
|
+
ctx.ui.setWorkingVisible(false);
|
|
6760
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", [`Worker Pi daemon bridge \xB7 degraded widget transcript`, ...renderBridgeWidget(state)], { placement: "aboveEditor" });
|
|
6761
|
+
}
|
|
6762
|
+
function applyStatus(state, payload) {
|
|
6763
|
+
const status = recordOf(payload.status) ?? payload;
|
|
6764
|
+
state.streaming = status.isStreaming === true || status.isCompacting === true || status.isBashRunning === true;
|
|
6765
|
+
state.cwd = typeof status.cwd === "string" ? status.cwd : state.cwd;
|
|
6766
|
+
state.model = typeof status.model === "string" ? status.model : state.model;
|
|
6767
|
+
const pending = typeof status.pendingMessageCount === "number" ? status.pendingMessageCount : 0;
|
|
6768
|
+
state.status = `${state.streaming ? "streaming" : "idle"}${pending ? ` \xB7 ${pending} queued` : ""}`;
|
|
6769
|
+
}
|
|
6770
|
+
function applyMessage(state, message2) {
|
|
6771
|
+
const record = recordOf(message2);
|
|
6772
|
+
if (!record)
|
|
6773
|
+
return;
|
|
6774
|
+
const role = String(record.role ?? "system");
|
|
6775
|
+
const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
|
|
6776
|
+
appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
|
|
6777
|
+
}
|
|
6778
|
+
function applyPiEvent(ctx, state, eventValue) {
|
|
6779
|
+
const event = recordOf(eventValue);
|
|
6780
|
+
if (!event)
|
|
6781
|
+
return;
|
|
6782
|
+
const type = String(event.type ?? "event");
|
|
6783
|
+
if (type === "agent_start") {
|
|
6784
|
+
state.streaming = true;
|
|
6785
|
+
state.status = "streaming";
|
|
6786
|
+
} else if (type === "agent_end") {
|
|
6787
|
+
state.streaming = false;
|
|
6788
|
+
state.status = "idle";
|
|
6789
|
+
} else if (type === "queue_update") {
|
|
6790
|
+
const steering = Array.isArray(event.steering) ? event.steering.length : 0;
|
|
6791
|
+
const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
|
|
6792
|
+
state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
|
|
6793
|
+
}
|
|
6794
|
+
const native = nativePiUi(ctx);
|
|
6795
|
+
if (state.nativeStream && native?.emitSessionEvent) {
|
|
6796
|
+
native.emitSessionEvent(eventValue);
|
|
6797
|
+
return;
|
|
6798
|
+
}
|
|
6799
|
+
if (type === "agent_end") {
|
|
6800
|
+
appendTranscript(state, "System", "Agent turn complete.");
|
|
6801
|
+
return;
|
|
6802
|
+
}
|
|
6803
|
+
if (type === "message_start" || type === "message_end" || type === "turn_end") {
|
|
6804
|
+
applyMessage(state, event.message);
|
|
6805
|
+
return;
|
|
6806
|
+
}
|
|
6807
|
+
if (type === "message_update") {
|
|
6808
|
+
const assistantEvent = recordOf(event.assistantMessageEvent);
|
|
6809
|
+
const delta = typeof assistantEvent?.delta === "string" ? assistantEvent.delta : typeof assistantEvent?.text === "string" ? assistantEvent.text : "";
|
|
6810
|
+
if (delta)
|
|
6811
|
+
appendTranscript(state, assistantEvent?.type === "thinking_delta" ? "Thinking" : "Pi", delta);
|
|
6812
|
+
return;
|
|
6813
|
+
}
|
|
6814
|
+
if (type === "tool_execution_start") {
|
|
6815
|
+
appendTranscript(state, "Tool", `${String(event.toolName ?? "tool")} ${asText(event.args ?? "")}`.trim());
|
|
6816
|
+
return;
|
|
6817
|
+
}
|
|
6818
|
+
if (type === "tool_execution_update") {
|
|
6819
|
+
appendTranscript(state, "Tool", asText(event.partialResult ?? ""));
|
|
6820
|
+
return;
|
|
6821
|
+
}
|
|
6822
|
+
if (type === "tool_execution_end") {
|
|
6823
|
+
appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
|
|
6824
|
+
}
|
|
6825
|
+
}
|
|
6826
|
+
function firstPendingShell(state) {
|
|
6827
|
+
return state.pendingShells[0];
|
|
6828
|
+
}
|
|
6829
|
+
function finishPendingShell(state, shell, result) {
|
|
6830
|
+
const index = state.pendingShells.indexOf(shell);
|
|
6831
|
+
if (index !== -1)
|
|
6832
|
+
state.pendingShells.splice(index, 1);
|
|
6833
|
+
shell.resolve(result);
|
|
6834
|
+
}
|
|
6835
|
+
function failPendingShell(state, shell, error) {
|
|
6836
|
+
const index = state.pendingShells.indexOf(shell);
|
|
6837
|
+
if (index !== -1)
|
|
6838
|
+
state.pendingShells.splice(index, 1);
|
|
6839
|
+
shell.reject(error);
|
|
6840
|
+
}
|
|
6841
|
+
function applyUiEvent(state, value) {
|
|
6842
|
+
const event = recordOf(value);
|
|
6843
|
+
if (!event)
|
|
6844
|
+
return;
|
|
6845
|
+
const type = String(event.type ?? "ui");
|
|
6846
|
+
if (type === "shell.chunk") {
|
|
6847
|
+
const pending = firstPendingShell(state);
|
|
6848
|
+
const chunk = asText(event.chunk);
|
|
6849
|
+
if (pending) {
|
|
6850
|
+
pending.sawChunk = true;
|
|
6851
|
+
pending.onData(Buffer.from(chunk));
|
|
6852
|
+
} else {
|
|
6853
|
+
appendTranscript(state, "Tool", chunk);
|
|
6854
|
+
}
|
|
6855
|
+
return;
|
|
6856
|
+
}
|
|
6857
|
+
if (type === "shell.end") {
|
|
6858
|
+
const pending = firstPendingShell(state);
|
|
6859
|
+
const output = asText(event.output ?? "");
|
|
6860
|
+
const exitCode = typeof event.exitCode === "number" ? event.exitCode : event.isError === true ? 1 : 0;
|
|
6861
|
+
if (pending) {
|
|
6862
|
+
if (output && !pending.sawChunk)
|
|
6863
|
+
pending.onData(Buffer.from(output));
|
|
6864
|
+
finishPendingShell(state, pending, { exitCode });
|
|
6865
|
+
} else {
|
|
6866
|
+
appendTranscript(state, event.isError === true ? "Error" : "Tool", output || `exit ${String(exitCode)}`);
|
|
6867
|
+
}
|
|
6868
|
+
return;
|
|
6869
|
+
}
|
|
6870
|
+
if (type === "shell.start") {
|
|
6871
|
+
if (!firstPendingShell(state))
|
|
6872
|
+
appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
|
|
6873
|
+
return;
|
|
6874
|
+
}
|
|
6875
|
+
appendTranscript(state, "System", `${type}: ${asText(event)}`);
|
|
6876
|
+
}
|
|
6877
|
+
function applyEnvelope(ctx, state, envelopeValue) {
|
|
6878
|
+
const envelope = recordOf(envelopeValue);
|
|
6879
|
+
if (!envelope)
|
|
6880
|
+
return;
|
|
6881
|
+
const type = String(envelope.type ?? "");
|
|
6882
|
+
if (type === "ready") {
|
|
6883
|
+
const metadata = recordOf(envelope.metadata);
|
|
6884
|
+
state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
|
|
6885
|
+
state.status = "worker Pi daemon ready";
|
|
6886
|
+
if (!state.nativeStream)
|
|
6887
|
+
appendTranscript(state, "System", "Connected to worker Pi daemon.");
|
|
6888
|
+
} else if (type === "status.update") {
|
|
6889
|
+
applyStatus(state, envelope);
|
|
6890
|
+
} else if (type === "activity.update") {
|
|
6891
|
+
const activity = recordOf(envelope.activity);
|
|
6892
|
+
state.activity = [activity?.label, activity?.detail].map(asText).filter(Boolean).join(" \u2014 ");
|
|
6893
|
+
} else if (type === "extension_ui_request") {
|
|
6894
|
+
state.pendingUi = parseExtensionUiRequest(envelope.request);
|
|
6895
|
+
appendTranscript(state, "System", `Extension UI request: ${state.pendingUi.prompt}`);
|
|
6896
|
+
} else if (type === "pi.ui_event") {
|
|
6897
|
+
applyUiEvent(state, envelope.event);
|
|
6898
|
+
} else if (type === "pi.event") {
|
|
6899
|
+
applyPiEvent(ctx, state, envelope.event);
|
|
6900
|
+
} else if (type === "error") {
|
|
6901
|
+
appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
|
|
6902
|
+
}
|
|
6903
|
+
syncNativeDisplayCwd(ctx, state);
|
|
6904
|
+
}
|
|
6905
|
+
async function waitForWorkerReady(options, ctx, state) {
|
|
6906
|
+
while (true) {
|
|
6907
|
+
const session = await getRunPiSessionViaServer(options.context, options.runId).catch((error) => ({
|
|
6908
|
+
ready: false,
|
|
6909
|
+
status: error instanceof Error ? error.message : String(error),
|
|
6910
|
+
retryAfterMs: 1000
|
|
6911
|
+
}));
|
|
6912
|
+
if (session.ready === false) {
|
|
6913
|
+
const status = String(session.status ?? "starting");
|
|
6914
|
+
state.status = `waiting for worker Pi daemon \xB7 ${status}`;
|
|
6915
|
+
updatePiUi(ctx, state);
|
|
6916
|
+
if (TERMINAL_RUN_STATUSES.has(status.toLowerCase())) {
|
|
6917
|
+
appendTranscript(state, "Error", `Run ended before worker Pi daemon became ready: ${status}`);
|
|
6918
|
+
return false;
|
|
6919
|
+
}
|
|
6920
|
+
await Bun.sleep(typeof session.retryAfterMs === "number" ? session.retryAfterMs : 750);
|
|
6921
|
+
continue;
|
|
6922
|
+
}
|
|
6923
|
+
const sessionRecord = recordOf(session) ?? {};
|
|
6924
|
+
applyEnvelope(ctx, state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
|
|
6925
|
+
updatePiUi(ctx, state);
|
|
6926
|
+
return true;
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6929
|
+
function parseWsPayload(message2) {
|
|
6930
|
+
if (typeof message2.data === "string")
|
|
6931
|
+
return JSON.parse(message2.data);
|
|
6932
|
+
return JSON.parse(Buffer.from(message2.data).toString("utf8"));
|
|
6933
|
+
}
|
|
6934
|
+
async function connectWorkerStream(options, ctx, state) {
|
|
6935
|
+
const ready = await waitForWorkerReady(options, ctx, state);
|
|
6936
|
+
if (!ready)
|
|
6937
|
+
return;
|
|
6938
|
+
let catchupDone = false;
|
|
6939
|
+
const buffered = [];
|
|
6940
|
+
const wsUrl = await buildRunPiEventsWebSocketUrl(options.context, options.runId);
|
|
6941
|
+
const socket = new WebSocket(wsUrl);
|
|
6942
|
+
const closePromise = new Promise((resolve20) => {
|
|
6943
|
+
socket.onopen = () => {
|
|
6944
|
+
state.wsConnected = true;
|
|
6945
|
+
state.status = "live worker Pi WebSocket connected";
|
|
6946
|
+
updatePiUi(ctx, state);
|
|
6947
|
+
};
|
|
6948
|
+
socket.onmessage = (message2) => {
|
|
6949
|
+
try {
|
|
6950
|
+
const payload = parseWsPayload(message2);
|
|
6951
|
+
if (!catchupDone)
|
|
6952
|
+
buffered.push(payload);
|
|
6953
|
+
else {
|
|
6954
|
+
applyEnvelope(ctx, state, payload);
|
|
6955
|
+
updatePiUi(ctx, state);
|
|
6956
|
+
}
|
|
6957
|
+
} catch (error) {
|
|
6958
|
+
appendTranscript(state, "Error", `Unparseable worker Pi event: ${error instanceof Error ? error.message : String(error)}`);
|
|
6959
|
+
updatePiUi(ctx, state);
|
|
6960
|
+
}
|
|
6961
|
+
};
|
|
6962
|
+
socket.onerror = () => socket.close();
|
|
6963
|
+
socket.onclose = () => {
|
|
6964
|
+
state.wsConnected = false;
|
|
6965
|
+
state.status = "worker Pi WebSocket disconnected";
|
|
6966
|
+
updatePiUi(ctx, state);
|
|
6967
|
+
resolve20();
|
|
6968
|
+
};
|
|
6969
|
+
});
|
|
6970
|
+
try {
|
|
6971
|
+
const [messagesPayload, statusPayload, commandsPayload] = await Promise.all([
|
|
6972
|
+
getRunPiMessagesViaServer(options.context, options.runId),
|
|
6973
|
+
getRunPiStatusViaServer(options.context, options.runId),
|
|
6974
|
+
getRunPiCommandsViaServer(options.context, options.runId)
|
|
6626
6975
|
]);
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6976
|
+
const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
|
|
6977
|
+
const native = nativePiUi(ctx);
|
|
6978
|
+
if (state.nativeStream && native?.appendSessionMessages)
|
|
6979
|
+
native.appendSessionMessages(messages);
|
|
6980
|
+
else
|
|
6981
|
+
for (const message2 of messages)
|
|
6982
|
+
applyMessage(state, message2);
|
|
6983
|
+
applyStatus(state, statusPayload);
|
|
6984
|
+
const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
|
|
6985
|
+
state.commands = commands.flatMap((command) => {
|
|
6986
|
+
const record = recordOf(command);
|
|
6987
|
+
return typeof record?.name === "string" ? [`/${record.name}`] : [];
|
|
6988
|
+
});
|
|
6989
|
+
catchupDone = true;
|
|
6990
|
+
for (const payload of buffered.splice(0))
|
|
6991
|
+
applyEnvelope(ctx, state, payload);
|
|
6992
|
+
updatePiUi(ctx, state);
|
|
6993
|
+
} catch (error) {
|
|
6994
|
+
appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
6995
|
+
catchupDone = true;
|
|
6996
|
+
updatePiUi(ctx, state);
|
|
6997
|
+
}
|
|
6998
|
+
await closePromise;
|
|
6999
|
+
}
|
|
7000
|
+
function createRemoteBashOperations(options, state, excludeFromContext) {
|
|
7001
|
+
return {
|
|
7002
|
+
exec(command, _cwd, execOptions) {
|
|
7003
|
+
return new Promise((resolve20, reject) => {
|
|
7004
|
+
const pending = {
|
|
7005
|
+
command,
|
|
7006
|
+
onData: execOptions.onData,
|
|
7007
|
+
resolve: resolve20,
|
|
7008
|
+
reject,
|
|
7009
|
+
sawChunk: false
|
|
7010
|
+
};
|
|
7011
|
+
const cleanup = () => {
|
|
7012
|
+
execOptions.signal?.removeEventListener("abort", onAbort);
|
|
7013
|
+
if (timer)
|
|
7014
|
+
clearTimeout(timer);
|
|
7015
|
+
};
|
|
7016
|
+
const onAbort = () => {
|
|
7017
|
+
cleanup();
|
|
7018
|
+
failPendingShell(state, pending, new Error("Remote worker shell command aborted locally."));
|
|
7019
|
+
};
|
|
7020
|
+
const timeoutMs = typeof execOptions.timeout === "number" && execOptions.timeout > 0 ? execOptions.timeout : 0;
|
|
7021
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
7022
|
+
cleanup();
|
|
7023
|
+
failPendingShell(state, pending, new Error(`Remote worker shell command timed out after ${timeoutMs}ms.`));
|
|
7024
|
+
}, timeoutMs) : null;
|
|
7025
|
+
const wrappedResolve = pending.resolve;
|
|
7026
|
+
const wrappedReject = pending.reject;
|
|
7027
|
+
pending.resolve = (result) => {
|
|
7028
|
+
cleanup();
|
|
7029
|
+
wrappedResolve(result);
|
|
7030
|
+
};
|
|
7031
|
+
pending.reject = (error) => {
|
|
7032
|
+
cleanup();
|
|
7033
|
+
wrappedReject(error);
|
|
7034
|
+
};
|
|
7035
|
+
execOptions.signal?.addEventListener("abort", onAbort, { once: true });
|
|
7036
|
+
state.pendingShells.push(pending);
|
|
7037
|
+
sendRunPiShellViaServer(options.context, options.runId, `${excludeFromContext ? "!!" : "!"}${command}`).catch((error) => {
|
|
7038
|
+
cleanup();
|
|
7039
|
+
failPendingShell(state, pending, error instanceof Error ? error : new Error(String(error)));
|
|
7040
|
+
});
|
|
7041
|
+
});
|
|
7042
|
+
}
|
|
7043
|
+
};
|
|
7044
|
+
}
|
|
7045
|
+
async function answerPendingUi(options, state, line) {
|
|
7046
|
+
const pending = state.pendingUi;
|
|
7047
|
+
if (!pending)
|
|
7048
|
+
return false;
|
|
7049
|
+
if (line === "/cancel") {
|
|
7050
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { cancelled: true });
|
|
7051
|
+
} else if (pending.method === "confirm") {
|
|
7052
|
+
const confirmed = /^(y|yes|true|1)$/i.test(line);
|
|
7053
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: confirmed, confirmed });
|
|
7054
|
+
} else if (pending.options.length > 0 && /^\d+$/.test(line)) {
|
|
7055
|
+
const selected = pending.options[Math.max(0, Number(line) - 1)] ?? line;
|
|
7056
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: selected });
|
|
7057
|
+
} else {
|
|
7058
|
+
await respondRunPiExtensionUiViaServer(options.context, options.runId, pending.requestId, { value: line });
|
|
7059
|
+
}
|
|
7060
|
+
appendTranscript(state, "System", `Responded to extension UI request ${pending.requestId}.`);
|
|
7061
|
+
state.pendingUi = null;
|
|
7062
|
+
return true;
|
|
7063
|
+
}
|
|
7064
|
+
async function routeInput(options, ctx, state, line) {
|
|
7065
|
+
const text2 = line.trim();
|
|
7066
|
+
if (!text2)
|
|
7067
|
+
return;
|
|
7068
|
+
if (await answerPendingUi(options, state, text2)) {
|
|
7069
|
+
updatePiUi(ctx, state);
|
|
7070
|
+
return;
|
|
7071
|
+
}
|
|
7072
|
+
if (text2 === "/detach" || text2 === "/quit" || text2 === "/q") {
|
|
7073
|
+
appendTranscript(state, "System", "Detached locally; worker Pi daemon continues.");
|
|
7074
|
+
updatePiUi(ctx, state);
|
|
7075
|
+
ctx.shutdown();
|
|
7076
|
+
return;
|
|
7077
|
+
}
|
|
7078
|
+
if (text2 === "/stop") {
|
|
7079
|
+
await abortRunPiViaServer(options.context, options.runId);
|
|
7080
|
+
appendTranscript(state, "System", "Stop requested for worker Pi daemon.");
|
|
7081
|
+
updatePiUi(ctx, state);
|
|
7082
|
+
ctx.shutdown();
|
|
7083
|
+
return;
|
|
7084
|
+
}
|
|
7085
|
+
if (text2.startsWith("!")) {
|
|
7086
|
+
appendTranscript(state, "You", text2);
|
|
7087
|
+
await sendRunPiShellViaServer(options.context, options.runId, text2);
|
|
7088
|
+
} else if (text2.startsWith("/")) {
|
|
7089
|
+
appendTranscript(state, "You", text2);
|
|
7090
|
+
const result = await runRunPiCommandViaServer(options.context, options.runId, text2);
|
|
7091
|
+
const message2 = typeof result.message === "string" ? result.message : "worker command accepted";
|
|
7092
|
+
appendTranscript(state, "System", message2);
|
|
7093
|
+
} else {
|
|
7094
|
+
appendTranscript(state, "You", text2);
|
|
7095
|
+
await sendRunPiPromptViaServer(options.context, options.runId, text2, state.streaming ? "steer" : undefined);
|
|
7096
|
+
}
|
|
7097
|
+
updatePiUi(ctx, state);
|
|
7098
|
+
}
|
|
7099
|
+
function createRigWorkerPiBridgeExtension(options) {
|
|
7100
|
+
return (pi) => {
|
|
7101
|
+
const state = {
|
|
7102
|
+
transcript: [],
|
|
7103
|
+
status: "starting worker Pi daemon bridge",
|
|
7104
|
+
activity: "",
|
|
7105
|
+
cwd: "",
|
|
7106
|
+
model: "",
|
|
7107
|
+
commands: [],
|
|
7108
|
+
streaming: false,
|
|
7109
|
+
pendingUi: null,
|
|
7110
|
+
pendingShells: [],
|
|
7111
|
+
wsConnected: false,
|
|
7112
|
+
nativeStream: false
|
|
6634
7113
|
};
|
|
7114
|
+
if (options.initialMessageSent)
|
|
7115
|
+
appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
|
|
7116
|
+
let nativePiUiContextAvailable = false;
|
|
7117
|
+
pi.on("user_bash", (event) => {
|
|
7118
|
+
state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
|
|
7119
|
+
return { operations: createRemoteBashOperations(options, state, event.excludeFromContext === true) };
|
|
7120
|
+
});
|
|
7121
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
7122
|
+
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
7123
|
+
state.nativeStream = nativePiUiContextAvailable;
|
|
7124
|
+
updatePiUi(ctx, state);
|
|
7125
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker Pi native stream bridge loaded" : "Rig worker Pi bridge extension loaded (degraded widget fallback)", "info");
|
|
7126
|
+
ctx.ui.onTerminalInput((data) => {
|
|
7127
|
+
if (data.includes("\x04")) {
|
|
7128
|
+
ctx.shutdown();
|
|
7129
|
+
return { consume: true };
|
|
7130
|
+
}
|
|
7131
|
+
if (!data.includes("\r") && !data.includes(`
|
|
7132
|
+
`))
|
|
7133
|
+
return;
|
|
7134
|
+
const inlineText = data.replace(/[\r\n]+/g, "").trim();
|
|
7135
|
+
const editorText = ctx.ui.getEditorText().trim();
|
|
7136
|
+
const text2 = [editorText, inlineText].filter(Boolean).join(" ").trim();
|
|
7137
|
+
if (!text2)
|
|
7138
|
+
return;
|
|
7139
|
+
if (text2.startsWith("!"))
|
|
7140
|
+
return;
|
|
7141
|
+
ctx.ui.setEditorText("");
|
|
7142
|
+
routeInput(options, ctx, state, text2).catch((error) => {
|
|
7143
|
+
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
7144
|
+
updatePiUi(ctx, state);
|
|
7145
|
+
});
|
|
7146
|
+
return { consume: true };
|
|
7147
|
+
});
|
|
7148
|
+
connectWorkerStream(options, ctx, state).catch((error) => {
|
|
7149
|
+
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
7150
|
+
updatePiUi(ctx, state);
|
|
7151
|
+
});
|
|
7152
|
+
});
|
|
7153
|
+
pi.on("session_shutdown", () => {});
|
|
7154
|
+
};
|
|
7155
|
+
}
|
|
7156
|
+
|
|
7157
|
+
// packages/cli/src/commands/_pi-frontend.ts
|
|
7158
|
+
function setTemporaryEnv(updates) {
|
|
7159
|
+
const previous = new Map;
|
|
7160
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
7161
|
+
previous.set(key, process.env[key]);
|
|
7162
|
+
process.env[key] = value;
|
|
7163
|
+
}
|
|
7164
|
+
return () => {
|
|
7165
|
+
for (const [key, value] of previous) {
|
|
7166
|
+
if (value === undefined)
|
|
7167
|
+
delete process.env[key];
|
|
7168
|
+
else
|
|
7169
|
+
process.env[key] = value;
|
|
7170
|
+
}
|
|
7171
|
+
};
|
|
7172
|
+
}
|
|
7173
|
+
async function attachRunBundledPiFrontend(context, input) {
|
|
7174
|
+
const tempRoot = mkdtempSync(join2(tmpdir(), "rig-pi-frontend-"));
|
|
7175
|
+
const cwd = join2(tempRoot, "workspace");
|
|
7176
|
+
const agentDir = join2(tempRoot, "agent");
|
|
7177
|
+
const sessionDir = join2(tempRoot, "sessions");
|
|
7178
|
+
const previousCwd = process.cwd();
|
|
7179
|
+
const restoreEnv = setTemporaryEnv({
|
|
7180
|
+
PI_CODING_AGENT_DIR: agentDir,
|
|
7181
|
+
PI_CODING_AGENT_SESSION_DIR: sessionDir,
|
|
7182
|
+
PI_OFFLINE: "1",
|
|
7183
|
+
PI_SKIP_VERSION_CHECK: "1"
|
|
7184
|
+
});
|
|
7185
|
+
let detached = false;
|
|
7186
|
+
try {
|
|
7187
|
+
await Bun.$`mkdir -p ${cwd} ${agentDir} ${sessionDir}`.quiet();
|
|
7188
|
+
process.chdir(cwd);
|
|
7189
|
+
await runPiMain([
|
|
7190
|
+
"--offline",
|
|
7191
|
+
"--no-session",
|
|
7192
|
+
"--no-tools",
|
|
7193
|
+
"--no-builtin-tools",
|
|
7194
|
+
"--no-skills",
|
|
7195
|
+
"--no-prompt-templates",
|
|
7196
|
+
"--no-themes",
|
|
7197
|
+
"--no-context-files",
|
|
7198
|
+
"--no-approve"
|
|
7199
|
+
], {
|
|
7200
|
+
extensionFactories: [
|
|
7201
|
+
createRigWorkerPiBridgeExtension({
|
|
7202
|
+
context,
|
|
7203
|
+
runId: input.runId,
|
|
7204
|
+
initialMessageSent: input.steered === true
|
|
7205
|
+
})
|
|
7206
|
+
]
|
|
7207
|
+
});
|
|
7208
|
+
detached = true;
|
|
7209
|
+
} finally {
|
|
7210
|
+
process.chdir(previousCwd);
|
|
7211
|
+
restoreEnv();
|
|
7212
|
+
rmSync5(tempRoot, { recursive: true, force: true });
|
|
6635
7213
|
}
|
|
7214
|
+
let run = { runId: input.runId, status: "unknown" };
|
|
7215
|
+
try {
|
|
7216
|
+
run = await getRunDetailsViaServer(context, input.runId);
|
|
7217
|
+
} catch {}
|
|
7218
|
+
return {
|
|
7219
|
+
run,
|
|
7220
|
+
logs: [],
|
|
7221
|
+
timeline: [],
|
|
7222
|
+
timelineCursor: null,
|
|
7223
|
+
steered: input.steered === true,
|
|
7224
|
+
detached,
|
|
7225
|
+
rendered: "actual bundled Pi frontend hosted Rig worker Pi bridge extension"
|
|
7226
|
+
};
|
|
6636
7227
|
}
|
|
7228
|
+
|
|
7229
|
+
// packages/cli/src/commands/_operator-view.ts
|
|
7230
|
+
var TERMINAL_RUN_STATUSES2 = new Set(["completed", "failed", "stopped", "cancelled", "canceled", "closed", "merged", "needs_attention", "needs-attention"]);
|
|
6637
7231
|
function runStatusFromPayload(payload) {
|
|
6638
7232
|
const run = payload.run && typeof payload.run === "object" && !Array.isArray(payload.run) ? payload.run : payload;
|
|
6639
7233
|
return String(run.status ?? "unknown").toLowerCase();
|
|
@@ -6672,198 +7266,15 @@ async function readOperatorSnapshot(context, runId, options = {}) {
|
|
|
6672
7266
|
const timelineCursor = typeof timelinePage.nextCursor === "string" ? timelinePage.nextCursor : options.timelineCursor ?? null;
|
|
6673
7267
|
return { run, logs, timeline, timelineCursor, rendered: renderOperatorSnapshot({ run, logs, timeline }) };
|
|
6674
7268
|
}
|
|
6675
|
-
function unwrapRun(runPayload) {
|
|
6676
|
-
return runPayload.run && typeof runPayload.run === "object" && !Array.isArray(runPayload.run) ? runPayload.run : runPayload;
|
|
6677
|
-
}
|
|
6678
|
-
function logDetail2(log3) {
|
|
6679
|
-
return typeof log3.detail === "string" ? log3.detail.trim() : "";
|
|
6680
|
-
}
|
|
6681
|
-
function logTitle(log3) {
|
|
6682
|
-
return typeof log3.title === "string" ? log3.title.trim() : "";
|
|
6683
|
-
}
|
|
6684
|
-
function renderAssistantTextFromTimeline(entries) {
|
|
6685
|
-
const assistant = entries.filter((entry) => entry.type === "assistant_message" && typeof entry.text === "string").at(-1);
|
|
6686
|
-
const text2 = typeof assistant?.text === "string" ? assistant.text.trimEnd() : "";
|
|
6687
|
-
if (!text2)
|
|
6688
|
-
return [];
|
|
6689
|
-
return [`${BLUE}${BOLD}Remote Pi assistant${RESET}`, ...text2.split(/\r?\n/).slice(-18)];
|
|
6690
|
-
}
|
|
6691
|
-
function renderToolLines(entries) {
|
|
6692
|
-
return entries.filter((entry) => entry.type === "tool_execution_start" || entry.type === "tool_execution_update" || entry.type === "tool_execution_end" || entry.type === "mcp_tool_call").slice(-8).map((entry) => `${DIM}[tool]${RESET} ${String(entry.toolName ?? entry.name ?? entry.title ?? entry.type)} ${String(entry.status ?? entry.state ?? "")}`.trim());
|
|
6693
|
-
}
|
|
6694
|
-
function renderStageLines(logs) {
|
|
6695
|
-
const latestByStage = new Map;
|
|
6696
|
-
for (const log3 of logs) {
|
|
6697
|
-
const title = logTitle(log3).toLowerCase();
|
|
6698
|
-
const stageName = String(log3.stage ?? "").toLowerCase();
|
|
6699
|
-
const stage = CANONICAL_STAGES2.find((candidate) => candidate.toLowerCase() === title || candidate.toLowerCase() === stageName);
|
|
6700
|
-
if (stage)
|
|
6701
|
-
latestByStage.set(stage, log3);
|
|
6702
|
-
}
|
|
6703
|
-
return CANONICAL_STAGES2.map((stage) => {
|
|
6704
|
-
const log3 = latestByStage.get(stage);
|
|
6705
|
-
const status = String(log3?.status ?? "pending");
|
|
6706
|
-
const detail = log3 ? logDetail2(log3) : "";
|
|
6707
|
-
const color = status === "completed" ? GREEN : status === "failed" || status === "rejected" ? RED : status === "pending" ? DIM : YELLOW;
|
|
6708
|
-
const mark = status === "completed" ? "\u2713" : status === "pending" ? "\xB7" : status === "failed" ? "\u2717" : "\u25B6";
|
|
6709
|
-
return `${color}${mark} ${stage}${RESET}${detail ? ` ${DIM}\u2014 ${detail.slice(0, 140)}${RESET}` : ""}`;
|
|
6710
|
-
});
|
|
6711
|
-
}
|
|
6712
|
-
function renderEventLines(logs) {
|
|
6713
|
-
return logs.filter((log3) => !CANONICAL_STAGES2.some((stage) => stage.toLowerCase() === logTitle(log3).toLowerCase())).slice(-12).flatMap((log3) => {
|
|
6714
|
-
const title = logTitle(log3) || "Rig event";
|
|
6715
|
-
const detail = logDetail2(log3);
|
|
6716
|
-
if (!detail)
|
|
6717
|
-
return [];
|
|
6718
|
-
const tone = String(log3.tone ?? "");
|
|
6719
|
-
const color = tone === "error" ? RED : tone === "tool" ? MAGENTA : DIM;
|
|
6720
|
-
return [`${color}[${title}]${RESET} ${detail.slice(0, 220)}`];
|
|
6721
|
-
});
|
|
6722
|
-
}
|
|
6723
|
-
|
|
6724
|
-
class RigRunComponent {
|
|
6725
|
-
truncateToWidth;
|
|
6726
|
-
snapshot = { run: {}, logs: [], timeline: [] };
|
|
6727
|
-
localEvents = [];
|
|
6728
|
-
constructor(truncateToWidth) {
|
|
6729
|
-
this.truncateToWidth = truncateToWidth;
|
|
6730
|
-
}
|
|
6731
|
-
update(snapshot) {
|
|
6732
|
-
this.snapshot = snapshot;
|
|
6733
|
-
}
|
|
6734
|
-
addLocalEvent(message2) {
|
|
6735
|
-
this.localEvents.push(`${new Date().toLocaleTimeString()} ${message2}`);
|
|
6736
|
-
this.localEvents = this.localEvents.slice(-8);
|
|
6737
|
-
}
|
|
6738
|
-
invalidate() {}
|
|
6739
|
-
render(width) {
|
|
6740
|
-
const run = unwrapRun(this.snapshot.run);
|
|
6741
|
-
const runId = String(run.runId ?? run.id ?? "run");
|
|
6742
|
-
const status = String(run.status ?? "unknown");
|
|
6743
|
-
const worker = typeof run.worktreePath === "string" && run.worktreePath.trim() ? run.worktreePath.trim() : typeof run.projectRoot === "string" && run.projectRoot.trim() ? run.projectRoot.trim() : "worker workspace pending";
|
|
6744
|
-
const lines = [
|
|
6745
|
-
`${BOLD}Rig Pi frontend${RESET} ${DIM}(local Pi TUI \u2192 Rig server \u2192 worker Pi backend)${RESET}`,
|
|
6746
|
-
`${BOLD}${runId}${RESET} \xB7 ${status} \xB7 ${DIM}${worker}${RESET}`,
|
|
6747
|
-
"",
|
|
6748
|
-
`${BOLD}Rig flow${RESET}`,
|
|
6749
|
-
...renderStageLines(this.snapshot.logs ?? []),
|
|
6750
|
-
"",
|
|
6751
|
-
...renderAssistantTextFromTimeline(this.snapshot.timeline ?? []),
|
|
6752
|
-
...renderToolLines(this.snapshot.timeline ?? []),
|
|
6753
|
-
"",
|
|
6754
|
-
`${BOLD}Rig / Pi events${RESET}`,
|
|
6755
|
-
...renderEventLines(this.snapshot.logs ?? []),
|
|
6756
|
-
...this.localEvents.map((event) => `${GREEN}[frontend]${RESET} ${event}`),
|
|
6757
|
-
""
|
|
6758
|
-
];
|
|
6759
|
-
return lines.slice(-42).map((line) => this.truncateToWidth(line, Math.max(10, width)));
|
|
6760
|
-
}
|
|
6761
|
-
}
|
|
6762
|
-
|
|
6763
|
-
class RigInputComponent {
|
|
6764
|
-
matchesKey;
|
|
6765
|
-
truncateToWidth;
|
|
6766
|
-
input;
|
|
6767
|
-
status = "Type text, /skill:..., or remote Pi slash commands. Local: /stop /detach.";
|
|
6768
|
-
constructor(InputCtor, matchesKey, truncateToWidth, onSubmit, onEscape) {
|
|
6769
|
-
this.matchesKey = matchesKey;
|
|
6770
|
-
this.truncateToWidth = truncateToWidth;
|
|
6771
|
-
this.input = new InputCtor;
|
|
6772
|
-
this.input.onSubmit = (value) => {
|
|
6773
|
-
const text2 = value.trim();
|
|
6774
|
-
this.input.setValue("");
|
|
6775
|
-
if (text2)
|
|
6776
|
-
Promise.resolve(onSubmit(text2));
|
|
6777
|
-
};
|
|
6778
|
-
this.input.onEscape = onEscape;
|
|
6779
|
-
}
|
|
6780
|
-
handleInput(data) {
|
|
6781
|
-
if (this.matchesKey(data, "ctrl+d")) {
|
|
6782
|
-
this.input.onEscape?.();
|
|
6783
|
-
return;
|
|
6784
|
-
}
|
|
6785
|
-
this.input.handleInput?.(data);
|
|
6786
|
-
}
|
|
6787
|
-
setStatus(status) {
|
|
6788
|
-
this.status = status;
|
|
6789
|
-
}
|
|
6790
|
-
invalidate() {
|
|
6791
|
-
this.input.invalidate();
|
|
6792
|
-
}
|
|
6793
|
-
render(width) {
|
|
6794
|
-
return [
|
|
6795
|
-
`${DIM}${this.truncateToWidth(this.status, Math.max(10, width))}${RESET}`,
|
|
6796
|
-
`${GREEN}${BOLD}You \u2192 worker Pi:${RESET}`,
|
|
6797
|
-
...this.input.render(width)
|
|
6798
|
-
];
|
|
6799
|
-
}
|
|
6800
|
-
}
|
|
6801
|
-
async function attachRunPiTuiFrontend(context, input) {
|
|
6802
|
-
const piTui = await loadPiTuiRuntime();
|
|
6803
|
-
const terminal = new piTui.ProcessTerminal;
|
|
6804
|
-
const tui = new piTui.TUI(terminal);
|
|
6805
|
-
const root = new piTui.Container;
|
|
6806
|
-
const runView = new RigRunComponent(piTui.truncateToWidth);
|
|
6807
|
-
let detached = false;
|
|
6808
|
-
let steered = input.steered === true;
|
|
6809
|
-
let latest = await readOperatorSnapshot(context, input.runId);
|
|
6810
|
-
let timelineCursor = latest.timelineCursor;
|
|
6811
|
-
runView.update(latest);
|
|
6812
|
-
if (steered)
|
|
6813
|
-
runView.addLocalEvent("initial message queued to worker Pi.");
|
|
6814
|
-
const stop = () => {
|
|
6815
|
-
detached = true;
|
|
6816
|
-
tui.stop();
|
|
6817
|
-
};
|
|
6818
|
-
const inputView = new RigInputComponent(piTui.Input, piTui.matchesKey, piTui.truncateToWidth, async (line) => {
|
|
6819
|
-
if (line === "/detach" || line === "/quit" || line === "/q") {
|
|
6820
|
-
runView.addLocalEvent("detached from run.");
|
|
6821
|
-
stop();
|
|
6822
|
-
return;
|
|
6823
|
-
}
|
|
6824
|
-
if (line === "/stop") {
|
|
6825
|
-
await stopRunViaServer(context, input.runId);
|
|
6826
|
-
runView.addLocalEvent("stop requested.");
|
|
6827
|
-
stop();
|
|
6828
|
-
return;
|
|
6829
|
-
}
|
|
6830
|
-
await steerRunViaServer(context, input.runId, line);
|
|
6831
|
-
steered = true;
|
|
6832
|
-
runView.addLocalEvent(`queued to worker Pi: ${line.slice(0, 160)}`);
|
|
6833
|
-
tui.requestRender();
|
|
6834
|
-
}, stop);
|
|
6835
|
-
root.addChild(runView);
|
|
6836
|
-
root.addChild(inputView);
|
|
6837
|
-
tui.addChild(root);
|
|
6838
|
-
tui.setFocus(inputView.input);
|
|
6839
|
-
tui.start();
|
|
6840
|
-
tui.requestRender(true);
|
|
6841
|
-
const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 1000));
|
|
6842
|
-
try {
|
|
6843
|
-
while (!detached && !TERMINAL_RUN_STATUSES.has(runStatusFromPayload(latest.run))) {
|
|
6844
|
-
await Bun.sleep(pollMs);
|
|
6845
|
-
latest = await readOperatorSnapshot(context, input.runId, { timelineCursor });
|
|
6846
|
-
timelineCursor = latest.timelineCursor;
|
|
6847
|
-
runView.update(latest);
|
|
6848
|
-
inputView.setStatus(`Remote worker ${runStatusFromPayload(latest.run)}. Input is forwarded to worker Pi; /stop /detach are local controls.`);
|
|
6849
|
-
tui.requestRender();
|
|
6850
|
-
}
|
|
6851
|
-
} finally {
|
|
6852
|
-
if (!detached)
|
|
6853
|
-
tui.stop();
|
|
6854
|
-
}
|
|
6855
|
-
return { ...latest, timelineCursor, steered, detached, rendered: renderOperatorSnapshot(latest) };
|
|
6856
|
-
}
|
|
6857
7269
|
async function attachRunOperatorView(context, input) {
|
|
6858
7270
|
let steered = false;
|
|
6859
7271
|
if (input.message?.trim()) {
|
|
6860
|
-
await steerRunViaServer(context, input.runId, input.message.trim());
|
|
7272
|
+
await sendRunPiPromptViaServer(context, input.runId, input.message.trim(), "steer").catch(() => steerRunViaServer(context, input.runId, input.message.trim()));
|
|
6861
7273
|
steered = true;
|
|
6862
7274
|
}
|
|
6863
7275
|
if (input.follow && !input.once && input.interactive !== false && context.outputMode === "text" && Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
6864
|
-
return
|
|
7276
|
+
return attachRunBundledPiFrontend(context, {
|
|
6865
7277
|
runId: input.runId,
|
|
6866
|
-
pollMs: input.pollMs,
|
|
6867
7278
|
steered
|
|
6868
7279
|
});
|
|
6869
7280
|
}
|
|
@@ -6874,7 +7285,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
6874
7285
|
surface.renderTimeline(snapshot.timeline);
|
|
6875
7286
|
surface.renderLogs(snapshot.logs);
|
|
6876
7287
|
if (steered)
|
|
6877
|
-
surface.info("
|
|
7288
|
+
surface.info("Message submitted to worker Pi.");
|
|
6878
7289
|
}
|
|
6879
7290
|
let detached = false;
|
|
6880
7291
|
let commandInput = null;
|
|
@@ -6893,7 +7304,7 @@ async function attachRunOperatorView(context, input) {
|
|
|
6893
7304
|
}
|
|
6894
7305
|
const pollMs = Math.max(250, Math.trunc(input.pollMs ?? 2000));
|
|
6895
7306
|
let timelineCursor = snapshot.timelineCursor;
|
|
6896
|
-
while (!detached && !
|
|
7307
|
+
while (!detached && !TERMINAL_RUN_STATUSES2.has(runStatusFromPayload(snapshot.run))) {
|
|
6897
7308
|
await Bun.sleep(pollMs);
|
|
6898
7309
|
snapshot = await readOperatorSnapshot(context, input.runId, { timelineCursor });
|
|
6899
7310
|
timelineCursor = snapshot.timelineCursor;
|
|
@@ -8948,8 +9359,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8948
9359
|
...input.model ? ["--model", input.model] : [],
|
|
8949
9360
|
"--prompt"
|
|
8950
9361
|
] : input.runtimeAdapter === "pi" ? [
|
|
8951
|
-
"
|
|
8952
|
-
"rpc",
|
|
9362
|
+
"__rig_pi_session_daemon__",
|
|
8953
9363
|
...input.model ? ["--model", input.model] : []
|
|
8954
9364
|
] : [
|
|
8955
9365
|
"--print",
|
|
@@ -9046,7 +9456,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9046
9456
|
projectRoot: context.projectRoot,
|
|
9047
9457
|
runId: input.runId,
|
|
9048
9458
|
stage,
|
|
9049
|
-
detail: stage === "Launch Pi" ? "Pi
|
|
9459
|
+
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,
|
|
9050
9460
|
status: stage === "Implement" || stage === "Launch Pi" ? "running" : "completed"
|
|
9051
9461
|
});
|
|
9052
9462
|
}
|
|
@@ -9087,6 +9497,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9087
9497
|
let latestProviderCommand = null;
|
|
9088
9498
|
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
9089
9499
|
let snapshotSidecarPromise = null;
|
|
9500
|
+
let wrapperManagesRuntimeSnapshot = false;
|
|
9090
9501
|
let dirtyBaselineApplied = false;
|
|
9091
9502
|
const childEnv = {
|
|
9092
9503
|
...process.env,
|
|
@@ -9155,6 +9566,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9155
9566
|
latestSessionDir = typeof payload.sessionDir === "string" ? payload.sessionDir : latestSessionDir;
|
|
9156
9567
|
latestLogsDir = typeof payload.logsDir === "string" ? payload.logsDir : latestLogsDir;
|
|
9157
9568
|
const runtimeId = typeof payload.runtimeId === "string" ? payload.runtimeId : null;
|
|
9569
|
+
wrapperManagesRuntimeSnapshot = payload.snapshotManaged === true;
|
|
9158
9570
|
latestRuntimeBranch = runtimeId;
|
|
9159
9571
|
provisioningAction.complete(latestRuntimeWorkspace ?? "Runtime ready.", {
|
|
9160
9572
|
runtimeId: runtimeId ?? null,
|
|
@@ -9192,7 +9604,7 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9192
9604
|
});
|
|
9193
9605
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
9194
9606
|
}
|
|
9195
|
-
if (!snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
9607
|
+
if (!wrapperManagesRuntimeSnapshot && !snapshotSidecarPromise && runtimeId && loadRuntimeSnapshotConfig(context.projectRoot).enabled) {
|
|
9196
9608
|
snapshotSidecarPromise = (async () => {
|
|
9197
9609
|
const { sidecar, error } = await resolveTaskRunSnapshotSidecar({
|
|
9198
9610
|
projectRoot: context.projectRoot,
|
|
@@ -9274,6 +9686,51 @@ ${planningClassification.planningRequired ? `Before implementing, write a concis
|
|
|
9274
9686
|
}
|
|
9275
9687
|
return true;
|
|
9276
9688
|
}
|
|
9689
|
+
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") {
|
|
9690
|
+
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";
|
|
9691
|
+
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");
|
|
9692
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
9693
|
+
id: nextRunLogId(),
|
|
9694
|
+
title,
|
|
9695
|
+
detail,
|
|
9696
|
+
tone: event.type === "pi.session.error" ? "error" : "info",
|
|
9697
|
+
status: reviewStarted ? "reviewing" : verificationStarted ? "validating" : "running",
|
|
9698
|
+
payload: {
|
|
9699
|
+
eventType: event.type,
|
|
9700
|
+
runId: typeof payload.runId === "string" ? payload.runId : null,
|
|
9701
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null,
|
|
9702
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : null
|
|
9703
|
+
},
|
|
9704
|
+
createdAt: new Date().toISOString()
|
|
9705
|
+
});
|
|
9706
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title });
|
|
9707
|
+
return true;
|
|
9708
|
+
}
|
|
9709
|
+
if (event.type === "pi.session.ready") {
|
|
9710
|
+
const privateMetadata = payload.privateMetadata && typeof payload.privateMetadata === "object" && !Array.isArray(payload.privateMetadata) ? payload.privateMetadata : null;
|
|
9711
|
+
const publicMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : null;
|
|
9712
|
+
if (privateMetadata) {
|
|
9713
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
9714
|
+
piSession: publicMetadata,
|
|
9715
|
+
piSessionPrivate: privateMetadata
|
|
9716
|
+
});
|
|
9717
|
+
const sessionId = typeof publicMetadata?.sessionId === "string" ? publicMetadata.sessionId : typeof privateMetadata.public === "object" && privateMetadata.public && !Array.isArray(privateMetadata.public) ? String(privateMetadata.public.sessionId ?? "") : "";
|
|
9718
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
9719
|
+
id: `log:${input.runId}:pi-session-ready`,
|
|
9720
|
+
title: "Worker Pi session ready",
|
|
9721
|
+
detail: sessionId ? `Session ${sessionId} is ready for WebSocket attach.` : "Worker Pi SDK session daemon is ready for WebSocket attach.",
|
|
9722
|
+
tone: "info",
|
|
9723
|
+
status: "running",
|
|
9724
|
+
payload: {
|
|
9725
|
+
sessionId: sessionId || null,
|
|
9726
|
+
runtimeId: typeof payload.runtimeId === "string" ? payload.runtimeId : null
|
|
9727
|
+
},
|
|
9728
|
+
createdAt: new Date().toISOString()
|
|
9729
|
+
});
|
|
9730
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Worker Pi session ready" });
|
|
9731
|
+
}
|
|
9732
|
+
return true;
|
|
9733
|
+
}
|
|
9277
9734
|
if (event.type === "pi.rpc.prompt.sent" || event.type === "pi.rpc.steering.delivered" || event.type === "pi.rpc.steering.poll.failed" || event.type === "pi.rpc.extension_ui.cancelled") {
|
|
9278
9735
|
const title = event.type === "pi.rpc.prompt.sent" ? "Delivered initial prompt to worker Pi" : event.type === "pi.rpc.steering.delivered" ? "Delivered steering to worker Pi" : event.type === "pi.rpc.steering.poll.failed" ? "Worker Pi steering poll failed" : "Pi RPC UI request auto-cancelled";
|
|
9279
9736
|
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")}`;
|
|
@@ -9805,7 +10262,7 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
9805
10262
|
startedAt,
|
|
9806
10263
|
finishedAt: requestedAt
|
|
9807
10264
|
});
|
|
9808
|
-
|
|
10265
|
+
process.exit(0);
|
|
9809
10266
|
}
|
|
9810
10267
|
const runPiPrFeedbackFix = async (message2) => {
|
|
9811
10268
|
appendPiStageLog({
|