@h-rig/cli 0.0.6-alpha.0 → 0.0.6-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/rig.js +445 -222
- package/dist/src/commands/_authority-runs.js +1 -0
- package/dist/src/commands/_doctor-checks.js +31 -13
- package/dist/src/commands/_operator-view.js +20 -2
- package/dist/src/commands/_preflight.js +25 -7
- package/dist/src/commands/_server-client.js +22 -4
- package/dist/src/commands/_snapshot-upload.js +26 -8
- package/dist/src/commands/agent.js +1 -0
- package/dist/src/commands/doctor.js +31 -13
- package/dist/src/commands/github.js +21 -3
- package/dist/src/commands/init.js +142 -62
- package/dist/src/commands/run.js +53 -9
- package/dist/src/commands/server.js +22 -4
- package/dist/src/commands/setup.js +36 -18
- package/dist/src/commands/task-run-driver.js +132 -17
- package/dist/src/commands/task.js +28 -10
- package/dist/src/commands.js +433 -213
- package/dist/src/index.js +445 -222
- package/dist/src/launcher.js +4 -2
- package/dist/src/runner.js +3 -2
- package/package.json +5 -4
package/dist/src/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;
|
|
@@ -3222,6 +3240,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3222
3240
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
3223
3241
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
3224
3242
|
const next = {
|
|
3243
|
+
...existing ?? {},
|
|
3225
3244
|
runId: input.runId,
|
|
3226
3245
|
projectRoot,
|
|
3227
3246
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -3254,7 +3273,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3254
3273
|
} else if ("errorText" in next) {
|
|
3255
3274
|
delete next.errorText;
|
|
3256
3275
|
}
|
|
3257
|
-
writeJsonFile3(
|
|
3276
|
+
writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3258
3277
|
return next;
|
|
3259
3278
|
}
|
|
3260
3279
|
|
|
@@ -3375,10 +3394,10 @@ async function executeAgent(context, args) {
|
|
|
3375
3394
|
status: "running",
|
|
3376
3395
|
startedAt: createdAt,
|
|
3377
3396
|
worktreePath: runtime.workspaceDir,
|
|
3378
|
-
artifactRoot:
|
|
3397
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3379
3398
|
logRoot: runtime.logsDir,
|
|
3380
|
-
sessionPath:
|
|
3381
|
-
sessionLogPath:
|
|
3399
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3400
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3382
3401
|
pid: process.pid
|
|
3383
3402
|
});
|
|
3384
3403
|
const result = await runInAgentRuntime({
|
|
@@ -3398,10 +3417,10 @@ async function executeAgent(context, args) {
|
|
|
3398
3417
|
startedAt: createdAt,
|
|
3399
3418
|
completedAt: failedAt,
|
|
3400
3419
|
worktreePath: runtime.workspaceDir,
|
|
3401
|
-
artifactRoot:
|
|
3420
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3402
3421
|
logRoot: runtime.logsDir,
|
|
3403
|
-
sessionPath:
|
|
3404
|
-
sessionLogPath:
|
|
3422
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3423
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3405
3424
|
pid: process.pid,
|
|
3406
3425
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3407
3426
|
});
|
|
@@ -3418,10 +3437,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3418
3437
|
startedAt: createdAt,
|
|
3419
3438
|
completedAt,
|
|
3420
3439
|
worktreePath: runtime.workspaceDir,
|
|
3421
|
-
artifactRoot:
|
|
3440
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3422
3441
|
logRoot: runtime.logsDir,
|
|
3423
|
-
sessionPath:
|
|
3424
|
-
sessionLogPath:
|
|
3442
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3443
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3425
3444
|
pid: process.pid
|
|
3426
3445
|
});
|
|
3427
3446
|
return {
|
|
@@ -3495,7 +3514,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3495
3514
|
import {
|
|
3496
3515
|
chmodSync,
|
|
3497
3516
|
copyFileSync as copyFileSync2,
|
|
3498
|
-
existsSync as
|
|
3517
|
+
existsSync as existsSync7,
|
|
3499
3518
|
mkdirSync as mkdirSync5,
|
|
3500
3519
|
readdirSync,
|
|
3501
3520
|
readlinkSync,
|
|
@@ -3505,7 +3524,7 @@ import {
|
|
|
3505
3524
|
unlinkSync
|
|
3506
3525
|
} from "fs";
|
|
3507
3526
|
import { homedir as homedir4 } from "os";
|
|
3508
|
-
import { resolve as
|
|
3527
|
+
import { resolve as resolve12 } from "path";
|
|
3509
3528
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3510
3529
|
import {
|
|
3511
3530
|
computeRuntimeImageFingerprint,
|
|
@@ -3524,13 +3543,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3524
3543
|
|
|
3525
3544
|
// packages/cli/src/commands/dist.ts
|
|
3526
3545
|
function collectRigValidatorBuildTargets(input) {
|
|
3527
|
-
const validatorsRoot =
|
|
3528
|
-
if (!
|
|
3546
|
+
const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3547
|
+
if (!existsSync7(validatorsRoot))
|
|
3529
3548
|
return [];
|
|
3530
3549
|
const targets = [];
|
|
3531
3550
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3532
3551
|
for (const category of categories) {
|
|
3533
|
-
const categoryDir =
|
|
3552
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
3534
3553
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3535
3554
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3536
3555
|
continue;
|
|
@@ -3539,7 +3558,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3539
3558
|
continue;
|
|
3540
3559
|
targets.push({
|
|
3541
3560
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3542
|
-
dest:
|
|
3561
|
+
dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3543
3562
|
cwd: input.hostProjectRoot
|
|
3544
3563
|
});
|
|
3545
3564
|
}
|
|
@@ -3548,16 +3567,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3548
3567
|
}
|
|
3549
3568
|
async function findLatestDistBinary(projectRoot) {
|
|
3550
3569
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3551
|
-
if (!
|
|
3570
|
+
if (!existsSync7(distRoot)) {
|
|
3552
3571
|
return null;
|
|
3553
3572
|
}
|
|
3554
3573
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3555
3574
|
name: entry.name,
|
|
3556
|
-
mtimeMs: statSync(
|
|
3575
|
+
mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
|
|
3557
3576
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3558
3577
|
for (const { name } of entries) {
|
|
3559
|
-
const candidate =
|
|
3560
|
-
if (
|
|
3578
|
+
const candidate = resolve12(distRoot, name, "bin", "rig");
|
|
3579
|
+
if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3561
3580
|
return candidate;
|
|
3562
3581
|
}
|
|
3563
3582
|
}
|
|
@@ -3569,7 +3588,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3569
3588
|
async function runDistDoctor(projectRoot) {
|
|
3570
3589
|
const bunPath = Bun.which("bun");
|
|
3571
3590
|
const rigPath = Bun.which("rig");
|
|
3572
|
-
const userBinDir =
|
|
3591
|
+
const userBinDir = resolve12(homedir4(), ".local/bin");
|
|
3573
3592
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3574
3593
|
let rigRunnable = false;
|
|
3575
3594
|
if (rigPath) {
|
|
@@ -3617,15 +3636,15 @@ async function executeDist(context, args) {
|
|
|
3617
3636
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3618
3637
|
let buildDir = null;
|
|
3619
3638
|
if (!source) {
|
|
3620
|
-
buildDir =
|
|
3639
|
+
buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3621
3640
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3622
|
-
source =
|
|
3641
|
+
source = resolve12(buildDir, "bin", "rig");
|
|
3623
3642
|
}
|
|
3624
|
-
if (!
|
|
3643
|
+
if (!existsSync7(source)) {
|
|
3625
3644
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3626
3645
|
}
|
|
3627
|
-
const installedPath =
|
|
3628
|
-
if (
|
|
3646
|
+
const installedPath = resolve12(installDir, "rig");
|
|
3647
|
+
if (existsSync7(installedPath)) {
|
|
3629
3648
|
unlinkSync(installedPath);
|
|
3630
3649
|
}
|
|
3631
3650
|
copyFileSync2(source, installedPath);
|
|
@@ -3667,22 +3686,22 @@ async function executeDist(context, args) {
|
|
|
3667
3686
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3668
3687
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3669
3688
|
const currentId = computeRuntimeImageId(fp);
|
|
3670
|
-
const imagesDir =
|
|
3689
|
+
const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3671
3690
|
mkdirSync5(imagesDir, { recursive: true });
|
|
3672
3691
|
let pruned = 0;
|
|
3673
3692
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3674
3693
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3675
|
-
rmSync4(
|
|
3694
|
+
rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
|
|
3676
3695
|
pruned++;
|
|
3677
3696
|
}
|
|
3678
3697
|
}
|
|
3679
3698
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3680
3699
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3681
3700
|
}
|
|
3682
|
-
const imageDir =
|
|
3683
|
-
mkdirSync5(
|
|
3684
|
-
mkdirSync5(
|
|
3685
|
-
mkdirSync5(
|
|
3701
|
+
const imageDir = resolve12(imagesDir, currentId);
|
|
3702
|
+
mkdirSync5(resolve12(imageDir, "bin/hooks"), { recursive: true });
|
|
3703
|
+
mkdirSync5(resolve12(imageDir, "bin/plugins"), { recursive: true });
|
|
3704
|
+
mkdirSync5(resolve12(imageDir, "bin/validators"), { recursive: true });
|
|
3686
3705
|
const hookNames = [
|
|
3687
3706
|
"scope-guard",
|
|
3688
3707
|
"import-guard",
|
|
@@ -3696,25 +3715,25 @@ async function executeDist(context, args) {
|
|
|
3696
3715
|
];
|
|
3697
3716
|
const targets = [];
|
|
3698
3717
|
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:
|
|
3718
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3719
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3701
3720
|
for (const hookName of hookNames) {
|
|
3702
3721
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3703
|
-
targets.push({ source: src, dest:
|
|
3704
|
-
targets.push({ source: src, dest:
|
|
3722
|
+
targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3723
|
+
targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3705
3724
|
}
|
|
3706
|
-
const pluginsDir =
|
|
3707
|
-
const binPluginsDir =
|
|
3708
|
-
const validatorsRoot =
|
|
3709
|
-
const binValidatorsDir =
|
|
3725
|
+
const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
|
|
3726
|
+
const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3727
|
+
const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3728
|
+
const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3710
3729
|
mkdirSync5(binPluginsDir, { recursive: true });
|
|
3711
3730
|
mkdirSync5(binValidatorsDir, { recursive: true });
|
|
3712
|
-
if (
|
|
3731
|
+
if (existsSync7(pluginsDir)) {
|
|
3713
3732
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3714
3733
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3715
3734
|
if (!m)
|
|
3716
3735
|
continue;
|
|
3717
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3736
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3718
3737
|
}
|
|
3719
3738
|
}
|
|
3720
3739
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3725,17 +3744,17 @@ async function executeDist(context, args) {
|
|
|
3725
3744
|
const isValidator = dest.includes("/bin/validators/");
|
|
3726
3745
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3727
3746
|
}
|
|
3728
|
-
if (
|
|
3747
|
+
if (existsSync7(pluginsDir)) {
|
|
3729
3748
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3730
3749
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3731
3750
|
if (!m)
|
|
3732
3751
|
continue;
|
|
3733
3752
|
const pluginName = m[1];
|
|
3734
|
-
const imageBin =
|
|
3753
|
+
const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
|
|
3735
3754
|
if (!pluginName)
|
|
3736
3755
|
continue;
|
|
3737
|
-
const symlinkPath =
|
|
3738
|
-
if (
|
|
3756
|
+
const symlinkPath = resolve12(binPluginsDir, pluginName);
|
|
3757
|
+
if (existsSync7(imageBin)) {
|
|
3739
3758
|
try {
|
|
3740
3759
|
unlinkSync(symlinkPath);
|
|
3741
3760
|
} catch {}
|
|
@@ -3743,10 +3762,10 @@ async function executeDist(context, args) {
|
|
|
3743
3762
|
}
|
|
3744
3763
|
}
|
|
3745
3764
|
}
|
|
3746
|
-
if (
|
|
3765
|
+
if (existsSync7(validatorsRoot)) {
|
|
3747
3766
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3748
3767
|
for (const category of categories) {
|
|
3749
|
-
const categoryDir =
|
|
3768
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
3750
3769
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3751
3770
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3752
3771
|
continue;
|
|
@@ -3754,9 +3773,9 @@ async function executeDist(context, args) {
|
|
|
3754
3773
|
if (!check || check === "index" || check === "shared")
|
|
3755
3774
|
continue;
|
|
3756
3775
|
const validatorName = `${category.name}-${check}`;
|
|
3757
|
-
const imageBin =
|
|
3758
|
-
const symlinkPath =
|
|
3759
|
-
if (
|
|
3776
|
+
const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
|
|
3777
|
+
const symlinkPath = resolve12(binValidatorsDir, validatorName);
|
|
3778
|
+
if (existsSync7(imageBin)) {
|
|
3760
3779
|
try {
|
|
3761
3780
|
unlinkSync(symlinkPath);
|
|
3762
3781
|
} catch {}
|
|
@@ -3765,18 +3784,18 @@ async function executeDist(context, args) {
|
|
|
3765
3784
|
}
|
|
3766
3785
|
}
|
|
3767
3786
|
}
|
|
3768
|
-
const agentsDir =
|
|
3769
|
-
if (
|
|
3787
|
+
const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3788
|
+
if (existsSync7(agentsDir)) {
|
|
3770
3789
|
let relinkCount = 0;
|
|
3771
3790
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3772
3791
|
if (!agentEntry.isDirectory())
|
|
3773
3792
|
continue;
|
|
3774
|
-
const agentBinDir =
|
|
3775
|
-
if (!
|
|
3793
|
+
const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
3794
|
+
if (!existsSync7(agentBinDir))
|
|
3776
3795
|
continue;
|
|
3777
3796
|
const walkDir = (dir) => {
|
|
3778
3797
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3779
|
-
const fullPath =
|
|
3798
|
+
const fullPath = resolve12(dir, entry.name);
|
|
3780
3799
|
if (entry.isDirectory()) {
|
|
3781
3800
|
walkDir(fullPath);
|
|
3782
3801
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -3810,7 +3829,7 @@ async function executeDist(context, args) {
|
|
|
3810
3829
|
|
|
3811
3830
|
// packages/cli/src/commands/inbox.ts
|
|
3812
3831
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
3813
|
-
import { resolve as
|
|
3832
|
+
import { resolve as resolve13 } from "path";
|
|
3814
3833
|
import {
|
|
3815
3834
|
listAuthorityRuns,
|
|
3816
3835
|
readJsonlFile as readJsonlFile3,
|
|
@@ -3827,7 +3846,7 @@ async function executeInbox(context, args) {
|
|
|
3827
3846
|
pending = task.rest;
|
|
3828
3847
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
3829
3848
|
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(
|
|
3849
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
3831
3850
|
runId: entry.runId,
|
|
3832
3851
|
record
|
|
3833
3852
|
})));
|
|
@@ -3855,7 +3874,7 @@ async function executeInbox(context, args) {
|
|
|
3855
3874
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
3856
3875
|
throw new CliError2("decision must be approve or reject.");
|
|
3857
3876
|
}
|
|
3858
|
-
const approvalsPath =
|
|
3877
|
+
const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
3859
3878
|
const approvals = readJsonlFile3(approvalsPath);
|
|
3860
3879
|
const resolvedAt = new Date().toISOString();
|
|
3861
3880
|
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 +3891,7 @@ async function executeInbox(context, args) {
|
|
|
3872
3891
|
pending = task.rest;
|
|
3873
3892
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
3874
3893
|
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(
|
|
3894
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
3876
3895
|
runId: entry.runId,
|
|
3877
3896
|
record
|
|
3878
3897
|
})));
|
|
@@ -3914,7 +3933,7 @@ async function executeInbox(context, args) {
|
|
|
3914
3933
|
const [key, ...restValue] = entry.split("=");
|
|
3915
3934
|
return [key, restValue.join("=")];
|
|
3916
3935
|
}));
|
|
3917
|
-
const requestsPath =
|
|
3936
|
+
const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
3918
3937
|
const requests = readJsonlFile3(requestsPath);
|
|
3919
3938
|
const resolvedAt = new Date().toISOString();
|
|
3920
3939
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -3929,14 +3948,14 @@ async function executeInbox(context, args) {
|
|
|
3929
3948
|
}
|
|
3930
3949
|
|
|
3931
3950
|
// packages/cli/src/commands/init.ts
|
|
3932
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
3951
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
3933
3952
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3934
|
-
import { resolve as
|
|
3953
|
+
import { resolve as resolve16 } from "path";
|
|
3935
3954
|
import { buildRigInitConfigSource } from "@rig/core";
|
|
3936
3955
|
|
|
3937
3956
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
3938
3957
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
3939
|
-
import { dirname as dirname2, resolve as
|
|
3958
|
+
import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
|
|
3940
3959
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
3941
3960
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
3942
3961
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -3958,15 +3977,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
3958
3977
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
3959
3978
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
3960
3979
|
}
|
|
3961
|
-
const resolved =
|
|
3980
|
+
const resolved = resolve14(root, relativePath);
|
|
3962
3981
|
const relativeToRoot = relative(root, resolved);
|
|
3963
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
3982
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
|
|
3964
3983
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
3965
3984
|
}
|
|
3966
3985
|
return resolved;
|
|
3967
3986
|
}
|
|
3968
3987
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
3969
|
-
const root =
|
|
3988
|
+
const root = resolve14(projectRoot);
|
|
3970
3989
|
const excludedDirectories = [...new Set([
|
|
3971
3990
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
3972
3991
|
...options.excludedDirectories ?? []
|
|
@@ -3978,7 +3997,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
3978
3997
|
for (const entry of entries) {
|
|
3979
3998
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
3980
3999
|
continue;
|
|
3981
|
-
const fullPath =
|
|
4000
|
+
const fullPath = resolve14(dir, entry.name);
|
|
3982
4001
|
if (entry.isDirectory()) {
|
|
3983
4002
|
await visit(fullPath);
|
|
3984
4003
|
continue;
|
|
@@ -4026,8 +4045,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
4026
4045
|
}
|
|
4027
4046
|
|
|
4028
4047
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
4029
|
-
import { existsSync as
|
|
4030
|
-
import { resolve as
|
|
4048
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
4049
|
+
import { resolve as resolve15 } from "path";
|
|
4031
4050
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
4032
4051
|
function check(id, label, status, detail, remediation) {
|
|
4033
4052
|
return {
|
|
@@ -4067,11 +4086,11 @@ function repoSlugFromConfig(config) {
|
|
|
4067
4086
|
function loadFallbackConfig(projectRoot) {
|
|
4068
4087
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
4069
4088
|
for (const name of candidates) {
|
|
4070
|
-
const path =
|
|
4071
|
-
if (!
|
|
4089
|
+
const path = resolve15(projectRoot, name);
|
|
4090
|
+
if (!existsSync8(path))
|
|
4072
4091
|
continue;
|
|
4073
4092
|
try {
|
|
4074
|
-
const source =
|
|
4093
|
+
const source = readFileSync5(path, "utf8");
|
|
4075
4094
|
if (name.endsWith(".json"))
|
|
4076
4095
|
return JSON.parse(source);
|
|
4077
4096
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -4150,7 +4169,7 @@ async function runRigDoctorChecks(options) {
|
|
|
4150
4169
|
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
4170
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
4152
4171
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
4153
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
4172
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
|
|
4154
4173
|
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
4174
|
const taskSourceKind = config?.taskSource?.kind;
|
|
4156
4175
|
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 +4264,10 @@ function countDoctorFailures(checks) {
|
|
|
4245
4264
|
}
|
|
4246
4265
|
|
|
4247
4266
|
// packages/cli/src/commands/init.ts
|
|
4248
|
-
var
|
|
4267
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4249
4268
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4250
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4251
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4269
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4270
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4252
4271
|
};
|
|
4253
4272
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
4254
4273
|
const trimmed = remoteUrl.trim();
|
|
@@ -4268,20 +4287,20 @@ function parseRepoSlug(value) {
|
|
|
4268
4287
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
4269
4288
|
}
|
|
4270
4289
|
function ensureRigPrivateDirs(projectRoot) {
|
|
4271
|
-
const rigDir =
|
|
4272
|
-
mkdirSync6(
|
|
4273
|
-
mkdirSync6(
|
|
4274
|
-
mkdirSync6(
|
|
4275
|
-
mkdirSync6(
|
|
4276
|
-
mkdirSync6(
|
|
4277
|
-
const taskConfigPath =
|
|
4278
|
-
if (!
|
|
4290
|
+
const rigDir = resolve16(projectRoot, ".rig");
|
|
4291
|
+
mkdirSync6(resolve16(rigDir, "state"), { recursive: true });
|
|
4292
|
+
mkdirSync6(resolve16(rigDir, "logs"), { recursive: true });
|
|
4293
|
+
mkdirSync6(resolve16(rigDir, "runs"), { recursive: true });
|
|
4294
|
+
mkdirSync6(resolve16(rigDir, "tmp"), { recursive: true });
|
|
4295
|
+
mkdirSync6(resolve16(projectRoot, "artifacts"), { recursive: true });
|
|
4296
|
+
const taskConfigPath = resolve16(rigDir, "task-config.json");
|
|
4297
|
+
if (!existsSync9(taskConfigPath))
|
|
4279
4298
|
writeFileSync5(taskConfigPath, `{}
|
|
4280
4299
|
`, "utf-8");
|
|
4281
4300
|
}
|
|
4282
4301
|
function ensureGitignoreEntries(projectRoot) {
|
|
4283
|
-
const path =
|
|
4284
|
-
const existing =
|
|
4302
|
+
const path = resolve16(projectRoot, ".gitignore");
|
|
4303
|
+
const existing = existsSync9(path) ? readFileSync6(path, "utf8") : "";
|
|
4285
4304
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
4286
4305
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4287
4306
|
if (missing.length === 0)
|
|
@@ -4294,14 +4313,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
4294
4313
|
`, "utf8");
|
|
4295
4314
|
}
|
|
4296
4315
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
4297
|
-
const path =
|
|
4298
|
-
const existing =
|
|
4316
|
+
const path = resolve16(projectRoot, "package.json");
|
|
4317
|
+
const existing = existsSync9(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
|
|
4299
4318
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
4300
4319
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
4301
4320
|
devDependencies[name] = spec;
|
|
4302
4321
|
}
|
|
4303
4322
|
const next = {
|
|
4304
|
-
...
|
|
4323
|
+
...existsSync9(path) ? existing : { name: "rig-project", private: true },
|
|
4305
4324
|
devDependencies
|
|
4306
4325
|
};
|
|
4307
4326
|
writeFileSync5(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -4371,15 +4390,54 @@ async function promptSelect(prompts, options) {
|
|
|
4371
4390
|
throw new CliError2("Init cancelled.", 1);
|
|
4372
4391
|
return String(value);
|
|
4373
4392
|
}
|
|
4374
|
-
|
|
4393
|
+
function sleep2(ms) {
|
|
4394
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
4395
|
+
}
|
|
4396
|
+
function positiveIntFromEnv(name, fallback) {
|
|
4397
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
4398
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
4399
|
+
}
|
|
4400
|
+
function apiSessionTokenFrom(payload) {
|
|
4401
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4402
|
+
return null;
|
|
4403
|
+
const token = payload.apiSessionToken;
|
|
4404
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
4405
|
+
}
|
|
4406
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
4407
|
+
writeFileSync5(resolve16(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
4408
|
+
authenticated: true,
|
|
4409
|
+
source: input.source,
|
|
4410
|
+
storedOnServer: true,
|
|
4411
|
+
selectedRepo: input.selectedRepo,
|
|
4412
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
4413
|
+
updatedAt: new Date().toISOString()
|
|
4414
|
+
}, null, 2)}
|
|
4415
|
+
`, "utf8");
|
|
4416
|
+
}
|
|
4417
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
4375
4418
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
4376
4419
|
return null;
|
|
4377
|
-
const
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4420
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
4421
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
4422
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
4423
|
+
const deadline = Date.now() + timeoutMs;
|
|
4424
|
+
let last = null;
|
|
4425
|
+
do {
|
|
4426
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
4427
|
+
method: "POST",
|
|
4428
|
+
headers: { "content-type": "application/json" },
|
|
4429
|
+
body: JSON.stringify({ pollId })
|
|
4430
|
+
}).catch(() => null);
|
|
4431
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4432
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
4433
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
4434
|
+
return last;
|
|
4435
|
+
}
|
|
4436
|
+
if (timeoutMs <= 0)
|
|
4437
|
+
return last;
|
|
4438
|
+
await sleep2(intervalMs);
|
|
4439
|
+
} while (Date.now() < deadline);
|
|
4440
|
+
return last;
|
|
4383
4441
|
}
|
|
4384
4442
|
async function runControlPlaneInit(context, options) {
|
|
4385
4443
|
const projectRoot = context.projectRoot;
|
|
@@ -4402,9 +4460,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
4402
4460
|
});
|
|
4403
4461
|
ensureRigPrivateDirs(projectRoot);
|
|
4404
4462
|
ensureGitignoreEntries(projectRoot);
|
|
4405
|
-
const configTsPath =
|
|
4406
|
-
const configJsonPath =
|
|
4407
|
-
const configExists =
|
|
4463
|
+
const configTsPath = resolve16(projectRoot, "rig.config.ts");
|
|
4464
|
+
const configJsonPath = resolve16(projectRoot, "rig.config.json");
|
|
4465
|
+
const configExists = existsSync9(configTsPath) || existsSync9(configJsonPath);
|
|
4408
4466
|
if (!options.privateStateOnly) {
|
|
4409
4467
|
if (configExists && !options.repair) {
|
|
4410
4468
|
if (context.outputMode !== "json")
|
|
@@ -4420,7 +4478,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
4420
4478
|
}
|
|
4421
4479
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
4422
4480
|
}
|
|
4423
|
-
writeFileSync5(
|
|
4481
|
+
writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
4424
4482
|
`, "utf8");
|
|
4425
4483
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
4426
4484
|
let uploadedSnapshot = null;
|
|
@@ -4442,10 +4500,14 @@ async function runControlPlaneInit(context, options) {
|
|
|
4442
4500
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
4443
4501
|
if (token) {
|
|
4444
4502
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
4445
|
-
|
|
4503
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4504
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4446
4505
|
if (serverKind === "remote") {
|
|
4447
|
-
|
|
4448
|
-
|
|
4506
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
4507
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
4508
|
+
selectedRepo: repo.slug,
|
|
4509
|
+
apiSessionToken
|
|
4510
|
+
});
|
|
4449
4511
|
}
|
|
4450
4512
|
} else if (authMethod === "device") {
|
|
4451
4513
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -4454,9 +4516,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
4454
4516
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
4455
4517
|
});
|
|
4456
4518
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4457
|
-
|
|
4458
|
-
|
|
4519
|
+
if (context.outputMode !== "json") {
|
|
4520
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
4521
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
4522
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
4523
|
+
}
|
|
4524
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
4525
|
+
if (completed) {
|
|
4526
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
4527
|
+
if (apiSessionToken) {
|
|
4528
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
4529
|
+
if (serverKind === "remote") {
|
|
4530
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4459
4533
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
4534
|
+
}
|
|
4460
4535
|
}
|
|
4461
4536
|
let remoteCheckoutPreparation = null;
|
|
4462
4537
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -4476,6 +4551,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
4476
4551
|
});
|
|
4477
4552
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
4478
4553
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
4554
|
+
if (serverRootSwitch && token) {
|
|
4555
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
4556
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4557
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4558
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
|
|
4559
|
+
}
|
|
4479
4560
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
4480
4561
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
4481
4562
|
remote: true,
|
|
@@ -4585,7 +4666,7 @@ function parseInitOptions(args) {
|
|
|
4585
4666
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
4586
4667
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
4587
4668
|
const projectRoot = context.projectRoot;
|
|
4588
|
-
const existingConfig =
|
|
4669
|
+
const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
|
|
4589
4670
|
let repair = false;
|
|
4590
4671
|
let privateStateOnly = false;
|
|
4591
4672
|
if (existingConfig) {
|
|
@@ -4685,7 +4766,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
4685
4766
|
});
|
|
4686
4767
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
4687
4768
|
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")}.` : "";
|
|
4769
|
+
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
4770
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
4690
4771
|
return result;
|
|
4691
4772
|
}
|
|
@@ -4847,8 +4928,8 @@ async function executeDoctor(context, args) {
|
|
|
4847
4928
|
}
|
|
4848
4929
|
|
|
4849
4930
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
4850
|
-
import { readFileSync as
|
|
4851
|
-
import { resolve as
|
|
4931
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
4932
|
+
import { resolve as resolve17 } from "path";
|
|
4852
4933
|
import {
|
|
4853
4934
|
appendJsonlRecord as appendJsonlRecord2,
|
|
4854
4935
|
readAuthorityRun as readAuthorityRun2,
|
|
@@ -4868,7 +4949,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
|
|
|
4868
4949
|
...patch,
|
|
4869
4950
|
updatedAt: new Date().toISOString()
|
|
4870
4951
|
};
|
|
4871
|
-
writeJsonFile4(
|
|
4952
|
+
writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
|
|
4872
4953
|
return next;
|
|
4873
4954
|
}
|
|
4874
4955
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -4876,21 +4957,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
4876
4957
|
if (!current) {
|
|
4877
4958
|
return;
|
|
4878
4959
|
}
|
|
4879
|
-
writeJsonFile4(
|
|
4960
|
+
writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
|
|
4880
4961
|
...current,
|
|
4881
4962
|
updatedAt: new Date().toISOString()
|
|
4882
4963
|
});
|
|
4883
4964
|
}
|
|
4884
4965
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
4885
|
-
appendJsonlRecord2(
|
|
4966
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
|
|
4886
4967
|
touchAuthorityRun(projectRoot, runId);
|
|
4887
4968
|
}
|
|
4888
4969
|
function appendRunLog(projectRoot, runId, value) {
|
|
4889
|
-
appendJsonlRecord2(
|
|
4970
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
|
|
4890
4971
|
touchAuthorityRun(projectRoot, runId);
|
|
4891
4972
|
}
|
|
4892
4973
|
function appendRunAction(projectRoot, runId, value) {
|
|
4893
|
-
appendJsonlRecord2(
|
|
4974
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
|
|
4894
4975
|
id: value.id,
|
|
4895
4976
|
type: "action",
|
|
4896
4977
|
actionType: value.actionType,
|
|
@@ -4961,7 +5042,7 @@ function buildRunPrompt(input) {
|
|
|
4961
5042
|
})();
|
|
4962
5043
|
const scopeText = (() => {
|
|
4963
5044
|
try {
|
|
4964
|
-
const parsed = JSON.parse(
|
|
5045
|
+
const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
|
|
4965
5046
|
const entry = parsed[input.taskId] ?? {};
|
|
4966
5047
|
const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
|
|
4967
5048
|
const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
|
|
@@ -5074,8 +5155,8 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
5074
5155
|
}
|
|
5075
5156
|
|
|
5076
5157
|
// packages/cli/src/commands/inspect.ts
|
|
5077
|
-
import { existsSync as
|
|
5078
|
-
import { resolve as
|
|
5158
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
5159
|
+
import { resolve as resolve18 } from "path";
|
|
5079
5160
|
import {
|
|
5080
5161
|
listAuthorityRuns as listAuthorityRuns2,
|
|
5081
5162
|
readAuthorityRun as readAuthorityRun3,
|
|
@@ -5096,8 +5177,8 @@ async function executeInspect(context, args) {
|
|
|
5096
5177
|
if (!latestRun) {
|
|
5097
5178
|
throw new CliError2(`No runs found for ${requiredTask}.`);
|
|
5098
5179
|
}
|
|
5099
|
-
const logsPath =
|
|
5100
|
-
if (!
|
|
5180
|
+
const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
5181
|
+
if (!existsSync10(logsPath)) {
|
|
5101
5182
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
5102
5183
|
}
|
|
5103
5184
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -5107,7 +5188,7 @@ async function executeInspect(context, args) {
|
|
|
5107
5188
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
5108
5189
|
requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
|
|
5109
5190
|
const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
|
|
5110
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
5191
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
|
|
5111
5192
|
if (!artifactRoot) {
|
|
5112
5193
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
5113
5194
|
}
|
|
@@ -5164,10 +5245,10 @@ async function executeInspect(context, args) {
|
|
|
5164
5245
|
case "failures": {
|
|
5165
5246
|
requireNoExtraArgs(rest, "bun run rig inspect failures");
|
|
5166
5247
|
const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
|
|
5167
|
-
if (!
|
|
5248
|
+
if (!existsSync10(failed)) {
|
|
5168
5249
|
console.log("No failures recorded.");
|
|
5169
5250
|
} else {
|
|
5170
|
-
process.stdout.write(
|
|
5251
|
+
process.stdout.write(readFileSync8(failed, "utf-8"));
|
|
5171
5252
|
}
|
|
5172
5253
|
return { ok: true, group: "inspect", command };
|
|
5173
5254
|
}
|
|
@@ -5184,11 +5265,11 @@ async function executeInspect(context, args) {
|
|
|
5184
5265
|
return { ok: true, group: "inspect", command };
|
|
5185
5266
|
case "audit": {
|
|
5186
5267
|
requireNoExtraArgs(rest, "bun run rig inspect audit");
|
|
5187
|
-
const auditPath =
|
|
5188
|
-
if (!
|
|
5268
|
+
const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
|
|
5269
|
+
if (!existsSync10(auditPath)) {
|
|
5189
5270
|
console.log("No audit log found.");
|
|
5190
5271
|
} else {
|
|
5191
|
-
const lines =
|
|
5272
|
+
const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
5192
5273
|
for (const line of lines) {
|
|
5193
5274
|
console.log(line);
|
|
5194
5275
|
}
|
|
@@ -5817,8 +5898,8 @@ async function executeRemote(context, args) {
|
|
|
5817
5898
|
}
|
|
5818
5899
|
|
|
5819
5900
|
// packages/cli/src/commands/run.ts
|
|
5820
|
-
import { existsSync as
|
|
5821
|
-
import { resolve as
|
|
5901
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
5902
|
+
import { resolve as resolve19 } from "path";
|
|
5822
5903
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
5823
5904
|
import {
|
|
5824
5905
|
listAuthorityRuns as listAuthorityRuns3,
|
|
@@ -5832,6 +5913,7 @@ import {
|
|
|
5832
5913
|
listOpenEpics,
|
|
5833
5914
|
resolveDefaultEpic,
|
|
5834
5915
|
runResume,
|
|
5916
|
+
runRestart,
|
|
5835
5917
|
runStatus,
|
|
5836
5918
|
runStop,
|
|
5837
5919
|
startRun,
|
|
@@ -5940,6 +6022,17 @@ async function attachRunOperatorView(context, input) {
|
|
|
5940
6022
|
}
|
|
5941
6023
|
|
|
5942
6024
|
// packages/cli/src/commands/run.ts
|
|
6025
|
+
function normalizeRemoteRunDetails(payload) {
|
|
6026
|
+
const run = payload.run;
|
|
6027
|
+
if (!run || typeof run !== "object" || Array.isArray(run))
|
|
6028
|
+
return null;
|
|
6029
|
+
return {
|
|
6030
|
+
...run,
|
|
6031
|
+
...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
|
|
6032
|
+
...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
|
|
6033
|
+
...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
|
|
6034
|
+
};
|
|
6035
|
+
}
|
|
5943
6036
|
function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
|
|
5944
6037
|
if (noEpicPrompt) {
|
|
5945
6038
|
return false;
|
|
@@ -6078,7 +6171,7 @@ async function executeRun(context, args) {
|
|
|
6078
6171
|
if (!run.value) {
|
|
6079
6172
|
throw new CliError2("run show requires --run <id>.");
|
|
6080
6173
|
}
|
|
6081
|
-
const record = readAuthorityRun4(context.projectRoot, run.value);
|
|
6174
|
+
const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
|
|
6082
6175
|
if (!record) {
|
|
6083
6176
|
throw new CliError2(`Run not found: ${run.value}`, 2);
|
|
6084
6177
|
}
|
|
@@ -6097,7 +6190,7 @@ async function executeRun(context, args) {
|
|
|
6097
6190
|
if (!run.value) {
|
|
6098
6191
|
throw new CliError2("run timeline requires --run <id>.");
|
|
6099
6192
|
}
|
|
6100
|
-
const timelinePath =
|
|
6193
|
+
const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
|
|
6101
6194
|
const printEvents = () => {
|
|
6102
6195
|
const events2 = readJsonlFile4(timelinePath);
|
|
6103
6196
|
if (context.outputMode === "text") {
|
|
@@ -6109,12 +6202,12 @@ async function executeRun(context, args) {
|
|
|
6109
6202
|
};
|
|
6110
6203
|
const events = printEvents();
|
|
6111
6204
|
if (follow.value && context.outputMode === "text") {
|
|
6112
|
-
let lastLength =
|
|
6205
|
+
let lastLength = existsSync11(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
|
|
6113
6206
|
while (true) {
|
|
6114
6207
|
await Bun.sleep(1000);
|
|
6115
|
-
if (!
|
|
6208
|
+
if (!existsSync11(timelinePath))
|
|
6116
6209
|
continue;
|
|
6117
|
-
const next =
|
|
6210
|
+
const next = readFileSync9(timelinePath, "utf8");
|
|
6118
6211
|
if (next.length <= lastLength)
|
|
6119
6212
|
continue;
|
|
6120
6213
|
const delta = next.slice(lastLength);
|
|
@@ -6259,6 +6352,20 @@ async function executeRun(context, args) {
|
|
|
6259
6352
|
}
|
|
6260
6353
|
return { ok: true, group: "run", command, details: resumed };
|
|
6261
6354
|
}
|
|
6355
|
+
case "restart": {
|
|
6356
|
+
requireNoExtraArgs(rest, "bun run rig run restart");
|
|
6357
|
+
if (context.dryRun) {
|
|
6358
|
+
if (context.outputMode === "text") {
|
|
6359
|
+
console.log("[dry-run] rig run restart");
|
|
6360
|
+
}
|
|
6361
|
+
return { ok: true, group: "run", command };
|
|
6362
|
+
}
|
|
6363
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
6364
|
+
if (context.outputMode === "text") {
|
|
6365
|
+
console.log(`Restarted run: ${restarted.runId}`);
|
|
6366
|
+
}
|
|
6367
|
+
return { ok: true, group: "run", command, details: restarted };
|
|
6368
|
+
}
|
|
6262
6369
|
case "stop": {
|
|
6263
6370
|
const runOption = takeOption(rest, "--run");
|
|
6264
6371
|
const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
|
|
@@ -6316,7 +6423,7 @@ async function executeServer(context, args, options) {
|
|
|
6316
6423
|
const authTokenResult = takeOption(pending, "--auth-token");
|
|
6317
6424
|
pending = authTokenResult.rest;
|
|
6318
6425
|
requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
|
|
6319
|
-
const commandParts = ["
|
|
6426
|
+
const commandParts = ["rig-server", "start"];
|
|
6320
6427
|
if (hostResult.value) {
|
|
6321
6428
|
commandParts.push("--host", hostResult.value);
|
|
6322
6429
|
}
|
|
@@ -6339,7 +6446,7 @@ async function executeServer(context, args, options) {
|
|
|
6339
6446
|
const eventResult = takeOption(pending, "--event");
|
|
6340
6447
|
pending = eventResult.rest;
|
|
6341
6448
|
requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
|
|
6342
|
-
const commandParts = ["
|
|
6449
|
+
const commandParts = ["rig-server", "notify-test"];
|
|
6343
6450
|
if (eventResult.value) {
|
|
6344
6451
|
commandParts.push("--event", eventResult.value);
|
|
6345
6452
|
}
|
|
@@ -6400,10 +6507,10 @@ async function executeServer(context, args, options) {
|
|
|
6400
6507
|
}
|
|
6401
6508
|
|
|
6402
6509
|
// packages/cli/src/commands/task.ts
|
|
6403
|
-
import { readFileSync as
|
|
6510
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6404
6511
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6405
6512
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
6406
|
-
import { resolve as
|
|
6513
|
+
import { resolve as resolve20 } from "path";
|
|
6407
6514
|
import {
|
|
6408
6515
|
taskArtifactDir,
|
|
6409
6516
|
taskArtifacts,
|
|
@@ -6734,7 +6841,7 @@ async function executeTask(context, args, options) {
|
|
|
6734
6841
|
const fileFlag = takeOption(rest.slice(1), "--file");
|
|
6735
6842
|
let content;
|
|
6736
6843
|
if (fileFlag.value) {
|
|
6737
|
-
content =
|
|
6844
|
+
content = readFileSync10(resolve20(context.projectRoot, fileFlag.value), "utf-8");
|
|
6738
6845
|
} else {
|
|
6739
6846
|
content = await readStdin();
|
|
6740
6847
|
}
|
|
@@ -6955,8 +7062,8 @@ async function executeTask(context, args, options) {
|
|
|
6955
7062
|
}
|
|
6956
7063
|
|
|
6957
7064
|
// packages/cli/src/commands/task-run-driver.ts
|
|
6958
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
6959
|
-
import { resolve as
|
|
7065
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7066
|
+
import { resolve as resolve21 } from "path";
|
|
6960
7067
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
6961
7068
|
import { createInterface as createLineInterface } from "readline";
|
|
6962
7069
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
@@ -6990,7 +7097,24 @@ import {
|
|
|
6990
7097
|
commitRunChanges,
|
|
6991
7098
|
runPrAutomation
|
|
6992
7099
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
7100
|
+
function looksLikeGitHubToken(value) {
|
|
7101
|
+
const token = value?.trim();
|
|
7102
|
+
if (!token)
|
|
7103
|
+
return false;
|
|
7104
|
+
return /^(gh[opusr]_|github_pat_)/.test(token);
|
|
7105
|
+
}
|
|
7106
|
+
function githubBridgeEnv(token) {
|
|
7107
|
+
const clean = token?.trim();
|
|
7108
|
+
if (!clean)
|
|
7109
|
+
return {};
|
|
7110
|
+
return {
|
|
7111
|
+
RIG_GITHUB_TOKEN: clean,
|
|
7112
|
+
GITHUB_TOKEN: clean,
|
|
7113
|
+
GH_TOKEN: clean
|
|
7114
|
+
};
|
|
7115
|
+
}
|
|
6993
7116
|
function buildPiRigBridgeEnv(input) {
|
|
7117
|
+
const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
|
|
6994
7118
|
return {
|
|
6995
7119
|
RIG_PROJECT_ROOT: input.projectRoot,
|
|
6996
7120
|
PROJECT_RIG_ROOT: input.projectRoot,
|
|
@@ -7000,7 +7124,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
7000
7124
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
7001
7125
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
7002
7126
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
7003
|
-
...
|
|
7127
|
+
...githubBridgeEnv(githubToken)
|
|
7004
7128
|
};
|
|
7005
7129
|
}
|
|
7006
7130
|
function runGitSync(cwd, args, input) {
|
|
@@ -7022,12 +7146,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
7022
7146
|
return 0;
|
|
7023
7147
|
let copied = 0;
|
|
7024
7148
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
7025
|
-
const sourcePath =
|
|
7026
|
-
const targetPath =
|
|
7149
|
+
const sourcePath = resolve21(sourceRoot, relativePath);
|
|
7150
|
+
const targetPath = resolve21(targetRoot, relativePath);
|
|
7027
7151
|
try {
|
|
7028
7152
|
if (!statSync2(sourcePath).isFile())
|
|
7029
7153
|
continue;
|
|
7030
|
-
mkdirSync7(
|
|
7154
|
+
mkdirSync7(resolve21(targetPath, ".."), { recursive: true });
|
|
7031
7155
|
copyFileSync3(sourcePath, targetPath);
|
|
7032
7156
|
copied += 1;
|
|
7033
7157
|
} catch {}
|
|
@@ -7061,6 +7185,14 @@ function buildTaskRunReviewEnv(config) {
|
|
|
7061
7185
|
...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
|
|
7062
7186
|
};
|
|
7063
7187
|
}
|
|
7188
|
+
function buildDirtyBaselineHandshakeEnv(input) {
|
|
7189
|
+
if (input.baselineMode !== "dirty-snapshot")
|
|
7190
|
+
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
7191
|
+
return {
|
|
7192
|
+
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
7193
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve21(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
7194
|
+
};
|
|
7195
|
+
}
|
|
7064
7196
|
function positiveInt(value, fallback) {
|
|
7065
7197
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
7066
7198
|
}
|
|
@@ -7153,6 +7285,12 @@ function appendPiStageLog(input) {
|
|
|
7153
7285
|
});
|
|
7154
7286
|
emitServerRunEvent({ type: "log", runId: input.runId, title: input.stage });
|
|
7155
7287
|
}
|
|
7288
|
+
async function runCheckedCommand(command, args, cwd, label = "git") {
|
|
7289
|
+
const result = await command(args, { cwd });
|
|
7290
|
+
if (result.exitCode !== 0) {
|
|
7291
|
+
throw new Error(`${label} ${args.join(" ")} failed (${result.exitCode}): ${result.stderr ?? result.stdout ?? ""}`.trim());
|
|
7292
|
+
}
|
|
7293
|
+
}
|
|
7156
7294
|
function createCommandRunner(binary) {
|
|
7157
7295
|
return async (args, options) => {
|
|
7158
7296
|
const child = spawn2(binary, [...args], {
|
|
@@ -7163,9 +7301,9 @@ function createCommandRunner(binary) {
|
|
|
7163
7301
|
const stderrChunks = [];
|
|
7164
7302
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7165
7303
|
child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7166
|
-
return await new Promise((
|
|
7167
|
-
child.once("error", (error) =>
|
|
7168
|
-
child.once("close", (code) =>
|
|
7304
|
+
return await new Promise((resolve22) => {
|
|
7305
|
+
child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
|
|
7306
|
+
child.once("close", (code) => resolve22({
|
|
7169
7307
|
exitCode: code ?? 1,
|
|
7170
7308
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
7171
7309
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -7228,6 +7366,7 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
7228
7366
|
command: gitCommand
|
|
7229
7367
|
});
|
|
7230
7368
|
stage("Commit", commit.committed ? "Committed run workspace changes." : "No workspace changes to commit.", "completed", "tool");
|
|
7369
|
+
await runCheckedCommand(gitCommand, ["push", "--set-upstream", "origin", branch], workspace, "git");
|
|
7231
7370
|
stage("Open PR", `Opening PR from ${branch}.`, "running", "tool");
|
|
7232
7371
|
const pr = await prAutomation({
|
|
7233
7372
|
projectRoot: workspace,
|
|
@@ -7238,7 +7377,6 @@ async function runTaskRunPostValidationLifecycle(input) {
|
|
|
7238
7377
|
sourceTask: input.sourceTask,
|
|
7239
7378
|
uploadedSnapshot: input.uploadedSnapshot,
|
|
7240
7379
|
command: ghCommand,
|
|
7241
|
-
gitCommand,
|
|
7242
7380
|
steerPi,
|
|
7243
7381
|
lifecycle: {
|
|
7244
7382
|
onPrOpened: async ({ prUrl }) => {
|
|
@@ -7361,7 +7499,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
|
|
|
7361
7499
|
return null;
|
|
7362
7500
|
}
|
|
7363
7501
|
for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
|
|
7364
|
-
const summary = readJsonFile3(
|
|
7502
|
+
const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
|
|
7365
7503
|
if (!summary || summary.status !== "fail") {
|
|
7366
7504
|
continue;
|
|
7367
7505
|
}
|
|
@@ -7442,9 +7580,9 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
7442
7580
|
if (!input.taskId || !input.workspaceDir) {
|
|
7443
7581
|
return { accepted: false, reason: null };
|
|
7444
7582
|
}
|
|
7445
|
-
const artifactDir =
|
|
7446
|
-
const reviewStatusPath =
|
|
7447
|
-
const taskResultPath =
|
|
7583
|
+
const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
|
|
7584
|
+
const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
|
|
7585
|
+
const taskResultPath = resolve21(artifactDir, "task-result.json");
|
|
7448
7586
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7449
7587
|
if (reviewStatus !== "APPROVED") {
|
|
7450
7588
|
return { accepted: false, reason: null };
|
|
@@ -7481,12 +7619,12 @@ function resolveTaskRunRetryContext(input) {
|
|
|
7481
7619
|
if (!input.taskId || !input.workspaceDir) {
|
|
7482
7620
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
7483
7621
|
}
|
|
7484
|
-
const artifactDir =
|
|
7485
|
-
const reviewStatePath =
|
|
7486
|
-
const reviewFeedbackPath =
|
|
7487
|
-
const reviewStatusPath =
|
|
7488
|
-
const failedApproachesPath =
|
|
7489
|
-
const validationSummaryPath =
|
|
7622
|
+
const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
|
|
7623
|
+
const reviewStatePath = resolve21(artifactDir, "review-state.json");
|
|
7624
|
+
const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
|
|
7625
|
+
const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
|
|
7626
|
+
const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
7627
|
+
const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
|
|
7490
7628
|
const reviewState = readJsonFile3(reviewStatePath, null);
|
|
7491
7629
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7492
7630
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
@@ -7541,11 +7679,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
7541
7679
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
7542
7680
|
}
|
|
7543
7681
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
7544
|
-
if (!
|
|
7682
|
+
if (!existsSync12(reviewStatusPath)) {
|
|
7545
7683
|
return null;
|
|
7546
7684
|
}
|
|
7547
7685
|
try {
|
|
7548
|
-
const status =
|
|
7686
|
+
const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
7549
7687
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
7550
7688
|
} catch {
|
|
7551
7689
|
return null;
|
|
@@ -7628,8 +7766,11 @@ function stringArrayField(record, key) {
|
|
|
7628
7766
|
}
|
|
7629
7767
|
async function executeRigOwnedTaskRun(context, input) {
|
|
7630
7768
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
7769
|
+
const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
|
|
7770
|
+
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
7771
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
7631
7772
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
7632
|
-
|
|
7773
|
+
let prompt = buildRunPrompt({
|
|
7633
7774
|
projectRoot: context.projectRoot,
|
|
7634
7775
|
taskId: input.taskId,
|
|
7635
7776
|
fallbackTitle: input.title,
|
|
@@ -7683,14 +7824,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7683
7824
|
taskId: runtimeTaskId,
|
|
7684
7825
|
createdAt: startedAt,
|
|
7685
7826
|
runtimeAdapter: input.runtimeAdapter,
|
|
7686
|
-
status: "created"
|
|
7827
|
+
status: resumeMode ? "preparing" : "created"
|
|
7687
7828
|
});
|
|
7688
7829
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7689
7830
|
status: "preparing",
|
|
7690
7831
|
startedAt,
|
|
7691
7832
|
completedAt: null,
|
|
7692
7833
|
errorText: null,
|
|
7693
|
-
artifactRoot: null,
|
|
7834
|
+
artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
|
|
7694
7835
|
runtimeAdapter: input.runtimeAdapter,
|
|
7695
7836
|
runtimeMode: input.runtimeMode,
|
|
7696
7837
|
interactionMode: input.interactionMode,
|
|
@@ -7706,9 +7847,9 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7706
7847
|
detail: input.taskId ?? input.title ?? runtimeTaskId
|
|
7707
7848
|
});
|
|
7708
7849
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7709
|
-
id: `log:${input.runId}:start`,
|
|
7710
|
-
title: "Rig task run started",
|
|
7711
|
-
detail: input.taskId ?? input.title ?? runtimeTaskId,
|
|
7850
|
+
id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
|
|
7851
|
+
title: resumeMode ? "Rig task run resumed" : "Rig task run started",
|
|
7852
|
+
detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
|
|
7712
7853
|
tone: "info",
|
|
7713
7854
|
status: "preparing",
|
|
7714
7855
|
createdAt: startedAt
|
|
@@ -7729,7 +7870,22 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7729
7870
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
7730
7871
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
7731
7872
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
7732
|
-
|
|
7873
|
+
const planningArtifactPath = resolve21("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
7874
|
+
const persistedPlanning = {
|
|
7875
|
+
...planningClassification,
|
|
7876
|
+
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
7877
|
+
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
7878
|
+
classifiedAt: new Date().toISOString()
|
|
7879
|
+
};
|
|
7880
|
+
mkdirSync7(resolve21(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
7881
|
+
writeFileSync6(resolve21(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
7882
|
+
`, "utf8");
|
|
7883
|
+
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
7884
|
+
prompt = `${prompt}
|
|
7885
|
+
|
|
7886
|
+
Rig planning classification:
|
|
7887
|
+
${JSON.stringify(persistedPlanning, null, 2)}
|
|
7888
|
+
${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."}`;
|
|
7733
7889
|
if (input.runtimeAdapter === "pi") {
|
|
7734
7890
|
for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
|
|
7735
7891
|
appendPiStageLog({
|
|
@@ -7771,11 +7927,11 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7771
7927
|
let reviewAction;
|
|
7772
7928
|
let verificationStarted = false;
|
|
7773
7929
|
let reviewStarted = false;
|
|
7774
|
-
let latestRuntimeWorkspace = null;
|
|
7775
|
-
let latestSessionDir = null;
|
|
7776
|
-
let latestLogsDir = null;
|
|
7930
|
+
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
7931
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve21(existingRunRecord.sessionPath, "..") : null;
|
|
7932
|
+
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
7777
7933
|
let latestProviderCommand = null;
|
|
7778
|
-
let latestRuntimeBranch = null;
|
|
7934
|
+
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
7779
7935
|
let snapshotSidecarPromise = null;
|
|
7780
7936
|
let dirtyBaselineApplied = false;
|
|
7781
7937
|
const childEnv = {
|
|
@@ -7793,9 +7949,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7793
7949
|
RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
|
|
7794
7950
|
RIG_SERVER_RUN_ID: input.runId
|
|
7795
7951
|
},
|
|
7796
|
-
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
7952
|
+
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
|
|
7953
|
+
...resumeMode ? {
|
|
7954
|
+
RIG_RUN_RESUME: "1",
|
|
7955
|
+
RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
|
|
7956
|
+
} : {}
|
|
7797
7957
|
};
|
|
7798
7958
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
7959
|
+
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
7799
7960
|
const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
|
|
7800
7961
|
const maxAttempts = automationLimits.maxValidationAttempts;
|
|
7801
7962
|
const promoteToValidating = (detail) => {
|
|
@@ -7850,22 +8011,29 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7850
8011
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7851
8012
|
status: "running",
|
|
7852
8013
|
worktreePath: latestRuntimeWorkspace,
|
|
7853
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
8014
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
7854
8015
|
logRoot: latestLogsDir,
|
|
7855
|
-
sessionPath: latestSessionDir ?
|
|
7856
|
-
sessionLogPath: latestLogsDir ?
|
|
8016
|
+
sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
|
|
8017
|
+
sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
|
|
7857
8018
|
branch: runtimeId
|
|
7858
8019
|
});
|
|
7859
8020
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
7860
8021
|
dirtyBaselineApplied = true;
|
|
7861
8022
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
8023
|
+
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
8024
|
+
if (readyFile) {
|
|
8025
|
+
mkdirSync7(resolve21(readyFile, ".."), { recursive: true });
|
|
8026
|
+
writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
8027
|
+
`, "utf8");
|
|
8028
|
+
}
|
|
7862
8029
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7863
8030
|
id: `log:${input.runId}:dirty-baseline`,
|
|
7864
8031
|
title: "Dirty baseline snapshot",
|
|
7865
8032
|
detail: dirty.detail,
|
|
7866
8033
|
tone: dirty.applied ? "tool" : "info",
|
|
7867
8034
|
status: dirty.applied ? "completed" : "skipped",
|
|
7868
|
-
createdAt: new Date().toISOString()
|
|
8035
|
+
createdAt: new Date().toISOString(),
|
|
8036
|
+
payload: readyFile ? { readyFile } : undefined
|
|
7869
8037
|
});
|
|
7870
8038
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
7871
8039
|
}
|
|
@@ -8091,7 +8259,36 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8091
8259
|
let reviewFailureDetail = null;
|
|
8092
8260
|
const stderrLines = [];
|
|
8093
8261
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
8094
|
-
|
|
8262
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
8263
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8264
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
8265
|
+
title: "Resume continuing from closeout phase",
|
|
8266
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
8267
|
+
tone: "info",
|
|
8268
|
+
status: resumePreviousStatus,
|
|
8269
|
+
createdAt: new Date().toISOString()
|
|
8270
|
+
});
|
|
8271
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
8272
|
+
exit = { code: 0, signal: null };
|
|
8273
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
8274
|
+
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
8275
|
+
taskId: input.taskId ?? runtimeTaskId,
|
|
8276
|
+
workspaceDir: latestRuntimeWorkspace
|
|
8277
|
+
});
|
|
8278
|
+
if (acceptedArtifactState.accepted) {
|
|
8279
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8280
|
+
id: `log:${input.runId}:resume-accepted-artifacts`,
|
|
8281
|
+
title: "Resume found accepted artifacts; continuing closeout",
|
|
8282
|
+
detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
|
|
8283
|
+
tone: "info",
|
|
8284
|
+
status: "validating",
|
|
8285
|
+
createdAt: new Date().toISOString()
|
|
8286
|
+
});
|
|
8287
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
|
|
8288
|
+
exit = { code: 0, signal: null };
|
|
8289
|
+
}
|
|
8290
|
+
}
|
|
8291
|
+
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
8095
8292
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
8096
8293
|
const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
8097
8294
|
cwd: context.projectRoot,
|
|
@@ -8132,7 +8329,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8132
8329
|
let acceptedArtifactObservedAt = null;
|
|
8133
8330
|
let acceptedArtifactPollTimer = null;
|
|
8134
8331
|
let acceptedArtifactKillTimer = null;
|
|
8135
|
-
const attemptExit = await new Promise((
|
|
8332
|
+
const attemptExit = await new Promise((resolve22) => {
|
|
8136
8333
|
let settled = false;
|
|
8137
8334
|
const settle = (result) => {
|
|
8138
8335
|
if (settled)
|
|
@@ -8140,7 +8337,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8140
8337
|
settled = true;
|
|
8141
8338
|
if (acceptedArtifactPollTimer)
|
|
8142
8339
|
clearInterval(acceptedArtifactPollTimer);
|
|
8143
|
-
|
|
8340
|
+
resolve22(result);
|
|
8144
8341
|
};
|
|
8145
8342
|
const pollAcceptedArtifacts = () => {
|
|
8146
8343
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -8337,6 +8534,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8337
8534
|
});
|
|
8338
8535
|
throw new CliError2(terminalFailureDetail, exit.code ?? 1);
|
|
8339
8536
|
}
|
|
8537
|
+
if (planningClassification.planningRequired) {
|
|
8538
|
+
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
8539
|
+
const expectedPlanPath = resolve21(planWorkspace, planningArtifactPath);
|
|
8540
|
+
if (!existsSync12(expectedPlanPath)) {
|
|
8541
|
+
const failedAt = new Date().toISOString();
|
|
8542
|
+
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
8543
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8544
|
+
status: "needs_attention",
|
|
8545
|
+
completedAt: failedAt,
|
|
8546
|
+
errorText: failureDetail
|
|
8547
|
+
});
|
|
8548
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8549
|
+
id: `log:${input.runId}:plan-artifact-missing`,
|
|
8550
|
+
title: "Required plan artifact missing",
|
|
8551
|
+
detail: failureDetail,
|
|
8552
|
+
tone: "error",
|
|
8553
|
+
status: "needs_attention",
|
|
8554
|
+
createdAt: failedAt
|
|
8555
|
+
});
|
|
8556
|
+
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
8557
|
+
throw new CliError2(failureDetail, 1);
|
|
8558
|
+
}
|
|
8559
|
+
}
|
|
8340
8560
|
const runPiPrFeedbackFix = async (message2) => {
|
|
8341
8561
|
appendPiStageLog({
|
|
8342
8562
|
projectRoot: context.projectRoot,
|
|
@@ -8394,9 +8614,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8394
8614
|
});
|
|
8395
8615
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
8396
8616
|
});
|
|
8397
|
-
const exitCode = await new Promise((
|
|
8398
|
-
child.once("error", () =>
|
|
8399
|
-
child.once("close", (code) =>
|
|
8617
|
+
const exitCode = await new Promise((resolve22) => {
|
|
8618
|
+
child.once("error", () => resolve22(1));
|
|
8619
|
+
child.once("close", (code) => resolve22(code ?? 1));
|
|
8400
8620
|
});
|
|
8401
8621
|
if (exitCode !== 0) {
|
|
8402
8622
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
@@ -8515,8 +8735,8 @@ async function executeTest(context, args) {
|
|
|
8515
8735
|
}
|
|
8516
8736
|
|
|
8517
8737
|
// packages/cli/src/commands/setup.ts
|
|
8518
|
-
import { existsSync as
|
|
8519
|
-
import { resolve as
|
|
8738
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
8739
|
+
import { resolve as resolve22 } from "path";
|
|
8520
8740
|
import { createPluginHost } from "@rig/core";
|
|
8521
8741
|
import {
|
|
8522
8742
|
isSupportedBunVersion as isSupportedBunVersion2,
|
|
@@ -8579,9 +8799,9 @@ function runSetupInit(projectRoot) {
|
|
|
8579
8799
|
mkdirSync8(stateDir, { recursive: true });
|
|
8580
8800
|
mkdirSync8(logsDir, { recursive: true });
|
|
8581
8801
|
mkdirSync8(artifactsDir, { recursive: true });
|
|
8582
|
-
const failuresPath =
|
|
8583
|
-
if (!
|
|
8584
|
-
|
|
8802
|
+
const failuresPath = resolve22(stateDir, "failed_approaches.md");
|
|
8803
|
+
if (!existsSync13(failuresPath)) {
|
|
8804
|
+
writeFileSync7(failuresPath, `# Failed Approaches
|
|
8585
8805
|
|
|
8586
8806
|
`, "utf-8");
|
|
8587
8807
|
}
|
|
@@ -8598,18 +8818,18 @@ async function runSetupCheck(projectRoot) {
|
|
|
8598
8818
|
}
|
|
8599
8819
|
async function runSetupPreflight(projectRoot) {
|
|
8600
8820
|
await runSetupCheck(projectRoot);
|
|
8601
|
-
const validationRoot =
|
|
8602
|
-
if (
|
|
8821
|
+
const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
8822
|
+
if (existsSync13(validationRoot)) {
|
|
8603
8823
|
const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
8604
8824
|
for (const validator of validators) {
|
|
8605
|
-
const script =
|
|
8606
|
-
if (
|
|
8825
|
+
const script = resolve22(validationRoot, validator.name, "validate.sh");
|
|
8826
|
+
if (existsSync13(script)) {
|
|
8607
8827
|
console.log(`OK: validator script ${script}`);
|
|
8608
8828
|
}
|
|
8609
8829
|
}
|
|
8610
8830
|
}
|
|
8611
|
-
const hooksRoot =
|
|
8612
|
-
if (
|
|
8831
|
+
const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
|
|
8832
|
+
if (existsSync13(hooksRoot)) {
|
|
8613
8833
|
const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
|
|
8614
8834
|
for (const hook of hooks) {
|
|
8615
8835
|
console.log(`OK: hook ${hook}`);
|