@plitzi/cli 0.38.13 → 0.38.15

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,258 @@
1
1
  # @plitzi/cli
2
2
 
3
+ ## 0.38.15
4
+
5
+ ### Patch Changes
6
+
7
+ - 6cdd5cf: ## Security
8
+
9
+ - **A write another site caused is refused, cookie or none** (`@plitzi/sdk-server`, `@plitzi/sdk-shared`): the page
10
+ server guarded nothing it answers past `/auth` — a server action (`/_action`), a realtime publish, a space's `/fn/`
11
+ route, its runtime's endpoints, the deployment's own `data` stages, the adapter-only `loginPath`, `logoutPath` and
12
+ `exchangePath`. A plain `text/plain` POST from any page open in the same browser ran them, with the visitor's session
13
+ cookie when there was one (`SameSite=None` off this machine) and, in a project nobody signs in to, as whoever holds
14
+ the server's own credentials. Every unsafe request a browser says another site caused (`Sec-Fetch-Site: cross-site`,
15
+ or a foreign `Origin`) is now refused `403 reason: 'foreign'`, saying how to allow it: `crossSiteWrites.origins`
16
+ (with `auth`, `identity.platformOrigins` too) and `crossSiteWrites.paths` for a form other sites post to by design.
17
+ `Authorization`, and callers that are not browsers — webhooks, scripts — pass as before. One rule for this and the
18
+ sign-in check (`crossSiteRequest`, `csrf.ts`). A self-hosted runtime's endpoints now sit where the platform forwards a
19
+ space's — after the auth chain and this guard (`data`, was `preAuth`).
20
+
21
+ ## Testing
22
+ - **`page check` and `page shot` anywhere the project is served** (`@plitzi/cli`): `--url <origin>` for a second server
23
+ of it, staging or a deploy, instead of `npm start`'s port; `page check --space <module>` holds the pages to that space
24
+ instead of the project's — an agent's page — authored with `authorIsolated`.
25
+ - **A route with a query is checked as its page** (`@plitzi/cli`): `page check "/?v=2"` matched the query as part of
26
+ the path, found no page, and checked the space's "not found" page in its place.
27
+
28
+ - **What an element that chooses its children draws is checked** (`@plitzi/sdk-authoring`, `@plitzi/cli`): with
29
+ `choosesChildren`, the page checks owed none of its children — and checked none of them either: a dashboard drawing
30
+ three panels reported `checked: 1`. Each child it may draw, and what is inside one under no condition of its own,
31
+ now carries `shownWith` (the child it comes with) on its handle: once that child is on screen it and everything
32
+ named in it are owed like the rest of the page, and `checked` counts them. `onScreenIfChosen(handles, page)` lists
33
+ them.
34
+ - **A page that met a server error is looked at once more** (`@plitzi/cli`): `page check` and `verify` failed a page
35
+ for a `500` from somebody else's service having a bad second (`/fn/<space>/weather`), and passed on the next run with
36
+ nothing changed. A page whose requests got a `5xx` is checked again once; its report is the second look, and says
37
+ what failed the first time (`retriedAfter`). A `4xx` fails the same way twice, and is never retried.
38
+
39
+ ## Plugins
40
+ - **`useNotify`** (`@plitzi/plitzi-sdk`): a plugin shows the page's notifications — `notify('Deleted 79 recordings', {
41
+ appearance: 'danger' })` — the same toasts an `addNotification` step shows, in the space's theme and `notifications`
42
+ look, with no trigger, flow or `window` event of the space's to wire. The step's vocabulary (`NOTIFICATION_PLACEMENTS`,
43
+ `NOTIFICATION_APPEARANCES`, `NOTIFICATION_DEFAULTS`) is one list in `@plitzi/sdk-shared`, read by both.
44
+ - **`appearance: 'danger'` looks like one** (`@plitzi/plitzi-sdk`): it reached the toast library as a type it does not
45
+ know, so a danger notification showed neither the error icon nor the colour `notifications.danger` sets.
46
+ A value a notification does not take — from a flow, or a plugin in plain JavaScript — is set aside by name and said
47
+ in the console, rather than shown as the default in silence (`readNotificationOptions`, `@plitzi/sdk-shared`).
48
+ - **`useServerAction`** (`@plitzi/plitzi-sdk`, `@plitzi/sdk-authoring`, `@plitzi/sdk-interactions`,
49
+ `@plitzi/sdk-server`): a plugin runs one of the space's server actions, awaited —
50
+ `useServerAction<typeof habitDone>('habits-done')({ id })` answers `{ ok, output }` or `{ ok: false, reason, error }`
51
+ — through the `runServerAction` step itself: the same access, refusals, session handling and dev-tools entry.
52
+ `defineAction` keeps the literal types of its `call` way in, so the input is typed by the fields it declares (the
53
+ required ones required); `taskAction` types both ends from the task's `run`, and `FunctionTask<T, R>` takes what
54
+ `run` answers. `CallInputOf` / `CallOutputOf` read them. One operation is one action, never a `case` in one route.
55
+ What a run answers is typed (`ServerActionStatus`): a status the server never uses reads as a run that did not
56
+ complete, never as one that did.
57
+ The key those types ride under (`CALL_TYPES`) is exported, by `@plitzi/sdk-shared` and `@plitzi/sdk-authoring` with
58
+ `TypedActionEntry`, `CallInputOf` and `CallOutputOf`: a package that emits declarations could not write the type of an
59
+ action it exports (`TS4023` on every `export const actions = [defineAction(…)]`).
60
+ - **A plugin is drawn on the server first** (docs): a preference per device belongs in kept state (`keepState`,
61
+ `paintedState`) as a bound prop, never read from `localStorage` while rendering — React discards the whole tree on the
62
+ mismatch, and only in the browser of whoever saved something. `@plitzi/plitzi-ui` is not the page's: a plugin
63
+ importing it bundles its own copy, with its own contexts and its own look. The authoring skill's `plugin-reach.md`
64
+ says what a plugin says and to whom: a notification, another element (an event and the space's flow), a server
65
+ action, shared state, other pages.
66
+
67
+ ## Realtime
68
+ - **A channel that retains** (`@plitzi/sdk-shared`, `@plitzi/sdk-server`, `@plitzi/sdk-elements`): declared
69
+ `retain: true` (with `publish: 'server'`, which authoring requires), the server keeps the latest message of each type
70
+ on a topic — said by `ctx.publish` or a flow's `realtime.publish` — and hands them to every page that joins, marked
71
+ `retained`. A page reads them as `<channel>.retained.<type>`, a plugin as `useChannel(topic).retained`, the newest of
72
+ a type holding whichever order they arrive in; `null` forgets a type. Kept in the store the actions' `kv` uses (beside
73
+ the grants, by compare-and-swap), so every replica hands out the same; a topic keeps at most 64 types and 512 KB, and
74
+ a publish past that is refused before anything is sent. State every screen shares is one publish and one binding —
75
+ no kv key, initial read or copy in `state` of the project's own. The fleet store `realtime.grants` is now
76
+ `realtime.kv`.
77
+ - **The MCP writes every field a channel takes** (`@plitzi/sdk-mcp`): `patchSettings`'s channel had no `grant` — which
78
+ the guide taught — nor `retain` or the limits, so an agent could not declare a private channel at all. All are there
79
+ now, and the guide says what `retain` is for and that `channel_<id>.retained` holds it.
80
+
81
+ ## Authoring
82
+ - **`plitzi explain` knows every `@plitzi` package installed** (`@plitzi/cli`): a name that was not
83
+ `@plitzi/sdk-authoring`'s (`createServer`, `createJsonAdapters`, `useChannel`) was answered as the project's own code,
84
+ to `grep` in `src/`. It is read from the published types of every one installed, through their re-exports and entry
85
+ points (`@plitzi/sdk-server/handlers`), and a name two packages export — `createServer` is sdk-server's and sdk-mcp's
86
+ — is answered with both.
87
+
88
+ - **A space module somebody else wrote, authored where it reaches nothing** (`@plitzi/sdk-authoring`):
89
+ `authorIsolated(file, { timeoutMs })` of `@plitzi/sdk-authoring/node` runs `authorSpace` on it in a process Node's
90
+ permission model holds to reading the module's folder and its packages — no file written, no process started, no
91
+ variable of the caller's environment — kills one that runs too long, and validates what it answers again, so a forged
92
+ answer gains nothing. A refusal comes back as `authorSpace` says it, with the problems as data.
93
+ - **Pages an agent writes, served** (`@plitzi/sdk-server`): `createAuthoringHost({ dir, frameAncestors })` serves each
94
+ space it is handed at an address of its own (`put(space, { id }) → /?__pt=<id>`) as a draft of its one space, kept in
95
+ `dir` across restarts; `drop(id)` forgets one, and anything else answers `404` saying where the pages are.
96
+ - **A space is chosen by host or credential** (`@plitzi/sdk-server`, docs): `getSpaceDeployment` is asked on every
97
+ request, and what a page fetches after it carries nothing of its address but the host — a space chosen by the page's
98
+ query or path lost everything it asked for, and `/_plitzi/space/<hash>.json` answered a bare `404`. The contract and
99
+ the README say so (the README offered "by path prefix"), and that `404` says why.
100
+
101
+ - **`plitzi explain on` says how to read what a trigger carries** (`@plitzi/sdk-authoring`): its doc sat above a
102
+ helper, so `explain` printed only the signature. It now leads with `[named('picked', on('onPick')), …('{{ picked.seat
103
+ }}')]`, and the CLI skill's plugin section says the same.
104
+
105
+ - Updated dependencies [6cdd5cf]
106
+ - @plitzi/sdk-authoring@0.38.15
107
+
108
+ ## 0.38.14
109
+
110
+ ### Patch Changes
111
+
112
+ - 493955a: ## SDK
113
+
114
+ - **An embedded space leaves the page's theme alone** (`@plitzi/plitzi-sdk`, `@plitzi/plitzi-builder`): plitzi-ui's
115
+ `Provider` stamps `dark` on `<html>` by default — and, being `light`, takes it off — so every space mounted with
116
+ `themeScope: 'container'` turned the page around it light, and nothing put it back. The SDK and the builder now leave
117
+ the class to their own theme provider (`applyColorModeClass={false}`).
118
+ - **A space inside a space** (`@plitzi/plitzi-sdk`, `@plitzi/sdk-shared`): one drawn inside another — the `plitziSdk`
119
+ element, a plugin rendering one, the builder's canvas — knows it (`render.enclosed`). It is `container` unless it
120
+ says otherwise, wears its theme on its own root and starts from the page's when its own is `system`, keeps its
121
+ stylesheet to its own root (`@scope` — its `:root` palette and `.dark` rules repainted the outer page, its
122
+ `.plitzi-sdk` rule restyled the outer root), writes no `<title>` (`ownsHead`) and no `theme` cookie.
123
+ - **The `plitziSdk` element draws** (`@plitzi/plitzi-sdk`): it rendered in `widget` mode with no page to show, so it
124
+ drew nothing. A widget with no `currentPageId` now shows the page its home address would. And it takes a space's
125
+ documents as well as a key: `offlineData`, bound whole (an `apiContainer`'s answer, a runtime's route, what
126
+ `plitzi_render` returns) or as `offlineData.schema` and `offlineData.style` from two elements — waiting, without an
127
+ error, for the half that has not answered; one that cannot be drawn is said why, in the console and on the canvas.
128
+ - **`<PlitziSdk>` inside a plugin draws** (`@plitzi/plitzi-sdk`): it mounted a router of its own inside the page's, and
129
+ React Router refused it — `routing: 'memory'` or not. A space inside another is a widget: `raw` becomes `widget`
130
+ there, one page and no router (a frame of its own, `iframe` or `shadow`, is left as asked).
131
+ - **A space inside another is as tall as what it holds** (`@plitzi/plitzi-sdk`): its root was `min-h-screen`, as a
132
+ space that IS the page is, so a card of three lines drawn in a panel took the window's height.
133
+ - **`<PlitziSdk.Plugin component>` takes a component with its own props** (`@plitzi/plitzi-sdk`), as `render()`'s
134
+ plugins do: typed `ComponentPluginFC`, it refused any component that read its attributes.
135
+ - **An edited page keeps its address in step** (`@plitzi/plitzi-sdk`): the routes are built from the pages' own
136
+ elements, and those were read again only when the list of pages changed — an edit to one page (its slug, its flag)
137
+ leaves that list the same, and the routes kept its old address until something else rebuilt them.
138
+ - **Packages declare only what they use** (`@plitzi/sdk-dev-tools`, `@plitzi/sdk-shared`): `@plitzi/sdk-plugins`, which
139
+ the dev tools no longer import, and `terser`, which no build here runs, are no longer dependencies.
140
+ - **An edited page keeps its route up to date** (`@plitzi/plitzi-sdk`): the routes are built from the pages' own
141
+ elements, which were read again only when the list of pages changed — an edit to one page (its slug, its flag) left
142
+ the list the same and the route at the old address. They follow every edit to a page now, and stay the same object
143
+ while no page changes.
144
+ - **`@plitzi/sdk-dev-tools` no longer depends on `@plitzi/sdk-plugins`**, which nothing in it imports.
145
+ - **A dropdown's menu opens inside a card that lifts or clips** (`@plitzi/sdk-elements`, `@plitzi/sdk-shared`): drawn
146
+ inside the dropdown, an ancestor with a `transform` — a card lifting on hover — made itself the containing block of
147
+ the menu's `position: fixed`, and one with `overflow: hidden` cut it off: the menu opened and nobody saw it. The open
148
+ menu and its backdrop are drawn at the space's root (`spaceRootOf`, `@plitzi/sdk-shared/helpers/spaceRoot`); a
149
+ style written for the popup by an ancestor's selector no longer reaches it — its own classes do.
150
+ - **A copy button says when it could not copy** (`@plitzi/sdk-elements`, `@plitzi/sdk-shared`,
151
+ `@plitzi/sdk-interactions`, `@plitzi/sdk-variables`, `@plitzi/sdk-dev-tools`): a page not served over https has no
152
+ clipboard — a phone opening a development server by its address — and a code block's "Copy" threw there. It reads
153
+ "Not copied", and says why. `clipboardWriter` and `copyText` (`@plitzi/sdk-shared/helpers/clipboard`) are the one
154
+ check, the `copyToClipboard` step's too.
155
+
156
+ ## Authoring
157
+ - **`plitziSdk` is authored like any element** (`@plitzi/sdk-authoring`, `@plitzi/sdk-elements`): its declaration moved
158
+ to `@plitzi/sdk-elements` beside the others, so it has a factory — `plitziSdk({ id, bind: { offlineData: 'view.data'
159
+ } })` —, its attributes typed, and `plitzi explain` knows it.
160
+ - **A binding may write part of an attribute** (`@plitzi/sdk-authoring`): `offlineData.schema` writes into
161
+ `offlineData`, as the runtime always did; authoring refused it as an attribute nothing reads.
162
+ - **`choosesChildren`** (`@plitzi/sdk-shared`, `@plitzi/sdk-authoring`): a plugin that shows only some of the elements
163
+ put inside it — a dashboard's panels, a wizard's step — says so in its declaration, and the page checks owe none of
164
+ them on screen; what it draws is still read by the checks of the whole page (images, sideways scroll, legibility).
165
+ `verify` was red on every such page.
166
+
167
+ - **A page says how the document scrolls** (`@plitzi/sdk-elements`, `@plitzi/sdk-authoring`, `@plitzi/sdk-mcp`):
168
+ `documentScroll: 'locked'` holds the document still while the page is open — a dashboard or a board used with a
169
+ finger, where the whole page was dragged and bounced back though nothing in it scrolls — and `'noBounce'` keeps the
170
+ scroll without the rubber band or pull-to-refresh. The page draws the rule itself, so it is in the server's HTML and
171
+ leaves with the page; never inside a widget, a space drawn in another, or the builder's canvas. A Settings select,
172
+ `PageSpec.documentScroll`, `upsertPage`'s `documentScroll` (`null` undoes it) and the page read; an unknown value is
173
+ refused (`attribute-value`). `upsertPage` is now held to the lint of the page it writes, as element writes are.
174
+
175
+ ## MCP
176
+ - **`@plitzi/sdk-mcp/render`** (`@plitzi/sdk-mcp`): `plitzi_render`'s widget render on its own — no MCP server, no SSR,
177
+ no OAuth — for a project whose own agent draws in its own pages: `render`, `renderWidgetShape` to declare it as a
178
+ tool, `renderGuideText` for the agent. `base`, the style of the project's space, brings its tokens, classes and fonts
179
+ to the widget.
180
+
181
+ ## Server
182
+ - **`publicUrl` says where people reach the server** (`@plitzi/sdk-server`): it was `http://127.0.0.1:<port>` whatever
183
+ the server was. It is `https` once `serverOptions.tls` gives it a certificate, and this machine's network address
184
+ with `HOST` open to the network (what a tablet opens, what an OAuth provider sends it back to); `PUBLIC_URL` still
185
+ wins. The network addresses it prints say `https` too.
186
+ - **A server serving TLS reaches itself** (`@plitzi/sdk-server`): with a local certificate (mkcert) Node trusts nothing
187
+ the browsers were told to, so a runtime calling its own MCP failed. Its requests to its own address now connect to its
188
+ listener and are taken when it presents exactly its certificate (`reachOwnServer`, exported from `/runtime`).
189
+ - **A runtime reads compressed answers again** (`@plitzi/sdk-server`): the dispatcher `reachOwnServer` (and the
190
+ platform's `reachSpaceInside`) installs reached every other host with an undici connector that offers HTTP/2, and an
191
+ answer that came back over h2 reached Node's `fetch` without its `content-encoding` — every API that compresses read
192
+ as Brotli bytes. Other hosts are reached over HTTP/1.1 now, as Node's own `fetch` reaches them.
193
+ - **A project's server serves HTTPS from `.env`** (`@plitzi/sdk-server`, `@plitzi/sdk-shared`): `TLS_CERT` and
194
+ `TLS_KEY`, the certificate's and the key's paths — what `plitzi cert` writes — with no code of the project's;
195
+ `serverOptions.tls` still wins. A network address the certificate does not name is said at boot, the machine's
196
+ `.local` name is printed among the addresses when it is named, and a server on loopback alone says how to open it to
197
+ the network.
198
+ - **A session survives a phone on the Wi-Fi** (`@plitzi/sdk-server`): a private network address (`192.168.…`, `10.…`,
199
+ Tailscale's `100.64/10`, IPv6 unique- and link-local) and a machine's mDNS name (`studio.local`) get the local
200
+ cookie profile — `SameSite=Lax`, no `Secure`, no `Domain`. They got `SameSite=None; Secure`, which a browser drops
201
+ over http: nobody could sign in from a tablet. A deployment's own `.local` sub-domains (`app.example.local`) keep the
202
+ strict profile.
203
+ - **Hiding the dev tools on a server over HTTPS hydrates** (`@plitzi/sdk-server`): a server with a certificate speaks
204
+ HTTP/2, which sends no `Host` — only `:authority` — and the cookies named for the origin's port (the dev tools'
205
+ `plitzi_debug_<port>`, forced flags, painted state) were looked for without it. Shift+F12 hid the dev tools in the
206
+ browser, the server rendered them back, and the page failed to hydrate. Every one is read off `requestAuthority`.
207
+ - **A rebuild of plugins says it finished** (`@plitzi/sdk-server`): bundles removed under a running server (`tmp/`
208
+ deleted) were each a warning, "rebuilding…", and their "ready" at a level a project's terminal does not show. It is
209
+ one episode now: said when noticed, and again when the last is built — how many, how long, which failed.
210
+ - **`tmp/dev-server.json` records the scheme** (`@plitzi/sdk-server`): `url` is `https` with a certificate, and the
211
+ file adds the network addresses and the `publicUrl`.
212
+ - **`public/` is asked about every time** (`@plitzi/sdk-server`): it was cached for an hour — a `.js` or `.css` in it
213
+ for a year, `immutable` — so a file a project rewrites while it runs arrived stale. It is `no-cache` now, answered
214
+ `304` by its `ETag` while unchanged; `immutable` stays for what is versioned (the SDK, the plugins).
215
+
216
+ ## CLI
217
+ - **`check`, `shot`, `verify` and `visual` find a server on HTTPS** (`@plitzi/cli`): they rebuilt
218
+ `http://127.0.0.1:<port>`, so a project serving a certificate had no page checked. They read the URL `npm start`
219
+ recorded, and take a local certificate on this machine only. A project's `playwright.config.ts` comes from
220
+ `plitzi upgrade`.
221
+ - **`verify` runs the project's tests** (`@plitzi/cli`): a step `test`, after the lint, when `package.json` has a
222
+ `test` script.
223
+ - **`tsconfig.json` is the project's** (`@plitzi/cli`): the compiler options moved to `plitzi/tsconfig.base.json`, which
224
+ the CLI keeps up and `tsconfig.json` extends — so a folder of the project's own (`scripts/`, `tools/`) goes in its
225
+ `include` without changing a file of the CLI's. `plitzi doctor --fix` makes an older `tsconfig.json` extend it,
226
+ keeping only the options the project changed.
227
+ - **`doctor --fix` and `upgrade` write as the project's Prettier does** (`@plitzi/cli`): a repaired `tsconfig.json` or
228
+ `package.json` left `verify`'s format step red.
229
+ - **`upgrade` renames `usePlitziServiceContext`** (`@plitzi/cli`): with `PlitziServiceProvider` and
230
+ `PlitziServiceContextValue`, renamed in 0.38.12 with no alias — import, use and the module path that named it.
231
+ - **`skills update` compares every file** (`@plitzi/cli`): only `SKILL.md` was, so a change in a reference alone was
232
+ "up to date".
233
+ - **`NOTES.md`, the project's notes for agents** (`@plitzi/cli`): `AGENTS.md` and `CLAUDE.md` are the CLI's and
234
+ `upgrade` replaces them, so what a project's own sessions had to know had nowhere to live. `CLAUDE.md` imports
235
+ `NOTES.md`, `AGENTS.md` sends every agent to it, and nothing the CLI does touches it; `doctor --fix` writes one for
236
+ a project that has none.
237
+
238
+ - **`start:dev-inspect`** (`@plitzi/cli`): a server project's `start:dev` with Node's inspector open on
239
+ 127.0.0.1:9229, so a debugger (VS Code, `chrome://inspect`) attaches to the server, and again after each restart.
240
+ `plitzi upgrade` adds it; `plitzi runtime add` has it restart on the runtime too.
241
+ - **`plitzi cert`** (`@plitzi/cli`): HTTPS while developing, for a phone or a tablet on the Wi-Fi — over http its
242
+ browser gives a page no microphone, camera or clipboard. A certificate from mkcert's local authority for this machine,
243
+ its network addresses and its `.local` name, in `tmp/tls/`, named in `.env`; what to install on the phone, said.
244
+ - **A browser project opens to the network** (`@plitzi/cli`): its `vite.config.ts` reads `HOST`, `TLS_CERT` and
245
+ `TLS_KEY` from `.env`, as a server project does, and writes `tmp/dev-server.json` — the port Vite took and the scheme
246
+ — so `page check`, `page shot` and `visual` find it on another port or over HTTPS. `plitzi upgrade` brings it.
247
+
248
+ ## Docs
249
+ - **Two plugins share through `state`** (`@plitzi/sdk-authoring` skill): each plugin is a bundle of its own, so a module
250
+ both import is two copies, and what one keeps there the other never sees.
251
+ - **`verify --help`** names the project's tests among its steps.
252
+
253
+ - Updated dependencies [493955a]
254
+ - @plitzi/sdk-authoring@0.38.14
255
+
3
256
  ## 0.38.13
4
257
 
5
258
  ### Patch Changes
package/README.md CHANGED
@@ -125,7 +125,9 @@ when an install fails the CLI says which setting names it.
125
125
  - **The authoring skill**, in `.claude/skills/`, so an agent working in the project knows how a space is put
126
126
  together before it touches one: a cheatsheet to start from, references by subject and recipes by intent — each a
127
127
  file that authors with no warning. `.claude` is left out of the project's lint and formatting.
128
- - **`AGENTS.md`**: the commands, the port, where data goes, how to look at a page, and what not to read.
128
+ - **`AGENTS.md`**: the commands, the port, where data goes, how to look at a page, and what not to read. The CLI's,
129
+ like `CLAUDE.md`, which imports it: `upgrade` replaces both. What the project's own sessions have to know goes in
130
+ **`NOTES.md`**, which both point at and nothing the CLI does touches.
129
131
  - **Quiet output.** `author` prints one line when the space is fine (every problem with its code when it is not), the
130
132
  server only what goes wrong (`npm start -- --verbose` for every request), `typecheck` one line per error. Under the
131
133
  warnings, `author` prints the space's **suggestions**
@@ -147,8 +149,9 @@ when an install fails the CLI says which setting names it.
147
149
  | `.plitzi/` | What the CLI records about the project: the space it came from (`space.json`), the functions' working copy, the files `create` wrote — what `space pull`, `space push` and `upgrade` stand on | yes |
148
150
 
149
151
  **`plitzi/` is the CLI's; `src/` is yours — but `src/main.ts`.** `plitzi/` holds `author.ts`, the types plugins import
150
- (`assets.d.ts`, server mode) or the page's base styles (`preflight.css`, client mode), and a `README.md` saying what
151
- each folder of `src/` is. `src/main.ts`, the entry point, is the CLI's too, kept in `src/` where an entry point is
152
+ (`assets.d.ts`, server mode) or the page's base styles (`preflight.css`, client mode), the compiler options
153
+ (`tsconfig.base.json`, which the project's own `tsconfig.json` extends — a folder of yours, `scripts/` or `tools/`, goes
154
+ in its `include`), and a `README.md` saying what each folder of `src/` is. `src/main.ts`, the entry point, is the CLI's too, kept in `src/` where an entry point is
152
155
  looked for; `upgrade` keeps all of them current, and the build compiles `src/` into `dist/main.js`. In server mode it is a few lines: it authors the space
153
156
  (`authorProjectSpace` from `@plitzi/sdk-authoring/node`, which reads `src/space/` as `author` reads it) and hands
154
157
  it, the actions and the options to `serveProject` from `@plitzi/sdk-server/project`, which wires the rest from where the
@@ -190,6 +193,8 @@ plitzi page check / --ssr # and what the server's HTML lac
190
193
  plitzi page check /enterprise --click site-footer-top # click one element: what it changed, or that nothing did
191
194
  plitzi page check / --click news-send --fill news-email=ana@example.com # a form sent as a visitor fills it
192
195
  plitzi page check /studio --as maya # a page for signed-in visitors, signed in through /auth first
196
+ plitzi page check / --url https://staging.example.com # the project served elsewhere: a second server, staging, a deploy
197
+ plitzi page check "/?__pt=weather" --url https://localhost:8091 --space tmp/pages/weather.ts # a page not the project's
193
198
  plitzi page shot /about --width 390 --scheme dark
194
199
  plitzi page shot / --frames 4 --every 500 # what moves: a marquee, an autoplay
195
200
  plitzi page shot / --viewport --click .knob open-button --frames 4 --every 250 --sheet # an interaction, on one sheet
@@ -200,12 +205,17 @@ plitzi page shot / --clip machine --sheet --steps "click .gm__knob; wait 300; fr
200
205
  ```
201
206
 
202
207
  Both run on the project's own Playwright against its running server (`npm start`), and refuse a port that answers as
203
- another project. They wait for the page to settle — loaded, then half a second with nothing asked for — counting no
208
+ another project. `--url` points them at the project served anywhere else — a second server of it, staging, a deploy —
209
+ and `page check --space <module>` holds the pages to that space instead of the project's: what that other server
210
+ serves, a page an agent wrote (authored in a process of its own, `authorIsolated` of `@plitzi/sdk-authoring/node`). They wait for the page to settle — loaded, then half a second with nothing asked for — counting no
204
211
  stream that stays open, so a page with a realtime `channel` is checked like any other (`openPage` of
205
- `@plitzi/sdk-authoring`, which the generated `npm run visual` uses too). `page check` reports every element the space owes the page that is missing or hidden (with why), broken
212
+ `@plitzi/sdk-authoring`, which the generated `npm run visual` uses too). `page check` reports every element the space owes the page that is missing or hidden (with why) — and, under a plugin
213
+ that chooses its children (`choosesChildren`), each it draws and what is named inside it — broken
206
214
  images, sideways scroll, a link or words cut off at the screen's edge (an ancestor hiding them, nothing scrolling to
207
215
  them), words that cannot be told from what is behind them (under 2:1 — the space's elements and what a plugin draws
208
- inside one; `aria-hidden` words are a decoration), console errors, refused requests and failed flows — and the
216
+ inside one; `aria-hidden` words are a decoration), console errors, refused requests and failed flows — a page whose
217
+ requests the server failed (`5xx`) is looked at once more, and the report is that second look, naming what failed the
218
+ first time (`--json`: `retriedAfter`); a `4xx` is not retried — and the
209
219
  page's data: a binding that reads a path its provider's answer lacks (with the keys it has) — none inside an element
210
220
  the page is not showing, which is not mounted — a provider that failed, and each list's rows, drawn and in its source
211
221
  (`feed 4 of 8 rows`, `hits not rendered (16 in its source)`; `--json`: `lists: { id: { rendered, source } }`). A page's state in a few hundred tokens, where a screenshot costs thousands.
@@ -312,7 +322,7 @@ and that running it once more will not change the answer (kept in `tmp/refusals.
312
322
  ```bash
313
323
  plitzi element remove ent-trust # the element's call taken out, with the styles and imports only it used
314
324
  plitzi element move ent-faq --before ent-pricing # put before (or --after) a sibling of the same list of children
315
- plitzi verify # author, lint:space, typecheck, lint, format and every page: what fails
325
+ plitzi verify # author, lint:space, typecheck, lint, test, format and every page: what fails
316
326
  plitzi verify --no-pages # the pages left out, said as such
317
327
  ```
318
328
 
@@ -420,7 +430,7 @@ plitzi explain class-and-css # a problem's code: what was wrong, what to writ
420
430
  plitzi explain content-attribute # a suggestion's code: what is written the long way, and the short one
421
431
  plitzi explain bindTemplate # a helper: how it is written, what it is for, an example
422
432
  plitzi explain motion # the arrivals, triggers and loops the SDK plays
423
- plitzi explain pageFamily # any other export of @plitzi/sdk-authoring: its signature and its doc's first line
433
+ plitzi explain pageFamily # any other export of a @plitzi package installed — createServer is sdk-server's and sdk-mcp's
424
434
  plitzi explain --list steps # every one of a kind: elements, steps, triggers, codes, transformers, helpers
425
435
  ```
426
436
 
@@ -496,6 +506,32 @@ It loads the plugins' declarations and the project's own `@plitzi/sdk-server` to
496
506
  never starts the server or the runtime. A check that cannot finish is said as an error
497
507
  of its area, never a crash.
498
508
 
509
+ ## `cert`
510
+
511
+ ```bash
512
+ plitzi cert # HTTPS for a phone or a tablet on your Wi-Fi
513
+ plitzi cert --name studio.tailnet.ts.net # a name of the machine's own too
514
+ plitzi cert --json # one object: { cert, key, names, authority, open, urls }
515
+ ```
516
+
517
+ A project opens to its network with `HOST=0.0.0.0` in `.env` — the server, or Vite, prints every address to open. Over
518
+ plain http, though, a phone's browser gives the page no microphone, camera, clipboard, geolocation or service worker:
519
+ it is not a secure context. `cert` makes a certificate for this machine — loopback, every network address it has, its
520
+ mDNS name (`studio.local`) and the names `--name` adds — in `tmp/tls/`, and sets `TLS_CERT` and `TLS_KEY` in `.env`,
521
+ which the server and Vite serve HTTPS from with no code of the project's.
522
+
523
+ It is signed by [mkcert](https://github.com/FiloSottile/mkcert)'s local authority, which `cert` asks you to install when
524
+ it is missing (`brew install mkcert`, `apt install mkcert`, `choco install mkcert`). At a terminal it runs
525
+ `mkcert -install` — it may ask for your password — so this machine's browsers trust it; with nobody there (an agent,
526
+ `--json`) it leaves that step to you. The phone trusts it once you install the authority there: `rootCA.pem`, in
527
+ `mkcert -CAROOT` — that file only, never `rootCA-key.pem` beside it — sent by AirDrop or mail and trusted fully in
528
+ Settings › General › About › Certificate Trust Settings on an iPhone; installed as a CA certificate under Settings ›
529
+ Security on Android.
530
+
531
+ A certificate names the addresses it was made for: when the router hands the machine a new one, the server says so at
532
+ boot, and `cert` makes one again — or open the `.local` name, which stays. A space read from Plitzi by a browser project
533
+ answers the address the phone opens once it is among the space's allowed domains.
534
+
499
535
  ## `feedback`
500
536
 
501
537
  ```bash
@@ -793,6 +829,7 @@ Every project has a `.env`, never committed, and a `.env.example`, committed: th
793
829
  for a clone to copy and fill in. A server project's `.env` is read as each script starts —
794
830
  `node --env-file-if-exists=.env` in `start`, `start:prod` and `author`, and in `start:dev`, which Node watches,
795
831
  `node --import @plitzi/sdk-server/env` (Node's flag there has its watcher restart on every write in the project) —
832
+ `start:dev-inspect` is the same with Node's inspector open on 127.0.0.1:9229, for a debugger to attach —
796
833
  before any of its modules is evaluated, so `src/config/serverOptions.ts` and the actions find their settings in
797
834
  `process.env` as they load. A change to it is read on the next start, and a deployment that sets its environment needs
798
835
  no file. `start:prod` also sets `NODE_ENV=production` itself — without it a deployed server is a development one, its
@@ -801,4 +838,5 @@ deployment started some other way says `devMode: false` in `src/config/serverOpt
801
838
  project's is Vite's: only `VITE_*` reaches the page, and ships in it.
802
839
  `create` gives each one a signing key there, made for it: `PLITZI_SIGNING_SECRET`, what `ctx.sign` and `ctx.verify`
803
840
  sign with — at least 32 characters (`doctor --fix` writes one where it is missing). `PORT` is left commented out: 8080,
804
- or the next free port while developing.
841
+ or the next free port while developing; so are `HOST`, loopback until it opens the project to the network, and
842
+ `TLS_CERT` and `TLS_KEY`, which `cert` sets.