@primitivedotdev/cli 0.26.4 → 0.28.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 +21 -4
- package/dist/oclif/index.js +1174 -111
- package/package.json +12 -6
package/dist/oclif/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import { randomUUID } from "node:crypto";
|
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { hostname } from "node:os";
|
|
7
|
+
import process$1 from "node:process";
|
|
8
|
+
import { createInterface } from "node:readline/promises";
|
|
7
9
|
//#region \0rolldown/runtime.js
|
|
8
10
|
var __defProp = Object.defineProperty;
|
|
9
11
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -664,11 +666,13 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
664
666
|
replayDelivery: () => replayDelivery,
|
|
665
667
|
replayEmailWebhooks: () => replayEmailWebhooks,
|
|
666
668
|
replyToEmail: () => replyToEmail,
|
|
669
|
+
resendCliSignupVerification: () => resendCliSignupVerification,
|
|
667
670
|
rotateWebhookSecret: () => rotateWebhookSecret,
|
|
668
671
|
searchEmails: () => searchEmails,
|
|
669
672
|
sendEmail: () => sendEmail,
|
|
670
673
|
setFunctionSecret: () => setFunctionSecret,
|
|
671
674
|
startCliLogin: () => startCliLogin,
|
|
675
|
+
startCliSignup: () => startCliSignup,
|
|
672
676
|
testEndpoint: () => testEndpoint,
|
|
673
677
|
testFunction: () => testFunction,
|
|
674
678
|
updateAccount: () => updateAccount,
|
|
@@ -676,6 +680,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
676
680
|
updateEndpoint: () => updateEndpoint,
|
|
677
681
|
updateFilter: () => updateFilter,
|
|
678
682
|
updateFunction: () => updateFunction,
|
|
683
|
+
verifyCliSignup: () => verifyCliSignup,
|
|
679
684
|
verifyDomain: () => verifyDomain
|
|
680
685
|
});
|
|
681
686
|
/**
|
|
@@ -711,6 +716,54 @@ const pollCliLogin = (options) => (options.client ?? client).post({
|
|
|
711
716
|
}
|
|
712
717
|
});
|
|
713
718
|
/**
|
|
719
|
+
* Start CLI account signup
|
|
720
|
+
*
|
|
721
|
+
* Starts a terminal-native CLI signup. The API validates the signup code,
|
|
722
|
+
* creates a pending signup session, sends an email verification code, and
|
|
723
|
+
* returns an opaque signup token used by the resend and verify steps. This
|
|
724
|
+
* endpoint does not require an API key.
|
|
725
|
+
*
|
|
726
|
+
*/
|
|
727
|
+
const startCliSignup = (options) => (options.client ?? client).post({
|
|
728
|
+
url: "/cli/signup/start",
|
|
729
|
+
...options,
|
|
730
|
+
headers: {
|
|
731
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
732
|
+
...options.headers
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
/**
|
|
736
|
+
* Resend CLI signup verification code
|
|
737
|
+
*
|
|
738
|
+
* Sends a new email verification code for a pending CLI signup session.
|
|
739
|
+
* This endpoint does not require an API key.
|
|
740
|
+
*
|
|
741
|
+
*/
|
|
742
|
+
const resendCliSignupVerification = (options) => (options.client ?? client).post({
|
|
743
|
+
url: "/cli/signup/resend",
|
|
744
|
+
...options,
|
|
745
|
+
headers: {
|
|
746
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
747
|
+
...options.headers
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
/**
|
|
751
|
+
* Verify CLI signup and create API key
|
|
752
|
+
*
|
|
753
|
+
* Verifies the email code for a CLI signup session, creates the account,
|
|
754
|
+
* redeems the reserved signup code, mints an org-scoped CLI API key, and
|
|
755
|
+
* returns the raw key exactly once. This endpoint does not require an API key.
|
|
756
|
+
*
|
|
757
|
+
*/
|
|
758
|
+
const verifyCliSignup = (options) => (options.client ?? client).post({
|
|
759
|
+
url: "/cli/signup/verify",
|
|
760
|
+
...options,
|
|
761
|
+
headers: {
|
|
762
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
763
|
+
...options.headers
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
/**
|
|
714
767
|
* Revoke the current CLI API key
|
|
715
768
|
*
|
|
716
769
|
* Revokes the API key used to authenticate the request. CLI clients use
|
|
@@ -1451,8 +1504,9 @@ const listFunctions = (options) => (options?.client ?? client).get({
|
|
|
1451
1504
|
* than relying on external imports.
|
|
1452
1505
|
*
|
|
1453
1506
|
* **Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`
|
|
1454
|
-
* (optional) is capped at 5 MiB UTF-8
|
|
1455
|
-
*
|
|
1507
|
+
* (optional) is capped at 5 MiB UTF-8, stored with each deployment
|
|
1508
|
+
* attempt, and sent to the runtime so stack traces can resolve to
|
|
1509
|
+
* original source files.
|
|
1456
1510
|
*
|
|
1457
1511
|
* **Auto-wiring.** On successful deploy, Primitive automatically
|
|
1458
1512
|
* creates a webhook endpoint that delivers inbound mail to the
|
|
@@ -1525,11 +1579,10 @@ const getFunction = (options) => (options.client ?? client).get({
|
|
|
1525
1579
|
* passing the same `code` re-runs the deploy and refreshes the
|
|
1526
1580
|
* binding set with the latest values from the secrets table.
|
|
1527
1581
|
*
|
|
1528
|
-
* On
|
|
1529
|
-
*
|
|
1530
|
-
* `
|
|
1531
|
-
*
|
|
1532
|
-
* without polling.
|
|
1582
|
+
* On deploy failure, the previously-deployed code stays live; the
|
|
1583
|
+
* runtime never serves a half-built bundle. The response uses
|
|
1584
|
+
* `error.code` `deploy_failed`, and the function's `deploy_error`
|
|
1585
|
+
* field carries the latest deploy error for dashboard/API reads.
|
|
1533
1586
|
*
|
|
1534
1587
|
*/
|
|
1535
1588
|
const updateFunction = (options) => (options.client ?? client).put({
|
|
@@ -1705,7 +1758,7 @@ const openapiDocument = {
|
|
|
1705
1758
|
"info": {
|
|
1706
1759
|
"title": "Primitive API",
|
|
1707
1760
|
"version": "1.0.0",
|
|
1708
|
-
"description": "The Primitive API lets you manage domains, emails, webhook endpoints,\nfilters, and account settings programmatically.\n\n## Authentication\n\
|
|
1761
|
+
"description": "The Primitive API lets you manage domains, emails, webhook endpoints,\nfilters, and account settings programmatically.\n\n## Authentication\n\nMost endpoints require a Bearer token in the `Authorization` header:\n\n```\nAuthorization: Bearer prim_<your_api_key>\n```\n\nAPI keys are org-scoped. Create and manage them in your dashboard\nunder Settings > API Keys. CLI login and signup endpoints explicitly\ndeclare `security: []`; they do not require an API key because they\nare used to mint one.\n\n## Rate Limiting\n\nThe API enforces a sliding window rate limit of **120 requests per\n60 seconds** per organization. When exceeded, the API returns `429`\nwith a `Retry-After` header indicating how many seconds to wait.\n\n## Pagination\n\nList endpoints use cursor-based pagination. Responses include a\n`meta` object with `total`, `limit`, and `cursor` fields. Pass the\n`cursor` value as a query parameter to fetch the next page. When\n`cursor` is `null`, there are no more results.\n\n## Response Format\n\nAll responses use a consistent envelope:\n\n```json\n{\n \"success\": true,\n \"data\": { ... },\n \"meta\": { \"total\": 42, \"limit\": 50, \"cursor\": \"...\" }\n}\n```\n\nErrors follow the same pattern:\n\n```json\n{\n \"success\": false,\n \"error\": { \"code\": \"not_found\", \"message\": \"Email not found\" }\n}\n```\n\n## Webhook signing\n\nOutbound webhook deliveries (configured via the `endpoints` API)\nare signed so receivers can verify they came from Primitive and\nhave not been tampered with in transit. The signing scheme is\ndeliberately simple so it can be reimplemented in any language\nin a few lines. The Node SDK's `verifyWebhookSignature` helper\nis the reference implementation; the wire details below let you\nwrite a verifier in Python, Go, Ruby, etc. without reading our\nsource.\n\n**Header**: `Primitive-Signature: t=<unix-seconds>,v1=<hex>`\n\nA legacy `MyMX-Signature` header is also sent on every delivery\nwith the same value, retained for back-compatibility with\nintegrations written before the rename. New code should read\n`Primitive-Signature`.\n\n**Signed string**: `${timestamp}.${rawBody}` where `timestamp`\nis the Unix-seconds integer from the `t=` parameter and\n`rawBody` is the exact bytes of the HTTP request body BEFORE\nany JSON decoding. Verify against the raw body, not a\nre-serialized parse, or you will silently mismatch on\ninsignificant whitespace.\n\n**Signature**: HMAC-SHA256 of the signed string, hex-encoded\n(lowercase). Use the account's webhook secret as the HMAC key,\nas a UTF-8 byte sequence.\n\n**Secret**: returned by `GET /account/webhook-secret`. The\nstring looks base64-shaped (e.g. `XNHBBW8VqoBjRfNs1tkZj11jTk...`)\nbut is NOT base64; use it AS-IS as a UTF-8 string for the HMAC\nkey. Base64-decoding before HMAC will silently produce\nmismatched signatures.\n\n**Tolerance**: by convention, reject deliveries whose `t=`\ntimestamp is more than 5 minutes off your wall-clock to defend\nagainst replay attacks. The Node SDK's helper enforces this by\ndefault.\n\n**Verification recipe** (any language):\n\n```\n1. Read the raw HTTP body (do not parse).\n2. Read `Primitive-Signature: t=<ts>,v1=<sig>`.\n3. Reject if abs(now - ts) > 300 seconds.\n4. expected = HMAC_SHA256_hex(secret_utf8, f\"{ts}.{rawBody}\")\n5. Constant-time compare expected to sig. Reject if not equal.\n```\n\nFor Node, use `verifyWebhookSignature` from\n`@primitivedotdev/sdk/webhook` (or the higher-level\n`handleWebhook` helper if you want a one-liner). For other\nlanguages, the recipe above is everything you need.\n\nTest deliveries: `POST /endpoints/{id}/test` triggers a fake\ndelivery to your endpoint URL, signed with your real account\nsecret, so you can confirm verification end-to-end without\nneeding real inbound mail. The test response carries the exact\n`signature` header value sent on the wire so you can compare\nstrings directly.\n",
|
|
1709
1762
|
"contact": {
|
|
1710
1763
|
"name": "Primitive",
|
|
1711
1764
|
"url": "https://primitive.dev"
|
|
@@ -1877,6 +1930,97 @@ const openapiDocument = {
|
|
|
1877
1930
|
}
|
|
1878
1931
|
}
|
|
1879
1932
|
} },
|
|
1933
|
+
"/cli/signup/start": { "post": {
|
|
1934
|
+
"operationId": "startCliSignup",
|
|
1935
|
+
"summary": "Start CLI account signup",
|
|
1936
|
+
"description": "Starts a terminal-native CLI signup. The API validates the signup code,\ncreates a pending signup session, sends an email verification code, and\nreturns an opaque signup token used by the resend and verify steps. This\nendpoint does not require an API key.\n",
|
|
1937
|
+
"tags": ["CLI"],
|
|
1938
|
+
"security": [],
|
|
1939
|
+
"requestBody": {
|
|
1940
|
+
"required": true,
|
|
1941
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/StartCliSignupInput" } } }
|
|
1942
|
+
},
|
|
1943
|
+
"responses": {
|
|
1944
|
+
"201": {
|
|
1945
|
+
"description": "CLI signup session created and verification email sent",
|
|
1946
|
+
"headers": { "Cache-Control": {
|
|
1947
|
+
"schema": { "type": "string" },
|
|
1948
|
+
"description": "Always `no-store`"
|
|
1949
|
+
} },
|
|
1950
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
1951
|
+
"type": "object",
|
|
1952
|
+
"properties": { "data": { "$ref": "#/components/schemas/CliSignupStartResult" } }
|
|
1953
|
+
}] } } }
|
|
1954
|
+
},
|
|
1955
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
1956
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
1957
|
+
}
|
|
1958
|
+
} },
|
|
1959
|
+
"/cli/signup/resend": { "post": {
|
|
1960
|
+
"operationId": "resendCliSignupVerification",
|
|
1961
|
+
"summary": "Resend CLI signup verification code",
|
|
1962
|
+
"description": "Sends a new email verification code for a pending CLI signup session.\nThis endpoint does not require an API key.\n",
|
|
1963
|
+
"tags": ["CLI"],
|
|
1964
|
+
"security": [],
|
|
1965
|
+
"requestBody": {
|
|
1966
|
+
"required": true,
|
|
1967
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResendCliSignupVerificationInput" } } }
|
|
1968
|
+
},
|
|
1969
|
+
"responses": {
|
|
1970
|
+
"200": {
|
|
1971
|
+
"description": "Verification email resent",
|
|
1972
|
+
"headers": { "Cache-Control": {
|
|
1973
|
+
"schema": { "type": "string" },
|
|
1974
|
+
"description": "Always `no-store`"
|
|
1975
|
+
} },
|
|
1976
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
1977
|
+
"type": "object",
|
|
1978
|
+
"properties": { "data": { "$ref": "#/components/schemas/CliSignupResendResult" } }
|
|
1979
|
+
}] } } }
|
|
1980
|
+
},
|
|
1981
|
+
"400": {
|
|
1982
|
+
"description": "Invalid token or expired token",
|
|
1983
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1984
|
+
},
|
|
1985
|
+
"429": {
|
|
1986
|
+
"description": "Global rate limit exceeded or resend requested too quickly",
|
|
1987
|
+
"headers": { "Retry-After": {
|
|
1988
|
+
"schema": { "type": "integer" },
|
|
1989
|
+
"description": "Seconds to wait before retrying"
|
|
1990
|
+
} },
|
|
1991
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
} },
|
|
1995
|
+
"/cli/signup/verify": { "post": {
|
|
1996
|
+
"operationId": "verifyCliSignup",
|
|
1997
|
+
"summary": "Verify CLI signup and create API key",
|
|
1998
|
+
"description": "Verifies the email code for a CLI signup session, creates the account,\nredeems the reserved signup code, mints an org-scoped CLI API key, and\nreturns the raw key exactly once. This endpoint does not require an API key.\n",
|
|
1999
|
+
"tags": ["CLI"],
|
|
2000
|
+
"security": [],
|
|
2001
|
+
"requestBody": {
|
|
2002
|
+
"required": true,
|
|
2003
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/VerifyCliSignupInput" } } }
|
|
2004
|
+
},
|
|
2005
|
+
"responses": {
|
|
2006
|
+
"200": {
|
|
2007
|
+
"description": "CLI signup verified and API key created",
|
|
2008
|
+
"headers": { "Cache-Control": {
|
|
2009
|
+
"schema": { "type": "string" },
|
|
2010
|
+
"description": "Always `no-store`"
|
|
2011
|
+
} },
|
|
2012
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
2013
|
+
"type": "object",
|
|
2014
|
+
"properties": { "data": { "$ref": "#/components/schemas/CliSignupVerifyResult" } }
|
|
2015
|
+
}] } } }
|
|
2016
|
+
},
|
|
2017
|
+
"400": {
|
|
2018
|
+
"description": "Invalid request, invalid verification code, expired token, invalid signup code, rejected password, or account creation failure",
|
|
2019
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2020
|
+
},
|
|
2021
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
2022
|
+
}
|
|
2023
|
+
} },
|
|
1880
2024
|
"/cli/logout": { "post": {
|
|
1881
2025
|
"operationId": "cliLogout",
|
|
1882
2026
|
"summary": "Revoke the current CLI API key",
|
|
@@ -3017,7 +3161,7 @@ const openapiDocument = {
|
|
|
3017
3161
|
"post": {
|
|
3018
3162
|
"operationId": "createFunction",
|
|
3019
3163
|
"summary": "Deploy a function",
|
|
3020
|
-
"description": "Creates and deploys a new function. The handler must be a single\nESM module whose default export is an object with an async\n`fetch(request, env)` method (Workers-style). The gateway\nHMAC-verifies the POST against the org's webhook secret before\ninvoking the handler; the request body parses to an\n`email.received` event (see `EmailReceivedEvent` and the\nWebhook payload section for the full schema). Code is bundled\nbefore being uploaded; ship a single self-contained file rather\nthan relying on external imports.\n\n**Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`\n(optional) is capped at 5 MiB UTF-8
|
|
3164
|
+
"description": "Creates and deploys a new function. The handler must be a single\nESM module whose default export is an object with an async\n`fetch(request, env)` method (Workers-style). The gateway\nHMAC-verifies the POST against the org's webhook secret before\ninvoking the handler; the request body parses to an\n`email.received` event (see `EmailReceivedEvent` and the\nWebhook payload section for the full schema). Code is bundled\nbefore being uploaded; ship a single self-contained file rather\nthan relying on external imports.\n\n**Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`\n(optional) is capped at 5 MiB UTF-8, stored with each deployment\nattempt, and sent to the runtime so stack traces can resolve to\noriginal source files.\n\n**Auto-wiring.** On successful deploy, Primitive automatically\ncreates a webhook endpoint that delivers inbound mail to the\nfunction. There is nothing to configure on the Endpoints API\nfor this to work; the gateway URL returned here is for\nreference only and is not directly callable from outside.\n\n**Secrets.** New functions ship with the managed secrets\n(`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`) already\nbound. Add user-set secrets via\n`POST /functions/{id}/secrets`; secret writes only land in the\nrunning handler on the next redeploy.\n",
|
|
3021
3165
|
"tags": ["Functions"],
|
|
3022
3166
|
"requestBody": {
|
|
3023
3167
|
"required": true,
|
|
@@ -3031,13 +3175,18 @@ const openapiDocument = {
|
|
|
3031
3175
|
"properties": { "data": { "$ref": "#/components/schemas/CreateFunctionResult" } }
|
|
3032
3176
|
}] } } }
|
|
3033
3177
|
},
|
|
3034
|
-
"400": {
|
|
3178
|
+
"400": {
|
|
3179
|
+
"description": "Invalid request parameters or customer-correctable deploy rejection",
|
|
3180
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
3181
|
+
},
|
|
3035
3182
|
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3036
3183
|
"409": {
|
|
3037
3184
|
"description": "A function with this name already exists in the org",
|
|
3038
3185
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
3039
3186
|
},
|
|
3040
|
-
"
|
|
3187
|
+
"424": { "$ref": "#/components/responses/DeployFailed" },
|
|
3188
|
+
"429": { "$ref": "#/components/responses/DeployFailed" },
|
|
3189
|
+
"503": { "$ref": "#/components/responses/DeployFailed" }
|
|
3041
3190
|
}
|
|
3042
3191
|
}
|
|
3043
3192
|
},
|
|
@@ -3063,7 +3212,7 @@ const openapiDocument = {
|
|
|
3063
3212
|
"put": {
|
|
3064
3213
|
"operationId": "updateFunction",
|
|
3065
3214
|
"summary": "Update and redeploy a function",
|
|
3066
|
-
"description": "Replaces the function's source code with the body's `code` and\ntriggers a redeploy. Same size limits as `POST /functions`.\nUse this verb to push secret writes into the running handler:\npassing the same `code` re-runs the deploy and refreshes the\nbinding set with the latest values from the secrets table.\n\nOn
|
|
3215
|
+
"description": "Replaces the function's source code with the body's `code` and\ntriggers a redeploy. Same size limits as `POST /functions`.\nUse this verb to push secret writes into the running handler:\npassing the same `code` re-runs the deploy and refreshes the\nbinding set with the latest values from the secrets table.\n\nOn deploy failure, the previously-deployed code stays live; the\nruntime never serves a half-built bundle. The response uses\n`error.code` `deploy_failed`, and the function's `deploy_error`\nfield carries the latest deploy error for dashboard/API reads.\n",
|
|
3067
3216
|
"tags": ["Functions"],
|
|
3068
3217
|
"requestBody": {
|
|
3069
3218
|
"required": true,
|
|
@@ -3077,10 +3226,15 @@ const openapiDocument = {
|
|
|
3077
3226
|
"properties": { "data": { "$ref": "#/components/schemas/FunctionDetail" } }
|
|
3078
3227
|
}] } } }
|
|
3079
3228
|
},
|
|
3080
|
-
"400": {
|
|
3229
|
+
"400": {
|
|
3230
|
+
"description": "Invalid request parameters or customer-correctable deploy rejection",
|
|
3231
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
3232
|
+
},
|
|
3081
3233
|
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3082
3234
|
"404": { "$ref": "#/components/responses/NotFound" },
|
|
3083
|
-
"
|
|
3235
|
+
"424": { "$ref": "#/components/responses/DeployFailed" },
|
|
3236
|
+
"429": { "$ref": "#/components/responses/DeployFailed" },
|
|
3237
|
+
"503": { "$ref": "#/components/responses/DeployFailed" }
|
|
3084
3238
|
}
|
|
3085
3239
|
},
|
|
3086
3240
|
"delete": {
|
|
@@ -3399,6 +3553,19 @@ const openapiDocument = {
|
|
|
3399
3553
|
}
|
|
3400
3554
|
} }
|
|
3401
3555
|
},
|
|
3556
|
+
"DeployFailed": {
|
|
3557
|
+
"description": "Function deploy could not be completed; previously deployed code remains live",
|
|
3558
|
+
"content": { "application/json": {
|
|
3559
|
+
"schema": { "$ref": "#/components/schemas/ErrorResponse" },
|
|
3560
|
+
"example": {
|
|
3561
|
+
"success": false,
|
|
3562
|
+
"error": {
|
|
3563
|
+
"code": "deploy_failed",
|
|
3564
|
+
"message": "Function deploy failed"
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
} }
|
|
3568
|
+
},
|
|
3402
3569
|
"RateLimited": {
|
|
3403
3570
|
"description": "Rate limit exceeded",
|
|
3404
3571
|
"headers": { "Retry-After": {
|
|
@@ -3758,6 +3925,162 @@ const openapiDocument = {
|
|
|
3758
3925
|
"org_name"
|
|
3759
3926
|
]
|
|
3760
3927
|
},
|
|
3928
|
+
"StartCliSignupInput": {
|
|
3929
|
+
"type": "object",
|
|
3930
|
+
"additionalProperties": false,
|
|
3931
|
+
"properties": {
|
|
3932
|
+
"email": {
|
|
3933
|
+
"type": "string",
|
|
3934
|
+
"format": "email",
|
|
3935
|
+
"maxLength": 254
|
|
3936
|
+
},
|
|
3937
|
+
"signup_code": {
|
|
3938
|
+
"type": "string",
|
|
3939
|
+
"minLength": 1,
|
|
3940
|
+
"maxLength": 128
|
|
3941
|
+
},
|
|
3942
|
+
"terms_accepted": {
|
|
3943
|
+
"type": "boolean",
|
|
3944
|
+
"const": true,
|
|
3945
|
+
"description": "Must be true to confirm acceptance of Primitive's Terms of Service and Privacy Policy"
|
|
3946
|
+
},
|
|
3947
|
+
"device_name": {
|
|
3948
|
+
"type": "string",
|
|
3949
|
+
"minLength": 1,
|
|
3950
|
+
"maxLength": 80,
|
|
3951
|
+
"description": "Human-readable device name used for the created CLI API key"
|
|
3952
|
+
},
|
|
3953
|
+
"metadata": {
|
|
3954
|
+
"type": "object",
|
|
3955
|
+
"additionalProperties": true,
|
|
3956
|
+
"description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
|
|
3957
|
+
}
|
|
3958
|
+
},
|
|
3959
|
+
"required": [
|
|
3960
|
+
"email",
|
|
3961
|
+
"signup_code",
|
|
3962
|
+
"terms_accepted"
|
|
3963
|
+
]
|
|
3964
|
+
},
|
|
3965
|
+
"CliSignupStartResult": {
|
|
3966
|
+
"type": "object",
|
|
3967
|
+
"properties": {
|
|
3968
|
+
"signup_token": {
|
|
3969
|
+
"type": "string",
|
|
3970
|
+
"description": "Opaque token used to verify or resend the pending CLI signup"
|
|
3971
|
+
},
|
|
3972
|
+
"email": {
|
|
3973
|
+
"type": "string",
|
|
3974
|
+
"format": "email"
|
|
3975
|
+
},
|
|
3976
|
+
"expires_in": {
|
|
3977
|
+
"type": "integer",
|
|
3978
|
+
"description": "Seconds until the pending signup expires"
|
|
3979
|
+
},
|
|
3980
|
+
"resend_after": {
|
|
3981
|
+
"type": "integer",
|
|
3982
|
+
"description": "Minimum seconds before requesting another verification email"
|
|
3983
|
+
},
|
|
3984
|
+
"verification_code_length": {
|
|
3985
|
+
"type": "integer",
|
|
3986
|
+
"description": "Number of digits in the emailed verification code"
|
|
3987
|
+
}
|
|
3988
|
+
},
|
|
3989
|
+
"required": [
|
|
3990
|
+
"signup_token",
|
|
3991
|
+
"email",
|
|
3992
|
+
"expires_in",
|
|
3993
|
+
"resend_after",
|
|
3994
|
+
"verification_code_length"
|
|
3995
|
+
]
|
|
3996
|
+
},
|
|
3997
|
+
"ResendCliSignupVerificationInput": {
|
|
3998
|
+
"type": "object",
|
|
3999
|
+
"additionalProperties": false,
|
|
4000
|
+
"properties": { "signup_token": {
|
|
4001
|
+
"type": "string",
|
|
4002
|
+
"minLength": 1
|
|
4003
|
+
} },
|
|
4004
|
+
"required": ["signup_token"]
|
|
4005
|
+
},
|
|
4006
|
+
"CliSignupResendResult": {
|
|
4007
|
+
"type": "object",
|
|
4008
|
+
"properties": {
|
|
4009
|
+
"email": {
|
|
4010
|
+
"type": "string",
|
|
4011
|
+
"format": "email"
|
|
4012
|
+
},
|
|
4013
|
+
"expires_in": {
|
|
4014
|
+
"type": "integer",
|
|
4015
|
+
"description": "Seconds until the pending signup expires"
|
|
4016
|
+
},
|
|
4017
|
+
"resend_after": {
|
|
4018
|
+
"type": "integer",
|
|
4019
|
+
"description": "Minimum seconds before requesting another verification email"
|
|
4020
|
+
},
|
|
4021
|
+
"verification_code_length": {
|
|
4022
|
+
"type": "integer",
|
|
4023
|
+
"description": "Number of digits in the emailed verification code"
|
|
4024
|
+
}
|
|
4025
|
+
},
|
|
4026
|
+
"required": [
|
|
4027
|
+
"email",
|
|
4028
|
+
"expires_in",
|
|
4029
|
+
"resend_after",
|
|
4030
|
+
"verification_code_length"
|
|
4031
|
+
]
|
|
4032
|
+
},
|
|
4033
|
+
"VerifyCliSignupInput": {
|
|
4034
|
+
"type": "object",
|
|
4035
|
+
"additionalProperties": false,
|
|
4036
|
+
"properties": {
|
|
4037
|
+
"signup_token": {
|
|
4038
|
+
"type": "string",
|
|
4039
|
+
"minLength": 1
|
|
4040
|
+
},
|
|
4041
|
+
"verification_code": {
|
|
4042
|
+
"type": "string",
|
|
4043
|
+
"minLength": 1,
|
|
4044
|
+
"maxLength": 32
|
|
4045
|
+
},
|
|
4046
|
+
"password": {
|
|
4047
|
+
"type": "string",
|
|
4048
|
+
"minLength": 1,
|
|
4049
|
+
"maxLength": 1024
|
|
4050
|
+
}
|
|
4051
|
+
},
|
|
4052
|
+
"required": [
|
|
4053
|
+
"signup_token",
|
|
4054
|
+
"verification_code",
|
|
4055
|
+
"password"
|
|
4056
|
+
]
|
|
4057
|
+
},
|
|
4058
|
+
"CliSignupVerifyResult": {
|
|
4059
|
+
"type": "object",
|
|
4060
|
+
"properties": {
|
|
4061
|
+
"api_key": {
|
|
4062
|
+
"type": "string",
|
|
4063
|
+
"description": "Newly-created API key for CLI authentication"
|
|
4064
|
+
},
|
|
4065
|
+
"key_id": {
|
|
4066
|
+
"type": "string",
|
|
4067
|
+
"format": "uuid"
|
|
4068
|
+
},
|
|
4069
|
+
"key_prefix": { "type": "string" },
|
|
4070
|
+
"org_id": {
|
|
4071
|
+
"type": "string",
|
|
4072
|
+
"format": "uuid"
|
|
4073
|
+
},
|
|
4074
|
+
"org_name": { "type": ["string", "null"] }
|
|
4075
|
+
},
|
|
4076
|
+
"required": [
|
|
4077
|
+
"api_key",
|
|
4078
|
+
"key_id",
|
|
4079
|
+
"key_prefix",
|
|
4080
|
+
"org_id",
|
|
4081
|
+
"org_name"
|
|
4082
|
+
]
|
|
4083
|
+
},
|
|
3761
4084
|
"CliLogoutInput": {
|
|
3762
4085
|
"type": "object",
|
|
3763
4086
|
"additionalProperties": false,
|
|
@@ -5224,7 +5547,7 @@ const openapiDocument = {
|
|
|
5224
5547
|
"type": "string",
|
|
5225
5548
|
"minLength": 1,
|
|
5226
5549
|
"maxLength": 5242880,
|
|
5227
|
-
"description": "Optional source map for the bundle. Up to 5 MiB UTF-8.\nStored
|
|
5550
|
+
"description": "Optional source map for the bundle. Up to 5 MiB UTF-8.\nStored with the deployment attempt and sent to the runtime\nto symbolicate stack traces in the function's logs.\n"
|
|
5228
5551
|
}
|
|
5229
5552
|
},
|
|
5230
5553
|
"required": ["name", "code"]
|
|
@@ -5756,6 +6079,58 @@ const operationManifest = [
|
|
|
5756
6079
|
"tag": "CLI",
|
|
5757
6080
|
"tagCommand": "cli"
|
|
5758
6081
|
},
|
|
6082
|
+
{
|
|
6083
|
+
"binaryResponse": false,
|
|
6084
|
+
"bodyRequired": true,
|
|
6085
|
+
"command": "resend-cli-signup-verification",
|
|
6086
|
+
"description": "Sends a new email verification code for a pending CLI signup session.\nThis endpoint does not require an API key.\n",
|
|
6087
|
+
"hasJsonBody": true,
|
|
6088
|
+
"method": "POST",
|
|
6089
|
+
"operationId": "resendCliSignupVerification",
|
|
6090
|
+
"path": "/cli/signup/resend",
|
|
6091
|
+
"pathParams": [],
|
|
6092
|
+
"queryParams": [],
|
|
6093
|
+
"requestSchema": {
|
|
6094
|
+
"type": "object",
|
|
6095
|
+
"additionalProperties": false,
|
|
6096
|
+
"properties": { "signup_token": {
|
|
6097
|
+
"type": "string",
|
|
6098
|
+
"minLength": 1
|
|
6099
|
+
} },
|
|
6100
|
+
"required": ["signup_token"]
|
|
6101
|
+
},
|
|
6102
|
+
"responseSchema": {
|
|
6103
|
+
"type": "object",
|
|
6104
|
+
"properties": {
|
|
6105
|
+
"email": {
|
|
6106
|
+
"type": "string",
|
|
6107
|
+
"format": "email"
|
|
6108
|
+
},
|
|
6109
|
+
"expires_in": {
|
|
6110
|
+
"type": "integer",
|
|
6111
|
+
"description": "Seconds until the pending signup expires"
|
|
6112
|
+
},
|
|
6113
|
+
"resend_after": {
|
|
6114
|
+
"type": "integer",
|
|
6115
|
+
"description": "Minimum seconds before requesting another verification email"
|
|
6116
|
+
},
|
|
6117
|
+
"verification_code_length": {
|
|
6118
|
+
"type": "integer",
|
|
6119
|
+
"description": "Number of digits in the emailed verification code"
|
|
6120
|
+
}
|
|
6121
|
+
},
|
|
6122
|
+
"required": [
|
|
6123
|
+
"email",
|
|
6124
|
+
"expires_in",
|
|
6125
|
+
"resend_after",
|
|
6126
|
+
"verification_code_length"
|
|
6127
|
+
]
|
|
6128
|
+
},
|
|
6129
|
+
"sdkName": "resendCliSignupVerification",
|
|
6130
|
+
"summary": "Resend CLI signup verification code",
|
|
6131
|
+
"tag": "CLI",
|
|
6132
|
+
"tagCommand": "cli"
|
|
6133
|
+
},
|
|
5759
6134
|
{
|
|
5760
6135
|
"binaryResponse": false,
|
|
5761
6136
|
"bodyRequired": false,
|
|
@@ -5822,8 +6197,160 @@ const operationManifest = [
|
|
|
5822
6197
|
"interval"
|
|
5823
6198
|
]
|
|
5824
6199
|
},
|
|
5825
|
-
"sdkName": "startCliLogin",
|
|
5826
|
-
"summary": "Start CLI browser login",
|
|
6200
|
+
"sdkName": "startCliLogin",
|
|
6201
|
+
"summary": "Start CLI browser login",
|
|
6202
|
+
"tag": "CLI",
|
|
6203
|
+
"tagCommand": "cli"
|
|
6204
|
+
},
|
|
6205
|
+
{
|
|
6206
|
+
"binaryResponse": false,
|
|
6207
|
+
"bodyRequired": true,
|
|
6208
|
+
"command": "start-cli-signup",
|
|
6209
|
+
"description": "Starts a terminal-native CLI signup. The API validates the signup code,\ncreates a pending signup session, sends an email verification code, and\nreturns an opaque signup token used by the resend and verify steps. This\nendpoint does not require an API key.\n",
|
|
6210
|
+
"hasJsonBody": true,
|
|
6211
|
+
"method": "POST",
|
|
6212
|
+
"operationId": "startCliSignup",
|
|
6213
|
+
"path": "/cli/signup/start",
|
|
6214
|
+
"pathParams": [],
|
|
6215
|
+
"queryParams": [],
|
|
6216
|
+
"requestSchema": {
|
|
6217
|
+
"type": "object",
|
|
6218
|
+
"additionalProperties": false,
|
|
6219
|
+
"properties": {
|
|
6220
|
+
"email": {
|
|
6221
|
+
"type": "string",
|
|
6222
|
+
"format": "email",
|
|
6223
|
+
"maxLength": 254
|
|
6224
|
+
},
|
|
6225
|
+
"signup_code": {
|
|
6226
|
+
"type": "string",
|
|
6227
|
+
"minLength": 1,
|
|
6228
|
+
"maxLength": 128
|
|
6229
|
+
},
|
|
6230
|
+
"terms_accepted": {
|
|
6231
|
+
"type": "boolean",
|
|
6232
|
+
"const": true,
|
|
6233
|
+
"description": "Must be true to confirm acceptance of Primitive's Terms of Service and Privacy Policy"
|
|
6234
|
+
},
|
|
6235
|
+
"device_name": {
|
|
6236
|
+
"type": "string",
|
|
6237
|
+
"minLength": 1,
|
|
6238
|
+
"maxLength": 80,
|
|
6239
|
+
"description": "Human-readable device name used for the created CLI API key"
|
|
6240
|
+
},
|
|
6241
|
+
"metadata": {
|
|
6242
|
+
"type": "object",
|
|
6243
|
+
"additionalProperties": true,
|
|
6244
|
+
"description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
|
|
6245
|
+
}
|
|
6246
|
+
},
|
|
6247
|
+
"required": [
|
|
6248
|
+
"email",
|
|
6249
|
+
"signup_code",
|
|
6250
|
+
"terms_accepted"
|
|
6251
|
+
]
|
|
6252
|
+
},
|
|
6253
|
+
"responseSchema": {
|
|
6254
|
+
"type": "object",
|
|
6255
|
+
"properties": {
|
|
6256
|
+
"signup_token": {
|
|
6257
|
+
"type": "string",
|
|
6258
|
+
"description": "Opaque token used to verify or resend the pending CLI signup"
|
|
6259
|
+
},
|
|
6260
|
+
"email": {
|
|
6261
|
+
"type": "string",
|
|
6262
|
+
"format": "email"
|
|
6263
|
+
},
|
|
6264
|
+
"expires_in": {
|
|
6265
|
+
"type": "integer",
|
|
6266
|
+
"description": "Seconds until the pending signup expires"
|
|
6267
|
+
},
|
|
6268
|
+
"resend_after": {
|
|
6269
|
+
"type": "integer",
|
|
6270
|
+
"description": "Minimum seconds before requesting another verification email"
|
|
6271
|
+
},
|
|
6272
|
+
"verification_code_length": {
|
|
6273
|
+
"type": "integer",
|
|
6274
|
+
"description": "Number of digits in the emailed verification code"
|
|
6275
|
+
}
|
|
6276
|
+
},
|
|
6277
|
+
"required": [
|
|
6278
|
+
"signup_token",
|
|
6279
|
+
"email",
|
|
6280
|
+
"expires_in",
|
|
6281
|
+
"resend_after",
|
|
6282
|
+
"verification_code_length"
|
|
6283
|
+
]
|
|
6284
|
+
},
|
|
6285
|
+
"sdkName": "startCliSignup",
|
|
6286
|
+
"summary": "Start CLI account signup",
|
|
6287
|
+
"tag": "CLI",
|
|
6288
|
+
"tagCommand": "cli"
|
|
6289
|
+
},
|
|
6290
|
+
{
|
|
6291
|
+
"binaryResponse": false,
|
|
6292
|
+
"bodyRequired": true,
|
|
6293
|
+
"command": "verify-cli-signup",
|
|
6294
|
+
"description": "Verifies the email code for a CLI signup session, creates the account,\nredeems the reserved signup code, mints an org-scoped CLI API key, and\nreturns the raw key exactly once. This endpoint does not require an API key.\n",
|
|
6295
|
+
"hasJsonBody": true,
|
|
6296
|
+
"method": "POST",
|
|
6297
|
+
"operationId": "verifyCliSignup",
|
|
6298
|
+
"path": "/cli/signup/verify",
|
|
6299
|
+
"pathParams": [],
|
|
6300
|
+
"queryParams": [],
|
|
6301
|
+
"requestSchema": {
|
|
6302
|
+
"type": "object",
|
|
6303
|
+
"additionalProperties": false,
|
|
6304
|
+
"properties": {
|
|
6305
|
+
"signup_token": {
|
|
6306
|
+
"type": "string",
|
|
6307
|
+
"minLength": 1
|
|
6308
|
+
},
|
|
6309
|
+
"verification_code": {
|
|
6310
|
+
"type": "string",
|
|
6311
|
+
"minLength": 1,
|
|
6312
|
+
"maxLength": 32
|
|
6313
|
+
},
|
|
6314
|
+
"password": {
|
|
6315
|
+
"type": "string",
|
|
6316
|
+
"minLength": 1,
|
|
6317
|
+
"maxLength": 1024
|
|
6318
|
+
}
|
|
6319
|
+
},
|
|
6320
|
+
"required": [
|
|
6321
|
+
"signup_token",
|
|
6322
|
+
"verification_code",
|
|
6323
|
+
"password"
|
|
6324
|
+
]
|
|
6325
|
+
},
|
|
6326
|
+
"responseSchema": {
|
|
6327
|
+
"type": "object",
|
|
6328
|
+
"properties": {
|
|
6329
|
+
"api_key": {
|
|
6330
|
+
"type": "string",
|
|
6331
|
+
"description": "Newly-created API key for CLI authentication"
|
|
6332
|
+
},
|
|
6333
|
+
"key_id": {
|
|
6334
|
+
"type": "string",
|
|
6335
|
+
"format": "uuid"
|
|
6336
|
+
},
|
|
6337
|
+
"key_prefix": { "type": "string" },
|
|
6338
|
+
"org_id": {
|
|
6339
|
+
"type": "string",
|
|
6340
|
+
"format": "uuid"
|
|
6341
|
+
},
|
|
6342
|
+
"org_name": { "type": ["string", "null"] }
|
|
6343
|
+
},
|
|
6344
|
+
"required": [
|
|
6345
|
+
"api_key",
|
|
6346
|
+
"key_id",
|
|
6347
|
+
"key_prefix",
|
|
6348
|
+
"org_id",
|
|
6349
|
+
"org_name"
|
|
6350
|
+
]
|
|
6351
|
+
},
|
|
6352
|
+
"sdkName": "verifyCliSignup",
|
|
6353
|
+
"summary": "Verify CLI signup and create API key",
|
|
5827
6354
|
"tag": "CLI",
|
|
5828
6355
|
"tagCommand": "cli"
|
|
5829
6356
|
},
|
|
@@ -7497,7 +8024,7 @@ const operationManifest = [
|
|
|
7497
8024
|
"binaryResponse": false,
|
|
7498
8025
|
"bodyRequired": true,
|
|
7499
8026
|
"command": "create-function",
|
|
7500
|
-
"description": "Creates and deploys a new function. The handler must be a single\nESM module whose default export is an object with an async\n`fetch(request, env)` method (Workers-style). The gateway\nHMAC-verifies the POST against the org's webhook secret before\ninvoking the handler; the request body parses to an\n`email.received` event (see `EmailReceivedEvent` and the\nWebhook payload section for the full schema). Code is bundled\nbefore being uploaded; ship a single self-contained file rather\nthan relying on external imports.\n\n**Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`\n(optional) is capped at 5 MiB UTF-8
|
|
8027
|
+
"description": "Creates and deploys a new function. The handler must be a single\nESM module whose default export is an object with an async\n`fetch(request, env)` method (Workers-style). The gateway\nHMAC-verifies the POST against the org's webhook secret before\ninvoking the handler; the request body parses to an\n`email.received` event (see `EmailReceivedEvent` and the\nWebhook payload section for the full schema). Code is bundled\nbefore being uploaded; ship a single self-contained file rather\nthan relying on external imports.\n\n**Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`\n(optional) is capped at 5 MiB UTF-8, stored with each deployment\nattempt, and sent to the runtime so stack traces can resolve to\noriginal source files.\n\n**Auto-wiring.** On successful deploy, Primitive automatically\ncreates a webhook endpoint that delivers inbound mail to the\nfunction. There is nothing to configure on the Endpoints API\nfor this to work; the gateway URL returned here is for\nreference only and is not directly callable from outside.\n\n**Secrets.** New functions ship with the managed secrets\n(`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`) already\nbound. Add user-set secrets via\n`POST /functions/{id}/secrets`; secret writes only land in the\nrunning handler on the next redeploy.\n",
|
|
7501
8028
|
"hasJsonBody": true,
|
|
7502
8029
|
"method": "POST",
|
|
7503
8030
|
"operationId": "createFunction",
|
|
@@ -7523,7 +8050,7 @@ const operationManifest = [
|
|
|
7523
8050
|
"type": "string",
|
|
7524
8051
|
"minLength": 1,
|
|
7525
8052
|
"maxLength": 5242880,
|
|
7526
|
-
"description": "Optional source map for the bundle. Up to 5 MiB UTF-8.\nStored
|
|
8053
|
+
"description": "Optional source map for the bundle. Up to 5 MiB UTF-8.\nStored with the deployment attempt and sent to the runtime\nto symbolicate stack traces in the function's logs.\n"
|
|
7527
8054
|
}
|
|
7528
8055
|
},
|
|
7529
8056
|
"required": ["name", "code"]
|
|
@@ -8127,7 +8654,7 @@ const operationManifest = [
|
|
|
8127
8654
|
"binaryResponse": false,
|
|
8128
8655
|
"bodyRequired": true,
|
|
8129
8656
|
"command": "update-function",
|
|
8130
|
-
"description": "Replaces the function's source code with the body's `code` and\ntriggers a redeploy. Same size limits as `POST /functions`.\nUse this verb to push secret writes into the running handler:\npassing the same `code` re-runs the deploy and refreshes the\nbinding set with the latest values from the secrets table.\n\nOn
|
|
8657
|
+
"description": "Replaces the function's source code with the body's `code` and\ntriggers a redeploy. Same size limits as `POST /functions`.\nUse this verb to push secret writes into the running handler:\npassing the same `code` re-runs the deploy and refreshes the\nbinding set with the latest values from the secrets table.\n\nOn deploy failure, the previously-deployed code stays live; the\nruntime never serves a half-built bundle. The response uses\n`error.code` `deploy_failed`, and the function's `deploy_error`\nfield carries the latest deploy error for dashboard/API reads.\n",
|
|
8131
8658
|
"hasJsonBody": true,
|
|
8132
8659
|
"method": "PUT",
|
|
8133
8660
|
"operationId": "updateFunction",
|
|
@@ -9386,7 +9913,7 @@ const CREDENTIALS_FILE = "credentials.json";
|
|
|
9386
9913
|
const CREDENTIALS_LOCK_DIR = "credentials.lock";
|
|
9387
9914
|
const CREDENTIALS_LOCK_STALE_MS = 1800 * 1e3;
|
|
9388
9915
|
const MALFORMED_CREDENTIALS_HINT = "Run `primitive logout` and then `primitive login`.";
|
|
9389
|
-
function isRecord(value) {
|
|
9916
|
+
function isRecord$1(value) {
|
|
9390
9917
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
9391
9918
|
}
|
|
9392
9919
|
function requireString(value, key) {
|
|
@@ -9409,7 +9936,7 @@ var StaleCredentialFormatError = class extends Error {
|
|
|
9409
9936
|
}
|
|
9410
9937
|
};
|
|
9411
9938
|
function parseCredentials(raw) {
|
|
9412
|
-
if (!isRecord(raw)) throw new Error(`Stored Primitive CLI credentials are malformed: expected a JSON object. ${MALFORMED_CREDENTIALS_HINT}`);
|
|
9939
|
+
if (!isRecord$1(raw)) throw new Error(`Stored Primitive CLI credentials are malformed: expected a JSON object. ${MALFORMED_CREDENTIALS_HINT}`);
|
|
9413
9940
|
if (typeof raw.api_base_url_1 !== "string" && typeof raw.base_url === "string") throw new StaleCredentialFormatError();
|
|
9414
9941
|
const orgName = raw.org_name;
|
|
9415
9942
|
if (orgName !== null && typeof orgName !== "string") throw new Error(`Stored Primitive CLI credentials are malformed: org_name must be a string or null. ${MALFORMED_CREDENTIALS_HINT}`);
|
|
@@ -9582,7 +10109,7 @@ function formatFunctionEndpointRedirect(match) {
|
|
|
9582
10109
|
return [
|
|
9583
10110
|
"This is a function endpoint. Function endpoints are tested differently. Run:",
|
|
9584
10111
|
"",
|
|
9585
|
-
` primitive functions
|
|
10112
|
+
` primitive functions test --id ${match.functionId}`,
|
|
9586
10113
|
"",
|
|
9587
10114
|
`(pass the function id, not the endpoint id. endpoint_id=${match.endpointId} function_id=${match.functionId})`
|
|
9588
10115
|
].join("\n");
|
|
@@ -9753,26 +10280,26 @@ function coerceParameterValue(parameter, value) {
|
|
|
9753
10280
|
if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") return value;
|
|
9754
10281
|
throw new Errors.CLIError(`Unsupported flag value for --${parameter.name}`);
|
|
9755
10282
|
}
|
|
9756
|
-
function cliError$
|
|
10283
|
+
function cliError$5(message) {
|
|
9757
10284
|
return new Errors.CLIError(message, { exit: 1 });
|
|
9758
10285
|
}
|
|
9759
10286
|
function parseJson(source, flagLabel) {
|
|
9760
10287
|
try {
|
|
9761
10288
|
return JSON.parse(source);
|
|
9762
10289
|
} catch (error) {
|
|
9763
|
-
throw cliError$
|
|
10290
|
+
throw cliError$5(`${flagLabel} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
9764
10291
|
}
|
|
9765
10292
|
}
|
|
9766
10293
|
function readJsonBody(flags) {
|
|
9767
10294
|
const bodyFile = flags["body-file"];
|
|
9768
10295
|
const rawBody = flags["raw-body"];
|
|
9769
|
-
if (bodyFile && rawBody) throw cliError$
|
|
10296
|
+
if (bodyFile && rawBody) throw cliError$5("Use either --raw-body or --body-file, not both");
|
|
9770
10297
|
if (typeof bodyFile === "string") {
|
|
9771
10298
|
let contents;
|
|
9772
10299
|
try {
|
|
9773
10300
|
contents = readFileSync(bodyFile, "utf8");
|
|
9774
10301
|
} catch (error) {
|
|
9775
|
-
throw cliError$
|
|
10302
|
+
throw cliError$5(`Could not read --body-file ${bodyFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
9776
10303
|
}
|
|
9777
10304
|
return parseJson(contents, `--body-file ${bodyFile}`);
|
|
9778
10305
|
}
|
|
@@ -9782,7 +10309,7 @@ function readTextFileFlag(path, flagLabel) {
|
|
|
9782
10309
|
try {
|
|
9783
10310
|
return readFileSync(path, "utf8");
|
|
9784
10311
|
} catch (error) {
|
|
9785
|
-
throw cliError$
|
|
10312
|
+
throw cliError$5(`Could not read ${flagLabel} ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
9786
10313
|
}
|
|
9787
10314
|
}
|
|
9788
10315
|
function extractErrorPayload(raw) {
|
|
@@ -9954,14 +10481,14 @@ function collectValues(parameters, flags) {
|
|
|
9954
10481
|
return values;
|
|
9955
10482
|
}
|
|
9956
10483
|
const OPERATION_HINTS = {
|
|
9957
|
-
createFunction: "Tip: prefer `primitive functions
|
|
9958
|
-
updateFunction: "Tip: prefer `primitive functions
|
|
9959
|
-
createFunctionSecret: "Tip: prefer `primitive functions
|
|
9960
|
-
setFunctionSecret: "Tip: prefer `primitive functions
|
|
10484
|
+
createFunction: "Tip: prefer `primitive functions deploy --name <name> --file <bundle>` for file-input ergonomics. This raw command exists for callers passing JSON.",
|
|
10485
|
+
updateFunction: "Tip: prefer `primitive functions redeploy --id <id> --file <bundle>` for file-input ergonomics. This raw command exists for callers passing JSON.",
|
|
10486
|
+
createFunctionSecret: "Tip: prefer `primitive functions set-secret --id <id> --key <KEY> --value <value> [--redeploy]` for secret writes that also push the binding live. This raw command exists for callers passing JSON.",
|
|
10487
|
+
setFunctionSecret: "Tip: prefer `primitive functions set-secret --id <id> --key <KEY> --value <value> [--redeploy]` for secret writes that also push the binding live. This raw command exists for callers passing JSON."
|
|
9961
10488
|
};
|
|
9962
10489
|
function createOperationCommand(operation) {
|
|
9963
10490
|
const { flags, bodyFieldFlagToProperty } = buildFlags(operation);
|
|
9964
|
-
const baseDescription = operation.description
|
|
10491
|
+
const baseDescription = operation.description !== null && operation.description !== void 0 ? canonicalizeCliReferences(operation.description) : `${operation.method} ${operation.path}`;
|
|
9965
10492
|
const schemaSummary = operation.hasJsonBody ? renderRequestSchemaSummary(operation.requestSchema) : null;
|
|
9966
10493
|
const hint = OPERATION_HINTS[operation.sdkName];
|
|
9967
10494
|
const descriptionWithSchema = schemaSummary ? `${baseDescription}\n\n${schemaSummary}` : baseDescription;
|
|
@@ -10067,12 +10594,15 @@ function createOperationCommand(operation) {
|
|
|
10067
10594
|
return OperationCommand;
|
|
10068
10595
|
}
|
|
10069
10596
|
const EMPTY_RESULT_HINTS = {
|
|
10070
|
-
listDeliveries: "(no results) No webhook deliveries logged yet. If you have an endpoint configured but expected to see test fires here: test deliveries from `primitive endpoints
|
|
10071
|
-
listEndpoints: "(no results) No webhook endpoints configured. Add one with `primitive endpoints
|
|
10072
|
-
listEmails: "(no results) No inbound emails received yet on this account. Send one to a verified domain to populate this list. For a compact view, prefer `primitive emails
|
|
10073
|
-
listDomains: "(no results) No domains on this account. Add one with `primitive domains
|
|
10597
|
+
listDeliveries: "(no results) No webhook deliveries logged yet. If you have an endpoint configured but expected to see test fires here: test deliveries from `primitive endpoints test` are NOT logged in this list, they're synchronous and visible only in the test-endpoint command's response. Real deliveries are logged when an inbound `email.received` event fans out to your endpoints. If you have no endpoints, run `primitive endpoints list` to check.",
|
|
10598
|
+
listEndpoints: "(no results) No webhook endpoints configured. Add one with `primitive endpoints create --url <your-url>`.",
|
|
10599
|
+
listEmails: "(no results) No inbound emails received yet on this account. Send one to a verified domain to populate this list. For a compact view, prefer `primitive emails latest`.",
|
|
10600
|
+
listDomains: "(no results) No domains on this account. Add one with `primitive domains add --domain <yourdomain.example>`.",
|
|
10074
10601
|
listFilters: "(no results) No filter rules configured."
|
|
10075
10602
|
};
|
|
10603
|
+
function canonicalizeCliReferences(description) {
|
|
10604
|
+
return description.replaceAll("`primitive emails:latest`", "`primitive emails latest`").replaceAll("`primitive describe emails:get-email | jq '.responseSchema.properties'`", "`primitive describe emails:get | jq '.responseSchema.properties'`");
|
|
10605
|
+
}
|
|
10076
10606
|
//#endregion
|
|
10077
10607
|
//#region src/oclif/commands/doctor.ts
|
|
10078
10608
|
const MIN_NODE_MAJOR = 22;
|
|
@@ -10233,7 +10763,7 @@ async function checkDomains(opts) {
|
|
|
10233
10763
|
if (result.error) return {
|
|
10234
10764
|
status: "warn",
|
|
10235
10765
|
message: "could not list domains",
|
|
10236
|
-
hint: "Run `primitive domains
|
|
10766
|
+
hint: "Run `primitive domains list` for the full error envelope."
|
|
10237
10767
|
};
|
|
10238
10768
|
const rows = result.data?.data ?? [];
|
|
10239
10769
|
const active = rows.filter((row) => row.is_active === true);
|
|
@@ -10245,7 +10775,7 @@ async function checkDomains(opts) {
|
|
|
10245
10775
|
if (active.length === 0) return {
|
|
10246
10776
|
status: "warn",
|
|
10247
10777
|
message: `${rows.length} domain(s), none active`,
|
|
10248
|
-
hint: "Run `primitive domains
|
|
10778
|
+
hint: "Run `primitive domains verify --id <id>` for any domain you intend to send / receive on."
|
|
10249
10779
|
};
|
|
10250
10780
|
return {
|
|
10251
10781
|
status: "ok",
|
|
@@ -10374,11 +10904,11 @@ function formatHeader(idWidth) {
|
|
|
10374
10904
|
return `${"ID".padEnd(idWidth)} ${"RECEIVED (UTC)".padEnd(RECEIVED_DISPLAY_WIDTH)} ${"FROM".padEnd(ADDRESS_DISPLAY_WIDTH)} ${"TO".padEnd(ADDRESS_DISPLAY_WIDTH)} SUBJECT`;
|
|
10375
10905
|
}
|
|
10376
10906
|
var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
10377
|
-
static description = `Print the N most recent inbound emails as a one-line-per-row text table. Designed for quick triage and visual scanning. For programmatic access, use \`primitive emails
|
|
10907
|
+
static description = `Print the N most recent inbound emails as a one-line-per-row text table. Designed for quick triage and visual scanning. For programmatic access, use \`primitive emails list\` (full JSON envelope, cursor pagination, filters) or pass \`--json\` here for the same raw shape without pagination/filters.
|
|
10378
10908
|
|
|
10379
10909
|
ID display is TTY-aware. When STDOUT is a terminal, the table truncates each row's id to the first ${ID_DISPLAY_WIDTH_SHORT} characters for readability. When STDOUT is piped or redirected (the row stream is being consumed by another command), the full UUID is printed so the id can be fed straight back into \`emails:get-email\`, \`emails:delete-email\`, etc. without a separate \`--json\` round-trip.
|
|
10380
10910
|
|
|
10381
|
-
Output streams: the column header line is written to STDERR so the row data on STDOUT stays grep/awk-friendly. \`--json\` writes everything (including the envelope) to STDOUT and is equivalent to running \`emails
|
|
10911
|
+
Output streams: the column header line is written to STDERR so the row data on STDOUT stays grep/awk-friendly. \`--json\` writes everything (including the envelope) to STDOUT and is equivalent to running \`emails list --limit N\` for the same N.`;
|
|
10382
10912
|
static summary = "Show the most recent inbound emails as a compact table";
|
|
10383
10913
|
static examples = [
|
|
10384
10914
|
"<%= config.bin %> emails latest",
|
|
@@ -10558,7 +11088,7 @@ function sleep$1(ms) {
|
|
|
10558
11088
|
//#endregion
|
|
10559
11089
|
//#region src/oclif/commands/emails-wait.ts
|
|
10560
11090
|
const DEFAULT_WAIT_TIMEOUT_SECONDS$1 = 300;
|
|
10561
|
-
function cliError$
|
|
11091
|
+
function cliError$4(message) {
|
|
10562
11092
|
return new Errors.CLIError(message, { exit: 1 });
|
|
10563
11093
|
}
|
|
10564
11094
|
var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
@@ -10638,7 +11168,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
10638
11168
|
try {
|
|
10639
11169
|
since = sinceFromFlags(flags);
|
|
10640
11170
|
} catch (error) {
|
|
10641
|
-
throw cliError$
|
|
11171
|
+
throw cliError$4(error instanceof Error ? error.message : String(error));
|
|
10642
11172
|
}
|
|
10643
11173
|
const filters = filtersFromFlags(flags);
|
|
10644
11174
|
const deadline = flags.timeout === 0 ? null : Date.now() + flags.timeout * 1e3;
|
|
@@ -10689,7 +11219,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
10689
11219
|
};
|
|
10690
11220
|
//#endregion
|
|
10691
11221
|
//#region src/oclif/commands/emails-watch.ts
|
|
10692
|
-
function cliError$
|
|
11222
|
+
function cliError$3(message) {
|
|
10693
11223
|
return new Errors.CLIError(message, { exit: 1 });
|
|
10694
11224
|
}
|
|
10695
11225
|
var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
@@ -10766,7 +11296,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
10766
11296
|
try {
|
|
10767
11297
|
since = sinceFromFlags(flags);
|
|
10768
11298
|
} catch (error) {
|
|
10769
|
-
throw cliError$
|
|
11299
|
+
throw cliError$3(error instanceof Error ? error.message : String(error));
|
|
10770
11300
|
}
|
|
10771
11301
|
const filters = filtersFromFlags(flags);
|
|
10772
11302
|
const deadline = flags.seconds ? Date.now() + flags.seconds * 1e3 : null;
|
|
@@ -10996,14 +11526,14 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
10996
11526
|
final update-function with the same bundle so the running handler
|
|
10997
11527
|
picks up the bindings. If a secret write fails after the create
|
|
10998
11528
|
step the function exists with whatever secrets succeeded and the
|
|
10999
|
-
redeploy has NOT fired; re-run \`primitive functions
|
|
11000
|
-
for the missing keys, then \`primitive functions
|
|
11529
|
+
redeploy has NOT fired; re-run \`primitive functions set-secret\`
|
|
11530
|
+
for the missing keys, then \`primitive functions redeploy\` to
|
|
11001
11531
|
push them live.`;
|
|
11002
11532
|
static summary = "Deploy a new function from a bundled handler file";
|
|
11003
11533
|
static examples = [
|
|
11004
|
-
"<%= config.bin %> functions
|
|
11005
|
-
"<%= config.bin %> functions
|
|
11006
|
-
"<%= config.bin %> functions
|
|
11534
|
+
"<%= config.bin %> functions deploy --name forwarder --file ./bundle.js",
|
|
11535
|
+
"<%= config.bin %> functions deploy --name forwarder --file ./bundle.js --source-map-file ./bundle.js.map",
|
|
11536
|
+
"<%= config.bin %> functions deploy --name forwarder --file ./bundle.js --secret OPENAI_KEY=sk-... --secret OWNER_EMAIL=me@example.com"
|
|
11007
11537
|
];
|
|
11008
11538
|
static flags = {
|
|
11009
11539
|
"api-key": Flags.string({
|
|
@@ -11028,7 +11558,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
11028
11558
|
description: "Path to the bundled ESM handler file (single self-contained module). Loaded as the `code` body field.",
|
|
11029
11559
|
required: true
|
|
11030
11560
|
}),
|
|
11031
|
-
"source-map-file": Flags.string({ description: "Optional path to a source map for the bundle. Stored
|
|
11561
|
+
"source-map-file": Flags.string({ description: "Optional path to a source map for the bundle. Stored with the deployment attempt and used to symbolicate stack traces in function logs." }),
|
|
11032
11562
|
secret: Flags.string({
|
|
11033
11563
|
description: `Secret KEY=VALUE to seed on the deployed function. Repeatable. KEY must match \`^[A-Z_][A-Z0-9_]*$\`; VALUE may contain \`=\` (only the first \`=\` is treated as a delimiter). Each KEY may only appear once per command. Passing one or more --secret flags fans out the deploy to create-function, set-secret per pair, then a final redeploy so the running handler picks up the bindings. ${SECRET_FLAG_SECURITY_NOTE}`,
|
|
11034
11564
|
multiple: true
|
|
@@ -11103,10 +11633,10 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
11103
11633
|
const succeeded = outcome.succeededKeys.length > 0 ? outcome.succeededKeys.join(", ") : "(none)";
|
|
11104
11634
|
const pending = outcome.pendingKeys.length > 0 ? outcome.pendingKeys.join(", ") : "(none)";
|
|
11105
11635
|
const allMissing = [outcome.failedKey, ...outcome.pendingKeys].join(", ");
|
|
11106
|
-
process.stderr.write(`Function ${outcome.created.name} (${outcome.created.id}) was created, but writing secret ${outcome.failedKey} failed; succeeded keys so far: ${succeeded}; keys not yet attempted: ${pending}. The redeploy is NOT yet live. Re-run \`primitive functions
|
|
11636
|
+
process.stderr.write(`Function ${outcome.created.name} (${outcome.created.id}) was created, but writing secret ${outcome.failedKey} failed; succeeded keys so far: ${succeeded}; keys not yet attempted: ${pending}. The redeploy is NOT yet live. Re-run \`primitive functions set-secret\` for each of [${allMissing}], then \`primitive functions redeploy --id ${outcome.created.id} --file <bundle>\` to push them live.\n`);
|
|
11107
11637
|
} else if (outcome.stage === "redeploy") {
|
|
11108
11638
|
const succeeded = outcome.succeededKeys.length > 0 ? outcome.succeededKeys.join(", ") : "(none)";
|
|
11109
|
-
process.stderr.write(`Function ${outcome.created.name} (${outcome.created.id}) was created and secrets [${succeeded}] were written, but the final redeploy failed; the new bindings are NOT yet live. Re-run \`primitive functions
|
|
11639
|
+
process.stderr.write(`Function ${outcome.created.name} (${outcome.created.id}) was created and secrets [${succeeded}] were written, but the final redeploy failed; the new bindings are NOT yet live. Re-run \`primitive functions redeploy --id ${outcome.created.id} --file <bundle>\` once the cause is fixed.\n`);
|
|
11110
11640
|
}
|
|
11111
11641
|
writeErrorWithHints(outcome.payload);
|
|
11112
11642
|
removeStaleSavedCredentialOnUnauthorized({
|
|
@@ -11123,8 +11653,8 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
11123
11653
|
};
|
|
11124
11654
|
//#endregion
|
|
11125
11655
|
//#region src/oclif/commands/functions-init.ts
|
|
11126
|
-
const SDK_VERSION_RANGE = "^0.
|
|
11127
|
-
const CLI_VERSION_RANGE = "^0.
|
|
11656
|
+
const SDK_VERSION_RANGE = "^0.28.0";
|
|
11657
|
+
const CLI_VERSION_RANGE = "^0.28.0";
|
|
11128
11658
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
11129
11659
|
const VALID_NAME = /^[a-z0-9][a-z0-9_-]{0,62}$/;
|
|
11130
11660
|
function isValidFunctionName(name) {
|
|
@@ -11269,8 +11799,8 @@ function renderPackageJson(name) {
|
|
|
11269
11799
|
type: "module",
|
|
11270
11800
|
scripts: {
|
|
11271
11801
|
build: "node build.mjs",
|
|
11272
|
-
deploy: `npm run build && primitive functions
|
|
11273
|
-
redeploy: "npm run build && primitive functions
|
|
11802
|
+
deploy: `npm run build && primitive functions deploy --name ${name} --file ./dist/handler.js`,
|
|
11803
|
+
redeploy: "npm run build && primitive functions redeploy --id $PRIMITIVE_FUNCTION_ID --file ./dist/handler.js"
|
|
11274
11804
|
},
|
|
11275
11805
|
dependencies: { "@primitivedotdev/sdk": SDK_VERSION_RANGE },
|
|
11276
11806
|
devDependencies: {
|
|
@@ -11341,7 +11871,7 @@ npm run build
|
|
|
11341
11871
|
npm run deploy
|
|
11342
11872
|
\`\`\`
|
|
11343
11873
|
|
|
11344
|
-
The deploy step calls \`primitive functions
|
|
11874
|
+
The deploy step calls \`primitive functions deploy\` (provided by the
|
|
11345
11875
|
\`@primitivedotdev/cli\` package; install with
|
|
11346
11876
|
\`npm install -g @primitivedotdev/cli\` or run via
|
|
11347
11877
|
\`npx @primitivedotdev/cli@latest <command>\`). It requires
|
|
@@ -11417,12 +11947,12 @@ var FunctionsInitCommand = class FunctionsInitCommand extends Command {
|
|
|
11417
11947
|
\`@primitivedotdev/sdk/api\` and demonstrates the canonical pattern:
|
|
11418
11948
|
parse the email.received event, send a reply via the SDK, return a
|
|
11419
11949
|
JSON envelope. The build script uses esbuild's JS API and emits
|
|
11420
|
-
./dist/handler.js, ready to hand to \`primitive functions
|
|
11950
|
+
./dist/handler.js, ready to hand to \`primitive functions deploy --file\`.
|
|
11421
11951
|
|
|
11422
11952
|
Refuses to overwrite an existing directory. Use --out-dir to pick a
|
|
11423
11953
|
different target path than ./<name>/.`;
|
|
11424
|
-
static summary = "Scaffold a new Primitive Function project ready for functions
|
|
11425
|
-
static examples = ["<%= config.bin %> functions
|
|
11954
|
+
static summary = "Scaffold a new Primitive Function project ready for functions deploy";
|
|
11955
|
+
static examples = ["<%= config.bin %> functions init my-fn", "<%= config.bin %> functions init my-fn --out-dir ./functions/my-fn"];
|
|
11426
11956
|
static args = { name: Args.string({
|
|
11427
11957
|
description: "Function name. Lowercase letters, digits, hyphens, underscores. 1-63 chars. Used as the directory name (when --out-dir is not set) and as the package.json name.",
|
|
11428
11958
|
required: true
|
|
@@ -11440,7 +11970,7 @@ var FunctionsInitCommand = class FunctionsInitCommand extends Command {
|
|
|
11440
11970
|
this.log(` cd ${outDir}`);
|
|
11441
11971
|
this.log(" npm install");
|
|
11442
11972
|
this.log(" npm run build");
|
|
11443
|
-
this.log(` primitive functions
|
|
11973
|
+
this.log(` primitive functions deploy --name ${args.name} --file ./dist/handler.js`);
|
|
11444
11974
|
}
|
|
11445
11975
|
};
|
|
11446
11976
|
//#endregion
|
|
@@ -11524,9 +12054,9 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
11524
12054
|
API calls (set-secret per pair, then update-function).`;
|
|
11525
12055
|
static summary = "Redeploy a function from a bundled handler file";
|
|
11526
12056
|
static examples = [
|
|
11527
|
-
"<%= config.bin %> functions
|
|
11528
|
-
"<%= config.bin %> functions
|
|
11529
|
-
"<%= config.bin %> functions
|
|
12057
|
+
"<%= config.bin %> functions redeploy --id <fn-id> --file ./bundle.js",
|
|
12058
|
+
"<%= config.bin %> functions redeploy --id <fn-id> --file ./bundle.js --source-map-file ./bundle.js.map",
|
|
12059
|
+
"<%= config.bin %> functions redeploy --id <fn-id> --file ./bundle.js --secret OPENAI_KEY=sk-... --secret OWNER_EMAIL=me@example.com"
|
|
11530
12060
|
];
|
|
11531
12061
|
static flags = {
|
|
11532
12062
|
"api-key": Flags.string({
|
|
@@ -11617,10 +12147,10 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
11617
12147
|
const succeeded = outcome.succeededKeys.length > 0 ? outcome.succeededKeys.join(", ") : "(none)";
|
|
11618
12148
|
const pending = outcome.pendingKeys.length > 0 ? outcome.pendingKeys.join(", ") : "(none)";
|
|
11619
12149
|
const allMissing = [outcome.failedKey, ...outcome.pendingKeys].join(", ");
|
|
11620
|
-
process.stderr.write(`Writing secret ${outcome.failedKey} failed before the redeploy; succeeded keys so far: ${succeeded}; keys not yet attempted: ${pending}. The new bundle has NOT been deployed. Re-run \`primitive functions
|
|
12150
|
+
process.stderr.write(`Writing secret ${outcome.failedKey} failed before the redeploy; succeeded keys so far: ${succeeded}; keys not yet attempted: ${pending}. The new bundle has NOT been deployed. Re-run \`primitive functions set-secret\` for each of [${allMissing}], then \`primitive functions redeploy --id ${flags.id} --file <bundle>\` to push them live.\n`);
|
|
11621
12151
|
} else if (outcome.stage === "redeploy") {
|
|
11622
12152
|
const succeeded = outcome.succeededKeys.length > 0 ? outcome.succeededKeys.join(", ") : "(none)";
|
|
11623
|
-
process.stderr.write(`Secrets [${succeeded}] were written, but the redeploy step failed; the new bindings are NOT yet live. Re-run \`primitive functions
|
|
12153
|
+
process.stderr.write(`Secrets [${succeeded}] were written, but the redeploy step failed; the new bindings are NOT yet live. Re-run \`primitive functions redeploy --id ${flags.id} --file <bundle>\` once the cause is fixed.\n`);
|
|
11624
12154
|
}
|
|
11625
12155
|
writeErrorWithHints(outcome.payload);
|
|
11626
12156
|
removeStaleSavedCredentialOnUnauthorized({
|
|
@@ -11702,7 +12232,7 @@ async function runSetSecret(api, params) {
|
|
|
11702
12232
|
};
|
|
11703
12233
|
}
|
|
11704
12234
|
var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command {
|
|
11705
|
-
static description = `Write a function secret and optionally redeploy so the new value lands in the running handler. Agent-grade shortcut for functions
|
|
12235
|
+
static description = `Write a function secret and optionally redeploy so the new value lands in the running handler. Agent-grade shortcut for functions set-function-secret + functions redeploy.
|
|
11706
12236
|
|
|
11707
12237
|
Without --redeploy this is a plain secret upsert: the value is
|
|
11708
12238
|
encrypted at rest but is NOT visible to the running handler until
|
|
@@ -11714,7 +12244,7 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
11714
12244
|
underscores; first character is a letter or underscore). System-
|
|
11715
12245
|
managed keys are reserved and rejected.`;
|
|
11716
12246
|
static summary = "Write a function secret (optionally redeploying to push it live)";
|
|
11717
|
-
static examples = ["<%= config.bin %> functions
|
|
12247
|
+
static examples = ["<%= config.bin %> functions set-secret --id <fn-id> --key API_TOKEN --value abc123", "<%= config.bin %> functions set-secret --id <fn-id> --key API_TOKEN --value abc123 --redeploy"];
|
|
11718
12248
|
static flags = {
|
|
11719
12249
|
"api-key": Flags.string({
|
|
11720
12250
|
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
|
|
@@ -11742,7 +12272,7 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
11742
12272
|
description: "Secret value (up to 4096 UTF-8 bytes). Encrypted at rest.",
|
|
11743
12273
|
required: true
|
|
11744
12274
|
}),
|
|
11745
|
-
redeploy: Flags.boolean({ description: "Also redeploy the function with its current code so the new value lands in the running handler. Without this, the secret is written but not visible to the handler until the next deploy. Note:
|
|
12275
|
+
redeploy: Flags.boolean({ description: "Also redeploy the function with its current code so the new value lands in the running handler. Without this, the secret is written but not visible to the handler until the next deploy. Note: when --redeploy re-uploads the function's current live code without a sourceMap field, the API preserves the current stored source map. Use `functions redeploy --file <bundle.js> --source-map-file <bundle.js.map>` to replace or restore a map." }),
|
|
11746
12276
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
11747
12277
|
};
|
|
11748
12278
|
async run() {
|
|
@@ -11793,8 +12323,8 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
11793
12323
|
value: flags.value
|
|
11794
12324
|
});
|
|
11795
12325
|
if (outcome.kind === "error") {
|
|
11796
|
-
if (outcome.stage === "get-function") process.stderr.write("Secret was written, but reading current function code for redeploy failed; the secret is NOT yet live. Re-run with --redeploy, or call `primitive functions
|
|
11797
|
-
else if (outcome.stage === "redeploy") process.stderr.write("Secret was written, but the redeploy step failed; the secret is NOT yet live. Inspect the function's deploy_error and re-run `primitive functions
|
|
12326
|
+
if (outcome.stage === "get-function") process.stderr.write("Secret was written, but reading current function code for redeploy failed; the secret is NOT yet live. Re-run with --redeploy, or call `primitive functions redeploy --id <id> --file <bundle>` once you have the bundle.\n");
|
|
12327
|
+
else if (outcome.stage === "redeploy") process.stderr.write("Secret was written, but the redeploy step failed; the secret is NOT yet live. Inspect the function's deploy_error and re-run `primitive functions redeploy --id <id> --file <bundle>` once the cause is fixed.\n");
|
|
11798
12328
|
writeErrorWithHints(outcome.payload);
|
|
11799
12329
|
removeStaleSavedCredentialOnUnauthorized({
|
|
11800
12330
|
...authFailureContext,
|
|
@@ -11815,10 +12345,10 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
11815
12345
|
static description = "Send a real test email through MX to trigger this function. With --wait, blocks until the function has processed the inbound; with --show-sends, also prints any outbound sends the function emitted in response.";
|
|
11816
12346
|
static summary = "Trigger a test invocation; with --wait, watch it land";
|
|
11817
12347
|
static examples = [
|
|
11818
|
-
"<%= config.bin %> functions
|
|
11819
|
-
"<%= config.bin %> functions
|
|
11820
|
-
"<%= config.bin %> functions
|
|
11821
|
-
"<%= config.bin %> functions
|
|
12348
|
+
"<%= config.bin %> functions test --id <fn-id>",
|
|
12349
|
+
"<%= config.bin %> functions test --id <fn-id> --local-part summarize",
|
|
12350
|
+
"<%= config.bin %> functions test --id <fn-id> --wait --show-sends",
|
|
12351
|
+
"<%= config.bin %> functions test --id <fn-id> --local-part summarize --wait --timeout 120"
|
|
11822
12352
|
];
|
|
11823
12353
|
static flags = {
|
|
11824
12354
|
"api-key": Flags.string({
|
|
@@ -11975,7 +12505,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
11975
12505
|
//#endregion
|
|
11976
12506
|
//#region src/oclif/commands/login.ts
|
|
11977
12507
|
const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
|
|
11978
|
-
function cliError$
|
|
12508
|
+
function cliError$2(message) {
|
|
11979
12509
|
return new Errors.CLIError(message, { exit: 1 });
|
|
11980
12510
|
}
|
|
11981
12511
|
function sleep(ms) {
|
|
@@ -11994,10 +12524,10 @@ function openBrowser(url) {
|
|
|
11994
12524
|
child.on("error", () => void 0);
|
|
11995
12525
|
child.unref();
|
|
11996
12526
|
}
|
|
11997
|
-
function unwrapData$
|
|
12527
|
+
function unwrapData$2(value) {
|
|
11998
12528
|
return value?.data ?? null;
|
|
11999
12529
|
}
|
|
12000
|
-
function retryAfterSeconds(result) {
|
|
12530
|
+
function retryAfterSeconds$1(result) {
|
|
12001
12531
|
const raw = result.response?.headers.get("retry-after");
|
|
12002
12532
|
if (!raw) return null;
|
|
12003
12533
|
const parsed = Number.parseInt(raw, 10);
|
|
@@ -12061,7 +12591,7 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
12061
12591
|
try {
|
|
12062
12592
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
12063
12593
|
} catch (error) {
|
|
12064
|
-
throw cliError$
|
|
12594
|
+
throw cliError$2(error instanceof Error ? error.message : String(error));
|
|
12065
12595
|
}
|
|
12066
12596
|
try {
|
|
12067
12597
|
await this.runWithCredentialLock(flags);
|
|
@@ -12090,8 +12620,8 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
12090
12620
|
if (existingStatus.status === "removed_stale") process.stderr.write("Continuing with a new Primitive CLI login...\n");
|
|
12091
12621
|
else if (existingStatus.status === "blocked") {
|
|
12092
12622
|
writeErrorWithHints(existingStatus.payload);
|
|
12093
|
-
throw cliError$
|
|
12094
|
-
} else throw cliError$
|
|
12623
|
+
throw cliError$2(existingStatus.message);
|
|
12624
|
+
} else throw cliError$2(`Already logged in${existing.org_name ? ` for ${existing.org_name}` : ""}. Run \`primitive logout\` before logging in again.`);
|
|
12095
12625
|
}
|
|
12096
12626
|
const apiClient = new PrimitiveApiClient({ apiBaseUrl1 });
|
|
12097
12627
|
const started = await startCliLogin({
|
|
@@ -12101,10 +12631,10 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
12101
12631
|
});
|
|
12102
12632
|
if (started.error) {
|
|
12103
12633
|
writeErrorWithHints(extractErrorPayload(started.error));
|
|
12104
|
-
throw cliError$
|
|
12634
|
+
throw cliError$2("Could not start Primitive CLI login.");
|
|
12105
12635
|
}
|
|
12106
|
-
const start = unwrapData$
|
|
12107
|
-
if (!start) throw cliError$
|
|
12636
|
+
const start = unwrapData$2(started.data);
|
|
12637
|
+
if (!start) throw cliError$2("Primitive API returned an empty CLI login response.");
|
|
12108
12638
|
process.stderr.write(`Your login code is: ${start.user_code}\n`);
|
|
12109
12639
|
if (!flags["no-browser"]) {
|
|
12110
12640
|
openBrowser(start.verification_uri_complete);
|
|
@@ -12124,8 +12654,8 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
12124
12654
|
responseStyle: "fields"
|
|
12125
12655
|
});
|
|
12126
12656
|
if (polled.data) {
|
|
12127
|
-
const login = unwrapData$
|
|
12128
|
-
if (!login) throw cliError$
|
|
12657
|
+
const login = unwrapData$2(polled.data);
|
|
12658
|
+
if (!login) throw cliError$2("Primitive API returned an empty CLI poll response.");
|
|
12129
12659
|
saveCliCredentials(this.config.configDir, {
|
|
12130
12660
|
api_key: login.api_key,
|
|
12131
12661
|
api_base_url_1: apiBaseUrl1,
|
|
@@ -12147,25 +12677,25 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
12147
12677
|
continue;
|
|
12148
12678
|
}
|
|
12149
12679
|
if (code === API_ERROR_CODES.slowDown) {
|
|
12150
|
-
interval = Math.min(retryAfterSeconds(polled) ?? interval + 5, MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS);
|
|
12680
|
+
interval = Math.min(retryAfterSeconds$1(polled) ?? interval + 5, MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS);
|
|
12151
12681
|
nextPollDelay = interval;
|
|
12152
12682
|
continue;
|
|
12153
12683
|
}
|
|
12154
|
-
if (code === API_ERROR_CODES.accessDenied) throw cliError$
|
|
12155
|
-
if (code === API_ERROR_CODES.expiredToken) throw cliError$
|
|
12156
|
-
if (code === API_ERROR_CODES.invalidDeviceCode) throw cliError$
|
|
12684
|
+
if (code === API_ERROR_CODES.accessDenied) throw cliError$2("Primitive CLI login was denied in the browser.");
|
|
12685
|
+
if (code === API_ERROR_CODES.expiredToken) throw cliError$2("Primitive CLI login expired. Run `primitive login` again.");
|
|
12686
|
+
if (code === API_ERROR_CODES.invalidDeviceCode) throw cliError$2("Primitive CLI login device code is invalid. Run `primitive login` again.");
|
|
12157
12687
|
writeErrorWithHints(payload);
|
|
12158
|
-
throw cliError$
|
|
12688
|
+
throw cliError$2("Primitive CLI login failed while polling for approval.");
|
|
12159
12689
|
}
|
|
12160
|
-
throw cliError$
|
|
12690
|
+
throw cliError$2("Primitive CLI login expired. Run `primitive login` again.");
|
|
12161
12691
|
}
|
|
12162
12692
|
};
|
|
12163
12693
|
//#endregion
|
|
12164
12694
|
//#region src/oclif/commands/logout.ts
|
|
12165
|
-
function cliError(message) {
|
|
12695
|
+
function cliError$1(message) {
|
|
12166
12696
|
return new Errors.CLIError(message, { exit: 1 });
|
|
12167
12697
|
}
|
|
12168
|
-
function unwrapData(value) {
|
|
12698
|
+
function unwrapData$1(value) {
|
|
12169
12699
|
return value?.data ?? null;
|
|
12170
12700
|
}
|
|
12171
12701
|
var LogoutCommand = class LogoutCommand extends Command {
|
|
@@ -12183,7 +12713,7 @@ var LogoutCommand = class LogoutCommand extends Command {
|
|
|
12183
12713
|
try {
|
|
12184
12714
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
12185
12715
|
} catch (error) {
|
|
12186
|
-
throw cliError(error instanceof Error ? error.message : String(error));
|
|
12716
|
+
throw cliError$1(error instanceof Error ? error.message : String(error));
|
|
12187
12717
|
}
|
|
12188
12718
|
try {
|
|
12189
12719
|
await this.runWithCredentialLock(flags);
|
|
@@ -12202,7 +12732,7 @@ var LogoutCommand = class LogoutCommand extends Command {
|
|
|
12202
12732
|
process.exitCode = 1;
|
|
12203
12733
|
return;
|
|
12204
12734
|
}
|
|
12205
|
-
if (!credentials) throw cliError("Not logged in. Run `primitive login` to create saved CLI credentials.");
|
|
12735
|
+
if (!credentials) throw cliError$1("Not logged in. Run `primitive login` to create saved CLI credentials.");
|
|
12206
12736
|
const apiBaseUrl1 = flags["api-base-url-1"] ? normalizeApiBaseUrl1(flags["api-base-url-1"]) : credentials.api_base_url_1;
|
|
12207
12737
|
const apiClient = new PrimitiveApiClient({
|
|
12208
12738
|
apiKey: credentials.api_key,
|
|
@@ -12224,15 +12754,101 @@ var LogoutCommand = class LogoutCommand extends Command {
|
|
|
12224
12754
|
return;
|
|
12225
12755
|
}
|
|
12226
12756
|
writeErrorWithHints(payload);
|
|
12227
|
-
throw cliError("Could not revoke the saved Primitive CLI API key.");
|
|
12757
|
+
throw cliError$1("Could not revoke the saved Primitive CLI API key.");
|
|
12228
12758
|
}
|
|
12229
|
-
const logout = unwrapData(result.data);
|
|
12759
|
+
const logout = unwrapData$1(result.data);
|
|
12230
12760
|
deleteCliCredentials(this.config.configDir);
|
|
12231
12761
|
const keyId = logout?.key_id ?? credentials.key_id;
|
|
12232
12762
|
process.stderr.write(`Logged out and revoked API key ${keyId}.\n`);
|
|
12233
12763
|
}
|
|
12234
12764
|
};
|
|
12235
12765
|
//#endregion
|
|
12766
|
+
//#region src/oclif/commands/reply.ts
|
|
12767
|
+
var ReplyCommand = class ReplyCommand extends Command {
|
|
12768
|
+
static description = `Reply to an inbound email.
|
|
12769
|
+
|
|
12770
|
+
The API derives recipients, the Re: subject, and threading headers from the inbound email id. Use \`primitive send --in-reply-to <message-id>\` only when you need to thread against a raw Message-Id instead of an inbound email stored by Primitive.`;
|
|
12771
|
+
static summary = "Reply to an inbound email";
|
|
12772
|
+
static examples = [
|
|
12773
|
+
"<%= config.bin %> reply --id <inbound-email-id> --body 'Thanks, got it.'",
|
|
12774
|
+
"<%= config.bin %> reply --id <inbound-email-id> --html '<p>Thanks, got it.</p>' --wait",
|
|
12775
|
+
"<%= config.bin %> reply --id <inbound-email-id> --from 'Support <support@example.com>' --body 'Thanks!'"
|
|
12776
|
+
];
|
|
12777
|
+
static flags = {
|
|
12778
|
+
"api-key": Flags.string({
|
|
12779
|
+
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
|
|
12780
|
+
env: "PRIMITIVE_API_KEY"
|
|
12781
|
+
}),
|
|
12782
|
+
"api-base-url-1": Flags.string({
|
|
12783
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
12784
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
12785
|
+
hidden: true
|
|
12786
|
+
}),
|
|
12787
|
+
"api-base-url-2": Flags.string({
|
|
12788
|
+
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
12789
|
+
env: "PRIMITIVE_API_BASE_URL_2",
|
|
12790
|
+
hidden: true
|
|
12791
|
+
}),
|
|
12792
|
+
id: Flags.string({
|
|
12793
|
+
description: "Inbound email id to reply to.",
|
|
12794
|
+
required: true
|
|
12795
|
+
}),
|
|
12796
|
+
body: Flags.string({ description: "Plain-text reply body. Either --body or --html (or both) is required." }),
|
|
12797
|
+
html: Flags.string({ description: "HTML reply body. Either --body or --html (or both) is required." }),
|
|
12798
|
+
from: Flags.string({ description: "Optional From header override. Defaults to the inbound recipient." }),
|
|
12799
|
+
wait: Flags.boolean({ description: "Block until the receiving MTA returns an outcome. Without --wait, the call returns once Primitive has accepted the reply for delivery." }),
|
|
12800
|
+
"wait-timeout-ms": Flags.integer({ description: "Maximum time to wait when --wait is set. Defaults to 30000ms." }),
|
|
12801
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
12802
|
+
};
|
|
12803
|
+
async run() {
|
|
12804
|
+
const { flags } = await this.parse(ReplyCommand);
|
|
12805
|
+
if (!flags.body && !flags.html) throw new Errors.CLIError("Either --body or --html (or both) is required.");
|
|
12806
|
+
await runWithTiming(flags.time, async () => {
|
|
12807
|
+
const baseUrlOverridden = flags["api-base-url-1"] !== void 0 || flags["api-base-url-2"] !== void 0;
|
|
12808
|
+
const auth = resolveCliAuth({
|
|
12809
|
+
apiKey: flags["api-key"],
|
|
12810
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
12811
|
+
apiBaseUrl2: flags["api-base-url-2"],
|
|
12812
|
+
configDir: this.config.configDir
|
|
12813
|
+
});
|
|
12814
|
+
const apiClient = new PrimitiveApiClient({
|
|
12815
|
+
apiKey: auth.apiKey,
|
|
12816
|
+
apiBaseUrl1: auth.apiBaseUrl1,
|
|
12817
|
+
apiBaseUrl2: auth.apiBaseUrl2
|
|
12818
|
+
});
|
|
12819
|
+
const result = await replyToEmail({
|
|
12820
|
+
body: {
|
|
12821
|
+
...flags.body !== void 0 ? { body_text: flags.body } : {},
|
|
12822
|
+
...flags.html !== void 0 ? { body_html: flags.html } : {},
|
|
12823
|
+
...flags.from !== void 0 ? { from: flags.from } : {},
|
|
12824
|
+
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
12825
|
+
...flags["wait-timeout-ms"] !== void 0 ? { wait_timeout_ms: flags["wait-timeout-ms"] } : {}
|
|
12826
|
+
},
|
|
12827
|
+
client: apiClient.client,
|
|
12828
|
+
path: { id: flags.id },
|
|
12829
|
+
responseStyle: "fields"
|
|
12830
|
+
});
|
|
12831
|
+
if (result.error) {
|
|
12832
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
12833
|
+
writeErrorWithHints(errorPayload);
|
|
12834
|
+
removeStaleSavedCredentialOnUnauthorized({
|
|
12835
|
+
auth,
|
|
12836
|
+
baseUrlOverridden,
|
|
12837
|
+
configDir: this.config.configDir,
|
|
12838
|
+
payload: errorPayload
|
|
12839
|
+
});
|
|
12840
|
+
process.exitCode = 1;
|
|
12841
|
+
return;
|
|
12842
|
+
}
|
|
12843
|
+
const envelope = result.data;
|
|
12844
|
+
writeIdempotentReplayBannerIfReplay(envelope?.data, { write: (chunk) => {
|
|
12845
|
+
process.stderr.write(chunk);
|
|
12846
|
+
} });
|
|
12847
|
+
this.log(JSON.stringify(envelope?.data ?? null, null, 2));
|
|
12848
|
+
});
|
|
12849
|
+
}
|
|
12850
|
+
};
|
|
12851
|
+
//#endregion
|
|
12236
12852
|
//#region src/oclif/commands/send.ts
|
|
12237
12853
|
const SUBJECT_MAX_LENGTH = 200;
|
|
12238
12854
|
function deriveSubject(body) {
|
|
@@ -12264,17 +12880,17 @@ async function pickDefaultFromAddress(apiClient, authFailureContext) {
|
|
|
12264
12880
|
throw new Errors.CLIError(`Could not look up your verified domains to default --from. Pass --from explicitly. Underlying error: ${formatErrorPayload(errorPayload)}`);
|
|
12265
12881
|
}
|
|
12266
12882
|
const first = result.data?.data?.find(isVerifiedDomain);
|
|
12267
|
-
if (!first) throw new Errors.CLIError("No active verified outbound domain found on this account; pass --from explicitly. To set up outbound, claim a domain via `primitive domains
|
|
12883
|
+
if (!first) throw new Errors.CLIError("No active verified outbound domain found on this account; pass --from explicitly. To set up outbound, claim a domain via `primitive domains add` and verify it.");
|
|
12268
12884
|
return `agent@${first.domain}`;
|
|
12269
12885
|
}
|
|
12270
12886
|
var SendCommand = class SendCommand extends Command {
|
|
12271
|
-
static description = `Send an outbound email. Agent-grade shortcut for sending
|
|
12887
|
+
static description = `Send an outbound email. Agent-grade shortcut for \`sending send\` with sensible defaults.
|
|
12272
12888
|
|
|
12273
12889
|
--from defaults to agent@<your-first-verified-outbound-domain> when omitted.
|
|
12274
12890
|
--subject defaults to the first line of the body when omitted.
|
|
12275
12891
|
|
|
12276
12892
|
For the full flag set (custom message-id threading on the wire,
|
|
12277
|
-
references arrays, etc.), use \`primitive sending
|
|
12893
|
+
references arrays, etc.), use \`primitive sending send\`.`;
|
|
12278
12894
|
static summary = "Send an email (simplified, agent-friendly)";
|
|
12279
12895
|
static examples = [
|
|
12280
12896
|
"<%= config.bin %> send --to alice@example.com --body 'Hi Alice!'",
|
|
@@ -12306,7 +12922,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
12306
12922
|
subject: Flags.string({ description: "Subject line. Defaults to the first line of --body / --html when omitted." }),
|
|
12307
12923
|
body: Flags.string({ description: "Plain-text message body. Either --body or --html (or both) is required." }),
|
|
12308
12924
|
html: Flags.string({ description: "HTML message body. Either --body or --html (or both) is required." }),
|
|
12309
|
-
"in-reply-to": Flags.string({ description: "Message-Id of the parent email when threading a reply on the wire. For replying to an inbound message you received, prefer `primitive
|
|
12925
|
+
"in-reply-to": Flags.string({ description: "Message-Id of the parent email when threading a reply on the wire. For replying to an inbound message you received, prefer `primitive reply --id <inbound-id>`." }),
|
|
12310
12926
|
wait: Flags.boolean({ description: "Block until the receiving MTA returns an outcome. Without --wait, the call returns once Primitive has accepted the message for delivery." }),
|
|
12311
12927
|
"wait-timeout-ms": Flags.integer({ description: "Maximum time to wait when --wait is set. Defaults to 30000ms." }),
|
|
12312
12928
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
@@ -12367,6 +12983,394 @@ var SendCommand = class SendCommand extends Command {
|
|
|
12367
12983
|
}
|
|
12368
12984
|
};
|
|
12369
12985
|
//#endregion
|
|
12986
|
+
//#region src/oclif/commands/signup.ts
|
|
12987
|
+
const INVALID_VERIFICATION_CODE = "invalid_verification_code";
|
|
12988
|
+
const CLERK_PASSWORD_REJECTED = "clerk_password_rejected";
|
|
12989
|
+
const EXPIRED_TOKEN = "expired_token";
|
|
12990
|
+
const INVALID_SIGNUP_TOKEN = "invalid_signup_token";
|
|
12991
|
+
const SLOW_DOWN = "slow_down";
|
|
12992
|
+
const PENDING_SIGNUP_FILE = "signup.json";
|
|
12993
|
+
function cliError(message) {
|
|
12994
|
+
return new Errors.CLIError(message, { exit: 1 });
|
|
12995
|
+
}
|
|
12996
|
+
function unwrapData(value) {
|
|
12997
|
+
return value?.data ?? null;
|
|
12998
|
+
}
|
|
12999
|
+
function isRecord(value) {
|
|
13000
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
13001
|
+
}
|
|
13002
|
+
function pendingSignupFromJson(value) {
|
|
13003
|
+
if (!isRecord(value)) return null;
|
|
13004
|
+
if (typeof value.signup_token !== "string" || typeof value.email !== "string" || typeof value.expires_in !== "number" || typeof value.resend_after !== "number" || typeof value.verification_code_length !== "number" || typeof value.api_base_url_1 !== "string" || typeof value.created_at !== "string" || typeof value.expires_at !== "string") return null;
|
|
13005
|
+
return {
|
|
13006
|
+
api_base_url_1: value.api_base_url_1,
|
|
13007
|
+
created_at: value.created_at,
|
|
13008
|
+
email: value.email,
|
|
13009
|
+
expires_at: value.expires_at,
|
|
13010
|
+
expires_in: value.expires_in,
|
|
13011
|
+
resend_after: value.resend_after,
|
|
13012
|
+
signup_token: value.signup_token,
|
|
13013
|
+
verification_code_length: value.verification_code_length
|
|
13014
|
+
};
|
|
13015
|
+
}
|
|
13016
|
+
function pendingSignupPath(configDir) {
|
|
13017
|
+
return join(configDir, PENDING_SIGNUP_FILE);
|
|
13018
|
+
}
|
|
13019
|
+
function deletePendingCliSignup(configDir) {
|
|
13020
|
+
rmSync(pendingSignupPath(configDir), { force: true });
|
|
13021
|
+
}
|
|
13022
|
+
function pendingSignupFromStart(start, apiBaseUrl1) {
|
|
13023
|
+
return {
|
|
13024
|
+
...start,
|
|
13025
|
+
api_base_url_1: apiBaseUrl1,
|
|
13026
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13027
|
+
expires_at: new Date(Date.now() + start.expires_in * 1e3).toISOString()
|
|
13028
|
+
};
|
|
13029
|
+
}
|
|
13030
|
+
function savePendingCliSignup(configDir, start, apiBaseUrl1) {
|
|
13031
|
+
mkdirSync(configDir, {
|
|
13032
|
+
mode: 448,
|
|
13033
|
+
recursive: true
|
|
13034
|
+
});
|
|
13035
|
+
const pending = pendingSignupFromStart(start, apiBaseUrl1);
|
|
13036
|
+
const path = pendingSignupPath(configDir);
|
|
13037
|
+
const tempPath = join(configDir, `${PENDING_SIGNUP_FILE}.${process$1.pid}.${randomUUID()}.tmp`);
|
|
13038
|
+
try {
|
|
13039
|
+
writeFileSync(tempPath, `${JSON.stringify(pending, null, 2)}\n`, { mode: 384 });
|
|
13040
|
+
chmodSync(tempPath, 384);
|
|
13041
|
+
renameSync(tempPath, path);
|
|
13042
|
+
chmodSync(path, 384);
|
|
13043
|
+
return pending;
|
|
13044
|
+
} catch (error) {
|
|
13045
|
+
rmSync(tempPath, { force: true });
|
|
13046
|
+
throw error;
|
|
13047
|
+
}
|
|
13048
|
+
}
|
|
13049
|
+
function loadPendingCliSignup(configDir, apiBaseUrl1) {
|
|
13050
|
+
const path = pendingSignupPath(configDir);
|
|
13051
|
+
let contents;
|
|
13052
|
+
try {
|
|
13053
|
+
contents = readFileSync(path, "utf8");
|
|
13054
|
+
} catch (error) {
|
|
13055
|
+
if (error && typeof error === "object" && error.code === "ENOENT") return null;
|
|
13056
|
+
throw error;
|
|
13057
|
+
}
|
|
13058
|
+
let pending;
|
|
13059
|
+
try {
|
|
13060
|
+
pending = pendingSignupFromJson(JSON.parse(contents));
|
|
13061
|
+
} catch {
|
|
13062
|
+
pending = null;
|
|
13063
|
+
}
|
|
13064
|
+
if (!pending) {
|
|
13065
|
+
deletePendingCliSignup(configDir);
|
|
13066
|
+
return null;
|
|
13067
|
+
}
|
|
13068
|
+
if (pending.api_base_url_1 !== apiBaseUrl1) return null;
|
|
13069
|
+
if (new Date(pending.expires_at).getTime() <= Date.now()) {
|
|
13070
|
+
deletePendingCliSignup(configDir);
|
|
13071
|
+
return null;
|
|
13072
|
+
}
|
|
13073
|
+
return {
|
|
13074
|
+
...pending,
|
|
13075
|
+
expires_in: Math.max(0, Math.ceil((new Date(pending.expires_at).getTime() - Date.now()) / 1e3))
|
|
13076
|
+
};
|
|
13077
|
+
}
|
|
13078
|
+
function retryAfterSeconds(result) {
|
|
13079
|
+
const raw = result.response?.headers.get("retry-after");
|
|
13080
|
+
if (!raw) return null;
|
|
13081
|
+
const parsed = Number.parseInt(raw, 10);
|
|
13082
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
13083
|
+
}
|
|
13084
|
+
function normalizeAnswer(value) {
|
|
13085
|
+
return value.trim();
|
|
13086
|
+
}
|
|
13087
|
+
async function promptLine(question) {
|
|
13088
|
+
const rl = createInterface({
|
|
13089
|
+
input: process$1.stdin,
|
|
13090
|
+
output: process$1.stderr
|
|
13091
|
+
});
|
|
13092
|
+
try {
|
|
13093
|
+
return normalizeAnswer(await rl.question(question));
|
|
13094
|
+
} finally {
|
|
13095
|
+
rl.close();
|
|
13096
|
+
}
|
|
13097
|
+
}
|
|
13098
|
+
async function promptHidden(question) {
|
|
13099
|
+
if (!process$1.stdin.isTTY || !process$1.stderr.isTTY || !process$1.stdin.setRawMode) throw cliError("Password input requires an interactive terminal with hidden input support.");
|
|
13100
|
+
return new Promise((resolve, reject) => {
|
|
13101
|
+
const input = process$1.stdin;
|
|
13102
|
+
let value = "";
|
|
13103
|
+
const cleanup = () => {
|
|
13104
|
+
input.setRawMode(false);
|
|
13105
|
+
input.pause();
|
|
13106
|
+
input.off("data", onData);
|
|
13107
|
+
};
|
|
13108
|
+
const finish = () => {
|
|
13109
|
+
cleanup();
|
|
13110
|
+
process$1.stderr.write("\n");
|
|
13111
|
+
resolve(value);
|
|
13112
|
+
};
|
|
13113
|
+
const onData = (chunk) => {
|
|
13114
|
+
const text = chunk.toString("utf8");
|
|
13115
|
+
for (const char of text) {
|
|
13116
|
+
if (char === "") {
|
|
13117
|
+
cleanup();
|
|
13118
|
+
process$1.stderr.write("\n");
|
|
13119
|
+
reject(cliError("Signup cancelled."));
|
|
13120
|
+
return;
|
|
13121
|
+
}
|
|
13122
|
+
if (char === "\r" || char === "\n") {
|
|
13123
|
+
finish();
|
|
13124
|
+
return;
|
|
13125
|
+
}
|
|
13126
|
+
if (char === "\b" || char === "") {
|
|
13127
|
+
value = value.slice(0, -1);
|
|
13128
|
+
continue;
|
|
13129
|
+
}
|
|
13130
|
+
value += char;
|
|
13131
|
+
}
|
|
13132
|
+
};
|
|
13133
|
+
process$1.stderr.write(question);
|
|
13134
|
+
input.setRawMode(true);
|
|
13135
|
+
input.resume();
|
|
13136
|
+
input.on("data", onData);
|
|
13137
|
+
});
|
|
13138
|
+
}
|
|
13139
|
+
function formatSignupSeconds(seconds) {
|
|
13140
|
+
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "soon";
|
|
13141
|
+
if (seconds < 60) return `${Math.ceil(seconds)} seconds`;
|
|
13142
|
+
const minutes = Math.ceil(seconds / 60);
|
|
13143
|
+
return `${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
13144
|
+
}
|
|
13145
|
+
function shouldRetrySignupPassword(errorCode) {
|
|
13146
|
+
return errorCode === CLERK_PASSWORD_REJECTED;
|
|
13147
|
+
}
|
|
13148
|
+
function signupErrorMessage(payload) {
|
|
13149
|
+
if (!isRecord(payload)) return null;
|
|
13150
|
+
const message = (isRecord(payload.error) ? payload.error : payload).message;
|
|
13151
|
+
return typeof message === "string" && message.trim() ? message : null;
|
|
13152
|
+
}
|
|
13153
|
+
async function promptRequired(question) {
|
|
13154
|
+
while (true) {
|
|
13155
|
+
const value = await promptLine(question);
|
|
13156
|
+
if (value) return value;
|
|
13157
|
+
process$1.stderr.write("Please enter a value.\n");
|
|
13158
|
+
}
|
|
13159
|
+
}
|
|
13160
|
+
async function promptRequiredPassword(question) {
|
|
13161
|
+
while (true) {
|
|
13162
|
+
const value = await promptHidden(question);
|
|
13163
|
+
if (value) return value;
|
|
13164
|
+
process$1.stderr.write("Please enter a password.\n");
|
|
13165
|
+
}
|
|
13166
|
+
}
|
|
13167
|
+
async function promptNewPassword() {
|
|
13168
|
+
while (true) {
|
|
13169
|
+
const password = await promptRequiredPassword("Password: ");
|
|
13170
|
+
if (password === await promptRequiredPassword("Confirm password: ")) return password;
|
|
13171
|
+
process$1.stderr.write("Passwords did not match. Try again.\n");
|
|
13172
|
+
}
|
|
13173
|
+
}
|
|
13174
|
+
async function confirmTerms() {
|
|
13175
|
+
process$1.stderr.write("By creating an account, you agree to Primitive's Terms of Service and Privacy Policy:\n");
|
|
13176
|
+
process$1.stderr.write(" https://primitive.dev/terms\n");
|
|
13177
|
+
process$1.stderr.write(" https://primitive.dev/privacy\n");
|
|
13178
|
+
const answer = (await promptRequired("Type 'yes' to continue: ")).toLowerCase();
|
|
13179
|
+
if (answer !== "yes" && answer !== "y") throw cliError("You must accept the terms to create an account.");
|
|
13180
|
+
}
|
|
13181
|
+
async function resendVerificationCode(params) {
|
|
13182
|
+
const resent = await (params.deps.resendCliSignupVerification ?? resendCliSignupVerification)({
|
|
13183
|
+
body: { signup_token: params.start.signup_token },
|
|
13184
|
+
client: params.apiClient.client,
|
|
13185
|
+
responseStyle: "fields"
|
|
13186
|
+
});
|
|
13187
|
+
if (resent.data) {
|
|
13188
|
+
const resend = unwrapData(resent.data);
|
|
13189
|
+
const next = resend ? {
|
|
13190
|
+
email: resend.email,
|
|
13191
|
+
expires_in: resend.expires_in,
|
|
13192
|
+
resend_after: resend.resend_after,
|
|
13193
|
+
signup_token: params.start.signup_token,
|
|
13194
|
+
verification_code_length: resend.verification_code_length
|
|
13195
|
+
} : params.start;
|
|
13196
|
+
savePendingCliSignup(params.configDir, next, params.apiBaseUrl1);
|
|
13197
|
+
process$1.stderr.write(`Sent a new ${next.verification_code_length}-digit verification code. It expires in ${formatSignupSeconds(next.expires_in)}.\n`);
|
|
13198
|
+
return next;
|
|
13199
|
+
}
|
|
13200
|
+
const payload = extractErrorPayload(resent.error);
|
|
13201
|
+
if (extractErrorCode(payload) === SLOW_DOWN) {
|
|
13202
|
+
const suffix = ` Wait ${formatSignupSeconds(retryAfterSeconds(resent) ?? params.start.resend_after)} before trying again.`;
|
|
13203
|
+
process$1.stderr.write(`Verification email was sent recently.${suffix}\n`);
|
|
13204
|
+
return params.start;
|
|
13205
|
+
}
|
|
13206
|
+
writeErrorWithHints(payload);
|
|
13207
|
+
throw cliError("Could not resend Primitive CLI signup verification email.");
|
|
13208
|
+
}
|
|
13209
|
+
async function runSignupWithCredentialLock(params) {
|
|
13210
|
+
const { configDir, flags } = params;
|
|
13211
|
+
const deps = params.deps ?? {};
|
|
13212
|
+
const promptRequiredFn = deps.promptRequired ?? promptRequired;
|
|
13213
|
+
const promptNewPasswordFn = deps.promptNewPassword ?? promptNewPassword;
|
|
13214
|
+
const confirmTermsFn = deps.confirmTerms ?? confirmTerms;
|
|
13215
|
+
const startFn = deps.startCliSignup ?? startCliSignup;
|
|
13216
|
+
const verifyFn = deps.verifyCliSignup ?? verifyCliSignup;
|
|
13217
|
+
const checkExistingLoginFn = deps.checkExistingLogin ?? checkExistingLogin;
|
|
13218
|
+
const apiBaseUrl1 = normalizeApiBaseUrl1(flags["api-base-url-1"]);
|
|
13219
|
+
let existing;
|
|
13220
|
+
try {
|
|
13221
|
+
existing = loadCliCredentials(configDir);
|
|
13222
|
+
} catch (error) {
|
|
13223
|
+
if (!flags.force) throw error;
|
|
13224
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
13225
|
+
process$1.stderr.write(`Replacing unreadable Primitive CLI credentials because --force was set: ${detail}\n`);
|
|
13226
|
+
existing = null;
|
|
13227
|
+
}
|
|
13228
|
+
if (existing && flags.force) process$1.stderr.write("Replacing saved Primitive CLI credentials after signup because --force was set.\n");
|
|
13229
|
+
else if (existing) {
|
|
13230
|
+
const existingStatus = await checkExistingLoginFn({
|
|
13231
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
13232
|
+
configDir,
|
|
13233
|
+
credentials: existing
|
|
13234
|
+
});
|
|
13235
|
+
if (existingStatus.status === "removed_stale") process$1.stderr.write("Continuing with Primitive CLI signup...\n");
|
|
13236
|
+
else if (existingStatus.status === "blocked") {
|
|
13237
|
+
writeErrorWithHints(existingStatus.payload);
|
|
13238
|
+
throw cliError(existingStatus.message);
|
|
13239
|
+
} else throw cliError(`Already logged in${existing.org_name ? ` for ${existing.org_name}` : ""}. Run \`primitive logout\` before creating a new account.`);
|
|
13240
|
+
}
|
|
13241
|
+
if (flags.force) deletePendingCliSignup(configDir);
|
|
13242
|
+
const apiClient = new PrimitiveApiClient({ apiBaseUrl1 });
|
|
13243
|
+
let start = flags.force ? null : loadPendingCliSignup(configDir, apiBaseUrl1);
|
|
13244
|
+
const resumed = Boolean(start);
|
|
13245
|
+
if (start) process$1.stderr.write(`Continuing pending Primitive CLI signup for ${start.email}.\n`);
|
|
13246
|
+
else {
|
|
13247
|
+
const signupCode = await promptRequiredFn("Signup code: ");
|
|
13248
|
+
await confirmTermsFn();
|
|
13249
|
+
const email = await promptRequiredFn("Email: ");
|
|
13250
|
+
const started = await startFn({
|
|
13251
|
+
body: {
|
|
13252
|
+
device_name: flags["device-name"] ?? hostname(),
|
|
13253
|
+
email,
|
|
13254
|
+
signup_code: signupCode,
|
|
13255
|
+
terms_accepted: true
|
|
13256
|
+
},
|
|
13257
|
+
client: apiClient.client,
|
|
13258
|
+
responseStyle: "fields"
|
|
13259
|
+
});
|
|
13260
|
+
if (started.error) {
|
|
13261
|
+
writeErrorWithHints(extractErrorPayload(started.error));
|
|
13262
|
+
throw cliError("Could not start Primitive CLI signup.");
|
|
13263
|
+
}
|
|
13264
|
+
const startResult = unwrapData(started.data);
|
|
13265
|
+
if (!startResult) throw cliError("Primitive API returned an empty CLI signup response.");
|
|
13266
|
+
start = savePendingCliSignup(configDir, startResult, apiBaseUrl1);
|
|
13267
|
+
}
|
|
13268
|
+
if (resumed) process$1.stderr.write(`Check your email for the ${start.verification_code_length}-digit verification code sent to ${start.email}.\n`);
|
|
13269
|
+
else process$1.stderr.write(`Sent a ${start.verification_code_length}-digit verification code to ${start.email}.\n`);
|
|
13270
|
+
process$1.stderr.write(`The code expires in ${formatSignupSeconds(start.expires_in)}.\n`);
|
|
13271
|
+
process$1.stderr.write(`Enter the code from the email, or type \`resend\` to send a new code after ${formatSignupSeconds(start.resend_after)}.\n`);
|
|
13272
|
+
while (true) {
|
|
13273
|
+
const verificationCode = await promptRequiredFn(`Verification code (${start.verification_code_length} digits): `);
|
|
13274
|
+
if (verificationCode.toLowerCase() === "resend") {
|
|
13275
|
+
start = await resendVerificationCode({
|
|
13276
|
+
apiBaseUrl1,
|
|
13277
|
+
apiClient,
|
|
13278
|
+
configDir,
|
|
13279
|
+
deps,
|
|
13280
|
+
start
|
|
13281
|
+
});
|
|
13282
|
+
continue;
|
|
13283
|
+
}
|
|
13284
|
+
let password = await promptNewPasswordFn();
|
|
13285
|
+
while (true) {
|
|
13286
|
+
const verified = await verifyFn({
|
|
13287
|
+
body: {
|
|
13288
|
+
password,
|
|
13289
|
+
signup_token: start.signup_token,
|
|
13290
|
+
verification_code: verificationCode
|
|
13291
|
+
},
|
|
13292
|
+
client: apiClient.client,
|
|
13293
|
+
responseStyle: "fields"
|
|
13294
|
+
});
|
|
13295
|
+
if (verified.data) {
|
|
13296
|
+
const signup = unwrapData(verified.data);
|
|
13297
|
+
if (!signup) throw cliError("Primitive API returned an empty CLI signup verification response.");
|
|
13298
|
+
saveCliCredentials(configDir, {
|
|
13299
|
+
api_key: signup.api_key,
|
|
13300
|
+
api_base_url_1: apiBaseUrl1,
|
|
13301
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13302
|
+
key_id: signup.key_id,
|
|
13303
|
+
key_prefix: signup.key_prefix,
|
|
13304
|
+
org_id: signup.org_id,
|
|
13305
|
+
org_name: signup.org_name
|
|
13306
|
+
});
|
|
13307
|
+
deletePendingCliSignup(configDir);
|
|
13308
|
+
const org = signup.org_name ? ` (${signup.org_name})` : "";
|
|
13309
|
+
process$1.stderr.write(`Created account and logged in to org ${signup.org_id}${org}.\n`);
|
|
13310
|
+
process$1.stderr.write(`Saved credentials to ${credentialsPath(configDir)}.\n`);
|
|
13311
|
+
return;
|
|
13312
|
+
}
|
|
13313
|
+
const payload = extractErrorPayload(verified.error);
|
|
13314
|
+
const code = extractErrorCode(payload);
|
|
13315
|
+
if (code === INVALID_VERIFICATION_CODE) {
|
|
13316
|
+
process$1.stderr.write("Invalid verification code. Try again or type `resend`.\n");
|
|
13317
|
+
break;
|
|
13318
|
+
}
|
|
13319
|
+
if (shouldRetrySignupPassword(code)) {
|
|
13320
|
+
const message = signupErrorMessage(payload);
|
|
13321
|
+
if (message) process$1.stderr.write(`Password rejected: ${message}\n`);
|
|
13322
|
+
process$1.stderr.write("Choose a different password and try again.\n");
|
|
13323
|
+
password = await promptNewPasswordFn();
|
|
13324
|
+
continue;
|
|
13325
|
+
}
|
|
13326
|
+
if (code === EXPIRED_TOKEN || code === INVALID_SIGNUP_TOKEN) deletePendingCliSignup(configDir);
|
|
13327
|
+
writeErrorWithHints(payload);
|
|
13328
|
+
throw cliError("Primitive CLI signup failed while verifying the account.");
|
|
13329
|
+
}
|
|
13330
|
+
}
|
|
13331
|
+
}
|
|
13332
|
+
var SignupCommand = class SignupCommand extends Command {
|
|
13333
|
+
static description = "Create a Primitive account from the terminal, verify your email, and save an org-scoped CLI API key locally.";
|
|
13334
|
+
static summary = "Create an account and log in";
|
|
13335
|
+
static examples = [
|
|
13336
|
+
"<%= config.bin %> signup",
|
|
13337
|
+
"<%= config.bin %> signup --device-name work-laptop",
|
|
13338
|
+
"<%= config.bin %> signup --force"
|
|
13339
|
+
];
|
|
13340
|
+
static flags = {
|
|
13341
|
+
"api-base-url-1": Flags.string({
|
|
13342
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
13343
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
13344
|
+
hidden: true
|
|
13345
|
+
}),
|
|
13346
|
+
"device-name": Flags.string({ description: "Device name used for the created CLI API key" }),
|
|
13347
|
+
force: Flags.boolean({
|
|
13348
|
+
char: "f",
|
|
13349
|
+
description: "Replace saved credentials without first verifying the existing login"
|
|
13350
|
+
})
|
|
13351
|
+
};
|
|
13352
|
+
async run() {
|
|
13353
|
+
const { flags } = await this.parse(SignupCommand);
|
|
13354
|
+
let releaseCredentialsLock;
|
|
13355
|
+
try {
|
|
13356
|
+
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
13357
|
+
} catch (error) {
|
|
13358
|
+
throw cliError(error instanceof Error ? error.message : String(error));
|
|
13359
|
+
}
|
|
13360
|
+
try {
|
|
13361
|
+
await this.runWithCredentialLock(flags);
|
|
13362
|
+
} finally {
|
|
13363
|
+
releaseCredentialsLock();
|
|
13364
|
+
}
|
|
13365
|
+
}
|
|
13366
|
+
async runWithCredentialLock(flags) {
|
|
13367
|
+
await runSignupWithCredentialLock({
|
|
13368
|
+
configDir: this.config.configDir,
|
|
13369
|
+
flags
|
|
13370
|
+
});
|
|
13371
|
+
}
|
|
13372
|
+
};
|
|
13373
|
+
//#endregion
|
|
12370
13374
|
//#region src/oclif/commands/whoami.ts
|
|
12371
13375
|
var WhoamiCommand = class WhoamiCommand extends Command {
|
|
12372
13376
|
static description = `Print the account currently authenticated by the API key. Useful as a credentials smoke test: confirms the key is live and shows which account it belongs to.`;
|
|
@@ -12509,7 +13513,7 @@ var ListOperationsCommand = class extends Command {
|
|
|
12509
13513
|
}
|
|
12510
13514
|
};
|
|
12511
13515
|
function lookupOperation(id) {
|
|
12512
|
-
const trimmed = id.trim();
|
|
13516
|
+
const trimmed = resolveOperationAlias(id.trim());
|
|
12513
13517
|
const sep = trimmed.indexOf(":");
|
|
12514
13518
|
const tag = sep === -1 ? "" : trimmed.slice(0, sep);
|
|
12515
13519
|
const cmd = sep === -1 ? trimmed : trimmed.slice(sep + 1);
|
|
@@ -12525,7 +13529,7 @@ function lookupOperation(id) {
|
|
|
12525
13529
|
}
|
|
12526
13530
|
var DescribeCommand = class DescribeCommand extends Command {
|
|
12527
13531
|
static args = { command: Args.string({
|
|
12528
|
-
description: "Command id to describe,
|
|
13532
|
+
description: "Command id to describe, e.g. `emails:list` or `emails:get-email`. Run `primitive list-operations | jq -r '.[] | \"\\(.tagCommand):\\(.command)\"'` to enumerate generated operation ids.",
|
|
12529
13533
|
required: true
|
|
12530
13534
|
}) };
|
|
12531
13535
|
static description = `Print the full operation manifest entry for a single API command, including the path, request schema, response schema, and per-field descriptions sourced from the OpenAPI spec.
|
|
@@ -12533,15 +13537,15 @@ var DescribeCommand = class DescribeCommand extends Command {
|
|
|
12533
13537
|
The manifest entry's \`responseSchema\` carries the inlined JSON Schema for the operation's 200/201 \`data\` envelope contents (\`$ref\`s resolved). Use it to look up what specific response fields mean. Examples:
|
|
12534
13538
|
|
|
12535
13539
|
# Which of EmailDetail's sender-shaped fields is canonical?
|
|
12536
|
-
primitive describe emails:get
|
|
12537
|
-
primitive describe emails:get
|
|
13540
|
+
primitive describe emails:get | jq '.responseSchema.properties | keys'
|
|
13541
|
+
primitive describe emails:get | jq -r '.responseSchema.properties.from_email.description'
|
|
12538
13542
|
|
|
12539
13543
|
# What does each value of SentEmailStatus mean?
|
|
12540
|
-
primitive describe
|
|
13544
|
+
primitive describe sent:get | jq -r '.responseSchema.properties.status.description'
|
|
12541
13545
|
|
|
12542
13546
|
\`requestSchema\` is the same shape for the request body when one exists. For a single field across many operations at once, use \`primitive list-operations | jq\` instead.`;
|
|
12543
13547
|
static summary = "Describe a single API operation in detail";
|
|
12544
|
-
static examples = ["<%= config.bin %> describe emails:get
|
|
13548
|
+
static examples = ["<%= config.bin %> describe emails:get", "<%= config.bin %> describe sent:get"];
|
|
12545
13549
|
async run() {
|
|
12546
13550
|
const { args } = await this.parse(DescribeCommand);
|
|
12547
13551
|
const { match, candidates } = lookupOperation(args.command);
|
|
@@ -12577,13 +13581,66 @@ var CompletionCommand = class CompletionCommand extends Command {
|
|
|
12577
13581
|
function commandId(operation) {
|
|
12578
13582
|
return `${operation.tagCommand}:${operation.command}`;
|
|
12579
13583
|
}
|
|
13584
|
+
const CANONICAL_OPERATION_ALIASES = {
|
|
13585
|
+
"account:show": "account:get-account",
|
|
13586
|
+
"account:storage": "account:get-storage-stats",
|
|
13587
|
+
"account:webhook-secret": "account:get-webhook-secret",
|
|
13588
|
+
"deliveries:list": "webhook-deliveries:list-deliveries",
|
|
13589
|
+
"deliveries:replay": "webhook-deliveries:replay-delivery",
|
|
13590
|
+
"domains:add": "domains:add-domain",
|
|
13591
|
+
"domains:delete": "domains:delete-domain",
|
|
13592
|
+
"domains:list": "domains:list-domains",
|
|
13593
|
+
"domains:update": "domains:update-domain",
|
|
13594
|
+
"domains:verify": "domains:verify-domain",
|
|
13595
|
+
"emails:delete": "emails:delete-email",
|
|
13596
|
+
"emails:discard-content": "emails:discard-email-content",
|
|
13597
|
+
"emails:download-raw": "emails:download-raw-email",
|
|
13598
|
+
"emails:get": "emails:get-email",
|
|
13599
|
+
"emails:list": "emails:list-emails",
|
|
13600
|
+
"emails:replay-webhooks": "emails:replay-email-webhooks",
|
|
13601
|
+
"emails:search": "emails:search-emails",
|
|
13602
|
+
"endpoints:create": "endpoints:create-endpoint",
|
|
13603
|
+
"endpoints:delete": "endpoints:delete-endpoint",
|
|
13604
|
+
"endpoints:list": "endpoints:list-endpoints",
|
|
13605
|
+
"endpoints:test": "endpoints:test-endpoint",
|
|
13606
|
+
"endpoints:update": "endpoints:update-endpoint",
|
|
13607
|
+
"filters:create": "filters:create-filter",
|
|
13608
|
+
"filters:delete": "filters:delete-filter",
|
|
13609
|
+
"filters:list": "filters:list-filters",
|
|
13610
|
+
"filters:update": "filters:update-filter",
|
|
13611
|
+
"functions:delete": "functions:delete-function",
|
|
13612
|
+
"functions:delete-secret": "functions:delete-function-secret",
|
|
13613
|
+
"functions:get": "functions:get-function",
|
|
13614
|
+
"functions:list": "functions:list-functions",
|
|
13615
|
+
"functions:list-secrets": "functions:list-function-secrets",
|
|
13616
|
+
"functions:logs": "functions:list-function-logs",
|
|
13617
|
+
"sending:get": "sending:get-sent-email",
|
|
13618
|
+
"sending:list": "sending:list-sent-emails",
|
|
13619
|
+
"sending:permissions": "sending:get-send-permissions",
|
|
13620
|
+
"sending:reply": "sending:reply-to-email",
|
|
13621
|
+
"sending:send": "sending:send-email",
|
|
13622
|
+
"sent:get": "sending:get-sent-email",
|
|
13623
|
+
"sent:list": "sending:list-sent-emails",
|
|
13624
|
+
"webhook-deliveries:list": "webhook-deliveries:list-deliveries",
|
|
13625
|
+
"webhook-deliveries:replay": "webhook-deliveries:replay-delivery"
|
|
13626
|
+
};
|
|
13627
|
+
const DESCRIBE_OPERATION_ALIASES = {
|
|
13628
|
+
...CANONICAL_OPERATION_ALIASES,
|
|
13629
|
+
reply: "sending:reply-to-email"
|
|
13630
|
+
};
|
|
13631
|
+
function resolveOperationAlias(id) {
|
|
13632
|
+
return DESCRIBE_OPERATION_ALIASES[id] ?? id;
|
|
13633
|
+
}
|
|
12580
13634
|
const OVERRIDDEN_OPERATION_IDS = new Set(["functions:test-function"]);
|
|
13635
|
+
const generatedCommands = Object.fromEntries(operationManifest.filter((operation) => !OVERRIDDEN_OPERATION_IDS.has(commandId(operation))).map((operation) => [commandId(operation), createOperationCommand(operation)]));
|
|
12581
13636
|
const COMMANDS = {
|
|
12582
13637
|
completion: CompletionCommand,
|
|
12583
13638
|
"list-operations": ListOperationsCommand,
|
|
12584
13639
|
describe: DescribeCommand,
|
|
12585
13640
|
send: SendCommand,
|
|
13641
|
+
reply: ReplyCommand,
|
|
12586
13642
|
login: LoginCommand,
|
|
13643
|
+
signup: SignupCommand,
|
|
12587
13644
|
logout: LogoutCommand,
|
|
12588
13645
|
whoami: WhoamiCommand,
|
|
12589
13646
|
doctor: DoctorCommand,
|
|
@@ -12594,8 +13651,14 @@ const COMMANDS = {
|
|
|
12594
13651
|
"functions:deploy": FunctionsDeployCommand,
|
|
12595
13652
|
"functions:redeploy": FunctionsRedeployCommand,
|
|
12596
13653
|
"functions:set-secret": FunctionsSetSecretCommand,
|
|
13654
|
+
"functions:test": FunctionsTestFunctionCommand,
|
|
12597
13655
|
"functions:test-function": FunctionsTestFunctionCommand,
|
|
12598
|
-
...Object.fromEntries(
|
|
13656
|
+
...Object.fromEntries(Object.entries(CANONICAL_OPERATION_ALIASES).map(([alias, target]) => {
|
|
13657
|
+
const command = generatedCommands[target];
|
|
13658
|
+
if (!command) throw new Error(`Missing generated command target for alias ${alias}`);
|
|
13659
|
+
return [alias, command];
|
|
13660
|
+
})),
|
|
13661
|
+
...generatedCommands
|
|
12599
13662
|
};
|
|
12600
13663
|
//#endregion
|
|
12601
|
-
export { COMMANDS, lookupOperation };
|
|
13664
|
+
export { CANONICAL_OPERATION_ALIASES, COMMANDS, lookupOperation };
|