@thebaycloud/cli 1.0.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,224 @@
1
+ # Changelog
2
+
3
+ ## 0.12.1
4
+
5
+ **A ship now says who is shipping.**
6
+
7
+ Every deploy request carries `x-supersonic-who`, set from `SUPERSONIC_WHO` and
8
+ nothing else — no TTY check, no `CI` check, no guessing. An agent that sets
9
+ `SUPERSONIC_WHO=agent` is recorded as one; anything else, including a CI
10
+ runner with no terminal at all, is recorded as `someone`. Reporting "agent"
11
+ just because there was no TTY would have been a confident lie in the one
12
+ field this feature exists to keep honest, so the CLI declares only what it
13
+ was told and otherwise says it doesn't know.
14
+
15
+ ## 0.12.0
16
+
17
+ **`ship` is the word now. `deploy` is an alias and always will be.**
18
+
19
+ `supersonic ship` is the act of sending your work out — it is what people
20
+ say, and it leaves "deploy" to mean the thing sysadmins do. `deploy` keeps
21
+ working, unchanged, forever: it is typed by every existing user and written
22
+ into every agent prompt and script that already exists, so it is not
23
+ deprecated, not warned about, and not going anywhere. `reship` joins
24
+ `redeploy` on the same terms.
25
+
26
+ **A flag `ship` does not understand now stops it.**
27
+
28
+ It used to be dropped in silence and the deploy went ahead anyway, so a
29
+ single typo — `--drt-run` — reserved a slug, uploaded a folder and created
30
+ an app nobody asked for. Found by doing exactly that by accident. The cost
31
+ lands hardest on agents: a person sees a stray app appear, an agent reads
32
+ "deploying — your app will be live at" and reports success for something it
33
+ never requested.
34
+
35
+ ## 0.11.0
36
+
37
+ **`--dev-cmd`, `--dev-port` and `--no-preview` are gone, and the link they were
38
+ for is better without them.**
39
+
40
+ Those flags ran your app locally and tunnelled your public URL to it while the
41
+ real build ran, so the address showed something instead of a placeholder. What
42
+ the address shows now is the room: the build itself, drawn, live, at the app's
43
+ own URL — for you and for anyone you send the link to. It needs nothing running
44
+ on your machine, works the same for every stack, and turns into the app the
45
+ moment it first answers.
46
+
47
+ Passing the removed flags is now an unknown-flag error rather than a silent
48
+ no-op, so a script still passing them says so instead of quietly changing
49
+ behaviour.
50
+
51
+ One thing the tunnel got wrong goes with it. A redeploy flips an app back to
52
+ "deploying", and an open tunnel outranked the published build for that whole
53
+ window — visitors to a working app were sent to a laptop mid-edit. A landed
54
+ build is now served whatever the status says.
55
+
56
+ ## 0.10.1
57
+
58
+ **`env set` and `env unset` stop announcing a rollout that is not happening.**
59
+
60
+ Both printed "— new revision rolling out", unconditionally and on both runtimes.
61
+ An app on a node has no revisions, and the line went out even for a write that
62
+ changed nothing — which is how the command read as a success while `env` went on
63
+ listing the key you had just removed. The server now says what it actually did:
64
+ "the node applies this within about ten seconds", or the new revision on Cloud
65
+ Run. The CLI repeats that instead of guessing.
66
+
67
+ What was genuinely broken is on the server, and is already fixed for 0.10.0 as
68
+ well: anything that came from your `.env` is stored as a secret, and these two
69
+ commands only ever edited the plain variables sitting beside it. Upgrading does
70
+ not fix that and staying does not break it — this release only stops the CLI
71
+ describing it wrongly.
72
+
73
+ ## 0.10.0
74
+
75
+ **`supersonic init` and `supersonic check` — the deploy loop, on your machine, in
76
+ two seconds.**
77
+
78
+ The loop for getting a `supersonic.json` right ran through a real deploy: upload,
79
+ provision, build, fail, read a log, guess. Eleven minutes an attempt, and eleven
80
+ attempts is two hours and a Cloud Build bill for every one of them. Both new
81
+ commands are local — no cloud, no build, no model.
82
+
83
+ `init` writes a **draft**, and says so. It reads what the files actually state: the
84
+ monorepo split, the install command from the lockfile, the build command and output
85
+ directory, the start command bound to `$PORT`, the runtime version from
86
+ `engines.node` / `.nvmrc` / `requires-python` / `.python-version`, the database from
87
+ a dependency scan, the framework, and every env var name a `process.env` /
88
+ `os.environ` grep can see. Then it prints what no static analysis can answer —
89
+ which service owns `/` when both are servers, whether `alembic upgrade head` should
90
+ run before traffic or is merely installed, whether a committed `dist/` is the
91
+ deliverable or stale, SPA-fallback intent, and which of those env var names are
92
+ secrets.
93
+
94
+ The detector doing the reading is the same one that read a `frontend/` + `backend/`
95
+ root as "Static site, 80% confidence" — its own highest-confidence answer, and
96
+ wrong. Nothing here makes it better. What changes is that its answer now lands in a
97
+ file, in front of the agent that wrote the app, instead of silently selecting a lane
98
+ on a server 200 seconds later. Never ask an agent to author JSON from nothing; ask
99
+ it to correct a draft.
100
+
101
+ `check` is that file resolved and validated exactly as a deploy would, printing per
102
+ service the command each phase runs — install, build, release, start, health, scale
103
+ — and exiting non-zero on any problem. It catches a static service that declared a
104
+ migration, a build with nowhere to publish from, two services claiming one path, a
105
+ directory that is not there, and a `requires-python` the runner does not have.
106
+
107
+ Both go through the control plane's own resolver, compiled into `vendor/resolve.js`
108
+ rather than ported. A second implementation would agree the day it was written and
109
+ diverge the first time a lane changed, and "check passes, the deploy resolves
110
+ differently" is indistinguishable from the platform being broken. `npm run bundle`
111
+ rebuilds it and a test fails a stale one — which caught the vendored detector still
112
+ answering `python:3.12` two days after the runner moved to 3.14, a number that
113
+ decided the `FROM python:…-slim` of every containerised Python build.
114
+
115
+ **git decides what gets uploaded, and the CLI says what it left behind.**
116
+
117
+ Packaging was nineteen `tar --exclude=` patterns plus `--exclude-from=.gitignore`.
118
+ tar matches those on basename at any depth, so a module named `src/build/`, a
119
+ Composer or `go mod vendor` `app/vendor/`, and a committed `dist/` that *was* the
120
+ deliverable were stripped out of the upload — silently, so the first sign of it was
121
+ "module not found" three stages later. And tar does not speak gitignore: `!keep.js`
122
+ was a literal filename, an anchored `/dist` matched nothing, `**/` meant something
123
+ else. `deploy` now runs `git ls-files --cached --others --exclude-standard` and packs
124
+ exactly that, so negations, anchors, nested .gitignore files and your global excludes
125
+ all work the way `git status` says they do. A folder that is not a repository still
126
+ gets the old denylist.
127
+
128
+ It also prints what it skipped and why — the ignored count with the directories
129
+ responsible, and the `.env` files held back to be sent as env vars instead.
130
+
131
+ **Four things that used to ship broken:**
132
+
133
+ - files git records as executable but whose working copy lost the bit are named
134
+ before upload, instead of arriving 0644 and exiting "permission denied"
135
+ - `.git` goes up when the build reads the version out of it (setuptools-scm,
136
+ hatch-vcs, versioneer), which otherwise publishes `0.0.0` without complaining
137
+ - paths that differ only by case are one file on your Mac and two on Linux; you now
138
+ hear about them here rather than from a failing import
139
+ - git-lfs pointers are fetched, or the deploy stops and names the files. They are
140
+ 130-byte text stubs with `.mp4` names, and every stage after this one reports
141
+ success over them.
142
+
143
+ **`status` can say an app is still coming.**
144
+
145
+ `ready ? live : down` had no third state, so an app mid-build was reported as
146
+ `○ down · revision — · env none` while its build was running normally and its URL
147
+ was serving 200. Now `◐ deploying`, with the stage it has reached.
148
+
149
+ **The live preview stops writing in your folder unannounced.**
150
+
151
+ It runs your project's dev command in your checkout, and on a fresh one it
152
+ installs first — which meant `npm install` in repositories that use bun, pnpm or
153
+ yarn, dropping a foreign lockfile into a clean tree two minutes after the command
154
+ had already returned. It now uses the package manager your lockfile names, prints
155
+ the exact command before running it, and `--no-preview` declines it entirely.
156
+
157
+ **Everything fixed since 0.9.1 that had never been published.**
158
+
159
+ Chiefly: `deploy` no longer prints "✓ your app is live at" one second in, before
160
+ anything has been built. That was fixed in the repository on 31 July and never
161
+ reached anyone, because publishing is something a person has to remember — which
162
+ is the actual bug behind this whole section.
163
+
164
+ ## 0.9.0
165
+
166
+ **Your `.env` goes up with the deploy.**
167
+
168
+ An app that reads an API key used to come up crash-looping until somebody noticed and
169
+ set the vars by hand. `deploy` now reads `.env` and `.env.local` and sets what the app
170
+ needs, so it starts working the first time.
171
+
172
+ What is deliberately left behind: vars Supersonic sets itself (`DATABASE_URL`, `PORT`,
173
+ `STORAGE_BUCKET`), anything whose value points at your own machine, and any key the app
174
+ already has — the value you set in production wins over the one in your `.env`, which is
175
+ usually a test key. Only names are ever printed, never values. `--no-env` opts out.
176
+
177
+ **The live preview works on every deploy, not just an app's first.**
178
+
179
+ The proxy preferred a published build over an open tunnel, so from an app's second
180
+ deploy onwards the URL showed the previous release and the preview was never reached.
181
+ An open tunnel now wins, for the duration of the build.
182
+
183
+ Finding the dev server's port no longer depends on it printing a line we recognise —
184
+ a server that announces nothing is found anyway, so `--dev-cmd` produces a preview on
185
+ stacks where it used to silently produce none.
186
+
187
+ ## 0.7.0
188
+
189
+ **`deploy` now builds on your machine and uploads only the result.**
190
+
191
+ Until now the CLI zipped your sources, uploaded them, and the cloud installed the
192
+ dependencies and ran the build from nothing — about 80 seconds. Your machine already has
193
+ the project and already builds it in under a second, so it does that instead and sends
194
+ only the output directory.
195
+
196
+ Measured on a real Vite project: **26 seconds** for a first deploy, **19 seconds** when
197
+ the output changed, and **1-2 seconds** when it did not change at all — the CLI hashes
198
+ what it built, asks whether that exact output is already live, and stops if it is.
199
+
200
+ Applies to projects that build to a directory: Vite, Create React App, Astro without an
201
+ SSR adapter, Next.js with `output: 'export'`, and plain static folders. Apps that run a
202
+ server still build in the cloud, unchanged — their artifact is an image, not a folder.
203
+
204
+ Every step falls back rather than failing. A server app, a project the detector cannot
205
+ place, a failed install, a failed build, an empty output directory, an unreachable
206
+ server: all of them continue to the cloud build exactly as before. `--cloud-build` forces
207
+ it.
208
+
209
+ **`deploy` signs you in by itself.** No separate `supersonic login` step the first time.
210
+
211
+ **A refused deploy now says why.** A plan limit used to surface as "the build may have
212
+ failed or timed out — check supersonic logs", sending you to hunt a failure that never
213
+ happened. It now shows what the server actually said.
214
+
215
+ **`whoami` shows your account email and plan.**
216
+
217
+ ### Notes
218
+
219
+ The stack detector is now compiled into this package rather than reimplemented in it, so
220
+ the CLI and the server always agree about what a project is.
221
+
222
+ Nothing you were relying on changed shape: `deploy`, `apps`, `status`, `logs`, `errors`,
223
+ `env`, `exec`, `rollback` and `diagnose` all behave as before, and `--json` output is
224
+ unchanged.
package/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # @thebaycloud/cli
2
+
3
+ Deploy anything to [Bay](https://app.thebay.cloud) in one command.
4
+
5
+ ```bash
6
+ npm install -g @thebaycloud/cli
7
+ ```
8
+
9
+ ## Usage
10
+
11
+ ```bash
12
+ bay init # write a DRAFT bay.json from this repo
13
+ bay check # what each phase would run, and what would fail
14
+ bay login # authenticate (defaults to app.thebay.cloud)
15
+ bay deploy # deploy the current repo (uses your git origin)
16
+ bay deploy --repo <url> # or deploy any public git repo
17
+ bay whoami
18
+ bay logout
19
+ ```
20
+
21
+ `bay deploy` streams the live build log — clone → detect stack → build → Cloud Run — and prints your live URL when it's up.
22
+
23
+ ## Before you deploy
24
+
25
+ `init` and `check` are local. No cloud, no build, no model, about two seconds each,
26
+ and they are the loop an agent authoring a `bay.json` should be in — because
27
+ the same loop through a real deploy is eleven minutes long.
28
+
29
+ `bay init` reads the repository and writes a **draft** `bay.json`: the
30
+ monorepo split, the install command from the lockfile, the build command and output
31
+ directory, the start command bound to `$PORT`, the runtime version the manifests ask
32
+ for, and the framework. Then it prints what it could not determine — which service
33
+ owns `/`, whether a migration should run before traffic, SPA fallback, which env var
34
+ names are secrets — because none of those are answerable from files, and a guess
35
+ would be indistinguishable from a decision. It refuses to overwrite an existing
36
+ config without `--force`.
37
+
38
+ `bay check` resolves and validates that file exactly as a deploy would, and
39
+ prints, per service, the command each phase runs. Non-zero exit on any problem.
40
+
41
+ Both go through `vendor/resolve.js`, which is `apps/web/lib/{resolve,app-config,
42
+ infer-services,repo-facts,lanes,plan-deps}.ts` compiled by
43
+ `scripts/bundle-resolver.mjs` — the control plane's own resolver, not a port of it.
44
+ Edit any of those and run `npm run bundle`; `test/vendor.test.js` fails on a stale
45
+ bundle, which is how a committed detector spent two days answering `python:3.12`
46
+ after the runner had moved to 3.14.
47
+
48
+ ## Options
49
+
50
+ - `bay login --url <control-plane>` — point at a different control-plane (defaults to `https://app.thebay.cloud`; also settable via `BAY_URL`).
51
+ - `bay login --email <e>` — skip the email prompt. Password can be piped via `SUPERSONIC_PASSWORD`.
52
+
53
+ Your session is stored in `~/.bay/config.json`.