@h-rig/cli 0.0.6-alpha.1 → 0.0.6-alpha.3
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 +356 -205
- package/dist/src/commands/_doctor-checks.js +31 -13
- package/dist/src/commands/_operator-view.js +20 -2
- package/dist/src/commands/_preflight.js +25 -7
- package/dist/src/commands/_server-client.js +22 -4
- package/dist/src/commands/_snapshot-upload.js +26 -8
- package/dist/src/commands/doctor.js +31 -13
- package/dist/src/commands/github.js +21 -3
- package/dist/src/commands/init.js +142 -62
- package/dist/src/commands/run.js +26 -8
- package/dist/src/commands/server.js +20 -2
- package/dist/src/commands/setup.js +36 -18
- package/dist/src/commands/task-run-driver.js +77 -5
- package/dist/src/commands/task.js +28 -10
- package/dist/src/commands.js +349 -198
- package/dist/src/index.js +356 -205
- package/package.json +4 -4
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
4
|
// packages/cli/src/commands/init.ts
|
|
5
|
-
import { appendFileSync, existsSync as
|
|
5
|
+
import { appendFileSync, existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
6
6
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
7
|
-
import { resolve as
|
|
7
|
+
import { resolve as resolve6 } from "path";
|
|
8
8
|
|
|
9
9
|
// packages/cli/src/runner.ts
|
|
10
10
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
@@ -154,6 +154,8 @@ function resolveSelectedConnection(projectRoot, options = {}) {
|
|
|
154
154
|
|
|
155
155
|
// packages/cli/src/commands/_server-client.ts
|
|
156
156
|
import { spawnSync } from "child_process";
|
|
157
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
158
|
+
import { resolve as resolve2 } from "path";
|
|
157
159
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
158
160
|
var cachedGitHubBearerToken;
|
|
159
161
|
function cleanToken(value) {
|
|
@@ -163,9 +165,25 @@ function cleanToken(value) {
|
|
|
163
165
|
function setGitHubBearerTokenForCurrentProcess(token) {
|
|
164
166
|
cachedGitHubBearerToken = cleanToken(token ?? undefined);
|
|
165
167
|
}
|
|
166
|
-
function
|
|
168
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
169
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
170
|
+
if (!existsSync2(path))
|
|
171
|
+
return null;
|
|
172
|
+
try {
|
|
173
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
174
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
167
180
|
if (cachedGitHubBearerToken !== undefined)
|
|
168
181
|
return cachedGitHubBearerToken;
|
|
182
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
183
|
+
if (privateSession) {
|
|
184
|
+
cachedGitHubBearerToken = privateSession;
|
|
185
|
+
return cachedGitHubBearerToken;
|
|
186
|
+
}
|
|
169
187
|
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
170
188
|
if (envToken) {
|
|
171
189
|
cachedGitHubBearerToken = envToken;
|
|
@@ -185,7 +203,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
185
203
|
if (selected?.connection.kind === "remote") {
|
|
186
204
|
return {
|
|
187
205
|
baseUrl: selected.connection.baseUrl,
|
|
188
|
-
authToken: readGitHubBearerTokenForRemote(),
|
|
206
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
189
207
|
connectionKind: "remote"
|
|
190
208
|
};
|
|
191
209
|
}
|
|
@@ -249,7 +267,7 @@ async function postGitHubTokenViaServer(context, token, options = {}) {
|
|
|
249
267
|
const payload = await requestServerJson(context, "/api/github/auth/token", {
|
|
250
268
|
method: "POST",
|
|
251
269
|
headers: { "content-type": "application/json" },
|
|
252
|
-
body: JSON.stringify({ token, selectedRepo: options.selectedRepo })
|
|
270
|
+
body: JSON.stringify({ token, selectedRepo: options.selectedRepo, projectRoot: options.projectRoot })
|
|
253
271
|
});
|
|
254
272
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
255
273
|
}
|
|
@@ -270,7 +288,7 @@ async function registerProjectViaServer(context, input) {
|
|
|
270
288
|
return payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
271
289
|
}
|
|
272
290
|
function sleep(ms) {
|
|
273
|
-
return new Promise((
|
|
291
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
274
292
|
}
|
|
275
293
|
async function switchServerProjectRootViaServer(context, projectRoot, options = {}) {
|
|
276
294
|
const switched = await requestServerJson(context, "/api/server/project-root", {
|
|
@@ -301,9 +319,9 @@ async function switchServerProjectRootViaServer(context, projectRoot, options =
|
|
|
301
319
|
}
|
|
302
320
|
|
|
303
321
|
// packages/cli/src/commands/_pi-install.ts
|
|
304
|
-
import { existsSync as
|
|
322
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, rmSync } from "fs";
|
|
305
323
|
import { homedir as homedir2 } from "os";
|
|
306
|
-
import { resolve as
|
|
324
|
+
import { resolve as resolve3 } from "path";
|
|
307
325
|
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
308
326
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
309
327
|
export { default } from '@rig/pi-rig';
|
|
@@ -318,11 +336,11 @@ async function defaultCommandRunner(command, options = {}) {
|
|
|
318
336
|
return { exitCode, stdout, stderr };
|
|
319
337
|
}
|
|
320
338
|
function resolvePiRigExtensionPath(homeDir) {
|
|
321
|
-
return
|
|
339
|
+
return resolve3(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
322
340
|
}
|
|
323
|
-
function resolvePiRigPackageSource(projectRoot, exists =
|
|
324
|
-
const localPackage =
|
|
325
|
-
if (exists(
|
|
341
|
+
function resolvePiRigPackageSource(projectRoot, exists = existsSync3) {
|
|
342
|
+
const localPackage = resolve3(projectRoot, "packages", "pi-rig");
|
|
343
|
+
if (exists(resolve3(localPackage, "package.json")))
|
|
326
344
|
return localPackage;
|
|
327
345
|
return `npm:${PI_RIG_PACKAGE_NAME}`;
|
|
328
346
|
}
|
|
@@ -373,13 +391,13 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
373
391
|
...next.exitCode === 0 ? {} : { error: (next.stderr || next.stdout).trim() || "pi --version failed after install" }
|
|
374
392
|
};
|
|
375
393
|
}
|
|
376
|
-
function removeManagedLegacyPiRigBridge(homeDir, exists =
|
|
394
|
+
function removeManagedLegacyPiRigBridge(homeDir, exists = existsSync3) {
|
|
377
395
|
const extensionPath = resolvePiRigExtensionPath(homeDir);
|
|
378
|
-
const indexPath =
|
|
396
|
+
const indexPath = resolve3(extensionPath, "index.ts");
|
|
379
397
|
if (!exists(indexPath))
|
|
380
398
|
return;
|
|
381
399
|
try {
|
|
382
|
-
const content =
|
|
400
|
+
const content = readFileSync3(indexPath, "utf8");
|
|
383
401
|
if (content === LEGACY_PI_RIG_MARKER || content.includes("Managed by Rig. Source package: @rig/pi-rig")) {
|
|
384
402
|
rmSync(extensionPath, { recursive: true, force: true });
|
|
385
403
|
}
|
|
@@ -395,13 +413,13 @@ async function checkPiRigInstall(input = {}) {
|
|
|
395
413
|
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
396
414
|
};
|
|
397
415
|
}
|
|
398
|
-
const exists = input.exists ??
|
|
416
|
+
const exists = input.exists ?? existsSync3;
|
|
399
417
|
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
400
418
|
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
401
419
|
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
402
420
|
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
403
421
|
${piListResult.stderr}`);
|
|
404
|
-
const legacyBridge = exists(
|
|
422
|
+
const legacyBridge = exists(resolve3(extensionPath, "index.ts"));
|
|
405
423
|
const hasPiRig = listedPiRig;
|
|
406
424
|
return {
|
|
407
425
|
extensionPath,
|
|
@@ -478,7 +496,7 @@ async function buildPiSetupChecks(input = {}) {
|
|
|
478
496
|
|
|
479
497
|
// packages/cli/src/commands/_snapshot-upload.ts
|
|
480
498
|
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
481
|
-
import { dirname as dirname2, resolve as
|
|
499
|
+
import { dirname as dirname2, resolve as resolve4, relative, sep } from "path";
|
|
482
500
|
var SNAPSHOT_ARCHIVE_VERSION = 1;
|
|
483
501
|
var SNAPSHOT_ARCHIVE_CONTENT_TYPE = "application/vnd.rig.snapshot+json";
|
|
484
502
|
var DEFAULT_EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -500,15 +518,15 @@ function assertManifestPath(root, relativePath) {
|
|
|
500
518
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\x00")) {
|
|
501
519
|
throw new Error(`Invalid snapshot path: ${relativePath}`);
|
|
502
520
|
}
|
|
503
|
-
const resolved =
|
|
521
|
+
const resolved = resolve4(root, relativePath);
|
|
504
522
|
const relativeToRoot = relative(root, resolved);
|
|
505
|
-
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." ||
|
|
523
|
+
if (relativeToRoot.startsWith("..") || relativeToRoot === ".." || resolve4(relativeToRoot) === resolved) {
|
|
506
524
|
throw new Error(`Snapshot path escapes project root: ${relativePath}`);
|
|
507
525
|
}
|
|
508
526
|
return resolved;
|
|
509
527
|
}
|
|
510
528
|
async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
511
|
-
const root =
|
|
529
|
+
const root = resolve4(projectRoot);
|
|
512
530
|
const excludedDirectories = [...new Set([
|
|
513
531
|
...DEFAULT_EXCLUDED_DIRECTORIES,
|
|
514
532
|
...options.excludedDirectories ?? []
|
|
@@ -520,7 +538,7 @@ async function buildSnapshotUploadManifest(projectRoot, options = {}) {
|
|
|
520
538
|
for (const entry of entries) {
|
|
521
539
|
if (entry.isDirectory() && excludedSet.has(entry.name))
|
|
522
540
|
continue;
|
|
523
|
-
const fullPath =
|
|
541
|
+
const fullPath = resolve4(dir, entry.name);
|
|
524
542
|
if (entry.isDirectory()) {
|
|
525
543
|
await visit(fullPath);
|
|
526
544
|
continue;
|
|
@@ -568,8 +586,8 @@ async function uploadSnapshotArchiveViaServer(context, input) {
|
|
|
568
586
|
}
|
|
569
587
|
|
|
570
588
|
// packages/cli/src/commands/_doctor-checks.ts
|
|
571
|
-
import { existsSync as
|
|
572
|
-
import { resolve as
|
|
589
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
590
|
+
import { resolve as resolve5 } from "path";
|
|
573
591
|
import { isSupportedBunVersion, MIN_SUPPORTED_BUN_VERSION } from "@rig/runtime/control-plane/setup-version";
|
|
574
592
|
|
|
575
593
|
// packages/cli/src/commands/_parsers.ts
|
|
@@ -621,11 +639,11 @@ function repoSlugFromConfig(config) {
|
|
|
621
639
|
function loadFallbackConfig(projectRoot) {
|
|
622
640
|
const candidates = ["rig.config.ts", "rig.config.mts", "rig.config.json"];
|
|
623
641
|
for (const name of candidates) {
|
|
624
|
-
const path =
|
|
625
|
-
if (!
|
|
642
|
+
const path = resolve5(projectRoot, name);
|
|
643
|
+
if (!existsSync4(path))
|
|
626
644
|
continue;
|
|
627
645
|
try {
|
|
628
|
-
const source =
|
|
646
|
+
const source = readFileSync4(path, "utf8");
|
|
629
647
|
if (name.endsWith(".json"))
|
|
630
648
|
return JSON.parse(source);
|
|
631
649
|
const owner = source.match(/owner\s*:\s*["']([^"']+)["']/)?.[1];
|
|
@@ -704,7 +722,7 @@ async function runRigDoctorChecks(options) {
|
|
|
704
722
|
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`)."));
|
|
705
723
|
const loadedConfig = await loadConfig(projectRoot).catch(() => null);
|
|
706
724
|
const config = loadedConfig ?? loadFallbackConfig(projectRoot);
|
|
707
|
-
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) =>
|
|
725
|
+
const hasConfigFile = ["rig.config.ts", "rig.config.mts", "rig.config.json"].some((name) => existsSync4(resolve5(projectRoot, name)));
|
|
708
726
|
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."));
|
|
709
727
|
const taskSourceKind = config?.taskSource?.kind;
|
|
710
728
|
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."));
|
|
@@ -790,10 +808,10 @@ function countDoctorFailures(checks) {
|
|
|
790
808
|
}
|
|
791
809
|
|
|
792
810
|
// packages/cli/src/commands/init.ts
|
|
793
|
-
var
|
|
811
|
+
var RIG_CONFIG_PACKAGE_DIST_TAG = "latest";
|
|
794
812
|
var RIG_CONFIG_DEV_DEPENDENCIES = {
|
|
795
|
-
"@rig/core": `npm:@h-rig/core@${
|
|
796
|
-
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${
|
|
813
|
+
"@rig/core": `npm:@h-rig/core@${RIG_CONFIG_PACKAGE_DIST_TAG}`,
|
|
814
|
+
"@rig/standard-plugin": `npm:@h-rig/standard-plugin@${RIG_CONFIG_PACKAGE_DIST_TAG}`
|
|
797
815
|
};
|
|
798
816
|
function parseRepoSlugFromRemote(remoteUrl) {
|
|
799
817
|
const trimmed = remoteUrl.trim();
|
|
@@ -813,20 +831,20 @@ function parseRepoSlug(value) {
|
|
|
813
831
|
return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
|
|
814
832
|
}
|
|
815
833
|
function ensureRigPrivateDirs(projectRoot) {
|
|
816
|
-
const rigDir =
|
|
817
|
-
mkdirSync2(
|
|
818
|
-
mkdirSync2(
|
|
819
|
-
mkdirSync2(
|
|
820
|
-
mkdirSync2(
|
|
821
|
-
mkdirSync2(
|
|
822
|
-
const taskConfigPath =
|
|
823
|
-
if (!
|
|
834
|
+
const rigDir = resolve6(projectRoot, ".rig");
|
|
835
|
+
mkdirSync2(resolve6(rigDir, "state"), { recursive: true });
|
|
836
|
+
mkdirSync2(resolve6(rigDir, "logs"), { recursive: true });
|
|
837
|
+
mkdirSync2(resolve6(rigDir, "runs"), { recursive: true });
|
|
838
|
+
mkdirSync2(resolve6(rigDir, "tmp"), { recursive: true });
|
|
839
|
+
mkdirSync2(resolve6(projectRoot, "artifacts"), { recursive: true });
|
|
840
|
+
const taskConfigPath = resolve6(rigDir, "task-config.json");
|
|
841
|
+
if (!existsSync5(taskConfigPath))
|
|
824
842
|
writeFileSync2(taskConfigPath, `{}
|
|
825
843
|
`, "utf-8");
|
|
826
844
|
}
|
|
827
845
|
function ensureGitignoreEntries(projectRoot) {
|
|
828
|
-
const path =
|
|
829
|
-
const existing =
|
|
846
|
+
const path = resolve6(projectRoot, ".gitignore");
|
|
847
|
+
const existing = existsSync5(path) ? readFileSync5(path, "utf8") : "";
|
|
830
848
|
const entries = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"];
|
|
831
849
|
const missing = entries.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
832
850
|
if (missing.length === 0)
|
|
@@ -839,14 +857,14 @@ function ensureGitignoreEntries(projectRoot) {
|
|
|
839
857
|
`, "utf8");
|
|
840
858
|
}
|
|
841
859
|
function ensureRigConfigPackageDependencies(projectRoot) {
|
|
842
|
-
const path =
|
|
843
|
-
const existing =
|
|
860
|
+
const path = resolve6(projectRoot, "package.json");
|
|
861
|
+
const existing = existsSync5(path) ? JSON.parse(readFileSync5(path, "utf8")) : {};
|
|
844
862
|
const devDependencies = existing.devDependencies && typeof existing.devDependencies === "object" && !Array.isArray(existing.devDependencies) ? { ...existing.devDependencies } : {};
|
|
845
863
|
for (const [name, spec] of Object.entries(RIG_CONFIG_DEV_DEPENDENCIES)) {
|
|
846
864
|
devDependencies[name] = spec;
|
|
847
865
|
}
|
|
848
866
|
const next = {
|
|
849
|
-
...
|
|
867
|
+
...existsSync5(path) ? existing : { name: "rig-project", private: true },
|
|
850
868
|
devDependencies
|
|
851
869
|
};
|
|
852
870
|
writeFileSync2(path, `${JSON.stringify(next, null, 2)}
|
|
@@ -916,15 +934,54 @@ async function promptSelect(prompts, options) {
|
|
|
916
934
|
throw new CliError2("Init cancelled.", 1);
|
|
917
935
|
return String(value);
|
|
918
936
|
}
|
|
919
|
-
|
|
937
|
+
function sleep2(ms) {
|
|
938
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
939
|
+
}
|
|
940
|
+
function positiveIntFromEnv(name, fallback) {
|
|
941
|
+
const value = Number.parseInt(process.env[name] ?? "", 10);
|
|
942
|
+
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
943
|
+
}
|
|
944
|
+
function apiSessionTokenFrom(payload) {
|
|
945
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
946
|
+
return null;
|
|
947
|
+
const token = payload.apiSessionToken;
|
|
948
|
+
return typeof token === "string" && token.trim() ? token.trim() : null;
|
|
949
|
+
}
|
|
950
|
+
function writeRemoteGitHubAuthState(projectRoot, input) {
|
|
951
|
+
writeFileSync2(resolve6(projectRoot, ".rig", "state", "github-auth.json"), `${JSON.stringify({
|
|
952
|
+
authenticated: true,
|
|
953
|
+
source: input.source,
|
|
954
|
+
storedOnServer: true,
|
|
955
|
+
selectedRepo: input.selectedRepo,
|
|
956
|
+
...input.apiSessionToken ? { apiSessionToken: input.apiSessionToken } : {},
|
|
957
|
+
updatedAt: new Date().toISOString()
|
|
958
|
+
}, null, 2)}
|
|
959
|
+
`, "utf8");
|
|
960
|
+
}
|
|
961
|
+
async function pollDeviceAuthUntilComplete(context, pollId, firstPayload) {
|
|
920
962
|
if (typeof pollId !== "string" || !pollId.trim())
|
|
921
963
|
return null;
|
|
922
|
-
const
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
964
|
+
const intervalSeconds = typeof firstPayload.interval === "number" && Number.isFinite(firstPayload.interval) && firstPayload.interval > 0 ? firstPayload.interval : 5;
|
|
965
|
+
const timeoutMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_TIMEOUT_MS", 300000);
|
|
966
|
+
const intervalMs = positiveIntFromEnv("RIG_DEVICE_AUTH_POLL_INTERVAL_MS", Math.max(1000, intervalSeconds * 1000));
|
|
967
|
+
const deadline = Date.now() + timeoutMs;
|
|
968
|
+
let last = null;
|
|
969
|
+
do {
|
|
970
|
+
const payload = await requestServerJson(context, "/api/github/auth/device/poll", {
|
|
971
|
+
method: "POST",
|
|
972
|
+
headers: { "content-type": "application/json" },
|
|
973
|
+
body: JSON.stringify({ pollId })
|
|
974
|
+
}).catch(() => null);
|
|
975
|
+
last = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
976
|
+
const status = typeof last?.status === "string" ? last.status : null;
|
|
977
|
+
if (status === "signed-in" || status === "expired" || status === "cancelled" || status === "failed") {
|
|
978
|
+
return last;
|
|
979
|
+
}
|
|
980
|
+
if (timeoutMs <= 0)
|
|
981
|
+
return last;
|
|
982
|
+
await sleep2(intervalMs);
|
|
983
|
+
} while (Date.now() < deadline);
|
|
984
|
+
return last;
|
|
928
985
|
}
|
|
929
986
|
async function runControlPlaneInit(context, options) {
|
|
930
987
|
const projectRoot = context.projectRoot;
|
|
@@ -947,9 +1004,9 @@ async function runControlPlaneInit(context, options) {
|
|
|
947
1004
|
});
|
|
948
1005
|
ensureRigPrivateDirs(projectRoot);
|
|
949
1006
|
ensureGitignoreEntries(projectRoot);
|
|
950
|
-
const configTsPath =
|
|
951
|
-
const configJsonPath =
|
|
952
|
-
const configExists =
|
|
1007
|
+
const configTsPath = resolve6(projectRoot, "rig.config.ts");
|
|
1008
|
+
const configJsonPath = resolve6(projectRoot, "rig.config.json");
|
|
1009
|
+
const configExists = existsSync5(configTsPath) || existsSync5(configJsonPath);
|
|
953
1010
|
if (!options.privateStateOnly) {
|
|
954
1011
|
if (configExists && !options.repair) {
|
|
955
1012
|
if (context.outputMode !== "json")
|
|
@@ -965,7 +1022,7 @@ async function runControlPlaneInit(context, options) {
|
|
|
965
1022
|
}
|
|
966
1023
|
ensureRigConfigPackageDependencies(projectRoot);
|
|
967
1024
|
}
|
|
968
|
-
writeFileSync2(
|
|
1025
|
+
writeFileSync2(resolve6(projectRoot, ".rig", "state", "project-link.json"), `${JSON.stringify({ repoSlug: repo.slug, connection: connectionAlias, linkedAt: new Date().toISOString() }, null, 2)}
|
|
969
1026
|
`, "utf8");
|
|
970
1027
|
const checkout = checkoutForInit(projectRoot, serverKind, options.remoteCheckout);
|
|
971
1028
|
let uploadedSnapshot = null;
|
|
@@ -987,10 +1044,14 @@ async function runControlPlaneInit(context, options) {
|
|
|
987
1044
|
const token = authMethod === "gh" && !options.githubToken ? readGhAuthToken() : options.githubToken?.trim();
|
|
988
1045
|
if (token) {
|
|
989
1046
|
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug });
|
|
990
|
-
|
|
1047
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
1048
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
991
1049
|
if (serverKind === "remote") {
|
|
992
|
-
|
|
993
|
-
|
|
1050
|
+
writeRemoteGitHubAuthState(projectRoot, {
|
|
1051
|
+
source: authMethod === "gh" ? "gh" : "init-token",
|
|
1052
|
+
selectedRepo: repo.slug,
|
|
1053
|
+
apiSessionToken
|
|
1054
|
+
});
|
|
994
1055
|
}
|
|
995
1056
|
} else if (authMethod === "device") {
|
|
996
1057
|
const payload = await requestServerJson(context, "/api/github/auth/device/start", {
|
|
@@ -999,9 +1060,22 @@ async function runControlPlaneInit(context, options) {
|
|
|
999
1060
|
body: JSON.stringify({ repoSlug: repo.slug })
|
|
1000
1061
|
});
|
|
1001
1062
|
deviceAuth = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
1002
|
-
|
|
1003
|
-
|
|
1063
|
+
if (context.outputMode !== "json") {
|
|
1064
|
+
const verificationUri = String(deviceAuth.verificationUri ?? deviceAuth.verification_uri ?? deviceAuth.verification_uri_complete ?? "the verification URL returned by the server");
|
|
1065
|
+
const userCode = String(deviceAuth.userCode ?? deviceAuth.user_code ?? "the returned user code");
|
|
1066
|
+
console.log(`GitHub device flow: open ${verificationUri} and enter ${userCode}. Waiting for authorization...`);
|
|
1067
|
+
}
|
|
1068
|
+
const completed = await pollDeviceAuthUntilComplete(context, deviceAuth.pollId, deviceAuth);
|
|
1069
|
+
if (completed) {
|
|
1070
|
+
const apiSessionToken = apiSessionTokenFrom(completed);
|
|
1071
|
+
if (apiSessionToken) {
|
|
1072
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken);
|
|
1073
|
+
if (serverKind === "remote") {
|
|
1074
|
+
writeRemoteGitHubAuthState(projectRoot, { source: "device", selectedRepo: repo.slug, apiSessionToken });
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1004
1077
|
deviceAuth = { ...deviceAuth, poll: completed, completed: completed.status === "signed-in" };
|
|
1078
|
+
}
|
|
1005
1079
|
}
|
|
1006
1080
|
let remoteCheckoutPreparation = null;
|
|
1007
1081
|
if (serverKind === "remote" && options.remoteCheckout?.kind !== "uploaded-snapshot") {
|
|
@@ -1021,6 +1095,12 @@ async function runControlPlaneInit(context, options) {
|
|
|
1021
1095
|
});
|
|
1022
1096
|
const checkoutPath = typeof checkout.path === "string" ? checkout.path : null;
|
|
1023
1097
|
const serverRootSwitch = serverKind === "remote" && checkoutPath ? await switchServerProjectRootViaServer(context, checkoutPath) : null;
|
|
1098
|
+
if (serverRootSwitch && token) {
|
|
1099
|
+
githubAuth = await postGitHubTokenViaServer(context, token, { selectedRepo: repo.slug, projectRoot: checkoutPath ?? undefined });
|
|
1100
|
+
const apiSessionToken = apiSessionTokenFrom(githubAuth);
|
|
1101
|
+
setGitHubBearerTokenForCurrentProcess(apiSessionToken ?? token);
|
|
1102
|
+
writeRemoteGitHubAuthState(projectRoot, { source: authMethod === "gh" ? "gh" : "init-token", selectedRepo: repo.slug, apiSessionToken });
|
|
1103
|
+
}
|
|
1024
1104
|
const activeProjectRegistration = serverRootSwitch ? await registerProjectViaServer(context, { repoSlug: repo.slug, checkout }) : null;
|
|
1025
1105
|
const pi = serverKind === "remote" ? await ensureRemotePiRigInstalled({ requestJson: (pathname, init) => requestServerJson(context, pathname, init) }).catch((error) => ({
|
|
1026
1106
|
remote: true,
|
|
@@ -1130,7 +1210,7 @@ function parseInitOptions(args) {
|
|
|
1130
1210
|
async function runInteractiveControlPlaneInit(context, prompts) {
|
|
1131
1211
|
prompts.intro?.("Initialize a Rig control-plane project");
|
|
1132
1212
|
const projectRoot = context.projectRoot;
|
|
1133
|
-
const existingConfig =
|
|
1213
|
+
const existingConfig = existsSync5(resolve6(projectRoot, "rig.config.ts")) || existsSync5(resolve6(projectRoot, "rig.config.json"));
|
|
1134
1214
|
let repair = false;
|
|
1135
1215
|
let privateStateOnly = false;
|
|
1136
1216
|
if (existingConfig) {
|
|
@@ -1230,7 +1310,7 @@ async function runInteractiveControlPlaneInit(context, prompts) {
|
|
|
1230
1310
|
});
|
|
1231
1311
|
const details = result.details && typeof result.details === "object" && !Array.isArray(result.details) ? result.details : {};
|
|
1232
1312
|
const deviceAuth = details.deviceAuth && typeof details.deviceAuth === "object" && !Array.isArray(details.deviceAuth) ? details.deviceAuth : null;
|
|
1233
|
-
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")}.` : "";
|
|
1313
|
+
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")}.` : "";
|
|
1234
1314
|
prompts.outro?.(`Rig project initialized.${deviceMessage} Next: rig doctor && rig task list`);
|
|
1235
1315
|
return result;
|
|
1236
1316
|
}
|
package/dist/src/commands/run.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/commands/run.ts
|
|
3
|
-
import { existsSync as
|
|
4
|
-
import { resolve as
|
|
3
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
4
|
+
import { resolve as resolve3 } from "path";
|
|
5
5
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
6
6
|
|
|
7
7
|
// packages/cli/src/runner.ts
|
|
@@ -85,6 +85,8 @@ function parsePositiveInt(value, option, fallback) {
|
|
|
85
85
|
|
|
86
86
|
// packages/cli/src/commands/_server-client.ts
|
|
87
87
|
import { spawnSync } from "child_process";
|
|
88
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
89
|
+
import { resolve as resolve2 } from "path";
|
|
88
90
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
89
91
|
|
|
90
92
|
// packages/cli/src/commands/_connection-state.ts
|
|
@@ -175,9 +177,25 @@ function cleanToken(value) {
|
|
|
175
177
|
const trimmed = value?.trim();
|
|
176
178
|
return trimmed ? trimmed : null;
|
|
177
179
|
}
|
|
178
|
-
function
|
|
180
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
181
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
182
|
+
if (!existsSync2(path))
|
|
183
|
+
return null;
|
|
184
|
+
try {
|
|
185
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
186
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
187
|
+
} catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
179
192
|
if (cachedGitHubBearerToken !== undefined)
|
|
180
193
|
return cachedGitHubBearerToken;
|
|
194
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
195
|
+
if (privateSession) {
|
|
196
|
+
cachedGitHubBearerToken = privateSession;
|
|
197
|
+
return cachedGitHubBearerToken;
|
|
198
|
+
}
|
|
181
199
|
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
182
200
|
if (envToken) {
|
|
183
201
|
cachedGitHubBearerToken = envToken;
|
|
@@ -197,7 +215,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
197
215
|
if (selected?.connection.kind === "remote") {
|
|
198
216
|
return {
|
|
199
217
|
baseUrl: selected.connection.baseUrl,
|
|
200
|
-
authToken: readGitHubBearerTokenForRemote(),
|
|
218
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
201
219
|
connectionKind: "remote"
|
|
202
220
|
};
|
|
203
221
|
}
|
|
@@ -545,7 +563,7 @@ async function executeRun(context, args) {
|
|
|
545
563
|
if (!run.value) {
|
|
546
564
|
throw new CliError2("run timeline requires --run <id>.");
|
|
547
565
|
}
|
|
548
|
-
const timelinePath =
|
|
566
|
+
const timelinePath = resolve3(resolveAuthorityRunDir(context.projectRoot, run.value), "timeline.jsonl");
|
|
549
567
|
const printEvents = () => {
|
|
550
568
|
const events2 = readJsonlFile(timelinePath);
|
|
551
569
|
if (context.outputMode === "text") {
|
|
@@ -557,12 +575,12 @@ async function executeRun(context, args) {
|
|
|
557
575
|
};
|
|
558
576
|
const events = printEvents();
|
|
559
577
|
if (follow.value && context.outputMode === "text") {
|
|
560
|
-
let lastLength =
|
|
578
|
+
let lastLength = existsSync3(timelinePath) ? readFileSync3(timelinePath, "utf8").length : 0;
|
|
561
579
|
while (true) {
|
|
562
580
|
await Bun.sleep(1000);
|
|
563
|
-
if (!
|
|
581
|
+
if (!existsSync3(timelinePath))
|
|
564
582
|
continue;
|
|
565
|
-
const next =
|
|
583
|
+
const next = readFileSync3(timelinePath, "utf8");
|
|
566
584
|
if (next.length <= lastLength)
|
|
567
585
|
continue;
|
|
568
586
|
const delta = next.slice(lastLength);
|
|
@@ -62,6 +62,8 @@ function normalizeRuntimeAdapter(value) {
|
|
|
62
62
|
|
|
63
63
|
// packages/cli/src/commands/_server-client.ts
|
|
64
64
|
import { spawnSync } from "child_process";
|
|
65
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
66
|
+
import { resolve as resolve2 } from "path";
|
|
65
67
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
66
68
|
|
|
67
69
|
// packages/cli/src/commands/_connection-state.ts
|
|
@@ -152,9 +154,25 @@ function cleanToken(value) {
|
|
|
152
154
|
const trimmed = value?.trim();
|
|
153
155
|
return trimmed ? trimmed : null;
|
|
154
156
|
}
|
|
155
|
-
function
|
|
157
|
+
function readPrivateRemoteSessionToken(projectRoot) {
|
|
158
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
159
|
+
if (!existsSync2(path))
|
|
160
|
+
return null;
|
|
161
|
+
try {
|
|
162
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
163
|
+
return cleanToken(typeof parsed.apiSessionToken === "string" ? parsed.apiSessionToken : typeof parsed.sessionToken === "string" ? parsed.sessionToken : undefined);
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
156
169
|
if (cachedGitHubBearerToken !== undefined)
|
|
157
170
|
return cachedGitHubBearerToken;
|
|
171
|
+
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
172
|
+
if (privateSession) {
|
|
173
|
+
cachedGitHubBearerToken = privateSession;
|
|
174
|
+
return cachedGitHubBearerToken;
|
|
175
|
+
}
|
|
158
176
|
const envToken = cleanToken(process.env.RIG_GITHUB_TOKEN) ?? cleanToken(process.env.GITHUB_TOKEN) ?? cleanToken(process.env.GH_TOKEN);
|
|
159
177
|
if (envToken) {
|
|
160
178
|
cachedGitHubBearerToken = envToken;
|
|
@@ -174,7 +192,7 @@ async function ensureServerForCli(projectRoot) {
|
|
|
174
192
|
if (selected?.connection.kind === "remote") {
|
|
175
193
|
return {
|
|
176
194
|
baseUrl: selected.connection.baseUrl,
|
|
177
|
-
authToken: readGitHubBearerTokenForRemote(),
|
|
195
|
+
authToken: readGitHubBearerTokenForRemote(projectRoot),
|
|
178
196
|
connectionKind: "remote"
|
|
179
197
|
};
|
|
180
198
|
}
|