primcli 1.14.1 → 1.14.3
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
|
@@ -73,6 +73,26 @@ primitive deliveries replay --id <delivery-id>
|
|
|
73
73
|
|
|
74
74
|
Generated API commands remain available for compatibility and full schema parity, for example `primitive emails:list-emails` and `primitive sending:reply-to-email`.
|
|
75
75
|
|
|
76
|
+
## Recipient routing
|
|
77
|
+
|
|
78
|
+
Bind a recipient address to a destination so inbound mail resolves to a single
|
|
79
|
+
endpoint. Pass `--function` to route an address to a function (its route-target
|
|
80
|
+
endpoint is created in the same call, enabling per-address routing like
|
|
81
|
+
`alice@acme.com -> functionA`), or `--endpoint` for an existing endpoint.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
primitive routes add alice@acme.com --function <function-id>
|
|
85
|
+
primitive routes add 'support+*@acme.com' --match wildcard --endpoint <endpoint-id>
|
|
86
|
+
primitive routes list
|
|
87
|
+
primitive routes test alice@acme.com # preview where an address resolves, with the rule trace
|
|
88
|
+
primitive routes update <route-id> --priority 5
|
|
89
|
+
primitive routes reorder --set <route-id>=10 --set <other-id>=20
|
|
90
|
+
primitive routes remove <route-id>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Recipient routing is gated by an organization entitlement; routes are inert
|
|
94
|
+
until it is enabled.
|
|
95
|
+
|
|
76
96
|
## x402 payments
|
|
77
97
|
|
|
78
98
|
The `primitive payments` command group drives non-custodial x402 USDC payments. One agent registers a payout address and requests a payment; the paying agent signs locally with its own wallet key and settles. The key never leaves your machine. Networks are `base` and `base-sepolia`. Amounts take a human USDC value (`--amount-usdc 0.01`) or token base units (`--amount 10000`, since USDC has 6 decimals). Your org is resolved automatically from your API key, so payout registration takes no org flag.
|
|
@@ -1124,6 +1124,21 @@ function detectPrimitiveKeyEnvMisname(env = process.env) {
|
|
|
1124
1124
|
if ((primitiveKey?.length ?? 0) > 0 && (primitiveApiKey?.length ?? 0) === 0) return "PRIMITIVE_KEY is set but the CLI reads PRIMITIVE_API_KEY. Rename your env var, or re-run with PRIMITIVE_API_KEY=$PRIMITIVE_KEY.";
|
|
1125
1125
|
return null;
|
|
1126
1126
|
}
|
|
1127
|
+
/**
|
|
1128
|
+
* True when a usable OAuth login session is saved on disk. Used by `pay-email`
|
|
1129
|
+
* to decide whether it can safely ignore an env-only `PRIMITIVE_API_KEY` in
|
|
1130
|
+
* favor of the payer's logged-in account (which owns the inbound challenge
|
|
1131
|
+
* inbox). Returns false when nothing is stored or the stored credential is
|
|
1132
|
+
* unreadable/legacy, so the caller falls back to whatever key did resolve
|
|
1133
|
+
* rather than silently dropping the user's only auth.
|
|
1134
|
+
*/
|
|
1135
|
+
function hasStoredCliLogin(configDir) {
|
|
1136
|
+
try {
|
|
1137
|
+
return loadCliCredentials(configDir) !== null;
|
|
1138
|
+
} catch {
|
|
1139
|
+
return false;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1127
1142
|
function resolveCliAuth(params) {
|
|
1128
1143
|
const apiKey = params.apiKey?.trim();
|
|
1129
1144
|
const apiBaseUrl = normalizeApiBaseUrl(params.apiBaseUrl);
|
|
@@ -1327,4 +1342,4 @@ function redactCliEnvironment(environment) {
|
|
|
1327
1342
|
};
|
|
1328
1343
|
}
|
|
1329
1344
|
//#endregion
|
|
1330
|
-
export {
|
|
1345
|
+
export { saveActiveChatState as A, resolveCliAuth as C, deleteChatState as D, chatStatePath as E, createClient as M, createConfig as N, loadActiveChatState as O, normalizeApiBaseUrl as S, saveSignupCredentials as T, deleteCliCredentials as _, normalizeCliEnvironmentName as a, hasStoredCliLogin as b, resolveConfigEnvironment as c, validateCliHeaderName as d, validateCliHeaderValue as f, credentialsPath as g, credentialsLockPath as h, loadCliConfig as i, PrimitiveApiClient as j, loadChatConversationByLocalId as k, saveCliConfig as l, cliAccessTokenExpiresAt as m, deleteCliConfig as n, redactCliEnvironment as o, acquireCliCredentialsLock as p, emptyCliConfig as r, removeCliEnvironment as s, DEFAULT_ENVIRONMENT as t, upsertCliEnvironment as u, deleteCliCredentialsLock as v, saveCliCredentials as w, loadCliCredentials as x, detectPrimitiveKeyEnvMisname as y };
|
package/dist/oclif/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, M as createClient, N as createConfig, O as loadActiveChatState, S as normalizeApiBaseUrl, T as saveSignupCredentials, _ as deleteCliCredentials, a as normalizeCliEnvironmentName, b as hasStoredCliLogin, c as resolveConfigEnvironment, d as validateCliHeaderName, f as validateCliHeaderValue, g as credentialsPath, h as credentialsLockPath, i as loadCliConfig, j as PrimitiveApiClient, k as loadChatConversationByLocalId, l as saveCliConfig, m as cliAccessTokenExpiresAt, n as deleteCliConfig, o as redactCliEnvironment, p as acquireCliCredentialsLock, r as emptyCliConfig, s as removeCliEnvironment, u as upsertCliEnvironment, v as deleteCliCredentialsLock, w as saveCliCredentials, x as loadCliCredentials, y as detectPrimitiveKeyEnvMisname } from "../cli-config-C-7V83Qd.js";
|
|
2
2
|
import { Args, Command, Errors, Flags, ux } from "@oclif/core";
|
|
3
3
|
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -3607,6 +3607,7 @@ const openapiDocument = {
|
|
|
3607
3607
|
},
|
|
3608
3608
|
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
3609
3609
|
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3610
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
3610
3611
|
"409": { "$ref": "#/components/responses/Conflict" }
|
|
3611
3612
|
}
|
|
3612
3613
|
}
|
|
@@ -3676,6 +3677,7 @@ const openapiDocument = {
|
|
|
3676
3677
|
},
|
|
3677
3678
|
"400": { "$ref": "#/components/responses/ValidationError" },
|
|
3678
3679
|
"401": { "$ref": "#/components/responses/Unauthorized" },
|
|
3680
|
+
"403": { "$ref": "#/components/responses/Forbidden" },
|
|
3679
3681
|
"404": { "$ref": "#/components/responses/NotFound" },
|
|
3680
3682
|
"409": { "$ref": "#/components/responses/Conflict" }
|
|
3681
3683
|
}
|
|
@@ -28619,6 +28621,23 @@ async function pollForSettlementInteraction(params) {
|
|
|
28619
28621
|
function shouldDeriveChallenge(flags) {
|
|
28620
28622
|
return flags.challenge === void 0 && flags["challenge-file"] === void 0;
|
|
28621
28623
|
}
|
|
28624
|
+
/**
|
|
28625
|
+
* Decide the api-key `pay-email` should authenticate its inbox reads with, given
|
|
28626
|
+
* the parsed flag value, whether `--api-key` was passed explicitly on the
|
|
28627
|
+
* command line (vs sourced from `PRIMITIVE_API_KEY`), and whether a logged-in
|
|
28628
|
+
* session exists. Returns the key to use, or `undefined` to fall through to the
|
|
28629
|
+
* stored login. See the block comment above for the precedence rationale.
|
|
28630
|
+
*/
|
|
28631
|
+
function resolvePayerInboxApiKey(params) {
|
|
28632
|
+
if (params.apiKeyFlag !== void 0 && params.apiKeyFromEnvOnly && params.hasStoredLogin) return {
|
|
28633
|
+
apiKey: void 0,
|
|
28634
|
+
usedStoredLoginOverEnvKey: true
|
|
28635
|
+
};
|
|
28636
|
+
return {
|
|
28637
|
+
apiKey: params.apiKeyFlag,
|
|
28638
|
+
usedStoredLoginOverEnvKey: false
|
|
28639
|
+
};
|
|
28640
|
+
}
|
|
28622
28641
|
/** The exact part name + content type the inbound matcher requires. */
|
|
28623
28642
|
const INTERACTION_PART_FILENAME = "interaction.json";
|
|
28624
28643
|
const INTERACTION_PART_CONTENT_TYPE = "application/json";
|
|
@@ -28744,9 +28763,17 @@ var PaymentsPayEmailCommand = class PaymentsPayEmailCommand extends Command {
|
|
|
28744
28763
|
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
28745
28764
|
};
|
|
28746
28765
|
async run() {
|
|
28747
|
-
const { flags } = await this.parse(PaymentsPayEmailCommand);
|
|
28766
|
+
const { flags, raw } = await this.parse(PaymentsPayEmailCommand);
|
|
28767
|
+
const apiKeyFlagExplicit = raw.some((token) => token.type === "flag" && token.flag === "api-key");
|
|
28768
|
+
const apiKeyFromEnvOnly = flags["api-key"] !== void 0 && !apiKeyFlagExplicit;
|
|
28769
|
+
const { apiKey: effectiveApiKey, usedStoredLoginOverEnvKey } = resolvePayerInboxApiKey({
|
|
28770
|
+
apiKeyFlag: flags["api-key"],
|
|
28771
|
+
apiKeyFromEnvOnly,
|
|
28772
|
+
hasStoredLogin: hasStoredCliLogin(this.config.configDir)
|
|
28773
|
+
});
|
|
28774
|
+
if (usedStoredLoginOverEnvKey) process.stderr.write("PRIMITIVE_API_KEY is set, but pay-email reads the inbound challenge from your logged-in account's inbox, so it is using your saved login for this command. Pass --api-key explicitly to override.\n");
|
|
28748
28775
|
const { apiClient, auth, baseUrlOverridden, requestConfig } = await createAuthenticatedCliApiClient({
|
|
28749
|
-
apiKey:
|
|
28776
|
+
apiKey: effectiveApiKey,
|
|
28750
28777
|
apiBaseUrl: flags["api-base-url"],
|
|
28751
28778
|
configDir: this.config.configDir
|
|
28752
28779
|
});
|
|
@@ -28768,6 +28795,7 @@ var PaymentsPayEmailCommand = class PaymentsPayEmailCommand extends Command {
|
|
|
28768
28795
|
...authFailureContext,
|
|
28769
28796
|
payload: errorPayload
|
|
28770
28797
|
});
|
|
28798
|
+
if (extractErrorCode(errorPayload) === API_ERROR_CODES.notFound && auth.source === "flag-or-env") process.stderr.write("The inbound challenge email was not found for the org this API key belongs to. pay-email reads the challenge from the PAYER account's inbox: unset PRIMITIVE_API_KEY (or pass --api-key for the payer's org, or run `primitive signin` as the payer) and retry.\n");
|
|
28771
28799
|
process.exitCode = 1;
|
|
28772
28800
|
return;
|
|
28773
28801
|
}
|
|
@@ -28795,7 +28823,7 @@ var PaymentsPayEmailCommand = class PaymentsPayEmailCommand extends Command {
|
|
|
28795
28823
|
challenge,
|
|
28796
28824
|
privateKey: flags["private-key"] ?? "",
|
|
28797
28825
|
resolvedApiBaseUrl: auth.apiBaseUrl,
|
|
28798
|
-
apiKey:
|
|
28826
|
+
apiKey: effectiveApiKey
|
|
28799
28827
|
});
|
|
28800
28828
|
} catch (error) {
|
|
28801
28829
|
reportX402Error(error, authFailureContext);
|
|
@@ -29163,6 +29191,434 @@ var ReplyCommand = class ReplyCommand extends Command {
|
|
|
29163
29191
|
}
|
|
29164
29192
|
};
|
|
29165
29193
|
//#endregion
|
|
29194
|
+
//#region src/oclif/commands/routes-add.ts
|
|
29195
|
+
function resolveCreateTarget(flags) {
|
|
29196
|
+
if (flags.function) return { function_id: flags.function };
|
|
29197
|
+
if (flags.endpoint) return { endpoint_id: flags.endpoint };
|
|
29198
|
+
return { error: "Provide exactly one of --function (route to a function) or --endpoint (an existing endpoint)." };
|
|
29199
|
+
}
|
|
29200
|
+
var RoutesAddCommand = class RoutesAddCommand extends Command {
|
|
29201
|
+
static description = `Create a recipient route binding an address pattern to a destination.
|
|
29202
|
+
|
|
29203
|
+
Provide exactly one of --function (route to a function; its route-target
|
|
29204
|
+
endpoint is created in the same transaction) or --endpoint (an existing
|
|
29205
|
+
endpoint). The match type defaults to exact; use --match wildcard for
|
|
29206
|
+
patterns like 'support+*@acme.com'.`;
|
|
29207
|
+
static summary = "Add a recipient route";
|
|
29208
|
+
static examples = [
|
|
29209
|
+
"<%= config.bin %> routes add alice@acme.com --function <fn-id>",
|
|
29210
|
+
"<%= config.bin %> routes add 'support+*@acme.com' --match wildcard --function <fn-id>",
|
|
29211
|
+
"<%= config.bin %> routes add billing@acme.com --endpoint <endpoint-id> --priority 10"
|
|
29212
|
+
];
|
|
29213
|
+
static args = { pattern: Args.string({
|
|
29214
|
+
description: "Recipient address or wildcard pattern (e.g. alice@acme.com).",
|
|
29215
|
+
required: true
|
|
29216
|
+
}) };
|
|
29217
|
+
static flags = {
|
|
29218
|
+
"api-key": Flags.string({
|
|
29219
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
29220
|
+
env: "PRIMITIVE_API_KEY"
|
|
29221
|
+
}),
|
|
29222
|
+
"api-base-url": Flags.string({
|
|
29223
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
29224
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
29225
|
+
hidden: true
|
|
29226
|
+
}),
|
|
29227
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION }),
|
|
29228
|
+
function: Flags.string({
|
|
29229
|
+
description: "Function id to route this address to (its route-target endpoint is minted if needed). Mutually exclusive with --endpoint.",
|
|
29230
|
+
exclusive: ["endpoint"]
|
|
29231
|
+
}),
|
|
29232
|
+
endpoint: Flags.string({
|
|
29233
|
+
description: "Existing endpoint id to route to. Mutually exclusive with --function.",
|
|
29234
|
+
exclusive: ["function"]
|
|
29235
|
+
}),
|
|
29236
|
+
match: Flags.string({
|
|
29237
|
+
description: "Match type for the pattern. regex requires the Power plan and may be rejected if not enabled.",
|
|
29238
|
+
options: [
|
|
29239
|
+
"exact",
|
|
29240
|
+
"wildcard",
|
|
29241
|
+
"regex"
|
|
29242
|
+
],
|
|
29243
|
+
default: "exact"
|
|
29244
|
+
}),
|
|
29245
|
+
domain: Flags.string({ description: "Scope the route to this domain id (defaults to the pattern's domain)." }),
|
|
29246
|
+
priority: Flags.integer({ description: "Evaluation order within a scope; lower is checked first." }),
|
|
29247
|
+
disabled: Flags.boolean({ description: "Create the route disabled." })
|
|
29248
|
+
};
|
|
29249
|
+
async run() {
|
|
29250
|
+
const { args, flags } = await this.parse(RoutesAddCommand);
|
|
29251
|
+
const target = resolveCreateTarget(flags);
|
|
29252
|
+
if ("error" in target) {
|
|
29253
|
+
process.stderr.write(`${target.error}\n`);
|
|
29254
|
+
process.exitCode = 1;
|
|
29255
|
+
return;
|
|
29256
|
+
}
|
|
29257
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
29258
|
+
apiKey: flags["api-key"],
|
|
29259
|
+
apiBaseUrl: flags["api-base-url"],
|
|
29260
|
+
configDir: this.config.configDir
|
|
29261
|
+
});
|
|
29262
|
+
await runWithTiming(flags.time, async () => {
|
|
29263
|
+
const result = await createRoute({
|
|
29264
|
+
client: apiClient.client,
|
|
29265
|
+
body: {
|
|
29266
|
+
match_type: flags.match,
|
|
29267
|
+
pattern: args.pattern,
|
|
29268
|
+
...target,
|
|
29269
|
+
...flags.domain ? { domain_id: flags.domain } : {},
|
|
29270
|
+
...flags.priority != null ? { priority: flags.priority } : {},
|
|
29271
|
+
...flags.disabled ? { enabled: false } : {}
|
|
29272
|
+
},
|
|
29273
|
+
responseStyle: "fields"
|
|
29274
|
+
});
|
|
29275
|
+
if (result.error) {
|
|
29276
|
+
const payload = extractErrorPayload(result.error);
|
|
29277
|
+
writeErrorWithHints(payload);
|
|
29278
|
+
surfaceUnauthorizedHint({
|
|
29279
|
+
auth,
|
|
29280
|
+
baseUrlOverridden,
|
|
29281
|
+
configDir: this.config.configDir,
|
|
29282
|
+
payload
|
|
29283
|
+
});
|
|
29284
|
+
process.exitCode = 1;
|
|
29285
|
+
return;
|
|
29286
|
+
}
|
|
29287
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
29288
|
+
});
|
|
29289
|
+
}
|
|
29290
|
+
};
|
|
29291
|
+
//#endregion
|
|
29292
|
+
//#region src/oclif/commands/routes-list.ts
|
|
29293
|
+
var RoutesListCommand = class RoutesListCommand extends Command {
|
|
29294
|
+
static description = "List the org's recipient routes in evaluation order.";
|
|
29295
|
+
static summary = "List recipient routes";
|
|
29296
|
+
static examples = ["<%= config.bin %> routes list"];
|
|
29297
|
+
static flags = {
|
|
29298
|
+
"api-key": Flags.string({
|
|
29299
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
29300
|
+
env: "PRIMITIVE_API_KEY"
|
|
29301
|
+
}),
|
|
29302
|
+
"api-base-url": Flags.string({
|
|
29303
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
29304
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
29305
|
+
hidden: true
|
|
29306
|
+
}),
|
|
29307
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
29308
|
+
};
|
|
29309
|
+
async run() {
|
|
29310
|
+
const { flags } = await this.parse(RoutesListCommand);
|
|
29311
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
29312
|
+
apiKey: flags["api-key"],
|
|
29313
|
+
apiBaseUrl: flags["api-base-url"],
|
|
29314
|
+
configDir: this.config.configDir
|
|
29315
|
+
});
|
|
29316
|
+
await runWithTiming(flags.time, async () => {
|
|
29317
|
+
const result = await listRoutes({
|
|
29318
|
+
client: apiClient.client,
|
|
29319
|
+
responseStyle: "fields"
|
|
29320
|
+
});
|
|
29321
|
+
if (result.error) {
|
|
29322
|
+
const payload = extractErrorPayload(result.error);
|
|
29323
|
+
writeErrorWithHints(payload);
|
|
29324
|
+
surfaceUnauthorizedHint({
|
|
29325
|
+
auth,
|
|
29326
|
+
baseUrlOverridden,
|
|
29327
|
+
configDir: this.config.configDir,
|
|
29328
|
+
payload
|
|
29329
|
+
});
|
|
29330
|
+
process.exitCode = 1;
|
|
29331
|
+
return;
|
|
29332
|
+
}
|
|
29333
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
29334
|
+
});
|
|
29335
|
+
}
|
|
29336
|
+
};
|
|
29337
|
+
//#endregion
|
|
29338
|
+
//#region src/oclif/commands/routes-remove.ts
|
|
29339
|
+
var RoutesRemoveCommand = class RoutesRemoveCommand extends Command {
|
|
29340
|
+
static description = "Delete a recipient route by id. Mail matching its pattern then falls back to the domain or organization default.";
|
|
29341
|
+
static summary = "Remove a recipient route";
|
|
29342
|
+
static examples = ["<%= config.bin %> routes remove <route-id>"];
|
|
29343
|
+
static args = { id: Args.string({
|
|
29344
|
+
description: "Route id (UUID) to delete.",
|
|
29345
|
+
required: true
|
|
29346
|
+
}) };
|
|
29347
|
+
static flags = {
|
|
29348
|
+
"api-key": Flags.string({
|
|
29349
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
29350
|
+
env: "PRIMITIVE_API_KEY"
|
|
29351
|
+
}),
|
|
29352
|
+
"api-base-url": Flags.string({
|
|
29353
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
29354
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
29355
|
+
hidden: true
|
|
29356
|
+
}),
|
|
29357
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION })
|
|
29358
|
+
};
|
|
29359
|
+
async run() {
|
|
29360
|
+
const { args, flags } = await this.parse(RoutesRemoveCommand);
|
|
29361
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
29362
|
+
apiKey: flags["api-key"],
|
|
29363
|
+
apiBaseUrl: flags["api-base-url"],
|
|
29364
|
+
configDir: this.config.configDir
|
|
29365
|
+
});
|
|
29366
|
+
await runWithTiming(flags.time, async () => {
|
|
29367
|
+
const result = await deleteRoute({
|
|
29368
|
+
client: apiClient.client,
|
|
29369
|
+
path: { id: args.id },
|
|
29370
|
+
responseStyle: "fields"
|
|
29371
|
+
});
|
|
29372
|
+
if (result.error) {
|
|
29373
|
+
const payload = extractErrorPayload(result.error);
|
|
29374
|
+
writeErrorWithHints(payload);
|
|
29375
|
+
surfaceUnauthorizedHint({
|
|
29376
|
+
auth,
|
|
29377
|
+
baseUrlOverridden,
|
|
29378
|
+
configDir: this.config.configDir,
|
|
29379
|
+
payload
|
|
29380
|
+
});
|
|
29381
|
+
process.exitCode = 1;
|
|
29382
|
+
return;
|
|
29383
|
+
}
|
|
29384
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
29385
|
+
});
|
|
29386
|
+
}
|
|
29387
|
+
};
|
|
29388
|
+
//#endregion
|
|
29389
|
+
//#region src/oclif/commands/routes-reorder.ts
|
|
29390
|
+
function parseReorderUpdates(set) {
|
|
29391
|
+
const updates = [];
|
|
29392
|
+
const seen = /* @__PURE__ */ new Set();
|
|
29393
|
+
for (const pair of set) {
|
|
29394
|
+
const eq = pair.lastIndexOf("=");
|
|
29395
|
+
const id = (eq >= 0 ? pair.slice(0, eq).trim() : "").toLowerCase();
|
|
29396
|
+
const priority = eq >= 0 ? Number(pair.slice(eq + 1).trim()) : NaN;
|
|
29397
|
+
if (!id || !Number.isInteger(priority) || priority < 0) return { error: `Invalid --set value "${pair}"; expected <route-id>=<priority> with a non-negative integer priority.` };
|
|
29398
|
+
if (seen.has(id)) return { error: `Route ${id} appears more than once in --set; specify each route at most once.` };
|
|
29399
|
+
seen.add(id);
|
|
29400
|
+
updates.push({
|
|
29401
|
+
id,
|
|
29402
|
+
priority
|
|
29403
|
+
});
|
|
29404
|
+
}
|
|
29405
|
+
return { updates };
|
|
29406
|
+
}
|
|
29407
|
+
var RoutesReorderCommand = class RoutesReorderCommand extends Command {
|
|
29408
|
+
static description = `Set the priority of one or more recipient routes in a single call.
|
|
29409
|
+
|
|
29410
|
+
Priority controls evaluation order within a scope; lower is checked first.
|
|
29411
|
+
Pass --set <route-id>=<priority> once per route you want to move.`;
|
|
29412
|
+
static summary = "Reorder recipient routes";
|
|
29413
|
+
static examples = ["<%= config.bin %> routes reorder --set <route-id>=10", "<%= config.bin %> routes reorder --set <id-a>=10 --set <id-b>=20"];
|
|
29414
|
+
static flags = {
|
|
29415
|
+
"api-key": Flags.string({
|
|
29416
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
29417
|
+
env: "PRIMITIVE_API_KEY"
|
|
29418
|
+
}),
|
|
29419
|
+
"api-base-url": Flags.string({
|
|
29420
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
29421
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
29422
|
+
hidden: true
|
|
29423
|
+
}),
|
|
29424
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION }),
|
|
29425
|
+
set: Flags.string({
|
|
29426
|
+
description: "A route and its new priority as <route-id>=<priority>. Repeatable.",
|
|
29427
|
+
multiple: true,
|
|
29428
|
+
required: true
|
|
29429
|
+
})
|
|
29430
|
+
};
|
|
29431
|
+
async run() {
|
|
29432
|
+
const { flags } = await this.parse(RoutesReorderCommand);
|
|
29433
|
+
const parsed = parseReorderUpdates(flags.set);
|
|
29434
|
+
if ("error" in parsed) {
|
|
29435
|
+
process.stderr.write(`${parsed.error}\n`);
|
|
29436
|
+
process.exitCode = 1;
|
|
29437
|
+
return;
|
|
29438
|
+
}
|
|
29439
|
+
const { updates } = parsed;
|
|
29440
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
29441
|
+
apiKey: flags["api-key"],
|
|
29442
|
+
apiBaseUrl: flags["api-base-url"],
|
|
29443
|
+
configDir: this.config.configDir
|
|
29444
|
+
});
|
|
29445
|
+
await runWithTiming(flags.time, async () => {
|
|
29446
|
+
const result = await reorderRoutes({
|
|
29447
|
+
client: apiClient.client,
|
|
29448
|
+
body: { updates },
|
|
29449
|
+
responseStyle: "fields"
|
|
29450
|
+
});
|
|
29451
|
+
if (result.error) {
|
|
29452
|
+
const payload = extractErrorPayload(result.error);
|
|
29453
|
+
writeErrorWithHints(payload);
|
|
29454
|
+
surfaceUnauthorizedHint({
|
|
29455
|
+
auth,
|
|
29456
|
+
baseUrlOverridden,
|
|
29457
|
+
configDir: this.config.configDir,
|
|
29458
|
+
payload
|
|
29459
|
+
});
|
|
29460
|
+
process.exitCode = 1;
|
|
29461
|
+
return;
|
|
29462
|
+
}
|
|
29463
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
29464
|
+
});
|
|
29465
|
+
}
|
|
29466
|
+
};
|
|
29467
|
+
//#endregion
|
|
29468
|
+
//#region src/oclif/commands/routes-test.ts
|
|
29469
|
+
var RoutesTestCommand = class RoutesTestCommand extends Command {
|
|
29470
|
+
static description = `Simulate routing for a recipient address.
|
|
29471
|
+
|
|
29472
|
+
Shows the resolved destination and a trace of every rule evaluated (which
|
|
29473
|
+
matched or missed, and why). Does not create or change anything.`;
|
|
29474
|
+
static summary = "Simulate routing for a recipient";
|
|
29475
|
+
static examples = ["<%= config.bin %> routes test alice@acme.com", "<%= config.bin %> routes test bounce@acme.com --event-type email.bounced"];
|
|
29476
|
+
static args = { recipient: Args.string({
|
|
29477
|
+
description: "Recipient address to simulate (e.g. alice@acme.com).",
|
|
29478
|
+
required: true
|
|
29479
|
+
}) };
|
|
29480
|
+
static flags = {
|
|
29481
|
+
"api-key": Flags.string({
|
|
29482
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
29483
|
+
env: "PRIMITIVE_API_KEY"
|
|
29484
|
+
}),
|
|
29485
|
+
"api-base-url": Flags.string({
|
|
29486
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
29487
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
29488
|
+
hidden: true
|
|
29489
|
+
}),
|
|
29490
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION }),
|
|
29491
|
+
"event-type": Flags.string({ description: "Event type to model (defaults to email.received)." })
|
|
29492
|
+
};
|
|
29493
|
+
async run() {
|
|
29494
|
+
const { args, flags } = await this.parse(RoutesTestCommand);
|
|
29495
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
29496
|
+
apiKey: flags["api-key"],
|
|
29497
|
+
apiBaseUrl: flags["api-base-url"],
|
|
29498
|
+
configDir: this.config.configDir
|
|
29499
|
+
});
|
|
29500
|
+
await runWithTiming(flags.time, async () => {
|
|
29501
|
+
const result = await simulateRoute({
|
|
29502
|
+
client: apiClient.client,
|
|
29503
|
+
body: {
|
|
29504
|
+
recipient: args.recipient,
|
|
29505
|
+
...flags["event-type"] ? { event_type: flags["event-type"] } : {}
|
|
29506
|
+
},
|
|
29507
|
+
responseStyle: "fields"
|
|
29508
|
+
});
|
|
29509
|
+
if (result.error) {
|
|
29510
|
+
const payload = extractErrorPayload(result.error);
|
|
29511
|
+
writeErrorWithHints(payload);
|
|
29512
|
+
surfaceUnauthorizedHint({
|
|
29513
|
+
auth,
|
|
29514
|
+
baseUrlOverridden,
|
|
29515
|
+
configDir: this.config.configDir,
|
|
29516
|
+
payload
|
|
29517
|
+
});
|
|
29518
|
+
process.exitCode = 1;
|
|
29519
|
+
return;
|
|
29520
|
+
}
|
|
29521
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
29522
|
+
});
|
|
29523
|
+
}
|
|
29524
|
+
};
|
|
29525
|
+
//#endregion
|
|
29526
|
+
//#region src/oclif/commands/routes-update.ts
|
|
29527
|
+
function buildUpdateBody(flags) {
|
|
29528
|
+
return {
|
|
29529
|
+
...flags.match ? { match_type: flags.match } : {},
|
|
29530
|
+
...flags.pattern ? { pattern: flags.pattern } : {},
|
|
29531
|
+
...flags.endpoint ? { endpoint_id: flags.endpoint } : {},
|
|
29532
|
+
...flags.domain ? { domain_id: flags.domain } : {},
|
|
29533
|
+
...flags.priority != null ? { priority: flags.priority } : {},
|
|
29534
|
+
...flags.enable ? { enabled: true } : {},
|
|
29535
|
+
...flags.disable ? { enabled: false } : {}
|
|
29536
|
+
};
|
|
29537
|
+
}
|
|
29538
|
+
var RoutesUpdateCommand = class RoutesUpdateCommand extends Command {
|
|
29539
|
+
static description = `Update an existing recipient route.
|
|
29540
|
+
|
|
29541
|
+
Change the pattern, match type, target endpoint, domain scope, priority, or
|
|
29542
|
+
enabled state. Provide at least one field; anything omitted is left as-is.`;
|
|
29543
|
+
static summary = "Update a recipient route";
|
|
29544
|
+
static examples = [
|
|
29545
|
+
"<%= config.bin %> routes update <route-id> --priority 5",
|
|
29546
|
+
"<%= config.bin %> routes update <route-id> --pattern alice@acme.com",
|
|
29547
|
+
"<%= config.bin %> routes update <route-id> --disable"
|
|
29548
|
+
];
|
|
29549
|
+
static args = { id: Args.string({
|
|
29550
|
+
description: "Route id (UUID) to update.",
|
|
29551
|
+
required: true
|
|
29552
|
+
}) };
|
|
29553
|
+
static flags = {
|
|
29554
|
+
"api-key": Flags.string({
|
|
29555
|
+
description: "Primitive API key override (defaults to PRIMITIVE_API_KEY or saved OAuth login credentials)",
|
|
29556
|
+
env: "PRIMITIVE_API_KEY"
|
|
29557
|
+
}),
|
|
29558
|
+
"api-base-url": Flags.string({
|
|
29559
|
+
description: API_BASE_URL_FLAG_DESCRIPTION,
|
|
29560
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
29561
|
+
hidden: true
|
|
29562
|
+
}),
|
|
29563
|
+
time: Flags.boolean({ description: TIME_FLAG_DESCRIPTION }),
|
|
29564
|
+
match: Flags.string({
|
|
29565
|
+
description: "New match type for the pattern.",
|
|
29566
|
+
options: [
|
|
29567
|
+
"exact",
|
|
29568
|
+
"wildcard",
|
|
29569
|
+
"regex"
|
|
29570
|
+
]
|
|
29571
|
+
}),
|
|
29572
|
+
pattern: Flags.string({ description: "New recipient address or wildcard pattern." }),
|
|
29573
|
+
endpoint: Flags.string({ description: "New target endpoint id (UUID)." }),
|
|
29574
|
+
domain: Flags.string({ description: "New domain scope id (UUID)." }),
|
|
29575
|
+
priority: Flags.integer({ description: "New evaluation priority within a scope; lower is checked first." }),
|
|
29576
|
+
enable: Flags.boolean({
|
|
29577
|
+
description: "Enable the route.",
|
|
29578
|
+
exclusive: ["disable"]
|
|
29579
|
+
}),
|
|
29580
|
+
disable: Flags.boolean({
|
|
29581
|
+
description: "Disable the route.",
|
|
29582
|
+
exclusive: ["enable"]
|
|
29583
|
+
})
|
|
29584
|
+
};
|
|
29585
|
+
async run() {
|
|
29586
|
+
const { args, flags } = await this.parse(RoutesUpdateCommand);
|
|
29587
|
+
const body = buildUpdateBody(flags);
|
|
29588
|
+
if (Object.keys(body).length === 0) {
|
|
29589
|
+
process.stderr.write("Provide at least one field to update (--match, --pattern, --endpoint, --domain, --priority, --enable/--disable).\n");
|
|
29590
|
+
process.exitCode = 1;
|
|
29591
|
+
return;
|
|
29592
|
+
}
|
|
29593
|
+
const { apiClient, auth, baseUrlOverridden } = await createAuthenticatedCliApiClient({
|
|
29594
|
+
apiKey: flags["api-key"],
|
|
29595
|
+
apiBaseUrl: flags["api-base-url"],
|
|
29596
|
+
configDir: this.config.configDir
|
|
29597
|
+
});
|
|
29598
|
+
await runWithTiming(flags.time, async () => {
|
|
29599
|
+
const result = await updateRoute({
|
|
29600
|
+
client: apiClient.client,
|
|
29601
|
+
path: { id: args.id },
|
|
29602
|
+
body,
|
|
29603
|
+
responseStyle: "fields"
|
|
29604
|
+
});
|
|
29605
|
+
if (result.error) {
|
|
29606
|
+
const payload = extractErrorPayload(result.error);
|
|
29607
|
+
writeErrorWithHints(payload);
|
|
29608
|
+
surfaceUnauthorizedHint({
|
|
29609
|
+
auth,
|
|
29610
|
+
baseUrlOverridden,
|
|
29611
|
+
configDir: this.config.configDir,
|
|
29612
|
+
payload
|
|
29613
|
+
});
|
|
29614
|
+
process.exitCode = 1;
|
|
29615
|
+
return;
|
|
29616
|
+
}
|
|
29617
|
+
this.log(JSON.stringify(result.data.data, null, 2));
|
|
29618
|
+
});
|
|
29619
|
+
}
|
|
29620
|
+
};
|
|
29621
|
+
//#endregion
|
|
29166
29622
|
//#region src/oclif/commands/semantic-search.ts
|
|
29167
29623
|
const DEFAULT_LIMIT$1 = 10;
|
|
29168
29624
|
const MAX_LIMIT$1 = 100;
|
|
@@ -30481,6 +30937,12 @@ const COMMANDS = {
|
|
|
30481
30937
|
"functions:route-unset": FunctionsRouteUnsetCommand,
|
|
30482
30938
|
"functions:route-get": FunctionsRouteGetCommand,
|
|
30483
30939
|
"functions:routing-topology": FunctionsRoutingTopologyCommand,
|
|
30940
|
+
"routes:add": RoutesAddCommand,
|
|
30941
|
+
"routes:list": RoutesListCommand,
|
|
30942
|
+
"routes:test": RoutesTestCommand,
|
|
30943
|
+
"routes:update": RoutesUpdateCommand,
|
|
30944
|
+
"routes:reorder": RoutesReorderCommand,
|
|
30945
|
+
"routes:remove": RoutesRemoveCommand,
|
|
30484
30946
|
...Object.fromEntries(Object.entries(CANONICAL_OPERATION_ALIASES).map(([alias, target]) => {
|
|
30485
30947
|
const command = generatedCommands[target];
|
|
30486
30948
|
if (!command) throw new Error(`Missing generated command target for alias ${alias}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as normalizeApiBaseUrl, c as resolveConfigEnvironment, i as loadCliConfig } from "../cli-config-C-7V83Qd.js";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primcli",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.3",
|
|
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,
|