@h-rig/cli 0.0.6-alpha.1 → 0.0.6-alpha.11
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 +481 -226
- package/dist/src/commands/_authority-runs.js +1 -0
- package/dist/src/commands/_doctor-checks.js +31 -13
- package/dist/src/commands/_operator-view.js +20 -2
- package/dist/src/commands/_preflight.js +25 -7
- package/dist/src/commands/_server-client.js +47 -9
- package/dist/src/commands/_snapshot-upload.js +26 -8
- package/dist/src/commands/agent.js +1 -0
- package/dist/src/commands/doctor.js +31 -13
- package/dist/src/commands/github.js +21 -3
- package/dist/src/commands/init.js +185 -67
- package/dist/src/commands/run.js +53 -9
- package/dist/src/commands/server.js +22 -4
- package/dist/src/commands/setup.js +36 -18
- package/dist/src/commands/task-run-driver.js +125 -16
- package/dist/src/commands/task.js +28 -10
- package/dist/src/commands.js +469 -217
- package/dist/src/index.js +481 -226
- package/dist/src/launcher.js +4 -2
- package/dist/src/runner.js +3 -2
- package/package.json +5 -4
package/dist/bin/rig.js
CHANGED
|
@@ -105,12 +105,13 @@ async function runCommand(context, parts) {
|
|
|
105
105
|
const envMode = process.env.RIG_BASH_MODE;
|
|
106
106
|
const effectiveMode = context.policyMode || (envMode === "off" || envMode === "observe" || envMode === "enforce" ? envMode : loadPolicy(context.projectRoot).mode);
|
|
107
107
|
const controlledPath = `${resolve(context.projectRoot, ".rig", "bin")}:${context.projectRoot}/rig/tools:${process.env.PATH ?? ""}`;
|
|
108
|
-
const
|
|
108
|
+
const usesInfrastructureBinary = parts[0] === "rig-server";
|
|
109
|
+
const controlledBash = usesInfrastructureBinary ? null : await ensureAgentShellBinary(context.projectRoot);
|
|
109
110
|
const commandEnv = [
|
|
110
111
|
"env",
|
|
111
112
|
`PATH=${controlledPath}`,
|
|
112
113
|
`PROJECT_RIG_ROOT=${context.projectRoot}`,
|
|
113
|
-
`BASH=${controlledBash}
|
|
114
|
+
...controlledBash ? [`BASH=${controlledBash}`] : [],
|
|
114
115
|
`RIG_BASH_MODE=${effectiveMode}`,
|
|
115
116
|
`RIG_POLICY_FILE=${resolve(context.projectRoot, "rig/policy/policy.json")}`,
|
|
116
117
|
...context.eventBus.getEventsFile() ? [`RIG_EVENTS_FILE=${context.eventBus.getEventsFile()}`] : []
|
|
@@ -2671,6 +2672,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
2671
2672
|
|
|
2672
2673
|
// packages/cli/src/commands/_server-client.ts
|
|
2673
2674
|
import { spawnSync } from "child_process";
|
|
2675
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
2676
|
+
import { resolve as resolve9 } from "path";
|
|
2674
2677
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
2675
2678
|
var cachedGitHubBearerToken;
|
|
2676
2679
|
function cleanToken(value) {
|
|
@@ -2680,9 +2683,25 @@ function cleanToken(value) {
|
|
|
2680
2683
|
function setGitHubBearerTokenForCurrentProcess(token) {
|
|
2681
2684
|
cachedGitHubBearerToken = cleanToken(token ?? undefined);
|
|
2682
2685
|
}
|
|
2683
|
-
function
|
|
2686
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
2687
|
+
const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
|
|
2688
|
+
if (!existsSync5(path))
|
|
2689
|
+
return null;
|
|
2690
|
+
try {
|
|
2691
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
2692
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
2693
|
+
} catch {
|
|
2694
|
+
return null;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
2684
2698
|
if (cachedGitHubBearerToken !== undefined)
|
|
2685
2699
|
return cachedGitHubBearerToken;
|
|
2700
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
2701
|
+
if (privateSession) {
|
|
2702
|
+
cachedGitHubBearerToken = privateSession;
|
|
2703
|
+
return cachedGitHubBearerToken;
|
|
2704
|
+
}
|
|
2686
2705
|
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
2687
2706
|
if (envToken) {
|
|
2688
2707
|
cachedGitHubBearerToken = envToken;
|
|
@@ -2702,7 +2721,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2702
2721
|
if (selected?.connection.kind === "remote") {
|
|
2703
2722
|
return {
|
|
2704
2723
|
baseUrl: selected.connection.baseUrl,
|
|
2705
|
-
authToken: readGitHubBearerTokenForRemote(),
|
|
2724
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
2706
2725
|
connectionKind: "remote"
|
|
2707
2726
|
};
|
|
2708
2727
|
}
|
|
@@ -2809,7 +2828,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
|
2809
2828
|
const payload = await requestServerJson(context, "/api/github/auth/token", {
|
|
2810
2829
|
method: "POST",
|
|
2811
2830
|
headers: { "content-type": "application/json" },
|
|
2812
|
-
body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
|
|
2831
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
|
|
2813
2832
|
});
|
|
2814
2833
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2815
2834
|
}
|
|
@@ -2830,18 +2849,38 @@ async function registerProjectViaServer(context, input) {
|
|
|
2830
2849
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2831
2850
|
}
|
|
2832
2851
|
function sleep(ms) {
|
|
2833
|
-
return new Promise((
|
|
2852
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
2853
|
+
}
|
|
2854
|
+
function isRetryableProjectRootSwitchError(error) {
|
|
2855
|
+
if (!(error instanceof Error))
|
|
2856
|
+
return false;
|
|
2857
|
+
const message = error.message.toLowerCase();
|
|
2858
|
+
return message.includes("rig server request failed (401): auth-required") || message.includes("rig server request failed (401): github-token-required") || message.includes("rig server request failed (502)") || message.includes("rig server request failed (503)") || message.includes("bad gateway") || message.includes("fetch failed") || message.includes("econnrefused") || message.includes("connection refused");
|
|
2834
2859
|
}
|
|
2835
2860
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
2836
|
-
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
2837
|
-
method: "POST",
|
|
2838
|
-
headers: { "content-type": "application/json" },
|
|
2839
|
-
body: JSON.stringify({ projectRoot })
|
|
2840
|
-
});
|
|
2841
2861
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
2842
2862
|
const pollMs = options.pollMs ?? 1000;
|
|
2843
2863
|
const deadline = Date.now() + timeoutMs;
|
|
2844
2864
|
let lastError;
|
|
2865
|
+
let switched = null;
|
|
2866
|
+
while (Date.now() < deadline) {
|
|
2867
|
+
try {
|
|
2868
|
+
switched = await requestServerJson(context, "/api/server/project-root", {
|
|
2869
|
+
method: "POST",
|
|
2870
|
+
headers: { "content-type": "application/json" },
|
|
2871
|
+
body: JSON.stringify({ projectRoot })
|
|
2872
|
+
});
|
|
2873
|
+
break;
|
|
2874
|
+
} catch (error) {
|
|
2875
|
+
lastError = error;
|
|
2876
|
+
if (!isRetryableProjectRootSwitchError(error))
|
|
2877
|
+
throw error;
|
|
2878
|
+
await sleep(pollMs);
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
if (!switched) {
|
|
2882
|
+
throw new CliError2(`Rig server did not accept project-root switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no response")}).`, 1);
|
|
2883
|
+
}
|
|
2845
2884
|
while (Date.now() < deadline) {
|
|
2846
2885
|
try {
|
|
2847
2886
|
const status = await requestServerJson(context, "/api/server/status");
|
|
@@ -2921,9 +2960,9 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2921
2960
|
}
|
|
2922
2961
|
|
|
2923
2962
|
// packages/cli/src/commands/_pi-install.ts
|
|
2924
|
-
import { existsSync as
|
|
2963
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2925
2964
|
import { homedir as homedir3 } from "os";
|
|
2926
|
-
import { resolve as
|
|
2965
|
+
import { resolve as resolve10 } from "path";
|
|
2927
2966
|
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2928
2967
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2929
2968
|
export { default } from '@rig/pi-rig';
|
|
@@ -2938,11 +2977,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
2938
2977
|
return { exitCode, stdout, stderr };
|
|
2939
2978
|
}
|
|
2940
2979
|
function resolvePiRigExtensionPath(homeDir) {
|
|
2941
|
-
return
|
|
2980
|
+
return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
2942
2981
|
}
|
|
2943
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
2944
|
-
const localPackage =
|
|
2945
|
-
if (exists(
|
|
2982
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
|
|
2983
|
+
const localPackage = resolve10(projectRoot, "packages", "pi-rig");
|
|
2984
|
+
if (exists(resolve10(localPackage, "package.json")))
|
|
2946
2985
|
return localPackage;
|
|
2947
2986
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
2948
2987
|
}
|
|
@@ -2993,13 +3032,13 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
2993
3032
|
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
2994
3033
|
};
|
|
2995
3034
|
}
|
|
2996
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists =
|
|
3035
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
|
|
2997
3036
|
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
2998
|
-
const indexPath =
|
|
3037
|
+
const indexPath = resolve10(extensionPath, "index.ts");
|
|
2999
3038
|
if (!exists(indexPath))
|
|
3000
3039
|
return;
|
|
3001
3040
|
try {
|
|
3002
|
-
const content =
|
|
3041
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
3003
3042
|
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
3004
3043
|
rmSync3(extensionPath, { recursive: true, force: true });
|
|
3005
3044
|
}
|
|
@@ -3015,13 +3054,13 @@ async function checkPiRigInstall(input = {}) {
|
|
|
3015
3054
|
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
3016
3055
|
};
|
|
3017
3056
|
}
|
|
3018
|
-
const exists = input.exists ??
|
|
3057
|
+
const exists = input.exists ?? existsSync6;
|
|
3019
3058
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3020
3059
|
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
3021
3060
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
3022
3061
|
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
3023
3062
|
${piListResult.stderr}`);
|
|
3024
|
-
const legacyBridge = exists(
|
|
3063
|
+
const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
|
|
3025
3064
|
const hasPiRig = listedPiRig;
|
|
3026
3065
|
return {
|
|
3027
3066
|
extensionPath,
|
|
@@ -3359,7 +3398,7 @@ async function executeQueue(context, args) {
|
|
|
3359
3398
|
}
|
|
3360
3399
|
|
|
3361
3400
|
// packages/cli/src/commands/agent.ts
|
|
3362
|
-
import { resolve as
|
|
3401
|
+
import { resolve as resolve12 } from "path";
|
|
3363
3402
|
import {
|
|
3364
3403
|
agentId,
|
|
3365
3404
|
cleanupAgentRuntime,
|
|
@@ -3369,8 +3408,8 @@ import {
|
|
|
3369
3408
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3370
3409
|
|
|
3371
3410
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3372
|
-
import { existsSync as
|
|
3373
|
-
import { resolve as
|
|
3411
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3412
|
+
import { resolve as resolve11 } from "path";
|
|
3374
3413
|
import {
|
|
3375
3414
|
readAuthorityRun,
|
|
3376
3415
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3392,8 +3431,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3392
3431
|
return "claude-code";
|
|
3393
3432
|
}
|
|
3394
3433
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3395
|
-
const issuesPath =
|
|
3396
|
-
if (!
|
|
3434
|
+
const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3435
|
+
if (!existsSync7(issuesPath)) {
|
|
3397
3436
|
return null;
|
|
3398
3437
|
}
|
|
3399
3438
|
let latest = null;
|
|
@@ -3428,6 +3467,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3428
3467
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
3429
3468
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
3430
3469
|
const next = {
|
|
3470
|
+
...existing ?? {},
|
|
3431
3471
|
runId: input.runId,
|
|
3432
3472
|
projectRoot,
|
|
3433
3473
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -3460,7 +3500,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3460
3500
|
} else if ("errorText" in next) {
|
|
3461
3501
|
delete next.errorText;
|
|
3462
3502
|
}
|
|
3463
|
-
writeJsonFile3(
|
|
3503
|
+
writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3464
3504
|
return next;
|
|
3465
3505
|
}
|
|
3466
3506
|
|
|
@@ -3581,10 +3621,10 @@ async function executeAgent(context, args) {
|
|
|
3581
3621
|
status: "running",
|
|
3582
3622
|
startedAt: createdAt,
|
|
3583
3623
|
worktreePath: runtime.workspaceDir,
|
|
3584
|
-
artifactRoot:
|
|
3624
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3585
3625
|
logRoot: runtime.logsDir,
|
|
3586
|
-
sessionPath:
|
|
3587
|
-
sessionLogPath:
|
|
3626
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3627
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3588
3628
|
pid: process.pid
|
|
3589
3629
|
});
|
|
3590
3630
|
const result = await runInAgentRuntime({
|
|
@@ -3604,10 +3644,10 @@ async function executeAgent(context, args) {
|
|
|
3604
3644
|
startedAt: createdAt,
|
|
3605
3645
|
completedAt: failedAt,
|
|
3606
3646
|
worktreePath: runtime.workspaceDir,
|
|
3607
|
-
artifactRoot:
|
|
3647
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3608
3648
|
logRoot: runtime.logsDir,
|
|
3609
|
-
sessionPath:
|
|
3610
|
-
sessionLogPath:
|
|
3649
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3650
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3611
3651
|
pid: process.pid,
|
|
3612
3652
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3613
3653
|
});
|
|
@@ -3624,10 +3664,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3624
3664
|
startedAt: createdAt,
|
|
3625
3665
|
completedAt,
|
|
3626
3666
|
worktreePath: runtime.workspaceDir,
|
|
3627
|
-
artifactRoot:
|
|
3667
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3628
3668
|
logRoot: runtime.logsDir,
|
|
3629
|
-
sessionPath:
|
|
3630
|
-
sessionLogPath:
|
|
3669
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3670
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3631
3671
|
pid: process.pid
|
|
3632
3672
|
});
|
|
3633
3673
|
return {
|
|
@@ -3701,7 +3741,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3701
3741
|
import {
|
|
3702
3742
|
chmodSync,
|
|
3703
3743
|
copyFileSync as copyFileSync2,
|
|
3704
|
-
existsSync as
|
|
3744
|
+
existsSync as existsSync8,
|
|
3705
3745
|
mkdirSync as mkdirSync6,
|
|
3706
3746
|
readdirSync,
|
|
3707
3747
|
readlinkSync,
|
|
@@ -3711,7 +3751,7 @@ import {
|
|
|
3711
3751
|
unlinkSync
|
|
3712
3752
|
} from "fs";
|
|
3713
3753
|
import { homedir as homedir4 } from "os";
|
|
3714
|
-
import { resolve as
|
|
3754
|
+
import { resolve as resolve13 } from "path";
|
|
3715
3755
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3716
3756
|
import {
|
|
3717
3757
|
computeRuntimeImageFingerprint,
|
|
@@ -3730,13 +3770,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3730
3770
|
|
|
3731
3771
|
// packages/cli/src/commands/dist.ts
|
|
3732
3772
|
function collectRigValidatorBuildTargets(input) {
|
|
3733
|
-
const validatorsRoot =
|
|
3734
|
-
if (!
|
|
3773
|
+
const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3774
|
+
if (!existsSync8(validatorsRoot))
|
|
3735
3775
|
return [];
|
|
3736
3776
|
const targets = [];
|
|
3737
3777
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3738
3778
|
for (const category of categories) {
|
|
3739
|
-
const categoryDir =
|
|
3779
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3740
3780
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3741
3781
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3742
3782
|
continue;
|
|
@@ -3745,7 +3785,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3745
3785
|
continue;
|
|
3746
3786
|
targets.push({
|
|
3747
3787
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3748
|
-
dest:
|
|
3788
|
+
dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3749
3789
|
cwd: input.hostProjectRoot
|
|
3750
3790
|
});
|
|
3751
3791
|
}
|
|
@@ -3754,16 +3794,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3754
3794
|
}
|
|
3755
3795
|
async function findLatestDistBinary(projectRoot) {
|
|
3756
3796
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3757
|
-
if (!
|
|
3797
|
+
if (!existsSync8(distRoot)) {
|
|
3758
3798
|
return null;
|
|
3759
3799
|
}
|
|
3760
3800
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3761
3801
|
name: entry.name,
|
|
3762
|
-
mtimeMs: statSync(
|
|
3802
|
+
mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
|
|
3763
3803
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3764
3804
|
for (const { name } of entries) {
|
|
3765
|
-
const candidate =
|
|
3766
|
-
if (
|
|
3805
|
+
const candidate = resolve13(distRoot, name, "bin", "rig");
|
|
3806
|
+
if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3767
3807
|
return candidate;
|
|
3768
3808
|
}
|
|
3769
3809
|
}
|
|
@@ -3775,7 +3815,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3775
3815
|
async function runDistDoctor(projectRoot) {
|
|
3776
3816
|
const bunPath = Bun.which("bun");
|
|
3777
3817
|
const rigPath = Bun.which("rig");
|
|
3778
|
-
const userBinDir =
|
|
3818
|
+
const userBinDir = resolve13(homedir4(), ".local/bin");
|
|
3779
3819
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3780
3820
|
let rigRunnable = false;
|
|
3781
3821
|
if (rigPath) {
|
|
@@ -3823,15 +3863,15 @@ async function executeDist(context, args) {
|
|
|
3823
3863
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3824
3864
|
let buildDir = null;
|
|
3825
3865
|
if (!source) {
|
|
3826
|
-
buildDir =
|
|
3866
|
+
buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3827
3867
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3828
|
-
source =
|
|
3868
|
+
source = resolve13(buildDir, "bin", "rig");
|
|
3829
3869
|
}
|
|
3830
|
-
if (!
|
|
3870
|
+
if (!existsSync8(source)) {
|
|
3831
3871
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3832
3872
|
}
|
|
3833
|
-
const installedPath =
|
|
3834
|
-
if (
|
|
3873
|
+
const installedPath = resolve13(installDir, "rig");
|
|
3874
|
+
if (existsSync8(installedPath)) {
|
|
3835
3875
|
unlinkSync(installedPath);
|
|
3836
3876
|
}
|
|
3837
3877
|
copyFileSync2(source, installedPath);
|
|
@@ -3873,22 +3913,22 @@ async function executeDist(context, args) {
|
|
|
3873
3913
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3874
3914
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3875
3915
|
const currentId = computeRuntimeImageId(fp);
|
|
3876
|
-
const imagesDir =
|
|
3916
|
+
const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3877
3917
|
mkdirSync6(imagesDir, { recursive: true });
|
|
3878
3918
|
let pruned = 0;
|
|
3879
3919
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3880
3920
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3881
|
-
rmSync4(
|
|
3921
|
+
rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
|
|
3882
3922
|
pruned++;
|
|
3883
3923
|
}
|
|
3884
3924
|
}
|
|
3885
3925
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3886
3926
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3887
3927
|
}
|
|
3888
|
-
const imageDir =
|
|
3889
|
-
mkdirSync6(
|
|
3890
|
-
mkdirSync6(
|
|
3891
|
-
mkdirSync6(
|
|
3928
|
+
const imageDir = resolve13(imagesDir, currentId);
|
|
3929
|
+
mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
|
|
3930
|
+
mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
|
|
3931
|
+
mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
|
|
3892
3932
|
const hookNames = [
|
|
3893
3933
|
"scope-guard",
|
|
3894
3934
|
"import-guard",
|
|
@@ -3902,25 +3942,25 @@ async function executeDist(context, args) {
|
|
|
3902
3942
|
];
|
|
3903
3943
|
const targets = [];
|
|
3904
3944
|
const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
|
|
3905
|
-
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest:
|
|
3906
|
-
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest:
|
|
3945
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3946
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3907
3947
|
for (const hookName of hookNames) {
|
|
3908
3948
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3909
|
-
targets.push({ source: src, dest:
|
|
3910
|
-
targets.push({ source: src, dest:
|
|
3949
|
+
targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3950
|
+
targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3911
3951
|
}
|
|
3912
|
-
const pluginsDir =
|
|
3913
|
-
const binPluginsDir =
|
|
3914
|
-
const validatorsRoot =
|
|
3915
|
-
const binValidatorsDir =
|
|
3952
|
+
const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
|
|
3953
|
+
const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3954
|
+
const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3955
|
+
const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3916
3956
|
mkdirSync6(binPluginsDir, { recursive: true });
|
|
3917
3957
|
mkdirSync6(binValidatorsDir, { recursive: true });
|
|
3918
|
-
if (
|
|
3958
|
+
if (existsSync8(pluginsDir)) {
|
|
3919
3959
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3920
3960
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3921
3961
|
if (!m)
|
|
3922
3962
|
continue;
|
|
3923
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3963
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3924
3964
|
}
|
|
3925
3965
|
}
|
|
3926
3966
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3931,17 +3971,17 @@ async function executeDist(context, args) {
|
|
|
3931
3971
|
const isValidator = dest.includes("/bin/validators/");
|
|
3932
3972
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3933
3973
|
}
|
|
3934
|
-
if (
|
|
3974
|
+
if (existsSync8(pluginsDir)) {
|
|
3935
3975
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3936
3976
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3937
3977
|
if (!m)
|
|
3938
3978
|
continue;
|
|
3939
3979
|
const pluginName = m[1];
|
|
3940
|
-
const imageBin =
|
|
3980
|
+
const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
|
|
3941
3981
|
if (!pluginName)
|
|
3942
3982
|
continue;
|
|
3943
|
-
const symlinkPath =
|
|
3944
|
-
if (
|
|
3983
|
+
const symlinkPath = resolve13(binPluginsDir, pluginName);
|
|
3984
|
+
if (existsSync8(imageBin)) {
|
|
3945
3985
|
try {
|
|
3946
3986
|
unlinkSync(symlinkPath);
|
|
3947
3987
|
} catch {}
|
|
@@ -3949,10 +3989,10 @@ async function executeDist(context, args) {
|
|
|
3949
3989
|
}
|
|
3950
3990
|
}
|
|
3951
3991
|
}
|
|
3952
|
-
if (
|
|
3992
|
+
if (existsSync8(validatorsRoot)) {
|
|
3953
3993
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3954
3994
|
for (const category of categories) {
|
|
3955
|
-
const categoryDir =
|
|
3995
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3956
3996
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3957
3997
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3958
3998
|
continue;
|
|
@@ -3960,9 +4000,9 @@ async function executeDist(context, args) {
|
|
|
3960
4000
|
if (!check || check === "index" || check === "shared")
|
|
3961
4001
|
continue;
|
|
3962
4002
|
const validatorName = `${category.name}-${check}`;
|
|
3963
|
-
const imageBin =
|
|
3964
|
-
const symlinkPath =
|
|
3965
|
-
if (
|
|
4003
|
+
const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
|
|
4004
|
+
const symlinkPath = resolve13(binValidatorsDir, validatorName);
|
|
4005
|
+
if (existsSync8(imageBin)) {
|
|
3966
4006
|
try {
|
|
3967
4007
|
unlinkSync(symlinkPath);
|
|
3968
4008
|
} catch {}
|
|
@@ -3971,18 +4011,18 @@ async function executeDist(context, args) {
|
|
|
3971
4011
|
}
|
|
3972
4012
|
}
|
|
3973
4013
|
}
|
|
3974
|
-
const agentsDir =
|
|
3975
|
-
if (
|
|
4014
|
+
const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
4015
|
+
if (existsSync8(agentsDir)) {
|
|
3976
4016
|
let relinkCount = 0;
|
|
3977
4017
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3978
4018
|
if (!agentEntry.isDirectory())
|
|
3979
4019
|
continue;
|
|
3980
|
-
const agentBinDir =
|
|
3981
|
-
if (!
|
|
4020
|
+
const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
4021
|
+
if (!existsSync8(agentBinDir))
|
|
3982
4022
|
continue;
|
|
3983
4023
|
const walkDir = (dir) => {
|
|
3984
4024
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3985
|
-
const fullPath =
|
|
4025
|
+
const fullPath = resolve13(dir, entry.name);
|
|
3986
4026
|
if (entry.isDirectory()) {
|
|
3987
4027
|
walkDir(fullPath);
|
|
3988
4028
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -4016,7 +4056,7 @@ async function executeDist(context, args) {
|
|
|
4016
4056
|
|
|
4017
4057
|
// packages/cli/src/commands/inbox.ts
|
|
4018
4058
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
4019
|
-
import { resolve as
|
|
4059
|
+
import { resolve as resolve14 } from "path";
|
|
4020
4060
|
import {
|
|
4021
4061
|
listAuthorityRuns,
|
|
4022
4062
|
readJsonlFile as readJsonlFile3,
|
|
@@ -4033,7 +4073,7 @@ async function executeInbox(context, args) {
|
|
|
4033
4073
|
pending = task.rest;
|
|
4034
4074
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
4035
4075
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
4036
|
-
const approvals = runs.flatMap((entry) => readJsonlFile3(
|
|
4076
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
4037
4077
|
runId: entry.runId,
|
|
4038
4078
|
record
|
|
4039
4079
|
})));
|
|
@@ -4061,7 +4101,7 @@ async function executeInbox(context, args) {
|
|
|
4061
4101
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4062
4102
|
throw new CliError2("decision must be approve or reject.");
|
|
4063
4103
|
}
|
|
4064
|
-
const approvalsPath =
|
|
4104
|
+
const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
4065
4105
|
const approvals = readJsonlFile3(approvalsPath);
|
|
4066
4106
|
const resolvedAt = new Date().toISOString();
|
|
4067
4107
|
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);
|
|
@@ -4078,7 +4118,7 @@ async function executeInbox(context, args) {
|
|
|
4078
4118
|
pending = task.rest;
|
|
4079
4119
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
4080
4120
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
4081
|
-
const requests = runs.flatMap((entry) => readJsonlFile3(
|
|
4121
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
4082
4122
|
runId: entry.runId,
|
|
4083
4123
|
record
|
|
4084
4124
|
})));
|
|
@@ -4120,7 +4160,7 @@ async function executeInbox(context, args) {
|
|
|
4120
4160
|
const [key, ...restValue] = entry.split("=");
|
|
4121
4161
|
return [key, restValue.join("=")];
|
|
4122
4162
|
}));
|
|
4123
|
-
const requestsPath =
|
|
4163
|
+
const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
4124
4164
|
const requests = readJsonlFile3(requestsPath);
|
|
4125
4165
|
const resolvedAt = new Date().toISOString();
|
|
4126
4166
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -4135,14 +4175,14 @@ async function executeInbox(context, args) {
|
|
|
4135
4175
|
}
|
|
4136
4176
|
|
|
4137
4177
|
// packages/cli/src/commands/init.ts
|
|
4138
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
4178
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4139
4179
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4140
|
-
import { resolve as
|
|
4180
|
+
import { resolve as resolve17 } from "path";
|
|
4141
4181
|
import { buildRigInitConfigSource } from "@rig/core";
|
|
4142
4182
|
|
|
4143
4183
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
4144
4184
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
4145
|
-
import { dirname as dirname2, resolve as
|
|
4185
|
+
import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
|
|
4146
4186
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
4147
4187
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
4148
4188
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -4164,15 +4204,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
4164
4204
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
4165
4205
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
4166
4206
|
}
|
|
4167
|
-
const resolved =
|
|
4207
|
+
const resolved = resolve15(root, relativePath);
|
|
4168
4208
|
const relativeToRoot = relative(root, resolved);
|
|
4169
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
4209
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
|
|
4170
4210
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
4171
4211
|
}
|
|
4172
4212
|
return resolved;
|
|
4173
4213
|
}
|
|
4174
4214
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
4175
|
-
const root =
|
|
4215
|
+
const root = resolve15(projectRoot);
|
|
4176
4216
|
const excludedDirectories = [...new Set([
|
|
4177
4217
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
4178
4218
|
...options.excludedDirectories ?? []
|
|
@@ -4184,7 +4224,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
4184
4224
|
for (const entry of entries) {
|
|
4185
4225
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
4186
4226
|
continue;
|
|
4187
|
-
const fullPath =
|
|
4227
|
+
const fullPath = resolve15(dir, entry.name);
|
|
4188
4228
|
if (entry.isDirectory()) {
|
|
4189
4229
|
await visit(fullPath);
|
|
4190
4230
|
continue;
|
|
@@ -4232,8 +4272,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
4232
4272
|
}
|
|
4233
4273
|
|
|
4234
4274
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
4235
|
-
import { existsSync as
|
|
4236
|
-
import { resolve as
|
|
4275
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
4276
|
+
import { resolve as resolve16 } from "path";
|
|
4237
4277
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
4238
4278
|
function check(id, label, status, detail, remediation) {
|
|
4239
4279
|
return {
|
|
@@ -4273,11 +4313,11 @@ function repoSlugFromConfig(config) {
|
|
|
4273
4313
|
function loadFallbackConfig(projectRoot) {
|
|
4274
4314
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
4275
4315
|
for (const name of candidates) {
|
|
4276
|
-
const path =
|
|
4277
|
-
if (!
|
|
4316
|
+
const path = resolve16(projectRoot, name);
|
|
4317
|
+
if (!existsSync9(path))
|
|
4278
4318
|
continue;
|
|
4279
4319
|
try {
|
|
4280
|
-
const source =
|
|
4320
|
+
const source = readFileSync5(path, "utf8");
|
|
4281
4321
|
if (name.endsWith(".json"))
|
|
4282
4322
|
return JSON.parse(source);
|
|
4283
4323
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -4356,7 +4396,7 @@ async function runRigDoctorChecks(options) {
|
|
|
4356
4396
|
checks.push(check("bun", `bun >= ${MIN_SUPPORTED_BUN_VERSION}`, isSupportedBunVersion(bunVersion) ? "pass" : "fail", `found ${bunVersion}`, `Install Bun ${MIN_SUPPORTED_BUN_VERSION} or newer.`), check("git", "git", which("git") ? "pass" : "fail", which("git") ?? undefined, "Install git and ensure it is on PATH."), check("jq", "jq", which("jq") ? "pass" : "warn", which("jq") ?? undefined, "Install jq (for example `brew install jq`)."));
|
|
4357
4397
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
4358
4398
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
4359
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
4399
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
|
|
4360
4400
|
checks.push(config ? check("config", "rig.config loadable", "pass") : check("config", "rig.config loadable", hasConfigFile ? "fail" : "fail", hasConfigFile ? "config file exists but failed to load" : "missing rig.config.ts/json", "Run `rig init` or fix the config error."));
|
|
4361
4401
|
const taskSourceKind = config?.taskSource?.kind;
|
|
4362
4402
|
checks.push(taskSourceKind ? check("task-source", "task source configured", "pass", taskSourceKind) : check("task-source", "task source configured", "fail", "missing taskSource", "Configure taskSource in rig.config.ts."));
|
|
@@ -4451,10 +4491,10 @@ function countDoctorFailures(checks) {
|
|
|
4451
4491
|
}
|
|
4452
4492
|
|
|
4453
4493
|
// packages/cli/src/commands/init.ts
|
|
4454
|
-
var
|
|
4494
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4455
4495
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4456
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4457
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4496
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4497
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4458
4498
|
};
|
|
4459
4499
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
4460
4500
|
const trimmed = remoteUrl.trim();
|
|
@@ -4474,20 +4514,20 @@ function parseRepoSlug(value) {
|
|
|
4474
4514
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
4475
4515
|
}
|
|
4476
4516
|
function ensureRigPrivateDirs(projectRoot) {
|
|
4477
|
-
const rigDir =
|
|
4478
|
-
mkdirSync7(
|
|
4479
|
-
mkdirSync7(
|
|
4480
|
-
mkdirSync7(
|
|
4481
|
-
mkdirSync7(
|
|
4482
|
-
mkdirSync7(
|
|
4483
|
-
const taskConfigPath =
|
|
4484
|
-
if (!
|
|
4517
|
+
const rigDir = resolve17(projectRoot, ".rig");
|
|
4518
|
+
mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
|
|
4519
|
+
mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
|
|
4520
|
+
mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
|
|
4521
|
+
mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
|
|
4522
|
+
mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
|
|
4523
|
+
const taskConfigPath = resolve17(rigDir, "task-config.json");
|
|
4524
|
+
if (!existsSync10(taskConfigPath))
|
|
4485
4525
|
writeFileSync5(taskConfigPath, `{}
|
|
4486
4526
|
`, "utf-8");
|
|
4487
4527
|
}
|
|
4488
4528
|
function ensureGitignoreEntries(projectRoot) {
|
|
4489
|
-
const path =
|
|
4490
|
-
const existing =
|
|
4529
|
+
const path = resolve17(projectRoot, ".gitignore");
|
|
4530
|
+
const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
|
|
4491
4531
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
4492
4532
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4493
4533
|
if (missing.length === 0)
|
|
@@ -4500,14 +4540,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
4500
4540
|
`, "utf8");
|
|
4501
4541
|
}
|
|
4502
4542
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
4503
|
-
const path =
|
|
4504
|
-
const existing =
|
|
4543
|
+
const path = resolve17(projectRoot, "package.json");
|
|
4544
|
+
const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
|
|
4505
4545
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
4506
4546
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
4507
4547
|
devDependencies[name] = spec;
|
|
4508
4548
|
}
|
|
4509
4549
|
const next = {
|
|
4510
|
-
...
|
|
4550
|
+
...existsSync10(path) ? existing : { name: "rig-project", private: true },
|
|
4511
4551
|
devDependencies
|
|
4512
4552
|
};
|
|
4513
4553
|
writeFileSync5(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -4577,15 +4617,71 @@ async function promptSelect(prompts, options) {
|
|
|
4577
4617
|
throw new CliError2("Init cancelled.", 1);
|
|
4578
4618
|
return String(value);
|
|
4579
4619
|
}
|
|
4580
|
-
|
|
4620
|
+
function sleep2(ms) {
|
|
4621
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
4622
|
+
}
|
|
4623
|
+
function positiveIntFromEnv(name, fallback) {
|
|
4624
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
4625
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
4626
|
+
}
|
|
4627
|
+
function apiSessionTokenFrom(payload) {
|
|
4628
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4629
|
+
return null;
|
|
4630
|
+
const token = payload.apiSessionToken;
|
|
4631
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
4632
|
+
}
|
|
4633
|
+
function cleanPayloadString(value) {
|
|
4634
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4635
|
+
}
|
|
4636
|
+
function remoteGitHubAuthMetadata(payload) {
|
|
4637
|
+
if (!payload)
|
|
4638
|
+
return {};
|
|
4639
|
+
const userNamespace = payload.userNamespace && typeof payload.userNamespace === "object" && !Array.isArray(payload.userNamespace) ? payload.userNamespace : null;
|
|
4640
|
+
return {
|
|
4641
|
+
...cleanPayloadString(payload.login) ? { login: cleanPayloadString(payload.login) } : {},
|
|
4642
|
+
...cleanPayloadString(payload.userId) ? { userId: cleanPayloadString(payload.userId) } : {},
|
|
4643
|
+
...cleanPayloadString(userNamespace?.key) ? { userNamespaceKey: cleanPayloadString(userNamespace?.key) } : {},
|
|
4644
|
+
...cleanPayloadString(userNamespace?.root) ? { userNamespaceRoot: cleanPayloadString(userNamespace?.root) } : {},
|
|
4645
|
+
...cleanPayloadString(userNamespace?.checkoutBaseDir) ? { checkoutBaseDir: cleanPayloadString(userNamespace?.checkoutBaseDir) } : {},
|
|
4646
|
+
...cleanPayloadString(userNamespace?.snapshotBaseDir) ? { snapshotBaseDir: cleanPayloadString(userNamespace?.snapshotBaseDir) } : {}
|
|
4647
|
+
};
|
|
4648
|
+
}
|
|
4649
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
4650
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
4651
|
+
authenticated: true,
|
|
4652
|
+
source: input.source,
|
|
4653
|
+
storedOnServer: true,
|
|
4654
|
+
selectedRepo: input.selectedRepo,
|
|
4655
|
+
...remoteGitHubAuthMetadata(input.authPayload ?? null),
|
|
4656
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
4657
|
+
updatedAt: new Date().toISOString()
|
|
4658
|
+
}, null, 2)}
|
|
4659
|
+
`, "utf8");
|
|
4660
|
+
}
|
|
4661
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
4581
4662
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
4582
4663
|
return null;
|
|
4583
|
-
const
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4664
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
4665
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
4666
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
4667
|
+
const deadline = Date.now() + timeoutMs;
|
|
4668
|
+
let last = null;
|
|
4669
|
+
do {
|
|
4670
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
4671
|
+
method: "POST",
|
|
4672
|
+
headers: { "content-type": "application/json" },
|
|
4673
|
+
body: JSON.stringify({ pollId })
|
|
4674
|
+
}).catch(() => null);
|
|
4675
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4676
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
4677
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
4678
|
+
return last;
|
|
4679
|
+
}
|
|
4680
|
+
if (timeoutMs <= 0)
|
|
4681
|
+
return last;
|
|
4682
|
+
await sleep2(intervalMs);
|
|
4683
|
+
} while (Date.now() < deadline);
|
|
4684
|
+
return last;
|
|
4589
4685
|
}
|
|
4590
4686
|
async function runControlPlaneInit(context, options) {
|
|
4591
4687
|
const projectRoot = context.projectRoot;
|
|
@@ -4608,9 +4704,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
4608
4704
|
});
|
|
4609
4705
|
ensureRigPrivateDirs(projectRoot);
|
|
4610
4706
|
ensureGitignoreEntries(projectRoot);
|
|
4611
|
-
const configTsPath =
|
|
4612
|
-
const configJsonPath =
|
|
4613
|
-
const configExists =
|
|
4707
|
+
const configTsPath = resolve17(projectRoot, "rig.config.ts");
|
|
4708
|
+
const configJsonPath = resolve17(projectRoot, "rig.config.json");
|
|
4709
|
+
const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
|
|
4614
4710
|
if (!options.privateStateOnly) {
|
|
4615
4711
|
if (configExists && !options.repair) {
|
|
4616
4712
|
if (context.outputMode !== "json")
|
|
@@ -4626,7 +4722,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
4626
4722
|
}
|
|
4627
4723
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
4628
4724
|
}
|
|
4629
|
-
writeFileSync5(
|
|
4725
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
4630
4726
|
`, "utf8");
|
|
4631
4727
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
4632
4728
|
let uploadedSnapshot = null;
|
|
@@ -4648,10 +4744,15 @@ async function runControlPlaneInit(context, options) {
|
|
|
4648
4744
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
4649
4745
|
if (token) {
|
|
4650
4746
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
4651
|
-
|
|
4747
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4748
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4652
4749
|
if (serverKind === "remote") {
|
|
4653
|
-
|
|
4654
|
-
|
|
4750
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
4751
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
4752
|
+
selectedRepo: repo.slug,
|
|
4753
|
+
apiSessionToken,
|
|
4754
|
+
authPayload: githubAuth
|
|
4755
|
+
});
|
|
4655
4756
|
}
|
|
4656
4757
|
} else if (authMethod === "device") {
|
|
4657
4758
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -4660,9 +4761,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
4660
4761
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
4661
4762
|
});
|
|
4662
4763
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4663
|
-
|
|
4664
|
-
|
|
4764
|
+
if (context.outputMode !== "json") {
|
|
4765
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
4766
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
4767
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
4768
|
+
}
|
|
4769
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
4770
|
+
if (completed) {
|
|
4771
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
4772
|
+
if (apiSessionToken) {
|
|
4773
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
4774
|
+
if (serverKind === "remote") {
|
|
4775
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken, authPayload: completed });
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4665
4778
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
4779
|
+
}
|
|
4666
4780
|
}
|
|
4667
4781
|
let remoteCheckoutPreparation = null;
|
|
4668
4782
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -4682,6 +4796,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
4682
4796
|
});
|
|
4683
4797
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
4684
4798
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
4799
|
+
if (serverRootSwitch && token) {
|
|
4800
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
4801
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4802
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4803
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken, authPayload: githubAuth });
|
|
4804
|
+
}
|
|
4685
4805
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
4686
4806
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
4687
4807
|
remote: true,
|
|
@@ -4791,7 +4911,7 @@ function parseInitOptions(args) {
|
|
|
4791
4911
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
4792
4912
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
4793
4913
|
const projectRoot = context.projectRoot;
|
|
4794
|
-
const existingConfig =
|
|
4914
|
+
const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
|
|
4795
4915
|
let repair = false;
|
|
4796
4916
|
let privateStateOnly = false;
|
|
4797
4917
|
if (existingConfig) {
|
|
@@ -4891,7 +5011,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
4891
5011
|
});
|
|
4892
5012
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
4893
5013
|
const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
|
|
4894
|
-
const deviceMessage = deviceAuth ? ` GitHub device flow: open ${String(deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server")} and enter ${String(deviceAuth.user_code ?? "the returned user code")}.` : "";
|
|
5014
|
+
const deviceMessage = deviceAuth ? ` GitHub device flow: open ${String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server")} and enter ${String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code")}.` : "";
|
|
4895
5015
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
4896
5016
|
return result;
|
|
4897
5017
|
}
|
|
@@ -5053,8 +5173,8 @@ async function executeDoctor(context, args) {
|
|
|
5053
5173
|
}
|
|
5054
5174
|
|
|
5055
5175
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
5056
|
-
import { readFileSync as
|
|
5057
|
-
import { resolve as
|
|
5176
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
5177
|
+
import { resolve as resolve18 } from "path";
|
|
5058
5178
|
import {
|
|
5059
5179
|
appendJsonlRecord as appendJsonlRecord2,
|
|
5060
5180
|
readAuthorityRun as readAuthorityRun2,
|
|
@@ -5074,7 +5194,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
|
|
|
5074
5194
|
...patch,
|
|
5075
5195
|
updatedAt: new Date().toISOString()
|
|
5076
5196
|
};
|
|
5077
|
-
writeJsonFile4(
|
|
5197
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
|
|
5078
5198
|
return next;
|
|
5079
5199
|
}
|
|
5080
5200
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -5082,21 +5202,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
5082
5202
|
if (!current) {
|
|
5083
5203
|
return;
|
|
5084
5204
|
}
|
|
5085
|
-
writeJsonFile4(
|
|
5205
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
|
|
5086
5206
|
...current,
|
|
5087
5207
|
updatedAt: new Date().toISOString()
|
|
5088
5208
|
});
|
|
5089
5209
|
}
|
|
5090
5210
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
5091
|
-
appendJsonlRecord2(
|
|
5211
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
|
|
5092
5212
|
touchAuthorityRun(projectRoot, runId);
|
|
5093
5213
|
}
|
|
5094
5214
|
function appendRunLog(projectRoot, runId, value) {
|
|
5095
|
-
appendJsonlRecord2(
|
|
5215
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
|
|
5096
5216
|
touchAuthorityRun(projectRoot, runId);
|
|
5097
5217
|
}
|
|
5098
5218
|
function appendRunAction(projectRoot, runId, value) {
|
|
5099
|
-
appendJsonlRecord2(
|
|
5219
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
|
|
5100
5220
|
id: value.id,
|
|
5101
5221
|
type: "action",
|
|
5102
5222
|
actionType: value.actionType,
|
|
@@ -5167,7 +5287,7 @@ function buildRunPrompt(input) {
|
|
|
5167
5287
|
})();
|
|
5168
5288
|
const scopeText = (() => {
|
|
5169
5289
|
try {
|
|
5170
|
-
const parsed = JSON.parse(
|
|
5290
|
+
const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
|
|
5171
5291
|
const entry = parsed[input.taskId] ?? {};
|
|
5172
5292
|
const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
|
|
5173
5293
|
const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
|
|
@@ -5280,8 +5400,8 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
5280
5400
|
}
|
|
5281
5401
|
|
|
5282
5402
|
// packages/cli/src/commands/inspect.ts
|
|
5283
|
-
import { existsSync as
|
|
5284
|
-
import { resolve as
|
|
5403
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
5404
|
+
import { resolve as resolve19 } from "path";
|
|
5285
5405
|
import {
|
|
5286
5406
|
listAuthorityRuns as listAuthorityRuns2,
|
|
5287
5407
|
readAuthorityRun as readAuthorityRun3,
|
|
@@ -5302,8 +5422,8 @@ async function executeInspect(context, args) {
|
|
|
5302
5422
|
if (!latestRun) {
|
|
5303
5423
|
throw new CliError2(`No runs found for ${requiredTask}.`);
|
|
5304
5424
|
}
|
|
5305
|
-
const logsPath =
|
|
5306
|
-
if (!
|
|
5425
|
+
const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
5426
|
+
if (!existsSync11(logsPath)) {
|
|
5307
5427
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
5308
5428
|
}
|
|
5309
5429
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -5313,7 +5433,7 @@ async function executeInspect(context, args) {
|
|
|
5313
5433
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
5314
5434
|
requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
|
|
5315
5435
|
const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
|
|
5316
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
5436
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
|
|
5317
5437
|
if (!artifactRoot) {
|
|
5318
5438
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
5319
5439
|
}
|
|
@@ -5370,10 +5490,10 @@ async function executeInspect(context, args) {
|
|
|
5370
5490
|
case "failures": {
|
|
5371
5491
|
requireNoExtraArgs(rest, "bun run rig inspect failures");
|
|
5372
5492
|
const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
|
|
5373
|
-
if (!
|
|
5493
|
+
if (!existsSync11(failed)) {
|
|
5374
5494
|
console.log("No failures recorded.");
|
|
5375
5495
|
} else {
|
|
5376
|
-
process.stdout.write(
|
|
5496
|
+
process.stdout.write(readFileSync8(failed, "utf-8"));
|
|
5377
5497
|
}
|
|
5378
5498
|
return { ok: true, group: "inspect", command };
|
|
5379
5499
|
}
|
|
@@ -5390,11 +5510,11 @@ async function executeInspect(context, args) {
|
|
|
5390
5510
|
return { ok: true, group: "inspect", command };
|
|
5391
5511
|
case "audit": {
|
|
5392
5512
|
requireNoExtraArgs(rest, "bun run rig inspect audit");
|
|
5393
|
-
const auditPath =
|
|
5394
|
-
if (!
|
|
5513
|
+
const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
|
|
5514
|
+
if (!existsSync11(auditPath)) {
|
|
5395
5515
|
console.log("No audit log found.");
|
|
5396
5516
|
} else {
|
|
5397
|
-
const lines =
|
|
5517
|
+
const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
5398
5518
|
for (const line of lines) {
|
|
5399
5519
|
console.log(line);
|
|
5400
5520
|
}
|
|
@@ -6023,8 +6143,8 @@ async function executeRemote(context, args) {
|
|
|
6023
6143
|
}
|
|
6024
6144
|
|
|
6025
6145
|
// packages/cli/src/commands/run.ts
|
|
6026
|
-
import { existsSync as
|
|
6027
|
-
import { resolve as
|
|
6146
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
6147
|
+
import { resolve as resolve20 } from "path";
|
|
6028
6148
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6029
6149
|
import {
|
|
6030
6150
|
listAuthorityRuns as listAuthorityRuns3,
|
|
@@ -6038,6 +6158,7 @@ import {
|
|
|
6038
6158
|
listOpenEpics,
|
|
6039
6159
|
resolveDefaultEpic,
|
|
6040
6160
|
runResume,
|
|
6161
|
+
runRestart,
|
|
6041
6162
|
runStatus,
|
|
6042
6163
|
runStop,
|
|
6043
6164
|
startRun,
|
|
@@ -6146,6 +6267,17 @@ async function attachRunOperatorView(context, input) {
|
|
|
6146
6267
|
}
|
|
6147
6268
|
|
|
6148
6269
|
// packages/cli/src/commands/run.ts
|
|
6270
|
+
function normalizeRemoteRunDetails(payload) {
|
|
6271
|
+
const run = payload.run;
|
|
6272
|
+
if (!run || typeof run !== "object" || Array.isArray(run))
|
|
6273
|
+
return null;
|
|
6274
|
+
return {
|
|
6275
|
+
...run,
|
|
6276
|
+
...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
|
|
6277
|
+
...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
|
|
6278
|
+
...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
|
|
6279
|
+
};
|
|
6280
|
+
}
|
|
6149
6281
|
function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
|
|
6150
6282
|
if (noEpicPrompt) {
|
|
6151
6283
|
return false;
|
|
@@ -6284,7 +6416,7 @@ async function executeRun(context, args) {
|
|
|
6284
6416
|
if (!run.value) {
|
|
6285
6417
|
throw new CliError2("run show requires --run <id>.");
|
|
6286
6418
|
}
|
|
6287
|
-
const record = readAuthorityRun4(context.projectRoot, run.value);
|
|
6419
|
+
const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
|
|
6288
6420
|
if (!record) {
|
|
6289
6421
|
throw new CliError2(`Run not found: ${run.value}`, 2);
|
|
6290
6422
|
}
|
|
@@ -6303,7 +6435,7 @@ async function executeRun(context, args) {
|
|
|
6303
6435
|
if (!run.value) {
|
|
6304
6436
|
throw new CliError2("run timeline requires --run <id>.");
|
|
6305
6437
|
}
|
|
6306
|
-
const timelinePath =
|
|
6438
|
+
const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
|
|
6307
6439
|
const printEvents = () => {
|
|
6308
6440
|
const events2 = readJsonlFile4(timelinePath);
|
|
6309
6441
|
if (context.outputMode === "text") {
|
|
@@ -6315,12 +6447,12 @@ async function executeRun(context, args) {
|
|
|
6315
6447
|
};
|
|
6316
6448
|
const events = printEvents();
|
|
6317
6449
|
if (follow.value && context.outputMode === "text") {
|
|
6318
|
-
let lastLength =
|
|
6450
|
+
let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
|
|
6319
6451
|
while (true) {
|
|
6320
6452
|
await Bun.sleep(1000);
|
|
6321
|
-
if (!
|
|
6453
|
+
if (!existsSync12(timelinePath))
|
|
6322
6454
|
continue;
|
|
6323
|
-
const next =
|
|
6455
|
+
const next = readFileSync9(timelinePath, "utf8");
|
|
6324
6456
|
if (next.length <= lastLength)
|
|
6325
6457
|
continue;
|
|
6326
6458
|
const delta = next.slice(lastLength);
|
|
@@ -6465,6 +6597,20 @@ async function executeRun(context, args) {
|
|
|
6465
6597
|
}
|
|
6466
6598
|
return { ok: true, group: "run", command, details: resumed };
|
|
6467
6599
|
}
|
|
6600
|
+
case "restart": {
|
|
6601
|
+
requireNoExtraArgs(rest, "bun run rig run restart");
|
|
6602
|
+
if (context.dryRun) {
|
|
6603
|
+
if (context.outputMode === "text") {
|
|
6604
|
+
console.log("[dry-run] rig run restart");
|
|
6605
|
+
}
|
|
6606
|
+
return { ok: true, group: "run", command };
|
|
6607
|
+
}
|
|
6608
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
6609
|
+
if (context.outputMode === "text") {
|
|
6610
|
+
console.log(`Restarted run: ${restarted.runId}`);
|
|
6611
|
+
}
|
|
6612
|
+
return { ok: true, group: "run", command, details: restarted };
|
|
6613
|
+
}
|
|
6468
6614
|
case "stop": {
|
|
6469
6615
|
const runOption = takeOption(rest, "--run");
|
|
6470
6616
|
const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
|
|
@@ -6522,7 +6668,7 @@ async function executeServer(context, args, options) {
|
|
|
6522
6668
|
const authTokenResult = takeOption(pending, "--auth-token");
|
|
6523
6669
|
pending = authTokenResult.rest;
|
|
6524
6670
|
requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
|
|
6525
|
-
const commandParts = ["
|
|
6671
|
+
const commandParts = ["rig-server", "start"];
|
|
6526
6672
|
if (hostResult.value) {
|
|
6527
6673
|
commandParts.push("--host", hostResult.value);
|
|
6528
6674
|
}
|
|
@@ -6545,7 +6691,7 @@ async function executeServer(context, args, options) {
|
|
|
6545
6691
|
const eventResult = takeOption(pending, "--event");
|
|
6546
6692
|
pending = eventResult.rest;
|
|
6547
6693
|
requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
|
|
6548
|
-
const commandParts = ["
|
|
6694
|
+
const commandParts = ["rig-server", "notify-test"];
|
|
6549
6695
|
if (eventResult.value) {
|
|
6550
6696
|
commandParts.push("--event", eventResult.value);
|
|
6551
6697
|
}
|
|
@@ -6606,10 +6752,10 @@ async function executeServer(context, args, options) {
|
|
|
6606
6752
|
}
|
|
6607
6753
|
|
|
6608
6754
|
// packages/cli/src/commands/task.ts
|
|
6609
|
-
import { readFileSync as
|
|
6755
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6610
6756
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6611
6757
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
6612
|
-
import { resolve as
|
|
6758
|
+
import { resolve as resolve21 } from "path";
|
|
6613
6759
|
import {
|
|
6614
6760
|
taskArtifactDir,
|
|
6615
6761
|
taskArtifacts,
|
|
@@ -6940,7 +7086,7 @@ async function executeTask(context, args, options) {
|
|
|
6940
7086
|
const fileFlag = takeOption(rest.slice(1), "--file");
|
|
6941
7087
|
let content;
|
|
6942
7088
|
if (fileFlag.value) {
|
|
6943
|
-
content =
|
|
7089
|
+
content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
|
|
6944
7090
|
} else {
|
|
6945
7091
|
content = await readStdin();
|
|
6946
7092
|
}
|
|
@@ -7161,8 +7307,8 @@ async function executeTask(context, args, options) {
|
|
|
7161
7307
|
}
|
|
7162
7308
|
|
|
7163
7309
|
// packages/cli/src/commands/task-run-driver.ts
|
|
7164
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
7165
|
-
import { resolve as
|
|
7310
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7311
|
+
import { resolve as resolve22 } from "path";
|
|
7166
7312
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
7167
7313
|
import { createInterface as createLineInterface } from "readline";
|
|
7168
7314
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
@@ -7196,7 +7342,24 @@ import {
|
|
|
7196
7342
|
commitRunChanges,
|
|
7197
7343
|
runPrAutomation
|
|
7198
7344
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
7345
|
+
function looksLikeGitHubToken(value) {
|
|
7346
|
+
const token = value?.trim();
|
|
7347
|
+
if (!token)
|
|
7348
|
+
return false;
|
|
7349
|
+
return /^(gh[opusr]_|github_pat_)/.test(token);
|
|
7350
|
+
}
|
|
7351
|
+
function githubBridgeEnv(token) {
|
|
7352
|
+
const clean = token?.trim();
|
|
7353
|
+
if (!clean)
|
|
7354
|
+
return {};
|
|
7355
|
+
return {
|
|
7356
|
+
RIG_GITHUB_TOKEN: clean,
|
|
7357
|
+
GITHUB_TOKEN: clean,
|
|
7358
|
+
GH_TOKEN: clean
|
|
7359
|
+
};
|
|
7360
|
+
}
|
|
7199
7361
|
function buildPiRigBridgeEnv(input) {
|
|
7362
|
+
const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
|
|
7200
7363
|
return {
|
|
7201
7364
|
RIG_PROJECT_ROOT: input.projectRoot,
|
|
7202
7365
|
PROJECT_RIG_ROOT: input.projectRoot,
|
|
@@ -7206,7 +7369,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
7206
7369
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
7207
7370
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
7208
7371
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
7209
|
-
...
|
|
7372
|
+
...githubBridgeEnv(githubToken)
|
|
7210
7373
|
};
|
|
7211
7374
|
}
|
|
7212
7375
|
function runGitSync(cwd, args, input) {
|
|
@@ -7228,12 +7391,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
7228
7391
|
return 0;
|
|
7229
7392
|
let copied = 0;
|
|
7230
7393
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
7231
|
-
const sourcePath =
|
|
7232
|
-
const targetPath =
|
|
7394
|
+
const sourcePath = resolve22(sourceRoot, relativePath);
|
|
7395
|
+
const targetPath = resolve22(targetRoot, relativePath);
|
|
7233
7396
|
try {
|
|
7234
7397
|
if (!statSync2(sourcePath).isFile())
|
|
7235
7398
|
continue;
|
|
7236
|
-
mkdirSync8(
|
|
7399
|
+
mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
|
|
7237
7400
|
copyFileSync3(sourcePath, targetPath);
|
|
7238
7401
|
copied += 1;
|
|
7239
7402
|
} catch {}
|
|
@@ -7267,6 +7430,14 @@ function buildTaskRunReviewEnv(config) {
|
|
|
7267
7430
|
...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
|
|
7268
7431
|
};
|
|
7269
7432
|
}
|
|
7433
|
+
function buildDirtyBaselineHandshakeEnv(input) {
|
|
7434
|
+
if (input.baselineMode !== "dirty-snapshot")
|
|
7435
|
+
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
7436
|
+
return {
|
|
7437
|
+
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
7438
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
7439
|
+
};
|
|
7440
|
+
}
|
|
7270
7441
|
function positiveInt(value, fallback) {
|
|
7271
7442
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
7272
7443
|
}
|
|
@@ -7375,9 +7546,9 @@ function createCommandRunner(binary) {
|
|
|
7375
7546
|
const stderrChunks = [];
|
|
7376
7547
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7377
7548
|
child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7378
|
-
return await new Promise((
|
|
7379
|
-
child.once("error", (error) =>
|
|
7380
|
-
child.once("close", (code) =>
|
|
7549
|
+
return await new Promise((resolve23) => {
|
|
7550
|
+
child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
|
|
7551
|
+
child.once("close", (code) => resolve23({
|
|
7381
7552
|
exitCode: code ?? 1,
|
|
7382
7553
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
7383
7554
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -7573,7 +7744,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
|
|
|
7573
7744
|
return null;
|
|
7574
7745
|
}
|
|
7575
7746
|
for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
|
|
7576
|
-
const summary = readJsonFile3(
|
|
7747
|
+
const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
|
|
7577
7748
|
if (!summary || summary.status !== "fail") {
|
|
7578
7749
|
continue;
|
|
7579
7750
|
}
|
|
@@ -7654,9 +7825,9 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
7654
7825
|
if (!input.taskId || !input.workspaceDir) {
|
|
7655
7826
|
return { accepted: false, reason: null };
|
|
7656
7827
|
}
|
|
7657
|
-
const artifactDir =
|
|
7658
|
-
const reviewStatusPath =
|
|
7659
|
-
const taskResultPath =
|
|
7828
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7829
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7830
|
+
const taskResultPath = resolve22(artifactDir, "task-result.json");
|
|
7660
7831
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7661
7832
|
if (reviewStatus !== "APPROVED") {
|
|
7662
7833
|
return { accepted: false, reason: null };
|
|
@@ -7693,12 +7864,12 @@ function resolveTaskRunRetryContext(input) {
|
|
|
7693
7864
|
if (!input.taskId || !input.workspaceDir) {
|
|
7694
7865
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
7695
7866
|
}
|
|
7696
|
-
const artifactDir =
|
|
7697
|
-
const reviewStatePath =
|
|
7698
|
-
const reviewFeedbackPath =
|
|
7699
|
-
const reviewStatusPath =
|
|
7700
|
-
const failedApproachesPath =
|
|
7701
|
-
const validationSummaryPath =
|
|
7867
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7868
|
+
const reviewStatePath = resolve22(artifactDir, "review-state.json");
|
|
7869
|
+
const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
|
|
7870
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7871
|
+
const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
7872
|
+
const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
|
|
7702
7873
|
const reviewState = readJsonFile3(reviewStatePath, null);
|
|
7703
7874
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7704
7875
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
@@ -7753,11 +7924,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
7753
7924
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
7754
7925
|
}
|
|
7755
7926
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
7756
|
-
if (!
|
|
7927
|
+
if (!existsSync13(reviewStatusPath)) {
|
|
7757
7928
|
return null;
|
|
7758
7929
|
}
|
|
7759
7930
|
try {
|
|
7760
|
-
const status =
|
|
7931
|
+
const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
7761
7932
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
7762
7933
|
} catch {
|
|
7763
7934
|
return null;
|
|
@@ -7840,8 +8011,11 @@ function stringArrayField(record, key) {
|
|
|
7840
8011
|
}
|
|
7841
8012
|
async function executeRigOwnedTaskRun(context, input) {
|
|
7842
8013
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
8014
|
+
const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
|
|
8015
|
+
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
8016
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
7843
8017
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
7844
|
-
|
|
8018
|
+
let prompt = buildRunPrompt({
|
|
7845
8019
|
projectRoot: context.projectRoot,
|
|
7846
8020
|
taskId: input.taskId,
|
|
7847
8021
|
fallbackTitle: input.title,
|
|
@@ -7895,14 +8069,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7895
8069
|
taskId: runtimeTaskId,
|
|
7896
8070
|
createdAt: startedAt,
|
|
7897
8071
|
runtimeAdapter: input.runtimeAdapter,
|
|
7898
|
-
status: "created"
|
|
8072
|
+
status: resumeMode ? "preparing" : "created"
|
|
7899
8073
|
});
|
|
7900
8074
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7901
8075
|
status: "preparing",
|
|
7902
8076
|
startedAt,
|
|
7903
8077
|
completedAt: null,
|
|
7904
8078
|
errorText: null,
|
|
7905
|
-
artifactRoot: null,
|
|
8079
|
+
artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
|
|
7906
8080
|
runtimeAdapter: input.runtimeAdapter,
|
|
7907
8081
|
runtimeMode: input.runtimeMode,
|
|
7908
8082
|
interactionMode: input.interactionMode,
|
|
@@ -7918,9 +8092,9 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7918
8092
|
detail: input.taskId ?? input.title ?? runtimeTaskId
|
|
7919
8093
|
});
|
|
7920
8094
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7921
|
-
id: `log:${input.runId}:start`,
|
|
7922
|
-
title: "Rig task run started",
|
|
7923
|
-
detail: input.taskId ?? input.title ?? runtimeTaskId,
|
|
8095
|
+
id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
|
|
8096
|
+
title: resumeMode ? "Rig task run resumed" : "Rig task run started",
|
|
8097
|
+
detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
|
|
7924
8098
|
tone: "info",
|
|
7925
8099
|
status: "preparing",
|
|
7926
8100
|
createdAt: startedAt
|
|
@@ -7941,7 +8115,22 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7941
8115
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
7942
8116
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
7943
8117
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
7944
|
-
|
|
8118
|
+
const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
8119
|
+
const persistedPlanning = {
|
|
8120
|
+
...planningClassification,
|
|
8121
|
+
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
8122
|
+
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
8123
|
+
classifiedAt: new Date().toISOString()
|
|
8124
|
+
};
|
|
8125
|
+
mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
8126
|
+
writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
8127
|
+
`, "utf8");
|
|
8128
|
+
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
8129
|
+
prompt = `${prompt}
|
|
8130
|
+
|
|
8131
|
+
Rig planning classification:
|
|
8132
|
+
${JSON.stringify(persistedPlanning, null, 2)}
|
|
8133
|
+
${planningClassification.planningRequired ? `Before implementing, write a concise implementation plan to ${planningArtifactPath}. Treat that plan artifact as required acceptance evidence for the Plan stage.` : "Planning is not required for this run; briefly state why before implementation."}`;
|
|
7945
8134
|
if (input.runtimeAdapter === "pi") {
|
|
7946
8135
|
for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
|
|
7947
8136
|
appendPiStageLog({
|
|
@@ -7983,11 +8172,11 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7983
8172
|
let reviewAction;
|
|
7984
8173
|
let verificationStarted = false;
|
|
7985
8174
|
let reviewStarted = false;
|
|
7986
|
-
let latestRuntimeWorkspace = null;
|
|
7987
|
-
let latestSessionDir = null;
|
|
7988
|
-
let latestLogsDir = null;
|
|
8175
|
+
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
8176
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
|
|
8177
|
+
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
7989
8178
|
let latestProviderCommand = null;
|
|
7990
|
-
let latestRuntimeBranch = null;
|
|
8179
|
+
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
7991
8180
|
let snapshotSidecarPromise = null;
|
|
7992
8181
|
let dirtyBaselineApplied = false;
|
|
7993
8182
|
const childEnv = {
|
|
@@ -8005,9 +8194,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8005
8194
|
RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
|
|
8006
8195
|
RIG_SERVER_RUN_ID: input.runId
|
|
8007
8196
|
},
|
|
8008
|
-
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
8197
|
+
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
|
|
8198
|
+
...resumeMode ? {
|
|
8199
|
+
RIG_RUN_RESUME: "1",
|
|
8200
|
+
RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
|
|
8201
|
+
} : {}
|
|
8009
8202
|
};
|
|
8010
8203
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
8204
|
+
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
8011
8205
|
const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
|
|
8012
8206
|
const maxAttempts = automationLimits.maxValidationAttempts;
|
|
8013
8207
|
const promoteToValidating = (detail) => {
|
|
@@ -8062,22 +8256,29 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8062
8256
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8063
8257
|
status: "running",
|
|
8064
8258
|
worktreePath: latestRuntimeWorkspace,
|
|
8065
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
8259
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
8066
8260
|
logRoot: latestLogsDir,
|
|
8067
|
-
sessionPath: latestSessionDir ?
|
|
8068
|
-
sessionLogPath: latestLogsDir ?
|
|
8261
|
+
sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
|
|
8262
|
+
sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
|
|
8069
8263
|
branch: runtimeId
|
|
8070
8264
|
});
|
|
8071
8265
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
8072
8266
|
dirtyBaselineApplied = true;
|
|
8073
8267
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
8268
|
+
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
8269
|
+
if (readyFile) {
|
|
8270
|
+
mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
|
|
8271
|
+
writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
8272
|
+
`, "utf8");
|
|
8273
|
+
}
|
|
8074
8274
|
appendRunLog(context.projectRoot, input.runId, {
|
|
8075
8275
|
id: `log:${input.runId}:dirty-baseline`,
|
|
8076
8276
|
title: "Dirty baseline snapshot",
|
|
8077
8277
|
detail: dirty.detail,
|
|
8078
8278
|
tone: dirty.applied ? "tool" : "info",
|
|
8079
8279
|
status: dirty.applied ? "completed" : "skipped",
|
|
8080
|
-
createdAt: new Date().toISOString()
|
|
8280
|
+
createdAt: new Date().toISOString(),
|
|
8281
|
+
payload: readyFile ? { readyFile } : undefined
|
|
8081
8282
|
});
|
|
8082
8283
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
8083
8284
|
}
|
|
@@ -8303,7 +8504,36 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8303
8504
|
let reviewFailureDetail = null;
|
|
8304
8505
|
const stderrLines = [];
|
|
8305
8506
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
8306
|
-
|
|
8507
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
8508
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8509
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
8510
|
+
title: "Resume continuing from closeout phase",
|
|
8511
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
8512
|
+
tone: "info",
|
|
8513
|
+
status: resumePreviousStatus,
|
|
8514
|
+
createdAt: new Date().toISOString()
|
|
8515
|
+
});
|
|
8516
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
8517
|
+
exit = { code: 0, signal: null };
|
|
8518
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
8519
|
+
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
8520
|
+
taskId: input.taskId ?? runtimeTaskId,
|
|
8521
|
+
workspaceDir: latestRuntimeWorkspace
|
|
8522
|
+
});
|
|
8523
|
+
if (acceptedArtifactState.accepted) {
|
|
8524
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8525
|
+
id: `log:${input.runId}:resume-accepted-artifacts`,
|
|
8526
|
+
title: "Resume found accepted artifacts; continuing closeout",
|
|
8527
|
+
detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
|
|
8528
|
+
tone: "info",
|
|
8529
|
+
status: "validating",
|
|
8530
|
+
createdAt: new Date().toISOString()
|
|
8531
|
+
});
|
|
8532
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
|
|
8533
|
+
exit = { code: 0, signal: null };
|
|
8534
|
+
}
|
|
8535
|
+
}
|
|
8536
|
+
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
8307
8537
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
8308
8538
|
const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
8309
8539
|
cwd: context.projectRoot,
|
|
@@ -8344,7 +8574,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8344
8574
|
let acceptedArtifactObservedAt = null;
|
|
8345
8575
|
let acceptedArtifactPollTimer = null;
|
|
8346
8576
|
let acceptedArtifactKillTimer = null;
|
|
8347
|
-
const attemptExit = await new Promise((
|
|
8577
|
+
const attemptExit = await new Promise((resolve23) => {
|
|
8348
8578
|
let settled = false;
|
|
8349
8579
|
const settle = (result) => {
|
|
8350
8580
|
if (settled)
|
|
@@ -8352,7 +8582,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8352
8582
|
settled = true;
|
|
8353
8583
|
if (acceptedArtifactPollTimer)
|
|
8354
8584
|
clearInterval(acceptedArtifactPollTimer);
|
|
8355
|
-
|
|
8585
|
+
resolve23(result);
|
|
8356
8586
|
};
|
|
8357
8587
|
const pollAcceptedArtifacts = () => {
|
|
8358
8588
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -8549,6 +8779,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8549
8779
|
});
|
|
8550
8780
|
throw new CliError2(terminalFailureDetail, exit.code ?? 1);
|
|
8551
8781
|
}
|
|
8782
|
+
if (planningClassification.planningRequired) {
|
|
8783
|
+
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
8784
|
+
const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
|
|
8785
|
+
if (!existsSync13(expectedPlanPath)) {
|
|
8786
|
+
const failedAt = new Date().toISOString();
|
|
8787
|
+
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
8788
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8789
|
+
status: "needs_attention",
|
|
8790
|
+
completedAt: failedAt,
|
|
8791
|
+
errorText: failureDetail
|
|
8792
|
+
});
|
|
8793
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8794
|
+
id: `log:${input.runId}:plan-artifact-missing`,
|
|
8795
|
+
title: "Required plan artifact missing",
|
|
8796
|
+
detail: failureDetail,
|
|
8797
|
+
tone: "error",
|
|
8798
|
+
status: "needs_attention",
|
|
8799
|
+
createdAt: failedAt
|
|
8800
|
+
});
|
|
8801
|
+
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
8802
|
+
throw new CliError2(failureDetail, 1);
|
|
8803
|
+
}
|
|
8804
|
+
}
|
|
8552
8805
|
const runPiPrFeedbackFix = async (message2) => {
|
|
8553
8806
|
appendPiStageLog({
|
|
8554
8807
|
projectRoot: context.projectRoot,
|
|
@@ -8606,9 +8859,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8606
8859
|
});
|
|
8607
8860
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
8608
8861
|
});
|
|
8609
|
-
const exitCode = await new Promise((
|
|
8610
|
-
child.once("error", () =>
|
|
8611
|
-
child.once("close", (code) =>
|
|
8862
|
+
const exitCode = await new Promise((resolve23) => {
|
|
8863
|
+
child.once("error", () => resolve23(1));
|
|
8864
|
+
child.once("close", (code) => resolve23(code ?? 1));
|
|
8612
8865
|
});
|
|
8613
8866
|
if (exitCode !== 0) {
|
|
8614
8867
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
@@ -8727,8 +8980,8 @@ async function executeTest(context, args) {
|
|
|
8727
8980
|
}
|
|
8728
8981
|
|
|
8729
8982
|
// packages/cli/src/commands/setup.ts
|
|
8730
|
-
import { existsSync as
|
|
8731
|
-
import { resolve as
|
|
8983
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
8984
|
+
import { resolve as resolve23 } from "path";
|
|
8732
8985
|
import { createPluginHost } from "@rig/core";
|
|
8733
8986
|
import {
|
|
8734
8987
|
isSupportedBunVersion as isSupportedBunVersion2,
|
|
@@ -8791,9 +9044,9 @@ function runSetupInit(projectRoot) {
|
|
|
8791
9044
|
mkdirSync9(stateDir, { recursive: true });
|
|
8792
9045
|
mkdirSync9(logsDir, { recursive: true });
|
|
8793
9046
|
mkdirSync9(artifactsDir, { recursive: true });
|
|
8794
|
-
const failuresPath =
|
|
8795
|
-
if (!
|
|
8796
|
-
|
|
9047
|
+
const failuresPath = resolve23(stateDir, "failed_approaches.md");
|
|
9048
|
+
if (!existsSync14(failuresPath)) {
|
|
9049
|
+
writeFileSync7(failuresPath, `# Failed Approaches
|
|
8797
9050
|
|
|
8798
9051
|
`, "utf-8");
|
|
8799
9052
|
}
|
|
@@ -8810,18 +9063,18 @@ async function runSetupCheck(projectRoot) {
|
|
|
8810
9063
|
}
|
|
8811
9064
|
async function runSetupPreflight(projectRoot) {
|
|
8812
9065
|
await runSetupCheck(projectRoot);
|
|
8813
|
-
const validationRoot =
|
|
8814
|
-
if (
|
|
9066
|
+
const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
9067
|
+
if (existsSync14(validationRoot)) {
|
|
8815
9068
|
const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
8816
9069
|
for (const validator of validators) {
|
|
8817
|
-
const script =
|
|
8818
|
-
if (
|
|
9070
|
+
const script = resolve23(validationRoot, validator.name, "validate.sh");
|
|
9071
|
+
if (existsSync14(script)) {
|
|
8819
9072
|
console.log(`OK: validator script ${script}`);
|
|
8820
9073
|
}
|
|
8821
9074
|
}
|
|
8822
9075
|
}
|
|
8823
|
-
const hooksRoot =
|
|
8824
|
-
if (
|
|
9076
|
+
const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
|
|
9077
|
+
if (existsSync14(hooksRoot)) {
|
|
8825
9078
|
const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
|
|
8826
9079
|
for (const hook of hooks) {
|
|
8827
9080
|
console.log(`OK: hook ${hook}`);
|
|
@@ -9198,8 +9451,8 @@ async function executeGroup(context, group, args) {
|
|
|
9198
9451
|
}
|
|
9199
9452
|
}
|
|
9200
9453
|
// packages/cli/src/launcher.ts
|
|
9201
|
-
import { existsSync as
|
|
9202
|
-
import { resolve as
|
|
9454
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9455
|
+
import { basename as basename2, resolve as resolve24 } from "path";
|
|
9203
9456
|
import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
|
|
9204
9457
|
import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
|
|
9205
9458
|
function parsePolicyMode(value) {
|
|
@@ -9212,7 +9465,7 @@ function parsePolicyMode(value) {
|
|
|
9212
9465
|
throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
|
|
9213
9466
|
}
|
|
9214
9467
|
function hasRigProjectMarker(candidate) {
|
|
9215
|
-
return
|
|
9468
|
+
return existsSync15(resolve24(candidate, RIG_DEFINITION_DIRNAME)) || existsSync15(resolve24(candidate, RIG_STATE_DIRNAME)) || existsSync15(resolve24(candidate, "rig.config.ts")) || existsSync15(resolve24(candidate, "rig.config.json"));
|
|
9216
9469
|
}
|
|
9217
9470
|
function resolveProjectRoot({
|
|
9218
9471
|
envProjectRoot,
|
|
@@ -9221,17 +9474,19 @@ function resolveProjectRoot({
|
|
|
9221
9474
|
cwd = process.cwd()
|
|
9222
9475
|
}) {
|
|
9223
9476
|
if (envProjectRoot) {
|
|
9224
|
-
return
|
|
9477
|
+
return resolve24(cwd, envProjectRoot);
|
|
9225
9478
|
}
|
|
9226
9479
|
const fallbackImportDir = importDir ?? cwd;
|
|
9227
|
-
const
|
|
9480
|
+
const execName = basename2(execPath).toLowerCase();
|
|
9481
|
+
const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
|
|
9482
|
+
const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
|
|
9228
9483
|
for (const candidate of candidates) {
|
|
9229
9484
|
const nearest = resolveNearestRigProjectRoot(candidate);
|
|
9230
9485
|
if (hasRigProjectMarker(nearest)) {
|
|
9231
9486
|
return nearest;
|
|
9232
9487
|
}
|
|
9233
9488
|
}
|
|
9234
|
-
return
|
|
9489
|
+
return resolve24(cwd);
|
|
9235
9490
|
}
|
|
9236
9491
|
function normalizeCliErrorCode(message2, isCliError) {
|
|
9237
9492
|
if (message2.startsWith("Invalid --policy-mode value:")) {
|
|
@@ -9298,7 +9553,7 @@ async function runRigCli(module, options = {}) {
|
|
|
9298
9553
|
runId: context.runId,
|
|
9299
9554
|
outcome,
|
|
9300
9555
|
eventsFile: context.eventBus.getEventsFile(),
|
|
9301
|
-
policyFile:
|
|
9556
|
+
policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
|
|
9302
9557
|
policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
|
|
9303
9558
|
}, null, 2));
|
|
9304
9559
|
}
|