primcli 1.20.0 → 1.22.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/oclif/index.js +593 -12
- package/package.json +1 -1
package/dist/oclif/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
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-BKso8oJu.js";
|
|
2
2
|
import { Args, Command, Errors, Flags, ux } from "@oclif/core";
|
|
3
|
-
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
3
|
+
import { chmodSync, closeSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import path, { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
6
6
|
import process$1 from "node:process";
|
|
7
7
|
import { createInterface } from "node:readline/promises";
|
|
8
8
|
import { hostname } from "node:os";
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
|
+
import { open, rm, stat } from "node:fs/promises";
|
|
10
11
|
import { gunzipSync } from "node:zlib";
|
|
11
12
|
import { concat, getAddress, hexToBytes, keccak256, stringToBytes } from "viem";
|
|
12
13
|
import { privateKeyToAccount } from "viem/accounts";
|
|
@@ -9241,6 +9242,40 @@ const openapiDocument = {
|
|
|
9241
9242
|
},
|
|
9242
9243
|
"required": ["filename", "content_base64"]
|
|
9243
9244
|
},
|
|
9245
|
+
"SendMailPayloadRef": {
|
|
9246
|
+
"type": "object",
|
|
9247
|
+
"additionalProperties": false,
|
|
9248
|
+
"description": "A reference to an already-uploaded Primitive Payloads object, delivered as an attachment without inlining the bytes — the way to send an attachment larger than the inline cap. Upload the object via /v1/payloads (with a client-held CEK the server never sees), then reference it here.",
|
|
9249
|
+
"properties": {
|
|
9250
|
+
"root": {
|
|
9251
|
+
"type": "string",
|
|
9252
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
9253
|
+
"description": "The 64-char lowercase-hex Merkle root of a finalized payloads object."
|
|
9254
|
+
},
|
|
9255
|
+
"filename": {
|
|
9256
|
+
"type": "string",
|
|
9257
|
+
"minLength": 1,
|
|
9258
|
+
"maxLength": 255,
|
|
9259
|
+
"description": "Attachment filename presented to the recipient."
|
|
9260
|
+
},
|
|
9261
|
+
"content_type": {
|
|
9262
|
+
"type": "string",
|
|
9263
|
+
"minLength": 1,
|
|
9264
|
+
"maxLength": 255,
|
|
9265
|
+
"description": "Optional MIME content type."
|
|
9266
|
+
},
|
|
9267
|
+
"cek": {
|
|
9268
|
+
"type": "string",
|
|
9269
|
+
"pattern": "^[A-Za-z0-9_-]{1,128}$",
|
|
9270
|
+
"description": "Base64url-encoded (unpadded) content-encryption key the recipient uses to decrypt. Travels with the email; the object store only ever holds ciphertext."
|
|
9271
|
+
}
|
|
9272
|
+
},
|
|
9273
|
+
"required": [
|
|
9274
|
+
"root",
|
|
9275
|
+
"filename",
|
|
9276
|
+
"cek"
|
|
9277
|
+
]
|
|
9278
|
+
},
|
|
9244
9279
|
"SendMailInput": {
|
|
9245
9280
|
"type": "object",
|
|
9246
9281
|
"additionalProperties": false,
|
|
@@ -9257,6 +9292,36 @@ const openapiDocument = {
|
|
|
9257
9292
|
"maxLength": 320,
|
|
9258
9293
|
"description": "Recipient address. Recipient eligibility depends on your account's outbound entitlements."
|
|
9259
9294
|
},
|
|
9295
|
+
"cc": {
|
|
9296
|
+
"description": "Carbon-copy recipients. Either a single address or an array of addresses. Each entry must be a single address; use the array form for multiple recipients. Cc recipients are visible to everyone who receives the message. The combined number of to, cc, and bcc recipients must not exceed 100.",
|
|
9297
|
+
"oneOf": [{
|
|
9298
|
+
"type": "string",
|
|
9299
|
+
"minLength": 1
|
|
9300
|
+
}, {
|
|
9301
|
+
"type": "array",
|
|
9302
|
+
"minItems": 1,
|
|
9303
|
+
"maxItems": 100,
|
|
9304
|
+
"items": {
|
|
9305
|
+
"type": "string",
|
|
9306
|
+
"minLength": 1
|
|
9307
|
+
}
|
|
9308
|
+
}]
|
|
9309
|
+
},
|
|
9310
|
+
"bcc": {
|
|
9311
|
+
"description": "Blind-carbon-copy recipients. Either a single address or an array of addresses. Each entry must be a single address; use the array form for multiple recipients. Bcc recipients receive the message but are not disclosed to the other recipients: no Bcc header is sent. The combined number of to, cc, and bcc recipients must not exceed 100.",
|
|
9312
|
+
"oneOf": [{
|
|
9313
|
+
"type": "string",
|
|
9314
|
+
"minLength": 1
|
|
9315
|
+
}, {
|
|
9316
|
+
"type": "array",
|
|
9317
|
+
"minItems": 1,
|
|
9318
|
+
"maxItems": 100,
|
|
9319
|
+
"items": {
|
|
9320
|
+
"type": "string",
|
|
9321
|
+
"minLength": 1
|
|
9322
|
+
}
|
|
9323
|
+
}]
|
|
9324
|
+
},
|
|
9260
9325
|
"subject": {
|
|
9261
9326
|
"type": "string",
|
|
9262
9327
|
"minLength": 1,
|
|
@@ -9295,6 +9360,12 @@ const openapiDocument = {
|
|
|
9295
9360
|
"description": "Inline attachments. Send requests with attachments to https://api.primitive.dev/v1/send-mail. Combined raw decoded attachment bytes must be at most 31457280.",
|
|
9296
9361
|
"items": { "$ref": "#/components/schemas/SendMailAttachment" }
|
|
9297
9362
|
},
|
|
9363
|
+
"payload_attachments": {
|
|
9364
|
+
"type": "array",
|
|
9365
|
+
"maxItems": 1,
|
|
9366
|
+
"description": "Deliver an already-uploaded Primitive Payloads object as an attachment by reference, without inlining the bytes — the way to send attachments larger than the inline cap. Upload the object via /v1/payloads (client-held CEK), then reference it here. v1 supports at most one.",
|
|
9367
|
+
"items": { "$ref": "#/components/schemas/SendMailPayloadRef" }
|
|
9368
|
+
},
|
|
9298
9369
|
"wait": {
|
|
9299
9370
|
"type": "boolean",
|
|
9300
9371
|
"description": "When true, wait for the first downstream SMTP delivery outcome before returning."
|
|
@@ -21770,6 +21841,36 @@ const operationManifest = [
|
|
|
21770
21841
|
"maxLength": 320,
|
|
21771
21842
|
"description": "Recipient address. Recipient eligibility depends on your account's outbound entitlements."
|
|
21772
21843
|
},
|
|
21844
|
+
"cc": {
|
|
21845
|
+
"description": "Carbon-copy recipients. Either a single address or an array of addresses. Each entry must be a single address; use the array form for multiple recipients. Cc recipients are visible to everyone who receives the message. The combined number of to, cc, and bcc recipients must not exceed 100.",
|
|
21846
|
+
"oneOf": [{
|
|
21847
|
+
"type": "string",
|
|
21848
|
+
"minLength": 1
|
|
21849
|
+
}, {
|
|
21850
|
+
"type": "array",
|
|
21851
|
+
"minItems": 1,
|
|
21852
|
+
"maxItems": 100,
|
|
21853
|
+
"items": {
|
|
21854
|
+
"type": "string",
|
|
21855
|
+
"minLength": 1
|
|
21856
|
+
}
|
|
21857
|
+
}]
|
|
21858
|
+
},
|
|
21859
|
+
"bcc": {
|
|
21860
|
+
"description": "Blind-carbon-copy recipients. Either a single address or an array of addresses. Each entry must be a single address; use the array form for multiple recipients. Bcc recipients receive the message but are not disclosed to the other recipients: no Bcc header is sent. The combined number of to, cc, and bcc recipients must not exceed 100.",
|
|
21861
|
+
"oneOf": [{
|
|
21862
|
+
"type": "string",
|
|
21863
|
+
"minLength": 1
|
|
21864
|
+
}, {
|
|
21865
|
+
"type": "array",
|
|
21866
|
+
"minItems": 1,
|
|
21867
|
+
"maxItems": 100,
|
|
21868
|
+
"items": {
|
|
21869
|
+
"type": "string",
|
|
21870
|
+
"minLength": 1
|
|
21871
|
+
}
|
|
21872
|
+
}]
|
|
21873
|
+
},
|
|
21773
21874
|
"subject": {
|
|
21774
21875
|
"type": "string",
|
|
21775
21876
|
"minLength": 1,
|
|
@@ -21832,6 +21933,45 @@ const operationManifest = [
|
|
|
21832
21933
|
"required": ["filename", "content_base64"]
|
|
21833
21934
|
}
|
|
21834
21935
|
},
|
|
21936
|
+
"payload_attachments": {
|
|
21937
|
+
"type": "array",
|
|
21938
|
+
"maxItems": 1,
|
|
21939
|
+
"description": "Deliver an already-uploaded Primitive Payloads object as an attachment by reference, without inlining the bytes — the way to send attachments larger than the inline cap. Upload the object via /v1/payloads (client-held CEK), then reference it here. v1 supports at most one.",
|
|
21940
|
+
"items": {
|
|
21941
|
+
"type": "object",
|
|
21942
|
+
"additionalProperties": false,
|
|
21943
|
+
"description": "A reference to an already-uploaded Primitive Payloads object, delivered as an attachment without inlining the bytes — the way to send an attachment larger than the inline cap. Upload the object via /v1/payloads (with a client-held CEK the server never sees), then reference it here.",
|
|
21944
|
+
"properties": {
|
|
21945
|
+
"root": {
|
|
21946
|
+
"type": "string",
|
|
21947
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
21948
|
+
"description": "The 64-char lowercase-hex Merkle root of a finalized payloads object."
|
|
21949
|
+
},
|
|
21950
|
+
"filename": {
|
|
21951
|
+
"type": "string",
|
|
21952
|
+
"minLength": 1,
|
|
21953
|
+
"maxLength": 255,
|
|
21954
|
+
"description": "Attachment filename presented to the recipient."
|
|
21955
|
+
},
|
|
21956
|
+
"content_type": {
|
|
21957
|
+
"type": "string",
|
|
21958
|
+
"minLength": 1,
|
|
21959
|
+
"maxLength": 255,
|
|
21960
|
+
"description": "Optional MIME content type."
|
|
21961
|
+
},
|
|
21962
|
+
"cek": {
|
|
21963
|
+
"type": "string",
|
|
21964
|
+
"pattern": "^[A-Za-z0-9_-]{1,128}$",
|
|
21965
|
+
"description": "Base64url-encoded (unpadded) content-encryption key the recipient uses to decrypt. Travels with the email; the object store only ever holds ciphertext."
|
|
21966
|
+
}
|
|
21967
|
+
},
|
|
21968
|
+
"required": [
|
|
21969
|
+
"root",
|
|
21970
|
+
"filename",
|
|
21971
|
+
"cek"
|
|
21972
|
+
]
|
|
21973
|
+
}
|
|
21974
|
+
},
|
|
21835
21975
|
"wait": {
|
|
21836
21976
|
"type": "boolean",
|
|
21837
21977
|
"description": "When true, wait for the first downstream SMTP delivery outcome before returning."
|
|
@@ -24841,7 +24981,7 @@ async function fetchEmailSearchPage(params) {
|
|
|
24841
24981
|
rows
|
|
24842
24982
|
};
|
|
24843
24983
|
}
|
|
24844
|
-
function sleep$
|
|
24984
|
+
function sleep$2(ms) {
|
|
24845
24985
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
24846
24986
|
}
|
|
24847
24987
|
function cliError$6(message) {
|
|
@@ -25874,7 +26014,7 @@ async function waitForReply(params) {
|
|
|
25874
26014
|
if (lastAccepted !== void 0) continue;
|
|
25875
26015
|
if (phase.deadline !== null && Date.now() >= phase.deadline) break;
|
|
25876
26016
|
if (totalDeadline !== null && Date.now() >= totalDeadline) return null;
|
|
25877
|
-
await sleep$
|
|
26017
|
+
await sleep$2(params.interval * 1e3);
|
|
25878
26018
|
}
|
|
25879
26019
|
}
|
|
25880
26020
|
return null;
|
|
@@ -26656,7 +26796,7 @@ var EmailsWaitCommand = class EmailsWaitCommand extends Command {
|
|
|
26656
26796
|
}
|
|
26657
26797
|
if (cursorAdvanced) continue;
|
|
26658
26798
|
if (deadline !== null && Date.now() >= deadline) break;
|
|
26659
|
-
await sleep$
|
|
26799
|
+
await sleep$2(flags.interval * 1e3);
|
|
26660
26800
|
}
|
|
26661
26801
|
process.stderr.write(`Timed out waiting for ${flags.number} matching email${flags.number === 1 ? "" : "s"}; received ${matched}.\n`);
|
|
26662
26802
|
process.exitCode = 1;
|
|
@@ -26775,7 +26915,7 @@ var EmailsWatchCommand = class EmailsWatchCommand extends Command {
|
|
|
26775
26915
|
}
|
|
26776
26916
|
if (cursorAdvanced) continue;
|
|
26777
26917
|
if (deadline !== null && Date.now() >= deadline) break;
|
|
26778
|
-
await sleep$
|
|
26918
|
+
await sleep$2(flags.interval * 1e3);
|
|
26779
26919
|
}
|
|
26780
26920
|
}
|
|
26781
26921
|
};
|
|
@@ -27936,8 +28076,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
27936
28076
|
name: "Primitive Team",
|
|
27937
28077
|
url: "https://primitive.dev"
|
|
27938
28078
|
};
|
|
27939
|
-
const SDK_VERSION_RANGE = "^1.
|
|
27940
|
-
const CLI_VERSION_RANGE = "^1.
|
|
28079
|
+
const SDK_VERSION_RANGE = "^1.22.0";
|
|
28080
|
+
const CLI_VERSION_RANGE = "^1.22.0";
|
|
27941
28081
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
27942
28082
|
function renderHandler() {
|
|
27943
28083
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -28672,7 +28812,7 @@ var FunctionsLogsCommand = class FunctionsLogsCommand extends Command {
|
|
|
28672
28812
|
return;
|
|
28673
28813
|
}
|
|
28674
28814
|
completedInitialFollowPoll = true;
|
|
28675
|
-
await sleep$
|
|
28815
|
+
await sleep$2(flags["poll-interval"] * 1e3);
|
|
28676
28816
|
}
|
|
28677
28817
|
});
|
|
28678
28818
|
}
|
|
@@ -29583,7 +29723,7 @@ var FunctionsTestFunctionCommand = class FunctionsTestFunctionCommand extends Co
|
|
|
29583
29723
|
trace = fetched;
|
|
29584
29724
|
break;
|
|
29585
29725
|
}
|
|
29586
|
-
await sleep$
|
|
29726
|
+
await sleep$2(pollIntervalMs);
|
|
29587
29727
|
}
|
|
29588
29728
|
if (!trace) {
|
|
29589
29729
|
const finalTrace = ((await getFunctionTestRunTrace({
|
|
@@ -29962,7 +30102,7 @@ const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
|
|
|
29962
30102
|
function cliError$3(message) {
|
|
29963
30103
|
return new Errors.CLIError(message, { exit: 1 });
|
|
29964
30104
|
}
|
|
29965
|
-
function sleep(ms) {
|
|
30105
|
+
function sleep$1(ms) {
|
|
29966
30106
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
29967
30107
|
}
|
|
29968
30108
|
function openBrowser(url) {
|
|
@@ -30127,7 +30267,7 @@ var LoginCommand$1 = class extends Command {
|
|
|
30127
30267
|
let interval = Math.min(Math.max(1, start.interval), MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS);
|
|
30128
30268
|
let nextPollDelay = 1;
|
|
30129
30269
|
while (Date.now() < deadline) {
|
|
30130
|
-
await sleep(nextPollDelay * 1e3);
|
|
30270
|
+
await sleep$1(nextPollDelay * 1e3);
|
|
30131
30271
|
nextPollDelay = interval;
|
|
30132
30272
|
const polled = await pollCliLogin({
|
|
30133
30273
|
body: { device_code: start.device_code },
|
|
@@ -31757,6 +31897,434 @@ var OrgSecretsSetCommand = class OrgSecretsSetCommand extends Command {
|
|
|
31757
31897
|
}
|
|
31758
31898
|
};
|
|
31759
31899
|
//#endregion
|
|
31900
|
+
//#region ../sdk-node/dist/payloads/index.js
|
|
31901
|
+
/**
|
|
31902
|
+
* Primitive Payloads — streaming client for large, content-addressed,
|
|
31903
|
+
* end-to-end-encrypted objects (attachments up to ~1 TB).
|
|
31904
|
+
*
|
|
31905
|
+
* Objects are split into fixed 64 MiB chunks; each chunk is encrypted with a
|
|
31906
|
+
* per-chunk AES-256-GCM key (HKDF-derived from the object CEK), content-addressed
|
|
31907
|
+
* by its ciphertext SHA-256, and committed under a Merkle root. This module
|
|
31908
|
+
* streams: it processes one chunk at a time and never holds the whole object in
|
|
31909
|
+
* memory, so a multi-GB file uploads/downloads in bounded memory.
|
|
31910
|
+
*
|
|
31911
|
+
* The chunk/manifest construction is byte-compatible with the server's object
|
|
31912
|
+
* model (packages/payloads-core in the API monorepo).
|
|
31913
|
+
*/
|
|
31914
|
+
const CHUNK_SIZE = 64 * 1024 * 1024;
|
|
31915
|
+
const MANIFEST_VERSION = 1;
|
|
31916
|
+
const CHUNK_KDF_INFO = "payloads-chunk";
|
|
31917
|
+
const OBJECT_ID_BYTES = 16;
|
|
31918
|
+
const CEK_BYTES = 32;
|
|
31919
|
+
const subtle = globalThis.crypto.subtle;
|
|
31920
|
+
const textEncoder = new TextEncoder();
|
|
31921
|
+
function randomBytes(length) {
|
|
31922
|
+
const out = new Uint8Array(length);
|
|
31923
|
+
globalThis.crypto.getRandomValues(out);
|
|
31924
|
+
return out;
|
|
31925
|
+
}
|
|
31926
|
+
function toHex(bytes) {
|
|
31927
|
+
let out = "";
|
|
31928
|
+
for (const b of bytes) out += b.toString(16).padStart(2, "0");
|
|
31929
|
+
return out;
|
|
31930
|
+
}
|
|
31931
|
+
function fromHex(hex) {
|
|
31932
|
+
if (hex.length % 2 !== 0) throw new Error("invalid hex: odd length");
|
|
31933
|
+
if (hex.length > 0 && !/^[0-9a-fA-F]+$/.test(hex)) throw new Error("invalid hex: non-hex character");
|
|
31934
|
+
const out = new Uint8Array(hex.length / 2);
|
|
31935
|
+
for (let i = 0; i < out.length; i++) out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
31936
|
+
return out;
|
|
31937
|
+
}
|
|
31938
|
+
async function contentHash(bytes) {
|
|
31939
|
+
return new Uint8Array(await subtle.digest("SHA-256", bytes));
|
|
31940
|
+
}
|
|
31941
|
+
async function contentHashHex(bytes) {
|
|
31942
|
+
return toHex(await contentHash(bytes));
|
|
31943
|
+
}
|
|
31944
|
+
/** Binary Merkle root over ordered hex leaf hashes (odd node promoted). */
|
|
31945
|
+
async function merkleRoot(leafHashesHex) {
|
|
31946
|
+
if (leafHashesHex.length === 0) return contentHashHex(new Uint8Array(0));
|
|
31947
|
+
let level = leafHashesHex.map(fromHex);
|
|
31948
|
+
while (level.length > 1) {
|
|
31949
|
+
const next = [];
|
|
31950
|
+
for (let i = 0; i < level.length; i += 2) {
|
|
31951
|
+
const left = level[i];
|
|
31952
|
+
const right = i + 1 < level.length ? level[i + 1] : level[i];
|
|
31953
|
+
const combined = new Uint8Array(left.length + right.length);
|
|
31954
|
+
combined.set(left, 0);
|
|
31955
|
+
combined.set(right, left.length);
|
|
31956
|
+
next.push(await contentHash(combined));
|
|
31957
|
+
}
|
|
31958
|
+
level = next;
|
|
31959
|
+
}
|
|
31960
|
+
return toHex(level[0]);
|
|
31961
|
+
}
|
|
31962
|
+
async function deriveChunkKey(cek, index) {
|
|
31963
|
+
const baseKey = await subtle.importKey("raw", cek, "HKDF", false, ["deriveKey"]);
|
|
31964
|
+
const info = textEncoder.encode(`${CHUNK_KDF_INFO}:${index}`);
|
|
31965
|
+
return subtle.deriveKey({
|
|
31966
|
+
name: "HKDF",
|
|
31967
|
+
hash: "SHA-256",
|
|
31968
|
+
salt: new Uint8Array(32),
|
|
31969
|
+
info
|
|
31970
|
+
}, baseKey, {
|
|
31971
|
+
name: "AES-GCM",
|
|
31972
|
+
length: 256
|
|
31973
|
+
}, false, ["encrypt", "decrypt"]);
|
|
31974
|
+
}
|
|
31975
|
+
function chunkNonce(index) {
|
|
31976
|
+
const nonce = new Uint8Array(12);
|
|
31977
|
+
new DataView(nonce.buffer).setUint32(8, index >>> 0, false);
|
|
31978
|
+
return nonce;
|
|
31979
|
+
}
|
|
31980
|
+
function chunkAad(objectId, index) {
|
|
31981
|
+
const aad = new Uint8Array(objectId.length + 4);
|
|
31982
|
+
aad.set(objectId, 0);
|
|
31983
|
+
new DataView(aad.buffer).setUint32(objectId.length, index >>> 0, false);
|
|
31984
|
+
return aad;
|
|
31985
|
+
}
|
|
31986
|
+
async function encryptChunk(cek, objectId, index, plaintext) {
|
|
31987
|
+
const key = await deriveChunkKey(cek, index);
|
|
31988
|
+
const ct = await subtle.encrypt({
|
|
31989
|
+
name: "AES-GCM",
|
|
31990
|
+
iv: chunkNonce(index),
|
|
31991
|
+
additionalData: chunkAad(objectId, index)
|
|
31992
|
+
}, key, plaintext);
|
|
31993
|
+
return new Uint8Array(ct);
|
|
31994
|
+
}
|
|
31995
|
+
async function decryptChunk(cek, objectId, index, ciphertext) {
|
|
31996
|
+
const key = await deriveChunkKey(cek, index);
|
|
31997
|
+
const pt = await subtle.decrypt({
|
|
31998
|
+
name: "AES-GCM",
|
|
31999
|
+
iv: chunkNonce(index),
|
|
32000
|
+
additionalData: chunkAad(objectId, index)
|
|
32001
|
+
}, key, ciphertext);
|
|
32002
|
+
return new Uint8Array(pt);
|
|
32003
|
+
}
|
|
32004
|
+
function authHeaders(apiKey) {
|
|
32005
|
+
return { authorization: `Bearer ${apiKey}` };
|
|
32006
|
+
}
|
|
32007
|
+
function apiRoot(baseUrl) {
|
|
32008
|
+
let end = baseUrl.length;
|
|
32009
|
+
while (end > 0 && baseUrl.charCodeAt(end - 1) === 47) end--;
|
|
32010
|
+
return `${baseUrl.slice(0, end)}/v1/payloads`;
|
|
32011
|
+
}
|
|
32012
|
+
const DEFAULT_MAX_RETRIES = 6;
|
|
32013
|
+
const RETRYABLE_STATUS = new Set([
|
|
32014
|
+
429,
|
|
32015
|
+
500,
|
|
32016
|
+
502,
|
|
32017
|
+
503,
|
|
32018
|
+
504
|
|
32019
|
+
]);
|
|
32020
|
+
function sleep(ms) {
|
|
32021
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
32022
|
+
}
|
|
32023
|
+
/**
|
|
32024
|
+
* fetch with exponential backoff on transient failures. Request bodies here are
|
|
32025
|
+
* in-memory buffers/strings (never streams), so a retry can safely resend them.
|
|
32026
|
+
*/
|
|
32027
|
+
async function retryingFetch(url, init, label, maxRetries = DEFAULT_MAX_RETRIES) {
|
|
32028
|
+
for (let attempt = 0;; attempt++) {
|
|
32029
|
+
let res;
|
|
32030
|
+
let networkError;
|
|
32031
|
+
try {
|
|
32032
|
+
res = await fetch(url, init);
|
|
32033
|
+
} catch (err) {
|
|
32034
|
+
networkError = err;
|
|
32035
|
+
}
|
|
32036
|
+
if (res && !RETRYABLE_STATUS.has(res.status)) return res;
|
|
32037
|
+
if (attempt >= maxRetries) {
|
|
32038
|
+
if (res) return res;
|
|
32039
|
+
throw new Error(`${label}: network error after ${attempt} retries: ${networkError?.message ?? networkError}`);
|
|
32040
|
+
}
|
|
32041
|
+
if (res) await res.text().catch(() => {});
|
|
32042
|
+
await sleep(Math.min(1e3 * 2 ** attempt, 15e3) + Math.floor(Math.random() * 250));
|
|
32043
|
+
}
|
|
32044
|
+
}
|
|
32045
|
+
async function initiate(opts, manifest) {
|
|
32046
|
+
const res = await retryingFetch(apiRoot(opts.baseUrl), {
|
|
32047
|
+
method: "POST",
|
|
32048
|
+
headers: {
|
|
32049
|
+
...authHeaders(opts.apiKey),
|
|
32050
|
+
"content-type": "application/json"
|
|
32051
|
+
},
|
|
32052
|
+
body: JSON.stringify({ manifest })
|
|
32053
|
+
}, "initiate");
|
|
32054
|
+
if (!res.ok) throw new Error(`initiate failed: HTTP ${res.status} ${await res.text()}`);
|
|
32055
|
+
await res.text();
|
|
32056
|
+
}
|
|
32057
|
+
async function putChunk(opts, root, d, bytes) {
|
|
32058
|
+
const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/chunks/${d.ciphertextHash}`, {
|
|
32059
|
+
method: "PUT",
|
|
32060
|
+
headers: {
|
|
32061
|
+
...authHeaders(opts.apiKey),
|
|
32062
|
+
"content-type": "application/octet-stream"
|
|
32063
|
+
},
|
|
32064
|
+
body: bytes
|
|
32065
|
+
}, `chunk ${d.index} upload`);
|
|
32066
|
+
if (!res.ok) throw new Error(`chunk ${d.index} upload failed: HTTP ${res.status} ${await res.text()}`);
|
|
32067
|
+
await res.text();
|
|
32068
|
+
}
|
|
32069
|
+
async function finalize(opts, root) {
|
|
32070
|
+
const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/finalize`, {
|
|
32071
|
+
method: "POST",
|
|
32072
|
+
headers: authHeaders(opts.apiKey)
|
|
32073
|
+
}, "finalize");
|
|
32074
|
+
if (!res.ok) throw new Error(`finalize failed: HTTP ${res.status} ${await res.text()}`);
|
|
32075
|
+
await res.text();
|
|
32076
|
+
}
|
|
32077
|
+
async function fetchManifest(opts, root) {
|
|
32078
|
+
const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/manifest`, { headers: authHeaders(opts.apiKey) }, "get manifest");
|
|
32079
|
+
if (!res.ok) throw new Error(`get manifest failed: HTTP ${res.status} ${await res.text()}`);
|
|
32080
|
+
return (await res.json()).data.manifest;
|
|
32081
|
+
}
|
|
32082
|
+
async function getChunkBytes(opts, root, d) {
|
|
32083
|
+
const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/chunks/${d.ciphertextHash}`, { headers: authHeaders(opts.apiKey) }, `get chunk ${d.index}`);
|
|
32084
|
+
if (!res.ok) throw new Error(`get chunk ${d.index} failed: HTTP ${res.status}`);
|
|
32085
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
32086
|
+
}
|
|
32087
|
+
async function readWindow(fh, position, length) {
|
|
32088
|
+
const buf = Buffer.allocUnsafe(length);
|
|
32089
|
+
let read = 0;
|
|
32090
|
+
while (read < length) {
|
|
32091
|
+
const { bytesRead } = await fh.read(buf, read, length - read, position + read);
|
|
32092
|
+
if (bytesRead === 0) break;
|
|
32093
|
+
read += bytesRead;
|
|
32094
|
+
}
|
|
32095
|
+
return read === length ? buf : buf.subarray(0, read);
|
|
32096
|
+
}
|
|
32097
|
+
async function mapLimit(items, limit, fn) {
|
|
32098
|
+
let next = 0;
|
|
32099
|
+
let firstError;
|
|
32100
|
+
const worker = async () => {
|
|
32101
|
+
while (next < items.length && firstError === void 0) {
|
|
32102
|
+
const i = next++;
|
|
32103
|
+
try {
|
|
32104
|
+
await fn(items[i], i);
|
|
32105
|
+
} catch (err) {
|
|
32106
|
+
if (firstError === void 0) firstError = err;
|
|
32107
|
+
return;
|
|
32108
|
+
}
|
|
32109
|
+
}
|
|
32110
|
+
};
|
|
32111
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length || 1) }, () => worker()));
|
|
32112
|
+
if (firstError !== void 0) throw firstError;
|
|
32113
|
+
}
|
|
32114
|
+
/**
|
|
32115
|
+
* Stream a file up as a Primitive Payload. Two passes over the file (encrypt to
|
|
32116
|
+
* build the manifest, then encrypt again to upload) keep memory bounded to a few
|
|
32117
|
+
* chunks — the whole object is never resident. Returns the content address
|
|
32118
|
+
* (merkleRoot) and the hex CEK needed to download it.
|
|
32119
|
+
*/
|
|
32120
|
+
async function pushFile(filePath, opts) {
|
|
32121
|
+
const chunkSize = opts.chunkSize ?? CHUNK_SIZE;
|
|
32122
|
+
const concurrency = opts.concurrency ?? 3;
|
|
32123
|
+
const { size } = await stat(filePath);
|
|
32124
|
+
const cek = randomBytes(CEK_BYTES);
|
|
32125
|
+
const objectId = randomBytes(OBJECT_ID_BYTES);
|
|
32126
|
+
const chunkCount = size === 0 ? 0 : Math.ceil(size / chunkSize);
|
|
32127
|
+
const fh = await open(filePath, "r");
|
|
32128
|
+
try {
|
|
32129
|
+
const descriptors = [];
|
|
32130
|
+
for (let index = 0; index < chunkCount; index++) {
|
|
32131
|
+
const position = index * chunkSize;
|
|
32132
|
+
const length = Math.min(chunkSize, size - position);
|
|
32133
|
+
const plaintext = await readWindow(fh, position, length);
|
|
32134
|
+
const ciphertext = await encryptChunk(cek, objectId, index, plaintext);
|
|
32135
|
+
descriptors.push({
|
|
32136
|
+
index,
|
|
32137
|
+
ciphertextHash: await contentHashHex(ciphertext),
|
|
32138
|
+
plaintextSize: length,
|
|
32139
|
+
ciphertextSize: ciphertext.length
|
|
32140
|
+
});
|
|
32141
|
+
opts.onProgress?.("encrypt", index + 1, chunkCount);
|
|
32142
|
+
}
|
|
32143
|
+
const root = await merkleRoot(descriptors.map((d) => d.ciphertextHash));
|
|
32144
|
+
await initiate(opts, {
|
|
32145
|
+
version: MANIFEST_VERSION,
|
|
32146
|
+
objectId: toHex(objectId),
|
|
32147
|
+
chunkSize,
|
|
32148
|
+
totalPlaintextSize: size,
|
|
32149
|
+
chunkCount,
|
|
32150
|
+
chunks: descriptors,
|
|
32151
|
+
merkleRoot: root
|
|
32152
|
+
});
|
|
32153
|
+
let uploaded = 0;
|
|
32154
|
+
await mapLimit(descriptors, concurrency, async (d) => {
|
|
32155
|
+
const plaintext = await readWindow(fh, d.index * chunkSize, d.plaintextSize);
|
|
32156
|
+
await putChunk(opts, root, d, await encryptChunk(cek, objectId, d.index, plaintext));
|
|
32157
|
+
opts.onProgress?.("upload", ++uploaded, chunkCount);
|
|
32158
|
+
});
|
|
32159
|
+
await finalize(opts, root);
|
|
32160
|
+
return {
|
|
32161
|
+
merkleRoot: root,
|
|
32162
|
+
cek: toHex(cek),
|
|
32163
|
+
chunkCount,
|
|
32164
|
+
totalBytes: size
|
|
32165
|
+
};
|
|
32166
|
+
} finally {
|
|
32167
|
+
await fh.close();
|
|
32168
|
+
}
|
|
32169
|
+
}
|
|
32170
|
+
/**
|
|
32171
|
+
* Stream a Primitive Payload down to a file, verifying and decrypting one chunk
|
|
32172
|
+
* at a time (bounded memory). Every chunk is content-address-checked before
|
|
32173
|
+
* decryption, so a corrupt or substituted chunk throws.
|
|
32174
|
+
*/
|
|
32175
|
+
async function pullFile(root, outPath, opts) {
|
|
32176
|
+
const manifest = await fetchManifest(opts, root);
|
|
32177
|
+
const computedRoot = await merkleRoot(manifest.chunks.map((c) => c.ciphertextHash));
|
|
32178
|
+
if (computedRoot !== root) throw new Error(`manifest does not match the requested content address (got ${computedRoot})`);
|
|
32179
|
+
const cek = fromHex(opts.cek);
|
|
32180
|
+
const objectId = fromHex(manifest.objectId);
|
|
32181
|
+
const out = createWriteStream(outPath);
|
|
32182
|
+
let onWriteError = () => {};
|
|
32183
|
+
const writeErrored = new Promise((_, reject) => {
|
|
32184
|
+
onWriteError = reject;
|
|
32185
|
+
});
|
|
32186
|
+
writeErrored.catch(() => {});
|
|
32187
|
+
out.on("error", (err) => onWriteError(err));
|
|
32188
|
+
try {
|
|
32189
|
+
let done = 0;
|
|
32190
|
+
for (const d of manifest.chunks) {
|
|
32191
|
+
const ciphertext = await getChunkBytes(opts, root, d);
|
|
32192
|
+
if (await contentHashHex(ciphertext) !== d.ciphertextHash) throw new Error(`chunk ${d.index} failed integrity check`);
|
|
32193
|
+
const plaintext = await decryptChunk(cek, objectId, d.index, ciphertext);
|
|
32194
|
+
if (!out.write(plaintext)) await Promise.race([new Promise((resolve) => out.once("drain", resolve)), writeErrored]);
|
|
32195
|
+
opts.onProgress?.("download", ++done, manifest.chunkCount);
|
|
32196
|
+
}
|
|
32197
|
+
await Promise.race([new Promise((resolve) => out.end(resolve)), writeErrored]);
|
|
32198
|
+
} catch (err) {
|
|
32199
|
+
out.destroy();
|
|
32200
|
+
await rm(outPath, { force: true });
|
|
32201
|
+
throw err;
|
|
32202
|
+
}
|
|
32203
|
+
return manifest;
|
|
32204
|
+
}
|
|
32205
|
+
//#endregion
|
|
32206
|
+
//#region src/oclif/commands/payloads.ts
|
|
32207
|
+
const DEFAULT_API_BASE_URL = "https://api.primitive.dev";
|
|
32208
|
+
const authFlags = {
|
|
32209
|
+
"api-key": Flags.string({
|
|
32210
|
+
description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved login credentials)",
|
|
32211
|
+
env: "PRIMITIVE_API_KEY"
|
|
32212
|
+
}),
|
|
32213
|
+
"api-base-url": Flags.string({
|
|
32214
|
+
description: "Override the API base URL. Internal testing only.",
|
|
32215
|
+
env: "PRIMITIVE_API_BASE_URL",
|
|
32216
|
+
hidden: true
|
|
32217
|
+
})
|
|
32218
|
+
};
|
|
32219
|
+
function resolveClient(configDir, flags) {
|
|
32220
|
+
const auth = resolveCliAuth({
|
|
32221
|
+
configDir,
|
|
32222
|
+
apiKey: flags["api-key"],
|
|
32223
|
+
apiBaseUrl: flags["api-base-url"]
|
|
32224
|
+
});
|
|
32225
|
+
if (!auth.apiKey) throw new Error("Not authenticated: set PRIMITIVE_API_KEY, pass --api-key, or run `primitive login`.");
|
|
32226
|
+
return {
|
|
32227
|
+
baseUrl: auth.apiBaseUrl ?? DEFAULT_API_BASE_URL,
|
|
32228
|
+
apiKey: auth.apiKey
|
|
32229
|
+
};
|
|
32230
|
+
}
|
|
32231
|
+
function progressToStderr() {
|
|
32232
|
+
let lastPct = -1;
|
|
32233
|
+
return (phase, done, total) => {
|
|
32234
|
+
const pct = total === 0 ? 100 : Math.floor(done / total * 100);
|
|
32235
|
+
if (pct !== lastPct) {
|
|
32236
|
+
lastPct = pct;
|
|
32237
|
+
process.stderr.write(`\r${phase}: ${done}/${total} (${pct}%) `);
|
|
32238
|
+
if (done === total) process.stderr.write("\n");
|
|
32239
|
+
}
|
|
32240
|
+
};
|
|
32241
|
+
}
|
|
32242
|
+
var PayloadsPushCommand = class PayloadsPushCommand extends Command {
|
|
32243
|
+
static description = `Upload a file as a Primitive Payload — a large, content-addressed, end-to-end-encrypted object.
|
|
32244
|
+
|
|
32245
|
+
The file is chunked and encrypted client-side and streamed up in bounded
|
|
32246
|
+
memory (multi-GB files never load fully into RAM). Prints the object's content
|
|
32247
|
+
address (merkle_root) and the hex CEK required to download it — keep the CEK
|
|
32248
|
+
secret; without it the object cannot be decrypted.`;
|
|
32249
|
+
static summary = "Stream-upload a file as an encrypted payload";
|
|
32250
|
+
static examples = ["<%= config.bin %> payloads push ./big-video.mp4"];
|
|
32251
|
+
static args = { file: Args.string({
|
|
32252
|
+
required: true,
|
|
32253
|
+
description: "Path to the file to upload"
|
|
32254
|
+
}) };
|
|
32255
|
+
static flags = {
|
|
32256
|
+
...authFlags,
|
|
32257
|
+
concurrency: Flags.integer({
|
|
32258
|
+
description: "Parallel chunk uploads",
|
|
32259
|
+
default: 3
|
|
32260
|
+
}),
|
|
32261
|
+
quiet: Flags.boolean({
|
|
32262
|
+
description: "Suppress progress output",
|
|
32263
|
+
default: false
|
|
32264
|
+
})
|
|
32265
|
+
};
|
|
32266
|
+
async run() {
|
|
32267
|
+
const { args, flags } = await this.parse(PayloadsPushCommand);
|
|
32268
|
+
const { baseUrl, apiKey } = resolveClient(this.config.configDir, flags);
|
|
32269
|
+
const result = await pushFile(args.file, {
|
|
32270
|
+
baseUrl,
|
|
32271
|
+
apiKey,
|
|
32272
|
+
concurrency: flags.concurrency,
|
|
32273
|
+
onProgress: flags.quiet ? void 0 : progressToStderr()
|
|
32274
|
+
});
|
|
32275
|
+
this.log(JSON.stringify({
|
|
32276
|
+
merkle_root: result.merkleRoot,
|
|
32277
|
+
cek: result.cek,
|
|
32278
|
+
chunk_count: result.chunkCount,
|
|
32279
|
+
total_bytes: result.totalBytes
|
|
32280
|
+
}, null, 2));
|
|
32281
|
+
}
|
|
32282
|
+
};
|
|
32283
|
+
var PayloadsPullCommand = class PayloadsPullCommand extends Command {
|
|
32284
|
+
static description = `Download and decrypt a Primitive Payload to a file.
|
|
32285
|
+
|
|
32286
|
+
Streams one chunk at a time, verifying each against its content address before
|
|
32287
|
+
decryption, so a corrupt or substituted chunk fails loudly. Requires the hex
|
|
32288
|
+
CEK printed by \`payloads push\`.`;
|
|
32289
|
+
static summary = "Stream-download and decrypt a payload to a file";
|
|
32290
|
+
static examples = ["<%= config.bin %> payloads pull <merkle_root> --cek <hex> --out ./restored.mp4"];
|
|
32291
|
+
static args = { root: Args.string({
|
|
32292
|
+
required: true,
|
|
32293
|
+
description: "Object content address (merkle_root)"
|
|
32294
|
+
}) };
|
|
32295
|
+
static flags = {
|
|
32296
|
+
...authFlags,
|
|
32297
|
+
out: Flags.string({
|
|
32298
|
+
required: true,
|
|
32299
|
+
description: "Output file path"
|
|
32300
|
+
}),
|
|
32301
|
+
cek: Flags.string({
|
|
32302
|
+
required: true,
|
|
32303
|
+
description: "Hex content-encryption key from `payloads push`"
|
|
32304
|
+
}),
|
|
32305
|
+
quiet: Flags.boolean({
|
|
32306
|
+
description: "Suppress progress output",
|
|
32307
|
+
default: false
|
|
32308
|
+
})
|
|
32309
|
+
};
|
|
32310
|
+
async run() {
|
|
32311
|
+
const { args, flags } = await this.parse(PayloadsPullCommand);
|
|
32312
|
+
const { baseUrl, apiKey } = resolveClient(this.config.configDir, flags);
|
|
32313
|
+
const manifest = await pullFile(args.root, flags.out, {
|
|
32314
|
+
baseUrl,
|
|
32315
|
+
apiKey,
|
|
32316
|
+
cek: flags.cek,
|
|
32317
|
+
onProgress: flags.quiet ? void 0 : progressToStderr()
|
|
32318
|
+
});
|
|
32319
|
+
this.log(JSON.stringify({
|
|
32320
|
+
merkle_root: args.root,
|
|
32321
|
+
out: flags.out,
|
|
32322
|
+
chunk_count: manifest.chunkCount,
|
|
32323
|
+
total_bytes: manifest.totalPlaintextSize
|
|
32324
|
+
}, null, 2));
|
|
32325
|
+
}
|
|
32326
|
+
};
|
|
32327
|
+
//#endregion
|
|
31760
32328
|
//#region ../sdk-node/dist/x402/index.js
|
|
31761
32329
|
/**
|
|
31762
32330
|
* x402 client-side signing.
|
|
@@ -33186,7 +33754,7 @@ async function pollForSettlementInteraction(params) {
|
|
|
33186
33754
|
}
|
|
33187
33755
|
if (receipt) return receipt;
|
|
33188
33756
|
if (deadline !== null && Date.now() >= deadline) break;
|
|
33189
|
-
await sleep$
|
|
33757
|
+
await sleep$2(params.intervalSeconds * 1e3);
|
|
33190
33758
|
}
|
|
33191
33759
|
return null;
|
|
33192
33760
|
}
|
|
@@ -34569,6 +35137,7 @@ var SendCommand = class SendCommand extends Command {
|
|
|
34569
35137
|
"<%= config.bin %> send --to alice@example.com --body 'See attached.' --attachment ./report.pdf",
|
|
34570
35138
|
"<%= config.bin %> send --to alice@example.com --from support@yourcompany.com --subject 'Quick question' --body 'Are you free Thursday?'",
|
|
34571
35139
|
"<%= config.bin %> send --to alice@example.com --html '<p>Hello!</p>'",
|
|
35140
|
+
"<%= config.bin %> send --to alice@example.com --cc bob@example.com --bcc audit@example.com --body 'Loop bob in; audit copy stays hidden.'",
|
|
34572
35141
|
"<%= config.bin %> send --to alice@example.com --body 'Confirmed' --wait",
|
|
34573
35142
|
"<%= config.bin %> send --to inbox@your-managed-domain.primitive.email --body 'self-loop smoke test' --wait # any *.primitive.email address routes back to the sending account; useful for proving outbound + inbound work end-to-end"
|
|
34574
35143
|
];
|
|
@@ -34598,6 +35167,14 @@ var SendCommand = class SendCommand extends Command {
|
|
|
34598
35167
|
description: "Attach a file to the email. Repeatable. Sends file bytes as a MIME attachment; use --body-file only for message body text.",
|
|
34599
35168
|
multiple: true
|
|
34600
35169
|
}),
|
|
35170
|
+
cc: Flags.string({
|
|
35171
|
+
description: "Carbon-copy recipient. Repeatable for multiple. Cc recipients are visible to everyone who receives the message.",
|
|
35172
|
+
multiple: true
|
|
35173
|
+
}),
|
|
35174
|
+
bcc: Flags.string({
|
|
35175
|
+
description: "Blind-carbon-copy recipient. Repeatable for multiple. Bcc recipients receive the message but are not disclosed to the other recipients.",
|
|
35176
|
+
multiple: true
|
|
35177
|
+
}),
|
|
34601
35178
|
"in-reply-to": Flags.string({ description: "Message-Id of the parent email when threading a reply on the wire. For replying to an inbound message you received, prefer `primitive reply --id <inbound-id>`." }),
|
|
34602
35179
|
wait: Flags.boolean({ description: "Block until the receiving MTA returns an outcome. Without --wait, the call returns once Primitive has accepted the message for delivery." }),
|
|
34603
35180
|
"wait-timeout-ms": Flags.integer({ description: "Maximum time to wait when --wait is set. Defaults to 30000ms." }),
|
|
@@ -34632,6 +35209,8 @@ var SendCommand = class SendCommand extends Command {
|
|
|
34632
35209
|
body: {
|
|
34633
35210
|
from,
|
|
34634
35211
|
to: flags.to,
|
|
35212
|
+
...flags.cc !== void 0 ? { cc: flags.cc } : {},
|
|
35213
|
+
...flags.bcc !== void 0 ? { bcc: flags.bcc } : {},
|
|
34635
35214
|
subject,
|
|
34636
35215
|
...bodies.body !== void 0 ? { body_text: bodies.body } : {},
|
|
34637
35216
|
...bodies.html !== void 0 ? { body_html: bodies.html } : {},
|
|
@@ -36148,6 +36727,8 @@ const COMMANDS = {
|
|
|
36148
36727
|
"org:secrets:list": OrgSecretsListCommand,
|
|
36149
36728
|
"org:secrets:set": OrgSecretsSetCommand,
|
|
36150
36729
|
"org:secrets:remove": OrgSecretsRemoveCommand,
|
|
36730
|
+
"payloads:push": PayloadsPushCommand,
|
|
36731
|
+
"payloads:pull": PayloadsPullCommand,
|
|
36151
36732
|
"functions:test": FunctionsTestFunctionCommand,
|
|
36152
36733
|
"functions:test-function": FunctionsTestFunctionCommand,
|
|
36153
36734
|
"functions:route-set": FunctionsRouteSetCommand,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primcli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.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,
|