@pithy-sh/cli 0.1.0

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 (234) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +72 -0
  3. package/scripts/templateManifest.ts +49 -0
  4. package/scripts/tsconfig.json +26 -0
  5. package/scripts/vendorTemplate.ts +84 -0
  6. package/scripts/verifyPack.ts +88 -0
  7. package/src/audit/cliAudit.ts +406 -0
  8. package/src/bin.ts +111 -0
  9. package/src/capabilities/add.ts +288 -0
  10. package/src/capabilities/addBootstrap.ts +275 -0
  11. package/src/capabilities/catalog.ts +175 -0
  12. package/src/capabilities/compose.ts +39 -0
  13. package/src/capabilities/configConstants.ts +74 -0
  14. package/src/capabilities/configImports.ts +397 -0
  15. package/src/capabilities/eject.ts +331 -0
  16. package/src/capabilities/emailProvisioner.ts +346 -0
  17. package/src/capabilities/entitlementGap.ts +70 -0
  18. package/src/capabilities/entryExports.ts +162 -0
  19. package/src/capabilities/flow.ts +550 -0
  20. package/src/capabilities/hostRegistry.ts +368 -0
  21. package/src/capabilities/loadFailure.ts +208 -0
  22. package/src/capabilities/manifests.ts +238 -0
  23. package/src/capabilities/mediaProvisioner.ts +471 -0
  24. package/src/capabilities/mintSecrets.ts +306 -0
  25. package/src/capabilities/paymentsProvisioner.ts +207 -0
  26. package/src/capabilities/prerequisites.ts +168 -0
  27. package/src/capabilities/r2Bucket.ts +113 -0
  28. package/src/capabilities/reconcile.ts +1483 -0
  29. package/src/capabilities/remove.ts +597 -0
  30. package/src/capabilities/requiredOptions.ts +92 -0
  31. package/src/capabilities/rotateSecrets.ts +305 -0
  32. package/src/capabilities/secrets.ts +178 -0
  33. package/src/capabilities/secretsDispatcher.ts +29 -0
  34. package/src/capabilities/secretsProvisioner.ts +389 -0
  35. package/src/capabilities/storageProvisioner.ts +414 -0
  36. package/src/capabilities/supportProvisioner.ts +515 -0
  37. package/src/capabilities/testersLoader.ts +52 -0
  38. package/src/capabilities/testersProvisioner.ts +236 -0
  39. package/src/capabilities/turnstileProvisioner.ts +347 -0
  40. package/src/capabilities/vectorProvisioner.ts +260 -0
  41. package/src/ci/fileModes.ts +223 -0
  42. package/src/ci/sourceFiles.ts +200 -0
  43. package/src/ci/workflowDrivers.ts +524 -0
  44. package/src/cloudflare/accountAnswer.ts +110 -0
  45. package/src/cloudflare/config.ts +685 -0
  46. package/src/cloudflare/storeId.ts +129 -0
  47. package/src/commands/add.ts +372 -0
  48. package/src/commands/alias.ts +205 -0
  49. package/src/commands/dashboard.ts +651 -0
  50. package/src/commands/deploy.ts +150 -0
  51. package/src/commands/dev.ts +37 -0
  52. package/src/commands/doctor.ts +2059 -0
  53. package/src/commands/email.ts +425 -0
  54. package/src/commands/env.ts +155 -0
  55. package/src/commands/feature.ts +359 -0
  56. package/src/commands/init.ts +538 -0
  57. package/src/commands/media.ts +303 -0
  58. package/src/commands/migrate.ts +129 -0
  59. package/src/commands/payments.ts +336 -0
  60. package/src/commands/provision.ts +368 -0
  61. package/src/commands/remove.ts +151 -0
  62. package/src/commands/secrets.ts +652 -0
  63. package/src/commands/seed.ts +229 -0
  64. package/src/commands/storage.ts +309 -0
  65. package/src/commands/support.ts +331 -0
  66. package/src/commands/testers.ts +1020 -0
  67. package/src/commands/token.ts +364 -0
  68. package/src/commands/turnstile.ts +271 -0
  69. package/src/commands/ui.ts +222 -0
  70. package/src/commands/upgrade.ts +517 -0
  71. package/src/commands/vector.ts +390 -0
  72. package/src/commands/worker.ts +295 -0
  73. package/src/dashboard/api.ts +323 -0
  74. package/src/dashboard/connect.ts +758 -0
  75. package/src/dashboard/contract.ts +289 -0
  76. package/src/dashboard/grant.ts +124 -0
  77. package/src/dashboard/registry.ts +519 -0
  78. package/src/dashboard/resolveTarget.ts +119 -0
  79. package/src/dev/delivery.ts +174 -0
  80. package/src/dev/devLogin.ts +155 -0
  81. package/src/dev/devLoginTargets.ts +91 -0
  82. package/src/dev/env.ts +206 -0
  83. package/src/dev/hostWorkers.ts +290 -0
  84. package/src/dev/keys.ts +111 -0
  85. package/src/dev/logging.ts +87 -0
  86. package/src/dev/openUrl.ts +75 -0
  87. package/src/dev/orchestrator.ts +1014 -0
  88. package/src/dev/ports.ts +220 -0
  89. package/src/dev/readyWatch.ts +142 -0
  90. package/src/dev/state.ts +90 -0
  91. package/src/devSecrets/bootstrapVars.ts +265 -0
  92. package/src/devSecrets/devVars.ts +240 -0
  93. package/src/devSecrets/edit.ts +256 -0
  94. package/src/devSecrets/file.ts +277 -0
  95. package/src/devSecrets/generate.ts +428 -0
  96. package/src/devSecrets/location.ts +80 -0
  97. package/src/devSecrets/mode.ts +71 -0
  98. package/src/devSecrets/records.ts +30 -0
  99. package/src/devSecrets/report.ts +99 -0
  100. package/src/devSecrets/seed.ts +344 -0
  101. package/src/devSecrets/store.ts +262 -0
  102. package/src/devSecrets/targets.ts +204 -0
  103. package/src/dispatch.ts +147 -0
  104. package/src/docs/catalog.ts +246 -0
  105. package/src/docs/writeCatalog.ts +45 -0
  106. package/src/doctor/cloudflare.ts +287 -0
  107. package/src/doctor/devPreferences.ts +155 -0
  108. package/src/doctor/devSecrets.ts +464 -0
  109. package/src/doctor/devVars.ts +414 -0
  110. package/src/doctor/devVarsLocal.ts +138 -0
  111. package/src/doctor/environments.ts +155 -0
  112. package/src/doctor/health.ts +354 -0
  113. package/src/doctor/localDelivery.ts +91 -0
  114. package/src/doctor/portsRegistry.ts +252 -0
  115. package/src/doctor/projectName.ts +584 -0
  116. package/src/doctor/secretBindings.ts +166 -0
  117. package/src/doctor/settings.ts +274 -0
  118. package/src/doctor/settingsSources.ts +202 -0
  119. package/src/doctor/workerName.ts +174 -0
  120. package/src/doctor/wranglerVars.ts +33 -0
  121. package/src/feature/bindings.ts +93 -0
  122. package/src/feature/create.ts +179 -0
  123. package/src/feature/destroy.ts +160 -0
  124. package/src/feature/devConfig.ts +201 -0
  125. package/src/feature/identity.ts +100 -0
  126. package/src/feature/manifest.ts +132 -0
  127. package/src/feature/ports.ts +615 -0
  128. package/src/feature/provision.ts +362 -0
  129. package/src/feature/sync.ts +148 -0
  130. package/src/feature/worktree.ts +282 -0
  131. package/src/help/groups.ts +47 -0
  132. package/src/help/rootUsage.ts +135 -0
  133. package/src/main.ts +73 -0
  134. package/src/migrations/ledger.ts +129 -0
  135. package/src/migrations/registry.ts +47 -0
  136. package/src/migrations/run.ts +1066 -0
  137. package/src/notifier/check.ts +129 -0
  138. package/src/notifier/installer.ts +48 -0
  139. package/src/notifier/notify.ts +152 -0
  140. package/src/notifier/state.ts +248 -0
  141. package/src/notifier/version.ts +59 -0
  142. package/src/platform/editor.ts +333 -0
  143. package/src/platform/rc.ts +118 -0
  144. package/src/platform/shell.ts +83 -0
  145. package/src/project/appBindings.ts +184 -0
  146. package/src/project/appWorkflows.ts +266 -0
  147. package/src/project/applyDomains.ts +166 -0
  148. package/src/project/askDomains.ts +220 -0
  149. package/src/project/atomic.ts +466 -0
  150. package/src/project/bindingEntries.ts +425 -0
  151. package/src/project/config.ts +701 -0
  152. package/src/project/dashboard.ts +118 -0
  153. package/src/project/deploy.ts +364 -0
  154. package/src/project/devVars.ts +113 -0
  155. package/src/project/domainPrompt.ts +191 -0
  156. package/src/project/domains.ts +386 -0
  157. package/src/project/envInventory.ts +356 -0
  158. package/src/project/environment.ts +125 -0
  159. package/src/project/extensions.ts +69 -0
  160. package/src/project/jsonc.ts +289 -0
  161. package/src/project/packageManager.ts +238 -0
  162. package/src/project/readOptionalFile.ts +342 -0
  163. package/src/project/rollback.ts +145 -0
  164. package/src/project/scaffold.ts +1088 -0
  165. package/src/project/templateFiles.ts +53 -0
  166. package/src/project/verifyDeploy.ts +230 -0
  167. package/src/project/versionMetadata.ts +77 -0
  168. package/src/project/workerAddress.ts +176 -0
  169. package/src/project/workerCommand.ts +564 -0
  170. package/src/project/workerIdentity.ts +50 -0
  171. package/src/project/workerManifest.ts +135 -0
  172. package/src/project/workerScaffold.ts +289 -0
  173. package/src/project/workerScope.ts +394 -0
  174. package/src/project/workers.ts +86 -0
  175. package/src/project/workflows.ts +281 -0
  176. package/src/project/wrangler.ts +168 -0
  177. package/src/provision/confirm.ts +86 -0
  178. package/src/provision/environment.ts +407 -0
  179. package/src/provision/featureConfig.ts +98 -0
  180. package/src/provision/mode.ts +62 -0
  181. package/src/provision/pendingSecrets.ts +96 -0
  182. package/src/provision/resources.ts +126 -0
  183. package/src/provision/secretBindings.ts +149 -0
  184. package/src/provision/store.ts +33 -0
  185. package/src/provision/unprovisioned.ts +114 -0
  186. package/src/provision/wranglerEnv.ts +220 -0
  187. package/src/rootFlags.ts +48 -0
  188. package/src/seed/drivers.ts +423 -0
  189. package/src/seed/media.ts +187 -0
  190. package/src/seed/plan.ts +137 -0
  191. package/src/seed/prepare.ts +224 -0
  192. package/src/seed/registry.ts +25 -0
  193. package/src/seed/run.ts +793 -0
  194. package/src/seed/safety.ts +206 -0
  195. package/src/terminal/logger.ts +42 -0
  196. package/src/terminal/output.ts +64 -0
  197. package/src/terminal/style.ts +132 -0
  198. package/src/test-utils/doctorHarness.ts +190 -0
  199. package/src/test-utils/migrateHarness.ts +126 -0
  200. package/src/test-utils/seedHarness.ts +173 -0
  201. package/src/test-utils/tempRepo.ts +45 -0
  202. package/src/tokens/config.ts +16 -0
  203. package/src/tokens/engine.ts +345 -0
  204. package/src/tokens/mintedTokens.ts +233 -0
  205. package/src/tokens/sinks.ts +84 -0
  206. package/src/ui/flow.ts +451 -0
  207. package/src/ui/react.ts +112 -0
  208. package/src/ui/routeAllowlist.ts +208 -0
  209. package/src/ui/scaffold.ts +113 -0
  210. package/src/ui/screenStyles.ts +127 -0
  211. package/src/ui/stubs.ts +135 -0
  212. package/src/ui/templates.ts +52 -0
  213. package/src/ui/wire.ts +311 -0
  214. package/src/ui/workerUi.ts +172 -0
  215. package/templates/starter/.dev.secrets.example.jsonc +43 -0
  216. package/templates/starter/.dev.vars.example +30 -0
  217. package/templates/starter/apps/api/package.json +22 -0
  218. package/templates/starter/apps/api/pithy.config.ts +65 -0
  219. package/templates/starter/apps/api/pithy.worker.jsonc +11 -0
  220. package/templates/starter/apps/api/src/bindings.workers.test.ts +18 -0
  221. package/templates/starter/apps/api/src/cloudflare-test.d.ts +11 -0
  222. package/templates/starter/apps/api/src/index.ts +8 -0
  223. package/templates/starter/apps/api/tsconfig.json +26 -0
  224. package/templates/starter/apps/api/wrangler.jsonc +68 -0
  225. package/templates/starter/biome.template.jsonc +75 -0
  226. package/templates/starter/gitignore +37 -0
  227. package/templates/starter/package.json +28 -0
  228. package/templates/starter/pithy.config.ts +67 -0
  229. package/templates/starter/plugins/no-console.grit +25 -0
  230. package/templates/starter/plugins/no-process-io.grit +25 -0
  231. package/templates/starter/tsconfig.json +14 -0
  232. package/templates/starter/tsconfig.tools.json +30 -0
  233. package/templates/starter/vitest.config.ts +124 -0
  234. package/templates/starter/vitest.workers.config.ts +26 -0
@@ -0,0 +1,205 @@
1
+ // SPDX-FileCopyrightText: 2026 Pithy
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import { ValidationError } from "@pithy-sh/core/src/error/pithyError";
5
+ import { defineCommand } from "citty";
6
+ import { appendToRcFile, readRcFile, removeFromRcFile } from "../platform/rc";
7
+ import { detectShell } from "../platform/shell";
8
+ import { formatJsonLine, withErrorReporting } from "../terminal/output";
9
+
10
+ /** Marker comments that delimit the block `--remove` finds and deletes. Universal `#` comment syntax. */
11
+ const MARKER_OPEN = "# >>> pithy alias >>>";
12
+ const MARKER_CLOSE = "# <<< pithy alias <<<";
13
+
14
+ /** What we print for an unknown shell, and the `alias` key in that JSON — the plain POSIX form. */
15
+ const DEFAULT_ALIAS = "alias p.='pithy'";
16
+
17
+ /** Options every alias entry point accepts: suppress human chatter, or emit machine-readable JSON. */
18
+ export interface AliasOptions {
19
+ /** Suppress the "Already pithy." line (used by the silent `pithy init` install). */
20
+ silent?: boolean;
21
+ /** Emit one JSON line instead of human text. */
22
+ json?: boolean;
23
+ }
24
+
25
+ /** What an alias payload says was being attempted — the discriminant every one of them leads with. */
26
+ type AliasAction = "install" | "remove" | "status";
27
+
28
+ /**
29
+ * Print one JSON line to stdout, led by the two keys every payload in this CLI carries.
30
+ *
31
+ * `command` is here rather than at each call site because this command's payloads are the only ones that
32
+ * were written without it, and a helper is the one place that cannot be forgotten on the next path added.
33
+ * `action` is the discriminant: alias has no subcommands, so it is what tells `install` from `remove` from
34
+ * `status`, and a payload without it is a line a consumer cannot classify (#231).
35
+ */
36
+ function emitJson(action: AliasAction, payload: Record<string, unknown>): void {
37
+ process.stdout.write(`${formatJsonLine({ command: "alias", action, ...payload })}\n`);
38
+ }
39
+
40
+ /** Print one human line (with trailing newline) to stdout. */
41
+ function emit(line: string): void {
42
+ process.stdout.write(`${line}\n`);
43
+ }
44
+
45
+ /**
46
+ * The unknown-shell path: we never write to an rc file we don't recognize. Print the alias line and how
47
+ * to add it by hand, then return cleanly (exit 0).
48
+ *
49
+ * **The action is a parameter because the payload has to name it.** Both `installAlias` and `removeAlias`
50
+ * end here, and the payload used to say only that a shell was not detected — so the one path where
51
+ * nothing was written to any file was the one path a consumer keying on `action` could not classify. It
52
+ * is the case that most needs detecting: the command exited 0 and changed nothing.
53
+ */
54
+ function logManualInstructions(action: Extract<AliasAction, "install" | "remove">, opts: AliasOptions): void {
55
+ if (opts.json) {
56
+ emitJson(action, { shell: null, manual: true, alias: DEFAULT_ALIAS });
57
+ return;
58
+ }
59
+ emit("Couldn't detect your shell.");
60
+ emit(`Add \`${DEFAULT_ALIAS}\` to your shell config to use \`p.\`.`);
61
+ }
62
+
63
+ /** True when the rc contents already carry our block or a hand-added `alias p.=`. */
64
+ function alreadyInstalled(contents: string): boolean {
65
+ return contents.includes(MARKER_OPEN) || /\balias\s+p\.\s*=/.test(contents);
66
+ }
67
+
68
+ /**
69
+ * Install the `p.` alias into the detected shell's rc file. Idempotent: a second run detects the existing
70
+ * block (or a hand-added `alias p.=`) and writes nothing. Silent mode suppresses only the "Already pithy."
71
+ * line — a fresh install still prints its confirmation.
72
+ */
73
+ export async function installAlias(opts: AliasOptions = {}): Promise<void> {
74
+ const shell = await detectShell();
75
+ if (!shell) {
76
+ logManualInstructions("install", opts);
77
+ return;
78
+ }
79
+
80
+ const contents = await readRcFile(shell.rcPath);
81
+ if (alreadyInstalled(contents)) {
82
+ if (opts.json) {
83
+ emitJson("install", {
84
+ installed: true,
85
+ alreadyInstalled: true,
86
+ shell: shell.kind,
87
+ rcPath: shell.rcPath,
88
+ alias: shell.aliasSyntax,
89
+ });
90
+ return;
91
+ }
92
+ if (!opts.silent) emit("Already pithy.");
93
+ return;
94
+ }
95
+
96
+ const block = [MARKER_OPEN, shell.aliasSyntax, MARKER_CLOSE].join("\n");
97
+ await appendToRcFile(shell.rcPath, `\n${block}\n`);
98
+
99
+ if (opts.json) {
100
+ emitJson("install", {
101
+ installed: true,
102
+ alreadyInstalled: false,
103
+ shell: shell.kind,
104
+ rcPath: shell.rcPath,
105
+ alias: shell.aliasSyntax,
106
+ });
107
+ return;
108
+ }
109
+ emit(`Added \`${shell.aliasSyntax}\` to ${shell.rcPath}`);
110
+ emit(`Reload your shell or run: source ${shell.rcPath}`);
111
+ }
112
+
113
+ /** Remove the alias block. Prints `No Pithy alias installed.` and exits 0 when there is nothing to remove. */
114
+ export async function removeAlias(opts: AliasOptions = {}): Promise<void> {
115
+ const shell = await detectShell();
116
+ if (!shell) {
117
+ logManualInstructions("remove", opts);
118
+ return;
119
+ }
120
+
121
+ const removed = await removeFromRcFile(shell.rcPath, MARKER_OPEN, MARKER_CLOSE);
122
+ if (opts.json) {
123
+ emitJson("remove", { removed, rcPath: shell.rcPath });
124
+ return;
125
+ }
126
+ if (!removed) {
127
+ emit("No Pithy alias installed.");
128
+ return;
129
+ }
130
+ emit(`Removed \`${shell.aliasSyntax}\` from ${shell.rcPath}`);
131
+ emit(`Reload your shell or run: source ${shell.rcPath}`);
132
+ }
133
+
134
+ /** Report whether the alias is installed. Prints `Unable to detect shell.` for an unknown shell. */
135
+ export async function aliasStatus(opts: AliasOptions = {}): Promise<void> {
136
+ const shell = await detectShell();
137
+ if (!shell) {
138
+ if (opts.json) {
139
+ emitJson("status", { installed: false, shell: null, rcPath: null });
140
+ return;
141
+ }
142
+ emit("Unable to detect shell.");
143
+ return;
144
+ }
145
+
146
+ const contents = await readRcFile(shell.rcPath);
147
+ const installed = contents.includes(MARKER_OPEN);
148
+ if (opts.json) {
149
+ emitJson("status", { installed, shell: shell.kind, rcPath: shell.rcPath });
150
+ return;
151
+ }
152
+ if (installed) emit(`Installed in ${shell.rcPath}`);
153
+ else emit("Not installed. Run `pithy alias` to install.");
154
+ }
155
+
156
+ /**
157
+ * The hidden root flags, in the order {@link handleHiddenFlags} tests them.
158
+ *
159
+ * Hidden from `--help`, not from anything that enumerates what the CLI parses: `bin.ts` answers them
160
+ * before citty sees the arguments, so they are in no command's `args` and a walk of the command tree
161
+ * cannot find them. `docs/catalog.ts` reads this rather than restating it — the docs catalog exists so
162
+ * the site can ask whether a flag it cites is real, and a flag missing from that answer reads as a typo
163
+ * on a page that is correct.
164
+ */
165
+ const PITHIEST = "--pithiest";
166
+ const PITHIER = "--pithier";
167
+ export const HIDDEN_ROOT_FLAGS: readonly string[] = [PITHIEST, PITHIER];
168
+
169
+ /**
170
+ * Handle the hidden root flags. `--pithier` is a synonym for `pithy alias` (install). `--pithiest` is the
171
+ * anti-feature: it prints `Pithy enough.` and writes nothing. Returns whether a flag was handled, so the
172
+ * root dispatch can skip normal command parsing. Neither flag appears in `--help`.
173
+ */
174
+ export async function handleHiddenFlags(argv: string[]): Promise<boolean> {
175
+ if (argv.includes(PITHIEST)) {
176
+ emit("Pithy enough.");
177
+ return true;
178
+ }
179
+ if (argv.includes(PITHIER)) {
180
+ await installAlias();
181
+ return true;
182
+ }
183
+ return false;
184
+ }
185
+
186
+ export default defineCommand({
187
+ meta: { name: "alias", description: "Install the `p.` shortcut for `pithy`" },
188
+ args: {
189
+ remove: { type: "boolean", default: false, description: "Uninstall the alias" },
190
+ status: { type: "boolean", default: false, description: "Report whether the alias is installed" },
191
+ json: { type: "boolean", default: false, description: "Machine-readable output" },
192
+ },
193
+ run: ({ args }) =>
194
+ withErrorReporting(args.json, async () => {
195
+ if (args.remove && args.status) {
196
+ throw new ValidationError({
197
+ message: "Pass either --remove or --status, not both.",
198
+ action: "Choose one.",
199
+ });
200
+ }
201
+ if (args.remove) return removeAlias({ json: args.json });
202
+ if (args.status) return aliasStatus({ json: args.json });
203
+ return installAlias({ json: args.json });
204
+ }),
205
+ });