@decidio/sdk 0.4.1 → 0.4.4
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 +195 -0
- package/README.md +19 -6
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +621 -151
- package/dist/resume.d.ts +4 -3
- package/dist/resume.js +56 -23
- package/openapi.yaml +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,201 @@ Release notes for the client SDK. The Python package `decidio` ships the same ch
|
|
|
4
4
|
version (the two are conformance-locked twins). Each entry names what changed, what a reader must do
|
|
5
5
|
on upgrade, and what proved it.
|
|
6
6
|
|
|
7
|
+
## 0.4.4 — the small findings of the 0.4.3 prodsec run: frames at 80 columns, a local install upgrades in place, a key-less .env survives the tour
|
|
8
|
+
|
|
9
|
+
An external prodsec reviewer ran 0.4.3 from a stranger's install (2026-09-04; grade B, "trust in the core mechanics
|
|
10
|
+
went up"). Its design questions (a mandatory `--issuer`, out-of-band issuer distribution, npm provenance) are founder
|
|
11
|
+
calls in the backlog; these are its small findings, each with a vector that was red first.
|
|
12
|
+
|
|
13
|
+
- **Frames never wider than the terminal.** The 0.4.2 frames had a width FLOOR of 84 columns, and the terminal driver
|
|
14
|
+
that gates the tours ran only at 300 columns - so on the most common width, 80, every border wrapped. The floor now
|
|
15
|
+
yields to the terminal, and both suites run the approval tour at 80 columns: no frame line wider than the window,
|
|
16
|
+
every frame line closes with its border.
|
|
17
|
+
- **The update gate knows a project-local install.** `npm install @decidio/sdk` then `npx @decidio/sdk …` (this
|
|
18
|
+
README's own path) runs the CLI from the project's node_modules; the gate offered `npm i -g` (global state nobody
|
|
19
|
+
asked for) and then re-ran the same old local file. Three shapes now, each upgraded the way it was installed:
|
|
20
|
+
project-local → `npm i @decidio/sdk@latest` in that project; global → `-g`; under npx → the `@latest` form,
|
|
21
|
+
printed. The re-run the gate spawns carries `DECIDIO_UPDATE_REEXEC=1` and never checks again, so a version that did
|
|
22
|
+
not move cannot loop the prompt. (Python: `pip install -U` already upgrades the interpreter that is running; it
|
|
23
|
+
gains the re-run marker.)
|
|
24
|
+
- **A key-less agent `.env` is left alone.** The tour used its own `.env.quickstart` only when `.env` held a signing
|
|
25
|
+
key; the SDK's `agentKey` is optional, so an agent configured without one had its agent fields overwritten and then
|
|
26
|
+
stripped by cleanup. ANY agent identity in `.env` (`DECIDIO_AGENT_ID`, `_DID`, `_KEY` or `DECIDIO_API_TOKEN`) now
|
|
27
|
+
sends the tour to its own file. Vector: such a file is byte-identical after the tour and its cleanup.
|
|
28
|
+
- **The ④ frame names what was sealed** without `--explain`: `decision payInvoice $86,000 · APPROVED · routed to
|
|
29
|
+
a human by policy (no auto-approve rule for this agent)` (REJECTED on the denial's record).
|
|
30
|
+
- A `.gitignore` the tour CREATES also ignores `node_modules/`; an existing file gets only the secret entries.
|
|
31
|
+
- `openapi.yaml` now states the release version (it read 0.1.11 beside a 0.4.3 package), and a seven-manifest
|
|
32
|
+
lockstep check (`packages/conformance/manifests.mjs`: both package.json, both package-lock.json, pyproject,
|
|
33
|
+
`__init__`, openapi) runs in CI and on the publish path - 0.4.3 shipped with both locks a version behind.
|
|
34
|
+
- Documented: the token recovery journal's vocabulary (`minted`, `retired how=persisted`, `retired how=revoked`,
|
|
35
|
+
`probe`) - `persisted` means the credential file holds the token and is its handle, not that it was revoked.
|
|
36
|
+
- **`logout` says what it does** (prodsec second pass on 0.4.3 - "A / A-", two Lows): it forgets the stored sign-in on
|
|
37
|
+
this computer; the session token itself stays valid until it expires - there is no server-side revocation yet, and no
|
|
38
|
+
wording implies one any more (`logout`, cleanup, the sign-in prompt, the usage page, both READMEs). Vectors: the
|
|
39
|
+
statement is asserted, and `logout` makes no API call.
|
|
40
|
+
- The shipped `.d.ts` state-machine comment names the terminal `discarded` state beside the chain that omitted it.
|
|
41
|
+
- Confirmed as intended, no change: the online `receipt` command prints the tokenized identities your access
|
|
42
|
+
covers ("Resolved for you"); the file keeps its tokens.
|
|
43
|
+
|
|
44
|
+
## 0.4.3 — the stale-install gate, frames you can read at a glance, and a phone door that lands
|
|
45
|
+
|
|
46
|
+
The founder's UAT round 2, second half (2026-09-04). Nobody is on 0.4.2 yet, so it was never tagged; this carries both.
|
|
47
|
+
|
|
48
|
+
- **A stale install must not keep running its defects** (founder ruling). Network commands (`quickstart`, `init`,
|
|
49
|
+
`login`, `doctor`, `approvals`, `receipt`, `tokens`) ask the public registry once a day whether a newer version
|
|
50
|
+
exists — npm for the TypeScript CLI, PyPI for Python, never our servers, cached in the user's profile, silent when
|
|
51
|
+
offline. If one does, an interactive run asks `<latest> is available (you have <yours>). Upgrade now? [y/n]`: `y`
|
|
52
|
+
runs the package manager's own upgrade (`npm i -g @decidio/sdk@latest` / `pip install -U decidio`, with its
|
|
53
|
+
integrity checks) and re-runs your command on the new version; `n` exits 1 naming the command to run when ready.
|
|
54
|
+
Under `npx` there is nothing installed to upgrade, so it prints the `@latest` form and exits. A headless run prints
|
|
55
|
+
the notice and continues; `DECIDIO_REQUIRE_LATEST=1` makes it exit instead (pinned CI is a choice, not a
|
|
56
|
+
surprise). `DECIDIO_NO_UPDATE_CHECK=1` skips the check. `verify`, `--help` and `--version` stay fully offline.
|
|
57
|
+
Seven vectors in both suites: not newer prints nothing; headless + newer notices and runs; the strict flag exits 1;
|
|
58
|
+
interactive `n` exits 1 running nothing; interactive `y` runs the upgrade (a labelled dry-run in the suite); an
|
|
59
|
+
unreachable registry is silent; `verify` never checks.
|
|
60
|
+
- **Frames carry a tone.** ③ (the choice) in orange, ④ and the summary in green for an approval and red for a
|
|
61
|
+
rejection — the frame and its title only, 256-colour orange with a yellow fallback, nothing under `NO_COLOR`
|
|
62
|
+
or a pipe. Founder: "it's hard for the eye to pick out which box means what."
|
|
63
|
+
- **The phone door lands.** iOS opens the installed app for `app.decidioai.com/q/*` (the association file now
|
|
64
|
+
claims it); the app consumes the code, signs in, and opens the decision. A phone without the app lands in its
|
|
65
|
+
browser on the phone build (`/phone/q/<code>`), the same single-decision screen the app shows. The desktop landing
|
|
66
|
+
is a hand-off page: the one decision on stage with its actions in view, the queue a click away (`?stage=1`).
|
|
67
|
+
- **`quickstart --phone web`** is the founder's test fork: the QR points at the phone web rendering, a path the app
|
|
68
|
+
never claims, so it opens in the browser even with the app installed. The default QR opens the app when it is
|
|
69
|
+
there. (`--demo` will pick the sandbox the same way, once it exists.)
|
|
70
|
+
- Founder decisions recorded (backlog): the sandbox for uninvited developers is TOUR-ONLY in v1 (no own agents —
|
|
71
|
+
surface risk and a second policy to maintain), its boxes are torn down at cleanup or by a one-hour sweeper, and
|
|
72
|
+
its phone surface is the web page, never the native app.
|
|
73
|
+
|
|
74
|
+
## 0.4.2 — the zero-miss release: three gates, and every finding of the two 0.4.1 reviews
|
|
75
|
+
|
|
76
|
+
Built to the plan in `docs/Decidio_SDK_Zero_Miss_Plan.md` after an outside engineer's first-run review of 0.4.1 (B+)
|
|
77
|
+
and the prodsec retest of 0.4.1 (A-, safety A). Every item of both reports has a disposition below; the gates that
|
|
78
|
+
would have caught them now run on every pull request, on Linux AND Windows.
|
|
79
|
+
|
|
80
|
+
**The founder's UAT, round 2 (2026-09-04), folded in before the tag.** Steps 0–6 green on the previous build; the
|
|
81
|
+
web-app door landed on home (fixed twice the same night: the desktop's deep link is `/queue?focus=<id>`, and a browser
|
|
82
|
+
with no session bounced to `/login` before the link was consumed), and approving from the queue proved the terminal
|
|
83
|
+
follows an outside decision. Then six findings on the tour itself, all taken but one (which became a founder call):
|
|
84
|
+
- **One line per event.** The tour's paragraphs are gone from the default output: every step is a frame with two to
|
|
85
|
+
four terse rows, the outcome is one line, the reminder is two, and "Where next" is one. `--explain` prints the
|
|
86
|
+
narration that used to be the default. Target met: the approval tour is about 40 lines without the QR.
|
|
87
|
+
- **Every step framed** (①②③④ and the summary), with an escape-aware word wrap so a styled row never overflows a
|
|
88
|
+
narrow window; continuation lines hang under the label column.
|
|
89
|
+
- **A short hand-off link.** `POST /demo/session-link` now returns `https://app.decidioai.com/q/<13-char code>`
|
|
90
|
+
instead of a 110-character URL: it fits the frame, stays clickable AND copyable, and the credential row remembers
|
|
91
|
+
which decision the code was minted for (`handoff_decision_id`, cleared in the same single-use burn), so the web
|
|
92
|
+
app's new `/q/<code>` route signs in and lands on that decision. A hand-off never flips `emailVerified`.
|
|
93
|
+
- **A small QR, still printed by default** (the founder's call: "it looks impressive in a demo"). The QR carries the
|
|
94
|
+
short link upper-cased, so the encoder uses its alphanumeric mode, at the lowest error-correction level with a
|
|
95
|
+
one-module quiet zone: 25 modules instead of 41, about 14 rows instead of 23.
|
|
96
|
+
- **The lab is invite-only, and the CLI says so.** An uninvited address running `login` used to be told "we emailed
|
|
97
|
+
you a link" and wait for nothing (the server's anti-enumeration answer). The CLI now says a link arrives only for
|
|
98
|
+
an invited address and where to request access.
|
|
99
|
+
- **"The wrapper IS the SDK."** The first sentence of the help (and the READMEs) settles the SDK-versus-wrapper
|
|
100
|
+
question: `guard.protect(fn, describe)` ships in this package; the CLI is the tour, the sign-in and the token admin.
|
|
101
|
+
- Founder calls recorded, not built: a sandbox tenant with device-bound identities and a single-decision preview page
|
|
102
|
+
for developers who are not invited (the SDK's distribution unlock); scan → native app → the decision (iOS claims
|
|
103
|
+
`/d/*` for the installed app; the QR opens Safari today); `record kind: agent-receipt` wording.
|
|
104
|
+
|
|
105
|
+
**Round 2, second look (2026-09-04, same night).** The founder re-ran on the deployed build: web-app approval and the
|
|
106
|
+
deep link now work; the QR was MISSING; the text above and below the frames was still dense. Fixes: (1) `qrcode@1.5`'s
|
|
107
|
+
half-block renderer throws "Invalid array length" on an ODD margin, so the 1-module quiet zone silently produced no QR
|
|
108
|
+
(the server logged it, the CLI showed nothing) - margin 2 with level L renders 25 modules in 13 rows, and the CLI now
|
|
109
|
+
says when no QR came back instead of dropping the row; (2) the sign-in block is six lines instead of eleven (one intro,
|
|
110
|
+
prompts that carry their own hints, one signed-in line); (3) the tour no longer prints the stderr "decidio →" host line
|
|
111
|
+
when its own Target line already said it; (4) ④ is three short rows (VALID + file / record kind + evidence / issuer)
|
|
112
|
+
instead of one wrapping line; (5) the reminder is two lines, the take-away has a breath around it.
|
|
113
|
+
|
|
114
|
+
**The founder's UAT pass (2026-09-03), folded in before the tag.** The script's steps 1–5 went green in a real
|
|
115
|
+
PowerShell; step 6 stopped on the sign-in: every command that needed the workspace asked for a fresh emailed link,
|
|
116
|
+
and "nobody would log in this many times". Ruled: sign in ONCE, no better and no worse than the web app; and a
|
|
117
|
+
human step you can believe from outside the terminal.
|
|
118
|
+
- **Sign in once.** `login [email]`: the CLI requests the emailed link itself, you paste it; the 30-day session is
|
|
119
|
+
kept in your user profile (`%LOCALAPPDATA%\decidio` / `~/.config/decidio`, owner-only, written through the same
|
|
120
|
+
atomic secret writer as `.env`), bound to the terminal window that created it (a second window gets its own) and to
|
|
121
|
+
the token's expiry. `logout` ends it; so does `quickstart cleanup` (the tour's sign-in ends with the tour, the way
|
|
122
|
+
the web app drops its session on sign-out). An exported `DECIDIO_SESSION_TOKEN` still outranks it for headless
|
|
123
|
+
shells. No server-side session revocation exists yet (the web app has none either); the stored token is discarded.
|
|
124
|
+
- **Three doors at the human step.** The ③ frame offers: answer **here** (y/n), open the decision in the **web app**
|
|
125
|
+
(a link that lands on it signed in), or scan a **QR** with **your phone** (the same link). The link comes from a
|
|
126
|
+
new authenticated endpoint, `POST /demo/session-link {decisionId}`: it mints the SAME single-use sign-in token the
|
|
127
|
+
emailed link carries, for the caller's own credential, five minutes instead of fifteen because it is shown on a
|
|
128
|
+
screen and lands in terminal scrollback, and it replaces any pending emailed link (one slot per credential). It
|
|
129
|
+
grants a sign-in and only a sign-in: the token is prefixed `h.` and the password-reset door refuses it, so a
|
|
130
|
+
session can move itself to a browser but never mint itself a durable password (invariant-guard block, fixed, with a
|
|
131
|
+
negative-smoke lock). The terminal races the prompt against the gated call: a decision
|
|
132
|
+
made in the app or on the phone closes the prompt, the CLI posts no approve/reject of its own, says "approved (or
|
|
133
|
+
rejected) in the web app (or on your phone) — this terminal saw it and followed", and the function runs
|
|
134
|
+
once, after the approval, exactly as before. If the hand-off endpoint fails, the terminal door still works alone.
|
|
135
|
+
- **Layout.** Each step is a headed section; the doors and the closing summary are frames sized to their content and
|
|
136
|
+
the terminal; the summary is a table (gated / decided / executed / receipt); the help page is in sections. Colour,
|
|
137
|
+
bold and clickable (OSC-8) links only on a TTY and never under `NO_COLOR`; every phrase the gates assert is unchanged.
|
|
138
|
+
- **Two platform defects found by the doors vector, both fixed:** on Windows a console read abandoned when the app
|
|
139
|
+
decided first kept libuv's reader thread blocked and Node's teardown waited forever (the process printed its last
|
|
140
|
+
line and never exited) — the CLI now drains stdout/stderr and exits explicitly on that path only; and Python's
|
|
141
|
+
`isatty()` is true for `NUL`, so the console-key reader spun on a headless run — a real console is now detected
|
|
142
|
+
through `GetConsoleMode` (and that same check now decides whether the CLI may prompt at all, so `NUL` is headless on every OS), pipes and `NUL` read on a thread, POSIX uses `select`.
|
|
143
|
+
- Vectors, both languages: `login` under a real terminal (email → link → stored session); the next command in the
|
|
144
|
+
same terminal asks nothing; another terminal is refused; `logout` removes the file; a headless tour on the stored
|
|
145
|
+
session; cleanup signs out; and the browser-decided tour (the fake gate approves after two polls; the CLI must post
|
|
146
|
+
no approval, run the function once, exit 0).
|
|
147
|
+
|
|
148
|
+
**Gates (structural):**
|
|
149
|
+
- **The stranger run.** Both CLI suites drive the interactive tour under a REAL terminal on every platform through one
|
|
150
|
+
shared driver (`packages/conformance/tty_driver.py`: POSIX pty, Windows ConPTY). `y`, `n` and three blank answers
|
|
151
|
+
are exercised end to end; a missing terminal driver is red on CI, never a skip. A `windows-latest` gates job runs
|
|
152
|
+
both CLI suites under ConPTY.
|
|
153
|
+
- **Claims as vectors.** `packages/conformance/vectors/claims.json`: retired phrases are tombstones that fail the build
|
|
154
|
+
if they reappear in shipped text; the help must carry what is true (`y approves`, `n rejects`, exit 3 at the human
|
|
155
|
+
moment, `--help` anywhere, `receipt <id> [--json]`); the no-answer stop's exit code is asserted from what the help
|
|
156
|
+
promises. The tombstone gate found a Python twin of a 0.4.1 text fix that had been missed.
|
|
157
|
+
- **Twin parity.** `packages/conformance/parity.mjs`: the vector labels of the TS and Python suites must match; a
|
|
158
|
+
one-sided vector is red unless `parity-allowlist.json` names it with a reason. The pre-0.4.2 asymmetry is
|
|
159
|
+
recorded as a baseline to burn down before 1.0.
|
|
160
|
+
- **No swallowed ledger read.** `packages/conformance/sdk-lint.mjs` refuses `.catch(() => undefined)`,
|
|
161
|
+
`except Exception: x = None` and empty catches around ledger and journal reads; it was red on seven such sites in
|
|
162
|
+
0.4.1 and is green now, with two justified allowances that carry their reason.
|
|
163
|
+
|
|
164
|
+
**Fixed, both languages unless noted:**
|
|
165
|
+
- The help said "YOUR Enter is the approval" (false since 0.4.1); it now says y approves, n rejects, Enter is not an
|
|
166
|
+
answer. `--help`/`-h` after any command prints the one help page (exit 0); the bare word `help` past the usage
|
|
167
|
+
positions stays a positional. `receipt <id> [--json]` is documented.
|
|
168
|
+
- The rejection half of the tour downloads and verifies the DENIAL's receipt, pinned, and names its record kind; all
|
|
169
|
+
three verifier doors print `kind:` (read from the receipt's own canonical record). The approval half names its kind
|
|
170
|
+
too. The tour's receipt goes through the same no-overwrite writer as the `receipt` command.
|
|
171
|
+
- Receipt filenames escape rather than strip: `a.b` and `ab` can no longer share a file (`~` + hex code point).
|
|
172
|
+
- The unset-session reminder prints on every end of the tour (approved, rejected, stopped) with both the bash and the
|
|
173
|
+
PowerShell command; the README carries both.
|
|
174
|
+
- An answer typed after the tour's blocking call expired (5 minutes) is not posted; the tour says the request must be
|
|
175
|
+
decided in the queue.
|
|
176
|
+
- `resolve()` on a decision whose entry was consumed between reading and claiming it now reads the ledger through one
|
|
177
|
+
shared mapping: `already_resolved` when it ran, never "durable state undefined" or a false live-attempt claim; the
|
|
178
|
+
resolved hook fires for every non-`unknown` outcome including the ledger-fallback ones.
|
|
179
|
+
- **A corrupt terminal ledger head is loud** (prodsec V041-01): `resolve()` propagates the state-corrupt error instead
|
|
180
|
+
of returning `unknown`; the poll worker says it once per decision and keeps serving the other actions; the webhook
|
|
181
|
+
doors answer 500 naming the error.
|
|
182
|
+
- A landed `init` token whose `persisted` journal note failed to write is held, not an orphan (the credential file is
|
|
183
|
+
the proof); the failed note is reported, never silent. `quickstart cleanup` names a held token instead of saying
|
|
184
|
+
the journal owes nothing (prodsec V041-02).
|
|
185
|
+
- The tour prints the call it actually makes (TS: the blocking primitive, with the note that real agents use
|
|
186
|
+
`guard.protect`; Python already ran `guard.protect(..., mode="blocking")` and now prints that too).
|
|
187
|
+
- Node relay: `--target` is never echoed raw, even on the shape-check refusal; the relay binds `127.0.0.1`.
|
|
188
|
+
|
|
189
|
+
**Guard round (one, by ruling): no block, six warnings, all taken.** The verifiers no longer print a record kind on an
|
|
190
|
+
INVALID receipt (an attacker-supplied field on a rejected document); Python's `receipt --json` keeps stdout JSON-only
|
|
191
|
+
and both suites now assert that on a stdout-only capture; an unreadable store directory is corruption, not "no
|
|
192
|
+
revisions", and an unlistable store makes the worker say so once instead of idling silently; the lint reaches
|
|
193
|
+
directory listings and multi-form excepts and refuses a `lint-allow` without a reason; the three-door gate carries a
|
|
194
|
+
correctly signed DENIAL fixture and all three doors print `kind: denial` (69/69); the claims scan walks the adapters too.
|
|
195
|
+
|
|
196
|
+
**Not reproduced:** the reviewer saw the no-answer stop exit 1 in PowerShell; under ConPTY on Windows and under a pty
|
|
197
|
+
on Linux both CLIs exit 3, and the gates now assert that code from the help's own table on both platforms.
|
|
198
|
+
|
|
199
|
+
**Unchanged by ruling:** adapters stay Beta (no real-engine suites yet); provenance and repository metadata stay
|
|
200
|
+
post-1.0.
|
|
201
|
+
|
|
7
202
|
## 0.4.1 — "no" means no
|
|
8
203
|
|
|
9
204
|
Two first-run defects found by an outside engineer within sixteen minutes of installing 0.4.0. Both were
|
package/README.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
One line to put a human-or-policy approval gate in front of any AI-agent action — and get a signed, verifiable Authority Receipt for every decision, with execution evidence accruing on the record.
|
|
4
4
|
|
|
5
5
|
`@decidio/sdk` is the client for Decidio — the system of record for enterprise authority, from OmniTwin Technologies.
|
|
6
|
+
The wrapper IS the SDK: `guard.protect(fn, describe)` ships in this package, and the `npx @decidio/sdk` command line
|
|
7
|
+
is the two-minute tour, the sign-in and the token admin around it. Install, wrap one function, run — the tour is optional.
|
|
6
8
|
|
|
7
9
|
## The two-minute tour
|
|
8
10
|
|
|
@@ -16,7 +18,11 @@ you approve right there (or from your Decidio app), executes on approval, then d
|
|
|
16
18
|
sealed Authority Receipt and verifies it offline in front of you, pinned to your workspace's
|
|
17
19
|
issuer. It mints a **7-day** tour token (`init` mints 90-day tokens — different jobs, different
|
|
18
20
|
lifetimes) and ends by offering to clean itself up (`quickstart cleanup` works later too).
|
|
19
|
-
Sign in when
|
|
21
|
+
Sign in once — `npx @decidio/sdk login`, or when the tour asks: the CLI emails you a link, you paste it, and
|
|
22
|
+
the sign-in is kept in your profile (owner-only) for 30 days, or until this terminal closes, `logout`, or
|
|
23
|
+
`quickstart cleanup`. At the human step the tour offers three doors: answer here, open the decision in
|
|
24
|
+
the web app (a one-time link, signed in for you), or scan the QR with your phone — whichever answers
|
|
25
|
+
first wins, and the terminal follows. No config: with no `DECIDIO_API_URL` set it targets the hosted synthetic
|
|
20
26
|
sandbox. Headless, the tour **stops at the routed decision** — approving is a human act, so it
|
|
21
27
|
prints the approve command and exits rather than approve its own request (CI can pass
|
|
22
28
|
`--auto-approve-demo`, which labels that authority as automated).
|
|
@@ -78,12 +84,14 @@ What `.env` contains after a successful init: `DECIDIO_API_URL`, `DECIDIO_AGENT_
|
|
|
78
84
|
`DECIDIO_WORKSPACE_ID`, and `DECIDIO_WEBHOOK_SECRET` (operator deployments only). Pick any
|
|
79
85
|
agent name — if it's already taken in your workspace, `init` stops cleanly and suggests a
|
|
80
86
|
fresh one (or pass `--unique` to auto-append a random suffix — collision-proof for shared
|
|
81
|
-
workspaces and CI).
|
|
87
|
+
workspaces and CI). Interactive terminals sign in once (`npx @decidio/sdk login` — stored owner-only in your
|
|
88
|
+
profile, bound to this terminal window, 30 days). Headless terminal (no prompts possible)? Export `DECIDIO_SESSION_TOKEN` first:
|
|
82
89
|
request a sign-in link, then exchange its `?login=` value at `POST /demo/login-consume` — the
|
|
83
90
|
response's `token` is your session token (the CLI prints these exact instructions when it
|
|
84
91
|
can't prompt). That session token is your workspace login, not the agent's credential: unset it
|
|
85
92
|
before launching agent code from the same shell, because child processes inherit exported
|
|
86
|
-
variables
|
|
93
|
+
variables (`unset DECIDIO_SESSION_TOKEN` in bash/zsh, `Remove-Item Env:DECIDIO_SESSION_TOKEN` in
|
|
94
|
+
PowerShell). In the tour's human step, `y` approves and `n` rejects — the rejection half is worth
|
|
87
95
|
watching once: the request is refused and your function does not run.
|
|
88
96
|
|
|
89
97
|
**3. Protect one function** — save this as `quickstart.mjs`:
|
|
@@ -639,7 +647,11 @@ that fits every other confirm error.
|
|
|
639
647
|
- `receipt <id>` — download a sealed decision's Authority Receipt to a `.json` file
|
|
640
648
|
- `verify [--issuer <did>] <file>...` — verify receipts offline (the canonical verifier; the standalone `@decidio/verify` package is the same implementation, for auditors who want zero Decidio dependency — the sdk depends on the verifier released alongside it, at the same version). Unknown flags are refused, never ignored. Fully offline AND fully decoupled: it touches no network and reads no `.env` — a receipt verifies identically from an empty directory.
|
|
641
649
|
- `tokens list [--mine]` / `tokens revoke <jti>` — see and kill agent tokens (values are never re-shown; the jti is the revocation handle). Each row carries its status — `active`, `expired`, or `REVOKED <date>` — so the list confirms the revocations it performs; re-revoking is an honest no-op (exit 1). The list is workspace-wide (a shared workspace shows every member's agents); `--mine` narrows to this directory's agent
|
|
642
|
-
- `quickstart cleanup` — revoke the tour's token and delete its local credentials. What stays, honestly: sealed records (permanent by design) and the agent's registration row (inert without key or token; server-side deregistration is on the roadmap)
|
|
650
|
+
- `quickstart cleanup` — revoke the tour's token and delete its local credentials. What stays, honestly: sealed records (permanent by design) and the agent's registration row (inert without key or token; server-side deregistration is on the roadmap) Also ends the stored sign-in.
|
|
651
|
+
- **Update check.** Network commands ask the public npm registry once a day whether a newer version exists; if so, an interactive run asks `Upgrade now? [y/n]` first (`y` upgrades the way you installed - `npm i @decidio/sdk@latest` in the project for a local install, `-g` for a global one, and under `npx` it prints the `@latest` form - then re-runs your command once; `n` exits), a headless run prints the notice and continues (`DECIDIO_REQUIRE_LATEST=1` makes it exit). `DECIDIO_NO_UPDATE_CHECK=1` skips it. Nothing is sent to Decidio; `verify` never checks.
|
|
652
|
+
- **The token recovery journal** (`.decidio-token-journal`, owner-only, gitignored; ids and timestamps, never a token value). One JSON row per event: `minted` (a token was issued, written BEFORE its credential file), `retired how=persisted` (the credential file now holds it - the token is live and that file is its handle; not revoked), `retired how=revoked` (revoked server-side), `probe` (a durability check). `doctor` names rows still outstanding; `quickstart cleanup` sweeps them.
|
|
653
|
+
- `quickstart --phone web` — the QR at the human step opens the phone web rendering even when the Decidio app is installed (by default it opens the app when there is one).
|
|
654
|
+
- `login [email]` / `logout` — sign in ONCE (the CLI emails you a link; you paste it) and forget that sign-in on this computer (the session token itself stays valid until it expires (up to 30 days) — there is no server-side revocation yet). The session is stored in your user profile, owner-only, bound to the terminal window that created it, for 30 days; `DECIDIO_SESSION_TOKEN` in the environment outranks it (headless shells).
|
|
643
655
|
- `dev [--target URL]` — local relay for the resume webhook (operator setups)
|
|
644
656
|
|
|
645
657
|
The CLI's stream contract: **stdout carries results only** (safe to pipe — `--json` output is
|
|
@@ -647,8 +659,9 @@ pure JSON); errors, usage shown on *error* paths, and the per-run target-host an
|
|
|
647
659
|
to **stderr**. Asked-for help (an explicit `--help`, or invoking the bare CLI) is itself a
|
|
648
660
|
result and prints on stdout. Exit codes: 0 success · 1 failure or honest no-op · 2 usage error · 3 `quickstart` stopped at the human moment (headless: the decision routed, but nothing executed and no receipt was written, so the run is deliberately not reported as a completed tour).
|
|
649
661
|
|
|
650
|
-
Commands that administer your workspace (`init`, `approvals`, `receipt`) use a **session**
|
|
651
|
-
sign-in
|
|
662
|
+
Commands that administer your workspace (`init`, `approvals`, `receipt`, `tokens`) use a **session**
|
|
663
|
+
sign-in: `login` stores it in your user profile (owner-only, bound to this terminal window, 30 days;
|
|
664
|
+
`logout` or `quickstart cleanup` forgets it here — the token itself stays valid until it expires), or `DECIDIO_SESSION_TOKEN` supplies it for headless shells. What lands on disk in `.env`: the agent's floor-limited
|
|
652
665
|
API token, its locally-generated private signing key, and a webhook secret (used only in
|
|
653
666
|
operator deployments) — `init` adds `.env` to `.gitignore` for you; move secrets to a secret
|
|
654
667
|
manager for production.
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
export type ApprovalAnswer = "approve" | "reject" | "again";
|
|
3
3
|
export declare function approvalAnswer(answer: unknown): ApprovalAnswer;
|
|
4
|
+
/** `decidio-receipt-<id>.json`, with every character outside [A-Za-z0-9_-] escaped as `~` + hex code point, so two
|
|
5
|
+
* distinct decision ids can never share a file (first-run review of 0.4.1: dots were STRIPPED, `a.b` and `ab`
|
|
6
|
+
* collided). `~` is outside the allowed set, so the escape is unambiguous. Exported for the smokes. */
|
|
7
|
+
export declare function receiptFileName(id: string): string;
|