@r4-sdk/cli 1.0.0 → 1.0.2

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/lib/index.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command44 } from "commander";
4
+ import { Command as Command45 } from "commander";
5
5
 
6
6
  // src/commands/agent/create.ts
7
7
  import { Command } from "commander";
8
8
  import ora from "ora";
9
9
 
10
10
  // src/lib/client.ts
11
+ import os from "os";
12
+ var AGENT_RUNTIME_HOSTNAME_HEADER = "X-R4-Agent-Hostname";
11
13
  var CliClient = class {
12
14
  apiKey;
13
15
  baseUrl;
@@ -15,19 +17,19 @@ var CliClient = class {
15
17
  this.apiKey = apiKey;
16
18
  this.baseUrl = baseUrl.replace(/\/$/, "");
17
19
  }
18
- normalizeMachinePath(path7) {
19
- if (!path7 || /^\w+:\/\//.test(path7)) {
20
+ normalizeMachinePath(path6) {
21
+ if (!path6 || /^\w+:\/\//.test(path6)) {
20
22
  throw new Error("Machine request path must be a relative machine API path.");
21
23
  }
22
- const normalizedPath = path7.startsWith("/") ? path7 : `/${path7}`;
24
+ const normalizedPath = path6.startsWith("/") ? path6 : `/${path6}`;
23
25
  return normalizedPath.startsWith("/api/v1/machine") ? normalizedPath : `/api/v1/machine${normalizedPath}`;
24
26
  }
25
27
  /**
26
28
  * Make an authenticated request to the machine API.
27
29
  * Handles error responses consistently with the SDK pattern.
28
30
  */
29
- async request(method, path7, body) {
30
- const url = `${this.baseUrl}${path7}`;
31
+ async request(method, path6, body) {
32
+ const url = `${this.baseUrl}${path6}`;
31
33
  const response = await fetch(url, {
32
34
  method,
33
35
  headers: {
@@ -62,11 +64,23 @@ var CliClient = class {
62
64
  }
63
65
  /** Register or re-confirm the local agent runtime public key. */
64
66
  async registerAgentPublicKey(body) {
65
- return this.request(
66
- "POST",
67
- "/api/v1/machine/vault/public-key",
68
- body
69
- );
67
+ const response = await fetch(`${this.baseUrl}/api/v1/machine/vault/public-key`, {
68
+ method: "POST",
69
+ headers: {
70
+ "X-API-Key": this.apiKey,
71
+ "Content-Type": "application/json",
72
+ [AGENT_RUNTIME_HOSTNAME_HEADER]: os.hostname()
73
+ },
74
+ body: JSON.stringify(body)
75
+ });
76
+ if (!response.ok) {
77
+ const errorBody = await response.json().catch(() => ({}));
78
+ const error = errorBody?.error;
79
+ const errorMessage = error?.message || `HTTP ${response.status}: ${response.statusText}`;
80
+ const errorCode = typeof error?.code === "string" ? ` [${error.code}]` : "";
81
+ throw new Error(`R4 API Error${errorCode}: ${errorMessage}`);
82
+ }
83
+ return response.json();
70
84
  }
71
85
  /** Read the authenticated machine principal and resolved policy context. */
72
86
  async getMachineIdentity() {
@@ -95,6 +109,27 @@ var CliClient = class {
95
109
  `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/items`
96
110
  );
97
111
  }
112
+ /** List directly shared vault items whose parent vault is otherwise hidden. */
113
+ async getSharedVaultItems() {
114
+ return this.request(
115
+ "GET",
116
+ "/api/v1/machine/vault/shared-items"
117
+ );
118
+ }
119
+ /** Retrieve the full machine vault-item detail payload. */
120
+ async getVaultItemDetail(vaultId, itemId) {
121
+ return this.request(
122
+ "GET",
123
+ `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/items/${encodeURIComponent(itemId)}`
124
+ );
125
+ }
126
+ /** Retrieve a zero-trust download ticket for a vault attachment. */
127
+ async getVaultAttachmentDownloadTicket(vaultId, assetId) {
128
+ return this.request(
129
+ "GET",
130
+ `/api/v1/machine/attachments/${encodeURIComponent(vaultId)}/assets/${encodeURIComponent(assetId)}/download-ticket`
131
+ );
132
+ }
98
133
  /** Create a checkpoint-signed vault item from a prebuilt JSON payload. */
99
134
  async createVaultItem(vaultId, body) {
100
135
  return this.request(
@@ -303,18 +338,18 @@ function withErrorHandler(fn) {
303
338
  }
304
339
 
305
340
  // src/lib/config.ts
306
- import fs2 from "node:fs";
341
+ import fs2 from "fs";
307
342
 
308
343
  // src/lib/profile-paths.ts
309
- import fs from "node:fs";
310
- import os from "node:os";
311
- import path from "node:path";
344
+ import fs from "fs";
345
+ import os2 from "os";
346
+ import path from "path";
312
347
  function sanitizeProfileName(profileName) {
313
348
  const sanitized = profileName.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
314
349
  return sanitized || "default";
315
350
  }
316
351
  function getR4HomeDir() {
317
- return path.join(os.homedir(), ".r4");
352
+ return path.join(os2.homedir(), ".r4");
318
353
  }
319
354
  function getProfilesDir() {
320
355
  return path.join(getR4HomeDir(), "profiles");
@@ -539,7 +574,7 @@ function clearManagedProfileDirectory(profileName) {
539
574
  }
540
575
 
541
576
  // src/lib/credentials-store.ts
542
- import fs3 from "node:fs";
577
+ import fs3 from "fs";
543
578
  function buildApiKeyFromParts(accessKey, secretKey) {
544
579
  if (!accessKey || !secretKey) {
545
580
  return void 0;
@@ -604,9 +639,9 @@ function clearProfileCredentials(profileName) {
604
639
  }
605
640
 
606
641
  // src/lib/private-key.ts
607
- import crypto from "node:crypto";
608
- import fs4 from "node:fs";
609
- import path2 from "node:path";
642
+ import crypto from "crypto";
643
+ import fs4 from "fs";
644
+ import path2 from "path";
610
645
  function getDefaultPrivateKeyPath(profileName) {
611
646
  return getManagedPrivateKeyPath(profileName);
612
647
  }
@@ -872,1793 +907,135 @@ function buildCreateAgentBody(options) {
872
907
  };
873
908
  }
874
909
  function createCommand() {
875
- return new Command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").requiredOption("--domain-tenant-id <id>", "Target domain tenant ID").option("--budget-id <id>", "Existing budget ID to assign").option("--vault-id <id>", "Optional vault ID to store credentials in").option("--new-budget-name <name>", "Create and assign a new per-agent budget").option("--new-budget-description <text>", "Description for the new per-agent budget").option("--new-budget-target <amount>", "Target amount for the new per-agent budget").option(
876
- "--security-group-id <id>",
877
- "Security group IDs (repeat or use comma-separated values)",
878
- collectOptionValues,
879
- []
880
- ).option(
881
- "--permission <grant>",
882
- "Machine permission grant (repeat or use comma-separated values)",
883
- collectOptionValues,
884
- []
885
- ).action(
886
- withErrorHandler(
887
- async (options, cmd) => {
888
- const globalOpts = cmd.optsWithGlobals();
889
- const connection = resolveConnection(globalOpts, { requireApiKey: true });
890
- const client = new CliClient(connection.apiKey, connection.baseUrl);
891
- const body = buildCreateAgentBody(options);
892
- const spinner = ora("Creating agent...").start();
893
- const response = await client.createAgent(body);
894
- spinner.stop();
895
- if (globalOpts.json) {
896
- console.log(JSON.stringify(response, null, 2));
897
- return;
898
- }
899
- success(`Created agent "${response.name}"`);
900
- printDetail(
901
- [
902
- ["ID", response.id],
903
- ["Access Key", response.accessKey],
904
- ["Access Secret", response.accessSecret],
905
- ["Vault Item", response.vaultItemId]
906
- ],
907
- false
908
- );
909
- }
910
- )
911
- );
912
- }
913
-
914
- // src/commands/agent/get.ts
915
- import { Command as Command2 } from "commander";
916
- import chalk2 from "chalk";
917
- import ora2 from "ora";
918
- function getCommand() {
919
- return new Command2("get").description("Get agent details").argument("<id>", "Agent ID").action(
920
- withErrorHandler(async (id, _opts, cmd) => {
921
- const globalOpts = cmd.optsWithGlobals();
922
- const connection = resolveConnection(globalOpts, { requireApiKey: true });
923
- const client = new CliClient(connection.apiKey, connection.baseUrl);
924
- const spinner = ora2("Fetching agent...").start();
925
- const agent = await client.getAgent(id);
926
- spinner.stop();
927
- if (globalOpts.json) {
928
- console.log(JSON.stringify(agent, null, 2));
929
- return;
930
- }
931
- console.log();
932
- console.log(chalk2.bold(` ${agent.name}`));
933
- console.log();
934
- printDetail(
935
- [
936
- ["ID", agent.id],
937
- ["Tenant", agent.tenantName],
938
- ["Domain", agent.domainName],
939
- ["Domain Tenant ID", agent.domainTenantId],
940
- ["Budget", agent.budgetId],
941
- ["Public Key Ready", agent.isPublicKeyRegistered ? "Yes" : "No"],
942
- ["Created At", agent.createdAt],
943
- ["Updated At", agent.updatedAt],
944
- ["Archived At", agent.archivedAt]
945
- ],
946
- false
947
- );
948
- if (agent.securityGroups.length > 0) {
949
- console.log();
950
- console.log(chalk2.bold(" Security Groups"));
951
- console.log();
952
- printTable(
953
- ["ID", "Name"],
954
- agent.securityGroups.map((group) => [group.id, group.name]),
955
- false
956
- );
957
- }
958
- console.log();
959
- })
960
- );
961
- }
962
-
963
- // src/commands/agent/get-tenant-roles.ts
964
- import { Command as Command3 } from "commander";
965
- import ora3 from "ora";
966
- function getTenantRolesCommand() {
967
- return new Command3("get-tenant-roles").description("Get the tenant roles assigned to an agent").argument("<id>", "Agent ID").action(
968
- withErrorHandler(async (id, _opts, cmd) => {
969
- const globalOpts = cmd.optsWithGlobals();
970
- const connection = resolveConnection(globalOpts, { requireApiKey: true });
971
- const client = new CliClient(connection.apiKey, connection.baseUrl);
972
- const spinner = ora3("Fetching agent tenant roles...").start();
973
- const response = await client.getAgentTenantRoles(id);
974
- spinner.stop();
975
- if (globalOpts.json) {
976
- console.log(JSON.stringify(response, null, 2));
977
- return;
978
- }
979
- console.log();
980
- printDetail(
981
- [
982
- ["Agent ID", id],
983
- ["Explicit Roles", response.roles.join(", ") || "(none)"],
984
- ["Inherited Roles", response.inheritedRoles.join(", ") || "(none)"]
985
- ],
986
- false
987
- );
988
- console.log();
989
- })
990
- );
991
- }
992
-
993
- // src/commands/agent/init.ts
994
- import { Command as Command5 } from "commander";
995
- import ora5 from "ora";
996
-
997
- // src/commands/doctor.ts
998
- import { Command as Command4 } from "commander";
999
- import chalk3 from "chalk";
1000
- import ora4 from "ora";
1001
-
1002
- // ../node/lib/index.js
1003
- import { randomUUID } from "node:crypto";
1004
- import path3 from "node:path";
1005
- import crypto2 from "node:crypto";
1006
- import fs5 from "node:fs";
1007
- import path4 from "node:path";
1008
- import fs22 from "node:fs";
1009
- import path22 from "node:path";
1010
- var R4Client = class {
1011
- apiKey;
1012
- baseUrl;
1013
- constructor(apiKey, baseUrl) {
1014
- this.apiKey = apiKey;
1015
- this.baseUrl = baseUrl.replace(/\/$/, "");
1016
- }
1017
- buildHeaders() {
1018
- return {
1019
- "X-API-Key": this.apiKey,
1020
- "Content-Type": "application/json"
1021
- };
1022
- }
1023
- normalizeMachinePath(path42) {
1024
- if (!path42 || /^\w+:\/\//.test(path42)) {
1025
- throw new Error("Machine request path must be a relative machine API path.");
1026
- }
1027
- const normalizedPath = path42.startsWith("/") ? path42 : `/${path42}`;
1028
- return normalizedPath.startsWith("/api/v1/machine") ? normalizedPath : `/api/v1/machine${normalizedPath}`;
1029
- }
1030
- async request(path42, init) {
1031
- const response = await fetch(`${this.baseUrl}${path42}`, {
1032
- ...init,
1033
- headers: {
1034
- ...this.buildHeaders(),
1035
- ...init.headers ?? {}
1036
- }
1037
- });
1038
- if (!response.ok) {
1039
- const errorBody = await response.json().catch(() => ({}));
1040
- const errorMessage = typeof errorBody.error?.message === "string" ? errorBody.error.message : `HTTP ${response.status}: ${response.statusText}`;
1041
- const errorCode = typeof errorBody.error?.code === "string" ? ` [${errorBody.error.code}]` : "";
1042
- throw new Error(`R4 API Error${errorCode}: ${errorMessage}`);
1043
- }
1044
- if (response.status === 204) {
1045
- return void 0;
1046
- }
1047
- const responseText = await response.text();
1048
- if (!responseText) {
1049
- return void 0;
1050
- }
1051
- try {
1052
- return JSON.parse(responseText);
1053
- } catch {
1054
- return responseText;
1055
- }
1056
- }
1057
- /**
1058
- * Calls any existing machine API route through the authenticated client.
1059
- * Accepts either `/api/v1/machine/...` or the shorter `/...` machine path.
1060
- */
1061
- async requestMachine(params) {
1062
- return this.request(this.normalizeMachinePath(params.path), {
1063
- method: params.method,
1064
- body: params.body === void 0 ? void 0 : JSON.stringify(params.body)
1065
- });
1066
- }
1067
- /**
1068
- * Registers or re-confirms the agent runtime's local RSA public key.
1069
- */
1070
- async registerAgentPublicKey(body) {
1071
- return this.request("/api/v1/machine/vault/public-key", {
1072
- method: "POST",
1073
- body: JSON.stringify(body)
1074
- });
1075
- }
1076
- /**
1077
- * Returns the current machine principal, session context, and resolved policy.
1078
- */
1079
- async getMachineIdentity() {
1080
- return this.request("/api/v1/machine/me", {
1081
- method: "GET"
1082
- });
1083
- }
1084
- /**
1085
- * Lists all accessible non-hidden vaults. When `projectId` is provided, the
1086
- * backend additionally filters to vaults associated with that project.
1087
- */
1088
- async listVaults(projectId) {
1089
- const search = projectId ? `?projectId=${encodeURIComponent(projectId)}` : "";
1090
- return this.request(`/api/v1/machine/vault${search}`, {
1091
- method: "GET"
1092
- });
1093
- }
1094
- /**
1095
- * Retrieves the active wrapped DEK for the authenticated agent on a vault.
1096
- */
1097
- async getAgentWrappedKey(vaultId) {
1098
- return this.request(
1099
- `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/wrapped-key`,
1100
- { method: "GET" }
1101
- );
1102
- }
1103
- /**
1104
- * Retrieves the trusted user-key directory for a vault so the runtime can
1105
- * verify wrapped-DEK signatures locally.
1106
- */
1107
- async getVaultUserKeyDirectory(vaultId, params) {
1108
- const searchParams = new URLSearchParams();
1109
- if (params?.knownTransparencyVersion !== void 0) {
1110
- searchParams.set("knownTransparencyVersion", String(params.knownTransparencyVersion));
1111
- }
1112
- if (params?.knownTransparencyHash) {
1113
- searchParams.set("knownTransparencyHash", params.knownTransparencyHash);
1114
- }
1115
- const search = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
1116
- return this.request(
1117
- `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/public-keys${search}`,
1118
- { method: "GET" }
1119
- );
1120
- }
1121
- /**
1122
- * Lists all items in a vault with lightweight metadata.
1123
- */
1124
- async listVaultItems(vaultId) {
1125
- return this.request(
1126
- `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/items`,
1127
- { method: "GET" }
1128
- );
1129
- }
1130
- /**
1131
- * Retrieves the full field payloads for a vault item.
1132
- */
1133
- async getVaultItemDetail(vaultId, itemId) {
1134
- return this.request(
1135
- `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/items/${encodeURIComponent(itemId)}`,
1136
- { method: "GET" }
1137
- );
1138
- }
1139
- /**
1140
- * Retrieves the provider-scoped runtime bundle needed for local decryption.
1141
- */
1142
- async getTokenProviderRuntime(tokenProviderId, params) {
1143
- const searchParams = new URLSearchParams();
1144
- if (params?.knownTransparencyVersion !== void 0) {
1145
- searchParams.set("knownTransparencyVersion", String(params.knownTransparencyVersion));
1146
- }
1147
- if (params?.knownTransparencyHash) {
1148
- searchParams.set("knownTransparencyHash", params.knownTransparencyHash);
1149
- }
1150
- const search = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
1151
- return this.request(
1152
- `/api/v1/machine/intelligence-provider/${encodeURIComponent(tokenProviderId)}/runtime${search}`,
1153
- { method: "GET" }
1154
- );
1155
- }
1156
- /**
1157
- * Runs the managed budget gate before a token-provider call is sent to the vendor.
1158
- */
1159
- async authorizeTokenProviderUsage(tokenProviderId, body) {
1160
- return this.request(
1161
- `/api/v1/machine/intelligence-provider/${encodeURIComponent(tokenProviderId)}/authorize-usage`,
1162
- {
1163
- method: "POST",
1164
- body: JSON.stringify(body)
1165
- }
1166
- );
1167
- }
1168
- /**
1169
- * Publishes finalized token usage for a completed managed provider call.
1170
- */
1171
- async reportTokenProviderUsage(tokenProviderId, body) {
1172
- return this.request(
1173
- `/api/v1/machine/intelligence-provider/${encodeURIComponent(tokenProviderId)}/report-usage`,
1174
- {
1175
- method: "POST",
1176
- body: JSON.stringify(body)
1177
- }
1178
- );
1179
- }
1180
- };
1181
- var TRANSPARENCY_WITNESS_PAYLOAD_PREFIX = "r4-transparency-witness-v1";
1182
- var DEFAULT_TRANSPARENCY_WITNESS_URL = "https://transparency.r4.dev";
1183
- var TRANSPARENCY_WITNESS_ROOT_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1184
- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA18JhILFiS/BOWR9laubW
1185
- g2vepQy26BXAlnrscZZVQUzBBaCM4hWobpt3Nh77vxP0gqVAJXP1hVhPPwxGQnOF
1186
- 4Qg/RK4iEETjMdmh3KMqFX9MeE9tP4cTOGtsgWsedNpu6TvMT+2vu+0ltmr7p4Xv
1187
- H0ID48Q8JLeNksc/RekrsfzQ9DVtXFS7z1FF2VQgzamdJsW9hGMiM7Q+0iXei7PW
1188
- 3PsLd1aNtqJ3lIj3t12qFiJiYyKF0hEq0//Abgb9SgDv/WOlRG1Ianf1/fnP2jer
1189
- ZYiZSylXqQdun0Db2d0+FDm/znV2AGAmBEXm6qnCogEHu77LoLyCyJOlB9WNtRwh
1190
- KnbzTmE2Mw/43jxvCcR7pE5kik/tdeMvqGFZfg3ozUG9eM0q0TURH6g9b9J4sBnR
1191
- dxz2PbF4cl/AeL4ANPmLz3kUQaDA6wR0veVk5jV+Uqr55TYz/zEbY1rtJbmnc53Q
1192
- ihPS6xtSiexrqnOgqm/AVbiRhxjPqfg3/VJM3zR5Blnu02AqVR9kCT0WkyEWRz5X
1193
- 6HU8DEocJIPz8UwBMKQ7rnjMPv/Fjpuav/EIad5vOdfxCZkjyTYoQg8vLUyfXvgD
1194
- mBWFgKIN8GTRyM+LjZIgznjN58dZ8ZvsGd14oKnH7WgAh9FVh8ri7gNmsdJeRTn/
1195
- 2zDkTlx+FQxAxqFaYV7qCvcCAwEAAQ==
1196
- -----END PUBLIC KEY-----`;
1197
- var buildOrgUserKeyDirectoryWitnessPayload = (orgId, head) => [
1198
- TRANSPARENCY_WITNESS_PAYLOAD_PREFIX,
1199
- "org-user-key-directory",
1200
- orgId,
1201
- String(head.version),
1202
- head.hash
1203
- ].join(":");
1204
- var buildAgentPublicKeyWitnessPayload = (agentId, head) => [
1205
- TRANSPARENCY_WITNESS_PAYLOAD_PREFIX,
1206
- "agent-public-key",
1207
- agentId,
1208
- String(head.version),
1209
- head.hash
1210
- ].join(":");
1211
- var buildOrgUserKeyDirectoryWitnessPath = (orgId) => `v1/orgs/${orgId}/user-key-directory-head.json`;
1212
- var buildAgentPublicKeyWitnessPath = (agentId) => `v1/agents/${agentId}/public-key-head.json`;
1213
- var buildTransparencyWitnessUrl = (baseUrl, path42) => `${baseUrl.replace(/\/+$/, "")}/${path42.replace(/^\/+/, "")}`;
1214
- function parseHostname(apiBaseUrl) {
1215
- const trimmed = apiBaseUrl.trim();
1216
- if (!trimmed) {
1217
- return null;
1218
- }
1219
- try {
1220
- const normalized = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
1221
- return new URL(normalized).hostname.toLowerCase();
1222
- } catch {
1223
- return null;
1224
- }
1225
- }
1226
- var shouldUseDefaultTransparencyWitness = (apiBaseUrl) => {
1227
- const hostname = parseHostname(apiBaseUrl);
1228
- return hostname === "r4.dev" || hostname === "api.r4.dev";
1229
- };
1230
- var resolveTransparencyWitnessBaseUrl = (params) => {
1231
- const configuredBaseUrl = params.configuredBaseUrl?.trim();
1232
- if (configuredBaseUrl) {
1233
- return configuredBaseUrl;
1234
- }
1235
- return shouldUseDefaultTransparencyWitness(params.apiBaseUrl) ? DEFAULT_TRANSPARENCY_WITNESS_URL : null;
1236
- };
1237
- var RSA_OAEP_CONFIG = {
1238
- padding: crypto2.constants.RSA_PKCS1_OAEP_PADDING,
1239
- oaepHash: "sha256"
1240
- };
1241
- var RSA_PSS_SIGN_CONFIG = {
1242
- padding: crypto2.constants.RSA_PKCS1_PSS_PADDING,
1243
- saltLength: 32
1244
- };
1245
- var USER_KEY_ROTATION_PREFIX = "r4-user-key-rotation-v1";
1246
- var USER_KEY_DIRECTORY_CHECKPOINT_PREFIX = "r4-user-key-directory-checkpoint-v1";
1247
- var USER_KEY_DIRECTORY_TRANSPARENCY_ENTRY_PREFIX = "r4-user-key-directory-transparency-entry-v1";
1248
- var AGENT_PUBLIC_KEY_TRANSPARENCY_ENTRY_PREFIX = "r4-agent-public-key-transparency-entry-v1";
1249
- var WRAPPED_DEK_SIGNATURE_PREFIX = "r4-wrapped-dek-signature-v1";
1250
- var VAULT_SUMMARY_CHECKPOINT_PREFIX = "r4-vault-summary-checkpoint-v1";
1251
- var VAULT_ITEM_DETAIL_CHECKPOINT_PREFIX = "r4-vault-item-detail-checkpoint-v1";
1252
- function pemToDer(pem, beginLabel, endLabel) {
1253
- const derBase64 = pem.replace(beginLabel, "").replace(endLabel, "").replace(/\s/g, "");
1254
- return Buffer.from(derBase64, "base64");
1255
- }
1256
- function getWrappedDekFingerprint(wrappedDek) {
1257
- return crypto2.createHash("sha256").update(Buffer.from(wrappedDek, "base64")).digest("hex");
1258
- }
1259
- function getCheckpointFingerprint(prefix, canonicalJson) {
1260
- return `${prefix}:${crypto2.createHash("sha256").update(canonicalJson, "utf8").digest("hex")}`;
1261
- }
1262
- function loadPrivateKey2(privateKeyPath) {
1263
- return fs5.readFileSync(path4.resolve(privateKeyPath), "utf8").trim();
1264
- }
1265
- function derivePublicKey2(privateKeyPem) {
1266
- return crypto2.createPublicKey(privateKeyPem).export({
1267
- type: "spki",
1268
- format: "pem"
1269
- }).toString();
1270
- }
1271
- function getPublicKeyFingerprint(publicKeyPem) {
1272
- const derBytes = pemToDer(publicKeyPem, "-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----");
1273
- return crypto2.createHash("sha256").update(derBytes).digest("hex");
1274
- }
1275
- function buildUserKeyRotationPayload(previousUserKeyPairId, newPublicKeyFingerprint) {
1276
- return `${USER_KEY_ROTATION_PREFIX}:${previousUserKeyPairId}:${newPublicKeyFingerprint}`;
1277
- }
1278
- function verifyUserKeyRotation(previousUserKeyPairId, newPublicKeyPem, rotationSignature, previousPublicKeyPem) {
1279
- const payload = buildUserKeyRotationPayload(
1280
- previousUserKeyPairId,
1281
- getPublicKeyFingerprint(newPublicKeyPem)
1282
- );
1283
- try {
1284
- return crypto2.verify(
1285
- "sha256",
1286
- Buffer.from(payload, "utf8"),
1287
- {
1288
- key: previousPublicKeyPem,
1289
- ...RSA_PSS_SIGN_CONFIG
1290
- },
1291
- Buffer.from(rotationSignature, "base64")
1292
- );
1293
- } catch {
1294
- return false;
1295
- }
1296
- }
1297
- function verifyTransparencyWitnessPayload(payload, signature, publicKeyPem) {
1298
- try {
1299
- return crypto2.verify(
1300
- "sha256",
1301
- Buffer.from(payload, "utf8"),
1302
- {
1303
- key: publicKeyPem,
1304
- ...RSA_PSS_SIGN_CONFIG
1305
- },
1306
- Buffer.from(signature, "base64")
1307
- );
1308
- } catch {
1309
- return false;
1310
- }
1311
- }
1312
- function verifyOrgUserKeyDirectoryWitnessArtifact(artifact, publicKeyPem) {
1313
- return verifyTransparencyWitnessPayload(
1314
- buildOrgUserKeyDirectoryWitnessPayload(artifact.orgId, artifact.head),
1315
- artifact.signature,
1316
- publicKeyPem
1317
- );
1318
- }
1319
- function verifyAgentPublicKeyWitnessArtifact(artifact, publicKeyPem) {
1320
- return verifyTransparencyWitnessPayload(
1321
- buildAgentPublicKeyWitnessPayload(artifact.agentId, artifact.head),
1322
- artifact.signature,
1323
- publicKeyPem
1324
- );
1325
- }
1326
- function buildAgentPublicKeyTransparencyEntryHash(entry) {
1327
- return getCheckpointFingerprint(
1328
- AGENT_PUBLIC_KEY_TRANSPARENCY_ENTRY_PREFIX,
1329
- JSON.stringify({
1330
- agentId: entry.agentId,
1331
- version: entry.version,
1332
- encryptionKeyId: entry.encryptionKeyId,
1333
- publicKey: entry.publicKey,
1334
- fingerprint: entry.fingerprint,
1335
- previousEncryptionKeyId: entry.previousEncryptionKeyId ?? null,
1336
- rotationSignature: entry.rotationSignature ?? null,
1337
- previousEntryHash: entry.previousEntryHash ?? null
1338
- })
1339
- );
1340
- }
1341
- function buildAgentPublicKeyTransparencyEntry(params) {
1342
- const normalized = {
1343
- agentId: params.agentId,
1344
- version: params.version,
1345
- encryptionKeyId: params.encryptionKeyId,
1346
- publicKey: params.publicKey,
1347
- fingerprint: params.fingerprint ?? getPublicKeyFingerprint(params.publicKey),
1348
- previousEncryptionKeyId: params.previousEncryptionKeyId ?? null,
1349
- rotationSignature: params.rotationSignature ?? null,
1350
- previousEntryHash: params.previousEntryHash ?? null
1351
- };
1352
- return {
1353
- ...normalized,
1354
- entryHash: buildAgentPublicKeyTransparencyEntryHash(normalized)
1355
- };
1356
- }
1357
- function verifyAgentPublicKeyTransparencyChain(entries) {
1358
- let previousVersion = null;
1359
- let previousHash = null;
1360
- for (const entry of entries) {
1361
- if (buildAgentPublicKeyTransparencyEntryHash(entry) !== entry.entryHash) {
1362
- return false;
1363
- }
1364
- if (entry.previousEntryHash !== previousHash) {
1365
- return false;
1366
- }
1367
- if (previousVersion !== null && entry.version !== previousVersion + 1) {
1368
- return false;
1369
- }
1370
- previousVersion = entry.version;
1371
- previousHash = entry.entryHash;
1372
- }
1373
- return true;
1374
- }
1375
- function buildAgentPublicKeyTransparencyHead(entries) {
1376
- const lastEntry = entries[entries.length - 1];
1377
- if (!lastEntry) {
1378
- return null;
1379
- }
1380
- return {
1381
- version: lastEntry.version,
1382
- hash: lastEntry.entryHash
1383
- };
1384
- }
1385
- function verifyAgentPublicKeyTransparencyProof(params) {
1386
- const currentEntryHead = {
1387
- version: params.currentEntry.version,
1388
- hash: params.currentEntry.entryHash
1389
- };
1390
- if (params.proof.head.version !== currentEntryHead.version || params.proof.head.hash !== currentEntryHead.hash) {
1391
- return false;
1392
- }
1393
- if (!verifyAgentPublicKeyTransparencyChain(params.proof.entries)) {
1394
- return false;
1395
- }
1396
- const proofHead = buildAgentPublicKeyTransparencyHead(params.proof.entries);
1397
- if (!proofHead || proofHead.version !== currentEntryHead.version || proofHead.hash !== currentEntryHead.hash) {
1398
- return false;
1399
- }
1400
- if (!params.previousHead) {
1401
- return params.proof.entries.length > 0;
1402
- }
1403
- if (params.currentEntry.version < params.previousHead.version) {
1404
- return false;
1405
- }
1406
- const previousEntry = params.proof.entries.find((entry) => entry.version === params.previousHead.version);
1407
- if (params.currentEntry.version === params.previousHead.version) {
1408
- return previousEntry?.entryHash === params.previousHead.hash;
1409
- }
1410
- return previousEntry?.entryHash === params.previousHead.hash;
1411
- }
1412
- function normalizeUserKeyDirectoryCheckpoint(checkpoint) {
1413
- return {
1414
- orgId: checkpoint.orgId,
1415
- version: checkpoint.version,
1416
- entries: [...checkpoint.entries].map((entry) => ({
1417
- userKeyPairId: entry.userKeyPairId,
1418
- orgUserId: entry.orgUserId,
1419
- fingerprint: entry.fingerprint,
1420
- previousUserKeyPairId: entry.previousUserKeyPairId ?? null,
1421
- rotationSignature: entry.rotationSignature ?? null
1422
- })).sort((left, right) => left.userKeyPairId.localeCompare(right.userKeyPairId))
1423
- };
1424
- }
1425
- function canonicalizeUserKeyDirectoryCheckpoint(checkpoint) {
1426
- return JSON.stringify(normalizeUserKeyDirectoryCheckpoint(checkpoint));
1427
- }
1428
- function buildUserKeyDirectoryCheckpointPayload(checkpoint) {
1429
- return getCheckpointFingerprint(
1430
- USER_KEY_DIRECTORY_CHECKPOINT_PREFIX,
1431
- canonicalizeUserKeyDirectoryCheckpoint(checkpoint)
1432
- );
1433
- }
1434
- function verifyUserKeyDirectoryCheckpoint(checkpoint, signature, publicKeyPem) {
1435
- try {
1436
- return crypto2.verify(
1437
- "sha256",
1438
- Buffer.from(buildUserKeyDirectoryCheckpointPayload(checkpoint), "utf8"),
1439
- {
1440
- key: publicKeyPem,
1441
- ...RSA_PSS_SIGN_CONFIG
1442
- },
1443
- Buffer.from(signature, "base64")
1444
- );
1445
- } catch {
1446
- return false;
1447
- }
1448
- }
1449
- function buildUserKeyDirectoryTransparencyEntryHash(entry) {
1450
- return getCheckpointFingerprint(
1451
- USER_KEY_DIRECTORY_TRANSPARENCY_ENTRY_PREFIX,
1452
- JSON.stringify({
1453
- orgId: entry.orgId,
1454
- version: entry.version,
1455
- directoryCheckpointPayload: entry.directoryCheckpointPayload,
1456
- signerUserKeyPairId: entry.signerUserKeyPairId,
1457
- signerOrgUserId: entry.signerOrgUserId,
1458
- signerFingerprint: entry.signerFingerprint,
1459
- signature: entry.signature,
1460
- previousEntryHash: entry.previousEntryHash ?? null
1461
- })
1462
- );
1463
- }
1464
- function buildUserKeyDirectoryTransparencyEntry(params) {
1465
- const entryWithoutHash = {
1466
- orgId: params.checkpoint.orgId,
1467
- version: params.checkpoint.version,
1468
- directoryCheckpointPayload: buildUserKeyDirectoryCheckpointPayload(params.checkpoint),
1469
- signerUserKeyPairId: params.signerUserKeyPairId,
1470
- signerOrgUserId: params.signerOrgUserId,
1471
- signerFingerprint: getPublicKeyFingerprint(params.signerPublicKey),
1472
- signature: params.signature,
1473
- previousEntryHash: params.previousEntryHash ?? null
1474
- };
1475
- return {
1476
- ...entryWithoutHash,
1477
- entryHash: buildUserKeyDirectoryTransparencyEntryHash(entryWithoutHash)
1478
- };
1479
- }
1480
- function verifyUserKeyDirectoryTransparencyProof(params) {
1481
- if (params.proof.head.version !== params.currentEntry.version || params.proof.head.hash !== params.currentEntry.entryHash) {
1482
- return false;
1483
- }
1484
- if (!params.previousHead) {
1485
- if (params.proof.entries.length === 0) {
1486
- return false;
1487
- }
1488
- for (let index = 0; index < params.proof.entries.length; index++) {
1489
- const entry = params.proof.entries[index];
1490
- if (!entry) {
1491
- return false;
1492
- }
1493
- const expectedHash = buildUserKeyDirectoryTransparencyEntryHash({
1494
- orgId: entry.orgId,
1495
- version: entry.version,
1496
- directoryCheckpointPayload: entry.directoryCheckpointPayload,
1497
- signerUserKeyPairId: entry.signerUserKeyPairId,
1498
- signerOrgUserId: entry.signerOrgUserId,
1499
- signerFingerprint: entry.signerFingerprint,
1500
- signature: entry.signature,
1501
- previousEntryHash: entry.previousEntryHash
1502
- });
1503
- if (expectedHash !== entry.entryHash) {
1504
- return false;
1505
- }
1506
- if (index === 0) {
1507
- continue;
1508
- }
1509
- const previousEntry = params.proof.entries[index - 1];
1510
- if (!previousEntry) {
1511
- return false;
1512
- }
1513
- if (entry.previousEntryHash !== previousEntry.entryHash || entry.version !== previousEntry.version + 1) {
1514
- return false;
1515
- }
1516
- }
1517
- const lastEntry = params.proof.entries[params.proof.entries.length - 1];
1518
- return lastEntry?.entryHash === params.currentEntry.entryHash && lastEntry.version === params.currentEntry.version;
1519
- }
1520
- if (params.currentEntry.version < params.previousHead.version) {
1521
- return false;
1522
- }
1523
- if (params.currentEntry.version === params.previousHead.version) {
1524
- return params.currentEntry.entryHash === params.previousHead.hash && params.proof.entries.length === 0;
1525
- }
1526
- if (params.proof.entries.length === 0) {
1527
- return false;
1528
- }
1529
- let previousVersion = params.previousHead.version;
1530
- let previousHash = params.previousHead.hash;
1531
- for (const entry of params.proof.entries) {
1532
- const expectedHash = buildUserKeyDirectoryTransparencyEntryHash({
1533
- orgId: entry.orgId,
1534
- version: entry.version,
1535
- directoryCheckpointPayload: entry.directoryCheckpointPayload,
1536
- signerUserKeyPairId: entry.signerUserKeyPairId,
1537
- signerOrgUserId: entry.signerOrgUserId,
1538
- signerFingerprint: entry.signerFingerprint,
1539
- signature: entry.signature,
1540
- previousEntryHash: entry.previousEntryHash
1541
- });
1542
- if (expectedHash !== entry.entryHash || entry.previousEntryHash !== previousHash || entry.version !== previousVersion + 1) {
1543
- return false;
1544
- }
1545
- previousVersion = entry.version;
1546
- previousHash = entry.entryHash;
1547
- }
1548
- return previousHash === params.currentEntry.entryHash && previousVersion === params.currentEntry.version;
1549
- }
1550
- function buildWrappedDekSignaturePayload(vaultId, recipientKeyId, signerUserKeyPairId, dekVersion, wrappedDek) {
1551
- return [
1552
- WRAPPED_DEK_SIGNATURE_PREFIX,
1553
- vaultId,
1554
- recipientKeyId,
1555
- signerUserKeyPairId,
1556
- String(dekVersion),
1557
- getWrappedDekFingerprint(wrappedDek)
1558
- ].join(":");
1559
- }
1560
- function verifyWrappedDekSignature(vaultId, recipientKeyId, signerUserKeyPairId, dekVersion, wrappedDek, wrappedDekSignature, signerPublicKeyPem) {
1561
- const payload = buildWrappedDekSignaturePayload(
1562
- vaultId,
1563
- recipientKeyId,
1564
- signerUserKeyPairId,
1565
- dekVersion,
1566
- wrappedDek
1567
- );
1568
- try {
1569
- return crypto2.verify(
1570
- "sha256",
1571
- Buffer.from(payload, "utf8"),
1572
- {
1573
- key: signerPublicKeyPem,
1574
- ...RSA_PSS_SIGN_CONFIG
1575
- },
1576
- Buffer.from(wrappedDekSignature, "base64")
1577
- );
1578
- } catch {
1579
- return false;
1580
- }
1581
- }
1582
- function normalizeVaultSummaryCheckpoint(checkpoint) {
1583
- return {
1584
- vaultId: checkpoint.vaultId,
1585
- version: checkpoint.version,
1586
- name: checkpoint.name,
1587
- dataClassification: checkpoint.dataClassification ?? null,
1588
- currentDekVersion: checkpoint.currentDekVersion ?? null,
1589
- items: [...checkpoint.items].map((item) => ({
1590
- id: item.id,
1591
- name: item.name,
1592
- type: item.type ?? null,
1593
- websites: [...item.websites],
1594
- groupId: item.groupId ?? null
1595
- })).sort((left, right) => left.id.localeCompare(right.id)),
1596
- groups: [...checkpoint.groups].map((group) => ({
1597
- id: group.id,
1598
- name: group.name,
1599
- parentId: group.parentId ?? null
1600
- })).sort((left, right) => left.id.localeCompare(right.id))
1601
- };
1602
- }
1603
- function canonicalizeVaultSummaryCheckpoint(checkpoint) {
1604
- return JSON.stringify(normalizeVaultSummaryCheckpoint(checkpoint));
1605
- }
1606
- function buildVaultSummaryCheckpointPayload(checkpoint) {
1607
- return getCheckpointFingerprint(
1608
- VAULT_SUMMARY_CHECKPOINT_PREFIX,
1609
- canonicalizeVaultSummaryCheckpoint(checkpoint)
1610
- );
1611
- }
1612
- function verifyVaultSummaryCheckpoint(checkpoint, signature, publicKeyPem) {
1613
- try {
1614
- return crypto2.verify(
1615
- "sha256",
1616
- Buffer.from(buildVaultSummaryCheckpointPayload(checkpoint), "utf8"),
1617
- {
1618
- key: publicKeyPem,
1619
- ...RSA_PSS_SIGN_CONFIG
1620
- },
1621
- Buffer.from(signature, "base64")
1622
- );
1623
- } catch {
1624
- return false;
1625
- }
1626
- }
1627
- function normalizeVaultItemDetailCheckpoint(checkpoint) {
1628
- return {
1629
- vaultItemId: checkpoint.vaultItemId,
1630
- vaultId: checkpoint.vaultId,
1631
- version: checkpoint.version,
1632
- name: checkpoint.name,
1633
- type: checkpoint.type ?? null,
1634
- websites: [...checkpoint.websites],
1635
- groupId: checkpoint.groupId ?? null,
1636
- fields: [...checkpoint.fields].map((field) => ({
1637
- id: field.id,
1638
- name: field.name,
1639
- type: field.type,
1640
- order: field.order,
1641
- fieldInstanceIds: [...field.fieldInstanceIds].sort(),
1642
- assetIds: [...field.assetIds].sort()
1643
- })).sort((left, right) => left.order - right.order || left.id.localeCompare(right.id))
1644
- };
1645
- }
1646
- function canonicalizeVaultItemDetailCheckpoint(checkpoint) {
1647
- return JSON.stringify(normalizeVaultItemDetailCheckpoint(checkpoint));
1648
- }
1649
- function buildVaultItemDetailCheckpointPayload(checkpoint) {
1650
- return getCheckpointFingerprint(
1651
- VAULT_ITEM_DETAIL_CHECKPOINT_PREFIX,
1652
- canonicalizeVaultItemDetailCheckpoint(checkpoint)
1653
- );
1654
- }
1655
- function verifyVaultItemDetailCheckpoint(checkpoint, signature, publicKeyPem) {
1656
- try {
1657
- return crypto2.verify(
1658
- "sha256",
1659
- Buffer.from(buildVaultItemDetailCheckpointPayload(checkpoint), "utf8"),
1660
- {
1661
- key: publicKeyPem,
1662
- ...RSA_PSS_SIGN_CONFIG
1663
- },
1664
- Buffer.from(signature, "base64")
1665
- );
1666
- } catch {
1667
- return false;
1668
- }
1669
- }
1670
- function isVaultEnvelope(value) {
1671
- if (!value.startsWith("{")) {
1672
- return false;
1673
- }
1674
- try {
1675
- const parsed = JSON.parse(value);
1676
- return parsed.v === 3;
1677
- } catch {
1678
- return false;
1679
- }
1680
- }
1681
- function decryptWithVaultDEK(encryptedValue, dek) {
1682
- if (!isVaultEnvelope(encryptedValue)) {
1683
- throw new Error("Invalid encrypted value: expected v3 vault envelope format");
1684
- }
1685
- const envelope = JSON.parse(encryptedValue);
1686
- const decipher = crypto2.createDecipheriv("aes-256-gcm", dek, Buffer.from(envelope.iv, "base64"));
1687
- decipher.setAuthTag(Buffer.from(envelope.t, "base64"));
1688
- const decrypted = Buffer.concat([
1689
- decipher.update(Buffer.from(envelope.d, "base64")),
1690
- decipher.final()
1691
- ]);
1692
- return decrypted.toString("utf8");
1693
- }
1694
- function decryptStoredFieldValue(value, dek) {
1695
- return isVaultEnvelope(value) ? decryptWithVaultDEK(value, dek) : value;
1696
- }
1697
- function unwrapDEKWithPrivateKey(wrappedDek, privateKeyPem) {
1698
- return crypto2.privateDecrypt(
1699
- { key: privateKeyPem, ...RSA_OAEP_CONFIG },
1700
- Buffer.from(wrappedDek, "base64")
1701
- );
1702
- }
1703
- function loadTrustStore(trustStorePath) {
1704
- try {
1705
- const raw = fs22.readFileSync(trustStorePath, "utf8");
1706
- const parsed = JSON.parse(raw);
1707
- return {
1708
- version: 1,
1709
- userKeyPins: parsed.userKeyPins ?? {},
1710
- checkpointVersionPins: parsed.checkpointVersionPins ?? {},
1711
- transparencyHeadPins: parsed.transparencyHeadPins ?? {}
1712
- };
1713
- } catch {
1714
- return {
1715
- version: 1,
1716
- userKeyPins: {},
1717
- checkpointVersionPins: {},
1718
- transparencyHeadPins: {}
1719
- };
1720
- }
1721
- }
1722
- function saveTrustStore(trustStorePath, store) {
1723
- fs22.mkdirSync(path22.dirname(trustStorePath), { recursive: true });
1724
- fs22.writeFileSync(trustStorePath, JSON.stringify(store, null, 2) + "\n", "utf8");
1725
- }
1726
- function getPinStorageKey(orgId, orgUserId) {
1727
- return `${orgId}:${orgUserId}`;
1728
- }
1729
- function getAgentPinStorageKey(orgId, agentId) {
1730
- return `agent:${agentId}`;
1731
- }
1732
- function getDirectoryPinStorageKey(orgId) {
1733
- return `org:${orgId}`;
1734
- }
1735
- function getAgentTransparencyPinStorageKey(orgId, agentId) {
1736
- return `agent-head:${agentId}`;
1737
- }
1738
- async function fetchWitnessArtifact(witnessBaseUrl, pathName) {
1739
- const response = await fetch(
1740
- buildTransparencyWitnessUrl(witnessBaseUrl, pathName),
1741
- {
1742
- cache: "no-store"
1743
- }
1744
- );
1745
- if (!response.ok) {
1746
- throw new Error(
1747
- `Failed to fetch public transparency witness artifact (${response.status}). Production first-trust bootstrapping needs access to https://transparency.r4.dev. If this is a dev environment, re-run with --dev or set R4_DEV=1. If this is production, verify outbound access to transparency.r4.dev and retry.`
1748
- );
1749
- }
1750
- return response.json();
1751
- }
1752
- function getSinglePinnedTransparencyHead(trustStorePath) {
1753
- const store = loadTrustStore(trustStorePath);
1754
- const heads = Object.values(store.transparencyHeadPins);
1755
- return heads.length === 1 ? heads[0] : null;
1756
- }
1757
- async function getPublicOrgWitnessHead(apiBaseUrl, orgId, configuredWitnessBaseUrl) {
1758
- const witnessBaseUrl = resolveTransparencyWitnessBaseUrl({
1759
- apiBaseUrl,
1760
- configuredBaseUrl: configuredWitnessBaseUrl
1761
- });
1762
- if (!witnessBaseUrl) {
1763
- return null;
1764
- }
1765
- const artifact = await fetchWitnessArtifact(
1766
- witnessBaseUrl,
1767
- buildOrgUserKeyDirectoryWitnessPath(orgId)
1768
- );
1769
- if (artifact.kind !== "org-user-key-directory" || artifact.orgId !== orgId || !verifyOrgUserKeyDirectoryWitnessArtifact(
1770
- artifact,
1771
- TRANSPARENCY_WITNESS_ROOT_PUBLIC_KEY_PEM
1772
- )) {
1773
- throw new Error(`Public transparency witness verification failed for org ${orgId}.`);
1774
- }
1775
- return artifact.head;
1776
- }
1777
- async function getPublicAgentWitnessHead(apiBaseUrl, agentId, configuredWitnessBaseUrl) {
1778
- const witnessBaseUrl = resolveTransparencyWitnessBaseUrl({
1779
- apiBaseUrl,
1780
- configuredBaseUrl: configuredWitnessBaseUrl
1781
- });
1782
- if (!witnessBaseUrl) {
1783
- return null;
1784
- }
1785
- const artifact = await fetchWitnessArtifact(
1786
- witnessBaseUrl,
1787
- buildAgentPublicKeyWitnessPath(agentId)
1788
- );
1789
- if (artifact.kind !== "agent-public-key" || artifact.agentId !== agentId || !verifyAgentPublicKeyWitnessArtifact(
1790
- artifact,
1791
- TRANSPARENCY_WITNESS_ROOT_PUBLIC_KEY_PEM
1792
- )) {
1793
- throw new Error(`Public transparency witness verification failed for agent ${agentId}.`);
1794
- }
1795
- return artifact.head;
1796
- }
1797
- async function pinVaultUserPublicKeys(trustStorePath, orgId, publicKeys) {
1798
- const store = loadTrustStore(trustStorePath);
1799
- let changed = false;
1800
- for (const key of publicKeys) {
1801
- const storageKey = getPinStorageKey(orgId, key.orgUserId);
1802
- const computedFingerprint = getPublicKeyFingerprint(key.publicKey);
1803
- if (computedFingerprint !== key.fingerprint) {
1804
- throw new Error(`Server returned a mismatched fingerprint for user ${key.orgUserId}.`);
1805
- }
1806
- const existing = store.userKeyPins[storageKey];
1807
- const verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
1808
- if (!existing) {
1809
- store.userKeyPins[storageKey] = {
1810
- keyPairId: key.userKeyPairId,
1811
- fingerprint: key.fingerprint,
1812
- publicKey: key.publicKey,
1813
- pinnedAt: verifiedAt,
1814
- verifiedAt
1815
- };
1816
- changed = true;
1817
- continue;
1818
- }
1819
- if (existing.keyPairId === key.userKeyPairId) {
1820
- if (existing.fingerprint !== key.fingerprint || existing.publicKey !== key.publicKey) {
1821
- throw new Error(
1822
- `Pinned public key ${key.userKeyPairId} changed unexpectedly for user ${key.orgUserId}.`
1823
- );
1824
- }
1825
- if (existing.verifiedAt !== verifiedAt) {
1826
- store.userKeyPins[storageKey] = {
1827
- ...existing,
1828
- verifiedAt
1829
- };
1830
- changed = true;
1831
- }
1832
- continue;
1833
- }
1834
- if (!key.previousUserKeyPairId || key.previousUserKeyPairId !== existing.keyPairId || !key.rotationSignature) {
1835
- throw new Error(`Public key rotation for user ${key.orgUserId} is missing a trusted continuity proof.`);
1836
- }
1837
- const rotationVerified = verifyUserKeyRotation(
1838
- existing.keyPairId,
1839
- key.publicKey,
1840
- key.rotationSignature,
1841
- existing.publicKey
1842
- );
1843
- if (!rotationVerified) {
1844
- throw new Error(`Public key rotation for user ${key.orgUserId} failed signature verification.`);
1845
- }
1846
- store.userKeyPins[storageKey] = {
1847
- keyPairId: key.userKeyPairId,
1848
- fingerprint: key.fingerprint,
1849
- publicKey: key.publicKey,
1850
- pinnedAt: existing.pinnedAt,
1851
- verifiedAt
1852
- };
1853
- changed = true;
1854
- }
1855
- if (changed) {
1856
- saveTrustStore(trustStorePath, store);
1857
- }
1858
- return publicKeys;
1859
- }
1860
- async function pinVaultAgentPublicKeys(trustStorePath, orgId, publicKeys, apiBaseUrl, configuredWitnessBaseUrl) {
1861
- const store = loadTrustStore(trustStorePath);
1862
- let changed = false;
1863
- for (const key of publicKeys) {
1864
- const storageKey = getAgentPinStorageKey(orgId, key.agentId);
1865
- const computedFingerprint = getPublicKeyFingerprint(key.publicKey);
1866
- if (computedFingerprint !== key.fingerprint) {
1867
- throw new Error(`Server returned a mismatched fingerprint for agent ${key.agentId}.`);
1868
- }
1869
- if (!key.transparency) {
1870
- throw new Error(`Server omitted the agent public-key transparency proof for agent ${key.agentId}.`);
1871
- }
1872
- const currentProofEntry = key.transparency.entries[key.transparency.entries.length - 1];
1873
- if (!currentProofEntry) {
1874
- throw new Error(`Server returned an empty agent public-key transparency proof for agent ${key.agentId}.`);
1875
- }
1876
- const expectedCurrentEntry = buildAgentPublicKeyTransparencyEntry({
1877
- agentId: key.agentId,
1878
- version: currentProofEntry.version,
1879
- encryptionKeyId: key.encryptionKeyId,
1880
- publicKey: key.publicKey,
1881
- fingerprint: key.fingerprint,
1882
- previousEncryptionKeyId: key.previousEncryptionKeyId ?? null,
1883
- rotationSignature: key.rotationSignature ?? null,
1884
- previousEntryHash: currentProofEntry.previousEntryHash ?? null
1885
- });
1886
- if (expectedCurrentEntry.entryHash !== currentProofEntry.entryHash) {
1887
- throw new Error(`Agent public-key transparency entry does not match the current key for agent ${key.agentId}.`);
1888
- }
1889
- const pinnedTransparencyHead = store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)] ?? null;
1890
- const witnessHead = !pinnedTransparencyHead ? await getPublicAgentWitnessHead(apiBaseUrl, key.agentId, configuredWitnessBaseUrl) : null;
1891
- const trustedPreviousHead = witnessHead ?? pinnedTransparencyHead;
1892
- if (witnessHead && key.transparency.head.version < witnessHead.version) {
1893
- throw new Error(`Public transparency witness head is ahead of the server response for agent ${key.agentId}.`);
1894
- }
1895
- if (witnessHead && key.transparency.head.version === witnessHead.version) {
1896
- if (key.transparency.head.hash !== witnessHead.hash) {
1897
- throw new Error(`Public transparency witness head fork detected for agent ${key.agentId}.`);
1898
- }
1899
- if (currentProofEntry.entryHash !== witnessHead.hash || expectedCurrentEntry.entryHash !== witnessHead.hash) {
1900
- throw new Error(`Agent public-key transparency witness anchor mismatch for agent ${key.agentId}.`);
1901
- }
1902
- } else if (!verifyAgentPublicKeyTransparencyProof({
1903
- currentEntry: expectedCurrentEntry,
1904
- proof: key.transparency,
1905
- previousHead: trustedPreviousHead
1906
- })) {
1907
- throw new Error(`Agent public-key transparency proof verification failed for agent ${key.agentId}.`);
1908
- }
1909
- const existing = store.userKeyPins[storageKey];
1910
- const verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
1911
- if (!existing) {
1912
- store.userKeyPins[storageKey] = {
1913
- keyPairId: key.encryptionKeyId,
1914
- fingerprint: key.fingerprint,
1915
- publicKey: key.publicKey,
1916
- pinnedAt: verifiedAt,
1917
- verifiedAt
1918
- };
1919
- store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)] = key.transparency.head;
1920
- changed = true;
1921
- continue;
1922
- }
1923
- if (existing.keyPairId === key.encryptionKeyId) {
1924
- if (existing.fingerprint !== key.fingerprint || existing.publicKey !== key.publicKey) {
1925
- throw new Error(
1926
- `Pinned public key ${key.encryptionKeyId} changed unexpectedly for agent ${key.agentId}.`
1927
- );
1928
- }
1929
- if (existing.verifiedAt !== verifiedAt || store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)]?.version !== key.transparency.head.version || store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)]?.hash !== key.transparency.head.hash) {
1930
- store.userKeyPins[storageKey] = {
1931
- ...existing,
1932
- verifiedAt
1933
- };
1934
- store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)] = key.transparency.head;
1935
- changed = true;
1936
- }
1937
- continue;
1938
- }
1939
- if (!key.previousEncryptionKeyId || key.previousEncryptionKeyId !== existing.keyPairId || !key.rotationSignature) {
1940
- throw new Error(`Public key rotation for agent ${key.agentId} is missing a trusted continuity proof.`);
1941
- }
1942
- const rotationVerified = verifyUserKeyRotation(
1943
- existing.keyPairId,
1944
- key.publicKey,
1945
- key.rotationSignature,
1946
- existing.publicKey
1947
- );
1948
- if (!rotationVerified) {
1949
- throw new Error(`Public key rotation for agent ${key.agentId} failed signature verification.`);
1950
- }
1951
- store.userKeyPins[storageKey] = {
1952
- keyPairId: key.encryptionKeyId,
1953
- fingerprint: key.fingerprint,
1954
- publicKey: key.publicKey,
1955
- pinnedAt: existing.pinnedAt,
1956
- verifiedAt
1957
- };
1958
- store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)] = key.transparency.head;
1959
- changed = true;
1960
- }
1961
- if (changed) {
1962
- saveTrustStore(trustStorePath, store);
1963
- }
1964
- return publicKeys;
1965
- }
1966
- async function verifySignedUserKeyDirectory(trustStorePath, directory, anchorHead) {
1967
- if (!directory.directoryCheckpoint) {
1968
- if (directory.publicKeys.length > 0) {
1969
- throw new Error("Server omitted the user-key directory checkpoint for a non-empty vault signer directory.");
1970
- }
1971
- return null;
1972
- }
1973
- const { directoryCheckpoint } = directory;
1974
- const orgId = directoryCheckpoint.checkpoint.orgId;
1975
- if (!directoryCheckpoint.signerOrgUserId || !directoryCheckpoint.signerPublicKey) {
1976
- throw new Error("Server returned an incomplete user-key directory signer payload.");
1977
- }
1978
- const signerFingerprint = getPublicKeyFingerprint(directoryCheckpoint.signerPublicKey);
1979
- const signerEntry = directoryCheckpoint.checkpoint.entries.find(
1980
- (entry) => entry.userKeyPairId === directoryCheckpoint.signerUserKeyPairId && entry.orgUserId === directoryCheckpoint.signerOrgUserId
1981
- );
1982
- const signerKey = {
1983
- userKeyPairId: directoryCheckpoint.signerUserKeyPairId,
1984
- orgUserId: directoryCheckpoint.signerOrgUserId,
1985
- publicKey: directoryCheckpoint.signerPublicKey,
1986
- fingerprint: signerFingerprint,
1987
- previousUserKeyPairId: signerEntry?.previousUserKeyPairId ?? null,
1988
- rotationSignature: signerEntry?.rotationSignature ?? null
1989
- };
1990
- const storeBeforeVerification = loadTrustStore(trustStorePath);
1991
- try {
1992
- await pinVaultUserPublicKeys(trustStorePath, orgId, [signerKey]);
1993
- const verified = verifyUserKeyDirectoryCheckpoint(
1994
- directoryCheckpoint.checkpoint,
1995
- directoryCheckpoint.signature,
1996
- directoryCheckpoint.signerPublicKey
1997
- );
1998
- if (!verified) {
1999
- throw new Error(`User-key directory signature verification failed for org ${orgId}.`);
2000
- }
2001
- if (!directory.transparency) {
2002
- throw new Error(`Server omitted the user-key directory transparency proof for org ${orgId}.`);
2003
- }
2004
- const store = loadTrustStore(trustStorePath);
2005
- const pinnedTransparencyHead = store.transparencyHeadPins[getDirectoryPinStorageKey(orgId)] ?? null;
2006
- const trustedPreviousHead = anchorHead ?? pinnedTransparencyHead;
2007
- const legacyPinnedVersion = store.checkpointVersionPins[getDirectoryPinStorageKey(orgId)] ?? null;
2008
- if (!trustedPreviousHead && legacyPinnedVersion !== null && directory.transparency.head.version < legacyPinnedVersion) {
2009
- throw new Error(`User-key transparency head rolled back unexpectedly for org ${orgId}.`);
2010
- }
2011
- if (!trustedPreviousHead && directory.transparency.entries.length === 0) {
2012
- throw new Error(`Server omitted the current transparency entry for org ${orgId}.`);
2013
- }
2014
- if (directory.transparency.entries.length > 0) {
2015
- const currentProofEntry = directory.transparency.entries[directory.transparency.entries.length - 1];
2016
- if (!currentProofEntry) {
2017
- throw new Error(`Server returned an empty transparency proof for org ${orgId}.`);
2018
- }
2019
- const expectedCurrentEntry = buildUserKeyDirectoryTransparencyEntry({
2020
- checkpoint: directoryCheckpoint.checkpoint,
2021
- signerUserKeyPairId: directoryCheckpoint.signerUserKeyPairId,
2022
- signerOrgUserId: directoryCheckpoint.signerOrgUserId,
2023
- signerPublicKey: directoryCheckpoint.signerPublicKey,
2024
- signature: directoryCheckpoint.signature,
2025
- previousEntryHash: currentProofEntry.previousEntryHash ?? null
2026
- });
2027
- if (expectedCurrentEntry.entryHash !== currentProofEntry.entryHash) {
2028
- throw new Error(`User-key transparency entry does not match the signed directory for org ${orgId}.`);
2029
- }
2030
- if (anchorHead && directory.transparency.head.version === anchorHead.version) {
2031
- if (directory.transparency.head.hash !== anchorHead.hash) {
2032
- throw new Error(`Public transparency witness head fork detected for org ${orgId}.`);
2033
- }
2034
- if (currentProofEntry.entryHash !== anchorHead.hash || expectedCurrentEntry.entryHash !== anchorHead.hash) {
2035
- throw new Error(`User-key transparency witness anchor mismatch for org ${orgId}.`);
2036
- }
2037
- } else if (!verifyUserKeyDirectoryTransparencyProof({
2038
- currentEntry: expectedCurrentEntry,
2039
- proof: directory.transparency,
2040
- previousHead: trustedPreviousHead
2041
- })) {
2042
- throw new Error(`User-key transparency proof verification failed for org ${orgId}.`);
2043
- }
2044
- } else if (anchorHead && directory.transparency.head.version === anchorHead.version) {
2045
- throw new Error(`Server omitted the current transparency entry required to verify org ${orgId} against the public witness.`);
2046
- } else if (!trustedPreviousHead || trustedPreviousHead.version !== directory.transparency.head.version || trustedPreviousHead.hash !== directory.transparency.head.hash) {
2047
- throw new Error(`Server returned an incomplete user-key transparency proof for org ${orgId}.`);
2048
- }
2049
- assertAndPinTransparencyHead(trustStorePath, orgId, directory.transparency.head);
2050
- const checkpointEntries = new Map(
2051
- directoryCheckpoint.checkpoint.entries.map((entry) => [entry.userKeyPairId, entry])
2052
- );
2053
- for (const key of directory.publicKeys) {
2054
- const entry = checkpointEntries.get(key.userKeyPairId);
2055
- if (!entry) {
2056
- throw new Error(`User key ${key.userKeyPairId} is missing from the signed org directory.`);
2057
- }
2058
- if (entry.orgUserId !== key.orgUserId || entry.fingerprint !== key.fingerprint || entry.previousUserKeyPairId !== key.previousUserKeyPairId || entry.rotationSignature !== key.rotationSignature) {
2059
- throw new Error(`User key ${key.userKeyPairId} does not match the signed org directory.`);
2060
- }
2061
- }
2062
- return orgId;
2063
- } catch (error) {
2064
- saveTrustStore(trustStorePath, storeBeforeVerification);
2065
- throw error;
2066
- }
2067
- }
2068
- async function verifyAndPinVaultUserPublicKeys(trustStorePath, directory, anchorHead) {
2069
- const orgId = await verifySignedUserKeyDirectory(trustStorePath, directory, anchorHead);
2070
- if (!orgId) {
2071
- return directory.publicKeys;
2072
- }
2073
- return pinVaultUserPublicKeys(trustStorePath, orgId, directory.publicKeys);
2074
- }
2075
- async function verifyAndPinVaultAgentPublicKeys(trustStorePath, orgId, publicKeys, apiBaseUrl, configuredWitnessBaseUrl) {
2076
- return pinVaultAgentPublicKeys(
2077
- trustStorePath,
2078
- orgId,
2079
- publicKeys,
2080
- apiBaseUrl,
2081
- configuredWitnessBaseUrl
2082
- );
2083
- }
2084
- function assertAndPinCheckpointVersion(trustStorePath, storageKey, version) {
2085
- const store = loadTrustStore(trustStorePath);
2086
- const pinnedVersion = store.checkpointVersionPins[storageKey];
2087
- if (pinnedVersion !== void 0 && version < pinnedVersion) {
2088
- throw new Error(`Checkpoint version rolled back unexpectedly for ${storageKey}.`);
2089
- }
2090
- if (pinnedVersion === void 0 || version > pinnedVersion) {
2091
- store.checkpointVersionPins[storageKey] = version;
2092
- saveTrustStore(trustStorePath, store);
2093
- }
2094
- }
2095
- function assertAndPinTransparencyHead(trustStorePath, orgId, head) {
2096
- const store = loadTrustStore(trustStorePath);
2097
- const storageKey = getDirectoryPinStorageKey(orgId);
2098
- const pinnedHead = store.transparencyHeadPins[storageKey];
2099
- if (pinnedHead) {
2100
- if (head.version < pinnedHead.version) {
2101
- throw new Error(`User-key transparency head rolled back unexpectedly for org ${orgId}.`);
2102
- }
2103
- if (head.version === pinnedHead.version && head.hash !== pinnedHead.hash) {
2104
- throw new Error(`User-key transparency head fork detected for org ${orgId}.`);
2105
- }
2106
- }
2107
- if (!pinnedHead || head.version > pinnedHead.version) {
2108
- store.transparencyHeadPins[storageKey] = head;
2109
- saveTrustStore(trustStorePath, store);
2110
- }
2111
- assertAndPinCheckpointVersion(trustStorePath, storageKey, head.version);
2112
- }
2113
- var R4_DEFAULT_API_BASE_URL2 = "https://r4.dev";
2114
- var R4_DEV_API_BASE_URL2 = "https://dev.r4.dev";
2115
- function toScreamingSnakeCase(input) {
2116
- return input.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toUpperCase();
2117
- }
2118
- function resolveTrustStorePath2(config) {
2119
- if (config.trustStorePath) {
2120
- return path3.resolve(config.trustStorePath);
2121
- }
2122
- if (config.privateKeyPath) {
2123
- return `${path3.resolve(config.privateKeyPath)}.trust.json`;
2124
- }
2125
- return path3.resolve(process.cwd(), ".r4-trust-store.json");
2126
- }
2127
- function resolveApiBaseUrl(config) {
2128
- if (config.baseUrl) {
2129
- return config.baseUrl;
2130
- }
2131
- return config.dev ? R4_DEV_API_BASE_URL2 : R4_DEFAULT_API_BASE_URL2;
2132
- }
2133
- function buildVaultSummaryCheckpointFromListResponse(response, version) {
2134
- return {
2135
- vaultId: response.vaultId,
2136
- version,
2137
- name: response.vaultName,
2138
- dataClassification: response.dataClassification ?? null,
2139
- currentDekVersion: response.currentDekVersion ?? null,
2140
- items: response.items.map((item) => ({
2141
- id: item.id,
2142
- name: item.name,
2143
- type: item.type ?? null,
2144
- websites: item.websites ?? [],
2145
- groupId: item.groupId ?? null
2146
- })),
2147
- groups: response.vaultItemGroups.map((group) => ({
2148
- id: group.id,
2149
- name: group.name,
2150
- parentId: group.parentId ?? null
2151
- }))
2152
- };
2153
- }
2154
- function buildVaultItemDetailCheckpointFromResponse(item, version) {
2155
- return {
2156
- vaultItemId: item.id,
2157
- vaultId: item.vaultId,
2158
- version,
2159
- name: item.name,
2160
- type: item.type ?? null,
2161
- websites: item.websites ?? [],
2162
- groupId: item.groupId ?? null,
2163
- fields: item.fields.map((field, index) => ({
2164
- id: field.id,
2165
- name: field.name,
2166
- type: field.type,
2167
- order: field.order ?? index,
2168
- fieldInstanceIds: field.fieldInstanceIds ?? [],
2169
- assetIds: field.assetIds ?? []
2170
- }))
2171
- };
2172
- }
2173
- function getCheckpointSignerPublicKey(params) {
2174
- const signerType = params.checkpoint.signerType ?? "USER_KEY_PAIR";
2175
- if (signerType === "AGENT_ENCRYPTION_KEY") {
2176
- const signerKey2 = params.agentKeys.find(
2177
- (publicKey) => publicKey.encryptionKeyId === params.checkpoint.signerEncryptionKeyId
2178
- );
2179
- const historicalSignerKey = params.agentKeys.flatMap((publicKey) => publicKey.transparency?.entries ?? []).find((entry) => entry.encryptionKeyId === params.checkpoint.signerEncryptionKeyId);
2180
- if (!signerKey2 && !historicalSignerKey) {
2181
- throw new Error(
2182
- `R4 SDK: checkpoint signer ${params.checkpoint.signerEncryptionKeyId ?? "unknown"} was not present in the trusted agent key directory.`
2183
- );
2184
- }
2185
- return signerKey2?.publicKey ?? historicalSignerKey.publicKey;
2186
- }
2187
- const signerKey = params.userKeys.find(
2188
- (publicKey) => publicKey.userKeyPairId === params.checkpoint.signerUserKeyPairId
2189
- );
2190
- if (!signerKey) {
2191
- throw new Error(
2192
- `R4 SDK: checkpoint signer ${params.checkpoint.signerUserKeyPairId ?? "unknown"} was not present in the trusted user key directory.`
2193
- );
2194
- }
2195
- return signerKey.publicKey;
2196
- }
2197
- var R4 = class _R4 {
2198
- client;
2199
- baseUrl;
2200
- transparencyWitnessUrl;
2201
- projectId;
2202
- privateKeyPem;
2203
- publicKeyPem;
2204
- trustStorePath;
2205
- _env = null;
2206
- constructor(config) {
2207
- if (!config.apiKey) {
2208
- throw new Error("R4 SDK: apiKey is required");
2209
- }
2210
- if (!config.privateKey && !config.privateKeyPath) {
2211
- throw new Error(
2212
- "R4 SDK: privateKey or privateKeyPath is required for zero-trust local decryption."
2213
- );
2214
- }
2215
- const baseUrl = resolveApiBaseUrl(config);
2216
- this.baseUrl = baseUrl;
2217
- this.transparencyWitnessUrl = config.transparencyWitnessUrl;
2218
- this.client = new R4Client(config.apiKey, baseUrl);
2219
- this.projectId = config.projectId;
2220
- this.privateKeyPem = config.privateKey ?? loadPrivateKey2(config.privateKeyPath);
2221
- this.publicKeyPem = derivePublicKey2(this.privateKeyPem);
2222
- this.trustStorePath = resolveTrustStorePath2(config);
2223
- }
2224
- /**
2225
- * Static factory method that creates and initializes an R4 instance.
2226
- */
2227
- static async create(config) {
2228
- const instance = new _R4(config);
2229
- await instance.init();
2230
- return instance;
2231
- }
2232
- /**
2233
- * Initializes the SDK by registering the agent public key (idempotent) and
2234
- * decrypting all accessible vault values locally into a flat env map.
2235
- */
2236
- async init() {
2237
- this._env = await this.fetchEnv();
2238
- }
2239
- /**
2240
- * Returns the locally decrypted env map.
2241
- */
2242
- get env() {
2243
- if (!this._env) {
2244
- throw new Error(
2245
- "R4 SDK: env is not initialized. Call await r4.init() first, or use R4.create() for automatic initialization."
2246
- );
2247
- }
2248
- return this._env;
2249
- }
2250
- /**
2251
- * Re-fetches and locally re-decrypts the current vault view.
2252
- */
2253
- async refresh() {
2254
- this._env = await this.fetchEnv();
2255
- }
2256
- /**
2257
- * Sends an authenticated request to an arbitrary machine API route so
2258
- * callers can use the full headless surface without hand-rolling fetch code.
2259
- */
2260
- async requestMachine(params) {
2261
- return this.client.requestMachine(params);
2262
- }
2263
- /**
2264
- * Fetches, verifies, and locally decrypts a token provider's vault-backed
2265
- * credential fields without exposing plaintext back to R4.
2266
- */
2267
- async getTokenProviderFields(tokenProviderId, options) {
2268
- if (options?.unsafeExport !== true) {
2269
- throw new Error(
2270
- "R4 SDK: getTokenProviderFields exports decrypted provider secrets. Pass { unsafeExport: true } to confirm or use callAnthropicWithTokenProvider() instead."
2271
- );
2272
- }
2273
- await this.registerAgentPublicKey();
2274
- const runtime = await this.getVerifiedTokenProviderRuntime(tokenProviderId);
2275
- return {
2276
- id: runtime.id,
2277
- name: runtime.name,
2278
- providerType: runtime.providerType,
2279
- fields: this.decryptTokenProviderFields(runtime)
2280
- };
2281
- }
2282
- /**
2283
- * Managed Anthropic call path:
2284
- * 1. decrypt the provider secret locally
2285
- * 2. preflight the budget gate with estimated usage
2286
- * 3. call Anthropic directly
2287
- * 4. publish finalized usage back to R4
2288
- */
2289
- async callAnthropicWithTokenProvider(params) {
2290
- await this.registerAgentPublicKey();
2291
- const runtime = await this.getVerifiedTokenProviderRuntime(params.tokenProviderId);
2292
- if (runtime.providerType !== "ANTHROPIC") {
2293
- throw new Error(
2294
- `R4 SDK: token provider ${params.tokenProviderId} is ${runtime.providerType}, not ANTHROPIC.`
2295
- );
2296
- }
2297
- const fields = this.decryptTokenProviderFields(runtime);
2298
- const apiKey = fields["API Key"];
2299
- const endpoint = fields["Endpoint URL"] || "https://api.anthropic.com/v1/messages";
2300
- const model = this.requireStringField(params.body.model, "body.model");
2301
- if (!apiKey) {
2302
- throw new Error(`R4 SDK: token provider ${params.tokenProviderId} does not contain an API Key field.`);
2303
- }
2304
- const requestId = randomUUID();
2305
- const authorization = await this.client.authorizeTokenProviderUsage(params.tokenProviderId, {
2306
- model,
2307
- estimatedInputTokens: params.estimatedInputTokens ?? 0,
2308
- estimatedOutputTokens: params.estimatedOutputTokens ?? this.inferEstimatedOutputTokens(params.body.max_tokens)
2309
- });
2310
- const response = await fetch(endpoint, {
2311
- method: "POST",
2312
- headers: {
2313
- "Content-Type": "application/json",
2314
- "x-api-key": apiKey,
2315
- "anthropic-version": "2023-06-01"
2316
- },
2317
- body: JSON.stringify(params.body),
2318
- signal: params.signal
2319
- });
2320
- if (!response.ok) {
2321
- const bodyText = await response.text().catch(() => "");
2322
- throw new Error(`Anthropic API error: ${response.status} ${response.statusText}${bodyText ? ` - ${bodyText}` : ""}`);
2323
- }
2324
- const parsedResponse = await response.json();
2325
- const inputTokens = this.toNonNegativeInt(parsedResponse.usage?.input_tokens);
2326
- const outputTokens = this.toNonNegativeInt(parsedResponse.usage?.output_tokens);
2327
- const usage = await this.client.reportTokenProviderUsage(params.tokenProviderId, {
2328
- requestId,
2329
- model,
2330
- vendorRequestId: typeof parsedResponse.id === "string" ? parsedResponse.id : null,
2331
- inputTokens,
2332
- outputTokens,
2333
- totalTokens: inputTokens + outputTokens,
2334
- estimatedCostUsd: authorization.estimatedCostUsd,
2335
- metadata: {
2336
- providerType: runtime.providerType
2337
- }
2338
- });
2339
- return {
2340
- response: parsedResponse,
2341
- usage,
2342
- authorization,
2343
- requestId
2344
- };
2345
- }
2346
- /**
2347
- * Registers the local agent public key, loads all accessible vaults, verifies
2348
- * wrapped-DEK signatures against pinned signer keys, unwraps each vault DEK,
2349
- * and builds a flat SCREAMING_SNAKE_CASE env map from decrypted field values.
2350
- */
2351
- async fetchEnv() {
2352
- await this.registerAgentPublicKey();
2353
- const { vaults } = await this.client.listVaults(this.projectId);
2354
- const envEntries = await Promise.all(vaults.map((vault) => this.fetchVaultEnv(vault.id)));
2355
- return Object.assign({}, ...envEntries);
2356
- }
2357
- /**
2358
- * Ensures the local agent public key is registered before any zero-trust
2359
- * wrapped-key or token-provider flow runs.
2360
- */
2361
- async registerAgentPublicKey() {
2362
- try {
2363
- await this.client.registerAgentPublicKey({
2364
- publicKey: this.publicKeyPem
2365
- });
2366
- } catch (error) {
2367
- throw new Error(
2368
- `R4 SDK: failed to register the local agent public key. The zero-trust SDK requires an AGENT-scoped API key and a matching local private key. ${error instanceof Error ? error.message : String(error)}`
2369
- );
2370
- }
2371
- }
2372
- /**
2373
- * Fetches a single vault's wrapped DEK, verifies it against the pinned signer
2374
- * directory, unwraps the DEK locally, then decrypts every field value in that
2375
- * vault item-by-item.
2376
- */
2377
- async fetchVaultEnv(vaultId) {
2378
- const pinnedTransparencyHead = getSinglePinnedTransparencyHead(this.trustStorePath);
2379
- const [wrappedKey, itemsResponse, initialPublicKeyDirectory] = await Promise.all([
2380
- this.client.getAgentWrappedKey(vaultId),
2381
- this.client.listVaultItems(vaultId),
2382
- this.client.getVaultUserKeyDirectory(
2383
- vaultId,
2384
- pinnedTransparencyHead ? {
2385
- knownTransparencyVersion: pinnedTransparencyHead.version,
2386
- knownTransparencyHash: pinnedTransparencyHead.hash
2387
- } : void 0
2388
- )
2389
- ]);
2390
- let publicKeyDirectory = initialPublicKeyDirectory;
2391
- let witnessAnchorHead = null;
2392
- if (!pinnedTransparencyHead) {
2393
- const orgId = initialPublicKeyDirectory.directoryCheckpoint?.checkpoint.orgId ?? null;
2394
- if (orgId && initialPublicKeyDirectory.directoryCheckpoint && initialPublicKeyDirectory.transparency) {
2395
- witnessAnchorHead = await getPublicOrgWitnessHead(
2396
- this.baseUrl,
2397
- orgId,
2398
- this.transparencyWitnessUrl
2399
- );
2400
- if (witnessAnchorHead) {
2401
- if (initialPublicKeyDirectory.transparency.head.version < witnessAnchorHead.version) {
2402
- throw new Error(`R4 SDK: public transparency witness head is ahead of the server response for org ${orgId}.`);
2403
- }
2404
- if (initialPublicKeyDirectory.transparency.head.version === witnessAnchorHead.version) {
2405
- if (initialPublicKeyDirectory.transparency.head.hash !== witnessAnchorHead.hash) {
2406
- throw new Error(`R4 SDK: public transparency witness head fork detected for org ${orgId}.`);
2407
- }
2408
- } else {
2409
- publicKeyDirectory = await this.client.getVaultUserKeyDirectory(vaultId, {
2410
- knownTransparencyVersion: witnessAnchorHead.version,
2411
- knownTransparencyHash: witnessAnchorHead.hash
2412
- });
2413
- }
910
+ return new Command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").requiredOption("--domain-tenant-id <id>", "Target domain tenant ID").option("--budget-id <id>", "Existing budget ID to assign").option("--vault-id <id>", "Optional vault ID to store credentials in").option("--new-budget-name <name>", "Create and assign a new per-agent budget").option("--new-budget-description <text>", "Description for the new per-agent budget").option("--new-budget-target <amount>", "Target amount for the new per-agent budget").option(
911
+ "--security-group-id <id>",
912
+ "Security group IDs (repeat or use comma-separated values)",
913
+ collectOptionValues,
914
+ []
915
+ ).option(
916
+ "--permission <grant>",
917
+ "Machine permission grant (repeat or use comma-separated values)",
918
+ collectOptionValues,
919
+ []
920
+ ).action(
921
+ withErrorHandler(
922
+ async (options, cmd) => {
923
+ const globalOpts = cmd.optsWithGlobals();
924
+ const connection = resolveConnection(globalOpts, { requireApiKey: true });
925
+ const client = new CliClient(connection.apiKey, connection.baseUrl);
926
+ const body = buildCreateAgentBody(options);
927
+ const spinner = ora("Creating agent...").start();
928
+ const response = await client.createAgent(body);
929
+ spinner.stop();
930
+ if (globalOpts.json) {
931
+ console.log(JSON.stringify(response, null, 2));
932
+ return;
2414
933
  }
934
+ success(`Created agent "${response.name}"`);
935
+ printDetail(
936
+ [
937
+ ["ID", response.id],
938
+ ["Access Key", response.accessKey],
939
+ ["Access Secret", response.accessSecret],
940
+ ["Vault Item", response.vaultItemId]
941
+ ],
942
+ false
943
+ );
2415
944
  }
2416
- }
2417
- const trustedPublicKeys = await verifyAndPinVaultUserPublicKeys(
2418
- this.trustStorePath,
2419
- publicKeyDirectory,
2420
- witnessAnchorHead
2421
- );
2422
- const checkpointOrgId = publicKeyDirectory.directoryCheckpoint?.checkpoint.orgId ?? vaultId;
2423
- const trustedAgentPublicKeys = await verifyAndPinVaultAgentPublicKeys(
2424
- this.trustStorePath,
2425
- checkpointOrgId,
2426
- publicKeyDirectory.agentPublicKeys ?? [],
2427
- this.baseUrl,
2428
- this.transparencyWitnessUrl
2429
- );
2430
- const signerKey = trustedPublicKeys.find(
2431
- (publicKey) => publicKey.userKeyPairId === wrappedKey.signerUserKeyPairId
2432
- );
2433
- const historicalAgentSignerKey = trustedAgentPublicKeys.flatMap((publicKey) => publicKey.transparency?.entries ?? []).find((entry) => entry.encryptionKeyId === wrappedKey.signerEncryptionKeyId);
2434
- const wrappedKeySignerPublicKey = wrappedKey.signerEncryptionKeyId ? trustedAgentPublicKeys.find(
2435
- (publicKey) => publicKey.encryptionKeyId === wrappedKey.signerEncryptionKeyId
2436
- )?.publicKey ?? historicalAgentSignerKey?.publicKey : signerKey?.publicKey;
2437
- if (!wrappedKeySignerPublicKey) {
2438
- throw new Error(
2439
- `R4 SDK: wrapped DEK signer ${wrappedKey.signerEncryptionKeyId ?? wrappedKey.signerUserKeyPairId ?? "unknown"} was not present in the trusted key directory.`
2440
- );
2441
- }
2442
- const signatureVerified = verifyWrappedDekSignature(
2443
- vaultId,
2444
- wrappedKey.encryptionKeyId,
2445
- wrappedKey.signerEncryptionKeyId ?? wrappedKey.signerUserKeyPairId,
2446
- wrappedKey.dekVersion,
2447
- wrappedKey.wrappedDek,
2448
- wrappedKey.wrappedDekSignature,
2449
- wrappedKeySignerPublicKey
2450
- );
2451
- if (!signatureVerified) {
2452
- throw new Error(`R4 SDK: wrapped DEK signature verification failed for vault ${vaultId}.`);
2453
- }
2454
- const dek = unwrapDEKWithPrivateKey(wrappedKey.wrappedDek, this.privateKeyPem);
2455
- if (!itemsResponse.summaryCheckpoint) {
2456
- throw new Error(`R4 SDK: vault ${vaultId} is missing a signed summary checkpoint.`);
2457
- }
2458
- const expectedSummaryCheckpoint = buildVaultSummaryCheckpointFromListResponse(
2459
- itemsResponse,
2460
- itemsResponse.summaryCheckpoint.checkpoint.version
2461
- );
2462
- const summaryVerified = verifyVaultSummaryCheckpoint(
2463
- expectedSummaryCheckpoint,
2464
- itemsResponse.summaryCheckpoint.signature,
2465
- getCheckpointSignerPublicKey({
2466
- checkpoint: itemsResponse.summaryCheckpoint,
2467
- userKeys: trustedPublicKeys,
2468
- agentKeys: trustedAgentPublicKeys
2469
- })
2470
- );
2471
- if (!summaryVerified) {
2472
- throw new Error(`R4 SDK: vault summary checkpoint verification failed for vault ${vaultId}.`);
2473
- }
2474
- assertAndPinCheckpointVersion(
2475
- this.trustStorePath,
2476
- `summary:${vaultId}`,
2477
- expectedSummaryCheckpoint.version
2478
- );
2479
- const itemDetails = await Promise.all(
2480
- itemsResponse.items.map((item) => this.client.getVaultItemDetail(vaultId, item.id))
2481
- );
2482
- const env = {};
2483
- for (const item of itemDetails) {
2484
- if (!item.detailCheckpoint) {
2485
- throw new Error(`R4 SDK: vault item ${item.id} is missing a signed detail checkpoint.`);
2486
- }
2487
- const expectedDetailCheckpoint = buildVaultItemDetailCheckpointFromResponse(
2488
- item,
2489
- item.detailCheckpoint.checkpoint.version
2490
- );
2491
- const detailVerified = verifyVaultItemDetailCheckpoint(
2492
- expectedDetailCheckpoint,
2493
- item.detailCheckpoint.signature,
2494
- getCheckpointSignerPublicKey({
2495
- checkpoint: item.detailCheckpoint,
2496
- userKeys: trustedPublicKeys,
2497
- agentKeys: trustedAgentPublicKeys
2498
- })
2499
- );
2500
- if (!detailVerified) {
2501
- throw new Error(`R4 SDK: vault item checkpoint verification failed for item ${item.id}.`);
945
+ )
946
+ );
947
+ }
948
+
949
+ // src/commands/agent/get.ts
950
+ import { Command as Command2 } from "commander";
951
+ import chalk2 from "chalk";
952
+ import ora2 from "ora";
953
+ function getCommand() {
954
+ return new Command2("get").description("Get agent details").argument("<id>", "Agent ID").action(
955
+ withErrorHandler(async (id, _opts, cmd) => {
956
+ const globalOpts = cmd.optsWithGlobals();
957
+ const connection = resolveConnection(globalOpts, { requireApiKey: true });
958
+ const client = new CliClient(connection.apiKey, connection.baseUrl);
959
+ const spinner = ora2("Fetching agent...").start();
960
+ const agent = await client.getAgent(id);
961
+ spinner.stop();
962
+ if (globalOpts.json) {
963
+ console.log(JSON.stringify(agent, null, 2));
964
+ return;
2502
965
  }
2503
- assertAndPinCheckpointVersion(
2504
- this.trustStorePath,
2505
- `detail:${item.id}`,
2506
- expectedDetailCheckpoint.version
966
+ console.log();
967
+ console.log(chalk2.bold(` ${agent.name}`));
968
+ console.log();
969
+ printDetail(
970
+ [
971
+ ["ID", agent.id],
972
+ ["Tenant", agent.tenantName],
973
+ ["Domain", agent.domainName],
974
+ ["Domain Tenant ID", agent.domainTenantId],
975
+ ["Budget", agent.budgetId],
976
+ ["Public Key Ready", agent.isPublicKeyRegistered ? "Yes" : "No"],
977
+ ["Created At", agent.createdAt],
978
+ ["Updated At", agent.updatedAt],
979
+ ["Archived At", agent.archivedAt]
980
+ ],
981
+ false
2507
982
  );
2508
- for (const field of item.fields) {
2509
- if (field.value === null) {
2510
- continue;
2511
- }
2512
- env[toScreamingSnakeCase(`${item.name}_${field.name}`)] = decryptStoredFieldValue(
2513
- field.value,
2514
- dek
2515
- );
2516
- }
2517
- }
2518
- return env;
2519
- }
2520
- async getVerifiedTokenProviderRuntime(tokenProviderId) {
2521
- const pinnedTransparencyHead = getSinglePinnedTransparencyHead(this.trustStorePath);
2522
- let runtime = await this.client.getTokenProviderRuntime(
2523
- tokenProviderId,
2524
- pinnedTransparencyHead ? {
2525
- knownTransparencyVersion: pinnedTransparencyHead.version,
2526
- knownTransparencyHash: pinnedTransparencyHead.hash
2527
- } : void 0
2528
- );
2529
- let witnessAnchorHead = null;
2530
- if (!pinnedTransparencyHead) {
2531
- const orgId = runtime.keyDirectory.directoryCheckpoint?.checkpoint.orgId ?? null;
2532
- if (orgId && runtime.keyDirectory.directoryCheckpoint && runtime.keyDirectory.transparency) {
2533
- witnessAnchorHead = await getPublicOrgWitnessHead(
2534
- this.baseUrl,
2535
- orgId,
2536
- this.transparencyWitnessUrl
983
+ if (agent.securityGroups.length > 0) {
984
+ console.log();
985
+ console.log(chalk2.bold(" Security Groups"));
986
+ console.log();
987
+ printTable(
988
+ ["ID", "Name"],
989
+ agent.securityGroups.map((group) => [group.id, group.name]),
990
+ false
2537
991
  );
2538
- if (witnessAnchorHead) {
2539
- if (runtime.keyDirectory.transparency.head.version < witnessAnchorHead.version) {
2540
- throw new Error(
2541
- `R4 SDK: public transparency witness head is ahead of the server response for org ${orgId}.`
2542
- );
2543
- }
2544
- if (runtime.keyDirectory.transparency.head.version === witnessAnchorHead.version) {
2545
- if (runtime.keyDirectory.transparency.head.hash !== witnessAnchorHead.hash) {
2546
- throw new Error(`R4 SDK: public transparency witness head fork detected for org ${orgId}.`);
2547
- }
2548
- } else {
2549
- runtime = await this.client.getTokenProviderRuntime(tokenProviderId, {
2550
- knownTransparencyVersion: witnessAnchorHead.version,
2551
- knownTransparencyHash: witnessAnchorHead.hash
2552
- });
2553
- }
2554
- }
2555
992
  }
2556
- }
2557
- const trustedPublicKeys = await verifyAndPinVaultUserPublicKeys(
2558
- this.trustStorePath,
2559
- runtime.keyDirectory,
2560
- witnessAnchorHead
2561
- );
2562
- const checkpointOrgId = runtime.keyDirectory.directoryCheckpoint?.checkpoint.orgId ?? runtime.vaultId;
2563
- const trustedAgentPublicKeys = await verifyAndPinVaultAgentPublicKeys(
2564
- this.trustStorePath,
2565
- checkpointOrgId,
2566
- runtime.keyDirectory.agentPublicKeys ?? [],
2567
- this.baseUrl,
2568
- this.transparencyWitnessUrl
2569
- );
2570
- const wrappedKeySignerPublicKey = this.getWrappedKeySignerPublicKey(
2571
- runtime.wrappedKey,
2572
- trustedPublicKeys,
2573
- trustedAgentPublicKeys
2574
- );
2575
- if (!verifyWrappedDekSignature(
2576
- runtime.vaultId,
2577
- runtime.wrappedKey.encryptionKeyId,
2578
- runtime.wrappedKey.signerEncryptionKeyId ?? runtime.wrappedKey.signerUserKeyPairId,
2579
- runtime.wrappedKey.dekVersion,
2580
- runtime.wrappedKey.wrappedDek,
2581
- runtime.wrappedKey.wrappedDekSignature,
2582
- wrappedKeySignerPublicKey
2583
- )) {
2584
- throw new Error(`R4 SDK: wrapped DEK signature verification failed for vault ${runtime.vaultId}.`);
2585
- }
2586
- const expectedDetailCheckpoint = buildVaultItemDetailCheckpointFromResponse(
2587
- runtime.vaultItemDetail,
2588
- runtime.vaultItemDetail.detailCheckpoint?.checkpoint.version ?? 0
2589
- );
2590
- if (!runtime.vaultItemDetail.detailCheckpoint) {
2591
- throw new Error(`R4 SDK: vault item ${runtime.vaultItemDetail.id} is missing a signed detail checkpoint.`);
2592
- }
2593
- const detailVerified = verifyVaultItemDetailCheckpoint(
2594
- expectedDetailCheckpoint,
2595
- runtime.vaultItemDetail.detailCheckpoint.signature,
2596
- getCheckpointSignerPublicKey({
2597
- checkpoint: runtime.vaultItemDetail.detailCheckpoint,
2598
- userKeys: trustedPublicKeys,
2599
- agentKeys: trustedAgentPublicKeys
2600
- })
2601
- );
2602
- if (!detailVerified) {
2603
- throw new Error(
2604
- `R4 SDK: vault item checkpoint verification failed for item ${runtime.vaultItemDetail.id}.`
2605
- );
2606
- }
2607
- assertAndPinCheckpointVersion(
2608
- this.trustStorePath,
2609
- `detail:${runtime.vaultItemDetail.id}`,
2610
- expectedDetailCheckpoint.version
2611
- );
2612
- return runtime;
2613
- }
2614
- decryptTokenProviderFields(runtime) {
2615
- const dek = unwrapDEKWithPrivateKey(runtime.wrappedKey.wrappedDek, this.privateKeyPem);
2616
- const fields = {};
2617
- for (const field of runtime.vaultItemDetail.fields) {
2618
- if (field.value === null) {
2619
- continue;
993
+ console.log();
994
+ })
995
+ );
996
+ }
997
+
998
+ // src/commands/agent/get-tenant-roles.ts
999
+ import { Command as Command3 } from "commander";
1000
+ import ora3 from "ora";
1001
+ function getTenantRolesCommand() {
1002
+ return new Command3("get-tenant-roles").description("Get the tenant roles assigned to an agent").argument("<id>", "Agent ID").action(
1003
+ withErrorHandler(async (id, _opts, cmd) => {
1004
+ const globalOpts = cmd.optsWithGlobals();
1005
+ const connection = resolveConnection(globalOpts, { requireApiKey: true });
1006
+ const client = new CliClient(connection.apiKey, connection.baseUrl);
1007
+ const spinner = ora3("Fetching agent tenant roles...").start();
1008
+ const response = await client.getAgentTenantRoles(id);
1009
+ spinner.stop();
1010
+ if (globalOpts.json) {
1011
+ console.log(JSON.stringify(response, null, 2));
1012
+ return;
2620
1013
  }
2621
- fields[field.name] = decryptStoredFieldValue(field.value, dek);
2622
- }
2623
- return fields;
2624
- }
2625
- getWrappedKeySignerPublicKey(wrappedKey, userKeys, agentKeys) {
2626
- const signerKey = userKeys.find(
2627
- (publicKey) => publicKey.userKeyPairId === wrappedKey.signerUserKeyPairId
2628
- );
2629
- const historicalAgentSignerKey = agentKeys.flatMap((publicKey) => publicKey.transparency?.entries ?? []).find((entry) => entry.encryptionKeyId === wrappedKey.signerEncryptionKeyId);
2630
- const wrappedKeySignerPublicKey = wrappedKey.signerEncryptionKeyId ? agentKeys.find(
2631
- (publicKey) => publicKey.encryptionKeyId === wrappedKey.signerEncryptionKeyId
2632
- )?.publicKey ?? historicalAgentSignerKey?.publicKey : signerKey?.publicKey;
2633
- if (!wrappedKeySignerPublicKey) {
2634
- throw new Error(
2635
- `R4 SDK: wrapped DEK signer ${wrappedKey.signerEncryptionKeyId ?? wrappedKey.signerUserKeyPairId ?? "unknown"} was not present in the trusted key directory.`
1014
+ console.log();
1015
+ printDetail(
1016
+ [
1017
+ ["Agent ID", id],
1018
+ ["Explicit Roles", response.roles.join(", ") || "(none)"],
1019
+ ["Inherited Roles", response.inheritedRoles.join(", ") || "(none)"]
1020
+ ],
1021
+ false
2636
1022
  );
2637
- }
2638
- return wrappedKeySignerPublicKey;
2639
- }
2640
- requireStringField(value, label) {
2641
- if (typeof value !== "string" || value.length === 0) {
2642
- throw new Error(`R4 SDK: ${label} must be a non-empty string.`);
2643
- }
2644
- return value;
2645
- }
2646
- inferEstimatedOutputTokens(value) {
2647
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
2648
- return Math.floor(value);
2649
- }
2650
- return 0;
2651
- }
2652
- toNonNegativeInt(value) {
2653
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
2654
- return Math.floor(value);
2655
- }
2656
- return 0;
2657
- }
2658
- };
2659
- var src_default = R4;
1023
+ console.log();
1024
+ })
1025
+ );
1026
+ }
1027
+
1028
+ // src/commands/agent/init.ts
1029
+ import { Command as Command5 } from "commander";
1030
+ import ora5 from "ora";
1031
+
1032
+ // src/commands/doctor.ts
1033
+ import { Command as Command4 } from "commander";
1034
+ import chalk3 from "chalk";
1035
+ import ora4 from "ora";
2660
1036
 
2661
1037
  // src/lib/doctor.ts
1038
+ import R4 from "@r4-sdk/node";
2662
1039
  function getAgentIdFromRegistration(registration) {
2663
1040
  const entryCount = registration?.transparency?.entries.length ?? 0;
2664
1041
  if (!registration?.transparency || entryCount === 0) {
@@ -2932,7 +1309,7 @@ async function runDoctorChecks(connection) {
2932
1309
  return report;
2933
1310
  }
2934
1311
  try {
2935
- const r4 = await src_default.create({
1312
+ const r4 = await R4.create({
2936
1313
  apiKey: connection.apiKey,
2937
1314
  baseUrl: connection.baseUrl,
2938
1315
  dev: connection.dev,
@@ -3048,8 +1425,8 @@ function doctorCommand(commandName = "doctor", description = "Verify CLI auth, r
3048
1425
  }
3049
1426
 
3050
1427
  // src/lib/credentials-file.ts
3051
- import fs6 from "node:fs";
3052
- import path5 from "node:path";
1428
+ import fs5 from "fs";
1429
+ import path3 from "path";
3053
1430
  function normalizeFieldName(fieldName) {
3054
1431
  return fieldName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
3055
1432
  }
@@ -3255,9 +1632,9 @@ function parsePlainTextContent(content) {
3255
1632
  return {};
3256
1633
  }
3257
1634
  function parseCredentialsFile(credentialsFilePath) {
3258
- const resolvedPath = path5.resolve(credentialsFilePath);
3259
- const content = fs6.readFileSync(resolvedPath, "utf8");
3260
- const extension = path5.extname(resolvedPath).toLowerCase();
1635
+ const resolvedPath = path3.resolve(credentialsFilePath);
1636
+ const content = fs5.readFileSync(resolvedPath, "utf8");
1637
+ const extension = path3.extname(resolvedPath).toLowerCase();
3261
1638
  if (extension === ".json") {
3262
1639
  return parseJsonContent(content);
3263
1640
  }
@@ -3380,8 +1757,8 @@ function cacheProfileIdentity(profileName, identity) {
3380
1757
  }
3381
1758
 
3382
1759
  // src/lib/prompt.ts
3383
- import readline from "node:readline";
3384
- import { Writable } from "node:stream";
1760
+ import readline from "readline";
1761
+ import { Writable } from "stream";
3385
1762
  var MutableOutput = class extends Writable {
3386
1763
  muted = false;
3387
1764
  _write(chunk, _encoding, callback) {
@@ -3860,14 +2237,14 @@ function logoutCommand() {
3860
2237
  import { Command as Command11 } from "commander";
3861
2238
 
3862
2239
  // src/lib/public-auth-client.ts
3863
- import crypto3 from "node:crypto";
2240
+ import crypto2 from "crypto";
3864
2241
  var PublicAuthClient = class {
3865
2242
  baseUrl;
3866
2243
  constructor(baseUrl) {
3867
2244
  this.baseUrl = baseUrl.replace(/\/$/, "");
3868
2245
  }
3869
- async request(path7, body) {
3870
- const response = await fetch(`${this.baseUrl}${path7}`, {
2246
+ async request(path6, body) {
2247
+ const response = await fetch(`${this.baseUrl}${path6}`, {
3871
2248
  method: "POST",
3872
2249
  headers: {
3873
2250
  "Content-Type": "application/json",
@@ -3893,7 +2270,7 @@ var PublicAuthClient = class {
3893
2270
  }
3894
2271
  };
3895
2272
  function solveAgentChallenge(nonce) {
3896
- return crypto3.createHash("sha256").update(nonce).digest("hex");
2273
+ return crypto2.createHash("sha256").update(nonce).digest("hex");
3897
2274
  }
3898
2275
 
3899
2276
  // src/lib/register-agent.ts
@@ -4273,8 +2650,8 @@ import { Command as Command16 } from "commander";
4273
2650
  import ora10 from "ora";
4274
2651
 
4275
2652
  // src/lib/json-input.ts
4276
- import fs7 from "node:fs";
4277
- import path6 from "node:path";
2653
+ import fs6 from "fs";
2654
+ import path4 from "path";
4278
2655
  function parseJsonInput(params) {
4279
2656
  const bodyFlagName = params.bodyFlagName ?? "--body";
4280
2657
  const bodyFileFlagName = params.bodyFileFlagName ?? "--body-file";
@@ -4284,7 +2661,7 @@ function parseJsonInput(params) {
4284
2661
  if (!params.body && !params.bodyFile) {
4285
2662
  return void 0;
4286
2663
  }
4287
- const rawInput = params.bodyFile ? fs7.readFileSync(path6.resolve(params.bodyFile), "utf8") : params.body;
2664
+ const rawInput = params.bodyFile ? fs6.readFileSync(path4.resolve(params.bodyFile), "utf8") : params.body;
4288
2665
  const sourceLabel = params.bodyFile ? `${bodyFileFlagName} ${params.bodyFile}` : bodyFlagName;
4289
2666
  try {
4290
2667
  return JSON.parse(rawInput);
@@ -4385,7 +2762,7 @@ function registerBudgetCommands(program2) {
4385
2762
 
4386
2763
  // src/commands/configure.ts
4387
2764
  import { Command as Command18 } from "commander";
4388
- import fs8 from "node:fs";
2765
+ import fs7 from "fs";
4389
2766
  async function promptRuntimeTarget(existingProfile) {
4390
2767
  const defaultTarget = existingProfile.baseUrl ? "custom" : existingProfile.dev ? "dev" : "prod";
4391
2768
  const runtimeTarget = await promptChoice(
@@ -4418,7 +2795,7 @@ async function promptRuntimeTarget(existingProfile) {
4418
2795
  }
4419
2796
  async function resolvePrivateKeyMaterial(profileName, existingProfile) {
4420
2797
  const managedPrivateKeyPath = getDefaultPrivateKeyPath(profileName);
4421
- const defaultMode = existingProfile.privateKeyPath || fs8.existsSync(managedPrivateKeyPath) ? "existing" : "generate";
2798
+ const defaultMode = existingProfile.privateKeyPath || fs7.existsSync(managedPrivateKeyPath) ? "existing" : "generate";
4422
2799
  const privateKeyMode = await promptChoice(
4423
2800
  "How should this profile handle its local private key?",
4424
2801
  [
@@ -4721,13 +3098,13 @@ function normalizeMethod(method) {
4721
3098
  function requestCommand() {
4722
3099
  return new Command23("request").description("Call an arbitrary machine API route").argument("<method>", "HTTP method (GET, POST, PUT, PATCH, DELETE)").argument("<path>", "Machine path like /webhook or /api/v1/machine/webhook").option("--body <json>", "Optional JSON request body").option("--body-file <path>", "Read the JSON request body from a file").action(
4723
3100
  withErrorHandler(
4724
- async (method, path7, options, cmd) => {
3101
+ async (method, path6, options, cmd) => {
4725
3102
  const globalOpts = cmd.optsWithGlobals();
4726
3103
  const config = resolveAuth(globalOpts);
4727
3104
  const client = new CliClient(config.apiKey, config.baseUrl);
4728
3105
  const result = await client.requestMachine({
4729
3106
  method: normalizeMethod(method),
4730
- path: path7,
3107
+ path: path6,
4731
3108
  body: parseJsonInput({
4732
3109
  body: options.body,
4733
3110
  bodyFile: options.bodyFile
@@ -5032,17 +3409,64 @@ function createItemCommand() {
5032
3409
  );
5033
3410
  }
5034
3411
 
5035
- // src/commands/vault/list.ts
3412
+ // src/commands/vault/download-asset.ts
3413
+ import path5 from "path";
5036
3414
  import { Command as Command32 } from "commander";
5037
3415
  import ora21 from "ora";
5038
- import R42 from "@r4-sdk/sdk";
3416
+ import R42 from "@r4-sdk/node";
3417
+ function downloadAssetCommand() {
3418
+ return new Command32("download-asset").description("Download and locally decrypt a vault attachment by vault ID and asset ID").argument("<vaultId>", "Vault ID that owns the attachment").argument("<assetId>", "Attachment asset ID to download").option(
3419
+ "--output <path>",
3420
+ "Destination file path (defaults to ./${assetId}.bin when omitted)"
3421
+ ).action(
3422
+ withErrorHandler(
3423
+ async (vaultId, assetId, opts, cmd) => {
3424
+ const globalOpts = cmd.optsWithGlobals();
3425
+ const config = resolveAuth(globalOpts);
3426
+ const sdk = new R42(config);
3427
+ const outputPath = path5.resolve(opts.output ?? `${assetId}.bin`);
3428
+ const spinner = ora21("Downloading and decrypting attachment...").start();
3429
+ const result = await sdk.downloadVaultAttachment({
3430
+ vaultId,
3431
+ assetId,
3432
+ outputPath
3433
+ });
3434
+ spinner.stop();
3435
+ if (globalOpts.json) {
3436
+ console.log(
3437
+ JSON.stringify(
3438
+ {
3439
+ assetId: result.assetId,
3440
+ vaultId: result.vaultId,
3441
+ outputPath: result.outputPath,
3442
+ plaintextSize: result.checkpoint.plaintextSize,
3443
+ plaintextSha256: result.checkpoint.plaintextSha256,
3444
+ ciphertextSize: result.checkpoint.ciphertextSize,
3445
+ ciphertextSha256: result.checkpoint.ciphertextSha256
3446
+ },
3447
+ null,
3448
+ 2
3449
+ )
3450
+ );
3451
+ return;
3452
+ }
3453
+ success(`Saved decrypted attachment to ${outputPath}`);
3454
+ }
3455
+ )
3456
+ );
3457
+ }
3458
+
3459
+ // src/commands/vault/list.ts
3460
+ import { Command as Command33 } from "commander";
3461
+ import ora22 from "ora";
3462
+ import R43 from "@r4-sdk/node";
5039
3463
  function listCommand5() {
5040
- return new Command32("list").description("List all locally decrypted environment variables").action(
3464
+ return new Command33("list").description("List all locally decrypted environment variables").action(
5041
3465
  withErrorHandler(async (_opts, cmd) => {
5042
3466
  const globalOpts = cmd.optsWithGlobals();
5043
3467
  const config = resolveAuth(globalOpts);
5044
- const spinner = ora21("Fetching environment variables...").start();
5045
- const r4 = await R42.create(config);
3468
+ const spinner = ora22("Fetching environment variables...").start();
3469
+ const r4 = await R43.create(config);
5046
3470
  spinner.stop();
5047
3471
  const env = r4.env;
5048
3472
  const keys = Object.keys(env).sort();
@@ -5063,12 +3487,13 @@ function listCommand5() {
5063
3487
  }
5064
3488
 
5065
3489
  // src/commands/vault/list-items.ts
5066
- import { Command as Command34 } from "commander";
3490
+ import { Command as Command35 } from "commander";
5067
3491
 
5068
3492
  // src/commands/vault/items.ts
5069
- import { Command as Command33 } from "commander";
5070
- import ora22 from "ora";
5071
- import R43 from "@r4-sdk/sdk";
3493
+ import { Command as Command34 } from "commander";
3494
+ import ora23 from "ora";
3495
+ import R44 from "@r4-sdk/node";
3496
+ var DIRECT_ITEM_SHARE_VAULT_LABEL = "[Direct Item Share]";
5072
3497
  function deriveItems(env) {
5073
3498
  const keys = Object.keys(env).sort();
5074
3499
  return keys.map((key) => ({
@@ -5079,15 +3504,18 @@ function deriveItems(env) {
5079
3504
  async function loadVaultMetadataRows(globalOpts) {
5080
3505
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5081
3506
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5082
- const { vaults } = await client.listVaults(connection.projectId);
5083
- const rows = [];
3507
+ const [{ vaults }, sharedItemsResponse] = await Promise.all([
3508
+ client.listVaults(connection.projectId),
3509
+ connection.projectId ? Promise.resolve(null) : client.getSharedVaultItems()
3510
+ ]);
3511
+ const rowsByItemId = /* @__PURE__ */ new Map();
5084
3512
  for (const vault of vaults) {
5085
3513
  const response = await client.listVaultItems(vault.id);
5086
3514
  const groupNames = Object.fromEntries(
5087
3515
  response.vaultItemGroups.map((group) => [group.id, group.name])
5088
3516
  );
5089
3517
  for (const item of response.items) {
5090
- rows.push({
3518
+ rowsByItemId.set(item.id, {
5091
3519
  vaultId: vault.id,
5092
3520
  vaultName: vault.name,
5093
3521
  itemId: item.id,
@@ -5099,13 +3527,33 @@ async function loadVaultMetadataRows(globalOpts) {
5099
3527
  });
5100
3528
  }
5101
3529
  }
5102
- return rows.sort((left, right) => {
3530
+ if (sharedItemsResponse) {
3531
+ const groupNames = Object.fromEntries(
3532
+ sharedItemsResponse.vaultItemGroups.map((group) => [group.id, group.name])
3533
+ );
3534
+ for (const item of sharedItemsResponse.vaultItems) {
3535
+ if (rowsByItemId.has(item.id)) {
3536
+ continue;
3537
+ }
3538
+ rowsByItemId.set(item.id, {
3539
+ vaultId: item.vaultId,
3540
+ vaultName: DIRECT_ITEM_SHARE_VAULT_LABEL,
3541
+ itemId: item.id,
3542
+ itemName: item.name,
3543
+ type: item.type,
3544
+ fieldCount: item.fieldCount ?? null,
3545
+ groupName: item.groupId ? groupNames[item.groupId] ?? item.groupId : null,
3546
+ websites: item.websites
3547
+ });
3548
+ }
3549
+ }
3550
+ return [...rowsByItemId.values()].sort((left, right) => {
5103
3551
  const vaultCompare = left.vaultName.localeCompare(right.vaultName);
5104
3552
  return vaultCompare !== 0 ? vaultCompare : left.itemName.localeCompare(right.itemName);
5105
3553
  });
5106
3554
  }
5107
3555
  async function renderVaultMetadataRows(globalOpts) {
5108
- const spinner = ora22("Fetching vault item metadata...").start();
3556
+ const spinner = ora23("Fetching vault item metadata...").start();
5109
3557
  const rows = await loadVaultMetadataRows(globalOpts);
5110
3558
  spinner.stop();
5111
3559
  if (globalOpts.json) {
@@ -5123,7 +3571,7 @@ async function renderVaultMetadataRows(globalOpts) {
5123
3571
  row.vaultName,
5124
3572
  row.itemName,
5125
3573
  row.type || "-",
5126
- String(row.fieldCount),
3574
+ row.fieldCount === null ? "-" : String(row.fieldCount),
5127
3575
  row.groupName || "-",
5128
3576
  row.websites.join(", ") || "-"
5129
3577
  ]),
@@ -5132,7 +3580,7 @@ async function renderVaultMetadataRows(globalOpts) {
5132
3580
  console.log();
5133
3581
  }
5134
3582
  function itemsCommand() {
5135
- return new Command33("items").description("List vault items from the decrypted env map, or use --metadata-only for raw machine metadata").option("--metadata-only", "List item metadata without local decryption").action(
3583
+ return new Command34("items").description("List vault items from the decrypted env map, or use --metadata-only for raw machine metadata").option("--metadata-only", "List item metadata without local decryption").action(
5136
3584
  withErrorHandler(async (opts, cmd) => {
5137
3585
  const globalOpts = cmd.optsWithGlobals();
5138
3586
  if (opts.metadataOnly) {
@@ -5140,8 +3588,8 @@ function itemsCommand() {
5140
3588
  return;
5141
3589
  }
5142
3590
  const config = resolveAuth(globalOpts);
5143
- const spinner = ora22("Fetching vault items...").start();
5144
- const r4 = await R43.create(config);
3591
+ const spinner = ora23("Fetching vault items...").start();
3592
+ const r4 = await R44.create(config);
5145
3593
  spinner.stop();
5146
3594
  const env = r4.env;
5147
3595
  const items = deriveItems(env);
@@ -5167,7 +3615,7 @@ function itemsCommand() {
5167
3615
 
5168
3616
  // src/commands/vault/list-items.ts
5169
3617
  function listItemsCommand() {
5170
- return new Command34("list-items").description("List vault item metadata only, without requiring local decryption").action(
3618
+ return new Command35("list-items").description("List vault item metadata only, without requiring local decryption").action(
5171
3619
  withErrorHandler(async (_opts, cmd) => {
5172
3620
  const globalOpts = cmd.optsWithGlobals();
5173
3621
  await renderVaultMetadataRows(globalOpts);
@@ -5176,15 +3624,15 @@ function listItemsCommand() {
5176
3624
  }
5177
3625
 
5178
3626
  // src/commands/vault/list-vaults.ts
5179
- import { Command as Command35 } from "commander";
5180
- import ora23 from "ora";
3627
+ import { Command as Command36 } from "commander";
3628
+ import ora24 from "ora";
5181
3629
  function listVaultsCommand() {
5182
- return new Command35("list-vaults").description("List visible vaults without requiring local decryption").action(
3630
+ return new Command36("list-vaults").description("List visible vaults without requiring local decryption").action(
5183
3631
  withErrorHandler(async (_opts, cmd) => {
5184
3632
  const globalOpts = cmd.optsWithGlobals();
5185
3633
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5186
3634
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5187
- const spinner = ora23("Fetching visible vaults...").start();
3635
+ const spinner = ora24("Fetching visible vaults...").start();
5188
3636
  const response = await client.listVaults(connection.projectId);
5189
3637
  spinner.stop();
5190
3638
  if (globalOpts.json) {
@@ -5213,17 +3661,17 @@ function listVaultsCommand() {
5213
3661
  }
5214
3662
 
5215
3663
  // src/commands/vault/get.ts
5216
- import { Command as Command36 } from "commander";
5217
- import ora24 from "ora";
5218
- import R44 from "@r4-sdk/sdk";
3664
+ import { Command as Command37 } from "commander";
3665
+ import ora25 from "ora";
3666
+ import R45 from "@r4-sdk/node";
5219
3667
  function getCommand2() {
5220
- return new Command36("get").description("Get a specific locally decrypted environment variable value").argument("<key>", "Environment variable key (SCREAMING_SNAKE_CASE)").action(
3668
+ return new Command37("get").description("Get a specific locally decrypted environment variable value").argument("<key>", "Environment variable key (SCREAMING_SNAKE_CASE)").action(
5221
3669
  withErrorHandler(
5222
3670
  async (keyArg, _opts, cmd) => {
5223
3671
  const globalOpts = cmd.optsWithGlobals();
5224
3672
  const config = resolveAuth(globalOpts);
5225
- const spinner = ora24("Fetching environment variables...").start();
5226
- const r4 = await R44.create(config);
3673
+ const spinner = ora25("Fetching environment variables...").start();
3674
+ const r4 = await R45.create(config);
5227
3675
  spinner.stop();
5228
3676
  const env = r4.env;
5229
3677
  const key = keyArg.toUpperCase();
@@ -5243,17 +3691,17 @@ function getCommand2() {
5243
3691
  }
5244
3692
 
5245
3693
  // src/commands/vault/search.ts
5246
- import { Command as Command37 } from "commander";
5247
- import ora25 from "ora";
5248
- import R45 from "@r4-sdk/sdk";
3694
+ import { Command as Command38 } from "commander";
3695
+ import ora26 from "ora";
3696
+ import R46 from "@r4-sdk/node";
5249
3697
  function searchCommand() {
5250
- return new Command37("search").description("Search vault items by name").argument("<query>", "Search query (case-insensitive match against key names)").action(
3698
+ return new Command38("search").description("Search vault items by name").argument("<query>", "Search query (case-insensitive match against key names)").action(
5251
3699
  withErrorHandler(
5252
3700
  async (query, _opts, cmd) => {
5253
3701
  const globalOpts = cmd.optsWithGlobals();
5254
3702
  const config = resolveAuth(globalOpts);
5255
- const spinner = ora25("Searching vault items...").start();
5256
- const r4 = await R45.create(config);
3703
+ const spinner = ora26("Searching vault items...").start();
3704
+ const r4 = await R46.create(config);
5257
3705
  spinner.stop();
5258
3706
  const env = r4.env;
5259
3707
  const lowerQuery = query.toLowerCase();
@@ -5283,6 +3731,7 @@ function registerVaultCommands(program2) {
5283
3731
  const vault = program2.command("vault").description("Manage vault secrets");
5284
3732
  vault.addCommand(createCommand3());
5285
3733
  vault.addCommand(createItemCommand());
3734
+ vault.addCommand(downloadAssetCommand());
5286
3735
  vault.addCommand(listCommand5());
5287
3736
  vault.addCommand(listVaultsCommand());
5288
3737
  vault.addCommand(listItemsCommand());
@@ -5292,16 +3741,16 @@ function registerVaultCommands(program2) {
5292
3741
  }
5293
3742
 
5294
3743
  // src/commands/project/add-vault.ts
5295
- import { Command as Command38 } from "commander";
5296
- import ora26 from "ora";
3744
+ import { Command as Command39 } from "commander";
3745
+ import ora27 from "ora";
5297
3746
  function addVaultCommand() {
5298
- return new Command38("add-vault").description("Associate a vault with a project").argument("<projectId>", "Project ID").argument("<vaultId>", "Vault ID").action(
3747
+ return new Command39("add-vault").description("Associate a vault with a project").argument("<projectId>", "Project ID").argument("<vaultId>", "Vault ID").action(
5299
3748
  withErrorHandler(
5300
3749
  async (projectId, vaultId, _opts, cmd) => {
5301
3750
  const globalOpts = cmd.optsWithGlobals();
5302
3751
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5303
3752
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5304
- const spinner = ora26("Associating vault with project...").start();
3753
+ const spinner = ora27("Associating vault with project...").start();
5305
3754
  await client.associateProjectVault({
5306
3755
  projectId,
5307
3756
  vaultId
@@ -5318,15 +3767,15 @@ function addVaultCommand() {
5318
3767
  }
5319
3768
 
5320
3769
  // src/commands/project/list.ts
5321
- import { Command as Command39 } from "commander";
5322
- import ora27 from "ora";
3770
+ import { Command as Command40 } from "commander";
3771
+ import ora28 from "ora";
5323
3772
  function listCommand6() {
5324
- return new Command39("list").description("List all projects").action(
3773
+ return new Command40("list").description("List all projects").action(
5325
3774
  withErrorHandler(async (_opts, cmd) => {
5326
3775
  const globalOpts = cmd.optsWithGlobals();
5327
3776
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5328
3777
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5329
- const spinner = ora27("Fetching projects...").start();
3778
+ const spinner = ora28("Fetching projects...").start();
5330
3779
  const response = await client.listProjects();
5331
3780
  spinner.stop();
5332
3781
  const rows = response.projects.map((p) => [
@@ -5350,16 +3799,16 @@ function listCommand6() {
5350
3799
  }
5351
3800
 
5352
3801
  // src/commands/project/get.ts
5353
- import { Command as Command40 } from "commander";
3802
+ import { Command as Command41 } from "commander";
5354
3803
  import chalk6 from "chalk";
5355
- import ora28 from "ora";
3804
+ import ora29 from "ora";
5356
3805
  function getCommand3() {
5357
- return new Command40("get").description("Get project details").argument("<id>", "Project ID").action(
3806
+ return new Command41("get").description("Get project details").argument("<id>", "Project ID").action(
5358
3807
  withErrorHandler(async (id, _opts, cmd) => {
5359
3808
  const globalOpts = cmd.optsWithGlobals();
5360
3809
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5361
3810
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5362
- const spinner = ora28("Fetching project...").start();
3811
+ const spinner = ora29("Fetching project...").start();
5363
3812
  const project = await client.getProject(id);
5364
3813
  spinner.stop();
5365
3814
  if (globalOpts.json) {
@@ -5417,9 +3866,9 @@ function getCommand3() {
5417
3866
  }
5418
3867
 
5419
3868
  // src/commands/project/create.ts
5420
- import { Command as Command41 } from "commander";
5421
- import readline2 from "node:readline";
5422
- import ora29 from "ora";
3869
+ import { Command as Command42 } from "commander";
3870
+ import readline2 from "readline";
3871
+ import ora30 from "ora";
5423
3872
  function prompt(question) {
5424
3873
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
5425
3874
  return new Promise((resolve) => {
@@ -5430,7 +3879,7 @@ function prompt(question) {
5430
3879
  });
5431
3880
  }
5432
3881
  function createCommand4() {
5433
- return new Command41("create").description("Create a new project").option("--name <name>", "Project name").option("--description <description>", "Project description").option("--external-id <externalId>", "External identifier").action(
3882
+ return new Command42("create").description("Create a new project").option("--name <name>", "Project name").option("--description <description>", "Project description").option("--external-id <externalId>", "External identifier").action(
5434
3883
  withErrorHandler(async (opts, cmd) => {
5435
3884
  const globalOpts = cmd.optsWithGlobals();
5436
3885
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
@@ -5442,7 +3891,7 @@ function createCommand4() {
5442
3891
  if (!name) {
5443
3892
  throw new Error("Project name is required.");
5444
3893
  }
5445
- const spinner = ora29("Creating project...").start();
3894
+ const spinner = ora30("Creating project...").start();
5446
3895
  const response = await client.createProject({
5447
3896
  name,
5448
3897
  description: opts.description,
@@ -5459,10 +3908,10 @@ function createCommand4() {
5459
3908
  }
5460
3909
 
5461
3910
  // src/commands/project/set-agents.ts
5462
- import { Command as Command42 } from "commander";
5463
- import ora30 from "ora";
3911
+ import { Command as Command43 } from "commander";
3912
+ import ora31 from "ora";
5464
3913
  function setAgentsCommand() {
5465
- return new Command42("set-agents").description("Replace the explicit agent membership for a project").argument("<projectId>", "Project ID").option(
3914
+ return new Command43("set-agents").description("Replace the explicit agent membership for a project").argument("<projectId>", "Project ID").option(
5466
3915
  "--agent-id <id>",
5467
3916
  "Agent ID to keep on the project (repeat or use comma-separated values)",
5468
3917
  collectOptionValues,
@@ -5473,7 +3922,7 @@ function setAgentsCommand() {
5473
3922
  const globalOpts = cmd.optsWithGlobals();
5474
3923
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5475
3924
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5476
- const spinner = ora30("Updating project agents...").start();
3925
+ const spinner = ora31("Updating project agents...").start();
5477
3926
  await client.updateProjectAgents(projectId, {
5478
3927
  agentIds: options.agentId
5479
3928
  });
@@ -5499,17 +3948,17 @@ function registerProjectCommands(program2) {
5499
3948
  }
5500
3949
 
5501
3950
  // src/commands/run/index.ts
5502
- import { spawn } from "node:child_process";
5503
- import ora31 from "ora";
5504
- import R46 from "@r4-sdk/sdk";
3951
+ import { spawn } from "child_process";
3952
+ import ora32 from "ora";
3953
+ import R47 from "@r4-sdk/node";
5505
3954
  function registerRunCommand(program2) {
5506
3955
  program2.command("run").description("Run a command with vault secrets injected as environment variables").argument("<command...>", "Command and arguments to execute").option("--prefix <prefix>", "Add prefix to all injected env var names").action(
5507
3956
  withErrorHandler(
5508
3957
  async (commandParts, opts, cmd) => {
5509
3958
  const globalOpts = cmd.optsWithGlobals();
5510
3959
  const config = resolveAuth(globalOpts);
5511
- const spinner = ora31("Loading vault secrets...").start();
5512
- const r4 = await R46.create(config);
3960
+ const spinner = ora32("Loading vault secrets...").start();
3961
+ const r4 = await R47.create(config);
5513
3962
  spinner.stop();
5514
3963
  const env = r4.env;
5515
3964
  const secretEnv = {};
@@ -5535,10 +3984,10 @@ function registerRunCommand(program2) {
5535
3984
  }
5536
3985
 
5537
3986
  // src/commands/security-group/create.ts
5538
- import { Command as Command43 } from "commander";
5539
- import ora32 from "ora";
3987
+ import { Command as Command44 } from "commander";
3988
+ import ora33 from "ora";
5540
3989
  function createCommand5() {
5541
- return new Command43("create").description("Create a tenant security group").requiredOption("--name <name>", "Security group name").requiredOption("--tenant-id <id>", "Tenant ID").option("--parent-id <id>", "Optional parent security group ID").option(
3990
+ return new Command44("create").description("Create a tenant security group").requiredOption("--name <name>", "Security group name").requiredOption("--tenant-id <id>", "Tenant ID").option("--parent-id <id>", "Optional parent security group ID").option(
5542
3991
  "--role <role>",
5543
3992
  "Tenant role to grant through this group (repeat or use comma-separated values)",
5544
3993
  collectOptionValues,
@@ -5549,7 +3998,7 @@ function createCommand5() {
5549
3998
  const globalOpts = cmd.optsWithGlobals();
5550
3999
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5551
4000
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5552
- const spinner = ora32("Creating security group...").start();
4001
+ const spinner = ora33("Creating security group...").start();
5553
4002
  await client.createSecurityGroup({
5554
4003
  name: options.name,
5555
4004
  parentId: options.parentId ?? null,
@@ -5585,8 +4034,8 @@ function registerSecurityGroupCommands(program2) {
5585
4034
  }
5586
4035
 
5587
4036
  // src/index.ts
5588
- var program = new Command44();
5589
- program.name("r4").description("R4 CLI \u2014 manage vaults, projects, and secrets from the terminal").version("1.0.0").option("--api-key <key>", "API key (overrides R4_API_KEY env var and config file)").option("--profile <name>", "CLI profile name (overrides R4_PROFILE and the saved current profile)").option("--project-id <id>", "Optional project ID filter (overrides R4_PROJECT_ID env var and config file)").option("--dev", "Use https://dev.r4.dev unless an explicit base URL override is set").option("--base-url <url>", "API base URL (default: https://r4.dev)").option("--private-key-path <path>", "Path to the agent private key PEM (overrides R4_PRIVATE_KEY_PATH env var and config file)").option("--trust-store-path <path>", "Path to the local signer trust-store JSON (overrides R4_TRUST_STORE_PATH env var and config file)").option("--json", "Output as JSON for scripting and piping", false);
4037
+ var program = new Command45();
4038
+ program.name("r4").description("R4 CLI \u2014 manage vaults, projects, and secrets from the terminal").version("1.0.2").option("--api-key <key>", "API key (overrides R4_API_KEY env var and config file)").option("--profile <name>", "CLI profile name (overrides R4_PROFILE and the saved current profile)").option("--project-id <id>", "Optional project ID filter (overrides R4_PROJECT_ID env var and config file)").option("--dev", "Use https://dev.r4.dev unless an explicit base URL override is set").option("--base-url <url>", "API base URL (default: https://r4.dev)").option("--private-key-path <path>", "Path to the agent private key PEM (overrides R4_PRIVATE_KEY_PATH env var and config file)").option("--trust-store-path <path>", "Path to the local signer trust-store JSON (overrides R4_TRUST_STORE_PATH env var and config file)").option("--json", "Output as JSON for scripting and piping", false);
5590
4039
  registerAgentCommands(program);
5591
4040
  registerAuthCommands(program);
5592
4041
  registerBillingCommands(program);