@primitivedotdev/sdk 0.20.0 → 0.21.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/dist/api/generated/index.js +1 -1
- package/dist/api/generated/sdk.gen.js +30 -0
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.js +40 -5
- package/dist/{api-DNF21MDo.js → api-BjzvA2Fy.js} +63 -6
- package/dist/{index-C6ObsYjq.d.ts → index-QTYQpSFt.d.ts} +215 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/oclif/api-command.js +74 -13
- package/dist/oclif/auth.js +65 -10
- package/dist/oclif/commands/emails-latest.js +23 -12
- package/dist/oclif/commands/emails-poll.js +121 -0
- package/dist/oclif/commands/emails-wait.js +171 -0
- package/dist/oclif/commands/emails-watch.js +165 -0
- package/dist/oclif/commands/functions-deploy.js +15 -6
- package/dist/oclif/commands/functions-redeploy.js +15 -6
- package/dist/oclif/commands/login.js +18 -14
- package/dist/oclif/commands/logout.js +9 -8
- package/dist/oclif/commands/send.js +21 -7
- package/dist/oclif/commands/whoami.js +15 -6
- package/dist/oclif/fish-completion.js +1 -1
- package/dist/oclif/index.js +6 -0
- package/dist/openapi/openapi.generated.js +397 -2
- package/dist/openapi/operations.generated.js +305 -1
- package/oclif.manifest.json +1327 -281
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { Command, Errors, Flags } from "@oclif/core";
|
|
|
4
4
|
import { getAccount, pollCliLogin, startCliLogin, } from "../../api/generated/sdk.gen.js";
|
|
5
5
|
import { PrimitiveApiClient } from "../../api/index.js";
|
|
6
6
|
import { API_ERROR_CODES, extractErrorCode, extractErrorPayload, removeStaleSavedCredentialOnUnauthorized, writeErrorWithHints, } from "../api-command.js";
|
|
7
|
-
import { acquireCliCredentialsLock, credentialsPath, loadCliCredentials,
|
|
7
|
+
import { acquireCliCredentialsLock, credentialsPath, loadCliCredentials, normalizeApiBaseUrl1, normalizeApiBaseUrl2, saveCliCredentials, } from "../auth.js";
|
|
8
8
|
const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
|
|
9
9
|
function cliError(message) {
|
|
10
10
|
return new Errors.CLIError(message, { exit: 1 });
|
|
@@ -36,13 +36,13 @@ function retryAfterSeconds(result) {
|
|
|
36
36
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
37
37
|
}
|
|
38
38
|
export async function checkExistingLogin(params) {
|
|
39
|
-
const baseUrlOverridden = params.
|
|
40
|
-
const
|
|
41
|
-
?
|
|
42
|
-
: params.credentials.
|
|
39
|
+
const baseUrlOverridden = params.apiBaseUrl1 !== undefined;
|
|
40
|
+
const probeApiBaseUrl1 = baseUrlOverridden
|
|
41
|
+
? normalizeApiBaseUrl1(params.apiBaseUrl1)
|
|
42
|
+
: params.credentials.api_base_url_1;
|
|
43
43
|
const apiClient = new PrimitiveApiClient({
|
|
44
44
|
apiKey: params.credentials.api_key,
|
|
45
|
-
|
|
45
|
+
apiBaseUrl1: probeApiBaseUrl1,
|
|
46
46
|
});
|
|
47
47
|
const result = await (params.checkAccount ??
|
|
48
48
|
((client) => getAccount({
|
|
@@ -54,7 +54,10 @@ export async function checkExistingLogin(params) {
|
|
|
54
54
|
const payload = extractErrorPayload(result.error);
|
|
55
55
|
const auth = {
|
|
56
56
|
apiKey: params.credentials.api_key,
|
|
57
|
-
|
|
57
|
+
apiBaseUrl1: probeApiBaseUrl1,
|
|
58
|
+
// Host-2 isn't relevant to checkExistingLogin (login is on host-1
|
|
59
|
+
// only), but the auth shape requires it. Use the default.
|
|
60
|
+
apiBaseUrl2: normalizeApiBaseUrl2(undefined),
|
|
58
61
|
credentials: params.credentials,
|
|
59
62
|
source: "stored",
|
|
60
63
|
};
|
|
@@ -84,9 +87,10 @@ class LoginCommand extends Command {
|
|
|
84
87
|
"<%= config.bin %> login --force",
|
|
85
88
|
];
|
|
86
89
|
static flags = {
|
|
87
|
-
"base-url": Flags.string({
|
|
88
|
-
description: "API base URL
|
|
89
|
-
env: "
|
|
90
|
+
"api-base-url-1": Flags.string({
|
|
91
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
92
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
93
|
+
hidden: true,
|
|
90
94
|
}),
|
|
91
95
|
"device-name": Flags.string({
|
|
92
96
|
description: "Device name shown in the browser approval screen",
|
|
@@ -117,7 +121,7 @@ class LoginCommand extends Command {
|
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
123
|
async runWithCredentialLock(flags) {
|
|
120
|
-
const
|
|
124
|
+
const apiBaseUrl1 = normalizeApiBaseUrl1(flags["api-base-url-1"]);
|
|
121
125
|
let existing;
|
|
122
126
|
try {
|
|
123
127
|
existing = loadCliCredentials(this.config.configDir);
|
|
@@ -134,7 +138,7 @@ class LoginCommand extends Command {
|
|
|
134
138
|
}
|
|
135
139
|
else if (existing) {
|
|
136
140
|
const existingStatus = await checkExistingLogin({
|
|
137
|
-
|
|
141
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
138
142
|
configDir: this.config.configDir,
|
|
139
143
|
credentials: existing,
|
|
140
144
|
});
|
|
@@ -150,7 +154,7 @@ class LoginCommand extends Command {
|
|
|
150
154
|
throw cliError(`Already logged in${org}. Run \`primitive logout\` before logging in again.`);
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
|
-
const apiClient = new PrimitiveApiClient({
|
|
157
|
+
const apiClient = new PrimitiveApiClient({ apiBaseUrl1 });
|
|
154
158
|
const deviceName = flags["device-name"] ?? hostname();
|
|
155
159
|
const started = await startCliLogin({
|
|
156
160
|
body: {
|
|
@@ -192,7 +196,7 @@ class LoginCommand extends Command {
|
|
|
192
196
|
}
|
|
193
197
|
saveCliCredentials(this.config.configDir, {
|
|
194
198
|
api_key: login.api_key,
|
|
195
|
-
|
|
199
|
+
api_base_url_1: apiBaseUrl1,
|
|
196
200
|
created_at: new Date().toISOString(),
|
|
197
201
|
key_id: login.key_id,
|
|
198
202
|
key_prefix: login.key_prefix,
|
|
@@ -2,7 +2,7 @@ import { Command, Errors, Flags } from "@oclif/core";
|
|
|
2
2
|
import { cliLogout } from "../../api/generated/sdk.gen.js";
|
|
3
3
|
import { PrimitiveApiClient } from "../../api/index.js";
|
|
4
4
|
import { API_ERROR_CODES, extractErrorCode, extractErrorPayload, writeErrorWithHints, } from "../api-command.js";
|
|
5
|
-
import { acquireCliCredentialsLock, deleteCliCredentials, loadCliCredentials,
|
|
5
|
+
import { acquireCliCredentialsLock, deleteCliCredentials, loadCliCredentials, normalizeApiBaseUrl1, } from "../auth.js";
|
|
6
6
|
function cliError(message) {
|
|
7
7
|
return new Errors.CLIError(message, { exit: 1 });
|
|
8
8
|
}
|
|
@@ -15,9 +15,10 @@ class LogoutCommand extends Command {
|
|
|
15
15
|
static summary = "Log out and revoke the saved CLI key";
|
|
16
16
|
static examples = ["<%= config.bin %> logout"];
|
|
17
17
|
static flags = {
|
|
18
|
-
"base-url": Flags.string({
|
|
19
|
-
description: "Override the API base URL
|
|
20
|
-
env: "
|
|
18
|
+
"api-base-url-1": Flags.string({
|
|
19
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
20
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
21
|
+
hidden: true,
|
|
21
22
|
}),
|
|
22
23
|
};
|
|
23
24
|
async run() {
|
|
@@ -52,12 +53,12 @@ class LogoutCommand extends Command {
|
|
|
52
53
|
if (!credentials) {
|
|
53
54
|
throw cliError("Not logged in. Run `primitive login` to create saved CLI credentials.");
|
|
54
55
|
}
|
|
55
|
-
const
|
|
56
|
-
?
|
|
57
|
-
: credentials.
|
|
56
|
+
const apiBaseUrl1 = flags["api-base-url-1"]
|
|
57
|
+
? normalizeApiBaseUrl1(flags["api-base-url-1"])
|
|
58
|
+
: credentials.api_base_url_1;
|
|
58
59
|
const apiClient = new PrimitiveApiClient({
|
|
59
60
|
apiKey: credentials.api_key,
|
|
60
|
-
|
|
61
|
+
apiBaseUrl1,
|
|
61
62
|
});
|
|
62
63
|
const result = await cliLogout({
|
|
63
64
|
body: { key_id: credentials.key_id },
|
|
@@ -115,9 +115,15 @@ class SendCommand extends Command {
|
|
|
115
115
|
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
|
|
116
116
|
env: "PRIMITIVE_API_KEY",
|
|
117
117
|
}),
|
|
118
|
-
"base-url": Flags.string({
|
|
119
|
-
description: "API base URL
|
|
120
|
-
env: "
|
|
118
|
+
"api-base-url-1": Flags.string({
|
|
119
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
120
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
121
|
+
hidden: true,
|
|
122
|
+
}),
|
|
123
|
+
"api-base-url-2": Flags.string({
|
|
124
|
+
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
125
|
+
env: "PRIMITIVE_API_BASE_URL_2",
|
|
126
|
+
hidden: true,
|
|
121
127
|
}),
|
|
122
128
|
to: Flags.string({
|
|
123
129
|
description: "Recipient address (e.g. alice@example.com).",
|
|
@@ -154,15 +160,18 @@ class SendCommand extends Command {
|
|
|
154
160
|
throw new Errors.CLIError("Either --body or --html (or both) is required.");
|
|
155
161
|
}
|
|
156
162
|
await runWithTiming(flags.time, async () => {
|
|
157
|
-
const baseUrlOverridden = flags["base-url"] !== undefined
|
|
163
|
+
const baseUrlOverridden = flags["api-base-url-1"] !== undefined ||
|
|
164
|
+
flags["api-base-url-2"] !== undefined;
|
|
158
165
|
const auth = resolveCliAuth({
|
|
159
166
|
apiKey: flags["api-key"],
|
|
160
|
-
|
|
167
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
168
|
+
apiBaseUrl2: flags["api-base-url-2"],
|
|
161
169
|
configDir: this.config.configDir,
|
|
162
170
|
});
|
|
163
171
|
const apiClient = new PrimitiveApiClient({
|
|
164
172
|
apiKey: auth.apiKey,
|
|
165
|
-
|
|
173
|
+
apiBaseUrl1: auth.apiBaseUrl1,
|
|
174
|
+
apiBaseUrl2: auth.apiBaseUrl2,
|
|
166
175
|
});
|
|
167
176
|
const authFailureContext = {
|
|
168
177
|
auth,
|
|
@@ -187,7 +196,12 @@ class SendCommand extends Command {
|
|
|
187
196
|
? { wait_timeout_ms: flags["wait-timeout-ms"] }
|
|
188
197
|
: {}),
|
|
189
198
|
},
|
|
190
|
-
|
|
199
|
+
// /send-mail goes to the attachments-supporting host. The
|
|
200
|
+
// wrapper exposes the host-2 client as _sendClient for this
|
|
201
|
+
// and any other host-2 operation that lands here. Customer
|
|
202
|
+
// SDK callers should use PrimitiveClient.send() instead so
|
|
203
|
+
// the routing stays internal.
|
|
204
|
+
client: apiClient._sendClient,
|
|
191
205
|
responseStyle: "fields",
|
|
192
206
|
});
|
|
193
207
|
if (result.error) {
|
|
@@ -25,9 +25,15 @@ class WhoamiCommand extends Command {
|
|
|
25
25
|
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
|
|
26
26
|
env: "PRIMITIVE_API_KEY",
|
|
27
27
|
}),
|
|
28
|
-
"base-url": Flags.string({
|
|
29
|
-
description: "API base URL
|
|
30
|
-
env: "
|
|
28
|
+
"api-base-url-1": Flags.string({
|
|
29
|
+
description: "Override the primary API base URL. Internal testing only; not documented to customers.",
|
|
30
|
+
env: "PRIMITIVE_API_BASE_URL_1",
|
|
31
|
+
hidden: true,
|
|
32
|
+
}),
|
|
33
|
+
"api-base-url-2": Flags.string({
|
|
34
|
+
description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
|
|
35
|
+
env: "PRIMITIVE_API_BASE_URL_2",
|
|
36
|
+
hidden: true,
|
|
31
37
|
}),
|
|
32
38
|
time: Flags.boolean({
|
|
33
39
|
description: TIME_FLAG_DESCRIPTION,
|
|
@@ -36,15 +42,18 @@ class WhoamiCommand extends Command {
|
|
|
36
42
|
async run() {
|
|
37
43
|
const { flags } = await this.parse(WhoamiCommand);
|
|
38
44
|
await runWithTiming(flags.time, async () => {
|
|
39
|
-
const baseUrlOverridden = flags["base-url"] !== undefined
|
|
45
|
+
const baseUrlOverridden = flags["api-base-url-1"] !== undefined ||
|
|
46
|
+
flags["api-base-url-2"] !== undefined;
|
|
40
47
|
const auth = resolveCliAuth({
|
|
41
48
|
apiKey: flags["api-key"],
|
|
42
|
-
|
|
49
|
+
apiBaseUrl1: flags["api-base-url-1"],
|
|
50
|
+
apiBaseUrl2: flags["api-base-url-2"],
|
|
43
51
|
configDir: this.config.configDir,
|
|
44
52
|
});
|
|
45
53
|
const apiClient = new PrimitiveApiClient({
|
|
46
54
|
apiKey: auth.apiKey,
|
|
47
|
-
|
|
55
|
+
apiBaseUrl1: auth.apiBaseUrl1,
|
|
56
|
+
apiBaseUrl2: auth.apiBaseUrl2,
|
|
48
57
|
});
|
|
49
58
|
const result = await getAccount({
|
|
50
59
|
client: apiClient.client,
|
|
@@ -73,7 +73,7 @@ export function renderFishCompletion(binName) {
|
|
|
73
73
|
]) {
|
|
74
74
|
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l '${fishEscape(parameter.name.replace(/_/g, "-"))}' -r -d '${fishEscape(parameter.description ?? parameter.name)}'`);
|
|
75
75
|
}
|
|
76
|
-
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'api-key' -r -d 'Primitive API key (defaults to PRIMITIVE_API_KEY or saved primitive login credentials)'
|
|
76
|
+
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'api-key' -r -d 'Primitive API key (defaults to PRIMITIVE_API_KEY or saved primitive login credentials)'`);
|
|
77
77
|
if (operation.hasJsonBody) {
|
|
78
78
|
lines.push(`complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'body' -r -d 'JSON request body'`, `complete -c ${binName} -n '${operationCondition(operation).replace(BIN_PLACEHOLDER, binName)}' -l 'body-file' -r -d 'Path to a JSON file used as the request body'`);
|
|
79
79
|
}
|
package/dist/oclif/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import { Args, Command, Errors } from "@oclif/core";
|
|
|
2
2
|
import { operationManifest, } from "../openapi/index.js";
|
|
3
3
|
import { createOperationCommand } from "./api-command.js";
|
|
4
4
|
import EmailsLatestCommand from "./commands/emails-latest.js";
|
|
5
|
+
import EmailsWaitCommand from "./commands/emails-wait.js";
|
|
6
|
+
import EmailsWatchCommand from "./commands/emails-watch.js";
|
|
5
7
|
import FunctionsDeployCommand from "./commands/functions-deploy.js";
|
|
6
8
|
import FunctionsRedeployCommand from "./commands/functions-redeploy.js";
|
|
7
9
|
import LoginCommand from "./commands/login.js";
|
|
@@ -133,6 +135,10 @@ export const COMMANDS = {
|
|
|
133
135
|
// inbound emails as a compact text table. emails:list-emails stays
|
|
134
136
|
// available for the full JSON envelope + cursor pagination.
|
|
135
137
|
"emails:latest": EmailsLatestCommand,
|
|
138
|
+
// `emails:watch` and `emails:wait` poll the search API for new matching
|
|
139
|
+
// inbound mail. `watch` defaults to a human table; `wait` defaults to JSONL.
|
|
140
|
+
"emails:watch": EmailsWatchCommand,
|
|
141
|
+
"emails:wait": EmailsWaitCommand,
|
|
136
142
|
// `functions:deploy` and `functions:redeploy` are file-input
|
|
137
143
|
// shortcuts for create-function / update-function. The underlying
|
|
138
144
|
// ops take `code` as a body string, which is awkward at the CLI
|