@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/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,7 +2845,7 @@ 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));
|
|
2830
2849
|
}
|
|
2831
2850
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
2832
2851
|
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
@@ -2917,9 +2936,9 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2917
2936
|
}
|
|
2918
2937
|
|
|
2919
2938
|
// packages/cli/src/commands/_pi-install.ts
|
|
2920
|
-
import { existsSync as
|
|
2939
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2921
2940
|
import { homedir as homedir3 } from "os";
|
|
2922
|
-
import { resolve as
|
|
2941
|
+
import { resolve as resolve10 } from "path";
|
|
2923
2942
|
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2924
2943
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2925
2944
|
export { default } from '@rig/pi-rig';
|
|
@@ -2934,11 +2953,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
2934
2953
|
return { exitCode, stdout, stderr };
|
|
2935
2954
|
}
|
|
2936
2955
|
function resolvePiRigExtensionPath(homeDir) {
|
|
2937
|
-
return
|
|
2956
|
+
return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
2938
2957
|
}
|
|
2939
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
2940
|
-
const localPackage =
|
|
2941
|
-
if (exists(
|
|
2958
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
|
|
2959
|
+
const localPackage = resolve10(projectRoot, "packages", "pi-rig");
|
|
2960
|
+
if (exists(resolve10(localPackage, "package.json")))
|
|
2942
2961
|
return localPackage;
|
|
2943
2962
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
2944
2963
|
}
|
|
@@ -2989,13 +3008,13 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
2989
3008
|
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
2990
3009
|
};
|
|
2991
3010
|
}
|
|
2992
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists =
|
|
3011
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
|
|
2993
3012
|
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
2994
|
-
const indexPath =
|
|
3013
|
+
const indexPath = resolve10(extensionPath, "index.ts");
|
|
2995
3014
|
if (!exists(indexPath))
|
|
2996
3015
|
return;
|
|
2997
3016
|
try {
|
|
2998
|
-
const content =
|
|
3017
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
2999
3018
|
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
3000
3019
|
rmSync3(extensionPath, { recursive: true, force: true });
|
|
3001
3020
|
}
|
|
@@ -3011,13 +3030,13 @@ async function checkPiRigInstall(input = {}) {
|
|
|
3011
3030
|
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
3012
3031
|
};
|
|
3013
3032
|
}
|
|
3014
|
-
const exists = input.exists ??
|
|
3033
|
+
const exists = input.exists ?? existsSync6;
|
|
3015
3034
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3016
3035
|
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
3017
3036
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
3018
3037
|
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
3019
3038
|
${piListResult.stderr}`);
|
|
3020
|
-
const legacyBridge = exists(
|
|
3039
|
+
const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
|
|
3021
3040
|
const hasPiRig = listedPiRig;
|
|
3022
3041
|
return {
|
|
3023
3042
|
extensionPath,
|
|
@@ -3355,7 +3374,7 @@ async function executeQueue(context, args) {
|
|
|
3355
3374
|
}
|
|
3356
3375
|
|
|
3357
3376
|
// packages/cli/src/commands/agent.ts
|
|
3358
|
-
import { resolve as
|
|
3377
|
+
import { resolve as resolve12 } from "path";
|
|
3359
3378
|
import {
|
|
3360
3379
|
agentId,
|
|
3361
3380
|
cleanupAgentRuntime,
|
|
@@ -3365,8 +3384,8 @@ import {
|
|
|
3365
3384
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3366
3385
|
|
|
3367
3386
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3368
|
-
import { existsSync as
|
|
3369
|
-
import { resolve as
|
|
3387
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3388
|
+
import { resolve as resolve11 } from "path";
|
|
3370
3389
|
import {
|
|
3371
3390
|
readAuthorityRun,
|
|
3372
3391
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3388,8 +3407,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3388
3407
|
return "claude-code";
|
|
3389
3408
|
}
|
|
3390
3409
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3391
|
-
const issuesPath =
|
|
3392
|
-
if (!
|
|
3410
|
+
const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3411
|
+
if (!existsSync7(issuesPath)) {
|
|
3393
3412
|
return null;
|
|
3394
3413
|
}
|
|
3395
3414
|
let latest = null;
|
|
@@ -3424,6 +3443,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3424
3443
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
3425
3444
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
3426
3445
|
const next = {
|
|
3446
|
+
...existing ?? {},
|
|
3427
3447
|
runId: input.runId,
|
|
3428
3448
|
projectRoot,
|
|
3429
3449
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -3456,7 +3476,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3456
3476
|
} else if ("errorText" in next) {
|
|
3457
3477
|
delete next.errorText;
|
|
3458
3478
|
}
|
|
3459
|
-
writeJsonFile3(
|
|
3479
|
+
writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3460
3480
|
return next;
|
|
3461
3481
|
}
|
|
3462
3482
|
|
|
@@ -3577,10 +3597,10 @@ async function executeAgent(context, args) {
|
|
|
3577
3597
|
status: "running",
|
|
3578
3598
|
startedAt: createdAt,
|
|
3579
3599
|
worktreePath: runtime.workspaceDir,
|
|
3580
|
-
artifactRoot:
|
|
3600
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3581
3601
|
logRoot: runtime.logsDir,
|
|
3582
|
-
sessionPath:
|
|
3583
|
-
sessionLogPath:
|
|
3602
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3603
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3584
3604
|
pid: process.pid
|
|
3585
3605
|
});
|
|
3586
3606
|
const result = await runInAgentRuntime({
|
|
@@ -3600,10 +3620,10 @@ async function executeAgent(context, args) {
|
|
|
3600
3620
|
startedAt: createdAt,
|
|
3601
3621
|
completedAt: failedAt,
|
|
3602
3622
|
worktreePath: runtime.workspaceDir,
|
|
3603
|
-
artifactRoot:
|
|
3623
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3604
3624
|
logRoot: runtime.logsDir,
|
|
3605
|
-
sessionPath:
|
|
3606
|
-
sessionLogPath:
|
|
3625
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3626
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3607
3627
|
pid: process.pid,
|
|
3608
3628
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3609
3629
|
});
|
|
@@ -3620,10 +3640,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3620
3640
|
startedAt: createdAt,
|
|
3621
3641
|
completedAt,
|
|
3622
3642
|
worktreePath: runtime.workspaceDir,
|
|
3623
|
-
artifactRoot:
|
|
3643
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3624
3644
|
logRoot: runtime.logsDir,
|
|
3625
|
-
sessionPath:
|
|
3626
|
-
sessionLogPath:
|
|
3645
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3646
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3627
3647
|
pid: process.pid
|
|
3628
3648
|
});
|
|
3629
3649
|
return {
|
|
@@ -3697,7 +3717,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3697
3717
|
import {
|
|
3698
3718
|
chmodSync,
|
|
3699
3719
|
copyFileSync as copyFileSync2,
|
|
3700
|
-
existsSync as
|
|
3720
|
+
existsSync as existsSync8,
|
|
3701
3721
|
mkdirSync as mkdirSync6,
|
|
3702
3722
|
readdirSync,
|
|
3703
3723
|
readlinkSync,
|
|
@@ -3707,7 +3727,7 @@ import {
|
|
|
3707
3727
|
unlinkSync
|
|
3708
3728
|
} from "fs";
|
|
3709
3729
|
import { homedir as homedir4 } from "os";
|
|
3710
|
-
import { resolve as
|
|
3730
|
+
import { resolve as resolve13 } from "path";
|
|
3711
3731
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3712
3732
|
import {
|
|
3713
3733
|
computeRuntimeImageFingerprint,
|
|
@@ -3726,13 +3746,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3726
3746
|
|
|
3727
3747
|
// packages/cli/src/commands/dist.ts
|
|
3728
3748
|
function collectRigValidatorBuildTargets(input) {
|
|
3729
|
-
const validatorsRoot =
|
|
3730
|
-
if (!
|
|
3749
|
+
const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3750
|
+
if (!existsSync8(validatorsRoot))
|
|
3731
3751
|
return [];
|
|
3732
3752
|
const targets = [];
|
|
3733
3753
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3734
3754
|
for (const category of categories) {
|
|
3735
|
-
const categoryDir =
|
|
3755
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3736
3756
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3737
3757
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3738
3758
|
continue;
|
|
@@ -3741,7 +3761,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3741
3761
|
continue;
|
|
3742
3762
|
targets.push({
|
|
3743
3763
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3744
|
-
dest:
|
|
3764
|
+
dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3745
3765
|
cwd: input.hostProjectRoot
|
|
3746
3766
|
});
|
|
3747
3767
|
}
|
|
@@ -3750,16 +3770,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3750
3770
|
}
|
|
3751
3771
|
async function findLatestDistBinary(projectRoot) {
|
|
3752
3772
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3753
|
-
if (!
|
|
3773
|
+
if (!existsSync8(distRoot)) {
|
|
3754
3774
|
return null;
|
|
3755
3775
|
}
|
|
3756
3776
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3757
3777
|
name: entry.name,
|
|
3758
|
-
mtimeMs: statSync(
|
|
3778
|
+
mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
|
|
3759
3779
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3760
3780
|
for (const { name } of entries) {
|
|
3761
|
-
const candidate =
|
|
3762
|
-
if (
|
|
3781
|
+
const candidate = resolve13(distRoot, name, "bin", "rig");
|
|
3782
|
+
if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3763
3783
|
return candidate;
|
|
3764
3784
|
}
|
|
3765
3785
|
}
|
|
@@ -3771,7 +3791,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3771
3791
|
async function runDistDoctor(projectRoot) {
|
|
3772
3792
|
const bunPath = Bun.which("bun");
|
|
3773
3793
|
const rigPath = Bun.which("rig");
|
|
3774
|
-
const userBinDir =
|
|
3794
|
+
const userBinDir = resolve13(homedir4(), ".local/bin");
|
|
3775
3795
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3776
3796
|
let rigRunnable = false;
|
|
3777
3797
|
if (rigPath) {
|
|
@@ -3819,15 +3839,15 @@ async function executeDist(context, args) {
|
|
|
3819
3839
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3820
3840
|
let buildDir = null;
|
|
3821
3841
|
if (!source) {
|
|
3822
|
-
buildDir =
|
|
3842
|
+
buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3823
3843
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3824
|
-
source =
|
|
3844
|
+
source = resolve13(buildDir, "bin", "rig");
|
|
3825
3845
|
}
|
|
3826
|
-
if (!
|
|
3846
|
+
if (!existsSync8(source)) {
|
|
3827
3847
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3828
3848
|
}
|
|
3829
|
-
const installedPath =
|
|
3830
|
-
if (
|
|
3849
|
+
const installedPath = resolve13(installDir, "rig");
|
|
3850
|
+
if (existsSync8(installedPath)) {
|
|
3831
3851
|
unlinkSync(installedPath);
|
|
3832
3852
|
}
|
|
3833
3853
|
copyFileSync2(source, installedPath);
|
|
@@ -3869,22 +3889,22 @@ async function executeDist(context, args) {
|
|
|
3869
3889
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3870
3890
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3871
3891
|
const currentId = computeRuntimeImageId(fp);
|
|
3872
|
-
const imagesDir =
|
|
3892
|
+
const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3873
3893
|
mkdirSync6(imagesDir, { recursive: true });
|
|
3874
3894
|
let pruned = 0;
|
|
3875
3895
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3876
3896
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3877
|
-
rmSync4(
|
|
3897
|
+
rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
|
|
3878
3898
|
pruned++;
|
|
3879
3899
|
}
|
|
3880
3900
|
}
|
|
3881
3901
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3882
3902
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3883
3903
|
}
|
|
3884
|
-
const imageDir =
|
|
3885
|
-
mkdirSync6(
|
|
3886
|
-
mkdirSync6(
|
|
3887
|
-
mkdirSync6(
|
|
3904
|
+
const imageDir = resolve13(imagesDir, currentId);
|
|
3905
|
+
mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
|
|
3906
|
+
mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
|
|
3907
|
+
mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
|
|
3888
3908
|
const hookNames = [
|
|
3889
3909
|
"scope-guard",
|
|
3890
3910
|
"import-guard",
|
|
@@ -3898,25 +3918,25 @@ async function executeDist(context, args) {
|
|
|
3898
3918
|
];
|
|
3899
3919
|
const targets = [];
|
|
3900
3920
|
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:
|
|
3921
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3922
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3903
3923
|
for (const hookName of hookNames) {
|
|
3904
3924
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3905
|
-
targets.push({ source: src, dest:
|
|
3906
|
-
targets.push({ source: src, dest:
|
|
3925
|
+
targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3926
|
+
targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3907
3927
|
}
|
|
3908
|
-
const pluginsDir =
|
|
3909
|
-
const binPluginsDir =
|
|
3910
|
-
const validatorsRoot =
|
|
3911
|
-
const binValidatorsDir =
|
|
3928
|
+
const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
|
|
3929
|
+
const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3930
|
+
const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3931
|
+
const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3912
3932
|
mkdirSync6(binPluginsDir, { recursive: true });
|
|
3913
3933
|
mkdirSync6(binValidatorsDir, { recursive: true });
|
|
3914
|
-
if (
|
|
3934
|
+
if (existsSync8(pluginsDir)) {
|
|
3915
3935
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3916
3936
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3917
3937
|
if (!m)
|
|
3918
3938
|
continue;
|
|
3919
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3939
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3920
3940
|
}
|
|
3921
3941
|
}
|
|
3922
3942
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3927,17 +3947,17 @@ async function executeDist(context, args) {
|
|
|
3927
3947
|
const isValidator = dest.includes("/bin/validators/");
|
|
3928
3948
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3929
3949
|
}
|
|
3930
|
-
if (
|
|
3950
|
+
if (existsSync8(pluginsDir)) {
|
|
3931
3951
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3932
3952
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3933
3953
|
if (!m)
|
|
3934
3954
|
continue;
|
|
3935
3955
|
const pluginName = m[1];
|
|
3936
|
-
const imageBin =
|
|
3956
|
+
const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
|
|
3937
3957
|
if (!pluginName)
|
|
3938
3958
|
continue;
|
|
3939
|
-
const symlinkPath =
|
|
3940
|
-
if (
|
|
3959
|
+
const symlinkPath = resolve13(binPluginsDir, pluginName);
|
|
3960
|
+
if (existsSync8(imageBin)) {
|
|
3941
3961
|
try {
|
|
3942
3962
|
unlinkSync(symlinkPath);
|
|
3943
3963
|
} catch {}
|
|
@@ -3945,10 +3965,10 @@ async function executeDist(context, args) {
|
|
|
3945
3965
|
}
|
|
3946
3966
|
}
|
|
3947
3967
|
}
|
|
3948
|
-
if (
|
|
3968
|
+
if (existsSync8(validatorsRoot)) {
|
|
3949
3969
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3950
3970
|
for (const category of categories) {
|
|
3951
|
-
const categoryDir =
|
|
3971
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3952
3972
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3953
3973
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3954
3974
|
continue;
|
|
@@ -3956,9 +3976,9 @@ async function executeDist(context, args) {
|
|
|
3956
3976
|
if (!check || check === "index" || check === "shared")
|
|
3957
3977
|
continue;
|
|
3958
3978
|
const validatorName = `${category.name}-${check}`;
|
|
3959
|
-
const imageBin =
|
|
3960
|
-
const symlinkPath =
|
|
3961
|
-
if (
|
|
3979
|
+
const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
|
|
3980
|
+
const symlinkPath = resolve13(binValidatorsDir, validatorName);
|
|
3981
|
+
if (existsSync8(imageBin)) {
|
|
3962
3982
|
try {
|
|
3963
3983
|
unlinkSync(symlinkPath);
|
|
3964
3984
|
} catch {}
|
|
@@ -3967,18 +3987,18 @@ async function executeDist(context, args) {
|
|
|
3967
3987
|
}
|
|
3968
3988
|
}
|
|
3969
3989
|
}
|
|
3970
|
-
const agentsDir =
|
|
3971
|
-
if (
|
|
3990
|
+
const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3991
|
+
if (existsSync8(agentsDir)) {
|
|
3972
3992
|
let relinkCount = 0;
|
|
3973
3993
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3974
3994
|
if (!agentEntry.isDirectory())
|
|
3975
3995
|
continue;
|
|
3976
|
-
const agentBinDir =
|
|
3977
|
-
if (!
|
|
3996
|
+
const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
3997
|
+
if (!existsSync8(agentBinDir))
|
|
3978
3998
|
continue;
|
|
3979
3999
|
const walkDir = (dir) => {
|
|
3980
4000
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3981
|
-
const fullPath =
|
|
4001
|
+
const fullPath = resolve13(dir, entry.name);
|
|
3982
4002
|
if (entry.isDirectory()) {
|
|
3983
4003
|
walkDir(fullPath);
|
|
3984
4004
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -4012,7 +4032,7 @@ async function executeDist(context, args) {
|
|
|
4012
4032
|
|
|
4013
4033
|
// packages/cli/src/commands/inbox.ts
|
|
4014
4034
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
4015
|
-
import { resolve as
|
|
4035
|
+
import { resolve as resolve14 } from "path";
|
|
4016
4036
|
import {
|
|
4017
4037
|
listAuthorityRuns,
|
|
4018
4038
|
readJsonlFile as readJsonlFile3,
|
|
@@ -4029,7 +4049,7 @@ async function executeInbox(context, args) {
|
|
|
4029
4049
|
pending = task.rest;
|
|
4030
4050
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
4031
4051
|
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(
|
|
4052
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
4033
4053
|
runId: entry.runId,
|
|
4034
4054
|
record
|
|
4035
4055
|
})));
|
|
@@ -4057,7 +4077,7 @@ async function executeInbox(context, args) {
|
|
|
4057
4077
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4058
4078
|
throw new CliError2("decision must be approve or reject.");
|
|
4059
4079
|
}
|
|
4060
|
-
const approvalsPath =
|
|
4080
|
+
const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
4061
4081
|
const approvals = readJsonlFile3(approvalsPath);
|
|
4062
4082
|
const resolvedAt = new Date().toISOString();
|
|
4063
4083
|
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 +4094,7 @@ async function executeInbox(context, args) {
|
|
|
4074
4094
|
pending = task.rest;
|
|
4075
4095
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
4076
4096
|
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(
|
|
4097
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
4078
4098
|
runId: entry.runId,
|
|
4079
4099
|
record
|
|
4080
4100
|
})));
|
|
@@ -4116,7 +4136,7 @@ async function executeInbox(context, args) {
|
|
|
4116
4136
|
const [key, ...restValue] = entry.split("=");
|
|
4117
4137
|
return [key, restValue.join("=")];
|
|
4118
4138
|
}));
|
|
4119
|
-
const requestsPath =
|
|
4139
|
+
const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
4120
4140
|
const requests = readJsonlFile3(requestsPath);
|
|
4121
4141
|
const resolvedAt = new Date().toISOString();
|
|
4122
4142
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -4131,14 +4151,14 @@ async function executeInbox(context, args) {
|
|
|
4131
4151
|
}
|
|
4132
4152
|
|
|
4133
4153
|
// packages/cli/src/commands/init.ts
|
|
4134
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
4154
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4135
4155
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4136
|
-
import { resolve as
|
|
4156
|
+
import { resolve as resolve17 } from "path";
|
|
4137
4157
|
import { buildRigInitConfigSource } from "@rig/core";
|
|
4138
4158
|
|
|
4139
4159
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
4140
4160
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
4141
|
-
import { dirname as dirname2, resolve as
|
|
4161
|
+
import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
|
|
4142
4162
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
4143
4163
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
4144
4164
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -4160,15 +4180,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
4160
4180
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
4161
4181
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
4162
4182
|
}
|
|
4163
|
-
const resolved =
|
|
4183
|
+
const resolved = resolve15(root, relativePath);
|
|
4164
4184
|
const relativeToRoot = relative(root, resolved);
|
|
4165
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
4185
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
|
|
4166
4186
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
4167
4187
|
}
|
|
4168
4188
|
return resolved;
|
|
4169
4189
|
}
|
|
4170
4190
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
4171
|
-
const root =
|
|
4191
|
+
const root = resolve15(projectRoot);
|
|
4172
4192
|
const excludedDirectories = [...new Set([
|
|
4173
4193
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
4174
4194
|
...options.excludedDirectories ?? []
|
|
@@ -4180,7 +4200,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
4180
4200
|
for (const entry of entries) {
|
|
4181
4201
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
4182
4202
|
continue;
|
|
4183
|
-
const fullPath =
|
|
4203
|
+
const fullPath = resolve15(dir, entry.name);
|
|
4184
4204
|
if (entry.isDirectory()) {
|
|
4185
4205
|
await visit(fullPath);
|
|
4186
4206
|
continue;
|
|
@@ -4228,8 +4248,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
4228
4248
|
}
|
|
4229
4249
|
|
|
4230
4250
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
4231
|
-
import { existsSync as
|
|
4232
|
-
import { resolve as
|
|
4251
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
4252
|
+
import { resolve as resolve16 } from "path";
|
|
4233
4253
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
4234
4254
|
function check(id, label, status, detail, remediation) {
|
|
4235
4255
|
return {
|
|
@@ -4269,11 +4289,11 @@ function repoSlugFromConfig(config) {
|
|
|
4269
4289
|
function loadFallbackConfig(projectRoot) {
|
|
4270
4290
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
4271
4291
|
for (const name of candidates) {
|
|
4272
|
-
const path =
|
|
4273
|
-
if (!
|
|
4292
|
+
const path = resolve16(projectRoot, name);
|
|
4293
|
+
if (!existsSync9(path))
|
|
4274
4294
|
continue;
|
|
4275
4295
|
try {
|
|
4276
|
-
const source =
|
|
4296
|
+
const source = readFileSync5(path, "utf8");
|
|
4277
4297
|
if (name.endsWith(".json"))
|
|
4278
4298
|
return JSON.parse(source);
|
|
4279
4299
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -4352,7 +4372,7 @@ async function runRigDoctorChecks(options) {
|
|
|
4352
4372
|
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
4373
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
4354
4374
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
4355
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
4375
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
|
|
4356
4376
|
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
4377
|
const taskSourceKind = config?.taskSource?.kind;
|
|
4358
4378
|
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 +4467,10 @@ function countDoctorFailures(checks) {
|
|
|
4447
4467
|
}
|
|
4448
4468
|
|
|
4449
4469
|
// packages/cli/src/commands/init.ts
|
|
4450
|
-
var
|
|
4470
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4451
4471
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4452
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4453
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4472
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4473
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4454
4474
|
};
|
|
4455
4475
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
4456
4476
|
const trimmed = remoteUrl.trim();
|
|
@@ -4470,20 +4490,20 @@ function parseRepoSlug(value) {
|
|
|
4470
4490
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
4471
4491
|
}
|
|
4472
4492
|
function ensureRigPrivateDirs(projectRoot) {
|
|
4473
|
-
const rigDir =
|
|
4474
|
-
mkdirSync7(
|
|
4475
|
-
mkdirSync7(
|
|
4476
|
-
mkdirSync7(
|
|
4477
|
-
mkdirSync7(
|
|
4478
|
-
mkdirSync7(
|
|
4479
|
-
const taskConfigPath =
|
|
4480
|
-
if (!
|
|
4493
|
+
const rigDir = resolve17(projectRoot, ".rig");
|
|
4494
|
+
mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
|
|
4495
|
+
mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
|
|
4496
|
+
mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
|
|
4497
|
+
mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
|
|
4498
|
+
mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
|
|
4499
|
+
const taskConfigPath = resolve17(rigDir, "task-config.json");
|
|
4500
|
+
if (!existsSync10(taskConfigPath))
|
|
4481
4501
|
writeFileSync5(taskConfigPath, `{}
|
|
4482
4502
|
`, "utf-8");
|
|
4483
4503
|
}
|
|
4484
4504
|
function ensureGitignoreEntries(projectRoot) {
|
|
4485
|
-
const path =
|
|
4486
|
-
const existing =
|
|
4505
|
+
const path = resolve17(projectRoot, ".gitignore");
|
|
4506
|
+
const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
|
|
4487
4507
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
4488
4508
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4489
4509
|
if (missing.length === 0)
|
|
@@ -4496,14 +4516,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
4496
4516
|
`, "utf8");
|
|
4497
4517
|
}
|
|
4498
4518
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
4499
|
-
const path =
|
|
4500
|
-
const existing =
|
|
4519
|
+
const path = resolve17(projectRoot, "package.json");
|
|
4520
|
+
const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
|
|
4501
4521
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
4502
4522
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
4503
4523
|
devDependencies[name] = spec;
|
|
4504
4524
|
}
|
|
4505
4525
|
const next = {
|
|
4506
|
-
...
|
|
4526
|
+
...existsSync10(path) ? existing : { name: "rig-project", private: true },
|
|
4507
4527
|
devDependencies
|
|
4508
4528
|
};
|
|
4509
4529
|
writeFileSync5(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -4573,15 +4593,54 @@ async function promptSelect(prompts, options) {
|
|
|
4573
4593
|
throw new CliError2("Init cancelled.", 1);
|
|
4574
4594
|
return String(value);
|
|
4575
4595
|
}
|
|
4576
|
-
|
|
4596
|
+
function sleep2(ms) {
|
|
4597
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
4598
|
+
}
|
|
4599
|
+
function positiveIntFromEnv(name, fallback) {
|
|
4600
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
4601
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
4602
|
+
}
|
|
4603
|
+
function apiSessionTokenFrom(payload) {
|
|
4604
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4605
|
+
return null;
|
|
4606
|
+
const token = payload.apiSessionToken;
|
|
4607
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
4608
|
+
}
|
|
4609
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
4610
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
4611
|
+
authenticated: true,
|
|
4612
|
+
source: input.source,
|
|
4613
|
+
storedOnServer: true,
|
|
4614
|
+
selectedRepo: input.selectedRepo,
|
|
4615
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
4616
|
+
updatedAt: new Date().toISOString()
|
|
4617
|
+
}, null, 2)}
|
|
4618
|
+
`, "utf8");
|
|
4619
|
+
}
|
|
4620
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
4577
4621
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
4578
4622
|
return null;
|
|
4579
|
-
const
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4623
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
4624
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
4625
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
4626
|
+
const deadline = Date.now() + timeoutMs;
|
|
4627
|
+
let last = null;
|
|
4628
|
+
do {
|
|
4629
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
4630
|
+
method: "POST",
|
|
4631
|
+
headers: { "content-type": "application/json" },
|
|
4632
|
+
body: JSON.stringify({ pollId })
|
|
4633
|
+
}).catch(() => null);
|
|
4634
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4635
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
4636
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
4637
|
+
return last;
|
|
4638
|
+
}
|
|
4639
|
+
if (timeoutMs <= 0)
|
|
4640
|
+
return last;
|
|
4641
|
+
await sleep2(intervalMs);
|
|
4642
|
+
} while (Date.now() < deadline);
|
|
4643
|
+
return last;
|
|
4585
4644
|
}
|
|
4586
4645
|
async function runControlPlaneInit(context, options) {
|
|
4587
4646
|
const projectRoot = context.projectRoot;
|
|
@@ -4604,9 +4663,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
4604
4663
|
});
|
|
4605
4664
|
ensureRigPrivateDirs(projectRoot);
|
|
4606
4665
|
ensureGitignoreEntries(projectRoot);
|
|
4607
|
-
const configTsPath =
|
|
4608
|
-
const configJsonPath =
|
|
4609
|
-
const configExists =
|
|
4666
|
+
const configTsPath = resolve17(projectRoot, "rig.config.ts");
|
|
4667
|
+
const configJsonPath = resolve17(projectRoot, "rig.config.json");
|
|
4668
|
+
const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
|
|
4610
4669
|
if (!options.privateStateOnly) {
|
|
4611
4670
|
if (configExists && !options.repair) {
|
|
4612
4671
|
if (context.outputMode !== "json")
|
|
@@ -4622,7 +4681,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
4622
4681
|
}
|
|
4623
4682
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
4624
4683
|
}
|
|
4625
|
-
writeFileSync5(
|
|
4684
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
4626
4685
|
`, "utf8");
|
|
4627
4686
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
4628
4687
|
let uploadedSnapshot = null;
|
|
@@ -4644,10 +4703,14 @@ async function runControlPlaneInit(context, options) {
|
|
|
4644
4703
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
4645
4704
|
if (token) {
|
|
4646
4705
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
4647
|
-
|
|
4706
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4707
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4648
4708
|
if (serverKind === "remote") {
|
|
4649
|
-
|
|
4650
|
-
|
|
4709
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
4710
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
4711
|
+
selectedRepo: repo.slug,
|
|
4712
|
+
apiSessionToken
|
|
4713
|
+
});
|
|
4651
4714
|
}
|
|
4652
4715
|
} else if (authMethod === "device") {
|
|
4653
4716
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -4656,9 +4719,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
4656
4719
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
4657
4720
|
});
|
|
4658
4721
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4659
|
-
|
|
4660
|
-
|
|
4722
|
+
if (context.outputMode !== "json") {
|
|
4723
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
4724
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
4725
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
4726
|
+
}
|
|
4727
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
4728
|
+
if (completed) {
|
|
4729
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
4730
|
+
if (apiSessionToken) {
|
|
4731
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
4732
|
+
if (serverKind === "remote") {
|
|
4733
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4661
4736
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
4737
|
+
}
|
|
4662
4738
|
}
|
|
4663
4739
|
let remoteCheckoutPreparation = null;
|
|
4664
4740
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -4678,6 +4754,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
4678
4754
|
});
|
|
4679
4755
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
4680
4756
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
4757
|
+
if (serverRootSwitch && token) {
|
|
4758
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
4759
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4760
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4761
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
|
|
4762
|
+
}
|
|
4681
4763
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
4682
4764
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
4683
4765
|
remote: true,
|
|
@@ -4787,7 +4869,7 @@ function parseInitOptions(args) {
|
|
|
4787
4869
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
4788
4870
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
4789
4871
|
const projectRoot = context.projectRoot;
|
|
4790
|
-
const existingConfig =
|
|
4872
|
+
const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
|
|
4791
4873
|
let repair = false;
|
|
4792
4874
|
let privateStateOnly = false;
|
|
4793
4875
|
if (existingConfig) {
|
|
@@ -4887,7 +4969,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
4887
4969
|
});
|
|
4888
4970
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
4889
4971
|
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")}.` : "";
|
|
4972
|
+
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
4973
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
4892
4974
|
return result;
|
|
4893
4975
|
}
|
|
@@ -5049,8 +5131,8 @@ async function executeDoctor(context, args) {
|
|
|
5049
5131
|
}
|
|
5050
5132
|
|
|
5051
5133
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
5052
|
-
import { readFileSync as
|
|
5053
|
-
import { resolve as
|
|
5134
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
5135
|
+
import { resolve as resolve18 } from "path";
|
|
5054
5136
|
import {
|
|
5055
5137
|
appendJsonlRecord as appendJsonlRecord2,
|
|
5056
5138
|
readAuthorityRun as readAuthorityRun2,
|
|
@@ -5070,7 +5152,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
|
|
|
5070
5152
|
...patch,
|
|
5071
5153
|
updatedAt: new Date().toISOString()
|
|
5072
5154
|
};
|
|
5073
|
-
writeJsonFile4(
|
|
5155
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
|
|
5074
5156
|
return next;
|
|
5075
5157
|
}
|
|
5076
5158
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -5078,21 +5160,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
5078
5160
|
if (!current) {
|
|
5079
5161
|
return;
|
|
5080
5162
|
}
|
|
5081
|
-
writeJsonFile4(
|
|
5163
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
|
|
5082
5164
|
...current,
|
|
5083
5165
|
updatedAt: new Date().toISOString()
|
|
5084
5166
|
});
|
|
5085
5167
|
}
|
|
5086
5168
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
5087
|
-
appendJsonlRecord2(
|
|
5169
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
|
|
5088
5170
|
touchAuthorityRun(projectRoot, runId);
|
|
5089
5171
|
}
|
|
5090
5172
|
function appendRunLog(projectRoot, runId, value) {
|
|
5091
|
-
appendJsonlRecord2(
|
|
5173
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
|
|
5092
5174
|
touchAuthorityRun(projectRoot, runId);
|
|
5093
5175
|
}
|
|
5094
5176
|
function appendRunAction(projectRoot, runId, value) {
|
|
5095
|
-
appendJsonlRecord2(
|
|
5177
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
|
|
5096
5178
|
id: value.id,
|
|
5097
5179
|
type: "action",
|
|
5098
5180
|
actionType: value.actionType,
|
|
@@ -5163,7 +5245,7 @@ function buildRunPrompt(input) {
|
|
|
5163
5245
|
})();
|
|
5164
5246
|
const scopeText = (() => {
|
|
5165
5247
|
try {
|
|
5166
|
-
const parsed = JSON.parse(
|
|
5248
|
+
const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
|
|
5167
5249
|
const entry = parsed[input.taskId] ?? {};
|
|
5168
5250
|
const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
|
|
5169
5251
|
const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
|
|
@@ -5276,8 +5358,8 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
5276
5358
|
}
|
|
5277
5359
|
|
|
5278
5360
|
// packages/cli/src/commands/inspect.ts
|
|
5279
|
-
import { existsSync as
|
|
5280
|
-
import { resolve as
|
|
5361
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
5362
|
+
import { resolve as resolve19 } from "path";
|
|
5281
5363
|
import {
|
|
5282
5364
|
listAuthorityRuns as listAuthorityRuns2,
|
|
5283
5365
|
readAuthorityRun as readAuthorityRun3,
|
|
@@ -5298,8 +5380,8 @@ async function executeInspect(context, args) {
|
|
|
5298
5380
|
if (!latestRun) {
|
|
5299
5381
|
throw new CliError2(`No runs found for ${requiredTask}.`);
|
|
5300
5382
|
}
|
|
5301
|
-
const logsPath =
|
|
5302
|
-
if (!
|
|
5383
|
+
const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
5384
|
+
if (!existsSync11(logsPath)) {
|
|
5303
5385
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
5304
5386
|
}
|
|
5305
5387
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -5309,7 +5391,7 @@ async function executeInspect(context, args) {
|
|
|
5309
5391
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
5310
5392
|
requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
|
|
5311
5393
|
const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
|
|
5312
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
5394
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
|
|
5313
5395
|
if (!artifactRoot) {
|
|
5314
5396
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
5315
5397
|
}
|
|
@@ -5366,10 +5448,10 @@ async function executeInspect(context, args) {
|
|
|
5366
5448
|
case "failures": {
|
|
5367
5449
|
requireNoExtraArgs(rest, "bun run rig inspect failures");
|
|
5368
5450
|
const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
|
|
5369
|
-
if (!
|
|
5451
|
+
if (!existsSync11(failed)) {
|
|
5370
5452
|
console.log("No failures recorded.");
|
|
5371
5453
|
} else {
|
|
5372
|
-
process.stdout.write(
|
|
5454
|
+
process.stdout.write(readFileSync8(failed, "utf-8"));
|
|
5373
5455
|
}
|
|
5374
5456
|
return { ok: true, group: "inspect", command };
|
|
5375
5457
|
}
|
|
@@ -5386,11 +5468,11 @@ async function executeInspect(context, args) {
|
|
|
5386
5468
|
return { ok: true, group: "inspect", command };
|
|
5387
5469
|
case "audit": {
|
|
5388
5470
|
requireNoExtraArgs(rest, "bun run rig inspect audit");
|
|
5389
|
-
const auditPath =
|
|
5390
|
-
if (!
|
|
5471
|
+
const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
|
|
5472
|
+
if (!existsSync11(auditPath)) {
|
|
5391
5473
|
console.log("No audit log found.");
|
|
5392
5474
|
} else {
|
|
5393
|
-
const lines =
|
|
5475
|
+
const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
5394
5476
|
for (const line of lines) {
|
|
5395
5477
|
console.log(line);
|
|
5396
5478
|
}
|
|
@@ -6019,8 +6101,8 @@ async function executeRemote(context, args) {
|
|
|
6019
6101
|
}
|
|
6020
6102
|
|
|
6021
6103
|
// packages/cli/src/commands/run.ts
|
|
6022
|
-
import { existsSync as
|
|
6023
|
-
import { resolve as
|
|
6104
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
6105
|
+
import { resolve as resolve20 } from "path";
|
|
6024
6106
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6025
6107
|
import {
|
|
6026
6108
|
listAuthorityRuns as listAuthorityRuns3,
|
|
@@ -6034,6 +6116,7 @@ import {
|
|
|
6034
6116
|
listOpenEpics,
|
|
6035
6117
|
resolveDefaultEpic,
|
|
6036
6118
|
runResume,
|
|
6119
|
+
runRestart,
|
|
6037
6120
|
runStatus,
|
|
6038
6121
|
runStop,
|
|
6039
6122
|
startRun,
|
|
@@ -6142,6 +6225,17 @@ async function attachRunOperatorView(context, input) {
|
|
|
6142
6225
|
}
|
|
6143
6226
|
|
|
6144
6227
|
// packages/cli/src/commands/run.ts
|
|
6228
|
+
function normalizeRemoteRunDetails(payload) {
|
|
6229
|
+
const run = payload.run;
|
|
6230
|
+
if (!run || typeof run !== "object" || Array.isArray(run))
|
|
6231
|
+
return null;
|
|
6232
|
+
return {
|
|
6233
|
+
...run,
|
|
6234
|
+
...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
|
|
6235
|
+
...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
|
|
6236
|
+
...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
|
|
6237
|
+
};
|
|
6238
|
+
}
|
|
6145
6239
|
function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
|
|
6146
6240
|
if (noEpicPrompt) {
|
|
6147
6241
|
return false;
|
|
@@ -6280,7 +6374,7 @@ async function executeRun(context, args) {
|
|
|
6280
6374
|
if (!run.value) {
|
|
6281
6375
|
throw new CliError2("run show requires --run <id>.");
|
|
6282
6376
|
}
|
|
6283
|
-
const record = readAuthorityRun4(context.projectRoot, run.value);
|
|
6377
|
+
const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
|
|
6284
6378
|
if (!record) {
|
|
6285
6379
|
throw new CliError2(`Run not found: ${run.value}`, 2);
|
|
6286
6380
|
}
|
|
@@ -6299,7 +6393,7 @@ async function executeRun(context, args) {
|
|
|
6299
6393
|
if (!run.value) {
|
|
6300
6394
|
throw new CliError2("run timeline requires --run <id>.");
|
|
6301
6395
|
}
|
|
6302
|
-
const timelinePath =
|
|
6396
|
+
const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
|
|
6303
6397
|
const printEvents = () => {
|
|
6304
6398
|
const events2 = readJsonlFile4(timelinePath);
|
|
6305
6399
|
if (context.outputMode === "text") {
|
|
@@ -6311,12 +6405,12 @@ async function executeRun(context, args) {
|
|
|
6311
6405
|
};
|
|
6312
6406
|
const events = printEvents();
|
|
6313
6407
|
if (follow.value && context.outputMode === "text") {
|
|
6314
|
-
let lastLength =
|
|
6408
|
+
let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
|
|
6315
6409
|
while (true) {
|
|
6316
6410
|
await Bun.sleep(1000);
|
|
6317
|
-
if (!
|
|
6411
|
+
if (!existsSync12(timelinePath))
|
|
6318
6412
|
continue;
|
|
6319
|
-
const next =
|
|
6413
|
+
const next = readFileSync9(timelinePath, "utf8");
|
|
6320
6414
|
if (next.length <= lastLength)
|
|
6321
6415
|
continue;
|
|
6322
6416
|
const delta = next.slice(lastLength);
|
|
@@ -6461,6 +6555,20 @@ async function executeRun(context, args) {
|
|
|
6461
6555
|
}
|
|
6462
6556
|
return { ok: true, group: "run", command, details: resumed };
|
|
6463
6557
|
}
|
|
6558
|
+
case "restart": {
|
|
6559
|
+
requireNoExtraArgs(rest, "bun run rig run restart");
|
|
6560
|
+
if (context.dryRun) {
|
|
6561
|
+
if (context.outputMode === "text") {
|
|
6562
|
+
console.log("[dry-run] rig run restart");
|
|
6563
|
+
}
|
|
6564
|
+
return { ok: true, group: "run", command };
|
|
6565
|
+
}
|
|
6566
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
6567
|
+
if (context.outputMode === "text") {
|
|
6568
|
+
console.log(`Restarted run: ${restarted.runId}`);
|
|
6569
|
+
}
|
|
6570
|
+
return { ok: true, group: "run", command, details: restarted };
|
|
6571
|
+
}
|
|
6464
6572
|
case "stop": {
|
|
6465
6573
|
const runOption = takeOption(rest, "--run");
|
|
6466
6574
|
const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
|
|
@@ -6518,7 +6626,7 @@ async function executeServer(context, args, options) {
|
|
|
6518
6626
|
const authTokenResult = takeOption(pending, "--auth-token");
|
|
6519
6627
|
pending = authTokenResult.rest;
|
|
6520
6628
|
requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
|
|
6521
|
-
const commandParts = ["
|
|
6629
|
+
const commandParts = ["rig-server", "start"];
|
|
6522
6630
|
if (hostResult.value) {
|
|
6523
6631
|
commandParts.push("--host", hostResult.value);
|
|
6524
6632
|
}
|
|
@@ -6541,7 +6649,7 @@ async function executeServer(context, args, options) {
|
|
|
6541
6649
|
const eventResult = takeOption(pending, "--event");
|
|
6542
6650
|
pending = eventResult.rest;
|
|
6543
6651
|
requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
|
|
6544
|
-
const commandParts = ["
|
|
6652
|
+
const commandParts = ["rig-server", "notify-test"];
|
|
6545
6653
|
if (eventResult.value) {
|
|
6546
6654
|
commandParts.push("--event", eventResult.value);
|
|
6547
6655
|
}
|
|
@@ -6602,10 +6710,10 @@ async function executeServer(context, args, options) {
|
|
|
6602
6710
|
}
|
|
6603
6711
|
|
|
6604
6712
|
// packages/cli/src/commands/task.ts
|
|
6605
|
-
import { readFileSync as
|
|
6713
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6606
6714
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6607
6715
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
6608
|
-
import { resolve as
|
|
6716
|
+
import { resolve as resolve21 } from "path";
|
|
6609
6717
|
import {
|
|
6610
6718
|
taskArtifactDir,
|
|
6611
6719
|
taskArtifacts,
|
|
@@ -6936,7 +7044,7 @@ async function executeTask(context, args, options) {
|
|
|
6936
7044
|
const fileFlag = takeOption(rest.slice(1), "--file");
|
|
6937
7045
|
let content;
|
|
6938
7046
|
if (fileFlag.value) {
|
|
6939
|
-
content =
|
|
7047
|
+
content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
|
|
6940
7048
|
} else {
|
|
6941
7049
|
content = await readStdin();
|
|
6942
7050
|
}
|
|
@@ -7157,8 +7265,8 @@ async function executeTask(context, args, options) {
|
|
|
7157
7265
|
}
|
|
7158
7266
|
|
|
7159
7267
|
// packages/cli/src/commands/task-run-driver.ts
|
|
7160
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
7161
|
-
import { resolve as
|
|
7268
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7269
|
+
import { resolve as resolve22 } from "path";
|
|
7162
7270
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
7163
7271
|
import { createInterface as createLineInterface } from "readline";
|
|
7164
7272
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
@@ -7192,7 +7300,24 @@ import {
|
|
|
7192
7300
|
commitRunChanges,
|
|
7193
7301
|
runPrAutomation
|
|
7194
7302
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
7303
|
+
function looksLikeGitHubToken(value) {
|
|
7304
|
+
const token = value?.trim();
|
|
7305
|
+
if (!token)
|
|
7306
|
+
return false;
|
|
7307
|
+
return /^(gh[opusr]_|github_pat_)/.test(token);
|
|
7308
|
+
}
|
|
7309
|
+
function githubBridgeEnv(token) {
|
|
7310
|
+
const clean = token?.trim();
|
|
7311
|
+
if (!clean)
|
|
7312
|
+
return {};
|
|
7313
|
+
return {
|
|
7314
|
+
RIG_GITHUB_TOKEN: clean,
|
|
7315
|
+
GITHUB_TOKEN: clean,
|
|
7316
|
+
GH_TOKEN: clean
|
|
7317
|
+
};
|
|
7318
|
+
}
|
|
7195
7319
|
function buildPiRigBridgeEnv(input) {
|
|
7320
|
+
const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
|
|
7196
7321
|
return {
|
|
7197
7322
|
RIG_PROJECT_ROOT: input.projectRoot,
|
|
7198
7323
|
PROJECT_RIG_ROOT: input.projectRoot,
|
|
@@ -7202,7 +7327,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
7202
7327
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
7203
7328
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
7204
7329
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
7205
|
-
...
|
|
7330
|
+
...githubBridgeEnv(githubToken)
|
|
7206
7331
|
};
|
|
7207
7332
|
}
|
|
7208
7333
|
function runGitSync(cwd, args, input) {
|
|
@@ -7224,12 +7349,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
7224
7349
|
return 0;
|
|
7225
7350
|
let copied = 0;
|
|
7226
7351
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
7227
|
-
const sourcePath =
|
|
7228
|
-
const targetPath =
|
|
7352
|
+
const sourcePath = resolve22(sourceRoot, relativePath);
|
|
7353
|
+
const targetPath = resolve22(targetRoot, relativePath);
|
|
7229
7354
|
try {
|
|
7230
7355
|
if (!statSync2(sourcePath).isFile())
|
|
7231
7356
|
continue;
|
|
7232
|
-
mkdirSync8(
|
|
7357
|
+
mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
|
|
7233
7358
|
copyFileSync3(sourcePath, targetPath);
|
|
7234
7359
|
copied += 1;
|
|
7235
7360
|
} catch {}
|
|
@@ -7263,6 +7388,14 @@ function buildTaskRunReviewEnv(config) {
|
|
|
7263
7388
|
...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
|
|
7264
7389
|
};
|
|
7265
7390
|
}
|
|
7391
|
+
function buildDirtyBaselineHandshakeEnv(input) {
|
|
7392
|
+
if (input.baselineMode !== "dirty-snapshot")
|
|
7393
|
+
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
7394
|
+
return {
|
|
7395
|
+
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
7396
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
7397
|
+
};
|
|
7398
|
+
}
|
|
7266
7399
|
function positiveInt(value, fallback) {
|
|
7267
7400
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
7268
7401
|
}
|
|
@@ -7355,6 +7488,12 @@ function appendPiStageLog(input) {
|
|
|
7355
7488
|
});
|
|
7356
7489
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
7357
7490
|
}
|
|
7491
|
+
async function runCheckedCommand(command, args, cwd, label = "git") {
|
|
7492
|
+
const result = await command(args, { cwd });
|
|
7493
|
+
if (result.exitCode !== 0) {
|
|
7494
|
+
throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7358
7497
|
function createCommandRunner(binary) {
|
|
7359
7498
|
return async (args, options) => {
|
|
7360
7499
|
const child = spawn2(binary, [...args], {
|
|
@@ -7365,9 +7504,9 @@ function createCommandRunner(binary) {
|
|
|
7365
7504
|
const stderrChunks = [];
|
|
7366
7505
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7367
7506
|
child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7368
|
-
return await new Promise((
|
|
7369
|
-
child.once("error", (error) =>
|
|
7370
|
-
child.once("close", (code) =>
|
|
7507
|
+
return await new Promise((resolve23) => {
|
|
7508
|
+
child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
|
|
7509
|
+
child.once("close", (code) => resolve23({
|
|
7371
7510
|
exitCode: code ?? 1,
|
|
7372
7511
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
7373
7512
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -7430,6 +7569,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
7430
7569
|
command: gitCommand
|
|
7431
7570
|
});
|
|
7432
7571
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
7572
|
+
await runCheckedCommand(gitCommand, ["push", "--set-upstream", "origin", branch], workspace, "git");
|
|
7433
7573
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
7434
7574
|
const pr = await prAutomation({
|
|
7435
7575
|
projectRoot: workspace,
|
|
@@ -7440,7 +7580,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
7440
7580
|
sourceTask: input.sourceTask,
|
|
7441
7581
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
7442
7582
|
command: ghCommand,
|
|
7443
|
-
gitCommand,
|
|
7444
7583
|
steerPi,
|
|
7445
7584
|
lifecycle: {
|
|
7446
7585
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -7563,7 +7702,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
|
|
|
7563
7702
|
return null;
|
|
7564
7703
|
}
|
|
7565
7704
|
for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
|
|
7566
|
-
const summary = readJsonFile3(
|
|
7705
|
+
const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
|
|
7567
7706
|
if (!summary || summary.status !== "fail") {
|
|
7568
7707
|
continue;
|
|
7569
7708
|
}
|
|
@@ -7644,9 +7783,9 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
7644
7783
|
if (!input.taskId || !input.workspaceDir) {
|
|
7645
7784
|
return { accepted: false, reason: null };
|
|
7646
7785
|
}
|
|
7647
|
-
const artifactDir =
|
|
7648
|
-
const reviewStatusPath =
|
|
7649
|
-
const taskResultPath =
|
|
7786
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7787
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7788
|
+
const taskResultPath = resolve22(artifactDir, "task-result.json");
|
|
7650
7789
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7651
7790
|
if (reviewStatus !== "APPROVED") {
|
|
7652
7791
|
return { accepted: false, reason: null };
|
|
@@ -7683,12 +7822,12 @@ function resolveTaskRunRetryContext(input) {
|
|
|
7683
7822
|
if (!input.taskId || !input.workspaceDir) {
|
|
7684
7823
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
7685
7824
|
}
|
|
7686
|
-
const artifactDir =
|
|
7687
|
-
const reviewStatePath =
|
|
7688
|
-
const reviewFeedbackPath =
|
|
7689
|
-
const reviewStatusPath =
|
|
7690
|
-
const failedApproachesPath =
|
|
7691
|
-
const validationSummaryPath =
|
|
7825
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7826
|
+
const reviewStatePath = resolve22(artifactDir, "review-state.json");
|
|
7827
|
+
const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
|
|
7828
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7829
|
+
const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
7830
|
+
const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
|
|
7692
7831
|
const reviewState = readJsonFile3(reviewStatePath, null);
|
|
7693
7832
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7694
7833
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
@@ -7743,11 +7882,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
7743
7882
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
7744
7883
|
}
|
|
7745
7884
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
7746
|
-
if (!
|
|
7885
|
+
if (!existsSync13(reviewStatusPath)) {
|
|
7747
7886
|
return null;
|
|
7748
7887
|
}
|
|
7749
7888
|
try {
|
|
7750
|
-
const status =
|
|
7889
|
+
const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
7751
7890
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
7752
7891
|
} catch {
|
|
7753
7892
|
return null;
|
|
@@ -7830,8 +7969,11 @@ function stringArrayField(record, key) {
|
|
|
7830
7969
|
}
|
|
7831
7970
|
async function executeRigOwnedTaskRun(context, input) {
|
|
7832
7971
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
7972
|
+
const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
|
|
7973
|
+
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
7974
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
7833
7975
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
7834
|
-
|
|
7976
|
+
let prompt = buildRunPrompt({
|
|
7835
7977
|
projectRoot: context.projectRoot,
|
|
7836
7978
|
taskId: input.taskId,
|
|
7837
7979
|
fallbackTitle: input.title,
|
|
@@ -7885,14 +8027,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7885
8027
|
taskId: runtimeTaskId,
|
|
7886
8028
|
createdAt: startedAt,
|
|
7887
8029
|
runtimeAdapter: input.runtimeAdapter,
|
|
7888
|
-
status: "created"
|
|
8030
|
+
status: resumeMode ? "preparing" : "created"
|
|
7889
8031
|
});
|
|
7890
8032
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7891
8033
|
status: "preparing",
|
|
7892
8034
|
startedAt,
|
|
7893
8035
|
completedAt: null,
|
|
7894
8036
|
errorText: null,
|
|
7895
|
-
artifactRoot: null,
|
|
8037
|
+
artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
|
|
7896
8038
|
runtimeAdapter: input.runtimeAdapter,
|
|
7897
8039
|
runtimeMode: input.runtimeMode,
|
|
7898
8040
|
interactionMode: input.interactionMode,
|
|
@@ -7908,9 +8050,9 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7908
8050
|
detail: input.taskId ?? input.title ?? runtimeTaskId
|
|
7909
8051
|
});
|
|
7910
8052
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7911
|
-
id: `log:${input.runId}:start`,
|
|
7912
|
-
title: "Rig task run started",
|
|
7913
|
-
detail: input.taskId ?? input.title ?? runtimeTaskId,
|
|
8053
|
+
id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
|
|
8054
|
+
title: resumeMode ? "Rig task run resumed" : "Rig task run started",
|
|
8055
|
+
detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
|
|
7914
8056
|
tone: "info",
|
|
7915
8057
|
status: "preparing",
|
|
7916
8058
|
createdAt: startedAt
|
|
@@ -7931,7 +8073,22 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7931
8073
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
7932
8074
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
7933
8075
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
7934
|
-
|
|
8076
|
+
const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
8077
|
+
const persistedPlanning = {
|
|
8078
|
+
...planningClassification,
|
|
8079
|
+
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
8080
|
+
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
8081
|
+
classifiedAt: new Date().toISOString()
|
|
8082
|
+
};
|
|
8083
|
+
mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
8084
|
+
writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
8085
|
+
`, "utf8");
|
|
8086
|
+
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
8087
|
+
prompt = `${prompt}
|
|
8088
|
+
|
|
8089
|
+
Rig planning classification:
|
|
8090
|
+
${JSON.stringify(persistedPlanning, null, 2)}
|
|
8091
|
+
${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."}`;
|
|
7935
8092
|
if (input.runtimeAdapter === "pi") {
|
|
7936
8093
|
for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
|
|
7937
8094
|
appendPiStageLog({
|
|
@@ -7973,11 +8130,11 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7973
8130
|
let reviewAction;
|
|
7974
8131
|
let verificationStarted = false;
|
|
7975
8132
|
let reviewStarted = false;
|
|
7976
|
-
let latestRuntimeWorkspace = null;
|
|
7977
|
-
let latestSessionDir = null;
|
|
7978
|
-
let latestLogsDir = null;
|
|
8133
|
+
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
8134
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve22(existingRunRecord.sessionPath, "..") : null;
|
|
8135
|
+
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
7979
8136
|
let latestProviderCommand = null;
|
|
7980
|
-
let latestRuntimeBranch = null;
|
|
8137
|
+
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
7981
8138
|
let snapshotSidecarPromise = null;
|
|
7982
8139
|
let dirtyBaselineApplied = false;
|
|
7983
8140
|
const childEnv = {
|
|
@@ -7995,9 +8152,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7995
8152
|
RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
|
|
7996
8153
|
RIG_SERVER_RUN_ID: input.runId
|
|
7997
8154
|
},
|
|
7998
|
-
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
8155
|
+
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
|
|
8156
|
+
...resumeMode ? {
|
|
8157
|
+
RIG_RUN_RESUME: "1",
|
|
8158
|
+
RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
|
|
8159
|
+
} : {}
|
|
7999
8160
|
};
|
|
8000
8161
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
8162
|
+
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
8001
8163
|
const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
|
|
8002
8164
|
const maxAttempts = automationLimits.maxValidationAttempts;
|
|
8003
8165
|
const promoteToValidating = (detail) => {
|
|
@@ -8052,22 +8214,29 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8052
8214
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8053
8215
|
status: "running",
|
|
8054
8216
|
worktreePath: latestRuntimeWorkspace,
|
|
8055
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
8217
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
8056
8218
|
logRoot: latestLogsDir,
|
|
8057
|
-
sessionPath: latestSessionDir ?
|
|
8058
|
-
sessionLogPath: latestLogsDir ?
|
|
8219
|
+
sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
|
|
8220
|
+
sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
|
|
8059
8221
|
branch: runtimeId
|
|
8060
8222
|
});
|
|
8061
8223
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
8062
8224
|
dirtyBaselineApplied = true;
|
|
8063
8225
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
8226
|
+
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
8227
|
+
if (readyFile) {
|
|
8228
|
+
mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
|
|
8229
|
+
writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
8230
|
+
`, "utf8");
|
|
8231
|
+
}
|
|
8064
8232
|
appendRunLog(context.projectRoot, input.runId, {
|
|
8065
8233
|
id: `log:${input.runId}:dirty-baseline`,
|
|
8066
8234
|
title: "Dirty baseline snapshot",
|
|
8067
8235
|
detail: dirty.detail,
|
|
8068
8236
|
tone: dirty.applied ? "tool" : "info",
|
|
8069
8237
|
status: dirty.applied ? "completed" : "skipped",
|
|
8070
|
-
createdAt: new Date().toISOString()
|
|
8238
|
+
createdAt: new Date().toISOString(),
|
|
8239
|
+
payload: readyFile ? { readyFile } : undefined
|
|
8071
8240
|
});
|
|
8072
8241
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
8073
8242
|
}
|
|
@@ -8293,7 +8462,36 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8293
8462
|
let reviewFailureDetail = null;
|
|
8294
8463
|
const stderrLines = [];
|
|
8295
8464
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
8296
|
-
|
|
8465
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
8466
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8467
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
8468
|
+
title: "Resume continuing from closeout phase",
|
|
8469
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
8470
|
+
tone: "info",
|
|
8471
|
+
status: resumePreviousStatus,
|
|
8472
|
+
createdAt: new Date().toISOString()
|
|
8473
|
+
});
|
|
8474
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
8475
|
+
exit = { code: 0, signal: null };
|
|
8476
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
8477
|
+
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
8478
|
+
taskId: input.taskId ?? runtimeTaskId,
|
|
8479
|
+
workspaceDir: latestRuntimeWorkspace
|
|
8480
|
+
});
|
|
8481
|
+
if (acceptedArtifactState.accepted) {
|
|
8482
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8483
|
+
id: `log:${input.runId}:resume-accepted-artifacts`,
|
|
8484
|
+
title: "Resume found accepted artifacts; continuing closeout",
|
|
8485
|
+
detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
|
|
8486
|
+
tone: "info",
|
|
8487
|
+
status: "validating",
|
|
8488
|
+
createdAt: new Date().toISOString()
|
|
8489
|
+
});
|
|
8490
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
|
|
8491
|
+
exit = { code: 0, signal: null };
|
|
8492
|
+
}
|
|
8493
|
+
}
|
|
8494
|
+
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
8297
8495
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
8298
8496
|
const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
8299
8497
|
cwd: context.projectRoot,
|
|
@@ -8334,7 +8532,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8334
8532
|
let acceptedArtifactObservedAt = null;
|
|
8335
8533
|
let acceptedArtifactPollTimer = null;
|
|
8336
8534
|
let acceptedArtifactKillTimer = null;
|
|
8337
|
-
const attemptExit = await new Promise((
|
|
8535
|
+
const attemptExit = await new Promise((resolve23) => {
|
|
8338
8536
|
let settled = false;
|
|
8339
8537
|
const settle = (result) => {
|
|
8340
8538
|
if (settled)
|
|
@@ -8342,7 +8540,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8342
8540
|
settled = true;
|
|
8343
8541
|
if (acceptedArtifactPollTimer)
|
|
8344
8542
|
clearInterval(acceptedArtifactPollTimer);
|
|
8345
|
-
|
|
8543
|
+
resolve23(result);
|
|
8346
8544
|
};
|
|
8347
8545
|
const pollAcceptedArtifacts = () => {
|
|
8348
8546
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -8539,6 +8737,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8539
8737
|
});
|
|
8540
8738
|
throw new CliError2(terminalFailureDetail, exit.code ?? 1);
|
|
8541
8739
|
}
|
|
8740
|
+
if (planningClassification.planningRequired) {
|
|
8741
|
+
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
8742
|
+
const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
|
|
8743
|
+
if (!existsSync13(expectedPlanPath)) {
|
|
8744
|
+
const failedAt = new Date().toISOString();
|
|
8745
|
+
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
8746
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8747
|
+
status: "needs_attention",
|
|
8748
|
+
completedAt: failedAt,
|
|
8749
|
+
errorText: failureDetail
|
|
8750
|
+
});
|
|
8751
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8752
|
+
id: `log:${input.runId}:plan-artifact-missing`,
|
|
8753
|
+
title: "Required plan artifact missing",
|
|
8754
|
+
detail: failureDetail,
|
|
8755
|
+
tone: "error",
|
|
8756
|
+
status: "needs_attention",
|
|
8757
|
+
createdAt: failedAt
|
|
8758
|
+
});
|
|
8759
|
+
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
8760
|
+
throw new CliError2(failureDetail, 1);
|
|
8761
|
+
}
|
|
8762
|
+
}
|
|
8542
8763
|
const runPiPrFeedbackFix = async (message2) => {
|
|
8543
8764
|
appendPiStageLog({
|
|
8544
8765
|
projectRoot: context.projectRoot,
|
|
@@ -8596,9 +8817,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8596
8817
|
});
|
|
8597
8818
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
8598
8819
|
});
|
|
8599
|
-
const exitCode = await new Promise((
|
|
8600
|
-
child.once("error", () =>
|
|
8601
|
-
child.once("close", (code) =>
|
|
8820
|
+
const exitCode = await new Promise((resolve23) => {
|
|
8821
|
+
child.once("error", () => resolve23(1));
|
|
8822
|
+
child.once("close", (code) => resolve23(code ?? 1));
|
|
8602
8823
|
});
|
|
8603
8824
|
if (exitCode !== 0) {
|
|
8604
8825
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
@@ -8717,8 +8938,8 @@ async function executeTest(context, args) {
|
|
|
8717
8938
|
}
|
|
8718
8939
|
|
|
8719
8940
|
// packages/cli/src/commands/setup.ts
|
|
8720
|
-
import { existsSync as
|
|
8721
|
-
import { resolve as
|
|
8941
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
8942
|
+
import { resolve as resolve23 } from "path";
|
|
8722
8943
|
import { createPluginHost } from "@rig/core";
|
|
8723
8944
|
import {
|
|
8724
8945
|
isSupportedBunVersion as isSupportedBunVersion2,
|
|
@@ -8781,9 +9002,9 @@ function runSetupInit(projectRoot) {
|
|
|
8781
9002
|
mkdirSync9(stateDir, { recursive: true });
|
|
8782
9003
|
mkdirSync9(logsDir, { recursive: true });
|
|
8783
9004
|
mkdirSync9(artifactsDir, { recursive: true });
|
|
8784
|
-
const failuresPath =
|
|
8785
|
-
if (!
|
|
8786
|
-
|
|
9005
|
+
const failuresPath = resolve23(stateDir, "failed_approaches.md");
|
|
9006
|
+
if (!existsSync14(failuresPath)) {
|
|
9007
|
+
writeFileSync7(failuresPath, `# Failed Approaches
|
|
8787
9008
|
|
|
8788
9009
|
`, "utf-8");
|
|
8789
9010
|
}
|
|
@@ -8800,18 +9021,18 @@ async function runSetupCheck(projectRoot) {
|
|
|
8800
9021
|
}
|
|
8801
9022
|
async function runSetupPreflight(projectRoot) {
|
|
8802
9023
|
await runSetupCheck(projectRoot);
|
|
8803
|
-
const validationRoot =
|
|
8804
|
-
if (
|
|
9024
|
+
const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
9025
|
+
if (existsSync14(validationRoot)) {
|
|
8805
9026
|
const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
8806
9027
|
for (const validator of validators) {
|
|
8807
|
-
const script =
|
|
8808
|
-
if (
|
|
9028
|
+
const script = resolve23(validationRoot, validator.name, "validate.sh");
|
|
9029
|
+
if (existsSync14(script)) {
|
|
8809
9030
|
console.log(`OK: validator script ${script}`);
|
|
8810
9031
|
}
|
|
8811
9032
|
}
|
|
8812
9033
|
}
|
|
8813
|
-
const hooksRoot =
|
|
8814
|
-
if (
|
|
9034
|
+
const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
|
|
9035
|
+
if (existsSync14(hooksRoot)) {
|
|
8815
9036
|
const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
|
|
8816
9037
|
for (const hook of hooks) {
|
|
8817
9038
|
console.log(`OK: hook ${hook}`);
|
|
@@ -9205,8 +9426,8 @@ var __testOnly = {
|
|
|
9205
9426
|
validateRequiredBugPromptValue
|
|
9206
9427
|
};
|
|
9207
9428
|
// packages/cli/src/launcher.ts
|
|
9208
|
-
import { existsSync as
|
|
9209
|
-
import { resolve as
|
|
9429
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9430
|
+
import { basename as basename2, resolve as resolve24 } from "path";
|
|
9210
9431
|
import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
|
|
9211
9432
|
import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
|
|
9212
9433
|
function parsePolicyMode(value) {
|
|
@@ -9219,7 +9440,7 @@ function parsePolicyMode(value) {
|
|
|
9219
9440
|
throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
|
|
9220
9441
|
}
|
|
9221
9442
|
function hasRigProjectMarker(candidate) {
|
|
9222
|
-
return
|
|
9443
|
+
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"));
|
|
9223
9444
|
}
|
|
9224
9445
|
function resolveProjectRoot({
|
|
9225
9446
|
envProjectRoot,
|
|
@@ -9228,17 +9449,19 @@ function resolveProjectRoot({
|
|
|
9228
9449
|
cwd = process.cwd()
|
|
9229
9450
|
}) {
|
|
9230
9451
|
if (envProjectRoot) {
|
|
9231
|
-
return
|
|
9452
|
+
return resolve24(cwd, envProjectRoot);
|
|
9232
9453
|
}
|
|
9233
9454
|
const fallbackImportDir = importDir ?? cwd;
|
|
9234
|
-
const
|
|
9455
|
+
const execName = basename2(execPath).toLowerCase();
|
|
9456
|
+
const execCandidates = execName === "rig" || execName === "rig.exe" ? [resolve24(execPath, "..", "..")] : [];
|
|
9457
|
+
const candidates = [cwd, ...execCandidates, resolve24(fallbackImportDir, "..")];
|
|
9235
9458
|
for (const candidate of candidates) {
|
|
9236
9459
|
const nearest = resolveNearestRigProjectRoot(candidate);
|
|
9237
9460
|
if (hasRigProjectMarker(nearest)) {
|
|
9238
9461
|
return nearest;
|
|
9239
9462
|
}
|
|
9240
9463
|
}
|
|
9241
|
-
return
|
|
9464
|
+
return resolve24(cwd);
|
|
9242
9465
|
}
|
|
9243
9466
|
function normalizeCliErrorCode(message2, isCliError) {
|
|
9244
9467
|
if (message2.startsWith("Invalid --policy-mode value:")) {
|
|
@@ -9305,7 +9528,7 @@ async function runRigCli(module, options = {}) {
|
|
|
9305
9528
|
runId: context.runId,
|
|
9306
9529
|
outcome,
|
|
9307
9530
|
eventsFile: context.eventBus.getEventsFile(),
|
|
9308
|
-
policyFile:
|
|
9531
|
+
policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
|
|
9309
9532
|
policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
|
|
9310
9533
|
}, null, 2));
|
|
9311
9534
|
}
|