octwin-cli 0.1.21 → 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/CHANGELOG.md +478 -350
- package/README.md +27 -4
- package/dist/index.js +1295 -94
- package/dist/lib/args-check.js +27 -10
- package/dist/lib/kb-path.js +76 -0
- package/dist/lib/page.js +61 -0
- package/dist/lib/render-check.js +20 -8
- package/dist/lib/validate.js +11 -2
- package/package.json +37 -37
- package/templates/starter/manifest.yaml +55 -54
package/CHANGELOG.md
CHANGED
|
@@ -1,350 +1,478 @@
|
|
|
1
|
-
# Changelog — octwin-cli
|
|
2
|
-
|
|
3
|
-
All notable changes to the `octwin` CLI (npm package [`octwin-cli`](https://www.npmjs.com/package/octwin-cli)).
|
|
4
|
-
Format: [Keep a Changelog](https://keepachangelog.com/) — newest first, bucketed into
|
|
5
|
-
**Added · Changed · Deprecated · Removed · Fixed · Security**. The platform-wide view lives in the
|
|
6
|
-
repo root [`CHANGELOG.md`](../../CHANGELOG.md); this file is the CLI-only cut that ships with the package.
|
|
7
|
-
|
|
8
|
-
## [0.1
|
|
9
|
-
|
|
10
|
-
###
|
|
11
|
-
- **`
|
|
12
|
-
`
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
###
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
the
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
- **`octwin
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
-
|
|
118
|
-
`
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
###
|
|
139
|
-
- **`
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
###
|
|
245
|
-
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
`
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
- **`octwin
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
1
|
+
# Changelog — octwin-cli
|
|
2
|
+
|
|
3
|
+
All notable changes to the `octwin` CLI (npm package [`octwin-cli`](https://www.npmjs.com/package/octwin-cli)).
|
|
4
|
+
Format: [Keep a Changelog](https://keepachangelog.com/) — newest first, bucketed into
|
|
5
|
+
**Added · Changed · Deprecated · Removed · Fixed · Security**. The platform-wide view lives in the
|
|
6
|
+
repo root [`CHANGELOG.md`](../../CHANGELOG.md); this file is the CLI-only cut that ships with the package.
|
|
7
|
+
|
|
8
|
+
## [0.5.1] - 2026-08-01
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **`validate --remote` blamed the platform's version for a bad `--project`.** Every 404 from
|
|
12
|
+
`POST /api/self/p/packs/validate` printed *"this platform has no /packs/validate endpoint yet
|
|
13
|
+
(older version)"*. That was accurate when the route resolved nothing, but it now resolves the
|
|
14
|
+
tenant and project **before** validating — and an unknown project, or one outside a token's pin,
|
|
15
|
+
answers 404 by design. So a typo in `--project` sent you looking for a version mismatch instead of
|
|
16
|
+
at the flag. The two cases are now told apart by the response **body**, not the status: a missing
|
|
17
|
+
route is Fastify's `error: 'Not Found'` and keeps the old wording, while the platform's own
|
|
18
|
+
`project '<slug>' not found under tenant` is printed as-is, with the flags to check.
|
|
19
|
+
|
|
20
|
+
## [0.5.0] - 2026-08-01
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- **`octwin seed [--pack <packId>]`** — apply the pack's demo/reference data to the project it is
|
|
24
|
+
installed on, without redeploying: `xrm.yaml` `demo:` records + scheduling availability, the
|
|
25
|
+
commerce catalog, and the demo operator topology. Reports what **each kind** produced.
|
|
26
|
+
Previously seeding was reachable only as `deploy --seed`, because the platform's seed endpoint was
|
|
27
|
+
keyed on an install id, guarded platform-admin, and carried no tenant/project segments — so the
|
|
28
|
+
`/api/self/**` surface could not reach it and a `pack:deploy` token never could. Re-seeding meant
|
|
29
|
+
a full redeploy or asking an operator. Idempotent and cheap to re-run: records upsert, and
|
|
30
|
+
existing media is REUSED rather than regenerated, so a second pass reports zero images.
|
|
31
|
+
|
|
32
|
+
## [0.4.0] - 2026-08-01
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- **`octwin projects create` / `octwin projects rm`** — a **disposable end-to-end environment** from
|
|
36
|
+
the CLI, which is what marketplace developers have been missing. `octwin deploy` has always needed
|
|
37
|
+
a project that already existed and the CLI could only *list* them, so standing up a throwaway
|
|
38
|
+
full deployment meant opening the console or asking an operator. Now:
|
|
39
|
+
```bash
|
|
40
|
+
octwin projects create "Scratch" # → slug `scratch`
|
|
41
|
+
octwin deploy --project scratch --seed # publish + install + demo data
|
|
42
|
+
octwin chat "hi" --project scratch # talk to it (--script for a whole conversation)
|
|
43
|
+
octwin projects rm scratch --yes # throw it away
|
|
44
|
+
```
|
|
45
|
+
A throwaway environment is deliberately **not a special kind of thing** — it is an ordinary
|
|
46
|
+
project in your own workspace, so it inherits your plan, entitlements, RBAC and teardown with no
|
|
47
|
+
separate lifecycle. `create` derives the URL slug from the name (`--slug` pins one, `--pack`
|
|
48
|
+
installs an already-published pack). `rm` is a HARD delete — the project and everything cascading
|
|
49
|
+
from it, no undo, not the same as archiving — so **without `--yes` it only prints what would be
|
|
50
|
+
destroyed**, making the dry run the default; the impact list is derived from `pg_constraint`, the
|
|
51
|
+
same payload the console's confirm dialog renders. Both need `projects:write`, which a
|
|
52
|
+
`pack:deploy` token does **not** confer — the CLI names that scope in the 403 hint.
|
|
53
|
+
|
|
54
|
+
## [0.3.0] - 2026-07-31
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
- **`octwin projects`** — the `--project <slug>` values your token can actually name, with the plan's
|
|
58
|
+
project cap. Every project-scoped command takes that flag and nothing printed the list, so an
|
|
59
|
+
author whose token was not pinned had to guess, and a wrong guess 404s identically to a project
|
|
60
|
+
that exists but has no install. `--archived` includes archived projects; `--json` for raw.
|
|
61
|
+
A `pack:deploy` token reaches it: the platform route was widened to accept `pack:deploy` alongside
|
|
62
|
+
`projects:read`, because `pack:deploy` is `access: 'special'` and confers nothing else — the
|
|
63
|
+
command would have 403ed for exactly the person it exists for.
|
|
64
|
+
- **`octwin validate --require-kb`** — turn a skipped KB-driven check into a failure, for CI.
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
- **`octwin validate` printed ✓ with two of its three checks switched off.** The render-intent and
|
|
68
|
+
primitive-argument checks read `<packDir>/.octwin/platform-kb/`, joined onto the pack directory
|
|
69
|
+
EXACTLY — so an author who pulled once at a repo root and then worked in `packs/<name>/` had both
|
|
70
|
+
checks silently disabled, and the ✓ printed above them unconditionally while the per-check ✓s lived
|
|
71
|
+
inside the `if`s. An entire batch of platform-backlog entries reached production through that hole.
|
|
72
|
+
Three fixes: the KB is now found by **walking up** (one pull at a repo root covers every pack under
|
|
73
|
+
it, `src/lib/kb-path.ts`); the readers report **three states** — absent, malformed, ok — so a
|
|
74
|
+
half-written pull no longer reads as "you haven't pulled yet"; and a skip is **announced on the
|
|
75
|
+
last line printed**, because a caveat above a ✓ does not reach a reader who stopped at the ✓.
|
|
76
|
+
Warn-and-exit-0 stays the default (the KB is a gitignored cache and `platform-kb pull` needs
|
|
77
|
+
`pack:deploy`, so a hard failure would break a fresh clone) — the defect was the silence, not the skip.
|
|
78
|
+
- **`octwin status` said "live and current" for a pack that could not receive a message.** Every field
|
|
79
|
+
it printed was process-global: `registered` is true for a pack ANY project on that instance loaded.
|
|
80
|
+
Dispatch is per-project and takes the oldest ACTIVE install, ignoring the rest. The route now
|
|
81
|
+
filters `status = 'active'` (an archived install used to report live) and compares against the real
|
|
82
|
+
dispatch target, so status can say plainly that another pack owns this project's messages.
|
|
83
|
+
- **`octwin validate --remote --project does-not-exist` answered `{ ok: true }`** while `deploy` with
|
|
84
|
+
the same flag 404ed. The platform route resolves tenant and project before validating now.
|
|
85
|
+
|
|
86
|
+
### Changed
|
|
87
|
+
- **`octwin validate --remote` also runs the platform's flow LINT.** It used to run only at pack load,
|
|
88
|
+
so a finding of severity `error` — an `assign:` whose value is a quoted literal, a `$t()` key with
|
|
89
|
+
no namespace — passed validate and failed the deploy. Offline `validate` cannot do this: the lint
|
|
90
|
+
needs a template-expanded `FlowDef`, which only the server builds.
|
|
91
|
+
- **`record_aggregate` / `record_group` now REQUIRE a scope** (`contact_id` or `all: true`) — a
|
|
92
|
+
platform contract change the offline arg check replays from the KB. Re-pull after upgrading.
|
|
93
|
+
|
|
94
|
+
## [0.2.0] - 2026-07-30
|
|
95
|
+
|
|
96
|
+
### Fixed
|
|
97
|
+
- **`records`, `cases`, `orders` and `catalog` printed a correct total and ZERO rows.** The platform
|
|
98
|
+
collapsed its eight per-domain list envelopes into one `Page<T>` whose collection key is `rows`;
|
|
99
|
+
the CLI still read `json.records` / `.cases` / `.orders` / `.products` behind a `?? []`, so the
|
|
100
|
+
fallback turned a key rename into an empty list instead of an error. `octwin records doctor`
|
|
101
|
+
reported *"5 record(s)"* and then *"(none — mint a `records:read` token …)"*, blaming the user's
|
|
102
|
+
credentials. All five readers now go through one `readPage` helper
|
|
103
|
+
(`src/lib/page.ts`) with a test pinned to the real envelope — a hand-copied key name in an
|
|
104
|
+
untested CLI is what shipped this, so the fix is the single reader, not five edits.
|
|
105
|
+
- **`--flag=value` silently did nothing.** `--limit=5` became the key `limit=5` and the command ran
|
|
106
|
+
with its default. Now parsed; `--set`/`--param` also accept repetition.
|
|
107
|
+
- **`octwin catalog` always printed `Products: 0`** — it counted the page, and the route's `total`
|
|
108
|
+
had never been read.
|
|
109
|
+
|
|
110
|
+
### Added
|
|
111
|
+
- **Write commands — a pack author can now exercise the state their pack creates, not just read it.**
|
|
112
|
+
Every one needs the matching `:write` scope; a leading verb (`create`/`note`/`transition`/…) is
|
|
113
|
+
what distinguishes a write from the read that shares its command.
|
|
114
|
+
- `records create|patch|stage|note`, `records tasks`, `records task complete`
|
|
115
|
+
- `cases assign|note|transition|decide` — `decide --dry-run` previews the customer-facing copy and
|
|
116
|
+
resulting status **without committing** (that route needs only `cases:read`)
|
|
117
|
+
- `orders transition|refund` · `catalog availability|stock` · `agents set`
|
|
118
|
+
- `scheduling rules|rule add|rule rm|exception add|exception rm`
|
|
119
|
+
- **`octwin feedback`** submits the pack's `FEEDBACK.md` to the platform team, attaching the pack
|
|
120
|
+
version, this CLI's version and the `content_hash` of the capability reference you pulled — the two
|
|
121
|
+
facts that separate a real platform gap from something already fixed or a stale KB. Replaces
|
|
122
|
+
"hand the file to the platform team".
|
|
123
|
+
- **`--offset` on every list**, and a line saying when a list was truncated and what to run next.
|
|
124
|
+
`has_more` was on every response and no command surfaced it, so a partial list looked complete.
|
|
125
|
+
|
|
126
|
+
### Changed
|
|
127
|
+
- `orders refund` **exits non-zero when the payment gateway refuses.** The route answers `200` with
|
|
128
|
+
the verdict in `gateway`, so reporting the HTTP status would tell an operator money moved when it
|
|
129
|
+
did not.
|
|
130
|
+
- A 403 on a record or case write now says it may be an **RBAC grant gap** rather than a missing
|
|
131
|
+
scope: the scope is checked on the token, then the verb is re-checked against that specific row.
|
|
132
|
+
|
|
133
|
+
## [0.1.21] - 2026-07-30
|
|
134
|
+
|
|
135
|
+
> **Never published to npm.** Bumped in-repo, then superseded hours later by 0.2.0 — everything
|
|
136
|
+
> below ships as part of that release. Kept as its own section because the changes are distinct.
|
|
137
|
+
|
|
138
|
+
### Added
|
|
139
|
+
- **`deploy` and `status` report the marketplace-listing verdict.** A manifest carrying
|
|
140
|
+
`listing.public: true` put the pack into an operator review queue, and the state existed only in
|
|
141
|
+
the console — so an author working from the CLI got no acknowledgement that the request had
|
|
142
|
+
registered, and never saw a rejection note (which the platform *requires* precisely because it is
|
|
143
|
+
their only feedback). Both commands now print it via `printPublicListing`, and stay silent for a
|
|
144
|
+
pack that never asked. `status` also distinguishes *approved* from *actually public*: an approval
|
|
145
|
+
pins the content sha it reviewed, so any edit returns the pack to the queue on its own.
|
|
146
|
+
- **`octwin pull` is discoverable.** It was implemented and answered `--help`, but appeared in
|
|
147
|
+
neither `octwin help` nor the README — the one command that recovers a deployed pack's only source
|
|
148
|
+
copy was invisible unless you already knew its name. Also added to the scope-requirement table, so
|
|
149
|
+
a 403 names `pack:deploy` instead of printing the generic hint.
|
|
150
|
+
|
|
151
|
+
### Fixed
|
|
152
|
+
- **The capability-reference drift check no longer fails silently on a narrow token.** The `?meta=1`
|
|
153
|
+
poll needs `pack:deploy`, but it rides on every networked command — so an author inspecting data
|
|
154
|
+
with a `records:read`-only token got no drift signal at all, and a stale reference is exactly what
|
|
155
|
+
leads to inventing a primitive from memory. A 401/403 there now says so once.
|
|
156
|
+
- **README: casework is declared in `worklist.yaml`, not a `cases.yaml`.** That grammar was retired;
|
|
157
|
+
following the README produced a pack whose casework silently never loaded. `octwin media generate`
|
|
158
|
+
was also missing from the command table.
|
|
159
|
+
- **`records` no longer blames the plan for a route the plan does not gate.** `GET /xrm/entities`
|
|
160
|
+
carries only the scope guard, so a 403 on the entity list wrongly cited the `records` plan feature.
|
|
161
|
+
|
|
162
|
+
### Removed
|
|
163
|
+
- **Unreachable rename branches in `init`.** The agent-id and `main`-flow renames sat behind
|
|
164
|
+
`--agent` / `--flow` options the CLI never parsed, against a template that ships no `main` flow —
|
|
165
|
+
both branches could never fire, and their patterns would have matched nothing if they had.
|
|
166
|
+
|
|
167
|
+
## [0.1.20] - 2026-07-29
|
|
168
|
+
|
|
169
|
+
### Added
|
|
170
|
+
- **`octwin validate` catches primitive arguments the platform would drop — offline, no token.** A
|
|
171
|
+
primitive takes the arguments it declares and silently ignored the rest, so an invented or
|
|
172
|
+
misspelled `args:` key deployed clean, ran clean, and simply did nothing: `record_list args: {
|
|
173
|
+
order: … }` (the argument is `sort`) never ordered, and `booking_cancel args: { booking_record_id:
|
|
174
|
+
… }` meant the REQUIRED `record_id` never arrived, so cancelling just failed. Validate now replays
|
|
175
|
+
the platform's contract from the pulled KB (`primitives/*.json` → `inputSchema`) and names the bad
|
|
176
|
+
argument, what the primitive takes, and any required argument that is missing. Scanning the 21
|
|
177
|
+
shipped marketplace packs found **ten** such errors across five packs. Keys are checked, values are
|
|
178
|
+
not — an `args:` value is normally an expression string resolved at runtime.
|
|
179
|
+
[`args-check.ts`](src/lib/args-check.ts).
|
|
180
|
+
- Silently skipped when the KB has not been pulled yet, like the render-intent check beside it.
|
|
181
|
+
- It cannot see inside a `use:` template body (expanding one is the platform's job) — `octwin
|
|
182
|
+
validate --remote` covers that case, and now also reports unreachable `$bind.<path>` reads and
|
|
183
|
+
`outputs:` port typos, which have no offline equivalent.
|
|
184
|
+
|
|
185
|
+
## [0.1.19] - 2026-07-29
|
|
186
|
+
|
|
187
|
+
### Added
|
|
188
|
+
- **`octwin validate` catches render-intent fields the platform would drop — offline, no token.** Each
|
|
189
|
+
render intent takes a fixed field set; a key outside it used to be swallowed at load and dropped at
|
|
190
|
+
render, with no error anywhere — just a card missing what the author wrote. Validate now replays the
|
|
191
|
+
platform's own contract from the pulled KB (`render-intents/*.json` → `allowed_keys`) and names the
|
|
192
|
+
bad field, the intent, and what is allowed. Scanning the 21 shipped marketplace packs found two real
|
|
193
|
+
instances in 223 intents: a `text_card` carrying `buttons` (the fallback's only escape button never
|
|
194
|
+
rendered) and a `list_picker` carrying a non-existent `group_by` (a whole mapper step feeding a
|
|
195
|
+
fabricated parameter). Silently skipped when the KB has not been pulled yet.
|
|
196
|
+
|
|
197
|
+
### Changed
|
|
198
|
+
- **`octwin platform-kb pull` now explodes the reference and writes an `INDEX.md`.** A pull used to
|
|
199
|
+
write ~787 KB across 34 flat files — about 225k tokens, more than the pack being authored — so
|
|
200
|
+
finding one primitive meant reading a 122 KB catalog. Catalogs the platform describes as
|
|
201
|
+
enumerable are now written **one file per entry** (`primitives/record_list.json`,
|
|
202
|
+
`render-intents/carousel.json`, `declarations/xrm.json`, …), with `INDEX.md` mapping every doc and
|
|
203
|
+
all 171 entries to its file with a one-line summary. Reading one primitive went from ~38k tokens to
|
|
204
|
+
~700. The layout comes from the platform (an `entries` descriptor on the bundle index) rather than
|
|
205
|
+
being hardcoded here, so this CLI keeps working against any platform version and a platform that
|
|
206
|
+
adds a catalog needs no CLI release. The pulled directory is cleared each pull, so a withdrawn
|
|
207
|
+
capability cannot linger as a file the authoring agent reads as current — its *contents* are
|
|
208
|
+
cleared rather than the directory itself, since on Windows a directory that is any shell's working
|
|
209
|
+
directory cannot be removed.
|
|
210
|
+
|
|
211
|
+
## [0.1.18] - 2026-07-29
|
|
212
|
+
|
|
213
|
+
### Fixed
|
|
214
|
+
- **The bundle collector and the platform's repo import disagreed about what a pack directory
|
|
215
|
+
contains.** `collectBundleFiles` had its own rule and kept three things the operator's GitHub
|
|
216
|
+
import drops: `*.ts`/`*.tsx`, `__snapshots__/` and `*.example`. Because the server rejects a
|
|
217
|
+
`.ts` outright, the same directory imported cleanly from GitHub and failed to deploy from
|
|
218
|
+
disk with `'…': executable code is not allowed` — an error blaming a file that was never
|
|
219
|
+
meant to ship (a snapshot test beside a flow, or the documented `xrm.yaml.example` template).
|
|
220
|
+
The rule now lives in a vendored `lib/pack-source.ts` beside the vendored validator, and a
|
|
221
|
+
parity test in the platform drives both copies over one table.
|
|
222
|
+
|
|
223
|
+
### Removed
|
|
224
|
+
- **`pack.json` is gone — the saved login IS the deploy target.** The file had eroded to a single
|
|
225
|
+
field, `platform_url`, which `octwin login --url … --token …` already states; `tenant`/`project`
|
|
226
|
+
became optional overrides once deploy tokens started carrying their own tenant and project pin.
|
|
227
|
+
So it was a second home for a fact `login` knew, and it cost real things: a machine-specific file
|
|
228
|
+
committed into every pack repo, a four-link config-precedence chain to document, and an
|
|
229
|
+
exclusion rule duplicated in the bundler and the server (which had already drifted apart).
|
|
230
|
+
`octwin init` and `octwin pull` no longer write one, and nothing reads one. **A pack directory is
|
|
231
|
+
now pack content and nothing else** — the same repo deploys from any machine, and a pulled pack
|
|
232
|
+
redeploys with no flags.
|
|
233
|
+
|
|
234
|
+
### Changed
|
|
235
|
+
- **`octwin login` now sets the default deploy target**, not just the token: it writes a
|
|
236
|
+
`default_url` key alongside the token in `~/.octwin/credentials.json`. Every command resolves
|
|
237
|
+
**flags → env (`PACK_PLATFORM_URL`/`PACK_TENANT`/`PACK_PROJECT`/`PACK_TOKEN`) → saved login**.
|
|
238
|
+
An existing credentials file simply has no `default_url` until the next `octwin login` — which is
|
|
239
|
+
exactly what the "no platform url" error now tells you to run. CI is unaffected (it passes
|
|
240
|
+
`PACK_PLATFORM_URL` + `PACK_TOKEN` as env and never had a `pack.json`).
|
|
241
|
+
|
|
242
|
+
## [0.1.17] - 2026-07-27
|
|
243
|
+
|
|
244
|
+
### Added
|
|
245
|
+
- **`octwin pull <packId>` — write a DEPLOYED pack's source back to disk.** The inverse of
|
|
246
|
+
`deploy`. A pack pushed with the CLI lived on the platform as an artifact the runtime served
|
|
247
|
+
but nothing could hand back, so its only source copy was the machine that pushed it. Pull it,
|
|
248
|
+
fix it, redeploy it. Writes a `pack.json` (retired in 0.1.18 — the saved login is now the
|
|
249
|
+
target) so the pulled directory redeploys where it came from; `--version` picks a version,
|
|
250
|
+
`--force` overwrites a non-empty dir. You may pull a pack
|
|
251
|
+
your tenant OWNS (an operator token pulls any) — a pack you merely installed is not yours to
|
|
252
|
+
read.
|
|
253
|
+
- **`octwin chat --script <file>` — drive a whole conversation from one invocation.** One turn per
|
|
254
|
+
line, in order, in a single process over a single SSE connection, waiting for each turn to settle
|
|
255
|
+
before sending the next. Blank lines and `#` comments are skipped; `tap:<id>` presses a rendered
|
|
256
|
+
row/button (the rest of the line is kept verbatim, since a tap id is itself colon-delimited),
|
|
257
|
+
`media:<path>` uploads a file, and `media:<path> | caption` attaches one.
|
|
258
|
+
|
|
259
|
+
This exists because **chaining invocations races the agent loop.** A turn ends on a quiet gap
|
|
260
|
+
(`TURN_SETTLE_MS`), which can arrive while the server-side loop is still running — so
|
|
261
|
+
`chat A && chat B` lets B's inbound land mid-turn, and the agent fills required fields with
|
|
262
|
+
placeholder text (`service_id: "service-id"`) or starts a second workflow run. Those look exactly
|
|
263
|
+
like flow bugs and cost a field author three separate investigations. The one-turn-per-invocation
|
|
264
|
+
rule is now stated in `chat --help` and the header usage as well.
|
|
265
|
+
|
|
266
|
+
### Changed
|
|
267
|
+
- **`octwin deploy` reports the suspended runs it invalidated.** A redeploy rebuilds the pack's
|
|
268
|
+
tools, and in-flight flow runs live in those tools — so every suspended conversation is dropped.
|
|
269
|
+
The deploy prints `ⓘ N suspended run(s) invalidated` when N > 0, instead of leaving the author to
|
|
270
|
+
discover it as a `workflow_resume_stale` on the next tap.
|
|
271
|
+
|
|
272
|
+
## [0.1.16] - 2026-07-26
|
|
273
|
+
|
|
274
|
+
### Added
|
|
275
|
+
- **A pack can ship committed images.** `jpg`/`jpeg`/`png`/`webp`/`gif`/`pdf` files in your pack
|
|
276
|
+
directory now travel as a separate `blobs` half of the bundle (base64 on the wire, `bytea` in
|
|
277
|
+
storage) instead of being rejected as "not an allowed pack file type". Declare each one under the
|
|
278
|
+
manifest's `static_assets: [{ file, key }]` and reference it from `config:` with the
|
|
279
|
+
`$pack_asset:<key>` sentinel — the platform uploads it to the media system at install and the
|
|
280
|
+
sentinel resolves to the served URL. `octwin validate` reports the image count alongside the file
|
|
281
|
+
count.
|
|
282
|
+
- **The size ceilings fail locally, before upload** — 2 MB per file, 32 MB of binary per pack,
|
|
283
|
+
mirroring the server. `svg` stays rejected: it is script-capable and these assets are served to
|
|
284
|
+
browsers.
|
|
285
|
+
|
|
286
|
+
### Fixed
|
|
287
|
+
- **Committed images were silently corrupted.** `collectBundleFiles` read *every* file with
|
|
288
|
+
`readFileSync(full, 'utf8')`, so a JPEG went through a lossy UTF-8 decode and arrived mangled
|
|
289
|
+
server-side — a deploy that "succeeded" and rendered a broken image. Binary files now split off
|
|
290
|
+
into `blobs` and keep their bytes.
|
|
291
|
+
- **`octwin status` reported `loaded=(none)` for every pack, always.** It printed a `loaded_version`
|
|
292
|
+
field that stopped existing when reload moved to content-sha keying, which also meant the
|
|
293
|
+
version-drift warning underneath it could never fire. It now prints the **content sha** the
|
|
294
|
+
instance has loaded and the one the catalog holds (with the artifact's `origin`), and warns off the
|
|
295
|
+
platform's own `up_to_date` flag. The sha is the more useful fact anyway: re-publishing the *same*
|
|
296
|
+
version changes it, which is exactly the author's inner loop. A pack that is live and current but
|
|
297
|
+
**withdrawn** from the catalog now says so, rather than reporting a clean bill of health.
|
|
298
|
+
|
|
299
|
+
## [0.1.15] - 2026-07-25
|
|
300
|
+
|
|
301
|
+
### Added
|
|
302
|
+
- **`octwin agents [packId::agentId] [--prompt]` — see what your agent actually runs as.** The platform
|
|
303
|
+
gained *governed* agent settings: an operator platform default can override the `default_model`
|
|
304
|
+
(and history window / working-memory toggle) your manifest declares, and that override is invisible
|
|
305
|
+
from the pack source. The command prints each governed setting's **effective** value, **which layer
|
|
306
|
+
won** (project override → platform default → pack manifest), and warns explicitly when your pack's
|
|
307
|
+
declared value is *not* in force. `--prompt` prints the exact system prompt the LLM sees for this
|
|
308
|
+
project (pack instructions + platform protocol + any project overlay).
|
|
309
|
+
- **`octwin orders [reference_id]` — the commerce a conversation produced.** The list (number,
|
|
310
|
+
status/payment, total, contact) or one order with its line items, the
|
|
311
|
+
subtotal/tax/shipping/discount/total breakdown, `payment_ref`, and the allowed transitions. On a
|
|
312
|
+
`pending`/`none` payment it explains *why that's expected*: the forward payment lifecycle is
|
|
313
|
+
**webhook-owned** (not patchable), and the default gateway-less `manual` driver makes
|
|
314
|
+
`payment_request` take its `empty` port — so the flow should confirm pay-on-delivery, not error.
|
|
315
|
+
- **`octwin analytics [entity]` — stage conversion for any pipelined entity.** The funnel engine that
|
|
316
|
+
powered journeys is now generic, so any entity declared with a `pipeline:` charts conversion +
|
|
317
|
+
drop-off (`--overview` / `--milestones` / `--trends` / `--cost`, and `--stage <id>` for the records
|
|
318
|
+
currently at a stage). An empty result names **both** causes the platform folds into one response —
|
|
319
|
+
no `pipeline:`, or no `view` grant on `record.<entity>`.
|
|
320
|
+
- **`octwin catalog [--readiness]`** — commerce products with price, availability and stock
|
|
321
|
+
(`untracked` when the SKU isn't inventory-tracked), plus the WhatsApp catalog binding.
|
|
322
|
+
`--readiness` runs the Meta Graph checklist with each failing item's suggested fix.
|
|
323
|
+
- **`octwin scheduling [--slots <resourceRecordId>]`** — the engine state (bookable resource types,
|
|
324
|
+
upcoming slots, booked seats) or the computed slots for one resource. This is how you verify the
|
|
325
|
+
availability rules a `deploy --seed` created, which 0.1.14 could create but not read back.
|
|
326
|
+
|
|
327
|
+
### Changed
|
|
328
|
+
- **Auth failures now name the scope the command needs.** Every 401/403 appends the required scope
|
|
329
|
+
(and the plan feature, where the route is entitlement-gated) from one client-side mirror of the
|
|
330
|
+
platform's scope registry — replacing the per-command hardcoded strings, several of which named no
|
|
331
|
+
scope at all. It also spells out that `pack:deploy` / `media:generate` are direct-grant only, so a
|
|
332
|
+
`tenant:admin` preset token does **not** confer them (the most common "but my token is admin" dead end).
|
|
333
|
+
|
|
334
|
+
## [0.1.14] - 2026-07-23
|
|
335
|
+
|
|
336
|
+
### Fixed
|
|
337
|
+
- **A failed demo seed no longer reports a clean `✓`.** `deploy` softens non-fatal
|
|
338
|
+
install steps (e.g. a demo-seed row) to warning frames, but the summary still printed
|
|
339
|
+
`✓ Deployed` and exited 0 — so a deploy that seeded ZERO records read as success (the
|
|
340
|
+
false-✓ trap). `deploy` now collects those step errors, prints `⚠ Deployed with N
|
|
341
|
+
warning(s) — data may be incomplete` with each message, and exits non-zero so CI /
|
|
342
|
+
a `deploy && chat` chain catches it.
|
|
343
|
+
|
|
344
|
+
### Added
|
|
345
|
+
- **`login` echoes what the token reaches.** After saving, `octwin login` calls
|
|
346
|
+
`/api/self/t/whoami` and prints the workspace, project pin, and scopes — so a fresh
|
|
347
|
+
token self-identifies without a second `octwin whoami` (best-effort; a network failure
|
|
348
|
+
never fails the save).
|
|
349
|
+
- **`validate --remote` surfaces warnings + the data-store trap.** The remote validate now
|
|
350
|
+
renders advisory `⚠` warnings (which don't block deploy) and, for a pack declaring
|
|
351
|
+
`required_adapters: [data-store]`, reports the missing-data-store error the deploy would
|
|
352
|
+
have 409'd on — plus a warning that an `xrm.yaml` pack usually shouldn't declare that
|
|
353
|
+
adapter at all. It also now catches (offline) a list-form `entities:` block and a
|
|
354
|
+
`localized: true` field seeded with a bare string — traps that previously surfaced only
|
|
355
|
+
mid-seed at deploy.
|
|
356
|
+
|
|
357
|
+
## [0.1.13] - 2026-07-22
|
|
358
|
+
|
|
359
|
+
### Added
|
|
360
|
+
- **A headless media loop — produce + send.** Two paired additions close the coverage hole where any
|
|
361
|
+
media-collect flow (e.g. `activate-app`'s registration / ID uploads) stalled at the upload prompt
|
|
362
|
+
because the CLI could neither *make* nor *send* an image:
|
|
363
|
+
- `octwin media generate "<prompt>" [--out <file.png>] [--size 1024x1024] [--json]` — AI-generates an
|
|
364
|
+
image on the platform (needs a `media:generate`-scoped token), stores it as a public asset, and prints
|
|
365
|
+
its `MEDIA-` handle + serve URL. `--out` downloads the bytes to a file (WhatsApp renders only
|
|
366
|
+
`.png`/`.jpg`); `--json` emits `{ media_id, url, mime, width, height, bytes }`.
|
|
367
|
+
- `octwin chat --media <file|media-id>` — uploads a local file (or a media id from
|
|
368
|
+
`media generate --json`) as an image/document/audio inbound; any accompanying `"message"` rides as
|
|
369
|
+
its caption. The platform's media pipeline folds the upload into a running collect, so media flows
|
|
370
|
+
are now fully drivable headlessly.
|
|
371
|
+
|
|
372
|
+
### Changed
|
|
373
|
+
- **The KB-drift nudge now names what changed.** With the platform serving per-entry content hashes, the
|
|
374
|
+
post-command nudge appends a `(N changed · M added · K removed)` summary instead of a bare hash pair.
|
|
375
|
+
- **`octwin platform-kb` prints a changelog on pull.** Instead of only a doc/catalog count, the pull now
|
|
376
|
+
diffs the fresh index against your last pull and lists exactly which docs/catalogs were added (`+`),
|
|
377
|
+
changed (`~`), or removed (`-`) — so a replaced schema shape is visible, not silent.
|
|
378
|
+
|
|
379
|
+
## [0.1.12] - 2026-07-22
|
|
380
|
+
|
|
381
|
+
### Fixed
|
|
382
|
+
- **The KB-drift nudge now fires after `chat` too.** `chat` — the command a debugging session runs most —
|
|
383
|
+
was missing from the networked-command list, so a chat-heavy session never noticed the platform's
|
|
384
|
+
capability reference had moved. The nudge also now shows the concrete drift
|
|
385
|
+
(`old-hash → new-hash`) so you can see it's real, not a heuristic.
|
|
386
|
+
|
|
387
|
+
### Changed
|
|
388
|
+
- **Every networked command announces what it's doing before it does it.** `chat` prints
|
|
389
|
+
`→ Connecting to <tenant>/<project> as '<handle>' …` before opening the stream and
|
|
390
|
+
`… delivered — waiting for the reply (up to Ns)` after the send; `status` / `whoami` /
|
|
391
|
+
`platform-kb` / `records` / `logs` / `cases` each print a one-line `→ …` header naming the
|
|
392
|
+
action and target before the first network call — no more silent seconds followed by a result
|
|
393
|
+
(or a hang with no clue what was being attempted). JSON modes (`--json`) stay clean for piping.
|
|
394
|
+
|
|
395
|
+
## [0.1.11] - 2026-07-21
|
|
396
|
+
|
|
397
|
+
### Fixed
|
|
398
|
+
- **Auth-failure hint on every command, not just the inspect family** (author-feedback A7). `validate`,
|
|
399
|
+
`deploy`, `status`, and the single-record read now print the scope/transient explanation on a 401/403
|
|
400
|
+
(the shared `authFailureHint`), instead of a bare `HTTP 401` that left it ambiguous whether the token
|
|
401
|
+
lacked a scope or the platform hiccupped.
|
|
402
|
+
|
|
403
|
+
## [0.1.10] - 2026-07-21
|
|
404
|
+
|
|
405
|
+
The capability reference is now **pulled, never bundled** — the authoring skill dropped its committed KB
|
|
406
|
+
snapshot, so `octwin platform-kb pull` is the single source of truth, and the CLI watches it for drift.
|
|
407
|
+
|
|
408
|
+
### Added
|
|
409
|
+
- **KB-drift nudge** — after any command that already talks to the platform (`deploy` / `status` /
|
|
410
|
+
`test` / `validate --remote` / `records` / `cases` / `logs` / `whoami`), the CLI does one tiny
|
|
411
|
+
`octwin-platform-kb?meta=1` GET and prints a one-line "the platform capability reference changed —
|
|
412
|
+
`octwin platform-kb pull`" when the platform's `content_hash` no longer matches your last pull.
|
|
413
|
+
Fail-silent + TTY-only + no-op until you've pulled once — the sibling of the existing CLI-upgrade notice.
|
|
414
|
+
|
|
415
|
+
### Changed
|
|
416
|
+
- **`octwin platform-kb pull`** now records the reference's `content_hash` in `.octwin/platform-kb/index.json`
|
|
417
|
+
(the fingerprint the drift check compares against).
|
|
418
|
+
|
|
419
|
+
The conversation-debugging release — driven by the second author-feedback round (xpeng-egypt),
|
|
420
|
+
which found the author→validate→deploy loop solid but multi-turn debugging impossible from the CLI.
|
|
421
|
+
|
|
422
|
+
### Added
|
|
423
|
+
- **`octwin chat --tap "<tap-id>"`** — press a rendered button / list row (sends the web channel's
|
|
424
|
+
`type:'interactive'` inbound). Paired with full render printing, any tap-driven flow is now drivable headlessly.
|
|
425
|
+
- **Full render printing** — every reply prints its complete interactive content: list sections/rows,
|
|
426
|
+
buttons, and carousel cards, each with its **tap id**; header/footer/list-button text included.
|
|
427
|
+
- **`--json` on `chat` / `logs` / `cases`** — raw SSE envelopes (chat) or raw event/case payloads.
|
|
428
|
+
- **`octwin cases [caseId] [--queues]`** — inspect casework (support tickets): the inbox, one case +
|
|
429
|
+
timeline + applicable decisions, or queue keys with open counts. Casework packs previously had zero
|
|
430
|
+
data-inspection path (`records case` was a dead end).
|
|
431
|
+
- **Per-subcommand `--help`/`-h`** — intercepted before any network or auth work (previously
|
|
432
|
+
`octwin logs --help` made a network call and could die with a 401).
|
|
433
|
+
- Multi-message turns: `chat` now collects **all** of a turn's renders (quiet-gap detection) instead
|
|
434
|
+
of cutting at the first one.
|
|
435
|
+
|
|
436
|
+
### Changed
|
|
437
|
+
- **`octwin logs`**: the conversation list shows contact handle + last activity and filters with
|
|
438
|
+
`--as <handle>`; the timeline prints **full** event text (no more 240-char truncation) plus the
|
|
439
|
+
renders each turn actually sent; footer points at `--json` / `mastra-messages` for verbatim payloads.
|
|
440
|
+
- **Network failures name the target URL** (`platform unreachable at <url>`), and 401s explain
|
|
441
|
+
invalid-token vs one-off transient instead of a bare status code.
|
|
442
|
+
- `octwin records` on an unknown entity now surfaces the server's error body, and for `case`/`cases`
|
|
443
|
+
points at `octwin cases`.
|
|
444
|
+
|
|
445
|
+
### Fixed
|
|
446
|
+
- **Multi-turn chat actually works.** Two bugs made consecutive `octwin chat` calls look like "every
|
|
447
|
+
call starts fresh": (1) the idempotency `local_id` was a **constant** per handle, so the platform's
|
|
448
|
+
5-minute inbound dedup silently dropped the second message — a fresh id is now minted per call;
|
|
449
|
+
(2) a fresh SSE connect **replays** recent history and the CLI printed the first render frame — i.e.
|
|
450
|
+
the *previous* turn's greeting — as the reply; the CLI now drains the replay and only accepts frames
|
|
451
|
+
newer than the connect boundary. The platform always kept one open conversation per `--as` handle —
|
|
452
|
+
the same handle genuinely continues the same conversation now.
|
|
453
|
+
|
|
454
|
+
## [0.1.8] - 2026-07-21
|
|
455
|
+
- Global-first install guidance (`npm i -g octwin-cli`); upgrade notice suppressed under `npx`.
|
|
456
|
+
|
|
457
|
+
## [0.1.7] - 2026-07-21
|
|
458
|
+
- `--version`/`-v` flag; daily fail-silent, TTY-only "newer version available" notice.
|
|
459
|
+
|
|
460
|
+
## [0.1.6] - 2026-07-21
|
|
461
|
+
- `octwin init` scaffolds a home-hub menu (`home` list_picker + example `browse` tool) instead of a bare greeting.
|
|
462
|
+
|
|
463
|
+
## [0.1.5] - 2026-07-20
|
|
464
|
+
- `octwin validate --remote` (the platform's FULL manifest + flow-DSL check, all errors at once);
|
|
465
|
+
headless debugging commands `octwin records` / `octwin logs` / `octwin chat`; `octwin test` became
|
|
466
|
+
an alias for `validate --remote`. First author-feedback round.
|
|
467
|
+
|
|
468
|
+
## [0.1.4] - 2026-07-20
|
|
469
|
+
- Authoring reliability + DX fixes from the first end-to-end external-pack test.
|
|
470
|
+
|
|
471
|
+
## [0.1.3] - 2026-07-20
|
|
472
|
+
- `octwin platform-kb pull` — pull the platform capability reference for the authoring skill.
|
|
473
|
+
|
|
474
|
+
## [0.1.2] - 2026-07-20
|
|
475
|
+
- Proper npm README.
|
|
476
|
+
|
|
477
|
+
## [0.1.1] - 2026-07-20
|
|
478
|
+
- Initial npm publish: `init` / `validate` / `login` / `whoami` / `deploy` / `status`, `oct_` deploy tokens.
|