@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/src/commands.js
CHANGED
|
@@ -2465,6 +2465,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
2465
2465
|
|
|
2466
2466
|
// packages/cli/src/commands/_server-client.ts
|
|
2467
2467
|
import { spawnSync } from "child_process";
|
|
2468
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
2469
|
+
import { resolve as resolve8 } from "path";
|
|
2468
2470
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
2469
2471
|
var cachedGitHubBearerToken;
|
|
2470
2472
|
function cleanToken(value) {
|
|
@@ -2474,9 +2476,25 @@ function cleanToken(value) {
|
|
|
2474
2476
|
function setGitHubBearerTokenForCurrentProcess(token) {
|
|
2475
2477
|
cachedGitHubBearerToken = cleanToken(token ?? undefined);
|
|
2476
2478
|
}
|
|
2477
|
-
function
|
|
2479
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
2480
|
+
const path = resolve8(projectRoot, ".rig", "state", "github-auth.json");
|
|
2481
|
+
if (!existsSync4(path))
|
|
2482
|
+
return null;
|
|
2483
|
+
try {
|
|
2484
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
2485
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
2486
|
+
} catch {
|
|
2487
|
+
return null;
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
2478
2491
|
if (cachedGitHubBearerToken !== undefined)
|
|
2479
2492
|
return cachedGitHubBearerToken;
|
|
2493
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
2494
|
+
if (privateSession) {
|
|
2495
|
+
cachedGitHubBearerToken = privateSession;
|
|
2496
|
+
return cachedGitHubBearerToken;
|
|
2497
|
+
}
|
|
2480
2498
|
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
2481
2499
|
if (envToken) {
|
|
2482
2500
|
cachedGitHubBearerToken = envToken;
|
|
@@ -2496,7 +2514,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
2496
2514
|
if (selected?.connection.kind === "remote") {
|
|
2497
2515
|
return {
|
|
2498
2516
|
baseUrl: selected.connection.baseUrl,
|
|
2499
|
-
authToken: readGitHubBearerTokenForRemote(),
|
|
2517
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
2500
2518
|
connectionKind: "remote"
|
|
2501
2519
|
};
|
|
2502
2520
|
}
|
|
@@ -2603,7 +2621,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
|
2603
2621
|
const payload = await requestServerJson(context, "/api/github/auth/token", {
|
|
2604
2622
|
method: "POST",
|
|
2605
2623
|
headers: { "content-type": "application/json" },
|
|
2606
|
-
body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
|
|
2624
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
|
|
2607
2625
|
});
|
|
2608
2626
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2609
2627
|
}
|
|
@@ -2624,7 +2642,7 @@ async function registerProjectViaServer(context, input) {
|
|
|
2624
2642
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
2625
2643
|
}
|
|
2626
2644
|
function sleep(ms) {
|
|
2627
|
-
return new Promise((
|
|
2645
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
2628
2646
|
}
|
|
2629
2647
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
2630
2648
|
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
@@ -2715,9 +2733,9 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2715
2733
|
}
|
|
2716
2734
|
|
|
2717
2735
|
// packages/cli/src/commands/_pi-install.ts
|
|
2718
|
-
import { existsSync as
|
|
2736
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2719
2737
|
import { homedir as homedir3 } from "os";
|
|
2720
|
-
import { resolve as
|
|
2738
|
+
import { resolve as resolve9 } from "path";
|
|
2721
2739
|
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2722
2740
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2723
2741
|
export { default } from '@rig/pi-rig';
|
|
@@ -2732,11 +2750,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
2732
2750
|
return { exitCode, stdout, stderr };
|
|
2733
2751
|
}
|
|
2734
2752
|
function resolvePiRigExtensionPath(homeDir) {
|
|
2735
|
-
return
|
|
2753
|
+
return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
2736
2754
|
}
|
|
2737
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
2738
|
-
const localPackage =
|
|
2739
|
-
if (exists(
|
|
2755
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
|
|
2756
|
+
const localPackage = resolve9(projectRoot, "packages", "pi-rig");
|
|
2757
|
+
if (exists(resolve9(localPackage, "package.json")))
|
|
2740
2758
|
return localPackage;
|
|
2741
2759
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
2742
2760
|
}
|
|
@@ -2787,13 +2805,13 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
2787
2805
|
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
2788
2806
|
};
|
|
2789
2807
|
}
|
|
2790
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists =
|
|
2808
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
|
|
2791
2809
|
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
2792
|
-
const indexPath =
|
|
2810
|
+
const indexPath = resolve9(extensionPath, "index.ts");
|
|
2793
2811
|
if (!exists(indexPath))
|
|
2794
2812
|
return;
|
|
2795
2813
|
try {
|
|
2796
|
-
const content =
|
|
2814
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
2797
2815
|
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
2798
2816
|
rmSync3(extensionPath, { recursive: true, force: true });
|
|
2799
2817
|
}
|
|
@@ -2809,13 +2827,13 @@ async function checkPiRigInstall(input = {}) {
|
|
|
2809
2827
|
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
2810
2828
|
};
|
|
2811
2829
|
}
|
|
2812
|
-
const exists = input.exists ??
|
|
2830
|
+
const exists = input.exists ?? existsSync5;
|
|
2813
2831
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
2814
2832
|
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
2815
2833
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
2816
2834
|
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
2817
2835
|
${piListResult.stderr}`);
|
|
2818
|
-
const legacyBridge = exists(
|
|
2836
|
+
const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
|
|
2819
2837
|
const hasPiRig = listedPiRig;
|
|
2820
2838
|
return {
|
|
2821
2839
|
extensionPath,
|
|
@@ -3153,7 +3171,7 @@ async function executeQueue(context, args) {
|
|
|
3153
3171
|
}
|
|
3154
3172
|
|
|
3155
3173
|
// packages/cli/src/commands/agent.ts
|
|
3156
|
-
import { resolve as
|
|
3174
|
+
import { resolve as resolve11 } from "path";
|
|
3157
3175
|
import {
|
|
3158
3176
|
agentId,
|
|
3159
3177
|
cleanupAgentRuntime,
|
|
@@ -3163,8 +3181,8 @@ import {
|
|
|
3163
3181
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3164
3182
|
|
|
3165
3183
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3166
|
-
import { existsSync as
|
|
3167
|
-
import { resolve as
|
|
3184
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3185
|
+
import { resolve as resolve10 } from "path";
|
|
3168
3186
|
import {
|
|
3169
3187
|
readAuthorityRun,
|
|
3170
3188
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3186,8 +3204,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3186
3204
|
return "claude-code";
|
|
3187
3205
|
}
|
|
3188
3206
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3189
|
-
const issuesPath =
|
|
3190
|
-
if (!
|
|
3207
|
+
const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3208
|
+
if (!existsSync6(issuesPath)) {
|
|
3191
3209
|
return null;
|
|
3192
3210
|
}
|
|
3193
3211
|
let latest = null;
|
|
@@ -3254,7 +3272,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3254
3272
|
} else if ("errorText" in next) {
|
|
3255
3273
|
delete next.errorText;
|
|
3256
3274
|
}
|
|
3257
|
-
writeJsonFile3(
|
|
3275
|
+
writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3258
3276
|
return next;
|
|
3259
3277
|
}
|
|
3260
3278
|
|
|
@@ -3375,10 +3393,10 @@ async function executeAgent(context, args) {
|
|
|
3375
3393
|
status: "running",
|
|
3376
3394
|
startedAt: createdAt,
|
|
3377
3395
|
worktreePath: runtime.workspaceDir,
|
|
3378
|
-
artifactRoot:
|
|
3396
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3379
3397
|
logRoot: runtime.logsDir,
|
|
3380
|
-
sessionPath:
|
|
3381
|
-
sessionLogPath:
|
|
3398
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3399
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3382
3400
|
pid: process.pid
|
|
3383
3401
|
});
|
|
3384
3402
|
const result = await runInAgentRuntime({
|
|
@@ -3398,10 +3416,10 @@ async function executeAgent(context, args) {
|
|
|
3398
3416
|
startedAt: createdAt,
|
|
3399
3417
|
completedAt: failedAt,
|
|
3400
3418
|
worktreePath: runtime.workspaceDir,
|
|
3401
|
-
artifactRoot:
|
|
3419
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3402
3420
|
logRoot: runtime.logsDir,
|
|
3403
|
-
sessionPath:
|
|
3404
|
-
sessionLogPath:
|
|
3421
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3422
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3405
3423
|
pid: process.pid,
|
|
3406
3424
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3407
3425
|
});
|
|
@@ -3418,10 +3436,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3418
3436
|
startedAt: createdAt,
|
|
3419
3437
|
completedAt,
|
|
3420
3438
|
worktreePath: runtime.workspaceDir,
|
|
3421
|
-
artifactRoot:
|
|
3439
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3422
3440
|
logRoot: runtime.logsDir,
|
|
3423
|
-
sessionPath:
|
|
3424
|
-
sessionLogPath:
|
|
3441
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3442
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3425
3443
|
pid: process.pid
|
|
3426
3444
|
});
|
|
3427
3445
|
return {
|
|
@@ -3495,7 +3513,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3495
3513
|
import {
|
|
3496
3514
|
chmodSync,
|
|
3497
3515
|
copyFileSync as copyFileSync2,
|
|
3498
|
-
existsSync as
|
|
3516
|
+
existsSync as existsSync7,
|
|
3499
3517
|
mkdirSync as mkdirSync5,
|
|
3500
3518
|
readdirSync,
|
|
3501
3519
|
readlinkSync,
|
|
@@ -3505,7 +3523,7 @@ import {
|
|
|
3505
3523
|
unlinkSync
|
|
3506
3524
|
} from "fs";
|
|
3507
3525
|
import { homedir as homedir4 } from "os";
|
|
3508
|
-
import { resolve as
|
|
3526
|
+
import { resolve as resolve12 } from "path";
|
|
3509
3527
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3510
3528
|
import {
|
|
3511
3529
|
computeRuntimeImageFingerprint,
|
|
@@ -3524,13 +3542,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3524
3542
|
|
|
3525
3543
|
// packages/cli/src/commands/dist.ts
|
|
3526
3544
|
function collectRigValidatorBuildTargets(input) {
|
|
3527
|
-
const validatorsRoot =
|
|
3528
|
-
if (!
|
|
3545
|
+
const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3546
|
+
if (!existsSync7(validatorsRoot))
|
|
3529
3547
|
return [];
|
|
3530
3548
|
const targets = [];
|
|
3531
3549
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3532
3550
|
for (const category of categories) {
|
|
3533
|
-
const categoryDir =
|
|
3551
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
3534
3552
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3535
3553
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3536
3554
|
continue;
|
|
@@ -3539,7 +3557,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3539
3557
|
continue;
|
|
3540
3558
|
targets.push({
|
|
3541
3559
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3542
|
-
dest:
|
|
3560
|
+
dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3543
3561
|
cwd: input.hostProjectRoot
|
|
3544
3562
|
});
|
|
3545
3563
|
}
|
|
@@ -3548,16 +3566,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3548
3566
|
}
|
|
3549
3567
|
async function findLatestDistBinary(projectRoot) {
|
|
3550
3568
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3551
|
-
if (!
|
|
3569
|
+
if (!existsSync7(distRoot)) {
|
|
3552
3570
|
return null;
|
|
3553
3571
|
}
|
|
3554
3572
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3555
3573
|
name: entry.name,
|
|
3556
|
-
mtimeMs: statSync(
|
|
3574
|
+
mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
|
|
3557
3575
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3558
3576
|
for (const { name } of entries) {
|
|
3559
|
-
const candidate =
|
|
3560
|
-
if (
|
|
3577
|
+
const candidate = resolve12(distRoot, name, "bin", "rig");
|
|
3578
|
+
if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3561
3579
|
return candidate;
|
|
3562
3580
|
}
|
|
3563
3581
|
}
|
|
@@ -3569,7 +3587,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3569
3587
|
async function runDistDoctor(projectRoot) {
|
|
3570
3588
|
const bunPath = Bun.which("bun");
|
|
3571
3589
|
const rigPath = Bun.which("rig");
|
|
3572
|
-
const userBinDir =
|
|
3590
|
+
const userBinDir = resolve12(homedir4(), ".local/bin");
|
|
3573
3591
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3574
3592
|
let rigRunnable = false;
|
|
3575
3593
|
if (rigPath) {
|
|
@@ -3617,15 +3635,15 @@ async function executeDist(context, args) {
|
|
|
3617
3635
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3618
3636
|
let buildDir = null;
|
|
3619
3637
|
if (!source) {
|
|
3620
|
-
buildDir =
|
|
3638
|
+
buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3621
3639
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3622
|
-
source =
|
|
3640
|
+
source = resolve12(buildDir, "bin", "rig");
|
|
3623
3641
|
}
|
|
3624
|
-
if (!
|
|
3642
|
+
if (!existsSync7(source)) {
|
|
3625
3643
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3626
3644
|
}
|
|
3627
|
-
const installedPath =
|
|
3628
|
-
if (
|
|
3645
|
+
const installedPath = resolve12(installDir, "rig");
|
|
3646
|
+
if (existsSync7(installedPath)) {
|
|
3629
3647
|
unlinkSync(installedPath);
|
|
3630
3648
|
}
|
|
3631
3649
|
copyFileSync2(source, installedPath);
|
|
@@ -3667,22 +3685,22 @@ async function executeDist(context, args) {
|
|
|
3667
3685
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3668
3686
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3669
3687
|
const currentId = computeRuntimeImageId(fp);
|
|
3670
|
-
const imagesDir =
|
|
3688
|
+
const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3671
3689
|
mkdirSync5(imagesDir, { recursive: true });
|
|
3672
3690
|
let pruned = 0;
|
|
3673
3691
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3674
3692
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3675
|
-
rmSync4(
|
|
3693
|
+
rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
|
|
3676
3694
|
pruned++;
|
|
3677
3695
|
}
|
|
3678
3696
|
}
|
|
3679
3697
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3680
3698
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3681
3699
|
}
|
|
3682
|
-
const imageDir =
|
|
3683
|
-
mkdirSync5(
|
|
3684
|
-
mkdirSync5(
|
|
3685
|
-
mkdirSync5(
|
|
3700
|
+
const imageDir = resolve12(imagesDir, currentId);
|
|
3701
|
+
mkdirSync5(resolve12(imageDir, "bin/hooks"), { recursive: true });
|
|
3702
|
+
mkdirSync5(resolve12(imageDir, "bin/plugins"), { recursive: true });
|
|
3703
|
+
mkdirSync5(resolve12(imageDir, "bin/validators"), { recursive: true });
|
|
3686
3704
|
const hookNames = [
|
|
3687
3705
|
"scope-guard",
|
|
3688
3706
|
"import-guard",
|
|
@@ -3696,25 +3714,25 @@ async function executeDist(context, args) {
|
|
|
3696
3714
|
];
|
|
3697
3715
|
const targets = [];
|
|
3698
3716
|
const hostProjectRoot = process.env.RIG_HOST_PROJECT_ROOT?.trim() || context.projectRoot;
|
|
3699
|
-
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest:
|
|
3700
|
-
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest:
|
|
3717
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3718
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3701
3719
|
for (const hookName of hookNames) {
|
|
3702
3720
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3703
|
-
targets.push({ source: src, dest:
|
|
3704
|
-
targets.push({ source: src, dest:
|
|
3721
|
+
targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3722
|
+
targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3705
3723
|
}
|
|
3706
|
-
const pluginsDir =
|
|
3707
|
-
const binPluginsDir =
|
|
3708
|
-
const validatorsRoot =
|
|
3709
|
-
const binValidatorsDir =
|
|
3724
|
+
const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
|
|
3725
|
+
const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3726
|
+
const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3727
|
+
const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3710
3728
|
mkdirSync5(binPluginsDir, { recursive: true });
|
|
3711
3729
|
mkdirSync5(binValidatorsDir, { recursive: true });
|
|
3712
|
-
if (
|
|
3730
|
+
if (existsSync7(pluginsDir)) {
|
|
3713
3731
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3714
3732
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3715
3733
|
if (!m)
|
|
3716
3734
|
continue;
|
|
3717
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3735
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3718
3736
|
}
|
|
3719
3737
|
}
|
|
3720
3738
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3725,17 +3743,17 @@ async function executeDist(context, args) {
|
|
|
3725
3743
|
const isValidator = dest.includes("/bin/validators/");
|
|
3726
3744
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3727
3745
|
}
|
|
3728
|
-
if (
|
|
3746
|
+
if (existsSync7(pluginsDir)) {
|
|
3729
3747
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3730
3748
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3731
3749
|
if (!m)
|
|
3732
3750
|
continue;
|
|
3733
3751
|
const pluginName = m[1];
|
|
3734
|
-
const imageBin =
|
|
3752
|
+
const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
|
|
3735
3753
|
if (!pluginName)
|
|
3736
3754
|
continue;
|
|
3737
|
-
const symlinkPath =
|
|
3738
|
-
if (
|
|
3755
|
+
const symlinkPath = resolve12(binPluginsDir, pluginName);
|
|
3756
|
+
if (existsSync7(imageBin)) {
|
|
3739
3757
|
try {
|
|
3740
3758
|
unlinkSync(symlinkPath);
|
|
3741
3759
|
} catch {}
|
|
@@ -3743,10 +3761,10 @@ async function executeDist(context, args) {
|
|
|
3743
3761
|
}
|
|
3744
3762
|
}
|
|
3745
3763
|
}
|
|
3746
|
-
if (
|
|
3764
|
+
if (existsSync7(validatorsRoot)) {
|
|
3747
3765
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3748
3766
|
for (const category of categories) {
|
|
3749
|
-
const categoryDir =
|
|
3767
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
3750
3768
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3751
3769
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3752
3770
|
continue;
|
|
@@ -3754,9 +3772,9 @@ async function executeDist(context, args) {
|
|
|
3754
3772
|
if (!check || check === "index" || check === "shared")
|
|
3755
3773
|
continue;
|
|
3756
3774
|
const validatorName = `${category.name}-${check}`;
|
|
3757
|
-
const imageBin =
|
|
3758
|
-
const symlinkPath =
|
|
3759
|
-
if (
|
|
3775
|
+
const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
|
|
3776
|
+
const symlinkPath = resolve12(binValidatorsDir, validatorName);
|
|
3777
|
+
if (existsSync7(imageBin)) {
|
|
3760
3778
|
try {
|
|
3761
3779
|
unlinkSync(symlinkPath);
|
|
3762
3780
|
} catch {}
|
|
@@ -3765,18 +3783,18 @@ async function executeDist(context, args) {
|
|
|
3765
3783
|
}
|
|
3766
3784
|
}
|
|
3767
3785
|
}
|
|
3768
|
-
const agentsDir =
|
|
3769
|
-
if (
|
|
3786
|
+
const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3787
|
+
if (existsSync7(agentsDir)) {
|
|
3770
3788
|
let relinkCount = 0;
|
|
3771
3789
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3772
3790
|
if (!agentEntry.isDirectory())
|
|
3773
3791
|
continue;
|
|
3774
|
-
const agentBinDir =
|
|
3775
|
-
if (!
|
|
3792
|
+
const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
3793
|
+
if (!existsSync7(agentBinDir))
|
|
3776
3794
|
continue;
|
|
3777
3795
|
const walkDir = (dir) => {
|
|
3778
3796
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3779
|
-
const fullPath =
|
|
3797
|
+
const fullPath = resolve12(dir, entry.name);
|
|
3780
3798
|
if (entry.isDirectory()) {
|
|
3781
3799
|
walkDir(fullPath);
|
|
3782
3800
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -3810,7 +3828,7 @@ async function executeDist(context, args) {
|
|
|
3810
3828
|
|
|
3811
3829
|
// packages/cli/src/commands/inbox.ts
|
|
3812
3830
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
3813
|
-
import { resolve as
|
|
3831
|
+
import { resolve as resolve13 } from "path";
|
|
3814
3832
|
import {
|
|
3815
3833
|
listAuthorityRuns,
|
|
3816
3834
|
readJsonlFile as readJsonlFile3,
|
|
@@ -3827,7 +3845,7 @@ async function executeInbox(context, args) {
|
|
|
3827
3845
|
pending = task.rest;
|
|
3828
3846
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
3829
3847
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
3830
|
-
const approvals = runs.flatMap((entry) => readJsonlFile3(
|
|
3848
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
3831
3849
|
runId: entry.runId,
|
|
3832
3850
|
record
|
|
3833
3851
|
})));
|
|
@@ -3855,7 +3873,7 @@ async function executeInbox(context, args) {
|
|
|
3855
3873
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
3856
3874
|
throw new CliError2("decision must be approve or reject.");
|
|
3857
3875
|
}
|
|
3858
|
-
const approvalsPath =
|
|
3876
|
+
const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
3859
3877
|
const approvals = readJsonlFile3(approvalsPath);
|
|
3860
3878
|
const resolvedAt = new Date().toISOString();
|
|
3861
3879
|
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);
|
|
@@ -3872,7 +3890,7 @@ async function executeInbox(context, args) {
|
|
|
3872
3890
|
pending = task.rest;
|
|
3873
3891
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
3874
3892
|
const runs = listAuthorityRuns(context.projectRoot).filter((entry) => (!run.value || entry.runId === run.value) && (!task.value || entry.taskId === task.value));
|
|
3875
|
-
const requests = runs.flatMap((entry) => readJsonlFile3(
|
|
3893
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
3876
3894
|
runId: entry.runId,
|
|
3877
3895
|
record
|
|
3878
3896
|
})));
|
|
@@ -3914,7 +3932,7 @@ async function executeInbox(context, args) {
|
|
|
3914
3932
|
const [key, ...restValue] = entry.split("=");
|
|
3915
3933
|
return [key, restValue.join("=")];
|
|
3916
3934
|
}));
|
|
3917
|
-
const requestsPath =
|
|
3935
|
+
const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
3918
3936
|
const requests = readJsonlFile3(requestsPath);
|
|
3919
3937
|
const resolvedAt = new Date().toISOString();
|
|
3920
3938
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -3929,14 +3947,14 @@ async function executeInbox(context, args) {
|
|
|
3929
3947
|
}
|
|
3930
3948
|
|
|
3931
3949
|
// packages/cli/src/commands/init.ts
|
|
3932
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
3950
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
3933
3951
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3934
|
-
import { resolve as
|
|
3952
|
+
import { resolve as resolve16 } from "path";
|
|
3935
3953
|
import { buildRigInitConfigSource } from "@rig/core";
|
|
3936
3954
|
|
|
3937
3955
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
3938
3956
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
3939
|
-
import { dirname as dirname2, resolve as
|
|
3957
|
+
import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
|
|
3940
3958
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
3941
3959
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
3942
3960
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -3958,15 +3976,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
3958
3976
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
3959
3977
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
3960
3978
|
}
|
|
3961
|
-
const resolved =
|
|
3979
|
+
const resolved = resolve14(root, relativePath);
|
|
3962
3980
|
const relativeToRoot = relative(root, resolved);
|
|
3963
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
3981
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
|
|
3964
3982
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
3965
3983
|
}
|
|
3966
3984
|
return resolved;
|
|
3967
3985
|
}
|
|
3968
3986
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
3969
|
-
const root =
|
|
3987
|
+
const root = resolve14(projectRoot);
|
|
3970
3988
|
const excludedDirectories = [...new Set([
|
|
3971
3989
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
3972
3990
|
...options.excludedDirectories ?? []
|
|
@@ -3978,7 +3996,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
3978
3996
|
for (const entry of entries) {
|
|
3979
3997
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
3980
3998
|
continue;
|
|
3981
|
-
const fullPath =
|
|
3999
|
+
const fullPath = resolve14(dir, entry.name);
|
|
3982
4000
|
if (entry.isDirectory()) {
|
|
3983
4001
|
await visit(fullPath);
|
|
3984
4002
|
continue;
|
|
@@ -4026,8 +4044,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
4026
4044
|
}
|
|
4027
4045
|
|
|
4028
4046
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
4029
|
-
import { existsSync as
|
|
4030
|
-
import { resolve as
|
|
4047
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
4048
|
+
import { resolve as resolve15 } from "path";
|
|
4031
4049
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
4032
4050
|
function check(id, label, status, detail, remediation) {
|
|
4033
4051
|
return {
|
|
@@ -4067,11 +4085,11 @@ function repoSlugFromConfig(config) {
|
|
|
4067
4085
|
function loadFallbackConfig(projectRoot) {
|
|
4068
4086
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
4069
4087
|
for (const name of candidates) {
|
|
4070
|
-
const path =
|
|
4071
|
-
if (!
|
|
4088
|
+
const path = resolve15(projectRoot, name);
|
|
4089
|
+
if (!existsSync8(path))
|
|
4072
4090
|
continue;
|
|
4073
4091
|
try {
|
|
4074
|
-
const source =
|
|
4092
|
+
const source = readFileSync5(path, "utf8");
|
|
4075
4093
|
if (name.endsWith(".json"))
|
|
4076
4094
|
return JSON.parse(source);
|
|
4077
4095
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -4150,7 +4168,7 @@ async function runRigDoctorChecks(options) {
|
|
|
4150
4168
|
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`)."));
|
|
4151
4169
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
4152
4170
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
4153
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
4171
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
|
|
4154
4172
|
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."));
|
|
4155
4173
|
const taskSourceKind = config?.taskSource?.kind;
|
|
4156
4174
|
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."));
|
|
@@ -4245,10 +4263,10 @@ function countDoctorFailures(checks) {
|
|
|
4245
4263
|
}
|
|
4246
4264
|
|
|
4247
4265
|
// packages/cli/src/commands/init.ts
|
|
4248
|
-
var
|
|
4266
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4249
4267
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4250
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4251
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4268
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4269
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4252
4270
|
};
|
|
4253
4271
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
4254
4272
|
const trimmed = remoteUrl.trim();
|
|
@@ -4268,20 +4286,20 @@ function parseRepoSlug(value) {
|
|
|
4268
4286
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
4269
4287
|
}
|
|
4270
4288
|
function ensureRigPrivateDirs(projectRoot) {
|
|
4271
|
-
const rigDir =
|
|
4272
|
-
mkdirSync6(
|
|
4273
|
-
mkdirSync6(
|
|
4274
|
-
mkdirSync6(
|
|
4275
|
-
mkdirSync6(
|
|
4276
|
-
mkdirSync6(
|
|
4277
|
-
const taskConfigPath =
|
|
4278
|
-
if (!
|
|
4289
|
+
const rigDir = resolve16(projectRoot, ".rig");
|
|
4290
|
+
mkdirSync6(resolve16(rigDir, "state"), { recursive: true });
|
|
4291
|
+
mkdirSync6(resolve16(rigDir, "logs"), { recursive: true });
|
|
4292
|
+
mkdirSync6(resolve16(rigDir, "runs"), { recursive: true });
|
|
4293
|
+
mkdirSync6(resolve16(rigDir, "tmp"), { recursive: true });
|
|
4294
|
+
mkdirSync6(resolve16(projectRoot, "artifacts"), { recursive: true });
|
|
4295
|
+
const taskConfigPath = resolve16(rigDir, "task-config.json");
|
|
4296
|
+
if (!existsSync9(taskConfigPath))
|
|
4279
4297
|
writeFileSync5(taskConfigPath, `{}
|
|
4280
4298
|
`, "utf-8");
|
|
4281
4299
|
}
|
|
4282
4300
|
function ensureGitignoreEntries(projectRoot) {
|
|
4283
|
-
const path =
|
|
4284
|
-
const existing =
|
|
4301
|
+
const path = resolve16(projectRoot, ".gitignore");
|
|
4302
|
+
const existing = existsSync9(path) ? readFileSync6(path, "utf8") : "";
|
|
4285
4303
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
4286
4304
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4287
4305
|
if (missing.length === 0)
|
|
@@ -4294,14 +4312,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
4294
4312
|
`, "utf8");
|
|
4295
4313
|
}
|
|
4296
4314
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
4297
|
-
const path =
|
|
4298
|
-
const existing =
|
|
4315
|
+
const path = resolve16(projectRoot, "package.json");
|
|
4316
|
+
const existing = existsSync9(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
|
|
4299
4317
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
4300
4318
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
4301
4319
|
devDependencies[name] = spec;
|
|
4302
4320
|
}
|
|
4303
4321
|
const next = {
|
|
4304
|
-
...
|
|
4322
|
+
...existsSync9(path) ? existing : { name: "rig-project", private: true },
|
|
4305
4323
|
devDependencies
|
|
4306
4324
|
};
|
|
4307
4325
|
writeFileSync5(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -4371,15 +4389,54 @@ async function promptSelect(prompts, options) {
|
|
|
4371
4389
|
throw new CliError2("Init cancelled.", 1);
|
|
4372
4390
|
return String(value);
|
|
4373
4391
|
}
|
|
4374
|
-
|
|
4392
|
+
function sleep2(ms) {
|
|
4393
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
4394
|
+
}
|
|
4395
|
+
function positiveIntFromEnv(name, fallback) {
|
|
4396
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
4397
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
4398
|
+
}
|
|
4399
|
+
function apiSessionTokenFrom(payload) {
|
|
4400
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4401
|
+
return null;
|
|
4402
|
+
const token = payload.apiSessionToken;
|
|
4403
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
4404
|
+
}
|
|
4405
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
4406
|
+
writeFileSync5(resolve16(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
4407
|
+
authenticated: true,
|
|
4408
|
+
source: input.source,
|
|
4409
|
+
storedOnServer: true,
|
|
4410
|
+
selectedRepo: input.selectedRepo,
|
|
4411
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
4412
|
+
updatedAt: new Date().toISOString()
|
|
4413
|
+
}, null, 2)}
|
|
4414
|
+
`, "utf8");
|
|
4415
|
+
}
|
|
4416
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
4375
4417
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
4376
4418
|
return null;
|
|
4377
|
-
const
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4419
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
4420
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
4421
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
4422
|
+
const deadline = Date.now() + timeoutMs;
|
|
4423
|
+
let last = null;
|
|
4424
|
+
do {
|
|
4425
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
4426
|
+
method: "POST",
|
|
4427
|
+
headers: { "content-type": "application/json" },
|
|
4428
|
+
body: JSON.stringify({ pollId })
|
|
4429
|
+
}).catch(() => null);
|
|
4430
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4431
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
4432
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
4433
|
+
return last;
|
|
4434
|
+
}
|
|
4435
|
+
if (timeoutMs <= 0)
|
|
4436
|
+
return last;
|
|
4437
|
+
await sleep2(intervalMs);
|
|
4438
|
+
} while (Date.now() < deadline);
|
|
4439
|
+
return last;
|
|
4383
4440
|
}
|
|
4384
4441
|
async function runControlPlaneInit(context, options) {
|
|
4385
4442
|
const projectRoot = context.projectRoot;
|
|
@@ -4402,9 +4459,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
4402
4459
|
});
|
|
4403
4460
|
ensureRigPrivateDirs(projectRoot);
|
|
4404
4461
|
ensureGitignoreEntries(projectRoot);
|
|
4405
|
-
const configTsPath =
|
|
4406
|
-
const configJsonPath =
|
|
4407
|
-
const configExists =
|
|
4462
|
+
const configTsPath = resolve16(projectRoot, "rig.config.ts");
|
|
4463
|
+
const configJsonPath = resolve16(projectRoot, "rig.config.json");
|
|
4464
|
+
const configExists = existsSync9(configTsPath) || existsSync9(configJsonPath);
|
|
4408
4465
|
if (!options.privateStateOnly) {
|
|
4409
4466
|
if (configExists && !options.repair) {
|
|
4410
4467
|
if (context.outputMode !== "json")
|
|
@@ -4420,7 +4477,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
4420
4477
|
}
|
|
4421
4478
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
4422
4479
|
}
|
|
4423
|
-
writeFileSync5(
|
|
4480
|
+
writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
4424
4481
|
`, "utf8");
|
|
4425
4482
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
4426
4483
|
let uploadedSnapshot = null;
|
|
@@ -4442,10 +4499,14 @@ async function runControlPlaneInit(context, options) {
|
|
|
4442
4499
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
4443
4500
|
if (token) {
|
|
4444
4501
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
4445
|
-
|
|
4502
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4503
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4446
4504
|
if (serverKind === "remote") {
|
|
4447
|
-
|
|
4448
|
-
|
|
4505
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
4506
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
4507
|
+
selectedRepo: repo.slug,
|
|
4508
|
+
apiSessionToken
|
|
4509
|
+
});
|
|
4449
4510
|
}
|
|
4450
4511
|
} else if (authMethod === "device") {
|
|
4451
4512
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -4454,9 +4515,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
4454
4515
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
4455
4516
|
});
|
|
4456
4517
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4457
|
-
|
|
4458
|
-
|
|
4518
|
+
if (context.outputMode !== "json") {
|
|
4519
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
4520
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
4521
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
4522
|
+
}
|
|
4523
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
4524
|
+
if (completed) {
|
|
4525
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
4526
|
+
if (apiSessionToken) {
|
|
4527
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
4528
|
+
if (serverKind === "remote") {
|
|
4529
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4459
4532
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
4533
|
+
}
|
|
4460
4534
|
}
|
|
4461
4535
|
let remoteCheckoutPreparation = null;
|
|
4462
4536
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -4476,6 +4550,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
4476
4550
|
});
|
|
4477
4551
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
4478
4552
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
4553
|
+
if (serverRootSwitch && token) {
|
|
4554
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
4555
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4556
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4557
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
|
|
4558
|
+
}
|
|
4479
4559
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
4480
4560
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
4481
4561
|
remote: true,
|
|
@@ -4585,7 +4665,7 @@ function parseInitOptions(args) {
|
|
|
4585
4665
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
4586
4666
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
4587
4667
|
const projectRoot = context.projectRoot;
|
|
4588
|
-
const existingConfig =
|
|
4668
|
+
const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
|
|
4589
4669
|
let repair = false;
|
|
4590
4670
|
let privateStateOnly = false;
|
|
4591
4671
|
if (existingConfig) {
|
|
@@ -4685,7 +4765,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
4685
4765
|
});
|
|
4686
4766
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
4687
4767
|
const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
|
|
4688
|
-
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")}.` : "";
|
|
4768
|
+
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")}.` : "";
|
|
4689
4769
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
4690
4770
|
return result;
|
|
4691
4771
|
}
|
|
@@ -4847,8 +4927,8 @@ async function executeDoctor(context, args) {
|
|
|
4847
4927
|
}
|
|
4848
4928
|
|
|
4849
4929
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
4850
|
-
import { readFileSync as
|
|
4851
|
-
import { resolve as
|
|
4930
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
4931
|
+
import { resolve as resolve17 } from "path";
|
|
4852
4932
|
import {
|
|
4853
4933
|
appendJsonlRecord as appendJsonlRecord2,
|
|
4854
4934
|
readAuthorityRun as readAuthorityRun2,
|
|
@@ -4868,7 +4948,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
|
|
|
4868
4948
|
...patch,
|
|
4869
4949
|
updatedAt: new Date().toISOString()
|
|
4870
4950
|
};
|
|
4871
|
-
writeJsonFile4(
|
|
4951
|
+
writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
|
|
4872
4952
|
return next;
|
|
4873
4953
|
}
|
|
4874
4954
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -4876,21 +4956,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
4876
4956
|
if (!current) {
|
|
4877
4957
|
return;
|
|
4878
4958
|
}
|
|
4879
|
-
writeJsonFile4(
|
|
4959
|
+
writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
|
|
4880
4960
|
...current,
|
|
4881
4961
|
updatedAt: new Date().toISOString()
|
|
4882
4962
|
});
|
|
4883
4963
|
}
|
|
4884
4964
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
4885
|
-
appendJsonlRecord2(
|
|
4965
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
|
|
4886
4966
|
touchAuthorityRun(projectRoot, runId);
|
|
4887
4967
|
}
|
|
4888
4968
|
function appendRunLog(projectRoot, runId, value) {
|
|
4889
|
-
appendJsonlRecord2(
|
|
4969
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
|
|
4890
4970
|
touchAuthorityRun(projectRoot, runId);
|
|
4891
4971
|
}
|
|
4892
4972
|
function appendRunAction(projectRoot, runId, value) {
|
|
4893
|
-
appendJsonlRecord2(
|
|
4973
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
|
|
4894
4974
|
id: value.id,
|
|
4895
4975
|
type: "action",
|
|
4896
4976
|
actionType: value.actionType,
|
|
@@ -4961,7 +5041,7 @@ function buildRunPrompt(input) {
|
|
|
4961
5041
|
})();
|
|
4962
5042
|
const scopeText = (() => {
|
|
4963
5043
|
try {
|
|
4964
|
-
const parsed = JSON.parse(
|
|
5044
|
+
const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
|
|
4965
5045
|
const entry = parsed[input.taskId] ?? {};
|
|
4966
5046
|
const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
|
|
4967
5047
|
const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
|
|
@@ -5074,8 +5154,8 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
5074
5154
|
}
|
|
5075
5155
|
|
|
5076
5156
|
// packages/cli/src/commands/inspect.ts
|
|
5077
|
-
import { existsSync as
|
|
5078
|
-
import { resolve as
|
|
5157
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
5158
|
+
import { resolve as resolve18 } from "path";
|
|
5079
5159
|
import {
|
|
5080
5160
|
listAuthorityRuns as listAuthorityRuns2,
|
|
5081
5161
|
readAuthorityRun as readAuthorityRun3,
|
|
@@ -5096,8 +5176,8 @@ async function executeInspect(context, args) {
|
|
|
5096
5176
|
if (!latestRun) {
|
|
5097
5177
|
throw new CliError2(`No runs found for ${requiredTask}.`);
|
|
5098
5178
|
}
|
|
5099
|
-
const logsPath =
|
|
5100
|
-
if (!
|
|
5179
|
+
const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
5180
|
+
if (!existsSync10(logsPath)) {
|
|
5101
5181
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
5102
5182
|
}
|
|
5103
5183
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -5107,7 +5187,7 @@ async function executeInspect(context, args) {
|
|
|
5107
5187
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
5108
5188
|
requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
|
|
5109
5189
|
const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
|
|
5110
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
5190
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
|
|
5111
5191
|
if (!artifactRoot) {
|
|
5112
5192
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
5113
5193
|
}
|
|
@@ -5164,10 +5244,10 @@ async function executeInspect(context, args) {
|
|
|
5164
5244
|
case "failures": {
|
|
5165
5245
|
requireNoExtraArgs(rest, "bun run rig inspect failures");
|
|
5166
5246
|
const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
|
|
5167
|
-
if (!
|
|
5247
|
+
if (!existsSync10(failed)) {
|
|
5168
5248
|
console.log("No failures recorded.");
|
|
5169
5249
|
} else {
|
|
5170
|
-
process.stdout.write(
|
|
5250
|
+
process.stdout.write(readFileSync8(failed, "utf-8"));
|
|
5171
5251
|
}
|
|
5172
5252
|
return { ok: true, group: "inspect", command };
|
|
5173
5253
|
}
|
|
@@ -5184,11 +5264,11 @@ async function executeInspect(context, args) {
|
|
|
5184
5264
|
return { ok: true, group: "inspect", command };
|
|
5185
5265
|
case "audit": {
|
|
5186
5266
|
requireNoExtraArgs(rest, "bun run rig inspect audit");
|
|
5187
|
-
const auditPath =
|
|
5188
|
-
if (!
|
|
5267
|
+
const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
|
|
5268
|
+
if (!existsSync10(auditPath)) {
|
|
5189
5269
|
console.log("No audit log found.");
|
|
5190
5270
|
} else {
|
|
5191
|
-
const lines =
|
|
5271
|
+
const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
5192
5272
|
for (const line of lines) {
|
|
5193
5273
|
console.log(line);
|
|
5194
5274
|
}
|
|
@@ -5817,8 +5897,8 @@ async function executeRemote(context, args) {
|
|
|
5817
5897
|
}
|
|
5818
5898
|
|
|
5819
5899
|
// packages/cli/src/commands/run.ts
|
|
5820
|
-
import { existsSync as
|
|
5821
|
-
import { resolve as
|
|
5900
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
5901
|
+
import { resolve as resolve19 } from "path";
|
|
5822
5902
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
5823
5903
|
import {
|
|
5824
5904
|
listAuthorityRuns as listAuthorityRuns3,
|
|
@@ -6097,7 +6177,7 @@ async function executeRun(context, args) {
|
|
|
6097
6177
|
if (!run.value) {
|
|
6098
6178
|
throw new CliError2("run timeline requires --run <id>.");
|
|
6099
6179
|
}
|
|
6100
|
-
const timelinePath =
|
|
6180
|
+
const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
|
|
6101
6181
|
const printEvents = () => {
|
|
6102
6182
|
const events2 = readJsonlFile4(timelinePath);
|
|
6103
6183
|
if (context.outputMode === "text") {
|
|
@@ -6109,12 +6189,12 @@ async function executeRun(context, args) {
|
|
|
6109
6189
|
};
|
|
6110
6190
|
const events = printEvents();
|
|
6111
6191
|
if (follow.value && context.outputMode === "text") {
|
|
6112
|
-
let lastLength =
|
|
6192
|
+
let lastLength = existsSync11(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
|
|
6113
6193
|
while (true) {
|
|
6114
6194
|
await Bun.sleep(1000);
|
|
6115
|
-
if (!
|
|
6195
|
+
if (!existsSync11(timelinePath))
|
|
6116
6196
|
continue;
|
|
6117
|
-
const next =
|
|
6197
|
+
const next = readFileSync9(timelinePath, "utf8");
|
|
6118
6198
|
if (next.length <= lastLength)
|
|
6119
6199
|
continue;
|
|
6120
6200
|
const delta = next.slice(lastLength);
|
|
@@ -6400,10 +6480,10 @@ async function executeServer(context, args, options) {
|
|
|
6400
6480
|
}
|
|
6401
6481
|
|
|
6402
6482
|
// packages/cli/src/commands/task.ts
|
|
6403
|
-
import { readFileSync as
|
|
6483
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6404
6484
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6405
6485
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
6406
|
-
import { resolve as
|
|
6486
|
+
import { resolve as resolve20 } from "path";
|
|
6407
6487
|
import {
|
|
6408
6488
|
taskArtifactDir,
|
|
6409
6489
|
taskArtifacts,
|
|
@@ -6734,7 +6814,7 @@ async function executeTask(context, args, options) {
|
|
|
6734
6814
|
const fileFlag = takeOption(rest.slice(1), "--file");
|
|
6735
6815
|
let content;
|
|
6736
6816
|
if (fileFlag.value) {
|
|
6737
|
-
content =
|
|
6817
|
+
content = readFileSync10(resolve20(context.projectRoot, fileFlag.value), "utf-8");
|
|
6738
6818
|
} else {
|
|
6739
6819
|
content = await readStdin();
|
|
6740
6820
|
}
|
|
@@ -6955,8 +7035,8 @@ async function executeTask(context, args, options) {
|
|
|
6955
7035
|
}
|
|
6956
7036
|
|
|
6957
7037
|
// packages/cli/src/commands/task-run-driver.ts
|
|
6958
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
6959
|
-
import { resolve as
|
|
7038
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7039
|
+
import { resolve as resolve21 } from "path";
|
|
6960
7040
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
6961
7041
|
import { createInterface as createLineInterface } from "readline";
|
|
6962
7042
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
@@ -7022,12 +7102,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
7022
7102
|
return 0;
|
|
7023
7103
|
let copied = 0;
|
|
7024
7104
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
7025
|
-
const sourcePath =
|
|
7026
|
-
const targetPath =
|
|
7105
|
+
const sourcePath = resolve21(sourceRoot, relativePath);
|
|
7106
|
+
const targetPath = resolve21(targetRoot, relativePath);
|
|
7027
7107
|
try {
|
|
7028
7108
|
if (!statSync2(sourcePath).isFile())
|
|
7029
7109
|
continue;
|
|
7030
|
-
mkdirSync7(
|
|
7110
|
+
mkdirSync7(resolve21(targetPath, ".."), { recursive: true });
|
|
7031
7111
|
copyFileSync3(sourcePath, targetPath);
|
|
7032
7112
|
copied += 1;
|
|
7033
7113
|
} catch {}
|
|
@@ -7061,6 +7141,14 @@ function buildTaskRunReviewEnv(config) {
|
|
|
7061
7141
|
...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
|
|
7062
7142
|
};
|
|
7063
7143
|
}
|
|
7144
|
+
function buildDirtyBaselineHandshakeEnv(input) {
|
|
7145
|
+
if (input.baselineMode !== "dirty-snapshot")
|
|
7146
|
+
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
7147
|
+
return {
|
|
7148
|
+
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
7149
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve21(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
7150
|
+
};
|
|
7151
|
+
}
|
|
7064
7152
|
function positiveInt(value, fallback) {
|
|
7065
7153
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
7066
7154
|
}
|
|
@@ -7169,9 +7257,9 @@ function createCommandRunner(binary) {
|
|
|
7169
7257
|
const stderrChunks = [];
|
|
7170
7258
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7171
7259
|
child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7172
|
-
return await new Promise((
|
|
7173
|
-
child.once("error", (error) =>
|
|
7174
|
-
child.once("close", (code) =>
|
|
7260
|
+
return await new Promise((resolve22) => {
|
|
7261
|
+
child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
|
|
7262
|
+
child.once("close", (code) => resolve22({
|
|
7175
7263
|
exitCode: code ?? 1,
|
|
7176
7264
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
7177
7265
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -7367,7 +7455,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
|
|
|
7367
7455
|
return null;
|
|
7368
7456
|
}
|
|
7369
7457
|
for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
|
|
7370
|
-
const summary = readJsonFile3(
|
|
7458
|
+
const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
|
|
7371
7459
|
if (!summary || summary.status !== "fail") {
|
|
7372
7460
|
continue;
|
|
7373
7461
|
}
|
|
@@ -7448,9 +7536,9 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
7448
7536
|
if (!input.taskId || !input.workspaceDir) {
|
|
7449
7537
|
return { accepted: false, reason: null };
|
|
7450
7538
|
}
|
|
7451
|
-
const artifactDir =
|
|
7452
|
-
const reviewStatusPath =
|
|
7453
|
-
const taskResultPath =
|
|
7539
|
+
const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
|
|
7540
|
+
const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
|
|
7541
|
+
const taskResultPath = resolve21(artifactDir, "task-result.json");
|
|
7454
7542
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7455
7543
|
if (reviewStatus !== "APPROVED") {
|
|
7456
7544
|
return { accepted: false, reason: null };
|
|
@@ -7487,12 +7575,12 @@ function resolveTaskRunRetryContext(input) {
|
|
|
7487
7575
|
if (!input.taskId || !input.workspaceDir) {
|
|
7488
7576
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
7489
7577
|
}
|
|
7490
|
-
const artifactDir =
|
|
7491
|
-
const reviewStatePath =
|
|
7492
|
-
const reviewFeedbackPath =
|
|
7493
|
-
const reviewStatusPath =
|
|
7494
|
-
const failedApproachesPath =
|
|
7495
|
-
const validationSummaryPath =
|
|
7578
|
+
const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
|
|
7579
|
+
const reviewStatePath = resolve21(artifactDir, "review-state.json");
|
|
7580
|
+
const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
|
|
7581
|
+
const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
|
|
7582
|
+
const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
7583
|
+
const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
|
|
7496
7584
|
const reviewState = readJsonFile3(reviewStatePath, null);
|
|
7497
7585
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7498
7586
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
@@ -7547,11 +7635,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
7547
7635
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
7548
7636
|
}
|
|
7549
7637
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
7550
|
-
if (!
|
|
7638
|
+
if (!existsSync12(reviewStatusPath)) {
|
|
7551
7639
|
return null;
|
|
7552
7640
|
}
|
|
7553
7641
|
try {
|
|
7554
|
-
const status =
|
|
7642
|
+
const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
7555
7643
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
7556
7644
|
} catch {
|
|
7557
7645
|
return null;
|
|
@@ -7635,7 +7723,7 @@ function stringArrayField(record, key) {
|
|
|
7635
7723
|
async function executeRigOwnedTaskRun(context, input) {
|
|
7636
7724
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
7637
7725
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
7638
|
-
|
|
7726
|
+
let prompt = buildRunPrompt({
|
|
7639
7727
|
projectRoot: context.projectRoot,
|
|
7640
7728
|
taskId: input.taskId,
|
|
7641
7729
|
fallbackTitle: input.title,
|
|
@@ -7735,7 +7823,22 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7735
7823
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
7736
7824
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
7737
7825
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
7738
|
-
|
|
7826
|
+
const planningArtifactPath = resolve21("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
7827
|
+
const persistedPlanning = {
|
|
7828
|
+
...planningClassification,
|
|
7829
|
+
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
7830
|
+
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
7831
|
+
classifiedAt: new Date().toISOString()
|
|
7832
|
+
};
|
|
7833
|
+
mkdirSync7(resolve21(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
7834
|
+
writeFileSync6(resolve21(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
7835
|
+
`, "utf8");
|
|
7836
|
+
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
7837
|
+
prompt = `${prompt}
|
|
7838
|
+
|
|
7839
|
+
Rig planning classification:
|
|
7840
|
+
${JSON.stringify(persistedPlanning, null, 2)}
|
|
7841
|
+
${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."}`;
|
|
7739
7842
|
if (input.runtimeAdapter === "pi") {
|
|
7740
7843
|
for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
|
|
7741
7844
|
appendPiStageLog({
|
|
@@ -7802,6 +7905,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7802
7905
|
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
7803
7906
|
};
|
|
7804
7907
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
7908
|
+
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
7805
7909
|
const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
|
|
7806
7910
|
const maxAttempts = automationLimits.maxValidationAttempts;
|
|
7807
7911
|
const promoteToValidating = (detail) => {
|
|
@@ -7856,22 +7960,29 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7856
7960
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7857
7961
|
status: "running",
|
|
7858
7962
|
worktreePath: latestRuntimeWorkspace,
|
|
7859
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
7963
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
7860
7964
|
logRoot: latestLogsDir,
|
|
7861
|
-
sessionPath: latestSessionDir ?
|
|
7862
|
-
sessionLogPath: latestLogsDir ?
|
|
7965
|
+
sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
|
|
7966
|
+
sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
|
|
7863
7967
|
branch: runtimeId
|
|
7864
7968
|
});
|
|
7865
7969
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
7866
7970
|
dirtyBaselineApplied = true;
|
|
7867
7971
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
7972
|
+
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
7973
|
+
if (readyFile) {
|
|
7974
|
+
mkdirSync7(resolve21(readyFile, ".."), { recursive: true });
|
|
7975
|
+
writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
7976
|
+
`, "utf8");
|
|
7977
|
+
}
|
|
7868
7978
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7869
7979
|
id: `log:${input.runId}:dirty-baseline`,
|
|
7870
7980
|
title: "Dirty baseline snapshot",
|
|
7871
7981
|
detail: dirty.detail,
|
|
7872
7982
|
tone: dirty.applied ? "tool" : "info",
|
|
7873
7983
|
status: dirty.applied ? "completed" : "skipped",
|
|
7874
|
-
createdAt: new Date().toISOString()
|
|
7984
|
+
createdAt: new Date().toISOString(),
|
|
7985
|
+
payload: readyFile ? { readyFile } : undefined
|
|
7875
7986
|
});
|
|
7876
7987
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
7877
7988
|
}
|
|
@@ -8138,7 +8249,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8138
8249
|
let acceptedArtifactObservedAt = null;
|
|
8139
8250
|
let acceptedArtifactPollTimer = null;
|
|
8140
8251
|
let acceptedArtifactKillTimer = null;
|
|
8141
|
-
const attemptExit = await new Promise((
|
|
8252
|
+
const attemptExit = await new Promise((resolve22) => {
|
|
8142
8253
|
let settled = false;
|
|
8143
8254
|
const settle = (result) => {
|
|
8144
8255
|
if (settled)
|
|
@@ -8146,7 +8257,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8146
8257
|
settled = true;
|
|
8147
8258
|
if (acceptedArtifactPollTimer)
|
|
8148
8259
|
clearInterval(acceptedArtifactPollTimer);
|
|
8149
|
-
|
|
8260
|
+
resolve22(result);
|
|
8150
8261
|
};
|
|
8151
8262
|
const pollAcceptedArtifacts = () => {
|
|
8152
8263
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -8343,6 +8454,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8343
8454
|
});
|
|
8344
8455
|
throw new CliError2(terminalFailureDetail, exit.code ?? 1);
|
|
8345
8456
|
}
|
|
8457
|
+
if (planningClassification.planningRequired) {
|
|
8458
|
+
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
8459
|
+
const expectedPlanPath = resolve21(planWorkspace, planningArtifactPath);
|
|
8460
|
+
if (!existsSync12(expectedPlanPath)) {
|
|
8461
|
+
const failedAt = new Date().toISOString();
|
|
8462
|
+
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
8463
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8464
|
+
status: "needs_attention",
|
|
8465
|
+
completedAt: failedAt,
|
|
8466
|
+
errorText: failureDetail
|
|
8467
|
+
});
|
|
8468
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8469
|
+
id: `log:${input.runId}:plan-artifact-missing`,
|
|
8470
|
+
title: "Required plan artifact missing",
|
|
8471
|
+
detail: failureDetail,
|
|
8472
|
+
tone: "error",
|
|
8473
|
+
status: "needs_attention",
|
|
8474
|
+
createdAt: failedAt
|
|
8475
|
+
});
|
|
8476
|
+
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
8477
|
+
throw new CliError2(failureDetail, 1);
|
|
8478
|
+
}
|
|
8479
|
+
}
|
|
8346
8480
|
const runPiPrFeedbackFix = async (message2) => {
|
|
8347
8481
|
appendPiStageLog({
|
|
8348
8482
|
projectRoot: context.projectRoot,
|
|
@@ -8400,9 +8534,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8400
8534
|
});
|
|
8401
8535
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
8402
8536
|
});
|
|
8403
|
-
const exitCode = await new Promise((
|
|
8404
|
-
child.once("error", () =>
|
|
8405
|
-
child.once("close", (code) =>
|
|
8537
|
+
const exitCode = await new Promise((resolve22) => {
|
|
8538
|
+
child.once("error", () => resolve22(1));
|
|
8539
|
+
child.once("close", (code) => resolve22(code ?? 1));
|
|
8406
8540
|
});
|
|
8407
8541
|
if (exitCode !== 0) {
|
|
8408
8542
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
@@ -8521,8 +8655,8 @@ async function executeTest(context, args) {
|
|
|
8521
8655
|
}
|
|
8522
8656
|
|
|
8523
8657
|
// packages/cli/src/commands/setup.ts
|
|
8524
|
-
import { existsSync as
|
|
8525
|
-
import { resolve as
|
|
8658
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
8659
|
+
import { resolve as resolve22 } from "path";
|
|
8526
8660
|
import { createPluginHost } from "@rig/core";
|
|
8527
8661
|
import {
|
|
8528
8662
|
isSupportedBunVersion as isSupportedBunVersion2,
|
|
@@ -8585,9 +8719,9 @@ function runSetupInit(projectRoot) {
|
|
|
8585
8719
|
mkdirSync8(stateDir, { recursive: true });
|
|
8586
8720
|
mkdirSync8(logsDir, { recursive: true });
|
|
8587
8721
|
mkdirSync8(artifactsDir, { recursive: true });
|
|
8588
|
-
const failuresPath =
|
|
8589
|
-
if (!
|
|
8590
|
-
|
|
8722
|
+
const failuresPath = resolve22(stateDir, "failed_approaches.md");
|
|
8723
|
+
if (!existsSync13(failuresPath)) {
|
|
8724
|
+
writeFileSync7(failuresPath, `# Failed Approaches
|
|
8591
8725
|
|
|
8592
8726
|
`, "utf-8");
|
|
8593
8727
|
}
|
|
@@ -8604,18 +8738,18 @@ async function runSetupCheck(projectRoot) {
|
|
|
8604
8738
|
}
|
|
8605
8739
|
async function runSetupPreflight(projectRoot) {
|
|
8606
8740
|
await runSetupCheck(projectRoot);
|
|
8607
|
-
const validationRoot =
|
|
8608
|
-
if (
|
|
8741
|
+
const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
8742
|
+
if (existsSync13(validationRoot)) {
|
|
8609
8743
|
const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
8610
8744
|
for (const validator of validators) {
|
|
8611
|
-
const script =
|
|
8612
|
-
if (
|
|
8745
|
+
const script = resolve22(validationRoot, validator.name, "validate.sh");
|
|
8746
|
+
if (existsSync13(script)) {
|
|
8613
8747
|
console.log(`OK: validator script ${script}`);
|
|
8614
8748
|
}
|
|
8615
8749
|
}
|
|
8616
8750
|
}
|
|
8617
|
-
const hooksRoot =
|
|
8618
|
-
if (
|
|
8751
|
+
const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
|
|
8752
|
+
if (existsSync13(hooksRoot)) {
|
|
8619
8753
|
const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
|
|
8620
8754
|
for (const hook of hooks) {
|
|
8621
8755
|
console.log(`OK: hook ${hook}`);
|