@primitivedotdev/cli 0.31.2 → 0.31.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/oclif/index.js +1124 -120
- package/package.json +5 -2
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,6 +645,7 @@ 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,
|
|
@@ -941,9 +942,11 @@ const listDomains = (options) => (options?.client ?? client).get({
|
|
|
941
942
|
/**
|
|
942
943
|
* Claim a new domain
|
|
943
944
|
*
|
|
944
|
-
* Creates an unverified domain claim
|
|
945
|
-
*
|
|
946
|
-
* calling the verify endpoint.
|
|
945
|
+
* Creates an unverified domain claim and returns the exact
|
|
946
|
+
* DNS records to publish in `dns_records`. Publish those
|
|
947
|
+
* records before calling the verify endpoint. To give users
|
|
948
|
+
* an importable DNS file, call `downloadDomainZoneFile` or run
|
|
949
|
+
* `primitive domains zone-file --id <domain-id>`.
|
|
947
950
|
*
|
|
948
951
|
*/
|
|
949
952
|
const addDomain = (options) => (options.client ?? client).post({
|
|
@@ -993,9 +996,15 @@ const updateDomain = (options) => (options.client ?? client).patch({
|
|
|
993
996
|
/**
|
|
994
997
|
* Verify domain ownership
|
|
995
998
|
*
|
|
996
|
-
* Checks DNS records
|
|
999
|
+
* Checks DNS records required for inbound routing, ownership,
|
|
1000
|
+
* and outbound authentication: MX, ownership TXT, SPF, DKIM,
|
|
1001
|
+
* DMARC, and TLS-RPT.
|
|
997
1002
|
* On success, the domain is promoted from unverified to verified.
|
|
998
|
-
* On failure, returns which checks passed and which failed
|
|
1003
|
+
* On failure, returns which checks passed and which failed,
|
|
1004
|
+
* plus the exact DNS records still expected. To give users
|
|
1005
|
+
* an importable DNS file for missing records, call
|
|
1006
|
+
* `downloadDomainZoneFile` or run
|
|
1007
|
+
* `primitive domains zone-file --id <domain-id>`.
|
|
999
1008
|
*
|
|
1000
1009
|
*/
|
|
1001
1010
|
const verifyDomain = (options) => (options.client ?? client).post({
|
|
@@ -1007,6 +1016,23 @@ const verifyDomain = (options) => (options.client ?? client).post({
|
|
|
1007
1016
|
...options
|
|
1008
1017
|
});
|
|
1009
1018
|
/**
|
|
1019
|
+
* Download domain DNS zone file
|
|
1020
|
+
*
|
|
1021
|
+
* Downloads a BIND-format DNS zone file containing the DNS records
|
|
1022
|
+
* required for a domain claim. Agents should offer this after
|
|
1023
|
+
* `addDomain` when users want to import DNS records instead of
|
|
1024
|
+
* copying each record manually.
|
|
1025
|
+
*
|
|
1026
|
+
*/
|
|
1027
|
+
const downloadDomainZoneFile = (options) => (options.client ?? client).get({
|
|
1028
|
+
security: [{
|
|
1029
|
+
scheme: "bearer",
|
|
1030
|
+
type: "http"
|
|
1031
|
+
}],
|
|
1032
|
+
url: "/domains/{id}/zone-file",
|
|
1033
|
+
...options
|
|
1034
|
+
});
|
|
1035
|
+
/**
|
|
1010
1036
|
* List inbound emails
|
|
1011
1037
|
*
|
|
1012
1038
|
* Returns a paginated list of INBOUND emails received at your
|
|
@@ -2333,7 +2359,7 @@ const openapiDocument = {
|
|
|
2333
2359
|
"post": {
|
|
2334
2360
|
"operationId": "addDomain",
|
|
2335
2361
|
"summary": "Claim a new domain",
|
|
2336
|
-
"description": "Creates an unverified domain claim
|
|
2362
|
+
"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
2363
|
"tags": ["Domains"],
|
|
2338
2364
|
"requestBody": {
|
|
2339
2365
|
"required": true,
|
|
@@ -2426,7 +2452,7 @@ const openapiDocument = {
|
|
|
2426
2452
|
"post": {
|
|
2427
2453
|
"operationId": "verifyDomain",
|
|
2428
2454
|
"summary": "Verify domain ownership",
|
|
2429
|
-
"description": "Checks DNS records
|
|
2455
|
+
"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
2456
|
"tags": ["Domains"],
|
|
2431
2457
|
"responses": {
|
|
2432
2458
|
"200": {
|
|
@@ -2442,6 +2468,38 @@ const openapiDocument = {
|
|
|
2442
2468
|
}
|
|
2443
2469
|
}
|
|
2444
2470
|
},
|
|
2471
|
+
"/domains/{id}/zone-file": {
|
|
2472
|
+
"parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
|
|
2473
|
+
"get": {
|
|
2474
|
+
"operationId": "downloadDomainZoneFile",
|
|
2475
|
+
"summary": "Download domain DNS zone file",
|
|
2476
|
+
"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",
|
|
2477
|
+
"tags": ["Domains"],
|
|
2478
|
+
"parameters": [{
|
|
2479
|
+
"name": "outbound_only",
|
|
2480
|
+
"in": "query",
|
|
2481
|
+
"schema": { "type": "boolean" },
|
|
2482
|
+
"description": "When true, include only outbound DNS records. Verified domains\ndefault to outbound-only; pending claims default to all required\nrecords.\n"
|
|
2483
|
+
}],
|
|
2484
|
+
"responses": {
|
|
2485
|
+
"200": {
|
|
2486
|
+
"description": "BIND-format zone file",
|
|
2487
|
+
"content": { "text/plain": { "schema": {
|
|
2488
|
+
"type": "string",
|
|
2489
|
+
"format": "binary"
|
|
2490
|
+
} } },
|
|
2491
|
+
"headers": { "Content-Disposition": { "schema": {
|
|
2492
|
+
"type": "string",
|
|
2493
|
+
"example": "attachment; filename=\"example.com.zone\""
|
|
2494
|
+
} } }
|
|
2495
|
+
},
|
|
2496
|
+
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
2497
|
+
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
2498
|
+
"404": { "$ref": "#/components/responses/NotFound" },
|
|
2499
|
+
"429": { "$ref": "#/components/responses/RateLimited" }
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
},
|
|
2445
2503
|
"/emails": { "get": {
|
|
2446
2504
|
"operationId": "listEmails",
|
|
2447
2505
|
"summary": "List inbound emails",
|
|
@@ -4731,7 +4789,7 @@ const openapiDocument = {
|
|
|
4731
4789
|
"required": ["secret"]
|
|
4732
4790
|
},
|
|
4733
4791
|
"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
|
|
4792
|
+
"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
4793
|
"oneOf": [{ "$ref": "#/components/schemas/VerifiedDomain" }, { "$ref": "#/components/schemas/UnverifiedDomain" }]
|
|
4736
4794
|
},
|
|
4737
4795
|
"VerifiedDomain": {
|
|
@@ -4771,6 +4829,74 @@ const openapiDocument = {
|
|
|
4771
4829
|
"created_at"
|
|
4772
4830
|
]
|
|
4773
4831
|
},
|
|
4832
|
+
"DomainDnsRecord": {
|
|
4833
|
+
"type": "object",
|
|
4834
|
+
"additionalProperties": false,
|
|
4835
|
+
"properties": {
|
|
4836
|
+
"type": {
|
|
4837
|
+
"type": "string",
|
|
4838
|
+
"enum": ["MX", "TXT"],
|
|
4839
|
+
"description": "DNS record type."
|
|
4840
|
+
},
|
|
4841
|
+
"name": {
|
|
4842
|
+
"type": "string",
|
|
4843
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
4844
|
+
},
|
|
4845
|
+
"fqdn": {
|
|
4846
|
+
"type": "string",
|
|
4847
|
+
"description": "Fully-qualified DNS record name."
|
|
4848
|
+
},
|
|
4849
|
+
"value": {
|
|
4850
|
+
"type": "string",
|
|
4851
|
+
"description": "Exact value to publish."
|
|
4852
|
+
},
|
|
4853
|
+
"priority": {
|
|
4854
|
+
"type": "integer",
|
|
4855
|
+
"description": "MX priority. Present only for MX records."
|
|
4856
|
+
},
|
|
4857
|
+
"ttl": {
|
|
4858
|
+
"type": "integer",
|
|
4859
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
4860
|
+
},
|
|
4861
|
+
"required": {
|
|
4862
|
+
"type": "boolean",
|
|
4863
|
+
"const": true
|
|
4864
|
+
},
|
|
4865
|
+
"purpose": {
|
|
4866
|
+
"type": "string",
|
|
4867
|
+
"enum": [
|
|
4868
|
+
"inbound_mx",
|
|
4869
|
+
"ownership_verification",
|
|
4870
|
+
"spf",
|
|
4871
|
+
"dkim",
|
|
4872
|
+
"dmarc",
|
|
4873
|
+
"tls_reporting"
|
|
4874
|
+
]
|
|
4875
|
+
},
|
|
4876
|
+
"status": {
|
|
4877
|
+
"type": "string",
|
|
4878
|
+
"enum": [
|
|
4879
|
+
"pending",
|
|
4880
|
+
"found",
|
|
4881
|
+
"missing",
|
|
4882
|
+
"incorrect"
|
|
4883
|
+
]
|
|
4884
|
+
},
|
|
4885
|
+
"message": {
|
|
4886
|
+
"type": "string",
|
|
4887
|
+
"description": "Short explanation of why this record is needed."
|
|
4888
|
+
}
|
|
4889
|
+
},
|
|
4890
|
+
"required": [
|
|
4891
|
+
"type",
|
|
4892
|
+
"name",
|
|
4893
|
+
"fqdn",
|
|
4894
|
+
"value",
|
|
4895
|
+
"required",
|
|
4896
|
+
"purpose",
|
|
4897
|
+
"status"
|
|
4898
|
+
]
|
|
4899
|
+
},
|
|
4774
4900
|
"UnverifiedDomain": {
|
|
4775
4901
|
"type": "object",
|
|
4776
4902
|
"properties": {
|
|
@@ -4791,6 +4917,11 @@ const openapiDocument = {
|
|
|
4791
4917
|
"type": "string",
|
|
4792
4918
|
"description": "Add this value as a TXT record to verify ownership"
|
|
4793
4919
|
},
|
|
4920
|
+
"dns_records": {
|
|
4921
|
+
"type": "array",
|
|
4922
|
+
"description": "Exact DNS records to publish for this pending domain claim.",
|
|
4923
|
+
"items": { "$ref": "#/components/schemas/DomainDnsRecord" }
|
|
4924
|
+
},
|
|
4794
4925
|
"created_at": {
|
|
4795
4926
|
"type": "string",
|
|
4796
4927
|
"format": "date-time"
|
|
@@ -4808,12 +4939,23 @@ const openapiDocument = {
|
|
|
4808
4939
|
"AddDomainInput": {
|
|
4809
4940
|
"type": "object",
|
|
4810
4941
|
"additionalProperties": false,
|
|
4811
|
-
"properties": {
|
|
4812
|
-
"
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4942
|
+
"properties": {
|
|
4943
|
+
"domain": {
|
|
4944
|
+
"type": "string",
|
|
4945
|
+
"minLength": 1,
|
|
4946
|
+
"maxLength": 253,
|
|
4947
|
+
"description": "The domain name to claim (e.g. \"example.com\")"
|
|
4948
|
+
},
|
|
4949
|
+
"confirmed": {
|
|
4950
|
+
"type": "boolean",
|
|
4951
|
+
"description": "Set to true to confirm replacing an existing mailbox provider after an mx_conflict response."
|
|
4952
|
+
},
|
|
4953
|
+
"outbound": {
|
|
4954
|
+
"type": "boolean",
|
|
4955
|
+
"deprecated": true,
|
|
4956
|
+
"description": "Deprecated and ignored. Outbound DNS is provisioned for every new domain claim."
|
|
4957
|
+
}
|
|
4958
|
+
},
|
|
4817
4959
|
"required": ["domain"]
|
|
4818
4960
|
},
|
|
4819
4961
|
"UpdateDomainInput": {
|
|
@@ -4835,10 +4977,17 @@ const openapiDocument = {
|
|
|
4835
4977
|
},
|
|
4836
4978
|
"DomainVerifyResult": { "oneOf": [{
|
|
4837
4979
|
"type": "object",
|
|
4838
|
-
"properties": {
|
|
4839
|
-
"
|
|
4840
|
-
|
|
4841
|
-
|
|
4980
|
+
"properties": {
|
|
4981
|
+
"verified": {
|
|
4982
|
+
"type": "boolean",
|
|
4983
|
+
"const": true
|
|
4984
|
+
},
|
|
4985
|
+
"dns_records": {
|
|
4986
|
+
"type": "array",
|
|
4987
|
+
"description": "Exact DNS records checked for this verification attempt.",
|
|
4988
|
+
"items": { "$ref": "#/components/schemas/DomainDnsRecord" }
|
|
4989
|
+
}
|
|
4990
|
+
},
|
|
4842
4991
|
"required": ["verified"]
|
|
4843
4992
|
}, {
|
|
4844
4993
|
"type": "object",
|
|
@@ -4855,6 +5004,27 @@ const openapiDocument = {
|
|
|
4855
5004
|
"type": "boolean",
|
|
4856
5005
|
"description": "Whether the TXT verification record was found"
|
|
4857
5006
|
},
|
|
5007
|
+
"spfFound": {
|
|
5008
|
+
"type": "boolean",
|
|
5009
|
+
"description": "Whether the SPF record includes Primitive."
|
|
5010
|
+
},
|
|
5011
|
+
"dkimFound": {
|
|
5012
|
+
"type": "boolean",
|
|
5013
|
+
"description": "Whether the DKIM public key record was found."
|
|
5014
|
+
},
|
|
5015
|
+
"dmarcFound": {
|
|
5016
|
+
"type": "boolean",
|
|
5017
|
+
"description": "Whether the DMARC record was found."
|
|
5018
|
+
},
|
|
5019
|
+
"tlsRptFound": {
|
|
5020
|
+
"type": "boolean",
|
|
5021
|
+
"description": "Whether the TLS-RPT record was found."
|
|
5022
|
+
},
|
|
5023
|
+
"dns_records": {
|
|
5024
|
+
"type": "array",
|
|
5025
|
+
"description": "Exact DNS records checked for this verification attempt.",
|
|
5026
|
+
"items": { "$ref": "#/components/schemas/DomainDnsRecord" }
|
|
5027
|
+
},
|
|
4858
5028
|
"error": {
|
|
4859
5029
|
"type": "string",
|
|
4860
5030
|
"description": "Human-readable verification failure reason"
|
|
@@ -5167,6 +5337,31 @@ const openapiDocument = {
|
|
|
5167
5337
|
"created_at"
|
|
5168
5338
|
]
|
|
5169
5339
|
},
|
|
5340
|
+
"SendMailAttachment": {
|
|
5341
|
+
"type": "object",
|
|
5342
|
+
"additionalProperties": false,
|
|
5343
|
+
"properties": {
|
|
5344
|
+
"filename": {
|
|
5345
|
+
"type": "string",
|
|
5346
|
+
"minLength": 1,
|
|
5347
|
+
"maxLength": 255,
|
|
5348
|
+
"description": "Attachment filename. Control characters are rejected."
|
|
5349
|
+
},
|
|
5350
|
+
"content_type": {
|
|
5351
|
+
"type": "string",
|
|
5352
|
+
"minLength": 1,
|
|
5353
|
+
"maxLength": 255,
|
|
5354
|
+
"description": "Optional MIME content type. Control characters are rejected."
|
|
5355
|
+
},
|
|
5356
|
+
"content_base64": {
|
|
5357
|
+
"type": "string",
|
|
5358
|
+
"minLength": 1,
|
|
5359
|
+
"maxLength": 44040192,
|
|
5360
|
+
"description": "Base64-encoded attachment bytes."
|
|
5361
|
+
}
|
|
5362
|
+
},
|
|
5363
|
+
"required": ["filename", "content_base64"]
|
|
5364
|
+
},
|
|
5170
5365
|
"SendMailInput": {
|
|
5171
5366
|
"type": "object",
|
|
5172
5367
|
"additionalProperties": false,
|
|
@@ -5215,6 +5410,12 @@ const openapiDocument = {
|
|
|
5215
5410
|
"pattern": "^[^\\x00-\\x1F\\x7F]+$"
|
|
5216
5411
|
}
|
|
5217
5412
|
},
|
|
5413
|
+
"attachments": {
|
|
5414
|
+
"type": "array",
|
|
5415
|
+
"maxItems": 100,
|
|
5416
|
+
"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.",
|
|
5417
|
+
"items": { "$ref": "#/components/schemas/SendMailAttachment" }
|
|
5418
|
+
},
|
|
5218
5419
|
"wait": {
|
|
5219
5420
|
"type": "boolean",
|
|
5220
5421
|
"description": "When true, wait for the first downstream SMTP delivery outcome before returning."
|
|
@@ -7502,7 +7703,7 @@ const operationManifest = [
|
|
|
7502
7703
|
"binaryResponse": false,
|
|
7503
7704
|
"bodyRequired": true,
|
|
7504
7705
|
"command": "add-domain",
|
|
7505
|
-
"description": "Creates an unverified domain claim
|
|
7706
|
+
"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
7707
|
"hasJsonBody": true,
|
|
7507
7708
|
"method": "POST",
|
|
7508
7709
|
"operationId": "addDomain",
|
|
@@ -7512,12 +7713,23 @@ const operationManifest = [
|
|
|
7512
7713
|
"requestSchema": {
|
|
7513
7714
|
"type": "object",
|
|
7514
7715
|
"additionalProperties": false,
|
|
7515
|
-
"properties": {
|
|
7516
|
-
"
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7716
|
+
"properties": {
|
|
7717
|
+
"domain": {
|
|
7718
|
+
"type": "string",
|
|
7719
|
+
"minLength": 1,
|
|
7720
|
+
"maxLength": 253,
|
|
7721
|
+
"description": "The domain name to claim (e.g. \"example.com\")"
|
|
7722
|
+
},
|
|
7723
|
+
"confirmed": {
|
|
7724
|
+
"type": "boolean",
|
|
7725
|
+
"description": "Set to true to confirm replacing an existing mailbox provider after an mx_conflict response."
|
|
7726
|
+
},
|
|
7727
|
+
"outbound": {
|
|
7728
|
+
"type": "boolean",
|
|
7729
|
+
"deprecated": true,
|
|
7730
|
+
"description": "Deprecated and ignored. Outbound DNS is provisioned for every new domain claim."
|
|
7731
|
+
}
|
|
7732
|
+
},
|
|
7521
7733
|
"required": ["domain"]
|
|
7522
7734
|
},
|
|
7523
7735
|
"responseSchema": {
|
|
@@ -7540,6 +7752,78 @@ const operationManifest = [
|
|
|
7540
7752
|
"type": "string",
|
|
7541
7753
|
"description": "Add this value as a TXT record to verify ownership"
|
|
7542
7754
|
},
|
|
7755
|
+
"dns_records": {
|
|
7756
|
+
"type": "array",
|
|
7757
|
+
"description": "Exact DNS records to publish for this pending domain claim.",
|
|
7758
|
+
"items": {
|
|
7759
|
+
"type": "object",
|
|
7760
|
+
"additionalProperties": false,
|
|
7761
|
+
"properties": {
|
|
7762
|
+
"type": {
|
|
7763
|
+
"type": "string",
|
|
7764
|
+
"enum": ["MX", "TXT"],
|
|
7765
|
+
"description": "DNS record type."
|
|
7766
|
+
},
|
|
7767
|
+
"name": {
|
|
7768
|
+
"type": "string",
|
|
7769
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
7770
|
+
},
|
|
7771
|
+
"fqdn": {
|
|
7772
|
+
"type": "string",
|
|
7773
|
+
"description": "Fully-qualified DNS record name."
|
|
7774
|
+
},
|
|
7775
|
+
"value": {
|
|
7776
|
+
"type": "string",
|
|
7777
|
+
"description": "Exact value to publish."
|
|
7778
|
+
},
|
|
7779
|
+
"priority": {
|
|
7780
|
+
"type": "integer",
|
|
7781
|
+
"description": "MX priority. Present only for MX records."
|
|
7782
|
+
},
|
|
7783
|
+
"ttl": {
|
|
7784
|
+
"type": "integer",
|
|
7785
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
7786
|
+
},
|
|
7787
|
+
"required": {
|
|
7788
|
+
"type": "boolean",
|
|
7789
|
+
"const": true
|
|
7790
|
+
},
|
|
7791
|
+
"purpose": {
|
|
7792
|
+
"type": "string",
|
|
7793
|
+
"enum": [
|
|
7794
|
+
"inbound_mx",
|
|
7795
|
+
"ownership_verification",
|
|
7796
|
+
"spf",
|
|
7797
|
+
"dkim",
|
|
7798
|
+
"dmarc",
|
|
7799
|
+
"tls_reporting"
|
|
7800
|
+
]
|
|
7801
|
+
},
|
|
7802
|
+
"status": {
|
|
7803
|
+
"type": "string",
|
|
7804
|
+
"enum": [
|
|
7805
|
+
"pending",
|
|
7806
|
+
"found",
|
|
7807
|
+
"missing",
|
|
7808
|
+
"incorrect"
|
|
7809
|
+
]
|
|
7810
|
+
},
|
|
7811
|
+
"message": {
|
|
7812
|
+
"type": "string",
|
|
7813
|
+
"description": "Short explanation of why this record is needed."
|
|
7814
|
+
}
|
|
7815
|
+
},
|
|
7816
|
+
"required": [
|
|
7817
|
+
"type",
|
|
7818
|
+
"name",
|
|
7819
|
+
"fqdn",
|
|
7820
|
+
"value",
|
|
7821
|
+
"required",
|
|
7822
|
+
"purpose",
|
|
7823
|
+
"status"
|
|
7824
|
+
]
|
|
7825
|
+
}
|
|
7826
|
+
},
|
|
7543
7827
|
"created_at": {
|
|
7544
7828
|
"type": "string",
|
|
7545
7829
|
"format": "date-time"
|
|
@@ -7583,6 +7867,36 @@ const operationManifest = [
|
|
|
7583
7867
|
"tag": "Domains",
|
|
7584
7868
|
"tagCommand": "domains"
|
|
7585
7869
|
},
|
|
7870
|
+
{
|
|
7871
|
+
"binaryResponse": true,
|
|
7872
|
+
"bodyRequired": false,
|
|
7873
|
+
"command": "download-domain-zone-file",
|
|
7874
|
+
"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",
|
|
7875
|
+
"hasJsonBody": false,
|
|
7876
|
+
"method": "GET",
|
|
7877
|
+
"operationId": "downloadDomainZoneFile",
|
|
7878
|
+
"path": "/domains/{id}/zone-file",
|
|
7879
|
+
"pathParams": [{
|
|
7880
|
+
"description": "Resource UUID",
|
|
7881
|
+
"enum": null,
|
|
7882
|
+
"name": "id",
|
|
7883
|
+
"required": true,
|
|
7884
|
+
"type": "string"
|
|
7885
|
+
}],
|
|
7886
|
+
"queryParams": [{
|
|
7887
|
+
"description": "When true, include only outbound DNS records. Verified domains\ndefault to outbound-only; pending claims default to all required\nrecords.\n",
|
|
7888
|
+
"enum": null,
|
|
7889
|
+
"name": "outbound_only",
|
|
7890
|
+
"required": false,
|
|
7891
|
+
"type": "boolean"
|
|
7892
|
+
}],
|
|
7893
|
+
"requestSchema": null,
|
|
7894
|
+
"responseSchema": null,
|
|
7895
|
+
"sdkName": "downloadDomainZoneFile",
|
|
7896
|
+
"summary": "Download domain DNS zone file",
|
|
7897
|
+
"tag": "Domains",
|
|
7898
|
+
"tagCommand": "domains"
|
|
7899
|
+
},
|
|
7586
7900
|
{
|
|
7587
7901
|
"binaryResponse": false,
|
|
7588
7902
|
"bodyRequired": false,
|
|
@@ -7598,7 +7912,7 @@ const operationManifest = [
|
|
|
7598
7912
|
"responseSchema": {
|
|
7599
7913
|
"type": "array",
|
|
7600
7914
|
"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
|
|
7915
|
+
"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
7916
|
"oneOf": [{
|
|
7603
7917
|
"type": "object",
|
|
7604
7918
|
"properties": {
|
|
@@ -7655,6 +7969,78 @@ const operationManifest = [
|
|
|
7655
7969
|
"type": "string",
|
|
7656
7970
|
"description": "Add this value as a TXT record to verify ownership"
|
|
7657
7971
|
},
|
|
7972
|
+
"dns_records": {
|
|
7973
|
+
"type": "array",
|
|
7974
|
+
"description": "Exact DNS records to publish for this pending domain claim.",
|
|
7975
|
+
"items": {
|
|
7976
|
+
"type": "object",
|
|
7977
|
+
"additionalProperties": false,
|
|
7978
|
+
"properties": {
|
|
7979
|
+
"type": {
|
|
7980
|
+
"type": "string",
|
|
7981
|
+
"enum": ["MX", "TXT"],
|
|
7982
|
+
"description": "DNS record type."
|
|
7983
|
+
},
|
|
7984
|
+
"name": {
|
|
7985
|
+
"type": "string",
|
|
7986
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
7987
|
+
},
|
|
7988
|
+
"fqdn": {
|
|
7989
|
+
"type": "string",
|
|
7990
|
+
"description": "Fully-qualified DNS record name."
|
|
7991
|
+
},
|
|
7992
|
+
"value": {
|
|
7993
|
+
"type": "string",
|
|
7994
|
+
"description": "Exact value to publish."
|
|
7995
|
+
},
|
|
7996
|
+
"priority": {
|
|
7997
|
+
"type": "integer",
|
|
7998
|
+
"description": "MX priority. Present only for MX records."
|
|
7999
|
+
},
|
|
8000
|
+
"ttl": {
|
|
8001
|
+
"type": "integer",
|
|
8002
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
8003
|
+
},
|
|
8004
|
+
"required": {
|
|
8005
|
+
"type": "boolean",
|
|
8006
|
+
"const": true
|
|
8007
|
+
},
|
|
8008
|
+
"purpose": {
|
|
8009
|
+
"type": "string",
|
|
8010
|
+
"enum": [
|
|
8011
|
+
"inbound_mx",
|
|
8012
|
+
"ownership_verification",
|
|
8013
|
+
"spf",
|
|
8014
|
+
"dkim",
|
|
8015
|
+
"dmarc",
|
|
8016
|
+
"tls_reporting"
|
|
8017
|
+
]
|
|
8018
|
+
},
|
|
8019
|
+
"status": {
|
|
8020
|
+
"type": "string",
|
|
8021
|
+
"enum": [
|
|
8022
|
+
"pending",
|
|
8023
|
+
"found",
|
|
8024
|
+
"missing",
|
|
8025
|
+
"incorrect"
|
|
8026
|
+
]
|
|
8027
|
+
},
|
|
8028
|
+
"message": {
|
|
8029
|
+
"type": "string",
|
|
8030
|
+
"description": "Short explanation of why this record is needed."
|
|
8031
|
+
}
|
|
8032
|
+
},
|
|
8033
|
+
"required": [
|
|
8034
|
+
"type",
|
|
8035
|
+
"name",
|
|
8036
|
+
"fqdn",
|
|
8037
|
+
"value",
|
|
8038
|
+
"required",
|
|
8039
|
+
"purpose",
|
|
8040
|
+
"status"
|
|
8041
|
+
]
|
|
8042
|
+
}
|
|
8043
|
+
},
|
|
7658
8044
|
"created_at": {
|
|
7659
8045
|
"type": "string",
|
|
7660
8046
|
"format": "date-time"
|
|
@@ -7756,7 +8142,7 @@ const operationManifest = [
|
|
|
7756
8142
|
"binaryResponse": false,
|
|
7757
8143
|
"bodyRequired": false,
|
|
7758
8144
|
"command": "verify-domain",
|
|
7759
|
-
"description": "Checks DNS records
|
|
8145
|
+
"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
8146
|
"hasJsonBody": false,
|
|
7761
8147
|
"method": "POST",
|
|
7762
8148
|
"operationId": "verifyDomain",
|
|
@@ -7772,10 +8158,84 @@ const operationManifest = [
|
|
|
7772
8158
|
"requestSchema": null,
|
|
7773
8159
|
"responseSchema": { "oneOf": [{
|
|
7774
8160
|
"type": "object",
|
|
7775
|
-
"properties": {
|
|
7776
|
-
"
|
|
7777
|
-
|
|
7778
|
-
|
|
8161
|
+
"properties": {
|
|
8162
|
+
"verified": {
|
|
8163
|
+
"type": "boolean",
|
|
8164
|
+
"const": true
|
|
8165
|
+
},
|
|
8166
|
+
"dns_records": {
|
|
8167
|
+
"type": "array",
|
|
8168
|
+
"description": "Exact DNS records checked for this verification attempt.",
|
|
8169
|
+
"items": {
|
|
8170
|
+
"type": "object",
|
|
8171
|
+
"additionalProperties": false,
|
|
8172
|
+
"properties": {
|
|
8173
|
+
"type": {
|
|
8174
|
+
"type": "string",
|
|
8175
|
+
"enum": ["MX", "TXT"],
|
|
8176
|
+
"description": "DNS record type."
|
|
8177
|
+
},
|
|
8178
|
+
"name": {
|
|
8179
|
+
"type": "string",
|
|
8180
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
8181
|
+
},
|
|
8182
|
+
"fqdn": {
|
|
8183
|
+
"type": "string",
|
|
8184
|
+
"description": "Fully-qualified DNS record name."
|
|
8185
|
+
},
|
|
8186
|
+
"value": {
|
|
8187
|
+
"type": "string",
|
|
8188
|
+
"description": "Exact value to publish."
|
|
8189
|
+
},
|
|
8190
|
+
"priority": {
|
|
8191
|
+
"type": "integer",
|
|
8192
|
+
"description": "MX priority. Present only for MX records."
|
|
8193
|
+
},
|
|
8194
|
+
"ttl": {
|
|
8195
|
+
"type": "integer",
|
|
8196
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
8197
|
+
},
|
|
8198
|
+
"required": {
|
|
8199
|
+
"type": "boolean",
|
|
8200
|
+
"const": true
|
|
8201
|
+
},
|
|
8202
|
+
"purpose": {
|
|
8203
|
+
"type": "string",
|
|
8204
|
+
"enum": [
|
|
8205
|
+
"inbound_mx",
|
|
8206
|
+
"ownership_verification",
|
|
8207
|
+
"spf",
|
|
8208
|
+
"dkim",
|
|
8209
|
+
"dmarc",
|
|
8210
|
+
"tls_reporting"
|
|
8211
|
+
]
|
|
8212
|
+
},
|
|
8213
|
+
"status": {
|
|
8214
|
+
"type": "string",
|
|
8215
|
+
"enum": [
|
|
8216
|
+
"pending",
|
|
8217
|
+
"found",
|
|
8218
|
+
"missing",
|
|
8219
|
+
"incorrect"
|
|
8220
|
+
]
|
|
8221
|
+
},
|
|
8222
|
+
"message": {
|
|
8223
|
+
"type": "string",
|
|
8224
|
+
"description": "Short explanation of why this record is needed."
|
|
8225
|
+
}
|
|
8226
|
+
},
|
|
8227
|
+
"required": [
|
|
8228
|
+
"type",
|
|
8229
|
+
"name",
|
|
8230
|
+
"fqdn",
|
|
8231
|
+
"value",
|
|
8232
|
+
"required",
|
|
8233
|
+
"purpose",
|
|
8234
|
+
"status"
|
|
8235
|
+
]
|
|
8236
|
+
}
|
|
8237
|
+
}
|
|
8238
|
+
},
|
|
7779
8239
|
"required": ["verified"]
|
|
7780
8240
|
}, {
|
|
7781
8241
|
"type": "object",
|
|
@@ -7792,6 +8252,94 @@ const operationManifest = [
|
|
|
7792
8252
|
"type": "boolean",
|
|
7793
8253
|
"description": "Whether the TXT verification record was found"
|
|
7794
8254
|
},
|
|
8255
|
+
"spfFound": {
|
|
8256
|
+
"type": "boolean",
|
|
8257
|
+
"description": "Whether the SPF record includes Primitive."
|
|
8258
|
+
},
|
|
8259
|
+
"dkimFound": {
|
|
8260
|
+
"type": "boolean",
|
|
8261
|
+
"description": "Whether the DKIM public key record was found."
|
|
8262
|
+
},
|
|
8263
|
+
"dmarcFound": {
|
|
8264
|
+
"type": "boolean",
|
|
8265
|
+
"description": "Whether the DMARC record was found."
|
|
8266
|
+
},
|
|
8267
|
+
"tlsRptFound": {
|
|
8268
|
+
"type": "boolean",
|
|
8269
|
+
"description": "Whether the TLS-RPT record was found."
|
|
8270
|
+
},
|
|
8271
|
+
"dns_records": {
|
|
8272
|
+
"type": "array",
|
|
8273
|
+
"description": "Exact DNS records checked for this verification attempt.",
|
|
8274
|
+
"items": {
|
|
8275
|
+
"type": "object",
|
|
8276
|
+
"additionalProperties": false,
|
|
8277
|
+
"properties": {
|
|
8278
|
+
"type": {
|
|
8279
|
+
"type": "string",
|
|
8280
|
+
"enum": ["MX", "TXT"],
|
|
8281
|
+
"description": "DNS record type."
|
|
8282
|
+
},
|
|
8283
|
+
"name": {
|
|
8284
|
+
"type": "string",
|
|
8285
|
+
"description": "DNS-provider host/name value relative to the managed root zone."
|
|
8286
|
+
},
|
|
8287
|
+
"fqdn": {
|
|
8288
|
+
"type": "string",
|
|
8289
|
+
"description": "Fully-qualified DNS record name."
|
|
8290
|
+
},
|
|
8291
|
+
"value": {
|
|
8292
|
+
"type": "string",
|
|
8293
|
+
"description": "Exact value to publish."
|
|
8294
|
+
},
|
|
8295
|
+
"priority": {
|
|
8296
|
+
"type": "integer",
|
|
8297
|
+
"description": "MX priority. Present only for MX records."
|
|
8298
|
+
},
|
|
8299
|
+
"ttl": {
|
|
8300
|
+
"type": "integer",
|
|
8301
|
+
"description": "Suggested TTL in seconds when the API can provide one."
|
|
8302
|
+
},
|
|
8303
|
+
"required": {
|
|
8304
|
+
"type": "boolean",
|
|
8305
|
+
"const": true
|
|
8306
|
+
},
|
|
8307
|
+
"purpose": {
|
|
8308
|
+
"type": "string",
|
|
8309
|
+
"enum": [
|
|
8310
|
+
"inbound_mx",
|
|
8311
|
+
"ownership_verification",
|
|
8312
|
+
"spf",
|
|
8313
|
+
"dkim",
|
|
8314
|
+
"dmarc",
|
|
8315
|
+
"tls_reporting"
|
|
8316
|
+
]
|
|
8317
|
+
},
|
|
8318
|
+
"status": {
|
|
8319
|
+
"type": "string",
|
|
8320
|
+
"enum": [
|
|
8321
|
+
"pending",
|
|
8322
|
+
"found",
|
|
8323
|
+
"missing",
|
|
8324
|
+
"incorrect"
|
|
8325
|
+
]
|
|
8326
|
+
},
|
|
8327
|
+
"message": {
|
|
8328
|
+
"type": "string",
|
|
8329
|
+
"description": "Short explanation of why this record is needed."
|
|
8330
|
+
}
|
|
8331
|
+
},
|
|
8332
|
+
"required": [
|
|
8333
|
+
"type",
|
|
8334
|
+
"name",
|
|
8335
|
+
"fqdn",
|
|
8336
|
+
"value",
|
|
8337
|
+
"required",
|
|
8338
|
+
"purpose",
|
|
8339
|
+
"status"
|
|
8340
|
+
]
|
|
8341
|
+
}
|
|
8342
|
+
},
|
|
7795
8343
|
"error": {
|
|
7796
8344
|
"type": "string",
|
|
7797
8345
|
"description": "Human-readable verification failure reason"
|
|
@@ -11165,6 +11713,36 @@ const operationManifest = [
|
|
|
11165
11713
|
"pattern": "^[^\\x00-\\x1F\\x7F]+$"
|
|
11166
11714
|
}
|
|
11167
11715
|
},
|
|
11716
|
+
"attachments": {
|
|
11717
|
+
"type": "array",
|
|
11718
|
+
"maxItems": 100,
|
|
11719
|
+
"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.",
|
|
11720
|
+
"items": {
|
|
11721
|
+
"type": "object",
|
|
11722
|
+
"additionalProperties": false,
|
|
11723
|
+
"properties": {
|
|
11724
|
+
"filename": {
|
|
11725
|
+
"type": "string",
|
|
11726
|
+
"minLength": 1,
|
|
11727
|
+
"maxLength": 255,
|
|
11728
|
+
"description": "Attachment filename. Control characters are rejected."
|
|
11729
|
+
},
|
|
11730
|
+
"content_type": {
|
|
11731
|
+
"type": "string",
|
|
11732
|
+
"minLength": 1,
|
|
11733
|
+
"maxLength": 255,
|
|
11734
|
+
"description": "Optional MIME content type. Control characters are rejected."
|
|
11735
|
+
},
|
|
11736
|
+
"content_base64": {
|
|
11737
|
+
"type": "string",
|
|
11738
|
+
"minLength": 1,
|
|
11739
|
+
"maxLength": 44040192,
|
|
11740
|
+
"description": "Base64-encoded attachment bytes."
|
|
11741
|
+
}
|
|
11742
|
+
},
|
|
11743
|
+
"required": ["filename", "content_base64"]
|
|
11744
|
+
}
|
|
11745
|
+
},
|
|
11168
11746
|
"wait": {
|
|
11169
11747
|
"type": "boolean",
|
|
11170
11748
|
"description": "When true, wait for the first downstream SMTP delivery outcome before returning."
|
|
@@ -11508,7 +12086,7 @@ var PrimitiveApiClient = class {
|
|
|
11508
12086
|
const CREDENTIALS_FILE = "credentials.json";
|
|
11509
12087
|
const CREDENTIALS_LOCK_DIR = "credentials.lock";
|
|
11510
12088
|
const CREDENTIALS_LOCK_STALE_MS = 1800 * 1e3;
|
|
11511
|
-
const MALFORMED_CREDENTIALS_HINT = "Run `primitive logout` and then `primitive
|
|
12089
|
+
const MALFORMED_CREDENTIALS_HINT = "Run `primitive logout` and then `primitive signin`.";
|
|
11512
12090
|
function isRecord$2(value) {
|
|
11513
12091
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
11514
12092
|
}
|
|
@@ -11586,10 +12164,10 @@ function loadCliCredentials(configDir) {
|
|
|
11586
12164
|
try {
|
|
11587
12165
|
rmSync(path, { force: true });
|
|
11588
12166
|
} catch {}
|
|
11589
|
-
process.stderr.write("Removed local Primitive CLI API-key login state. API keys are still valid when passed explicitly, but
|
|
12167
|
+
process.stderr.write("Removed local Primitive CLI API-key login state. API keys are still valid when passed explicitly, but saved CLI auth now uses OAuth. Run `primitive signin` to create an OAuth session. No API key was revoked.\n");
|
|
11590
12168
|
return null;
|
|
11591
12169
|
}
|
|
11592
|
-
if (error instanceof SyntaxError) throw new Error("Stored Primitive CLI credentials are not valid JSON. Run `primitive logout` and then `primitive
|
|
12170
|
+
if (error instanceof SyntaxError) throw new Error("Stored Primitive CLI credentials are not valid JSON. Run `primitive logout` and then `primitive signin`.");
|
|
11593
12171
|
throw error;
|
|
11594
12172
|
}
|
|
11595
12173
|
}
|
|
@@ -11872,7 +12450,7 @@ function redactCliEnvironment(environment) {
|
|
|
11872
12450
|
//#region src/oclif/api-client.ts
|
|
11873
12451
|
const API_HEADERS_ENV = "PRIMITIVE_API_HEADERS";
|
|
11874
12452
|
const OAUTH_REFRESH_SKEW_MS = 60 * 1e3;
|
|
11875
|
-
const SAVED_CLI_OAUTH_SESSION_EXPIRED_MESSAGE = "Saved Primitive CLI OAuth session expired or was revoked. Run `primitive
|
|
12453
|
+
const SAVED_CLI_OAUTH_SESSION_EXPIRED_MESSAGE = "Saved Primitive CLI OAuth session expired or was revoked. Run `primitive signin` to authenticate again.";
|
|
11876
12454
|
function mergeHeaders(...headers) {
|
|
11877
12455
|
const merged = {};
|
|
11878
12456
|
for (const headerSet of headers) {
|
|
@@ -11970,7 +12548,7 @@ async function refreshStoredCliCredentials(params) {
|
|
|
11970
12548
|
throw new Errors.CLIError(detail, { exit: 1 });
|
|
11971
12549
|
}
|
|
11972
12550
|
const current = loadCliCredentials(params.configDir);
|
|
11973
|
-
if (!current) throw new Errors.CLIError("Saved Primitive CLI OAuth session is no longer available. Run `primitive
|
|
12551
|
+
if (!current) throw new Errors.CLIError("Saved Primitive CLI OAuth session is no longer available. Run `primitive signin` to authenticate again.", { exit: 1 });
|
|
11974
12552
|
if (!shouldRefresh(current, now)) return current;
|
|
11975
12553
|
const fetchImpl = params.fetch ?? fetch;
|
|
11976
12554
|
const body = new URLSearchParams({
|
|
@@ -12279,26 +12857,26 @@ function coerceParameterValue(parameter, value) {
|
|
|
12279
12857
|
if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") return value;
|
|
12280
12858
|
throw new Errors.CLIError(`Unsupported flag value for --${parameter.name}`);
|
|
12281
12859
|
}
|
|
12282
|
-
function cliError$
|
|
12860
|
+
function cliError$7(message) {
|
|
12283
12861
|
return new Errors.CLIError(message, { exit: 1 });
|
|
12284
12862
|
}
|
|
12285
12863
|
function parseJson(source, flagLabel) {
|
|
12286
12864
|
try {
|
|
12287
12865
|
return JSON.parse(source);
|
|
12288
12866
|
} catch (error) {
|
|
12289
|
-
throw cliError$
|
|
12867
|
+
throw cliError$7(`${flagLabel} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
12290
12868
|
}
|
|
12291
12869
|
}
|
|
12292
12870
|
function readJsonBody(flags) {
|
|
12293
12871
|
const bodyFile = flags["body-file"];
|
|
12294
12872
|
const rawBody = flags["raw-body"];
|
|
12295
|
-
if (bodyFile && rawBody) throw cliError$
|
|
12873
|
+
if (bodyFile && rawBody) throw cliError$7("Use either --raw-body or --body-file, not both");
|
|
12296
12874
|
if (typeof bodyFile === "string") {
|
|
12297
12875
|
let contents;
|
|
12298
12876
|
try {
|
|
12299
12877
|
contents = readFileSync(bodyFile, "utf8");
|
|
12300
12878
|
} catch (error) {
|
|
12301
|
-
throw cliError$
|
|
12879
|
+
throw cliError$7(`Could not read --body-file ${bodyFile}: ${error instanceof Error ? error.message : String(error)}`);
|
|
12302
12880
|
}
|
|
12303
12881
|
return parseJson(contents, `--body-file ${bodyFile}`);
|
|
12304
12882
|
}
|
|
@@ -12308,7 +12886,7 @@ function readTextFileFlag(path, flagLabel) {
|
|
|
12308
12886
|
try {
|
|
12309
12887
|
return readFileSync(path, "utf8");
|
|
12310
12888
|
} catch (error) {
|
|
12311
|
-
throw cliError$
|
|
12889
|
+
throw cliError$7(`Could not read ${flagLabel} ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
12312
12890
|
}
|
|
12313
12891
|
}
|
|
12314
12892
|
function extractErrorPayload(raw) {
|
|
@@ -12355,7 +12933,7 @@ function extractErrorCode(payload) {
|
|
|
12355
12933
|
if (typeof direct === "string") return direct;
|
|
12356
12934
|
}
|
|
12357
12935
|
}
|
|
12358
|
-
const ERROR_CODE_HINTS = { [API_ERROR_CODES.unauthorized]: "Hint: run `primitive
|
|
12936
|
+
const ERROR_CODE_HINTS = { [API_ERROR_CODES.unauthorized]: "Hint: run `primitive signin`, pass --api-key explicitly, or set PRIMITIVE_API_KEY in your environment. `primitive whoami` is the fastest way to verify auth is live." };
|
|
12359
12937
|
const NETWORK_ERROR_HINTS = {
|
|
12360
12938
|
ENETUNREACH: "Hint: the network is unreachable. If you're behind a proxy and set HTTP(S)_PROXY, re-run with NODE_USE_ENV_PROXY=1 (Node 22+ ignores those env vars by default). `primitive doctor` reports the local environment in one shot.",
|
|
12361
12939
|
ECONNREFUSED: "Hint: the server refused the connection. Check that your firewall allows egress to *.primitive.dev, that your PRIMITIVE_API_BASE_URL_* overrides (if any) point at a reachable host, and re-run with NODE_USE_ENV_PROXY=1 if you're behind a proxy. `primitive doctor` reports the local environment in one shot.",
|
|
@@ -12393,7 +12971,7 @@ function surfaceUnauthorizedHint(params) {
|
|
|
12393
12971
|
process.stderr.write("Saved Primitive CLI credentials were rejected by the overridden API base URL. The saved credential is preserved; unset PRIMITIVE_API_BASE_URL_1, run `primitive config reset` to clear configured URL overrides, or run `primitive logout` to remove the stored credential.\n");
|
|
12394
12972
|
return;
|
|
12395
12973
|
}
|
|
12396
|
-
process.stderr.write("Your saved Primitive CLI OAuth session was rejected. If the command was working a moment ago, please retry; brief retries often clear transient rejections. If it keeps failing, run `primitive logout && primitive
|
|
12974
|
+
process.stderr.write("Your saved Primitive CLI OAuth session was rejected. If the command was working a moment ago, please retry; brief retries often clear transient rejections. If it keeps failing, run `primitive logout && primitive signin` to mint a fresh session.\n");
|
|
12397
12975
|
}
|
|
12398
12976
|
function formatElapsed(ms) {
|
|
12399
12977
|
const seconds = ms / 1e3;
|
|
@@ -12501,6 +13079,10 @@ function operationOutputPayload(envelope, includeEnvelope) {
|
|
|
12501
13079
|
return includeEnvelope ? envelope ?? null : envelope?.data ?? null;
|
|
12502
13080
|
}
|
|
12503
13081
|
const OPERATION_HINTS = {
|
|
13082
|
+
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.",
|
|
13083
|
+
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.",
|
|
13084
|
+
downloadDomainZoneFile: "Tip: prefer `primitive domains zone-file --id <domain-id> --output <domain>.zone` for CLI-friendly file output.",
|
|
13085
|
+
sendEmail: "Tip: prefer `primitive send --to <address> --body <text> --attachment <file>` for file attachments. This raw command exists for callers passing JSON.",
|
|
12504
13086
|
createFunction: "Tip: prefer `primitive functions deploy --name <name> --file <bundle>` for file-input ergonomics. This raw command exists for callers passing JSON.",
|
|
12505
13087
|
updateFunction: "Tip: prefer `primitive functions redeploy --id <id> --file <bundle>` for file-input ergonomics. This raw command exists for callers passing JSON.",
|
|
12506
13088
|
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 +13194,8 @@ const EMPTY_RESULT_HINTS = {
|
|
|
12612
13194
|
listEndpoints: "(no results) No webhook endpoints configured. Add one with `primitive endpoints create --url <your-url>`.",
|
|
12613
13195
|
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
13196
|
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."
|
|
13197
|
+
listFilters: "(no results) No filter rules configured.",
|
|
13198
|
+
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
13199
|
};
|
|
12617
13200
|
function canonicalizeCliReferences(description) {
|
|
12618
13201
|
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'`");
|
|
@@ -12756,11 +13339,11 @@ function sleep$1(ms) {
|
|
|
12756
13339
|
//#region src/oclif/commands/chat.ts
|
|
12757
13340
|
const DEFAULT_CHAT_TIMEOUT_SECONDS = 120;
|
|
12758
13341
|
const DEFAULT_STRICT_PHASE_SECONDS = 60;
|
|
12759
|
-
function cliError$
|
|
13342
|
+
function cliError$6(message) {
|
|
12760
13343
|
return new Errors.CLIError(message, { exit: 1 });
|
|
12761
13344
|
}
|
|
12762
13345
|
async function readStdinToString() {
|
|
12763
|
-
if (process.stdin.isTTY) throw cliError$
|
|
13346
|
+
if (process.stdin.isTTY) throw cliError$6("No message provided. Pass the message as the second positional argument or pipe it via stdin.");
|
|
12764
13347
|
const chunks = [];
|
|
12765
13348
|
for await (const chunk of process.stdin) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
12766
13349
|
return Buffer.concat(chunks).toString("utf8");
|
|
@@ -12796,7 +13379,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
12796
13379
|
};
|
|
12797
13380
|
static flags = {
|
|
12798
13381
|
"api-key": Flags.string({
|
|
12799
|
-
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive
|
|
13382
|
+
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive signin` credentials)",
|
|
12800
13383
|
env: "PRIMITIVE_API_KEY"
|
|
12801
13384
|
}),
|
|
12802
13385
|
"api-base-url-1": Flags.string({
|
|
@@ -12841,7 +13424,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
12841
13424
|
async run() {
|
|
12842
13425
|
const { args, flags } = await this.parse(ChatCommand);
|
|
12843
13426
|
const message = args.message !== void 0 && args.message !== "" ? args.message : await readStdinToString();
|
|
12844
|
-
if (!message.trim()) throw cliError$
|
|
13427
|
+
if (!message.trim()) throw cliError$6("Message body is empty.");
|
|
12845
13428
|
await runWithTiming(flags.time, async () => {
|
|
12846
13429
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
12847
13430
|
apiKey: flags["api-key"],
|
|
@@ -12879,7 +13462,7 @@ var ChatCommand = class ChatCommand extends Command {
|
|
|
12879
13462
|
return;
|
|
12880
13463
|
}
|
|
12881
13464
|
const sent = sendResult.data?.data;
|
|
12882
|
-
if (!sent) throw cliError$
|
|
13465
|
+
if (!sent) throw cliError$6("Send succeeded but the API returned no data.");
|
|
12883
13466
|
const reply = await waitForReply({
|
|
12884
13467
|
apiClient,
|
|
12885
13468
|
authFailureContext,
|
|
@@ -13203,23 +13786,23 @@ function checkApiKey(opts) {
|
|
|
13203
13786
|
if (typeof parsed?.api_key === "string" && parsed.api_key.length > 0) return {
|
|
13204
13787
|
status: "fail",
|
|
13205
13788
|
message: `${credsPath} contains legacy API-key login state`,
|
|
13206
|
-
hint: "Run `primitive
|
|
13789
|
+
hint: "Run `primitive signin` to create saved OAuth credentials. Existing API keys still work with --api-key or PRIMITIVE_API_KEY."
|
|
13207
13790
|
};
|
|
13208
13791
|
if (parsed) return {
|
|
13209
13792
|
status: "fail",
|
|
13210
13793
|
message: `${credsPath} exists but contains no OAuth access_token`,
|
|
13211
|
-
hint: "Run `primitive logout` to clear it, then `primitive
|
|
13794
|
+
hint: "Run `primitive logout` to clear it, then `primitive signin` to recreate."
|
|
13212
13795
|
};
|
|
13213
13796
|
return {
|
|
13214
13797
|
status: "fail",
|
|
13215
13798
|
message: `${credsPath} exists but is unreadable or malformed${parseError ? ` (${parseError})` : ""}`,
|
|
13216
|
-
hint: "Run `primitive logout` to clear it, then `primitive
|
|
13799
|
+
hint: "Run `primitive logout` to clear it, then `primitive signin` to recreate."
|
|
13217
13800
|
};
|
|
13218
13801
|
}
|
|
13219
13802
|
return {
|
|
13220
13803
|
status: "fail",
|
|
13221
13804
|
message: "no CLI OAuth session or explicit API key found",
|
|
13222
|
-
hint: "Run `primitive
|
|
13805
|
+
hint: "Run `primitive signin`, pass --api-key explicitly, or export PRIMITIVE_API_KEY=prim_..."
|
|
13223
13806
|
};
|
|
13224
13807
|
}
|
|
13225
13808
|
async function checkAccount(client) {
|
|
@@ -13379,6 +13962,144 @@ var DoctorCommand = class DoctorCommand extends Command {
|
|
|
13379
13962
|
}
|
|
13380
13963
|
};
|
|
13381
13964
|
//#endregion
|
|
13965
|
+
//#region src/oclif/commands/domains-zone-file.ts
|
|
13966
|
+
function zoneFileUrl(baseUrl, domainId, outboundOnly) {
|
|
13967
|
+
const url = new URL(`${baseUrl.replace(/\/$/, "")}/domains/${encodeURIComponent(domainId)}/zone-file`);
|
|
13968
|
+
if (outboundOnly) url.searchParams.set("outbound_only", "true");
|
|
13969
|
+
return url.toString();
|
|
13970
|
+
}
|
|
13971
|
+
function contentDispositionFilename(value) {
|
|
13972
|
+
if (!value) return null;
|
|
13973
|
+
const match = /filename\*=UTF-8''([^;]+)|filename="?([^";]+)"?/i.exec(value);
|
|
13974
|
+
const raw = match?.[1] ?? match?.[2];
|
|
13975
|
+
if (!raw) return null;
|
|
13976
|
+
try {
|
|
13977
|
+
return decodeURIComponent(raw);
|
|
13978
|
+
} catch {
|
|
13979
|
+
return raw;
|
|
13980
|
+
}
|
|
13981
|
+
}
|
|
13982
|
+
function findDomain(domains, domain) {
|
|
13983
|
+
const match = domains.find((entry) => entry.domain === domain);
|
|
13984
|
+
if (!match) throw new Errors.CLIError(`Domain ${domain} was not found.`, { exit: 1 });
|
|
13985
|
+
return match;
|
|
13986
|
+
}
|
|
13987
|
+
async function responseErrorPayload(response) {
|
|
13988
|
+
if ((response.headers.get("content-type")?.toLowerCase() ?? "").includes("application/json")) return response.json().catch(() => ({
|
|
13989
|
+
code: "http_error",
|
|
13990
|
+
message: `HTTP ${response.status} ${response.statusText}`.trim()
|
|
13991
|
+
}));
|
|
13992
|
+
return {
|
|
13993
|
+
code: "http_error",
|
|
13994
|
+
message: (await response.text().catch(() => "")).trim() || `HTTP ${response.status} ${response.statusText}`.trim()
|
|
13995
|
+
};
|
|
13996
|
+
}
|
|
13997
|
+
var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
|
|
13998
|
+
static description = `Download a BIND-format DNS zone file for a domain claim.
|
|
13999
|
+
|
|
14000
|
+
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.
|
|
14001
|
+
|
|
14002
|
+
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.`;
|
|
14003
|
+
static summary = "Download a DNS zone file for a domain";
|
|
14004
|
+
static examples = [
|
|
14005
|
+
"<%= config.bin %> domains zone-file --id <domain-id>",
|
|
14006
|
+
"<%= config.bin %> domains zone-file --id <domain-id> --output example.com.zone",
|
|
14007
|
+
"<%= config.bin %> domains zone-file --domain example.com --output example.com.zone",
|
|
14008
|
+
"<%= config.bin %> domains zone-file --id <domain-id> --outbound-only"
|
|
14009
|
+
];
|
|
14010
|
+
static flags = {
|
|
14011
|
+
"api-key": Flags.string({
|
|
14012
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
14013
|
+
env: "PRIMITIVE_API_KEY"
|
|
14014
|
+
}),
|
|
14015
|
+
"api-base-url-1": Flags.string({
|
|
14016
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
14017
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
14018
|
+
hidden: true
|
|
14019
|
+
}),
|
|
14020
|
+
"api-base-url-2": Flags.string({
|
|
14021
|
+
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
14022
|
+
env: "PRIMITIVE_API_BASE_URL_2",
|
|
14023
|
+
hidden: true
|
|
14024
|
+
}),
|
|
14025
|
+
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`." }),
|
|
14026
|
+
id: Flags.string({ description: "Domain id returned by `primitive domains add` or `primitive domains list`." }),
|
|
14027
|
+
output: Flags.string({
|
|
14028
|
+
char: "o",
|
|
14029
|
+
description: "Write the zone file to this path instead of stdout. Defaults to stdout."
|
|
14030
|
+
}),
|
|
14031
|
+
"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." }),
|
|
14032
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
14033
|
+
};
|
|
14034
|
+
async run() {
|
|
14035
|
+
const { flags } = await this.parse(DomainsZoneFileCommand);
|
|
14036
|
+
if (flags.id && flags.domain) throw new Errors.CLIError("Use only one of --id or --domain.", { exit: 1 });
|
|
14037
|
+
if (!flags.id && !flags.domain) throw new Errors.CLIError("Pass --id <domain-id> or --domain <domain>.", { exit: 1 });
|
|
14038
|
+
await runWithTiming(flags.time, async () => {
|
|
14039
|
+
const { apiClient, auth, baseUrlOverridden, requestConfig } = await createAuthenticatedCliApiClient({
|
|
14040
|
+
apiKey: flags["api-key"],
|
|
14041
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
14042
|
+
apiBaseUrl2: flags["api-base-url-2"],
|
|
14043
|
+
configDir: this.config.configDir
|
|
14044
|
+
});
|
|
14045
|
+
let domainId = flags.id;
|
|
14046
|
+
if (!domainId && flags.domain) {
|
|
14047
|
+
const result = await listDomains({
|
|
14048
|
+
client: apiClient.client,
|
|
14049
|
+
responseStyle: "fields"
|
|
14050
|
+
});
|
|
14051
|
+
if (result.error) {
|
|
14052
|
+
const errorPayload = extractErrorPayload(result.error);
|
|
14053
|
+
writeErrorWithHints(errorPayload);
|
|
14054
|
+
surfaceUnauthorizedHint({
|
|
14055
|
+
auth,
|
|
14056
|
+
baseUrlOverridden,
|
|
14057
|
+
configDir: this.config.configDir,
|
|
14058
|
+
payload: errorPayload
|
|
14059
|
+
});
|
|
14060
|
+
process.exitCode = 1;
|
|
14061
|
+
return;
|
|
14062
|
+
}
|
|
14063
|
+
const envelope = result.data;
|
|
14064
|
+
domainId = findDomain(envelope?.data ?? [], flags.domain).id;
|
|
14065
|
+
}
|
|
14066
|
+
if (!domainId) throw new Errors.CLIError("Could not resolve a domain id.", { exit: 1 });
|
|
14067
|
+
let response;
|
|
14068
|
+
try {
|
|
14069
|
+
response = await fetch(zoneFileUrl(requestConfig.resolvedApiBaseUrl1, domainId, flags["outbound-only"]), { headers: {
|
|
14070
|
+
...requestConfig.headers ?? {},
|
|
14071
|
+
...auth.apiKey ? { authorization: `Bearer ${auth.apiKey}` } : {}
|
|
14072
|
+
} });
|
|
14073
|
+
} catch (error) {
|
|
14074
|
+
writeErrorWithHints(error);
|
|
14075
|
+
process.exitCode = 1;
|
|
14076
|
+
return;
|
|
14077
|
+
}
|
|
14078
|
+
if (!response.ok) {
|
|
14079
|
+
const errorPayload = extractErrorPayload(await responseErrorPayload(response));
|
|
14080
|
+
writeErrorWithHints(errorPayload);
|
|
14081
|
+
surfaceUnauthorizedHint({
|
|
14082
|
+
auth,
|
|
14083
|
+
baseUrlOverridden,
|
|
14084
|
+
configDir: this.config.configDir,
|
|
14085
|
+
payload: errorPayload
|
|
14086
|
+
});
|
|
14087
|
+
process.exitCode = 1;
|
|
14088
|
+
return;
|
|
14089
|
+
}
|
|
14090
|
+
const zoneFile = await response.text();
|
|
14091
|
+
const output = flags.output;
|
|
14092
|
+
if (output) {
|
|
14093
|
+
writeFileSync(output, zoneFile, "utf8");
|
|
14094
|
+
const filename = contentDispositionFilename(response.headers.get("content-disposition"));
|
|
14095
|
+
process.stderr.write(`Wrote ${filename ?? "zone file"} to ${output}\n`);
|
|
14096
|
+
return;
|
|
14097
|
+
}
|
|
14098
|
+
process.stdout.write(zoneFile);
|
|
14099
|
+
});
|
|
14100
|
+
}
|
|
14101
|
+
};
|
|
14102
|
+
//#endregion
|
|
13382
14103
|
//#region src/oclif/commands/emails-latest.ts
|
|
13383
14104
|
const DEFAULT_LIMIT = 10;
|
|
13384
14105
|
const MAX_LIMIT = 100;
|
|
@@ -13489,7 +14210,7 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
|
|
|
13489
14210
|
//#endregion
|
|
13490
14211
|
//#region src/oclif/commands/emails-wait.ts
|
|
13491
14212
|
const DEFAULT_WAIT_TIMEOUT_SECONDS$1 = 300;
|
|
13492
|
-
function cliError$
|
|
14213
|
+
function cliError$5(message) {
|
|
13493
14214
|
return new Errors.CLIError(message, { exit: 1 });
|
|
13494
14215
|
}
|
|
13495
14216
|
var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
@@ -13564,7 +14285,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
13564
14285
|
try {
|
|
13565
14286
|
since = sinceFromFlags(flags);
|
|
13566
14287
|
} catch (error) {
|
|
13567
|
-
throw cliError$
|
|
14288
|
+
throw cliError$5(error instanceof Error ? error.message : String(error));
|
|
13568
14289
|
}
|
|
13569
14290
|
const filters = filtersFromFlags(flags);
|
|
13570
14291
|
const deadline = flags.timeout === 0 ? null : Date.now() + flags.timeout * 1e3;
|
|
@@ -13615,7 +14336,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
13615
14336
|
};
|
|
13616
14337
|
//#endregion
|
|
13617
14338
|
//#region src/oclif/commands/emails-watch.ts
|
|
13618
|
-
function cliError$
|
|
14339
|
+
function cliError$4(message) {
|
|
13619
14340
|
return new Errors.CLIError(message, { exit: 1 });
|
|
13620
14341
|
}
|
|
13621
14342
|
var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
@@ -13686,7 +14407,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
13686
14407
|
try {
|
|
13687
14408
|
since = sinceFromFlags(flags);
|
|
13688
14409
|
} catch (error) {
|
|
13689
|
-
throw cliError$
|
|
14410
|
+
throw cliError$4(error instanceof Error ? error.message : String(error));
|
|
13690
14411
|
}
|
|
13691
14412
|
const filters = filtersFromFlags(flags);
|
|
13692
14413
|
const deadline = flags.seconds ? Date.now() + flags.seconds * 1e3 : null;
|
|
@@ -14731,7 +15452,7 @@ The deploy step calls \`primitive functions deploy\` (provided by the
|
|
|
14731
15452
|
\`npm install -g @primitivedotdev/cli\` or run via
|
|
14732
15453
|
\`npx @primitivedotdev/cli@latest <command>\`). It requires
|
|
14733
15454
|
\`PRIMITIVE_API_KEY\` to be set in your shell (or pass \`--api-key\`).
|
|
14734
|
-
Run \`primitive
|
|
15455
|
+
Run \`primitive signin\` once to save a key in your CLI config if you
|
|
14735
15456
|
prefer that to an env var.
|
|
14736
15457
|
`;
|
|
14737
15458
|
}
|
|
@@ -15790,7 +16511,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
15790
16511
|
//#endregion
|
|
15791
16512
|
//#region src/oclif/commands/login.ts
|
|
15792
16513
|
const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
|
|
15793
|
-
function cliError$
|
|
16514
|
+
function cliError$3(message) {
|
|
15794
16515
|
return new Errors.CLIError(message, { exit: 1 });
|
|
15795
16516
|
}
|
|
15796
16517
|
function sleep(ms) {
|
|
@@ -15866,7 +16587,7 @@ async function checkExistingLogin(params) {
|
|
|
15866
16587
|
message: code === API_ERROR_CODES.unauthorized ? "Saved Primitive CLI OAuth credentials were rejected by an API URL different from the one they were saved with. Run `primitive logout` to remove them, or switch back to the original environment before logging in again." : "A saved Primitive CLI OAuth session exists, but the CLI could not verify whether it is still valid. Run `primitive logout` before logging in again."
|
|
15867
16588
|
};
|
|
15868
16589
|
}
|
|
15869
|
-
var LoginCommand = class
|
|
16590
|
+
var LoginCommand = class extends Command {
|
|
15870
16591
|
static description = "Log in by opening Primitive in your browser and saving an org-scoped OAuth session locally.";
|
|
15871
16592
|
static summary = "Log in with browser approval";
|
|
15872
16593
|
static examples = [
|
|
@@ -15884,24 +16605,28 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
15884
16605
|
"no-browser": Flags.boolean({ description: "Do not attempt to open the browser automatically" }),
|
|
15885
16606
|
force: Flags.boolean({
|
|
15886
16607
|
char: "f",
|
|
15887
|
-
description: "Replace saved credentials without first verifying the existing
|
|
16608
|
+
description: "Replace saved credentials without first verifying the existing session"
|
|
15888
16609
|
})
|
|
15889
16610
|
};
|
|
15890
16611
|
async run() {
|
|
15891
|
-
const
|
|
16612
|
+
const commandClass = this.constructor;
|
|
16613
|
+
const { flags } = await this.parse(commandClass);
|
|
15892
16614
|
let releaseCredentialsLock;
|
|
15893
16615
|
try {
|
|
15894
16616
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
15895
16617
|
} catch (error) {
|
|
15896
|
-
throw cliError$
|
|
16618
|
+
throw cliError$3(error instanceof Error ? error.message : String(error));
|
|
15897
16619
|
}
|
|
15898
16620
|
try {
|
|
15899
|
-
await this.runWithCredentialLock(flags);
|
|
16621
|
+
await this.runWithCredentialLock(flags, this.retryCommand());
|
|
15900
16622
|
} finally {
|
|
15901
16623
|
releaseCredentialsLock();
|
|
15902
16624
|
}
|
|
15903
16625
|
}
|
|
15904
|
-
|
|
16626
|
+
retryCommand() {
|
|
16627
|
+
return "login";
|
|
16628
|
+
}
|
|
16629
|
+
async runWithCredentialLock(flags, retryCommand) {
|
|
15905
16630
|
const { apiClient, requestConfig } = createCliApiClient({
|
|
15906
16631
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
15907
16632
|
configDir: this.config.configDir
|
|
@@ -15927,8 +16652,8 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
15927
16652
|
if (existingStatus.status === "removed_stale") process.stderr.write("Continuing with a new Primitive CLI login...\n");
|
|
15928
16653
|
else if (existingStatus.status === "blocked") {
|
|
15929
16654
|
writeErrorWithHints(existingStatus.payload);
|
|
15930
|
-
throw cliError$
|
|
15931
|
-
} else throw cliError$
|
|
16655
|
+
throw cliError$3(existingStatus.message);
|
|
16656
|
+
} else throw cliError$3(`Already logged in${existing.org_name ? ` for ${existing.org_name}` : ""}. Run \`primitive logout\` before logging in again.`);
|
|
15932
16657
|
}
|
|
15933
16658
|
const started = await startCliLogin({
|
|
15934
16659
|
body: { device_name: flags["device-name"] ?? hostname() },
|
|
@@ -15937,11 +16662,11 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
15937
16662
|
});
|
|
15938
16663
|
if (started.error) {
|
|
15939
16664
|
writeErrorWithHints(extractErrorPayload(started.error));
|
|
15940
|
-
throw cliError$
|
|
16665
|
+
throw cliError$3("Could not start Primitive CLI login.");
|
|
15941
16666
|
}
|
|
15942
16667
|
const start = unwrapData$2(started.data);
|
|
15943
|
-
if (!start) throw cliError$
|
|
15944
|
-
process.stderr.write(`Your
|
|
16668
|
+
if (!start) throw cliError$3("Primitive API returned an empty CLI login response.");
|
|
16669
|
+
process.stderr.write(`Your sign-in code is: ${start.user_code}\n`);
|
|
15945
16670
|
if (!flags["no-browser"]) {
|
|
15946
16671
|
openBrowser(start.verification_uri_complete);
|
|
15947
16672
|
process.stderr.write("Opening Primitive in your browser...\n");
|
|
@@ -15961,7 +16686,7 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
15961
16686
|
});
|
|
15962
16687
|
if (polled.data) {
|
|
15963
16688
|
const login = unwrapData$2(polled.data);
|
|
15964
|
-
if (!login) throw cliError$
|
|
16689
|
+
if (!login) throw cliError$3("Primitive API returned an empty CLI poll response.");
|
|
15965
16690
|
saveCliCredentials(this.config.configDir, {
|
|
15966
16691
|
access_token: login.access_token,
|
|
15967
16692
|
api_base_url_1: apiBaseUrl1,
|
|
@@ -15991,25 +16716,25 @@ var LoginCommand = class LoginCommand extends Command {
|
|
|
15991
16716
|
nextPollDelay = interval;
|
|
15992
16717
|
continue;
|
|
15993
16718
|
}
|
|
15994
|
-
if (code === API_ERROR_CODES.accessDenied) throw cliError$
|
|
15995
|
-
if (code === API_ERROR_CODES.expiredToken) throw cliError$
|
|
15996
|
-
if (code === API_ERROR_CODES.invalidDeviceCode) throw cliError$
|
|
16719
|
+
if (code === API_ERROR_CODES.accessDenied) throw cliError$3("Primitive CLI login was denied in the browser.");
|
|
16720
|
+
if (code === API_ERROR_CODES.expiredToken) throw cliError$3(`Primitive CLI login expired. Run \`primitive ${retryCommand}\` again.`);
|
|
16721
|
+
if (code === API_ERROR_CODES.invalidDeviceCode) throw cliError$3(`Primitive CLI login device code is invalid. Run \`primitive ${retryCommand}\` again.`);
|
|
15997
16722
|
writeErrorWithHints(payload);
|
|
15998
|
-
throw cliError$
|
|
16723
|
+
throw cliError$3("Primitive CLI login failed while polling for approval.");
|
|
15999
16724
|
}
|
|
16000
|
-
throw cliError$
|
|
16725
|
+
throw cliError$3(`Primitive CLI login expired. Run \`primitive ${retryCommand}\` again.`);
|
|
16001
16726
|
}
|
|
16002
16727
|
};
|
|
16003
16728
|
//#endregion
|
|
16004
16729
|
//#region src/oclif/commands/logout.ts
|
|
16005
|
-
function cliError$
|
|
16730
|
+
function cliError$2(message) {
|
|
16006
16731
|
return new Errors.CLIError(message, { exit: 1 });
|
|
16007
16732
|
}
|
|
16008
16733
|
function unwrapData$1(value) {
|
|
16009
16734
|
return value?.data ?? null;
|
|
16010
16735
|
}
|
|
16011
16736
|
function isSavedOAuthSessionExpiredError(error) {
|
|
16012
|
-
return error instanceof Error && error.message === "Saved Primitive CLI OAuth session expired or was revoked. Run `primitive
|
|
16737
|
+
return error instanceof Error && error.message === "Saved Primitive CLI OAuth session expired or was revoked. Run `primitive signin` to authenticate again.";
|
|
16013
16738
|
}
|
|
16014
16739
|
async function runLogoutWithCredentialLock(params) {
|
|
16015
16740
|
const deps = {
|
|
@@ -16027,7 +16752,7 @@ async function runLogoutWithCredentialLock(params) {
|
|
|
16027
16752
|
process.exitCode = 1;
|
|
16028
16753
|
return;
|
|
16029
16754
|
}
|
|
16030
|
-
if (!credentials) throw cliError$
|
|
16755
|
+
if (!credentials) throw cliError$2("Not logged in. Run `primitive signin` to create saved CLI credentials.");
|
|
16031
16756
|
let authenticated;
|
|
16032
16757
|
try {
|
|
16033
16758
|
authenticated = await deps.createAuthenticatedCliApiClient({
|
|
@@ -16059,7 +16784,7 @@ async function runLogoutWithCredentialLock(params) {
|
|
|
16059
16784
|
return;
|
|
16060
16785
|
}
|
|
16061
16786
|
writeErrorWithHints(payload);
|
|
16062
|
-
throw cliError$
|
|
16787
|
+
throw cliError$2("Could not revoke the saved Primitive CLI OAuth grant.");
|
|
16063
16788
|
}
|
|
16064
16789
|
const logout = unwrapData$1(result.data);
|
|
16065
16790
|
deleteCliCredentials(params.configDir);
|
|
@@ -16081,7 +16806,7 @@ var LogoutCommand = class LogoutCommand extends Command {
|
|
|
16081
16806
|
try {
|
|
16082
16807
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
16083
16808
|
} catch (error) {
|
|
16084
|
-
throw cliError$
|
|
16809
|
+
throw cliError$2(error instanceof Error ? error.message : String(error));
|
|
16085
16810
|
}
|
|
16086
16811
|
try {
|
|
16087
16812
|
await runLogoutWithCredentialLock({
|
|
@@ -16287,12 +17012,46 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
16287
17012
|
}
|
|
16288
17013
|
};
|
|
16289
17014
|
//#endregion
|
|
17015
|
+
//#region src/oclif/attachments.ts
|
|
17016
|
+
function readAttachmentBytes(path, readFile) {
|
|
17017
|
+
try {
|
|
17018
|
+
return Buffer.from(readFile(path));
|
|
17019
|
+
} catch (error) {
|
|
17020
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
17021
|
+
throw new Errors.CLIError(`Could not read --attachment ${path}: ${detail}`, { exit: 1 });
|
|
17022
|
+
}
|
|
17023
|
+
}
|
|
17024
|
+
function hasControlCharacter(value) {
|
|
17025
|
+
return Array.from(value).some((character) => {
|
|
17026
|
+
const code = character.charCodeAt(0);
|
|
17027
|
+
return code <= 31 || code >= 127 && code <= 159;
|
|
17028
|
+
});
|
|
17029
|
+
}
|
|
17030
|
+
function validateAttachmentFilename(path, filename) {
|
|
17031
|
+
if (!filename) throw new Errors.CLIError(`Could not derive an attachment filename from ${path}. Pass a file path.`, { exit: 1 });
|
|
17032
|
+
if (hasControlCharacter(filename)) throw new Errors.CLIError(`Attachment filename ${filename} contains control characters.`, { exit: 1 });
|
|
17033
|
+
}
|
|
17034
|
+
function readAttachmentFiles(paths, readFile = readFileSync) {
|
|
17035
|
+
if (!paths || paths.length === 0) return void 0;
|
|
17036
|
+
return paths.map((path) => {
|
|
17037
|
+
const filename = basename(path);
|
|
17038
|
+
validateAttachmentFilename(path, filename);
|
|
17039
|
+
const bytes = readAttachmentBytes(path, readFile);
|
|
17040
|
+
if (bytes.length === 0) throw new Errors.CLIError(`Attachment file ${path} is empty. Attachments must contain at least one byte.`, { exit: 1 });
|
|
17041
|
+
return {
|
|
17042
|
+
content_base64: bytes.toString("base64"),
|
|
17043
|
+
filename
|
|
17044
|
+
};
|
|
17045
|
+
});
|
|
17046
|
+
}
|
|
17047
|
+
//#endregion
|
|
16290
17048
|
//#region src/oclif/commands/send.ts
|
|
16291
17049
|
var SendCommand = class SendCommand extends Command {
|
|
16292
17050
|
static description = `Send an outbound email. Agent-grade shortcut for \`sending send\` with sensible defaults.
|
|
16293
17051
|
|
|
16294
17052
|
--from defaults to agent@<your-first-verified-outbound-domain> when omitted.
|
|
16295
17053
|
--subject defaults to the first line of the body when omitted.
|
|
17054
|
+
--attachment attaches a file; repeat it to attach multiple files.
|
|
16296
17055
|
|
|
16297
17056
|
For the full flag set (custom message-id threading on the wire,
|
|
16298
17057
|
references arrays, etc.), use \`primitive sending send\`.`;
|
|
@@ -16300,6 +17059,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
16300
17059
|
static examples = [
|
|
16301
17060
|
"<%= config.bin %> send --to alice@example.com --body 'Hi Alice!'",
|
|
16302
17061
|
"<%= config.bin %> send --to alice@example.com --body-file ./message.txt",
|
|
17062
|
+
"<%= config.bin %> send --to alice@example.com --body 'See attached.' --attachment ./report.pdf",
|
|
16303
17063
|
"<%= config.bin %> send --to alice@example.com --from support@yourcompany.com --subject 'Quick question' --body 'Are you free Thursday?'",
|
|
16304
17064
|
"<%= config.bin %> send --to alice@example.com --html '<p>Hello!</p>'",
|
|
16305
17065
|
"<%= config.bin %> send --to alice@example.com --body 'Confirmed' --wait",
|
|
@@ -16327,11 +17087,15 @@ var SendCommand = class SendCommand extends Command {
|
|
|
16327
17087
|
from: Flags.string({ description: "Sender address. Defaults to agent@<your-first-verified-outbound-domain>." }),
|
|
16328
17088
|
subject: Flags.string({ description: "Subject line. Defaults to the first line of --body / --html when omitted." }),
|
|
16329
17089
|
body: Flags.string({ description: "Plain-text message body. Either --body or --html (or both) is required." }),
|
|
16330
|
-
"body-file": Flags.string({ description: "Read the plain-text message body from a UTF-8 file. Mutually exclusive with --body and --body-stdin." }),
|
|
17090
|
+
"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." }),
|
|
16331
17091
|
"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." }),
|
|
16332
17092
|
html: Flags.string({ description: "HTML message body. Either --body or --html (or both) is required." }),
|
|
16333
17093
|
"html-file": Flags.string({ description: "Read the HTML message body from a UTF-8 file. Mutually exclusive with --html and --html-stdin." }),
|
|
16334
17094
|
"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." }),
|
|
17095
|
+
attachment: Flags.string({
|
|
17096
|
+
description: "Attach a file to the email. Repeatable. Sends file bytes as a MIME attachment; use --body-file only for message body text.",
|
|
17097
|
+
multiple: true
|
|
17098
|
+
}),
|
|
16335
17099
|
"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>`." }),
|
|
16336
17100
|
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." }),
|
|
16337
17101
|
"wait-timeout-ms": Flags.integer({ description: "Maximum time to wait when --wait is set. Defaults to 30000ms." }),
|
|
@@ -16348,6 +17112,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
16348
17112
|
htmlStdin: flags["html-stdin"]
|
|
16349
17113
|
});
|
|
16350
17114
|
if (bodies.kind === "error") throw new Errors.CLIError(bodies.message);
|
|
17115
|
+
const attachments = readAttachmentFiles(flags.attachment);
|
|
16351
17116
|
await runWithTiming(flags.time, async () => {
|
|
16352
17117
|
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
16353
17118
|
apiKey: flags["api-key"],
|
|
@@ -16369,6 +17134,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
16369
17134
|
subject,
|
|
16370
17135
|
...bodies.body !== void 0 ? { body_text: bodies.body } : {},
|
|
16371
17136
|
...bodies.html !== void 0 ? { body_html: bodies.html } : {},
|
|
17137
|
+
...attachments !== void 0 ? { attachments } : {},
|
|
16372
17138
|
...flags["in-reply-to"] !== void 0 ? { in_reply_to: flags["in-reply-to"] } : {},
|
|
16373
17139
|
...flags.wait !== void 0 ? { wait: flags.wait } : {},
|
|
16374
17140
|
...flags["wait-timeout-ms"] !== void 0 ? { wait_timeout_ms: flags["wait-timeout-ms"] } : {}
|
|
@@ -16401,7 +17167,14 @@ const EXPIRED_TOKEN = "expired_token";
|
|
|
16401
17167
|
const INVALID_SIGNUP_TOKEN = "invalid_signup_token";
|
|
16402
17168
|
const SLOW_DOWN = "slow_down";
|
|
16403
17169
|
const PENDING_SIGNUP_FILE = "signup.json";
|
|
16404
|
-
|
|
17170
|
+
const DEFAULT_SIGNUP_COMMAND_COPY = {
|
|
17171
|
+
actionNoun: "signup",
|
|
17172
|
+
actionGerund: "creating a new account",
|
|
17173
|
+
confirmCommand: (email) => `signup confirm ${email} <code>`,
|
|
17174
|
+
resendCommand: (email) => `signup resend ${email}`,
|
|
17175
|
+
startCommand: (email) => `signup ${email}`
|
|
17176
|
+
};
|
|
17177
|
+
function cliError$1(message) {
|
|
16405
17178
|
return new Errors.CLIError(message, { exit: 1 });
|
|
16406
17179
|
}
|
|
16407
17180
|
function unwrapData(value) {
|
|
@@ -16490,9 +17263,10 @@ function loadPendingAgentSignup(configDir, apiBaseUrl1) {
|
|
|
16490
17263
|
};
|
|
16491
17264
|
}
|
|
16492
17265
|
function requirePendingSignupForEmail(params) {
|
|
17266
|
+
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
16493
17267
|
const pending = loadPendingAgentSignup(params.configDir, params.apiBaseUrl1);
|
|
16494
|
-
if (!pending) throw cliError(`No pending
|
|
16495
|
-
if (normalizeEmail(pending.email) !== normalizeEmail(params.email)) throw cliError(`Pending
|
|
17268
|
+
if (!pending) throw cliError$1(`No pending ${copy.actionNoun} for ${params.email}. Run \`primitive ${copy.startCommand(params.email)}\` first.`);
|
|
17269
|
+
if (normalizeEmail(pending.email) !== normalizeEmail(params.email)) throw cliError$1(`Pending ${copy.actionNoun} is for ${pending.email}, not ${params.email}. Run \`primitive ${copy.startCommand(params.email)} --force\` to replace it.`);
|
|
16496
17270
|
return pending;
|
|
16497
17271
|
}
|
|
16498
17272
|
function retryAfterSeconds(result) {
|
|
@@ -16529,13 +17303,14 @@ async function promptRequired(question) {
|
|
|
16529
17303
|
}
|
|
16530
17304
|
}
|
|
16531
17305
|
async function confirmTerms() {
|
|
16532
|
-
process$1.stderr.write("By
|
|
17306
|
+
process$1.stderr.write("By continuing, you agree to Primitive's Terms of Service and Privacy Policy:\n");
|
|
16533
17307
|
process$1.stderr.write(" https://primitive.dev/terms\n");
|
|
16534
17308
|
process$1.stderr.write(" https://primitive.dev/privacy\n");
|
|
16535
17309
|
const answer = (await promptRequired("Type 'yes' to continue: ")).toLowerCase();
|
|
16536
|
-
if (answer !== "yes" && answer !== "y") throw cliError("You must accept the terms to create an account.");
|
|
17310
|
+
if (answer !== "yes" && answer !== "y") throw cliError$1("You must accept the terms to create an account.");
|
|
16537
17311
|
}
|
|
16538
17312
|
async function checkExistingCredentials(params) {
|
|
17313
|
+
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
16539
17314
|
const checkExistingLoginFn = params.deps.checkExistingLogin ?? checkExistingLogin;
|
|
16540
17315
|
let existing;
|
|
16541
17316
|
try {
|
|
@@ -16547,7 +17322,7 @@ async function checkExistingCredentials(params) {
|
|
|
16547
17322
|
existing = null;
|
|
16548
17323
|
}
|
|
16549
17324
|
if (existing && params.flags.force) {
|
|
16550
|
-
process$1.stderr.write(
|
|
17325
|
+
process$1.stderr.write(`Replacing saved Primitive CLI credentials after ${copy.actionNoun} because --force was set.\n`);
|
|
16551
17326
|
return;
|
|
16552
17327
|
}
|
|
16553
17328
|
if (!existing) return;
|
|
@@ -16563,9 +17338,9 @@ async function checkExistingCredentials(params) {
|
|
|
16563
17338
|
}
|
|
16564
17339
|
if (existingStatus.status === "blocked") {
|
|
16565
17340
|
writeErrorWithHints(existingStatus.payload);
|
|
16566
|
-
throw cliError(existingStatus.message);
|
|
17341
|
+
throw cliError$1(existingStatus.message);
|
|
16567
17342
|
}
|
|
16568
|
-
throw cliError(`Already logged in${existing.org_name ? ` for ${existing.org_name}` : ""}. Run \`primitive logout\` before
|
|
17343
|
+
throw cliError$1(`Already logged in${existing.org_name ? ` for ${existing.org_name}` : ""}. Run \`primitive logout\` before ${copy.actionGerund}.`);
|
|
16569
17344
|
}
|
|
16570
17345
|
function saveSignupCredentials(params) {
|
|
16571
17346
|
saveCliCredentials(params.configDir, {
|
|
@@ -16582,23 +17357,24 @@ function saveSignupCredentials(params) {
|
|
|
16582
17357
|
token_type: params.signup.token_type
|
|
16583
17358
|
});
|
|
16584
17359
|
}
|
|
16585
|
-
function writeStartInstructions(start) {
|
|
17360
|
+
function writeStartInstructions(start, copy = DEFAULT_SIGNUP_COMMAND_COPY) {
|
|
16586
17361
|
process$1.stderr.write(`Sent a ${start.verification_code_length}-digit verification code to ${start.email}.\n`);
|
|
16587
17362
|
process$1.stderr.write(`The code expires in ${formatSignupSeconds(start.expires_in)}.\n`);
|
|
16588
|
-
process$1.stderr.write(`Run \`primitive
|
|
17363
|
+
process$1.stderr.write(`Run \`primitive ${copy.confirmCommand(start.email)}\` to finish.\n`);
|
|
16589
17364
|
}
|
|
16590
17365
|
async function startSignup(params) {
|
|
17366
|
+
const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
|
|
16591
17367
|
const existingPending = loadPendingAgentSignup(params.configDir, params.apiBaseUrl1);
|
|
16592
17368
|
if (existingPending && !params.flags.force) {
|
|
16593
17369
|
if (normalizeEmail(existingPending.email) === normalizeEmail(params.email)) {
|
|
16594
|
-
process$1.stderr.write(`Continuing pending Primitive
|
|
16595
|
-
process$1.stderr.write(`Run \`primitive
|
|
17370
|
+
process$1.stderr.write(`Continuing pending Primitive ${copy.actionNoun} for ${existingPending.email}.\n`);
|
|
17371
|
+
process$1.stderr.write(`Run \`primitive ${copy.confirmCommand(existingPending.email)}\` to finish, or \`primitive ${copy.resendCommand(existingPending.email)}\` to send a new code.\n`);
|
|
16596
17372
|
return {
|
|
16597
17373
|
pending: existingPending,
|
|
16598
17374
|
started: false
|
|
16599
17375
|
};
|
|
16600
17376
|
}
|
|
16601
|
-
throw cliError(`Pending
|
|
17377
|
+
throw cliError$1(`Pending ${copy.actionNoun} is for ${existingPending.email}. Run \`primitive ${copy.startCommand(params.email)} --force\` to replace it.`);
|
|
16602
17378
|
}
|
|
16603
17379
|
if (params.flags.force) deletePendingAgentSignup(params.configDir);
|
|
16604
17380
|
const promptRequiredFn = params.deps.promptRequired ?? promptRequired;
|
|
@@ -16618,10 +17394,10 @@ async function startSignup(params) {
|
|
|
16618
17394
|
});
|
|
16619
17395
|
if (started.error) {
|
|
16620
17396
|
writeErrorWithHints(extractErrorPayload(started.error));
|
|
16621
|
-
throw cliError("Could not start Primitive agent signup.");
|
|
17397
|
+
throw cliError$1("Could not start Primitive agent signup.");
|
|
16622
17398
|
}
|
|
16623
17399
|
const startResult = unwrapData(started.data);
|
|
16624
|
-
if (!startResult) throw cliError("Primitive API returned an empty agent signup response.");
|
|
17400
|
+
if (!startResult) throw cliError$1("Primitive API returned an empty agent signup response.");
|
|
16625
17401
|
return {
|
|
16626
17402
|
pending: savePendingAgentSignup(params.configDir, startResult, params.apiBaseUrl1),
|
|
16627
17403
|
started: true
|
|
@@ -16659,7 +17435,7 @@ async function resendVerificationCode(params) {
|
|
|
16659
17435
|
}
|
|
16660
17436
|
if (code === EXPIRED_TOKEN || code === INVALID_SIGNUP_TOKEN) deletePendingAgentSignup(params.configDir);
|
|
16661
17437
|
writeErrorWithHints(payload);
|
|
16662
|
-
throw cliError("Could not resend Primitive agent signup verification email.");
|
|
17438
|
+
throw cliError$1("Could not resend Primitive agent signup verification email.");
|
|
16663
17439
|
}
|
|
16664
17440
|
async function runSignupStartWithCredentialLock(params) {
|
|
16665
17441
|
const { configDir, flags } = params;
|
|
@@ -16669,6 +17445,7 @@ async function runSignupStartWithCredentialLock(params) {
|
|
|
16669
17445
|
await checkExistingCredentials({
|
|
16670
17446
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
16671
17447
|
configDir,
|
|
17448
|
+
copy: params.copy,
|
|
16672
17449
|
deps,
|
|
16673
17450
|
flags
|
|
16674
17451
|
});
|
|
@@ -16680,11 +17457,12 @@ async function runSignupStartWithCredentialLock(params) {
|
|
|
16680
17457
|
apiBaseUrl1: requestConfig.resolvedApiBaseUrl1,
|
|
16681
17458
|
apiClient,
|
|
16682
17459
|
configDir,
|
|
17460
|
+
copy: params.copy,
|
|
16683
17461
|
deps,
|
|
16684
17462
|
email,
|
|
16685
17463
|
flags
|
|
16686
17464
|
});
|
|
16687
|
-
if (start.started) writeStartInstructions(start.pending);
|
|
17465
|
+
if (start.started) writeStartInstructions(start.pending, params.copy);
|
|
16688
17466
|
}
|
|
16689
17467
|
async function runSignupConfirmWithCredentialLock(params) {
|
|
16690
17468
|
const { configDir, flags } = params;
|
|
@@ -16692,6 +17470,7 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
16692
17470
|
if (!params.skipExistingCredentialCheck) await checkExistingCredentials({
|
|
16693
17471
|
apiBaseUrl1: flags["api-base-url-1"],
|
|
16694
17472
|
configDir,
|
|
17473
|
+
copy: params.copy,
|
|
16695
17474
|
deps,
|
|
16696
17475
|
flags
|
|
16697
17476
|
});
|
|
@@ -16702,6 +17481,7 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
16702
17481
|
const apiBaseUrl1 = requestConfig.resolvedApiBaseUrl1;
|
|
16703
17482
|
const pending = requirePendingSignupForEmail({
|
|
16704
17483
|
apiBaseUrl1,
|
|
17484
|
+
copy: params.copy,
|
|
16705
17485
|
configDir,
|
|
16706
17486
|
email: params.email
|
|
16707
17487
|
});
|
|
@@ -16716,7 +17496,7 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
16716
17496
|
});
|
|
16717
17497
|
if (verified.data) {
|
|
16718
17498
|
const signup = unwrapData(verified.data);
|
|
16719
|
-
if (!signup) throw cliError("Primitive API returned an empty agent signup verification response.");
|
|
17499
|
+
if (!signup) throw cliError$1("Primitive API returned an empty agent signup verification response.");
|
|
16720
17500
|
saveSignupCredentials({
|
|
16721
17501
|
apiBaseUrl1,
|
|
16722
17502
|
configDir,
|
|
@@ -16730,10 +17510,10 @@ async function runSignupConfirmWithCredentialLock(params) {
|
|
|
16730
17510
|
}
|
|
16731
17511
|
const payload = extractErrorPayload(verified.error);
|
|
16732
17512
|
const code = extractErrorCode(payload);
|
|
16733
|
-
if (code === INVALID_VERIFICATION_CODE) throw cliError(
|
|
17513
|
+
if (code === INVALID_VERIFICATION_CODE) throw cliError$1(`Invalid verification code. Try again or run ${(params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY).resendCommand(params.email)}.`);
|
|
16734
17514
|
if (code === EXPIRED_TOKEN || code === INVALID_SIGNUP_TOKEN) deletePendingAgentSignup(configDir);
|
|
16735
17515
|
writeErrorWithHints(payload);
|
|
16736
|
-
throw cliError("Primitive agent signup failed while verifying the account.");
|
|
17516
|
+
throw cliError$1("Primitive agent signup failed while verifying the account.");
|
|
16737
17517
|
}
|
|
16738
17518
|
async function runSignupResendWithCredentialLock(params) {
|
|
16739
17519
|
const deps = params.deps ?? {};
|
|
@@ -16743,6 +17523,7 @@ async function runSignupResendWithCredentialLock(params) {
|
|
|
16743
17523
|
});
|
|
16744
17524
|
const pending = requirePendingSignupForEmail({
|
|
16745
17525
|
apiBaseUrl1: requestConfig.resolvedApiBaseUrl1,
|
|
17526
|
+
copy: params.copy,
|
|
16746
17527
|
configDir: params.configDir,
|
|
16747
17528
|
email: params.email
|
|
16748
17529
|
});
|
|
@@ -16857,7 +17638,7 @@ var SignupCommand = class SignupCommand extends Command {
|
|
|
16857
17638
|
try {
|
|
16858
17639
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
16859
17640
|
} catch (error) {
|
|
16860
|
-
throw cliError(error instanceof Error ? error.message : String(error));
|
|
17641
|
+
throw cliError$1(error instanceof Error ? error.message : String(error));
|
|
16861
17642
|
}
|
|
16862
17643
|
try {
|
|
16863
17644
|
await runSignupStartWithCredentialLock({
|
|
@@ -16902,7 +17683,7 @@ var SignupConfirmCommand = class SignupConfirmCommand extends Command {
|
|
|
16902
17683
|
try {
|
|
16903
17684
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
16904
17685
|
} catch (error) {
|
|
16905
|
-
throw cliError(error instanceof Error ? error.message : String(error));
|
|
17686
|
+
throw cliError$1(error instanceof Error ? error.message : String(error));
|
|
16906
17687
|
}
|
|
16907
17688
|
try {
|
|
16908
17689
|
await runSignupConfirmWithCredentialLock({
|
|
@@ -16935,7 +17716,7 @@ var SignupResendCommand = class SignupResendCommand extends Command {
|
|
|
16935
17716
|
try {
|
|
16936
17717
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
16937
17718
|
} catch (error) {
|
|
16938
|
-
throw cliError(error instanceof Error ? error.message : String(error));
|
|
17719
|
+
throw cliError$1(error instanceof Error ? error.message : String(error));
|
|
16939
17720
|
}
|
|
16940
17721
|
try {
|
|
16941
17722
|
await runSignupResendWithCredentialLock({
|
|
@@ -16959,7 +17740,7 @@ var SignupInteractiveCommand = class SignupInteractiveCommand extends Command {
|
|
|
16959
17740
|
try {
|
|
16960
17741
|
releaseCredentialsLock = acquireCliCredentialsLock(this.config.configDir);
|
|
16961
17742
|
} catch (error) {
|
|
16962
|
-
throw cliError(error instanceof Error ? error.message : String(error));
|
|
17743
|
+
throw cliError$1(error instanceof Error ? error.message : String(error));
|
|
16963
17744
|
}
|
|
16964
17745
|
try {
|
|
16965
17746
|
await runSignupInteractiveWithCredentialLock({
|
|
@@ -16972,6 +17753,167 @@ var SignupInteractiveCommand = class SignupInteractiveCommand extends Command {
|
|
|
16972
17753
|
}
|
|
16973
17754
|
};
|
|
16974
17755
|
//#endregion
|
|
17756
|
+
//#region src/oclif/commands/signin.ts
|
|
17757
|
+
function cliError(message) {
|
|
17758
|
+
return new Errors.CLIError(message, { exit: 1 });
|
|
17759
|
+
}
|
|
17760
|
+
const SIGNIN_OTP_COPY = {
|
|
17761
|
+
actionNoun: "sign-in",
|
|
17762
|
+
actionGerund: "signing in",
|
|
17763
|
+
confirmCommand: (email) => `signin otp confirm ${email} <code>`,
|
|
17764
|
+
resendCommand: (email) => `signin otp resend ${email}`,
|
|
17765
|
+
startCommand: (email) => `signin otp ${email}`
|
|
17766
|
+
};
|
|
17767
|
+
function acquireCredentialsLock(configDir) {
|
|
17768
|
+
try {
|
|
17769
|
+
return acquireCliCredentialsLock(configDir);
|
|
17770
|
+
} catch (error) {
|
|
17771
|
+
throw cliError(error instanceof Error ? error.message : String(error));
|
|
17772
|
+
}
|
|
17773
|
+
}
|
|
17774
|
+
function commonOtpStartFlags() {
|
|
17775
|
+
return {
|
|
17776
|
+
"accept-terms": Flags.boolean({ description: "Confirm acceptance of Primitive's Terms of Service and Privacy Policy" }),
|
|
17777
|
+
"api-base-url-1": Flags.string({
|
|
17778
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
17779
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
17780
|
+
hidden: true
|
|
17781
|
+
}),
|
|
17782
|
+
"device-name": Flags.string({ description: "Device name used for the created CLI OAuth session" }),
|
|
17783
|
+
force: Flags.boolean({
|
|
17784
|
+
char: "f",
|
|
17785
|
+
description: "Replace saved credentials or pending sign-in state when needed"
|
|
17786
|
+
}),
|
|
17787
|
+
"signup-code": Flags.string({
|
|
17788
|
+
description: "Signup code required to start OTP sign-in",
|
|
17789
|
+
env: "PRIMITIVE_SIGNUP_CODE"
|
|
17790
|
+
})
|
|
17791
|
+
};
|
|
17792
|
+
}
|
|
17793
|
+
var SigninCommand = class extends LoginCommand {
|
|
17794
|
+
static description = `Sign in to an existing Primitive account with browser approval and save an org-scoped OAuth session locally.
|
|
17795
|
+
|
|
17796
|
+
This is the canonical sign-in command. It defaults to the same browser approval flow as \`primitive signin browser\`. For email-code sign-in, use \`primitive signin otp <email> --signup-code <code>\`, then \`primitive signin otp confirm <email> <code>\`. For new account creation, use \`primitive signup <email>\`.`;
|
|
17797
|
+
static summary = "Sign in to an existing account";
|
|
17798
|
+
static examples = [
|
|
17799
|
+
"<%= config.bin %> signin",
|
|
17800
|
+
"<%= config.bin %> signin browser",
|
|
17801
|
+
"<%= config.bin %> signin --no-browser",
|
|
17802
|
+
"<%= config.bin %> signin otp user@example.com --signup-code invite-code --accept-terms",
|
|
17803
|
+
"<%= config.bin %> signin otp confirm user@example.com 123456"
|
|
17804
|
+
];
|
|
17805
|
+
retryCommand() {
|
|
17806
|
+
return "signin";
|
|
17807
|
+
}
|
|
17808
|
+
};
|
|
17809
|
+
var SigninBrowserCommand = class extends LoginCommand {
|
|
17810
|
+
static description = "Sign in to an existing Primitive account by opening Primitive in your browser and saving an org-scoped OAuth session locally.";
|
|
17811
|
+
static summary = "Sign in with browser approval";
|
|
17812
|
+
static examples = [
|
|
17813
|
+
"<%= config.bin %> signin browser",
|
|
17814
|
+
"<%= config.bin %> signin browser --device-name work-laptop",
|
|
17815
|
+
"<%= config.bin %> signin browser --no-browser",
|
|
17816
|
+
"<%= config.bin %> signin browser --force"
|
|
17817
|
+
];
|
|
17818
|
+
retryCommand() {
|
|
17819
|
+
return "signin browser";
|
|
17820
|
+
}
|
|
17821
|
+
};
|
|
17822
|
+
var SigninOtpCommand = class SigninOtpCommand extends Command {
|
|
17823
|
+
static args = { email: Args.string({
|
|
17824
|
+
description: "Email address to sign in with",
|
|
17825
|
+
required: false
|
|
17826
|
+
}) };
|
|
17827
|
+
static description = "Start email-code sign-in using Primitive's signup/auth OTP flow, send a verification code, and save the pending token locally. Requires a signup code.";
|
|
17828
|
+
static summary = "Start OTP sign-in";
|
|
17829
|
+
static examples = ["<%= config.bin %> signin otp user@example.com --signup-code invite-code --accept-terms", "<%= config.bin %> signin otp confirm user@example.com 123456"];
|
|
17830
|
+
static flags = commonOtpStartFlags();
|
|
17831
|
+
async run() {
|
|
17832
|
+
const { args, flags } = await this.parse(SigninOtpCommand);
|
|
17833
|
+
const releaseCredentialsLock = acquireCredentialsLock(this.config.configDir);
|
|
17834
|
+
try {
|
|
17835
|
+
await runSignupStartWithCredentialLock({
|
|
17836
|
+
configDir: this.config.configDir,
|
|
17837
|
+
copy: SIGNIN_OTP_COPY,
|
|
17838
|
+
email: args.email,
|
|
17839
|
+
flags
|
|
17840
|
+
});
|
|
17841
|
+
} finally {
|
|
17842
|
+
releaseCredentialsLock();
|
|
17843
|
+
}
|
|
17844
|
+
}
|
|
17845
|
+
};
|
|
17846
|
+
var SigninOtpConfirmCommand = class SigninOtpConfirmCommand extends Command {
|
|
17847
|
+
static args = {
|
|
17848
|
+
email: Args.string({
|
|
17849
|
+
description: "Email address used to start OTP sign-in",
|
|
17850
|
+
required: true
|
|
17851
|
+
}),
|
|
17852
|
+
code: Args.string({
|
|
17853
|
+
description: "Verification code from the sign-in email",
|
|
17854
|
+
required: true
|
|
17855
|
+
})
|
|
17856
|
+
};
|
|
17857
|
+
static description = "Confirm a pending OTP sign-in, create an OAuth session, and save CLI credentials locally.";
|
|
17858
|
+
static summary = "Confirm OTP sign-in";
|
|
17859
|
+
static examples = ["<%= config.bin %> signin otp confirm user@example.com 123456", "<%= config.bin %> signin otp confirm user@example.com 123456 --org-id 00000000-0000-4000-8000-000000000000"];
|
|
17860
|
+
static flags = {
|
|
17861
|
+
"api-base-url-1": Flags.string({
|
|
17862
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
17863
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
17864
|
+
hidden: true
|
|
17865
|
+
}),
|
|
17866
|
+
force: Flags.boolean({
|
|
17867
|
+
char: "f",
|
|
17868
|
+
description: "Replace saved credentials after verification"
|
|
17869
|
+
}),
|
|
17870
|
+
"org-id": Flags.string({ description: "Workspace id to target when the email belongs to multiple workspaces" })
|
|
17871
|
+
};
|
|
17872
|
+
async run() {
|
|
17873
|
+
const { args, flags } = await this.parse(SigninOtpConfirmCommand);
|
|
17874
|
+
const releaseCredentialsLock = acquireCredentialsLock(this.config.configDir);
|
|
17875
|
+
try {
|
|
17876
|
+
await runSignupConfirmWithCredentialLock({
|
|
17877
|
+
code: args.code,
|
|
17878
|
+
configDir: this.config.configDir,
|
|
17879
|
+
copy: SIGNIN_OTP_COPY,
|
|
17880
|
+
email: args.email,
|
|
17881
|
+
flags
|
|
17882
|
+
});
|
|
17883
|
+
} finally {
|
|
17884
|
+
releaseCredentialsLock();
|
|
17885
|
+
}
|
|
17886
|
+
}
|
|
17887
|
+
};
|
|
17888
|
+
var SigninOtpResendCommand = class SigninOtpResendCommand extends Command {
|
|
17889
|
+
static args = { email: Args.string({
|
|
17890
|
+
description: "Email address used to start OTP sign-in",
|
|
17891
|
+
required: true
|
|
17892
|
+
}) };
|
|
17893
|
+
static description = "Resend the verification code for a pending OTP sign-in.";
|
|
17894
|
+
static summary = "Resend OTP sign-in code";
|
|
17895
|
+
static examples = ["<%= config.bin %> signin otp resend user@example.com"];
|
|
17896
|
+
static flags = { "api-base-url-1": Flags.string({
|
|
17897
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
17898
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
17899
|
+
hidden: true
|
|
17900
|
+
}) };
|
|
17901
|
+
async run() {
|
|
17902
|
+
const { args, flags } = await this.parse(SigninOtpResendCommand);
|
|
17903
|
+
const releaseCredentialsLock = acquireCredentialsLock(this.config.configDir);
|
|
17904
|
+
try {
|
|
17905
|
+
await runSignupResendWithCredentialLock({
|
|
17906
|
+
configDir: this.config.configDir,
|
|
17907
|
+
copy: SIGNIN_OTP_COPY,
|
|
17908
|
+
email: args.email,
|
|
17909
|
+
flags
|
|
17910
|
+
});
|
|
17911
|
+
} finally {
|
|
17912
|
+
releaseCredentialsLock();
|
|
17913
|
+
}
|
|
17914
|
+
}
|
|
17915
|
+
};
|
|
17916
|
+
//#endregion
|
|
16975
17917
|
//#region src/oclif/commands/whoami.ts
|
|
16976
17918
|
var WhoamiCommand = class WhoamiCommand extends Command {
|
|
16977
17919
|
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.`;
|
|
@@ -17103,36 +18045,88 @@ function renderFishCompletion(binName) {
|
|
|
17103
18045
|
//#endregion
|
|
17104
18046
|
//#region src/oclif/index.ts
|
|
17105
18047
|
var ListOperationsCommand = class extends Command {
|
|
17106
|
-
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>`.";
|
|
18048
|
+
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>`.";
|
|
17107
18049
|
static summary = "List all generated API operations (JSON)";
|
|
17108
18050
|
async run() {
|
|
17109
18051
|
this.log(JSON.stringify(operationManifest, null, 2));
|
|
17110
18052
|
}
|
|
17111
18053
|
};
|
|
18054
|
+
function operationId(operation) {
|
|
18055
|
+
return `${operation.tagCommand}:${operation.command}`;
|
|
18056
|
+
}
|
|
18057
|
+
function normalizeLookupToken(value) {
|
|
18058
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
18059
|
+
}
|
|
18060
|
+
function unique(values) {
|
|
18061
|
+
return [...new Set(values)];
|
|
18062
|
+
}
|
|
18063
|
+
function operationLookupTokens(operation) {
|
|
18064
|
+
return unique([
|
|
18065
|
+
operationId(operation),
|
|
18066
|
+
operation.command,
|
|
18067
|
+
operation.operationId,
|
|
18068
|
+
operation.sdkName,
|
|
18069
|
+
`${operation.tagCommand}:${operation.operationId}`,
|
|
18070
|
+
`${operation.tagCommand}:${operation.sdkName}`
|
|
18071
|
+
]);
|
|
18072
|
+
}
|
|
18073
|
+
function levenshteinDistance(left, right) {
|
|
18074
|
+
if (left === right) return 0;
|
|
18075
|
+
if (left.length === 0) return right.length;
|
|
18076
|
+
if (right.length === 0) return left.length;
|
|
18077
|
+
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
18078
|
+
for (let leftIndex = 0; leftIndex < left.length; leftIndex += 1) {
|
|
18079
|
+
const current = [leftIndex + 1];
|
|
18080
|
+
for (let rightIndex = 0; rightIndex < right.length; rightIndex += 1) {
|
|
18081
|
+
const substitutionCost = left[leftIndex] === right[rightIndex] ? 0 : 1;
|
|
18082
|
+
current[rightIndex + 1] = Math.min(current[rightIndex] + 1, previous[rightIndex + 1] + 1, previous[rightIndex] + substitutionCost);
|
|
18083
|
+
}
|
|
18084
|
+
previous = current;
|
|
18085
|
+
}
|
|
18086
|
+
return previous[right.length] ?? Number.POSITIVE_INFINITY;
|
|
18087
|
+
}
|
|
18088
|
+
function scoreLookupToken(query, token) {
|
|
18089
|
+
const normalizedQuery = normalizeLookupToken(query);
|
|
18090
|
+
const normalizedToken = normalizeLookupToken(token);
|
|
18091
|
+
if (!normalizedQuery || !normalizedToken) return 0;
|
|
18092
|
+
if (normalizedQuery === normalizedToken) return 100;
|
|
18093
|
+
if (normalizedToken.includes(normalizedQuery)) return Math.max(50, 90 - (normalizedToken.length - normalizedQuery.length));
|
|
18094
|
+
if (normalizedQuery.includes(normalizedToken)) return Math.max(45, 80 - (normalizedQuery.length - normalizedToken.length));
|
|
18095
|
+
const distance = levenshteinDistance(normalizedQuery, normalizedToken);
|
|
18096
|
+
const maxLength = Math.max(normalizedQuery.length, normalizedToken.length);
|
|
18097
|
+
return Math.round((1 - distance / maxLength) * 75);
|
|
18098
|
+
}
|
|
18099
|
+
function scoreOperation(query, operation) {
|
|
18100
|
+
return Math.max(...operationLookupTokens(operation).map((token) => scoreLookupToken(query, token)));
|
|
18101
|
+
}
|
|
17112
18102
|
function lookupOperation(id) {
|
|
17113
18103
|
const trimmed = resolveOperationAlias(id.trim());
|
|
17114
|
-
const
|
|
17115
|
-
const tag = sep === -1 ? "" : trimmed.slice(0, sep);
|
|
17116
|
-
const cmd = sep === -1 ? trimmed : trimmed.slice(sep + 1);
|
|
17117
|
-
const match = operationManifest.find((op) => op.command === cmd && op.tagCommand === tag) ?? null;
|
|
18104
|
+
const match = operationManifest.find((op) => operationLookupTokens(op).some((token) => token === trimmed || normalizeLookupToken(token) === normalizeLookupToken(trimmed))) ?? null;
|
|
17118
18105
|
if (match) return {
|
|
17119
18106
|
match,
|
|
17120
18107
|
candidates: []
|
|
17121
18108
|
};
|
|
17122
18109
|
return {
|
|
17123
18110
|
match: null,
|
|
17124
|
-
candidates: operationManifest.
|
|
18111
|
+
candidates: operationManifest.map((op) => ({
|
|
18112
|
+
id: operationId(op),
|
|
18113
|
+
score: scoreOperation(trimmed, op)
|
|
18114
|
+
})).filter(({ score }) => score >= 45).sort((left, right) => right.score - left.score || left.id.localeCompare(right.id)).slice(0, 5).map(({ id }) => id)
|
|
17125
18115
|
};
|
|
17126
18116
|
}
|
|
17127
18117
|
var DescribeCommand = class DescribeCommand extends Command {
|
|
17128
18118
|
static args = { command: Args.string({
|
|
17129
|
-
description: "Command id to describe, e.g. `emails:list
|
|
18119
|
+
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.",
|
|
17130
18120
|
required: true
|
|
17131
18121
|
}) };
|
|
17132
18122
|
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.
|
|
17133
18123
|
|
|
17134
18124
|
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:
|
|
17135
18125
|
|
|
18126
|
+
# Domain setup records returned by add/verify
|
|
18127
|
+
primitive describe domains:add
|
|
18128
|
+
primitive describe addDomain
|
|
18129
|
+
|
|
17136
18130
|
# Which of EmailDetail's sender-shaped fields is canonical?
|
|
17137
18131
|
primitive describe emails:get | jq '.responseSchema.properties | keys'
|
|
17138
18132
|
primitive describe emails:get | jq -r '.responseSchema.properties.from_email.description'
|
|
@@ -17142,7 +18136,12 @@ var DescribeCommand = class DescribeCommand extends Command {
|
|
|
17142
18136
|
|
|
17143
18137
|
\`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.`;
|
|
17144
18138
|
static summary = "Describe a single API operation in detail";
|
|
17145
|
-
static examples = [
|
|
18139
|
+
static examples = [
|
|
18140
|
+
"<%= config.bin %> describe addDomain",
|
|
18141
|
+
"<%= config.bin %> describe domains:add",
|
|
18142
|
+
"<%= config.bin %> describe emails:get",
|
|
18143
|
+
"<%= config.bin %> describe sent:get"
|
|
18144
|
+
];
|
|
17146
18145
|
async run() {
|
|
17147
18146
|
const { args } = await this.parse(DescribeCommand);
|
|
17148
18147
|
const { match, candidates } = lookupOperation(args.command);
|
|
@@ -17175,9 +18174,6 @@ var CompletionCommand = class CompletionCommand extends Command {
|
|
|
17175
18174
|
await this.config.runCommand("autocomplete", [args.shell]);
|
|
17176
18175
|
}
|
|
17177
18176
|
};
|
|
17178
|
-
function commandId(operation) {
|
|
17179
|
-
return `${operation.tagCommand}:${operation.command}`;
|
|
17180
|
-
}
|
|
17181
18177
|
const CANONICAL_OPERATION_ALIASES = {
|
|
17182
18178
|
"account:show": "account:get-account",
|
|
17183
18179
|
"account:storage": "account:get-storage-stats",
|
|
@@ -17222,14 +18218,15 @@ const CANONICAL_OPERATION_ALIASES = {
|
|
|
17222
18218
|
};
|
|
17223
18219
|
const DESCRIBE_OPERATION_ALIASES = {
|
|
17224
18220
|
...CANONICAL_OPERATION_ALIASES,
|
|
18221
|
+
"domains:zone-file": "domains:download-domain-zone-file",
|
|
17225
18222
|
"functions:logs": "functions:list-function-logs",
|
|
17226
18223
|
reply: "sending:reply-to-email"
|
|
17227
18224
|
};
|
|
17228
18225
|
function resolveOperationAlias(id) {
|
|
17229
18226
|
return DESCRIBE_OPERATION_ALIASES[id] ?? id;
|
|
17230
18227
|
}
|
|
17231
|
-
const OVERRIDDEN_OPERATION_IDS = new Set(["functions:test-function"]);
|
|
17232
|
-
const generatedCommands = Object.fromEntries(operationManifest.filter((operation) => !OVERRIDDEN_OPERATION_IDS.has(
|
|
18228
|
+
const OVERRIDDEN_OPERATION_IDS = new Set(["domains:download-domain-zone-file", "functions:test-function"]);
|
|
18229
|
+
const generatedCommands = Object.fromEntries(operationManifest.filter((operation) => !OVERRIDDEN_OPERATION_IDS.has(operationId(operation))).map((operation) => [operationId(operation), createOperationCommand(operation)]));
|
|
17233
18230
|
const COMMANDS = {
|
|
17234
18231
|
completion: CompletionCommand,
|
|
17235
18232
|
"list-operations": ListOperationsCommand,
|
|
@@ -17243,6 +18240,11 @@ const COMMANDS = {
|
|
|
17243
18240
|
reply: ReplyCommand,
|
|
17244
18241
|
chat: ChatCommand,
|
|
17245
18242
|
login: LoginCommand,
|
|
18243
|
+
signin: SigninCommand,
|
|
18244
|
+
"signin:browser": SigninBrowserCommand,
|
|
18245
|
+
"signin:otp": SigninOtpCommand,
|
|
18246
|
+
"signin:otp:confirm": SigninOtpConfirmCommand,
|
|
18247
|
+
"signin:otp:resend": SigninOtpResendCommand,
|
|
17246
18248
|
signup: SignupCommand,
|
|
17247
18249
|
"signup:confirm": SignupConfirmCommand,
|
|
17248
18250
|
"signup:interactive": SignupInteractiveCommand,
|
|
@@ -17253,6 +18255,8 @@ const COMMANDS = {
|
|
|
17253
18255
|
"emails:latest": EmailsLatestCommand,
|
|
17254
18256
|
"emails:watch": EmailsWatchCommand,
|
|
17255
18257
|
"emails:wait": EmailsWaitCommand,
|
|
18258
|
+
"domains:zone-file": DomainsZoneFileCommand,
|
|
18259
|
+
"domains:download-domain-zone-file": DomainsZoneFileCommand,
|
|
17256
18260
|
"functions:init": FunctionsInitCommand,
|
|
17257
18261
|
"functions:templates": FunctionsTemplatesCommand,
|
|
17258
18262
|
"functions:deploy": FunctionsDeployCommand,
|