@h-rig/cli 0.0.6-alpha.1 → 0.0.6-alpha.11
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 +481 -226
- 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 +47 -9
- 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 +185 -67
- 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 +125 -16
- package/dist/src/commands/task.js +28 -10
- package/dist/src/commands.js +469 -217
- package/dist/src/index.js +481 -226
- 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,18 +2642,38 @@ 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));
|
|
2646
|
+
}
|
|
2647
|
+
function isRetryableProjectRootSwitchError(error) {
|
|
2648
|
+
if (!(error instanceof Error))
|
|
2649
|
+
return false;
|
|
2650
|
+
const message = error.message.toLowerCase();
|
|
2651
|
+
return message.includes("rig server request failed (401): auth-required") || message.includes("rig server request failed (401): github-token-required") || message.includes("rig server request failed (502)") || message.includes("rig server request failed (503)") || message.includes("bad gateway") || message.includes("fetch failed") || message.includes("econnrefused") || message.includes("connection refused");
|
|
2628
2652
|
}
|
|
2629
2653
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
2630
|
-
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
2631
|
-
method: "POST",
|
|
2632
|
-
headers: { "content-type": "application/json" },
|
|
2633
|
-
body: JSON.stringify({ projectRoot })
|
|
2634
|
-
});
|
|
2635
2654
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
2636
2655
|
const pollMs = options.pollMs ?? 1000;
|
|
2637
2656
|
const deadline = Date.now() + timeoutMs;
|
|
2638
2657
|
let lastError;
|
|
2658
|
+
let switched = null;
|
|
2659
|
+
while (Date.now() < deadline) {
|
|
2660
|
+
try {
|
|
2661
|
+
switched = await requestServerJson(context, "/api/server/project-root", {
|
|
2662
|
+
method: "POST",
|
|
2663
|
+
headers: { "content-type": "application/json" },
|
|
2664
|
+
body: JSON.stringify({ projectRoot })
|
|
2665
|
+
});
|
|
2666
|
+
break;
|
|
2667
|
+
} catch (error) {
|
|
2668
|
+
lastError = error;
|
|
2669
|
+
if (!isRetryableProjectRootSwitchError(error))
|
|
2670
|
+
throw error;
|
|
2671
|
+
await sleep(pollMs);
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
if (!switched) {
|
|
2675
|
+
throw new CliError2(`Rig server did not accept project-root switch to ${projectRoot} before timeout (${lastError instanceof Error ? lastError.message : String(lastError ?? "no response")}).`, 1);
|
|
2676
|
+
}
|
|
2639
2677
|
while (Date.now() < deadline) {
|
|
2640
2678
|
try {
|
|
2641
2679
|
const status = await requestServerJson(context, "/api/server/status");
|
|
@@ -2715,9 +2753,9 @@ async function submitTaskRunViaServer(context, input) {
|
|
|
2715
2753
|
}
|
|
2716
2754
|
|
|
2717
2755
|
// packages/cli/src/commands/_pi-install.ts
|
|
2718
|
-
import { existsSync as
|
|
2756
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
2719
2757
|
import { homedir as homedir3 } from "os";
|
|
2720
|
-
import { resolve as
|
|
2758
|
+
import { resolve as resolve9 } from "path";
|
|
2721
2759
|
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
2722
2760
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
2723
2761
|
export { default } from '@rig/pi-rig';
|
|
@@ -2732,11 +2770,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
2732
2770
|
return { exitCode, stdout, stderr };
|
|
2733
2771
|
}
|
|
2734
2772
|
function resolvePiRigExtensionPath(homeDir) {
|
|
2735
|
-
return
|
|
2773
|
+
return resolve9(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
2736
2774
|
}
|
|
2737
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
2738
|
-
const localPackage =
|
|
2739
|
-
if (exists(
|
|
2775
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync5) {
|
|
2776
|
+
const localPackage = resolve9(projectRoot, "packages", "pi-rig");
|
|
2777
|
+
if (exists(resolve9(localPackage, "package.json")))
|
|
2740
2778
|
return localPackage;
|
|
2741
2779
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
2742
2780
|
}
|
|
@@ -2787,13 +2825,13 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
2787
2825
|
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
2788
2826
|
};
|
|
2789
2827
|
}
|
|
2790
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists =
|
|
2828
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync5) {
|
|
2791
2829
|
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
2792
|
-
const indexPath =
|
|
2830
|
+
const indexPath = resolve9(extensionPath, "index.ts");
|
|
2793
2831
|
if (!exists(indexPath))
|
|
2794
2832
|
return;
|
|
2795
2833
|
try {
|
|
2796
|
-
const content =
|
|
2834
|
+
const content = readFileSync4(indexPath, "utf8");
|
|
2797
2835
|
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
2798
2836
|
rmSync3(extensionPath, { recursive: true, force: true });
|
|
2799
2837
|
}
|
|
@@ -2809,13 +2847,13 @@ async function checkPiRigInstall(input = {}) {
|
|
|
2809
2847
|
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
2810
2848
|
};
|
|
2811
2849
|
}
|
|
2812
|
-
const exists = input.exists ??
|
|
2850
|
+
const exists = input.exists ?? existsSync5;
|
|
2813
2851
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
2814
2852
|
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
2815
2853
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
2816
2854
|
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
2817
2855
|
${piListResult.stderr}`);
|
|
2818
|
-
const legacyBridge = exists(
|
|
2856
|
+
const legacyBridge = exists(resolve9(extensionPath, "index.ts"));
|
|
2819
2857
|
const hasPiRig = listedPiRig;
|
|
2820
2858
|
return {
|
|
2821
2859
|
extensionPath,
|
|
@@ -3153,7 +3191,7 @@ async function executeQueue(context, args) {
|
|
|
3153
3191
|
}
|
|
3154
3192
|
|
|
3155
3193
|
// packages/cli/src/commands/agent.ts
|
|
3156
|
-
import { resolve as
|
|
3194
|
+
import { resolve as resolve11 } from "path";
|
|
3157
3195
|
import {
|
|
3158
3196
|
agentId,
|
|
3159
3197
|
cleanupAgentRuntime,
|
|
@@ -3163,8 +3201,8 @@ import {
|
|
|
3163
3201
|
} from "@rig/runtime/control-plane/runtime/isolation";
|
|
3164
3202
|
|
|
3165
3203
|
// packages/cli/src/commands/_authority-runs.ts
|
|
3166
|
-
import { existsSync as
|
|
3167
|
-
import { resolve as
|
|
3204
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3205
|
+
import { resolve as resolve10 } from "path";
|
|
3168
3206
|
import {
|
|
3169
3207
|
readAuthorityRun,
|
|
3170
3208
|
readJsonlFile as readJsonlFile2,
|
|
@@ -3186,8 +3224,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
3186
3224
|
return "claude-code";
|
|
3187
3225
|
}
|
|
3188
3226
|
function readLatestBeadRecord(projectRoot, taskId) {
|
|
3189
|
-
const issuesPath =
|
|
3190
|
-
if (!
|
|
3227
|
+
const issuesPath = resolve10(resolveControlPlaneMonorepoRoot(projectRoot), ".beads", "issues.jsonl");
|
|
3228
|
+
if (!existsSync6(issuesPath)) {
|
|
3191
3229
|
return null;
|
|
3192
3230
|
}
|
|
3193
3231
|
let latest = null;
|
|
@@ -3222,6 +3260,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3222
3260
|
const runtimeAdapter = normalizeRuntimeAdapter(input.runtimeAdapter ?? existing?.runtimeAdapter ?? "claude-code");
|
|
3223
3261
|
const title = resolveTaskTitleForAuthorityRun(projectRoot, input.taskId) ?? input.taskId;
|
|
3224
3262
|
const next = {
|
|
3263
|
+
...existing ?? {},
|
|
3225
3264
|
runId: input.runId,
|
|
3226
3265
|
projectRoot,
|
|
3227
3266
|
workspaceId: existing?.workspaceId ?? RIG_WORKSPACE_ID,
|
|
@@ -3254,7 +3293,7 @@ function upsertAgentAuthorityRun(projectRoot, input) {
|
|
|
3254
3293
|
} else if ("errorText" in next) {
|
|
3255
3294
|
delete next.errorText;
|
|
3256
3295
|
}
|
|
3257
|
-
writeJsonFile3(
|
|
3296
|
+
writeJsonFile3(resolve10(resolveAuthorityRunDir(projectRoot, input.runId), "run.json"), next);
|
|
3258
3297
|
return next;
|
|
3259
3298
|
}
|
|
3260
3299
|
|
|
@@ -3375,10 +3414,10 @@ async function executeAgent(context, args) {
|
|
|
3375
3414
|
status: "running",
|
|
3376
3415
|
startedAt: createdAt,
|
|
3377
3416
|
worktreePath: runtime.workspaceDir,
|
|
3378
|
-
artifactRoot:
|
|
3417
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3379
3418
|
logRoot: runtime.logsDir,
|
|
3380
|
-
sessionPath:
|
|
3381
|
-
sessionLogPath:
|
|
3419
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3420
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3382
3421
|
pid: process.pid
|
|
3383
3422
|
});
|
|
3384
3423
|
const result = await runInAgentRuntime({
|
|
@@ -3398,10 +3437,10 @@ async function executeAgent(context, args) {
|
|
|
3398
3437
|
startedAt: createdAt,
|
|
3399
3438
|
completedAt: failedAt,
|
|
3400
3439
|
worktreePath: runtime.workspaceDir,
|
|
3401
|
-
artifactRoot:
|
|
3440
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3402
3441
|
logRoot: runtime.logsDir,
|
|
3403
|
-
sessionPath:
|
|
3404
|
-
sessionLogPath:
|
|
3442
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3443
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3405
3444
|
pid: process.pid,
|
|
3406
3445
|
errorText: result.stderr ? result.stderr.trim() : `Agent runtime command failed (${result.exitCode})`
|
|
3407
3446
|
});
|
|
@@ -3418,10 +3457,10 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3418
3457
|
startedAt: createdAt,
|
|
3419
3458
|
completedAt,
|
|
3420
3459
|
worktreePath: runtime.workspaceDir,
|
|
3421
|
-
artifactRoot:
|
|
3460
|
+
artifactRoot: resolve11(runtime.workspaceDir, "artifacts", taskId),
|
|
3422
3461
|
logRoot: runtime.logsDir,
|
|
3423
|
-
sessionPath:
|
|
3424
|
-
sessionLogPath:
|
|
3462
|
+
sessionPath: resolve11(runtime.sessionDir, "session.json"),
|
|
3463
|
+
sessionLogPath: resolve11(runtime.logsDir, "agent-stdout.log"),
|
|
3425
3464
|
pid: process.pid
|
|
3426
3465
|
});
|
|
3427
3466
|
return {
|
|
@@ -3495,7 +3534,7 @@ ${result.stderr.trim()}` : ""}`, result.exitCode);
|
|
|
3495
3534
|
import {
|
|
3496
3535
|
chmodSync,
|
|
3497
3536
|
copyFileSync as copyFileSync2,
|
|
3498
|
-
existsSync as
|
|
3537
|
+
existsSync as existsSync7,
|
|
3499
3538
|
mkdirSync as mkdirSync5,
|
|
3500
3539
|
readdirSync,
|
|
3501
3540
|
readlinkSync,
|
|
@@ -3505,7 +3544,7 @@ import {
|
|
|
3505
3544
|
unlinkSync
|
|
3506
3545
|
} from "fs";
|
|
3507
3546
|
import { homedir as homedir4 } from "os";
|
|
3508
|
-
import { resolve as
|
|
3547
|
+
import { resolve as resolve12 } from "path";
|
|
3509
3548
|
import { buildBinary as buildBinary2 } from "@rig/runtime/control-plane/runtime/isolation";
|
|
3510
3549
|
import {
|
|
3511
3550
|
computeRuntimeImageFingerprint,
|
|
@@ -3524,13 +3563,13 @@ async function runQuietBinaryProbe(binaryPath, args, cwd) {
|
|
|
3524
3563
|
|
|
3525
3564
|
// packages/cli/src/commands/dist.ts
|
|
3526
3565
|
function collectRigValidatorBuildTargets(input) {
|
|
3527
|
-
const validatorsRoot =
|
|
3528
|
-
if (!
|
|
3566
|
+
const validatorsRoot = resolve12(input.hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3567
|
+
if (!existsSync7(validatorsRoot))
|
|
3529
3568
|
return [];
|
|
3530
3569
|
const targets = [];
|
|
3531
3570
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3532
3571
|
for (const category of categories) {
|
|
3533
|
-
const categoryDir =
|
|
3572
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
3534
3573
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3535
3574
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3536
3575
|
continue;
|
|
@@ -3539,7 +3578,7 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3539
3578
|
continue;
|
|
3540
3579
|
targets.push({
|
|
3541
3580
|
source: `packages/runtime/src/control-plane/validators/${category.name}/${entry.name}`,
|
|
3542
|
-
dest:
|
|
3581
|
+
dest: resolve12(input.imageDir, `bin/validators/${category.name}-${check}`),
|
|
3543
3582
|
cwd: input.hostProjectRoot
|
|
3544
3583
|
});
|
|
3545
3584
|
}
|
|
@@ -3548,16 +3587,16 @@ function collectRigValidatorBuildTargets(input) {
|
|
|
3548
3587
|
}
|
|
3549
3588
|
async function findLatestDistBinary(projectRoot) {
|
|
3550
3589
|
const distRoot = resolveControlPlaneHostDistDir(projectRoot);
|
|
3551
|
-
if (!
|
|
3590
|
+
if (!existsSync7(distRoot)) {
|
|
3552
3591
|
return null;
|
|
3553
3592
|
}
|
|
3554
3593
|
const entries = readdirSync(distRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith("rig-")).map((entry) => ({
|
|
3555
3594
|
name: entry.name,
|
|
3556
|
-
mtimeMs: statSync(
|
|
3595
|
+
mtimeMs: statSync(resolve12(distRoot, entry.name)).mtimeMs
|
|
3557
3596
|
})).sort((a, b) => b.mtimeMs - a.mtimeMs || b.name.localeCompare(a.name));
|
|
3558
3597
|
for (const { name } of entries) {
|
|
3559
|
-
const candidate =
|
|
3560
|
-
if (
|
|
3598
|
+
const candidate = resolve12(distRoot, name, "bin", "rig");
|
|
3599
|
+
if (existsSync7(candidate) && await isRunnableRigBinary(candidate, projectRoot)) {
|
|
3561
3600
|
return candidate;
|
|
3562
3601
|
}
|
|
3563
3602
|
}
|
|
@@ -3569,7 +3608,7 @@ async function isRunnableRigBinary(binaryPath, projectRoot) {
|
|
|
3569
3608
|
async function runDistDoctor(projectRoot) {
|
|
3570
3609
|
const bunPath = Bun.which("bun");
|
|
3571
3610
|
const rigPath = Bun.which("rig");
|
|
3572
|
-
const userBinDir =
|
|
3611
|
+
const userBinDir = resolve12(homedir4(), ".local/bin");
|
|
3573
3612
|
const userBinInPath = (process.env.PATH || "").split(":").filter(Boolean).includes(userBinDir);
|
|
3574
3613
|
let rigRunnable = false;
|
|
3575
3614
|
if (rigPath) {
|
|
@@ -3617,15 +3656,15 @@ async function executeDist(context, args) {
|
|
|
3617
3656
|
let source = await findLatestDistBinary(context.projectRoot);
|
|
3618
3657
|
let buildDir = null;
|
|
3619
3658
|
if (!source) {
|
|
3620
|
-
buildDir =
|
|
3659
|
+
buildDir = resolve12(resolveControlPlaneHostDistDir(context.projectRoot), `rig-install-${Date.now()}`);
|
|
3621
3660
|
await context.runCommand(["bun", "run", "packages/cli/bin/build-rig-binaries.ts", "--output-dir", buildDir]);
|
|
3622
|
-
source =
|
|
3661
|
+
source = resolve12(buildDir, "bin", "rig");
|
|
3623
3662
|
}
|
|
3624
|
-
if (!
|
|
3663
|
+
if (!existsSync7(source)) {
|
|
3625
3664
|
throw new CliError2(`Unable to locate rig binary at ${source}.`, 2);
|
|
3626
3665
|
}
|
|
3627
|
-
const installedPath =
|
|
3628
|
-
if (
|
|
3666
|
+
const installedPath = resolve12(installDir, "rig");
|
|
3667
|
+
if (existsSync7(installedPath)) {
|
|
3629
3668
|
unlinkSync(installedPath);
|
|
3630
3669
|
}
|
|
3631
3670
|
copyFileSync2(source, installedPath);
|
|
@@ -3667,22 +3706,22 @@ async function executeDist(context, args) {
|
|
|
3667
3706
|
requireNoExtraArgs(rest, "bun run rig dist rebuild-agent");
|
|
3668
3707
|
const fp = await computeRuntimeImageFingerprint(context.projectRoot);
|
|
3669
3708
|
const currentId = computeRuntimeImageId(fp);
|
|
3670
|
-
const imagesDir =
|
|
3709
|
+
const imagesDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "images");
|
|
3671
3710
|
mkdirSync5(imagesDir, { recursive: true });
|
|
3672
3711
|
let pruned = 0;
|
|
3673
3712
|
for (const entry of readdirSync(imagesDir, { withFileTypes: true })) {
|
|
3674
3713
|
if (entry.isDirectory() && entry.name !== currentId) {
|
|
3675
|
-
rmSync4(
|
|
3714
|
+
rmSync4(resolve12(imagesDir, entry.name), { recursive: true, force: true });
|
|
3676
3715
|
pruned++;
|
|
3677
3716
|
}
|
|
3678
3717
|
}
|
|
3679
3718
|
if (pruned > 0 && context.outputMode === "text") {
|
|
3680
3719
|
console.log(`Pruned ${pruned} stale image(s).`);
|
|
3681
3720
|
}
|
|
3682
|
-
const imageDir =
|
|
3683
|
-
mkdirSync5(
|
|
3684
|
-
mkdirSync5(
|
|
3685
|
-
mkdirSync5(
|
|
3721
|
+
const imageDir = resolve12(imagesDir, currentId);
|
|
3722
|
+
mkdirSync5(resolve12(imageDir, "bin/hooks"), { recursive: true });
|
|
3723
|
+
mkdirSync5(resolve12(imageDir, "bin/plugins"), { recursive: true });
|
|
3724
|
+
mkdirSync5(resolve12(imageDir, "bin/validators"), { recursive: true });
|
|
3686
3725
|
const hookNames = [
|
|
3687
3726
|
"scope-guard",
|
|
3688
3727
|
"import-guard",
|
|
@@ -3696,25 +3735,25 @@ async function executeDist(context, args) {
|
|
|
3696
3735
|
];
|
|
3697
3736
|
const targets = [];
|
|
3698
3737
|
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:
|
|
3738
|
+
targets.push({ source: "packages/runtime/bin/rig-agent.ts", dest: resolve12(imageDir, "bin/rig-agent"), cwd: hostProjectRoot });
|
|
3739
|
+
targets.push({ source: "packages/runtime/bin/rig-agent-dispatch.ts", dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "rig-agent"), cwd: hostProjectRoot });
|
|
3701
3740
|
for (const hookName of hookNames) {
|
|
3702
3741
|
const src = `packages/runtime/src/control-plane/hooks/${hookName}.ts`;
|
|
3703
|
-
targets.push({ source: src, dest:
|
|
3704
|
-
targets.push({ source: src, dest:
|
|
3742
|
+
targets.push({ source: src, dest: resolve12(imageDir, `bin/hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3743
|
+
targets.push({ source: src, dest: resolve12(resolveControlPlaneHostBinDir(context.projectRoot), `hooks/${hookName}`), cwd: hostProjectRoot });
|
|
3705
3744
|
}
|
|
3706
|
-
const pluginsDir =
|
|
3707
|
-
const binPluginsDir =
|
|
3708
|
-
const validatorsRoot =
|
|
3709
|
-
const binValidatorsDir =
|
|
3745
|
+
const pluginsDir = resolve12(context.projectRoot, "rig/plugins");
|
|
3746
|
+
const binPluginsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "plugins");
|
|
3747
|
+
const validatorsRoot = resolve12(hostProjectRoot, "packages/runtime/src/control-plane/validators");
|
|
3748
|
+
const binValidatorsDir = resolve12(resolveControlPlaneHostBinDir(context.projectRoot), "validators");
|
|
3710
3749
|
mkdirSync5(binPluginsDir, { recursive: true });
|
|
3711
3750
|
mkdirSync5(binValidatorsDir, { recursive: true });
|
|
3712
|
-
if (
|
|
3751
|
+
if (existsSync7(pluginsDir)) {
|
|
3713
3752
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3714
3753
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3715
3754
|
if (!m)
|
|
3716
3755
|
continue;
|
|
3717
|
-
targets.push({ source: `rig/plugins/${entry.name}`, dest:
|
|
3756
|
+
targets.push({ source: `rig/plugins/${entry.name}`, dest: resolve12(imageDir, `bin/plugins/${m[1]}`), cwd: context.projectRoot });
|
|
3718
3757
|
}
|
|
3719
3758
|
}
|
|
3720
3759
|
targets.push(...collectRigValidatorBuildTargets({ contextProjectRoot: context.projectRoot, hostProjectRoot, imageDir }));
|
|
@@ -3725,17 +3764,17 @@ async function executeDist(context, args) {
|
|
|
3725
3764
|
const isValidator = dest.includes("/bin/validators/");
|
|
3726
3765
|
await buildBinary2(source, dest, cwd, isValidator ? { AGENT_BUN_PATH: Bun.which("bun") || "bun" } : { AGENT_PROJECT_ROOT: context.projectRoot });
|
|
3727
3766
|
}
|
|
3728
|
-
if (
|
|
3767
|
+
if (existsSync7(pluginsDir)) {
|
|
3729
3768
|
for (const entry of readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
3730
3769
|
const m = entry.name.match(/^(.+)\.plugin\.(ts|js|mjs|cjs)$/);
|
|
3731
3770
|
if (!m)
|
|
3732
3771
|
continue;
|
|
3733
3772
|
const pluginName = m[1];
|
|
3734
|
-
const imageBin =
|
|
3773
|
+
const imageBin = resolve12(imageDir, `bin/plugins/${pluginName}`);
|
|
3735
3774
|
if (!pluginName)
|
|
3736
3775
|
continue;
|
|
3737
|
-
const symlinkPath =
|
|
3738
|
-
if (
|
|
3776
|
+
const symlinkPath = resolve12(binPluginsDir, pluginName);
|
|
3777
|
+
if (existsSync7(imageBin)) {
|
|
3739
3778
|
try {
|
|
3740
3779
|
unlinkSync(symlinkPath);
|
|
3741
3780
|
} catch {}
|
|
@@ -3743,10 +3782,10 @@ async function executeDist(context, args) {
|
|
|
3743
3782
|
}
|
|
3744
3783
|
}
|
|
3745
3784
|
}
|
|
3746
|
-
if (
|
|
3785
|
+
if (existsSync7(validatorsRoot)) {
|
|
3747
3786
|
const categories = readdirSync(validatorsRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
3748
3787
|
for (const category of categories) {
|
|
3749
|
-
const categoryDir =
|
|
3788
|
+
const categoryDir = resolve12(validatorsRoot, category.name);
|
|
3750
3789
|
for (const entry of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
3751
3790
|
if (!entry.isFile() || !entry.name.endsWith(".ts"))
|
|
3752
3791
|
continue;
|
|
@@ -3754,9 +3793,9 @@ async function executeDist(context, args) {
|
|
|
3754
3793
|
if (!check || check === "index" || check === "shared")
|
|
3755
3794
|
continue;
|
|
3756
3795
|
const validatorName = `${category.name}-${check}`;
|
|
3757
|
-
const imageBin =
|
|
3758
|
-
const symlinkPath =
|
|
3759
|
-
if (
|
|
3796
|
+
const imageBin = resolve12(imageDir, `bin/validators/${validatorName}`);
|
|
3797
|
+
const symlinkPath = resolve12(binValidatorsDir, validatorName);
|
|
3798
|
+
if (existsSync7(imageBin)) {
|
|
3760
3799
|
try {
|
|
3761
3800
|
unlinkSync(symlinkPath);
|
|
3762
3801
|
} catch {}
|
|
@@ -3765,18 +3804,18 @@ async function executeDist(context, args) {
|
|
|
3765
3804
|
}
|
|
3766
3805
|
}
|
|
3767
3806
|
}
|
|
3768
|
-
const agentsDir =
|
|
3769
|
-
if (
|
|
3807
|
+
const agentsDir = resolve12(resolveControlPlaneMonorepoRuntimeDir(context.projectRoot), "agents");
|
|
3808
|
+
if (existsSync7(agentsDir)) {
|
|
3770
3809
|
let relinkCount = 0;
|
|
3771
3810
|
for (const agentEntry of readdirSync(agentsDir, { withFileTypes: true })) {
|
|
3772
3811
|
if (!agentEntry.isDirectory())
|
|
3773
3812
|
continue;
|
|
3774
|
-
const agentBinDir =
|
|
3775
|
-
if (!
|
|
3813
|
+
const agentBinDir = resolve12(agentsDir, agentEntry.name, "worktree", ".rig", "bin");
|
|
3814
|
+
if (!existsSync7(agentBinDir))
|
|
3776
3815
|
continue;
|
|
3777
3816
|
const walkDir = (dir) => {
|
|
3778
3817
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
3779
|
-
const fullPath =
|
|
3818
|
+
const fullPath = resolve12(dir, entry.name);
|
|
3780
3819
|
if (entry.isDirectory()) {
|
|
3781
3820
|
walkDir(fullPath);
|
|
3782
3821
|
} else if (entry.isSymbolicLink()) {
|
|
@@ -3810,7 +3849,7 @@ async function executeDist(context, args) {
|
|
|
3810
3849
|
|
|
3811
3850
|
// packages/cli/src/commands/inbox.ts
|
|
3812
3851
|
import { writeFileSync as writeFileSync4 } from "fs";
|
|
3813
|
-
import { resolve as
|
|
3852
|
+
import { resolve as resolve13 } from "path";
|
|
3814
3853
|
import {
|
|
3815
3854
|
listAuthorityRuns,
|
|
3816
3855
|
readJsonlFile as readJsonlFile3,
|
|
@@ -3827,7 +3866,7 @@ async function executeInbox(context, args) {
|
|
|
3827
3866
|
pending = task.rest;
|
|
3828
3867
|
requireNoExtraArgs(pending, "bun run rig inbox approvals [--run <id>] [--task <id>]");
|
|
3829
3868
|
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(
|
|
3869
|
+
const approvals = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "approvals.jsonl")).map((record) => ({
|
|
3831
3870
|
runId: entry.runId,
|
|
3832
3871
|
record
|
|
3833
3872
|
})));
|
|
@@ -3855,7 +3894,7 @@ async function executeInbox(context, args) {
|
|
|
3855
3894
|
if (decision.value !== "approve" && decision.value !== "reject") {
|
|
3856
3895
|
throw new CliError2("decision must be approve or reject.");
|
|
3857
3896
|
}
|
|
3858
|
-
const approvalsPath =
|
|
3897
|
+
const approvalsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "approvals.jsonl");
|
|
3859
3898
|
const approvals = readJsonlFile3(approvalsPath);
|
|
3860
3899
|
const resolvedAt = new Date().toISOString();
|
|
3861
3900
|
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 +3911,7 @@ async function executeInbox(context, args) {
|
|
|
3872
3911
|
pending = task.rest;
|
|
3873
3912
|
requireNoExtraArgs(pending, "bun run rig inbox inputs [--run <id>] [--task <id>]");
|
|
3874
3913
|
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(
|
|
3914
|
+
const requests = runs.flatMap((entry) => readJsonlFile3(resolve13(resolveAuthorityRunDir2(context.projectRoot, entry.runId), "user-input.jsonl")).map((record) => ({
|
|
3876
3915
|
runId: entry.runId,
|
|
3877
3916
|
record
|
|
3878
3917
|
})));
|
|
@@ -3914,7 +3953,7 @@ async function executeInbox(context, args) {
|
|
|
3914
3953
|
const [key, ...restValue] = entry.split("=");
|
|
3915
3954
|
return [key, restValue.join("=")];
|
|
3916
3955
|
}));
|
|
3917
|
-
const requestsPath =
|
|
3956
|
+
const requestsPath = resolve13(resolveAuthorityRunDir2(context.projectRoot, run.value), "user-input.jsonl");
|
|
3918
3957
|
const requests = readJsonlFile3(requestsPath);
|
|
3919
3958
|
const resolvedAt = new Date().toISOString();
|
|
3920
3959
|
const next = requests.map((entry) => entry.requestId === request.value || entry.id === request.value ? { ...entry, status: "resolved", answers: parsedAnswers, respondedAt: resolvedAt, resolvedAt } : entry);
|
|
@@ -3929,14 +3968,14 @@ async function executeInbox(context, args) {
|
|
|
3929
3968
|
}
|
|
3930
3969
|
|
|
3931
3970
|
// packages/cli/src/commands/init.ts
|
|
3932
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
3971
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
3933
3972
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3934
|
-
import { resolve as
|
|
3973
|
+
import { resolve as resolve16 } from "path";
|
|
3935
3974
|
import { buildRigInitConfigSource } from "@rig/core";
|
|
3936
3975
|
|
|
3937
3976
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
3938
3977
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
3939
|
-
import { dirname as dirname2, resolve as
|
|
3978
|
+
import { dirname as dirname2, resolve as resolve14, relative, sep } from "path";
|
|
3940
3979
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
3941
3980
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
3942
3981
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -3958,15 +3997,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
3958
3997
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
3959
3998
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
3960
3999
|
}
|
|
3961
|
-
const resolved =
|
|
4000
|
+
const resolved = resolve14(root, relativePath);
|
|
3962
4001
|
const relativeToRoot = relative(root, resolved);
|
|
3963
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
4002
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve14(relativeToRoot) === resolved) {
|
|
3964
4003
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
3965
4004
|
}
|
|
3966
4005
|
return resolved;
|
|
3967
4006
|
}
|
|
3968
4007
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
3969
|
-
const root =
|
|
4008
|
+
const root = resolve14(projectRoot);
|
|
3970
4009
|
const excludedDirectories = [...new Set([
|
|
3971
4010
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
3972
4011
|
...options.excludedDirectories ?? []
|
|
@@ -3978,7 +4017,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
3978
4017
|
for (const entry of entries) {
|
|
3979
4018
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
3980
4019
|
continue;
|
|
3981
|
-
const fullPath =
|
|
4020
|
+
const fullPath = resolve14(dir, entry.name);
|
|
3982
4021
|
if (entry.isDirectory()) {
|
|
3983
4022
|
await visit(fullPath);
|
|
3984
4023
|
continue;
|
|
@@ -4026,8 +4065,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
4026
4065
|
}
|
|
4027
4066
|
|
|
4028
4067
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
4029
|
-
import { existsSync as
|
|
4030
|
-
import { resolve as
|
|
4068
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
4069
|
+
import { resolve as resolve15 } from "path";
|
|
4031
4070
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
4032
4071
|
function check(id, label, status, detail, remediation) {
|
|
4033
4072
|
return {
|
|
@@ -4067,11 +4106,11 @@ function repoSlugFromConfig(config) {
|
|
|
4067
4106
|
function loadFallbackConfig(projectRoot) {
|
|
4068
4107
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
4069
4108
|
for (const name of candidates) {
|
|
4070
|
-
const path =
|
|
4071
|
-
if (!
|
|
4109
|
+
const path = resolve15(projectRoot, name);
|
|
4110
|
+
if (!existsSync8(path))
|
|
4072
4111
|
continue;
|
|
4073
4112
|
try {
|
|
4074
|
-
const source =
|
|
4113
|
+
const source = readFileSync5(path, "utf8");
|
|
4075
4114
|
if (name.endsWith(".json"))
|
|
4076
4115
|
return JSON.parse(source);
|
|
4077
4116
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -4150,7 +4189,7 @@ async function runRigDoctorChecks(options) {
|
|
|
4150
4189
|
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
4190
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
4152
4191
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
4153
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
4192
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync8(resolve15(projectRoot, name)));
|
|
4154
4193
|
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
4194
|
const taskSourceKind = config?.taskSource?.kind;
|
|
4156
4195
|
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 +4284,10 @@ function countDoctorFailures(checks) {
|
|
|
4245
4284
|
}
|
|
4246
4285
|
|
|
4247
4286
|
// packages/cli/src/commands/init.ts
|
|
4248
|
-
var
|
|
4287
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
4249
4288
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
4250
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
4251
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
4289
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
4290
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
4252
4291
|
};
|
|
4253
4292
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
4254
4293
|
const trimmed = remoteUrl.trim();
|
|
@@ -4268,20 +4307,20 @@ function parseRepoSlug(value) {
|
|
|
4268
4307
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
4269
4308
|
}
|
|
4270
4309
|
function ensureRigPrivateDirs(projectRoot) {
|
|
4271
|
-
const rigDir =
|
|
4272
|
-
mkdirSync6(
|
|
4273
|
-
mkdirSync6(
|
|
4274
|
-
mkdirSync6(
|
|
4275
|
-
mkdirSync6(
|
|
4276
|
-
mkdirSync6(
|
|
4277
|
-
const taskConfigPath =
|
|
4278
|
-
if (!
|
|
4310
|
+
const rigDir = resolve16(projectRoot, ".rig");
|
|
4311
|
+
mkdirSync6(resolve16(rigDir, "state"), { recursive: true });
|
|
4312
|
+
mkdirSync6(resolve16(rigDir, "logs"), { recursive: true });
|
|
4313
|
+
mkdirSync6(resolve16(rigDir, "runs"), { recursive: true });
|
|
4314
|
+
mkdirSync6(resolve16(rigDir, "tmp"), { recursive: true });
|
|
4315
|
+
mkdirSync6(resolve16(projectRoot, "artifacts"), { recursive: true });
|
|
4316
|
+
const taskConfigPath = resolve16(rigDir, "task-config.json");
|
|
4317
|
+
if (!existsSync9(taskConfigPath))
|
|
4279
4318
|
writeFileSync5(taskConfigPath, `{}
|
|
4280
4319
|
`, "utf-8");
|
|
4281
4320
|
}
|
|
4282
4321
|
function ensureGitignoreEntries(projectRoot) {
|
|
4283
|
-
const path =
|
|
4284
|
-
const existing =
|
|
4322
|
+
const path = resolve16(projectRoot, ".gitignore");
|
|
4323
|
+
const existing = existsSync9(path) ? readFileSync6(path, "utf8") : "";
|
|
4285
4324
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
4286
4325
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4287
4326
|
if (missing.length === 0)
|
|
@@ -4294,14 +4333,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
4294
4333
|
`, "utf8");
|
|
4295
4334
|
}
|
|
4296
4335
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
4297
|
-
const path =
|
|
4298
|
-
const existing =
|
|
4336
|
+
const path = resolve16(projectRoot, "package.json");
|
|
4337
|
+
const existing = existsSync9(path) ? JSON.parse(readFileSync6(path, "utf8")) : {};
|
|
4299
4338
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
4300
4339
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
4301
4340
|
devDependencies[name] = spec;
|
|
4302
4341
|
}
|
|
4303
4342
|
const next = {
|
|
4304
|
-
...
|
|
4343
|
+
...existsSync9(path) ? existing : { name: "rig-project", private: true },
|
|
4305
4344
|
devDependencies
|
|
4306
4345
|
};
|
|
4307
4346
|
writeFileSync5(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -4371,15 +4410,71 @@ async function promptSelect(prompts, options) {
|
|
|
4371
4410
|
throw new CliError2("Init cancelled.", 1);
|
|
4372
4411
|
return String(value);
|
|
4373
4412
|
}
|
|
4374
|
-
|
|
4413
|
+
function sleep2(ms) {
|
|
4414
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
4415
|
+
}
|
|
4416
|
+
function positiveIntFromEnv(name, fallback) {
|
|
4417
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
4418
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
4419
|
+
}
|
|
4420
|
+
function apiSessionTokenFrom(payload) {
|
|
4421
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
4422
|
+
return null;
|
|
4423
|
+
const token = payload.apiSessionToken;
|
|
4424
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
4425
|
+
}
|
|
4426
|
+
function cleanPayloadString(value) {
|
|
4427
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
4428
|
+
}
|
|
4429
|
+
function remoteGitHubAuthMetadata(payload) {
|
|
4430
|
+
if (!payload)
|
|
4431
|
+
return {};
|
|
4432
|
+
const userNamespace = payload.userNamespace && typeof payload.userNamespace === "object" && !Array.isArray(payload.userNamespace) ? payload.userNamespace : null;
|
|
4433
|
+
return {
|
|
4434
|
+
...cleanPayloadString(payload.login) ? { login: cleanPayloadString(payload.login) } : {},
|
|
4435
|
+
...cleanPayloadString(payload.userId) ? { userId: cleanPayloadString(payload.userId) } : {},
|
|
4436
|
+
...cleanPayloadString(userNamespace?.key) ? { userNamespaceKey: cleanPayloadString(userNamespace?.key) } : {},
|
|
4437
|
+
...cleanPayloadString(userNamespace?.root) ? { userNamespaceRoot: cleanPayloadString(userNamespace?.root) } : {},
|
|
4438
|
+
...cleanPayloadString(userNamespace?.checkoutBaseDir) ? { checkoutBaseDir: cleanPayloadString(userNamespace?.checkoutBaseDir) } : {},
|
|
4439
|
+
...cleanPayloadString(userNamespace?.snapshotBaseDir) ? { snapshotBaseDir: cleanPayloadString(userNamespace?.snapshotBaseDir) } : {}
|
|
4440
|
+
};
|
|
4441
|
+
}
|
|
4442
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
4443
|
+
writeFileSync5(resolve16(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
4444
|
+
authenticated: true,
|
|
4445
|
+
source: input.source,
|
|
4446
|
+
storedOnServer: true,
|
|
4447
|
+
selectedRepo: input.selectedRepo,
|
|
4448
|
+
...remoteGitHubAuthMetadata(input.authPayload ?? null),
|
|
4449
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
4450
|
+
updatedAt: new Date().toISOString()
|
|
4451
|
+
}, null, 2)}
|
|
4452
|
+
`, "utf8");
|
|
4453
|
+
}
|
|
4454
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
4375
4455
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
4376
4456
|
return null;
|
|
4377
|
-
const
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4457
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
4458
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
4459
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
4460
|
+
const deadline = Date.now() + timeoutMs;
|
|
4461
|
+
let last = null;
|
|
4462
|
+
do {
|
|
4463
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
4464
|
+
method: "POST",
|
|
4465
|
+
headers: { "content-type": "application/json" },
|
|
4466
|
+
body: JSON.stringify({ pollId })
|
|
4467
|
+
}).catch(() => null);
|
|
4468
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
4469
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
4470
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
4471
|
+
return last;
|
|
4472
|
+
}
|
|
4473
|
+
if (timeoutMs <= 0)
|
|
4474
|
+
return last;
|
|
4475
|
+
await sleep2(intervalMs);
|
|
4476
|
+
} while (Date.now() < deadline);
|
|
4477
|
+
return last;
|
|
4383
4478
|
}
|
|
4384
4479
|
async function runControlPlaneInit(context, options) {
|
|
4385
4480
|
const projectRoot = context.projectRoot;
|
|
@@ -4402,9 +4497,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
4402
4497
|
});
|
|
4403
4498
|
ensureRigPrivateDirs(projectRoot);
|
|
4404
4499
|
ensureGitignoreEntries(projectRoot);
|
|
4405
|
-
const configTsPath =
|
|
4406
|
-
const configJsonPath =
|
|
4407
|
-
const configExists =
|
|
4500
|
+
const configTsPath = resolve16(projectRoot, "rig.config.ts");
|
|
4501
|
+
const configJsonPath = resolve16(projectRoot, "rig.config.json");
|
|
4502
|
+
const configExists = existsSync9(configTsPath) || existsSync9(configJsonPath);
|
|
4408
4503
|
if (!options.privateStateOnly) {
|
|
4409
4504
|
if (configExists && !options.repair) {
|
|
4410
4505
|
if (context.outputMode !== "json")
|
|
@@ -4420,7 +4515,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
4420
4515
|
}
|
|
4421
4516
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
4422
4517
|
}
|
|
4423
|
-
writeFileSync5(
|
|
4518
|
+
writeFileSync5(resolve16(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
4424
4519
|
`, "utf8");
|
|
4425
4520
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
4426
4521
|
let uploadedSnapshot = null;
|
|
@@ -4442,10 +4537,15 @@ async function runControlPlaneInit(context, options) {
|
|
|
4442
4537
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
4443
4538
|
if (token) {
|
|
4444
4539
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
4445
|
-
|
|
4540
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4541
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4446
4542
|
if (serverKind === "remote") {
|
|
4447
|
-
|
|
4448
|
-
|
|
4543
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
4544
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
4545
|
+
selectedRepo: repo.slug,
|
|
4546
|
+
apiSessionToken,
|
|
4547
|
+
authPayload: githubAuth
|
|
4548
|
+
});
|
|
4449
4549
|
}
|
|
4450
4550
|
} else if (authMethod === "device") {
|
|
4451
4551
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -4454,9 +4554,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
4454
4554
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
4455
4555
|
});
|
|
4456
4556
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
4457
|
-
|
|
4458
|
-
|
|
4557
|
+
if (context.outputMode !== "json") {
|
|
4558
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
4559
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
4560
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
4561
|
+
}
|
|
4562
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
4563
|
+
if (completed) {
|
|
4564
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
4565
|
+
if (apiSessionToken) {
|
|
4566
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
4567
|
+
if (serverKind === "remote") {
|
|
4568
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken, authPayload: completed });
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4459
4571
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
4572
|
+
}
|
|
4460
4573
|
}
|
|
4461
4574
|
let remoteCheckoutPreparation = null;
|
|
4462
4575
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -4476,6 +4589,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
4476
4589
|
});
|
|
4477
4590
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
4478
4591
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
4592
|
+
if (serverRootSwitch && token) {
|
|
4593
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
4594
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
4595
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
4596
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken, authPayload: githubAuth });
|
|
4597
|
+
}
|
|
4479
4598
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
4480
4599
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
4481
4600
|
remote: true,
|
|
@@ -4585,7 +4704,7 @@ function parseInitOptions(args) {
|
|
|
4585
4704
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
4586
4705
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
4587
4706
|
const projectRoot = context.projectRoot;
|
|
4588
|
-
const existingConfig =
|
|
4707
|
+
const existingConfig = existsSync9(resolve16(projectRoot, "rig.config.ts")) || existsSync9(resolve16(projectRoot, "rig.config.json"));
|
|
4589
4708
|
let repair = false;
|
|
4590
4709
|
let privateStateOnly = false;
|
|
4591
4710
|
if (existingConfig) {
|
|
@@ -4685,7 +4804,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
4685
4804
|
});
|
|
4686
4805
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
4687
4806
|
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")}.` : "";
|
|
4807
|
+
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
4808
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
4690
4809
|
return result;
|
|
4691
4810
|
}
|
|
@@ -4847,8 +4966,8 @@ async function executeDoctor(context, args) {
|
|
|
4847
4966
|
}
|
|
4848
4967
|
|
|
4849
4968
|
// packages/cli/src/commands/_run-driver-helpers.ts
|
|
4850
|
-
import { readFileSync as
|
|
4851
|
-
import { resolve as
|
|
4969
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
4970
|
+
import { resolve as resolve17 } from "path";
|
|
4852
4971
|
import {
|
|
4853
4972
|
appendJsonlRecord as appendJsonlRecord2,
|
|
4854
4973
|
readAuthorityRun as readAuthorityRun2,
|
|
@@ -4868,7 +4987,7 @@ function patchAuthorityRun(projectRoot, runId, patch) {
|
|
|
4868
4987
|
...patch,
|
|
4869
4988
|
updatedAt: new Date().toISOString()
|
|
4870
4989
|
};
|
|
4871
|
-
writeJsonFile4(
|
|
4990
|
+
writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), next);
|
|
4872
4991
|
return next;
|
|
4873
4992
|
}
|
|
4874
4993
|
function touchAuthorityRun(projectRoot, runId) {
|
|
@@ -4876,21 +4995,21 @@ function touchAuthorityRun(projectRoot, runId) {
|
|
|
4876
4995
|
if (!current) {
|
|
4877
4996
|
return;
|
|
4878
4997
|
}
|
|
4879
|
-
writeJsonFile4(
|
|
4998
|
+
writeJsonFile4(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "run.json"), {
|
|
4880
4999
|
...current,
|
|
4881
5000
|
updatedAt: new Date().toISOString()
|
|
4882
5001
|
});
|
|
4883
5002
|
}
|
|
4884
5003
|
function appendRunTimeline(projectRoot, runId, value) {
|
|
4885
|
-
appendJsonlRecord2(
|
|
5004
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), value);
|
|
4886
5005
|
touchAuthorityRun(projectRoot, runId);
|
|
4887
5006
|
}
|
|
4888
5007
|
function appendRunLog(projectRoot, runId, value) {
|
|
4889
|
-
appendJsonlRecord2(
|
|
5008
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "logs.jsonl"), value);
|
|
4890
5009
|
touchAuthorityRun(projectRoot, runId);
|
|
4891
5010
|
}
|
|
4892
5011
|
function appendRunAction(projectRoot, runId, value) {
|
|
4893
|
-
appendJsonlRecord2(
|
|
5012
|
+
appendJsonlRecord2(resolve17(resolveAuthorityRunDir3(projectRoot, runId), "timeline.jsonl"), {
|
|
4894
5013
|
id: value.id,
|
|
4895
5014
|
type: "action",
|
|
4896
5015
|
actionType: value.actionType,
|
|
@@ -4961,7 +5080,7 @@ function buildRunPrompt(input) {
|
|
|
4961
5080
|
})();
|
|
4962
5081
|
const scopeText = (() => {
|
|
4963
5082
|
try {
|
|
4964
|
-
const parsed = JSON.parse(
|
|
5083
|
+
const parsed = JSON.parse(readFileSync7(resolveControlPlaneTaskConfigPath(input.projectRoot), "utf8"));
|
|
4965
5084
|
const entry = parsed[input.taskId] ?? {};
|
|
4966
5085
|
const scope = Array.isArray(entry.scope) ? entry.scope.filter((item) => typeof item === "string") : [];
|
|
4967
5086
|
const validation = Array.isArray(entry.validation) ? entry.validation.filter((item) => typeof item === "string") : [];
|
|
@@ -5074,8 +5193,8 @@ function renderSourceScopeValidation(task, validation) {
|
|
|
5074
5193
|
}
|
|
5075
5194
|
|
|
5076
5195
|
// packages/cli/src/commands/inspect.ts
|
|
5077
|
-
import { existsSync as
|
|
5078
|
-
import { resolve as
|
|
5196
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
5197
|
+
import { resolve as resolve18 } from "path";
|
|
5079
5198
|
import {
|
|
5080
5199
|
listAuthorityRuns as listAuthorityRuns2,
|
|
5081
5200
|
readAuthorityRun as readAuthorityRun3,
|
|
@@ -5096,8 +5215,8 @@ async function executeInspect(context, args) {
|
|
|
5096
5215
|
if (!latestRun) {
|
|
5097
5216
|
throw new CliError2(`No runs found for ${requiredTask}.`);
|
|
5098
5217
|
}
|
|
5099
|
-
const logsPath =
|
|
5100
|
-
if (!
|
|
5218
|
+
const logsPath = resolve18(resolveAuthorityRunDir4(context.projectRoot, latestRun.runId), "logs.jsonl");
|
|
5219
|
+
if (!existsSync10(logsPath)) {
|
|
5101
5220
|
throw new CliError2(`No logs found for run ${latestRun.runId}.`);
|
|
5102
5221
|
}
|
|
5103
5222
|
await context.runCommand(["cat", logsPath]);
|
|
@@ -5107,7 +5226,7 @@ async function executeInspect(context, args) {
|
|
|
5107
5226
|
const { value: task, rest: remaining } = takeOption(rest, "--task");
|
|
5108
5227
|
requireNoExtraArgs(remaining, "bun run rig inspect artifacts --task <beads-id>");
|
|
5109
5228
|
const requiredTask = requireTask(task, "bun run rig inspect artifacts --task <beads-id>");
|
|
5110
|
-
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) =>
|
|
5229
|
+
const artifactRoot = resolveTaskArtifactDirs(context.projectRoot, requiredTask).find((path) => existsSync10(path));
|
|
5111
5230
|
if (!artifactRoot) {
|
|
5112
5231
|
throw new CliError2(`No artifacts found for ${requiredTask}.`);
|
|
5113
5232
|
}
|
|
@@ -5164,10 +5283,10 @@ async function executeInspect(context, args) {
|
|
|
5164
5283
|
case "failures": {
|
|
5165
5284
|
requireNoExtraArgs(rest, "bun run rig inspect failures");
|
|
5166
5285
|
const failed = resolveHarnessPaths2(context.projectRoot).failedApproachesPath;
|
|
5167
|
-
if (!
|
|
5286
|
+
if (!existsSync10(failed)) {
|
|
5168
5287
|
console.log("No failures recorded.");
|
|
5169
5288
|
} else {
|
|
5170
|
-
process.stdout.write(
|
|
5289
|
+
process.stdout.write(readFileSync8(failed, "utf-8"));
|
|
5171
5290
|
}
|
|
5172
5291
|
return { ok: true, group: "inspect", command };
|
|
5173
5292
|
}
|
|
@@ -5184,11 +5303,11 @@ async function executeInspect(context, args) {
|
|
|
5184
5303
|
return { ok: true, group: "inspect", command };
|
|
5185
5304
|
case "audit": {
|
|
5186
5305
|
requireNoExtraArgs(rest, "bun run rig inspect audit");
|
|
5187
|
-
const auditPath =
|
|
5188
|
-
if (!
|
|
5306
|
+
const auditPath = resolve18(resolveHarnessPaths2(context.projectRoot).logsDir, "audit.jsonl");
|
|
5307
|
+
if (!existsSync10(auditPath)) {
|
|
5189
5308
|
console.log("No audit log found.");
|
|
5190
5309
|
} else {
|
|
5191
|
-
const lines =
|
|
5310
|
+
const lines = readFileSync8(auditPath, "utf-8").split(/\r?\n/).filter(Boolean).slice(-20);
|
|
5192
5311
|
for (const line of lines) {
|
|
5193
5312
|
console.log(line);
|
|
5194
5313
|
}
|
|
@@ -5817,8 +5936,8 @@ async function executeRemote(context, args) {
|
|
|
5817
5936
|
}
|
|
5818
5937
|
|
|
5819
5938
|
// packages/cli/src/commands/run.ts
|
|
5820
|
-
import { existsSync as
|
|
5821
|
-
import { resolve as
|
|
5939
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
|
|
5940
|
+
import { resolve as resolve19 } from "path";
|
|
5822
5941
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
5823
5942
|
import {
|
|
5824
5943
|
listAuthorityRuns as listAuthorityRuns3,
|
|
@@ -5832,6 +5951,7 @@ import {
|
|
|
5832
5951
|
listOpenEpics,
|
|
5833
5952
|
resolveDefaultEpic,
|
|
5834
5953
|
runResume,
|
|
5954
|
+
runRestart,
|
|
5835
5955
|
runStatus,
|
|
5836
5956
|
runStop,
|
|
5837
5957
|
startRun,
|
|
@@ -5940,6 +6060,17 @@ async function attachRunOperatorView(context, input) {
|
|
|
5940
6060
|
}
|
|
5941
6061
|
|
|
5942
6062
|
// packages/cli/src/commands/run.ts
|
|
6063
|
+
function normalizeRemoteRunDetails(payload) {
|
|
6064
|
+
const run = payload.run;
|
|
6065
|
+
if (!run || typeof run !== "object" || Array.isArray(run))
|
|
6066
|
+
return null;
|
|
6067
|
+
return {
|
|
6068
|
+
...run,
|
|
6069
|
+
...Array.isArray(payload.timeline) ? { timeline: payload.timeline } : {},
|
|
6070
|
+
...Array.isArray(payload.approvals) ? { approvals: payload.approvals } : {},
|
|
6071
|
+
...Array.isArray(payload.userInputs) ? { userInputs: payload.userInputs } : {}
|
|
6072
|
+
};
|
|
6073
|
+
}
|
|
5943
6074
|
function shouldPromptForEpicSelection(context, command, promptEpic, noEpicPrompt) {
|
|
5944
6075
|
if (noEpicPrompt) {
|
|
5945
6076
|
return false;
|
|
@@ -6078,7 +6209,7 @@ async function executeRun(context, args) {
|
|
|
6078
6209
|
if (!run.value) {
|
|
6079
6210
|
throw new CliError2("run show requires --run <id>.");
|
|
6080
6211
|
}
|
|
6081
|
-
const record = readAuthorityRun4(context.projectRoot, run.value);
|
|
6212
|
+
const record = readAuthorityRun4(context.projectRoot, run.value) ?? normalizeRemoteRunDetails(await getRunDetailsViaServer(context, run.value).catch(() => ({})));
|
|
6082
6213
|
if (!record) {
|
|
6083
6214
|
throw new CliError2(`Run not found: ${run.value}`, 2);
|
|
6084
6215
|
}
|
|
@@ -6097,7 +6228,7 @@ async function executeRun(context, args) {
|
|
|
6097
6228
|
if (!run.value) {
|
|
6098
6229
|
throw new CliError2("run timeline requires --run <id>.");
|
|
6099
6230
|
}
|
|
6100
|
-
const timelinePath =
|
|
6231
|
+
const timelinePath = resolve19(resolveAuthorityRunDir5(context.projectRoot, run.value), "timeline.jsonl");
|
|
6101
6232
|
const printEvents = () => {
|
|
6102
6233
|
const events2 = readJsonlFile4(timelinePath);
|
|
6103
6234
|
if (context.outputMode === "text") {
|
|
@@ -6109,12 +6240,12 @@ async function executeRun(context, args) {
|
|
|
6109
6240
|
};
|
|
6110
6241
|
const events = printEvents();
|
|
6111
6242
|
if (follow.value && context.outputMode === "text") {
|
|
6112
|
-
let lastLength =
|
|
6243
|
+
let lastLength = existsSync11(timelinePath) ? readFileSync9(timelinePath, "utf8").length : 0;
|
|
6113
6244
|
while (true) {
|
|
6114
6245
|
await Bun.sleep(1000);
|
|
6115
|
-
if (!
|
|
6246
|
+
if (!existsSync11(timelinePath))
|
|
6116
6247
|
continue;
|
|
6117
|
-
const next =
|
|
6248
|
+
const next = readFileSync9(timelinePath, "utf8");
|
|
6118
6249
|
if (next.length <= lastLength)
|
|
6119
6250
|
continue;
|
|
6120
6251
|
const delta = next.slice(lastLength);
|
|
@@ -6259,6 +6390,20 @@ async function executeRun(context, args) {
|
|
|
6259
6390
|
}
|
|
6260
6391
|
return { ok: true, group: "run", command, details: resumed };
|
|
6261
6392
|
}
|
|
6393
|
+
case "restart": {
|
|
6394
|
+
requireNoExtraArgs(rest, "bun run rig run restart");
|
|
6395
|
+
if (context.dryRun) {
|
|
6396
|
+
if (context.outputMode === "text") {
|
|
6397
|
+
console.log("[dry-run] rig run restart");
|
|
6398
|
+
}
|
|
6399
|
+
return { ok: true, group: "run", command };
|
|
6400
|
+
}
|
|
6401
|
+
const restarted = await runRestart(context.projectRoot, runtimeContext);
|
|
6402
|
+
if (context.outputMode === "text") {
|
|
6403
|
+
console.log(`Restarted run: ${restarted.runId}`);
|
|
6404
|
+
}
|
|
6405
|
+
return { ok: true, group: "run", command, details: restarted };
|
|
6406
|
+
}
|
|
6262
6407
|
case "stop": {
|
|
6263
6408
|
const runOption = takeOption(rest, "--run");
|
|
6264
6409
|
const positionalRunId = runOption.rest.length > 0 ? runOption.rest[0] : undefined;
|
|
@@ -6316,7 +6461,7 @@ async function executeServer(context, args, options) {
|
|
|
6316
6461
|
const authTokenResult = takeOption(pending, "--auth-token");
|
|
6317
6462
|
pending = authTokenResult.rest;
|
|
6318
6463
|
requireNoExtraArgs(pending, "bun run rig server start [--host <host>] [--port <n>] [--poll-ms <n>] [--auth-token <token>]");
|
|
6319
|
-
const commandParts = ["
|
|
6464
|
+
const commandParts = ["rig-server", "start"];
|
|
6320
6465
|
if (hostResult.value) {
|
|
6321
6466
|
commandParts.push("--host", hostResult.value);
|
|
6322
6467
|
}
|
|
@@ -6339,7 +6484,7 @@ async function executeServer(context, args, options) {
|
|
|
6339
6484
|
const eventResult = takeOption(pending, "--event");
|
|
6340
6485
|
pending = eventResult.rest;
|
|
6341
6486
|
requireNoExtraArgs(pending, "bun run rig server notify-test [--event <type>]");
|
|
6342
|
-
const commandParts = ["
|
|
6487
|
+
const commandParts = ["rig-server", "notify-test"];
|
|
6343
6488
|
if (eventResult.value) {
|
|
6344
6489
|
commandParts.push("--event", eventResult.value);
|
|
6345
6490
|
}
|
|
@@ -6400,10 +6545,10 @@ async function executeServer(context, args, options) {
|
|
|
6400
6545
|
}
|
|
6401
6546
|
|
|
6402
6547
|
// packages/cli/src/commands/task.ts
|
|
6403
|
-
import { readFileSync as
|
|
6548
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
6404
6549
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
6405
6550
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
6406
|
-
import { resolve as
|
|
6551
|
+
import { resolve as resolve20 } from "path";
|
|
6407
6552
|
import {
|
|
6408
6553
|
taskArtifactDir,
|
|
6409
6554
|
taskArtifacts,
|
|
@@ -6734,7 +6879,7 @@ async function executeTask(context, args, options) {
|
|
|
6734
6879
|
const fileFlag = takeOption(rest.slice(1), "--file");
|
|
6735
6880
|
let content;
|
|
6736
6881
|
if (fileFlag.value) {
|
|
6737
|
-
content =
|
|
6882
|
+
content = readFileSync10(resolve20(context.projectRoot, fileFlag.value), "utf-8");
|
|
6738
6883
|
} else {
|
|
6739
6884
|
content = await readStdin();
|
|
6740
6885
|
}
|
|
@@ -6955,8 +7100,8 @@ async function executeTask(context, args, options) {
|
|
|
6955
7100
|
}
|
|
6956
7101
|
|
|
6957
7102
|
// packages/cli/src/commands/task-run-driver.ts
|
|
6958
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
6959
|
-
import { resolve as
|
|
7103
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync11, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
7104
|
+
import { resolve as resolve21 } from "path";
|
|
6960
7105
|
import { spawn as spawn2, spawnSync as spawnSync5 } from "child_process";
|
|
6961
7106
|
import { createInterface as createLineInterface } from "readline";
|
|
6962
7107
|
import { loadConfig as loadConfig2 } from "@rig/core/load-config";
|
|
@@ -6990,7 +7135,24 @@ import {
|
|
|
6990
7135
|
commitRunChanges,
|
|
6991
7136
|
runPrAutomation
|
|
6992
7137
|
} from "@rig/runtime/control-plane/native/pr-automation";
|
|
7138
|
+
function looksLikeGitHubToken(value) {
|
|
7139
|
+
const token = value?.trim();
|
|
7140
|
+
if (!token)
|
|
7141
|
+
return false;
|
|
7142
|
+
return /^(gh[opusr]_|github_pat_)/.test(token);
|
|
7143
|
+
}
|
|
7144
|
+
function githubBridgeEnv(token) {
|
|
7145
|
+
const clean = token?.trim();
|
|
7146
|
+
if (!clean)
|
|
7147
|
+
return {};
|
|
7148
|
+
return {
|
|
7149
|
+
RIG_GITHUB_TOKEN: clean,
|
|
7150
|
+
GITHUB_TOKEN: clean,
|
|
7151
|
+
GH_TOKEN: clean
|
|
7152
|
+
};
|
|
7153
|
+
}
|
|
6993
7154
|
function buildPiRigBridgeEnv(input) {
|
|
7155
|
+
const githubToken = input.githubToken?.trim() || (looksLikeGitHubToken(input.authToken) ? input.authToken.trim() : "");
|
|
6994
7156
|
return {
|
|
6995
7157
|
RIG_PROJECT_ROOT: input.projectRoot,
|
|
6996
7158
|
PROJECT_RIG_ROOT: input.projectRoot,
|
|
@@ -7000,7 +7162,7 @@ function buildPiRigBridgeEnv(input) {
|
|
|
7000
7162
|
RIG_RUNTIME_ADAPTER: "pi",
|
|
7001
7163
|
...input.serverUrl ? { RIG_SERVER_URL: input.serverUrl } : {},
|
|
7002
7164
|
...input.authToken ? { RIG_AUTH_TOKEN: input.authToken } : {},
|
|
7003
|
-
...
|
|
7165
|
+
...githubBridgeEnv(githubToken)
|
|
7004
7166
|
};
|
|
7005
7167
|
}
|
|
7006
7168
|
function runGitSync(cwd, args, input) {
|
|
@@ -7022,12 +7184,12 @@ function copyUntrackedDirtyFiles(sourceRoot, targetRoot) {
|
|
|
7022
7184
|
return 0;
|
|
7023
7185
|
let copied = 0;
|
|
7024
7186
|
for (const relativePath of listed.stdout.split("\x00").filter(Boolean)) {
|
|
7025
|
-
const sourcePath =
|
|
7026
|
-
const targetPath =
|
|
7187
|
+
const sourcePath = resolve21(sourceRoot, relativePath);
|
|
7188
|
+
const targetPath = resolve21(targetRoot, relativePath);
|
|
7027
7189
|
try {
|
|
7028
7190
|
if (!statSync2(sourcePath).isFile())
|
|
7029
7191
|
continue;
|
|
7030
|
-
mkdirSync7(
|
|
7192
|
+
mkdirSync7(resolve21(targetPath, ".."), { recursive: true });
|
|
7031
7193
|
copyFileSync3(sourcePath, targetPath);
|
|
7032
7194
|
copied += 1;
|
|
7033
7195
|
} catch {}
|
|
@@ -7061,6 +7223,14 @@ function buildTaskRunReviewEnv(config) {
|
|
|
7061
7223
|
...review?.provider ? { AI_REVIEW_PROVIDER: review.provider } : {}
|
|
7062
7224
|
};
|
|
7063
7225
|
}
|
|
7226
|
+
function buildDirtyBaselineHandshakeEnv(input) {
|
|
7227
|
+
if (input.baselineMode !== "dirty-snapshot")
|
|
7228
|
+
return { RIG_BASELINE_MODE: input.baselineMode ?? "head" };
|
|
7229
|
+
return {
|
|
7230
|
+
RIG_BASELINE_MODE: "dirty-snapshot",
|
|
7231
|
+
RIG_DIRTY_BASELINE_READY_FILE: resolve21(input.projectRoot, ".rig", "runs", input.runId, "dirty-baseline.ready.json")
|
|
7232
|
+
};
|
|
7233
|
+
}
|
|
7064
7234
|
function positiveInt(value, fallback) {
|
|
7065
7235
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
7066
7236
|
}
|
|
@@ -7169,9 +7339,9 @@ function createCommandRunner(binary) {
|
|
|
7169
7339
|
const stderrChunks = [];
|
|
7170
7340
|
child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk))));
|
|
7171
7341
|
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) =>
|
|
7342
|
+
return await new Promise((resolve22) => {
|
|
7343
|
+
child.once("error", (error) => resolve22({ exitCode: 1, stderr: error.message }));
|
|
7344
|
+
child.once("close", (code) => resolve22({
|
|
7175
7345
|
exitCode: code ?? 1,
|
|
7176
7346
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
7177
7347
|
stderr: Buffer.concat(stderrChunks).toString("utf8")
|
|
@@ -7367,7 +7537,7 @@ function summarizeValidationFailure(projectRoot, taskId2) {
|
|
|
7367
7537
|
return null;
|
|
7368
7538
|
}
|
|
7369
7539
|
for (const artifactDir of resolveTaskArtifactDirs2(projectRoot, taskId2)) {
|
|
7370
|
-
const summary = readJsonFile3(
|
|
7540
|
+
const summary = readJsonFile3(resolve21(artifactDir, "validation-summary.json"), null);
|
|
7371
7541
|
if (!summary || summary.status !== "fail") {
|
|
7372
7542
|
continue;
|
|
7373
7543
|
}
|
|
@@ -7448,9 +7618,9 @@ function readTaskRunAcceptedArtifactState(input) {
|
|
|
7448
7618
|
if (!input.taskId || !input.workspaceDir) {
|
|
7449
7619
|
return { accepted: false, reason: null };
|
|
7450
7620
|
}
|
|
7451
|
-
const artifactDir =
|
|
7452
|
-
const reviewStatusPath =
|
|
7453
|
-
const taskResultPath =
|
|
7621
|
+
const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
|
|
7622
|
+
const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
|
|
7623
|
+
const taskResultPath = resolve21(artifactDir, "task-result.json");
|
|
7454
7624
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7455
7625
|
if (reviewStatus !== "APPROVED") {
|
|
7456
7626
|
return { accepted: false, reason: null };
|
|
@@ -7487,12 +7657,12 @@ function resolveTaskRunRetryContext(input) {
|
|
|
7487
7657
|
if (!input.taskId || !input.workspaceDir) {
|
|
7488
7658
|
return { shouldRetry: false, failureDetail: null, nextPrompt: null };
|
|
7489
7659
|
}
|
|
7490
|
-
const artifactDir =
|
|
7491
|
-
const reviewStatePath =
|
|
7492
|
-
const reviewFeedbackPath =
|
|
7493
|
-
const reviewStatusPath =
|
|
7494
|
-
const failedApproachesPath =
|
|
7495
|
-
const validationSummaryPath =
|
|
7660
|
+
const artifactDir = resolve21(input.workspaceDir, "artifacts", input.taskId);
|
|
7661
|
+
const reviewStatePath = resolve21(artifactDir, "review-state.json");
|
|
7662
|
+
const reviewFeedbackPath = resolve21(artifactDir, "review-feedback.md");
|
|
7663
|
+
const reviewStatusPath = resolve21(artifactDir, "review-status.txt");
|
|
7664
|
+
const failedApproachesPath = resolve21(input.workspaceDir, ".rig", "state", "failed_approaches.md");
|
|
7665
|
+
const validationSummaryPath = resolve21(artifactDir, "validation-summary.json");
|
|
7496
7666
|
const reviewState = readJsonFile3(reviewStatePath, null);
|
|
7497
7667
|
const reviewStatus = readTaskRunReviewStatus(reviewStatusPath);
|
|
7498
7668
|
const reviewRejected = isTaskRunReviewRejected(reviewState);
|
|
@@ -7547,11 +7717,11 @@ function summarizeTaskRunReviewFailure(reviewState) {
|
|
|
7547
7717
|
return "Completion verification rejected the task. Read review-feedback.md for required fixes.";
|
|
7548
7718
|
}
|
|
7549
7719
|
function readTaskRunReviewStatus(reviewStatusPath) {
|
|
7550
|
-
if (!
|
|
7720
|
+
if (!existsSync12(reviewStatusPath)) {
|
|
7551
7721
|
return null;
|
|
7552
7722
|
}
|
|
7553
7723
|
try {
|
|
7554
|
-
const status =
|
|
7724
|
+
const status = readFileSync11(reviewStatusPath, "utf8").trim().toUpperCase();
|
|
7555
7725
|
return status === "APPROVED" || status === "REJECTED" ? status : null;
|
|
7556
7726
|
} catch {
|
|
7557
7727
|
return null;
|
|
@@ -7634,8 +7804,11 @@ function stringArrayField(record, key) {
|
|
|
7634
7804
|
}
|
|
7635
7805
|
async function executeRigOwnedTaskRun(context, input) {
|
|
7636
7806
|
const runtimeTaskId = input.taskId?.trim() || `adhoc-${input.runId}`;
|
|
7807
|
+
const existingRunRecord = readAuthorityRun5(context.projectRoot, input.runId);
|
|
7808
|
+
const resumeMode = process.env.RIG_RUN_RESUME === "1";
|
|
7809
|
+
const resumePreviousStatus = String(existingRunRecord?.status ?? "").toLowerCase();
|
|
7637
7810
|
const sourceTask = readRunSourceTaskContract(context.projectRoot, input.runId, input.taskId);
|
|
7638
|
-
|
|
7811
|
+
let prompt = buildRunPrompt({
|
|
7639
7812
|
projectRoot: context.projectRoot,
|
|
7640
7813
|
taskId: input.taskId,
|
|
7641
7814
|
fallbackTitle: input.title,
|
|
@@ -7689,14 +7862,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7689
7862
|
taskId: runtimeTaskId,
|
|
7690
7863
|
createdAt: startedAt,
|
|
7691
7864
|
runtimeAdapter: input.runtimeAdapter,
|
|
7692
|
-
status: "created"
|
|
7865
|
+
status: resumeMode ? "preparing" : "created"
|
|
7693
7866
|
});
|
|
7694
7867
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7695
7868
|
status: "preparing",
|
|
7696
7869
|
startedAt,
|
|
7697
7870
|
completedAt: null,
|
|
7698
7871
|
errorText: null,
|
|
7699
|
-
artifactRoot: null,
|
|
7872
|
+
artifactRoot: resumeMode ? existingRunRecord?.artifactRoot ?? null : null,
|
|
7700
7873
|
runtimeAdapter: input.runtimeAdapter,
|
|
7701
7874
|
runtimeMode: input.runtimeMode,
|
|
7702
7875
|
interactionMode: input.interactionMode,
|
|
@@ -7712,9 +7885,9 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7712
7885
|
detail: input.taskId ?? input.title ?? runtimeTaskId
|
|
7713
7886
|
});
|
|
7714
7887
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7715
|
-
id: `log:${input.runId}:start`,
|
|
7716
|
-
title: "Rig task run started",
|
|
7717
|
-
detail: input.taskId ?? input.title ?? runtimeTaskId,
|
|
7888
|
+
id: `log:${input.runId}:${resumeMode ? "resume" : "start"}`,
|
|
7889
|
+
title: resumeMode ? "Rig task run resumed" : "Rig task run started",
|
|
7890
|
+
detail: resumeMode ? `Continuing the same run lifecycle for ${input.taskId ?? input.title ?? runtimeTaskId}.` : input.taskId ?? input.title ?? runtimeTaskId,
|
|
7718
7891
|
tone: "info",
|
|
7719
7892
|
status: "preparing",
|
|
7720
7893
|
createdAt: startedAt
|
|
@@ -7735,7 +7908,22 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7735
7908
|
const loadedAutomationConfig = await loadTaskRunAutomationConfig(context.projectRoot);
|
|
7736
7909
|
const automationConfig = input.prMode ? { ...loadedAutomationConfig ?? {}, pr: { ...loadedAutomationConfig?.pr ?? {}, mode: input.prMode } } : loadedAutomationConfig;
|
|
7737
7910
|
const planningClassification = classifyPlanningNeed({ config: automationConfig, sourceTask });
|
|
7738
|
-
|
|
7911
|
+
const planningArtifactPath = resolve21("artifacts", runtimeTaskId, "implementation-plan.md");
|
|
7912
|
+
const persistedPlanning = {
|
|
7913
|
+
...planningClassification,
|
|
7914
|
+
classifier: input.runtimeAdapter === "pi" ? "pi-rig-structured-policy" : "rig-structured-policy",
|
|
7915
|
+
artifactPath: planningClassification.planningRequired ? planningArtifactPath : null,
|
|
7916
|
+
classifiedAt: new Date().toISOString()
|
|
7917
|
+
};
|
|
7918
|
+
mkdirSync7(resolve21(context.projectRoot, ".rig", "runs", input.runId), { recursive: true });
|
|
7919
|
+
writeFileSync6(resolve21(context.projectRoot, ".rig", "runs", input.runId, "planning-classification.json"), `${JSON.stringify(persistedPlanning, null, 2)}
|
|
7920
|
+
`, "utf8");
|
|
7921
|
+
patchAuthorityRun(context.projectRoot, input.runId, { planning: persistedPlanning });
|
|
7922
|
+
prompt = `${prompt}
|
|
7923
|
+
|
|
7924
|
+
Rig planning classification:
|
|
7925
|
+
${JSON.stringify(persistedPlanning, null, 2)}
|
|
7926
|
+
${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
7927
|
if (input.runtimeAdapter === "pi") {
|
|
7740
7928
|
for (const stage of ["Connect", "GitHub/task sync", "Prepare workspace", "Launch Pi", "Plan", "Implement"]) {
|
|
7741
7929
|
appendPiStageLog({
|
|
@@ -7777,11 +7965,11 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7777
7965
|
let reviewAction;
|
|
7778
7966
|
let verificationStarted = false;
|
|
7779
7967
|
let reviewStarted = false;
|
|
7780
|
-
let latestRuntimeWorkspace = null;
|
|
7781
|
-
let latestSessionDir = null;
|
|
7782
|
-
let latestLogsDir = null;
|
|
7968
|
+
let latestRuntimeWorkspace = resumeMode && typeof existingRunRecord?.worktreePath === "string" ? existingRunRecord.worktreePath : null;
|
|
7969
|
+
let latestSessionDir = resumeMode && typeof existingRunRecord?.sessionPath === "string" ? resolve21(existingRunRecord.sessionPath, "..") : null;
|
|
7970
|
+
let latestLogsDir = resumeMode && typeof existingRunRecord?.logRoot === "string" ? existingRunRecord.logRoot : null;
|
|
7783
7971
|
let latestProviderCommand = null;
|
|
7784
|
-
let latestRuntimeBranch = null;
|
|
7972
|
+
let latestRuntimeBranch = resumeMode && typeof existingRunRecord?.branch === "string" ? existingRunRecord.branch : null;
|
|
7785
7973
|
let snapshotSidecarPromise = null;
|
|
7786
7974
|
let dirtyBaselineApplied = false;
|
|
7787
7975
|
const childEnv = {
|
|
@@ -7799,9 +7987,14 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7799
7987
|
RIG_RUNTIME_ADAPTER: input.runtimeAdapter,
|
|
7800
7988
|
RIG_SERVER_RUN_ID: input.runId
|
|
7801
7989
|
},
|
|
7802
|
-
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {}
|
|
7990
|
+
...sourceTask ? { RIG_SOURCE_TASK_JSON: JSON.stringify(sourceTask) } : {},
|
|
7991
|
+
...resumeMode ? {
|
|
7992
|
+
RIG_RUN_RESUME: "1",
|
|
7993
|
+
RIG_RUNTIME_ARTIFACT_CLEANUP: "preserve"
|
|
7994
|
+
} : {}
|
|
7803
7995
|
};
|
|
7804
7996
|
Object.assign(childEnv, buildTaskRunReviewEnv(automationConfig));
|
|
7997
|
+
Object.assign(childEnv, buildDirtyBaselineHandshakeEnv({ projectRoot: context.projectRoot, runId: input.runId, baselineMode: input.baselineMode }));
|
|
7805
7998
|
const automationLimits = resolveTaskRunAutomationLimits(automationConfig);
|
|
7806
7999
|
const maxAttempts = automationLimits.maxValidationAttempts;
|
|
7807
8000
|
const promoteToValidating = (detail) => {
|
|
@@ -7856,22 +8049,29 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
7856
8049
|
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
7857
8050
|
status: "running",
|
|
7858
8051
|
worktreePath: latestRuntimeWorkspace,
|
|
7859
|
-
artifactRoot: latestRuntimeWorkspace && input.taskId ?
|
|
8052
|
+
artifactRoot: latestRuntimeWorkspace && input.taskId ? resolve21(latestRuntimeWorkspace, "artifacts", input.taskId) : null,
|
|
7860
8053
|
logRoot: latestLogsDir,
|
|
7861
|
-
sessionPath: latestSessionDir ?
|
|
7862
|
-
sessionLogPath: latestLogsDir ?
|
|
8054
|
+
sessionPath: latestSessionDir ? resolve21(latestSessionDir, "session.json") : null,
|
|
8055
|
+
sessionLogPath: latestLogsDir ? resolve21(latestLogsDir, "agent-stdout.log") : null,
|
|
7863
8056
|
branch: runtimeId
|
|
7864
8057
|
});
|
|
7865
8058
|
if (!dirtyBaselineApplied && input.baselineMode === "dirty-snapshot" && latestRuntimeWorkspace) {
|
|
7866
8059
|
dirtyBaselineApplied = true;
|
|
7867
8060
|
const dirty = applyDirtyBaselineSnapshot({ sourceRoot: context.projectRoot, targetRoot: latestRuntimeWorkspace });
|
|
8061
|
+
const readyFile = childEnv.RIG_DIRTY_BASELINE_READY_FILE;
|
|
8062
|
+
if (readyFile) {
|
|
8063
|
+
mkdirSync7(resolve21(readyFile, ".."), { recursive: true });
|
|
8064
|
+
writeFileSync6(readyFile, `${JSON.stringify({ ...dirty, workspaceDir: latestRuntimeWorkspace, appliedAt: new Date().toISOString() }, null, 2)}
|
|
8065
|
+
`, "utf8");
|
|
8066
|
+
}
|
|
7868
8067
|
appendRunLog(context.projectRoot, input.runId, {
|
|
7869
8068
|
id: `log:${input.runId}:dirty-baseline`,
|
|
7870
8069
|
title: "Dirty baseline snapshot",
|
|
7871
8070
|
detail: dirty.detail,
|
|
7872
8071
|
tone: dirty.applied ? "tool" : "info",
|
|
7873
8072
|
status: dirty.applied ? "completed" : "skipped",
|
|
7874
|
-
createdAt: new Date().toISOString()
|
|
8073
|
+
createdAt: new Date().toISOString(),
|
|
8074
|
+
payload: readyFile ? { readyFile } : undefined
|
|
7875
8075
|
});
|
|
7876
8076
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Dirty baseline snapshot" });
|
|
7877
8077
|
}
|
|
@@ -8097,7 +8297,36 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8097
8297
|
let reviewFailureDetail = null;
|
|
8098
8298
|
const stderrLines = [];
|
|
8099
8299
|
const piAcceptedArtifactExitGraceMs = resolvePiAcceptedArtifactExitGraceMs(childEnv);
|
|
8100
|
-
|
|
8300
|
+
if (resumeMode && ["validating", "reviewing"].includes(resumePreviousStatus)) {
|
|
8301
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8302
|
+
id: `log:${input.runId}:resume-closeout-phase`,
|
|
8303
|
+
title: "Resume continuing from closeout phase",
|
|
8304
|
+
detail: `Previous run status was ${resumePreviousStatus}; skipping agent relaunch and continuing validation/PR/merge closeout for the same run.`,
|
|
8305
|
+
tone: "info",
|
|
8306
|
+
status: resumePreviousStatus,
|
|
8307
|
+
createdAt: new Date().toISOString()
|
|
8308
|
+
});
|
|
8309
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume continuing from closeout phase" });
|
|
8310
|
+
exit = { code: 0, signal: null };
|
|
8311
|
+
} else if (resumeMode && latestRuntimeWorkspace) {
|
|
8312
|
+
const acceptedArtifactState = readTaskRunAcceptedArtifactState({
|
|
8313
|
+
taskId: input.taskId ?? runtimeTaskId,
|
|
8314
|
+
workspaceDir: latestRuntimeWorkspace
|
|
8315
|
+
});
|
|
8316
|
+
if (acceptedArtifactState.accepted) {
|
|
8317
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8318
|
+
id: `log:${input.runId}:resume-accepted-artifacts`,
|
|
8319
|
+
title: "Resume found accepted artifacts; continuing closeout",
|
|
8320
|
+
detail: acceptedArtifactState.reason ?? "Accepted task artifacts are present from the previous run process.",
|
|
8321
|
+
tone: "info",
|
|
8322
|
+
status: "validating",
|
|
8323
|
+
createdAt: new Date().toISOString()
|
|
8324
|
+
});
|
|
8325
|
+
emitServerRunEvent({ type: "log", runId: input.runId, title: "Resume found accepted artifacts; continuing closeout" });
|
|
8326
|
+
exit = { code: 0, signal: null };
|
|
8327
|
+
}
|
|
8328
|
+
}
|
|
8329
|
+
for (let attempt = 1;!exit && attempt <= maxAttempts; attempt += 1) {
|
|
8101
8330
|
const attemptHostAgentCommand = buildHostAgentCommand(currentPrompt);
|
|
8102
8331
|
const child = spawn2(attemptHostAgentCommand[0], attemptHostAgentCommand.slice(1), {
|
|
8103
8332
|
cwd: context.projectRoot,
|
|
@@ -8138,7 +8367,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8138
8367
|
let acceptedArtifactObservedAt = null;
|
|
8139
8368
|
let acceptedArtifactPollTimer = null;
|
|
8140
8369
|
let acceptedArtifactKillTimer = null;
|
|
8141
|
-
const attemptExit = await new Promise((
|
|
8370
|
+
const attemptExit = await new Promise((resolve22) => {
|
|
8142
8371
|
let settled = false;
|
|
8143
8372
|
const settle = (result) => {
|
|
8144
8373
|
if (settled)
|
|
@@ -8146,7 +8375,7 @@ async function executeRigOwnedTaskRun(context, input) {
|
|
|
8146
8375
|
settled = true;
|
|
8147
8376
|
if (acceptedArtifactPollTimer)
|
|
8148
8377
|
clearInterval(acceptedArtifactPollTimer);
|
|
8149
|
-
|
|
8378
|
+
resolve22(result);
|
|
8150
8379
|
};
|
|
8151
8380
|
const pollAcceptedArtifacts = () => {
|
|
8152
8381
|
const artifactState = readTaskRunAcceptedArtifactState({
|
|
@@ -8343,6 +8572,29 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8343
8572
|
});
|
|
8344
8573
|
throw new CliError2(terminalFailureDetail, exit.code ?? 1);
|
|
8345
8574
|
}
|
|
8575
|
+
if (planningClassification.planningRequired) {
|
|
8576
|
+
const planWorkspace = latestRuntimeWorkspace ?? context.projectRoot;
|
|
8577
|
+
const expectedPlanPath = resolve21(planWorkspace, planningArtifactPath);
|
|
8578
|
+
if (!existsSync12(expectedPlanPath)) {
|
|
8579
|
+
const failedAt = new Date().toISOString();
|
|
8580
|
+
const failureDetail = `Planning was required (${planningClassification.reason}) but ${planningArtifactPath} was not written before implementation completed.`;
|
|
8581
|
+
patchAuthorityRun(context.projectRoot, input.runId, {
|
|
8582
|
+
status: "needs_attention",
|
|
8583
|
+
completedAt: failedAt,
|
|
8584
|
+
errorText: failureDetail
|
|
8585
|
+
});
|
|
8586
|
+
appendRunLog(context.projectRoot, input.runId, {
|
|
8587
|
+
id: `log:${input.runId}:plan-artifact-missing`,
|
|
8588
|
+
title: "Required plan artifact missing",
|
|
8589
|
+
detail: failureDetail,
|
|
8590
|
+
tone: "error",
|
|
8591
|
+
status: "needs_attention",
|
|
8592
|
+
createdAt: failedAt
|
|
8593
|
+
});
|
|
8594
|
+
emitServerRunEvent({ type: "failed", runId: input.runId, error: failureDetail });
|
|
8595
|
+
throw new CliError2(failureDetail, 1);
|
|
8596
|
+
}
|
|
8597
|
+
}
|
|
8346
8598
|
const runPiPrFeedbackFix = async (message2) => {
|
|
8347
8599
|
appendPiStageLog({
|
|
8348
8600
|
projectRoot: context.projectRoot,
|
|
@@ -8400,9 +8652,9 @@ Failed to update task source for ${input.taskId ?? runtimeTaskId} to failed: ${e
|
|
|
8400
8652
|
});
|
|
8401
8653
|
emitServerRunEvent({ type: "log", runId: input.runId, title: "Pi PR feedback fix stderr" });
|
|
8402
8654
|
});
|
|
8403
|
-
const exitCode = await new Promise((
|
|
8404
|
-
child.once("error", () =>
|
|
8405
|
-
child.once("close", (code) =>
|
|
8655
|
+
const exitCode = await new Promise((resolve22) => {
|
|
8656
|
+
child.once("error", () => resolve22(1));
|
|
8657
|
+
child.once("close", (code) => resolve22(code ?? 1));
|
|
8406
8658
|
});
|
|
8407
8659
|
if (exitCode !== 0) {
|
|
8408
8660
|
throw new Error(`Pi PR feedback fix failed with exit code ${exitCode}.`);
|
|
@@ -8521,8 +8773,8 @@ async function executeTest(context, args) {
|
|
|
8521
8773
|
}
|
|
8522
8774
|
|
|
8523
8775
|
// packages/cli/src/commands/setup.ts
|
|
8524
|
-
import { existsSync as
|
|
8525
|
-
import { resolve as
|
|
8776
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
8777
|
+
import { resolve as resolve22 } from "path";
|
|
8526
8778
|
import { createPluginHost } from "@rig/core";
|
|
8527
8779
|
import {
|
|
8528
8780
|
isSupportedBunVersion as isSupportedBunVersion2,
|
|
@@ -8585,9 +8837,9 @@ function runSetupInit(projectRoot) {
|
|
|
8585
8837
|
mkdirSync8(stateDir, { recursive: true });
|
|
8586
8838
|
mkdirSync8(logsDir, { recursive: true });
|
|
8587
8839
|
mkdirSync8(artifactsDir, { recursive: true });
|
|
8588
|
-
const failuresPath =
|
|
8589
|
-
if (!
|
|
8590
|
-
|
|
8840
|
+
const failuresPath = resolve22(stateDir, "failed_approaches.md");
|
|
8841
|
+
if (!existsSync13(failuresPath)) {
|
|
8842
|
+
writeFileSync7(failuresPath, `# Failed Approaches
|
|
8591
8843
|
|
|
8592
8844
|
`, "utf-8");
|
|
8593
8845
|
}
|
|
@@ -8604,18 +8856,18 @@ async function runSetupCheck(projectRoot) {
|
|
|
8604
8856
|
}
|
|
8605
8857
|
async function runSetupPreflight(projectRoot) {
|
|
8606
8858
|
await runSetupCheck(projectRoot);
|
|
8607
|
-
const validationRoot =
|
|
8608
|
-
if (
|
|
8859
|
+
const validationRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "validation");
|
|
8860
|
+
if (existsSync13(validationRoot)) {
|
|
8609
8861
|
const validators = readdirSync2(validationRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory());
|
|
8610
8862
|
for (const validator of validators) {
|
|
8611
|
-
const script =
|
|
8612
|
-
if (
|
|
8863
|
+
const script = resolve22(validationRoot, validator.name, "validate.sh");
|
|
8864
|
+
if (existsSync13(script)) {
|
|
8613
8865
|
console.log(`OK: validator script ${script}`);
|
|
8614
8866
|
}
|
|
8615
8867
|
}
|
|
8616
8868
|
}
|
|
8617
|
-
const hooksRoot =
|
|
8618
|
-
if (
|
|
8869
|
+
const hooksRoot = resolve22(resolveControlPlaneDefinitionRoot(projectRoot), "hooks");
|
|
8870
|
+
if (existsSync13(hooksRoot)) {
|
|
8619
8871
|
const hooks = readdirSync2(hooksRoot).filter((name) => name.endsWith(".sh"));
|
|
8620
8872
|
for (const hook of hooks) {
|
|
8621
8873
|
console.log(`OK: hook ${hook}`);
|