@r4-sdk/cli 1.0.1 → 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(
@@ -307,14 +342,14 @@ import fs2 from "fs";
307
342
 
308
343
  // src/lib/profile-paths.ts
309
344
  import fs from "fs";
310
- import os from "os";
345
+ import os2 from "os";
311
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");
@@ -890,1792 +925,117 @@ function createCommand() {
890
925
  const client = new CliClient(connection.apiKey, connection.baseUrl);
891
926
  const body = buildCreateAgentBody(options);
892
927
  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 "crypto";
1004
- import path3 from "path";
1005
- import crypto2 from "crypto";
1006
- import fs5 from "fs";
1007
- import path4 from "path";
1008
- import fs22 from "fs";
1009
- import path22 from "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 nestedError = typeof errorBody.error === "object" && errorBody.error !== null ? errorBody.error : null;
1041
- const topLevelMessage = Array.isArray(errorBody.message) ? errorBody.message.filter((item) => typeof item === "string").join("; ") : typeof errorBody.message === "string" ? errorBody.message : null;
1042
- const errorMessage = typeof nestedError?.message === "string" ? nestedError.message : topLevelMessage || (typeof errorBody.error === "string" ? errorBody.error : null) || `HTTP ${response.status}: ${response.statusText}`;
1043
- const errorCode = typeof nestedError?.code === "string" ? ` [${nestedError.code}]` : "";
1044
- throw new Error(`R4 API Error${errorCode}: ${errorMessage}`);
1045
- }
1046
- if (response.status === 204) {
1047
- return void 0;
1048
- }
1049
- const responseText = await response.text();
1050
- if (!responseText) {
1051
- return void 0;
1052
- }
1053
- try {
1054
- return JSON.parse(responseText);
1055
- } catch {
1056
- return responseText;
1057
- }
1058
- }
1059
- /**
1060
- * Calls any existing machine API route through the authenticated client.
1061
- * Accepts either `/api/v1/machine/...` or the shorter `/...` machine path.
1062
- */
1063
- async requestMachine(params) {
1064
- return this.request(this.normalizeMachinePath(params.path), {
1065
- method: params.method,
1066
- body: params.body === void 0 ? void 0 : JSON.stringify(params.body)
1067
- });
1068
- }
1069
- /**
1070
- * Registers or re-confirms the agent runtime's local RSA public key.
1071
- */
1072
- async registerAgentPublicKey(body) {
1073
- return this.request("/api/v1/machine/vault/public-key", {
1074
- method: "POST",
1075
- body: JSON.stringify(body)
1076
- });
1077
- }
1078
- /**
1079
- * Returns the current machine principal, session context, and resolved policy.
1080
- */
1081
- async getMachineIdentity() {
1082
- return this.request("/api/v1/machine/me", {
1083
- method: "GET"
1084
- });
1085
- }
1086
- /**
1087
- * Lists all accessible non-hidden vaults. When `projectId` is provided, the
1088
- * backend additionally filters to vaults associated with that project.
1089
- */
1090
- async listVaults(projectId) {
1091
- const search = projectId ? `?projectId=${encodeURIComponent(projectId)}` : "";
1092
- return this.request(`/api/v1/machine/vault${search}`, {
1093
- method: "GET"
1094
- });
1095
- }
1096
- /**
1097
- * Retrieves the active wrapped DEK for the authenticated agent on a vault.
1098
- */
1099
- async getAgentWrappedKey(vaultId) {
1100
- return this.request(
1101
- `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/wrapped-key`,
1102
- { method: "GET" }
1103
- );
1104
- }
1105
- /**
1106
- * Retrieves the trusted user-key directory for a vault so the runtime can
1107
- * verify wrapped-DEK signatures locally.
1108
- */
1109
- async getVaultUserKeyDirectory(vaultId, params) {
1110
- const searchParams = new URLSearchParams();
1111
- if (params?.knownTransparencyVersion !== void 0) {
1112
- searchParams.set("knownTransparencyVersion", String(params.knownTransparencyVersion));
1113
- }
1114
- if (params?.knownTransparencyHash) {
1115
- searchParams.set("knownTransparencyHash", params.knownTransparencyHash);
1116
- }
1117
- const search = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
1118
- return this.request(
1119
- `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/public-keys${search}`,
1120
- { method: "GET" }
1121
- );
1122
- }
1123
- /**
1124
- * Lists all items in a vault with lightweight metadata.
1125
- */
1126
- async listVaultItems(vaultId) {
1127
- return this.request(
1128
- `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/items`,
1129
- { method: "GET" }
1130
- );
1131
- }
1132
- /**
1133
- * Retrieves the full field payloads for a vault item.
1134
- */
1135
- async getVaultItemDetail(vaultId, itemId) {
1136
- return this.request(
1137
- `/api/v1/machine/vault/${encodeURIComponent(vaultId)}/items/${encodeURIComponent(itemId)}`,
1138
- { method: "GET" }
1139
- );
1140
- }
1141
- /**
1142
- * Retrieves the provider-scoped runtime bundle needed for local decryption.
1143
- */
1144
- async getTokenProviderRuntime(tokenProviderId, params) {
1145
- const searchParams = new URLSearchParams();
1146
- if (params?.knownTransparencyVersion !== void 0) {
1147
- searchParams.set("knownTransparencyVersion", String(params.knownTransparencyVersion));
1148
- }
1149
- if (params?.knownTransparencyHash) {
1150
- searchParams.set("knownTransparencyHash", params.knownTransparencyHash);
1151
- }
1152
- const search = searchParams.size > 0 ? `?${searchParams.toString()}` : "";
1153
- return this.request(
1154
- `/api/v1/machine/intelligence-provider/${encodeURIComponent(tokenProviderId)}/runtime${search}`,
1155
- { method: "GET" }
1156
- );
1157
- }
1158
- /**
1159
- * Runs the managed budget gate before a token-provider call is sent to the vendor.
1160
- */
1161
- async authorizeTokenProviderUsage(tokenProviderId, body) {
1162
- return this.request(
1163
- `/api/v1/machine/intelligence-provider/${encodeURIComponent(tokenProviderId)}/authorize-usage`,
1164
- {
1165
- method: "POST",
1166
- body: JSON.stringify(body)
1167
- }
1168
- );
1169
- }
1170
- /**
1171
- * Publishes finalized token usage for a completed managed provider call.
1172
- */
1173
- async reportTokenProviderUsage(tokenProviderId, body) {
1174
- return this.request(
1175
- `/api/v1/machine/intelligence-provider/${encodeURIComponent(tokenProviderId)}/report-usage`,
1176
- {
1177
- method: "POST",
1178
- body: JSON.stringify(body)
1179
- }
1180
- );
1181
- }
1182
- };
1183
- var TRANSPARENCY_WITNESS_PAYLOAD_PREFIX = "r4-transparency-witness-v1";
1184
- var DEFAULT_TRANSPARENCY_WITNESS_URL = "https://transparency-prod.r4.dev";
1185
- var DEV_TRANSPARENCY_WITNESS_URL = "https://transparency-dev.r4.dev";
1186
- var TRANSPARENCY_WITNESS_ROOT_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1187
- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA18JhILFiS/BOWR9laubW
1188
- g2vepQy26BXAlnrscZZVQUzBBaCM4hWobpt3Nh77vxP0gqVAJXP1hVhPPwxGQnOF
1189
- 4Qg/RK4iEETjMdmh3KMqFX9MeE9tP4cTOGtsgWsedNpu6TvMT+2vu+0ltmr7p4Xv
1190
- H0ID48Q8JLeNksc/RekrsfzQ9DVtXFS7z1FF2VQgzamdJsW9hGMiM7Q+0iXei7PW
1191
- 3PsLd1aNtqJ3lIj3t12qFiJiYyKF0hEq0//Abgb9SgDv/WOlRG1Ianf1/fnP2jer
1192
- ZYiZSylXqQdun0Db2d0+FDm/znV2AGAmBEXm6qnCogEHu77LoLyCyJOlB9WNtRwh
1193
- KnbzTmE2Mw/43jxvCcR7pE5kik/tdeMvqGFZfg3ozUG9eM0q0TURH6g9b9J4sBnR
1194
- dxz2PbF4cl/AeL4ANPmLz3kUQaDA6wR0veVk5jV+Uqr55TYz/zEbY1rtJbmnc53Q
1195
- ihPS6xtSiexrqnOgqm/AVbiRhxjPqfg3/VJM3zR5Blnu02AqVR9kCT0WkyEWRz5X
1196
- 6HU8DEocJIPz8UwBMKQ7rnjMPv/Fjpuav/EIad5vOdfxCZkjyTYoQg8vLUyfXvgD
1197
- mBWFgKIN8GTRyM+LjZIgznjN58dZ8ZvsGd14oKnH7WgAh9FVh8ri7gNmsdJeRTn/
1198
- 2zDkTlx+FQxAxqFaYV7qCvcCAwEAAQ==
1199
- -----END PUBLIC KEY-----`;
1200
- var buildOrgUserKeyDirectoryWitnessPayload = (orgId, head) => [
1201
- TRANSPARENCY_WITNESS_PAYLOAD_PREFIX,
1202
- "org-user-key-directory",
1203
- orgId,
1204
- String(head.version),
1205
- head.hash
1206
- ].join(":");
1207
- var buildAgentPublicKeyWitnessPayload = (agentId, head) => [
1208
- TRANSPARENCY_WITNESS_PAYLOAD_PREFIX,
1209
- "agent-public-key",
1210
- agentId,
1211
- String(head.version),
1212
- head.hash
1213
- ].join(":");
1214
- var buildOrgUserKeyDirectoryWitnessPath = (orgId) => `v1/orgs/${orgId}/user-key-directory-head.json`;
1215
- var buildAgentPublicKeyWitnessPath = (agentId) => `v1/agents/${agentId}/public-key-head.json`;
1216
- var buildTransparencyWitnessUrl = (baseUrl, path42) => `${baseUrl.replace(/\/+$/, "")}/${path42.replace(/^\/+/, "")}`;
1217
- function parseHostname(apiBaseUrl) {
1218
- const trimmed = apiBaseUrl.trim();
1219
- if (!trimmed) {
1220
- return null;
1221
- }
1222
- try {
1223
- const normalized = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
1224
- return new URL(normalized).hostname.toLowerCase();
1225
- } catch {
1226
- return null;
1227
- }
1228
- }
1229
- var resolveDefaultTransparencyWitnessBaseUrl = (apiBaseUrl) => {
1230
- const hostname = parseHostname(apiBaseUrl);
1231
- if (hostname === "r4.dev" || hostname === "api.r4.dev") {
1232
- return DEFAULT_TRANSPARENCY_WITNESS_URL;
1233
- }
1234
- if (hostname === "dev.r4.dev") {
1235
- return DEV_TRANSPARENCY_WITNESS_URL;
1236
- }
1237
- return null;
1238
- };
1239
- var resolveTransparencyWitnessBaseUrl = (params) => {
1240
- const configuredBaseUrl = params.configuredBaseUrl?.trim();
1241
- if (configuredBaseUrl) {
1242
- return configuredBaseUrl;
1243
- }
1244
- return resolveDefaultTransparencyWitnessBaseUrl(params.apiBaseUrl);
1245
- };
1246
- var RSA_OAEP_CONFIG = {
1247
- padding: crypto2.constants.RSA_PKCS1_OAEP_PADDING,
1248
- oaepHash: "sha256"
1249
- };
1250
- var RSA_PSS_SIGN_CONFIG = {
1251
- padding: crypto2.constants.RSA_PKCS1_PSS_PADDING,
1252
- saltLength: 32
1253
- };
1254
- var USER_KEY_ROTATION_PREFIX = "r4-user-key-rotation-v1";
1255
- var USER_KEY_DIRECTORY_CHECKPOINT_PREFIX = "r4-user-key-directory-checkpoint-v1";
1256
- var USER_KEY_DIRECTORY_TRANSPARENCY_ENTRY_PREFIX = "r4-user-key-directory-transparency-entry-v1";
1257
- var AGENT_PUBLIC_KEY_TRANSPARENCY_ENTRY_PREFIX = "r4-agent-public-key-transparency-entry-v1";
1258
- var WRAPPED_DEK_SIGNATURE_PREFIX = "r4-wrapped-dek-signature-v1";
1259
- var VAULT_SUMMARY_CHECKPOINT_PREFIX = "r4-vault-summary-checkpoint-v1";
1260
- var VAULT_ITEM_DETAIL_CHECKPOINT_PREFIX = "r4-vault-item-detail-checkpoint-v1";
1261
- function pemToDer(pem, beginLabel, endLabel) {
1262
- const derBase64 = pem.replace(beginLabel, "").replace(endLabel, "").replace(/\s/g, "");
1263
- return Buffer.from(derBase64, "base64");
1264
- }
1265
- function getWrappedDekFingerprint(wrappedDek) {
1266
- return crypto2.createHash("sha256").update(Buffer.from(wrappedDek, "base64")).digest("hex");
1267
- }
1268
- function getCheckpointFingerprint(prefix, canonicalJson) {
1269
- return `${prefix}:${crypto2.createHash("sha256").update(canonicalJson, "utf8").digest("hex")}`;
1270
- }
1271
- function loadPrivateKey2(privateKeyPath) {
1272
- return fs5.readFileSync(path4.resolve(privateKeyPath), "utf8").trim();
1273
- }
1274
- function derivePublicKey2(privateKeyPem) {
1275
- return crypto2.createPublicKey(privateKeyPem).export({
1276
- type: "spki",
1277
- format: "pem"
1278
- }).toString();
1279
- }
1280
- function getPublicKeyFingerprint(publicKeyPem) {
1281
- const derBytes = pemToDer(publicKeyPem, "-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----");
1282
- return crypto2.createHash("sha256").update(derBytes).digest("hex");
1283
- }
1284
- function buildUserKeyRotationPayload(previousUserKeyPairId, newPublicKeyFingerprint) {
1285
- return `${USER_KEY_ROTATION_PREFIX}:${previousUserKeyPairId}:${newPublicKeyFingerprint}`;
1286
- }
1287
- function verifyUserKeyRotation(previousUserKeyPairId, newPublicKeyPem, rotationSignature, previousPublicKeyPem) {
1288
- const payload = buildUserKeyRotationPayload(
1289
- previousUserKeyPairId,
1290
- getPublicKeyFingerprint(newPublicKeyPem)
1291
- );
1292
- try {
1293
- return crypto2.verify(
1294
- "sha256",
1295
- Buffer.from(payload, "utf8"),
1296
- {
1297
- key: previousPublicKeyPem,
1298
- ...RSA_PSS_SIGN_CONFIG
1299
- },
1300
- Buffer.from(rotationSignature, "base64")
1301
- );
1302
- } catch {
1303
- return false;
1304
- }
1305
- }
1306
- function verifyTransparencyWitnessPayload(payload, signature, publicKeyPem) {
1307
- try {
1308
- return crypto2.verify(
1309
- "sha256",
1310
- Buffer.from(payload, "utf8"),
1311
- {
1312
- key: publicKeyPem,
1313
- ...RSA_PSS_SIGN_CONFIG
1314
- },
1315
- Buffer.from(signature, "base64")
1316
- );
1317
- } catch {
1318
- return false;
1319
- }
1320
- }
1321
- function verifyOrgUserKeyDirectoryWitnessArtifact(artifact, publicKeyPem) {
1322
- return verifyTransparencyWitnessPayload(
1323
- buildOrgUserKeyDirectoryWitnessPayload(artifact.orgId, artifact.head),
1324
- artifact.signature,
1325
- publicKeyPem
1326
- );
1327
- }
1328
- function verifyAgentPublicKeyWitnessArtifact(artifact, publicKeyPem) {
1329
- return verifyTransparencyWitnessPayload(
1330
- buildAgentPublicKeyWitnessPayload(artifact.agentId, artifact.head),
1331
- artifact.signature,
1332
- publicKeyPem
1333
- );
1334
- }
1335
- function buildAgentPublicKeyTransparencyEntryHash(entry) {
1336
- return getCheckpointFingerprint(
1337
- AGENT_PUBLIC_KEY_TRANSPARENCY_ENTRY_PREFIX,
1338
- JSON.stringify({
1339
- agentId: entry.agentId,
1340
- version: entry.version,
1341
- encryptionKeyId: entry.encryptionKeyId,
1342
- publicKey: entry.publicKey,
1343
- fingerprint: entry.fingerprint,
1344
- previousEncryptionKeyId: entry.previousEncryptionKeyId ?? null,
1345
- rotationSignature: entry.rotationSignature ?? null,
1346
- previousEntryHash: entry.previousEntryHash ?? null
1347
- })
1348
- );
1349
- }
1350
- function buildAgentPublicKeyTransparencyEntry(params) {
1351
- const normalized = {
1352
- agentId: params.agentId,
1353
- version: params.version,
1354
- encryptionKeyId: params.encryptionKeyId,
1355
- publicKey: params.publicKey,
1356
- fingerprint: params.fingerprint ?? getPublicKeyFingerprint(params.publicKey),
1357
- previousEncryptionKeyId: params.previousEncryptionKeyId ?? null,
1358
- rotationSignature: params.rotationSignature ?? null,
1359
- previousEntryHash: params.previousEntryHash ?? null
1360
- };
1361
- return {
1362
- ...normalized,
1363
- entryHash: buildAgentPublicKeyTransparencyEntryHash(normalized)
1364
- };
1365
- }
1366
- function verifyAgentPublicKeyTransparencyChain(entries) {
1367
- let previousVersion = null;
1368
- let previousHash = null;
1369
- for (const entry of entries) {
1370
- if (buildAgentPublicKeyTransparencyEntryHash(entry) !== entry.entryHash) {
1371
- return false;
1372
- }
1373
- if (entry.previousEntryHash !== previousHash) {
1374
- return false;
1375
- }
1376
- if (previousVersion !== null && entry.version !== previousVersion + 1) {
1377
- return false;
1378
- }
1379
- previousVersion = entry.version;
1380
- previousHash = entry.entryHash;
1381
- }
1382
- return true;
1383
- }
1384
- function buildAgentPublicKeyTransparencyHead(entries) {
1385
- const lastEntry = entries[entries.length - 1];
1386
- if (!lastEntry) {
1387
- return null;
1388
- }
1389
- return {
1390
- version: lastEntry.version,
1391
- hash: lastEntry.entryHash
1392
- };
1393
- }
1394
- function verifyAgentPublicKeyTransparencyProof(params) {
1395
- const currentEntryHead = {
1396
- version: params.currentEntry.version,
1397
- hash: params.currentEntry.entryHash
1398
- };
1399
- if (params.proof.head.version !== currentEntryHead.version || params.proof.head.hash !== currentEntryHead.hash) {
1400
- return false;
1401
- }
1402
- if (!verifyAgentPublicKeyTransparencyChain(params.proof.entries)) {
1403
- return false;
1404
- }
1405
- const proofHead = buildAgentPublicKeyTransparencyHead(params.proof.entries);
1406
- if (!proofHead || proofHead.version !== currentEntryHead.version || proofHead.hash !== currentEntryHead.hash) {
1407
- return false;
1408
- }
1409
- if (!params.previousHead) {
1410
- return params.proof.entries.length > 0;
1411
- }
1412
- if (params.currentEntry.version < params.previousHead.version) {
1413
- return false;
1414
- }
1415
- const previousEntry = params.proof.entries.find((entry) => entry.version === params.previousHead.version);
1416
- if (params.currentEntry.version === params.previousHead.version) {
1417
- return previousEntry?.entryHash === params.previousHead.hash;
1418
- }
1419
- return previousEntry?.entryHash === params.previousHead.hash;
1420
- }
1421
- function normalizeUserKeyDirectoryCheckpoint(checkpoint) {
1422
- return {
1423
- orgId: checkpoint.orgId,
1424
- version: checkpoint.version,
1425
- entries: [...checkpoint.entries].map((entry) => ({
1426
- userKeyPairId: entry.userKeyPairId,
1427
- orgUserId: entry.orgUserId,
1428
- fingerprint: entry.fingerprint,
1429
- previousUserKeyPairId: entry.previousUserKeyPairId ?? null,
1430
- rotationSignature: entry.rotationSignature ?? null
1431
- })).sort((left, right) => left.userKeyPairId.localeCompare(right.userKeyPairId))
1432
- };
1433
- }
1434
- function canonicalizeUserKeyDirectoryCheckpoint(checkpoint) {
1435
- return JSON.stringify(normalizeUserKeyDirectoryCheckpoint(checkpoint));
1436
- }
1437
- function buildUserKeyDirectoryCheckpointPayload(checkpoint) {
1438
- return getCheckpointFingerprint(
1439
- USER_KEY_DIRECTORY_CHECKPOINT_PREFIX,
1440
- canonicalizeUserKeyDirectoryCheckpoint(checkpoint)
1441
- );
1442
- }
1443
- function verifyUserKeyDirectoryCheckpoint(checkpoint, signature, publicKeyPem) {
1444
- try {
1445
- return crypto2.verify(
1446
- "sha256",
1447
- Buffer.from(buildUserKeyDirectoryCheckpointPayload(checkpoint), "utf8"),
1448
- {
1449
- key: publicKeyPem,
1450
- ...RSA_PSS_SIGN_CONFIG
1451
- },
1452
- Buffer.from(signature, "base64")
1453
- );
1454
- } catch {
1455
- return false;
1456
- }
1457
- }
1458
- function buildUserKeyDirectoryTransparencyEntryHash(entry) {
1459
- return getCheckpointFingerprint(
1460
- USER_KEY_DIRECTORY_TRANSPARENCY_ENTRY_PREFIX,
1461
- JSON.stringify({
1462
- orgId: entry.orgId,
1463
- version: entry.version,
1464
- directoryCheckpointPayload: entry.directoryCheckpointPayload,
1465
- signerUserKeyPairId: entry.signerUserKeyPairId,
1466
- signerOrgUserId: entry.signerOrgUserId,
1467
- signerFingerprint: entry.signerFingerprint,
1468
- signature: entry.signature,
1469
- previousEntryHash: entry.previousEntryHash ?? null
1470
- })
1471
- );
1472
- }
1473
- function buildUserKeyDirectoryTransparencyEntry(params) {
1474
- const entryWithoutHash = {
1475
- orgId: params.checkpoint.orgId,
1476
- version: params.checkpoint.version,
1477
- directoryCheckpointPayload: buildUserKeyDirectoryCheckpointPayload(params.checkpoint),
1478
- signerUserKeyPairId: params.signerUserKeyPairId,
1479
- signerOrgUserId: params.signerOrgUserId,
1480
- signerFingerprint: getPublicKeyFingerprint(params.signerPublicKey),
1481
- signature: params.signature,
1482
- previousEntryHash: params.previousEntryHash ?? null
1483
- };
1484
- return {
1485
- ...entryWithoutHash,
1486
- entryHash: buildUserKeyDirectoryTransparencyEntryHash(entryWithoutHash)
1487
- };
1488
- }
1489
- function verifyUserKeyDirectoryTransparencyProof(params) {
1490
- if (params.proof.head.version !== params.currentEntry.version || params.proof.head.hash !== params.currentEntry.entryHash) {
1491
- return false;
1492
- }
1493
- if (!params.previousHead) {
1494
- if (params.proof.entries.length === 0) {
1495
- return false;
1496
- }
1497
- for (let index = 0; index < params.proof.entries.length; index++) {
1498
- const entry = params.proof.entries[index];
1499
- if (!entry) {
1500
- return false;
1501
- }
1502
- const expectedHash = buildUserKeyDirectoryTransparencyEntryHash({
1503
- orgId: entry.orgId,
1504
- version: entry.version,
1505
- directoryCheckpointPayload: entry.directoryCheckpointPayload,
1506
- signerUserKeyPairId: entry.signerUserKeyPairId,
1507
- signerOrgUserId: entry.signerOrgUserId,
1508
- signerFingerprint: entry.signerFingerprint,
1509
- signature: entry.signature,
1510
- previousEntryHash: entry.previousEntryHash
1511
- });
1512
- if (expectedHash !== entry.entryHash) {
1513
- return false;
1514
- }
1515
- if (index === 0) {
1516
- continue;
1517
- }
1518
- const previousEntry = params.proof.entries[index - 1];
1519
- if (!previousEntry) {
1520
- return false;
1521
- }
1522
- if (entry.previousEntryHash !== previousEntry.entryHash || entry.version !== previousEntry.version + 1) {
1523
- return false;
1524
- }
1525
- }
1526
- const lastEntry = params.proof.entries[params.proof.entries.length - 1];
1527
- return lastEntry?.entryHash === params.currentEntry.entryHash && lastEntry.version === params.currentEntry.version;
1528
- }
1529
- if (params.currentEntry.version < params.previousHead.version) {
1530
- return false;
1531
- }
1532
- if (params.currentEntry.version === params.previousHead.version) {
1533
- if (params.currentEntry.entryHash !== params.previousHead.hash) {
1534
- return false;
1535
- }
1536
- if (params.proof.entries.length === 0) {
1537
- return true;
1538
- }
1539
- return params.proof.entries.length === 1 && params.proof.entries[0]?.entryHash === params.currentEntry.entryHash && params.proof.entries[0]?.version === params.currentEntry.version;
1540
- }
1541
- if (params.proof.entries.length === 0) {
1542
- return false;
1543
- }
1544
- let previousVersion = params.previousHead.version;
1545
- let previousHash = params.previousHead.hash;
1546
- for (const entry of params.proof.entries) {
1547
- const expectedHash = buildUserKeyDirectoryTransparencyEntryHash({
1548
- orgId: entry.orgId,
1549
- version: entry.version,
1550
- directoryCheckpointPayload: entry.directoryCheckpointPayload,
1551
- signerUserKeyPairId: entry.signerUserKeyPairId,
1552
- signerOrgUserId: entry.signerOrgUserId,
1553
- signerFingerprint: entry.signerFingerprint,
1554
- signature: entry.signature,
1555
- previousEntryHash: entry.previousEntryHash
1556
- });
1557
- if (expectedHash !== entry.entryHash || entry.previousEntryHash !== previousHash || entry.version !== previousVersion + 1) {
1558
- return false;
1559
- }
1560
- previousVersion = entry.version;
1561
- previousHash = entry.entryHash;
1562
- }
1563
- return previousHash === params.currentEntry.entryHash && previousVersion === params.currentEntry.version;
1564
- }
1565
- function buildWrappedDekSignaturePayload(vaultId, recipientKeyId, signerUserKeyPairId, dekVersion, wrappedDek) {
1566
- return [
1567
- WRAPPED_DEK_SIGNATURE_PREFIX,
1568
- vaultId,
1569
- recipientKeyId,
1570
- signerUserKeyPairId,
1571
- String(dekVersion),
1572
- getWrappedDekFingerprint(wrappedDek)
1573
- ].join(":");
1574
- }
1575
- function verifyWrappedDekSignature(vaultId, recipientKeyId, signerUserKeyPairId, dekVersion, wrappedDek, wrappedDekSignature, signerPublicKeyPem) {
1576
- const payload = buildWrappedDekSignaturePayload(
1577
- vaultId,
1578
- recipientKeyId,
1579
- signerUserKeyPairId,
1580
- dekVersion,
1581
- wrappedDek
1582
- );
1583
- try {
1584
- return crypto2.verify(
1585
- "sha256",
1586
- Buffer.from(payload, "utf8"),
1587
- {
1588
- key: signerPublicKeyPem,
1589
- ...RSA_PSS_SIGN_CONFIG
1590
- },
1591
- Buffer.from(wrappedDekSignature, "base64")
1592
- );
1593
- } catch {
1594
- return false;
1595
- }
1596
- }
1597
- function normalizeVaultSummaryCheckpoint(checkpoint) {
1598
- return {
1599
- vaultId: checkpoint.vaultId,
1600
- version: checkpoint.version,
1601
- name: checkpoint.name,
1602
- dataClassification: checkpoint.dataClassification ?? null,
1603
- currentDekVersion: checkpoint.currentDekVersion ?? null,
1604
- items: [...checkpoint.items].map((item) => ({
1605
- id: item.id,
1606
- name: item.name,
1607
- type: item.type ?? null,
1608
- websites: [...item.websites],
1609
- groupId: item.groupId ?? null
1610
- })).sort((left, right) => left.id.localeCompare(right.id)),
1611
- groups: [...checkpoint.groups].map((group) => ({
1612
- id: group.id,
1613
- name: group.name,
1614
- parentId: group.parentId ?? null
1615
- })).sort((left, right) => left.id.localeCompare(right.id))
1616
- };
1617
- }
1618
- function canonicalizeVaultSummaryCheckpoint(checkpoint) {
1619
- return JSON.stringify(normalizeVaultSummaryCheckpoint(checkpoint));
1620
- }
1621
- function buildVaultSummaryCheckpointPayload(checkpoint) {
1622
- return getCheckpointFingerprint(
1623
- VAULT_SUMMARY_CHECKPOINT_PREFIX,
1624
- canonicalizeVaultSummaryCheckpoint(checkpoint)
1625
- );
1626
- }
1627
- function verifyVaultSummaryCheckpoint(checkpoint, signature, publicKeyPem) {
1628
- try {
1629
- return crypto2.verify(
1630
- "sha256",
1631
- Buffer.from(buildVaultSummaryCheckpointPayload(checkpoint), "utf8"),
1632
- {
1633
- key: publicKeyPem,
1634
- ...RSA_PSS_SIGN_CONFIG
1635
- },
1636
- Buffer.from(signature, "base64")
1637
- );
1638
- } catch {
1639
- return false;
1640
- }
1641
- }
1642
- function normalizeVaultItemDetailCheckpoint(checkpoint) {
1643
- return {
1644
- vaultItemId: checkpoint.vaultItemId,
1645
- vaultId: checkpoint.vaultId,
1646
- version: checkpoint.version,
1647
- name: checkpoint.name,
1648
- type: checkpoint.type ?? null,
1649
- websites: [...checkpoint.websites],
1650
- groupId: checkpoint.groupId ?? null,
1651
- fields: [...checkpoint.fields].map((field) => ({
1652
- id: field.id,
1653
- name: field.name,
1654
- type: field.type,
1655
- order: field.order,
1656
- fieldInstanceIds: [...field.fieldInstanceIds].sort(),
1657
- assetIds: [...field.assetIds].sort()
1658
- })).sort((left, right) => left.order - right.order || left.id.localeCompare(right.id))
1659
- };
1660
- }
1661
- function canonicalizeVaultItemDetailCheckpoint(checkpoint) {
1662
- return JSON.stringify(normalizeVaultItemDetailCheckpoint(checkpoint));
1663
- }
1664
- function buildVaultItemDetailCheckpointPayload(checkpoint) {
1665
- return getCheckpointFingerprint(
1666
- VAULT_ITEM_DETAIL_CHECKPOINT_PREFIX,
1667
- canonicalizeVaultItemDetailCheckpoint(checkpoint)
1668
- );
1669
- }
1670
- function verifyVaultItemDetailCheckpoint(checkpoint, signature, publicKeyPem) {
1671
- try {
1672
- return crypto2.verify(
1673
- "sha256",
1674
- Buffer.from(buildVaultItemDetailCheckpointPayload(checkpoint), "utf8"),
1675
- {
1676
- key: publicKeyPem,
1677
- ...RSA_PSS_SIGN_CONFIG
1678
- },
1679
- Buffer.from(signature, "base64")
1680
- );
1681
- } catch {
1682
- return false;
1683
- }
1684
- }
1685
- function isVaultEnvelope(value) {
1686
- if (!value.startsWith("{")) {
1687
- return false;
1688
- }
1689
- try {
1690
- const parsed = JSON.parse(value);
1691
- return parsed.v === 3;
1692
- } catch {
1693
- return false;
1694
- }
1695
- }
1696
- function decryptWithVaultDEK(encryptedValue, dek) {
1697
- if (!isVaultEnvelope(encryptedValue)) {
1698
- throw new Error("Invalid encrypted value: expected v3 vault envelope format");
1699
- }
1700
- const envelope = JSON.parse(encryptedValue);
1701
- const decipher = crypto2.createDecipheriv("aes-256-gcm", dek, Buffer.from(envelope.iv, "base64"));
1702
- decipher.setAuthTag(Buffer.from(envelope.t, "base64"));
1703
- const decrypted = Buffer.concat([
1704
- decipher.update(Buffer.from(envelope.d, "base64")),
1705
- decipher.final()
1706
- ]);
1707
- return decrypted.toString("utf8");
1708
- }
1709
- function decryptStoredFieldValue(value, dek) {
1710
- return isVaultEnvelope(value) ? decryptWithVaultDEK(value, dek) : value;
1711
- }
1712
- function unwrapDEKWithPrivateKey(wrappedDek, privateKeyPem) {
1713
- return crypto2.privateDecrypt(
1714
- { key: privateKeyPem, ...RSA_OAEP_CONFIG },
1715
- Buffer.from(wrappedDek, "base64")
1716
- );
1717
- }
1718
- function loadTrustStore(trustStorePath) {
1719
- try {
1720
- const raw = fs22.readFileSync(trustStorePath, "utf8");
1721
- const parsed = JSON.parse(raw);
1722
- return {
1723
- version: 1,
1724
- userKeyPins: parsed.userKeyPins ?? {},
1725
- checkpointVersionPins: parsed.checkpointVersionPins ?? {},
1726
- transparencyHeadPins: parsed.transparencyHeadPins ?? {}
1727
- };
1728
- } catch {
1729
- return {
1730
- version: 1,
1731
- userKeyPins: {},
1732
- checkpointVersionPins: {},
1733
- transparencyHeadPins: {}
1734
- };
1735
- }
1736
- }
1737
- function saveTrustStore(trustStorePath, store) {
1738
- fs22.mkdirSync(path22.dirname(trustStorePath), { recursive: true });
1739
- fs22.writeFileSync(trustStorePath, JSON.stringify(store, null, 2) + "\n", "utf8");
1740
- }
1741
- function getPinStorageKey(orgId, orgUserId) {
1742
- return `${orgId}:${orgUserId}`;
1743
- }
1744
- function getAgentPinStorageKey(orgId, agentId) {
1745
- void orgId;
1746
- return `agent:${agentId}`;
1747
- }
1748
- function getDirectoryPinStorageKey(orgId) {
1749
- return `org:${orgId}`;
1750
- }
1751
- function getAgentTransparencyPinStorageKey(orgId, agentId) {
1752
- void orgId;
1753
- return `agent-head:${agentId}`;
1754
- }
1755
- async function fetchWitnessArtifact(witnessBaseUrl, pathName) {
1756
- const response = await fetch(
1757
- buildTransparencyWitnessUrl(witnessBaseUrl, pathName),
1758
- {
1759
- cache: "no-store"
1760
- }
1761
- );
1762
- if (!response.ok) {
1763
- throw new Error(
1764
- `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.`
1765
- );
1766
- }
1767
- return response.json();
1768
- }
1769
- function getSinglePinnedTransparencyHead(trustStorePath) {
1770
- const store = loadTrustStore(trustStorePath);
1771
- const heads = Object.values(store.transparencyHeadPins);
1772
- return heads.length === 1 ? heads[0] : null;
1773
- }
1774
- async function getPublicOrgWitnessHead(apiBaseUrl, orgId, configuredWitnessBaseUrl) {
1775
- const witnessBaseUrl = resolveTransparencyWitnessBaseUrl({
1776
- apiBaseUrl,
1777
- configuredBaseUrl: configuredWitnessBaseUrl
1778
- });
1779
- if (!witnessBaseUrl) {
1780
- return null;
1781
- }
1782
- const artifact = await fetchWitnessArtifact(
1783
- witnessBaseUrl,
1784
- buildOrgUserKeyDirectoryWitnessPath(orgId)
1785
- );
1786
- if (artifact.kind !== "org-user-key-directory" || artifact.orgId !== orgId || !verifyOrgUserKeyDirectoryWitnessArtifact(
1787
- artifact,
1788
- TRANSPARENCY_WITNESS_ROOT_PUBLIC_KEY_PEM
1789
- )) {
1790
- throw new Error(`Public transparency witness verification failed for org ${orgId}.`);
1791
- }
1792
- return artifact.head;
1793
- }
1794
- async function getPublicAgentWitnessHead(apiBaseUrl, agentId, configuredWitnessBaseUrl) {
1795
- const witnessBaseUrl = resolveTransparencyWitnessBaseUrl({
1796
- apiBaseUrl,
1797
- configuredBaseUrl: configuredWitnessBaseUrl
1798
- });
1799
- if (!witnessBaseUrl) {
1800
- return null;
1801
- }
1802
- const artifact = await fetchWitnessArtifact(
1803
- witnessBaseUrl,
1804
- buildAgentPublicKeyWitnessPath(agentId)
1805
- );
1806
- if (artifact.kind !== "agent-public-key" || artifact.agentId !== agentId || !verifyAgentPublicKeyWitnessArtifact(
1807
- artifact,
1808
- TRANSPARENCY_WITNESS_ROOT_PUBLIC_KEY_PEM
1809
- )) {
1810
- throw new Error(`Public transparency witness verification failed for agent ${agentId}.`);
1811
- }
1812
- return artifact.head;
1813
- }
1814
- async function pinVaultUserPublicKeys(trustStorePath, orgId, publicKeys) {
1815
- const store = loadTrustStore(trustStorePath);
1816
- let changed = false;
1817
- for (const key of publicKeys) {
1818
- const storageKey = getPinStorageKey(orgId, key.orgUserId);
1819
- const computedFingerprint = getPublicKeyFingerprint(key.publicKey);
1820
- if (computedFingerprint !== key.fingerprint) {
1821
- throw new Error(`Server returned a mismatched fingerprint for user ${key.orgUserId}.`);
1822
- }
1823
- const existing = store.userKeyPins[storageKey];
1824
- const verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
1825
- if (!existing) {
1826
- store.userKeyPins[storageKey] = {
1827
- keyPairId: key.userKeyPairId,
1828
- fingerprint: key.fingerprint,
1829
- publicKey: key.publicKey,
1830
- pinnedAt: verifiedAt,
1831
- verifiedAt
1832
- };
1833
- changed = true;
1834
- continue;
1835
- }
1836
- if (existing.keyPairId === key.userKeyPairId) {
1837
- if (existing.fingerprint !== key.fingerprint || existing.publicKey !== key.publicKey) {
1838
- throw new Error(
1839
- `Pinned public key ${key.userKeyPairId} changed unexpectedly for user ${key.orgUserId}.`
1840
- );
1841
- }
1842
- if (existing.verifiedAt !== verifiedAt) {
1843
- store.userKeyPins[storageKey] = {
1844
- ...existing,
1845
- verifiedAt
1846
- };
1847
- changed = true;
1848
- }
1849
- continue;
1850
- }
1851
- if (!key.previousUserKeyPairId || key.previousUserKeyPairId !== existing.keyPairId || !key.rotationSignature) {
1852
- throw new Error(`Public key rotation for user ${key.orgUserId} is missing a trusted continuity proof.`);
1853
- }
1854
- const rotationVerified = verifyUserKeyRotation(
1855
- existing.keyPairId,
1856
- key.publicKey,
1857
- key.rotationSignature,
1858
- existing.publicKey
1859
- );
1860
- if (!rotationVerified) {
1861
- throw new Error(`Public key rotation for user ${key.orgUserId} failed signature verification.`);
1862
- }
1863
- store.userKeyPins[storageKey] = {
1864
- keyPairId: key.userKeyPairId,
1865
- fingerprint: key.fingerprint,
1866
- publicKey: key.publicKey,
1867
- pinnedAt: existing.pinnedAt,
1868
- verifiedAt
1869
- };
1870
- changed = true;
1871
- }
1872
- if (changed) {
1873
- saveTrustStore(trustStorePath, store);
1874
- }
1875
- return publicKeys;
1876
- }
1877
- async function pinVaultAgentPublicKeys(trustStorePath, orgId, publicKeys, apiBaseUrl, configuredWitnessBaseUrl) {
1878
- const store = loadTrustStore(trustStorePath);
1879
- let changed = false;
1880
- for (const key of publicKeys) {
1881
- const storageKey = getAgentPinStorageKey(orgId, key.agentId);
1882
- const computedFingerprint = getPublicKeyFingerprint(key.publicKey);
1883
- if (computedFingerprint !== key.fingerprint) {
1884
- throw new Error(`Server returned a mismatched fingerprint for agent ${key.agentId}.`);
1885
- }
1886
- if (!key.transparency) {
1887
- throw new Error(`Server omitted the agent public-key transparency proof for agent ${key.agentId}.`);
1888
- }
1889
- const currentProofEntry = key.transparency.entries[key.transparency.entries.length - 1];
1890
- if (!currentProofEntry) {
1891
- throw new Error(`Server returned an empty agent public-key transparency proof for agent ${key.agentId}.`);
1892
- }
1893
- const expectedCurrentEntry = buildAgentPublicKeyTransparencyEntry({
1894
- agentId: key.agentId,
1895
- version: currentProofEntry.version,
1896
- encryptionKeyId: key.encryptionKeyId,
1897
- publicKey: key.publicKey,
1898
- fingerprint: key.fingerprint,
1899
- previousEncryptionKeyId: key.previousEncryptionKeyId ?? null,
1900
- rotationSignature: key.rotationSignature ?? null,
1901
- previousEntryHash: currentProofEntry.previousEntryHash ?? null
1902
- });
1903
- if (expectedCurrentEntry.entryHash !== currentProofEntry.entryHash) {
1904
- throw new Error(`Agent public-key transparency entry does not match the current key for agent ${key.agentId}.`);
1905
- }
1906
- const pinnedTransparencyHead = store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)] ?? null;
1907
- const witnessHead = !pinnedTransparencyHead ? await getPublicAgentWitnessHead(apiBaseUrl, key.agentId, configuredWitnessBaseUrl) : null;
1908
- const trustedPreviousHead = witnessHead ?? pinnedTransparencyHead;
1909
- if (witnessHead && key.transparency.head.version < witnessHead.version) {
1910
- throw new Error(`Public transparency witness head is ahead of the server response for agent ${key.agentId}.`);
1911
- }
1912
- if (witnessHead && key.transparency.head.version === witnessHead.version) {
1913
- if (key.transparency.head.hash !== witnessHead.hash) {
1914
- throw new Error(`Public transparency witness head fork detected for agent ${key.agentId}.`);
1915
- }
1916
- if (currentProofEntry.entryHash !== witnessHead.hash || expectedCurrentEntry.entryHash !== witnessHead.hash) {
1917
- throw new Error(`Agent public-key transparency witness anchor mismatch for agent ${key.agentId}.`);
1918
- }
1919
- } else if (!verifyAgentPublicKeyTransparencyProof({
1920
- currentEntry: expectedCurrentEntry,
1921
- proof: key.transparency,
1922
- previousHead: trustedPreviousHead
1923
- })) {
1924
- throw new Error(`Agent public-key transparency proof verification failed for agent ${key.agentId}.`);
1925
- }
1926
- const existing = store.userKeyPins[storageKey];
1927
- const verifiedAt = (/* @__PURE__ */ new Date()).toISOString();
1928
- if (!existing) {
1929
- store.userKeyPins[storageKey] = {
1930
- keyPairId: key.encryptionKeyId,
1931
- fingerprint: key.fingerprint,
1932
- publicKey: key.publicKey,
1933
- pinnedAt: verifiedAt,
1934
- verifiedAt
1935
- };
1936
- store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)] = key.transparency.head;
1937
- changed = true;
1938
- continue;
1939
- }
1940
- if (existing.keyPairId === key.encryptionKeyId) {
1941
- if (existing.fingerprint !== key.fingerprint || existing.publicKey !== key.publicKey) {
1942
- throw new Error(
1943
- `Pinned public key ${key.encryptionKeyId} changed unexpectedly for agent ${key.agentId}.`
1944
- );
1945
- }
1946
- 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) {
1947
- store.userKeyPins[storageKey] = {
1948
- ...existing,
1949
- verifiedAt
1950
- };
1951
- store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)] = key.transparency.head;
1952
- changed = true;
1953
- }
1954
- continue;
1955
- }
1956
- if (!key.previousEncryptionKeyId || key.previousEncryptionKeyId !== existing.keyPairId || !key.rotationSignature) {
1957
- throw new Error(`Public key rotation for agent ${key.agentId} is missing a trusted continuity proof.`);
1958
- }
1959
- const rotationVerified = verifyUserKeyRotation(
1960
- existing.keyPairId,
1961
- key.publicKey,
1962
- key.rotationSignature,
1963
- existing.publicKey
1964
- );
1965
- if (!rotationVerified) {
1966
- throw new Error(`Public key rotation for agent ${key.agentId} failed signature verification.`);
1967
- }
1968
- store.userKeyPins[storageKey] = {
1969
- keyPairId: key.encryptionKeyId,
1970
- fingerprint: key.fingerprint,
1971
- publicKey: key.publicKey,
1972
- pinnedAt: existing.pinnedAt,
1973
- verifiedAt
1974
- };
1975
- store.transparencyHeadPins[getAgentTransparencyPinStorageKey(orgId, key.agentId)] = key.transparency.head;
1976
- changed = true;
1977
- }
1978
- if (changed) {
1979
- saveTrustStore(trustStorePath, store);
1980
- }
1981
- return publicKeys;
1982
- }
1983
- async function verifySignedUserKeyDirectory(trustStorePath, directory, anchorHead) {
1984
- if (!directory.directoryCheckpoint) {
1985
- if (directory.publicKeys.length > 0) {
1986
- throw new Error("Server omitted the user-key directory checkpoint for a non-empty vault signer directory.");
1987
- }
1988
- return null;
1989
- }
1990
- const { directoryCheckpoint } = directory;
1991
- const orgId = directoryCheckpoint.checkpoint.orgId;
1992
- if (!directoryCheckpoint.signerOrgUserId || !directoryCheckpoint.signerPublicKey) {
1993
- throw new Error("Server returned an incomplete user-key directory signer payload.");
1994
- }
1995
- const signerFingerprint = getPublicKeyFingerprint(directoryCheckpoint.signerPublicKey);
1996
- const signerEntry = directoryCheckpoint.checkpoint.entries.find(
1997
- (entry) => entry.userKeyPairId === directoryCheckpoint.signerUserKeyPairId && entry.orgUserId === directoryCheckpoint.signerOrgUserId
1998
- );
1999
- const signerKey = {
2000
- userKeyPairId: directoryCheckpoint.signerUserKeyPairId,
2001
- orgUserId: directoryCheckpoint.signerOrgUserId,
2002
- publicKey: directoryCheckpoint.signerPublicKey,
2003
- fingerprint: signerFingerprint,
2004
- previousUserKeyPairId: signerEntry?.previousUserKeyPairId ?? null,
2005
- rotationSignature: signerEntry?.rotationSignature ?? null
2006
- };
2007
- const storeBeforeVerification = loadTrustStore(trustStorePath);
2008
- try {
2009
- await pinVaultUserPublicKeys(trustStorePath, orgId, [signerKey]);
2010
- const verified = verifyUserKeyDirectoryCheckpoint(
2011
- directoryCheckpoint.checkpoint,
2012
- directoryCheckpoint.signature,
2013
- directoryCheckpoint.signerPublicKey
2014
- );
2015
- if (!verified) {
2016
- throw new Error(`User-key directory signature verification failed for org ${orgId}.`);
2017
- }
2018
- if (!directory.transparency) {
2019
- throw new Error(`Server omitted the user-key directory transparency proof for org ${orgId}.`);
2020
- }
2021
- const store = loadTrustStore(trustStorePath);
2022
- const pinnedTransparencyHead = store.transparencyHeadPins[getDirectoryPinStorageKey(orgId)] ?? null;
2023
- const trustedPreviousHead = anchorHead ?? pinnedTransparencyHead;
2024
- const legacyPinnedVersion = store.checkpointVersionPins[getDirectoryPinStorageKey(orgId)] ?? null;
2025
- if (!trustedPreviousHead && legacyPinnedVersion !== null && directory.transparency.head.version < legacyPinnedVersion) {
2026
- throw new Error(`User-key transparency head rolled back unexpectedly for org ${orgId}.`);
2027
- }
2028
- if (!trustedPreviousHead && directory.transparency.entries.length === 0) {
2029
- throw new Error(`Server omitted the current transparency entry for org ${orgId}.`);
2030
- }
2031
- if (directory.transparency.entries.length > 0) {
2032
- const currentProofEntry = directory.transparency.entries[directory.transparency.entries.length - 1];
2033
- if (!currentProofEntry) {
2034
- throw new Error(`Server returned an empty transparency proof for org ${orgId}.`);
2035
- }
2036
- const expectedCurrentEntry = buildUserKeyDirectoryTransparencyEntry({
2037
- checkpoint: directoryCheckpoint.checkpoint,
2038
- signerUserKeyPairId: directoryCheckpoint.signerUserKeyPairId,
2039
- signerOrgUserId: directoryCheckpoint.signerOrgUserId,
2040
- signerPublicKey: directoryCheckpoint.signerPublicKey,
2041
- signature: directoryCheckpoint.signature,
2042
- previousEntryHash: currentProofEntry.previousEntryHash ?? null
2043
- });
2044
- if (expectedCurrentEntry.entryHash !== currentProofEntry.entryHash) {
2045
- throw new Error(`User-key transparency entry does not match the signed directory for org ${orgId}.`);
2046
- }
2047
- if (anchorHead && directory.transparency.head.version === anchorHead.version) {
2048
- if (directory.transparency.head.hash !== anchorHead.hash) {
2049
- throw new Error(`Public transparency witness head fork detected for org ${orgId}.`);
2050
- }
2051
- if (currentProofEntry.entryHash !== anchorHead.hash || expectedCurrentEntry.entryHash !== anchorHead.hash) {
2052
- throw new Error(`User-key transparency witness anchor mismatch for org ${orgId}.`);
2053
- }
2054
- } else if (!verifyUserKeyDirectoryTransparencyProof({
2055
- currentEntry: expectedCurrentEntry,
2056
- proof: directory.transparency,
2057
- previousHead: trustedPreviousHead
2058
- })) {
2059
- throw new Error(`User-key transparency proof verification failed for org ${orgId}.`);
2060
- }
2061
- } else if (anchorHead && directory.transparency.head.version === anchorHead.version) {
2062
- throw new Error(`Server omitted the current transparency entry required to verify org ${orgId} against the public witness.`);
2063
- } else if (!trustedPreviousHead || trustedPreviousHead.version !== directory.transparency.head.version || trustedPreviousHead.hash !== directory.transparency.head.hash) {
2064
- throw new Error(`Server returned an incomplete user-key transparency proof for org ${orgId}.`);
2065
- }
2066
- assertAndPinTransparencyHead(trustStorePath, orgId, directory.transparency.head);
2067
- const checkpointEntries = new Map(
2068
- directoryCheckpoint.checkpoint.entries.map((entry) => [entry.userKeyPairId, entry])
2069
- );
2070
- for (const key of directory.publicKeys) {
2071
- const entry = checkpointEntries.get(key.userKeyPairId);
2072
- if (!entry) {
2073
- throw new Error(`User key ${key.userKeyPairId} is missing from the signed org directory.`);
2074
- }
2075
- if (entry.orgUserId !== key.orgUserId || entry.fingerprint !== key.fingerprint || entry.previousUserKeyPairId !== key.previousUserKeyPairId || entry.rotationSignature !== key.rotationSignature) {
2076
- throw new Error(`User key ${key.userKeyPairId} does not match the signed org directory.`);
2077
- }
2078
- }
2079
- return orgId;
2080
- } catch (error) {
2081
- saveTrustStore(trustStorePath, storeBeforeVerification);
2082
- throw error;
2083
- }
2084
- }
2085
- async function verifyAndPinVaultUserPublicKeys(trustStorePath, directory, anchorHead) {
2086
- const orgId = await verifySignedUserKeyDirectory(trustStorePath, directory, anchorHead);
2087
- if (!orgId) {
2088
- return directory.publicKeys;
2089
- }
2090
- return pinVaultUserPublicKeys(trustStorePath, orgId, directory.publicKeys);
2091
- }
2092
- async function verifyAndPinVaultAgentPublicKeys(trustStorePath, orgId, publicKeys, apiBaseUrl, configuredWitnessBaseUrl) {
2093
- return pinVaultAgentPublicKeys(
2094
- trustStorePath,
2095
- orgId,
2096
- publicKeys,
2097
- apiBaseUrl,
2098
- configuredWitnessBaseUrl
2099
- );
2100
- }
2101
- function assertAndPinCheckpointVersion(trustStorePath, storageKey, version) {
2102
- const store = loadTrustStore(trustStorePath);
2103
- const pinnedVersion = store.checkpointVersionPins[storageKey];
2104
- if (pinnedVersion !== void 0 && version < pinnedVersion) {
2105
- throw new Error(`Checkpoint version rolled back unexpectedly for ${storageKey}.`);
2106
- }
2107
- if (pinnedVersion === void 0 || version > pinnedVersion) {
2108
- store.checkpointVersionPins[storageKey] = version;
2109
- saveTrustStore(trustStorePath, store);
2110
- }
2111
- }
2112
- function assertAndPinTransparencyHead(trustStorePath, orgId, head) {
2113
- const store = loadTrustStore(trustStorePath);
2114
- const storageKey = getDirectoryPinStorageKey(orgId);
2115
- const pinnedHead = store.transparencyHeadPins[storageKey];
2116
- if (pinnedHead) {
2117
- if (head.version < pinnedHead.version) {
2118
- throw new Error(`User-key transparency head rolled back unexpectedly for org ${orgId}.`);
2119
- }
2120
- if (head.version === pinnedHead.version && head.hash !== pinnedHead.hash) {
2121
- throw new Error(`User-key transparency head fork detected for org ${orgId}.`);
2122
- }
2123
- }
2124
- if (!pinnedHead || head.version > pinnedHead.version) {
2125
- store.transparencyHeadPins[storageKey] = head;
2126
- saveTrustStore(trustStorePath, store);
2127
- }
2128
- assertAndPinCheckpointVersion(trustStorePath, storageKey, head.version);
2129
- }
2130
- var R4_DEFAULT_API_BASE_URL2 = "https://r4.dev";
2131
- var R4_DEV_API_BASE_URL2 = "https://dev.r4.dev";
2132
- function toScreamingSnakeCase(input) {
2133
- return input.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toUpperCase();
2134
- }
2135
- function resolveTrustStorePath2(config) {
2136
- if (config.trustStorePath) {
2137
- return path3.resolve(config.trustStorePath);
2138
- }
2139
- if (config.privateKeyPath) {
2140
- return `${path3.resolve(config.privateKeyPath)}.trust.json`;
2141
- }
2142
- return path3.resolve(process.cwd(), ".r4-trust-store.json");
2143
- }
2144
- function resolveApiBaseUrl(config) {
2145
- if (config.baseUrl) {
2146
- return config.baseUrl;
2147
- }
2148
- return config.dev ? R4_DEV_API_BASE_URL2 : R4_DEFAULT_API_BASE_URL2;
2149
- }
2150
- function buildVaultSummaryCheckpointFromListResponse(response, version) {
2151
- return {
2152
- vaultId: response.vaultId,
2153
- version,
2154
- name: response.vaultName,
2155
- dataClassification: response.dataClassification ?? null,
2156
- currentDekVersion: response.currentDekVersion ?? null,
2157
- items: response.items.map((item) => ({
2158
- id: item.id,
2159
- name: item.name,
2160
- type: item.type ?? null,
2161
- websites: item.websites ?? [],
2162
- groupId: item.groupId ?? null
2163
- })),
2164
- groups: response.vaultItemGroups.map((group) => ({
2165
- id: group.id,
2166
- name: group.name,
2167
- parentId: group.parentId ?? null
2168
- }))
2169
- };
2170
- }
2171
- function buildVaultItemDetailCheckpointFromResponse(item, version) {
2172
- return {
2173
- vaultItemId: item.id,
2174
- vaultId: item.vaultId,
2175
- version,
2176
- name: item.name,
2177
- type: item.type ?? null,
2178
- websites: item.websites ?? [],
2179
- groupId: item.groupId ?? null,
2180
- fields: item.fields.map((field, index) => ({
2181
- id: field.id,
2182
- name: field.name,
2183
- type: field.type,
2184
- order: field.order ?? index,
2185
- fieldInstanceIds: field.fieldInstanceIds ?? [],
2186
- assetIds: field.assetIds ?? []
2187
- }))
2188
- };
2189
- }
2190
- function getCheckpointSignerPublicKey(params) {
2191
- const signerType = params.checkpoint.signerType ?? "USER_KEY_PAIR";
2192
- if (signerType === "AGENT_ENCRYPTION_KEY") {
2193
- const signerKey2 = params.agentKeys.find(
2194
- (publicKey) => publicKey.encryptionKeyId === params.checkpoint.signerEncryptionKeyId
2195
- );
2196
- const historicalSignerKey = params.agentKeys.flatMap((publicKey) => publicKey.transparency?.entries ?? []).find((entry) => entry.encryptionKeyId === params.checkpoint.signerEncryptionKeyId);
2197
- if (!signerKey2 && !historicalSignerKey) {
2198
- throw new Error(
2199
- `R4 SDK: checkpoint signer ${params.checkpoint.signerEncryptionKeyId ?? "unknown"} was not present in the trusted agent key directory.`
2200
- );
2201
- }
2202
- return signerKey2?.publicKey ?? historicalSignerKey.publicKey;
2203
- }
2204
- const signerKey = params.userKeys.find(
2205
- (publicKey) => publicKey.userKeyPairId === params.checkpoint.signerUserKeyPairId
2206
- );
2207
- if (!signerKey) {
2208
- throw new Error(
2209
- `R4 SDK: checkpoint signer ${params.checkpoint.signerUserKeyPairId ?? "unknown"} was not present in the trusted user key directory.`
2210
- );
2211
- }
2212
- return signerKey.publicKey;
2213
- }
2214
- var R4 = class _R4 {
2215
- client;
2216
- baseUrl;
2217
- transparencyWitnessUrl;
2218
- projectId;
2219
- privateKeyPem;
2220
- publicKeyPem;
2221
- trustStorePath;
2222
- _env = null;
2223
- constructor(config) {
2224
- if (!config.apiKey) {
2225
- throw new Error("R4 SDK: apiKey is required");
2226
- }
2227
- if (!config.privateKey && !config.privateKeyPath) {
2228
- throw new Error(
2229
- "R4 SDK: privateKey or privateKeyPath is required for zero-trust local decryption."
2230
- );
2231
- }
2232
- const baseUrl = resolveApiBaseUrl(config);
2233
- this.baseUrl = baseUrl;
2234
- this.transparencyWitnessUrl = config.transparencyWitnessUrl;
2235
- this.client = new R4Client(config.apiKey, baseUrl);
2236
- this.projectId = config.projectId;
2237
- this.privateKeyPem = config.privateKey ?? loadPrivateKey2(config.privateKeyPath);
2238
- this.publicKeyPem = derivePublicKey2(this.privateKeyPem);
2239
- this.trustStorePath = resolveTrustStorePath2(config);
2240
- }
2241
- /**
2242
- * Static factory method that creates and initializes an R4 instance.
2243
- */
2244
- static async create(config) {
2245
- const instance = new _R4(config);
2246
- await instance.init();
2247
- return instance;
2248
- }
2249
- /**
2250
- * Initializes the SDK by registering the agent public key (idempotent) and
2251
- * decrypting all accessible vault values locally into a flat env map.
2252
- */
2253
- async init() {
2254
- this._env = await this.fetchEnv();
2255
- }
2256
- /**
2257
- * Returns the locally decrypted env map.
2258
- */
2259
- get env() {
2260
- if (!this._env) {
2261
- throw new Error(
2262
- "R4 SDK: env is not initialized. Call await r4.init() first, or use R4.create() for automatic initialization."
2263
- );
2264
- }
2265
- return this._env;
2266
- }
2267
- /**
2268
- * Re-fetches and locally re-decrypts the current vault view.
2269
- */
2270
- async refresh() {
2271
- this._env = await this.fetchEnv();
2272
- }
2273
- /**
2274
- * Sends an authenticated request to an arbitrary machine API route so
2275
- * callers can use the full headless surface without hand-rolling fetch code.
2276
- */
2277
- async requestMachine(params) {
2278
- return this.client.requestMachine(params);
2279
- }
2280
- /**
2281
- * Fetches, verifies, and locally decrypts a token provider's vault-backed
2282
- * credential fields without exposing plaintext back to R4.
2283
- */
2284
- async getTokenProviderFields(tokenProviderId, options) {
2285
- if (options?.unsafeExport !== true) {
2286
- throw new Error(
2287
- "R4 SDK: getTokenProviderFields exports decrypted provider secrets. Pass { unsafeExport: true } to confirm or use callAnthropicWithTokenProvider() instead."
2288
- );
2289
- }
2290
- await this.registerAgentPublicKey();
2291
- const runtime = await this.getVerifiedTokenProviderRuntime(tokenProviderId);
2292
- return {
2293
- id: runtime.id,
2294
- name: runtime.name,
2295
- providerType: runtime.providerType,
2296
- fields: this.decryptTokenProviderFields(runtime)
2297
- };
2298
- }
2299
- /**
2300
- * Managed Anthropic call path:
2301
- * 1. decrypt the provider secret locally
2302
- * 2. preflight the budget gate with estimated usage
2303
- * 3. call Anthropic directly
2304
- * 4. publish finalized usage back to R4
2305
- */
2306
- async callAnthropicWithTokenProvider(params) {
2307
- await this.registerAgentPublicKey();
2308
- const runtime = await this.getVerifiedTokenProviderRuntime(params.tokenProviderId);
2309
- if (runtime.providerType !== "ANTHROPIC") {
2310
- throw new Error(
2311
- `R4 SDK: token provider ${params.tokenProviderId} is ${runtime.providerType}, not ANTHROPIC.`
2312
- );
2313
- }
2314
- const fields = this.decryptTokenProviderFields(runtime);
2315
- const apiKey = fields["API Key"];
2316
- const endpoint = fields["Endpoint URL"] || "https://api.anthropic.com/v1/messages";
2317
- const model = this.requireStringField(params.body.model, "body.model");
2318
- if (!apiKey) {
2319
- throw new Error(`R4 SDK: token provider ${params.tokenProviderId} does not contain an API Key field.`);
2320
- }
2321
- const requestId = randomUUID();
2322
- const authorization = await this.client.authorizeTokenProviderUsage(params.tokenProviderId, {
2323
- model,
2324
- estimatedInputTokens: params.estimatedInputTokens ?? 0,
2325
- estimatedOutputTokens: params.estimatedOutputTokens ?? this.inferEstimatedOutputTokens(params.body.max_tokens)
2326
- });
2327
- const response = await fetch(endpoint, {
2328
- method: "POST",
2329
- headers: {
2330
- "Content-Type": "application/json",
2331
- "x-api-key": apiKey,
2332
- "anthropic-version": "2023-06-01"
2333
- },
2334
- body: JSON.stringify(params.body),
2335
- signal: params.signal
2336
- });
2337
- if (!response.ok) {
2338
- const bodyText = await response.text().catch(() => "");
2339
- throw new Error(`Anthropic API error: ${response.status} ${response.statusText}${bodyText ? ` - ${bodyText}` : ""}`);
2340
- }
2341
- const parsedResponse = await response.json();
2342
- const inputTokens = this.toNonNegativeInt(parsedResponse.usage?.input_tokens);
2343
- const outputTokens = this.toNonNegativeInt(parsedResponse.usage?.output_tokens);
2344
- const usage = await this.client.reportTokenProviderUsage(params.tokenProviderId, {
2345
- requestId,
2346
- model,
2347
- vendorRequestId: typeof parsedResponse.id === "string" ? parsedResponse.id : null,
2348
- inputTokens,
2349
- outputTokens,
2350
- totalTokens: inputTokens + outputTokens,
2351
- estimatedCostUsd: authorization.estimatedCostUsd,
2352
- metadata: {
2353
- providerType: runtime.providerType
2354
- }
2355
- });
2356
- return {
2357
- response: parsedResponse,
2358
- usage,
2359
- authorization,
2360
- requestId
2361
- };
2362
- }
2363
- /**
2364
- * Registers the local agent public key, loads all accessible vaults, verifies
2365
- * wrapped-DEK signatures against pinned signer keys, unwraps each vault DEK,
2366
- * and builds a flat SCREAMING_SNAKE_CASE env map from decrypted field values.
2367
- */
2368
- async fetchEnv() {
2369
- await this.registerAgentPublicKey();
2370
- const { vaults } = await this.client.listVaults(this.projectId);
2371
- const envEntries = await Promise.all(vaults.map((vault) => this.fetchVaultEnv(vault.id)));
2372
- return Object.assign({}, ...envEntries);
2373
- }
2374
- /**
2375
- * Ensures the local agent public key is registered before any zero-trust
2376
- * wrapped-key or token-provider flow runs.
2377
- */
2378
- async registerAgentPublicKey() {
2379
- try {
2380
- await this.client.registerAgentPublicKey({
2381
- publicKey: this.publicKeyPem
2382
- });
2383
- } catch (error) {
2384
- throw new Error(
2385
- `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)}`
2386
- );
2387
- }
2388
- }
2389
- /**
2390
- * Fetches a single vault's wrapped DEK, verifies it against the pinned signer
2391
- * directory, unwraps the DEK locally, then decrypts every field value in that
2392
- * vault item-by-item.
2393
- */
2394
- async fetchVaultEnv(vaultId) {
2395
- const pinnedTransparencyHead = getSinglePinnedTransparencyHead(this.trustStorePath);
2396
- const [wrappedKey, itemsResponse, initialPublicKeyDirectory] = await Promise.all([
2397
- this.client.getAgentWrappedKey(vaultId),
2398
- this.client.listVaultItems(vaultId),
2399
- this.client.getVaultUserKeyDirectory(
2400
- vaultId,
2401
- pinnedTransparencyHead ? {
2402
- knownTransparencyVersion: pinnedTransparencyHead.version,
2403
- knownTransparencyHash: pinnedTransparencyHead.hash
2404
- } : void 0
2405
- )
2406
- ]);
2407
- let publicKeyDirectory = initialPublicKeyDirectory;
2408
- let witnessAnchorHead = null;
2409
- if (!pinnedTransparencyHead) {
2410
- const orgId = initialPublicKeyDirectory.directoryCheckpoint?.checkpoint.orgId ?? null;
2411
- if (orgId && initialPublicKeyDirectory.directoryCheckpoint && initialPublicKeyDirectory.transparency) {
2412
- witnessAnchorHead = await getPublicOrgWitnessHead(
2413
- this.baseUrl,
2414
- orgId,
2415
- this.transparencyWitnessUrl
2416
- );
2417
- if (witnessAnchorHead) {
2418
- if (initialPublicKeyDirectory.transparency.head.version < witnessAnchorHead.version) {
2419
- throw new Error(`R4 SDK: public transparency witness head is ahead of the server response for org ${orgId}.`);
2420
- }
2421
- if (initialPublicKeyDirectory.transparency.head.version === witnessAnchorHead.version) {
2422
- if (initialPublicKeyDirectory.transparency.head.hash !== witnessAnchorHead.hash) {
2423
- throw new Error(`R4 SDK: public transparency witness head fork detected for org ${orgId}.`);
2424
- }
2425
- } else {
2426
- publicKeyDirectory = await this.client.getVaultUserKeyDirectory(vaultId, {
2427
- knownTransparencyVersion: witnessAnchorHead.version,
2428
- knownTransparencyHash: witnessAnchorHead.hash
2429
- });
2430
- }
928
+ const response = await client.createAgent(body);
929
+ spinner.stop();
930
+ if (globalOpts.json) {
931
+ console.log(JSON.stringify(response, null, 2));
932
+ return;
2431
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
+ );
2432
944
  }
2433
- }
2434
- const trustedPublicKeys = await verifyAndPinVaultUserPublicKeys(
2435
- this.trustStorePath,
2436
- publicKeyDirectory,
2437
- witnessAnchorHead
2438
- );
2439
- const checkpointOrgId = publicKeyDirectory.directoryCheckpoint?.checkpoint.orgId ?? vaultId;
2440
- const trustedAgentPublicKeys = await verifyAndPinVaultAgentPublicKeys(
2441
- this.trustStorePath,
2442
- checkpointOrgId,
2443
- publicKeyDirectory.agentPublicKeys ?? [],
2444
- this.baseUrl,
2445
- this.transparencyWitnessUrl
2446
- );
2447
- const signerKey = trustedPublicKeys.find(
2448
- (publicKey) => publicKey.userKeyPairId === wrappedKey.signerUserKeyPairId
2449
- );
2450
- const historicalAgentSignerKey = trustedAgentPublicKeys.flatMap((publicKey) => publicKey.transparency?.entries ?? []).find((entry) => entry.encryptionKeyId === wrappedKey.signerEncryptionKeyId);
2451
- const wrappedKeySignerPublicKey = wrappedKey.signerEncryptionKeyId ? trustedAgentPublicKeys.find(
2452
- (publicKey) => publicKey.encryptionKeyId === wrappedKey.signerEncryptionKeyId
2453
- )?.publicKey ?? historicalAgentSignerKey?.publicKey : signerKey?.publicKey;
2454
- if (!wrappedKeySignerPublicKey) {
2455
- throw new Error(
2456
- `R4 SDK: wrapped DEK signer ${wrappedKey.signerEncryptionKeyId ?? wrappedKey.signerUserKeyPairId ?? "unknown"} was not present in the trusted key directory.`
2457
- );
2458
- }
2459
- const signatureVerified = verifyWrappedDekSignature(
2460
- vaultId,
2461
- wrappedKey.encryptionKeyId,
2462
- wrappedKey.signerEncryptionKeyId ?? wrappedKey.signerUserKeyPairId,
2463
- wrappedKey.dekVersion,
2464
- wrappedKey.wrappedDek,
2465
- wrappedKey.wrappedDekSignature,
2466
- wrappedKeySignerPublicKey
2467
- );
2468
- if (!signatureVerified) {
2469
- throw new Error(`R4 SDK: wrapped DEK signature verification failed for vault ${vaultId}.`);
2470
- }
2471
- const dek = unwrapDEKWithPrivateKey(wrappedKey.wrappedDek, this.privateKeyPem);
2472
- if (!itemsResponse.summaryCheckpoint) {
2473
- throw new Error(`R4 SDK: vault ${vaultId} is missing a signed summary checkpoint.`);
2474
- }
2475
- const expectedSummaryCheckpoint = buildVaultSummaryCheckpointFromListResponse(
2476
- itemsResponse,
2477
- itemsResponse.summaryCheckpoint.checkpoint.version
2478
- );
2479
- const summaryVerified = verifyVaultSummaryCheckpoint(
2480
- expectedSummaryCheckpoint,
2481
- itemsResponse.summaryCheckpoint.signature,
2482
- getCheckpointSignerPublicKey({
2483
- checkpoint: itemsResponse.summaryCheckpoint,
2484
- userKeys: trustedPublicKeys,
2485
- agentKeys: trustedAgentPublicKeys
2486
- })
2487
- );
2488
- if (!summaryVerified) {
2489
- throw new Error(`R4 SDK: vault summary checkpoint verification failed for vault ${vaultId}.`);
2490
- }
2491
- assertAndPinCheckpointVersion(
2492
- this.trustStorePath,
2493
- `summary:${vaultId}`,
2494
- expectedSummaryCheckpoint.version
2495
- );
2496
- const itemDetails = await Promise.all(
2497
- itemsResponse.items.map((item) => this.client.getVaultItemDetail(vaultId, item.id))
2498
- );
2499
- const env = {};
2500
- for (const item of itemDetails) {
2501
- if (!item.detailCheckpoint) {
2502
- throw new Error(`R4 SDK: vault item ${item.id} is missing a signed detail checkpoint.`);
2503
- }
2504
- const expectedDetailCheckpoint = buildVaultItemDetailCheckpointFromResponse(
2505
- item,
2506
- item.detailCheckpoint.checkpoint.version
2507
- );
2508
- const detailVerified = verifyVaultItemDetailCheckpoint(
2509
- expectedDetailCheckpoint,
2510
- item.detailCheckpoint.signature,
2511
- getCheckpointSignerPublicKey({
2512
- checkpoint: item.detailCheckpoint,
2513
- userKeys: trustedPublicKeys,
2514
- agentKeys: trustedAgentPublicKeys
2515
- })
2516
- );
2517
- if (!detailVerified) {
2518
- 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;
2519
965
  }
2520
- assertAndPinCheckpointVersion(
2521
- this.trustStorePath,
2522
- `detail:${item.id}`,
2523
- 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
2524
982
  );
2525
- for (const field of item.fields) {
2526
- if (field.value === null) {
2527
- continue;
2528
- }
2529
- env[toScreamingSnakeCase(`${item.name}_${field.name}`)] = decryptStoredFieldValue(
2530
- field.value,
2531
- dek
2532
- );
2533
- }
2534
- }
2535
- return env;
2536
- }
2537
- async getVerifiedTokenProviderRuntime(tokenProviderId) {
2538
- const pinnedTransparencyHead = getSinglePinnedTransparencyHead(this.trustStorePath);
2539
- let runtime = await this.client.getTokenProviderRuntime(
2540
- tokenProviderId,
2541
- pinnedTransparencyHead ? {
2542
- knownTransparencyVersion: pinnedTransparencyHead.version,
2543
- knownTransparencyHash: pinnedTransparencyHead.hash
2544
- } : void 0
2545
- );
2546
- let witnessAnchorHead = null;
2547
- if (!pinnedTransparencyHead) {
2548
- const orgId = runtime.keyDirectory.directoryCheckpoint?.checkpoint.orgId ?? null;
2549
- if (orgId && runtime.keyDirectory.directoryCheckpoint && runtime.keyDirectory.transparency) {
2550
- witnessAnchorHead = await getPublicOrgWitnessHead(
2551
- this.baseUrl,
2552
- orgId,
2553
- 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
2554
991
  );
2555
- if (witnessAnchorHead) {
2556
- if (runtime.keyDirectory.transparency.head.version < witnessAnchorHead.version) {
2557
- throw new Error(
2558
- `R4 SDK: public transparency witness head is ahead of the server response for org ${orgId}.`
2559
- );
2560
- }
2561
- if (runtime.keyDirectory.transparency.head.version === witnessAnchorHead.version) {
2562
- if (runtime.keyDirectory.transparency.head.hash !== witnessAnchorHead.hash) {
2563
- throw new Error(`R4 SDK: public transparency witness head fork detected for org ${orgId}.`);
2564
- }
2565
- } else {
2566
- runtime = await this.client.getTokenProviderRuntime(tokenProviderId, {
2567
- knownTransparencyVersion: witnessAnchorHead.version,
2568
- knownTransparencyHash: witnessAnchorHead.hash
2569
- });
2570
- }
2571
- }
2572
992
  }
2573
- }
2574
- const trustedPublicKeys = await verifyAndPinVaultUserPublicKeys(
2575
- this.trustStorePath,
2576
- runtime.keyDirectory,
2577
- witnessAnchorHead
2578
- );
2579
- const checkpointOrgId = runtime.keyDirectory.directoryCheckpoint?.checkpoint.orgId ?? runtime.vaultId;
2580
- const trustedAgentPublicKeys = await verifyAndPinVaultAgentPublicKeys(
2581
- this.trustStorePath,
2582
- checkpointOrgId,
2583
- runtime.keyDirectory.agentPublicKeys ?? [],
2584
- this.baseUrl,
2585
- this.transparencyWitnessUrl
2586
- );
2587
- const wrappedKeySignerPublicKey = this.getWrappedKeySignerPublicKey(
2588
- runtime.wrappedKey,
2589
- trustedPublicKeys,
2590
- trustedAgentPublicKeys
2591
- );
2592
- if (!verifyWrappedDekSignature(
2593
- runtime.vaultId,
2594
- runtime.wrappedKey.encryptionKeyId,
2595
- runtime.wrappedKey.signerEncryptionKeyId ?? runtime.wrappedKey.signerUserKeyPairId,
2596
- runtime.wrappedKey.dekVersion,
2597
- runtime.wrappedKey.wrappedDek,
2598
- runtime.wrappedKey.wrappedDekSignature,
2599
- wrappedKeySignerPublicKey
2600
- )) {
2601
- throw new Error(`R4 SDK: wrapped DEK signature verification failed for vault ${runtime.vaultId}.`);
2602
- }
2603
- const expectedDetailCheckpoint = buildVaultItemDetailCheckpointFromResponse(
2604
- runtime.vaultItemDetail,
2605
- runtime.vaultItemDetail.detailCheckpoint?.checkpoint.version ?? 0
2606
- );
2607
- if (!runtime.vaultItemDetail.detailCheckpoint) {
2608
- throw new Error(`R4 SDK: vault item ${runtime.vaultItemDetail.id} is missing a signed detail checkpoint.`);
2609
- }
2610
- const detailVerified = verifyVaultItemDetailCheckpoint(
2611
- expectedDetailCheckpoint,
2612
- runtime.vaultItemDetail.detailCheckpoint.signature,
2613
- getCheckpointSignerPublicKey({
2614
- checkpoint: runtime.vaultItemDetail.detailCheckpoint,
2615
- userKeys: trustedPublicKeys,
2616
- agentKeys: trustedAgentPublicKeys
2617
- })
2618
- );
2619
- if (!detailVerified) {
2620
- throw new Error(
2621
- `R4 SDK: vault item checkpoint verification failed for item ${runtime.vaultItemDetail.id}.`
2622
- );
2623
- }
2624
- assertAndPinCheckpointVersion(
2625
- this.trustStorePath,
2626
- `detail:${runtime.vaultItemDetail.id}`,
2627
- expectedDetailCheckpoint.version
2628
- );
2629
- return runtime;
2630
- }
2631
- decryptTokenProviderFields(runtime) {
2632
- const dek = unwrapDEKWithPrivateKey(runtime.wrappedKey.wrappedDek, this.privateKeyPem);
2633
- const fields = {};
2634
- for (const field of runtime.vaultItemDetail.fields) {
2635
- if (field.value === null) {
2636
- 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;
2637
1013
  }
2638
- fields[field.name] = decryptStoredFieldValue(field.value, dek);
2639
- }
2640
- return fields;
2641
- }
2642
- getWrappedKeySignerPublicKey(wrappedKey, userKeys, agentKeys) {
2643
- const signerKey = userKeys.find(
2644
- (publicKey) => publicKey.userKeyPairId === wrappedKey.signerUserKeyPairId
2645
- );
2646
- const historicalAgentSignerKey = agentKeys.flatMap((publicKey) => publicKey.transparency?.entries ?? []).find((entry) => entry.encryptionKeyId === wrappedKey.signerEncryptionKeyId);
2647
- const wrappedKeySignerPublicKey = wrappedKey.signerEncryptionKeyId ? agentKeys.find(
2648
- (publicKey) => publicKey.encryptionKeyId === wrappedKey.signerEncryptionKeyId
2649
- )?.publicKey ?? historicalAgentSignerKey?.publicKey : signerKey?.publicKey;
2650
- if (!wrappedKeySignerPublicKey) {
2651
- throw new Error(
2652
- `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
2653
1022
  );
2654
- }
2655
- return wrappedKeySignerPublicKey;
2656
- }
2657
- requireStringField(value, label) {
2658
- if (typeof value !== "string" || value.length === 0) {
2659
- throw new Error(`R4 SDK: ${label} must be a non-empty string.`);
2660
- }
2661
- return value;
2662
- }
2663
- inferEstimatedOutputTokens(value) {
2664
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
2665
- return Math.floor(value);
2666
- }
2667
- return 0;
2668
- }
2669
- toNonNegativeInt(value) {
2670
- if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
2671
- return Math.floor(value);
2672
- }
2673
- return 0;
2674
- }
2675
- };
2676
- var index_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";
2677
1036
 
2678
1037
  // src/lib/doctor.ts
1038
+ import R4 from "@r4-sdk/node";
2679
1039
  function getAgentIdFromRegistration(registration) {
2680
1040
  const entryCount = registration?.transparency?.entries.length ?? 0;
2681
1041
  if (!registration?.transparency || entryCount === 0) {
@@ -2949,7 +1309,7 @@ async function runDoctorChecks(connection) {
2949
1309
  return report;
2950
1310
  }
2951
1311
  try {
2952
- const r4 = await index_default.create({
1312
+ const r4 = await R4.create({
2953
1313
  apiKey: connection.apiKey,
2954
1314
  baseUrl: connection.baseUrl,
2955
1315
  dev: connection.dev,
@@ -3065,8 +1425,8 @@ function doctorCommand(commandName = "doctor", description = "Verify CLI auth, r
3065
1425
  }
3066
1426
 
3067
1427
  // src/lib/credentials-file.ts
3068
- import fs6 from "fs";
3069
- import path5 from "path";
1428
+ import fs5 from "fs";
1429
+ import path3 from "path";
3070
1430
  function normalizeFieldName(fieldName) {
3071
1431
  return fieldName.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
3072
1432
  }
@@ -3272,9 +1632,9 @@ function parsePlainTextContent(content) {
3272
1632
  return {};
3273
1633
  }
3274
1634
  function parseCredentialsFile(credentialsFilePath) {
3275
- const resolvedPath = path5.resolve(credentialsFilePath);
3276
- const content = fs6.readFileSync(resolvedPath, "utf8");
3277
- 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();
3278
1638
  if (extension === ".json") {
3279
1639
  return parseJsonContent(content);
3280
1640
  }
@@ -3877,14 +2237,14 @@ function logoutCommand() {
3877
2237
  import { Command as Command11 } from "commander";
3878
2238
 
3879
2239
  // src/lib/public-auth-client.ts
3880
- import crypto3 from "crypto";
2240
+ import crypto2 from "crypto";
3881
2241
  var PublicAuthClient = class {
3882
2242
  baseUrl;
3883
2243
  constructor(baseUrl) {
3884
2244
  this.baseUrl = baseUrl.replace(/\/$/, "");
3885
2245
  }
3886
- async request(path7, body) {
3887
- const response = await fetch(`${this.baseUrl}${path7}`, {
2246
+ async request(path6, body) {
2247
+ const response = await fetch(`${this.baseUrl}${path6}`, {
3888
2248
  method: "POST",
3889
2249
  headers: {
3890
2250
  "Content-Type": "application/json",
@@ -3910,7 +2270,7 @@ var PublicAuthClient = class {
3910
2270
  }
3911
2271
  };
3912
2272
  function solveAgentChallenge(nonce) {
3913
- return crypto3.createHash("sha256").update(nonce).digest("hex");
2273
+ return crypto2.createHash("sha256").update(nonce).digest("hex");
3914
2274
  }
3915
2275
 
3916
2276
  // src/lib/register-agent.ts
@@ -4290,8 +2650,8 @@ import { Command as Command16 } from "commander";
4290
2650
  import ora10 from "ora";
4291
2651
 
4292
2652
  // src/lib/json-input.ts
4293
- import fs7 from "fs";
4294
- import path6 from "path";
2653
+ import fs6 from "fs";
2654
+ import path4 from "path";
4295
2655
  function parseJsonInput(params) {
4296
2656
  const bodyFlagName = params.bodyFlagName ?? "--body";
4297
2657
  const bodyFileFlagName = params.bodyFileFlagName ?? "--body-file";
@@ -4301,7 +2661,7 @@ function parseJsonInput(params) {
4301
2661
  if (!params.body && !params.bodyFile) {
4302
2662
  return void 0;
4303
2663
  }
4304
- 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;
4305
2665
  const sourceLabel = params.bodyFile ? `${bodyFileFlagName} ${params.bodyFile}` : bodyFlagName;
4306
2666
  try {
4307
2667
  return JSON.parse(rawInput);
@@ -4402,7 +2762,7 @@ function registerBudgetCommands(program2) {
4402
2762
 
4403
2763
  // src/commands/configure.ts
4404
2764
  import { Command as Command18 } from "commander";
4405
- import fs8 from "fs";
2765
+ import fs7 from "fs";
4406
2766
  async function promptRuntimeTarget(existingProfile) {
4407
2767
  const defaultTarget = existingProfile.baseUrl ? "custom" : existingProfile.dev ? "dev" : "prod";
4408
2768
  const runtimeTarget = await promptChoice(
@@ -4435,7 +2795,7 @@ async function promptRuntimeTarget(existingProfile) {
4435
2795
  }
4436
2796
  async function resolvePrivateKeyMaterial(profileName, existingProfile) {
4437
2797
  const managedPrivateKeyPath = getDefaultPrivateKeyPath(profileName);
4438
- const defaultMode = existingProfile.privateKeyPath || fs8.existsSync(managedPrivateKeyPath) ? "existing" : "generate";
2798
+ const defaultMode = existingProfile.privateKeyPath || fs7.existsSync(managedPrivateKeyPath) ? "existing" : "generate";
4439
2799
  const privateKeyMode = await promptChoice(
4440
2800
  "How should this profile handle its local private key?",
4441
2801
  [
@@ -4738,13 +3098,13 @@ function normalizeMethod(method) {
4738
3098
  function requestCommand() {
4739
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(
4740
3100
  withErrorHandler(
4741
- async (method, path7, options, cmd) => {
3101
+ async (method, path6, options, cmd) => {
4742
3102
  const globalOpts = cmd.optsWithGlobals();
4743
3103
  const config = resolveAuth(globalOpts);
4744
3104
  const client = new CliClient(config.apiKey, config.baseUrl);
4745
3105
  const result = await client.requestMachine({
4746
3106
  method: normalizeMethod(method),
4747
- path: path7,
3107
+ path: path6,
4748
3108
  body: parseJsonInput({
4749
3109
  body: options.body,
4750
3110
  bodyFile: options.bodyFile
@@ -5049,17 +3409,64 @@ function createItemCommand() {
5049
3409
  );
5050
3410
  }
5051
3411
 
5052
- // src/commands/vault/list.ts
3412
+ // src/commands/vault/download-asset.ts
3413
+ import path5 from "path";
5053
3414
  import { Command as Command32 } from "commander";
5054
3415
  import ora21 from "ora";
5055
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";
5056
3463
  function listCommand5() {
5057
- 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(
5058
3465
  withErrorHandler(async (_opts, cmd) => {
5059
3466
  const globalOpts = cmd.optsWithGlobals();
5060
3467
  const config = resolveAuth(globalOpts);
5061
- const spinner = ora21("Fetching environment variables...").start();
5062
- const r4 = await R42.create(config);
3468
+ const spinner = ora22("Fetching environment variables...").start();
3469
+ const r4 = await R43.create(config);
5063
3470
  spinner.stop();
5064
3471
  const env = r4.env;
5065
3472
  const keys = Object.keys(env).sort();
@@ -5080,12 +3487,13 @@ function listCommand5() {
5080
3487
  }
5081
3488
 
5082
3489
  // src/commands/vault/list-items.ts
5083
- import { Command as Command34 } from "commander";
3490
+ import { Command as Command35 } from "commander";
5084
3491
 
5085
3492
  // src/commands/vault/items.ts
5086
- import { Command as Command33 } from "commander";
5087
- import ora22 from "ora";
5088
- import R43 from "@r4-sdk/node";
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]";
5089
3497
  function deriveItems(env) {
5090
3498
  const keys = Object.keys(env).sort();
5091
3499
  return keys.map((key) => ({
@@ -5096,15 +3504,18 @@ function deriveItems(env) {
5096
3504
  async function loadVaultMetadataRows(globalOpts) {
5097
3505
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5098
3506
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5099
- const { vaults } = await client.listVaults(connection.projectId);
5100
- 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();
5101
3512
  for (const vault of vaults) {
5102
3513
  const response = await client.listVaultItems(vault.id);
5103
3514
  const groupNames = Object.fromEntries(
5104
3515
  response.vaultItemGroups.map((group) => [group.id, group.name])
5105
3516
  );
5106
3517
  for (const item of response.items) {
5107
- rows.push({
3518
+ rowsByItemId.set(item.id, {
5108
3519
  vaultId: vault.id,
5109
3520
  vaultName: vault.name,
5110
3521
  itemId: item.id,
@@ -5116,13 +3527,33 @@ async function loadVaultMetadataRows(globalOpts) {
5116
3527
  });
5117
3528
  }
5118
3529
  }
5119
- 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) => {
5120
3551
  const vaultCompare = left.vaultName.localeCompare(right.vaultName);
5121
3552
  return vaultCompare !== 0 ? vaultCompare : left.itemName.localeCompare(right.itemName);
5122
3553
  });
5123
3554
  }
5124
3555
  async function renderVaultMetadataRows(globalOpts) {
5125
- const spinner = ora22("Fetching vault item metadata...").start();
3556
+ const spinner = ora23("Fetching vault item metadata...").start();
5126
3557
  const rows = await loadVaultMetadataRows(globalOpts);
5127
3558
  spinner.stop();
5128
3559
  if (globalOpts.json) {
@@ -5140,7 +3571,7 @@ async function renderVaultMetadataRows(globalOpts) {
5140
3571
  row.vaultName,
5141
3572
  row.itemName,
5142
3573
  row.type || "-",
5143
- String(row.fieldCount),
3574
+ row.fieldCount === null ? "-" : String(row.fieldCount),
5144
3575
  row.groupName || "-",
5145
3576
  row.websites.join(", ") || "-"
5146
3577
  ]),
@@ -5149,7 +3580,7 @@ async function renderVaultMetadataRows(globalOpts) {
5149
3580
  console.log();
5150
3581
  }
5151
3582
  function itemsCommand() {
5152
- 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(
5153
3584
  withErrorHandler(async (opts, cmd) => {
5154
3585
  const globalOpts = cmd.optsWithGlobals();
5155
3586
  if (opts.metadataOnly) {
@@ -5157,8 +3588,8 @@ function itemsCommand() {
5157
3588
  return;
5158
3589
  }
5159
3590
  const config = resolveAuth(globalOpts);
5160
- const spinner = ora22("Fetching vault items...").start();
5161
- const r4 = await R43.create(config);
3591
+ const spinner = ora23("Fetching vault items...").start();
3592
+ const r4 = await R44.create(config);
5162
3593
  spinner.stop();
5163
3594
  const env = r4.env;
5164
3595
  const items = deriveItems(env);
@@ -5184,7 +3615,7 @@ function itemsCommand() {
5184
3615
 
5185
3616
  // src/commands/vault/list-items.ts
5186
3617
  function listItemsCommand() {
5187
- 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(
5188
3619
  withErrorHandler(async (_opts, cmd) => {
5189
3620
  const globalOpts = cmd.optsWithGlobals();
5190
3621
  await renderVaultMetadataRows(globalOpts);
@@ -5193,15 +3624,15 @@ function listItemsCommand() {
5193
3624
  }
5194
3625
 
5195
3626
  // src/commands/vault/list-vaults.ts
5196
- import { Command as Command35 } from "commander";
5197
- import ora23 from "ora";
3627
+ import { Command as Command36 } from "commander";
3628
+ import ora24 from "ora";
5198
3629
  function listVaultsCommand() {
5199
- 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(
5200
3631
  withErrorHandler(async (_opts, cmd) => {
5201
3632
  const globalOpts = cmd.optsWithGlobals();
5202
3633
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5203
3634
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5204
- const spinner = ora23("Fetching visible vaults...").start();
3635
+ const spinner = ora24("Fetching visible vaults...").start();
5205
3636
  const response = await client.listVaults(connection.projectId);
5206
3637
  spinner.stop();
5207
3638
  if (globalOpts.json) {
@@ -5230,17 +3661,17 @@ function listVaultsCommand() {
5230
3661
  }
5231
3662
 
5232
3663
  // src/commands/vault/get.ts
5233
- import { Command as Command36 } from "commander";
5234
- import ora24 from "ora";
5235
- import R44 from "@r4-sdk/node";
3664
+ import { Command as Command37 } from "commander";
3665
+ import ora25 from "ora";
3666
+ import R45 from "@r4-sdk/node";
5236
3667
  function getCommand2() {
5237
- 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(
5238
3669
  withErrorHandler(
5239
3670
  async (keyArg, _opts, cmd) => {
5240
3671
  const globalOpts = cmd.optsWithGlobals();
5241
3672
  const config = resolveAuth(globalOpts);
5242
- const spinner = ora24("Fetching environment variables...").start();
5243
- const r4 = await R44.create(config);
3673
+ const spinner = ora25("Fetching environment variables...").start();
3674
+ const r4 = await R45.create(config);
5244
3675
  spinner.stop();
5245
3676
  const env = r4.env;
5246
3677
  const key = keyArg.toUpperCase();
@@ -5260,17 +3691,17 @@ function getCommand2() {
5260
3691
  }
5261
3692
 
5262
3693
  // src/commands/vault/search.ts
5263
- import { Command as Command37 } from "commander";
5264
- import ora25 from "ora";
5265
- import R45 from "@r4-sdk/node";
3694
+ import { Command as Command38 } from "commander";
3695
+ import ora26 from "ora";
3696
+ import R46 from "@r4-sdk/node";
5266
3697
  function searchCommand() {
5267
- 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(
5268
3699
  withErrorHandler(
5269
3700
  async (query, _opts, cmd) => {
5270
3701
  const globalOpts = cmd.optsWithGlobals();
5271
3702
  const config = resolveAuth(globalOpts);
5272
- const spinner = ora25("Searching vault items...").start();
5273
- const r4 = await R45.create(config);
3703
+ const spinner = ora26("Searching vault items...").start();
3704
+ const r4 = await R46.create(config);
5274
3705
  spinner.stop();
5275
3706
  const env = r4.env;
5276
3707
  const lowerQuery = query.toLowerCase();
@@ -5300,6 +3731,7 @@ function registerVaultCommands(program2) {
5300
3731
  const vault = program2.command("vault").description("Manage vault secrets");
5301
3732
  vault.addCommand(createCommand3());
5302
3733
  vault.addCommand(createItemCommand());
3734
+ vault.addCommand(downloadAssetCommand());
5303
3735
  vault.addCommand(listCommand5());
5304
3736
  vault.addCommand(listVaultsCommand());
5305
3737
  vault.addCommand(listItemsCommand());
@@ -5309,16 +3741,16 @@ function registerVaultCommands(program2) {
5309
3741
  }
5310
3742
 
5311
3743
  // src/commands/project/add-vault.ts
5312
- import { Command as Command38 } from "commander";
5313
- import ora26 from "ora";
3744
+ import { Command as Command39 } from "commander";
3745
+ import ora27 from "ora";
5314
3746
  function addVaultCommand() {
5315
- 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(
5316
3748
  withErrorHandler(
5317
3749
  async (projectId, vaultId, _opts, cmd) => {
5318
3750
  const globalOpts = cmd.optsWithGlobals();
5319
3751
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5320
3752
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5321
- const spinner = ora26("Associating vault with project...").start();
3753
+ const spinner = ora27("Associating vault with project...").start();
5322
3754
  await client.associateProjectVault({
5323
3755
  projectId,
5324
3756
  vaultId
@@ -5335,15 +3767,15 @@ function addVaultCommand() {
5335
3767
  }
5336
3768
 
5337
3769
  // src/commands/project/list.ts
5338
- import { Command as Command39 } from "commander";
5339
- import ora27 from "ora";
3770
+ import { Command as Command40 } from "commander";
3771
+ import ora28 from "ora";
5340
3772
  function listCommand6() {
5341
- return new Command39("list").description("List all projects").action(
3773
+ return new Command40("list").description("List all projects").action(
5342
3774
  withErrorHandler(async (_opts, cmd) => {
5343
3775
  const globalOpts = cmd.optsWithGlobals();
5344
3776
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5345
3777
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5346
- const spinner = ora27("Fetching projects...").start();
3778
+ const spinner = ora28("Fetching projects...").start();
5347
3779
  const response = await client.listProjects();
5348
3780
  spinner.stop();
5349
3781
  const rows = response.projects.map((p) => [
@@ -5367,16 +3799,16 @@ function listCommand6() {
5367
3799
  }
5368
3800
 
5369
3801
  // src/commands/project/get.ts
5370
- import { Command as Command40 } from "commander";
3802
+ import { Command as Command41 } from "commander";
5371
3803
  import chalk6 from "chalk";
5372
- import ora28 from "ora";
3804
+ import ora29 from "ora";
5373
3805
  function getCommand3() {
5374
- 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(
5375
3807
  withErrorHandler(async (id, _opts, cmd) => {
5376
3808
  const globalOpts = cmd.optsWithGlobals();
5377
3809
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5378
3810
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5379
- const spinner = ora28("Fetching project...").start();
3811
+ const spinner = ora29("Fetching project...").start();
5380
3812
  const project = await client.getProject(id);
5381
3813
  spinner.stop();
5382
3814
  if (globalOpts.json) {
@@ -5434,9 +3866,9 @@ function getCommand3() {
5434
3866
  }
5435
3867
 
5436
3868
  // src/commands/project/create.ts
5437
- import { Command as Command41 } from "commander";
3869
+ import { Command as Command42 } from "commander";
5438
3870
  import readline2 from "readline";
5439
- import ora29 from "ora";
3871
+ import ora30 from "ora";
5440
3872
  function prompt(question) {
5441
3873
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
5442
3874
  return new Promise((resolve) => {
@@ -5447,7 +3879,7 @@ function prompt(question) {
5447
3879
  });
5448
3880
  }
5449
3881
  function createCommand4() {
5450
- 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(
5451
3883
  withErrorHandler(async (opts, cmd) => {
5452
3884
  const globalOpts = cmd.optsWithGlobals();
5453
3885
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
@@ -5459,7 +3891,7 @@ function createCommand4() {
5459
3891
  if (!name) {
5460
3892
  throw new Error("Project name is required.");
5461
3893
  }
5462
- const spinner = ora29("Creating project...").start();
3894
+ const spinner = ora30("Creating project...").start();
5463
3895
  const response = await client.createProject({
5464
3896
  name,
5465
3897
  description: opts.description,
@@ -5476,10 +3908,10 @@ function createCommand4() {
5476
3908
  }
5477
3909
 
5478
3910
  // src/commands/project/set-agents.ts
5479
- import { Command as Command42 } from "commander";
5480
- import ora30 from "ora";
3911
+ import { Command as Command43 } from "commander";
3912
+ import ora31 from "ora";
5481
3913
  function setAgentsCommand() {
5482
- 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(
5483
3915
  "--agent-id <id>",
5484
3916
  "Agent ID to keep on the project (repeat or use comma-separated values)",
5485
3917
  collectOptionValues,
@@ -5490,7 +3922,7 @@ function setAgentsCommand() {
5490
3922
  const globalOpts = cmd.optsWithGlobals();
5491
3923
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5492
3924
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5493
- const spinner = ora30("Updating project agents...").start();
3925
+ const spinner = ora31("Updating project agents...").start();
5494
3926
  await client.updateProjectAgents(projectId, {
5495
3927
  agentIds: options.agentId
5496
3928
  });
@@ -5517,16 +3949,16 @@ function registerProjectCommands(program2) {
5517
3949
 
5518
3950
  // src/commands/run/index.ts
5519
3951
  import { spawn } from "child_process";
5520
- import ora31 from "ora";
5521
- import R46 from "@r4-sdk/node";
3952
+ import ora32 from "ora";
3953
+ import R47 from "@r4-sdk/node";
5522
3954
  function registerRunCommand(program2) {
5523
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(
5524
3956
  withErrorHandler(
5525
3957
  async (commandParts, opts, cmd) => {
5526
3958
  const globalOpts = cmd.optsWithGlobals();
5527
3959
  const config = resolveAuth(globalOpts);
5528
- const spinner = ora31("Loading vault secrets...").start();
5529
- const r4 = await R46.create(config);
3960
+ const spinner = ora32("Loading vault secrets...").start();
3961
+ const r4 = await R47.create(config);
5530
3962
  spinner.stop();
5531
3963
  const env = r4.env;
5532
3964
  const secretEnv = {};
@@ -5552,10 +3984,10 @@ function registerRunCommand(program2) {
5552
3984
  }
5553
3985
 
5554
3986
  // src/commands/security-group/create.ts
5555
- import { Command as Command43 } from "commander";
5556
- import ora32 from "ora";
3987
+ import { Command as Command44 } from "commander";
3988
+ import ora33 from "ora";
5557
3989
  function createCommand5() {
5558
- 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(
5559
3991
  "--role <role>",
5560
3992
  "Tenant role to grant through this group (repeat or use comma-separated values)",
5561
3993
  collectOptionValues,
@@ -5566,7 +3998,7 @@ function createCommand5() {
5566
3998
  const globalOpts = cmd.optsWithGlobals();
5567
3999
  const connection = resolveConnection(globalOpts, { requireApiKey: true });
5568
4000
  const client = new CliClient(connection.apiKey, connection.baseUrl);
5569
- const spinner = ora32("Creating security group...").start();
4001
+ const spinner = ora33("Creating security group...").start();
5570
4002
  await client.createSecurityGroup({
5571
4003
  name: options.name,
5572
4004
  parentId: options.parentId ?? null,
@@ -5602,8 +4034,8 @@ function registerSecurityGroupCommands(program2) {
5602
4034
  }
5603
4035
 
5604
4036
  // src/index.ts
5605
- var program = new Command44();
5606
- program.name("r4").description("R4 CLI \u2014 manage vaults, projects, and secrets from the terminal").version("1.0.1").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);
5607
4039
  registerAgentCommands(program);
5608
4040
  registerAuthCommands(program);
5609
4041
  registerBillingCommands(program);