@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/src/index.js
CHANGED
|
@@ -101,12 +101,13 @@ async function runCommand(context, parts) {
|
|
|
101
101
|
const envMode = process.env.RIG_BASH_MODE;
|
|
102
102
|
const effectiveMode = context.policyMode || (envMode === "off" || envMode === "observe" || envMode === "enforce" ? envMode : loadPolicy(context.projectRoot).mode);
|
|
103
103
|
const controlledPath = `${resolve(context.projectRoot, ".rig", "bin")}:${context.projectRoot}/rig/tools:${process.env.PATH ?? ""}`;
|
|
104
|
-
const
|
|
104
|
+
const usesInfrastructureBinary = parts[0] === "rig-server";
|
|
105
|
+
const controlledBash = usesInfrastructureBinary ? null : await ensureAgentShellBinary(context.projectRoot);
|
|
105
106
|
const commandEnv = [
|
|
106
107
|
"env",
|
|
107
108
|
`PATH=${controlledPath}`,
|
|
108
109
|
`PROJECT_RIG_ROOT=${context.projectRoot}`,
|
|
109
|
-
`BASH=${controlledBash}
|
|
110
|
+
...controlledBash ? [`BASH=${controlledBash}`] : [],
|
|
110
111
|
`RIG_BASH_MODE=${effectiveMode}`,
|
|
111
112
|
`RIG_POLICY_FILE=${resolve(context.projectRoot, "rig/policy/policy.json")}`,
|
|
112
113
|
...context.eventBus.getEventsFile() ? [`RIG_EVENTS_FILE=${context.eventBus.getEventsFile()}`] : []
|
|
@@ -2667,6 +2668,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
2667
2668
|
|
|
2668
2669
|
// packages/cli/src/commands/_server-client.ts
|
|
2669
2670
|
import { spawnSync } from "child_process";
|
|
2671
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
2672
|
+
import { resolve as resolve9 } from "path";
|
|
2670
2673
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
2671
2674
|
var cachedGitHubBearerToken;
|
|
2672
2675
|
function cleanToken(value) {
|
|
@@ -2676,9 +2679,25 @@ function cleanToken(value) {
|
|
|
2676
2679
|
function setGitHubBearerTokenForCurrentProcess(token) {
|
|
2677
2680
|
cachedGitHubBearerToken = cleanToken(token ?? undefined);
|
|
2678
2681
|
}
|
|
2679
|
-
function
|
|
2682
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
2683
|
+
const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
|
|
2684
|
+
if (!existsSync5(path))
|
|
2685
|
+
return null;
|
|
2686
|
+
try {
|
|
2687
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
2688
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
2689
|
+
} catch {
|
|
2690
|
+
return null;
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
2680
2694
|
if (cachedGitHubBearerToken !== undefined)
|
|
2681
2695
|
return cachedGitHubBearerToken;
|
|
2696
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
2697
|
+
if (privateSession) {
|
|
2698
|
+
cachedGitHubBearerToken = privateSession;
|
|
2699
|
+
return cachedGitHubBearerToken;
|
|
2700
|
+
}
|
|
2682
2701
|
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
2683
2702
|
if (envToken) {
|
|
2684
2703
|
cachedGitHubBearerToken = envToken;
|
|
@@ -2698,7 +2717,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2698
2717
|
if (selected?.connection.kind === "remote") {
|
|
2699
2718
|
return {
|
|
2700
2719
|
baseUrl: selected.connection.baseUrl,
|
|
2701
|
-
authToken: readGitHubBearerTokenForRemote(),
|
|
2720
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
2702
2721
|
connectionKind: "remote"
|
|
2703
2722
|
};
|
|
2704
2723
|
}
|
|
@@ -2805,7 +2824,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
|
2805
2824
|
const payload = await requestServerJson(context, "/api/github/auth/token", {
|
|
2806
2825
|
method: "POST",
|
|
2807
2826
|
headers: { "content-type": "application/json" },
|
|
2808
|
-
body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
|
|
2827
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
|
|
2809
2828
|
});
|
|
2810
2829
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2811
2830
|
}
|
|
@@ -2826,18 +2845,38 @@ async function registerProjectViaServer(context, input) {
|
|
|
2826
2845
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2827
2846
|
}
|
|
2828
2847
|
function sleep(ms) {
|
|
2829
|
-
return new Promise((
|
|
2848
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
2849
|
+
}
|
|
2850
|
+
function isRetryableProjectRootSwitchError(error) {
|
|
2851
|
+
if (!(error instanceof Error))
|
|
2852
|
+
return false;
|
|
2853
|
+
const message = error.message.toLowerCase();
|
|
2854
|
+
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");
|
|
2830
2855
|
}
|
|
2831
2856
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
2832
|
-
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
2833
|
-
method: "POST",
|
|
2834
|
-
headers: { "content-type": "application/json" },
|
|
2835
|
-
body: JSON.stringify({ projectRoot })
|
|
2836
|
-
});
|
|
2837
2857
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
2838
2858
|
const pollMs = options.pollMs ?? 1000;
|
|
2839
2859
|
const deadline = Date.now() + timeoutMs;
|
|
2840
2860
|
let lastError;
|
|
2861
|
+
let switched = null;
|
|
2862
|
+
while (Date.now() < deadline) {
|
|
2863
|
+
try {
|
|
2864
|
+
switched = await requestServerJson(context, "/api/server/project-root", {
|
|
2865
|
+
method: "POST",
|
|
2866
|
+
headers: { "content-type": "application/json" },
|
|
2867
|
+
body: JSON.stringify({ projectRoot })
|
|
2868
|
+
});
|
|
2869
|
+
break;
|
|
2870
|
+
} catch (error) {
|
|
2871
|
+
lastError = error;
|
|
2872
|
+
if (!isRetryableProjectRootSwitchError(error))
|
|
2873
|
+
throw error;
|
|
2874
|
+
await sleep(pollMs);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
if (!switched) {
|
|
2878
|
+
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);
|
|
2879
|
+
}
|
|
2841
2880
|
while (Date.now() < deadline) {
|
|
2842
2881
|
try {
|
|
2843
2882
|
const status = await requestServerJson(context, "/api/server/status");
|
|
@@ -2917,9 +2956,9 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2917
2956
|
}
|
|
2918
2957
|
|
|
2919
2958
|
// packages/cli/src/commands/_pi-install.ts
|
|
2920
|
-
import { existsSync as
|
|
2959
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2921
2960
|
import { homedir as homedir3 } from "os";
|
|
2922
|
-
import { resolve as
|
|
2961
|
+
import { resolve as resolve10 } from "path";
|
|
2923
2962
|
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2924
2963
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2925
2964
|
export { default } from '@rig/pi-rig';
|
|
@@ -2934,11 +2973,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
2934
2973
|
return { exitCode, stdout, stderr };
|
|
2935
2974
|
}
|
|
2936
2975
|
function resolvePiRigExtensionPath(homeDir) {
|
|
2937
|
-
return
|
|
2976
|
+
return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
2938
2977
|
}
|
|
2939
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
2940
|
-
const localPackage =
|
|
2941
|
-
if (exists(
|
|
2978
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
|
|
2979
|
+
const localPackage = resolve10(projectRoot, "packages", "pi-rig");
|
|
2980
|
+
if (exists(resolve10(localPackage, "package.json")))
|
|
2942
2981
|
return localPackage;
|
|
2943
2982
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
2944
2983
|
}
|
|
@@ -2989,13 +3028,13 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
2989
3028
|
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
2990
3029
|
};
|
|
2991
3030
|
}
|
|
2992
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists =
|
|
3031
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
|
|
2993
3032
|
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
2994
|
-
const indexPath =
|
|
3033
|
+
const indexPath = resolve10(extensionPath, "index.ts");
|
|
2995
3034
|
if (!exists(indexPath))
|
|
2996
3035
|
return;
|
|
2997
3036
|
try {
|
|
2998
|
-
const content =
|
|
3037
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
2999
3038
|
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
3000
3039
|
rmSync3(extensionPath, { recursive: true, force: true });
|
|
3001
3040
|
}
|
|
@@ -3011,13 +3050,13 @@ async function checkPiRigInstall(input = {}) {
|
|
|
3011
3050
|
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
3012
3051
|
};
|
|
3013
3052
|
}
|
|
3014
|
-
const exists = input.exists ??
|
|
3053
|
+
const exists = input.exists ?? existsSync6;
|
|
3015
3054
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3016
3055
|
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
3017
3056
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
3018
3057
|
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
3019
3058
|
${piListResult.stderr}`);
|
|
3020
|
-
const legacyBridge = exists(
|
|
3059
|
+
const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
|
|
3021
3060
|
const hasPiRig = listedPiRig;
|
|
3022
3061
|
return {
|
|
3023
3062
|
extensionPath,
|
|
@@ -3355,7 +3394,7 @@ async function executeQueue(context, args) {
|
|
|
3355
3394
|
}
|
|
3356
3395
|
|
|
3357
3396
|
// packages/cli/src/commands/agent.ts
|
|
3358
|
-
import { resolve as
|
|
3397
|
+
import { resolve as resolve12 } from "path";
|
|
3359
3398
|
import {
|
|
3360
3399
|
agentId,
|
|
3361
3400
|
cleanupAgentRuntime,
|
|
@@ -3365,8 +3404,8 @@ import {
|
|
|
3365
3404
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3366
3405
|
|
|
3367
3406
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3368
|
-
import { existsSync as
|
|
3369
|
-
import { resolve as
|
|
3407
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3408
|
+
import { resolve as resolve11 } from "path";
|
|
3370
3409
|
import {
|
|
3371
3410
|
readAuthorityRun,
|
|
3372
3411
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3388,8 +3427,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3388
3427
|
return "claude-code";
|
|
3389
3428
|
}
|
|
3390
3429
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3391
|
-
const issuesPath =
|
|
3392
|
-
if (!
|
|
3430
|
+
const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3431
|
+
if (!existsSync7(issuesPath)) {
|
|
3393
3432
|
return null;
|
|
3394
3433
|
}
|
|
3395
3434
|
let latest = null;
|
|
@@ -3424,6 +3463,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3424
3463
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
3425
3464
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
3426
3465
|
const next = {
|
|
3466
|
+
...existing ?? {},
|
|
3427
3467
|
runId: input.runId,
|
|
3428
3468
|
projectRoot,
|
|
3429
3469
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -3456,7 +3496,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3456
3496
|
} else if ("errorText" in next) {
|
|
3457
3497
|
delete next.errorText;
|
|
3458
3498
|
}
|
|
3459
|
-
writeJsonFile3(
|
|
3499
|
+
writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3460
3500
|
return next;
|
|
3461
3501
|
}
|
|
3462
3502
|
|
|
@@ -3577,10 +3617,10 @@ async function executeAgent(context, args) {
|
|
|
3577
3617
|
status: "running",
|
|
3578
3618
|
startedAt: createdAt,
|
|
3579
3619
|
worktreePath: runtime.workspaceDir,
|
|
3580
|
-
artifactRoot:
|
|
3620
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3581
3621
|
logRoot: runtime.logsDir,
|
|
3582
|
-
sessionPath:
|
|
3583
|
-
sessionLogPath:
|
|
3622
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3623
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3584
3624
|
pid: process.pid
|
|
3585
3625
|
});
|
|
3586
3626
|
const result = await runInAgentRuntime({
|
|
@@ -3600,10 +3640,10 @@ async function executeAgent(context, args) {
|
|
|
3600
3640
|
startedAt: createdAt,
|
|
3601
3641
|
completedAt: failedAt,
|
|
3602
3642
|
worktreePath: runtime.workspaceDir,
|
|
3603
|
-
artifactRoot:
|
|
3643
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3604
3644
|
logRoot: runtime.logsDir,
|
|
3605
|
-
sessionPath:
|
|
3606
|
-
sessionLogPath:
|
|
3645
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3646
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3607
3647
|
pid: process.pid,
|
|
3608
3648
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3609
3649
|
});
|
|
@@ -3620,10 +3660,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3620
3660
|
startedAt: createdAt,
|
|
3621
3661
|
completedAt,
|
|
3622
3662
|
worktreePath: runtime.workspaceDir,
|
|
3623
|
-
artifactRoot:
|
|
3663
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3624
3664
|
logRoot: runtime.logsDir,
|
|
3625
|
-
sessionPath:
|
|
3626
|
-
sessionLogPath:
|
|
3665
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3666
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3627
3667
|
pid: process.pid
|
|
3628
3668
|
});
|
|
3629
3669
|
return {
|
|
@@ -3697,7 +3737,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3697
3737
|
import {
|
|
3698
3738
|
chmodSync,
|
|
3699
3739
|
copyFileSync as copyFileSync2,
|
|
3700
|
-
existsSync as
|
|
3740
|
+
existsSync as existsSync8,
|
|
3701
3741
|
mkdirSync as mkdirSync6,
|
|
3702
3742
|
readdirSync,
|
|
3703
3743
|
readlinkSync,
|
|
@@ -3707,7 +3747,7 @@ import {
|
|
|
3707
3747
|
unlinkSync
|
|
3708
3748
|
} from "fs";
|
|
3709
3749
|
import { homedir as homedir4 } from "os";
|
|
3710
|
-
import { resolve as
|
|
3750
|
+
import { resolve as resolve13 } from "path";
|
|
3711
3751
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3712
3752
|
import {
|
|
3713
3753
|
computeRuntimeImageFingerprint,
|
|
@@ -3726,13 +3766,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3726
3766
|
|
|
3727
3767
|
// packages/cli/src/commands/dist.ts
|
|
3728
3768
|
function collectRigValidatorBuildTargets(input) {
|
|
3729
|
-
const validatorsRoot =
|
|
3730
|
-
if (!
|
|
3769
|
+
const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3770
|
+
if (!existsSync8(validatorsRoot))
|
|
3731
3771
|
return [];
|
|
3732
3772
|
const targets = [];
|
|
3733
3773
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3734
3774
|
for (const category of categories) {
|
|
3735
|
-
const categoryDir =
|
|
3775
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3736
3776
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3737
3777
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3738
3778
|
continue;
|
|
@@ -3741,7 +3781,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3741
3781
|
continue;
|
|
3742
3782
|
targets.push({
|
|
3743
3783
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3744
|
-
dest:
|
|
3784
|
+
dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3745
3785
|
cwd: input.hostProjectRoot
|
|
3746
3786
|
});
|
|
3747
3787
|
}
|
|
@@ -3750,16 +3790,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3750
3790
|
}
|
|
3751
3791
|
async function findLatestDistBinary(projectRoot) {
|
|
3752
3792
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3753
|
-
if (!
|
|
3793
|
+
if (!existsSync8(distRoot)) {
|
|
3754
3794
|
return null;
|
|
3755
3795
|
}
|
|
3756
3796
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3757
3797
|
name: entry.name,
|
|
3758
|
-
mtimeMs: statSync(
|
|
3798
|
+
mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
|
|
3759
3799
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3760
3800
|
for (const { name } of entries) {
|
|
3761
|
-
const candidate =
|
|
3762
|
-
if (
|
|
3801
|
+
const candidate = resolve13(distRoot, name, "bin", "rig");
|
|
3802
|
+
if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3763
3803
|
return candidate;
|
|
3764
3804
|
}
|
|
3765
3805
|
}
|
|
@@ -3771,7 +3811,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3771
3811
|
async function runDistDoctor(projectRoot) {
|
|
3772
3812
|
const bunPath = Bun.which("bun");
|
|
3773
3813
|
const rigPath = Bun.which("rig");
|
|
3774
|
-
const userBinDir =
|
|
3814
|
+
const userBinDir = resolve13(homedir4(), ".local/bin");
|
|
3775
3815
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3776
3816
|
let rigRunnable = false;
|
|
3777
3817
|
if (rigPath) {
|
|
@@ -3819,15 +3859,15 @@ async function executeDist(context, args) {
|
|
|
3819
3859
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3820
3860
|
let buildDir = null;
|
|
3821
3861
|
if (!source) {
|
|
3822
|
-
buildDir =
|
|
3862
|
+
buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3823
3863
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3824
|
-
source =
|
|
3864
|
+
source = resolve13(buildDir, "bin", "rig");
|
|
3825
3865
|
}
|
|
3826
|
-
if (!
|
|
3866
|
+
if (!existsSync8(source)) {
|
|
3827
3867
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3828
3868
|
}
|
|
3829
|
-
const installedPath =
|
|
3830
|
-
if (
|
|
3869
|
+
const installedPath = resolve13(installDir, "rig");
|
|
3870
|
+
if (existsSync8(installedPath)) {
|
|
3831
3871
|
unlinkSync(installedPath);
|
|
3832
3872
|
}
|
|
3833
3873
|
copyFileSync2(source, installedPath);
|
|
@@ -3869,22 +3909,22 @@ async function executeDist(context, args) {
|
|
|
3869
3909
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3870
3910
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3871
3911
|
const currentId = computeRuntimeImageId(fp);
|
|
3872
|
-
const imagesDir =
|
|
3912
|
+
const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3873
3913
|
mkdirSync6(imagesDir, { recursive: true });
|
|
3874
3914
|
let pruned = 0;
|
|
3875
3915
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3876
3916
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3877
|
-
rmSync4(
|
|
3917
|
+
rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
|
|
3878
3918
|
pruned++;
|
|
3879
3919
|
}
|
|
3880
3920
|
}
|
|
3881
3921
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3882
3922
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3883
3923
|
}
|
|
3884
|
-
const imageDir =
|
|
3885
|
-
mkdirSync6(
|
|
3886
|
-
mkdirSync6(
|
|
3887
|
-
mkdirSync6(
|
|
3924
|
+
const imageDir = resolve13(imagesDir, currentId);
|
|
3925
|
+
mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
|
|
3926
|
+
mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
|
|
3927
|
+
mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
|
|
3888
3928
|
const hookNames = [
|
|
3889
3929
|
"scope-guard",
|
|
3890
3930
|
"import-guard",
|
|
@@ -3898,25 +3938,25 @@ async function executeDist(context, args) {
|
|
|
3898
3938
|
];
|
|
3899
3939
|
const targets = [];
|
|
3900
3940
|
const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
|
|
3901
|
-
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest:
|
|
3902
|
-
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest:
|
|
3941
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3942
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3903
3943
|
for (const hookName of hookNames) {
|
|
3904
3944
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3905
|
-
targets.push({ source: src, dest:
|
|
3906
|
-
targets.push({ source: src, dest:
|
|
3945
|
+
targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3946
|
+
targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3907
3947
|
}
|
|
3908
|
-
const pluginsDir =
|
|
3909
|
-
const binPluginsDir =
|
|
3910
|
-
const validatorsRoot =
|
|
3911
|
-
const binValidatorsDir =
|
|
3948
|
+
const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
|
|
3949
|
+
const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3950
|
+
const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3951
|
+
const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3912
3952
|
mkdirSync6(binPluginsDir, { recursive: true });
|
|
3913
3953
|
mkdirSync6(binValidatorsDir, { recursive: true });
|
|
3914
|
-
if (
|
|
3954
|
+
if (existsSync8(pluginsDir)) {
|
|
3915
3955
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3916
3956
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3917
3957
|
if (!m)
|
|
3918
3958
|
continue;
|
|
3919
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3959
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3920
3960
|
}
|
|
3921
3961
|
}
|
|
3922
3962
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3927,17 +3967,17 @@ async function executeDist(context, args) {
|
|
|
3927
3967
|
const isValidator = dest.includes("/bin/validators/");
|
|
3928
3968
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3929
3969
|
}
|
|
3930
|
-
if (
|
|
3970
|
+
if (existsSync8(pluginsDir)) {
|
|
3931
3971
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3932
3972
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3933
3973
|
if (!m)
|
|
3934
3974
|
continue;
|
|
3935
3975
|
const pluginName = m[1];
|
|
3936
|
-
const imageBin =
|
|
3976
|
+
const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
|
|
3937
3977
|
if (!pluginName)
|
|
3938
3978
|
continue;
|
|
3939
|
-
const symlinkPath =
|
|
3940
|
-
if (
|
|
3979
|
+
const symlinkPath = resolve13(binPluginsDir, pluginName);
|
|
3980
|
+
if (existsSync8(imageBin)) {
|
|
3941
3981
|
try {
|
|
3942
3982
|
unlinkSync(symlinkPath);
|
|
3943
3983
|
} catch {}
|
|
@@ -3945,10 +3985,10 @@ async function executeDist(context, args) {
|
|
|
3945
3985
|
}
|
|
3946
3986
|
}
|
|
3947
3987
|
}
|
|
3948
|
-
if (
|
|
3988
|
+
if (existsSync8(validatorsRoot)) {
|
|
3949
3989
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3950
3990
|
for (const category of categories) {
|
|
3951
|
-
const categoryDir =
|
|
3991
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3952
3992
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3953
3993
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3954
3994
|
continue;
|
|
@@ -3956,9 +3996,9 @@ async function executeDist(context, args) {
|
|
|
3956
3996
|
if (!check || check === "index" || check === "shared")
|
|
3957
3997
|
continue;
|
|
3958
3998
|
const validatorName = `${category.name}-${check}`;
|
|
3959
|
-
const imageBin =
|
|
3960
|
-
const symlinkPath =
|
|
3961
|
-
if (
|
|
3999
|
+
const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
|
|
4000
|
+
const symlinkPath = resolve13(binValidatorsDir, validatorName);
|
|
4001
|
+
if (existsSync8(imageBin)) {
|
|
3962
4002
|
try {
|
|
3963
4003
|
unlinkSync(symlinkPath);
|
|
3964
4004
|
} catch {}
|
|
@@ -3967,18 +4007,18 @@ async function executeDist(context, args) {
|
|
|
3967
4007
|
}
|
|
3968
4008
|
}
|
|
3969
4009
|
}
|
|
3970
|
-
const agentsDir =
|
|
3971
|
-
if (
|
|
4010
|
+
const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
4011
|
+
if (existsSync8(agentsDir)) {
|
|
3972
4012
|
let relinkCount = 0;
|
|
3973
4013
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3974
4014
|
if (!agentEntry.isDirectory())
|
|
3975
4015
|
continue;
|
|
3976
|
-
const agentBinDir =
|
|
3977
|
-
if (!
|
|
4016
|
+
const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
4017
|
+
if (!existsSync8(agentBinDir))
|
|
3978
4018
|
continue;
|
|
3979
4019
|
const walkDir = (dir) => {
|
|
3980
4020
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3981
|
-
const fullPath =
|
|
4021
|
+
const fullPath = resolve13(dir, entry.name);
|
|
3982
4022
|
if (entry.isDirectory()) {
|
|
3983
4023
|
walkDir(fullPath);
|
|
3984
4024
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -4012,7 +4052,7 @@ async function executeDist(context, args) {
|
|
|
4012
4052
|
|
|
4013
4053
|
// packages/cli/src/commands/inbox.ts
|
|
4014
4054
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
4015
|
-
import { resolve as
|
|
4055
|
+
import { resolve as resolve14 } from "path";
|
|
4016
4056
|
import {
|
|
4017
4057
|
listAuthorityRuns,
|
|
4018
4058
|
readJsonlFile as readJsonlFile3,
|
|
@@ -4029,7 +4069,7 @@ async function executeInbox(context, args) {
|
|
|
4029
4069
|
pending = task.rest;
|
|
4030
4070
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
4031
4071
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
4032
|
-
const approvals = runs.flatMap((entry) => readJsonlFile3(
|
|
4072
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
4033
4073
|
runId: entry.runId,
|
|
4034
4074
|
record
|
|
4035
4075
|
})));
|
|
@@ -4057,7 +4097,7 @@ async function executeInbox(context, args) {
|
|
|
4057
4097
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4058
4098
|
throw new CliError2("decision must be approve or reject.");
|
|
4059
4099
|
}
|
|
4060
|
-
const approvalsPath =
|
|
4100
|
+
const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
4061
4101
|
const approvals = readJsonlFile3(approvalsPath);
|
|
4062
4102
|
const resolvedAt = new Date().toISOString();
|
|
4063
4103
|
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);
|
|
@@ -4074,7 +4114,7 @@ async function executeInbox(context, args) {
|
|
|
4074
4114
|
pending = task.rest;
|
|
4075
4115
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
4076
4116
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
4077
|
-
const requests = runs.flatMap((entry) => readJsonlFile3(
|
|
4117
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
4078
4118
|
runId: entry.runId,
|
|
4079
4119
|
record
|
|
4080
4120
|
})));
|
|
@@ -4116,7 +4156,7 @@ async function executeInbox(context, args) {
|
|
|
4116
4156
|
const [key, ...restValue] = entry.split("=");
|
|
4117
4157
|
return [key, restValue.join("=")];
|
|
4118
4158
|
}));
|
|
4119
|
-
const requestsPath =
|
|
4159
|
+
const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
4120
4160
|
const requests = readJsonlFile3(requestsPath);
|
|
4121
4161
|
const resolvedAt = new Date().toISOString();
|
|
4122
4162
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -4131,14 +4171,14 @@ async function executeInbox(context, args) {
|
|
|
4131
4171
|
}
|
|
4132
4172
|
|
|
4133
4173
|
// packages/cli/src/commands/init.ts
|
|
4134
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
4174
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4135
4175
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4136
|
-
import { resolve as
|
|
4176
|
+
import { resolve as resolve17 } from "path";
|
|
4137
4177
|
import { buildRigInitConfigSource } from "@rig/core";
|
|
4138
4178
|
|
|
4139
4179
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
4140
4180
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
4141
|
-
import { dirname as dirname2, resolve as
|
|
4181
|
+
import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
|
|
4142
4182
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
4143
4183
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
4144
4184
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -4160,15 +4200,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
4160
4200
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
4161
4201
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
4162
4202
|
}
|
|
4163
|
-
const resolved =
|
|
4203
|
+
const resolved = resolve15(root, relativePath);
|
|
4164
4204
|
const relativeToRoot = relative(root, resolved);
|
|
4165
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
4205
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
|
|
4166
4206
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
4167
4207
|
}
|
|
4168
4208
|
return resolved;
|
|
4169
4209
|
}
|
|
4170
4210
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
4171
|
-
const root =
|
|
4211
|
+
const root = resolve15(projectRoot);
|
|
4172
4212
|
const excludedDirectories = [...new Set([
|
|
4173
4213
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
4174
4214
|
...options.excludedDirectories ?? []
|
|
@@ -4180,7 +4220,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
4180
4220
|
for (const entry of entries) {
|
|
4181
4221
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
4182
4222
|
continue;
|
|
4183
|
-
const fullPath =
|
|
4223
|
+
const fullPath = resolve15(dir, entry.name);
|
|
4184
4224
|
if (entry.isDirectory()) {
|
|
4185
4225
|
await visit(fullPath);
|
|
4186
4226
|
continue;
|
|
@@ -4228,8 +4268,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
4228
4268
|
}
|
|
4229
4269
|
|
|
4230
4270
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
4231
|
-
import { existsSync as
|
|
4232
|
-
import { resolve as
|
|
4271
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
4272
|
+
import { resolve as resolve16 } from "path";
|
|
4233
4273
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
4234
4274
|
function check(id, label, status, detail, remediation) {
|
|
4235
4275
|
return {
|
|
@@ -4269,11 +4309,11 @@ function repoSlugFromConfig(config) {
|
|
|
4269
4309
|
function loadFallbackConfig(projectRoot) {
|
|
4270
4310
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
4271
4311
|
for (const name of candidates) {
|
|
4272
|
-
const path =
|
|
4273
|
-
if (!
|
|
4312
|
+
const path = resolve16(projectRoot, name);
|
|
4313
|
+
if (!existsSync9(path))
|
|
4274
4314
|
continue;
|
|
4275
4315
|
try {
|
|
4276
|
-
const source =
|
|
4316
|
+
const source = readFileSync5(path, "utf8");
|
|
4277
4317
|
if (name.endsWith(".json"))
|
|
4278
4318
|
return JSON.parse(source);
|
|
4279
4319
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -4352,7 +4392,7 @@ async function runRigDoctorChecks(options) {
|
|
|
4352
4392
|
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`)."));
|
|
4353
4393
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
4354
4394
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
4355
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
4395
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
|
|
4356
4396
|
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."));
|
|
4357
4397
|
const taskSourceKind = config?.taskSource?.kind;
|
|
4358
4398
|
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."));
|
|
@@ -4447,10 +4487,10 @@ function countDoctorFailures(checks) {
|
|
|
4447
4487
|
}
|
|
4448
4488
|
|
|
4449
4489
|
// packages/cli/src/commands/init.ts
|
|
4450
|
-
var
|
|
4490
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4451
4491
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4452
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4453
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4492
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4493
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4454
4494
|
};
|
|
4455
4495
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
4456
4496
|
const trimmed = remoteUrl.trim();
|
|
@@ -4470,20 +4510,20 @@ function parseRepoSlug(value) {
|
|
|
4470
4510
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
4471
4511
|
}
|
|
4472
4512
|
function ensureRigPrivateDirs(projectRoot) {
|
|
4473
|
-
const rigDir =
|
|
4474
|
-
mkdirSync7(
|
|
4475
|
-
mkdirSync7(
|
|
4476
|
-
mkdirSync7(
|
|
4477
|
-
mkdirSync7(
|
|
4478
|
-
mkdirSync7(
|
|
4479
|
-
const taskConfigPath =
|
|
4480
|
-
if (!
|
|
4513
|
+
const rigDir = resolve17(projectRoot, ".rig");
|
|
4514
|
+
mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
|
|
4515
|
+
mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
|
|
4516
|
+
mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
|
|
4517
|
+
mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
|
|
4518
|
+
mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
|
|
4519
|
+
const taskConfigPath = resolve17(rigDir, "task-config.json");
|
|
4520
|
+
if (!existsSync10(taskConfigPath))
|
|
4481
4521
|
writeFileSync5(taskConfigPath, `{}
|
|
4482
4522
|
`, "utf-8");
|
|
4483
4523
|
}
|
|
4484
4524
|
function ensureGitignoreEntries(projectRoot) {
|
|
4485
|
-
const path =
|
|
4486
|
-
const existing =
|
|
4525
|
+
const path = resolve17(projectRoot, ".gitignore");
|
|
4526
|
+
const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
|
|
4487
4527
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
4488
4528
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4489
4529
|
if (missing.length === 0)
|
|
@@ -4496,14 +4536,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
4496
4536
|
`, "utf8");
|
|
4497
4537
|
}
|
|
4498
4538
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
4499
|
-
const path =
|
|
4500
|
-
const existing =
|
|
4539
|
+
const path = resolve17(projectRoot, "package.json");
|
|
4540
|
+
const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
|
|
4501
4541
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
4502
4542
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
4503
4543
|
devDependencies[name] = spec;
|
|
4504
4544
|
}
|
|
4505
4545
|
const next = {
|
|
4506
|
-
...
|
|
4546
|
+
...existsSync10(path) ? existing : { name: "rig-project", private: true },
|
|
4507
4547
|
devDependencies
|
|
4508
4548
|
};
|
|
4509
4549
|
writeFileSync5(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -4573,15 +4613,71 @@ async function promptSelect(prompts, options) {
|
|
|
4573
4613
|
throw new CliError2("Init cancelled.", 1);
|
|
4574
4614
|
return String(value);
|
|
4575
4615
|
}
|
|
4576
|
-
|
|
4616
|
+
function sleep2(ms) {
|
|
4617
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
4618
|
+
}
|
|
4619
|
+
function positiveIntFromEnv(name, fallback) {
|
|
4620
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
4621
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
4622
|
+
}
|
|
4623
|
+
function apiSessionTokenFrom(payload) {
|
|
4624
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4625
|
+
return null;
|
|
4626
|
+
const token = payload.apiSessionToken;
|
|
4627
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
4628
|
+
}
|
|
4629
|
+
function cleanPayloadString(value) {
|
|
4630
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4631
|
+
}
|
|
4632
|
+
function remoteGitHubAuthMetadata(payload) {
|
|
4633
|
+
if (!payload)
|
|
4634
|
+
return {};
|
|
4635
|
+
const userNamespace = payload.userNamespace && typeof payload.userNamespace === "object" && !Array.isArray(payload.userNamespace) ? payload.userNamespace : null;
|
|
4636
|
+
return {
|
|
4637
|
+
...cleanPayloadString(payload.login) ? { login: cleanPayloadString(payload.login) } : {},
|
|
4638
|
+
...cleanPayloadString(payload.userId) ? { userId: cleanPayloadString(payload.userId) } : {},
|
|
4639
|
+
...cleanPayloadString(userNamespace?.key) ? { userNamespaceKey: cleanPayloadString(userNamespace?.key) } : {},
|
|
4640
|
+
...cleanPayloadString(userNamespace?.root) ? { userNamespaceRoot: cleanPayloadString(userNamespace?.root) } : {},
|
|
4641
|
+
...cleanPayloadString(userNamespace?.checkoutBaseDir) ? { checkoutBaseDir: cleanPayloadString(userNamespace?.checkoutBaseDir) } : {},
|
|
4642
|
+
...cleanPayloadString(userNamespace?.snapshotBaseDir) ? { snapshotBaseDir: cleanPayloadString(userNamespace?.snapshotBaseDir) } : {}
|
|
4643
|
+
};
|
|
4644
|
+
}
|
|
4645
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
4646
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
4647
|
+
authenticated: true,
|
|
4648
|
+
source: input.source,
|
|
4649
|
+
storedOnServer: true,
|
|
4650
|
+
selectedRepo: input.selectedRepo,
|
|
4651
|
+
...remoteGitHubAuthMetadata(input.authPayload ?? null),
|
|
4652
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
4653
|
+
updatedAt: new Date().toISOString()
|
|
4654
|
+
}, null, 2)}
|
|
4655
|
+
`, "utf8");
|
|
4656
|
+
}
|
|
4657
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
4577
4658
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
4578
4659
|
return null;
|
|
4579
|
-
const
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4660
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
4661
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
4662
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
4663
|
+
const deadline = Date.now() + timeoutMs;
|
|
4664
|
+
let last = null;
|
|
4665
|
+
do {
|
|
4666
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
4667
|
+
method: "POST",
|
|
4668
|
+
headers: { "content-type": "application/json" },
|
|
4669
|
+
body: JSON.stringify({ pollId })
|
|
4670
|
+
}).catch(() => null);
|
|
4671
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4672
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
4673
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
4674
|
+
return last;
|
|
4675
|
+
}
|
|
4676
|
+
if (timeoutMs <= 0)
|
|
4677
|
+
return last;
|
|
4678
|
+
await sleep2(intervalMs);
|
|
4679
|
+
} while (Date.now() < deadline);
|
|
4680
|
+
return last;
|
|
4585
4681
|
}
|
|
4586
4682
|
async function runControlPlaneInit(context, options) {
|
|
4587
4683
|
const projectRoot = context.projectRoot;
|
|
@@ -4604,9 +4700,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
4604
4700
|
});
|
|
4605
4701
|
ensureRigPrivateDirs(projectRoot);
|
|
4606
4702
|
ensureGitignoreEntries(projectRoot);
|
|
4607
|
-
const configTsPath =
|
|
4608
|
-
const configJsonPath =
|
|
4609
|
-
const configExists =
|
|
4703
|
+
const configTsPath = resolve17(projectRoot, "rig.config.ts");
|
|
4704
|
+
const configJsonPath = resolve17(projectRoot, "rig.config.json");
|
|
4705
|
+
const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
|
|
4610
4706
|
if (!options.privateStateOnly) {
|
|
4611
4707
|
if (configExists && !options.repair) {
|
|
4612
4708
|
if (context.outputMode !== "json")
|
|
@@ -4622,7 +4718,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
4622
4718
|
}
|
|
4623
4719
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
4624
4720
|
}
|
|
4625
|
-
writeFileSync5(
|
|
4721
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
4626
4722
|
`, "utf8");
|
|
4627
4723
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
4628
4724
|
let uploadedSnapshot = null;
|
|
@@ -4644,10 +4740,15 @@ async function runControlPlaneInit(context, options) {
|
|
|
4644
4740
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
4645
4741
|
if (token) {
|
|
4646
4742
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
4647
|
-
|
|
4743
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4744
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4648
4745
|
if (serverKind === "remote") {
|
|
4649
|
-
|
|
4650
|
-
|
|
4746
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
4747
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
4748
|
+
selectedRepo: repo.slug,
|
|
4749
|
+
apiSessionToken,
|
|
4750
|
+
authPayload: githubAuth
|
|
4751
|
+
});
|
|
4651
4752
|
}
|
|
4652
4753
|
} else if (authMethod === "device") {
|
|
4653
4754
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -4656,9 +4757,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
4656
4757
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
4657
4758
|
});
|
|
4658
4759
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4659
|
-
|
|
4660
|
-
|
|
4760
|
+
if (context.outputMode !== "json") {
|
|
4761
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
4762
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
4763
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
4764
|
+
}
|
|
4765
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
4766
|
+
if (completed) {
|
|
4767
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
4768
|
+
if (apiSessionToken) {
|
|
4769
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
4770
|
+
if (serverKind === "remote") {
|
|
4771
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken, authPayload: completed });
|
|
4772
|
+
}
|
|
4773
|
+
}
|
|
4661
4774
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
4775
|
+
}
|
|
4662
4776
|
}
|
|
4663
4777
|
let remoteCheckoutPreparation = null;
|
|
4664
4778
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -4678,6 +4792,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
4678
4792
|
});
|
|
4679
4793
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
4680
4794
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
4795
|
+
if (serverRootSwitch && token) {
|
|
4796
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
4797
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4798
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4799
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken, authPayload: githubAuth });
|
|
4800
|
+
}
|
|
4681
4801
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
4682
4802
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
4683
4803
|
remote: true,
|
|
@@ -4787,7 +4907,7 @@ function parseInitOptions(args) {
|
|
|
4787
4907
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
4788
4908
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
4789
4909
|
const projectRoot = context.projectRoot;
|
|
4790
|
-
const existingConfig =
|
|
4910
|
+
const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
|
|
4791
4911
|
let repair = false;
|
|
4792
4912
|
let privateStateOnly = false;
|
|
4793
4913
|
if (existingConfig) {
|
|
@@ -4887,7 +5007,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
4887
5007
|
});
|
|
4888
5008
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
4889
5009
|
const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
|
|
4890
|
-
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")}.` : "";
|
|
5010
|
+
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")}.` : "";
|
|
4891
5011
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
4892
5012
|
return result;
|
|
4893
5013
|
}
|
|
@@ -5049,8 +5169,8 @@ async function executeDoctor(context, args) {
|
|
|
5049
5169
|
}
|
|
5050
5170
|
|
|
5051
5171
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
5052
|
-
import { readFileSync as
|
|
5053
|
-
import { resolve as
|
|
5172
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
5173
|
+
import { resolve as resolve18 } from "path";
|
|
5054
5174
|
import {
|
|
5055
5175
|
appendJsonlRecord as appendJsonlRecord2,
|
|
5056
5176
|
readAuthorityRun as readAuthorityRun2,
|
|
@@ -5070,7 +5190,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
|
|
|
5070
5190
|
...patch,
|
|
5071
5191
|
updatedAt: new Date().toISOString()
|
|
5072
5192
|
};
|
|
5073
|
-
writeJsonFile4(
|
|
5193
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
|
|
5074
5194
|
return next;
|
|
5075
5195
|
}
|
|
5076
5196
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -5078,21 +5198,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
5078
5198
|
if (!current) {
|
|
5079
5199
|
return;
|
|
5080
5200
|
}
|
|
5081
|
-
writeJsonFile4(
|
|
5201
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
|
|
5082
5202
|
...current,
|
|
5083
5203
|
updatedAt: new Date().toISOString()
|
|
5084
5204
|
});
|
|
5085
5205
|
}
|
|
5086
5206
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
5087
|
-
appendJsonlRecord2(
|
|
5207
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
|
|
5088
5208
|
touchAuthorityRun(projectRoot, runId);
|
|
5089
5209
|
}
|
|
5090
5210
|
function appendRunLog(projectRoot, runId, value) {
|
|
5091
|
-
appendJsonlRecord2(
|
|
5211
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
|
|
5092
5212
|
touchAuthorityRun(projectRoot, runId);
|
|
5093
5213
|
}
|
|
5094
5214
|
function appendRunAction(projectRoot, runId, value) {
|
|
5095
|
-
appendJsonlRecord2(
|
|
5215
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
|
|
5096
5216
|
id: value.id,
|
|
5097
5217
|
type: "action",
|
|
5098
5218
|
actionType: value.actionType,
|
|
@@ -5163,7 +5283,7 @@ function buildRunPrompt(input) {
|
|
|
5163
5283
|
})();
|
|
5164
5284
|
const scopeText = (() => {
|
|
5165
5285
|
try {
|
|
5166
|
-
const parsed = JSON.parse(
|
|
5286
|
+
const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
|
|
5167
5287
|
const entry = parsed[input.taskId] ?? {};
|
|
5168
5288
|
const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
|
|
5169
5289
|
const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
|
|
@@ -5276,8 +5396,8 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
5276
5396
|
}
|
|
5277
5397
|
|
|
5278
5398
|
// packages/cli/src/commands/inspect.ts
|
|
5279
|
-
import { existsSync as
|
|
5280
|
-
import { resolve as
|
|
5399
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
5400
|
+
import { resolve as resolve19 } from "path";
|
|
5281
5401
|
import {
|
|
5282
5402
|
listAuthorityRuns as listAuthorityRuns2,
|
|
5283
5403
|
readAuthorityRun as readAuthorityRun3,
|
|
@@ -5298,8 +5418,8 @@ async function executeInspect(context, args) {
|
|
|
5298
5418
|
if (!latestRun) {
|
|
5299
5419
|
throw new CliError2(`No runs found for ${requiredTask}.`);
|
|
5300
5420
|
}
|
|
5301
|
-
const logsPath =
|
|
5302
|
-
if (!
|
|
5421
|
+
const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
5422
|
+
if (!existsSync11(logsPath)) {
|
|
5303
5423
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
5304
5424
|
}
|
|
5305
5425
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -5309,7 +5429,7 @@ async function executeInspect(context, args) {
|
|
|
5309
5429
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
5310
5430
|
requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
|
|
5311
5431
|
const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
|
|
5312
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
5432
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
|
|
5313
5433
|
if (!artifactRoot) {
|
|
5314
5434
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
5315
5435
|
}
|
|
@@ -5366,10 +5486,10 @@ async function executeInspect(context, args) {
|
|
|
5366
5486
|
case "failures": {
|
|
5367
5487
|
requireNoExtraArgs(rest, "bun run rig inspect failures");
|
|
5368
5488
|
const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
|
|
5369
|
-
if (!
|
|
5489
|
+
if (!existsSync11(failed)) {
|
|
5370
5490
|
console.log("No failures recorded.");
|
|
5371
5491
|
} else {
|
|
5372
|
-
process.stdout.write(
|
|
5492
|
+
process.stdout.write(readFileSync8(failed, "utf-8"));
|
|
5373
5493
|
}
|
|
5374
5494
|
return { ok: true, group: "inspect", command };
|
|
5375
5495
|
}
|
|
@@ -5386,11 +5506,11 @@ async function executeInspect(context, args) {
|
|
|
5386
5506
|
return { ok: true, group: "inspect", command };
|
|
5387
5507
|
case "audit": {
|
|
5388
5508
|
requireNoExtraArgs(rest, "bun run rig inspect audit");
|
|
5389
|
-
const auditPath =
|
|
5390
|
-
if (!
|
|
5509
|
+
const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
|
|
5510
|
+
if (!existsSync11(auditPath)) {
|
|
5391
5511
|
console.log("No audit log found.");
|
|
5392
5512
|
} else {
|
|
5393
|
-
const lines =
|
|
5513
|
+
const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
5394
5514
|
for (const line of lines) {
|
|
5395
5515
|
console.log(line);
|
|
5396
5516
|
}
|
|
@@ -6019,8 +6139,8 @@ async function executeRemote(context, args) {
|
|
|
6019
6139
|
}
|
|
6020
6140
|
|
|
6021
6141
|
// packages/cli/src/commands/run.ts
|
|
6022
|
-
import { existsSync as
|
|
6023
|
-
import { resolve as
|
|
6142
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
6143
|
+
import { resolve as resolve20 } from "path";
|
|
6024
6144
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6025
6145
|
import {
|
|
6026
6146
|
listAuthorityRuns as listAuthorityRuns3,
|
|
@@ -6034,6 +6154,7 @@ import {
|
|
|
6034
6154
|
listOpenEpics,
|
|
6035
6155
|
resolveDefaultEpic,
|
|
6036
6156
|
runResume,
|
|
6157
|
+
runRestart,
|
|
6037
6158
|
runStatus,
|
|
6038
6159
|
runStop,
|
|
6039
6160
|
startRun,
|
|
@@ -6142,6 +6263,17 @@ async function attachRunOperatorView(context, input) {
|
|
|
6142
6263
|
}
|
|
6143
6264
|
|
|
6144
6265
|
// packages/cli/src/commands/run.ts
|
|
6266
|
+
function normalizeRemoteRunDetails(payload) {
|
|
6267
|
+
const run = payload.run;
|
|
6268
|
+
if (!run || typeof run !== "object" || Array.isArray(run))
|
|
6269
|
+
return null;
|
|
6270
|
+
return {
|
|
6271
|
+
...run,
|
|
6272
|
+
...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
|
|
6273
|
+
...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
|
|
6274
|
+
...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
|
|
6275
|
+
};
|
|
6276
|
+
}
|
|
6145
6277
|
function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
|
|
6146
6278
|
if (noEpicPrompt) {
|
|
6147
6279
|
return false;
|
|
@@ -6280,7 +6412,7 @@ async function executeRun(context, args) {
|
|
|
6280
6412
|
if (!run.value) {
|
|
6281
6413
|
throw new CliError2("run show requires --run <id>.");
|
|
6282
6414
|
}
|
|
6283
|
-
const record = readAuthorityRun4(context.projectRoot, run.value);
|
|
6415
|
+
const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
|
|
6284
6416
|
if (!record) {
|
|
6285
6417
|
throw new CliError2(`Run not found: ${run.value}`, 2);
|
|
6286
6418
|
}
|
|
@@ -6299,7 +6431,7 @@ async function executeRun(context, args) {
|
|
|
6299
6431
|
if (!run.value) {
|
|
6300
6432
|
throw new CliError2("run timeline requires --run <id>.");
|
|
6301
6433
|
}
|
|
6302
|
-
const timelinePath =
|
|
6434
|
+
const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
|
|
6303
6435
|
const printEvents = () => {
|
|
6304
6436
|
const events2 = readJsonlFile4(timelinePath);
|
|
6305
6437
|
if (context.outputMode === "text") {
|
|
@@ -6311,12 +6443,12 @@ async function executeRun(context, args) {
|
|
|
6311
6443
|
};
|
|
6312
6444
|
const events = printEvents();
|
|
6313
6445
|
if (follow.value && context.outputMode === "text") {
|
|
6314
|
-
let lastLength =
|
|
6446
|
+
let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
|
|
6315
6447
|
while (true) {
|
|
6316
6448
|
await Bun.sleep(1000);
|
|
6317
|
-
if (!
|
|
6449
|
+
if (!existsSync12(timelinePath))
|
|
6318
6450
|
continue;
|
|
6319
|
-
const next =
|
|
6451
|
+
const next = readFileSync9(timelinePath, "utf8");
|
|
6320
6452
|
if (next.length <= lastLength)
|
|
6321
6453
|
continue;
|
|
6322
6454
|
const delta = next.slice(lastLength);
|
|
@@ -6461,6 +6593,20 @@ async function executeRun(context, args) {
|
|
|
6461
6593
|
}
|
|
6462
6594
|
return { ok: true, group: "run", command, details: resumed };
|
|
6463
6595
|
}
|
|
6596
|
+
case "restart": {
|
|
6597
|
+
requireNoExtraArgs(rest, "bun run rig run restart");
|
|
6598
|
+
if (context.dryRun) {
|
|
6599
|
+
if (context.outputMode === "text") {
|
|
6600
|
+
console.log("[dry-run] rig run restart");
|
|
6601
|
+
}
|
|
6602
|
+
return { ok: true, group: "run", command };
|
|
6603
|
+
}
|
|
6604
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
6605
|
+
if (context.outputMode === "text") {
|
|
6606
|
+
console.log(`Restarted run: ${restarted.runId}`);
|
|
6607
|
+
}
|
|
6608
|
+
return { ok: true, group: "run", command, details: restarted };
|
|
6609
|
+
}
|
|
6464
6610
|
case "stop": {
|
|
6465
6611
|
const runOption = takeOption(rest, "--run");
|
|
6466
6612
|
const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
|
|
@@ -6518,7 +6664,7 @@ async function executeServer(context, args, options) {
|
|
|
6518
6664
|
const authTokenResult = takeOption(pending, "--auth-token");
|
|
6519
6665
|
pending = authTokenResult.rest;
|
|
6520
6666
|
requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
|
|
6521
|
-
const commandParts = ["
|
|
6667
|
+
const commandParts = ["rig-server", "start"];
|
|
6522
6668
|
if (hostResult.value) {
|
|
6523
6669
|
commandParts.push("--host", hostResult.value);
|
|
6524
6670
|
}
|
|
@@ -6541,7 +6687,7 @@ async function executeServer(context, args, options) {
|
|
|
6541
6687
|
const eventResult = takeOption(pending, "--event");
|
|
6542
6688
|
pending = eventResult.rest;
|
|
6543
6689
|
requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
|
|
6544
|
-
const commandParts = ["
|
|
6690
|
+
const commandParts = ["rig-server", "notify-test"];
|
|
6545
6691
|
if (eventResult.value) {
|
|
6546
6692
|
commandParts.push("--event", eventResult.value);
|
|
6547
6693
|
}
|
|
@@ -6602,10 +6748,10 @@ async function executeServer(context, args, options) {
|
|
|
6602
6748
|
}
|
|
6603
6749
|
|
|
6604
6750
|
// packages/cli/src/commands/task.ts
|
|
6605
|
-
import { readFileSync as
|
|
6751
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6606
6752
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6607
6753
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
6608
|
-
import { resolve as
|
|
6754
|
+
import { resolve as resolve21 } from "path";
|
|
6609
6755
|
import {
|
|
6610
6756
|
taskArtifactDir,
|
|
6611
6757
|
taskArtifacts,
|
|
@@ -6936,7 +7082,7 @@ async function executeTask(context, args, options) {
|
|
|
6936
7082
|
const fileFlag = takeOption(rest.slice(1), "--file");
|
|
6937
7083
|
let content;
|
|
6938
7084
|
if (fileFlag.value) {
|
|
6939
|
-
content =
|
|
7085
|
+
content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
|
|
6940
7086
|
} else {
|
|
6941
7087
|
content = await readStdin();
|
|
6942
7088
|
}
|
|
@@ -7157,8 +7303,8 @@ async function executeTask(context, args, options) {
|
|
|
7157
7303
|
}
|
|
7158
7304
|
|
|
7159
7305
|
// packages/cli/src/commands/task-run-driver.ts
|
|
7160
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
7161
|
-
import { resolve as
|
|
7306
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7307
|
+
import { resolve as resolve22 } from "path";
|
|
7162
7308
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
7163
7309
|
import { createInterface as createLineInterface } from "readline";
|
|
7164
7310
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
@@ -7192,7 +7338,24 @@ import {
|
|
|
7192
7338
|
commitRunChanges,
|
|
7193
7339
|
runPrAutomation
|
|
7194
7340
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
7341
|
+
function looksLikeGitHubToken(value) {
|
|
7342
|
+
const token = value?.trim();
|
|
7343
|
+
if (!token)
|
|
7344
|
+
return false;
|
|
7345
|
+
return /^(gh[opusr]_|github_pat_)/.test(token);
|
|
7346
|
+
}
|
|
7347
|
+
function githubBridgeEnv(token) {
|
|
7348
|
+
const clean = token?.trim();
|
|
7349
|
+
if (!clean)
|
|
7350
|
+
return {};
|
|
7351
|
+
return {
|
|
7352
|
+
RIG_GITHUB_TOKEN: clean,
|
|
7353
|
+
GITHUB_TOKEN: clean,
|
|
7354
|
+
GH_TOKEN: clean
|
|
7355
|
+
};
|
|
7356
|
+
}
|
|
7195
7357
|
function buildPiRigBridgeEnv(input) {
|
|
7358
|
+
const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
|
|
7196
7359
|
return {
|
|
7197
7360
|
RIG_PROJECT_ROOT: input.projectRoot,
|
|
7198
7361
|
PROJECT_RIG_ROOT: input.projectRoot,
|
|
@@ -7202,7 +7365,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
7202
7365
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
7203
7366
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
7204
7367
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
7205
|
-
...
|
|
7368
|
+
...githubBridgeEnv(githubToken)
|
|
7206
7369
|
};
|
|
7207
7370
|
}
|
|
7208
7371
|
function runGitSync(cwd, args, input) {
|
|
@@ -7224,12 +7387,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
7224
7387
|
return 0;
|
|
7225
7388
|
let copied = 0;
|
|
7226
7389
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
7227
|
-
const sourcePath =
|
|
7228
|
-
const targetPath =
|
|
7390
|
+
const sourcePath = resolve22(sourceRoot, relativePath);
|
|
7391
|
+
const targetPath = resolve22(targetRoot, relativePath);
|
|
7229
7392
|
try {
|
|
7230
7393
|
if (!statSync2(sourcePath).isFile())
|
|
7231
7394
|
continue;
|
|
7232
|
-
mkdirSync8(
|
|
7395
|
+
mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
|
|
7233
7396
|
copyFileSync3(sourcePath, targetPath);
|
|
7234
7397
|
copied += 1;
|
|
7235
7398
|
} catch {}
|
|
@@ -7263,6 +7426,14 @@ function buildTaskRunReviewEnv(config) {
|
|
|
7263
7426
|
...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
|
|
7264
7427
|
};
|
|
7265
7428
|
}
|
|
7429
|
+
function buildDirtyBaselineHandshakeEnv(input) {
|
|
7430
|
+
if (input.baselineMode !== "dirty-snapshot")
|
|
7431
|
+
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
7432
|
+
return {
|
|
7433
|
+
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
7434
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
7435
|
+
};
|
|
7436
|
+
}
|
|
7266
7437
|
function positiveInt(value, fallback) {
|
|
7267
7438
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
7268
7439
|
}
|
|
@@ -7371,9 +7542,9 @@ function createCommandRunner(binary) {
|
|
|
7371
7542
|
const stderrChunks = [];
|
|
7372
7543
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7373
7544
|
child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7374
|
-
return await new Promise((
|
|
7375
|
-
child.once("error", (error) =>
|
|
7376
|
-
child.once("close", (code) =>
|
|
7545
|
+
return await new Promise((resolve23) => {
|
|
7546
|
+
child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
|
|
7547
|
+
child.once("close", (code) => resolve23({
|
|
7377
7548
|
exitCode: code ?? 1,
|
|
7378
7549
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
7379
7550
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -7569,7 +7740,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
|
|
|
7569
7740
|
return null;
|
|
7570
7741
|
}
|
|
7571
7742
|
for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
|
|
7572
|
-
const summary = readJsonFile3(
|
|
7743
|
+
const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
|
|
7573
7744
|
if (!summary || summary.status !== "fail") {
|
|
7574
7745
|
continue;
|
|
7575
7746
|
}
|
|
@@ -7650,9 +7821,9 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
7650
7821
|
if (!input.taskId || !input.workspaceDir) {
|
|
7651
7822
|
return { accepted: false, reason: null };
|
|
7652
7823
|
}
|
|
7653
|
-
const artifactDir =
|
|
7654
|
-
const reviewStatusPath =
|
|
7655
|
-
const taskResultPath =
|
|
7824
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7825
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7826
|
+
const taskResultPath = resolve22(artifactDir, "task-result.json");
|
|
7656
7827
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7657
7828
|
if (reviewStatus !== "APPROVED") {
|
|
7658
7829
|
return { accepted: false, reason: null };
|
|
@@ -7689,12 +7860,12 @@ function resolveTaskRunRetryContext(input) {
|
|
|
7689
7860
|
if (!input.taskId || !input.workspaceDir) {
|
|
7690
7861
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
7691
7862
|
}
|
|
7692
|
-
const artifactDir =
|
|
7693
|
-
const reviewStatePath =
|
|
7694
|
-
const reviewFeedbackPath =
|
|
7695
|
-
const reviewStatusPath =
|
|
7696
|
-
const failedApproachesPath =
|
|
7697
|
-
const validationSummaryPath =
|
|
7863
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7864
|
+
const reviewStatePath = resolve22(artifactDir, "review-state.json");
|
|
7865
|
+
const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
|
|
7866
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7867
|
+
const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
7868
|
+
const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
|
|
7698
7869
|
const reviewState = readJsonFile3(reviewStatePath, null);
|
|
7699
7870
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7700
7871
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
@@ -7749,11 +7920,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
7749
7920
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
7750
7921
|
}
|
|
7751
7922
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
7752
|
-
if (!
|
|
7923
|
+
if (!existsSync13(reviewStatusPath)) {
|
|
7753
7924
|
return null;
|
|
7754
7925
|
}
|
|
7755
7926
|
try {
|
|
7756
|
-
const status =
|
|
7927
|
+
const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
7757
7928
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
7758
7929
|
} catch {
|
|
7759
7930
|
return null;
|
|
@@ -7836,8 +8007,11 @@ function stringArrayField(record, key) {
|
|
|
7836
8007
|
}
|
|
7837
8008
|
async function executeRigOwnedTaskRun(context, input) {
|
|
7838
8009
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
8010
|
+
const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
|
|
8011
|
+
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
8012
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
7839
8013
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
7840
|
-
|
|
8014
|
+
let prompt = buildRunPrompt({
|
|
7841
8015
|
projectRoot: context.projectRoot,
|
|
7842
8016
|
taskId: input.taskId,
|
|
7843
8017
|
fallbackTitle: input.title,
|
|
@@ -7891,14 +8065,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7891
8065
|
taskId: runtimeTaskId,
|
|
7892
8066
|
createdAt: startedAt,
|
|
7893
8067
|
runtimeAdapter: input.runtimeAdapter,
|
|
7894
|
-
status: "created"
|
|
8068
|
+
status: resumeMode ? "preparing" : "created"
|
|
7895
8069
|
});
|
|
7896
8070
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7897
8071
|
status: "preparing",
|
|
7898
8072
|
startedAt,
|
|
7899
8073
|
completedAt: null,
|
|
7900
8074
|
errorText: null,
|
|
7901
|
-
artifactRoot: null,
|
|
8075
|
+
artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
|
|
7902
8076
|
runtimeAdapter: input.runtimeAdapter,
|
|
7903
8077
|
runtimeMode: input.runtimeMode,
|
|
7904
8078
|
interactionMode: input.interactionMode,
|
|
@@ -7914,9 +8088,9 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7914
8088
|
detail: input.taskId ?? input.title ?? runtimeTaskId
|
|
7915
8089
|
});
|
|
7916
8090
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7917
|
-
id: `log:${input.runId}:start`,
|
|
7918
|
-
title: "Rig task run started",
|
|
7919
|
-
detail: input.taskId ?? input.title ?? runtimeTaskId,
|
|
8091
|
+
id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
|
|
8092
|
+
title: resumeMode ? "Rig task run resumed" : "Rig task run started",
|
|
8093
|
+
detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
|
|
7920
8094
|
tone: "info",
|
|
7921
8095
|
status: "preparing",
|
|
7922
8096
|
createdAt: startedAt
|
|
@@ -7937,7 +8111,22 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7937
8111
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
7938
8112
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
7939
8113
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
7940
|
-
|
|
8114
|
+
const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
8115
|
+
const persistedPlanning = {
|
|
8116
|
+
...planningClassification,
|
|
8117
|
+
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
8118
|
+
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
8119
|
+
classifiedAt: new Date().toISOString()
|
|
8120
|
+
};
|
|
8121
|
+
mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
8122
|
+
writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
8123
|
+
`, "utf8");
|
|
8124
|
+
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
8125
|
+
prompt = `${prompt}
|
|
8126
|
+
|
|
8127
|
+
Rig planning classification:
|
|
8128
|
+
${JSON.stringify(persistedPlanning, null, 2)}
|
|
8129
|
+
${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."}`;
|
|
7941
8130
|
if (input.runtimeAdapter === "pi") {
|
|
7942
8131
|
for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
|
|
7943
8132
|
appendPiStageLog({
|
|
@@ -7979,11 +8168,11 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7979
8168
|
let reviewAction;
|
|
7980
8169
|
let verificationStarted = false;
|
|
7981
8170
|
let reviewStarted = false;
|
|
7982
|
-
let latestRuntimeWorkspace = null;
|
|
7983
|
-
let latestSessionDir = null;
|
|
7984
|
-
let latestLogsDir = null;
|
|
8171
|
+
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
8172
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
|
|
8173
|
+
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
7985
8174
|
let latestProviderCommand = null;
|
|
7986
|
-
let latestRuntimeBranch = null;
|
|
8175
|
+
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
7987
8176
|
let snapshotSidecarPromise = null;
|
|
7988
8177
|
let dirtyBaselineApplied = false;
|
|
7989
8178
|
const childEnv = {
|
|
@@ -8001,9 +8190,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8001
8190
|
RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
|
|
8002
8191
|
RIG_SERVER_RUN_ID: input.runId
|
|
8003
8192
|
},
|
|
8004
|
-
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
8193
|
+
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
|
|
8194
|
+
...resumeMode ? {
|
|
8195
|
+
RIG_RUN_RESUME: "1",
|
|
8196
|
+
RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
|
|
8197
|
+
} : {}
|
|
8005
8198
|
};
|
|
8006
8199
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
8200
|
+
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
8007
8201
|
const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
|
|
8008
8202
|
const maxAttempts = automationLimits.maxValidationAttempts;
|
|
8009
8203
|
const promoteToValidating = (detail) => {
|
|
@@ -8058,22 +8252,29 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8058
8252
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8059
8253
|
status: "running",
|
|
8060
8254
|
worktreePath: latestRuntimeWorkspace,
|
|
8061
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
8255
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
8062
8256
|
logRoot: latestLogsDir,
|
|
8063
|
-
sessionPath: latestSessionDir ?
|
|
8064
|
-
sessionLogPath: latestLogsDir ?
|
|
8257
|
+
sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
|
|
8258
|
+
sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
|
|
8065
8259
|
branch: runtimeId
|
|
8066
8260
|
});
|
|
8067
8261
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
8068
8262
|
dirtyBaselineApplied = true;
|
|
8069
8263
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
8264
|
+
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
8265
|
+
if (readyFile) {
|
|
8266
|
+
mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
|
|
8267
|
+
writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
8268
|
+
`, "utf8");
|
|
8269
|
+
}
|
|
8070
8270
|
appendRunLog(context.projectRoot, input.runId, {
|
|
8071
8271
|
id: `log:${input.runId}:dirty-baseline`,
|
|
8072
8272
|
title: "Dirty baseline snapshot",
|
|
8073
8273
|
detail: dirty.detail,
|
|
8074
8274
|
tone: dirty.applied ? "tool" : "info",
|
|
8075
8275
|
status: dirty.applied ? "completed" : "skipped",
|
|
8076
|
-
createdAt: new Date().toISOString()
|
|
8276
|
+
createdAt: new Date().toISOString(),
|
|
8277
|
+
payload: readyFile ? { readyFile } : undefined
|
|
8077
8278
|
});
|
|
8078
8279
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
8079
8280
|
}
|
|
@@ -8299,7 +8500,36 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8299
8500
|
let reviewFailureDetail = null;
|
|
8300
8501
|
const stderrLines = [];
|
|
8301
8502
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
8302
|
-
|
|
8503
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
8504
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8505
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
8506
|
+
title: "Resume continuing from closeout phase",
|
|
8507
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
8508
|
+
tone: "info",
|
|
8509
|
+
status: resumePreviousStatus,
|
|
8510
|
+
createdAt: new Date().toISOString()
|
|
8511
|
+
});
|
|
8512
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
8513
|
+
exit = { code: 0, signal: null };
|
|
8514
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
8515
|
+
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
8516
|
+
taskId: input.taskId ?? runtimeTaskId,
|
|
8517
|
+
workspaceDir: latestRuntimeWorkspace
|
|
8518
|
+
});
|
|
8519
|
+
if (acceptedArtifactState.accepted) {
|
|
8520
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8521
|
+
id: `log:${input.runId}:resume-accepted-artifacts`,
|
|
8522
|
+
title: "Resume found accepted artifacts; continuing closeout",
|
|
8523
|
+
detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
|
|
8524
|
+
tone: "info",
|
|
8525
|
+
status: "validating",
|
|
8526
|
+
createdAt: new Date().toISOString()
|
|
8527
|
+
});
|
|
8528
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
|
|
8529
|
+
exit = { code: 0, signal: null };
|
|
8530
|
+
}
|
|
8531
|
+
}
|
|
8532
|
+
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
8303
8533
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
8304
8534
|
const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
8305
8535
|
cwd: context.projectRoot,
|
|
@@ -8340,7 +8570,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8340
8570
|
let acceptedArtifactObservedAt = null;
|
|
8341
8571
|
let acceptedArtifactPollTimer = null;
|
|
8342
8572
|
let acceptedArtifactKillTimer = null;
|
|
8343
|
-
const attemptExit = await new Promise((
|
|
8573
|
+
const attemptExit = await new Promise((resolve23) => {
|
|
8344
8574
|
let settled = false;
|
|
8345
8575
|
const settle = (result) => {
|
|
8346
8576
|
if (settled)
|
|
@@ -8348,7 +8578,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8348
8578
|
settled = true;
|
|
8349
8579
|
if (acceptedArtifactPollTimer)
|
|
8350
8580
|
clearInterval(acceptedArtifactPollTimer);
|
|
8351
|
-
|
|
8581
|
+
resolve23(result);
|
|
8352
8582
|
};
|
|
8353
8583
|
const pollAcceptedArtifacts = () => {
|
|
8354
8584
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -8545,6 +8775,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8545
8775
|
});
|
|
8546
8776
|
throw new CliError2(terminalFailureDetail, exit.code ?? 1);
|
|
8547
8777
|
}
|
|
8778
|
+
if (planningClassification.planningRequired) {
|
|
8779
|
+
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
8780
|
+
const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
|
|
8781
|
+
if (!existsSync13(expectedPlanPath)) {
|
|
8782
|
+
const failedAt = new Date().toISOString();
|
|
8783
|
+
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
8784
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8785
|
+
status: "needs_attention",
|
|
8786
|
+
completedAt: failedAt,
|
|
8787
|
+
errorText: failureDetail
|
|
8788
|
+
});
|
|
8789
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8790
|
+
id: `log:${input.runId}:plan-artifact-missing`,
|
|
8791
|
+
title: "Required plan artifact missing",
|
|
8792
|
+
detail: failureDetail,
|
|
8793
|
+
tone: "error",
|
|
8794
|
+
status: "needs_attention",
|
|
8795
|
+
createdAt: failedAt
|
|
8796
|
+
});
|
|
8797
|
+
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
8798
|
+
throw new CliError2(failureDetail, 1);
|
|
8799
|
+
}
|
|
8800
|
+
}
|
|
8548
8801
|
const runPiPrFeedbackFix = async (message2) => {
|
|
8549
8802
|
appendPiStageLog({
|
|
8550
8803
|
projectRoot: context.projectRoot,
|
|
@@ -8602,9 +8855,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8602
8855
|
});
|
|
8603
8856
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
8604
8857
|
});
|
|
8605
|
-
const exitCode = await new Promise((
|
|
8606
|
-
child.once("error", () =>
|
|
8607
|
-
child.once("close", (code) =>
|
|
8858
|
+
const exitCode = await new Promise((resolve23) => {
|
|
8859
|
+
child.once("error", () => resolve23(1));
|
|
8860
|
+
child.once("close", (code) => resolve23(code ?? 1));
|
|
8608
8861
|
});
|
|
8609
8862
|
if (exitCode !== 0) {
|
|
8610
8863
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
@@ -8723,8 +8976,8 @@ async function executeTest(context, args) {
|
|
|
8723
8976
|
}
|
|
8724
8977
|
|
|
8725
8978
|
// packages/cli/src/commands/setup.ts
|
|
8726
|
-
import { existsSync as
|
|
8727
|
-
import { resolve as
|
|
8979
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
8980
|
+
import { resolve as resolve23 } from "path";
|
|
8728
8981
|
import { createPluginHost } from "@rig/core";
|
|
8729
8982
|
import {
|
|
8730
8983
|
isSupportedBunVersion as isSupportedBunVersion2,
|
|
@@ -8787,9 +9040,9 @@ function runSetupInit(projectRoot) {
|
|
|
8787
9040
|
mkdirSync9(stateDir, { recursive: true });
|
|
8788
9041
|
mkdirSync9(logsDir, { recursive: true });
|
|
8789
9042
|
mkdirSync9(artifactsDir, { recursive: true });
|
|
8790
|
-
const failuresPath =
|
|
8791
|
-
if (!
|
|
8792
|
-
|
|
9043
|
+
const failuresPath = resolve23(stateDir, "failed_approaches.md");
|
|
9044
|
+
if (!existsSync14(failuresPath)) {
|
|
9045
|
+
writeFileSync7(failuresPath, `# Failed Approaches
|
|
8793
9046
|
|
|
8794
9047
|
`, "utf-8");
|
|
8795
9048
|
}
|
|
@@ -8806,18 +9059,18 @@ async function runSetupCheck(projectRoot) {
|
|
|
8806
9059
|
}
|
|
8807
9060
|
async function runSetupPreflight(projectRoot) {
|
|
8808
9061
|
await runSetupCheck(projectRoot);
|
|
8809
|
-
const validationRoot =
|
|
8810
|
-
if (
|
|
9062
|
+
const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
9063
|
+
if (existsSync14(validationRoot)) {
|
|
8811
9064
|
const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
8812
9065
|
for (const validator of validators) {
|
|
8813
|
-
const script =
|
|
8814
|
-
if (
|
|
9066
|
+
const script = resolve23(validationRoot, validator.name, "validate.sh");
|
|
9067
|
+
if (existsSync14(script)) {
|
|
8815
9068
|
console.log(`OK: validator script ${script}`);
|
|
8816
9069
|
}
|
|
8817
9070
|
}
|
|
8818
9071
|
}
|
|
8819
|
-
const hooksRoot =
|
|
8820
|
-
if (
|
|
9072
|
+
const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
|
|
9073
|
+
if (existsSync14(hooksRoot)) {
|
|
8821
9074
|
const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
|
|
8822
9075
|
for (const hook of hooks) {
|
|
8823
9076
|
console.log(`OK: hook ${hook}`);
|
|
@@ -9211,8 +9464,8 @@ var __testOnly = {
|
|
|
9211
9464
|
validateRequiredBugPromptValue
|
|
9212
9465
|
};
|
|
9213
9466
|
// packages/cli/src/launcher.ts
|
|
9214
|
-
import { existsSync as
|
|
9215
|
-
import { resolve as
|
|
9467
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9468
|
+
import { basename as basename2, resolve as resolve24 } from "path";
|
|
9216
9469
|
import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
|
|
9217
9470
|
import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
|
|
9218
9471
|
function parsePolicyMode(value) {
|
|
@@ -9225,7 +9478,7 @@ function parsePolicyMode(value) {
|
|
|
9225
9478
|
throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
|
|
9226
9479
|
}
|
|
9227
9480
|
function hasRigProjectMarker(candidate) {
|
|
9228
|
-
return
|
|
9481
|
+
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"));
|
|
9229
9482
|
}
|
|
9230
9483
|
function resolveProjectRoot({
|
|
9231
9484
|
envProjectRoot,
|
|
@@ -9234,17 +9487,19 @@ function resolveProjectRoot({
|
|
|
9234
9487
|
cwd = process.cwd()
|
|
9235
9488
|
}) {
|
|
9236
9489
|
if (envProjectRoot) {
|
|
9237
|
-
return
|
|
9490
|
+
return resolve24(cwd, envProjectRoot);
|
|
9238
9491
|
}
|
|
9239
9492
|
const fallbackImportDir = importDir ?? cwd;
|
|
9240
|
-
const
|
|
9493
|
+
const execName = basename2(execPath).toLowerCase();
|
|
9494
|
+
const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
|
|
9495
|
+
const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
|
|
9241
9496
|
for (const candidate of candidates) {
|
|
9242
9497
|
const nearest = resolveNearestRigProjectRoot(candidate);
|
|
9243
9498
|
if (hasRigProjectMarker(nearest)) {
|
|
9244
9499
|
return nearest;
|
|
9245
9500
|
}
|
|
9246
9501
|
}
|
|
9247
|
-
return
|
|
9502
|
+
return resolve24(cwd);
|
|
9248
9503
|
}
|
|
9249
9504
|
function normalizeCliErrorCode(message2, isCliError) {
|
|
9250
9505
|
if (message2.startsWith("Invalid --policy-mode value:")) {
|
|
@@ -9311,7 +9566,7 @@ async function runRigCli(module, options = {}) {
|
|
|
9311
9566
|
runId: context.runId,
|
|
9312
9567
|
outcome,
|
|
9313
9568
|
eventsFile: context.eventBus.getEventsFile(),
|
|
9314
|
-
policyFile:
|
|
9569
|
+
policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
|
|
9315
9570
|
policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
|
|
9316
9571
|
}, null, 2));
|
|
9317
9572
|
}
|