@h-rig/cli 0.0.6-alpha.0 → 0.0.6-alpha.10
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 +445 -222
- 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 +22 -4
- 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 +142 -62
- 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 +132 -17
- package/dist/src/commands/task.js +28 -10
- package/dist/src/commands.js +433 -213
- package/dist/src/index.js +445 -222
- 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,7 +2849,7 @@ 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));
|
|
2834
2853
|
}
|
|
2835
2854
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
2836
2855
|
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
@@ -2921,9 +2940,9 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2921
2940
|
}
|
|
2922
2941
|
|
|
2923
2942
|
// packages/cli/src/commands/_pi-install.ts
|
|
2924
|
-
import { existsSync as
|
|
2943
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2925
2944
|
import { homedir as homedir3 } from "os";
|
|
2926
|
-
import { resolve as
|
|
2945
|
+
import { resolve as resolve10 } from "path";
|
|
2927
2946
|
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2928
2947
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2929
2948
|
export { default } from '@rig/pi-rig';
|
|
@@ -2938,11 +2957,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
2938
2957
|
return { exitCode, stdout, stderr };
|
|
2939
2958
|
}
|
|
2940
2959
|
function resolvePiRigExtensionPath(homeDir) {
|
|
2941
|
-
return
|
|
2960
|
+
return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
2942
2961
|
}
|
|
2943
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
2944
|
-
const localPackage =
|
|
2945
|
-
if (exists(
|
|
2962
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
|
|
2963
|
+
const localPackage = resolve10(projectRoot, "packages", "pi-rig");
|
|
2964
|
+
if (exists(resolve10(localPackage, "package.json")))
|
|
2946
2965
|
return localPackage;
|
|
2947
2966
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
2948
2967
|
}
|
|
@@ -2993,13 +3012,13 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
2993
3012
|
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
2994
3013
|
};
|
|
2995
3014
|
}
|
|
2996
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists =
|
|
3015
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
|
|
2997
3016
|
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
2998
|
-
const indexPath =
|
|
3017
|
+
const indexPath = resolve10(extensionPath, "index.ts");
|
|
2999
3018
|
if (!exists(indexPath))
|
|
3000
3019
|
return;
|
|
3001
3020
|
try {
|
|
3002
|
-
const content =
|
|
3021
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
3003
3022
|
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
3004
3023
|
rmSync3(extensionPath, { recursive: true, force: true });
|
|
3005
3024
|
}
|
|
@@ -3015,13 +3034,13 @@ async function checkPiRigInstall(input = {}) {
|
|
|
3015
3034
|
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
3016
3035
|
};
|
|
3017
3036
|
}
|
|
3018
|
-
const exists = input.exists ??
|
|
3037
|
+
const exists = input.exists ?? existsSync6;
|
|
3019
3038
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3020
3039
|
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
3021
3040
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
3022
3041
|
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
3023
3042
|
${piListResult.stderr}`);
|
|
3024
|
-
const legacyBridge = exists(
|
|
3043
|
+
const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
|
|
3025
3044
|
const hasPiRig = listedPiRig;
|
|
3026
3045
|
return {
|
|
3027
3046
|
extensionPath,
|
|
@@ -3359,7 +3378,7 @@ async function executeQueue(context, args) {
|
|
|
3359
3378
|
}
|
|
3360
3379
|
|
|
3361
3380
|
// packages/cli/src/commands/agent.ts
|
|
3362
|
-
import { resolve as
|
|
3381
|
+
import { resolve as resolve12 } from "path";
|
|
3363
3382
|
import {
|
|
3364
3383
|
agentId,
|
|
3365
3384
|
cleanupAgentRuntime,
|
|
@@ -3369,8 +3388,8 @@ import {
|
|
|
3369
3388
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3370
3389
|
|
|
3371
3390
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3372
|
-
import { existsSync as
|
|
3373
|
-
import { resolve as
|
|
3391
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3392
|
+
import { resolve as resolve11 } from "path";
|
|
3374
3393
|
import {
|
|
3375
3394
|
readAuthorityRun,
|
|
3376
3395
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3392,8 +3411,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3392
3411
|
return "claude-code";
|
|
3393
3412
|
}
|
|
3394
3413
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3395
|
-
const issuesPath =
|
|
3396
|
-
if (!
|
|
3414
|
+
const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3415
|
+
if (!existsSync7(issuesPath)) {
|
|
3397
3416
|
return null;
|
|
3398
3417
|
}
|
|
3399
3418
|
let latest = null;
|
|
@@ -3428,6 +3447,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3428
3447
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
3429
3448
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
3430
3449
|
const next = {
|
|
3450
|
+
...existing ?? {},
|
|
3431
3451
|
runId: input.runId,
|
|
3432
3452
|
projectRoot,
|
|
3433
3453
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -3460,7 +3480,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3460
3480
|
} else if ("errorText" in next) {
|
|
3461
3481
|
delete next.errorText;
|
|
3462
3482
|
}
|
|
3463
|
-
writeJsonFile3(
|
|
3483
|
+
writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3464
3484
|
return next;
|
|
3465
3485
|
}
|
|
3466
3486
|
|
|
@@ -3581,10 +3601,10 @@ async function executeAgent(context, args) {
|
|
|
3581
3601
|
status: "running",
|
|
3582
3602
|
startedAt: createdAt,
|
|
3583
3603
|
worktreePath: runtime.workspaceDir,
|
|
3584
|
-
artifactRoot:
|
|
3604
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3585
3605
|
logRoot: runtime.logsDir,
|
|
3586
|
-
sessionPath:
|
|
3587
|
-
sessionLogPath:
|
|
3606
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3607
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3588
3608
|
pid: process.pid
|
|
3589
3609
|
});
|
|
3590
3610
|
const result = await runInAgentRuntime({
|
|
@@ -3604,10 +3624,10 @@ async function executeAgent(context, args) {
|
|
|
3604
3624
|
startedAt: createdAt,
|
|
3605
3625
|
completedAt: failedAt,
|
|
3606
3626
|
worktreePath: runtime.workspaceDir,
|
|
3607
|
-
artifactRoot:
|
|
3627
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3608
3628
|
logRoot: runtime.logsDir,
|
|
3609
|
-
sessionPath:
|
|
3610
|
-
sessionLogPath:
|
|
3629
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3630
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3611
3631
|
pid: process.pid,
|
|
3612
3632
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3613
3633
|
});
|
|
@@ -3624,10 +3644,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3624
3644
|
startedAt: createdAt,
|
|
3625
3645
|
completedAt,
|
|
3626
3646
|
worktreePath: runtime.workspaceDir,
|
|
3627
|
-
artifactRoot:
|
|
3647
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3628
3648
|
logRoot: runtime.logsDir,
|
|
3629
|
-
sessionPath:
|
|
3630
|
-
sessionLogPath:
|
|
3649
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3650
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3631
3651
|
pid: process.pid
|
|
3632
3652
|
});
|
|
3633
3653
|
return {
|
|
@@ -3701,7 +3721,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3701
3721
|
import {
|
|
3702
3722
|
chmodSync,
|
|
3703
3723
|
copyFileSync as copyFileSync2,
|
|
3704
|
-
existsSync as
|
|
3724
|
+
existsSync as existsSync8,
|
|
3705
3725
|
mkdirSync as mkdirSync6,
|
|
3706
3726
|
readdirSync,
|
|
3707
3727
|
readlinkSync,
|
|
@@ -3711,7 +3731,7 @@ import {
|
|
|
3711
3731
|
unlinkSync
|
|
3712
3732
|
} from "fs";
|
|
3713
3733
|
import { homedir as homedir4 } from "os";
|
|
3714
|
-
import { resolve as
|
|
3734
|
+
import { resolve as resolve13 } from "path";
|
|
3715
3735
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3716
3736
|
import {
|
|
3717
3737
|
computeRuntimeImageFingerprint,
|
|
@@ -3730,13 +3750,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3730
3750
|
|
|
3731
3751
|
// packages/cli/src/commands/dist.ts
|
|
3732
3752
|
function collectRigValidatorBuildTargets(input) {
|
|
3733
|
-
const validatorsRoot =
|
|
3734
|
-
if (!
|
|
3753
|
+
const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3754
|
+
if (!existsSync8(validatorsRoot))
|
|
3735
3755
|
return [];
|
|
3736
3756
|
const targets = [];
|
|
3737
3757
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3738
3758
|
for (const category of categories) {
|
|
3739
|
-
const categoryDir =
|
|
3759
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3740
3760
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3741
3761
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3742
3762
|
continue;
|
|
@@ -3745,7 +3765,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3745
3765
|
continue;
|
|
3746
3766
|
targets.push({
|
|
3747
3767
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3748
|
-
dest:
|
|
3768
|
+
dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3749
3769
|
cwd: input.hostProjectRoot
|
|
3750
3770
|
});
|
|
3751
3771
|
}
|
|
@@ -3754,16 +3774,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3754
3774
|
}
|
|
3755
3775
|
async function findLatestDistBinary(projectRoot) {
|
|
3756
3776
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3757
|
-
if (!
|
|
3777
|
+
if (!existsSync8(distRoot)) {
|
|
3758
3778
|
return null;
|
|
3759
3779
|
}
|
|
3760
3780
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3761
3781
|
name: entry.name,
|
|
3762
|
-
mtimeMs: statSync(
|
|
3782
|
+
mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
|
|
3763
3783
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3764
3784
|
for (const { name } of entries) {
|
|
3765
|
-
const candidate =
|
|
3766
|
-
if (
|
|
3785
|
+
const candidate = resolve13(distRoot, name, "bin", "rig");
|
|
3786
|
+
if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3767
3787
|
return candidate;
|
|
3768
3788
|
}
|
|
3769
3789
|
}
|
|
@@ -3775,7 +3795,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3775
3795
|
async function runDistDoctor(projectRoot) {
|
|
3776
3796
|
const bunPath = Bun.which("bun");
|
|
3777
3797
|
const rigPath = Bun.which("rig");
|
|
3778
|
-
const userBinDir =
|
|
3798
|
+
const userBinDir = resolve13(homedir4(), ".local/bin");
|
|
3779
3799
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3780
3800
|
let rigRunnable = false;
|
|
3781
3801
|
if (rigPath) {
|
|
@@ -3823,15 +3843,15 @@ async function executeDist(context, args) {
|
|
|
3823
3843
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3824
3844
|
let buildDir = null;
|
|
3825
3845
|
if (!source) {
|
|
3826
|
-
buildDir =
|
|
3846
|
+
buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3827
3847
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3828
|
-
source =
|
|
3848
|
+
source = resolve13(buildDir, "bin", "rig");
|
|
3829
3849
|
}
|
|
3830
|
-
if (!
|
|
3850
|
+
if (!existsSync8(source)) {
|
|
3831
3851
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3832
3852
|
}
|
|
3833
|
-
const installedPath =
|
|
3834
|
-
if (
|
|
3853
|
+
const installedPath = resolve13(installDir, "rig");
|
|
3854
|
+
if (existsSync8(installedPath)) {
|
|
3835
3855
|
unlinkSync(installedPath);
|
|
3836
3856
|
}
|
|
3837
3857
|
copyFileSync2(source, installedPath);
|
|
@@ -3873,22 +3893,22 @@ async function executeDist(context, args) {
|
|
|
3873
3893
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3874
3894
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3875
3895
|
const currentId = computeRuntimeImageId(fp);
|
|
3876
|
-
const imagesDir =
|
|
3896
|
+
const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3877
3897
|
mkdirSync6(imagesDir, { recursive: true });
|
|
3878
3898
|
let pruned = 0;
|
|
3879
3899
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3880
3900
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3881
|
-
rmSync4(
|
|
3901
|
+
rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
|
|
3882
3902
|
pruned++;
|
|
3883
3903
|
}
|
|
3884
3904
|
}
|
|
3885
3905
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3886
3906
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3887
3907
|
}
|
|
3888
|
-
const imageDir =
|
|
3889
|
-
mkdirSync6(
|
|
3890
|
-
mkdirSync6(
|
|
3891
|
-
mkdirSync6(
|
|
3908
|
+
const imageDir = resolve13(imagesDir, currentId);
|
|
3909
|
+
mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
|
|
3910
|
+
mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
|
|
3911
|
+
mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
|
|
3892
3912
|
const hookNames = [
|
|
3893
3913
|
"scope-guard",
|
|
3894
3914
|
"import-guard",
|
|
@@ -3902,25 +3922,25 @@ async function executeDist(context, args) {
|
|
|
3902
3922
|
];
|
|
3903
3923
|
const targets = [];
|
|
3904
3924
|
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:
|
|
3925
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3926
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3907
3927
|
for (const hookName of hookNames) {
|
|
3908
3928
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3909
|
-
targets.push({ source: src, dest:
|
|
3910
|
-
targets.push({ source: src, dest:
|
|
3929
|
+
targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3930
|
+
targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3911
3931
|
}
|
|
3912
|
-
const pluginsDir =
|
|
3913
|
-
const binPluginsDir =
|
|
3914
|
-
const validatorsRoot =
|
|
3915
|
-
const binValidatorsDir =
|
|
3932
|
+
const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
|
|
3933
|
+
const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3934
|
+
const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3935
|
+
const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3916
3936
|
mkdirSync6(binPluginsDir, { recursive: true });
|
|
3917
3937
|
mkdirSync6(binValidatorsDir, { recursive: true });
|
|
3918
|
-
if (
|
|
3938
|
+
if (existsSync8(pluginsDir)) {
|
|
3919
3939
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3920
3940
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3921
3941
|
if (!m)
|
|
3922
3942
|
continue;
|
|
3923
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3943
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3924
3944
|
}
|
|
3925
3945
|
}
|
|
3926
3946
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3931,17 +3951,17 @@ async function executeDist(context, args) {
|
|
|
3931
3951
|
const isValidator = dest.includes("/bin/validators/");
|
|
3932
3952
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3933
3953
|
}
|
|
3934
|
-
if (
|
|
3954
|
+
if (existsSync8(pluginsDir)) {
|
|
3935
3955
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3936
3956
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3937
3957
|
if (!m)
|
|
3938
3958
|
continue;
|
|
3939
3959
|
const pluginName = m[1];
|
|
3940
|
-
const imageBin =
|
|
3960
|
+
const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
|
|
3941
3961
|
if (!pluginName)
|
|
3942
3962
|
continue;
|
|
3943
|
-
const symlinkPath =
|
|
3944
|
-
if (
|
|
3963
|
+
const symlinkPath = resolve13(binPluginsDir, pluginName);
|
|
3964
|
+
if (existsSync8(imageBin)) {
|
|
3945
3965
|
try {
|
|
3946
3966
|
unlinkSync(symlinkPath);
|
|
3947
3967
|
} catch {}
|
|
@@ -3949,10 +3969,10 @@ async function executeDist(context, args) {
|
|
|
3949
3969
|
}
|
|
3950
3970
|
}
|
|
3951
3971
|
}
|
|
3952
|
-
if (
|
|
3972
|
+
if (existsSync8(validatorsRoot)) {
|
|
3953
3973
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3954
3974
|
for (const category of categories) {
|
|
3955
|
-
const categoryDir =
|
|
3975
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3956
3976
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3957
3977
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3958
3978
|
continue;
|
|
@@ -3960,9 +3980,9 @@ async function executeDist(context, args) {
|
|
|
3960
3980
|
if (!check || check === "index" || check === "shared")
|
|
3961
3981
|
continue;
|
|
3962
3982
|
const validatorName = `${category.name}-${check}`;
|
|
3963
|
-
const imageBin =
|
|
3964
|
-
const symlinkPath =
|
|
3965
|
-
if (
|
|
3983
|
+
const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
|
|
3984
|
+
const symlinkPath = resolve13(binValidatorsDir, validatorName);
|
|
3985
|
+
if (existsSync8(imageBin)) {
|
|
3966
3986
|
try {
|
|
3967
3987
|
unlinkSync(symlinkPath);
|
|
3968
3988
|
} catch {}
|
|
@@ -3971,18 +3991,18 @@ async function executeDist(context, args) {
|
|
|
3971
3991
|
}
|
|
3972
3992
|
}
|
|
3973
3993
|
}
|
|
3974
|
-
const agentsDir =
|
|
3975
|
-
if (
|
|
3994
|
+
const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3995
|
+
if (existsSync8(agentsDir)) {
|
|
3976
3996
|
let relinkCount = 0;
|
|
3977
3997
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3978
3998
|
if (!agentEntry.isDirectory())
|
|
3979
3999
|
continue;
|
|
3980
|
-
const agentBinDir =
|
|
3981
|
-
if (!
|
|
4000
|
+
const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
4001
|
+
if (!existsSync8(agentBinDir))
|
|
3982
4002
|
continue;
|
|
3983
4003
|
const walkDir = (dir) => {
|
|
3984
4004
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3985
|
-
const fullPath =
|
|
4005
|
+
const fullPath = resolve13(dir, entry.name);
|
|
3986
4006
|
if (entry.isDirectory()) {
|
|
3987
4007
|
walkDir(fullPath);
|
|
3988
4008
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -4016,7 +4036,7 @@ async function executeDist(context, args) {
|
|
|
4016
4036
|
|
|
4017
4037
|
// packages/cli/src/commands/inbox.ts
|
|
4018
4038
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
4019
|
-
import { resolve as
|
|
4039
|
+
import { resolve as resolve14 } from "path";
|
|
4020
4040
|
import {
|
|
4021
4041
|
listAuthorityRuns,
|
|
4022
4042
|
readJsonlFile as readJsonlFile3,
|
|
@@ -4033,7 +4053,7 @@ async function executeInbox(context, args) {
|
|
|
4033
4053
|
pending = task.rest;
|
|
4034
4054
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
4035
4055
|
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(
|
|
4056
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
4037
4057
|
runId: entry.runId,
|
|
4038
4058
|
record
|
|
4039
4059
|
})));
|
|
@@ -4061,7 +4081,7 @@ async function executeInbox(context, args) {
|
|
|
4061
4081
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4062
4082
|
throw new CliError2("decision must be approve or reject.");
|
|
4063
4083
|
}
|
|
4064
|
-
const approvalsPath =
|
|
4084
|
+
const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
4065
4085
|
const approvals = readJsonlFile3(approvalsPath);
|
|
4066
4086
|
const resolvedAt = new Date().toISOString();
|
|
4067
4087
|
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 +4098,7 @@ async function executeInbox(context, args) {
|
|
|
4078
4098
|
pending = task.rest;
|
|
4079
4099
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
4080
4100
|
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(
|
|
4101
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
4082
4102
|
runId: entry.runId,
|
|
4083
4103
|
record
|
|
4084
4104
|
})));
|
|
@@ -4120,7 +4140,7 @@ async function executeInbox(context, args) {
|
|
|
4120
4140
|
const [key, ...restValue] = entry.split("=");
|
|
4121
4141
|
return [key, restValue.join("=")];
|
|
4122
4142
|
}));
|
|
4123
|
-
const requestsPath =
|
|
4143
|
+
const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
4124
4144
|
const requests = readJsonlFile3(requestsPath);
|
|
4125
4145
|
const resolvedAt = new Date().toISOString();
|
|
4126
4146
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -4135,14 +4155,14 @@ async function executeInbox(context, args) {
|
|
|
4135
4155
|
}
|
|
4136
4156
|
|
|
4137
4157
|
// packages/cli/src/commands/init.ts
|
|
4138
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
4158
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4139
4159
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4140
|
-
import { resolve as
|
|
4160
|
+
import { resolve as resolve17 } from "path";
|
|
4141
4161
|
import { buildRigInitConfigSource } from "@rig/core";
|
|
4142
4162
|
|
|
4143
4163
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
4144
4164
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
4145
|
-
import { dirname as dirname2, resolve as
|
|
4165
|
+
import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
|
|
4146
4166
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
4147
4167
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
4148
4168
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -4164,15 +4184,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
4164
4184
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
4165
4185
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
4166
4186
|
}
|
|
4167
|
-
const resolved =
|
|
4187
|
+
const resolved = resolve15(root, relativePath);
|
|
4168
4188
|
const relativeToRoot = relative(root, resolved);
|
|
4169
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
4189
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
|
|
4170
4190
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
4171
4191
|
}
|
|
4172
4192
|
return resolved;
|
|
4173
4193
|
}
|
|
4174
4194
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
4175
|
-
const root =
|
|
4195
|
+
const root = resolve15(projectRoot);
|
|
4176
4196
|
const excludedDirectories = [...new Set([
|
|
4177
4197
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
4178
4198
|
...options.excludedDirectories ?? []
|
|
@@ -4184,7 +4204,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
4184
4204
|
for (const entry of entries) {
|
|
4185
4205
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
4186
4206
|
continue;
|
|
4187
|
-
const fullPath =
|
|
4207
|
+
const fullPath = resolve15(dir, entry.name);
|
|
4188
4208
|
if (entry.isDirectory()) {
|
|
4189
4209
|
await visit(fullPath);
|
|
4190
4210
|
continue;
|
|
@@ -4232,8 +4252,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
4232
4252
|
}
|
|
4233
4253
|
|
|
4234
4254
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
4235
|
-
import { existsSync as
|
|
4236
|
-
import { resolve as
|
|
4255
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
4256
|
+
import { resolve as resolve16 } from "path";
|
|
4237
4257
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
4238
4258
|
function check(id, label, status, detail, remediation) {
|
|
4239
4259
|
return {
|
|
@@ -4273,11 +4293,11 @@ function repoSlugFromConfig(config) {
|
|
|
4273
4293
|
function loadFallbackConfig(projectRoot) {
|
|
4274
4294
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
4275
4295
|
for (const name of candidates) {
|
|
4276
|
-
const path =
|
|
4277
|
-
if (!
|
|
4296
|
+
const path = resolve16(projectRoot, name);
|
|
4297
|
+
if (!existsSync9(path))
|
|
4278
4298
|
continue;
|
|
4279
4299
|
try {
|
|
4280
|
-
const source =
|
|
4300
|
+
const source = readFileSync5(path, "utf8");
|
|
4281
4301
|
if (name.endsWith(".json"))
|
|
4282
4302
|
return JSON.parse(source);
|
|
4283
4303
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -4356,7 +4376,7 @@ async function runRigDoctorChecks(options) {
|
|
|
4356
4376
|
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
4377
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
4358
4378
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
4359
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
4379
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
|
|
4360
4380
|
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
4381
|
const taskSourceKind = config?.taskSource?.kind;
|
|
4362
4382
|
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 +4471,10 @@ function countDoctorFailures(checks) {
|
|
|
4451
4471
|
}
|
|
4452
4472
|
|
|
4453
4473
|
// packages/cli/src/commands/init.ts
|
|
4454
|
-
var
|
|
4474
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4455
4475
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4456
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4457
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4476
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4477
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4458
4478
|
};
|
|
4459
4479
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
4460
4480
|
const trimmed = remoteUrl.trim();
|
|
@@ -4474,20 +4494,20 @@ function parseRepoSlug(value) {
|
|
|
4474
4494
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
4475
4495
|
}
|
|
4476
4496
|
function ensureRigPrivateDirs(projectRoot) {
|
|
4477
|
-
const rigDir =
|
|
4478
|
-
mkdirSync7(
|
|
4479
|
-
mkdirSync7(
|
|
4480
|
-
mkdirSync7(
|
|
4481
|
-
mkdirSync7(
|
|
4482
|
-
mkdirSync7(
|
|
4483
|
-
const taskConfigPath =
|
|
4484
|
-
if (!
|
|
4497
|
+
const rigDir = resolve17(projectRoot, ".rig");
|
|
4498
|
+
mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
|
|
4499
|
+
mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
|
|
4500
|
+
mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
|
|
4501
|
+
mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
|
|
4502
|
+
mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
|
|
4503
|
+
const taskConfigPath = resolve17(rigDir, "task-config.json");
|
|
4504
|
+
if (!existsSync10(taskConfigPath))
|
|
4485
4505
|
writeFileSync5(taskConfigPath, `{}
|
|
4486
4506
|
`, "utf-8");
|
|
4487
4507
|
}
|
|
4488
4508
|
function ensureGitignoreEntries(projectRoot) {
|
|
4489
|
-
const path =
|
|
4490
|
-
const existing =
|
|
4509
|
+
const path = resolve17(projectRoot, ".gitignore");
|
|
4510
|
+
const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
|
|
4491
4511
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
4492
4512
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4493
4513
|
if (missing.length === 0)
|
|
@@ -4500,14 +4520,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
4500
4520
|
`, "utf8");
|
|
4501
4521
|
}
|
|
4502
4522
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
4503
|
-
const path =
|
|
4504
|
-
const existing =
|
|
4523
|
+
const path = resolve17(projectRoot, "package.json");
|
|
4524
|
+
const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
|
|
4505
4525
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
4506
4526
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
4507
4527
|
devDependencies[name] = spec;
|
|
4508
4528
|
}
|
|
4509
4529
|
const next = {
|
|
4510
|
-
...
|
|
4530
|
+
...existsSync10(path) ? existing : { name: "rig-project", private: true },
|
|
4511
4531
|
devDependencies
|
|
4512
4532
|
};
|
|
4513
4533
|
writeFileSync5(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -4577,15 +4597,54 @@ async function promptSelect(prompts, options) {
|
|
|
4577
4597
|
throw new CliError2("Init cancelled.", 1);
|
|
4578
4598
|
return String(value);
|
|
4579
4599
|
}
|
|
4580
|
-
|
|
4600
|
+
function sleep2(ms) {
|
|
4601
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
4602
|
+
}
|
|
4603
|
+
function positiveIntFromEnv(name, fallback) {
|
|
4604
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
4605
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
4606
|
+
}
|
|
4607
|
+
function apiSessionTokenFrom(payload) {
|
|
4608
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4609
|
+
return null;
|
|
4610
|
+
const token = payload.apiSessionToken;
|
|
4611
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
4612
|
+
}
|
|
4613
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
4614
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
4615
|
+
authenticated: true,
|
|
4616
|
+
source: input.source,
|
|
4617
|
+
storedOnServer: true,
|
|
4618
|
+
selectedRepo: input.selectedRepo,
|
|
4619
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
4620
|
+
updatedAt: new Date().toISOString()
|
|
4621
|
+
}, null, 2)}
|
|
4622
|
+
`, "utf8");
|
|
4623
|
+
}
|
|
4624
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
4581
4625
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
4582
4626
|
return null;
|
|
4583
|
-
const
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4627
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
4628
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
4629
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
4630
|
+
const deadline = Date.now() + timeoutMs;
|
|
4631
|
+
let last = null;
|
|
4632
|
+
do {
|
|
4633
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
4634
|
+
method: "POST",
|
|
4635
|
+
headers: { "content-type": "application/json" },
|
|
4636
|
+
body: JSON.stringify({ pollId })
|
|
4637
|
+
}).catch(() => null);
|
|
4638
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4639
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
4640
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
4641
|
+
return last;
|
|
4642
|
+
}
|
|
4643
|
+
if (timeoutMs <= 0)
|
|
4644
|
+
return last;
|
|
4645
|
+
await sleep2(intervalMs);
|
|
4646
|
+
} while (Date.now() < deadline);
|
|
4647
|
+
return last;
|
|
4589
4648
|
}
|
|
4590
4649
|
async function runControlPlaneInit(context, options) {
|
|
4591
4650
|
const projectRoot = context.projectRoot;
|
|
@@ -4608,9 +4667,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
4608
4667
|
});
|
|
4609
4668
|
ensureRigPrivateDirs(projectRoot);
|
|
4610
4669
|
ensureGitignoreEntries(projectRoot);
|
|
4611
|
-
const configTsPath =
|
|
4612
|
-
const configJsonPath =
|
|
4613
|
-
const configExists =
|
|
4670
|
+
const configTsPath = resolve17(projectRoot, "rig.config.ts");
|
|
4671
|
+
const configJsonPath = resolve17(projectRoot, "rig.config.json");
|
|
4672
|
+
const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
|
|
4614
4673
|
if (!options.privateStateOnly) {
|
|
4615
4674
|
if (configExists && !options.repair) {
|
|
4616
4675
|
if (context.outputMode !== "json")
|
|
@@ -4626,7 +4685,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
4626
4685
|
}
|
|
4627
4686
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
4628
4687
|
}
|
|
4629
|
-
writeFileSync5(
|
|
4688
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
4630
4689
|
`, "utf8");
|
|
4631
4690
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
4632
4691
|
let uploadedSnapshot = null;
|
|
@@ -4648,10 +4707,14 @@ async function runControlPlaneInit(context, options) {
|
|
|
4648
4707
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
4649
4708
|
if (token) {
|
|
4650
4709
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
4651
|
-
|
|
4710
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4711
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4652
4712
|
if (serverKind === "remote") {
|
|
4653
|
-
|
|
4654
|
-
|
|
4713
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
4714
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
4715
|
+
selectedRepo: repo.slug,
|
|
4716
|
+
apiSessionToken
|
|
4717
|
+
});
|
|
4655
4718
|
}
|
|
4656
4719
|
} else if (authMethod === "device") {
|
|
4657
4720
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -4660,9 +4723,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
4660
4723
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
4661
4724
|
});
|
|
4662
4725
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4663
|
-
|
|
4664
|
-
|
|
4726
|
+
if (context.outputMode !== "json") {
|
|
4727
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
4728
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
4729
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
4730
|
+
}
|
|
4731
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
4732
|
+
if (completed) {
|
|
4733
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
4734
|
+
if (apiSessionToken) {
|
|
4735
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
4736
|
+
if (serverKind === "remote") {
|
|
4737
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
|
|
4738
|
+
}
|
|
4739
|
+
}
|
|
4665
4740
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
4741
|
+
}
|
|
4666
4742
|
}
|
|
4667
4743
|
let remoteCheckoutPreparation = null;
|
|
4668
4744
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -4682,6 +4758,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
4682
4758
|
});
|
|
4683
4759
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
4684
4760
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
4761
|
+
if (serverRootSwitch && token) {
|
|
4762
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
4763
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4764
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4765
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
|
|
4766
|
+
}
|
|
4685
4767
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
4686
4768
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
4687
4769
|
remote: true,
|
|
@@ -4791,7 +4873,7 @@ function parseInitOptions(args) {
|
|
|
4791
4873
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
4792
4874
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
4793
4875
|
const projectRoot = context.projectRoot;
|
|
4794
|
-
const existingConfig =
|
|
4876
|
+
const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
|
|
4795
4877
|
let repair = false;
|
|
4796
4878
|
let privateStateOnly = false;
|
|
4797
4879
|
if (existingConfig) {
|
|
@@ -4891,7 +4973,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
4891
4973
|
});
|
|
4892
4974
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
4893
4975
|
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")}.` : "";
|
|
4976
|
+
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
4977
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
4896
4978
|
return result;
|
|
4897
4979
|
}
|
|
@@ -5053,8 +5135,8 @@ async function executeDoctor(context, args) {
|
|
|
5053
5135
|
}
|
|
5054
5136
|
|
|
5055
5137
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
5056
|
-
import { readFileSync as
|
|
5057
|
-
import { resolve as
|
|
5138
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
5139
|
+
import { resolve as resolve18 } from "path";
|
|
5058
5140
|
import {
|
|
5059
5141
|
appendJsonlRecord as appendJsonlRecord2,
|
|
5060
5142
|
readAuthorityRun as readAuthorityRun2,
|
|
@@ -5074,7 +5156,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
|
|
|
5074
5156
|
...patch,
|
|
5075
5157
|
updatedAt: new Date().toISOString()
|
|
5076
5158
|
};
|
|
5077
|
-
writeJsonFile4(
|
|
5159
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
|
|
5078
5160
|
return next;
|
|
5079
5161
|
}
|
|
5080
5162
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -5082,21 +5164,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
5082
5164
|
if (!current) {
|
|
5083
5165
|
return;
|
|
5084
5166
|
}
|
|
5085
|
-
writeJsonFile4(
|
|
5167
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
|
|
5086
5168
|
...current,
|
|
5087
5169
|
updatedAt: new Date().toISOString()
|
|
5088
5170
|
});
|
|
5089
5171
|
}
|
|
5090
5172
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
5091
|
-
appendJsonlRecord2(
|
|
5173
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
|
|
5092
5174
|
touchAuthorityRun(projectRoot, runId);
|
|
5093
5175
|
}
|
|
5094
5176
|
function appendRunLog(projectRoot, runId, value) {
|
|
5095
|
-
appendJsonlRecord2(
|
|
5177
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
|
|
5096
5178
|
touchAuthorityRun(projectRoot, runId);
|
|
5097
5179
|
}
|
|
5098
5180
|
function appendRunAction(projectRoot, runId, value) {
|
|
5099
|
-
appendJsonlRecord2(
|
|
5181
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
|
|
5100
5182
|
id: value.id,
|
|
5101
5183
|
type: "action",
|
|
5102
5184
|
actionType: value.actionType,
|
|
@@ -5167,7 +5249,7 @@ function buildRunPrompt(input) {
|
|
|
5167
5249
|
})();
|
|
5168
5250
|
const scopeText = (() => {
|
|
5169
5251
|
try {
|
|
5170
|
-
const parsed = JSON.parse(
|
|
5252
|
+
const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
|
|
5171
5253
|
const entry = parsed[input.taskId] ?? {};
|
|
5172
5254
|
const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
|
|
5173
5255
|
const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
|
|
@@ -5280,8 +5362,8 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
5280
5362
|
}
|
|
5281
5363
|
|
|
5282
5364
|
// packages/cli/src/commands/inspect.ts
|
|
5283
|
-
import { existsSync as
|
|
5284
|
-
import { resolve as
|
|
5365
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
5366
|
+
import { resolve as resolve19 } from "path";
|
|
5285
5367
|
import {
|
|
5286
5368
|
listAuthorityRuns as listAuthorityRuns2,
|
|
5287
5369
|
readAuthorityRun as readAuthorityRun3,
|
|
@@ -5302,8 +5384,8 @@ async function executeInspect(context, args) {
|
|
|
5302
5384
|
if (!latestRun) {
|
|
5303
5385
|
throw new CliError2(`No runs found for ${requiredTask}.`);
|
|
5304
5386
|
}
|
|
5305
|
-
const logsPath =
|
|
5306
|
-
if (!
|
|
5387
|
+
const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
5388
|
+
if (!existsSync11(logsPath)) {
|
|
5307
5389
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
5308
5390
|
}
|
|
5309
5391
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -5313,7 +5395,7 @@ async function executeInspect(context, args) {
|
|
|
5313
5395
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
5314
5396
|
requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
|
|
5315
5397
|
const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
|
|
5316
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
5398
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
|
|
5317
5399
|
if (!artifactRoot) {
|
|
5318
5400
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
5319
5401
|
}
|
|
@@ -5370,10 +5452,10 @@ async function executeInspect(context, args) {
|
|
|
5370
5452
|
case "failures": {
|
|
5371
5453
|
requireNoExtraArgs(rest, "bun run rig inspect failures");
|
|
5372
5454
|
const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
|
|
5373
|
-
if (!
|
|
5455
|
+
if (!existsSync11(failed)) {
|
|
5374
5456
|
console.log("No failures recorded.");
|
|
5375
5457
|
} else {
|
|
5376
|
-
process.stdout.write(
|
|
5458
|
+
process.stdout.write(readFileSync8(failed, "utf-8"));
|
|
5377
5459
|
}
|
|
5378
5460
|
return { ok: true, group: "inspect", command };
|
|
5379
5461
|
}
|
|
@@ -5390,11 +5472,11 @@ async function executeInspect(context, args) {
|
|
|
5390
5472
|
return { ok: true, group: "inspect", command };
|
|
5391
5473
|
case "audit": {
|
|
5392
5474
|
requireNoExtraArgs(rest, "bun run rig inspect audit");
|
|
5393
|
-
const auditPath =
|
|
5394
|
-
if (!
|
|
5475
|
+
const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
|
|
5476
|
+
if (!existsSync11(auditPath)) {
|
|
5395
5477
|
console.log("No audit log found.");
|
|
5396
5478
|
} else {
|
|
5397
|
-
const lines =
|
|
5479
|
+
const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
5398
5480
|
for (const line of lines) {
|
|
5399
5481
|
console.log(line);
|
|
5400
5482
|
}
|
|
@@ -6023,8 +6105,8 @@ async function executeRemote(context, args) {
|
|
|
6023
6105
|
}
|
|
6024
6106
|
|
|
6025
6107
|
// packages/cli/src/commands/run.ts
|
|
6026
|
-
import { existsSync as
|
|
6027
|
-
import { resolve as
|
|
6108
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
6109
|
+
import { resolve as resolve20 } from "path";
|
|
6028
6110
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6029
6111
|
import {
|
|
6030
6112
|
listAuthorityRuns as listAuthorityRuns3,
|
|
@@ -6038,6 +6120,7 @@ import {
|
|
|
6038
6120
|
listOpenEpics,
|
|
6039
6121
|
resolveDefaultEpic,
|
|
6040
6122
|
runResume,
|
|
6123
|
+
runRestart,
|
|
6041
6124
|
runStatus,
|
|
6042
6125
|
runStop,
|
|
6043
6126
|
startRun,
|
|
@@ -6146,6 +6229,17 @@ async function attachRunOperatorView(context, input) {
|
|
|
6146
6229
|
}
|
|
6147
6230
|
|
|
6148
6231
|
// packages/cli/src/commands/run.ts
|
|
6232
|
+
function normalizeRemoteRunDetails(payload) {
|
|
6233
|
+
const run = payload.run;
|
|
6234
|
+
if (!run || typeof run !== "object" || Array.isArray(run))
|
|
6235
|
+
return null;
|
|
6236
|
+
return {
|
|
6237
|
+
...run,
|
|
6238
|
+
...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
|
|
6239
|
+
...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
|
|
6240
|
+
...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
|
|
6241
|
+
};
|
|
6242
|
+
}
|
|
6149
6243
|
function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
|
|
6150
6244
|
if (noEpicPrompt) {
|
|
6151
6245
|
return false;
|
|
@@ -6284,7 +6378,7 @@ async function executeRun(context, args) {
|
|
|
6284
6378
|
if (!run.value) {
|
|
6285
6379
|
throw new CliError2("run show requires --run <id>.");
|
|
6286
6380
|
}
|
|
6287
|
-
const record = readAuthorityRun4(context.projectRoot, run.value);
|
|
6381
|
+
const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
|
|
6288
6382
|
if (!record) {
|
|
6289
6383
|
throw new CliError2(`Run not found: ${run.value}`, 2);
|
|
6290
6384
|
}
|
|
@@ -6303,7 +6397,7 @@ async function executeRun(context, args) {
|
|
|
6303
6397
|
if (!run.value) {
|
|
6304
6398
|
throw new CliError2("run timeline requires --run <id>.");
|
|
6305
6399
|
}
|
|
6306
|
-
const timelinePath =
|
|
6400
|
+
const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
|
|
6307
6401
|
const printEvents = () => {
|
|
6308
6402
|
const events2 = readJsonlFile4(timelinePath);
|
|
6309
6403
|
if (context.outputMode === "text") {
|
|
@@ -6315,12 +6409,12 @@ async function executeRun(context, args) {
|
|
|
6315
6409
|
};
|
|
6316
6410
|
const events = printEvents();
|
|
6317
6411
|
if (follow.value && context.outputMode === "text") {
|
|
6318
|
-
let lastLength =
|
|
6412
|
+
let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
|
|
6319
6413
|
while (true) {
|
|
6320
6414
|
await Bun.sleep(1000);
|
|
6321
|
-
if (!
|
|
6415
|
+
if (!existsSync12(timelinePath))
|
|
6322
6416
|
continue;
|
|
6323
|
-
const next =
|
|
6417
|
+
const next = readFileSync9(timelinePath, "utf8");
|
|
6324
6418
|
if (next.length <= lastLength)
|
|
6325
6419
|
continue;
|
|
6326
6420
|
const delta = next.slice(lastLength);
|
|
@@ -6465,6 +6559,20 @@ async function executeRun(context, args) {
|
|
|
6465
6559
|
}
|
|
6466
6560
|
return { ok: true, group: "run", command, details: resumed };
|
|
6467
6561
|
}
|
|
6562
|
+
case "restart": {
|
|
6563
|
+
requireNoExtraArgs(rest, "bun run rig run restart");
|
|
6564
|
+
if (context.dryRun) {
|
|
6565
|
+
if (context.outputMode === "text") {
|
|
6566
|
+
console.log("[dry-run] rig run restart");
|
|
6567
|
+
}
|
|
6568
|
+
return { ok: true, group: "run", command };
|
|
6569
|
+
}
|
|
6570
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
6571
|
+
if (context.outputMode === "text") {
|
|
6572
|
+
console.log(`Restarted run: ${restarted.runId}`);
|
|
6573
|
+
}
|
|
6574
|
+
return { ok: true, group: "run", command, details: restarted };
|
|
6575
|
+
}
|
|
6468
6576
|
case "stop": {
|
|
6469
6577
|
const runOption = takeOption(rest, "--run");
|
|
6470
6578
|
const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
|
|
@@ -6522,7 +6630,7 @@ async function executeServer(context, args, options) {
|
|
|
6522
6630
|
const authTokenResult = takeOption(pending, "--auth-token");
|
|
6523
6631
|
pending = authTokenResult.rest;
|
|
6524
6632
|
requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
|
|
6525
|
-
const commandParts = ["
|
|
6633
|
+
const commandParts = ["rig-server", "start"];
|
|
6526
6634
|
if (hostResult.value) {
|
|
6527
6635
|
commandParts.push("--host", hostResult.value);
|
|
6528
6636
|
}
|
|
@@ -6545,7 +6653,7 @@ async function executeServer(context, args, options) {
|
|
|
6545
6653
|
const eventResult = takeOption(pending, "--event");
|
|
6546
6654
|
pending = eventResult.rest;
|
|
6547
6655
|
requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
|
|
6548
|
-
const commandParts = ["
|
|
6656
|
+
const commandParts = ["rig-server", "notify-test"];
|
|
6549
6657
|
if (eventResult.value) {
|
|
6550
6658
|
commandParts.push("--event", eventResult.value);
|
|
6551
6659
|
}
|
|
@@ -6606,10 +6714,10 @@ async function executeServer(context, args, options) {
|
|
|
6606
6714
|
}
|
|
6607
6715
|
|
|
6608
6716
|
// packages/cli/src/commands/task.ts
|
|
6609
|
-
import { readFileSync as
|
|
6717
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6610
6718
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6611
6719
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
6612
|
-
import { resolve as
|
|
6720
|
+
import { resolve as resolve21 } from "path";
|
|
6613
6721
|
import {
|
|
6614
6722
|
taskArtifactDir,
|
|
6615
6723
|
taskArtifacts,
|
|
@@ -6940,7 +7048,7 @@ async function executeTask(context, args, options) {
|
|
|
6940
7048
|
const fileFlag = takeOption(rest.slice(1), "--file");
|
|
6941
7049
|
let content;
|
|
6942
7050
|
if (fileFlag.value) {
|
|
6943
|
-
content =
|
|
7051
|
+
content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
|
|
6944
7052
|
} else {
|
|
6945
7053
|
content = await readStdin();
|
|
6946
7054
|
}
|
|
@@ -7161,8 +7269,8 @@ async function executeTask(context, args, options) {
|
|
|
7161
7269
|
}
|
|
7162
7270
|
|
|
7163
7271
|
// packages/cli/src/commands/task-run-driver.ts
|
|
7164
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
7165
|
-
import { resolve as
|
|
7272
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7273
|
+
import { resolve as resolve22 } from "path";
|
|
7166
7274
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
7167
7275
|
import { createInterface as createLineInterface } from "readline";
|
|
7168
7276
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
@@ -7196,7 +7304,24 @@ import {
|
|
|
7196
7304
|
commitRunChanges,
|
|
7197
7305
|
runPrAutomation
|
|
7198
7306
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
7307
|
+
function looksLikeGitHubToken(value) {
|
|
7308
|
+
const token = value?.trim();
|
|
7309
|
+
if (!token)
|
|
7310
|
+
return false;
|
|
7311
|
+
return /^(gh[opusr]_|github_pat_)/.test(token);
|
|
7312
|
+
}
|
|
7313
|
+
function githubBridgeEnv(token) {
|
|
7314
|
+
const clean = token?.trim();
|
|
7315
|
+
if (!clean)
|
|
7316
|
+
return {};
|
|
7317
|
+
return {
|
|
7318
|
+
RIG_GITHUB_TOKEN: clean,
|
|
7319
|
+
GITHUB_TOKEN: clean,
|
|
7320
|
+
GH_TOKEN: clean
|
|
7321
|
+
};
|
|
7322
|
+
}
|
|
7199
7323
|
function buildPiRigBridgeEnv(input) {
|
|
7324
|
+
const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
|
|
7200
7325
|
return {
|
|
7201
7326
|
RIG_PROJECT_ROOT: input.projectRoot,
|
|
7202
7327
|
PROJECT_RIG_ROOT: input.projectRoot,
|
|
@@ -7206,7 +7331,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
7206
7331
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
7207
7332
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
7208
7333
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
7209
|
-
...
|
|
7334
|
+
...githubBridgeEnv(githubToken)
|
|
7210
7335
|
};
|
|
7211
7336
|
}
|
|
7212
7337
|
function runGitSync(cwd, args, input) {
|
|
@@ -7228,12 +7353,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
7228
7353
|
return 0;
|
|
7229
7354
|
let copied = 0;
|
|
7230
7355
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
7231
|
-
const sourcePath =
|
|
7232
|
-
const targetPath =
|
|
7356
|
+
const sourcePath = resolve22(sourceRoot, relativePath);
|
|
7357
|
+
const targetPath = resolve22(targetRoot, relativePath);
|
|
7233
7358
|
try {
|
|
7234
7359
|
if (!statSync2(sourcePath).isFile())
|
|
7235
7360
|
continue;
|
|
7236
|
-
mkdirSync8(
|
|
7361
|
+
mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
|
|
7237
7362
|
copyFileSync3(sourcePath, targetPath);
|
|
7238
7363
|
copied += 1;
|
|
7239
7364
|
} catch {}
|
|
@@ -7267,6 +7392,14 @@ function buildTaskRunReviewEnv(config) {
|
|
|
7267
7392
|
...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
|
|
7268
7393
|
};
|
|
7269
7394
|
}
|
|
7395
|
+
function buildDirtyBaselineHandshakeEnv(input) {
|
|
7396
|
+
if (input.baselineMode !== "dirty-snapshot")
|
|
7397
|
+
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
7398
|
+
return {
|
|
7399
|
+
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
7400
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
7401
|
+
};
|
|
7402
|
+
}
|
|
7270
7403
|
function positiveInt(value, fallback) {
|
|
7271
7404
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
7272
7405
|
}
|
|
@@ -7359,6 +7492,12 @@ function appendPiStageLog(input) {
|
|
|
7359
7492
|
});
|
|
7360
7493
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
7361
7494
|
}
|
|
7495
|
+
async function runCheckedCommand(command, args, cwd, label = "git") {
|
|
7496
|
+
const result = await command(args, { cwd });
|
|
7497
|
+
if (result.exitCode !== 0) {
|
|
7498
|
+
throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
|
|
7499
|
+
}
|
|
7500
|
+
}
|
|
7362
7501
|
function createCommandRunner(binary) {
|
|
7363
7502
|
return async (args, options) => {
|
|
7364
7503
|
const child = spawn2(binary, [...args], {
|
|
@@ -7369,9 +7508,9 @@ function createCommandRunner(binary) {
|
|
|
7369
7508
|
const stderrChunks = [];
|
|
7370
7509
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7371
7510
|
child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7372
|
-
return await new Promise((
|
|
7373
|
-
child.once("error", (error) =>
|
|
7374
|
-
child.once("close", (code) =>
|
|
7511
|
+
return await new Promise((resolve23) => {
|
|
7512
|
+
child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
|
|
7513
|
+
child.once("close", (code) => resolve23({
|
|
7375
7514
|
exitCode: code ?? 1,
|
|
7376
7515
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
7377
7516
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -7434,6 +7573,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
7434
7573
|
command: gitCommand
|
|
7435
7574
|
});
|
|
7436
7575
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
7576
|
+
await runCheckedCommand(gitCommand, ["push", "--set-upstream", "origin", branch], workspace, "git");
|
|
7437
7577
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
7438
7578
|
const pr = await prAutomation({
|
|
7439
7579
|
projectRoot: workspace,
|
|
@@ -7444,7 +7584,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
7444
7584
|
sourceTask: input.sourceTask,
|
|
7445
7585
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
7446
7586
|
command: ghCommand,
|
|
7447
|
-
gitCommand,
|
|
7448
7587
|
steerPi,
|
|
7449
7588
|
lifecycle: {
|
|
7450
7589
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -7567,7 +7706,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
|
|
|
7567
7706
|
return null;
|
|
7568
7707
|
}
|
|
7569
7708
|
for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
|
|
7570
|
-
const summary = readJsonFile3(
|
|
7709
|
+
const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
|
|
7571
7710
|
if (!summary || summary.status !== "fail") {
|
|
7572
7711
|
continue;
|
|
7573
7712
|
}
|
|
@@ -7648,9 +7787,9 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
7648
7787
|
if (!input.taskId || !input.workspaceDir) {
|
|
7649
7788
|
return { accepted: false, reason: null };
|
|
7650
7789
|
}
|
|
7651
|
-
const artifactDir =
|
|
7652
|
-
const reviewStatusPath =
|
|
7653
|
-
const taskResultPath =
|
|
7790
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7791
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7792
|
+
const taskResultPath = resolve22(artifactDir, "task-result.json");
|
|
7654
7793
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7655
7794
|
if (reviewStatus !== "APPROVED") {
|
|
7656
7795
|
return { accepted: false, reason: null };
|
|
@@ -7687,12 +7826,12 @@ function resolveTaskRunRetryContext(input) {
|
|
|
7687
7826
|
if (!input.taskId || !input.workspaceDir) {
|
|
7688
7827
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
7689
7828
|
}
|
|
7690
|
-
const artifactDir =
|
|
7691
|
-
const reviewStatePath =
|
|
7692
|
-
const reviewFeedbackPath =
|
|
7693
|
-
const reviewStatusPath =
|
|
7694
|
-
const failedApproachesPath =
|
|
7695
|
-
const validationSummaryPath =
|
|
7829
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7830
|
+
const reviewStatePath = resolve22(artifactDir, "review-state.json");
|
|
7831
|
+
const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
|
|
7832
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7833
|
+
const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
7834
|
+
const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
|
|
7696
7835
|
const reviewState = readJsonFile3(reviewStatePath, null);
|
|
7697
7836
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7698
7837
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
@@ -7747,11 +7886,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
7747
7886
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
7748
7887
|
}
|
|
7749
7888
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
7750
|
-
if (!
|
|
7889
|
+
if (!existsSync13(reviewStatusPath)) {
|
|
7751
7890
|
return null;
|
|
7752
7891
|
}
|
|
7753
7892
|
try {
|
|
7754
|
-
const status =
|
|
7893
|
+
const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
7755
7894
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
7756
7895
|
} catch {
|
|
7757
7896
|
return null;
|
|
@@ -7834,8 +7973,11 @@ function stringArrayField(record, key) {
|
|
|
7834
7973
|
}
|
|
7835
7974
|
async function executeRigOwnedTaskRun(context, input) {
|
|
7836
7975
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
7976
|
+
const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
|
|
7977
|
+
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
7978
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
7837
7979
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
7838
|
-
|
|
7980
|
+
let prompt = buildRunPrompt({
|
|
7839
7981
|
projectRoot: context.projectRoot,
|
|
7840
7982
|
taskId: input.taskId,
|
|
7841
7983
|
fallbackTitle: input.title,
|
|
@@ -7889,14 +8031,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7889
8031
|
taskId: runtimeTaskId,
|
|
7890
8032
|
createdAt: startedAt,
|
|
7891
8033
|
runtimeAdapter: input.runtimeAdapter,
|
|
7892
|
-
status: "created"
|
|
8034
|
+
status: resumeMode ? "preparing" : "created"
|
|
7893
8035
|
});
|
|
7894
8036
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7895
8037
|
status: "preparing",
|
|
7896
8038
|
startedAt,
|
|
7897
8039
|
completedAt: null,
|
|
7898
8040
|
errorText: null,
|
|
7899
|
-
artifactRoot: null,
|
|
8041
|
+
artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
|
|
7900
8042
|
runtimeAdapter: input.runtimeAdapter,
|
|
7901
8043
|
runtimeMode: input.runtimeMode,
|
|
7902
8044
|
interactionMode: input.interactionMode,
|
|
@@ -7912,9 +8054,9 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7912
8054
|
detail: input.taskId ?? input.title ?? runtimeTaskId
|
|
7913
8055
|
});
|
|
7914
8056
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7915
|
-
id: `log:${input.runId}:start`,
|
|
7916
|
-
title: "Rig task run started",
|
|
7917
|
-
detail: input.taskId ?? input.title ?? runtimeTaskId,
|
|
8057
|
+
id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
|
|
8058
|
+
title: resumeMode ? "Rig task run resumed" : "Rig task run started",
|
|
8059
|
+
detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
|
|
7918
8060
|
tone: "info",
|
|
7919
8061
|
status: "preparing",
|
|
7920
8062
|
createdAt: startedAt
|
|
@@ -7935,7 +8077,22 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7935
8077
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
7936
8078
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
7937
8079
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
7938
|
-
|
|
8080
|
+
const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
8081
|
+
const persistedPlanning = {
|
|
8082
|
+
...planningClassification,
|
|
8083
|
+
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
8084
|
+
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
8085
|
+
classifiedAt: new Date().toISOString()
|
|
8086
|
+
};
|
|
8087
|
+
mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
8088
|
+
writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
8089
|
+
`, "utf8");
|
|
8090
|
+
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
8091
|
+
prompt = `${prompt}
|
|
8092
|
+
|
|
8093
|
+
Rig planning classification:
|
|
8094
|
+
${JSON.stringify(persistedPlanning, null, 2)}
|
|
8095
|
+
${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."}`;
|
|
7939
8096
|
if (input.runtimeAdapter === "pi") {
|
|
7940
8097
|
for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
|
|
7941
8098
|
appendPiStageLog({
|
|
@@ -7977,11 +8134,11 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7977
8134
|
let reviewAction;
|
|
7978
8135
|
let verificationStarted = false;
|
|
7979
8136
|
let reviewStarted = false;
|
|
7980
|
-
let latestRuntimeWorkspace = null;
|
|
7981
|
-
let latestSessionDir = null;
|
|
7982
|
-
let latestLogsDir = null;
|
|
8137
|
+
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
8138
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
|
|
8139
|
+
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
7983
8140
|
let latestProviderCommand = null;
|
|
7984
|
-
let latestRuntimeBranch = null;
|
|
8141
|
+
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
7985
8142
|
let snapshotSidecarPromise = null;
|
|
7986
8143
|
let dirtyBaselineApplied = false;
|
|
7987
8144
|
const childEnv = {
|
|
@@ -7999,9 +8156,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7999
8156
|
RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
|
|
8000
8157
|
RIG_SERVER_RUN_ID: input.runId
|
|
8001
8158
|
},
|
|
8002
|
-
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
8159
|
+
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
|
|
8160
|
+
...resumeMode ? {
|
|
8161
|
+
RIG_RUN_RESUME: "1",
|
|
8162
|
+
RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
|
|
8163
|
+
} : {}
|
|
8003
8164
|
};
|
|
8004
8165
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
8166
|
+
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
8005
8167
|
const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
|
|
8006
8168
|
const maxAttempts = automationLimits.maxValidationAttempts;
|
|
8007
8169
|
const promoteToValidating = (detail) => {
|
|
@@ -8056,22 +8218,29 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8056
8218
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8057
8219
|
status: "running",
|
|
8058
8220
|
worktreePath: latestRuntimeWorkspace,
|
|
8059
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
8221
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
8060
8222
|
logRoot: latestLogsDir,
|
|
8061
|
-
sessionPath: latestSessionDir ?
|
|
8062
|
-
sessionLogPath: latestLogsDir ?
|
|
8223
|
+
sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
|
|
8224
|
+
sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
|
|
8063
8225
|
branch: runtimeId
|
|
8064
8226
|
});
|
|
8065
8227
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
8066
8228
|
dirtyBaselineApplied = true;
|
|
8067
8229
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
8230
|
+
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
8231
|
+
if (readyFile) {
|
|
8232
|
+
mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
|
|
8233
|
+
writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
8234
|
+
`, "utf8");
|
|
8235
|
+
}
|
|
8068
8236
|
appendRunLog(context.projectRoot, input.runId, {
|
|
8069
8237
|
id: `log:${input.runId}:dirty-baseline`,
|
|
8070
8238
|
title: "Dirty baseline snapshot",
|
|
8071
8239
|
detail: dirty.detail,
|
|
8072
8240
|
tone: dirty.applied ? "tool" : "info",
|
|
8073
8241
|
status: dirty.applied ? "completed" : "skipped",
|
|
8074
|
-
createdAt: new Date().toISOString()
|
|
8242
|
+
createdAt: new Date().toISOString(),
|
|
8243
|
+
payload: readyFile ? { readyFile } : undefined
|
|
8075
8244
|
});
|
|
8076
8245
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
8077
8246
|
}
|
|
@@ -8297,7 +8466,36 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8297
8466
|
let reviewFailureDetail = null;
|
|
8298
8467
|
const stderrLines = [];
|
|
8299
8468
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
8300
|
-
|
|
8469
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
8470
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8471
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
8472
|
+
title: "Resume continuing from closeout phase",
|
|
8473
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
8474
|
+
tone: "info",
|
|
8475
|
+
status: resumePreviousStatus,
|
|
8476
|
+
createdAt: new Date().toISOString()
|
|
8477
|
+
});
|
|
8478
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
8479
|
+
exit = { code: 0, signal: null };
|
|
8480
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
8481
|
+
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
8482
|
+
taskId: input.taskId ?? runtimeTaskId,
|
|
8483
|
+
workspaceDir: latestRuntimeWorkspace
|
|
8484
|
+
});
|
|
8485
|
+
if (acceptedArtifactState.accepted) {
|
|
8486
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8487
|
+
id: `log:${input.runId}:resume-accepted-artifacts`,
|
|
8488
|
+
title: "Resume found accepted artifacts; continuing closeout",
|
|
8489
|
+
detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
|
|
8490
|
+
tone: "info",
|
|
8491
|
+
status: "validating",
|
|
8492
|
+
createdAt: new Date().toISOString()
|
|
8493
|
+
});
|
|
8494
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
|
|
8495
|
+
exit = { code: 0, signal: null };
|
|
8496
|
+
}
|
|
8497
|
+
}
|
|
8498
|
+
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
8301
8499
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
8302
8500
|
const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
8303
8501
|
cwd: context.projectRoot,
|
|
@@ -8338,7 +8536,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8338
8536
|
let acceptedArtifactObservedAt = null;
|
|
8339
8537
|
let acceptedArtifactPollTimer = null;
|
|
8340
8538
|
let acceptedArtifactKillTimer = null;
|
|
8341
|
-
const attemptExit = await new Promise((
|
|
8539
|
+
const attemptExit = await new Promise((resolve23) => {
|
|
8342
8540
|
let settled = false;
|
|
8343
8541
|
const settle = (result) => {
|
|
8344
8542
|
if (settled)
|
|
@@ -8346,7 +8544,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8346
8544
|
settled = true;
|
|
8347
8545
|
if (acceptedArtifactPollTimer)
|
|
8348
8546
|
clearInterval(acceptedArtifactPollTimer);
|
|
8349
|
-
|
|
8547
|
+
resolve23(result);
|
|
8350
8548
|
};
|
|
8351
8549
|
const pollAcceptedArtifacts = () => {
|
|
8352
8550
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -8543,6 +8741,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8543
8741
|
});
|
|
8544
8742
|
throw new CliError2(terminalFailureDetail, exit.code ?? 1);
|
|
8545
8743
|
}
|
|
8744
|
+
if (planningClassification.planningRequired) {
|
|
8745
|
+
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
8746
|
+
const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
|
|
8747
|
+
if (!existsSync13(expectedPlanPath)) {
|
|
8748
|
+
const failedAt = new Date().toISOString();
|
|
8749
|
+
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
8750
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8751
|
+
status: "needs_attention",
|
|
8752
|
+
completedAt: failedAt,
|
|
8753
|
+
errorText: failureDetail
|
|
8754
|
+
});
|
|
8755
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8756
|
+
id: `log:${input.runId}:plan-artifact-missing`,
|
|
8757
|
+
title: "Required plan artifact missing",
|
|
8758
|
+
detail: failureDetail,
|
|
8759
|
+
tone: "error",
|
|
8760
|
+
status: "needs_attention",
|
|
8761
|
+
createdAt: failedAt
|
|
8762
|
+
});
|
|
8763
|
+
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
8764
|
+
throw new CliError2(failureDetail, 1);
|
|
8765
|
+
}
|
|
8766
|
+
}
|
|
8546
8767
|
const runPiPrFeedbackFix = async (message2) => {
|
|
8547
8768
|
appendPiStageLog({
|
|
8548
8769
|
projectRoot: context.projectRoot,
|
|
@@ -8600,9 +8821,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8600
8821
|
});
|
|
8601
8822
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
8602
8823
|
});
|
|
8603
|
-
const exitCode = await new Promise((
|
|
8604
|
-
child.once("error", () =>
|
|
8605
|
-
child.once("close", (code) =>
|
|
8824
|
+
const exitCode = await new Promise((resolve23) => {
|
|
8825
|
+
child.once("error", () => resolve23(1));
|
|
8826
|
+
child.once("close", (code) => resolve23(code ?? 1));
|
|
8606
8827
|
});
|
|
8607
8828
|
if (exitCode !== 0) {
|
|
8608
8829
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
@@ -8721,8 +8942,8 @@ async function executeTest(context, args) {
|
|
|
8721
8942
|
}
|
|
8722
8943
|
|
|
8723
8944
|
// packages/cli/src/commands/setup.ts
|
|
8724
|
-
import { existsSync as
|
|
8725
|
-
import { resolve as
|
|
8945
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
8946
|
+
import { resolve as resolve23 } from "path";
|
|
8726
8947
|
import { createPluginHost } from "@rig/core";
|
|
8727
8948
|
import {
|
|
8728
8949
|
isSupportedBunVersion as isSupportedBunVersion2,
|
|
@@ -8785,9 +9006,9 @@ function runSetupInit(projectRoot) {
|
|
|
8785
9006
|
mkdirSync9(stateDir, { recursive: true });
|
|
8786
9007
|
mkdirSync9(logsDir, { recursive: true });
|
|
8787
9008
|
mkdirSync9(artifactsDir, { recursive: true });
|
|
8788
|
-
const failuresPath =
|
|
8789
|
-
if (!
|
|
8790
|
-
|
|
9009
|
+
const failuresPath = resolve23(stateDir, "failed_approaches.md");
|
|
9010
|
+
if (!existsSync14(failuresPath)) {
|
|
9011
|
+
writeFileSync7(failuresPath, `# Failed Approaches
|
|
8791
9012
|
|
|
8792
9013
|
`, "utf-8");
|
|
8793
9014
|
}
|
|
@@ -8804,18 +9025,18 @@ async function runSetupCheck(projectRoot) {
|
|
|
8804
9025
|
}
|
|
8805
9026
|
async function runSetupPreflight(projectRoot) {
|
|
8806
9027
|
await runSetupCheck(projectRoot);
|
|
8807
|
-
const validationRoot =
|
|
8808
|
-
if (
|
|
9028
|
+
const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
9029
|
+
if (existsSync14(validationRoot)) {
|
|
8809
9030
|
const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
8810
9031
|
for (const validator of validators) {
|
|
8811
|
-
const script =
|
|
8812
|
-
if (
|
|
9032
|
+
const script = resolve23(validationRoot, validator.name, "validate.sh");
|
|
9033
|
+
if (existsSync14(script)) {
|
|
8813
9034
|
console.log(`OK: validator script ${script}`);
|
|
8814
9035
|
}
|
|
8815
9036
|
}
|
|
8816
9037
|
}
|
|
8817
|
-
const hooksRoot =
|
|
8818
|
-
if (
|
|
9038
|
+
const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
|
|
9039
|
+
if (existsSync14(hooksRoot)) {
|
|
8819
9040
|
const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
|
|
8820
9041
|
for (const hook of hooks) {
|
|
8821
9042
|
console.log(`OK: hook ${hook}`);
|
|
@@ -9192,8 +9413,8 @@ async function executeGroup(context, group, args) {
|
|
|
9192
9413
|
}
|
|
9193
9414
|
}
|
|
9194
9415
|
// packages/cli/src/launcher.ts
|
|
9195
|
-
import { existsSync as
|
|
9196
|
-
import { resolve as
|
|
9416
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9417
|
+
import { basename as basename2, resolve as resolve24 } from "path";
|
|
9197
9418
|
import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
|
|
9198
9419
|
import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
|
|
9199
9420
|
function parsePolicyMode(value) {
|
|
@@ -9206,7 +9427,7 @@ function parsePolicyMode(value) {
|
|
|
9206
9427
|
throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
|
|
9207
9428
|
}
|
|
9208
9429
|
function hasRigProjectMarker(candidate) {
|
|
9209
|
-
return
|
|
9430
|
+
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"));
|
|
9210
9431
|
}
|
|
9211
9432
|
function resolveProjectRoot({
|
|
9212
9433
|
envProjectRoot,
|
|
@@ -9215,17 +9436,19 @@ function resolveProjectRoot({
|
|
|
9215
9436
|
cwd = process.cwd()
|
|
9216
9437
|
}) {
|
|
9217
9438
|
if (envProjectRoot) {
|
|
9218
|
-
return
|
|
9439
|
+
return resolve24(cwd, envProjectRoot);
|
|
9219
9440
|
}
|
|
9220
9441
|
const fallbackImportDir = importDir ?? cwd;
|
|
9221
|
-
const
|
|
9442
|
+
const execName = basename2(execPath).toLowerCase();
|
|
9443
|
+
const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
|
|
9444
|
+
const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
|
|
9222
9445
|
for (const candidate of candidates) {
|
|
9223
9446
|
const nearest = resolveNearestRigProjectRoot(candidate);
|
|
9224
9447
|
if (hasRigProjectMarker(nearest)) {
|
|
9225
9448
|
return nearest;
|
|
9226
9449
|
}
|
|
9227
9450
|
}
|
|
9228
|
-
return
|
|
9451
|
+
return resolve24(cwd);
|
|
9229
9452
|
}
|
|
9230
9453
|
function normalizeCliErrorCode(message2, isCliError) {
|
|
9231
9454
|
if (message2.startsWith("Invalid --policy-mode value:")) {
|
|
@@ -9292,7 +9515,7 @@ async function runRigCli(module, options = {}) {
|
|
|
9292
9515
|
runId: context.runId,
|
|
9293
9516
|
outcome,
|
|
9294
9517
|
eventsFile: context.eventBus.getEventsFile(),
|
|
9295
|
-
policyFile:
|
|
9518
|
+
policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
|
|
9296
9519
|
policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
|
|
9297
9520
|
}, null, 2));
|
|
9298
9521
|
}
|