buckyos 0.7.116 → 0.7.119

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/cli/README.md +14 -10
  2. package/cli/core/argv.ts +0 -4
  3. package/cli/core/auth.ts +104 -20
  4. package/cli/core/config.ts +0 -38
  5. package/cli/core/development_mode.ts +103 -0
  6. package/cli/core/identity.ts +39 -31
  7. package/cli/core/registry.ts +0 -2
  8. package/cli/dist/cli.mjs +320 -230
  9. package/cli/dist/cli.mjs.map +1 -1
  10. package/cli/runtime/host.ts +8 -10
  11. package/cli/version.ts +1 -1
  12. package/dist/browser.cjs +1 -1
  13. package/dist/browser.mjs +59 -58
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.mjs +59 -58
  16. package/dist/namelib.d.ts +4 -0
  17. package/dist/namelib.d.ts.map +1 -1
  18. package/dist/ndm_proxy-68354a89.js +13 -0
  19. package/dist/ndm_proxy-68354a89.js.map +1 -0
  20. package/dist/{ndm_proxy-a719bc58.mjs → ndm_proxy-c00bc561.mjs} +75 -58
  21. package/dist/ndm_proxy-c00bc561.mjs.map +1 -0
  22. package/dist/ndn_types-859bbd48.js +2 -0
  23. package/dist/ndn_types-859bbd48.js.map +1 -0
  24. package/dist/{ndn_types-ca94286b.mjs → ndn_types-f6c08d20.mjs} +43 -7
  25. package/dist/ndn_types-f6c08d20.mjs.map +1 -0
  26. package/dist/node.cjs +1 -1
  27. package/dist/node.mjs +59 -58
  28. package/dist/provision.cjs +1 -1
  29. package/dist/provision.mjs +1 -1
  30. package/dist/sn_client.d.ts +16 -0
  31. package/dist/sn_client.d.ts.map +1 -1
  32. package/dist/task_mgr_client.d.ts +10 -0
  33. package/dist/task_mgr_client.d.ts.map +1 -1
  34. package/dist/{tus_client-f1532e14.mjs → tus_client-573066b3.mjs} +2 -2
  35. package/dist/{tus_client-f1532e14.mjs.map → tus_client-573066b3.mjs.map} +1 -1
  36. package/dist/{tus_client-32d016c4.js → tus_client-83ad0f4f.js} +2 -2
  37. package/dist/{tus_client-32d016c4.js.map → tus_client-83ad0f4f.js.map} +1 -1
  38. package/dist/types.d.ts +1 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/dist/ndm_proxy-36879436.js +0 -13
  42. package/dist/ndm_proxy-36879436.js.map +0 -1
  43. package/dist/ndm_proxy-a719bc58.mjs.map +0 -1
  44. package/dist/ndn_types-c4672b04.js +0 -2
  45. package/dist/ndn_types-c4672b04.js.map +0 -1
  46. package/dist/ndn_types-ca94286b.mjs.map +0 -1
package/cli/dist/cli.mjs CHANGED
@@ -2,7 +2,7 @@ import process from "node:process";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import * as nodePath from "node:path";
4
4
  import { resolve as resolve$5, dirname as dirname$3 } from "node:path";
5
- import { namelib, BuckyOSSDK, RuntimeType, buckyos, VerifyHubClient, createAppInstanceId, parseSessionTokenClaims, ndn, ndm_proxy } from "buckyos/node";
5
+ import { BuckyOSSDK, RuntimeType, buckyos, namelib, VerifyHubClient, createAppInstanceId, parseSessionTokenClaims, ndn, ndm_proxy } from "buckyos/node";
6
6
  import { createHash } from "node:crypto";
7
7
  import { readFile, writeFile, readdir, stat, lstat, realpath, mkdtemp, rename, rm, mkdir, chmod, symlink, copyFile, open } from "node:fs/promises";
8
8
  import { createReadStream, createWriteStream } from "node:fs";
@@ -94,8 +94,6 @@ const STRING_GLOBALS = /* @__PURE__ */ new Map([
94
94
  ["zone", "zone"],
95
95
  ["endpoint", "endpoint"],
96
96
  ["identity", "identity"],
97
- ["identity-root", "identityRoot"],
98
- ["security-root", "securityRoot"],
99
97
  ["session-token", "sessionToken"],
100
98
  ["session-token-file", "sessionTokenFile"],
101
99
  ["output", "output"],
@@ -485,8 +483,6 @@ const TOOL_ENVIRONMENT_NAMES = [
485
483
  "BUCKYOS_TOOL_ENDPOINT",
486
484
  "BUCKYOS_TOOL_IDENTITY",
487
485
  "BUCKYOS_TOOL_OUTPUT",
488
- "BUCKYOS_IDENTITY_ROOT",
489
- "BUCKYOS_SECURITY_ROOT",
490
486
  "BUCKYOS_APPCLIENT_SESSION_TOKEN",
491
487
  "BUCKYOS_ROOT",
492
488
  "SOURCE_DATE_EPOCH"
@@ -499,14 +495,16 @@ function buildDistributionPolicy(options) {
499
495
  const configRoot = path.resolve(
500
496
  environment2.BUCKYOS_TOOL_CONFIG_DIR ?? path.join(homeDir, ".buckyos_tool")
501
497
  );
502
- const readPaths = /* @__PURE__ */ new Set([path.resolve(packageRoot2), path.resolve(cwd), configRoot]);
498
+ const readPaths = /* @__PURE__ */ new Set([
499
+ path.resolve(packageRoot2),
500
+ path.resolve(cwd),
501
+ configRoot,
502
+ path.resolve(path.join(homeDir, ".buckyos")),
503
+ path.resolve(path.join(homeDir, ".buckycli"))
504
+ ]);
503
505
  const writePaths = /* @__PURE__ */ new Set([path.resolve(cwd), configRoot]);
504
506
  if (options.distribution === "system")
505
507
  readPaths.add(buckyosRoot);
506
- if (environment2.BUCKYOS_IDENTITY_ROOT && environment2.BUCKYOS_SECURITY_ROOT) {
507
- readPaths.add(path.resolve(environment2.BUCKYOS_IDENTITY_ROOT));
508
- readPaths.add(path.resolve(environment2.BUCKYOS_SECURITY_ROOT));
509
- }
510
508
  const parsed = collectArgumentPaths(argv);
511
509
  for (const candidate of parsed.read)
512
510
  readPaths.add(resolveInputPath(candidate, cwd, path));
@@ -603,7 +601,7 @@ function collectArgumentPaths(argv) {
603
601
  write.push(value);
604
602
  } else if (name === "allow-read") {
605
603
  read.push(value);
606
- } else if (["input", "session-token-file", "identity-root", "security-root", "pikg"].includes(name)) {
604
+ } else if (["input", "session-token-file", "pikg"].includes(name)) {
607
605
  if (value !== "-")
608
606
  read.push(value);
609
607
  } else if (name === "source" && module === "pikg") {
@@ -686,8 +684,6 @@ const ENVIRONMENT_NAMES = [
686
684
  "BUCKYOS_TOOL_ENDPOINT",
687
685
  "BUCKYOS_TOOL_IDENTITY",
688
686
  "BUCKYOS_TOOL_OUTPUT",
689
- "BUCKYOS_IDENTITY_ROOT",
690
- "BUCKYOS_SECURITY_ROOT",
691
687
  "BUCKYOS_APPCLIENT_SESSION_TOKEN",
692
688
  "BUCKYOS_ROOT"
693
689
  ];
@@ -830,34 +826,6 @@ async function resolveConfig(explicit, environment2 = readEnvironment(), options
830
826
  void 0,
831
827
  sources
832
828
  );
833
- const identityRoot = select(
834
- "identity_root",
835
- explicit.identityRoot,
836
- environment2.BUCKYOS_IDENTITY_ROOT,
837
- void 0,
838
- void 0,
839
- sources
840
- );
841
- const securityRoot = select(
842
- "security_root",
843
- explicit.securityRoot,
844
- environment2.BUCKYOS_SECURITY_ROOT,
845
- void 0,
846
- void 0,
847
- sources
848
- );
849
- if (!!explicit.identityRoot !== !!explicit.securityRoot) {
850
- throw new UsageError(
851
- "IDENTITY_ROOT_PAIR_REQUIRED",
852
- "--identity-root and --security-root must be provided together"
853
- );
854
- }
855
- if (!!environment2.BUCKYOS_IDENTITY_ROOT !== !!environment2.BUCKYOS_SECURITY_ROOT && !explicit.identityRoot) {
856
- throw new UsageError(
857
- "IDENTITY_ROOT_PAIR_REQUIRED",
858
- "BUCKYOS_IDENTITY_ROOT and BUCKYOS_SECURITY_ROOT must be provided together"
859
- );
860
- }
861
829
  let output;
862
830
  if (options.interactive && explicit.output === void 0) {
863
831
  output = "table";
@@ -890,8 +858,6 @@ async function resolveConfig(explicit, environment2 = readEnvironment(), options
890
858
  zone,
891
859
  endpoint,
892
860
  identity,
893
- identityRoot,
894
- securityRoot,
895
861
  sessionToken,
896
862
  sessionTokenFile: explicit.sessionTokenFile,
897
863
  output,
@@ -925,8 +891,6 @@ function localResolvedConfig(explicit, environment2 = readEnvironment(), options
925
891
  zone: explicit.zone ?? environment2.BUCKYOS_TOOL_ZONE,
926
892
  endpoint: explicit.endpoint ?? environment2.BUCKYOS_TOOL_ENDPOINT,
927
893
  identity: explicit.identity ?? environment2.BUCKYOS_TOOL_IDENTITY,
928
- identityRoot: explicit.identityRoot ?? environment2.BUCKYOS_IDENTITY_ROOT,
929
- securityRoot: explicit.securityRoot ?? environment2.BUCKYOS_SECURITY_ROOT,
930
894
  sessionToken: explicit.sessionToken ?? environment2.BUCKYOS_APPCLIENT_SESSION_TOKEN,
931
895
  sessionTokenFile: explicit.sessionTokenFile,
932
896
  output,
@@ -1091,6 +1055,196 @@ function redactUrl(value) {
1091
1055
  function configValueError(message) {
1092
1056
  return new ToolError("INVALID_CONFIG_VALUE", message, 2);
1093
1057
  }
1058
+ class BuckyOSRuntimeAdapter {
1059
+ #sdk = new BuckyOSSDK("node");
1060
+ async initialize(config, session) {
1061
+ const authenticated = session.current();
1062
+ const zoneHost = resolveZoneHost(config);
1063
+ const initialize = this.#sdk.initBuckyOS;
1064
+ await initialize.call(this.#sdk, authenticated.principal.appId, {
1065
+ appId: authenticated.principal.appId,
1066
+ ownerUserId: authenticated.principal.id,
1067
+ runtimeType: RuntimeType.AppClient,
1068
+ zoneHost,
1069
+ defaultProtocol: config.defaultProtocol,
1070
+ sessionToken: authenticated.token,
1071
+ privateKeySearchPaths: [],
1072
+ autoRenew: false,
1073
+ verifyHubServiceUrl: resolveServiceUrl(config, "verify-hub")
1074
+ });
1075
+ return {
1076
+ zone: config.zone ?? zoneHost,
1077
+ endpoint: config.endpoint ?? `${config.defaultProtocol}${zoneHost}`,
1078
+ defaultProtocol: config.defaultProtocol
1079
+ };
1080
+ }
1081
+ }
1082
+ class BuckyOSServiceClientRegistry {
1083
+ #config;
1084
+ #session;
1085
+ #clients = /* @__PURE__ */ new Map();
1086
+ constructor(config, session) {
1087
+ this.#config = config;
1088
+ this.#session = session;
1089
+ }
1090
+ async call(service, method, params, options) {
1091
+ let client = this.#clients.get(service);
1092
+ if (!client) {
1093
+ const RpcClient = buckyos.kRPCClient;
1094
+ client = new RpcClient(resolveServiceUrl(this.#config, service), null, null, {
1095
+ sessionTokenProvider: async () => (await this.#session.ensureValid()).token
1096
+ });
1097
+ this.#clients.set(service, client);
1098
+ }
1099
+ const request = client.call(method, params, { traceId: options.traceId });
1100
+ return await withDeadline(request, options.timeoutMs, options.signal);
1101
+ }
1102
+ async createEventReader(pattern, signal) {
1103
+ return await buckyos.createEventReader(pattern, { keepaliveMs: 5e3, signal });
1104
+ }
1105
+ }
1106
+ class InteractiveSession {
1107
+ constructor(authentication, clients, connection) {
1108
+ this.authentication = authentication;
1109
+ this.clients = clients;
1110
+ this.connection = connection;
1111
+ this.startedAt = Date.now();
1112
+ }
1113
+ static async create(config, authentication, runtime = new BuckyOSRuntimeAdapter(), clients) {
1114
+ await authentication.connect();
1115
+ const connection = await runtime.initialize(config, authentication);
1116
+ return new InteractiveSession(
1117
+ authentication,
1118
+ clients ?? new BuckyOSServiceClientRegistry(config, authentication),
1119
+ connection
1120
+ );
1121
+ }
1122
+ async reconnect() {
1123
+ await this.authentication.reconnect();
1124
+ }
1125
+ }
1126
+ function resolveServiceUrl(config, service) {
1127
+ if (config.endpoint) {
1128
+ const endpoint = new URL(config.endpoint);
1129
+ const marker = endpoint.pathname.indexOf("/kapi/");
1130
+ if (marker >= 0)
1131
+ endpoint.pathname = `${endpoint.pathname.slice(0, marker)}/kapi/${service}`;
1132
+ else if (endpoint.pathname.endsWith("/kapi")) {
1133
+ endpoint.pathname = `${endpoint.pathname}/${service}`;
1134
+ } else
1135
+ endpoint.pathname = `${endpoint.pathname.replace(/\/$/, "")}/kapi/${service}`;
1136
+ endpoint.search = "";
1137
+ endpoint.hash = "";
1138
+ return endpoint.toString().replace(/\/$/, "");
1139
+ }
1140
+ const host2 = resolveZoneHost(config);
1141
+ return `${config.defaultProtocol}${host2}/kapi/${service}`;
1142
+ }
1143
+ function resolveZoneHost(config) {
1144
+ if (config.zone) {
1145
+ try {
1146
+ return namelib.DID.fromStr(config.zone).toRawHostName();
1147
+ } catch {
1148
+ throw new UsageError("INVALID_ZONE", `invalid zone host or DID: ${config.zone}`);
1149
+ }
1150
+ }
1151
+ if (config.endpoint)
1152
+ return new URL(config.endpoint).host;
1153
+ throw new UsageError("CONNECTION_REQUIRED", "an endpoint or zone is required for online commands");
1154
+ }
1155
+ async function withDeadline(promise, timeoutMs, signal) {
1156
+ if (signal?.aborted)
1157
+ throw new ToolError("CANCELED", "operation canceled", 8);
1158
+ let timer;
1159
+ let abortHandler;
1160
+ const guards = [
1161
+ new Promise((_, reject) => {
1162
+ timer = setTimeout(
1163
+ () => reject(new ToolError("TIMEOUT", "operation timed out", 8, true)),
1164
+ timeoutMs
1165
+ );
1166
+ })
1167
+ ];
1168
+ if (signal) {
1169
+ guards.push(
1170
+ new Promise((_, reject) => {
1171
+ abortHandler = () => reject(new ToolError("CANCELED", "operation canceled", 8));
1172
+ signal.addEventListener("abort", abortHandler, { once: true });
1173
+ })
1174
+ );
1175
+ }
1176
+ try {
1177
+ return await Promise.race([promise, ...guards]);
1178
+ } finally {
1179
+ if (timer !== void 0)
1180
+ clearTimeout(timer);
1181
+ if (signal && abortHandler)
1182
+ signal.removeEventListener("abort", abortHandler);
1183
+ }
1184
+ }
1185
+ const BUCKYOS_DEV_CONFIG_SCHEMA_VERSION = 1;
1186
+ async function readTargetDevelopmentMode(config) {
1187
+ try {
1188
+ const url = resolveServiceUrl(config, "control-panel");
1189
+ const client = new buckyos.kRPCClient(url);
1190
+ const value = await withTimeout(
1191
+ client.call("system.dev_mode.get", {}),
1192
+ config.timeoutMs
1193
+ );
1194
+ const parsed = parseBuckyOSDevConfig(value);
1195
+ return parsed.enabled ? { state: "enabled", config: parsed } : { state: "disabled", config: parsed };
1196
+ } catch (error) {
1197
+ return {
1198
+ state: "unavailable",
1199
+ reason: error instanceof Error ? error.message : String(error)
1200
+ };
1201
+ }
1202
+ }
1203
+ function parseBuckyOSDevConfig(value) {
1204
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1205
+ throw new Error("BuckyOSDevConfig must be an object");
1206
+ }
1207
+ const config = value;
1208
+ const keys = Object.keys(config).sort();
1209
+ const expectedKeys = ["enabled", "enabled_at", "enabled_by", "schema_version"];
1210
+ if (JSON.stringify(keys) !== JSON.stringify(expectedKeys)) {
1211
+ throw new Error("BuckyOSDevConfig contains missing or unsupported fields");
1212
+ }
1213
+ if (config.schema_version !== BUCKYOS_DEV_CONFIG_SCHEMA_VERSION) {
1214
+ throw new Error(`unsupported BuckyOSDevConfig schema_version ${String(config.schema_version)}`);
1215
+ }
1216
+ if (typeof config.enabled !== "boolean") {
1217
+ throw new Error("BuckyOSDevConfig enabled must be a boolean");
1218
+ }
1219
+ const enabledAt = config.enabled_at;
1220
+ const enabledBy = config.enabled_by;
1221
+ const hasAuditFields = Number.isSafeInteger(enabledAt) && enabledAt > 0 && typeof enabledBy === "string" && enabledBy.trim().length > 0;
1222
+ const hasNoAuditFields = enabledAt === null && enabledBy === null;
1223
+ if (!hasAuditFields && !hasNoAuditFields) {
1224
+ throw new Error("BuckyOSDevConfig enabled_at and enabled_by must be valid and present together");
1225
+ }
1226
+ if (config.enabled && !hasAuditFields) {
1227
+ throw new Error("enabled BuckyOSDevConfig requires enable audit fields");
1228
+ }
1229
+ return {
1230
+ schema_version: 1,
1231
+ enabled: config.enabled,
1232
+ enabled_at: enabledAt,
1233
+ enabled_by: enabledBy
1234
+ };
1235
+ }
1236
+ async function withTimeout(promise, timeoutMs) {
1237
+ let timer;
1238
+ const timeout = new Promise((_, reject) => {
1239
+ timer = setTimeout(() => reject(new Error("developer-mode lookup timed out")), timeoutMs);
1240
+ });
1241
+ try {
1242
+ return await Promise.race([promise, timeout]);
1243
+ } finally {
1244
+ if (timer !== void 0)
1245
+ clearTimeout(timer);
1246
+ }
1247
+ }
1094
1248
  const join$2 = (...parts) => getHost().path.join(...parts);
1095
1249
  const LOCAL_NODE_GATEWAY_ENDPOINT = "http://127.0.0.1:3180";
1096
1250
  const IDENTITY_CANDIDATE_LIMIT = 8;
@@ -1174,45 +1328,38 @@ async function readCurrentDeviceIdentity(environment2 = readEnvironment()) {
1174
1328
  securityRoot: join$2(buckyosRoot, "security")
1175
1329
  };
1176
1330
  }
1177
- function identityRootPairs(config, environment2 = readEnvironment()) {
1331
+ function identityRootPairs(config, environment2 = readEnvironment(), options = {}) {
1332
+ const device = config.implicitDeviceIdentity;
1333
+ if (device) {
1334
+ return [{
1335
+ publicRoot: device.publicRoot,
1336
+ securityRoot: device.securityRoot,
1337
+ source: "current-device"
1338
+ }];
1339
+ }
1340
+ const home = environment2.HOME ?? environment2.USERPROFILE ?? getHost().homeDir();
1178
1341
  const pairs = [];
1179
- if (config.identityRoot && config.securityRoot) {
1342
+ if (options.includeOperations ?? true) {
1180
1343
  pairs.push({
1181
- publicRoot: config.identityRoot,
1182
- securityRoot: config.securityRoot,
1183
- source: "explicit"
1344
+ publicRoot: join$2(home, ".buckyos", "local", "identity"),
1345
+ securityRoot: join$2(home, ".buckyos", "security"),
1346
+ source: "buckyos-home"
1184
1347
  });
1185
1348
  }
1186
- pairs.push({
1187
- publicRoot: join$2(config.configDir, "local", "identity"),
1188
- securityRoot: join$2(config.configDir, "security"),
1189
- source: "tool"
1190
- });
1191
- if (environment2.BUCKYOS_IDENTITY_ROOT && environment2.BUCKYOS_SECURITY_ROOT) {
1192
- const duplicate = config.identityRoot === environment2.BUCKYOS_IDENTITY_ROOT && config.securityRoot === environment2.BUCKYOS_SECURITY_ROOT;
1193
- if (!duplicate) {
1194
- pairs.push({
1195
- publicRoot: environment2.BUCKYOS_IDENTITY_ROOT,
1196
- securityRoot: environment2.BUCKYOS_SECURITY_ROOT,
1197
- source: "environment"
1198
- });
1199
- }
1200
- }
1201
- if (getHost().policy.distribution === "system") {
1202
- const buckyosRoot = environment2.BUCKYOS_ROOT ?? defaultBuckyOSRoot(environment2);
1349
+ if (options.includeDeveloper ?? false) {
1203
1350
  pairs.push({
1204
- publicRoot: join$2(buckyosRoot, "local", "identity"),
1205
- securityRoot: join$2(buckyosRoot, "security"),
1206
- source: "buckyos-root"
1351
+ publicRoot: join$2(home, ".buckycli", "local", "identity"),
1352
+ securityRoot: join$2(home, ".buckycli", "security"),
1353
+ source: "buckycli-home"
1207
1354
  });
1208
1355
  }
1209
1356
  return deduplicatePairs(pairs);
1210
1357
  }
1211
- async function resolveIdentityMaterial(selectedIdentity, config, environment2 = readEnvironment()) {
1358
+ async function resolveIdentityMaterial(selectedIdentity, config, environment2 = readEnvironment(), options = {}) {
1212
1359
  const selected = selectedIdentity.trim();
1213
1360
  if (!selected)
1214
1361
  throw new UsageError("IDENTITY_REQUIRED", "identity is empty");
1215
- for (const roots of identityRootPairs(config, environment2)) {
1362
+ for (const roots of identityRootPairs(config, environment2, options)) {
1216
1363
  for (const directory of await candidateDirectories(roots.publicRoot, selected)) {
1217
1364
  const material = await loadIdentityMaterial(roots, directory, selected);
1218
1365
  if (material)
@@ -1225,11 +1372,11 @@ async function resolveIdentityMaterial(selectedIdentity, config, environment2 =
1225
1372
  EXIT_AUTH
1226
1373
  );
1227
1374
  }
1228
- async function scanIdentityCandidates(config, environment2 = readEnvironment()) {
1375
+ async function scanIdentityCandidates(config, environment2 = readEnvironment(), options = {}) {
1229
1376
  const candidates = [];
1230
1377
  const skipped = [];
1231
1378
  const seen = /* @__PURE__ */ new Set();
1232
- const roots = identityRootPairs(config, environment2);
1379
+ const roots = identityRootPairs(config, environment2, options);
1233
1380
  for (const pair of roots) {
1234
1381
  let directories;
1235
1382
  try {
@@ -1283,16 +1430,18 @@ async function scanIdentityCandidates(config, environment2 = readEnvironment())
1283
1430
  break;
1284
1431
  }
1285
1432
  return {
1286
- order: Object.freeze(["explicit", "tool", "environment", "buckyos-root"]),
1433
+ order: Object.freeze(roots.map((pair) => pair.source)),
1287
1434
  limit: IDENTITY_CANDIDATE_LIMIT,
1288
1435
  candidates,
1289
1436
  skipped
1290
1437
  };
1291
1438
  }
1292
- async function identityCandidateView(config, environment2 = readEnvironment()) {
1439
+ async function identityCandidateView(config, environment2 = readEnvironment(), developerModeEnabled = false) {
1293
1440
  if (config.identity) {
1294
1441
  try {
1295
- const material = await resolveIdentityMaterial(config.identity, config, environment2);
1442
+ const material = await resolveIdentityMaterial(config.identity, config, environment2, {
1443
+ includeDeveloper: developerModeEnabled
1444
+ });
1296
1445
  return {
1297
1446
  mode: "explicit",
1298
1447
  order: [material.did],
@@ -1317,7 +1466,9 @@ async function identityCandidateView(config, environment2 = readEnvironment()) {
1317
1466
  };
1318
1467
  }
1319
1468
  }
1320
- const scan = await scanIdentityCandidates(config, environment2);
1469
+ const scan = await scanIdentityCandidates(config, environment2, {
1470
+ includeDeveloper: developerModeEnabled
1471
+ });
1321
1472
  return {
1322
1473
  mode: "search",
1323
1474
  order: scan.order,
@@ -1353,7 +1504,8 @@ async function loadIdentityMaterial(roots, directory, selected) {
1353
1504
  securityRoot: roots.securityRoot,
1354
1505
  documentPath,
1355
1506
  privateKeyPath,
1356
- privateKeyPem
1507
+ privateKeyPem,
1508
+ source: roots.source
1357
1509
  };
1358
1510
  } catch (error) {
1359
1511
  if (!isHostError(error, "NotFound"))
@@ -1438,133 +1590,6 @@ function deduplicatePairs(pairs) {
1438
1590
  return true;
1439
1591
  });
1440
1592
  }
1441
- class BuckyOSRuntimeAdapter {
1442
- #sdk = new BuckyOSSDK("node");
1443
- async initialize(config, session) {
1444
- const authenticated = session.current();
1445
- const zoneHost = resolveZoneHost(config);
1446
- const initialize = this.#sdk.initBuckyOS;
1447
- await initialize.call(this.#sdk, authenticated.principal.appId, {
1448
- appId: authenticated.principal.appId,
1449
- ownerUserId: authenticated.principal.id,
1450
- runtimeType: RuntimeType.AppClient,
1451
- zoneHost,
1452
- defaultProtocol: config.defaultProtocol,
1453
- sessionToken: authenticated.token,
1454
- privateKeySearchPaths: [],
1455
- autoRenew: false,
1456
- verifyHubServiceUrl: resolveServiceUrl(config, "verify-hub")
1457
- });
1458
- return {
1459
- zone: config.zone ?? zoneHost,
1460
- endpoint: config.endpoint ?? `${config.defaultProtocol}${zoneHost}`,
1461
- defaultProtocol: config.defaultProtocol
1462
- };
1463
- }
1464
- }
1465
- class BuckyOSServiceClientRegistry {
1466
- #config;
1467
- #session;
1468
- #clients = /* @__PURE__ */ new Map();
1469
- constructor(config, session) {
1470
- this.#config = config;
1471
- this.#session = session;
1472
- }
1473
- async call(service, method, params, options) {
1474
- let client = this.#clients.get(service);
1475
- if (!client) {
1476
- const RpcClient = buckyos.kRPCClient;
1477
- client = new RpcClient(resolveServiceUrl(this.#config, service), null, null, {
1478
- sessionTokenProvider: async () => (await this.#session.ensureValid()).token
1479
- });
1480
- this.#clients.set(service, client);
1481
- }
1482
- const request = client.call(method, params, { traceId: options.traceId });
1483
- return await withDeadline(request, options.timeoutMs, options.signal);
1484
- }
1485
- async createEventReader(pattern, signal) {
1486
- return await buckyos.createEventReader(pattern, { keepaliveMs: 5e3, signal });
1487
- }
1488
- }
1489
- class InteractiveSession {
1490
- constructor(authentication, clients, connection) {
1491
- this.authentication = authentication;
1492
- this.clients = clients;
1493
- this.connection = connection;
1494
- this.startedAt = Date.now();
1495
- }
1496
- static async create(config, authentication, runtime = new BuckyOSRuntimeAdapter(), clients) {
1497
- await authentication.connect();
1498
- const connection = await runtime.initialize(config, authentication);
1499
- return new InteractiveSession(
1500
- authentication,
1501
- clients ?? new BuckyOSServiceClientRegistry(config, authentication),
1502
- connection
1503
- );
1504
- }
1505
- async reconnect() {
1506
- await this.authentication.reconnect();
1507
- }
1508
- }
1509
- function resolveServiceUrl(config, service) {
1510
- if (config.endpoint) {
1511
- const endpoint = new URL(config.endpoint);
1512
- const marker = endpoint.pathname.indexOf("/kapi/");
1513
- if (marker >= 0)
1514
- endpoint.pathname = `${endpoint.pathname.slice(0, marker)}/kapi/${service}`;
1515
- else if (endpoint.pathname.endsWith("/kapi")) {
1516
- endpoint.pathname = `${endpoint.pathname}/${service}`;
1517
- } else
1518
- endpoint.pathname = `${endpoint.pathname.replace(/\/$/, "")}/kapi/${service}`;
1519
- endpoint.search = "";
1520
- endpoint.hash = "";
1521
- return endpoint.toString().replace(/\/$/, "");
1522
- }
1523
- const host2 = resolveZoneHost(config);
1524
- return `${config.defaultProtocol}${host2}/kapi/${service}`;
1525
- }
1526
- function resolveZoneHost(config) {
1527
- if (config.zone) {
1528
- try {
1529
- return namelib.DID.fromStr(config.zone).toRawHostName();
1530
- } catch {
1531
- throw new UsageError("INVALID_ZONE", `invalid zone host or DID: ${config.zone}`);
1532
- }
1533
- }
1534
- if (config.endpoint)
1535
- return new URL(config.endpoint).host;
1536
- throw new UsageError("CONNECTION_REQUIRED", "an endpoint or zone is required for online commands");
1537
- }
1538
- async function withDeadline(promise, timeoutMs, signal) {
1539
- if (signal?.aborted)
1540
- throw new ToolError("CANCELED", "operation canceled", 8);
1541
- let timer;
1542
- let abortHandler;
1543
- const guards = [
1544
- new Promise((_, reject) => {
1545
- timer = setTimeout(
1546
- () => reject(new ToolError("TIMEOUT", "operation timed out", 8, true)),
1547
- timeoutMs
1548
- );
1549
- })
1550
- ];
1551
- if (signal) {
1552
- guards.push(
1553
- new Promise((_, reject) => {
1554
- abortHandler = () => reject(new ToolError("CANCELED", "operation canceled", 8));
1555
- signal.addEventListener("abort", abortHandler, { once: true });
1556
- })
1557
- );
1558
- }
1559
- try {
1560
- return await Promise.race([promise, ...guards]);
1561
- } finally {
1562
- if (timer !== void 0)
1563
- clearTimeout(timer);
1564
- if (signal && abortHandler)
1565
- signal.removeEventListener("abort", abortHandler);
1566
- }
1567
- }
1568
1593
  const LOGIN_APP_ID = "buckycli";
1569
1594
  const LOGIN_TOKEN_TTL_SECONDS = 10 * 60;
1570
1595
  class AuthenticationSession {
@@ -1575,15 +1600,18 @@ class AuthenticationSession {
1575
1600
  this.#transport = dependencies.transport ?? new SdkAuthenticationTransport();
1576
1601
  this.#readPassword = dependencies.readPassword ?? readSecret;
1577
1602
  this.#readUsername = dependencies.readUsername ?? readVisible;
1603
+ this.#readDevelopmentMode = dependencies.readDevelopmentMode ?? readTargetDevelopmentMode;
1578
1604
  this.#now = dependencies.now ?? Date.now;
1579
1605
  }
1580
1606
  #environment;
1581
1607
  #transport;
1582
1608
  #readPassword;
1583
1609
  #readUsername;
1610
+ #readDevelopmentMode;
1584
1611
  #now;
1585
1612
  #session;
1586
1613
  #acceptedIdentity;
1614
+ #developmentMode;
1587
1615
  #identityAttempts;
1588
1616
  async connect() {
1589
1617
  if (this.#session)
@@ -1631,6 +1659,7 @@ class AuthenticationSession {
1631
1659
  renewable: session.renewable,
1632
1660
  expires_at: expiresAt ?? null,
1633
1661
  remaining_seconds: remainingSeconds,
1662
+ developer_mode: this.#developmentMode?.state ?? null,
1634
1663
  identity_attempts: this.#identityAttempts
1635
1664
  };
1636
1665
  }
@@ -1658,34 +1687,39 @@ class AuthenticationSession {
1658
1687
  const injected = this.#environment.BUCKYOS_APPCLIENT_SESSION_TOKEN?.trim();
1659
1688
  if (injected)
1660
1689
  return externalSession(injected, "environment", this.#now());
1661
- if (this.#acceptedIdentity)
1690
+ if (this.#acceptedIdentity) {
1691
+ if (this.#acceptedIdentity.source === "buckycli-home") {
1692
+ await this.#requireDeveloperIdentityAccess();
1693
+ }
1662
1694
  return await this.#loginWithIdentity(this.#acceptedIdentity);
1695
+ }
1663
1696
  if (this.config.identity) {
1664
- const material = await resolveIdentityMaterial(
1665
- this.config.identity,
1666
- this.config,
1667
- this.#environment
1668
- );
1697
+ const material = await this.#resolveExplicitIdentity(this.config.identity);
1669
1698
  const session = await this.#loginWithIdentity(material);
1670
1699
  this.#acceptedIdentity = material;
1671
1700
  return session;
1672
1701
  }
1673
- const candidates = await scanIdentityCandidates(this.config, this.#environment);
1674
1702
  this.#identityAttempts = [];
1675
- for (const candidate of candidates.candidates) {
1676
- try {
1677
- const session = await this.#loginWithIdentity(candidate.material);
1678
- this.#acceptedIdentity = candidate.material;
1679
- return session;
1680
- } catch (error) {
1681
- if (!isIdentityRejection(error))
1682
- throw error;
1683
- this.#identityAttempts.push({
1684
- identity: candidate.material.did,
1685
- code: error.code,
1686
- message: sanitizeMessage(error.message)
1687
- });
1688
- }
1703
+ const operations = await scanIdentityCandidates(this.config, this.#environment);
1704
+ const operationsSession = await this.#tryIdentityCandidates(operations.candidates);
1705
+ if (operationsSession)
1706
+ return operationsSession;
1707
+ const developmentMode = await this.#checkDevelopmentMode();
1708
+ if (developmentMode.state === "enabled" && operations.candidates.length < IDENTITY_CANDIDATE_LIMIT) {
1709
+ const operationsDids = new Set(
1710
+ operations.candidates.map((candidate) => candidate.material.did)
1711
+ );
1712
+ const developer = await scanIdentityCandidates(this.config, this.#environment, {
1713
+ includeOperations: false,
1714
+ includeDeveloper: true
1715
+ });
1716
+ const remaining = IDENTITY_CANDIDATE_LIMIT - operations.candidates.length;
1717
+ const candidates = developer.candidates.filter(
1718
+ (candidate) => !operationsDids.has(candidate.material.did)
1719
+ ).slice(0, remaining);
1720
+ const developerSession = await this.#tryIdentityCandidates(candidates);
1721
+ if (developerSession)
1722
+ return developerSession;
1689
1723
  }
1690
1724
  if (this.#identityAttempts.length > 0) {
1691
1725
  throw new ToolError(
@@ -1716,6 +1750,40 @@ class AuthenticationSession {
1716
1750
  );
1717
1751
  return authenticatedSession(token, "password", true, this.#now());
1718
1752
  }
1753
+ async #resolveExplicitIdentity(selectedIdentity) {
1754
+ if (this.config.implicitDeviceIdentity) {
1755
+ return await resolveIdentityMaterial(selectedIdentity, this.config, this.#environment);
1756
+ }
1757
+ try {
1758
+ return await resolveIdentityMaterial(selectedIdentity, this.config, this.#environment);
1759
+ } catch (error) {
1760
+ if (!(error instanceof ToolError) || error.code !== "IDENTITY_NOT_FOUND")
1761
+ throw error;
1762
+ }
1763
+ await this.#requireDeveloperIdentityAccess();
1764
+ return await resolveIdentityMaterial(selectedIdentity, this.config, this.#environment, {
1765
+ includeOperations: false,
1766
+ includeDeveloper: true
1767
+ });
1768
+ }
1769
+ async #tryIdentityCandidates(candidates) {
1770
+ for (const candidate of candidates) {
1771
+ try {
1772
+ const session = await this.#loginWithIdentity(candidate.material);
1773
+ this.#acceptedIdentity = candidate.material;
1774
+ return session;
1775
+ } catch (error) {
1776
+ if (!isIdentityRejection(error))
1777
+ throw error;
1778
+ this.#identityAttempts.push({
1779
+ identity: candidate.material.did,
1780
+ code: error.code,
1781
+ message: sanitizeMessage(error.message)
1782
+ });
1783
+ }
1784
+ }
1785
+ return void 0;
1786
+ }
1719
1787
  async #loginWithIdentity(material) {
1720
1788
  const loginJwt = await createLoginJwt(
1721
1789
  material.subject,
@@ -1731,6 +1799,30 @@ class AuthenticationSession {
1731
1799
  );
1732
1800
  return authenticatedSession(token, "identity", true, this.#now());
1733
1801
  }
1802
+ async #checkDevelopmentMode() {
1803
+ this.#developmentMode = await this.#readDevelopmentMode(this.config);
1804
+ return this.#developmentMode;
1805
+ }
1806
+ async #requireDeveloperIdentityAccess() {
1807
+ const decision = await this.#checkDevelopmentMode();
1808
+ if (decision.state === "enabled")
1809
+ return;
1810
+ this.#acceptedIdentity = void 0;
1811
+ if (decision.state === "disabled") {
1812
+ throw new ToolError(
1813
+ "DEVELOPER_IDENTITY_DISABLED",
1814
+ "the target Zone has developer mode disabled; local developer identities are unavailable",
1815
+ EXIT_AUTH
1816
+ );
1817
+ }
1818
+ throw new ToolError(
1819
+ "DEVELOPER_MODE_UNAVAILABLE",
1820
+ "the target Zone developer-mode configuration could not be verified",
1821
+ EXIT_AUTH,
1822
+ true,
1823
+ { reason: sanitizeMessage(decision.reason) }
1824
+ );
1825
+ }
1734
1826
  }
1735
1827
  function isIdentityRejection(error) {
1736
1828
  return error instanceof ToolError && IDENTITY_REJECTION_CODES.includes(error.code);
@@ -2005,8 +2097,6 @@ const GLOBAL_OPTIONS = [
2005
2097
  { name: "zone", property: "zone", type: "string", scope: "session" },
2006
2098
  { name: "endpoint", property: "endpoint", type: "string", scope: "session" },
2007
2099
  { name: "identity", property: "identity", type: "string", scope: "session" },
2008
- { name: "identity-root", property: "identityRoot", type: "string", scope: "session" },
2009
- { name: "security-root", property: "securityRoot", type: "string", scope: "session" },
2010
2100
  {
2011
2101
  name: "session-token",
2012
2102
  property: "sessionToken",
@@ -4758,7 +4848,7 @@ function invalid(stage, message, entry) {
4758
4848
  function stableJsonDigest(value) {
4759
4849
  return sha256Bytes(new TextEncoder().encode(ndn.toCanonicalJsonString(value)));
4760
4850
  }
4761
- const PACKAGE_VERSION = "0.7.116";
4851
+ const PACKAGE_VERSION = "0.7.119";
4762
4852
  const TOOL_VERSION = PACKAGE_VERSION;
4763
4853
  const SDK_VERSION = PACKAGE_VERSION;
4764
4854
  const PROTOCOL_VERSION = "1";