@capxul/cli 4.20.0-beta.6 → 4.20.0-beta.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/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Capxul CLI
2
2
 
3
3
  The CLI provides email OTP login, persistent first-party sessions, terminal-owned
4
- signup, a bundled Openfort wallet page, backend profile and account reads,
5
- diagnostics, and one global collection preference. It requires Node 24 or later
6
- on macOS or Linux.
4
+ signup, personal and Organization contacts, a bundled Openfort wallet page,
5
+ backend profile and account reads, diagnostics, and one global collection
6
+ preference. It requires Node 24 or later on macOS or Linux.
7
7
 
8
8
  ```sh
9
9
  capxul --help
@@ -24,6 +24,51 @@ machinery and collection controls send no observation records.
24
24
  check uses the public Capxul SDK and verifies the backend response nonce.
25
25
  It does not authenticate a person or submit a transaction.
26
26
 
27
+ ## Contact commands
28
+
29
+ ```sh
30
+ capxul contact list [--include-hidden]
31
+ capxul contact get --entry-id PARTY_ID
32
+ capxul contact add --input FILE|- [--confirm]
33
+ capxul contact label --entry-id PARTY_ID --label TEXT [--confirm]
34
+ capxul contact hide --entry-id PARTY_ID [--confirm]
35
+ capxul contact unhide --entry-id PARTY_ID [--confirm]
36
+
37
+ capxul org contact list --org ORGANIZATION_ID [--include-hidden]
38
+ capxul org contact get --org ORGANIZATION_ID --entry-id PARTY_ID
39
+ capxul org contact add --org ORGANIZATION_ID --input FILE|- [--confirm]
40
+ capxul org contact label --org ORGANIZATION_ID --entry-id PARTY_ID --label TEXT [--confirm]
41
+ capxul org contact hide --org ORGANIZATION_ID --entry-id PARTY_ID [--confirm]
42
+ capxul org contact unhide --org ORGANIZATION_ID --entry-id PARTY_ID [--confirm]
43
+ ```
44
+
45
+ Personal commands use the authenticated Account address book. Organization
46
+ commands require one explicit `--org` or `--org-id`; they never use the saved
47
+ Organization read default. `list` omits hidden entries unless
48
+ `--include-hidden` is present. `get`, `label`, `hide`, and `unhide` address one
49
+ stable Party with `--entry-id`.
50
+
51
+ `add --input` reads the existing `AddressBookAddInput` JSON shape. For example:
52
+
53
+ ```json
54
+ {
55
+ "ref": { "kind": "email", "email": "supplier@example.test" },
56
+ "label": "New Supplier"
57
+ }
58
+ ```
59
+
60
+ Use `--input -` to read the same object from stdin. An interactive `add` can
61
+ collect the reference kind, value, and optional label instead. Interactive
62
+ writes show the actor and exact contact change, then ask a default-no question.
63
+ JSON, CI, and other noninteractive writes require `--confirm` before the CLI
64
+ creates a client. Contact writes do not start a browser signer.
65
+
66
+ Success data is the SDK value without a CLI wrapper: `list` returns
67
+ `AddressBookEntry[]`, `get` returns `AddressBookEntry | null`, and each mutation
68
+ returns `AddressBookEntry`. Human `get` prints `No contact found` for null and
69
+ exits successfully. Entries retain their stable PartyId, exact reference,
70
+ relationships, hidden state, and last activity time.
71
+
27
72
  ## Organization commands
28
73
 
29
74
  ```sh
@@ -44,6 +89,14 @@ These commands accept `--email`. Without it, they restore the protected current
44
89
  session. Organization reads accept `--org` or its alias `--org-id`. Identical
45
90
  repeated IDs are accepted. Different IDs are refused.
46
91
 
92
+ A read without `--org` resolves one exact Organization in this order: the saved
93
+ read default, then the recovery handle this machine recorded for the person's
94
+ Organization journey. A recovery handle can also name an Organization whose
95
+ setup lane still runs, which the own-access list does not carry yet; the read
96
+ reports that exact lane, and it applies the usual current-access rule again once
97
+ the Organization reports ready. The read refuses an Organization it cannot
98
+ reach, and it never substitutes another one.
99
+
47
100
  `org use` requires an explicit Organization ID. It checks current access and
48
101
  saves a protected per-person read default. The default never grants authority.
49
102
  Each read checks current access again. Without an explicit ID or saved default,
@@ -60,12 +113,74 @@ No match returns exit 2. Duplicate matches return exit 1.
60
113
  seconds and defaults to 120. Timeout returns exit 5; interruption returns 130.
61
114
  A successful read can report a failed or pending domain state.
62
115
 
116
+ ## Invitation commands
117
+
118
+ ```sh
119
+ capxul org invite list [--mine] [--limit N] [--cursor C] [--phase PHASE ...] [--org O]
120
+ capxul org invite get --invitation-id I [--org O]
121
+ capxul org invite wait --invitation-id I [--org O] [--timeout-seconds N]
122
+ capxul org invite review --invitation-id I --org O [--timeout-seconds N]
123
+ capxul org invite accept --invitation-id I --offer-digest D --org O [--confirm] [--timeout-seconds N]
124
+ capxul org invite decline --invitation-id I --org O [--confirm]
125
+ capxul org invite cancel --invitation-id I --org O [--confirm]
126
+ capxul org invite resend --invitation-id I --org O [--confirm]
127
+ capxul org invite retry --invitation-id I --org O [--confirm]
128
+ capxul org member invite (--to-email E | --account-id ID) [--budget-id B ...] [--permission-id M ...] [--new-budget-name N --asset A (--limit Q | --unlimited) (--recipient-account ID ... | --any-recipient) --actions pay[,commitments]] --org O [--request-key K] [--preview] [--confirm]
129
+ ```
130
+
131
+ `org invite list` returns your own offers. It accepts `--limit` (1--100),
132
+ `--cursor`, and repeated `--phase` values, and its result carries `invitations`,
133
+ `nextCursor`, and `observedAt`: pass `nextCursor` back as `--cursor`, and change
134
+ no other filter between the two calls. `--mine` names the own-offer projection,
135
+ which is already the default. With `--org O` the command returns that
136
+ Organization's invitations for a current Admin instead; the Organization list is
137
+ not filtered to you, so `--mine` together with `--org` refuses with exit 2. The
138
+ other invitation commands require an exact `--invitation-id`.
139
+
140
+ `org invite get` and `org invite wait` use `--org` when you name it. Without
141
+ `--org` they walk your own-offer pages, 100 rows per request, until they find
142
+ the invitation or the pages end. They never consult the saved read default, and
143
+ an offer you cannot see is refused. `--timeout-seconds` bounds the resolution
144
+ and the read together on both commands.
145
+
146
+ `org invite wait` only reads. It finishes when the offer settles or when the
147
+ recovery asks a person to act. It never retries, resends, or accepts.
148
+
149
+ `org invite review` requires `--org`, returns the current `InvitationView`
150
+ directly in `data`, and performs no write or signing.
151
+
152
+ The four transitions require `--org` in both modes, as `accept` does. They never
153
+ author a new offer. A noninteractive or JSON run requires `--confirm`. An
154
+ interactive run shows the resolved current offer and asks a default-no question.
155
+
156
+ `org member invite` authorizes one exact offer. `--preview` writes nothing and
157
+ needs neither a request key nor confirmation. A noninteractive write requires
158
+ `--request-key` and `--confirm`. An interactive run prints the request key it
159
+ used before it submits, so a lost response is recoverable with the same
160
+ identity. At most one `--new-budget-name` is accepted per command.
161
+
162
+ `org invite accept` is the exact grantee's consent commit. It takes the exact
163
+ digest the offer shows as `--offer-digest`: a noninteractive or JSON run must
164
+ supply it, and without it the command refuses with exit 2 before it creates a
165
+ client. An interactive run may omit it, and then reads the offer and fills the
166
+ digest from the value it displayed. A digest you state is never replaced by the
167
+ displayed one. `--expected-offer` remains a compatibility spelling, and both
168
+ spellings must match when supplied together. The command returns the
169
+ `InvitationView` directly in `data`. The grant is executed by the deployment's
170
+ technical executor, so the command needs no browser bridge and works in a
171
+ headless or CI session. A repeat with the stored digest returns the same
172
+ accepted result; a different digest refuses and cannot overwrite consent. The
173
+ returned view is the authorization-time view of the consent commit
174
+ (`pending_grant`); read the settled `active` state with `get` or `wait`.
175
+
63
176
  ## Organization writes
64
177
 
65
178
  `org create` and `org retry` restore the protected current session and start the
66
179
  existing command-scoped browser signer. They never read or write the saved read
67
- default. Local input checks run before any client, browser, or backend work, and
68
- they include the confirmation gate:
180
+ default. `org create` writes the person's Organization recovery handle. That
181
+ handle holds the exact business handle and the exact Organization ID, and it
182
+ holds nothing else. Local input checks run before any client, browser, or
183
+ backend work, and they include the confirmation gate:
69
184
 
70
185
  - A noninteractive or JSON write requires `--confirm`. Without it the command
71
186
  refuses with exit 2 before any client, browser, or mutation work.
@@ -106,6 +221,47 @@ failed lane still reports the Organization it is about. The command returns
106
221
  success only after an exact `org(O).getLifecycle()` read reports `ready`. It
107
222
  never saves a read default and never returns a different ready Organization.
108
223
 
224
+ `org create` continues the Organization journey this machine already prepared.
225
+ Before the first durable write it records the exact business handle as the safe
226
+ recovery handle. It adds the first committed Organization ID to that handle
227
+ before it forwards any founder authorization digest, and it keeps that handle
228
+ after a failure, an interruption, or a ready result. A later create therefore
229
+ resolves exactly one Organization:
230
+
231
+ - The same handle resumes the exact recorded Organization. It calls the SDK
232
+ recovery owner once per attempt and settles the same lifecycle. It never
233
+ submits a second bootstrap, and it never creates a second Organization or
234
+ treasury.
235
+ - A different handle refuses with exit 2 while the recorded Organization is not
236
+ ready. The refusal names the exact Organization.
237
+ - A different handle creates one Organization only after the recorded
238
+ Organization reaches ready.
239
+ - A recorded Organization that is already ready, and still in the person's
240
+ current access, returns unchanged, with no write and no signature. A ready
241
+ result the person can no longer reach refuses, and the committed recovery
242
+ handle stays recorded for exact recovery.
243
+ - A create that continues a recorded handle checks current access before it
244
+ reports success. The committed Organization ID becomes durable first, so a
245
+ refusal or an interruption keeps the exact recovery handle, and a ready
246
+ lifecycle line reaches stderr only after the check resolves.
247
+ - A terminal preview reads the recorded Organization's lifecycle only for the
248
+ handle that continues it. A different handle is a new creation, so an
249
+ unreachable recorded Organization never blocks it.
250
+ - A handle the backend refuses is refused before any Organization write. The
251
+ command releases the prepared handle, so the person can correct the handle and
252
+ create. Every other failure keeps the handle, because its Organization may
253
+ already exist.
254
+
255
+ The recovery line names the exact Organization and the safe handle. JSON mode
256
+ writes it as one newline-delimited progress object:
257
+
258
+ ```json
259
+ { "type": "organization.recovery", "organizationId": "O", "handle": "acme" }
260
+ ```
261
+
262
+ `org retry` stays explicit: `--org` (or its alias `--org-id`) is the one write
263
+ scope, and no read default or recovery handle is consulted.
264
+
109
265
  `org retry` requires an explicit `--org` (or `--org-id`). It validates the ID
110
266
  with the public `toOrgId` constructor, reads that exact Organization's lifecycle
111
267
  for its terminal preview, resumes that same durable setup lane, then settles the
@@ -162,7 +318,8 @@ unsafe permissions, symlinks, and corrupt state produce a storage failure.
162
318
 
163
319
  ## Output
164
320
 
165
- `--json` writes one version 1 envelope to stdout. Success contains `data`.
321
+ `--json` writes one version 1 envelope to stdout. Success contains `data`, which
322
+ can be an object, array, or null according to the command's SDK result.
166
323
  Failure contains `error.code` and CLI-owned `error.message`. Each envelope has
167
324
  `command`, `invocationId`, and `outcome`. An invocation ID is null before a
168
325
  command starts. Human errors go to stderr.
@@ -242,11 +399,21 @@ stored and tagged as `legacy-native-grant` in status. A profile read asks for an
242
399
  explicit new login; it never exchanges the old grant for a broader session.
243
400
  Logout forgets and attempts to revoke both credential types for the selected email.
244
401
 
245
- For a human, `capxul auth login` prompts for email and a hidden OTP. `auth signup`
246
- also prompts for the current Profile fields: display name, two-letter country
247
- code, and handle. The browser opens only when the shared Core onboarding journey
248
- needs Openfort wallet readiness. It shows wallet status only. It has no email,
249
- OTP, Profile, or approval form.
402
+ `capxul auth login` and `capxul auth signup` stay separate commands. On a
403
+ terminal, each command is a wizard. `auth login` asks for the email and a masked
404
+ OTP. It accepts the code as a paste. It retries an invalid code at most three
405
+ times for one request. After sign-in, an incomplete account gets one offer to
406
+ continue setup in the same command. `auth signup` authenticates first, then asks
407
+ for the Profile fields: display name, two-letter country code, and handle. A
408
+ known value is the prompt default, so Enter keeps it.
409
+
410
+ The browser opens only when the shared Core onboarding journey needs Openfort
411
+ wallet readiness. It shows wallet status only. It has no email, OTP, Profile, or
412
+ approval form.
413
+
414
+ Ctrl+C or Ctrl+D at a wizard prompt cancels the command. The command prints no
415
+ success result and exits 130. A step that already finished keeps its result: a
416
+ completed sign-in keeps its session.
250
417
 
251
418
  For an agent, send and verify in separate processes:
252
419
 
@@ -254,7 +421,6 @@ For an agent, send and verify in separate processes:
254
421
  capxul auth send --email "$TEST_EMAIL" --json
255
422
  # Supply the delivered six-digit OTP through stdin, not a command argument.
256
423
  capxul auth verify --email "$TEST_EMAIL" --otp-stdin --json
257
- capxul auth signup --email "$TEST_EMAIL" --display-name "Test Person" --country GH --handle test_person --json
258
424
  capxul auth profile --email "$TEST_EMAIL" --json
259
425
  # A later process uses the same protected CLI home; no new OTP is required.
260
426
  capxul auth profile --email "$TEST_EMAIL" --json
@@ -262,12 +428,13 @@ capxul auth status --email "$TEST_EMAIL" --json
262
428
  capxul auth logout --email "$TEST_EMAIL" --json
263
429
  ```
264
430
 
265
- `--otp-stdin` reads up to 64 bytes, ending at EOF. Pipe from a secret provider or
266
- use input redirection from a protected file. OTPs are never accepted in argv,
267
- persisted in the continuation, or included in output. Non-interactive commands
268
- without the required input refuse with exit 2 instead of prompting.
431
+ A machine run never prompts. `auth login` with `--otp-stdin` sends the OTP and
432
+ verifies one code from stdin. It returns the tagged result
433
+ `setupState: "setup-required"` when the account needs setup. It does not start
434
+ setup work, so sign-in never becomes signup.
269
435
 
270
- A fresh non-interactive signup can do verification and setup in one command:
436
+ A fresh machine signup does verification and setup in one command. State every
437
+ Profile field; a missing field refuses with exit 2:
271
438
 
272
439
  ```sh
273
440
  # Supply only the delivered OTP through stdin.
@@ -275,6 +442,11 @@ capxul auth signup --email "$TEST_EMAIL" --otp-stdin \
275
442
  --display-name "Test Person" --country GH --handle test_person --json
276
443
  ```
277
444
 
445
+ `--otp-stdin` reads up to 64 bytes, ending at EOF. Pipe from a secret provider or
446
+ use input redirection from a protected file. An invalid value refuses with exit 2. A missing `--otp-stdin` refuses with exit 2 before the command starts client
447
+ work. OTPs are never accepted in argv, persisted in the continuation, or included
448
+ in output.
449
+
278
450
  The local page binds to an OS-selected loopback port. A random launch capability
279
451
  is redeemed once and removed from the URL before Openfort starts. The page receives
280
452
  only the authenticated wallet token and encryption session in memory. Closing it
@@ -282,6 +454,29 @@ ends the current wallet attempt. Re-run `auth signup` with the same CLI home to
282
454
  resume the same Profile and Account without another OTP while the backend session
283
455
  remains valid.
284
456
 
457
+ `auth signup` returns only two readiness results. `setupState: "ready"` is a
458
+ personal Account that the Account readiness owner reads as ready and deployed,
459
+ and its data carries the public identifiers: the Profile, the public wallet
460
+ address (`smartAccount.signerAddress`), the personal Smart Account address
461
+ (`smartAccount.smartAccountAddress`), the Account ID
462
+ (`lifecycle.accountId`), and the ready status. Any other state returns the typed
463
+ recoverable result `setupState: "setup-required"`, with the lifecycle, its
464
+ failure code, and the command that resumes the journey. A ready Account starts
465
+ no wallet work, so a retry keeps the one Profile, wallet, Smart Account, and
466
+ Account. An interruption keeps the stored session and stores no code, so the
467
+ next command resumes the same setup.
468
+
469
+ A ready personal Account continues the same journey into Organization creation
470
+ with `capxul org create --name NAME --handle HANDLE --country CC`. The create
471
+ command records the safe recovery handle for that Organization, so an
472
+ interruption or a restart resumes the exact same Organization.
473
+
474
+ The CLI checks `--display-name`, `--country`, and `--handle` before it opens a
475
+ client, so a malformed value refuses with exit 2 and no code is sent. The handle
476
+ check covers the grammar only: a handle that another person holds, and a reserved
477
+ handle, refuse through the identity owner after sign-in. Both refusals name the
478
+ handle, and neither offers verification-code recovery for a Profile problem.
479
+
285
480
  The version 2 `first-party-session` record stores the opaque provider credential
286
481
  in the existing protected plaintext store. Its scope includes the application
287
482
  key, issuer, environment, and email. Each process validates restoration with the