superbee 0.4.3-pre.7 → 0.4.3-pre.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/superbee.mjs CHANGED
@@ -44,7 +44,7 @@ var __toESM = (mod, isNodeMode, target3) => (target3 = mod != null ? __create(__
44
44
  var define_SUPERBEE_BUILD_IDENTITY_default;
45
45
  var init_define_SUPERBEE_BUILD_IDENTITY = __esm({
46
46
  "<define:__SUPERBEE_BUILD_IDENTITY__>"() {
47
- define_SUPERBEE_BUILD_IDENTITY_default = { schema: "superbee.build-identity.v1", package: { name: "superbee", version: "0.4.3-pre.7" }, source: { commit: "9d53ab5b75db0b4c25ab0a214551d4211cc150a8", dirty: false }, artifact: { channel: "npm-package" }, compatibility_contracts: { skill: 1, hook: 1, mcp: 1 } };
47
+ define_SUPERBEE_BUILD_IDENTITY_default = { schema: "superbee.build-identity.v1", package: { name: "superbee", version: "0.4.3-pre.8" }, source: { commit: "9d37b0a82d04092a95ebb2fd95d9faf93964d384", dirty: false }, artifact: { channel: "npm-package" }, compatibility_contracts: { skill: 1, hook: 1, mcp: 1 } };
48
48
  }
49
49
  });
50
50
 
@@ -27545,7 +27545,7 @@ var init_whole_document_transport = __esm({
27545
27545
  init_version_transport();
27546
27546
  init_read_adapter();
27547
27547
  HOSTED_MANAGED_FIELDS = /* @__PURE__ */ new Set(["actor", "superbee_updated_by", "generated", "verified", "timestamp", "okf_version"]);
27548
- IMPORT_CARRIED_FIELDS = Object.freeze(["generated", "timestamp", "actor", "okf_version"]);
27548
+ IMPORT_CARRIED_FIELDS = Object.freeze(["generated", "timestamp", "actor", "okf_version", "superbee_updated_by", "verified"]);
27549
27549
  IMPORT_DROPPED_FIELDS = Object.freeze([...HOSTED_MANAGED_FIELDS].filter((field) => !IMPORT_CARRIED_FIELDS.includes(field)));
27550
27550
  FRONTMATTER_KEY_LIMIT = 32;
27551
27551
  UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
@@ -28287,12 +28287,37 @@ async function deleteLocal(local, id, options = {}) {
28287
28287
  }
28288
28288
  }
28289
28289
  async function retireQuietly(backend, mode, target3) {
28290
+ let failed = false, failure3;
28290
28291
  try {
28291
28292
  await retireAcknowledgedBody(backend, mode, target3);
28292
28293
  } catch (error51) {
28293
- const name = error51?.name;
28294
- if (!(error51 instanceof JournalGuardConflict) && !(typeof name === "string" && TRANSIENT_STORAGE_ERRORS.has(name))) throw error51;
28294
+ failed = true;
28295
+ failure3 = error51;
28295
28296
  }
28297
+ try {
28298
+ if (!failed || failure3 instanceof JournalGuardConflict) retirementFailures.get(backend)?.delete(target3);
28299
+ else recordRetirementFailure(backend, target3, failure3);
28300
+ } catch {
28301
+ }
28302
+ }
28303
+ function recordRetirementFailure(backend, target3, error51) {
28304
+ let failures = retirementFailures.get(backend);
28305
+ if (!failures) retirementFailures.set(backend, failures = /* @__PURE__ */ new Map());
28306
+ const previous3 = failures.get(target3);
28307
+ const described = describeRetirementError(error51);
28308
+ failures.set(target3, { target: target3, failures: (previous3?.failures ?? 0) + 1, error: described, since: previous3?.since ?? (/* @__PURE__ */ new Date()).toISOString() });
28309
+ }
28310
+ function describeRetirementError(error51) {
28311
+ let name = "Error", message = "";
28312
+ try {
28313
+ const candidate = error51;
28314
+ if (candidate !== null && typeof candidate === "object") {
28315
+ if (typeof candidate.name === "string" && candidate.name) name = candidate.name;
28316
+ if (typeof candidate.message === "string") message = candidate.message;
28317
+ } else message = String(error51);
28318
+ } catch {
28319
+ }
28320
+ return { name: name.slice(0, 64), message: message.slice(0, RETIREMENT_MESSAGE_CHARS) };
28296
28321
  }
28297
28322
  function conflictResolutionKey(id) {
28298
28323
  return `conflict-resolution:${id}`;
@@ -29055,7 +29080,7 @@ async function reclaimInFlight(local) {
29055
29080
  }
29056
29081
  return reclaimed;
29057
29082
  }
29058
- var BOOTSTRAP_KEY, SYNC_KEY, PULL_KEY, ACKNOWLEDGED_KEY, EMPTY_REGISTRY, UNSETTLED_STATES, DEFAULT_BATCH_SIZE, DEFAULT_CONCURRENCY, MIN_BOUNDED_DELETIONS, COMPOSE_ATTEMPTS, TRANSIENT_STORAGE_ERRORS, ConflictReviewStaleError, BUSY_REFUSAL_CODES, PULL_MARK_ATTEMPTS;
29083
+ var BOOTSTRAP_KEY, SYNC_KEY, PULL_KEY, ACKNOWLEDGED_KEY, EMPTY_REGISTRY, UNSETTLED_STATES, DEFAULT_BATCH_SIZE, DEFAULT_CONCURRENCY, MIN_BOUNDED_DELETIONS, COMPOSE_ATTEMPTS, RETIREMENT_MESSAGE_CHARS, retirementFailures, ConflictReviewStaleError, BUSY_REFUSAL_CODES, PULL_MARK_ATTEMPTS;
29059
29084
  var init_local_bundle = __esm({
29060
29085
  "../browser-local/src/local-bundle.ts"() {
29061
29086
  "use strict";
@@ -29084,7 +29109,8 @@ var init_local_bundle = __esm({
29084
29109
  DEFAULT_CONCURRENCY = 8;
29085
29110
  MIN_BOUNDED_DELETIONS = 8;
29086
29111
  COMPOSE_ATTEMPTS = 3;
29087
- TRANSIENT_STORAGE_ERRORS = /* @__PURE__ */ new Set(["AbortError", "InvalidStateError", "TransactionInactiveError", "UnknownError"]);
29112
+ RETIREMENT_MESSAGE_CHARS = 512;
29113
+ retirementFailures = /* @__PURE__ */ new WeakMap();
29088
29114
  ConflictReviewStaleError = class extends Error {
29089
29115
  constructor() {
29090
29116
  super("The conflict changed since review. Inspect it again before choosing a resolution.");
@@ -134372,9 +134398,806 @@ var init_publish_state = __esm({
134372
134398
  }
134373
134399
  });
134374
134400
 
134375
- // ../cli/src/commands/checkout-adopt.ts
134401
+ // ../cli/src/commands/export.ts
134402
+ import { randomBytes as randomBytes9 } from "node:crypto";
134403
+ import { homedir as homedir39 } from "node:os";
134404
+ import { link as link3, lstat as lstat8, mkdir as mkdir4, open as open4, readdir as readdir8, realpath as realpath11, rename as rename2, rm as rm2, unlink as unlink13 } from "node:fs/promises";
134376
134405
  import path54 from "node:path";
134377
- import { lstat as lstat8, realpath as realpath11 } from "node:fs/promises";
134406
+ import { parseArgs as parseArgs41 } from "node:util";
134407
+ function exportDeps(partial2) {
134408
+ return {
134409
+ stdout: partial2.stdout ?? ((text5) => void process.stdout.write(text5)),
134410
+ auth: partial2.auth ?? defaultHostedAuthDeps(homedir39()),
134411
+ cwd: partial2.cwd ?? process.cwd(),
134412
+ ...partial2.fetch ? { fetch: partial2.fetch } : {}
134413
+ };
134414
+ }
134415
+ async function entryKind(target3) {
134416
+ let info;
134417
+ try {
134418
+ info = await lstat8(target3);
134419
+ } catch (error51) {
134420
+ if (error51.code === "ENOENT") return "absent";
134421
+ throw error51;
134422
+ }
134423
+ if (!info.isDirectory()) return "other";
134424
+ return (await readdir8(target3)).length === 0 ? "empty-dir" : "dir";
134425
+ }
134426
+ function shown(paths) {
134427
+ return [...paths].sort().slice(0, SHOWN);
134428
+ }
134429
+ function refusalCode(body) {
134430
+ const error51 = body?.error;
134431
+ if (typeof error51 === "string") return error51;
134432
+ const code3 = error51?.code;
134433
+ return typeof code3 === "string" ? code3 : void 0;
134434
+ }
134435
+ async function namedClient(source, deps2, resume2) {
134436
+ const { target: target3 } = source;
134437
+ const account = await connectHostedAccount(target3, { ...source.workspace !== null ? { workspace: source.workspace } : {}, resume: resume2, deadlineMs: EXPORT_DEADLINE_MS }, deps2);
134438
+ const { reference } = resolveBundleReference(
134439
+ { slug: source.slug, bundleId: source.bundleId },
134440
+ account,
134441
+ target3,
134442
+ (named) => `${cliInvocation()} export ${commandToken(named)} --host ${commandToken(hostArgument(target3))} --to <folder>`
134443
+ );
134444
+ return { client: reference.slug === null ? account.client : account.client.within(reference.slug), slug: reference.slug };
134445
+ }
134446
+ async function fetchExport(source, deps2, resume2) {
134447
+ const named = source.binding ? null : await namedClient(source, deps2, resume2);
134448
+ const client = named ? named.client : (await connectCheckout(source.binding, { resume: resume2, deadlineMs: EXPORT_DEADLINE_MS }, deps2)).client;
134449
+ const slug = named ? named.slug : source.binding.workspace_slug ?? null;
134450
+ return fetchExportThrough({ client, slug, source, resume: resume2 });
134451
+ }
134452
+ async function fetchExportThrough(request3) {
134453
+ const { source } = request3;
134454
+ const { bundleId: bundleId2, target: target3 } = source;
134455
+ for (let attempt = 1; ; attempt += 1) {
134456
+ const chain = new ExportPageChain();
134457
+ let bytes = 0;
134458
+ let cursor;
134459
+ let moved = false;
134460
+ for (; ; ) {
134461
+ let page;
134462
+ for (let tries = 1; page === void 0; tries += 1) {
134463
+ const answer2 = await exportAnswer(request3, { bundleId: bundleId2, paged: true, ...cursor !== void 0 ? { cursor } : {} });
134464
+ if (answer2.refused === "invalid_input" && cursor === void 0 && attempt === 1) return verified(await readArchive(await wholeAnswer(request3), source, MAX_EXPORT_BYTES), source);
134465
+ if (answer2.refused === "concurrent_change") {
134466
+ moved = true;
134467
+ break;
134468
+ }
134469
+ try {
134470
+ if (answer2.refused !== void 0) throw await exportRefusal(request3, answer2.refused, answer2.status);
134471
+ page = verified(await readArchive(answer2.ok, source, MAX_EXPORT_BYTES - bytes), source, { paged: true });
134472
+ } catch (error51) {
134473
+ const again = error51 instanceof CliError && (error51.details?.reason === "export_incomplete" || answer2.refused === "backend_unavailable" && error51.code === "TRANSIENT");
134474
+ if (!again || tries >= 2) throw error51;
134475
+ await pause2(PAGE_RETRY_MS);
134476
+ }
134477
+ }
134478
+ if (moved) break;
134479
+ bytes += page.bytes;
134480
+ cursor = joining(source, () => chain.add(page));
134481
+ if (cursor === void 0) return joining(source, () => chain.joined());
134482
+ }
134483
+ if (attempt >= PAGED_EXPORT_ATTEMPTS) {
134484
+ throw new CliError("TRANSIENT", `'${bundleId2}' kept changing on ${target3.origin} while it was exported; nothing was written`, {
134485
+ details: { reason: "export_source_changed", bundle_id: bundleId2, host: target3.origin, retryable: true },
134486
+ help: "retry the same command once the bundle is not being edited"
134487
+ });
134488
+ }
134489
+ const base = PAGED_EXPORT_RESTART_MS[attempt - 1];
134490
+ await pause2(Math.round(base / 2 + Math.random() * base / 2));
134491
+ }
134492
+ }
134493
+ async function exportAnswer({ client, source, resume: resume2 }, input) {
134494
+ let answer2;
134495
+ try {
134496
+ answer2 = await client.carrier.stream(`${client.prefix}/export`, input, client.signal);
134497
+ } catch (error51) {
134498
+ throw hostedFailure(error51, source.target, resume2);
134499
+ }
134500
+ if (!answer2.ok) return { refused: refusalCode(answer2.body) ?? "RUNTIME", status: answer2.status };
134501
+ return { ok: answer2.body };
134502
+ }
134503
+ async function wholeAnswer(request3) {
134504
+ const answer2 = await exportAnswer(request3, { bundleId: request3.source.bundleId });
134505
+ if (answer2.refused !== void 0) throw await exportRefusal(request3, answer2.refused, answer2.status);
134506
+ return answer2.ok;
134507
+ }
134508
+ function exportTooLarge(source) {
134509
+ return new CliError("RUNTIME", `${source.target.origin} answered an export larger than ${MAX_EXPORT_BYTES} bytes`, {
134510
+ details: { reason: "export_too_large", host: source.target.origin, bundle_id: source.bundleId, retryable: false, next: "the person exports it with Export bundle in the Superbee app" },
134511
+ help: `${cliInvocation()} whoami --host ${commandToken(hostArgument(source.target))}`
134512
+ });
134513
+ }
134514
+ async function exportRefusal({ client, slug, source, resume: resume2 }, code3, status2) {
134515
+ const { target: target3, bundleId: bundleId2 } = source;
134516
+ if (code3 === "bundle_not_found") {
134517
+ if (source.binding) return await bundleAbsent(source.binding, client);
134518
+ const text5 = hostedBundleReferenceText({ slug, bundleId: bundleId2 });
134519
+ return new CliError("NOT_FOUND", `no hosted bundle '${text5}' is visible to you on ${target3.origin}`, {
134520
+ details: { bundle_id: bundleId2, ...slug !== null ? { reference: text5 } : {}, host: target3.origin },
134521
+ help: hostedListCommand(target3)
134522
+ });
134523
+ }
134524
+ if (code3 === "result_too_large") {
134525
+ return new CliError("FORBIDDEN", `hosted bundle '${bundleId2}' is larger than an export carries`, {
134526
+ details: { reason: "bundle_too_large", bundle_id: bundleId2, host: target3.origin, next: "the person exports it with Export bundle in the Superbee app" },
134527
+ help: `${cliInvocation()} whoami --host ${commandToken(hostArgument(target3))}`
134528
+ });
134529
+ }
134530
+ if (code3 === "validation_failed") {
134531
+ return new CliError("FORBIDDEN", `hosted bundle '${bundleId2}' holds a path an export cannot carry`, {
134532
+ details: { reason: "unexportable_path", bundle_id: bundleId2, host: target3.origin, next: "the person renames the document or file in the Superbee app, then the same command is retried" },
134533
+ help: `${cliInvocation()} whoami --host ${commandToken(hostArgument(target3))}`
134534
+ });
134535
+ }
134536
+ return hostedFailure(new RemoteError(`hosted export answered ${status2}`, code3, status2), target3, resume2);
134537
+ }
134538
+ async function readArchive(body, source, budget) {
134539
+ const { target: target3, bundleId: bundleId2 } = source;
134540
+ const reader = body.getReader();
134541
+ const chunks = [];
134542
+ let size = 0;
134543
+ try {
134544
+ for (; ; ) {
134545
+ const part = await reader.read();
134546
+ if (part.done) break;
134547
+ size += part.value.byteLength;
134548
+ if (size > budget) throw exportTooLarge(source);
134549
+ chunks.push(part.value);
134550
+ }
134551
+ } catch (error51) {
134552
+ if (error51 instanceof CliError) throw error51;
134553
+ throw new CliError("TRANSIENT", `the export of '${bundleId2}' from ${target3.origin} stopped part way; nothing was written`, {
134554
+ details: { reason: "export_incomplete", bundle_id: bundleId2, host: target3.origin, retryable: true },
134555
+ help: "retry the same command"
134556
+ });
134557
+ } finally {
134558
+ void reader.cancel().catch(() => {
134559
+ });
134560
+ }
134561
+ const archive = new Uint8Array(size);
134562
+ let offset = 0;
134563
+ for (const chunk of chunks) {
134564
+ archive.set(chunk, offset);
134565
+ offset += chunk.byteLength;
134566
+ }
134567
+ return archive;
134568
+ }
134569
+ function verified(archive, source, options = {}) {
134570
+ return refusingArchive(source, () => verifyExport(archive, source.bundleId, options));
134571
+ }
134572
+ function joining(source, join19) {
134573
+ return refusingArchive(source, join19);
134574
+ }
134575
+ function refusingArchive(source, verify) {
134576
+ try {
134577
+ return verify();
134578
+ } catch (error51) {
134579
+ if (!(error51 instanceof ExportArchiveError)) throw error51;
134580
+ if (error51.problem === "incomplete") {
134581
+ throw new CliError("TRANSIENT", `the export of '${source.bundleId}' was not completed by the host; nothing was written`, {
134582
+ details: { reason: "export_incomplete", bundle_id: source.bundleId, host: source.target.origin, retryable: true },
134583
+ help: "retry the same command"
134584
+ });
134585
+ }
134586
+ throw new CliError("RUNTIME", `${source.target.origin} answered an export this CLI refuses (${error51.message}); nothing was written`, {
134587
+ details: { reason: `export_${error51.problem}`, bundle_id: source.bundleId, host: source.target.origin, retryable: false },
134588
+ help: "upgrade Superbee (npm install -g superbee); if it persists, report this bundle and reason"
134589
+ });
134590
+ }
134591
+ }
134592
+ async function writeNewFile(file2, bytes) {
134593
+ const handle = await open4(file2, "wx", 420);
134594
+ try {
134595
+ await handle.writeFile(bytes);
134596
+ await handle.sync();
134597
+ } finally {
134598
+ await handle.close();
134599
+ }
134600
+ }
134601
+ async function writeEntries(root, entries) {
134602
+ for (const entry of entries) {
134603
+ const file2 = path54.join(root, ...entry.path.split("/"));
134604
+ await mkdir4(path54.dirname(file2), { recursive: true });
134605
+ await writeNewFile(file2, entry.bytes);
134606
+ }
134607
+ }
134608
+ function gitRun(dir, args) {
134609
+ let result3;
134610
+ try {
134611
+ result3 = runGit(dir, args);
134612
+ } catch (error51) {
134613
+ const missing2 = error51.code === "GIT_MISSING";
134614
+ throw new CliError(missing2 ? "GIT_MISSING" : "RUNTIME", `git could not run (${error51 instanceof Error ? error51.message : String(error51)})`, {
134615
+ details: { reason: missing2 ? "git_missing" : "git_unavailable" },
134616
+ help: "install git, or export without --git"
134617
+ });
134618
+ }
134619
+ if (result3.status !== 0) {
134620
+ throw new CliError("RUNTIME", `git ${args.find((arg) => !arg.startsWith("-") && !arg.includes("=")) ?? ""} failed: ${result3.stderr.trim().split("\n").at(-1) ?? ""}`, {
134621
+ details: { reason: "git_failed" },
134622
+ help: "fix the git error, then retry the same command"
134623
+ });
134624
+ }
134625
+ return result3.stdout.trim();
134626
+ }
134627
+ function commitExport(dir, message, options) {
134628
+ const quiet = ["-c", "core.hooksPath=/dev/null"];
134629
+ gitRun(dir, [...quiet, "init", "-q"]);
134630
+ gitRun(dir, ["symbolic-ref", "HEAD", `refs/heads/${BOARD_BRANCH}`]);
134631
+ const excluded = ["--", ".", `:(exclude)${IN_PLACE_STAGING}`, `:(exclude)${CHECKOUT_MARKER_DIR}`];
134632
+ gitRun(dir, ["add", "-A", ...options.force ? ["--force"] : [], ...excluded]);
134633
+ gitRun(dir, [...quiet, ...identityFlags(dir), "-c", "commit.gpgsign=false", "commit", "-q", "--no-verify", "--allow-empty", "-m", message]);
134634
+ const committed = { branch: BOARD_BRANCH, commit: gitRun(dir, ["rev-parse", "HEAD"]) };
134635
+ if (options.force) return committed;
134636
+ const ignored = gitRun(dir, ["ls-files", "--others", "--ignored", "--exclude-standard", ...excluded]).split("\n").filter(Boolean);
134637
+ return ignored.length === 0 ? committed : { ...committed, ignored: { count: ignored.length, paths: shown(ignored) } };
134638
+ }
134639
+ function commitMessage(source, exported) {
134640
+ return `Export ${source.bundleId} from ${source.target.origin} at revision ${exported.source.revision}
134641
+
134642
+ superbee export, ${exported.exportedAt}: ${exported.counts.documents} documents, ${exported.counts.reserved} reserved files, ${exported.counts.blobs} blobs. History is not included.
134643
+ `;
134644
+ }
134645
+ function stagingName(target3) {
134646
+ return path54.join(path54.dirname(target3), `.${path54.basename(target3)}.superbee-export-${process.pid}-${randomBytes9(6).toString("hex")}.partial`);
134647
+ }
134648
+ function processAlive(pid) {
134649
+ try {
134650
+ process.kill(pid, 0);
134651
+ return true;
134652
+ } catch (error51) {
134653
+ return error51.code === "EPERM";
134654
+ }
134655
+ }
134656
+ async function removeAbandonedStaging(target3) {
134657
+ const prefix = `.${path54.basename(target3)}.superbee-export-`;
134658
+ let removed = 0;
134659
+ let names;
134660
+ try {
134661
+ names = await readdir8(path54.dirname(target3));
134662
+ } catch {
134663
+ return 0;
134664
+ }
134665
+ for (const name of names) {
134666
+ if (!name.startsWith(prefix)) continue;
134667
+ const match = /^(\d+)-[0-9a-f]{12}\.partial$/.exec(name.slice(prefix.length));
134668
+ if (!match) continue;
134669
+ const age = await lstat8(path54.join(path54.dirname(target3), name)).then((info) => Date.now() - info.mtimeMs, () => 0);
134670
+ if (processAlive(Number(match[1])) && age < ABANDONED_AFTER_MS) continue;
134671
+ await rm2(path54.join(path54.dirname(target3), name), { recursive: true, force: true });
134672
+ removed += 1;
134673
+ }
134674
+ return removed;
134675
+ }
134676
+ async function assertStandalone(target3, home2) {
134677
+ const parent = path54.dirname(target3);
134678
+ const help2 = "pass --to <folder> outside any bundle, board or checkout";
134679
+ const checkout2 = await hostedCheckoutAt(parent, home2);
134680
+ if (checkout2) {
134681
+ throw new CliError("FORBIDDEN", `refusing to export inside the hosted checkout at ${checkout2.path}`, { details: { reason: "inside_checkout", enclosing: checkout2.path }, help: help2 });
134682
+ }
134683
+ const enclosing = await findBundleRoot(parent).catch(() => null);
134684
+ if (enclosing) {
134685
+ throw new CliError("FORBIDDEN", `refusing to export inside the local bundle at ${enclosing}`, { details: { reason: "inside_bundle", enclosing }, help: help2 });
134686
+ }
134687
+ const binding = await resolveProjectBinding(parent).catch(() => null);
134688
+ if (binding) {
134689
+ throw new CliError("FORBIDDEN", `refusing to export inside a project bound to a board (${binding.file})`, { details: { reason: "inside_bound_project", binding: binding.file }, help: help2 });
134690
+ }
134691
+ }
134692
+ async function exportToFolder(source, toArg, git2, deps2, mode, resume2) {
134693
+ const requested = path54.resolve(deps2.cwd, toArg);
134694
+ assertBundleOutsidePrivateState(requested, deps2.auth.home);
134695
+ const found = await entryKind(requested);
134696
+ if (found === "other") throw new CliError("ALREADY_EXISTS", `${requested} exists and is not a folder`, { details: { reason: "not_a_folder", folder: requested }, help: "pass --to <new folder>" });
134697
+ if (found === "dir") throw new CliError("ALREADY_EXISTS", `${requested} is not empty`, { details: { reason: "not_empty", folder: requested }, help: "pass --to <new or empty folder>" });
134698
+ await mkdir4(path54.dirname(requested), { recursive: true });
134699
+ const target3 = path54.join(await realpath11(path54.dirname(requested)), path54.basename(requested));
134700
+ assertBundleOutsidePrivateState(target3, deps2.auth.home);
134701
+ await assertStandalone(target3, deps2.auth.home);
134702
+ const exported = await fetchExport(source, deps2, resume2);
134703
+ const abandoned = await removeAbandonedStaging(target3);
134704
+ const staging = stagingName(target3);
134705
+ await mkdir4(staging);
134706
+ let gitResult = null;
134707
+ try {
134708
+ await writeEntries(staging, exported.entries);
134709
+ if (git2) gitResult = commitExport(staging, commitMessage(source, exported), { force: true });
134710
+ const now = await entryKind(target3);
134711
+ if (now === "dir" || now === "other") throw new CliError("ALREADY_EXISTS", `${target3} is no longer empty`, { details: { reason: "not_empty", folder: target3 }, help: "pass --to <new or empty folder>" });
134712
+ try {
134713
+ await rename2(staging, target3);
134714
+ } catch (error51) {
134715
+ const code3 = error51.code;
134716
+ if (code3 === "ENOTEMPTY" || code3 === "EEXIST" || code3 === "ENOTDIR") {
134717
+ throw new CliError("ALREADY_EXISTS", `${target3} is no longer empty`, { details: { reason: "not_empty", folder: target3 }, help: "pass --to <new or empty folder>" });
134718
+ }
134719
+ throw error51;
134720
+ }
134721
+ } catch (error51) {
134722
+ await rm2(staging, { recursive: true, force: true }).catch(() => {
134723
+ });
134724
+ throw error51;
134725
+ }
134726
+ const home2 = await bundleHomeAt(target3, { home: deps2.auth.home });
134727
+ deps2.stdout(
134728
+ render(
134729
+ {
134730
+ export: "created",
134731
+ folder: target3,
134732
+ home: home2.home,
134733
+ bundle_id: source.bundleId,
134734
+ host: source.target.origin,
134735
+ revision: exported.source.revision,
134736
+ exported_at: exported.exportedAt,
134737
+ okf_edition: exported.source.okfEdition,
134738
+ documents: exported.counts.documents,
134739
+ reserved: exported.counts.reserved,
134740
+ blobs: exported.counts.blobs,
134741
+ bytes: exported.bytes,
134742
+ root_index: exported.entries.some((entry) => entry.path === "index.md"),
134743
+ ...gitResult ? { git: gitResult } : {},
134744
+ ...abandoned > 0 ? { removed_abandoned_exports: abandoned } : {},
134745
+ hosted: HOSTED_UNCHANGED,
134746
+ history: NO_HISTORY,
134747
+ help: nextSteps(target3, gitResult !== null)
134748
+ },
134749
+ mode
134750
+ )
134751
+ );
134752
+ }
134753
+ function nextSteps(folder, git2) {
134754
+ const steps = [commandFragment`${cliInvocation()} status --dir ${commandToken(folder)}`];
134755
+ if (git2) steps.push(commandFragment`git -C ${commandToken(folder)} remote add origin <url> && ${cliInvocation()} sync --establish --dir ${commandToken(folder)}`);
134756
+ return steps;
134757
+ }
134758
+ function isJournal(value) {
134759
+ const record6 = value;
134760
+ return record6 !== null && typeof record6 === "object" && record6.schema === JOURNAL_SCHEMA && typeof record6.bundle_id === "string" && typeof record6.host === "string" && typeof record6.audience === "string" && Number.isSafeInteger(record6.revision) && typeof record6.exported_at === "string" && typeof record6.git === "boolean" && Array.isArray(record6.adds) && record6.adds.every((entry) => typeof entry === "string") && Number.isSafeInteger(record6.same) && Array.isArray(record6.kept_local) && record6.kept_local.every((entry) => typeof entry === "string") && Array.isArray(record6.deleted_locally) && record6.deleted_locally.every((entry) => typeof entry === "string") && Number.isSafeInteger(record6.unsent_kept);
134761
+ }
134762
+ async function stagingDir(folder) {
134763
+ const staging = path54.join(folder, IN_PLACE_STAGING);
134764
+ try {
134765
+ if (!(await lstat8(staging)).isDirectory()) return null;
134766
+ return await realpath11(staging) === path54.join(await realpath11(folder), IN_PLACE_STAGING) ? staging : null;
134767
+ } catch {
134768
+ return null;
134769
+ }
134770
+ }
134771
+ async function readJournal(folder) {
134772
+ const staging = await stagingDir(folder);
134773
+ if (!staging) return null;
134774
+ let journal;
134775
+ const read = readRegularFileNoFollowSync(path54.join(staging, JOURNAL));
134776
+ if (read.state !== "present" || read.bytes.byteLength > MAX_JOURNAL_BYTES) return null;
134777
+ try {
134778
+ const value = JSON.parse(read.bytes.toString("utf8"));
134779
+ if (!isJournal(value)) return null;
134780
+ journal = value;
134781
+ } catch {
134782
+ return null;
134783
+ }
134784
+ const marker = readCheckoutMarker(folder);
134785
+ if (!marker || marker.bundle_id !== journal.bundle_id || marker.host !== bindingHostArgument({ origin: journal.host, audience: journal.audience })) return null;
134786
+ return journal;
134787
+ }
134788
+ async function sameBytes(file2, bytes) {
134789
+ const read = readRegularFileNoFollowSync(file2);
134790
+ return read.state === "present" && Buffer.compare(read.bytes, Buffer.from(bytes)) === 0;
134791
+ }
134792
+ async function placementOf(folder, entry) {
134793
+ let info;
134794
+ try {
134795
+ info = await lstat8(path54.join(folder, ...entry.path.split("/")));
134796
+ } catch (error51) {
134797
+ const code3 = error51.code;
134798
+ if (code3 === "ENOENT") return "add";
134799
+ if (code3 === "ENOTDIR") return "kept";
134800
+ throw error51;
134801
+ }
134802
+ if (!info.isFile()) return "kept";
134803
+ return await sameBytes(path54.join(folder, ...entry.path.split("/")), entry.bytes) ? "same" : "kept";
134804
+ }
134805
+ async function placeStaged(folder, staging, relative2) {
134806
+ const final = path54.join(folder, ...relative2.split("/"));
134807
+ const staged = path54.join(staging, "files", ...relative2.split("/"));
134808
+ try {
134809
+ if ((await lstat8(staged)).isFile()) {
134810
+ const stagedParent = await realpath11(path54.dirname(staged));
134811
+ if (!stagedParent.startsWith(`${await realpath11(staging)}${path54.sep}`)) return "kept";
134812
+ } else return "kept";
134813
+ } catch (error51) {
134814
+ if (error51.code !== "ENOENT") return "kept";
134815
+ return await lstat8(final).then((info) => info.isFile(), () => false) ? "same" : "kept";
134816
+ }
134817
+ try {
134818
+ await mkdir4(path54.dirname(final), { recursive: true });
134819
+ const parent = await realpath11(path54.dirname(final));
134820
+ if (parent !== folder && !parent.startsWith(`${folder}${path54.sep}`)) return "kept";
134821
+ } catch {
134822
+ return "kept";
134823
+ }
134824
+ let outcome;
134825
+ try {
134826
+ await link3(staged, final);
134827
+ outcome = "added";
134828
+ } catch (error51) {
134829
+ const code3 = error51.code;
134830
+ if (code3 !== "EEXIST") throw error51;
134831
+ const stagedRead = readRegularFileNoFollowSync(staged);
134832
+ outcome = stagedRead.state === "present" && await sameBytes(final, stagedRead.bytes) ? "same" : "kept";
134833
+ }
134834
+ await unlink13(staged).catch(() => {
134835
+ });
134836
+ return outcome;
134837
+ }
134838
+ async function writeJournal(staging, journal) {
134839
+ const temp = path54.join(staging, `.${JOURNAL}.${randomBytes9(6).toString("hex")}.tmp`);
134840
+ await writeNewFile(temp, Buffer.from(`${JSON.stringify(journal)}
134841
+ `, "utf8"));
134842
+ await rename2(temp, path54.join(staging, JOURNAL));
134843
+ }
134844
+ async function finishInPlace(folder, journal) {
134845
+ const staging = await stagingDir(folder);
134846
+ if (!staging) throw new CliError("RUNTIME", `the in-place export's staging folder in ${folder} is gone`, { help: `${cliInvocation()} export --in-place --dir ${commandToken(folder)}` });
134847
+ const added = [];
134848
+ const kept = [...journal.kept_local];
134849
+ let same = journal.same;
134850
+ for (const relative2 of journal.adds) {
134851
+ classifyEntryPath(relative2);
134852
+ const outcome = await placeStaged(folder, staging, relative2);
134853
+ if (outcome === "added") added.push(relative2);
134854
+ else if (outcome === "same") same += 1;
134855
+ else kept.push(relative2);
134856
+ }
134857
+ let git2 = null;
134858
+ if (journal.git) {
134859
+ git2 = await ownCommit(folder) ?? commitExport(folder, `Export ${journal.bundle_id} from ${journal.host} at revision ${journal.revision}
134860
+
134861
+ superbee export --in-place, ${journal.exported_at}. History is not included.
134862
+ `, { force: false });
134863
+ }
134864
+ await rm2(path54.join(staging, "files"), { recursive: true, force: true });
134865
+ await unlink13(path54.join(staging, JOURNAL)).catch(() => {
134866
+ });
134867
+ await rm2(staging, { recursive: true, force: true });
134868
+ const marker = await removeCheckoutMarker(folder, { origin: journal.host, audience: journal.audience, bundle_id: journal.bundle_id }).catch(() => false) ? "removed" : "none";
134869
+ return { added, kept, same, git: git2, marker };
134870
+ }
134871
+ function inPlaceReceipt(folder, journal, finished, homeName, resumed) {
134872
+ return {
134873
+ export: "converted",
134874
+ folder,
134875
+ home: homeName,
134876
+ bundle_id: journal.bundle_id,
134877
+ host: journal.host,
134878
+ revision: journal.revision,
134879
+ exported_at: journal.exported_at,
134880
+ ...resumed ? { resumed: true } : {},
134881
+ binding: "removed (the folder is no longer a hosted checkout)",
134882
+ marker: finished.marker,
134883
+ added: finished.added.length,
134884
+ ...finished.added.length > 0 ? { added_paths: shown(finished.added) } : {},
134885
+ unchanged: finished.same,
134886
+ kept_local: finished.kept.length,
134887
+ ...finished.kept.length > 0 ? { kept_local_paths: shown(finished.kept), kept_local_note: "these files differ from the host's (or block its path); the folder's own were kept" } : {},
134888
+ ...journal.deleted_locally.length > 0 ? { deleted_locally: journal.deleted_locally.length, deleted_locally_paths: shown(journal.deleted_locally), deleted_locally_note: "deleted in this folder and not sent; the host's copies were not put back" } : {},
134889
+ ...journal.unsent_kept > 0 ? { unsent_kept: journal.unsent_kept, unsent_note: "changes sync had not sent stay in this folder only" } : {},
134890
+ ...finished.git ? { git: finished.git } : {},
134891
+ hosted: HOSTED_UNCHANGED,
134892
+ history: NO_HISTORY,
134893
+ help: nextSteps(folder, finished.git !== null)
134894
+ };
134895
+ }
134896
+ async function ownCommit(folder) {
134897
+ const head = runGit(folder, ["rev-parse", "--verify", "--quiet", "HEAD"]);
134898
+ const top = runGit(folder, ["rev-parse", "--show-toplevel"]);
134899
+ const own5 = top.status === 0 && await realpath11(top.stdout.trim()).catch(() => "") === await realpath11(folder).catch(() => folder);
134900
+ if (!own5 || head.status !== 0) return null;
134901
+ return { branch: runGit(folder, ["symbolic-ref", "-q", "--short", "HEAD"]).stdout.trim() || BOARD_BRANCH, commit: head.stdout.trim() };
134902
+ }
134903
+ async function assertNoRepository(folder) {
134904
+ if (await lstat8(path54.join(folder, ".git")).then(() => true, () => false)) {
134905
+ throw new CliError("ALREADY_EXISTS", `${folder} already holds a Git repository`, {
134906
+ details: { reason: "already_git", folder },
134907
+ help: `${cliInvocation()} export --in-place --dir ${commandToken(folder)}`
134908
+ });
134909
+ }
134910
+ }
134911
+ async function exportInPlace(dirArg, git2, keepUnsent, deps2, mode, resume2) {
134912
+ const requested = path54.resolve(deps2.cwd, dirArg ?? ".");
134913
+ assertBundleOutsidePrivateState(requested, deps2.auth.home);
134914
+ const binding = await hostedCheckoutFor(dirArg === void 0 ? [] : ["--dir", dirArg], deps2.auth.home, deps2.cwd);
134915
+ if (!binding) {
134916
+ const folder2 = await realpath11(requested).catch(() => requested);
134917
+ const journal = await readJournal(folder2);
134918
+ if (journal) {
134919
+ const finished = await finishInPlace(folder2, journal);
134920
+ const home4 = await bundleHomeAt(folder2, { home: deps2.auth.home });
134921
+ deps2.stdout(render(inPlaceReceipt(folder2, journal, finished, home4.home, true), mode));
134922
+ return;
134923
+ }
134924
+ if (await entryKind(folder2) === "absent") throw new CliError("NOT_FOUND", `${folder2} does not exist`, { help: `${cliInvocation()} export --in-place --dir <checkout folder>` });
134925
+ const marker = readCheckoutMarker(folder2);
134926
+ if (marker) {
134927
+ const existing = git2 ? await ownCommit(folder2) : null;
134928
+ if (git2 && !existing) await assertNoRepository(folder2);
134929
+ const staging = await stagingDir(folder2);
134930
+ if (staging && !await lstat8(path54.join(staging, JOURNAL)).then(() => true, () => false)) await rm2(staging, { recursive: true, force: true });
134931
+ await removeCheckoutMarker(folder2, { origin: marker.host, audience: `${marker.host}/mcp`, bundle_id: marker.bundle_id });
134932
+ const committed = existing ?? (git2 ? commitExport(folder2, `Export ${marker.bundle_id} (an unbound copy of a hosted checkout)
134933
+
134934
+ superbee export --in-place. Nothing was fetched from the host; history is not included.
134935
+ `, { force: false }) : null);
134936
+ const home4 = await bundleHomeAt(folder2, { home: deps2.auth.home });
134937
+ deps2.stdout(
134938
+ render(
134939
+ {
134940
+ export: "converted",
134941
+ folder: folder2,
134942
+ home: home4.home,
134943
+ bundle_id: marker.bundle_id,
134944
+ from: "an unbound copy of a hosted checkout",
134945
+ marker: "removed",
134946
+ fetched: false,
134947
+ note: `nothing was fetched: the files are kept as they are. To fill in what the copy lacks from the host first, adopt it (${cliInvocation()} checkout --adopt <folder> --host <url>), then export --in-place`,
134948
+ ...committed ? { git: committed } : {},
134949
+ hosted: HOSTED_UNCHANGED,
134950
+ history: NO_HISTORY,
134951
+ help: nextSteps(folder2, committed !== null)
134952
+ },
134953
+ mode
134954
+ )
134955
+ );
134956
+ return;
134957
+ }
134958
+ const home3 = await bundleHomeAt(folder2, { home: deps2.auth.home });
134959
+ deps2.stdout(render({ export: "unchanged", folder: folder2, home: home3.home, reason: "not a hosted checkout: nothing to convert" }, mode));
134960
+ return;
134961
+ }
134962
+ const folder = binding.path;
134963
+ if (git2) await assertNoRepository(folder);
134964
+ const status2 = await hostedStatus(binding, deps2.auth.home);
134965
+ if (status2.sync.state === "busy") {
134966
+ throw new CliError("CONFLICT", `another command holds the checkout lock for ${folder}`, { details: { reason: "checkout_busy", folder }, help: "wait for it to finish, then retry the same command" });
134967
+ }
134968
+ const unsentRefusal = (count5, sync3) => new CliError("CONFLICT", `the checkout of '${binding.bundle_id}' at ${folder} has ${count5} change(s) not on the host`, {
134969
+ details: {
134970
+ reason: "unsent_changes",
134971
+ folder,
134972
+ ...sync3,
134973
+ keep_them_here_only: commandFragment`${cliInvocation()} export --in-place --dir ${commandToken(folder)} --keep-unsent${git2 ? commandFragment` --git` : commandFragment``}`
134974
+ },
134975
+ help: commandFragment`${cliInvocation()} sync --dir ${commandToken(folder)}`
134976
+ });
134977
+ const early = Number(status2.sync.unsent ?? 0) + Number(status2.sync.conflicts ?? 0) + Number(status2.sync.held_files ?? 0) + Number(status2.sync.held_deletions ?? 0);
134978
+ if (early > 0 && !keepUnsent) throw unsentRefusal(early, status2.sync);
134979
+ const source = { target: resolveHostedTarget(binding.audience), bundleId: binding.bundle_id, slug: binding.workspace_slug ?? null, workspace: null, binding };
134980
+ const exported = await fetchExport(source, deps2, resume2);
134981
+ const result3 = await filesystemPushRoleLocks().request(checkoutLockName(folder), { ifAvailable: true }, async (lock) => {
134982
+ if (!lock) {
134983
+ throw new CliError("CONFLICT", `another command holds the checkout lock for ${folder}`, { details: { reason: "checkout_busy", folder }, help: "wait for it to finish, then retry the same command" });
134984
+ }
134985
+ const current = await bindingForPath(deps2.auth.home, folder);
134986
+ if (!current || current.checkout_id !== binding.checkout_id) {
134987
+ throw new CliError("CONFLICT", `the checkout at ${folder} changed while it was exported`, { details: { reason: "checkout_changed", folder }, help: "retry the same command" });
134988
+ }
134989
+ const store = await FileJournaledBackend.open({ directory: checkoutStoreDir(deps2.auth.home, binding.checkout_id), readOnly: true });
134990
+ let pending;
134991
+ try {
134992
+ const classified = await classifyCheckout(binding, deps2.auth.home, store);
134993
+ pending = /* @__PURE__ */ new Set([...classified.unsent, ...classified.conflicts, ...classified.held.keys(), ...classified.heldDeletions]);
134994
+ } finally {
134995
+ await store.close();
134996
+ }
134997
+ if (pending.size > 0 && !keepUnsent) throw unsentRefusal(pending.size, { unsent_ids: shown([...pending]) });
134998
+ const staging = path54.join(folder, IN_PLACE_STAGING);
134999
+ await rm2(staging, { recursive: true, force: true });
135000
+ await mkdir4(path54.join(staging, "files"), { recursive: true });
135001
+ const adds = [];
135002
+ const kept = [];
135003
+ const deletedLocally = [];
135004
+ let same = 0;
135005
+ for (const entry of exported.entries) {
135006
+ const id = entry.kind === "document" ? entry.path.slice(0, -3) : entry.path;
135007
+ const placement = await placementOf(folder, entry);
135008
+ if (placement === "add" && pending.has(id)) {
135009
+ deletedLocally.push(entry.path);
135010
+ continue;
135011
+ }
135012
+ if (placement === "same") same += 1;
135013
+ else if (placement === "kept") kept.push(entry.path);
135014
+ else {
135015
+ const file2 = path54.join(staging, "files", ...entry.path.split("/"));
135016
+ await mkdir4(path54.dirname(file2), { recursive: true });
135017
+ await writeNewFile(file2, entry.bytes);
135018
+ adds.push(entry.path);
135019
+ }
135020
+ }
135021
+ const journal = {
135022
+ schema: JOURNAL_SCHEMA,
135023
+ bundle_id: binding.bundle_id,
135024
+ host: binding.origin,
135025
+ audience: binding.audience,
135026
+ revision: exported.source.revision,
135027
+ exported_at: exported.exportedAt,
135028
+ git: git2,
135029
+ adds,
135030
+ same,
135031
+ kept_local: kept,
135032
+ deleted_locally: deletedLocally,
135033
+ unsent_kept: pending.size
135034
+ };
135035
+ await writeJournal(staging, journal);
135036
+ await releaseCheckout(deps2.auth.home, binding);
135037
+ return { journal, finished: await finishInPlace(folder, journal) };
135038
+ });
135039
+ const home2 = await bundleHomeAt(folder, { home: deps2.auth.home });
135040
+ deps2.stdout(render(inPlaceReceipt(folder, result3.journal, result3.finished, home2.home, false), mode));
135041
+ }
135042
+ async function exportCommand(argv2, partial2 = {}) {
135043
+ const deps2 = exportDeps(partial2);
135044
+ const { values, positionals } = parseLeafOrUsage(
135045
+ () => parseArgs41({
135046
+ args: argv2,
135047
+ options: {
135048
+ to: { type: "string" },
135049
+ "in-place": { type: "boolean" },
135050
+ dir: { type: "string" },
135051
+ host: { type: "string" },
135052
+ workspace: { type: "string" },
135053
+ git: { type: "boolean" },
135054
+ "keep-unsent": { type: "boolean" },
135055
+ json: { type: "boolean" },
135056
+ help: { type: "boolean", short: "h" }
135057
+ },
135058
+ allowPositionals: true
135059
+ }),
135060
+ CLI_LEAVES.export
135061
+ );
135062
+ if (values.help) {
135063
+ deps2.stdout(renderUsage(EXPORT_USAGE));
135064
+ return;
135065
+ }
135066
+ const mode = resolveMode(values);
135067
+ const usage3 = (message) => new CliError("USAGE", message, { help: `${cliInvocation()} export --help` });
135068
+ const named = positionals[0];
135069
+ const inPlace = values["in-place"] === true;
135070
+ if (inPlace === (values.to !== void 0)) throw usage3("pass exactly one of --to <folder> and --in-place");
135071
+ if (named !== void 0 && values.dir !== void 0) throw usage3("pass a bundle id or --dir <checkout>, not both");
135072
+ if (named === void 0 && (values.host !== void 0 || values.workspace !== void 0)) throw usage3("--host and --workspace go with a bundle id; a checkout uses its own");
135073
+ if (inPlace && named !== void 0) throw usage3("--in-place converts a checkout folder: pass --dir <checkout>, not a bundle id");
135074
+ if (!inPlace && values["keep-unsent"]) throw usage3("--keep-unsent goes with --in-place");
135075
+ const reference = named === void 0 ? void 0 : parseHostedBundleReference(named);
135076
+ if (named !== void 0 && !reference) throw usage3(`'${named}' is not a hosted bundle id or <workspace>/<bundle-id>`);
135077
+ const git2 = values.git === true;
135078
+ const resume2 = commandFragment`${cliInvocation()} export${named !== void 0 ? commandFragment` ${commandToken(named)}` : commandFragment``}${values.dir !== void 0 ? commandFragment` --dir ${commandToken(path54.resolve(deps2.cwd, values.dir))}` : commandFragment``}${values.to !== void 0 ? commandFragment` --to ${commandToken(path54.resolve(deps2.cwd, values.to))}` : commandFragment``}${inPlace ? commandFragment` --in-place` : commandFragment``}${values.host !== void 0 ? commandFragment` --host ${commandToken(values.host)}` : commandFragment``}${values.workspace !== void 0 ? commandFragment` --workspace ${commandToken(values.workspace)}` : commandFragment``}${git2 ? commandFragment` --git` : commandFragment``}${values["keep-unsent"] ? commandFragment` --keep-unsent` : commandFragment``}${values.json ? commandFragment` --json` : commandFragment``}`;
135079
+ if (values.dir !== void 0) assertBundleOutsidePrivateState(path54.resolve(deps2.cwd, values.dir), deps2.auth.home);
135080
+ if (inPlace) {
135081
+ await exportInPlace(values.dir, git2, values["keep-unsent"] === true, deps2, mode, resume2);
135082
+ return;
135083
+ }
135084
+ let source;
135085
+ if (reference) {
135086
+ const named2 = await requireHostedBundleHost(values.host, deps2.auth);
135087
+ source = {
135088
+ target: named2.target,
135089
+ bundleId: reference.bundleId,
135090
+ slug: reference.slug,
135091
+ workspace: workspaceForNamedHost(named2, values.workspace) ?? null,
135092
+ binding: null
135093
+ };
135094
+ } else {
135095
+ const binding = await hostedCheckoutFor(values.dir === void 0 ? [] : ["--dir", values.dir], deps2.auth.home, deps2.cwd);
135096
+ if (!binding) {
135097
+ throw new CliError("NOT_FOUND", `${path54.resolve(deps2.cwd, values.dir ?? ".")} is not a hosted checkout`, {
135098
+ details: { reason: "not_a_checkout" },
135099
+ help: `${cliInvocation()} export <bundle-id> --to ${commandToken(values.to)}`
135100
+ });
135101
+ }
135102
+ source = { target: resolveHostedTarget(binding.audience), bundleId: binding.bundle_id, slug: binding.workspace_slug ?? null, workspace: null, binding };
135103
+ }
135104
+ await exportToFolder(source, values.to, git2, deps2, mode, resume2);
135105
+ }
135106
+ var EXPORT_USAGE, EXPORT_DEADLINE_MS, SHOWN, JOURNAL, JOURNAL_SCHEMA, MAX_JOURNAL_BYTES, NO_HISTORY, HOSTED_UNCHANGED, PAGED_EXPORT_ATTEMPTS, PAGED_EXPORT_RESTART_MS, PAGE_RETRY_MS, pause2, ABANDONED_AFTER_MS;
135107
+ var init_export = __esm({
135108
+ "../cli/src/commands/export.ts"() {
135109
+ "use strict";
135110
+ init_define_SUPERBEE_BUILD_IDENTITY();
135111
+ init_define_SUPERBEE_UPDATE_POLICY();
135112
+ init_src();
135113
+ init_filesystem_push_role();
135114
+ init_src2();
135115
+ init_args();
135116
+ init_bundle2();
135117
+ init_bundle_home();
135118
+ init_command_spec();
135119
+ init_command_text();
135120
+ init_errors4();
135121
+ init_invocation();
135122
+ init_output2();
135123
+ init_private_state_bundle_boundary();
135124
+ init_nofollow_read();
135125
+ init_autopull2();
135126
+ init_session();
135127
+ init_discovery();
135128
+ init_binding();
135129
+ init_client();
135130
+ init_account();
135131
+ init_refusals();
135132
+ init_reference();
135133
+ init_sync2();
135134
+ init_status();
135135
+ init_file_journaled_backend();
135136
+ init_marker();
135137
+ init_export_archive();
135138
+ init_export_archive();
135139
+ EXPORT_USAGE = `superbee export \u2014 copy a hosted bundle out of hosted Superbee
135140
+
135141
+ Usage:
135142
+ superbee export <bundle-id> --to <folder> [--host <url>] [--workspace <id>] [--git] [--json]
135143
+ superbee export [--dir <checkout>] --to <folder> [--git] [--json]
135144
+ superbee export [--dir <checkout>] --in-place [--git] [--keep-unsent] [--json]
135145
+
135146
+ Fetches the hosted bundle's portable export: every document, reserved file (index.md, log.md)
135147
+ and blob at its latest confirmed revision, byte for byte as the host stores them. The archive is
135148
+ verified whole (every entry against the host's digest manifest) before any file is written.
135149
+ Signs in if needed (AUTH_REQUIRED, exit 4, carries the one link to relay and the command to
135150
+ re-run). The hosted bundle is never changed. History is not exported: the folder holds the
135151
+ current revision only.
135152
+
135153
+ --to <folder> writes a new local bundle there. The folder must be new or empty; it appears
135154
+ complete or not at all. With a bundle id the host is --host, else your last sign-in; with
135155
+ --dir (or run inside a checkout) the checkout's own host and bundle are used. A bundle id in more
135156
+ than one of your workspaces is named <workspace>/<bundle-id>, or with --workspace.
135157
+
135158
+ --in-place converts a hosted checkout into a plain local bundle: it adds the files the checkout
135159
+ lacks (blobs, reserved files, documents added on the host), never overwrites a file, then
135160
+ forgets the checkout's binding. It refuses a checkout with changes sync has not sent, conflicts
135161
+ or held files; run 'superbee sync' first, or pass --keep-unsent to keep them in this folder only.
135162
+ If it is interrupted, re-running the same command finishes it.
135163
+
135164
+ --git makes the result a Git board: a repository on branch '${BOARD_BRANCH}' whose first commit is the
135165
+ export. Share it with 'git remote add origin <url>' and 'superbee sync --establish'.
135166
+
135167
+ Options:
135168
+ --to <folder> New or empty folder to export into
135169
+ --in-place Convert the hosted checkout (--dir, else the current folder) into a local bundle
135170
+ --dir <checkout> The hosted checkout to export (default: the current folder)
135171
+ --host <url> Hosted Superbee URL, with a bundle id; default: your last sign-in
135172
+ --workspace <id> With a bundle id: your workspace that holds it, by id or slug
135173
+ --git Initialize a Git repository on branch '${BOARD_BRANCH}' and commit the export
135174
+ --keep-unsent With --in-place: convert even with unsent changes, conflicts or held files
135175
+ --json Emit compact JSON instead of TOON
135176
+ -h, --help Show this help
135177
+
135178
+ Examples:
135179
+ superbee export team.knowledge --to ~/work/team-knowledge --git
135180
+ superbee export --dir ~/work/team --to ~/work/team-copy
135181
+ superbee export --dir ~/work/team --in-place
135182
+ `;
135183
+ EXPORT_DEADLINE_MS = 6e4;
135184
+ SHOWN = 20;
135185
+ JOURNAL = IN_PLACE_JOURNAL;
135186
+ JOURNAL_SCHEMA = 1;
135187
+ MAX_JOURNAL_BYTES = 8 * 1024 * 1024;
135188
+ NO_HISTORY = "not exported: the folder holds the current revision only";
135189
+ HOSTED_UNCHANGED = "unchanged: the hosted bundle still exists and was not modified";
135190
+ PAGED_EXPORT_ATTEMPTS = 3;
135191
+ PAGED_EXPORT_RESTART_MS = [500, 1500];
135192
+ PAGE_RETRY_MS = 1e3;
135193
+ pause2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
135194
+ ABANDONED_AFTER_MS = 60 * 60 * 1e3;
135195
+ }
135196
+ });
135197
+
135198
+ // ../cli/src/commands/checkout-adopt.ts
135199
+ import path55 from "node:path";
135200
+ import { lstat as lstat9, realpath as realpath12 } from "node:fs/promises";
134378
135201
  function readLeaf(file2) {
134379
135202
  const read = readRegularFileNoFollowSync(file2);
134380
135203
  if (read.state === "missing") return null;
@@ -134397,11 +135220,11 @@ async function withCheckoutLock(folder, body) {
134397
135220
  });
134398
135221
  }
134399
135222
  async function adopt(folderArg, options, deps2, mode) {
134400
- const folder = path54.resolve(deps2.cwd, folderArg);
135223
+ const folder = path55.resolve(deps2.cwd, folderArg);
134401
135224
  let canonical2;
134402
135225
  try {
134403
- canonical2 = await realpath11(folder);
134404
- if (!(await lstat8(canonical2)).isDirectory()) throw new Error("not a folder");
135226
+ canonical2 = await realpath12(folder);
135227
+ if (!(await lstat9(canonical2)).isDirectory()) throw new Error("not a folder");
134405
135228
  } catch {
134406
135229
  throw new CliError("NOT_FOUND", `${folder} is not a folder`, { help: `${cliInvocation()} checkout --adopt <folder> --host <url>` });
134407
135230
  }
@@ -134416,7 +135239,7 @@ async function adopt(folderArg, options, deps2, mode) {
134416
135239
  }
134417
135240
  if (options.workspace !== void 0 && await rebindToWorkspace(canonical2, bound, options, deps2, mode)) return;
134418
135241
  const marker2 = await writeCheckoutMarker(canonical2, bound).catch(() => null);
134419
- deps2.stdout(render({ adopted: "unchanged", ...bindingView(bound), marker: marker2 ? "written" : "present", help: nextSteps(canonical2) }, mode));
135242
+ deps2.stdout(render({ adopted: "unchanged", ...bindingView(bound), marker: marker2 ? "written" : "present", help: nextSteps2(canonical2) }, mode));
134420
135243
  return;
134421
135244
  }
134422
135245
  const folderMarker = readCheckoutMarker(canonical2);
@@ -134456,7 +135279,7 @@ async function adopt(folderArg, options, deps2, mode) {
134456
135279
  ...marker2 ? { marker: "written" } : {},
134457
135280
  catalog: relocated ? { relocated: true, label: relocated.label, id: relocated.id } : { relocated: false, note: "no catalog entry named the old folder" },
134458
135281
  ...options.workspace !== void 0 ? { workspace_flag: "ignored: a moved checkout keeps the workspace it was bound with" } : {},
134459
- help: [...nextSteps(canonical2), `${cliInvocation()} sync --dir ${commandToken(canonical2)}`]
135282
+ help: [...nextSteps2(canonical2), `${cliInvocation()} sync --dir ${commandToken(canonical2)}`]
134460
135283
  },
134461
135284
  mode
134462
135285
  )
@@ -134508,7 +135331,7 @@ async function adopt(folderArg, options, deps2, mode) {
134508
135331
  const typed = { slug: marker.workspace_slug ?? null, bundleId: bundleId2 };
134509
135332
  await assertStandaloneFolder(canonical2);
134510
135333
  const resume2 = commandFragment`${cliInvocation()} checkout --adopt ${commandToken(canonical2)} --host ${commandToken(bindingHostArgument(target3))}${options.workspace !== void 0 ? commandFragment` --workspace ${commandToken(options.workspace)}` : commandFragment``}${options.json ? commandFragment` --json` : commandFragment``}`;
134511
- const connection = await connectHostedBundle(typed, target3, options.workspace, deps2, resume2);
135334
+ const connection = await connectHostedBundle(typed, target3, options.workspace, deps2, resume2, { deadlineMs: EXPORT_DEADLINE_MS });
134512
135335
  if (typed.slug === null && connection.reference.slug !== null) {
134513
135336
  const found = readBundleListing(await connection.client.bundles()).lookup({ slug: null, bundleId: bundleId2 });
134514
135337
  if (found.holders > 1) {
@@ -134520,8 +135343,12 @@ async function adopt(folderArg, options, deps2, mode) {
134520
135343
  }
134521
135344
  }
134522
135345
  const published = await readPublishedExtras(home2, canonical2);
134523
- const extras = published && published.host === target3.origin && published.bundle_id === bundleId2 ? published.extras : void 0;
134524
- const result3 = await bindFolderInPlace({ canonical: canonical2, target: target3, bundleId: bundleId2, connection, deps: deps2, resume: resume2, ...extras ? { extras } : {} });
135346
+ const host = await adoptedExtras(canonical2, exportRequestFor(connection, target3, bundleId2, resume2));
135347
+ const extras = {
135348
+ ...host.extras,
135349
+ ...published && published.host === target3.origin && published.bundle_id === bundleId2 ? published.extras : {}
135350
+ };
135351
+ const result3 = await bindWithExtras({ canonical: canonical2, target: target3, bundleId: bundleId2, connection, deps: deps2, resume: resume2, extras }, host);
134525
135352
  if (published) await clearPublishedExtras(home2, canonical2);
134526
135353
  const cataloged = await registerInCatalog(home2, result3.binding);
134527
135354
  const syncHelp = `${cliInvocation()} sync --dir ${commandToken(canonical2)}`;
@@ -134535,8 +135362,9 @@ async function adopt(folderArg, options, deps2, mode) {
134535
135362
  ...result3.conflicts.length > 0 ? { conflicts: listed(result3.conflicts) } : {},
134536
135363
  ...result3.localOnly.length > 0 ? { local_only: listed(result3.localOnly), local_only_note: "the next sync sends these as new documents, which re-creates any the host deleted since the copy was made; delete any you do not want first" } : {},
134537
135364
  root_index: result3.rootIndex,
135365
+ ...result3.otherFiles ? { other_files: result3.otherFiles, warning: "files other than documents were not checked against the host" } : {},
134538
135366
  ...result3.markerWritten ? { marker: "written" } : {},
134539
- help: result3.conflicts.length > 0 ? [`${cliInvocation()} sync --dir ${commandToken(canonical2)} --inspect --doc ${commandToken(result3.conflicts[0])}`, syncHelp] : [syncHelp, ...nextSteps(canonical2)]
135367
+ help: result3.conflicts.length > 0 ? [`${cliInvocation()} sync --dir ${commandToken(canonical2)} --inspect --doc ${commandToken(result3.conflicts[0])}`, syncHelp] : [syncHelp, ...nextSteps2(canonical2)]
134540
135368
  },
134541
135369
  mode
134542
135370
  )
@@ -134551,7 +135379,7 @@ async function rebindToWorkspace(canonical2, bound, options, deps2, mode) {
134551
135379
  }
134552
135380
  const target3 = resolveHostedTarget(options.host);
134553
135381
  const resume2 = commandFragment`${cliInvocation()} checkout --adopt ${commandToken(canonical2)} --host ${commandToken(bindingHostArgument(target3))} --workspace ${commandToken(options.workspace)}${options.json ? commandFragment` --json` : commandFragment``}`;
134554
- const connection = await connectHostedBundle({ slug: null, bundleId: bound.bundle_id }, target3, options.workspace, deps2, resume2);
135382
+ const connection = await connectHostedBundle({ slug: null, bundleId: bound.bundle_id }, target3, options.workspace, deps2, resume2, { deadlineMs: EXPORT_DEADLINE_MS });
134555
135383
  const slug = connection.reference.slug;
134556
135384
  if (slug === null || slug === (bound.workspace_slug ?? null)) return false;
134557
135385
  if (connection.identity.principalId !== bound.principal_id) {
@@ -134600,7 +135428,8 @@ async function rebindToWorkspace(canonical2, bound, options, deps2, mode) {
134600
135428
  await releaseCheckout(home2, bound);
134601
135429
  return pending;
134602
135430
  });
134603
- const result3 = await bindFolderInPlace({ canonical: canonical2, target: target3, bundleId: bound.bundle_id, connection, deps: deps2, resume: resume2 });
135431
+ const host = await adoptedExtras(canonical2, exportRequestFor(connection, target3, bound.bundle_id, resume2));
135432
+ const result3 = await bindWithExtras({ canonical: canonical2, target: target3, bundleId: bound.bundle_id, connection, deps: deps2, resume: resume2, extras: host.extras }, host);
134604
135433
  deps2.stdout(
134605
135434
  render(
134606
135435
  {
@@ -134614,7 +135443,7 @@ async function rebindToWorkspace(canonical2, bound, options, deps2, mode) {
134614
135443
  documents: { placed: result3.placed.length, matched: result3.matched.length, conflicts: result3.conflicts.length, local_only: result3.localOnly.length },
134615
135444
  ...result3.conflicts.length > 0 ? { conflicts: listed(result3.conflicts) } : {},
134616
135445
  ...result3.localOnly.length > 0 ? { local_only: listed(result3.localOnly) } : {},
134617
- help: [`${cliInvocation()} sync --dir ${commandToken(canonical2)}`, ...nextSteps(canonical2)]
135446
+ help: [`${cliInvocation()} sync --dir ${commandToken(canonical2)}`, ...nextSteps2(canonical2)]
134618
135447
  },
134619
135448
  mode
134620
135449
  )
@@ -134672,7 +135501,7 @@ async function bindFolderInPlace(input) {
134672
135501
  const hostIds = /* @__PURE__ */ new Set();
134673
135502
  for (const head of heads) {
134674
135503
  hostIds.add(head.id);
134675
- const file2 = path54.join(canonical2, pathFromConceptId(head.id));
135504
+ const file2 = path55.join(canonical2, pathFromConceptId(head.id));
134676
135505
  if (await parentUnsafe(canonical2, file2)) {
134677
135506
  conflicts.push(head.id);
134678
135507
  continue;
@@ -134706,12 +135535,12 @@ async function bindFolderInPlace(input) {
134706
135535
  if (index2) {
134707
135536
  const hostRoot2 = Buffer.from(index2.content, "utf8");
134708
135537
  root = digestOf(hostRoot2);
134709
- const current = readLeaf(path54.join(canonical2, ROOT_INDEX));
135538
+ const current = readLeaf(path55.join(canonical2, ROOT_INDEX));
134710
135539
  if (current === "unsafe") rootIndex = "kept (not a plain file; sync holds it)";
134711
135540
  else if (current === null) {
134712
- const placedRoot = (await placeNew(path54.join(canonical2, ROOT_INDEX), hostRoot2)).placed;
135541
+ const placedRoot = (await placeNew(path55.join(canonical2, ROOT_INDEX), hostRoot2)).placed;
134713
135542
  if (placedRoot) {
134714
- placedFiles.set(path54.join(canonical2, ROOT_INDEX), root);
135543
+ placedFiles.set(path55.join(canonical2, ROOT_INDEX), root);
134715
135544
  rootBase = index2.version;
134716
135545
  }
134717
135546
  rootIndex = placedRoot ? "placed" : "kept";
@@ -134746,6 +135575,25 @@ async function bindFolderInPlace(input) {
134746
135575
  }
134747
135576
  });
134748
135577
  }
135578
+ function exportRequestFor(connection, target3, bundleId2, resume2) {
135579
+ const { client, reference } = connection;
135580
+ return {
135581
+ client: reference.slug === null ? client : client.within(reference.slug),
135582
+ slug: reference.slug,
135583
+ source: { target: target3, bundleId: bundleId2, slug: reference.slug, workspace: null, binding: null },
135584
+ resume: resume2
135585
+ };
135586
+ }
135587
+ async function bindWithExtras(input, host) {
135588
+ try {
135589
+ const result3 = await bindFolderInPlace(input);
135590
+ return host.unplaced === null ? result3 : { ...result3, otherFiles: `not recorded: ${host.unplaced}; files other than documents here that differ from nothing on record read as edits until the next checkout` };
135591
+ } catch (error51) {
135592
+ await removePlaced(input.canonical, host.placed, false).catch(() => {
135593
+ });
135594
+ throw error51;
135595
+ }
135596
+ }
134749
135597
  var LISTED;
134750
135598
  var init_checkout_adopt = __esm({
134751
135599
  "../cli/src/commands/checkout-adopt.ts"() {
@@ -134773,24 +135621,25 @@ var init_checkout_adopt = __esm({
134773
135621
  init_sync_scan();
134774
135622
  init_checkout_store();
134775
135623
  init_checkout();
135624
+ init_export();
134776
135625
  LISTED = 20;
134777
135626
  }
134778
135627
  });
134779
135628
 
134780
135629
  // ../cli/src/commands/checkout.ts
134781
- import { homedir as homedir39 } from "node:os";
134782
- import { lstat as lstat9, mkdir as mkdir4, readFile as readFile5, readdir as readdir8, realpath as realpath12, rmdir as rmdir3, stat as stat8, unlink as unlink13 } from "node:fs/promises";
134783
- import path55 from "node:path";
134784
- import { parseArgs as parseArgs41 } from "node:util";
135630
+ import { homedir as homedir40 } from "node:os";
135631
+ import { lstat as lstat10, mkdir as mkdir5, readFile as readFile5, readdir as readdir9, realpath as realpath13, rmdir as rmdir3, stat as stat8, unlink as unlink14 } from "node:fs/promises";
135632
+ import path56 from "node:path";
135633
+ import { parseArgs as parseArgs42 } from "node:util";
134785
135634
  function checkoutDeps(partial2) {
134786
135635
  return {
134787
135636
  stdout: partial2.stdout ?? ((text5) => void process.stdout.write(text5)),
134788
- auth: partial2.auth ?? defaultHostedAuthDeps(homedir39()),
135637
+ auth: partial2.auth ?? defaultHostedAuthDeps(homedir40()),
134789
135638
  cwd: partial2.cwd ?? process.cwd(),
134790
135639
  ...partial2.fetch ? { fetch: partial2.fetch } : {}
134791
135640
  };
134792
135641
  }
134793
- async function entryKind(target3) {
135642
+ async function entryKind2(target3) {
134794
135643
  let info;
134795
135644
  try {
134796
135645
  info = await stat8(target3);
@@ -134799,7 +135648,7 @@ async function entryKind(target3) {
134799
135648
  throw error51;
134800
135649
  }
134801
135650
  if (!info.isDirectory()) return "other";
134802
- return (await readdir8(target3)).length === 0 ? "empty-dir" : "dir";
135651
+ return (await readdir9(target3)).length === 0 ? "empty-dir" : "dir";
134803
135652
  }
134804
135653
  function bindingView(binding) {
134805
135654
  return {
@@ -134812,11 +135661,11 @@ function bindingView(binding) {
134812
135661
  folder: binding.path
134813
135662
  };
134814
135663
  }
134815
- function nextSteps(folder) {
135664
+ function nextSteps2(folder) {
134816
135665
  return [`${cliInvocation()} list --dir ${commandToken(folder)}`, `${cliInvocation()} status --dir ${commandToken(folder)}`];
134817
135666
  }
134818
135667
  async function assertStandaloneFolder(folder) {
134819
- const parent = path55.dirname(folder);
135668
+ const parent = path56.dirname(folder);
134820
135669
  const enclosing = await findBundleRoot(parent).catch(() => null);
134821
135670
  if (enclosing) {
134822
135671
  throw new CliError("FORBIDDEN", `refusing to check out inside the local bundle at ${enclosing}`, {
@@ -134875,17 +135724,17 @@ function lockFailure2(error51, folder) {
134875
135724
  return error51;
134876
135725
  }
134877
135726
  async function canonicalFolder(folder) {
134878
- const parent = path55.dirname(folder);
134879
- await mkdir4(parent, { recursive: true });
135727
+ const parent = path56.dirname(folder);
135728
+ await mkdir5(parent, { recursive: true });
134880
135729
  let link4 = false;
134881
135730
  try {
134882
- link4 = (await lstat9(folder)).isSymbolicLink();
135731
+ link4 = (await lstat10(folder)).isSymbolicLink();
134883
135732
  } catch (error51) {
134884
135733
  if (error51.code !== "ENOENT") throw error51;
134885
135734
  }
134886
- if (!link4) return path55.join(await realpath12(parent), path55.basename(folder));
135735
+ if (!link4) return path56.join(await realpath13(parent), path56.basename(folder));
134887
135736
  try {
134888
- return await realpath12(folder);
135737
+ return await realpath13(folder);
134889
135738
  } catch {
134890
135739
  throw new CliError("USAGE", `the --dir folder ${folder} is a symbolic link to a folder that does not exist`, { help: "pass --dir <the real folder>" });
134891
135740
  }
@@ -134916,25 +135765,25 @@ async function removePlaced(folder, placed, createdFolder) {
134916
135765
  const dirs = /* @__PURE__ */ new Set();
134917
135766
  for (const [file2, digest3] of placed) {
134918
135767
  try {
134919
- if (digestOf(await readFile5(file2)) === digest3) await unlink13(file2);
135768
+ if (digestOf(await readFile5(file2)) === digest3) await unlink14(file2);
134920
135769
  } catch {
134921
135770
  }
134922
- for (let dir = path55.dirname(file2); dir !== folder && dir.startsWith(`${folder}${path55.sep}`); dir = path55.dirname(dir)) dirs.add(dir);
135771
+ for (let dir = path56.dirname(file2); dir !== folder && dir.startsWith(`${folder}${path56.sep}`); dir = path56.dirname(dir)) dirs.add(dir);
134923
135772
  }
134924
135773
  for (const dir of [...dirs].sort((a, b) => b.length - a.length)) await rmdir3(dir).catch(() => {
134925
135774
  });
134926
135775
  if (createdFolder) await rmdir3(folder).catch(() => {
134927
135776
  });
134928
- return await entryKind(folder) !== "dir";
135777
+ return await entryKind2(folder) !== "dir";
134929
135778
  }
134930
135779
  async function release(folderArg, deps2, mode) {
134931
- const folder = path55.resolve(deps2.cwd, folderArg);
135780
+ const folder = path56.resolve(deps2.cwd, folderArg);
134932
135781
  let canonical2;
134933
135782
  try {
134934
- canonical2 = (await lstat9(folder)).isSymbolicLink() ? await realpath12(folder) : path55.join(await realpath12(path55.dirname(folder)), path55.basename(folder));
135783
+ canonical2 = (await lstat10(folder)).isSymbolicLink() ? await realpath13(folder) : path56.join(await realpath13(path56.dirname(folder)), path56.basename(folder));
134935
135784
  } catch {
134936
135785
  try {
134937
- canonical2 = path55.join(await realpath12(path55.dirname(folder)), path55.basename(folder));
135786
+ canonical2 = path56.join(await realpath13(path56.dirname(folder)), path56.basename(folder));
134938
135787
  } catch {
134939
135788
  canonical2 = folder;
134940
135789
  }
@@ -134971,7 +135820,7 @@ async function release(folderArg, deps2, mode) {
134971
135820
  }
134972
135821
  async function checkoutHost(flag, folder, typed, values, home2, env, fetchImpl) {
134973
135822
  if (flag === void 0) {
134974
- const real = await realpath12(folder).catch(() => null);
135823
+ const real = await realpath13(folder).catch(() => null);
134975
135824
  const bound = real ? await bindingForPath(home2, real).catch(() => null) : null;
134976
135825
  if (bound) return { target: checkoutTarget(bound), workspace: values.workspace, origin: {} };
134977
135826
  }
@@ -134985,7 +135834,7 @@ async function checkoutHost(flag, folder, typed, values, home2, env, fetchImpl)
134985
135834
  async function checkout(argv2, partial2 = {}) {
134986
135835
  const deps2 = checkoutDeps(partial2);
134987
135836
  const { values, positionals } = parseLeafOrUsage(
134988
- () => parseArgs41({
135837
+ () => parseArgs42({
134989
135838
  args: argv2,
134990
135839
  options: {
134991
135840
  host: { type: "string" },
@@ -135027,21 +135876,21 @@ async function checkout(argv2, partial2 = {}) {
135027
135876
  throw new CliError("USAGE", `'${positionals[0]}' is not a hosted bundle id or <workspace>/<bundle-id>`, { help: `${cliInvocation()} checkout --help` });
135028
135877
  }
135029
135878
  const bundleId2 = typed.bundleId;
135030
- const folder = path55.resolve(deps2.cwd, values.dir ?? bundleId2);
135879
+ const folder = path56.resolve(deps2.cwd, values.dir ?? bundleId2);
135031
135880
  assertBundleOutsidePrivateState(folder, deps2.auth.home);
135032
135881
  const { target: target3, workspace: workspaceFlag2, origin: hostOrigin } = await checkoutHost(values.host, folder, typed, values, deps2.auth.home, deps2.auth.env, deps2.auth.fetch);
135033
135882
  const prefix = syncRoutePrefix(target3);
135034
135883
  const resume2 = commandFragment`${cliInvocation()} checkout ${commandToken(hostedBundleReferenceText(typed))} --host ${commandToken(hostArgument(target3))} --dir ${commandToken(folder)}${workspaceFlag2 !== void 0 ? commandFragment` --workspace ${commandToken(workspaceFlag2)}` : commandFragment``}${values.json ? commandFragment` --json` : commandFragment``}`;
135035
- const found = await entryKind(folder);
135884
+ const found = await entryKind2(folder);
135036
135885
  if (found === "other") {
135037
135886
  throw new CliError("ALREADY_EXISTS", `${folder} exists and is not a folder`, { help: "pass --dir <new folder>" });
135038
135887
  }
135039
135888
  if (found === "dir") {
135040
- const existing = await bindingForPath(deps2.auth.home, await realpath12(folder));
135889
+ const existing = await bindingForPath(deps2.auth.home, await realpath13(folder));
135041
135890
  const sameWorkspace = workspaceFlag2 === void 0 || workspaceFlag2 === existing?.workspace_slug || workspaceFlag2 === existing?.workspace;
135042
135891
  const asked = typed.slug === null && sameWorkspace ? { slug: existing?.workspace_slug ?? null, bundleId: bundleId2 } : typed;
135043
135892
  if (existing && bindsBundle(existing, target3, asked)) {
135044
- deps2.stdout(render({ checkout: "unchanged", ...bindingView(existing), help: nextSteps(existing.path) }, mode));
135893
+ deps2.stdout(render({ checkout: "unchanged", ...bindingView(existing), help: nextSteps2(existing.path) }, mode));
135045
135894
  return;
135046
135895
  }
135047
135896
  const marker = existing ? null : readCheckoutMarker(folder);
@@ -135052,7 +135901,7 @@ async function checkout(argv2, partial2 = {}) {
135052
135901
  });
135053
135902
  }
135054
135903
  await assertStandaloneFolder(folder);
135055
- const { identity, reader, listed: listed2, workspace, reference, definitionWrites, documentInputBytes } = await connectHostedBundle(typed, target3, workspaceFlag2, deps2, resume2);
135904
+ const { identity, reader, listed: listed2, workspace, reference, client, definitionWrites, documentInputBytes } = await connectHostedBundle(typed, target3, workspaceFlag2, deps2, resume2, { deadlineMs: EXPORT_DEADLINE_MS });
135056
135905
  const canonical2 = await canonicalFolder(folder);
135057
135906
  let createdFolder = false;
135058
135907
  const placed = /* @__PURE__ */ new Map();
@@ -135064,7 +135913,7 @@ async function checkout(argv2, partial2 = {}) {
135064
135913
  help: "wait for it to finish, then retry the same command"
135065
135914
  });
135066
135915
  }
135067
- const kind2 = await entryKind(canonical2);
135916
+ const kind2 = await entryKind2(canonical2);
135068
135917
  if (kind2 === "dir" || kind2 === "other") {
135069
135918
  throw new CliError("ALREADY_EXISTS", `${canonical2} is no longer empty`, { details: { reason: "not_empty", folder: canonical2 }, help: "pass --dir <new or empty folder>" });
135070
135919
  }
@@ -135074,7 +135923,7 @@ async function checkout(argv2, partial2 = {}) {
135074
135923
  await releaseCheckout(deps2.auth.home, replaced);
135075
135924
  }
135076
135925
  if (kind2 === "absent") {
135077
- await mkdir4(canonical2);
135926
+ await mkdir5(canonical2);
135078
135927
  createdFolder = true;
135079
135928
  }
135080
135929
  const identityNow = await folderIdentity(canonical2);
@@ -135109,9 +135958,12 @@ async function checkout(argv2, partial2 = {}) {
135109
135958
  const stored = await store.readHeads({ project: (head) => head.id });
135110
135959
  assertProjectable(stored, bundleId2, target3);
135111
135960
  const exported = await exportFresh(store, canonical2, (file2, digest3) => placed.set(file2, digest3));
135112
- if (exported.kept.length > 0) {
135961
+ const portable = await exportOrReason({ client: reference.slug === null ? client : client.within(reference.slug), slug: reference.slug, source: { target: target3, bundleId: bundleId2, slug: reference.slug, workspace: workspaceFlag2 ?? null, binding: null }, resume: resume2 });
135962
+ const extras = typeof portable === "string" ? NO_EXTRAS : await placeExtras(portable, canonical2, (file2, digest3) => placed.set(file2, digest3));
135963
+ const kept = [...exported.kept, ...extras.kept];
135964
+ if (kept.length > 0) {
135113
135965
  throw new CliError("CONFLICT", `files appeared in ${canonical2} during checkout`, {
135114
- details: { reason: "folder_changed", kept: exported.kept.slice(0, 20), kept_total: exported.kept.length },
135966
+ details: { reason: "folder_changed", kept: kept.slice(0, 20), kept_total: kept.length },
135115
135967
  help: "retry the same command once nothing else writes to the folder"
135116
135968
  });
135117
135969
  }
@@ -135122,14 +135974,14 @@ async function checkout(argv2, partial2 = {}) {
135122
135974
  if (id !== ROOT_INDEX && version4) files[id] = { digest: digest3, version: version4 };
135123
135975
  }
135124
135976
  const rootBase = exported.exported[ROOT_INDEX] !== void 0 ? (await store.readReserved("", "index.md"))?.version ?? null : null;
135125
- await writeProjection(deps2.auth.home, binding.checkout_id, { files, root: exported.exported[ROOT_INDEX] ?? null, rootBase });
135977
+ await writeProjection(deps2.auth.home, binding.checkout_id, { files, root: exported.exported[ROOT_INDEX] ?? null, rootBase, extras: extras.placed });
135126
135978
  const markerFile = await writeCheckoutMarker(canonical2, binding);
135127
135979
  if (markerFile) placed.set(markerFile, digestOf(checkoutMarkerBytes(binding)));
135128
135980
  const ready = { ...binding, state: "ready" };
135129
135981
  await writeBinding(deps2.auth.home, ready);
135130
135982
  await recordPulled(deps2.auth.home, ready.checkout_id);
135131
135983
  await indexCheckoutPath(deps2.auth.home, ready);
135132
- return { binding: ready, documents: exported.documents, root: exported.root, digest: marker.headsDigest ?? null };
135984
+ return { binding: ready, documents: exported.documents, root: exported.root, unplaced: typeof portable === "string" ? portable : null, reserved: extras.reserved, other: extras.other, digest: marker.headsDigest ?? null };
135133
135985
  } catch (error51) {
135134
135986
  await store?.close().catch(() => {
135135
135987
  });
@@ -135162,22 +136014,96 @@ async function checkout(argv2, partial2 = {}) {
135162
136014
  catalog: cataloged,
135163
136015
  documents: result3.documents,
135164
136016
  root_index: result3.root,
136017
+ ...result3.unplaced === null ? { reserved_files: result3.reserved, other_files: result3.other } : {
136018
+ other_files: `not placed: ${result3.unplaced}, so reserved files below the root and files other than documents stay on the host; superbee export copies them`,
136019
+ warning: "this checkout lacks the host's files other than documents, and sync never brings them: check out again into a new folder to get them"
136020
+ },
135165
136021
  heads_digest: result3.digest,
135166
136022
  ...replacedBinding ? { replaced_stale_checkout: { bundle_id: replacedBinding.bundle_id, host: replacedBinding.origin } } : {},
135167
136023
  mode: "sync",
135168
136024
  refused: REFUSED_SUMMARY,
135169
- help: nextSteps(result3.binding.path)
136025
+ help: nextSteps2(result3.binding.path)
135170
136026
  },
135171
136027
  mode
135172
136028
  )
135173
136029
  );
135174
136030
  }
135175
- async function connectHostedBundle(typed, target3, workspaceFlag2, deps2, resume2) {
136031
+ async function exportOrReason(request3) {
136032
+ try {
136033
+ return await fetchExportThrough(request3);
136034
+ } catch (error51) {
136035
+ if (!(error51 instanceof CliError)) throw error51;
136036
+ if (error51.details?.code === "not_found" && error51.details?.status === 404) return `${request3.source.target.origin} serves no bundle export`;
136037
+ const reason = error51.details?.reason ?? error51.details?.code;
136038
+ return `the bundle export could not be read: ${error51.message.replace(/; nothing was written$/, "")}${typeof reason === "string" ? ` (${reason})` : ""}`;
136039
+ }
136040
+ }
136041
+ async function adoptedExtras(folder, request3) {
136042
+ const portable = await exportOrReason(request3);
136043
+ if (typeof portable === "string") return { extras: {}, placed: /* @__PURE__ */ new Map(), unplaced: portable };
136044
+ const extras = {};
136045
+ const placed = /* @__PURE__ */ new Map();
136046
+ for (const entry of portable.entries) {
136047
+ if (entry.kind === "document" || entry.path === ROOT_INDEX) continue;
136048
+ const file2 = path56.join(folder, ...entry.path.split("/"));
136049
+ if (await parentUnsafe(folder, file2)) continue;
136050
+ const found = readRegularFileNoFollowSync(file2);
136051
+ if (found.state === "missing") {
136052
+ try {
136053
+ await ensureParentInside(folder, file2);
136054
+ } catch (error51) {
136055
+ if (error51 instanceof UnsafePlacementError) continue;
136056
+ throw error51;
136057
+ }
136058
+ if ((await placeNew(file2, entry.bytes)).placed) {
136059
+ extras[entry.path] = digestOf(entry.bytes);
136060
+ placed.set(file2, digestOf(entry.bytes));
136061
+ }
136062
+ continue;
136063
+ }
136064
+ if (found.state === "present" && Buffer.from(entry.bytes).equals(found.bytes)) extras[entry.path] = digestOf(entry.bytes);
136065
+ }
136066
+ return { extras, placed, unplaced: null };
136067
+ }
136068
+ async function placeExtras(exported, folder, onPlaced) {
136069
+ const placed = {};
136070
+ const kept = [];
136071
+ let reserved = 0;
136072
+ let other = 0;
136073
+ for (const entry of exported.entries) {
136074
+ if (entry.kind === "document" || entry.path === ROOT_INDEX) continue;
136075
+ const file2 = path56.join(folder, ...entry.path.split("/"));
136076
+ try {
136077
+ await ensureParentInside(folder, file2);
136078
+ } catch (error51) {
136079
+ if (!(error51 instanceof UnsafePlacementError)) throw error51;
136080
+ kept.push(entry.path);
136081
+ continue;
136082
+ }
136083
+ const outcome = await placeNew(file2, entry.bytes);
136084
+ if (!outcome.placed) {
136085
+ kept.push(entry.path);
136086
+ continue;
136087
+ }
136088
+ const digest3 = digestOf(entry.bytes);
136089
+ onPlaced(file2, digest3);
136090
+ placed[entry.path] = digest3;
136091
+ if (entry.kind === "reserved") reserved += 1;
136092
+ else other += 1;
136093
+ }
136094
+ return { placed, reserved, other, kept };
136095
+ }
136096
+ async function connectHostedBundle(typed, target3, workspaceFlag2, deps2, resume2, options = {}) {
135176
136097
  const { bundleId: bundleId2 } = typed;
135177
136098
  const command = (named) => `${cliInvocation()} checkout ${commandToken(named)} --host ${commandToken(hostArgument(target3))}`;
135178
136099
  const account = await connectHostedAccount(
135179
136100
  target3,
135180
- { workspace: workspaceFlag2, resume: resume2, otherWorkspace: `${command(hostedBundleReferenceText(typed))} --workspace <id>` },
136101
+ {
136102
+ workspace: workspaceFlag2,
136103
+ resume: resume2,
136104
+ otherWorkspace: `${command(hostedBundleReferenceText(typed))} --workspace <id>`,
136105
+ ...options.deadlineMs !== void 0 ? { deadlineMs: options.deadlineMs } : {}
136106
+ },
135181
136107
  deps2
135182
136108
  );
135183
136109
  const { client, identity, workspace } = account;
@@ -135261,7 +136187,7 @@ function assertProjectable(ids, bundleId2, target3) {
135261
136187
  });
135262
136188
  }
135263
136189
  }
135264
- var CHECKOUT_DOCUMENT_LIMIT, CHECKOUT_USAGE, REFUSED_SUMMARY;
136190
+ var CHECKOUT_DOCUMENT_LIMIT, CHECKOUT_USAGE, NO_EXTRAS, REFUSED_SUMMARY;
135265
136191
  var init_checkout = __esm({
135266
136192
  "../cli/src/commands/checkout.ts"() {
135267
136193
  "use strict";
@@ -135279,6 +136205,7 @@ var init_checkout = __esm({
135279
136205
  init_invocation();
135280
136206
  init_output2();
135281
136207
  init_private_state_bundle_boundary();
136208
+ init_nofollow_read();
135282
136209
  init_session();
135283
136210
  init_account();
135284
136211
  init_freshness2();
@@ -135290,6 +136217,7 @@ var init_checkout = __esm({
135290
136217
  init_catalog();
135291
136218
  init_marker();
135292
136219
  init_checkout_adopt();
136220
+ init_export();
135293
136221
  init_reference();
135294
136222
  CHECKOUT_DOCUMENT_LIMIT = 1e4;
135295
136223
  CHECKOUT_USAGE = `superbee checkout \u2014 mirror a hosted bundle into a local folder
@@ -135305,8 +136233,11 @@ hosted bundle into --dir (default: ./<bundle-id>), which must be new or empty. T
135305
136233
  else, for a folder that is already a checkout, its own host, else the host of your last sign-in
135306
136234
  when it is the only host you are signed in to (never SUPERBEE_HOST alone); signed in to more than
135307
136235
  one host, a checkout without --host is refused and names them. The receipt names the host it
135308
- bound. The folder holds plain bundle files, so every command runs on it with --dir <folder>. The
135309
- link to the host is kept in private state, keyed by the folder's path, never in the folder.
136236
+ bound. The folder holds plain bundle files, so every command runs on it with --dir <folder>: the
136237
+ same files 'superbee export' writes (documents, every index.md and log.md, and other files such as
136238
+ data/*.json), with the bytes the host stores. Sync sends documents only; it leaves the other files
136239
+ be while they are as placed. The link to the host is kept in private state, keyed by the folder's
136240
+ path, never in the folder.
135310
136241
  Re-running for the same folder and bundle is a no-op. A checkout whose folder was deleted, or
135311
136242
  replaced by a new empty folder, is replaced, unless it holds changes sync has not sent yet; a
135312
136243
  checkout emptied in place is refused, because removing every file is a pending edit.
@@ -135364,6 +136295,7 @@ Examples:
135364
136295
  superbee checkout --adopt ~/restored/team --host https://mcp.getsuperbee.com
135365
136296
  superbee checkout --release ~/work/team
135366
136297
  `;
136298
+ NO_EXTRAS = Object.freeze({ placed: {}, reserved: 0, other: 0, kept: [] });
135367
136299
  REFUSED_SUMMARY = Object.freeze(
135368
136300
  HOSTED_CHECKOUT_REFUSALS.filter((row4) => row4.reason !== "checkout_target").map((row4) => {
135369
136301
  const words = row4.words.join(" ");
@@ -135373,800 +136305,6 @@ Examples:
135373
136305
  }
135374
136306
  });
135375
136307
 
135376
- // ../cli/src/commands/export.ts
135377
- import { randomBytes as randomBytes9 } from "node:crypto";
135378
- import { homedir as homedir40 } from "node:os";
135379
- import { link as link3, lstat as lstat10, mkdir as mkdir5, open as open4, readdir as readdir9, realpath as realpath13, rename as rename2, rm as rm2, unlink as unlink14 } from "node:fs/promises";
135380
- import path56 from "node:path";
135381
- import { parseArgs as parseArgs42 } from "node:util";
135382
- function exportDeps(partial2) {
135383
- return {
135384
- stdout: partial2.stdout ?? ((text5) => void process.stdout.write(text5)),
135385
- auth: partial2.auth ?? defaultHostedAuthDeps(homedir40()),
135386
- cwd: partial2.cwd ?? process.cwd(),
135387
- ...partial2.fetch ? { fetch: partial2.fetch } : {}
135388
- };
135389
- }
135390
- async function entryKind2(target3) {
135391
- let info;
135392
- try {
135393
- info = await lstat10(target3);
135394
- } catch (error51) {
135395
- if (error51.code === "ENOENT") return "absent";
135396
- throw error51;
135397
- }
135398
- if (!info.isDirectory()) return "other";
135399
- return (await readdir9(target3)).length === 0 ? "empty-dir" : "dir";
135400
- }
135401
- function shown(paths) {
135402
- return [...paths].sort().slice(0, SHOWN);
135403
- }
135404
- function refusalCode(body) {
135405
- const error51 = body?.error;
135406
- if (typeof error51 === "string") return error51;
135407
- const code3 = error51?.code;
135408
- return typeof code3 === "string" ? code3 : void 0;
135409
- }
135410
- async function namedClient(source, deps2, resume2) {
135411
- const { target: target3 } = source;
135412
- const account = await connectHostedAccount(target3, { ...source.workspace !== null ? { workspace: source.workspace } : {}, resume: resume2, deadlineMs: EXPORT_DEADLINE_MS }, deps2);
135413
- const { reference } = resolveBundleReference(
135414
- { slug: source.slug, bundleId: source.bundleId },
135415
- account,
135416
- target3,
135417
- (named) => `${cliInvocation()} export ${commandToken(named)} --host ${commandToken(hostArgument(target3))} --to <folder>`
135418
- );
135419
- return { client: reference.slug === null ? account.client : account.client.within(reference.slug), slug: reference.slug };
135420
- }
135421
- async function fetchExport(source, deps2, resume2) {
135422
- const { bundleId: bundleId2, target: target3 } = source;
135423
- const named = source.binding ? null : await namedClient(source, deps2, resume2);
135424
- const client = named ? named.client : (await connectCheckout(source.binding, { resume: resume2, deadlineMs: EXPORT_DEADLINE_MS }, deps2)).client;
135425
- const slug = named ? named.slug : source.binding.workspace_slug ?? null;
135426
- const request3 = { client, slug, source, resume: resume2 };
135427
- for (let attempt = 1; ; attempt += 1) {
135428
- const chain = new ExportPageChain();
135429
- let bytes = 0;
135430
- let cursor;
135431
- let moved = false;
135432
- for (; ; ) {
135433
- let page;
135434
- for (let tries = 1; page === void 0; tries += 1) {
135435
- const answer2 = await exportAnswer(request3, { bundleId: bundleId2, paged: true, ...cursor !== void 0 ? { cursor } : {} });
135436
- if (answer2.refused === "invalid_input" && cursor === void 0 && attempt === 1) return verified(await readArchive(await wholeAnswer(request3), source, MAX_EXPORT_BYTES), source);
135437
- if (answer2.refused === "concurrent_change") {
135438
- moved = true;
135439
- break;
135440
- }
135441
- try {
135442
- if (answer2.refused !== void 0) throw await exportRefusal(request3, answer2.refused, answer2.status);
135443
- page = verified(await readArchive(answer2.ok, source, MAX_EXPORT_BYTES - bytes), source, { paged: true });
135444
- } catch (error51) {
135445
- const again = error51 instanceof CliError && (error51.details?.reason === "export_incomplete" || answer2.refused === "backend_unavailable" && error51.code === "TRANSIENT");
135446
- if (!again || tries >= 2) throw error51;
135447
- await pause2(PAGE_RETRY_MS);
135448
- }
135449
- }
135450
- if (moved) break;
135451
- bytes += page.bytes;
135452
- cursor = joining(source, () => chain.add(page));
135453
- if (cursor === void 0) return joining(source, () => chain.joined());
135454
- }
135455
- if (attempt >= PAGED_EXPORT_ATTEMPTS) {
135456
- throw new CliError("TRANSIENT", `'${bundleId2}' kept changing on ${target3.origin} while it was exported; nothing was written`, {
135457
- details: { reason: "export_source_changed", bundle_id: bundleId2, host: target3.origin, retryable: true },
135458
- help: "retry the same command once the bundle is not being edited"
135459
- });
135460
- }
135461
- const base = PAGED_EXPORT_RESTART_MS[attempt - 1];
135462
- await pause2(Math.round(base / 2 + Math.random() * base / 2));
135463
- }
135464
- }
135465
- async function exportAnswer({ client, source, resume: resume2 }, input) {
135466
- let answer2;
135467
- try {
135468
- answer2 = await client.carrier.stream(`${client.prefix}/export`, input, client.signal);
135469
- } catch (error51) {
135470
- throw hostedFailure(error51, source.target, resume2);
135471
- }
135472
- if (!answer2.ok) return { refused: refusalCode(answer2.body) ?? "RUNTIME", status: answer2.status };
135473
- return { ok: answer2.body };
135474
- }
135475
- async function wholeAnswer(request3) {
135476
- const answer2 = await exportAnswer(request3, { bundleId: request3.source.bundleId });
135477
- if (answer2.refused !== void 0) throw await exportRefusal(request3, answer2.refused, answer2.status);
135478
- return answer2.ok;
135479
- }
135480
- function exportTooLarge(source) {
135481
- return new CliError("RUNTIME", `${source.target.origin} answered an export larger than ${MAX_EXPORT_BYTES} bytes`, {
135482
- details: { reason: "export_too_large", host: source.target.origin, bundle_id: source.bundleId, retryable: false, next: "the person exports it with Export bundle in the Superbee app" },
135483
- help: `${cliInvocation()} whoami --host ${commandToken(hostArgument(source.target))}`
135484
- });
135485
- }
135486
- async function exportRefusal({ client, slug, source, resume: resume2 }, code3, status2) {
135487
- const { target: target3, bundleId: bundleId2 } = source;
135488
- if (code3 === "bundle_not_found") {
135489
- if (source.binding) return await bundleAbsent(source.binding, client);
135490
- const text5 = hostedBundleReferenceText({ slug, bundleId: bundleId2 });
135491
- return new CliError("NOT_FOUND", `no hosted bundle '${text5}' is visible to you on ${target3.origin}`, {
135492
- details: { bundle_id: bundleId2, ...slug !== null ? { reference: text5 } : {}, host: target3.origin },
135493
- help: hostedListCommand(target3)
135494
- });
135495
- }
135496
- if (code3 === "result_too_large") {
135497
- return new CliError("FORBIDDEN", `hosted bundle '${bundleId2}' is larger than an export carries`, {
135498
- details: { reason: "bundle_too_large", bundle_id: bundleId2, host: target3.origin, next: "the person exports it with Export bundle in the Superbee app" },
135499
- help: `${cliInvocation()} whoami --host ${commandToken(hostArgument(target3))}`
135500
- });
135501
- }
135502
- if (code3 === "validation_failed") {
135503
- return new CliError("FORBIDDEN", `hosted bundle '${bundleId2}' holds a path an export cannot carry`, {
135504
- details: { reason: "unexportable_path", bundle_id: bundleId2, host: target3.origin, next: "the person renames the document or file in the Superbee app, then the same command is retried" },
135505
- help: `${cliInvocation()} whoami --host ${commandToken(hostArgument(target3))}`
135506
- });
135507
- }
135508
- return hostedFailure(new RemoteError(`hosted export answered ${status2}`, code3, status2), target3, resume2);
135509
- }
135510
- async function readArchive(body, source, budget) {
135511
- const { target: target3, bundleId: bundleId2 } = source;
135512
- const reader = body.getReader();
135513
- const chunks = [];
135514
- let size = 0;
135515
- try {
135516
- for (; ; ) {
135517
- const part = await reader.read();
135518
- if (part.done) break;
135519
- size += part.value.byteLength;
135520
- if (size > budget) throw exportTooLarge(source);
135521
- chunks.push(part.value);
135522
- }
135523
- } catch (error51) {
135524
- if (error51 instanceof CliError) throw error51;
135525
- throw new CliError("TRANSIENT", `the export of '${bundleId2}' from ${target3.origin} stopped part way; nothing was written`, {
135526
- details: { reason: "export_incomplete", bundle_id: bundleId2, host: target3.origin, retryable: true },
135527
- help: "retry the same command"
135528
- });
135529
- } finally {
135530
- void reader.cancel().catch(() => {
135531
- });
135532
- }
135533
- const archive = new Uint8Array(size);
135534
- let offset = 0;
135535
- for (const chunk of chunks) {
135536
- archive.set(chunk, offset);
135537
- offset += chunk.byteLength;
135538
- }
135539
- return archive;
135540
- }
135541
- function verified(archive, source, options = {}) {
135542
- return refusingArchive(source, () => verifyExport(archive, source.bundleId, options));
135543
- }
135544
- function joining(source, join19) {
135545
- return refusingArchive(source, join19);
135546
- }
135547
- function refusingArchive(source, verify) {
135548
- try {
135549
- return verify();
135550
- } catch (error51) {
135551
- if (!(error51 instanceof ExportArchiveError)) throw error51;
135552
- if (error51.problem === "incomplete") {
135553
- throw new CliError("TRANSIENT", `the export of '${source.bundleId}' was not completed by the host; nothing was written`, {
135554
- details: { reason: "export_incomplete", bundle_id: source.bundleId, host: source.target.origin, retryable: true },
135555
- help: "retry the same command"
135556
- });
135557
- }
135558
- throw new CliError("RUNTIME", `${source.target.origin} answered an export this CLI refuses (${error51.message}); nothing was written`, {
135559
- details: { reason: `export_${error51.problem}`, bundle_id: source.bundleId, host: source.target.origin, retryable: false },
135560
- help: "upgrade Superbee (npm install -g superbee); if it persists, report this bundle and reason"
135561
- });
135562
- }
135563
- }
135564
- async function writeNewFile(file2, bytes) {
135565
- const handle = await open4(file2, "wx", 420);
135566
- try {
135567
- await handle.writeFile(bytes);
135568
- await handle.sync();
135569
- } finally {
135570
- await handle.close();
135571
- }
135572
- }
135573
- async function writeEntries(root, entries) {
135574
- for (const entry of entries) {
135575
- const file2 = path56.join(root, ...entry.path.split("/"));
135576
- await mkdir5(path56.dirname(file2), { recursive: true });
135577
- await writeNewFile(file2, entry.bytes);
135578
- }
135579
- }
135580
- function gitRun(dir, args) {
135581
- let result3;
135582
- try {
135583
- result3 = runGit(dir, args);
135584
- } catch (error51) {
135585
- const missing2 = error51.code === "GIT_MISSING";
135586
- throw new CliError(missing2 ? "GIT_MISSING" : "RUNTIME", `git could not run (${error51 instanceof Error ? error51.message : String(error51)})`, {
135587
- details: { reason: missing2 ? "git_missing" : "git_unavailable" },
135588
- help: "install git, or export without --git"
135589
- });
135590
- }
135591
- if (result3.status !== 0) {
135592
- throw new CliError("RUNTIME", `git ${args.find((arg) => !arg.startsWith("-") && !arg.includes("=")) ?? ""} failed: ${result3.stderr.trim().split("\n").at(-1) ?? ""}`, {
135593
- details: { reason: "git_failed" },
135594
- help: "fix the git error, then retry the same command"
135595
- });
135596
- }
135597
- return result3.stdout.trim();
135598
- }
135599
- function commitExport(dir, message, options) {
135600
- const quiet = ["-c", "core.hooksPath=/dev/null"];
135601
- gitRun(dir, [...quiet, "init", "-q"]);
135602
- gitRun(dir, ["symbolic-ref", "HEAD", `refs/heads/${BOARD_BRANCH}`]);
135603
- const excluded = ["--", ".", `:(exclude)${IN_PLACE_STAGING}`, `:(exclude)${CHECKOUT_MARKER_DIR}`];
135604
- gitRun(dir, ["add", "-A", ...options.force ? ["--force"] : [], ...excluded]);
135605
- gitRun(dir, [...quiet, ...identityFlags(dir), "-c", "commit.gpgsign=false", "commit", "-q", "--no-verify", "--allow-empty", "-m", message]);
135606
- const committed = { branch: BOARD_BRANCH, commit: gitRun(dir, ["rev-parse", "HEAD"]) };
135607
- if (options.force) return committed;
135608
- const ignored = gitRun(dir, ["ls-files", "--others", "--ignored", "--exclude-standard", ...excluded]).split("\n").filter(Boolean);
135609
- return ignored.length === 0 ? committed : { ...committed, ignored: { count: ignored.length, paths: shown(ignored) } };
135610
- }
135611
- function commitMessage(source, exported) {
135612
- return `Export ${source.bundleId} from ${source.target.origin} at revision ${exported.source.revision}
135613
-
135614
- superbee export, ${exported.exportedAt}: ${exported.counts.documents} documents, ${exported.counts.reserved} reserved files, ${exported.counts.blobs} blobs. History is not included.
135615
- `;
135616
- }
135617
- function stagingName(target3) {
135618
- return path56.join(path56.dirname(target3), `.${path56.basename(target3)}.superbee-export-${process.pid}-${randomBytes9(6).toString("hex")}.partial`);
135619
- }
135620
- function processAlive(pid) {
135621
- try {
135622
- process.kill(pid, 0);
135623
- return true;
135624
- } catch (error51) {
135625
- return error51.code === "EPERM";
135626
- }
135627
- }
135628
- async function removeAbandonedStaging(target3) {
135629
- const prefix = `.${path56.basename(target3)}.superbee-export-`;
135630
- let removed = 0;
135631
- let names;
135632
- try {
135633
- names = await readdir9(path56.dirname(target3));
135634
- } catch {
135635
- return 0;
135636
- }
135637
- for (const name of names) {
135638
- if (!name.startsWith(prefix)) continue;
135639
- const match = /^(\d+)-[0-9a-f]{12}\.partial$/.exec(name.slice(prefix.length));
135640
- if (!match) continue;
135641
- const age = await lstat10(path56.join(path56.dirname(target3), name)).then((info) => Date.now() - info.mtimeMs, () => 0);
135642
- if (processAlive(Number(match[1])) && age < ABANDONED_AFTER_MS) continue;
135643
- await rm2(path56.join(path56.dirname(target3), name), { recursive: true, force: true });
135644
- removed += 1;
135645
- }
135646
- return removed;
135647
- }
135648
- async function assertStandalone(target3, home2) {
135649
- const parent = path56.dirname(target3);
135650
- const help2 = "pass --to <folder> outside any bundle, board or checkout";
135651
- const checkout2 = await hostedCheckoutAt(parent, home2);
135652
- if (checkout2) {
135653
- throw new CliError("FORBIDDEN", `refusing to export inside the hosted checkout at ${checkout2.path}`, { details: { reason: "inside_checkout", enclosing: checkout2.path }, help: help2 });
135654
- }
135655
- const enclosing = await findBundleRoot(parent).catch(() => null);
135656
- if (enclosing) {
135657
- throw new CliError("FORBIDDEN", `refusing to export inside the local bundle at ${enclosing}`, { details: { reason: "inside_bundle", enclosing }, help: help2 });
135658
- }
135659
- const binding = await resolveProjectBinding(parent).catch(() => null);
135660
- if (binding) {
135661
- throw new CliError("FORBIDDEN", `refusing to export inside a project bound to a board (${binding.file})`, { details: { reason: "inside_bound_project", binding: binding.file }, help: help2 });
135662
- }
135663
- }
135664
- async function exportToFolder(source, toArg, git2, deps2, mode, resume2) {
135665
- const requested = path56.resolve(deps2.cwd, toArg);
135666
- assertBundleOutsidePrivateState(requested, deps2.auth.home);
135667
- const found = await entryKind2(requested);
135668
- if (found === "other") throw new CliError("ALREADY_EXISTS", `${requested} exists and is not a folder`, { details: { reason: "not_a_folder", folder: requested }, help: "pass --to <new folder>" });
135669
- if (found === "dir") throw new CliError("ALREADY_EXISTS", `${requested} is not empty`, { details: { reason: "not_empty", folder: requested }, help: "pass --to <new or empty folder>" });
135670
- await mkdir5(path56.dirname(requested), { recursive: true });
135671
- const target3 = path56.join(await realpath13(path56.dirname(requested)), path56.basename(requested));
135672
- assertBundleOutsidePrivateState(target3, deps2.auth.home);
135673
- await assertStandalone(target3, deps2.auth.home);
135674
- const exported = await fetchExport(source, deps2, resume2);
135675
- const abandoned = await removeAbandonedStaging(target3);
135676
- const staging = stagingName(target3);
135677
- await mkdir5(staging);
135678
- let gitResult = null;
135679
- try {
135680
- await writeEntries(staging, exported.entries);
135681
- if (git2) gitResult = commitExport(staging, commitMessage(source, exported), { force: true });
135682
- const now = await entryKind2(target3);
135683
- if (now === "dir" || now === "other") throw new CliError("ALREADY_EXISTS", `${target3} is no longer empty`, { details: { reason: "not_empty", folder: target3 }, help: "pass --to <new or empty folder>" });
135684
- try {
135685
- await rename2(staging, target3);
135686
- } catch (error51) {
135687
- const code3 = error51.code;
135688
- if (code3 === "ENOTEMPTY" || code3 === "EEXIST" || code3 === "ENOTDIR") {
135689
- throw new CliError("ALREADY_EXISTS", `${target3} is no longer empty`, { details: { reason: "not_empty", folder: target3 }, help: "pass --to <new or empty folder>" });
135690
- }
135691
- throw error51;
135692
- }
135693
- } catch (error51) {
135694
- await rm2(staging, { recursive: true, force: true }).catch(() => {
135695
- });
135696
- throw error51;
135697
- }
135698
- const home2 = await bundleHomeAt(target3, { home: deps2.auth.home });
135699
- deps2.stdout(
135700
- render(
135701
- {
135702
- export: "created",
135703
- folder: target3,
135704
- home: home2.home,
135705
- bundle_id: source.bundleId,
135706
- host: source.target.origin,
135707
- revision: exported.source.revision,
135708
- exported_at: exported.exportedAt,
135709
- okf_edition: exported.source.okfEdition,
135710
- documents: exported.counts.documents,
135711
- reserved: exported.counts.reserved,
135712
- blobs: exported.counts.blobs,
135713
- bytes: exported.bytes,
135714
- root_index: exported.entries.some((entry) => entry.path === "index.md"),
135715
- ...gitResult ? { git: gitResult } : {},
135716
- ...abandoned > 0 ? { removed_abandoned_exports: abandoned } : {},
135717
- hosted: HOSTED_UNCHANGED,
135718
- history: NO_HISTORY,
135719
- help: nextSteps2(target3, gitResult !== null)
135720
- },
135721
- mode
135722
- )
135723
- );
135724
- }
135725
- function nextSteps2(folder, git2) {
135726
- const steps = [commandFragment`${cliInvocation()} status --dir ${commandToken(folder)}`];
135727
- if (git2) steps.push(commandFragment`git -C ${commandToken(folder)} remote add origin <url> && ${cliInvocation()} sync --establish --dir ${commandToken(folder)}`);
135728
- return steps;
135729
- }
135730
- function isJournal(value) {
135731
- const record6 = value;
135732
- return record6 !== null && typeof record6 === "object" && record6.schema === JOURNAL_SCHEMA && typeof record6.bundle_id === "string" && typeof record6.host === "string" && typeof record6.audience === "string" && Number.isSafeInteger(record6.revision) && typeof record6.exported_at === "string" && typeof record6.git === "boolean" && Array.isArray(record6.adds) && record6.adds.every((entry) => typeof entry === "string") && Number.isSafeInteger(record6.same) && Array.isArray(record6.kept_local) && record6.kept_local.every((entry) => typeof entry === "string") && Array.isArray(record6.deleted_locally) && record6.deleted_locally.every((entry) => typeof entry === "string") && Number.isSafeInteger(record6.unsent_kept);
135733
- }
135734
- async function stagingDir(folder) {
135735
- const staging = path56.join(folder, IN_PLACE_STAGING);
135736
- try {
135737
- if (!(await lstat10(staging)).isDirectory()) return null;
135738
- return await realpath13(staging) === path56.join(await realpath13(folder), IN_PLACE_STAGING) ? staging : null;
135739
- } catch {
135740
- return null;
135741
- }
135742
- }
135743
- async function readJournal(folder) {
135744
- const staging = await stagingDir(folder);
135745
- if (!staging) return null;
135746
- let journal;
135747
- const read = readRegularFileNoFollowSync(path56.join(staging, JOURNAL));
135748
- if (read.state !== "present" || read.bytes.byteLength > MAX_JOURNAL_BYTES) return null;
135749
- try {
135750
- const value = JSON.parse(read.bytes.toString("utf8"));
135751
- if (!isJournal(value)) return null;
135752
- journal = value;
135753
- } catch {
135754
- return null;
135755
- }
135756
- const marker = readCheckoutMarker(folder);
135757
- if (!marker || marker.bundle_id !== journal.bundle_id || marker.host !== bindingHostArgument({ origin: journal.host, audience: journal.audience })) return null;
135758
- return journal;
135759
- }
135760
- async function sameBytes(file2, bytes) {
135761
- const read = readRegularFileNoFollowSync(file2);
135762
- return read.state === "present" && Buffer.compare(read.bytes, Buffer.from(bytes)) === 0;
135763
- }
135764
- async function placementOf(folder, entry) {
135765
- let info;
135766
- try {
135767
- info = await lstat10(path56.join(folder, ...entry.path.split("/")));
135768
- } catch (error51) {
135769
- const code3 = error51.code;
135770
- if (code3 === "ENOENT") return "add";
135771
- if (code3 === "ENOTDIR") return "kept";
135772
- throw error51;
135773
- }
135774
- if (!info.isFile()) return "kept";
135775
- return await sameBytes(path56.join(folder, ...entry.path.split("/")), entry.bytes) ? "same" : "kept";
135776
- }
135777
- async function placeStaged(folder, staging, relative2) {
135778
- const final = path56.join(folder, ...relative2.split("/"));
135779
- const staged = path56.join(staging, "files", ...relative2.split("/"));
135780
- try {
135781
- if ((await lstat10(staged)).isFile()) {
135782
- const stagedParent = await realpath13(path56.dirname(staged));
135783
- if (!stagedParent.startsWith(`${await realpath13(staging)}${path56.sep}`)) return "kept";
135784
- } else return "kept";
135785
- } catch (error51) {
135786
- if (error51.code !== "ENOENT") return "kept";
135787
- return await lstat10(final).then((info) => info.isFile(), () => false) ? "same" : "kept";
135788
- }
135789
- try {
135790
- await mkdir5(path56.dirname(final), { recursive: true });
135791
- const parent = await realpath13(path56.dirname(final));
135792
- if (parent !== folder && !parent.startsWith(`${folder}${path56.sep}`)) return "kept";
135793
- } catch {
135794
- return "kept";
135795
- }
135796
- let outcome;
135797
- try {
135798
- await link3(staged, final);
135799
- outcome = "added";
135800
- } catch (error51) {
135801
- const code3 = error51.code;
135802
- if (code3 !== "EEXIST") throw error51;
135803
- const stagedRead = readRegularFileNoFollowSync(staged);
135804
- outcome = stagedRead.state === "present" && await sameBytes(final, stagedRead.bytes) ? "same" : "kept";
135805
- }
135806
- await unlink14(staged).catch(() => {
135807
- });
135808
- return outcome;
135809
- }
135810
- async function writeJournal(staging, journal) {
135811
- const temp = path56.join(staging, `.${JOURNAL}.${randomBytes9(6).toString("hex")}.tmp`);
135812
- await writeNewFile(temp, Buffer.from(`${JSON.stringify(journal)}
135813
- `, "utf8"));
135814
- await rename2(temp, path56.join(staging, JOURNAL));
135815
- }
135816
- async function finishInPlace(folder, journal) {
135817
- const staging = await stagingDir(folder);
135818
- if (!staging) throw new CliError("RUNTIME", `the in-place export's staging folder in ${folder} is gone`, { help: `${cliInvocation()} export --in-place --dir ${commandToken(folder)}` });
135819
- const added = [];
135820
- const kept = [...journal.kept_local];
135821
- let same = journal.same;
135822
- for (const relative2 of journal.adds) {
135823
- classifyEntryPath(relative2);
135824
- const outcome = await placeStaged(folder, staging, relative2);
135825
- if (outcome === "added") added.push(relative2);
135826
- else if (outcome === "same") same += 1;
135827
- else kept.push(relative2);
135828
- }
135829
- let git2 = null;
135830
- if (journal.git) {
135831
- git2 = await ownCommit(folder) ?? commitExport(folder, `Export ${journal.bundle_id} from ${journal.host} at revision ${journal.revision}
135832
-
135833
- superbee export --in-place, ${journal.exported_at}. History is not included.
135834
- `, { force: false });
135835
- }
135836
- await rm2(path56.join(staging, "files"), { recursive: true, force: true });
135837
- await unlink14(path56.join(staging, JOURNAL)).catch(() => {
135838
- });
135839
- await rm2(staging, { recursive: true, force: true });
135840
- const marker = await removeCheckoutMarker(folder, { origin: journal.host, audience: journal.audience, bundle_id: journal.bundle_id }).catch(() => false) ? "removed" : "none";
135841
- return { added, kept, same, git: git2, marker };
135842
- }
135843
- function inPlaceReceipt(folder, journal, finished, homeName, resumed) {
135844
- return {
135845
- export: "converted",
135846
- folder,
135847
- home: homeName,
135848
- bundle_id: journal.bundle_id,
135849
- host: journal.host,
135850
- revision: journal.revision,
135851
- exported_at: journal.exported_at,
135852
- ...resumed ? { resumed: true } : {},
135853
- binding: "removed (the folder is no longer a hosted checkout)",
135854
- marker: finished.marker,
135855
- added: finished.added.length,
135856
- ...finished.added.length > 0 ? { added_paths: shown(finished.added) } : {},
135857
- unchanged: finished.same,
135858
- kept_local: finished.kept.length,
135859
- ...finished.kept.length > 0 ? { kept_local_paths: shown(finished.kept), kept_local_note: "these files differ from the host's (or block its path); the folder's own were kept" } : {},
135860
- ...journal.deleted_locally.length > 0 ? { deleted_locally: journal.deleted_locally.length, deleted_locally_paths: shown(journal.deleted_locally), deleted_locally_note: "deleted in this folder and not sent; the host's copies were not put back" } : {},
135861
- ...journal.unsent_kept > 0 ? { unsent_kept: journal.unsent_kept, unsent_note: "changes sync had not sent stay in this folder only" } : {},
135862
- ...finished.git ? { git: finished.git } : {},
135863
- hosted: HOSTED_UNCHANGED,
135864
- history: NO_HISTORY,
135865
- help: nextSteps2(folder, finished.git !== null)
135866
- };
135867
- }
135868
- async function ownCommit(folder) {
135869
- const head = runGit(folder, ["rev-parse", "--verify", "--quiet", "HEAD"]);
135870
- const top = runGit(folder, ["rev-parse", "--show-toplevel"]);
135871
- const own5 = top.status === 0 && await realpath13(top.stdout.trim()).catch(() => "") === await realpath13(folder).catch(() => folder);
135872
- if (!own5 || head.status !== 0) return null;
135873
- return { branch: runGit(folder, ["symbolic-ref", "-q", "--short", "HEAD"]).stdout.trim() || BOARD_BRANCH, commit: head.stdout.trim() };
135874
- }
135875
- async function assertNoRepository(folder) {
135876
- if (await lstat10(path56.join(folder, ".git")).then(() => true, () => false)) {
135877
- throw new CliError("ALREADY_EXISTS", `${folder} already holds a Git repository`, {
135878
- details: { reason: "already_git", folder },
135879
- help: `${cliInvocation()} export --in-place --dir ${commandToken(folder)}`
135880
- });
135881
- }
135882
- }
135883
- async function exportInPlace(dirArg, git2, keepUnsent, deps2, mode, resume2) {
135884
- const requested = path56.resolve(deps2.cwd, dirArg ?? ".");
135885
- assertBundleOutsidePrivateState(requested, deps2.auth.home);
135886
- const binding = await hostedCheckoutFor(dirArg === void 0 ? [] : ["--dir", dirArg], deps2.auth.home, deps2.cwd);
135887
- if (!binding) {
135888
- const folder2 = await realpath13(requested).catch(() => requested);
135889
- const journal = await readJournal(folder2);
135890
- if (journal) {
135891
- const finished = await finishInPlace(folder2, journal);
135892
- const home4 = await bundleHomeAt(folder2, { home: deps2.auth.home });
135893
- deps2.stdout(render(inPlaceReceipt(folder2, journal, finished, home4.home, true), mode));
135894
- return;
135895
- }
135896
- if (await entryKind2(folder2) === "absent") throw new CliError("NOT_FOUND", `${folder2} does not exist`, { help: `${cliInvocation()} export --in-place --dir <checkout folder>` });
135897
- const marker = readCheckoutMarker(folder2);
135898
- if (marker) {
135899
- const existing = git2 ? await ownCommit(folder2) : null;
135900
- if (git2 && !existing) await assertNoRepository(folder2);
135901
- const staging = await stagingDir(folder2);
135902
- if (staging && !await lstat10(path56.join(staging, JOURNAL)).then(() => true, () => false)) await rm2(staging, { recursive: true, force: true });
135903
- await removeCheckoutMarker(folder2, { origin: marker.host, audience: `${marker.host}/mcp`, bundle_id: marker.bundle_id });
135904
- const committed = existing ?? (git2 ? commitExport(folder2, `Export ${marker.bundle_id} (an unbound copy of a hosted checkout)
135905
-
135906
- superbee export --in-place. Nothing was fetched from the host; history is not included.
135907
- `, { force: false }) : null);
135908
- const home4 = await bundleHomeAt(folder2, { home: deps2.auth.home });
135909
- deps2.stdout(
135910
- render(
135911
- {
135912
- export: "converted",
135913
- folder: folder2,
135914
- home: home4.home,
135915
- bundle_id: marker.bundle_id,
135916
- from: "an unbound copy of a hosted checkout",
135917
- marker: "removed",
135918
- fetched: false,
135919
- note: `nothing was fetched: the files are kept as they are. To fill in what the copy lacks from the host first, adopt it (${cliInvocation()} checkout --adopt <folder> --host <url>), then export --in-place`,
135920
- ...committed ? { git: committed } : {},
135921
- hosted: HOSTED_UNCHANGED,
135922
- history: NO_HISTORY,
135923
- help: nextSteps2(folder2, committed !== null)
135924
- },
135925
- mode
135926
- )
135927
- );
135928
- return;
135929
- }
135930
- const home3 = await bundleHomeAt(folder2, { home: deps2.auth.home });
135931
- deps2.stdout(render({ export: "unchanged", folder: folder2, home: home3.home, reason: "not a hosted checkout: nothing to convert" }, mode));
135932
- return;
135933
- }
135934
- const folder = binding.path;
135935
- if (git2) await assertNoRepository(folder);
135936
- const status2 = await hostedStatus(binding, deps2.auth.home);
135937
- if (status2.sync.state === "busy") {
135938
- throw new CliError("CONFLICT", `another command holds the checkout lock for ${folder}`, { details: { reason: "checkout_busy", folder }, help: "wait for it to finish, then retry the same command" });
135939
- }
135940
- const unsentRefusal = (count5, sync3) => new CliError("CONFLICT", `the checkout of '${binding.bundle_id}' at ${folder} has ${count5} change(s) not on the host`, {
135941
- details: {
135942
- reason: "unsent_changes",
135943
- folder,
135944
- ...sync3,
135945
- keep_them_here_only: commandFragment`${cliInvocation()} export --in-place --dir ${commandToken(folder)} --keep-unsent${git2 ? commandFragment` --git` : commandFragment``}`
135946
- },
135947
- help: commandFragment`${cliInvocation()} sync --dir ${commandToken(folder)}`
135948
- });
135949
- const early = Number(status2.sync.unsent ?? 0) + Number(status2.sync.conflicts ?? 0) + Number(status2.sync.held_files ?? 0) + Number(status2.sync.held_deletions ?? 0);
135950
- if (early > 0 && !keepUnsent) throw unsentRefusal(early, status2.sync);
135951
- const source = { target: resolveHostedTarget(binding.audience), bundleId: binding.bundle_id, slug: binding.workspace_slug ?? null, workspace: null, binding };
135952
- const exported = await fetchExport(source, deps2, resume2);
135953
- const result3 = await filesystemPushRoleLocks().request(checkoutLockName(folder), { ifAvailable: true }, async (lock) => {
135954
- if (!lock) {
135955
- throw new CliError("CONFLICT", `another command holds the checkout lock for ${folder}`, { details: { reason: "checkout_busy", folder }, help: "wait for it to finish, then retry the same command" });
135956
- }
135957
- const current = await bindingForPath(deps2.auth.home, folder);
135958
- if (!current || current.checkout_id !== binding.checkout_id) {
135959
- throw new CliError("CONFLICT", `the checkout at ${folder} changed while it was exported`, { details: { reason: "checkout_changed", folder }, help: "retry the same command" });
135960
- }
135961
- const store = await FileJournaledBackend.open({ directory: checkoutStoreDir(deps2.auth.home, binding.checkout_id), readOnly: true });
135962
- let pending;
135963
- try {
135964
- const classified = await classifyCheckout(binding, deps2.auth.home, store);
135965
- pending = /* @__PURE__ */ new Set([...classified.unsent, ...classified.conflicts, ...classified.held.keys(), ...classified.heldDeletions]);
135966
- } finally {
135967
- await store.close();
135968
- }
135969
- if (pending.size > 0 && !keepUnsent) throw unsentRefusal(pending.size, { unsent_ids: shown([...pending]) });
135970
- const staging = path56.join(folder, IN_PLACE_STAGING);
135971
- await rm2(staging, { recursive: true, force: true });
135972
- await mkdir5(path56.join(staging, "files"), { recursive: true });
135973
- const adds = [];
135974
- const kept = [];
135975
- const deletedLocally = [];
135976
- let same = 0;
135977
- for (const entry of exported.entries) {
135978
- const id = entry.kind === "document" ? entry.path.slice(0, -3) : entry.path;
135979
- const placement = await placementOf(folder, entry);
135980
- if (placement === "add" && pending.has(id)) {
135981
- deletedLocally.push(entry.path);
135982
- continue;
135983
- }
135984
- if (placement === "same") same += 1;
135985
- else if (placement === "kept") kept.push(entry.path);
135986
- else {
135987
- const file2 = path56.join(staging, "files", ...entry.path.split("/"));
135988
- await mkdir5(path56.dirname(file2), { recursive: true });
135989
- await writeNewFile(file2, entry.bytes);
135990
- adds.push(entry.path);
135991
- }
135992
- }
135993
- const journal = {
135994
- schema: JOURNAL_SCHEMA,
135995
- bundle_id: binding.bundle_id,
135996
- host: binding.origin,
135997
- audience: binding.audience,
135998
- revision: exported.source.revision,
135999
- exported_at: exported.exportedAt,
136000
- git: git2,
136001
- adds,
136002
- same,
136003
- kept_local: kept,
136004
- deleted_locally: deletedLocally,
136005
- unsent_kept: pending.size
136006
- };
136007
- await writeJournal(staging, journal);
136008
- await releaseCheckout(deps2.auth.home, binding);
136009
- return { journal, finished: await finishInPlace(folder, journal) };
136010
- });
136011
- const home2 = await bundleHomeAt(folder, { home: deps2.auth.home });
136012
- deps2.stdout(render(inPlaceReceipt(folder, result3.journal, result3.finished, home2.home, false), mode));
136013
- }
136014
- async function exportCommand(argv2, partial2 = {}) {
136015
- const deps2 = exportDeps(partial2);
136016
- const { values, positionals } = parseLeafOrUsage(
136017
- () => parseArgs42({
136018
- args: argv2,
136019
- options: {
136020
- to: { type: "string" },
136021
- "in-place": { type: "boolean" },
136022
- dir: { type: "string" },
136023
- host: { type: "string" },
136024
- workspace: { type: "string" },
136025
- git: { type: "boolean" },
136026
- "keep-unsent": { type: "boolean" },
136027
- json: { type: "boolean" },
136028
- help: { type: "boolean", short: "h" }
136029
- },
136030
- allowPositionals: true
136031
- }),
136032
- CLI_LEAVES.export
136033
- );
136034
- if (values.help) {
136035
- deps2.stdout(renderUsage(EXPORT_USAGE));
136036
- return;
136037
- }
136038
- const mode = resolveMode(values);
136039
- const usage3 = (message) => new CliError("USAGE", message, { help: `${cliInvocation()} export --help` });
136040
- const named = positionals[0];
136041
- const inPlace = values["in-place"] === true;
136042
- if (inPlace === (values.to !== void 0)) throw usage3("pass exactly one of --to <folder> and --in-place");
136043
- if (named !== void 0 && values.dir !== void 0) throw usage3("pass a bundle id or --dir <checkout>, not both");
136044
- if (named === void 0 && (values.host !== void 0 || values.workspace !== void 0)) throw usage3("--host and --workspace go with a bundle id; a checkout uses its own");
136045
- if (inPlace && named !== void 0) throw usage3("--in-place converts a checkout folder: pass --dir <checkout>, not a bundle id");
136046
- if (!inPlace && values["keep-unsent"]) throw usage3("--keep-unsent goes with --in-place");
136047
- const reference = named === void 0 ? void 0 : parseHostedBundleReference(named);
136048
- if (named !== void 0 && !reference) throw usage3(`'${named}' is not a hosted bundle id or <workspace>/<bundle-id>`);
136049
- const git2 = values.git === true;
136050
- const resume2 = commandFragment`${cliInvocation()} export${named !== void 0 ? commandFragment` ${commandToken(named)}` : commandFragment``}${values.dir !== void 0 ? commandFragment` --dir ${commandToken(path56.resolve(deps2.cwd, values.dir))}` : commandFragment``}${values.to !== void 0 ? commandFragment` --to ${commandToken(path56.resolve(deps2.cwd, values.to))}` : commandFragment``}${inPlace ? commandFragment` --in-place` : commandFragment``}${values.host !== void 0 ? commandFragment` --host ${commandToken(values.host)}` : commandFragment``}${values.workspace !== void 0 ? commandFragment` --workspace ${commandToken(values.workspace)}` : commandFragment``}${git2 ? commandFragment` --git` : commandFragment``}${values["keep-unsent"] ? commandFragment` --keep-unsent` : commandFragment``}${values.json ? commandFragment` --json` : commandFragment``}`;
136051
- if (values.dir !== void 0) assertBundleOutsidePrivateState(path56.resolve(deps2.cwd, values.dir), deps2.auth.home);
136052
- if (inPlace) {
136053
- await exportInPlace(values.dir, git2, values["keep-unsent"] === true, deps2, mode, resume2);
136054
- return;
136055
- }
136056
- let source;
136057
- if (reference) {
136058
- const named2 = await requireHostedBundleHost(values.host, deps2.auth);
136059
- source = {
136060
- target: named2.target,
136061
- bundleId: reference.bundleId,
136062
- slug: reference.slug,
136063
- workspace: workspaceForNamedHost(named2, values.workspace) ?? null,
136064
- binding: null
136065
- };
136066
- } else {
136067
- const binding = await hostedCheckoutFor(values.dir === void 0 ? [] : ["--dir", values.dir], deps2.auth.home, deps2.cwd);
136068
- if (!binding) {
136069
- throw new CliError("NOT_FOUND", `${path56.resolve(deps2.cwd, values.dir ?? ".")} is not a hosted checkout`, {
136070
- details: { reason: "not_a_checkout" },
136071
- help: `${cliInvocation()} export <bundle-id> --to ${commandToken(values.to)}`
136072
- });
136073
- }
136074
- source = { target: resolveHostedTarget(binding.audience), bundleId: binding.bundle_id, slug: binding.workspace_slug ?? null, workspace: null, binding };
136075
- }
136076
- await exportToFolder(source, values.to, git2, deps2, mode, resume2);
136077
- }
136078
- var EXPORT_USAGE, EXPORT_DEADLINE_MS, SHOWN, JOURNAL, JOURNAL_SCHEMA, MAX_JOURNAL_BYTES, NO_HISTORY, HOSTED_UNCHANGED, PAGED_EXPORT_ATTEMPTS, PAGED_EXPORT_RESTART_MS, PAGE_RETRY_MS, pause2, ABANDONED_AFTER_MS;
136079
- var init_export = __esm({
136080
- "../cli/src/commands/export.ts"() {
136081
- "use strict";
136082
- init_define_SUPERBEE_BUILD_IDENTITY();
136083
- init_define_SUPERBEE_UPDATE_POLICY();
136084
- init_src();
136085
- init_filesystem_push_role();
136086
- init_src2();
136087
- init_args();
136088
- init_bundle2();
136089
- init_bundle_home();
136090
- init_command_spec();
136091
- init_command_text();
136092
- init_errors4();
136093
- init_invocation();
136094
- init_output2();
136095
- init_private_state_bundle_boundary();
136096
- init_nofollow_read();
136097
- init_autopull2();
136098
- init_session();
136099
- init_discovery();
136100
- init_binding();
136101
- init_client();
136102
- init_account();
136103
- init_refusals();
136104
- init_reference();
136105
- init_sync2();
136106
- init_status();
136107
- init_file_journaled_backend();
136108
- init_marker();
136109
- init_export_archive();
136110
- init_export_archive();
136111
- EXPORT_USAGE = `superbee export \u2014 copy a hosted bundle out of hosted Superbee
136112
-
136113
- Usage:
136114
- superbee export <bundle-id> --to <folder> [--host <url>] [--workspace <id>] [--git] [--json]
136115
- superbee export [--dir <checkout>] --to <folder> [--git] [--json]
136116
- superbee export [--dir <checkout>] --in-place [--git] [--keep-unsent] [--json]
136117
-
136118
- Fetches the hosted bundle's portable export: every document, reserved file (index.md, log.md)
136119
- and blob at its latest confirmed revision, byte for byte as the host stores them. The archive is
136120
- verified whole (every entry against the host's digest manifest) before any file is written.
136121
- Signs in if needed (AUTH_REQUIRED, exit 4, carries the one link to relay and the command to
136122
- re-run). The hosted bundle is never changed. History is not exported: the folder holds the
136123
- current revision only.
136124
-
136125
- --to <folder> writes a new local bundle there. The folder must be new or empty; it appears
136126
- complete or not at all. With a bundle id the host is --host, else your last sign-in; with
136127
- --dir (or run inside a checkout) the checkout's own host and bundle are used. A bundle id in more
136128
- than one of your workspaces is named <workspace>/<bundle-id>, or with --workspace.
136129
-
136130
- --in-place converts a hosted checkout into a plain local bundle: it adds the files the checkout
136131
- lacks (blobs, reserved files, documents added on the host), never overwrites a file, then
136132
- forgets the checkout's binding. It refuses a checkout with changes sync has not sent, conflicts
136133
- or held files; run 'superbee sync' first, or pass --keep-unsent to keep them in this folder only.
136134
- If it is interrupted, re-running the same command finishes it.
136135
-
136136
- --git makes the result a Git board: a repository on branch '${BOARD_BRANCH}' whose first commit is the
136137
- export. Share it with 'git remote add origin <url>' and 'superbee sync --establish'.
136138
-
136139
- Options:
136140
- --to <folder> New or empty folder to export into
136141
- --in-place Convert the hosted checkout (--dir, else the current folder) into a local bundle
136142
- --dir <checkout> The hosted checkout to export (default: the current folder)
136143
- --host <url> Hosted Superbee URL, with a bundle id; default: your last sign-in
136144
- --workspace <id> With a bundle id: your workspace that holds it, by id or slug
136145
- --git Initialize a Git repository on branch '${BOARD_BRANCH}' and commit the export
136146
- --keep-unsent With --in-place: convert even with unsent changes, conflicts or held files
136147
- --json Emit compact JSON instead of TOON
136148
- -h, --help Show this help
136149
-
136150
- Examples:
136151
- superbee export team.knowledge --to ~/work/team-knowledge --git
136152
- superbee export --dir ~/work/team --to ~/work/team-copy
136153
- superbee export --dir ~/work/team --in-place
136154
- `;
136155
- EXPORT_DEADLINE_MS = 6e4;
136156
- SHOWN = 20;
136157
- JOURNAL = IN_PLACE_JOURNAL;
136158
- JOURNAL_SCHEMA = 1;
136159
- MAX_JOURNAL_BYTES = 8 * 1024 * 1024;
136160
- NO_HISTORY = "not exported: the folder holds the current revision only";
136161
- HOSTED_UNCHANGED = "unchanged: the hosted bundle still exists and was not modified";
136162
- PAGED_EXPORT_ATTEMPTS = 3;
136163
- PAGED_EXPORT_RESTART_MS = [500, 1500];
136164
- PAGE_RETRY_MS = 1e3;
136165
- pause2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
136166
- ABANDONED_AFTER_MS = 60 * 60 * 1e3;
136167
- }
136168
- });
136169
-
136170
136308
  // ../cli/src/commands/op.ts
136171
136309
  import { homedir as homedir41 } from "node:os";
136172
136310
  import path57 from "node:path";
@@ -136453,8 +136591,9 @@ function cleanAuthor(author) {
136453
136591
  function gitVersions(board, id, rel, current, okfVersion, now, documentInputBytes) {
136454
136592
  const repoPath = board.prefix === "" ? rel : `${board.prefix}/${rel}`;
136455
136593
  const log = runGit(board.top, ["log", "--no-renames", "--format=%H%x1f%an <%ae>%x1f%aI", "HEAD", "--", repoPath]);
136456
- if (log.status !== 0) return { rows: [], skipped: 0 };
136594
+ if (log.status !== 0) return { rows: [], skipped: 0, oversized: [] };
136457
136595
  const rows = [];
136596
+ const oversized = [];
136458
136597
  let skipped = 0;
136459
136598
  let previous3 = current;
136460
136599
  for (const line2 of log.stdout.split("\n").filter(Boolean)) {
@@ -136481,19 +136620,24 @@ function gitVersions(board, id, rel, current, okfVersion, now, documentInputByte
136481
136620
  skipped += 1;
136482
136621
  continue;
136483
136622
  }
136623
+ const frontmatter = Object.fromEntries(
136624
+ Object.entries(parsed2.frontmatter).filter(([key2]) => !IMPORT_DROPPED_FIELDS.includes(key2))
136625
+ );
136626
+ if (Buffer.byteLength(JSON.stringify(frontmatter)) > FRONTMATTER_JSON_BYTES) {
136627
+ skipped += 1;
136628
+ oversized.push(`${rel}@${sha.slice(0, 12)}`);
136629
+ continue;
136630
+ }
136484
136631
  rows.push({
136485
136632
  documentId: id,
136486
136633
  label: `imported:git/${sha}`,
136487
136634
  author: cleanAuthor(author ?? ""),
136488
136635
  authoredAt: new Date(when).toISOString(),
136489
- // An imported version never asserts verification or host attribution, as a current document does not.
136490
- frontmatter: Object.fromEntries(
136491
- Object.entries(parsed2.frontmatter).filter(([key2]) => !IMPORT_DROPPED_FIELDS.includes(key2))
136492
- ),
136636
+ frontmatter,
136493
136637
  body: parsed2.body ?? ""
136494
136638
  });
136495
136639
  }
136496
- return { rows: rows.reverse(), skipped };
136640
+ return { rows: rows.reverse(), skipped, oversized };
136497
136641
  }
136498
136642
  async function okfVersionOf(rootIndex) {
136499
136643
  if (rootIndex === null) return void 0;
@@ -136636,7 +136780,12 @@ async function planPublish(folder, options) {
136636
136780
  throw error51;
136637
136781
  }
136638
136782
  if (request3.kind !== "create") throw new Error(`'${id}' became a ${request3.kind} request`);
136639
- if (Buffer.byteLength(JSON.stringify(request3.payload.frontmatter)) > FRONTMATTER_JSON_BYTES) {
136783
+ const frontmatterBytes = Buffer.byteLength(JSON.stringify(request3.payload.frontmatter));
136784
+ if ("verified" in request3.payload.frontmatter && frontmatterBytes <= FRONTMATTER_JSON_BYTES && frontmatterBytes > FRONTMATTER_JSON_BYTES - VERIFIED_STAMP_ROOM_BYTES) {
136785
+ blockers.push({ path: entry.rel, reason: "too_large", message: `'${id}' carries verified and leaves less than ${VERIFIED_STAMP_ROOM_BYTES} bytes of the ${FRONTMATTER_JSON_BYTES / 1024} KiB of frontmatter the host stores for its stamps on later edits; shorten its frontmatter` });
136786
+ continue;
136787
+ }
136788
+ if (frontmatterBytes > FRONTMATTER_JSON_BYTES) {
136640
136789
  blockers.push({ path: entry.rel, reason: "too_large", message: `'${id}' has more than the ${FRONTMATTER_JSON_BYTES / 1024} KiB of frontmatter the host stores, counting ${request3.provenance.carried.join(", ")}` });
136641
136790
  continue;
136642
136791
  }
@@ -136676,6 +136825,7 @@ async function planPublish(folder, options) {
136676
136825
  const versions = gitVersions(options.board, file2.id, file2.rel, file2.text, okfVersion, options.now ?? Date.now(), documentInputBytes);
136677
136826
  history.push(...versions.rows);
136678
136827
  skippedVersions += versions.skipped;
136828
+ for (const version4 of versions.oversized) skipped.push({ path: version4, reason: `an earlier version with more than the ${FRONTMATTER_JSON_BYTES / 1024} KiB of frontmatter the host stores; its history is imported without it` });
136679
136829
  }
136680
136830
  historyPlan = {
136681
136831
  mode: "git",
@@ -136816,7 +136966,7 @@ function packParts(content3, versions) {
136816
136966
  }
136817
136967
  return parts;
136818
136968
  }
136819
- var MiB, PUBLISH_BOUNDS, STAGED_PUBLISH_BOUNDS, CONTENT_TYPES, HOSTED_KIND_WRITE, FORMAT_AT_EDGE, FORMAT_INSIDE, WIDEST_TARGET;
136969
+ var MiB, PUBLISH_BOUNDS, STAGED_PUBLISH_BOUNDS, CONTENT_TYPES, HOSTED_KIND_WRITE, VERIFIED_STAMP_ROOM_BYTES, FORMAT_AT_EDGE, FORMAT_INSIDE, WIDEST_TARGET;
136820
136970
  var init_publish_plan = __esm({
136821
136971
  "../cli/src/hosted/publish-plan.ts"() {
136822
136972
  "use strict";
@@ -136876,6 +137026,7 @@ var init_publish_plan = __esm({
136876
137026
  ".xml": "application/xml"
136877
137027
  };
136878
137028
  HOSTED_KIND_WRITE = Object.freeze({ strict: true, persistActor: true, producer: "process:superbee-hosted" });
137029
+ VERIFIED_STAMP_ROOM_BYTES = 512;
136879
137030
  FORMAT_AT_EDGE = new RegExp("^\\p{Cf}|\\p{Cf}$", "u");
136880
137031
  FORMAT_INSIDE = new RegExp("(?![\\u200c\\u200d])\\p{Cf}", "u");
136881
137032
  WIDEST_TARGET = Object.freeze({ workspace: "w".repeat(64), bundleId: "b".repeat(128), name: "n".repeat(128) });
@@ -137236,7 +137387,7 @@ function provenanceOf(plan) {
137236
137387
  return {
137237
137388
  carried: plan.provenance.carried,
137238
137389
  dropped: plan.provenance.dropped,
137239
- note: "provenance travels as each file states it (a claim, not a verification); verification events and the last-editor attribution stay in the folder, and the host attributes the creation to you"
137390
+ note: "provenance travels as each file states it (a claim, not a verification), so documents read on the host as they do here; the host records the creation as yours and stamps the writer itself on every later edit"
137240
137391
  };
137241
137392
  }
137242
137393
  function createRefusal(code3, message, context) {
@@ -137263,6 +137414,14 @@ function createRefusal(code3, message, context) {
137263
137414
  details,
137264
137415
  help: `put the files back as they were and re-run the same command to finish it; if the creation already finished, bind this folder to it (your edits become sync conflicts): ${cliInvocation()} checkout --adopt ${commandToken(context.folder)} --host ${commandToken(bindingHostArgument(context.target))}`
137265
137416
  });
137417
+ case "validation_failed":
137418
+ if (/sets (superbee_updated_by|verified)\b.*which only the host writes/.test(message)) {
137419
+ return new CliError("FORBIDDEN", `${context.target.origin} does not yet keep each document's superbee_updated_by and verified on a new bundle, which this CLI carries`, {
137420
+ details: { ...details, reason: "host_predates_provenance" },
137421
+ help: "leave the files as they are: publish again once the host is updated, or publish with an earlier superbee CLI, which leaves those fields behind"
137422
+ });
137423
+ }
137424
+ return new CliError("USAGE", `${context.target.origin} refused the bundle (${code3}): ${message}`, { details, help: "fix the files it names, then preview again" });
137266
137425
  default:
137267
137426
  return new CliError("USAGE", `${context.target.origin} refused the bundle (${code3}): ${message}`, { details, help: "fix the files it names, then preview again" });
137268
137427
  }
@@ -137709,9 +137868,10 @@ Without --yes, previews only, with no network: what travels (documents, reserved
137709
137868
  files), what stays (dot-files, links), how it is sent, every bound the host enforces, the history
137710
137869
  plan, and what will happen to the folder.
137711
137870
 
137712
- Each document's provenance (generated, timestamp, actor, okf_version) travels as its file states
137713
- it, a claim rather than a verification; verified and superbee_updated_by never travel. The
137714
- preview and the receipt count both, per field.
137871
+ Each document's provenance (generated, timestamp, actor, okf_version, superbee_updated_by,
137872
+ verified) travels as its file states it, a claim rather than a verification, so the bundle reads
137873
+ on the host as it does here. The host still records the creation as yours, and stamps the writer
137874
+ itself on every later edit. The preview and the receipt count each field.
137715
137875
 
137716
137876
  A bundle within one request's bounds (at most 1,000 documents, 100 other files of 1 MB each,
137717
137877
  1,000 earlier versions, 1,500 objects in all, 3 MiB) is sent in one request. A larger one is sent