@capxul/cli 4.20.0-beta.1 → 4.20.0-beta.11

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,7 +1,9 @@
1
1
  # Capxul CLI
2
2
 
3
- The CLI provides local diagnostics, an online backend check, and one global
4
- collection preference. It requires Node 24 or later on macOS or Linux.
3
+ The CLI provides email OTP login, persistent first-party sessions, terminal-owned
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.
5
7
 
6
8
  ```sh
7
9
  capxul --help
@@ -14,27 +16,216 @@ capxul telemetry enable --json
14
16
  capxul doctor --online --timeout-ms 30000 --json
15
17
  ```
16
18
 
17
- Help, version, and completion do not read configuration or contact a server.
19
+ Help and version do not require application credentials or a working backend.
20
+ With no arguments, `capxul` shows the same generated help as `capxul --help`.
21
+ They use the same observation policy as ordinary commands. Shell completion
22
+ machinery and collection controls send no observation records.
18
23
  `doctor` checks local configuration unless `--online` is present. An online
19
24
  check uses the public Capxul SDK and verifies the backend response nonce.
20
25
  It does not authenticate a person or submit a transaction.
21
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
+
72
+ ## Organization commands
73
+
74
+ ```sh
75
+ capxul org list --json
76
+ capxul org use --org ORGANIZATION_ID
77
+ capxul org status --org ORGANIZATION_ID --json
78
+ capxul org get --json
79
+ capxul org me --json
80
+ capxul org members --json
81
+ capxul org member list --json
82
+ capxul org member get --account-id ACCOUNT_ID --json
83
+ capxul org wait --timeout-seconds 120 --json
84
+ capxul org create --name NAME --handle HANDLE --country CC [--bio TEXT] [--size TEXT] [--confirm]
85
+ capxul org retry --org ORGANIZATION_ID [--confirm]
86
+ ```
87
+
88
+ These commands accept `--email`. Without it, they restore the protected current
89
+ session. Organization reads accept `--org` or its alias `--org-id`. Identical
90
+ repeated IDs are accepted. Different IDs are refused.
91
+
92
+ A read without --org uses the saved read default, then this machine's
93
+ recovery ID, then the one unfinished setup returned by the backend. org list
94
+ includes unfinished founder setups. This works from a clean authenticated home.
95
+ If more than one Organization is possible, give an exact --org. A ready
96
+ Organization must still appear in current access.
97
+
98
+ org use saves a protected per-person read default. The default grants no
99
+ authority. Member lookup uses an exact AccountId. Reads do not sign or change
100
+ setup state.
101
+
102
+ org wait opens one exact setup subscription. It continues when needsAttention
103
+ has a scheduled next check. It stops at ready, failed, awaitingAuthorization,
104
+ or attention without a next check. It does not request a check or poll. The
105
+ timeout accepts 1 to 3600 seconds and defaults to 120. A deadline returns
106
+ CLI_DEADLINE at exit 5; Ctrl-C returns CANCELLED at exit 130.
107
+
108
+ ## Invitation commands
109
+
110
+ ```sh
111
+ capxul org invite list [--mine] [--limit N] [--cursor C] [--phase PHASE ...] [--org O]
112
+ capxul org invite get --invitation-id I [--org O]
113
+ capxul org invite wait --invitation-id I [--org O] [--timeout-seconds N]
114
+ capxul org invite review --invitation-id I --org O [--timeout-seconds N]
115
+ capxul org invite accept --invitation-id I --offer-digest D --org O [--confirm] [--timeout-seconds N]
116
+ capxul org invite decline --invitation-id I --org O [--confirm]
117
+ capxul org invite cancel --invitation-id I --org O [--confirm]
118
+ capxul org invite resend --invitation-id I --org O [--confirm]
119
+ capxul org invite retry --invitation-id I --org O [--confirm]
120
+ 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]
121
+ ```
122
+
123
+ `org invite list` returns your own offers. It accepts `--limit` (1--100),
124
+ `--cursor`, and repeated `--phase` values, and its result carries `invitations`,
125
+ `nextCursor`, and `observedAt`: pass `nextCursor` back as `--cursor`, and change
126
+ no other filter between the two calls. `--mine` names the own-offer projection,
127
+ which is already the default. With `--org O` the command returns that
128
+ Organization's invitations for a current Admin instead; the Organization list is
129
+ not filtered to you, so `--mine` together with `--org` refuses with exit 2. The
130
+ other invitation commands require an exact `--invitation-id`.
131
+
132
+ `org invite get` and `org invite wait` use `--org` when you name it. Without
133
+ `--org` they walk your own-offer pages, 100 rows per request, until they find
134
+ the invitation or the pages end. They never consult the saved read default, and
135
+ an offer you cannot see is refused. `--timeout-seconds` bounds the resolution
136
+ and the read together on both commands.
137
+
138
+ `org invite wait` only reads. It finishes when the offer settles or when the
139
+ recovery asks a person to act. It never retries, resends, or accepts.
140
+
141
+ `org invite review` requires `--org`, returns the current `InvitationView`
142
+ directly in `data`, and performs no write or signing.
143
+
144
+ The four transitions require `--org` in both modes, as `accept` does. They never
145
+ author a new offer. A noninteractive or JSON run requires `--confirm`. An
146
+ interactive run shows the resolved current offer and asks a default-no question.
147
+
148
+ `org member invite` authorizes one exact offer. `--preview` writes nothing and
149
+ needs neither a request key nor confirmation. A noninteractive write requires
150
+ `--request-key` and `--confirm`. An interactive run prints the request key it
151
+ used before it submits, so a lost response is recoverable with the same
152
+ identity. At most one `--new-budget-name` is accepted per command.
153
+
154
+ `org invite accept` is the exact grantee's consent commit. It takes the exact
155
+ digest the offer shows as `--offer-digest`: a noninteractive or JSON run must
156
+ supply it, and without it the command refuses with exit 2 before it creates a
157
+ client. An interactive run may omit it, and then reads the offer and fills the
158
+ digest from the value it displayed. A digest you state is never replaced by the
159
+ displayed one. `--expected-offer` remains a compatibility spelling, and both
160
+ spellings must match when supplied together. The command returns the
161
+ `InvitationView` directly in `data`. The grant is executed by the deployment's
162
+ technical executor, so the command needs no browser bridge and works in a
163
+ headless or CI session. A repeat with the stored digest returns the same
164
+ accepted result; a different digest refuses and cannot overwrite consent. The
165
+ returned view is the authorization-time view of the consent commit
166
+ (`pending_grant`); read the settled `active` state with `get` or `wait`.
167
+
168
+ ## Organization writes
169
+
170
+ org create and org retry use the protected current session unless --email names
171
+ one. A JSON or noninteractive write requires --confirm. A terminal write shows
172
+ the current Organization and recovery information, then asks a default-no
173
+ question. The signer starts after confirmation.
174
+
175
+ org create validates name, handle, and country before client work. It calls
176
+ onboarding.beginOrganization, saves and prints the exact Organization ID,
177
+ authorizes only an awaitingAuthorization setup, then subscribes to setup
178
+ changes. It reports success only after ready and current access. JSON progress
179
+ uses one line per state:
180
+
181
+ { "type": "organization.lifecycle", "organizationId": "O", "status": "processing" }
182
+
183
+ org retry requires one exact --org. It authorizes only awaitingAuthorization.
184
+ For an accepted setup it asks the backend to check the same operation hash
185
+ through setup.resume, then subscribes. Neither command replaces a submitted
186
+ operation. The local recovery record keeps a safe handle and Organization ID
187
+ for convenience; the backend unfinished list supports recovery without it.
188
+
189
+ --timeout-seconds sets one 1-to-3600-second deadline for the command. A
190
+ deadline stops local work and returns exit 5. If the last observed setup has
191
+ another automatic check scheduled, the message is "Stopped waiting.
192
+ Organization setup continues in the background." Before acceptance or without
193
+ a scheduled check, the message is "Stopped waiting. Read the Organization
194
+ setup status." The error details include the safe Organization ID when known,
195
+ last observed setup state, setup reason, and bounded provider diagnostics.
196
+ Ctrl-C stops local work at exit 130. Neither stop writes a backend failure.
197
+
22
198
  ## Configuration
23
199
 
24
200
  | Variable | Use |
25
201
  | ------------------------------ | ------------------------------------------------------------------------------------------------------------ |
26
202
  | `CAPXUL_CLI_HOME` | Directory for global CLI settings. Defaults to `$XDG_CONFIG_HOME/capxul/cli`, or `$HOME/.config/capxul/cli`. |
27
- | `CAPXUL_PUBLISHABLE_KEY` | Application publishable key, required for an online check. |
203
+ | `CAPXUL_PUBLISHABLE_KEY` | Optional developer override for the bundled first-party staging key. |
28
204
  | `CAPXUL_BOOTSTRAP_URL` | Bootstrap origin. Defaults to `https://api.staging.capxul.com`. Convex Cloud origins are rejected. |
29
- | `CAPXUL_POSTHOG_HOST` | PostHog ingestion origin. |
30
- | `CAPXUL_POSTHOG_PROJECT_TOKEN` | Project ingestion token. Both PostHog variables are required for export. |
205
+ | `CAPXUL_POSTHOG_HOST` | Optional development override for the built-in public PostHog ingestion origin. |
206
+ | `CAPXUL_POSTHOG_PROJECT_TOKEN` | Optional development override for the built-in public project ingestion token. |
31
207
  | `CAPXUL_TELEMETRY_DISABLED` | Set to `true` to disable remote observation regardless of the saved preference. |
32
208
 
33
- Collection defaults to enabled. Local commands send no remote signals.
209
+ The published CLI includes the verified first-party staging application key and
210
+ Capxul-owned public ingestion configuration. You do not need key or PostHog
211
+ environment variables. A different bootstrap origin requires an explicit key. Collection defaults to enabled for
212
+ ordinary local and online commands, help, version, and safely attributed argument
213
+ refusals. Parser refusals produce a completion without a start. Early native
214
+ global errors with no resolved command route send nothing, because the CLI
215
+ cannot determine whether they belong to a silent collection control.
34
216
  `telemetry disable` saves one preference for the OS user and sends no final
35
217
  remote event. Already running CLI processes check the current preference before
36
218
  each export. Requests already sent cannot be recalled. `telemetry status`
37
219
  reports the stored preference, effective policy, configuration, and reason.
220
+ `--log-level` controls normal diagnostic output, not collection. An eligible
221
+ invocation still produces its remote completion unless collection is disabled.
222
+
223
+ Eligible commands reuse a random anonymous identifier stored in the protected
224
+ CLI directory. Invocation and trace identifiers remain separate. A first-use
225
+ marker records the first eligible observed use, not a download or a verified
226
+ person. If this state cannot be accessed safely, observation stops and the
227
+ command retains its normal result. Delivery is bounded and best-effort; the CLI
228
+ does not keep a persistent activity queue.
38
229
 
39
230
  Settings use a versioned JSON file in a directory with mode `0700`. The file has
40
231
  mode `0600`. Writes are atomic and serialize between processes. A later writer
@@ -43,8 +234,14 @@ unsafe permissions, symlinks, and corrupt state produce a storage failure.
43
234
 
44
235
  ## Output
45
236
 
46
- `--json` writes one version 1 envelope to stdout. Success contains `data`.
47
- Failure contains `error.code` and CLI-owned `error.message`. Each envelope has
237
+ `--json` writes one version 1 envelope to stdout. Success contains `data`, which
238
+ can be an object, array, or null according to the command's SDK result.
239
+ Failure contains `error.code` and CLI-owned `error.message`. A wallet failure
240
+ also includes its known `error.mode` and allowed `error.details`: wallet stage,
241
+ operation, provider, provider code, and HTTP status. An unknown browser failure
242
+ uses mode `unknown`. No provider message, token, signature, or native cause enters
243
+ the result. These fields remain available when collection is off or
244
+ `--log-level none` is set. Each envelope has
48
245
  `command`, `invocationId`, and `outcome`. An invocation ID is null before a
49
246
  command starts. Human errors go to stderr.
50
247
 
@@ -93,6 +290,15 @@ compinit
93
290
  Use `npm update -g @capxul/cli` or `brew upgrade xelmar-tech/tap/capxul` to update.
94
291
  Use one installer for the `capxul` executable to avoid conflicting PATH entries.
95
292
 
293
+ After a successful human command, help, or version request, the CLI can show an
294
+ update notice on terminal stderr. It checks the public npm `latest` tag at most
295
+ once per 24 hours. It shows only the command for the verified running npm-global
296
+ or Homebrew installation. It does not execute that command. Unknown installations,
297
+ development versions, JSON output, redirected stderr, CI, shell completion, and
298
+ all `telemetry` commands receive no notice. The optional check has one 500 ms
299
+ budget. Safe failed attempts are cached. Storage or network failures remain
300
+ silent and do not change the command result. This check sends no telemetry.
301
+
96
302
  ## Development commands
97
303
 
98
304
  ```sh
@@ -105,3 +311,103 @@ vp exec node --test --test-name-pattern='@capxul/cli' scripts/__tests__/pack-ins
105
311
  The last command installs the tarball outside the workspace and exercises the
106
312
  installed executable with isolated settings and a local HTTP server. The CI
107
313
  `CLI / Linux / Node 24.0.0` job runs this proof at the declared minimum version.
314
+
315
+ ## Email login and session restoration
316
+
317
+ This CLI establishes a first-party BetterAuth session. It has broader authority
318
+ than the earlier native `account:read` grant. Existing native grants remain
319
+ stored and tagged as `legacy-native-grant` in status. A profile read asks for an
320
+ explicit new login; it never exchanges the old grant for a broader session.
321
+ Logout forgets and attempts to revoke both credential types for the selected email.
322
+
323
+ `capxul auth login` and `capxul auth signup` stay separate commands. On a
324
+ terminal, each command is a wizard. `auth login` asks for the email and a masked
325
+ OTP. It accepts the code as a paste. It retries an invalid code at most three
326
+ times for one request. After sign-in, an incomplete account gets one offer to
327
+ continue setup in the same command. `auth signup` authenticates first, then asks
328
+ for the Profile fields: display name, two-letter country code, and handle. A
329
+ known value is the prompt default, so Enter keeps it.
330
+
331
+ The browser opens only when the shared Core onboarding journey needs Openfort
332
+ wallet readiness. It shows wallet status only. It has no email, OTP, Profile, or
333
+ approval form.
334
+
335
+ Ctrl+C or Ctrl+D at a wizard prompt cancels the command. The command prints no
336
+ success result and exits 130. A step that already finished keeps its result: a
337
+ completed sign-in keeps its session.
338
+
339
+ For an agent, send and verify in separate processes:
340
+
341
+ ```sh
342
+ capxul auth send --email "$TEST_EMAIL" --json
343
+ # Supply the delivered six-digit OTP through stdin, not a command argument.
344
+ capxul auth verify --email "$TEST_EMAIL" --otp-stdin --json
345
+ capxul auth profile --email "$TEST_EMAIL" --json
346
+ # A later process uses the same protected CLI home; no new OTP is required.
347
+ capxul auth profile --email "$TEST_EMAIL" --json
348
+ capxul auth status --email "$TEST_EMAIL" --json
349
+ capxul auth logout --email "$TEST_EMAIL" --json
350
+ ```
351
+
352
+ A machine run never prompts. `auth login` with `--otp-stdin` sends the OTP and
353
+ verifies one code from stdin. It returns the tagged result
354
+ `setupState: "setup-required"` when the account needs setup. It does not start
355
+ setup work, so sign-in never becomes signup.
356
+
357
+ A fresh machine signup does verification and setup in one command. State every
358
+ Profile field; a missing field refuses with exit 2:
359
+
360
+ ```sh
361
+ # Supply only the delivered OTP through stdin.
362
+ capxul auth signup --email "$TEST_EMAIL" --otp-stdin \
363
+ --display-name "Test Person" --country GH --handle test_person --json
364
+ ```
365
+
366
+ `--otp-stdin` reads up to 64 bytes, ending at EOF. Pipe from a secret provider or
367
+ 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
368
+ work. OTPs are never accepted in argv, persisted in the continuation, or included
369
+ in output.
370
+
371
+ The local page binds to an OS-selected loopback port. A random launch capability
372
+ is redeemed once and removed from the URL before Openfort starts. The page receives
373
+ only the authenticated wallet token and encryption session in memory. Closing it
374
+ ends the current wallet attempt. Re-run `auth signup` with the same CLI home to
375
+ resume the same Profile and Account without another OTP while the backend session
376
+ remains valid.
377
+
378
+ `auth signup` returns only two readiness results. `setupState: "ready"` is a
379
+ personal Account that the Account readiness owner reads as ready and deployed,
380
+ and its data carries the public identifiers: the Profile, the public wallet
381
+ address (`smartAccount.signerAddress`), the personal Smart Account address
382
+ (`smartAccount.smartAccountAddress`), the Account ID
383
+ (`lifecycle.accountId`), and the ready status. Any other state returns the typed
384
+ recoverable result `setupState: "setup-required"`, with the lifecycle, its
385
+ failure code, and the command that resumes the journey. A ready Account starts
386
+ no wallet work, so a retry keeps the one Profile, wallet, Smart Account, and
387
+ Account. An interruption keeps the stored session and stores no code, so the
388
+ next command resumes the same setup.
389
+
390
+ A ready personal Account continues the same journey into Organization creation
391
+ with `capxul org create --name NAME --handle HANDLE --country CC`. The create
392
+ command records the safe recovery handle for that Organization, so an
393
+ interruption or a restart resumes the exact same Organization.
394
+
395
+ The CLI checks `--display-name`, `--country`, and `--handle` before it opens a
396
+ client, so a malformed value refuses with exit 2 and no code is sent. The handle
397
+ check covers the grammar only: a handle that another person holds, and a reserved
398
+ handle, refuse through the identity owner after sign-in. Both refusals name the
399
+ handle, and neither offers verification-code recovery for a Profile problem.
400
+
401
+ The version 2 `first-party-session` record stores the opaque provider credential
402
+ in the existing protected plaintext store. Its scope includes the application
403
+ key, issuer, environment, and email. Each process validates restoration with the
404
+ backend; cached session data is not authority. Conditional replacement prevents
405
+ a concurrent logout from being undone by a delayed credential save. The backend
406
+ owns expiry and revocation. A failed remote logout is reported as `unconfirmed`;
407
+ local sign-out remains in effect.
408
+
409
+ `auth profile` returns a backend-read Profile and account lifecycle without opening
410
+ the browser. Successful email authentication can return `setupState: "setup-required"`.
411
+ `auth signup` returns `setupState: "ready"` only after Core reads a ready Account.
412
+ After logout, profile reads refuse with `NOT_AUTHENTICATED` and exit 3. An invalid
413
+ or expired OTP refuses with exit 2.
Binary file