buckyos 0.7.118 → 0.7.120

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 (35) hide show
  1. package/cli/dist/cli.mjs +210 -93
  2. package/cli/dist/cli.mjs.map +1 -1
  3. package/cli/modules/app.ts +213 -94
  4. package/cli/runtime/host.ts +6 -0
  5. package/cli/version.ts +1 -1
  6. package/dist/browser.cjs +1 -1
  7. package/dist/browser.mjs +3 -3
  8. package/dist/index.cjs +1 -1
  9. package/dist/index.mjs +3 -3
  10. package/dist/namelib.d.ts +4 -0
  11. package/dist/namelib.d.ts.map +1 -1
  12. package/dist/{ndm_proxy-20e0a471.js → ndm_proxy-68354a89.js} +5 -5
  13. package/dist/{ndm_proxy-20e0a471.js.map → ndm_proxy-68354a89.js.map} +1 -1
  14. package/dist/{ndm_proxy-6cb0701b.mjs → ndm_proxy-c00bc561.mjs} +14 -3
  15. package/dist/{ndm_proxy-6cb0701b.mjs.map → ndm_proxy-c00bc561.mjs.map} +1 -1
  16. package/dist/ndn_types-859bbd48.js +2 -0
  17. package/dist/ndn_types-859bbd48.js.map +1 -0
  18. package/dist/{ndn_types-ca94286b.mjs → ndn_types-f6c08d20.mjs} +43 -7
  19. package/dist/ndn_types-f6c08d20.mjs.map +1 -0
  20. package/dist/node.cjs +1 -1
  21. package/dist/node.mjs +3 -3
  22. package/dist/provision.cjs +1 -1
  23. package/dist/provision.mjs +1 -1
  24. package/dist/sn_client.d.ts +16 -0
  25. package/dist/sn_client.d.ts.map +1 -1
  26. package/dist/{tus_client-f1532e14.mjs → tus_client-573066b3.mjs} +2 -2
  27. package/dist/{tus_client-f1532e14.mjs.map → tus_client-573066b3.mjs.map} +1 -1
  28. package/dist/{tus_client-32d016c4.js → tus_client-83ad0f4f.js} +2 -2
  29. package/dist/{tus_client-32d016c4.js.map → tus_client-83ad0f4f.js.map} +1 -1
  30. package/dist/types.d.ts +1 -0
  31. package/dist/types.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/dist/ndn_types-c4672b04.js +0 -2
  34. package/dist/ndn_types-c4672b04.js.map +0 -1
  35. package/dist/ndn_types-ca94286b.mjs.map +0 -1
package/cli/dist/cli.mjs CHANGED
@@ -506,6 +506,9 @@ function buildDistributionPolicy(options) {
506
506
  if (options.distribution === "system")
507
507
  readPaths.add(buckyosRoot);
508
508
  const parsed = collectArgumentPaths(argv);
509
+ if (options.distribution === "system" && parsed.module === "app" && ["fetch", "install"].includes(parsed.verb ?? "")) {
510
+ writePaths.add(path.join(buckyosRoot, "cache", "control_panel", "pikg_staging", "incoming"));
511
+ }
509
512
  for (const candidate of parsed.read)
510
513
  readPaths.add(resolveInputPath(candidate, cwd, path));
511
514
  for (const candidate of parsed.write)
@@ -4848,7 +4851,7 @@ function invalid(stage, message, entry) {
4848
4851
  function stableJsonDigest(value) {
4849
4852
  return sha256Bytes(new TextEncoder().encode(ndn.toCanonicalJsonString(value)));
4850
4853
  }
4851
- const PACKAGE_VERSION = "0.7.118";
4854
+ const PACKAGE_VERSION = "0.7.120";
4852
4855
  const TOOL_VERSION = PACKAGE_VERSION;
4853
4856
  const SDK_VERSION = PACKAGE_VERSION;
4854
4857
  const PROTOCOL_VERSION = "1";
@@ -6709,7 +6712,7 @@ async function installApp(ctx, input, dependencies) {
6709
6712
  ...sourceRpcParams(source),
6710
6713
  ...planScopeAndOptions(inspection.plan),
6711
6714
  options: installOptions(input),
6712
- plan: submittedPlan ?? null,
6715
+ plan: submittedPlan ?? inspection.plan,
6713
6716
  approved_plan_fingerprint: expectString$1(inspection.plan, "plan_fingerprint"),
6714
6717
  idempotency_key: idempotencyKey(ctx)
6715
6718
  };
@@ -6781,6 +6784,7 @@ async function upgradeApps(ctx, input, dependencies) {
6781
6784
  ...sourceRpcParams(source),
6782
6785
  ...planScopeAndOptions(inspection.plan),
6783
6786
  options: installOptions(input),
6787
+ plan: inspection.plan,
6784
6788
  approved_plan_fingerprint: expectString$1(inspection.plan, "plan_fingerprint"),
6785
6789
  idempotency_key: idempotencyKey(ctx)
6786
6790
  }),
@@ -6852,36 +6856,79 @@ async function prepareSource(ctx, input, purpose, dependencies) {
6852
6856
  const classified = await classifySource(ctx, input);
6853
6857
  if (classified.kind === "catalog")
6854
6858
  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");
6859
+ const host2 = getHost();
6860
+ const localDirectory = localPikgDirectory(ctx);
6861
+ let directory;
6862
+ let localFileId;
6863
+ if (localDirectory) {
6864
+ try {
6865
+ await host2.mkdir(localDirectory, { recursive: true, mode: 448 });
6866
+ directory = localDirectory;
6867
+ localFileId = crypto.randomUUID().replaceAll("-", "");
6868
+ } catch (error) {
6869
+ if (!isHostError(error, "PermissionDenied") && !isHostError(error, "NotFound"))
6870
+ throw error;
6871
+ }
6860
6872
  }
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
- );
6873
+ if (!localFileId) {
6874
+ await host2.mkdir(ctx.configStore.root, { recursive: true, mode: 448 });
6875
+ directory = await host2.makeTempDir({ dir: ctx.configStore.root, prefix: ".pikg-" });
6874
6876
  }
6875
- if (metadata.purpose !== purpose) {
6876
- throw new ToolError("INVALID_SERVICE_RESPONSE", "staging purpose does not match the request", 9);
6877
+ let path = host2.path.join(directory, `${localFileId ?? "source"}.pikg`);
6878
+ const snapshotAt = (destination) => classified.kind === "url" ? downloadPikg(classified.url, destination, dependencies.download, ctx.signal) : readPikg(classified.path, destination, ctx.signal);
6879
+ try {
6880
+ let snapshot;
6881
+ try {
6882
+ snapshot = await snapshotAt(path);
6883
+ } catch (error) {
6884
+ if (!localFileId || !isHostError(error, "PermissionDenied"))
6885
+ throw error;
6886
+ await host2.remove(path).catch(() => {
6887
+ });
6888
+ localFileId = void 0;
6889
+ await host2.mkdir(ctx.configStore.root, { recursive: true, mode: 448 });
6890
+ directory = await host2.makeTempDir({ dir: ctx.configStore.root, prefix: ".pikg-" });
6891
+ path = host2.path.join(directory, "source.pikg");
6892
+ snapshot = await snapshotAt(path);
6893
+ }
6894
+ snapshot.localFileId = localFileId;
6895
+ const stage = dependencies.stagePikg ?? stagePikg;
6896
+ const metadata = await stage(ctx, snapshot, purpose);
6897
+ if (metadata.schema_version !== PLAN_SCHEMA_VERSION) {
6898
+ throw new ToolError("UNSUPPORTED_SCHEMA_VERSION", "staging returned a non-v4 schema");
6899
+ }
6900
+ if (metadata.pikg_digest !== snapshot.digest || metadata.size !== snapshot.size) {
6901
+ throw new ToolError(
6902
+ "PIKG_DIGEST_MISMATCH",
6903
+ "the staged PIKG does not match the client byte snapshot",
6904
+ EXIT_OPERATION,
6905
+ false,
6906
+ {
6907
+ expected_digest: snapshot.digest,
6908
+ staged_digest: metadata.pikg_digest,
6909
+ expected_size: snapshot.size,
6910
+ staged_size: metadata.size
6911
+ }
6912
+ );
6913
+ }
6914
+ if (metadata.purpose !== purpose) {
6915
+ throw new ToolError(
6916
+ "INVALID_SERVICE_RESPONSE",
6917
+ "staging purpose does not match the request",
6918
+ 9
6919
+ );
6920
+ }
6921
+ return {
6922
+ kind: snapshot.kind,
6923
+ display: snapshot.display,
6924
+ snapshot,
6925
+ staging: metadata,
6926
+ serviceSource: { kind: "local_pikg", staging_handle: metadata.handle }
6927
+ };
6928
+ } finally {
6929
+ await host2.remove(localFileId ? path : directory, { recursive: !localFileId }).catch(() => {
6930
+ });
6877
6931
  }
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
6932
  }
6886
6933
  async function classifySource(ctx, input) {
6887
6934
  const positional = optionalString(input.source);
@@ -6929,69 +6976,106 @@ async function classifySource(ctx, input) {
6929
6976
  serviceSource: { kind: "identifier", identifier }
6930
6977
  };
6931
6978
  }
6932
- async function readPikg(path, kind) {
6979
+ function localPikgDirectory(ctx) {
6980
+ const root = getHost().policy.buckyosRoot;
6981
+ const hostname = new URL(ctx.connection.endpoint).hostname;
6982
+ if (!root || !["localhost", "127.0.0.1", "[::1]"].includes(hostname))
6983
+ return void 0;
6984
+ return getHost().path.join(root, "cache", "control_panel", "pikg_staging", "incoming");
6985
+ }
6986
+ async function* fileChunks(file, size) {
6987
+ const buffer = new Uint8Array(size);
6988
+ while (true) {
6989
+ let used = 0;
6990
+ while (used < buffer.length) {
6991
+ const read = await file.read(buffer.subarray(used));
6992
+ if (read === null)
6993
+ break;
6994
+ if (read === 0)
6995
+ throw new UsageError("PIKG_READ_FAILED", "PIKG read made no progress");
6996
+ used += read;
6997
+ }
6998
+ if (used === 0)
6999
+ break;
7000
+ yield buffer.subarray(0, used);
7001
+ }
7002
+ }
7003
+ async function writePikgSnapshot(path, chunks, signal) {
7004
+ const output = await getHost().open(path, { write: true, createNew: true, mode: 384 });
7005
+ const hash = getHost().createHash("sha256");
7006
+ const magic = new Uint8Array(ZIP_LOCAL_MAGIC.length);
7007
+ let size = 0;
7008
+ try {
7009
+ for await (const chunk of chunks) {
7010
+ signal.throwIfAborted();
7011
+ if (size < magic.length)
7012
+ magic.set(chunk.subarray(0, magic.length - size), size);
7013
+ hash.update(chunk);
7014
+ size += chunk.length;
7015
+ if (!Number.isSafeInteger(size))
7016
+ throw new UsageError("PIKG_TOO_LARGE", "PIKG is too large");
7017
+ let offset = 0;
7018
+ while (offset < chunk.length) {
7019
+ const written = await output.write(chunk.subarray(offset));
7020
+ if (written === 0)
7021
+ throw new UsageError("PIKG_WRITE_FAILED", "PIKG write made no progress");
7022
+ offset += written;
7023
+ }
7024
+ }
7025
+ validatePikgSnapshot(magic.subarray(0, Math.min(size, magic.length)));
7026
+ await output.sync();
7027
+ return { path, size, digest: hash.digestHex() };
7028
+ } finally {
7029
+ await output.close();
7030
+ }
7031
+ }
7032
+ async function readPikg(source, destination, signal) {
6933
7033
  let file;
6934
7034
  try {
6935
- file = await getHost().open(path, { read: true });
7035
+ file = await getHost().open(source, { read: true });
6936
7036
  } catch (error) {
6937
7037
  throw new UsageError("PIKG_READ_FAILED", `failed to open PIKG: ${errorMessage(error)}`);
6938
7038
  }
6939
7039
  try {
6940
- const stat2 = await file.stat();
6941
- if (!stat2.isFile)
7040
+ const before = await file.stat();
7041
+ if (!before.isFile)
6942
7042
  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;
6953
- }
6954
- if (offset !== bytes.length) {
6955
- throw new UsageError("PIKG_READ_FAILED", "PIKG changed size while being read");
7043
+ const snapshot = await writePikgSnapshot(destination, fileChunks(file, 256 * 1024), signal);
7044
+ const after = await file.stat();
7045
+ if (snapshot.size !== before.size || before.size !== after.size || before.mtime?.getTime() !== after.mtime?.getTime()) {
7046
+ throw new UsageError("PIKG_READ_FAILED", "PIKG changed while being read");
6956
7047
  }
6957
- validatePikgSnapshot(bytes);
6958
- return {
6959
- kind,
6960
- display: path,
6961
- bytes,
6962
- digest: await sha256Id(bytes),
6963
- size: bytes.length
6964
- };
7048
+ return { kind: "pikg", display: source, ...snapshot };
6965
7049
  } finally {
6966
7050
  await file.close();
6967
7051
  }
6968
7052
  }
6969
- async function downloadPikg(url, downloader, signal) {
6970
- let bytes;
7053
+ async function downloadPikg(url, destination, downloader, signal) {
7054
+ let chunks;
6971
7055
  try {
6972
- bytes = downloader ? await downloader(url, signal) : await defaultDownload(url, signal);
7056
+ chunks = downloader ? [await downloader(url, signal)] : await defaultDownload(url, signal);
7057
+ const snapshot = await writePikgSnapshot(destination, chunks, signal);
7058
+ return { kind: "url", display: safeUrl(url), ...snapshot };
6973
7059
  } catch (error) {
6974
- if (error instanceof ToolError)
7060
+ if (error instanceof ToolError || isHostError(error, "PermissionDenied"))
6975
7061
  throw error;
7062
+ signal.throwIfAborted();
6976
7063
  throw new ToolError(
6977
7064
  "PIKG_DOWNLOAD_FAILED",
6978
7065
  `failed to download PIKG: ${errorMessage(error)}`,
6979
7066
  5,
6980
7067
  true
6981
7068
  );
7069
+ } finally {
7070
+ if (chunks instanceof ReadableStream && !chunks.locked)
7071
+ await chunks.cancel().catch(() => {
7072
+ });
6982
7073
  }
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
7074
  }
6992
7075
  async function defaultDownload(url, signal) {
6993
7076
  const response = await fetch(url, { method: "GET", redirect: "follow", signal });
6994
- if (!response.ok) {
7077
+ if (!response.ok || !response.body) {
7078
+ await response.body?.cancel();
6995
7079
  throw new ToolError(
6996
7080
  "PIKG_DOWNLOAD_FAILED",
6997
7081
  `PIKG download returned HTTP ${response.status}`,
@@ -6999,46 +7083,84 @@ async function defaultDownload(url, signal) {
6999
7083
  response.status >= 500
7000
7084
  );
7001
7085
  }
7002
- return new Uint8Array(await response.arrayBuffer());
7086
+ return response.body;
7003
7087
  }
7004
7088
  async function stagePikg(ctx, snapshot, purpose) {
7089
+ const identity = { pikg_digest: snapshot.digest, size: snapshot.size, purpose };
7090
+ if (snapshot.localFileId) {
7091
+ try {
7092
+ const value2 = expectObject$1(
7093
+ await callControl(ctx, "apps.staging.finalize", {
7094
+ ...identity,
7095
+ local_file_id: snapshot.localFileId
7096
+ }),
7097
+ "apps.staging.finalize response"
7098
+ );
7099
+ return parseStagingMetadata(value2);
7100
+ } catch (error) {
7101
+ if (!errorMessage(error).includes("LOCAL_PIKG_UNAVAILABLE"))
7102
+ throw error;
7103
+ }
7104
+ }
7005
7105
  const session = ctx.session;
7006
7106
  if (!session)
7007
7107
  throw new ToolError("AUTH_REQUIRED", "authenticated session is required", 3);
7008
7108
  const token = (await session.ensureValid()).token;
7009
- const hash = ndn.sha256Bytes(snapshot.bytes);
7010
- const chunkId = ndn.ChunkId.fromMix256Result(snapshot.size, hash).toString();
7011
7109
  const proxy = ndm_proxy.createNdmProxyClient({
7012
7110
  endpoint: gatewayOrigin(ctx.connection.endpoint),
7013
7111
  sessionToken: token,
7014
7112
  fetcher: (request, init) => fetch(normalizeNdmRequestUrl(request), { ...init, signal: ctx.signal })
7015
7113
  });
7016
- let uploadError;
7017
- try {
7018
- await proxy.putChunk(chunkId, snapshot.bytes);
7019
- } catch (error) {
7020
- uploadError = error;
7021
- }
7022
- let value;
7114
+ let fileObjectId;
7023
7115
  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
- );
7116
+ const file = await getHost().open(snapshot.path, { read: true });
7117
+ const chunks = new ndn.SimpleChunkList();
7118
+ const hash = getHost().createHash("sha256");
7119
+ try {
7120
+ for await (const bytes of fileChunks(file, 32 * 1024 * 1024)) {
7121
+ ctx.signal.throwIfAborted();
7122
+ hash.update(bytes);
7123
+ const chunk = ndn.ChunkId.fromMix256Result(bytes.length, ndn.sha256Bytes(bytes));
7124
+ await proxy.putChunk(chunk.toString(), bytes);
7125
+ chunks.appendChunk(chunk);
7126
+ }
7127
+ } finally {
7128
+ await file.close();
7129
+ }
7130
+ if (chunks.total_size !== snapshot.size || hash.digestHex() !== snapshot.digest) {
7131
+ throw new ToolError("PIKG_DIGEST_MISMATCH", "PIKG snapshot changed during upload");
7132
+ }
7133
+ let content = chunks.body[0].toString();
7134
+ if (chunks.body.length > 1) {
7135
+ const [id2, body2] = chunks.genObjId();
7136
+ await proxy.putObject({ obj_id: id2.toString(), obj_data: body2 });
7137
+ content = id2.toString();
7138
+ }
7139
+ const fileObject = new ndn.FileObject(`${snapshot.digest}.pikg`, snapshot.size, content);
7140
+ const [id, body] = fileObject.genObjId();
7141
+ await proxy.putObject({ obj_id: id.toString(), obj_data: body });
7142
+ fileObjectId = id.toString();
7031
7143
  } catch (error) {
7032
- if (uploadError === void 0)
7144
+ if (error instanceof ToolError)
7033
7145
  throw error;
7146
+ ctx.signal.throwIfAborted();
7034
7147
  throw new ToolError(
7035
7148
  "PIKG_UPLOAD_FAILED",
7036
- `failed to upload PIKG snapshot: ${errorMessage(uploadError)}`,
7149
+ `failed to upload PIKG: ${errorMessage(error)}`,
7037
7150
  5,
7038
- true,
7039
- { finalize_error: errorMessage(error) }
7151
+ true
7040
7152
  );
7041
7153
  }
7154
+ const value = expectObject$1(
7155
+ await callControl(ctx, "apps.staging.finalize", {
7156
+ ...identity,
7157
+ source_obj_id: fileObjectId
7158
+ }),
7159
+ "apps.staging.finalize response"
7160
+ );
7161
+ return parseStagingMetadata(value);
7162
+ }
7163
+ function parseStagingMetadata(value) {
7042
7164
  return {
7043
7165
  schema_version: expectNumber(value, "schema_version"),
7044
7166
  handle: expectString$1(value, "handle"),
@@ -7522,11 +7644,6 @@ function validatePikgSnapshot(bytes) {
7522
7644
  throw new UsageError("INVALID_PIKG", "PIKG magic mismatch: expected a ZIP container");
7523
7645
  }
7524
7646
  }
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
7647
  function gatewayOrigin(endpoint) {
7531
7648
  const url = new URL(endpoint);
7532
7649
  const marker = url.pathname.indexOf("/kapi");