@primitivedotdev/cli 0.34.0 → 0.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/{cli-config-D9nB6fOW.js → cli-config-DREZ2BxT.js} +41 -48
- package/dist/oclif/index.js +1341 -416
- package/dist/oclif/root-signup-hint.js +12 -11
- package/man/primitive.1 +2 -5
- package/package.json +7 -3
package/dist/oclif/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { Args, Command, Errors, Flags } from "@oclif/core";
|
|
1
|
+
import { A as createConfig, C as chatStatePath, D as saveActiveChatState, E as loadChatConversationByLocalId, O as PrimitiveApiClient, S as saveCliCredentials, T as loadActiveChatState, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as normalizeApiBaseUrl, c as resolveConfigEnvironment, d as validateCliHeaderName, f as validateCliHeaderValue, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, k as createClient, 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 deleteChatState, x as resolveCliAuth, y as loadCliCredentials } from "../cli-config-DREZ2BxT.js";
|
|
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";
|
|
5
5
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
@@ -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({
|
|
@@ -68,6 +68,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
68
68
|
resendCliSignupVerification: () => resendCliSignupVerification,
|
|
69
69
|
rotateWebhookSecret: () => rotateWebhookSecret,
|
|
70
70
|
searchEmails: () => searchEmails,
|
|
71
|
+
semanticSearch: () => semanticSearch,
|
|
71
72
|
sendEmail: () => sendEmail,
|
|
72
73
|
setFunctionSecret: () => setFunctionSecret,
|
|
73
74
|
startAgentSignup: () => startAgentSignup,
|
|
@@ -595,9 +596,9 @@ const downloadAttachments = (options) => (options.client ?? client).get({
|
|
|
595
596
|
* derivation (Reply-To, then From, then bare sender), and the
|
|
596
597
|
* `Re:` subject prefix are all derived server-side from the
|
|
597
598
|
* stored inbound row. The request body carries only the message
|
|
598
|
-
* body
|
|
599
|
-
*
|
|
600
|
-
* false`).
|
|
599
|
+
* body, optional From override, optional attachments, and optional
|
|
600
|
+
* `wait` flag; passing any header or recipient override is
|
|
601
|
+
* rejected by the schema (`additionalProperties: false`).
|
|
601
602
|
*
|
|
602
603
|
* Forwards through the same gates as `/send-mail`: the response
|
|
603
604
|
* status, error envelope, and `idempotent_replay` flag mirror
|
|
@@ -933,14 +934,13 @@ const getSendPermissions = (options) => (options?.client ?? client).get({
|
|
|
933
934
|
* the request returns once the relay accepts the message for delivery.
|
|
934
935
|
* Set `wait: true` to wait for the first downstream SMTP delivery outcome.
|
|
935
936
|
*
|
|
936
|
-
* **Host routing.** /send-mail is served by the
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
*
|
|
941
|
-
*
|
|
942
|
-
*
|
|
943
|
-
* route /send-mail to the attachments host automatically.
|
|
937
|
+
* **Host routing.** /send-mail is served by the canonical API host
|
|
938
|
+
* (`https://api.primitive.dev/v1`) so the request body can carry
|
|
939
|
+
* inline attachments up to ~30 MiB raw. The legacy dashboard
|
|
940
|
+
* compatibility host (`https://www.primitive.dev/api/v1`) also accepts
|
|
941
|
+
* /send-mail, but Vercel request body limits apply before proxying.
|
|
942
|
+
* The typed SDKs route /send-mail to the canonical API host
|
|
943
|
+
* automatically.
|
|
944
944
|
*
|
|
945
945
|
*/
|
|
946
946
|
const sendEmail = (options) => (options.client ?? client).post({
|
|
@@ -956,6 +956,42 @@ const sendEmail = (options) => (options.client ?? client).post({
|
|
|
956
956
|
}
|
|
957
957
|
});
|
|
958
958
|
/**
|
|
959
|
+
* Semantic search across received and sent mail
|
|
960
|
+
*
|
|
961
|
+
* Ranked search across both received and sent mail. The `mode`
|
|
962
|
+
* field selects the ranking strategy:
|
|
963
|
+
*
|
|
964
|
+
* - `keyword`: lexical full-text matching only (no embeddings).
|
|
965
|
+
* - `semantic`: meaning-based matching using vector embeddings.
|
|
966
|
+
* - `hybrid` (default): blends the semantic and keyword signals.
|
|
967
|
+
*
|
|
968
|
+
* Results are ordered by a relevance `score`. Every row reports the
|
|
969
|
+
* fields it matched (`matched_fields`), a match-centered excerpt per
|
|
970
|
+
* field (`snippets`), and a `score_breakdown` whose components account
|
|
971
|
+
* for the `score`. Page through results by passing the prior
|
|
972
|
+
* response's `meta.cursor` back as `cursor`.
|
|
973
|
+
*
|
|
974
|
+
* Requires the Pro plan and the `semantic_search_enabled`
|
|
975
|
+
* entitlement; callers without them receive `403`.
|
|
976
|
+
*
|
|
977
|
+
* Host routing: this operation is served only by the search host
|
|
978
|
+
* (`https://api.primitive.dev/v1`). The typed SDKs route it there
|
|
979
|
+
* automatically.
|
|
980
|
+
*
|
|
981
|
+
*/
|
|
982
|
+
const semanticSearch = (options) => (options.client ?? client).post({
|
|
983
|
+
security: [{
|
|
984
|
+
scheme: "bearer",
|
|
985
|
+
type: "http"
|
|
986
|
+
}],
|
|
987
|
+
url: "/semantic-search",
|
|
988
|
+
...options,
|
|
989
|
+
headers: {
|
|
990
|
+
...options.body !== void 0 && { "Content-Type": "application/json" },
|
|
991
|
+
...options.headers
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
/**
|
|
959
995
|
* List outbound sent emails
|
|
960
996
|
*
|
|
961
997
|
* Returns a paginated list of OUTBOUND emails the caller's
|
|
@@ -1346,11 +1382,11 @@ const openapiDocument = {
|
|
|
1346
1382
|
}
|
|
1347
1383
|
},
|
|
1348
1384
|
"servers": [{
|
|
1349
|
-
"url": "https://www.primitive.dev/api/v1",
|
|
1350
|
-
"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"
|
|
1351
|
-
}, {
|
|
1352
1385
|
"url": "https://api.primitive.dev/v1",
|
|
1353
|
-
"description": "
|
|
1386
|
+
"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"
|
|
1387
|
+
}, {
|
|
1388
|
+
"url": "https://www.primitive.dev/api/v1",
|
|
1389
|
+
"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"
|
|
1354
1390
|
}],
|
|
1355
1391
|
"security": [{ "BearerAuth": [] }],
|
|
1356
1392
|
"tags": [
|
|
@@ -1378,6 +1414,10 @@ const openapiDocument = {
|
|
|
1378
1414
|
"name": "Emails",
|
|
1379
1415
|
"description": "List, inspect, and manage received emails"
|
|
1380
1416
|
},
|
|
1417
|
+
{
|
|
1418
|
+
"name": "Search",
|
|
1419
|
+
"description": "Semantic and hybrid search across received and sent mail"
|
|
1420
|
+
},
|
|
1381
1421
|
{
|
|
1382
1422
|
"name": "Sending",
|
|
1383
1423
|
"description": "Send outbound emails through the Primitive API"
|
|
@@ -2366,7 +2406,14 @@ const openapiDocument = {
|
|
|
2366
2406
|
"post": {
|
|
2367
2407
|
"operationId": "replyToEmail",
|
|
2368
2408
|
"summary": "Reply to an inbound email",
|
|
2369
|
-
"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 and optional
|
|
2409
|
+
"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",
|
|
2410
|
+
"servers": [{
|
|
2411
|
+
"url": "https://api.primitive.dev/v1",
|
|
2412
|
+
"description": "Canonical API host (recommended)"
|
|
2413
|
+
}, {
|
|
2414
|
+
"url": "https://www.primitive.dev/api/v1",
|
|
2415
|
+
"description": "Legacy compatibility host (Vercel body limit applies)"
|
|
2416
|
+
}],
|
|
2370
2417
|
"tags": ["Sending"],
|
|
2371
2418
|
"requestBody": {
|
|
2372
2419
|
"required": true,
|
|
@@ -2774,13 +2821,13 @@ const openapiDocument = {
|
|
|
2774
2821
|
"/send-mail": { "post": {
|
|
2775
2822
|
"operationId": "sendEmail",
|
|
2776
2823
|
"summary": "Send outbound email",
|
|
2777
|
-
"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
|
|
2824
|
+
"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",
|
|
2778
2825
|
"servers": [{
|
|
2779
2826
|
"url": "https://api.primitive.dev/v1",
|
|
2780
|
-
"description": "
|
|
2827
|
+
"description": "Canonical API host (recommended)"
|
|
2781
2828
|
}, {
|
|
2782
2829
|
"url": "https://www.primitive.dev/api/v1",
|
|
2783
|
-
"description": "
|
|
2830
|
+
"description": "Legacy compatibility host (Vercel body limit applies)"
|
|
2784
2831
|
}],
|
|
2785
2832
|
"tags": ["Sending"],
|
|
2786
2833
|
"parameters": [{
|
|
@@ -2816,6 +2863,42 @@ const openapiDocument = {
|
|
|
2816
2863
|
"503": { "$ref": "#/components/responses/ServiceUnavailable" }
|
|
2817
2864
|
}
|
|
2818
2865
|
} },
|
|
2866
|
+
"/semantic-search": { "post": {
|
|
2867
|
+
"operationId": "semanticSearch",
|
|
2868
|
+
"summary": "Semantic search across received and sent mail",
|
|
2869
|
+
"description": "Ranked search across both received and sent mail. The `mode`\nfield selects the ranking strategy:\n\n- `keyword`: lexical full-text matching only (no embeddings).\n- `semantic`: meaning-based matching using vector embeddings.\n- `hybrid` (default): blends the semantic and keyword signals.\n\nResults are ordered by a relevance `score`. Every row reports the\nfields it matched (`matched_fields`), a match-centered excerpt per\nfield (`snippets`), and a `score_breakdown` whose components account\nfor the `score`. Page through results by passing the prior\nresponse's `meta.cursor` back as `cursor`.\n\nRequires the Pro plan and the `semantic_search_enabled`\nentitlement; callers without them receive `403`.\n\nHost routing: this operation is served only by the search host\n(`https://api.primitive.dev/v1`). The typed SDKs route it there\nautomatically.\n",
|
|
2870
|
+
"servers": [{
|
|
2871
|
+
"url": "https://api.primitive.dev/v1",
|
|
2872
|
+
"description": "Search host"
|
|
2873
|
+
}],
|
|
2874
|
+
"tags": ["Search"],
|
|
2875
|
+
"requestBody": {
|
|
2876
|
+
"required": true,
|
|
2877
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/SemanticSearchInput" } } }
|
|
2878
|
+
},
|
|
2879
|
+
"responses": {
|
|
2880
|
+
"200": {
|
|
2881
|
+
"description": "Ranked search results",
|
|
2882
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
2883
|
+
"type": "object",
|
|
2884
|
+
"properties": {
|
|
2885
|
+
"data": {
|
|
2886
|
+
"type": "array",
|
|
2887
|
+
"items": { "$ref": "#/components/schemas/SemanticSearchResult" }
|
|
2888
|
+
},
|
|
2889
|
+
"meta": { "$ref": "#/components/schemas/SemanticSearchMeta" }
|
|
2890
|
+
},
|
|
2891
|
+
"required": ["data", "meta"]
|
|
2892
|
+
}] } } }
|
|
2893
|
+
},
|
|
2894
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
2895
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
2896
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
2897
|
+
"429": { "$ref": "#/components/responses/RateLimited" },
|
|
2898
|
+
"500": { "$ref": "#/components/responses/InternalError" },
|
|
2899
|
+
"503": { "$ref": "#/components/responses/ServiceUnavailable" }
|
|
2900
|
+
}
|
|
2901
|
+
} },
|
|
2819
2902
|
"/sent-emails": { "get": {
|
|
2820
2903
|
"operationId": "listSentEmails",
|
|
2821
2904
|
"summary": "List outbound sent emails",
|
|
@@ -5660,6 +5743,236 @@ const openapiDocument = {
|
|
|
5660
5743
|
"body_size_bytes"
|
|
5661
5744
|
]
|
|
5662
5745
|
},
|
|
5746
|
+
"SemanticSearchField": {
|
|
5747
|
+
"type": "string",
|
|
5748
|
+
"enum": [
|
|
5749
|
+
"subject",
|
|
5750
|
+
"headers",
|
|
5751
|
+
"addresses",
|
|
5752
|
+
"body"
|
|
5753
|
+
],
|
|
5754
|
+
"description": "A searchable email field."
|
|
5755
|
+
},
|
|
5756
|
+
"SemanticSearchInput": {
|
|
5757
|
+
"type": "object",
|
|
5758
|
+
"properties": {
|
|
5759
|
+
"query": {
|
|
5760
|
+
"type": "string",
|
|
5761
|
+
"minLength": 1,
|
|
5762
|
+
"maxLength": 2048,
|
|
5763
|
+
"description": "Free-text query. Required for `semantic` and `hybrid` modes;\noptional for `keyword` mode.\n"
|
|
5764
|
+
},
|
|
5765
|
+
"mode": {
|
|
5766
|
+
"type": "string",
|
|
5767
|
+
"enum": [
|
|
5768
|
+
"hybrid",
|
|
5769
|
+
"semantic",
|
|
5770
|
+
"keyword"
|
|
5771
|
+
],
|
|
5772
|
+
"default": "hybrid",
|
|
5773
|
+
"description": "Ranking strategy. `keyword` is lexical only, `semantic` is\nembedding-based, `hybrid` blends both.\n"
|
|
5774
|
+
},
|
|
5775
|
+
"corpus": {
|
|
5776
|
+
"type": "array",
|
|
5777
|
+
"items": {
|
|
5778
|
+
"type": "string",
|
|
5779
|
+
"enum": ["inbound", "outbound"]
|
|
5780
|
+
},
|
|
5781
|
+
"minItems": 1,
|
|
5782
|
+
"maxItems": 2,
|
|
5783
|
+
"description": "Which mail to search. Defaults to both received (`inbound`)\nand sent (`outbound`).\n"
|
|
5784
|
+
},
|
|
5785
|
+
"search_in": {
|
|
5786
|
+
"type": "array",
|
|
5787
|
+
"items": { "$ref": "#/components/schemas/SemanticSearchField" },
|
|
5788
|
+
"description": "Restrict matching to these fields. Defaults to all."
|
|
5789
|
+
},
|
|
5790
|
+
"exclude": {
|
|
5791
|
+
"type": "array",
|
|
5792
|
+
"items": { "$ref": "#/components/schemas/SemanticSearchField" },
|
|
5793
|
+
"description": "Exclude these fields from matching."
|
|
5794
|
+
},
|
|
5795
|
+
"date_from": {
|
|
5796
|
+
"type": "string",
|
|
5797
|
+
"format": "date-time",
|
|
5798
|
+
"description": "Only include mail at or after this timestamp."
|
|
5799
|
+
},
|
|
5800
|
+
"date_to": {
|
|
5801
|
+
"type": "string",
|
|
5802
|
+
"format": "date-time",
|
|
5803
|
+
"description": "Only include mail at or before this timestamp."
|
|
5804
|
+
},
|
|
5805
|
+
"include": {
|
|
5806
|
+
"type": "array",
|
|
5807
|
+
"items": {
|
|
5808
|
+
"type": "string",
|
|
5809
|
+
"enum": ["coverage"]
|
|
5810
|
+
},
|
|
5811
|
+
"description": "Opt-in extras. `coverage` adds an index-coverage snapshot to\n`meta`. Matched fields, snippets, and the score breakdown are\nalways returned regardless of this field.\n"
|
|
5812
|
+
},
|
|
5813
|
+
"limit": {
|
|
5814
|
+
"type": "integer",
|
|
5815
|
+
"minimum": 1,
|
|
5816
|
+
"maximum": 100,
|
|
5817
|
+
"default": 10,
|
|
5818
|
+
"description": "Maximum number of results to return."
|
|
5819
|
+
},
|
|
5820
|
+
"cursor": {
|
|
5821
|
+
"type": "string",
|
|
5822
|
+
"description": "Opaque pagination cursor from a prior response's `meta.cursor`."
|
|
5823
|
+
}
|
|
5824
|
+
}
|
|
5825
|
+
},
|
|
5826
|
+
"SemanticSearchSnippet": {
|
|
5827
|
+
"type": "object",
|
|
5828
|
+
"properties": {
|
|
5829
|
+
"field": {
|
|
5830
|
+
"type": "string",
|
|
5831
|
+
"description": "The field this excerpt came from."
|
|
5832
|
+
},
|
|
5833
|
+
"text": {
|
|
5834
|
+
"type": "string",
|
|
5835
|
+
"description": "Plain-text excerpt centered on the match (no markup)."
|
|
5836
|
+
}
|
|
5837
|
+
},
|
|
5838
|
+
"required": ["field", "text"]
|
|
5839
|
+
},
|
|
5840
|
+
"SemanticSearchScoreBreakdown": {
|
|
5841
|
+
"type": "object",
|
|
5842
|
+
"description": "Additive contributions to `score`. `semantic` and `keyword` are the\nraw signals times the mode's weight (null when not applicable);\nthese plus `field_boost` and `recency` sum to `score` before each\nvalue is independently rounded to 5 decimal places.\n",
|
|
5843
|
+
"properties": {
|
|
5844
|
+
"semantic": { "type": ["number", "null"] },
|
|
5845
|
+
"keyword": { "type": ["number", "null"] },
|
|
5846
|
+
"field_boost": { "type": "number" },
|
|
5847
|
+
"recency": { "type": "number" }
|
|
5848
|
+
},
|
|
5849
|
+
"required": [
|
|
5850
|
+
"semantic",
|
|
5851
|
+
"keyword",
|
|
5852
|
+
"field_boost",
|
|
5853
|
+
"recency"
|
|
5854
|
+
]
|
|
5855
|
+
},
|
|
5856
|
+
"SemanticSearchResult": {
|
|
5857
|
+
"type": "object",
|
|
5858
|
+
"properties": {
|
|
5859
|
+
"source_type": {
|
|
5860
|
+
"type": "string",
|
|
5861
|
+
"enum": ["inbound_email", "sent_email"],
|
|
5862
|
+
"description": "Whether this row is a received or sent message."
|
|
5863
|
+
},
|
|
5864
|
+
"id": {
|
|
5865
|
+
"type": "string",
|
|
5866
|
+
"description": "Message id. Combine with `api_url` to fetch the full record."
|
|
5867
|
+
},
|
|
5868
|
+
"subject": { "type": ["string", "null"] },
|
|
5869
|
+
"from": { "type": ["string", "null"] },
|
|
5870
|
+
"to": { "type": ["string", "null"] },
|
|
5871
|
+
"timestamp": {
|
|
5872
|
+
"type": "string",
|
|
5873
|
+
"description": "Message timestamp (received_at for inbound, created_at for sent)."
|
|
5874
|
+
},
|
|
5875
|
+
"status": {
|
|
5876
|
+
"type": "string",
|
|
5877
|
+
"description": "Lifecycle status of the message."
|
|
5878
|
+
},
|
|
5879
|
+
"score": {
|
|
5880
|
+
"type": "number",
|
|
5881
|
+
"description": "Overall relevance score; the `score_breakdown` components account for it."
|
|
5882
|
+
},
|
|
5883
|
+
"semantic_score": {
|
|
5884
|
+
"type": ["number", "null"],
|
|
5885
|
+
"description": "Raw semantic similarity signal, or null when not applicable."
|
|
5886
|
+
},
|
|
5887
|
+
"keyword_score": {
|
|
5888
|
+
"type": ["number", "null"],
|
|
5889
|
+
"description": "Raw keyword (lexical) signal, or null when not applicable."
|
|
5890
|
+
},
|
|
5891
|
+
"matched_fields": {
|
|
5892
|
+
"type": "array",
|
|
5893
|
+
"items": { "$ref": "#/components/schemas/SemanticSearchField" },
|
|
5894
|
+
"description": "Fields where the query matched."
|
|
5895
|
+
},
|
|
5896
|
+
"snippets": {
|
|
5897
|
+
"type": "array",
|
|
5898
|
+
"items": { "$ref": "#/components/schemas/SemanticSearchSnippet" },
|
|
5899
|
+
"description": "Match-centered excerpts, one per matched field."
|
|
5900
|
+
},
|
|
5901
|
+
"score_breakdown": { "$ref": "#/components/schemas/SemanticSearchScoreBreakdown" },
|
|
5902
|
+
"api_url": {
|
|
5903
|
+
"type": ["string", "null"],
|
|
5904
|
+
"description": "Relative API path to fetch the full message."
|
|
5905
|
+
}
|
|
5906
|
+
},
|
|
5907
|
+
"required": [
|
|
5908
|
+
"source_type",
|
|
5909
|
+
"id",
|
|
5910
|
+
"subject",
|
|
5911
|
+
"from",
|
|
5912
|
+
"to",
|
|
5913
|
+
"timestamp",
|
|
5914
|
+
"status",
|
|
5915
|
+
"score",
|
|
5916
|
+
"semantic_score",
|
|
5917
|
+
"keyword_score",
|
|
5918
|
+
"matched_fields",
|
|
5919
|
+
"snippets",
|
|
5920
|
+
"score_breakdown",
|
|
5921
|
+
"api_url"
|
|
5922
|
+
]
|
|
5923
|
+
},
|
|
5924
|
+
"SemanticSearchCoverage": {
|
|
5925
|
+
"type": "object",
|
|
5926
|
+
"description": "Index-coverage snapshot for the org, returned only when the `coverage` include option is requested.",
|
|
5927
|
+
"properties": {
|
|
5928
|
+
"embedded_chunks": { "type": "integer" },
|
|
5929
|
+
"pending_chunks": { "type": "integer" },
|
|
5930
|
+
"skipped_plan_chunks": { "type": "integer" },
|
|
5931
|
+
"skipped_quota_chunks": { "type": "integer" },
|
|
5932
|
+
"unsupported_attachment_chunks": { "type": "integer" },
|
|
5933
|
+
"failed_chunks": { "type": "integer" }
|
|
5934
|
+
},
|
|
5935
|
+
"required": [
|
|
5936
|
+
"embedded_chunks",
|
|
5937
|
+
"pending_chunks",
|
|
5938
|
+
"skipped_plan_chunks",
|
|
5939
|
+
"skipped_quota_chunks",
|
|
5940
|
+
"unsupported_attachment_chunks",
|
|
5941
|
+
"failed_chunks"
|
|
5942
|
+
]
|
|
5943
|
+
},
|
|
5944
|
+
"SemanticSearchMeta": {
|
|
5945
|
+
"type": "object",
|
|
5946
|
+
"properties": {
|
|
5947
|
+
"limit": {
|
|
5948
|
+
"type": "integer",
|
|
5949
|
+
"description": "Page size used for this request."
|
|
5950
|
+
},
|
|
5951
|
+
"cursor": {
|
|
5952
|
+
"type": ["string", "null"],
|
|
5953
|
+
"description": "Cursor for the next page, or null if there are no more results."
|
|
5954
|
+
},
|
|
5955
|
+
"mode": {
|
|
5956
|
+
"type": "string",
|
|
5957
|
+
"enum": [
|
|
5958
|
+
"hybrid",
|
|
5959
|
+
"semantic",
|
|
5960
|
+
"keyword"
|
|
5961
|
+
],
|
|
5962
|
+
"description": "Ranking mode used for this response."
|
|
5963
|
+
},
|
|
5964
|
+
"coverage": {
|
|
5965
|
+
"oneOf": [{ "$ref": "#/components/schemas/SemanticSearchCoverage" }, { "type": "null" }],
|
|
5966
|
+
"description": "Index-coverage snapshot, present only when requested via\n`include: [coverage]`; otherwise null.\n"
|
|
5967
|
+
}
|
|
5968
|
+
},
|
|
5969
|
+
"required": [
|
|
5970
|
+
"limit",
|
|
5971
|
+
"cursor",
|
|
5972
|
+
"mode",
|
|
5973
|
+
"coverage"
|
|
5974
|
+
]
|
|
5975
|
+
},
|
|
5663
5976
|
"SentEmailDetail": {
|
|
5664
5977
|
"description": "Full sent-email record, including `body_text` and\n`body_html`. Returned by /sent-emails/{id}.\n",
|
|
5665
5978
|
"allOf": [{ "$ref": "#/components/schemas/SentEmailSummary" }, {
|
|
@@ -5698,6 +6011,12 @@ const openapiDocument = {
|
|
|
5698
6011
|
"wait": {
|
|
5699
6012
|
"type": "boolean",
|
|
5700
6013
|
"description": "When true, wait for the first downstream SMTP delivery outcome before returning, mirroring the send-mail `wait` semantics."
|
|
6014
|
+
},
|
|
6015
|
+
"attachments": {
|
|
6016
|
+
"type": "array",
|
|
6017
|
+
"maxItems": 100,
|
|
6018
|
+
"description": "Inline attachments for this reply. Use https://api.primitive.dev/v1 for replies with attachments. Combined raw decoded attachment bytes must be at most 31457280.",
|
|
6019
|
+
"items": { "$ref": "#/components/schemas/SendMailAttachment" }
|
|
5701
6020
|
}
|
|
5702
6021
|
}
|
|
5703
6022
|
},
|
|
@@ -11458,35 +11777,247 @@ const operationManifest = [
|
|
|
11458
11777
|
},
|
|
11459
11778
|
{
|
|
11460
11779
|
"binaryResponse": false,
|
|
11461
|
-
"bodyRequired":
|
|
11462
|
-
"command": "
|
|
11463
|
-
"description": "
|
|
11464
|
-
"hasJsonBody":
|
|
11465
|
-
"method": "
|
|
11466
|
-
"operationId": "
|
|
11467
|
-
"path": "/
|
|
11780
|
+
"bodyRequired": true,
|
|
11781
|
+
"command": "semantic-search",
|
|
11782
|
+
"description": "Ranked search across both received and sent mail. The `mode`\nfield selects the ranking strategy:\n\n- `keyword`: lexical full-text matching only (no embeddings).\n- `semantic`: meaning-based matching using vector embeddings.\n- `hybrid` (default): blends the semantic and keyword signals.\n\nResults are ordered by a relevance `score`. Every row reports the\nfields it matched (`matched_fields`), a match-centered excerpt per\nfield (`snippets`), and a `score_breakdown` whose components account\nfor the `score`. Page through results by passing the prior\nresponse's `meta.cursor` back as `cursor`.\n\nRequires the Pro plan and the `semantic_search_enabled`\nentitlement; callers without them receive `403`.\n\nHost routing: this operation is served only by the search host\n(`https://api.primitive.dev/v1`). The typed SDKs route it there\nautomatically.\n",
|
|
11783
|
+
"hasJsonBody": true,
|
|
11784
|
+
"method": "POST",
|
|
11785
|
+
"operationId": "semanticSearch",
|
|
11786
|
+
"path": "/semantic-search",
|
|
11468
11787
|
"pathParams": [],
|
|
11469
11788
|
"queryParams": [],
|
|
11470
|
-
"requestSchema":
|
|
11789
|
+
"requestSchema": {
|
|
11790
|
+
"type": "object",
|
|
11791
|
+
"properties": {
|
|
11792
|
+
"query": {
|
|
11793
|
+
"type": "string",
|
|
11794
|
+
"minLength": 1,
|
|
11795
|
+
"maxLength": 2048,
|
|
11796
|
+
"description": "Free-text query. Required for `semantic` and `hybrid` modes;\noptional for `keyword` mode.\n"
|
|
11797
|
+
},
|
|
11798
|
+
"mode": {
|
|
11799
|
+
"type": "string",
|
|
11800
|
+
"enum": [
|
|
11801
|
+
"hybrid",
|
|
11802
|
+
"semantic",
|
|
11803
|
+
"keyword"
|
|
11804
|
+
],
|
|
11805
|
+
"default": "hybrid",
|
|
11806
|
+
"description": "Ranking strategy. `keyword` is lexical only, `semantic` is\nembedding-based, `hybrid` blends both.\n"
|
|
11807
|
+
},
|
|
11808
|
+
"corpus": {
|
|
11809
|
+
"type": "array",
|
|
11810
|
+
"items": {
|
|
11811
|
+
"type": "string",
|
|
11812
|
+
"enum": ["inbound", "outbound"]
|
|
11813
|
+
},
|
|
11814
|
+
"minItems": 1,
|
|
11815
|
+
"maxItems": 2,
|
|
11816
|
+
"description": "Which mail to search. Defaults to both received (`inbound`)\nand sent (`outbound`).\n"
|
|
11817
|
+
},
|
|
11818
|
+
"search_in": {
|
|
11819
|
+
"type": "array",
|
|
11820
|
+
"items": {
|
|
11821
|
+
"type": "string",
|
|
11822
|
+
"enum": [
|
|
11823
|
+
"subject",
|
|
11824
|
+
"headers",
|
|
11825
|
+
"addresses",
|
|
11826
|
+
"body"
|
|
11827
|
+
],
|
|
11828
|
+
"description": "A searchable email field."
|
|
11829
|
+
},
|
|
11830
|
+
"description": "Restrict matching to these fields. Defaults to all."
|
|
11831
|
+
},
|
|
11832
|
+
"exclude": {
|
|
11833
|
+
"type": "array",
|
|
11834
|
+
"items": {
|
|
11835
|
+
"type": "string",
|
|
11836
|
+
"enum": [
|
|
11837
|
+
"subject",
|
|
11838
|
+
"headers",
|
|
11839
|
+
"addresses",
|
|
11840
|
+
"body"
|
|
11841
|
+
],
|
|
11842
|
+
"description": "A searchable email field."
|
|
11843
|
+
},
|
|
11844
|
+
"description": "Exclude these fields from matching."
|
|
11845
|
+
},
|
|
11846
|
+
"date_from": {
|
|
11847
|
+
"type": "string",
|
|
11848
|
+
"format": "date-time",
|
|
11849
|
+
"description": "Only include mail at or after this timestamp."
|
|
11850
|
+
},
|
|
11851
|
+
"date_to": {
|
|
11852
|
+
"type": "string",
|
|
11853
|
+
"format": "date-time",
|
|
11854
|
+
"description": "Only include mail at or before this timestamp."
|
|
11855
|
+
},
|
|
11856
|
+
"include": {
|
|
11857
|
+
"type": "array",
|
|
11858
|
+
"items": {
|
|
11859
|
+
"type": "string",
|
|
11860
|
+
"enum": ["coverage"]
|
|
11861
|
+
},
|
|
11862
|
+
"description": "Opt-in extras. `coverage` adds an index-coverage snapshot to\n`meta`. Matched fields, snippets, and the score breakdown are\nalways returned regardless of this field.\n"
|
|
11863
|
+
},
|
|
11864
|
+
"limit": {
|
|
11865
|
+
"type": "integer",
|
|
11866
|
+
"minimum": 1,
|
|
11867
|
+
"maximum": 100,
|
|
11868
|
+
"default": 10,
|
|
11869
|
+
"description": "Maximum number of results to return."
|
|
11870
|
+
},
|
|
11871
|
+
"cursor": {
|
|
11872
|
+
"type": "string",
|
|
11873
|
+
"description": "Opaque pagination cursor from a prior response's `meta.cursor`."
|
|
11874
|
+
}
|
|
11875
|
+
}
|
|
11876
|
+
},
|
|
11471
11877
|
"responseSchema": {
|
|
11472
11878
|
"type": "array",
|
|
11473
11879
|
"items": {
|
|
11474
|
-
"
|
|
11475
|
-
"
|
|
11476
|
-
"
|
|
11477
|
-
|
|
11478
|
-
"
|
|
11479
|
-
"
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
{
|
|
11486
|
-
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
|
|
11880
|
+
"type": "object",
|
|
11881
|
+
"properties": {
|
|
11882
|
+
"source_type": {
|
|
11883
|
+
"type": "string",
|
|
11884
|
+
"enum": ["inbound_email", "sent_email"],
|
|
11885
|
+
"description": "Whether this row is a received or sent message."
|
|
11886
|
+
},
|
|
11887
|
+
"id": {
|
|
11888
|
+
"type": "string",
|
|
11889
|
+
"description": "Message id. Combine with `api_url` to fetch the full record."
|
|
11890
|
+
},
|
|
11891
|
+
"subject": { "type": ["string", "null"] },
|
|
11892
|
+
"from": { "type": ["string", "null"] },
|
|
11893
|
+
"to": { "type": ["string", "null"] },
|
|
11894
|
+
"timestamp": {
|
|
11895
|
+
"type": "string",
|
|
11896
|
+
"description": "Message timestamp (received_at for inbound, created_at for sent)."
|
|
11897
|
+
},
|
|
11898
|
+
"status": {
|
|
11899
|
+
"type": "string",
|
|
11900
|
+
"description": "Lifecycle status of the message."
|
|
11901
|
+
},
|
|
11902
|
+
"score": {
|
|
11903
|
+
"type": "number",
|
|
11904
|
+
"description": "Overall relevance score; the `score_breakdown` components account for it."
|
|
11905
|
+
},
|
|
11906
|
+
"semantic_score": {
|
|
11907
|
+
"type": ["number", "null"],
|
|
11908
|
+
"description": "Raw semantic similarity signal, or null when not applicable."
|
|
11909
|
+
},
|
|
11910
|
+
"keyword_score": {
|
|
11911
|
+
"type": ["number", "null"],
|
|
11912
|
+
"description": "Raw keyword (lexical) signal, or null when not applicable."
|
|
11913
|
+
},
|
|
11914
|
+
"matched_fields": {
|
|
11915
|
+
"type": "array",
|
|
11916
|
+
"items": {
|
|
11917
|
+
"type": "string",
|
|
11918
|
+
"enum": [
|
|
11919
|
+
"subject",
|
|
11920
|
+
"headers",
|
|
11921
|
+
"addresses",
|
|
11922
|
+
"body"
|
|
11923
|
+
],
|
|
11924
|
+
"description": "A searchable email field."
|
|
11925
|
+
},
|
|
11926
|
+
"description": "Fields where the query matched."
|
|
11927
|
+
},
|
|
11928
|
+
"snippets": {
|
|
11929
|
+
"type": "array",
|
|
11930
|
+
"items": {
|
|
11931
|
+
"type": "object",
|
|
11932
|
+
"properties": {
|
|
11933
|
+
"field": {
|
|
11934
|
+
"type": "string",
|
|
11935
|
+
"description": "The field this excerpt came from."
|
|
11936
|
+
},
|
|
11937
|
+
"text": {
|
|
11938
|
+
"type": "string",
|
|
11939
|
+
"description": "Plain-text excerpt centered on the match (no markup)."
|
|
11940
|
+
}
|
|
11941
|
+
},
|
|
11942
|
+
"required": ["field", "text"]
|
|
11943
|
+
},
|
|
11944
|
+
"description": "Match-centered excerpts, one per matched field."
|
|
11945
|
+
},
|
|
11946
|
+
"score_breakdown": {
|
|
11947
|
+
"type": "object",
|
|
11948
|
+
"description": "Additive contributions to `score`. `semantic` and `keyword` are the\nraw signals times the mode's weight (null when not applicable);\nthese plus `field_boost` and `recency` sum to `score` before each\nvalue is independently rounded to 5 decimal places.\n",
|
|
11949
|
+
"properties": {
|
|
11950
|
+
"semantic": { "type": ["number", "null"] },
|
|
11951
|
+
"keyword": { "type": ["number", "null"] },
|
|
11952
|
+
"field_boost": { "type": "number" },
|
|
11953
|
+
"recency": { "type": "number" }
|
|
11954
|
+
},
|
|
11955
|
+
"required": [
|
|
11956
|
+
"semantic",
|
|
11957
|
+
"keyword",
|
|
11958
|
+
"field_boost",
|
|
11959
|
+
"recency"
|
|
11960
|
+
]
|
|
11961
|
+
},
|
|
11962
|
+
"api_url": {
|
|
11963
|
+
"type": ["string", "null"],
|
|
11964
|
+
"description": "Relative API path to fetch the full message."
|
|
11965
|
+
}
|
|
11966
|
+
},
|
|
11967
|
+
"required": [
|
|
11968
|
+
"source_type",
|
|
11969
|
+
"id",
|
|
11970
|
+
"subject",
|
|
11971
|
+
"from",
|
|
11972
|
+
"to",
|
|
11973
|
+
"timestamp",
|
|
11974
|
+
"status",
|
|
11975
|
+
"score",
|
|
11976
|
+
"semantic_score",
|
|
11977
|
+
"keyword_score",
|
|
11978
|
+
"matched_fields",
|
|
11979
|
+
"snippets",
|
|
11980
|
+
"score_breakdown",
|
|
11981
|
+
"api_url"
|
|
11982
|
+
]
|
|
11983
|
+
}
|
|
11984
|
+
},
|
|
11985
|
+
"sdkName": "semanticSearch",
|
|
11986
|
+
"summary": "Semantic search across received and sent mail",
|
|
11987
|
+
"tag": "Search",
|
|
11988
|
+
"tagCommand": "search"
|
|
11989
|
+
},
|
|
11990
|
+
{
|
|
11991
|
+
"binaryResponse": false,
|
|
11992
|
+
"bodyRequired": false,
|
|
11993
|
+
"command": "get-send-permissions",
|
|
11994
|
+
"description": "Returns a flat list of rules describing every recipient the\ncaller may send to. Each rule has a `type`, a kind-specific\npayload, and a human-readable `description`. If any rule\nmatches the recipient, /send-mail will accept the send under\nthe recipient-scope check.\n\nThe endpoint is the answer to \"where can I send\" without\nexposing internal entitlement names. Agents that don't\nrecognize a `type` can still read the `description` prose\nand act on it.\n\nRule kinds, ordered broadest-first so an agent can stop\nscanning at the first match:\n\n 1. `any_recipient` (one entry, only when the org can send\n anywhere): every other rule below it is redundant.\n 2. `managed_zone` (always emitted, one per Primitive-managed\n zone): sends to any address at *.primitive.email or\n *.email.works always succeed; no entitlement required.\n 3. `your_domain` (one per active verified outbound domain\n owned by the org): sends to that domain are approved.\n 4. `address` (one per address that has authenticated\n inbound mail to the org, capped at `meta.address_cap`):\n sends to that exact address are approved.\n\nThe list is informational, not an authorization check.\n/send-mail remains the source of truth on whether an\nindividual send will succeed (it also enforces the\nfrom-address and the `send_mail` entitlement, which are\nnot recipient-scope concerns and are not represented here).\n",
|
|
11995
|
+
"hasJsonBody": false,
|
|
11996
|
+
"method": "GET",
|
|
11997
|
+
"operationId": "getSendPermissions",
|
|
11998
|
+
"path": "/send-permissions",
|
|
11999
|
+
"pathParams": [],
|
|
12000
|
+
"queryParams": [],
|
|
12001
|
+
"requestSchema": null,
|
|
12002
|
+
"responseSchema": {
|
|
12003
|
+
"type": "array",
|
|
12004
|
+
"items": {
|
|
12005
|
+
"description": "One recipient-scope rule describing a destination the caller\nmay send to. Discriminated on `type`. Each rule carries a\nhuman-prose `description` field intended for display.\n\nRule kinds are stable within an SDK release. A response\ncontaining a `type` value not enumerated in this schema\nmeans the server is running a newer version than the SDK;\nupgrade the SDK to the release that matches the server's\nschema. Strict-parsing SDKs (Go, Python) will raise a\ndecode error in that case rather than silently dropping\nthe unknown rule, since silent drops would let an outbound\nagent reason from an incomplete view of its own permissions.\n",
|
|
12006
|
+
"discriminator": {
|
|
12007
|
+
"propertyName": "type",
|
|
12008
|
+
"mapping": {
|
|
12009
|
+
"any_recipient": "#/components/schemas/SendPermissionAnyRecipient",
|
|
12010
|
+
"managed_zone": "#/components/schemas/SendPermissionManagedZone",
|
|
12011
|
+
"your_domain": "#/components/schemas/SendPermissionYourDomain",
|
|
12012
|
+
"address": "#/components/schemas/SendPermissionAddress"
|
|
12013
|
+
}
|
|
12014
|
+
},
|
|
12015
|
+
"oneOf": [
|
|
12016
|
+
{
|
|
12017
|
+
"type": "object",
|
|
12018
|
+
"description": "The caller can send to any recipient. When this rule is\npresent, every other rule in the response is redundant.\n",
|
|
12019
|
+
"properties": {
|
|
12020
|
+
"type": {
|
|
11490
12021
|
"type": "string",
|
|
11491
12022
|
"enum": ["any_recipient"]
|
|
11492
12023
|
},
|
|
@@ -12108,7 +12639,7 @@ const operationManifest = [
|
|
|
12108
12639
|
"binaryResponse": false,
|
|
12109
12640
|
"bodyRequired": true,
|
|
12110
12641
|
"command": "reply-to-email",
|
|
12111
|
-
"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 and optional
|
|
12642
|
+
"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",
|
|
12112
12643
|
"hasJsonBody": true,
|
|
12113
12644
|
"method": "POST",
|
|
12114
12645
|
"operationId": "replyToEmail",
|
|
@@ -12143,6 +12674,36 @@ const operationManifest = [
|
|
|
12143
12674
|
"wait": {
|
|
12144
12675
|
"type": "boolean",
|
|
12145
12676
|
"description": "When true, wait for the first downstream SMTP delivery outcome before returning, mirroring the send-mail `wait` semantics."
|
|
12677
|
+
},
|
|
12678
|
+
"attachments": {
|
|
12679
|
+
"type": "array",
|
|
12680
|
+
"maxItems": 100,
|
|
12681
|
+
"description": "Inline attachments for this reply. Use https://api.primitive.dev/v1 for replies with attachments. Combined raw decoded attachment bytes must be at most 31457280.",
|
|
12682
|
+
"items": {
|
|
12683
|
+
"type": "object",
|
|
12684
|
+
"additionalProperties": false,
|
|
12685
|
+
"properties": {
|
|
12686
|
+
"filename": {
|
|
12687
|
+
"type": "string",
|
|
12688
|
+
"minLength": 1,
|
|
12689
|
+
"maxLength": 255,
|
|
12690
|
+
"description": "Attachment filename. Control characters are rejected."
|
|
12691
|
+
},
|
|
12692
|
+
"content_type": {
|
|
12693
|
+
"type": "string",
|
|
12694
|
+
"minLength": 1,
|
|
12695
|
+
"maxLength": 255,
|
|
12696
|
+
"description": "Optional MIME content type. Control characters are rejected."
|
|
12697
|
+
},
|
|
12698
|
+
"content_base64": {
|
|
12699
|
+
"type": "string",
|
|
12700
|
+
"minLength": 1,
|
|
12701
|
+
"maxLength": 44040192,
|
|
12702
|
+
"description": "Base64-encoded attachment bytes."
|
|
12703
|
+
}
|
|
12704
|
+
},
|
|
12705
|
+
"required": ["filename", "content_base64"]
|
|
12706
|
+
}
|
|
12146
12707
|
}
|
|
12147
12708
|
}
|
|
12148
12709
|
},
|
|
@@ -12243,7 +12804,7 @@ const operationManifest = [
|
|
|
12243
12804
|
"binaryResponse": false,
|
|
12244
12805
|
"bodyRequired": true,
|
|
12245
12806
|
"command": "send-email",
|
|
12246
|
-
"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
|
|
12807
|
+
"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",
|
|
12247
12808
|
"hasJsonBody": true,
|
|
12248
12809
|
"method": "POST",
|
|
12249
12810
|
"operationId": "sendEmail",
|
|
@@ -12740,19 +13301,15 @@ function cliApiHeadersFromEnv(env = process.env) {
|
|
|
12740
13301
|
}
|
|
12741
13302
|
function resolveCliApiRequestConfig(params) {
|
|
12742
13303
|
const currentEnvironment = resolveConfigEnvironment(loadCliConfig(params.configDir));
|
|
12743
|
-
const
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
const apiBaseUrl1 = params.apiBaseUrl1 !== void 0 ? normalizeApiBaseUrl1(params.apiBaseUrl1) : configuredApiBaseUrl1;
|
|
12747
|
-
const apiBaseUrl2 = params.apiBaseUrl2 !== void 0 ? normalizeApiBaseUrl2(params.apiBaseUrl2) : configuredApiBaseUrl2;
|
|
13304
|
+
const configuredApiBaseUrl = currentEnvironment?.config.api_base_url;
|
|
13305
|
+
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 });
|
|
13306
|
+
const apiBaseUrl = params.apiBaseUrl !== void 0 ? normalizeApiBaseUrl(params.apiBaseUrl) : configuredApiBaseUrl;
|
|
12748
13307
|
return {
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
baseUrlOverridden: apiBaseUrl1 !== void 0 || apiBaseUrl2 !== void 0,
|
|
13308
|
+
apiBaseUrl,
|
|
13309
|
+
baseUrlOverridden: apiBaseUrl !== void 0,
|
|
12752
13310
|
environmentName: currentEnvironment?.name ?? null,
|
|
12753
13311
|
headers: mergeHeaders(currentEnvironment?.config.headers, cliApiHeadersFromEnv(params.env)),
|
|
12754
|
-
|
|
12755
|
-
resolvedApiBaseUrl2: normalizeApiBaseUrl2(apiBaseUrl2)
|
|
13312
|
+
resolvedApiBaseUrl: normalizeApiBaseUrl(apiBaseUrl)
|
|
12756
13313
|
};
|
|
12757
13314
|
}
|
|
12758
13315
|
function createCliApiClient(params) {
|
|
@@ -12760,15 +13317,14 @@ function createCliApiClient(params) {
|
|
|
12760
13317
|
return {
|
|
12761
13318
|
apiClient: new PrimitiveApiClient({
|
|
12762
13319
|
apiKey: params.apiKey,
|
|
12763
|
-
|
|
12764
|
-
apiBaseUrl2: requestConfig.resolvedApiBaseUrl2,
|
|
13320
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
12765
13321
|
headers: requestConfig.headers
|
|
12766
13322
|
}),
|
|
12767
13323
|
requestConfig
|
|
12768
13324
|
};
|
|
12769
13325
|
}
|
|
12770
|
-
function oauthTokenEndpoint(
|
|
12771
|
-
const url = new URL(
|
|
13326
|
+
function oauthTokenEndpoint(apiBaseUrl) {
|
|
13327
|
+
const url = new URL(apiBaseUrl);
|
|
12772
13328
|
url.pathname = "/oauth/token";
|
|
12773
13329
|
url.search = "";
|
|
12774
13330
|
url.hash = "";
|
|
@@ -12816,7 +13372,7 @@ async function refreshStoredCliCredentials(params) {
|
|
|
12816
13372
|
});
|
|
12817
13373
|
let response;
|
|
12818
13374
|
try {
|
|
12819
|
-
response = await fetchImpl(oauthTokenEndpoint(params.
|
|
13375
|
+
response = await fetchImpl(oauthTokenEndpoint(params.apiBaseUrl), {
|
|
12820
13376
|
body,
|
|
12821
13377
|
headers: {
|
|
12822
13378
|
...params.headers ?? {},
|
|
@@ -12856,13 +13412,12 @@ async function createAuthenticatedCliApiClient(params) {
|
|
|
12856
13412
|
const requestConfig = resolveCliApiRequestConfig(params);
|
|
12857
13413
|
let auth = resolveCliAuth({
|
|
12858
13414
|
apiKey: params.apiKey,
|
|
12859
|
-
|
|
12860
|
-
apiBaseUrl2: requestConfig.apiBaseUrl2,
|
|
13415
|
+
apiBaseUrl: requestConfig.apiBaseUrl,
|
|
12861
13416
|
configDir: params.configDir
|
|
12862
13417
|
});
|
|
12863
13418
|
if (auth.source === "stored" && auth.credentials) {
|
|
12864
13419
|
const refreshed = await refreshStoredCliCredentials({
|
|
12865
|
-
|
|
13420
|
+
apiBaseUrl: auth.apiBaseUrl,
|
|
12866
13421
|
configDir: params.configDir,
|
|
12867
13422
|
credentials: auth.credentials,
|
|
12868
13423
|
credentialsLockHeld: params.credentialsLockHeld,
|
|
@@ -12879,8 +13434,7 @@ async function createAuthenticatedCliApiClient(params) {
|
|
|
12879
13434
|
return {
|
|
12880
13435
|
apiClient: new PrimitiveApiClient({
|
|
12881
13436
|
apiKey: auth.apiKey,
|
|
12882
|
-
|
|
12883
|
-
apiBaseUrl2: auth.apiBaseUrl2,
|
|
13437
|
+
apiBaseUrl: auth.apiBaseUrl,
|
|
12884
13438
|
headers: requestConfig.headers
|
|
12885
13439
|
}),
|
|
12886
13440
|
auth,
|
|
@@ -13196,7 +13750,7 @@ const NETWORK_ERROR_HINTS = {
|
|
|
13196
13750
|
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.",
|
|
13197
13751
|
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.",
|
|
13198
13752
|
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.",
|
|
13199
|
-
EAI_AGAIN: "Hint: DNS lookup failed. Check /etc/resolv.conf inside containers, and try `curl -v https://
|
|
13753
|
+
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."
|
|
13200
13754
|
};
|
|
13201
13755
|
function writeErrorWithHints(payload) {
|
|
13202
13756
|
process.stderr.write(`${formatErrorPayload(payload)}\n`);
|
|
@@ -13225,8 +13779,8 @@ function writeErrorWithHints(payload) {
|
|
|
13225
13779
|
*/
|
|
13226
13780
|
function surfaceUnauthorizedHint(params) {
|
|
13227
13781
|
if (extractErrorCode(params.payload) !== API_ERROR_CODES.unauthorized || params.auth.source !== "stored") return;
|
|
13228
|
-
if (params.baseUrlOverridden && params.auth.credentials !== null && params.auth.
|
|
13229
|
-
process.stderr.write("Saved Primitive CLI credentials were rejected by the overridden API base URL. The saved credential is preserved; unset
|
|
13782
|
+
if (params.baseUrlOverridden && params.auth.credentials !== null && params.auth.apiBaseUrl !== params.auth.credentials.api_base_url) {
|
|
13783
|
+
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");
|
|
13230
13784
|
return;
|
|
13231
13785
|
}
|
|
13232
13786
|
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");
|
|
@@ -13247,13 +13801,10 @@ async function runWithTiming(enabled, fn) {
|
|
|
13247
13801
|
}
|
|
13248
13802
|
}
|
|
13249
13803
|
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.";
|
|
13250
|
-
const
|
|
13251
|
-
const API_BASE_URL_2_FLAG_DESCRIPTION = "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.";
|
|
13252
|
-
const HOST_2_OPERATIONS = new Set(["sendEmail"]);
|
|
13804
|
+
const API_BASE_URL_FLAG_DESCRIPTION = "Override the API base URL. Internal testing only; not documented to customers.";
|
|
13253
13805
|
const RESERVED_FLAG_NAMES = new Set([
|
|
13254
13806
|
"api-key",
|
|
13255
|
-
"api-base-url
|
|
13256
|
-
"api-base-url-2",
|
|
13807
|
+
"api-base-url",
|
|
13257
13808
|
"raw-body",
|
|
13258
13809
|
"body-file",
|
|
13259
13810
|
"envelope",
|
|
@@ -13285,14 +13836,9 @@ function buildFlags(operation) {
|
|
|
13285
13836
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
13286
13837
|
env: "PRIMITIVE_API_KEY"
|
|
13287
13838
|
}),
|
|
13288
|
-
"api-base-url
|
|
13289
|
-
description: "Override the
|
|
13290
|
-
env: "
|
|
13291
|
-
hidden: true
|
|
13292
|
-
}),
|
|
13293
|
-
"api-base-url-2": Flags.string({
|
|
13294
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
13295
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
13839
|
+
"api-base-url": Flags.string({
|
|
13840
|
+
description: "Override the API base URL. Internal testing only; not documented to customers.",
|
|
13841
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
13296
13842
|
hidden: true
|
|
13297
13843
|
}),
|
|
13298
13844
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
@@ -13380,8 +13926,7 @@ function createOperationCommand(operation) {
|
|
|
13380
13926
|
await runWithTiming(parsedFlags.time === true, async () => {
|
|
13381
13927
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
13382
13928
|
apiKey: typeof parsedFlags["api-key"] === "string" ? parsedFlags["api-key"] : void 0,
|
|
13383
|
-
|
|
13384
|
-
apiBaseUrl2: typeof parsedFlags["api-base-url-2"] === "string" ? parsedFlags["api-base-url-2"] : void 0,
|
|
13929
|
+
apiBaseUrl: typeof parsedFlags["api-base-url"] === "string" ? parsedFlags["api-base-url"] : void 0,
|
|
13385
13930
|
configDir: this.config.configDir
|
|
13386
13931
|
});
|
|
13387
13932
|
let body;
|
|
@@ -13402,10 +13947,9 @@ function createOperationCommand(operation) {
|
|
|
13402
13947
|
}
|
|
13403
13948
|
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.`);
|
|
13404
13949
|
const operationFn = sdk_gen_exports[operation.sdkName];
|
|
13405
|
-
const targetClient = HOST_2_OPERATIONS.has(operation.sdkName) ? apiClient._sendClient : apiClient.client;
|
|
13406
13950
|
const result = await operationFn({
|
|
13407
13951
|
body,
|
|
13408
|
-
client:
|
|
13952
|
+
client: apiClient.client,
|
|
13409
13953
|
parseAs: operation.binaryResponse ? "blob" : "auto",
|
|
13410
13954
|
path: collectValues(operation.pathParams, parsedFlags),
|
|
13411
13955
|
query: collectValues(operation.queryParams, parsedFlags),
|
|
@@ -13480,6 +14024,39 @@ function canonicalizeCliReferences(description) {
|
|
|
13480
14024
|
return description.replaceAll("`primitive emails:latest`", "`primitive emails latest`").replaceAll("`primitive describe emails:get-email | jq '.responseSchema.properties'`", "`primitive describe emails:get | jq '.responseSchema.properties'`");
|
|
13481
14025
|
}
|
|
13482
14026
|
//#endregion
|
|
14027
|
+
//#region src/oclif/attachments.ts
|
|
14028
|
+
function readAttachmentBytes(path, readFile) {
|
|
14029
|
+
try {
|
|
14030
|
+
return Buffer.from(readFile(path));
|
|
14031
|
+
} catch (error) {
|
|
14032
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
14033
|
+
throw new Errors.CLIError(`Could not read --attachment ${path}: ${detail}`, { exit: 1 });
|
|
14034
|
+
}
|
|
14035
|
+
}
|
|
14036
|
+
function hasControlCharacter(value) {
|
|
14037
|
+
return Array.from(value).some((character) => {
|
|
14038
|
+
const code = character.charCodeAt(0);
|
|
14039
|
+
return code <= 31 || code >= 127 && code <= 159;
|
|
14040
|
+
});
|
|
14041
|
+
}
|
|
14042
|
+
function validateAttachmentFilename(path, filename) {
|
|
14043
|
+
if (!filename) throw new Errors.CLIError(`Could not derive an attachment filename from ${path}. Pass a file path.`, { exit: 1 });
|
|
14044
|
+
if (hasControlCharacter(filename)) throw new Errors.CLIError(`Attachment filename ${filename} contains control characters.`, { exit: 1 });
|
|
14045
|
+
}
|
|
14046
|
+
function readAttachmentFiles(paths, readFile = readFileSync) {
|
|
14047
|
+
if (!paths || paths.length === 0) return void 0;
|
|
14048
|
+
return paths.map((path) => {
|
|
14049
|
+
const filename = basename(path);
|
|
14050
|
+
validateAttachmentFilename(path, filename);
|
|
14051
|
+
const bytes = readAttachmentBytes(path, readFile);
|
|
14052
|
+
if (bytes.length === 0) throw new Errors.CLIError(`Attachment file ${path} is empty. Attachments must contain at least one byte.`, { exit: 1 });
|
|
14053
|
+
return {
|
|
14054
|
+
content_base64: bytes.toString("base64"),
|
|
14055
|
+
filename
|
|
14056
|
+
};
|
|
14057
|
+
});
|
|
14058
|
+
}
|
|
14059
|
+
//#endregion
|
|
13483
14060
|
//#region src/oclif/outbound-defaults.ts
|
|
13484
14061
|
const SUBJECT_MAX_LENGTH = 200;
|
|
13485
14062
|
function deriveSubject(body) {
|
|
@@ -13630,6 +14207,30 @@ async function readStdinToString(missingMessage = "No message provided. Pass the
|
|
|
13630
14207
|
for await (const chunk of process.stdin) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
13631
14208
|
return Buffer.concat(chunks).toString("utf8");
|
|
13632
14209
|
}
|
|
14210
|
+
function chatColor(color, text) {
|
|
14211
|
+
return ux.colorize(color, text);
|
|
14212
|
+
}
|
|
14213
|
+
function chatCommandText(command) {
|
|
14214
|
+
return chatColor("cyan", command);
|
|
14215
|
+
}
|
|
14216
|
+
function chatDetailLine(line) {
|
|
14217
|
+
return chatColor("dim", line);
|
|
14218
|
+
}
|
|
14219
|
+
function chatFailureText(message) {
|
|
14220
|
+
return chatColor("red", message);
|
|
14221
|
+
}
|
|
14222
|
+
function chatHeading(text) {
|
|
14223
|
+
return chatColor("bold", text);
|
|
14224
|
+
}
|
|
14225
|
+
function chatNoticeText(message) {
|
|
14226
|
+
return chatColor("yellow", message);
|
|
14227
|
+
}
|
|
14228
|
+
function chatProgressText(message) {
|
|
14229
|
+
return chatColor("cyan", message);
|
|
14230
|
+
}
|
|
14231
|
+
function chatSuccessText(message) {
|
|
14232
|
+
return chatColor("greenBright", message);
|
|
14233
|
+
}
|
|
13633
14234
|
var ChatProgressIndicator = class {
|
|
13634
14235
|
currentMessage = null;
|
|
13635
14236
|
frameIndex = 0;
|
|
@@ -13650,7 +14251,7 @@ var ChatProgressIndicator = class {
|
|
|
13650
14251
|
this.timer.unref?.();
|
|
13651
14252
|
return;
|
|
13652
14253
|
}
|
|
13653
|
-
this.stream.write(`${message}\n`);
|
|
14254
|
+
this.stream.write(`${chatProgressText(message)}\n`);
|
|
13654
14255
|
}
|
|
13655
14256
|
update(message, options = {}) {
|
|
13656
14257
|
this.currentMessage = message;
|
|
@@ -13663,10 +14264,10 @@ var ChatProgressIndicator = class {
|
|
|
13663
14264
|
return;
|
|
13664
14265
|
}
|
|
13665
14266
|
this.stopTimer();
|
|
13666
|
-
this.stream.write(`${message}\n`);
|
|
14267
|
+
this.stream.write(`${chatProgressText(message)}\n`);
|
|
13667
14268
|
if (options.heartbeatMs !== void 0) {
|
|
13668
14269
|
this.timer = setInterval(() => {
|
|
13669
|
-
this.stream.write(`${formatWaitingHeartbeat(message, this.now() - this.startedAt, options.timeoutSeconds)}\n`);
|
|
14270
|
+
this.stream.write(`${chatProgressText(formatWaitingHeartbeat(message, this.now() - this.startedAt, options.timeoutSeconds))}\n`);
|
|
13670
14271
|
}, options.heartbeatMs);
|
|
13671
14272
|
this.timer.unref?.();
|
|
13672
14273
|
}
|
|
@@ -13675,17 +14276,17 @@ var ChatProgressIndicator = class {
|
|
|
13675
14276
|
if (this.stream.isTTY) {
|
|
13676
14277
|
const currentMessage = this.currentMessage;
|
|
13677
14278
|
this.clearLine();
|
|
13678
|
-
this.stream.write(`${message}\n`);
|
|
14279
|
+
this.stream.write(`${chatNoticeText(message)}\n`);
|
|
13679
14280
|
if (currentMessage !== null && this.timer !== null) this.render(currentMessage);
|
|
13680
14281
|
return;
|
|
13681
14282
|
}
|
|
13682
|
-
this.stream.write(`${message}\n`);
|
|
14283
|
+
this.stream.write(`${chatNoticeText(message)}\n`);
|
|
13683
14284
|
}
|
|
13684
14285
|
succeed(message) {
|
|
13685
|
-
this.finish(`${message} after ${formatElapsed(this.now() - this.startedAt)}.`);
|
|
14286
|
+
this.finish(chatSuccessText(`${message} after ${formatElapsed(this.now() - this.startedAt)}.`));
|
|
13686
14287
|
}
|
|
13687
14288
|
fail(message) {
|
|
13688
|
-
this.finish(message);
|
|
14289
|
+
this.finish(chatFailureText(message));
|
|
13689
14290
|
}
|
|
13690
14291
|
finish(message) {
|
|
13691
14292
|
this.stopTimer();
|
|
@@ -13702,9 +14303,10 @@ var ChatProgressIndicator = class {
|
|
|
13702
14303
|
];
|
|
13703
14304
|
const frame = frames[this.frameIndex % frames.length];
|
|
13704
14305
|
this.frameIndex += 1;
|
|
13705
|
-
const
|
|
13706
|
-
|
|
13707
|
-
this.
|
|
14306
|
+
const elapsed = `(${formatElapsed(this.now() - this.startedAt)})`;
|
|
14307
|
+
const plainLine = `${frame} ${message} ${elapsed}`;
|
|
14308
|
+
this.lastLineLength = Math.max(this.lastLineLength, plainLine.length);
|
|
14309
|
+
this.stream.write(`\r${chatProgressText(`${frame} ${message}`)} ${chatColor("dim", elapsed)}`);
|
|
13708
14310
|
}
|
|
13709
14311
|
clearLine() {
|
|
13710
14312
|
if (this.lastLineLength > 0) {
|
|
@@ -13950,45 +14552,46 @@ function formatChatResponse(context) {
|
|
|
13950
14552
|
const accepted = context.sent.accepted.join(", ") || context.recipient;
|
|
13951
14553
|
const responseBody = resolveChatResponseBody(context.reply);
|
|
13952
14554
|
const lines = [
|
|
13953
|
-
"Reply received",
|
|
14555
|
+
chatSuccessText("Reply received"),
|
|
13954
14556
|
"",
|
|
13955
|
-
"Sent",
|
|
13956
|
-
` To: ${accepted}
|
|
13957
|
-
` From: ${context.sent.from || context.from}
|
|
13958
|
-
` Subject: ${context.subject}
|
|
13959
|
-
` Sent email id: ${context.sent.id}
|
|
13960
|
-
` Delivery status: ${context.sent.delivery_status ?? context.sent.status}
|
|
14557
|
+
chatHeading("Sent"),
|
|
14558
|
+
chatDetailLine(` To: ${accepted}`),
|
|
14559
|
+
chatDetailLine(` From: ${context.sent.from || context.from}`),
|
|
14560
|
+
chatDetailLine(` Subject: ${context.subject}`),
|
|
14561
|
+
chatDetailLine(` Sent email id: ${context.sent.id}`),
|
|
14562
|
+
chatColor("green", ` Delivery status: ${context.sent.delivery_status ?? context.sent.status}`),
|
|
13961
14563
|
"",
|
|
13962
|
-
"Reply",
|
|
13963
|
-
` Email id: ${context.reply.id}
|
|
13964
|
-
` From: ${context.reply.from_email}
|
|
13965
|
-
` To: ${context.reply.to_email}
|
|
13966
|
-
` Subject: ${context.reply.subject ?? "(no subject)"}
|
|
13967
|
-
` Received: ${context.reply.received_at}
|
|
13968
|
-
` Match: ${matchDescription(context.matchStrategy)}`
|
|
14564
|
+
chatHeading("Reply"),
|
|
14565
|
+
chatDetailLine(` Email id: ${context.reply.id}`),
|
|
14566
|
+
chatDetailLine(` From: ${context.reply.from_email}`),
|
|
14567
|
+
chatDetailLine(` To: ${context.reply.to_email}`),
|
|
14568
|
+
chatDetailLine(` Subject: ${context.reply.subject ?? "(no subject)"}`),
|
|
14569
|
+
chatDetailLine(` Received: ${context.reply.received_at}`),
|
|
14570
|
+
chatDetailLine(` Match: ${matchDescription(context.matchStrategy)}`)
|
|
13969
14571
|
];
|
|
13970
|
-
if (context.reply.reply_to_sent_email_id) lines.push(` Reply to sent email id: ${context.reply.reply_to_sent_email_id}`);
|
|
13971
|
-
if (context.reply.message_id) lines.push(` Message-Id: ${context.reply.message_id}`);
|
|
13972
|
-
if (context.localChatId !== void 0) lines.push(` Local chat id: ${context.localChatId}`);
|
|
13973
|
-
lines.push("", "Helpful follow-up commands", " Replace <message> before running commands that include it.", " Commands are templates; use --json for parse-safe output.", " When shown, --strict-only prefers timing out over matching the wrong reply.");
|
|
13974
|
-
for (const { description, command } of buildChatFollowUpCommands(context)) lines.push(` ${description}
|
|
13975
|
-
lines.push("", `Response body (${responseBody.format}; use --json for parsing)
|
|
14572
|
+
if (context.reply.reply_to_sent_email_id) lines.push(chatDetailLine(` Reply to sent email id: ${context.reply.reply_to_sent_email_id}`));
|
|
14573
|
+
if (context.reply.message_id) lines.push(chatDetailLine(` Message-Id: ${context.reply.message_id}`));
|
|
14574
|
+
if (context.localChatId !== void 0) lines.push(chatDetailLine(` Local chat id: ${context.localChatId}`));
|
|
14575
|
+
lines.push("", chatHeading("Helpful follow-up commands"), chatDetailLine(" Replace <message> before running commands that include it."), chatDetailLine(" Commands are templates; use --json for parse-safe output."), chatDetailLine(" When shown, --strict-only prefers timing out over matching the wrong reply."));
|
|
14576
|
+
for (const { description, command } of buildChatFollowUpCommands(context)) lines.push(chatHeading(` ${description}:`), ` ${chatCommandText(command)}`);
|
|
14577
|
+
lines.push("", chatHeading(`Response body (${responseBody.format}; use --json for parsing)`), "----- BEGIN RESPONSE -----", responseBody.body || "(empty response)", "----- END RESPONSE -----");
|
|
13976
14578
|
return lines.join("\n");
|
|
13977
14579
|
}
|
|
13978
14580
|
function formatChatRecoveryContext(context) {
|
|
14581
|
+
const accepted = context.sent.accepted.join(", ") || context.recipient;
|
|
13979
14582
|
const lines = [
|
|
13980
14583
|
"",
|
|
13981
|
-
"Sent message context",
|
|
13982
|
-
` To: ${
|
|
13983
|
-
` From: ${context.sent.from || context.from}
|
|
13984
|
-
` Subject: ${context.subject}
|
|
13985
|
-
` Sent email id: ${context.sent.id}
|
|
13986
|
-
` Delivery status: ${context.sent.delivery_status ?? context.sent.status}
|
|
13987
|
-
` Poll since: ${context.sentAtIso}
|
|
14584
|
+
chatHeading("Sent message context"),
|
|
14585
|
+
chatDetailLine(` To: ${accepted}`),
|
|
14586
|
+
chatDetailLine(` From: ${context.sent.from || context.from}`),
|
|
14587
|
+
chatDetailLine(` Subject: ${context.subject}`),
|
|
14588
|
+
chatDetailLine(` Sent email id: ${context.sent.id}`),
|
|
14589
|
+
chatColor("green", ` Delivery status: ${context.sent.delivery_status ?? context.sent.status}`),
|
|
14590
|
+
chatDetailLine(` Poll since: ${context.sentAtIso}`),
|
|
13988
14591
|
"",
|
|
13989
|
-
"Helpful recovery commands"
|
|
14592
|
+
chatHeading("Helpful recovery commands")
|
|
13990
14593
|
];
|
|
13991
|
-
for (const { description, command } of buildChatRecoveryCommands(context)) lines.push(` ${description}
|
|
14594
|
+
for (const { description, command } of buildChatRecoveryCommands(context)) lines.push(chatHeading(` ${description}:`), ` ${chatCommandText(command)}`);
|
|
13992
14595
|
return lines.join("\n");
|
|
13993
14596
|
}
|
|
13994
14597
|
async function loadInboundEmailDetail(params) {
|
|
@@ -14081,8 +14684,10 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14081
14684
|
"<%= config.bin %> chat help@agent.acme.dev 'how do I rotate my API key?'",
|
|
14082
14685
|
"cat error.log | <%= config.bin %> chat help@agent.acme.dev",
|
|
14083
14686
|
"<%= config.bin %> chat reply 'one more thing'",
|
|
14687
|
+
"<%= config.bin %> chat reply 'see attached' --attachment ./report.pdf",
|
|
14084
14688
|
"<%= config.bin %> chat help@agent.acme.dev --reply 'one more thing'",
|
|
14085
14689
|
"<%= config.bin %> chat help@agent.acme.dev --reply 'one more thing' --reply-to-email-id <inbound-email-id>",
|
|
14690
|
+
"<%= config.bin %> chat help@agent.acme.dev 'can you review this?' --attachment ./report.pdf",
|
|
14086
14691
|
"<%= config.bin %> chat help@agent.acme.dev 'follow up question' --json",
|
|
14087
14692
|
"<%= config.bin %> chat help@agent.acme.dev 'one more thing' --timeout 300"
|
|
14088
14693
|
];
|
|
@@ -14098,14 +14703,9 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14098
14703
|
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive signin` credentials)",
|
|
14099
14704
|
env: "PRIMITIVE_API_KEY"
|
|
14100
14705
|
}),
|
|
14101
|
-
"api-base-url
|
|
14706
|
+
"api-base-url": Flags.string({
|
|
14102
14707
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
14103
|
-
env: "
|
|
14104
|
-
hidden: true
|
|
14105
|
-
}),
|
|
14106
|
-
"api-base-url-2": Flags.string({
|
|
14107
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
14108
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
14708
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
14109
14709
|
hidden: true
|
|
14110
14710
|
}),
|
|
14111
14711
|
from: Flags.string({ description: "Sender address. Defaults to agent@<your-first-verified-outbound-domain>." }),
|
|
@@ -14116,6 +14716,11 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14116
14716
|
reply: Flags.string({ description: "Reply body. Continues the latest inbound email from the recipient to your sender address; pass --reply-to-email-id for an exact thread." }),
|
|
14117
14717
|
"reply-to-email-id": Flags.string({ description: "Inbound email id to continue exactly. Uses Primitive's reply endpoint, so recipient, subject, and threading headers are derived from the inbound email." }),
|
|
14118
14718
|
"in-reply-to": Flags.string({ description: "Raw Message-Id of the parent email to thread a new send against. Prefer --reply-to-email-id with --reply when continuing an inbound email stored by Primitive." }),
|
|
14719
|
+
attachment: Flags.string({
|
|
14720
|
+
char: "a",
|
|
14721
|
+
description: "Attach a file to this chat message. Repeat --attachment to attach multiple files.",
|
|
14722
|
+
multiple: true
|
|
14723
|
+
}),
|
|
14119
14724
|
"chat-local-id": Flags.integer({
|
|
14120
14725
|
description: "Local chat id to update after this command succeeds. Internal plumbing for `primitive chat reply`.",
|
|
14121
14726
|
hidden: true,
|
|
@@ -14159,8 +14764,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14159
14764
|
await runWithTiming(flags.time, async () => {
|
|
14160
14765
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
14161
14766
|
apiKey: flags["api-key"],
|
|
14162
|
-
|
|
14163
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
14767
|
+
apiBaseUrl: flags["api-base-url"],
|
|
14164
14768
|
configDir: this.config.configDir
|
|
14165
14769
|
});
|
|
14166
14770
|
const authFailureContext = {
|
|
@@ -14169,6 +14773,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14169
14773
|
configDir: this.config.configDir
|
|
14170
14774
|
};
|
|
14171
14775
|
const progress = flags.quiet ? null : new ChatProgressIndicator(process.stderr);
|
|
14776
|
+
const attachments = readAttachmentFiles(flags.attachment);
|
|
14172
14777
|
let from;
|
|
14173
14778
|
let parentReply;
|
|
14174
14779
|
let subject;
|
|
@@ -14227,7 +14832,8 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14227
14832
|
const sendResult = parentReply !== void 0 ? await replyToEmail({
|
|
14228
14833
|
body: {
|
|
14229
14834
|
body_text: message,
|
|
14230
|
-
from
|
|
14835
|
+
from,
|
|
14836
|
+
...attachments !== void 0 ? { attachments } : {}
|
|
14231
14837
|
},
|
|
14232
14838
|
client: apiClient.client,
|
|
14233
14839
|
path: { id: parentReply.id },
|
|
@@ -14238,9 +14844,10 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
14238
14844
|
to: args.recipient,
|
|
14239
14845
|
subject,
|
|
14240
14846
|
body_text: message,
|
|
14241
|
-
...flags["in-reply-to"] !== void 0 ? { in_reply_to: flags["in-reply-to"] } : {}
|
|
14847
|
+
...flags["in-reply-to"] !== void 0 ? { in_reply_to: flags["in-reply-to"] } : {},
|
|
14848
|
+
...attachments !== void 0 ? { attachments } : {}
|
|
14242
14849
|
},
|
|
14243
|
-
client: apiClient.
|
|
14850
|
+
client: apiClient.client,
|
|
14244
14851
|
responseStyle: "fields"
|
|
14245
14852
|
});
|
|
14246
14853
|
if (sendResult.error) {
|
|
@@ -14353,6 +14960,7 @@ var ChatReplyCommand = class ChatReplyCommand extends Command {
|
|
|
14353
14960
|
"<%= config.bin %> chat reply 'one more thing'",
|
|
14354
14961
|
"<%= config.bin %> chat reply 0 'one more thing'",
|
|
14355
14962
|
"<%= config.bin %> chat reply --id 0 'one more thing'",
|
|
14963
|
+
"<%= config.bin %> chat reply 'see attached' --attachment ./report.pdf",
|
|
14356
14964
|
"cat follow-up.txt | <%= config.bin %> chat reply"
|
|
14357
14965
|
];
|
|
14358
14966
|
static args = {
|
|
@@ -14364,14 +14972,9 @@ var ChatReplyCommand = class ChatReplyCommand extends Command {
|
|
|
14364
14972
|
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive signin` credentials)",
|
|
14365
14973
|
env: "PRIMITIVE_API_KEY"
|
|
14366
14974
|
}),
|
|
14367
|
-
"api-base-url
|
|
14975
|
+
"api-base-url": Flags.string({
|
|
14368
14976
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
14369
|
-
env: "
|
|
14370
|
-
hidden: true
|
|
14371
|
-
}),
|
|
14372
|
-
"api-base-url-2": Flags.string({
|
|
14373
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
14374
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
14977
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
14375
14978
|
hidden: true
|
|
14376
14979
|
}),
|
|
14377
14980
|
id: Flags.integer({
|
|
@@ -14389,6 +14992,11 @@ var ChatReplyCommand = class ChatReplyCommand extends Command {
|
|
|
14389
14992
|
min: 1
|
|
14390
14993
|
}),
|
|
14391
14994
|
"strict-only": Flags.boolean({ description: "Disable the time-window fallback. If the active chat was saved from a strict match, this is already the default." }),
|
|
14995
|
+
attachment: Flags.string({
|
|
14996
|
+
char: "a",
|
|
14997
|
+
description: "Attach a file to the reply. Repeat --attachment to attach multiple files.",
|
|
14998
|
+
multiple: true
|
|
14999
|
+
}),
|
|
14392
15000
|
interval: Flags.integer({
|
|
14393
15001
|
description: "Seconds between polls while waiting for the reply.",
|
|
14394
15002
|
min: 1
|
|
@@ -14431,10 +15039,10 @@ var ChatReplyCommand = class ChatReplyCommand extends Command {
|
|
|
14431
15039
|
String(state.local_id)
|
|
14432
15040
|
];
|
|
14433
15041
|
if (flags["api-key"] !== void 0) argv.push("--api-key", flags["api-key"]);
|
|
14434
|
-
if (flags["api-base-url
|
|
14435
|
-
if (flags["api-base-url-2"] !== void 0) argv.push("--api-base-url-2", flags["api-base-url-2"]);
|
|
15042
|
+
if (flags["api-base-url"] !== void 0) argv.push("--api-base-url", flags["api-base-url"]);
|
|
14436
15043
|
if (flags.json) argv.push("--json");
|
|
14437
15044
|
if (flags.quiet) argv.push("--quiet");
|
|
15045
|
+
for (const attachment of flags.attachment ?? []) argv.push("--attachment", attachment);
|
|
14438
15046
|
if (state.strict_only || flags["strict-only"]) argv.push("--strict-only");
|
|
14439
15047
|
if (flags.time) argv.push("--time");
|
|
14440
15048
|
await ChatCommand.run(argv, { root: this.config.root });
|
|
@@ -14546,8 +15154,7 @@ function upsertCliEnvironmentAndClearCredentialsIfSwitched(params) {
|
|
|
14546
15154
|
const previousActiveEnvironment = resolveConfigEnvironment(previousConfig);
|
|
14547
15155
|
const previousEnvironment = previousActiveEnvironment?.name ?? null;
|
|
14548
15156
|
const config = upsertCliEnvironment({
|
|
14549
|
-
|
|
14550
|
-
apiBaseUrl2: params.apiBaseUrl2,
|
|
15157
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
14551
15158
|
config: previousConfig,
|
|
14552
15159
|
environmentName: params.environmentName,
|
|
14553
15160
|
headers: params.headers,
|
|
@@ -14555,7 +15162,7 @@ function upsertCliEnvironmentAndClearCredentialsIfSwitched(params) {
|
|
|
14555
15162
|
});
|
|
14556
15163
|
const activeEnvironment = resolveConfigEnvironment(config);
|
|
14557
15164
|
const environment = activeEnvironment?.name ?? null;
|
|
14558
|
-
const shouldClearCredentials = existsSync(credentialsPath(params.configDir)) && (previousEnvironment !== environment || previousActiveEnvironment?.config.
|
|
15165
|
+
const shouldClearCredentials = existsSync(credentialsPath(params.configDir)) && (previousEnvironment !== environment || previousActiveEnvironment?.config.api_base_url !== activeEnvironment?.config.api_base_url);
|
|
14559
15166
|
let removedCredentials = false;
|
|
14560
15167
|
if (shouldClearCredentials) {
|
|
14561
15168
|
const releaseLock = acquireCliCredentialsLock(params.configDir);
|
|
@@ -14605,10 +15212,9 @@ var ConfigSetCommand = class ConfigSetCommand extends Command {
|
|
|
14605
15212
|
static flags = {
|
|
14606
15213
|
environment: Flags.string({
|
|
14607
15214
|
char: "e",
|
|
14608
|
-
description: "Environment name to create or update"
|
|
15215
|
+
description: "Environment name to create or update. Defaults to the active environment, or default when none is active."
|
|
14609
15216
|
}),
|
|
14610
|
-
"api-base-url
|
|
14611
|
-
"api-base-url-2": Flags.string({ description: "Attachments-supporting API base URL" }),
|
|
15217
|
+
"api-base-url": Flags.string({ description: "API base URL" }),
|
|
14612
15218
|
header: Flags.string({
|
|
14613
15219
|
description: "Request header in name=value form. Repeatable.",
|
|
14614
15220
|
multiple: true
|
|
@@ -14621,10 +15227,9 @@ var ConfigSetCommand = class ConfigSetCommand extends Command {
|
|
|
14621
15227
|
async run() {
|
|
14622
15228
|
const { flags } = await this.parse(ConfigSetCommand);
|
|
14623
15229
|
const headers = flags.header ?? [];
|
|
14624
|
-
if (flags["api-base-url
|
|
15230
|
+
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 });
|
|
14625
15231
|
const { environment, removedCredentials } = upsertCliEnvironmentAndClearCredentialsIfSwitched({
|
|
14626
|
-
|
|
14627
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
15232
|
+
apiBaseUrl: flags["api-base-url"],
|
|
14628
15233
|
configDir: this.config.configDir,
|
|
14629
15234
|
environmentName: flags.environment,
|
|
14630
15235
|
headers,
|
|
@@ -14672,8 +15277,7 @@ var ConfigListCommand = class ConfigListCommand extends Command {
|
|
|
14672
15277
|
const active = activeEnvironment === name ? "*" : " ";
|
|
14673
15278
|
const headerNames = Object.keys(environment.headers ?? {});
|
|
14674
15279
|
this.log(`${active} ${name}`);
|
|
14675
|
-
if (environment.
|
|
14676
|
-
if (environment.api_base_url_2) this.log(` api_base_url_2: ${environment.api_base_url_2}`);
|
|
15280
|
+
if (environment.api_base_url) this.log(` api_base_url: ${environment.api_base_url}`);
|
|
14677
15281
|
this.log(` headers: ${headerNames.length > 0 ? headerNames.join(", ") : "(none)"}`);
|
|
14678
15282
|
}
|
|
14679
15283
|
}
|
|
@@ -14845,7 +15449,7 @@ async function checkAccount(client) {
|
|
|
14845
15449
|
} catch (error) {
|
|
14846
15450
|
const code = error instanceof Error && error.cause && typeof error.cause === "object" && typeof error.cause.code === "string" ? error.cause.code : void 0;
|
|
14847
15451
|
const message = error instanceof Error ? error.message : String(error);
|
|
14848
|
-
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://
|
|
15452
|
+
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.";
|
|
14849
15453
|
return {
|
|
14850
15454
|
outcome: {
|
|
14851
15455
|
status: "fail",
|
|
@@ -14899,14 +15503,9 @@ var DoctorCommand = class DoctorCommand extends Command {
|
|
|
14899
15503
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
14900
15504
|
env: "PRIMITIVE_API_KEY"
|
|
14901
15505
|
}),
|
|
14902
|
-
"api-base-url
|
|
15506
|
+
"api-base-url": Flags.string({
|
|
14903
15507
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
14904
|
-
env: "
|
|
14905
|
-
hidden: true
|
|
14906
|
-
}),
|
|
14907
|
-
"api-base-url-2": Flags.string({
|
|
14908
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
14909
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
15508
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
14910
15509
|
hidden: true
|
|
14911
15510
|
})
|
|
14912
15511
|
};
|
|
@@ -14932,8 +15531,7 @@ var DoctorCommand = class DoctorCommand extends Command {
|
|
|
14932
15531
|
if (apiKeyCheck.status !== "fail") {
|
|
14933
15532
|
const { apiClient, auth } = await createAuthenticatedCliApiClient({
|
|
14934
15533
|
apiKey: flags["api-key"],
|
|
14935
|
-
|
|
14936
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
15534
|
+
apiBaseUrl: flags["api-base-url"],
|
|
14937
15535
|
configDir: this.config.configDir
|
|
14938
15536
|
});
|
|
14939
15537
|
if (auth.apiKey !== void 0) {
|
|
@@ -15019,14 +15617,9 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
15019
15617
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15020
15618
|
env: "PRIMITIVE_API_KEY"
|
|
15021
15619
|
}),
|
|
15022
|
-
"api-base-url
|
|
15620
|
+
"api-base-url": Flags.string({
|
|
15023
15621
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15024
|
-
env: "
|
|
15025
|
-
hidden: true
|
|
15026
|
-
}),
|
|
15027
|
-
"api-base-url-2": Flags.string({
|
|
15028
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15029
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
15622
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15030
15623
|
hidden: true
|
|
15031
15624
|
}),
|
|
15032
15625
|
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`." }),
|
|
@@ -15045,8 +15638,7 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
15045
15638
|
await runWithTiming(flags.time, async () => {
|
|
15046
15639
|
const { apiClient, auth, baseUrlOverridden, requestConfig } = await createAuthenticatedCliApiClient({
|
|
15047
15640
|
apiKey: flags["api-key"],
|
|
15048
|
-
|
|
15049
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
15641
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15050
15642
|
configDir: this.config.configDir
|
|
15051
15643
|
});
|
|
15052
15644
|
let domainId = flags.id;
|
|
@@ -15073,7 +15665,7 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
15073
15665
|
if (!domainId) throw new Errors.CLIError("Could not resolve a domain id.", { exit: 1 });
|
|
15074
15666
|
let response;
|
|
15075
15667
|
try {
|
|
15076
|
-
response = await fetch(zoneFileUrl(requestConfig.
|
|
15668
|
+
response = await fetch(zoneFileUrl(requestConfig.resolvedApiBaseUrl, domainId, flags["outbound-only"]), { headers: {
|
|
15077
15669
|
...requestConfig.headers ?? {},
|
|
15078
15670
|
...auth.apiKey ? { authorization: `Bearer ${auth.apiKey}` } : {}
|
|
15079
15671
|
} });
|
|
@@ -15108,14 +15700,14 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
|
15108
15700
|
};
|
|
15109
15701
|
//#endregion
|
|
15110
15702
|
//#region src/oclif/commands/emails-latest.ts
|
|
15111
|
-
const DEFAULT_LIMIT = 10;
|
|
15112
|
-
const MAX_LIMIT = 100;
|
|
15703
|
+
const DEFAULT_LIMIT$1 = 10;
|
|
15704
|
+
const MAX_LIMIT$1 = 100;
|
|
15113
15705
|
const SUBJECT_DISPLAY_WIDTH = 50;
|
|
15114
15706
|
const ADDRESS_DISPLAY_WIDTH = 32;
|
|
15115
15707
|
const ID_DISPLAY_WIDTH_SHORT = 8;
|
|
15116
15708
|
const ID_DISPLAY_WIDTH_FULL = 36;
|
|
15117
15709
|
const RECEIVED_DISPLAY_WIDTH = 19;
|
|
15118
|
-
function truncate$
|
|
15710
|
+
function truncate$2(value, width) {
|
|
15119
15711
|
if (value.length <= width) return value.padEnd(width);
|
|
15120
15712
|
return `${value.slice(0, width - 3)}...`;
|
|
15121
15713
|
}
|
|
@@ -15129,10 +15721,10 @@ function formatReceivedAt(value) {
|
|
|
15129
15721
|
function pickIdWidth(isTty) {
|
|
15130
15722
|
return isTty ? ID_DISPLAY_WIDTH_SHORT : ID_DISPLAY_WIDTH_FULL;
|
|
15131
15723
|
}
|
|
15132
|
-
function formatRow(email, idWidth) {
|
|
15133
|
-
return `${truncate$
|
|
15724
|
+
function formatRow$1(email, idWidth) {
|
|
15725
|
+
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)}`;
|
|
15134
15726
|
}
|
|
15135
|
-
function formatHeader(idWidth) {
|
|
15727
|
+
function formatHeader$1(idWidth) {
|
|
15136
15728
|
return `${"ID".padEnd(idWidth)} ${"RECEIVED (UTC)".padEnd(RECEIVED_DISPLAY_WIDTH)} ${"FROM".padEnd(ADDRESS_DISPLAY_WIDTH)} ${"TO".padEnd(ADDRESS_DISPLAY_WIDTH)} SUBJECT`;
|
|
15137
15729
|
}
|
|
15138
15730
|
var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
@@ -15153,21 +15745,16 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
15153
15745
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15154
15746
|
env: "PRIMITIVE_API_KEY"
|
|
15155
15747
|
}),
|
|
15156
|
-
"api-base-url
|
|
15748
|
+
"api-base-url": Flags.string({
|
|
15157
15749
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15158
|
-
env: "
|
|
15159
|
-
hidden: true
|
|
15160
|
-
}),
|
|
15161
|
-
"api-base-url-2": Flags.string({
|
|
15162
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15163
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
15750
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15164
15751
|
hidden: true
|
|
15165
15752
|
}),
|
|
15166
15753
|
limit: Flags.integer({
|
|
15167
|
-
description: `Number of rows to print (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}).`,
|
|
15168
|
-
default: DEFAULT_LIMIT,
|
|
15754
|
+
description: `Number of rows to print (1-${MAX_LIMIT$1}, default ${DEFAULT_LIMIT$1}).`,
|
|
15755
|
+
default: DEFAULT_LIMIT$1,
|
|
15169
15756
|
min: 1,
|
|
15170
|
-
max: MAX_LIMIT
|
|
15757
|
+
max: MAX_LIMIT$1
|
|
15171
15758
|
}),
|
|
15172
15759
|
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." }),
|
|
15173
15760
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
@@ -15177,8 +15764,7 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
15177
15764
|
await runWithTiming(flags.time, async () => {
|
|
15178
15765
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
15179
15766
|
apiKey: flags["api-key"],
|
|
15180
|
-
|
|
15181
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
15767
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15182
15768
|
configDir: this.config.configDir
|
|
15183
15769
|
});
|
|
15184
15770
|
const result = await listEmails({
|
|
@@ -15209,8 +15795,8 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
15209
15795
|
return;
|
|
15210
15796
|
}
|
|
15211
15797
|
const idWidth = pickIdWidth(Boolean(process.stdout.isTTY));
|
|
15212
|
-
process.stderr.write(`${formatHeader(idWidth)}\n`);
|
|
15213
|
-
for (const row of rows) this.log(formatRow(row, idWidth));
|
|
15798
|
+
process.stderr.write(`${formatHeader$1(idWidth)}\n`);
|
|
15799
|
+
for (const row of rows) this.log(formatRow$1(row, idWidth));
|
|
15214
15800
|
});
|
|
15215
15801
|
}
|
|
15216
15802
|
};
|
|
@@ -15233,14 +15819,9 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
15233
15819
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15234
15820
|
env: "PRIMITIVE_API_KEY"
|
|
15235
15821
|
}),
|
|
15236
|
-
"api-base-url
|
|
15822
|
+
"api-base-url": Flags.string({
|
|
15237
15823
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15238
|
-
env: "
|
|
15239
|
-
hidden: true
|
|
15240
|
-
}),
|
|
15241
|
-
"api-base-url-2": Flags.string({
|
|
15242
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15243
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
15824
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15244
15825
|
hidden: true
|
|
15245
15826
|
}),
|
|
15246
15827
|
body: Flags.string({ description: "Full-text body filter" }),
|
|
@@ -15284,8 +15865,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
15284
15865
|
const { flags } = await this.parse(EmailsWaitCommand);
|
|
15285
15866
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
15286
15867
|
apiKey: flags["api-key"],
|
|
15287
|
-
|
|
15288
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
15868
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15289
15869
|
configDir: this.config.configDir
|
|
15290
15870
|
});
|
|
15291
15871
|
let since;
|
|
@@ -15327,10 +15907,10 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
15327
15907
|
for (const email of collectNewAcceptedEmails(page.rows, seenIds)) {
|
|
15328
15908
|
if (flags.table) {
|
|
15329
15909
|
if (!headerPrinted) {
|
|
15330
|
-
process.stderr.write(`${formatHeader(idWidth)}\n`);
|
|
15910
|
+
process.stderr.write(`${formatHeader$1(idWidth)}\n`);
|
|
15331
15911
|
headerPrinted = true;
|
|
15332
15912
|
}
|
|
15333
|
-
this.log(formatRow(email, idWidth));
|
|
15913
|
+
this.log(formatRow$1(email, idWidth));
|
|
15334
15914
|
} else this.log(JSON.stringify(email));
|
|
15335
15915
|
matched += 1;
|
|
15336
15916
|
if (matched >= flags.number) return;
|
|
@@ -15361,14 +15941,9 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
15361
15941
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
15362
15942
|
env: "PRIMITIVE_API_KEY"
|
|
15363
15943
|
}),
|
|
15364
|
-
"api-base-url
|
|
15944
|
+
"api-base-url": Flags.string({
|
|
15365
15945
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
15366
|
-
env: "
|
|
15367
|
-
hidden: true
|
|
15368
|
-
}),
|
|
15369
|
-
"api-base-url-2": Flags.string({
|
|
15370
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
15371
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
15946
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
15372
15947
|
hidden: true
|
|
15373
15948
|
}),
|
|
15374
15949
|
body: Flags.string({ description: "Full-text body filter" }),
|
|
@@ -15408,8 +15983,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
15408
15983
|
const { flags } = await this.parse(EmailsWatchCommand);
|
|
15409
15984
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
15410
15985
|
apiKey: flags["api-key"],
|
|
15411
|
-
|
|
15412
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
15986
|
+
apiBaseUrl: flags["api-base-url"],
|
|
15413
15987
|
configDir: this.config.configDir
|
|
15414
15988
|
});
|
|
15415
15989
|
let since;
|
|
@@ -15452,10 +16026,10 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
15452
16026
|
if (flags.jsonl) this.log(JSON.stringify(email));
|
|
15453
16027
|
else {
|
|
15454
16028
|
if (!headerPrinted) {
|
|
15455
|
-
process.stderr.write(`${formatHeader(idWidth)}\n`);
|
|
16029
|
+
process.stderr.write(`${formatHeader$1(idWidth)}\n`);
|
|
15456
16030
|
headerPrinted = true;
|
|
15457
16031
|
}
|
|
15458
|
-
this.log(formatRow(email, idWidth));
|
|
16032
|
+
this.log(formatRow$1(email, idWidth));
|
|
15459
16033
|
}
|
|
15460
16034
|
printed += 1;
|
|
15461
16035
|
if (flags.number && printed >= flags.number) return;
|
|
@@ -16154,14 +16728,9 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
16154
16728
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
16155
16729
|
env: "PRIMITIVE_API_KEY"
|
|
16156
16730
|
}),
|
|
16157
|
-
"api-base-url
|
|
16731
|
+
"api-base-url": Flags.string({
|
|
16158
16732
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
16159
|
-
env: "
|
|
16160
|
-
hidden: true
|
|
16161
|
-
}),
|
|
16162
|
-
"api-base-url-2": Flags.string({
|
|
16163
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
16164
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
16733
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
16165
16734
|
hidden: true
|
|
16166
16735
|
}),
|
|
16167
16736
|
name: Flags.string({
|
|
@@ -16239,8 +16808,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
16239
16808
|
emitRawSendMailFetchWarning(code, (chunk) => process.stderr.write(chunk));
|
|
16240
16809
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
16241
16810
|
apiKey: flags["api-key"],
|
|
16242
|
-
|
|
16243
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
16811
|
+
apiBaseUrl: flags["api-base-url"],
|
|
16244
16812
|
configDir: this.config.configDir
|
|
16245
16813
|
});
|
|
16246
16814
|
const authFailureContext = {
|
|
@@ -16354,8 +16922,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
16354
16922
|
}
|
|
16355
16923
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
16356
16924
|
apiKey: flags["api-key"],
|
|
16357
|
-
|
|
16358
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
16925
|
+
apiBaseUrl: flags["api-base-url"],
|
|
16359
16926
|
configDir: this.config.configDir
|
|
16360
16927
|
});
|
|
16361
16928
|
const authFailureContext = {
|
|
@@ -16444,8 +17011,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
16444
17011
|
name: "Primitive Team",
|
|
16445
17012
|
url: "https://primitive.dev"
|
|
16446
17013
|
};
|
|
16447
|
-
const SDK_VERSION_RANGE = "^0.
|
|
16448
|
-
const CLI_VERSION_RANGE = "^0.
|
|
17014
|
+
const SDK_VERSION_RANGE = "^0.35.1";
|
|
17015
|
+
const CLI_VERSION_RANGE = "^0.35.1";
|
|
16449
17016
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
16450
17017
|
function renderHandler() {
|
|
16451
17018
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -16602,7 +17169,7 @@ export default {
|
|
|
16602
17169
|
|
|
16603
17170
|
const client = createPrimitiveClient({
|
|
16604
17171
|
apiKey: env.PRIMITIVE_API_KEY,
|
|
16605
|
-
|
|
17172
|
+
apiBaseUrl: env.PRIMITIVE_API_BASE_URL,
|
|
16606
17173
|
});
|
|
16607
17174
|
|
|
16608
17175
|
// To add an LLM or another API, store its key as a Function secret.
|
|
@@ -16626,7 +17193,7 @@ export default {
|
|
|
16626
17193
|
// route "support@" to a ticketing flow and "sales@" to a lead
|
|
16627
17194
|
// capture flow before calling client.reply.
|
|
16628
17195
|
|
|
16629
|
-
// client.reply routes through POST /
|
|
17196
|
+
// client.reply routes through POST /v1/emails/{id}/reply
|
|
16630
17197
|
// (NOT /send-mail) so the server derives recipients, the
|
|
16631
17198
|
// \`Re: <parent>\` subject, threading headers, and the
|
|
16632
17199
|
// in_reply_to_email_id foreign key automatically. The FK is
|
|
@@ -17007,14 +17574,9 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
|
|
|
17007
17574
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17008
17575
|
env: "PRIMITIVE_API_KEY"
|
|
17009
17576
|
}),
|
|
17010
|
-
"api-base-url
|
|
17577
|
+
"api-base-url": Flags.string({
|
|
17011
17578
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
17012
|
-
env: "
|
|
17013
|
-
hidden: true
|
|
17014
|
-
}),
|
|
17015
|
-
"api-base-url-2": Flags.string({
|
|
17016
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
17017
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
17579
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
17018
17580
|
hidden: true
|
|
17019
17581
|
}),
|
|
17020
17582
|
id: Flags.string({
|
|
@@ -17045,8 +17607,7 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
|
|
|
17045
17607
|
await runWithTiming(flags.time, async () => {
|
|
17046
17608
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
17047
17609
|
apiKey: flags["api-key"],
|
|
17048
|
-
|
|
17049
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
17610
|
+
apiBaseUrl: flags["api-base-url"],
|
|
17050
17611
|
configDir: this.config.configDir
|
|
17051
17612
|
});
|
|
17052
17613
|
const seenIds = /* @__PURE__ */ new Set();
|
|
@@ -17205,14 +17766,9 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
17205
17766
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17206
17767
|
env: "PRIMITIVE_API_KEY"
|
|
17207
17768
|
}),
|
|
17208
|
-
"api-base-url
|
|
17769
|
+
"api-base-url": Flags.string({
|
|
17209
17770
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
17210
|
-
env: "
|
|
17211
|
-
hidden: true
|
|
17212
|
-
}),
|
|
17213
|
-
"api-base-url-2": Flags.string({
|
|
17214
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
17215
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
17771
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
17216
17772
|
hidden: true
|
|
17217
17773
|
}),
|
|
17218
17774
|
id: Flags.string({
|
|
@@ -17281,8 +17837,7 @@ var FunctionsRedeployCommand = class FunctionsRedeployCommand extends Command {
|
|
|
17281
17837
|
emitRawSendMailFetchWarning(code, (chunk) => process.stderr.write(chunk));
|
|
17282
17838
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
17283
17839
|
apiKey: flags["api-key"],
|
|
17284
|
-
|
|
17285
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
17840
|
+
apiBaseUrl: flags["api-base-url"],
|
|
17286
17841
|
configDir: this.config.configDir
|
|
17287
17842
|
});
|
|
17288
17843
|
const authFailureContext = {
|
|
@@ -17465,14 +18020,9 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
17465
18020
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17466
18021
|
env: "PRIMITIVE_API_KEY"
|
|
17467
18022
|
}),
|
|
17468
|
-
"api-base-url
|
|
18023
|
+
"api-base-url": Flags.string({
|
|
17469
18024
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
17470
|
-
env: "
|
|
17471
|
-
hidden: true
|
|
17472
|
-
}),
|
|
17473
|
-
"api-base-url-2": Flags.string({
|
|
17474
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
17475
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
18025
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
17476
18026
|
hidden: true
|
|
17477
18027
|
}),
|
|
17478
18028
|
id: Flags.string({
|
|
@@ -17496,8 +18046,7 @@ var FunctionsSetSecretCommand = class FunctionsSetSecretCommand extends Command
|
|
|
17496
18046
|
await runWithTiming(flags.time, async () => {
|
|
17497
18047
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
17498
18048
|
apiKey: flags["api-key"],
|
|
17499
|
-
|
|
17500
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
18049
|
+
apiBaseUrl: flags["api-base-url"],
|
|
17501
18050
|
configDir: this.config.configDir
|
|
17502
18051
|
});
|
|
17503
18052
|
const authFailureContext = {
|
|
@@ -17694,14 +18243,9 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
17694
18243
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17695
18244
|
env: "PRIMITIVE_API_KEY"
|
|
17696
18245
|
}),
|
|
17697
|
-
"api-base-url
|
|
17698
|
-
description:
|
|
17699
|
-
env: "
|
|
17700
|
-
hidden: true
|
|
17701
|
-
}),
|
|
17702
|
-
"api-base-url-2": Flags.string({
|
|
17703
|
-
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
17704
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
18246
|
+
"api-base-url": Flags.string({
|
|
18247
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
18248
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
17705
18249
|
hidden: true
|
|
17706
18250
|
}),
|
|
17707
18251
|
id: Flags.string({
|
|
@@ -17729,8 +18273,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
17729
18273
|
const shouldShowSends = flags["show-sends"];
|
|
17730
18274
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
17731
18275
|
apiKey: flags["api-key"],
|
|
17732
|
-
|
|
17733
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
18276
|
+
apiBaseUrl: flags["api-base-url"],
|
|
17734
18277
|
configDir: this.config.configDir
|
|
17735
18278
|
});
|
|
17736
18279
|
await runWithTiming(flags.time, async () => {
|
|
@@ -17818,6 +18361,7 @@ const DOMAIN_DISPLAY_WIDTH = 34;
|
|
|
17818
18361
|
const STATUS_DISPLAY_WIDTH = 12;
|
|
17819
18362
|
const BOOL_DISPLAY_WIDTH = 7;
|
|
17820
18363
|
const NUM_DISPLAY_WIDTH = 6;
|
|
18364
|
+
const DEFAULT_PRIMITIVE_LOCAL_PART = "agent";
|
|
17821
18365
|
function plural(count, singular, pluralValue = `${singular}s`) {
|
|
17822
18366
|
return `${count} ${count === 1 ? singular : pluralValue}`;
|
|
17823
18367
|
}
|
|
@@ -17840,7 +18384,7 @@ function formatInboxDate(value) {
|
|
|
17840
18384
|
const pad = (n) => String(n).padStart(2, "0");
|
|
17841
18385
|
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())} UTC`;
|
|
17842
18386
|
}
|
|
17843
|
-
function truncate(value, width) {
|
|
18387
|
+
function truncate$1(value, width) {
|
|
17844
18388
|
if (value.length <= width) return value.padEnd(width);
|
|
17845
18389
|
return `${value.slice(0, width - 3)}...`;
|
|
17846
18390
|
}
|
|
@@ -17853,6 +18397,14 @@ function domainSummary(domain) {
|
|
|
17853
18397
|
default: return `${domain.domain} has status ${String(domain.status)}.`;
|
|
17854
18398
|
}
|
|
17855
18399
|
}
|
|
18400
|
+
function findSuggestedPrimitiveAddress(domains) {
|
|
18401
|
+
const domain = domains.find((entry) => entry.managed && entry.active && entry.receiving_ready);
|
|
18402
|
+
if (!domain) return null;
|
|
18403
|
+
return {
|
|
18404
|
+
address: `${DEFAULT_PRIMITIVE_LOCAL_PART}@${domain.domain}`,
|
|
18405
|
+
domain: domain.domain
|
|
18406
|
+
};
|
|
18407
|
+
}
|
|
17856
18408
|
function focusInboxStatus(status, domainName) {
|
|
17857
18409
|
const normalized = domainName.toLowerCase();
|
|
17858
18410
|
const domain = status.domains.find((entry) => entry.domain.toLowerCase() === normalized);
|
|
@@ -17882,7 +18434,7 @@ function formatDomainHeader() {
|
|
|
17882
18434
|
}
|
|
17883
18435
|
function formatDomainRow(domain) {
|
|
17884
18436
|
return [
|
|
17885
|
-
truncate(domain.domain, DOMAIN_DISPLAY_WIDTH),
|
|
18437
|
+
truncate$1(domain.domain, DOMAIN_DISPLAY_WIDTH),
|
|
17886
18438
|
statusText(domain.status).padEnd(STATUS_DISPLAY_WIDTH),
|
|
17887
18439
|
yesNo(domain.receiving_ready).padEnd(BOOL_DISPLAY_WIDTH),
|
|
17888
18440
|
yesNo(domain.processing_ready).padEnd(BOOL_DISPLAY_WIDTH),
|
|
@@ -17899,12 +18451,14 @@ function formatInboxStatus(status) {
|
|
|
17899
18451
|
"",
|
|
17900
18452
|
"Domains"
|
|
17901
18453
|
];
|
|
18454
|
+
const suggestedAddress = findSuggestedPrimitiveAddress(status.domains);
|
|
17902
18455
|
if (status.domains.length === 0) lines.push("No domains configured.");
|
|
17903
18456
|
else {
|
|
17904
18457
|
lines.push(formatDomainHeader());
|
|
17905
18458
|
for (const domain of status.domains) lines.push(formatDomainRow(domain));
|
|
17906
18459
|
}
|
|
17907
18460
|
lines.push("", `Endpoints: ${status.endpoints.enabled}/${status.endpoints.total} enabled (${status.endpoints.fallback_enabled} fallback, ${status.endpoints.domain_scoped_enabled} domain-scoped, ${status.endpoints.function_enabled} function)`, `Functions: ${status.functions.deployed}/${status.functions.total} deployed (${status.functions.pending} pending, ${status.functions.failed} failed)`, `Recent inbound: ${plural(status.recent_emails.total, "email")} latest ${formatInboxDate(status.recent_emails.latest_received_at)}`);
|
|
18461
|
+
if (suggestedAddress) lines.push("", `Primitive address: ${suggestedAddress.address}`, ` Any local-part at ${suggestedAddress.domain} can receive mail.`, ` Try: primitive send --to ${suggestedAddress.address} --subject "hello" --body "test"`);
|
|
17908
18462
|
if (status.next_actions.length > 0) {
|
|
17909
18463
|
lines.push("", "Next actions");
|
|
17910
18464
|
for (const action of status.next_actions) lines.push(formatNextAction(action));
|
|
@@ -17926,14 +18480,9 @@ var InboxStatusCommand = class InboxStatusCommand extends Command {
|
|
|
17926
18480
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17927
18481
|
env: "PRIMITIVE_API_KEY"
|
|
17928
18482
|
}),
|
|
17929
|
-
"api-base-url
|
|
17930
|
-
description:
|
|
17931
|
-
env: "
|
|
17932
|
-
hidden: true
|
|
17933
|
-
}),
|
|
17934
|
-
"api-base-url-2": Flags.string({
|
|
17935
|
-
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
17936
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
18483
|
+
"api-base-url": Flags.string({
|
|
18484
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
18485
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
17937
18486
|
hidden: true
|
|
17938
18487
|
}),
|
|
17939
18488
|
domain: Flags.string({ description: "Focus domain readiness and recent email fields on one domain returned by the inbox status API." }),
|
|
@@ -17945,8 +18494,7 @@ var InboxStatusCommand = class InboxStatusCommand extends Command {
|
|
|
17945
18494
|
await runWithTiming(flags.time, async () => {
|
|
17946
18495
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
17947
18496
|
apiKey: flags["api-key"],
|
|
17948
|
-
|
|
17949
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
18497
|
+
apiBaseUrl: flags["api-base-url"],
|
|
17950
18498
|
configDir: this.config.configDir
|
|
17951
18499
|
});
|
|
17952
18500
|
const result = await getInboxStatus({
|
|
@@ -17981,6 +18529,174 @@ var InboxStatusCommand = class InboxStatusCommand extends Command {
|
|
|
17981
18529
|
}
|
|
17982
18530
|
};
|
|
17983
18531
|
//#endregion
|
|
18532
|
+
//#region src/oclif/commands/inbox-setup.ts
|
|
18533
|
+
const DEFAULT_FUNCTION_NAME = "inbound-reply";
|
|
18534
|
+
const DEFAULT_LOCAL_PART = "inbox";
|
|
18535
|
+
const FUNCTION_ID_PLACEHOLDER = "<function-id>";
|
|
18536
|
+
function firstUsableManagedDomain(status) {
|
|
18537
|
+
return status.domains.find((domain) => domain.managed && domain.receiving_ready && domain.active) ?? status.domains.find((domain) => domain.managed && domain.receiving_ready) ?? null;
|
|
18538
|
+
}
|
|
18539
|
+
function buildInboxSetupCommands(functionName = DEFAULT_FUNCTION_NAME) {
|
|
18540
|
+
return {
|
|
18541
|
+
scaffold: [
|
|
18542
|
+
`primitive functions init ${functionName}`,
|
|
18543
|
+
`cd ${functionName}`,
|
|
18544
|
+
"npm install",
|
|
18545
|
+
"npm run build",
|
|
18546
|
+
`primitive functions deploy --name ${functionName} --file ./dist/handler.js --wait`,
|
|
18547
|
+
`primitive functions test --id ${FUNCTION_ID_PLACEHOLDER} --wait --show-sends`
|
|
18548
|
+
],
|
|
18549
|
+
logs: `primitive functions logs --id ${FUNCTION_ID_PLACEHOLDER}`,
|
|
18550
|
+
status: "primitive inbox status"
|
|
18551
|
+
};
|
|
18552
|
+
}
|
|
18553
|
+
function buildInboxSetupProof(commands) {
|
|
18554
|
+
return {
|
|
18555
|
+
after_test: [
|
|
18556
|
+
"inbound id for the generated test email",
|
|
18557
|
+
"function id matching the deployed Function",
|
|
18558
|
+
"invocation status completed, failed, or send_failed",
|
|
18559
|
+
"reply/send result emitted by the handler"
|
|
18560
|
+
],
|
|
18561
|
+
logs_command: commands.logs
|
|
18562
|
+
};
|
|
18563
|
+
}
|
|
18564
|
+
function buildInboxSetupGuide(status) {
|
|
18565
|
+
const domain = firstUsableManagedDomain(status);
|
|
18566
|
+
const commands = buildInboxSetupCommands();
|
|
18567
|
+
const mode = !status.receiving_ready ? "not_receiving" : status.processing_ready ? "actively_processed" : "stored_only";
|
|
18568
|
+
return {
|
|
18569
|
+
readiness: {
|
|
18570
|
+
ready: status.ready,
|
|
18571
|
+
receiving_ready: status.receiving_ready,
|
|
18572
|
+
processing_ready: status.processing_ready,
|
|
18573
|
+
mode,
|
|
18574
|
+
summary: status.summary
|
|
18575
|
+
},
|
|
18576
|
+
receive: {
|
|
18577
|
+
address: domain ? `${DEFAULT_LOCAL_PART}@${domain.domain}` : null,
|
|
18578
|
+
domain: domain?.domain ?? null,
|
|
18579
|
+
managed: domain?.managed ?? false,
|
|
18580
|
+
placeholder_local_part: domain ? DEFAULT_LOCAL_PART : null
|
|
18581
|
+
},
|
|
18582
|
+
processing: {
|
|
18583
|
+
stored_only: status.receiving_ready && !status.processing_ready,
|
|
18584
|
+
active: status.processing_ready,
|
|
18585
|
+
enabled_endpoints: status.endpoints.enabled,
|
|
18586
|
+
deployed_functions: status.functions.deployed
|
|
18587
|
+
},
|
|
18588
|
+
commands,
|
|
18589
|
+
proof: buildInboxSetupProof(commands),
|
|
18590
|
+
status
|
|
18591
|
+
};
|
|
18592
|
+
}
|
|
18593
|
+
function formatReadiness(guide) {
|
|
18594
|
+
const readiness = guide.readiness.ready ? "ready" : "not ready";
|
|
18595
|
+
const receiving = guide.readiness.receiving_ready ? "yes" : "no";
|
|
18596
|
+
const processing = guide.readiness.processing_ready ? "yes" : "no";
|
|
18597
|
+
const mode = guide.readiness.mode === "actively_processed" ? "actively processed" : guide.readiness.mode === "stored_only" ? "stored-only" : "not receiving";
|
|
18598
|
+
return [
|
|
18599
|
+
`Readiness: ${readiness}`,
|
|
18600
|
+
`Receiving: ${receiving}`,
|
|
18601
|
+
`Processing: ${processing}`,
|
|
18602
|
+
`Mode: ${mode}`
|
|
18603
|
+
].join("\n");
|
|
18604
|
+
}
|
|
18605
|
+
function formatReceiveAddress(guide) {
|
|
18606
|
+
if (!guide.receive.domain || !guide.receive.address) return "Receive address: none found on a receiving-ready Primitive-managed domain";
|
|
18607
|
+
return [`Receive address: ${guide.receive.address}`, `Receive domain: ${guide.receive.domain} (Primitive-managed)`].join("\n");
|
|
18608
|
+
}
|
|
18609
|
+
function formatDomainDetails(status) {
|
|
18610
|
+
if (status.domains.length === 0) return ["Domains: none configured"];
|
|
18611
|
+
return status.domains.map((domain) => `- ${domain.domain}: ${statusText(domain.status)}, receive ${domain.receiving_ready ? "yes" : "no"}, process ${domain.processing_ready ? "yes" : "no"}, routes ${domain.processing_route_count}`);
|
|
18612
|
+
}
|
|
18613
|
+
function formatScaffoldCommands(commands) {
|
|
18614
|
+
return commands.scaffold.map((command) => ` ${command}`);
|
|
18615
|
+
}
|
|
18616
|
+
function formatInboxSetupGuide(guide) {
|
|
18617
|
+
const lines = [
|
|
18618
|
+
"Inbound setup",
|
|
18619
|
+
"",
|
|
18620
|
+
guide.readiness.summary,
|
|
18621
|
+
"",
|
|
18622
|
+
formatReadiness(guide),
|
|
18623
|
+
"",
|
|
18624
|
+
formatReceiveAddress(guide),
|
|
18625
|
+
"",
|
|
18626
|
+
"Domains",
|
|
18627
|
+
...formatDomainDetails(guide.status),
|
|
18628
|
+
"",
|
|
18629
|
+
`Processing routes: ${guide.processing.enabled_endpoints} enabled endpoint(s), ${guide.processing.deployed_functions} deployed Function(s)`
|
|
18630
|
+
];
|
|
18631
|
+
if (guide.readiness.mode === "not_receiving") lines.push("", "Next actions", "Make a receiving-ready domain available, then re-run:", ` ${guide.commands.status}`);
|
|
18632
|
+
else if (!guide.processing.active) lines.push("", "Next actions", "No processing route is enabled. Scaffold, deploy, and test an email Function:", ...formatScaffoldCommands(guide.commands));
|
|
18633
|
+
else lines.push("", "Next actions", "Inbound mail has an active processing route. Run a Function test when you know the Function id:", ` primitive functions test --id ${FUNCTION_ID_PLACEHOLDER} --wait --show-sends`);
|
|
18634
|
+
if (guide.status.next_actions.length > 0) {
|
|
18635
|
+
lines.push("", "API suggested actions");
|
|
18636
|
+
for (const action of guide.status.next_actions) lines.push(action.command ? `- ${action.message}\n ${action.command}` : `- ${action.message}`);
|
|
18637
|
+
}
|
|
18638
|
+
lines.push("", "Proof after functions test", "- Inbound id: the generated test email should have an inbound id.", "- Function id: the run should point at the Function id you deployed.", "- Invocation status: expect completed; failed or send_failed identifies the failing stage.", "- Reply/send result: --show-sends should show the handler's outbound result when it replies or sends.", "- Logs:", ` ${guide.proof.logs_command}`);
|
|
18639
|
+
return lines.join("\n");
|
|
18640
|
+
}
|
|
18641
|
+
var InboxSetupCommand = class InboxSetupCommand extends Command {
|
|
18642
|
+
static description = `Guide inbound email setup from the server-owned inbox status API.
|
|
18643
|
+
|
|
18644
|
+
This command does not scaffold, deploy, or run tests. It verifies auth, fetches inbox readiness, shows the first usable Primitive-managed receive address/domain, explains whether inbound mail is stored-only or actively processed, and prints the exact commands to add a Function processing route when one is missing.`;
|
|
18645
|
+
static summary = "Guide inbound email setup";
|
|
18646
|
+
static examples = ["<%= config.bin %> inbox setup", "<%= config.bin %> inbox setup --json"];
|
|
18647
|
+
static flags = {
|
|
18648
|
+
"api-key": Flags.string({
|
|
18649
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
18650
|
+
env: "PRIMITIVE_API_KEY"
|
|
18651
|
+
}),
|
|
18652
|
+
"api-base-url": Flags.string({
|
|
18653
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
18654
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18655
|
+
hidden: true
|
|
18656
|
+
}),
|
|
18657
|
+
json: Flags.boolean({ description: "Print structured readiness, receive address, commands, proof metadata, and raw status as JSON." }),
|
|
18658
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
18659
|
+
};
|
|
18660
|
+
async run() {
|
|
18661
|
+
const { flags } = await this.parse(InboxSetupCommand);
|
|
18662
|
+
await runWithTiming(flags.time, async () => {
|
|
18663
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
18664
|
+
apiKey: flags["api-key"],
|
|
18665
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18666
|
+
configDir: this.config.configDir
|
|
18667
|
+
});
|
|
18668
|
+
const result = await getInboxStatus({
|
|
18669
|
+
client: apiClient.client,
|
|
18670
|
+
responseStyle: "fields"
|
|
18671
|
+
});
|
|
18672
|
+
if (result.error) {
|
|
18673
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
18674
|
+
writeErrorWithHints(errorPayload);
|
|
18675
|
+
surfaceUnauthorizedHint({
|
|
18676
|
+
auth,
|
|
18677
|
+
baseUrlOverridden,
|
|
18678
|
+
configDir: this.config.configDir,
|
|
18679
|
+
payload: errorPayload
|
|
18680
|
+
});
|
|
18681
|
+
process.exitCode = 1;
|
|
18682
|
+
return;
|
|
18683
|
+
}
|
|
18684
|
+
const envelope = result.data ?? {};
|
|
18685
|
+
const status = envelope.data;
|
|
18686
|
+
if (!status) throw new Errors.CLIError("Primitive API returned no inbox status.", { exit: 1 });
|
|
18687
|
+
const guide = buildInboxSetupGuide(status);
|
|
18688
|
+
if (flags.json) {
|
|
18689
|
+
this.log(JSON.stringify({
|
|
18690
|
+
...envelope,
|
|
18691
|
+
data: guide
|
|
18692
|
+
}, null, 2));
|
|
18693
|
+
return;
|
|
18694
|
+
}
|
|
18695
|
+
this.log(formatInboxSetupGuide(guide));
|
|
18696
|
+
});
|
|
18697
|
+
}
|
|
18698
|
+
};
|
|
18699
|
+
//#endregion
|
|
17984
18700
|
//#region src/oclif/commands/login.ts
|
|
17985
18701
|
const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
|
|
17986
18702
|
function cliError$3(message) {
|
|
@@ -18013,14 +18729,14 @@ function retryAfterSeconds$1(result) {
|
|
|
18013
18729
|
}
|
|
18014
18730
|
async function checkExistingLogin(params) {
|
|
18015
18731
|
const requestConfig = resolveCliApiRequestConfig({
|
|
18016
|
-
|
|
18732
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
18017
18733
|
configDir: params.configDir
|
|
18018
18734
|
});
|
|
18019
|
-
const
|
|
18735
|
+
const probeApiBaseUrl = requestConfig.apiBaseUrl ?? params.credentials.api_base_url;
|
|
18020
18736
|
let credentials = params.credentials;
|
|
18021
18737
|
try {
|
|
18022
18738
|
credentials = await refreshStoredCliCredentials({
|
|
18023
|
-
|
|
18739
|
+
apiBaseUrl: probeApiBaseUrl,
|
|
18024
18740
|
configDir: params.configDir,
|
|
18025
18741
|
credentials,
|
|
18026
18742
|
credentialsLockHeld: params.credentialsLockHeld,
|
|
@@ -18036,8 +18752,7 @@ async function checkExistingLogin(params) {
|
|
|
18036
18752
|
}
|
|
18037
18753
|
const apiClient = new PrimitiveApiClient({
|
|
18038
18754
|
apiKey: credentials.access_token,
|
|
18039
|
-
|
|
18040
|
-
apiBaseUrl2: requestConfig.resolvedApiBaseUrl2,
|
|
18755
|
+
apiBaseUrl: probeApiBaseUrl,
|
|
18041
18756
|
headers: requestConfig.headers
|
|
18042
18757
|
});
|
|
18043
18758
|
const result = await (params.checkAccount ?? ((client) => getAccount({
|
|
@@ -18047,7 +18762,7 @@ async function checkExistingLogin(params) {
|
|
|
18047
18762
|
if (!result.error) return { status: "valid" };
|
|
18048
18763
|
const payload = extractErrorPayload(result.error);
|
|
18049
18764
|
const code = extractErrorCode(payload);
|
|
18050
|
-
const baseUrlDiffersFromSaved = requestConfig.baseUrlOverridden && requestConfig.
|
|
18765
|
+
const baseUrlDiffersFromSaved = requestConfig.baseUrlOverridden && requestConfig.apiBaseUrl !== params.credentials.api_base_url;
|
|
18051
18766
|
if (code === API_ERROR_CODES.unauthorized && !baseUrlDiffersFromSaved) {
|
|
18052
18767
|
deleteCliCredentials(params.configDir);
|
|
18053
18768
|
process.stderr.write("Removed saved Primitive CLI OAuth credentials because the existing session was rejected during login. Continuing with a fresh login.\n");
|
|
@@ -18068,9 +18783,9 @@ var LoginCommand$1 = class extends Command {
|
|
|
18068
18783
|
"<%= config.bin %> login --force"
|
|
18069
18784
|
];
|
|
18070
18785
|
static flags = {
|
|
18071
|
-
"api-base-url
|
|
18786
|
+
"api-base-url": Flags.string({
|
|
18072
18787
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
18073
|
-
env: "
|
|
18788
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18074
18789
|
hidden: true
|
|
18075
18790
|
}),
|
|
18076
18791
|
"device-name": Flags.string({ description: "Device name shown in the browser approval screen" }),
|
|
@@ -18103,10 +18818,10 @@ var LoginCommand$1 = class extends Command {
|
|
|
18103
18818
|
}
|
|
18104
18819
|
async runWithCredentialLock(flags, retryCommand) {
|
|
18105
18820
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
18106
|
-
|
|
18821
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18107
18822
|
configDir: this.config.configDir
|
|
18108
18823
|
});
|
|
18109
|
-
const
|
|
18824
|
+
const apiBaseUrl = requestConfig.resolvedApiBaseUrl;
|
|
18110
18825
|
let existing;
|
|
18111
18826
|
try {
|
|
18112
18827
|
existing = loadCliCredentials(this.config.configDir);
|
|
@@ -18119,7 +18834,7 @@ var LoginCommand$1 = class extends Command {
|
|
|
18119
18834
|
if (existing && flags.force) process.stderr.write("Replacing saved Primitive CLI credentials after browser approval because --force was set.\n");
|
|
18120
18835
|
else if (existing) {
|
|
18121
18836
|
const existingStatus = await checkExistingLogin({
|
|
18122
|
-
|
|
18837
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18123
18838
|
configDir: this.config.configDir,
|
|
18124
18839
|
credentials: existing,
|
|
18125
18840
|
credentialsLockHeld: true
|
|
@@ -18165,7 +18880,7 @@ var LoginCommand$1 = class extends Command {
|
|
|
18165
18880
|
deleteChatState(this.config.configDir);
|
|
18166
18881
|
saveCliCredentials(this.config.configDir, {
|
|
18167
18882
|
access_token: login.access_token,
|
|
18168
|
-
|
|
18883
|
+
api_base_url: apiBaseUrl,
|
|
18169
18884
|
auth_method: "oauth",
|
|
18170
18885
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18171
18886
|
expires_at: cliAccessTokenExpiresAt(login.expires_in),
|
|
@@ -18229,9 +18944,11 @@ function normalizeEmail(email) {
|
|
|
18229
18944
|
}
|
|
18230
18945
|
function pendingSignupFromJson(value) {
|
|
18231
18946
|
if (!isRecord(value)) return null;
|
|
18232
|
-
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.
|
|
18947
|
+
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;
|
|
18948
|
+
const apiBaseUrl = value.api_base_url ?? value.api_base_url_1;
|
|
18949
|
+
if (typeof apiBaseUrl !== "string") return null;
|
|
18233
18950
|
return {
|
|
18234
|
-
|
|
18951
|
+
api_base_url: apiBaseUrl,
|
|
18235
18952
|
created_at: value.created_at,
|
|
18236
18953
|
email: value.email,
|
|
18237
18954
|
expires_at: value.expires_at,
|
|
@@ -18247,20 +18964,20 @@ function pendingSignupPath(configDir) {
|
|
|
18247
18964
|
function deletePendingAgentSignup(configDir) {
|
|
18248
18965
|
rmSync(pendingSignupPath(configDir), { force: true });
|
|
18249
18966
|
}
|
|
18250
|
-
function pendingSignupFromStart(start,
|
|
18967
|
+
function pendingSignupFromStart(start, apiBaseUrl) {
|
|
18251
18968
|
return {
|
|
18252
18969
|
...start,
|
|
18253
|
-
|
|
18970
|
+
api_base_url: apiBaseUrl,
|
|
18254
18971
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18255
18972
|
expires_at: new Date(Date.now() + start.expires_in * 1e3).toISOString()
|
|
18256
18973
|
};
|
|
18257
18974
|
}
|
|
18258
|
-
function savePendingAgentSignup(configDir, start,
|
|
18975
|
+
function savePendingAgentSignup(configDir, start, apiBaseUrl) {
|
|
18259
18976
|
mkdirSync(configDir, {
|
|
18260
18977
|
mode: 448,
|
|
18261
18978
|
recursive: true
|
|
18262
18979
|
});
|
|
18263
|
-
const pending = pendingSignupFromStart(start,
|
|
18980
|
+
const pending = pendingSignupFromStart(start, apiBaseUrl);
|
|
18264
18981
|
const path = pendingSignupPath(configDir);
|
|
18265
18982
|
const tempPath = join(configDir, `${PENDING_SIGNUP_FILE}.${process$1.pid}.${randomUUID()}.tmp`);
|
|
18266
18983
|
try {
|
|
@@ -18274,7 +18991,7 @@ function savePendingAgentSignup(configDir, start, apiBaseUrl1) {
|
|
|
18274
18991
|
throw error;
|
|
18275
18992
|
}
|
|
18276
18993
|
}
|
|
18277
|
-
function loadPendingAgentSignup(configDir,
|
|
18994
|
+
function loadPendingAgentSignup(configDir, apiBaseUrl) {
|
|
18278
18995
|
const path = pendingSignupPath(configDir);
|
|
18279
18996
|
let contents;
|
|
18280
18997
|
try {
|
|
@@ -18293,7 +19010,7 @@ function loadPendingAgentSignup(configDir, apiBaseUrl1) {
|
|
|
18293
19010
|
deletePendingAgentSignup(configDir);
|
|
18294
19011
|
return null;
|
|
18295
19012
|
}
|
|
18296
|
-
if (pending.
|
|
19013
|
+
if (pending.api_base_url !== apiBaseUrl) return null;
|
|
18297
19014
|
if (new Date(pending.expires_at).getTime() <= Date.now()) {
|
|
18298
19015
|
deletePendingAgentSignup(configDir);
|
|
18299
19016
|
return null;
|
|
@@ -18303,11 +19020,100 @@ function loadPendingAgentSignup(configDir, apiBaseUrl1) {
|
|
|
18303
19020
|
expires_in: Math.max(0, Math.ceil((new Date(pending.expires_at).getTime() - Date.now()) / 1e3))
|
|
18304
19021
|
};
|
|
18305
19022
|
}
|
|
19023
|
+
function readPendingAgentSignupState(configDir, apiBaseUrl) {
|
|
19024
|
+
const path = pendingSignupPath(configDir);
|
|
19025
|
+
let contents;
|
|
19026
|
+
try {
|
|
19027
|
+
contents = readFileSync(path, "utf8");
|
|
19028
|
+
} catch (error) {
|
|
19029
|
+
if (error && typeof error === "object" && error.code === "ENOENT") return null;
|
|
19030
|
+
throw error;
|
|
19031
|
+
}
|
|
19032
|
+
let pending;
|
|
19033
|
+
try {
|
|
19034
|
+
pending = pendingSignupFromJson(JSON.parse(contents));
|
|
19035
|
+
} catch {
|
|
19036
|
+
pending = null;
|
|
19037
|
+
}
|
|
19038
|
+
if (!pending) {
|
|
19039
|
+
deletePendingAgentSignup(configDir);
|
|
19040
|
+
return null;
|
|
19041
|
+
}
|
|
19042
|
+
if (pending.api_base_url !== apiBaseUrl) return null;
|
|
19043
|
+
return pending;
|
|
19044
|
+
}
|
|
19045
|
+
function pendingSignupStartCommand(email) {
|
|
19046
|
+
return `primitive signup ${email ?? "<email>"} --signup-code <invite-code> --accept-terms`;
|
|
19047
|
+
}
|
|
19048
|
+
function buildSignupStatus(params) {
|
|
19049
|
+
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
19050
|
+
const pending = readPendingAgentSignupState(params.configDir, params.apiBaseUrl);
|
|
19051
|
+
if (!pending) return {
|
|
19052
|
+
code_length: null,
|
|
19053
|
+
confirm_command: null,
|
|
19054
|
+
email: null,
|
|
19055
|
+
expired: false,
|
|
19056
|
+
expires_at: null,
|
|
19057
|
+
expires_in: null,
|
|
19058
|
+
pending: false,
|
|
19059
|
+
resend_after: null,
|
|
19060
|
+
resend_command: null,
|
|
19061
|
+
signup_command: pendingSignupStartCommand(params.email)
|
|
19062
|
+
};
|
|
19063
|
+
if (params.email && normalizeEmail(pending.email) !== normalizeEmail(params.email)) throw cliError$2(`Pending ${copy.actionNoun} is for ${pending.email}, not ${params.email}. Run \`primitive signup status\` without an email argument to inspect it.`);
|
|
19064
|
+
const expiresAtMs = new Date(pending.expires_at).getTime();
|
|
19065
|
+
const expiresIn = Number.isFinite(expiresAtMs) ? Math.ceil((expiresAtMs - Date.now()) / 1e3) : null;
|
|
19066
|
+
return {
|
|
19067
|
+
code_length: pending.verification_code_length,
|
|
19068
|
+
confirm_command: `primitive ${copy.confirmCommand(pending.email)}`,
|
|
19069
|
+
email: pending.email,
|
|
19070
|
+
expired: expiresIn !== null && expiresIn <= 0,
|
|
19071
|
+
expires_at: pending.expires_at,
|
|
19072
|
+
expires_in: expiresIn === null ? null : Math.max(0, expiresIn),
|
|
19073
|
+
pending: true,
|
|
19074
|
+
resend_after: pending.resend_after,
|
|
19075
|
+
resend_command: `primitive ${copy.resendCommand(pending.email)}`
|
|
19076
|
+
};
|
|
19077
|
+
}
|
|
19078
|
+
function writeSignupStatus(status) {
|
|
19079
|
+
if (!status.pending) {
|
|
19080
|
+
process$1.stdout.write("No pending Primitive signup found.\n");
|
|
19081
|
+
process$1.stdout.write(`Start one with \`${status.signup_command ?? pendingSignupStartCommand()}\`.\n`);
|
|
19082
|
+
return;
|
|
19083
|
+
}
|
|
19084
|
+
process$1.stdout.write(`Pending Primitive signup for ${status.email}.\n`);
|
|
19085
|
+
if (status.code_length !== null) process$1.stdout.write(`Verification code length: ${status.code_length}\n`);
|
|
19086
|
+
if (status.expires_at) if (status.expired) process$1.stdout.write(`Expired at: ${status.expires_at}\n`);
|
|
19087
|
+
else {
|
|
19088
|
+
process$1.stdout.write(`Expires at: ${status.expires_at}\n`);
|
|
19089
|
+
process$1.stdout.write(`Expires in: ${formatSignupSeconds(status.expires_in)}\n`);
|
|
19090
|
+
}
|
|
19091
|
+
if (status.resend_after !== null) process$1.stdout.write(`Resend after: ${formatSignupSeconds(status.resend_after)}\n`);
|
|
19092
|
+
if (status.confirm_command) process$1.stdout.write(`Confirm: ${status.confirm_command}\n`);
|
|
19093
|
+
if (status.resend_command) process$1.stdout.write(`Resend: ${status.resend_command}\n`);
|
|
19094
|
+
}
|
|
19095
|
+
function runSignupStatus(params) {
|
|
19096
|
+
const { requestConfig } = createCliApiClient({
|
|
19097
|
+
apiBaseUrl: params.flags["api-base-url"],
|
|
19098
|
+
configDir: params.configDir
|
|
19099
|
+
});
|
|
19100
|
+
const status = buildSignupStatus({
|
|
19101
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
19102
|
+
configDir: params.configDir,
|
|
19103
|
+
copy: params.copy,
|
|
19104
|
+
email: params.email
|
|
19105
|
+
});
|
|
19106
|
+
if (params.flags.json) {
|
|
19107
|
+
process$1.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
|
|
19108
|
+
return;
|
|
19109
|
+
}
|
|
19110
|
+
writeSignupStatus(status);
|
|
19111
|
+
}
|
|
18306
19112
|
function requirePendingSignupForEmail(params) {
|
|
18307
19113
|
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
18308
|
-
const pending = loadPendingAgentSignup(params.configDir, params.
|
|
18309
|
-
if (!pending) throw cliError$2(`No pending ${copy.actionNoun} for ${params.email}. Run \`primitive ${copy.startCommand(params.email)}\` first.`);
|
|
18310
|
-
if (normalizeEmail(pending.email) !== normalizeEmail(params.email)) throw cliError$2(`Pending ${copy.actionNoun} is for ${pending.email}, not ${params.email}. Run \`primitive ${copy.startCommand(params.email)} --force\` to replace it.`);
|
|
19114
|
+
const pending = loadPendingAgentSignup(params.configDir, params.apiBaseUrl);
|
|
19115
|
+
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.`);
|
|
19116
|
+
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.`);
|
|
18311
19117
|
return pending;
|
|
18312
19118
|
}
|
|
18313
19119
|
function retryAfterSeconds(result) {
|
|
@@ -18368,7 +19174,7 @@ async function checkExistingCredentials(params) {
|
|
|
18368
19174
|
}
|
|
18369
19175
|
if (!existing) return;
|
|
18370
19176
|
const existingStatus = await checkExistingLoginFn({
|
|
18371
|
-
|
|
19177
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
18372
19178
|
configDir: params.configDir,
|
|
18373
19179
|
credentials: existing,
|
|
18374
19180
|
credentialsLockHeld: true
|
|
@@ -18387,7 +19193,7 @@ function saveSignupCredentials(params) {
|
|
|
18387
19193
|
deleteChatState(params.configDir);
|
|
18388
19194
|
saveCliCredentials(params.configDir, {
|
|
18389
19195
|
access_token: params.signup.access_token,
|
|
18390
|
-
|
|
19196
|
+
api_base_url: params.apiBaseUrl,
|
|
18391
19197
|
auth_method: "oauth",
|
|
18392
19198
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18393
19199
|
expires_at: cliAccessTokenExpiresAt(params.signup.expires_in),
|
|
@@ -18406,17 +19212,17 @@ function writeStartInstructions(start, copy = DEFAULT_SIGNUP_COMMAND_COPY) {
|
|
|
18406
19212
|
}
|
|
18407
19213
|
async function startSignup(params) {
|
|
18408
19214
|
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
18409
|
-
const existingPending = loadPendingAgentSignup(params.configDir, params.
|
|
19215
|
+
const existingPending = loadPendingAgentSignup(params.configDir, params.apiBaseUrl);
|
|
18410
19216
|
if (existingPending && !params.flags.force) {
|
|
18411
19217
|
if (normalizeEmail(existingPending.email) === normalizeEmail(params.email)) {
|
|
18412
19218
|
process$1.stderr.write(`Continuing pending Primitive ${copy.actionNoun} for ${existingPending.email}.\n`);
|
|
18413
|
-
process$1.stderr.write(`Run \`primitive ${copy.confirmCommand(existingPending.email)}\` to finish,
|
|
19219
|
+
process$1.stderr.write(`Run \`primitive ${copy.confirmCommand(existingPending.email)}\` to finish, \`primitive ${copy.resendCommand(existingPending.email)}\` to send a new code, or \`primitive signup status\` to inspect it.\n`);
|
|
18414
19220
|
return {
|
|
18415
19221
|
pending: existingPending,
|
|
18416
19222
|
started: false
|
|
18417
19223
|
};
|
|
18418
19224
|
}
|
|
18419
|
-
throw cliError$2(`Pending ${copy.actionNoun} is for ${existingPending.email}. Run \`primitive ${copy.startCommand(params.email)} --force\` to replace it.`);
|
|
19225
|
+
throw cliError$2(`Pending ${copy.actionNoun} is for ${existingPending.email}. Run \`primitive signup status\` to inspect it, or \`primitive ${copy.startCommand(params.email)} --force\` to replace it.`);
|
|
18420
19226
|
}
|
|
18421
19227
|
if (params.flags.force) deletePendingAgentSignup(params.configDir);
|
|
18422
19228
|
const promptRequiredFn = params.deps.promptRequired ?? promptRequired;
|
|
@@ -18441,7 +19247,7 @@ async function startSignup(params) {
|
|
|
18441
19247
|
const startResult = unwrapData$1(started.data);
|
|
18442
19248
|
if (!startResult) throw cliError$2("Primitive API returned an empty agent signup response.");
|
|
18443
19249
|
return {
|
|
18444
|
-
pending: savePendingAgentSignup(params.configDir, startResult, params.
|
|
19250
|
+
pending: savePendingAgentSignup(params.configDir, startResult, params.apiBaseUrl),
|
|
18445
19251
|
started: true
|
|
18446
19252
|
};
|
|
18447
19253
|
}
|
|
@@ -18461,7 +19267,7 @@ async function resendVerificationCode(params) {
|
|
|
18461
19267
|
verification_code_length: resend.verification_code_length
|
|
18462
19268
|
} : params.start;
|
|
18463
19269
|
return {
|
|
18464
|
-
pending: savePendingAgentSignup(params.configDir, next, params.
|
|
19270
|
+
pending: savePendingAgentSignup(params.configDir, next, params.apiBaseUrl),
|
|
18465
19271
|
resent: true
|
|
18466
19272
|
};
|
|
18467
19273
|
}
|
|
@@ -18485,18 +19291,18 @@ async function runSignupStartWithCredentialLock(params) {
|
|
|
18485
19291
|
const promptRequiredFn = deps.promptRequired ?? promptRequired;
|
|
18486
19292
|
const email = params.email ?? await promptRequiredFn("Email: ");
|
|
18487
19293
|
await checkExistingCredentials({
|
|
18488
|
-
|
|
19294
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18489
19295
|
configDir,
|
|
18490
19296
|
copy: params.copy,
|
|
18491
19297
|
deps,
|
|
18492
19298
|
flags
|
|
18493
19299
|
});
|
|
18494
19300
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
18495
|
-
|
|
19301
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18496
19302
|
configDir
|
|
18497
19303
|
});
|
|
18498
19304
|
const start = await startSignup({
|
|
18499
|
-
|
|
19305
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
18500
19306
|
apiClient,
|
|
18501
19307
|
configDir,
|
|
18502
19308
|
copy: params.copy,
|
|
@@ -18510,19 +19316,19 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
18510
19316
|
const { configDir, flags } = params;
|
|
18511
19317
|
const deps = params.deps ?? {};
|
|
18512
19318
|
if (!params.skipExistingCredentialCheck) await checkExistingCredentials({
|
|
18513
|
-
|
|
19319
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18514
19320
|
configDir,
|
|
18515
19321
|
copy: params.copy,
|
|
18516
19322
|
deps,
|
|
18517
19323
|
flags
|
|
18518
19324
|
});
|
|
18519
19325
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
18520
|
-
|
|
19326
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18521
19327
|
configDir
|
|
18522
19328
|
});
|
|
18523
|
-
const
|
|
19329
|
+
const apiBaseUrl = requestConfig.resolvedApiBaseUrl;
|
|
18524
19330
|
const pending = requirePendingSignupForEmail({
|
|
18525
|
-
|
|
19331
|
+
apiBaseUrl,
|
|
18526
19332
|
copy: params.copy,
|
|
18527
19333
|
configDir,
|
|
18528
19334
|
email: params.email
|
|
@@ -18540,7 +19346,7 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
18540
19346
|
const signup = unwrapData$1(verified.data);
|
|
18541
19347
|
if (!signup) throw cliError$2("Primitive API returned an empty agent signup verification response.");
|
|
18542
19348
|
saveSignupCredentials({
|
|
18543
|
-
|
|
19349
|
+
apiBaseUrl,
|
|
18544
19350
|
configDir,
|
|
18545
19351
|
signup
|
|
18546
19352
|
});
|
|
@@ -18552,25 +19358,27 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
18552
19358
|
}
|
|
18553
19359
|
const payload = extractErrorPayload(verified.error);
|
|
18554
19360
|
const code = extractErrorCode(payload);
|
|
18555
|
-
if (code === INVALID_VERIFICATION_CODE) throw cliError$2(`Invalid verification code. Try again
|
|
19361
|
+
if (code === INVALID_VERIFICATION_CODE) throw cliError$2(`Invalid verification code. Try again, run ${(params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY).resendCommand(params.email)}, or run primitive signup status.`);
|
|
18556
19362
|
if (code === EXPIRED_TOKEN || code === INVALID_SIGNUP_TOKEN) deletePendingAgentSignup(configDir);
|
|
18557
19363
|
writeErrorWithHints(payload);
|
|
18558
19364
|
throw cliError$2("Primitive agent signup failed while verifying the account.");
|
|
18559
19365
|
}
|
|
18560
19366
|
async function runSignupResendWithCredentialLock(params) {
|
|
18561
19367
|
const deps = params.deps ?? {};
|
|
19368
|
+
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
18562
19369
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
18563
|
-
|
|
19370
|
+
apiBaseUrl: params.flags["api-base-url"],
|
|
18564
19371
|
configDir: params.configDir
|
|
18565
19372
|
});
|
|
18566
|
-
const pending = requirePendingSignupForEmail({
|
|
18567
|
-
|
|
18568
|
-
copy
|
|
19373
|
+
const pending = params.email ? requirePendingSignupForEmail({
|
|
19374
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
19375
|
+
copy,
|
|
18569
19376
|
configDir: params.configDir,
|
|
18570
19377
|
email: params.email
|
|
18571
|
-
});
|
|
19378
|
+
}) : loadPendingAgentSignup(params.configDir, requestConfig.resolvedApiBaseUrl);
|
|
19379
|
+
if (!pending) throw cliError$2(`No pending ${copy.actionNoun} found. Run \`primitive signup status\` to inspect pending state, or start one with \`${pendingSignupStartCommand()}\`.`);
|
|
18572
19380
|
const resend = await resendVerificationCode({
|
|
18573
|
-
|
|
19381
|
+
apiBaseUrl: requestConfig.resolvedApiBaseUrl,
|
|
18574
19382
|
apiClient,
|
|
18575
19383
|
configDir: params.configDir,
|
|
18576
19384
|
deps,
|
|
@@ -18583,20 +19391,20 @@ async function runSignupInteractiveWithCredentialLock(params) {
|
|
|
18583
19391
|
const deps = params.deps ?? {};
|
|
18584
19392
|
const promptRequiredFn = deps.promptRequired ?? promptRequired;
|
|
18585
19393
|
await checkExistingCredentials({
|
|
18586
|
-
|
|
19394
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18587
19395
|
configDir,
|
|
18588
19396
|
deps,
|
|
18589
19397
|
flags
|
|
18590
19398
|
});
|
|
18591
19399
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
18592
|
-
|
|
19400
|
+
apiBaseUrl: flags["api-base-url"],
|
|
18593
19401
|
configDir
|
|
18594
19402
|
});
|
|
18595
|
-
const
|
|
18596
|
-
let start = flags.force ? null : loadPendingAgentSignup(configDir,
|
|
19403
|
+
const apiBaseUrl = requestConfig.resolvedApiBaseUrl;
|
|
19404
|
+
let start = flags.force ? null : loadPendingAgentSignup(configDir, apiBaseUrl);
|
|
18597
19405
|
if (start) process$1.stderr.write(`Continuing pending Primitive signup for ${start.email}.\n`);
|
|
18598
19406
|
else start = (await startSignup({
|
|
18599
|
-
|
|
19407
|
+
apiBaseUrl,
|
|
18600
19408
|
apiClient,
|
|
18601
19409
|
configDir,
|
|
18602
19410
|
deps,
|
|
@@ -18610,7 +19418,7 @@ async function runSignupInteractiveWithCredentialLock(params) {
|
|
|
18610
19418
|
const verificationCode = await promptRequiredFn(`Verification code (${start.verification_code_length} digits): `);
|
|
18611
19419
|
if (verificationCode.toLowerCase() === "resend") {
|
|
18612
19420
|
const resend = await resendVerificationCode({
|
|
18613
|
-
|
|
19421
|
+
apiBaseUrl,
|
|
18614
19422
|
apiClient,
|
|
18615
19423
|
configDir,
|
|
18616
19424
|
deps,
|
|
@@ -18627,7 +19435,7 @@ async function runSignupInteractiveWithCredentialLock(params) {
|
|
|
18627
19435
|
deps,
|
|
18628
19436
|
email: start.email,
|
|
18629
19437
|
flags: {
|
|
18630
|
-
"api-base-url
|
|
19438
|
+
"api-base-url": flags["api-base-url"],
|
|
18631
19439
|
force: true
|
|
18632
19440
|
},
|
|
18633
19441
|
skipExistingCredentialCheck: true
|
|
@@ -18645,9 +19453,9 @@ async function runSignupInteractiveWithCredentialLock(params) {
|
|
|
18645
19453
|
function commonStartFlags() {
|
|
18646
19454
|
return {
|
|
18647
19455
|
"accept-terms": Flags.boolean({ description: "Confirm acceptance of Primitive's Terms of Service and Privacy Policy" }),
|
|
18648
|
-
"api-base-url
|
|
19456
|
+
"api-base-url": Flags.string({
|
|
18649
19457
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
18650
|
-
env: "
|
|
19458
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18651
19459
|
hidden: true
|
|
18652
19460
|
}),
|
|
18653
19461
|
"device-name": Flags.string({ description: "Device name used for the created CLI OAuth session" }),
|
|
@@ -18708,9 +19516,9 @@ var SignupConfirmCommand = class SignupConfirmCommand extends Command {
|
|
|
18708
19516
|
static summary = "Confirm account signup";
|
|
18709
19517
|
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"];
|
|
18710
19518
|
static flags = {
|
|
18711
|
-
"api-base-url
|
|
19519
|
+
"api-base-url": Flags.string({
|
|
18712
19520
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
18713
|
-
env: "
|
|
19521
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18714
19522
|
hidden: true
|
|
18715
19523
|
}),
|
|
18716
19524
|
force: Flags.boolean({
|
|
@@ -18741,15 +19549,15 @@ var SignupConfirmCommand = class SignupConfirmCommand extends Command {
|
|
|
18741
19549
|
};
|
|
18742
19550
|
var SignupResendCommand = class SignupResendCommand extends Command {
|
|
18743
19551
|
static args = { email: Args.string({
|
|
18744
|
-
description: "Email address used to start signup",
|
|
18745
|
-
required:
|
|
19552
|
+
description: "Email address used to start signup. Defaults to the saved pending signup.",
|
|
19553
|
+
required: false
|
|
18746
19554
|
}) };
|
|
18747
19555
|
static description = "Resend the verification code for a pending signup.";
|
|
18748
19556
|
static summary = "Resend signup verification code";
|
|
18749
|
-
static examples = ["<%= config.bin %> signup resend user@example.com"];
|
|
18750
|
-
static flags = { "api-base-url
|
|
19557
|
+
static examples = ["<%= config.bin %> signup resend", "<%= config.bin %> signup resend user@example.com"];
|
|
19558
|
+
static flags = { "api-base-url": Flags.string({
|
|
18751
19559
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
18752
|
-
env: "
|
|
19560
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18753
19561
|
hidden: true
|
|
18754
19562
|
}) };
|
|
18755
19563
|
async run() {
|
|
@@ -18771,6 +19579,35 @@ var SignupResendCommand = class SignupResendCommand extends Command {
|
|
|
18771
19579
|
}
|
|
18772
19580
|
}
|
|
18773
19581
|
};
|
|
19582
|
+
var SignupStatusCommand = class SignupStatusCommand extends Command {
|
|
19583
|
+
static args = { email: Args.string({
|
|
19584
|
+
description: "Email address expected in the pending signup",
|
|
19585
|
+
required: false
|
|
19586
|
+
}) };
|
|
19587
|
+
static description = "Inspect the locally saved pending Primitive signup state.";
|
|
19588
|
+
static summary = "Show pending signup status";
|
|
19589
|
+
static examples = [
|
|
19590
|
+
"<%= config.bin %> signup status",
|
|
19591
|
+
"<%= config.bin %> signup status user@example.com",
|
|
19592
|
+
"<%= config.bin %> signup status --json"
|
|
19593
|
+
];
|
|
19594
|
+
static flags = {
|
|
19595
|
+
"api-base-url": Flags.string({
|
|
19596
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19597
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19598
|
+
hidden: true
|
|
19599
|
+
}),
|
|
19600
|
+
json: Flags.boolean({ description: "Print pending signup status as JSON" })
|
|
19601
|
+
};
|
|
19602
|
+
async run() {
|
|
19603
|
+
const { args, flags } = await this.parse(SignupStatusCommand);
|
|
19604
|
+
runSignupStatus({
|
|
19605
|
+
configDir: this.config.configDir,
|
|
19606
|
+
email: args.email,
|
|
19607
|
+
flags
|
|
19608
|
+
});
|
|
19609
|
+
}
|
|
19610
|
+
};
|
|
18774
19611
|
var SignupInteractiveCommand = class SignupInteractiveCommand extends Command {
|
|
18775
19612
|
static description = "Run the full signup flow in one interactive terminal session.";
|
|
18776
19613
|
static summary = "Run interactive account signup";
|
|
@@ -18825,7 +19662,7 @@ async function runLogoutWithCredentialLock(params) {
|
|
|
18825
19662
|
let authenticated;
|
|
18826
19663
|
try {
|
|
18827
19664
|
authenticated = await deps.createAuthenticatedCliApiClient({
|
|
18828
|
-
|
|
19665
|
+
apiBaseUrl: params.flags["api-base-url"],
|
|
18829
19666
|
configDir: params.configDir,
|
|
18830
19667
|
credentialsLockHeld: true
|
|
18831
19668
|
});
|
|
@@ -18889,9 +19726,9 @@ var LogoutCommand = class LogoutCommand extends Command {
|
|
|
18889
19726
|
static summary = "Log out and revoke the saved CLI OAuth grant";
|
|
18890
19727
|
static examples = ["<%= config.bin %> logout", "<%= config.bin %> logout --force"];
|
|
18891
19728
|
static flags = {
|
|
18892
|
-
"api-base-url
|
|
19729
|
+
"api-base-url": Flags.string({
|
|
18893
19730
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
18894
|
-
env: "
|
|
19731
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
18895
19732
|
hidden: true
|
|
18896
19733
|
}),
|
|
18897
19734
|
force: Flags.boolean({
|
|
@@ -19031,6 +19868,7 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
19031
19868
|
static examples = [
|
|
19032
19869
|
"<%= config.bin %> reply --id <inbound-email-id> --body 'Thanks, got it.'",
|
|
19033
19870
|
"<%= config.bin %> reply --id <inbound-email-id> --body-file ./reply.txt",
|
|
19871
|
+
"<%= config.bin %> reply --id <inbound-email-id> --body 'See attached.' --attachment ./report.pdf",
|
|
19034
19872
|
"<%= config.bin %> reply --id <inbound-email-id> --html '<p>Thanks, got it.</p>' --wait",
|
|
19035
19873
|
"<%= config.bin %> reply --id <inbound-email-id> --from 'Support <support@example.com>' --body 'Thanks!'"
|
|
19036
19874
|
];
|
|
@@ -19039,14 +19877,9 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
19039
19877
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
19040
19878
|
env: "PRIMITIVE_API_KEY"
|
|
19041
19879
|
}),
|
|
19042
|
-
"api-base-url
|
|
19043
|
-
description: "Override the
|
|
19044
|
-
env: "
|
|
19045
|
-
hidden: true
|
|
19046
|
-
}),
|
|
19047
|
-
"api-base-url-2": Flags.string({
|
|
19048
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
19049
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
19880
|
+
"api-base-url": Flags.string({
|
|
19881
|
+
description: "Override the API base URL. Internal testing only; not documented to customers.",
|
|
19882
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19050
19883
|
hidden: true
|
|
19051
19884
|
}),
|
|
19052
19885
|
id: Flags.string({
|
|
@@ -19054,12 +19887,17 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
19054
19887
|
required: true
|
|
19055
19888
|
}),
|
|
19056
19889
|
body: Flags.string({ description: "Plain-text reply body. Either --body or --html (or both) is required." }),
|
|
19057
|
-
"body-file": Flags.string({ description: "Read the plain-text reply body from a UTF-8 file. Mutually exclusive with --body and --body-stdin." }),
|
|
19890
|
+
"body-file": Flags.string({ description: "Read the plain-text reply body from a UTF-8 file; this does not attach the file. Use --attachment for attachments. Mutually exclusive with --body and --body-stdin." }),
|
|
19058
19891
|
"body-stdin": Flags.boolean({ description: "Read the plain-text reply body from stdin. Mutually exclusive with --body and --body-file. Stdin can only be consumed once." }),
|
|
19059
19892
|
html: Flags.string({ description: "HTML reply body. Either --body or --html (or both) is required." }),
|
|
19060
|
-
"html-file": Flags.string({ description: "Read the HTML reply body from a UTF-8 file. Mutually exclusive with --html and --html-stdin." }),
|
|
19893
|
+
"html-file": Flags.string({ description: "Read the HTML reply body from a UTF-8 file; this does not attach the file. Use --attachment for attachments. Mutually exclusive with --html and --html-stdin." }),
|
|
19061
19894
|
"html-stdin": Flags.boolean({ description: "Read the HTML reply body from stdin. Mutually exclusive with --html and --html-file. Stdin can only be consumed once." }),
|
|
19062
19895
|
from: Flags.string({ description: "Optional From header override. Defaults to the inbound recipient." }),
|
|
19896
|
+
attachment: Flags.string({
|
|
19897
|
+
char: "a",
|
|
19898
|
+
description: "Attach a file to the reply. Repeat --attachment to attach multiple files.",
|
|
19899
|
+
multiple: true
|
|
19900
|
+
}),
|
|
19063
19901
|
wait: Flags.boolean({ description: "Block until the receiving MTA returns an outcome. Without --wait, the call returns once Primitive has accepted the reply for delivery." }),
|
|
19064
19902
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
19065
19903
|
};
|
|
@@ -19077,15 +19915,16 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
19077
19915
|
await runWithTiming(flags.time, async () => {
|
|
19078
19916
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
19079
19917
|
apiKey: flags["api-key"],
|
|
19080
|
-
|
|
19081
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
19918
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19082
19919
|
configDir: this.config.configDir
|
|
19083
19920
|
});
|
|
19921
|
+
const attachments = readAttachmentFiles(flags.attachment);
|
|
19084
19922
|
const result = await replyToEmail({
|
|
19085
19923
|
body: {
|
|
19086
19924
|
...bodies.body !== void 0 ? { body_text: bodies.body } : {},
|
|
19087
19925
|
...bodies.html !== void 0 ? { body_html: bodies.html } : {},
|
|
19088
19926
|
...flags.from !== void 0 ? { from: flags.from } : {},
|
|
19927
|
+
...attachments !== void 0 ? { attachments } : {},
|
|
19089
19928
|
...flags.wait !== void 0 ? { wait: flags.wait } : {}
|
|
19090
19929
|
},
|
|
19091
19930
|
client: apiClient.client,
|
|
@@ -19113,38 +19952,133 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
19113
19952
|
}
|
|
19114
19953
|
};
|
|
19115
19954
|
//#endregion
|
|
19116
|
-
//#region src/oclif/
|
|
19117
|
-
|
|
19118
|
-
|
|
19119
|
-
|
|
19120
|
-
|
|
19121
|
-
|
|
19122
|
-
|
|
19123
|
-
|
|
19955
|
+
//#region src/oclif/commands/semantic-search.ts
|
|
19956
|
+
const DEFAULT_LIMIT = 10;
|
|
19957
|
+
const MAX_LIMIT = 100;
|
|
19958
|
+
const SCORE_WIDTH = 7;
|
|
19959
|
+
const SOURCE_WIDTH = 4;
|
|
19960
|
+
const SUBJECT_WIDTH = 40;
|
|
19961
|
+
const FROM_WIDTH = 26;
|
|
19962
|
+
const SNIPPET_WIDTH = 60;
|
|
19963
|
+
function truncate(value, width) {
|
|
19964
|
+
if (value.length <= width) return value.padEnd(width);
|
|
19965
|
+
return `${value.slice(0, width - 3)}...`;
|
|
19124
19966
|
}
|
|
19125
|
-
function
|
|
19126
|
-
return
|
|
19127
|
-
const code = character.charCodeAt(0);
|
|
19128
|
-
return code <= 31 || code >= 127 && code <= 159;
|
|
19129
|
-
});
|
|
19967
|
+
function sourceLabel(t) {
|
|
19968
|
+
return t === "inbound_email" ? "in" : "out";
|
|
19130
19969
|
}
|
|
19131
|
-
function
|
|
19132
|
-
|
|
19133
|
-
if (hasControlCharacter(filename)) throw new Errors.CLIError(`Attachment filename ${filename} contains control characters.`, { exit: 1 });
|
|
19970
|
+
function formatRow(r) {
|
|
19971
|
+
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)}`;
|
|
19134
19972
|
}
|
|
19135
|
-
function
|
|
19136
|
-
|
|
19137
|
-
return paths.map((path) => {
|
|
19138
|
-
const filename = basename(path);
|
|
19139
|
-
validateAttachmentFilename(path, filename);
|
|
19140
|
-
const bytes = readAttachmentBytes(path, readFile);
|
|
19141
|
-
if (bytes.length === 0) throw new Errors.CLIError(`Attachment file ${path} is empty. Attachments must contain at least one byte.`, { exit: 1 });
|
|
19142
|
-
return {
|
|
19143
|
-
content_base64: bytes.toString("base64"),
|
|
19144
|
-
filename
|
|
19145
|
-
};
|
|
19146
|
-
});
|
|
19973
|
+
function formatHeader() {
|
|
19974
|
+
return `${"SCORE".padStart(SCORE_WIDTH)} ${"SRC".padEnd(SOURCE_WIDTH)} ${"SUBJECT".padEnd(SUBJECT_WIDTH)} ${"FROM".padEnd(FROM_WIDTH)} EXCERPT`;
|
|
19147
19975
|
}
|
|
19976
|
+
var SemanticSearchCommand = class SemanticSearchCommand extends Command {
|
|
19977
|
+
static description = `Search received and sent mail by meaning or keywords.
|
|
19978
|
+
|
|
19979
|
+
Returns ranked rows. Each row carries a relevance score, the fields it
|
|
19980
|
+
matched, and a match-centered excerpt. Defaults to hybrid mode (blends
|
|
19981
|
+
semantic and keyword signals); use \`--mode keyword\` for plain
|
|
19982
|
+
full-text matching and \`--mode semantic\` for embedding-only.
|
|
19983
|
+
|
|
19984
|
+
Requires the Pro plan with the semantic_search_enabled entitlement.`;
|
|
19985
|
+
static summary = "Semantic / hybrid / keyword search across received and sent mail";
|
|
19986
|
+
static examples = [
|
|
19987
|
+
"<%= config.bin %> semantic-search \"invoice from acme\"",
|
|
19988
|
+
"<%= config.bin %> semantic-search \"shipping update\" --mode keyword",
|
|
19989
|
+
"<%= config.bin %> semantic-search \"kickoff\" --corpus inbound --limit 25",
|
|
19990
|
+
"<%= config.bin %> semantic-search renewal --json | jq '.data[].id'"
|
|
19991
|
+
];
|
|
19992
|
+
static args = { query: Args.string({
|
|
19993
|
+
description: "The search query.",
|
|
19994
|
+
required: true
|
|
19995
|
+
}) };
|
|
19996
|
+
static flags = {
|
|
19997
|
+
"api-key": Flags.string({
|
|
19998
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
19999
|
+
env: "PRIMITIVE_API_KEY"
|
|
20000
|
+
}),
|
|
20001
|
+
"api-base-url": Flags.string({
|
|
20002
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
20003
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
20004
|
+
hidden: true
|
|
20005
|
+
}),
|
|
20006
|
+
mode: Flags.string({
|
|
20007
|
+
description: "Ranking strategy.",
|
|
20008
|
+
options: [
|
|
20009
|
+
"hybrid",
|
|
20010
|
+
"semantic",
|
|
20011
|
+
"keyword"
|
|
20012
|
+
],
|
|
20013
|
+
default: "hybrid"
|
|
20014
|
+
}),
|
|
20015
|
+
corpus: Flags.string({
|
|
20016
|
+
description: "Restrict to inbound or outbound. Pass twice to include both (the default).",
|
|
20017
|
+
options: ["inbound", "outbound"],
|
|
20018
|
+
multiple: true
|
|
20019
|
+
}),
|
|
20020
|
+
"date-from": Flags.string({ description: "Only include mail at or after this ISO-8601 timestamp." }),
|
|
20021
|
+
"date-to": Flags.string({ description: "Only include mail at or before this ISO-8601 timestamp." }),
|
|
20022
|
+
limit: Flags.integer({
|
|
20023
|
+
description: `Maximum results to return (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}).`,
|
|
20024
|
+
default: DEFAULT_LIMIT,
|
|
20025
|
+
min: 1,
|
|
20026
|
+
max: MAX_LIMIT
|
|
20027
|
+
}),
|
|
20028
|
+
cursor: Flags.string({ description: "Opaque pagination cursor from a prior response's meta.cursor." }),
|
|
20029
|
+
json: Flags.boolean({ description: "Print the raw response envelope as JSON on STDOUT instead of the text table." }),
|
|
20030
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
20031
|
+
};
|
|
20032
|
+
async run() {
|
|
20033
|
+
const { args, flags } = await this.parse(SemanticSearchCommand);
|
|
20034
|
+
await runWithTiming(flags.time, async () => {
|
|
20035
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
20036
|
+
apiKey: flags["api-key"],
|
|
20037
|
+
apiBaseUrl: flags["api-base-url"],
|
|
20038
|
+
configDir: this.config.configDir
|
|
20039
|
+
});
|
|
20040
|
+
const result = await semanticSearch({
|
|
20041
|
+
client: apiClient.client,
|
|
20042
|
+
body: {
|
|
20043
|
+
query: args.query,
|
|
20044
|
+
mode: flags.mode,
|
|
20045
|
+
...flags.corpus ? { corpus: flags.corpus } : {},
|
|
20046
|
+
...flags["date-from"] ? { date_from: flags["date-from"] } : {},
|
|
20047
|
+
...flags["date-to"] ? { date_to: flags["date-to"] } : {},
|
|
20048
|
+
limit: flags.limit,
|
|
20049
|
+
...flags.cursor ? { cursor: flags.cursor } : {}
|
|
20050
|
+
},
|
|
20051
|
+
responseStyle: "fields"
|
|
20052
|
+
});
|
|
20053
|
+
if (result.error) {
|
|
20054
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
20055
|
+
writeErrorWithHints(errorPayload);
|
|
20056
|
+
surfaceUnauthorizedHint({
|
|
20057
|
+
auth,
|
|
20058
|
+
baseUrlOverridden,
|
|
20059
|
+
configDir: this.config.configDir,
|
|
20060
|
+
payload: errorPayload
|
|
20061
|
+
});
|
|
20062
|
+
process.exitCode = 1;
|
|
20063
|
+
return;
|
|
20064
|
+
}
|
|
20065
|
+
const envelope = result.data;
|
|
20066
|
+
if (flags.json) {
|
|
20067
|
+
this.log(JSON.stringify(envelope ?? null, null, 2));
|
|
20068
|
+
return;
|
|
20069
|
+
}
|
|
20070
|
+
const rows = envelope?.data ?? [];
|
|
20071
|
+
if (rows.length === 0) {
|
|
20072
|
+
process.stderr.write("No matching mail.\n");
|
|
20073
|
+
return;
|
|
20074
|
+
}
|
|
20075
|
+
process.stderr.write(`${formatHeader()}\n`);
|
|
20076
|
+
for (const row of rows) this.log(formatRow(row));
|
|
20077
|
+
const nextCursor = envelope?.meta?.cursor ?? null;
|
|
20078
|
+
if (nextCursor) process.stderr.write(`\nNext page: pass --cursor ${nextCursor}\n`);
|
|
20079
|
+
});
|
|
20080
|
+
}
|
|
20081
|
+
};
|
|
19148
20082
|
//#endregion
|
|
19149
20083
|
//#region src/oclif/commands/send.ts
|
|
19150
20084
|
var SendCommand = class SendCommand extends Command {
|
|
@@ -19171,14 +20105,9 @@ var SendCommand = class SendCommand extends Command {
|
|
|
19171
20105
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
19172
20106
|
env: "PRIMITIVE_API_KEY"
|
|
19173
20107
|
}),
|
|
19174
|
-
"api-base-url
|
|
19175
|
-
description: "Override the
|
|
19176
|
-
env: "
|
|
19177
|
-
hidden: true
|
|
19178
|
-
}),
|
|
19179
|
-
"api-base-url-2": Flags.string({
|
|
19180
|
-
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
19181
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
20108
|
+
"api-base-url": Flags.string({
|
|
20109
|
+
description: "Override the API base URL. Internal testing only; not documented to customers.",
|
|
20110
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19182
20111
|
hidden: true
|
|
19183
20112
|
}),
|
|
19184
20113
|
to: Flags.string({
|
|
@@ -19217,8 +20146,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
19217
20146
|
await runWithTiming(flags.time, async () => {
|
|
19218
20147
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
19219
20148
|
apiKey: flags["api-key"],
|
|
19220
|
-
|
|
19221
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
20149
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19222
20150
|
configDir: this.config.configDir
|
|
19223
20151
|
});
|
|
19224
20152
|
const authFailureContext = {
|
|
@@ -19240,7 +20168,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
19240
20168
|
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
19241
20169
|
...flags["wait-timeout-ms"] !== void 0 ? { wait_timeout_ms: flags["wait-timeout-ms"] } : {}
|
|
19242
20170
|
},
|
|
19243
|
-
client: apiClient.
|
|
20171
|
+
client: apiClient.client,
|
|
19244
20172
|
responseStyle: "fields"
|
|
19245
20173
|
});
|
|
19246
20174
|
if (result.error) {
|
|
@@ -19311,9 +20239,9 @@ function acquireCredentialsLock(configDir) {
|
|
|
19311
20239
|
function commonOtpStartFlags() {
|
|
19312
20240
|
return {
|
|
19313
20241
|
"accept-terms": Flags.boolean({ description: "Confirm acceptance of Primitive's Terms of Service and Privacy Policy" }),
|
|
19314
|
-
"api-base-url
|
|
20242
|
+
"api-base-url": Flags.string({
|
|
19315
20243
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19316
|
-
env: "
|
|
20244
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19317
20245
|
hidden: true
|
|
19318
20246
|
}),
|
|
19319
20247
|
"device-name": Flags.string({ description: "Device name used for the created CLI OAuth session" }),
|
|
@@ -19492,9 +20420,9 @@ var SigninOtpConfirmCommand = class extends Command {
|
|
|
19492
20420
|
static summary = "Confirm OTP sign-in";
|
|
19493
20421
|
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"];
|
|
19494
20422
|
static flags = {
|
|
19495
|
-
"api-base-url
|
|
20423
|
+
"api-base-url": Flags.string({
|
|
19496
20424
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19497
|
-
env: "
|
|
20425
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19498
20426
|
hidden: true
|
|
19499
20427
|
}),
|
|
19500
20428
|
force: Flags.boolean({
|
|
@@ -19571,9 +20499,9 @@ var SigninOtpResendCommand = class extends Command {
|
|
|
19571
20499
|
static description = "Resend the verification code for a pending OTP sign-in.";
|
|
19572
20500
|
static summary = "Resend OTP sign-in code";
|
|
19573
20501
|
static examples = ["<%= config.bin %> signin otp resend user@example.com"];
|
|
19574
|
-
static flags = { "api-base-url
|
|
20502
|
+
static flags = { "api-base-url": Flags.string({
|
|
19575
20503
|
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
19576
|
-
env: "
|
|
20504
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19577
20505
|
hidden: true
|
|
19578
20506
|
}) };
|
|
19579
20507
|
async run() {
|
|
@@ -19659,14 +20587,9 @@ var WhoamiCommand = class WhoamiCommand extends Command {
|
|
|
19659
20587
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
19660
20588
|
env: "PRIMITIVE_API_KEY"
|
|
19661
20589
|
}),
|
|
19662
|
-
"api-base-url
|
|
19663
|
-
description:
|
|
19664
|
-
env: "
|
|
19665
|
-
hidden: true
|
|
19666
|
-
}),
|
|
19667
|
-
"api-base-url-2": Flags.string({
|
|
19668
|
-
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
19669
|
-
env: "PRIMITIVE_API_BASE_URL_2",
|
|
20590
|
+
"api-base-url": Flags.string({
|
|
20591
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
20592
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
19670
20593
|
hidden: true
|
|
19671
20594
|
}),
|
|
19672
20595
|
json: Flags.boolean({ description: "Print the full account JSON response. Default output hides setup and billing internals." }),
|
|
@@ -19677,8 +20600,7 @@ var WhoamiCommand = class WhoamiCommand extends Command {
|
|
|
19677
20600
|
await runWithTiming(flags.time, async () => {
|
|
19678
20601
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
19679
20602
|
apiKey: flags["api-key"],
|
|
19680
|
-
|
|
19681
|
-
apiBaseUrl2: flags["api-base-url-2"],
|
|
20603
|
+
apiBaseUrl: flags["api-base-url"],
|
|
19682
20604
|
configDir: this.config.configDir
|
|
19683
20605
|
});
|
|
19684
20606
|
const result = await getAccount({
|
|
@@ -20002,14 +20924,17 @@ const COMMANDS = {
|
|
|
20002
20924
|
"signup:confirm": SignupConfirmCommand,
|
|
20003
20925
|
"signup:interactive": SignupInteractiveCommand,
|
|
20004
20926
|
"signup:resend": SignupResendCommand,
|
|
20927
|
+
"signup:status": SignupStatusCommand,
|
|
20005
20928
|
logout: LogoutCommand,
|
|
20006
20929
|
whoami: WhoamiCommand,
|
|
20007
20930
|
doctor: DoctorCommand,
|
|
20008
20931
|
"emails:latest": EmailsLatestCommand,
|
|
20009
20932
|
"emails:watch": EmailsWatchCommand,
|
|
20010
20933
|
"emails:wait": EmailsWaitCommand,
|
|
20934
|
+
"semantic-search": SemanticSearchCommand,
|
|
20011
20935
|
"domains:zone-file": DomainsZoneFileCommand,
|
|
20012
20936
|
"domains:download-domain-zone-file": DomainsZoneFileCommand,
|
|
20937
|
+
"inbox:setup": InboxSetupCommand,
|
|
20013
20938
|
"inbox:status": InboxStatusCommand,
|
|
20014
20939
|
"inbox:get-inbox-status": InboxStatusCommand,
|
|
20015
20940
|
"functions:init": FunctionsInitCommand,
|