@devdogsuga/devtools 0.1.26 → 0.1.27

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 (271) hide show
  1. package/README.md +72 -11
  2. package/bin/devtools.mjs +4 -4
  3. package/dist/cli-CZKkNHK5.js +6619 -0
  4. package/dist/failure-log-BBPGejQv.js +256 -0
  5. package/dist/launch.js +267 -281
  6. package/dist/local-env-D8HC2JK_.js +351 -0
  7. package/dist/local-env-DrBCXqD8.js +2 -0
  8. package/dist/menu-CQBsQRgH.js +1684 -0
  9. package/dist/peer-redirect-hooks.js +15 -0
  10. package/dist/peers-RBplwovA.js +229 -0
  11. package/dist/root-C2OmZDwD.js +94 -0
  12. package/dist/stack-Bx-55oi_.js +2 -0
  13. package/dist/stack-BynCgRVS.js +181 -0
  14. package/dist/supabase-project-0knM0mqd.js +426 -0
  15. package/dist/telemetry-C4foD8MD.js +249 -0
  16. package/dist/telemetry.js +2 -148
  17. package/package.json +10 -29
  18. package/bin/devtools-ci-bare.mjs +0 -48
  19. package/bin/devtools-ci.mjs +0 -31
  20. package/dist/args.d.ts +0 -26
  21. package/dist/args.js +0 -78
  22. package/dist/bws/bw.d.ts +0 -18
  23. package/dist/bws/bw.js +0 -70
  24. package/dist/bws/client.d.ts +0 -43
  25. package/dist/bws/client.js +0 -363
  26. package/dist/bws/environments.d.ts +0 -96
  27. package/dist/bws/environments.js +0 -111
  28. package/dist/bws/pace.d.ts +0 -25
  29. package/dist/bws/pace.js +0 -43
  30. package/dist/bws/retry.d.ts +0 -12
  31. package/dist/bws/retry.js +0 -39
  32. package/dist/bws/store.d.ts +0 -1
  33. package/dist/bws/store.js +0 -32
  34. package/dist/bws/token.d.ts +0 -31
  35. package/dist/bws/token.js +0 -88
  36. package/dist/bws/vault.d.ts +0 -29
  37. package/dist/bws/vault.js +0 -200
  38. package/dist/catalog.d.ts +0 -57
  39. package/dist/catalog.js +0 -57
  40. package/dist/cf/build.d.ts +0 -13
  41. package/dist/cf/build.js +0 -65
  42. package/dist/cf/commands.d.ts +0 -1
  43. package/dist/cf/commands.js +0 -172
  44. package/dist/cf/local-env.d.ts +0 -58
  45. package/dist/cf/local-env.js +0 -177
  46. package/dist/ci.d.ts +0 -1
  47. package/dist/ci.js +0 -328
  48. package/dist/cli.d.ts +0 -11
  49. package/dist/cli.js +0 -1094
  50. package/dist/commands.d.ts +0 -261
  51. package/dist/commands.js +0 -1318
  52. package/dist/completions.d.ts +0 -4
  53. package/dist/completions.js +0 -110
  54. package/dist/cron/commands.d.ts +0 -47
  55. package/dist/cron/commands.js +0 -372
  56. package/dist/cron/discovery.d.ts +0 -55
  57. package/dist/cron/discovery.js +0 -85
  58. package/dist/cron/schema.d.ts +0 -45
  59. package/dist/cron/schema.js +0 -35
  60. package/dist/db/config-push.d.ts +0 -8
  61. package/dist/db/config-push.js +0 -11
  62. package/dist/db/connection.d.ts +0 -79
  63. package/dist/db/connection.js +0 -114
  64. package/dist/db/exec.d.ts +0 -1
  65. package/dist/db/exec.js +0 -11
  66. package/dist/db/generate-types.d.ts +0 -1
  67. package/dist/db/generate-types.js +0 -4
  68. package/dist/db/generated-env.d.ts +0 -69
  69. package/dist/db/generated-env.js +0 -113
  70. package/dist/db/introspect.d.ts +0 -1
  71. package/dist/db/introspect.js +0 -122
  72. package/dist/db/local-env.d.ts +0 -65
  73. package/dist/db/local-env.js +0 -23
  74. package/dist/db/new-migration.d.ts +0 -8
  75. package/dist/db/new-migration.js +0 -69
  76. package/dist/db/pooler.d.ts +0 -23
  77. package/dist/db/pooler.js +0 -55
  78. package/dist/db/run.d.ts +0 -68
  79. package/dist/db/run.js +0 -156
  80. package/dist/db/seed-buckets.d.ts +0 -2
  81. package/dist/db/seed-buckets.js +0 -4
  82. package/dist/db/seed-production.d.ts +0 -8
  83. package/dist/db/seed-production.js +0 -79
  84. package/dist/db/seed-roles.d.ts +0 -7
  85. package/dist/db/seed-roles.js +0 -33
  86. package/dist/db/session-refresh.d.ts +0 -43
  87. package/dist/db/session-refresh.js +0 -40
  88. package/dist/deploy/migrations.d.ts +0 -20
  89. package/dist/deploy/migrations.js +0 -68
  90. package/dist/deploy/orphans.d.ts +0 -20
  91. package/dist/deploy/orphans.js +0 -177
  92. package/dist/deploy/preflight.d.ts +0 -53
  93. package/dist/deploy/preflight.js +0 -189
  94. package/dist/deploy/report.d.ts +0 -26
  95. package/dist/deploy/report.js +0 -70
  96. package/dist/deploy/require-planner.d.ts +0 -2
  97. package/dist/deploy/require-planner.js +0 -60
  98. package/dist/deploy/require-token.d.ts +0 -15
  99. package/dist/deploy/require-token.js +0 -78
  100. package/dist/deploy/secrets-file.d.ts +0 -17
  101. package/dist/deploy/secrets-file.js +0 -166
  102. package/dist/deploy/write-env.d.ts +0 -40
  103. package/dist/deploy/write-env.js +0 -395
  104. package/dist/docs/index-pages.d.ts +0 -38
  105. package/dist/docs/index-pages.js +0 -186
  106. package/dist/emails/commands.d.ts +0 -15
  107. package/dist/emails/commands.js +0 -130
  108. package/dist/emails/email-types.d.ts +0 -13
  109. package/dist/emails/email-types.js +0 -1
  110. package/dist/emails/fixtures.d.ts +0 -20
  111. package/dist/emails/fixtures.js +0 -20
  112. package/dist/env/audit.d.ts +0 -206
  113. package/dist/env/audit.js +0 -436
  114. package/dist/env/cloudflare.d.ts +0 -21
  115. package/dist/env/cloudflare.js +0 -61
  116. package/dist/env/commands.d.ts +0 -99
  117. package/dist/env/commands.js +0 -613
  118. package/dist/env/discovery.d.ts +0 -15
  119. package/dist/env/discovery.js +0 -239
  120. package/dist/env/document.d.ts +0 -119
  121. package/dist/env/document.js +0 -323
  122. package/dist/env/example.d.ts +0 -68
  123. package/dist/env/example.js +0 -605
  124. package/dist/env/selection.d.ts +0 -105
  125. package/dist/env/selection.js +0 -228
  126. package/dist/env-entry.d.ts +0 -49
  127. package/dist/env-entry.js +0 -204
  128. package/dist/environment-doctor.d.ts +0 -45
  129. package/dist/environment-doctor.js +0 -429
  130. package/dist/environment.d.ts +0 -92
  131. package/dist/environment.js +0 -185
  132. package/dist/fingerprint.d.ts +0 -10
  133. package/dist/fingerprint.js +0 -18
  134. package/dist/gen/campus-map.d.ts +0 -1
  135. package/dist/gen/campus-map.js +0 -473
  136. package/dist/gen/commands.d.ts +0 -1
  137. package/dist/gen/commands.js +0 -25
  138. package/dist/gen/hypno.d.ts +0 -1
  139. package/dist/gen/hypno.js +0 -134
  140. package/dist/gh/client.d.ts +0 -80
  141. package/dist/gh/client.js +0 -259
  142. package/dist/gh/environments.d.ts +0 -83
  143. package/dist/gh/environments.js +0 -178
  144. package/dist/gh/rulesets/actors.d.ts +0 -14
  145. package/dist/gh/rulesets/actors.js +0 -69
  146. package/dist/gh/rulesets/api.d.ts +0 -15
  147. package/dist/gh/rulesets/api.js +0 -104
  148. package/dist/gh/rulesets/commands.d.ts +0 -3
  149. package/dist/gh/rulesets/commands.js +0 -203
  150. package/dist/gh/rulesets/desired.d.ts +0 -112
  151. package/dist/gh/rulesets/desired.js +0 -271
  152. package/dist/gh/rulesets/diff.d.ts +0 -65
  153. package/dist/gh/rulesets/diff.js +0 -170
  154. package/dist/gh/rulesets/types.d.ts +0 -76
  155. package/dist/gh/rulesets/types.js +0 -13
  156. package/dist/gh/settings/api.d.ts +0 -40
  157. package/dist/gh/settings/api.js +0 -180
  158. package/dist/gh/settings/commands.d.ts +0 -1
  159. package/dist/gh/settings/commands.js +0 -254
  160. package/dist/gh/settings/desired.d.ts +0 -53
  161. package/dist/gh/settings/desired.js +0 -43
  162. package/dist/gh/settings/diff.d.ts +0 -57
  163. package/dist/gh/settings/diff.js +0 -161
  164. package/dist/gh/settings/types.d.ts +0 -92
  165. package/dist/gh/settings/types.js +0 -11
  166. package/dist/gh/settings/workflows.d.ts +0 -41
  167. package/dist/gh/settings/workflows.js +0 -128
  168. package/dist/grantRoot.d.ts +0 -42
  169. package/dist/grantRoot.js +0 -89
  170. package/dist/help.d.ts +0 -18
  171. package/dist/help.js +0 -169
  172. package/dist/images/commands.d.ts +0 -32
  173. package/dist/images/commands.js +0 -231
  174. package/dist/images/events.d.ts +0 -47
  175. package/dist/images/events.js +0 -116
  176. package/dist/images/graphics.d.ts +0 -70
  177. package/dist/images/graphics.js +0 -234
  178. package/dist/images/open-graph-types.d.ts +0 -75
  179. package/dist/images/open-graph-types.js +0 -1
  180. package/dist/images/prompts.d.ts +0 -34
  181. package/dist/images/prompts.js +0 -100
  182. package/dist/images/render.d.ts +0 -21
  183. package/dist/images/render.js +0 -61
  184. package/dist/images/select.d.ts +0 -64
  185. package/dist/images/select.js +0 -96
  186. package/dist/instance.d.ts +0 -69
  187. package/dist/instance.js +0 -102
  188. package/dist/invocation.d.ts +0 -49
  189. package/dist/invocation.js +0 -78
  190. package/dist/launch-ci.d.ts +0 -1
  191. package/dist/launch-ci.js +0 -90
  192. package/dist/launch.d.ts +0 -18
  193. package/dist/menu.d.ts +0 -43
  194. package/dist/menu.js +0 -300
  195. package/dist/moderation.d.ts +0 -70
  196. package/dist/moderation.js +0 -128
  197. package/dist/oauth/browser.d.ts +0 -1
  198. package/dist/oauth/browser.js +0 -13
  199. package/dist/oauth/config.d.ts +0 -24
  200. package/dist/oauth/config.js +0 -24
  201. package/dist/oauth/connect-url.d.ts +0 -17
  202. package/dist/oauth/connect-url.js +0 -23
  203. package/dist/oauth/db.d.ts +0 -37
  204. package/dist/oauth/db.js +0 -108
  205. package/dist/oauth/device.d.ts +0 -93
  206. package/dist/oauth/device.js +0 -198
  207. package/dist/oauth/discovery.d.ts +0 -28
  208. package/dist/oauth/discovery.js +0 -54
  209. package/dist/oauth/env-file.d.ts +0 -6
  210. package/dist/oauth/env-file.js +0 -35
  211. package/dist/oauth/exchange.d.ts +0 -43
  212. package/dist/oauth/exchange.js +0 -93
  213. package/dist/oauth/label.d.ts +0 -1
  214. package/dist/oauth/label.js +0 -25
  215. package/dist/oauth/loopback.d.ts +0 -52
  216. package/dist/oauth/loopback.js +0 -168
  217. package/dist/oauth/pkce.d.ts +0 -6
  218. package/dist/oauth/pkce.js +0 -34
  219. package/dist/oauth/target.d.ts +0 -54
  220. package/dist/oauth/target.js +0 -102
  221. package/dist/oauth/transport.d.ts +0 -41
  222. package/dist/oauth/transport.js +0 -32
  223. package/dist/oauth/wizard.d.ts +0 -40
  224. package/dist/oauth/wizard.js +0 -578
  225. package/dist/persona.d.ts +0 -18
  226. package/dist/persona.js +0 -221
  227. package/dist/pick.d.ts +0 -6
  228. package/dist/pick.js +0 -64
  229. package/dist/pipes.d.ts +0 -1
  230. package/dist/pipes.js +0 -25
  231. package/dist/planner/checks.d.ts +0 -26
  232. package/dist/planner/checks.js +0 -104
  233. package/dist/planner/commands.d.ts +0 -10
  234. package/dist/planner/commands.js +0 -367
  235. package/dist/planner/db.d.ts +0 -15
  236. package/dist/planner/db.js +0 -33
  237. package/dist/planner/role.d.ts +0 -33
  238. package/dist/planner/role.js +0 -68
  239. package/dist/repo/peer-redirect-hooks.d.ts +0 -16
  240. package/dist/repo/peer-redirect-hooks.js +0 -10
  241. package/dist/repo/peer-redirect.d.ts +0 -4
  242. package/dist/repo/peer-redirect.js +0 -26
  243. package/dist/repo/peers.d.ts +0 -39
  244. package/dist/repo/peers.js +0 -139
  245. package/dist/repo/resolve.d.ts +0 -41
  246. package/dist/repo/resolve.js +0 -152
  247. package/dist/repo/root.d.ts +0 -23
  248. package/dist/repo/root.js +0 -91
  249. package/dist/repo/source.d.ts +0 -14
  250. package/dist/repo/source.js +0 -54
  251. package/dist/repo/supabase-project.d.ts +0 -60
  252. package/dist/repo/supabase-project.js +0 -124
  253. package/dist/repo/tsx-loader.d.ts +0 -6
  254. package/dist/repo/tsx-loader.js +0 -41
  255. package/dist/run/pick.d.ts +0 -90
  256. package/dist/run/pick.js +0 -664
  257. package/dist/setup.d.ts +0 -1
  258. package/dist/setup.js +0 -280
  259. package/dist/stack.d.ts +0 -48
  260. package/dist/stack.js +0 -403
  261. package/dist/telemetry.d.ts +0 -59
  262. package/dist/tier.d.ts +0 -40
  263. package/dist/tier.js +0 -96
  264. package/dist/ui.d.ts +0 -40
  265. package/dist/ui.js +0 -71
  266. package/dist/version.d.ts +0 -7
  267. package/dist/version.js +0 -17
  268. package/dist/workers.d.ts +0 -5
  269. package/dist/workers.js +0 -33
  270. package/dist/workflows/commands.d.ts +0 -54
  271. package/dist/workflows/commands.js +0 -763
@@ -1,429 +0,0 @@
1
- /**
2
- * `pnpm devtools doctor [--app <slug>] [--report]`
3
- *
4
- * The new environment checker. Entirely read-only: it inspects this machine
5
- * and this checkout's `.env`, and for a hosted Supabase project, makes a
6
- * couple of network calls to say whether it is reachable — never writes
7
- * anything. `setup.ts`'s hosted wizard runs the same checks at the end of
8
- * its own run (see its last step), so a contributor who set up through the
9
- * wizard and one who ran `doctor` cold see the same report.
10
- *
11
- * Every check carries a stable FAQ id from the docs contract's list (see
12
- * `docs/_shared/getting-started/troubleshooting.md` once written) so a
13
- * failure message can point at
14
- * `https://devdogsuga.org/docs/<app>/getting-started/troubleshooting#<id>`
15
- * rather than explaining itself twice.
16
- *
17
- * Pure classification lives in small exported functions so this file's own
18
- * tests do not have to fake `child_process`/`fs` to prove the logic; the
19
- * orchestrator (`runEnvironmentDoctor`) is the only part that actually
20
- * touches the machine.
21
- */
22
- import { execFileSync } from "node:child_process";
23
- import { existsSync, readFileSync } from "node:fs";
24
- import { join } from "node:path";
25
- import { log, note } from "@clack/prompts";
26
- import { parse as parseDotenv } from "dotenv";
27
- import { discoverRepoRoot } from "./repo/root.js";
28
- import { validateSessionPoolerUrl } from "./db/pooler.js";
29
- const TROUBLESHOOTING_APPS = [
30
- "schedule-builder",
31
- "study-group-finder",
32
- "platform",
33
- ];
34
- function faqUrl(app, id) {
35
- return `https://devdogsuga.org/docs/${app}/getting-started/troubleshooting#${id}`;
36
- }
37
- // ── Pure checks ──────────────────────────────────────────────────────────────
38
- /** The floor is 22.12: pnpm 11 needs `node:sqlite`, stable there. `.nvmrc`
39
- * pins 24, which is what a `fnm use` picks up; a machine below the floor is
40
- * a WARN, not an error — plenty of contributors' shells still resolve an
41
- * old global `node` first. */
42
- export function checkNodeVersion(nodeVersion) {
43
- const [major = 0, minor = 0] = nodeVersion.split(".").map(Number);
44
- const ok = major > 22 || (major === 22 && minor >= 12);
45
- return {
46
- id: "node-version",
47
- status: ok ? "ok" : "warn",
48
- summary: ok
49
- ? `Node ${nodeVersion}`
50
- : `Node ${nodeVersion} is below the floor (>= 22.12)`,
51
- fix: ok
52
- ? undefined
53
- : "Install fnm, then `fnm use` in the repo (reads .nvmrc).",
54
- faqId: ok ? undefined : "node-version",
55
- };
56
- }
57
- export function checkFnmHookPresent(shellProfile) {
58
- const present = shellProfile !== null &&
59
- (shellProfile.includes("fnm env") || shellProfile.includes("fnm_env"));
60
- return {
61
- id: "fnm-not-found",
62
- status: present ? "ok" : "warn",
63
- summary: present
64
- ? "fnm hook found in shell profile"
65
- : "No fnm hook found in your shell profile",
66
- fix: present
67
- ? undefined
68
- : "Add fnm's shell hook (`fnm env --use-on-cd --shell <bash|zsh|powershell> | ...`) and restart your terminal.",
69
- faqId: present ? undefined : "fnm-not-found",
70
- };
71
- }
72
- export function checkPnpmVersion(pnpmVersion, pinned) {
73
- if (!pnpmVersion) {
74
- return {
75
- id: "pnpm-missing",
76
- status: "warn",
77
- summary: "pnpm is not on PATH",
78
- fix: "`npm install -g pnpm` (never `corepack enable` — this repo does not use corepack).",
79
- faqId: "pnpm-missing",
80
- };
81
- }
82
- const pinnedVersion = pinned.replace(/^pnpm@/, "").split("+")[0];
83
- const ok = pnpmVersion === pinnedVersion;
84
- return {
85
- id: "pnpm-version",
86
- status: ok ? "ok" : "warn",
87
- summary: ok
88
- ? `pnpm ${pnpmVersion}`
89
- : `pnpm ${pnpmVersion} does not match the pinned ${pinnedVersion}`,
90
- fix: ok
91
- ? undefined
92
- : "pnpm self-switches to the pinned version on its first run in the repo (pmOnFail=download). Run any pnpm command here again.",
93
- faqId: ok ? undefined : "pnpm-version",
94
- };
95
- }
96
- /** `git config core.autocrlf` — `input` or `false` is correct on every
97
- * platform this repo documents (WSL2, native Windows, macOS, Linux); `true`
98
- * rewrites LF to CRLF on checkout and breaks anything that shells out to a
99
- * script with a shebang. */
100
- export function checkAutocrlf(value) {
101
- const ok = value === "input" || value === "false" || value === null;
102
- return {
103
- id: "crlf-line-endings",
104
- status: ok ? "ok" : "warn",
105
- summary: ok
106
- ? "core.autocrlf is not rewriting line endings"
107
- : `core.autocrlf=${value} rewrites LF to CRLF on checkout`,
108
- fix: ok
109
- ? undefined
110
- : "`git config --global core.autocrlf input` (or `false`), then re-clone or `git rm --cached -r . && git reset --hard`.",
111
- faqId: ok ? undefined : "crlf-line-endings",
112
- };
113
- }
114
- /** WSL2's own filesystem, never `/mnt/c/...` — a Windows-drive checkout
115
- * makes every file operation cross the 9p boundary and Docker bind mounts
116
- * silently fail to see edits. */
117
- export function checkRepoUnderMntC(repoRoot) {
118
- const ok = !repoRoot.startsWith("/mnt/");
119
- return {
120
- id: "repo-under-mnt-c",
121
- status: ok ? "ok" : "warn",
122
- summary: ok
123
- ? "Repo is on the Linux filesystem"
124
- : `Repo is under ${repoRoot} — a Windows drive mounted into WSL2`,
125
- fix: ok
126
- ? undefined
127
- : "Clone into your Linux home (~), never /mnt/c/..., and reopen with the VS Code WSL extension.",
128
- faqId: ok ? undefined : "repo-under-mnt-c",
129
- };
130
- }
131
- /** Windows only. `RemoteSigned` (or looser) is what lets fnm's PowerShell
132
- * hook and pnpm's shims actually run. */
133
- export function checkPowerShellExecutionPolicy(policy) {
134
- const restricted = policy === "Restricted" || policy === "AllSigned";
135
- return {
136
- id: "powershell-execution-policy",
137
- status: restricted ? "warn" : "ok",
138
- summary: restricted
139
- ? `Execution policy is ${policy}, which blocks locally-created scripts`
140
- : `Execution policy is ${policy ?? "unrestricted"}`,
141
- fix: restricted
142
- ? "`Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`, then restart your terminal."
143
- : undefined,
144
- faqId: restricted ? "powershell-execution-policy" : undefined,
145
- };
146
- }
147
- /**
148
- * The checkout's development env the way every other command loads it:
149
- * `.env.generated` (the local stack's connection block, written by `db
150
- * start`) first and winning, then `.env`. Reading `.env` alone reported a
151
- * working local-Docker checkout as missing PUBLISHABLE_KEY, and took the
152
- * blank hosted template's `https://$PROJECT_REF.supabase.co` for a real
153
- * hosted project that "did not respond".
154
- */
155
- export function readCheckoutEnv(repoRoot) {
156
- const read = (name) => {
157
- const path = join(repoRoot, name);
158
- return existsSync(path) ? parseDotenv(readFileSync(path, "utf8")) : {};
159
- };
160
- const generated = Object.fromEntries(Object.entries(read(".env.generated")).filter(([, value]) => value !== ""));
161
- return { ...read(".env"), ...generated };
162
- }
163
- export function checkEnvKeysPresent(env, keys) {
164
- const missing = keys.filter((k) => !env[k]);
165
- return {
166
- id: "env-incomplete",
167
- status: missing.length === 0 ? "ok" : "warn",
168
- summary: missing.length === 0
169
- ? ".env has every key this run needs"
170
- : `.env is missing: ${missing.join(", ")}`,
171
- fix: missing.length === 0
172
- ? undefined
173
- : "`pnpm devtools setup` (or `pnpm devtools env init`) fills in the blanks.",
174
- faqId: missing.length === 0 ? undefined : "env-incomplete",
175
- };
176
- }
177
- export { validateSessionPoolerUrl };
178
- // ── Orchestrator ─────────────────────────────────────────────────────────────
179
- function has(cmd, args) {
180
- try {
181
- return execFileSync(cmd, args, {
182
- stdio: ["ignore", "pipe", "ignore"],
183
- encoding: "utf8",
184
- }).trim();
185
- }
186
- catch {
187
- return null;
188
- }
189
- }
190
- function gitConfig(key) {
191
- return has("git", ["config", "--get", key]);
192
- }
193
- function readNvmrc(repoRoot) {
194
- try {
195
- return readFileSync(join(repoRoot, ".nvmrc"), "utf8").trim();
196
- }
197
- catch {
198
- return null;
199
- }
200
- }
201
- function readPackageManagerPin(repoRoot) {
202
- try {
203
- const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
204
- return pkg.packageManager ?? null;
205
- }
206
- catch {
207
- return null;
208
- }
209
- }
210
- function readShellProfile() {
211
- const home = process.env.HOME;
212
- if (!home)
213
- return null;
214
- for (const file of [
215
- ".bashrc",
216
- ".zshrc",
217
- ".config/fish/config.fish",
218
- ".bash_profile",
219
- ]) {
220
- const path = join(home, file);
221
- if (existsSync(path)) {
222
- try {
223
- return readFileSync(path, "utf8");
224
- }
225
- catch {
226
- // try the next one
227
- }
228
- }
229
- }
230
- return null;
231
- }
232
- function renderCheck(app, check) {
233
- const icon = check.status === "ok" ? "OK " : check.status === "skip" ? "SKIP" : "WARN";
234
- const lines = [`${icon} ${check.summary}`];
235
- if (check.status === "warn") {
236
- if (check.fix)
237
- lines.push(` fix: ${check.fix}`);
238
- if (check.faqId)
239
- lines.push(` ${faqUrl(app, check.faqId)}`);
240
- }
241
- return lines.join("\n");
242
- }
243
- export async function runEnvironmentDoctor(opts) {
244
- const app = opts.app && TROUBLESHOOTING_APPS.includes(opts.app) ? opts.app : "platform";
245
- const repoRoot = discoverRepoRoot();
246
- const checks = [];
247
- const nodeCheck = checkNodeVersion(process.versions.node);
248
- checks.push(nodeCheck);
249
- // Only worth raising when Node is wrong: nvm, Volta or a system Node that
250
- // already satisfies the floor is fine, and flagging a missing fnm hook there
251
- // is a false alarm.
252
- if (nodeCheck.status !== "ok") {
253
- checks.push(checkFnmHookPresent(readShellProfile()));
254
- }
255
- else {
256
- const pinnedMajor = repoRoot ? readNvmrc(repoRoot)?.split(".")[0] : null;
257
- const major = process.versions.node.split(".")[0];
258
- if (pinnedMajor && /^\d+$/.test(pinnedMajor) && pinnedMajor !== major) {
259
- checks.push({
260
- id: "node-version",
261
- status: "warn",
262
- summary: `Node ${process.versions.node} works, but the repo pins Node ${pinnedMajor} (.nvmrc)`,
263
- fix: "Run `fnm install && fnm use` in the repo.",
264
- faqId: "node-version",
265
- });
266
- }
267
- }
268
- const pnpmVersion = has("pnpm", ["--version"]);
269
- const pin = repoRoot ? readPackageManagerPin(repoRoot) : null;
270
- checks.push(pin
271
- ? checkPnpmVersion(pnpmVersion, pin)
272
- : {
273
- id: "pnpm-version",
274
- status: pnpmVersion ? "ok" : "warn",
275
- summary: pnpmVersion ? `pnpm ${pnpmVersion}` : "pnpm is not on PATH",
276
- fix: pnpmVersion
277
- ? undefined
278
- : "`npm install -g pnpm` (never `corepack enable`).",
279
- faqId: pnpmVersion ? undefined : "pnpm-missing",
280
- });
281
- checks.push(checkAutocrlf(gitConfig("core.autocrlf")));
282
- if (repoRoot)
283
- checks.push(checkRepoUnderMntC(repoRoot));
284
- if (process.platform === "win32") {
285
- checks.push(checkPowerShellExecutionPolicy(has("powershell", ["-NoProfile", "-Command", "Get-ExecutionPolicy"])));
286
- }
287
- // Local Docker stack — only relevant when the checkout has a local
288
- // `.env.generated`, the signature of `db start` having run at least once.
289
- if (repoRoot && existsSync(join(repoRoot, ".env.generated"))) {
290
- const dockerUp = has("docker", ["info"]) !== null;
291
- checks.push({
292
- id: "docker-not-running",
293
- status: dockerUp ? "ok" : "warn",
294
- summary: dockerUp
295
- ? "Docker is running"
296
- : "Docker daemon is not answering",
297
- fix: dockerUp
298
- ? undefined
299
- : "Start Docker Desktop, or Docker Engine in WSL2. Never Colima.",
300
- faqId: dockerUp ? undefined : "docker-not-running",
301
- });
302
- }
303
- if (!opts.app || opts.app === "study-group-finder") {
304
- const flutter = has("flutter", ["--version"]) !== null;
305
- checks.push({
306
- id: "flutter-missing",
307
- status: flutter ? "ok" : "skip",
308
- summary: flutter ? "Flutter is installed" : "Flutter is not installed",
309
- fix: flutter
310
- ? undefined
311
- : "Install the Flutter SDK for study-group-finder.",
312
- faqId: flutter ? undefined : "flutter-missing",
313
- });
314
- if (flutter) {
315
- const devices = has("adb", ["devices"]);
316
- const hasDevice = Boolean(devices
317
- ?.split("\n")
318
- .slice(1)
319
- .some((line) => line.trim().endsWith("device")));
320
- checks.push({
321
- id: "adb-no-devices",
322
- status: hasDevice ? "ok" : "skip",
323
- summary: hasDevice
324
- ? "adb sees a device or emulator"
325
- : "adb sees no device or emulator",
326
- fix: hasDevice
327
- ? undefined
328
- : "Start an Android Studio emulator (or plug in a device). On WSL2 with a Windows emulator, run `adb kill-server` on Windows first.",
329
- faqId: hasDevice ? undefined : "adb-no-devices",
330
- });
331
- }
332
- }
333
- let env = {};
334
- if (repoRoot) {
335
- env = readCheckoutEnv(repoRoot);
336
- checks.push(checkEnvKeysPresent(env, ["API_URL", "PUBLISHABLE_KEY"]));
337
- }
338
- // ── Hosted checks — only when .env looks like a hosted project ───────────
339
- const isHosted = Boolean(env.API_URL &&
340
- !env.API_URL.includes("127.0.0.1") &&
341
- !env.API_URL.includes("localhost"));
342
- if (isHosted && env.API_URL) {
343
- let reachable = false;
344
- let probeStatus = null;
345
- try {
346
- const res = await fetch(`${env.API_URL}/auth/v1/settings`, {
347
- signal: AbortSignal.timeout(5000),
348
- });
349
- probeStatus = res.status;
350
- reachable = res.status < 500;
351
- }
352
- catch {
353
- reachable = false;
354
- }
355
- checks.push({
356
- id: "supabase-unreachable",
357
- status: reachable ? "ok" : "warn",
358
- summary: reachable
359
- ? "The hosted project answers"
360
- : "The hosted project's API URL did not respond",
361
- fix: reachable
362
- ? undefined
363
- : "Check the API URL in .env, and that the project is not paused.",
364
- faqId: reachable ? undefined : "supabase-unreachable",
365
- });
366
- if (probeStatus === 503 || probeStatus === 522 || probeStatus === 540) {
367
- checks.push({
368
- id: "supabase-paused",
369
- status: "warn",
370
- summary: "The project looks paused",
371
- fix: "Unpause it from the Supabase dashboard (free tier pauses after ~1 week idle).",
372
- faqId: "supabase-paused",
373
- });
374
- }
375
- if (reachable && env.SECRET_KEY) {
376
- let keysValid = false;
377
- try {
378
- const res = await fetch(`${env.API_URL}/auth/v1/admin/users?page=1&per_page=1`, {
379
- headers: {
380
- Authorization: `Bearer ${env.SECRET_KEY}`,
381
- apikey: env.SECRET_KEY,
382
- },
383
- signal: AbortSignal.timeout(5000),
384
- });
385
- keysValid = res.status !== 401 && res.status !== 403;
386
- }
387
- catch {
388
- keysValid = false;
389
- }
390
- checks.push({
391
- id: "supabase-keys-invalid",
392
- status: keysValid ? "ok" : "warn",
393
- summary: keysValid
394
- ? "SECRET_KEY is accepted"
395
- : "SECRET_KEY was rejected",
396
- fix: keysValid
397
- ? undefined
398
- : "Copy the service_role secret key again from Project Settings -> API.",
399
- faqId: keysValid ? undefined : "supabase-keys-invalid",
400
- });
401
- }
402
- }
403
- if (env.DB_URL) {
404
- const validation = validateSessionPoolerUrl(env.DB_URL);
405
- checks.push({
406
- id: validation.faqId ?? "db-url-direct-connection",
407
- status: validation.ok ? "ok" : "warn",
408
- summary: validation.ok
409
- ? "DB_URL is a Session pooler string"
410
- : (validation.message ?? "DB_URL is not a Session pooler string"),
411
- fix: validation.ok ? undefined : validation.message,
412
- faqId: validation.faqId,
413
- });
414
- }
415
- note(checks.map((c) => renderCheck(app, c)).join("\n\n"), opts.app ? `Doctor — ${opts.app}` : "Doctor");
416
- const warnings = checks.filter((c) => c.status === "warn").length;
417
- if (warnings === 0)
418
- log.success("Everything checked out.");
419
- else
420
- log.warn(`${warnings} check${warnings === 1 ? "" : "s"} need attention.`);
421
- if (opts.report) {
422
- const redacted = [
423
- `OS: ${process.platform} ${process.arch}`,
424
- `Node: ${process.versions.node}`,
425
- ...checks.map((c) => `${c.status.toUpperCase().padEnd(4)} ${c.id}: ${c.summary}`),
426
- ].join("\n");
427
- note(redacted, "Paste this in Discord");
428
- }
429
- }
@@ -1,92 +0,0 @@
1
- import type { Condition } from "./commands.js";
2
- import { findRepoRoot } from "./repo/root.js";
3
- /**
4
- * The repo root, which is where `supabase/config.toml` and `.env` live.
5
- *
6
- * Used to be a fixed `join(import.meta.dirname, "..", "..", "..")` — that
7
- * only worked because devtools lived inside the repo it operated on. Now
8
- * published as its own package, every former reader of that constant calls
9
- * `findRepoRoot()` (re-exported here for callers that already import from
10
- * this module) — see `repo/root.ts` for the marker and why it is a lazy,
11
- * throwing function rather than a constant.
12
- */
13
- export { findRepoRoot };
14
- /** A fact that may be unreadable. See the header: unknown is not "no". */
15
- export type Known = "yes" | "no" | "unknown";
16
- export interface Environment {
17
- /** The Docker daemon answers. Without it there is no Supabase on this machine. */
18
- docker: Known;
19
- /** The local Supabase stack's containers are up. */
20
- stack: Known;
21
- /** The root `.env` exists, so `setup` has been run at least once. */
22
- envFile: Known;
23
- }
24
- /**
25
- * The environment that adapts nothing.
26
- *
27
- * What the tests use, and what any caller that would rather not probe can
28
- * pass. Because nothing is hidden or flagged on `"unknown"`, this is exactly
29
- * the pre-adaptation menu, which makes it a safe fallback rather than a
30
- * special case to remember.
31
- */
32
- export declare const UNKNOWN_ENVIRONMENT: Environment;
33
- /**
34
- * Reads the machine.
35
- *
36
- * Two subprocesses at worst, one when Docker is down, plus an `existsSync`.
37
- * The budget is "cheaper than the first keystroke", because this runs before
38
- * the wizard's first screen and anything slower would be paid on every
39
- * invocation to save a few of them later.
40
- *
41
- * It does NOT call `supabase status`, which is the authoritative answer but
42
- * takes the better part of a second: it shells out through `pnpm exec` and
43
- * then talks to every service in the stack. `docker ps` answers the only
44
- * question the menu has (is it up?) in a fraction of that, and the commands
45
- * that need real credentials go through `resolveInstance` (`instance.ts`),
46
- * which reads the session's already-entered env at the moment it matters.
47
- */
48
- export declare function probeEnvironment(execute?: (file: string, args: string[]) => string | null): Environment;
49
- /**
50
- * Whether a `Condition` from the command tree currently holds.
51
- *
52
- * The tree declares conditions as strings so that it stays inert data the docs
53
- * build can render (see `commands.ts`); this is the one place that knows what
54
- * they mean. `"unknown"` propagates rather than collapsing to false, and both
55
- * callers below read it as "do nothing".
56
- */
57
- export declare function holds(condition: Condition, env: Environment): Known;
58
- /**
59
- * Whether the wizard should offer this command at all.
60
- *
61
- * False only for a command whose `when` is definitively unmet: `stop`
62
- * against a stack that is already stopped. This is reserved for commands that
63
- * would be *meaningless*, never merely inconvenient: one that would fail with
64
- * a good error message stays in the menu and carries `needs` instead, because
65
- * a contributor who cannot find a command they know exists is worse off than
66
- * one who runs it and is told why it did not work.
67
- *
68
- * Hiding is a wizard-only decision. `--help` still lists these, the dispatcher
69
- * still accepts them, and the generated reference still documents them. The
70
- * menu is a guide to right now, and those three are the reference.
71
- */
72
- export declare function isOffered(node: {
73
- when?: Condition;
74
- }, env: Environment): boolean;
75
- /**
76
- * Why this command will not work right now, or `null` if nothing is in the way.
77
- *
78
- * Appended to the entry's hint, so the menu says "needs Docker, which is not
79
- * running" on the line itself, rather than after the reader has chosen it and
80
- * waited for a connection attempt to fail.
81
- */
82
- export declare function blockedBecause(node: {
83
- needs?: Condition;
84
- }, env: Environment): string | null;
85
- /**
86
- * The three facts, as the wizard's opening note and as `status --local`.
87
- *
88
- * Printed before the first question rather than discovered through failures:
89
- * "Docker running no" at the top of the screen is the whole explanation for
90
- * why the database commands below it are flagged, and it costs one glance.
91
- */
92
- export declare function describeEnvironment(env: Environment): string;