@h-rig/cli 0.0.6-alpha.9 → 0.0.6-alpha.90
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/README.md +1 -1
- package/dist/bin/build-rig-binaries.d.ts +2 -0
- package/dist/bin/rig.d.ts +2 -0
- package/dist/bin/rig.js +11052 -6668
- package/dist/src/app/board.d.ts +23 -0
- package/dist/src/app/board.js +1786 -0
- package/dist/src/app/drone-ui.d.ts +37 -0
- package/dist/src/app/drone-ui.js +294 -0
- package/dist/src/app/theme.d.ts +47 -0
- package/dist/src/app/theme.js +150 -0
- package/dist/src/commands/_async-ui.d.ts +13 -0
- package/dist/src/commands/_async-ui.js +223 -0
- package/dist/src/commands/_authority-runs.d.ts +22 -0
- package/dist/src/commands/_authority-runs.js +2 -3
- package/dist/src/commands/_cli-format.d.ts +49 -0
- package/dist/src/commands/_cli-format.js +447 -0
- package/dist/src/commands/_connection-state.d.ts +44 -0
- package/dist/src/commands/_connection-state.js +30 -11
- package/dist/src/commands/_doctor-checks.d.ts +52 -0
- package/dist/src/commands/_doctor-checks.js +180 -44
- package/dist/src/commands/_help-catalog.d.ts +51 -0
- package/dist/src/commands/_help-catalog.js +442 -0
- package/dist/src/commands/_json-output.d.ts +11 -0
- package/dist/src/commands/_json-output.js +56 -0
- package/dist/src/commands/_operator-surface.d.ts +34 -0
- package/dist/src/commands/_operator-surface.js +220 -0
- package/dist/src/commands/_operator-view.d.ts +30 -0
- package/dist/src/commands/_operator-view.js +803 -73
- package/dist/src/commands/_parsers.d.ts +15 -0
- package/dist/src/commands/_parsers.js +18 -11
- package/dist/src/commands/_paths.d.ts +11 -0
- package/dist/src/commands/_pi-frontend.d.ts +27 -0
- package/dist/src/commands/_pi-frontend.js +742 -0
- package/dist/src/commands/_pi-install.d.ts +42 -0
- package/dist/src/commands/_pi-install.js +5 -4
- package/dist/src/commands/_policy.d.ts +8 -0
- package/dist/src/commands/_policy.js +12 -5
- package/dist/src/commands/_preflight.d.ts +22 -0
- package/dist/src/commands/_preflight.js +190 -128
- package/dist/src/commands/_probes.d.ts +1 -0
- package/dist/src/commands/_run-driver-helpers.d.ts +99 -0
- package/dist/src/commands/_run-driver-helpers.js +75 -22
- package/dist/src/commands/_run-replay.d.ts +24 -0
- package/dist/src/commands/_run-replay.js +142 -0
- package/dist/src/commands/_server-client.d.ts +186 -0
- package/dist/src/commands/_server-client.js +360 -61
- package/dist/src/commands/_snapshot-upload.d.ts +39 -0
- package/dist/src/commands/_snapshot-upload.js +163 -39
- package/dist/src/commands/_spinner.d.ts +25 -0
- package/dist/src/commands/_spinner.js +65 -0
- package/dist/src/commands/_task-picker.d.ts +9 -0
- package/dist/src/commands/_task-picker.js +172 -19
- package/dist/src/commands/agent.d.ts +3 -0
- package/dist/src/commands/agent.js +39 -20
- package/dist/src/commands/browser.d.ts +65 -0
- package/dist/src/commands/browser.js +334 -51
- package/dist/src/commands/connect.d.ts +7 -0
- package/dist/src/commands/connect.js +272 -33
- package/dist/src/commands/dist.d.ts +28 -0
- package/dist/src/commands/dist.js +19 -12
- package/dist/src/commands/doctor.d.ts +3 -0
- package/dist/src/commands/doctor.js +378 -45
- package/dist/src/commands/github.d.ts +3 -0
- package/dist/src/commands/github.js +375 -53
- package/dist/src/commands/inbox.d.ts +30 -0
- package/dist/src/commands/inbox.js +760 -70
- package/dist/src/commands/init.d.ts +74 -0
- package/dist/src/commands/init.js +963 -119
- package/dist/src/commands/inspect.d.ts +3 -0
- package/dist/src/commands/inspect.js +588 -32
- package/dist/src/commands/inspector.d.ts +3 -0
- package/dist/src/commands/inspector.js +20 -13
- package/dist/src/commands/pi.d.ts +3 -0
- package/dist/src/commands/pi.js +177 -0
- package/dist/src/commands/plugin.d.ts +16 -0
- package/dist/src/commands/plugin.js +95 -27
- package/dist/src/commands/profile-and-review.d.ts +4 -0
- package/dist/src/commands/profile-and-review.js +26 -19
- package/dist/src/commands/queue.d.ts +3 -0
- package/dist/src/commands/queue.js +32 -12
- package/dist/src/commands/remote.d.ts +3 -0
- package/dist/src/commands/remote.js +43 -36
- package/dist/src/commands/repo-git-harness.d.ts +5 -0
- package/dist/src/commands/repo-git-harness.js +22 -15
- package/dist/src/commands/run.d.ts +3 -0
- package/dist/src/commands/run.js +1379 -159
- package/dist/src/commands/server.d.ts +7 -0
- package/dist/src/commands/server.js +502 -57
- package/dist/src/commands/setup.d.ts +16 -0
- package/dist/src/commands/setup.js +390 -63
- package/dist/src/commands/stats.d.ts +12 -0
- package/dist/src/commands/stats.js +1051 -0
- package/dist/src/commands/task-report-bug.d.ts +19 -0
- package/dist/src/commands/task-report-bug.js +260 -62
- package/dist/src/commands/task-run-driver.d.ts +132 -0
- package/dist/src/commands/task-run-driver.js +886 -130
- package/dist/src/commands/task.d.ts +14 -0
- package/dist/src/commands/task.js +1644 -314
- package/dist/src/commands/test.d.ts +3 -0
- package/dist/src/commands/test.js +15 -8
- package/dist/src/commands/workspace.d.ts +3 -0
- package/dist/src/commands/workspace.js +18 -11
- package/dist/src/commands.d.ts +29 -0
- package/dist/src/commands.js +10967 -6637
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +10168 -5787
- package/dist/src/launcher.d.ts +61 -0
- package/dist/src/launcher.js +77 -13
- package/dist/src/report-bug.d.ts +44 -0
- package/dist/src/report-bug.js +3 -3
- package/dist/src/runner.d.ts +47 -0
- package/dist/src/runner.js +16 -22
- package/dist/src/withMutedConsole.d.ts +2 -0
- package/package.json +13 -6
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type PiInstallCheck = {
|
|
2
|
+
readonly ok: boolean;
|
|
3
|
+
readonly label: string;
|
|
4
|
+
readonly detail?: string;
|
|
5
|
+
readonly hint?: string;
|
|
6
|
+
};
|
|
7
|
+
export type CommandRunner = (command: string[], options?: {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
}) => Promise<{
|
|
10
|
+
exitCode: number;
|
|
11
|
+
stdout: string;
|
|
12
|
+
stderr: string;
|
|
13
|
+
}>;
|
|
14
|
+
export type PiRigInstallStatus = {
|
|
15
|
+
readonly pi: PiInstallCheck;
|
|
16
|
+
readonly piRig: PiInstallCheck;
|
|
17
|
+
readonly extensionPath: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function resolvePiRigExtensionPath(homeDir: string): string;
|
|
20
|
+
export declare function resolvePiRigPackageSource(projectRoot: string, exists?: (path: string) => boolean): string;
|
|
21
|
+
export declare function checkPiRigInstall(input?: {
|
|
22
|
+
readonly homeDir?: string;
|
|
23
|
+
readonly commandRunner?: CommandRunner;
|
|
24
|
+
readonly exists?: (path: string) => boolean;
|
|
25
|
+
}): Promise<PiRigInstallStatus>;
|
|
26
|
+
export declare function ensurePiRigInstalled(input: {
|
|
27
|
+
readonly projectRoot: string;
|
|
28
|
+
readonly homeDir?: string;
|
|
29
|
+
readonly commandRunner?: CommandRunner;
|
|
30
|
+
}): Promise<PiRigInstallStatus & {
|
|
31
|
+
installedPath: string;
|
|
32
|
+
}>;
|
|
33
|
+
export declare function ensureRemotePiRigInstalled(input: {
|
|
34
|
+
readonly requestJson: (pathname: string, init?: RequestInit) => Promise<unknown>;
|
|
35
|
+
}): Promise<PiRigInstallStatus & {
|
|
36
|
+
readonly remote: true;
|
|
37
|
+
}>;
|
|
38
|
+
export declare function buildPiSetupChecks(input?: {
|
|
39
|
+
readonly homeDir?: string;
|
|
40
|
+
readonly commandRunner?: CommandRunner;
|
|
41
|
+
readonly exists?: (path: string) => boolean;
|
|
42
|
+
}): Promise<PiInstallCheck[]>;
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import { existsSync, readFileSync, rmSync } from "fs";
|
|
4
4
|
import { homedir } from "os";
|
|
5
5
|
import { resolve } from "path";
|
|
6
|
-
var PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
6
|
+
var PI_RIG_PACKAGE_NAME = "@h-rig/pi-rig";
|
|
7
|
+
var LEGACY_PI_RIG_PACKAGE_NAME = "@rig/pi-rig";
|
|
7
8
|
var LEGACY_PI_RIG_MARKER = `// Managed by Rig. Source package: @rig/pi-rig.
|
|
8
9
|
export { default } from '@rig/pi-rig';
|
|
9
10
|
`;
|
|
@@ -31,7 +32,7 @@ function resolvePiHomeDir(inputHomeDir) {
|
|
|
31
32
|
function piListContainsPiRig(output) {
|
|
32
33
|
return output.split(/\r?\n/).some((line) => {
|
|
33
34
|
const normalized = line.trim();
|
|
34
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
35
|
+
return normalized.includes(PI_RIG_PACKAGE_NAME) || normalized.includes(LEGACY_PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
async function safeRun(runner, command, options) {
|
|
@@ -55,7 +56,7 @@ async function ensurePiBinaryAvailable(input) {
|
|
|
55
56
|
}
|
|
56
57
|
return { ok: true, detail: (current.stdout || current.stderr).trim() || undefined };
|
|
57
58
|
}
|
|
58
|
-
const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi@latest install";
|
|
59
|
+
const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim() || "bunx @earendil-works/pi-coding-agent@latest install";
|
|
59
60
|
const parts = splitInstallCommand(installCommand);
|
|
60
61
|
if (parts.length === 0) {
|
|
61
62
|
return { ok: false, error: (current.stderr || current.stdout).trim() || "pi --version failed" };
|
|
@@ -147,7 +148,7 @@ async function ensureRemotePiRigInstalled(input) {
|
|
|
147
148
|
const payload = await input.requestJson("/api/pi-rig/install", {
|
|
148
149
|
method: "POST",
|
|
149
150
|
headers: { "content-type": "application/json" },
|
|
150
|
-
body: JSON.stringify({ package:
|
|
151
|
+
body: JSON.stringify({ package: PI_RIG_PACKAGE_NAME, scope: "global" })
|
|
151
152
|
});
|
|
152
153
|
const record = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
153
154
|
const piOk = record.piOk === true || record.ok === true;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type RunnerContext } from "../runner";
|
|
2
|
+
import type { PolicyMode } from "@rig/runtime/control-plane/runtime/guard-types";
|
|
3
|
+
export declare function resolveEffectivePolicyMode(context: RunnerContext): PolicyMode;
|
|
4
|
+
export declare function appendControlledBashAudit(context: RunnerContext, mode: PolicyMode, command: string, args: string[], matchedRuleIds: string[], action?: "warn" | "blocked"): void;
|
|
5
|
+
export declare function enforceNativeCommandPolicy(context: RunnerContext, args: string[], options: {
|
|
6
|
+
commandPrefix: string;
|
|
7
|
+
writeAuditLog: boolean;
|
|
8
|
+
}): Promise<void>;
|
|
@@ -5,12 +5,19 @@ import { resolve } from "path";
|
|
|
5
5
|
|
|
6
6
|
// packages/cli/src/runner.ts
|
|
7
7
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
8
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
8
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
9
9
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
10
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
11
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
12
10
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
13
|
-
|
|
11
|
+
|
|
12
|
+
class CliError extends RuntimeCliError {
|
|
13
|
+
hint;
|
|
14
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
15
|
+
super(message, exitCode);
|
|
16
|
+
if (options.hint?.trim()) {
|
|
17
|
+
this.hint = options.hint.trim();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
14
21
|
function formatCommand(parts) {
|
|
15
22
|
return parts.map((part) => /[^a-zA-Z0-9_./:-]/.test(part) ? JSON.stringify(part) : part).join(" ");
|
|
16
23
|
}
|
|
@@ -69,7 +76,7 @@ async function enforceNativeCommandPolicy(context, args, options) {
|
|
|
69
76
|
appendControlledBashAudit(context, mode, command, args, matchedRuleIds, action === "block" ? "blocked" : action === "warn" ? "warn" : undefined);
|
|
70
77
|
}
|
|
71
78
|
if (action === "block") {
|
|
72
|
-
throw new
|
|
79
|
+
throw new CliError(`Policy blocked command: ${command}`, 126, { hint: "Review rig/policy/policy.json, or re-run with `--policy-mode observe` to log instead of block." });
|
|
73
80
|
}
|
|
74
81
|
}
|
|
75
82
|
export {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type RunnerContext } from "../runner";
|
|
2
|
+
export type TaskRunPreflightStatus = "pass" | "warn" | "fail";
|
|
3
|
+
export type TaskRunPreflightCheck = {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly status: TaskRunPreflightStatus;
|
|
7
|
+
readonly detail?: string;
|
|
8
|
+
readonly remediation?: string;
|
|
9
|
+
};
|
|
10
|
+
export type TaskRunPreflightResult = {
|
|
11
|
+
readonly ok: boolean;
|
|
12
|
+
readonly checks: readonly TaskRunPreflightCheck[];
|
|
13
|
+
};
|
|
14
|
+
export type TaskRunPreflightOptions = {
|
|
15
|
+
readonly taskId?: string | null;
|
|
16
|
+
readonly runtimeAdapter?: "claude-code" | "codex" | "pi";
|
|
17
|
+
readonly requestJson?: (pathname: string, init?: RequestInit) => Promise<unknown>;
|
|
18
|
+
};
|
|
19
|
+
export declare function runFastTaskRunPreflight(context: RunnerContext, options?: TaskRunPreflightOptions): Promise<TaskRunPreflightResult>;
|
|
20
|
+
export declare function runProjectMainSyncPreflight(context: RunnerContext, options: {
|
|
21
|
+
disabled: boolean;
|
|
22
|
+
}): Promise<void>;
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// packages/cli/src/runner.ts
|
|
3
3
|
import { EventBus } from "@rig/runtime/control-plane/runtime/events";
|
|
4
|
-
import { CliError } from "@rig/runtime/control-plane/errors";
|
|
4
|
+
import { CliError as RuntimeCliError } from "@rig/runtime/control-plane/errors";
|
|
5
5
|
import { evaluate, loadPolicy, resolveAction } from "@rig/runtime/control-plane/runtime/guard";
|
|
6
|
-
import { PluginManager } from "@rig/runtime/control-plane/runtime/plugins";
|
|
7
|
-
import { loadRuntimeContextFromEnv } from "@rig/runtime/control-plane/runtime/context";
|
|
8
6
|
import { buildBinary } from "@rig/runtime/control-plane/runtime/isolation";
|
|
9
|
-
|
|
7
|
+
|
|
8
|
+
class CliError extends RuntimeCliError {
|
|
9
|
+
hint;
|
|
10
|
+
constructor(message, exitCode = 1, options = {}) {
|
|
11
|
+
super(message, exitCode);
|
|
12
|
+
if (options.hint?.trim()) {
|
|
13
|
+
this.hint = options.hint.trim();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
10
17
|
|
|
11
18
|
// packages/cli/src/commands/_preflight.ts
|
|
12
19
|
import { ensureProjectMainFreshBeforeRun } from "@rig/runtime/control-plane/project-main-pre-run-sync";
|
|
@@ -33,9 +40,14 @@ function readJsonFile(path) {
|
|
|
33
40
|
try {
|
|
34
41
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
35
42
|
} catch (error) {
|
|
36
|
-
throw new
|
|
43
|
+
throw new CliError(`Invalid Rig connection state at ${path}: ${error instanceof Error ? error.message : String(error)}`, 1, { hint: "Fix or delete that file, then re-select a server with `rig server use <alias|local>`." });
|
|
37
44
|
}
|
|
38
45
|
}
|
|
46
|
+
function writeJsonFile(path, value) {
|
|
47
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
48
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
49
|
+
`, "utf8");
|
|
50
|
+
}
|
|
39
51
|
function normalizeConnection(value) {
|
|
40
52
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
41
53
|
return null;
|
|
@@ -76,29 +88,42 @@ function readRepoConnection(projectRoot) {
|
|
|
76
88
|
return {
|
|
77
89
|
selected,
|
|
78
90
|
project: typeof record.project === "string" ? record.project : undefined,
|
|
79
|
-
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined
|
|
91
|
+
linkedAt: typeof record.linkedAt === "string" ? record.linkedAt : undefined,
|
|
92
|
+
serverProjectRoot: typeof record.serverProjectRoot === "string" && record.serverProjectRoot.trim() ? record.serverProjectRoot.trim() : undefined
|
|
80
93
|
};
|
|
81
94
|
}
|
|
95
|
+
function writeRepoConnection(projectRoot, state) {
|
|
96
|
+
writeJsonFile(resolveRepoConnectionPath(projectRoot), state);
|
|
97
|
+
}
|
|
82
98
|
function resolveSelectedConnection(projectRoot, options = {}) {
|
|
83
99
|
const repo = readRepoConnection(projectRoot);
|
|
84
100
|
if (!repo)
|
|
85
101
|
return null;
|
|
86
102
|
if (repo.selected === "local")
|
|
87
|
-
return { alias: "local", connection: { kind: "local", mode: "auto" } };
|
|
103
|
+
return { alias: "local", connection: { kind: "local", mode: "auto" }, serverProjectRoot: repo.serverProjectRoot };
|
|
88
104
|
const global = readGlobalConnections(options);
|
|
89
105
|
const connection = global.connections[repo.selected];
|
|
90
106
|
if (!connection) {
|
|
91
|
-
throw new
|
|
107
|
+
throw new CliError(`Selected Rig server "${repo.selected}" was not found. Run \`rig server list\` or \`rig server use local\`.`, 1);
|
|
92
108
|
}
|
|
93
|
-
return { alias: repo.selected, connection };
|
|
109
|
+
return { alias: repo.selected, connection, serverProjectRoot: repo.serverProjectRoot };
|
|
110
|
+
}
|
|
111
|
+
function writeRepoServerProjectRoot(projectRoot, serverProjectRoot) {
|
|
112
|
+
const repo = readRepoConnection(projectRoot);
|
|
113
|
+
if (!repo)
|
|
114
|
+
return;
|
|
115
|
+
writeRepoConnection(projectRoot, { ...repo, serverProjectRoot });
|
|
94
116
|
}
|
|
95
117
|
|
|
96
118
|
// packages/cli/src/commands/_server-client.ts
|
|
97
|
-
import { spawnSync } from "child_process";
|
|
98
119
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
99
120
|
import { resolve as resolve2 } from "path";
|
|
100
121
|
import { ensureLocalRigServerConnection } from "@rig/runtime/local-server";
|
|
101
|
-
var
|
|
122
|
+
var scopedGitHubBearerTokens = new Map;
|
|
123
|
+
var serverPhaseListener = null;
|
|
124
|
+
function reportServerPhase(label) {
|
|
125
|
+
serverPhaseListener?.(label);
|
|
126
|
+
}
|
|
102
127
|
function cleanToken(value) {
|
|
103
128
|
const trimmed = value?.trim();
|
|
104
129
|
return trimmed ? trimmed : null;
|
|
@@ -115,49 +140,80 @@ function readPrivateRemoteSessionToken(projectRoot) {
|
|
|
115
140
|
}
|
|
116
141
|
}
|
|
117
142
|
function readGitHubBearerTokenForRemote(projectRoot) {
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
const scopedKey = resolve2(projectRoot);
|
|
144
|
+
if (scopedGitHubBearerTokens.has(scopedKey))
|
|
145
|
+
return scopedGitHubBearerTokens.get(scopedKey) ?? null;
|
|
120
146
|
const privateSession = readPrivateRemoteSessionToken(projectRoot);
|
|
121
|
-
if (privateSession)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return
|
|
147
|
+
if (privateSession)
|
|
148
|
+
return privateSession;
|
|
149
|
+
return cleanToken(process.env.RIG_SERVER_AUTH_TOKEN) ?? cleanToken(process.env.RIG_REMOTE_AUTH_TOKEN);
|
|
150
|
+
}
|
|
151
|
+
function readStoredGitHubAuthToken(projectRoot) {
|
|
152
|
+
const path = resolve2(projectRoot, ".rig", "state", "github-auth.json");
|
|
153
|
+
if (!existsSync2(path))
|
|
154
|
+
return null;
|
|
155
|
+
try {
|
|
156
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
157
|
+
return cleanToken(typeof parsed.token === "string" ? parsed.token : undefined);
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
129
160
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
134
|
-
});
|
|
135
|
-
cachedGitHubBearerToken = result.status === 0 ? cleanToken(result.stdout) : null;
|
|
136
|
-
return cachedGitHubBearerToken;
|
|
161
|
+
}
|
|
162
|
+
function readLocalConnectionFallbackToken(projectRoot) {
|
|
163
|
+
return readGitHubBearerTokenForRemote(projectRoot) ?? cleanToken(process.env.RIG_GITHUB_TOKEN) ?? readStoredGitHubAuthToken(projectRoot);
|
|
137
164
|
}
|
|
138
165
|
async function ensureServerForCli(projectRoot) {
|
|
139
166
|
try {
|
|
140
167
|
const selected = resolveSelectedConnection(projectRoot);
|
|
141
168
|
if (selected?.connection.kind === "remote") {
|
|
169
|
+
reportServerPhase(`Connecting to ${selected.alias}\u2026`);
|
|
170
|
+
const authToken = readGitHubBearerTokenForRemote(projectRoot);
|
|
171
|
+
const serverProjectRoot = selected.serverProjectRoot ?? await backfillRemoteServerProjectRoot(projectRoot, selected.connection.baseUrl, authToken);
|
|
142
172
|
return {
|
|
143
173
|
baseUrl: selected.connection.baseUrl,
|
|
144
|
-
authToken
|
|
145
|
-
connectionKind: "remote"
|
|
174
|
+
authToken,
|
|
175
|
+
connectionKind: "remote",
|
|
176
|
+
serverProjectRoot
|
|
146
177
|
};
|
|
147
178
|
}
|
|
179
|
+
reportServerPhase("Starting local Rig server\u2026");
|
|
148
180
|
const connection = await ensureLocalRigServerConnection(projectRoot);
|
|
149
181
|
return {
|
|
150
182
|
baseUrl: connection.baseUrl,
|
|
151
|
-
authToken: connection.authToken,
|
|
152
|
-
connectionKind: "local"
|
|
183
|
+
authToken: connection.authToken ?? readLocalConnectionFallbackToken(projectRoot),
|
|
184
|
+
connectionKind: "local",
|
|
185
|
+
serverProjectRoot: resolve2(projectRoot)
|
|
153
186
|
};
|
|
154
187
|
} catch (error) {
|
|
155
188
|
if (error instanceof Error) {
|
|
156
|
-
throw new
|
|
189
|
+
throw new CliError(error.message, 1);
|
|
157
190
|
}
|
|
158
191
|
throw error;
|
|
159
192
|
}
|
|
160
193
|
}
|
|
194
|
+
async function backfillRemoteServerProjectRoot(projectRoot, baseUrl, authToken) {
|
|
195
|
+
const repo = readRepoConnection(projectRoot);
|
|
196
|
+
const slug = repo?.project?.trim();
|
|
197
|
+
if (!slug)
|
|
198
|
+
return null;
|
|
199
|
+
try {
|
|
200
|
+
const response = await fetch(`${baseUrl}/api/projects/${encodeURIComponent(slug)}`, {
|
|
201
|
+
headers: mergeHeaders(undefined, authToken)
|
|
202
|
+
});
|
|
203
|
+
if (!response.ok)
|
|
204
|
+
return null;
|
|
205
|
+
const payload = await response.json();
|
|
206
|
+
const project = payload.project && typeof payload.project === "object" && !Array.isArray(payload.project) ? payload.project : null;
|
|
207
|
+
const checkouts = Array.isArray(project?.checkouts) ? project.checkouts : [];
|
|
208
|
+
const latestCheckout = [...checkouts].reverse().find((entry) => Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.path === "string"));
|
|
209
|
+
const path = typeof latestCheckout?.path === "string" && latestCheckout.path.trim() ? latestCheckout.path.trim() : null;
|
|
210
|
+
if (path)
|
|
211
|
+
writeRepoServerProjectRoot(projectRoot, path);
|
|
212
|
+
return path;
|
|
213
|
+
} catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
161
217
|
function mergeHeaders(headers, authToken) {
|
|
162
218
|
const merged = new Headers(headers);
|
|
163
219
|
if (authToken) {
|
|
@@ -180,12 +236,65 @@ function diagnosticMessage(payload) {
|
|
|
180
236
|
});
|
|
181
237
|
return messages.length > 0 ? messages.join("; ") : null;
|
|
182
238
|
}
|
|
239
|
+
var serverReachabilityCache = new Map;
|
|
240
|
+
async function probeServerReachability(baseUrl, authToken) {
|
|
241
|
+
try {
|
|
242
|
+
const response = await fetch(`${baseUrl.replace(/\/+$/, "")}/api/server/status`, {
|
|
243
|
+
headers: mergeHeaders(undefined, authToken),
|
|
244
|
+
signal: AbortSignal.timeout(1500)
|
|
245
|
+
});
|
|
246
|
+
return response.ok;
|
|
247
|
+
} catch {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function cachedServerReachability(projectRoot, baseUrl, authToken) {
|
|
252
|
+
const key = resolve2(projectRoot);
|
|
253
|
+
const cached = serverReachabilityCache.get(key);
|
|
254
|
+
if (cached)
|
|
255
|
+
return cached;
|
|
256
|
+
const probe = probeServerReachability(baseUrl, authToken);
|
|
257
|
+
serverReachabilityCache.set(key, probe);
|
|
258
|
+
return probe;
|
|
259
|
+
}
|
|
260
|
+
function describeSelectedServer(projectRoot, server) {
|
|
261
|
+
try {
|
|
262
|
+
const selected = resolveSelectedConnection(projectRoot);
|
|
263
|
+
if (selected) {
|
|
264
|
+
return {
|
|
265
|
+
alias: selected.alias,
|
|
266
|
+
target: selected.connection.kind === "remote" ? selected.connection.baseUrl : server.baseUrl
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
} catch {}
|
|
270
|
+
return { alias: server.connectionKind === "remote" ? "remote" : "local", target: server.baseUrl };
|
|
271
|
+
}
|
|
272
|
+
async function buildServerFailureContext(projectRoot, server) {
|
|
273
|
+
const { alias, target } = describeSelectedServer(projectRoot, server);
|
|
274
|
+
const reachable = await cachedServerReachability(projectRoot, server.baseUrl, server.authToken);
|
|
275
|
+
const reachability = reachable ? "server is reachable" : "server is unreachable";
|
|
276
|
+
return {
|
|
277
|
+
contextLine: `Currently connected to: ${alias} at ${target} (${reachability}).`,
|
|
278
|
+
hint: "Check the selected server with `rig server status`, or switch with `rig server use <alias|local>`."
|
|
279
|
+
};
|
|
280
|
+
}
|
|
183
281
|
async function requestServerJson(context, pathname, init = {}) {
|
|
184
282
|
const server = await ensureServerForCli(context.projectRoot);
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
headers
|
|
188
|
-
});
|
|
283
|
+
const headers = mergeHeaders(init.headers, server.authToken);
|
|
284
|
+
if (server.serverProjectRoot)
|
|
285
|
+
headers.set("x-rig-project-root", server.serverProjectRoot);
|
|
286
|
+
reportServerPhase(`${(init.method ?? "GET").toUpperCase()} ${pathname.split("?")[0]}\u2026`);
|
|
287
|
+
let response;
|
|
288
|
+
try {
|
|
289
|
+
response = await fetch(`${server.baseUrl}${pathname}`, {
|
|
290
|
+
...init,
|
|
291
|
+
headers
|
|
292
|
+
});
|
|
293
|
+
} catch (error) {
|
|
294
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
295
|
+
throw new CliError(`Rig server request failed: ${error instanceof Error ? error.message : String(error)}
|
|
296
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
297
|
+
}
|
|
189
298
|
const text = await response.text();
|
|
190
299
|
const payload = text.trim().length > 0 ? (() => {
|
|
191
300
|
try {
|
|
@@ -196,83 +305,26 @@ async function requestServerJson(context, pathname, init = {}) {
|
|
|
196
305
|
})() : null;
|
|
197
306
|
if (!response.ok) {
|
|
198
307
|
const diagnostics = diagnosticMessage(payload);
|
|
199
|
-
const
|
|
200
|
-
|
|
308
|
+
const rawDetail = diagnostics ?? (text || response.statusText);
|
|
309
|
+
const detail = diagnostics ? rawDetail : rawDetail.split(`
|
|
310
|
+
`).map((line) => line.trim()).find((line) => line.length > 0)?.slice(0, 200) ?? response.statusText;
|
|
311
|
+
const failure = await buildServerFailureContext(context.projectRoot, server);
|
|
312
|
+
throw new CliError(`Rig server request failed (${response.status}): ${detail}
|
|
313
|
+
${failure.contextLine}`, 1, { hint: failure.hint });
|
|
201
314
|
}
|
|
202
315
|
return payload;
|
|
203
316
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
proc.exited
|
|
216
|
-
]);
|
|
217
|
-
return { exitCode, stdout, stderr };
|
|
218
|
-
}
|
|
219
|
-
function resolvePiRigExtensionPath(homeDir) {
|
|
220
|
-
return resolve3(homeDir, ".pi", "agent", "extensions", "pi-rig");
|
|
221
|
-
}
|
|
222
|
-
function resolvePiHomeDir(inputHomeDir) {
|
|
223
|
-
return inputHomeDir ?? process.env.RIG_PI_HOME_DIR?.trim() ?? homedir2();
|
|
224
|
-
}
|
|
225
|
-
function piListContainsPiRig(output) {
|
|
226
|
-
return output.split(/\r?\n/).some((line) => {
|
|
227
|
-
const normalized = line.trim();
|
|
228
|
-
return normalized.includes(PI_RIG_PACKAGE_NAME) || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(normalized);
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
async function safeRun(runner, command, options) {
|
|
232
|
-
try {
|
|
233
|
-
return await runner(command, options);
|
|
234
|
-
} catch (error) {
|
|
235
|
-
return { exitCode: 1, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
async function checkPiRigInstall(input = {}) {
|
|
239
|
-
const home = resolvePiHomeDir(input.homeDir);
|
|
240
|
-
const extensionPath = resolvePiRigExtensionPath(home);
|
|
241
|
-
if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1") {
|
|
242
|
-
return {
|
|
243
|
-
extensionPath,
|
|
244
|
-
pi: { ok: true, label: "pi", detail: "fake-pi" },
|
|
245
|
-
piRig: { ok: true, label: "pi-rig global extension", detail: extensionPath }
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
const exists = input.exists ?? existsSync3;
|
|
249
|
-
const runner = input.commandRunner ?? defaultCommandRunner;
|
|
250
|
-
const piResult = await safeRun(runner, ["pi", "--version"]);
|
|
251
|
-
const piListResult = piResult.exitCode === 0 ? await safeRun(runner, ["pi", "list"]) : { exitCode: 1, stdout: "", stderr: "" };
|
|
252
|
-
const listedPiRig = piListResult.exitCode === 0 && piListContainsPiRig(`${piListResult.stdout}
|
|
253
|
-
${piListResult.stderr}`);
|
|
254
|
-
const legacyBridge = exists(resolve3(extensionPath, "index.ts"));
|
|
255
|
-
const hasPiRig = listedPiRig;
|
|
256
|
-
return {
|
|
257
|
-
extensionPath,
|
|
258
|
-
pi: {
|
|
259
|
-
ok: piResult.exitCode === 0,
|
|
260
|
-
label: "pi",
|
|
261
|
-
detail: (piResult.stdout || piResult.stderr).trim() || undefined,
|
|
262
|
-
hint: piResult.exitCode === 0 ? undefined : "Install Pi or run `rig init --yes` to install/update the Pi runtime."
|
|
263
|
-
},
|
|
264
|
-
piRig: {
|
|
265
|
-
ok: hasPiRig,
|
|
266
|
-
label: "pi-rig global extension",
|
|
267
|
-
detail: hasPiRig ? piListResult.stdout.trim() || PI_RIG_PACKAGE_NAME : legacyBridge ? `${extensionPath} (legacy bridge; reinstall required)` : undefined,
|
|
268
|
-
hint: hasPiRig ? undefined : "Run `rig init --yes` to install/enable the global pi-rig package with `pi install`."
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
async function buildPiSetupChecks(input = {}) {
|
|
273
|
-
const status = await checkPiRigInstall(input);
|
|
274
|
-
return [status.pi, status.piRig];
|
|
275
|
-
}
|
|
317
|
+
var RESUMABLE_RUN_STATUSES = new Set([
|
|
318
|
+
"created",
|
|
319
|
+
"preparing",
|
|
320
|
+
"running",
|
|
321
|
+
"validating",
|
|
322
|
+
"reviewing",
|
|
323
|
+
"stopped",
|
|
324
|
+
"failed",
|
|
325
|
+
"needs-attention",
|
|
326
|
+
"needs_attention"
|
|
327
|
+
]);
|
|
276
328
|
|
|
277
329
|
// packages/cli/src/commands/_preflight.ts
|
|
278
330
|
function preflightCheck(id, label, status, detail, remediation) {
|
|
@@ -329,6 +381,9 @@ function permissionAllowsPr(payload) {
|
|
|
329
381
|
}
|
|
330
382
|
return null;
|
|
331
383
|
}
|
|
384
|
+
function isNotFoundError(error) {
|
|
385
|
+
return /\b(404|not found)\b/i.test(message(error));
|
|
386
|
+
}
|
|
332
387
|
function projectCheckoutReady(payload) {
|
|
333
388
|
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
334
389
|
return null;
|
|
@@ -361,19 +416,33 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
361
416
|
const checks = [];
|
|
362
417
|
const request = options.requestJson ?? ((pathname, init) => requestServerJson(context, pathname, init));
|
|
363
418
|
const taskId = options.taskId?.trim() || null;
|
|
419
|
+
const requiresCurrentRunApi = Boolean(taskId);
|
|
420
|
+
const selectedServer = options.requestJson ? null : await ensureServerForCli(context.projectRoot).catch(() => null);
|
|
421
|
+
const allowLocalLegacyTaskRunCompatibility = selectedServer?.connectionKind === "local";
|
|
422
|
+
let legacyServerCompatibility = false;
|
|
364
423
|
try {
|
|
365
424
|
await request("/api/server/status");
|
|
366
425
|
checks.push(preflightCheck("server", "Rig server reachable", "pass"));
|
|
367
426
|
} catch (error) {
|
|
368
|
-
|
|
427
|
+
if (isNotFoundError(error)) {
|
|
428
|
+
try {
|
|
429
|
+
await request("/health");
|
|
430
|
+
legacyServerCompatibility = !requiresCurrentRunApi || allowLocalLegacyTaskRunCompatibility;
|
|
431
|
+
checks.push(requiresCurrentRunApi && !allowLocalLegacyTaskRunCompatibility ? preflightCheck("server", "Rig server reachable", "fail", "legacy /health endpoint only; current task-run APIs are required", "Upgrade/select the Rig server before launching a task run.") : preflightCheck("server", "Rig server reachable", "pass", allowLocalLegacyTaskRunCompatibility ? "local legacy /health endpoint; submit endpoint will be authoritative" : "legacy /health endpoint"));
|
|
432
|
+
} catch (healthError) {
|
|
433
|
+
checks.push(preflightCheck("server", "Rig server reachable", "fail", message(healthError), "Start or select a reachable Rig server."));
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
checks.push(preflightCheck("server", "Rig server reachable", "fail", message(error), "Start or select a reachable Rig server."));
|
|
437
|
+
}
|
|
369
438
|
}
|
|
370
439
|
const repo = readRepoConnection(context.projectRoot);
|
|
371
|
-
checks.push(repo ? preflightCheck("project-link", "project linked to Rig
|
|
440
|
+
checks.push(repo ? preflightCheck("project-link", "project linked to Rig server", repo.project ? "pass" : "warn", `${repo.selected}${repo.project ? ` -> ${repo.project}` : ""}`, "Run `rig init --yes --repo owner/repo` to record the GitHub repo slug.") : preflightCheck("project-link", "project linked to Rig server", legacyServerCompatibility ? "warn" : "fail", "missing .rig/state/connection.json", "Run `rig init` or `rig server use <alias|local>`."));
|
|
372
441
|
try {
|
|
373
442
|
const auth = await request("/api/github/auth/status");
|
|
374
|
-
checks.push(isAuthenticated(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
|
|
443
|
+
checks.push(isAuthenticated(auth) ? preflightCheck("github-auth", "GitHub auth valid", "pass") : preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", "not authenticated", "Run `rig github auth import-gh` or `rig github auth token --token <token>`."));
|
|
375
444
|
} catch (error) {
|
|
376
|
-
checks.push(preflightCheck("github-auth", "GitHub auth valid", "fail", message(error), "Fix GitHub auth on the selected Rig server."));
|
|
445
|
+
checks.push(preflightCheck("github-auth", "GitHub auth valid", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix GitHub auth on the selected Rig server."));
|
|
377
446
|
}
|
|
378
447
|
try {
|
|
379
448
|
const projection = await request("/api/workspace/task-projection");
|
|
@@ -401,9 +470,9 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
401
470
|
try {
|
|
402
471
|
const tasks = await request(`/api/workspace/tasks?limit=200&refresh=1`);
|
|
403
472
|
const found = Array.isArray(tasks) && tasks.some((task) => taskMatchesId(task, taskId));
|
|
404
|
-
checks.push(found ? preflightCheck("issue", "task/issue accessible", "pass", taskId) : preflightCheck("issue", "task/issue accessible", "fail", taskId, "Confirm the issue exists and matches the configured task filters."));
|
|
473
|
+
checks.push(found ? preflightCheck("issue", "task/issue accessible", "pass", taskId) : preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", taskId, "Confirm the issue exists and matches the configured task filters."));
|
|
405
474
|
} catch (error) {
|
|
406
|
-
checks.push(preflightCheck("issue", "task/issue accessible", "fail", message(error), "Fix the task source before launching a run."));
|
|
475
|
+
checks.push(preflightCheck("issue", "task/issue accessible", legacyServerCompatibility ? "warn" : "fail", message(error), "Fix the task source before launching a run."));
|
|
407
476
|
}
|
|
408
477
|
try {
|
|
409
478
|
const runs = await request("/api/runs?limit=200");
|
|
@@ -414,14 +483,7 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
414
483
|
}
|
|
415
484
|
}
|
|
416
485
|
if ((options.runtimeAdapter ?? "pi") === "pi") {
|
|
417
|
-
|
|
418
|
-
ok: false,
|
|
419
|
-
label: "pi/pi-rig checks",
|
|
420
|
-
hint: message(error)
|
|
421
|
-
}]);
|
|
422
|
-
for (const pi of piChecks) {
|
|
423
|
-
checks.push(preflightCheck(pi.label === "pi" ? "pi" : "pi-rig", pi.label, pi.ok ? "pass" : "fail", pi.detail, pi.hint ?? (pi.ok ? undefined : "Run `rig init --yes` to install/update Pi and enable pi-rig.")));
|
|
424
|
-
}
|
|
486
|
+
checks.push(preflightCheck("runtime", "worker Pi SDK session daemon", "pass", selectedServer?.connectionKind === "remote" ? "remote worker-owned runtime" : "bundled server-owned runtime"));
|
|
425
487
|
} else {
|
|
426
488
|
checks.push(preflightCheck("runtime", "runtime adapter", "pass", options.runtimeAdapter));
|
|
427
489
|
}
|
|
@@ -429,9 +491,9 @@ async function runFastTaskRunPreflight(context, options = {}) {
|
|
|
429
491
|
if (failures.length > 0) {
|
|
430
492
|
const summary = failures.map((check) => `${check.label}${check.detail ? `: ${check.detail}` : ""}`).join("; ");
|
|
431
493
|
if (failures.some((check) => check.id === "duplicate-active-run") && taskId) {
|
|
432
|
-
throw new
|
|
494
|
+
throw new CliError(`Task ${taskId} already has an active Rig run. ${summary}`, 1, { hint: `Attach to it with \`rig run attach <run-id> --follow\`, or stop it first with \`rig run stop <run-id>\`.` });
|
|
433
495
|
}
|
|
434
|
-
throw new
|
|
496
|
+
throw new CliError(`Task run preflight failed: ${summary}`, 1, { hint: "Run `rig doctor` to diagnose, then retry `rig task run`." });
|
|
435
497
|
}
|
|
436
498
|
return { ok: true, checks };
|
|
437
499
|
}
|
|
@@ -448,7 +510,7 @@ async function runProjectMainSyncPreflight(context, options) {
|
|
|
448
510
|
runBootstrap: async () => {
|
|
449
511
|
const bootstrap = await context.runCommand(["bun", "run", "bootstrap"]);
|
|
450
512
|
if (bootstrap.exitCode !== 0) {
|
|
451
|
-
throw new
|
|
513
|
+
throw new CliError(bootstrap.stderr || bootstrap.stdout || "bun run bootstrap failed during project pre-run sync", bootstrap.exitCode || 1);
|
|
452
514
|
}
|
|
453
515
|
}
|
|
454
516
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runQuietBinaryProbe(binaryPath: string, args: string[], cwd: string): Promise<boolean>;
|