@primitivedotdev/cli 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{cli-config-SktG2dzR.js → cli-config-D7wN_PBc.js} +59 -49
- package/dist/oclif/index.js +1322 -374
- package/dist/oclif/root-signup-hint.js +12 -11
- package/man/primitive.1 +2 -5
- package/package.json +1 -1
package/dist/oclif/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as createClient, C as saveCliCredentials, D as loadChatConversationByLocalId, E as loadActiveChatState, O as saveActiveChatState, S as resolveCliAuth, T as deleteChatState, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as
|
|
1
|
+
import { A as createClient, C as saveCliCredentials, D as loadChatConversationByLocalId, E as loadActiveChatState, O as saveActiveChatState, S as resolveCliAuth, T as deleteChatState, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as loadCliCredentials, c as resolveConfigEnvironment, d as validateCliHeaderName, f as validateCliHeaderValue, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as createConfig, k as PrimitiveApiClient, l as saveCliConfig, m as cliAccessTokenExpiresAt, n as deleteCliConfig, o as redactCliEnvironment, p as acquireCliCredentialsLock, r as emptyCliConfig, s as removeCliEnvironment, u as upsertCliEnvironment, v as deleteCliCredentialsLock, w as chatStatePath, x as normalizeApiBaseUrl, y as detectPrimitiveKeyEnvMisname } from "../cli-config-D7wN_PBc.js";
|
|
2
2
|
import { Args, Command, Errors, Flags, ux } from "@oclif/core";
|
|
3
3
|
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -20,7 +20,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
20
20
|
};
|
|
21
21
|
//#endregion
|
|
22
22
|
//#region ../packages/api-core/src/api/client.gen.ts
|
|
23
|
-
const client = createClient(createConfig({ baseUrl: "https://
|
|
23
|
+
const client = createClient(createConfig({ baseUrl: "https://api.primitive.dev/v1" }));
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region ../packages/api-core/src/api/sdk.gen.ts
|
|
26
26
|
var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -44,8 +44,10 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
44
44
|
getConversation: () => getConversation,
|
|
45
45
|
getEmail: () => getEmail,
|
|
46
46
|
getFunction: () => getFunction,
|
|
47
|
+
getFunctionRouting: () => getFunctionRouting,
|
|
47
48
|
getFunctionTestRunTrace: () => getFunctionTestRunTrace,
|
|
48
49
|
getInboxStatus: () => getInboxStatus,
|
|
50
|
+
getOrgRoutingTopology: () => getOrgRoutingTopology,
|
|
49
51
|
getSendPermissions: () => getSendPermissions,
|
|
50
52
|
getSentEmail: () => getSentEmail,
|
|
51
53
|
getStorageStats: () => getStorageStats,
|
|
@@ -70,12 +72,14 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
70
72
|
searchEmails: () => searchEmails,
|
|
71
73
|
semanticSearch: () => semanticSearch,
|
|
72
74
|
sendEmail: () => sendEmail,
|
|
75
|
+
setFunctionRoute: () => setFunctionRoute,
|
|
73
76
|
setFunctionSecret: () => setFunctionSecret,
|
|
74
77
|
startAgentSignup: () => startAgentSignup,
|
|
75
78
|
startCliLogin: () => startCliLogin,
|
|
76
79
|
startCliSignup: () => startCliSignup,
|
|
77
80
|
testEndpoint: () => testEndpoint,
|
|
78
81
|
testFunction: () => testFunction,
|
|
82
|
+
unsetFunctionRoute: () => unsetFunctionRoute,
|
|
79
83
|
updateAccount: () => updateAccount,
|
|
80
84
|
updateDomain: () => updateDomain,
|
|
81
85
|
updateEndpoint: () => updateEndpoint,
|
|
@@ -934,14 +938,13 @@ const getSendPermissions = (options) => (options?.client ?? client).get({
|
|
|
934
938
|
* the request returns once the relay accepts the message for delivery.
|
|
935
939
|
* Set `wait: true` to wait for the first downstream SMTP delivery outcome.
|
|
936
940
|
*
|
|
937
|
-
* **Host routing.** /send-mail is served by the
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
*
|
|
941
|
-
*
|
|
942
|
-
*
|
|
943
|
-
*
|
|
944
|
-
* route /send-mail to the attachments host automatically.
|
|
941
|
+
* **Host routing.** /send-mail is served by the canonical API host
|
|
942
|
+
* (`https://api.primitive.dev/v1`) so the request body can carry
|
|
943
|
+
* inline attachments up to ~30 MiB raw. The legacy dashboard
|
|
944
|
+
* compatibility host (`https://www.primitive.dev/api/v1`) also accepts
|
|
945
|
+
* /send-mail, but Vercel request body limits apply before proxying.
|
|
946
|
+
* The typed SDKs route /send-mail to the canonical API host
|
|
947
|
+
* automatically.
|
|
945
948
|
*
|
|
946
949
|
*/
|
|
947
950
|
const sendEmail = (options) => (options.client ?? client).post({
|
|
@@ -1103,11 +1106,13 @@ const listFunctions = (options) => (options?.client ?? client).get({
|
|
|
1103
1106
|
* attempt, and sent to the runtime so stack traces can resolve to
|
|
1104
1107
|
* original source files.
|
|
1105
1108
|
*
|
|
1106
|
-
* **
|
|
1107
|
-
*
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1109
|
+
* **Routing.** On successful deploy, the function code is live
|
|
1110
|
+
* in the runtime, but inbound mail will not reach it until at
|
|
1111
|
+
* least one route is bound. Routes are managed from the Primitive
|
|
1112
|
+
* dashboard. A `deploy_status` of `deployed` means the script is
|
|
1113
|
+
* installed, not that the function is receiving mail. The
|
|
1114
|
+
* internal runtime URL is not returned by the API and is not a
|
|
1115
|
+
* customer-facing integration surface.
|
|
1111
1116
|
*
|
|
1112
1117
|
* **Secrets.** New functions ship with the managed secrets
|
|
1113
1118
|
* (`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`,
|
|
@@ -1132,7 +1137,7 @@ const createFunction = (options) => (options.client ?? client).post({
|
|
|
1132
1137
|
* Delete a function
|
|
1133
1138
|
*
|
|
1134
1139
|
* Soft-deletes the function row, removes the script from the edge
|
|
1135
|
-
* runtime, and deactivates
|
|
1140
|
+
* runtime, and deactivates any route bound to this function so no
|
|
1136
1141
|
* further inbound mail is delivered. Past deploy history,
|
|
1137
1142
|
* invocations, and logs are retained.
|
|
1138
1143
|
*
|
|
@@ -1249,6 +1254,80 @@ const getFunctionTestRunTrace = (options) => (options.client ?? client).get({
|
|
|
1249
1254
|
...options
|
|
1250
1255
|
});
|
|
1251
1256
|
/**
|
|
1257
|
+
* Get the org's function routing topology
|
|
1258
|
+
*
|
|
1259
|
+
* Returns a single snapshot of how inbound mail is routed across
|
|
1260
|
+
* this org's active domains and functions: which active domain has
|
|
1261
|
+
* which function bound, the org's fallback function (if any), and
|
|
1262
|
+
* every deployed function with no route bound. Use this to answer
|
|
1263
|
+
* "which of my functions actually receive mail?" diagnostically.
|
|
1264
|
+
*
|
|
1265
|
+
*/
|
|
1266
|
+
const getOrgRoutingTopology = (options) => (options?.client ?? client).get({
|
|
1267
|
+
security: [{
|
|
1268
|
+
scheme: "bearer",
|
|
1269
|
+
type: "http"
|
|
1270
|
+
}],
|
|
1271
|
+
url: "/functions/routing-topology",
|
|
1272
|
+
...options
|
|
1273
|
+
});
|
|
1274
|
+
/**
|
|
1275
|
+
* Get a function's current route binding
|
|
1276
|
+
*
|
|
1277
|
+
* Returns the endpoint binding for the function, or null when no
|
|
1278
|
+
* route is currently bound. The binding identifies whether the
|
|
1279
|
+
* function receives mail for a specific domain (scoped) or for any
|
|
1280
|
+
* active domain that has no scoped binding (fallback).
|
|
1281
|
+
*
|
|
1282
|
+
*/
|
|
1283
|
+
const getFunctionRouting = (options) => (options.client ?? client).get({
|
|
1284
|
+
security: [{
|
|
1285
|
+
scheme: "bearer",
|
|
1286
|
+
type: "http"
|
|
1287
|
+
}],
|
|
1288
|
+
url: "/functions/{id}/routing",
|
|
1289
|
+
...options
|
|
1290
|
+
});
|
|
1291
|
+
/**
|
|
1292
|
+
* Unbind any route from a function
|
|
1293
|
+
*
|
|
1294
|
+
* Deactivates every active endpoint bound to this function. The
|
|
1295
|
+
* function stays deployed but stops receiving inbound mail. Safe
|
|
1296
|
+
* to call when no route is currently bound (no-op).
|
|
1297
|
+
*
|
|
1298
|
+
*/
|
|
1299
|
+
const unsetFunctionRoute = (options) => (options.client ?? client).delete({
|
|
1300
|
+
security: [{
|
|
1301
|
+
scheme: "bearer",
|
|
1302
|
+
type: "http"
|
|
1303
|
+
}],
|
|
1304
|
+
url: "/functions/{id}/route",
|
|
1305
|
+
...options
|
|
1306
|
+
});
|
|
1307
|
+
/**
|
|
1308
|
+
* Bind a route to a function
|
|
1309
|
+
*
|
|
1310
|
+
* Binds inbound mail to this function. The route target is either
|
|
1311
|
+
* a specific verified domain (scoped) or the org's fallback (any
|
|
1312
|
+
* active domain with no scoped binding). If another function is
|
|
1313
|
+
* already bound at the target, returns a `conflict` envelope
|
|
1314
|
+
* describing the holder; re-issue with `takeover: true` to
|
|
1315
|
+
* deactivate that prior binding and install this one.
|
|
1316
|
+
*
|
|
1317
|
+
*/
|
|
1318
|
+
const setFunctionRoute = (options) => (options.client ?? client).put({
|
|
1319
|
+
security: [{
|
|
1320
|
+
scheme: "bearer",
|
|
1321
|
+
type: "http"
|
|
1322
|
+
}],
|
|
1323
|
+
url: "/functions/{id}/route",
|
|
1324
|
+
...options,
|
|
1325
|
+
headers: {
|
|
1326
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
1327
|
+
...options.headers
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1330
|
+
/**
|
|
1252
1331
|
* List a function's secrets
|
|
1253
1332
|
*
|
|
1254
1333
|
* Returns metadata for every secret bound to the function, with
|
|
@@ -1383,11 +1462,11 @@ const openapiDocument = {
|
|
|
1383
1462
|
}
|
|
1384
1463
|
},
|
|
1385
1464
|
"servers": [{
|
|
1386
|
-
"url": "https://www.primitive.dev/api/v1",
|
|
1387
|
-
"description": "Primary API host (PRIMITIVE_API_BASE_URL_1). Carries every operation\nexcept attachment-supporting send. Vercel-backed; request body is\ncapped at 4.5 MB by the platform.\n"
|
|
1388
|
-
}, {
|
|
1389
1465
|
"url": "https://api.primitive.dev/v1",
|
|
1390
|
-
"description": "
|
|
1466
|
+
"description": "Canonical API host (PRIMITIVE_API_BASE_URL). Carries every public\nAPI operation. Cloudflare Workers-backed; attachment-capable send\noperations can carry up to ~30 MiB raw request bodies before base64\nencoding.\n"
|
|
1467
|
+
}, {
|
|
1468
|
+
"url": "https://www.primitive.dev/api/v1",
|
|
1469
|
+
"description": "Legacy dashboard compatibility host. Requests are forwarded to the\ncanonical API host, but Vercel request body limits still apply before\nproxying. New integrations should use https://api.primitive.dev/v1.\n"
|
|
1391
1470
|
}],
|
|
1392
1471
|
"security": [{ "BearerAuth": [] }],
|
|
1393
1472
|
"tags": [
|
|
@@ -2410,10 +2489,10 @@ const openapiDocument = {
|
|
|
2410
2489
|
"description": "Sends an outbound reply to the inbound email identified by `id`.\nThreading headers (`In-Reply-To`, `References`), recipient\nderivation (Reply-To, then From, then bare sender), and the\n`Re:` subject prefix are all derived server-side from the\nstored inbound row. The request body carries only the message\nbody, optional From override, optional attachments, and optional\n`wait` flag; passing any header or recipient override is\nrejected by the schema (`additionalProperties: false`).\n\nForwards through the same gates as `/send-mail`: the response\nstatus, error envelope, and `idempotent_replay` flag mirror\nthe send-mail contract verbatim.\n",
|
|
2411
2490
|
"servers": [{
|
|
2412
2491
|
"url": "https://api.primitive.dev/v1",
|
|
2413
|
-
"description": "
|
|
2492
|
+
"description": "Canonical API host (recommended)"
|
|
2414
2493
|
}, {
|
|
2415
2494
|
"url": "https://www.primitive.dev/api/v1",
|
|
2416
|
-
"description": "
|
|
2495
|
+
"description": "Legacy compatibility host (Vercel body limit applies)"
|
|
2417
2496
|
}],
|
|
2418
2497
|
"tags": ["Sending"],
|
|
2419
2498
|
"requestBody": {
|
|
@@ -2822,13 +2901,13 @@ const openapiDocument = {
|
|
|
2822
2901
|
"/send-mail": { "post": {
|
|
2823
2902
|
"operationId": "sendEmail",
|
|
2824
2903
|
"summary": "Send outbound email",
|
|
2825
|
-
"description": "Sends an outbound email through Primitive's outbound relay. By default\nthe request returns once the relay accepts the message for delivery.\nSet `wait: true` to wait for the first downstream SMTP delivery outcome.\n\n**Host routing.** /send-mail is served by the
|
|
2904
|
+
"description": "Sends an outbound email through Primitive's outbound relay. By default\nthe request returns once the relay accepts the message for delivery.\nSet `wait: true` to wait for the first downstream SMTP delivery outcome.\n\n**Host routing.** /send-mail is served by the canonical API host\n(`https://api.primitive.dev/v1`) so the request body can carry\ninline attachments up to ~30 MiB raw. The legacy dashboard\ncompatibility host (`https://www.primitive.dev/api/v1`) also accepts\n/send-mail, but Vercel request body limits apply before proxying.\nThe typed SDKs route /send-mail to the canonical API host\nautomatically.\n",
|
|
2826
2905
|
"servers": [{
|
|
2827
2906
|
"url": "https://api.primitive.dev/v1",
|
|
2828
|
-
"description": "
|
|
2907
|
+
"description": "Canonical API host (recommended)"
|
|
2829
2908
|
}, {
|
|
2830
2909
|
"url": "https://www.primitive.dev/api/v1",
|
|
2831
|
-
"description": "
|
|
2910
|
+
"description": "Legacy compatibility host (Vercel body limit applies)"
|
|
2832
2911
|
}],
|
|
2833
2912
|
"tags": ["Sending"],
|
|
2834
2913
|
"parameters": [{
|
|
@@ -3030,7 +3109,7 @@ const openapiDocument = {
|
|
|
3030
3109
|
"post": {
|
|
3031
3110
|
"operationId": "createFunction",
|
|
3032
3111
|
"summary": "Deploy a function",
|
|
3033
|
-
"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**
|
|
3112
|
+
"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**Routing.** On successful deploy, the function code is live\nin the runtime, but inbound mail will not reach it until at\nleast one route is bound. Routes are managed from the Primitive\ndashboard. A `deploy_status` of `deployed` means the script is\ninstalled, not that the function is receiving mail. The\ninternal runtime URL is not returned by the API and is not a\ncustomer-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",
|
|
3034
3113
|
"tags": ["Functions"],
|
|
3035
3114
|
"requestBody": {
|
|
3036
3115
|
"required": true,
|
|
@@ -3109,7 +3188,7 @@ const openapiDocument = {
|
|
|
3109
3188
|
"delete": {
|
|
3110
3189
|
"operationId": "deleteFunction",
|
|
3111
3190
|
"summary": "Delete a function",
|
|
3112
|
-
"description": "Soft-deletes the function row, removes the script from the edge\nruntime, and deactivates
|
|
3191
|
+
"description": "Soft-deletes the function row, removes the script from the edge\nruntime, and deactivates any route bound to this function so no\nfurther inbound mail is delivered. Past deploy history,\ninvocations, and logs are retained.\n\nReturns 502 if the runtime delete fails partway; the function\nrow stays in place and the call is safe to retry until it\nsucceeds.\n",
|
|
3113
3192
|
"tags": ["Functions"],
|
|
3114
3193
|
"responses": {
|
|
3115
3194
|
"200": { "$ref": "#/components/responses/Deleted" },
|
|
@@ -3194,6 +3273,92 @@ const openapiDocument = {
|
|
|
3194
3273
|
}
|
|
3195
3274
|
}
|
|
3196
3275
|
},
|
|
3276
|
+
"/functions/routing-topology": { "get": {
|
|
3277
|
+
"operationId": "getOrgRoutingTopology",
|
|
3278
|
+
"summary": "Get the org's function routing topology",
|
|
3279
|
+
"description": "Returns a single snapshot of how inbound mail is routed across\nthis org's active domains and functions: which active domain has\nwhich function bound, the org's fallback function (if any), and\nevery deployed function with no route bound. Use this to answer\n\"which of my functions actually receive mail?\" diagnostically.\n",
|
|
3280
|
+
"tags": ["Functions"],
|
|
3281
|
+
"responses": {
|
|
3282
|
+
"200": {
|
|
3283
|
+
"description": "Routing topology",
|
|
3284
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
3285
|
+
"type": "object",
|
|
3286
|
+
"properties": { "data": { "$ref": "#/components/schemas/RoutingTopology" } }
|
|
3287
|
+
}] } } }
|
|
3288
|
+
},
|
|
3289
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3290
|
+
"403": { "$ref": "#/components/responses/Forbidden" }
|
|
3291
|
+
}
|
|
3292
|
+
} },
|
|
3293
|
+
"/functions/{id}/routing": {
|
|
3294
|
+
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
3295
|
+
"get": {
|
|
3296
|
+
"operationId": "getFunctionRouting",
|
|
3297
|
+
"summary": "Get a function's current route binding",
|
|
3298
|
+
"description": "Returns the endpoint binding for the function, or null when no\nroute is currently bound. The binding identifies whether the\nfunction receives mail for a specific domain (scoped) or for any\nactive domain that has no scoped binding (fallback).\n",
|
|
3299
|
+
"tags": ["Functions"],
|
|
3300
|
+
"responses": {
|
|
3301
|
+
"200": {
|
|
3302
|
+
"description": "Function routing",
|
|
3303
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
3304
|
+
"type": "object",
|
|
3305
|
+
"properties": { "data": { "oneOf": [{ "$ref": "#/components/schemas/FunctionRouting" }, { "type": "null" }] } }
|
|
3306
|
+
}] } } }
|
|
3307
|
+
},
|
|
3308
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3309
|
+
"404": { "$ref": "#/components/responses/NotFound" }
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
},
|
|
3313
|
+
"/functions/{id}/route": {
|
|
3314
|
+
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
3315
|
+
"put": {
|
|
3316
|
+
"operationId": "setFunctionRoute",
|
|
3317
|
+
"summary": "Bind a route to a function",
|
|
3318
|
+
"description": "Binds inbound mail to this function. The route target is either\na specific verified domain (scoped) or the org's fallback (any\nactive domain with no scoped binding). If another function is\nalready bound at the target, returns a `conflict` envelope\ndescribing the holder; re-issue with `takeover: true` to\ndeactivate that prior binding and install this one.\n",
|
|
3319
|
+
"tags": ["Functions"],
|
|
3320
|
+
"requestBody": {
|
|
3321
|
+
"required": true,
|
|
3322
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/FunctionRouteBody" } } }
|
|
3323
|
+
},
|
|
3324
|
+
"responses": {
|
|
3325
|
+
"200": {
|
|
3326
|
+
"description": "Route bound, or conflict requiring takeover",
|
|
3327
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
3328
|
+
"type": "object",
|
|
3329
|
+
"properties": { "data": { "$ref": "#/components/schemas/FunctionRouteResult" } }
|
|
3330
|
+
}] } } }
|
|
3331
|
+
},
|
|
3332
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
3333
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3334
|
+
"404": { "$ref": "#/components/responses/NotFound" }
|
|
3335
|
+
}
|
|
3336
|
+
},
|
|
3337
|
+
"delete": {
|
|
3338
|
+
"operationId": "unsetFunctionRoute",
|
|
3339
|
+
"summary": "Unbind any route from a function",
|
|
3340
|
+
"description": "Deactivates every active endpoint bound to this function. The\nfunction stays deployed but stops receiving inbound mail. Safe\nto call when no route is currently bound (no-op).\n",
|
|
3341
|
+
"tags": ["Functions"],
|
|
3342
|
+
"responses": {
|
|
3343
|
+
"200": {
|
|
3344
|
+
"description": "Route unbound",
|
|
3345
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
3346
|
+
"type": "object",
|
|
3347
|
+
"properties": { "data": {
|
|
3348
|
+
"type": "object",
|
|
3349
|
+
"properties": { "unrouted": {
|
|
3350
|
+
"type": "boolean",
|
|
3351
|
+
"enum": [true]
|
|
3352
|
+
} },
|
|
3353
|
+
"required": ["unrouted"]
|
|
3354
|
+
} }
|
|
3355
|
+
}] } } }
|
|
3356
|
+
},
|
|
3357
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3358
|
+
"404": { "$ref": "#/components/responses/NotFound" }
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
},
|
|
3197
3362
|
"/functions/{id}/secrets": {
|
|
3198
3363
|
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
3199
3364
|
"get": {
|
|
@@ -6719,6 +6884,178 @@ const openapiDocument = {
|
|
|
6719
6884
|
"trace_url"
|
|
6720
6885
|
]
|
|
6721
6886
|
},
|
|
6887
|
+
"FunctionRouting": {
|
|
6888
|
+
"type": "object",
|
|
6889
|
+
"description": "A single route binding for a function. `domain` is null when the\nbinding is the org's fallback (any active domain without a scoped\nbinding); otherwise it carries the scoped domain. `rules` is\nreserved for future routing predicates.\n",
|
|
6890
|
+
"properties": {
|
|
6891
|
+
"endpoint_id": {
|
|
6892
|
+
"type": "string",
|
|
6893
|
+
"format": "uuid"
|
|
6894
|
+
},
|
|
6895
|
+
"enabled": { "type": "boolean" },
|
|
6896
|
+
"domain": {
|
|
6897
|
+
"type": ["object", "null"],
|
|
6898
|
+
"properties": {
|
|
6899
|
+
"id": {
|
|
6900
|
+
"type": "string",
|
|
6901
|
+
"format": "uuid"
|
|
6902
|
+
},
|
|
6903
|
+
"name": { "type": ["string", "null"] }
|
|
6904
|
+
},
|
|
6905
|
+
"required": ["id"]
|
|
6906
|
+
},
|
|
6907
|
+
"rules": {
|
|
6908
|
+
"type": "object",
|
|
6909
|
+
"description": "Future routing predicates. Currently empty."
|
|
6910
|
+
},
|
|
6911
|
+
"delivery_count": { "type": "integer" },
|
|
6912
|
+
"success_count": { "type": "integer" },
|
|
6913
|
+
"failure_count": { "type": "integer" },
|
|
6914
|
+
"consecutive_fails": { "type": "integer" },
|
|
6915
|
+
"last_delivery_at": {
|
|
6916
|
+
"type": ["string", "null"],
|
|
6917
|
+
"format": "date-time"
|
|
6918
|
+
},
|
|
6919
|
+
"last_success_at": {
|
|
6920
|
+
"type": ["string", "null"],
|
|
6921
|
+
"format": "date-time"
|
|
6922
|
+
},
|
|
6923
|
+
"last_failure_at": {
|
|
6924
|
+
"type": ["string", "null"],
|
|
6925
|
+
"format": "date-time"
|
|
6926
|
+
}
|
|
6927
|
+
},
|
|
6928
|
+
"required": [
|
|
6929
|
+
"endpoint_id",
|
|
6930
|
+
"enabled",
|
|
6931
|
+
"domain",
|
|
6932
|
+
"rules"
|
|
6933
|
+
]
|
|
6934
|
+
},
|
|
6935
|
+
"RoutingTopology": {
|
|
6936
|
+
"type": "object",
|
|
6937
|
+
"description": "Org-wide map of function routing: which domain points at which\nfunction, the org's fallback binding (if any), and every\ndeployed function with no route currently bound.\n",
|
|
6938
|
+
"properties": {
|
|
6939
|
+
"domains": {
|
|
6940
|
+
"type": "array",
|
|
6941
|
+
"items": {
|
|
6942
|
+
"type": "object",
|
|
6943
|
+
"properties": {
|
|
6944
|
+
"domain_id": {
|
|
6945
|
+
"type": "string",
|
|
6946
|
+
"format": "uuid"
|
|
6947
|
+
},
|
|
6948
|
+
"domain": { "type": "string" },
|
|
6949
|
+
"routed_function": {
|
|
6950
|
+
"type": ["object", "null"],
|
|
6951
|
+
"properties": {
|
|
6952
|
+
"id": {
|
|
6953
|
+
"type": "string",
|
|
6954
|
+
"format": "uuid"
|
|
6955
|
+
},
|
|
6956
|
+
"name": { "type": "string" }
|
|
6957
|
+
},
|
|
6958
|
+
"required": ["id", "name"]
|
|
6959
|
+
},
|
|
6960
|
+
"endpoint_enabled": { "type": ["boolean", "null"] }
|
|
6961
|
+
},
|
|
6962
|
+
"required": [
|
|
6963
|
+
"domain_id",
|
|
6964
|
+
"domain",
|
|
6965
|
+
"routed_function",
|
|
6966
|
+
"endpoint_enabled"
|
|
6967
|
+
]
|
|
6968
|
+
}
|
|
6969
|
+
},
|
|
6970
|
+
"fallback_function": {
|
|
6971
|
+
"type": ["object", "null"],
|
|
6972
|
+
"properties": {
|
|
6973
|
+
"id": {
|
|
6974
|
+
"type": "string",
|
|
6975
|
+
"format": "uuid"
|
|
6976
|
+
},
|
|
6977
|
+
"name": { "type": "string" }
|
|
6978
|
+
},
|
|
6979
|
+
"required": ["id", "name"]
|
|
6980
|
+
},
|
|
6981
|
+
"fallback_enabled": { "type": ["boolean", "null"] },
|
|
6982
|
+
"unrouted_functions": {
|
|
6983
|
+
"type": "array",
|
|
6984
|
+
"items": {
|
|
6985
|
+
"type": "object",
|
|
6986
|
+
"properties": {
|
|
6987
|
+
"id": {
|
|
6988
|
+
"type": "string",
|
|
6989
|
+
"format": "uuid"
|
|
6990
|
+
},
|
|
6991
|
+
"name": { "type": "string" }
|
|
6992
|
+
},
|
|
6993
|
+
"required": ["id", "name"]
|
|
6994
|
+
}
|
|
6995
|
+
}
|
|
6996
|
+
},
|
|
6997
|
+
"required": [
|
|
6998
|
+
"domains",
|
|
6999
|
+
"fallback_function",
|
|
7000
|
+
"fallback_enabled",
|
|
7001
|
+
"unrouted_functions"
|
|
7002
|
+
]
|
|
7003
|
+
},
|
|
7004
|
+
"FunctionRouteBody": {
|
|
7005
|
+
"type": "object",
|
|
7006
|
+
"description": "Target for a route binding. Either a specific verified domain\n(scoped) or the org-wide fallback. Pass `takeover: true` to\ndeactivate any conflicting binding before installing this one.\n",
|
|
7007
|
+
"properties": {
|
|
7008
|
+
"target": { "oneOf": [{
|
|
7009
|
+
"type": "object",
|
|
7010
|
+
"properties": {
|
|
7011
|
+
"kind": {
|
|
7012
|
+
"type": "string",
|
|
7013
|
+
"enum": ["domain"]
|
|
7014
|
+
},
|
|
7015
|
+
"domainId": {
|
|
7016
|
+
"type": "string",
|
|
7017
|
+
"format": "uuid"
|
|
7018
|
+
}
|
|
7019
|
+
},
|
|
7020
|
+
"required": ["kind", "domainId"]
|
|
7021
|
+
}, {
|
|
7022
|
+
"type": "object",
|
|
7023
|
+
"properties": { "kind": {
|
|
7024
|
+
"type": "string",
|
|
7025
|
+
"enum": ["fallback"]
|
|
7026
|
+
} },
|
|
7027
|
+
"required": ["kind"]
|
|
7028
|
+
}] },
|
|
7029
|
+
"takeover": {
|
|
7030
|
+
"type": "boolean",
|
|
7031
|
+
"description": "When true, deactivate any conflicting binding before installing this one."
|
|
7032
|
+
}
|
|
7033
|
+
},
|
|
7034
|
+
"required": ["target"]
|
|
7035
|
+
},
|
|
7036
|
+
"FunctionRouteResult": {
|
|
7037
|
+
"type": "object",
|
|
7038
|
+
"description": "On success, carries the new `routing`. On conflict, carries\n`conflict` describing the binding holder so the caller can\nre-issue with `takeover: true`.\n",
|
|
7039
|
+
"properties": {
|
|
7040
|
+
"routing": { "oneOf": [{ "$ref": "#/components/schemas/FunctionRouting" }, { "type": "null" }] },
|
|
7041
|
+
"conflict": {
|
|
7042
|
+
"type": "object",
|
|
7043
|
+
"properties": {
|
|
7044
|
+
"kind": {
|
|
7045
|
+
"type": "string",
|
|
7046
|
+
"enum": ["http", "function"]
|
|
7047
|
+
},
|
|
7048
|
+
"functionId": {
|
|
7049
|
+
"type": ["string", "null"],
|
|
7050
|
+
"format": "uuid"
|
|
7051
|
+
},
|
|
7052
|
+
"functionName": { "type": ["string", "null"] },
|
|
7053
|
+
"url": { "type": ["string", "null"] }
|
|
7054
|
+
},
|
|
7055
|
+
"required": ["kind"]
|
|
7056
|
+
}
|
|
7057
|
+
}
|
|
7058
|
+
},
|
|
6722
7059
|
"FunctionTestRunState": {
|
|
6723
7060
|
"type": "string",
|
|
6724
7061
|
"description": "High-level state for a function test run trace:\n - `send_failed`: the initial test email send failed.\n - `waiting_for_send`: the test run was created but no send result has been recorded yet.\n - `waiting_for_inbound`: the test send was queued and the matching inbound email has not arrived yet.\n - `waiting_for_function`: the inbound email arrived and webhook/function processing is still in flight.\n - `completed`: the function webhook completed successfully.\n - `failed`: webhook delivery exhausted retries.\n",
|
|
@@ -10434,7 +10771,7 @@ const operationManifest = [
|
|
|
10434
10771
|
"binaryResponse": false,
|
|
10435
10772
|
"bodyRequired": true,
|
|
10436
10773
|
"command": "create-function",
|
|
10437
|
-
"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**
|
|
10774
|
+
"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**Routing.** On successful deploy, the function code is live\nin the runtime, but inbound mail will not reach it until at\nleast one route is bound. Routes are managed from the Primitive\ndashboard. A `deploy_status` of `deployed` means the script is\ninstalled, not that the function is receiving mail. The\ninternal runtime URL is not returned by the API and is not a\ncustomer-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",
|
|
10438
10775
|
"hasJsonBody": true,
|
|
10439
10776
|
"method": "POST",
|
|
10440
10777
|
"operationId": "createFunction",
|
|
@@ -10570,7 +10907,7 @@ const operationManifest = [
|
|
|
10570
10907
|
"binaryResponse": false,
|
|
10571
10908
|
"bodyRequired": false,
|
|
10572
10909
|
"command": "delete-function",
|
|
10573
|
-
"description": "Soft-deletes the function row, removes the script from the edge\nruntime, and deactivates
|
|
10910
|
+
"description": "Soft-deletes the function row, removes the script from the edge\nruntime, and deactivates any route bound to this function so no\nfurther inbound mail is delivered. Past deploy history,\ninvocations, and logs are retained.\n\nReturns 502 if the runtime delete fails partway; the function\nrow stays in place and the call is safe to retry until it\nsucceeds.\n",
|
|
10574
10911
|
"hasJsonBody": false,
|
|
10575
10912
|
"method": "DELETE",
|
|
10576
10913
|
"operationId": "deleteFunction",
|
|
@@ -10694,57 +11031,128 @@ const operationManifest = [
|
|
|
10694
11031
|
{
|
|
10695
11032
|
"binaryResponse": false,
|
|
10696
11033
|
"bodyRequired": false,
|
|
10697
|
-
"command": "get-function-
|
|
10698
|
-
"description": "Returns the
|
|
11034
|
+
"command": "get-function-routing",
|
|
11035
|
+
"description": "Returns the endpoint binding for the function, or null when no\nroute is currently bound. The binding identifies whether the\nfunction receives mail for a specific domain (scoped) or for any\nactive domain that has no scoped binding (fallback).\n",
|
|
10699
11036
|
"hasJsonBody": false,
|
|
10700
11037
|
"method": "GET",
|
|
10701
|
-
"operationId": "
|
|
10702
|
-
"path": "/functions/{id}/
|
|
11038
|
+
"operationId": "getFunctionRouting",
|
|
11039
|
+
"path": "/functions/{id}/routing",
|
|
10703
11040
|
"pathParams": [{
|
|
10704
11041
|
"description": "Resource UUID",
|
|
10705
11042
|
"enum": null,
|
|
10706
11043
|
"name": "id",
|
|
10707
11044
|
"required": true,
|
|
10708
11045
|
"type": "string"
|
|
10709
|
-
}, {
|
|
10710
|
-
"description": "Function test run id returned by POST /functions/{id}/test.",
|
|
10711
|
-
"enum": null,
|
|
10712
|
-
"name": "run_id",
|
|
10713
|
-
"required": true,
|
|
10714
|
-
"type": "string"
|
|
10715
11046
|
}],
|
|
10716
11047
|
"queryParams": [],
|
|
10717
11048
|
"requestSchema": null,
|
|
10718
|
-
"responseSchema": {
|
|
11049
|
+
"responseSchema": { "oneOf": [{
|
|
10719
11050
|
"type": "object",
|
|
10720
|
-
"description": "
|
|
11051
|
+
"description": "A single route binding for a function. `domain` is null when the\nbinding is the org's fallback (any active domain without a scoped\nbinding); otherwise it carries the scoped domain. `rules` is\nreserved for future routing predicates.\n",
|
|
10721
11052
|
"properties": {
|
|
10722
|
-
"
|
|
11053
|
+
"endpoint_id": {
|
|
10723
11054
|
"type": "string",
|
|
10724
|
-
"
|
|
10725
|
-
"enum": [
|
|
10726
|
-
"send_failed",
|
|
10727
|
-
"waiting_for_send",
|
|
10728
|
-
"waiting_for_inbound",
|
|
10729
|
-
"waiting_for_function",
|
|
10730
|
-
"completed",
|
|
10731
|
-
"failed"
|
|
10732
|
-
]
|
|
11055
|
+
"format": "uuid"
|
|
10733
11056
|
},
|
|
10734
|
-
"
|
|
10735
|
-
|
|
11057
|
+
"enabled": { "type": "boolean" },
|
|
11058
|
+
"domain": {
|
|
11059
|
+
"type": ["object", "null"],
|
|
10736
11060
|
"properties": {
|
|
10737
11061
|
"id": {
|
|
10738
11062
|
"type": "string",
|
|
10739
11063
|
"format": "uuid"
|
|
10740
11064
|
},
|
|
10741
|
-
"
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
11065
|
+
"name": { "type": ["string", "null"] }
|
|
11066
|
+
},
|
|
11067
|
+
"required": ["id"]
|
|
11068
|
+
},
|
|
11069
|
+
"rules": {
|
|
11070
|
+
"type": "object",
|
|
11071
|
+
"description": "Future routing predicates. Currently empty."
|
|
11072
|
+
},
|
|
11073
|
+
"delivery_count": { "type": "integer" },
|
|
11074
|
+
"success_count": { "type": "integer" },
|
|
11075
|
+
"failure_count": { "type": "integer" },
|
|
11076
|
+
"consecutive_fails": { "type": "integer" },
|
|
11077
|
+
"last_delivery_at": {
|
|
11078
|
+
"type": ["string", "null"],
|
|
11079
|
+
"format": "date-time"
|
|
11080
|
+
},
|
|
11081
|
+
"last_success_at": {
|
|
11082
|
+
"type": ["string", "null"],
|
|
11083
|
+
"format": "date-time"
|
|
11084
|
+
},
|
|
11085
|
+
"last_failure_at": {
|
|
11086
|
+
"type": ["string", "null"],
|
|
11087
|
+
"format": "date-time"
|
|
11088
|
+
}
|
|
11089
|
+
},
|
|
11090
|
+
"required": [
|
|
11091
|
+
"endpoint_id",
|
|
11092
|
+
"enabled",
|
|
11093
|
+
"domain",
|
|
11094
|
+
"rules"
|
|
11095
|
+
]
|
|
11096
|
+
}, { "type": "null" }] },
|
|
11097
|
+
"sdkName": "getFunctionRouting",
|
|
11098
|
+
"summary": "Get a function's current route binding",
|
|
11099
|
+
"tag": "Functions",
|
|
11100
|
+
"tagCommand": "functions"
|
|
11101
|
+
},
|
|
11102
|
+
{
|
|
11103
|
+
"binaryResponse": false,
|
|
11104
|
+
"bodyRequired": false,
|
|
11105
|
+
"command": "get-function-test-run-trace",
|
|
11106
|
+
"description": "Returns the current end-to-end trace for a function test run.\nThe trace is intentionally partial while the test is still in\nflight: callers can poll this endpoint and watch it fill in\nfrom send -> inbound -> webhook deliveries -> outbound\nrequests, logs, and replies.\n",
|
|
11107
|
+
"hasJsonBody": false,
|
|
11108
|
+
"method": "GET",
|
|
11109
|
+
"operationId": "getFunctionTestRunTrace",
|
|
11110
|
+
"path": "/functions/{id}/test-runs/{run_id}/trace",
|
|
11111
|
+
"pathParams": [{
|
|
11112
|
+
"description": "Resource UUID",
|
|
11113
|
+
"enum": null,
|
|
11114
|
+
"name": "id",
|
|
11115
|
+
"required": true,
|
|
11116
|
+
"type": "string"
|
|
11117
|
+
}, {
|
|
11118
|
+
"description": "Function test run id returned by POST /functions/{id}/test.",
|
|
11119
|
+
"enum": null,
|
|
11120
|
+
"name": "run_id",
|
|
11121
|
+
"required": true,
|
|
11122
|
+
"type": "string"
|
|
11123
|
+
}],
|
|
11124
|
+
"queryParams": [],
|
|
11125
|
+
"requestSchema": null,
|
|
11126
|
+
"responseSchema": {
|
|
11127
|
+
"type": "object",
|
|
11128
|
+
"description": "End-to-end trace for a `POST /functions/{id}/test` run. The\nshape is stable, but many nested sections are null or empty\nuntil the corresponding phase has happened.\n",
|
|
11129
|
+
"properties": {
|
|
11130
|
+
"state": {
|
|
11131
|
+
"type": "string",
|
|
11132
|
+
"description": "High-level state for a function test run trace:\n - `send_failed`: the initial test email send failed.\n - `waiting_for_send`: the test run was created but no send result has been recorded yet.\n - `waiting_for_inbound`: the test send was queued and the matching inbound email has not arrived yet.\n - `waiting_for_function`: the inbound email arrived and webhook/function processing is still in flight.\n - `completed`: the function webhook completed successfully.\n - `failed`: webhook delivery exhausted retries.\n",
|
|
11133
|
+
"enum": [
|
|
11134
|
+
"send_failed",
|
|
11135
|
+
"waiting_for_send",
|
|
11136
|
+
"waiting_for_inbound",
|
|
11137
|
+
"waiting_for_function",
|
|
11138
|
+
"completed",
|
|
11139
|
+
"failed"
|
|
11140
|
+
]
|
|
11141
|
+
},
|
|
11142
|
+
"test_run": {
|
|
11143
|
+
"type": "object",
|
|
11144
|
+
"properties": {
|
|
11145
|
+
"id": {
|
|
11146
|
+
"type": "string",
|
|
11147
|
+
"format": "uuid"
|
|
11148
|
+
},
|
|
11149
|
+
"function_id": {
|
|
11150
|
+
"type": "string",
|
|
11151
|
+
"format": "uuid"
|
|
11152
|
+
},
|
|
11153
|
+
"inbound_domain": { "type": "string" },
|
|
11154
|
+
"to": { "type": "string" },
|
|
11155
|
+
"from": { "type": "string" },
|
|
10748
11156
|
"subject": { "type": "string" },
|
|
10749
11157
|
"poll_since": {
|
|
10750
11158
|
"type": "string",
|
|
@@ -11124,6 +11532,92 @@ const operationManifest = [
|
|
|
11124
11532
|
"tag": "Functions",
|
|
11125
11533
|
"tagCommand": "functions"
|
|
11126
11534
|
},
|
|
11535
|
+
{
|
|
11536
|
+
"binaryResponse": false,
|
|
11537
|
+
"bodyRequired": false,
|
|
11538
|
+
"command": "get-org-routing-topology",
|
|
11539
|
+
"description": "Returns a single snapshot of how inbound mail is routed across\nthis org's active domains and functions: which active domain has\nwhich function bound, the org's fallback function (if any), and\nevery deployed function with no route bound. Use this to answer\n\"which of my functions actually receive mail?\" diagnostically.\n",
|
|
11540
|
+
"hasJsonBody": false,
|
|
11541
|
+
"method": "GET",
|
|
11542
|
+
"operationId": "getOrgRoutingTopology",
|
|
11543
|
+
"path": "/functions/routing-topology",
|
|
11544
|
+
"pathParams": [],
|
|
11545
|
+
"queryParams": [],
|
|
11546
|
+
"requestSchema": null,
|
|
11547
|
+
"responseSchema": {
|
|
11548
|
+
"type": "object",
|
|
11549
|
+
"description": "Org-wide map of function routing: which domain points at which\nfunction, the org's fallback binding (if any), and every\ndeployed function with no route currently bound.\n",
|
|
11550
|
+
"properties": {
|
|
11551
|
+
"domains": {
|
|
11552
|
+
"type": "array",
|
|
11553
|
+
"items": {
|
|
11554
|
+
"type": "object",
|
|
11555
|
+
"properties": {
|
|
11556
|
+
"domain_id": {
|
|
11557
|
+
"type": "string",
|
|
11558
|
+
"format": "uuid"
|
|
11559
|
+
},
|
|
11560
|
+
"domain": { "type": "string" },
|
|
11561
|
+
"routed_function": {
|
|
11562
|
+
"type": ["object", "null"],
|
|
11563
|
+
"properties": {
|
|
11564
|
+
"id": {
|
|
11565
|
+
"type": "string",
|
|
11566
|
+
"format": "uuid"
|
|
11567
|
+
},
|
|
11568
|
+
"name": { "type": "string" }
|
|
11569
|
+
},
|
|
11570
|
+
"required": ["id", "name"]
|
|
11571
|
+
},
|
|
11572
|
+
"endpoint_enabled": { "type": ["boolean", "null"] }
|
|
11573
|
+
},
|
|
11574
|
+
"required": [
|
|
11575
|
+
"domain_id",
|
|
11576
|
+
"domain",
|
|
11577
|
+
"routed_function",
|
|
11578
|
+
"endpoint_enabled"
|
|
11579
|
+
]
|
|
11580
|
+
}
|
|
11581
|
+
},
|
|
11582
|
+
"fallback_function": {
|
|
11583
|
+
"type": ["object", "null"],
|
|
11584
|
+
"properties": {
|
|
11585
|
+
"id": {
|
|
11586
|
+
"type": "string",
|
|
11587
|
+
"format": "uuid"
|
|
11588
|
+
},
|
|
11589
|
+
"name": { "type": "string" }
|
|
11590
|
+
},
|
|
11591
|
+
"required": ["id", "name"]
|
|
11592
|
+
},
|
|
11593
|
+
"fallback_enabled": { "type": ["boolean", "null"] },
|
|
11594
|
+
"unrouted_functions": {
|
|
11595
|
+
"type": "array",
|
|
11596
|
+
"items": {
|
|
11597
|
+
"type": "object",
|
|
11598
|
+
"properties": {
|
|
11599
|
+
"id": {
|
|
11600
|
+
"type": "string",
|
|
11601
|
+
"format": "uuid"
|
|
11602
|
+
},
|
|
11603
|
+
"name": { "type": "string" }
|
|
11604
|
+
},
|
|
11605
|
+
"required": ["id", "name"]
|
|
11606
|
+
}
|
|
11607
|
+
}
|
|
11608
|
+
},
|
|
11609
|
+
"required": [
|
|
11610
|
+
"domains",
|
|
11611
|
+
"fallback_function",
|
|
11612
|
+
"fallback_enabled",
|
|
11613
|
+
"unrouted_functions"
|
|
11614
|
+
]
|
|
11615
|
+
},
|
|
11616
|
+
"sdkName": "getOrgRoutingTopology",
|
|
11617
|
+
"summary": "Get the org's function routing topology",
|
|
11618
|
+
"tag": "Functions",
|
|
11619
|
+
"tagCommand": "functions"
|
|
11620
|
+
},
|
|
11127
11621
|
{
|
|
11128
11622
|
"binaryResponse": false,
|
|
11129
11623
|
"bodyRequired": false,
|
|
@@ -11343,6 +11837,130 @@ const operationManifest = [
|
|
|
11343
11837
|
"tag": "Functions",
|
|
11344
11838
|
"tagCommand": "functions"
|
|
11345
11839
|
},
|
|
11840
|
+
{
|
|
11841
|
+
"binaryResponse": false,
|
|
11842
|
+
"bodyRequired": true,
|
|
11843
|
+
"command": "set-function-route",
|
|
11844
|
+
"description": "Binds inbound mail to this function. The route target is either\na specific verified domain (scoped) or the org's fallback (any\nactive domain with no scoped binding). If another function is\nalready bound at the target, returns a `conflict` envelope\ndescribing the holder; re-issue with `takeover: true` to\ndeactivate that prior binding and install this one.\n",
|
|
11845
|
+
"hasJsonBody": true,
|
|
11846
|
+
"method": "PUT",
|
|
11847
|
+
"operationId": "setFunctionRoute",
|
|
11848
|
+
"path": "/functions/{id}/route",
|
|
11849
|
+
"pathParams": [{
|
|
11850
|
+
"description": "Resource UUID",
|
|
11851
|
+
"enum": null,
|
|
11852
|
+
"name": "id",
|
|
11853
|
+
"required": true,
|
|
11854
|
+
"type": "string"
|
|
11855
|
+
}],
|
|
11856
|
+
"queryParams": [],
|
|
11857
|
+
"requestSchema": {
|
|
11858
|
+
"type": "object",
|
|
11859
|
+
"description": "Target for a route binding. Either a specific verified domain\n(scoped) or the org-wide fallback. Pass `takeover: true` to\ndeactivate any conflicting binding before installing this one.\n",
|
|
11860
|
+
"properties": {
|
|
11861
|
+
"target": { "oneOf": [{
|
|
11862
|
+
"type": "object",
|
|
11863
|
+
"properties": {
|
|
11864
|
+
"kind": {
|
|
11865
|
+
"type": "string",
|
|
11866
|
+
"enum": ["domain"]
|
|
11867
|
+
},
|
|
11868
|
+
"domainId": {
|
|
11869
|
+
"type": "string",
|
|
11870
|
+
"format": "uuid"
|
|
11871
|
+
}
|
|
11872
|
+
},
|
|
11873
|
+
"required": ["kind", "domainId"]
|
|
11874
|
+
}, {
|
|
11875
|
+
"type": "object",
|
|
11876
|
+
"properties": { "kind": {
|
|
11877
|
+
"type": "string",
|
|
11878
|
+
"enum": ["fallback"]
|
|
11879
|
+
} },
|
|
11880
|
+
"required": ["kind"]
|
|
11881
|
+
}] },
|
|
11882
|
+
"takeover": {
|
|
11883
|
+
"type": "boolean",
|
|
11884
|
+
"description": "When true, deactivate any conflicting binding before installing this one."
|
|
11885
|
+
}
|
|
11886
|
+
},
|
|
11887
|
+
"required": ["target"]
|
|
11888
|
+
},
|
|
11889
|
+
"responseSchema": {
|
|
11890
|
+
"type": "object",
|
|
11891
|
+
"description": "On success, carries the new `routing`. On conflict, carries\n`conflict` describing the binding holder so the caller can\nre-issue with `takeover: true`.\n",
|
|
11892
|
+
"properties": {
|
|
11893
|
+
"routing": { "oneOf": [{
|
|
11894
|
+
"type": "object",
|
|
11895
|
+
"description": "A single route binding for a function. `domain` is null when the\nbinding is the org's fallback (any active domain without a scoped\nbinding); otherwise it carries the scoped domain. `rules` is\nreserved for future routing predicates.\n",
|
|
11896
|
+
"properties": {
|
|
11897
|
+
"endpoint_id": {
|
|
11898
|
+
"type": "string",
|
|
11899
|
+
"format": "uuid"
|
|
11900
|
+
},
|
|
11901
|
+
"enabled": { "type": "boolean" },
|
|
11902
|
+
"domain": {
|
|
11903
|
+
"type": ["object", "null"],
|
|
11904
|
+
"properties": {
|
|
11905
|
+
"id": {
|
|
11906
|
+
"type": "string",
|
|
11907
|
+
"format": "uuid"
|
|
11908
|
+
},
|
|
11909
|
+
"name": { "type": ["string", "null"] }
|
|
11910
|
+
},
|
|
11911
|
+
"required": ["id"]
|
|
11912
|
+
},
|
|
11913
|
+
"rules": {
|
|
11914
|
+
"type": "object",
|
|
11915
|
+
"description": "Future routing predicates. Currently empty."
|
|
11916
|
+
},
|
|
11917
|
+
"delivery_count": { "type": "integer" },
|
|
11918
|
+
"success_count": { "type": "integer" },
|
|
11919
|
+
"failure_count": { "type": "integer" },
|
|
11920
|
+
"consecutive_fails": { "type": "integer" },
|
|
11921
|
+
"last_delivery_at": {
|
|
11922
|
+
"type": ["string", "null"],
|
|
11923
|
+
"format": "date-time"
|
|
11924
|
+
},
|
|
11925
|
+
"last_success_at": {
|
|
11926
|
+
"type": ["string", "null"],
|
|
11927
|
+
"format": "date-time"
|
|
11928
|
+
},
|
|
11929
|
+
"last_failure_at": {
|
|
11930
|
+
"type": ["string", "null"],
|
|
11931
|
+
"format": "date-time"
|
|
11932
|
+
}
|
|
11933
|
+
},
|
|
11934
|
+
"required": [
|
|
11935
|
+
"endpoint_id",
|
|
11936
|
+
"enabled",
|
|
11937
|
+
"domain",
|
|
11938
|
+
"rules"
|
|
11939
|
+
]
|
|
11940
|
+
}, { "type": "null" }] },
|
|
11941
|
+
"conflict": {
|
|
11942
|
+
"type": "object",
|
|
11943
|
+
"properties": {
|
|
11944
|
+
"kind": {
|
|
11945
|
+
"type": "string",
|
|
11946
|
+
"enum": ["http", "function"]
|
|
11947
|
+
},
|
|
11948
|
+
"functionId": {
|
|
11949
|
+
"type": ["string", "null"],
|
|
11950
|
+
"format": "uuid"
|
|
11951
|
+
},
|
|
11952
|
+
"functionName": { "type": ["string", "null"] },
|
|
11953
|
+
"url": { "type": ["string", "null"] }
|
|
11954
|
+
},
|
|
11955
|
+
"required": ["kind"]
|
|
11956
|
+
}
|
|
11957
|
+
}
|
|
11958
|
+
},
|
|
11959
|
+
"sdkName": "setFunctionRoute",
|
|
11960
|
+
"summary": "Bind a route to a function",
|
|
11961
|
+
"tag": "Functions",
|
|
11962
|
+
"tagCommand": "functions"
|
|
11963
|
+
},
|
|
11346
11964
|
{
|
|
11347
11965
|
"binaryResponse": false,
|
|
11348
11966
|
"bodyRequired": true,
|
|
@@ -11496,6 +12114,37 @@ const operationManifest = [
|
|
|
11496
12114
|
"tag": "Functions",
|
|
11497
12115
|
"tagCommand": "functions"
|
|
11498
12116
|
},
|
|
12117
|
+
{
|
|
12118
|
+
"binaryResponse": false,
|
|
12119
|
+
"bodyRequired": false,
|
|
12120
|
+
"command": "unset-function-route",
|
|
12121
|
+
"description": "Deactivates every active endpoint bound to this function. The\nfunction stays deployed but stops receiving inbound mail. Safe\nto call when no route is currently bound (no-op).\n",
|
|
12122
|
+
"hasJsonBody": false,
|
|
12123
|
+
"method": "DELETE",
|
|
12124
|
+
"operationId": "unsetFunctionRoute",
|
|
12125
|
+
"path": "/functions/{id}/route",
|
|
12126
|
+
"pathParams": [{
|
|
12127
|
+
"description": "Resource UUID",
|
|
12128
|
+
"enum": null,
|
|
12129
|
+
"name": "id",
|
|
12130
|
+
"required": true,
|
|
12131
|
+
"type": "string"
|
|
12132
|
+
}],
|
|
12133
|
+
"queryParams": [],
|
|
12134
|
+
"requestSchema": null,
|
|
12135
|
+
"responseSchema": {
|
|
12136
|
+
"type": "object",
|
|
12137
|
+
"properties": { "unrouted": {
|
|
12138
|
+
"type": "boolean",
|
|
12139
|
+
"enum": [true]
|
|
12140
|
+
} },
|
|
12141
|
+
"required": ["unrouted"]
|
|
12142
|
+
},
|
|
12143
|
+
"sdkName": "unsetFunctionRoute",
|
|
12144
|
+
"summary": "Unbind any route from a function",
|
|
12145
|
+
"tag": "Functions",
|
|
12146
|
+
"tagCommand": "functions"
|
|
12147
|
+
},
|
|
11499
12148
|
{
|
|
11500
12149
|
"binaryResponse": false,
|
|
11501
12150
|
"bodyRequired": true,
|
|
@@ -12805,7 +13454,7 @@ const operationManifest = [
|
|
|
12805
13454
|
"binaryResponse": false,
|
|
12806
13455
|
"bodyRequired": true,
|
|
12807
13456
|
"command": "send-email",
|
|
12808
|
-
"description": "Sends an outbound email through Primitive's outbound relay. By default\nthe request returns once the relay accepts the message for delivery.\nSet `wait: true` to wait for the first downstream SMTP delivery outcome.\n\n**Host routing.** /send-mail is served by the
|
|
13457
|
+
"description": "Sends an outbound email through Primitive's outbound relay. By default\nthe request returns once the relay accepts the message for delivery.\nSet `wait: true` to wait for the first downstream SMTP delivery outcome.\n\n**Host routing.** /send-mail is served by the canonical API host\n(`https://api.primitive.dev/v1`) so the request body can carry\ninline attachments up to ~30 MiB raw. The legacy dashboard\ncompatibility host (`https://www.primitive.dev/api/v1`) also accepts\n/send-mail, but Vercel request body limits apply before proxying.\nThe typed SDKs route /send-mail to the canonical API host\nautomatically.\n",
|
|
12809
13458
|
"hasJsonBody": true,
|
|
12810
13459
|
"method": "POST",
|
|
12811
13460
|
"operationId": "sendEmail",
|
|
@@ -13302,19 +13951,15 @@ function cliApiHeadersFromEnv(env = process.env) {
|
|
|
13302
13951
|
}
|
|
13303
13952
|
function resolveCliApiRequestConfig(params) {
|
|
13304
13953
|
const currentEnvironment = resolveConfigEnvironment(loadCliConfig(params.configDir));
|
|
13305
|
-
const
|
|
13306
|
-
|
|
13307
|
-
|
|
13308
|
-
const apiBaseUrl1 = params.apiBaseUrl1 !== void 0 ? normalizeApiBaseUrl1(params.apiBaseUrl1) : configuredApiBaseUrl1;
|
|
13309
|
-
const apiBaseUrl2 = params.apiBaseUrl2 !== void 0 ? normalizeApiBaseUrl2(params.apiBaseUrl2) : configuredApiBaseUrl2;
|
|
13954
|
+
const configuredApiBaseUrl = currentEnvironment?.config.api_base_url;
|
|
13955
|
+
if (currentEnvironment !== null && currentEnvironment.name !== "default" && params.apiBaseUrl === void 0 && configuredApiBaseUrl === void 0) throw new Errors.CLIError(`The active Primitive CLI environment \`${currentEnvironment.name}\` does not specify an api_base_url. Set one with \`primitive config set --environment ${currentEnvironment.name} --api-base-url https://...\`, or switch to a different environment with \`primitive config use <name>\`. Refusing to fall back to the production default for a non-default environment.`, { exit: 1 });
|
|
13956
|
+
const apiBaseUrl = params.apiBaseUrl !== void 0 ? normalizeApiBaseUrl(params.apiBaseUrl) : configuredApiBaseUrl;
|
|
13310
13957
|
return {
|
|
13311
|
-
|
|
13312
|
-
|
|
13313
|
-
baseUrlOverridden: apiBaseUrl1 !== void 0 || apiBaseUrl2 !== void 0,
|
|
13958
|
+
apiBaseUrl,
|
|
13959
|
+
baseUrlOverridden: apiBaseUrl !== void 0,
|
|
13314
13960
|
environmentName: currentEnvironment?.name ?? null,
|
|
13315
13961
|
headers: mergeHeaders(currentEnvironment?.config.headers, cliApiHeadersFromEnv(params.env)),
|
|
13316
|
-
|
|
13317
|
-
resolvedApiBaseUrl2: normalizeApiBaseUrl2(apiBaseUrl2)
|
|
13962
|
+
resolvedApiBaseUrl: normalizeApiBaseUrl(apiBaseUrl)
|
|
13318
13963
|
};
|
|
13319
13964
|
}
|
|
13320
13965
|
function createCliApiClient(params) {
|
|
@@ -13322,15 +13967,14 @@ function createCliApiClient(params) {
|
|
|
13322
13967
|
return {
|
|
13323
13968
|
apiClient: new PrimitiveApiClient({
|
|
13324
13969
|
apiKey: params.apiKey,
|
|
13325
|
-
|
|
13326
|
-
apiBaseUrl2: requestConfig.resolvedApiBaseUrl2,
|
|
13970
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
13327
13971
|
headers: requestConfig.headers
|
|
13328
13972
|
}),
|
|
13329
13973
|
requestConfig
|
|
13330
13974
|
};
|
|
13331
13975
|
}
|
|
13332
|
-
function oauthTokenEndpoint(
|
|
13333
|
-
const url = new URL(
|
|
13976
|
+
function oauthTokenEndpoint(apiBaseUrl) {
|
|
13977
|
+
const url = new URL(apiBaseUrl);
|
|
13334
13978
|
url.pathname = "/oauth/token";
|
|
13335
13979
|
url.search = "";
|
|
13336
13980
|
url.hash = "";
|
|
@@ -13378,7 +14022,7 @@ async function refreshStoredCliCredentials(params) {
|
|
|
13378
14022
|
});
|
|
13379
14023
|
let response;
|
|
13380
14024
|
try {
|
|
13381
|
-
response = await fetchImpl(oauthTokenEndpoint(params.
|
|
14025
|
+
response = await fetchImpl(oauthTokenEndpoint(params.apiBaseUrl), {
|
|
13382
14026
|
body,
|
|
13383
14027
|
headers: {
|
|
13384
14028
|
...params.headers ?? {},
|
|
@@ -13418,13 +14062,16 @@ async function createAuthenticatedCliApiClient(params) {
|
|
|
13418
14062
|
const requestConfig = resolveCliApiRequestConfig(params);
|
|
13419
14063
|
let auth = resolveCliAuth({
|
|
13420
14064
|
apiKey: params.apiKey,
|
|
13421
|
-
|
|
13422
|
-
apiBaseUrl2: requestConfig.apiBaseUrl2,
|
|
14065
|
+
apiBaseUrl: requestConfig.apiBaseUrl,
|
|
13423
14066
|
configDir: params.configDir
|
|
13424
14067
|
});
|
|
14068
|
+
if (auth.apiKey === void 0) {
|
|
14069
|
+
const hint = detectPrimitiveKeyEnvMisname(params.env ?? process.env);
|
|
14070
|
+
if (hint) process.stderr.write(`${hint}\n`);
|
|
14071
|
+
}
|
|
13425
14072
|
if (auth.source === "stored" && auth.credentials) {
|
|
13426
14073
|
const refreshed = await refreshStoredCliCredentials({
|
|
13427
|
-
|
|
14074
|
+
apiBaseUrl: auth.apiBaseUrl,
|
|
13428
14075
|
configDir: params.configDir,
|
|
13429
14076
|
credentials: auth.credentials,
|
|
13430
14077
|
credentialsLockHeld: params.credentialsLockHeld,
|
|
@@ -13441,8 +14088,7 @@ async function createAuthenticatedCliApiClient(params) {
|
|
|
13441
14088
|
return {
|
|
13442
14089
|
apiClient: new PrimitiveApiClient({
|
|
13443
14090
|
apiKey: auth.apiKey,
|
|
13444
|
-
|
|
13445
|
-
apiBaseUrl2: auth.apiBaseUrl2,
|
|
14091
|
+
apiBaseUrl: auth.apiBaseUrl,
|
|
13446
14092
|
headers: requestConfig.headers
|
|
13447
14093
|
}),
|
|
13448
14094
|
auth,
|
|
@@ -13758,7 +14404,7 @@ const NETWORK_ERROR_HINTS = {
|
|
|
13758
14404
|
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.",
|
|
13759
14405
|
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.",
|
|
13760
14406
|
ETIMEDOUT: "Hint: the connection timed out. Check egress rules and proxy configuration; if you're behind a proxy, re-run with NODE_USE_ENV_PROXY=1 and HTTPS_PROXY set. `primitive doctor` reports the local environment in one shot.",
|
|
13761
|
-
EAI_AGAIN: "Hint: DNS lookup failed. Check /etc/resolv.conf inside containers, and try `curl -v https://
|
|
14407
|
+
EAI_AGAIN: "Hint: DNS lookup failed. Check /etc/resolv.conf inside containers, and try `curl -v https://api.primitive.dev/v1/account` to confirm the host resolves. `primitive doctor` reports the local environment in one shot."
|
|
13762
14408
|
};
|
|
13763
14409
|
function writeErrorWithHints(payload) {
|
|
13764
14410
|
process.stderr.write(`${formatErrorPayload(payload)}\n`);
|
|
@@ -13787,8 +14433,8 @@ function writeErrorWithHints(payload) {
|
|
|
13787
14433
|
*/
|
|
13788
14434
|
function surfaceUnauthorizedHint(params) {
|
|
13789
14435
|
if (extractErrorCode(params.payload) !== API_ERROR_CODES.unauthorized || params.auth.source !== "stored") return;
|
|
13790
|
-
if (params.baseUrlOverridden && params.auth.credentials !== null && params.auth.
|
|
13791
|
-
process.stderr.write("Saved Primitive CLI credentials were rejected by the overridden API base URL. The saved credential is preserved; unset
|
|
14436
|
+
if (params.baseUrlOverridden && params.auth.credentials !== null && params.auth.apiBaseUrl !== params.auth.credentials.api_base_url) {
|
|
14437
|
+
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, run `primitive config reset` to clear configured URL overrides, or run `primitive logout` to remove the stored credential.\n");
|
|
13792
14438
|
return;
|
|
13793
14439
|
}
|
|
13794
14440
|
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 signin` to mint a fresh session.\n");
|
|
@@ -13809,13 +14455,10 @@ async function runWithTiming(enabled, fn) {
|
|
|
13809
14455
|
}
|
|
13810
14456
|
}
|
|
13811
14457
|
const TIME_FLAG_DESCRIPTION = "Print the wall-clock duration of this command to stderr after it completes (e.g. `[time: 1.34s]`). Useful for measuring `--wait` send latency, comparing CLI overhead, or capturing timing in scripts.";
|
|
13812
|
-
const
|
|
13813
|
-
const API_BASE_URL_2_FLAG_DESCRIPTION = "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.";
|
|
13814
|
-
const HOST_2_OPERATIONS = new Set(["sendEmail", "replyToEmail"]);
|
|
14458
|
+
const API_BASE_URL_FLAG_DESCRIPTION = "Override the API base URL. Internal testing only; not documented to customers.";
|
|
13815
14459
|
const RESERVED_FLAG_NAMES = new Set([
|
|
13816
14460
|
"api-key",
|
|
13817
|
-
"api-base-url
|
|
13818
|
-
"api-base-url-2",
|
|
14461
|
+
"api-base-url",
|
|
13819
14462
|
"raw-body",
|
|
13820
14463
|
"body-file",
|
|
13821
14464
|
"envelope",
|
|
@@ -13847,14 +14490,9 @@ function buildFlags(operation) {
|
|
|
13847
14490
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
13848
14491
|
env: "PRIMITIVE_API_KEY"
|
|
13849
14492
|
}),
|
|
13850
|
-
"api-base-url
|
|
13851
|
-
description: "Override the
|
|
13852
|
-
env: "
|
|
13853
|
-
hidden: true
|
|
13854
|
-
}),
|
|
13855
|
-
"api-base-url-2": Flags.string({
|
|
13856
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
13857
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
14493
|
+
"api-base-url": Flags.string({
|
|
14494
|
+
description: "Override the API base URL. Internal testing only; not documented to customers.",
|
|
14495
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
13858
14496
|
hidden: true
|
|
13859
14497
|
}),
|
|
13860
14498
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
@@ -13942,8 +14580,7 @@ function createOperationCommand(operation) {
|
|
|
13942
14580
|
await runWithTiming(parsedFlags.time === true, async () => {
|
|
13943
14581
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
13944
14582
|
apiKey: typeof parsedFlags["api-key"] === "string" ? parsedFlags["api-key"] : void 0,
|
|
13945
|
-
|
|
13946
|
-
apiBaseUrl2: typeof parsedFlags["api-base-url-2"] === "string" ? parsedFlags["api-base-url-2"] : void 0,
|
|
14583
|
+
apiBaseUrl: typeof parsedFlags["api-base-url"] === "string" ? parsedFlags["api-base-url"] : void 0,
|
|
13947
14584
|
configDir: this.config.configDir
|
|
13948
14585
|
});
|
|
13949
14586
|
let body;
|
|
@@ -13964,10 +14601,9 @@ function createOperationCommand(operation) {
|
|
|
13964
14601
|
}
|
|
13965
14602
|
if (operation.bodyRequired && body === void 0) throw new Errors.CLIError(`Operation ${operation.operationId} requires a body. Pass each field as a --flag (see --help) or supply JSON via --raw-body / --body-file.`);
|
|
13966
14603
|
const operationFn = sdk_gen_exports[operation.sdkName];
|
|
13967
|
-
const targetClient = HOST_2_OPERATIONS.has(operation.sdkName) ? apiClient._sendClient : apiClient.client;
|
|
13968
14604
|
const result = await operationFn({
|
|
13969
14605
|
body,
|
|
13970
|
-
client:
|
|
14606
|
+
client: apiClient.client,
|
|
13971
14607
|
parseAs: operation.binaryResponse ? "blob" : "auto",
|
|
13972
14608
|
path: collectValues(operation.pathParams, parsedFlags),
|
|
13973
14609
|
query: collectValues(operation.queryParams, parsedFlags),
|
|
@@ -14721,14 +15357,9 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14721
15357
|
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive signin` credentials)",
|
|
14722
15358
|
env: "PRIMITIVE_API_KEY"
|
|
14723
15359
|
}),
|
|
14724
|
-
"api-base-url
|
|
15360
|
+
"api-base-url": Flags.string({
|
|
14725
15361
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
14726
|
-
env: "
|
|
14727
|
-
hidden: true
|
|
14728
|
-
}),
|
|
14729
|
-
"api-base-url-2": Flags.string({
|
|
14730
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
14731
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
15362
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
14732
15363
|
hidden: true
|
|
14733
15364
|
}),
|
|
14734
15365
|
from: Flags.string({ description: "Sender address. Defaults to agent@<your-first-verified-outbound-domain>." }),
|
|
@@ -14787,8 +15418,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14787
15418
|
await runWithTiming(flags.time, async () => {
|
|
14788
15419
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
14789
15420
|
apiKey: flags["api-key"],
|
|
14790
|
-
|
|
14791
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
15421
|
+
apiBaseUrl: flags["api-base-url"],
|
|
14792
15422
|
configDir: this.config.configDir
|
|
14793
15423
|
});
|
|
14794
15424
|
const authFailureContext = {
|
|
@@ -14859,7 +15489,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14859
15489
|
from,
|
|
14860
15490
|
...attachments !== void 0 ? { attachments } : {}
|
|
14861
15491
|
},
|
|
14862
|
-
client: apiClient.
|
|
15492
|
+
client: apiClient.client,
|
|
14863
15493
|
path: { id: parentReply.id },
|
|
14864
15494
|
responseStyle: "fields"
|
|
14865
15495
|
}) : await sendEmail({
|
|
@@ -14871,7 +15501,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14871
15501
|
...flags["in-reply-to"] !== void 0 ? { in_reply_to: flags["in-reply-to"] } : {},
|
|
14872
15502
|
...attachments !== void 0 ? { attachments } : {}
|
|
14873
15503
|
},
|
|
14874
|
-
client: apiClient.
|
|
15504
|
+
client: apiClient.client,
|
|
14875
15505
|
responseStyle: "fields"
|
|
14876
15506
|
});
|
|
14877
15507
|
if (sendResult.error) {
|
|
@@ -14996,14 +15626,9 @@ var ChatReplyCommand = class ChatReplyCommand extends Command {
|
|
|
14996
15626
|
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive signin` credentials)",
|
|
14997
15627
|
env: "PRIMITIVE_API_KEY"
|
|
14998
15628
|
}),
|
|
14999
|
-
"api-base-url
|
|
15629
|
+
"api-base-url": Flags.string({
|
|
15000
15630
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15001
|
-
env: "
|
|
15002
|
-
hidden: true
|
|
15003
|
-
}),
|
|
15004
|
-
"api-base-url-2": Flags.string({
|
|
15005
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15006
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
15631
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15007
15632
|
hidden: true
|
|
15008
15633
|
}),
|
|
15009
15634
|
id: Flags.integer({
|
|
@@ -15068,8 +15693,7 @@ var ChatReplyCommand = class ChatReplyCommand extends Command {
|
|
|
15068
15693
|
String(state.local_id)
|
|
15069
15694
|
];
|
|
15070
15695
|
if (flags["api-key"] !== void 0) argv.push("--api-key", flags["api-key"]);
|
|
15071
|
-
if (flags["api-base-url
|
|
15072
|
-
if (flags["api-base-url-2"] !== void 0) argv.push("--api-base-url-2", flags["api-base-url-2"]);
|
|
15696
|
+
if (flags["api-base-url"] !== void 0) argv.push("--api-base-url", flags["api-base-url"]);
|
|
15073
15697
|
if (flags.json) argv.push("--json");
|
|
15074
15698
|
if (flags.quiet) argv.push("--quiet");
|
|
15075
15699
|
for (const attachment of flags.attachment ?? []) argv.push("--attachment", attachment);
|
|
@@ -15184,8 +15808,7 @@ function upsertCliEnvironmentAndClearCredentialsIfSwitched(params) {
|
|
|
15184
15808
|
const previousActiveEnvironment = resolveConfigEnvironment(previousConfig);
|
|
15185
15809
|
const previousEnvironment = previousActiveEnvironment?.name ?? null;
|
|
15186
15810
|
const config = upsertCliEnvironment({
|
|
15187
|
-
|
|
15188
|
-
apiBaseUrl2: params.apiBaseUrl2,
|
|
15811
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
15189
15812
|
config: previousConfig,
|
|
15190
15813
|
environmentName: params.environmentName,
|
|
15191
15814
|
headers: params.headers,
|
|
@@ -15193,7 +15816,7 @@ function upsertCliEnvironmentAndClearCredentialsIfSwitched(params) {
|
|
|
15193
15816
|
});
|
|
15194
15817
|
const activeEnvironment = resolveConfigEnvironment(config);
|
|
15195
15818
|
const environment = activeEnvironment?.name ?? null;
|
|
15196
|
-
const shouldClearCredentials = existsSync(credentialsPath(params.configDir)) && (previousEnvironment !== environment || previousActiveEnvironment?.config.
|
|
15819
|
+
const shouldClearCredentials = existsSync(credentialsPath(params.configDir)) && (previousEnvironment !== environment || previousActiveEnvironment?.config.api_base_url !== activeEnvironment?.config.api_base_url);
|
|
15197
15820
|
let removedCredentials = false;
|
|
15198
15821
|
if (shouldClearCredentials) {
|
|
15199
15822
|
const releaseLock = acquireCliCredentialsLock(params.configDir);
|
|
@@ -15243,10 +15866,9 @@ var ConfigSetCommand = class ConfigSetCommand extends Command {
|
|
|
15243
15866
|
static flags = {
|
|
15244
15867
|
environment: Flags.string({
|
|
15245
15868
|
char: "e",
|
|
15246
|
-
description: "Environment name to create or update"
|
|
15869
|
+
description: "Environment name to create or update. Defaults to the active environment, or default when none is active."
|
|
15247
15870
|
}),
|
|
15248
|
-
"api-base-url
|
|
15249
|
-
"api-base-url-2": Flags.string({ description: "Attachments-supporting API base URL" }),
|
|
15871
|
+
"api-base-url": Flags.string({ description: "API base URL" }),
|
|
15250
15872
|
header: Flags.string({
|
|
15251
15873
|
description: "Request header in name=value form. Repeatable.",
|
|
15252
15874
|
multiple: true
|
|
@@ -15259,10 +15881,9 @@ var ConfigSetCommand = class ConfigSetCommand extends Command {
|
|
|
15259
15881
|
async run() {
|
|
15260
15882
|
const { flags } = await this.parse(ConfigSetCommand);
|
|
15261
15883
|
const headers = flags.header ?? [];
|
|
15262
|
-
if (flags["api-base-url
|
|
15884
|
+
if (flags["api-base-url"] === void 0 && headers.length === 0 && (flags["unset-header"] ?? []).length === 0) throw new Errors.CLIError("Nothing to set. Pass an API base URL, --header, or --unset-header.", { exit: 1 });
|
|
15263
15885
|
const { environment, removedCredentials } = upsertCliEnvironmentAndClearCredentialsIfSwitched({
|
|
15264
|
-
|
|
15265
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
15886
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15266
15887
|
configDir: this.config.configDir,
|
|
15267
15888
|
environmentName: flags.environment,
|
|
15268
15889
|
headers,
|
|
@@ -15310,8 +15931,7 @@ var ConfigListCommand = class ConfigListCommand extends Command {
|
|
|
15310
15931
|
const active = activeEnvironment === name ? "*" : " ";
|
|
15311
15932
|
const headerNames = Object.keys(environment.headers ?? {});
|
|
15312
15933
|
this.log(`${active} ${name}`);
|
|
15313
|
-
if (environment.
|
|
15314
|
-
if (environment.api_base_url_2) this.log(` api_base_url_2: ${environment.api_base_url_2}`);
|
|
15934
|
+
if (environment.api_base_url) this.log(` api_base_url: ${environment.api_base_url}`);
|
|
15315
15935
|
this.log(` headers: ${headerNames.length > 0 ? headerNames.join(", ") : "(none)"}`);
|
|
15316
15936
|
}
|
|
15317
15937
|
}
|
|
@@ -15408,9 +16028,7 @@ function checkApiKey(opts) {
|
|
|
15408
16028
|
message: "provided but does not start with prim_",
|
|
15409
16029
|
hint: "Verify the key is a Primitive API key, not a value from another service."
|
|
15410
16030
|
};
|
|
15411
|
-
|
|
15412
|
-
const primitiveApiKey = env.PRIMITIVE_API_KEY;
|
|
15413
|
-
if ((primitiveKey?.length ?? 0) > 0 && (primitiveApiKey?.length ?? 0) === 0) return {
|
|
16031
|
+
if (detectPrimitiveKeyEnvMisname(env)) return {
|
|
15414
16032
|
status: "fail",
|
|
15415
16033
|
message: "PRIMITIVE_KEY is set but the CLI reads PRIMITIVE_API_KEY",
|
|
15416
16034
|
hint: "Rename your env var, or re-run with PRIMITIVE_API_KEY=$PRIMITIVE_KEY."
|
|
@@ -15483,7 +16101,7 @@ async function checkAccount(client) {
|
|
|
15483
16101
|
} catch (error) {
|
|
15484
16102
|
const code = error instanceof Error && error.cause && typeof error.cause === "object" && typeof error.cause.code === "string" ? error.cause.code : void 0;
|
|
15485
16103
|
const message = error instanceof Error ? error.message : String(error);
|
|
15486
|
-
const hint = code === "ENETUNREACH" || code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EAI_AGAIN" ? "Network unreachable. If you're behind a proxy, re-run with NODE_USE_ENV_PROXY=1 and HTTPS_PROXY set. If you're in a container, check that egress to *.primitive.dev is allowed." : "Inspect the error above. `curl https://
|
|
16104
|
+
const hint = code === "ENETUNREACH" || code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EAI_AGAIN" ? "Network unreachable. If you're behind a proxy, re-run with NODE_USE_ENV_PROXY=1 and HTTPS_PROXY set. If you're in a container, check that egress to *.primitive.dev is allowed." : "Inspect the error above. `curl https://api.primitive.dev/v1/account -H \"Authorization: Bearer $PRIMITIVE_API_KEY\"` is the fastest way to bisect CLI vs network.";
|
|
15487
16105
|
return {
|
|
15488
16106
|
outcome: {
|
|
15489
16107
|
status: "fail",
|
|
@@ -15537,14 +16155,9 @@ var DoctorCommand = class DoctorCommand extends Command {
|
|
|
15537
16155
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15538
16156
|
env: "PRIMITIVE_API_KEY"
|
|
15539
16157
|
}),
|
|
15540
|
-
"api-base-url
|
|
16158
|
+
"api-base-url": Flags.string({
|
|
15541
16159
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15542
|
-
env: "
|
|
15543
|
-
hidden: true
|
|
15544
|
-
}),
|
|
15545
|
-
"api-base-url-2": Flags.string({
|
|
15546
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15547
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
16160
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15548
16161
|
hidden: true
|
|
15549
16162
|
})
|
|
15550
16163
|
};
|
|
@@ -15570,8 +16183,7 @@ var DoctorCommand = class DoctorCommand extends Command {
|
|
|
15570
16183
|
if (apiKeyCheck.status !== "fail") {
|
|
15571
16184
|
const { apiClient, auth } = await createAuthenticatedCliApiClient({
|
|
15572
16185
|
apiKey: flags["api-key"],
|
|
15573
|
-
|
|
15574
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
16186
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15575
16187
|
configDir: this.config.configDir
|
|
15576
16188
|
});
|
|
15577
16189
|
if (auth.apiKey !== void 0) {
|
|
@@ -15657,14 +16269,9 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
15657
16269
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15658
16270
|
env: "PRIMITIVE_API_KEY"
|
|
15659
16271
|
}),
|
|
15660
|
-
"api-base-url
|
|
16272
|
+
"api-base-url": Flags.string({
|
|
15661
16273
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15662
|
-
env: "
|
|
15663
|
-
hidden: true
|
|
15664
|
-
}),
|
|
15665
|
-
"api-base-url-2": Flags.string({
|
|
15666
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15667
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
16274
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15668
16275
|
hidden: true
|
|
15669
16276
|
}),
|
|
15670
16277
|
domain: Flags.string({ description: "Domain name to look up before downloading its zone file. Prefer --id when you have the domain id from `primitive domains add`." }),
|
|
@@ -15683,8 +16290,7 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
15683
16290
|
await runWithTiming(flags.time, async () => {
|
|
15684
16291
|
const { apiClient, auth, baseUrlOverridden, requestConfig } = await createAuthenticatedCliApiClient({
|
|
15685
16292
|
apiKey: flags["api-key"],
|
|
15686
|
-
|
|
15687
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
16293
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15688
16294
|
configDir: this.config.configDir
|
|
15689
16295
|
});
|
|
15690
16296
|
let domainId = flags.id;
|
|
@@ -15711,7 +16317,7 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
15711
16317
|
if (!domainId) throw new Errors.CLIError("Could not resolve a domain id.", { exit: 1 });
|
|
15712
16318
|
let response;
|
|
15713
16319
|
try {
|
|
15714
|
-
response = await fetch(zoneFileUrl(requestConfig.
|
|
16320
|
+
response = await fetch(zoneFileUrl(requestConfig.resolvedApiBaseUrl, domainId, flags["outbound-only"]), { headers: {
|
|
15715
16321
|
...requestConfig.headers ?? {},
|
|
15716
16322
|
...auth.apiKey ? { authorization: `Bearer ${auth.apiKey}` } : {}
|
|
15717
16323
|
} });
|
|
@@ -15746,14 +16352,14 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
15746
16352
|
};
|
|
15747
16353
|
//#endregion
|
|
15748
16354
|
//#region src/oclif/commands/emails-latest.ts
|
|
15749
|
-
const DEFAULT_LIMIT = 10;
|
|
15750
|
-
const MAX_LIMIT = 100;
|
|
16355
|
+
const DEFAULT_LIMIT$1 = 10;
|
|
16356
|
+
const MAX_LIMIT$1 = 100;
|
|
15751
16357
|
const SUBJECT_DISPLAY_WIDTH = 50;
|
|
15752
16358
|
const ADDRESS_DISPLAY_WIDTH = 32;
|
|
15753
16359
|
const ID_DISPLAY_WIDTH_SHORT = 8;
|
|
15754
16360
|
const ID_DISPLAY_WIDTH_FULL = 36;
|
|
15755
16361
|
const RECEIVED_DISPLAY_WIDTH = 19;
|
|
15756
|
-
function truncate$
|
|
16362
|
+
function truncate$2(value, width) {
|
|
15757
16363
|
if (value.length <= width) return value.padEnd(width);
|
|
15758
16364
|
return `${value.slice(0, width - 3)}...`;
|
|
15759
16365
|
}
|
|
@@ -15767,10 +16373,10 @@ function formatReceivedAt(value) {
|
|
|
15767
16373
|
function pickIdWidth(isTty) {
|
|
15768
16374
|
return isTty ? ID_DISPLAY_WIDTH_SHORT : ID_DISPLAY_WIDTH_FULL;
|
|
15769
16375
|
}
|
|
15770
|
-
function formatRow(email, idWidth) {
|
|
15771
|
-
return `${truncate$
|
|
16376
|
+
function formatRow$1(email, idWidth) {
|
|
16377
|
+
return `${truncate$2(email.id.slice(0, idWidth), idWidth)} ${formatReceivedAt(email.received_at)} ${truncate$2(email.sender ?? "", ADDRESS_DISPLAY_WIDTH)} ${truncate$2(email.recipient ?? "", ADDRESS_DISPLAY_WIDTH)} ${truncate$2((email.subject ?? "").replace(/\s+/g, " "), SUBJECT_DISPLAY_WIDTH)}`;
|
|
15772
16378
|
}
|
|
15773
|
-
function formatHeader(idWidth) {
|
|
16379
|
+
function formatHeader$1(idWidth) {
|
|
15774
16380
|
return `${"ID".padEnd(idWidth)} ${"RECEIVED (UTC)".padEnd(RECEIVED_DISPLAY_WIDTH)} ${"FROM".padEnd(ADDRESS_DISPLAY_WIDTH)} ${"TO".padEnd(ADDRESS_DISPLAY_WIDTH)} SUBJECT`;
|
|
15775
16381
|
}
|
|
15776
16382
|
var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
@@ -15791,21 +16397,16 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
15791
16397
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15792
16398
|
env: "PRIMITIVE_API_KEY"
|
|
15793
16399
|
}),
|
|
15794
|
-
"api-base-url
|
|
16400
|
+
"api-base-url": Flags.string({
|
|
15795
16401
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15796
|
-
env: "
|
|
15797
|
-
hidden: true
|
|
15798
|
-
}),
|
|
15799
|
-
"api-base-url-2": Flags.string({
|
|
15800
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15801
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
16402
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15802
16403
|
hidden: true
|
|
15803
16404
|
}),
|
|
15804
16405
|
limit: Flags.integer({
|
|
15805
|
-
description: `Number of rows to print (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}).`,
|
|
15806
|
-
default: DEFAULT_LIMIT,
|
|
16406
|
+
description: `Number of rows to print (1-${MAX_LIMIT$1}, default ${DEFAULT_LIMIT$1}).`,
|
|
16407
|
+
default: DEFAULT_LIMIT$1,
|
|
15807
16408
|
min: 1,
|
|
15808
|
-
max: MAX_LIMIT
|
|
16409
|
+
max: MAX_LIMIT$1
|
|
15809
16410
|
}),
|
|
15810
16411
|
json: Flags.boolean({ description: "Print the raw response envelope (with full UUIDs and meta) as JSON on STDOUT instead of the text table. Useful for piping into `jq`, capturing ids for follow-up commands, or scripting." }),
|
|
15811
16412
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
@@ -15815,8 +16416,7 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
15815
16416
|
await runWithTiming(flags.time, async () => {
|
|
15816
16417
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
15817
16418
|
apiKey: flags["api-key"],
|
|
15818
|
-
|
|
15819
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
16419
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15820
16420
|
configDir: this.config.configDir
|
|
15821
16421
|
});
|
|
15822
16422
|
const result = await listEmails({
|
|
@@ -15847,8 +16447,8 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
15847
16447
|
return;
|
|
15848
16448
|
}
|
|
15849
16449
|
const idWidth = pickIdWidth(Boolean(process.stdout.isTTY));
|
|
15850
|
-
process.stderr.write(`${formatHeader(idWidth)}\n`);
|
|
15851
|
-
for (const row of rows) this.log(formatRow(row, idWidth));
|
|
16450
|
+
process.stderr.write(`${formatHeader$1(idWidth)}\n`);
|
|
16451
|
+
for (const row of rows) this.log(formatRow$1(row, idWidth));
|
|
15852
16452
|
});
|
|
15853
16453
|
}
|
|
15854
16454
|
};
|
|
@@ -15871,14 +16471,9 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
15871
16471
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15872
16472
|
env: "PRIMITIVE_API_KEY"
|
|
15873
16473
|
}),
|
|
15874
|
-
"api-base-url
|
|
16474
|
+
"api-base-url": Flags.string({
|
|
15875
16475
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15876
|
-
env: "
|
|
15877
|
-
hidden: true
|
|
15878
|
-
}),
|
|
15879
|
-
"api-base-url-2": Flags.string({
|
|
15880
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15881
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
16476
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15882
16477
|
hidden: true
|
|
15883
16478
|
}),
|
|
15884
16479
|
body: Flags.string({ description: "Full-text body filter" }),
|
|
@@ -15922,8 +16517,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
15922
16517
|
const { flags } = await this.parse(EmailsWaitCommand);
|
|
15923
16518
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
15924
16519
|
apiKey: flags["api-key"],
|
|
15925
|
-
|
|
15926
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
16520
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15927
16521
|
configDir: this.config.configDir
|
|
15928
16522
|
});
|
|
15929
16523
|
let since;
|
|
@@ -15965,10 +16559,10 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
15965
16559
|
for (const email of collectNewAcceptedEmails(page.rows, seenIds)) {
|
|
15966
16560
|
if (flags.table) {
|
|
15967
16561
|
if (!headerPrinted) {
|
|
15968
|
-
process.stderr.write(`${formatHeader(idWidth)}\n`);
|
|
16562
|
+
process.stderr.write(`${formatHeader$1(idWidth)}\n`);
|
|
15969
16563
|
headerPrinted = true;
|
|
15970
16564
|
}
|
|
15971
|
-
this.log(formatRow(email, idWidth));
|
|
16565
|
+
this.log(formatRow$1(email, idWidth));
|
|
15972
16566
|
} else this.log(JSON.stringify(email));
|
|
15973
16567
|
matched += 1;
|
|
15974
16568
|
if (matched >= flags.number) return;
|
|
@@ -15999,14 +16593,9 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
15999
16593
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
16000
16594
|
env: "PRIMITIVE_API_KEY"
|
|
16001
16595
|
}),
|
|
16002
|
-
"api-base-url
|
|
16596
|
+
"api-base-url": Flags.string({
|
|
16003
16597
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
16004
|
-
env: "
|
|
16005
|
-
hidden: true
|
|
16006
|
-
}),
|
|
16007
|
-
"api-base-url-2": Flags.string({
|
|
16008
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
16009
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
16598
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
16010
16599
|
hidden: true
|
|
16011
16600
|
}),
|
|
16012
16601
|
body: Flags.string({ description: "Full-text body filter" }),
|
|
@@ -16046,8 +16635,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
16046
16635
|
const { flags } = await this.parse(EmailsWatchCommand);
|
|
16047
16636
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
16048
16637
|
apiKey: flags["api-key"],
|
|
16049
|
-
|
|
16050
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
16638
|
+
apiBaseUrl: flags["api-base-url"],
|
|
16051
16639
|
configDir: this.config.configDir
|
|
16052
16640
|
});
|
|
16053
16641
|
let since;
|
|
@@ -16090,10 +16678,10 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
16090
16678
|
if (flags.jsonl) this.log(JSON.stringify(email));
|
|
16091
16679
|
else {
|
|
16092
16680
|
if (!headerPrinted) {
|
|
16093
|
-
process.stderr.write(`${formatHeader(idWidth)}\n`);
|
|
16681
|
+
process.stderr.write(`${formatHeader$1(idWidth)}\n`);
|
|
16094
16682
|
headerPrinted = true;
|
|
16095
16683
|
}
|
|
16096
|
-
this.log(formatRow(email, idWidth));
|
|
16684
|
+
this.log(formatRow$1(email, idWidth));
|
|
16097
16685
|
}
|
|
16098
16686
|
printed += 1;
|
|
16099
16687
|
if (flags.number && printed >= flags.number) return;
|
|
@@ -16667,6 +17255,18 @@ const SECRET_SOURCE_FLAGS_DESCRIPTION = "Safe sources: --secret-from-env KEY rea
|
|
|
16667
17255
|
const SINGLE_SECRET_VALUE_SOURCE_DESCRIPTION = "Instead of --value, use --value-from-env ENV_VAR, --value-file PATH, --value-from-env-file FILE[:KEY], or --stdin to avoid putting the secret value in shell history or process argv. If KEY is omitted from --value-from-env-file, the command's --key is used.";
|
|
16668
17256
|
//#endregion
|
|
16669
17257
|
//#region src/oclif/commands/functions-deploy.ts
|
|
17258
|
+
async function writeRouteStatusHint(apiClient, functionId) {
|
|
17259
|
+
try {
|
|
17260
|
+
const result = await getFunctionRouting({
|
|
17261
|
+
client: apiClient.client,
|
|
17262
|
+
path: { id: functionId },
|
|
17263
|
+
responseStyle: "fields"
|
|
17264
|
+
});
|
|
17265
|
+
if (result.error) return;
|
|
17266
|
+
if (result.data?.data) process.stderr.write("Route bound. Function will receive inbound mail.\n");
|
|
17267
|
+
else process.stderr.write(`Deployed but no route is bound. Inbound mail will not reach this function until you bind one: primitive functions route-set --id ${functionId} --domain <domain-id> (or --fallback)\n`);
|
|
17268
|
+
} catch {}
|
|
17269
|
+
}
|
|
16670
17270
|
async function runDeployWithSecrets(api, params) {
|
|
16671
17271
|
const createResult = await api.createFunction({
|
|
16672
17272
|
code: params.code,
|
|
@@ -16792,14 +17392,9 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
16792
17392
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
16793
17393
|
env: "PRIMITIVE_API_KEY"
|
|
16794
17394
|
}),
|
|
16795
|
-
"api-base-url
|
|
17395
|
+
"api-base-url": Flags.string({
|
|
16796
17396
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
16797
|
-
env: "
|
|
16798
|
-
hidden: true
|
|
16799
|
-
}),
|
|
16800
|
-
"api-base-url-2": Flags.string({
|
|
16801
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
16802
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
17397
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
16803
17398
|
hidden: true
|
|
16804
17399
|
}),
|
|
16805
17400
|
name: Flags.string({
|
|
@@ -16877,8 +17472,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
16877
17472
|
emitRawSendMailFetchWarning(code, (chunk) => process.stderr.write(chunk));
|
|
16878
17473
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
16879
17474
|
apiKey: flags["api-key"],
|
|
16880
|
-
|
|
16881
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
17475
|
+
apiBaseUrl: flags["api-base-url"],
|
|
16882
17476
|
configDir: this.config.configDir
|
|
16883
17477
|
});
|
|
16884
17478
|
const authFailureContext = {
|
|
@@ -16972,10 +17566,11 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
16972
17566
|
const detail = waitResult.function.deploy_error ? `: ${waitResult.function.deploy_error}` : ".";
|
|
16973
17567
|
process.stderr.write(`Function ${payload.id} deploy failed${detail}\n`);
|
|
16974
17568
|
process.exitCode = 1;
|
|
16975
|
-
}
|
|
17569
|
+
} else await writeRouteStatusHint(apiClient, payload.id);
|
|
16976
17570
|
return;
|
|
16977
17571
|
}
|
|
16978
17572
|
this.log(JSON.stringify(payload, null, 2));
|
|
17573
|
+
await writeRouteStatusHint(apiClient, payload.id);
|
|
16979
17574
|
});
|
|
16980
17575
|
}
|
|
16981
17576
|
async runSourceMode(flags, sourceDir) {
|
|
@@ -16992,8 +17587,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
16992
17587
|
}
|
|
16993
17588
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
16994
17589
|
apiKey: flags["api-key"],
|
|
16995
|
-
|
|
16996
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
17590
|
+
apiBaseUrl: flags["api-base-url"],
|
|
16997
17591
|
configDir: this.config.configDir
|
|
16998
17592
|
});
|
|
16999
17593
|
const authFailureContext = {
|
|
@@ -17068,10 +17662,11 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
17068
17662
|
const detail = waitResult.function.deploy_error ? `: ${waitResult.function.deploy_error}` : ".";
|
|
17069
17663
|
process.stderr.write(`Function ${payload.id} deploy failed${detail}\n`);
|
|
17070
17664
|
process.exitCode = 1;
|
|
17071
|
-
}
|
|
17665
|
+
} else await writeRouteStatusHint(apiClient, payload.id);
|
|
17072
17666
|
return;
|
|
17073
17667
|
}
|
|
17074
17668
|
this.log(JSON.stringify(payload, null, 2));
|
|
17669
|
+
await writeRouteStatusHint(apiClient, payload.id);
|
|
17075
17670
|
}
|
|
17076
17671
|
};
|
|
17077
17672
|
//#endregion
|
|
@@ -17082,8 +17677,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
17082
17677
|
name: "Primitive Team",
|
|
17083
17678
|
url: "https://primitive.dev"
|
|
17084
17679
|
};
|
|
17085
|
-
const SDK_VERSION_RANGE = "^0.
|
|
17086
|
-
const CLI_VERSION_RANGE = "^0.
|
|
17680
|
+
const SDK_VERSION_RANGE = "^0.36.0";
|
|
17681
|
+
const CLI_VERSION_RANGE = "^0.36.0";
|
|
17087
17682
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
17088
17683
|
function renderHandler() {
|
|
17089
17684
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -17240,7 +17835,7 @@ export default {
|
|
|
17240
17835
|
|
|
17241
17836
|
const client = createPrimitiveClient({
|
|
17242
17837
|
apiKey: env.PRIMITIVE_API_KEY,
|
|
17243
|
-
|
|
17838
|
+
apiBaseUrl: env.PRIMITIVE_API_BASE_URL,
|
|
17244
17839
|
});
|
|
17245
17840
|
|
|
17246
17841
|
// To add an LLM or another API, store its key as a Function secret.
|
|
@@ -17264,7 +17859,7 @@ export default {
|
|
|
17264
17859
|
// route "support@" to a ticketing flow and "sales@" to a lead
|
|
17265
17860
|
// capture flow before calling client.reply.
|
|
17266
17861
|
|
|
17267
|
-
// client.reply routes through POST /
|
|
17862
|
+
// client.reply routes through POST /v1/emails/{id}/reply
|
|
17268
17863
|
// (NOT /send-mail) so the server derives recipients, the
|
|
17269
17864
|
// \`Re: <parent>\` subject, threading headers, and the
|
|
17270
17865
|
// in_reply_to_email_id foreign key automatically. The FK is
|
|
@@ -17390,6 +17985,23 @@ After the first deploy, copy the returned function id into your shell:
|
|
|
17390
17985
|
export PRIMITIVE_FUNCTION_ID=<fn-id>
|
|
17391
17986
|
\`\`\`
|
|
17392
17987
|
|
|
17988
|
+
## Bind a route
|
|
17989
|
+
|
|
17990
|
+
A deployed Function does not receive inbound mail until a route is
|
|
17991
|
+
bound to it. Without this step, the Function is installed in the
|
|
17992
|
+
runtime but unreachable, and \`functions test\` will refuse to send
|
|
17993
|
+
(returns 422 \`no_endpoint\`).
|
|
17994
|
+
|
|
17995
|
+
\`\`\`
|
|
17996
|
+
primitive functions route-set --id "$PRIMITIVE_FUNCTION_ID" --domain <domain-id>
|
|
17997
|
+
\`\`\`
|
|
17998
|
+
|
|
17999
|
+
Use \`--fallback\` instead of \`--domain\` to bind the Function as the
|
|
18000
|
+
org-wide fallback for any active domain that has no scoped binding.
|
|
18001
|
+
Run \`primitive functions route-get --id "$PRIMITIVE_FUNCTION_ID"\` to
|
|
18002
|
+
inspect the current binding, or \`primitive functions routing-topology\`
|
|
18003
|
+
for the org-wide view of which domain points at which Function.
|
|
18004
|
+
|
|
17393
18005
|
## Prove it works
|
|
17394
18006
|
|
|
17395
18007
|
\`\`\`
|
|
@@ -17645,14 +18257,9 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
|
|
|
17645
18257
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17646
18258
|
env: "PRIMITIVE_API_KEY"
|
|
17647
18259
|
}),
|
|
17648
|
-
"api-base-url
|
|
18260
|
+
"api-base-url": Flags.string({
|
|
17649
18261
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
17650
|
-
env: "
|
|
17651
|
-
hidden: true
|
|
17652
|
-
}),
|
|
17653
|
-
"api-base-url-2": Flags.string({
|
|
17654
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
17655
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
18262
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
17656
18263
|
hidden: true
|
|
17657
18264
|
}),
|
|
17658
18265
|
id: Flags.string({
|
|
@@ -17683,8 +18290,7 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
|
|
|
17683
18290
|
await runWithTiming(flags.time, async () => {
|
|
17684
18291
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
17685
18292
|
apiKey: flags["api-key"],
|
|
17686
|
-
|
|
17687
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
18293
|
+
apiBaseUrl: flags["api-base-url"],
|
|
17688
18294
|
configDir: this.config.configDir
|
|
17689
18295
|
});
|
|
17690
18296
|
const seenIds = /* @__PURE__ */ new Set();
|
|
@@ -17843,14 +18449,9 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
17843
18449
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17844
18450
|
env: "PRIMITIVE_API_KEY"
|
|
17845
18451
|
}),
|
|
17846
|
-
"api-base-url
|
|
18452
|
+
"api-base-url": Flags.string({
|
|
17847
18453
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
17848
|
-
env: "
|
|
17849
|
-
hidden: true
|
|
17850
|
-
}),
|
|
17851
|
-
"api-base-url-2": Flags.string({
|
|
17852
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
17853
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
18454
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
17854
18455
|
hidden: true
|
|
17855
18456
|
}),
|
|
17856
18457
|
id: Flags.string({
|
|
@@ -17919,8 +18520,7 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
17919
18520
|
emitRawSendMailFetchWarning(code, (chunk) => process.stderr.write(chunk));
|
|
17920
18521
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
17921
18522
|
apiKey: flags["api-key"],
|
|
17922
|
-
|
|
17923
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
18523
|
+
apiBaseUrl: flags["api-base-url"],
|
|
17924
18524
|
configDir: this.config.configDir
|
|
17925
18525
|
});
|
|
17926
18526
|
const authFailureContext = {
|
|
@@ -18012,6 +18612,239 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
18012
18612
|
}
|
|
18013
18613
|
};
|
|
18014
18614
|
//#endregion
|
|
18615
|
+
//#region src/oclif/commands/functions-route-get.ts
|
|
18616
|
+
var FunctionsRouteGetCommand = class FunctionsRouteGetCommand extends Command {
|
|
18617
|
+
static description = `Show the current route binding for a function. Returns the binding (domain or fallback, with delivery counters) or null when no route is bound.`;
|
|
18618
|
+
static summary = "Show a function's current route binding";
|
|
18619
|
+
static examples = ["<%= config.bin %> functions route-get --id <fn-id>"];
|
|
18620
|
+
static flags = {
|
|
18621
|
+
"api-key": Flags.string({
|
|
18622
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18623
|
+
env: "PRIMITIVE_API_KEY"
|
|
18624
|
+
}),
|
|
18625
|
+
"api-base-url": Flags.string({
|
|
18626
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
18627
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18628
|
+
hidden: true
|
|
18629
|
+
}),
|
|
18630
|
+
id: Flags.string({
|
|
18631
|
+
description: "Function id (UUID) whose route binding to show.",
|
|
18632
|
+
required: true
|
|
18633
|
+
}),
|
|
18634
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
18635
|
+
};
|
|
18636
|
+
async run() {
|
|
18637
|
+
const { flags } = await this.parse(FunctionsRouteGetCommand);
|
|
18638
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18639
|
+
apiKey: flags["api-key"],
|
|
18640
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18641
|
+
configDir: this.config.configDir
|
|
18642
|
+
});
|
|
18643
|
+
await runWithTiming(flags.time, async () => {
|
|
18644
|
+
const result = await getFunctionRouting({
|
|
18645
|
+
client: apiClient.client,
|
|
18646
|
+
path: { id: flags.id },
|
|
18647
|
+
responseStyle: "fields"
|
|
18648
|
+
});
|
|
18649
|
+
if (result.error) {
|
|
18650
|
+
const payload = extractErrorPayload(result.error);
|
|
18651
|
+
writeErrorWithHints(payload);
|
|
18652
|
+
surfaceUnauthorizedHint({
|
|
18653
|
+
auth,
|
|
18654
|
+
baseUrlOverridden,
|
|
18655
|
+
configDir: this.config.configDir,
|
|
18656
|
+
payload
|
|
18657
|
+
});
|
|
18658
|
+
process.exitCode = 1;
|
|
18659
|
+
return;
|
|
18660
|
+
}
|
|
18661
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
18662
|
+
});
|
|
18663
|
+
}
|
|
18664
|
+
};
|
|
18665
|
+
//#endregion
|
|
18666
|
+
//#region src/oclif/commands/functions-route-set.ts
|
|
18667
|
+
var FunctionsRouteSetCommand = class FunctionsRouteSetCommand extends Command {
|
|
18668
|
+
static description = `Bind inbound mail to a function by setting its route target.
|
|
18669
|
+
|
|
18670
|
+
Exactly one of --domain or --fallback is required. --domain scopes the
|
|
18671
|
+
binding to a single verified inbound domain. --fallback binds the
|
|
18672
|
+
function to any active domain that has no scoped binding of its own.
|
|
18673
|
+
|
|
18674
|
+
If another function is already bound at the target, the API returns a
|
|
18675
|
+
conflict envelope rather than overwriting; re-run with --takeover to
|
|
18676
|
+
deactivate the prior binding before installing this one.`;
|
|
18677
|
+
static summary = "Bind inbound mail to a function";
|
|
18678
|
+
static examples = [
|
|
18679
|
+
"<%= config.bin %> functions route-set --id <fn-id> --domain <domain-id>",
|
|
18680
|
+
"<%= config.bin %> functions route-set --id <fn-id> --fallback",
|
|
18681
|
+
"<%= config.bin %> functions route-set --id <fn-id> --domain <domain-id> --takeover"
|
|
18682
|
+
];
|
|
18683
|
+
static flags = {
|
|
18684
|
+
"api-key": Flags.string({
|
|
18685
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18686
|
+
env: "PRIMITIVE_API_KEY"
|
|
18687
|
+
}),
|
|
18688
|
+
"api-base-url": Flags.string({
|
|
18689
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
18690
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18691
|
+
hidden: true
|
|
18692
|
+
}),
|
|
18693
|
+
id: Flags.string({
|
|
18694
|
+
description: "Function id (UUID) to bind a route to.",
|
|
18695
|
+
required: true
|
|
18696
|
+
}),
|
|
18697
|
+
domain: Flags.string({
|
|
18698
|
+
description: "Verified inbound domain id (UUID) to scope this function to. Mutually exclusive with --fallback.",
|
|
18699
|
+
exclusive: ["fallback"]
|
|
18700
|
+
}),
|
|
18701
|
+
fallback: Flags.boolean({
|
|
18702
|
+
description: "Bind this function as the org fallback (any active domain without a scoped binding). Mutually exclusive with --domain.",
|
|
18703
|
+
exclusive: ["domain"]
|
|
18704
|
+
}),
|
|
18705
|
+
takeover: Flags.boolean({ description: "Deactivate any conflicting binding before installing this one. Without this flag, the API returns a `conflict` envelope when another function is already bound at the target." }),
|
|
18706
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
18707
|
+
};
|
|
18708
|
+
async run() {
|
|
18709
|
+
const { flags } = await this.parse(FunctionsRouteSetCommand);
|
|
18710
|
+
if (!flags.domain && !flags.fallback) {
|
|
18711
|
+
process.stderr.write("Provide exactly one of --domain (scoped binding) or --fallback (org fallback).\n");
|
|
18712
|
+
process.exitCode = 1;
|
|
18713
|
+
return;
|
|
18714
|
+
}
|
|
18715
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18716
|
+
apiKey: flags["api-key"],
|
|
18717
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18718
|
+
configDir: this.config.configDir
|
|
18719
|
+
});
|
|
18720
|
+
await runWithTiming(flags.time, async () => {
|
|
18721
|
+
const target = flags.domain ? {
|
|
18722
|
+
kind: "domain",
|
|
18723
|
+
domainId: flags.domain
|
|
18724
|
+
} : { kind: "fallback" };
|
|
18725
|
+
const result = await setFunctionRoute({
|
|
18726
|
+
client: apiClient.client,
|
|
18727
|
+
path: { id: flags.id },
|
|
18728
|
+
body: {
|
|
18729
|
+
target,
|
|
18730
|
+
...flags.takeover ? { takeover: true } : {}
|
|
18731
|
+
},
|
|
18732
|
+
responseStyle: "fields"
|
|
18733
|
+
});
|
|
18734
|
+
if (result.error) {
|
|
18735
|
+
const payload = extractErrorPayload(result.error);
|
|
18736
|
+
writeErrorWithHints(payload);
|
|
18737
|
+
surfaceUnauthorizedHint({
|
|
18738
|
+
auth,
|
|
18739
|
+
baseUrlOverridden,
|
|
18740
|
+
configDir: this.config.configDir,
|
|
18741
|
+
payload
|
|
18742
|
+
});
|
|
18743
|
+
process.exitCode = 1;
|
|
18744
|
+
return;
|
|
18745
|
+
}
|
|
18746
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
18747
|
+
});
|
|
18748
|
+
}
|
|
18749
|
+
};
|
|
18750
|
+
//#endregion
|
|
18751
|
+
//#region src/oclif/commands/functions-route-unset.ts
|
|
18752
|
+
var FunctionsRouteUnsetCommand = class FunctionsRouteUnsetCommand extends Command {
|
|
18753
|
+
static description = `Unbind every active route from a function. The function stays deployed but stops receiving inbound mail. Safe to call when no route is currently bound.`;
|
|
18754
|
+
static summary = "Unbind any route from a function";
|
|
18755
|
+
static examples = ["<%= config.bin %> functions route-unset --id <fn-id>"];
|
|
18756
|
+
static flags = {
|
|
18757
|
+
"api-key": Flags.string({
|
|
18758
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18759
|
+
env: "PRIMITIVE_API_KEY"
|
|
18760
|
+
}),
|
|
18761
|
+
"api-base-url": Flags.string({
|
|
18762
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
18763
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18764
|
+
hidden: true
|
|
18765
|
+
}),
|
|
18766
|
+
id: Flags.string({
|
|
18767
|
+
description: "Function id (UUID) whose routes should be unbound.",
|
|
18768
|
+
required: true
|
|
18769
|
+
}),
|
|
18770
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
18771
|
+
};
|
|
18772
|
+
async run() {
|
|
18773
|
+
const { flags } = await this.parse(FunctionsRouteUnsetCommand);
|
|
18774
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18775
|
+
apiKey: flags["api-key"],
|
|
18776
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18777
|
+
configDir: this.config.configDir
|
|
18778
|
+
});
|
|
18779
|
+
await runWithTiming(flags.time, async () => {
|
|
18780
|
+
const result = await unsetFunctionRoute({
|
|
18781
|
+
client: apiClient.client,
|
|
18782
|
+
path: { id: flags.id },
|
|
18783
|
+
responseStyle: "fields"
|
|
18784
|
+
});
|
|
18785
|
+
if (result.error) {
|
|
18786
|
+
const payload = extractErrorPayload(result.error);
|
|
18787
|
+
writeErrorWithHints(payload);
|
|
18788
|
+
surfaceUnauthorizedHint({
|
|
18789
|
+
auth,
|
|
18790
|
+
baseUrlOverridden,
|
|
18791
|
+
configDir: this.config.configDir,
|
|
18792
|
+
payload
|
|
18793
|
+
});
|
|
18794
|
+
process.exitCode = 1;
|
|
18795
|
+
return;
|
|
18796
|
+
}
|
|
18797
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
18798
|
+
});
|
|
18799
|
+
}
|
|
18800
|
+
};
|
|
18801
|
+
//#endregion
|
|
18802
|
+
//#region src/oclif/commands/functions-routing-topology.ts
|
|
18803
|
+
var FunctionsRoutingTopologyCommand = class FunctionsRoutingTopologyCommand extends Command {
|
|
18804
|
+
static description = `Show the org-wide function routing topology. Lists every active domain with its bound function (if any), the org fallback function (if any), and every deployed function with no route currently bound.`;
|
|
18805
|
+
static summary = "Show the org-wide function routing topology";
|
|
18806
|
+
static examples = ["<%= config.bin %> functions routing-topology"];
|
|
18807
|
+
static flags = {
|
|
18808
|
+
"api-key": Flags.string({
|
|
18809
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18810
|
+
env: "PRIMITIVE_API_KEY"
|
|
18811
|
+
}),
|
|
18812
|
+
"api-base-url": Flags.string({
|
|
18813
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
18814
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18815
|
+
hidden: true
|
|
18816
|
+
}),
|
|
18817
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
18818
|
+
};
|
|
18819
|
+
async run() {
|
|
18820
|
+
const { flags } = await this.parse(FunctionsRoutingTopologyCommand);
|
|
18821
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18822
|
+
apiKey: flags["api-key"],
|
|
18823
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18824
|
+
configDir: this.config.configDir
|
|
18825
|
+
});
|
|
18826
|
+
await runWithTiming(flags.time, async () => {
|
|
18827
|
+
const result = await getOrgRoutingTopology({
|
|
18828
|
+
client: apiClient.client,
|
|
18829
|
+
responseStyle: "fields"
|
|
18830
|
+
});
|
|
18831
|
+
if (result.error) {
|
|
18832
|
+
const payload = extractErrorPayload(result.error);
|
|
18833
|
+
writeErrorWithHints(payload);
|
|
18834
|
+
surfaceUnauthorizedHint({
|
|
18835
|
+
auth,
|
|
18836
|
+
baseUrlOverridden,
|
|
18837
|
+
configDir: this.config.configDir,
|
|
18838
|
+
payload
|
|
18839
|
+
});
|
|
18840
|
+
process.exitCode = 1;
|
|
18841
|
+
return;
|
|
18842
|
+
}
|
|
18843
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
18844
|
+
});
|
|
18845
|
+
}
|
|
18846
|
+
};
|
|
18847
|
+
//#endregion
|
|
18015
18848
|
//#region src/oclif/commands/functions-set-secret.ts
|
|
18016
18849
|
async function runSetSecret(api, params) {
|
|
18017
18850
|
const setResult = await api.setSecret({
|
|
@@ -18103,14 +18936,9 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
18103
18936
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18104
18937
|
env: "PRIMITIVE_API_KEY"
|
|
18105
18938
|
}),
|
|
18106
|
-
"api-base-url
|
|
18939
|
+
"api-base-url": Flags.string({
|
|
18107
18940
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
18108
|
-
env: "
|
|
18109
|
-
hidden: true
|
|
18110
|
-
}),
|
|
18111
|
-
"api-base-url-2": Flags.string({
|
|
18112
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
18113
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
18941
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18114
18942
|
hidden: true
|
|
18115
18943
|
}),
|
|
18116
18944
|
id: Flags.string({
|
|
@@ -18134,8 +18962,7 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
18134
18962
|
await runWithTiming(flags.time, async () => {
|
|
18135
18963
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18136
18964
|
apiKey: flags["api-key"],
|
|
18137
|
-
|
|
18138
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
18965
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18139
18966
|
configDir: this.config.configDir
|
|
18140
18967
|
});
|
|
18141
18968
|
const authFailureContext = {
|
|
@@ -18195,6 +19022,7 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
18195
19022
|
return;
|
|
18196
19023
|
}
|
|
18197
19024
|
this.log(JSON.stringify(outcome.result, null, 2));
|
|
19025
|
+
if (outcome.result.redeploy === void 0) process.stderr.write(`Secret ${flags.key} saved. Not live until redeploy. Re-run with --redeploy, or run \`primitive functions redeploy --id ${flags.id} --file <bundle.js>\`.\n`);
|
|
18198
19026
|
});
|
|
18199
19027
|
}
|
|
18200
19028
|
};
|
|
@@ -18319,7 +19147,7 @@ async function maybeWriteEndpointNoiseWarning(params) {
|
|
|
18319
19147
|
} catch {}
|
|
18320
19148
|
}
|
|
18321
19149
|
var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Command {
|
|
18322
|
-
static description = "Send a real test email through MX to trigger this function. With --wait, blocks until the function has processed the inbound; with --show-sends, also prints any outbound sends the function emitted in response.";
|
|
19150
|
+
static description = "Send a real test email through MX to trigger this function. The function must have an active route bound (see `functions route-set`); without one the API returns a 422 immediately so no doomed test send is queued. With --wait, blocks until the function has processed the inbound; with --show-sends, also prints any outbound sends the function emitted in response.";
|
|
18323
19151
|
static summary = "Trigger a test invocation; with --wait, watch it land";
|
|
18324
19152
|
static examples = [
|
|
18325
19153
|
"<%= config.bin %> functions test --id <fn-id>",
|
|
@@ -18332,14 +19160,9 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
18332
19160
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18333
19161
|
env: "PRIMITIVE_API_KEY"
|
|
18334
19162
|
}),
|
|
18335
|
-
"api-base-url
|
|
18336
|
-
description:
|
|
18337
|
-
env: "
|
|
18338
|
-
hidden: true
|
|
18339
|
-
}),
|
|
18340
|
-
"api-base-url-2": Flags.string({
|
|
18341
|
-
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
18342
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
19163
|
+
"api-base-url": Flags.string({
|
|
19164
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
19165
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18343
19166
|
hidden: true
|
|
18344
19167
|
}),
|
|
18345
19168
|
id: Flags.string({
|
|
@@ -18367,8 +19190,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
18367
19190
|
const shouldShowSends = flags["show-sends"];
|
|
18368
19191
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18369
19192
|
apiKey: flags["api-key"],
|
|
18370
|
-
|
|
18371
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
19193
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18372
19194
|
configDir: this.config.configDir
|
|
18373
19195
|
});
|
|
18374
19196
|
await runWithTiming(flags.time, async () => {
|
|
@@ -18431,13 +19253,35 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
18431
19253
|
return;
|
|
18432
19254
|
}
|
|
18433
19255
|
const fetched = result.data.data;
|
|
19256
|
+
if (fetched.inbound_email && Array.isArray(fetched.deliveries) && fetched.deliveries.length === 0 && Date.now() - startedAt > 15e3) {
|
|
19257
|
+
writeFunctionTestProgress(`Inbound email arrived but no route matched. Bind one with: primitive functions route-set --id ${flags.id} --domain <domain-id> (or --fallback), then retry.`);
|
|
19258
|
+
process.exitCode = 1;
|
|
19259
|
+
return;
|
|
19260
|
+
}
|
|
18434
19261
|
if (TERMINAL_TEST_TRACE_STATES.has(fetched.state)) {
|
|
18435
19262
|
trace = fetched;
|
|
18436
19263
|
break;
|
|
18437
19264
|
}
|
|
18438
19265
|
await sleep$1(pollIntervalMs);
|
|
18439
19266
|
}
|
|
18440
|
-
if (!trace)
|
|
19267
|
+
if (!trace) {
|
|
19268
|
+
const finalTrace = ((await getFunctionTestRunTrace({
|
|
19269
|
+
client: apiClient.client,
|
|
19270
|
+
path: {
|
|
19271
|
+
id: flags.id,
|
|
19272
|
+
run_id: invocation.test_run_id
|
|
19273
|
+
},
|
|
19274
|
+
responseStyle: "fields"
|
|
19275
|
+
}).catch(() => null))?.data)?.data;
|
|
19276
|
+
const inboundLanded = Boolean(finalTrace?.inbound_email);
|
|
19277
|
+
const deliveryCount = finalTrace?.deliveries?.length ?? 0;
|
|
19278
|
+
const logCount = finalTrace?.logs?.length ?? 0;
|
|
19279
|
+
const replyCount = finalTrace?.replies?.length ?? 0;
|
|
19280
|
+
const webhookStatus = finalTrace?.inbound_email?.webhook_status ?? "n/a";
|
|
19281
|
+
writeFunctionTestProgress(`Timed out after ${flags.timeout}s. Trace summary: inbound_landed=${inboundLanded} deliveries=${deliveryCount} logs=${logCount} replies=${replyCount} webhook_status=${webhookStatus}. Browse ${invocation.watch_url} for the live view, or inspect ${invocation.trace_url}.`);
|
|
19282
|
+
process.exitCode = 2;
|
|
19283
|
+
return;
|
|
19284
|
+
}
|
|
18441
19285
|
const outcome = buildFunctionTestOutcome({
|
|
18442
19286
|
elapsedSeconds: Math.round((Date.now() - startedAt) / 1e3),
|
|
18443
19287
|
functionId: flags.id,
|
|
@@ -18479,7 +19323,7 @@ function formatInboxDate(value) {
|
|
|
18479
19323
|
const pad = (n) => String(n).padStart(2, "0");
|
|
18480
19324
|
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())} UTC`;
|
|
18481
19325
|
}
|
|
18482
|
-
function truncate(value, width) {
|
|
19326
|
+
function truncate$1(value, width) {
|
|
18483
19327
|
if (value.length <= width) return value.padEnd(width);
|
|
18484
19328
|
return `${value.slice(0, width - 3)}...`;
|
|
18485
19329
|
}
|
|
@@ -18529,7 +19373,7 @@ function formatDomainHeader() {
|
|
|
18529
19373
|
}
|
|
18530
19374
|
function formatDomainRow(domain) {
|
|
18531
19375
|
return [
|
|
18532
|
-
truncate(domain.domain, DOMAIN_DISPLAY_WIDTH),
|
|
19376
|
+
truncate$1(domain.domain, DOMAIN_DISPLAY_WIDTH),
|
|
18533
19377
|
statusText(domain.status).padEnd(STATUS_DISPLAY_WIDTH),
|
|
18534
19378
|
yesNo(domain.receiving_ready).padEnd(BOOL_DISPLAY_WIDTH),
|
|
18535
19379
|
yesNo(domain.processing_ready).padEnd(BOOL_DISPLAY_WIDTH),
|
|
@@ -18575,14 +19419,9 @@ var InboxStatusCommand = class InboxStatusCommand extends Command {
|
|
|
18575
19419
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18576
19420
|
env: "PRIMITIVE_API_KEY"
|
|
18577
19421
|
}),
|
|
18578
|
-
"api-base-url
|
|
18579
|
-
description:
|
|
18580
|
-
env: "
|
|
18581
|
-
hidden: true
|
|
18582
|
-
}),
|
|
18583
|
-
"api-base-url-2": Flags.string({
|
|
18584
|
-
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
18585
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
19422
|
+
"api-base-url": Flags.string({
|
|
19423
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
19424
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18586
19425
|
hidden: true
|
|
18587
19426
|
}),
|
|
18588
19427
|
domain: Flags.string({ description: "Focus domain readiness and recent email fields on one domain returned by the inbox status API." }),
|
|
@@ -18594,8 +19433,7 @@ var InboxStatusCommand = class InboxStatusCommand extends Command {
|
|
|
18594
19433
|
await runWithTiming(flags.time, async () => {
|
|
18595
19434
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18596
19435
|
apiKey: flags["api-key"],
|
|
18597
|
-
|
|
18598
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
19436
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18599
19437
|
configDir: this.config.configDir
|
|
18600
19438
|
});
|
|
18601
19439
|
const result = await getInboxStatus({
|
|
@@ -18750,14 +19588,9 @@ var InboxSetupCommand = class InboxSetupCommand extends Command {
|
|
|
18750
19588
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18751
19589
|
env: "PRIMITIVE_API_KEY"
|
|
18752
19590
|
}),
|
|
18753
|
-
"api-base-url
|
|
18754
|
-
description:
|
|
18755
|
-
env: "
|
|
18756
|
-
hidden: true
|
|
18757
|
-
}),
|
|
18758
|
-
"api-base-url-2": Flags.string({
|
|
18759
|
-
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
18760
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
19591
|
+
"api-base-url": Flags.string({
|
|
19592
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
19593
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18761
19594
|
hidden: true
|
|
18762
19595
|
}),
|
|
18763
19596
|
json: Flags.boolean({ description: "Print structured readiness, receive address, commands, proof metadata, and raw status as JSON." }),
|
|
@@ -18768,8 +19601,7 @@ var InboxSetupCommand = class InboxSetupCommand extends Command {
|
|
|
18768
19601
|
await runWithTiming(flags.time, async () => {
|
|
18769
19602
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18770
19603
|
apiKey: flags["api-key"],
|
|
18771
|
-
|
|
18772
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
19604
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18773
19605
|
configDir: this.config.configDir
|
|
18774
19606
|
});
|
|
18775
19607
|
const result = await getInboxStatus({
|
|
@@ -18836,14 +19668,14 @@ function retryAfterSeconds$1(result) {
|
|
|
18836
19668
|
}
|
|
18837
19669
|
async function checkExistingLogin(params) {
|
|
18838
19670
|
const requestConfig = resolveCliApiRequestConfig({
|
|
18839
|
-
|
|
19671
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
18840
19672
|
configDir: params.configDir
|
|
18841
19673
|
});
|
|
18842
|
-
const
|
|
19674
|
+
const probeApiBaseUrl = requestConfig.apiBaseUrl ?? params.credentials.api_base_url;
|
|
18843
19675
|
let credentials = params.credentials;
|
|
18844
19676
|
try {
|
|
18845
19677
|
credentials = await refreshStoredCliCredentials({
|
|
18846
|
-
|
|
19678
|
+
apiBaseUrl: probeApiBaseUrl,
|
|
18847
19679
|
configDir: params.configDir,
|
|
18848
19680
|
credentials,
|
|
18849
19681
|
credentialsLockHeld: params.credentialsLockHeld,
|
|
@@ -18859,8 +19691,7 @@ async function checkExistingLogin(params) {
|
|
|
18859
19691
|
}
|
|
18860
19692
|
const apiClient = new PrimitiveApiClient({
|
|
18861
19693
|
apiKey: credentials.access_token,
|
|
18862
|
-
|
|
18863
|
-
apiBaseUrl2: requestConfig.resolvedApiBaseUrl2,
|
|
19694
|
+
apiBaseUrl: probeApiBaseUrl,
|
|
18864
19695
|
headers: requestConfig.headers
|
|
18865
19696
|
});
|
|
18866
19697
|
const result = await (params.checkAccount ?? ((client) => getAccount({
|
|
@@ -18870,7 +19701,7 @@ async function checkExistingLogin(params) {
|
|
|
18870
19701
|
if (!result.error) return { status: "valid" };
|
|
18871
19702
|
const payload = extractErrorPayload(result.error);
|
|
18872
19703
|
const code = extractErrorCode(payload);
|
|
18873
|
-
const baseUrlDiffersFromSaved = requestConfig.baseUrlOverridden && requestConfig.
|
|
19704
|
+
const baseUrlDiffersFromSaved = requestConfig.baseUrlOverridden && requestConfig.apiBaseUrl !== params.credentials.api_base_url;
|
|
18874
19705
|
if (code === API_ERROR_CODES.unauthorized && !baseUrlDiffersFromSaved) {
|
|
18875
19706
|
deleteCliCredentials(params.configDir);
|
|
18876
19707
|
process.stderr.write("Removed saved Primitive CLI OAuth credentials because the existing session was rejected during login. Continuing with a fresh login.\n");
|
|
@@ -18891,9 +19722,9 @@ var LoginCommand$1 = class extends Command {
|
|
|
18891
19722
|
"<%= config.bin %> login --force"
|
|
18892
19723
|
];
|
|
18893
19724
|
static flags = {
|
|
18894
|
-
"api-base-url
|
|
19725
|
+
"api-base-url": Flags.string({
|
|
18895
19726
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
18896
|
-
env: "
|
|
19727
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18897
19728
|
hidden: true
|
|
18898
19729
|
}),
|
|
18899
19730
|
"device-name": Flags.string({ description: "Device name shown in the browser approval screen" }),
|
|
@@ -18926,10 +19757,10 @@ var LoginCommand$1 = class extends Command {
|
|
|
18926
19757
|
}
|
|
18927
19758
|
async runWithCredentialLock(flags, retryCommand) {
|
|
18928
19759
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
18929
|
-
|
|
19760
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18930
19761
|
configDir: this.config.configDir
|
|
18931
19762
|
});
|
|
18932
|
-
const
|
|
19763
|
+
const apiBaseUrl = requestConfig.resolvedApiBaseUrl;
|
|
18933
19764
|
let existing;
|
|
18934
19765
|
try {
|
|
18935
19766
|
existing = loadCliCredentials(this.config.configDir);
|
|
@@ -18942,7 +19773,7 @@ var LoginCommand$1 = class extends Command {
|
|
|
18942
19773
|
if (existing && flags.force) process.stderr.write("Replacing saved Primitive CLI credentials after browser approval because --force was set.\n");
|
|
18943
19774
|
else if (existing) {
|
|
18944
19775
|
const existingStatus = await checkExistingLogin({
|
|
18945
|
-
|
|
19776
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18946
19777
|
configDir: this.config.configDir,
|
|
18947
19778
|
credentials: existing,
|
|
18948
19779
|
credentialsLockHeld: true
|
|
@@ -18988,7 +19819,7 @@ var LoginCommand$1 = class extends Command {
|
|
|
18988
19819
|
deleteChatState(this.config.configDir);
|
|
18989
19820
|
saveCliCredentials(this.config.configDir, {
|
|
18990
19821
|
access_token: login.access_token,
|
|
18991
|
-
|
|
19822
|
+
api_base_url: apiBaseUrl,
|
|
18992
19823
|
auth_method: "oauth",
|
|
18993
19824
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18994
19825
|
expires_at: cliAccessTokenExpiresAt(login.expires_in),
|
|
@@ -19052,9 +19883,11 @@ function normalizeEmail(email) {
|
|
|
19052
19883
|
}
|
|
19053
19884
|
function pendingSignupFromJson(value) {
|
|
19054
19885
|
if (!isRecord(value)) return null;
|
|
19055
|
-
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.
|
|
19886
|
+
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.created_at !== "string" || typeof value.expires_at !== "string") return null;
|
|
19887
|
+
const apiBaseUrl = value.api_base_url ?? value.api_base_url_1;
|
|
19888
|
+
if (typeof apiBaseUrl !== "string") return null;
|
|
19056
19889
|
return {
|
|
19057
|
-
|
|
19890
|
+
api_base_url: apiBaseUrl,
|
|
19058
19891
|
created_at: value.created_at,
|
|
19059
19892
|
email: value.email,
|
|
19060
19893
|
expires_at: value.expires_at,
|
|
@@ -19070,20 +19903,20 @@ function pendingSignupPath(configDir) {
|
|
|
19070
19903
|
function deletePendingAgentSignup(configDir) {
|
|
19071
19904
|
rmSync(pendingSignupPath(configDir), { force: true });
|
|
19072
19905
|
}
|
|
19073
|
-
function pendingSignupFromStart(start,
|
|
19906
|
+
function pendingSignupFromStart(start, apiBaseUrl) {
|
|
19074
19907
|
return {
|
|
19075
19908
|
...start,
|
|
19076
|
-
|
|
19909
|
+
api_base_url: apiBaseUrl,
|
|
19077
19910
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19078
19911
|
expires_at: new Date(Date.now() + start.expires_in * 1e3).toISOString()
|
|
19079
19912
|
};
|
|
19080
19913
|
}
|
|
19081
|
-
function savePendingAgentSignup(configDir, start,
|
|
19914
|
+
function savePendingAgentSignup(configDir, start, apiBaseUrl) {
|
|
19082
19915
|
mkdirSync(configDir, {
|
|
19083
19916
|
mode: 448,
|
|
19084
19917
|
recursive: true
|
|
19085
19918
|
});
|
|
19086
|
-
const pending = pendingSignupFromStart(start,
|
|
19919
|
+
const pending = pendingSignupFromStart(start, apiBaseUrl);
|
|
19087
19920
|
const path = pendingSignupPath(configDir);
|
|
19088
19921
|
const tempPath = join(configDir, `${PENDING_SIGNUP_FILE}.${process$1.pid}.${randomUUID()}.tmp`);
|
|
19089
19922
|
try {
|
|
@@ -19097,7 +19930,7 @@ function savePendingAgentSignup(configDir, start, apiBaseUrl1) {
|
|
|
19097
19930
|
throw error;
|
|
19098
19931
|
}
|
|
19099
19932
|
}
|
|
19100
|
-
function loadPendingAgentSignup(configDir,
|
|
19933
|
+
function loadPendingAgentSignup(configDir, apiBaseUrl) {
|
|
19101
19934
|
const path = pendingSignupPath(configDir);
|
|
19102
19935
|
let contents;
|
|
19103
19936
|
try {
|
|
@@ -19116,7 +19949,7 @@ function loadPendingAgentSignup(configDir, apiBaseUrl1) {
|
|
|
19116
19949
|
deletePendingAgentSignup(configDir);
|
|
19117
19950
|
return null;
|
|
19118
19951
|
}
|
|
19119
|
-
if (pending.
|
|
19952
|
+
if (pending.api_base_url !== apiBaseUrl) return null;
|
|
19120
19953
|
if (new Date(pending.expires_at).getTime() <= Date.now()) {
|
|
19121
19954
|
deletePendingAgentSignup(configDir);
|
|
19122
19955
|
return null;
|
|
@@ -19126,7 +19959,7 @@ function loadPendingAgentSignup(configDir, apiBaseUrl1) {
|
|
|
19126
19959
|
expires_in: Math.max(0, Math.ceil((new Date(pending.expires_at).getTime() - Date.now()) / 1e3))
|
|
19127
19960
|
};
|
|
19128
19961
|
}
|
|
19129
|
-
function readPendingAgentSignupState(configDir,
|
|
19962
|
+
function readPendingAgentSignupState(configDir, apiBaseUrl) {
|
|
19130
19963
|
const path = pendingSignupPath(configDir);
|
|
19131
19964
|
let contents;
|
|
19132
19965
|
try {
|
|
@@ -19145,7 +19978,7 @@ function readPendingAgentSignupState(configDir, apiBaseUrl1) {
|
|
|
19145
19978
|
deletePendingAgentSignup(configDir);
|
|
19146
19979
|
return null;
|
|
19147
19980
|
}
|
|
19148
|
-
if (pending.
|
|
19981
|
+
if (pending.api_base_url !== apiBaseUrl) return null;
|
|
19149
19982
|
return pending;
|
|
19150
19983
|
}
|
|
19151
19984
|
function pendingSignupStartCommand(email) {
|
|
@@ -19153,7 +19986,7 @@ function pendingSignupStartCommand(email) {
|
|
|
19153
19986
|
}
|
|
19154
19987
|
function buildSignupStatus(params) {
|
|
19155
19988
|
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
19156
|
-
const pending = readPendingAgentSignupState(params.configDir, params.
|
|
19989
|
+
const pending = readPendingAgentSignupState(params.configDir, params.apiBaseUrl);
|
|
19157
19990
|
if (!pending) return {
|
|
19158
19991
|
code_length: null,
|
|
19159
19992
|
confirm_command: null,
|
|
@@ -19200,11 +20033,11 @@ function writeSignupStatus(status) {
|
|
|
19200
20033
|
}
|
|
19201
20034
|
function runSignupStatus(params) {
|
|
19202
20035
|
const { requestConfig } = createCliApiClient({
|
|
19203
|
-
|
|
20036
|
+
apiBaseUrl: params.flags["api-base-url"],
|
|
19204
20037
|
configDir: params.configDir
|
|
19205
20038
|
});
|
|
19206
20039
|
const status = buildSignupStatus({
|
|
19207
|
-
|
|
20040
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
19208
20041
|
configDir: params.configDir,
|
|
19209
20042
|
copy: params.copy,
|
|
19210
20043
|
email: params.email
|
|
@@ -19217,7 +20050,7 @@ function runSignupStatus(params) {
|
|
|
19217
20050
|
}
|
|
19218
20051
|
function requirePendingSignupForEmail(params) {
|
|
19219
20052
|
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
19220
|
-
const pending = loadPendingAgentSignup(params.configDir, params.
|
|
20053
|
+
const pending = loadPendingAgentSignup(params.configDir, params.apiBaseUrl);
|
|
19221
20054
|
if (!pending) throw cliError$2(`No pending ${copy.actionNoun} for ${params.email}. Run \`primitive signup status ${params.email}\` to inspect pending state, or \`primitive ${copy.startCommand(params.email)}\` first.`);
|
|
19222
20055
|
if (normalizeEmail(pending.email) !== normalizeEmail(params.email)) throw cliError$2(`Pending ${copy.actionNoun} is for ${pending.email}, not ${params.email}. Run \`primitive signup status\` to inspect it, or \`primitive ${copy.startCommand(params.email)} --force\` to replace it.`);
|
|
19223
20056
|
return pending;
|
|
@@ -19280,7 +20113,7 @@ async function checkExistingCredentials(params) {
|
|
|
19280
20113
|
}
|
|
19281
20114
|
if (!existing) return;
|
|
19282
20115
|
const existingStatus = await checkExistingLoginFn({
|
|
19283
|
-
|
|
20116
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
19284
20117
|
configDir: params.configDir,
|
|
19285
20118
|
credentials: existing,
|
|
19286
20119
|
credentialsLockHeld: true
|
|
@@ -19299,7 +20132,7 @@ function saveSignupCredentials(params) {
|
|
|
19299
20132
|
deleteChatState(params.configDir);
|
|
19300
20133
|
saveCliCredentials(params.configDir, {
|
|
19301
20134
|
access_token: params.signup.access_token,
|
|
19302
|
-
|
|
20135
|
+
api_base_url: params.apiBaseUrl,
|
|
19303
20136
|
auth_method: "oauth",
|
|
19304
20137
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19305
20138
|
expires_at: cliAccessTokenExpiresAt(params.signup.expires_in),
|
|
@@ -19318,7 +20151,7 @@ function writeStartInstructions(start, copy = DEFAULT_SIGNUP_COMMAND_COPY) {
|
|
|
19318
20151
|
}
|
|
19319
20152
|
async function startSignup(params) {
|
|
19320
20153
|
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
19321
|
-
const existingPending = loadPendingAgentSignup(params.configDir, params.
|
|
20154
|
+
const existingPending = loadPendingAgentSignup(params.configDir, params.apiBaseUrl);
|
|
19322
20155
|
if (existingPending && !params.flags.force) {
|
|
19323
20156
|
if (normalizeEmail(existingPending.email) === normalizeEmail(params.email)) {
|
|
19324
20157
|
process$1.stderr.write(`Continuing pending Primitive ${copy.actionNoun} for ${existingPending.email}.\n`);
|
|
@@ -19353,7 +20186,7 @@ async function startSignup(params) {
|
|
|
19353
20186
|
const startResult = unwrapData$1(started.data);
|
|
19354
20187
|
if (!startResult) throw cliError$2("Primitive API returned an empty agent signup response.");
|
|
19355
20188
|
return {
|
|
19356
|
-
pending: savePendingAgentSignup(params.configDir, startResult, params.
|
|
20189
|
+
pending: savePendingAgentSignup(params.configDir, startResult, params.apiBaseUrl),
|
|
19357
20190
|
started: true
|
|
19358
20191
|
};
|
|
19359
20192
|
}
|
|
@@ -19373,7 +20206,7 @@ async function resendVerificationCode(params) {
|
|
|
19373
20206
|
verification_code_length: resend.verification_code_length
|
|
19374
20207
|
} : params.start;
|
|
19375
20208
|
return {
|
|
19376
|
-
pending: savePendingAgentSignup(params.configDir, next, params.
|
|
20209
|
+
pending: savePendingAgentSignup(params.configDir, next, params.apiBaseUrl),
|
|
19377
20210
|
resent: true
|
|
19378
20211
|
};
|
|
19379
20212
|
}
|
|
@@ -19397,18 +20230,18 @@ async function runSignupStartWithCredentialLock(params) {
|
|
|
19397
20230
|
const promptRequiredFn = deps.promptRequired ?? promptRequired;
|
|
19398
20231
|
const email = params.email ?? await promptRequiredFn("Email: ");
|
|
19399
20232
|
await checkExistingCredentials({
|
|
19400
|
-
|
|
20233
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19401
20234
|
configDir,
|
|
19402
20235
|
copy: params.copy,
|
|
19403
20236
|
deps,
|
|
19404
20237
|
flags
|
|
19405
20238
|
});
|
|
19406
20239
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
19407
|
-
|
|
20240
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19408
20241
|
configDir
|
|
19409
20242
|
});
|
|
19410
20243
|
const start = await startSignup({
|
|
19411
|
-
|
|
20244
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
19412
20245
|
apiClient,
|
|
19413
20246
|
configDir,
|
|
19414
20247
|
copy: params.copy,
|
|
@@ -19422,19 +20255,19 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
19422
20255
|
const { configDir, flags } = params;
|
|
19423
20256
|
const deps = params.deps ?? {};
|
|
19424
20257
|
if (!params.skipExistingCredentialCheck) await checkExistingCredentials({
|
|
19425
|
-
|
|
20258
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19426
20259
|
configDir,
|
|
19427
20260
|
copy: params.copy,
|
|
19428
20261
|
deps,
|
|
19429
20262
|
flags
|
|
19430
20263
|
});
|
|
19431
20264
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
19432
|
-
|
|
20265
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19433
20266
|
configDir
|
|
19434
20267
|
});
|
|
19435
|
-
const
|
|
20268
|
+
const apiBaseUrl = requestConfig.resolvedApiBaseUrl;
|
|
19436
20269
|
const pending = requirePendingSignupForEmail({
|
|
19437
|
-
|
|
20270
|
+
apiBaseUrl,
|
|
19438
20271
|
copy: params.copy,
|
|
19439
20272
|
configDir,
|
|
19440
20273
|
email: params.email
|
|
@@ -19452,7 +20285,7 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
19452
20285
|
const signup = unwrapData$1(verified.data);
|
|
19453
20286
|
if (!signup) throw cliError$2("Primitive API returned an empty agent signup verification response.");
|
|
19454
20287
|
saveSignupCredentials({
|
|
19455
|
-
|
|
20288
|
+
apiBaseUrl,
|
|
19456
20289
|
configDir,
|
|
19457
20290
|
signup
|
|
19458
20291
|
});
|
|
@@ -19473,18 +20306,18 @@ async function runSignupResendWithCredentialLock(params) {
|
|
|
19473
20306
|
const deps = params.deps ?? {};
|
|
19474
20307
|
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
19475
20308
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
19476
|
-
|
|
20309
|
+
apiBaseUrl: params.flags["api-base-url"],
|
|
19477
20310
|
configDir: params.configDir
|
|
19478
20311
|
});
|
|
19479
20312
|
const pending = params.email ? requirePendingSignupForEmail({
|
|
19480
|
-
|
|
20313
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
19481
20314
|
copy,
|
|
19482
20315
|
configDir: params.configDir,
|
|
19483
20316
|
email: params.email
|
|
19484
|
-
}) : loadPendingAgentSignup(params.configDir, requestConfig.
|
|
20317
|
+
}) : loadPendingAgentSignup(params.configDir, requestConfig.resolvedApiBaseUrl);
|
|
19485
20318
|
if (!pending) throw cliError$2(`No pending ${copy.actionNoun} found. Run \`primitive signup status\` to inspect pending state, or start one with \`${pendingSignupStartCommand()}\`.`);
|
|
19486
20319
|
const resend = await resendVerificationCode({
|
|
19487
|
-
|
|
20320
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
19488
20321
|
apiClient,
|
|
19489
20322
|
configDir: params.configDir,
|
|
19490
20323
|
deps,
|
|
@@ -19497,20 +20330,20 @@ async function runSignupInteractiveWithCredentialLock(params) {
|
|
|
19497
20330
|
const deps = params.deps ?? {};
|
|
19498
20331
|
const promptRequiredFn = deps.promptRequired ?? promptRequired;
|
|
19499
20332
|
await checkExistingCredentials({
|
|
19500
|
-
|
|
20333
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19501
20334
|
configDir,
|
|
19502
20335
|
deps,
|
|
19503
20336
|
flags
|
|
19504
20337
|
});
|
|
19505
20338
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
19506
|
-
|
|
20339
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19507
20340
|
configDir
|
|
19508
20341
|
});
|
|
19509
|
-
const
|
|
19510
|
-
let start = flags.force ? null : loadPendingAgentSignup(configDir,
|
|
20342
|
+
const apiBaseUrl = requestConfig.resolvedApiBaseUrl;
|
|
20343
|
+
let start = flags.force ? null : loadPendingAgentSignup(configDir, apiBaseUrl);
|
|
19511
20344
|
if (start) process$1.stderr.write(`Continuing pending Primitive signup for ${start.email}.\n`);
|
|
19512
20345
|
else start = (await startSignup({
|
|
19513
|
-
|
|
20346
|
+
apiBaseUrl,
|
|
19514
20347
|
apiClient,
|
|
19515
20348
|
configDir,
|
|
19516
20349
|
deps,
|
|
@@ -19524,7 +20357,7 @@ async function runSignupInteractiveWithCredentialLock(params) {
|
|
|
19524
20357
|
const verificationCode = await promptRequiredFn(`Verification code (${start.verification_code_length} digits): `);
|
|
19525
20358
|
if (verificationCode.toLowerCase() === "resend") {
|
|
19526
20359
|
const resend = await resendVerificationCode({
|
|
19527
|
-
|
|
20360
|
+
apiBaseUrl,
|
|
19528
20361
|
apiClient,
|
|
19529
20362
|
configDir,
|
|
19530
20363
|
deps,
|
|
@@ -19541,7 +20374,7 @@ async function runSignupInteractiveWithCredentialLock(params) {
|
|
|
19541
20374
|
deps,
|
|
19542
20375
|
email: start.email,
|
|
19543
20376
|
flags: {
|
|
19544
|
-
"api-base-url
|
|
20377
|
+
"api-base-url": flags["api-base-url"],
|
|
19545
20378
|
force: true
|
|
19546
20379
|
},
|
|
19547
20380
|
skipExistingCredentialCheck: true
|
|
@@ -19559,9 +20392,9 @@ async function runSignupInteractiveWithCredentialLock(params) {
|
|
|
19559
20392
|
function commonStartFlags() {
|
|
19560
20393
|
return {
|
|
19561
20394
|
"accept-terms": Flags.boolean({ description: "Confirm acceptance of Primitive's Terms of Service and Privacy Policy" }),
|
|
19562
|
-
"api-base-url
|
|
20395
|
+
"api-base-url": Flags.string({
|
|
19563
20396
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19564
|
-
env: "
|
|
20397
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19565
20398
|
hidden: true
|
|
19566
20399
|
}),
|
|
19567
20400
|
"device-name": Flags.string({ description: "Device name used for the created CLI OAuth session" }),
|
|
@@ -19622,9 +20455,9 @@ var SignupConfirmCommand = class SignupConfirmCommand extends Command {
|
|
|
19622
20455
|
static summary = "Confirm account signup";
|
|
19623
20456
|
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"];
|
|
19624
20457
|
static flags = {
|
|
19625
|
-
"api-base-url
|
|
20458
|
+
"api-base-url": Flags.string({
|
|
19626
20459
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19627
|
-
env: "
|
|
20460
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19628
20461
|
hidden: true
|
|
19629
20462
|
}),
|
|
19630
20463
|
force: Flags.boolean({
|
|
@@ -19661,9 +20494,9 @@ var SignupResendCommand = class SignupResendCommand extends Command {
|
|
|
19661
20494
|
static description = "Resend the verification code for a pending signup.";
|
|
19662
20495
|
static summary = "Resend signup verification code";
|
|
19663
20496
|
static examples = ["<%= config.bin %> signup resend", "<%= config.bin %> signup resend user@example.com"];
|
|
19664
|
-
static flags = { "api-base-url
|
|
20497
|
+
static flags = { "api-base-url": Flags.string({
|
|
19665
20498
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19666
|
-
env: "
|
|
20499
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19667
20500
|
hidden: true
|
|
19668
20501
|
}) };
|
|
19669
20502
|
async run() {
|
|
@@ -19698,9 +20531,9 @@ var SignupStatusCommand = class SignupStatusCommand extends Command {
|
|
|
19698
20531
|
"<%= config.bin %> signup status --json"
|
|
19699
20532
|
];
|
|
19700
20533
|
static flags = {
|
|
19701
|
-
"api-base-url
|
|
20534
|
+
"api-base-url": Flags.string({
|
|
19702
20535
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19703
|
-
env: "
|
|
20536
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19704
20537
|
hidden: true
|
|
19705
20538
|
}),
|
|
19706
20539
|
json: Flags.boolean({ description: "Print pending signup status as JSON" })
|
|
@@ -19768,7 +20601,7 @@ async function runLogoutWithCredentialLock(params) {
|
|
|
19768
20601
|
let authenticated;
|
|
19769
20602
|
try {
|
|
19770
20603
|
authenticated = await deps.createAuthenticatedCliApiClient({
|
|
19771
|
-
|
|
20604
|
+
apiBaseUrl: params.flags["api-base-url"],
|
|
19772
20605
|
configDir: params.configDir,
|
|
19773
20606
|
credentialsLockHeld: true
|
|
19774
20607
|
});
|
|
@@ -19832,9 +20665,9 @@ var LogoutCommand = class LogoutCommand extends Command {
|
|
|
19832
20665
|
static summary = "Log out and revoke the saved CLI OAuth grant";
|
|
19833
20666
|
static examples = ["<%= config.bin %> logout", "<%= config.bin %> logout --force"];
|
|
19834
20667
|
static flags = {
|
|
19835
|
-
"api-base-url
|
|
20668
|
+
"api-base-url": Flags.string({
|
|
19836
20669
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19837
|
-
env: "
|
|
20670
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19838
20671
|
hidden: true
|
|
19839
20672
|
}),
|
|
19840
20673
|
force: Flags.boolean({
|
|
@@ -19983,14 +20816,9 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
19983
20816
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
19984
20817
|
env: "PRIMITIVE_API_KEY"
|
|
19985
20818
|
}),
|
|
19986
|
-
"api-base-url
|
|
19987
|
-
description: "Override the
|
|
19988
|
-
env: "
|
|
19989
|
-
hidden: true
|
|
19990
|
-
}),
|
|
19991
|
-
"api-base-url-2": Flags.string({
|
|
19992
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
19993
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
20819
|
+
"api-base-url": Flags.string({
|
|
20820
|
+
description: "Override the API base URL. Internal testing only; not documented to customers.",
|
|
20821
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19994
20822
|
hidden: true
|
|
19995
20823
|
}),
|
|
19996
20824
|
id: Flags.string({
|
|
@@ -20026,8 +20854,7 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
20026
20854
|
await runWithTiming(flags.time, async () => {
|
|
20027
20855
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
20028
20856
|
apiKey: flags["api-key"],
|
|
20029
|
-
|
|
20030
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
20857
|
+
apiBaseUrl: flags["api-base-url"],
|
|
20031
20858
|
configDir: this.config.configDir
|
|
20032
20859
|
});
|
|
20033
20860
|
const attachments = readAttachmentFiles(flags.attachment);
|
|
@@ -20039,7 +20866,7 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
20039
20866
|
...attachments !== void 0 ? { attachments } : {},
|
|
20040
20867
|
...flags.wait !== void 0 ? { wait: flags.wait } : {}
|
|
20041
20868
|
},
|
|
20042
|
-
client: apiClient.
|
|
20869
|
+
client: apiClient.client,
|
|
20043
20870
|
path: { id: flags.id },
|
|
20044
20871
|
responseStyle: "fields"
|
|
20045
20872
|
});
|
|
@@ -20064,6 +20891,134 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
20064
20891
|
}
|
|
20065
20892
|
};
|
|
20066
20893
|
//#endregion
|
|
20894
|
+
//#region src/oclif/commands/semantic-search.ts
|
|
20895
|
+
const DEFAULT_LIMIT = 10;
|
|
20896
|
+
const MAX_LIMIT = 100;
|
|
20897
|
+
const SCORE_WIDTH = 7;
|
|
20898
|
+
const SOURCE_WIDTH = 4;
|
|
20899
|
+
const SUBJECT_WIDTH = 40;
|
|
20900
|
+
const FROM_WIDTH = 26;
|
|
20901
|
+
const SNIPPET_WIDTH = 60;
|
|
20902
|
+
function truncate(value, width) {
|
|
20903
|
+
if (value.length <= width) return value.padEnd(width);
|
|
20904
|
+
return `${value.slice(0, width - 3)}...`;
|
|
20905
|
+
}
|
|
20906
|
+
function sourceLabel(t) {
|
|
20907
|
+
return t === "inbound_email" ? "in" : "out";
|
|
20908
|
+
}
|
|
20909
|
+
function formatRow(r) {
|
|
20910
|
+
return `${r.score.toFixed(3).padStart(SCORE_WIDTH)} ${sourceLabel(r.source_type).padEnd(SOURCE_WIDTH)} ${truncate((r.subject ?? "").replace(/\s+/g, " "), SUBJECT_WIDTH)} ${truncate(r.from ?? "", FROM_WIDTH)} ${truncate((r.snippets[0]?.text ?? "").replace(/\s+/g, " "), SNIPPET_WIDTH)}`;
|
|
20911
|
+
}
|
|
20912
|
+
function formatHeader() {
|
|
20913
|
+
return `${"SCORE".padStart(SCORE_WIDTH)} ${"SRC".padEnd(SOURCE_WIDTH)} ${"SUBJECT".padEnd(SUBJECT_WIDTH)} ${"FROM".padEnd(FROM_WIDTH)} EXCERPT`;
|
|
20914
|
+
}
|
|
20915
|
+
var SemanticSearchCommand = class SemanticSearchCommand extends Command {
|
|
20916
|
+
static description = `Search received and sent mail by meaning or keywords.
|
|
20917
|
+
|
|
20918
|
+
Returns ranked rows. Each row carries a relevance score, the fields it
|
|
20919
|
+
matched, and a match-centered excerpt. Defaults to hybrid mode (blends
|
|
20920
|
+
semantic and keyword signals); use \`--mode keyword\` for plain
|
|
20921
|
+
full-text matching and \`--mode semantic\` for embedding-only.
|
|
20922
|
+
|
|
20923
|
+
Requires the Pro plan with the semantic_search_enabled entitlement.`;
|
|
20924
|
+
static summary = "Semantic / hybrid / keyword search across received and sent mail";
|
|
20925
|
+
static examples = [
|
|
20926
|
+
"<%= config.bin %> semantic-search \"invoice from acme\"",
|
|
20927
|
+
"<%= config.bin %> semantic-search \"shipping update\" --mode keyword",
|
|
20928
|
+
"<%= config.bin %> semantic-search \"kickoff\" --corpus inbound --limit 25",
|
|
20929
|
+
"<%= config.bin %> semantic-search renewal --json | jq '.data[].id'"
|
|
20930
|
+
];
|
|
20931
|
+
static args = { query: Args.string({
|
|
20932
|
+
description: "The search query.",
|
|
20933
|
+
required: true
|
|
20934
|
+
}) };
|
|
20935
|
+
static flags = {
|
|
20936
|
+
"api-key": Flags.string({
|
|
20937
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
20938
|
+
env: "PRIMITIVE_API_KEY"
|
|
20939
|
+
}),
|
|
20940
|
+
"api-base-url": Flags.string({
|
|
20941
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
20942
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
20943
|
+
hidden: true
|
|
20944
|
+
}),
|
|
20945
|
+
mode: Flags.string({
|
|
20946
|
+
description: "Ranking strategy.",
|
|
20947
|
+
options: [
|
|
20948
|
+
"hybrid",
|
|
20949
|
+
"semantic",
|
|
20950
|
+
"keyword"
|
|
20951
|
+
],
|
|
20952
|
+
default: "hybrid"
|
|
20953
|
+
}),
|
|
20954
|
+
corpus: Flags.string({
|
|
20955
|
+
description: "Restrict to inbound or outbound. Pass twice to include both (the default).",
|
|
20956
|
+
options: ["inbound", "outbound"],
|
|
20957
|
+
multiple: true
|
|
20958
|
+
}),
|
|
20959
|
+
"date-from": Flags.string({ description: "Only include mail at or after this ISO-8601 timestamp." }),
|
|
20960
|
+
"date-to": Flags.string({ description: "Only include mail at or before this ISO-8601 timestamp." }),
|
|
20961
|
+
limit: Flags.integer({
|
|
20962
|
+
description: `Maximum results to return (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}).`,
|
|
20963
|
+
default: DEFAULT_LIMIT,
|
|
20964
|
+
min: 1,
|
|
20965
|
+
max: MAX_LIMIT
|
|
20966
|
+
}),
|
|
20967
|
+
cursor: Flags.string({ description: "Opaque pagination cursor from a prior response's meta.cursor." }),
|
|
20968
|
+
json: Flags.boolean({ description: "Print the raw response envelope as JSON on STDOUT instead of the text table." }),
|
|
20969
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
20970
|
+
};
|
|
20971
|
+
async run() {
|
|
20972
|
+
const { args, flags } = await this.parse(SemanticSearchCommand);
|
|
20973
|
+
await runWithTiming(flags.time, async () => {
|
|
20974
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
20975
|
+
apiKey: flags["api-key"],
|
|
20976
|
+
apiBaseUrl: flags["api-base-url"],
|
|
20977
|
+
configDir: this.config.configDir
|
|
20978
|
+
});
|
|
20979
|
+
const result = await semanticSearch({
|
|
20980
|
+
client: apiClient.client,
|
|
20981
|
+
body: {
|
|
20982
|
+
query: args.query,
|
|
20983
|
+
mode: flags.mode,
|
|
20984
|
+
...flags.corpus ? { corpus: flags.corpus } : {},
|
|
20985
|
+
...flags["date-from"] ? { date_from: flags["date-from"] } : {},
|
|
20986
|
+
...flags["date-to"] ? { date_to: flags["date-to"] } : {},
|
|
20987
|
+
limit: flags.limit,
|
|
20988
|
+
...flags.cursor ? { cursor: flags.cursor } : {}
|
|
20989
|
+
},
|
|
20990
|
+
responseStyle: "fields"
|
|
20991
|
+
});
|
|
20992
|
+
if (result.error) {
|
|
20993
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
20994
|
+
writeErrorWithHints(errorPayload);
|
|
20995
|
+
surfaceUnauthorizedHint({
|
|
20996
|
+
auth,
|
|
20997
|
+
baseUrlOverridden,
|
|
20998
|
+
configDir: this.config.configDir,
|
|
20999
|
+
payload: errorPayload
|
|
21000
|
+
});
|
|
21001
|
+
process.exitCode = 1;
|
|
21002
|
+
return;
|
|
21003
|
+
}
|
|
21004
|
+
const envelope = result.data;
|
|
21005
|
+
if (flags.json) {
|
|
21006
|
+
this.log(JSON.stringify(envelope ?? null, null, 2));
|
|
21007
|
+
return;
|
|
21008
|
+
}
|
|
21009
|
+
const rows = envelope?.data ?? [];
|
|
21010
|
+
if (rows.length === 0) {
|
|
21011
|
+
process.stderr.write("No matching mail.\n");
|
|
21012
|
+
return;
|
|
21013
|
+
}
|
|
21014
|
+
process.stderr.write(`${formatHeader()}\n`);
|
|
21015
|
+
for (const row of rows) this.log(formatRow(row));
|
|
21016
|
+
const nextCursor = envelope?.meta?.cursor ?? null;
|
|
21017
|
+
if (nextCursor) process.stderr.write(`\nNext page: pass --cursor ${nextCursor}\n`);
|
|
21018
|
+
});
|
|
21019
|
+
}
|
|
21020
|
+
};
|
|
21021
|
+
//#endregion
|
|
20067
21022
|
//#region src/oclif/commands/send.ts
|
|
20068
21023
|
var SendCommand = class SendCommand extends Command {
|
|
20069
21024
|
static description = `Send an outbound email. Agent-grade shortcut for \`sending send\` with sensible defaults.
|
|
@@ -20089,14 +21044,9 @@ var SendCommand = class SendCommand extends Command {
|
|
|
20089
21044
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
20090
21045
|
env: "PRIMITIVE_API_KEY"
|
|
20091
21046
|
}),
|
|
20092
|
-
"api-base-url
|
|
20093
|
-
description: "Override the
|
|
20094
|
-
env: "
|
|
20095
|
-
hidden: true
|
|
20096
|
-
}),
|
|
20097
|
-
"api-base-url-2": Flags.string({
|
|
20098
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
20099
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
21047
|
+
"api-base-url": Flags.string({
|
|
21048
|
+
description: "Override the API base URL. Internal testing only; not documented to customers.",
|
|
21049
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
20100
21050
|
hidden: true
|
|
20101
21051
|
}),
|
|
20102
21052
|
to: Flags.string({
|
|
@@ -20135,8 +21085,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
20135
21085
|
await runWithTiming(flags.time, async () => {
|
|
20136
21086
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
20137
21087
|
apiKey: flags["api-key"],
|
|
20138
|
-
|
|
20139
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
21088
|
+
apiBaseUrl: flags["api-base-url"],
|
|
20140
21089
|
configDir: this.config.configDir
|
|
20141
21090
|
});
|
|
20142
21091
|
const authFailureContext = {
|
|
@@ -20158,7 +21107,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
20158
21107
|
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
20159
21108
|
...flags["wait-timeout-ms"] !== void 0 ? { wait_timeout_ms: flags["wait-timeout-ms"] } : {}
|
|
20160
21109
|
},
|
|
20161
|
-
client: apiClient.
|
|
21110
|
+
client: apiClient.client,
|
|
20162
21111
|
responseStyle: "fields"
|
|
20163
21112
|
});
|
|
20164
21113
|
if (result.error) {
|
|
@@ -20229,9 +21178,9 @@ function acquireCredentialsLock(configDir) {
|
|
|
20229
21178
|
function commonOtpStartFlags() {
|
|
20230
21179
|
return {
|
|
20231
21180
|
"accept-terms": Flags.boolean({ description: "Confirm acceptance of Primitive's Terms of Service and Privacy Policy" }),
|
|
20232
|
-
"api-base-url
|
|
21181
|
+
"api-base-url": Flags.string({
|
|
20233
21182
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
20234
|
-
env: "
|
|
21183
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
20235
21184
|
hidden: true
|
|
20236
21185
|
}),
|
|
20237
21186
|
"device-name": Flags.string({ description: "Device name used for the created CLI OAuth session" }),
|
|
@@ -20410,9 +21359,9 @@ var SigninOtpConfirmCommand = class extends Command {
|
|
|
20410
21359
|
static summary = "Confirm OTP sign-in";
|
|
20411
21360
|
static examples = ["<%= config.bin %> signin otp confirm user@example.com 123456", "<%= config.bin %> signin otp confirm user@example.com 123456 --org-id 00000000-0000-4000-8000-000000000000"];
|
|
20412
21361
|
static flags = {
|
|
20413
|
-
"api-base-url
|
|
21362
|
+
"api-base-url": Flags.string({
|
|
20414
21363
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
20415
|
-
env: "
|
|
21364
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
20416
21365
|
hidden: true
|
|
20417
21366
|
}),
|
|
20418
21367
|
force: Flags.boolean({
|
|
@@ -20489,9 +21438,9 @@ var SigninOtpResendCommand = class extends Command {
|
|
|
20489
21438
|
static description = "Resend the verification code for a pending OTP sign-in.";
|
|
20490
21439
|
static summary = "Resend OTP sign-in code";
|
|
20491
21440
|
static examples = ["<%= config.bin %> signin otp resend user@example.com"];
|
|
20492
|
-
static flags = { "api-base-url
|
|
21441
|
+
static flags = { "api-base-url": Flags.string({
|
|
20493
21442
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
20494
|
-
env: "
|
|
21443
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
20495
21444
|
hidden: true
|
|
20496
21445
|
}) };
|
|
20497
21446
|
async run() {
|
|
@@ -20577,14 +21526,9 @@ var WhoamiCommand = class WhoamiCommand extends Command {
|
|
|
20577
21526
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
20578
21527
|
env: "PRIMITIVE_API_KEY"
|
|
20579
21528
|
}),
|
|
20580
|
-
"api-base-url
|
|
20581
|
-
description:
|
|
20582
|
-
env: "
|
|
20583
|
-
hidden: true
|
|
20584
|
-
}),
|
|
20585
|
-
"api-base-url-2": Flags.string({
|
|
20586
|
-
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
20587
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
21529
|
+
"api-base-url": Flags.string({
|
|
21530
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
21531
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
20588
21532
|
hidden: true
|
|
20589
21533
|
}),
|
|
20590
21534
|
json: Flags.boolean({ description: "Print the full account JSON response. Default output hides setup and billing internals." }),
|
|
@@ -20595,8 +21539,7 @@ var WhoamiCommand = class WhoamiCommand extends Command {
|
|
|
20595
21539
|
await runWithTiming(flags.time, async () => {
|
|
20596
21540
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
20597
21541
|
apiKey: flags["api-key"],
|
|
20598
|
-
|
|
20599
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
21542
|
+
apiBaseUrl: flags["api-base-url"],
|
|
20600
21543
|
configDir: this.config.configDir
|
|
20601
21544
|
});
|
|
20602
21545
|
const result = await getAccount({
|
|
@@ -20927,6 +21870,7 @@ const COMMANDS = {
|
|
|
20927
21870
|
"emails:latest": EmailsLatestCommand,
|
|
20928
21871
|
"emails:watch": EmailsWatchCommand,
|
|
20929
21872
|
"emails:wait": EmailsWaitCommand,
|
|
21873
|
+
"semantic-search": SemanticSearchCommand,
|
|
20930
21874
|
"domains:zone-file": DomainsZoneFileCommand,
|
|
20931
21875
|
"domains:download-domain-zone-file": DomainsZoneFileCommand,
|
|
20932
21876
|
"inbox:setup": InboxSetupCommand,
|
|
@@ -20939,6 +21883,10 @@ const COMMANDS = {
|
|
|
20939
21883
|
"functions:set-secret": FunctionsSetSecretCommand,
|
|
20940
21884
|
"functions:test": FunctionsTestFunctionCommand,
|
|
20941
21885
|
"functions:test-function": FunctionsTestFunctionCommand,
|
|
21886
|
+
"functions:route-set": FunctionsRouteSetCommand,
|
|
21887
|
+
"functions:route-unset": FunctionsRouteUnsetCommand,
|
|
21888
|
+
"functions:route-get": FunctionsRouteGetCommand,
|
|
21889
|
+
"functions:routing-topology": FunctionsRoutingTopologyCommand,
|
|
20942
21890
|
...Object.fromEntries(Object.entries(CANONICAL_OPERATION_ALIASES).map(([alias, target]) => {
|
|
20943
21891
|
const command = generatedCommands[target];
|
|
20944
21892
|
if (!command) throw new Error(`Missing generated command target for alias ${alias}`);
|