primcli 1.27.0 → 1.29.0
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 +32 -0
- package/dist/oclif/index.js +792 -39
- package/man/primitive.1 +6 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -206,6 +206,24 @@ primitive memories get state --function <function-id>
|
|
|
206
206
|
Values must be valid JSON. Strings must be quoted as JSON strings, so use
|
|
207
207
|
`'"hello"'`, not `hello`.
|
|
208
208
|
|
|
209
|
+
## Credits
|
|
210
|
+
|
|
211
|
+
Redeem a credit code for your organization and check the credit balance.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
primitive credits redeem LAUNCH50
|
|
215
|
+
primitive credits balance
|
|
216
|
+
primitive credits balance --json
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
`credits redeem` prints the credit added and its expiry. On a refusal it prints
|
|
220
|
+
the server's message (for example an invalid or already redeemed code) and
|
|
221
|
+
exits non-zero. Redeeming needs an organization owner or admin; with an API key,
|
|
222
|
+
the key's creator must currently be an owner or admin. Each run sends a new
|
|
223
|
+
Idempotency-Key; pass `--idempotency-key <key>` to retry the same redemption
|
|
224
|
+
safely. On any failure the command prints the key it used to stderr, so you can
|
|
225
|
+
run the same command again with `--idempotency-key <key>`.
|
|
226
|
+
|
|
209
227
|
## Recipient routing
|
|
210
228
|
|
|
211
229
|
Bind a recipient address to a destination so inbound mail resolves to a single
|
|
@@ -317,3 +335,17 @@ timeout; Ctrl-C exits 130. Bare `primitive listen` prints one raw JSON event per
|
|
|
317
335
|
line. Use `--transport poll` explicitly for HTTP polling. Accept or enqueue each
|
|
318
336
|
event within 30 seconds. Closing preserves pending work, and retries can deliver
|
|
319
337
|
an event more than once.
|
|
338
|
+
|
|
339
|
+
### Connected-agent listeners
|
|
340
|
+
|
|
341
|
+
Use the agent's existing connected-address credential with the same listener API.
|
|
342
|
+
The server automatically restricts its private subscription to inbound email for
|
|
343
|
+
that address. No owner credential, recipient filter, or relay is required. Names
|
|
344
|
+
are isolated per credential. Revoking or replacing the credential removes its
|
|
345
|
+
subscriptions and queued deliveries; reconnect with the new credential to start
|
|
346
|
+
receiving new events.
|
|
347
|
+
|
|
348
|
+
Address-scoped events contain parsed message content in `email.parsed`.
|
|
349
|
+
`email.content.raw` and `email.content.download` are null. Signed download links,
|
|
350
|
+
account routing metadata, and other SMTP envelope recipients are not exposed.
|
|
351
|
+
Attachments can be fetched through the authenticated email attachment API.
|
package/dist/oclif/index.js
CHANGED
|
@@ -71,10 +71,11 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
71
71
|
downloadEmailAttachmentPart: () => downloadEmailAttachmentPart,
|
|
72
72
|
downloadRawEmail: () => downloadRawEmail,
|
|
73
73
|
downloadSentAttachmentPart: () => downloadSentAttachmentPart,
|
|
74
|
-
getAccount: () => getAccount
|
|
74
|
+
getAccount: () => getAccount,
|
|
75
75
|
getAgent: () => getAgent,
|
|
76
76
|
getChallenge: () => getChallenge,
|
|
77
77
|
getConversation: () => getConversation,
|
|
78
|
+
getCreditBalance: () => getCreditBalance,
|
|
78
79
|
getEmail: () => getEmail,
|
|
79
80
|
getFunction: () => getFunction,
|
|
80
81
|
getFunctionRouting: () => getFunctionRouting,
|
|
@@ -118,6 +119,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
118
119
|
pollCliLogin: () => pollCliLogin,
|
|
119
120
|
publishAgent: () => publishAgent,
|
|
120
121
|
pullWebhookEvent: () => pullWebhookEvent$1,
|
|
122
|
+
redeemCreditCode: () => redeemCreditCode,
|
|
121
123
|
registerPayoutAddress: () => registerPayoutAddress,
|
|
122
124
|
removeAgentConnection: () => removeAgentConnection,
|
|
123
125
|
reorderRoutes: () => reorderRoutes,
|
|
@@ -411,7 +413,7 @@ const cliLogout = (options) => (options?.client ?? client).post({
|
|
|
411
413
|
/**
|
|
412
414
|
* Get account info
|
|
413
415
|
*/
|
|
414
|
-
const getAccount
|
|
416
|
+
const getAccount = (options) => (options?.client ?? client).get({
|
|
415
417
|
security: [{
|
|
416
418
|
scheme: "bearer",
|
|
417
419
|
type: "http"
|
|
@@ -493,6 +495,55 @@ const rotateWebhookSecret = (options) => (options?.client ?? client).post({
|
|
|
493
495
|
...options
|
|
494
496
|
});
|
|
495
497
|
/**
|
|
498
|
+
* Redeem a credit code
|
|
499
|
+
*
|
|
500
|
+
* Redeem a credit code for the authenticated organization. The credit is
|
|
501
|
+
* added to the organization prepaid credit and shows in
|
|
502
|
+
* `GET /credits/balance` under `prepaid_credit`.
|
|
503
|
+
*
|
|
504
|
+
* Redeeming requires an organization owner or admin. An API key redeems
|
|
505
|
+
* with the authority of the user who created it, based on that user's
|
|
506
|
+
* current role; a key without that authority gets the same
|
|
507
|
+
* `credit_code_invalid` refusal as an unknown code.
|
|
508
|
+
*
|
|
509
|
+
* The `Idempotency-Key` header is required. Retrying with the same key
|
|
510
|
+
* and code returns the original grant with `replayed: true` and grants
|
|
511
|
+
* nothing new; the same key with a different code is refused with
|
|
512
|
+
* `idempotency_key_reused`. Every refusal carries a human-readable
|
|
513
|
+
* `error.message` that can be shown to the user as is.
|
|
514
|
+
*
|
|
515
|
+
*/
|
|
516
|
+
const redeemCreditCode = (options) => (options.client ?? client).post({
|
|
517
|
+
security: [{
|
|
518
|
+
scheme: "bearer",
|
|
519
|
+
type: "http"
|
|
520
|
+
}],
|
|
521
|
+
url: "/credits/redeem",
|
|
522
|
+
...options,
|
|
523
|
+
headers: {
|
|
524
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
525
|
+
...options.headers
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
/**
|
|
529
|
+
* Get credit balance
|
|
530
|
+
*
|
|
531
|
+
* Read the organization credit position. `prepaid_credit` is the prepaid
|
|
532
|
+
* usage credit (paid top-ups, redeemed credit codes and granted credit)
|
|
533
|
+
* that can still pay for usage. `budget` is the active agent spending
|
|
534
|
+
* budget an operator funded for top-ups, or null when there is none.
|
|
535
|
+
* Amounts are strings of integer micros (1 USD = 1000000 micros).
|
|
536
|
+
*
|
|
537
|
+
*/
|
|
538
|
+
const getCreditBalance = (options) => (options?.client ?? client).get({
|
|
539
|
+
security: [{
|
|
540
|
+
scheme: "bearer",
|
|
541
|
+
type: "http"
|
|
542
|
+
}],
|
|
543
|
+
url: "/credits/balance",
|
|
544
|
+
...options
|
|
545
|
+
});
|
|
546
|
+
/**
|
|
496
547
|
* List all domains
|
|
497
548
|
*
|
|
498
549
|
* Returns all verified and unverified domains for your organization,
|
|
@@ -969,7 +1020,7 @@ const listEndpoints = (options) => (options?.client ?? client).get({
|
|
|
969
1020
|
* accepts the signature.
|
|
970
1021
|
*
|
|
971
1022
|
*
|
|
972
|
-
* For local receiving, use kind=pull and a stable name, without url, function_id or domain_id. Named creation resumes the same active destination; omitted filters preserve its selection, while conflicting supplied configuration returns 409. New unfiltered destinations receive all subsequently ready eligible event types. Pull destinations do not occupy HTTP routing slots. Signing-secret setup and a test HTTP delivery are not required for pull receiving.
|
|
1023
|
+
* For local receiving, use kind=pull and a stable name, without url, function_id or domain_id. Named creation resumes the same active destination; omitted filters preserve its selection, while conflicting supplied configuration returns 409. New unfiltered destinations receive all subsequently ready eligible event types within their credential scope. Connected-agent credentials automatically create private subscriptions restricted to inbound email for their assigned address. Names are isolated per credential; revocation or replacement deletes its subscriptions. Address-scoped events contain parsed mail without raw MIME, signed download links, routing metadata, or sibling envelope recipients. Pull destinations do not occupy HTTP routing slots. Signing-secret setup and a test HTTP delivery are not required for pull receiving.
|
|
973
1024
|
*/
|
|
974
1025
|
const createEndpoint = (options) => (options.client ?? client).post({
|
|
975
1026
|
security: [{
|
|
@@ -2783,6 +2834,10 @@ const openapiDocument = {
|
|
|
2783
2834
|
"name": "Account",
|
|
2784
2835
|
"description": "Manage your account settings, storage, and webhook secret"
|
|
2785
2836
|
},
|
|
2837
|
+
{
|
|
2838
|
+
"name": "Credits",
|
|
2839
|
+
"description": "Redeem credit codes and read the organization credit balance"
|
|
2840
|
+
},
|
|
2786
2841
|
{
|
|
2787
2842
|
"name": "Domains",
|
|
2788
2843
|
"description": "Claim, verify, and manage email domains"
|
|
@@ -3387,6 +3442,129 @@ const openapiDocument = {
|
|
|
3387
3442
|
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
3388
3443
|
}
|
|
3389
3444
|
} },
|
|
3445
|
+
"/credits/redeem": { "post": {
|
|
3446
|
+
"operationId": "redeemCreditCode",
|
|
3447
|
+
"summary": "Redeem a credit code",
|
|
3448
|
+
"description": "Redeem a credit code for the authenticated organization. The credit is\nadded to the organization prepaid credit and shows in\n`GET /credits/balance` under `prepaid_credit`.\n\nRedeeming requires an organization owner or admin. An API key redeems\nwith the authority of the user who created it, based on that user's\ncurrent role; a key without that authority gets the same\n`credit_code_invalid` refusal as an unknown code.\n\nThe `Idempotency-Key` header is required. Retrying with the same key\nand code returns the original grant with `replayed: true` and grants\nnothing new; the same key with a different code is refused with\n`idempotency_key_reused`. Every refusal carries a human-readable\n`error.message` that can be shown to the user as is.\n",
|
|
3449
|
+
"tags": ["Credits"],
|
|
3450
|
+
"parameters": [{
|
|
3451
|
+
"name": "Idempotency-Key",
|
|
3452
|
+
"in": "header",
|
|
3453
|
+
"required": true,
|
|
3454
|
+
"description": "Required client-supplied key: 1 to 200 printable ASCII\ncharacters, without spaces. Use a new key for each code you\nredeem and reuse it only to retry the same request.\n",
|
|
3455
|
+
"schema": {
|
|
3456
|
+
"type": "string",
|
|
3457
|
+
"minLength": 1,
|
|
3458
|
+
"maxLength": 200,
|
|
3459
|
+
"pattern": "^[\\x21-\\x7E]{1,200}$"
|
|
3460
|
+
}
|
|
3461
|
+
}],
|
|
3462
|
+
"requestBody": {
|
|
3463
|
+
"required": true,
|
|
3464
|
+
"content": { "application/json": {
|
|
3465
|
+
"schema": { "$ref": "#/components/schemas/RedeemCreditCodeInput" },
|
|
3466
|
+
"example": { "code": "LAUNCH50" }
|
|
3467
|
+
} }
|
|
3468
|
+
},
|
|
3469
|
+
"responses": {
|
|
3470
|
+
"200": {
|
|
3471
|
+
"description": "The code was redeemed, or this request replayed an earlier\nredemption with the same Idempotency-Key.\n",
|
|
3472
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
3473
|
+
"type": "object",
|
|
3474
|
+
"properties": { "data": { "$ref": "#/components/schemas/CreditRedemption" } }
|
|
3475
|
+
}] } } }
|
|
3476
|
+
},
|
|
3477
|
+
"400": {
|
|
3478
|
+
"description": "`idempotency_key_required` means the Idempotency-Key header is\nmissing. `validation_error` means the header is empty, longer than\n200 characters or not printable non-space ASCII, or the body is\ninvalid.\n",
|
|
3479
|
+
"content": { "application/json": {
|
|
3480
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
3481
|
+
"example": {
|
|
3482
|
+
"success": false,
|
|
3483
|
+
"error": {
|
|
3484
|
+
"code": "idempotency_key_required",
|
|
3485
|
+
"message": "an Idempotency-Key header is required"
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
} }
|
|
3489
|
+
},
|
|
3490
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3491
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
3492
|
+
"409": {
|
|
3493
|
+
"description": "The organization or user already redeemed this code, or already\nredeemed another code in the same exclusive group (a set of codes\nof which each organization or user may redeem only one).\n`error.code` is `credit_code_already_redeemed`.\n",
|
|
3494
|
+
"content": { "application/json": {
|
|
3495
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
3496
|
+
"example": {
|
|
3497
|
+
"success": false,
|
|
3498
|
+
"error": {
|
|
3499
|
+
"code": "credit_code_already_redeemed",
|
|
3500
|
+
"message": "Your organization has already redeemed this promotion."
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
} }
|
|
3504
|
+
},
|
|
3505
|
+
"422": {
|
|
3506
|
+
"description": "The code cannot be redeemed. `credit_code_invalid` covers unknown,\nexpired and exhausted codes and callers without authority to\nredeem; `credit_code_not_eligible` means the organization does not\nmeet the promotion terms; `credit_code_balance_cap` means the\norganization already holds the most promotional credit it can;\n`idempotency_key_reused` means this Idempotency-Key already\nredeemed a different code.\n",
|
|
3507
|
+
"content": { "application/json": {
|
|
3508
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
3509
|
+
"example": {
|
|
3510
|
+
"success": false,
|
|
3511
|
+
"error": {
|
|
3512
|
+
"code": "credit_code_invalid",
|
|
3513
|
+
"message": "That code isn't valid or is no longer available."
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
} }
|
|
3517
|
+
},
|
|
3518
|
+
"429": {
|
|
3519
|
+
"description": "Too many requests or redemption attempts. `error.code` is\n`rate_limited`. Wait for `Retry-After` when present. Like other\nrate-limited responses, it also carries the `ratelimit-limit`,\n`ratelimit-remaining`, `ratelimit-reset` and `ratelimit-policy`\nquota headers when the limiter reports them.\n",
|
|
3520
|
+
"headers": { "Retry-After": {
|
|
3521
|
+
"schema": { "type": "integer" },
|
|
3522
|
+
"description": "Seconds to wait before retrying"
|
|
3523
|
+
} },
|
|
3524
|
+
"content": { "application/json": {
|
|
3525
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
3526
|
+
"example": {
|
|
3527
|
+
"success": false,
|
|
3528
|
+
"error": {
|
|
3529
|
+
"code": "rate_limited",
|
|
3530
|
+
"message": "Rate limit exceeded. Try again shortly."
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
} }
|
|
3534
|
+
},
|
|
3535
|
+
"503": {
|
|
3536
|
+
"description": "The code could not be redeemed right now. Retry later with the\nsame Idempotency-Key. `error.code` is `service_unavailable`.\n",
|
|
3537
|
+
"content": { "application/json": {
|
|
3538
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
3539
|
+
"example": {
|
|
3540
|
+
"success": false,
|
|
3541
|
+
"error": {
|
|
3542
|
+
"code": "service_unavailable",
|
|
3543
|
+
"message": "We couldn't redeem that code right now. Try again in a few minutes."
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
} }
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
} },
|
|
3550
|
+
"/credits/balance": { "get": {
|
|
3551
|
+
"operationId": "getCreditBalance",
|
|
3552
|
+
"summary": "Get credit balance",
|
|
3553
|
+
"description": "Read the organization credit position. `prepaid_credit` is the prepaid\nusage credit (paid top-ups, redeemed credit codes and granted credit)\nthat can still pay for usage. `budget` is the active agent spending\nbudget an operator funded for top-ups, or null when there is none.\nAmounts are strings of integer micros (1 USD = 1000000 micros).\n",
|
|
3554
|
+
"tags": ["Credits"],
|
|
3555
|
+
"responses": {
|
|
3556
|
+
"200": {
|
|
3557
|
+
"description": "The organization credit balance.",
|
|
3558
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
3559
|
+
"type": "object",
|
|
3560
|
+
"properties": { "data": { "$ref": "#/components/schemas/CreditBalance" } }
|
|
3561
|
+
}] } } }
|
|
3562
|
+
},
|
|
3563
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3564
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
3565
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
3566
|
+
}
|
|
3567
|
+
} },
|
|
3390
3568
|
"/domains": {
|
|
3391
3569
|
"get": {
|
|
3392
3570
|
"operationId": "listDomains",
|
|
@@ -4221,7 +4399,7 @@ const openapiDocument = {
|
|
|
4221
4399
|
"post": {
|
|
4222
4400
|
"operationId": "createEndpoint",
|
|
4223
4401
|
"summary": "Create a webhook endpoint",
|
|
4224
|
-
"description": "Creates a new webhook endpoint. If a deactivated endpoint\nwith the same URL and domain exists, it is reactivated\ninstead. Subject to plan limits on the number of active\nendpoints.\n\n**Signing is account-scoped, not per-endpoint.** This call\ndoes not return any signing material; every endpoint on the\naccount uses the same webhook secret, fetched via\n`GET /account/webhook-secret`. See the API-level \"Webhook\nsigning\" section for the full wire format (header name,\nsigned string, hash algo, secret format, tolerance) and a\nlanguage-agnostic verification recipe.\n\nAfter creating the endpoint, fire a test delivery against\nit via `POST /endpoints/{id}/test` to confirm your verifier\naccepts the signature.\n\n\nFor local receiving, use kind=pull and a stable name, without url, function_id or domain_id. Named creation resumes the same active destination; omitted filters preserve its selection, while conflicting supplied configuration returns 409. New unfiltered destinations receive all subsequently ready eligible event types. Pull destinations do not occupy HTTP routing slots. Signing-secret setup and a test HTTP delivery are not required for pull receiving.",
|
|
4402
|
+
"description": "Creates a new webhook endpoint. If a deactivated endpoint\nwith the same URL and domain exists, it is reactivated\ninstead. Subject to plan limits on the number of active\nendpoints.\n\n**Signing is account-scoped, not per-endpoint.** This call\ndoes not return any signing material; every endpoint on the\naccount uses the same webhook secret, fetched via\n`GET /account/webhook-secret`. See the API-level \"Webhook\nsigning\" section for the full wire format (header name,\nsigned string, hash algo, secret format, tolerance) and a\nlanguage-agnostic verification recipe.\n\nAfter creating the endpoint, fire a test delivery against\nit via `POST /endpoints/{id}/test` to confirm your verifier\naccepts the signature.\n\n\nFor local receiving, use kind=pull and a stable name, without url, function_id or domain_id. Named creation resumes the same active destination; omitted filters preserve its selection, while conflicting supplied configuration returns 409. New unfiltered destinations receive all subsequently ready eligible event types within their credential scope. Connected-agent credentials automatically create private subscriptions restricted to inbound email for their assigned address. Names are isolated per credential; revocation or replacement deletes its subscriptions. Address-scoped events contain parsed mail without raw MIME, signed download links, routing metadata, or sibling envelope recipients. Pull destinations do not occupy HTTP routing slots. Signing-secret setup and a test HTTP delivery are not required for pull receiving.",
|
|
4225
4403
|
"tags": ["Endpoints"],
|
|
4226
4404
|
"requestBody": {
|
|
4227
4405
|
"required": true,
|
|
@@ -8075,7 +8253,15 @@ const openapiDocument = {
|
|
|
8075
8253
|
"event_content_unavailable",
|
|
8076
8254
|
"event_preparation_failed",
|
|
8077
8255
|
"subscription_unavailable",
|
|
8078
|
-
"stale_delivery"
|
|
8256
|
+
"stale_delivery",
|
|
8257
|
+
"idempotency_key_required",
|
|
8258
|
+
"idempotency_key_reused",
|
|
8259
|
+
"credit_code_invalid",
|
|
8260
|
+
"credit_code_already_redeemed",
|
|
8261
|
+
"credit_code_not_eligible",
|
|
8262
|
+
"credit_code_balance_cap",
|
|
8263
|
+
"rate_limited",
|
|
8264
|
+
"service_unavailable"
|
|
8079
8265
|
]
|
|
8080
8266
|
},
|
|
8081
8267
|
"message": { "type": "string" },
|
|
@@ -9041,6 +9227,148 @@ const openapiDocument = {
|
|
|
9041
9227
|
} },
|
|
9042
9228
|
"required": ["secret"]
|
|
9043
9229
|
},
|
|
9230
|
+
"RedeemCreditCodeInput": {
|
|
9231
|
+
"type": "object",
|
|
9232
|
+
"additionalProperties": false,
|
|
9233
|
+
"required": ["code"],
|
|
9234
|
+
"properties": { "code": {
|
|
9235
|
+
"type": "string",
|
|
9236
|
+
"pattern": "^\\s*\\S(?:[\\s\\S]{0,254}\\S)?\\s*$",
|
|
9237
|
+
"description": "The credit code to redeem. Surrounding whitespace is ignored; the\nrest must be 1 to 256 characters.\n"
|
|
9238
|
+
} }
|
|
9239
|
+
},
|
|
9240
|
+
"CreditRedemption": {
|
|
9241
|
+
"type": "object",
|
|
9242
|
+
"additionalProperties": false,
|
|
9243
|
+
"required": [
|
|
9244
|
+
"redemption_id",
|
|
9245
|
+
"amount_micros",
|
|
9246
|
+
"currency",
|
|
9247
|
+
"granted_at",
|
|
9248
|
+
"expires_at",
|
|
9249
|
+
"label",
|
|
9250
|
+
"replayed",
|
|
9251
|
+
"message"
|
|
9252
|
+
],
|
|
9253
|
+
"properties": {
|
|
9254
|
+
"redemption_id": {
|
|
9255
|
+
"type": "string",
|
|
9256
|
+
"description": "Identifier of the redemption."
|
|
9257
|
+
},
|
|
9258
|
+
"amount_micros": {
|
|
9259
|
+
"type": "string",
|
|
9260
|
+
"pattern": "^[0-9]+$",
|
|
9261
|
+
"description": "Credit granted, in micros of `currency` (1 USD = 1000000 micros)."
|
|
9262
|
+
},
|
|
9263
|
+
"currency": {
|
|
9264
|
+
"type": "string",
|
|
9265
|
+
"description": "Currency of the granted credit, for example `usd`."
|
|
9266
|
+
},
|
|
9267
|
+
"granted_at": {
|
|
9268
|
+
"type": "string",
|
|
9269
|
+
"format": "date-time",
|
|
9270
|
+
"description": "When the credit was granted."
|
|
9271
|
+
},
|
|
9272
|
+
"expires_at": {
|
|
9273
|
+
"type": ["string", "null"],
|
|
9274
|
+
"format": "date-time",
|
|
9275
|
+
"description": "When the granted credit expires, or null when it does not expire."
|
|
9276
|
+
},
|
|
9277
|
+
"label": {
|
|
9278
|
+
"type": ["string", "null"],
|
|
9279
|
+
"description": "Customer-facing label of the promotion, when it has one."
|
|
9280
|
+
},
|
|
9281
|
+
"replayed": {
|
|
9282
|
+
"type": "boolean",
|
|
9283
|
+
"description": "True when this Idempotency-Key already redeemed this code and the\noriginal grant is returned. No second grant is made.\n"
|
|
9284
|
+
},
|
|
9285
|
+
"message": {
|
|
9286
|
+
"type": "string",
|
|
9287
|
+
"description": "Human-readable summary of the grant, suitable to show as is."
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
},
|
|
9291
|
+
"CreditBalance": {
|
|
9292
|
+
"type": "object",
|
|
9293
|
+
"required": ["budget"],
|
|
9294
|
+
"properties": {
|
|
9295
|
+
"budget": {
|
|
9296
|
+
"description": "The active agent spending budget an operator funded for top-ups,\nor null when there is none.\n",
|
|
9297
|
+
"type": ["object", "null"],
|
|
9298
|
+
"additionalProperties": false,
|
|
9299
|
+
"required": [
|
|
9300
|
+
"currency",
|
|
9301
|
+
"max_amount_micros",
|
|
9302
|
+
"spent_micros",
|
|
9303
|
+
"remaining_micros",
|
|
9304
|
+
"per_topup_cap_micros",
|
|
9305
|
+
"expires_at",
|
|
9306
|
+
"status"
|
|
9307
|
+
],
|
|
9308
|
+
"properties": {
|
|
9309
|
+
"currency": {
|
|
9310
|
+
"type": "string",
|
|
9311
|
+
"description": "Currency of the budget, for example `usd`."
|
|
9312
|
+
},
|
|
9313
|
+
"max_amount_micros": {
|
|
9314
|
+
"type": "string",
|
|
9315
|
+
"pattern": "^[0-9]+$",
|
|
9316
|
+
"description": "Total allowance the operator funded, in micros."
|
|
9317
|
+
},
|
|
9318
|
+
"spent_micros": {
|
|
9319
|
+
"type": "string",
|
|
9320
|
+
"pattern": "^[0-9]+$",
|
|
9321
|
+
"description": "How much of the allowance has been drawn down, in micros."
|
|
9322
|
+
},
|
|
9323
|
+
"remaining_micros": {
|
|
9324
|
+
"type": "string",
|
|
9325
|
+
"pattern": "^[0-9]+$",
|
|
9326
|
+
"description": "`max_amount_micros - spent_micros`, floored at zero."
|
|
9327
|
+
},
|
|
9328
|
+
"per_topup_cap_micros": {
|
|
9329
|
+
"type": ["string", "null"],
|
|
9330
|
+
"pattern": "^[0-9]+$",
|
|
9331
|
+
"description": "Largest single top-up allowed, in micros, or null for no per-top-up cap."
|
|
9332
|
+
},
|
|
9333
|
+
"expires_at": {
|
|
9334
|
+
"type": ["string", "null"],
|
|
9335
|
+
"format": "date-time",
|
|
9336
|
+
"description": "When the allowance expires, or null for no expiry."
|
|
9337
|
+
},
|
|
9338
|
+
"status": {
|
|
9339
|
+
"type": "string",
|
|
9340
|
+
"description": "Budget status. An active budget reads `active`."
|
|
9341
|
+
}
|
|
9342
|
+
}
|
|
9343
|
+
},
|
|
9344
|
+
"prepaid_credit": {
|
|
9345
|
+
"description": "Prepaid usage credit (top-ups, redeemed credit codes and granted\ncredit) that can still pay for usage, or null when there is none.\nOmitted when an exact figure cannot be given right now; the budget\nis still returned.\n",
|
|
9346
|
+
"type": ["object", "null"],
|
|
9347
|
+
"additionalProperties": false,
|
|
9348
|
+
"required": [
|
|
9349
|
+
"currency",
|
|
9350
|
+
"remaining_micros",
|
|
9351
|
+
"next_expires_at"
|
|
9352
|
+
],
|
|
9353
|
+
"properties": {
|
|
9354
|
+
"currency": {
|
|
9355
|
+
"type": "string",
|
|
9356
|
+
"description": "Currency of the credit, which is the organization billing currency."
|
|
9357
|
+
},
|
|
9358
|
+
"remaining_micros": {
|
|
9359
|
+
"type": "string",
|
|
9360
|
+
"pattern": "^[0-9]+$",
|
|
9361
|
+
"description": "What the credit can still pay for, in micros."
|
|
9362
|
+
},
|
|
9363
|
+
"next_expires_at": {
|
|
9364
|
+
"type": ["string", "null"],
|
|
9365
|
+
"format": "date-time",
|
|
9366
|
+
"description": "Earliest expiry among credits with a balance, or null when none of them expires."
|
|
9367
|
+
}
|
|
9368
|
+
}
|
|
9369
|
+
}
|
|
9370
|
+
}
|
|
9371
|
+
},
|
|
9044
9372
|
"InboxStatus": {
|
|
9045
9373
|
"type": "object",
|
|
9046
9374
|
"additionalProperties": false,
|
|
@@ -11644,6 +11972,10 @@ const openapiDocument = {
|
|
|
11644
11972
|
"Endpoint": {
|
|
11645
11973
|
"type": "object",
|
|
11646
11974
|
"properties": {
|
|
11975
|
+
"recipient": {
|
|
11976
|
+
"type": ["string", "null"],
|
|
11977
|
+
"description": "Server-enforced recipient for an address-scoped pull subscription; null for account-wide receiving."
|
|
11978
|
+
},
|
|
11647
11979
|
"receiver_capabilities": {
|
|
11648
11980
|
"type": "object",
|
|
11649
11981
|
"properties": {
|
|
@@ -15479,6 +15811,181 @@ const operationManifest = [
|
|
|
15479
15811
|
"tag": "CLI",
|
|
15480
15812
|
"tagCommand": "cli"
|
|
15481
15813
|
},
|
|
15814
|
+
{
|
|
15815
|
+
"binaryResponse": false,
|
|
15816
|
+
"bodyRequired": false,
|
|
15817
|
+
"command": "get-credit-balance",
|
|
15818
|
+
"description": "Read the organization credit position. `prepaid_credit` is the prepaid\nusage credit (paid top-ups, redeemed credit codes and granted credit)\nthat can still pay for usage. `budget` is the active agent spending\nbudget an operator funded for top-ups, or null when there is none.\nAmounts are strings of integer micros (1 USD = 1000000 micros).\n",
|
|
15819
|
+
"hasJsonBody": false,
|
|
15820
|
+
"method": "GET",
|
|
15821
|
+
"operationId": "getCreditBalance",
|
|
15822
|
+
"path": "/credits/balance",
|
|
15823
|
+
"pathParams": [],
|
|
15824
|
+
"queryParams": [],
|
|
15825
|
+
"requestSchema": null,
|
|
15826
|
+
"responseSchema": {
|
|
15827
|
+
"type": "object",
|
|
15828
|
+
"required": ["budget"],
|
|
15829
|
+
"properties": {
|
|
15830
|
+
"budget": {
|
|
15831
|
+
"description": "The active agent spending budget an operator funded for top-ups,\nor null when there is none.\n",
|
|
15832
|
+
"type": ["object", "null"],
|
|
15833
|
+
"additionalProperties": false,
|
|
15834
|
+
"required": [
|
|
15835
|
+
"currency",
|
|
15836
|
+
"max_amount_micros",
|
|
15837
|
+
"spent_micros",
|
|
15838
|
+
"remaining_micros",
|
|
15839
|
+
"per_topup_cap_micros",
|
|
15840
|
+
"expires_at",
|
|
15841
|
+
"status"
|
|
15842
|
+
],
|
|
15843
|
+
"properties": {
|
|
15844
|
+
"currency": {
|
|
15845
|
+
"type": "string",
|
|
15846
|
+
"description": "Currency of the budget, for example `usd`."
|
|
15847
|
+
},
|
|
15848
|
+
"max_amount_micros": {
|
|
15849
|
+
"type": "string",
|
|
15850
|
+
"pattern": "^[0-9]+$",
|
|
15851
|
+
"description": "Total allowance the operator funded, in micros."
|
|
15852
|
+
},
|
|
15853
|
+
"spent_micros": {
|
|
15854
|
+
"type": "string",
|
|
15855
|
+
"pattern": "^[0-9]+$",
|
|
15856
|
+
"description": "How much of the allowance has been drawn down, in micros."
|
|
15857
|
+
},
|
|
15858
|
+
"remaining_micros": {
|
|
15859
|
+
"type": "string",
|
|
15860
|
+
"pattern": "^[0-9]+$",
|
|
15861
|
+
"description": "`max_amount_micros - spent_micros`, floored at zero."
|
|
15862
|
+
},
|
|
15863
|
+
"per_topup_cap_micros": {
|
|
15864
|
+
"type": ["string", "null"],
|
|
15865
|
+
"pattern": "^[0-9]+$",
|
|
15866
|
+
"description": "Largest single top-up allowed, in micros, or null for no per-top-up cap."
|
|
15867
|
+
},
|
|
15868
|
+
"expires_at": {
|
|
15869
|
+
"type": ["string", "null"],
|
|
15870
|
+
"format": "date-time",
|
|
15871
|
+
"description": "When the allowance expires, or null for no expiry."
|
|
15872
|
+
},
|
|
15873
|
+
"status": {
|
|
15874
|
+
"type": "string",
|
|
15875
|
+
"description": "Budget status. An active budget reads `active`."
|
|
15876
|
+
}
|
|
15877
|
+
}
|
|
15878
|
+
},
|
|
15879
|
+
"prepaid_credit": {
|
|
15880
|
+
"description": "Prepaid usage credit (top-ups, redeemed credit codes and granted\ncredit) that can still pay for usage, or null when there is none.\nOmitted when an exact figure cannot be given right now; the budget\nis still returned.\n",
|
|
15881
|
+
"type": ["object", "null"],
|
|
15882
|
+
"additionalProperties": false,
|
|
15883
|
+
"required": [
|
|
15884
|
+
"currency",
|
|
15885
|
+
"remaining_micros",
|
|
15886
|
+
"next_expires_at"
|
|
15887
|
+
],
|
|
15888
|
+
"properties": {
|
|
15889
|
+
"currency": {
|
|
15890
|
+
"type": "string",
|
|
15891
|
+
"description": "Currency of the credit, which is the organization billing currency."
|
|
15892
|
+
},
|
|
15893
|
+
"remaining_micros": {
|
|
15894
|
+
"type": "string",
|
|
15895
|
+
"pattern": "^[0-9]+$",
|
|
15896
|
+
"description": "What the credit can still pay for, in micros."
|
|
15897
|
+
},
|
|
15898
|
+
"next_expires_at": {
|
|
15899
|
+
"type": ["string", "null"],
|
|
15900
|
+
"format": "date-time",
|
|
15901
|
+
"description": "Earliest expiry among credits with a balance, or null when none of them expires."
|
|
15902
|
+
}
|
|
15903
|
+
}
|
|
15904
|
+
}
|
|
15905
|
+
}
|
|
15906
|
+
},
|
|
15907
|
+
"sdkName": "getCreditBalance",
|
|
15908
|
+
"summary": "Get credit balance",
|
|
15909
|
+
"tag": "Credits",
|
|
15910
|
+
"tagCommand": "credits"
|
|
15911
|
+
},
|
|
15912
|
+
{
|
|
15913
|
+
"binaryResponse": false,
|
|
15914
|
+
"bodyRequired": true,
|
|
15915
|
+
"command": "redeem-credit-code",
|
|
15916
|
+
"description": "Redeem a credit code for the authenticated organization. The credit is\nadded to the organization prepaid credit and shows in\n`GET /credits/balance` under `prepaid_credit`.\n\nRedeeming requires an organization owner or admin. An API key redeems\nwith the authority of the user who created it, based on that user's\ncurrent role; a key without that authority gets the same\n`credit_code_invalid` refusal as an unknown code.\n\nThe `Idempotency-Key` header is required. Retrying with the same key\nand code returns the original grant with `replayed: true` and grants\nnothing new; the same key with a different code is refused with\n`idempotency_key_reused`. Every refusal carries a human-readable\n`error.message` that can be shown to the user as is.\n",
|
|
15917
|
+
"hasJsonBody": true,
|
|
15918
|
+
"method": "POST",
|
|
15919
|
+
"operationId": "redeemCreditCode",
|
|
15920
|
+
"path": "/credits/redeem",
|
|
15921
|
+
"pathParams": [],
|
|
15922
|
+
"queryParams": [],
|
|
15923
|
+
"requestSchema": {
|
|
15924
|
+
"type": "object",
|
|
15925
|
+
"additionalProperties": false,
|
|
15926
|
+
"required": ["code"],
|
|
15927
|
+
"properties": { "code": {
|
|
15928
|
+
"type": "string",
|
|
15929
|
+
"pattern": "^\\s*\\S(?:[\\s\\S]{0,254}\\S)?\\s*$",
|
|
15930
|
+
"description": "The credit code to redeem. Surrounding whitespace is ignored; the\nrest must be 1 to 256 characters.\n"
|
|
15931
|
+
} }
|
|
15932
|
+
},
|
|
15933
|
+
"responseSchema": {
|
|
15934
|
+
"type": "object",
|
|
15935
|
+
"additionalProperties": false,
|
|
15936
|
+
"required": [
|
|
15937
|
+
"redemption_id",
|
|
15938
|
+
"amount_micros",
|
|
15939
|
+
"currency",
|
|
15940
|
+
"granted_at",
|
|
15941
|
+
"expires_at",
|
|
15942
|
+
"label",
|
|
15943
|
+
"replayed",
|
|
15944
|
+
"message"
|
|
15945
|
+
],
|
|
15946
|
+
"properties": {
|
|
15947
|
+
"redemption_id": {
|
|
15948
|
+
"type": "string",
|
|
15949
|
+
"description": "Identifier of the redemption."
|
|
15950
|
+
},
|
|
15951
|
+
"amount_micros": {
|
|
15952
|
+
"type": "string",
|
|
15953
|
+
"pattern": "^[0-9]+$",
|
|
15954
|
+
"description": "Credit granted, in micros of `currency` (1 USD = 1000000 micros)."
|
|
15955
|
+
},
|
|
15956
|
+
"currency": {
|
|
15957
|
+
"type": "string",
|
|
15958
|
+
"description": "Currency of the granted credit, for example `usd`."
|
|
15959
|
+
},
|
|
15960
|
+
"granted_at": {
|
|
15961
|
+
"type": "string",
|
|
15962
|
+
"format": "date-time",
|
|
15963
|
+
"description": "When the credit was granted."
|
|
15964
|
+
},
|
|
15965
|
+
"expires_at": {
|
|
15966
|
+
"type": ["string", "null"],
|
|
15967
|
+
"format": "date-time",
|
|
15968
|
+
"description": "When the granted credit expires, or null when it does not expire."
|
|
15969
|
+
},
|
|
15970
|
+
"label": {
|
|
15971
|
+
"type": ["string", "null"],
|
|
15972
|
+
"description": "Customer-facing label of the promotion, when it has one."
|
|
15973
|
+
},
|
|
15974
|
+
"replayed": {
|
|
15975
|
+
"type": "boolean",
|
|
15976
|
+
"description": "True when this Idempotency-Key already redeemed this code and the\noriginal grant is returned. No second grant is made.\n"
|
|
15977
|
+
},
|
|
15978
|
+
"message": {
|
|
15979
|
+
"type": "string",
|
|
15980
|
+
"description": "Human-readable summary of the grant, suitable to show as is."
|
|
15981
|
+
}
|
|
15982
|
+
}
|
|
15983
|
+
},
|
|
15984
|
+
"sdkName": "redeemCreditCode",
|
|
15985
|
+
"summary": "Redeem a credit code",
|
|
15986
|
+
"tag": "Credits",
|
|
15987
|
+
"tagCommand": "credits"
|
|
15988
|
+
},
|
|
15482
15989
|
{
|
|
15483
15990
|
"binaryResponse": false,
|
|
15484
15991
|
"bodyRequired": true,
|
|
@@ -18072,7 +18579,7 @@ const operationManifest = [
|
|
|
18072
18579
|
"binaryResponse": false,
|
|
18073
18580
|
"bodyRequired": true,
|
|
18074
18581
|
"command": "create-endpoint",
|
|
18075
|
-
"description": "Creates a new webhook endpoint. If a deactivated endpoint\nwith the same URL and domain exists, it is reactivated\ninstead. Subject to plan limits on the number of active\nendpoints.\n\n**Signing is account-scoped, not per-endpoint.** This call\ndoes not return any signing material; every endpoint on the\naccount uses the same webhook secret, fetched via\n`GET /account/webhook-secret`. See the API-level \"Webhook\nsigning\" section for the full wire format (header name,\nsigned string, hash algo, secret format, tolerance) and a\nlanguage-agnostic verification recipe.\n\nAfter creating the endpoint, fire a test delivery against\nit via `POST /endpoints/{id}/test` to confirm your verifier\naccepts the signature.\n\n\nFor local receiving, use kind=pull and a stable name, without url, function_id or domain_id. Named creation resumes the same active destination; omitted filters preserve its selection, while conflicting supplied configuration returns 409. New unfiltered destinations receive all subsequently ready eligible event types. Pull destinations do not occupy HTTP routing slots. Signing-secret setup and a test HTTP delivery are not required for pull receiving.",
|
|
18582
|
+
"description": "Creates a new webhook endpoint. If a deactivated endpoint\nwith the same URL and domain exists, it is reactivated\ninstead. Subject to plan limits on the number of active\nendpoints.\n\n**Signing is account-scoped, not per-endpoint.** This call\ndoes not return any signing material; every endpoint on the\naccount uses the same webhook secret, fetched via\n`GET /account/webhook-secret`. See the API-level \"Webhook\nsigning\" section for the full wire format (header name,\nsigned string, hash algo, secret format, tolerance) and a\nlanguage-agnostic verification recipe.\n\nAfter creating the endpoint, fire a test delivery against\nit via `POST /endpoints/{id}/test` to confirm your verifier\naccepts the signature.\n\n\nFor local receiving, use kind=pull and a stable name, without url, function_id or domain_id. Named creation resumes the same active destination; omitted filters preserve its selection, while conflicting supplied configuration returns 409. New unfiltered destinations receive all subsequently ready eligible event types within their credential scope. Connected-agent credentials automatically create private subscriptions restricted to inbound email for their assigned address. Names are isolated per credential; revocation or replacement deletes its subscriptions. Address-scoped events contain parsed mail without raw MIME, signed download links, routing metadata, or sibling envelope recipients. Pull destinations do not occupy HTTP routing slots. Signing-secret setup and a test HTTP delivery are not required for pull receiving.",
|
|
18076
18583
|
"hasJsonBody": true,
|
|
18077
18584
|
"method": "POST",
|
|
18078
18585
|
"operationId": "createEndpoint",
|
|
@@ -18140,6 +18647,10 @@ const operationManifest = [
|
|
|
18140
18647
|
"responseSchema": {
|
|
18141
18648
|
"type": "object",
|
|
18142
18649
|
"properties": {
|
|
18650
|
+
"recipient": {
|
|
18651
|
+
"type": ["string", "null"],
|
|
18652
|
+
"description": "Server-enforced recipient for an address-scoped pull subscription; null for account-wide receiving."
|
|
18653
|
+
},
|
|
18143
18654
|
"receiver_capabilities": {
|
|
18144
18655
|
"type": "object",
|
|
18145
18656
|
"properties": {
|
|
@@ -18316,6 +18827,10 @@ const operationManifest = [
|
|
|
18316
18827
|
"items": {
|
|
18317
18828
|
"type": "object",
|
|
18318
18829
|
"properties": {
|
|
18830
|
+
"recipient": {
|
|
18831
|
+
"type": ["string", "null"],
|
|
18832
|
+
"description": "Server-enforced recipient for an address-scoped pull subscription; null for account-wide receiving."
|
|
18833
|
+
},
|
|
18319
18834
|
"receiver_capabilities": {
|
|
18320
18835
|
"type": "object",
|
|
18321
18836
|
"properties": {
|
|
@@ -18782,6 +19297,10 @@ const operationManifest = [
|
|
|
18782
19297
|
"responseSchema": {
|
|
18783
19298
|
"type": "object",
|
|
18784
19299
|
"properties": {
|
|
19300
|
+
"recipient": {
|
|
19301
|
+
"type": ["string", "null"],
|
|
19302
|
+
"description": "Server-enforced recipient for an address-scoped pull subscription; null for account-wide receiving."
|
|
19303
|
+
},
|
|
18785
19304
|
"receiver_capabilities": {
|
|
18786
19305
|
"type": "object",
|
|
18787
19306
|
"properties": {
|
|
@@ -30421,6 +30940,240 @@ var ConfigCommand = class extends ConfigListCommand {
|
|
|
30421
30940
|
static description = "Manage local Primitive CLI request environments for API endpoint overrides and request headers.";
|
|
30422
30941
|
};
|
|
30423
30942
|
//#endregion
|
|
30943
|
+
//#region src/oclif/commands/credits.ts
|
|
30944
|
+
const API_KEY_FLAG$1 = Flags.string({
|
|
30945
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
30946
|
+
env: "PRIMITIVE_API_KEY"
|
|
30947
|
+
});
|
|
30948
|
+
const API_BASE_URL_FLAG$1 = Flags.string({
|
|
30949
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
30950
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
30951
|
+
hidden: true
|
|
30952
|
+
});
|
|
30953
|
+
const MICROS_PER_UNIT = 1000000n;
|
|
30954
|
+
/**
|
|
30955
|
+
* Format an integer micros string as a currency amount. USD renders as
|
|
30956
|
+
* `$12.50`; other currencies as `12.50 EUR`. Two decimals are always shown,
|
|
30957
|
+
* and up to six when the amount has sub-cent precision. Input that is not a
|
|
30958
|
+
* non-negative integer string is returned unchanged with the currency code.
|
|
30959
|
+
*/
|
|
30960
|
+
function formatMicros(micros, currency) {
|
|
30961
|
+
const code = currency.toUpperCase();
|
|
30962
|
+
if (!/^[0-9]+$/.test(micros)) return `${micros} micros ${code}`.trim();
|
|
30963
|
+
const value = BigInt(micros);
|
|
30964
|
+
const whole = value / MICROS_PER_UNIT;
|
|
30965
|
+
const fraction = (value % MICROS_PER_UNIT).toString().padStart(6, "0").replace(/0+$/, "").padEnd(2, "0");
|
|
30966
|
+
const amount = `${whole.toLocaleString("en-US")}.${fraction}`;
|
|
30967
|
+
return code === "USD" ? `$${amount}` : `${amount} ${code}`;
|
|
30968
|
+
}
|
|
30969
|
+
function formatExpiry(value) {
|
|
30970
|
+
if (!value) return "never";
|
|
30971
|
+
const date = new Date(value);
|
|
30972
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
30973
|
+
return date.toISOString().slice(0, 10);
|
|
30974
|
+
}
|
|
30975
|
+
function formatRedemptionSummary(redemption) {
|
|
30976
|
+
const lines = [redemption.replayed ? `Already redeemed with this idempotency key: ${formatMicros(redemption.amount_micros, redemption.currency)} in credit. No new credit was added.` : `Redeemed ${formatMicros(redemption.amount_micros, redemption.currency)} in credit.`, `Expires: ${formatExpiry(redemption.expires_at)}`];
|
|
30977
|
+
if (redemption.label?.trim()) lines.push(`Promotion: ${redemption.label}`);
|
|
30978
|
+
lines.push(`Redemption id: ${redemption.redemption_id}`);
|
|
30979
|
+
return lines.join("\n");
|
|
30980
|
+
}
|
|
30981
|
+
function formatBalanceSummary(balance) {
|
|
30982
|
+
const lines = [];
|
|
30983
|
+
const prepaid = balance.prepaid_credit;
|
|
30984
|
+
if (prepaid === void 0) lines.push("Prepaid credit: not available right now, try again shortly");
|
|
30985
|
+
else if (prepaid === null) lines.push("Prepaid credit: none");
|
|
30986
|
+
else {
|
|
30987
|
+
const expiry = prepaid.next_expires_at ? `next expiry ${formatExpiry(prepaid.next_expires_at)}` : "no expiry";
|
|
30988
|
+
lines.push(`Prepaid credit: ${formatMicros(prepaid.remaining_micros, prepaid.currency)} (${expiry})`);
|
|
30989
|
+
}
|
|
30990
|
+
const budget = balance.budget;
|
|
30991
|
+
if (!budget) lines.push("Spending budget: none");
|
|
30992
|
+
else {
|
|
30993
|
+
const details = [`status ${budget.status}`];
|
|
30994
|
+
if (budget.per_topup_cap_micros) details.push(`per top-up cap ${formatMicros(budget.per_topup_cap_micros, budget.currency)}`);
|
|
30995
|
+
details.push(`expires ${formatExpiry(budget.expires_at)}`);
|
|
30996
|
+
lines.push(`Spending budget: ${formatMicros(budget.remaining_micros, budget.currency)} of ${formatMicros(budget.max_amount_micros, budget.currency)} remaining (${details.join(", ")})`);
|
|
30997
|
+
}
|
|
30998
|
+
return lines.join("\n");
|
|
30999
|
+
}
|
|
31000
|
+
/**
|
|
31001
|
+
* Lines printed after a failed redemption so the user can retry without risk
|
|
31002
|
+
* of a second grant: reusing the same key and code returns the original grant
|
|
31003
|
+
* if the first attempt did land. No shell command is printed, since quoting
|
|
31004
|
+
* differs between shells.
|
|
31005
|
+
*/
|
|
31006
|
+
function formatRetryHint(idempotencyKey) {
|
|
31007
|
+
return [`Idempotency-Key: ${idempotencyKey}`, `To retry this redemption safely, run the same command again with --idempotency-key ${idempotencyKey}.`].join("\n");
|
|
31008
|
+
}
|
|
31009
|
+
/**
|
|
31010
|
+
* Print a failed request. A server refusal (an error envelope with a
|
|
31011
|
+
* message) prints the message as the primary line, since the redeem route
|
|
31012
|
+
* writes it to be shown to the user; anything else falls back to the shared
|
|
31013
|
+
* error renderer. Always sets a non-zero exit code.
|
|
31014
|
+
*/
|
|
31015
|
+
function reportCreditsError(error, context, options) {
|
|
31016
|
+
const payload = extractErrorPayload(error);
|
|
31017
|
+
const body = payload;
|
|
31018
|
+
if (!options.json && body && typeof body === "object" && typeof body.message === "string" && typeof body.code === "string" && body.code !== "unauthorized") {
|
|
31019
|
+
process.stderr.write(`${options.prefix}: ${body.message}\n`);
|
|
31020
|
+
process.stderr.write(`Error code: ${body.code}\n`);
|
|
31021
|
+
} else writeErrorWithHints(payload);
|
|
31022
|
+
surfaceUnauthorizedHint({
|
|
31023
|
+
...context,
|
|
31024
|
+
payload
|
|
31025
|
+
});
|
|
31026
|
+
process.exitCode = 1;
|
|
31027
|
+
}
|
|
31028
|
+
var CreditsRedeemCommand = class CreditsRedeemCommand extends Command {
|
|
31029
|
+
static description = `Redeem a credit code for your organization.
|
|
31030
|
+
|
|
31031
|
+
Prints the credit added and when it expires. On a refusal (an invalid,
|
|
31032
|
+
already redeemed or ineligible code, for example) the server's message is
|
|
31033
|
+
printed and the command exits non-zero.
|
|
31034
|
+
|
|
31035
|
+
Redeeming needs an organization owner or admin. With an API key, the key's
|
|
31036
|
+
creator must currently be an owner or admin.
|
|
31037
|
+
|
|
31038
|
+
The request carries an Idempotency-Key. One is generated for each run; pass
|
|
31039
|
+
--idempotency-key to retry the same redemption safely, for example after a
|
|
31040
|
+
network error. Retrying with the same key and code returns the original
|
|
31041
|
+
grant and adds no second credit. On any failure the key in use is printed to
|
|
31042
|
+
stderr so the same command can be rerun with --idempotency-key.`;
|
|
31043
|
+
static summary = "Redeem a credit code";
|
|
31044
|
+
static examples = [
|
|
31045
|
+
"<%= config.bin %> credits redeem LAUNCH50",
|
|
31046
|
+
"<%= config.bin %> credits redeem LAUNCH50 --idempotency-key redeem-launch50-1",
|
|
31047
|
+
"<%= config.bin %> credits redeem LAUNCH50 --json"
|
|
31048
|
+
];
|
|
31049
|
+
static args = { code: Args.string({
|
|
31050
|
+
description: "The credit code to redeem.",
|
|
31051
|
+
required: true
|
|
31052
|
+
}) };
|
|
31053
|
+
static flags = {
|
|
31054
|
+
"api-key": API_KEY_FLAG$1,
|
|
31055
|
+
"api-base-url": API_BASE_URL_FLAG$1,
|
|
31056
|
+
"idempotency-key": Flags.string({ description: "Idempotency key for the request, at most 200 characters. Defaults to a new random key per run; reuse a key only to retry the same code." }),
|
|
31057
|
+
json: Flags.boolean({ description: "Print the full redemption JSON on success, or the error JSON on failure." }),
|
|
31058
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
31059
|
+
};
|
|
31060
|
+
async run() {
|
|
31061
|
+
const { args, flags } = await this.parse(CreditsRedeemCommand);
|
|
31062
|
+
const code = args.code.trim();
|
|
31063
|
+
if (!code) {
|
|
31064
|
+
process.stderr.write("The credit code must not be empty.\n");
|
|
31065
|
+
process.exitCode = 1;
|
|
31066
|
+
return;
|
|
31067
|
+
}
|
|
31068
|
+
const idempotencyKey = flags["idempotency-key"]?.trim() || `cli-redeem-${randomUUID()}`;
|
|
31069
|
+
if (idempotencyKey.length > 200) {
|
|
31070
|
+
process.stderr.write("--idempotency-key must be at most 200 characters.\n");
|
|
31071
|
+
process.exitCode = 1;
|
|
31072
|
+
return;
|
|
31073
|
+
}
|
|
31074
|
+
if (!/^[\x21-\x7E]+$/.test(idempotencyKey)) {
|
|
31075
|
+
process.stderr.write("--idempotency-key must contain only printable ASCII characters, without spaces.\n");
|
|
31076
|
+
process.exitCode = 1;
|
|
31077
|
+
return;
|
|
31078
|
+
}
|
|
31079
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
31080
|
+
apiKey: flags["api-key"],
|
|
31081
|
+
apiBaseUrl: flags["api-base-url"],
|
|
31082
|
+
configDir: this.config.configDir
|
|
31083
|
+
});
|
|
31084
|
+
const writeRetryHint = () => process.stderr.write(`${formatRetryHint(idempotencyKey)}\n`);
|
|
31085
|
+
await runWithTiming(flags.time, async () => {
|
|
31086
|
+
let result;
|
|
31087
|
+
try {
|
|
31088
|
+
result = await redeemCreditCode({
|
|
31089
|
+
client: apiClient.client,
|
|
31090
|
+
body: { code },
|
|
31091
|
+
headers: { "Idempotency-Key": idempotencyKey },
|
|
31092
|
+
responseStyle: "fields"
|
|
31093
|
+
});
|
|
31094
|
+
} catch (error) {
|
|
31095
|
+
writeRetryHint();
|
|
31096
|
+
throw error;
|
|
31097
|
+
}
|
|
31098
|
+
if (result.error) {
|
|
31099
|
+
reportCreditsError(result.error, {
|
|
31100
|
+
auth,
|
|
31101
|
+
baseUrlOverridden,
|
|
31102
|
+
configDir: this.config.configDir
|
|
31103
|
+
}, {
|
|
31104
|
+
json: Boolean(flags.json),
|
|
31105
|
+
prefix: `Could not redeem ${code}`
|
|
31106
|
+
});
|
|
31107
|
+
writeRetryHint();
|
|
31108
|
+
return;
|
|
31109
|
+
}
|
|
31110
|
+
const redemption = result.data?.data;
|
|
31111
|
+
if (!redemption) {
|
|
31112
|
+
process.stderr.write("Server returned an empty redemption body.\n");
|
|
31113
|
+
writeRetryHint();
|
|
31114
|
+
process.exitCode = 1;
|
|
31115
|
+
return;
|
|
31116
|
+
}
|
|
31117
|
+
if (flags.json) {
|
|
31118
|
+
this.log(JSON.stringify(redemption, null, 2));
|
|
31119
|
+
return;
|
|
31120
|
+
}
|
|
31121
|
+
this.log(formatRedemptionSummary(redemption));
|
|
31122
|
+
});
|
|
31123
|
+
}
|
|
31124
|
+
};
|
|
31125
|
+
var CreditsBalanceCommand = class CreditsBalanceCommand extends Command {
|
|
31126
|
+
static description = `Show your organization's credit balance.
|
|
31127
|
+
|
|
31128
|
+
Prints the prepaid credit (top-ups, redeemed credit codes and granted
|
|
31129
|
+
credit) that can still pay for usage, and the agent spending budget when an
|
|
31130
|
+
operator has funded one. Pass --json for the raw response.`;
|
|
31131
|
+
static summary = "Show the credit balance";
|
|
31132
|
+
static examples = ["<%= config.bin %> credits balance", "<%= config.bin %> credits balance --json"];
|
|
31133
|
+
static flags = {
|
|
31134
|
+
"api-key": API_KEY_FLAG$1,
|
|
31135
|
+
"api-base-url": API_BASE_URL_FLAG$1,
|
|
31136
|
+
json: Flags.boolean({ description: "Print the full balance JSON response." }),
|
|
31137
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
31138
|
+
};
|
|
31139
|
+
async run() {
|
|
31140
|
+
const { flags } = await this.parse(CreditsBalanceCommand);
|
|
31141
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
31142
|
+
apiKey: flags["api-key"],
|
|
31143
|
+
apiBaseUrl: flags["api-base-url"],
|
|
31144
|
+
configDir: this.config.configDir
|
|
31145
|
+
});
|
|
31146
|
+
await runWithTiming(flags.time, async () => {
|
|
31147
|
+
const result = await getCreditBalance({
|
|
31148
|
+
client: apiClient.client,
|
|
31149
|
+
responseStyle: "fields"
|
|
31150
|
+
});
|
|
31151
|
+
if (result.error) {
|
|
31152
|
+
reportCreditsError(result.error, {
|
|
31153
|
+
auth,
|
|
31154
|
+
baseUrlOverridden,
|
|
31155
|
+
configDir: this.config.configDir
|
|
31156
|
+
}, {
|
|
31157
|
+
json: Boolean(flags.json),
|
|
31158
|
+
prefix: "Could not read the balance"
|
|
31159
|
+
});
|
|
31160
|
+
return;
|
|
31161
|
+
}
|
|
31162
|
+
const balance = result.data?.data;
|
|
31163
|
+
if (!balance) {
|
|
31164
|
+
process.stderr.write("Server returned an empty balance body.\n");
|
|
31165
|
+
process.exitCode = 1;
|
|
31166
|
+
return;
|
|
31167
|
+
}
|
|
31168
|
+
if (flags.json) {
|
|
31169
|
+
this.log(JSON.stringify(balance, null, 2));
|
|
31170
|
+
return;
|
|
31171
|
+
}
|
|
31172
|
+
this.log(formatBalanceSummary(balance));
|
|
31173
|
+
});
|
|
31174
|
+
}
|
|
31175
|
+
};
|
|
31176
|
+
//#endregion
|
|
30424
31177
|
//#region src/oclif/commands/doctor.ts
|
|
30425
31178
|
const MIN_NODE_MAJOR = 22;
|
|
30426
31179
|
function renderRow({ label, outcome }) {
|
|
@@ -30524,7 +31277,7 @@ function checkApiKey(opts) {
|
|
|
30524
31277
|
}
|
|
30525
31278
|
async function checkAccount(client) {
|
|
30526
31279
|
try {
|
|
30527
|
-
const result = await getAccount
|
|
31280
|
+
const result = await getAccount({
|
|
30528
31281
|
client: client.client,
|
|
30529
31282
|
responseStyle: "fields"
|
|
30530
31283
|
});
|
|
@@ -32303,8 +33056,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
32303
33056
|
name: "Primitive Team",
|
|
32304
33057
|
url: "https://primitive.dev"
|
|
32305
33058
|
};
|
|
32306
|
-
const SDK_VERSION_RANGE = "^1.
|
|
32307
|
-
const CLI_VERSION_RANGE = "^1.
|
|
33059
|
+
const SDK_VERSION_RANGE = "^1.29.0";
|
|
33060
|
+
const CLI_VERSION_RANGE = "^1.29.0";
|
|
32308
33061
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
32309
33062
|
function renderHandler() {
|
|
32310
33063
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -34960,7 +35713,7 @@ async function pullFile(root, outPath, opts) {
|
|
|
34960
35713
|
return manifest;
|
|
34961
35714
|
}
|
|
34962
35715
|
//#endregion
|
|
34963
|
-
//#region ../sdk-node/dist/api-
|
|
35716
|
+
//#region ../sdk-node/dist/api-27BvnkVA.js
|
|
34964
35717
|
const jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) };
|
|
34965
35718
|
function createSseClient({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) {
|
|
34966
35719
|
let lastEventId;
|
|
@@ -35548,17 +36301,6 @@ const createClient = (config = {}) => {
|
|
|
35548
36301
|
};
|
|
35549
36302
|
const client$1 = createClient(createConfig({ baseUrl: "https://api.primitive.dev/v1" }));
|
|
35550
36303
|
/**
|
|
35551
|
-
* Get account info
|
|
35552
|
-
*/
|
|
35553
|
-
const getAccount = (options) => (options?.client ?? client$1).get({
|
|
35554
|
-
security: [{
|
|
35555
|
-
scheme: "bearer",
|
|
35556
|
-
type: "http"
|
|
35557
|
-
}],
|
|
35558
|
-
url: "/account",
|
|
35559
|
-
...options
|
|
35560
|
-
});
|
|
35561
|
-
/**
|
|
35562
36304
|
* Receive a pending webhook event without a public endpoint
|
|
35563
36305
|
*
|
|
35564
36306
|
* Wait up to 30 seconds for one existing event. The body string preserves the exact webhook serialization; headers and canonical occurrence/type metadata travel alongside it. A fixed lease protects the attempt while a short handler accepts input. Retry may redeliver an occurrence: deduplicate event_id. Empty delivery means no offer now, not proof all input was delivered; inspect backlog and persistent gap_count/last_gap_reason. Queue retention is 24 hours and does not extend source-content retention. Disconnecting preserves pending work. Same named destination shares consumption; different destinations get independent copies.
|
|
@@ -35632,7 +36374,7 @@ function unwrap(result) {
|
|
|
35632
36374
|
var EventConnection = class {
|
|
35633
36375
|
socket;
|
|
35634
36376
|
pending;
|
|
35635
|
-
|
|
36377
|
+
origin;
|
|
35636
36378
|
touch;
|
|
35637
36379
|
constructor(client, endpointId, options = {}) {
|
|
35638
36380
|
this.client = client;
|
|
@@ -35648,15 +36390,9 @@ var EventConnection = class {
|
|
|
35648
36390
|
}
|
|
35649
36391
|
async open(signal) {
|
|
35650
36392
|
if (this.options.transport === "poll" || this.socket?.readyState === 1) return;
|
|
35651
|
-
const account = unwrap(await getAccount({
|
|
35652
|
-
client: this.client,
|
|
35653
|
-
responseStyle: "fields",
|
|
35654
|
-
throwOnError: false,
|
|
35655
|
-
signal: AbortSignal.any([signal, AbortSignal.timeout(15e3)])
|
|
35656
|
-
}));
|
|
35657
|
-
if (!account.data?.id || this.accountId && account.data.id !== this.accountId) throw new EventReceiverError("The authenticated account changed; restart the receiver", "identity_changed", 403);
|
|
35658
|
-
this.accountId = account.data.id;
|
|
35659
36393
|
const config = this.client.getConfig();
|
|
36394
|
+
if (this.origin !== void 0 && this.origin !== config.baseUrl) throw new EventReceiverError("The API environment changed; restart the receiver", "identity_changed", 403);
|
|
36395
|
+
this.origin = config.baseUrl;
|
|
35660
36396
|
const auth = typeof config.auth === "function" ? await config.auth({
|
|
35661
36397
|
type: "http",
|
|
35662
36398
|
scheme: "bearer"
|
|
@@ -36021,7 +36757,7 @@ function resolveListenSubscription(configDir, identity, explicit) {
|
|
|
36021
36757
|
var ListenError = class extends Error {};
|
|
36022
36758
|
var RequestFailure = class extends ListenError {
|
|
36023
36759
|
constructor(status, code, retryAfter) {
|
|
36024
|
-
super(status === 401 || status === 403 ? "Listener authorization failed. Run primitive signin or check your API key and account permissions." : status === 409 && code === "subscription_conflict" ? "This subscription has different event filters. Omit --events to resume it, or use primitive listen --subscription new-subscription (add your --events selection)." : `Listener API request failed (HTTP ${status || "transport"}, ${code}).`);
|
|
36760
|
+
super(code === "agent_connection_scope_forbidden" ? "This operation is outside the connected address grant. Use inbound email events and this credential’s own subscription." : status === 401 || status === 403 ? "Listener authorization failed. Run primitive signin or check your API key and account permissions." : status === 409 && code === "subscription_conflict" ? "This subscription has different event filters. Omit --events to resume it, or use primitive listen --subscription new-subscription (add your --events selection)." : `Listener API request failed (HTTP ${status || "transport"}, ${code}).`);
|
|
36025
36761
|
this.status = status;
|
|
36026
36762
|
this.code = code;
|
|
36027
36763
|
this.retryAfter = retryAfter;
|
|
@@ -36081,6 +36817,7 @@ async function runListen(options) {
|
|
|
36081
36817
|
let accountId;
|
|
36082
36818
|
let verifiedKey;
|
|
36083
36819
|
let verified = false;
|
|
36820
|
+
let connectedCredential = false;
|
|
36084
36821
|
let confirmed = 0;
|
|
36085
36822
|
let release;
|
|
36086
36823
|
let stream;
|
|
@@ -36136,7 +36873,16 @@ async function runListen(options) {
|
|
|
36136
36873
|
if (origin !== void 0 && origin !== base) throw new ListenError("The API environment changed while listening. Restart the listener.");
|
|
36137
36874
|
origin = base;
|
|
36138
36875
|
if (!verified || auth.auth.apiKey !== verifiedKey) {
|
|
36139
|
-
|
|
36876
|
+
if (auth.auth.apiKey?.startsWith("pconn_")) {
|
|
36877
|
+
connectedCredential = true;
|
|
36878
|
+
const identity = `connection:${scryptSync(auth.auth.apiKey, "primitive-listener-identity-v1", 32).toString("hex")}`;
|
|
36879
|
+
if (accountId !== void 0 && accountId !== identity) throw new ListenError("The connected credential changed while listening. Restart the listener.");
|
|
36880
|
+
accountId = identity;
|
|
36881
|
+
verifiedKey = auth.auth.apiKey;
|
|
36882
|
+
verified = true;
|
|
36883
|
+
return auth.apiClient.client;
|
|
36884
|
+
}
|
|
36885
|
+
const account = await retry(() => getAccount({
|
|
36140
36886
|
client: auth.apiClient.client,
|
|
36141
36887
|
responseStyle: "fields",
|
|
36142
36888
|
signal: AbortSignal.any([signal, AbortSignal.timeout(3e4)])
|
|
@@ -36179,7 +36925,7 @@ async function runListen(options) {
|
|
|
36179
36925
|
stream = new EventConnection(streamClient, endpoint.id);
|
|
36180
36926
|
}
|
|
36181
36927
|
const selected = endpoint.rules?.event_types;
|
|
36182
|
-
const selection = Array.isArray(selected) && selected.every((value) => typeof value === "string" && /^[a-zA-Z0-9_.-]+$/.test(value)) ? selected.join(", ") : "all events";
|
|
36928
|
+
const selection = Array.isArray(selected) && selected.every((value) => typeof value === "string" && /^[a-zA-Z0-9_.-]+$/.test(value)) ? selected.join(", ") : endpoint.recipient ? `inbound email for ${JSON.stringify(endpoint.recipient)}` : "all events";
|
|
36183
36929
|
stderr.write(`Listening on subscription ${subscription.name} (${endpoint.id}); ${resumed ? "resumed" : "created"}, mode ${options.mode ?? "stdout"}, selection: ${selection}. Retention: 24 hours; handler limit: 30 seconds. Pending count follows the first poll. Ctrl-C disconnects; delete with primitive endpoints delete --id ${endpoint.id}.\n`);
|
|
36184
36930
|
let lastGap = -1;
|
|
36185
36931
|
let lastBacklog = -1;
|
|
@@ -36242,6 +36988,7 @@ async function runListen(options) {
|
|
|
36242
36988
|
delivery_id: delivery.delivery_id,
|
|
36243
36989
|
lease_token: delivery.lease_token
|
|
36244
36990
|
};
|
|
36991
|
+
if ((connectedCredential || endpoint.recipient) && completion.mode === "http") completion.confirmed = false;
|
|
36245
36992
|
try {
|
|
36246
36993
|
const receipt = await retry(async () => {
|
|
36247
36994
|
if (stream) {
|
|
@@ -36280,7 +37027,7 @@ async function runListen(options) {
|
|
|
36280
37027
|
//#region src/oclif/commands/listen.ts
|
|
36281
37028
|
var ListenCommand = class ListenCommand extends Command {
|
|
36282
37029
|
static summary = "Receive webhook events locally without a public endpoint";
|
|
36283
|
-
static description = "Subscribe once and reconnect using the same durable server queue. Use a short --exec hook to durably accept an event, --forward-to for a local webhook, or newline-delimited JSON on stdout.";
|
|
37030
|
+
static description = "Subscribe once and reconnect using the same durable server queue. Connected-agent credentials automatically receive only their assigned address. Use a short --exec hook to durably accept an event, --forward-to for a local webhook, or newline-delimited JSON on stdout.";
|
|
36284
37031
|
static examples = [
|
|
36285
37032
|
"<%= config.bin %> listen",
|
|
36286
37033
|
"<%= config.bin %> listen --subscription my-agent --exec \"python3 accept.py\"",
|
|
@@ -36302,7 +37049,7 @@ var ListenCommand = class ListenCommand extends Command {
|
|
|
36302
37049
|
min: 1,
|
|
36303
37050
|
max: 2147483
|
|
36304
37051
|
}),
|
|
36305
|
-
subscription: Flags.string({ description: "Stable subscription name. Defaults to a saved name for this
|
|
37052
|
+
subscription: Flags.string({ description: "Stable subscription name. Defaults to a saved name for this credential scope and API environment." }),
|
|
36306
37053
|
exec: Flags.string({
|
|
36307
37054
|
description: "Short POSIX shell command accepting each complete event on stdin.",
|
|
36308
37055
|
exclusive: ["forward-to"]
|
|
@@ -36679,7 +37426,7 @@ async function checkExistingLogin(params) {
|
|
|
36679
37426
|
apiBaseUrl: probeApiBaseUrl,
|
|
36680
37427
|
headers: requestConfig.headers
|
|
36681
37428
|
});
|
|
36682
|
-
const result = await (params.checkAccount ?? ((client) => getAccount
|
|
37429
|
+
const result = await (params.checkAccount ?? ((client) => getAccount({
|
|
36683
37430
|
client: client.client,
|
|
36684
37431
|
responseStyle: "fields"
|
|
36685
37432
|
})))(apiClient);
|
|
@@ -40318,7 +41065,7 @@ var PaymentsRegisterPayoutAddressCommand = class PaymentsRegisterPayoutAddressCo
|
|
|
40318
41065
|
configDir: this.config.configDir
|
|
40319
41066
|
});
|
|
40320
41067
|
await runWithTiming(flags.time, async () => {
|
|
40321
|
-
const account = await getAccount
|
|
41068
|
+
const account = await getAccount({
|
|
40322
41069
|
client: apiClient.client,
|
|
40323
41070
|
responseStyle: "fields"
|
|
40324
41071
|
});
|
|
@@ -42511,7 +43258,7 @@ var WhoamiCommand = class WhoamiCommand extends Command {
|
|
|
42511
43258
|
apiBaseUrl: flags["api-base-url"],
|
|
42512
43259
|
configDir: this.config.configDir
|
|
42513
43260
|
});
|
|
42514
|
-
const result = await getAccount
|
|
43261
|
+
const result = await getAccount({
|
|
42515
43262
|
client: apiClient.client,
|
|
42516
43263
|
responseStyle: "fields"
|
|
42517
43264
|
});
|
|
@@ -42868,6 +43615,8 @@ const CANONICAL_OPERATION_ALIASES = {
|
|
|
42868
43615
|
};
|
|
42869
43616
|
const DESCRIBE_OPERATION_ALIASES = {
|
|
42870
43617
|
...CANONICAL_OPERATION_ALIASES,
|
|
43618
|
+
"credits:balance": "credits:get-credit-balance",
|
|
43619
|
+
"credits:redeem": "credits:redeem-credit-code",
|
|
42871
43620
|
"domains:zone-file": "domains:download-domain-zone-file",
|
|
42872
43621
|
"functions:logs": "functions:list-function-logs",
|
|
42873
43622
|
"memories:delete": "memories:delete-memory",
|
|
@@ -42885,7 +43634,8 @@ const OVERRIDDEN_OPERATION_IDS = new Set([
|
|
|
42885
43634
|
"inbox:get-inbox-status",
|
|
42886
43635
|
"search:semantic-search",
|
|
42887
43636
|
"payments:register-payout-address",
|
|
42888
|
-
"payments:pay-challenge"
|
|
43637
|
+
"payments:pay-challenge",
|
|
43638
|
+
"credits:redeem-credit-code"
|
|
42889
43639
|
]);
|
|
42890
43640
|
const generatedCommands = Object.fromEntries(operationManifest.filter((operation) => !OVERRIDDEN_OPERATION_IDS.has(operationId(operation))).map((operation) => [operationId(operation), createOperationCommand(operation)]));
|
|
42891
43641
|
const COMMANDS = {
|
|
@@ -42966,6 +43716,9 @@ const COMMANDS = {
|
|
|
42966
43716
|
"memories:get": MemoriesGetCommand,
|
|
42967
43717
|
"memories:delete": MemoriesDeleteCommand,
|
|
42968
43718
|
"memories:search": MemoriesSearchCommand,
|
|
43719
|
+
"credits:redeem": CreditsRedeemCommand,
|
|
43720
|
+
"credits:redeem-credit-code": CreditsRedeemCommand,
|
|
43721
|
+
"credits:balance": CreditsBalanceCommand,
|
|
42969
43722
|
...Object.fromEntries(Object.entries(CANONICAL_OPERATION_ALIASES).map(([alias, target]) => {
|
|
42970
43723
|
const command = generatedCommands[target];
|
|
42971
43724
|
if (!command) throw new Error(`Missing generated command target for alias ${alias}`);
|
package/man/primitive.1
CHANGED
|
@@ -133,6 +133,12 @@ Read a durable memory by key.
|
|
|
133
133
|
.B primitive memories search PREFIX
|
|
134
134
|
List memories by key prefix.
|
|
135
135
|
.TP
|
|
136
|
+
.B primitive credits redeem CODE
|
|
137
|
+
Redeem a credit code for your organization.
|
|
138
|
+
.TP
|
|
139
|
+
.B primitive credits balance
|
|
140
|
+
Show the organization credit balance.
|
|
141
|
+
.TP
|
|
136
142
|
.B primitive list-operations
|
|
137
143
|
Print the generated API operation manifest.
|
|
138
144
|
.TP
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primcli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.0",
|
|
4
4
|
"description": "Official Primitive CLI: deploy Primitive Functions, send and inspect mail, manage endpoints, all from the terminal. Wraps the @primitivedotdev/sdk runtime client with one-shot commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -55,6 +55,9 @@
|
|
|
55
55
|
"agent": {
|
|
56
56
|
"description": "Agent signup and authentication API operations"
|
|
57
57
|
},
|
|
58
|
+
"credits": {
|
|
59
|
+
"description": "Redeem credit codes and check the organization credit balance"
|
|
60
|
+
},
|
|
58
61
|
"domains": {
|
|
59
62
|
"description": "Claim, verify, manage email domains, and download DNS zone files"
|
|
60
63
|
},
|