@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 +1 -1
- package/dist/oclif/index.js +495 -92
- package/dist/oclif/root-signup-hint.js +3 -2
- package/package.json +3 -2
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 `--
|
|
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
|
|
package/dist/oclif/index.js
CHANGED
|
@@ -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.
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
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
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
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.
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
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
|
|
209
|
-
* when needed
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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:
|
|
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$
|
|
16649
|
-
const MAX_LIMIT$
|
|
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$
|
|
16700
|
-
default: DEFAULT_LIMIT$
|
|
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$
|
|
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).
|
|
17665
|
-
|
|
17666
|
-
|
|
17667
|
-
|
|
17668
|
-
|
|
17669
|
-
|
|
17670
|
-
|
|
17671
|
-
|
|
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
|
-
|
|
17871
|
-
|
|
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
|
|
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 (
|
|
17915
|
-
|
|
17916
|
-
|
|
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:
|
|
18079
|
+
payload: secretsOutcome.payload
|
|
17920
18080
|
});
|
|
17921
18081
|
process.exitCode = 1;
|
|
17922
18082
|
return;
|
|
17923
18083
|
}
|
|
17924
|
-
|
|
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
|
|
17974
|
-
const CLI_VERSION_RANGE = "^1.0
|
|
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>"} --
|
|
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
|
|
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: "
|
|
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> --
|
|
117
|
-
" to create an account
|
|
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
|
|
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": "
|
|
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."
|