buckyos 0.7.119 → 0.7.121

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 (44) hide show
  1. package/cli/README.md +24 -3
  2. package/cli/core/app.ts +28 -0
  3. package/cli/deno.json +2 -1
  4. package/cli/dist/cli.mjs +777 -169
  5. package/cli/dist/cli.mjs.map +1 -1
  6. package/cli/modules/app.ts +213 -94
  7. package/cli/modules/provision.ts +690 -0
  8. package/cli/runtime/host.ts +25 -2
  9. package/cli/runtime/host_deno.ts +4 -0
  10. package/cli/runtime/host_node.ts +4 -0
  11. package/cli/version.ts +1 -1
  12. package/dist/browser.cjs +1 -1
  13. package/dist/browser.mjs +124 -124
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.mjs +124 -124
  16. package/dist/{ndm_proxy-c00bc561.mjs → ndm_proxy-8f547002.mjs} +113 -216
  17. package/dist/ndm_proxy-8f547002.mjs.map +1 -0
  18. package/dist/ndm_proxy-93a8869b.js +13 -0
  19. package/dist/ndm_proxy-93a8869b.js.map +1 -0
  20. package/dist/ndn_types-44c69036.js +2 -0
  21. package/dist/ndn_types-44c69036.js.map +1 -0
  22. package/dist/{ndn_types-f6c08d20.mjs → ndn_types-d768245f.mjs} +149 -39
  23. package/dist/ndn_types-d768245f.mjs.map +1 -0
  24. package/dist/node.cjs +1 -1
  25. package/dist/node.mjs +124 -124
  26. package/dist/provision.cjs +6 -6
  27. package/dist/provision.cjs.map +1 -1
  28. package/dist/provision.d.ts +2 -0
  29. package/dist/provision.d.ts.map +1 -1
  30. package/dist/provision.mjs +1084 -35
  31. package/dist/provision.mjs.map +1 -1
  32. package/dist/provision_activate.d.ts +146 -0
  33. package/dist/provision_activate.d.ts.map +1 -0
  34. package/dist/{tus_client-573066b3.mjs → tus_client-b1c35e8c.mjs} +2 -2
  35. package/dist/{tus_client-573066b3.mjs.map → tus_client-b1c35e8c.mjs.map} +1 -1
  36. package/dist/{tus_client-83ad0f4f.js → tus_client-d0798623.js} +2 -2
  37. package/dist/{tus_client-83ad0f4f.js.map → tus_client-d0798623.js.map} +1 -1
  38. package/package.json +1 -1
  39. package/dist/ndm_proxy-68354a89.js +0 -13
  40. package/dist/ndm_proxy-68354a89.js.map +0 -1
  41. package/dist/ndm_proxy-c00bc561.mjs.map +0 -1
  42. package/dist/ndn_types-859bbd48.js +0 -2
  43. package/dist/ndn_types-859bbd48.js.map +0 -1
  44. package/dist/ndn_types-f6c08d20.mjs.map +0 -1
package/cli/dist/cli.mjs CHANGED
@@ -3,6 +3,7 @@ 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
5
  import { BuckyOSSDK, RuntimeType, buckyos, namelib, VerifyHubClient, createAppInstanceId, parseSessionTokenClaims, ndn, ndm_proxy } from "buckyos/node";
6
+ import * as provisionSdk from "buckyos/provision";
6
7
  import { createHash } from "node:crypto";
7
8
  import { readFile, writeFile, readdir, stat, lstat, realpath, mkdtemp, rename, rm, mkdir, chmod, symlink, copyFile, open } from "node:fs/promises";
8
9
  import { createReadStream, createWriteStream } from "node:fs";
@@ -21,6 +22,7 @@ const EXIT_AUTH = 3;
21
22
  const EXIT_PERMISSION = 4;
22
23
  const EXIT_UNAVAILABLE = 5;
23
24
  const EXIT_OPERATION = 6;
25
+ const EXIT_PARTIAL = 7;
24
26
  const EXIT_TIMEOUT = 8;
25
27
  const EXIT_INTERNAL = 9;
26
28
  class ToolError extends Error {
@@ -506,15 +508,21 @@ function buildDistributionPolicy(options) {
506
508
  if (options.distribution === "system")
507
509
  readPaths.add(buckyosRoot);
508
510
  const parsed = collectArgumentPaths(argv);
511
+ if (options.distribution === "system" && parsed.module === "app" && ["fetch", "install"].includes(parsed.verb ?? "")) {
512
+ writePaths.add(path.join(buckyosRoot, "cache", "control_panel", "pikg_staging", "incoming"));
513
+ }
509
514
  for (const candidate of parsed.read)
510
515
  readPaths.add(resolveInputPath(candidate, cwd, path));
516
+ for (const candidate of parsed.readParents) {
517
+ readPaths.add(path.dirname(resolveInputPath(candidate, cwd, path)));
518
+ }
511
519
  for (const candidate of parsed.write)
512
520
  writePaths.add(resolveInputPath(candidate, cwd, path));
513
521
  for (const candidate of parsed.writeParents) {
514
522
  writePaths.add(path.dirname(resolveInputPath(candidate, cwd, path)));
515
523
  }
516
524
  const subprocesses = parsed.module === "pikg" && ["init", "build"].includes(parsed.verb ?? "") ? ["docker"] : [];
517
- const network = parsed.module !== void 0 && !["pikg", "command", "completion", "config"].includes(parsed.module);
525
+ const network = parsed.module !== void 0 && !["pikg", "command", "completion", "config", "provision"].includes(parsed.module);
518
526
  const allowedEnvironment = options.distribution === "developer" ? TOOL_ENVIRONMENT_NAMES.filter((name) => name !== "BUCKYOS_ROOT") : TOOL_ENVIRONMENT_NAMES;
519
527
  return Object.freeze({
520
528
  name: options.distribution === "system" ? "system-default" : "developer-default",
@@ -539,6 +547,7 @@ function resolveInputPath(value, cwd, path) {
539
547
  }
540
548
  function collectArgumentPaths(argv) {
541
549
  const read = [];
550
+ const readParents = [];
542
551
  const write = [];
543
552
  const writeParents = [];
544
553
  let module;
@@ -614,6 +623,14 @@ function collectArgumentPaths(argv) {
614
623
  write.push(value);
615
624
  else if (name === "file" && module === "system-config" && verb === "set-file") {
616
625
  read.push(value);
626
+ } else if (name === "root" && module === "provision") {
627
+ read.push(value);
628
+ if (verb === "activate")
629
+ write.push(value);
630
+ } else if (name === "owner-key-backup" && module === "provision") {
631
+ readParents.push(value);
632
+ if (verb === "activate")
633
+ writeParents.push(value);
617
634
  } else if (!valueOptions.has(name))
618
635
  continue;
619
636
  continue;
@@ -629,7 +646,7 @@ function collectArgumentPaths(argv) {
629
646
  writeParents.push(token);
630
647
  }
631
648
  }
632
- return { read, write, writeParents, module, verb };
649
+ return { read, readParents, write, writeParents, module, verb };
633
650
  }
634
651
  function policyView(host2) {
635
652
  return {
@@ -2477,7 +2494,7 @@ function replPrompt(config) {
2477
2494
  return `buckyos[${profile}|${zone}|${identity}]> `;
2478
2495
  }
2479
2496
  const EMPTY_INPUT$1 = { type: "object", properties: {}, additionalProperties: false };
2480
- const OBJECT_OUTPUT$7 = { type: "object", additionalProperties: true };
2497
+ const OBJECT_OUTPUT$8 = { type: "object", additionalProperties: true };
2481
2498
  function createAuthModule() {
2482
2499
  return {
2483
2500
  name: "auth",
@@ -2487,7 +2504,7 @@ function createAuthModule() {
2487
2504
  verb: "whoami",
2488
2505
  summary: "Show the effective principal and application identity",
2489
2506
  inputSchema: EMPTY_INPUT$1,
2490
- outputSchema: OBJECT_OUTPUT$7,
2507
+ outputSchema: OBJECT_OUTPUT$8,
2491
2508
  resultSchemaVersion: 1,
2492
2509
  access: { mode: "fixed", level: "read" },
2493
2510
  asyncMode: "sync",
@@ -2505,7 +2522,7 @@ function createAuthModule() {
2505
2522
  verb: "session-status",
2506
2523
  summary: "Show the in-memory session state without exposing credentials",
2507
2524
  inputSchema: EMPTY_INPUT$1,
2508
- outputSchema: OBJECT_OUTPUT$7,
2525
+ outputSchema: OBJECT_OUTPUT$8,
2509
2526
  resultSchemaVersion: 1,
2510
2527
  access: { mode: "fixed", level: "read" },
2511
2528
  asyncMode: "sync",
@@ -2526,7 +2543,7 @@ const EMPTY_INPUT = {
2526
2543
  properties: {},
2527
2544
  additionalProperties: false
2528
2545
  };
2529
- const OBJECT_OUTPUT$6 = { type: "object", additionalProperties: true };
2546
+ const OBJECT_OUTPUT$7 = { type: "object", additionalProperties: true };
2530
2547
  function createCoreModules(registry) {
2531
2548
  return [createCommandModule(registry), createConfigModule(), createCompletionModule(registry)];
2532
2549
  }
@@ -2539,7 +2556,7 @@ function createCommandModule(registry) {
2539
2556
  verb: "list",
2540
2557
  summary: "List registered modules and commands",
2541
2558
  inputSchema: EMPTY_INPUT,
2542
- outputSchema: OBJECT_OUTPUT$6,
2559
+ outputSchema: OBJECT_OUTPUT$7,
2543
2560
  resultSchemaVersion: 1,
2544
2561
  access: { mode: "fixed", level: "read" },
2545
2562
  asyncMode: "sync",
@@ -2572,7 +2589,7 @@ function createCommandModule(registry) {
2572
2589
  required: ["target_module", "target_verb"],
2573
2590
  additionalProperties: false
2574
2591
  },
2575
- outputSchema: OBJECT_OUTPUT$6,
2592
+ outputSchema: OBJECT_OUTPUT$7,
2576
2593
  resultSchemaVersion: 1,
2577
2594
  access: { mode: "fixed", level: "read" },
2578
2595
  asyncMode: "sync",
@@ -2594,7 +2611,7 @@ function createConfigModule() {
2594
2611
  verb: "list",
2595
2612
  summary: "List the global configuration and available profiles",
2596
2613
  inputSchema: EMPTY_INPUT,
2597
- outputSchema: OBJECT_OUTPUT$6,
2614
+ outputSchema: OBJECT_OUTPUT$7,
2598
2615
  resultSchemaVersion: 1,
2599
2616
  access: { mode: "fixed", level: "read" },
2600
2617
  asyncMode: "sync",
@@ -2625,7 +2642,7 @@ function createConfigModule() {
2625
2642
  },
2626
2643
  additionalProperties: false
2627
2644
  },
2628
- outputSchema: OBJECT_OUTPUT$6,
2645
+ outputSchema: OBJECT_OUTPUT$7,
2629
2646
  resultSchemaVersion: 1,
2630
2647
  access: { mode: "fixed", level: "read" },
2631
2648
  asyncMode: "sync",
@@ -2635,11 +2652,11 @@ function createConfigModule() {
2635
2652
  "buckyos config get zone --profile-name production"
2636
2653
  ],
2637
2654
  handler: async (ctx, input) => {
2638
- const profileName = optionalString$3(input.profile_name);
2655
+ const profileName = optionalString$4(input.profile_name);
2639
2656
  const value = profileName ? await ctx.configStore.readProfile(profileName) : await ctx.configStore.readConfig();
2640
2657
  if (!value)
2641
2658
  throw new UsageError("PROFILE_NOT_FOUND", `profile not found: ${profileName}`);
2642
- const key = optionalString$3(input.key);
2659
+ const key = optionalString$4(input.key);
2643
2660
  if (!key)
2644
2661
  return { value };
2645
2662
  if (!Object.hasOwn(value, key)) {
@@ -2671,7 +2688,7 @@ function createConfigModule() {
2671
2688
  required: ["key", "value"],
2672
2689
  additionalProperties: false
2673
2690
  },
2674
- outputSchema: OBJECT_OUTPUT$6,
2691
+ outputSchema: OBJECT_OUTPUT$7,
2675
2692
  resultSchemaVersion: 1,
2676
2693
  access: { mode: "fixed", level: "write" },
2677
2694
  asyncMode: "sync",
@@ -2683,7 +2700,7 @@ function createConfigModule() {
2683
2700
  handler: async (ctx, input) => {
2684
2701
  const key = String(input.key);
2685
2702
  const rawValue = String(input.value);
2686
- const profileName = optionalString$3(input.profile_name);
2703
+ const profileName = optionalString$4(input.profile_name);
2687
2704
  if (profileName) {
2688
2705
  validateProfileName(profileName);
2689
2706
  const profile = await ctx.configStore.readProfile(profileName) ?? { schema_version: 1 };
@@ -2711,7 +2728,7 @@ function createConfigModule() {
2711
2728
  required: ["profile_name"],
2712
2729
  additionalProperties: false
2713
2730
  },
2714
- outputSchema: OBJECT_OUTPUT$6,
2731
+ outputSchema: OBJECT_OUTPUT$7,
2715
2732
  resultSchemaVersion: 1,
2716
2733
  access: { mode: "fixed", level: "write" },
2717
2734
  asyncMode: "sync",
@@ -2743,7 +2760,7 @@ function createConfigModule() {
2743
2760
  properties: { effective: { type: "boolean" } },
2744
2761
  additionalProperties: false
2745
2762
  },
2746
- outputSchema: OBJECT_OUTPUT$6,
2763
+ outputSchema: OBJECT_OUTPUT$7,
2747
2764
  resultSchemaVersion: 1,
2748
2765
  access: { mode: "fixed", level: "read" },
2749
2766
  asyncMode: "sync",
@@ -2829,7 +2846,7 @@ function outputValue(value) {
2829
2846
  }
2830
2847
  return value;
2831
2848
  }
2832
- function optionalString$3(value) {
2849
+ function optionalString$4(value) {
2833
2850
  return typeof value === "string" && value ? value : void 0;
2834
2851
  }
2835
2852
  function completionScript(registry, shell) {
@@ -2888,7 +2905,7 @@ function createSystemModule() {
2888
2905
  };
2889
2906
  }
2890
2907
  const SERVICE_NAME = "system_config";
2891
- const OBJECT_OUTPUT$5 = { type: "object", additionalProperties: false };
2908
+ const OBJECT_OUTPUT$6 = { type: "object", additionalProperties: false };
2892
2909
  const KEY_INPUT = {
2893
2910
  type: "object",
2894
2911
  properties: { key: { type: "string", minLength: 1 } },
@@ -2906,7 +2923,7 @@ function createSystemConfigModule() {
2906
2923
  positionals: [{ name: "key", description: "System-config key" }],
2907
2924
  inputSchema: KEY_INPUT,
2908
2925
  outputSchema: {
2909
- ...OBJECT_OUTPUT$5,
2926
+ ...OBJECT_OUTPUT$6,
2910
2927
  properties: {
2911
2928
  key: { type: "string" },
2912
2929
  value: { type: "string" },
@@ -2949,7 +2966,7 @@ function createSystemConfigModule() {
2949
2966
  additionalProperties: false
2950
2967
  },
2951
2968
  outputSchema: {
2952
- ...OBJECT_OUTPUT$5,
2969
+ ...OBJECT_OUTPUT$6,
2953
2970
  properties: {
2954
2971
  key: { type: "string" },
2955
2972
  items: { type: "array", items: { type: "string" } }
@@ -3003,7 +3020,7 @@ function setCommand() {
3003
3020
  additionalProperties: false
3004
3021
  },
3005
3022
  outputSchema: {
3006
- ...OBJECT_OUTPUT$5,
3023
+ ...OBJECT_OUTPUT$6,
3007
3024
  properties: { key: { type: "string" }, updated: { type: "boolean" } },
3008
3025
  required: ["key", "updated"]
3009
3026
  },
@@ -3042,7 +3059,7 @@ function setFileCommand() {
3042
3059
  additionalProperties: false
3043
3060
  },
3044
3061
  outputSchema: {
3045
- ...OBJECT_OUTPUT$5,
3062
+ ...OBJECT_OUTPUT$6,
3046
3063
  properties: { key: { type: "string" }, updated: { type: "boolean" } },
3047
3064
  required: ["key", "updated"]
3048
3065
  },
@@ -3079,7 +3096,7 @@ function mutationCommand(options) {
3079
3096
  additionalProperties: false
3080
3097
  },
3081
3098
  outputSchema: {
3082
- ...OBJECT_OUTPUT$5,
3099
+ ...OBJECT_OUTPUT$6,
3083
3100
  properties: {
3084
3101
  key: { type: "string" },
3085
3102
  [options.resultProperty]: { type: "boolean" }
@@ -4127,7 +4144,7 @@ function validateServiceConfigTips(value, label) {
4127
4144
  } else {
4128
4145
  throw invalid("schema", `${label}.service_endpoints.${name}.expose.route.type is invalid`);
4129
4146
  }
4130
- optionalString$2(expose.scope, `${label}.service_endpoints.${name}.expose.scope`);
4147
+ optionalString$3(expose.scope, `${label}.service_endpoints.${name}.expose.scope`);
4131
4148
  optionalBoolean(
4132
4149
  expose.allow_guest,
4133
4150
  `${label}.service_endpoints.${name}.expose.allow_guest`
@@ -4181,7 +4198,7 @@ function validateServiceConfigTips(value, label) {
4181
4198
  throw invalid("schema", `${label}.rdb_instances.${name}.schema.${backend} is invalid`);
4182
4199
  }
4183
4200
  }
4184
- optionalString$2(database.connection, `${label}.rdb_instances.${name}.connection`);
4201
+ optionalString$3(database.connection, `${label}.rdb_instances.${name}.connection`);
4185
4202
  }
4186
4203
  if (config.instance_volume !== void 0) {
4187
4204
  const volume = expectObject$3(config.instance_volume, `${label}.instance_volume`);
@@ -4209,8 +4226,8 @@ function validateServiceConfigTips(value, label) {
4209
4226
  validateStringMap(environment2.description ?? {}, `${label}.bash_envs.${name}.description`);
4210
4227
  }
4211
4228
  validateStringMap(config.runtime_caps ?? {}, `${label}.runtime_caps`);
4212
- optionalString$2(config.container_param, `${label}.container_param`);
4213
- optionalString$2(config.start_param, `${label}.start_param`);
4229
+ optionalString$3(config.container_param, `${label}.container_param`);
4230
+ optionalString$3(config.start_param, `${label}.start_param`);
4214
4231
  return config;
4215
4232
  }
4216
4233
  function appDocObjectId(value) {
@@ -4604,7 +4621,7 @@ function validateAppDocShape(appDoc) {
4604
4621
  }
4605
4622
  if (appDoc.name !== void 0)
4606
4623
  expectNonEmptyString(appDoc.name, "APPDOC.name");
4607
- optionalString$2(appDoc.copyright, "APPDOC.copyright");
4624
+ optionalString$3(appDoc.copyright, "APPDOC.copyright");
4608
4625
  for (const field of ["tags", "categories"]) {
4609
4626
  const values = appDoc[field];
4610
4627
  if (values !== void 0 && (!Array.isArray(values) || values.length === 0 || values.some((value) => typeof value !== "string"))) {
@@ -4814,7 +4831,7 @@ function optionalBoolean(value, label) {
4814
4831
  throw invalid("schema", `${label} must be boolean`);
4815
4832
  }
4816
4833
  }
4817
- function optionalString$2(value, label) {
4834
+ function optionalString$3(value, label) {
4818
4835
  if (value !== void 0 && typeof value !== "string") {
4819
4836
  throw invalid("schema", `${label} must be a string`);
4820
4837
  }
@@ -4848,7 +4865,7 @@ function invalid(stage, message, entry) {
4848
4865
  function stableJsonDigest(value) {
4849
4866
  return sha256Bytes(new TextEncoder().encode(ndn.toCanonicalJsonString(value)));
4850
4867
  }
4851
- const PACKAGE_VERSION = "0.7.119";
4868
+ const PACKAGE_VERSION = "0.7.121";
4852
4869
  const TOOL_VERSION = PACKAGE_VERSION;
4853
4870
  const SDK_VERSION = PACKAGE_VERSION;
4854
4871
  const PROTOCOL_VERSION = "1";
@@ -4865,7 +4882,7 @@ const VERSION$1 = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
4865
4882
  const SHA256_ID = /^sha256:[0-9a-f]{64}$/;
4866
4883
  const OBJECT_ID = /^(?:appdoc|pkg):[0-9a-f]{64}$/;
4867
4884
  const SAFE_GENERATED_FILE = /^[A-Za-z0-9._-]+$/;
4868
- const OBJECT_OUTPUT$4 = { type: "object", additionalProperties: true };
4885
+ const OBJECT_OUTPUT$5 = { type: "object", additionalProperties: true };
4869
4886
  function packageEnvironmentQualifier(key, selector) {
4870
4887
  const effective = selector ?? derivedSelector(key);
4871
4888
  const os = effective?.os;
@@ -4902,7 +4919,7 @@ function createPikgModule(dependencies = {}) {
4902
4919
  properties: { meta_dir: { type: "string", minLength: 1 } },
4903
4920
  additionalProperties: false
4904
4921
  },
4905
- outputSchema: OBJECT_OUTPUT$4,
4922
+ outputSchema: OBJECT_OUTPUT$5,
4906
4923
  resultSchemaVersion: 1,
4907
4924
  access: { mode: "fixed", level: "read" },
4908
4925
  asyncMode: "sync",
@@ -4949,7 +4966,7 @@ function createPikgModule(dependencies = {}) {
4949
4966
  },
4950
4967
  additionalProperties: false
4951
4968
  },
4952
- outputSchema: OBJECT_OUTPUT$4,
4969
+ outputSchema: OBJECT_OUTPUT$5,
4953
4970
  resultSchemaVersion: 1,
4954
4971
  access: { mode: "fixed", level: "write" },
4955
4972
  asyncMode: "sync",
@@ -4973,7 +4990,7 @@ function createPikgModule(dependencies = {}) {
4973
4990
  properties: { meta_dir: { type: "string", minLength: 1 } },
4974
4991
  additionalProperties: false
4975
4992
  },
4976
- outputSchema: OBJECT_OUTPUT$4,
4993
+ outputSchema: OBJECT_OUTPUT$5,
4977
4994
  resultSchemaVersion: 1,
4978
4995
  access: { mode: "fixed", level: "write" },
4979
4996
  asyncMode: "sync",
@@ -4994,7 +5011,7 @@ function createPikgModule(dependencies = {}) {
4994
5011
  properties: { dist_dir: { type: "string", minLength: 1 } },
4995
5012
  additionalProperties: false
4996
5013
  },
4997
- outputSchema: OBJECT_OUTPUT$4,
5014
+ outputSchema: OBJECT_OUTPUT$5,
4998
5015
  resultSchemaVersion: 1,
4999
5016
  access: { mode: "fixed", level: "write" },
5000
5017
  asyncMode: "sync",
@@ -5014,7 +5031,7 @@ function createPikgModule(dependencies = {}) {
5014
5031
  required: ["pikg_path"],
5015
5032
  additionalProperties: false
5016
5033
  },
5017
- outputSchema: OBJECT_OUTPUT$4,
5034
+ outputSchema: OBJECT_OUTPUT$5,
5018
5035
  resultSchemaVersion: 1,
5019
5036
  access: { mode: "fixed", level: "read" },
5020
5037
  asyncMode: "sync",
@@ -5035,7 +5052,7 @@ function createPikgModule(dependencies = {}) {
5035
5052
  properties: { meta_dir: { type: "string", minLength: 1 } },
5036
5053
  additionalProperties: false
5037
5054
  },
5038
- outputSchema: OBJECT_OUTPUT$4,
5055
+ outputSchema: OBJECT_OUTPUT$5,
5039
5056
  resultSchemaVersion: 1,
5040
5057
  access: { mode: "fixed", level: "destructive" },
5041
5058
  asyncMode: "sync",
@@ -6226,16 +6243,16 @@ async function observeTask(ctx, taskId) {
6226
6243
  const envelope = expectObject$2(response, "TaskManager get_task response");
6227
6244
  const task = expectObject$2(envelope.task ?? envelope, "TaskManager task");
6228
6245
  const phase = expectString$2(task.phase, "task.phase");
6229
- const outcome = optionalString$1(task.outcome);
6246
+ const outcome = optionalString$2(task.outcome);
6230
6247
  const taskError = isObject$1(task.error) ? task.error : void 0;
6231
6248
  return {
6232
6249
  revision: typeof task.revision === "number" ? task.revision : void 0,
6233
6250
  phase,
6234
6251
  outcome,
6235
- message: optionalString$1(task.message),
6252
+ message: optionalString$2(task.message),
6236
6253
  error: taskError ? {
6237
- code: optionalString$1(taskError.code),
6238
- message: optionalString$1(taskError.message),
6254
+ code: optionalString$2(taskError.code),
6255
+ message: optionalString$2(taskError.message),
6239
6256
  retryable: typeof taskError.retryable === "boolean" ? taskError.retryable : void 0,
6240
6257
  details: isObject$1(taskError.detail) ? taskError.detail : void 0
6241
6258
  } : void 0,
@@ -6255,7 +6272,7 @@ function normalizeTaskErrorCode(code, outcome) {
6255
6272
  return code.trim().replaceAll(/[^A-Za-z0-9]+/g, "_").toUpperCase();
6256
6273
  return outcome === "Canceled" ? "CANCELED" : "TASK_FAILED";
6257
6274
  }
6258
- function optionalString$1(value) {
6275
+ function optionalString$2(value) {
6259
6276
  return typeof value === "string" ? value : void 0;
6260
6277
  }
6261
6278
  function expectString$2(value, label) {
@@ -6295,7 +6312,7 @@ const CONTROL_PANEL_SERVICE = "control-panel";
6295
6312
  const PLAN_SCHEMA_VERSION = 4;
6296
6313
  const STAGING_RELEASE_TIMEOUT_MS = 5e3;
6297
6314
  const ZIP_LOCAL_MAGIC = [80, 75, 3, 4];
6298
- const OBJECT_OUTPUT$3 = { type: "object", additionalProperties: true };
6315
+ const OBJECT_OUTPUT$4 = { type: "object", additionalProperties: true };
6299
6316
  const INSTALL_POLICIES = [
6300
6317
  "strict-public",
6301
6318
  "normal",
@@ -6314,7 +6331,7 @@ function createAppModule(dependencies = {}) {
6314
6331
  lifecycleCommand("start", "Start an installed App"),
6315
6332
  lifecycleCommand("stop", "Stop an installed App"),
6316
6333
  restartCommand(),
6317
- statusCommand()
6334
+ statusCommand$1()
6318
6335
  ];
6319
6336
  return {
6320
6337
  name: "app",
@@ -6351,7 +6368,7 @@ function fetchCommand(dependencies) {
6351
6368
  target: { type: "object", additionalProperties: true },
6352
6369
  install_params: { type: "object", additionalProperties: true }
6353
6370
  }),
6354
- outputSchema: OBJECT_OUTPUT$3,
6371
+ outputSchema: OBJECT_OUTPUT$4,
6355
6372
  resultSchemaVersion: 1,
6356
6373
  access: { mode: "fixed", level: "read" },
6357
6374
  asyncMode: "sync",
@@ -6368,7 +6385,7 @@ function listCommand$1() {
6368
6385
  verb: "list",
6369
6386
  summary: "List visible installed Apps",
6370
6387
  inputSchema: emptyInputSchema(),
6371
- outputSchema: OBJECT_OUTPUT$3,
6388
+ outputSchema: OBJECT_OUTPUT$4,
6372
6389
  resultSchemaVersion: 1,
6373
6390
  access: { mode: "fixed", level: "read" },
6374
6391
  asyncMode: "sync",
@@ -6426,7 +6443,7 @@ function installCommand(dependencies) {
6426
6443
  no_wait: { type: "boolean" },
6427
6444
  policy: { type: "string", enum: [...INSTALL_POLICIES] }
6428
6445
  }),
6429
- outputSchema: OBJECT_OUTPUT$3,
6446
+ outputSchema: OBJECT_OUTPUT$4,
6430
6447
  resultSchemaVersion: 1,
6431
6448
  access: { mode: "fixed", level: "write" },
6432
6449
  asyncMode: "either",
@@ -6470,7 +6487,7 @@ function upgradeCommand(dependencies) {
6470
6487
  },
6471
6488
  additionalProperties: false
6472
6489
  },
6473
- outputSchema: OBJECT_OUTPUT$3,
6490
+ outputSchema: OBJECT_OUTPUT$4,
6474
6491
  resultSchemaVersion: 1,
6475
6492
  access: { mode: "fixed", level: "write" },
6476
6493
  asyncMode: "either",
@@ -6587,7 +6604,7 @@ function restartCommand() {
6587
6604
  examples: ["buckyos app restart app1"]
6588
6605
  };
6589
6606
  }
6590
- function statusCommand() {
6607
+ function statusCommand$1() {
6591
6608
  return {
6592
6609
  verb: "status",
6593
6610
  summary: "Show desired, task, scheduled, runtime, version, and readiness state",
@@ -6599,7 +6616,7 @@ function statusCommand() {
6599
6616
  properties: { app_name: { type: "string", minLength: 1 } },
6600
6617
  additionalProperties: false
6601
6618
  },
6602
- outputSchema: OBJECT_OUTPUT$3,
6619
+ outputSchema: OBJECT_OUTPUT$4,
6603
6620
  resultSchemaVersion: 1,
6604
6621
  access: { mode: "fixed", level: "read" },
6605
6622
  asyncMode: "sync",
@@ -6640,7 +6657,7 @@ async function fetchApp(ctx, input, dependencies) {
6640
6657
  const source = await prepareSource(ctx, input, "inspect", dependencies);
6641
6658
  try {
6642
6659
  let inspection = await inspectSource(ctx, source, input);
6643
- const planPathInput = optionalString(input.plan);
6660
+ const planPathInput = optionalString$1(input.plan);
6644
6661
  let planPath;
6645
6662
  if (planPathInput) {
6646
6663
  inspection = await finalizePlanChoices(ctx, source, input, inspection);
@@ -6664,7 +6681,7 @@ async function fetchApp(ctx, input, dependencies) {
6664
6681
  }
6665
6682
  async function installApp(ctx, input, dependencies) {
6666
6683
  rejectDryRunNoWait(input);
6667
- const submittedPlan = optionalString(input.plan) ? await readPlanFile(resolveFromCwd(ctx, String(input.plan))) : void 0;
6684
+ const submittedPlan = optionalString$1(input.plan) ? await readPlanFile(resolveFromCwd(ctx, String(input.plan))) : void 0;
6668
6685
  const source = await prepareSource(ctx, input, "install", dependencies);
6669
6686
  let keepStaging = false;
6670
6687
  try {
@@ -6709,7 +6726,7 @@ async function installApp(ctx, input, dependencies) {
6709
6726
  ...sourceRpcParams(source),
6710
6727
  ...planScopeAndOptions(inspection.plan),
6711
6728
  options: installOptions(input),
6712
- plan: submittedPlan ?? null,
6729
+ plan: submittedPlan ?? inspection.plan,
6713
6730
  approved_plan_fingerprint: expectString$1(inspection.plan, "plan_fingerprint"),
6714
6731
  idempotency_key: idempotencyKey(ctx)
6715
6732
  };
@@ -6781,6 +6798,7 @@ async function upgradeApps(ctx, input, dependencies) {
6781
6798
  ...sourceRpcParams(source),
6782
6799
  ...planScopeAndOptions(inspection.plan),
6783
6800
  options: installOptions(input),
6801
+ plan: inspection.plan,
6784
6802
  approved_plan_fingerprint: expectString$1(inspection.plan, "plan_fingerprint"),
6785
6803
  idempotency_key: idempotencyKey(ctx)
6786
6804
  }),
@@ -6852,47 +6870,90 @@ async function prepareSource(ctx, input, purpose, dependencies) {
6852
6870
  const classified = await classifySource(ctx, input);
6853
6871
  if (classified.kind === "catalog")
6854
6872
  return classified;
6855
- const snapshot = classified.kind === "url" ? await downloadPikg(classified.url, dependencies.download, ctx.signal) : await readPikg(classified.path, classified.kind);
6856
- const stage = dependencies.stagePikg ?? stagePikg;
6857
- const metadata = await stage(ctx, snapshot, purpose);
6858
- if (metadata.schema_version !== PLAN_SCHEMA_VERSION) {
6859
- throw new ToolError("UNSUPPORTED_SCHEMA_VERSION", "staging returned a non-v4 schema");
6873
+ const host2 = getHost();
6874
+ const localDirectory = localPikgDirectory(ctx);
6875
+ let directory;
6876
+ let localFileId;
6877
+ if (localDirectory) {
6878
+ try {
6879
+ await host2.mkdir(localDirectory, { recursive: true, mode: 448 });
6880
+ directory = localDirectory;
6881
+ localFileId = crypto.randomUUID().replaceAll("-", "");
6882
+ } catch (error) {
6883
+ if (!isHostError(error, "PermissionDenied") && !isHostError(error, "NotFound"))
6884
+ throw error;
6885
+ }
6860
6886
  }
6861
- if (metadata.pikg_digest !== snapshot.digest || metadata.size !== snapshot.size) {
6862
- throw new ToolError(
6863
- "PIKG_DIGEST_MISMATCH",
6864
- "the staged PIKG does not match the client byte snapshot",
6865
- EXIT_OPERATION,
6866
- false,
6867
- {
6868
- expected_digest: snapshot.digest,
6869
- staged_digest: metadata.pikg_digest,
6870
- expected_size: snapshot.size,
6871
- staged_size: metadata.size
6872
- }
6873
- );
6887
+ if (!localFileId) {
6888
+ await host2.mkdir(ctx.configStore.root, { recursive: true, mode: 448 });
6889
+ directory = await host2.makeTempDir({ dir: ctx.configStore.root, prefix: ".pikg-" });
6874
6890
  }
6875
- if (metadata.purpose !== purpose) {
6876
- throw new ToolError("INVALID_SERVICE_RESPONSE", "staging purpose does not match the request", 9);
6891
+ let path = host2.path.join(directory, `${localFileId ?? "source"}.pikg`);
6892
+ const snapshotAt = (destination) => classified.kind === "url" ? downloadPikg(classified.url, destination, dependencies.download, ctx.signal) : readPikg(classified.path, destination, ctx.signal);
6893
+ try {
6894
+ let snapshot;
6895
+ try {
6896
+ snapshot = await snapshotAt(path);
6897
+ } catch (error) {
6898
+ if (!localFileId || !isHostError(error, "PermissionDenied"))
6899
+ throw error;
6900
+ await host2.remove(path).catch(() => {
6901
+ });
6902
+ localFileId = void 0;
6903
+ await host2.mkdir(ctx.configStore.root, { recursive: true, mode: 448 });
6904
+ directory = await host2.makeTempDir({ dir: ctx.configStore.root, prefix: ".pikg-" });
6905
+ path = host2.path.join(directory, "source.pikg");
6906
+ snapshot = await snapshotAt(path);
6907
+ }
6908
+ snapshot.localFileId = localFileId;
6909
+ const stage = dependencies.stagePikg ?? stagePikg;
6910
+ const metadata = await stage(ctx, snapshot, purpose);
6911
+ if (metadata.schema_version !== PLAN_SCHEMA_VERSION) {
6912
+ throw new ToolError("UNSUPPORTED_SCHEMA_VERSION", "staging returned a non-v4 schema");
6913
+ }
6914
+ if (metadata.pikg_digest !== snapshot.digest || metadata.size !== snapshot.size) {
6915
+ throw new ToolError(
6916
+ "PIKG_DIGEST_MISMATCH",
6917
+ "the staged PIKG does not match the client byte snapshot",
6918
+ EXIT_OPERATION,
6919
+ false,
6920
+ {
6921
+ expected_digest: snapshot.digest,
6922
+ staged_digest: metadata.pikg_digest,
6923
+ expected_size: snapshot.size,
6924
+ staged_size: metadata.size
6925
+ }
6926
+ );
6927
+ }
6928
+ if (metadata.purpose !== purpose) {
6929
+ throw new ToolError(
6930
+ "INVALID_SERVICE_RESPONSE",
6931
+ "staging purpose does not match the request",
6932
+ 9
6933
+ );
6934
+ }
6935
+ return {
6936
+ kind: snapshot.kind,
6937
+ display: snapshot.display,
6938
+ snapshot,
6939
+ staging: metadata,
6940
+ serviceSource: { kind: "local_pikg", staging_handle: metadata.handle }
6941
+ };
6942
+ } finally {
6943
+ await host2.remove(localFileId ? path : directory, { recursive: !localFileId }).catch(() => {
6944
+ });
6877
6945
  }
6878
- return {
6879
- kind: snapshot.kind,
6880
- display: snapshot.display,
6881
- snapshot,
6882
- staging: metadata,
6883
- serviceSource: { kind: "local_pikg", staging_handle: metadata.handle }
6884
- };
6885
6946
  }
6886
6947
  async function classifySource(ctx, input) {
6887
- const positional = optionalString(input.source);
6888
- const pikg = optionalString(input.pikg);
6948
+ const positional = optionalString$1(input.source);
6949
+ const pikg = optionalString$1(input.pikg);
6889
6950
  if (positional && pikg) {
6890
6951
  throw new UsageError("ARGUMENT_CONFLICT", "<source> and --pikg are mutually exclusive");
6891
6952
  }
6892
6953
  const raw = pikg ?? positional;
6893
6954
  if (!raw)
6894
6955
  throw new UsageError("MISSING_ARGUMENT", "source or --pikg is required");
6895
- const forced = pikg ? "pikg" : optionalString(input.from);
6956
+ const forced = pikg ? "pikg" : optionalString$1(input.from);
6896
6957
  if (pikg && forced && forced !== "pikg") {
6897
6958
  throw new UsageError("ARGUMENT_CONFLICT", "--pikg conflicts with --from");
6898
6959
  }
@@ -6929,69 +6990,106 @@ async function classifySource(ctx, input) {
6929
6990
  serviceSource: { kind: "identifier", identifier }
6930
6991
  };
6931
6992
  }
6932
- async function readPikg(path, kind) {
6993
+ function localPikgDirectory(ctx) {
6994
+ const root = getHost().policy.buckyosRoot;
6995
+ const hostname = new URL(ctx.connection.endpoint).hostname;
6996
+ if (!root || !["localhost", "127.0.0.1", "[::1]"].includes(hostname))
6997
+ return void 0;
6998
+ return getHost().path.join(root, "cache", "control_panel", "pikg_staging", "incoming");
6999
+ }
7000
+ async function* fileChunks(file, size) {
7001
+ const buffer = new Uint8Array(size);
7002
+ while (true) {
7003
+ let used = 0;
7004
+ while (used < buffer.length) {
7005
+ const read = await file.read(buffer.subarray(used));
7006
+ if (read === null)
7007
+ break;
7008
+ if (read === 0)
7009
+ throw new UsageError("PIKG_READ_FAILED", "PIKG read made no progress");
7010
+ used += read;
7011
+ }
7012
+ if (used === 0)
7013
+ break;
7014
+ yield buffer.subarray(0, used);
7015
+ }
7016
+ }
7017
+ async function writePikgSnapshot(path, chunks, signal) {
7018
+ const output = await getHost().open(path, { write: true, createNew: true, mode: 384 });
7019
+ const hash = getHost().createHash("sha256");
7020
+ const magic = new Uint8Array(ZIP_LOCAL_MAGIC.length);
7021
+ let size = 0;
7022
+ try {
7023
+ for await (const chunk of chunks) {
7024
+ signal.throwIfAborted();
7025
+ if (size < magic.length)
7026
+ magic.set(chunk.subarray(0, magic.length - size), size);
7027
+ hash.update(chunk);
7028
+ size += chunk.length;
7029
+ if (!Number.isSafeInteger(size))
7030
+ throw new UsageError("PIKG_TOO_LARGE", "PIKG is too large");
7031
+ let offset = 0;
7032
+ while (offset < chunk.length) {
7033
+ const written = await output.write(chunk.subarray(offset));
7034
+ if (written === 0)
7035
+ throw new UsageError("PIKG_WRITE_FAILED", "PIKG write made no progress");
7036
+ offset += written;
7037
+ }
7038
+ }
7039
+ validatePikgSnapshot(magic.subarray(0, Math.min(size, magic.length)));
7040
+ await output.sync();
7041
+ return { path, size, digest: hash.digestHex() };
7042
+ } finally {
7043
+ await output.close();
7044
+ }
7045
+ }
7046
+ async function readPikg(source, destination, signal) {
6933
7047
  let file;
6934
7048
  try {
6935
- file = await getHost().open(path, { read: true });
7049
+ file = await getHost().open(source, { read: true });
6936
7050
  } catch (error) {
6937
7051
  throw new UsageError("PIKG_READ_FAILED", `failed to open PIKG: ${errorMessage(error)}`);
6938
7052
  }
6939
7053
  try {
6940
- const stat2 = await file.stat();
6941
- if (!stat2.isFile)
7054
+ const before = await file.stat();
7055
+ if (!before.isFile)
6942
7056
  throw new UsageError("INVALID_PIKG_SOURCE", "PIKG source is not a file");
6943
- if (stat2.size > Number.MAX_SAFE_INTEGER) {
6944
- throw new UsageError("PIKG_TOO_LARGE", "PIKG is too large for this client");
6945
- }
6946
- const bytes = new Uint8Array(stat2.size);
6947
- let offset = 0;
6948
- while (offset < bytes.length) {
6949
- const read = await file.read(bytes.subarray(offset));
6950
- if (read === null)
6951
- break;
6952
- offset += read;
7057
+ const snapshot = await writePikgSnapshot(destination, fileChunks(file, 256 * 1024), signal);
7058
+ const after = await file.stat();
7059
+ if (snapshot.size !== before.size || before.size !== after.size || before.mtime?.getTime() !== after.mtime?.getTime()) {
7060
+ throw new UsageError("PIKG_READ_FAILED", "PIKG changed while being read");
6953
7061
  }
6954
- if (offset !== bytes.length) {
6955
- throw new UsageError("PIKG_READ_FAILED", "PIKG changed size while being read");
6956
- }
6957
- validatePikgSnapshot(bytes);
6958
- return {
6959
- kind,
6960
- display: path,
6961
- bytes,
6962
- digest: await sha256Id(bytes),
6963
- size: bytes.length
6964
- };
7062
+ return { kind: "pikg", display: source, ...snapshot };
6965
7063
  } finally {
6966
7064
  await file.close();
6967
7065
  }
6968
7066
  }
6969
- async function downloadPikg(url, downloader, signal) {
6970
- let bytes;
7067
+ async function downloadPikg(url, destination, downloader, signal) {
7068
+ let chunks;
6971
7069
  try {
6972
- bytes = downloader ? await downloader(url, signal) : await defaultDownload(url, signal);
7070
+ chunks = downloader ? [await downloader(url, signal)] : await defaultDownload(url, signal);
7071
+ const snapshot = await writePikgSnapshot(destination, chunks, signal);
7072
+ return { kind: "url", display: safeUrl(url), ...snapshot };
6973
7073
  } catch (error) {
6974
- if (error instanceof ToolError)
7074
+ if (error instanceof ToolError || isHostError(error, "PermissionDenied"))
6975
7075
  throw error;
7076
+ signal.throwIfAborted();
6976
7077
  throw new ToolError(
6977
7078
  "PIKG_DOWNLOAD_FAILED",
6978
7079
  `failed to download PIKG: ${errorMessage(error)}`,
6979
7080
  5,
6980
7081
  true
6981
7082
  );
7083
+ } finally {
7084
+ if (chunks instanceof ReadableStream && !chunks.locked)
7085
+ await chunks.cancel().catch(() => {
7086
+ });
6982
7087
  }
6983
- validatePikgSnapshot(bytes);
6984
- return {
6985
- kind: "url",
6986
- display: safeUrl(url),
6987
- bytes,
6988
- digest: await sha256Id(bytes),
6989
- size: bytes.length
6990
- };
6991
7088
  }
6992
7089
  async function defaultDownload(url, signal) {
6993
7090
  const response = await fetch(url, { method: "GET", redirect: "follow", signal });
6994
- if (!response.ok) {
7091
+ if (!response.ok || !response.body) {
7092
+ await response.body?.cancel();
6995
7093
  throw new ToolError(
6996
7094
  "PIKG_DOWNLOAD_FAILED",
6997
7095
  `PIKG download returned HTTP ${response.status}`,
@@ -6999,46 +7097,84 @@ async function defaultDownload(url, signal) {
6999
7097
  response.status >= 500
7000
7098
  );
7001
7099
  }
7002
- return new Uint8Array(await response.arrayBuffer());
7100
+ return response.body;
7003
7101
  }
7004
7102
  async function stagePikg(ctx, snapshot, purpose) {
7103
+ const identity = { pikg_digest: snapshot.digest, size: snapshot.size, purpose };
7104
+ if (snapshot.localFileId) {
7105
+ try {
7106
+ const value2 = expectObject$1(
7107
+ await callControl(ctx, "apps.staging.finalize", {
7108
+ ...identity,
7109
+ local_file_id: snapshot.localFileId
7110
+ }),
7111
+ "apps.staging.finalize response"
7112
+ );
7113
+ return parseStagingMetadata(value2);
7114
+ } catch (error) {
7115
+ if (!errorMessage(error).includes("LOCAL_PIKG_UNAVAILABLE"))
7116
+ throw error;
7117
+ }
7118
+ }
7005
7119
  const session = ctx.session;
7006
7120
  if (!session)
7007
7121
  throw new ToolError("AUTH_REQUIRED", "authenticated session is required", 3);
7008
7122
  const token = (await session.ensureValid()).token;
7009
- const hash = ndn.sha256Bytes(snapshot.bytes);
7010
- const chunkId = ndn.ChunkId.fromMix256Result(snapshot.size, hash).toString();
7011
7123
  const proxy = ndm_proxy.createNdmProxyClient({
7012
7124
  endpoint: gatewayOrigin(ctx.connection.endpoint),
7013
7125
  sessionToken: token,
7014
7126
  fetcher: (request, init) => fetch(normalizeNdmRequestUrl(request), { ...init, signal: ctx.signal })
7015
7127
  });
7016
- let uploadError;
7128
+ let fileObjectId;
7017
7129
  try {
7018
- await proxy.putChunk(chunkId, snapshot.bytes);
7019
- } catch (error) {
7020
- uploadError = error;
7021
- }
7022
- let value;
7023
- try {
7024
- value = expectObject$1(
7025
- await callControl(ctx, "apps.staging.finalize", {
7026
- source_obj_id: chunkId,
7027
- purpose
7028
- }),
7029
- "apps.staging.finalize response"
7030
- );
7130
+ const file = await getHost().open(snapshot.path, { read: true });
7131
+ const chunks = new ndn.SimpleChunkList();
7132
+ const hash = getHost().createHash("sha256");
7133
+ try {
7134
+ for await (const bytes of fileChunks(file, 32 * 1024 * 1024)) {
7135
+ ctx.signal.throwIfAborted();
7136
+ hash.update(bytes);
7137
+ const chunk = ndn.ChunkId.fromMix256Result(bytes.length, ndn.sha256Bytes(bytes));
7138
+ await proxy.putChunk(chunk.toString(), bytes);
7139
+ chunks.appendChunk(chunk);
7140
+ }
7141
+ } finally {
7142
+ await file.close();
7143
+ }
7144
+ if (chunks.total_size !== snapshot.size || hash.digestHex() !== snapshot.digest) {
7145
+ throw new ToolError("PIKG_DIGEST_MISMATCH", "PIKG snapshot changed during upload");
7146
+ }
7147
+ let content = chunks.body[0].toString();
7148
+ if (chunks.body.length > 1) {
7149
+ const [id2, body2] = chunks.genObjId();
7150
+ await proxy.putObject({ obj_id: id2.toString(), obj_data: body2 });
7151
+ content = id2.toString();
7152
+ }
7153
+ const fileObject = new ndn.FileObject(`${snapshot.digest}.pikg`, snapshot.size, content);
7154
+ const [id, body] = fileObject.genObjId();
7155
+ await proxy.putObject({ obj_id: id.toString(), obj_data: body });
7156
+ fileObjectId = id.toString();
7031
7157
  } catch (error) {
7032
- if (uploadError === void 0)
7158
+ if (error instanceof ToolError)
7033
7159
  throw error;
7160
+ ctx.signal.throwIfAborted();
7034
7161
  throw new ToolError(
7035
7162
  "PIKG_UPLOAD_FAILED",
7036
- `failed to upload PIKG snapshot: ${errorMessage(uploadError)}`,
7163
+ `failed to upload PIKG: ${errorMessage(error)}`,
7037
7164
  5,
7038
- true,
7039
- { finalize_error: errorMessage(error) }
7165
+ true
7040
7166
  );
7041
7167
  }
7168
+ const value = expectObject$1(
7169
+ await callControl(ctx, "apps.staging.finalize", {
7170
+ ...identity,
7171
+ source_obj_id: fileObjectId
7172
+ }),
7173
+ "apps.staging.finalize response"
7174
+ );
7175
+ return parseStagingMetadata(value);
7176
+ }
7177
+ function parseStagingMetadata(value) {
7042
7178
  return {
7043
7179
  schema_version: expectNumber(value, "schema_version"),
7044
7180
  handle: expectString$1(value, "handle"),
@@ -7138,11 +7274,11 @@ async function observeInstallTask(ctx, taskId) {
7138
7274
  const sanitizedDetails = error && isObject(error.details) ? sanitizeAppOutput(error.details) : void 0;
7139
7275
  return {
7140
7276
  phase: expectString$1(status, "task_phase"),
7141
- outcome: optionalString(status.task_outcome),
7277
+ outcome: optionalString$1(status.task_outcome),
7142
7278
  message: installProgressMessage(status),
7143
7279
  error: error ? {
7144
- code: optionalString(error.code),
7145
- message: optionalString(error.message),
7280
+ code: optionalString$1(error.code),
7281
+ message: optionalString$1(error.message),
7146
7282
  retryable: typeof error.retryable === "boolean" ? error.retryable : void 0,
7147
7283
  details: isObject(sanitizedDetails) ? sanitizedDetails : void 0
7148
7284
  } : void 0,
@@ -7240,7 +7376,7 @@ function installedScope(details) {
7240
7376
  };
7241
7377
  }
7242
7378
  function installOptions(input) {
7243
- const policy2 = optionalString(input.policy) ?? "normal";
7379
+ const policy2 = optionalString$1(input.policy) ?? "normal";
7244
7380
  return { policy: policy2.replaceAll("-", "_").toUpperCase() };
7245
7381
  }
7246
7382
  function sourceSummary(source) {
@@ -7522,11 +7658,6 @@ function validatePikgSnapshot(bytes) {
7522
7658
  throw new UsageError("INVALID_PIKG", "PIKG magic mismatch: expected a ZIP container");
7523
7659
  }
7524
7660
  }
7525
- async function sha256Id(bytes) {
7526
- const digestInput = Uint8Array.from(bytes);
7527
- const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", digestInput.buffer));
7528
- return [...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("");
7529
- }
7530
7661
  function gatewayOrigin(endpoint) {
7531
7662
  const url = new URL(endpoint);
7532
7663
  const marker = url.pathname.indexOf("/kapi");
@@ -7569,13 +7700,13 @@ function parseEmbeddedJson(message) {
7569
7700
  }
7570
7701
  function readinessValue(inspection, field) {
7571
7702
  const readiness = isObject(inspection.status.readiness) ? inspection.status.readiness : void 0;
7572
- return readiness ? optionalString(readiness[field]) : void 0;
7703
+ return readiness ? optionalString$1(readiness[field]) : void 0;
7573
7704
  }
7574
7705
  function planUse(plan) {
7575
- return optionalString(plan.plan_use);
7706
+ return optionalString$1(plan.plan_use);
7576
7707
  }
7577
7708
  function installProgressMessage(status) {
7578
- const stage = optionalString(status.stage);
7709
+ const stage = optionalString$1(status.stage);
7579
7710
  const progress = isObject(status.progress) ? status.progress : void 0;
7580
7711
  const percent = progress && typeof progress.percent === "number" ? `${progress.percent}%` : void 0;
7581
7712
  return [stage, percent].filter(Boolean).join(" ") || void 0;
@@ -7599,7 +7730,7 @@ function selectorCommand(options) {
7599
7730
  required: ["app_name"],
7600
7731
  additionalProperties: false
7601
7732
  },
7602
- outputSchema: OBJECT_OUTPUT$3,
7733
+ outputSchema: OBJECT_OUTPUT$4,
7603
7734
  resultSchemaVersion: 1,
7604
7735
  access: { mode: "fixed", level: options.access },
7605
7736
  asyncMode: options.asyncMode,
@@ -7681,7 +7812,7 @@ function expectNumber(object, property) {
7681
7812
  }
7682
7813
  return value;
7683
7814
  }
7684
- function optionalString(value) {
7815
+ function optionalString$1(value) {
7685
7816
  return typeof value === "string" && value.trim() ? value : void 0;
7686
7817
  }
7687
7818
  function optionalNumber(value) {
@@ -7826,7 +7957,7 @@ function splitServices(value) {
7826
7957
  return [...new Set(items.map((item) => String(item).trim()).filter(Boolean))];
7827
7958
  }
7828
7959
  const CONTROL_PANEL$2 = "control-panel";
7829
- const OBJECT_OUTPUT$2 = { type: "object", additionalProperties: true };
7960
+ const OBJECT_OUTPUT$3 = { type: "object", additionalProperties: true };
7830
7961
  function createDiagnosticModule(dependencies = {}) {
7831
7962
  return {
7832
7963
  name: "diagnostic",
@@ -7865,7 +7996,7 @@ function collectCommand() {
7865
7996
  required: ["services"],
7866
7997
  additionalProperties: false
7867
7998
  },
7868
- outputSchema: OBJECT_OUTPUT$2,
7999
+ outputSchema: OBJECT_OUTPUT$3,
7869
8000
  resultSchemaVersion: 1,
7870
8001
  access: { mode: "fixed", level: "privileged" },
7871
8002
  asyncMode: "task",
@@ -7919,7 +8050,7 @@ function exportCommand$1(dependencies) {
7919
8050
  required: ["bundle_id", "path"],
7920
8051
  additionalProperties: false
7921
8052
  },
7922
- outputSchema: OBJECT_OUTPUT$2,
8053
+ outputSchema: OBJECT_OUTPUT$3,
7923
8054
  resultSchemaVersion: 1,
7924
8055
  access: { mode: "fixed", level: "privileged" },
7925
8056
  asyncMode: "sync",
@@ -7961,7 +8092,7 @@ function compact$2(value) {
7961
8092
  return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== void 0));
7962
8093
  }
7963
8094
  const CONTROL_PANEL$1 = "control-panel";
7964
- const OBJECT_OUTPUT$1 = { type: "object", additionalProperties: true };
8095
+ const OBJECT_OUTPUT$2 = { type: "object", additionalProperties: true };
7965
8096
  function createLogModule(dependencies = {}) {
7966
8097
  return {
7967
8098
  name: "log",
@@ -8022,7 +8153,7 @@ function tailCommand(dependencies) {
8022
8153
  }
8023
8154
  ],
8024
8155
  inputSchema: filterSchema({ from: { type: "string", enum: ["start", "end"] } }),
8025
- outputSchema: OBJECT_OUTPUT$1,
8156
+ outputSchema: OBJECT_OUTPUT$2,
8026
8157
  resultSchemaVersion: 1,
8027
8158
  access: { mode: "fixed", level: "read" },
8028
8159
  asyncMode: "stream",
@@ -8078,7 +8209,7 @@ function exportCommand(dependencies) {
8078
8209
  ...filterSchema({ path: { type: "string", minLength: 1 } }),
8079
8210
  required: ["path"]
8080
8211
  },
8081
- outputSchema: OBJECT_OUTPUT$1,
8212
+ outputSchema: OBJECT_OUTPUT$2,
8082
8213
  resultSchemaVersion: 1,
8083
8214
  access: { mode: "fixed", level: "read" },
8084
8215
  asyncMode: "sync",
@@ -8214,7 +8345,7 @@ function abortableSleep(milliseconds, signal) {
8214
8345
  }
8215
8346
  const TASK_MANAGER = "task-manager";
8216
8347
  const CONTROL_PANEL = "control-panel";
8217
- const OBJECT_OUTPUT = { type: "object", additionalProperties: true };
8348
+ const OBJECT_OUTPUT$1 = { type: "object", additionalProperties: true };
8218
8349
  function createTaskModule() {
8219
8350
  return {
8220
8351
  name: "task",
@@ -8317,7 +8448,7 @@ function getCommand() {
8317
8448
  required: ["task_id"],
8318
8449
  additionalProperties: false
8319
8450
  },
8320
- outputSchema: OBJECT_OUTPUT,
8451
+ outputSchema: OBJECT_OUTPUT$1,
8321
8452
  resultSchemaVersion: 1,
8322
8453
  access: { mode: "fixed", level: "read" },
8323
8454
  asyncMode: "sync",
@@ -8358,7 +8489,7 @@ function waitCommand() {
8358
8489
  summary: "Stream changes until a Task becomes terminal",
8359
8490
  positionals: [{ name: "task_id", description: "Opaque Task ID", required: true }],
8360
8491
  inputSchema: taskIdSchema(),
8361
- outputSchema: OBJECT_OUTPUT,
8492
+ outputSchema: OBJECT_OUTPUT$1,
8362
8493
  resultSchemaVersion: 1,
8363
8494
  access: { mode: "fixed", level: "read" },
8364
8495
  asyncMode: "stream",
@@ -8409,7 +8540,7 @@ function cancelCommand() {
8409
8540
  required: ["task_id"],
8410
8541
  additionalProperties: false
8411
8542
  },
8412
- outputSchema: OBJECT_OUTPUT,
8543
+ outputSchema: OBJECT_OUTPUT$1,
8413
8544
  resultSchemaVersion: 1,
8414
8545
  access: { mode: "fixed", level: "write" },
8415
8546
  asyncMode: "sync",
@@ -8452,7 +8583,7 @@ function retryCommand() {
8452
8583
  required: ["task_id"],
8453
8584
  additionalProperties: false
8454
8585
  },
8455
- outputSchema: OBJECT_OUTPUT,
8586
+ outputSchema: OBJECT_OUTPUT$1,
8456
8587
  resultSchemaVersion: 1,
8457
8588
  access: { mode: "fixed", level: "write" },
8458
8589
  asyncMode: "either",
@@ -8525,6 +8656,459 @@ function compact(value) {
8525
8656
  function invalidResponse(label) {
8526
8657
  throw new ToolError("INVALID_SERVICE_RESPONSE", `${label} is invalid`, EXIT_INTERNAL);
8527
8658
  }
8659
+ const sdk = provisionSdk;
8660
+ const OBJECT_OUTPUT = { type: "object", additionalProperties: true };
8661
+ const ROOT_OPTION = {
8662
+ name: "root",
8663
+ description: "Installed BuckyOS root on this machine (BUCKYOS_ROOT); read by status/check, written by activate",
8664
+ type: "string",
8665
+ required: true
8666
+ };
8667
+ const TARGET_OPTIONS = [
8668
+ ROOT_OPTION,
8669
+ {
8670
+ name: "domain",
8671
+ description: "Fixed public host name; Owner DID and Zone DID become did:web:<domain>",
8672
+ type: "string",
8673
+ required: true
8674
+ },
8675
+ {
8676
+ name: "owner-name",
8677
+ property: "owner_name",
8678
+ description: "Local administrator / OwnerDocument name (lowercase DNS label)",
8679
+ type: "string",
8680
+ required: true
8681
+ },
8682
+ {
8683
+ name: "owner-key-backup",
8684
+ property: "owner_key_backup",
8685
+ description: "New file for the Owner private-key recovery copy; its directory must exist and the file must not",
8686
+ type: "string",
8687
+ required: true
8688
+ },
8689
+ {
8690
+ name: "public-ip",
8691
+ property: "public_ip",
8692
+ description: "Fixed public IP used only for the A/AAAA record instructions",
8693
+ type: "string"
8694
+ },
8695
+ {
8696
+ name: "rtcp-port",
8697
+ property: "rtcp_port",
8698
+ description: "RTCP port, default 2980",
8699
+ type: "integer"
8700
+ },
8701
+ {
8702
+ name: "guest-access",
8703
+ property: "guest_access",
8704
+ description: "Enable guest access in the first-boot configuration",
8705
+ type: "boolean"
8706
+ }
8707
+ ];
8708
+ const TARGET_INPUT_PROPERTIES = {
8709
+ root: { type: "string", minLength: 1 },
8710
+ domain: { type: "string", minLength: 1 },
8711
+ owner_name: { type: "string", minLength: 1 },
8712
+ owner_key_backup: { type: "string", minLength: 1 },
8713
+ public_ip: { type: "string" },
8714
+ rtcp_port: { type: "integer", minimum: 1 },
8715
+ guest_access: { type: "boolean" }
8716
+ };
8717
+ const TARGET_REQUIRED = ["root", "domain", "owner_name", "owner_key_backup"];
8718
+ const POLICY_NOTE = "Paths are read from argv by the launcher to grant the minimal filesystem permissions; root and owner_key_backup given only inside --input are limited to the policy roots (cwd, and the paired BUCKYOS_ROOT of the system distribution).";
8719
+ function createProvisionModule(dependencies = {}) {
8720
+ const deps = {
8721
+ inspect: dependencies.inspect ?? sdk.inspectActivationRoot,
8722
+ check: dependencies.check ?? sdk.checkOfflineActivation,
8723
+ activate: dependencies.activate ?? sdk.activateOfflineZone
8724
+ };
8725
+ return {
8726
+ name: "provision",
8727
+ summary: "Activate an installed BuckyOS root on this machine (first Zone, Owner, and OOD)",
8728
+ commands: [statusCommand(deps), checkCommand(deps), activateCommand(deps)]
8729
+ };
8730
+ }
8731
+ function statusCommand(deps) {
8732
+ return {
8733
+ verb: "status",
8734
+ summary: "Inspect the local activation material of an installed BuckyOS root",
8735
+ description: "Read-only. Reports not_installed, unactivated, partial, configured, or invalid from the files under the root; configured only means the first-boot material is complete and internally consistent (startup_required=true). Service health, DNS, TLS, and public DID discovery are not checked. " + POLICY_NOTE,
8736
+ options: [ROOT_OPTION],
8737
+ inputSchema: {
8738
+ type: "object",
8739
+ properties: { root: { type: "string", minLength: 1 } },
8740
+ required: ["root"],
8741
+ additionalProperties: false
8742
+ },
8743
+ outputSchema: {
8744
+ ...OBJECT_OUTPUT,
8745
+ properties: {
8746
+ root: { type: "string" },
8747
+ state: {
8748
+ type: "string",
8749
+ enum: ["not_installed", "unactivated", "partial", "configured", "invalid"]
8750
+ },
8751
+ startup_required: { type: "boolean" }
8752
+ },
8753
+ required: ["root", "state", "startup_required"]
8754
+ },
8755
+ resultSchemaVersion: 1,
8756
+ access: { mode: "fixed", level: "read" },
8757
+ asyncMode: "sync",
8758
+ requiresSession: false,
8759
+ execution: "local",
8760
+ networkAccess: false,
8761
+ examples: ["buckyos provision status --root /opt/buckyos"],
8762
+ handler: async (ctx, input) => {
8763
+ const root = resolvePath(ctx, requiredString(input, "root"));
8764
+ await assertHostAccess(root, "read");
8765
+ return statusView(await runSdk(() => deps.inspect(root)));
8766
+ }
8767
+ };
8768
+ }
8769
+ function checkCommand(deps) {
8770
+ return {
8771
+ verb: "check",
8772
+ summary: "Validate activation parameters and the target root without writing anything",
8773
+ description: "Read-only precheck shared with activate: parameters, target state, and the owner key backup location. No keys are generated, no backup is written, and no operation is produced; activate re-validates the target itself. Fails with the first problem code when the target is not ready. " + POLICY_NOTE,
8774
+ options: TARGET_OPTIONS,
8775
+ inputSchema: {
8776
+ type: "object",
8777
+ properties: TARGET_INPUT_PROPERTIES,
8778
+ required: TARGET_REQUIRED,
8779
+ additionalProperties: false
8780
+ },
8781
+ outputSchema: {
8782
+ ...OBJECT_OUTPUT,
8783
+ properties: {
8784
+ ready: { type: "boolean" },
8785
+ root: { type: "string" },
8786
+ state: { type: "string" }
8787
+ },
8788
+ required: ["ready", "root", "state"]
8789
+ },
8790
+ resultSchemaVersion: 1,
8791
+ access: { mode: "fixed", level: "read" },
8792
+ asyncMode: "sync",
8793
+ requiresSession: false,
8794
+ execution: "local",
8795
+ networkAccess: false,
8796
+ examples: [
8797
+ "buckyos provision check --root /opt/buckyos --domain corp.example.com --owner-name admin --owner-key-backup /secure-backup/corp-owner.pem"
8798
+ ],
8799
+ handler: async (ctx, input) => {
8800
+ const options = activationOptions(ctx, input);
8801
+ await assertHostAccess(options.rootDir, "read");
8802
+ await assertHostAccess(getHost().path.dirname(options.ownerKeyBackupPath), "read");
8803
+ const precheck = await runSdk(() => deps.check(options));
8804
+ if (!precheck.ready)
8805
+ throw precheckError(precheck);
8806
+ return precheckView(precheck);
8807
+ }
8808
+ };
8809
+ }
8810
+ function activateCommand(deps) {
8811
+ return {
8812
+ verb: "activate",
8813
+ summary: "Activate the root: create the first Zone, Owner, and OOD identity and the first-boot configuration",
8814
+ description: "Privileged local write. Generates random Ed25519 Owner/device keys and current-time documents, saves the Owner private-key backup, then commits the identity, Zone, and start_config files; etc/node_identity.json is written last because node-daemon treats it as the activation switch. The administrator password is read from the secret admin_password field of --input JSON or from a hidden terminal prompt; there is no --admin-password option. Non-interactive runs require --yes. Repeated, partial, and invalid targets are rejected; there is no --force. After success restart BuckyOS through the installer or node control entry; this command never reports the Zone as online. " + POLICY_NOTE,
8815
+ options: TARGET_OPTIONS,
8816
+ inputSchema: {
8817
+ type: "object",
8818
+ properties: {
8819
+ ...TARGET_INPUT_PROPERTIES,
8820
+ admin_password: {
8821
+ type: "string",
8822
+ secret: true,
8823
+ description: "Administrator password (secret; --input JSON or hidden prompt only)"
8824
+ }
8825
+ },
8826
+ required: TARGET_REQUIRED,
8827
+ additionalProperties: false
8828
+ },
8829
+ outputSchema: {
8830
+ ...OBJECT_OUTPUT,
8831
+ properties: {
8832
+ root: { type: "string" },
8833
+ state: { type: "string", enum: ["configured"] },
8834
+ startup_required: { type: "boolean" }
8835
+ },
8836
+ required: ["root", "state", "startup_required"]
8837
+ },
8838
+ resultSchemaVersion: 1,
8839
+ access: { mode: "fixed", level: "privileged" },
8840
+ asyncMode: "sync",
8841
+ requiresSession: false,
8842
+ execution: "local",
8843
+ networkAccess: false,
8844
+ examples: [
8845
+ "buckyos provision activate --root /opt/buckyos --domain corp.example.com --owner-name admin --owner-key-backup /secure-backup/corp-owner.pem",
8846
+ "buckyos --non-interactive --yes --input activation.json provision activate --root /opt/buckyos --owner-key-backup /secure-backup/corp-owner.pem"
8847
+ ],
8848
+ handler: async (ctx, input) => {
8849
+ const options = activationOptions(ctx, input);
8850
+ const backupDirectory = getHost().path.dirname(options.ownerKeyBackupPath);
8851
+ await assertHostAccess(options.rootDir, "read");
8852
+ await assertHostAccess(options.rootDir, "write");
8853
+ await assertHostAccess(backupDirectory, "read");
8854
+ await assertHostAccess(backupDirectory, "write");
8855
+ const precheck = await runSdk(() => deps.check(options));
8856
+ if (!precheck.ready)
8857
+ throw precheckError(precheck);
8858
+ const adminPassword = await obtainAdminPassword(ctx, input);
8859
+ await confirmActivation(ctx, precheck);
8860
+ const result = await runSdk(
8861
+ () => deps.activate({ ...options, adminPassword, traceId: ctx.traceId })
8862
+ );
8863
+ return resultView(ctx, input, result);
8864
+ }
8865
+ };
8866
+ }
8867
+ function activationOptions(ctx, input) {
8868
+ return {
8869
+ rootDir: resolvePath(ctx, requiredString(input, "root")),
8870
+ domain: requiredString(input, "domain"),
8871
+ ownerName: requiredString(input, "owner_name"),
8872
+ ownerKeyBackupPath: resolvePath(ctx, requiredString(input, "owner_key_backup")),
8873
+ publicIp: optionalString(input, "public_ip"),
8874
+ rtcpPort: typeof input.rtcp_port === "number" ? input.rtcp_port : void 0,
8875
+ guestAccess: input.guest_access === true
8876
+ };
8877
+ }
8878
+ function requiredString(input, key) {
8879
+ const value = optionalString(input, key);
8880
+ if (!value)
8881
+ throw new UsageError("INVALID_ARGUMENT", `${key} is required`);
8882
+ return value;
8883
+ }
8884
+ function optionalString(input, key) {
8885
+ const value = input[key];
8886
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
8887
+ }
8888
+ function resolvePath(ctx, value) {
8889
+ const path = getHost().path;
8890
+ return path.isAbsolute(value) ? path.resolve(value) : path.resolve(ctx.cwd, value);
8891
+ }
8892
+ async function assertHostAccess(path, operation) {
8893
+ try {
8894
+ await getHost().assertAccess(path, operation);
8895
+ } catch (error) {
8896
+ if (isHostError(error, "PermissionDenied")) {
8897
+ throw new ToolError(
8898
+ "HOST_ACCESS_DENIED",
8899
+ `${error.message}; pass the path on the command line (--root / --owner-key-backup) so the launcher can grant it`,
8900
+ EXIT_PERMISSION,
8901
+ false,
8902
+ { path, operation }
8903
+ );
8904
+ }
8905
+ throw error;
8906
+ }
8907
+ }
8908
+ async function obtainAdminPassword(ctx, input) {
8909
+ if (typeof input.admin_password === "string")
8910
+ return input.admin_password;
8911
+ if (ctx.config.nonInteractive) {
8912
+ throw new UsageError(
8913
+ "SECRET_REQUIRED",
8914
+ "admin_password must be provided through --input JSON in non-interactive mode"
8915
+ );
8916
+ }
8917
+ if (!ctx.io.inputIsTerminal) {
8918
+ throw new UsageError(
8919
+ "SECRET_INPUT_UNAVAILABLE",
8920
+ "the administrator password requires an interactive terminal or --input JSON"
8921
+ );
8922
+ }
8923
+ const host2 = getHost();
8924
+ let password;
8925
+ let confirmation;
8926
+ try {
8927
+ password = await host2.readSecret("Administrator password: ");
8928
+ confirmation = await host2.readSecret("Confirm administrator password: ");
8929
+ } catch (error) {
8930
+ if (isHostError(error, "PermissionDenied")) {
8931
+ throw new UsageError("SECRET_INPUT_UNAVAILABLE", error.message);
8932
+ }
8933
+ throw error;
8934
+ }
8935
+ if (password !== confirmation) {
8936
+ throw new UsageError("PASSWORD_MISMATCH", "the administrator passwords do not match");
8937
+ }
8938
+ return password;
8939
+ }
8940
+ async function confirmActivation(ctx, precheck) {
8941
+ if (ctx.confirmed)
8942
+ return;
8943
+ if (ctx.config.nonInteractive) {
8944
+ throw new ToolError(
8945
+ "CONFIRMATION_REQUIRED",
8946
+ "provision activate requires --yes in non-interactive mode",
8947
+ EXIT_PERMISSION
8948
+ );
8949
+ }
8950
+ if (!ctx.io.inputIsTerminal) {
8951
+ throw new ToolError(
8952
+ "CONFIRMATION_REQUIRED",
8953
+ "provision activate requires an interactive terminal or --yes",
8954
+ EXIT_PERMISSION
8955
+ );
8956
+ }
8957
+ await ctx.io.stderr(
8958
+ [
8959
+ "Activation summary",
8960
+ ` BUCKYOS_ROOT : ${precheck.rootDir}`,
8961
+ ` Owner/Zone : ${precheck.zoneDid}`,
8962
+ ` Device : ${precheck.deviceDid}`,
8963
+ ` Admin : ${precheck.ownerName}`,
8964
+ ` Network : ${"wan"} (no SN, no BNS)`,
8965
+ ` RTCP port : ${precheck.rtcpPort}`,
8966
+ ` Guest access : ${precheck.guestAccess}`,
8967
+ ` Owner backup : ${precheck.ownerKeyBackup.path}`,
8968
+ ""
8969
+ ].join("\n")
8970
+ );
8971
+ const answer = (await ctx.io.prompt("Write this activation to disk? [y/N] "))?.trim().toLowerCase();
8972
+ if (answer !== "y" && answer !== "yes") {
8973
+ throw new ToolError("CONFIRMATION_DECLINED", "activation was declined", EXIT_PERMISSION);
8974
+ }
8975
+ }
8976
+ function isSdkError(error) {
8977
+ return error instanceof Error && error.name === "ActivationError" && typeof error.code === "string";
8978
+ }
8979
+ async function runSdk(run) {
8980
+ try {
8981
+ return await run();
8982
+ } catch (error) {
8983
+ if (isSdkError(error)) {
8984
+ const exitCode = error.code === "INVALID_ARGUMENT" ? EXIT_USAGE : error.code === "ACTIVATION_COMMIT_FAILED" ? EXIT_PARTIAL : EXIT_OPERATION;
8985
+ throw new ToolError(error.code, error.message, exitCode, false, error.details ?? {});
8986
+ }
8987
+ throw error;
8988
+ }
8989
+ }
8990
+ function precheckError(precheck) {
8991
+ const [first] = precheck.problems;
8992
+ const single = precheck.problems.length === 1;
8993
+ return new ToolError(
8994
+ single ? first.code : "ACTIVATION_PRECHECK_FAILED",
8995
+ single ? first.message : `activation precheck failed: ${precheck.problems.map((p) => p.message).join("; ")}`,
8996
+ EXIT_OPERATION,
8997
+ false,
8998
+ precheckView(precheck)
8999
+ );
9000
+ }
9001
+ function statusView(status) {
9002
+ return {
9003
+ root: status.rootDir,
9004
+ state: status.state,
9005
+ startup_required: status.startupRequired,
9006
+ observed_at: status.observedAt,
9007
+ zone_did: status.zoneDid,
9008
+ owner_did: status.ownerDid,
9009
+ device_did: status.deviceDid,
9010
+ device_name: status.deviceName,
9011
+ access_hostname: status.accessHostname,
9012
+ files: status.files.map((file) => ({
9013
+ path: file.path,
9014
+ role: file.role,
9015
+ required: file.required,
9016
+ secret: file.secret,
9017
+ present: file.present
9018
+ })),
9019
+ problems: status.problems,
9020
+ warnings: status.warnings,
9021
+ lock: status.lock ? {
9022
+ path: status.lock.path,
9023
+ stage: status.lock.stage,
9024
+ trace_id: status.lock.traceId,
9025
+ started_at: status.lock.startedAt,
9026
+ domain: status.lock.domain,
9027
+ committed_files: status.lock.committedFiles,
9028
+ owner_key_backup: status.lock.ownerKeyBackupPath,
9029
+ corrupt: status.lock.corrupt
9030
+ } : null
9031
+ };
9032
+ }
9033
+ function precheckView(precheck) {
9034
+ return {
9035
+ ready: precheck.ready,
9036
+ root: precheck.rootDir,
9037
+ state: precheck.state,
9038
+ domain: precheck.domain,
9039
+ owner_name: precheck.ownerName,
9040
+ owner_did: precheck.ownerDid,
9041
+ zone_did: precheck.zoneDid,
9042
+ device_did: precheck.deviceDid,
9043
+ device_name: precheck.deviceName,
9044
+ access_hostname: precheck.accessHostname,
9045
+ rtcp_port: precheck.rtcpPort,
9046
+ guest_access: precheck.guestAccess,
9047
+ public_ip: precheck.publicIp,
9048
+ dns_records: precheck.dnsRecords,
9049
+ owner_key_backup: {
9050
+ path: precheck.ownerKeyBackup.path,
9051
+ directory_exists: precheck.ownerKeyBackup.directoryExists,
9052
+ exists: precheck.ownerKeyBackup.exists
9053
+ },
9054
+ planned_files: precheck.plannedFiles,
9055
+ problems: precheck.problems,
9056
+ target: statusView(precheck.status)
9057
+ };
9058
+ }
9059
+ function resultView(ctx, input, result) {
9060
+ const dnsNotes = [
9061
+ ...result.dnsRecords.map((record) => `${record.type} ${record.name} ${record.value}`),
9062
+ ...result.publicIp ? [] : [`A/AAAA ${result.domain} <this node's fixed public IP>`],
9063
+ `The address record is also saved in etc/zone_dns_records.json under ${result.rootDir}.`,
9064
+ `Local boot uses etc/${result.domain}.zone.json; no DNS BOOT/PKX/DEV records are required.`,
9065
+ "TLS/ACME is not provided by SN in this mode; configure it on the public gateway."
9066
+ ];
9067
+ return {
9068
+ root: result.rootDir,
9069
+ state: result.state,
9070
+ startup_required: result.startupRequired,
9071
+ domain: result.domain,
9072
+ owner_name: result.ownerName,
9073
+ owner_did: result.ownerDid,
9074
+ zone_did: result.zoneDid,
9075
+ device_did: result.deviceDid,
9076
+ device_name: result.deviceName,
9077
+ access_hostname: result.accessHostname,
9078
+ rtcp_port: result.rtcpPort,
9079
+ guest_access: result.guestAccess,
9080
+ public_ip: result.publicIp,
9081
+ document_iat: result.documentIat,
9082
+ document_exp: result.documentExp,
9083
+ owner_key_backup: result.ownerKeyBackup,
9084
+ dns_records: result.dnsRecords,
9085
+ dns_notes: dnsNotes,
9086
+ committed_files: result.committedFiles,
9087
+ warnings: result.warnings,
9088
+ next_steps: [
9089
+ "Restart BuckyOS through the installer or the node control entry to leave activation mode and boot the new Zone.",
9090
+ "Check the running Zone afterwards with system status; activation does not verify service health, DNS, or TLS.",
9091
+ `Keep the Owner private-key backup ${result.ownerKeyBackup.path} offline; it is the only recovery copy.`
9092
+ ],
9093
+ audit: {
9094
+ module: ctx.command.module,
9095
+ verb: ctx.command.verb,
9096
+ trace_id: ctx.traceId,
9097
+ tool_version: TOOL_VERSION,
9098
+ input_fingerprint: inputFingerprint(input)
9099
+ }
9100
+ };
9101
+ }
9102
+ function inputFingerprint(input) {
9103
+ const material = {};
9104
+ for (const key of Object.keys(TARGET_INPUT_PROPERTIES).sort()) {
9105
+ if (input[key] !== void 0)
9106
+ material[key] = input[key];
9107
+ }
9108
+ const hash = getHost().createHash("sha256");
9109
+ hash.update(new TextEncoder().encode(JSON.stringify(material)));
9110
+ return hash.digestHex();
9111
+ }
8528
9112
  const VERSION = TOOL_VERSION;
8529
9113
  class BuckyOSToolApplication {
8530
9114
  #environment;
@@ -8541,7 +9125,8 @@ class BuckyOSToolApplication {
8541
9125
  dependencies.pikg,
8542
9126
  dependencies.app,
8543
9127
  dependencies.log,
8544
- dependencies.diagnostic
9128
+ dependencies.diagnostic,
9129
+ dependencies.provision
8545
9130
  );
8546
9131
  this.#environment = dependencies.environment ?? readEnvironment();
8547
9132
  this.#cwd = dependencies.cwd ?? getHost().cwd();
@@ -8627,6 +9212,8 @@ class BuckyOSToolApplication {
8627
9212
  `);
8628
9213
  return EXIT_SUCCESS;
8629
9214
  }
9215
+ if (command.execution === "local")
9216
+ assertNoSessionOptions(invocation.global, command);
8630
9217
  const setup = await this.#resolveForCommand(command, invocation.global);
8631
9218
  const inputObject = invocation.global.input ? await this.#readInputObject(invocation.global.input) : void 0;
8632
9219
  const parsed = parseCommandArgs(command, invocation.actionArgv, inputObject);
@@ -8872,7 +9459,7 @@ class BuckyOSToolApplication {
8872
9459
  return parsed;
8873
9460
  }
8874
9461
  }
8875
- function createRegistry(pikgDependencies, appDependencies, logDependencies, diagnosticDependencies) {
9462
+ function createRegistry(pikgDependencies, appDependencies, logDependencies, diagnosticDependencies, provisionDependencies) {
8876
9463
  const registry = new CommandRegistry();
8877
9464
  for (const module of createCoreModules(registry))
8878
9465
  registry.register(module);
@@ -8884,8 +9471,26 @@ function createRegistry(pikgDependencies, appDependencies, logDependencies, diag
8884
9471
  registry.register(createTaskModule());
8885
9472
  registry.register(createLogModule(logDependencies));
8886
9473
  registry.register(createDiagnosticModule(diagnosticDependencies));
9474
+ registry.register(createProvisionModule(provisionDependencies));
8887
9475
  return registry;
8888
9476
  }
9477
+ function assertNoSessionOptions(global, command) {
9478
+ for (const [name, value] of [
9479
+ ["profile", global.profile],
9480
+ ["zone", global.zone],
9481
+ ["endpoint", global.endpoint],
9482
+ ["identity", global.identity],
9483
+ ["session-token", global.sessionToken],
9484
+ ["session-token-file", global.sessionTokenFile]
9485
+ ]) {
9486
+ if (value !== void 0) {
9487
+ throw new UsageError(
9488
+ "ARGUMENT_CONFLICT",
9489
+ `--${name} cannot be combined with the local command ${command.module} ${command.verb}`
9490
+ );
9491
+ }
9492
+ }
9493
+ }
8889
9494
  const unavailableClients = {
8890
9495
  call: () => Promise.reject(new ToolError("INTERNAL_ERROR", "service clients are unavailable", 9))
8891
9496
  };
@@ -9153,6 +9758,9 @@ class NodeHost {
9153
9758
  throw translateNodeError(error, path);
9154
9759
  }
9155
9760
  }
9761
+ async assertAccess(path, operation) {
9762
+ await this.assertPath(path, operation);
9763
+ }
9156
9764
  createHash(_algorithm) {
9157
9765
  const hash = createHash("sha256");
9158
9766
  return {