deepspace 0.11.0 → 0.13.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 CHANGED
@@ -1,5 +1,203 @@
1
1
  # deepspace
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Raise the app-files ceiling to 1 GiB by giving uploads the same streaming
8
+ discipline the deploy asset transport already has.
9
+
10
+ **The 25 MiB per-file limit was never a product decision.** It was the size of
11
+ one HTTP request leaking out as a rule about files: the handler read the whole
12
+ body into a ~128 MiB isolate, so the body had to be small. Media above it had
13
+ nowhere to live — deploy assets refuse it by design and named app files as its
14
+ home, which app files then also refused.
15
+
16
+ **App files now upload the way deploy assets do.** A file above the part size
17
+ is uploaded through R2's native multipart API: `POST /api/files/multipart` →
18
+ `PUT /api/files/multipart/part` × N → `POST /api/files/multipart/complete`,
19
+ with `DELETE /api/files/multipart` abandoning one. Every part is piped into R2
20
+ through a `FixedLengthStream` with a required `Content-Length`, so the worker
21
+ holds no more than one chunk of one part and a truncated body fails the write
22
+ instead of landing a corrupt object. An upload is therefore bounded by the size
23
+ of one request, never by the size of the file. There is no second upload
24
+ system: the same shared handler, the same validation, and both mounts — the
25
+ app's `/api/files` and the owner's `/api/app-files/:appId`.
26
+
27
+ **A request that declares no length is refused with 411.** `Number(null)` is
28
+ `0`, so a chunked body would otherwise clear every size bound as "empty" and
29
+ then be read unbounded — and R2 cannot size a write it has no length for. One
30
+ helper asks that question for the control bodies and the part path alike.
31
+
32
+ **Small files still take one round trip.** The single-request path is
33
+ unchanged below the part size, and its behaviour on both mounts is covered by
34
+ regression tests.
35
+
36
+ **Limits now say which question they answer.** `MAX_APP_FILE_BYTES` is 1 GiB
37
+ (the largest file, checked at init against the declared total and again at
38
+ complete against what R2 assembled — an over-ceiling object is deleted, not
39
+ kept). `MAX_UPLOAD_REQUEST_BYTES` is 25 MiB (the most one request body may
40
+ carry). `UPLOAD_PART_BYTES` is 20 MiB — both the part size init advertises and
41
+ the hard bound on a part, because `MAX_UPLOAD_PARTS` (52) is computed against
42
+ it: admitting 25 MiB parts would have made the real in-flight bound 1300 MiB
43
+ while the advertised ceiling stayed 1 GiB.
44
+
45
+ **Both clients chunk automatically, with peak memory bounded by the part size
46
+ rather than by the file** — roughly 40 MB for a file of any size. `deepspace
47
+ app files put` reports progress per part; `useR2Files().upload` takes an
48
+ optional `onProgress`. Parts go sequentially, a retryable failure (429, 5xx, a
49
+ dropped connection) is retried **once** with the range re-read so a transient
50
+ on part 40 of 52 does not discard a gibibyte, and any other failure abandons
51
+ the session — so a retry never leaves parts holding the app's quota.
52
+
53
+ `uploadBase64` stays single-request, and its client-side budget is now derived
54
+ from the ENCODED body (`MAX_BASE64_UPLOAD_BYTES`, ~18.75 MiB decoded). Measured
55
+ against the decoded size it accepted payloads whose bodies were a third larger
56
+ than the server would ever take — a check that green-lit uploads guaranteed to 413.
57
+
58
+ **Failures are classified rather than relayed.** R2's multipart error codes
59
+ become honest answers: an unknown or expired session is a 404 telling the
60
+ caller to start a new upload, parts that cannot assemble are a 400 saying why,
61
+ a missing `Content-Length` is a 411, and only genuine faults are 5xx. A stolen
62
+ `uploadId` is inert — every call rebuilds the R2 key from the caller's own
63
+ authenticated prefix, so a session id from another app names an upload that
64
+ does not exist there.
65
+
66
+ ### Patch Changes
67
+
68
+ - Fix a set of CLI honesty defects found by two independent agent-experience
69
+ audits against 0.12.0 — cases where a command reported something the platform
70
+ was not doing.
71
+
72
+ Patch, not minor: every item is a bug fix. `dev start --host` is a new flag on
73
+ an existing command and `dev start --json` now emits the readiness envelope it
74
+ already advertised, so neither adds API surface a caller could not already ask
75
+ for. Under this repo's 0.x policy that stays patch-appropriate.
76
+ - **`status` no longer reports a live release for an undeployed app.** Liveness
77
+ came from the append-only release log, which undeploy never touches, so
78
+ `status` printed `Live release #4 · https://…` while the edge 404'd and
79
+ `app list` said `undeployed`. Both commands now read one predicate over the
80
+ registry row, and `app list` reports collaborated apps (with a ROLE column)
81
+ instead of hiding the apps a collaborator can deploy but never discover.
82
+ - **The scaffolder persists its git identity.** It committed with `git -c
83
+ user.name=… -c user.email=…` and wrote nothing to `.git/config`, so the next
84
+ commit — the one `deploy` requires — died `unable to auto-detect email
85
+ address` in any container without global git config.
86
+ - **`--help` is ANSI-free when stdout is not a terminal.** citty freezes its
87
+ no-color decision at module load and only honors `NO_COLOR=1`, never
88
+ consulting stdout; help now renders through one chokepoint that strips SGR
89
+ escapes off a pipe and leaves a terminal coloured.
90
+ - **`dev start --json` emits a real readiness envelope** the moment the port
91
+ answers, instead of nothing until the server exits, and gains `--host` for
92
+ binding a specific interface. Readiness and the port pre-check both probe by
93
+ CONNECTING: a bind probe answers "can I bind this address", which on
94
+ macOS/BSD reports 127.0.0.1 free while vite serves 0.0.0.0. A readiness
95
+ timeout emits `{"ok":false,"code":"dev_server_not_ready"}` rather than
96
+ nothing, so a caller blocking on that line cannot hang.
97
+ - **Generated apps reserve the agent-protocol paths.** `/llms.txt`,
98
+ `/llms-full.txt`, `/.well-known/mcp`, `/.well-known/mcp.json` and
99
+ `/.well-known/mcp/*` fell through to the SPA shell, so a probe read the
100
+ homepage as a published manifest. They are now in the deploy worker's
101
+ `run_worker_first` baseline (the only list that reaches Cloudflare — the
102
+ CLI's reserved list is a deny-list that strips them from the app's own
103
+ config), and the app's static fallback withholds the SPA shell for them. A
104
+ real `public/llms.txt` still serves; only the shell standing in for one is
105
+ withheld.
106
+ - **`rollback` waits for edge propagation** before claiming a URL is live,
107
+ reusing deploy's wait — now one implementation instead of a private copy.
108
+ - **Human output no longer prints raw user ids.** `whoami` drops the `UserID:`
109
+ line, `releases` resolves actors to emails the way `activity` already did,
110
+ and the on-behalf deploy notice states the attribution instead of an opaque
111
+ id. All ids stay in `--json`.
112
+ - **The oversize-push recipe works when executed verbatim.** `git reset --soft`
113
+ leaves the file STAGED, so the documented re-commit re-added the same blob and
114
+ the push failed identically; the recipe now includes `git restore --staged`,
115
+ says to MOVE the file out of `public/` (`.gitignore` does not exclude it from
116
+ the deploy bundle), and is covered by a test that runs the shipped sentence
117
+ step by step against a real remote.
118
+ - **Deploy validates asset sizes before it pushes.** The commit reached the
119
+ cloud repo first, so the repo advanced onto a release the platform then
120
+ refused; the refusal now names the file and its size in MiB rather than a
121
+ SHA-256 and a raw byte count. Only the PER-FILE cap is checked locally, from
122
+ a constant the deploy worker now imports rather than duplicates — the
123
+ per-deploy total is env-configurable and the server dedupes by content hash
124
+ before summing, so any local total would be a guess doing different
125
+ arithmetic.
126
+ - **`app files put` refuses active content.** `.svg`, `.html`, `.htm`, `.js`
127
+ and `.mjs` were missing from the type map, uploaded as
128
+ `application/octet-stream`, and were stored past the server's own 415 — then
129
+ served un-renderable under a `✓`. `.xml` is deliberately left unmapped so
130
+ sitemaps and RSS feeds keep working exactly as before.
131
+ - **`app files rm` reports a missing key** instead of printing `✓ deleted` for
132
+ a name that was never there. The HTTP contract is unchanged — DELETE stays
133
+ idempotent and 200, now carrying `existed`, because deployed apps branch on
134
+ `res.ok`; the refusal is the CLI's. The oversize code is `too_large` on both
135
+ sides rather than `too_large` from the worker and `file_too_large` from the
136
+ client for one condition.
137
+ - **Ceilings appear in `--help`** for `deploy`, `push` and `app files put`,
138
+ read from the constants rather than restated.
139
+ - **The unknown-command suggester withholds the executable action when the
140
+ guess is destructive** (`rm` → `app files rm`, `delete` → `secrets delete`).
141
+ The suggestion still appears in prose; only the runnable `action` is dropped.
142
+ A whole command passed as ONE quoted token (`deepspace "auth whoami"`, which
143
+ a shell that does not word-split produces) is now recognised and reported as
144
+ a quoting problem, instead of suggesting a command that printed identically
145
+ to what was typed.
146
+ - **The collaborator-invite failure says what actually failed** — email
147
+ delivery, with the invite not created and the charge voided — instead of a
148
+ bare "please try again" that loops forever on an undeliverable address.
149
+ - **MCP discovery advertises every protocol version the handshake accepts.**
150
+ The handshake itself was already spec-correct (a server MUST echo a supported
151
+ version the client requested); the card advertising only the newest is what
152
+ made the two look like they disagreed.
153
+
154
+ - Fix documentation sites built from MDX crashing on every client-side navigation. The article's imperative code-block pass reparented each compiled `pre`, which is invisible on the default runtime but detaches a React-owned node on the executable runtime, so the next route swap threw `NotFoundError: The node to be removed is not a child of this node` and tore the article out of the page. The prose subtree now names its single writer and the imperative passes cannot reach React-rendered nodes; MDX gets the same code-block and tab-group structure from React components instead. Also fixes the assistant chip clipping the site name, the search trigger rendering at the reading scale instead of the chrome scale, and the theme control buttons sitting edge to edge.
155
+
156
+ ## 0.12.0
157
+
158
+ ### Minor Changes
159
+
160
+ - Add `deepspace app files`, and make oversized pushes and uploads fail fast with
161
+ advice that actually works.
162
+
163
+ **`deepspace app files put|list|get|rm`** reaches the app's own R2 allocation
164
+ from the command line, so images and media can be published without a deploy
165
+ and without living in Git history. Keys are relative to the app; the platform
166
+ owns the physical prefix and validates every key against it.
167
+
168
+ **An oversized push now fails fast and correctly.** A push refused with HTTP
169
+ 413 is classified as `push_too_large` and names both ceilings (20 MiB per file,
170
+ 32 MiB of compressed history per push) plus any local file over the per-file
171
+ cap. `deploy` renders a rejection with the same text `push` does, instead of a
172
+ bare "resolve the rejection".
173
+
174
+ **The oversize remediation was wrong and is fixed.** It advised `git rm
175
+ --cached` + `.gitignore` + re-commit, which provably does not work: the blob
176
+ stays reachable from the earlier commit and the push is rejected identically.
177
+ The advice now says to drop the file from the commits that carry it (or rewrite
178
+ history if already pushed), states that untracking alone is insufficient, and
179
+ points at `deepspace app files put` as media's real home. Both facts are
180
+ covered by tests that run the recipes against a real repository.
181
+
182
+ **No git call can hang the CLI any more.** `deepspace deploy` could sit
183
+ silently for minutes on a push the server had already refused, because
184
+ `spawnSync` had no timeout. Every git invocation is now bounded, and the
185
+ deploy push-retry loop has a wall-clock budget rather than only an attempt
186
+ count.
187
+
188
+ **Oversized uploads report their size, not a JSON parse error.** An upload past
189
+ the edge's request limit returns an HTML error page; the browser hook and the
190
+ CLI both fed that to `JSON.parse` and surfaced `Unexpected token '<' … is not
191
+ valid JSON`. Both now read failures as text first, and the 25 MiB ceiling is
192
+ enforced by the shared upload handler — so it binds hand-rolled requests too,
193
+ not just SDK callers. `uploadBase64` is measured on decoded bytes.
194
+
195
+ **The owner files endpoint takes human sessions only.** `APP_OWNER_JWT` (the
196
+ ten-year token baked into every deployed app) and preview tokens carry the
197
+ owner as `sub` on the same issuer and audience, so they verify. Accepting them
198
+ would have let any app read and overwrite files in every other app its owner
199
+ has; tokens carrying a `scope` claim are now refused there.
200
+
3
201
  ## 0.11.0
4
202
 
5
203
  ### Minor Changes