@primitivedotdev/cli 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,7 +48,7 @@ Use `primitive signin <email> --signup-code <code> --accept-terms`, then `primit
48
48
 
49
49
  Use `primitive logout --force` to remove local CLI credentials, pending email-code auth state, and stale credential locks without contacting Primitive. This is the recovery command when an interrupted auth command leaves the CLI saying another credential operation is already in progress.
50
50
 
51
- Use `primitive signup <email>` for new account creation, then `primitive signup confirm <email> <code>` with the emailed verification code. Non-interactive signup is available with `--signup-code` and `--accept-terms`.
51
+ Use `primitive signup <email>` for new account creation, then `primitive signup confirm <email> <code>` with the emailed verification code. Non-interactive signup is available with `--accept-terms` (pass `--signup-code <code>` too if you have one).
52
52
 
53
53
  ## Command style
54
54
 
@@ -124,10 +124,11 @@ const pollCliLogin = (options) => (options.client ?? client).post({
124
124
  /**
125
125
  * Start CLI account signup
126
126
  *
127
- * Starts a terminal-native CLI signup. The API validates the signup code,
128
- * creates a pending signup session, sends an email verification code, and
129
- * returns an opaque signup token used by the resend and verify steps. This
130
- * endpoint does not require an API key.
127
+ * Starts a terminal-native CLI signup. `signup_code` is optional;
128
+ * omit it to sign up without one. The API creates a pending signup
129
+ * session, sends an email verification code, and returns an opaque
130
+ * signup token used by the resend and verify steps. This endpoint
131
+ * does not require an API key.
131
132
  *
132
133
  */
133
134
  const startCliSignup = (options) => (options.client ?? client).post({
@@ -156,10 +157,12 @@ const resendCliSignupVerification = (options) => (options.client ?? client).post
156
157
  /**
157
158
  * Verify CLI signup and create OAuth session
158
159
  *
159
- * Verifies the email code for a CLI signup session, creates the account,
160
- * redeems the reserved signup code, creates an org-scoped OAuth CLI
161
- * session, and returns the token set exactly once. This endpoint does not
162
- * require an API key.
160
+ * Verifies the email code for a CLI signup session and creates the
161
+ * account. When the session was started with a `signup_code`, the
162
+ * reserved code is redeemed; sessions started without a code skip
163
+ * the redemption step. Either way an org-scoped OAuth CLI session
164
+ * is created and the token set is returned exactly once. This
165
+ * endpoint does not require an API key.
163
166
  *
164
167
  */
165
168
  const verifyCliSignup = (options) => (options.client ?? client).post({
@@ -173,10 +176,11 @@ const verifyCliSignup = (options) => (options.client ?? client).post({
173
176
  /**
174
177
  * Start agent account signup
175
178
  *
176
- * Starts an agent-native signup session. The API validates the signup code,
177
- * creates a pending signup session, sends an email verification code, and
178
- * returns an opaque signup token used by the resend and verify steps. This
179
- * endpoint does not require an API key.
179
+ * Starts an agent-native signup session. `signup_code` is optional;
180
+ * omit it to sign up without one. The API creates a pending signup
181
+ * session, sends an email verification code, and returns an opaque
182
+ * signup token used by the resend and verify steps. This endpoint
183
+ * does not require an API key.
180
184
  *
181
185
  */
182
186
  const startAgentSignup = (options) => (options.client ?? client).post({
@@ -205,11 +209,15 @@ const resendAgentSignupVerification = (options) => (options.client ?? client).po
205
209
  /**
206
210
  * Verify agent signup and create OAuth tokens
207
211
  *
208
- * Verifies the email code for an agent signup session, creates the account
209
- * when needed, redeems the reserved signup code, mints an org-scoped OAuth
210
- * session for CLI authentication, and returns the raw tokens exactly once.
211
- * For existing users, the optional `org_id` selects which accessible
212
- * workspace should receive the new session.
212
+ * Verifies the email code for an agent signup session and creates
213
+ * the account when needed. When the session was started with a
214
+ * `signup_code`, the reserved code is redeemed; sessions started
215
+ * without a code skip the redemption step. An org-scoped OAuth
216
+ * session for CLI authentication is minted and the raw tokens are
217
+ * returned exactly once. For existing users, the optional `org_id`
218
+ * selects which accessible workspace should receive the new
219
+ * session (no signup-code redemption is performed for existing
220
+ * users regardless of how the session was started).
213
221
  *
214
222
  */
215
223
  const verifyAgentSignup = (options) => (options.client ?? client).post({
@@ -1642,7 +1650,7 @@ const openapiDocument = {
1642
1650
  "/cli/signup/start": { "post": {
1643
1651
  "operationId": "startCliSignup",
1644
1652
  "summary": "Start CLI account signup",
1645
- "description": "Starts a terminal-native CLI signup. The API validates the signup code,\ncreates a pending signup session, sends an email verification code, and\nreturns an opaque signup token used by the resend and verify steps. This\nendpoint does not require an API key.\n",
1653
+ "description": "Starts a terminal-native CLI signup. `signup_code` is optional;\nomit it to sign up without one. The API creates a pending signup\nsession, sends an email verification code, and returns an opaque\nsignup token used by the resend and verify steps. This endpoint\ndoes not require an API key.\n",
1646
1654
  "tags": ["CLI"],
1647
1655
  "security": [],
1648
1656
  "requestBody": {
@@ -1704,7 +1712,7 @@ const openapiDocument = {
1704
1712
  "/cli/signup/verify": { "post": {
1705
1713
  "operationId": "verifyCliSignup",
1706
1714
  "summary": "Verify CLI signup and create OAuth session",
1707
- "description": "Verifies the email code for a CLI signup session, creates the account,\nredeems the reserved signup code, creates an org-scoped OAuth CLI\nsession, and returns the token set exactly once. This endpoint does not\nrequire an API key.\n",
1715
+ "description": "Verifies the email code for a CLI signup session and creates the\naccount. When the session was started with a `signup_code`, the\nreserved code is redeemed; sessions started without a code skip\nthe redemption step. Either way an org-scoped OAuth CLI session\nis created and the token set is returned exactly once. This\nendpoint does not require an API key.\n",
1708
1716
  "tags": ["CLI"],
1709
1717
  "security": [],
1710
1718
  "requestBody": {
@@ -1733,7 +1741,7 @@ const openapiDocument = {
1733
1741
  "/agent/signup/start": { "post": {
1734
1742
  "operationId": "startAgentSignup",
1735
1743
  "summary": "Start agent account signup",
1736
- "description": "Starts an agent-native signup session. The API validates the signup code,\ncreates a pending signup session, sends an email verification code, and\nreturns an opaque signup token used by the resend and verify steps. This\nendpoint does not require an API key.\n",
1744
+ "description": "Starts an agent-native signup session. `signup_code` is optional;\nomit it to sign up without one. The API creates a pending signup\nsession, sends an email verification code, and returns an opaque\nsignup token used by the resend and verify steps. This endpoint\ndoes not require an API key.\n",
1737
1745
  "tags": ["Agent"],
1738
1746
  "security": [],
1739
1747
  "requestBody": {
@@ -1795,7 +1803,7 @@ const openapiDocument = {
1795
1803
  "/agent/signup/verify": { "post": {
1796
1804
  "operationId": "verifyAgentSignup",
1797
1805
  "summary": "Verify agent signup and create OAuth tokens",
1798
- "description": "Verifies the email code for an agent signup session, creates the account\nwhen needed, redeems the reserved signup code, mints an org-scoped OAuth\nsession for CLI authentication, and returns the raw tokens exactly once.\nFor existing users, the optional `org_id` selects which accessible\nworkspace should receive the new session.\n",
1806
+ "description": "Verifies the email code for an agent signup session and creates\nthe account when needed. When the session was started with a\n`signup_code`, the reserved code is redeemed; sessions started\nwithout a code skip the redemption step. An org-scoped OAuth\nsession for CLI authentication is minted and the raw tokens are\nreturned exactly once. For existing users, the optional `org_id`\nselects which accessible workspace should receive the new\nsession (no signup-code redemption is performed for existing\nusers regardless of how the session was started).\n",
1799
1807
  "tags": ["Agent"],
1800
1808
  "security": [],
1801
1809
  "requestBody": {
@@ -4045,7 +4053,8 @@ const openapiDocument = {
4045
4053
  "signup_code": {
4046
4054
  "type": "string",
4047
4055
  "minLength": 1,
4048
- "maxLength": 128
4056
+ "maxLength": 128,
4057
+ "description": "Optional signup code. Omit if you do not have one."
4049
4058
  },
4050
4059
  "terms_accepted": {
4051
4060
  "type": "boolean",
@@ -4064,11 +4073,7 @@ const openapiDocument = {
4064
4073
  "description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
4065
4074
  }
4066
4075
  },
4067
- "required": [
4068
- "email",
4069
- "signup_code",
4070
- "terms_accepted"
4071
- ]
4076
+ "required": ["email", "terms_accepted"]
4072
4077
  },
4073
4078
  "CliSignupStartResult": {
4074
4079
  "type": "object",
@@ -4233,7 +4238,8 @@ const openapiDocument = {
4233
4238
  "signup_code": {
4234
4239
  "type": "string",
4235
4240
  "minLength": 1,
4236
- "maxLength": 128
4241
+ "maxLength": 128,
4242
+ "description": "Optional signup code. Omit if you do not have one."
4237
4243
  },
4238
4244
  "terms_accepted": {
4239
4245
  "type": "boolean",
@@ -4252,11 +4258,7 @@ const openapiDocument = {
4252
4258
  "description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
4253
4259
  }
4254
4260
  },
4255
- "required": [
4256
- "email",
4257
- "signup_code",
4258
- "terms_accepted"
4259
- ]
4261
+ "required": ["email", "terms_accepted"]
4260
4262
  },
4261
4263
  "AgentSignupStartResult": {
4262
4264
  "type": "object",
@@ -7781,7 +7783,7 @@ const operationManifest = [
7781
7783
  "binaryResponse": false,
7782
7784
  "bodyRequired": true,
7783
7785
  "command": "start-agent-signup",
7784
- "description": "Starts an agent-native signup session. The API validates the signup code,\ncreates a pending signup session, sends an email verification code, and\nreturns an opaque signup token used by the resend and verify steps. This\nendpoint does not require an API key.\n",
7786
+ "description": "Starts an agent-native signup session. `signup_code` is optional;\nomit it to sign up without one. The API creates a pending signup\nsession, sends an email verification code, and returns an opaque\nsignup token used by the resend and verify steps. This endpoint\ndoes not require an API key.\n",
7785
7787
  "hasJsonBody": true,
7786
7788
  "method": "POST",
7787
7789
  "operationId": "startAgentSignup",
@@ -7800,7 +7802,8 @@ const operationManifest = [
7800
7802
  "signup_code": {
7801
7803
  "type": "string",
7802
7804
  "minLength": 1,
7803
- "maxLength": 128
7805
+ "maxLength": 128,
7806
+ "description": "Optional signup code. Omit if you do not have one."
7804
7807
  },
7805
7808
  "terms_accepted": {
7806
7809
  "type": "boolean",
@@ -7819,11 +7822,7 @@ const operationManifest = [
7819
7822
  "description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
7820
7823
  }
7821
7824
  },
7822
- "required": [
7823
- "email",
7824
- "signup_code",
7825
- "terms_accepted"
7826
- ]
7825
+ "required": ["email", "terms_accepted"]
7827
7826
  },
7828
7827
  "responseSchema": {
7829
7828
  "type": "object",
@@ -7866,7 +7865,7 @@ const operationManifest = [
7866
7865
  "binaryResponse": false,
7867
7866
  "bodyRequired": true,
7868
7867
  "command": "verify-agent-signup",
7869
- "description": "Verifies the email code for an agent signup session, creates the account\nwhen needed, redeems the reserved signup code, mints an org-scoped OAuth\nsession for CLI authentication, and returns the raw tokens exactly once.\nFor existing users, the optional `org_id` selects which accessible\nworkspace should receive the new session.\n",
7868
+ "description": "Verifies the email code for an agent signup session and creates\nthe account when needed. When the session was started with a\n`signup_code`, the reserved code is redeemed; sessions started\nwithout a code skip the redemption step. An org-scoped OAuth\nsession for CLI authentication is minted and the raw tokens are\nreturned exactly once. For existing users, the optional `org_id`\nselects which accessible workspace should receive the new\nsession (no signup-code redemption is performed for existing\nusers regardless of how the session was started).\n",
7870
7869
  "hasJsonBody": true,
7871
7870
  "method": "POST",
7872
7871
  "operationId": "verifyAgentSignup",
@@ -8236,7 +8235,7 @@ const operationManifest = [
8236
8235
  "binaryResponse": false,
8237
8236
  "bodyRequired": true,
8238
8237
  "command": "start-cli-signup",
8239
- "description": "Starts a terminal-native CLI signup. The API validates the signup code,\ncreates a pending signup session, sends an email verification code, and\nreturns an opaque signup token used by the resend and verify steps. This\nendpoint does not require an API key.\n",
8238
+ "description": "Starts a terminal-native CLI signup. `signup_code` is optional;\nomit it to sign up without one. The API creates a pending signup\nsession, sends an email verification code, and returns an opaque\nsignup token used by the resend and verify steps. This endpoint\ndoes not require an API key.\n",
8240
8239
  "hasJsonBody": true,
8241
8240
  "method": "POST",
8242
8241
  "operationId": "startCliSignup",
@@ -8255,7 +8254,8 @@ const operationManifest = [
8255
8254
  "signup_code": {
8256
8255
  "type": "string",
8257
8256
  "minLength": 1,
8258
- "maxLength": 128
8257
+ "maxLength": 128,
8258
+ "description": "Optional signup code. Omit if you do not have one."
8259
8259
  },
8260
8260
  "terms_accepted": {
8261
8261
  "type": "boolean",
@@ -8274,11 +8274,7 @@ const operationManifest = [
8274
8274
  "description": "Optional client metadata stored with the signup session; serialized JSON must be 2048 bytes or fewer"
8275
8275
  }
8276
8276
  },
8277
- "required": [
8278
- "email",
8279
- "signup_code",
8280
- "terms_accepted"
8281
- ]
8277
+ "required": ["email", "terms_accepted"]
8282
8278
  },
8283
8279
  "responseSchema": {
8284
8280
  "type": "object",
@@ -8321,7 +8317,7 @@ const operationManifest = [
8321
8317
  "binaryResponse": false,
8322
8318
  "bodyRequired": true,
8323
8319
  "command": "verify-cli-signup",
8324
- "description": "Verifies the email code for a CLI signup session, creates the account,\nredeems the reserved signup code, creates an org-scoped OAuth CLI\nsession, and returns the token set exactly once. This endpoint does not\nrequire an API key.\n",
8320
+ "description": "Verifies the email code for a CLI signup session and creates the\naccount. When the session was started with a `signup_code`, the\nreserved code is redeemed; sessions started without a code skip\nthe redemption step. Either way an org-scoped OAuth CLI session\nis created and the token set is returned exactly once. This\nendpoint does not require an API key.\n",
8325
8321
  "hasJsonBody": true,
8326
8322
  "method": "POST",
8327
8323
  "operationId": "verifyCliSignup",
@@ -14566,7 +14562,7 @@ const OPERATION_HINTS = {
14566
14562
  updateFunction: "Tip: prefer `primitive functions redeploy --id <id> --file <bundle>` for file-input ergonomics. This raw command exists for callers passing JSON.",
14567
14563
  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.",
14568
14564
  setFunctionSecret: "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.",
14569
- startAgentSignup: "Tip: also pass --signup-code <code> (request from Primitive; invite-only during the agent beta) and --terms-accepted. Capture the signup_token from the response and feed it to `primitive agent verify-agent-signup --signup-token <token> --verification-code <6-digit-code>` (the verify flag accepts --code as an alias). The high-level `primitive signup <email>` command walks an interactive user through both steps with friendlier prompts.",
14565
+ startAgentSignup: "Tip: pass --terms-accepted, and optionally --signup-code <code> if you have one. Capture the signup_token from the response and feed it to `primitive agent verify-agent-signup --signup-token <token> --verification-code <6-digit-code>` (the verify flag accepts --code as an alias). The high-level `primitive signup <email>` command walks an interactive user through both steps with friendlier prompts.",
14570
14566
  verifyAgentSignup: "Tip: pass --verification-code <code> (or --code; both work). The response carries OAuth tokens but not your assigned inbox domain; run `primitive domains list` (or `primitive whoami`) after success to see the managed *.primitive.email address that routes to this account."
14571
14567
  };
14572
14568
  const OPERATION_FLAG_ALIASES = { verifyAgentSignup: { verification_code: ["code"] } };
@@ -16645,8 +16641,8 @@ var DomainsZoneFileCommand = class DomainsZoneFileCommand extends Command {
16645
16641
  };
16646
16642
  //#endregion
16647
16643
  //#region src/oclif/commands/emails-latest.ts
16648
- const DEFAULT_LIMIT$1 = 10;
16649
- const MAX_LIMIT$1 = 100;
16644
+ const DEFAULT_LIMIT$2 = 10;
16645
+ const MAX_LIMIT$2 = 100;
16650
16646
  const SUBJECT_DISPLAY_WIDTH = 50;
16651
16647
  const ADDRESS_DISPLAY_WIDTH = 32;
16652
16648
  const ID_DISPLAY_WIDTH_SHORT = 8;
@@ -16696,10 +16692,10 @@ var EmailsLatestCommand = class EmailsLatestCommand extends Command {
16696
16692
  hidden: true
16697
16693
  }),
16698
16694
  limit: Flags.integer({
16699
- description: `Number of rows to print (1-${MAX_LIMIT$1}, default ${DEFAULT_LIMIT$1}).`,
16700
- default: DEFAULT_LIMIT$1,
16695
+ description: `Number of rows to print (1-${MAX_LIMIT$2}, default ${DEFAULT_LIMIT$2}).`,
16696
+ default: DEFAULT_LIMIT$2,
16701
16697
  min: 1,
16702
- max: MAX_LIMIT$1
16698
+ max: MAX_LIMIT$2
16703
16699
  }),
16704
16700
  json: Flags.boolean({ description: "Print the raw response envelope (with full UUIDs and meta) as JSON on STDOUT instead of the text table. Useful for piping into `jq`, capturing ids for follow-up commands, or scripting." }),
16705
16701
  time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
@@ -17175,6 +17171,108 @@ async function runSourceDeploy(api, params) {
17175
17171
  result: data
17176
17172
  };
17177
17173
  }
17174
+ async function runSourceDeployWithSecrets(api, params) {
17175
+ const listed = await api.listFunctions();
17176
+ if (listed.error) return {
17177
+ kind: "error",
17178
+ payload: extractErrorPayload(listed.error),
17179
+ stage: "lookup"
17180
+ };
17181
+ const foundId = (listed.data?.data ?? []).find((f) => f.name === params.name)?.id ?? null;
17182
+ let functionId;
17183
+ let createPayload;
17184
+ if (foundId === null) {
17185
+ const created = await api.createFunction({
17186
+ files: params.files,
17187
+ name: params.name
17188
+ });
17189
+ if (created.error) return {
17190
+ kind: "error",
17191
+ payload: extractErrorPayload(created.error),
17192
+ stage: "create"
17193
+ };
17194
+ const data = created.data?.data;
17195
+ if (!data) return {
17196
+ kind: "error",
17197
+ payload: {
17198
+ code: "client_error",
17199
+ message: "Create returned no data"
17200
+ },
17201
+ stage: "create"
17202
+ };
17203
+ functionId = data.id;
17204
+ createPayload = data;
17205
+ } else functionId = foundId;
17206
+ const writtenSecrets = [];
17207
+ const succeededKeys = [];
17208
+ for (let i = 0; i < params.secrets.length; i++) {
17209
+ const pair = params.secrets[i];
17210
+ const pendingKeys = params.secrets.slice(i + 1).map((p) => p.key);
17211
+ const setResult = await api.setSecret({
17212
+ id: functionId,
17213
+ key: pair.key,
17214
+ value: pair.value
17215
+ });
17216
+ if (setResult.error) return {
17217
+ ...createPayload ? { created: createPayload } : {},
17218
+ failedKey: pair.key,
17219
+ functionId,
17220
+ kind: "error",
17221
+ payload: extractErrorPayload(setResult.error),
17222
+ pendingKeys,
17223
+ stage: "set-secret",
17224
+ succeededKeys
17225
+ };
17226
+ const secret = setResult.data?.data;
17227
+ if (!secret) return {
17228
+ ...createPayload ? { created: createPayload } : {},
17229
+ failedKey: pair.key,
17230
+ functionId,
17231
+ kind: "error",
17232
+ payload: {
17233
+ code: "client_error",
17234
+ message: "Secret write returned no data"
17235
+ },
17236
+ pendingKeys,
17237
+ stage: "set-secret",
17238
+ succeededKeys
17239
+ };
17240
+ writtenSecrets.push(secret);
17241
+ succeededKeys.push(pair.key);
17242
+ }
17243
+ const updated = await api.updateFunction({
17244
+ files: params.files,
17245
+ id: functionId
17246
+ });
17247
+ if (updated.error) return {
17248
+ ...createPayload ? { created: createPayload } : {},
17249
+ functionId,
17250
+ kind: "error",
17251
+ payload: extractErrorPayload(updated.error),
17252
+ stage: "secret-redeploy",
17253
+ succeededKeys
17254
+ };
17255
+ const redeployed = updated.data?.data;
17256
+ if (!redeployed) return {
17257
+ ...createPayload ? { created: createPayload } : {},
17258
+ functionId,
17259
+ kind: "error",
17260
+ payload: {
17261
+ code: "client_error",
17262
+ message: "Redeploy returned no data"
17263
+ },
17264
+ stage: "secret-redeploy",
17265
+ succeededKeys
17266
+ };
17267
+ return {
17268
+ kind: "ok",
17269
+ result: {
17270
+ action: foundId === null ? "created" : "redeployed",
17271
+ redeploy: redeployed,
17272
+ secrets: writtenSecrets
17273
+ }
17274
+ };
17275
+ }
17178
17276
  function renderBuildFailure(payload, write) {
17179
17277
  if (typeof payload !== "object" || payload === null) return false;
17180
17278
  const error = payload.error ?? payload;
@@ -17661,14 +17759,23 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
17661
17759
 
17662
17760
  Pass secret source flags to seed bindings in the same command. Keys
17663
17761
  must match \`^[A-Z_][A-Z0-9_]*$\` (uppercase letters, digits,
17664
- underscores; first character is a letter or underscore). With one
17665
- or more secrets the deploy fans out to multiple API calls:
17666
- create-function, set-secret per pair, then a final update-function
17667
- with the same bundle so the running handler picks up the bindings.
17668
- If a secret write fails after the create step the function exists
17669
- with whatever secrets succeeded and the redeploy has NOT fired;
17670
- re-run \`primitive functions set-secret\` for the missing keys, then
17671
- \`primitive functions redeploy\` to push them live. ${SECRET_SOURCE_FLAGS_DESCRIPTION}`;
17762
+ underscores; first character is a letter or underscore).
17763
+
17764
+ With one or more secrets the deploy fans out to multiple API calls.
17765
+ For --file (and for --source when no function with the given name
17766
+ exists yet): create-function, set-secret per pair, then a final
17767
+ update-function so the running handler picks up the bindings. For
17768
+ --source against an existing function name: the create-function step
17769
+ is replaced by an id lookup, then set-secret per pair, then a single
17770
+ update-function that binds the new code and the new secret env in
17771
+ one step (avoiding an intermediate redeploy that would briefly run
17772
+ the new code with the previous secret bindings).
17773
+
17774
+ If a secret write fails before the final redeploy, the function row
17775
+ carries whatever bindings landed but the running handler has NOT yet
17776
+ picked them up. Re-run \`primitive functions set-secret\` for the
17777
+ missing keys, then re-run \`primitive functions deploy\` (or
17778
+ \`functions redeploy\`) to push them live. ${SECRET_SOURCE_FLAGS_DESCRIPTION}`;
17672
17779
  static summary = "Deploy a new function from a bundled handler file";
17673
17780
  static examples = [
17674
17781
  "<%= config.bin %> functions deploy --name forwarder --file ./bundle.js",
@@ -17678,6 +17785,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
17678
17785
  "<%= config.bin %> functions deploy --name forwarder --file ./bundle.js --source-map-file ./bundle.js.map",
17679
17786
  "<%= config.bin %> functions deploy --name forwarder --file ./bundle.js --secret OPENAI_KEY=sk-... --secret OWNER_EMAIL=me@example.com",
17680
17787
  "<%= config.bin %> functions deploy --name forwarder --file ./bundle.js --secret-from-env OPENAI_KEY --secret-from-env-file .env.local:OWNER_EMAIL",
17788
+ "<%= config.bin %> functions deploy --name triage --source . --secret-from-env ANTHROPIC_API_KEY",
17681
17789
  "printf '%s' \"$OPENAI_KEY\" | <%= config.bin %> functions deploy --name forwarder --file ./bundle.js --secret-from-stdin OPENAI_KEY"
17682
17790
  ];
17683
17791
  static flags = {
@@ -17867,8 +17975,15 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
17867
17975
  });
17868
17976
  }
17869
17977
  async runSourceMode(flags, sourceDir) {
17870
- if ((flags.secret?.length ?? 0) > 0 || (flags["secret-from-env"]?.length ?? 0) > 0 || (flags["secret-from-file"]?.length ?? 0) > 0 || (flags["secret-from-env-file"]?.length ?? 0) > 0 || flags["secret-from-stdin"] !== void 0) {
17871
- process.stderr.write("Secret flags are not supported with --source yet. Deploy from source first, then set secrets with `primitive functions set-secret` and redeploy.\n");
17978
+ const parsedSecrets = resolveSecretFlags({
17979
+ fromEnv: flags["secret-from-env"] ?? [],
17980
+ fromEnvFile: flags["secret-from-env-file"] ?? [],
17981
+ fromFile: flags["secret-from-file"] ?? [],
17982
+ fromStdin: flags["secret-from-stdin"],
17983
+ inline: flags.secret ?? []
17984
+ });
17985
+ if (parsedSecrets.kind === "error") {
17986
+ process.stderr.write(`${parsedSecrets.message}\n`);
17872
17987
  process.exitCode = 1;
17873
17988
  return;
17874
17989
  }
@@ -17888,7 +18003,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
17888
18003
  baseUrlOverridden,
17889
18004
  configDir: this.config.configDir
17890
18005
  };
17891
- const outcome = await runSourceDeploy({
18006
+ const apiSurface = {
17892
18007
  createFunction: (p) => createFunction({
17893
18008
  body: {
17894
18009
  files: p.files,
@@ -17901,27 +18016,80 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
17901
18016
  client: apiClient.client,
17902
18017
  responseStyle: "fields"
17903
18018
  }),
18019
+ setSecret: (p) => setFunctionSecret({
18020
+ body: { value: p.value },
18021
+ client: apiClient.client,
18022
+ path: {
18023
+ id: p.id,
18024
+ key: p.key
18025
+ },
18026
+ responseStyle: "fields"
18027
+ }),
17904
18028
  updateFunction: (p) => updateFunction({
17905
18029
  body: { files: p.files },
17906
18030
  client: apiClient.client,
17907
18031
  path: { id: p.id },
17908
18032
  responseStyle: "fields"
17909
18033
  })
17910
- }, {
18034
+ };
18035
+ if (parsedSecrets.secrets.length === 0) {
18036
+ const outcome = await runSourceDeploy(apiSurface, {
18037
+ files: collected.files,
18038
+ name: flags.name
18039
+ });
18040
+ if (outcome.kind === "error") {
18041
+ renderBuildFailure(outcome.payload, (chunk) => process.stderr.write(chunk));
18042
+ writeErrorWithHints(outcome.payload);
18043
+ surfaceUnauthorizedHint({
18044
+ ...authFailureContext,
18045
+ payload: outcome.payload
18046
+ });
18047
+ process.exitCode = 1;
18048
+ return;
18049
+ }
18050
+ await this.finishSourceDeploy({
18051
+ apiClient,
18052
+ authFailureContext,
18053
+ flags,
18054
+ payload: outcome.result
18055
+ });
18056
+ return;
18057
+ }
18058
+ const secretsOutcome = await runSourceDeployWithSecrets(apiSurface, {
17911
18059
  files: collected.files,
17912
- name: flags.name
18060
+ name: flags.name,
18061
+ secrets: parsedSecrets.secrets
17913
18062
  });
17914
- if (outcome.kind === "error") {
17915
- renderBuildFailure(outcome.payload, (chunk) => process.stderr.write(chunk));
17916
- writeErrorWithHints(outcome.payload);
18063
+ if (secretsOutcome.kind === "error") {
18064
+ if (secretsOutcome.stage === "set-secret") {
18065
+ const succeeded = secretsOutcome.succeededKeys.length > 0 ? secretsOutcome.succeededKeys.join(", ") : "(none)";
18066
+ const pending = secretsOutcome.pendingKeys.length > 0 ? secretsOutcome.pendingKeys.join(", ") : "(none)";
18067
+ const allMissing = [secretsOutcome.failedKey, ...secretsOutcome.pendingKeys].join(", ");
18068
+ const createdClause = secretsOutcome.created ? `Function ${secretsOutcome.created.name} (${secretsOutcome.functionId}) was created` : `Function ${flags.name} (${secretsOutcome.functionId}) already existed`;
18069
+ const stagingWarning = secretsOutcome.succeededKeys.length > 0 ? ` Note: [${succeeded}] are now staged on the function row and will bind on the next deploy of this function (including one that does not pass --secret).` : "";
18070
+ process.stderr.write(`${createdClause}, but writing secret ${secretsOutcome.failedKey} failed; succeeded keys so far: ${succeeded}; keys not yet attempted: ${pending}. The redeploy is NOT yet live. Re-run \`primitive functions set-secret\` for each of [${allMissing}], then \`primitive functions deploy --source ${sourceDir} --name ${flags.name}\` to push them live.${stagingWarning}\n`);
18071
+ } else if (secretsOutcome.stage === "secret-redeploy") {
18072
+ const succeeded = secretsOutcome.succeededKeys.length > 0 ? secretsOutcome.succeededKeys.join(", ") : "(none)";
18073
+ const createdClause = secretsOutcome.created ? `Function ${secretsOutcome.created.name} (${secretsOutcome.functionId}) was created and` : `Function ${flags.name} (${secretsOutcome.functionId}) already existed and`;
18074
+ process.stderr.write(`${createdClause} secrets [${succeeded}] were written, but the final redeploy failed; the new bindings are NOT yet live. Re-run \`primitive functions deploy --source ${sourceDir} --name ${flags.name}\` once the cause is fixed.\n`);
18075
+ } else renderBuildFailure(secretsOutcome.payload, (chunk) => process.stderr.write(chunk));
18076
+ writeErrorWithHints(secretsOutcome.payload);
17917
18077
  surfaceUnauthorizedHint({
17918
18078
  ...authFailureContext,
17919
- payload: outcome.payload
18079
+ payload: secretsOutcome.payload
17920
18080
  });
17921
18081
  process.exitCode = 1;
17922
18082
  return;
17923
18083
  }
17924
- const payload = outcome.result;
18084
+ await this.finishSourceDeploy({
18085
+ apiClient,
18086
+ authFailureContext,
18087
+ flags,
18088
+ payload: secretsOutcome.result.redeploy
18089
+ });
18090
+ }
18091
+ async finishSourceDeploy(args) {
18092
+ const { apiClient, authFailureContext, flags, payload } = args;
17925
18093
  if (flags.wait) {
17926
18094
  const waitResult = await waitForFunctionDeploy({
17927
18095
  getFunction: (p) => getFunction({
@@ -17970,8 +18138,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
17970
18138
  name: "Primitive Team",
17971
18139
  url: "https://primitive.dev"
17972
18140
  };
17973
- const SDK_VERSION_RANGE = "^1.0.1";
17974
- const CLI_VERSION_RANGE = "^1.0.1";
18141
+ const SDK_VERSION_RANGE = "^1.2.0";
18142
+ const CLI_VERSION_RANGE = "^1.2.0";
17975
18143
  const ESBUILD_VERSION_RANGE = "^0.27.0";
17976
18144
  function renderHandler() {
17977
18145
  return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
@@ -20214,7 +20382,8 @@ const DEFAULT_SIGNUP_COMMAND_COPY = {
20214
20382
  actionGerund: "creating a new account",
20215
20383
  confirmCommand: (email) => `signup confirm ${email} <code>`,
20216
20384
  resendCommand: (email) => `signup resend ${email}`,
20217
- startCommand: (email) => `signup ${email}`
20385
+ startCommand: (email) => `signup ${email}`,
20386
+ codeRequired: false
20218
20387
  };
20219
20388
  function cliError$2(message) {
20220
20389
  return new Errors.CLIError(message, { exit: 1 });
@@ -20329,7 +20498,7 @@ function readPendingAgentSignupState(configDir, apiBaseUrl) {
20329
20498
  return pending;
20330
20499
  }
20331
20500
  function pendingSignupStartCommand(email) {
20332
- return `primitive signup ${email ?? "<email>"} --signup-code <invite-code> --accept-terms`;
20501
+ return `primitive signup ${email ?? "<email>"} --accept-terms`;
20333
20502
  }
20334
20503
  function buildSignupStatus(params) {
20335
20504
  const copy = params.copy ?? DEFAULT_SIGNUP_COMMAND_COPY;
@@ -20495,16 +20664,17 @@ async function startSignup(params) {
20495
20664
  throw cliError$2(`Pending ${copy.actionNoun} is for ${existingPending.email}. Run \`primitive signup status\` to inspect it, or \`primitive ${copy.startCommand(params.email)} --force\` to replace it.`);
20496
20665
  }
20497
20666
  if (params.flags.force) deletePendingAgentSignup(params.configDir);
20498
- const promptRequiredFn = params.deps.promptRequired ?? promptRequired;
20499
20667
  const confirmTermsFn = params.deps.confirmTerms ?? confirmTerms;
20668
+ const promptRequiredFn = params.deps.promptRequired ?? promptRequired;
20500
20669
  const startFn = params.deps.startAgentSignup ?? startAgentSignup;
20501
- const signupCode = params.flags["signup-code"] ?? await promptRequiredFn("Signup code: ");
20670
+ const rawSignupCode = params.flags["signup-code"];
20671
+ const signupCode = (rawSignupCode && rawSignupCode.trim().length > 0 ? rawSignupCode : void 0) ?? (copy.codeRequired ? await promptRequiredFn("Signup code: ") : void 0);
20502
20672
  if (!params.flags["accept-terms"]) await confirmTermsFn();
20503
20673
  const started = await startFn({
20504
20674
  body: {
20505
20675
  device_name: params.flags["device-name"] ?? hostname(),
20506
20676
  email: params.email,
20507
- signup_code: signupCode,
20677
+ ...signupCode ? { signup_code: signupCode } : {},
20508
20678
  terms_accepted: true
20509
20679
  },
20510
20680
  client: params.apiClient.client,
@@ -20734,7 +20904,7 @@ function commonStartFlags() {
20734
20904
  description: "Replace saved credentials or pending signup state when needed"
20735
20905
  }),
20736
20906
  "signup-code": Flags.string({
20737
- description: "Signup code required to create an account",
20907
+ description: "Optional signup code. Omit if you do not have one.",
20738
20908
  env: "PRIMITIVE_SIGNUP_CODE"
20739
20909
  })
20740
20910
  };
@@ -20748,6 +20918,7 @@ var SignupCommand = class SignupCommand extends Command {
20748
20918
  static summary = "Start account signup";
20749
20919
  static examples = [
20750
20920
  "<%= config.bin %> signup user@example.com",
20921
+ "<%= config.bin %> signup user@example.com --accept-terms",
20751
20922
  "<%= config.bin %> signup user@example.com --signup-code invite-code --accept-terms",
20752
20923
  "<%= config.bin %> signup confirm user@example.com 123456"
20753
20924
  ];
@@ -21223,8 +21394,8 @@ var ReplyCommand = class ReplyCommand extends Command {
21223
21394
  };
21224
21395
  //#endregion
21225
21396
  //#region src/oclif/commands/semantic-search.ts
21226
- const DEFAULT_LIMIT = 10;
21227
- const MAX_LIMIT = 100;
21397
+ const DEFAULT_LIMIT$1 = 10;
21398
+ const MAX_LIMIT$1 = 100;
21228
21399
  const SCORE_WIDTH = 7;
21229
21400
  const SOURCE_WIDTH = 4;
21230
21401
  const SUBJECT_WIDTH = 40;
@@ -21290,10 +21461,10 @@ var SemanticSearchCommand = class SemanticSearchCommand extends Command {
21290
21461
  "date-from": Flags.string({ description: "Only include mail at or after this ISO-8601 timestamp." }),
21291
21462
  "date-to": Flags.string({ description: "Only include mail at or before this ISO-8601 timestamp." }),
21292
21463
  limit: Flags.integer({
21293
- description: `Maximum results to return (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}).`,
21294
- default: DEFAULT_LIMIT,
21464
+ description: `Maximum results to return (1-${MAX_LIMIT$1}, default ${DEFAULT_LIMIT$1}).`,
21465
+ default: DEFAULT_LIMIT$1,
21295
21466
  min: 1,
21296
- max: MAX_LIMIT
21467
+ max: MAX_LIMIT$1
21297
21468
  }),
21298
21469
  cursor: Flags.string({ description: "Opaque pagination cursor from a prior response's meta.cursor." }),
21299
21470
  json: Flags.boolean({ description: "Print the raw response envelope as JSON on STDOUT instead of the text table." }),
@@ -21350,6 +21521,230 @@ var SemanticSearchCommand = class SemanticSearchCommand extends Command {
21350
21521
  }
21351
21522
  };
21352
21523
  //#endregion
21524
+ //#region src/oclif/commands/search.ts
21525
+ const DEFAULT_LIMIT = 10;
21526
+ const MAX_LIMIT = 100;
21527
+ const LEXICAL_ONLY_FLAGS = [
21528
+ "from",
21529
+ "to",
21530
+ "subject",
21531
+ "body",
21532
+ "domain",
21533
+ "domain-id",
21534
+ "has-attachment",
21535
+ "status",
21536
+ "sort",
21537
+ "snippet",
21538
+ "include-facets"
21539
+ ];
21540
+ var SearchCommand = class SearchCommand extends Command {
21541
+ static description = `Search received (default) or both received and sent mail (with --mode).
21542
+
21543
+ Default behavior is lexical full-text matching: the positional query is sent as \`q=<query>\` to the inbound search endpoint, which matches against subject, body, sender, and recipient in a single pass. Structured filters (--from, --to, --subject, --body, --domain, --has-attachment, --date-from, --date-to, --status) AND with the text query.
21544
+
21545
+ Pass --mode to switch to the cross-corpus semantic backend (covers inbound and outbound). \`--mode keyword\` is plain full-text; \`--mode semantic\` is embedding-only; \`--mode hybrid\` blends both. Semantic modes require the Pro plan with the semantic_search_enabled entitlement.
21546
+
21547
+ Output is a fixed-width text table by default (header on STDERR so rows stay grep/awk-friendly). Use --json for the raw envelope.`;
21548
+ static summary = "Search mail (lexical by default; --mode for semantic)";
21549
+ static examples = [
21550
+ "<%= config.bin %> search \"invoice\"",
21551
+ "<%= config.bin %> search \"renewal\" --from acme.com",
21552
+ "<%= config.bin %> search \"kickoff\" --mode hybrid",
21553
+ "<%= config.bin %> search \"shipping\" --mode keyword --corpus outbound",
21554
+ "<%= config.bin %> search \"needle\" --json | jq '.data[0].id'"
21555
+ ];
21556
+ static args = { query: Args.string({
21557
+ description: "The search query. Matched against every indexed field (subject, body, sender, recipient) when lexical; against the embedding when semantic.",
21558
+ required: true
21559
+ }) };
21560
+ static flags = {
21561
+ "api-key": Flags.string({
21562
+ description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
21563
+ env: "PRIMITIVE_API_KEY"
21564
+ }),
21565
+ "api-base-url": Flags.string({
21566
+ description: API_BASE_URL_FLAG_DESCRIPTION,
21567
+ env: "PRIMITIVE_API_BASE_URL",
21568
+ hidden: true
21569
+ }),
21570
+ mode: Flags.string({
21571
+ description: "Switch to the cross-corpus semantic backend. Omit for the default lexical backend.",
21572
+ options: [
21573
+ "hybrid",
21574
+ "semantic",
21575
+ "keyword"
21576
+ ]
21577
+ }),
21578
+ from: Flags.string({ description: "Lexical only. Filter by sender address or sender domain." }),
21579
+ to: Flags.string({ description: "Lexical only. Filter by recipient address or recipient domain." }),
21580
+ subject: Flags.string({ description: "Lexical only. Full-text search restricted to the subject." }),
21581
+ body: Flags.string({ description: "Lexical only. Full-text search restricted to the parsed text body." }),
21582
+ domain: Flags.string({ description: "Lexical only. Filter by the recipient's mail domain." }),
21583
+ "domain-id": Flags.string({ description: "Lexical only. Filter by domain ID." }),
21584
+ "has-attachment": Flags.string({
21585
+ description: "Lexical only. Filter by whether the email has one or more attachments.",
21586
+ options: ["true", "false"]
21587
+ }),
21588
+ status: Flags.string({
21589
+ description: "Lexical only. Filter by parse status.",
21590
+ options: [
21591
+ "pending",
21592
+ "accepted",
21593
+ "completed",
21594
+ "rejected"
21595
+ ]
21596
+ }),
21597
+ sort: Flags.string({
21598
+ description: "Lexical only. Result ordering.",
21599
+ options: [
21600
+ "relevance",
21601
+ "received_at_desc",
21602
+ "received_at_asc"
21603
+ ]
21604
+ }),
21605
+ snippet: Flags.string({
21606
+ description: "Lexical only. Include match-centered subject/body highlights on each row.",
21607
+ options: ["true", "false"]
21608
+ }),
21609
+ "include-facets": Flags.string({
21610
+ description: "Lexical only. Include facet counts for sender, domain, status, and attachment presence.",
21611
+ options: ["true", "false"]
21612
+ }),
21613
+ corpus: Flags.string({
21614
+ description: "Semantic only. Restrict to inbound or outbound mail. Pass twice to include both (the default).",
21615
+ options: ["inbound", "outbound"],
21616
+ multiple: true
21617
+ }),
21618
+ "date-from": Flags.string({ description: "Only include mail at or after this ISO-8601 timestamp." }),
21619
+ "date-to": Flags.string({ description: "Only include mail at or before this ISO-8601 timestamp." }),
21620
+ limit: Flags.integer({
21621
+ description: `Maximum results to return (1-${MAX_LIMIT}, default ${DEFAULT_LIMIT}).`,
21622
+ default: DEFAULT_LIMIT,
21623
+ min: 1,
21624
+ max: MAX_LIMIT
21625
+ }),
21626
+ cursor: Flags.string({ description: "Opaque pagination cursor from a prior response's meta.cursor." }),
21627
+ json: Flags.boolean({ description: "Print the raw response envelope as JSON on STDOUT instead of the text table." }),
21628
+ envelope: Flags.boolean({ description: "Lexical text-table only. Surface the next pagination cursor (if any) on STDERR below the table. Has no effect with --json; --json already prints the full envelope including meta." }),
21629
+ time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
21630
+ };
21631
+ async run() {
21632
+ const { args, flags } = await this.parse(SearchCommand);
21633
+ await runWithTiming(flags.time, async () => {
21634
+ const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
21635
+ apiKey: flags["api-key"],
21636
+ apiBaseUrl: flags["api-base-url"],
21637
+ configDir: this.config.configDir
21638
+ });
21639
+ const handleError = (error) => {
21640
+ const errorPayload = extractErrorPayload(error);
21641
+ writeErrorWithHints(errorPayload);
21642
+ surfaceUnauthorizedHint({
21643
+ auth,
21644
+ baseUrlOverridden,
21645
+ configDir: this.config.configDir,
21646
+ payload: errorPayload
21647
+ });
21648
+ process.exitCode = 1;
21649
+ };
21650
+ if (flags.mode) {
21651
+ if (flags.envelope) {
21652
+ process.stderr.write("--envelope only applies to lexical mode. Use --json for the raw semantic envelope.\n");
21653
+ process.exitCode = 2;
21654
+ return;
21655
+ }
21656
+ const incompatible = LEXICAL_ONLY_FLAGS.filter((name) => flags[name] !== void 0);
21657
+ if (incompatible.length > 0) {
21658
+ const isOne = incompatible.length === 1;
21659
+ process.stderr.write(`Flag${isOne ? "" : "s"} --${incompatible.join(", --")} only ${isOne ? "applies" : "apply"} to the lexical backend. Omit --mode to use ${isOne ? "it" : "them"}.\n`);
21660
+ process.exitCode = 2;
21661
+ return;
21662
+ }
21663
+ const result = await semanticSearch({
21664
+ client: apiClient.client,
21665
+ body: {
21666
+ query: args.query,
21667
+ mode: flags.mode,
21668
+ ...flags.corpus ? { corpus: flags.corpus } : {},
21669
+ ...flags["date-from"] ? { date_from: flags["date-from"] } : {},
21670
+ ...flags["date-to"] ? { date_to: flags["date-to"] } : {},
21671
+ limit: flags.limit,
21672
+ ...flags.cursor ? { cursor: flags.cursor } : {}
21673
+ },
21674
+ responseStyle: "fields"
21675
+ });
21676
+ if (result.error) {
21677
+ handleError(result.error);
21678
+ return;
21679
+ }
21680
+ const envelope = result.data;
21681
+ if (flags.json) {
21682
+ this.log(JSON.stringify(envelope ?? null, null, 2));
21683
+ return;
21684
+ }
21685
+ const rows = envelope?.data ?? [];
21686
+ if (rows.length === 0) {
21687
+ process.stderr.write("No matching mail.\n");
21688
+ return;
21689
+ }
21690
+ process.stderr.write(`${formatHeader()}\n`);
21691
+ for (const row of rows) this.log(formatRow(row));
21692
+ const nextCursor = envelope?.meta?.cursor ?? null;
21693
+ if (nextCursor) process.stderr.write(`\nNext page: pass --cursor ${nextCursor}\n`);
21694
+ return;
21695
+ }
21696
+ if (flags.corpus && flags.corpus.length > 0) {
21697
+ process.stderr.write("--corpus only applies to semantic mode. Pass --mode keyword|semantic|hybrid to enable it.\n");
21698
+ process.exitCode = 2;
21699
+ return;
21700
+ }
21701
+ const query = {
21702
+ q: flags.domain ? `${args.query} domain:${quoteDslValue(flags.domain)}` : args.query,
21703
+ limit: flags.limit
21704
+ };
21705
+ if (flags.from) query.from = flags.from;
21706
+ if (flags.to) query.to = flags.to;
21707
+ if (flags.subject) query.subject = flags.subject;
21708
+ if (flags.body) query.body = flags.body;
21709
+ if (flags["domain-id"]) query.domain_id = flags["domain-id"];
21710
+ if (flags["has-attachment"]) query.has_attachment = flags["has-attachment"];
21711
+ if (flags.status) query.status = flags.status;
21712
+ if (flags.sort) query.sort = flags.sort;
21713
+ if (flags.snippet) query.snippet = flags.snippet;
21714
+ if (flags["include-facets"]) query.include_facets = flags["include-facets"];
21715
+ if (flags["date-from"]) query.date_from = flags["date-from"];
21716
+ if (flags["date-to"]) query.date_to = flags["date-to"];
21717
+ if (flags.cursor) query.cursor = flags.cursor;
21718
+ const result = await searchEmails({
21719
+ client: apiClient.client,
21720
+ query,
21721
+ responseStyle: "fields"
21722
+ });
21723
+ if (result.error) {
21724
+ handleError(result.error);
21725
+ return;
21726
+ }
21727
+ const envelope = result.data;
21728
+ if (flags.json) {
21729
+ this.log(JSON.stringify(envelope ?? null, null, 2));
21730
+ return;
21731
+ }
21732
+ const rows = envelope?.data ?? [];
21733
+ if (rows.length === 0) {
21734
+ process.stderr.write("No matching mail.\n");
21735
+ return;
21736
+ }
21737
+ const idWidth = pickIdWidth(Boolean(process.stdout.isTTY));
21738
+ process.stderr.write(`${formatHeader$1(idWidth)}\n`);
21739
+ for (const row of rows) this.log(formatRow$1(row, idWidth));
21740
+ if (flags.envelope) {
21741
+ const nextCursor = envelope?.meta?.cursor ?? null;
21742
+ if (nextCursor) process.stderr.write(`\nNext page: pass --cursor ${nextCursor}\n`);
21743
+ }
21744
+ });
21745
+ }
21746
+ };
21747
+ //#endregion
21353
21748
  //#region src/oclif/commands/send.ts
21354
21749
  var SendCommand = class SendCommand extends Command {
21355
21750
  static description = `Send an outbound email. Agent-grade shortcut for \`sending send\` with sensible defaults.
@@ -21469,35 +21864,40 @@ const SIGNIN_OTP_COPY = {
21469
21864
  actionGerund: "signing in",
21470
21865
  confirmCommand: (email) => `signin otp confirm ${email} <code>`,
21471
21866
  resendCommand: (email) => `signin otp resend ${email}`,
21472
- startCommand: (email) => `signin otp ${email}`
21867
+ startCommand: (email) => `signin otp ${email}`,
21868
+ codeRequired: true
21473
21869
  };
21474
21870
  const SIGNIN_EMAIL_COPY = {
21475
21871
  actionNoun: "sign-in",
21476
21872
  actionGerund: "signing in",
21477
21873
  confirmCommand: (email) => `signin confirm ${email} <code>`,
21478
21874
  resendCommand: (email) => `signin resend ${email}`,
21479
- startCommand: (email) => `signin ${email}`
21875
+ startCommand: (email) => `signin ${email}`,
21876
+ codeRequired: true
21480
21877
  };
21481
21878
  const LOGIN_EMAIL_COPY = {
21482
21879
  actionNoun: "login",
21483
21880
  actionGerund: "logging in",
21484
21881
  confirmCommand: (email) => `login confirm ${email} <code>`,
21485
21882
  resendCommand: (email) => `login resend ${email}`,
21486
- startCommand: (email) => `login ${email}`
21883
+ startCommand: (email) => `login ${email}`,
21884
+ codeRequired: true
21487
21885
  };
21488
21886
  const LOGIN_OTP_COPY = {
21489
21887
  actionNoun: "login",
21490
21888
  actionGerund: "logging in",
21491
21889
  confirmCommand: (email) => `login otp confirm ${email} <code>`,
21492
21890
  resendCommand: (email) => `login otp resend ${email}`,
21493
- startCommand: (email) => `login otp ${email}`
21891
+ startCommand: (email) => `login otp ${email}`,
21892
+ codeRequired: true
21494
21893
  };
21495
21894
  const OTP_COPY = {
21496
21895
  actionNoun: "email-code auth",
21497
21896
  actionGerund: "authenticating",
21498
21897
  confirmCommand: (email) => `otp confirm ${email} <code>`,
21499
21898
  resendCommand: (email) => `otp resend ${email}`,
21500
- startCommand: (email) => `otp ${email}`
21899
+ startCommand: (email) => `otp ${email}`,
21900
+ codeRequired: true
21501
21901
  };
21502
21902
  function acquireCredentialsLock(configDir) {
21503
21903
  try {
@@ -22170,7 +22570,8 @@ function resolveOperationAlias(id) {
22170
22570
  const OVERRIDDEN_OPERATION_IDS = new Set([
22171
22571
  "domains:download-domain-zone-file",
22172
22572
  "functions:test-function",
22173
- "inbox:get-inbox-status"
22573
+ "inbox:get-inbox-status",
22574
+ "search:semantic-search"
22174
22575
  ]);
22175
22576
  const generatedCommands = Object.fromEntries(operationManifest.filter((operation) => !OVERRIDDEN_OPERATION_IDS.has(operationId(operation))).map((operation) => [operationId(operation), createOperationCommand(operation)]));
22176
22577
  const COMMANDS = {
@@ -22214,7 +22615,9 @@ const COMMANDS = {
22214
22615
  "emails:latest": EmailsLatestCommand,
22215
22616
  "emails:watch": EmailsWatchCommand,
22216
22617
  "emails:wait": EmailsWaitCommand,
22618
+ search: SearchCommand,
22217
22619
  "semantic-search": SemanticSearchCommand,
22620
+ "search:semantic-search": SemanticSearchCommand,
22218
22621
  "domains:zone-file": DomainsZoneFileCommand,
22219
22622
  "domains:download-domain-zone-file": DomainsZoneFileCommand,
22220
22623
  "inbox:setup": InboxSetupCommand,
@@ -113,8 +113,9 @@ function loggedOutSignupHint() {
113
113
  return [
114
114
  "New to Primitive?",
115
115
  " You or your user don't have an account yet?",
116
- " Run `primitive signup <email> --signup-code <invite-code> --accept-terms`",
117
- " to create an account, get your own domain, and get started now.",
116
+ " Run `primitive signup <email> --accept-terms`",
117
+ " to create an account and get started.",
118
+ " Add `--signup-code <code>` if you have one.",
118
119
  ""
119
120
  ].join("\n");
120
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primitivedotdev/cli",
3
- "version": "1.0.1",
3
+ "version": "1.2.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,
@@ -62,7 +62,8 @@
62
62
  "description": "List, inspect, and wait for received emails. Prefer task aliases like `primitive emails list`, `primitive emails get`, `primitive emails latest`, `primitive emails wait`, and `primitive emails watch`; generated API names remain available for compatibility."
63
63
  },
64
64
  "search": {
65
- "description": "Semantic, hybrid, and keyword search across received and sent mail. Use `primitive semantic-search <query>`."
65
+ "description": "Cross-corpus semantic search. Prefer `primitive search <query>` for lexical inbound search and `primitive semantic-search <query>` for meaning-aware ranking across inbound and outbound.",
66
+ "hidden": true
66
67
  },
67
68
  "sending": {
68
69
  "description": "Send outbound emails. Prefer `primitive send` for fresh sends and `primitive reply --id <inbound-id>` for replies. Use `primitive domains list` or `primitive inbox status` to find usable sender domains for --from; `primitive sending permissions` lists recipient-scope destinations you may send to."