primitive 1.26.1 → 1.27.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.
Files changed (3) hide show
  1. package/README.md +17 -0
  2. package/dist/oclif/index.js +1456 -370
  3. package/package.json +1 -1
@@ -1,4 +1,4 @@
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";
1
+ import { A as saveActiveChatState, C as resolveCliAuth, D as deleteChatState, E as chatStatePath, M as createClient$1, N as createConfig$1, 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
3
  import { chmodSync, closeSync, constants, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { createHash, randomUUID, scryptSync } from "node:crypto";
@@ -27,7 +27,7 @@ var __exportAll = (all, no_symbols) => {
27
27
  };
28
28
  //#endregion
29
29
  //#region ../packages/api-core/src/api/client.gen.ts
30
- const client = createClient(createConfig({ baseUrl: "https://api.primitive.dev/v1" }));
30
+ const client = createClient$1(createConfig$1({ baseUrl: "https://api.primitive.dev/v1" }));
31
31
  //#endregion
32
32
  //#region ../packages/api-core/src/api/sdk.gen.ts
33
33
  var sdk_gen_exports = /* @__PURE__ */ __exportAll({
@@ -36,7 +36,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
36
36
  cancelSentEmail: () => cancelSentEmail,
37
37
  checkDomainDns: () => checkDomainDns,
38
38
  cliLogout: () => cliLogout,
39
- completeWebhookEvent: () => completeWebhookEvent,
39
+ completeWebhookEvent: () => completeWebhookEvent$1,
40
40
  createAgentAccount: () => createAgentAccount,
41
41
  createAgentClaimLink: () => createAgentClaimLink,
42
42
  createChallenge: () => createChallenge,
@@ -71,7 +71,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
71
71
  downloadEmailAttachmentPart: () => downloadEmailAttachmentPart,
72
72
  downloadRawEmail: () => downloadRawEmail,
73
73
  downloadSentAttachmentPart: () => downloadSentAttachmentPart,
74
- getAccount: () => getAccount,
74
+ getAccount: () => getAccount$1,
75
75
  getAgent: () => getAgent,
76
76
  getChallenge: () => getChallenge,
77
77
  getConversation: () => getConversation,
@@ -117,7 +117,7 @@ var sdk_gen_exports = /* @__PURE__ */ __exportAll({
117
117
  payChallenge: () => payChallenge,
118
118
  pollCliLogin: () => pollCliLogin,
119
119
  publishAgent: () => publishAgent,
120
- pullWebhookEvent: () => pullWebhookEvent,
120
+ pullWebhookEvent: () => pullWebhookEvent$1,
121
121
  registerPayoutAddress: () => registerPayoutAddress,
122
122
  removeAgentConnection: () => removeAgentConnection,
123
123
  reorderRoutes: () => reorderRoutes,
@@ -411,7 +411,7 @@ const cliLogout = (options) => (options?.client ?? client).post({
411
411
  /**
412
412
  * Get account info
413
413
  */
414
- const getAccount = (options) => (options?.client ?? client).get({
414
+ const getAccount$1 = (options) => (options?.client ?? client).get({
415
415
  security: [{
416
416
  scheme: "bearer",
417
417
  type: "http"
@@ -1025,7 +1025,7 @@ const updateEndpoint = (options) => (options.client ?? client).patch({
1025
1025
  *
1026
1026
  * Wait up to 30 seconds for one existing event. The body string preserves the exact webhook serialization; headers and canonical occurrence/type metadata travel alongside it. A fixed lease protects the attempt while a short handler accepts input. Retry may redeliver an occurrence: deduplicate event_id. Empty delivery means no offer now, not proof all input was delivered; inspect backlog and persistent gap_count/last_gap_reason. Queue retention is 24 hours and does not extend source-content retention. Disconnecting preserves pending work. Same named destination shares consumption; different destinations get independent copies.
1027
1027
  */
1028
- const pullWebhookEvent = (options) => (options.client ?? client).post({
1028
+ const pullWebhookEvent$1 = (options) => (options.client ?? client).post({
1029
1029
  security: [{
1030
1030
  scheme: "bearer",
1031
1031
  type: "http"
@@ -1042,7 +1042,7 @@ const pullWebhookEvent = (options) => (options.client ?? client).post({
1042
1042
  *
1043
1043
  * Report normalized handling evidence for the queue, attempt and lease. Required JSON fields: queue_id, delivery_id, lease_token, mode, duration_ms. For mode=exec include exit_code; stdout includes write_succeeded; http includes status_code and optional transport_error, error_code, confirmed. Supply this discriminated request with --raw-body in the generated CLI. The server classifies success and retry. Duplicate identical completion is idempotent; a stale lease cannot finish a newer attempt. Exec success means durable input acceptance, not completion of agent reasoning. Only successful HTTP handling with recognized confirmation evidence can confirm content discard. Never send arbitrary handler output or HTTP response bodies.
1044
1044
  */
1045
- const completeWebhookEvent = (options) => (options.client ?? client).post({
1045
+ const completeWebhookEvent$1 = (options) => (options.client ?? client).post({
1046
1046
  security: [{
1047
1047
  scheme: "bearer",
1048
1048
  type: "http"
@@ -11405,14 +11405,16 @@ const openapiDocument = {
11405
11405
  "oneOf": [
11406
11406
  { "$ref": "#/components/schemas/CompleteWebhookHttpInput" },
11407
11407
  { "$ref": "#/components/schemas/CompleteWebhookExecInput" },
11408
- { "$ref": "#/components/schemas/CompleteWebhookStdoutInput" }
11408
+ { "$ref": "#/components/schemas/CompleteWebhookStdoutInput" },
11409
+ { "$ref": "#/components/schemas/CompleteWebhookSdkInput" }
11409
11410
  ],
11410
11411
  "discriminator": {
11411
11412
  "propertyName": "mode",
11412
11413
  "mapping": {
11413
11414
  "http": "#/components/schemas/CompleteWebhookHttpInput",
11414
11415
  "exec": "#/components/schemas/CompleteWebhookExecInput",
11415
- "stdout": "#/components/schemas/CompleteWebhookStdoutInput"
11416
+ "stdout": "#/components/schemas/CompleteWebhookStdoutInput",
11417
+ "sdk": "#/components/schemas/CompleteWebhookSdkInput"
11416
11418
  }
11417
11419
  }
11418
11420
  },
@@ -11566,6 +11568,49 @@ const openapiDocument = {
11566
11568
  ],
11567
11569
  "additionalProperties": false
11568
11570
  },
11571
+ "CompleteWebhookSdkInput": {
11572
+ "type": "object",
11573
+ "properties": {
11574
+ "queue_id": {
11575
+ "type": "string",
11576
+ "format": "uuid",
11577
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
11578
+ },
11579
+ "delivery_id": {
11580
+ "type": "string",
11581
+ "format": "uuid",
11582
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
11583
+ },
11584
+ "lease_token": {
11585
+ "type": "string",
11586
+ "format": "uuid",
11587
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
11588
+ },
11589
+ "duration_ms": {
11590
+ "type": "integer",
11591
+ "minimum": 0,
11592
+ "maximum": 3e4
11593
+ },
11594
+ "mode": {
11595
+ "type": "string",
11596
+ "enum": ["sdk"]
11597
+ },
11598
+ "accepted": { "type": "boolean" },
11599
+ "transport_error": {
11600
+ "type": "string",
11601
+ "enum": ["timeout", "io"]
11602
+ }
11603
+ },
11604
+ "required": [
11605
+ "queue_id",
11606
+ "delivery_id",
11607
+ "lease_token",
11608
+ "duration_ms",
11609
+ "mode",
11610
+ "accepted"
11611
+ ],
11612
+ "additionalProperties": false
11613
+ },
11569
11614
  "CompleteWebhookResponse": {
11570
11615
  "type": "object",
11571
11616
  "properties": {
@@ -11599,6 +11644,31 @@ const openapiDocument = {
11599
11644
  "Endpoint": {
11600
11645
  "type": "object",
11601
11646
  "properties": {
11647
+ "receiver_capabilities": {
11648
+ "type": "object",
11649
+ "properties": {
11650
+ "completion_modes": {
11651
+ "type": "array",
11652
+ "items": {
11653
+ "type": "string",
11654
+ "enum": [
11655
+ "http",
11656
+ "exec",
11657
+ "stdout",
11658
+ "sdk"
11659
+ ]
11660
+ }
11661
+ },
11662
+ "stream_protocols": {
11663
+ "type": "array",
11664
+ "items": {
11665
+ "type": "string",
11666
+ "enum": ["primitive.events.v1"]
11667
+ }
11668
+ }
11669
+ },
11670
+ "required": ["completion_modes", "stream_protocols"]
11671
+ },
11602
11672
  "id": {
11603
11673
  "type": "string",
11604
11674
  "format": "uuid"
@@ -17908,6 +17978,49 @@ const operationManifest = [
17908
17978
  "write_succeeded"
17909
17979
  ],
17910
17980
  "additionalProperties": false
17981
+ },
17982
+ {
17983
+ "type": "object",
17984
+ "properties": {
17985
+ "queue_id": {
17986
+ "type": "string",
17987
+ "format": "uuid",
17988
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
17989
+ },
17990
+ "delivery_id": {
17991
+ "type": "string",
17992
+ "format": "uuid",
17993
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
17994
+ },
17995
+ "lease_token": {
17996
+ "type": "string",
17997
+ "format": "uuid",
17998
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
17999
+ },
18000
+ "duration_ms": {
18001
+ "type": "integer",
18002
+ "minimum": 0,
18003
+ "maximum": 3e4
18004
+ },
18005
+ "mode": {
18006
+ "type": "string",
18007
+ "enum": ["sdk"]
18008
+ },
18009
+ "accepted": { "type": "boolean" },
18010
+ "transport_error": {
18011
+ "type": "string",
18012
+ "enum": ["timeout", "io"]
18013
+ }
18014
+ },
18015
+ "required": [
18016
+ "queue_id",
18017
+ "delivery_id",
18018
+ "lease_token",
18019
+ "duration_ms",
18020
+ "mode",
18021
+ "accepted"
18022
+ ],
18023
+ "additionalProperties": false
17911
18024
  }
17912
18025
  ],
17913
18026
  "discriminator": {
@@ -17915,7 +18028,8 @@ const operationManifest = [
17915
18028
  "mapping": {
17916
18029
  "http": "#/components/schemas/CompleteWebhookHttpInput",
17917
18030
  "exec": "#/components/schemas/CompleteWebhookExecInput",
17918
- "stdout": "#/components/schemas/CompleteWebhookStdoutInput"
18031
+ "stdout": "#/components/schemas/CompleteWebhookStdoutInput",
18032
+ "sdk": "#/components/schemas/CompleteWebhookSdkInput"
17919
18033
  }
17920
18034
  }
17921
18035
  },
@@ -18026,6 +18140,31 @@ const operationManifest = [
18026
18140
  "responseSchema": {
18027
18141
  "type": "object",
18028
18142
  "properties": {
18143
+ "receiver_capabilities": {
18144
+ "type": "object",
18145
+ "properties": {
18146
+ "completion_modes": {
18147
+ "type": "array",
18148
+ "items": {
18149
+ "type": "string",
18150
+ "enum": [
18151
+ "http",
18152
+ "exec",
18153
+ "stdout",
18154
+ "sdk"
18155
+ ]
18156
+ }
18157
+ },
18158
+ "stream_protocols": {
18159
+ "type": "array",
18160
+ "items": {
18161
+ "type": "string",
18162
+ "enum": ["primitive.events.v1"]
18163
+ }
18164
+ }
18165
+ },
18166
+ "required": ["completion_modes", "stream_protocols"]
18167
+ },
18029
18168
  "id": {
18030
18169
  "type": "string",
18031
18170
  "format": "uuid"
@@ -18177,6 +18316,31 @@ const operationManifest = [
18177
18316
  "items": {
18178
18317
  "type": "object",
18179
18318
  "properties": {
18319
+ "receiver_capabilities": {
18320
+ "type": "object",
18321
+ "properties": {
18322
+ "completion_modes": {
18323
+ "type": "array",
18324
+ "items": {
18325
+ "type": "string",
18326
+ "enum": [
18327
+ "http",
18328
+ "exec",
18329
+ "stdout",
18330
+ "sdk"
18331
+ ]
18332
+ }
18333
+ },
18334
+ "stream_protocols": {
18335
+ "type": "array",
18336
+ "items": {
18337
+ "type": "string",
18338
+ "enum": ["primitive.events.v1"]
18339
+ }
18340
+ }
18341
+ },
18342
+ "required": ["completion_modes", "stream_protocols"]
18343
+ },
18180
18344
  "id": {
18181
18345
  "type": "string",
18182
18346
  "format": "uuid"
@@ -18618,6 +18782,31 @@ const operationManifest = [
18618
18782
  "responseSchema": {
18619
18783
  "type": "object",
18620
18784
  "properties": {
18785
+ "receiver_capabilities": {
18786
+ "type": "object",
18787
+ "properties": {
18788
+ "completion_modes": {
18789
+ "type": "array",
18790
+ "items": {
18791
+ "type": "string",
18792
+ "enum": [
18793
+ "http",
18794
+ "exec",
18795
+ "stdout",
18796
+ "sdk"
18797
+ ]
18798
+ }
18799
+ },
18800
+ "stream_protocols": {
18801
+ "type": "array",
18802
+ "items": {
18803
+ "type": "string",
18804
+ "enum": ["primitive.events.v1"]
18805
+ }
18806
+ }
18807
+ },
18808
+ "required": ["completion_modes", "stream_protocols"]
18809
+ },
18621
18810
  "id": {
18622
18811
  "type": "string",
18623
18812
  "format": "uuid"
@@ -27644,7 +27833,7 @@ function isMemoryJsonValue(value, seen = /* @__PURE__ */ new Set()) {
27644
27833
  const API_HEADERS_ENV = "PRIMITIVE_API_HEADERS";
27645
27834
  const OAUTH_REFRESH_SKEW_MS = 60 * 1e3;
27646
27835
  const SAVED_CLI_OAUTH_SESSION_EXPIRED_MESSAGE = "Saved Primitive CLI OAuth session expired or was revoked. Run `primitive signin` to authenticate again.";
27647
- function mergeHeaders(...headers) {
27836
+ function mergeHeaders$1(...headers) {
27648
27837
  const merged = {};
27649
27838
  for (const headerSet of headers) {
27650
27839
  if (!headerSet) continue;
@@ -27682,7 +27871,7 @@ function resolveCliApiRequestConfig(params) {
27682
27871
  apiBaseUrl,
27683
27872
  baseUrlOverridden: apiBaseUrl !== void 0,
27684
27873
  environmentName: currentEnvironment?.name ?? null,
27685
- headers: mergeHeaders(currentEnvironment?.config.headers, cliApiHeadersFromEnv(params.env)),
27874
+ headers: mergeHeaders$1(currentEnvironment?.config.headers, cliApiHeadersFromEnv(params.env)),
27686
27875
  resolvedApiBaseUrl: normalizeApiBaseUrl(apiBaseUrl)
27687
27876
  };
27688
27877
  }
@@ -30335,7 +30524,7 @@ function checkApiKey(opts) {
30335
30524
  }
30336
30525
  async function checkAccount(client) {
30337
30526
  try {
30338
- const result = await getAccount({
30527
+ const result = await getAccount$1({
30339
30528
  client: client.client,
30340
30529
  responseStyle: "fields"
30341
30530
  });
@@ -32114,8 +32303,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
32114
32303
  name: "Primitive Team",
32115
32304
  url: "https://primitive.dev"
32116
32305
  };
32117
- const SDK_VERSION_RANGE = "^1.26.1";
32118
- const CLI_VERSION_RANGE = "^1.26.1";
32306
+ const SDK_VERSION_RANGE = "^1.27.0";
32307
+ const CLI_VERSION_RANGE = "^1.27.0";
32119
32308
  const ESBUILD_VERSION_RANGE = "^0.27.0";
32120
32309
  function renderHandler() {
32121
32310
  return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
@@ -34149,6 +34338,11 @@ const FORWARDED_HEADERS = new Set([
34149
34338
  "x-primitive-email-id",
34150
34339
  "x-primitive-endpoint-id"
34151
34340
  ]);
34341
+ function normalizeForwardTarget(target) {
34342
+ if (/^\d+$/.test(target)) return `http://localhost:${target}`;
34343
+ if (/^(localhost|127\.0\.0\.1|\[::1\])(?::|\/|$)/.test(target)) return `http://${target}`;
34344
+ return target;
34345
+ }
34152
34346
  function validateListenHandlerOptions(options) {
34153
34347
  if (options.exec !== void 0 && process.platform === "win32") throw new Error("--exec requires a POSIX shell and process groups. On Windows, use --forward-to with a local HTTP handler.");
34154
34348
  if (options.exec !== void 0 && options.forwardTo !== void 0) throw new Error("Choose either --exec or --forward-to.");
@@ -34156,7 +34350,7 @@ function validateListenHandlerOptions(options) {
34156
34350
  if (options.forwardTo !== void 0) {
34157
34351
  let url;
34158
34352
  try {
34159
- url = new URL(options.forwardTo);
34353
+ url = new URL(normalizeForwardTarget(options.forwardTo));
34160
34354
  } catch {
34161
34355
  throw new Error("--forward-to requires an HTTP or HTTPS URL.");
34162
34356
  }
@@ -34301,7 +34495,7 @@ var ForwardFailure = class extends Error {
34301
34495
  };
34302
34496
  async function forward(target, delivery, signal) {
34303
34497
  signal.throwIfAborted();
34304
- const url = new URL(target);
34498
+ const url = new URL(normalizeForwardTarget(target));
34305
34499
  const body = Buffer.from(delivery.body, "utf8");
34306
34500
  const headers = {};
34307
34501
  for (const [name, value] of Object.entries(delivery.headers)) if (FORWARDED_HEADERS.has(name.toLowerCase())) headers[name.toLowerCase()] = value;
@@ -34441,6 +34635,1169 @@ function createListenHandler(options = {}) {
34441
34635
  };
34442
34636
  }
34443
34637
  //#endregion
34638
+ //#region ../sdk-node/dist/payloads-BMngnJlE.js
34639
+ /**
34640
+ * Load a Node builtin at call time. Exported for direct testing of the
34641
+ * unavailable-module error path; production code goes through the typed
34642
+ * wrappers below.
34643
+ */
34644
+ async function loadBuiltin(specifier, operation) {
34645
+ let builtin;
34646
+ if (typeof process !== "undefined" && typeof process.getBuiltinModule === "function") try {
34647
+ builtin = process.getBuiltinModule(specifier);
34648
+ } catch {
34649
+ builtin = void 0;
34650
+ }
34651
+ if (builtin !== void 0 && builtin !== null) return builtin;
34652
+ try {
34653
+ return await import(specifier);
34654
+ } catch (cause) {
34655
+ throw new Error(`${operation} requires file system access, which this runtime does not provide. Pass in-memory bytes instead of a file path when running outside Node.js.`, { cause });
34656
+ }
34657
+ }
34658
+ /** `node:fs`, loaded lazily so Workers-style bundles never see the import. */
34659
+ function loadNodeFs(operation) {
34660
+ return loadBuiltin("node:fs", operation);
34661
+ }
34662
+ /** `node:fs/promises`, loaded lazily for the same reason as {@link loadNodeFs}. */
34663
+ function loadNodeFsPromises(operation) {
34664
+ return loadBuiltin("node:fs/promises", operation);
34665
+ }
34666
+ const CHUNK_SIZE = 64 * 1024 * 1024;
34667
+ const MANIFEST_VERSION = 1;
34668
+ const CHUNK_KDF_INFO = "payloads-chunk";
34669
+ const OBJECT_ID_BYTES = 16;
34670
+ const CEK_BYTES = 32;
34671
+ function subtle() {
34672
+ return globalThis.crypto.subtle;
34673
+ }
34674
+ const textEncoder = new TextEncoder();
34675
+ function randomBytes(length) {
34676
+ const out = new Uint8Array(length);
34677
+ globalThis.crypto.getRandomValues(out);
34678
+ return out;
34679
+ }
34680
+ function toHex(bytes) {
34681
+ let out = "";
34682
+ for (const b of bytes) out += b.toString(16).padStart(2, "0");
34683
+ return out;
34684
+ }
34685
+ function fromHex(hex) {
34686
+ if (hex.length % 2 !== 0) throw new Error("invalid hex: odd length");
34687
+ if (hex.length > 0 && !/^[0-9a-fA-F]+$/.test(hex)) throw new Error("invalid hex: non-hex character");
34688
+ const out = new Uint8Array(hex.length / 2);
34689
+ for (let i = 0; i < out.length; i++) out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
34690
+ return out;
34691
+ }
34692
+ async function contentHash(bytes) {
34693
+ return new Uint8Array(await subtle().digest("SHA-256", bytes));
34694
+ }
34695
+ async function contentHashHex(bytes) {
34696
+ return toHex(await contentHash(bytes));
34697
+ }
34698
+ /** Binary Merkle root over ordered hex leaf hashes (odd node promoted). */
34699
+ async function merkleRoot(leafHashesHex) {
34700
+ if (leafHashesHex.length === 0) return contentHashHex(new Uint8Array(0));
34701
+ let level = leafHashesHex.map(fromHex);
34702
+ while (level.length > 1) {
34703
+ const next = [];
34704
+ for (let i = 0; i < level.length; i += 2) {
34705
+ const left = level[i];
34706
+ const right = i + 1 < level.length ? level[i + 1] : level[i];
34707
+ const combined = new Uint8Array(left.length + right.length);
34708
+ combined.set(left, 0);
34709
+ combined.set(right, left.length);
34710
+ next.push(await contentHash(combined));
34711
+ }
34712
+ level = next;
34713
+ }
34714
+ return toHex(level[0]);
34715
+ }
34716
+ async function deriveChunkKey(cek, index) {
34717
+ const baseKey = await subtle().importKey("raw", cek, "HKDF", false, ["deriveKey"]);
34718
+ const info = textEncoder.encode(`${CHUNK_KDF_INFO}:${index}`);
34719
+ return subtle().deriveKey({
34720
+ name: "HKDF",
34721
+ hash: "SHA-256",
34722
+ salt: new Uint8Array(32),
34723
+ info
34724
+ }, baseKey, {
34725
+ name: "AES-GCM",
34726
+ length: 256
34727
+ }, false, ["encrypt", "decrypt"]);
34728
+ }
34729
+ function chunkNonce(index) {
34730
+ const nonce = new Uint8Array(12);
34731
+ new DataView(nonce.buffer).setUint32(8, index >>> 0, false);
34732
+ return nonce;
34733
+ }
34734
+ function chunkAad(objectId, index) {
34735
+ const aad = new Uint8Array(objectId.length + 4);
34736
+ aad.set(objectId, 0);
34737
+ new DataView(aad.buffer).setUint32(objectId.length, index >>> 0, false);
34738
+ return aad;
34739
+ }
34740
+ async function encryptChunk(cek, objectId, index, plaintext) {
34741
+ const key = await deriveChunkKey(cek, index);
34742
+ const ct = await subtle().encrypt({
34743
+ name: "AES-GCM",
34744
+ iv: chunkNonce(index),
34745
+ additionalData: chunkAad(objectId, index)
34746
+ }, key, plaintext);
34747
+ return new Uint8Array(ct);
34748
+ }
34749
+ async function decryptChunk(cek, objectId, index, ciphertext) {
34750
+ const key = await deriveChunkKey(cek, index);
34751
+ const pt = await subtle().decrypt({
34752
+ name: "AES-GCM",
34753
+ iv: chunkNonce(index),
34754
+ additionalData: chunkAad(objectId, index)
34755
+ }, key, ciphertext);
34756
+ return new Uint8Array(pt);
34757
+ }
34758
+ function authHeaders(apiKey) {
34759
+ return { authorization: `Bearer ${apiKey}` };
34760
+ }
34761
+ function apiRoot(baseUrl) {
34762
+ let end = baseUrl.length;
34763
+ while (end > 0 && baseUrl.charCodeAt(end - 1) === 47) end--;
34764
+ return `${baseUrl.slice(0, end)}/v1/payloads`;
34765
+ }
34766
+ const DEFAULT_MAX_RETRIES = 6;
34767
+ const RETRYABLE_STATUS = new Set([
34768
+ 429,
34769
+ 500,
34770
+ 502,
34771
+ 503,
34772
+ 504
34773
+ ]);
34774
+ function sleep$1(ms) {
34775
+ return new Promise((resolve) => setTimeout(resolve, ms));
34776
+ }
34777
+ /**
34778
+ * fetch with exponential backoff on transient failures. Request bodies here are
34779
+ * in-memory buffers/strings (never streams), so a retry can safely resend them.
34780
+ */
34781
+ async function retryingFetch(url, init, label, maxRetries = DEFAULT_MAX_RETRIES) {
34782
+ for (let attempt = 0;; attempt++) {
34783
+ let res;
34784
+ let networkError;
34785
+ try {
34786
+ res = await fetch(url, init);
34787
+ } catch (err) {
34788
+ networkError = err;
34789
+ }
34790
+ if (res && !RETRYABLE_STATUS.has(res.status)) return res;
34791
+ if (attempt >= maxRetries) {
34792
+ if (res) return res;
34793
+ throw new Error(`${label}: network error after ${attempt} retries: ${networkError?.message ?? networkError}`);
34794
+ }
34795
+ if (res) await res.text().catch(() => {});
34796
+ await sleep$1(Math.min(1e3 * 2 ** attempt, 15e3) + Math.floor(Math.random() * 250));
34797
+ }
34798
+ }
34799
+ async function initiate(opts, manifest) {
34800
+ const res = await retryingFetch(apiRoot(opts.baseUrl), {
34801
+ method: "POST",
34802
+ headers: {
34803
+ ...authHeaders(opts.apiKey),
34804
+ "content-type": "application/json"
34805
+ },
34806
+ body: JSON.stringify({ manifest })
34807
+ }, "initiate");
34808
+ if (!res.ok) throw new Error(`initiate failed: HTTP ${res.status} ${await res.text()}`);
34809
+ await res.text();
34810
+ }
34811
+ async function putChunk(opts, root, d, bytes) {
34812
+ const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/chunks/${d.ciphertextHash}`, {
34813
+ method: "PUT",
34814
+ headers: {
34815
+ ...authHeaders(opts.apiKey),
34816
+ "content-type": "application/octet-stream"
34817
+ },
34818
+ body: bytes
34819
+ }, `chunk ${d.index} upload`);
34820
+ if (!res.ok) throw new Error(`chunk ${d.index} upload failed: HTTP ${res.status} ${await res.text()}`);
34821
+ await res.text();
34822
+ }
34823
+ async function finalize(opts, root) {
34824
+ const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/finalize`, {
34825
+ method: "POST",
34826
+ headers: authHeaders(opts.apiKey)
34827
+ }, "finalize");
34828
+ if (!res.ok) throw new Error(`finalize failed: HTTP ${res.status} ${await res.text()}`);
34829
+ await res.text();
34830
+ }
34831
+ async function fetchManifest(opts, root) {
34832
+ const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/manifest`, { headers: authHeaders(opts.apiKey) }, "get manifest");
34833
+ if (!res.ok) throw new Error(`get manifest failed: HTTP ${res.status} ${await res.text()}`);
34834
+ return (await res.json()).data.manifest;
34835
+ }
34836
+ async function getChunkBytes(opts, root, d) {
34837
+ const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/chunks/${d.ciphertextHash}`, { headers: authHeaders(opts.apiKey) }, `get chunk ${d.index}`);
34838
+ if (!res.ok) throw new Error(`get chunk ${d.index} failed: HTTP ${res.status}`);
34839
+ return new Uint8Array(await res.arrayBuffer());
34840
+ }
34841
+ async function readWindow(fh, position, length) {
34842
+ const buf = Buffer.allocUnsafe(length);
34843
+ let read = 0;
34844
+ while (read < length) {
34845
+ const { bytesRead } = await fh.read(buf, read, length - read, position + read);
34846
+ if (bytesRead === 0) break;
34847
+ read += bytesRead;
34848
+ }
34849
+ return read === length ? buf : buf.subarray(0, read);
34850
+ }
34851
+ async function mapLimit(items, limit, fn) {
34852
+ let next = 0;
34853
+ let firstError;
34854
+ const worker = async () => {
34855
+ while (next < items.length && firstError === void 0) {
34856
+ const i = next++;
34857
+ try {
34858
+ await fn(items[i], i);
34859
+ } catch (err) {
34860
+ if (firstError === void 0) firstError = err;
34861
+ return;
34862
+ }
34863
+ }
34864
+ };
34865
+ await Promise.all(Array.from({ length: Math.min(limit, items.length || 1) }, () => worker()));
34866
+ if (firstError !== void 0) throw firstError;
34867
+ }
34868
+ /**
34869
+ * Stream a file up as a Primitive Payload. Two passes over the file (encrypt to
34870
+ * build the manifest, then encrypt again to upload) keep memory bounded to a few
34871
+ * chunks — the whole object is never resident. Returns the content address
34872
+ * (merkleRoot) and the hex CEK needed to download it.
34873
+ */
34874
+ async function pushFile(filePath, opts) {
34875
+ const { open, stat } = await loadNodeFsPromises("pushFile");
34876
+ const chunkSize = opts.chunkSize ?? CHUNK_SIZE;
34877
+ const concurrency = opts.concurrency ?? 3;
34878
+ const { size } = await stat(filePath);
34879
+ const cek = randomBytes(CEK_BYTES);
34880
+ const objectId = randomBytes(OBJECT_ID_BYTES);
34881
+ const chunkCount = size === 0 ? 0 : Math.ceil(size / chunkSize);
34882
+ const fh = await open(filePath, "r");
34883
+ try {
34884
+ const descriptors = [];
34885
+ for (let index = 0; index < chunkCount; index++) {
34886
+ const position = index * chunkSize;
34887
+ const length = Math.min(chunkSize, size - position);
34888
+ const plaintext = await readWindow(fh, position, length);
34889
+ const ciphertext = await encryptChunk(cek, objectId, index, plaintext);
34890
+ descriptors.push({
34891
+ index,
34892
+ ciphertextHash: await contentHashHex(ciphertext),
34893
+ plaintextSize: length,
34894
+ ciphertextSize: ciphertext.length
34895
+ });
34896
+ opts.onProgress?.("encrypt", index + 1, chunkCount);
34897
+ }
34898
+ const root = await merkleRoot(descriptors.map((d) => d.ciphertextHash));
34899
+ await initiate(opts, {
34900
+ version: MANIFEST_VERSION,
34901
+ objectId: toHex(objectId),
34902
+ chunkSize,
34903
+ totalPlaintextSize: size,
34904
+ chunkCount,
34905
+ chunks: descriptors,
34906
+ merkleRoot: root
34907
+ });
34908
+ let uploaded = 0;
34909
+ await mapLimit(descriptors, concurrency, async (d) => {
34910
+ const plaintext = await readWindow(fh, d.index * chunkSize, d.plaintextSize);
34911
+ await putChunk(opts, root, d, await encryptChunk(cek, objectId, d.index, plaintext));
34912
+ opts.onProgress?.("upload", ++uploaded, chunkCount);
34913
+ });
34914
+ await finalize(opts, root);
34915
+ return {
34916
+ merkleRoot: root,
34917
+ cek: toHex(cek),
34918
+ chunkCount,
34919
+ totalBytes: size
34920
+ };
34921
+ } finally {
34922
+ await fh.close();
34923
+ }
34924
+ }
34925
+ /**
34926
+ * Stream a Primitive Payload down to a file, verifying and decrypting one chunk
34927
+ * at a time (bounded memory). Every chunk is content-address-checked before
34928
+ * decryption, so a corrupt or substituted chunk throws.
34929
+ */
34930
+ async function pullFile(root, outPath, opts) {
34931
+ const { createWriteStream } = await loadNodeFs("pullFile");
34932
+ const { rm } = await loadNodeFsPromises("pullFile");
34933
+ const manifest = await fetchManifest(opts, root);
34934
+ const computedRoot = await merkleRoot(manifest.chunks.map((c) => c.ciphertextHash));
34935
+ if (computedRoot !== root) throw new Error(`manifest does not match the requested content address (got ${computedRoot})`);
34936
+ const cek = fromHex(opts.cek);
34937
+ const objectId = fromHex(manifest.objectId);
34938
+ const out = createWriteStream(outPath);
34939
+ let onWriteError = () => {};
34940
+ const writeErrored = new Promise((_, reject) => {
34941
+ onWriteError = reject;
34942
+ });
34943
+ writeErrored.catch(() => {});
34944
+ out.on("error", (err) => onWriteError(err));
34945
+ try {
34946
+ let done = 0;
34947
+ for (const d of manifest.chunks) {
34948
+ const ciphertext = await getChunkBytes(opts, root, d);
34949
+ if (await contentHashHex(ciphertext) !== d.ciphertextHash) throw new Error(`chunk ${d.index} failed integrity check`);
34950
+ const plaintext = await decryptChunk(cek, objectId, d.index, ciphertext);
34951
+ if (!out.write(plaintext)) await Promise.race([new Promise((resolve) => out.once("drain", resolve)), writeErrored]);
34952
+ opts.onProgress?.("download", ++done, manifest.chunkCount);
34953
+ }
34954
+ await Promise.race([new Promise((resolve) => out.end(resolve)), writeErrored]);
34955
+ } catch (err) {
34956
+ out.destroy();
34957
+ await rm(outPath, { force: true });
34958
+ throw err;
34959
+ }
34960
+ return manifest;
34961
+ }
34962
+ //#endregion
34963
+ //#region ../sdk-node/dist/api-D4xf448r.js
34964
+ const jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) };
34965
+ function createSseClient({ onRequest, onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) {
34966
+ let lastEventId;
34967
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
34968
+ const createStream = async function* () {
34969
+ let retryDelay = sseDefaultRetryDelay ?? 3e3;
34970
+ let attempt = 0;
34971
+ const signal = options.signal ?? new AbortController().signal;
34972
+ while (true) {
34973
+ if (signal.aborted) break;
34974
+ attempt++;
34975
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
34976
+ if (lastEventId !== void 0) headers.set("Last-Event-ID", lastEventId);
34977
+ try {
34978
+ const requestInit = {
34979
+ redirect: "follow",
34980
+ ...options,
34981
+ body: options.serializedBody,
34982
+ headers,
34983
+ signal
34984
+ };
34985
+ let request = new Request(url, requestInit);
34986
+ if (onRequest) request = await onRequest(url, requestInit);
34987
+ const response = await (options.fetch ?? globalThis.fetch)(request);
34988
+ if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
34989
+ if (!response.body) throw new Error("No body in SSE response");
34990
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
34991
+ let buffer = "";
34992
+ const abortHandler = () => {
34993
+ try {
34994
+ reader.cancel();
34995
+ } catch {}
34996
+ };
34997
+ signal.addEventListener("abort", abortHandler);
34998
+ try {
34999
+ while (true) {
35000
+ const { done, value } = await reader.read();
35001
+ if (done) break;
35002
+ buffer += value;
35003
+ buffer = buffer.replace(/\r\n?/g, "\n");
35004
+ const chunks = buffer.split("\n\n");
35005
+ buffer = chunks.pop() ?? "";
35006
+ for (const chunk of chunks) {
35007
+ const lines = chunk.split("\n");
35008
+ const dataLines = [];
35009
+ let eventName;
35010
+ for (const line of lines) if (line.startsWith("data:")) dataLines.push(line.replace(/^data:\s*/, ""));
35011
+ else if (line.startsWith("event:")) eventName = line.replace(/^event:\s*/, "");
35012
+ else if (line.startsWith("id:")) lastEventId = line.replace(/^id:\s*/, "");
35013
+ else if (line.startsWith("retry:")) {
35014
+ const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
35015
+ if (!Number.isNaN(parsed)) retryDelay = parsed;
35016
+ }
35017
+ let data;
35018
+ let parsedJson = false;
35019
+ if (dataLines.length) {
35020
+ const rawData = dataLines.join("\n");
35021
+ try {
35022
+ data = JSON.parse(rawData);
35023
+ parsedJson = true;
35024
+ } catch {
35025
+ data = rawData;
35026
+ }
35027
+ }
35028
+ if (parsedJson) {
35029
+ if (responseValidator) await responseValidator(data);
35030
+ if (responseTransformer) data = await responseTransformer(data);
35031
+ }
35032
+ onSseEvent?.({
35033
+ data,
35034
+ event: eventName,
35035
+ id: lastEventId,
35036
+ retry: retryDelay
35037
+ });
35038
+ if (dataLines.length) yield data;
35039
+ }
35040
+ }
35041
+ } finally {
35042
+ signal.removeEventListener("abort", abortHandler);
35043
+ reader.releaseLock();
35044
+ }
35045
+ break;
35046
+ } catch (error) {
35047
+ onSseError?.(error);
35048
+ if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) break;
35049
+ await sleep(Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4));
35050
+ }
35051
+ }
35052
+ };
35053
+ return { stream: createStream() };
35054
+ }
35055
+ const separatorArrayExplode = (style) => {
35056
+ switch (style) {
35057
+ case "label": return ".";
35058
+ case "matrix": return ";";
35059
+ case "simple": return ",";
35060
+ default: return "&";
35061
+ }
35062
+ };
35063
+ const separatorArrayNoExplode = (style) => {
35064
+ switch (style) {
35065
+ case "form": return ",";
35066
+ case "pipeDelimited": return "|";
35067
+ case "spaceDelimited": return "%20";
35068
+ default: return ",";
35069
+ }
35070
+ };
35071
+ const separatorObjectExplode = (style) => {
35072
+ switch (style) {
35073
+ case "label": return ".";
35074
+ case "matrix": return ";";
35075
+ case "simple": return ",";
35076
+ default: return "&";
35077
+ }
35078
+ };
35079
+ const serializeArrayParam = ({ allowReserved, explode, name, style, value }) => {
35080
+ if (!explode) {
35081
+ const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
35082
+ switch (style) {
35083
+ case "label": return `.${joinedValues}`;
35084
+ case "matrix": return `;${name}=${joinedValues}`;
35085
+ case "simple": return joinedValues;
35086
+ default: return `${name}=${joinedValues}`;
35087
+ }
35088
+ }
35089
+ const separator = separatorArrayExplode(style);
35090
+ const joinedValues = value.map((v) => {
35091
+ if (style === "label" || style === "simple") return allowReserved ? v : encodeURIComponent(v);
35092
+ return serializePrimitiveParam({
35093
+ allowReserved,
35094
+ name,
35095
+ value: v
35096
+ });
35097
+ }).join(separator);
35098
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
35099
+ };
35100
+ const serializePrimitiveParam = ({ allowReserved, name, value }) => {
35101
+ if (value === void 0 || value === null) return "";
35102
+ if (typeof value === "object") throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");
35103
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
35104
+ };
35105
+ const serializeObjectParam = ({ allowReserved, explode, name, style, value, valueOnly }) => {
35106
+ if (value instanceof Date) return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
35107
+ if (style !== "deepObject" && !explode) {
35108
+ let values = [];
35109
+ Object.entries(value).forEach(([key, v]) => {
35110
+ values = [
35111
+ ...values,
35112
+ key,
35113
+ allowReserved ? v : encodeURIComponent(v)
35114
+ ];
35115
+ });
35116
+ const joinedValues = values.join(",");
35117
+ switch (style) {
35118
+ case "form": return `${name}=${joinedValues}`;
35119
+ case "label": return `.${joinedValues}`;
35120
+ case "matrix": return `;${name}=${joinedValues}`;
35121
+ default: return joinedValues;
35122
+ }
35123
+ }
35124
+ const separator = separatorObjectExplode(style);
35125
+ const joinedValues = Object.entries(value).map(([key, v]) => serializePrimitiveParam({
35126
+ allowReserved,
35127
+ name: style === "deepObject" ? `${name}[${key}]` : key,
35128
+ value: v
35129
+ })).join(separator);
35130
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
35131
+ };
35132
+ const PATH_PARAM_RE = /\{[^{}]+\}/g;
35133
+ const defaultPathSerializer = ({ path, url: _url }) => {
35134
+ let url = _url;
35135
+ const matches = _url.match(PATH_PARAM_RE);
35136
+ if (matches) for (const match of matches) {
35137
+ let explode = false;
35138
+ let name = match.substring(1, match.length - 1);
35139
+ let style = "simple";
35140
+ if (name.endsWith("*")) {
35141
+ explode = true;
35142
+ name = name.substring(0, name.length - 1);
35143
+ }
35144
+ if (name.startsWith(".")) {
35145
+ name = name.substring(1);
35146
+ style = "label";
35147
+ } else if (name.startsWith(";")) {
35148
+ name = name.substring(1);
35149
+ style = "matrix";
35150
+ }
35151
+ const value = path[name];
35152
+ if (value === void 0 || value === null) continue;
35153
+ if (Array.isArray(value)) {
35154
+ url = url.replace(match, serializeArrayParam({
35155
+ explode,
35156
+ name,
35157
+ style,
35158
+ value
35159
+ }));
35160
+ continue;
35161
+ }
35162
+ if (typeof value === "object") {
35163
+ url = url.replace(match, serializeObjectParam({
35164
+ explode,
35165
+ name,
35166
+ style,
35167
+ value,
35168
+ valueOnly: true
35169
+ }));
35170
+ continue;
35171
+ }
35172
+ if (style === "matrix") {
35173
+ url = url.replace(match, `;${serializePrimitiveParam({
35174
+ name,
35175
+ value
35176
+ })}`);
35177
+ continue;
35178
+ }
35179
+ const replaceValue = encodeURIComponent(style === "label" ? `.${value}` : value);
35180
+ url = url.replace(match, replaceValue);
35181
+ }
35182
+ return url;
35183
+ };
35184
+ const getUrl = ({ baseUrl, path, query, querySerializer, url: _url }) => {
35185
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
35186
+ let url = (baseUrl ?? "") + pathUrl;
35187
+ if (path) url = defaultPathSerializer({
35188
+ path,
35189
+ url
35190
+ });
35191
+ let search = query ? querySerializer(query) : "";
35192
+ if (search.startsWith("?")) search = search.substring(1);
35193
+ if (search) url += `?${search}`;
35194
+ return url;
35195
+ };
35196
+ function getValidRequestBody(options) {
35197
+ const hasBody = options.body !== void 0;
35198
+ if (hasBody && options.bodySerializer) {
35199
+ if ("serializedBody" in options) return options.serializedBody !== void 0 && options.serializedBody !== "" ? options.serializedBody : null;
35200
+ return options.body !== "" ? options.body : null;
35201
+ }
35202
+ if (hasBody) return options.body;
35203
+ }
35204
+ const getAuthToken = async (auth, callback) => {
35205
+ const token = typeof callback === "function" ? await callback(auth) : callback;
35206
+ if (!token) return;
35207
+ if (auth.scheme === "bearer") return `Bearer ${token}`;
35208
+ if (auth.scheme === "basic") return `Basic ${btoa(token)}`;
35209
+ return token;
35210
+ };
35211
+ const createQuerySerializer = ({ parameters = {}, ...args } = {}) => {
35212
+ const querySerializer = (queryParams) => {
35213
+ const search = [];
35214
+ if (queryParams && typeof queryParams === "object") for (const name in queryParams) {
35215
+ const value = queryParams[name];
35216
+ if (value === void 0 || value === null) continue;
35217
+ const options = parameters[name] || args;
35218
+ if (Array.isArray(value)) {
35219
+ const serializedArray = serializeArrayParam({
35220
+ allowReserved: options.allowReserved,
35221
+ explode: true,
35222
+ name,
35223
+ style: "form",
35224
+ value,
35225
+ ...options.array
35226
+ });
35227
+ if (serializedArray) search.push(serializedArray);
35228
+ } else if (typeof value === "object") {
35229
+ const serializedObject = serializeObjectParam({
35230
+ allowReserved: options.allowReserved,
35231
+ explode: true,
35232
+ name,
35233
+ style: "deepObject",
35234
+ value,
35235
+ ...options.object
35236
+ });
35237
+ if (serializedObject) search.push(serializedObject);
35238
+ } else {
35239
+ const serializedPrimitive = serializePrimitiveParam({
35240
+ allowReserved: options.allowReserved,
35241
+ name,
35242
+ value
35243
+ });
35244
+ if (serializedPrimitive) search.push(serializedPrimitive);
35245
+ }
35246
+ }
35247
+ return search.join("&");
35248
+ };
35249
+ return querySerializer;
35250
+ };
35251
+ /**
35252
+ * Infers parseAs value from provided Content-Type header.
35253
+ */
35254
+ const getParseAs = (contentType) => {
35255
+ if (!contentType) return "stream";
35256
+ const cleanContent = contentType.split(";")[0]?.trim();
35257
+ if (!cleanContent) return;
35258
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) return "json";
35259
+ if (cleanContent === "multipart/form-data") return "formData";
35260
+ if ([
35261
+ "application/",
35262
+ "audio/",
35263
+ "image/",
35264
+ "video/"
35265
+ ].some((type) => cleanContent.startsWith(type))) return "blob";
35266
+ if (cleanContent.startsWith("text/")) return "text";
35267
+ };
35268
+ const checkForExistence = (options, name) => {
35269
+ if (!name) return false;
35270
+ if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) return true;
35271
+ return false;
35272
+ };
35273
+ async function setAuthParams(options) {
35274
+ for (const auth of options.security ?? []) {
35275
+ if (checkForExistence(options, auth.name)) continue;
35276
+ const token = await getAuthToken(auth, options.auth);
35277
+ if (!token) continue;
35278
+ const name = auth.name ?? "Authorization";
35279
+ switch (auth.in) {
35280
+ case "query":
35281
+ if (!options.query) options.query = {};
35282
+ options.query[name] = token;
35283
+ break;
35284
+ case "cookie":
35285
+ options.headers.append("Cookie", `${name}=${token}`);
35286
+ break;
35287
+ default:
35288
+ options.headers.set(name, token);
35289
+ break;
35290
+ }
35291
+ }
35292
+ }
35293
+ const buildUrl = (options) => getUrl({
35294
+ baseUrl: options.baseUrl,
35295
+ path: options.path,
35296
+ query: options.query,
35297
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
35298
+ url: options.url
35299
+ });
35300
+ const mergeConfigs = (a, b) => {
35301
+ const config = {
35302
+ ...a,
35303
+ ...b
35304
+ };
35305
+ if (config.baseUrl?.endsWith("/")) config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
35306
+ config.headers = mergeHeaders(a.headers, b.headers);
35307
+ return config;
35308
+ };
35309
+ const headersEntries = (headers) => {
35310
+ const entries = [];
35311
+ headers.forEach((value, key) => {
35312
+ entries.push([key, value]);
35313
+ });
35314
+ return entries;
35315
+ };
35316
+ const mergeHeaders = (...headers) => {
35317
+ const mergedHeaders = new Headers();
35318
+ for (const header of headers) {
35319
+ if (!header) continue;
35320
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
35321
+ for (const [key, value] of iterator) if (value === null) mergedHeaders.delete(key);
35322
+ else if (Array.isArray(value)) for (const v of value) mergedHeaders.append(key, v);
35323
+ else if (value !== void 0) mergedHeaders.set(key, typeof value === "object" ? JSON.stringify(value) : value);
35324
+ }
35325
+ return mergedHeaders;
35326
+ };
35327
+ var Interceptors = class {
35328
+ fns = [];
35329
+ clear() {
35330
+ this.fns = [];
35331
+ }
35332
+ eject(id) {
35333
+ const index = this.getInterceptorIndex(id);
35334
+ if (this.fns[index]) this.fns[index] = null;
35335
+ }
35336
+ exists(id) {
35337
+ const index = this.getInterceptorIndex(id);
35338
+ return Boolean(this.fns[index]);
35339
+ }
35340
+ getInterceptorIndex(id) {
35341
+ if (typeof id === "number") return this.fns[id] ? id : -1;
35342
+ return this.fns.indexOf(id);
35343
+ }
35344
+ update(id, fn) {
35345
+ const index = this.getInterceptorIndex(id);
35346
+ if (this.fns[index]) {
35347
+ this.fns[index] = fn;
35348
+ return id;
35349
+ }
35350
+ return false;
35351
+ }
35352
+ use(fn) {
35353
+ this.fns.push(fn);
35354
+ return this.fns.length - 1;
35355
+ }
35356
+ };
35357
+ const createInterceptors = () => ({
35358
+ error: new Interceptors(),
35359
+ request: new Interceptors(),
35360
+ response: new Interceptors()
35361
+ });
35362
+ const defaultQuerySerializer = createQuerySerializer({
35363
+ allowReserved: false,
35364
+ array: {
35365
+ explode: true,
35366
+ style: "form"
35367
+ },
35368
+ object: {
35369
+ explode: true,
35370
+ style: "deepObject"
35371
+ }
35372
+ });
35373
+ const defaultHeaders = { "Content-Type": "application/json" };
35374
+ const createConfig = (override = {}) => ({
35375
+ ...jsonBodySerializer,
35376
+ headers: defaultHeaders,
35377
+ parseAs: "auto",
35378
+ querySerializer: defaultQuerySerializer,
35379
+ ...override
35380
+ });
35381
+ const createClient = (config = {}) => {
35382
+ let _config = mergeConfigs(createConfig(), config);
35383
+ const getConfig = () => ({ ..._config });
35384
+ const setConfig = (config) => {
35385
+ _config = mergeConfigs(_config, config);
35386
+ return getConfig();
35387
+ };
35388
+ const interceptors = createInterceptors();
35389
+ const beforeRequest = async (options) => {
35390
+ const opts = {
35391
+ ..._config,
35392
+ ...options,
35393
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
35394
+ headers: mergeHeaders(_config.headers, options.headers),
35395
+ serializedBody: void 0
35396
+ };
35397
+ if (opts.security) await setAuthParams(opts);
35398
+ if (opts.requestValidator) await opts.requestValidator(opts);
35399
+ if (opts.body !== void 0 && opts.bodySerializer) opts.serializedBody = opts.bodySerializer(opts.body);
35400
+ if (opts.body === void 0 || opts.serializedBody === "") opts.headers.delete("Content-Type");
35401
+ const resolvedOpts = opts;
35402
+ return {
35403
+ opts: resolvedOpts,
35404
+ url: buildUrl(resolvedOpts)
35405
+ };
35406
+ };
35407
+ const request = async (options) => {
35408
+ const throwOnError = options.throwOnError ?? _config.throwOnError;
35409
+ const responseStyle = options.responseStyle ?? _config.responseStyle;
35410
+ let request;
35411
+ let response;
35412
+ try {
35413
+ const { opts, url } = await beforeRequest(options);
35414
+ const requestInit = {
35415
+ redirect: "follow",
35416
+ ...opts,
35417
+ body: getValidRequestBody(opts)
35418
+ };
35419
+ request = new Request(url, requestInit);
35420
+ for (const fn of interceptors.request.fns) if (fn) request = await fn(request, opts);
35421
+ const _fetch = opts.fetch;
35422
+ response = await _fetch(request);
35423
+ for (const fn of interceptors.response.fns) if (fn) response = await fn(response, request, opts);
35424
+ const result = {
35425
+ request,
35426
+ response
35427
+ };
35428
+ if (response.ok) {
35429
+ const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
35430
+ if (response.status === 204 || response.headers.get("Content-Length") === "0") {
35431
+ let emptyData;
35432
+ switch (parseAs) {
35433
+ case "arrayBuffer":
35434
+ case "blob":
35435
+ case "text":
35436
+ emptyData = await response[parseAs]();
35437
+ break;
35438
+ case "formData":
35439
+ emptyData = new FormData();
35440
+ break;
35441
+ case "stream":
35442
+ emptyData = response.body;
35443
+ break;
35444
+ default:
35445
+ emptyData = {};
35446
+ break;
35447
+ }
35448
+ return opts.responseStyle === "data" ? emptyData : {
35449
+ data: emptyData,
35450
+ ...result
35451
+ };
35452
+ }
35453
+ let data;
35454
+ switch (parseAs) {
35455
+ case "arrayBuffer":
35456
+ case "blob":
35457
+ case "formData":
35458
+ case "text":
35459
+ data = await response[parseAs]();
35460
+ break;
35461
+ case "json": {
35462
+ const text = await response.text();
35463
+ data = text ? JSON.parse(text) : {};
35464
+ break;
35465
+ }
35466
+ case "stream": return opts.responseStyle === "data" ? response.body : {
35467
+ data: response.body,
35468
+ ...result
35469
+ };
35470
+ }
35471
+ if (parseAs === "json") {
35472
+ if (opts.responseValidator) await opts.responseValidator(data);
35473
+ if (opts.responseTransformer) data = await opts.responseTransformer(data);
35474
+ }
35475
+ return opts.responseStyle === "data" ? data : {
35476
+ data,
35477
+ ...result
35478
+ };
35479
+ }
35480
+ const textError = await response.text();
35481
+ let jsonError;
35482
+ try {
35483
+ jsonError = JSON.parse(textError);
35484
+ } catch {}
35485
+ throw jsonError ?? textError;
35486
+ } catch (error) {
35487
+ let finalError = error;
35488
+ for (const fn of interceptors.error.fns) if (fn) finalError = await fn(finalError, response, request, options);
35489
+ finalError = finalError || {};
35490
+ if (throwOnError) throw finalError;
35491
+ return responseStyle === "data" ? void 0 : {
35492
+ error: finalError,
35493
+ request,
35494
+ response
35495
+ };
35496
+ }
35497
+ };
35498
+ const makeMethodFn = (method) => (options) => request({
35499
+ ...options,
35500
+ method
35501
+ });
35502
+ const makeSseFn = (method) => async (options) => {
35503
+ const { opts, url } = await beforeRequest(options);
35504
+ return createSseClient({
35505
+ ...opts,
35506
+ body: opts.body,
35507
+ method,
35508
+ onRequest: async (url, init) => {
35509
+ let request = new Request(url, init);
35510
+ for (const fn of interceptors.request.fns) if (fn) request = await fn(request, opts);
35511
+ return request;
35512
+ },
35513
+ serializedBody: getValidRequestBody(opts),
35514
+ url
35515
+ });
35516
+ };
35517
+ const _buildUrl = (options) => buildUrl({
35518
+ ..._config,
35519
+ ...options
35520
+ });
35521
+ return {
35522
+ buildUrl: _buildUrl,
35523
+ connect: makeMethodFn("CONNECT"),
35524
+ delete: makeMethodFn("DELETE"),
35525
+ get: makeMethodFn("GET"),
35526
+ getConfig,
35527
+ head: makeMethodFn("HEAD"),
35528
+ interceptors,
35529
+ options: makeMethodFn("OPTIONS"),
35530
+ patch: makeMethodFn("PATCH"),
35531
+ post: makeMethodFn("POST"),
35532
+ put: makeMethodFn("PUT"),
35533
+ request,
35534
+ setConfig,
35535
+ sse: {
35536
+ connect: makeSseFn("CONNECT"),
35537
+ delete: makeSseFn("DELETE"),
35538
+ get: makeSseFn("GET"),
35539
+ head: makeSseFn("HEAD"),
35540
+ options: makeSseFn("OPTIONS"),
35541
+ patch: makeSseFn("PATCH"),
35542
+ post: makeSseFn("POST"),
35543
+ put: makeSseFn("PUT"),
35544
+ trace: makeSseFn("TRACE")
35545
+ },
35546
+ trace: makeMethodFn("TRACE")
35547
+ };
35548
+ };
35549
+ const client$1 = createClient(createConfig({ baseUrl: "https://api.primitive.dev/v1" }));
35550
+ /**
35551
+ * Get account info
35552
+ */
35553
+ const getAccount = (options) => (options?.client ?? client$1).get({
35554
+ security: [{
35555
+ scheme: "bearer",
35556
+ type: "http"
35557
+ }],
35558
+ url: "/account",
35559
+ ...options
35560
+ });
35561
+ /**
35562
+ * Receive a pending webhook event without a public endpoint
35563
+ *
35564
+ * Wait up to 30 seconds for one existing event. The body string preserves the exact webhook serialization; headers and canonical occurrence/type metadata travel alongside it. A fixed lease protects the attempt while a short handler accepts input. Retry may redeliver an occurrence: deduplicate event_id. Empty delivery means no offer now, not proof all input was delivered; inspect backlog and persistent gap_count/last_gap_reason. Queue retention is 24 hours and does not extend source-content retention. Disconnecting preserves pending work. Same named destination shares consumption; different destinations get independent copies.
35565
+ */
35566
+ const pullWebhookEvent = (options) => (options.client ?? client$1).post({
35567
+ security: [{
35568
+ scheme: "bearer",
35569
+ type: "http"
35570
+ }],
35571
+ url: "/endpoints/{id}/pull",
35572
+ ...options,
35573
+ headers: {
35574
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
35575
+ ...options.headers
35576
+ }
35577
+ });
35578
+ /**
35579
+ * Report the outcome of a local webhook delivery attempt
35580
+ *
35581
+ * Report normalized handling evidence for the queue, attempt and lease. Required JSON fields: queue_id, delivery_id, lease_token, mode, duration_ms. For mode=exec include exit_code; stdout includes write_succeeded; http includes status_code and optional transport_error, error_code, confirmed. Supply this discriminated request with --raw-body in the generated CLI. The server classifies success and retry. Duplicate identical completion is idempotent; a stale lease cannot finish a newer attempt. Exec success means durable input acceptance, not completion of agent reasoning. Only successful HTTP handling with recognized confirmation evidence can confirm content discard. Never send arbitrary handler output or HTTP response bodies.
35582
+ */
35583
+ const completeWebhookEvent = (options) => (options.client ?? client$1).post({
35584
+ security: [{
35585
+ scheme: "bearer",
35586
+ type: "http"
35587
+ }],
35588
+ url: "/endpoints/{id}/complete",
35589
+ ...options,
35590
+ headers: {
35591
+ ...options.body !== void 0 && { "Content-Type": "application/json" },
35592
+ ...options.headers
35593
+ }
35594
+ });
35595
+ var EventReceiverError = class extends Error {
35596
+ constructor(message, code, status = 0, retryAfterMs = 0) {
35597
+ super(message);
35598
+ this.code = code;
35599
+ this.status = status;
35600
+ this.retryAfterMs = retryAfterMs;
35601
+ this.name = "EventReceiverError";
35602
+ }
35603
+ };
35604
+ function record(value) {
35605
+ return value !== null && typeof value === "object" && !Array.isArray(value);
35606
+ }
35607
+ function validateOffer(value) {
35608
+ if (!record(value) || !Number.isSafeInteger(value.backlog) || Number(value.backlog) < 0 || !Number.isSafeInteger(value.gap_count) || Number(value.gap_count) < 0 || value.last_gap_reason !== null && typeof value.last_gap_reason !== "string" || value.retention_seconds !== 86400 || value.handler_timeout_seconds !== 30) throw new EventReceiverError("Invalid event status response", "invalid_response");
35609
+ const d = value.delivery;
35610
+ if (d !== null && (!record(d) || ![
35611
+ "queue_id",
35612
+ "event_id",
35613
+ "delivery_id",
35614
+ "lease_token",
35615
+ "event_type",
35616
+ "body",
35617
+ "lease_expires_at"
35618
+ ].every((key) => typeof d[key] === "string") || !Number.isFinite(Date.parse(String(d.lease_expires_at))) || !record(d.headers) || !Object.values(d.headers).every((header) => typeof header === "string"))) throw new EventReceiverError("Invalid event delivery response", "invalid_response");
35619
+ return value;
35620
+ }
35621
+ function retryAfter(value) {
35622
+ if (!value) return 0;
35623
+ const ms = /^\d+(\.\d+)?$/.test(value) ? Number(value) * 1e3 : Date.parse(value) - Date.now();
35624
+ return Number.isFinite(ms) ? Math.max(0, ms) : 0;
35625
+ }
35626
+ function unwrap(result) {
35627
+ if (result.data !== void 0 && !result.error) return result.data;
35628
+ const code = record(result.error) && record(result.error.error) && typeof result.error.error.code === "string" ? result.error.error.code : "request_failed";
35629
+ throw new EventReceiverError(`Event request failed (${result.response?.status ?? 0}, ${code})`, code, result.response?.status ?? 0, retryAfter(result.response?.headers.get("retry-after") ?? null));
35630
+ }
35631
+ /** Shared by the SDK and CLI. Completion retries preserve the original evidence. */
35632
+ var EventConnection = class {
35633
+ socket;
35634
+ pending;
35635
+ accountId;
35636
+ touch;
35637
+ constructor(client, endpointId, options = {}) {
35638
+ this.client = client;
35639
+ this.endpointId = endpointId;
35640
+ this.options = options;
35641
+ }
35642
+ close() {
35643
+ const socket = this.socket;
35644
+ this.socket = void 0;
35645
+ this.pending?.reject(new EventReceiverError("Event connection closed", "disconnected"));
35646
+ this.pending = void 0;
35647
+ socket?.close();
35648
+ }
35649
+ async open(signal) {
35650
+ if (this.options.transport === "poll" || this.socket?.readyState === 1) return;
35651
+ const account = unwrap(await getAccount({
35652
+ client: this.client,
35653
+ responseStyle: "fields",
35654
+ throwOnError: false,
35655
+ signal: AbortSignal.any([signal, AbortSignal.timeout(15e3)])
35656
+ }));
35657
+ if (!account.data?.id || this.accountId && account.data.id !== this.accountId) throw new EventReceiverError("The authenticated account changed; restart the receiver", "identity_changed", 403);
35658
+ this.accountId = account.data.id;
35659
+ const config = this.client.getConfig();
35660
+ const auth = typeof config.auth === "function" ? await config.auth({
35661
+ type: "http",
35662
+ scheme: "bearer"
35663
+ }) : config.auth;
35664
+ const authorization = new Headers(config.headers).get("authorization");
35665
+ const token = typeof auth === "string" ? auth.replace(/^Bearer /i, "") : authorization?.replace(/^Bearer /i, "");
35666
+ if (!token) throw new EventReceiverError("Event receiver requires bearer credentials", "unauthorized", 401);
35667
+ const url = new URL(`${config.baseUrl?.replace(/\/+$/, "")}/endpoints/${encodeURIComponent(this.endpointId)}/stream`);
35668
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && [
35669
+ "localhost",
35670
+ "127.0.0.1",
35671
+ "[::1]"
35672
+ ].includes(url.hostname))) throw new EventReceiverError("Event connections require HTTPS (except loopback development)", "unsupported");
35673
+ url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
35674
+ const socket = (this.options.webSocketFactory ?? ((address, protocol) => new WebSocket(address, protocol)))(url.href, "primitive.events.v1");
35675
+ this.socket = socket;
35676
+ socket.addEventListener("message", (message) => {
35677
+ if (this.socket !== socket) return;
35678
+ let frame;
35679
+ try {
35680
+ frame = JSON.parse(String(message.data));
35681
+ } catch {
35682
+ this.fail(new EventReceiverError("Invalid event frame", "invalid_response"));
35683
+ return;
35684
+ }
35685
+ this.touch?.();
35686
+ if (!record(frame)) {
35687
+ this.fail(new EventReceiverError("Invalid event frame", "invalid_response"));
35688
+ return;
35689
+ }
35690
+ if (frame.type === "ping") {
35691
+ socket.send(JSON.stringify({ type: "pong" }));
35692
+ return;
35693
+ }
35694
+ if (frame.type === "status") {
35695
+ try {
35696
+ this.options.onStatus?.(validateOffer(frame.data));
35697
+ } catch (error) {
35698
+ this.fail(error);
35699
+ }
35700
+ return;
35701
+ }
35702
+ if (frame.type === "error") {
35703
+ this.fail(new EventReceiverError("Event stream request failed", typeof frame.code === "string" ? frame.code : "request_failed", typeof frame.status === "number" ? frame.status : 0, retryAfter(typeof frame.retry_after === "string" ? frame.retry_after : null)));
35704
+ return;
35705
+ }
35706
+ this.pending?.resolve(frame);
35707
+ });
35708
+ socket.addEventListener("close", () => {
35709
+ if (this.socket === socket) this.fail(new EventReceiverError("Event stream disconnected", "disconnected"));
35710
+ });
35711
+ socket.addEventListener("error", () => {
35712
+ if (this.socket === socket) this.fail(new EventReceiverError("Event stream connection failed", "disconnected"));
35713
+ });
35714
+ const ready = await this.exchange(signal, void 0, () => {
35715
+ socket.addEventListener("open", () => socket.send(JSON.stringify({
35716
+ type: "authenticate",
35717
+ token
35718
+ })), { once: true });
35719
+ }, 15e3);
35720
+ if (!record(ready) || ready.type !== "ready" || ready.protocol !== "primitive.events.v1") {
35721
+ this.close();
35722
+ throw new EventReceiverError("Unsupported event stream protocol", "unsupported");
35723
+ }
35724
+ }
35725
+ fail(error) {
35726
+ this.pending?.reject(error);
35727
+ this.pending = void 0;
35728
+ this.close();
35729
+ }
35730
+ async exchange(signal, frame, start, timeoutMs = 6e4) {
35731
+ signal.throwIfAborted();
35732
+ if (this.pending) throw new EventReceiverError("Concurrent event operation", "busy", 409);
35733
+ let cleanup = () => {};
35734
+ try {
35735
+ return await new Promise((resolve, reject) => {
35736
+ const abort = () => {
35737
+ reject(signal.reason);
35738
+ this.close();
35739
+ };
35740
+ let timer;
35741
+ this.touch = () => {
35742
+ clearTimeout(timer);
35743
+ timer = setTimeout(() => this.fail(new EventReceiverError("Event stream timed out", "disconnected")), timeoutMs);
35744
+ };
35745
+ this.touch();
35746
+ cleanup = () => {
35747
+ this.touch = void 0;
35748
+ clearTimeout(timer);
35749
+ signal.removeEventListener("abort", abort);
35750
+ this.pending = void 0;
35751
+ };
35752
+ this.pending = {
35753
+ resolve,
35754
+ reject
35755
+ };
35756
+ signal.addEventListener("abort", abort, { once: true });
35757
+ if (frame) this.socket?.send(JSON.stringify(frame));
35758
+ start?.();
35759
+ });
35760
+ } finally {
35761
+ cleanup();
35762
+ }
35763
+ }
35764
+ async receive(signal) {
35765
+ if (this.options.transport === "poll") return validateOffer(unwrap(await pullWebhookEvent({
35766
+ client: this.client,
35767
+ path: { id: this.endpointId },
35768
+ body: { wait_seconds: 25 },
35769
+ responseStyle: "fields",
35770
+ throwOnError: false,
35771
+ signal: AbortSignal.any([signal, AbortSignal.timeout(45e3)])
35772
+ })).data);
35773
+ await this.open(signal);
35774
+ const frame = await this.exchange(signal, { type: "receive" }, void 0, 9e4);
35775
+ if (!record(frame) || frame.type !== "event") throw new EventReceiverError("Invalid event frame", "invalid_response");
35776
+ return validateOffer(frame.data);
35777
+ }
35778
+ async complete(body, signal) {
35779
+ let receipt;
35780
+ if (this.options.transport === "poll") receipt = unwrap(await completeWebhookEvent({
35781
+ client: this.client,
35782
+ path: { id: this.endpointId },
35783
+ body,
35784
+ responseStyle: "fields",
35785
+ throwOnError: false,
35786
+ signal: AbortSignal.any([signal, AbortSignal.timeout(15e3)])
35787
+ })).data;
35788
+ else {
35789
+ await this.open(signal);
35790
+ const frame = await this.exchange(signal, {
35791
+ type: "complete",
35792
+ body
35793
+ }, void 0, 15e3);
35794
+ if (!record(frame) || frame.type !== "receipt") throw new EventReceiverError("Invalid event receipt", "invalid_response");
35795
+ receipt = frame.data;
35796
+ }
35797
+ if (!record(receipt) || !["completed", "already_completed"].includes(String(receipt.result))) throw new EventReceiverError("Invalid event receipt", "invalid_response");
35798
+ }
35799
+ };
35800
+ //#endregion
34444
35801
  //#region src/oclif/listen-state.ts
34445
35802
  var ListenStateError = class extends Error {};
34446
35803
  function normalizeListenOrigin(value) {
@@ -34726,6 +36083,7 @@ async function runListen(options) {
34726
36083
  let verified = false;
34727
36084
  let confirmed = 0;
34728
36085
  let release;
36086
+ let stream;
34729
36087
  async function retry(operation) {
34730
36088
  for (let attempt = 0;; attempt++) {
34731
36089
  signal.throwIfAborted();
@@ -34736,9 +36094,14 @@ async function runListen(options) {
34736
36094
  error = failure(result, now());
34737
36095
  } catch (caught) {
34738
36096
  if (signal.aborted) throw signal.reason;
34739
- if (caught instanceof ListenError) throw caught;
34740
- if (caught instanceof SyntaxError) throw new ListenError("The API returned malformed JSON.");
34741
- error = new RequestFailure(0, "transport", 0);
36097
+ if (caught instanceof EventReceiverError) {
36098
+ if (["invalid_response", "unsupported"].includes(caught.code)) throw new ListenError(caught.message);
36099
+ error = new RequestFailure(caught.status, caught.code, caught.retryAfterMs);
36100
+ } else {
36101
+ if (caught instanceof ListenError) throw caught;
36102
+ if (caught instanceof SyntaxError) throw new ListenError("The API returned malformed JSON.");
36103
+ error = new RequestFailure(0, "transport", 0);
36104
+ }
34742
36105
  }
34743
36106
  if (![
34744
36107
  0,
@@ -34762,7 +36125,7 @@ async function runListen(options) {
34762
36125
  signal: AbortSignal.any([
34763
36126
  signal,
34764
36127
  ...init?.signal ? [init.signal] : [],
34765
- AbortSignal.timeout(3e4)
36128
+ AbortSignal.timeout(45e3)
34766
36129
  ])
34767
36130
  })
34768
36131
  });
@@ -34773,7 +36136,7 @@ async function runListen(options) {
34773
36136
  if (origin !== void 0 && origin !== base) throw new ListenError("The API environment changed while listening. Restart the listener.");
34774
36137
  origin = base;
34775
36138
  if (!verified || auth.auth.apiKey !== verifiedKey) {
34776
- const account = await retry(() => getAccount({
36139
+ const account = await retry(() => getAccount$1({
34777
36140
  client: auth.apiClient.client,
34778
36141
  responseStyle: "fields",
34779
36142
  signal: AbortSignal.any([signal, AbortSignal.timeout(3e4)])
@@ -34809,6 +36172,12 @@ async function runListen(options) {
34809
36172
  });
34810
36173
  const endpoint = registered.data;
34811
36174
  if (registered.success !== true || !endpoint || typeof endpoint.id !== "string" || endpoint.kind !== "pull" || endpoint.enabled === false) throw new ListenError("The API did not return an enabled pull subscription.");
36175
+ const streamClient = await freshClient();
36176
+ if (options.transport !== "poll") {
36177
+ if (!endpoint.receiver_capabilities?.stream_protocols.includes("primitive.events.v1")) throw new ListenError("This API does not support WebSocket events. Use --transport poll explicitly for an older API.");
36178
+ if (!endpoint.receiver_capabilities.completion_modes.includes(options.mode ?? "stdout")) throw new ListenError("This API does not support the selected listener handler mode.");
36179
+ stream = new EventConnection(streamClient, endpoint.id);
36180
+ }
34812
36181
  const selected = endpoint.rules?.event_types;
34813
36182
  const selection = Array.isArray(selected) && selected.every((value) => typeof value === "string" && /^[a-zA-Z0-9_.-]+$/.test(value)) ? selected.join(", ") : "all events";
34814
36183
  stderr.write(`Listening on subscription ${subscription.name} (${endpoint.id}); ${resumed ? "resumed" : "created"}, mode ${options.mode ?? "stdout"}, selection: ${selection}. Retention: 24 hours; handler limit: 30 seconds. Pending count follows the first poll. Ctrl-C disconnects; delete with primitive endpoints delete --id ${endpoint.id}.\n`);
@@ -34816,13 +36185,22 @@ async function runListen(options) {
34816
36185
  let lastBacklog = -1;
34817
36186
  while (!signal.aborted && (options.number === void 0 || confirmed < options.number)) {
34818
36187
  const pollStarted = now();
34819
- const result = await retry(async () => pullWebhookEvent({
34820
- client: await freshClient(),
34821
- path: { id: endpoint.id },
34822
- body: { wait_seconds: 25 },
34823
- responseStyle: "fields",
34824
- signal: AbortSignal.any([signal, AbortSignal.timeout(4e4)])
34825
- }));
36188
+ const result = await retry(async () => {
36189
+ if (stream) {
36190
+ streamClient.setConfig((await freshClient()).getConfig());
36191
+ return { data: {
36192
+ success: true,
36193
+ data: await stream.receive(signal)
36194
+ } };
36195
+ }
36196
+ return pullWebhookEvent$1({
36197
+ client: await freshClient(),
36198
+ path: { id: endpoint.id },
36199
+ body: { wait_seconds: 25 },
36200
+ responseStyle: "fields",
36201
+ signal: AbortSignal.any([signal, AbortSignal.timeout(45e3)])
36202
+ });
36203
+ });
34826
36204
  if (result.success !== true || !result.data || !Number.isSafeInteger(result.data.gap_count) || result.data.gap_count < 0 || result.data.last_gap_reason !== null && typeof result.data.last_gap_reason !== "string" || !Number.isSafeInteger(result.data.backlog) || result.data.backlog < 0 || result.data.handler_timeout_seconds !== 30 || result.data.retention_seconds !== 86400) throw new ListenError("The API returned an invalid pull response.");
34827
36205
  const data = result.data;
34828
36206
  const gapReasons = {
@@ -34865,13 +36243,23 @@ async function runListen(options) {
34865
36243
  lease_token: delivery.lease_token
34866
36244
  };
34867
36245
  try {
34868
- const receipt = await retry(async () => completeWebhookEvent({
34869
- client: await freshClient(),
34870
- path: { id: endpoint.id },
34871
- body: completion,
34872
- responseStyle: "fields",
34873
- signal: AbortSignal.any([signal, AbortSignal.timeout(3e4)])
34874
- }));
36246
+ const receipt = await retry(async () => {
36247
+ if (stream) {
36248
+ streamClient.setConfig((await freshClient()).getConfig());
36249
+ await stream.complete(completion, signal);
36250
+ return { data: {
36251
+ success: true,
36252
+ data: { result: "completed" }
36253
+ } };
36254
+ }
36255
+ return completeWebhookEvent$1({
36256
+ client: await freshClient(),
36257
+ path: { id: endpoint.id },
36258
+ body: completion,
36259
+ responseStyle: "fields",
36260
+ signal: AbortSignal.any([signal, AbortSignal.timeout(3e4)])
36261
+ });
36262
+ });
34875
36263
  if (receipt.success !== true || !["completed", "already_completed"].includes(receipt.data?.result)) throw new ListenError("The API returned an invalid completion receipt.");
34876
36264
  if (handled.succeeded) confirmed++;
34877
36265
  else stderr.write("Handler reported failure; the server will apply its retry policy.\n");
@@ -34883,6 +36271,7 @@ async function runListen(options) {
34883
36271
  } catch (error) {
34884
36272
  if (!signal.aborted) throw error;
34885
36273
  } finally {
36274
+ stream?.close();
34886
36275
  release?.();
34887
36276
  }
34888
36277
  return confirmed;
@@ -34895,9 +36284,24 @@ var ListenCommand = class ListenCommand extends Command {
34895
36284
  static examples = [
34896
36285
  "<%= config.bin %> listen",
34897
36286
  "<%= config.bin %> listen --subscription my-agent --exec \"python3 accept.py\"",
34898
- "<%= config.bin %> listen --forward-to http://127.0.0.1:3000/webhook"
36287
+ "<%= config.bin %> listen --forward-to localhost:3000",
36288
+ "<%= config.bin %> listen --once --timeout 60"
34899
36289
  ];
34900
36290
  static flags = {
36291
+ transport: Flags.string({
36292
+ description: "Event transport; WebSocket is the default.",
36293
+ options: ["websocket", "poll"],
36294
+ default: "websocket"
36295
+ }),
36296
+ once: Flags.boolean({
36297
+ description: "Exit after one successfully handled and confirmed event.",
36298
+ exclusive: ["number"]
36299
+ }),
36300
+ timeout: Flags.integer({
36301
+ description: "Stop after this many seconds; exit 2 if the requested count was not reached.",
36302
+ min: 1,
36303
+ max: 2147483
36304
+ }),
34901
36305
  subscription: Flags.string({ description: "Stable subscription name. Defaults to a saved name for this account and API environment." }),
34902
36306
  exec: Flags.string({
34903
36307
  description: "Short POSIX shell command accepting each complete event on stdin.",
@@ -34931,17 +36335,23 @@ var ListenCommand = class ListenCommand extends Command {
34931
36335
  forwardTo: flags["forward-to"]
34932
36336
  });
34933
36337
  const controller = new AbortController();
36338
+ let timedOut = false;
36339
+ const timer = flags.timeout === void 0 ? void 0 : setTimeout(() => {
36340
+ timedOut = true;
36341
+ controller.abort();
36342
+ }, flags.timeout * 1e3);
34934
36343
  const cancel = () => controller.abort();
34935
36344
  process.on("SIGINT", cancel);
34936
36345
  process.on("SIGTERM", cancel);
34937
36346
  try {
34938
36347
  await runListen({
36348
+ transport: flags.transport === "poll" ? "poll" : "websocket",
34939
36349
  configDir: this.config.configDir,
34940
36350
  apiKey: flags["api-key"],
34941
36351
  apiBaseUrl: flags["api-base-url"],
34942
36352
  subscription: flags.subscription,
34943
36353
  events: events === void 0 ? void 0 : [...new Set(events)],
34944
- number: flags.number,
36354
+ number: flags.once ? 1 : flags.number,
34945
36355
  mode: flags.exec ? "exec" : flags["forward-to"] ? "http" : "stdout",
34946
36356
  handler,
34947
36357
  signal: controller.signal
@@ -34949,10 +36359,11 @@ var ListenCommand = class ListenCommand extends Command {
34949
36359
  } catch (error) {
34950
36360
  throw new Errors.CLIError(error instanceof ListenError || error instanceof ListenStateError ? error.message : "The listener stopped because of a local state or connection error.");
34951
36361
  } finally {
36362
+ clearTimeout(timer);
34952
36363
  process.removeListener("SIGINT", cancel);
34953
36364
  process.removeListener("SIGTERM", cancel);
34954
36365
  }
34955
- if (controller.signal.aborted) process.exitCode = 130;
36366
+ if (controller.signal.aborted) process.exitCode = timedOut ? 2 : 130;
34956
36367
  }
34957
36368
  };
34958
36369
  //#endregion
@@ -35215,7 +36626,7 @@ const MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS = 60;
35215
36626
  function cliError$3(message) {
35216
36627
  return new Errors.CLIError(message, { exit: 1 });
35217
36628
  }
35218
- function sleep$1(ms) {
36629
+ function sleep(ms) {
35219
36630
  return new Promise((resolve) => setTimeout(resolve, ms));
35220
36631
  }
35221
36632
  function openBrowser(url) {
@@ -35268,7 +36679,7 @@ async function checkExistingLogin(params) {
35268
36679
  apiBaseUrl: probeApiBaseUrl,
35269
36680
  headers: requestConfig.headers
35270
36681
  });
35271
- const result = await (params.checkAccount ?? ((client) => getAccount({
36682
+ const result = await (params.checkAccount ?? ((client) => getAccount$1({
35272
36683
  client: client.client,
35273
36684
  responseStyle: "fields"
35274
36685
  })))(apiClient);
@@ -35380,7 +36791,7 @@ var LoginCommand$1 = class extends Command {
35380
36791
  let interval = Math.min(Math.max(1, start.interval), MAX_CLI_LOGIN_POLL_INTERVAL_SECONDS);
35381
36792
  let nextPollDelay = 1;
35382
36793
  while (Date.now() < deadline) {
35383
- await sleep$1(nextPollDelay * 1e3);
36794
+ await sleep(nextPollDelay * 1e3);
35384
36795
  nextPollDelay = interval;
35385
36796
  const polled = await pollCliLogin({
35386
36797
  body: { device_code: start.device_code },
@@ -37010,331 +38421,6 @@ var OrgSecretsSetCommand = class OrgSecretsSetCommand extends Command {
37010
38421
  }
37011
38422
  };
37012
38423
  //#endregion
37013
- //#region ../sdk-node/dist/payloads-BfJAtqW9.js
37014
- /**
37015
- * Load a Node builtin at call time. Exported for direct testing of the
37016
- * unavailable-module error path; production code goes through the typed
37017
- * wrappers below.
37018
- */
37019
- async function loadBuiltin(specifier, operation) {
37020
- let builtin;
37021
- if (typeof process !== "undefined" && typeof process.getBuiltinModule === "function") try {
37022
- builtin = process.getBuiltinModule(specifier);
37023
- } catch {
37024
- builtin = void 0;
37025
- }
37026
- if (builtin !== void 0 && builtin !== null) return builtin;
37027
- try {
37028
- return await import(specifier);
37029
- } catch (cause) {
37030
- throw new Error(`${operation} requires file system access, which this runtime does not provide. Pass in-memory bytes instead of a file path when running outside Node.js.`, { cause });
37031
- }
37032
- }
37033
- /** `node:fs`, loaded lazily so Workers-style bundles never see the import. */
37034
- function loadNodeFs(operation) {
37035
- return loadBuiltin("node:fs", operation);
37036
- }
37037
- /** `node:fs/promises`, loaded lazily for the same reason as {@link loadNodeFs}. */
37038
- function loadNodeFsPromises(operation) {
37039
- return loadBuiltin("node:fs/promises", operation);
37040
- }
37041
- const CHUNK_SIZE = 64 * 1024 * 1024;
37042
- const MANIFEST_VERSION = 1;
37043
- const CHUNK_KDF_INFO = "payloads-chunk";
37044
- const OBJECT_ID_BYTES = 16;
37045
- const CEK_BYTES = 32;
37046
- function subtle() {
37047
- return globalThis.crypto.subtle;
37048
- }
37049
- const textEncoder = new TextEncoder();
37050
- function randomBytes(length) {
37051
- const out = new Uint8Array(length);
37052
- globalThis.crypto.getRandomValues(out);
37053
- return out;
37054
- }
37055
- function toHex(bytes) {
37056
- let out = "";
37057
- for (const b of bytes) out += b.toString(16).padStart(2, "0");
37058
- return out;
37059
- }
37060
- function fromHex(hex) {
37061
- if (hex.length % 2 !== 0) throw new Error("invalid hex: odd length");
37062
- if (hex.length > 0 && !/^[0-9a-fA-F]+$/.test(hex)) throw new Error("invalid hex: non-hex character");
37063
- const out = new Uint8Array(hex.length / 2);
37064
- for (let i = 0; i < out.length; i++) out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
37065
- return out;
37066
- }
37067
- async function contentHash(bytes) {
37068
- return new Uint8Array(await subtle().digest("SHA-256", bytes));
37069
- }
37070
- async function contentHashHex(bytes) {
37071
- return toHex(await contentHash(bytes));
37072
- }
37073
- /** Binary Merkle root over ordered hex leaf hashes (odd node promoted). */
37074
- async function merkleRoot(leafHashesHex) {
37075
- if (leafHashesHex.length === 0) return contentHashHex(new Uint8Array(0));
37076
- let level = leafHashesHex.map(fromHex);
37077
- while (level.length > 1) {
37078
- const next = [];
37079
- for (let i = 0; i < level.length; i += 2) {
37080
- const left = level[i];
37081
- const right = i + 1 < level.length ? level[i + 1] : level[i];
37082
- const combined = new Uint8Array(left.length + right.length);
37083
- combined.set(left, 0);
37084
- combined.set(right, left.length);
37085
- next.push(await contentHash(combined));
37086
- }
37087
- level = next;
37088
- }
37089
- return toHex(level[0]);
37090
- }
37091
- async function deriveChunkKey(cek, index) {
37092
- const baseKey = await subtle().importKey("raw", cek, "HKDF", false, ["deriveKey"]);
37093
- const info = textEncoder.encode(`${CHUNK_KDF_INFO}:${index}`);
37094
- return subtle().deriveKey({
37095
- name: "HKDF",
37096
- hash: "SHA-256",
37097
- salt: new Uint8Array(32),
37098
- info
37099
- }, baseKey, {
37100
- name: "AES-GCM",
37101
- length: 256
37102
- }, false, ["encrypt", "decrypt"]);
37103
- }
37104
- function chunkNonce(index) {
37105
- const nonce = new Uint8Array(12);
37106
- new DataView(nonce.buffer).setUint32(8, index >>> 0, false);
37107
- return nonce;
37108
- }
37109
- function chunkAad(objectId, index) {
37110
- const aad = new Uint8Array(objectId.length + 4);
37111
- aad.set(objectId, 0);
37112
- new DataView(aad.buffer).setUint32(objectId.length, index >>> 0, false);
37113
- return aad;
37114
- }
37115
- async function encryptChunk(cek, objectId, index, plaintext) {
37116
- const key = await deriveChunkKey(cek, index);
37117
- const ct = await subtle().encrypt({
37118
- name: "AES-GCM",
37119
- iv: chunkNonce(index),
37120
- additionalData: chunkAad(objectId, index)
37121
- }, key, plaintext);
37122
- return new Uint8Array(ct);
37123
- }
37124
- async function decryptChunk(cek, objectId, index, ciphertext) {
37125
- const key = await deriveChunkKey(cek, index);
37126
- const pt = await subtle().decrypt({
37127
- name: "AES-GCM",
37128
- iv: chunkNonce(index),
37129
- additionalData: chunkAad(objectId, index)
37130
- }, key, ciphertext);
37131
- return new Uint8Array(pt);
37132
- }
37133
- function authHeaders(apiKey) {
37134
- return { authorization: `Bearer ${apiKey}` };
37135
- }
37136
- function apiRoot(baseUrl) {
37137
- let end = baseUrl.length;
37138
- while (end > 0 && baseUrl.charCodeAt(end - 1) === 47) end--;
37139
- return `${baseUrl.slice(0, end)}/v1/payloads`;
37140
- }
37141
- const DEFAULT_MAX_RETRIES = 6;
37142
- const RETRYABLE_STATUS = new Set([
37143
- 429,
37144
- 500,
37145
- 502,
37146
- 503,
37147
- 504
37148
- ]);
37149
- function sleep(ms) {
37150
- return new Promise((resolve) => setTimeout(resolve, ms));
37151
- }
37152
- /**
37153
- * fetch with exponential backoff on transient failures. Request bodies here are
37154
- * in-memory buffers/strings (never streams), so a retry can safely resend them.
37155
- */
37156
- async function retryingFetch(url, init, label, maxRetries = DEFAULT_MAX_RETRIES) {
37157
- for (let attempt = 0;; attempt++) {
37158
- let res;
37159
- let networkError;
37160
- try {
37161
- res = await fetch(url, init);
37162
- } catch (err) {
37163
- networkError = err;
37164
- }
37165
- if (res && !RETRYABLE_STATUS.has(res.status)) return res;
37166
- if (attempt >= maxRetries) {
37167
- if (res) return res;
37168
- throw new Error(`${label}: network error after ${attempt} retries: ${networkError?.message ?? networkError}`);
37169
- }
37170
- if (res) await res.text().catch(() => {});
37171
- await sleep(Math.min(1e3 * 2 ** attempt, 15e3) + Math.floor(Math.random() * 250));
37172
- }
37173
- }
37174
- async function initiate(opts, manifest) {
37175
- const res = await retryingFetch(apiRoot(opts.baseUrl), {
37176
- method: "POST",
37177
- headers: {
37178
- ...authHeaders(opts.apiKey),
37179
- "content-type": "application/json"
37180
- },
37181
- body: JSON.stringify({ manifest })
37182
- }, "initiate");
37183
- if (!res.ok) throw new Error(`initiate failed: HTTP ${res.status} ${await res.text()}`);
37184
- await res.text();
37185
- }
37186
- async function putChunk(opts, root, d, bytes) {
37187
- const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/chunks/${d.ciphertextHash}`, {
37188
- method: "PUT",
37189
- headers: {
37190
- ...authHeaders(opts.apiKey),
37191
- "content-type": "application/octet-stream"
37192
- },
37193
- body: bytes
37194
- }, `chunk ${d.index} upload`);
37195
- if (!res.ok) throw new Error(`chunk ${d.index} upload failed: HTTP ${res.status} ${await res.text()}`);
37196
- await res.text();
37197
- }
37198
- async function finalize(opts, root) {
37199
- const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/finalize`, {
37200
- method: "POST",
37201
- headers: authHeaders(opts.apiKey)
37202
- }, "finalize");
37203
- if (!res.ok) throw new Error(`finalize failed: HTTP ${res.status} ${await res.text()}`);
37204
- await res.text();
37205
- }
37206
- async function fetchManifest(opts, root) {
37207
- const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/manifest`, { headers: authHeaders(opts.apiKey) }, "get manifest");
37208
- if (!res.ok) throw new Error(`get manifest failed: HTTP ${res.status} ${await res.text()}`);
37209
- return (await res.json()).data.manifest;
37210
- }
37211
- async function getChunkBytes(opts, root, d) {
37212
- const res = await retryingFetch(`${apiRoot(opts.baseUrl)}/${root}/chunks/${d.ciphertextHash}`, { headers: authHeaders(opts.apiKey) }, `get chunk ${d.index}`);
37213
- if (!res.ok) throw new Error(`get chunk ${d.index} failed: HTTP ${res.status}`);
37214
- return new Uint8Array(await res.arrayBuffer());
37215
- }
37216
- async function readWindow(fh, position, length) {
37217
- const buf = Buffer.allocUnsafe(length);
37218
- let read = 0;
37219
- while (read < length) {
37220
- const { bytesRead } = await fh.read(buf, read, length - read, position + read);
37221
- if (bytesRead === 0) break;
37222
- read += bytesRead;
37223
- }
37224
- return read === length ? buf : buf.subarray(0, read);
37225
- }
37226
- async function mapLimit(items, limit, fn) {
37227
- let next = 0;
37228
- let firstError;
37229
- const worker = async () => {
37230
- while (next < items.length && firstError === void 0) {
37231
- const i = next++;
37232
- try {
37233
- await fn(items[i], i);
37234
- } catch (err) {
37235
- if (firstError === void 0) firstError = err;
37236
- return;
37237
- }
37238
- }
37239
- };
37240
- await Promise.all(Array.from({ length: Math.min(limit, items.length || 1) }, () => worker()));
37241
- if (firstError !== void 0) throw firstError;
37242
- }
37243
- /**
37244
- * Stream a file up as a Primitive Payload. Two passes over the file (encrypt to
37245
- * build the manifest, then encrypt again to upload) keep memory bounded to a few
37246
- * chunks — the whole object is never resident. Returns the content address
37247
- * (merkleRoot) and the hex CEK needed to download it.
37248
- */
37249
- async function pushFile(filePath, opts) {
37250
- const { open, stat } = await loadNodeFsPromises("pushFile");
37251
- const chunkSize = opts.chunkSize ?? CHUNK_SIZE;
37252
- const concurrency = opts.concurrency ?? 3;
37253
- const { size } = await stat(filePath);
37254
- const cek = randomBytes(CEK_BYTES);
37255
- const objectId = randomBytes(OBJECT_ID_BYTES);
37256
- const chunkCount = size === 0 ? 0 : Math.ceil(size / chunkSize);
37257
- const fh = await open(filePath, "r");
37258
- try {
37259
- const descriptors = [];
37260
- for (let index = 0; index < chunkCount; index++) {
37261
- const position = index * chunkSize;
37262
- const length = Math.min(chunkSize, size - position);
37263
- const plaintext = await readWindow(fh, position, length);
37264
- const ciphertext = await encryptChunk(cek, objectId, index, plaintext);
37265
- descriptors.push({
37266
- index,
37267
- ciphertextHash: await contentHashHex(ciphertext),
37268
- plaintextSize: length,
37269
- ciphertextSize: ciphertext.length
37270
- });
37271
- opts.onProgress?.("encrypt", index + 1, chunkCount);
37272
- }
37273
- const root = await merkleRoot(descriptors.map((d) => d.ciphertextHash));
37274
- await initiate(opts, {
37275
- version: MANIFEST_VERSION,
37276
- objectId: toHex(objectId),
37277
- chunkSize,
37278
- totalPlaintextSize: size,
37279
- chunkCount,
37280
- chunks: descriptors,
37281
- merkleRoot: root
37282
- });
37283
- let uploaded = 0;
37284
- await mapLimit(descriptors, concurrency, async (d) => {
37285
- const plaintext = await readWindow(fh, d.index * chunkSize, d.plaintextSize);
37286
- await putChunk(opts, root, d, await encryptChunk(cek, objectId, d.index, plaintext));
37287
- opts.onProgress?.("upload", ++uploaded, chunkCount);
37288
- });
37289
- await finalize(opts, root);
37290
- return {
37291
- merkleRoot: root,
37292
- cek: toHex(cek),
37293
- chunkCount,
37294
- totalBytes: size
37295
- };
37296
- } finally {
37297
- await fh.close();
37298
- }
37299
- }
37300
- /**
37301
- * Stream a Primitive Payload down to a file, verifying and decrypting one chunk
37302
- * at a time (bounded memory). Every chunk is content-address-checked before
37303
- * decryption, so a corrupt or substituted chunk throws.
37304
- */
37305
- async function pullFile(root, outPath, opts) {
37306
- const { createWriteStream } = await loadNodeFs("pullFile");
37307
- const { rm } = await loadNodeFsPromises("pullFile");
37308
- const manifest = await fetchManifest(opts, root);
37309
- const computedRoot = await merkleRoot(manifest.chunks.map((c) => c.ciphertextHash));
37310
- if (computedRoot !== root) throw new Error(`manifest does not match the requested content address (got ${computedRoot})`);
37311
- const cek = fromHex(opts.cek);
37312
- const objectId = fromHex(manifest.objectId);
37313
- const out = createWriteStream(outPath);
37314
- let onWriteError = () => {};
37315
- const writeErrored = new Promise((_, reject) => {
37316
- onWriteError = reject;
37317
- });
37318
- writeErrored.catch(() => {});
37319
- out.on("error", (err) => onWriteError(err));
37320
- try {
37321
- let done = 0;
37322
- for (const d of manifest.chunks) {
37323
- const ciphertext = await getChunkBytes(opts, root, d);
37324
- if (await contentHashHex(ciphertext) !== d.ciphertextHash) throw new Error(`chunk ${d.index} failed integrity check`);
37325
- const plaintext = await decryptChunk(cek, objectId, d.index, ciphertext);
37326
- if (!out.write(plaintext)) await Promise.race([new Promise((resolve) => out.once("drain", resolve)), writeErrored]);
37327
- opts.onProgress?.("download", ++done, manifest.chunkCount);
37328
- }
37329
- await Promise.race([new Promise((resolve) => out.end(resolve)), writeErrored]);
37330
- } catch (err) {
37331
- out.destroy();
37332
- await rm(outPath, { force: true });
37333
- throw err;
37334
- }
37335
- return manifest;
37336
- }
37337
- //#endregion
37338
38424
  //#region src/oclif/commands/payloads.ts
37339
38425
  const DEFAULT_API_BASE_URL = "https://api.primitive.dev";
37340
38426
  const authFlags = {
@@ -39232,7 +40318,7 @@ var PaymentsRegisterPayoutAddressCommand = class PaymentsRegisterPayoutAddressCo
39232
40318
  configDir: this.config.configDir
39233
40319
  });
39234
40320
  await runWithTiming(flags.time, async () => {
39235
- const account = await getAccount({
40321
+ const account = await getAccount$1({
39236
40322
  client: apiClient.client,
39237
40323
  responseStyle: "fields"
39238
40324
  });
@@ -41425,7 +42511,7 @@ var WhoamiCommand = class WhoamiCommand extends Command {
41425
42511
  apiBaseUrl: flags["api-base-url"],
41426
42512
  configDir: this.config.configDir
41427
42513
  });
41428
- const result = await getAccount({
42514
+ const result = await getAccount$1({
41429
42515
  client: apiClient.client,
41430
42516
  responseStyle: "fields"
41431
42517
  });