austere-axi 0.5.1 → 0.6.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/README.md +94 -99
- package/dist/austere.js +126 -126
- package/dist/catalog.json +1 -1
- package/dist/chrome/chrome.js +5 -5
- package/dist/cli.mjs +458 -263
- package/dist/pack-kit.mjs +23 -23
- package/dist/packs/mermaid/catalog.json +3 -3
- package/dist/packs/mermaid/pack.js +4 -4
- package/dist/packs/shadcn/catalog.json +3 -3
- package/dist/packs/shadcn/pack.js +5 -5
- package/package.json +17 -14
- package/types/pack-kit.d.ts +4 -3
- package/types/pack.d.ts +27 -8
package/README.md
CHANGED
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
[](https://www.npmjs.com/package/austere-axi)
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
|
|
11
|
-
Review pages for coding agents.
|
|
11
|
+
Review pages for coding agents.
|
|
12
|
+
|
|
13
|
+
1. The agent writes a short [OpenUI Lang](https://github.com/thesysdev/openui) program, not HTML.
|
|
14
|
+
2. You open it in the browser, click any part, and write a note.
|
|
15
|
+
3. The agent gets each note with the name of the statement it points at, edits only those statements, and the page updates in place.
|
|
12
16
|
|
|
13
17
|

|
|
14
18
|
|
|
@@ -31,13 +35,13 @@ decide = Form("rollout", Buttons([Button("Send decision", @ToAssistant("Decision
|
|
|
31
35
|
strategyField = FormControl("Rollout", RadioGroup("strategy", [RadioItem("Big bang", "", "bigbang"), RadioItem("Dual-write", "", "dualwrite")]))
|
|
32
36
|
```
|
|
33
37
|
|
|
34
|
-
|
|
38
|
+
A full page (KPIs, diagram, table, chart, diff, decision form) is about 1,000 tokens: [`examples/session-migration.html`](examples/session-migration.html).
|
|
35
39
|
|
|
36
|
-
**
|
|
40
|
+
**See it without installing:** the [gallery](https://mandalorianbatman.github.io/austere-axi/gallery/) shows example pages next to their source and token counts. The [playground](https://mandalorianbatman.github.io/austere-axi/play/) lets you edit OpenUI Lang and see the page live.
|
|
37
41
|
|
|
38
42
|
## Why
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
Same loop as [lavish-axi](https://github.com/kunchenguid/lavish-axi). The difference: the agent never writes HTML, CSS or layout.
|
|
41
45
|
|
|
42
46
|
| | lavish-axi | austere-axi |
|
|
43
47
|
|---|---|---|
|
|
@@ -47,17 +51,17 @@ The loop is the same as [lavish-axi](https://github.com/kunchenguid/lavish-axi).
|
|
|
47
51
|
| Validation | Layout audit in the browser | `austere-axi check`, in Node, no browser |
|
|
48
52
|
| Look | Different for each agent | One dark design |
|
|
49
53
|
|
|
50
|
-
Fewer output tokens
|
|
54
|
+
Fewer output tokens: faster pages, lower cost. From the [benchmark](docs/benchmark.md):
|
|
51
55
|
|
|
52
56
|
<!-- bench:start -->
|
|
53
57
|
Across 36 paired runs (6 review pages, 2 models), lavish-axi spent a median **3.2×** the output tokens of austere-axi to produce the same page, took **2.4×** as long and cost **2.5×** as much. Answering one reviewer note took **2.7×** the output tokens.
|
|
54
58
|
<!-- bench:end -->
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
lavish-axi does better in some cases. Read the benchmark caveats before you quote the numbers.
|
|
57
61
|
|
|
58
62
|
## Install
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
Needs Node.js 22.18 or later.
|
|
61
65
|
|
|
62
66
|
**Let your agent do it.** Install the setup skill, then ask your agent to "set up austere-axi":
|
|
63
67
|
|
|
@@ -67,35 +71,40 @@ npx skills add MandalorianBatman/austere-axi --skill austere-setup
|
|
|
67
71
|
|
|
68
72
|
It checks Node, installs the `austere` skill, offers the optional items below, and opens a test page.
|
|
69
73
|
|
|
70
|
-
**Or do it yourself.** Agents can run the CLI with no install:
|
|
74
|
+
**Or do it yourself.** Agents can run the CLI with no install (`-y` skips the npm prompt):
|
|
71
75
|
|
|
72
76
|
```sh
|
|
73
77
|
npx -y austere-axi --help
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
Optional:
|
|
77
81
|
|
|
78
82
|
```sh
|
|
79
|
-
npm install -g austere-axi
|
|
83
|
+
npm install -g austere-axi # `austere-axi` command, no npx
|
|
80
84
|
npx skills add MandalorianBatman/austere-axi --skill austere # tells agents when to make a page; adds /austere
|
|
81
85
|
```
|
|
82
86
|
|
|
83
|
-
The `austere` skill
|
|
87
|
+
The `austere` skill only points at `--help` and `spec`, so it never goes stale. A global install stays on its version until `npm update -g austere-axi`.
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
### Claude Code plugin
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
Bundles the `austere` skill (and `/austere`), the `austere-setup` skill and the MCP server:
|
|
88
92
|
|
|
89
93
|
```sh
|
|
90
94
|
claude plugin marketplace add MandalorianBatman/austere-axi
|
|
91
95
|
claude plugin install austere@austere-axi
|
|
92
96
|
```
|
|
93
97
|
|
|
94
|
-
In a session: `/plugin marketplace add MandalorianBatman/austere-axi`, then `/plugin install austere@austere-axi`. The MCP server runs `npx -y austere-axi mcp
|
|
98
|
+
In a session: `/plugin marketplace add MandalorianBatman/austere-axi`, then `/plugin install austere@austere-axi`. The MCP server runs `npx -y austere-axi mcp` (needs Node 22.18+ and npm access). `claude plugin update austere` updates the skill text; the CLI comes from npm each time.
|
|
99
|
+
|
|
100
|
+
### MCP
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
`austere-axi mcp` is an MCP server on stdio. Tools: `spec`, `check`, `open`, `poll`, `reply`, `patch`, `end`. Each one runs the CLI command of the same name, so the loop is the same as on the command line.
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
- `poll` returns `status: timeout` after 50 s by default, because many clients cancel longer calls. The agent polls again; nothing is lost. `timeout_ms: 0` waits until feedback arrives.
|
|
105
|
+
- `poll` and `reply` take the `cursor` a poll returned, like `--cursor`.
|
|
106
|
+
- Pass absolute file paths. The server resolves relative paths against its own working directory, which depends on the client.
|
|
107
|
+
- No screen? Set `AUSTERE_AXI_NO_OPEN=1` in the server's `env` so `open` prints the link instead. Other [environment variables](#environment) work the same way.
|
|
99
108
|
|
|
100
109
|
**Claude Desktop** (`claude_desktop_config.json`) and **Cursor** (`~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project):
|
|
101
110
|
|
|
@@ -110,7 +119,7 @@ In a session: `/plugin marketplace add MandalorianBatman/austere-axi`, then `/pl
|
|
|
110
119
|
}
|
|
111
120
|
```
|
|
112
121
|
|
|
113
|
-
**Codex** (`~/.codex/config.toml`)
|
|
122
|
+
**Codex** (`~/.codex/config.toml`), or `codex mcp add austere -- npx -y austere-axi mcp`:
|
|
114
123
|
|
|
115
124
|
```toml
|
|
116
125
|
[mcp_servers.austere]
|
|
@@ -118,8 +127,6 @@ command = "npx"
|
|
|
118
127
|
args = ["-y", "austere-axi", "mcp"]
|
|
119
128
|
```
|
|
120
129
|
|
|
121
|
-
Or `codex mcp add austere -- npx -y austere-axi mcp`. On a machine with no screen, add `AUSTERE_AXI_NO_OPEN=1` to the server's `env` so `open` prints the link instead of launching a browser. The other [environment variables](#environment) work the same way. Pass absolute file paths: the server resolves relative ones against its own working directory, which depends on the client.
|
|
122
|
-
|
|
123
130
|
## Quick start
|
|
124
131
|
|
|
125
132
|
```sh
|
|
@@ -141,18 +148,20 @@ agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-pol
|
|
|
141
148
|
└─► page updates in place and flashes the changed statements
|
|
142
149
|
```
|
|
143
150
|
|
|
144
|
-
- **A page is a plain `.html` file
|
|
145
|
-
- **Components
|
|
146
|
-
- **
|
|
147
|
-
- **
|
|
148
|
-
- **Changes
|
|
149
|
-
- **Buttons and forms
|
|
150
|
-
- **Phone review
|
|
151
|
+
- **A page is a plain `.html` file:** one runtime `<script>` tag and one `<script type="text/openui">` block. Opened directly, it loads the runtime from the CDN. Under review, the server serves a local copy.
|
|
152
|
+
- **Components:** 58 stock OpenUI components, plus `Architecture` (`Node`, `Edge`, `Group`), `Diff` and `FileTree`. `Mermaid` and nine shadcn-style components come as bundled [packs](#components-and-packs).
|
|
153
|
+
- **Diagrams lay out automatically** ([dagre](https://github.com/dagrejs/dagre)); the agent never writes coordinates. Every node and edge is a click target. `lanes` puts each group in a column; `tone` colors a box or line to show risk. Use Mermaid for sequences, state machines, ER and gantt.
|
|
154
|
+
- **Notes** attach to the smallest named statement under the click, with detail where it exists: table row and column, `file:+line` in a diff, a diagram node, or selected text. The reviewer can move a note to a parent section.
|
|
155
|
+
- **Changes flash** after each agent turn, and the bar shows `N changed`. Click it to keep the last turn's changes outlined until you click again.
|
|
156
|
+
- **Buttons and forms send feedback.** `@ToAssistant("…")` sends a message with the form values. A button with no action sends its label.
|
|
157
|
+
- **Phone review:** with Tailscale running, the server also listens on the tailnet IP and prints a phone link. That link carries your owner key; keep it private. The review UI has a mobile layout.
|
|
151
158
|
|
|
152
159
|
## CLI
|
|
153
160
|
|
|
161
|
+
Usage errors (unknown command or flag, missing argument) exit 2 before anything runs; failures exit 1. `<command> --help` prints that command's flags and an example.
|
|
162
|
+
|
|
154
163
|
```
|
|
155
|
-
austere-axi sessions +
|
|
164
|
+
austere-axi sessions + command list
|
|
156
165
|
austere-axi <file> validate + open a review [--no-open] [--reopen]
|
|
157
166
|
austere-axi poll <file> long-poll for feedback [--cursor n] [--agent-reply "..."] [--after n] [--takeover]
|
|
158
167
|
austere-axi reply <file> post a reply without polling [--cursor n]
|
|
@@ -171,22 +180,21 @@ austere-axi share <file> --as "<name>" invite link for a teammate on your tail
|
|
|
171
180
|
austere-axi share list|revoke see invites, cut one off
|
|
172
181
|
austere-axi stop stop the background server
|
|
173
182
|
austere-axi server --persist run the server in the foreground and never idle out
|
|
174
|
-
austere-axi mcp MCP server on stdio (see
|
|
183
|
+
austere-axi mcp MCP server on stdio (see MCP)
|
|
175
184
|
```
|
|
176
185
|
|
|
177
|
-
Output is compact YAML for agents. Each `poll` item
|
|
186
|
+
Output is compact YAML for agents. Each `poll` item includes its statement and line, so the agent does not re-read the file.
|
|
178
187
|
|
|
179
|
-
Session rules
|
|
188
|
+
### Session rules
|
|
180
189
|
|
|
181
|
-
- One poll listens on a session at a time.
|
|
182
|
-
-
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
- `feedback <file>` returns notes no agent has answered, also from earlier days. Reading them acknowledges them; `reply` marks them answered.
|
|
190
|
+
- **One listener:** one `poll` listens on a session at a time.
|
|
191
|
+
- **No lost notes:** every note is written to `~/.austere-axi/hub.db` before the page is told it was sent, so it survives a server crash or restart. Feedback sent while no agent listens waits for the next poll.
|
|
192
|
+
- **Acknowledge with the cursor:** `poll` prints a `cursor`. Passing it back (`--cursor n` on the next `poll` or `reply`), or replying at all, acknowledges everything up to it. Without it, `poll` returns the same feedback again, so a crash or timed-out tool call loses nothing. `--after n` replays from any point.
|
|
193
|
+
- **End is soft:** ending a review (the agent's `end`, or **End review** in the page) means the agent stopped. The reviewer can still send notes. The next `poll` returns every unacknowledged note, including ones sent before the end or released from hold after it, and reopens the review. A batch the reviewer sent together with end stays final.
|
|
194
|
+
- **Close is hard:** only the owner's **close** (`close <file>`, or **Close review** in the page) locks the composer. `reopen <file>` or **Reopen review** unlocks it.
|
|
195
|
+
- A review the user ended or closed opens again only with `--reopen`.
|
|
196
|
+
- **Tabs closed:** `poll` returns `browser_disconnected` 10 s after the last review tab closes, or 5 min after a dropped connection (for example, a locked phone). The review stays open, so later notes reach the next `poll`.
|
|
197
|
+
- `feedback <file>` returns notes no agent has answered, from any day. Reading them acknowledges them; `reply` marks them answered.
|
|
190
198
|
|
|
191
199
|
## Components and packs
|
|
192
200
|
|
|
@@ -205,24 +213,25 @@ austere-axi components add austere-pack-gantt --trust # npm name, folder, or .
|
|
|
205
213
|
austere-axi check --fix plan.html # adds the pack tag to the page
|
|
206
214
|
```
|
|
207
215
|
|
|
208
|
-
A page names its packs in the head, so it renders
|
|
216
|
+
A page names its packs in the head, so it renders anywhere:
|
|
209
217
|
|
|
210
218
|
```html
|
|
211
219
|
<meta name="austere-packs" content="austere-pack-gantt@1.2.0 sha384-…">
|
|
212
220
|
```
|
|
213
221
|
|
|
214
|
-
- Under review, the server serves installed
|
|
222
|
+
- Under review, the server serves an installed pack only while it matches its hash in `~/.austere-axi/packs.json`. Opened directly, the page loads packs from jsDelivr with Subresource Integrity. `export` inlines them.
|
|
215
223
|
- A pack component with no pack tag is a `missing-pack` error. `check --fix` repairs it.
|
|
216
|
-
-
|
|
217
|
-
-
|
|
224
|
+
- **Bundled packs** need no tag and load only when used:
|
|
225
|
+
- `Mermaid`.
|
|
226
|
+
- `shadcn`: `Progress`, `Avatar`, `AvatarGroup`, `Tooltip`, `HoverCard`, `Breadcrumb` (`BreadcrumbItem`), `Kbd`, `Skeleton`, `Calendar` and `Timeline` (`TimelineItem`). Read-only.
|
|
218
227
|
```
|
|
219
228
|
rollout = Progress(64, "Rows migrated")
|
|
220
229
|
freeze = Calendar("2026-11", ["2026-11-04 Code freeze"])
|
|
221
230
|
```
|
|
222
231
|
|
|
223
|
-
**Trust.** Pack code runs inside your review pages, where notes and button presses also come from. Packs under `@austere-axi/` on npm install directly. Other npm packs and any `.tgz` need `--trust
|
|
232
|
+
**Trust.** Pack code runs inside your review pages, where notes and button presses also come from. Packs under `@austere-axi/` on npm install directly. Other npm packs and any `.tgz` need `--trust`, so read them first. A local folder counts as your own. A page with third-party packs shows a warning, and `poll` tells the agent.
|
|
224
233
|
|
|
225
|
-
**Write a pack:** [Writing a pack](docs/writing-a-pack.md)
|
|
234
|
+
**Write a pack:** [Writing a pack](docs/writing-a-pack.md) goes from scaffold to `npm publish`, with [austere-pack-gantt](https://github.com/MandalorianBatman/austere-pack-gantt) as the example. Short version:
|
|
226
235
|
|
|
227
236
|
```sh
|
|
228
237
|
austere-axi components new austere-pack-milestones
|
|
@@ -230,7 +239,7 @@ cd austere-pack-milestones && npm install && npm run build
|
|
|
230
239
|
austere-axi components add .
|
|
231
240
|
```
|
|
232
241
|
|
|
233
|
-
Each component uses `austereComponent` from `austere-axi/pack`: a PascalCase name, zod props in positional order (append-only after you publish), a one-line description and an example. The build loads the pack in Node, so build only code you trust.
|
|
242
|
+
Each component uses `austereComponent` from `austere-axi/pack`: a PascalCase name, zod props in positional order (append-only after you publish), a one-line description and an example. The build loads the pack in Node, so build only code you trust.
|
|
234
243
|
|
|
235
244
|
## Hub
|
|
236
245
|
|
|
@@ -243,13 +252,14 @@ austere-axi hub notes # every note, same for an agent: [words] [--project dir]
|
|
|
243
252
|
austere-axi hub off # back to the plain session list; the database stays
|
|
244
253
|
```
|
|
245
254
|
|
|
246
|
-
|
|
255
|
+
The home page shows which reviews are live and how much feedback the agent answered. `/notes` lists every note, newest first, with search, status and project filters.
|
|
247
256
|
|
|
248
|
-
Session files from
|
|
257
|
+
- Session files from before 0.5 (`~/.austere-axi/sessions/*.json`) are imported into `hub.db` on first start and renamed to `*.imported`.
|
|
258
|
+
- One server runs per state dir; a second one refuses to start. `server.lock` holds the running server's pid and port. If that server no longer answers on the port, the lock is stale and is taken over.
|
|
249
259
|
|
|
250
260
|
### Always on, behind a reverse proxy
|
|
251
261
|
|
|
252
|
-
|
|
262
|
+
Run the server under a supervisor with `--persist` so it never idles out, and point the proxy at it:
|
|
253
263
|
|
|
254
264
|
```ini
|
|
255
265
|
# ~/.config/systemd/user/austere-axi.service
|
|
@@ -265,11 +275,13 @@ Restart=on-failure
|
|
|
265
275
|
WantedBy=default.target
|
|
266
276
|
```
|
|
267
277
|
|
|
268
|
-
`systemctl --user enable --now austere-axi`.
|
|
278
|
+
Then `systemctl --user enable --now austere-axi`. CLI commands on the same machine use the running server. A newer CLI that replaces an older server also starts it with `--persist`. `AUSTERE_AXI_PERSIST=1` is the same as `--persist`.
|
|
279
|
+
|
|
280
|
+
Read the [trust model](#team-review) first: behind a proxy on the same machine, every request looks local.
|
|
269
281
|
|
|
270
282
|
## Team review
|
|
271
283
|
|
|
272
|
-
Teammates on your tailnet can review a page and leave notes under their own name. The server still runs on your machine.
|
|
284
|
+
Teammates on your tailnet can review a page and leave notes under their own name. The server still runs on your machine. No accounts, no hosted service.
|
|
273
285
|
|
|
274
286
|
```sh
|
|
275
287
|
austere-axi share plan.html --as "Ana" # prints a link; notes wait for you
|
|
@@ -278,20 +290,22 @@ austere-axi share list [--links] # who has a link
|
|
|
278
290
|
austere-axi share revoke Ana # name or id; takes effect at once
|
|
279
291
|
```
|
|
280
292
|
|
|
281
|
-
Send the link to the teammate.
|
|
293
|
+
Send the link to the teammate. It needs Tailscale (or `AUSTERE_AXI_HOST`); without one, `share` stops with an error.
|
|
282
294
|
|
|
283
|
-
- **Hold (default):** a teammate's note waits in your side panel.
|
|
295
|
+
- **Hold (default):** a teammate's note waits in your side panel. **Send** one note, **Send all**, or **Discard**. The agent does not see held notes (`poll` and `feedback` skip them) until you send them. Up to 200 held notes per teammate; more are refused until you send or discard some.
|
|
284
296
|
- **Auto:** the note goes to the agent without your approval.
|
|
285
|
-
- **Author:** `poll` and `feedback` show `from: <name>` on
|
|
286
|
-
- **Reviewers can
|
|
297
|
+
- **Author:** `poll` and `feedback` show `from: <name>` on a teammate's note. Your own notes have no `from`. The hub Notes view shows the author and can filter on it.
|
|
298
|
+
- **Reviewers can** read the page and the conversation, and add notes. They cannot end the review, export, or send held notes.
|
|
287
299
|
|
|
288
|
-
Trust model
|
|
300
|
+
**Trust model.** Anyone with the link who can reach your machine on the tailnet can post notes under that name until you revoke it. Share only with people you trust, and revoke when the review is done.
|
|
289
301
|
|
|
290
|
-
|
|
302
|
+
- The link holds a random 192-bit token. Opening it shows a "Continue as <name>" button. Pressing it (a same-origin POST) trades the token for a cookie and removes it from the address bar, so a link opened from another site cannot turn your browser into a reviewer.
|
|
303
|
+
- A request without a valid invite or the owner key is refused, on loopback too. The hub and its Notes view are yours only. A reverse proxy on the same machine gets no owner rights from looking local.
|
|
304
|
+
- Your own phone link carries the owner key (`~/.austere-axi/owner.key`); do not forward it. To revoke all old phone links and owner cookies, delete that file and restart the server.
|
|
291
305
|
|
|
292
306
|
## GitHub Action
|
|
293
307
|
|
|
294
|
-
|
|
308
|
+
Posts an austere review page on every pull request: runs `austere-axi check` on the pages you commit, exports each with `austere-axi export`, uploads them as a workflow artifact, and posts or updates **one** PR comment with a summary and a download link.
|
|
295
309
|
|
|
296
310
|
```yaml
|
|
297
311
|
# .github/workflows/austere.yml
|
|
@@ -324,12 +338,12 @@ jobs:
|
|
|
324
338
|
|
|
325
339
|
Outputs: `ok` (`true` when every page passed) and `artifact-url`.
|
|
326
340
|
|
|
327
|
-
- **A failing check fails the job.** The
|
|
341
|
+
- **A failing check fails the job.** The log and the job summary show each error with its statement and line. Pages that passed are still exported.
|
|
328
342
|
- **Fork PRs** get a read-only token, so the comment is skipped with a warning. The job summary and the artifact still carry the report.
|
|
329
|
-
- **Sticky comment:** the action updates only a marked comment posted by its own token's account (a bot account for `github.token`).
|
|
343
|
+
- **Sticky comment:** the action updates only a marked comment posted by its own token's account (a bot account for `github.token`). If someone else posts a comment with the same marker, the action leaves it alone and posts its own.
|
|
330
344
|
- **Triggers:** use `pull_request`. Do not use `pull_request_target` to run pages from a fork.
|
|
331
|
-
- **Artifact:** GitHub zips it and
|
|
332
|
-
- The action lives in this
|
|
345
|
+
- **Artifact:** GitHub zips it and needs a login to download. Unzip and open the HTML in a browser; it works offline. No Pages preview.
|
|
346
|
+
- The action lives in this repo under `action/`, so one ref pins both the action and the CLI contract it calls. `version` still picks the CLI.
|
|
333
347
|
|
|
334
348
|
## Environment
|
|
335
349
|
|
|
@@ -350,22 +364,29 @@ Outputs: `ok` (`true` when every page passed) and `artifact-url`.
|
|
|
350
364
|
|
|
351
365
|
## Security
|
|
352
366
|
|
|
353
|
-
The server binds to loopback, plus the Tailscale IP when Tailscale is up.
|
|
367
|
+
The server binds to loopback, plus the Tailscale IP when Tailscale is up. Never to all interfaces.
|
|
368
|
+
|
|
369
|
+
**Loopback is not the owner.** Other OS users on the machine can reach loopback too. The owner key (`owner.key`) is what proves ownership.
|
|
370
|
+
|
|
371
|
+
- **API:** `/api/*` answers only loopback callers that send the owner key, and refuses browser requests. `/api/health` needs no key. It proves the server holds the key with an HMAC of its port and the CLI's nonce, so the CLI never talks to, or hands the key to, another program on its port.
|
|
372
|
+
- **Browser sign-in:** when the CLI opens your browser, it gets a single-use login code from the server and opens the page with it. The server swaps the code for the owner cookie and removes it from the address bar. The key never appears on a command line or in output. A code works once, for 60 seconds. Another local user who reads it from the process list in that window can use it first; your browser then shows "used already", and `austere-axi login` gives you a new one.
|
|
373
|
+
- **Other browsers:** a printed `url` works in a browser that already has the cookie. For any other browser (SSH with a forwarded port, `--no-open`, a second browser), run `austere-axi login [file]` and open the link it prints.
|
|
374
|
+
- **Owner cookie** is an HMAC of the key, not the key. Browsers share `127.0.0.1` cookies across ports, so a program on another local port that your browser visits can read it. With it, that program can open your pages and the hub as you in a browser, but cannot call `/api/*`, which needs the key.
|
|
375
|
+
|
|
376
|
+
**Files.** On macOS and Linux the state dir is set to mode 0700 on every run. New files in it (`owner.key`, `server.log`, and `hub.db`, which holds every note) are mode 0600. Files an older version created keep their old mode, behind the 0700 dir. On Windows, file modes do not apply; only your user profile's permissions protect the state dir.
|
|
377
|
+
|
|
378
|
+
**Requests.**
|
|
354
379
|
|
|
355
|
-
- Loopback is not the owner: other OS users on the machine can reach it too. The owner key in `owner.key` is.
|
|
356
|
-
- `/api/*` answers only loopback callers that send the owner key, and refuses browser requests. `/api/health` needs no key; it proves the server holds the key with an HMAC of its port and the CLI's nonce, so the CLI never talks to, or hands the key to, another program on its port.
|
|
357
|
-
- A browser signs in with a single-use login code: when the CLI opens your browser, it asks the server for a code and opens the page with it; the server swaps it for the owner cookie and drops it from the address bar. The key is never on a command line or in printed output. A code works once and for 60 seconds. Another local user who reads it from the process list in that time can use it first; your browser then shows "used already", and `austere-axi login` gives you a new one.
|
|
358
|
-
- A printed `url` works in a browser that already has the cookie. For any other browser (SSH with a forwarded port, `--no-open`, a second browser), run `austere-axi login [file]` and open the link it prints.
|
|
359
|
-
- The owner cookie is an HMAC of the key, not the key. Browsers share `127.0.0.1` cookies across ports, so any program listening on another local port that your browser visits can read it. With it, that program can open your pages and the hub as you in a browser, but not call `/api/*`, which needs the key.
|
|
360
|
-
- On macOS and Linux the state dir is made mode 0700 on every run, and new files in it (`owner.key`, `server.log`, and `hub.db`, which holds every note) are mode 0600. Files an older version created keep their old mode, behind the 0700 dir. On Windows, file modes do not apply: the state dir is protected only by your user profile's permissions.
|
|
361
380
|
- `Host` headers must be on an allowlist (DNS-rebinding defense).
|
|
362
381
|
- Browser routes that change state refuse a foreign `Origin`.
|
|
363
382
|
- Invite tokens are random, compared in constant time, and kept out of the log. A reviewer link can only add notes to its own page. See [Team review](#team-review).
|
|
364
|
-
- The page runs in a sandboxed iframe with no same-origin access. Packs run there too. Page files are also served with a `Content-Security-Policy: sandbox` header, so the page stays sandboxed when opened in a popup or its own tab. In review, links that open a new window are opened by the review page, and only if they are external `http(s):` or `mailto:` links. A popup the frame opens any other way stays sandboxed.
|
|
365
383
|
- The server serves files only from the page's own directory, with no dotfiles.
|
|
366
|
-
- Third-party packs need `--trust`. Installed packs must match their lockfile hash; CDN loads use Subresource Integrity.
|
|
367
384
|
|
|
368
|
-
|
|
385
|
+
**Page sandbox.** The page runs in a sandboxed iframe with no same-origin access; packs run there too. Page files are also served with `Content-Security-Policy: sandbox`, so a page opened in a popup or its own tab stays sandboxed. In review, the review page opens new-window links only if they are external `http(s):` or `mailto:` links. A popup the frame opens any other way stays sandboxed.
|
|
386
|
+
|
|
387
|
+
**Packs.** Third-party packs need `--trust`. Installed packs must match their lockfile hash; CDN loads use Subresource Integrity.
|
|
388
|
+
|
|
389
|
+
**Telemetry.** OpenUI's Node side has PostHog telemetry; the CLI sets `OPENUI_TELEMETRY_DISABLED=1`. The browser runtime sends none.
|
|
369
390
|
|
|
370
391
|
## Not in v1
|
|
371
392
|
|
|
@@ -382,35 +403,9 @@ npm run build # dist/: runtime, packs, pack kit, Mermaid, spec, catalog, rev
|
|
|
382
403
|
npm test
|
|
383
404
|
npm run typecheck # tsc over src/cli, src/shared and the pack types
|
|
384
405
|
node dist/cli.mjs examples/session-migration.html
|
|
385
|
-
npm run site # _site/: the website (needs Chrome or CHROME_PATH); see site/ and examples/
|
|
386
406
|
```
|
|
387
407
|
|
|
388
|
-
|
|
389
|
-
|---|---|
|
|
390
|
-
| `src/runtime/components/` | Custom components (`Architecture` family, `Diff`, `FileTree`), one file each |
|
|
391
|
-
| `src/runtime/stock.js` | Which stock components ship, which stay out of `spec`, their groups |
|
|
392
|
-
| `docs/` | [Writing a pack](docs/writing-a-pack.md) |
|
|
393
|
-
| `packs/` | Packs bundled with austere-axi (`mermaid`, `shadcn`) |
|
|
394
|
-
| `src/runtime/pack-api.js`, `src/pack-kit/` | `austere-axi/pack` and the `components build` pipeline |
|
|
395
|
-
| `src/shared/catalog.js` | Component catalog and prompt tiers |
|
|
396
|
-
| `src/runtime/` | Library, annotation layer, review bridge, styles |
|
|
397
|
-
| `src/chrome/` | Review UI around the page iframe |
|
|
398
|
-
| `src/cli/` | CLI, server, sessions, `check`, `export`, packs, hub |
|
|
399
|
-
| `action/` | The [GitHub Action](#github-action) (not published to npm) |
|
|
400
|
-
| `skills/` | Published skills: `austere`, `austere-setup` |
|
|
401
|
-
| `.claude-plugin/` | Claude Code marketplace and plugin manifest (in git, not in the npm package) |
|
|
402
|
-
|
|
403
|
-
Maintainer guides (not published): [`AGENTS.md`](AGENTS.md), [`.agents/skills/add-component`](.agents/skills/add-component/SKILL.md) to add a component, [`.agents/skills/review-change`](.agents/skills/review-change/SKILL.md) before you commit. Tests enforce the component rules: every component has a group, `spec` stays in its token budget, released prop orders are append-only (`npm run signatures`), and every example passes `check`.
|
|
404
|
-
|
|
405
|
-
Visual checks: `npm run gallery`, `npm run shot`, and `npm run test:browser` (real review loop in headless Chrome; needs Chrome or `CHROME_PATH`).
|
|
406
|
-
|
|
407
|
-
Website: `site/` holds the hand-written pages and CSS, `examples/` feeds the gallery (add a file there and it appears with a generated thumbnail), `scripts/site.mjs` builds `_site/`, and `.github/workflows/pages.yml` deploys it to GitHub Pages on every push to `main`. Examples need a `<meta name="description">` for the gallery blurb. None of it is published to npm.
|
|
408
|
-
|
|
409
|
-
Playground (`/play/`, source in `site/play/`): a textarea editor beside a sandboxed frame running `dist/austere.js`. It plays the part of the review chrome over the same `postMessage` bridge, so edits re-render in place and clicks arrive as the annotations `poll` prints. Problems come from `src/shared/check-program.js`, the same code as `austere-axi check`, bundled for the browser by `scripts/site.mjs` (with the spec, the component catalog and one template per example in `play/data.json`). The program travels in the URL hash (`#code=v1.<deflate-raw, base64url>`), so there is no server. `test/browser/play.browser.test.js` drives it in headless Chrome as part of `npm run test:browser`. Packs other than the bundled ones are not available there.
|
|
410
|
-
|
|
411
|
-
Benchmark against lavish-axi: `npm run bench` (needs model keys; see [docs/benchmark.md](docs/benchmark.md)). `npm run bench:report` rebuilds its tables from the committed results.
|
|
412
|
-
|
|
413
|
-
**CI** builds and tests every push to `main` and every pull request (Node 22, 24, 26 on Linux; Node 24 on Windows; browser suite). `action.yml` runs the GitHub Action on fixture pages (`test/fixtures/action/`). **Release:** bump `version` in `package.json`, push, and publish a GitHub Release. `publish.yml` tests again and publishes to npm with trusted publishing.
|
|
408
|
+
Repo layout, website, playground, benchmark, CI and release: [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
414
409
|
|
|
415
410
|
## License
|
|
416
411
|
|