@primitivedotdev/cli 0.31.0 → 0.31.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/oclif/index.js +1680 -578
- package/package.json +4 -1
package/dist/oclif/index.js
CHANGED
|
@@ -667,11 +667,13 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
667
667
|
replayDelivery: () => replayDelivery,
|
|
668
668
|
replayEmailWebhooks: () => replayEmailWebhooks,
|
|
669
669
|
replyToEmail: () => replyToEmail,
|
|
670
|
+
resendAgentSignupVerification: () => resendAgentSignupVerification,
|
|
670
671
|
resendCliSignupVerification: () => resendCliSignupVerification,
|
|
671
672
|
rotateWebhookSecret: () => rotateWebhookSecret,
|
|
672
673
|
searchEmails: () => searchEmails,
|
|
673
674
|
sendEmail: () => sendEmail,
|
|
674
675
|
setFunctionSecret: () => setFunctionSecret,
|
|
676
|
+
startAgentSignup: () => startAgentSignup,
|
|
675
677
|
startCliLogin: () => startCliLogin,
|
|
676
678
|
startCliSignup: () => startCliSignup,
|
|
677
679
|
testEndpoint: () => testEndpoint,
|
|
@@ -681,6 +683,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
681
683
|
updateEndpoint: () => updateEndpoint,
|
|
682
684
|
updateFilter: () => updateFilter,
|
|
683
685
|
updateFunction: () => updateFunction,
|
|
686
|
+
verifyAgentSignup: () => verifyAgentSignup,
|
|
684
687
|
verifyCliSignup: () => verifyCliSignup,
|
|
685
688
|
verifyDomain: () => verifyDomain
|
|
686
689
|
});
|
|
@@ -704,8 +707,8 @@ const startCliLogin = (options) => (options?.client ?? client).post({
|
|
|
704
707
|
* Poll CLI browser login
|
|
705
708
|
*
|
|
706
709
|
* Polls a CLI login session until the browser approval either succeeds,
|
|
707
|
-
* is denied, expires, or is polled too quickly. The
|
|
708
|
-
* only after approval and is returned exactly once.
|
|
710
|
+
* is denied, expires, or is polled too quickly. The OAuth token set is
|
|
711
|
+
* created only after approval and is returned exactly once.
|
|
709
712
|
*
|
|
710
713
|
*/
|
|
711
714
|
const pollCliLogin = (options) => (options.client ?? client).post({
|
|
@@ -749,11 +752,12 @@ const resendCliSignupVerification = (options) => (options.client ?? client).post
|
|
|
749
752
|
}
|
|
750
753
|
});
|
|
751
754
|
/**
|
|
752
|
-
* Verify CLI signup and create
|
|
755
|
+
* Verify CLI signup and create OAuth session
|
|
753
756
|
*
|
|
754
757
|
* Verifies the email code for a CLI signup session, creates the account,
|
|
755
|
-
* redeems the reserved signup code,
|
|
756
|
-
* returns the
|
|
758
|
+
* redeems the reserved signup code, creates an org-scoped OAuth CLI
|
|
759
|
+
* session, and returns the token set exactly once. This endpoint does not
|
|
760
|
+
* require an API key.
|
|
757
761
|
*
|
|
758
762
|
*/
|
|
759
763
|
const verifyCliSignup = (options) => (options.client ?? client).post({
|
|
@@ -765,10 +769,61 @@ const verifyCliSignup = (options) => (options.client ?? client).post({
|
|
|
765
769
|
}
|
|
766
770
|
});
|
|
767
771
|
/**
|
|
768
|
-
*
|
|
772
|
+
* Start agent account signup
|
|
769
773
|
*
|
|
770
|
-
*
|
|
771
|
-
*
|
|
774
|
+
* Starts an agent-native signup session. The API validates the signup code,
|
|
775
|
+
* creates a pending signup session, sends an email verification code, and
|
|
776
|
+
* returns an opaque signup token used by the resend and verify steps. This
|
|
777
|
+
* endpoint does not require an API key.
|
|
778
|
+
*
|
|
779
|
+
*/
|
|
780
|
+
const startAgentSignup = (options) => (options.client ?? client).post({
|
|
781
|
+
url: "/agent/signup/start",
|
|
782
|
+
...options,
|
|
783
|
+
headers: {
|
|
784
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
785
|
+
...options.headers
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
/**
|
|
789
|
+
* Resend agent signup verification code
|
|
790
|
+
*
|
|
791
|
+
* Sends a new email verification code for a pending agent signup session.
|
|
792
|
+
* This endpoint does not require an API key.
|
|
793
|
+
*
|
|
794
|
+
*/
|
|
795
|
+
const resendAgentSignupVerification = (options) => (options.client ?? client).post({
|
|
796
|
+
url: "/agent/signup/resend",
|
|
797
|
+
...options,
|
|
798
|
+
headers: {
|
|
799
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
800
|
+
...options.headers
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
/**
|
|
804
|
+
* Verify agent signup and create OAuth tokens
|
|
805
|
+
*
|
|
806
|
+
* Verifies the email code for an agent signup session, creates the account
|
|
807
|
+
* when needed, redeems the reserved signup code, mints an org-scoped OAuth
|
|
808
|
+
* session for CLI authentication, and returns the raw tokens exactly once.
|
|
809
|
+
* For existing users, the optional `org_id` selects which accessible
|
|
810
|
+
* workspace should receive the new session.
|
|
811
|
+
*
|
|
812
|
+
*/
|
|
813
|
+
const verifyAgentSignup = (options) => (options.client ?? client).post({
|
|
814
|
+
url: "/agent/signup/verify",
|
|
815
|
+
...options,
|
|
816
|
+
headers: {
|
|
817
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
818
|
+
...options.headers
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
/**
|
|
822
|
+
* Revoke the current CLI OAuth session
|
|
823
|
+
*
|
|
824
|
+
* Revokes the OAuth grant used to authenticate the request. API-key
|
|
825
|
+
* authenticated legacy logout requests succeed without deleting server API
|
|
826
|
+
* keys so old local CLI state can be cleared safely.
|
|
772
827
|
*
|
|
773
828
|
*/
|
|
774
829
|
const cliLogout = (options) => (options?.client ?? client).post({
|
|
@@ -1478,9 +1533,8 @@ const getSentEmail = (options) => (options.client ?? client).get({
|
|
|
1478
1533
|
* List functions
|
|
1479
1534
|
*
|
|
1480
1535
|
* Returns every active (non-deleted) function in the org, newest
|
|
1481
|
-
* first. Each entry carries
|
|
1482
|
-
*
|
|
1483
|
-
* the source code or deploy errors, use `GET /functions/{id}`.
|
|
1536
|
+
* first. Each entry carries deploy status and timestamps. To
|
|
1537
|
+
* inspect the source code or deploy errors, use `GET /functions/{id}`.
|
|
1484
1538
|
*
|
|
1485
1539
|
*/
|
|
1486
1540
|
const listFunctions = (options) => (options?.client ?? client).get({
|
|
@@ -1496,13 +1550,14 @@ const listFunctions = (options) => (options?.client ?? client).get({
|
|
|
1496
1550
|
*
|
|
1497
1551
|
* Creates and deploys a new function. The handler must be a single
|
|
1498
1552
|
* ESM module whose default export is an object with an async
|
|
1499
|
-
* `fetch(request, env)` method (Workers-style).
|
|
1500
|
-
*
|
|
1501
|
-
*
|
|
1502
|
-
* `
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1505
|
-
*
|
|
1553
|
+
* `fetch(request, env)` method (Workers-style). Primitive signs
|
|
1554
|
+
* each delivery and forwards the `Primitive-Signature` header to
|
|
1555
|
+
* the handler. Verify the raw request body with
|
|
1556
|
+
* `PRIMITIVE_WEBHOOK_SECRET` before parsing JSON; after verification
|
|
1557
|
+
* the request body parses to an `email.received` event (see
|
|
1558
|
+
* `EmailReceivedEvent` and the Webhook payload section for the full
|
|
1559
|
+
* schema). Code is bundled before being uploaded; ship a single
|
|
1560
|
+
* self-contained file rather than relying on external imports.
|
|
1506
1561
|
*
|
|
1507
1562
|
* **Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`
|
|
1508
1563
|
* (optional) is capped at 5 MiB UTF-8, stored with each deployment
|
|
@@ -1512,12 +1567,12 @@ const listFunctions = (options) => (options?.client ?? client).get({
|
|
|
1512
1567
|
* **Auto-wiring.** On successful deploy, Primitive automatically
|
|
1513
1568
|
* creates a webhook endpoint that delivers inbound mail to the
|
|
1514
1569
|
* function. There is nothing to configure on the Endpoints API
|
|
1515
|
-
* for this to work; the
|
|
1516
|
-
*
|
|
1570
|
+
* for this to work; the internal runtime URL is not returned by
|
|
1571
|
+
* the API and is not a customer-facing integration surface.
|
|
1517
1572
|
*
|
|
1518
1573
|
* **Secrets.** New functions ship with the managed secrets
|
|
1519
|
-
* (`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY
|
|
1520
|
-
* bound. Add user-set secrets via
|
|
1574
|
+
* (`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`,
|
|
1575
|
+
* `PRIMITIVE_API_BASE_URL`) already bound. Add user-set secrets via
|
|
1521
1576
|
* `POST /functions/{id}/secrets`; secret writes only land in the
|
|
1522
1577
|
* running handler on the next redeploy.
|
|
1523
1578
|
*
|
|
@@ -1663,9 +1718,9 @@ const getFunctionTestRunTrace = (options) => (options.client ?? client).get({
|
|
|
1663
1718
|
* never returned.** Secret writes are write-only.
|
|
1664
1719
|
*
|
|
1665
1720
|
* Managed entries (e.g. `PRIMITIVE_WEBHOOK_SECRET`,
|
|
1666
|
-
* `PRIMITIVE_API_KEY`) carry a
|
|
1667
|
-
* `created_at` / `updated_at`. They
|
|
1668
|
-
* or deleted via this API.
|
|
1721
|
+
* `PRIMITIVE_API_KEY`, `PRIMITIVE_API_BASE_URL`) carry a
|
|
1722
|
+
* `description` instead of `created_at` / `updated_at`. They
|
|
1723
|
+
* cannot be created, updated, or deleted via this API.
|
|
1669
1724
|
*
|
|
1670
1725
|
*/
|
|
1671
1726
|
const listFunctionSecrets = (options) => (options.client ?? client).get({
|
|
@@ -1778,7 +1833,7 @@ const openapiDocument = {
|
|
|
1778
1833
|
"info": {
|
|
1779
1834
|
"title": "Primitive API",
|
|
1780
1835
|
"version": "1.0.0",
|
|
1781
|
-
"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
|
|
1836
|
+
"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 plus CLI/agent signup endpoints\nexplicitly declare `security: []`; they do not require an API key because\nthey are used to create OAuth CLI sessions.\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",
|
|
1782
1837
|
"contact": {
|
|
1783
1838
|
"name": "Primitive",
|
|
1784
1839
|
"url": "https://primitive.dev"
|
|
@@ -1801,6 +1856,10 @@ const openapiDocument = {
|
|
|
1801
1856
|
"name": "CLI",
|
|
1802
1857
|
"description": "Browser-assisted CLI authentication"
|
|
1803
1858
|
},
|
|
1859
|
+
{
|
|
1860
|
+
"name": "Agent",
|
|
1861
|
+
"description": "Agent signup and authentication"
|
|
1862
|
+
},
|
|
1804
1863
|
{
|
|
1805
1864
|
"name": "Account",
|
|
1806
1865
|
"description": "Manage your account settings, storage, and webhook secret"
|
|
@@ -1831,7 +1890,7 @@ const openapiDocument = {
|
|
|
1831
1890
|
},
|
|
1832
1891
|
{
|
|
1833
1892
|
"name": "Functions",
|
|
1834
|
-
"description": "Deploy JavaScript handlers that run on inbound mail. Each function\nis a single ESM module whose default export is an object with an\nasync `fetch(request, env)` method, in the shape of a Workers-style\nhandler.
|
|
1893
|
+
"description": "Deploy JavaScript handlers that run on inbound mail. Each function\nis a single ESM module whose default export is an object with an\nasync `fetch(request, env)` method, in the shape of a Workers-style\nhandler. Primitive signs each delivery and forwards the\n`Primitive-Signature` header to the handler; verify the raw request\nbody with `PRIMITIVE_WEBHOOK_SECRET` before trusting the parsed\n`email.received` event (see `EmailReceivedEvent` and the Webhook\npayload section for the full schema). Code runs on\nPrimitive's edge runtime; there is no infrastructure to manage.\nSecrets land in `env` as encrypted bindings and are refreshed on\nevery redeploy.\n"
|
|
1835
1894
|
}
|
|
1836
1895
|
],
|
|
1837
1896
|
"paths": {
|
|
@@ -1864,7 +1923,7 @@ const openapiDocument = {
|
|
|
1864
1923
|
"/cli/login/poll": { "post": {
|
|
1865
1924
|
"operationId": "pollCliLogin",
|
|
1866
1925
|
"summary": "Poll CLI browser login",
|
|
1867
|
-
"description": "Polls a CLI login session until the browser approval either succeeds,\nis denied, expires, or is polled too quickly. The
|
|
1926
|
+
"description": "Polls a CLI login session until the browser approval either succeeds,\nis denied, expires, or is polled too quickly. The OAuth token set is\ncreated only after approval and is returned exactly once.\n",
|
|
1868
1927
|
"tags": ["CLI"],
|
|
1869
1928
|
"security": [],
|
|
1870
1929
|
"requestBody": {
|
|
@@ -1873,7 +1932,7 @@ const openapiDocument = {
|
|
|
1873
1932
|
},
|
|
1874
1933
|
"responses": {
|
|
1875
1934
|
"200": {
|
|
1876
|
-
"description": "CLI login approved and
|
|
1935
|
+
"description": "CLI login approved and OAuth token set created",
|
|
1877
1936
|
"headers": { "Cache-Control": {
|
|
1878
1937
|
"schema": { "type": "string" },
|
|
1879
1938
|
"description": "Always `no-store`"
|
|
@@ -2014,8 +2073,8 @@ const openapiDocument = {
|
|
|
2014
2073
|
} },
|
|
2015
2074
|
"/cli/signup/verify": { "post": {
|
|
2016
2075
|
"operationId": "verifyCliSignup",
|
|
2017
|
-
"summary": "Verify CLI signup and create
|
|
2018
|
-
"description": "Verifies the email code for a CLI signup session, creates the account,\nredeems the reserved signup code,
|
|
2076
|
+
"summary": "Verify CLI signup and create OAuth session",
|
|
2077
|
+
"description": "Verifies the email code for a CLI signup session, creates the account,\nredeems the reserved signup code, creates an org-scoped OAuth CLI\nsession, and returns the token set exactly once. This endpoint does not\nrequire an API key.\n",
|
|
2019
2078
|
"tags": ["CLI"],
|
|
2020
2079
|
"security": [],
|
|
2021
2080
|
"requestBody": {
|
|
@@ -2024,7 +2083,7 @@ const openapiDocument = {
|
|
|
2024
2083
|
},
|
|
2025
2084
|
"responses": {
|
|
2026
2085
|
"200": {
|
|
2027
|
-
"description": "CLI signup verified and
|
|
2086
|
+
"description": "CLI signup verified and OAuth token set created",
|
|
2028
2087
|
"headers": { "Cache-Control": {
|
|
2029
2088
|
"schema": { "type": "string" },
|
|
2030
2089
|
"description": "Always `no-store`"
|
|
@@ -2041,10 +2100,106 @@ const openapiDocument = {
|
|
|
2041
2100
|
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
2042
2101
|
}
|
|
2043
2102
|
} },
|
|
2103
|
+
"/agent/signup/start": { "post": {
|
|
2104
|
+
"operationId": "startAgentSignup",
|
|
2105
|
+
"summary": "Start agent account signup",
|
|
2106
|
+
"description": "Starts an agent-native signup session. 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",
|
|
2107
|
+
"tags": ["Agent"],
|
|
2108
|
+
"security": [],
|
|
2109
|
+
"requestBody": {
|
|
2110
|
+
"required": true,
|
|
2111
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/StartAgentSignupInput" } } }
|
|
2112
|
+
},
|
|
2113
|
+
"responses": {
|
|
2114
|
+
"201": {
|
|
2115
|
+
"description": "Agent signup session created and verification email sent",
|
|
2116
|
+
"headers": { "Cache-Control": {
|
|
2117
|
+
"schema": { "type": "string" },
|
|
2118
|
+
"description": "Always `no-store`"
|
|
2119
|
+
} },
|
|
2120
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
2121
|
+
"type": "object",
|
|
2122
|
+
"properties": { "data": { "$ref": "#/components/schemas/AgentSignupStartResult" } }
|
|
2123
|
+
}] } } }
|
|
2124
|
+
},
|
|
2125
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
2126
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
2127
|
+
}
|
|
2128
|
+
} },
|
|
2129
|
+
"/agent/signup/resend": { "post": {
|
|
2130
|
+
"operationId": "resendAgentSignupVerification",
|
|
2131
|
+
"summary": "Resend agent signup verification code",
|
|
2132
|
+
"description": "Sends a new email verification code for a pending agent signup session.\nThis endpoint does not require an API key.\n",
|
|
2133
|
+
"tags": ["Agent"],
|
|
2134
|
+
"security": [],
|
|
2135
|
+
"requestBody": {
|
|
2136
|
+
"required": true,
|
|
2137
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResendAgentSignupVerificationInput" } } }
|
|
2138
|
+
},
|
|
2139
|
+
"responses": {
|
|
2140
|
+
"200": {
|
|
2141
|
+
"description": "Verification email resent",
|
|
2142
|
+
"headers": { "Cache-Control": {
|
|
2143
|
+
"schema": { "type": "string" },
|
|
2144
|
+
"description": "Always `no-store`"
|
|
2145
|
+
} },
|
|
2146
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
2147
|
+
"type": "object",
|
|
2148
|
+
"properties": { "data": { "$ref": "#/components/schemas/AgentSignupResendResult" } }
|
|
2149
|
+
}] } } }
|
|
2150
|
+
},
|
|
2151
|
+
"400": {
|
|
2152
|
+
"description": "Invalid token or expired token",
|
|
2153
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2154
|
+
},
|
|
2155
|
+
"429": {
|
|
2156
|
+
"description": "Global rate limit exceeded or resend requested too quickly",
|
|
2157
|
+
"headers": { "Retry-After": {
|
|
2158
|
+
"schema": { "type": "integer" },
|
|
2159
|
+
"description": "Seconds to wait before retrying"
|
|
2160
|
+
} },
|
|
2161
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
} },
|
|
2165
|
+
"/agent/signup/verify": { "post": {
|
|
2166
|
+
"operationId": "verifyAgentSignup",
|
|
2167
|
+
"summary": "Verify agent signup and create OAuth tokens",
|
|
2168
|
+
"description": "Verifies the email code for an agent signup session, creates the account\nwhen needed, redeems the reserved signup code, mints an org-scoped OAuth\nsession for CLI authentication, and returns the raw tokens exactly once.\nFor existing users, the optional `org_id` selects which accessible\nworkspace should receive the new session.\n",
|
|
2169
|
+
"tags": ["Agent"],
|
|
2170
|
+
"security": [],
|
|
2171
|
+
"requestBody": {
|
|
2172
|
+
"required": true,
|
|
2173
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/VerifyAgentSignupInput" } } }
|
|
2174
|
+
},
|
|
2175
|
+
"responses": {
|
|
2176
|
+
"200": {
|
|
2177
|
+
"description": "Agent signup verified and OAuth tokens created",
|
|
2178
|
+
"headers": { "Cache-Control": {
|
|
2179
|
+
"schema": { "type": "string" },
|
|
2180
|
+
"description": "Always `no-store`"
|
|
2181
|
+
} },
|
|
2182
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
2183
|
+
"type": "object",
|
|
2184
|
+
"properties": { "data": { "$ref": "#/components/schemas/AgentSignupVerifyResult" } }
|
|
2185
|
+
}] } } }
|
|
2186
|
+
},
|
|
2187
|
+
"400": {
|
|
2188
|
+
"description": "Invalid request, invalid verification code, expired token, invalid signup code, or account creation failure",
|
|
2189
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2190
|
+
},
|
|
2191
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
2192
|
+
"409": {
|
|
2193
|
+
"description": "Existing account is not in a usable workspace state",
|
|
2194
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
|
|
2195
|
+
},
|
|
2196
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
2197
|
+
}
|
|
2198
|
+
} },
|
|
2044
2199
|
"/cli/logout": { "post": {
|
|
2045
2200
|
"operationId": "cliLogout",
|
|
2046
|
-
"summary": "Revoke the current CLI
|
|
2047
|
-
"description": "Revokes the
|
|
2201
|
+
"summary": "Revoke the current CLI OAuth session",
|
|
2202
|
+
"description": "Revokes the OAuth grant used to authenticate the request. API-key\nauthenticated legacy logout requests succeed without deleting server API\nkeys so old local CLI state can be cleared safely.\n",
|
|
2048
2203
|
"tags": ["CLI"],
|
|
2049
2204
|
"requestBody": {
|
|
2050
2205
|
"required": false,
|
|
@@ -2052,7 +2207,7 @@ const openapiDocument = {
|
|
|
2052
2207
|
},
|
|
2053
2208
|
"responses": {
|
|
2054
2209
|
"200": {
|
|
2055
|
-
"description": "CLI
|
|
2210
|
+
"description": "CLI logout completed",
|
|
2056
2211
|
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
2057
2212
|
"type": "object",
|
|
2058
2213
|
"properties": { "data": { "$ref": "#/components/schemas/CliLogoutResult" } }
|
|
@@ -3171,7 +3326,7 @@ const openapiDocument = {
|
|
|
3171
3326
|
"get": {
|
|
3172
3327
|
"operationId": "listFunctions",
|
|
3173
3328
|
"summary": "List functions",
|
|
3174
|
-
"description": "Returns every active (non-deleted) function in the org, newest\nfirst. Each entry carries
|
|
3329
|
+
"description": "Returns every active (non-deleted) function in the org, newest\nfirst. Each entry carries deploy status and timestamps. To\ninspect the source code or deploy errors, use `GET /functions/{id}`.\n",
|
|
3175
3330
|
"tags": ["Functions"],
|
|
3176
3331
|
"responses": {
|
|
3177
3332
|
"200": {
|
|
@@ -3190,7 +3345,7 @@ const openapiDocument = {
|
|
|
3190
3345
|
"post": {
|
|
3191
3346
|
"operationId": "createFunction",
|
|
3192
3347
|
"summary": "Deploy a function",
|
|
3193
|
-
"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).
|
|
3348
|
+
"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). Primitive signs\neach delivery and forwards the `Primitive-Signature` header to\nthe handler. Verify the raw request body with\n`PRIMITIVE_WEBHOOK_SECRET` before parsing JSON; after verification\nthe request body parses to an `email.received` event (see\n`EmailReceivedEvent` and the Webhook payload section for the full\nschema). Code is bundled before being uploaded; ship a single\nself-contained file rather than 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 internal runtime URL is not returned by\nthe API and is not a customer-facing integration surface.\n\n**Secrets.** New functions ship with the managed secrets\n(`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`,\n`PRIMITIVE_API_BASE_URL`) already bound. Add user-set secrets via\n`POST /functions/{id}/secrets`; secret writes only land in the\nrunning handler on the next redeploy.\n",
|
|
3194
3349
|
"tags": ["Functions"],
|
|
3195
3350
|
"requestBody": {
|
|
3196
3351
|
"required": true,
|
|
@@ -3359,7 +3514,7 @@ const openapiDocument = {
|
|
|
3359
3514
|
"get": {
|
|
3360
3515
|
"operationId": "listFunctionSecrets",
|
|
3361
3516
|
"summary": "List a function's secrets",
|
|
3362
|
-
"description": "Returns metadata for every secret bound to the function, with\nmanaged entries (provisioned by Primitive) listed first and\nuser-set entries listed alphabetically after. **Values are\nnever returned.** Secret writes are write-only.\n\nManaged entries (e.g. `PRIMITIVE_WEBHOOK_SECRET`,\n`PRIMITIVE_API_KEY`) carry a
|
|
3517
|
+
"description": "Returns metadata for every secret bound to the function, with\nmanaged entries (provisioned by Primitive) listed first and\nuser-set entries listed alphabetically after. **Values are\nnever returned.** Secret writes are write-only.\n\nManaged entries (e.g. `PRIMITIVE_WEBHOOK_SECRET`,\n`PRIMITIVE_API_KEY`, `PRIMITIVE_API_BASE_URL`) carry a\n`description` instead of `created_at` / `updated_at`. They\ncannot be created, updated, or deleted via this API.\n",
|
|
3363
3518
|
"tags": ["Functions"],
|
|
3364
3519
|
"responses": {
|
|
3365
3520
|
"200": {
|
|
@@ -3780,7 +3935,14 @@ const openapiDocument = {
|
|
|
3780
3935
|
"slow_down",
|
|
3781
3936
|
"access_denied",
|
|
3782
3937
|
"expired_token",
|
|
3783
|
-
"invalid_device_code"
|
|
3938
|
+
"invalid_device_code",
|
|
3939
|
+
"invalid_signup_code",
|
|
3940
|
+
"invalid_signup_token",
|
|
3941
|
+
"invalid_verification_code",
|
|
3942
|
+
"email_delivery_failed",
|
|
3943
|
+
"clerk_signup_failed",
|
|
3944
|
+
"no_orgs_for_user",
|
|
3945
|
+
"org_not_accessible"
|
|
3784
3946
|
]
|
|
3785
3947
|
},
|
|
3786
3948
|
"message": { "type": "string" },
|
|
@@ -3964,13 +4126,42 @@ const openapiDocument = {
|
|
|
3964
4126
|
"properties": {
|
|
3965
4127
|
"api_key": {
|
|
3966
4128
|
"type": "string",
|
|
3967
|
-
"description": "
|
|
4129
|
+
"description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
|
|
3968
4130
|
},
|
|
3969
4131
|
"key_id": {
|
|
4132
|
+
"type": "string",
|
|
4133
|
+
"format": "uuid",
|
|
4134
|
+
"description": "Legacy alias for oauth_grant_id"
|
|
4135
|
+
},
|
|
4136
|
+
"key_prefix": {
|
|
4137
|
+
"type": "string",
|
|
4138
|
+
"description": "Legacy display prefix derived from access_token"
|
|
4139
|
+
},
|
|
4140
|
+
"access_token": {
|
|
4141
|
+
"type": "string",
|
|
4142
|
+
"description": "OAuth access token for CLI API authentication"
|
|
4143
|
+
},
|
|
4144
|
+
"refresh_token": {
|
|
4145
|
+
"type": "string",
|
|
4146
|
+
"description": "OAuth refresh token used by the CLI to renew access"
|
|
4147
|
+
},
|
|
4148
|
+
"token_type": {
|
|
4149
|
+
"type": "string",
|
|
4150
|
+
"enum": ["Bearer"]
|
|
4151
|
+
},
|
|
4152
|
+
"expires_in": {
|
|
4153
|
+
"type": "integer",
|
|
4154
|
+
"description": "Seconds until access_token expires"
|
|
4155
|
+
},
|
|
4156
|
+
"auth_method": {
|
|
4157
|
+
"type": "string",
|
|
4158
|
+
"enum": ["oauth"]
|
|
4159
|
+
},
|
|
4160
|
+
"oauth_grant_id": {
|
|
3970
4161
|
"type": "string",
|
|
3971
4162
|
"format": "uuid"
|
|
3972
4163
|
},
|
|
3973
|
-
"
|
|
4164
|
+
"oauth_client_id": { "type": "string" },
|
|
3974
4165
|
"org_id": {
|
|
3975
4166
|
"type": "string",
|
|
3976
4167
|
"format": "uuid"
|
|
@@ -3981,6 +4172,13 @@ const openapiDocument = {
|
|
|
3981
4172
|
"api_key",
|
|
3982
4173
|
"key_id",
|
|
3983
4174
|
"key_prefix",
|
|
4175
|
+
"access_token",
|
|
4176
|
+
"refresh_token",
|
|
4177
|
+
"token_type",
|
|
4178
|
+
"expires_in",
|
|
4179
|
+
"auth_method",
|
|
4180
|
+
"oauth_grant_id",
|
|
4181
|
+
"oauth_client_id",
|
|
3984
4182
|
"org_id",
|
|
3985
4183
|
"org_name"
|
|
3986
4184
|
]
|
|
@@ -4008,7 +4206,7 @@ const openapiDocument = {
|
|
|
4008
4206
|
"type": "string",
|
|
4009
4207
|
"minLength": 1,
|
|
4010
4208
|
"maxLength": 80,
|
|
4011
|
-
"description": "Human-readable device name used for the created CLI
|
|
4209
|
+
"description": "Human-readable device name used for the created CLI OAuth grant"
|
|
4012
4210
|
},
|
|
4013
4211
|
"metadata": {
|
|
4014
4212
|
"type": "object",
|
|
@@ -4109,24 +4307,49 @@ const openapiDocument = {
|
|
|
4109
4307
|
"maxLength": 1024
|
|
4110
4308
|
}
|
|
4111
4309
|
},
|
|
4112
|
-
"required": [
|
|
4113
|
-
"signup_token",
|
|
4114
|
-
"verification_code",
|
|
4115
|
-
"password"
|
|
4116
|
-
]
|
|
4310
|
+
"required": ["signup_token", "verification_code"]
|
|
4117
4311
|
},
|
|
4118
4312
|
"CliSignupVerifyResult": {
|
|
4119
4313
|
"type": "object",
|
|
4120
4314
|
"properties": {
|
|
4121
4315
|
"api_key": {
|
|
4122
4316
|
"type": "string",
|
|
4123
|
-
"description": "
|
|
4317
|
+
"description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
|
|
4124
4318
|
},
|
|
4125
4319
|
"key_id": {
|
|
4320
|
+
"type": "string",
|
|
4321
|
+
"format": "uuid",
|
|
4322
|
+
"description": "Legacy alias for oauth_grant_id"
|
|
4323
|
+
},
|
|
4324
|
+
"key_prefix": {
|
|
4325
|
+
"type": "string",
|
|
4326
|
+
"description": "Legacy display prefix derived from access_token"
|
|
4327
|
+
},
|
|
4328
|
+
"access_token": {
|
|
4329
|
+
"type": "string",
|
|
4330
|
+
"description": "OAuth access token for CLI API authentication"
|
|
4331
|
+
},
|
|
4332
|
+
"refresh_token": {
|
|
4333
|
+
"type": "string",
|
|
4334
|
+
"description": "OAuth refresh token used by the CLI to renew access"
|
|
4335
|
+
},
|
|
4336
|
+
"token_type": {
|
|
4337
|
+
"type": "string",
|
|
4338
|
+
"enum": ["Bearer"]
|
|
4339
|
+
},
|
|
4340
|
+
"expires_in": {
|
|
4341
|
+
"type": "integer",
|
|
4342
|
+
"description": "Seconds until access_token expires"
|
|
4343
|
+
},
|
|
4344
|
+
"auth_method": {
|
|
4345
|
+
"type": "string",
|
|
4346
|
+
"enum": ["oauth"]
|
|
4347
|
+
},
|
|
4348
|
+
"oauth_grant_id": {
|
|
4126
4349
|
"type": "string",
|
|
4127
4350
|
"format": "uuid"
|
|
4128
4351
|
},
|
|
4129
|
-
"
|
|
4352
|
+
"oauth_client_id": { "type": "string" },
|
|
4130
4353
|
"org_id": {
|
|
4131
4354
|
"type": "string",
|
|
4132
4355
|
"format": "uuid"
|
|
@@ -4137,93 +4360,311 @@ const openapiDocument = {
|
|
|
4137
4360
|
"api_key",
|
|
4138
4361
|
"key_id",
|
|
4139
4362
|
"key_prefix",
|
|
4363
|
+
"access_token",
|
|
4364
|
+
"refresh_token",
|
|
4365
|
+
"token_type",
|
|
4366
|
+
"expires_in",
|
|
4367
|
+
"auth_method",
|
|
4368
|
+
"oauth_grant_id",
|
|
4369
|
+
"oauth_client_id",
|
|
4140
4370
|
"org_id",
|
|
4141
4371
|
"org_name"
|
|
4142
4372
|
]
|
|
4143
4373
|
},
|
|
4144
|
-
"
|
|
4374
|
+
"StartAgentSignupInput": {
|
|
4145
4375
|
"type": "object",
|
|
4146
4376
|
"additionalProperties": false,
|
|
4147
|
-
"properties": { "key_id": {
|
|
4148
|
-
"type": "string",
|
|
4149
|
-
"format": "uuid",
|
|
4150
|
-
"description": "Optional key id guard; when provided it must match the authenticated API key"
|
|
4151
|
-
} }
|
|
4152
|
-
},
|
|
4153
|
-
"CliLogoutResult": {
|
|
4154
|
-
"type": "object",
|
|
4155
4377
|
"properties": {
|
|
4156
|
-
"
|
|
4378
|
+
"email": {
|
|
4379
|
+
"type": "string",
|
|
4380
|
+
"format": "email",
|
|
4381
|
+
"maxLength": 254
|
|
4382
|
+
},
|
|
4383
|
+
"signup_code": {
|
|
4384
|
+
"type": "string",
|
|
4385
|
+
"minLength": 1,
|
|
4386
|
+
"maxLength": 128
|
|
4387
|
+
},
|
|
4388
|
+
"terms_accepted": {
|
|
4157
4389
|
"type": "boolean",
|
|
4158
|
-
"const": true
|
|
4390
|
+
"const": true,
|
|
4391
|
+
"description": "Must be true to confirm acceptance of Primitive's Terms of Service and Privacy Policy"
|
|
4159
4392
|
},
|
|
4160
|
-
"
|
|
4393
|
+
"device_name": {
|
|
4161
4394
|
"type": "string",
|
|
4162
|
-
"
|
|
4395
|
+
"minLength": 1,
|
|
4396
|
+
"maxLength": 80,
|
|
4397
|
+
"description": "Human-readable device name used for the created agent OAuth session"
|
|
4398
|
+
},
|
|
4399
|
+
"metadata": {
|
|
4400
|
+
"type": "object",
|
|
4401
|
+
"additionalProperties": true,
|
|
4402
|
+
"description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
|
|
4163
4403
|
}
|
|
4164
4404
|
},
|
|
4165
|
-
"required": [
|
|
4405
|
+
"required": [
|
|
4406
|
+
"email",
|
|
4407
|
+
"signup_code",
|
|
4408
|
+
"terms_accepted"
|
|
4409
|
+
]
|
|
4166
4410
|
},
|
|
4167
|
-
"
|
|
4411
|
+
"AgentSignupStartResult": {
|
|
4168
4412
|
"type": "object",
|
|
4169
4413
|
"properties": {
|
|
4170
|
-
"
|
|
4414
|
+
"signup_token": {
|
|
4171
4415
|
"type": "string",
|
|
4172
|
-
"
|
|
4416
|
+
"description": "Opaque token used to verify or resend the pending agent signup"
|
|
4173
4417
|
},
|
|
4174
|
-
"email": {
|
|
4175
|
-
"plan": { "type": "string" },
|
|
4176
|
-
"created_at": {
|
|
4418
|
+
"email": {
|
|
4177
4419
|
"type": "string",
|
|
4178
|
-
"format": "
|
|
4420
|
+
"format": "email"
|
|
4179
4421
|
},
|
|
4180
|
-
"
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
"subscription_current_period_end": {
|
|
4184
|
-
"type": ["string", "null"],
|
|
4185
|
-
"format": "date-time"
|
|
4422
|
+
"expires_in": {
|
|
4423
|
+
"type": "integer",
|
|
4424
|
+
"description": "Seconds until the pending signup expires"
|
|
4186
4425
|
},
|
|
4187
|
-
"
|
|
4188
|
-
|
|
4189
|
-
"
|
|
4190
|
-
"minimum": 0,
|
|
4191
|
-
"maximum": 15
|
|
4426
|
+
"resend_after": {
|
|
4427
|
+
"type": "integer",
|
|
4428
|
+
"description": "Minimum seconds before requesting another verification email"
|
|
4192
4429
|
},
|
|
4193
|
-
"
|
|
4194
|
-
|
|
4195
|
-
"
|
|
4196
|
-
"format": "date-time"
|
|
4430
|
+
"verification_code_length": {
|
|
4431
|
+
"type": "integer",
|
|
4432
|
+
"description": "Number of digits in the emailed verification code"
|
|
4197
4433
|
}
|
|
4198
4434
|
},
|
|
4199
4435
|
"required": [
|
|
4200
|
-
"
|
|
4436
|
+
"signup_token",
|
|
4201
4437
|
"email",
|
|
4202
|
-
"
|
|
4203
|
-
"
|
|
4204
|
-
"
|
|
4438
|
+
"expires_in",
|
|
4439
|
+
"resend_after",
|
|
4440
|
+
"verification_code_length"
|
|
4205
4441
|
]
|
|
4206
4442
|
},
|
|
4207
|
-
"
|
|
4443
|
+
"ResendAgentSignupVerificationInput": {
|
|
4444
|
+
"type": "object",
|
|
4445
|
+
"additionalProperties": false,
|
|
4446
|
+
"properties": { "signup_token": {
|
|
4447
|
+
"type": "string",
|
|
4448
|
+
"minLength": 1
|
|
4449
|
+
} },
|
|
4450
|
+
"required": ["signup_token"]
|
|
4451
|
+
},
|
|
4452
|
+
"AgentSignupResendResult": {
|
|
4208
4453
|
"type": "object",
|
|
4209
4454
|
"properties": {
|
|
4210
|
-
"
|
|
4455
|
+
"email": {
|
|
4211
4456
|
"type": "string",
|
|
4212
|
-
"format": "
|
|
4457
|
+
"format": "email"
|
|
4213
4458
|
},
|
|
4214
|
-
"
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
"type": ["number", "null"],
|
|
4218
|
-
"minimum": 0,
|
|
4219
|
-
"maximum": 15
|
|
4459
|
+
"expires_in": {
|
|
4460
|
+
"type": "integer",
|
|
4461
|
+
"description": "Seconds until the pending signup expires"
|
|
4220
4462
|
},
|
|
4221
|
-
"
|
|
4463
|
+
"resend_after": {
|
|
4464
|
+
"type": "integer",
|
|
4465
|
+
"description": "Minimum seconds before requesting another verification email"
|
|
4466
|
+
},
|
|
4467
|
+
"verification_code_length": {
|
|
4468
|
+
"type": "integer",
|
|
4469
|
+
"description": "Number of digits in the emailed verification code"
|
|
4470
|
+
}
|
|
4222
4471
|
},
|
|
4223
4472
|
"required": [
|
|
4224
|
-
"id",
|
|
4225
4473
|
"email",
|
|
4226
|
-
"
|
|
4474
|
+
"expires_in",
|
|
4475
|
+
"resend_after",
|
|
4476
|
+
"verification_code_length"
|
|
4477
|
+
]
|
|
4478
|
+
},
|
|
4479
|
+
"VerifyAgentSignupInput": {
|
|
4480
|
+
"type": "object",
|
|
4481
|
+
"additionalProperties": false,
|
|
4482
|
+
"properties": {
|
|
4483
|
+
"signup_token": {
|
|
4484
|
+
"type": "string",
|
|
4485
|
+
"minLength": 1
|
|
4486
|
+
},
|
|
4487
|
+
"verification_code": {
|
|
4488
|
+
"type": "string",
|
|
4489
|
+
"minLength": 1,
|
|
4490
|
+
"maxLength": 32
|
|
4491
|
+
},
|
|
4492
|
+
"org_id": {
|
|
4493
|
+
"type": "string",
|
|
4494
|
+
"format": "uuid",
|
|
4495
|
+
"description": "Optional workspace id to target when the verified email already belongs to multiple workspaces"
|
|
4496
|
+
}
|
|
4497
|
+
},
|
|
4498
|
+
"required": ["signup_token", "verification_code"]
|
|
4499
|
+
},
|
|
4500
|
+
"AgentOrgRef": {
|
|
4501
|
+
"type": "object",
|
|
4502
|
+
"properties": {
|
|
4503
|
+
"id": {
|
|
4504
|
+
"type": "string",
|
|
4505
|
+
"format": "uuid"
|
|
4506
|
+
},
|
|
4507
|
+
"name": { "type": ["string", "null"] }
|
|
4508
|
+
},
|
|
4509
|
+
"required": ["id", "name"]
|
|
4510
|
+
},
|
|
4511
|
+
"AgentSignupVerifyResult": {
|
|
4512
|
+
"type": "object",
|
|
4513
|
+
"properties": {
|
|
4514
|
+
"api_key": {
|
|
4515
|
+
"type": "string",
|
|
4516
|
+
"description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
|
|
4517
|
+
},
|
|
4518
|
+
"key_id": {
|
|
4519
|
+
"type": "string",
|
|
4520
|
+
"format": "uuid",
|
|
4521
|
+
"description": "Legacy alias for oauth_grant_id"
|
|
4522
|
+
},
|
|
4523
|
+
"key_prefix": {
|
|
4524
|
+
"type": "string",
|
|
4525
|
+
"description": "Legacy display prefix derived from access_token"
|
|
4526
|
+
},
|
|
4527
|
+
"access_token": {
|
|
4528
|
+
"type": "string",
|
|
4529
|
+
"description": "OAuth access token for CLI API authentication"
|
|
4530
|
+
},
|
|
4531
|
+
"refresh_token": {
|
|
4532
|
+
"type": "string",
|
|
4533
|
+
"description": "OAuth refresh token used by the CLI to renew access"
|
|
4534
|
+
},
|
|
4535
|
+
"token_type": {
|
|
4536
|
+
"type": "string",
|
|
4537
|
+
"enum": ["Bearer"]
|
|
4538
|
+
},
|
|
4539
|
+
"expires_in": {
|
|
4540
|
+
"type": "integer",
|
|
4541
|
+
"description": "Seconds until access_token expires"
|
|
4542
|
+
},
|
|
4543
|
+
"auth_method": {
|
|
4544
|
+
"type": "string",
|
|
4545
|
+
"enum": ["oauth"]
|
|
4546
|
+
},
|
|
4547
|
+
"oauth_grant_id": {
|
|
4548
|
+
"type": "string",
|
|
4549
|
+
"format": "uuid"
|
|
4550
|
+
},
|
|
4551
|
+
"oauth_client_id": { "type": "string" },
|
|
4552
|
+
"org_id": {
|
|
4553
|
+
"type": "string",
|
|
4554
|
+
"format": "uuid"
|
|
4555
|
+
},
|
|
4556
|
+
"org_name": { "type": ["string", "null"] },
|
|
4557
|
+
"orgs": {
|
|
4558
|
+
"type": "array",
|
|
4559
|
+
"items": { "$ref": "#/components/schemas/AgentOrgRef" },
|
|
4560
|
+
"description": "Workspaces available to the verified email. The minted session targets `org_id`."
|
|
4561
|
+
}
|
|
4562
|
+
},
|
|
4563
|
+
"required": [
|
|
4564
|
+
"api_key",
|
|
4565
|
+
"key_id",
|
|
4566
|
+
"key_prefix",
|
|
4567
|
+
"access_token",
|
|
4568
|
+
"refresh_token",
|
|
4569
|
+
"token_type",
|
|
4570
|
+
"expires_in",
|
|
4571
|
+
"auth_method",
|
|
4572
|
+
"oauth_grant_id",
|
|
4573
|
+
"oauth_client_id",
|
|
4574
|
+
"org_id",
|
|
4575
|
+
"org_name",
|
|
4576
|
+
"orgs"
|
|
4577
|
+
]
|
|
4578
|
+
},
|
|
4579
|
+
"CliLogoutInput": {
|
|
4580
|
+
"type": "object",
|
|
4581
|
+
"additionalProperties": false,
|
|
4582
|
+
"properties": { "key_id": {
|
|
4583
|
+
"type": "string",
|
|
4584
|
+
"format": "uuid",
|
|
4585
|
+
"description": "Optional id guard; when provided it must match the authenticated OAuth grant id or API key id"
|
|
4586
|
+
} }
|
|
4587
|
+
},
|
|
4588
|
+
"CliLogoutResult": {
|
|
4589
|
+
"type": "object",
|
|
4590
|
+
"properties": {
|
|
4591
|
+
"revoked": {
|
|
4592
|
+
"type": "boolean",
|
|
4593
|
+
"description": "True when an OAuth grant was revoked. False for API-key-authenticated legacy logout, which only clears local CLI state."
|
|
4594
|
+
},
|
|
4595
|
+
"key_id": {
|
|
4596
|
+
"type": "string",
|
|
4597
|
+
"format": "uuid",
|
|
4598
|
+
"description": "API key id for API-key-authenticated legacy logout"
|
|
4599
|
+
},
|
|
4600
|
+
"oauth_grant_id": {
|
|
4601
|
+
"type": "string",
|
|
4602
|
+
"format": "uuid",
|
|
4603
|
+
"description": "OAuth grant id revoked by OAuth-authenticated logout"
|
|
4604
|
+
}
|
|
4605
|
+
},
|
|
4606
|
+
"required": ["revoked"]
|
|
4607
|
+
},
|
|
4608
|
+
"Account": {
|
|
4609
|
+
"type": "object",
|
|
4610
|
+
"properties": {
|
|
4611
|
+
"id": {
|
|
4612
|
+
"type": "string",
|
|
4613
|
+
"format": "uuid"
|
|
4614
|
+
},
|
|
4615
|
+
"email": { "type": "string" },
|
|
4616
|
+
"plan": { "type": "string" },
|
|
4617
|
+
"created_at": {
|
|
4618
|
+
"type": "string",
|
|
4619
|
+
"format": "date-time"
|
|
4620
|
+
},
|
|
4621
|
+
"onboarding_completed": { "type": "boolean" },
|
|
4622
|
+
"onboarding_step": { "type": ["string", "null"] },
|
|
4623
|
+
"stripe_subscription_status": { "type": ["string", "null"] },
|
|
4624
|
+
"subscription_current_period_end": {
|
|
4625
|
+
"type": ["string", "null"],
|
|
4626
|
+
"format": "date-time"
|
|
4627
|
+
},
|
|
4628
|
+
"subscription_cancel_at_period_end": { "type": ["boolean", "null"] },
|
|
4629
|
+
"spam_threshold": {
|
|
4630
|
+
"type": ["number", "null"],
|
|
4631
|
+
"minimum": 0,
|
|
4632
|
+
"maximum": 15
|
|
4633
|
+
},
|
|
4634
|
+
"discard_content_on_webhook_confirmed": { "type": "boolean" },
|
|
4635
|
+
"webhook_secret_rotated_at": {
|
|
4636
|
+
"type": ["string", "null"],
|
|
4637
|
+
"format": "date-time"
|
|
4638
|
+
}
|
|
4639
|
+
},
|
|
4640
|
+
"required": [
|
|
4641
|
+
"id",
|
|
4642
|
+
"email",
|
|
4643
|
+
"plan",
|
|
4644
|
+
"created_at",
|
|
4645
|
+
"discard_content_on_webhook_confirmed"
|
|
4646
|
+
]
|
|
4647
|
+
},
|
|
4648
|
+
"AccountUpdated": {
|
|
4649
|
+
"type": "object",
|
|
4650
|
+
"properties": {
|
|
4651
|
+
"id": {
|
|
4652
|
+
"type": "string",
|
|
4653
|
+
"format": "uuid"
|
|
4654
|
+
},
|
|
4655
|
+
"email": { "type": "string" },
|
|
4656
|
+
"plan": { "type": "string" },
|
|
4657
|
+
"spam_threshold": {
|
|
4658
|
+
"type": ["number", "null"],
|
|
4659
|
+
"minimum": 0,
|
|
4660
|
+
"maximum": 15
|
|
4661
|
+
},
|
|
4662
|
+
"discard_content_on_webhook_confirmed": { "type": "boolean" }
|
|
4663
|
+
},
|
|
4664
|
+
"required": [
|
|
4665
|
+
"id",
|
|
4666
|
+
"email",
|
|
4667
|
+
"plan",
|
|
4227
4668
|
"discard_content_on_webhook_confirmed"
|
|
4228
4669
|
]
|
|
4229
4670
|
},
|
|
@@ -5525,11 +5966,6 @@ const openapiDocument = {
|
|
|
5525
5966
|
"format": "date-time",
|
|
5526
5967
|
"description": "Timestamp of the most recent successful deploy. Null until the first deploy succeeds."
|
|
5527
5968
|
},
|
|
5528
|
-
"gateway_url": {
|
|
5529
|
-
"type": "string",
|
|
5530
|
-
"format": "uri",
|
|
5531
|
-
"description": "URL the platform's webhook delivery loop posts to in order\nto invoke the function. Reference only; not directly\ncallable from outside.\n"
|
|
5532
|
-
},
|
|
5533
5969
|
"created_at": {
|
|
5534
5970
|
"type": "string",
|
|
5535
5971
|
"format": "date-time"
|
|
@@ -5543,7 +5979,6 @@ const openapiDocument = {
|
|
|
5543
5979
|
"id",
|
|
5544
5980
|
"name",
|
|
5545
5981
|
"deploy_status",
|
|
5546
|
-
"gateway_url",
|
|
5547
5982
|
"created_at",
|
|
5548
5983
|
"updated_at"
|
|
5549
5984
|
]
|
|
@@ -5570,10 +6005,6 @@ const openapiDocument = {
|
|
|
5570
6005
|
"type": ["string", "null"],
|
|
5571
6006
|
"format": "date-time"
|
|
5572
6007
|
},
|
|
5573
|
-
"gateway_url": {
|
|
5574
|
-
"type": "string",
|
|
5575
|
-
"format": "uri"
|
|
5576
|
-
},
|
|
5577
6008
|
"created_at": {
|
|
5578
6009
|
"type": "string",
|
|
5579
6010
|
"format": "date-time"
|
|
@@ -5588,7 +6019,6 @@ const openapiDocument = {
|
|
|
5588
6019
|
"name",
|
|
5589
6020
|
"code",
|
|
5590
6021
|
"deploy_status",
|
|
5591
|
-
"gateway_url",
|
|
5592
6022
|
"created_at",
|
|
5593
6023
|
"updated_at"
|
|
5594
6024
|
]
|
|
@@ -5626,17 +6056,12 @@ const openapiDocument = {
|
|
|
5626
6056
|
"format": "uuid"
|
|
5627
6057
|
},
|
|
5628
6058
|
"name": { "type": "string" },
|
|
5629
|
-
"deploy_status": { "$ref": "#/components/schemas/FunctionDeployStatus" }
|
|
5630
|
-
"gateway_url": {
|
|
5631
|
-
"type": "string",
|
|
5632
|
-
"format": "uri"
|
|
5633
|
-
}
|
|
6059
|
+
"deploy_status": { "$ref": "#/components/schemas/FunctionDeployStatus" }
|
|
5634
6060
|
},
|
|
5635
6061
|
"required": [
|
|
5636
6062
|
"id",
|
|
5637
6063
|
"name",
|
|
5638
|
-
"deploy_status"
|
|
5639
|
-
"gateway_url"
|
|
6064
|
+
"deploy_status"
|
|
5640
6065
|
]
|
|
5641
6066
|
},
|
|
5642
6067
|
"UpdateFunctionInput": {
|
|
@@ -6112,7 +6537,7 @@ const openapiDocument = {
|
|
|
6112
6537
|
"key": {
|
|
6113
6538
|
"type": "string",
|
|
6114
6539
|
"pattern": "^[A-Z_][A-Z0-9_]*$",
|
|
6115
|
-
"description": "Uppercase letters, digits, and underscores. Must start with\na letter or underscore. System-managed keys (e.g.\nPRIMITIVE_WEBHOOK_SECRET) are reserved.\n"
|
|
6540
|
+
"description": "Uppercase letters, digits, and underscores. Must start with\na letter or underscore. System-managed keys (e.g.\nPRIMITIVE_WEBHOOK_SECRET, PRIMITIVE_API_KEY, and\nPRIMITIVE_API_BASE_URL) are reserved.\n"
|
|
6116
6541
|
},
|
|
6117
6542
|
"value": {
|
|
6118
6543
|
"type": "string",
|
|
@@ -6382,105 +6807,15 @@ const operationManifest = [
|
|
|
6382
6807
|
"tag": "Account",
|
|
6383
6808
|
"tagCommand": "account"
|
|
6384
6809
|
},
|
|
6385
|
-
{
|
|
6386
|
-
"binaryResponse": false,
|
|
6387
|
-
"bodyRequired": false,
|
|
6388
|
-
"command": "cli-logout",
|
|
6389
|
-
"description": "Revokes the API key used to authenticate the request. CLI clients use\nthis endpoint during `primitive logout` before removing local credentials.\n",
|
|
6390
|
-
"hasJsonBody": true,
|
|
6391
|
-
"method": "POST",
|
|
6392
|
-
"operationId": "cliLogout",
|
|
6393
|
-
"path": "/cli/logout",
|
|
6394
|
-
"pathParams": [],
|
|
6395
|
-
"queryParams": [],
|
|
6396
|
-
"requestSchema": {
|
|
6397
|
-
"type": "object",
|
|
6398
|
-
"additionalProperties": false,
|
|
6399
|
-
"properties": { "key_id": {
|
|
6400
|
-
"type": "string",
|
|
6401
|
-
"format": "uuid",
|
|
6402
|
-
"description": "Optional key id guard; when provided it must match the authenticated API key"
|
|
6403
|
-
} }
|
|
6404
|
-
},
|
|
6405
|
-
"responseSchema": {
|
|
6406
|
-
"type": "object",
|
|
6407
|
-
"properties": {
|
|
6408
|
-
"revoked": {
|
|
6409
|
-
"type": "boolean",
|
|
6410
|
-
"const": true
|
|
6411
|
-
},
|
|
6412
|
-
"key_id": {
|
|
6413
|
-
"type": "string",
|
|
6414
|
-
"format": "uuid"
|
|
6415
|
-
}
|
|
6416
|
-
},
|
|
6417
|
-
"required": ["revoked", "key_id"]
|
|
6418
|
-
},
|
|
6419
|
-
"sdkName": "cliLogout",
|
|
6420
|
-
"summary": "Revoke the current CLI API key",
|
|
6421
|
-
"tag": "CLI",
|
|
6422
|
-
"tagCommand": "cli"
|
|
6423
|
-
},
|
|
6424
|
-
{
|
|
6425
|
-
"binaryResponse": false,
|
|
6426
|
-
"bodyRequired": true,
|
|
6427
|
-
"command": "poll-cli-login",
|
|
6428
|
-
"description": "Polls a CLI login session until the browser approval either succeeds,\nis denied, expires, or is polled too quickly. The API key is generated\nonly after approval and is returned exactly once.\n",
|
|
6429
|
-
"hasJsonBody": true,
|
|
6430
|
-
"method": "POST",
|
|
6431
|
-
"operationId": "pollCliLogin",
|
|
6432
|
-
"path": "/cli/login/poll",
|
|
6433
|
-
"pathParams": [],
|
|
6434
|
-
"queryParams": [],
|
|
6435
|
-
"requestSchema": {
|
|
6436
|
-
"type": "object",
|
|
6437
|
-
"additionalProperties": false,
|
|
6438
|
-
"properties": { "device_code": {
|
|
6439
|
-
"type": "string",
|
|
6440
|
-
"minLength": 1
|
|
6441
|
-
} },
|
|
6442
|
-
"required": ["device_code"]
|
|
6443
|
-
},
|
|
6444
|
-
"responseSchema": {
|
|
6445
|
-
"type": "object",
|
|
6446
|
-
"properties": {
|
|
6447
|
-
"api_key": {
|
|
6448
|
-
"type": "string",
|
|
6449
|
-
"description": "Newly-created API key for CLI authentication"
|
|
6450
|
-
},
|
|
6451
|
-
"key_id": {
|
|
6452
|
-
"type": "string",
|
|
6453
|
-
"format": "uuid"
|
|
6454
|
-
},
|
|
6455
|
-
"key_prefix": { "type": "string" },
|
|
6456
|
-
"org_id": {
|
|
6457
|
-
"type": "string",
|
|
6458
|
-
"format": "uuid"
|
|
6459
|
-
},
|
|
6460
|
-
"org_name": { "type": ["string", "null"] }
|
|
6461
|
-
},
|
|
6462
|
-
"required": [
|
|
6463
|
-
"api_key",
|
|
6464
|
-
"key_id",
|
|
6465
|
-
"key_prefix",
|
|
6466
|
-
"org_id",
|
|
6467
|
-
"org_name"
|
|
6468
|
-
]
|
|
6469
|
-
},
|
|
6470
|
-
"sdkName": "pollCliLogin",
|
|
6471
|
-
"summary": "Poll CLI browser login",
|
|
6472
|
-
"tag": "CLI",
|
|
6473
|
-
"tagCommand": "cli"
|
|
6474
|
-
},
|
|
6475
6810
|
{
|
|
6476
6811
|
"binaryResponse": false,
|
|
6477
6812
|
"bodyRequired": true,
|
|
6478
|
-
"command": "resend-
|
|
6479
|
-
"description": "Sends a new email verification code for a pending
|
|
6813
|
+
"command": "resend-agent-signup-verification",
|
|
6814
|
+
"description": "Sends a new email verification code for a pending agent signup session.\nThis endpoint does not require an API key.\n",
|
|
6480
6815
|
"hasJsonBody": true,
|
|
6481
6816
|
"method": "POST",
|
|
6482
|
-
"operationId": "
|
|
6483
|
-
"path": "/
|
|
6817
|
+
"operationId": "resendAgentSignupVerification",
|
|
6818
|
+
"path": "/agent/signup/resend",
|
|
6484
6819
|
"pathParams": [],
|
|
6485
6820
|
"queryParams": [],
|
|
6486
6821
|
"requestSchema": {
|
|
@@ -6519,10 +6854,394 @@ const operationManifest = [
|
|
|
6519
6854
|
"verification_code_length"
|
|
6520
6855
|
]
|
|
6521
6856
|
},
|
|
6522
|
-
"sdkName": "
|
|
6523
|
-
"summary": "Resend
|
|
6524
|
-
"tag": "
|
|
6525
|
-
"tagCommand": "
|
|
6857
|
+
"sdkName": "resendAgentSignupVerification",
|
|
6858
|
+
"summary": "Resend agent signup verification code",
|
|
6859
|
+
"tag": "Agent",
|
|
6860
|
+
"tagCommand": "agent"
|
|
6861
|
+
},
|
|
6862
|
+
{
|
|
6863
|
+
"binaryResponse": false,
|
|
6864
|
+
"bodyRequired": true,
|
|
6865
|
+
"command": "start-agent-signup",
|
|
6866
|
+
"description": "Starts an agent-native signup session. 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",
|
|
6867
|
+
"hasJsonBody": true,
|
|
6868
|
+
"method": "POST",
|
|
6869
|
+
"operationId": "startAgentSignup",
|
|
6870
|
+
"path": "/agent/signup/start",
|
|
6871
|
+
"pathParams": [],
|
|
6872
|
+
"queryParams": [],
|
|
6873
|
+
"requestSchema": {
|
|
6874
|
+
"type": "object",
|
|
6875
|
+
"additionalProperties": false,
|
|
6876
|
+
"properties": {
|
|
6877
|
+
"email": {
|
|
6878
|
+
"type": "string",
|
|
6879
|
+
"format": "email",
|
|
6880
|
+
"maxLength": 254
|
|
6881
|
+
},
|
|
6882
|
+
"signup_code": {
|
|
6883
|
+
"type": "string",
|
|
6884
|
+
"minLength": 1,
|
|
6885
|
+
"maxLength": 128
|
|
6886
|
+
},
|
|
6887
|
+
"terms_accepted": {
|
|
6888
|
+
"type": "boolean",
|
|
6889
|
+
"const": true,
|
|
6890
|
+
"description": "Must be true to confirm acceptance of Primitive's Terms of Service and Privacy Policy"
|
|
6891
|
+
},
|
|
6892
|
+
"device_name": {
|
|
6893
|
+
"type": "string",
|
|
6894
|
+
"minLength": 1,
|
|
6895
|
+
"maxLength": 80,
|
|
6896
|
+
"description": "Human-readable device name used for the created agent OAuth session"
|
|
6897
|
+
},
|
|
6898
|
+
"metadata": {
|
|
6899
|
+
"type": "object",
|
|
6900
|
+
"additionalProperties": true,
|
|
6901
|
+
"description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
|
|
6902
|
+
}
|
|
6903
|
+
},
|
|
6904
|
+
"required": [
|
|
6905
|
+
"email",
|
|
6906
|
+
"signup_code",
|
|
6907
|
+
"terms_accepted"
|
|
6908
|
+
]
|
|
6909
|
+
},
|
|
6910
|
+
"responseSchema": {
|
|
6911
|
+
"type": "object",
|
|
6912
|
+
"properties": {
|
|
6913
|
+
"signup_token": {
|
|
6914
|
+
"type": "string",
|
|
6915
|
+
"description": "Opaque token used to verify or resend the pending agent signup"
|
|
6916
|
+
},
|
|
6917
|
+
"email": {
|
|
6918
|
+
"type": "string",
|
|
6919
|
+
"format": "email"
|
|
6920
|
+
},
|
|
6921
|
+
"expires_in": {
|
|
6922
|
+
"type": "integer",
|
|
6923
|
+
"description": "Seconds until the pending signup expires"
|
|
6924
|
+
},
|
|
6925
|
+
"resend_after": {
|
|
6926
|
+
"type": "integer",
|
|
6927
|
+
"description": "Minimum seconds before requesting another verification email"
|
|
6928
|
+
},
|
|
6929
|
+
"verification_code_length": {
|
|
6930
|
+
"type": "integer",
|
|
6931
|
+
"description": "Number of digits in the emailed verification code"
|
|
6932
|
+
}
|
|
6933
|
+
},
|
|
6934
|
+
"required": [
|
|
6935
|
+
"signup_token",
|
|
6936
|
+
"email",
|
|
6937
|
+
"expires_in",
|
|
6938
|
+
"resend_after",
|
|
6939
|
+
"verification_code_length"
|
|
6940
|
+
]
|
|
6941
|
+
},
|
|
6942
|
+
"sdkName": "startAgentSignup",
|
|
6943
|
+
"summary": "Start agent account signup",
|
|
6944
|
+
"tag": "Agent",
|
|
6945
|
+
"tagCommand": "agent"
|
|
6946
|
+
},
|
|
6947
|
+
{
|
|
6948
|
+
"binaryResponse": false,
|
|
6949
|
+
"bodyRequired": true,
|
|
6950
|
+
"command": "verify-agent-signup",
|
|
6951
|
+
"description": "Verifies the email code for an agent signup session, creates the account\nwhen needed, redeems the reserved signup code, mints an org-scoped OAuth\nsession for CLI authentication, and returns the raw tokens exactly once.\nFor existing users, the optional `org_id` selects which accessible\nworkspace should receive the new session.\n",
|
|
6952
|
+
"hasJsonBody": true,
|
|
6953
|
+
"method": "POST",
|
|
6954
|
+
"operationId": "verifyAgentSignup",
|
|
6955
|
+
"path": "/agent/signup/verify",
|
|
6956
|
+
"pathParams": [],
|
|
6957
|
+
"queryParams": [],
|
|
6958
|
+
"requestSchema": {
|
|
6959
|
+
"type": "object",
|
|
6960
|
+
"additionalProperties": false,
|
|
6961
|
+
"properties": {
|
|
6962
|
+
"signup_token": {
|
|
6963
|
+
"type": "string",
|
|
6964
|
+
"minLength": 1
|
|
6965
|
+
},
|
|
6966
|
+
"verification_code": {
|
|
6967
|
+
"type": "string",
|
|
6968
|
+
"minLength": 1,
|
|
6969
|
+
"maxLength": 32
|
|
6970
|
+
},
|
|
6971
|
+
"org_id": {
|
|
6972
|
+
"type": "string",
|
|
6973
|
+
"format": "uuid",
|
|
6974
|
+
"description": "Optional workspace id to target when the verified email already belongs to multiple workspaces"
|
|
6975
|
+
}
|
|
6976
|
+
},
|
|
6977
|
+
"required": ["signup_token", "verification_code"]
|
|
6978
|
+
},
|
|
6979
|
+
"responseSchema": {
|
|
6980
|
+
"type": "object",
|
|
6981
|
+
"properties": {
|
|
6982
|
+
"api_key": {
|
|
6983
|
+
"type": "string",
|
|
6984
|
+
"description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
|
|
6985
|
+
},
|
|
6986
|
+
"key_id": {
|
|
6987
|
+
"type": "string",
|
|
6988
|
+
"format": "uuid",
|
|
6989
|
+
"description": "Legacy alias for oauth_grant_id"
|
|
6990
|
+
},
|
|
6991
|
+
"key_prefix": {
|
|
6992
|
+
"type": "string",
|
|
6993
|
+
"description": "Legacy display prefix derived from access_token"
|
|
6994
|
+
},
|
|
6995
|
+
"access_token": {
|
|
6996
|
+
"type": "string",
|
|
6997
|
+
"description": "OAuth access token for CLI API authentication"
|
|
6998
|
+
},
|
|
6999
|
+
"refresh_token": {
|
|
7000
|
+
"type": "string",
|
|
7001
|
+
"description": "OAuth refresh token used by the CLI to renew access"
|
|
7002
|
+
},
|
|
7003
|
+
"token_type": {
|
|
7004
|
+
"type": "string",
|
|
7005
|
+
"enum": ["Bearer"]
|
|
7006
|
+
},
|
|
7007
|
+
"expires_in": {
|
|
7008
|
+
"type": "integer",
|
|
7009
|
+
"description": "Seconds until access_token expires"
|
|
7010
|
+
},
|
|
7011
|
+
"auth_method": {
|
|
7012
|
+
"type": "string",
|
|
7013
|
+
"enum": ["oauth"]
|
|
7014
|
+
},
|
|
7015
|
+
"oauth_grant_id": {
|
|
7016
|
+
"type": "string",
|
|
7017
|
+
"format": "uuid"
|
|
7018
|
+
},
|
|
7019
|
+
"oauth_client_id": { "type": "string" },
|
|
7020
|
+
"org_id": {
|
|
7021
|
+
"type": "string",
|
|
7022
|
+
"format": "uuid"
|
|
7023
|
+
},
|
|
7024
|
+
"org_name": { "type": ["string", "null"] },
|
|
7025
|
+
"orgs": {
|
|
7026
|
+
"type": "array",
|
|
7027
|
+
"items": {
|
|
7028
|
+
"type": "object",
|
|
7029
|
+
"properties": {
|
|
7030
|
+
"id": {
|
|
7031
|
+
"type": "string",
|
|
7032
|
+
"format": "uuid"
|
|
7033
|
+
},
|
|
7034
|
+
"name": { "type": ["string", "null"] }
|
|
7035
|
+
},
|
|
7036
|
+
"required": ["id", "name"]
|
|
7037
|
+
},
|
|
7038
|
+
"description": "Workspaces available to the verified email. The minted session targets `org_id`."
|
|
7039
|
+
}
|
|
7040
|
+
},
|
|
7041
|
+
"required": [
|
|
7042
|
+
"api_key",
|
|
7043
|
+
"key_id",
|
|
7044
|
+
"key_prefix",
|
|
7045
|
+
"access_token",
|
|
7046
|
+
"refresh_token",
|
|
7047
|
+
"token_type",
|
|
7048
|
+
"expires_in",
|
|
7049
|
+
"auth_method",
|
|
7050
|
+
"oauth_grant_id",
|
|
7051
|
+
"oauth_client_id",
|
|
7052
|
+
"org_id",
|
|
7053
|
+
"org_name",
|
|
7054
|
+
"orgs"
|
|
7055
|
+
]
|
|
7056
|
+
},
|
|
7057
|
+
"sdkName": "verifyAgentSignup",
|
|
7058
|
+
"summary": "Verify agent signup and create OAuth tokens",
|
|
7059
|
+
"tag": "Agent",
|
|
7060
|
+
"tagCommand": "agent"
|
|
7061
|
+
},
|
|
7062
|
+
{
|
|
7063
|
+
"binaryResponse": false,
|
|
7064
|
+
"bodyRequired": false,
|
|
7065
|
+
"command": "cli-logout",
|
|
7066
|
+
"description": "Revokes the OAuth grant used to authenticate the request. API-key\nauthenticated legacy logout requests succeed without deleting server API\nkeys so old local CLI state can be cleared safely.\n",
|
|
7067
|
+
"hasJsonBody": true,
|
|
7068
|
+
"method": "POST",
|
|
7069
|
+
"operationId": "cliLogout",
|
|
7070
|
+
"path": "/cli/logout",
|
|
7071
|
+
"pathParams": [],
|
|
7072
|
+
"queryParams": [],
|
|
7073
|
+
"requestSchema": {
|
|
7074
|
+
"type": "object",
|
|
7075
|
+
"additionalProperties": false,
|
|
7076
|
+
"properties": { "key_id": {
|
|
7077
|
+
"type": "string",
|
|
7078
|
+
"format": "uuid",
|
|
7079
|
+
"description": "Optional id guard; when provided it must match the authenticated OAuth grant id or API key id"
|
|
7080
|
+
} }
|
|
7081
|
+
},
|
|
7082
|
+
"responseSchema": {
|
|
7083
|
+
"type": "object",
|
|
7084
|
+
"properties": {
|
|
7085
|
+
"revoked": {
|
|
7086
|
+
"type": "boolean",
|
|
7087
|
+
"description": "True when an OAuth grant was revoked. False for API-key-authenticated legacy logout, which only clears local CLI state."
|
|
7088
|
+
},
|
|
7089
|
+
"key_id": {
|
|
7090
|
+
"type": "string",
|
|
7091
|
+
"format": "uuid",
|
|
7092
|
+
"description": "API key id for API-key-authenticated legacy logout"
|
|
7093
|
+
},
|
|
7094
|
+
"oauth_grant_id": {
|
|
7095
|
+
"type": "string",
|
|
7096
|
+
"format": "uuid",
|
|
7097
|
+
"description": "OAuth grant id revoked by OAuth-authenticated logout"
|
|
7098
|
+
}
|
|
7099
|
+
},
|
|
7100
|
+
"required": ["revoked"]
|
|
7101
|
+
},
|
|
7102
|
+
"sdkName": "cliLogout",
|
|
7103
|
+
"summary": "Revoke the current CLI OAuth session",
|
|
7104
|
+
"tag": "CLI",
|
|
7105
|
+
"tagCommand": "cli"
|
|
7106
|
+
},
|
|
7107
|
+
{
|
|
7108
|
+
"binaryResponse": false,
|
|
7109
|
+
"bodyRequired": true,
|
|
7110
|
+
"command": "poll-cli-login",
|
|
7111
|
+
"description": "Polls a CLI login session until the browser approval either succeeds,\nis denied, expires, or is polled too quickly. The OAuth token set is\ncreated only after approval and is returned exactly once.\n",
|
|
7112
|
+
"hasJsonBody": true,
|
|
7113
|
+
"method": "POST",
|
|
7114
|
+
"operationId": "pollCliLogin",
|
|
7115
|
+
"path": "/cli/login/poll",
|
|
7116
|
+
"pathParams": [],
|
|
7117
|
+
"queryParams": [],
|
|
7118
|
+
"requestSchema": {
|
|
7119
|
+
"type": "object",
|
|
7120
|
+
"additionalProperties": false,
|
|
7121
|
+
"properties": { "device_code": {
|
|
7122
|
+
"type": "string",
|
|
7123
|
+
"minLength": 1
|
|
7124
|
+
} },
|
|
7125
|
+
"required": ["device_code"]
|
|
7126
|
+
},
|
|
7127
|
+
"responseSchema": {
|
|
7128
|
+
"type": "object",
|
|
7129
|
+
"properties": {
|
|
7130
|
+
"api_key": {
|
|
7131
|
+
"type": "string",
|
|
7132
|
+
"description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
|
|
7133
|
+
},
|
|
7134
|
+
"key_id": {
|
|
7135
|
+
"type": "string",
|
|
7136
|
+
"format": "uuid",
|
|
7137
|
+
"description": "Legacy alias for oauth_grant_id"
|
|
7138
|
+
},
|
|
7139
|
+
"key_prefix": {
|
|
7140
|
+
"type": "string",
|
|
7141
|
+
"description": "Legacy display prefix derived from access_token"
|
|
7142
|
+
},
|
|
7143
|
+
"access_token": {
|
|
7144
|
+
"type": "string",
|
|
7145
|
+
"description": "OAuth access token for CLI API authentication"
|
|
7146
|
+
},
|
|
7147
|
+
"refresh_token": {
|
|
7148
|
+
"type": "string",
|
|
7149
|
+
"description": "OAuth refresh token used by the CLI to renew access"
|
|
7150
|
+
},
|
|
7151
|
+
"token_type": {
|
|
7152
|
+
"type": "string",
|
|
7153
|
+
"enum": ["Bearer"]
|
|
7154
|
+
},
|
|
7155
|
+
"expires_in": {
|
|
7156
|
+
"type": "integer",
|
|
7157
|
+
"description": "Seconds until access_token expires"
|
|
7158
|
+
},
|
|
7159
|
+
"auth_method": {
|
|
7160
|
+
"type": "string",
|
|
7161
|
+
"enum": ["oauth"]
|
|
7162
|
+
},
|
|
7163
|
+
"oauth_grant_id": {
|
|
7164
|
+
"type": "string",
|
|
7165
|
+
"format": "uuid"
|
|
7166
|
+
},
|
|
7167
|
+
"oauth_client_id": { "type": "string" },
|
|
7168
|
+
"org_id": {
|
|
7169
|
+
"type": "string",
|
|
7170
|
+
"format": "uuid"
|
|
7171
|
+
},
|
|
7172
|
+
"org_name": { "type": ["string", "null"] }
|
|
7173
|
+
},
|
|
7174
|
+
"required": [
|
|
7175
|
+
"api_key",
|
|
7176
|
+
"key_id",
|
|
7177
|
+
"key_prefix",
|
|
7178
|
+
"access_token",
|
|
7179
|
+
"refresh_token",
|
|
7180
|
+
"token_type",
|
|
7181
|
+
"expires_in",
|
|
7182
|
+
"auth_method",
|
|
7183
|
+
"oauth_grant_id",
|
|
7184
|
+
"oauth_client_id",
|
|
7185
|
+
"org_id",
|
|
7186
|
+
"org_name"
|
|
7187
|
+
]
|
|
7188
|
+
},
|
|
7189
|
+
"sdkName": "pollCliLogin",
|
|
7190
|
+
"summary": "Poll CLI browser login",
|
|
7191
|
+
"tag": "CLI",
|
|
7192
|
+
"tagCommand": "cli"
|
|
7193
|
+
},
|
|
7194
|
+
{
|
|
7195
|
+
"binaryResponse": false,
|
|
7196
|
+
"bodyRequired": true,
|
|
7197
|
+
"command": "resend-cli-signup-verification",
|
|
7198
|
+
"description": "Sends a new email verification code for a pending CLI signup session.\nThis endpoint does not require an API key.\n",
|
|
7199
|
+
"hasJsonBody": true,
|
|
7200
|
+
"method": "POST",
|
|
7201
|
+
"operationId": "resendCliSignupVerification",
|
|
7202
|
+
"path": "/cli/signup/resend",
|
|
7203
|
+
"pathParams": [],
|
|
7204
|
+
"queryParams": [],
|
|
7205
|
+
"requestSchema": {
|
|
7206
|
+
"type": "object",
|
|
7207
|
+
"additionalProperties": false,
|
|
7208
|
+
"properties": { "signup_token": {
|
|
7209
|
+
"type": "string",
|
|
7210
|
+
"minLength": 1
|
|
7211
|
+
} },
|
|
7212
|
+
"required": ["signup_token"]
|
|
7213
|
+
},
|
|
7214
|
+
"responseSchema": {
|
|
7215
|
+
"type": "object",
|
|
7216
|
+
"properties": {
|
|
7217
|
+
"email": {
|
|
7218
|
+
"type": "string",
|
|
7219
|
+
"format": "email"
|
|
7220
|
+
},
|
|
7221
|
+
"expires_in": {
|
|
7222
|
+
"type": "integer",
|
|
7223
|
+
"description": "Seconds until the pending signup expires"
|
|
7224
|
+
},
|
|
7225
|
+
"resend_after": {
|
|
7226
|
+
"type": "integer",
|
|
7227
|
+
"description": "Minimum seconds before requesting another verification email"
|
|
7228
|
+
},
|
|
7229
|
+
"verification_code_length": {
|
|
7230
|
+
"type": "integer",
|
|
7231
|
+
"description": "Number of digits in the emailed verification code"
|
|
7232
|
+
}
|
|
7233
|
+
},
|
|
7234
|
+
"required": [
|
|
7235
|
+
"email",
|
|
7236
|
+
"expires_in",
|
|
7237
|
+
"resend_after",
|
|
7238
|
+
"verification_code_length"
|
|
7239
|
+
]
|
|
7240
|
+
},
|
|
7241
|
+
"sdkName": "resendCliSignupVerification",
|
|
7242
|
+
"summary": "Resend CLI signup verification code",
|
|
7243
|
+
"tag": "CLI",
|
|
7244
|
+
"tagCommand": "cli"
|
|
6526
7245
|
},
|
|
6527
7246
|
{
|
|
6528
7247
|
"binaryResponse": false,
|
|
@@ -6629,7 +7348,7 @@ const operationManifest = [
|
|
|
6629
7348
|
"type": "string",
|
|
6630
7349
|
"minLength": 1,
|
|
6631
7350
|
"maxLength": 80,
|
|
6632
|
-
"description": "Human-readable device name used for the created CLI
|
|
7351
|
+
"description": "Human-readable device name used for the created CLI OAuth grant"
|
|
6633
7352
|
},
|
|
6634
7353
|
"metadata": {
|
|
6635
7354
|
"type": "object",
|
|
@@ -6684,7 +7403,7 @@ const operationManifest = [
|
|
|
6684
7403
|
"binaryResponse": false,
|
|
6685
7404
|
"bodyRequired": true,
|
|
6686
7405
|
"command": "verify-cli-signup",
|
|
6687
|
-
"description": "Verifies the email code for a CLI signup session, creates the account,\nredeems the reserved signup code,
|
|
7406
|
+
"description": "Verifies the email code for a CLI signup session, creates the account,\nredeems the reserved signup code, creates an org-scoped OAuth CLI\nsession, and returns the token set exactly once. This endpoint does not\nrequire an API key.\n",
|
|
6688
7407
|
"hasJsonBody": true,
|
|
6689
7408
|
"method": "POST",
|
|
6690
7409
|
"operationId": "verifyCliSignup",
|
|
@@ -6710,24 +7429,49 @@ const operationManifest = [
|
|
|
6710
7429
|
"maxLength": 1024
|
|
6711
7430
|
}
|
|
6712
7431
|
},
|
|
6713
|
-
"required": [
|
|
6714
|
-
"signup_token",
|
|
6715
|
-
"verification_code",
|
|
6716
|
-
"password"
|
|
6717
|
-
]
|
|
7432
|
+
"required": ["signup_token", "verification_code"]
|
|
6718
7433
|
},
|
|
6719
7434
|
"responseSchema": {
|
|
6720
7435
|
"type": "object",
|
|
6721
7436
|
"properties": {
|
|
6722
7437
|
"api_key": {
|
|
6723
7438
|
"type": "string",
|
|
6724
|
-
"description": "
|
|
7439
|
+
"description": "Legacy alias for access_token. New CLI builds should persist access_token and refresh_token."
|
|
6725
7440
|
},
|
|
6726
7441
|
"key_id": {
|
|
7442
|
+
"type": "string",
|
|
7443
|
+
"format": "uuid",
|
|
7444
|
+
"description": "Legacy alias for oauth_grant_id"
|
|
7445
|
+
},
|
|
7446
|
+
"key_prefix": {
|
|
7447
|
+
"type": "string",
|
|
7448
|
+
"description": "Legacy display prefix derived from access_token"
|
|
7449
|
+
},
|
|
7450
|
+
"access_token": {
|
|
7451
|
+
"type": "string",
|
|
7452
|
+
"description": "OAuth access token for CLI API authentication"
|
|
7453
|
+
},
|
|
7454
|
+
"refresh_token": {
|
|
7455
|
+
"type": "string",
|
|
7456
|
+
"description": "OAuth refresh token used by the CLI to renew access"
|
|
7457
|
+
},
|
|
7458
|
+
"token_type": {
|
|
7459
|
+
"type": "string",
|
|
7460
|
+
"enum": ["Bearer"]
|
|
7461
|
+
},
|
|
7462
|
+
"expires_in": {
|
|
7463
|
+
"type": "integer",
|
|
7464
|
+
"description": "Seconds until access_token expires"
|
|
7465
|
+
},
|
|
7466
|
+
"auth_method": {
|
|
7467
|
+
"type": "string",
|
|
7468
|
+
"enum": ["oauth"]
|
|
7469
|
+
},
|
|
7470
|
+
"oauth_grant_id": {
|
|
6727
7471
|
"type": "string",
|
|
6728
7472
|
"format": "uuid"
|
|
6729
7473
|
},
|
|
6730
|
-
"
|
|
7474
|
+
"oauth_client_id": { "type": "string" },
|
|
6731
7475
|
"org_id": {
|
|
6732
7476
|
"type": "string",
|
|
6733
7477
|
"format": "uuid"
|
|
@@ -6738,12 +7482,19 @@ const operationManifest = [
|
|
|
6738
7482
|
"api_key",
|
|
6739
7483
|
"key_id",
|
|
6740
7484
|
"key_prefix",
|
|
7485
|
+
"access_token",
|
|
7486
|
+
"refresh_token",
|
|
7487
|
+
"token_type",
|
|
7488
|
+
"expires_in",
|
|
7489
|
+
"auth_method",
|
|
7490
|
+
"oauth_grant_id",
|
|
7491
|
+
"oauth_client_id",
|
|
6741
7492
|
"org_id",
|
|
6742
7493
|
"org_name"
|
|
6743
7494
|
]
|
|
6744
7495
|
},
|
|
6745
7496
|
"sdkName": "verifyCliSignup",
|
|
6746
|
-
"summary": "Verify CLI signup and create
|
|
7497
|
+
"summary": "Verify CLI signup and create OAuth session",
|
|
6747
7498
|
"tag": "CLI",
|
|
6748
7499
|
"tagCommand": "cli"
|
|
6749
7500
|
},
|
|
@@ -8437,7 +9188,7 @@ const operationManifest = [
|
|
|
8437
9188
|
"binaryResponse": false,
|
|
8438
9189
|
"bodyRequired": true,
|
|
8439
9190
|
"command": "create-function",
|
|
8440
|
-
"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).
|
|
9191
|
+
"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). Primitive signs\neach delivery and forwards the `Primitive-Signature` header to\nthe handler. Verify the raw request body with\n`PRIMITIVE_WEBHOOK_SECRET` before parsing JSON; after verification\nthe request body parses to an `email.received` event (see\n`EmailReceivedEvent` and the Webhook payload section for the full\nschema). Code is bundled before being uploaded; ship a single\nself-contained file rather than 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 internal runtime URL is not returned by\nthe API and is not a customer-facing integration surface.\n\n**Secrets.** New functions ship with the managed secrets\n(`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`,\n`PRIMITIVE_API_BASE_URL`) already bound. Add user-set secrets via\n`POST /functions/{id}/secrets`; secret writes only land in the\nrunning handler on the next redeploy.\n",
|
|
8441
9192
|
"hasJsonBody": true,
|
|
8442
9193
|
"method": "POST",
|
|
8443
9194
|
"operationId": "createFunction",
|
|
@@ -8485,17 +9236,12 @@ const operationManifest = [
|
|
|
8485
9236
|
"failed"
|
|
8486
9237
|
],
|
|
8487
9238
|
"description": "Lifecycle state of the latest deploy attempt:\n * `pending` — deploy in flight; the runtime has not yet\n confirmed the new bundle is live.\n * `deployed` — the running edge handler is the latest code.\n * `failed` — the most recent deploy attempt failed; the\n previously-live code (if any) is still running. The\n `deploy_error` field carries the error message.\n"
|
|
8488
|
-
},
|
|
8489
|
-
"gateway_url": {
|
|
8490
|
-
"type": "string",
|
|
8491
|
-
"format": "uri"
|
|
8492
9239
|
}
|
|
8493
9240
|
},
|
|
8494
9241
|
"required": [
|
|
8495
9242
|
"id",
|
|
8496
9243
|
"name",
|
|
8497
|
-
"deploy_status"
|
|
8498
|
-
"gateway_url"
|
|
9244
|
+
"deploy_status"
|
|
8499
9245
|
]
|
|
8500
9246
|
},
|
|
8501
9247
|
"sdkName": "createFunction",
|
|
@@ -8528,7 +9274,7 @@ const operationManifest = [
|
|
|
8528
9274
|
"key": {
|
|
8529
9275
|
"type": "string",
|
|
8530
9276
|
"pattern": "^[A-Z_][A-Z0-9_]*$",
|
|
8531
|
-
"description": "Uppercase letters, digits, and underscores. Must start with\na letter or underscore. System-managed keys (e.g.\nPRIMITIVE_WEBHOOK_SECRET) are reserved.\n"
|
|
9277
|
+
"description": "Uppercase letters, digits, and underscores. Must start with\na letter or underscore. System-managed keys (e.g.\nPRIMITIVE_WEBHOOK_SECRET, PRIMITIVE_API_KEY, and\nPRIMITIVE_API_BASE_URL) are reserved.\n"
|
|
8532
9278
|
},
|
|
8533
9279
|
"value": {
|
|
8534
9280
|
"type": "string",
|
|
@@ -8671,10 +9417,6 @@ const operationManifest = [
|
|
|
8671
9417
|
"type": ["string", "null"],
|
|
8672
9418
|
"format": "date-time"
|
|
8673
9419
|
},
|
|
8674
|
-
"gateway_url": {
|
|
8675
|
-
"type": "string",
|
|
8676
|
-
"format": "uri"
|
|
8677
|
-
},
|
|
8678
9420
|
"created_at": {
|
|
8679
9421
|
"type": "string",
|
|
8680
9422
|
"format": "date-time"
|
|
@@ -8689,7 +9431,6 @@ const operationManifest = [
|
|
|
8689
9431
|
"name",
|
|
8690
9432
|
"code",
|
|
8691
9433
|
"deploy_status",
|
|
8692
|
-
"gateway_url",
|
|
8693
9434
|
"created_at",
|
|
8694
9435
|
"updated_at"
|
|
8695
9436
|
]
|
|
@@ -9235,7 +9976,7 @@ const operationManifest = [
|
|
|
9235
9976
|
"binaryResponse": false,
|
|
9236
9977
|
"bodyRequired": false,
|
|
9237
9978
|
"command": "list-function-secrets",
|
|
9238
|
-
"description": "Returns metadata for every secret bound to the function, with\nmanaged entries (provisioned by Primitive) listed first and\nuser-set entries listed alphabetically after. **Values are\nnever returned.** Secret writes are write-only.\n\nManaged entries (e.g. `PRIMITIVE_WEBHOOK_SECRET`,\n`PRIMITIVE_API_KEY`) carry a
|
|
9979
|
+
"description": "Returns metadata for every secret bound to the function, with\nmanaged entries (provisioned by Primitive) listed first and\nuser-set entries listed alphabetically after. **Values are\nnever returned.** Secret writes are write-only.\n\nManaged entries (e.g. `PRIMITIVE_WEBHOOK_SECRET`,\n`PRIMITIVE_API_KEY`, `PRIMITIVE_API_BASE_URL`) carry a\n`description` instead of `created_at` / `updated_at`. They\ncannot be created, updated, or deleted via this API.\n",
|
|
9239
9980
|
"hasJsonBody": false,
|
|
9240
9981
|
"method": "GET",
|
|
9241
9982
|
"operationId": "listFunctionSecrets",
|
|
@@ -9291,7 +10032,7 @@ const operationManifest = [
|
|
|
9291
10032
|
"binaryResponse": false,
|
|
9292
10033
|
"bodyRequired": false,
|
|
9293
10034
|
"command": "list-functions",
|
|
9294
|
-
"description": "Returns every active (non-deleted) function in the org, newest\nfirst. Each entry carries
|
|
10035
|
+
"description": "Returns every active (non-deleted) function in the org, newest\nfirst. Each entry carries deploy status and timestamps. To\ninspect the source code or deploy errors, use `GET /functions/{id}`.\n",
|
|
9295
10036
|
"hasJsonBody": false,
|
|
9296
10037
|
"method": "GET",
|
|
9297
10038
|
"operationId": "listFunctions",
|
|
@@ -9328,11 +10069,6 @@ const operationManifest = [
|
|
|
9328
10069
|
"format": "date-time",
|
|
9329
10070
|
"description": "Timestamp of the most recent successful deploy. Null until the first deploy succeeds."
|
|
9330
10071
|
},
|
|
9331
|
-
"gateway_url": {
|
|
9332
|
-
"type": "string",
|
|
9333
|
-
"format": "uri",
|
|
9334
|
-
"description": "URL the platform's webhook delivery loop posts to in order\nto invoke the function. Reference only; not directly\ncallable from outside.\n"
|
|
9335
|
-
},
|
|
9336
10072
|
"created_at": {
|
|
9337
10073
|
"type": "string",
|
|
9338
10074
|
"format": "date-time"
|
|
@@ -9346,7 +10082,6 @@ const operationManifest = [
|
|
|
9346
10082
|
"id",
|
|
9347
10083
|
"name",
|
|
9348
10084
|
"deploy_status",
|
|
9349
|
-
"gateway_url",
|
|
9350
10085
|
"created_at",
|
|
9351
10086
|
"updated_at"
|
|
9352
10087
|
]
|
|
@@ -9575,10 +10310,6 @@ const operationManifest = [
|
|
|
9575
10310
|
"type": ["string", "null"],
|
|
9576
10311
|
"format": "date-time"
|
|
9577
10312
|
},
|
|
9578
|
-
"gateway_url": {
|
|
9579
|
-
"type": "string",
|
|
9580
|
-
"format": "uri"
|
|
9581
|
-
},
|
|
9582
10313
|
"created_at": {
|
|
9583
10314
|
"type": "string",
|
|
9584
10315
|
"format": "date-time"
|
|
@@ -9593,7 +10324,6 @@ const operationManifest = [
|
|
|
9593
10324
|
"name",
|
|
9594
10325
|
"code",
|
|
9595
10326
|
"deploy_status",
|
|
9596
|
-
"gateway_url",
|
|
9597
10327
|
"created_at",
|
|
9598
10328
|
"updated_at"
|
|
9599
10329
|
]
|
|
@@ -10788,28 +11518,34 @@ function requireString(value, key) {
|
|
|
10788
11518
|
return raw;
|
|
10789
11519
|
}
|
|
10790
11520
|
/**
|
|
10791
|
-
* Sentinel returned by parseCredentials when the on-disk credentials
|
|
10792
|
-
*
|
|
10793
|
-
*
|
|
10794
|
-
*
|
|
10795
|
-
* as a class-tagged error so loadCliCredentials can distinguish it
|
|
10796
|
-
* from a genuine malformed-credentials error.
|
|
11521
|
+
* Sentinel returned by parseCredentials when the on-disk credentials were
|
|
11522
|
+
* written by an API-key-based CLI. The caller treats this as "not logged in"
|
|
11523
|
+
* after clearing the local file. The backing API key is intentionally not
|
|
11524
|
+
* revoked; API keys still work when passed explicitly via --api-key/env.
|
|
10797
11525
|
*/
|
|
10798
|
-
var
|
|
11526
|
+
var LegacyApiKeyCredentialFormatError = class extends Error {
|
|
10799
11527
|
constructor() {
|
|
10800
|
-
super("
|
|
10801
|
-
this.name = "
|
|
11528
|
+
super("legacy_api_key_credential_format");
|
|
11529
|
+
this.name = "LegacyApiKeyCredentialFormatError";
|
|
10802
11530
|
}
|
|
10803
11531
|
};
|
|
10804
11532
|
function parseCredentials(raw) {
|
|
10805
11533
|
if (!isRecord$2(raw)) throw new Error(`Stored Primitive CLI credentials are malformed: expected a JSON object. ${MALFORMED_CREDENTIALS_HINT}`);
|
|
10806
|
-
if (
|
|
11534
|
+
if (raw.auth_method !== "oauth") {
|
|
11535
|
+
if (typeof raw.api_key === "string" || typeof raw.key_id === "string" || typeof raw.base_url === "string") throw new LegacyApiKeyCredentialFormatError();
|
|
11536
|
+
throw new Error(`Stored Primitive CLI credentials are malformed: auth_method must be oauth. ${MALFORMED_CREDENTIALS_HINT}`);
|
|
11537
|
+
}
|
|
10807
11538
|
const orgName = raw.org_name;
|
|
10808
11539
|
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}`);
|
|
11540
|
+
if (requireString(raw, "token_type") !== "Bearer") throw new Error(`Stored Primitive CLI credentials are malformed: token_type must be Bearer. ${MALFORMED_CREDENTIALS_HINT}`);
|
|
10809
11541
|
return {
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
11542
|
+
auth_method: "oauth",
|
|
11543
|
+
access_token: requireString(raw, "access_token"),
|
|
11544
|
+
refresh_token: requireString(raw, "refresh_token"),
|
|
11545
|
+
token_type: "Bearer",
|
|
11546
|
+
expires_at: requireString(raw, "expires_at"),
|
|
11547
|
+
oauth_grant_id: requireString(raw, "oauth_grant_id"),
|
|
11548
|
+
oauth_client_id: requireString(raw, "oauth_client_id"),
|
|
10813
11549
|
org_id: requireString(raw, "org_id"),
|
|
10814
11550
|
org_name: orgName,
|
|
10815
11551
|
api_base_url_1: requireString(raw, "api_base_url_1"),
|
|
@@ -10830,6 +11566,9 @@ function normalizeApiBaseUrl1(url) {
|
|
|
10830
11566
|
function normalizeApiBaseUrl2(url) {
|
|
10831
11567
|
return normalize(url, DEFAULT_API_BASE_URL_2);
|
|
10832
11568
|
}
|
|
11569
|
+
function cliAccessTokenExpiresAt(expiresInSeconds, now = Date.now) {
|
|
11570
|
+
return new Date(now() + expiresInSeconds * 1e3).toISOString();
|
|
11571
|
+
}
|
|
10833
11572
|
function loadCliCredentials(configDir) {
|
|
10834
11573
|
const path = credentialsPath(configDir);
|
|
10835
11574
|
let contents;
|
|
@@ -10843,11 +11582,11 @@ function loadCliCredentials(configDir) {
|
|
|
10843
11582
|
try {
|
|
10844
11583
|
return parseCredentials(JSON.parse(contents));
|
|
10845
11584
|
} catch (error) {
|
|
10846
|
-
if (error instanceof
|
|
11585
|
+
if (error instanceof LegacyApiKeyCredentialFormatError) {
|
|
10847
11586
|
try {
|
|
10848
11587
|
rmSync(path, { force: true });
|
|
10849
11588
|
} catch {}
|
|
10850
|
-
process.stderr.write("
|
|
11589
|
+
process.stderr.write("Removed local Primitive CLI API-key login state. API keys are still valid when passed explicitly, but `primitive login` now uses OAuth. Run `primitive login` to create an OAuth session. No API key was revoked.\n");
|
|
10851
11590
|
return null;
|
|
10852
11591
|
}
|
|
10853
11592
|
if (error instanceof SyntaxError) throw new Error("Stored Primitive CLI credentials are not valid JSON. Run `primitive logout` and then `primitive login`.");
|
|
@@ -10932,7 +11671,7 @@ function resolveCliAuth(params) {
|
|
|
10932
11671
|
};
|
|
10933
11672
|
const credentials = loadCliCredentials(params.configDir);
|
|
10934
11673
|
if (credentials) return {
|
|
10935
|
-
apiKey: credentials.
|
|
11674
|
+
apiKey: credentials.access_token,
|
|
10936
11675
|
apiBaseUrl1: params.apiBaseUrl1 ? normalizeApiBaseUrl1(params.apiBaseUrl1) : credentials.api_base_url_1,
|
|
10937
11676
|
apiBaseUrl2,
|
|
10938
11677
|
credentials,
|
|
@@ -10970,7 +11709,7 @@ function validateCliHeaderName(name) {
|
|
|
10970
11709
|
const trimmed = name.trim();
|
|
10971
11710
|
if (!trimmed) throw new Errors.CLIError("Header name must be a non-empty string.", { exit: 1 });
|
|
10972
11711
|
if (!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(trimmed)) throw new Errors.CLIError(`Invalid header name: ${name}`, { exit: 1 });
|
|
10973
|
-
if (trimmed.toLowerCase() === "authorization") throw new Errors.CLIError("The Authorization header is managed by PRIMITIVE_API_KEY or saved CLI credentials.", { exit: 1 });
|
|
11712
|
+
if (trimmed.toLowerCase() === "authorization") throw new Errors.CLIError("The Authorization header is managed by PRIMITIVE_API_KEY or saved OAuth CLI credentials.", { exit: 1 });
|
|
10974
11713
|
return trimmed;
|
|
10975
11714
|
}
|
|
10976
11715
|
function validateCliHeaderValue(value, name) {
|
|
@@ -11132,6 +11871,8 @@ function redactCliEnvironment(environment) {
|
|
|
11132
11871
|
//#endregion
|
|
11133
11872
|
//#region src/oclif/api-client.ts
|
|
11134
11873
|
const API_HEADERS_ENV = "PRIMITIVE_API_HEADERS";
|
|
11874
|
+
const OAUTH_REFRESH_SKEW_MS = 60 * 1e3;
|
|
11875
|
+
const SAVED_CLI_OAUTH_SESSION_EXPIRED_MESSAGE = "Saved Primitive CLI OAuth session expired or was revoked. Run `primitive login` to authenticate again.";
|
|
11135
11876
|
function mergeHeaders(...headers) {
|
|
11136
11877
|
const merged = {};
|
|
11137
11878
|
for (const headerSet of headers) {
|
|
@@ -11190,14 +11931,115 @@ function createCliApiClient(params) {
|
|
|
11190
11931
|
requestConfig
|
|
11191
11932
|
};
|
|
11192
11933
|
}
|
|
11193
|
-
function
|
|
11934
|
+
function oauthTokenEndpoint(apiBaseUrl1) {
|
|
11935
|
+
const url = new URL(apiBaseUrl1);
|
|
11936
|
+
url.pathname = "/oauth/token";
|
|
11937
|
+
url.search = "";
|
|
11938
|
+
url.hash = "";
|
|
11939
|
+
return url.toString();
|
|
11940
|
+
}
|
|
11941
|
+
function shouldRefresh(credentials, now) {
|
|
11942
|
+
const expiresAt = Date.parse(credentials.expires_at);
|
|
11943
|
+
if (!Number.isFinite(expiresAt)) return true;
|
|
11944
|
+
return expiresAt <= now() + OAUTH_REFRESH_SKEW_MS;
|
|
11945
|
+
}
|
|
11946
|
+
function isOAuthRefreshSuccess(value) {
|
|
11947
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
11948
|
+
const row = value;
|
|
11949
|
+
return typeof row.access_token === "string" && typeof row.refresh_token === "string" && row.token_type === "Bearer" && typeof row.expires_in === "number" && Number.isFinite(row.expires_in) && row.expires_in > 0;
|
|
11950
|
+
}
|
|
11951
|
+
function oauthErrorCode(value) {
|
|
11952
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
11953
|
+
const raw = value.error;
|
|
11954
|
+
return typeof raw === "string" ? raw : null;
|
|
11955
|
+
}
|
|
11956
|
+
function oauthErrorDescription(value) {
|
|
11957
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
11958
|
+
const raw = value.error_description;
|
|
11959
|
+
return typeof raw === "string" && raw.trim() ? raw : null;
|
|
11960
|
+
}
|
|
11961
|
+
async function refreshStoredCliCredentials(params) {
|
|
11962
|
+
const now = params.now ?? Date.now;
|
|
11963
|
+
if (!shouldRefresh(params.credentials, now)) return params.credentials;
|
|
11964
|
+
let releaseLock;
|
|
11965
|
+
try {
|
|
11966
|
+
if (!params.credentialsLockHeld) try {
|
|
11967
|
+
releaseLock = acquireCliCredentialsLock(params.configDir);
|
|
11968
|
+
} catch (error) {
|
|
11969
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
11970
|
+
throw new Errors.CLIError(detail, { exit: 1 });
|
|
11971
|
+
}
|
|
11972
|
+
const current = loadCliCredentials(params.configDir);
|
|
11973
|
+
if (!current) throw new Errors.CLIError("Saved Primitive CLI OAuth session is no longer available. Run `primitive login` to authenticate again.", { exit: 1 });
|
|
11974
|
+
if (!shouldRefresh(current, now)) return current;
|
|
11975
|
+
const fetchImpl = params.fetch ?? fetch;
|
|
11976
|
+
const body = new URLSearchParams({
|
|
11977
|
+
client_id: current.oauth_client_id,
|
|
11978
|
+
grant_type: "refresh_token",
|
|
11979
|
+
refresh_token: current.refresh_token
|
|
11980
|
+
});
|
|
11981
|
+
let response;
|
|
11982
|
+
try {
|
|
11983
|
+
response = await fetchImpl(oauthTokenEndpoint(params.apiBaseUrl1), {
|
|
11984
|
+
body,
|
|
11985
|
+
headers: {
|
|
11986
|
+
...params.headers ?? {},
|
|
11987
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
11988
|
+
},
|
|
11989
|
+
method: "POST"
|
|
11990
|
+
});
|
|
11991
|
+
} catch (error) {
|
|
11992
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
11993
|
+
throw new Errors.CLIError(`Could not refresh saved Primitive CLI OAuth credentials: ${detail}`, { exit: 1 });
|
|
11994
|
+
}
|
|
11995
|
+
const payload = await response.json().catch(() => null);
|
|
11996
|
+
if (!response.ok) {
|
|
11997
|
+
const code = oauthErrorCode(payload);
|
|
11998
|
+
const description = oauthErrorDescription(payload);
|
|
11999
|
+
if (code === "invalid_grant") {
|
|
12000
|
+
deleteCliCredentials(params.configDir);
|
|
12001
|
+
throw new Errors.CLIError(SAVED_CLI_OAUTH_SESSION_EXPIRED_MESSAGE, { exit: 1 });
|
|
12002
|
+
}
|
|
12003
|
+
throw new Errors.CLIError(`Could not refresh saved Primitive CLI OAuth credentials${description ? `: ${description}` : "."}`, { exit: 1 });
|
|
12004
|
+
}
|
|
12005
|
+
if (!isOAuthRefreshSuccess(payload)) throw new Errors.CLIError("Primitive OAuth token endpoint returned an unexpected refresh response.", { exit: 1 });
|
|
12006
|
+
const next = {
|
|
12007
|
+
...current,
|
|
12008
|
+
access_token: payload.access_token,
|
|
12009
|
+
expires_at: cliAccessTokenExpiresAt(payload.expires_in, now),
|
|
12010
|
+
refresh_token: payload.refresh_token,
|
|
12011
|
+
token_type: payload.token_type
|
|
12012
|
+
};
|
|
12013
|
+
saveCliCredentials(params.configDir, next);
|
|
12014
|
+
return next;
|
|
12015
|
+
} finally {
|
|
12016
|
+
releaseLock?.();
|
|
12017
|
+
}
|
|
12018
|
+
}
|
|
12019
|
+
async function createAuthenticatedCliApiClient(params) {
|
|
11194
12020
|
const requestConfig = resolveCliApiRequestConfig(params);
|
|
11195
|
-
|
|
12021
|
+
let auth = resolveCliAuth({
|
|
11196
12022
|
apiKey: params.apiKey,
|
|
11197
12023
|
apiBaseUrl1: requestConfig.apiBaseUrl1,
|
|
11198
12024
|
apiBaseUrl2: requestConfig.apiBaseUrl2,
|
|
11199
12025
|
configDir: params.configDir
|
|
11200
12026
|
});
|
|
12027
|
+
if (auth.source === "stored" && auth.credentials) {
|
|
12028
|
+
const refreshed = await refreshStoredCliCredentials({
|
|
12029
|
+
apiBaseUrl1: auth.apiBaseUrl1,
|
|
12030
|
+
configDir: params.configDir,
|
|
12031
|
+
credentials: auth.credentials,
|
|
12032
|
+
credentialsLockHeld: params.credentialsLockHeld,
|
|
12033
|
+
fetch: params.fetch,
|
|
12034
|
+
headers: requestConfig.headers,
|
|
12035
|
+
now: params.now
|
|
12036
|
+
});
|
|
12037
|
+
auth = {
|
|
12038
|
+
...auth,
|
|
12039
|
+
apiKey: refreshed.access_token,
|
|
12040
|
+
credentials: refreshed
|
|
12041
|
+
};
|
|
12042
|
+
}
|
|
11201
12043
|
return {
|
|
11202
12044
|
apiClient: new PrimitiveApiClient({
|
|
11203
12045
|
apiKey: auth.apiKey,
|
|
@@ -11513,7 +12355,7 @@ function extractErrorCode(payload) {
|
|
|
11513
12355
|
if (typeof direct === "string") return direct;
|
|
11514
12356
|
}
|
|
11515
12357
|
}
|
|
11516
|
-
const ERROR_CODE_HINTS = { [API_ERROR_CODES.unauthorized]: "Hint: run `primitive login`, pass --api-key explicitly, or set PRIMITIVE_API_KEY in your environment. `primitive whoami` is the fastest way to verify
|
|
12358
|
+
const ERROR_CODE_HINTS = { [API_ERROR_CODES.unauthorized]: "Hint: run `primitive login`, pass --api-key explicitly, or set PRIMITIVE_API_KEY in your environment. `primitive whoami` is the fastest way to verify auth is live." };
|
|
11517
12359
|
const NETWORK_ERROR_HINTS = {
|
|
11518
12360
|
ENETUNREACH: "Hint: the network is unreachable. If you're behind a proxy and set HTTP(S)_PROXY, re-run with NODE_USE_ENV_PROXY=1 (Node 22+ ignores those env vars by default). `primitive doctor` reports the local environment in one shot.",
|
|
11519
12361
|
ECONNREFUSED: "Hint: the server refused the connection. Check that your firewall allows egress to *.primitive.dev, that your PRIMITIVE_API_BASE_URL_* overrides (if any) point at a reachable host, and re-run with NODE_USE_ENV_PROXY=1 if you're behind a proxy. `primitive doctor` reports the local environment in one shot.",
|
|
@@ -11551,7 +12393,7 @@ function surfaceUnauthorizedHint(params) {
|
|
|
11551
12393
|
process.stderr.write("Saved Primitive CLI credentials were rejected by the overridden API base URL. The saved credential is preserved; unset PRIMITIVE_API_BASE_URL_1, run `primitive config reset` to clear configured URL overrides, or run `primitive logout` to remove the stored credential.\n");
|
|
11552
12394
|
return;
|
|
11553
12395
|
}
|
|
11554
|
-
process.stderr.write("Your saved Primitive CLI
|
|
12396
|
+
process.stderr.write("Your saved Primitive CLI OAuth session was rejected. If the command was working a moment ago, please retry; brief retries often clear transient rejections. If it keeps failing, run `primitive logout && primitive login` to mint a fresh session.\n");
|
|
11555
12397
|
}
|
|
11556
12398
|
function formatElapsed(ms) {
|
|
11557
12399
|
const seconds = ms / 1e3;
|
|
@@ -11601,7 +12443,7 @@ function bodyFieldFlag(field) {
|
|
|
11601
12443
|
function buildFlags(operation) {
|
|
11602
12444
|
const flags = {
|
|
11603
12445
|
"api-key": Flags.string({
|
|
11604
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
12446
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
11605
12447
|
env: "PRIMITIVE_API_KEY"
|
|
11606
12448
|
}),
|
|
11607
12449
|
"api-base-url-1": Flags.string({
|
|
@@ -11679,7 +12521,7 @@ function createOperationCommand(operation) {
|
|
|
11679
12521
|
const { flags } = await this.parse(OperationCommand);
|
|
11680
12522
|
const parsedFlags = flags;
|
|
11681
12523
|
await runWithTiming(parsedFlags.time === true, async () => {
|
|
11682
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
12524
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
11683
12525
|
apiKey: typeof parsedFlags["api-key"] === "string" ? parsedFlags["api-key"] : void 0,
|
|
11684
12526
|
apiBaseUrl1: typeof parsedFlags["api-base-url-1"] === "string" ? parsedFlags["api-base-url-1"] : void 0,
|
|
11685
12527
|
apiBaseUrl2: typeof parsedFlags["api-base-url-2"] === "string" ? parsedFlags["api-base-url-2"] : void 0,
|
|
@@ -11802,7 +12644,7 @@ async function pickDefaultFromAddress(apiClient, authFailureContext) {
|
|
|
11802
12644
|
...authFailureContext,
|
|
11803
12645
|
payload: errorPayload
|
|
11804
12646
|
});
|
|
11805
|
-
throw new Errors.CLIError("Cannot send:
|
|
12647
|
+
throw new Errors.CLIError("Cannot send: CLI auth is missing or invalid (see hint above).", { exit: 1 });
|
|
11806
12648
|
}
|
|
11807
12649
|
throw new Errors.CLIError(`Could not look up your verified domains to default --from. Pass --from explicitly. Underlying error: ${formatErrorPayload(errorPayload)}`);
|
|
11808
12650
|
}
|
|
@@ -12001,7 +12843,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
12001
12843
|
const message = args.message !== void 0 && args.message !== "" ? args.message : await readStdinToString();
|
|
12002
12844
|
if (!message.trim()) throw cliError$5("Message body is empty.");
|
|
12003
12845
|
await runWithTiming(flags.time, async () => {
|
|
12004
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
12846
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
12005
12847
|
apiKey: flags["api-key"],
|
|
12006
12848
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
12007
12849
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -12354,13 +13196,18 @@ function checkApiKey(opts) {
|
|
|
12354
13196
|
} catch (error) {
|
|
12355
13197
|
parseError = error instanceof Error ? error.message : String(error);
|
|
12356
13198
|
}
|
|
12357
|
-
if (parsed?.
|
|
13199
|
+
if (parsed?.auth_method === "oauth" && typeof parsed.access_token === "string" && parsed.access_token.length > 0) return {
|
|
12358
13200
|
status: "ok",
|
|
12359
|
-
message: `loaded from ${credsPath}`
|
|
13201
|
+
message: `loaded OAuth session from ${credsPath}`
|
|
13202
|
+
};
|
|
13203
|
+
if (typeof parsed?.api_key === "string" && parsed.api_key.length > 0) return {
|
|
13204
|
+
status: "fail",
|
|
13205
|
+
message: `${credsPath} contains legacy API-key login state`,
|
|
13206
|
+
hint: "Run `primitive login` to create saved OAuth credentials. Existing API keys still work with --api-key or PRIMITIVE_API_KEY."
|
|
12360
13207
|
};
|
|
12361
13208
|
if (parsed) return {
|
|
12362
13209
|
status: "fail",
|
|
12363
|
-
message: `${credsPath} exists but contains no
|
|
13210
|
+
message: `${credsPath} exists but contains no OAuth access_token`,
|
|
12364
13211
|
hint: "Run `primitive logout` to clear it, then `primitive login` to recreate."
|
|
12365
13212
|
};
|
|
12366
13213
|
return {
|
|
@@ -12371,7 +13218,7 @@ function checkApiKey(opts) {
|
|
|
12371
13218
|
}
|
|
12372
13219
|
return {
|
|
12373
13220
|
status: "fail",
|
|
12374
|
-
message: "no API key found",
|
|
13221
|
+
message: "no CLI OAuth session or explicit API key found",
|
|
12375
13222
|
hint: "Run `primitive login`, pass --api-key explicitly, or export PRIMITIVE_API_KEY=prim_..."
|
|
12376
13223
|
};
|
|
12377
13224
|
}
|
|
@@ -12454,12 +13301,12 @@ async function checkDomains(client) {
|
|
|
12454
13301
|
}
|
|
12455
13302
|
}
|
|
12456
13303
|
var DoctorCommand = class DoctorCommand extends Command {
|
|
12457
|
-
static description = `Run a one-shot environment health check: Node version, proxy env,
|
|
13304
|
+
static description = `Run a one-shot environment health check: Node version, proxy env, CLI auth resolution, /account reachability, and verified-domain status. Fails fast on anything that would block other commands and prints actionable hints for each warning or failure.`;
|
|
12458
13305
|
static summary = "Check the local environment and live API for common problems";
|
|
12459
13306
|
static examples = ["<%= config.bin %> doctor", "<%= config.bin %> doctor --api-key prim_..."];
|
|
12460
13307
|
static flags = {
|
|
12461
13308
|
"api-key": Flags.string({
|
|
12462
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
13309
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
12463
13310
|
env: "PRIMITIVE_API_KEY"
|
|
12464
13311
|
}),
|
|
12465
13312
|
"api-base-url-1": Flags.string({
|
|
@@ -12489,11 +13336,11 @@ var DoctorCommand = class DoctorCommand extends Command {
|
|
|
12489
13336
|
configDir: this.config.configDir
|
|
12490
13337
|
});
|
|
12491
13338
|
rows.push({
|
|
12492
|
-
label: "
|
|
13339
|
+
label: "Auth",
|
|
12493
13340
|
outcome: apiKeyCheck
|
|
12494
13341
|
});
|
|
12495
13342
|
if (apiKeyCheck.status !== "fail") {
|
|
12496
|
-
const { apiClient, auth } = createAuthenticatedCliApiClient({
|
|
13343
|
+
const { apiClient, auth } = await createAuthenticatedCliApiClient({
|
|
12497
13344
|
apiKey: flags["api-key"],
|
|
12498
13345
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
12499
13346
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -12575,7 +13422,7 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
12575
13422
|
];
|
|
12576
13423
|
static flags = {
|
|
12577
13424
|
"api-key": Flags.string({
|
|
12578
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
13425
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
12579
13426
|
env: "PRIMITIVE_API_KEY"
|
|
12580
13427
|
}),
|
|
12581
13428
|
"api-base-url-1": Flags.string({
|
|
@@ -12600,7 +13447,7 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
12600
13447
|
async run() {
|
|
12601
13448
|
const { flags } = await this.parse(EmailsLatestCommand);
|
|
12602
13449
|
await runWithTiming(flags.time, async () => {
|
|
12603
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
13450
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
12604
13451
|
apiKey: flags["api-key"],
|
|
12605
13452
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
12606
13453
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -12655,7 +13502,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
12655
13502
|
];
|
|
12656
13503
|
static flags = {
|
|
12657
13504
|
"api-key": Flags.string({
|
|
12658
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
13505
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
12659
13506
|
env: "PRIMITIVE_API_KEY"
|
|
12660
13507
|
}),
|
|
12661
13508
|
"api-base-url-1": Flags.string({
|
|
@@ -12707,7 +13554,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
12707
13554
|
};
|
|
12708
13555
|
async run() {
|
|
12709
13556
|
const { flags } = await this.parse(EmailsWaitCommand);
|
|
12710
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
13557
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
12711
13558
|
apiKey: flags["api-key"],
|
|
12712
13559
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
12713
13560
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -12781,7 +13628,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
12781
13628
|
];
|
|
12782
13629
|
static flags = {
|
|
12783
13630
|
"api-key": Flags.string({
|
|
12784
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
13631
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
12785
13632
|
env: "PRIMITIVE_API_KEY"
|
|
12786
13633
|
}),
|
|
12787
13634
|
"api-base-url-1": Flags.string({
|
|
@@ -12829,7 +13676,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
12829
13676
|
};
|
|
12830
13677
|
async run() {
|
|
12831
13678
|
const { flags } = await this.parse(EmailsWatchCommand);
|
|
12832
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
13679
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
12833
13680
|
apiKey: flags["api-key"],
|
|
12834
13681
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
12835
13682
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -12913,7 +13760,6 @@ function toDeployWaitSnapshot(value) {
|
|
|
12913
13760
|
...value.deploy_error !== void 0 ? { deploy_error: value.deploy_error } : {},
|
|
12914
13761
|
deploy_status: value.deploy_status,
|
|
12915
13762
|
...value.deployed_at !== void 0 ? { deployed_at: value.deployed_at } : {},
|
|
12916
|
-
gateway_url: value.gateway_url,
|
|
12917
13763
|
id: value.id,
|
|
12918
13764
|
name: value.name,
|
|
12919
13765
|
...value.updated_at !== void 0 ? { updated_at: value.updated_at } : {}
|
|
@@ -13448,7 +14294,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
13448
14294
|
];
|
|
13449
14295
|
static flags = {
|
|
13450
14296
|
"api-key": Flags.string({
|
|
13451
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
14297
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
13452
14298
|
env: "PRIMITIVE_API_KEY"
|
|
13453
14299
|
}),
|
|
13454
14300
|
"api-base-url-1": Flags.string({
|
|
@@ -13525,7 +14371,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
13525
14371
|
const code = readTextFileFlag(flags.file, "--file");
|
|
13526
14372
|
const sourceMap = flags["source-map-file"] ? readTextFileFlag(flags["source-map-file"], "--source-map-file") : void 0;
|
|
13527
14373
|
emitRawSendMailFetchWarning(code, (chunk) => process.stderr.write(chunk));
|
|
13528
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
14374
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
13529
14375
|
apiKey: flags["api-key"],
|
|
13530
14376
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
13531
14377
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -13637,22 +14483,33 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
13637
14483
|
name: "Primitive Team",
|
|
13638
14484
|
url: "https://primitive.dev"
|
|
13639
14485
|
};
|
|
13640
|
-
const SDK_VERSION_RANGE = "^0.31.
|
|
13641
|
-
const CLI_VERSION_RANGE = "^0.31.
|
|
14486
|
+
const SDK_VERSION_RANGE = "^0.31.1";
|
|
14487
|
+
const CLI_VERSION_RANGE = "^0.31.1";
|
|
13642
14488
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
13643
14489
|
function renderHandler() {
|
|
13644
|
-
return `// env.PRIMITIVE_API_KEY
|
|
14490
|
+
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
14491
|
+
// env.PRIMITIVE_API_BASE_URL are auto-injected by the Primitive Functions runtime.
|
|
13645
14492
|
//
|
|
13646
14493
|
// Imports are taken from the \`/api\` subpath, NOT the package root.
|
|
13647
14494
|
// The root export pulls in webhook signing helpers that depend on
|
|
13648
14495
|
// \`node:crypto\`, which breaks Workers-style bundles. The \`/api\`
|
|
13649
|
-
// subpath is Workers-safe and exposes everything a handler needs
|
|
14496
|
+
// subpath is Workers-safe and exposes everything a handler needs,
|
|
14497
|
+
// including Web Crypto signature verification.
|
|
13650
14498
|
import {
|
|
13651
14499
|
createPrimitiveClient,
|
|
13652
14500
|
normalizeReceivedEmail,
|
|
14501
|
+
PRIMITIVE_SIGNATURE_HEADER,
|
|
13653
14502
|
type EmailReceivedEvent,
|
|
14503
|
+
verifyWebhookSignature,
|
|
14504
|
+
WebhookVerificationError,
|
|
13654
14505
|
} from "@primitivedotdev/sdk/api";
|
|
13655
14506
|
|
|
14507
|
+
interface Env {
|
|
14508
|
+
PRIMITIVE_API_KEY: string;
|
|
14509
|
+
PRIMITIVE_API_BASE_URL: string;
|
|
14510
|
+
PRIMITIVE_WEBHOOK_SECRET: string;
|
|
14511
|
+
}
|
|
14512
|
+
|
|
13656
14513
|
// Loop-protection knob. Only used by the isLoop helper below; the
|
|
13657
14514
|
// handler's outbound reply address is server-defaulted (no
|
|
13658
14515
|
// from-address parameter is passed to client.reply). Update this if
|
|
@@ -13702,10 +14559,26 @@ export function isLoop(event: EmailReceivedEvent): boolean {
|
|
|
13702
14559
|
export default {
|
|
13703
14560
|
async fetch(
|
|
13704
14561
|
req: Request,
|
|
13705
|
-
env:
|
|
14562
|
+
env: Env,
|
|
13706
14563
|
): Promise<Response> {
|
|
13707
14564
|
try {
|
|
13708
|
-
const
|
|
14565
|
+
const rawBody = await req.text();
|
|
14566
|
+
const signatureHeader = req.headers.get(PRIMITIVE_SIGNATURE_HEADER) ?? "";
|
|
14567
|
+
|
|
14568
|
+
try {
|
|
14569
|
+
await verifyWebhookSignature({
|
|
14570
|
+
rawBody,
|
|
14571
|
+
signatureHeader,
|
|
14572
|
+
secret: env.PRIMITIVE_WEBHOOK_SECRET,
|
|
14573
|
+
});
|
|
14574
|
+
} catch (signatureError) {
|
|
14575
|
+
if (signatureError instanceof WebhookVerificationError) {
|
|
14576
|
+
return new Response("invalid signature", { status: 401 });
|
|
14577
|
+
}
|
|
14578
|
+
throw signatureError;
|
|
14579
|
+
}
|
|
14580
|
+
|
|
14581
|
+
const event = JSON.parse(rawBody) as EmailReceivedEvent;
|
|
13709
14582
|
|
|
13710
14583
|
// Only "email.received" exists today. Future event types will
|
|
13711
14584
|
// arrive with a different discriminator; return 2xx so the
|
|
@@ -13715,15 +14588,17 @@ export default {
|
|
|
13715
14588
|
return Response.json({ ok: true, skipped: event.event });
|
|
13716
14589
|
}
|
|
13717
14590
|
|
|
13718
|
-
// Loop protection runs immediately after
|
|
13719
|
-
//
|
|
13720
|
-
//
|
|
13721
|
-
// how to extend it.
|
|
14591
|
+
// Loop protection runs immediately after signature verification
|
|
14592
|
+
// and the event-type check. See isLoop above for what's covered
|
|
14593
|
+
// and how to extend it.
|
|
13722
14594
|
if (isLoop(event)) {
|
|
13723
14595
|
return Response.json({ ok: true, skipped: "loop" });
|
|
13724
14596
|
}
|
|
13725
14597
|
|
|
13726
|
-
const client = createPrimitiveClient({
|
|
14598
|
+
const client = createPrimitiveClient({
|
|
14599
|
+
apiKey: env.PRIMITIVE_API_KEY,
|
|
14600
|
+
apiBaseUrl1: env.PRIMITIVE_API_BASE_URL,
|
|
14601
|
+
});
|
|
13727
14602
|
|
|
13728
14603
|
// Recipient gate
|
|
13729
14604
|
// https://www.primitive.dev/docs/sending#who-you-can-send-to
|
|
@@ -13891,7 +14766,7 @@ function renderEmailReplyTemplateFiles(name) {
|
|
|
13891
14766
|
const FUNCTION_TEMPLATES = [{
|
|
13892
14767
|
author: PRIMITIVE_TEAM_AUTHOR,
|
|
13893
14768
|
dependencies: ["@primitivedotdev/sdk"],
|
|
13894
|
-
description: "A deployable TypeScript email handler that
|
|
14769
|
+
description: "A deployable TypeScript email handler that verifies signed email.received events, skips likely loops, and replies with the Primitive SDK.",
|
|
13895
14770
|
devDependencies: [
|
|
13896
14771
|
"@primitivedotdev/cli",
|
|
13897
14772
|
"esbuild",
|
|
@@ -14082,7 +14957,7 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
|
|
|
14082
14957
|
];
|
|
14083
14958
|
static flags = {
|
|
14084
14959
|
"api-key": Flags.string({
|
|
14085
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
14960
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
14086
14961
|
env: "PRIMITIVE_API_KEY"
|
|
14087
14962
|
}),
|
|
14088
14963
|
"api-base-url-1": Flags.string({
|
|
@@ -14121,7 +14996,7 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
|
|
|
14121
14996
|
if (flags["poll-interval"] <= 0) this.error("--poll-interval must be greater than 0.", { exit: 2 });
|
|
14122
14997
|
if (flags.follow && flags.cursor) this.error("--cursor cannot be combined with --follow.", { exit: 2 });
|
|
14123
14998
|
await runWithTiming(flags.time, async () => {
|
|
14124
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
14999
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
14125
15000
|
apiKey: flags["api-key"],
|
|
14126
15001
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
14127
15002
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -14280,7 +15155,7 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
14280
15155
|
];
|
|
14281
15156
|
static flags = {
|
|
14282
15157
|
"api-key": Flags.string({
|
|
14283
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
15158
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
14284
15159
|
env: "PRIMITIVE_API_KEY"
|
|
14285
15160
|
}),
|
|
14286
15161
|
"api-base-url-1": Flags.string({
|
|
@@ -14357,7 +15232,7 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
14357
15232
|
const code = readTextFileFlag(flags.file, "--file");
|
|
14358
15233
|
const sourceMap = flags["source-map-file"] ? readTextFileFlag(flags["source-map-file"], "--source-map-file") : void 0;
|
|
14359
15234
|
emitRawSendMailFetchWarning(code, (chunk) => process.stderr.write(chunk));
|
|
14360
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
15235
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
14361
15236
|
apiKey: flags["api-key"],
|
|
14362
15237
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
14363
15238
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -14540,7 +15415,7 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
14540
15415
|
];
|
|
14541
15416
|
static flags = {
|
|
14542
15417
|
"api-key": Flags.string({
|
|
14543
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
15418
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
14544
15419
|
env: "PRIMITIVE_API_KEY"
|
|
14545
15420
|
}),
|
|
14546
15421
|
"api-base-url-1": Flags.string({
|
|
@@ -14572,7 +15447,7 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
14572
15447
|
async run() {
|
|
14573
15448
|
const { flags } = await this.parse(FunctionsSetSecretCommand);
|
|
14574
15449
|
await runWithTiming(flags.time, async () => {
|
|
14575
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
15450
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
14576
15451
|
apiKey: flags["api-key"],
|
|
14577
15452
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
14578
15453
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -14763,7 +15638,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
14763
15638
|
];
|
|
14764
15639
|
static flags = {
|
|
14765
15640
|
"api-key": Flags.string({
|
|
14766
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
15641
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
14767
15642
|
env: "PRIMITIVE_API_KEY"
|
|
14768
15643
|
}),
|
|
14769
15644
|
"api-base-url-1": Flags.string({
|
|
@@ -14799,7 +15674,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
14799
15674
|
const { flags } = await this.parse(FunctionsTestFunctionCommand);
|
|
14800
15675
|
const shouldWait = flags.wait || flags["show-sends"];
|
|
14801
15676
|
const shouldShowSends = flags["show-sends"];
|
|
14802
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
15677
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
14803
15678
|
apiKey: flags["api-key"],
|
|
14804
15679
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
14805
15680
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -14949,8 +15824,25 @@ async function checkExistingLogin(params) {
|
|
|
14949
15824
|
configDir: params.configDir
|
|
14950
15825
|
});
|
|
14951
15826
|
const probeApiBaseUrl1 = requestConfig.apiBaseUrl1 ?? params.credentials.api_base_url_1;
|
|
15827
|
+
let credentials = params.credentials;
|
|
15828
|
+
try {
|
|
15829
|
+
credentials = await refreshStoredCliCredentials({
|
|
15830
|
+
apiBaseUrl1: probeApiBaseUrl1,
|
|
15831
|
+
configDir: params.configDir,
|
|
15832
|
+
credentials,
|
|
15833
|
+
credentialsLockHeld: params.credentialsLockHeld,
|
|
15834
|
+
headers: requestConfig.headers
|
|
15835
|
+
});
|
|
15836
|
+
} catch (error) {
|
|
15837
|
+
if (loadCliCredentials(params.configDir) === null) return { status: "removed_stale" };
|
|
15838
|
+
return {
|
|
15839
|
+
status: "blocked",
|
|
15840
|
+
payload: error,
|
|
15841
|
+
message: "A saved Primitive CLI OAuth session exists, but the CLI could not refresh it. Run `primitive logout` before logging in again."
|
|
15842
|
+
};
|
|
15843
|
+
}
|
|
14952
15844
|
const apiClient = new PrimitiveApiClient({
|
|
14953
|
-
apiKey:
|
|
15845
|
+
apiKey: credentials.access_token,
|
|
14954
15846
|
apiBaseUrl1: probeApiBaseUrl1,
|
|
14955
15847
|
apiBaseUrl2: requestConfig.resolvedApiBaseUrl2,
|
|
14956
15848
|
headers: requestConfig.headers
|
|
@@ -14965,17 +15857,17 @@ async function checkExistingLogin(params) {
|
|
|
14965
15857
|
const baseUrlDiffersFromSaved = requestConfig.baseUrlOverridden && requestConfig.apiBaseUrl1 !== params.credentials.api_base_url_1;
|
|
14966
15858
|
if (code === API_ERROR_CODES.unauthorized && !baseUrlDiffersFromSaved) {
|
|
14967
15859
|
deleteCliCredentials(params.configDir);
|
|
14968
|
-
process.stderr.write("Removed saved Primitive CLI credentials because the existing
|
|
15860
|
+
process.stderr.write("Removed saved Primitive CLI OAuth credentials because the existing session was rejected during login. Continuing with a fresh login.\n");
|
|
14969
15861
|
return { status: "removed_stale" };
|
|
14970
15862
|
}
|
|
14971
15863
|
return {
|
|
14972
15864
|
status: "blocked",
|
|
14973
15865
|
payload,
|
|
14974
|
-
message: code === API_ERROR_CODES.unauthorized ? "Saved Primitive CLI credentials were rejected by an API URL different from the one they were saved with. Run `primitive logout` to remove them, or switch back to the original environment before logging in again." : "A saved Primitive CLI
|
|
15866
|
+
message: code === API_ERROR_CODES.unauthorized ? "Saved Primitive CLI OAuth credentials were rejected by an API URL different from the one they were saved with. Run `primitive logout` to remove them, or switch back to the original environment before logging in again." : "A saved Primitive CLI OAuth session exists, but the CLI could not verify whether it is still valid. Run `primitive logout` before logging in again."
|
|
14975
15867
|
};
|
|
14976
15868
|
}
|
|
14977
15869
|
var LoginCommand = class LoginCommand extends Command {
|
|
14978
|
-
static description = "Log in by opening Primitive in your browser and saving an org-scoped
|
|
15870
|
+
static description = "Log in by opening Primitive in your browser and saving an org-scoped OAuth session locally.";
|
|
14979
15871
|
static summary = "Log in with browser approval";
|
|
14980
15872
|
static examples = [
|
|
14981
15873
|
"<%= config.bin %> login",
|
|
@@ -15029,7 +15921,8 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
15029
15921
|
const existingStatus = await checkExistingLogin({
|
|
15030
15922
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
15031
15923
|
configDir: this.config.configDir,
|
|
15032
|
-
credentials: existing
|
|
15924
|
+
credentials: existing,
|
|
15925
|
+
credentialsLockHeld: true
|
|
15033
15926
|
});
|
|
15034
15927
|
if (existingStatus.status === "removed_stale") process.stderr.write("Continuing with a new Primitive CLI login...\n");
|
|
15035
15928
|
else if (existingStatus.status === "blocked") {
|
|
@@ -15070,13 +15963,17 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
15070
15963
|
const login = unwrapData$2(polled.data);
|
|
15071
15964
|
if (!login) throw cliError$2("Primitive API returned an empty CLI poll response.");
|
|
15072
15965
|
saveCliCredentials(this.config.configDir, {
|
|
15073
|
-
|
|
15966
|
+
access_token: login.access_token,
|
|
15074
15967
|
api_base_url_1: apiBaseUrl1,
|
|
15968
|
+
auth_method: "oauth",
|
|
15075
15969
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15076
|
-
|
|
15077
|
-
|
|
15970
|
+
expires_at: cliAccessTokenExpiresAt(login.expires_in),
|
|
15971
|
+
oauth_client_id: login.oauth_client_id,
|
|
15972
|
+
oauth_grant_id: login.oauth_grant_id,
|
|
15078
15973
|
org_id: login.org_id,
|
|
15079
|
-
org_name: login.org_name
|
|
15974
|
+
org_name: login.org_name,
|
|
15975
|
+
refresh_token: login.refresh_token,
|
|
15976
|
+
token_type: login.token_type
|
|
15080
15977
|
});
|
|
15081
15978
|
const org = login.org_name ? ` (${login.org_name})` : "";
|
|
15082
15979
|
process.stderr.write(`Logged in to org ${login.org_id}${org}.\n`);
|
|
@@ -15098,22 +15995,80 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
15098
15995
|
if (code === API_ERROR_CODES.expiredToken) throw cliError$2("Primitive CLI login expired. Run `primitive login` again.");
|
|
15099
15996
|
if (code === API_ERROR_CODES.invalidDeviceCode) throw cliError$2("Primitive CLI login device code is invalid. Run `primitive login` again.");
|
|
15100
15997
|
writeErrorWithHints(payload);
|
|
15101
|
-
throw cliError$2("Primitive CLI login failed while polling for approval.");
|
|
15998
|
+
throw cliError$2("Primitive CLI login failed while polling for approval.");
|
|
15999
|
+
}
|
|
16000
|
+
throw cliError$2("Primitive CLI login expired. Run `primitive login` again.");
|
|
16001
|
+
}
|
|
16002
|
+
};
|
|
16003
|
+
//#endregion
|
|
16004
|
+
//#region src/oclif/commands/logout.ts
|
|
16005
|
+
function cliError$1(message) {
|
|
16006
|
+
return new Errors.CLIError(message, { exit: 1 });
|
|
16007
|
+
}
|
|
16008
|
+
function unwrapData$1(value) {
|
|
16009
|
+
return value?.data ?? null;
|
|
16010
|
+
}
|
|
16011
|
+
function isSavedOAuthSessionExpiredError(error) {
|
|
16012
|
+
return error instanceof Error && error.message === "Saved Primitive CLI OAuth session expired or was revoked. Run `primitive login` to authenticate again.";
|
|
16013
|
+
}
|
|
16014
|
+
async function runLogoutWithCredentialLock(params) {
|
|
16015
|
+
const deps = {
|
|
16016
|
+
cliLogout,
|
|
16017
|
+
createAuthenticatedCliApiClient,
|
|
16018
|
+
...params.deps
|
|
16019
|
+
};
|
|
16020
|
+
let credentials;
|
|
16021
|
+
try {
|
|
16022
|
+
credentials = loadCliCredentials(params.configDir);
|
|
16023
|
+
} catch (error) {
|
|
16024
|
+
deleteCliCredentials(params.configDir);
|
|
16025
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
16026
|
+
process.stderr.write(`Removed unreadable Primitive CLI credentials. Backing OAuth grant was not revoked: ${detail}\n`);
|
|
16027
|
+
process.exitCode = 1;
|
|
16028
|
+
return;
|
|
16029
|
+
}
|
|
16030
|
+
if (!credentials) throw cliError$1("Not logged in. Run `primitive login` to create saved CLI credentials.");
|
|
16031
|
+
let authenticated;
|
|
16032
|
+
try {
|
|
16033
|
+
authenticated = await deps.createAuthenticatedCliApiClient({
|
|
16034
|
+
apiBaseUrl1: params.flags["api-base-url-1"],
|
|
16035
|
+
configDir: params.configDir,
|
|
16036
|
+
credentialsLockHeld: true
|
|
16037
|
+
});
|
|
16038
|
+
} catch (error) {
|
|
16039
|
+
if (isSavedOAuthSessionExpiredError(error) && loadCliCredentials(params.configDir) === null) {
|
|
16040
|
+
process.stderr.write("Logged out (OAuth session was already expired or revoked on the server).\n");
|
|
16041
|
+
return;
|
|
16042
|
+
}
|
|
16043
|
+
throw error;
|
|
16044
|
+
}
|
|
16045
|
+
const freshCredentials = authenticated.auth.credentials ?? credentials;
|
|
16046
|
+
const result = await deps.cliLogout({
|
|
16047
|
+
body: { key_id: freshCredentials.oauth_grant_id },
|
|
16048
|
+
client: authenticated.apiClient.client,
|
|
16049
|
+
responseStyle: "fields"
|
|
16050
|
+
});
|
|
16051
|
+
if (result.error) {
|
|
16052
|
+
const payload = extractErrorPayload(result.error);
|
|
16053
|
+
const code = extractErrorCode(payload);
|
|
16054
|
+
if (code === API_ERROR_CODES.unauthorized || code === API_ERROR_CODES.notFound) {
|
|
16055
|
+
deleteCliCredentials(params.configDir);
|
|
16056
|
+
writeErrorWithHints(payload);
|
|
16057
|
+
process.stderr.write("Removed saved Primitive CLI credentials because the backing OAuth grant is already unavailable.\n");
|
|
16058
|
+
process.exitCode = 1;
|
|
16059
|
+
return;
|
|
15102
16060
|
}
|
|
15103
|
-
|
|
16061
|
+
writeErrorWithHints(payload);
|
|
16062
|
+
throw cliError$1("Could not revoke the saved Primitive CLI OAuth grant.");
|
|
15104
16063
|
}
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
return new Errors.CLIError(message, { exit: 1 });
|
|
15110
|
-
}
|
|
15111
|
-
function unwrapData$1(value) {
|
|
15112
|
-
return value?.data ?? null;
|
|
16064
|
+
const logout = unwrapData$1(result.data);
|
|
16065
|
+
deleteCliCredentials(params.configDir);
|
|
16066
|
+
const grantId = logout?.oauth_grant_id ?? freshCredentials.oauth_grant_id;
|
|
16067
|
+
process.stderr.write(`Logged out and revoked OAuth grant ${grantId}.\n`);
|
|
15113
16068
|
}
|
|
15114
16069
|
var LogoutCommand = class LogoutCommand extends Command {
|
|
15115
|
-
static description = "Log out by revoking the saved Primitive CLI
|
|
15116
|
-
static summary = "Log out and revoke the saved CLI
|
|
16070
|
+
static description = "Log out by revoking the saved Primitive CLI OAuth grant and deleting local credentials.";
|
|
16071
|
+
static summary = "Log out and revoke the saved CLI OAuth grant";
|
|
15117
16072
|
static examples = ["<%= config.bin %> logout"];
|
|
15118
16073
|
static flags = { "api-base-url-1": Flags.string({
|
|
15119
16074
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
@@ -15129,56 +16084,14 @@ var LogoutCommand = class LogoutCommand extends Command {
|
|
|
15129
16084
|
throw cliError$1(error instanceof Error ? error.message : String(error));
|
|
15130
16085
|
}
|
|
15131
16086
|
try {
|
|
15132
|
-
await
|
|
16087
|
+
await runLogoutWithCredentialLock({
|
|
16088
|
+
configDir: this.config.configDir,
|
|
16089
|
+
flags
|
|
16090
|
+
});
|
|
15133
16091
|
} finally {
|
|
15134
16092
|
releaseCredentialsLock();
|
|
15135
16093
|
}
|
|
15136
16094
|
}
|
|
15137
|
-
async runWithCredentialLock(flags) {
|
|
15138
|
-
let credentials;
|
|
15139
|
-
try {
|
|
15140
|
-
credentials = loadCliCredentials(this.config.configDir);
|
|
15141
|
-
} catch (error) {
|
|
15142
|
-
deleteCliCredentials(this.config.configDir);
|
|
15143
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
15144
|
-
process.stderr.write(`Removed unreadable Primitive CLI credentials. Backing API key was not revoked: ${detail}\n`);
|
|
15145
|
-
process.exitCode = 1;
|
|
15146
|
-
return;
|
|
15147
|
-
}
|
|
15148
|
-
if (!credentials) throw cliError$1("Not logged in. Run `primitive login` to create saved CLI credentials.");
|
|
15149
|
-
const requestConfig = resolveCliApiRequestConfig({
|
|
15150
|
-
apiBaseUrl1: flags["api-base-url-1"],
|
|
15151
|
-
configDir: this.config.configDir
|
|
15152
|
-
});
|
|
15153
|
-
const apiBaseUrl1 = requestConfig.apiBaseUrl1 ? normalizeApiBaseUrl1(requestConfig.apiBaseUrl1) : credentials.api_base_url_1;
|
|
15154
|
-
const apiClient = new PrimitiveApiClient({
|
|
15155
|
-
apiKey: credentials.api_key,
|
|
15156
|
-
apiBaseUrl1,
|
|
15157
|
-
headers: requestConfig.headers
|
|
15158
|
-
});
|
|
15159
|
-
const result = await cliLogout({
|
|
15160
|
-
body: { key_id: credentials.key_id },
|
|
15161
|
-
client: apiClient.client,
|
|
15162
|
-
responseStyle: "fields"
|
|
15163
|
-
});
|
|
15164
|
-
if (result.error) {
|
|
15165
|
-
const payload = extractErrorPayload(result.error);
|
|
15166
|
-
const code = extractErrorCode(payload);
|
|
15167
|
-
if (code === API_ERROR_CODES.unauthorized || code === API_ERROR_CODES.notFound) {
|
|
15168
|
-
deleteCliCredentials(this.config.configDir);
|
|
15169
|
-
writeErrorWithHints(payload);
|
|
15170
|
-
process.stderr.write("Removed saved Primitive CLI credentials because the backing API key is already unavailable.\n");
|
|
15171
|
-
process.exitCode = 1;
|
|
15172
|
-
return;
|
|
15173
|
-
}
|
|
15174
|
-
writeErrorWithHints(payload);
|
|
15175
|
-
throw cliError$1("Could not revoke the saved Primitive CLI API key.");
|
|
15176
|
-
}
|
|
15177
|
-
const logout = unwrapData$1(result.data);
|
|
15178
|
-
deleteCliCredentials(this.config.configDir);
|
|
15179
|
-
const keyId = logout?.key_id ?? credentials.key_id;
|
|
15180
|
-
process.stderr.write(`Logged out and revoked API key ${keyId}.\n`);
|
|
15181
|
-
}
|
|
15182
16095
|
};
|
|
15183
16096
|
//#endregion
|
|
15184
16097
|
//#region src/oclif/message-body-sources.ts
|
|
@@ -15295,7 +16208,7 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
15295
16208
|
];
|
|
15296
16209
|
static flags = {
|
|
15297
16210
|
"api-key": Flags.string({
|
|
15298
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
16211
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15299
16212
|
env: "PRIMITIVE_API_KEY"
|
|
15300
16213
|
}),
|
|
15301
16214
|
"api-base-url-1": Flags.string({
|
|
@@ -15335,7 +16248,7 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
15335
16248
|
});
|
|
15336
16249
|
if (bodies.kind === "error") throw new Errors.CLIError(bodies.message);
|
|
15337
16250
|
await runWithTiming(flags.time, async () => {
|
|
15338
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
16251
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
15339
16252
|
apiKey: flags["api-key"],
|
|
15340
16253
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
15341
16254
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -15394,7 +16307,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
15394
16307
|
];
|
|
15395
16308
|
static flags = {
|
|
15396
16309
|
"api-key": Flags.string({
|
|
15397
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
16310
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15398
16311
|
env: "PRIMITIVE_API_KEY"
|
|
15399
16312
|
}),
|
|
15400
16313
|
"api-base-url-1": Flags.string({
|
|
@@ -15436,7 +16349,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
15436
16349
|
});
|
|
15437
16350
|
if (bodies.kind === "error") throw new Errors.CLIError(bodies.message);
|
|
15438
16351
|
await runWithTiming(flags.time, async () => {
|
|
15439
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
16352
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
15440
16353
|
apiKey: flags["api-key"],
|
|
15441
16354
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
15442
16355
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -15484,7 +16397,6 @@ var SendCommand = class SendCommand extends Command {
|
|
|
15484
16397
|
//#endregion
|
|
15485
16398
|
//#region src/oclif/commands/signup.ts
|
|
15486
16399
|
const INVALID_VERIFICATION_CODE = "invalid_verification_code";
|
|
15487
|
-
const CLERK_PASSWORD_REJECTED = "clerk_password_rejected";
|
|
15488
16400
|
const EXPIRED_TOKEN = "expired_token";
|
|
15489
16401
|
const INVALID_SIGNUP_TOKEN = "invalid_signup_token";
|
|
15490
16402
|
const SLOW_DOWN = "slow_down";
|
|
@@ -15498,6 +16410,9 @@ function unwrapData(value) {
|
|
|
15498
16410
|
function isRecord(value) {
|
|
15499
16411
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15500
16412
|
}
|
|
16413
|
+
function normalizeEmail(email) {
|
|
16414
|
+
return email.trim().toLowerCase();
|
|
16415
|
+
}
|
|
15501
16416
|
function pendingSignupFromJson(value) {
|
|
15502
16417
|
if (!isRecord(value)) return null;
|
|
15503
16418
|
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;
|
|
@@ -15515,7 +16430,7 @@ function pendingSignupFromJson(value) {
|
|
|
15515
16430
|
function pendingSignupPath(configDir) {
|
|
15516
16431
|
return join(configDir, PENDING_SIGNUP_FILE);
|
|
15517
16432
|
}
|
|
15518
|
-
function
|
|
16433
|
+
function deletePendingAgentSignup(configDir) {
|
|
15519
16434
|
rmSync(pendingSignupPath(configDir), { force: true });
|
|
15520
16435
|
}
|
|
15521
16436
|
function pendingSignupFromStart(start, apiBaseUrl1) {
|
|
@@ -15526,7 +16441,7 @@ function pendingSignupFromStart(start, apiBaseUrl1) {
|
|
|
15526
16441
|
expires_at: new Date(Date.now() + start.expires_in * 1e3).toISOString()
|
|
15527
16442
|
};
|
|
15528
16443
|
}
|
|
15529
|
-
function
|
|
16444
|
+
function savePendingAgentSignup(configDir, start, apiBaseUrl1) {
|
|
15530
16445
|
mkdirSync(configDir, {
|
|
15531
16446
|
mode: 448,
|
|
15532
16447
|
recursive: true
|
|
@@ -15545,7 +16460,7 @@ function savePendingCliSignup(configDir, start, apiBaseUrl1) {
|
|
|
15545
16460
|
throw error;
|
|
15546
16461
|
}
|
|
15547
16462
|
}
|
|
15548
|
-
function
|
|
16463
|
+
function loadPendingAgentSignup(configDir, apiBaseUrl1) {
|
|
15549
16464
|
const path = pendingSignupPath(configDir);
|
|
15550
16465
|
let contents;
|
|
15551
16466
|
try {
|
|
@@ -15561,12 +16476,12 @@ function loadPendingCliSignup(configDir, apiBaseUrl1) {
|
|
|
15561
16476
|
pending = null;
|
|
15562
16477
|
}
|
|
15563
16478
|
if (!pending) {
|
|
15564
|
-
|
|
16479
|
+
deletePendingAgentSignup(configDir);
|
|
15565
16480
|
return null;
|
|
15566
16481
|
}
|
|
15567
16482
|
if (pending.api_base_url_1 !== apiBaseUrl1) return null;
|
|
15568
16483
|
if (new Date(pending.expires_at).getTime() <= Date.now()) {
|
|
15569
|
-
|
|
16484
|
+
deletePendingAgentSignup(configDir);
|
|
15570
16485
|
return null;
|
|
15571
16486
|
}
|
|
15572
16487
|
return {
|
|
@@ -15574,6 +16489,12 @@ function loadPendingCliSignup(configDir, apiBaseUrl1) {
|
|
|
15574
16489
|
expires_in: Math.max(0, Math.ceil((new Date(pending.expires_at).getTime() - Date.now()) / 1e3))
|
|
15575
16490
|
};
|
|
15576
16491
|
}
|
|
16492
|
+
function requirePendingSignupForEmail(params) {
|
|
16493
|
+
const pending = loadPendingAgentSignup(params.configDir, params.apiBaseUrl1);
|
|
16494
|
+
if (!pending) throw cliError(`No pending signup for ${params.email}. Run \`primitive signup ${params.email}\` first.`);
|
|
16495
|
+
if (normalizeEmail(pending.email) !== normalizeEmail(params.email)) throw cliError(`Pending signup is for ${pending.email}, not ${params.email}. Run \`primitive signup ${params.email} --force\` to replace it.`);
|
|
16496
|
+
return pending;
|
|
16497
|
+
}
|
|
15577
16498
|
function retryAfterSeconds(result) {
|
|
15578
16499
|
const raw = result.response?.headers.get("retry-after");
|
|
15579
16500
|
if (!raw) return null;
|
|
@@ -15594,61 +16515,12 @@ async function promptLine(question) {
|
|
|
15594
16515
|
rl.close();
|
|
15595
16516
|
}
|
|
15596
16517
|
}
|
|
15597
|
-
async function promptHidden(question) {
|
|
15598
|
-
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.");
|
|
15599
|
-
return new Promise((resolve, reject) => {
|
|
15600
|
-
const input = process$1.stdin;
|
|
15601
|
-
let value = "";
|
|
15602
|
-
const cleanup = () => {
|
|
15603
|
-
input.setRawMode(false);
|
|
15604
|
-
input.pause();
|
|
15605
|
-
input.off("data", onData);
|
|
15606
|
-
};
|
|
15607
|
-
const finish = () => {
|
|
15608
|
-
cleanup();
|
|
15609
|
-
process$1.stderr.write("\n");
|
|
15610
|
-
resolve(value);
|
|
15611
|
-
};
|
|
15612
|
-
const onData = (chunk) => {
|
|
15613
|
-
const text = chunk.toString("utf8");
|
|
15614
|
-
for (const char of text) {
|
|
15615
|
-
if (char === "") {
|
|
15616
|
-
cleanup();
|
|
15617
|
-
process$1.stderr.write("\n");
|
|
15618
|
-
reject(cliError("Signup cancelled."));
|
|
15619
|
-
return;
|
|
15620
|
-
}
|
|
15621
|
-
if (char === "\r" || char === "\n") {
|
|
15622
|
-
finish();
|
|
15623
|
-
return;
|
|
15624
|
-
}
|
|
15625
|
-
if (char === "\b" || char === "") {
|
|
15626
|
-
value = value.slice(0, -1);
|
|
15627
|
-
continue;
|
|
15628
|
-
}
|
|
15629
|
-
value += char;
|
|
15630
|
-
}
|
|
15631
|
-
};
|
|
15632
|
-
process$1.stderr.write(question);
|
|
15633
|
-
input.setRawMode(true);
|
|
15634
|
-
input.resume();
|
|
15635
|
-
input.on("data", onData);
|
|
15636
|
-
});
|
|
15637
|
-
}
|
|
15638
16518
|
function formatSignupSeconds(seconds) {
|
|
15639
16519
|
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) return "soon";
|
|
15640
16520
|
if (seconds < 60) return `${Math.ceil(seconds)} seconds`;
|
|
15641
16521
|
const minutes = Math.ceil(seconds / 60);
|
|
15642
16522
|
return `${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
15643
16523
|
}
|
|
15644
|
-
function shouldRetrySignupPassword(errorCode) {
|
|
15645
|
-
return errorCode === CLERK_PASSWORD_REJECTED;
|
|
15646
|
-
}
|
|
15647
|
-
function signupErrorMessage(payload) {
|
|
15648
|
-
if (!isRecord(payload)) return null;
|
|
15649
|
-
const message = (isRecord(payload.error) ? payload.error : payload).message;
|
|
15650
|
-
return typeof message === "string" && message.trim() ? message : null;
|
|
15651
|
-
}
|
|
15652
16524
|
async function promptRequired(question) {
|
|
15653
16525
|
while (true) {
|
|
15654
16526
|
const value = await promptLine(question);
|
|
@@ -15656,20 +16528,6 @@ async function promptRequired(question) {
|
|
|
15656
16528
|
process$1.stderr.write("Please enter a value.\n");
|
|
15657
16529
|
}
|
|
15658
16530
|
}
|
|
15659
|
-
async function promptRequiredPassword(question) {
|
|
15660
|
-
while (true) {
|
|
15661
|
-
const value = await promptHidden(question);
|
|
15662
|
-
if (value) return value;
|
|
15663
|
-
process$1.stderr.write("Please enter a password.\n");
|
|
15664
|
-
}
|
|
15665
|
-
}
|
|
15666
|
-
async function promptNewPassword() {
|
|
15667
|
-
while (true) {
|
|
15668
|
-
const password = await promptRequiredPassword("Password: ");
|
|
15669
|
-
if (password === await promptRequiredPassword("Confirm password: ")) return password;
|
|
15670
|
-
process$1.stderr.write("Passwords did not match. Try again.\n");
|
|
15671
|
-
}
|
|
15672
|
-
}
|
|
15673
16531
|
async function confirmTerms() {
|
|
15674
16532
|
process$1.stderr.write("By creating an account, you agree to Primitive's Terms of Service and Privacy Policy:\n");
|
|
15675
16533
|
process$1.stderr.write(" https://primitive.dev/terms\n");
|
|
@@ -15677,8 +16535,100 @@ async function confirmTerms() {
|
|
|
15677
16535
|
const answer = (await promptRequired("Type 'yes' to continue: ")).toLowerCase();
|
|
15678
16536
|
if (answer !== "yes" && answer !== "y") throw cliError("You must accept the terms to create an account.");
|
|
15679
16537
|
}
|
|
16538
|
+
async function checkExistingCredentials(params) {
|
|
16539
|
+
const checkExistingLoginFn = params.deps.checkExistingLogin ?? checkExistingLogin;
|
|
16540
|
+
let existing;
|
|
16541
|
+
try {
|
|
16542
|
+
existing = loadCliCredentials(params.configDir);
|
|
16543
|
+
} catch (error) {
|
|
16544
|
+
if (!params.flags.force) throw error;
|
|
16545
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
16546
|
+
process$1.stderr.write(`Replacing unreadable Primitive CLI credentials because --force was set: ${detail}\n`);
|
|
16547
|
+
existing = null;
|
|
16548
|
+
}
|
|
16549
|
+
if (existing && params.flags.force) {
|
|
16550
|
+
process$1.stderr.write("Replacing saved Primitive CLI credentials after signup because --force was set.\n");
|
|
16551
|
+
return;
|
|
16552
|
+
}
|
|
16553
|
+
if (!existing) return;
|
|
16554
|
+
const existingStatus = await checkExistingLoginFn({
|
|
16555
|
+
apiBaseUrl1: params.apiBaseUrl1,
|
|
16556
|
+
configDir: params.configDir,
|
|
16557
|
+
credentials: existing,
|
|
16558
|
+
credentialsLockHeld: true
|
|
16559
|
+
});
|
|
16560
|
+
if (existingStatus.status === "removed_stale") {
|
|
16561
|
+
process$1.stderr.write("Continuing with Primitive signup...\n");
|
|
16562
|
+
return;
|
|
16563
|
+
}
|
|
16564
|
+
if (existingStatus.status === "blocked") {
|
|
16565
|
+
writeErrorWithHints(existingStatus.payload);
|
|
16566
|
+
throw cliError(existingStatus.message);
|
|
16567
|
+
}
|
|
16568
|
+
throw cliError(`Already logged in${existing.org_name ? ` for ${existing.org_name}` : ""}. Run \`primitive logout\` before creating a new account.`);
|
|
16569
|
+
}
|
|
16570
|
+
function saveSignupCredentials(params) {
|
|
16571
|
+
saveCliCredentials(params.configDir, {
|
|
16572
|
+
access_token: params.signup.access_token,
|
|
16573
|
+
api_base_url_1: params.apiBaseUrl1,
|
|
16574
|
+
auth_method: "oauth",
|
|
16575
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16576
|
+
expires_at: cliAccessTokenExpiresAt(params.signup.expires_in),
|
|
16577
|
+
oauth_client_id: params.signup.oauth_client_id,
|
|
16578
|
+
oauth_grant_id: params.signup.oauth_grant_id,
|
|
16579
|
+
org_id: params.signup.org_id,
|
|
16580
|
+
org_name: params.signup.org_name,
|
|
16581
|
+
refresh_token: params.signup.refresh_token,
|
|
16582
|
+
token_type: params.signup.token_type
|
|
16583
|
+
});
|
|
16584
|
+
}
|
|
16585
|
+
function writeStartInstructions(start) {
|
|
16586
|
+
process$1.stderr.write(`Sent a ${start.verification_code_length}-digit verification code to ${start.email}.\n`);
|
|
16587
|
+
process$1.stderr.write(`The code expires in ${formatSignupSeconds(start.expires_in)}.\n`);
|
|
16588
|
+
process$1.stderr.write(`Run \`primitive signup confirm ${start.email} <code>\` to finish.\n`);
|
|
16589
|
+
}
|
|
16590
|
+
async function startSignup(params) {
|
|
16591
|
+
const existingPending = loadPendingAgentSignup(params.configDir, params.apiBaseUrl1);
|
|
16592
|
+
if (existingPending && !params.flags.force) {
|
|
16593
|
+
if (normalizeEmail(existingPending.email) === normalizeEmail(params.email)) {
|
|
16594
|
+
process$1.stderr.write(`Continuing pending Primitive signup for ${existingPending.email}.\n`);
|
|
16595
|
+
process$1.stderr.write(`Run \`primitive signup confirm ${existingPending.email} <code>\` to finish, or \`primitive signup resend ${existingPending.email}\` to send a new code.\n`);
|
|
16596
|
+
return {
|
|
16597
|
+
pending: existingPending,
|
|
16598
|
+
started: false
|
|
16599
|
+
};
|
|
16600
|
+
}
|
|
16601
|
+
throw cliError(`Pending signup is for ${existingPending.email}. Run \`primitive signup ${params.email} --force\` to replace it.`);
|
|
16602
|
+
}
|
|
16603
|
+
if (params.flags.force) deletePendingAgentSignup(params.configDir);
|
|
16604
|
+
const promptRequiredFn = params.deps.promptRequired ?? promptRequired;
|
|
16605
|
+
const confirmTermsFn = params.deps.confirmTerms ?? confirmTerms;
|
|
16606
|
+
const startFn = params.deps.startAgentSignup ?? startAgentSignup;
|
|
16607
|
+
const signupCode = params.flags["signup-code"] ?? await promptRequiredFn("Signup code: ");
|
|
16608
|
+
if (!params.flags["accept-terms"]) await confirmTermsFn();
|
|
16609
|
+
const started = await startFn({
|
|
16610
|
+
body: {
|
|
16611
|
+
device_name: params.flags["device-name"] ?? hostname(),
|
|
16612
|
+
email: params.email,
|
|
16613
|
+
signup_code: signupCode,
|
|
16614
|
+
terms_accepted: true
|
|
16615
|
+
},
|
|
16616
|
+
client: params.apiClient.client,
|
|
16617
|
+
responseStyle: "fields"
|
|
16618
|
+
});
|
|
16619
|
+
if (started.error) {
|
|
16620
|
+
writeErrorWithHints(extractErrorPayload(started.error));
|
|
16621
|
+
throw cliError("Could not start Primitive agent signup.");
|
|
16622
|
+
}
|
|
16623
|
+
const startResult = unwrapData(started.data);
|
|
16624
|
+
if (!startResult) throw cliError("Primitive API returned an empty agent signup response.");
|
|
16625
|
+
return {
|
|
16626
|
+
pending: savePendingAgentSignup(params.configDir, startResult, params.apiBaseUrl1),
|
|
16627
|
+
started: true
|
|
16628
|
+
};
|
|
16629
|
+
}
|
|
15680
16630
|
async function resendVerificationCode(params) {
|
|
15681
|
-
const resent = await (params.deps.
|
|
16631
|
+
const resent = await (params.deps.resendAgentSignupVerification ?? resendAgentSignupVerification)({
|
|
15682
16632
|
body: { signup_token: params.start.signup_token },
|
|
15683
16633
|
client: params.apiClient.client,
|
|
15684
16634
|
responseStyle: "fields"
|
|
@@ -15692,167 +16642,262 @@ async function resendVerificationCode(params) {
|
|
|
15692
16642
|
signup_token: params.start.signup_token,
|
|
15693
16643
|
verification_code_length: resend.verification_code_length
|
|
15694
16644
|
} : params.start;
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
16645
|
+
return {
|
|
16646
|
+
pending: savePendingAgentSignup(params.configDir, next, params.apiBaseUrl1),
|
|
16647
|
+
resent: true
|
|
16648
|
+
};
|
|
15698
16649
|
}
|
|
15699
16650
|
const payload = extractErrorPayload(resent.error);
|
|
15700
|
-
|
|
15701
|
-
|
|
15702
|
-
|
|
15703
|
-
|
|
16651
|
+
const code = extractErrorCode(payload);
|
|
16652
|
+
if (code === SLOW_DOWN) {
|
|
16653
|
+
const retryAfter = retryAfterSeconds(resent) ?? params.start.resend_after;
|
|
16654
|
+
process$1.stderr.write(`Verification email was sent recently. Wait ${formatSignupSeconds(retryAfter)} before trying again.\n`);
|
|
16655
|
+
return {
|
|
16656
|
+
pending: params.start,
|
|
16657
|
+
resent: false
|
|
16658
|
+
};
|
|
15704
16659
|
}
|
|
16660
|
+
if (code === EXPIRED_TOKEN || code === INVALID_SIGNUP_TOKEN) deletePendingAgentSignup(params.configDir);
|
|
15705
16661
|
writeErrorWithHints(payload);
|
|
15706
|
-
throw cliError("Could not resend Primitive
|
|
16662
|
+
throw cliError("Could not resend Primitive agent signup verification email.");
|
|
15707
16663
|
}
|
|
15708
|
-
async function
|
|
16664
|
+
async function runSignupStartWithCredentialLock(params) {
|
|
15709
16665
|
const { configDir, flags } = params;
|
|
15710
16666
|
const deps = params.deps ?? {};
|
|
15711
16667
|
const promptRequiredFn = deps.promptRequired ?? promptRequired;
|
|
15712
|
-
const
|
|
15713
|
-
|
|
15714
|
-
|
|
15715
|
-
|
|
15716
|
-
|
|
16668
|
+
const email = params.email ?? await promptRequiredFn("Email: ");
|
|
16669
|
+
await checkExistingCredentials({
|
|
16670
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
16671
|
+
configDir,
|
|
16672
|
+
deps,
|
|
16673
|
+
flags
|
|
16674
|
+
});
|
|
16675
|
+
const { apiClient, requestConfig } = createCliApiClient({
|
|
16676
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
16677
|
+
configDir
|
|
16678
|
+
});
|
|
16679
|
+
const start = await startSignup({
|
|
16680
|
+
apiBaseUrl1: requestConfig.resolvedApiBaseUrl1,
|
|
16681
|
+
apiClient,
|
|
16682
|
+
configDir,
|
|
16683
|
+
deps,
|
|
16684
|
+
email,
|
|
16685
|
+
flags
|
|
16686
|
+
});
|
|
16687
|
+
if (start.started) writeStartInstructions(start.pending);
|
|
16688
|
+
}
|
|
16689
|
+
async function runSignupConfirmWithCredentialLock(params) {
|
|
16690
|
+
const { configDir, flags } = params;
|
|
16691
|
+
const deps = params.deps ?? {};
|
|
16692
|
+
if (!params.skipExistingCredentialCheck) await checkExistingCredentials({
|
|
16693
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
16694
|
+
configDir,
|
|
16695
|
+
deps,
|
|
16696
|
+
flags
|
|
16697
|
+
});
|
|
15717
16698
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
15718
16699
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
15719
16700
|
configDir
|
|
15720
16701
|
});
|
|
15721
16702
|
const apiBaseUrl1 = requestConfig.resolvedApiBaseUrl1;
|
|
15722
|
-
|
|
15723
|
-
|
|
15724
|
-
|
|
15725
|
-
|
|
15726
|
-
|
|
15727
|
-
|
|
15728
|
-
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
|
|
16703
|
+
const pending = requirePendingSignupForEmail({
|
|
16704
|
+
apiBaseUrl1,
|
|
16705
|
+
configDir,
|
|
16706
|
+
email: params.email
|
|
16707
|
+
});
|
|
16708
|
+
const verified = await (deps.verifyAgentSignup ?? verifyAgentSignup)({
|
|
16709
|
+
body: {
|
|
16710
|
+
...flags["org-id"] ? { org_id: flags["org-id"] } : {},
|
|
16711
|
+
signup_token: pending.signup_token,
|
|
16712
|
+
verification_code: params.code
|
|
16713
|
+
},
|
|
16714
|
+
client: apiClient.client,
|
|
16715
|
+
responseStyle: "fields"
|
|
16716
|
+
});
|
|
16717
|
+
if (verified.data) {
|
|
16718
|
+
const signup = unwrapData(verified.data);
|
|
16719
|
+
if (!signup) throw cliError("Primitive API returned an empty agent signup verification response.");
|
|
16720
|
+
saveSignupCredentials({
|
|
16721
|
+
apiBaseUrl1,
|
|
15735
16722
|
configDir,
|
|
15736
|
-
|
|
15737
|
-
});
|
|
15738
|
-
if (existingStatus.status === "removed_stale") process$1.stderr.write("Continuing with Primitive CLI signup...\n");
|
|
15739
|
-
else if (existingStatus.status === "blocked") {
|
|
15740
|
-
writeErrorWithHints(existingStatus.payload);
|
|
15741
|
-
throw cliError(existingStatus.message);
|
|
15742
|
-
} else throw cliError(`Already logged in${existing.org_name ? ` for ${existing.org_name}` : ""}. Run \`primitive logout\` before creating a new account.`);
|
|
15743
|
-
}
|
|
15744
|
-
if (flags.force) deletePendingCliSignup(configDir);
|
|
15745
|
-
let start = flags.force ? null : loadPendingCliSignup(configDir, apiBaseUrl1);
|
|
15746
|
-
const resumed = Boolean(start);
|
|
15747
|
-
if (start) process$1.stderr.write(`Continuing pending Primitive CLI signup for ${start.email}.\n`);
|
|
15748
|
-
else {
|
|
15749
|
-
const signupCode = await promptRequiredFn("Signup code: ");
|
|
15750
|
-
await confirmTermsFn();
|
|
15751
|
-
const email = await promptRequiredFn("Email: ");
|
|
15752
|
-
const started = await startFn({
|
|
15753
|
-
body: {
|
|
15754
|
-
device_name: flags["device-name"] ?? hostname(),
|
|
15755
|
-
email,
|
|
15756
|
-
signup_code: signupCode,
|
|
15757
|
-
terms_accepted: true
|
|
15758
|
-
},
|
|
15759
|
-
client: apiClient.client,
|
|
15760
|
-
responseStyle: "fields"
|
|
16723
|
+
signup
|
|
15761
16724
|
});
|
|
15762
|
-
|
|
15763
|
-
|
|
15764
|
-
|
|
15765
|
-
}
|
|
15766
|
-
|
|
15767
|
-
if (!startResult) throw cliError("Primitive API returned an empty CLI signup response.");
|
|
15768
|
-
start = savePendingCliSignup(configDir, startResult, apiBaseUrl1);
|
|
16725
|
+
deletePendingAgentSignup(configDir);
|
|
16726
|
+
const org = signup.org_name ? ` (${signup.org_name})` : "";
|
|
16727
|
+
process$1.stderr.write(`Logged in to org ${signup.org_id}${org}.\n`);
|
|
16728
|
+
process$1.stderr.write(`Saved credentials to ${credentialsPath(configDir)}.\n`);
|
|
16729
|
+
return;
|
|
15769
16730
|
}
|
|
15770
|
-
|
|
15771
|
-
|
|
16731
|
+
const payload = extractErrorPayload(verified.error);
|
|
16732
|
+
const code = extractErrorCode(payload);
|
|
16733
|
+
if (code === INVALID_VERIFICATION_CODE) throw cliError("Invalid verification code. Try again or run signup resend.");
|
|
16734
|
+
if (code === EXPIRED_TOKEN || code === INVALID_SIGNUP_TOKEN) deletePendingAgentSignup(configDir);
|
|
16735
|
+
writeErrorWithHints(payload);
|
|
16736
|
+
throw cliError("Primitive agent signup failed while verifying the account.");
|
|
16737
|
+
}
|
|
16738
|
+
async function runSignupResendWithCredentialLock(params) {
|
|
16739
|
+
const deps = params.deps ?? {};
|
|
16740
|
+
const { apiClient, requestConfig } = createCliApiClient({
|
|
16741
|
+
apiBaseUrl1: params.flags["api-base-url-1"],
|
|
16742
|
+
configDir: params.configDir
|
|
16743
|
+
});
|
|
16744
|
+
const pending = requirePendingSignupForEmail({
|
|
16745
|
+
apiBaseUrl1: requestConfig.resolvedApiBaseUrl1,
|
|
16746
|
+
configDir: params.configDir,
|
|
16747
|
+
email: params.email
|
|
16748
|
+
});
|
|
16749
|
+
const resend = await resendVerificationCode({
|
|
16750
|
+
apiBaseUrl1: requestConfig.resolvedApiBaseUrl1,
|
|
16751
|
+
apiClient,
|
|
16752
|
+
configDir: params.configDir,
|
|
16753
|
+
deps,
|
|
16754
|
+
start: pending
|
|
16755
|
+
});
|
|
16756
|
+
if (resend.resent) process$1.stderr.write(`Sent a new ${resend.pending.verification_code_length}-digit verification code to ${resend.pending.email}. It expires in ${formatSignupSeconds(resend.pending.expires_in)}.\n`);
|
|
16757
|
+
}
|
|
16758
|
+
async function runSignupInteractiveWithCredentialLock(params) {
|
|
16759
|
+
const { configDir, flags } = params;
|
|
16760
|
+
const deps = params.deps ?? {};
|
|
16761
|
+
const promptRequiredFn = deps.promptRequired ?? promptRequired;
|
|
16762
|
+
await checkExistingCredentials({
|
|
16763
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
16764
|
+
configDir,
|
|
16765
|
+
deps,
|
|
16766
|
+
flags
|
|
16767
|
+
});
|
|
16768
|
+
const { apiClient, requestConfig } = createCliApiClient({
|
|
16769
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
16770
|
+
configDir
|
|
16771
|
+
});
|
|
16772
|
+
const apiBaseUrl1 = requestConfig.resolvedApiBaseUrl1;
|
|
16773
|
+
let start = flags.force ? null : loadPendingAgentSignup(configDir, apiBaseUrl1);
|
|
16774
|
+
if (start) process$1.stderr.write(`Continuing pending Primitive signup for ${start.email}.\n`);
|
|
16775
|
+
else start = (await startSignup({
|
|
16776
|
+
apiBaseUrl1,
|
|
16777
|
+
apiClient,
|
|
16778
|
+
configDir,
|
|
16779
|
+
deps,
|
|
16780
|
+
email: await promptRequiredFn("Email: "),
|
|
16781
|
+
flags
|
|
16782
|
+
})).pending;
|
|
16783
|
+
process$1.stderr.write(`Check your email for the ${start.verification_code_length}-digit verification code sent to ${start.email}.\n`);
|
|
15772
16784
|
process$1.stderr.write(`The code expires in ${formatSignupSeconds(start.expires_in)}.\n`);
|
|
15773
16785
|
process$1.stderr.write(`Enter the code from the email, or type \`resend\` to send a new code after ${formatSignupSeconds(start.resend_after)}.\n`);
|
|
15774
16786
|
while (true) {
|
|
15775
16787
|
const verificationCode = await promptRequiredFn(`Verification code (${start.verification_code_length} digits): `);
|
|
15776
16788
|
if (verificationCode.toLowerCase() === "resend") {
|
|
15777
|
-
|
|
16789
|
+
const resend = await resendVerificationCode({
|
|
15778
16790
|
apiBaseUrl1,
|
|
15779
16791
|
apiClient,
|
|
15780
16792
|
configDir,
|
|
15781
16793
|
deps,
|
|
15782
16794
|
start
|
|
15783
16795
|
});
|
|
16796
|
+
start = resend.pending;
|
|
16797
|
+
if (resend.resent) process$1.stderr.write(`Sent a new ${start.verification_code_length}-digit verification code. It expires in ${formatSignupSeconds(start.expires_in)}.\n`);
|
|
15784
16798
|
continue;
|
|
15785
16799
|
}
|
|
15786
|
-
|
|
15787
|
-
|
|
15788
|
-
|
|
15789
|
-
|
|
15790
|
-
|
|
15791
|
-
|
|
15792
|
-
|
|
16800
|
+
try {
|
|
16801
|
+
await runSignupConfirmWithCredentialLock({
|
|
16802
|
+
code: verificationCode,
|
|
16803
|
+
configDir,
|
|
16804
|
+
deps,
|
|
16805
|
+
email: start.email,
|
|
16806
|
+
flags: {
|
|
16807
|
+
"api-base-url-1": flags["api-base-url-1"],
|
|
16808
|
+
force: true
|
|
15793
16809
|
},
|
|
15794
|
-
|
|
15795
|
-
responseStyle: "fields"
|
|
16810
|
+
skipExistingCredentialCheck: true
|
|
15796
16811
|
});
|
|
15797
|
-
|
|
15798
|
-
|
|
15799
|
-
|
|
15800
|
-
saveCliCredentials(configDir, {
|
|
15801
|
-
api_key: signup.api_key,
|
|
15802
|
-
api_base_url_1: apiBaseUrl1,
|
|
15803
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15804
|
-
key_id: signup.key_id,
|
|
15805
|
-
key_prefix: signup.key_prefix,
|
|
15806
|
-
org_id: signup.org_id,
|
|
15807
|
-
org_name: signup.org_name
|
|
15808
|
-
});
|
|
15809
|
-
deletePendingCliSignup(configDir);
|
|
15810
|
-
const org = signup.org_name ? ` (${signup.org_name})` : "";
|
|
15811
|
-
process$1.stderr.write(`Created account and logged in to org ${signup.org_id}${org}.\n`);
|
|
15812
|
-
process$1.stderr.write(`Saved credentials to ${credentialsPath(configDir)}.\n`);
|
|
15813
|
-
return;
|
|
15814
|
-
}
|
|
15815
|
-
const payload = extractErrorPayload(verified.error);
|
|
15816
|
-
const code = extractErrorCode(payload);
|
|
15817
|
-
if (code === INVALID_VERIFICATION_CODE) {
|
|
16812
|
+
return;
|
|
16813
|
+
} catch (error) {
|
|
16814
|
+
if (error instanceof Errors.CLIError && error.message.startsWith("Invalid verification code.")) {
|
|
15818
16815
|
process$1.stderr.write("Invalid verification code. Try again or type `resend`.\n");
|
|
15819
|
-
break;
|
|
15820
|
-
}
|
|
15821
|
-
if (shouldRetrySignupPassword(code)) {
|
|
15822
|
-
const message = signupErrorMessage(payload);
|
|
15823
|
-
if (message) process$1.stderr.write(`Password rejected: ${message}\n`);
|
|
15824
|
-
process$1.stderr.write("Choose a different password and try again.\n");
|
|
15825
|
-
password = await promptNewPasswordFn();
|
|
15826
16816
|
continue;
|
|
15827
16817
|
}
|
|
15828
|
-
|
|
15829
|
-
writeErrorWithHints(payload);
|
|
15830
|
-
throw cliError("Primitive CLI signup failed while verifying the account.");
|
|
16818
|
+
throw error;
|
|
15831
16819
|
}
|
|
15832
16820
|
}
|
|
15833
16821
|
}
|
|
16822
|
+
function commonStartFlags() {
|
|
16823
|
+
return {
|
|
16824
|
+
"accept-terms": Flags.boolean({ description: "Confirm acceptance of Primitive's Terms of Service and Privacy Policy" }),
|
|
16825
|
+
"api-base-url-1": Flags.string({
|
|
16826
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
16827
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
16828
|
+
hidden: true
|
|
16829
|
+
}),
|
|
16830
|
+
"device-name": Flags.string({ description: "Device name used for the created CLI OAuth session" }),
|
|
16831
|
+
force: Flags.boolean({
|
|
16832
|
+
char: "f",
|
|
16833
|
+
description: "Replace saved credentials or pending signup state when needed"
|
|
16834
|
+
}),
|
|
16835
|
+
"signup-code": Flags.string({
|
|
16836
|
+
description: "Signup code required to create an account",
|
|
16837
|
+
env: "PRIMITIVE_SIGNUP_CODE"
|
|
16838
|
+
})
|
|
16839
|
+
};
|
|
16840
|
+
}
|
|
15834
16841
|
var SignupCommand = class SignupCommand extends Command {
|
|
15835
|
-
static
|
|
15836
|
-
|
|
16842
|
+
static args = { email: Args.string({
|
|
16843
|
+
description: "Email address to sign up",
|
|
16844
|
+
required: false
|
|
16845
|
+
}) };
|
|
16846
|
+
static description = "Start a Primitive account signup, send an email verification code, and save a pending signup token locally.";
|
|
16847
|
+
static summary = "Start account signup";
|
|
15837
16848
|
static examples = [
|
|
15838
|
-
"<%= config.bin %> signup",
|
|
15839
|
-
"<%= config.bin %> signup --
|
|
15840
|
-
"<%= config.bin %> signup
|
|
16849
|
+
"<%= config.bin %> signup user@example.com",
|
|
16850
|
+
"<%= config.bin %> signup user@example.com --signup-code invite-code --accept-terms",
|
|
16851
|
+
"<%= config.bin %> signup confirm user@example.com 123456"
|
|
15841
16852
|
];
|
|
16853
|
+
static flags = commonStartFlags();
|
|
16854
|
+
async run() {
|
|
16855
|
+
const { args, flags } = await this.parse(SignupCommand);
|
|
16856
|
+
let releaseCredentialsLock;
|
|
16857
|
+
try {
|
|
16858
|
+
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
16859
|
+
} catch (error) {
|
|
16860
|
+
throw cliError(error instanceof Error ? error.message : String(error));
|
|
16861
|
+
}
|
|
16862
|
+
try {
|
|
16863
|
+
await runSignupStartWithCredentialLock({
|
|
16864
|
+
configDir: this.config.configDir,
|
|
16865
|
+
email: args.email,
|
|
16866
|
+
flags
|
|
16867
|
+
});
|
|
16868
|
+
} finally {
|
|
16869
|
+
releaseCredentialsLock();
|
|
16870
|
+
}
|
|
16871
|
+
}
|
|
16872
|
+
};
|
|
16873
|
+
var SignupConfirmCommand = class SignupConfirmCommand extends Command {
|
|
16874
|
+
static args = {
|
|
16875
|
+
email: Args.string({
|
|
16876
|
+
description: "Email address used to start signup",
|
|
16877
|
+
required: true
|
|
16878
|
+
}),
|
|
16879
|
+
code: Args.string({
|
|
16880
|
+
description: "Verification code from the signup email",
|
|
16881
|
+
required: true
|
|
16882
|
+
})
|
|
16883
|
+
};
|
|
16884
|
+
static description = "Confirm a pending Primitive signup, create an OAuth session, and save CLI credentials locally.";
|
|
16885
|
+
static summary = "Confirm account signup";
|
|
16886
|
+
static examples = ["<%= config.bin %> signup confirm user@example.com 123456", "<%= config.bin %> signup confirm user@example.com 123456 --org-id 00000000-0000-4000-8000-000000000000"];
|
|
15842
16887
|
static flags = {
|
|
15843
16888
|
"api-base-url-1": Flags.string({
|
|
15844
16889
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15845
16890
|
env: "PRIMITIVE_API_BASE_URL_1",
|
|
15846
16891
|
hidden: true
|
|
15847
16892
|
}),
|
|
15848
|
-
"device-name": Flags.string({ description: "Device name used for the created CLI API key" }),
|
|
15849
16893
|
force: Flags.boolean({
|
|
15850
16894
|
char: "f",
|
|
15851
|
-
description: "Replace saved credentials
|
|
15852
|
-
})
|
|
16895
|
+
description: "Replace saved credentials after verification"
|
|
16896
|
+
}),
|
|
16897
|
+
"org-id": Flags.string({ description: "Workspace id to target when the email belongs to multiple workspaces" })
|
|
15853
16898
|
};
|
|
15854
16899
|
async run() {
|
|
15855
|
-
const { flags } = await this.parse(
|
|
16900
|
+
const { args, flags } = await this.parse(SignupConfirmCommand);
|
|
15856
16901
|
let releaseCredentialsLock;
|
|
15857
16902
|
try {
|
|
15858
16903
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
@@ -15860,27 +16905,81 @@ var SignupCommand = class SignupCommand extends Command {
|
|
|
15860
16905
|
throw cliError(error instanceof Error ? error.message : String(error));
|
|
15861
16906
|
}
|
|
15862
16907
|
try {
|
|
15863
|
-
await
|
|
16908
|
+
await runSignupConfirmWithCredentialLock({
|
|
16909
|
+
code: args.code,
|
|
16910
|
+
configDir: this.config.configDir,
|
|
16911
|
+
email: args.email,
|
|
16912
|
+
flags
|
|
16913
|
+
});
|
|
15864
16914
|
} finally {
|
|
15865
16915
|
releaseCredentialsLock();
|
|
15866
16916
|
}
|
|
15867
16917
|
}
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
16918
|
+
};
|
|
16919
|
+
var SignupResendCommand = class SignupResendCommand extends Command {
|
|
16920
|
+
static args = { email: Args.string({
|
|
16921
|
+
description: "Email address used to start signup",
|
|
16922
|
+
required: true
|
|
16923
|
+
}) };
|
|
16924
|
+
static description = "Resend the verification code for a pending signup.";
|
|
16925
|
+
static summary = "Resend signup verification code";
|
|
16926
|
+
static examples = ["<%= config.bin %> signup resend user@example.com"];
|
|
16927
|
+
static flags = { "api-base-url-1": Flags.string({
|
|
16928
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
16929
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
16930
|
+
hidden: true
|
|
16931
|
+
}) };
|
|
16932
|
+
async run() {
|
|
16933
|
+
const { args, flags } = await this.parse(SignupResendCommand);
|
|
16934
|
+
let releaseCredentialsLock;
|
|
16935
|
+
try {
|
|
16936
|
+
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
16937
|
+
} catch (error) {
|
|
16938
|
+
throw cliError(error instanceof Error ? error.message : String(error));
|
|
16939
|
+
}
|
|
16940
|
+
try {
|
|
16941
|
+
await runSignupResendWithCredentialLock({
|
|
16942
|
+
configDir: this.config.configDir,
|
|
16943
|
+
email: args.email,
|
|
16944
|
+
flags
|
|
16945
|
+
});
|
|
16946
|
+
} finally {
|
|
16947
|
+
releaseCredentialsLock();
|
|
16948
|
+
}
|
|
16949
|
+
}
|
|
16950
|
+
};
|
|
16951
|
+
var SignupInteractiveCommand = class SignupInteractiveCommand extends Command {
|
|
16952
|
+
static description = "Run the full signup flow in one interactive terminal session.";
|
|
16953
|
+
static summary = "Run interactive account signup";
|
|
16954
|
+
static examples = ["<%= config.bin %> signup interactive"];
|
|
16955
|
+
static flags = commonStartFlags();
|
|
16956
|
+
async run() {
|
|
16957
|
+
const { flags } = await this.parse(SignupInteractiveCommand);
|
|
16958
|
+
let releaseCredentialsLock;
|
|
16959
|
+
try {
|
|
16960
|
+
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
16961
|
+
} catch (error) {
|
|
16962
|
+
throw cliError(error instanceof Error ? error.message : String(error));
|
|
16963
|
+
}
|
|
16964
|
+
try {
|
|
16965
|
+
await runSignupInteractiveWithCredentialLock({
|
|
16966
|
+
configDir: this.config.configDir,
|
|
16967
|
+
flags
|
|
16968
|
+
});
|
|
16969
|
+
} finally {
|
|
16970
|
+
releaseCredentialsLock();
|
|
16971
|
+
}
|
|
15873
16972
|
}
|
|
15874
16973
|
};
|
|
15875
16974
|
//#endregion
|
|
15876
16975
|
//#region src/oclif/commands/whoami.ts
|
|
15877
16976
|
var WhoamiCommand = class WhoamiCommand extends Command {
|
|
15878
|
-
static description = `Print the account currently authenticated by
|
|
16977
|
+
static description = `Print the account currently authenticated by saved OAuth credentials or an explicit API key. Useful as a credentials smoke test: confirms auth is live and shows which account it belongs to.`;
|
|
15879
16978
|
static summary = "Print the authenticated account (credentials smoke test)";
|
|
15880
16979
|
static examples = ["<%= config.bin %> whoami", "<%= config.bin %> whoami --api-key prim_..."];
|
|
15881
16980
|
static flags = {
|
|
15882
16981
|
"api-key": Flags.string({
|
|
15883
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
16982
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15884
16983
|
env: "PRIMITIVE_API_KEY"
|
|
15885
16984
|
}),
|
|
15886
16985
|
"api-base-url-1": Flags.string({
|
|
@@ -15898,7 +16997,7 @@ var WhoamiCommand = class WhoamiCommand extends Command {
|
|
|
15898
16997
|
async run() {
|
|
15899
16998
|
const { flags } = await this.parse(WhoamiCommand);
|
|
15900
16999
|
await runWithTiming(flags.time, async () => {
|
|
15901
|
-
const { apiClient, auth, baseUrlOverridden } = createAuthenticatedCliApiClient({
|
|
17000
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
15902
17001
|
apiKey: flags["api-key"],
|
|
15903
17002
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
15904
17003
|
apiBaseUrl2: flags["api-base-url-2"],
|
|
@@ -15992,7 +17091,7 @@ function renderFishCompletion(binName) {
|
|
|
15992
17091
|
for (const operation of topicOperations) {
|
|
15993
17092
|
lines.push(`complete -c ${binName} -f -n '__fish_${binName}_topic_needs_subcommand ${fishEscape(topic)}' -a '${fishEscape(operation.command)}' -d '${fishEscape(operation.summary ?? `${operation.method} ${operation.path}`)}'`);
|
|
15994
17093
|
for (const parameter of [...operation.pathParams, ...operation.queryParams]) lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l '${fishEscape(parameter.name.replace(/_/g, "-"))}' -r -d '${fishEscape(parameter.description ?? parameter.name)}'`);
|
|
15995
|
-
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'api-key' -r -d 'Primitive API key (defaults to PRIMITIVE_API_KEY or saved
|
|
17094
|
+
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'api-key' -r -d 'Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)'`);
|
|
15996
17095
|
if (!operation.binaryResponse) lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'envelope' -d 'Print the full response envelope, including pagination metadata'`);
|
|
15997
17096
|
if (operation.hasJsonBody) lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'body' -r -d 'JSON request body'`, `complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'body-file' -r -d 'Path to a JSON file used as the request body'`);
|
|
15998
17097
|
if (operation.binaryResponse) lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'output' -r -d 'Write binary response bytes to a file'`);
|
|
@@ -16145,6 +17244,9 @@ const COMMANDS = {
|
|
|
16145
17244
|
chat: ChatCommand,
|
|
16146
17245
|
login: LoginCommand,
|
|
16147
17246
|
signup: SignupCommand,
|
|
17247
|
+
"signup:confirm": SignupConfirmCommand,
|
|
17248
|
+
"signup:interactive": SignupInteractiveCommand,
|
|
17249
|
+
"signup:resend": SignupResendCommand,
|
|
16148
17250
|
logout: LogoutCommand,
|
|
16149
17251
|
whoami: WhoamiCommand,
|
|
16150
17252
|
doctor: DoctorCommand,
|