octwin-cli 0.8.6 → 0.8.8
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 +74 -0
- package/README.md +30 -9
- package/dist/index.js +531 -360
- package/dist/lib/declaration-check.js +50 -1
- package/dist/lib/device-login.js +127 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,80 @@ Format: [Keep a Changelog](https://keepachangelog.com/) — newest first, bucket
|
|
|
5
5
|
**Added · Changed · Deprecated · Removed · Fixed · Security**. The platform-wide view lives in the
|
|
6
6
|
repo root [`CHANGELOG.md`](../../CHANGELOG.md); this file is the CLI-only cut that ships with the package.
|
|
7
7
|
|
|
8
|
+
## [0.8.8] - 2026-09-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **`octwin login` can sign you in through a browser — no token is pasted anywhere.** Run it with
|
|
12
|
+
no `--token` and it prints an approval link:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Open this link to approve: https://app.octwin.ai/cli-login?code=WDJB-MJHT
|
|
16
|
+
Code shown on that page: WDJB-MJHT
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A workspace admin opens it, checks which machine is asking, and clicks Approve; the CLI collects a
|
|
20
|
+
30-day token by itself and saves it exactly where `--token` would have. `octwin login --token oct_…`
|
|
21
|
+
is unchanged, and CI should still skip login entirely and set `PACK_PLATFORM_URL` + `PACK_TOKEN`.
|
|
22
|
+
|
|
23
|
+
**The wait is bounded and resumable, because this CLI is mostly driven by an agent.** An agent's
|
|
24
|
+
shell call is killed after about two minutes, and a wait that outlives its caller is worse than a
|
|
25
|
+
short one — the process dies mid-handshake and the code the human is still typing belongs to
|
|
26
|
+
nothing. So `login` waits 90 seconds, then exits **75** (`EX_TEMPFAIL`) with the handshake kept in
|
|
27
|
+
`~/.octwin/pending-login.json`; running it again resumes the **same** code, which the platform
|
|
28
|
+
keeps alive for ten minutes. `0` still means signed in and `1` still means failed, so an agent
|
|
29
|
+
branches on the exit code instead of parsing prose. `--wait <seconds>` overrides the 90 (`--wait 0`
|
|
30
|
+
prints the code and returns at once), and `--json` prints the same facts as an object.
|
|
31
|
+
|
|
32
|
+
**Approve only a link you opened because you ran the command.** The link carries the code
|
|
33
|
+
(RFC 8628 `verification_uri_complete`), so the click *is* the consent: approving a link somebody
|
|
34
|
+
sent you would connect their machine to your workspace. The console says so on the page. The code
|
|
35
|
+
is printed here too, and shown there, so you can always tell one request from another.
|
|
36
|
+
|
|
37
|
+
The pending handshake gets its own file rather than a new key in `credentials.json`: every value
|
|
38
|
+
in that file is a token, and its one reserved key (`default_url`) is collision-free only because a
|
|
39
|
+
platform url always contains `://`. A second value shape there would end that argument.
|
|
40
|
+
|
|
41
|
+
- **A 401 now points at `octwin login`, not at the console.** With a 30-day browser token, renewing is
|
|
42
|
+
one command; the old hint sent people to mint a replacement by hand. Mint a token yourself if you
|
|
43
|
+
want a different lifetime — the console offers 30 / 90 days / a year / never.
|
|
44
|
+
|
|
45
|
+
## [0.8.7] - 2026-09-06
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
- **`validate` now reads a map's KEYS, not only its values.** The declaration walker reported four
|
|
49
|
+
things — an unknown key, a missing `required` key, a wrong scalar type, a value outside a closed
|
|
50
|
+
`enum` — and every one of them is about a VALUE. But a map's keys carry meaning in several
|
|
51
|
+
declarations (`roles.yaml` grant resources and verbs, `xrm.yaml` entity names), and those keys had
|
|
52
|
+
no check at all. Rule 5 replays `propertyNames` — both the `enum` and the `pattern` form.
|
|
53
|
+
|
|
54
|
+
This is why publishing the RBAC vocabulary was worth doing. A pack author wrote `case:` where the
|
|
55
|
+
grant key is `record.case`, passed offline `validate` clean, and learned the truth one bad guess at
|
|
56
|
+
a time from `--remote` round-trips. They gave up on custom RBAC over it. The check now says
|
|
57
|
+
`not a valid key here — must be one of "record.case", …` before a deploy is attempted.
|
|
58
|
+
|
|
59
|
+
It stays inside the file's false-positive rule because it is a **faithful replay**: the same `enum`
|
|
60
|
+
membership and the same JS regex engine Zod itself runs, over a pattern the platform generated. A
|
|
61
|
+
pattern this engine cannot compile is treated like a combinator — walk away, do not guess.
|
|
62
|
+
|
|
63
|
+
Verified 2026-09-06 against the shipped binary: across all 23 marketplace packs rule 5 reports
|
|
64
|
+
**nothing**, with 10 of the 12 pulled declaration schemas carrying `propertyNames` (so the silence
|
|
65
|
+
is a result, not an unexercised branch). A deliberately broken key is caught on both branches — a
|
|
66
|
+
role named `Regional-Agent` against `^[a-z][a-z0-9_]*$`, and a grant key `case:` against an enum
|
|
67
|
+
vocabulary — each naming the file, the path and the rule it broke.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
- **`deploy` had stopped printing its seed counts entirely.** `printDeploySuccess` read
|
|
71
|
+
`r.summary.records` / `.updated` / `.images` / `.rules` / `.failed`, but the deploy route's
|
|
72
|
+
`summary` is a run-log **string** (`"clinic v1.2.0 — {…}"`), not an object. So every field was
|
|
73
|
+
`undefined`, `parts` stayed empty, and the `if (parts.length)` guard turned *"I am reading the
|
|
74
|
+
wrong field"* into *"there was nothing to say"* — the block printed nothing and looked like a
|
|
75
|
+
deploy that simply seeded no rows. The counts have been on `r.seeded`, keyed by seed kind, and
|
|
76
|
+
`printSeedCounts` already renders them (filtering zeros, which is what makes a partial seed
|
|
77
|
+
legible). The dead block is gone and `printSeedCounts` is called instead.
|
|
78
|
+
|
|
79
|
+
Same shape as the bug 0.8.6 fixed in `validate`: *no data* and *no problem* rendering identically.
|
|
80
|
+
Two independent places in this CLI reached it within one week.
|
|
81
|
+
|
|
8
82
|
## [0.8.6] - 2026-09-04
|
|
9
83
|
|
|
10
84
|
### Changed
|
package/README.md
CHANGED
|
@@ -46,9 +46,10 @@ git init && git add -A && git commit -m "init pack"
|
|
|
46
46
|
# 2. Author it — edit manifest.yaml, flows/tools/main.flow.yaml (+ its locale),
|
|
47
47
|
# and prompts/identity.md. Everything is pure YAML.
|
|
48
48
|
|
|
49
|
-
# 3. Point it at your platform — one command, no config file
|
|
50
|
-
#
|
|
51
|
-
|
|
49
|
+
# 3. Point it at your platform — one command, no config file.
|
|
50
|
+
# It prints a link; approve it in the console and it saves a 30-day token
|
|
51
|
+
# by itself. (Already have a token? add --token oct_…)
|
|
52
|
+
octwin login --url https://your-octwin.example.com
|
|
52
53
|
|
|
53
54
|
# 4. Validate → deploy → confirm it's live
|
|
54
55
|
octwin validate
|
|
@@ -65,7 +66,7 @@ octwin status # "✓ live and current" once it's warm
|
|
|
65
66
|
| --- | --- |
|
|
66
67
|
| `octwin init <dir>` | Scaffold a new pure-YAML pack into `<dir>` (writes a starter `manifest.yaml`, flow, prompt, `.gitignore` and `README.md` — pack content only). Options: `--id`, `--description`, `--display-name`. |
|
|
67
68
|
| `octwin validate` | Check the pack locally (structure + pure-YAML rules). `--remote` runs **the same validation the platform runs to load your pack** — every declaration file (`xrm.yaml`, `scheduling.yaml`, `worklist.yaml`, `roles.yaml`, `automation.yaml`, `integrations.yaml`, `surveys.yaml`, `taps.yaml`, `commands.yaml`, `journeys/`, `messages.<lang>.yaml`), the cross-file checks between them, and the **flow lint** — conventions the schema can't express, like an `assign:` whose value is a quoted literal or a `$t()` key with no namespace. These need template expansion and the platform's own schemas, which only the server has, so a local ✓ does not cover them. A `--remote` ✓ and a successful deploy are now the same answer by construction, not by convention. |
|
|
68
|
-
| `octwin login` |
|
|
69
|
+
| `octwin login` | Sign in to a platform URL **and make it the default target** (both stored in `~/.octwin/credentials.json`). With no `--token` it prints a link to approve in the console, then collects a 30-day token itself — nothing is pasted. The wait is bounded and resumable: run it again to keep waiting on the same request. `--url`, `--token`, `--wait <seconds>`, `--json`. |
|
|
69
70
|
| `octwin whoami` | Verify the saved/passed token is valid for a tenant. `--url`, `--tenant`. |
|
|
70
71
|
| `octwin deploy` | Upload + install the pack onto your tenant's project. `--seed` also runs the pack's demo seed. Says **nothing** about the public marketplace unless asked: `--request-listing` submits the pack for operator review (needs `listing.public: true` in the manifest too), `--withdraw-listing` retracts. Reports the **listing verdict** whenever one exists. |
|
|
71
72
|
| `octwin status` | Report what the platform has live for this pack — installed version, the **content sha** the instance loaded vs. the one the catalog holds (a redeploy of the *same* version changes it), its flows, and whether it is live on the public marketplace. |
|
|
@@ -164,10 +165,12 @@ Three things worth knowing when you read the output:
|
|
|
164
165
|
holds pack content and nothing else, so the same repo deploys from any machine:
|
|
165
166
|
|
|
166
167
|
```bash
|
|
167
|
-
octwin login --url https://your-octwin.example.com
|
|
168
|
+
octwin login --url https://your-octwin.example.com # approve in a browser
|
|
169
|
+
octwin login --url https://your-octwin.example.com --token oct_… # or bring your own token
|
|
168
170
|
```
|
|
169
171
|
|
|
170
|
-
|
|
172
|
+
Either way it stores the token *and* makes the URL your default target, in
|
|
173
|
+
`~/.octwin/credentials.json`:
|
|
171
174
|
|
|
172
175
|
```jsonc
|
|
173
176
|
{
|
|
@@ -176,6 +179,9 @@ That stores the token *and* makes the URL your default target, in `~/.octwin/cre
|
|
|
176
179
|
}
|
|
177
180
|
```
|
|
178
181
|
|
|
182
|
+
A browser sign-in in progress is held separately, in `~/.octwin/pending-login.json`, and is deleted
|
|
183
|
+
as soon as the token arrives — the credentials file holds tokens only.
|
|
184
|
+
|
|
179
185
|
Tenant and project need no setting at all — the **token carries its own tenant**, plus an optional
|
|
180
186
|
project pin. `--tenant` / `--project` exist only as overrides (a multi-workspace human, or an
|
|
181
187
|
unpinned token that must name a project).
|
|
@@ -195,9 +201,24 @@ variables (add `PACK_PROJECT` only if the token isn't pinned).
|
|
|
195
201
|
## Authentication
|
|
196
202
|
|
|
197
203
|
You authenticate with a tenant-scoped **deploy token** (prefixed `oct_…`) — not a password and not
|
|
198
|
-
an operator token.
|
|
199
|
-
|
|
200
|
-
|
|
204
|
+
an operator token. It is **least-privilege** (scope `pack:deploy`): it can deploy packs to your
|
|
205
|
+
tenant but cannot manage members, billing, or other tenants, and it is revocable at any time. A
|
|
206
|
+
token can never mint another token, which is why approving a browser sign-in needs a *human*
|
|
207
|
+
workspace admin.
|
|
208
|
+
|
|
209
|
+
Two ways to get one:
|
|
210
|
+
|
|
211
|
+
- **`octwin login` with no `--token`** prints an approval link. A workspace admin opens it, sees
|
|
212
|
+
which machine is asking, and clicks Approve; the CLI collects a 30-day token by polling. Nothing is
|
|
213
|
+
copied by hand, so no live credential passes through a chat window or an email. The wait is
|
|
214
|
+
bounded (90s) and **resumable** — run the command again to keep waiting on the same request, which
|
|
215
|
+
lives ten minutes. Exit code `75` means *still waiting*; `0` means signed in.
|
|
216
|
+
**Approve only a link you opened because you ran the command.** The link carries the code, so
|
|
217
|
+
clicking Approve on a link someone sent you would connect *their* machine to your workspace. The
|
|
218
|
+
code is printed too, and the page shows it, so you can always tell one request from another.
|
|
219
|
+
- **Generate one yourself** in the Octwin console (**your workspace → API tokens → Generate**) and
|
|
220
|
+
pass it with `--token`. This is the way to pick narrower scopes or a different expiry, and the
|
|
221
|
+
way to get a token for CI.
|
|
201
222
|
|
|
202
223
|
Add the optional **`media:generate`** scope to let a `--seed` deploy AI-generate seed images
|
|
203
224
|
(for a demo record field like `photo: "generate:<prompt>"`); without it, such fields are seeded as
|