primcli 1.75.0 → 3.0.0-preview.2
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 +124 -63
- package/bin/claude-pending-mail.mjs +1 -1
- package/bin/run.js +14 -0
- package/dist/{api-command-rlS3eTlZ.js → api-command-B37DPKFA.js} +752 -56
- package/dist/{cli-config-B80p0pLk.js → cli-config-Ms_0vNjq.js} +59 -16
- package/dist/{machine-session-Cq_nD5eF.js → machine-session-g-xPd3X0.js} +25 -12
- package/dist/{notify-session-CbY4FYUt.js → notify-session-Bo8WgG3q.js} +334 -4
- package/dist/oclif/auto-signal-worker.js +3 -3
- package/dist/oclif/auto-signals.js +2 -2
- package/dist/oclif/global-org-flag.js +76 -0
- package/dist/oclif/index.js +112344 -49050
- package/dist/oclif/receiver-heal.js +3 -3
- package/dist/oclif/root-signup-hint.js +2 -2
- package/dist/receiver.d.ts +95 -0
- package/dist/receiver.js +569 -0
- package/dist/{repeats-Dj_o6c-X-LyCkKYjp.js → repeats-Dj_o6c-X-BzXj5ICh.js} +4973 -2383
- package/dist/{scoped-chat-B0thCwME.js → scoped-chat-TJlyUTi7.js} +1 -1
- package/dist/{signal-command-DCPsJBdz.js → signal-command-BO93dM2Q.js} +9 -8
- package/man/primitive.1 +43 -0
- package/package.json +53 -8
package/README.md
CHANGED
|
@@ -150,9 +150,9 @@ enter it, wait for the email and take what you need from it:
|
|
|
150
150
|
```bash
|
|
151
151
|
# Sign up with your address, then:
|
|
152
152
|
primitive wait --from github.com --code # prints 482913
|
|
153
|
-
primitive wait --from
|
|
153
|
+
primitive wait --from accounts.example.com --link # prints the sign-in link
|
|
154
154
|
primitive wait --subject "verify" --timeout 90s
|
|
155
|
-
primitive wait --from
|
|
155
|
+
primitive wait --from billing.example.com --code --json
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
`primitive wait` waits for the next email to the identity the CLI resolves (a
|
|
@@ -224,8 +224,55 @@ Use `primitive signin <email> --accept-terms`, then `primitive signin confirm <e
|
|
|
224
224
|
|
|
225
225
|
Use `primitive logout --force` to remove local CLI credentials, pending email-code auth state, and stale credential locks without contacting Primitive. This is the recovery command when an interrupted auth command leaves the CLI saying another credential operation is already in progress.
|
|
226
226
|
|
|
227
|
+
Use `primitive org leave` to leave the organization you are signed in to. It asks for confirmation first; pass `--yes` to skip the prompt in scripts. Leaving is final. Only a person signed in with `primitive login` can leave; API keys are refused. The only owner of an organization cannot leave it until someone else is made an owner. If the server asks you to confirm in the dashboard, the command prints the confirmation page and, in a terminal, asks for the token it shows; in a script, pass the token with `--step-up-token`. Refusals from the server are printed as returned, and the command exits 1.
|
|
228
|
+
|
|
227
229
|
Use `primitive signup <email>` for new account creation, then `primitive signup confirm <email> <code>` with the emailed verification code. Non-interactive signup is available with `--accept-terms`.
|
|
228
230
|
|
|
231
|
+
### Switching organizations
|
|
232
|
+
|
|
233
|
+
Where the server supports it, browser login (`primitive login` with no email) logs you in as yourself rather than into one organization. The organization you choose in the browser becomes the current one, and you can switch without logging in again:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
primitive orgs # your organizations, your role, and whether the CLI may be used there
|
|
237
|
+
primitive org use acme # switch by name or id
|
|
238
|
+
primitive emails list --org beta # act in another organization for one command
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`--org` works on every command (set `PRIMITIVE_CLI_ORG` for the same effect; `primitive memories` keeps its own `--org` flag for organization-scoped memories). Each organization decides who may use the CLI there; when it refuses, `primitive orgs` and `primitive org use` show the reason (`not_a_member`, `cli_access_disabled_by_org`, `role_insufficient` or `cli_access_revoked`). The CLI keeps a short-lived access token per organization and gets a new one when it expires or is rejected. `primitive logout` signs you out of every organization.
|
|
242
|
+
|
|
243
|
+
Against a server without organization switching, and for logins saved by earlier CLI versions, the login stays bound to one organization and works as before. `--org` naming a different organization is refused for such a login, for an API key and for a connected agent profile.
|
|
244
|
+
|
|
245
|
+
`primitive login --permission <permission>` asks for a narrowed login. A server that does not offer narrowed CLI logins refuses it, and the CLI stops rather than logging in with a wider grant.
|
|
246
|
+
|
|
247
|
+
### Grants and keys
|
|
248
|
+
|
|
249
|
+
`primitive whoami` shows your organization, role and grant when the server reports them. `primitive whoami --check mail:send` asks whether the current credential may use a permission, prints the decision, and exits 1 when it may not; nothing is enforced by asking. A request refused because the credential's grant lacks a permission fails with `grant_forbidden`, and the CLI names the permission.
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
primitive keys list
|
|
253
|
+
primitive keys get <id> --json
|
|
254
|
+
primitive keys create --name orders --permission mail:read --rule <rule-id>
|
|
255
|
+
primitive keys update <id> --add mail:send,mail:reply
|
|
256
|
+
primitive keys rotate <id>
|
|
257
|
+
primitive keys revoke <id>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
`keys list` shows each key's id, prefix, name, permissions, the mail it reaches, expiry and last use, and `keys get` shows one key in full. Neither shows a secret. You see your own keys, and every key if your role may read them all.
|
|
261
|
+
|
|
262
|
+
`keys create` needs you to say what the key can do: a `--preset`, or `--permission` with the mail it reaches (`--address`, `--domain`, `--rule`, each repeatable, or `--all-mailboxes`). There is no default, and a super admin key needs `--preset super_admin`. A key never holds more than you do, and your organization's credential policy applies. `keys update` adds or removes permissions (`--add`, `--remove`) or replaces the key's email rules (`--rule`); the key keeps its id and secret. A request already held open with that key, such as a long poll or a wait, ends once when its permissions change.
|
|
263
|
+
|
|
264
|
+
Creating, rotating and changing a key need your approval in the browser. The command prints the approval link, opens it where it can, and waits for the one-time token the page shows. In a script, approve first and pass the token with `--step-up-token`. The new secret is printed once, alone on stdout (or in the JSON with `--json`), and the CLI never saves it, so store it right away. `keys revoke` asks for confirmation; pass `--yes` in scripts.
|
|
265
|
+
|
|
266
|
+
`primitive roles list`, `primitive roles groups`, `primitive roles mailbox-access` and `primitive roles access-mode` read roles, groups, mailbox access and the access mode. Where the server has not enabled them, they print `feature_disabled`. The built-in roles are `owner`, `admin`, `operator` and `member`. `operator` has every permission `admin` has except managing members, changing roles, sending invitations and billing. By default, owners, admins and operators may use the CLI. An organization can open it to every member by setting `cli_access` to `all_members` in its credential policy.
|
|
267
|
+
|
|
268
|
+
### Trying the preview
|
|
269
|
+
|
|
270
|
+
Install the 3.0.0 preview with `npm install -g primitive@preview`. `npm install -g primitive` stays on the latest stable version.
|
|
271
|
+
|
|
272
|
+
### Downgrading after signing in as yourself
|
|
273
|
+
|
|
274
|
+
A login as yourself is saved with `auth_method: "user_grant"`, which CLI 1.75.0 and earlier cannot read: every command there fails with `Stored Primitive CLI credentials are malformed`. Run `primitive logout` with this CLI before installing an older one. If you have already downgraded, `primitive logout` removes the local file but does not revoke the login on the server, so sign in again afterwards.
|
|
275
|
+
|
|
229
276
|
## Reply state and the agent loop
|
|
230
277
|
|
|
231
278
|
Every inbound email carries reply state: `awaiting` is `you` when the latest
|
|
@@ -315,6 +362,7 @@ primitive emails get --id <inbound-email-id> --compact
|
|
|
315
362
|
primitive sent list
|
|
316
363
|
primitive sent delete --id <sent-email-id>
|
|
317
364
|
primitive domains list
|
|
365
|
+
primitive billing
|
|
318
366
|
primitive functions templates
|
|
319
367
|
primitive functions init my-fn --template email-reply
|
|
320
368
|
primitive functions logs --id <function-id>
|
|
@@ -427,8 +475,8 @@ signal or interaction, so two agents cannot keep acknowledging each other.
|
|
|
427
475
|
acknowledgement for a message identified by its Message-Id.
|
|
428
476
|
|
|
429
477
|
A plain `primitive reply` to an email the server classifies as an interaction
|
|
430
|
-
the reply does not complete (a
|
|
431
|
-
|
|
478
|
+
the reply does not complete (a contact request, or a kind this CLI cannot
|
|
479
|
+
answer), or to informational mail or a status signal, is still sent.
|
|
432
480
|
Without `--json` one `Warning:` line on stderr names the command that answers
|
|
433
481
|
it; with `--json` the envelope carries `interaction_warning` (`code`,
|
|
434
482
|
`email_id`, `kind`, `category`, `message`, `expected`), null otherwise.
|
|
@@ -629,65 +677,79 @@ primitive routes remove <route-id>
|
|
|
629
677
|
Recipient routing is gated by an organization entitlement; routes are inert
|
|
630
678
|
until it is enabled.
|
|
631
679
|
|
|
632
|
-
##
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
--to payer@their-domain.example --amount 10000 --network base-sepolia
|
|
658
|
-
# Payer (recommended): pay the email challenge in one step. Signs the challenge
|
|
659
|
-
# locally with your wallet key AND sends the signed interaction.json, so you
|
|
660
|
-
# skip the manual sign-then-send dance. --in-reply-to is the inbound challenge
|
|
661
|
-
# email you received; it is fetched to address the payment to the payee, with
|
|
662
|
-
# From defaulting to the payer it was sent to. The send is not threaded under
|
|
663
|
-
# the challenge (the payment associates by interaction_id). The message carries
|
|
664
|
-
# a short default note alongside the attachment; pass --body to customize it.
|
|
665
|
-
primitive payments pay-email --challenge-file challenge.json \
|
|
666
|
-
--in-reply-to <inbound-challenge-email-id> --wait
|
|
667
|
-
|
|
668
|
-
# Advanced: sign only, without sending. Emits the portable interaction.json
|
|
669
|
-
# artifact you can attach yourself (e.g. with `primitive send --attachment`).
|
|
670
|
-
primitive payments pay-email-step --challenge-file challenge.json > interaction.json
|
|
671
|
-
|
|
672
|
-
# Inspect a challenge by id, or list your registered payout addresses.
|
|
673
|
-
primitive payments get-challenge --id <challenge-id>
|
|
674
|
-
primitive payments list-payout-addresses
|
|
675
|
-
|
|
676
|
-
# Read and update the org spend policy (kill-switch, per-payment and daily caps,
|
|
677
|
-
# payee allowlist). The update merges: omitted fields keep their value.
|
|
678
|
-
primitive payments get-spend-policy
|
|
679
|
-
primitive payments update-spend-policy --max-per-payment 5000000
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
`charge` is the friendly verb that matches the SDK `charge` and accepts `--amount-usdc`; `create-challenge` is the lower-level command that takes base-unit `--amount`. Either creates a challenge. All four signing commands accept `--json`. `register-payout-address` and `pay` print a human-readable summary by default and raw JSON with `--json`; `pay-email` and `pay-email-step` print JSON by default (the send result and the `interaction.json` bytes, respectively), and `--json` switches them to a fuller envelope object.
|
|
683
|
-
|
|
684
|
-
The signing commands (`register-payout-address`, `pay`, `pay-email`, and `pay-email-step`) need your wallet key. `pay-email` is the recommended payer path for the email-native flow: it signs and sends in one step. `pay-email-step` signs only and emits the `interaction.json` artifact for advanced use where you want to deliver it yourself. Set the key in `PRIMITIVE_X402_PRIVATE_KEY` (a `0x`-prefixed hex private key) so it never lands in shell history or the process list:
|
|
685
|
-
|
|
686
|
-
```bash
|
|
687
|
-
export PRIMITIVE_X402_PRIVATE_KEY=0x...
|
|
680
|
+
## Supported receiver API
|
|
681
|
+
|
|
682
|
+
A receiver in a coding harness that the CLI does not support must decide
|
|
683
|
+
which received emails may wake its session. `primitive/receiver` exposes the
|
|
684
|
+
CLI's own contact admission policy for that decision. It is the only
|
|
685
|
+
supported library import from this package.
|
|
686
|
+
|
|
687
|
+
```ts
|
|
688
|
+
import { createWakePolicy, WakePolicyError } from "primitive/receiver";
|
|
689
|
+
|
|
690
|
+
const policy = createWakePolicy({
|
|
691
|
+
recipient: "agent@example.com",
|
|
692
|
+
contactRequests: false,
|
|
693
|
+
readContactPolicy: (signal) => readPolicy(signal),
|
|
694
|
+
readContactPage: (cursor, signal) => readContacts(cursor, signal),
|
|
695
|
+
readNetworkAdmission: (emailId, sender, signal) =>
|
|
696
|
+
checkNetworkAdmission(emailId, sender, signal),
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
const decision = await policy.admit({ emailId, sender, receivedAt }, signal);
|
|
700
|
+
if (decision.kind !== "denied") {
|
|
701
|
+
const confirm = await policy.recheck(decision, signal);
|
|
702
|
+
confirm(); // immediately before the wake
|
|
703
|
+
wake(decision.kind === "request" ? "contact request" : "mail");
|
|
704
|
+
}
|
|
688
705
|
```
|
|
689
706
|
|
|
690
|
-
|
|
707
|
+
The three readers call the API as the connected address. Each returns the
|
|
708
|
+
`data` of one operation:
|
|
709
|
+
|
|
710
|
+
| Reader | API operation | CLI command |
|
|
711
|
+
| --- | --- | --- |
|
|
712
|
+
| `readContactPolicy` | `getAgentContactPolicy` | `primitive contacts get-agent-contact-policy` |
|
|
713
|
+
| `readContactPage` | `listAgentContacts`, 100 rows per page, with the next `cursor` or `null` | `primitive agent contacts list` |
|
|
714
|
+
| `readNetworkAdmission` | `checkDefaultNetworkContactAdmission` | `primitive agent-networks check-default-network-contact-admission` |
|
|
715
|
+
|
|
716
|
+
`admit` returns one of three decisions for an email:
|
|
717
|
+
|
|
718
|
+
- `allowed`: the email may wake the session.
|
|
719
|
+
- `request`: a first-contact request from an unknown sender. It is returned
|
|
720
|
+
only when `contactRequests` is true and the owner allows contact requests.
|
|
721
|
+
Show it as a request, never as a task. Allow at most one per sender.
|
|
722
|
+
- `denied`: the email must not wake the session.
|
|
723
|
+
|
|
724
|
+
An admission carries the email ID, sender and received time, normalized to
|
|
725
|
+
lower case and ISO 8601. Pass the sender only after you check that it is
|
|
726
|
+
authenticated.
|
|
727
|
+
|
|
728
|
+
Permission can change between admission and wake. Call `recheck` with the
|
|
729
|
+
admission that `admit` returned, then call the function it returns
|
|
730
|
+
synchronously right before the wake. That function throws when the
|
|
731
|
+
permission was refreshed since, or when the policy read behind it is older
|
|
732
|
+
than 30 seconds. Admissions are bound to the policy that issued them, so
|
|
733
|
+
`recheck` rejects any other object with a `TypeError`.
|
|
734
|
+
|
|
735
|
+
Failures are `WakePolicyError` values. Branch on `code`:
|
|
736
|
+
|
|
737
|
+
| `code` | `retryable` | Meaning |
|
|
738
|
+
| --- | --- | --- |
|
|
739
|
+
| `policy_read_failed` | true | A policy read failed temporarily. The policy paces repeated reads itself. |
|
|
740
|
+
| `network_admission_pending` | true | The server has not settled network admission for this email. Retry in a few seconds. |
|
|
741
|
+
| `permission_changed` | true | Permission changed or expired before the wake. Admit the email again. |
|
|
742
|
+
| `policy_unavailable` | false | The policy is invalid or access was lost. Retrying will not help until access is restored. |
|
|
743
|
+
|
|
744
|
+
A reader reports a temporary failure, such as a rate limit, a 5xx response
|
|
745
|
+
or a dropped connection, by throwing `new WakePolicyError("policy_read_failed")`.
|
|
746
|
+
Any other error from a reader makes the policy unavailable. An aborted signal
|
|
747
|
+
rejects with the signal's reason.
|
|
748
|
+
|
|
749
|
+
The package also resolves `primitive/bin/*`, `primitive/dist/*` and
|
|
750
|
+
`primitive/man/*` so existing deep imports keep working. Those paths are not
|
|
751
|
+
supported. File names under `dist/` other than `dist/receiver.js` change
|
|
752
|
+
between releases, and the paths may stop resolving in a later major version.
|
|
691
753
|
|
|
692
754
|
## Migrating from `@primitivedotdev/sdk` CLI
|
|
693
755
|
|
|
@@ -1357,7 +1419,6 @@ untrusted content names the command:
|
|
|
1357
1419
|
|
|
1358
1420
|
| Server classification | `how to answer` |
|
|
1359
1421
|
|---|---|
|
|
1360
|
-
| `x402.payment/1` | `primitive payments challenge-from-email --id <id>` to review, `primitive payments pay-email --in-reply-to <id>` to pay |
|
|
1361
1422
|
| `primitive.contact/1` | `primitive contacts accept --id <id>` |
|
|
1362
1423
|
| `repeat.tick/1` | reply if needed; `primitive repeat stop --id <id>` once its goal is met |
|
|
1363
1424
|
| `repeat.stop/1`, status signals, `fyi` | no reply needed |
|
|
@@ -1370,7 +1431,7 @@ When the server classifies the email as an interaction, its `interaction.json`
|
|
|
1370
1431
|
part is left out of the attachment list. `primitive inbox next` carries the
|
|
1371
1432
|
same `interaction` and `next_actions` in its JSON envelope, prints the same
|
|
1372
1433
|
line above the conversation, and ends with matching instructions: the answering
|
|
1373
|
-
command first for
|
|
1434
|
+
command first for contact requests (a reply only as optional), "No
|
|
1374
1435
|
reply needed." for fyi mail, signals and repeat-stopped notices, and the stop
|
|
1375
1436
|
command for a repeat only when the recipient may stop it. `inbox next` skips
|
|
1376
1437
|
mail that needs no reply (fyi mail, status signals, repeat-stopped notices):
|
|
@@ -200,7 +200,7 @@ export const WAKE_SENTENCES = {
|
|
|
200
200
|
export function wakeSentence(label) {
|
|
201
201
|
if (label === "fyi" || label === "repeat.stop/1")
|
|
202
202
|
return WAKE_SENTENCES.no_reply;
|
|
203
|
-
if (label === "
|
|
203
|
+
if (label === "primitive.contact/1")
|
|
204
204
|
return WAKE_SENTENCES.answer_with_command;
|
|
205
205
|
if (label === "repeat.tick/1") return WAKE_SENTENCES.repeat;
|
|
206
206
|
return WAKE_SENTENCES.unsupported;
|
package/bin/run.js
CHANGED
|
@@ -72,5 +72,19 @@ const { writeRootAuthContextIfNeeded } = await import(
|
|
|
72
72
|
);
|
|
73
73
|
await writeRootAuthContextIfNeeded();
|
|
74
74
|
|
|
75
|
+
// `--org <org>` works on every command: take it out of argv before oclif
|
|
76
|
+
// parses the command, and pass it on as PRIMITIVE_CLI_ORG. Applied last, so
|
|
77
|
+
// the detached receiver and signal workers started above never inherit it.
|
|
78
|
+
{
|
|
79
|
+
const { applyGlobalOrgFlag } = await import(
|
|
80
|
+
"../dist/oclif/global-org-flag.js"
|
|
81
|
+
);
|
|
82
|
+
const orgFlagError = applyGlobalOrgFlag();
|
|
83
|
+
if (orgFlagError) {
|
|
84
|
+
process.stderr.write(`${orgFlagError}\n`);
|
|
85
|
+
process.exit(2);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
75
89
|
const { execute } = await import("@oclif/core");
|
|
76
90
|
await execute({ dir: import.meta.url });
|