void 0.10.3 → 0.10.5

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.
Files changed (37) hide show
  1. package/dist/{auth-cmd-CblKzAdD.mjs → auth-cmd-UoCwfDQv.mjs} +2 -2
  2. package/dist/{build-cmd-CJQSRupz.mjs → build-cmd-CVyuBHjP.mjs} +1 -1
  3. package/dist/{cache-CMzjhxHh.mjs → cache-ACORQCIq.mjs} +1 -1
  4. package/dist/{cancel-deploy-S2N8NSv6.mjs → cancel-deploy-Gi8zmoZC.mjs} +1 -1
  5. package/dist/cli/cli.mjs +102 -39
  6. package/dist/{client-YIzxv2ZO.mjs → client-BlbnA92X.mjs} +44 -0
  7. package/dist/{config--wXmVOoe.mjs → config-CbJS4Krx.mjs} +3 -3
  8. package/dist/{create-project-pLQdzEPe.mjs → create-project-LjvkYziS.mjs} +1 -1
  9. package/dist/{db-hAmBumot.mjs → db-C00yjN01.mjs} +17 -14
  10. package/dist/{delete-CfSe3EJO.mjs → delete-CplfvTgU.mjs} +1 -1
  11. package/dist/{deploy-DpOdwtla.mjs → deploy-CISNL5uK.mjs} +28 -30
  12. package/dist/{discover-C8N1I4tK.mjs → discover-ZWuBvt22.mjs} +1 -1
  13. package/dist/{domain-CkowysQx.mjs → domain-B4HpcgNG.mjs} +1 -1
  14. package/dist/{env-CIwG2y7G.mjs → env-DNMJpeVF.mjs} +3 -3
  15. package/dist/{env-types-CaJaIRXU.mjs → env-types-BS8cZead.mjs} +1 -1
  16. package/dist/{env-validation-LH6eoyW-.mjs → env-validation-BxbtBbjh.mjs} +1 -1
  17. package/dist/{gen-q6McxXgE.mjs → gen-C0pV2gRQ.mjs} +2 -2
  18. package/dist/{github-cmd-D3L0f93X.mjs → github-cmd-CwtLD1yI.mjs} +273 -15
  19. package/dist/index.mjs +11 -11
  20. package/dist/{init-CZP69LJu.mjs → init-D58GxCtt.mjs} +7 -7
  21. package/dist/{link-BKLElphS.mjs → link-ZFmboqY3.mjs} +2 -2
  22. package/dist/{list-B7qXBBjo.mjs → list-CbNNjXTm.mjs} +1 -1
  23. package/dist/{login-B3l_sDwT.mjs → login-CvxxF_9z.mjs} +1 -1
  24. package/dist/{logs-AXm660MB.mjs → logs-BDZ9AuhM.mjs} +1 -1
  25. package/dist/{mcp-D2plINxM.mjs → mcp-C8BjRt_z.mjs} +1 -1
  26. package/dist/{plugin-inference-C3fLzFvP.mjs → plugin-inference-D04iL1mW.mjs} +1 -1
  27. package/dist/{prepare-DRRboRqH.mjs → prepare-DUo9q8cM.mjs} +6 -6
  28. package/dist/{preset-B7ZQZn0u.mjs → preset-D0My64KQ.mjs} +1 -1
  29. package/dist/{project-cmd-InegJruy.mjs → project-cmd-Dd1gNRdd.mjs} +8 -8
  30. package/dist/{project-tsconfig-BTNuNoJ0.mjs → project-tsconfig-DMBV55K2.mjs} +2 -2
  31. package/dist/{rollback-CHVE-DeT.mjs → rollback-DzmCCrFg.mjs} +1 -1
  32. package/dist/{route-types-D03ryMXz.mjs → route-types-BpSJEW20.mjs} +1 -1
  33. package/dist/{scan-DGEp1-1Q.mjs → scan---8wfN58.mjs} +1 -1
  34. package/dist/{secret-CVKwIOm3.mjs → secret-CMLYWkE-.mjs} +1 -1
  35. package/dist/sveltekit.mjs +1 -1
  36. package/package.json +2 -2
  37. package/skills/void/docs/reference/cli.md +37 -1
@@ -36,7 +36,7 @@ function discoverSchema(root, paths = readProjectPaths(root)) {
36
36
  */
37
37
  function schemaSpecifier(location, root) {
38
38
  if (!root) return location.type === "file" ? "./db/schema.ts" : "./db/schema/*.ts";
39
- const rel = relative(root, location.path).replace(/\\/g, "/");
39
+ const rel = relative(root, location.path);
40
40
  return location.type === "file" ? `./${rel}` : `./${rel}/*.ts`;
41
41
  }
42
42
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { s as import_picocolors } from "./output-DeiS4oEX.mjs";
2
- import { f as getToken, n as PlatformClient } from "./client-YIzxv2ZO.mjs";
2
+ import { f as getToken, n as PlatformClient } from "./client-BlbnA92X.mjs";
3
3
  import { c as R, u as Se } from "./dist-abzUneor.mjs";
4
4
  import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
5
5
  //#region src/cli/domain.ts
@@ -1,10 +1,10 @@
1
1
  import { a as join } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
2
2
  import { n as cliTitle } from "./output-DeiS4oEX.mjs";
3
- import { f as getToken, n as PlatformClient } from "./client-YIzxv2ZO.mjs";
3
+ import { f as getToken, n as PlatformClient } from "./client-BlbnA92X.mjs";
4
4
  import { c as R, g as ge, u as Se, y as ye } from "./dist-abzUneor.mjs";
5
5
  import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
6
- import { r as generateEnvTypes, t as findEnvFile } from "./env-types-CaJaIRXU.mjs";
7
- import { a as loadUserEnvFile, i as validateProdEnv, n as formatEnvReport, t as fetchRemoteSecretNames } from "./env-validation-LH6eoyW-.mjs";
6
+ import { r as generateEnvTypes, t as findEnvFile } from "./env-types-BS8cZead.mjs";
7
+ import { a as loadUserEnvFile, i as validateProdEnv, n as formatEnvReport, t as fetchRemoteSecretNames } from "./env-validation-BxbtBbjh.mjs";
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
9
  //#region src/cli/env-example.ts
10
10
  /**
@@ -31,7 +31,7 @@ function findEnvFileForPaths(root, paths) {
31
31
  * @param outDir Absolute path to the project's `.void/` directory.
32
32
  */
33
33
  function generateEnvTypes(envFileAbs, outDir) {
34
- const importPath = relative(outDir, envFileAbs).replace(/\\/g, "/");
34
+ const importPath = relative(outDir, envFileAbs);
35
35
  const lines = [];
36
36
  lines.push("// Auto-generated by void — do not edit");
37
37
  lines.push("export {}");
@@ -1,7 +1,7 @@
1
1
  import { a as join } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
2
2
  import { i as voidWarn } from "./log-BdD_Fpms.mjs";
3
3
  import { n as mergeAndExpand, r as parseDotenvFile } from "./dotenv-lS94ymhM.mjs";
4
- import { t as findEnvFile } from "./env-types-CaJaIRXU.mjs";
4
+ import { t as findEnvFile } from "./env-types-BS8cZead.mjs";
5
5
  import { pathToFileURL } from "node:url";
6
6
  //#region src/host/load-user-env.ts
7
7
  /**
@@ -2,9 +2,9 @@ import { a as join, n as dirname, s as relative } from "./pathe.M-eThtNZ-BrPhGF_
2
2
  import { n as cliTitle } from "./output-DeiS4oEX.mjs";
3
3
  import { n as readProjectPaths } from "./project-paths-CCMrHYQm.mjs";
4
4
  import { _ as me, c as R, g as ge, i as Ee, x as q, y as ye } from "./dist-abzUneor.mjs";
5
- import { n as ensureProjectTsconfig } from "./project-tsconfig-BTNuNoJ0.mjs";
5
+ import { n as ensureProjectTsconfig } from "./project-tsconfig-DMBV55K2.mjs";
6
6
  import { c as getDatabaseDialect, f as readConfig } from "./config-8dLIngKW.mjs";
7
- import { n as writeDrizzleConfig } from "./config--wXmVOoe.mjs";
7
+ import { n as writeDrizzleConfig } from "./config-CbJS4Krx.mjs";
8
8
  import { r as readJournal, t as collectMigrations } from "./collect-9rO3JFNM.mjs";
9
9
  import { n as listDrizzleArtifacts, t as assertJournalCoherence } from "./validate-CNWm-PsL.mjs";
10
10
  import { t as buildDrizzleKitSpawnEnv } from "./yarn-pnp-WLW2IHUY.mjs";
@@ -1,5 +1,5 @@
1
1
  import { s as import_picocolors } from "./output-DeiS4oEX.mjs";
2
- import { c as createCodeChallenge, f as getToken, h as openBrowser, l as createCodeVerifier, n as PlatformClient, o as parsePlatformErrorBody, s as awaitBrowserCallback, t as PlatformApiError } from "./client-YIzxv2ZO.mjs";
2
+ import { c as createCodeChallenge, f as getToken, h as openBrowser, l as createCodeVerifier, n as PlatformClient, o as parsePlatformErrorBody, s as awaitBrowserCallback, t as PlatformApiError } from "./client-BlbnA92X.mjs";
3
3
  import { _ as me, c as R, i as Ee, l as Re, x as q } from "./dist-abzUneor.mjs";
4
4
  import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
5
5
  import { r as resolveProjectBySlug, t as getRequestedProjectSlug } from "./resolve-project-D4O1_fZz.mjs";
@@ -7,6 +7,20 @@ import { t as collectGitDeployMetadata } from "./git-metadata-Ce0AtSZL.mjs";
7
7
  import { randomBytes } from "node:crypto";
8
8
  //#region src/cli/github-cmd.ts
9
9
  /**
10
+ * The workflow file `void init --github` scaffolds and the platform default for
11
+ * a connection's OIDC deploy pin. Kept in sync with the server default
12
+ * (`github_connections.workflow_path`) and the `init.ts` scaffolder.
13
+ */
14
+ const DEFAULT_WORKFLOW_PATH = ".github/workflows/void-deploy.yml";
15
+ /**
16
+ * Client-side mirror of the server's `isValidWorkflowPath` (packages/api
17
+ * routes/github.ts). Used to reject a bad path at the interactive prompt before
18
+ * an API round-trip. The server re-validates, so this is DX only, not security.
19
+ */
20
+ function isValidWorkflowPath(path) {
21
+ return path.length > 0 && path.startsWith(".github/workflows/") && (path.endsWith(".yml") || path.endsWith(".yaml")) && !path.includes("..");
22
+ }
23
+ /**
10
24
  * Map a PlatformApiError status + error code string to a friendly user message.
11
25
  * Returns null for unrecognised cases (caller should re-throw).
12
26
  */
@@ -62,6 +76,22 @@ function formatInstallations(installations) {
62
76
  return header;
63
77
  }).join("\n\n");
64
78
  }
79
+ /**
80
+ * Render a GitHub connection as an aligned key/value block for `void github
81
+ * status`. The workflow file is the OIDC deploy pin — it is only read for
82
+ * github_actions builds (container builds never use it), so it is annotated as
83
+ * unused when the executor is container to avoid implying it is active.
84
+ */
85
+ function formatConnection(connection) {
86
+ const row = (label, value) => ` ${label.padEnd(16)}${value}`;
87
+ const workflow = connection.build_executor === "github_actions" ? connection.workflow_path : `${connection.workflow_path} (unused for container builds)`;
88
+ return [
89
+ row("Repository", connection.repo_full_name),
90
+ row("Branch", connection.branch),
91
+ row("Build executor", connection.build_executor),
92
+ row("Deploy workflow", workflow)
93
+ ].join("\n");
94
+ }
65
95
  async function runGithubCommand(root, args) {
66
96
  const token = getToken(root);
67
97
  if (!token) {
@@ -74,7 +104,9 @@ async function runGithubCommand(root, args) {
74
104
  case "installations": return runInstallations(client);
75
105
  case "connect": return runConnect(root, client, args);
76
106
  case "update": return runGithubUpdate(root, client, args);
107
+ case "status": return runStatus(root, client, args);
77
108
  case "link": return runGithubLink(root, client);
109
+ case "join": return runGithubJoin(root, client);
78
110
  }
79
111
  }
80
112
  /**
@@ -156,6 +188,49 @@ async function runGithubLink(root, client) {
156
188
  if (status === "already_linked") R.success("GitHub account already linked.");
157
189
  else R.success("GitHub account linked.");
158
190
  }
191
+ /**
192
+ * Discover and join the shared GitHub App installations owned by the
193
+ * organizations the logged-in GitHub identity belongs to. Drives the same
194
+ * 3-endpoint localhost + PKCE handshake as {@link runGithubLink}, but against
195
+ * the platform's JOIN routes: membership upsert is idempotent, so unlike link
196
+ * there is no 409/already-linked conflict to map. The only special error is the
197
+ * fail-closed 503 raised when org-installation sharing is not configured on the
198
+ * instance.
199
+ */
200
+ async function runGithubJoin(root, client) {
201
+ const verifier = createCodeVerifier();
202
+ const challenge = createCodeChallenge(verifier);
203
+ const state = randomBytes(32).toString("hex");
204
+ let count;
205
+ try {
206
+ ({count} = await awaitBrowserCallback({
207
+ state,
208
+ buildAuthUrl: async (callback) => {
209
+ callback.searchParams.set("state", state);
210
+ const { url } = await client.getGithubJoinUrl(callback.href, challenge);
211
+ return url;
212
+ },
213
+ describeError: (p) => p.get("error") ? "GitHub authorization failed." : null,
214
+ onUrl: (url) => R.message(`Open this URL to join your organization's shared GitHub App installations:\n${import_picocolors.default.cyan(url)}`),
215
+ finalize: async (params) => {
216
+ const joinCode = params.get("join_code");
217
+ if (!joinCode) throw new Error("github: join callback did not return a join code.");
218
+ return client.completeGithubJoin(joinCode, verifier);
219
+ }
220
+ }));
221
+ } catch (err) {
222
+ if (err instanceof PlatformApiError && err.status === 503 && err.code === "github_oauth_not_configured") {
223
+ R.error("Organization installation sharing is not enabled on this Void instance.");
224
+ process.exit(1);
225
+ }
226
+ throw err;
227
+ }
228
+ if (count === 0) R.info("No new installations to join.");
229
+ else {
230
+ R.success(`Joined ${count} installation${count === 1 ? "" : "s"}.`);
231
+ R.message("Run `void github installations` to see them, then `void github connect`.");
232
+ }
233
+ }
159
234
  async function runInstallations(client) {
160
235
  const installations = await client.listGithubInstallations();
161
236
  if (installations.length === 0) {
@@ -164,6 +239,50 @@ async function runInstallations(client) {
164
239
  }
165
240
  R.message(formatInstallations(installations));
166
241
  }
242
+ /**
243
+ * Per-repo connect authorization round-trip for a non-owner org member.
244
+ *
245
+ * When the connect route returns 403 `connect_authorization_required` (Task 8),
246
+ * the member has no proof they can access the specific repo under the shared
247
+ * installation. This drives the same localhost + PKCE handshake as
248
+ * {@link runGithubLink}/{@link runGithubJoin}, but against the platform's
249
+ * connect-authorize route (Task 7): the callback mints and returns the grant IN
250
+ * the redirect URL (`?connect_grant=…`), so there is no separate `/complete`
251
+ * POST — `finalize` just reads the grant out of the callback query. A
252
+ * `?error=repo_not_accessible` callback is mapped to a friendly per-repo message
253
+ * by `describeError`, which rejects out of the handshake (no grant, no retry).
254
+ *
255
+ * Returns the grant plus the PKCE verifier the caller must echo to the connect
256
+ * route so the platform can bind the grant to this exchange.
257
+ */
258
+ async function runGithubConnectAuthorize(client, installationId, repoFullName) {
259
+ const verifier = createCodeVerifier();
260
+ const challenge = createCodeChallenge(verifier);
261
+ const state = randomBytes(32).toString("hex");
262
+ const { connectGrant } = await awaitBrowserCallback({
263
+ state,
264
+ buildAuthUrl: async (callback) => {
265
+ callback.searchParams.set("state", state);
266
+ const { url } = await client.getGithubConnectAuthorizeUrl(callback.href, challenge, installationId, repoFullName);
267
+ return url;
268
+ },
269
+ describeError: (p) => {
270
+ const e = p.get("error");
271
+ if (e === "repo_not_accessible") return `You don't have access to ${repoFullName} under this installation on GitHub.`;
272
+ return e ? "GitHub authorization failed." : null;
273
+ },
274
+ onUrl: (url) => R.message(`Open this URL to authorize access to ${repoFullName}:\n${import_picocolors.default.cyan(url)}`),
275
+ finalize: async (params) => {
276
+ const grant = params.get("connect_grant");
277
+ if (!grant) throw new Error("github: connect authorization did not return a grant.");
278
+ return { connectGrant: grant };
279
+ }
280
+ });
281
+ return {
282
+ connectGrant,
283
+ codeVerifier: verifier
284
+ };
285
+ }
167
286
  async function runConnect(root, client, args) {
168
287
  const isTTY = process.stdin.isTTY === true;
169
288
  let projectId;
@@ -294,18 +413,80 @@ async function runConnect(root, client, args) {
294
413
  }
295
414
  branch = input.trim() || branchDefault;
296
415
  }
297
- const executor = args.executor ?? "container";
416
+ let executor;
417
+ if (args.executor !== void 0) executor = args.executor;
418
+ else if (!isTTY) executor = "container";
419
+ else {
420
+ const picked = await Ee({
421
+ message: "Build executor:",
422
+ options: [{
423
+ value: "container",
424
+ label: "Cloudflare Container",
425
+ hint: "build on Void (default)"
426
+ }, {
427
+ value: "github_actions",
428
+ label: "GitHub Actions",
429
+ hint: "build in your repo's runner"
430
+ }],
431
+ initialValue: "container"
432
+ });
433
+ if (q(picked)) {
434
+ me("Cancelled.");
435
+ process.exit(0);
436
+ }
437
+ executor = picked;
438
+ }
439
+ let workflowPath = args.workflowPath;
440
+ if (executor === "github_actions" && args.workflowPath === void 0 && isTTY) {
441
+ const input = await Re({
442
+ message: "Deploy workflow file:",
443
+ placeholder: DEFAULT_WORKFLOW_PATH,
444
+ initialValue: DEFAULT_WORKFLOW_PATH,
445
+ validate(value) {
446
+ const v = (value ?? "").trim();
447
+ if (v.length === 0 || isValidWorkflowPath(v)) return;
448
+ return "Must be under .github/workflows/, end in .yml/.yaml, and not contain \"..\".";
449
+ }
450
+ });
451
+ if (q(input)) {
452
+ me("Cancelled.");
453
+ process.exit(0);
454
+ }
455
+ const chosen = input.trim() || ".github/workflows/void-deploy.yml";
456
+ if (chosen !== ".github/workflows/void-deploy.yml") workflowPath = chosen;
457
+ }
458
+ const attemptConnect = (extra) => client.connectGithub(projectId, {
459
+ installationId: installation.installation_id,
460
+ repoFullName,
461
+ branch,
462
+ buildExecutor: executor,
463
+ workflowPath,
464
+ ...extra
465
+ });
298
466
  let connection;
299
467
  try {
300
- connection = await client.connectGithub(projectId, {
301
- installationId: installation.installation_id,
302
- repoFullName,
303
- branch,
304
- buildExecutor: executor,
305
- workflowPath: args.workflowPath
306
- });
468
+ connection = await attemptConnect();
307
469
  } catch (err) {
308
- if (err instanceof PlatformApiError) {
470
+ if (!(err instanceof PlatformApiError)) throw err;
471
+ if (err.status === 403 && err.code === "connect_authorization_required") {
472
+ if (!isTTY) {
473
+ R.error("connect: this installation requires per-repo authorization, which needs an interactive browser sign-in. Run `void github connect` locally to authorize, then retry.");
474
+ process.exit(1);
475
+ }
476
+ const grant = await runGithubConnectAuthorize(client, installation.installation_id, repoFullName);
477
+ try {
478
+ connection = await attemptConnect({
479
+ connectGrant: grant.connectGrant,
480
+ codeVerifier: grant.codeVerifier
481
+ });
482
+ } catch (retryErr) {
483
+ if (retryErr instanceof PlatformApiError && retryErr.status === 403) {
484
+ R.error("connect: GitHub authorization failed or expired. Run `void github connect` again.");
485
+ process.exit(1);
486
+ }
487
+ throw retryErr;
488
+ }
489
+ } else {
309
490
  const body = parsePlatformErrorBody(err.body);
310
491
  const code = body?.error ?? body?.message ?? "";
311
492
  const message = describeConnectError(err.status, code);
@@ -315,10 +496,9 @@ async function runConnect(root, client, args) {
315
496
  }
316
497
  throw err;
317
498
  }
318
- throw err;
319
499
  }
320
- if (args.workflowPath !== void 0 && connection.workflow_path !== args.workflowPath) {
321
- R.error(`connect: server did not apply --workflow ${args.workflowPath} (got ${connection.workflow_path ?? "none"}). The platform may be too old to support a custom workflow path.`);
500
+ if (workflowPath !== void 0 && connection.workflow_path !== workflowPath) {
501
+ R.error(`connect: server did not apply workflow path ${workflowPath} (got ${connection.workflow_path ?? "none"}). The platform may be too old to support a custom workflow path.`);
322
502
  process.exit(1);
323
503
  }
324
504
  R.success(`Connected ${repoFullName} (${branch}) → project. Push to ${branch} to deploy.`);
@@ -381,6 +561,7 @@ async function runGithubUpdate(root, client, args) {
381
561
  }
382
562
  let branch;
383
563
  let executor;
564
+ let workflowPath = args.workflowPath ?? current.workflow_path;
384
565
  if (isTTY) {
385
566
  if (args.branch !== void 0) branch = args.branch;
386
567
  else {
@@ -414,6 +595,23 @@ async function runGithubUpdate(root, client, args) {
414
595
  }
415
596
  executor = picked;
416
597
  }
598
+ if (executor === "github_actions" && args.workflowPath === void 0) {
599
+ const input = await Re({
600
+ message: "Deploy workflow file:",
601
+ placeholder: current.workflow_path,
602
+ initialValue: current.workflow_path,
603
+ validate(value) {
604
+ const v = (value ?? "").trim();
605
+ if (v.length === 0 || isValidWorkflowPath(v)) return;
606
+ return "Must be under .github/workflows/, end in .yml/.yaml, and not contain \"..\".";
607
+ }
608
+ });
609
+ if (q(input)) {
610
+ me("Cancelled.");
611
+ process.exit(0);
612
+ }
613
+ workflowPath = input.trim() || current.workflow_path;
614
+ }
417
615
  } else {
418
616
  branch = args.branch ?? current.branch;
419
617
  executor = args.executor ?? current.build_executor;
@@ -421,7 +619,7 @@ async function runGithubUpdate(root, client, args) {
421
619
  const patch = {};
422
620
  if (branch !== current.branch) patch.branch = branch;
423
621
  if (executor !== current.build_executor) patch.buildExecutor = executor;
424
- if (args.workflowPath !== void 0 && args.workflowPath !== current.workflow_path) patch.workflowPath = args.workflowPath;
622
+ if (workflowPath !== current.workflow_path) patch.workflowPath = workflowPath;
425
623
  if (patch.branch === void 0 && patch.buildExecutor === void 0 && patch.workflowPath === void 0) {
426
624
  R.info(`No changes. ${current.repo_full_name} already deploys from ${current.branch} via ${current.build_executor}.`);
427
625
  return;
@@ -443,10 +641,70 @@ async function runGithubUpdate(root, client, args) {
443
641
  throw err;
444
642
  }
445
643
  if (patch.workflowPath !== void 0 && updated.workflow_path !== patch.workflowPath) {
446
- R.error(`update: server did not apply --workflow ${patch.workflowPath} (got ${updated.workflow_path ?? "none"}). The platform may be too old to support a custom workflow path.`);
644
+ R.error(`update: server did not apply workflow path ${patch.workflowPath} (got ${updated.workflow_path ?? "none"}). The platform may be too old to support a custom workflow path.`);
447
645
  process.exit(1);
448
646
  }
449
647
  R.success(`Updated ${updated.repo_full_name} → branch ${updated.branch}, executor ${updated.build_executor}, workflow ${updated.workflow_path}.`);
450
648
  }
649
+ /**
650
+ * `void github status [project]` — print the current GitHub connection (repo,
651
+ * branch, build executor, deploy workflow). Read-only; never mutates. Project
652
+ * resolution mirrors connect/update; a 404 means the project was never
653
+ * connected and points the user at `connect`.
654
+ */
655
+ async function runStatus(root, client, args) {
656
+ const isTTY = process.stdin.isTTY === true;
657
+ let projectId;
658
+ const requestedSlug = getRequestedProjectSlug({ projectSlug: args.projectName || void 0 });
659
+ if (requestedSlug) {
660
+ const resolved = resolveProjectBySlug(await client.listProjects(), requestedSlug);
661
+ if (!resolved) {
662
+ R.error(`status: No project found with slug '${requestedSlug}'.`);
663
+ process.exit(1);
664
+ }
665
+ projectId = resolved.projectId;
666
+ } else {
667
+ const linked = readProjectConfig(root);
668
+ if (linked) projectId = linked.projectId;
669
+ else if (!isTTY) {
670
+ R.error("status: No project specified. Pass a project name or --project <name>, set VOID_PROJECT, or link one with `void project link`.");
671
+ process.exit(1);
672
+ } else {
673
+ const projects = await client.listProjects();
674
+ if (projects.length === 0) {
675
+ R.error("No projects found. Create one first with `void deploy`.");
676
+ process.exit(1);
677
+ }
678
+ const picked = await Ee({
679
+ message: "Select a project:",
680
+ options: projects.map((p) => ({
681
+ value: p.id,
682
+ label: p.slug
683
+ }))
684
+ });
685
+ if (q(picked)) {
686
+ me("Cancelled.");
687
+ process.exit(0);
688
+ }
689
+ projectId = picked;
690
+ }
691
+ }
692
+ let connection;
693
+ try {
694
+ connection = await client.getGithubConnection(projectId);
695
+ } catch (err) {
696
+ if (err instanceof PlatformApiError) {
697
+ const body = parsePlatformErrorBody(err.body);
698
+ const code = body?.error ?? body?.message ?? "";
699
+ const message = describeUpdateError(err.status, code);
700
+ if (message !== null) {
701
+ R.error(`status: ${message}`);
702
+ process.exit(1);
703
+ }
704
+ }
705
+ throw err;
706
+ }
707
+ R.message(formatConnection(connection));
708
+ }
451
709
  //#endregion
452
710
  export { runGithubCommand };
package/dist/index.mjs CHANGED
@@ -1,22 +1,22 @@
1
1
  import { r as __toESM } from "./chunk-DJd-R1mw.mjs";
2
2
  import { a as join, n as dirname, s as relative } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
3
3
  import { u as require_picocolors } from "./output-DeiS4oEX.mjs";
4
- import { C as hasApiRoutes, S as resolveSandboxConfig, T as resolveAssetRoutingPolicy, _ as readWranglerCompat, a as prerenderPagesNode, b as SANDBOX_MIGRATION_TAG, c as stripInternalEnvKeys, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as readExistingLiveBindingConfig, h as normalizeWranglerCompatibilityFlags, i as prerenderPages, l as LIVE_MIGRATION_TAG, m as nextDurableObjectMigrationTag, n as resolveDistDir, o as filterLoadedEnv, p as hasDurableObjectSqliteMigration, r as resolveWorkerDirName, s as resolveRemoteMode, u as ensureWranglerCompat, v as resolveLiveBindingConfig, w as hasDocumentRoutes, x as isSandboxEnabled, y as syncWranglerBindings } from "./deploy-DpOdwtla.mjs";
4
+ import { C as hasApiRoutes, S as resolveSandboxConfig, T as resolveAssetRoutingPolicy, _ as readWranglerCompat, a as prerenderPagesNode, b as SANDBOX_MIGRATION_TAG, c as stripInternalEnvKeys, d as ensureWranglerCompatibilityDate, f as ensureWranglerNodejsAlsFlag, g as readExistingLiveBindingConfig, h as normalizeWranglerCompatibilityFlags, i as prerenderPages, l as LIVE_MIGRATION_TAG, m as nextDurableObjectMigrationTag, n as resolveDistDir, o as filterLoadedEnv, p as hasDurableObjectSqliteMigration, r as resolveWorkerDirName, s as resolveRemoteMode, u as ensureWranglerCompat, v as resolveLiveBindingConfig, w as hasDocumentRoutes, x as isSandboxEnabled, y as syncWranglerBindings } from "./deploy-CISNL5uK.mjs";
5
5
  import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-CCMrHYQm.mjs";
6
6
  import { n as loadVoidAuthConfig, t as findVoidAuthConfig } from "./config-hhMYPVRT.mjs";
7
- import { f as getToken, m as isStagingMode } from "./client-YIzxv2ZO.mjs";
7
+ import { f as getToken, m as isStagingMode } from "./client-BlbnA92X.mjs";
8
8
  import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
9
- import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-BTNuNoJ0.mjs";
9
+ import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-DMBV55K2.mjs";
10
10
  import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-8dLIngKW.mjs";
11
- import { a as scanQueuesSync, i as scanWebSocketRoutesSync, n as scanRoutePatternsSync, o as scanJobsSync, r as scanRoutes, t as scanMiddlewareSync } from "./scan-DGEp1-1Q.mjs";
12
- import { a as SSR_CLIENT_ENTRY_RELATIVE_PATHS, c as validateSsrEntries, f as isIdentifier, g as walk, i as inferProjectBindings, l as validateSsrEntry, m as isMemberExpression, n as FRAMEWORK_SCAN_DIRS, o as SSR_SERVER_ENTRY_RELATIVE_PATHS, p as isLiteral, r as detectFramework, t as DEFAULT_SCAN_DIRS, u as inferBindingsFromSource } from "./plugin-inference-C3fLzFvP.mjs";
11
+ import { a as scanQueuesSync, i as scanWebSocketRoutesSync, n as scanRoutePatternsSync, o as scanJobsSync, r as scanRoutes, t as scanMiddlewareSync } from "./scan---8wfN58.mjs";
12
+ import { a as SSR_CLIENT_ENTRY_RELATIVE_PATHS, c as validateSsrEntries, f as isIdentifier, g as walk, i as inferProjectBindings, l as validateSsrEntry, m as isMemberExpression, n as FRAMEWORK_SCAN_DIRS, o as SSR_SERVER_ENTRY_RELATIVE_PATHS, p as isLiteral, r as detectFramework, t as DEFAULT_SCAN_DIRS, u as inferBindingsFromSource } from "./plugin-inference-D04iL1mW.mjs";
13
13
  import { i as voidWarn, n as voidError, r as voidLog, t as setShowTimestamp } from "./log-BdD_Fpms.mjs";
14
14
  import { r as parseDotenvFile } from "./dotenv-lS94ymhM.mjs";
15
- import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-D03ryMXz.mjs";
16
- import { t as discoverSchema } from "./discover-C8N1I4tK.mjs";
15
+ import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-BpSJEW20.mjs";
16
+ import { t as discoverSchema } from "./discover-ZWuBvt22.mjs";
17
17
  import { t as MagicString } from "./magic-string.es-C1Fb0uxq.mjs";
18
- import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-CaJaIRXU.mjs";
19
- import { a as loadUserEnvFile } from "./env-validation-LH6eoyW-.mjs";
18
+ import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-BS8cZead.mjs";
19
+ import { a as loadUserEnvFile } from "./env-validation-BxbtBbjh.mjs";
20
20
  import { i as resolveLayoutIds } from "./scan-VCAM1oh3.mjs";
21
21
  import { n as STAGING_PROXY_URL, t as DEFAULT_PROXY_URL } from "./proxy-M3pxItg2.mjs";
22
22
  import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-BNWymgnH.mjs";
@@ -4916,7 +4916,7 @@ const STAGING_API_URL = "https://api.staging.void.cloud";
4916
4916
  */
4917
4917
  async function runAiSetupPrompt(root, token, staging, server) {
4918
4918
  const { select, text, isCancel, log } = await import("./dist-abzUneor.mjs").then((n) => n.p);
4919
- const { PlatformClient } = await import("./client-YIzxv2ZO.mjs").then((n) => n.r);
4919
+ const { PlatformClient } = await import("./client-BlbnA92X.mjs").then((n) => n.r);
4920
4920
  const { writeProjectConfig } = await import("./project-CH9pdo16.mjs").then((n) => n.n);
4921
4921
  const { inferDefaultSlug } = await import("./resolve-project-D4O1_fZz.mjs").then((n) => n.i);
4922
4922
  const client = new PlatformClient(token, staging ? STAGING_API_URL : DEFAULT_API_URL);
@@ -4945,7 +4945,7 @@ async function runAiSetupPrompt(root, token, staging, server) {
4945
4945
  let slug;
4946
4946
  if (result === "__create__") {
4947
4947
  const { validateProjectSlug } = await import("./project-slug-rX3kTOfY.mjs").then((n) => n.t);
4948
- const { createProjectWithSlugRetry } = await import("./create-project-pLQdzEPe.mjs").then((n) => n.t);
4948
+ const { createProjectWithSlugRetry } = await import("./create-project-LjvkYziS.mjs").then((n) => n.t);
4949
4949
  let created;
4950
4950
  try {
4951
4951
  created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
@@ -1,19 +1,19 @@
1
1
  import { a as join, c as resolve, i as isAbsolute, n as dirname, s as relative, t as basename } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
2
2
  import { n as cliTitle } from "./output-DeiS4oEX.mjs";
3
3
  import { n as readProjectPaths } from "./project-paths-CCMrHYQm.mjs";
4
- import { f as getToken, m as isStagingMode, n as PlatformClient } from "./client-YIzxv2ZO.mjs";
4
+ import { f as getToken, m as isStagingMode, n as PlatformClient } from "./client-BlbnA92X.mjs";
5
5
  import { _ as me, c as R, g as ge, i as Ee, l as Re, u as Se, v as ue, x as q, y as ye } from "./dist-abzUneor.mjs";
6
6
  import { a as writeProjectConfig, r as readProjectConfig } from "./project-CH9pdo16.mjs";
7
- import { n as ensureProjectTsconfig, t as createProjectTsconfig } from "./project-tsconfig-BTNuNoJ0.mjs";
7
+ import { n as ensureProjectTsconfig, t as createProjectTsconfig } from "./project-tsconfig-DMBV55K2.mjs";
8
8
  import { c as getDatabaseDialect, f as readConfig } from "./config-8dLIngKW.mjs";
9
- import { r as detectFramework } from "./plugin-inference-C3fLzFvP.mjs";
9
+ import { r as detectFramework } from "./plugin-inference-D04iL1mW.mjs";
10
10
  import { r as parseDotenvFile, t as expandDotenv } from "./dotenv-lS94ymhM.mjs";
11
11
  import { t as MagicString } from "./magic-string.es-C1Fb0uxq.mjs";
12
- import { n as writeDrizzleConfig } from "./config--wXmVOoe.mjs";
12
+ import { n as writeDrizzleConfig } from "./config-CbJS4Krx.mjs";
13
13
  import { n as getAgentById, r as getPackageDir, t as detectAgents } from "./agents-MhSzNMiC.mjs";
14
- import { n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-pLQdzEPe.mjs";
15
- import { t as promptForLoginToken } from "./login-B3l_sDwT.mjs";
16
- import { r as formatProjectCommand } from "./preset-B7ZQZn0u.mjs";
14
+ import { n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-LjvkYziS.mjs";
15
+ import { t as promptForLoginToken } from "./login-CvxxF_9z.mjs";
16
+ import { r as formatProjectCommand } from "./preset-D0My64KQ.mjs";
17
17
  import { n as findInstalledYarnPnpRuntime, r as hasLocalLockfile, t as buildDrizzleKitSpawnEnv } from "./yarn-pnp-WLW2IHUY.mjs";
18
18
  import { createRequire } from "node:module";
19
19
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmdirSync, unlinkSync, writeFileSync } from "node:fs";
@@ -1,8 +1,8 @@
1
1
  import { n as cliTitle, s as import_picocolors } from "./output-DeiS4oEX.mjs";
2
- import { d as getSiteDomain, f as getToken, n as PlatformClient } from "./client-YIzxv2ZO.mjs";
2
+ import { d as getSiteDomain, f as getToken, n as PlatformClient } from "./client-BlbnA92X.mjs";
3
3
  import { _ as me, c as R, g as ge, y as ye } from "./dist-abzUneor.mjs";
4
4
  import { a as writeProjectConfig, r as readProjectConfig } from "./project-CH9pdo16.mjs";
5
- import { n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-pLQdzEPe.mjs";
5
+ import { n as promptAndCreateProject, r as promptProjectSelection } from "./create-project-LjvkYziS.mjs";
6
6
  import { r as resolveProjectBySlug } from "./resolve-project-D4O1_fZz.mjs";
7
7
  //#region src/cli/link.ts
8
8
  async function runLink(root, slug) {
@@ -1,5 +1,5 @@
1
1
  import { a as ensureUtc, i as dim, l as tablePrefix, n as cliTitle, s as import_picocolors } from "./output-DeiS4oEX.mjs";
2
- import { f as getToken, n as PlatformClient } from "./client-YIzxv2ZO.mjs";
2
+ import { f as getToken, n as PlatformClient } from "./client-BlbnA92X.mjs";
3
3
  import { c as R, g as ge, y as ye } from "./dist-abzUneor.mjs";
4
4
  import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
5
5
  import { r as resolveProjectBySlug } from "./resolve-project-D4O1_fZz.mjs";
@@ -1,5 +1,5 @@
1
1
  import { n as cliTitle, r as createSpinner, s as import_picocolors } from "./output-DeiS4oEX.mjs";
2
- import { _ as runLogin, a as isExpiredTokenError, f as getToken, g as removeToken, m as isStagingMode, n as PlatformClient, p as getTokenSource, v as saveToken } from "./client-YIzxv2ZO.mjs";
2
+ import { _ as runLogin, a as isExpiredTokenError, f as getToken, g as removeToken, m as isStagingMode, n as PlatformClient, p as getTokenSource, v as saveToken } from "./client-BlbnA92X.mjs";
3
3
  import { _ as me, c as R, g as ge, i as Ee, x as q, y as ye } from "./dist-abzUneor.mjs";
4
4
  //#region src/cli/login.ts
5
5
  async function promptForLoginToken(root = process.cwd()) {
@@ -1,5 +1,5 @@
1
1
  import { i as dim, n as cliTitle, s as import_picocolors } from "./output-DeiS4oEX.mjs";
2
- import { f as getToken, n as PlatformClient } from "./client-YIzxv2ZO.mjs";
2
+ import { f as getToken, n as PlatformClient } from "./client-BlbnA92X.mjs";
3
3
  import { c as R, g as ge, y as ye } from "./dist-abzUneor.mjs";
4
4
  import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
5
5
  //#region src/cli/logs.ts
@@ -1,5 +1,5 @@
1
1
  import { a as join, n as dirname } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
2
- import { f as getToken, n as PlatformClient } from "./client-YIzxv2ZO.mjs";
2
+ import { f as getToken, n as PlatformClient } from "./client-BlbnA92X.mjs";
3
3
  import { a as writeProjectConfig } from "./project-CH9pdo16.mjs";
4
4
  import { existsSync, readFileSync, readdirSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
@@ -359,7 +359,7 @@ const SSR_SERVER_ENTRY_RELATIVE_PATHS = ["src/main.ssr.ts", "src/main.ssr.tsx"];
359
359
  const SSR_CLIENT_ENTRY_RELATIVE_PATHS = ["src/main.client.ts", "src/main.client.tsx"];
360
360
  function resolveSingleEntry(root, entries, label, paths = readProjectPaths(root)) {
361
361
  const matches = entries.map((entryName) => ({
362
- relativePath: relative(root, join(paths.ssrDir, entryName)).replace(/\\/g, "/"),
362
+ relativePath: relative(root, join(paths.ssrDir, entryName)),
363
363
  absolutePath: join(paths.ssrDir, entryName)
364
364
  })).filter((entry) => existsSync(entry.absolutePath));
365
365
  if (matches.length === 0) return null;
@@ -3,13 +3,13 @@ import { n as cliTitle } from "./output-DeiS4oEX.mjs";
3
3
  import { i as resolveProjectPaths } from "./project-paths-CCMrHYQm.mjs";
4
4
  import { n as loadVoidAuthConfig, t as findVoidAuthConfig } from "./config-hhMYPVRT.mjs";
5
5
  import { c as R, g as ge, u as Se, y as ye } from "./dist-abzUneor.mjs";
6
- import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-BTNuNoJ0.mjs";
6
+ import { n as ensureProjectTsconfig, r as writeFileIfChanged } from "./project-tsconfig-DMBV55K2.mjs";
7
7
  import { c as getDatabaseDialect, f as readConfig } from "./config-8dLIngKW.mjs";
8
- import { r as scanRoutes } from "./scan-DGEp1-1Q.mjs";
9
- import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework } from "./plugin-inference-C3fLzFvP.mjs";
10
- import { i as generateDbDts, n as generateQueueTypes, t as generateRouteTypes } from "./route-types-D03ryMXz.mjs";
11
- import { t as discoverSchema } from "./discover-C8N1I4tK.mjs";
12
- import { r as generateEnvTypes, t as findEnvFile } from "./env-types-CaJaIRXU.mjs";
8
+ import { r as scanRoutes } from "./scan---8wfN58.mjs";
9
+ import { i as inferProjectBindings, n as FRAMEWORK_SCAN_DIRS, r as detectFramework } from "./plugin-inference-D04iL1mW.mjs";
10
+ import { i as generateDbDts, n as generateQueueTypes, t as generateRouteTypes } from "./route-types-BpSJEW20.mjs";
11
+ import { t as discoverSchema } from "./discover-ZWuBvt22.mjs";
12
+ import { r as generateEnvTypes, t as findEnvFile } from "./env-types-BS8cZead.mjs";
13
13
  import { n as generateVoidAuthDrizzleSchema, t as generateVoidAuthDatabaseModule } from "./drizzle-DqstQTGq.mjs";
14
14
  import { existsSync, mkdirSync, rmSync } from "node:fs";
15
15
  import { fileURLToPath } from "node:url";
@@ -1,6 +1,6 @@
1
1
  import { a as join, c as resolve } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
2
2
  import { n as readProjectPaths } from "./project-paths-CCMrHYQm.mjs";
3
- import { s as hasSsrEntry } from "./plugin-inference-C3fLzFvP.mjs";
3
+ import { s as hasSsrEntry } from "./plugin-inference-D04iL1mW.mjs";
4
4
  import { i as readPackageJson, n as hasPackageDependency, t as getPackageDependencies } from "./package-json-Bg_GJdJB.mjs";
5
5
  import { existsSync } from "node:fs";
6
6
  //#region src/cli/preset.ts