@primitivedotdev/cli 0.31.3 → 0.31.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/oclif/index.js +1507 -94
- package/man/primitive.1 +94 -0
- package/package.json +10 -3
package/dist/oclif/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Args, Command, Errors, Flags } from "@oclif/core";
|
|
2
2
|
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { hostname } from "node:os";
|
|
7
7
|
import process$1 from "node:process";
|
|
@@ -645,11 +645,13 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
|
|
|
645
645
|
deleteFunctionSecret: () => deleteFunctionSecret,
|
|
646
646
|
discardEmailContent: () => discardEmailContent,
|
|
647
647
|
downloadAttachments: () => downloadAttachments,
|
|
648
|
+
downloadDomainZoneFile: () => downloadDomainZoneFile,
|
|
648
649
|
downloadRawEmail: () => downloadRawEmail,
|
|
649
650
|
getAccount: () => getAccount,
|
|
650
651
|
getEmail: () => getEmail,
|
|
651
652
|
getFunction: () => getFunction,
|
|
652
653
|
getFunctionTestRunTrace: () => getFunctionTestRunTrace,
|
|
654
|
+
getInboxStatus: () => getInboxStatus,
|
|
653
655
|
getSendPermissions: () => getSendPermissions,
|
|
654
656
|
getSentEmail: () => getSentEmail,
|
|
655
657
|
getStorageStats: () => getStorageStats,
|
|
@@ -941,9 +943,11 @@ const listDomains = (options) => (options?.client ?? client).get({
|
|
|
941
943
|
/**
|
|
942
944
|
* Claim a new domain
|
|
943
945
|
*
|
|
944
|
-
* Creates an unverified domain claim
|
|
945
|
-
*
|
|
946
|
-
* calling the verify endpoint.
|
|
946
|
+
* Creates an unverified domain claim and returns the exact
|
|
947
|
+
* DNS records to publish in `dns_records`. Publish those
|
|
948
|
+
* records before calling the verify endpoint. To give users
|
|
949
|
+
* an importable DNS file, call `downloadDomainZoneFile` or run
|
|
950
|
+
* `primitive domains zone-file --id <domain-id>`.
|
|
947
951
|
*
|
|
948
952
|
*/
|
|
949
953
|
const addDomain = (options) => (options.client ?? client).post({
|
|
@@ -993,9 +997,15 @@ const updateDomain = (options) => (options.client ?? client).patch({
|
|
|
993
997
|
/**
|
|
994
998
|
* Verify domain ownership
|
|
995
999
|
*
|
|
996
|
-
* Checks DNS records
|
|
1000
|
+
* Checks DNS records required for inbound routing, ownership,
|
|
1001
|
+
* and outbound authentication: MX, ownership TXT, SPF, DKIM,
|
|
1002
|
+
* DMARC, and TLS-RPT.
|
|
997
1003
|
* On success, the domain is promoted from unverified to verified.
|
|
998
|
-
* On failure, returns which checks passed and which failed
|
|
1004
|
+
* On failure, returns which checks passed and which failed,
|
|
1005
|
+
* plus the exact DNS records still expected. To give users
|
|
1006
|
+
* an importable DNS file for missing records, call
|
|
1007
|
+
* `downloadDomainZoneFile` or run
|
|
1008
|
+
* `primitive domains zone-file --id <domain-id>`.
|
|
999
1009
|
*
|
|
1000
1010
|
*/
|
|
1001
1011
|
const verifyDomain = (options) => (options.client ?? client).post({
|
|
@@ -1007,6 +1017,45 @@ const verifyDomain = (options) => (options.client ?? client).post({
|
|
|
1007
1017
|
...options
|
|
1008
1018
|
});
|
|
1009
1019
|
/**
|
|
1020
|
+
* Download domain DNS zone file
|
|
1021
|
+
*
|
|
1022
|
+
* Downloads a BIND-format DNS zone file containing the DNS records
|
|
1023
|
+
* required for a domain claim. Agents should offer this after
|
|
1024
|
+
* `addDomain` when users want to import DNS records instead of
|
|
1025
|
+
* copying each record manually.
|
|
1026
|
+
*
|
|
1027
|
+
*/
|
|
1028
|
+
const downloadDomainZoneFile = (options) => (options.client ?? client).get({
|
|
1029
|
+
security: [{
|
|
1030
|
+
scheme: "bearer",
|
|
1031
|
+
type: "http"
|
|
1032
|
+
}],
|
|
1033
|
+
url: "/domains/{id}/zone-file",
|
|
1034
|
+
...options
|
|
1035
|
+
});
|
|
1036
|
+
/**
|
|
1037
|
+
* Get inbound inbox readiness
|
|
1038
|
+
*
|
|
1039
|
+
* Returns one consolidated view of inbound domain readiness,
|
|
1040
|
+
* webhook/function processing routes, deployed Functions, and
|
|
1041
|
+
* recent inbound email activity.
|
|
1042
|
+
*
|
|
1043
|
+
* Agents should call this before guiding a user through inbound
|
|
1044
|
+
* setup. It answers the practical questions "can I receive mail",
|
|
1045
|
+
* "will anything process that mail", and "what should I do next"
|
|
1046
|
+
* without forcing clients to stitch together domains, endpoints,
|
|
1047
|
+
* functions, and emails manually.
|
|
1048
|
+
*
|
|
1049
|
+
*/
|
|
1050
|
+
const getInboxStatus = (options) => (options?.client ?? client).get({
|
|
1051
|
+
security: [{
|
|
1052
|
+
scheme: "bearer",
|
|
1053
|
+
type: "http"
|
|
1054
|
+
}],
|
|
1055
|
+
url: "/inbox/status",
|
|
1056
|
+
...options
|
|
1057
|
+
});
|
|
1058
|
+
/**
|
|
1010
1059
|
* List inbound emails
|
|
1011
1060
|
*
|
|
1012
1061
|
* Returns a paginated list of INBOUND emails received at your
|
|
@@ -1868,6 +1917,10 @@ const openapiDocument = {
|
|
|
1868
1917
|
"name": "Domains",
|
|
1869
1918
|
"description": "Claim, verify, and manage email domains"
|
|
1870
1919
|
},
|
|
1920
|
+
{
|
|
1921
|
+
"name": "Inbox",
|
|
1922
|
+
"description": "Check inbound email setup and processing readiness"
|
|
1923
|
+
},
|
|
1871
1924
|
{
|
|
1872
1925
|
"name": "Emails",
|
|
1873
1926
|
"description": "List, inspect, and manage received emails"
|
|
@@ -2333,7 +2386,7 @@ const openapiDocument = {
|
|
|
2333
2386
|
"post": {
|
|
2334
2387
|
"operationId": "addDomain",
|
|
2335
2388
|
"summary": "Claim a new domain",
|
|
2336
|
-
"description": "Creates an unverified domain claim
|
|
2389
|
+
"description": "Creates an unverified domain claim and returns the exact\nDNS records to publish in `dns_records`. Publish those\nrecords before calling the verify endpoint. To give users\nan importable DNS file, call `downloadDomainZoneFile` or run\n`primitive domains zone-file --id <domain-id>`.\n",
|
|
2337
2390
|
"tags": ["Domains"],
|
|
2338
2391
|
"requestBody": {
|
|
2339
2392
|
"required": true,
|
|
@@ -2426,7 +2479,7 @@ const openapiDocument = {
|
|
|
2426
2479
|
"post": {
|
|
2427
2480
|
"operationId": "verifyDomain",
|
|
2428
2481
|
"summary": "Verify domain ownership",
|
|
2429
|
-
"description": "Checks DNS records
|
|
2482
|
+
"description": "Checks DNS records required for inbound routing, ownership,\nand outbound authentication: MX, ownership TXT, SPF, DKIM,\nDMARC, and TLS-RPT.\nOn success, the domain is promoted from unverified to verified.\nOn failure, returns which checks passed and which failed,\nplus the exact DNS records still expected. To give users\nan importable DNS file for missing records, call\n`downloadDomainZoneFile` or run\n`primitive domains zone-file --id <domain-id>`.\n",
|
|
2430
2483
|
"tags": ["Domains"],
|
|
2431
2484
|
"responses": {
|
|
2432
2485
|
"200": {
|
|
@@ -2442,6 +2495,55 @@ const openapiDocument = {
|
|
|
2442
2495
|
}
|
|
2443
2496
|
}
|
|
2444
2497
|
},
|
|
2498
|
+
"/domains/{id}/zone-file": {
|
|
2499
|
+
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
2500
|
+
"get": {
|
|
2501
|
+
"operationId": "downloadDomainZoneFile",
|
|
2502
|
+
"summary": "Download domain DNS zone file",
|
|
2503
|
+
"description": "Downloads a BIND-format DNS zone file containing the DNS records\nrequired for a domain claim. Agents should offer this after\n`addDomain` when users want to import DNS records instead of\ncopying each record manually.\n",
|
|
2504
|
+
"tags": ["Domains"],
|
|
2505
|
+
"parameters": [{
|
|
2506
|
+
"name": "outbound_only",
|
|
2507
|
+
"in": "query",
|
|
2508
|
+
"schema": { "type": "boolean" },
|
|
2509
|
+
"description": "When true, include only outbound DNS records. Verified domains\ndefault to outbound-only; pending claims default to all required\nrecords.\n"
|
|
2510
|
+
}],
|
|
2511
|
+
"responses": {
|
|
2512
|
+
"200": {
|
|
2513
|
+
"description": "BIND-format zone file",
|
|
2514
|
+
"content": { "text/plain": { "schema": {
|
|
2515
|
+
"type": "string",
|
|
2516
|
+
"format": "binary"
|
|
2517
|
+
} } },
|
|
2518
|
+
"headers": { "Content-Disposition": { "schema": {
|
|
2519
|
+
"type": "string",
|
|
2520
|
+
"example": "attachment; filename=\"example.com.zone\""
|
|
2521
|
+
} } }
|
|
2522
|
+
},
|
|
2523
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
2524
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
2525
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
2526
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
},
|
|
2530
|
+
"/inbox/status": { "get": {
|
|
2531
|
+
"operationId": "getInboxStatus",
|
|
2532
|
+
"summary": "Get inbound inbox readiness",
|
|
2533
|
+
"description": "Returns one consolidated view of inbound domain readiness,\nwebhook/function processing routes, deployed Functions, and\nrecent inbound email activity.\n\nAgents should call this before guiding a user through inbound\nsetup. It answers the practical questions \"can I receive mail\",\n\"will anything process that mail\", and \"what should I do next\"\nwithout forcing clients to stitch together domains, endpoints,\nfunctions, and emails manually.\n",
|
|
2534
|
+
"tags": ["Inbox"],
|
|
2535
|
+
"responses": {
|
|
2536
|
+
"200": {
|
|
2537
|
+
"description": "Consolidated inbox readiness status",
|
|
2538
|
+
"content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
|
|
2539
|
+
"type": "object",
|
|
2540
|
+
"properties": { "data": { "$ref": "#/components/schemas/InboxStatus" } }
|
|
2541
|
+
}] } } }
|
|
2542
|
+
},
|
|
2543
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
2544
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
2545
|
+
}
|
|
2546
|
+
} },
|
|
2445
2547
|
"/emails": { "get": {
|
|
2446
2548
|
"operationId": "listEmails",
|
|
2447
2549
|
"summary": "List inbound emails",
|
|
@@ -4730,8 +4832,183 @@ const openapiDocument = {
|
|
|
4730
4832
|
} },
|
|
4731
4833
|
"required": ["secret"]
|
|
4732
4834
|
},
|
|
4835
|
+
"InboxStatus": {
|
|
4836
|
+
"type": "object",
|
|
4837
|
+
"additionalProperties": false,
|
|
4838
|
+
"properties": {
|
|
4839
|
+
"ready": {
|
|
4840
|
+
"type": "boolean",
|
|
4841
|
+
"description": "True when at least one active inbound domain has an enabled processing route."
|
|
4842
|
+
},
|
|
4843
|
+
"receiving_ready": {
|
|
4844
|
+
"type": "boolean",
|
|
4845
|
+
"description": "True when at least one active verified or managed domain can receive mail."
|
|
4846
|
+
},
|
|
4847
|
+
"processing_ready": {
|
|
4848
|
+
"type": "boolean",
|
|
4849
|
+
"description": "True when at least one receiving-ready domain has an enabled webhook or function route."
|
|
4850
|
+
},
|
|
4851
|
+
"summary": {
|
|
4852
|
+
"type": "string",
|
|
4853
|
+
"description": "Short human-readable status summary."
|
|
4854
|
+
},
|
|
4855
|
+
"next_actions": {
|
|
4856
|
+
"type": "array",
|
|
4857
|
+
"items": { "$ref": "#/components/schemas/InboxStatusNextAction" }
|
|
4858
|
+
},
|
|
4859
|
+
"domains": {
|
|
4860
|
+
"type": "array",
|
|
4861
|
+
"items": { "$ref": "#/components/schemas/InboxStatusDomain" }
|
|
4862
|
+
},
|
|
4863
|
+
"endpoints": { "$ref": "#/components/schemas/InboxStatusEndpointSummary" },
|
|
4864
|
+
"functions": { "$ref": "#/components/schemas/InboxStatusFunctionSummary" },
|
|
4865
|
+
"recent_emails": { "$ref": "#/components/schemas/InboxStatusRecentEmailSummary" }
|
|
4866
|
+
},
|
|
4867
|
+
"required": [
|
|
4868
|
+
"ready",
|
|
4869
|
+
"receiving_ready",
|
|
4870
|
+
"processing_ready",
|
|
4871
|
+
"summary",
|
|
4872
|
+
"next_actions",
|
|
4873
|
+
"domains",
|
|
4874
|
+
"endpoints",
|
|
4875
|
+
"functions",
|
|
4876
|
+
"recent_emails"
|
|
4877
|
+
]
|
|
4878
|
+
},
|
|
4879
|
+
"InboxStatusNextAction": {
|
|
4880
|
+
"type": "object",
|
|
4881
|
+
"additionalProperties": false,
|
|
4882
|
+
"properties": {
|
|
4883
|
+
"kind": {
|
|
4884
|
+
"type": "string",
|
|
4885
|
+
"enum": [
|
|
4886
|
+
"add_domain",
|
|
4887
|
+
"verify_domain",
|
|
4888
|
+
"configure_processing",
|
|
4889
|
+
"send_test_email",
|
|
4890
|
+
"fix_failed_functions"
|
|
4891
|
+
]
|
|
4892
|
+
},
|
|
4893
|
+
"message": {
|
|
4894
|
+
"type": "string",
|
|
4895
|
+
"description": "Human-readable next step."
|
|
4896
|
+
},
|
|
4897
|
+
"command": {
|
|
4898
|
+
"type": "string",
|
|
4899
|
+
"description": "Suggested Primitive CLI command when there is an obvious next step."
|
|
4900
|
+
}
|
|
4901
|
+
},
|
|
4902
|
+
"required": ["kind", "message"]
|
|
4903
|
+
},
|
|
4904
|
+
"InboxStatusDomain": {
|
|
4905
|
+
"type": "object",
|
|
4906
|
+
"additionalProperties": false,
|
|
4907
|
+
"properties": {
|
|
4908
|
+
"id": { "type": "string" },
|
|
4909
|
+
"domain": { "type": "string" },
|
|
4910
|
+
"verified": { "type": "boolean" },
|
|
4911
|
+
"active": { "type": "boolean" },
|
|
4912
|
+
"managed": { "type": "boolean" },
|
|
4913
|
+
"receiving_ready": { "type": "boolean" },
|
|
4914
|
+
"processing_ready": { "type": "boolean" },
|
|
4915
|
+
"processing_route_count": { "type": "integer" },
|
|
4916
|
+
"endpoint_count": { "type": "integer" },
|
|
4917
|
+
"enabled_endpoint_count": { "type": "integer" },
|
|
4918
|
+
"function_endpoint_count": { "type": "integer" },
|
|
4919
|
+
"email_count": {
|
|
4920
|
+
"type": "integer",
|
|
4921
|
+
"description": "Number of inbound emails received for this domain in the last 30 days."
|
|
4922
|
+
},
|
|
4923
|
+
"latest_email_received_at": {
|
|
4924
|
+
"type": ["string", "null"],
|
|
4925
|
+
"format": "date-time",
|
|
4926
|
+
"description": "Most recent inbound email received for this domain in the last 30 days."
|
|
4927
|
+
},
|
|
4928
|
+
"status": {
|
|
4929
|
+
"type": "string",
|
|
4930
|
+
"enum": [
|
|
4931
|
+
"ready",
|
|
4932
|
+
"stored_only",
|
|
4933
|
+
"pending_dns",
|
|
4934
|
+
"inactive"
|
|
4935
|
+
]
|
|
4936
|
+
}
|
|
4937
|
+
},
|
|
4938
|
+
"required": [
|
|
4939
|
+
"id",
|
|
4940
|
+
"domain",
|
|
4941
|
+
"verified",
|
|
4942
|
+
"active",
|
|
4943
|
+
"managed",
|
|
4944
|
+
"receiving_ready",
|
|
4945
|
+
"processing_ready",
|
|
4946
|
+
"processing_route_count",
|
|
4947
|
+
"endpoint_count",
|
|
4948
|
+
"enabled_endpoint_count",
|
|
4949
|
+
"function_endpoint_count",
|
|
4950
|
+
"email_count",
|
|
4951
|
+
"latest_email_received_at",
|
|
4952
|
+
"status"
|
|
4953
|
+
]
|
|
4954
|
+
},
|
|
4955
|
+
"InboxStatusEndpointSummary": {
|
|
4956
|
+
"type": "object",
|
|
4957
|
+
"additionalProperties": false,
|
|
4958
|
+
"properties": {
|
|
4959
|
+
"total": { "type": "integer" },
|
|
4960
|
+
"enabled": { "type": "integer" },
|
|
4961
|
+
"disabled": { "type": "integer" },
|
|
4962
|
+
"fallback_enabled": { "type": "integer" },
|
|
4963
|
+
"domain_scoped_enabled": { "type": "integer" },
|
|
4964
|
+
"http_enabled": { "type": "integer" },
|
|
4965
|
+
"function_enabled": { "type": "integer" }
|
|
4966
|
+
},
|
|
4967
|
+
"required": [
|
|
4968
|
+
"total",
|
|
4969
|
+
"enabled",
|
|
4970
|
+
"disabled",
|
|
4971
|
+
"fallback_enabled",
|
|
4972
|
+
"domain_scoped_enabled",
|
|
4973
|
+
"http_enabled",
|
|
4974
|
+
"function_enabled"
|
|
4975
|
+
]
|
|
4976
|
+
},
|
|
4977
|
+
"InboxStatusFunctionSummary": {
|
|
4978
|
+
"type": "object",
|
|
4979
|
+
"additionalProperties": false,
|
|
4980
|
+
"properties": {
|
|
4981
|
+
"total": { "type": "integer" },
|
|
4982
|
+
"deployed": { "type": "integer" },
|
|
4983
|
+
"pending": { "type": "integer" },
|
|
4984
|
+
"failed": { "type": "integer" }
|
|
4985
|
+
},
|
|
4986
|
+
"required": [
|
|
4987
|
+
"total",
|
|
4988
|
+
"deployed",
|
|
4989
|
+
"pending",
|
|
4990
|
+
"failed"
|
|
4991
|
+
]
|
|
4992
|
+
},
|
|
4993
|
+
"InboxStatusRecentEmailSummary": {
|
|
4994
|
+
"type": "object",
|
|
4995
|
+
"description": "Inbound email activity from the last 30 days.",
|
|
4996
|
+
"additionalProperties": false,
|
|
4997
|
+
"properties": {
|
|
4998
|
+
"total": {
|
|
4999
|
+
"type": "integer",
|
|
5000
|
+
"description": "Number of inbound emails received in the last 30 days."
|
|
5001
|
+
},
|
|
5002
|
+
"latest_received_at": {
|
|
5003
|
+
"type": ["string", "null"],
|
|
5004
|
+
"format": "date-time",
|
|
5005
|
+
"description": "Most recent inbound email received in the last 30 days."
|
|
5006
|
+
}
|
|
5007
|
+
},
|
|
5008
|
+
"required": ["total", "latest_received_at"]
|
|
5009
|
+
},
|
|
4733
5010
|
"Domain": {
|
|
4734
|
-
"description": "A domain can be either verified or unverified. Verified domains have\n`is_active` and `spam_threshold` fields. Unverified domains have a\n`verification_token` for DNS
|
|
5011
|
+
"description": "A domain can be either verified or unverified. Verified domains have\n`is_active` and `spam_threshold` fields. Unverified domains have a\n`verification_token` and `dns_records` for DNS setup.\n",
|
|
4735
5012
|
"oneOf": [{ "$ref": "#/components/schemas/VerifiedDomain" }, { "$ref": "#/components/schemas/UnverifiedDomain" }]
|
|
4736
5013
|
},
|
|
4737
5014
|
"VerifiedDomain": {
|
|
@@ -4771,6 +5048,74 @@ const openapiDocument = {
|
|
|
4771
5048
|
"created_at"
|
|
4772
5049
|
]
|
|
4773
5050
|
},
|
|
5051
|
+
"DomainDnsRecord": {
|
|
5052
|
+
"type": "object",
|
|
5053
|
+
"additionalProperties": false,
|
|
5054
|
+
"properties": {
|
|
5055
|
+
"type": {
|
|
5056
|
+
"type": "string",
|
|
5057
|
+
"enum": ["MX", "TXT"],
|
|
5058
|
+
"description": "DNS record type."
|
|
5059
|
+
},
|
|
5060
|
+
"name": {
|
|
5061
|
+
"type": "string",
|
|
5062
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
5063
|
+
},
|
|
5064
|
+
"fqdn": {
|
|
5065
|
+
"type": "string",
|
|
5066
|
+
"description": "Fully-qualified DNS record name."
|
|
5067
|
+
},
|
|
5068
|
+
"value": {
|
|
5069
|
+
"type": "string",
|
|
5070
|
+
"description": "Exact value to publish."
|
|
5071
|
+
},
|
|
5072
|
+
"priority": {
|
|
5073
|
+
"type": "integer",
|
|
5074
|
+
"description": "MX priority. Present only for MX records."
|
|
5075
|
+
},
|
|
5076
|
+
"ttl": {
|
|
5077
|
+
"type": "integer",
|
|
5078
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
5079
|
+
},
|
|
5080
|
+
"required": {
|
|
5081
|
+
"type": "boolean",
|
|
5082
|
+
"const": true
|
|
5083
|
+
},
|
|
5084
|
+
"purpose": {
|
|
5085
|
+
"type": "string",
|
|
5086
|
+
"enum": [
|
|
5087
|
+
"inbound_mx",
|
|
5088
|
+
"ownership_verification",
|
|
5089
|
+
"spf",
|
|
5090
|
+
"dkim",
|
|
5091
|
+
"dmarc",
|
|
5092
|
+
"tls_reporting"
|
|
5093
|
+
]
|
|
5094
|
+
},
|
|
5095
|
+
"status": {
|
|
5096
|
+
"type": "string",
|
|
5097
|
+
"enum": [
|
|
5098
|
+
"pending",
|
|
5099
|
+
"found",
|
|
5100
|
+
"missing",
|
|
5101
|
+
"incorrect"
|
|
5102
|
+
]
|
|
5103
|
+
},
|
|
5104
|
+
"message": {
|
|
5105
|
+
"type": "string",
|
|
5106
|
+
"description": "Short explanation of why this record is needed."
|
|
5107
|
+
}
|
|
5108
|
+
},
|
|
5109
|
+
"required": [
|
|
5110
|
+
"type",
|
|
5111
|
+
"name",
|
|
5112
|
+
"fqdn",
|
|
5113
|
+
"value",
|
|
5114
|
+
"required",
|
|
5115
|
+
"purpose",
|
|
5116
|
+
"status"
|
|
5117
|
+
]
|
|
5118
|
+
},
|
|
4774
5119
|
"UnverifiedDomain": {
|
|
4775
5120
|
"type": "object",
|
|
4776
5121
|
"properties": {
|
|
@@ -4791,6 +5136,11 @@ const openapiDocument = {
|
|
|
4791
5136
|
"type": "string",
|
|
4792
5137
|
"description": "Add this value as a TXT record to verify ownership"
|
|
4793
5138
|
},
|
|
5139
|
+
"dns_records": {
|
|
5140
|
+
"type": "array",
|
|
5141
|
+
"description": "Exact DNS records to publish for this pending domain claim.",
|
|
5142
|
+
"items": { "$ref": "#/components/schemas/DomainDnsRecord" }
|
|
5143
|
+
},
|
|
4794
5144
|
"created_at": {
|
|
4795
5145
|
"type": "string",
|
|
4796
5146
|
"format": "date-time"
|
|
@@ -4808,12 +5158,23 @@ const openapiDocument = {
|
|
|
4808
5158
|
"AddDomainInput": {
|
|
4809
5159
|
"type": "object",
|
|
4810
5160
|
"additionalProperties": false,
|
|
4811
|
-
"properties": {
|
|
4812
|
-
"
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
5161
|
+
"properties": {
|
|
5162
|
+
"domain": {
|
|
5163
|
+
"type": "string",
|
|
5164
|
+
"minLength": 1,
|
|
5165
|
+
"maxLength": 253,
|
|
5166
|
+
"description": "The domain name to claim (e.g. \"example.com\")"
|
|
5167
|
+
},
|
|
5168
|
+
"confirmed": {
|
|
5169
|
+
"type": "boolean",
|
|
5170
|
+
"description": "Set to true to confirm replacing an existing mailbox provider after an mx_conflict response."
|
|
5171
|
+
},
|
|
5172
|
+
"outbound": {
|
|
5173
|
+
"type": "boolean",
|
|
5174
|
+
"deprecated": true,
|
|
5175
|
+
"description": "Deprecated and ignored. Outbound DNS is provisioned for every new domain claim."
|
|
5176
|
+
}
|
|
5177
|
+
},
|
|
4817
5178
|
"required": ["domain"]
|
|
4818
5179
|
},
|
|
4819
5180
|
"UpdateDomainInput": {
|
|
@@ -4835,10 +5196,17 @@ const openapiDocument = {
|
|
|
4835
5196
|
},
|
|
4836
5197
|
"DomainVerifyResult": { "oneOf": [{
|
|
4837
5198
|
"type": "object",
|
|
4838
|
-
"properties": {
|
|
4839
|
-
"
|
|
4840
|
-
|
|
4841
|
-
|
|
5199
|
+
"properties": {
|
|
5200
|
+
"verified": {
|
|
5201
|
+
"type": "boolean",
|
|
5202
|
+
"const": true
|
|
5203
|
+
},
|
|
5204
|
+
"dns_records": {
|
|
5205
|
+
"type": "array",
|
|
5206
|
+
"description": "Exact DNS records checked for this verification attempt.",
|
|
5207
|
+
"items": { "$ref": "#/components/schemas/DomainDnsRecord" }
|
|
5208
|
+
}
|
|
5209
|
+
},
|
|
4842
5210
|
"required": ["verified"]
|
|
4843
5211
|
}, {
|
|
4844
5212
|
"type": "object",
|
|
@@ -4855,6 +5223,27 @@ const openapiDocument = {
|
|
|
4855
5223
|
"type": "boolean",
|
|
4856
5224
|
"description": "Whether the TXT verification record was found"
|
|
4857
5225
|
},
|
|
5226
|
+
"spfFound": {
|
|
5227
|
+
"type": "boolean",
|
|
5228
|
+
"description": "Whether the SPF record includes Primitive."
|
|
5229
|
+
},
|
|
5230
|
+
"dkimFound": {
|
|
5231
|
+
"type": "boolean",
|
|
5232
|
+
"description": "Whether the DKIM public key record was found."
|
|
5233
|
+
},
|
|
5234
|
+
"dmarcFound": {
|
|
5235
|
+
"type": "boolean",
|
|
5236
|
+
"description": "Whether the DMARC record was found."
|
|
5237
|
+
},
|
|
5238
|
+
"tlsRptFound": {
|
|
5239
|
+
"type": "boolean",
|
|
5240
|
+
"description": "Whether the TLS-RPT record was found."
|
|
5241
|
+
},
|
|
5242
|
+
"dns_records": {
|
|
5243
|
+
"type": "array",
|
|
5244
|
+
"description": "Exact DNS records checked for this verification attempt.",
|
|
5245
|
+
"items": { "$ref": "#/components/schemas/DomainDnsRecord" }
|
|
5246
|
+
},
|
|
4858
5247
|
"error": {
|
|
4859
5248
|
"type": "string",
|
|
4860
5249
|
"description": "Human-readable verification failure reason"
|
|
@@ -5167,6 +5556,31 @@ const openapiDocument = {
|
|
|
5167
5556
|
"created_at"
|
|
5168
5557
|
]
|
|
5169
5558
|
},
|
|
5559
|
+
"SendMailAttachment": {
|
|
5560
|
+
"type": "object",
|
|
5561
|
+
"additionalProperties": false,
|
|
5562
|
+
"properties": {
|
|
5563
|
+
"filename": {
|
|
5564
|
+
"type": "string",
|
|
5565
|
+
"minLength": 1,
|
|
5566
|
+
"maxLength": 255,
|
|
5567
|
+
"description": "Attachment filename. Control characters are rejected."
|
|
5568
|
+
},
|
|
5569
|
+
"content_type": {
|
|
5570
|
+
"type": "string",
|
|
5571
|
+
"minLength": 1,
|
|
5572
|
+
"maxLength": 255,
|
|
5573
|
+
"description": "Optional MIME content type. Control characters are rejected."
|
|
5574
|
+
},
|
|
5575
|
+
"content_base64": {
|
|
5576
|
+
"type": "string",
|
|
5577
|
+
"minLength": 1,
|
|
5578
|
+
"maxLength": 44040192,
|
|
5579
|
+
"description": "Base64-encoded attachment bytes."
|
|
5580
|
+
}
|
|
5581
|
+
},
|
|
5582
|
+
"required": ["filename", "content_base64"]
|
|
5583
|
+
},
|
|
5170
5584
|
"SendMailInput": {
|
|
5171
5585
|
"type": "object",
|
|
5172
5586
|
"additionalProperties": false,
|
|
@@ -5215,6 +5629,12 @@ const openapiDocument = {
|
|
|
5215
5629
|
"pattern": "^[^\\x00-\\x1F\\x7F]+$"
|
|
5216
5630
|
}
|
|
5217
5631
|
},
|
|
5632
|
+
"attachments": {
|
|
5633
|
+
"type": "array",
|
|
5634
|
+
"maxItems": 100,
|
|
5635
|
+
"description": "Inline attachments. Send requests with attachments to https://api.primitive.dev/v1/send-mail. Combined raw decoded attachment bytes must be at most 31457280.",
|
|
5636
|
+
"items": { "$ref": "#/components/schemas/SendMailAttachment" }
|
|
5637
|
+
},
|
|
5218
5638
|
"wait": {
|
|
5219
5639
|
"type": "boolean",
|
|
5220
5640
|
"description": "When true, wait for the first downstream SMTP delivery outcome before returning."
|
|
@@ -7502,7 +7922,7 @@ const operationManifest = [
|
|
|
7502
7922
|
"binaryResponse": false,
|
|
7503
7923
|
"bodyRequired": true,
|
|
7504
7924
|
"command": "add-domain",
|
|
7505
|
-
"description": "Creates an unverified domain claim
|
|
7925
|
+
"description": "Creates an unverified domain claim and returns the exact\nDNS records to publish in `dns_records`. Publish those\nrecords before calling the verify endpoint. To give users\nan importable DNS file, call `downloadDomainZoneFile` or run\n`primitive domains zone-file --id <domain-id>`.\n",
|
|
7506
7926
|
"hasJsonBody": true,
|
|
7507
7927
|
"method": "POST",
|
|
7508
7928
|
"operationId": "addDomain",
|
|
@@ -7512,12 +7932,23 @@ const operationManifest = [
|
|
|
7512
7932
|
"requestSchema": {
|
|
7513
7933
|
"type": "object",
|
|
7514
7934
|
"additionalProperties": false,
|
|
7515
|
-
"properties": {
|
|
7516
|
-
"
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7935
|
+
"properties": {
|
|
7936
|
+
"domain": {
|
|
7937
|
+
"type": "string",
|
|
7938
|
+
"minLength": 1,
|
|
7939
|
+
"maxLength": 253,
|
|
7940
|
+
"description": "The domain name to claim (e.g. \"example.com\")"
|
|
7941
|
+
},
|
|
7942
|
+
"confirmed": {
|
|
7943
|
+
"type": "boolean",
|
|
7944
|
+
"description": "Set to true to confirm replacing an existing mailbox provider after an mx_conflict response."
|
|
7945
|
+
},
|
|
7946
|
+
"outbound": {
|
|
7947
|
+
"type": "boolean",
|
|
7948
|
+
"deprecated": true,
|
|
7949
|
+
"description": "Deprecated and ignored. Outbound DNS is provisioned for every new domain claim."
|
|
7950
|
+
}
|
|
7951
|
+
},
|
|
7521
7952
|
"required": ["domain"]
|
|
7522
7953
|
},
|
|
7523
7954
|
"responseSchema": {
|
|
@@ -7540,14 +7971,86 @@ const operationManifest = [
|
|
|
7540
7971
|
"type": "string",
|
|
7541
7972
|
"description": "Add this value as a TXT record to verify ownership"
|
|
7542
7973
|
},
|
|
7543
|
-
"
|
|
7544
|
-
"type": "
|
|
7545
|
-
"
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7974
|
+
"dns_records": {
|
|
7975
|
+
"type": "array",
|
|
7976
|
+
"description": "Exact DNS records to publish for this pending domain claim.",
|
|
7977
|
+
"items": {
|
|
7978
|
+
"type": "object",
|
|
7979
|
+
"additionalProperties": false,
|
|
7980
|
+
"properties": {
|
|
7981
|
+
"type": {
|
|
7982
|
+
"type": "string",
|
|
7983
|
+
"enum": ["MX", "TXT"],
|
|
7984
|
+
"description": "DNS record type."
|
|
7985
|
+
},
|
|
7986
|
+
"name": {
|
|
7987
|
+
"type": "string",
|
|
7988
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
7989
|
+
},
|
|
7990
|
+
"fqdn": {
|
|
7991
|
+
"type": "string",
|
|
7992
|
+
"description": "Fully-qualified DNS record name."
|
|
7993
|
+
},
|
|
7994
|
+
"value": {
|
|
7995
|
+
"type": "string",
|
|
7996
|
+
"description": "Exact value to publish."
|
|
7997
|
+
},
|
|
7998
|
+
"priority": {
|
|
7999
|
+
"type": "integer",
|
|
8000
|
+
"description": "MX priority. Present only for MX records."
|
|
8001
|
+
},
|
|
8002
|
+
"ttl": {
|
|
8003
|
+
"type": "integer",
|
|
8004
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
8005
|
+
},
|
|
8006
|
+
"required": {
|
|
8007
|
+
"type": "boolean",
|
|
8008
|
+
"const": true
|
|
8009
|
+
},
|
|
8010
|
+
"purpose": {
|
|
8011
|
+
"type": "string",
|
|
8012
|
+
"enum": [
|
|
8013
|
+
"inbound_mx",
|
|
8014
|
+
"ownership_verification",
|
|
8015
|
+
"spf",
|
|
8016
|
+
"dkim",
|
|
8017
|
+
"dmarc",
|
|
8018
|
+
"tls_reporting"
|
|
8019
|
+
]
|
|
8020
|
+
},
|
|
8021
|
+
"status": {
|
|
8022
|
+
"type": "string",
|
|
8023
|
+
"enum": [
|
|
8024
|
+
"pending",
|
|
8025
|
+
"found",
|
|
8026
|
+
"missing",
|
|
8027
|
+
"incorrect"
|
|
8028
|
+
]
|
|
8029
|
+
},
|
|
8030
|
+
"message": {
|
|
8031
|
+
"type": "string",
|
|
8032
|
+
"description": "Short explanation of why this record is needed."
|
|
8033
|
+
}
|
|
8034
|
+
},
|
|
8035
|
+
"required": [
|
|
8036
|
+
"type",
|
|
8037
|
+
"name",
|
|
8038
|
+
"fqdn",
|
|
8039
|
+
"value",
|
|
8040
|
+
"required",
|
|
8041
|
+
"purpose",
|
|
8042
|
+
"status"
|
|
8043
|
+
]
|
|
8044
|
+
}
|
|
8045
|
+
},
|
|
8046
|
+
"created_at": {
|
|
8047
|
+
"type": "string",
|
|
8048
|
+
"format": "date-time"
|
|
8049
|
+
}
|
|
8050
|
+
},
|
|
8051
|
+
"required": [
|
|
8052
|
+
"id",
|
|
8053
|
+
"org_id",
|
|
7551
8054
|
"domain",
|
|
7552
8055
|
"verified",
|
|
7553
8056
|
"verification_token",
|
|
@@ -7583,6 +8086,36 @@ const operationManifest = [
|
|
|
7583
8086
|
"tag": "Domains",
|
|
7584
8087
|
"tagCommand": "domains"
|
|
7585
8088
|
},
|
|
8089
|
+
{
|
|
8090
|
+
"binaryResponse": true,
|
|
8091
|
+
"bodyRequired": false,
|
|
8092
|
+
"command": "download-domain-zone-file",
|
|
8093
|
+
"description": "Downloads a BIND-format DNS zone file containing the DNS records\nrequired for a domain claim. Agents should offer this after\n`addDomain` when users want to import DNS records instead of\ncopying each record manually.\n",
|
|
8094
|
+
"hasJsonBody": false,
|
|
8095
|
+
"method": "GET",
|
|
8096
|
+
"operationId": "downloadDomainZoneFile",
|
|
8097
|
+
"path": "/domains/{id}/zone-file",
|
|
8098
|
+
"pathParams": [{
|
|
8099
|
+
"description": "Resource UUID",
|
|
8100
|
+
"enum": null,
|
|
8101
|
+
"name": "id",
|
|
8102
|
+
"required": true,
|
|
8103
|
+
"type": "string"
|
|
8104
|
+
}],
|
|
8105
|
+
"queryParams": [{
|
|
8106
|
+
"description": "When true, include only outbound DNS records. Verified domains\ndefault to outbound-only; pending claims default to all required\nrecords.\n",
|
|
8107
|
+
"enum": null,
|
|
8108
|
+
"name": "outbound_only",
|
|
8109
|
+
"required": false,
|
|
8110
|
+
"type": "boolean"
|
|
8111
|
+
}],
|
|
8112
|
+
"requestSchema": null,
|
|
8113
|
+
"responseSchema": null,
|
|
8114
|
+
"sdkName": "downloadDomainZoneFile",
|
|
8115
|
+
"summary": "Download domain DNS zone file",
|
|
8116
|
+
"tag": "Domains",
|
|
8117
|
+
"tagCommand": "domains"
|
|
8118
|
+
},
|
|
7586
8119
|
{
|
|
7587
8120
|
"binaryResponse": false,
|
|
7588
8121
|
"bodyRequired": false,
|
|
@@ -7598,7 +8131,7 @@ const operationManifest = [
|
|
|
7598
8131
|
"responseSchema": {
|
|
7599
8132
|
"type": "array",
|
|
7600
8133
|
"items": {
|
|
7601
|
-
"description": "A domain can be either verified or unverified. Verified domains have\n`is_active` and `spam_threshold` fields. Unverified domains have a\n`verification_token` for DNS
|
|
8134
|
+
"description": "A domain can be either verified or unverified. Verified domains have\n`is_active` and `spam_threshold` fields. Unverified domains have a\n`verification_token` and `dns_records` for DNS setup.\n",
|
|
7602
8135
|
"oneOf": [{
|
|
7603
8136
|
"type": "object",
|
|
7604
8137
|
"properties": {
|
|
@@ -7655,6 +8188,78 @@ const operationManifest = [
|
|
|
7655
8188
|
"type": "string",
|
|
7656
8189
|
"description": "Add this value as a TXT record to verify ownership"
|
|
7657
8190
|
},
|
|
8191
|
+
"dns_records": {
|
|
8192
|
+
"type": "array",
|
|
8193
|
+
"description": "Exact DNS records to publish for this pending domain claim.",
|
|
8194
|
+
"items": {
|
|
8195
|
+
"type": "object",
|
|
8196
|
+
"additionalProperties": false,
|
|
8197
|
+
"properties": {
|
|
8198
|
+
"type": {
|
|
8199
|
+
"type": "string",
|
|
8200
|
+
"enum": ["MX", "TXT"],
|
|
8201
|
+
"description": "DNS record type."
|
|
8202
|
+
},
|
|
8203
|
+
"name": {
|
|
8204
|
+
"type": "string",
|
|
8205
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
8206
|
+
},
|
|
8207
|
+
"fqdn": {
|
|
8208
|
+
"type": "string",
|
|
8209
|
+
"description": "Fully-qualified DNS record name."
|
|
8210
|
+
},
|
|
8211
|
+
"value": {
|
|
8212
|
+
"type": "string",
|
|
8213
|
+
"description": "Exact value to publish."
|
|
8214
|
+
},
|
|
8215
|
+
"priority": {
|
|
8216
|
+
"type": "integer",
|
|
8217
|
+
"description": "MX priority. Present only for MX records."
|
|
8218
|
+
},
|
|
8219
|
+
"ttl": {
|
|
8220
|
+
"type": "integer",
|
|
8221
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
8222
|
+
},
|
|
8223
|
+
"required": {
|
|
8224
|
+
"type": "boolean",
|
|
8225
|
+
"const": true
|
|
8226
|
+
},
|
|
8227
|
+
"purpose": {
|
|
8228
|
+
"type": "string",
|
|
8229
|
+
"enum": [
|
|
8230
|
+
"inbound_mx",
|
|
8231
|
+
"ownership_verification",
|
|
8232
|
+
"spf",
|
|
8233
|
+
"dkim",
|
|
8234
|
+
"dmarc",
|
|
8235
|
+
"tls_reporting"
|
|
8236
|
+
]
|
|
8237
|
+
},
|
|
8238
|
+
"status": {
|
|
8239
|
+
"type": "string",
|
|
8240
|
+
"enum": [
|
|
8241
|
+
"pending",
|
|
8242
|
+
"found",
|
|
8243
|
+
"missing",
|
|
8244
|
+
"incorrect"
|
|
8245
|
+
]
|
|
8246
|
+
},
|
|
8247
|
+
"message": {
|
|
8248
|
+
"type": "string",
|
|
8249
|
+
"description": "Short explanation of why this record is needed."
|
|
8250
|
+
}
|
|
8251
|
+
},
|
|
8252
|
+
"required": [
|
|
8253
|
+
"type",
|
|
8254
|
+
"name",
|
|
8255
|
+
"fqdn",
|
|
8256
|
+
"value",
|
|
8257
|
+
"required",
|
|
8258
|
+
"purpose",
|
|
8259
|
+
"status"
|
|
8260
|
+
]
|
|
8261
|
+
}
|
|
8262
|
+
},
|
|
7658
8263
|
"created_at": {
|
|
7659
8264
|
"type": "string",
|
|
7660
8265
|
"format": "date-time"
|
|
@@ -7756,7 +8361,7 @@ const operationManifest = [
|
|
|
7756
8361
|
"binaryResponse": false,
|
|
7757
8362
|
"bodyRequired": false,
|
|
7758
8363
|
"command": "verify-domain",
|
|
7759
|
-
"description": "Checks DNS records
|
|
8364
|
+
"description": "Checks DNS records required for inbound routing, ownership,\nand outbound authentication: MX, ownership TXT, SPF, DKIM,\nDMARC, and TLS-RPT.\nOn success, the domain is promoted from unverified to verified.\nOn failure, returns which checks passed and which failed,\nplus the exact DNS records still expected. To give users\nan importable DNS file for missing records, call\n`downloadDomainZoneFile` or run\n`primitive domains zone-file --id <domain-id>`.\n",
|
|
7760
8365
|
"hasJsonBody": false,
|
|
7761
8366
|
"method": "POST",
|
|
7762
8367
|
"operationId": "verifyDomain",
|
|
@@ -7772,10 +8377,84 @@ const operationManifest = [
|
|
|
7772
8377
|
"requestSchema": null,
|
|
7773
8378
|
"responseSchema": { "oneOf": [{
|
|
7774
8379
|
"type": "object",
|
|
7775
|
-
"properties": {
|
|
7776
|
-
"
|
|
7777
|
-
|
|
7778
|
-
|
|
8380
|
+
"properties": {
|
|
8381
|
+
"verified": {
|
|
8382
|
+
"type": "boolean",
|
|
8383
|
+
"const": true
|
|
8384
|
+
},
|
|
8385
|
+
"dns_records": {
|
|
8386
|
+
"type": "array",
|
|
8387
|
+
"description": "Exact DNS records checked for this verification attempt.",
|
|
8388
|
+
"items": {
|
|
8389
|
+
"type": "object",
|
|
8390
|
+
"additionalProperties": false,
|
|
8391
|
+
"properties": {
|
|
8392
|
+
"type": {
|
|
8393
|
+
"type": "string",
|
|
8394
|
+
"enum": ["MX", "TXT"],
|
|
8395
|
+
"description": "DNS record type."
|
|
8396
|
+
},
|
|
8397
|
+
"name": {
|
|
8398
|
+
"type": "string",
|
|
8399
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
8400
|
+
},
|
|
8401
|
+
"fqdn": {
|
|
8402
|
+
"type": "string",
|
|
8403
|
+
"description": "Fully-qualified DNS record name."
|
|
8404
|
+
},
|
|
8405
|
+
"value": {
|
|
8406
|
+
"type": "string",
|
|
8407
|
+
"description": "Exact value to publish."
|
|
8408
|
+
},
|
|
8409
|
+
"priority": {
|
|
8410
|
+
"type": "integer",
|
|
8411
|
+
"description": "MX priority. Present only for MX records."
|
|
8412
|
+
},
|
|
8413
|
+
"ttl": {
|
|
8414
|
+
"type": "integer",
|
|
8415
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
8416
|
+
},
|
|
8417
|
+
"required": {
|
|
8418
|
+
"type": "boolean",
|
|
8419
|
+
"const": true
|
|
8420
|
+
},
|
|
8421
|
+
"purpose": {
|
|
8422
|
+
"type": "string",
|
|
8423
|
+
"enum": [
|
|
8424
|
+
"inbound_mx",
|
|
8425
|
+
"ownership_verification",
|
|
8426
|
+
"spf",
|
|
8427
|
+
"dkim",
|
|
8428
|
+
"dmarc",
|
|
8429
|
+
"tls_reporting"
|
|
8430
|
+
]
|
|
8431
|
+
},
|
|
8432
|
+
"status": {
|
|
8433
|
+
"type": "string",
|
|
8434
|
+
"enum": [
|
|
8435
|
+
"pending",
|
|
8436
|
+
"found",
|
|
8437
|
+
"missing",
|
|
8438
|
+
"incorrect"
|
|
8439
|
+
]
|
|
8440
|
+
},
|
|
8441
|
+
"message": {
|
|
8442
|
+
"type": "string",
|
|
8443
|
+
"description": "Short explanation of why this record is needed."
|
|
8444
|
+
}
|
|
8445
|
+
},
|
|
8446
|
+
"required": [
|
|
8447
|
+
"type",
|
|
8448
|
+
"name",
|
|
8449
|
+
"fqdn",
|
|
8450
|
+
"value",
|
|
8451
|
+
"required",
|
|
8452
|
+
"purpose",
|
|
8453
|
+
"status"
|
|
8454
|
+
]
|
|
8455
|
+
}
|
|
8456
|
+
}
|
|
8457
|
+
},
|
|
7779
8458
|
"required": ["verified"]
|
|
7780
8459
|
}, {
|
|
7781
8460
|
"type": "object",
|
|
@@ -7792,6 +8471,94 @@ const operationManifest = [
|
|
|
7792
8471
|
"type": "boolean",
|
|
7793
8472
|
"description": "Whether the TXT verification record was found"
|
|
7794
8473
|
},
|
|
8474
|
+
"spfFound": {
|
|
8475
|
+
"type": "boolean",
|
|
8476
|
+
"description": "Whether the SPF record includes Primitive."
|
|
8477
|
+
},
|
|
8478
|
+
"dkimFound": {
|
|
8479
|
+
"type": "boolean",
|
|
8480
|
+
"description": "Whether the DKIM public key record was found."
|
|
8481
|
+
},
|
|
8482
|
+
"dmarcFound": {
|
|
8483
|
+
"type": "boolean",
|
|
8484
|
+
"description": "Whether the DMARC record was found."
|
|
8485
|
+
},
|
|
8486
|
+
"tlsRptFound": {
|
|
8487
|
+
"type": "boolean",
|
|
8488
|
+
"description": "Whether the TLS-RPT record was found."
|
|
8489
|
+
},
|
|
8490
|
+
"dns_records": {
|
|
8491
|
+
"type": "array",
|
|
8492
|
+
"description": "Exact DNS records checked for this verification attempt.",
|
|
8493
|
+
"items": {
|
|
8494
|
+
"type": "object",
|
|
8495
|
+
"additionalProperties": false,
|
|
8496
|
+
"properties": {
|
|
8497
|
+
"type": {
|
|
8498
|
+
"type": "string",
|
|
8499
|
+
"enum": ["MX", "TXT"],
|
|
8500
|
+
"description": "DNS record type."
|
|
8501
|
+
},
|
|
8502
|
+
"name": {
|
|
8503
|
+
"type": "string",
|
|
8504
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
8505
|
+
},
|
|
8506
|
+
"fqdn": {
|
|
8507
|
+
"type": "string",
|
|
8508
|
+
"description": "Fully-qualified DNS record name."
|
|
8509
|
+
},
|
|
8510
|
+
"value": {
|
|
8511
|
+
"type": "string",
|
|
8512
|
+
"description": "Exact value to publish."
|
|
8513
|
+
},
|
|
8514
|
+
"priority": {
|
|
8515
|
+
"type": "integer",
|
|
8516
|
+
"description": "MX priority. Present only for MX records."
|
|
8517
|
+
},
|
|
8518
|
+
"ttl": {
|
|
8519
|
+
"type": "integer",
|
|
8520
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
8521
|
+
},
|
|
8522
|
+
"required": {
|
|
8523
|
+
"type": "boolean",
|
|
8524
|
+
"const": true
|
|
8525
|
+
},
|
|
8526
|
+
"purpose": {
|
|
8527
|
+
"type": "string",
|
|
8528
|
+
"enum": [
|
|
8529
|
+
"inbound_mx",
|
|
8530
|
+
"ownership_verification",
|
|
8531
|
+
"spf",
|
|
8532
|
+
"dkim",
|
|
8533
|
+
"dmarc",
|
|
8534
|
+
"tls_reporting"
|
|
8535
|
+
]
|
|
8536
|
+
},
|
|
8537
|
+
"status": {
|
|
8538
|
+
"type": "string",
|
|
8539
|
+
"enum": [
|
|
8540
|
+
"pending",
|
|
8541
|
+
"found",
|
|
8542
|
+
"missing",
|
|
8543
|
+
"incorrect"
|
|
8544
|
+
]
|
|
8545
|
+
},
|
|
8546
|
+
"message": {
|
|
8547
|
+
"type": "string",
|
|
8548
|
+
"description": "Short explanation of why this record is needed."
|
|
8549
|
+
}
|
|
8550
|
+
},
|
|
8551
|
+
"required": [
|
|
8552
|
+
"type",
|
|
8553
|
+
"name",
|
|
8554
|
+
"fqdn",
|
|
8555
|
+
"value",
|
|
8556
|
+
"required",
|
|
8557
|
+
"purpose",
|
|
8558
|
+
"status"
|
|
8559
|
+
]
|
|
8560
|
+
}
|
|
8561
|
+
},
|
|
7795
8562
|
"error": {
|
|
7796
8563
|
"type": "string",
|
|
7797
8564
|
"description": "Human-readable verification failure reason"
|
|
@@ -10299,39 +11066,226 @@ const operationManifest = [
|
|
|
10299
11066
|
"pending",
|
|
10300
11067
|
"deployed",
|
|
10301
11068
|
"failed"
|
|
10302
|
-
],
|
|
10303
|
-
"description": "Lifecycle state of the latest deploy attempt:\n * `pending` — deploy in flight; the runtime has not yet\n confirmed the new bundle is live.\n * `deployed` — the running edge handler is the latest code.\n * `failed` — the most recent deploy attempt failed; the\n previously-live code (if any) is still running. The\n `deploy_error` field carries the error message.\n"
|
|
10304
|
-
},
|
|
10305
|
-
"deploy_error": {
|
|
10306
|
-
"type": ["string", "null"],
|
|
10307
|
-
"description": "Error message from the most recent failed deploy, or null\nafter a successful deploy. Surface this to users to explain\na `failed` status without polling.\n"
|
|
10308
|
-
},
|
|
10309
|
-
"deployed_at": {
|
|
10310
|
-
"type": ["string", "null"],
|
|
10311
|
-
"format": "date-time"
|
|
10312
|
-
},
|
|
10313
|
-
"created_at": {
|
|
10314
|
-
"type": "string",
|
|
10315
|
-
"format": "date-time"
|
|
11069
|
+
],
|
|
11070
|
+
"description": "Lifecycle state of the latest deploy attempt:\n * `pending` — deploy in flight; the runtime has not yet\n confirmed the new bundle is live.\n * `deployed` — the running edge handler is the latest code.\n * `failed` — the most recent deploy attempt failed; the\n previously-live code (if any) is still running. The\n `deploy_error` field carries the error message.\n"
|
|
11071
|
+
},
|
|
11072
|
+
"deploy_error": {
|
|
11073
|
+
"type": ["string", "null"],
|
|
11074
|
+
"description": "Error message from the most recent failed deploy, or null\nafter a successful deploy. Surface this to users to explain\na `failed` status without polling.\n"
|
|
11075
|
+
},
|
|
11076
|
+
"deployed_at": {
|
|
11077
|
+
"type": ["string", "null"],
|
|
11078
|
+
"format": "date-time"
|
|
11079
|
+
},
|
|
11080
|
+
"created_at": {
|
|
11081
|
+
"type": "string",
|
|
11082
|
+
"format": "date-time"
|
|
11083
|
+
},
|
|
11084
|
+
"updated_at": {
|
|
11085
|
+
"type": "string",
|
|
11086
|
+
"format": "date-time"
|
|
11087
|
+
}
|
|
11088
|
+
},
|
|
11089
|
+
"required": [
|
|
11090
|
+
"id",
|
|
11091
|
+
"name",
|
|
11092
|
+
"code",
|
|
11093
|
+
"deploy_status",
|
|
11094
|
+
"created_at",
|
|
11095
|
+
"updated_at"
|
|
11096
|
+
]
|
|
11097
|
+
},
|
|
11098
|
+
"sdkName": "updateFunction",
|
|
11099
|
+
"summary": "Update and redeploy a function",
|
|
11100
|
+
"tag": "Functions",
|
|
11101
|
+
"tagCommand": "functions"
|
|
11102
|
+
},
|
|
11103
|
+
{
|
|
11104
|
+
"binaryResponse": false,
|
|
11105
|
+
"bodyRequired": false,
|
|
11106
|
+
"command": "get-inbox-status",
|
|
11107
|
+
"description": "Returns one consolidated view of inbound domain readiness,\nwebhook/function processing routes, deployed Functions, and\nrecent inbound email activity.\n\nAgents should call this before guiding a user through inbound\nsetup. It answers the practical questions \"can I receive mail\",\n\"will anything process that mail\", and \"what should I do next\"\nwithout forcing clients to stitch together domains, endpoints,\nfunctions, and emails manually.\n",
|
|
11108
|
+
"hasJsonBody": false,
|
|
11109
|
+
"method": "GET",
|
|
11110
|
+
"operationId": "getInboxStatus",
|
|
11111
|
+
"path": "/inbox/status",
|
|
11112
|
+
"pathParams": [],
|
|
11113
|
+
"queryParams": [],
|
|
11114
|
+
"requestSchema": null,
|
|
11115
|
+
"responseSchema": {
|
|
11116
|
+
"type": "object",
|
|
11117
|
+
"additionalProperties": false,
|
|
11118
|
+
"properties": {
|
|
11119
|
+
"ready": {
|
|
11120
|
+
"type": "boolean",
|
|
11121
|
+
"description": "True when at least one active inbound domain has an enabled processing route."
|
|
11122
|
+
},
|
|
11123
|
+
"receiving_ready": {
|
|
11124
|
+
"type": "boolean",
|
|
11125
|
+
"description": "True when at least one active verified or managed domain can receive mail."
|
|
11126
|
+
},
|
|
11127
|
+
"processing_ready": {
|
|
11128
|
+
"type": "boolean",
|
|
11129
|
+
"description": "True when at least one receiving-ready domain has an enabled webhook or function route."
|
|
11130
|
+
},
|
|
11131
|
+
"summary": {
|
|
11132
|
+
"type": "string",
|
|
11133
|
+
"description": "Short human-readable status summary."
|
|
11134
|
+
},
|
|
11135
|
+
"next_actions": {
|
|
11136
|
+
"type": "array",
|
|
11137
|
+
"items": {
|
|
11138
|
+
"type": "object",
|
|
11139
|
+
"additionalProperties": false,
|
|
11140
|
+
"properties": {
|
|
11141
|
+
"kind": {
|
|
11142
|
+
"type": "string",
|
|
11143
|
+
"enum": [
|
|
11144
|
+
"add_domain",
|
|
11145
|
+
"verify_domain",
|
|
11146
|
+
"configure_processing",
|
|
11147
|
+
"send_test_email",
|
|
11148
|
+
"fix_failed_functions"
|
|
11149
|
+
]
|
|
11150
|
+
},
|
|
11151
|
+
"message": {
|
|
11152
|
+
"type": "string",
|
|
11153
|
+
"description": "Human-readable next step."
|
|
11154
|
+
},
|
|
11155
|
+
"command": {
|
|
11156
|
+
"type": "string",
|
|
11157
|
+
"description": "Suggested Primitive CLI command when there is an obvious next step."
|
|
11158
|
+
}
|
|
11159
|
+
},
|
|
11160
|
+
"required": ["kind", "message"]
|
|
11161
|
+
}
|
|
11162
|
+
},
|
|
11163
|
+
"domains": {
|
|
11164
|
+
"type": "array",
|
|
11165
|
+
"items": {
|
|
11166
|
+
"type": "object",
|
|
11167
|
+
"additionalProperties": false,
|
|
11168
|
+
"properties": {
|
|
11169
|
+
"id": { "type": "string" },
|
|
11170
|
+
"domain": { "type": "string" },
|
|
11171
|
+
"verified": { "type": "boolean" },
|
|
11172
|
+
"active": { "type": "boolean" },
|
|
11173
|
+
"managed": { "type": "boolean" },
|
|
11174
|
+
"receiving_ready": { "type": "boolean" },
|
|
11175
|
+
"processing_ready": { "type": "boolean" },
|
|
11176
|
+
"processing_route_count": { "type": "integer" },
|
|
11177
|
+
"endpoint_count": { "type": "integer" },
|
|
11178
|
+
"enabled_endpoint_count": { "type": "integer" },
|
|
11179
|
+
"function_endpoint_count": { "type": "integer" },
|
|
11180
|
+
"email_count": {
|
|
11181
|
+
"type": "integer",
|
|
11182
|
+
"description": "Number of inbound emails received for this domain in the last 30 days."
|
|
11183
|
+
},
|
|
11184
|
+
"latest_email_received_at": {
|
|
11185
|
+
"type": ["string", "null"],
|
|
11186
|
+
"format": "date-time",
|
|
11187
|
+
"description": "Most recent inbound email received for this domain in the last 30 days."
|
|
11188
|
+
},
|
|
11189
|
+
"status": {
|
|
11190
|
+
"type": "string",
|
|
11191
|
+
"enum": [
|
|
11192
|
+
"ready",
|
|
11193
|
+
"stored_only",
|
|
11194
|
+
"pending_dns",
|
|
11195
|
+
"inactive"
|
|
11196
|
+
]
|
|
11197
|
+
}
|
|
11198
|
+
},
|
|
11199
|
+
"required": [
|
|
11200
|
+
"id",
|
|
11201
|
+
"domain",
|
|
11202
|
+
"verified",
|
|
11203
|
+
"active",
|
|
11204
|
+
"managed",
|
|
11205
|
+
"receiving_ready",
|
|
11206
|
+
"processing_ready",
|
|
11207
|
+
"processing_route_count",
|
|
11208
|
+
"endpoint_count",
|
|
11209
|
+
"enabled_endpoint_count",
|
|
11210
|
+
"function_endpoint_count",
|
|
11211
|
+
"email_count",
|
|
11212
|
+
"latest_email_received_at",
|
|
11213
|
+
"status"
|
|
11214
|
+
]
|
|
11215
|
+
}
|
|
11216
|
+
},
|
|
11217
|
+
"endpoints": {
|
|
11218
|
+
"type": "object",
|
|
11219
|
+
"additionalProperties": false,
|
|
11220
|
+
"properties": {
|
|
11221
|
+
"total": { "type": "integer" },
|
|
11222
|
+
"enabled": { "type": "integer" },
|
|
11223
|
+
"disabled": { "type": "integer" },
|
|
11224
|
+
"fallback_enabled": { "type": "integer" },
|
|
11225
|
+
"domain_scoped_enabled": { "type": "integer" },
|
|
11226
|
+
"http_enabled": { "type": "integer" },
|
|
11227
|
+
"function_enabled": { "type": "integer" }
|
|
11228
|
+
},
|
|
11229
|
+
"required": [
|
|
11230
|
+
"total",
|
|
11231
|
+
"enabled",
|
|
11232
|
+
"disabled",
|
|
11233
|
+
"fallback_enabled",
|
|
11234
|
+
"domain_scoped_enabled",
|
|
11235
|
+
"http_enabled",
|
|
11236
|
+
"function_enabled"
|
|
11237
|
+
]
|
|
11238
|
+
},
|
|
11239
|
+
"functions": {
|
|
11240
|
+
"type": "object",
|
|
11241
|
+
"additionalProperties": false,
|
|
11242
|
+
"properties": {
|
|
11243
|
+
"total": { "type": "integer" },
|
|
11244
|
+
"deployed": { "type": "integer" },
|
|
11245
|
+
"pending": { "type": "integer" },
|
|
11246
|
+
"failed": { "type": "integer" }
|
|
11247
|
+
},
|
|
11248
|
+
"required": [
|
|
11249
|
+
"total",
|
|
11250
|
+
"deployed",
|
|
11251
|
+
"pending",
|
|
11252
|
+
"failed"
|
|
11253
|
+
]
|
|
10316
11254
|
},
|
|
10317
|
-
"
|
|
10318
|
-
"type": "
|
|
10319
|
-
"
|
|
11255
|
+
"recent_emails": {
|
|
11256
|
+
"type": "object",
|
|
11257
|
+
"description": "Inbound email activity from the last 30 days.",
|
|
11258
|
+
"additionalProperties": false,
|
|
11259
|
+
"properties": {
|
|
11260
|
+
"total": {
|
|
11261
|
+
"type": "integer",
|
|
11262
|
+
"description": "Number of inbound emails received in the last 30 days."
|
|
11263
|
+
},
|
|
11264
|
+
"latest_received_at": {
|
|
11265
|
+
"type": ["string", "null"],
|
|
11266
|
+
"format": "date-time",
|
|
11267
|
+
"description": "Most recent inbound email received in the last 30 days."
|
|
11268
|
+
}
|
|
11269
|
+
},
|
|
11270
|
+
"required": ["total", "latest_received_at"]
|
|
10320
11271
|
}
|
|
10321
11272
|
},
|
|
10322
11273
|
"required": [
|
|
10323
|
-
"
|
|
10324
|
-
"
|
|
10325
|
-
"
|
|
10326
|
-
"
|
|
10327
|
-
"
|
|
10328
|
-
"
|
|
11274
|
+
"ready",
|
|
11275
|
+
"receiving_ready",
|
|
11276
|
+
"processing_ready",
|
|
11277
|
+
"summary",
|
|
11278
|
+
"next_actions",
|
|
11279
|
+
"domains",
|
|
11280
|
+
"endpoints",
|
|
11281
|
+
"functions",
|
|
11282
|
+
"recent_emails"
|
|
10329
11283
|
]
|
|
10330
11284
|
},
|
|
10331
|
-
"sdkName": "
|
|
10332
|
-
"summary": "
|
|
10333
|
-
"tag": "
|
|
10334
|
-
"tagCommand": "
|
|
11285
|
+
"sdkName": "getInboxStatus",
|
|
11286
|
+
"summary": "Get inbound inbox readiness",
|
|
11287
|
+
"tag": "Inbox",
|
|
11288
|
+
"tagCommand": "inbox"
|
|
10335
11289
|
},
|
|
10336
11290
|
{
|
|
10337
11291
|
"binaryResponse": false,
|
|
@@ -11165,6 +12119,36 @@ const operationManifest = [
|
|
|
11165
12119
|
"pattern": "^[^\\x00-\\x1F\\x7F]+$"
|
|
11166
12120
|
}
|
|
11167
12121
|
},
|
|
12122
|
+
"attachments": {
|
|
12123
|
+
"type": "array",
|
|
12124
|
+
"maxItems": 100,
|
|
12125
|
+
"description": "Inline attachments. Send requests with attachments to https://api.primitive.dev/v1/send-mail. Combined raw decoded attachment bytes must be at most 31457280.",
|
|
12126
|
+
"items": {
|
|
12127
|
+
"type": "object",
|
|
12128
|
+
"additionalProperties": false,
|
|
12129
|
+
"properties": {
|
|
12130
|
+
"filename": {
|
|
12131
|
+
"type": "string",
|
|
12132
|
+
"minLength": 1,
|
|
12133
|
+
"maxLength": 255,
|
|
12134
|
+
"description": "Attachment filename. Control characters are rejected."
|
|
12135
|
+
},
|
|
12136
|
+
"content_type": {
|
|
12137
|
+
"type": "string",
|
|
12138
|
+
"minLength": 1,
|
|
12139
|
+
"maxLength": 255,
|
|
12140
|
+
"description": "Optional MIME content type. Control characters are rejected."
|
|
12141
|
+
},
|
|
12142
|
+
"content_base64": {
|
|
12143
|
+
"type": "string",
|
|
12144
|
+
"minLength": 1,
|
|
12145
|
+
"maxLength": 44040192,
|
|
12146
|
+
"description": "Base64-encoded attachment bytes."
|
|
12147
|
+
}
|
|
12148
|
+
},
|
|
12149
|
+
"required": ["filename", "content_base64"]
|
|
12150
|
+
}
|
|
12151
|
+
},
|
|
11168
12152
|
"wait": {
|
|
11169
12153
|
"type": "boolean",
|
|
11170
12154
|
"description": "When true, wait for the first downstream SMTP delivery outcome before returning."
|
|
@@ -12501,6 +13485,11 @@ function operationOutputPayload(envelope, includeEnvelope) {
|
|
|
12501
13485
|
return includeEnvelope ? envelope ?? null : envelope?.data ?? null;
|
|
12502
13486
|
}
|
|
12503
13487
|
const OPERATION_HINTS = {
|
|
13488
|
+
addDomain: "Tip: after this returns a domain id, run `primitive domains zone-file --id <domain-id> --output <domain>.zone` when the user wants an importable DNS zone file.",
|
|
13489
|
+
verifyDomain: "Tip: if DNS is still missing, run `primitive domains zone-file --id <domain-id> --output <domain>.zone` to give the user an importable DNS zone file.",
|
|
13490
|
+
downloadDomainZoneFile: "Tip: prefer `primitive domains zone-file --id <domain-id> --output <domain>.zone` for CLI-friendly file output.",
|
|
13491
|
+
getInboxStatus: "Tip: prefer `primitive inbox status` for a compact readiness summary and next-step commands.",
|
|
13492
|
+
sendEmail: "Tip: prefer `primitive send --to <address> --body <text> --attachment <file>` for file attachments. This raw command exists for callers passing JSON.",
|
|
12504
13493
|
createFunction: "Tip: prefer `primitive functions deploy --name <name> --file <bundle>` for file-input ergonomics. This raw command exists for callers passing JSON.",
|
|
12505
13494
|
updateFunction: "Tip: prefer `primitive functions redeploy --id <id> --file <bundle>` for file-input ergonomics. This raw command exists for callers passing JSON.",
|
|
12506
13495
|
createFunctionSecret: "Tip: prefer `primitive functions set-secret --id <id> --key <KEY> --value <value> [--redeploy]` for secret writes that also push the binding live. This raw command exists for callers passing JSON.",
|
|
@@ -12612,7 +13601,8 @@ const EMPTY_RESULT_HINTS = {
|
|
|
12612
13601
|
listEndpoints: "(no results) No webhook endpoints configured. Add one with `primitive endpoints create --url <your-url>`.",
|
|
12613
13602
|
listEmails: "(no results) No inbound emails received yet on this account. Send one to a verified domain to populate this list. For a compact view, prefer `primitive emails latest`.",
|
|
12614
13603
|
listDomains: "(no results) No domains on this account. Add one with `primitive domains add --domain <yourdomain.example>`.",
|
|
12615
|
-
listFilters: "(no results) No filter rules configured."
|
|
13604
|
+
listFilters: "(no results) No filter rules configured.",
|
|
13605
|
+
listFunctions: "(no results) No Functions configured yet. Start with `primitive functions templates`, then `primitive functions init --template <template>` and `primitive functions deploy --name <name> --file <bundle>`."
|
|
12616
13606
|
};
|
|
12617
13607
|
function canonicalizeCliReferences(description) {
|
|
12618
13608
|
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'`");
|
|
@@ -13379,6 +14369,144 @@ var DoctorCommand = class DoctorCommand extends Command {
|
|
|
13379
14369
|
}
|
|
13380
14370
|
};
|
|
13381
14371
|
//#endregion
|
|
14372
|
+
//#region src/oclif/commands/domains-zone-file.ts
|
|
14373
|
+
function zoneFileUrl(baseUrl, domainId, outboundOnly) {
|
|
14374
|
+
const url = new URL(`${baseUrl.replace(/\/$/, "")}/domains/${encodeURIComponent(domainId)}/zone-file`);
|
|
14375
|
+
if (outboundOnly) url.searchParams.set("outbound_only", "true");
|
|
14376
|
+
return url.toString();
|
|
14377
|
+
}
|
|
14378
|
+
function contentDispositionFilename(value) {
|
|
14379
|
+
if (!value) return null;
|
|
14380
|
+
const match = /filename\*=UTF-8''([^;]+)|filename="?([^";]+)"?/i.exec(value);
|
|
14381
|
+
const raw = match?.[1] ?? match?.[2];
|
|
14382
|
+
if (!raw) return null;
|
|
14383
|
+
try {
|
|
14384
|
+
return decodeURIComponent(raw);
|
|
14385
|
+
} catch {
|
|
14386
|
+
return raw;
|
|
14387
|
+
}
|
|
14388
|
+
}
|
|
14389
|
+
function findDomain(domains, domain) {
|
|
14390
|
+
const match = domains.find((entry) => entry.domain === domain);
|
|
14391
|
+
if (!match) throw new Errors.CLIError(`Domain ${domain} was not found.`, { exit: 1 });
|
|
14392
|
+
return match;
|
|
14393
|
+
}
|
|
14394
|
+
async function responseErrorPayload(response) {
|
|
14395
|
+
if ((response.headers.get("content-type")?.toLowerCase() ?? "").includes("application/json")) return response.json().catch(() => ({
|
|
14396
|
+
code: "http_error",
|
|
14397
|
+
message: `HTTP ${response.status} ${response.statusText}`.trim()
|
|
14398
|
+
}));
|
|
14399
|
+
return {
|
|
14400
|
+
code: "http_error",
|
|
14401
|
+
message: (await response.text().catch(() => "")).trim() || `HTTP ${response.status} ${response.statusText}`.trim()
|
|
14402
|
+
};
|
|
14403
|
+
}
|
|
14404
|
+
var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
14405
|
+
static description = `Download a BIND-format DNS zone file for a domain claim.
|
|
14406
|
+
|
|
14407
|
+
Use this when a DNS provider supports zone-file import and you want to publish all required records at once instead of copying each record manually. The file is generated by the Primitive API, matching the dashboard download flow.
|
|
14408
|
+
|
|
14409
|
+
Agents: after claiming a domain, tell users they can run \`primitive domains zone-file --id <domain-id> --output <domain>.zone\` to get an importable DNS zone file.`;
|
|
14410
|
+
static summary = "Download a DNS zone file for a domain";
|
|
14411
|
+
static examples = [
|
|
14412
|
+
"<%= config.bin %> domains zone-file --id <domain-id>",
|
|
14413
|
+
"<%= config.bin %> domains zone-file --id <domain-id> --output example.com.zone",
|
|
14414
|
+
"<%= config.bin %> domains zone-file --domain example.com --output example.com.zone",
|
|
14415
|
+
"<%= config.bin %> domains zone-file --id <domain-id> --outbound-only"
|
|
14416
|
+
];
|
|
14417
|
+
static flags = {
|
|
14418
|
+
"api-key": Flags.string({
|
|
14419
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
14420
|
+
env: "PRIMITIVE_API_KEY"
|
|
14421
|
+
}),
|
|
14422
|
+
"api-base-url-1": Flags.string({
|
|
14423
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
14424
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
14425
|
+
hidden: true
|
|
14426
|
+
}),
|
|
14427
|
+
"api-base-url-2": Flags.string({
|
|
14428
|
+
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
14429
|
+
env: "PRIMITIVE_API_BASE_URL_2",
|
|
14430
|
+
hidden: true
|
|
14431
|
+
}),
|
|
14432
|
+
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`." }),
|
|
14433
|
+
id: Flags.string({ description: "Domain id returned by `primitive domains add` or `primitive domains list`." }),
|
|
14434
|
+
output: Flags.string({
|
|
14435
|
+
char: "o",
|
|
14436
|
+
description: "Write the zone file to this path instead of stdout. Defaults to stdout."
|
|
14437
|
+
}),
|
|
14438
|
+
"outbound-only": Flags.boolean({ description: "Include only outbound DNS records. Verified domains default to outbound-only at the API; this flag is explicit for agents and scripts." }),
|
|
14439
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
14440
|
+
};
|
|
14441
|
+
async run() {
|
|
14442
|
+
const { flags } = await this.parse(DomainsZoneFileCommand);
|
|
14443
|
+
if (flags.id && flags.domain) throw new Errors.CLIError("Use only one of --id or --domain.", { exit: 1 });
|
|
14444
|
+
if (!flags.id && !flags.domain) throw new Errors.CLIError("Pass --id <domain-id> or --domain <domain>.", { exit: 1 });
|
|
14445
|
+
await runWithTiming(flags.time, async () => {
|
|
14446
|
+
const { apiClient, auth, baseUrlOverridden, requestConfig } = await createAuthenticatedCliApiClient({
|
|
14447
|
+
apiKey: flags["api-key"],
|
|
14448
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
14449
|
+
apiBaseUrl2: flags["api-base-url-2"],
|
|
14450
|
+
configDir: this.config.configDir
|
|
14451
|
+
});
|
|
14452
|
+
let domainId = flags.id;
|
|
14453
|
+
if (!domainId && flags.domain) {
|
|
14454
|
+
const result = await listDomains({
|
|
14455
|
+
client: apiClient.client,
|
|
14456
|
+
responseStyle: "fields"
|
|
14457
|
+
});
|
|
14458
|
+
if (result.error) {
|
|
14459
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
14460
|
+
writeErrorWithHints(errorPayload);
|
|
14461
|
+
surfaceUnauthorizedHint({
|
|
14462
|
+
auth,
|
|
14463
|
+
baseUrlOverridden,
|
|
14464
|
+
configDir: this.config.configDir,
|
|
14465
|
+
payload: errorPayload
|
|
14466
|
+
});
|
|
14467
|
+
process.exitCode = 1;
|
|
14468
|
+
return;
|
|
14469
|
+
}
|
|
14470
|
+
const envelope = result.data;
|
|
14471
|
+
domainId = findDomain(envelope?.data ?? [], flags.domain).id;
|
|
14472
|
+
}
|
|
14473
|
+
if (!domainId) throw new Errors.CLIError("Could not resolve a domain id.", { exit: 1 });
|
|
14474
|
+
let response;
|
|
14475
|
+
try {
|
|
14476
|
+
response = await fetch(zoneFileUrl(requestConfig.resolvedApiBaseUrl1, domainId, flags["outbound-only"]), { headers: {
|
|
14477
|
+
...requestConfig.headers ?? {},
|
|
14478
|
+
...auth.apiKey ? { authorization: `Bearer ${auth.apiKey}` } : {}
|
|
14479
|
+
} });
|
|
14480
|
+
} catch (error) {
|
|
14481
|
+
writeErrorWithHints(error);
|
|
14482
|
+
process.exitCode = 1;
|
|
14483
|
+
return;
|
|
14484
|
+
}
|
|
14485
|
+
if (!response.ok) {
|
|
14486
|
+
const errorPayload = extractErrorPayload(await responseErrorPayload(response));
|
|
14487
|
+
writeErrorWithHints(errorPayload);
|
|
14488
|
+
surfaceUnauthorizedHint({
|
|
14489
|
+
auth,
|
|
14490
|
+
baseUrlOverridden,
|
|
14491
|
+
configDir: this.config.configDir,
|
|
14492
|
+
payload: errorPayload
|
|
14493
|
+
});
|
|
14494
|
+
process.exitCode = 1;
|
|
14495
|
+
return;
|
|
14496
|
+
}
|
|
14497
|
+
const zoneFile = await response.text();
|
|
14498
|
+
const output = flags.output;
|
|
14499
|
+
if (output) {
|
|
14500
|
+
writeFileSync(output, zoneFile, "utf8");
|
|
14501
|
+
const filename = contentDispositionFilename(response.headers.get("content-disposition"));
|
|
14502
|
+
process.stderr.write(`Wrote ${filename ?? "zone file"} to ${output}\n`);
|
|
14503
|
+
return;
|
|
14504
|
+
}
|
|
14505
|
+
process.stdout.write(zoneFile);
|
|
14506
|
+
});
|
|
14507
|
+
}
|
|
14508
|
+
};
|
|
14509
|
+
//#endregion
|
|
13382
14510
|
//#region src/oclif/commands/emails-latest.ts
|
|
13383
14511
|
const DEFAULT_LIMIT = 10;
|
|
13384
14512
|
const MAX_LIMIT = 100;
|
|
@@ -13387,7 +14515,7 @@ const ADDRESS_DISPLAY_WIDTH = 32;
|
|
|
13387
14515
|
const ID_DISPLAY_WIDTH_SHORT = 8;
|
|
13388
14516
|
const ID_DISPLAY_WIDTH_FULL = 36;
|
|
13389
14517
|
const RECEIVED_DISPLAY_WIDTH = 19;
|
|
13390
|
-
function truncate(value, width) {
|
|
14518
|
+
function truncate$1(value, width) {
|
|
13391
14519
|
if (value.length <= width) return value.padEnd(width);
|
|
13392
14520
|
return `${value.slice(0, width - 3)}...`;
|
|
13393
14521
|
}
|
|
@@ -13402,7 +14530,7 @@ function pickIdWidth(isTty) {
|
|
|
13402
14530
|
return isTty ? ID_DISPLAY_WIDTH_SHORT : ID_DISPLAY_WIDTH_FULL;
|
|
13403
14531
|
}
|
|
13404
14532
|
function formatRow(email, idWidth) {
|
|
13405
|
-
return `${truncate(email.id.slice(0, idWidth), idWidth)} ${formatReceivedAt(email.received_at)} ${truncate(email.sender ?? "", ADDRESS_DISPLAY_WIDTH)} ${truncate(email.recipient ?? "", ADDRESS_DISPLAY_WIDTH)} ${truncate((email.subject ?? "").replace(/\s+/g, " "), SUBJECT_DISPLAY_WIDTH)}`;
|
|
14533
|
+
return `${truncate$1(email.id.slice(0, idWidth), idWidth)} ${formatReceivedAt(email.received_at)} ${truncate$1(email.sender ?? "", ADDRESS_DISPLAY_WIDTH)} ${truncate$1(email.recipient ?? "", ADDRESS_DISPLAY_WIDTH)} ${truncate$1((email.subject ?? "").replace(/\s+/g, " "), SUBJECT_DISPLAY_WIDTH)}`;
|
|
13406
14534
|
}
|
|
13407
14535
|
function formatHeader(idWidth) {
|
|
13408
14536
|
return `${"ID".padEnd(idWidth)} ${"RECEIVED (UTC)".padEnd(RECEIVED_DISPLAY_WIDTH)} ${"FROM".padEnd(ADDRESS_DISPLAY_WIDTH)} ${"TO".padEnd(ADDRESS_DISPLAY_WIDTH)} SUBJECT`;
|
|
@@ -15788,6 +16916,174 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
15788
16916
|
}
|
|
15789
16917
|
};
|
|
15790
16918
|
//#endregion
|
|
16919
|
+
//#region src/oclif/commands/inbox-status.ts
|
|
16920
|
+
const DOMAIN_DISPLAY_WIDTH = 34;
|
|
16921
|
+
const STATUS_DISPLAY_WIDTH = 12;
|
|
16922
|
+
const BOOL_DISPLAY_WIDTH = 7;
|
|
16923
|
+
const NUM_DISPLAY_WIDTH = 6;
|
|
16924
|
+
function plural(count, singular, pluralValue = `${singular}s`) {
|
|
16925
|
+
return `${count} ${count === 1 ? singular : pluralValue}`;
|
|
16926
|
+
}
|
|
16927
|
+
function statusText(status) {
|
|
16928
|
+
switch (status) {
|
|
16929
|
+
case "ready": return "ready";
|
|
16930
|
+
case "stored_only": return "stored-only";
|
|
16931
|
+
case "pending_dns": return "pending-dns";
|
|
16932
|
+
case "inactive": return "inactive";
|
|
16933
|
+
default: return String(status);
|
|
16934
|
+
}
|
|
16935
|
+
}
|
|
16936
|
+
function yesNo(value) {
|
|
16937
|
+
return value ? "yes" : "no";
|
|
16938
|
+
}
|
|
16939
|
+
function formatInboxDate(value) {
|
|
16940
|
+
if (!value) return "never";
|
|
16941
|
+
const d = new Date(value);
|
|
16942
|
+
if (Number.isNaN(d.getTime())) return value;
|
|
16943
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
16944
|
+
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())} UTC`;
|
|
16945
|
+
}
|
|
16946
|
+
function truncate(value, width) {
|
|
16947
|
+
if (value.length <= width) return value.padEnd(width);
|
|
16948
|
+
return `${value.slice(0, width - 3)}...`;
|
|
16949
|
+
}
|
|
16950
|
+
function domainSummary(domain) {
|
|
16951
|
+
switch (domain.status) {
|
|
16952
|
+
case "ready": return `${domain.domain} can receive mail and has ${plural(domain.processing_route_count, "processing route")}.`;
|
|
16953
|
+
case "stored_only": return `${domain.domain} can receive and store mail, but has no enabled processing route.`;
|
|
16954
|
+
case "pending_dns": return `${domain.domain} is waiting on DNS verification before it can receive mail.`;
|
|
16955
|
+
case "inactive": return `${domain.domain} is verified but inactive.`;
|
|
16956
|
+
default: return `${domain.domain} has status ${String(domain.status)}.`;
|
|
16957
|
+
}
|
|
16958
|
+
}
|
|
16959
|
+
function focusInboxStatus(status, domainName) {
|
|
16960
|
+
const normalized = domainName.toLowerCase();
|
|
16961
|
+
const domain = status.domains.find((entry) => entry.domain.toLowerCase() === normalized);
|
|
16962
|
+
if (!domain) throw new Errors.CLIError(`Domain ${domainName} was not found.`, { exit: 1 });
|
|
16963
|
+
return {
|
|
16964
|
+
...status,
|
|
16965
|
+
domains: [domain],
|
|
16966
|
+
ready: domain.receiving_ready && domain.processing_ready,
|
|
16967
|
+
receiving_ready: domain.receiving_ready,
|
|
16968
|
+
processing_ready: domain.processing_ready,
|
|
16969
|
+
summary: domainSummary(domain),
|
|
16970
|
+
recent_emails: {
|
|
16971
|
+
total: domain.email_count,
|
|
16972
|
+
latest_received_at: domain.latest_email_received_at
|
|
16973
|
+
}
|
|
16974
|
+
};
|
|
16975
|
+
}
|
|
16976
|
+
function formatDomainHeader() {
|
|
16977
|
+
return [
|
|
16978
|
+
"DOMAIN".padEnd(DOMAIN_DISPLAY_WIDTH),
|
|
16979
|
+
"STATUS".padEnd(STATUS_DISPLAY_WIDTH),
|
|
16980
|
+
"RECEIVE".padEnd(BOOL_DISPLAY_WIDTH),
|
|
16981
|
+
"PROCESS".padEnd(BOOL_DISPLAY_WIDTH),
|
|
16982
|
+
"EMAILS".padStart(NUM_DISPLAY_WIDTH),
|
|
16983
|
+
"ROUTES".padStart(NUM_DISPLAY_WIDTH)
|
|
16984
|
+
].join(" ");
|
|
16985
|
+
}
|
|
16986
|
+
function formatDomainRow(domain) {
|
|
16987
|
+
return [
|
|
16988
|
+
truncate(domain.domain, DOMAIN_DISPLAY_WIDTH),
|
|
16989
|
+
statusText(domain.status).padEnd(STATUS_DISPLAY_WIDTH),
|
|
16990
|
+
yesNo(domain.receiving_ready).padEnd(BOOL_DISPLAY_WIDTH),
|
|
16991
|
+
yesNo(domain.processing_ready).padEnd(BOOL_DISPLAY_WIDTH),
|
|
16992
|
+
String(domain.email_count).padStart(NUM_DISPLAY_WIDTH),
|
|
16993
|
+
String(domain.processing_route_count).padStart(NUM_DISPLAY_WIDTH)
|
|
16994
|
+
].join(" ");
|
|
16995
|
+
}
|
|
16996
|
+
function formatNextAction(action) {
|
|
16997
|
+
return action.command ? `- ${action.message}\n ${action.command}` : `- ${action.message}`;
|
|
16998
|
+
}
|
|
16999
|
+
function formatInboxStatus(status) {
|
|
17000
|
+
const lines = [
|
|
17001
|
+
status.summary,
|
|
17002
|
+
"",
|
|
17003
|
+
"Domains"
|
|
17004
|
+
];
|
|
17005
|
+
if (status.domains.length === 0) lines.push("No domains configured.");
|
|
17006
|
+
else {
|
|
17007
|
+
lines.push(formatDomainHeader());
|
|
17008
|
+
for (const domain of status.domains) lines.push(formatDomainRow(domain));
|
|
17009
|
+
}
|
|
17010
|
+
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)}`);
|
|
17011
|
+
if (status.next_actions.length > 0) {
|
|
17012
|
+
lines.push("", "Next actions");
|
|
17013
|
+
for (const action of status.next_actions) lines.push(formatNextAction(action));
|
|
17014
|
+
}
|
|
17015
|
+
return lines.join("\n");
|
|
17016
|
+
}
|
|
17017
|
+
var InboxStatusCommand = class InboxStatusCommand extends Command {
|
|
17018
|
+
static description = `Show consolidated inbound email readiness.
|
|
17019
|
+
|
|
17020
|
+
This checks the server-owned inbox status API instead of reconstructing readiness locally from separate domain, endpoint, function, and email lists. Use it before testing inbound email setup: it tells you whether mail can be received, whether anything will process it, and which next command is most useful.`;
|
|
17021
|
+
static summary = "Show inbound email readiness";
|
|
17022
|
+
static examples = [
|
|
17023
|
+
"<%= config.bin %> inbox status",
|
|
17024
|
+
"<%= config.bin %> inbox status --domain example.com",
|
|
17025
|
+
"<%= config.bin %> inbox status --json | jq '.data.next_actions'"
|
|
17026
|
+
];
|
|
17027
|
+
static flags = {
|
|
17028
|
+
"api-key": Flags.string({
|
|
17029
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17030
|
+
env: "PRIMITIVE_API_KEY"
|
|
17031
|
+
}),
|
|
17032
|
+
"api-base-url-1": Flags.string({
|
|
17033
|
+
description: API_BASE_URL_1_FLAG_DESCRIPTION,
|
|
17034
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
17035
|
+
hidden: true
|
|
17036
|
+
}),
|
|
17037
|
+
"api-base-url-2": Flags.string({
|
|
17038
|
+
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
17039
|
+
env: "PRIMITIVE_API_BASE_URL_2",
|
|
17040
|
+
hidden: true
|
|
17041
|
+
}),
|
|
17042
|
+
domain: Flags.string({ description: "Focus domain readiness and recent email fields on one domain returned by the inbox status API." }),
|
|
17043
|
+
json: Flags.boolean({ description: "Print the raw response envelope as JSON. With --domain, domain readiness and recent email fields are focused while endpoint, function, and next-action summaries remain account-level." }),
|
|
17044
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
17045
|
+
};
|
|
17046
|
+
async run() {
|
|
17047
|
+
const { flags } = await this.parse(InboxStatusCommand);
|
|
17048
|
+
await runWithTiming(flags.time, async () => {
|
|
17049
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
17050
|
+
apiKey: flags["api-key"],
|
|
17051
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
17052
|
+
apiBaseUrl2: flags["api-base-url-2"],
|
|
17053
|
+
configDir: this.config.configDir
|
|
17054
|
+
});
|
|
17055
|
+
const result = await getInboxStatus({
|
|
17056
|
+
client: apiClient.client,
|
|
17057
|
+
responseStyle: "fields"
|
|
17058
|
+
});
|
|
17059
|
+
if (result.error) {
|
|
17060
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
17061
|
+
writeErrorWithHints(errorPayload);
|
|
17062
|
+
surfaceUnauthorizedHint({
|
|
17063
|
+
auth,
|
|
17064
|
+
baseUrlOverridden,
|
|
17065
|
+
configDir: this.config.configDir,
|
|
17066
|
+
payload: errorPayload
|
|
17067
|
+
});
|
|
17068
|
+
process.exitCode = 1;
|
|
17069
|
+
return;
|
|
17070
|
+
}
|
|
17071
|
+
const envelope = result.data ?? {};
|
|
17072
|
+
const status = envelope.data;
|
|
17073
|
+
if (!status) throw new Errors.CLIError("Primitive API returned no inbox status.", { exit: 1 });
|
|
17074
|
+
const outputStatus = flags.domain ? focusInboxStatus(status, flags.domain) : status;
|
|
17075
|
+
if (flags.json) {
|
|
17076
|
+
this.log(JSON.stringify({
|
|
17077
|
+
...envelope,
|
|
17078
|
+
data: outputStatus
|
|
17079
|
+
}, null, 2));
|
|
17080
|
+
return;
|
|
17081
|
+
}
|
|
17082
|
+
this.log(formatInboxStatus(outputStatus));
|
|
17083
|
+
});
|
|
17084
|
+
}
|
|
17085
|
+
};
|
|
17086
|
+
//#endregion
|
|
15791
17087
|
//#region src/oclif/commands/login.ts
|
|
15792
17088
|
const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
|
|
15793
17089
|
function cliError$3(message) {
|
|
@@ -16291,12 +17587,46 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
16291
17587
|
}
|
|
16292
17588
|
};
|
|
16293
17589
|
//#endregion
|
|
17590
|
+
//#region src/oclif/attachments.ts
|
|
17591
|
+
function readAttachmentBytes(path, readFile) {
|
|
17592
|
+
try {
|
|
17593
|
+
return Buffer.from(readFile(path));
|
|
17594
|
+
} catch (error) {
|
|
17595
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
17596
|
+
throw new Errors.CLIError(`Could not read --attachment ${path}: ${detail}`, { exit: 1 });
|
|
17597
|
+
}
|
|
17598
|
+
}
|
|
17599
|
+
function hasControlCharacter(value) {
|
|
17600
|
+
return Array.from(value).some((character) => {
|
|
17601
|
+
const code = character.charCodeAt(0);
|
|
17602
|
+
return code <= 31 || code >= 127 && code <= 159;
|
|
17603
|
+
});
|
|
17604
|
+
}
|
|
17605
|
+
function validateAttachmentFilename(path, filename) {
|
|
17606
|
+
if (!filename) throw new Errors.CLIError(`Could not derive an attachment filename from ${path}. Pass a file path.`, { exit: 1 });
|
|
17607
|
+
if (hasControlCharacter(filename)) throw new Errors.CLIError(`Attachment filename ${filename} contains control characters.`, { exit: 1 });
|
|
17608
|
+
}
|
|
17609
|
+
function readAttachmentFiles(paths, readFile = readFileSync) {
|
|
17610
|
+
if (!paths || paths.length === 0) return void 0;
|
|
17611
|
+
return paths.map((path) => {
|
|
17612
|
+
const filename = basename(path);
|
|
17613
|
+
validateAttachmentFilename(path, filename);
|
|
17614
|
+
const bytes = readAttachmentBytes(path, readFile);
|
|
17615
|
+
if (bytes.length === 0) throw new Errors.CLIError(`Attachment file ${path} is empty. Attachments must contain at least one byte.`, { exit: 1 });
|
|
17616
|
+
return {
|
|
17617
|
+
content_base64: bytes.toString("base64"),
|
|
17618
|
+
filename
|
|
17619
|
+
};
|
|
17620
|
+
});
|
|
17621
|
+
}
|
|
17622
|
+
//#endregion
|
|
16294
17623
|
//#region src/oclif/commands/send.ts
|
|
16295
17624
|
var SendCommand = class SendCommand extends Command {
|
|
16296
17625
|
static description = `Send an outbound email. Agent-grade shortcut for \`sending send\` with sensible defaults.
|
|
16297
17626
|
|
|
16298
17627
|
--from defaults to agent@<your-first-verified-outbound-domain> when omitted.
|
|
16299
17628
|
--subject defaults to the first line of the body when omitted.
|
|
17629
|
+
--attachment attaches a file; repeat it to attach multiple files.
|
|
16300
17630
|
|
|
16301
17631
|
For the full flag set (custom message-id threading on the wire,
|
|
16302
17632
|
references arrays, etc.), use \`primitive sending send\`.`;
|
|
@@ -16304,6 +17634,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
16304
17634
|
static examples = [
|
|
16305
17635
|
"<%= config.bin %> send --to alice@example.com --body 'Hi Alice!'",
|
|
16306
17636
|
"<%= config.bin %> send --to alice@example.com --body-file ./message.txt",
|
|
17637
|
+
"<%= config.bin %> send --to alice@example.com --body 'See attached.' --attachment ./report.pdf",
|
|
16307
17638
|
"<%= config.bin %> send --to alice@example.com --from support@yourcompany.com --subject 'Quick question' --body 'Are you free Thursday?'",
|
|
16308
17639
|
"<%= config.bin %> send --to alice@example.com --html '<p>Hello!</p>'",
|
|
16309
17640
|
"<%= config.bin %> send --to alice@example.com --body 'Confirmed' --wait",
|
|
@@ -16331,11 +17662,15 @@ var SendCommand = class SendCommand extends Command {
|
|
|
16331
17662
|
from: Flags.string({ description: "Sender address. Defaults to agent@<your-first-verified-outbound-domain>." }),
|
|
16332
17663
|
subject: Flags.string({ description: "Subject line. Defaults to the first line of --body / --html when omitted." }),
|
|
16333
17664
|
body: Flags.string({ description: "Plain-text message body. Either --body or --html (or both) is required." }),
|
|
16334
|
-
"body-file": Flags.string({ description: "Read the plain-text message body from a UTF-8 file. Mutually exclusive with --body and --body-stdin." }),
|
|
17665
|
+
"body-file": Flags.string({ description: "Read the plain-text message body from a UTF-8 file. This does not attach the file; use --attachment for file attachments. Mutually exclusive with --body and --body-stdin." }),
|
|
16335
17666
|
"body-stdin": Flags.boolean({ description: "Read the plain-text message body from stdin. Mutually exclusive with --body and --body-file. Stdin can only be consumed once." }),
|
|
16336
17667
|
html: Flags.string({ description: "HTML message body. Either --body or --html (or both) is required." }),
|
|
16337
17668
|
"html-file": Flags.string({ description: "Read the HTML message body from a UTF-8 file. Mutually exclusive with --html and --html-stdin." }),
|
|
16338
17669
|
"html-stdin": Flags.boolean({ description: "Read the HTML message body from stdin. Mutually exclusive with --html and --html-file. Stdin can only be consumed once." }),
|
|
17670
|
+
attachment: Flags.string({
|
|
17671
|
+
description: "Attach a file to the email. Repeatable. Sends file bytes as a MIME attachment; use --body-file only for message body text.",
|
|
17672
|
+
multiple: true
|
|
17673
|
+
}),
|
|
16339
17674
|
"in-reply-to": Flags.string({ description: "Message-Id of the parent email when threading a reply on the wire. For replying to an inbound message you received, prefer `primitive reply --id <inbound-id>`." }),
|
|
16340
17675
|
wait: Flags.boolean({ description: "Block until the receiving MTA returns an outcome. Without --wait, the call returns once Primitive has accepted the message for delivery." }),
|
|
16341
17676
|
"wait-timeout-ms": Flags.integer({ description: "Maximum time to wait when --wait is set. Defaults to 30000ms." }),
|
|
@@ -16352,6 +17687,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
16352
17687
|
htmlStdin: flags["html-stdin"]
|
|
16353
17688
|
});
|
|
16354
17689
|
if (bodies.kind === "error") throw new Errors.CLIError(bodies.message);
|
|
17690
|
+
const attachments = readAttachmentFiles(flags.attachment);
|
|
16355
17691
|
await runWithTiming(flags.time, async () => {
|
|
16356
17692
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
16357
17693
|
apiKey: flags["api-key"],
|
|
@@ -16373,6 +17709,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
16373
17709
|
subject,
|
|
16374
17710
|
...bodies.body !== void 0 ? { body_text: bodies.body } : {},
|
|
16375
17711
|
...bodies.html !== void 0 ? { body_html: bodies.html } : {},
|
|
17712
|
+
...attachments !== void 0 ? { attachments } : {},
|
|
16376
17713
|
...flags["in-reply-to"] !== void 0 ? { in_reply_to: flags["in-reply-to"] } : {},
|
|
16377
17714
|
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
16378
17715
|
...flags["wait-timeout-ms"] !== void 0 ? { wait_timeout_ms: flags["wait-timeout-ms"] } : {}
|
|
@@ -17153,25 +18490,39 @@ var SigninOtpResendCommand = class SigninOtpResendCommand extends Command {
|
|
|
17153
18490
|
};
|
|
17154
18491
|
//#endregion
|
|
17155
18492
|
//#region src/oclif/commands/whoami.ts
|
|
18493
|
+
function formatWhoamiSummary(account) {
|
|
18494
|
+
return [
|
|
18495
|
+
`Authenticated as ${account.email}`,
|
|
18496
|
+
`Account id: ${account.id}`,
|
|
18497
|
+
`Plan: ${account.plan}`
|
|
18498
|
+
].join("\n");
|
|
18499
|
+
}
|
|
17156
18500
|
var WhoamiCommand = class WhoamiCommand extends Command {
|
|
17157
|
-
static description = `Print the account currently authenticated by saved OAuth credentials or an explicit API key. Useful as a credentials smoke test: confirms auth is live and shows which account it belongs to
|
|
18501
|
+
static description = `Print the account currently authenticated by saved OAuth credentials or an explicit API key. Useful as a credentials smoke test: confirms auth is live and shows which account it belongs to.
|
|
18502
|
+
|
|
18503
|
+
The default output is a concise human summary. Pass --json only when a script intentionally needs the full /account response.`;
|
|
17158
18504
|
static summary = "Print the authenticated account (credentials smoke test)";
|
|
17159
|
-
static examples = [
|
|
18505
|
+
static examples = [
|
|
18506
|
+
"<%= config.bin %> whoami",
|
|
18507
|
+
"<%= config.bin %> whoami --api-key prim_...",
|
|
18508
|
+
"<%= config.bin %> whoami --json | jq .id"
|
|
18509
|
+
];
|
|
17160
18510
|
static flags = {
|
|
17161
18511
|
"api-key": Flags.string({
|
|
17162
18512
|
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
17163
18513
|
env: "PRIMITIVE_API_KEY"
|
|
17164
18514
|
}),
|
|
17165
18515
|
"api-base-url-1": Flags.string({
|
|
17166
|
-
description:
|
|
18516
|
+
description: API_BASE_URL_1_FLAG_DESCRIPTION,
|
|
17167
18517
|
env: "PRIMITIVE_API_BASE_URL_1",
|
|
17168
18518
|
hidden: true
|
|
17169
18519
|
}),
|
|
17170
18520
|
"api-base-url-2": Flags.string({
|
|
17171
|
-
description:
|
|
18521
|
+
description: API_BASE_URL_2_FLAG_DESCRIPTION,
|
|
17172
18522
|
env: "PRIMITIVE_API_BASE_URL_2",
|
|
17173
18523
|
hidden: true
|
|
17174
18524
|
}),
|
|
18525
|
+
json: Flags.boolean({ description: "Print the full account JSON response. Default output hides setup and billing internals." }),
|
|
17175
18526
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
17176
18527
|
};
|
|
17177
18528
|
async run() {
|
|
@@ -17204,12 +18555,11 @@ var WhoamiCommand = class WhoamiCommand extends Command {
|
|
|
17204
18555
|
process.stderr.write("Server returned an empty account body; this should not happen for a valid key.\n");
|
|
17205
18556
|
throw new Errors.CLIError("unexpected empty response");
|
|
17206
18557
|
}
|
|
17207
|
-
|
|
17208
|
-
|
|
17209
|
-
|
|
17210
|
-
|
|
17211
|
-
|
|
17212
|
-
this.log(JSON.stringify(account, null, 2));
|
|
18558
|
+
if (flags.json) {
|
|
18559
|
+
this.log(JSON.stringify(account, null, 2));
|
|
18560
|
+
return;
|
|
18561
|
+
}
|
|
18562
|
+
this.log(formatWhoamiSummary(account));
|
|
17213
18563
|
});
|
|
17214
18564
|
}
|
|
17215
18565
|
};
|
|
@@ -17283,36 +18633,88 @@ function renderFishCompletion(binName) {
|
|
|
17283
18633
|
//#endregion
|
|
17284
18634
|
//#region src/oclif/index.ts
|
|
17285
18635
|
var ListOperationsCommand = class extends Command {
|
|
17286
|
-
static description = "List all generated API operations as JSON. Useful for piping to `jq` to discover available commands, their request/response schemas, and per-field descriptions. For inspecting a single operation in detail, prefer `primitive describe <command>`.";
|
|
18636
|
+
static description = "List all generated API operations as JSON. Useful for piping to `jq` to discover available commands, their request/response schemas, and per-field descriptions. For inspecting a single operation in detail, prefer `primitive describe <command-or-operation-name>`.";
|
|
17287
18637
|
static summary = "List all generated API operations (JSON)";
|
|
17288
18638
|
async run() {
|
|
17289
18639
|
this.log(JSON.stringify(operationManifest, null, 2));
|
|
17290
18640
|
}
|
|
17291
18641
|
};
|
|
18642
|
+
function operationId(operation) {
|
|
18643
|
+
return `${operation.tagCommand}:${operation.command}`;
|
|
18644
|
+
}
|
|
18645
|
+
function normalizeLookupToken(value) {
|
|
18646
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
18647
|
+
}
|
|
18648
|
+
function unique(values) {
|
|
18649
|
+
return [...new Set(values)];
|
|
18650
|
+
}
|
|
18651
|
+
function operationLookupTokens(operation) {
|
|
18652
|
+
return unique([
|
|
18653
|
+
operationId(operation),
|
|
18654
|
+
operation.command,
|
|
18655
|
+
operation.operationId,
|
|
18656
|
+
operation.sdkName,
|
|
18657
|
+
`${operation.tagCommand}:${operation.operationId}`,
|
|
18658
|
+
`${operation.tagCommand}:${operation.sdkName}`
|
|
18659
|
+
]);
|
|
18660
|
+
}
|
|
18661
|
+
function levenshteinDistance(left, right) {
|
|
18662
|
+
if (left === right) return 0;
|
|
18663
|
+
if (left.length === 0) return right.length;
|
|
18664
|
+
if (right.length === 0) return left.length;
|
|
18665
|
+
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
18666
|
+
for (let leftIndex = 0; leftIndex < left.length; leftIndex += 1) {
|
|
18667
|
+
const current = [leftIndex + 1];
|
|
18668
|
+
for (let rightIndex = 0; rightIndex < right.length; rightIndex += 1) {
|
|
18669
|
+
const substitutionCost = left[leftIndex] === right[rightIndex] ? 0 : 1;
|
|
18670
|
+
current[rightIndex + 1] = Math.min(current[rightIndex] + 1, previous[rightIndex + 1] + 1, previous[rightIndex] + substitutionCost);
|
|
18671
|
+
}
|
|
18672
|
+
previous = current;
|
|
18673
|
+
}
|
|
18674
|
+
return previous[right.length] ?? Number.POSITIVE_INFINITY;
|
|
18675
|
+
}
|
|
18676
|
+
function scoreLookupToken(query, token) {
|
|
18677
|
+
const normalizedQuery = normalizeLookupToken(query);
|
|
18678
|
+
const normalizedToken = normalizeLookupToken(token);
|
|
18679
|
+
if (!normalizedQuery || !normalizedToken) return 0;
|
|
18680
|
+
if (normalizedQuery === normalizedToken) return 100;
|
|
18681
|
+
if (normalizedToken.includes(normalizedQuery)) return Math.max(50, 90 - (normalizedToken.length - normalizedQuery.length));
|
|
18682
|
+
if (normalizedQuery.includes(normalizedToken)) return Math.max(45, 80 - (normalizedQuery.length - normalizedToken.length));
|
|
18683
|
+
const distance = levenshteinDistance(normalizedQuery, normalizedToken);
|
|
18684
|
+
const maxLength = Math.max(normalizedQuery.length, normalizedToken.length);
|
|
18685
|
+
return Math.round((1 - distance / maxLength) * 75);
|
|
18686
|
+
}
|
|
18687
|
+
function scoreOperation(query, operation) {
|
|
18688
|
+
return Math.max(...operationLookupTokens(operation).map((token) => scoreLookupToken(query, token)));
|
|
18689
|
+
}
|
|
17292
18690
|
function lookupOperation(id) {
|
|
17293
18691
|
const trimmed = resolveOperationAlias(id.trim());
|
|
17294
|
-
const
|
|
17295
|
-
const tag = sep === -1 ? "" : trimmed.slice(0, sep);
|
|
17296
|
-
const cmd = sep === -1 ? trimmed : trimmed.slice(sep + 1);
|
|
17297
|
-
const match = operationManifest.find((op) => op.command === cmd && op.tagCommand === tag) ?? null;
|
|
18692
|
+
const match = operationManifest.find((op) => operationLookupTokens(op).some((token) => token === trimmed || normalizeLookupToken(token) === normalizeLookupToken(trimmed))) ?? null;
|
|
17298
18693
|
if (match) return {
|
|
17299
18694
|
match,
|
|
17300
18695
|
candidates: []
|
|
17301
18696
|
};
|
|
17302
18697
|
return {
|
|
17303
18698
|
match: null,
|
|
17304
|
-
candidates: operationManifest.
|
|
18699
|
+
candidates: operationManifest.map((op) => ({
|
|
18700
|
+
id: operationId(op),
|
|
18701
|
+
score: scoreOperation(trimmed, op)
|
|
18702
|
+
})).filter(({ score }) => score >= 45).sort((left, right) => right.score - left.score || left.id.localeCompare(right.id)).slice(0, 5).map(({ id }) => id)
|
|
17305
18703
|
};
|
|
17306
18704
|
}
|
|
17307
18705
|
var DescribeCommand = class DescribeCommand extends Command {
|
|
17308
18706
|
static args = { command: Args.string({
|
|
17309
|
-
description: "Command id to describe, e.g. `emails:list
|
|
18707
|
+
description: "Command id, alias, or SDK operation name to describe, e.g. `emails:list`, `emails:get-email`, or `getEmail`. Run `primitive list-operations | jq -r '.[] | \"\\(.tagCommand):\\(.command) \\(.operationId)\"'` to enumerate generated operation ids.",
|
|
17310
18708
|
required: true
|
|
17311
18709
|
}) };
|
|
17312
18710
|
static description = `Print the full operation manifest entry for a single API command, including the path, request schema, response schema, and per-field descriptions sourced from the OpenAPI spec.
|
|
17313
18711
|
|
|
17314
18712
|
The manifest entry's \`responseSchema\` carries the inlined JSON Schema for the operation's 200/201 \`data\` envelope contents (\`$ref\`s resolved). Use it to look up what specific response fields mean. Examples:
|
|
17315
18713
|
|
|
18714
|
+
# Domain setup records returned by add/verify
|
|
18715
|
+
primitive describe domains:add
|
|
18716
|
+
primitive describe addDomain
|
|
18717
|
+
|
|
17316
18718
|
# Which of EmailDetail's sender-shaped fields is canonical?
|
|
17317
18719
|
primitive describe emails:get | jq '.responseSchema.properties | keys'
|
|
17318
18720
|
primitive describe emails:get | jq -r '.responseSchema.properties.from_email.description'
|
|
@@ -17322,7 +18724,12 @@ var DescribeCommand = class DescribeCommand extends Command {
|
|
|
17322
18724
|
|
|
17323
18725
|
\`requestSchema\` is the same shape for the request body when one exists. For a single field across many operations at once, use \`primitive list-operations | jq\` instead.`;
|
|
17324
18726
|
static summary = "Describe a single API operation in detail";
|
|
17325
|
-
static examples = [
|
|
18727
|
+
static examples = [
|
|
18728
|
+
"<%= config.bin %> describe addDomain",
|
|
18729
|
+
"<%= config.bin %> describe domains:add",
|
|
18730
|
+
"<%= config.bin %> describe emails:get",
|
|
18731
|
+
"<%= config.bin %> describe sent:get"
|
|
18732
|
+
];
|
|
17326
18733
|
async run() {
|
|
17327
18734
|
const { args } = await this.parse(DescribeCommand);
|
|
17328
18735
|
const { match, candidates } = lookupOperation(args.command);
|
|
@@ -17355,9 +18762,6 @@ var CompletionCommand = class CompletionCommand extends Command {
|
|
|
17355
18762
|
await this.config.runCommand("autocomplete", [args.shell]);
|
|
17356
18763
|
}
|
|
17357
18764
|
};
|
|
17358
|
-
function commandId(operation) {
|
|
17359
|
-
return `${operation.tagCommand}:${operation.command}`;
|
|
17360
|
-
}
|
|
17361
18765
|
const CANONICAL_OPERATION_ALIASES = {
|
|
17362
18766
|
"account:show": "account:get-account",
|
|
17363
18767
|
"account:storage": "account:get-storage-stats",
|
|
@@ -17402,14 +18806,19 @@ const CANONICAL_OPERATION_ALIASES = {
|
|
|
17402
18806
|
};
|
|
17403
18807
|
const DESCRIBE_OPERATION_ALIASES = {
|
|
17404
18808
|
...CANONICAL_OPERATION_ALIASES,
|
|
18809
|
+
"domains:zone-file": "domains:download-domain-zone-file",
|
|
17405
18810
|
"functions:logs": "functions:list-function-logs",
|
|
17406
18811
|
reply: "sending:reply-to-email"
|
|
17407
18812
|
};
|
|
17408
18813
|
function resolveOperationAlias(id) {
|
|
17409
18814
|
return DESCRIBE_OPERATION_ALIASES[id] ?? id;
|
|
17410
18815
|
}
|
|
17411
|
-
const OVERRIDDEN_OPERATION_IDS = new Set([
|
|
17412
|
-
|
|
18816
|
+
const OVERRIDDEN_OPERATION_IDS = new Set([
|
|
18817
|
+
"domains:download-domain-zone-file",
|
|
18818
|
+
"functions:test-function",
|
|
18819
|
+
"inbox:get-inbox-status"
|
|
18820
|
+
]);
|
|
18821
|
+
const generatedCommands = Object.fromEntries(operationManifest.filter((operation) => !OVERRIDDEN_OPERATION_IDS.has(operationId(operation))).map((operation) => [operationId(operation), createOperationCommand(operation)]));
|
|
17413
18822
|
const COMMANDS = {
|
|
17414
18823
|
completion: CompletionCommand,
|
|
17415
18824
|
"list-operations": ListOperationsCommand,
|
|
@@ -17438,6 +18847,10 @@ const COMMANDS = {
|
|
|
17438
18847
|
"emails:latest": EmailsLatestCommand,
|
|
17439
18848
|
"emails:watch": EmailsWatchCommand,
|
|
17440
18849
|
"emails:wait": EmailsWaitCommand,
|
|
18850
|
+
"domains:zone-file": DomainsZoneFileCommand,
|
|
18851
|
+
"domains:download-domain-zone-file": DomainsZoneFileCommand,
|
|
18852
|
+
"inbox:status": InboxStatusCommand,
|
|
18853
|
+
"inbox:get-inbox-status": InboxStatusCommand,
|
|
17441
18854
|
"functions:init": FunctionsInitCommand,
|
|
17442
18855
|
"functions:templates": FunctionsTemplatesCommand,
|
|
17443
18856
|
"functions:deploy": FunctionsDeployCommand,
|