@primitivedotdev/cli 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/oclif/index.js +865 -11
  2. package/package.json +7 -1
@@ -3,9 +3,9 @@ import { Args, Command, Errors, Flags, ux } from "@oclif/core";
3
3
  import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import path, { basename, dirname, join, relative, resolve, sep } from "node:path";
6
- import { hostname } from "node:os";
7
6
  import process$1 from "node:process";
8
7
  import { createInterface } from "node:readline/promises";
8
+ import { hostname } from "node:os";
9
9
  import { spawn } from "node:child_process";
10
10
  //#region \0rolldown/runtime.js
11
11
  var __defProp = Object.defineProperty;
@@ -32,12 +32,14 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
32
32
  createFilter: () => createFilter,
33
33
  createFunction: () => createFunction,
34
34
  createFunctionSecret: () => createFunctionSecret,
35
+ createOrgSecret: () => createOrgSecret,
35
36
  deleteDomain: () => deleteDomain,
36
37
  deleteEmail: () => deleteEmail,
37
38
  deleteEndpoint: () => deleteEndpoint,
38
39
  deleteFilter: () => deleteFilter,
39
40
  deleteFunction: () => deleteFunction,
40
41
  deleteFunctionSecret: () => deleteFunctionSecret,
42
+ deleteOrgSecret: () => deleteOrgSecret,
41
43
  discardEmailContent: () => discardEmailContent,
42
44
  downloadAttachments: () => downloadAttachments,
43
45
  downloadDomainZoneFile: () => downloadDomainZoneFile,
@@ -63,6 +65,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
63
65
  listFunctionLogs: () => listFunctionLogs,
64
66
  listFunctionSecrets: () => listFunctionSecrets,
65
67
  listFunctions: () => listFunctions,
68
+ listOrgSecrets: () => listOrgSecrets,
66
69
  listSentEmails: () => listSentEmails,
67
70
  pollCliLogin: () => pollCliLogin,
68
71
  replayDelivery: () => replayDelivery,
@@ -76,6 +79,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
76
79
  sendEmail: () => sendEmail,
77
80
  setFunctionRoute: () => setFunctionRoute,
78
81
  setFunctionSecret: () => setFunctionSecret,
82
+ setOrgSecret: () => setOrgSecret,
79
83
  startAgentClaim: () => startAgentClaim,
80
84
  startAgentSignup: () => startAgentSignup,
81
85
  startCliLogin: () => startCliLogin,
@@ -1194,10 +1198,12 @@ const listFunctions = (options) => (options?.client ?? client).get({
1194
1198
  * each delivery and forwards the `Primitive-Signature` header to
1195
1199
  * the handler. Verify the raw request body with
1196
1200
  * `PRIMITIVE_WEBHOOK_SECRET` before parsing JSON; after verification
1197
- * the request body parses to an `email.received` event (see
1198
- * `EmailReceivedEvent` and the Webhook payload section for the full
1199
- * schema). Code is bundled before being uploaded; ship a single
1200
- * self-contained file rather than relying on external imports.
1201
+ * the request body parses to a webhook event whose `event` field is
1202
+ * `email.received` for normal inbound mail, or a machine-mail type
1203
+ * (`email.bounced`, `email.tls_report`, `email.dmarc_report`,
1204
+ * `email.dmarc_failure`) for bounces and reports. Code is bundled
1205
+ * before being uploaded; ship a single self-contained file rather
1206
+ * than relying on external imports.
1201
1207
  *
1202
1208
  * **Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`
1203
1209
  * (optional) is capped at 5 MiB UTF-8, stored with each deployment
@@ -1515,6 +1521,85 @@ const setFunctionSecret = (options) => (options.client ?? client).put({
1515
1521
  }
1516
1522
  });
1517
1523
  /**
1524
+ * List org-level (global) secrets
1525
+ *
1526
+ * Returns metadata for every org-level secret. Org secrets apply
1527
+ * to every function in the org and are read as `env.<KEY>` in
1528
+ * handlers. **Values are never returned.** Secret writes are
1529
+ * write-only. A function-level secret of the same name overrides
1530
+ * the org-level value for that function.
1531
+ *
1532
+ */
1533
+ const listOrgSecrets = (options) => (options?.client ?? client).get({
1534
+ security: [{
1535
+ scheme: "bearer",
1536
+ type: "http"
1537
+ }],
1538
+ url: "/org/secrets",
1539
+ ...options
1540
+ });
1541
+ /**
1542
+ * Create or update an org secret
1543
+ *
1544
+ * Idempotent insert-or-update keyed on `(org_id, key)`. Returns
1545
+ * 201 the first time the key is set, 200 on subsequent updates.
1546
+ * Values are encrypted at rest. A changed value lands in a
1547
+ * function only on that function's next deploy.
1548
+ *
1549
+ * Keys must match `^[A-Z_][A-Z0-9_]*$` (uppercase letters,
1550
+ * digits, underscores; first character is a letter or
1551
+ * underscore). Values are at most 4096 UTF-8 bytes. System-
1552
+ * managed keys are reserved and rejected.
1553
+ *
1554
+ */
1555
+ const createOrgSecret = (options) => (options.client ?? client).post({
1556
+ security: [{
1557
+ scheme: "bearer",
1558
+ type: "http"
1559
+ }],
1560
+ url: "/org/secrets",
1561
+ ...options,
1562
+ headers: {
1563
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1564
+ ...options.headers
1565
+ }
1566
+ });
1567
+ /**
1568
+ * Delete an org secret
1569
+ *
1570
+ * Removes the org secret. Functions keep the previous value until
1571
+ * each is redeployed. Returns 404 if the key did not exist.
1572
+ *
1573
+ */
1574
+ const deleteOrgSecret = (options) => (options.client ?? client).delete({
1575
+ security: [{
1576
+ scheme: "bearer",
1577
+ type: "http"
1578
+ }],
1579
+ url: "/org/secrets/{key}",
1580
+ ...options
1581
+ });
1582
+ /**
1583
+ * Set an org secret by key
1584
+ *
1585
+ * Path-keyed companion to `POST /org/secrets`. Idempotent:
1586
+ * returns 201 the first time the key is set, 200 on subsequent
1587
+ * updates. Same validation and write-only guarantees as POST.
1588
+ *
1589
+ */
1590
+ const setOrgSecret = (options) => (options.client ?? client).put({
1591
+ security: [{
1592
+ scheme: "bearer",
1593
+ type: "http"
1594
+ }],
1595
+ url: "/org/secrets/{key}",
1596
+ ...options,
1597
+ headers: {
1598
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
1599
+ ...options.headers
1600
+ }
1601
+ });
1602
+ /**
1518
1603
  * List a function's execution logs
1519
1604
  *
1520
1605
  * Returns the most recent `function_logs` rows for the function,
@@ -1618,7 +1703,7 @@ const openapiDocument = {
1618
1703
  },
1619
1704
  {
1620
1705
  "name": "Functions",
1621
- "description": "Deploy JavaScript handlers that run on inbound mail. Each function\nis a single ESM module whose default export is an object with an\nasync `fetch(request, env)` method, in the shape of a Workers-style\nhandler. Primitive signs each delivery and forwards the\n`Primitive-Signature` header to the handler; verify the raw request\nbody with `PRIMITIVE_WEBHOOK_SECRET` before trusting the parsed\n`email.received` event (see `EmailReceivedEvent` and the Webhook\npayload section for the full schema). Code runs on\nPrimitive's edge runtime; there is no infrastructure to manage.\nSecrets land in `env` as encrypted bindings and are refreshed on\nevery redeploy.\n"
1706
+ "description": "Deploy JavaScript handlers that run on inbound mail. Each function\nis a single ESM module whose default export is an object with an\nasync `fetch(request, env)` method, in the shape of a Workers-style\nhandler. Primitive signs each delivery and forwards the\n`Primitive-Signature` header to the handler; verify the raw request\nbody with `PRIMITIVE_WEBHOOK_SECRET` before trusting the parsed event.\nThe `event` field is `email.received` for normal inbound mail, or a\nmachine-mail type (`email.bounced`, `email.tls_report`,\n`email.dmarc_report`, `email.dmarc_failure`) for bounces and reports;\nthe payload shape is otherwise identical. Code runs on\nPrimitive's edge runtime; there is no infrastructure to manage.\nSecrets land in `env` as encrypted bindings and are refreshed on\nevery redeploy.\n"
1622
1707
  }
1623
1708
  ],
1624
1709
  "paths": {
@@ -2390,6 +2475,25 @@ const openapiDocument = {
2390
2475
  "format": "date-time"
2391
2476
  },
2392
2477
  "description": "Filter emails created on or before this timestamp"
2478
+ },
2479
+ {
2480
+ "name": "since",
2481
+ "in": "query",
2482
+ "schema": {
2483
+ "type": "string",
2484
+ "maxLength": 200
2485
+ },
2486
+ "description": "Forward-tail cursor. Returns rows that became visible AFTER this\ncursor, oldest-first, so a caller can stream new inbound mail by\nre-passing the cursor from each response. Mutually exclusive with\n`cursor` (which pages history newest-first). Pass the `meta.cursor`\nfrom the previous `since` response; an empty page means caught up.\n"
2487
+ },
2488
+ {
2489
+ "name": "wait",
2490
+ "in": "query",
2491
+ "schema": {
2492
+ "type": "integer",
2493
+ "minimum": 0,
2494
+ "maximum": 30
2495
+ },
2496
+ "description": "Long-poll: hold the request up to this many seconds waiting for new\nmail past `since`, returning as soon as any arrives (or an empty\npage when the wait elapses). Requires `since`. Omitted means no wait\n(returns immediately); the server treats an absent value as 0. NOT\ngiven an OpenAPI `default` on purpose: a default makes some\ngenerators (e.g. openapi-python-client) send `wait=0` on every call,\nwhich then fails the `wait` requires `since` check for plain history\nlistings.\n"
2393
2497
  }
2394
2498
  ],
2395
2499
  "responses": {
@@ -3329,7 +3433,7 @@ const openapiDocument = {
3329
3433
  "post": {
3330
3434
  "operationId": "createFunction",
3331
3435
  "summary": "Deploy a function",
3332
- "description": "Creates and deploys a new function. The handler must be a single\nESM module whose default export is an object with an async\n`fetch(request, env)` method (Workers-style). Primitive signs\neach delivery and forwards the `Primitive-Signature` header to\nthe handler. Verify the raw request body with\n`PRIMITIVE_WEBHOOK_SECRET` before parsing JSON; after verification\nthe request body parses to an `email.received` event (see\n`EmailReceivedEvent` and the Webhook payload section for the full\nschema). Code is bundled before being uploaded; ship a single\nself-contained file rather than relying on external imports.\n\n**Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`\n(optional) is capped at 5 MiB UTF-8, stored with each deployment\nattempt, and sent to the runtime so stack traces can resolve to\noriginal source files.\n\n**Routing.** On successful deploy, the function code is live\nin the runtime, but inbound mail will not reach it until at\nleast one route is bound. Routes are managed from the Primitive\ndashboard. A `deploy_status` of `deployed` means the script is\ninstalled, not that the function is receiving mail. The\ninternal runtime URL is not returned by the API and is not a\ncustomer-facing integration surface.\n\n**Secrets.** New functions ship with the managed secrets\n(`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`,\n`PRIMITIVE_API_BASE_URL`) already bound. Add user-set secrets via\n`POST /functions/{id}/secrets`; secret writes only land in the\nrunning handler on the next redeploy.\n",
3436
+ "description": "Creates and deploys a new function. The handler must be a single\nESM module whose default export is an object with an async\n`fetch(request, env)` method (Workers-style). Primitive signs\neach delivery and forwards the `Primitive-Signature` header to\nthe handler. Verify the raw request body with\n`PRIMITIVE_WEBHOOK_SECRET` before parsing JSON; after verification\nthe request body parses to a webhook event whose `event` field is\n`email.received` for normal inbound mail, or a machine-mail type\n(`email.bounced`, `email.tls_report`, `email.dmarc_report`,\n`email.dmarc_failure`) for bounces and reports. Code is bundled\nbefore being uploaded; ship a single self-contained file rather\nthan relying on external imports.\n\n**Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`\n(optional) is capped at 5 MiB UTF-8, stored with each deployment\nattempt, and sent to the runtime so stack traces can resolve to\noriginal source files.\n\n**Routing.** On successful deploy, the function code is live\nin the runtime, but inbound mail will not reach it until at\nleast one route is bound. Routes are managed from the Primitive\ndashboard. A `deploy_status` of `deployed` means the script is\ninstalled, not that the function is receiving mail. The\ninternal runtime URL is not returned by the API and is not a\ncustomer-facing integration surface.\n\n**Secrets.** New functions ship with the managed secrets\n(`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`,\n`PRIMITIVE_API_BASE_URL`) already bound. Add user-set secrets via\n`POST /functions/{id}/secrets`; secret writes only land in the\nrunning handler on the next redeploy.\n",
3333
3437
  "tags": ["Functions"],
3334
3438
  "requestBody": {
3335
3439
  "required": true,
@@ -3688,6 +3792,111 @@ const openapiDocument = {
3688
3792
  }
3689
3793
  }
3690
3794
  },
3795
+ "/org/secrets": {
3796
+ "get": {
3797
+ "operationId": "listOrgSecrets",
3798
+ "summary": "List org-level (global) secrets",
3799
+ "description": "Returns metadata for every org-level secret. Org secrets apply\nto every function in the org and are read as `env.<KEY>` in\nhandlers. **Values are never returned.** Secret writes are\nwrite-only. A function-level secret of the same name overrides\nthe org-level value for that function.\n",
3800
+ "tags": ["Functions"],
3801
+ "responses": {
3802
+ "200": {
3803
+ "description": "List of org secrets (metadata only, no values)",
3804
+ "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
3805
+ "type": "object",
3806
+ "properties": { "data": {
3807
+ "type": "object",
3808
+ "properties": { "items": {
3809
+ "type": "array",
3810
+ "items": { "$ref": "#/components/schemas/OrgSecretListItem" }
3811
+ } },
3812
+ "required": ["items"]
3813
+ } }
3814
+ }] } } }
3815
+ },
3816
+ "401": { "$ref": "#/components/responses/Unauthorized" }
3817
+ }
3818
+ },
3819
+ "post": {
3820
+ "operationId": "createOrgSecret",
3821
+ "summary": "Create or update an org secret",
3822
+ "description": "Idempotent insert-or-update keyed on `(org_id, key)`. Returns\n201 the first time the key is set, 200 on subsequent updates.\nValues are encrypted at rest. A changed value lands in a\nfunction only on that function's next deploy.\n\nKeys must match `^[A-Z_][A-Z0-9_]*$` (uppercase letters,\ndigits, underscores; first character is a letter or\nunderscore). Values are at most 4096 UTF-8 bytes. System-\nmanaged keys are reserved and rejected.\n",
3823
+ "tags": ["Functions"],
3824
+ "requestBody": {
3825
+ "required": true,
3826
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateOrgSecretInput" } } }
3827
+ },
3828
+ "responses": {
3829
+ "200": {
3830
+ "description": "Secret updated",
3831
+ "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
3832
+ "type": "object",
3833
+ "properties": { "data": { "$ref": "#/components/schemas/OrgSecretWriteResult" } }
3834
+ }] } } }
3835
+ },
3836
+ "201": {
3837
+ "description": "Secret created",
3838
+ "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
3839
+ "type": "object",
3840
+ "properties": { "data": { "$ref": "#/components/schemas/OrgSecretWriteResult" } }
3841
+ }] } } }
3842
+ },
3843
+ "400": { "$ref": "#/components/responses/ValidationError" },
3844
+ "401": { "$ref": "#/components/responses/Unauthorized" }
3845
+ }
3846
+ }
3847
+ },
3848
+ "/org/secrets/{key}": {
3849
+ "parameters": [{
3850
+ "name": "key",
3851
+ "in": "path",
3852
+ "required": true,
3853
+ "description": "Secret key. Must match `^[A-Z_][A-Z0-9_]*$`.",
3854
+ "schema": {
3855
+ "type": "string",
3856
+ "pattern": "^[A-Z_][A-Z0-9_]*$"
3857
+ }
3858
+ }],
3859
+ "put": {
3860
+ "operationId": "setOrgSecret",
3861
+ "summary": "Set an org secret by key",
3862
+ "description": "Path-keyed companion to `POST /org/secrets`. Idempotent:\nreturns 201 the first time the key is set, 200 on subsequent\nupdates. Same validation and write-only guarantees as POST.\n",
3863
+ "tags": ["Functions"],
3864
+ "requestBody": {
3865
+ "required": true,
3866
+ "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SetOrgSecretInput" } } }
3867
+ },
3868
+ "responses": {
3869
+ "200": {
3870
+ "description": "Secret updated",
3871
+ "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
3872
+ "type": "object",
3873
+ "properties": { "data": { "$ref": "#/components/schemas/OrgSecretWriteResult" } }
3874
+ }] } } }
3875
+ },
3876
+ "201": {
3877
+ "description": "Secret created",
3878
+ "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/SuccessEnvelope" }, {
3879
+ "type": "object",
3880
+ "properties": { "data": { "$ref": "#/components/schemas/OrgSecretWriteResult" } }
3881
+ }] } } }
3882
+ },
3883
+ "400": { "$ref": "#/components/responses/ValidationError" },
3884
+ "401": { "$ref": "#/components/responses/Unauthorized" }
3885
+ }
3886
+ },
3887
+ "delete": {
3888
+ "operationId": "deleteOrgSecret",
3889
+ "summary": "Delete an org secret",
3890
+ "description": "Removes the org secret. Functions keep the previous value until\neach is redeployed. Returns 404 if the key did not exist.\n",
3891
+ "tags": ["Functions"],
3892
+ "responses": {
3893
+ "204": { "description": "Secret deleted" },
3894
+ "400": { "$ref": "#/components/responses/ValidationError" },
3895
+ "401": { "$ref": "#/components/responses/Unauthorized" },
3896
+ "404": { "$ref": "#/components/responses/NotFound" }
3897
+ }
3898
+ }
3899
+ },
3691
3900
  "/functions/{id}/logs": {
3692
3901
  "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
3693
3902
  "get": {
@@ -4849,6 +5058,16 @@ const openapiDocument = {
4849
5058
  },
4850
5059
  "email": { "type": "string" },
4851
5060
  "plan": { "type": "string" },
5061
+ "limits": { "$ref": "#/components/schemas/PlanLimits" },
5062
+ "entitlements": {
5063
+ "type": "array",
5064
+ "items": { "type": "string" },
5065
+ "description": "Granted org entitlement keys (sorted). A headless caller reads its\ncapabilities here — e.g. an emailless agent seeing only\n[\"send_mail\", \"send_to_known_addresses\"] knows it is reply-only.\n"
5066
+ },
5067
+ "managed_inbox_address": {
5068
+ "type": ["string", "null"],
5069
+ "description": "The managed inbox FQDN to reply as, or null if the org has no managed inbox."
5070
+ },
4852
5071
  "created_at": {
4853
5072
  "type": "string",
4854
5073
  "format": "date-time"
@@ -4876,6 +5095,9 @@ const openapiDocument = {
4876
5095
  "id",
4877
5096
  "email",
4878
5097
  "plan",
5098
+ "limits",
5099
+ "entitlements",
5100
+ "managed_inbox_address",
4879
5101
  "created_at",
4880
5102
  "discard_content_on_webhook_confirmed"
4881
5103
  ]
@@ -7886,6 +8108,81 @@ const openapiDocument = {
7886
8108
  "updated_at",
7887
8109
  "created"
7888
8110
  ]
8111
+ },
8112
+ "OrgSecretListItem": {
8113
+ "type": "object",
8114
+ "description": "One row from GET /org/secrets. Org secrets are always user-set\n(there are no managed org secrets), so `created_at` /\n`updated_at` are always present.\n",
8115
+ "properties": {
8116
+ "key": { "type": "string" },
8117
+ "created_at": {
8118
+ "type": "string",
8119
+ "format": "date-time"
8120
+ },
8121
+ "updated_at": {
8122
+ "type": "string",
8123
+ "format": "date-time"
8124
+ }
8125
+ },
8126
+ "required": [
8127
+ "key",
8128
+ "created_at",
8129
+ "updated_at"
8130
+ ]
8131
+ },
8132
+ "CreateOrgSecretInput": {
8133
+ "type": "object",
8134
+ "additionalProperties": false,
8135
+ "description": "Body for POST /org/secrets.",
8136
+ "properties": {
8137
+ "key": {
8138
+ "type": "string",
8139
+ "pattern": "^[A-Z_][A-Z0-9_]*$",
8140
+ "description": "Uppercase letters, digits, and underscores. Must start with\na letter or underscore. System-managed keys are reserved.\n"
8141
+ },
8142
+ "value": {
8143
+ "type": "string",
8144
+ "minLength": 1,
8145
+ "maxLength": 4096,
8146
+ "description": "Secret value, up to 4096 UTF-8 bytes. Encrypted at rest.\nNever returned by any read endpoint.\n"
8147
+ }
8148
+ },
8149
+ "required": ["key", "value"]
8150
+ },
8151
+ "SetOrgSecretInput": {
8152
+ "type": "object",
8153
+ "additionalProperties": false,
8154
+ "description": "Body for PUT /org/secrets/{key}. Key comes from the path.",
8155
+ "properties": { "value": {
8156
+ "type": "string",
8157
+ "minLength": 1,
8158
+ "maxLength": 4096
8159
+ } },
8160
+ "required": ["value"]
8161
+ },
8162
+ "OrgSecretWriteResult": {
8163
+ "type": "object",
8164
+ "description": "Returned by POST and PUT org secret routes.",
8165
+ "properties": {
8166
+ "key": { "type": "string" },
8167
+ "created_at": {
8168
+ "type": "string",
8169
+ "format": "date-time"
8170
+ },
8171
+ "updated_at": {
8172
+ "type": "string",
8173
+ "format": "date-time"
8174
+ },
8175
+ "created": {
8176
+ "type": "boolean",
8177
+ "description": "True if this call inserted a new row, false if it updated an existing one."
8178
+ }
8179
+ },
8180
+ "required": [
8181
+ "key",
8182
+ "created_at",
8183
+ "updated_at",
8184
+ "created"
8185
+ ]
7889
8186
  }
7890
8187
  }
7891
8188
  }
@@ -7914,6 +8211,39 @@ const operationManifest = [
7914
8211
  },
7915
8212
  "email": { "type": "string" },
7916
8213
  "plan": { "type": "string" },
8214
+ "limits": {
8215
+ "type": "object",
8216
+ "description": "Plan-derived quota limits for an account.",
8217
+ "properties": {
8218
+ "storage_mb": { "type": "number" },
8219
+ "send_per_hour": { "type": "number" },
8220
+ "send_per_day": { "type": "number" },
8221
+ "api_per_minute": { "type": "number" },
8222
+ "webhooks_max_global": { "type": ["number", "null"] },
8223
+ "webhooks_per_domain": { "type": "boolean" },
8224
+ "filters_per_domain": { "type": "boolean" },
8225
+ "spam_thresholds_per_domain": { "type": "boolean" }
8226
+ },
8227
+ "required": [
8228
+ "storage_mb",
8229
+ "send_per_hour",
8230
+ "send_per_day",
8231
+ "api_per_minute",
8232
+ "webhooks_max_global",
8233
+ "webhooks_per_domain",
8234
+ "filters_per_domain",
8235
+ "spam_thresholds_per_domain"
8236
+ ]
8237
+ },
8238
+ "entitlements": {
8239
+ "type": "array",
8240
+ "items": { "type": "string" },
8241
+ "description": "Granted org entitlement keys (sorted). A headless caller reads its\ncapabilities here — e.g. an emailless agent seeing only\n[\"send_mail\", \"send_to_known_addresses\"] knows it is reply-only.\n"
8242
+ },
8243
+ "managed_inbox_address": {
8244
+ "type": ["string", "null"],
8245
+ "description": "The managed inbox FQDN to reply as, or null if the org has no managed inbox."
8246
+ },
7917
8247
  "created_at": {
7918
8248
  "type": "string",
7919
8249
  "format": "date-time"
@@ -7941,6 +8271,9 @@ const operationManifest = [
7941
8271
  "id",
7942
8272
  "email",
7943
8273
  "plan",
8274
+ "limits",
8275
+ "entitlements",
8276
+ "managed_inbox_address",
7944
8277
  "created_at",
7945
8278
  "discard_content_on_webhook_confirmed"
7946
8279
  ]
@@ -10397,7 +10730,23 @@ const operationManifest = [
10397
10730
  "name": "date_to",
10398
10731
  "required": false,
10399
10732
  "type": "string"
10400
- }
10733
+ },
10734
+ {
10735
+ "description": "Forward-tail cursor. Returns rows that became visible AFTER this\ncursor, oldest-first, so a caller can stream new inbound mail by\nre-passing the cursor from each response. Mutually exclusive with\n`cursor` (which pages history newest-first). Pass the `meta.cursor`\nfrom the previous `since` response; an empty page means caught up.\n",
10736
+ "enum": null,
10737
+ "name": "since",
10738
+ "required": false,
10739
+ "type": "string"
10740
+ },
10741
+ {
10742
+ "description": "Long-poll: hold the request up to this many seconds waiting for new\nmail past `since`, returning as soon as any arrives (or an empty\npage when the wait elapses). Requires `since`. Omitted means no wait\n(returns immediately); the server treats an absent value as 0. NOT\ngiven an OpenAPI `default` on purpose: a default makes some\ngenerators (e.g. openapi-python-client) send `wait=0` on every call,\nwhich then fails the `wait` requires `since` check for plain history\nlistings.\n",
10743
+ "enum": null,
10744
+ "maximum": 30,
10745
+ "minimum": 0,
10746
+ "name": "wait",
10747
+ "required": false,
10748
+ "type": "integer"
10749
+ }
10401
10750
  ],
10402
10751
  "requestSchema": null,
10403
10752
  "responseSchema": {
@@ -11406,7 +11755,7 @@ const operationManifest = [
11406
11755
  "binaryResponse": false,
11407
11756
  "bodyRequired": true,
11408
11757
  "command": "create-function",
11409
- "description": "Creates and deploys a new function. The handler must be a single\nESM module whose default export is an object with an async\n`fetch(request, env)` method (Workers-style). Primitive signs\neach delivery and forwards the `Primitive-Signature` header to\nthe handler. Verify the raw request body with\n`PRIMITIVE_WEBHOOK_SECRET` before parsing JSON; after verification\nthe request body parses to an `email.received` event (see\n`EmailReceivedEvent` and the Webhook payload section for the full\nschema). Code is bundled before being uploaded; ship a single\nself-contained file rather than relying on external imports.\n\n**Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`\n(optional) is capped at 5 MiB UTF-8, stored with each deployment\nattempt, and sent to the runtime so stack traces can resolve to\noriginal source files.\n\n**Routing.** On successful deploy, the function code is live\nin the runtime, but inbound mail will not reach it until at\nleast one route is bound. Routes are managed from the Primitive\ndashboard. A `deploy_status` of `deployed` means the script is\ninstalled, not that the function is receiving mail. The\ninternal runtime URL is not returned by the API and is not a\ncustomer-facing integration surface.\n\n**Secrets.** New functions ship with the managed secrets\n(`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`,\n`PRIMITIVE_API_BASE_URL`) already bound. Add user-set secrets via\n`POST /functions/{id}/secrets`; secret writes only land in the\nrunning handler on the next redeploy.\n",
11758
+ "description": "Creates and deploys a new function. The handler must be a single\nESM module whose default export is an object with an async\n`fetch(request, env)` method (Workers-style). Primitive signs\neach delivery and forwards the `Primitive-Signature` header to\nthe handler. Verify the raw request body with\n`PRIMITIVE_WEBHOOK_SECRET` before parsing JSON; after verification\nthe request body parses to a webhook event whose `event` field is\n`email.received` for normal inbound mail, or a machine-mail type\n(`email.bounced`, `email.tls_report`, `email.dmarc_report`,\n`email.dmarc_failure`) for bounces and reports. Code is bundled\nbefore being uploaded; ship a single self-contained file rather\nthan relying on external imports.\n\n**Code limits.** `code` is capped at 1 MiB UTF-8. `sourceMap`\n(optional) is capped at 5 MiB UTF-8, stored with each deployment\nattempt, and sent to the runtime so stack traces can resolve to\noriginal source files.\n\n**Routing.** On successful deploy, the function code is live\nin the runtime, but inbound mail will not reach it until at\nleast one route is bound. Routes are managed from the Primitive\ndashboard. A `deploy_status` of `deployed` means the script is\ninstalled, not that the function is receiving mail. The\ninternal runtime URL is not returned by the API and is not a\ncustomer-facing integration surface.\n\n**Secrets.** New functions ship with the managed secrets\n(`PRIMITIVE_WEBHOOK_SECRET`, `PRIMITIVE_API_KEY`,\n`PRIMITIVE_API_BASE_URL`) already bound. Add user-set secrets via\n`POST /functions/{id}/secrets`; secret writes only land in the\nrunning handler on the next redeploy.\n",
11410
11759
  "hasJsonBody": true,
11411
11760
  "method": "POST",
11412
11761
  "operationId": "createFunction",
@@ -11538,6 +11887,66 @@ const operationManifest = [
11538
11887
  "tag": "Functions",
11539
11888
  "tagCommand": "functions"
11540
11889
  },
11890
+ {
11891
+ "binaryResponse": false,
11892
+ "bodyRequired": true,
11893
+ "command": "create-org-secret",
11894
+ "description": "Idempotent insert-or-update keyed on `(org_id, key)`. Returns\n201 the first time the key is set, 200 on subsequent updates.\nValues are encrypted at rest. A changed value lands in a\nfunction only on that function's next deploy.\n\nKeys must match `^[A-Z_][A-Z0-9_]*$` (uppercase letters,\ndigits, underscores; first character is a letter or\nunderscore). Values are at most 4096 UTF-8 bytes. System-\nmanaged keys are reserved and rejected.\n",
11895
+ "hasJsonBody": true,
11896
+ "method": "POST",
11897
+ "operationId": "createOrgSecret",
11898
+ "path": "/org/secrets",
11899
+ "pathParams": [],
11900
+ "queryParams": [],
11901
+ "requestSchema": {
11902
+ "type": "object",
11903
+ "additionalProperties": false,
11904
+ "description": "Body for POST /org/secrets.",
11905
+ "properties": {
11906
+ "key": {
11907
+ "type": "string",
11908
+ "pattern": "^[A-Z_][A-Z0-9_]*$",
11909
+ "description": "Uppercase letters, digits, and underscores. Must start with\na letter or underscore. System-managed keys are reserved.\n"
11910
+ },
11911
+ "value": {
11912
+ "type": "string",
11913
+ "minLength": 1,
11914
+ "maxLength": 4096,
11915
+ "description": "Secret value, up to 4096 UTF-8 bytes. Encrypted at rest.\nNever returned by any read endpoint.\n"
11916
+ }
11917
+ },
11918
+ "required": ["key", "value"]
11919
+ },
11920
+ "responseSchema": {
11921
+ "type": "object",
11922
+ "description": "Returned by POST and PUT org secret routes.",
11923
+ "properties": {
11924
+ "key": { "type": "string" },
11925
+ "created_at": {
11926
+ "type": "string",
11927
+ "format": "date-time"
11928
+ },
11929
+ "updated_at": {
11930
+ "type": "string",
11931
+ "format": "date-time"
11932
+ },
11933
+ "created": {
11934
+ "type": "boolean",
11935
+ "description": "True if this call inserted a new row, false if it updated an existing one."
11936
+ }
11937
+ },
11938
+ "required": [
11939
+ "key",
11940
+ "created_at",
11941
+ "updated_at",
11942
+ "created"
11943
+ ]
11944
+ },
11945
+ "sdkName": "createOrgSecret",
11946
+ "summary": "Create or update an org secret",
11947
+ "tag": "Functions",
11948
+ "tagCommand": "functions"
11949
+ },
11541
11950
  {
11542
11951
  "binaryResponse": false,
11543
11952
  "bodyRequired": false,
@@ -11592,6 +12001,30 @@ const operationManifest = [
11592
12001
  "tag": "Functions",
11593
12002
  "tagCommand": "functions"
11594
12003
  },
12004
+ {
12005
+ "binaryResponse": false,
12006
+ "bodyRequired": false,
12007
+ "command": "delete-org-secret",
12008
+ "description": "Removes the org secret. Functions keep the previous value until\neach is redeployed. Returns 404 if the key did not exist.\n",
12009
+ "hasJsonBody": false,
12010
+ "method": "DELETE",
12011
+ "operationId": "deleteOrgSecret",
12012
+ "path": "/org/secrets/{key}",
12013
+ "pathParams": [{
12014
+ "description": "Secret key. Must match `^[A-Z_][A-Z0-9_]*$`.",
12015
+ "enum": null,
12016
+ "name": "key",
12017
+ "required": true,
12018
+ "type": "string"
12019
+ }],
12020
+ "queryParams": [],
12021
+ "requestSchema": null,
12022
+ "responseSchema": null,
12023
+ "sdkName": "deleteOrgSecret",
12024
+ "summary": "Delete an org secret",
12025
+ "tag": "Functions",
12026
+ "tagCommand": "functions"
12027
+ },
11595
12028
  {
11596
12029
  "binaryResponse": false,
11597
12030
  "bodyRequired": false,
@@ -12472,6 +12905,50 @@ const operationManifest = [
12472
12905
  "tag": "Functions",
12473
12906
  "tagCommand": "functions"
12474
12907
  },
12908
+ {
12909
+ "binaryResponse": false,
12910
+ "bodyRequired": false,
12911
+ "command": "list-org-secrets",
12912
+ "description": "Returns metadata for every org-level secret. Org secrets apply\nto every function in the org and are read as `env.<KEY>` in\nhandlers. **Values are never returned.** Secret writes are\nwrite-only. A function-level secret of the same name overrides\nthe org-level value for that function.\n",
12913
+ "hasJsonBody": false,
12914
+ "method": "GET",
12915
+ "operationId": "listOrgSecrets",
12916
+ "path": "/org/secrets",
12917
+ "pathParams": [],
12918
+ "queryParams": [],
12919
+ "requestSchema": null,
12920
+ "responseSchema": {
12921
+ "type": "object",
12922
+ "properties": { "items": {
12923
+ "type": "array",
12924
+ "items": {
12925
+ "type": "object",
12926
+ "description": "One row from GET /org/secrets. Org secrets are always user-set\n(there are no managed org secrets), so `created_at` /\n`updated_at` are always present.\n",
12927
+ "properties": {
12928
+ "key": { "type": "string" },
12929
+ "created_at": {
12930
+ "type": "string",
12931
+ "format": "date-time"
12932
+ },
12933
+ "updated_at": {
12934
+ "type": "string",
12935
+ "format": "date-time"
12936
+ }
12937
+ },
12938
+ "required": [
12939
+ "key",
12940
+ "created_at",
12941
+ "updated_at"
12942
+ ]
12943
+ }
12944
+ } },
12945
+ "required": ["items"]
12946
+ },
12947
+ "sdkName": "listOrgSecrets",
12948
+ "summary": "List org-level (global) secrets",
12949
+ "tag": "Functions",
12950
+ "tagCommand": "functions"
12951
+ },
12475
12952
  {
12476
12953
  "binaryResponse": false,
12477
12954
  "bodyRequired": true,
@@ -12660,6 +13137,64 @@ const operationManifest = [
12660
13137
  "tag": "Functions",
12661
13138
  "tagCommand": "functions"
12662
13139
  },
13140
+ {
13141
+ "binaryResponse": false,
13142
+ "bodyRequired": true,
13143
+ "command": "set-org-secret",
13144
+ "description": "Path-keyed companion to `POST /org/secrets`. Idempotent:\nreturns 201 the first time the key is set, 200 on subsequent\nupdates. Same validation and write-only guarantees as POST.\n",
13145
+ "hasJsonBody": true,
13146
+ "method": "PUT",
13147
+ "operationId": "setOrgSecret",
13148
+ "path": "/org/secrets/{key}",
13149
+ "pathParams": [{
13150
+ "description": "Secret key. Must match `^[A-Z_][A-Z0-9_]*$`.",
13151
+ "enum": null,
13152
+ "name": "key",
13153
+ "required": true,
13154
+ "type": "string"
13155
+ }],
13156
+ "queryParams": [],
13157
+ "requestSchema": {
13158
+ "type": "object",
13159
+ "additionalProperties": false,
13160
+ "description": "Body for PUT /org/secrets/{key}. Key comes from the path.",
13161
+ "properties": { "value": {
13162
+ "type": "string",
13163
+ "minLength": 1,
13164
+ "maxLength": 4096
13165
+ } },
13166
+ "required": ["value"]
13167
+ },
13168
+ "responseSchema": {
13169
+ "type": "object",
13170
+ "description": "Returned by POST and PUT org secret routes.",
13171
+ "properties": {
13172
+ "key": { "type": "string" },
13173
+ "created_at": {
13174
+ "type": "string",
13175
+ "format": "date-time"
13176
+ },
13177
+ "updated_at": {
13178
+ "type": "string",
13179
+ "format": "date-time"
13180
+ },
13181
+ "created": {
13182
+ "type": "boolean",
13183
+ "description": "True if this call inserted a new row, false if it updated an existing one."
13184
+ }
13185
+ },
13186
+ "required": [
13187
+ "key",
13188
+ "created_at",
13189
+ "updated_at",
13190
+ "created"
13191
+ ]
13192
+ },
13193
+ "sdkName": "setOrgSecret",
13194
+ "summary": "Set an org secret by key",
13195
+ "tag": "Functions",
13196
+ "tagCommand": "functions"
13197
+ },
12663
13198
  {
12664
13199
  "binaryResponse": false,
12665
13200
  "bodyRequired": false,
@@ -15344,6 +15879,76 @@ function canonicalizeCliReferences(description) {
15344
15879
  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'`");
15345
15880
  }
15346
15881
  //#endregion
15882
+ //#region src/oclif/commands/agent-upgrade.ts
15883
+ /**
15884
+ * Interactive upgrade of an emailless agent account to a full developer
15885
+ * account: starts the email claim, prompts for the emailed code, and verifies
15886
+ * it. Combines the generated `agent:claim` (start) and `agent:claim-verify`
15887
+ * into one flow with a prompt, mirroring the `signup` interactive command.
15888
+ * Authenticated by the agent's own API key (the org is taken from the key).
15889
+ */
15890
+ var AgentUpgradeCommand = class AgentUpgradeCommand extends Command {
15891
+ static description = "Upgrade an emailless agent account to a full developer account by confirming an email. Authenticated by the agent's own API key (PRIMITIVE_API_KEY).";
15892
+ static summary = "Upgrade an agent account to developer (email confirmation)";
15893
+ static examples = ["<%= config.bin %> agent upgrade --email you@example.com"];
15894
+ static flags = {
15895
+ email: Flags.string({ description: "Email to confirm. Prompted if omitted." }),
15896
+ code: Flags.string({ description: "Verification code from the email. Prompted if omitted." }),
15897
+ "api-key": Flags.string({
15898
+ env: "PRIMITIVE_API_KEY",
15899
+ description: "Agent API key (defaults to PRIMITIVE_API_KEY or saved credentials)."
15900
+ }),
15901
+ "api-base-url": Flags.string({ description: "Override the API base URL." })
15902
+ };
15903
+ async run() {
15904
+ const { flags } = await this.parse(AgentUpgradeCommand);
15905
+ const { apiClient } = await createAuthenticatedCliApiClient({
15906
+ apiKey: flags["api-key"],
15907
+ apiBaseUrl: flags["api-base-url"],
15908
+ configDir: this.config.configDir
15909
+ });
15910
+ const email = flags.email ?? await promptRequired$1("Email to confirm: ");
15911
+ const started = await startAgentClaim({
15912
+ body: { email },
15913
+ client: apiClient.client,
15914
+ responseStyle: "fields"
15915
+ });
15916
+ if (!started.data) {
15917
+ writeErrorWithHints(extractErrorPayload(started.error));
15918
+ this.exit(1);
15919
+ return;
15920
+ }
15921
+ process$1.stderr.write(`Verification code sent to ${email}.\n`);
15922
+ const verified = await verifyAgentClaim({
15923
+ body: { verification_code: flags.code ?? await promptRequired$1("Verification code: ") },
15924
+ client: apiClient.client,
15925
+ responseStyle: "fields"
15926
+ });
15927
+ const result = verified.data?.data;
15928
+ if (result) {
15929
+ this.log(JSON.stringify(result, null, 2));
15930
+ process$1.stderr.write(`Upgraded to ${result.plan}. Your API key and managed inbox carry over; the send cap is lifted.\n`);
15931
+ return;
15932
+ }
15933
+ writeErrorWithHints(extractErrorPayload(verified.error));
15934
+ this.exit(1);
15935
+ }
15936
+ };
15937
+ async function promptRequired$1(question) {
15938
+ const rl = createInterface({
15939
+ input: process$1.stdin,
15940
+ output: process$1.stderr
15941
+ });
15942
+ try {
15943
+ for (;;) {
15944
+ const answer = (await rl.question(question)).trim();
15945
+ if (answer) return answer;
15946
+ }
15947
+ } finally {
15948
+ rl.close();
15949
+ }
15950
+ }
15951
+ //#endregion
15347
15952
  //#region src/oclif/attachments.ts
15348
15953
  function readAttachmentBytes(path, readFile) {
15349
15954
  try {
@@ -18777,8 +19382,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
18777
19382
  name: "Primitive Team",
18778
19383
  url: "https://primitive.dev"
18779
19384
  };
18780
- const SDK_VERSION_RANGE = "^1.3.0";
18781
- const CLI_VERSION_RANGE = "^1.3.0";
19385
+ const SDK_VERSION_RANGE = "^1.5.0";
19386
+ const CLI_VERSION_RANGE = "^1.5.0";
18782
19387
  const ESBUILD_VERSION_RANGE = "^0.27.0";
18783
19388
  function renderHandler() {
18784
19389
  return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
@@ -21923,6 +22528,251 @@ var LogoutCommand = class LogoutCommand extends Command {
21923
22528
  }
21924
22529
  };
21925
22530
  //#endregion
22531
+ //#region src/oclif/commands/org-secrets-shared.ts
22532
+ function orgSecretsUrl(baseUrl, key) {
22533
+ const base = `${baseUrl.replace(/\/$/, "")}/org/secrets`;
22534
+ return key ? `${base}/${encodeURIComponent(key)}` : base;
22535
+ }
22536
+ function orgSecretsAuthHeaders(requestHeaders, apiKey) {
22537
+ return {
22538
+ ...requestHeaders ?? {},
22539
+ ...apiKey ? { authorization: `Bearer ${apiKey}` } : {}
22540
+ };
22541
+ }
22542
+ async function orgSecretsErrorPayload(response) {
22543
+ if ((response.headers.get("content-type")?.toLowerCase() ?? "").includes("application/json")) return response.json().catch(() => ({
22544
+ code: "http_error",
22545
+ message: `HTTP ${response.status} ${response.statusText}`.trim()
22546
+ }));
22547
+ return {
22548
+ code: "http_error",
22549
+ message: (await response.text().catch(() => "")).trim() || `HTTP ${response.status} ${response.statusText}`.trim()
22550
+ };
22551
+ }
22552
+ async function runOrgSecretsRequest(fetchImpl, baseUrl, headers, op) {
22553
+ const url = op.kind === "remove" ? orgSecretsUrl(baseUrl, op.key) : orgSecretsUrl(baseUrl);
22554
+ const init = op.kind === "set" ? {
22555
+ method: "POST",
22556
+ headers: {
22557
+ ...headers,
22558
+ "content-type": "application/json"
22559
+ },
22560
+ body: JSON.stringify({
22561
+ key: op.key,
22562
+ value: op.value
22563
+ })
22564
+ } : op.kind === "remove" ? {
22565
+ method: "DELETE",
22566
+ headers
22567
+ } : { headers };
22568
+ let response;
22569
+ try {
22570
+ response = await fetchImpl(url, init);
22571
+ } catch (error) {
22572
+ return {
22573
+ kind: "error",
22574
+ payload: extractErrorPayload(error)
22575
+ };
22576
+ }
22577
+ if (!response.ok) return {
22578
+ kind: "error",
22579
+ payload: extractErrorPayload(await orgSecretsErrorPayload(response))
22580
+ };
22581
+ if (op.kind === "remove") return {
22582
+ kind: "ok",
22583
+ data: null
22584
+ };
22585
+ const body = await response.json().catch(() => ({}));
22586
+ if (op.kind === "list") return {
22587
+ kind: "ok",
22588
+ data: body.data?.items ?? []
22589
+ };
22590
+ return {
22591
+ kind: "ok",
22592
+ data: body.data ?? {}
22593
+ };
22594
+ }
22595
+ //#endregion
22596
+ //#region src/oclif/commands/org-secrets-list.ts
22597
+ var OrgSecretsListCommand = class OrgSecretsListCommand extends Command {
22598
+ static description = `List your organization's global secrets.
22599
+
22600
+ Global secrets apply to every function in the org and are read as
22601
+ \`env.<KEY>\` in handlers. Only the keys and timestamps are returned;
22602
+ the values are encrypted at rest and never surfaced.`;
22603
+ static summary = "List global secrets (keys only; values never returned)";
22604
+ static examples = ["<%= config.bin %> org secrets list"];
22605
+ static flags = {
22606
+ "api-key": Flags.string({
22607
+ description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
22608
+ env: "PRIMITIVE_API_KEY"
22609
+ }),
22610
+ "api-base-url": Flags.string({
22611
+ description: "Override the primary API base URL. Internal testing only; not documented to customers.",
22612
+ env: "PRIMITIVE_API_BASE_URL",
22613
+ hidden: true
22614
+ }),
22615
+ time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
22616
+ };
22617
+ async run() {
22618
+ const { flags } = await this.parse(OrgSecretsListCommand);
22619
+ await runWithTiming(flags.time, async () => {
22620
+ const { auth, baseUrlOverridden, requestConfig } = await createAuthenticatedCliApiClient({
22621
+ apiKey: flags["api-key"],
22622
+ apiBaseUrl: flags["api-base-url"],
22623
+ configDir: this.config.configDir
22624
+ });
22625
+ const outcome = await runOrgSecretsRequest(fetch, requestConfig.resolvedApiBaseUrl, orgSecretsAuthHeaders(requestConfig.headers, auth.apiKey), { kind: "list" });
22626
+ if (outcome.kind === "error") {
22627
+ writeErrorWithHints(outcome.payload);
22628
+ surfaceUnauthorizedHint({
22629
+ auth,
22630
+ baseUrlOverridden,
22631
+ configDir: this.config.configDir,
22632
+ payload: outcome.payload
22633
+ });
22634
+ process.exitCode = 1;
22635
+ return;
22636
+ }
22637
+ this.log(JSON.stringify(outcome.data, null, 2));
22638
+ });
22639
+ }
22640
+ };
22641
+ //#endregion
22642
+ //#region src/oclif/commands/org-secrets-remove.ts
22643
+ var OrgSecretsRemoveCommand = class OrgSecretsRemoveCommand extends Command {
22644
+ static description = `Delete a global secret.
22645
+
22646
+ Deployed functions keep the previous value until each is redeployed. A
22647
+ function that defines its own secret of the same name is unaffected.`;
22648
+ static summary = "Delete a global secret";
22649
+ static aliases = ["org:secrets:delete"];
22650
+ static examples = ["<%= config.bin %> org secrets remove --key STRIPE_KEY"];
22651
+ static flags = {
22652
+ "api-key": Flags.string({
22653
+ description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
22654
+ env: "PRIMITIVE_API_KEY"
22655
+ }),
22656
+ "api-base-url": Flags.string({
22657
+ description: "Override the primary API base URL. Internal testing only; not documented to customers.",
22658
+ env: "PRIMITIVE_API_BASE_URL",
22659
+ hidden: true
22660
+ }),
22661
+ key: Flags.string({
22662
+ description: "Global secret key to delete.",
22663
+ required: true
22664
+ }),
22665
+ time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
22666
+ };
22667
+ async run() {
22668
+ const { flags } = await this.parse(OrgSecretsRemoveCommand);
22669
+ await runWithTiming(flags.time, async () => {
22670
+ const { auth, baseUrlOverridden, requestConfig } = await createAuthenticatedCliApiClient({
22671
+ apiKey: flags["api-key"],
22672
+ apiBaseUrl: flags["api-base-url"],
22673
+ configDir: this.config.configDir
22674
+ });
22675
+ const outcome = await runOrgSecretsRequest(fetch, requestConfig.resolvedApiBaseUrl, orgSecretsAuthHeaders(requestConfig.headers, auth.apiKey), {
22676
+ kind: "remove",
22677
+ key: flags.key
22678
+ });
22679
+ if (outcome.kind === "error") {
22680
+ writeErrorWithHints(outcome.payload);
22681
+ surfaceUnauthorizedHint({
22682
+ auth,
22683
+ baseUrlOverridden,
22684
+ configDir: this.config.configDir,
22685
+ payload: outcome.payload
22686
+ });
22687
+ process.exitCode = 1;
22688
+ return;
22689
+ }
22690
+ process.stderr.write(`Global secret ${flags.key} deleted. Deployed functions keep the previous value until each is redeployed.\n`);
22691
+ });
22692
+ }
22693
+ };
22694
+ //#endregion
22695
+ //#region src/oclif/commands/org-secrets-set.ts
22696
+ var OrgSecretsSetCommand = class OrgSecretsSetCommand extends Command {
22697
+ static description = `Set a global secret available to every function as \`env.<KEY>\`.
22698
+
22699
+ Global secrets are read into each function at deploy time, so a new or
22700
+ changed value lands in a function only on its next redeploy. A function
22701
+ secret with the same key overrides the global value for that function.
22702
+
22703
+ Keys must match \`^[A-Z_][A-Z0-9_]*$\` (uppercase letters, digits,
22704
+ underscores; first character a letter or underscore). System-managed keys
22705
+ are reserved and rejected. ${SINGLE_SECRET_VALUE_SOURCE_DESCRIPTION}`;
22706
+ static summary = "Set a global secret shared across all functions";
22707
+ static examples = [
22708
+ "<%= config.bin %> org secrets set --key STRIPE_KEY --value sk_live_...",
22709
+ "<%= config.bin %> org secrets set --key OPENAI_KEY --value-from-env OPENAI_KEY",
22710
+ "printf '%s' \"$OPENAI_KEY\" | <%= config.bin %> org secrets set --key OPENAI_KEY --stdin"
22711
+ ];
22712
+ static flags = {
22713
+ "api-key": Flags.string({
22714
+ description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
22715
+ env: "PRIMITIVE_API_KEY"
22716
+ }),
22717
+ "api-base-url": Flags.string({
22718
+ description: "Override the primary API base URL. Internal testing only; not documented to customers.",
22719
+ env: "PRIMITIVE_API_BASE_URL",
22720
+ hidden: true
22721
+ }),
22722
+ key: Flags.string({
22723
+ description: "Secret key. Uppercase letters, digits, underscores; must start with a letter or underscore. System-managed keys are reserved.",
22724
+ required: true
22725
+ }),
22726
+ value: Flags.string({ description: "Secret value (up to 4096 UTF-8 bytes). Encrypted at rest. Visible in shell history and process argv; prefer a non-argv source for sensitive values." }),
22727
+ "value-from-env": Flags.string({ description: "Environment variable to read as the secret value. Example: --value-from-env OPENAI_KEY reads process.env.OPENAI_KEY." }),
22728
+ "value-file": Flags.string({ description: "UTF-8 file to read as the secret value. The full file contents become the value." }),
22729
+ "value-from-env-file": Flags.string({ description: "Dotenv-style file to read as the secret value. Use FILE to read --key from that file, or FILE:KEY to read a different key." }),
22730
+ stdin: Flags.boolean({ description: "Read the secret value from stdin. A single trailing line ending is stripped." }),
22731
+ time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
22732
+ };
22733
+ async run() {
22734
+ const { flags } = await this.parse(OrgSecretsSetCommand);
22735
+ await runWithTiming(flags.time, async () => {
22736
+ const { auth, baseUrlOverridden, requestConfig } = await createAuthenticatedCliApiClient({
22737
+ apiKey: flags["api-key"],
22738
+ apiBaseUrl: flags["api-base-url"],
22739
+ configDir: this.config.configDir
22740
+ });
22741
+ const resolved = resolveSingleSecretValue({
22742
+ key: flags.key,
22743
+ value: flags.value,
22744
+ valueFile: flags["value-file"],
22745
+ valueFromEnv: flags["value-from-env"],
22746
+ valueFromEnvFile: flags["value-from-env-file"],
22747
+ stdin: flags.stdin
22748
+ });
22749
+ if (resolved.kind === "error") {
22750
+ process.stderr.write(`${resolved.message}\n`);
22751
+ process.exitCode = 1;
22752
+ return;
22753
+ }
22754
+ const outcome = await runOrgSecretsRequest(fetch, requestConfig.resolvedApiBaseUrl, orgSecretsAuthHeaders(requestConfig.headers, auth.apiKey), {
22755
+ kind: "set",
22756
+ key: flags.key,
22757
+ value: resolved.value
22758
+ });
22759
+ if (outcome.kind === "error") {
22760
+ writeErrorWithHints(outcome.payload);
22761
+ surfaceUnauthorizedHint({
22762
+ auth,
22763
+ baseUrlOverridden,
22764
+ configDir: this.config.configDir,
22765
+ payload: outcome.payload
22766
+ });
22767
+ process.exitCode = 1;
22768
+ return;
22769
+ }
22770
+ this.log(JSON.stringify(outcome.data, null, 2));
22771
+ process.stderr.write(`Global secret ${flags.key} saved. Deployed functions pick it up on their next redeploy; a function secret of the same name overrides it.\n`);
22772
+ });
22773
+ }
22774
+ };
22775
+ //#endregion
21926
22776
  //#region src/oclif/message-body-sources.ts
21927
22777
  function defaultReadFile(path) {
21928
22778
  return readFileSync(path, "utf8");
@@ -23358,6 +24208,7 @@ const OVERRIDDEN_OPERATION_IDS = new Set([
23358
24208
  const generatedCommands = Object.fromEntries(operationManifest.filter((operation) => !OVERRIDDEN_OPERATION_IDS.has(operationId(operation))).map((operation) => [operationId(operation), createOperationCommand(operation)]));
23359
24209
  const COMMANDS = {
23360
24210
  completion: CompletionCommand,
24211
+ "agent:upgrade": AgentUpgradeCommand,
23361
24212
  "list-operations": ListOperationsCommand,
23362
24213
  config: ConfigCommand,
23363
24214
  "config:list": ConfigListCommand,
@@ -23410,6 +24261,9 @@ const COMMANDS = {
23410
24261
  "functions:deploy": FunctionsDeployCommand,
23411
24262
  "functions:redeploy": FunctionsRedeployCommand,
23412
24263
  "functions:set-secret": FunctionsSetSecretCommand,
24264
+ "org:secrets:list": OrgSecretsListCommand,
24265
+ "org:secrets:set": OrgSecretsSetCommand,
24266
+ "org:secrets:remove": OrgSecretsRemoveCommand,
23413
24267
  "functions:test": FunctionsTestFunctionCommand,
23414
24268
  "functions:test-function": FunctionsTestFunctionCommand,
23415
24269
  "functions:route-set": FunctionsRouteSetCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitivedotdev/cli",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "Official Primitive CLI: deploy Primitive Functions, send and inspect mail, manage endpoints, all from the terminal. Wraps the @primitivedotdev/sdk runtime client with one-shot commands.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -97,6 +97,12 @@
97
97
  },
98
98
  "functions": {
99
99
  "description": "Deploy JavaScript handlers that run on inbound mail. Prefer `primitive functions templates`, `primitive functions init`, `primitive functions deploy`, `primitive functions redeploy`, `primitive functions list`, `primitive functions get`, `primitive functions logs`, and `primitive functions set-secret`; generated API names remain available for compatibility."
100
+ },
101
+ "org": {
102
+ "description": "Manage org-level (global) resources shared across functions"
103
+ },
104
+ "org:secrets": {
105
+ "description": "Global secrets shared across every function. Use `primitive org secrets list|set|remove`. Changes land in a function on its next redeploy; a function secret of the same name overrides the global."
100
106
  }
101
107
  },
102
108
  "topicSeparator": " "