austere-axi 0.4.1 → 0.5.1
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 +162 -22
- package/dist/austere.js +103 -94
- package/dist/catalog.json +1 -1
- package/dist/chrome/chrome.css +1 -1
- package/dist/chrome/chrome.js +9 -7
- package/dist/cli.mjs +2300 -1453
- package/dist/packs/mermaid/catalog.json +2 -2
- package/dist/packs/mermaid/pack.js +1 -1
- package/dist/packs/shadcn/catalog.json +2 -2
- package/dist/packs/shadcn/pack.js +1 -1
- package/package.json +8 -2
- package/skills/austere-setup/SKILL.md +5 -3
package/README.md
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
Review pages for coding agents. The agent writes a short [OpenUI Lang](https://github.com/thesysdev/openui) program, not HTML. You open it in the browser, click on any part, and write a note. The agent gets each note with the name of the statement it points at. It edits only those statements, and the page updates in place.
|
|
12
12
|
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
<sub>The real loop, about 30 s: [MP4](docs/demo.mp4) · [WebM](docs/demo.webm).</sub>
|
|
16
|
+
|
|
13
17
|
```
|
|
14
18
|
root = Stack([intro, flow, options, decide], "column", "l")
|
|
15
19
|
intro = Card([CardHeader("Session storage migration", "Redis → Postgres"), introText])
|
|
@@ -29,6 +33,8 @@ strategyField = FormControl("Rollout", RadioGroup("strategy", [RadioItem("Big ba
|
|
|
29
33
|
|
|
30
34
|
[`examples/session-migration.html`](examples/session-migration.html) is a full page (KPIs, diagram, table, chart, diff, decision form) in about 1,000 tokens.
|
|
31
35
|
|
|
36
|
+
**Website:** [mandalorianbatman.github.io/austere-axi](https://mandalorianbatman.github.io/austere-axi/) has a [gallery](https://mandalorianbatman.github.io/austere-axi/gallery/) of example pages (postmortem, API review, PR summary, sprint plan) next to their source and token counts, and a [playground](https://mandalorianbatman.github.io/austere-axi/play/) where you edit OpenUI Lang and see the page with nothing installed.
|
|
37
|
+
|
|
32
38
|
## Why
|
|
33
39
|
|
|
34
40
|
The loop is the same as [lavish-axi](https://github.com/kunchenguid/lavish-axi). The difference: the agent never writes HTML, CSS or layout.
|
|
@@ -36,12 +42,18 @@ The loop is the same as [lavish-axi](https://github.com/kunchenguid/lavish-axi).
|
|
|
36
42
|
| | lavish-axi | austere-axi |
|
|
37
43
|
|---|---|---|
|
|
38
44
|
| Agent writes | Full HTML + CSS + SVG | OpenUI Lang statements |
|
|
39
|
-
| After feedback |
|
|
45
|
+
| After feedback | Edits the HTML; the page reloads | Edits the named statements; the page patches in place |
|
|
40
46
|
| Feedback points at | CSS selector + text | Statement name and line, plus table cell, diff line, diagram node or selected text |
|
|
41
47
|
| Validation | Layout audit in the browser | `austere-axi check`, in Node, no browser |
|
|
42
48
|
| Look | Different for each agent | One dark design |
|
|
43
49
|
|
|
44
|
-
Fewer output tokens means faster pages and lower cost.
|
|
50
|
+
Fewer output tokens means faster pages and lower cost. From the [benchmark](docs/benchmark.md):
|
|
51
|
+
|
|
52
|
+
<!-- bench:start -->
|
|
53
|
+
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
|
+
<!-- bench:end -->
|
|
55
|
+
|
|
56
|
+
The benchmark also shows where lavish-axi does better; read its caveats before quoting the numbers.
|
|
45
57
|
|
|
46
58
|
## Install
|
|
47
59
|
|
|
@@ -70,6 +82,44 @@ npx skills add MandalorianBatman/austere-axi --skill austere # tells agents wh
|
|
|
70
82
|
|
|
71
83
|
The `austere` skill is a stub that points at `--help` and `spec`, so it does not go out of date. A global install stays on its version until `npm update -g austere-axi`.
|
|
72
84
|
|
|
85
|
+
## Install as a Claude Code plugin
|
|
86
|
+
|
|
87
|
+
The plugin bundles the `austere` skill (and `/austere`), the `austere-setup` skill and the MCP server below:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
claude plugin marketplace add MandalorianBatman/austere-axi
|
|
91
|
+
claude plugin install austere@austere-axi
|
|
92
|
+
```
|
|
93
|
+
|
|
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`, so it needs Node 22.18 or later and npm access. `claude plugin update austere` picks up new skill text; the CLI comes from npm each time.
|
|
95
|
+
|
|
96
|
+
## Use with MCP
|
|
97
|
+
|
|
98
|
+
`austere-axi mcp` is an MCP server on stdio. Its tools are `spec`, `check`, `open` (validate and open the review), `poll`, `reply`, `patch` and `end`. Each runs the CLI command of the same name and returns its output, so the review loop is the same as on the command line. `poll` returns `status: timeout` after 50 s by default, because many clients cancel longer tool calls; the agent polls again and nothing is lost. Pass `timeout_ms: 0` to wait until feedback arrives when your client allows long calls. `poll` and `reply` take the `cursor` a poll returned, as `--cursor` does.
|
|
99
|
+
|
|
100
|
+
**Claude Desktop** (`claude_desktop_config.json`) and **Cursor** (`~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project):
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"mcpServers": {
|
|
105
|
+
"austere": {
|
|
106
|
+
"command": "npx",
|
|
107
|
+
"args": ["-y", "austere-axi", "mcp"]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Codex** (`~/.codex/config.toml`):
|
|
114
|
+
|
|
115
|
+
```toml
|
|
116
|
+
[mcp_servers.austere]
|
|
117
|
+
command = "npx"
|
|
118
|
+
args = ["-y", "austere-axi", "mcp"]
|
|
119
|
+
```
|
|
120
|
+
|
|
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
|
+
|
|
73
123
|
## Quick start
|
|
74
124
|
|
|
75
125
|
```sh
|
|
@@ -95,6 +145,7 @@ agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-pol
|
|
|
95
145
|
- **Components**: 58 stock OpenUI components, plus `Architecture` (`Node`, `Edge`, `Group`), `Diff` and `FileTree`. `Mermaid` and nine shadcn-style components are bundled [packs](#components-and-packs).
|
|
96
146
|
- **Architecture diagrams** lay out automatically ([dagre](https://github.com/dagrejs/dagre)). The agent never writes coordinates. Each 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.
|
|
97
147
|
- **Annotations** go 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.
|
|
148
|
+
- **Changes**: after each agent turn the changed statements flash, and the bar shows `N changed`. That button is a toggle: on, it keeps the last turn's changes outlined until you switch it off, and each new turn replaces the outlines with its own changes.
|
|
98
149
|
- **Buttons and forms** send feedback. `@ToAssistant("…")` sends a message with the form values. A button with no action sends its label.
|
|
99
150
|
- **Phone review**: when Tailscale runs, the server also listens on the tailnet IP and prints a phone link. The link carries your owner key; keep it to yourself. The review UI has a mobile layout.
|
|
100
151
|
|
|
@@ -103,19 +154,24 @@ agent edits statements ◄── austere-axi poll plan.html ◄─┘ (long-pol
|
|
|
103
154
|
```
|
|
104
155
|
austere-axi sessions + usage
|
|
105
156
|
austere-axi <file> validate + open a review [--no-open] [--reopen]
|
|
106
|
-
austere-axi poll <file> long-poll for feedback [--agent-reply "..."] [--takeover]
|
|
107
|
-
austere-axi reply <file> post a reply without polling
|
|
108
|
-
austere-axi end <file>
|
|
157
|
+
austere-axi poll <file> long-poll for feedback [--cursor n] [--agent-reply "..."] [--after n] [--takeover]
|
|
158
|
+
austere-axi reply <file> post a reply without polling [--cursor n]
|
|
159
|
+
austere-axi end <file> stop as the agent; the reviewer can still add notes
|
|
160
|
+
austere-axi close <file> close the review: no more notes until reopen
|
|
161
|
+
austere-axi reopen <file> reopen an ended or closed review
|
|
162
|
+
austere-axi login [file] single-use link that signs a browser in as you (SSH, --no-open)
|
|
109
163
|
austere-axi feedback <file> feedback no agent has answered yet [--all] [--peek]
|
|
110
164
|
austere-axi check <file> validate the program (no browser) [--fix]
|
|
111
|
-
austere-axi patch <file> merge statements from stdin into the program
|
|
165
|
+
austere-axi patch <file> merge statements from stdin into the program (reports dropped unreferenced ones)
|
|
112
166
|
austere-axi spec [group|pack...] component reference (~5.8k tokens; groups narrow it)
|
|
113
167
|
austere-axi components ... list | search | show | add | remove | new | build
|
|
114
168
|
austere-axi export <file> one self-contained HTML file, works offline
|
|
115
|
-
austere-axi hub on|off|status|list|notes|open
|
|
169
|
+
austere-axi hub on|off|status|list|notes|open home page with every page and note
|
|
116
170
|
austere-axi share <file> --as "<name>" invite link for a teammate on your tailnet [--auto]
|
|
117
171
|
austere-axi share list|revoke see invites, cut one off
|
|
118
172
|
austere-axi stop stop the background server
|
|
173
|
+
austere-axi server --persist run the server in the foreground and never idle out
|
|
174
|
+
austere-axi mcp MCP server on stdio (see Use with MCP)
|
|
119
175
|
```
|
|
120
176
|
|
|
121
177
|
Output is compact YAML for agents. Each `poll` item has its statement and that statement's line, so the agent does not need to read the file again.
|
|
@@ -123,10 +179,14 @@ Output is compact YAML for agents. Each `poll` item has its statement and that s
|
|
|
123
179
|
Session rules:
|
|
124
180
|
|
|
125
181
|
- One poll listens on a session at a time.
|
|
182
|
+
- Every note is written to `~/.austere-axi/hub.db` before the page is told it was sent, so a note survives a crash or restart of the server.
|
|
126
183
|
- Feedback sent while no agent listens waits for the next poll.
|
|
127
|
-
- A
|
|
128
|
-
-
|
|
129
|
-
- `
|
|
184
|
+
- Feedback stays unread until the agent acknowledges it. `poll` prints a `cursor`. Passing it back (`--cursor n` on the next `poll` or `reply`), or replying at all, acknowledges everything up to it. A `poll` without either returns the same feedback again, so feedback lost on the way to the agent (a crash, a timed-out tool call) is delivered again. `--after n` replays from any point.
|
|
185
|
+
- 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 note the agent has not acknowledged, including ones sent before the end or released from hold after it, and reopens the review. A batch the reviewer sent with end stays final.
|
|
186
|
+
- Only the owner's **close** (`close <file>`, or **Close review** in the page) locks the composer. `reopen <file>` or **Reopen review** unlocks it.
|
|
187
|
+
- A review the user ended or closed does not open again unless you pass `--reopen`.
|
|
188
|
+
- If all review tabs stay closed, `poll` returns `browser_disconnected`: 10 s after a tab closes, or 5 min after a dropped connection (for example, a locked phone). The agent stops polling but does not end the review, so notes left later are picked up by the next `poll`.
|
|
189
|
+
- `feedback <file>` returns notes no agent has answered, also from earlier days. Reading them acknowledges them; `reply` marks them answered.
|
|
130
190
|
|
|
131
191
|
## Components and packs
|
|
132
192
|
|
|
@@ -162,7 +222,7 @@ A page names its packs in the head, so it renders wherever you open it:
|
|
|
162
222
|
|
|
163
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`; read them first. A local folder counts as your own. A page with third-party packs shows a warning, and `poll` tells the agent.
|
|
164
224
|
|
|
165
|
-
**Write a pack:**
|
|
225
|
+
**Write a pack:** [Writing a pack](docs/writing-a-pack.md) walks through one from scaffold to `npm publish`, using [austere-pack-gantt](https://github.com/MandalorianBatman/austere-pack-gantt), a Gantt chart pack built outside this repo, as the example. The short version:
|
|
166
226
|
|
|
167
227
|
```sh
|
|
168
228
|
austere-axi components new austere-pack-milestones
|
|
@@ -174,16 +234,38 @@ Each component uses `austereComponent` from `austere-axi/pack`: a PascalCase nam
|
|
|
174
234
|
|
|
175
235
|
## Hub
|
|
176
236
|
|
|
177
|
-
The server starts when needed and stops after 30 idle minutes. `hub on`
|
|
237
|
+
The server starts when needed and stops after 30 idle minutes. Every review is kept in `~/.austere-axi/hub.db` (SQLite) either way. `hub on` adds a home page over it and indexes every page you `check` or `patch`:
|
|
178
238
|
|
|
179
239
|
```sh
|
|
180
240
|
austere-axi hub on # http://127.0.0.1:4388/: pages by project and day, with search
|
|
181
241
|
austere-axi hub list # the same, for an agent: [words] [--project dir]
|
|
182
242
|
austere-axi hub notes # every note, same for an agent: [words] [--project dir] [--unanswered]
|
|
183
|
-
austere-axi hub off # back to
|
|
243
|
+
austere-axi hub off # back to the plain session list; the database stays
|
|
184
244
|
```
|
|
185
245
|
|
|
186
|
-
`/notes` lists every note on every page, newest first, with search, status and project filters. The home page shows which reviews are live and how much feedback the agent has answered.
|
|
246
|
+
`/notes` lists every note on every page, newest first, with search, status and project filters. The home page shows which reviews are live and how much feedback the agent has answered.
|
|
247
|
+
|
|
248
|
+
Session files from versions before 0.5 (`~/.austere-axi/sessions/*.json`) are imported into `hub.db` on first start and renamed to `*.imported`. One server runs per state dir; a second one refuses to start. `server.lock` names the running server's pid and port; a lock whose server no longer answers on that port is stale and is taken over.
|
|
249
|
+
|
|
250
|
+
### Always on, behind a reverse proxy
|
|
251
|
+
|
|
252
|
+
To serve review pages at a public address, run the server under a supervisor with `--persist` so it never idles out, and point the proxy at it:
|
|
253
|
+
|
|
254
|
+
```ini
|
|
255
|
+
# ~/.config/systemd/user/austere-axi.service
|
|
256
|
+
[Unit]
|
|
257
|
+
Description=austere-axi review server
|
|
258
|
+
|
|
259
|
+
[Service]
|
|
260
|
+
ExecStart=/usr/bin/env npx -y austere-axi server --persist
|
|
261
|
+
Environment=AUSTERE_AXI_LINK_URL=https://austere.example.com
|
|
262
|
+
Restart=on-failure
|
|
263
|
+
|
|
264
|
+
[Install]
|
|
265
|
+
WantedBy=default.target
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
`systemctl --user enable --now austere-axi`. Commands on the same machine use the running server; a newer CLI that replaces an older server starts the new one with `--persist` too. `AUSTERE_AXI_PERSIST=1` does the same as `--persist`. Read the reverse-proxy note in [Team review](#team-review) first: behind a proxy on the same machine every request looks local.
|
|
187
269
|
|
|
188
270
|
## Team review
|
|
189
271
|
|
|
@@ -198,21 +280,63 @@ austere-axi share revoke Ana # name or id; takes effect at o
|
|
|
198
280
|
|
|
199
281
|
Send the link to the teammate. The link needs Tailscale (or `AUSTERE_AXI_HOST`); without one, `share` stops with an error.
|
|
200
282
|
|
|
201
|
-
- **Hold (default):** a teammate's note waits in your side panel. Press **Send** on one note, or **Send all**. **Discard** removes it. Held notes are not in `poll` or `feedback`, so the agent does not see them until you send them.
|
|
283
|
+
- **Hold (default):** a teammate's note waits in your side panel. Press **Send** on one note, or **Send all**. **Discard** removes it. Held notes are not in `poll` or `feedback`, so the agent does not see them until you send them. Each teammate can have up to 200 notes held at once; more are refused until you send or discard some.
|
|
202
284
|
- **Auto:** the note goes to the agent without your approval.
|
|
203
285
|
- **Author:** `poll` and `feedback` show `from: <name>` on each teammate's note. Your own notes have no `from`. The hub Notes view shows the author and can filter on it.
|
|
204
286
|
- **Reviewers can:** read the page and the conversation, and add notes. They cannot end the review, export, or send held notes.
|
|
205
287
|
|
|
206
|
-
Trust model: anyone who has the link, and can reach your machine on the tailnet, can post notes under that name until you revoke it. Share links only with people you trust, and revoke when the review is done. The link holds a random 192-bit token
|
|
288
|
+
Trust model: anyone who has the link, and can reach your machine on the tailnet, can post notes under that name until you revoke it. Share links only with people you trust, and revoke when the review is done. 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 the address bar drops it. A link opened from another site therefore cannot switch your browser into a reviewer.
|
|
289
|
+
|
|
290
|
+
A request without a valid invite or the owner key is refused, on loopback too. The hub and its Notes view are yours only. Your own phone link carries the owner key (`~/.austere-axi/owner.key`); do not forward it. To make all old phone links and owner cookies stop working, delete that file and restart the server. A reverse proxy on the same machine gets no owner rights from looking local.
|
|
291
|
+
|
|
292
|
+
## GitHub Action
|
|
207
293
|
|
|
208
|
-
|
|
294
|
+
Post an austere review page on every pull request. The action runs `austere-axi check` on the pages you commit, exports each one as a self-contained HTML file with `austere-axi export`, uploads them as a workflow artifact, and posts or updates **one** comment on the PR with a summary and a download link.
|
|
295
|
+
|
|
296
|
+
```yaml
|
|
297
|
+
# .github/workflows/austere.yml
|
|
298
|
+
name: austere review
|
|
299
|
+
on: pull_request
|
|
300
|
+
|
|
301
|
+
permissions:
|
|
302
|
+
contents: read
|
|
303
|
+
pull-requests: write # to post the comment
|
|
304
|
+
|
|
305
|
+
jobs:
|
|
306
|
+
review:
|
|
307
|
+
runs-on: ubuntu-latest
|
|
308
|
+
steps:
|
|
309
|
+
- uses: actions/checkout@v7
|
|
310
|
+
- uses: MandalorianBatman/austere-axi/action@main # pin a release tag or SHA once you settle
|
|
311
|
+
with:
|
|
312
|
+
pages: reviews/*.html
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
| Input | Default | |
|
|
316
|
+
|---|---|---|
|
|
317
|
+
| `pages` | required | paths or globs, one per line, relative to the workspace |
|
|
318
|
+
| `version` | `latest` | austere-axi version from npm; pin it for reproducible runs |
|
|
319
|
+
| `artifact-name` | `austere-review` | workflow artifact that holds the exported pages |
|
|
320
|
+
| `retention-days` | repository default | artifact retention |
|
|
321
|
+
| `comment` | `true` | `false` leaves only the artifact and the job summary |
|
|
322
|
+
| `comment-tag` | `default` | names the sticky comment; use one per use of the action in the same PR |
|
|
323
|
+
| `github-token` | `github.token` | needs `pull-requests: write` to comment |
|
|
324
|
+
|
|
325
|
+
Outputs: `ok` (`true` when every page passed) and `artifact-url`.
|
|
326
|
+
|
|
327
|
+
- **A failing check fails the job.** The `check` output, with each error's statement and line, is in the log and in the job summary; pages that passed are still exported.
|
|
328
|
+
- **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`). A comment someone else writes with the same marker is left alone, and the action posts its own.
|
|
330
|
+
- **Triggers:** use `pull_request`. Do not use `pull_request_target` to run pages from a fork.
|
|
331
|
+
- **Artifact:** GitHub zips it and requires a login to download it. Unzip and open the HTML in a browser; it works offline. A Pages preview is not part of the action.
|
|
332
|
+
- The action lives in this repository, under `action/`, so one ref pins the action and the CLI contract it calls. `version` still picks the CLI.
|
|
209
333
|
|
|
210
334
|
## Environment
|
|
211
335
|
|
|
212
336
|
| Variable | Default | |
|
|
213
337
|
|---|---|---|
|
|
214
338
|
| `AUSTERE_AXI_PORT` | `4388` | server port |
|
|
215
|
-
| `AUSTERE_AXI_STATE_DIR` | `~/.austere-axi` | sessions, server log |
|
|
339
|
+
| `AUSTERE_AXI_STATE_DIR` | `~/.austere-axi` | sessions, owner key, server log (mode 0700 on macOS and Linux) |
|
|
216
340
|
| `AUSTERE_AXI_NO_OPEN` | | `1` = never open a browser |
|
|
217
341
|
| `AUSTERE_AXI_HOST` | | extra bind address (never `0.0.0.0`) |
|
|
218
342
|
| `AUSTERE_AXI_NO_TAILSCALE` | | `1` = loopback only |
|
|
@@ -220,18 +344,24 @@ Off loopback, a request without a valid invite or the owner key is refused. The
|
|
|
220
344
|
| `AUSTERE_AXI_LINK_URL` | | full origin of the printed remote link, e.g. `https://austere.example.com` behind a TLS proxy; the host is accepted without `ALLOWED_HOSTS` |
|
|
221
345
|
| `AUSTERE_AXI_ALLOWED_HOSTS` | | extra `Host` headers to accept (reverse proxy) |
|
|
222
346
|
| `AUSTERE_AXI_DISCONNECT_GRACE_MS` | `300000` | how long a dropped connection can stay away before `poll` returns; `0` disables |
|
|
223
|
-
| `AUSTERE_AXI_IDLE_TIMEOUT_MS` | `1800000` | idle shutdown; `0` disables
|
|
347
|
+
| `AUSTERE_AXI_IDLE_TIMEOUT_MS` | `1800000` | idle shutdown; `0` disables |
|
|
348
|
+
| `AUSTERE_AXI_PERSIST` | | `1`: never idle out, as `server --persist` |
|
|
224
349
|
| `AUSTERE_AXI_NPM_REGISTRY` | `npm_config_registry`, else npmjs | where `components add` gets packs |
|
|
225
350
|
|
|
226
351
|
## Security
|
|
227
352
|
|
|
228
353
|
The server binds to loopback, plus the Tailscale IP when Tailscale is up. It never binds to all interfaces.
|
|
229
354
|
|
|
230
|
-
-
|
|
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.
|
|
231
361
|
- `Host` headers must be on an allowlist (DNS-rebinding defense).
|
|
232
362
|
- Browser routes that change state refuse a foreign `Origin`.
|
|
233
363
|
- 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).
|
|
234
|
-
- The page runs in a sandboxed iframe with no same-origin access. Packs run there too.
|
|
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.
|
|
235
365
|
- The server serves files only from the page's own directory, with no dotfiles.
|
|
236
366
|
- Third-party packs need `--trust`. Installed packs must match their lockfile hash; CDN loads use Subresource Integrity.
|
|
237
367
|
|
|
@@ -252,25 +382,35 @@ npm run build # dist/: runtime, packs, pack kit, Mermaid, spec, catalog, rev
|
|
|
252
382
|
npm test
|
|
253
383
|
npm run typecheck # tsc over src/cli, src/shared and the pack types
|
|
254
384
|
node dist/cli.mjs examples/session-migration.html
|
|
385
|
+
npm run site # _site/: the website (needs Chrome or CHROME_PATH); see site/ and examples/
|
|
255
386
|
```
|
|
256
387
|
|
|
257
388
|
| Path | Contents |
|
|
258
389
|
|---|---|
|
|
259
390
|
| `src/runtime/components/` | Custom components (`Architecture` family, `Diff`, `FileTree`), one file each |
|
|
260
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) |
|
|
261
393
|
| `packs/` | Packs bundled with austere-axi (`mermaid`, `shadcn`) |
|
|
262
394
|
| `src/runtime/pack-api.js`, `src/pack-kit/` | `austere-axi/pack` and the `components build` pipeline |
|
|
263
395
|
| `src/shared/catalog.js` | Component catalog and prompt tiers |
|
|
264
396
|
| `src/runtime/` | Library, annotation layer, review bridge, styles |
|
|
265
397
|
| `src/chrome/` | Review UI around the page iframe |
|
|
266
398
|
| `src/cli/` | CLI, server, sessions, `check`, `export`, packs, hub |
|
|
399
|
+
| `action/` | The [GitHub Action](#github-action) (not published to npm) |
|
|
267
400
|
| `skills/` | Published skills: `austere`, `austere-setup` |
|
|
401
|
+
| `.claude-plugin/` | Claude Code marketplace and plugin manifest (in git, not in the npm package) |
|
|
268
402
|
|
|
269
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`.
|
|
270
404
|
|
|
271
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`).
|
|
272
406
|
|
|
273
|
-
|
|
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.
|
|
274
414
|
|
|
275
415
|
## License
|
|
276
416
|
|