@h-rig/init-plugin 0.0.6-alpha.156 → 0.0.6-alpha.158

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.
@@ -1,121 +0,0 @@
1
- import { spawnSync } from "node:child_process";
2
- import { type GitHubAuthStatus, type GitHubUserFetcher } from "@rig/github-provider-plugin";
3
- import { type PlacementKind } from "@rig/runtime/control-plane/placement";
4
- export type RigConfigStatus = {
5
- readonly exists: boolean;
6
- readonly valid: boolean;
7
- readonly path: string;
8
- readonly slug: string | null;
9
- readonly reason?: string;
10
- };
11
- export type RigStateStatus = {
12
- readonly valid: boolean;
13
- readonly selected: string | null;
14
- readonly project: string | null;
15
- readonly reason?: string;
16
- };
17
- export type RigAuthValidation = {
18
- readonly ok: boolean;
19
- readonly source: "stored-token" | "gh" | "missing";
20
- readonly login?: string | null;
21
- readonly detail: string;
22
- readonly status?: GitHubAuthStatus;
23
- };
24
- export type RigSetupStatus = {
25
- readonly configured: boolean;
26
- readonly projectRoot: string;
27
- readonly slug: string | null;
28
- readonly config: RigConfigStatus;
29
- readonly state: RigStateStatus;
30
- readonly auth: RigAuthValidation;
31
- readonly reasons: readonly string[];
32
- };
33
- export type RigSetupPlacement = {
34
- readonly alias: string;
35
- readonly kind: PlacementKind;
36
- readonly host?: string;
37
- readonly port?: number;
38
- readonly token?: string | null;
39
- };
40
- export type RigSetupDeps = {
41
- readonly spawn?: typeof spawnSync;
42
- readonly fetch?: typeof fetch;
43
- readonly fetchUser?: GitHubUserFetcher;
44
- };
45
- export type RunSetupInput = {
46
- readonly projectRoot: string;
47
- readonly slug: string;
48
- readonly placement: RigSetupPlacement;
49
- readonly rewriteConfig?: boolean;
50
- readonly ensurePi?: boolean;
51
- readonly githubToken?: string | null;
52
- readonly importGhToken?: boolean;
53
- readonly ensureLabels?: boolean;
54
- readonly deps?: RigSetupDeps;
55
- };
56
- export declare function parseRepoSlugFromRemote(remoteUrl: string): string | null;
57
- export declare function parseRepoSlug(value: string): {
58
- owner: string;
59
- repo: string;
60
- slug: string;
61
- };
62
- export declare function detectOriginRepoSlug(projectRoot: string, deps?: RigSetupDeps): string | null;
63
- export declare function ensureRigPrivateDirs(projectRoot: string): void;
64
- export declare function ensureGitignoreEntries(projectRoot: string): void;
65
- export declare function writeRigConnectionState(projectRoot: string, slug: string, placement: RigSetupPlacement): void;
66
- export declare function writeRigConfig(projectRoot: string, slug: string): void;
67
- export declare function readRigConfigStatus(projectRoot: string): RigConfigStatus;
68
- export declare function readRigConnectionStatus(projectRoot: string): RigStateStatus;
69
- export declare function detectGhAuth(projectRoot: string, slug: string, deps?: RigSetupDeps): RigAuthValidation | null;
70
- export declare function validateGitHubAuth(projectRoot: string, slug: string | null, deps?: RigSetupDeps): Promise<RigAuthValidation>;
71
- export declare function saveGitHubTokenLocally(projectRoot: string, token: string, slug: string, deps?: RigSetupDeps): Promise<void>;
72
- export declare function readGhAuthToken(projectRoot: string, deps?: RigSetupDeps): string;
73
- export declare function ensureGitHubAuth(input: {
74
- projectRoot: string;
75
- slug: string;
76
- token?: string | null;
77
- importGhToken?: boolean;
78
- deps?: RigSetupDeps;
79
- }): Promise<RigAuthValidation>;
80
- export declare function ensureGitHubLabels(input: {
81
- projectRoot: string;
82
- slug: string;
83
- token?: string | null;
84
- deps?: RigSetupDeps;
85
- }): Promise<{
86
- ok: true;
87
- method: "api" | "gh";
88
- labels: readonly string[];
89
- }>;
90
- export declare function ensurePiRigInstalledForSetup(projectRoot: string, deps?: RigSetupDeps): {
91
- ok: true;
92
- detail: string;
93
- };
94
- export declare function detectRigStartupStatus(input: {
95
- projectRoot: string;
96
- deps?: RigSetupDeps;
97
- }): Promise<RigSetupStatus>;
98
- export declare const detectStartupStatus: typeof detectRigStartupStatus;
99
- export declare function applyRigSetupProject(input: {
100
- projectRoot: string;
101
- slug: string;
102
- placement: RigSetupPlacement;
103
- rewriteConfig: boolean;
104
- ensurePi?: boolean;
105
- ensureLabels?: boolean;
106
- deps?: RigSetupDeps;
107
- }): Promise<{
108
- repoSlug: string;
109
- placement: string;
110
- configWritten: boolean;
111
- labels: unknown;
112
- pi: unknown;
113
- }>;
114
- export declare function runSetup(input: RunSetupInput): Promise<{
115
- repoSlug: string;
116
- placement: string;
117
- configWritten: boolean;
118
- labels: unknown;
119
- pi: unknown;
120
- status: RigSetupStatus;
121
- }>;
package/dist/src/setup.js DELETED
@@ -1,458 +0,0 @@
1
- // @bun
2
- // packages/init-plugin/src/setup.ts
3
- import { spawnSync } from "child_process";
4
- import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
5
- import { dirname, resolve } from "path";
6
- import {
7
- createGitHubAuthStore,
8
- probeGitHubRepository,
9
- resolveGitHubAuthStatus,
10
- saveGitHubTokenForProject
11
- } from "@rig/github-provider-plugin";
12
- import { addPlacement, selectPlacement } from "@rig/runtime/control-plane/placement";
13
-
14
- // packages/init-plugin/src/rig-init-builder.ts
15
- function buildRigInitConfigSource(input) {
16
- const lines = [`import { defineConfig } from "@rig/core/config";`];
17
- if (input.useStandardPlugin) {
18
- lines.push(`import { standardPlugins } from "@rig/standard-plugin/bundle";`);
19
- if (input.taskSource.kind === "github-issues") {
20
- lines.push(`import { createStateGitHubCredentialProvider } from "@rig/standard-plugin";`);
21
- }
22
- }
23
- lines.push(``, `export default defineConfig({`);
24
- const projectRepo = input.projectRepo ?? (input.taskSource.kind === "github-issues" ? `${input.taskSource.owner}/${input.taskSource.repo}` : undefined);
25
- lines.push(projectRepo ? ` project: { name: ${JSON.stringify(input.projectName)}, repo: ${JSON.stringify(projectRepo)} },` : ` project: { name: ${JSON.stringify(input.projectName)} },`);
26
- if (input.useStandardPlugin && input.taskSource.kind === "github-issues") {
27
- lines.push(` plugins: [...standardPlugins({`);
28
- lines.push(` taskSources: {`);
29
- lines.push(` githubCredentialProvider: createStateGitHubCredentialProvider(),`);
30
- lines.push(` githubWorkspaceId: ${JSON.stringify(`${input.taskSource.owner}/${input.taskSource.repo}`)},`);
31
- lines.push(` githubUserId: process.env.RIG_GITHUB_USER_ID ?? "server-selected-user",`);
32
- lines.push(` },`);
33
- lines.push(` })],`);
34
- } else {
35
- lines.push(` plugins: [${input.useStandardPlugin ? "...standardPlugins()" : ""}],`);
36
- }
37
- if (input.taskSource.kind === "github-issues") {
38
- lines.push(` taskSource: {`);
39
- lines.push(` kind: "github-issues",`);
40
- lines.push(` owner: ${JSON.stringify(input.taskSource.owner)},`);
41
- lines.push(` repo: ${JSON.stringify(input.taskSource.repo)},`);
42
- lines.push(` // labels: ["task"], // uncomment to filter by labels`);
43
- lines.push(` state: "open",`);
44
- if (input.taskSource.assignee?.trim()) {
45
- lines.push(` options: { assignee: ${JSON.stringify(input.taskSource.assignee.trim())} },`);
46
- }
47
- lines.push(` },`);
48
- } else {
49
- lines.push(` taskSource: {`);
50
- lines.push(` kind: "files",`);
51
- lines.push(` path: ${JSON.stringify(input.taskSource.path)},`);
52
- lines.push(` },`);
53
- }
54
- lines.push(` workspace: { mainRepo: ".", isolation: "worktree" },`);
55
- const sshTarget = input.sshTarget?.trim();
56
- lines.push(sshTarget ? ` runtime: { harness: "pi", mode: "yolo", server: { sshTarget: ${JSON.stringify(sshTarget)} } },` : ` runtime: { harness: "pi", mode: "yolo" }, // server.sshTarget unset = local placement`);
57
- lines.push(` planning: { mode: "auto" },`);
58
- lines.push(` github: {`);
59
- lines.push(` issueUpdates: "lifecycle",`);
60
- lines.push(` projects: { enabled: false },`);
61
- lines.push(` },`);
62
- lines.push(` automation: { maxValidationAttempts: 30, maxPrFixIterations: 100500 },`);
63
- lines.push(` pr: { mode: "auto", watchChecks: true, autoFixChecks: true, autoFixReview: true },`);
64
- lines.push(` merge: { mode: "auto", method: "repo-default", deleteBranch: "repo-default", bypass: false },`);
65
- lines.push(` issueAnalysis: { enabled: true, harness: "pi", mode: "continuous" },`);
66
- lines.push(`});`);
67
- lines.push(``);
68
- return lines.join(`
69
- `);
70
- }
71
-
72
- // packages/init-plugin/src/setup.ts
73
- var RIG_LABELS_TO_ENSURE = [
74
- "rig:running",
75
- "rig:pr-open",
76
- "rig:ci-fixing",
77
- "rig:merging",
78
- "rig:done",
79
- "rig:needs-attention",
80
- "rig:ready",
81
- "rig:blocked",
82
- "rig:generated"
83
- ];
84
- var RIG_LABEL_METADATA = {
85
- "rig:running": { color: "1d76db", description: "Rig is actively working on this issue." },
86
- "rig:pr-open": { color: "5319e7", description: "Rig opened a pull request for this issue." },
87
- "rig:ci-fixing": { color: "fbca04", description: "Rig is fixing CI or review feedback for this issue." },
88
- "rig:merging": { color: "0052cc", description: "Rig is merging the completed change for this issue." },
89
- "rig:done": { color: "0e8a16", description: "Rig completed this issue." },
90
- "rig:needs-attention": { color: "d93f0b", description: "Rig needs operator attention for this issue." },
91
- "rig:ready": { color: "0e8a16", description: "Rig issue analysis marked this issue ready." },
92
- "rig:blocked": { color: "d93f0b", description: "Rig issue analysis found blockers for this issue." },
93
- "rig:generated": { color: "c5def5", description: "Rig generated this follow-up issue." }
94
- };
95
- function cleanString(value) {
96
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
97
- }
98
- function parseRepoSlugFromRemote(remoteUrl) {
99
- const trimmed = remoteUrl.trim();
100
- const match = trimmed.match(/github\.com[:/]([^/\s]+)\/([^/\s.]+)(?:\.git)?$/i);
101
- return match ? `${match[1]}/${match[2]}` : null;
102
- }
103
- function parseRepoSlug(value) {
104
- const match = value.trim().match(/^([^/\s]+)\/([^/\s]+)$/);
105
- if (!match)
106
- throw new Error(`Invalid GitHub repo slug "${value}". Expected owner/repo.`);
107
- return { owner: match[1], repo: match[2], slug: `${match[1]}/${match[2]}` };
108
- }
109
- function runSyncCommand(command, input = {}) {
110
- const executable = command[0];
111
- if (!executable)
112
- throw new Error("command is required");
113
- return (input.spawn ?? spawnSync)(executable, [...command.slice(1)], {
114
- cwd: input.cwd,
115
- encoding: "utf8",
116
- timeout: input.timeoutMs ?? 1e4,
117
- env: input.env ?? process.env
118
- });
119
- }
120
- function detectOriginRepoSlug(projectRoot, deps = {}) {
121
- const result = runSyncCommand(["git", "-C", projectRoot, "remote", "get-url", "origin"], { timeoutMs: 5000, spawn: deps.spawn });
122
- if (result.status !== 0 || result.error)
123
- return null;
124
- return parseRepoSlugFromRemote(result.stdout.trim());
125
- }
126
- function connectionStatePath(projectRoot) {
127
- return resolve(projectRoot, ".rig", "state", "connection.json");
128
- }
129
- function projectLinkStatePath(projectRoot) {
130
- return resolve(projectRoot, ".rig", "state", "project-link.json");
131
- }
132
- function readJsonRecord(path) {
133
- if (!existsSync(path))
134
- return null;
135
- try {
136
- const parsed = JSON.parse(readFileSync(path, "utf-8"));
137
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
138
- } catch {
139
- return null;
140
- }
141
- }
142
- function writeJsonFile(path, value) {
143
- mkdirSync(dirname(path), { recursive: true });
144
- writeFileSync(path, `${JSON.stringify(value, null, 2)}
145
- `, "utf-8");
146
- }
147
- function ensureRigPrivateDirs(projectRoot) {
148
- mkdirSync(resolve(projectRoot, ".rig", "state"), { recursive: true });
149
- mkdirSync(resolve(projectRoot, ".rig", "logs"), { recursive: true });
150
- mkdirSync(resolve(projectRoot, ".rig", "runs"), { recursive: true });
151
- mkdirSync(resolve(projectRoot, ".rig", "tmp"), { recursive: true });
152
- mkdirSync(resolve(projectRoot, "artifacts"), { recursive: true });
153
- const taskConfigPath = resolve(projectRoot, ".rig", "task-config.json");
154
- if (!existsSync(taskConfigPath))
155
- writeFileSync(taskConfigPath, `{}
156
- `, "utf-8");
157
- }
158
- function ensureGitignoreEntries(projectRoot) {
159
- const path = resolve(projectRoot, ".gitignore");
160
- const existing = existsSync(path) ? readFileSync(path, "utf-8") : "";
161
- const lines = new Set(existing.split(/\r?\n/));
162
- const missing = [".rig/state/", ".rig/logs/", ".rig/runs/", ".rig/tmp/"].filter((entry) => !lines.has(entry));
163
- if (missing.length === 0)
164
- return;
165
- const prefix = existing.length > 0 && !existing.endsWith(`
166
- `) ? `
167
- ` : "";
168
- appendFileSync(path, `${prefix}${missing.join(`
169
- `)}
170
- `, "utf-8");
171
- }
172
- function writeRigConnectionState(projectRoot, slug, placement) {
173
- const previous = readJsonRecord(connectionStatePath(projectRoot)) ?? {};
174
- writeJsonFile(connectionStatePath(projectRoot), {
175
- ...previous,
176
- selected: placement.alias,
177
- project: slug,
178
- linkedAt: new Date().toISOString()
179
- });
180
- writeJsonFile(projectLinkStatePath(projectRoot), {
181
- repoSlug: slug,
182
- connection: placement.alias,
183
- linkedAt: new Date().toISOString()
184
- });
185
- if (placement.alias === "local")
186
- delete process.env.RIG_REMOTE_ALIAS;
187
- else
188
- process.env.RIG_REMOTE_ALIAS = placement.alias;
189
- }
190
- function writeRigConfig(projectRoot, slug) {
191
- const repo = parseRepoSlug(slug);
192
- writeFileSync(resolve(projectRoot, "rig.config.ts"), buildRigInitConfigSource({
193
- projectName: repo.slug,
194
- projectRepo: repo.slug,
195
- taskSource: { kind: "github-issues", owner: repo.owner, repo: repo.repo },
196
- useStandardPlugin: true
197
- }), "utf-8");
198
- }
199
- function readRigConfigStatus(projectRoot) {
200
- const path = resolve(projectRoot, "rig.config.ts");
201
- if (!existsSync(path))
202
- return { exists: false, valid: false, path, slug: null, reason: "missing rig.config.ts" };
203
- try {
204
- const source = readFileSync(path, "utf-8");
205
- const owner = source.match(/\bowner:\s*["']([^"']+)["']/)?.[1] ?? null;
206
- const repoValues = [...source.matchAll(/\brepo:\s*["']([^"']+)["']/g)].map((match) => match[1]).filter(Boolean);
207
- const taskRepo = repoValues.find((value) => !value.includes("/")) ?? null;
208
- const projectRepo = repoValues.find((value) => value.includes("/")) ?? null;
209
- const githubIssues = /\bkind:\s*["']github-issues["']/.test(source);
210
- const slug = owner && taskRepo ? `${owner}/${taskRepo}` : projectRepo;
211
- if (!githubIssues || !slug)
212
- return { exists: true, valid: false, path, slug: slug ?? null, reason: "rig.config.ts is not a GitHub Issues Rig config" };
213
- parseRepoSlug(slug);
214
- return { exists: true, valid: true, path, slug };
215
- } catch (error) {
216
- return { exists: true, valid: false, path, slug: null, reason: error instanceof Error ? error.message : String(error) };
217
- }
218
- }
219
- function readRigConnectionStatus(projectRoot) {
220
- const stateDir = resolve(projectRoot, ".rig", "state");
221
- if (!existsSync(stateDir))
222
- return { valid: false, selected: null, project: null, reason: "missing .rig/state" };
223
- const connection = readJsonRecord(connectionStatePath(projectRoot));
224
- if (!connection)
225
- return { valid: false, selected: null, project: null, reason: "missing or invalid .rig/state/connection.json" };
226
- const selected = cleanString(connection.selected);
227
- const project = cleanString(connection.project);
228
- if (!selected)
229
- return { valid: false, selected: null, project, reason: "connection.json is missing selected placement" };
230
- if (!project)
231
- return { valid: false, selected, project: null, reason: "connection.json is missing project slug" };
232
- try {
233
- parseRepoSlug(project);
234
- } catch (error) {
235
- return { valid: false, selected, project, reason: error instanceof Error ? error.message : String(error) };
236
- }
237
- return { valid: true, selected, project };
238
- }
239
- function detectGhAuth(projectRoot, slug, deps = {}) {
240
- const user = runSyncCommand(["gh", "api", "user", "--jq", ".login"], { cwd: projectRoot, timeoutMs: 5000, spawn: deps.spawn });
241
- if (user.status !== 0 || user.error || !user.stdout.trim())
242
- return null;
243
- const repo = runSyncCommand(["gh", "repo", "view", slug, "--json", "nameWithOwner", "--jq", ".nameWithOwner"], { cwd: projectRoot, timeoutMs: 5000, spawn: deps.spawn });
244
- if (repo.status !== 0 || repo.error)
245
- return { ok: false, source: "gh", login: user.stdout.trim(), detail: (repo.stderr || repo.stdout || "gh cannot access the selected repository").trim() };
246
- return { ok: true, source: "gh", login: user.stdout.trim(), detail: "gh CLI authentication can access the selected repository" };
247
- }
248
- async function validateGitHubAuth(projectRoot, slug, deps = {}) {
249
- if (!slug)
250
- return { ok: false, source: "missing", detail: "GitHub repo slug is unknown" };
251
- const status = resolveGitHubAuthStatus({ projectRoot, oauthConfigured: Boolean(process.env.RIG_GITHUB_OAUTH_CLIENT_ID?.trim()) });
252
- if (status.signedIn) {
253
- const store = createGitHubAuthStore(projectRoot);
254
- if (!status.selectedRepo) {
255
- store.saveSelectedRepo(slug);
256
- return { ok: true, source: "stored-token", login: status.login, detail: "stored Rig GitHub token selected for this repo", status: store.status({ oauthConfigured: status.oauthConfigured }) };
257
- }
258
- if (status.selectedRepo !== slug)
259
- return { ok: false, source: "stored-token", login: status.login, detail: `stored GitHub token is scoped to ${status.selectedRepo}, not ${slug}`, status };
260
- return { ok: true, source: "stored-token", login: status.login, detail: "stored Rig GitHub token is present", status };
261
- }
262
- const gh = detectGhAuth(projectRoot, slug, deps);
263
- if (gh)
264
- return gh;
265
- return { ok: false, source: "missing", detail: "Sign in with `gh auth login`, choose Setup \u2192 GitHub auth, or paste a token." };
266
- }
267
- async function saveGitHubTokenLocally(projectRoot, token, slug, deps = {}) {
268
- ensureRigPrivateDirs(projectRoot);
269
- await saveGitHubTokenForProject({
270
- projectRoot,
271
- token,
272
- tokenSource: "manual-token",
273
- selectedRepo: slug,
274
- ...deps.fetchUser ? { fetchUser: deps.fetchUser } : {}
275
- });
276
- createGitHubAuthStore(projectRoot).copyToLocalProjectRoot(projectRoot);
277
- }
278
- function readGhAuthToken(projectRoot, deps = {}) {
279
- const result = runSyncCommand(["gh", "auth", "token"], { cwd: projectRoot, timeoutMs: 1e4, spawn: deps.spawn });
280
- if (result.status !== 0 || result.error || !result.stdout.trim())
281
- throw new Error((result.stderr || result.stdout || "Could not read GitHub token from `gh auth token`.").trim());
282
- return result.stdout.trim();
283
- }
284
- async function ensureGitHubAuth(input) {
285
- const current = await validateGitHubAuth(input.projectRoot, input.slug, input.deps);
286
- if (current.ok && !input.token && !input.importGhToken)
287
- return current;
288
- if (input.token?.trim())
289
- await saveGitHubTokenLocally(input.projectRoot, input.token.trim(), input.slug, input.deps);
290
- else if (input.importGhToken)
291
- await saveGitHubTokenLocally(input.projectRoot, readGhAuthToken(input.projectRoot, input.deps), input.slug, input.deps);
292
- return validateGitHubAuth(input.projectRoot, input.slug, input.deps);
293
- }
294
- async function ensureGitHubLabels(input) {
295
- const repo = parseRepoSlug(input.slug);
296
- const token = input.token?.trim() || createGitHubAuthStore(input.projectRoot).readToken();
297
- if (token) {
298
- const fetchLabels = input.deps?.fetch ?? fetch;
299
- for (const name of RIG_LABELS_TO_ENSURE) {
300
- const metadata = RIG_LABEL_METADATA[name];
301
- const response = await fetchLabels(`https://api.github.com/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/labels`, {
302
- method: "POST",
303
- headers: {
304
- accept: "application/vnd.github+json",
305
- authorization: `Bearer ${token}`,
306
- "content-type": "application/json",
307
- "user-agent": "rig"
308
- },
309
- body: JSON.stringify({ name, color: metadata.color, description: metadata.description })
310
- });
311
- if (response.ok)
312
- continue;
313
- const text = await response.text().catch(() => "");
314
- if (response.status === 422 && /already_exists|already exists|exists/i.test(text))
315
- continue;
316
- throw new Error(`Could not create GitHub label ${name}: ${response.status} ${text || response.statusText}`);
317
- }
318
- return { ok: true, method: "api", labels: RIG_LABELS_TO_ENSURE };
319
- }
320
- const gh = detectGhAuth(input.projectRoot, input.slug, input.deps);
321
- if (!gh?.ok)
322
- throw new Error("GitHub labels require a stored Rig token or gh auth.");
323
- for (const name of RIG_LABELS_TO_ENSURE) {
324
- const metadata = RIG_LABEL_METADATA[name];
325
- const result = runSyncCommand(["gh", "label", "create", name, "--repo", input.slug, "--color", metadata.color, "--description", metadata.description, "--force"], { cwd: input.projectRoot, timeoutMs: 1e4, spawn: input.deps?.spawn });
326
- if (result.status !== 0 || result.error)
327
- throw new Error(`gh label create ${name} failed: ${(result.stderr || result.stdout || result.error?.message || "unknown error").trim()}`);
328
- }
329
- return { ok: true, method: "gh", labels: RIG_LABELS_TO_ENSURE };
330
- }
331
- function piListContainsRigExtension(output) {
332
- return output.split(/\r?\n/).some((line) => line.includes("@h-rig/pi-rig") || /(?:^|[\\/])packages[\\/]pi-rig(?:$|\s)/.test(line));
333
- }
334
- function splitInstallCommand(value) {
335
- return value.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((part) => part.replace(/^["']|["']$/g, "")) ?? [];
336
- }
337
- function ensurePiRigInstalledForSetup(projectRoot, deps = {}) {
338
- if (process.env.RIG_TEST_FAKE_PI_INSTALL === "1")
339
- return { ok: true, detail: "fake-pi" };
340
- let version = runSyncCommand(["pi", "--version"], { cwd: projectRoot, timeoutMs: 1e4, spawn: deps.spawn });
341
- if (version.status !== 0 || version.error) {
342
- const installCommand = process.env.RIG_PI_INSTALL_COMMAND?.trim();
343
- if (!installCommand)
344
- throw new Error(`Pi/OMP is not available: ${(version.stderr || version.stdout || version.error?.message || "pi --version failed").trim()}. Install Pi/OMP or set RIG_PI_INSTALL_COMMAND.`);
345
- const parts = splitInstallCommand(installCommand);
346
- if (parts.length === 0)
347
- throw new Error("RIG_PI_INSTALL_COMMAND is empty.");
348
- const install = runSyncCommand(parts, { cwd: projectRoot, timeoutMs: 120000, spawn: deps.spawn });
349
- if (install.status !== 0 || install.error)
350
- throw new Error(`Pi/OMP install command failed: ${(install.stderr || install.stdout || install.error?.message || "unknown error").trim()}`);
351
- version = runSyncCommand(["pi", "--version"], { cwd: projectRoot, timeoutMs: 1e4, spawn: deps.spawn });
352
- if (version.status !== 0 || version.error)
353
- throw new Error(`Pi/OMP is still unavailable after install: ${(version.stderr || version.stdout || version.error?.message || "pi --version failed").trim()}`);
354
- }
355
- let list = runSyncCommand(["pi", "list"], { cwd: projectRoot, timeoutMs: 1e4, spawn: deps.spawn });
356
- if (!piListContainsRigExtension(`${list.stdout}
357
- ${list.stderr}`)) {
358
- const packageSource = existsSync(resolve(projectRoot, "packages", "pi-rig", "package.json")) ? resolve(projectRoot, "packages", "pi-rig") : "npm:@h-rig/pi-rig";
359
- const install = runSyncCommand(["pi", "install", packageSource], { cwd: projectRoot, timeoutMs: 120000, spawn: deps.spawn });
360
- if (install.status !== 0 || install.error)
361
- throw new Error(`Could not install/register the Rig OMP extension: ${(install.stderr || install.stdout || install.error?.message || "pi install failed").trim()}`);
362
- list = runSyncCommand(["pi", "list"], { cwd: projectRoot, timeoutMs: 1e4, spawn: deps.spawn });
363
- if (!piListContainsRigExtension(`${list.stdout}
364
- ${list.stderr}`))
365
- throw new Error("Pi/OMP is installed, but `pi list` does not show the Rig extension.");
366
- }
367
- return { ok: true, detail: (version.stdout || version.stderr).trim() || "pi available; rig extension registered" };
368
- }
369
- async function detectRigStartupStatus(input) {
370
- const projectRoot = input.projectRoot;
371
- const config = readRigConfigStatus(projectRoot);
372
- const state = readRigConnectionStatus(projectRoot);
373
- const detectedSlug = detectOriginRepoSlug(projectRoot, input.deps);
374
- const slug = config.slug ?? state.project ?? detectedSlug;
375
- const reasons = [];
376
- if (!detectedSlug)
377
- reasons.push("git origin does not point at a GitHub owner/repo remote");
378
- if (!config.exists || !config.valid)
379
- reasons.push(config.reason ?? "rig.config.ts is invalid");
380
- if (!state.valid)
381
- reasons.push(state.reason ?? ".rig/state/connection.json is invalid");
382
- if (config.slug && state.project && config.slug !== state.project)
383
- reasons.push(`rig.config.ts repo ${config.slug} does not match connection project ${state.project}`);
384
- if (slug && detectedSlug && slug !== detectedSlug)
385
- reasons.push(`configured repo ${slug} does not match git origin ${detectedSlug}`);
386
- const auth = await validateGitHubAuth(projectRoot, slug, input.deps);
387
- if (!auth.ok)
388
- reasons.push(auth.detail);
389
- return { configured: reasons.length === 0, projectRoot, slug, config, state, auth, reasons };
390
- }
391
- var detectStartupStatus = detectRigStartupStatus;
392
- async function applyRigSetupProject(input) {
393
- const repo = parseRepoSlug(input.slug);
394
- ensureRigPrivateDirs(input.projectRoot);
395
- ensureGitignoreEntries(input.projectRoot);
396
- if (input.placement.alias !== "local" && input.placement.host) {
397
- addPlacement(input.projectRoot, { alias: input.placement.alias, host: input.placement.host, port: input.placement.port, token: input.placement.token, select: true });
398
- } else {
399
- selectPlacement(input.projectRoot, input.placement.alias);
400
- }
401
- writeRigConnectionState(input.projectRoot, repo.slug, input.placement);
402
- if (input.rewriteConfig)
403
- writeRigConfig(input.projectRoot, repo.slug);
404
- const labels = input.ensureLabels === false ? { skipped: true } : await ensureGitHubLabels({ projectRoot: input.projectRoot, slug: repo.slug, deps: input.deps });
405
- const pi = input.ensurePi === false ? { skipped: true } : ensurePiRigInstalledForSetup(input.projectRoot, input.deps);
406
- return { repoSlug: repo.slug, placement: input.placement.alias, configWritten: input.rewriteConfig, labels, pi };
407
- }
408
- async function runSetup(input) {
409
- const repo = parseRepoSlug(input.slug);
410
- const auth = await ensureGitHubAuth({ projectRoot: input.projectRoot, slug: repo.slug, token: input.githubToken, importGhToken: input.importGhToken, deps: input.deps });
411
- if (!auth.ok)
412
- throw new Error(auth.detail);
413
- const token = createGitHubAuthStore(input.projectRoot).readToken();
414
- const probe = await probeGitHubRepository({
415
- owner: repo.owner,
416
- repo: repo.repo,
417
- token,
418
- scopes: auth.status?.scopes ?? [],
419
- ...input.deps?.fetch ? { fetchRepository: input.deps.fetch } : {}
420
- });
421
- if (!probe.ok)
422
- throw new Error(probe.message);
423
- const result = await applyRigSetupProject({
424
- projectRoot: input.projectRoot,
425
- slug: repo.slug,
426
- placement: input.placement,
427
- rewriteConfig: input.rewriteConfig ?? true,
428
- ensurePi: input.ensurePi,
429
- ensureLabels: input.ensureLabels,
430
- deps: input.deps
431
- });
432
- const status = await detectRigStartupStatus({ projectRoot: input.projectRoot, deps: input.deps });
433
- if (!status.configured)
434
- throw new Error(`Setup wrote state but doctor still reports: ${status.reasons.join("; ")}`);
435
- return { ...result, status };
436
- }
437
- export {
438
- writeRigConnectionState,
439
- writeRigConfig,
440
- validateGitHubAuth,
441
- saveGitHubTokenLocally,
442
- runSetup,
443
- readRigConnectionStatus,
444
- readRigConfigStatus,
445
- readGhAuthToken,
446
- parseRepoSlugFromRemote,
447
- parseRepoSlug,
448
- ensureRigPrivateDirs,
449
- ensurePiRigInstalledForSetup,
450
- ensureGitignoreEntries,
451
- ensureGitHubLabels,
452
- ensureGitHubAuth,
453
- detectStartupStatus,
454
- detectRigStartupStatus,
455
- detectOriginRepoSlug,
456
- detectGhAuth,
457
- applyRigSetupProject
458
- };