@h-rig/cli 0.0.6-alpha.1 → 0.0.6-alpha.2
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 +338 -204
- 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/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 +26 -8
- package/dist/src/commands/server.js +20 -2
- package/dist/src/commands/setup.js +36 -18
- package/dist/src/commands/task-run-driver.js +59 -4
- package/dist/src/commands/task.js +28 -10
- package/dist/src/commands.js +331 -197
- package/dist/src/index.js +338 -204
- package/package.json +4 -4
package/dist/bin/rig.js
CHANGED
|
@@ -2671,6 +2671,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
2671
2671
|
|
|
2672
2672
|
// packages/cli/src/commands/_server-client.ts
|
|
2673
2673
|
import { spawnSync } from "child_process";
|
|
2674
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
2675
|
+
import { resolve as resolve9 } from "path";
|
|
2674
2676
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
2675
2677
|
var cachedGitHubBearerToken;
|
|
2676
2678
|
function cleanToken(value) {
|
|
@@ -2680,9 +2682,25 @@ function cleanToken(value) {
|
|
|
2680
2682
|
function setGitHubBearerTokenForCurrentProcess(token) {
|
|
2681
2683
|
cachedGitHubBearerToken = cleanToken(token ?? undefined);
|
|
2682
2684
|
}
|
|
2683
|
-
function
|
|
2685
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
2686
|
+
const path = resolve9(projectRoot, ".rig", "state", "github-auth.json");
|
|
2687
|
+
if (!existsSync5(path))
|
|
2688
|
+
return null;
|
|
2689
|
+
try {
|
|
2690
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
2691
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
2692
|
+
} catch {
|
|
2693
|
+
return null;
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
2684
2697
|
if (cachedGitHubBearerToken !== undefined)
|
|
2685
2698
|
return cachedGitHubBearerToken;
|
|
2699
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
2700
|
+
if (privateSession) {
|
|
2701
|
+
cachedGitHubBearerToken = privateSession;
|
|
2702
|
+
return cachedGitHubBearerToken;
|
|
2703
|
+
}
|
|
2686
2704
|
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
2687
2705
|
if (envToken) {
|
|
2688
2706
|
cachedGitHubBearerToken = envToken;
|
|
@@ -2702,7 +2720,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2702
2720
|
if (selected?.connection.kind === "remote") {
|
|
2703
2721
|
return {
|
|
2704
2722
|
baseUrl: selected.connection.baseUrl,
|
|
2705
|
-
authToken: readGitHubBearerTokenForRemote(),
|
|
2723
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
2706
2724
|
connectionKind: "remote"
|
|
2707
2725
|
};
|
|
2708
2726
|
}
|
|
@@ -2809,7 +2827,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
|
2809
2827
|
const payload = await requestServerJson(context, "/api/github/auth/token", {
|
|
2810
2828
|
method: "POST",
|
|
2811
2829
|
headers: { "content-type": "application/json" },
|
|
2812
|
-
body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
|
|
2830
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
|
|
2813
2831
|
});
|
|
2814
2832
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2815
2833
|
}
|
|
@@ -2830,7 +2848,7 @@ async function registerProjectViaServer(context, input) {
|
|
|
2830
2848
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2831
2849
|
}
|
|
2832
2850
|
function sleep(ms) {
|
|
2833
|
-
return new Promise((
|
|
2851
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
2834
2852
|
}
|
|
2835
2853
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
2836
2854
|
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
@@ -2921,9 +2939,9 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2921
2939
|
}
|
|
2922
2940
|
|
|
2923
2941
|
// packages/cli/src/commands/_pi-install.ts
|
|
2924
|
-
import { existsSync as
|
|
2942
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2925
2943
|
import { homedir as homedir3 } from "os";
|
|
2926
|
-
import { resolve as
|
|
2944
|
+
import { resolve as resolve10 } from "path";
|
|
2927
2945
|
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2928
2946
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2929
2947
|
export { default } from '@rig/pi-rig';
|
|
@@ -2938,11 +2956,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
2938
2956
|
return { exitCode, stdout, stderr };
|
|
2939
2957
|
}
|
|
2940
2958
|
function resolvePiRigExtensionPath(homeDir) {
|
|
2941
|
-
return
|
|
2959
|
+
return resolve10(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
2942
2960
|
}
|
|
2943
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
2944
|
-
const localPackage =
|
|
2945
|
-
if (exists(
|
|
2961
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync6) {
|
|
2962
|
+
const localPackage = resolve10(projectRoot, "packages", "pi-rig");
|
|
2963
|
+
if (exists(resolve10(localPackage, "package.json")))
|
|
2946
2964
|
return localPackage;
|
|
2947
2965
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
2948
2966
|
}
|
|
@@ -2993,13 +3011,13 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
2993
3011
|
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
2994
3012
|
};
|
|
2995
3013
|
}
|
|
2996
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists =
|
|
3014
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync6) {
|
|
2997
3015
|
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
2998
|
-
const indexPath =
|
|
3016
|
+
const indexPath = resolve10(extensionPath, "index.ts");
|
|
2999
3017
|
if (!exists(indexPath))
|
|
3000
3018
|
return;
|
|
3001
3019
|
try {
|
|
3002
|
-
const content =
|
|
3020
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
3003
3021
|
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
3004
3022
|
rmSync3(extensionPath, { recursive: true, force: true });
|
|
3005
3023
|
}
|
|
@@ -3015,13 +3033,13 @@ async function checkPiRigInstall(input = {}) {
|
|
|
3015
3033
|
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
3016
3034
|
};
|
|
3017
3035
|
}
|
|
3018
|
-
const exists = input.exists ??
|
|
3036
|
+
const exists = input.exists ?? existsSync6;
|
|
3019
3037
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
3020
3038
|
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
3021
3039
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
3022
3040
|
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
3023
3041
|
${piListResult.stderr}`);
|
|
3024
|
-
const legacyBridge = exists(
|
|
3042
|
+
const legacyBridge = exists(resolve10(extensionPath, "index.ts"));
|
|
3025
3043
|
const hasPiRig = listedPiRig;
|
|
3026
3044
|
return {
|
|
3027
3045
|
extensionPath,
|
|
@@ -3359,7 +3377,7 @@ async function executeQueue(context, args) {
|
|
|
3359
3377
|
}
|
|
3360
3378
|
|
|
3361
3379
|
// packages/cli/src/commands/agent.ts
|
|
3362
|
-
import { resolve as
|
|
3380
|
+
import { resolve as resolve12 } from "path";
|
|
3363
3381
|
import {
|
|
3364
3382
|
agentId,
|
|
3365
3383
|
cleanupAgentRuntime,
|
|
@@ -3369,8 +3387,8 @@ import {
|
|
|
3369
3387
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3370
3388
|
|
|
3371
3389
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3372
|
-
import { existsSync as
|
|
3373
|
-
import { resolve as
|
|
3390
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3391
|
+
import { resolve as resolve11 } from "path";
|
|
3374
3392
|
import {
|
|
3375
3393
|
readAuthorityRun,
|
|
3376
3394
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3392,8 +3410,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3392
3410
|
return "claude-code";
|
|
3393
3411
|
}
|
|
3394
3412
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3395
|
-
const issuesPath =
|
|
3396
|
-
if (!
|
|
3413
|
+
const issuesPath = resolve11(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3414
|
+
if (!existsSync7(issuesPath)) {
|
|
3397
3415
|
return null;
|
|
3398
3416
|
}
|
|
3399
3417
|
let latest = null;
|
|
@@ -3460,7 +3478,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3460
3478
|
} else if ("errorText" in next) {
|
|
3461
3479
|
delete next.errorText;
|
|
3462
3480
|
}
|
|
3463
|
-
writeJsonFile3(
|
|
3481
|
+
writeJsonFile3(resolve11(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3464
3482
|
return next;
|
|
3465
3483
|
}
|
|
3466
3484
|
|
|
@@ -3581,10 +3599,10 @@ async function executeAgent(context, args) {
|
|
|
3581
3599
|
status: "running",
|
|
3582
3600
|
startedAt: createdAt,
|
|
3583
3601
|
worktreePath: runtime.workspaceDir,
|
|
3584
|
-
artifactRoot:
|
|
3602
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3585
3603
|
logRoot: runtime.logsDir,
|
|
3586
|
-
sessionPath:
|
|
3587
|
-
sessionLogPath:
|
|
3604
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3605
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3588
3606
|
pid: process.pid
|
|
3589
3607
|
});
|
|
3590
3608
|
const result = await runInAgentRuntime({
|
|
@@ -3604,10 +3622,10 @@ async function executeAgent(context, args) {
|
|
|
3604
3622
|
startedAt: createdAt,
|
|
3605
3623
|
completedAt: failedAt,
|
|
3606
3624
|
worktreePath: runtime.workspaceDir,
|
|
3607
|
-
artifactRoot:
|
|
3625
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3608
3626
|
logRoot: runtime.logsDir,
|
|
3609
|
-
sessionPath:
|
|
3610
|
-
sessionLogPath:
|
|
3627
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3628
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3611
3629
|
pid: process.pid,
|
|
3612
3630
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3613
3631
|
});
|
|
@@ -3624,10 +3642,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3624
3642
|
startedAt: createdAt,
|
|
3625
3643
|
completedAt,
|
|
3626
3644
|
worktreePath: runtime.workspaceDir,
|
|
3627
|
-
artifactRoot:
|
|
3645
|
+
artifactRoot: resolve12(runtime.workspaceDir, "artifacts", taskId),
|
|
3628
3646
|
logRoot: runtime.logsDir,
|
|
3629
|
-
sessionPath:
|
|
3630
|
-
sessionLogPath:
|
|
3647
|
+
sessionPath: resolve12(runtime.sessionDir, "session.json"),
|
|
3648
|
+
sessionLogPath: resolve12(runtime.logsDir, "agent-stdout.log"),
|
|
3631
3649
|
pid: process.pid
|
|
3632
3650
|
});
|
|
3633
3651
|
return {
|
|
@@ -3701,7 +3719,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3701
3719
|
import {
|
|
3702
3720
|
chmodSync,
|
|
3703
3721
|
copyFileSync as copyFileSync2,
|
|
3704
|
-
existsSync as
|
|
3722
|
+
existsSync as existsSync8,
|
|
3705
3723
|
mkdirSync as mkdirSync6,
|
|
3706
3724
|
readdirSync,
|
|
3707
3725
|
readlinkSync,
|
|
@@ -3711,7 +3729,7 @@ import {
|
|
|
3711
3729
|
unlinkSync
|
|
3712
3730
|
} from "fs";
|
|
3713
3731
|
import { homedir as homedir4 } from "os";
|
|
3714
|
-
import { resolve as
|
|
3732
|
+
import { resolve as resolve13 } from "path";
|
|
3715
3733
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3716
3734
|
import {
|
|
3717
3735
|
computeRuntimeImageFingerprint,
|
|
@@ -3730,13 +3748,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3730
3748
|
|
|
3731
3749
|
// packages/cli/src/commands/dist.ts
|
|
3732
3750
|
function collectRigValidatorBuildTargets(input) {
|
|
3733
|
-
const validatorsRoot =
|
|
3734
|
-
if (!
|
|
3751
|
+
const validatorsRoot = resolve13(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3752
|
+
if (!existsSync8(validatorsRoot))
|
|
3735
3753
|
return [];
|
|
3736
3754
|
const targets = [];
|
|
3737
3755
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3738
3756
|
for (const category of categories) {
|
|
3739
|
-
const categoryDir =
|
|
3757
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3740
3758
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3741
3759
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3742
3760
|
continue;
|
|
@@ -3745,7 +3763,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3745
3763
|
continue;
|
|
3746
3764
|
targets.push({
|
|
3747
3765
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3748
|
-
dest:
|
|
3766
|
+
dest: resolve13(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3749
3767
|
cwd: input.hostProjectRoot
|
|
3750
3768
|
});
|
|
3751
3769
|
}
|
|
@@ -3754,16 +3772,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3754
3772
|
}
|
|
3755
3773
|
async function findLatestDistBinary(projectRoot) {
|
|
3756
3774
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3757
|
-
if (!
|
|
3775
|
+
if (!existsSync8(distRoot)) {
|
|
3758
3776
|
return null;
|
|
3759
3777
|
}
|
|
3760
3778
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3761
3779
|
name: entry.name,
|
|
3762
|
-
mtimeMs: statSync(
|
|
3780
|
+
mtimeMs: statSync(resolve13(distRoot, entry.name)).mtimeMs
|
|
3763
3781
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3764
3782
|
for (const { name } of entries) {
|
|
3765
|
-
const candidate =
|
|
3766
|
-
if (
|
|
3783
|
+
const candidate = resolve13(distRoot, name, "bin", "rig");
|
|
3784
|
+
if (existsSync8(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3767
3785
|
return candidate;
|
|
3768
3786
|
}
|
|
3769
3787
|
}
|
|
@@ -3775,7 +3793,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3775
3793
|
async function runDistDoctor(projectRoot) {
|
|
3776
3794
|
const bunPath = Bun.which("bun");
|
|
3777
3795
|
const rigPath = Bun.which("rig");
|
|
3778
|
-
const userBinDir =
|
|
3796
|
+
const userBinDir = resolve13(homedir4(), ".local/bin");
|
|
3779
3797
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3780
3798
|
let rigRunnable = false;
|
|
3781
3799
|
if (rigPath) {
|
|
@@ -3823,15 +3841,15 @@ async function executeDist(context, args) {
|
|
|
3823
3841
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3824
3842
|
let buildDir = null;
|
|
3825
3843
|
if (!source) {
|
|
3826
|
-
buildDir =
|
|
3844
|
+
buildDir = resolve13(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3827
3845
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3828
|
-
source =
|
|
3846
|
+
source = resolve13(buildDir, "bin", "rig");
|
|
3829
3847
|
}
|
|
3830
|
-
if (!
|
|
3848
|
+
if (!existsSync8(source)) {
|
|
3831
3849
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3832
3850
|
}
|
|
3833
|
-
const installedPath =
|
|
3834
|
-
if (
|
|
3851
|
+
const installedPath = resolve13(installDir, "rig");
|
|
3852
|
+
if (existsSync8(installedPath)) {
|
|
3835
3853
|
unlinkSync(installedPath);
|
|
3836
3854
|
}
|
|
3837
3855
|
copyFileSync2(source, installedPath);
|
|
@@ -3873,22 +3891,22 @@ async function executeDist(context, args) {
|
|
|
3873
3891
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3874
3892
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3875
3893
|
const currentId = computeRuntimeImageId(fp);
|
|
3876
|
-
const imagesDir =
|
|
3894
|
+
const imagesDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3877
3895
|
mkdirSync6(imagesDir, { recursive: true });
|
|
3878
3896
|
let pruned = 0;
|
|
3879
3897
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3880
3898
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3881
|
-
rmSync4(
|
|
3899
|
+
rmSync4(resolve13(imagesDir, entry.name), { recursive: true, force: true });
|
|
3882
3900
|
pruned++;
|
|
3883
3901
|
}
|
|
3884
3902
|
}
|
|
3885
3903
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3886
3904
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3887
3905
|
}
|
|
3888
|
-
const imageDir =
|
|
3889
|
-
mkdirSync6(
|
|
3890
|
-
mkdirSync6(
|
|
3891
|
-
mkdirSync6(
|
|
3906
|
+
const imageDir = resolve13(imagesDir, currentId);
|
|
3907
|
+
mkdirSync6(resolve13(imageDir, "bin/hooks"), { recursive: true });
|
|
3908
|
+
mkdirSync6(resolve13(imageDir, "bin/plugins"), { recursive: true });
|
|
3909
|
+
mkdirSync6(resolve13(imageDir, "bin/validators"), { recursive: true });
|
|
3892
3910
|
const hookNames = [
|
|
3893
3911
|
"scope-guard",
|
|
3894
3912
|
"import-guard",
|
|
@@ -3902,25 +3920,25 @@ async function executeDist(context, args) {
|
|
|
3902
3920
|
];
|
|
3903
3921
|
const targets = [];
|
|
3904
3922
|
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:
|
|
3923
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve13(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3924
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3907
3925
|
for (const hookName of hookNames) {
|
|
3908
3926
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3909
|
-
targets.push({ source: src, dest:
|
|
3910
|
-
targets.push({ source: src, dest:
|
|
3927
|
+
targets.push({ source: src, dest: resolve13(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3928
|
+
targets.push({ source: src, dest: resolve13(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3911
3929
|
}
|
|
3912
|
-
const pluginsDir =
|
|
3913
|
-
const binPluginsDir =
|
|
3914
|
-
const validatorsRoot =
|
|
3915
|
-
const binValidatorsDir =
|
|
3930
|
+
const pluginsDir = resolve13(context.projectRoot, "rig/plugins");
|
|
3931
|
+
const binPluginsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3932
|
+
const validatorsRoot = resolve13(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3933
|
+
const binValidatorsDir = resolve13(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3916
3934
|
mkdirSync6(binPluginsDir, { recursive: true });
|
|
3917
3935
|
mkdirSync6(binValidatorsDir, { recursive: true });
|
|
3918
|
-
if (
|
|
3936
|
+
if (existsSync8(pluginsDir)) {
|
|
3919
3937
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3920
3938
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3921
3939
|
if (!m)
|
|
3922
3940
|
continue;
|
|
3923
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3941
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve13(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3924
3942
|
}
|
|
3925
3943
|
}
|
|
3926
3944
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3931,17 +3949,17 @@ async function executeDist(context, args) {
|
|
|
3931
3949
|
const isValidator = dest.includes("/bin/validators/");
|
|
3932
3950
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3933
3951
|
}
|
|
3934
|
-
if (
|
|
3952
|
+
if (existsSync8(pluginsDir)) {
|
|
3935
3953
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3936
3954
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3937
3955
|
if (!m)
|
|
3938
3956
|
continue;
|
|
3939
3957
|
const pluginName = m[1];
|
|
3940
|
-
const imageBin =
|
|
3958
|
+
const imageBin = resolve13(imageDir, `bin/plugins/${pluginName}`);
|
|
3941
3959
|
if (!pluginName)
|
|
3942
3960
|
continue;
|
|
3943
|
-
const symlinkPath =
|
|
3944
|
-
if (
|
|
3961
|
+
const symlinkPath = resolve13(binPluginsDir, pluginName);
|
|
3962
|
+
if (existsSync8(imageBin)) {
|
|
3945
3963
|
try {
|
|
3946
3964
|
unlinkSync(symlinkPath);
|
|
3947
3965
|
} catch {}
|
|
@@ -3949,10 +3967,10 @@ async function executeDist(context, args) {
|
|
|
3949
3967
|
}
|
|
3950
3968
|
}
|
|
3951
3969
|
}
|
|
3952
|
-
if (
|
|
3970
|
+
if (existsSync8(validatorsRoot)) {
|
|
3953
3971
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3954
3972
|
for (const category of categories) {
|
|
3955
|
-
const categoryDir =
|
|
3973
|
+
const categoryDir = resolve13(validatorsRoot, category.name);
|
|
3956
3974
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3957
3975
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3958
3976
|
continue;
|
|
@@ -3960,9 +3978,9 @@ async function executeDist(context, args) {
|
|
|
3960
3978
|
if (!check || check === "index" || check === "shared")
|
|
3961
3979
|
continue;
|
|
3962
3980
|
const validatorName = `${category.name}-${check}`;
|
|
3963
|
-
const imageBin =
|
|
3964
|
-
const symlinkPath =
|
|
3965
|
-
if (
|
|
3981
|
+
const imageBin = resolve13(imageDir, `bin/validators/${validatorName}`);
|
|
3982
|
+
const symlinkPath = resolve13(binValidatorsDir, validatorName);
|
|
3983
|
+
if (existsSync8(imageBin)) {
|
|
3966
3984
|
try {
|
|
3967
3985
|
unlinkSync(symlinkPath);
|
|
3968
3986
|
} catch {}
|
|
@@ -3971,18 +3989,18 @@ async function executeDist(context, args) {
|
|
|
3971
3989
|
}
|
|
3972
3990
|
}
|
|
3973
3991
|
}
|
|
3974
|
-
const agentsDir =
|
|
3975
|
-
if (
|
|
3992
|
+
const agentsDir = resolve13(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3993
|
+
if (existsSync8(agentsDir)) {
|
|
3976
3994
|
let relinkCount = 0;
|
|
3977
3995
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3978
3996
|
if (!agentEntry.isDirectory())
|
|
3979
3997
|
continue;
|
|
3980
|
-
const agentBinDir =
|
|
3981
|
-
if (!
|
|
3998
|
+
const agentBinDir = resolve13(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
3999
|
+
if (!existsSync8(agentBinDir))
|
|
3982
4000
|
continue;
|
|
3983
4001
|
const walkDir = (dir) => {
|
|
3984
4002
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3985
|
-
const fullPath =
|
|
4003
|
+
const fullPath = resolve13(dir, entry.name);
|
|
3986
4004
|
if (entry.isDirectory()) {
|
|
3987
4005
|
walkDir(fullPath);
|
|
3988
4006
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -4016,7 +4034,7 @@ async function executeDist(context, args) {
|
|
|
4016
4034
|
|
|
4017
4035
|
// packages/cli/src/commands/inbox.ts
|
|
4018
4036
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
4019
|
-
import { resolve as
|
|
4037
|
+
import { resolve as resolve14 } from "path";
|
|
4020
4038
|
import {
|
|
4021
4039
|
listAuthorityRuns,
|
|
4022
4040
|
readJsonlFile as readJsonlFile3,
|
|
@@ -4033,7 +4051,7 @@ async function executeInbox(context, args) {
|
|
|
4033
4051
|
pending = task.rest;
|
|
4034
4052
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
4035
4053
|
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(
|
|
4054
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
4037
4055
|
runId: entry.runId,
|
|
4038
4056
|
record
|
|
4039
4057
|
})));
|
|
@@ -4061,7 +4079,7 @@ async function executeInbox(context, args) {
|
|
|
4061
4079
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
4062
4080
|
throw new CliError2("decision must be approve or reject.");
|
|
4063
4081
|
}
|
|
4064
|
-
const approvalsPath =
|
|
4082
|
+
const approvalsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
4065
4083
|
const approvals = readJsonlFile3(approvalsPath);
|
|
4066
4084
|
const resolvedAt = new Date().toISOString();
|
|
4067
4085
|
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 +4096,7 @@ async function executeInbox(context, args) {
|
|
|
4078
4096
|
pending = task.rest;
|
|
4079
4097
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
4080
4098
|
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(
|
|
4099
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve14(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
4082
4100
|
runId: entry.runId,
|
|
4083
4101
|
record
|
|
4084
4102
|
})));
|
|
@@ -4120,7 +4138,7 @@ async function executeInbox(context, args) {
|
|
|
4120
4138
|
const [key, ...restValue] = entry.split("=");
|
|
4121
4139
|
return [key, restValue.join("=")];
|
|
4122
4140
|
}));
|
|
4123
|
-
const requestsPath =
|
|
4141
|
+
const requestsPath = resolve14(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
4124
4142
|
const requests = readJsonlFile3(requestsPath);
|
|
4125
4143
|
const resolvedAt = new Date().toISOString();
|
|
4126
4144
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -4135,14 +4153,14 @@ async function executeInbox(context, args) {
|
|
|
4135
4153
|
}
|
|
4136
4154
|
|
|
4137
4155
|
// packages/cli/src/commands/init.ts
|
|
4138
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
4156
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4139
4157
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4140
|
-
import { resolve as
|
|
4158
|
+
import { resolve as resolve17 } from "path";
|
|
4141
4159
|
import { buildRigInitConfigSource } from "@rig/core";
|
|
4142
4160
|
|
|
4143
4161
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
4144
4162
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
4145
|
-
import { dirname as dirname2, resolve as
|
|
4163
|
+
import { dirname as dirname2, resolve as resolve15, relative, sep } from "path";
|
|
4146
4164
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
4147
4165
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
4148
4166
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -4164,15 +4182,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
4164
4182
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
4165
4183
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
4166
4184
|
}
|
|
4167
|
-
const resolved =
|
|
4185
|
+
const resolved = resolve15(root, relativePath);
|
|
4168
4186
|
const relativeToRoot = relative(root, resolved);
|
|
4169
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
4187
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve15(relativeToRoot) === resolved) {
|
|
4170
4188
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
4171
4189
|
}
|
|
4172
4190
|
return resolved;
|
|
4173
4191
|
}
|
|
4174
4192
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
4175
|
-
const root =
|
|
4193
|
+
const root = resolve15(projectRoot);
|
|
4176
4194
|
const excludedDirectories = [...new Set([
|
|
4177
4195
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
4178
4196
|
...options.excludedDirectories ?? []
|
|
@@ -4184,7 +4202,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
4184
4202
|
for (const entry of entries) {
|
|
4185
4203
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
4186
4204
|
continue;
|
|
4187
|
-
const fullPath =
|
|
4205
|
+
const fullPath = resolve15(dir, entry.name);
|
|
4188
4206
|
if (entry.isDirectory()) {
|
|
4189
4207
|
await visit(fullPath);
|
|
4190
4208
|
continue;
|
|
@@ -4232,8 +4250,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
4232
4250
|
}
|
|
4233
4251
|
|
|
4234
4252
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
4235
|
-
import { existsSync as
|
|
4236
|
-
import { resolve as
|
|
4253
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
4254
|
+
import { resolve as resolve16 } from "path";
|
|
4237
4255
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
4238
4256
|
function check(id, label, status, detail, remediation) {
|
|
4239
4257
|
return {
|
|
@@ -4273,11 +4291,11 @@ function repoSlugFromConfig(config) {
|
|
|
4273
4291
|
function loadFallbackConfig(projectRoot) {
|
|
4274
4292
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
4275
4293
|
for (const name of candidates) {
|
|
4276
|
-
const path =
|
|
4277
|
-
if (!
|
|
4294
|
+
const path = resolve16(projectRoot, name);
|
|
4295
|
+
if (!existsSync9(path))
|
|
4278
4296
|
continue;
|
|
4279
4297
|
try {
|
|
4280
|
-
const source =
|
|
4298
|
+
const source = readFileSync5(path, "utf8");
|
|
4281
4299
|
if (name.endsWith(".json"))
|
|
4282
4300
|
return JSON.parse(source);
|
|
4283
4301
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -4356,7 +4374,7 @@ async function runRigDoctorChecks(options) {
|
|
|
4356
4374
|
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
4375
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
4358
4376
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
4359
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
4377
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync9(resolve16(projectRoot, name)));
|
|
4360
4378
|
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
4379
|
const taskSourceKind = config?.taskSource?.kind;
|
|
4362
4380
|
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 +4469,10 @@ function countDoctorFailures(checks) {
|
|
|
4451
4469
|
}
|
|
4452
4470
|
|
|
4453
4471
|
// packages/cli/src/commands/init.ts
|
|
4454
|
-
var
|
|
4472
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4455
4473
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4456
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4457
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4474
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4475
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4458
4476
|
};
|
|
4459
4477
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
4460
4478
|
const trimmed = remoteUrl.trim();
|
|
@@ -4474,20 +4492,20 @@ function parseRepoSlug(value) {
|
|
|
4474
4492
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
4475
4493
|
}
|
|
4476
4494
|
function ensureRigPrivateDirs(projectRoot) {
|
|
4477
|
-
const rigDir =
|
|
4478
|
-
mkdirSync7(
|
|
4479
|
-
mkdirSync7(
|
|
4480
|
-
mkdirSync7(
|
|
4481
|
-
mkdirSync7(
|
|
4482
|
-
mkdirSync7(
|
|
4483
|
-
const taskConfigPath =
|
|
4484
|
-
if (!
|
|
4495
|
+
const rigDir = resolve17(projectRoot, ".rig");
|
|
4496
|
+
mkdirSync7(resolve17(rigDir, "state"), { recursive: true });
|
|
4497
|
+
mkdirSync7(resolve17(rigDir, "logs"), { recursive: true });
|
|
4498
|
+
mkdirSync7(resolve17(rigDir, "runs"), { recursive: true });
|
|
4499
|
+
mkdirSync7(resolve17(rigDir, "tmp"), { recursive: true });
|
|
4500
|
+
mkdirSync7(resolve17(projectRoot, "artifacts"), { recursive: true });
|
|
4501
|
+
const taskConfigPath = resolve17(rigDir, "task-config.json");
|
|
4502
|
+
if (!existsSync10(taskConfigPath))
|
|
4485
4503
|
writeFileSync5(taskConfigPath, `{}
|
|
4486
4504
|
`, "utf-8");
|
|
4487
4505
|
}
|
|
4488
4506
|
function ensureGitignoreEntries(projectRoot) {
|
|
4489
|
-
const path =
|
|
4490
|
-
const existing =
|
|
4507
|
+
const path = resolve17(projectRoot, ".gitignore");
|
|
4508
|
+
const existing = existsSync10(path) ? readFileSync6(path, "utf8") : "";
|
|
4491
4509
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
4492
4510
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4493
4511
|
if (missing.length === 0)
|
|
@@ -4500,14 +4518,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
4500
4518
|
`, "utf8");
|
|
4501
4519
|
}
|
|
4502
4520
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
4503
|
-
const path =
|
|
4504
|
-
const existing =
|
|
4521
|
+
const path = resolve17(projectRoot, "package.json");
|
|
4522
|
+
const existing = existsSync10(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
|
|
4505
4523
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
4506
4524
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
4507
4525
|
devDependencies[name] = spec;
|
|
4508
4526
|
}
|
|
4509
4527
|
const next = {
|
|
4510
|
-
...
|
|
4528
|
+
...existsSync10(path) ? existing : { name: "rig-project", private: true },
|
|
4511
4529
|
devDependencies
|
|
4512
4530
|
};
|
|
4513
4531
|
writeFileSync5(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -4577,15 +4595,54 @@ async function promptSelect(prompts, options) {
|
|
|
4577
4595
|
throw new CliError2("Init cancelled.", 1);
|
|
4578
4596
|
return String(value);
|
|
4579
4597
|
}
|
|
4580
|
-
|
|
4598
|
+
function sleep2(ms) {
|
|
4599
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
4600
|
+
}
|
|
4601
|
+
function positiveIntFromEnv(name, fallback) {
|
|
4602
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
4603
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
4604
|
+
}
|
|
4605
|
+
function apiSessionTokenFrom(payload) {
|
|
4606
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4607
|
+
return null;
|
|
4608
|
+
const token = payload.apiSessionToken;
|
|
4609
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
4610
|
+
}
|
|
4611
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
4612
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
4613
|
+
authenticated: true,
|
|
4614
|
+
source: input.source,
|
|
4615
|
+
storedOnServer: true,
|
|
4616
|
+
selectedRepo: input.selectedRepo,
|
|
4617
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
4618
|
+
updatedAt: new Date().toISOString()
|
|
4619
|
+
}, null, 2)}
|
|
4620
|
+
`, "utf8");
|
|
4621
|
+
}
|
|
4622
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
4581
4623
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
4582
4624
|
return null;
|
|
4583
|
-
const
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4625
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
4626
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
4627
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
4628
|
+
const deadline = Date.now() + timeoutMs;
|
|
4629
|
+
let last = null;
|
|
4630
|
+
do {
|
|
4631
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
4632
|
+
method: "POST",
|
|
4633
|
+
headers: { "content-type": "application/json" },
|
|
4634
|
+
body: JSON.stringify({ pollId })
|
|
4635
|
+
}).catch(() => null);
|
|
4636
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4637
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
4638
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
4639
|
+
return last;
|
|
4640
|
+
}
|
|
4641
|
+
if (timeoutMs <= 0)
|
|
4642
|
+
return last;
|
|
4643
|
+
await sleep2(intervalMs);
|
|
4644
|
+
} while (Date.now() < deadline);
|
|
4645
|
+
return last;
|
|
4589
4646
|
}
|
|
4590
4647
|
async function runControlPlaneInit(context, options) {
|
|
4591
4648
|
const projectRoot = context.projectRoot;
|
|
@@ -4608,9 +4665,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
4608
4665
|
});
|
|
4609
4666
|
ensureRigPrivateDirs(projectRoot);
|
|
4610
4667
|
ensureGitignoreEntries(projectRoot);
|
|
4611
|
-
const configTsPath =
|
|
4612
|
-
const configJsonPath =
|
|
4613
|
-
const configExists =
|
|
4668
|
+
const configTsPath = resolve17(projectRoot, "rig.config.ts");
|
|
4669
|
+
const configJsonPath = resolve17(projectRoot, "rig.config.json");
|
|
4670
|
+
const configExists = existsSync10(configTsPath) || existsSync10(configJsonPath);
|
|
4614
4671
|
if (!options.privateStateOnly) {
|
|
4615
4672
|
if (configExists && !options.repair) {
|
|
4616
4673
|
if (context.outputMode !== "json")
|
|
@@ -4626,7 +4683,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
4626
4683
|
}
|
|
4627
4684
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
4628
4685
|
}
|
|
4629
|
-
writeFileSync5(
|
|
4686
|
+
writeFileSync5(resolve17(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
4630
4687
|
`, "utf8");
|
|
4631
4688
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
4632
4689
|
let uploadedSnapshot = null;
|
|
@@ -4648,10 +4705,14 @@ async function runControlPlaneInit(context, options) {
|
|
|
4648
4705
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
4649
4706
|
if (token) {
|
|
4650
4707
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
4651
|
-
|
|
4708
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4709
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4652
4710
|
if (serverKind === "remote") {
|
|
4653
|
-
|
|
4654
|
-
|
|
4711
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
4712
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
4713
|
+
selectedRepo: repo.slug,
|
|
4714
|
+
apiSessionToken
|
|
4715
|
+
});
|
|
4655
4716
|
}
|
|
4656
4717
|
} else if (authMethod === "device") {
|
|
4657
4718
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -4660,9 +4721,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
4660
4721
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
4661
4722
|
});
|
|
4662
4723
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4663
|
-
|
|
4664
|
-
|
|
4724
|
+
if (context.outputMode !== "json") {
|
|
4725
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
4726
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
4727
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
4728
|
+
}
|
|
4729
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
4730
|
+
if (completed) {
|
|
4731
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
4732
|
+
if (apiSessionToken) {
|
|
4733
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
4734
|
+
if (serverKind === "remote") {
|
|
4735
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4665
4738
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
4739
|
+
}
|
|
4666
4740
|
}
|
|
4667
4741
|
let remoteCheckoutPreparation = null;
|
|
4668
4742
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -4682,6 +4756,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
4682
4756
|
});
|
|
4683
4757
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
4684
4758
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
4759
|
+
if (serverRootSwitch && token) {
|
|
4760
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
4761
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4762
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4763
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
|
|
4764
|
+
}
|
|
4685
4765
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
4686
4766
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
4687
4767
|
remote: true,
|
|
@@ -4791,7 +4871,7 @@ function parseInitOptions(args) {
|
|
|
4791
4871
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
4792
4872
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
4793
4873
|
const projectRoot = context.projectRoot;
|
|
4794
|
-
const existingConfig =
|
|
4874
|
+
const existingConfig = existsSync10(resolve17(projectRoot, "rig.config.ts")) || existsSync10(resolve17(projectRoot, "rig.config.json"));
|
|
4795
4875
|
let repair = false;
|
|
4796
4876
|
let privateStateOnly = false;
|
|
4797
4877
|
if (existingConfig) {
|
|
@@ -4891,7 +4971,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
4891
4971
|
});
|
|
4892
4972
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
4893
4973
|
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")}.` : "";
|
|
4974
|
+
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
4975
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
4896
4976
|
return result;
|
|
4897
4977
|
}
|
|
@@ -5053,8 +5133,8 @@ async function executeDoctor(context, args) {
|
|
|
5053
5133
|
}
|
|
5054
5134
|
|
|
5055
5135
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
5056
|
-
import { readFileSync as
|
|
5057
|
-
import { resolve as
|
|
5136
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
5137
|
+
import { resolve as resolve18 } from "path";
|
|
5058
5138
|
import {
|
|
5059
5139
|
appendJsonlRecord as appendJsonlRecord2,
|
|
5060
5140
|
readAuthorityRun as readAuthorityRun2,
|
|
@@ -5074,7 +5154,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
|
|
|
5074
5154
|
...patch,
|
|
5075
5155
|
updatedAt: new Date().toISOString()
|
|
5076
5156
|
};
|
|
5077
|
-
writeJsonFile4(
|
|
5157
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
|
|
5078
5158
|
return next;
|
|
5079
5159
|
}
|
|
5080
5160
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -5082,21 +5162,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
5082
5162
|
if (!current) {
|
|
5083
5163
|
return;
|
|
5084
5164
|
}
|
|
5085
|
-
writeJsonFile4(
|
|
5165
|
+
writeJsonFile4(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
|
|
5086
5166
|
...current,
|
|
5087
5167
|
updatedAt: new Date().toISOString()
|
|
5088
5168
|
});
|
|
5089
5169
|
}
|
|
5090
5170
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
5091
|
-
appendJsonlRecord2(
|
|
5171
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
|
|
5092
5172
|
touchAuthorityRun(projectRoot, runId);
|
|
5093
5173
|
}
|
|
5094
5174
|
function appendRunLog(projectRoot, runId, value) {
|
|
5095
|
-
appendJsonlRecord2(
|
|
5175
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
|
|
5096
5176
|
touchAuthorityRun(projectRoot, runId);
|
|
5097
5177
|
}
|
|
5098
5178
|
function appendRunAction(projectRoot, runId, value) {
|
|
5099
|
-
appendJsonlRecord2(
|
|
5179
|
+
appendJsonlRecord2(resolve18(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
|
|
5100
5180
|
id: value.id,
|
|
5101
5181
|
type: "action",
|
|
5102
5182
|
actionType: value.actionType,
|
|
@@ -5167,7 +5247,7 @@ function buildRunPrompt(input) {
|
|
|
5167
5247
|
})();
|
|
5168
5248
|
const scopeText = (() => {
|
|
5169
5249
|
try {
|
|
5170
|
-
const parsed = JSON.parse(
|
|
5250
|
+
const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
|
|
5171
5251
|
const entry = parsed[input.taskId] ?? {};
|
|
5172
5252
|
const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
|
|
5173
5253
|
const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
|
|
@@ -5280,8 +5360,8 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
5280
5360
|
}
|
|
5281
5361
|
|
|
5282
5362
|
// packages/cli/src/commands/inspect.ts
|
|
5283
|
-
import { existsSync as
|
|
5284
|
-
import { resolve as
|
|
5363
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
|
|
5364
|
+
import { resolve as resolve19 } from "path";
|
|
5285
5365
|
import {
|
|
5286
5366
|
listAuthorityRuns as listAuthorityRuns2,
|
|
5287
5367
|
readAuthorityRun as readAuthorityRun3,
|
|
@@ -5302,8 +5382,8 @@ async function executeInspect(context, args) {
|
|
|
5302
5382
|
if (!latestRun) {
|
|
5303
5383
|
throw new CliError2(`No runs found for ${requiredTask}.`);
|
|
5304
5384
|
}
|
|
5305
|
-
const logsPath =
|
|
5306
|
-
if (!
|
|
5385
|
+
const logsPath = resolve19(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
5386
|
+
if (!existsSync11(logsPath)) {
|
|
5307
5387
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
5308
5388
|
}
|
|
5309
5389
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -5313,7 +5393,7 @@ async function executeInspect(context, args) {
|
|
|
5313
5393
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
5314
5394
|
requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
|
|
5315
5395
|
const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
|
|
5316
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
5396
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync11(path));
|
|
5317
5397
|
if (!artifactRoot) {
|
|
5318
5398
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
5319
5399
|
}
|
|
@@ -5370,10 +5450,10 @@ async function executeInspect(context, args) {
|
|
|
5370
5450
|
case "failures": {
|
|
5371
5451
|
requireNoExtraArgs(rest, "bun run rig inspect failures");
|
|
5372
5452
|
const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
|
|
5373
|
-
if (!
|
|
5453
|
+
if (!existsSync11(failed)) {
|
|
5374
5454
|
console.log("No failures recorded.");
|
|
5375
5455
|
} else {
|
|
5376
|
-
process.stdout.write(
|
|
5456
|
+
process.stdout.write(readFileSync8(failed, "utf-8"));
|
|
5377
5457
|
}
|
|
5378
5458
|
return { ok: true, group: "inspect", command };
|
|
5379
5459
|
}
|
|
@@ -5390,11 +5470,11 @@ async function executeInspect(context, args) {
|
|
|
5390
5470
|
return { ok: true, group: "inspect", command };
|
|
5391
5471
|
case "audit": {
|
|
5392
5472
|
requireNoExtraArgs(rest, "bun run rig inspect audit");
|
|
5393
|
-
const auditPath =
|
|
5394
|
-
if (!
|
|
5473
|
+
const auditPath = resolve19(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
|
|
5474
|
+
if (!existsSync11(auditPath)) {
|
|
5395
5475
|
console.log("No audit log found.");
|
|
5396
5476
|
} else {
|
|
5397
|
-
const lines =
|
|
5477
|
+
const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
5398
5478
|
for (const line of lines) {
|
|
5399
5479
|
console.log(line);
|
|
5400
5480
|
}
|
|
@@ -6023,8 +6103,8 @@ async function executeRemote(context, args) {
|
|
|
6023
6103
|
}
|
|
6024
6104
|
|
|
6025
6105
|
// packages/cli/src/commands/run.ts
|
|
6026
|
-
import { existsSync as
|
|
6027
|
-
import { resolve as
|
|
6106
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
6107
|
+
import { resolve as resolve20 } from "path";
|
|
6028
6108
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6029
6109
|
import {
|
|
6030
6110
|
listAuthorityRuns as listAuthorityRuns3,
|
|
@@ -6303,7 +6383,7 @@ async function executeRun(context, args) {
|
|
|
6303
6383
|
if (!run.value) {
|
|
6304
6384
|
throw new CliError2("run timeline requires --run <id>.");
|
|
6305
6385
|
}
|
|
6306
|
-
const timelinePath =
|
|
6386
|
+
const timelinePath = resolve20(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
|
|
6307
6387
|
const printEvents = () => {
|
|
6308
6388
|
const events2 = readJsonlFile4(timelinePath);
|
|
6309
6389
|
if (context.outputMode === "text") {
|
|
@@ -6315,12 +6395,12 @@ async function executeRun(context, args) {
|
|
|
6315
6395
|
};
|
|
6316
6396
|
const events = printEvents();
|
|
6317
6397
|
if (follow.value && context.outputMode === "text") {
|
|
6318
|
-
let lastLength =
|
|
6398
|
+
let lastLength = existsSync12(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
|
|
6319
6399
|
while (true) {
|
|
6320
6400
|
await Bun.sleep(1000);
|
|
6321
|
-
if (!
|
|
6401
|
+
if (!existsSync12(timelinePath))
|
|
6322
6402
|
continue;
|
|
6323
|
-
const next =
|
|
6403
|
+
const next = readFileSync9(timelinePath, "utf8");
|
|
6324
6404
|
if (next.length <= lastLength)
|
|
6325
6405
|
continue;
|
|
6326
6406
|
const delta = next.slice(lastLength);
|
|
@@ -6606,10 +6686,10 @@ async function executeServer(context, args, options) {
|
|
|
6606
6686
|
}
|
|
6607
6687
|
|
|
6608
6688
|
// packages/cli/src/commands/task.ts
|
|
6609
|
-
import { readFileSync as
|
|
6689
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6610
6690
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6611
6691
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
6612
|
-
import { resolve as
|
|
6692
|
+
import { resolve as resolve21 } from "path";
|
|
6613
6693
|
import {
|
|
6614
6694
|
taskArtifactDir,
|
|
6615
6695
|
taskArtifacts,
|
|
@@ -6940,7 +7020,7 @@ async function executeTask(context, args, options) {
|
|
|
6940
7020
|
const fileFlag = takeOption(rest.slice(1), "--file");
|
|
6941
7021
|
let content;
|
|
6942
7022
|
if (fileFlag.value) {
|
|
6943
|
-
content =
|
|
7023
|
+
content = readFileSync10(resolve21(context.projectRoot, fileFlag.value), "utf-8");
|
|
6944
7024
|
} else {
|
|
6945
7025
|
content = await readStdin();
|
|
6946
7026
|
}
|
|
@@ -7161,8 +7241,8 @@ async function executeTask(context, args, options) {
|
|
|
7161
7241
|
}
|
|
7162
7242
|
|
|
7163
7243
|
// packages/cli/src/commands/task-run-driver.ts
|
|
7164
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
7165
|
-
import { resolve as
|
|
7244
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7245
|
+
import { resolve as resolve22 } from "path";
|
|
7166
7246
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
7167
7247
|
import { createInterface as createLineInterface } from "readline";
|
|
7168
7248
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
@@ -7228,12 +7308,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
7228
7308
|
return 0;
|
|
7229
7309
|
let copied = 0;
|
|
7230
7310
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
7231
|
-
const sourcePath =
|
|
7232
|
-
const targetPath =
|
|
7311
|
+
const sourcePath = resolve22(sourceRoot, relativePath);
|
|
7312
|
+
const targetPath = resolve22(targetRoot, relativePath);
|
|
7233
7313
|
try {
|
|
7234
7314
|
if (!statSync2(sourcePath).isFile())
|
|
7235
7315
|
continue;
|
|
7236
|
-
mkdirSync8(
|
|
7316
|
+
mkdirSync8(resolve22(targetPath, ".."), { recursive: true });
|
|
7237
7317
|
copyFileSync3(sourcePath, targetPath);
|
|
7238
7318
|
copied += 1;
|
|
7239
7319
|
} catch {}
|
|
@@ -7267,6 +7347,14 @@ function buildTaskRunReviewEnv(config) {
|
|
|
7267
7347
|
...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
|
|
7268
7348
|
};
|
|
7269
7349
|
}
|
|
7350
|
+
function buildDirtyBaselineHandshakeEnv(input) {
|
|
7351
|
+
if (input.baselineMode !== "dirty-snapshot")
|
|
7352
|
+
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
7353
|
+
return {
|
|
7354
|
+
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
7355
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve22(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
7356
|
+
};
|
|
7357
|
+
}
|
|
7270
7358
|
function positiveInt(value, fallback) {
|
|
7271
7359
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
7272
7360
|
}
|
|
@@ -7375,9 +7463,9 @@ function createCommandRunner(binary) {
|
|
|
7375
7463
|
const stderrChunks = [];
|
|
7376
7464
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7377
7465
|
child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7378
|
-
return await new Promise((
|
|
7379
|
-
child.once("error", (error) =>
|
|
7380
|
-
child.once("close", (code) =>
|
|
7466
|
+
return await new Promise((resolve23) => {
|
|
7467
|
+
child.once("error", (error) => resolve23({ exitCode: 1, stderr: error.message }));
|
|
7468
|
+
child.once("close", (code) => resolve23({
|
|
7381
7469
|
exitCode: code ?? 1,
|
|
7382
7470
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
7383
7471
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -7573,7 +7661,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
|
|
|
7573
7661
|
return null;
|
|
7574
7662
|
}
|
|
7575
7663
|
for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
|
|
7576
|
-
const summary = readJsonFile3(
|
|
7664
|
+
const summary = readJsonFile3(resolve22(artifactDir, "validation-summary.json"), null);
|
|
7577
7665
|
if (!summary || summary.status !== "fail") {
|
|
7578
7666
|
continue;
|
|
7579
7667
|
}
|
|
@@ -7654,9 +7742,9 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
7654
7742
|
if (!input.taskId || !input.workspaceDir) {
|
|
7655
7743
|
return { accepted: false, reason: null };
|
|
7656
7744
|
}
|
|
7657
|
-
const artifactDir =
|
|
7658
|
-
const reviewStatusPath =
|
|
7659
|
-
const taskResultPath =
|
|
7745
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7746
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7747
|
+
const taskResultPath = resolve22(artifactDir, "task-result.json");
|
|
7660
7748
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7661
7749
|
if (reviewStatus !== "APPROVED") {
|
|
7662
7750
|
return { accepted: false, reason: null };
|
|
@@ -7693,12 +7781,12 @@ function resolveTaskRunRetryContext(input) {
|
|
|
7693
7781
|
if (!input.taskId || !input.workspaceDir) {
|
|
7694
7782
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
7695
7783
|
}
|
|
7696
|
-
const artifactDir =
|
|
7697
|
-
const reviewStatePath =
|
|
7698
|
-
const reviewFeedbackPath =
|
|
7699
|
-
const reviewStatusPath =
|
|
7700
|
-
const failedApproachesPath =
|
|
7701
|
-
const validationSummaryPath =
|
|
7784
|
+
const artifactDir = resolve22(input.workspaceDir, "artifacts", input.taskId);
|
|
7785
|
+
const reviewStatePath = resolve22(artifactDir, "review-state.json");
|
|
7786
|
+
const reviewFeedbackPath = resolve22(artifactDir, "review-feedback.md");
|
|
7787
|
+
const reviewStatusPath = resolve22(artifactDir, "review-status.txt");
|
|
7788
|
+
const failedApproachesPath = resolve22(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
7789
|
+
const validationSummaryPath = resolve22(artifactDir, "validation-summary.json");
|
|
7702
7790
|
const reviewState = readJsonFile3(reviewStatePath, null);
|
|
7703
7791
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7704
7792
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
@@ -7753,11 +7841,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
7753
7841
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
7754
7842
|
}
|
|
7755
7843
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
7756
|
-
if (!
|
|
7844
|
+
if (!existsSync13(reviewStatusPath)) {
|
|
7757
7845
|
return null;
|
|
7758
7846
|
}
|
|
7759
7847
|
try {
|
|
7760
|
-
const status =
|
|
7848
|
+
const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
7761
7849
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
7762
7850
|
} catch {
|
|
7763
7851
|
return null;
|
|
@@ -7841,7 +7929,7 @@ function stringArrayField(record, key) {
|
|
|
7841
7929
|
async function executeRigOwnedTaskRun(context, input) {
|
|
7842
7930
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
7843
7931
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
7844
|
-
|
|
7932
|
+
let prompt = buildRunPrompt({
|
|
7845
7933
|
projectRoot: context.projectRoot,
|
|
7846
7934
|
taskId: input.taskId,
|
|
7847
7935
|
fallbackTitle: input.title,
|
|
@@ -7941,7 +8029,22 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7941
8029
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
7942
8030
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
7943
8031
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
7944
|
-
|
|
8032
|
+
const planningArtifactPath = resolve22("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
8033
|
+
const persistedPlanning = {
|
|
8034
|
+
...planningClassification,
|
|
8035
|
+
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
8036
|
+
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
8037
|
+
classifiedAt: new Date().toISOString()
|
|
8038
|
+
};
|
|
8039
|
+
mkdirSync8(resolve22(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
8040
|
+
writeFileSync6(resolve22(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
8041
|
+
`, "utf8");
|
|
8042
|
+
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
8043
|
+
prompt = `${prompt}
|
|
8044
|
+
|
|
8045
|
+
Rig planning classification:
|
|
8046
|
+
${JSON.stringify(persistedPlanning, null, 2)}
|
|
8047
|
+
${planningClassification.planningRequired ? `Before implementing, write a concise implementation plan to ${planningArtifactPath}. Treat that plan artifact as required acceptance evidence for the Plan stage.` : "Planning is not required for this run; briefly state why before implementation."}`;
|
|
7945
8048
|
if (input.runtimeAdapter === "pi") {
|
|
7946
8049
|
for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
|
|
7947
8050
|
appendPiStageLog({
|
|
@@ -8008,6 +8111,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8008
8111
|
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
8009
8112
|
};
|
|
8010
8113
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
8114
|
+
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
8011
8115
|
const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
|
|
8012
8116
|
const maxAttempts = automationLimits.maxValidationAttempts;
|
|
8013
8117
|
const promoteToValidating = (detail) => {
|
|
@@ -8062,22 +8166,29 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8062
8166
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8063
8167
|
status: "running",
|
|
8064
8168
|
worktreePath: latestRuntimeWorkspace,
|
|
8065
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
8169
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve22(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
8066
8170
|
logRoot: latestLogsDir,
|
|
8067
|
-
sessionPath: latestSessionDir ?
|
|
8068
|
-
sessionLogPath: latestLogsDir ?
|
|
8171
|
+
sessionPath: latestSessionDir ? resolve22(latestSessionDir, "session.json") : null,
|
|
8172
|
+
sessionLogPath: latestLogsDir ? resolve22(latestLogsDir, "agent-stdout.log") : null,
|
|
8069
8173
|
branch: runtimeId
|
|
8070
8174
|
});
|
|
8071
8175
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
8072
8176
|
dirtyBaselineApplied = true;
|
|
8073
8177
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
8178
|
+
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
8179
|
+
if (readyFile) {
|
|
8180
|
+
mkdirSync8(resolve22(readyFile, ".."), { recursive: true });
|
|
8181
|
+
writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
8182
|
+
`, "utf8");
|
|
8183
|
+
}
|
|
8074
8184
|
appendRunLog(context.projectRoot, input.runId, {
|
|
8075
8185
|
id: `log:${input.runId}:dirty-baseline`,
|
|
8076
8186
|
title: "Dirty baseline snapshot",
|
|
8077
8187
|
detail: dirty.detail,
|
|
8078
8188
|
tone: dirty.applied ? "tool" : "info",
|
|
8079
8189
|
status: dirty.applied ? "completed" : "skipped",
|
|
8080
|
-
createdAt: new Date().toISOString()
|
|
8190
|
+
createdAt: new Date().toISOString(),
|
|
8191
|
+
payload: readyFile ? { readyFile } : undefined
|
|
8081
8192
|
});
|
|
8082
8193
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
8083
8194
|
}
|
|
@@ -8344,7 +8455,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8344
8455
|
let acceptedArtifactObservedAt = null;
|
|
8345
8456
|
let acceptedArtifactPollTimer = null;
|
|
8346
8457
|
let acceptedArtifactKillTimer = null;
|
|
8347
|
-
const attemptExit = await new Promise((
|
|
8458
|
+
const attemptExit = await new Promise((resolve23) => {
|
|
8348
8459
|
let settled = false;
|
|
8349
8460
|
const settle = (result) => {
|
|
8350
8461
|
if (settled)
|
|
@@ -8352,7 +8463,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8352
8463
|
settled = true;
|
|
8353
8464
|
if (acceptedArtifactPollTimer)
|
|
8354
8465
|
clearInterval(acceptedArtifactPollTimer);
|
|
8355
|
-
|
|
8466
|
+
resolve23(result);
|
|
8356
8467
|
};
|
|
8357
8468
|
const pollAcceptedArtifacts = () => {
|
|
8358
8469
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -8549,6 +8660,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8549
8660
|
});
|
|
8550
8661
|
throw new CliError2(terminalFailureDetail, exit.code ?? 1);
|
|
8551
8662
|
}
|
|
8663
|
+
if (planningClassification.planningRequired) {
|
|
8664
|
+
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
8665
|
+
const expectedPlanPath = resolve22(planWorkspace, planningArtifactPath);
|
|
8666
|
+
if (!existsSync13(expectedPlanPath)) {
|
|
8667
|
+
const failedAt = new Date().toISOString();
|
|
8668
|
+
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
8669
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8670
|
+
status: "needs_attention",
|
|
8671
|
+
completedAt: failedAt,
|
|
8672
|
+
errorText: failureDetail
|
|
8673
|
+
});
|
|
8674
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8675
|
+
id: `log:${input.runId}:plan-artifact-missing`,
|
|
8676
|
+
title: "Required plan artifact missing",
|
|
8677
|
+
detail: failureDetail,
|
|
8678
|
+
tone: "error",
|
|
8679
|
+
status: "needs_attention",
|
|
8680
|
+
createdAt: failedAt
|
|
8681
|
+
});
|
|
8682
|
+
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
8683
|
+
throw new CliError2(failureDetail, 1);
|
|
8684
|
+
}
|
|
8685
|
+
}
|
|
8552
8686
|
const runPiPrFeedbackFix = async (message2) => {
|
|
8553
8687
|
appendPiStageLog({
|
|
8554
8688
|
projectRoot: context.projectRoot,
|
|
@@ -8606,9 +8740,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8606
8740
|
});
|
|
8607
8741
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
8608
8742
|
});
|
|
8609
|
-
const exitCode = await new Promise((
|
|
8610
|
-
child.once("error", () =>
|
|
8611
|
-
child.once("close", (code) =>
|
|
8743
|
+
const exitCode = await new Promise((resolve23) => {
|
|
8744
|
+
child.once("error", () => resolve23(1));
|
|
8745
|
+
child.once("close", (code) => resolve23(code ?? 1));
|
|
8612
8746
|
});
|
|
8613
8747
|
if (exitCode !== 0) {
|
|
8614
8748
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
@@ -8727,8 +8861,8 @@ async function executeTest(context, args) {
|
|
|
8727
8861
|
}
|
|
8728
8862
|
|
|
8729
8863
|
// packages/cli/src/commands/setup.ts
|
|
8730
|
-
import { existsSync as
|
|
8731
|
-
import { resolve as
|
|
8864
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
8865
|
+
import { resolve as resolve23 } from "path";
|
|
8732
8866
|
import { createPluginHost } from "@rig/core";
|
|
8733
8867
|
import {
|
|
8734
8868
|
isSupportedBunVersion as isSupportedBunVersion2,
|
|
@@ -8791,9 +8925,9 @@ function runSetupInit(projectRoot) {
|
|
|
8791
8925
|
mkdirSync9(stateDir, { recursive: true });
|
|
8792
8926
|
mkdirSync9(logsDir, { recursive: true });
|
|
8793
8927
|
mkdirSync9(artifactsDir, { recursive: true });
|
|
8794
|
-
const failuresPath =
|
|
8795
|
-
if (!
|
|
8796
|
-
|
|
8928
|
+
const failuresPath = resolve23(stateDir, "failed_approaches.md");
|
|
8929
|
+
if (!existsSync14(failuresPath)) {
|
|
8930
|
+
writeFileSync7(failuresPath, `# Failed Approaches
|
|
8797
8931
|
|
|
8798
8932
|
`, "utf-8");
|
|
8799
8933
|
}
|
|
@@ -8810,18 +8944,18 @@ async function runSetupCheck(projectRoot) {
|
|
|
8810
8944
|
}
|
|
8811
8945
|
async function runSetupPreflight(projectRoot) {
|
|
8812
8946
|
await runSetupCheck(projectRoot);
|
|
8813
|
-
const validationRoot =
|
|
8814
|
-
if (
|
|
8947
|
+
const validationRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
8948
|
+
if (existsSync14(validationRoot)) {
|
|
8815
8949
|
const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
8816
8950
|
for (const validator of validators) {
|
|
8817
|
-
const script =
|
|
8818
|
-
if (
|
|
8951
|
+
const script = resolve23(validationRoot, validator.name, "validate.sh");
|
|
8952
|
+
if (existsSync14(script)) {
|
|
8819
8953
|
console.log(`OK: validator script ${script}`);
|
|
8820
8954
|
}
|
|
8821
8955
|
}
|
|
8822
8956
|
}
|
|
8823
|
-
const hooksRoot =
|
|
8824
|
-
if (
|
|
8957
|
+
const hooksRoot = resolve23(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
|
|
8958
|
+
if (existsSync14(hooksRoot)) {
|
|
8825
8959
|
const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
|
|
8826
8960
|
for (const hook of hooks) {
|
|
8827
8961
|
console.log(`OK: hook ${hook}`);
|
|
@@ -9198,8 +9332,8 @@ async function executeGroup(context, group, args) {
|
|
|
9198
9332
|
}
|
|
9199
9333
|
}
|
|
9200
9334
|
// packages/cli/src/launcher.ts
|
|
9201
|
-
import { existsSync as
|
|
9202
|
-
import { resolve as
|
|
9335
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9336
|
+
import { resolve as resolve24 } from "path";
|
|
9203
9337
|
import { loadDotEnvSecrets } from "@rig/runtime/baked-secrets";
|
|
9204
9338
|
import { RIG_DEFINITION_DIRNAME, RIG_STATE_DIRNAME, resolveNearestRigProjectRoot } from "@rig/runtime/layout";
|
|
9205
9339
|
function parsePolicyMode(value) {
|
|
@@ -9212,7 +9346,7 @@ function parsePolicyMode(value) {
|
|
|
9212
9346
|
throw new Error(`Invalid --policy-mode value: ${value}. Use off|observe|enforce.`);
|
|
9213
9347
|
}
|
|
9214
9348
|
function hasRigProjectMarker(candidate) {
|
|
9215
|
-
return
|
|
9349
|
+
return existsSync15(resolve24(candidate, RIG_DEFINITION_DIRNAME)) || existsSync15(resolve24(candidate, RIG_STATE_DIRNAME)) || existsSync15(resolve24(candidate, "rig.config.ts")) || existsSync15(resolve24(candidate, "rig.config.json"));
|
|
9216
9350
|
}
|
|
9217
9351
|
function resolveProjectRoot({
|
|
9218
9352
|
envProjectRoot,
|
|
@@ -9221,17 +9355,17 @@ function resolveProjectRoot({
|
|
|
9221
9355
|
cwd = process.cwd()
|
|
9222
9356
|
}) {
|
|
9223
9357
|
if (envProjectRoot) {
|
|
9224
|
-
return
|
|
9358
|
+
return resolve24(cwd, envProjectRoot);
|
|
9225
9359
|
}
|
|
9226
9360
|
const fallbackImportDir = importDir ?? cwd;
|
|
9227
|
-
const candidates = [cwd,
|
|
9361
|
+
const candidates = [cwd, resolve24(execPath, "..", ".."), resolve24(fallbackImportDir, "..")];
|
|
9228
9362
|
for (const candidate of candidates) {
|
|
9229
9363
|
const nearest = resolveNearestRigProjectRoot(candidate);
|
|
9230
9364
|
if (hasRigProjectMarker(nearest)) {
|
|
9231
9365
|
return nearest;
|
|
9232
9366
|
}
|
|
9233
9367
|
}
|
|
9234
|
-
return
|
|
9368
|
+
return resolve24(cwd);
|
|
9235
9369
|
}
|
|
9236
9370
|
function normalizeCliErrorCode(message2, isCliError) {
|
|
9237
9371
|
if (message2.startsWith("Invalid --policy-mode value:")) {
|
|
@@ -9298,7 +9432,7 @@ async function runRigCli(module, options = {}) {
|
|
|
9298
9432
|
runId: context.runId,
|
|
9299
9433
|
outcome,
|
|
9300
9434
|
eventsFile: context.eventBus.getEventsFile(),
|
|
9301
|
-
policyFile:
|
|
9435
|
+
policyFile: resolve24(projectRoot, "rig", "policy", "policy.json"),
|
|
9302
9436
|
policyMode: context.policyMode ?? policyMode ?? module.loadPolicy(projectRoot).mode
|
|
9303
9437
|
}, null, 2));
|
|
9304
9438
|
}
|