superbee 0.3.0-pre.6 → 0.4.0-pre.1

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, target2) => (target2 = 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.3.0-pre.6" }, source: { commit: "3c313f3bfc9c052c6d4354111df5c293f8cde272", 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.0-pre.1" }, source: { commit: "2b1084b2af86558ac242ab74c14fa5b4f9f2dbb4", dirty: false }, artifact: { channel: "npm-package" }, compatibility_contracts: { skill: 1, hook: 1, mcp: 1 } };
48
48
  }
49
49
  });
50
50
 
@@ -9416,6 +9416,9 @@ var init_mutation = __esm({
9416
9416
  });
9417
9417
 
9418
9418
  // ../core/src/kinds.ts
9419
+ function isConventionId(id) {
9420
+ return id.startsWith(CONVENTIONS_PREFIX);
9421
+ }
9419
9422
  function buildKindRegistry(docs, initialWarnings = [], options = {}) {
9420
9423
  const accepted = /* @__PURE__ */ new Map();
9421
9424
  const warnings = [...initialWarnings];
@@ -20551,6 +20554,18 @@ var init_reference = __esm({
20551
20554
  import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
20552
20555
  import { lstat as lstat2, readdir as readdir2, rm, stat as stat2, unlink as unlink2 } from "node:fs/promises";
20553
20556
  import { join as join7 } from "node:path";
20557
+ function withDefinitionWrites(binding, stated) {
20558
+ const { definition_writes: _previous, ...rest } = binding;
20559
+ return stated === null ? rest : { ...rest, definition_writes: stated };
20560
+ }
20561
+ async function recordDefinitionWrites(home2, binding, stated) {
20562
+ const current = await readBinding(home2, binding.checkout_id);
20563
+ if (current === null) return binding;
20564
+ if ((current.definition_writes ?? null) === stated) return current;
20565
+ const next = withDefinitionWrites(current, stated);
20566
+ await writeBinding(home2, next);
20567
+ return next;
20568
+ }
20554
20569
  function sameFolder(a, b) {
20555
20570
  return a.dev === b.dev && a.ino === b.ino && (!a.birth || !b.birth || a.birth === b.birth);
20556
20571
  }
@@ -20618,7 +20633,9 @@ async function writeBinding(home2, binding) {
20618
20633
  }
20619
20634
  async function readBinding(home2, checkoutId) {
20620
20635
  const value = await readJson(home2, join7(checkoutDir(home2, checkoutId), "binding.json"));
20621
- return isBinding(value) && value.checkout_id === checkoutId ? value : null;
20636
+ if (!isBinding(value) || value.checkout_id !== checkoutId) return null;
20637
+ const stated = value.definition_writes;
20638
+ return stated === void 0 || stated === "allowed" || stated === "refused" ? value : { ...value, definition_writes: "refused" };
20622
20639
  }
20623
20640
  async function indexCheckoutPath(home2, binding) {
20624
20641
  await writeUserStateFileAtomic0600(
@@ -24032,12 +24049,19 @@ function idleBounded(body, deadlineMs, abort) {
24032
24049
  })
24033
24050
  );
24034
24051
  }
24052
+ function requestHeaders(credentials, extra, type) {
24053
+ const headers = new Headers(extra);
24054
+ for (const [name, value] of Object.entries(credentials)) headers.set(name, value);
24055
+ headers.set("Content-Type", type);
24056
+ headers.set("Accept", "application/json");
24057
+ return headers;
24058
+ }
24035
24059
  function createFetchCarrier(options) {
24036
24060
  const fetcher = options.fetch ?? fetch;
24037
24061
  const deadlineMs = options.deadlineMs ?? 15e3;
24038
24062
  const bindingHeader = options.bindingHeader ?? "X-Superbee-Checkout";
24039
24063
  const base = new URL(options.baseUrl);
24040
- async function send(path53, input, signal, extra, deadline) {
24064
+ async function send(path53, body, signal, extra, deadline) {
24041
24065
  if (!path53.startsWith("/")) throw new TypeError(`hosted route '${path53}' must be absolute`);
24042
24066
  let credentials;
24043
24067
  try {
@@ -24050,14 +24074,29 @@ function createFetchCarrier(options) {
24050
24074
  return await fetcher(new URL(path53, base), {
24051
24075
  method: "POST",
24052
24076
  redirect: "error",
24053
- headers: { ...credentials, ...extra, "Content-Type": "application/json", Accept: "application/json" },
24054
- body: JSON.stringify(input),
24077
+ headers: requestHeaders(credentials, extra, body.type),
24078
+ body: body.content,
24055
24079
  signal: AbortSignal.any([signal, deadline])
24056
24080
  });
24057
24081
  } catch (cause) {
24058
24082
  throw new HostedCarrierError("unavailable", { cause });
24059
24083
  }
24060
24084
  }
24085
+ const jsonBody = (input) => ({ type: "application/json", content: JSON.stringify(input) });
24086
+ const owned = /* @__PURE__ */ new Set(["x-superbee-write-request", bindingHeader.toLowerCase(), RECREATE_HEADER.toLowerCase(), VIA_HEADER.toLowerCase(), ACCEPT_DELETES_HEADER.toLowerCase()]);
24087
+ async function answer2(path53, body, signal, extra, maximum) {
24088
+ if (!Number.isSafeInteger(maximum) || maximum <= 0) throw new HostedCarrierError("denied");
24089
+ const deadline = AbortSignal.timeout(deadlineMs);
24090
+ const response = await send(path53, body, signal, extra, deadline);
24091
+ let read;
24092
+ try {
24093
+ read = await readBounded(response, maximum);
24094
+ } catch (error51) {
24095
+ if (response.ok) throw error51 instanceof HostedCarrierError ? error51 : new HostedCarrierError("unavailable", { cause: error51 });
24096
+ read = void 0;
24097
+ }
24098
+ return { status: response.status, headers: response.headers, body: read };
24099
+ }
24061
24100
  return {
24062
24101
  async json(path53, input, signal, request2) {
24063
24102
  if (request2.writeRequest !== void 0 && !WRITE_REQUEST.test(request2.writeRequest)) throw new HostedCarrierError("denied");
@@ -24071,23 +24110,26 @@ function createFetchCarrier(options) {
24071
24110
  if (request2.recreate !== void 0) extra[RECREATE_HEADER] = request2.recreate;
24072
24111
  if (request2.via !== void 0) extra[VIA_HEADER] = request2.via;
24073
24112
  if (request2.acceptDeletes !== void 0) extra[ACCEPT_DELETES_HEADER] = String(request2.acceptDeletes);
24074
- const deadline = AbortSignal.timeout(deadlineMs);
24075
- const response = await send(path53, input, signal, extra, deadline);
24076
- let body;
24077
- try {
24078
- body = await readBounded(response, request2.maximum);
24079
- } catch (error51) {
24080
- if (response.ok) throw error51 instanceof HostedCarrierError ? error51 : new HostedCarrierError("unavailable", { cause: error51 });
24081
- body = void 0;
24113
+ return answer2(path53, jsonBody(input), signal, extra, request2.maximum);
24114
+ },
24115
+ async bytes(path53, body, signal, request2) {
24116
+ if (!(body instanceof Uint8Array)) throw new HostedCarrierError("denied");
24117
+ if (request2.writeRequest !== void 0 && !WRITE_REQUEST.test(request2.writeRequest)) throw new HostedCarrierError("denied");
24118
+ const extra = {};
24119
+ for (const [name, value] of Object.entries(request2.headers ?? {})) {
24120
+ const lower = name.toLowerCase();
24121
+ if (!ROUTE_HEADER.test(lower) || owned.has(lower) || lower in extra || typeof value !== "string" || !ROUTE_HEADER_VALUE.test(value)) throw new HostedCarrierError("denied");
24122
+ extra[lower] = value;
24082
24123
  }
24083
- return { status: response.status, headers: response.headers, body };
24124
+ if (request2.writeRequest !== void 0) extra["X-Superbee-Write-Request"] = request2.writeRequest;
24125
+ return answer2(path53, { type: "application/octet-stream", content: body }, signal, extra, request2.maximum);
24084
24126
  },
24085
24127
  async stream(path53, input, signal) {
24086
24128
  const idle = new AbortController();
24087
24129
  const firstByte = setTimeout(() => idle.abort(), deadlineMs);
24088
24130
  let response;
24089
24131
  try {
24090
- response = await send(path53, input, signal, {}, idle.signal);
24132
+ response = await send(path53, jsonBody(input), signal, {}, idle.signal);
24091
24133
  } finally {
24092
24134
  clearTimeout(firstByte);
24093
24135
  }
@@ -24099,7 +24141,7 @@ function createFetchCarrier(options) {
24099
24141
  }
24100
24142
  };
24101
24143
  }
24102
- var ACCEPT_DELETES_HEADER, MAXIMUM_ACCEPTED_DELETIONS, RECREATE_HEADER, VIA_HEADER, HostedAnswerTooLarge, HostedCarrierError, WRITE_REQUEST, BINDING;
24144
+ var ACCEPT_DELETES_HEADER, MAXIMUM_ACCEPTED_DELETIONS, RECREATE_HEADER, VIA_HEADER, HostedAnswerTooLarge, HostedCarrierError, WRITE_REQUEST, BINDING, ROUTE_HEADER, ROUTE_HEADER_VALUE;
24103
24145
  var init_carrier = __esm({
24104
24146
  "../core/src/hosted-transport/carrier.ts"() {
24105
24147
  "use strict";
@@ -24127,10 +24169,43 @@ var init_carrier = __esm({
24127
24169
  };
24128
24170
  WRITE_REQUEST = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
24129
24171
  BINDING = /^sha256:[a-f0-9]{64}$/;
24172
+ ROUTE_HEADER = /^x-superbee-[a-z0-9]+(?:-[a-z0-9]+)*$/;
24173
+ ROUTE_HEADER_VALUE = /^[\x20-\x7e]{0,1024}$/;
24130
24174
  }
24131
24175
  });
24132
24176
 
24133
24177
  // ../core/src/hosted-transport/answer-rows.ts
24178
+ function definitionFindingsText(details) {
24179
+ const findings = isRecord7(details) && Array.isArray(details.findings) ? details.findings : [];
24180
+ const clauses = [];
24181
+ for (const finding of findings) {
24182
+ if (!isRecord7(finding)) continue;
24183
+ const rule = findingText(finding.rule);
24184
+ if (!rule) continue;
24185
+ const type = findingText(finding.type);
24186
+ const conventionId = findingText(finding.conventionId);
24187
+ const field = findingText(finding.field);
24188
+ const detail = findingText(finding.detail);
24189
+ const where = [type ? `Kind '${type}'` : conventionId ? `'${conventionId}'` : "", field ? `field '${field}'` : ""].filter(Boolean).join(" ");
24190
+ const instances = isRecord7(finding.instances) ? finding.instances : void 0;
24191
+ const total = instances && Number.isSafeInteger(instances.count) && instances.count > 0 ? instances.count : 0;
24192
+ const ids = instances && Array.isArray(instances.ids) ? instances.ids.map(findingText).filter((id) => id !== void 0) : [];
24193
+ const shown2 = ids.slice(0, FINDING_IDS_SHOWN);
24194
+ const documents = total > 0 ? `: ${total} document${total === 1 ? "" : "s"}${shown2.length > 0 ? `, ${shown2.join(", ")}${total > shown2.length ? ", ..." : ""}` : ""}` : "";
24195
+ clauses.push(`${rule}${where ? ` on ${where}` : ""}${detail ? ` (${detail})` : ""}${documents}`);
24196
+ }
24197
+ const more = isRecord7(details) && details.truncated === true;
24198
+ let text4 = "";
24199
+ for (const [index2, clause] of clauses.entries()) {
24200
+ const next = text4 === "" ? clause : `${text4}; ${clause}`;
24201
+ const rest = clauses.length - index2;
24202
+ if (new TextEncoder().encode(next).byteLength > DEFINITION_FINDINGS_TEXT_BYTES - 32) {
24203
+ return text4 === "" ? `${clause.slice(0, 512)}...${rest > 1 ? `; and ${rest - 1} more` : ""}` : `${text4}; and ${rest} more`;
24204
+ }
24205
+ text4 = next;
24206
+ }
24207
+ return more && text4 !== "" ? `${text4}; and more` : text4;
24208
+ }
24134
24209
  function parseWriteResult(raw, expected) {
24135
24210
  const refuse = () => new HostedAnswerError("the write result envelope is not one the client admits");
24136
24211
  if (!isRecord7(raw) || !onlyKeys(raw, ["ok", "operationId", "data", "error"]) || typeof raw.operationId !== "string" || !expected.operationIds.includes(raw.operationId)) throw refuse();
@@ -24144,7 +24219,8 @@ function parseWriteResult(raw, expected) {
24144
24219
  }
24145
24220
  if (raw.ok !== false) throw refuse();
24146
24221
  const error51 = raw.error;
24147
- if (raw.data !== void 0 || !isRecord7(error51) || !onlyKeys(error51, ERROR_KEYS) || !WRITE_ERROR_CODES.includes(error51.code) || typeof error51.message !== "string" || error51.retryable !== false || error51.writeState !== "not_applied" && error51.writeState !== "unknown" || error51.currentVersion !== void 0 && !isContentVersion(error51.currentVersion) || error51.code === "write_outcome_unknown" && error51.writeState !== "unknown" || (error51.code === "request_capacity" ? error51.scope !== "principal" && error51.scope !== "bundle" || error51.writeState !== "not_applied" || error51.resetAt !== void 0 && (typeof error51.resetAt !== "string" || !Number.isFinite(Date.parse(error51.resetAt))) : error51.scope !== void 0 || error51.resetAt !== void 0) || (error51.code === "deletions_held" ? operationId !== DELETE_OPERATION_ID || error51.writeState !== "not_applied" || !count2(error51.deletions) || !count2(error51.baseline) : error51.deletions !== void 0 || error51.baseline !== void 0)) throw refuse();
24222
+ if (raw.data !== void 0 || !isRecord7(error51) || !onlyKeys(error51, ERROR_KEYS) || !WRITE_ERROR_CODES.includes(error51.code) || typeof error51.message !== "string" || error51.retryable !== false || error51.writeState !== "not_applied" && error51.writeState !== "unknown" || error51.currentVersion !== void 0 && !isContentVersion(error51.currentVersion) || error51.code === "write_outcome_unknown" && error51.writeState !== "unknown" || (error51.code === "request_capacity" ? error51.scope !== "principal" && error51.scope !== "bundle" || error51.writeState !== "not_applied" || error51.resetAt !== void 0 && (typeof error51.resetAt !== "string" || !Number.isFinite(Date.parse(error51.resetAt))) : error51.scope !== void 0 || error51.resetAt !== void 0) || (error51.code === "deletions_held" ? operationId !== DELETE_OPERATION_ID || error51.writeState !== "not_applied" || !count2(error51.deletions) || !count2(error51.baseline) : error51.deletions !== void 0 || error51.baseline !== void 0) || // A model-change refusal says nothing was applied; its findings ride on it alone.
24223
+ error51.code === "definition_incompatible" && error51.writeState !== "not_applied" || error51.definitionDetails !== void 0 && error51.code !== "definition_incompatible") throw refuse();
24148
24224
  return { ok: false, operationId, error: { ...error51 } };
24149
24225
  }
24150
24226
  function updateRow(answer2) {
@@ -24234,13 +24310,14 @@ function decodeOutcomeAnswer(value, expected) {
24234
24310
  throw malformed4();
24235
24311
  }
24236
24312
  }
24237
- var CAPACITY_REFUSAL_CODES, DELETIONS_HELD_REFUSAL_CODE, UPDATE_ANSWER_ROWS, OUTCOME_ANSWER_ROWS, READ_ANSWER_ROWS, WRITE_ERROR_CODES, DELETE_OPERATION_ID, HostedAnswerError, isRecord7, onlyKeys, DATA_KEYS, DELETE_DATA_KEYS, ERROR_KEYS, count2, BEFORE_DISPATCH, rowsByAnswer, HostedOutcomeError, OUTCOME_CONTENT_BYTES;
24313
+ var CAPACITY_REFUSAL_CODES, DELETIONS_HELD_REFUSAL_CODE, UPDATE_ANSWER_ROWS, OUTCOME_ANSWER_ROWS, READ_ANSWER_ROWS, WRITE_ERROR_CODES, DELETE_OPERATION_ID, FINDING_IDS_SHOWN, DEFINITION_FINDINGS_TEXT_BYTES, findingText, HostedAnswerError, isRecord7, onlyKeys, DATA_KEYS, DELETE_DATA_KEYS, ERROR_KEYS, count2, BEFORE_DISPATCH, rowsByAnswer, HostedOutcomeError, OUTCOME_CONTENT_BYTES;
24238
24314
  var init_answer_rows = __esm({
24239
24315
  "../core/src/hosted-transport/answer-rows.ts"() {
24240
24316
  "use strict";
24241
24317
  init_define_SUPERBEE_BUILD_IDENTITY();
24242
24318
  init_define_SUPERBEE_UPDATE_POLICY();
24243
24319
  init_version_transport();
24320
+ init_host_text();
24244
24321
  CAPACITY_REFUSAL_CODES = Object.freeze({ principal: "REQUEST_CAPACITY_PRINCIPAL", bundle: "REQUEST_CAPACITY_BUNDLE" });
24245
24322
  DELETIONS_HELD_REFUSAL_CODE = "DELETIONS_HELD";
24246
24323
  UPDATE_ANSWER_ROWS = Object.freeze([
@@ -24261,6 +24338,8 @@ var init_answer_rows = __esm({
24261
24338
  // The remaining not_applied codes of the write schema: definitive once recorded, like the transient ones.
24262
24339
  { answer: "200 result_too_large", recorded: "settled-only", outcome: "refused" },
24263
24340
  { answer: "200 field_action_refused", recorded: "settled-only", outcome: "refused" },
24341
+ // A model change the host's compatibility check refused (designs/hosted-model-evolution.md 4.6).
24342
+ { answer: "200 definition_incompatible", recorded: "settled-only", outcome: "refused" },
24264
24343
  { answer: "200 document_exists", recorded: "settled-only", outcome: "refused" },
24265
24344
  { answer: "200 candidate_unavailable", recorded: "settled-only", outcome: "refused" },
24266
24345
  { answer: "200 candidate_recovery_unavailable", recorded: "settled-only", outcome: "refused" },
@@ -24337,9 +24416,17 @@ var init_answer_rows = __esm({
24337
24416
  "document_id_not_canonical",
24338
24417
  "document_id_collision",
24339
24418
  "request_capacity",
24340
- "deletions_held"
24419
+ "deletions_held",
24420
+ "definition_incompatible"
24341
24421
  ]);
24342
24422
  DELETE_OPERATION_ID = "documents.delete.v1";
24423
+ FINDING_IDS_SHOWN = 5;
24424
+ DEFINITION_FINDINGS_TEXT_BYTES = 2048;
24425
+ findingText = (value) => {
24426
+ if (typeof value !== "string" || value.length > 256) return void 0;
24427
+ const text4 = stripHostText(value, 256);
24428
+ return text4.length > 0 ? text4 : void 0;
24429
+ };
24343
24430
  HostedAnswerError = class extends Error {
24344
24431
  name = "HostedAnswerError";
24345
24432
  };
@@ -24347,7 +24434,7 @@ var init_answer_rows = __esm({
24347
24434
  onlyKeys = (value, allowed) => Object.keys(value).every((key) => allowed.includes(key));
24348
24435
  DATA_KEYS = ["bundleId", "documentId", "version", "changed", "scope"];
24349
24436
  DELETE_DATA_KEYS = [...DATA_KEYS, "deletedVersion", "deleted"];
24350
- ERROR_KEYS = ["code", "message", "retryable", "writeState", "currentVersion", "diagnostics", "fieldActionDetails", "candidate", "retentionUnavailable", "scope", "resetAt", "deletions", "baseline"];
24437
+ ERROR_KEYS = ["code", "message", "retryable", "writeState", "currentVersion", "diagnostics", "fieldActionDetails", "definitionDetails", "candidate", "retentionUnavailable", "scope", "resetAt", "deletions", "baseline"];
24351
24438
  count2 = (value) => Number.isSafeInteger(value) && value >= 1;
24352
24439
  BEFORE_DISPATCH = /* @__PURE__ */ new Map([
24353
24440
  [429, { code: "request_capacity", lenient: true }],
@@ -24594,7 +24681,8 @@ function decodeHostedCapabilities(value) {
24594
24681
  bound: Object.freeze({ documents: bound.documents, bytes: bound.bytes }),
24595
24682
  paged,
24596
24683
  root,
24597
- operationsRetentionMs: retention ?? DEFAULT_OPERATIONS_RETENTION_MS
24684
+ operationsRetentionMs: retention ?? DEFAULT_OPERATIONS_RETENTION_MS,
24685
+ definitionWrites: body.definitionWrites === void 0 ? null : body.definitionWrites === "allowed" ? "allowed" : "refused"
24598
24686
  });
24599
24687
  }
24600
24688
  function boundedLines(body, maximum) {
@@ -25034,7 +25122,9 @@ function createWholeDocumentTransport(options) {
25034
25122
  if (error51.code === "version_conflict" && intent.base === null) return servedHead(intent, "conflict", error51.currentVersion);
25035
25123
  if (error51.code === "version_conflict") return error51.currentVersion === void 0 ? servedHead(intent) : { kind: "conflict", actual: error51.currentVersion };
25036
25124
  const row3 = UPDATE_ANSWER_ROWS.find((candidate) => candidate.answer === `200 ${error51.code}`) ?? updateRow("200 other");
25037
- return row3.code ? denial(row3.code, error51.message) : { kind: "refused", code: error51.code, message: error51.message };
25125
+ if (row3.code) return denial(row3.code, error51.message);
25126
+ const findings = error51.code === "definition_incompatible" ? definitionFindingsText(error51.definitionDetails) : "";
25127
+ return { kind: "refused", code: error51.code, message: findings ? `${error51.message} Findings: ${findings}.` : error51.message };
25038
25128
  }
25039
25129
  function prepare(intent) {
25040
25130
  const request2 = wholeDocumentRequest(bundleId, intent, options.okfVersion);
@@ -30277,9 +30367,17 @@ function qualifyingCarrier(carrier, prefix, slug) {
30277
30367
  if (bundleId.includes("/")) throw new TypeError("a qualified reference sent through a qualifying client");
30278
30368
  return { ...input, bundleId: hostedBundleReferenceText({ slug, bundleId }) };
30279
30369
  };
30370
+ const bytes = carrier.bytes?.bind(carrier);
30280
30371
  return {
30281
30372
  json: async (route, input, signal, options) => carrier.json(route, qualify(route, input), signal, options),
30282
- stream: async (route, input, signal) => carrier.stream(route, qualify(route, input), signal)
30373
+ stream: async (route, input, signal) => carrier.stream(route, qualify(route, input), signal),
30374
+ // A raw body names no bundle in a body; only unscoped routes carry one.
30375
+ ...bytes ? {
30376
+ bytes: async (route, body, signal, options) => {
30377
+ if (scoped(route)) throw new TypeError(`a raw body sent to the bundle-scoped route ${route} through a qualifying client`);
30378
+ return bytes(route, body, signal, options);
30379
+ }
30380
+ } : {}
30283
30381
  };
30284
30382
  }
30285
30383
  function createHostedSyncClient(options) {
@@ -30480,7 +30578,15 @@ var init_client = __esm({
30480
30578
  BUNDLES_BYTES = 256 * 1024;
30481
30579
  AGENT_AUDIENCE_PATH = /^\/agents\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/mcp$/;
30482
30580
  BUNDLE_LIST_CAP = 100;
30483
- UNSCOPED_ROUTES = Object.freeze(["whoami", "bundles", "bundle-create"]);
30581
+ UNSCOPED_ROUTES = Object.freeze([
30582
+ "whoami",
30583
+ "bundles",
30584
+ "bundle-create",
30585
+ "bundle-create-begin",
30586
+ "bundle-create-stage",
30587
+ "bundle-create-blob",
30588
+ "bundle-create-commit"
30589
+ ]);
30484
30590
  }
30485
30591
  });
30486
30592
 
@@ -30488,12 +30594,31 @@ var init_client = __esm({
30488
30594
  import { createHash as createHash10 } from "node:crypto";
30489
30595
  import { promises as fs11 } from "node:fs";
30490
30596
  import path29 from "node:path";
30491
- function heldPathReason(rel) {
30597
+ function underFolded(rel, folder) {
30598
+ const folded = foldedPath(rel);
30599
+ return folded === folder || folded.startsWith(`${folder}/`);
30600
+ }
30601
+ function heldPathReason(rel, options = {}) {
30492
30602
  if (isReservedFile(rel)) return "reserved_file";
30493
30603
  if (!rel.endsWith(".md")) return "not_a_document";
30494
- if (HELD_PREFIXES.some((prefix) => rel.startsWith(prefix))) return "convention_folder";
30604
+ if (HELD_FOLDERS.some((folder) => underFolded(rel, folder))) return "convention_folder";
30605
+ if (underFolded(rel, "conventions") && !(options.definitionWrites === "allowed" && isConventionId(rel))) return "convention_folder";
30495
30606
  return null;
30496
30607
  }
30608
+ function heldTypeReason(rel, type, definitionWrites) {
30609
+ if (type === "View") return `'${conceptIdFromPath(rel)}' is a View registration, which the host installs; it does not sync`;
30610
+ if (type === CONVENTION_TYPE && !(definitionWrites === "allowed" && isConventionId(rel))) return `'${conceptIdFromPath(rel)}' is a Kind convention outside conventions/; the host reads Kinds only from conventions/`;
30611
+ if (definitionWrites === "allowed" && isConventionId(rel) && type !== CONVENTION_TYPE) return `'${conceptIdFromPath(rel)}' is under conventions/ but is not a Convention; only Kind conventions sync there`;
30612
+ return null;
30613
+ }
30614
+ function heldPathMessage(rel, definitionWrites = null, sender = "sync") {
30615
+ if (underFolded(rel, "views-registry")) return `${rel} is under views-registry/, which holds the bundle's View registrations; they are installed on the host and do not sync`;
30616
+ if (underFolded(rel, "conventions") && definitionWrites === "refused") return `${rel} is under conventions/, which holds the bundle's model: ${DEFINITIONS_REFUSED.why}; ${DEFINITIONS_REFUSED.instead}`;
30617
+ if (underFolded(rel, "conventions") && definitionWrites === "allowed") {
30618
+ return sender === "app" ? `${rel} is under conventions/, which holds the bundle's model; change it with the kind and recipe commands` : `${rel} is not under conventions/ as spelled; the bundle's Kinds sync only from the folder named exactly conventions/`;
30619
+ }
30620
+ return `${rel} is under ${rel.split("/")[0]}/, which holds conventions edited in the Superbee app`;
30621
+ }
30497
30622
  async function readDeletionWindow(store) {
30498
30623
  const raw = await store.readMeta(DELETION_WINDOW_KEY);
30499
30624
  const hold = raw?.hold && Array.isArray(raw.hold.ids) && typeof raw.hold.token === "string" ? { ids: raw.hold.ids.filter((id) => typeof id === "string"), token: raw.hold.token } : null;
@@ -30700,7 +30825,10 @@ function foldedPath(id) {
30700
30825
  function held(id, rel, reason, message) {
30701
30826
  return { id, path: rel, reason, message };
30702
30827
  }
30703
- function unsendable(id, rel, bytes, stored, context) {
30828
+ function unsendable(id, rel, bytes, stored, context, fence) {
30829
+ if (fence && heldPathReason(rel, { definitionWrites: fence.definitionWrites }) === "convention_folder") {
30830
+ return held(id, rel, "convention_folder", heldPathMessage(rel, fence.definitionWrites, fence.sender));
30831
+ }
30704
30832
  if (bytes.byteLength > WHOLE_DOCUMENT_BOUNDS.payloadBytes) {
30705
30833
  return held(id, rel, "too_large", `'${id}' is over the ${WHOLE_DOCUMENT_BOUNDS.payloadBytes / 1024} KiB a sync write carries`);
30706
30834
  }
@@ -30721,6 +30849,8 @@ function unsendable(id, rel, bytes, stored, context) {
30721
30849
  if (Buffer.byteLength(JSON.stringify(frontmatter)) > FRONTMATTER_JSON_BYTES || Object.keys(frontmatter).length > FRONTMATTER_KEY_LIMIT) {
30722
30850
  return held(id, rel, "too_large", `'${id}' has more frontmatter than the host accepts (${FRONTMATTER_JSON_BYTES / 1024} KiB, ${FRONTMATTER_KEY_LIMIT} fields)`);
30723
30851
  }
30852
+ const typed = fence ? heldTypeReason(rel, frontmatter.type, fence.definitionWrites) : null;
30853
+ if (typed !== null) return held(id, rel, "not_sendable", typed);
30724
30854
  const storedType = stored?.frontmatter.type;
30725
30855
  if (typeof storedType === "string" && storedType !== frontmatter.type) {
30726
30856
  return held(id, rel, "type_change", `'${id}' changes type from '${storedType}' to '${String(frontmatter.type)}', which sync cannot send`);
@@ -30783,11 +30913,7 @@ async function scanCheckout(context) {
30783
30913
  continue;
30784
30914
  }
30785
30915
  }
30786
- if (heldPathReason(rel) === "convention_folder") {
30787
- report.held.push(held(id, rel, "convention_folder", `${rel} is under ${rel.split("/")[0]}/, which holds conventions edited in the Superbee app`));
30788
- continue;
30789
- }
30790
- const refusal = unsendable(id, rel, bytes, stored.document?.doc ?? null, context);
30916
+ const refusal = unsendable(id, rel, bytes, stored.document?.doc ?? null, context, { definitionWrites: context.definitionWrites ?? null });
30791
30917
  if (refusal) {
30792
30918
  report.held.push(refusal);
30793
30919
  continue;
@@ -30824,6 +30950,14 @@ async function scanCheckout(context) {
30824
30950
  delete projection.files[id];
30825
30951
  continue;
30826
30952
  }
30953
+ const definitionWrites = context.definitionWrites ?? null;
30954
+ const refusedPath = heldPathReason(rel, { definitionWrites }) === "convention_folder";
30955
+ const refusedType = refusedPath ? null : heldTypeReason(rel, stored.document.doc.frontmatter.type, definitionWrites);
30956
+ if (refusedPath || refusedType !== null) {
30957
+ delete projection.files[id];
30958
+ report.held.push(held(id, rel, refusedPath ? "convention_folder" : "not_sendable", `${refusedPath ? heldPathMessage(rel, definitionWrites) : refusedType}; the host's version is placed back in the folder`));
30959
+ continue;
30960
+ }
30827
30961
  if (stored.intents.some((row3) => row3.state === "conflict")) {
30828
30962
  report.held.push(held(id, rel, "deleted_locally", `${rel} was deleted while '${id}' has a conflict to resolve; resolve it first`));
30829
30963
  continue;
@@ -31114,7 +31248,7 @@ async function recoverPlacements(folder, projection) {
31114
31248
  }
31115
31249
  delete projection.discarded;
31116
31250
  }
31117
- var PROJECTION_FILE, PROJECTION_SCHEMA, PROJECTION_BYTES, FRONTMATTER_JSON_BYTES, HELD_PREFIXES, MIN_HELD_DELETIONS, DELETION_WINDOW_MS, DELETION_WINDOW_KEY, UTF8;
31251
+ var PROJECTION_FILE, PROJECTION_SCHEMA, PROJECTION_BYTES, FRONTMATTER_JSON_BYTES, HELD_FOLDERS, DEFINITIONS_REFUSED, MIN_HELD_DELETIONS, DELETION_WINDOW_MS, DELETION_WINDOW_KEY, UTF8;
31118
31252
  var init_sync_scan = __esm({
31119
31253
  "../cli/src/hosted/sync-scan.ts"() {
31120
31254
  "use strict";
@@ -31132,7 +31266,11 @@ var init_sync_scan = __esm({
31132
31266
  PROJECTION_SCHEMA = 2;
31133
31267
  PROJECTION_BYTES = 8 * 1024 * 1024;
31134
31268
  FRONTMATTER_JSON_BYTES = 16 * 1024;
31135
- HELD_PREFIXES = ["conventions/", "views/"];
31269
+ HELD_FOLDERS = ["views", "views-registry"];
31270
+ DEFINITIONS_REFUSED = Object.freeze({
31271
+ why: "you don't have permission to change this bundle's model",
31272
+ instead: "ask whoever manages access to it"
31273
+ });
31136
31274
  MIN_HELD_DELETIONS = 3;
31137
31275
  DELETION_WINDOW_MS = 24 * 60 * 60 * 1e3;
31138
31276
  DELETION_WINDOW_KEY = "cli-deletion-window";
@@ -31196,6 +31334,13 @@ function hostedCheckoutRefusal(row3, words, binding, extra = {}) {
31196
31334
  help: "choose another folder for a local bundle"
31197
31335
  });
31198
31336
  }
31337
+ if (row3.definitions && binding.definition_writes === "refused") {
31338
+ const { why, instead } = DEFINITIONS_REFUSED;
31339
+ return new CliError("FORBIDDEN", `'${words}' refused: ${why} (bundle ${binding.bundle_id} on ${binding.origin}); ${instead}`, {
31340
+ details: { ...details, reason: "definitions_refused" },
31341
+ help: instead
31342
+ });
31343
+ }
31199
31344
  if (row3.instead) {
31200
31345
  return new CliError("FORBIDDEN", `'${words}' cannot sync from a hosted checkout (${row3.why}): ${row3.instead}`, { details, help: row3.instead });
31201
31346
  }
@@ -31211,9 +31356,23 @@ async function assertAllowedInHostedCheckout(command, args, context = {}) {
31211
31356
  if (skip) return;
31212
31357
  const binding = await checkoutFor(command, dir, context.home ?? homedir12(), context.cwd ?? process.cwd());
31213
31358
  if (!binding) return;
31359
+ if (row3.definitions && binding.definition_writes === "allowed") return;
31214
31360
  const sub = row3.words.length > 1 ? args.find((token) => !token.startsWith("-")) : void 0;
31215
31361
  throw hostedCheckoutRefusal(row3, sub ? `${command} ${sub}` : command, binding);
31216
31362
  }
31363
+ async function assertKindOnlyRecipe(words, dir, recipe2, context = {}) {
31364
+ const artifacts = [...recipe2.docs.map((doc2) => doc2.id), ...recipe2.pages.flatMap((page) => [page.registry.id, page.entry]), ...recipe2.references.map((reference) => reference.doc.id)];
31365
+ const outside = artifacts.filter((id) => !isConventionId(id));
31366
+ if (outside.length === 0) return;
31367
+ const binding = await checkoutFor("recipe", dir, context.home ?? homedir12(), context.cwd ?? process.cwd());
31368
+ if (!binding) return;
31369
+ throw hostedCheckoutRefusal(
31370
+ { words: words.split(" "), reason: "not_syncable", why: `recipe '${recipe2.id}' installs ${outside.length} artifact(s) outside conventions/, such as Views and References, and a checkout sends Kind conventions only`, instead: KINDS_INSTEAD },
31371
+ words,
31372
+ binding,
31373
+ { recipe: recipe2.id, artifacts: outside.slice(0, 20).join(", "), artifacts_total: String(outside.length) }
31374
+ );
31375
+ }
31217
31376
  function hostedHeldWriteRefusal(binding, held2) {
31218
31377
  const instead = HELD_INSTEAD[held2.reason];
31219
31378
  return hostedCheckoutRefusal(
@@ -31286,6 +31445,7 @@ var init_refusals = __esm({
31286
31445
  "use strict";
31287
31446
  init_define_SUPERBEE_BUILD_IDENTITY();
31288
31447
  init_define_SUPERBEE_UPDATE_POLICY();
31448
+ init_src();
31289
31449
  init_bundle2();
31290
31450
  init_errors4();
31291
31451
  init_command_text();
@@ -31297,20 +31457,20 @@ var init_refusals = __esm({
31297
31457
  KINDS_INSTEAD = "to design Kinds, work in a local or Git bundle and publish it";
31298
31458
  HOSTED_CHECKOUT_REFUSALS = Object.freeze([
31299
31459
  { words: ["doc", "verify"], reason: "not_syncable", why: "verification is a managed field the host records" },
31300
- { words: ["kind", "*"], reason: "not_syncable", why: "a hosted bundle's Kinds cannot be changed from a checkout", instead: KINDS_INSTEAD },
31301
- { words: ["recipe", "add"], reason: "not_syncable", why: "recipes change a hosted bundle's Kinds, which cannot be changed from a checkout", instead: KINDS_INSTEAD },
31302
- { words: ["recipe", "evolve"], reason: "not_syncable", why: "recipes change a hosted bundle's Kinds, which cannot be changed from a checkout", instead: KINDS_INSTEAD },
31460
+ { words: ["kind", "*"], reason: "not_syncable", why: "a hosted bundle's Kinds cannot be changed from a checkout", instead: KINDS_INSTEAD, definitions: true },
31461
+ { words: ["recipe", "add"], reason: "not_syncable", why: "recipes change a hosted bundle's Kinds, which cannot be changed from a checkout", instead: KINDS_INSTEAD, definitions: true },
31462
+ { words: ["recipe", "evolve"], reason: "not_syncable", why: "recipes change a hosted bundle's Kinds, which cannot be changed from a checkout", instead: KINDS_INSTEAD, definitions: true },
31303
31463
  { words: ["artifact", "*"], reason: "not_syncable", why: "artifacts carry blobs, which do not sync" },
31304
31464
  {
31305
31465
  words: ["promote"],
31306
31466
  reason: "not_syncable",
31307
- why: "a key that is not a .md document is stored as a blob, and blobs, reserved files and conventions do not sync",
31467
+ why: "a key that is not a .md document is stored as a blob; blobs, reserved files and View files do not sync, and conventions change only through the kind and recipe commands",
31308
31468
  when: (args) => heldKey(docKey(args) ?? "")
31309
31469
  },
31310
31470
  {
31311
31471
  words: ["delete"],
31312
31472
  reason: "not_syncable",
31313
- why: "a key that is not a .md document is a blob, and blobs, reserved files and conventions do not sync (delete a document with doc delete)",
31473
+ why: "a key that is not a .md document is a blob; blobs, reserved files and View files do not sync, and conventions change only through the kind and recipe commands (delete a document with doc delete)",
31314
31474
  when: (args) => {
31315
31475
  const key = docKey(args);
31316
31476
  return key !== void 0 && heldKey(key);
@@ -31324,7 +31484,7 @@ var init_refusals = __esm({
31324
31484
  when: (args) => !args.some((token) => token === "--check" || token.startsWith("--check="))
31325
31485
  },
31326
31486
  { words: ["serve"], reason: "not_syncable", why: "the served bundle accepts writes and deletes that do not sync" },
31327
- { words: ["ui"], reason: "not_syncable", why: "the local app writes Views and conventions, which do not sync" },
31487
+ { words: ["ui"], reason: "not_syncable", why: "the local app writes Views, which do not sync, and conventions outside the kind and recipe commands" },
31328
31488
  { words: ["init"], reason: "checkout_target", why: "the folder is a hosted checkout, not a local bundle" }
31329
31489
  ].map((row3) => Object.freeze({ ...row3, words: Object.freeze([...row3.words]) })));
31330
31490
  HELD_INSTEAD = {
@@ -34519,6 +34679,7 @@ function refusalRow(id, row3, accessWithdrawn = false) {
34519
34679
  if (SIGN_IN_CODES.has(code3) && accessWithdrawn) return { id, state: "refused", reason: "access_withdrawn", version: null, message: ACCESS_WITHDRAWN_MESSAGE };
34520
34680
  if (SIGN_IN_CODES.has(code3)) return { id, state: "paused", reason: "sign_in", version: null, message: "The hosted session ended before this change was sent; sign in and run sync again." };
34521
34681
  if (BUSY_REFUSAL_CODES.has(code3)) return { id, state: "paused", reason: "busy", version: null, message: "The host was busy and did not apply this change; run sync again." };
34682
+ if (code3 === "definition_incompatible") return { id, state: "refused", reason: code3, version: null, message: `The host refused this model change (${code3}): ${message} Fix the documents it names (the sync that sends them sends this change again), or edit the Kind and run sync again.` };
34522
34683
  return { id, state: "refused", reason: code3, version: null, message: `The host refused this document (${code3}): ${message} Edit the file and run sync again.` };
34523
34684
  }
34524
34685
  function waitingRow(id, pausedBy, notSent) {
@@ -34858,13 +35019,14 @@ async function withSession(binding, deps, resumeCommand, body, options = {}) {
34858
35019
  } catch (error51) {
34859
35020
  throw await readFailure(error51, { binding, target: target2, client }, resumeCommand, await unsent());
34860
35021
  }
35022
+ const bound = await recordDefinitionWrites(deps.auth.home, binding, capabilities.definitionWrites);
34861
35023
  projection = await readProjection(deps.auth.home, binding.checkout_id, store);
34862
35024
  await recoverPlacements(binding.path, projection);
34863
35025
  const record5 = projection;
34864
35026
  const persist = () => writeProjection(deps.auth.home, binding.checkout_id, record5);
34865
35027
  await persist();
34866
35028
  return await body({
34867
- binding,
35029
+ binding: bound,
34868
35030
  target: target2,
34869
35031
  reader,
34870
35032
  capabilities,
@@ -34889,18 +35051,23 @@ async function withSession(binding, deps, resumeCommand, body, options = {}) {
34889
35051
  throw lockFailure(error51, binding.path);
34890
35052
  });
34891
35053
  }
34892
- function createsFirst(store, blocked) {
35054
+ function createsFirst(store, blocked, models) {
34893
35055
  return new Proxy(store, {
34894
35056
  get(inner, prop) {
34895
35057
  if (prop === "listIntents") {
34896
35058
  return async (state) => {
34897
- const rows = await inner.listIntents(state);
35059
+ const rows = (await inner.listIntents(state)).filter(
35060
+ (row3) => !(state === "pending" && (row3.base === null && row3.kind !== DOCUMENT_DELETE_KIND && blocked.has(row3.target) || !models && isConventionId(row3.target)))
35061
+ );
34898
35062
  if (state !== "pending") return rows;
34899
35063
  const deleting = (row3) => row3.kind === DOCUMENT_DELETE_KIND;
35064
+ const model = (row3) => isConventionId(row3.target);
34900
35065
  return [
34901
- ...rows.filter((row3) => row3.base === null && !deleting(row3) && !blocked.has(row3.target)),
34902
- ...rows.filter((row3) => row3.base !== null && !deleting(row3)),
34903
- ...rows.filter(deleting)
35066
+ ...rows.filter((row3) => model(row3) && !deleting(row3)),
35067
+ ...rows.filter((row3) => !model(row3) && row3.base === null && !deleting(row3)),
35068
+ ...rows.filter((row3) => !model(row3) && row3.base !== null && !deleting(row3)),
35069
+ ...rows.filter((row3) => !model(row3) && deleting(row3)),
35070
+ ...rows.filter((row3) => model(row3) && deleting(row3))
34904
35071
  ];
34905
35072
  };
34906
35073
  }
@@ -34909,18 +35076,18 @@ function createsFirst(store, blocked) {
34909
35076
  }
34910
35077
  });
34911
35078
  }
34912
- async function requeueBusy(store) {
34913
- let requeued = 0;
35079
+ async function requeueRefused(store, eligible) {
35080
+ const requeued = [];
34914
35081
  const unsettled = await store.listIntents(UNSETTLED_STATES);
34915
35082
  for (const row3 of unsettled) {
34916
- if (row3.state !== "refused" || !row3.refusal || !BUSY_REFUSAL_CODES.has(row3.refusal.code)) continue;
35083
+ if (row3.state !== "refused" || !row3.refusal || !eligible(row3)) continue;
34917
35084
  if (unsettled.some((other) => other.after === row3.requestId)) continue;
34918
35085
  const current = await store.readWithJournal(row3.target);
34919
35086
  const latest = current.intents.filter((intent) => intent.state !== "acknowledged").at(-1);
34920
35087
  if (latest?.requestId !== row3.requestId) continue;
34921
35088
  const supersede = { requestId: row3.requestId, expectedState: "refused", expectedAttempts: row3.attempts };
34922
35089
  if (row3.kind === DOCUMENT_DELETE_KIND) {
34923
- if (await supersedeDeletion(store, row3)) requeued += 1;
35090
+ if (await supersedeDeletion(store, row3)) requeued.push(row3.target);
34924
35091
  continue;
34925
35092
  }
34926
35093
  if (!current.document) continue;
@@ -34938,10 +35105,37 @@ async function requeueBusy(store) {
34938
35105
  },
34939
35106
  supersede
34940
35107
  });
34941
- requeued += 1;
35108
+ requeued.push(row3.target);
34942
35109
  }
34943
35110
  return requeued;
34944
35111
  }
35112
+ function intentFrontmatter(row3, okfVersion) {
35113
+ const text4 = row3.kind === DOCUMENT_DELETE_KIND ? row3.baseContent : row3.content;
35114
+ if (!text4) return null;
35115
+ try {
35116
+ return parseMarkdown(text4, row3.target, { okfVersion }).frontmatter;
35117
+ } catch {
35118
+ return null;
35119
+ }
35120
+ }
35121
+ function recordLanded(landed, row3, okfVersion) {
35122
+ const frontmatter = intentFrontmatter(row3, okfVersion);
35123
+ if (isConventionId(row3.target)) {
35124
+ if (typeof frontmatter?.governs === "string") landed.governed.add(frontmatter.governs);
35125
+ landed.conventionChanged = true;
35126
+ } else if (typeof frontmatter?.type === "string") landed.types.add(frontmatter.type);
35127
+ }
35128
+ function definitionRetry(row3, landed, okfVersion, models) {
35129
+ const code3 = row3.refusal?.code;
35130
+ if (isConventionId(row3.target)) {
35131
+ if (!models || code3 !== "definition_incompatible") return false;
35132
+ const governs = intentFrontmatter(row3, okfVersion)?.governs;
35133
+ return landed.conventionChanged || typeof governs === "string" && landed.types.has(governs);
35134
+ }
35135
+ if (code3 !== "validation_failed") return false;
35136
+ const type = intentFrontmatter(row3, okfVersion)?.type;
35137
+ return typeof type === "string" && landed.governed.has(type);
35138
+ }
34945
35139
  async function caseCollidingCreates(store) {
34946
35140
  const creates = (await store.listIntents("pending")).filter((row3) => row3.base === null);
34947
35141
  if (creates.length === 0) return [];
@@ -34996,13 +35190,23 @@ async function pushChanges(session, deps, acceptDeletes, heldSink = {}) {
34996
35190
  heldSink.value ??= { deletions, baseline };
34997
35191
  }
34998
35192
  });
34999
- const ordered = createsFirst(store, new Set(collisions.map((row3) => row3.id)));
35193
+ const models = session.capabilities.definitionWrites === "allowed";
35194
+ const withheld = models ? [] : [...new Set((await store.listIntents("pending")).filter((row3) => isConventionId(row3.target)).map((row3) => row3.target))].map((id) => ({
35195
+ id,
35196
+ path: `${id}.md`,
35197
+ reason: "convention_folder",
35198
+ message: heldPathMessage(`${id}.md`, session.capabilities.definitionWrites)
35199
+ }));
35200
+ const ordered = createsFirst(store, new Set(collisions.map((row3) => row3.id)), models);
35000
35201
  let signInRequired = false;
35202
+ const landed = { types: /* @__PURE__ */ new Set(), governed: /* @__PURE__ */ new Set(), conventionChanged: false };
35203
+ const retried = /* @__PURE__ */ new Set();
35001
35204
  for (let pass = 0; pass < PUSH_PASSES; pass += 1) {
35002
35205
  const report = await push2(ordered, transport, { remote: reader, write: { ...deps.write, settlement: WHOLE_DOCUMENT_SETTLEMENT } });
35003
35206
  for (const settled of report.settled) {
35004
35207
  if (settled.state !== "acknowledged") continue;
35005
35208
  const row3 = await store.readIntent(settled.requestId);
35209
+ if (row3) recordLanded(landed, row3, session.okfVersion);
35006
35210
  acknowledged.set(settled.target, row3?.acknowledgedVersion === DELETION_VERSION ? "" : row3?.acknowledgedVersion ?? "");
35007
35211
  if (row3?.kind === DOCUMENT_DELETE_KIND) {
35008
35212
  deleted.add(settled.target);
@@ -35014,10 +35218,19 @@ async function pushChanges(session, deps, acceptDeletes, heldSink = {}) {
35014
35218
  signInRequired = /^(AUTH_REQUIRED|UNAUTHORIZED)\b/.test(control?.reason ?? "") && !denied;
35015
35219
  break;
35016
35220
  }
35017
- if (pass === PUSH_PASSES - 1 || await requeueBusy(store) === 0) break;
35221
+ if (pass === PUSH_PASSES - 1) break;
35222
+ const model = /* @__PURE__ */ new Set();
35223
+ const requeued = await requeueRefused(store, (row3) => {
35224
+ if (busyRefusal(row3)) return true;
35225
+ if (retried.has(row3.target) || !definitionRetry(row3, landed, session.okfVersion, models)) return false;
35226
+ model.add(row3.target);
35227
+ return true;
35228
+ });
35229
+ if (requeued.length === 0) break;
35230
+ for (const id of requeued) if (model.has(id)) retried.add(id);
35018
35231
  await (deps.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))))(50 + Math.floor(Math.random() * 200));
35019
35232
  }
35020
- return { acknowledged, deleted, deletedIntents, signInRequired, accessWithdrawn: denied, notSent: null, collisions, via };
35233
+ return { acknowledged, deleted, deletedIntents, signInRequired, accessWithdrawn: denied, notSent: null, collisions: [...collisions, ...withheld], via };
35021
35234
  }
35022
35235
  async function forgetPullDigest(store) {
35023
35236
  const marker = await store.readMeta("pull");
@@ -35109,6 +35322,7 @@ async function runSync(binding, values, deps, mode) {
35109
35322
  okfVersion: session.okfVersion,
35110
35323
  local,
35111
35324
  projection,
35325
+ definitionWrites: session.capabilities.definitionWrites,
35112
35326
  ...acceptDeletes !== void 0 ? { acceptDeletes, confirmAccept: confirmAtTerminal(binding, terminal) } : {}
35113
35327
  });
35114
35328
  await session.persist();
@@ -35256,7 +35470,7 @@ async function pullOnly(binding, session, deps, resumeCommand) {
35256
35470
  {
35257
35471
  const { store, local, reader, projection } = session;
35258
35472
  await reclaimInFlight(local);
35259
- await scanCheckout({ folder: binding.path, bundleId: binding.bundle_id, okfVersion: session.okfVersion, local, projection });
35473
+ await scanCheckout({ folder: binding.path, bundleId: binding.bundle_id, okfVersion: session.okfVersion, local, projection, definitionWrites: session.capabilities.definitionWrites });
35260
35474
  await session.persist();
35261
35475
  let report;
35262
35476
  try {
@@ -35499,7 +35713,7 @@ async function runInspect2(binding, values, deps, mode) {
35499
35713
  }
35500
35714
  async function assertSendable(session, id, file2, bytes, resumeCommand) {
35501
35715
  const stored = await session.store.readWithJournal(id);
35502
- const held2 = unsendable(id, `${id}.md`, bytes, stored.document?.doc ?? null, { bundleId: session.binding.bundle_id, okfVersion: session.okfVersion });
35716
+ const held2 = unsendable(id, `${id}.md`, bytes, stored.document?.doc ?? null, { bundleId: session.binding.bundle_id, okfVersion: session.okfVersion }, { definitionWrites: session.capabilities.definitionWrites });
35503
35717
  if (held2) throw new CliError("CONFLICT", `${file2} cannot be sent: ${held2.message}`, { details: { reason: held2.reason, id, file: file2 }, help: `edit ${file2}, then re-run: ${resumeCommand}` });
35504
35718
  }
35505
35719
  async function resolveFolder(session, id, choice, conflict, resumeCommand) {
@@ -35691,7 +35905,7 @@ async function hostedSync(argv2, binding, partial2 = {}) {
35691
35905
  if (values["restore-deletes"]) return runRestore(binding, deps, mode);
35692
35906
  return runSync(binding, values, deps, mode);
35693
35907
  }
35694
- var HOSTED_SYNC_USAGE, DEFAULT_ROW_LIMIT, INSPECT_PREVIEW_CHARS2, PUSH_PASSES, GIT_ONLY_FLAGS;
35908
+ var HOSTED_SYNC_USAGE, DEFAULT_ROW_LIMIT, INSPECT_PREVIEW_CHARS2, PUSH_PASSES, GIT_ONLY_FLAGS, busyRefusal;
35695
35909
  var init_sync2 = __esm({
35696
35910
  "../cli/src/hosted/sync.ts"() {
35697
35911
  "use strict";
@@ -35783,6 +35997,7 @@ unless SUPERBEE_VIA says otherwise. SUPERBEE_ACTOR does not set it.
35783
35997
  INSPECT_PREVIEW_CHARS2 = 4e3;
35784
35998
  PUSH_PASSES = 3;
35785
35999
  GIT_ONLY_FLAGS = ["establish", "pull-only", "show-incoming", "yes", "body-out", "migrate"];
36000
+ busyRefusal = (row3) => row3.refusal !== void 0 && BUSY_REFUSAL_CODES.has(row3.refusal.code);
35786
36001
  }
35787
36002
  });
35788
36003
 
@@ -84039,6 +84254,7 @@ async function recipeEvolve(argv2, stdout) {
84039
84254
  throw new CliError("USAGE", loaded.error.message, { help: `${cliInvocation()} recipes` });
84040
84255
  }
84041
84256
  const remote = await resolveRemoteFlag(values.remote, values.dir);
84257
+ if (remote === void 0) await assertKindOnlyRecipe("recipe evolve", values.dir, loaded.recipe);
84042
84258
  const bundle = await openBundle(values.dir, remote);
84043
84259
  const target2 = values.dir !== void 0 ? commandFragment` --dir ${commandQuoted(values.dir)}` : remote !== void 0 ? commandFragment` --remote ${commandQuoted(remote)}` : commandLiteral("");
84044
84260
  const planCommand = `${cliInvocation()} recipe evolve ${commandQuoted(ref)}${target2}`;
@@ -84094,6 +84310,7 @@ async function recipeAdd(argv2, stdout) {
84094
84310
  throw new CliError("USAGE", loaded.error.message, { help: `${cliInvocation()} recipes` });
84095
84311
  }
84096
84312
  const remote = await resolveRemoteFlag(values.remote, values.dir);
84313
+ if (remote === void 0) await assertKindOnlyRecipe("recipe add", values.dir, loaded.recipe);
84097
84314
  const bundle = await openBundle(values.dir, remote);
84098
84315
  const result3 = await applyRecipe(bundle, loaded.recipe);
84099
84316
  const registry2 = await loadKinds(bundle);
@@ -84140,6 +84357,7 @@ var init_recipe = __esm({
84140
84357
  init_recipes();
84141
84358
  init_recipe_evolution();
84142
84359
  init_recipe_source();
84360
+ init_refusals();
84143
84361
  init_command_text();
84144
84362
  RECIPE_USAGE = `superbee recipe \u2014 apply a recipe to this bundle
84145
84363
 
@@ -84220,7 +84438,9 @@ async function classifyCheckout(binding, home2, store) {
84220
84438
  okfVersion: await storeOkfVersion(store),
84221
84439
  local: openLocalBundle(binding.checkout_id, { backend: store }),
84222
84440
  projection: await readProjection(home2, binding.checkout_id, store),
84223
- preview: true
84441
+ preview: true,
84442
+ // What the host said at the last sync: a preview makes no request.
84443
+ definitionWrites: binding.definition_writes ?? null
84224
84444
  });
84225
84445
  for (const id of report.pending) result3.unsent.add(id);
84226
84446
  for (const id of report.conflicted) result3.conflicts.add(id);
@@ -119715,7 +119935,7 @@ function checkoutGuard(binding, home2, folder, options) {
119715
119935
  }
119716
119936
  const rel = pathFromConceptId(id);
119717
119937
  const reason = heldPathReason(rel);
119718
- if (reason === "convention_folder") refuse({ id, path: rel, reason, message: `${rel} is under ${rel.split("/")[0]}/, which holds conventions edited in the Superbee app` });
119938
+ if (reason === "convention_folder") refuse({ id, path: rel, reason, message: heldPathMessage(rel, binding.definition_writes ?? null, "app") });
119719
119939
  if (reason !== null) refuse({ id, path: rel, reason, message: `${rel} is a reserved OKF file, which sync does not send` });
119720
119940
  return rel;
119721
119941
  };
@@ -119738,7 +119958,7 @@ function checkoutGuard(binding, home2, folder, options) {
119738
119958
  const rel = documentPath(id);
119739
119959
  const bytes = Buffer.from(stringifyDoc(doc2.frontmatter, doc2.body ?? ""), "utf8");
119740
119960
  const stored = await storedDocument(binding, home2, folder, id);
119741
- const held2 = unsendable(id, rel, bytes, stored.frontmatter ? { frontmatter: stored.frontmatter } : null, { bundleId: binding.bundle_id, okfVersion: stored.okfVersion });
119961
+ const held2 = unsendable(id, rel, bytes, stored.frontmatter ? { frontmatter: stored.frontmatter } : null, { bundleId: binding.bundle_id, okfVersion: stored.okfVersion }, { definitionWrites: null, sender: "app" });
119742
119962
  if (held2) refuse(held2);
119743
119963
  const current = await folder.read(id).then((read) => read, () => null);
119744
119964
  const expected = writeOptions?.expectedVersion;
@@ -119755,7 +119975,7 @@ function checkoutGuard(binding, home2, folder, options) {
119755
119975
  case "deleteBlob": {
119756
119976
  const key = String(args[0]);
119757
119977
  const reason = heldPathReason(key) ?? "not_a_document";
119758
- const message = reason === "convention_folder" ? `${key} is under ${key.split("/")[0]}/, which holds conventions edited in the Superbee app` : reason === "reserved_file" ? `${key} is a reserved OKF file, which sync does not send` : `${key} is not a .md document; files other than documents do not sync`;
119978
+ const message = reason === "convention_folder" ? heldPathMessage(key, binding.definition_writes ?? null, "app") : reason === "reserved_file" ? `${key} is a reserved OKF file, which sync does not send` : `${key} is not a .md document; files other than documents do not sync`;
119759
119979
  return refuse({ id: key, path: key, reason, message });
119760
119980
  }
119761
119981
  default:
@@ -128609,7 +128829,7 @@ Options:
128609
128829
 
128610
128830
  // ../cli/src/hosted/publish-state.ts
128611
128831
  import { createHash as createHash20 } from "node:crypto";
128612
- import { unlink as unlink10 } from "node:fs/promises";
128832
+ import { readdir as readdir6, unlink as unlink10 } from "node:fs/promises";
128613
128833
  import path45 from "node:path";
128614
128834
  function recordDir(home2) {
128615
128835
  return path45.join(userStateDir(home2), "hosted-publish");
@@ -128639,6 +128859,23 @@ async function writePendingCreate(home2, canonical2, pending) {
128639
128859
  await writeUserStateFileAtomic0600(home2, recordDir(home2), recordName2(canonical2, pendingSuffix(pending.bundle_id)), `${JSON.stringify(pending)}
128640
128860
  `);
128641
128861
  }
128862
+ async function listPendingCreates(home2) {
128863
+ let names;
128864
+ try {
128865
+ names = await readdir6(recordDir(home2));
128866
+ } catch {
128867
+ return [];
128868
+ }
128869
+ const records = [];
128870
+ for (const name of names.filter((entry) => /^[0-9a-f]{64}-[0-9a-f]{64}\.json$/.test(entry))) {
128871
+ try {
128872
+ const value = JSON.parse(await readUserStateFile(home2, path45.join(recordDir(home2), name), 1024 * 1024));
128873
+ if (typeof value.request_id === "string" && typeof value.bundle_id === "string" && typeof value.host === "string") records.push(value);
128874
+ } catch {
128875
+ }
128876
+ }
128877
+ return records;
128878
+ }
128642
128879
  function clearPendingCreate(home2, canonical2, bundleId) {
128643
128880
  return clearRecord(home2, canonical2, pendingSuffix(bundleId));
128644
128881
  }
@@ -128912,7 +129149,7 @@ async function rebindToWorkspace(canonical2, bound, options, deps, mode) {
128912
129149
  }
128913
129150
  async function bindFolderInPlace(input) {
128914
129151
  const { canonical: canonical2, target: target2, bundleId, deps, resume: resume2 } = input;
128915
- const { identity, reader, listed: isListed, workspace, reference } = input.connection;
129152
+ const { identity, reader, listed: isListed, workspace, reference, definitionWrites } = input.connection;
128916
129153
  const home2 = deps.auth.home;
128917
129154
  const placedFiles = /* @__PURE__ */ new Map();
128918
129155
  return withCheckoutLock(canonical2, async () => {
@@ -128921,7 +129158,7 @@ async function bindFolderInPlace(input) {
128921
129158
  }
128922
129159
  const identityNow = await folderIdentity(canonical2);
128923
129160
  if (!identityNow) throw new CliError("RUNTIME", `${canonical2} disappeared while binding it`, { help: "retry the same command" });
128924
- const binding = {
129161
+ const binding = withDefinitionWrites({
128925
129162
  schema: 1,
128926
129163
  checkout_id: newCheckoutId(),
128927
129164
  path: canonical2,
@@ -128936,7 +129173,7 @@ async function bindFolderInPlace(input) {
128936
129173
  created_at: new Date(deps.auth.now()).toISOString(),
128937
129174
  state: "hydrating",
128938
129175
  folder_identity: identityNow
128939
- };
129176
+ }, definitionWrites);
128940
129177
  await writeBinding(home2, binding);
128941
129178
  let store;
128942
129179
  try {
@@ -129061,7 +129298,7 @@ var init_checkout_adopt = __esm({
129061
129298
 
129062
129299
  // ../cli/src/commands/checkout.ts
129063
129300
  import { homedir as homedir37 } from "node:os";
129064
- import { lstat as lstat9, mkdir as mkdir4, readFile as readFile5, readdir as readdir6, realpath as realpath11, rmdir as rmdir3, stat as stat8, unlink as unlink11 } from "node:fs/promises";
129301
+ import { lstat as lstat9, mkdir as mkdir4, readFile as readFile5, readdir as readdir7, realpath as realpath11, rmdir as rmdir3, stat as stat8, unlink as unlink11 } from "node:fs/promises";
129065
129302
  import path47 from "node:path";
129066
129303
  import { parseArgs as parseArgs39 } from "node:util";
129067
129304
  function checkoutDeps(partial2) {
@@ -129081,7 +129318,7 @@ async function entryKind(target2) {
129081
129318
  throw error51;
129082
129319
  }
129083
129320
  if (!info.isDirectory()) return "other";
129084
- return (await readdir6(target2)).length === 0 ? "empty-dir" : "dir";
129321
+ return (await readdir7(target2)).length === 0 ? "empty-dir" : "dir";
129085
129322
  }
129086
129323
  function bindingView(binding) {
129087
129324
  return {
@@ -129321,7 +129558,7 @@ async function checkout(argv2, partial2 = {}) {
129321
129558
  });
129322
129559
  }
129323
129560
  await assertStandaloneFolder(folder);
129324
- const { identity, reader, listed: listed2, workspace, reference } = await connectHostedBundle(typed, target2, values.workspace, deps, resume2);
129561
+ const { identity, reader, listed: listed2, workspace, reference, definitionWrites } = await connectHostedBundle(typed, target2, values.workspace, deps, resume2);
129325
129562
  const canonical2 = await canonicalFolder(folder);
129326
129563
  let createdFolder = false;
129327
129564
  const placed = /* @__PURE__ */ new Map();
@@ -129348,7 +129585,7 @@ async function checkout(argv2, partial2 = {}) {
129348
129585
  }
129349
129586
  const identityNow = await folderIdentity(canonical2);
129350
129587
  if (!identityNow) throw new CliError("RUNTIME", `${canonical2} disappeared during checkout`, { help: "retry the same command" });
129351
- const binding = {
129588
+ const binding = withDefinitionWrites({
129352
129589
  schema: 1,
129353
129590
  checkout_id: newCheckoutId(),
129354
129591
  path: canonical2,
@@ -129363,7 +129600,7 @@ async function checkout(argv2, partial2 = {}) {
129363
129600
  created_at: new Date(deps.auth.now()).toISOString(),
129364
129601
  state: "hydrating",
129365
129602
  folder_identity: identityNow
129366
- };
129603
+ }, definitionWrites);
129367
129604
  await writeBinding(deps.auth.home, binding);
129368
129605
  let store;
129369
129606
  try {
@@ -129485,7 +129722,7 @@ async function connectHostedBundle(typed, target2, workspaceFlag, deps, resume2)
129485
129722
  }
129486
129723
  if (ids.length > Math.min(CHECKOUT_DOCUMENT_LIMIT, capabilities.bound.documents)) throw tooLarge(reference, target2, ids.length);
129487
129724
  assertProjectable(ids, bundleId, target2);
129488
- return { identity, reader, listed: listed2, workspace: tenantId ?? workspace, reference, client };
129725
+ return { identity, reader, listed: listed2, workspace: tenantId ?? workspace, reference, client, definitionWrites: capabilities.definitionWrites };
129489
129726
  }
129490
129727
  function catalogLabels(bundleId) {
129491
129728
  const base = bundleId.slice(0, 60).replace(/[._-]+$/, "");
@@ -129640,7 +129877,7 @@ Examples:
129640
129877
  // ../cli/src/commands/export.ts
129641
129878
  import { randomBytes as randomBytes9 } from "node:crypto";
129642
129879
  import { homedir as homedir38 } from "node:os";
129643
- import { link as link3, lstat as lstat10, mkdir as mkdir5, open as open4, readdir as readdir7, realpath as realpath12, rename as rename2, rm as rm2, unlink as unlink12 } from "node:fs/promises";
129880
+ import { link as link3, lstat as lstat10, mkdir as mkdir5, open as open4, readdir as readdir8, realpath as realpath12, rename as rename2, rm as rm2, unlink as unlink12 } from "node:fs/promises";
129644
129881
  import path48 from "node:path";
129645
129882
  import { parseArgs as parseArgs40 } from "node:util";
129646
129883
  function exportDeps(partial2) {
@@ -129660,7 +129897,7 @@ async function entryKind2(target2) {
129660
129897
  throw error51;
129661
129898
  }
129662
129899
  if (!info.isDirectory()) return "other";
129663
- return (await readdir7(target2)).length === 0 ? "empty-dir" : "dir";
129900
+ return (await readdir8(target2)).length === 0 ? "empty-dir" : "dir";
129664
129901
  }
129665
129902
  function shown(paths) {
129666
129903
  return [...paths].sort().slice(0, SHOWN);
@@ -129837,7 +130074,7 @@ async function removeAbandonedStaging(target2) {
129837
130074
  let removed = 0;
129838
130075
  let names;
129839
130076
  try {
129840
- names = await readdir7(path48.dirname(target2));
130077
+ names = await readdir8(path48.dirname(target2));
129841
130078
  } catch {
129842
130079
  return 0;
129843
130080
  }
@@ -130772,20 +131009,20 @@ async function planPublish(folder, options) {
130772
131009
  skipped.push({ path: entry.rel, reason: "not a plain file" });
130773
131010
  continue;
130774
131011
  }
130775
- const bytes2 = await fs15.readFile(path50.join(folder, entry.rel));
131012
+ const bytes = await fs15.readFile(path50.join(folder, entry.rel));
130776
131013
  if (isReservedFile(entry.rel)) {
130777
- const text4 = utf83(bytes2);
131014
+ const text4 = utf83(bytes);
130778
131015
  if (text4 === null) {
130779
131016
  blockers.push({ path: entry.rel, reason: "not_utf8", message: `${entry.rel} is a reserved file that is not UTF-8 text` });
130780
131017
  continue;
130781
131018
  }
130782
- if (bytes2.byteLength > PUBLISH_BOUNDS.reservedBytes) {
131019
+ if (bytes.byteLength > PUBLISH_BOUNDS.reservedBytes) {
130783
131020
  blockers.push({ path: entry.rel, reason: "too_large", message: `${entry.rel} is over the ${PUBLISH_BOUNDS.reservedBytes / 1024} KiB a reserved file may hold` });
130784
131021
  continue;
130785
131022
  }
130786
131023
  const dir = path50.posix.dirname(entry.rel);
130787
131024
  reserved.push({ dir: dir === "." ? "" : dir, name: path50.posix.basename(entry.rel), content: text4 });
130788
- if (entry.rel !== "index.md") extras[entry.rel] = digest2(bytes2);
131025
+ if (entry.rel !== "index.md") extras[entry.rel] = digest2(bytes);
130789
131026
  continue;
130790
131027
  }
130791
131028
  if (!entry.rel.endsWith(".md")) {
@@ -130795,12 +131032,12 @@ async function planPublish(folder, options) {
130795
131032
  blockers.push({ path: entry.rel, reason: "unsafe_path", message: `${entry.rel} cannot be a file key (${error51.message})` });
130796
131033
  continue;
130797
131034
  }
130798
- if (bytes2.byteLength > PUBLISH_BOUNDS.blobBytes) {
130799
- blockers.push({ path: entry.rel, reason: "too_large", message: `${entry.rel} is over the 1 MB a file may hold` });
131035
+ if (bytes.byteLength > STAGED_PUBLISH_BOUNDS.blobBytes) {
131036
+ blockers.push({ path: entry.rel, reason: "too_large", message: `${entry.rel} is over the 16 MiB a file may hold` });
130800
131037
  continue;
130801
131038
  }
130802
- blobs2.push({ key: entry.rel, contentType: contentTypeOf(entry.rel), base64: bytes2.toString("base64") });
130803
- extras[entry.rel] = digest2(bytes2);
131039
+ blobs2.push({ key: entry.rel, contentType: contentTypeOf(entry.rel), bytes });
131040
+ extras[entry.rel] = digest2(bytes);
130804
131041
  continue;
130805
131042
  }
130806
131043
  const id = conceptIdFromPath(entry.rel);
@@ -130814,14 +131051,14 @@ async function planPublish(folder, options) {
130814
131051
  blockers.push({ path: entry.rel, reason: "document_id_not_canonical", message: `${entry.rel} is not in its canonical spelling (a segment starts or ends with a space or an invisible format character, holds a format character other than a joiner, or is not NFC); rename it` });
130815
131052
  continue;
130816
131053
  }
130817
- const refusal = unsendable(id, entry.rel, bytes2, null, { bundleId: "publish", okfVersion });
131054
+ const refusal = unsendable(id, entry.rel, bytes, null, { bundleId: "publish", okfVersion });
130818
131055
  if (refusal) {
130819
131056
  blockers.push({ path: entry.rel, reason: refusal.reason, message: refusal.message });
130820
131057
  continue;
130821
131058
  }
130822
131059
  let request2;
130823
131060
  try {
130824
- request2 = wholeDocumentRequest("publish", { kind: "document.write", target: id, base: null, content: utf83(bytes2) }, okfVersion);
131061
+ request2 = wholeDocumentRequest("publish", { kind: "document.write", target: id, base: null, content: utf83(bytes) }, okfVersion);
130825
131062
  } catch (error51) {
130826
131063
  if (error51 instanceof WholeDocumentInputError) {
130827
131064
  blockers.push({ path: entry.rel, reason: "not_sendable", message: error51.message });
@@ -130831,7 +131068,7 @@ async function planPublish(folder, options) {
130831
131068
  }
130832
131069
  if (request2.kind === "delete") throw new Error(`'${id}' became a delete request`);
130833
131070
  documents.push({ id, frontmatter: request2.payload.frontmatter, body: request2.payload.body ?? "" });
130834
- documentFiles.push({ id, rel: entry.rel, text: utf83(bytes2) });
131071
+ documentFiles.push({ id, rel: entry.rel, text: utf83(bytes) });
130835
131072
  }
130836
131073
  if (rootIndex === null) blockers.push({ path: "index.md", reason: "no_root_index", message: "a hosted bundle needs a root index.md" });
130837
131074
  if (edition === "unsupported") {
@@ -130852,9 +131089,7 @@ async function planPublish(folder, options) {
130852
131089
  claimed.set(key, file2);
130853
131090
  }
130854
131091
  }
130855
- if (documents.length > PUBLISH_BOUNDS.documents) blockers.push({ path: ".", reason: "too_many_documents", message: `${documents.length} documents; a hosted bundle is created with at most ${PUBLISH_BOUNDS.documents}` });
130856
131092
  if (reserved.length > PUBLISH_BOUNDS.reserved) blockers.push({ path: ".", reason: "too_many_reserved", message: `${reserved.length} reserved files; at most ${PUBLISH_BOUNDS.reserved}` });
130857
- if (blobs2.length > PUBLISH_BOUNDS.blobs) blockers.push({ path: ".", reason: "too_many_files", message: `${blobs2.length} files that are not documents; at most ${PUBLISH_BOUNDS.blobs}` });
130858
131093
  let history = [];
130859
131094
  let historyPlan = { mode: "current-only", versions: 0, skipped: 0, note: "history starts at publish" };
130860
131095
  if (options.history) {
@@ -130873,24 +131108,49 @@ async function planPublish(folder, options) {
130873
131108
  skipped: skippedVersions,
130874
131109
  note: "earlier Git versions are imported as labeled, unverified history (imported:git/<commit>); nothing reads them back yet"
130875
131110
  };
130876
- if (history.length > PUBLISH_BOUNDS.history || documents.length + reserved.length + blobs2.length + history.length > PUBLISH_BOUNDS.objects) {
130877
- blockers.push({
130878
- path: ".",
130879
- reason: "too_much_history",
130880
- message: `${history.length} earlier versions: one creation carries at most ${PUBLISH_BOUNDS.history} versions and ${PUBLISH_BOUNDS.objects} objects in all; publish without --with-history`
130881
- });
130882
- }
130883
131111
  }
130884
131112
  }
130885
- if (documents.length + reserved.length + blobs2.length + history.length > PUBLISH_BOUNDS.objects && !blockers.some((b) => b.reason === "too_much_history")) {
130886
- blockers.push({ path: ".", reason: "too_many_objects", message: `one creation carries at most ${PUBLISH_BOUNDS.objects} documents, reserved files and files in all` });
130887
- }
130888
- const plan = { documents, reserved, blobs: blobs2, history, skipped, blockers, historyPlan, extras };
130889
- const bytes = Buffer.byteLength(JSON.stringify(createBody(plan, { workspace: "w".repeat(64), bundleId: "b".repeat(128), name: "n".repeat(128) })));
130890
- if (bytes > PUBLISH_BOUNDS.requestBytes) {
130891
- blockers.push({ path: ".", reason: "request_too_large", message: `the bundle is ${Math.ceil(bytes / 1024)} KiB as one request; the host takes at most ${PUBLISH_BOUNDS.requestBytes / 1024 / 1024} MiB` });
131113
+ const draft = { documents, reserved, blobs: blobs2, history };
131114
+ const sendPath = publishPath(draft);
131115
+ if (sendPath.mode === "staged") blockers.push(...stagedBlockers(draft));
131116
+ return { documents, reserved, blobs: blobs2, history, path: sendPath, skipped, blockers, historyPlan, extras };
131117
+ }
131118
+ function publishPath(plan) {
131119
+ const bounds = PUBLISH_BOUNDS;
131120
+ const objects = plan.documents.length + plan.reserved.length + plan.blobs.length + plan.history.length;
131121
+ const why = plan.documents.length > bounds.documents ? `${plan.documents.length} documents (one request carries at most ${bounds.documents})` : plan.blobs.length > bounds.blobs ? `${plan.blobs.length} other files (one request carries at most ${bounds.blobs})` : plan.history.length > bounds.history ? `${plan.history.length} earlier versions (one request carries at most ${bounds.history})` : objects > bounds.objects ? `${objects} objects in all (one request carries at most ${bounds.objects})` : plan.blobs.some((blob) => blob.bytes.byteLength > bounds.blobBytes) ? `a file over 1 MB (${plan.blobs.find((blob) => blob.bytes.byteLength > bounds.blobBytes).key})` : null;
131122
+ if (why !== null) return { mode: "staged", why };
131123
+ const base643 = plan.blobs.reduce((sum, blob) => sum + Math.ceil(blob.bytes.byteLength / 3) * 4, 0);
131124
+ const bytes = base643 > bounds.requestBytes ? base643 : Buffer.byteLength(JSON.stringify(createBody(plan, WIDEST_TARGET)));
131125
+ if (bytes > bounds.requestBytes) return { mode: "staged", why: `${Math.ceil(bytes / 1024)} KiB as one request (one request carries at most ${bounds.requestBytes / MiB} MiB)` };
131126
+ return { mode: "one-shot" };
131127
+ }
131128
+ function stagedBlockers(plan) {
131129
+ const bounds = STAGED_PUBLISH_BOUNDS;
131130
+ const blockers = [];
131131
+ const noHistory = "publish without --with-history, or with fewer earlier versions";
131132
+ if (plan.documents.length > bounds.documents) blockers.push({ path: ".", reason: "too_many_documents", message: `${plan.documents.length} documents; a hosted bundle is created with at most ${bounds.documents}` });
131133
+ if (plan.blobs.length > bounds.blobs) blockers.push({ path: ".", reason: "too_many_files", message: `${plan.blobs.length} files that are not documents; at most ${bounds.blobs}` });
131134
+ if (plan.history.length > bounds.history) blockers.push({ path: ".", reason: "too_much_history", message: `${plan.history.length} earlier versions; a creation carries at most ${bounds.history}: ${noHistory}` });
131135
+ const content3 = stagedContent(plan);
131136
+ const current = distinctBytes([...content3.documents, ...content3.reserved, ...content3.blobs]);
131137
+ if (current > bounds.currentBytes) blockers.push({ path: ".", reason: "too_large", message: `the bundle's current files are ${Math.ceil(current / MiB)} MiB; a creation carries at most ${bounds.currentBytes / MiB} MiB` });
131138
+ const history = distinctBytes(content3.history);
131139
+ if (history > bounds.historyBytes) blockers.push({ path: ".", reason: "too_much_history", message: `the earlier versions are ${Math.ceil(history / MiB)} MiB; a creation carries at most ${bounds.historyBytes / MiB} MiB: ${noHistory}` });
131140
+ const parts = packParts(content3, content3.objects.keys()).length;
131141
+ if (parts > bounds.parts) blockers.push({ path: ".", reason: "too_many_parts", message: `the bundle needs ${parts} parts; a creation holds at most ${bounds.parts}: ${plan.history.length > 0 ? noHistory : "publish fewer files"}` });
131142
+ const manifest = Buffer.byteLength(JSON.stringify(manifestBody(content3, WIDEST_TARGET)));
131143
+ if (manifest > bounds.manifestBytes) {
131144
+ blockers.push({
131145
+ path: ".",
131146
+ reason: "manifest_too_large",
131147
+ message: `the list of everything sent is ${Math.ceil(manifest / 1024)} KiB; the host takes at most ${bounds.manifestBytes / MiB} MiB: ${plan.history.length > 0 ? noHistory : "publish fewer files"}`
131148
+ });
130892
131149
  }
130893
- return plan;
131150
+ return blockers;
131151
+ }
131152
+ function distinctBytes(entries) {
131153
+ return [...new Map(entries.map((entry) => [entry.version, entry.bytes])).values()].reduce((sum, bytes) => sum + bytes, 0);
130894
131154
  }
130895
131155
  function createBody(plan, target2) {
130896
131156
  return {
@@ -130899,14 +131159,89 @@ function createBody(plan, target2) {
130899
131159
  name: target2.name,
130900
131160
  documents: plan.documents,
130901
131161
  reserved: plan.reserved,
130902
- blobs: plan.blobs,
131162
+ blobs: plan.blobs.map((blob) => ({ key: blob.key, contentType: blob.contentType, base64: blob.bytes.toString("base64") })),
130903
131163
  ...plan.history.length > 0 ? { history: plan.history } : {}
130904
131164
  };
130905
131165
  }
130906
131166
  function planDigest(body) {
130907
131167
  return digest2(Buffer.from(JSON.stringify(body), "utf8"));
130908
131168
  }
130909
- var PUBLISH_BOUNDS, CONTENT_TYPES, HOSTED_KIND_WRITE, FORMAT_AT_EDGE, FORMAT_INSIDE;
131169
+ function stagedContent(plan) {
131170
+ const wire = (object5) => ({ ...object5, frontmatter: JSON.parse(JSON.stringify(object5.frontmatter)) });
131171
+ const objects = /* @__PURE__ */ new Map();
131172
+ const keep = (version3, object5) => {
131173
+ if (!objects.has(version3)) objects.set(version3, object5);
131174
+ };
131175
+ const stored = (text4) => ({ version: versionOfBytes(text4), bytes: Buffer.byteLength(text4, "utf8") });
131176
+ const conventions = [];
131177
+ const documents = plan.documents.map((sent) => {
131178
+ const doc2 = wire(sent);
131179
+ const text4 = stringifyDoc(doc2.frontmatter, doc2.body);
131180
+ const entry = stored(text4);
131181
+ keep(entry.version, { kind: "documents", value: doc2 });
131182
+ if (matchesFilter({ id: doc2.id, frontmatter: parseMarkdown(text4, doc2.id).frontmatter }, { prefix: CONVENTIONS_PREFIX, type: CONVENTION_TYPE })) conventions.push(doc2);
131183
+ return { id: doc2.id, ...entry };
131184
+ });
131185
+ const reserved = plan.reserved.map((file2) => {
131186
+ const entry = stored(file2.content);
131187
+ keep(entry.version, { kind: "reserved", value: file2 });
131188
+ return { dir: file2.dir, name: file2.name, ...entry };
131189
+ });
131190
+ const ordinals = /* @__PURE__ */ new Map();
131191
+ const history = plan.history.map((sent) => {
131192
+ const row3 = wire(sent);
131193
+ const ordinal = (ordinals.get(row3.documentId) ?? 0) + 1;
131194
+ ordinals.set(row3.documentId, ordinal);
131195
+ const entry = stored(stringifyDoc(row3.frontmatter, row3.body));
131196
+ keep(entry.version, { kind: "history", value: { ...row3, ordinal } });
131197
+ return { documentId: row3.documentId, ordinal, label: row3.label, author: row3.author, authoredAt: row3.authoredAt, ...entry };
131198
+ });
131199
+ const blobBytes = /* @__PURE__ */ new Map();
131200
+ const blobs2 = plan.blobs.map((blob) => {
131201
+ const version3 = blobVersion(blob.bytes);
131202
+ if (!blobBytes.has(version3)) blobBytes.set(version3, { key: blob.key, bytes: blob.bytes });
131203
+ return { key: blob.key, contentType: blob.contentType, version: version3, bytes: blob.bytes.byteLength };
131204
+ });
131205
+ const root = plan.reserved.find((file2) => file2.dir === "" && file2.name === "index.md")?.content ?? "";
131206
+ return { root, conventions, documents, reserved, blobs: blobs2, history, objects, blobBytes };
131207
+ }
131208
+ function manifestBody(content3, target2) {
131209
+ return {
131210
+ workspace: target2.workspace,
131211
+ bundleId: target2.bundleId,
131212
+ name: target2.name,
131213
+ root: content3.root,
131214
+ conventions: content3.conventions,
131215
+ documents: content3.documents,
131216
+ reserved: content3.reserved,
131217
+ blobs: content3.blobs,
131218
+ history: content3.history
131219
+ };
131220
+ }
131221
+ function packParts(content3, versions) {
131222
+ const bounds = STAGED_PUBLISH_BOUNDS;
131223
+ const parts = [];
131224
+ let part = null;
131225
+ let size = 0;
131226
+ const envelope = 512;
131227
+ for (const version3 of new Set(versions)) {
131228
+ const object5 = content3.objects.get(version3);
131229
+ if (!object5) continue;
131230
+ const bytes = Buffer.byteLength(JSON.stringify(object5.value)) + 1;
131231
+ if (part === null || part.versions.length >= bounds.partObjects || size + bytes > bounds.partTargetBytes) {
131232
+ part = { documents: [], reserved: [], history: [], versions: [] };
131233
+ parts.push(part);
131234
+ size = envelope;
131235
+ }
131236
+ if (object5.kind === "documents") part.documents.push(object5.value);
131237
+ else if (object5.kind === "reserved") part.reserved.push(object5.value);
131238
+ else part.history.push(object5.value);
131239
+ part.versions.push(version3);
131240
+ size += bytes;
131241
+ }
131242
+ return parts;
131243
+ }
131244
+ var MiB, PUBLISH_BOUNDS, STAGED_PUBLISH_BOUNDS, CONTENT_TYPES, HOSTED_KIND_WRITE, FORMAT_AT_EDGE, FORMAT_INSIDE, WIDEST_TARGET;
130910
131245
  var init_publish_plan = __esm({
130911
131246
  "../cli/src/hosted/publish-plan.ts"() {
130912
131247
  "use strict";
@@ -130915,9 +131250,11 @@ var init_publish_plan = __esm({
130915
131250
  init_src2();
130916
131251
  init_src();
130917
131252
  init_hosted_transport();
131253
+ init_versioning();
130918
131254
  init_projection();
130919
131255
  init_sync_scan();
130920
131256
  init_src();
131257
+ MiB = 1024 * 1024;
130921
131258
  PUBLISH_BOUNDS = Object.freeze({
130922
131259
  objects: 1500,
130923
131260
  documents: 1e3,
@@ -130926,7 +131263,22 @@ var init_publish_plan = __esm({
130926
131263
  history: 1e3,
130927
131264
  reservedBytes: 64 * 1024,
130928
131265
  blobBytes: 1e6,
130929
- requestBytes: 3 * 1024 * 1024
131266
+ requestBytes: 3 * MiB
131267
+ });
131268
+ STAGED_PUBLISH_BOUNDS = Object.freeze({
131269
+ documents: 1e4,
131270
+ reserved: 1e3,
131271
+ blobs: 1e3,
131272
+ history: 5e3,
131273
+ blobBytes: 16 * MiB,
131274
+ currentBytes: 64 * MiB,
131275
+ historyBytes: 64 * MiB,
131276
+ manifestBytes: 3 * MiB,
131277
+ /** The host's cap on one part's body; parts are packed to {@link partTargetBytes}. */
131278
+ partBytes: 3 * MiB,
131279
+ partTargetBytes: 1 * MiB,
131280
+ partObjects: 500,
131281
+ parts: 400
130930
131282
  });
130931
131283
  CONTENT_TYPES = {
130932
131284
  ".txt": "text/plain",
@@ -130951,6 +131303,246 @@ var init_publish_plan = __esm({
130951
131303
  HOSTED_KIND_WRITE = Object.freeze({ strict: true, persistActor: true, producer: "process:superbee-hosted" });
130952
131304
  FORMAT_AT_EDGE = new RegExp("^\\p{Cf}|\\p{Cf}$", "u");
130953
131305
  FORMAT_INSIDE = new RegExp("(?![\\u200c\\u200d])\\p{Cf}", "u");
131306
+ WIDEST_TARGET = Object.freeze({ workspace: "w".repeat(64), bundleId: "b".repeat(128), name: "n".repeat(128) });
131307
+ }
131308
+ });
131309
+
131310
+ // ../cli/src/hosted/publish-staged.ts
131311
+ function isFinalBeforeReservation(refusal) {
131312
+ return !refusal.reserved && refusal.route === "bundle-create-begin" && ["bundle_exists", "document_id_collision", "document_id_not_canonical", "validation_failed", "result_too_large"].includes(refusal.code);
131313
+ }
131314
+ async function runStagedCreate(request2) {
131315
+ const { client, requestId: requestId2, target: target2, content: content3 } = request2;
131316
+ const sleep = request2.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
131317
+ const progress = request2.progress ?? (() => {
131318
+ });
131319
+ const route = (name) => `${client.prefix}/${name}`;
131320
+ let reserved = false;
131321
+ let planHash = "";
131322
+ let requests = 0;
131323
+ let bytesSent = 0;
131324
+ const objects = content3.objects.size + content3.blobBytes.size;
131325
+ const contentBytes = [...content3.objects.values()].reduce((sum, object5) => sum + Buffer.byteLength(JSON.stringify(object5.value)), 0) + [...content3.blobBytes.values()].reduce((sum, blob) => sum + blob.bytes.byteLength, 0);
131326
+ const maximumRequests = REQUEST_ALLOWANCE + REQUESTS_PER_OBJECT * objects;
131327
+ const maximumBytes = BYTES_ALLOWANCE + BYTES_PER_CONTENT_BYTE * contentBytes;
131328
+ const unknownOutcome = (why) => new CliError("TRANSIENT", `${why}; the bundle may be partly created`, {
131329
+ details: { reason: "write_outcome_unknown", bundle_id: target2.bundleId, workspace: target2.workspace, host: client.target.origin, request_id: requestId2, retryable: true },
131330
+ help: `re-run the same command; it resumes the same creation, sending only what the host is missing: ${request2.resume}`
131331
+ });
131332
+ async function send(name, body, headers) {
131333
+ for (let attempt = 0; ; attempt++) {
131334
+ requests += 1;
131335
+ bytesSent += body instanceof Uint8Array ? body.byteLength : 0;
131336
+ if (requests > maximumRequests || bytesSent > maximumBytes) {
131337
+ throw unknownOutcome(`${client.target.origin} did not finish the creation within ${maximumRequests} requests and ${Math.ceil(maximumBytes / (1024 * 1024))} MiB sent`);
131338
+ }
131339
+ let answer2;
131340
+ try {
131341
+ if (body instanceof Uint8Array) {
131342
+ if (!client.carrier.bytes) throw new CliError("RUNTIME", "this client cannot send a file's raw bytes");
131343
+ answer2 = await client.carrier.bytes(route(name), body, client.signal, { maximum: ANSWER_BYTES, writeRequest: requestId2, ...headers ? { headers } : {} });
131344
+ } else {
131345
+ answer2 = await client.carrier.json(route(name), body, client.signal, { maximum: ANSWER_BYTES, writeRequest: requestId2 });
131346
+ }
131347
+ } catch (error51) {
131348
+ if (error51 instanceof HostedCarrierError && error51.code === "unavailable") throw unknownOutcome(`the answer from ${client.target.origin} to ${name} did not arrive`);
131349
+ throw hostedFailure(error51, client.target, request2.resume);
131350
+ }
131351
+ if (answer2.status !== 503) return answer2;
131352
+ const delay3 = RETRY_DELAYS_MS[attempt];
131353
+ if (delay3 === void 0) throw unknownOutcome(`${client.target.origin} kept answering ${name} 503`);
131354
+ await sleep(delay3);
131355
+ }
131356
+ }
131357
+ function read(name, answer2) {
131358
+ const envelope = answer2.body ?? {};
131359
+ const code3 = typeof envelope.error?.code === "string" ? envelope.error.code : null;
131360
+ const message = typeof envelope.error?.message === "string" ? envelope.error.message : "";
131361
+ if (answer2.status === 200 && envelope.ok === true && typeof envelope.data === "object" && envelope.data !== null) return envelope.data;
131362
+ if (answer2.status === 200 && envelope.ok === false && code3 === "staged_manifest_missing") return "no_manifest";
131363
+ if (answer2.status === 200 && envelope.ok === false && code3 === "request_conflict" && envelope.error?.retryable === true) return "busy";
131364
+ if (answer2.status === 200 && envelope.ok === false && code3 === "validation_failed" && VERSION_MISMATCH.test(message)) {
131365
+ throw new CliError("RUNTIME", `${client.target.origin} stores an object differently from this CLI (client/host contract mismatch)`, {
131366
+ details: { reason: "staged_mismatch", route: name, host_message: message, host: client.target.origin, request_id: requestId2, retryable: false },
131367
+ help: `upgrade Superbee (npm install -g superbee), then re-run the same command, which resumes this creation: ${request2.resume}`
131368
+ });
131369
+ }
131370
+ if (answer2.status === 200 && envelope.ok === false && code3 !== null) throw new StagedRefusal(code3, message, reserved, name);
131371
+ if (answer2.status === 429 && code3 === "bundle_create_limit") throw new StagedRefusal(code3, message, reserved, name);
131372
+ if (answer2.status === 400 && !reserved) throw new StagedRefusal(code3 ?? "invalid_input", message || `the host refused ${name} as malformed`, false, name);
131373
+ throw hostedFailure(new RemoteError(`hosted ${name} answered ${answer2.status}`, code3 ?? "RUNTIME", answer2.status), client.target, request2.resume);
131374
+ }
131375
+ async function status2(name, data) {
131376
+ const missing2 = data.missing;
131377
+ const staged = data.staged;
131378
+ if (data.state !== "staging" && data.state !== "importing" && data.state !== "created" || typeof data.planHash !== "string" || !VERSION.test(data.planHash) || !isStringList(missing2?.versions) || !isStringList(missing2?.blobs) || typeof staged?.versions !== "number" || typeof staged.blobVersions !== "number") {
131379
+ throw hostedFailure(new MalformedAnswer(`not a staged creation's status`, route(name)), client.target, request2.resume);
131380
+ }
131381
+ const answer2 = data;
131382
+ planHash = answer2.planHash;
131383
+ if (answer2.state !== "staging" && !reserved) {
131384
+ reserved = true;
131385
+ await request2.onReserved?.();
131386
+ }
131387
+ return answer2;
131388
+ }
131389
+ const manifest = manifestBody(content3, target2);
131390
+ async function begin() {
131391
+ let data = read("bundle-create-begin", await send("bundle-create-begin", manifest));
131392
+ for (let retry = 0; data === "busy"; retry++) {
131393
+ if (retry >= CONFLICT_RETRIES) {
131394
+ throw new CliError("TRANSIENT", `an earlier commit of this creation may still be running on ${client.target.origin}`, {
131395
+ details: { reason: "commit_running", bundle_id: target2.bundleId, workspace: target2.workspace, host: client.target.origin, request_id: requestId2, retryable: true },
131396
+ help: `re-run the same command in a minute: ${request2.resume}`
131397
+ });
131398
+ }
131399
+ progress({ phase: "wait", seconds: CONFLICT_RETRY_MS / 1e3, reason: "an earlier commit of this creation may still be running" });
131400
+ await sleep(CONFLICT_RETRY_MS);
131401
+ data = read("bundle-create-begin", await send("bundle-create-begin", manifest));
131402
+ }
131403
+ if (data === "no_manifest") throw hostedFailure(new MalformedAnswer("begin answered that no manifest is staged", route("bundle-create-begin")), client.target, request2.resume);
131404
+ const answer2 = await status2("bundle-create-begin", data);
131405
+ progress({ phase: "begin", state: answer2.state, versions: content3.objects.size, staged: answer2.staged.versions, blobs: content3.blobBytes.size, stagedBlobs: answer2.staged.blobVersions });
131406
+ return answer2;
131407
+ }
131408
+ async function fill(from) {
131409
+ const unknown3 = [...from.missing.versions.filter((v2) => !content3.objects.has(v2)), ...from.missing.blobs.filter((v2) => !content3.blobBytes.has(v2))];
131410
+ if (unknown3.length > 0) {
131411
+ throw new CliError("RUNTIME", `${client.target.origin} asks for ${unknown3.length} object(s) this folder does not hold (client/host contract mismatch)`, {
131412
+ details: { reason: "staged_mismatch", versions: unknown3.slice(0, 5), host: client.target.origin, request_id: requestId2, retryable: false },
131413
+ help: "upgrade Superbee (npm install -g superbee), then re-run the same command"
131414
+ });
131415
+ }
131416
+ const blobs2 = from.missing.blobs;
131417
+ let next = 0;
131418
+ let gone = false;
131419
+ const upload = async () => {
131420
+ try {
131421
+ await uploadEach();
131422
+ } catch (error51) {
131423
+ gone = true;
131424
+ throw error51;
131425
+ }
131426
+ };
131427
+ const uploadEach = async () => {
131428
+ for (let at = next++; at < blobs2.length && !gone; at = next++) {
131429
+ const version3 = blobs2[at];
131430
+ const blob = content3.blobBytes.get(version3);
131431
+ const data = read(
131432
+ "bundle-create-blob",
131433
+ await send("bundle-create-blob", blob.bytes, {
131434
+ "X-Superbee-Workspace": target2.workspace,
131435
+ "X-Superbee-Bundle": target2.bundleId,
131436
+ "X-Superbee-Blob-Version": version3,
131437
+ "X-Superbee-Plan-Hash": planHash
131438
+ })
131439
+ );
131440
+ if (data === "no_manifest" || data === "busy") {
131441
+ gone = true;
131442
+ return;
131443
+ }
131444
+ progress({ phase: "blob", blob: at + 1, blobs: blobs2.length, key: blob.key, bytes: blob.bytes.byteLength });
131445
+ }
131446
+ };
131447
+ let failure3 = null;
131448
+ const settled = await Promise.allSettled(Array.from({ length: Math.min(BLOBS_IN_FLIGHT, blobs2.length) }, upload));
131449
+ for (const result3 of settled) if (result3.status === "rejected" && failure3 === null) failure3 = result3.reason;
131450
+ if (failure3 !== null) throw failure3;
131451
+ if (gone) return "no_manifest";
131452
+ const parts = packParts(content3, from.missing.versions);
131453
+ let last = null;
131454
+ for (const [index2, part] of parts.entries()) {
131455
+ const data = read(
131456
+ "bundle-create-stage",
131457
+ await send("bundle-create-stage", { workspace: target2.workspace, bundleId: target2.bundleId, planHash, documents: part.documents, reserved: part.reserved, history: part.history })
131458
+ );
131459
+ if (data === "no_manifest" || data === "busy") return "no_manifest";
131460
+ last = await status2("bundle-create-stage", data);
131461
+ progress({ phase: "stage", part: index2 + 1, parts: parts.length, objects: part.versions.length });
131462
+ }
131463
+ return blobs2.length > 0 ? null : last;
131464
+ }
131465
+ const outstanding = (s) => s.missing.versions.length + s.missing.blobs.length;
131466
+ const missingKey = (s) => `${outstanding(s)}:${s.missing.versions[0] ?? ""}:${s.missing.blobs[0] ?? ""}`;
131467
+ let current = await begin();
131468
+ let lastFilled = null;
131469
+ let stalls = 0;
131470
+ let commits = 0;
131471
+ for (; ; ) {
131472
+ if (current !== null && current.state !== "created" && outstanding(current) > 0) {
131473
+ const key = missingKey(current);
131474
+ stalls = key === lastFilled ? stalls + 1 : 0;
131475
+ if (stalls >= MAXIMUM_STALLS) {
131476
+ throw new CliError("RUNTIME", `${client.target.origin} keeps naming the same objects as missing after they were sent ${MAXIMUM_STALLS} times`, {
131477
+ details: { reason: "staged_stall", missing: { versions: current.missing.versions.slice(0, 5), blobs: current.missing.blobs.slice(0, 5) }, host: client.target.origin, request_id: requestId2, retryable: true },
131478
+ help: `re-run the same command later; it resumes this creation: ${request2.resume}`
131479
+ });
131480
+ }
131481
+ lastFilled = key;
131482
+ const after = await fill(current);
131483
+ if (after === "no_manifest") {
131484
+ current = await begin();
131485
+ continue;
131486
+ }
131487
+ if (after !== null && outstanding(after) > 0) {
131488
+ current = after;
131489
+ continue;
131490
+ }
131491
+ }
131492
+ commits += 1;
131493
+ const answer2 = await send("bundle-create-commit", { workspace: target2.workspace, bundleId: target2.bundleId, planHash });
131494
+ const data = read("bundle-create-commit", answer2);
131495
+ if (data === "no_manifest" || data === "busy") {
131496
+ current = await begin();
131497
+ continue;
131498
+ }
131499
+ if (typeof data.state !== "string") {
131500
+ return data;
131501
+ }
131502
+ current = await status2("bundle-create-commit", data);
131503
+ progress({ phase: "commit", call: commits, state: current.state, ...current.written ? { written: current.written } : {} });
131504
+ }
131505
+ }
131506
+ var STAGED_CREATE_ROUTES, ANSWER_BYTES, BLOBS_IN_FLIGHT, RETRY_DELAYS_MS, REQUEST_ALLOWANCE, REQUESTS_PER_OBJECT, BYTES_PER_CONTENT_BYTE, BYTES_ALLOWANCE, MAXIMUM_STALLS, CONFLICT_RETRY_MS, CONFLICT_RETRIES, StagedRefusal, VERSION_MISMATCH, VERSION, isStringList;
131507
+ var init_publish_staged = __esm({
131508
+ "../cli/src/hosted/publish-staged.ts"() {
131509
+ "use strict";
131510
+ init_define_SUPERBEE_BUILD_IDENTITY();
131511
+ init_define_SUPERBEE_UPDATE_POLICY();
131512
+ init_src();
131513
+ init_hosted_transport();
131514
+ init_errors4();
131515
+ init_client();
131516
+ init_publish_plan();
131517
+ STAGED_CREATE_ROUTES = Object.freeze(["bundle-create-begin", "bundle-create-stage", "bundle-create-blob", "bundle-create-commit"]);
131518
+ ANSWER_BYTES = 256 * 1024;
131519
+ BLOBS_IN_FLIGHT = 2;
131520
+ RETRY_DELAYS_MS = Object.freeze([500, 1e3, 2e3, 4e3]);
131521
+ REQUEST_ALLOWANCE = 2e3;
131522
+ REQUESTS_PER_OBJECT = 4;
131523
+ BYTES_PER_CONTENT_BYTE = 3;
131524
+ BYTES_ALLOWANCE = 64 * 1024 * 1024;
131525
+ MAXIMUM_STALLS = 3;
131526
+ CONFLICT_RETRY_MS = 6e4;
131527
+ CONFLICT_RETRIES = 5;
131528
+ StagedRefusal = class extends Error {
131529
+ code;
131530
+ hostMessage;
131531
+ /** True when the creation was reserved: the request id must be kept to finish it. */
131532
+ reserved;
131533
+ /** The route that answered it. */
131534
+ route;
131535
+ constructor(code3, hostMessage, reserved, route) {
131536
+ super(`${code3}: ${hostMessage}`);
131537
+ this.code = code3;
131538
+ this.hostMessage = hostMessage;
131539
+ this.reserved = reserved;
131540
+ this.route = route;
131541
+ }
131542
+ };
131543
+ VERSION_MISMATCH = /version or size is not the manifest's/i;
131544
+ VERSION = /^sha256:[0-9a-f]{64}$/;
131545
+ isStringList = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
130954
131546
  }
130955
131547
  });
130956
131548
 
@@ -130963,6 +131555,8 @@ import { parseArgs as parseArgs42 } from "node:util";
130963
131555
  function publishDeps(partial2) {
130964
131556
  return {
130965
131557
  stdout: partial2.stdout ?? ((text4) => void process.stdout.write(text4)),
131558
+ stderr: partial2.stderr ?? ((text4) => void process.stderr.write(text4)),
131559
+ ...partial2.sleep ? { sleep: partial2.sleep } : {},
130966
131560
  auth: partial2.auth ?? defaultHostedAuthDeps(homedir40()),
130967
131561
  cwd: partial2.cwd ?? process.cwd(),
130968
131562
  ...partial2.fetch ? { fetch: partial2.fetch } : {}
@@ -131058,6 +131652,7 @@ function summary(plan) {
131058
131652
  documents: plan.documents.length,
131059
131653
  reserved_files: plan.reserved.length,
131060
131654
  other_files: plan.blobs.length,
131655
+ sent: plan.path.mode === "one-shot" ? "one request" : `staged: ${plan.path.why}; a list of everything, parts of about 1 MiB, each file, then commits until created (resumable)`,
131061
131656
  history: { mode: plan.historyPlan.mode, versions: plan.historyPlan.versions, ...plan.historyPlan.skipped > 0 ? { skipped: plan.historyPlan.skipped } : {}, note: plan.historyPlan.note }
131062
131657
  };
131063
131658
  }
@@ -131069,11 +131664,17 @@ function createRefusal(code3, message, context) {
131069
131664
  case "workspace_not_found":
131070
131665
  return new CliError("NOT_FOUND", `'${context.workspace}' is not one of your workspaces on ${context.target.origin}`, { details, help: `${cliInvocation()} whoami --host ${commandToken(bindingHostArgument(context.target))}` });
131071
131666
  case "bundle_create_unavailable":
131072
- case "bundle_create_limit":
131073
- return new CliError("FORBIDDEN", code3 === "bundle_create_limit" ? `your bundle creation limit in ${context.workspace} is used up` : `${context.workspace} does not offer bundle creation to this client`, {
131074
- details,
131075
- help: "ask a workspace admin in the Superbee app"
131076
- });
131667
+ case "bundle_create_limit": {
131668
+ const open5 = code3 === "bundle_create_limit" && /staged creations open/i.test(message);
131669
+ return new CliError(
131670
+ "FORBIDDEN",
131671
+ open5 ? `you have too many unfinished large publishes open in ${context.workspace}` : code3 === "bundle_create_limit" ? `your bundle creation limit in ${context.workspace} is used up` : `${context.workspace} does not offer bundle creation to this client`,
131672
+ {
131673
+ details: open5 && context.openCreations ? { ...details, open: context.openCreations.map((record5) => ({ bundle_id: record5.bundle_id, ...record5.folder ? { folder: record5.folder } : {} })) } : details,
131674
+ help: open5 ? `finish one by re-running its publish${context.openCreations && context.openCreations.length > 0 ? ` (unfinished from here: ${context.openCreations.map((record5) => `'${record5.bundle_id}'${record5.folder ? ` from ${record5.folder}` : ""}`).join(", ")})` : ""}, or wait for one to expire 7 days after it began` : "ask a workspace admin in the Superbee app"
131675
+ }
131676
+ );
131677
+ }
131077
131678
  case "request_conflict":
131078
131679
  return new CliError("CONFLICT", `an unfinished publish of '${context.bundleId}' from this folder carried other contents, and the host holds the id for it`, {
131079
131680
  details,
@@ -131083,6 +131684,103 @@ function createRefusal(code3, message, context) {
131083
131684
  return new CliError("USAGE", `${context.target.origin} refused the bundle (${code3}): ${message}`, { details, help: "fix the files it names, then preview again" });
131084
131685
  }
131085
131686
  }
131687
+ function progressLine(event, json2) {
131688
+ if (json2) return `${JSON.stringify({ event: "publish.progress", ...event })}
131689
+ `;
131690
+ const mib = (bytes) => `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
131691
+ switch (event.phase) {
131692
+ case "begin":
131693
+ return `publish: ${event.state}: ${event.staged}/${event.versions} objects and ${event.stagedBlobs}/${event.blobs} files on the host
131694
+ `;
131695
+ case "stage":
131696
+ return `publish: sent part ${event.part}/${event.parts} (${event.objects} objects)
131697
+ `;
131698
+ case "blob":
131699
+ return `publish: sent file ${event.blob}/${event.blobs} ${event.key} (${mib(event.bytes)})
131700
+ `;
131701
+ case "commit":
131702
+ return `publish: commit ${event.call}: ${event.state}${event.written ? ` (written: ${Object.entries(event.written).map(([kind2, count5]) => `${count5} ${kind2}`).join(", ")})` : ""}
131703
+ `;
131704
+ case "wait":
131705
+ return `publish: waiting ${event.seconds} s: ${event.reason}
131706
+ `;
131707
+ }
131708
+ }
131709
+ async function sendCreation(plan, context) {
131710
+ const { workspace, bundleId, name, home: home2, canonical: canonical2, target: target2, client, yesCommand } = context;
131711
+ const staged = plan.path.mode === "staged" ? stagedContent(plan) : null;
131712
+ const body = staged ? manifestBody(staged, { workspace, bundleId, name }) : createBody(plan, { workspace, bundleId, name });
131713
+ const digest3 = planDigest(body);
131714
+ const earlier = await readPendingCreate(home2, canonical2, bundleId);
131715
+ const resumes = earlier !== null && earlier.host === target2.origin && earlier.workspace === workspace;
131716
+ const requestId2 = resumes ? earlier.request_id : randomUUID6();
131717
+ const record5 = resumes ? { ...earlier, ...staged ? { staged: true, folder: canonical2 } : {} } : { request_id: requestId2, host: target2.origin, workspace, bundle_id: bundleId, digest: digest3, ...staged ? { staged: true, folder: canonical2 } : {} };
131718
+ if (!resumes || staged && earlier.staged !== true) await writePendingCreate(home2, canonical2, record5);
131719
+ const refusalContext = { bundleId, workspace, target: target2, resume: yesCommand, folder: canonical2 };
131720
+ if (staged) {
131721
+ let data;
131722
+ try {
131723
+ data = await runStagedCreate({
131724
+ client,
131725
+ requestId: requestId2,
131726
+ target: { workspace, bundleId, name },
131727
+ content: staged,
131728
+ resume: String(yesCommand),
131729
+ progress: (event) => context.deps.stderr(progressLine(event, context.json)),
131730
+ ...context.deps.sleep ? { sleep: context.deps.sleep } : {},
131731
+ // Once the host holds the id for this request, only this request id can finish it.
131732
+ onReserved: async () => {
131733
+ if (record5.reserved !== true) await writePendingCreate(home2, canonical2, { ...record5, reserved: true });
131734
+ }
131735
+ });
131736
+ } catch (error51) {
131737
+ if (error51 instanceof StagedRefusal) {
131738
+ if (record5.reserved !== true && isFinalBeforeReservation(error51)) await clearPendingCreate(home2, canonical2, bundleId);
131739
+ const openCreations = error51.code === "bundle_create_limit" ? (await listPendingCreates(home2)).filter((other) => other.staged === true && other.host === target2.origin && other.workspace === workspace && other.request_id !== requestId2) : void 0;
131740
+ throw createRefusal(error51.code, error51.hostMessage, { ...refusalContext, ...openCreations ? { openCreations } : {} });
131741
+ }
131742
+ throw error51;
131743
+ }
131744
+ await clearPendingCreate(home2, canonical2, bundleId);
131745
+ return data;
131746
+ }
131747
+ let answer2;
131748
+ try {
131749
+ answer2 = await client.carrier.json(`${client.prefix}/bundle-create`, body, client.signal, { maximum: CREATE_ANSWER_BYTES, writeRequest: requestId2 });
131750
+ } catch (error51) {
131751
+ if (error51 instanceof HostedCarrierError && error51.code === "unavailable") {
131752
+ throw new CliError("TRANSIENT", `the answer from ${target2.origin} did not arrive; the bundle may be partly created`, {
131753
+ details: { reason: "write_outcome_unknown", bundle_id: bundleId, workspace, host: target2.origin, request_id: requestId2, retryable: true },
131754
+ help: `re-run the same command; it re-sends the same request, which finishes or confirms the creation: ${yesCommand}`
131755
+ });
131756
+ }
131757
+ throw hostedFailure(error51, target2, yesCommand);
131758
+ }
131759
+ const envelope = answer2.body ?? {};
131760
+ const code3 = typeof envelope.error?.code === "string" ? envelope.error.code : null;
131761
+ const hostMessage = typeof envelope.error?.message === "string" ? envelope.error.message : "";
131762
+ if (answer2.status === 503 && code3 === "write_outcome_unknown") {
131763
+ throw new CliError("TRANSIENT", `${target2.origin} may have partly created '${bundleId}'`, {
131764
+ details: { reason: "write_outcome_unknown", bundle_id: bundleId, workspace, host: target2.origin, request_id: requestId2, retryable: true },
131765
+ help: `re-run the same command; it re-sends the same request, which finishes or confirms the creation: ${yesCommand}`
131766
+ });
131767
+ }
131768
+ const forgettable = record5.reserved !== true;
131769
+ if (answer2.status === 429 && code3 === "bundle_create_limit") {
131770
+ if (forgettable) await clearPendingCreate(home2, canonical2, bundleId);
131771
+ throw createRefusal(code3, hostMessage, refusalContext);
131772
+ }
131773
+ if (answer2.status === 200 && envelope.ok === false && code3 !== null) {
131774
+ if (code3 !== "request_conflict" && forgettable) await clearPendingCreate(home2, canonical2, bundleId);
131775
+ throw createRefusal(code3, hostMessage, refusalContext);
131776
+ }
131777
+ if (answer2.status !== 200 || envelope.ok !== true || typeof envelope.data !== "object" || envelope.data === null) {
131778
+ if (answer2.status === 400 && forgettable) await clearPendingCreate(home2, canonical2, bundleId);
131779
+ throw hostedFailure(new RemoteError(`hosted bundle-create answered ${answer2.status}`, code3 ?? "RUNTIME", answer2.status), target2, yesCommand);
131780
+ }
131781
+ await clearPendingCreate(home2, canonical2, bundleId);
131782
+ return envelope.data;
131783
+ }
131086
131784
  async function publish(argv2, partial2 = {}) {
131087
131785
  const deps = publishDeps(partial2);
131088
131786
  const { values } = parseLeafOrUsage(
@@ -131138,13 +131836,15 @@ async function publish(argv2, partial2 = {}) {
131138
131836
  const withHistory = values["with-history"] === true;
131139
131837
  const plan = await planPublish(canonical2, withHistory ? { history: true, ...board ? { board } : {}, now: deps.auth.now() } : { history: false });
131140
131838
  const yesCommand = commandFragment`${cliInvocation()} publish --to hosted${values.dir !== void 0 ? commandFragment` --dir ${commandToken(canonical2)}` : commandFragment``}${target2 ? commandFragment` --host ${commandToken(bindingHostArgument(target2))}` : commandFragment``}${values.workspace !== void 0 ? commandFragment` --workspace ${commandToken(values.workspace)}` : commandFragment``} --bundle-id ${commandToken(bundleId)}${values.name !== void 0 ? commandFragment` --name ${commandToken(name)}` : commandFragment``}${withHistory ? commandFragment` --with-history` : commandFragment``} --yes${values.json ? commandFragment` --json` : commandFragment``}`;
131839
+ const converts = plan.documents.length <= CHECKOUT_DOCUMENT_LIMIT;
131840
+ const uncheckable = `leave this folder as it is${board ? " (still bound to the Git board)" : ""}: a hosted checkout holds at most ${CHECKOUT_DOCUMENT_LIMIT} documents, so use the bundle in the app; from then on, edits here do not reach the hosted bundle, nor its edits here`;
131141
131841
  const gitPlan = board ? {
131142
131842
  branch: board.branch,
131143
131843
  upstream: board.upstream,
131144
131844
  head: boardHead(board),
131145
131845
  sync: boardState?.block.state,
131146
131846
  ...boardState && (boardState.block.ahead ?? 0) + (boardState.block.uncommitted ?? 0) > 0 ? { not_on_branch: { ahead: boardState.block.ahead, uncommitted: boardState.block.uncommitted, note: "these travel to hosted but not to the board branch teammates still sync" } } : {},
131147
- will: "unbind this folder from the board branch; the branch and its commits stay, locally and on origin"
131847
+ will: converts ? "unbind this folder from the board branch; the branch and its commits stay, locally and on origin" : "keep this folder bound to the board branch: the board and the hosted bundle then diverge, and neither's edits reach the other"
131148
131848
  } : null;
131149
131849
  if (!values.yes) {
131150
131850
  const blockers = [...plan.blockers.map((b) => ({ path: b.path, reason: b.reason, message: b.message })), ...boardState?.blocker ? [boardState.blocker] : []];
@@ -131168,8 +131868,10 @@ async function publish(argv2, partial2 = {}) {
131168
131868
  ...gitPlan ? { git: gitPlan } : {},
131169
131869
  then: [
131170
131870
  "create the bundle in your workspace, reachable only by you (write) until you share it in the app",
131171
- "convert this folder in place into a hosted checkout: no file rewritten, a read-only .superbee/checkout.json added",
131172
- ...board ? ["unbind the Git board first; teammates keep the board branch until you tell them"] : []
131871
+ ...converts ? [
131872
+ "convert this folder in place into a hosted checkout: no file rewritten, a read-only .superbee/checkout.json added",
131873
+ ...board ? ["unbind the Git board first; teammates keep the board branch until you tell them"] : []
131874
+ ] : [uncheckable]
131173
131875
  ],
131174
131876
  network: "none (preview)",
131175
131877
  help: blockers.length === 0 ? [String(yesCommand)] : !target2 ? [`${cliInvocation()} login --host <url>`] : ["fix the blocking files, then preview again"]
@@ -131206,48 +131908,34 @@ async function publish(argv2, partial2 = {}) {
131206
131908
  help: otherWorkspace
131207
131909
  });
131208
131910
  }
131209
- const body = createBody(plan, { workspace, bundleId, name });
131210
- const digest3 = planDigest(body);
131211
- const earlier = await readPendingCreate(home2, canonical2, bundleId);
131212
- const resumes = earlier !== null && earlier.host === target2.origin && earlier.workspace === workspace;
131213
- const requestId2 = resumes ? earlier.request_id : randomUUID6();
131214
- if (!resumes) await writePendingCreate(home2, canonical2, { request_id: requestId2, host: target2.origin, workspace, bundle_id: bundleId, digest: digest3 });
131215
- const context = { bundleId, workspace, target: target2, resume: yesCommand, folder: canonical2 };
131216
- let answer2;
131217
- try {
131218
- answer2 = await client.carrier.json(`${client.prefix}/bundle-create`, body, client.signal, { maximum: CREATE_ANSWER_BYTES, writeRequest: requestId2 });
131219
- } catch (error51) {
131220
- if (error51 instanceof HostedCarrierError && error51.code === "unavailable") {
131221
- throw new CliError("TRANSIENT", `the answer from ${target2.origin} did not arrive; the bundle may be partly created`, {
131222
- details: { reason: "write_outcome_unknown", bundle_id: bundleId, workspace, host: target2.origin, request_id: requestId2, retryable: true },
131223
- help: `re-run the same command; it re-sends the same request, which finishes or confirms the creation: ${yesCommand}`
131224
- });
131225
- }
131226
- throw hostedFailure(error51, target2, yesCommand);
131227
- }
131228
- const envelope = answer2.body ?? {};
131229
- const code3 = typeof envelope.error?.code === "string" ? envelope.error.code : null;
131230
- const hostMessage = typeof envelope.error?.message === "string" ? envelope.error.message : "";
131231
- if (answer2.status === 503 && code3 === "write_outcome_unknown") {
131232
- throw new CliError("TRANSIENT", `${target2.origin} may have partly created '${bundleId}'`, {
131233
- details: { reason: "write_outcome_unknown", bundle_id: bundleId, workspace, host: target2.origin, request_id: requestId2, retryable: true },
131234
- help: `re-run the same command; it re-sends the same request, which finishes or confirms the creation: ${yesCommand}`
131235
- });
131236
- }
131237
- if (answer2.status === 429 && code3 === "bundle_create_limit") {
131238
- await clearPendingCreate(home2, canonical2, bundleId);
131239
- throw createRefusal(code3, hostMessage, context);
131240
- }
131241
- if (answer2.status === 200 && envelope.ok === false && code3 !== null) {
131242
- if (code3 !== "request_conflict") await clearPendingCreate(home2, canonical2, bundleId);
131243
- throw createRefusal(code3, hostMessage, context);
131244
- }
131245
- if (answer2.status !== 200 || envelope.ok !== true || typeof envelope.data !== "object" || envelope.data === null) {
131246
- if (answer2.status === 400) await clearPendingCreate(home2, canonical2, bundleId);
131247
- throw hostedFailure(new RemoteError(`hosted bundle-create answered ${answer2.status}`, code3 ?? "RUNTIME", answer2.status), target2, yesCommand);
131911
+ const created = await sendCreation(plan, { workspace, bundleId, name, home: home2, canonical: canonical2, target: target2, client, yesCommand, deps, json: values.json === true });
131912
+ if (!converts) {
131913
+ deps.stdout(
131914
+ render(
131915
+ {
131916
+ published: "created",
131917
+ bundle_id: bundleId,
131918
+ name,
131919
+ host: target2.origin,
131920
+ workspace,
131921
+ access: "write (only you, until you share it in the app)",
131922
+ sent: {
131923
+ documents: created.documents ?? plan.documents.length,
131924
+ reserved_files: created.reserved ?? plan.reserved.length,
131925
+ other_files: created.blobs ?? plan.blobs.length,
131926
+ history: created.history ?? { imported: plan.history.length, verified: false }
131927
+ },
131928
+ folder: canonical2,
131929
+ home: facts.home,
131930
+ checkout: `not converted: ${uncheckable}`,
131931
+ diverges: `this folder${board ? " and its Git board" : ""} and the hosted bundle '${bundleId}' are now separate copies: edits here do not reach the hosted bundle`,
131932
+ help: [hostedListCommand(target2)]
131933
+ },
131934
+ mode
131935
+ )
131936
+ );
131937
+ return;
131248
131938
  }
131249
- await clearPendingCreate(home2, canonical2, bundleId);
131250
- const created = envelope.data;
131251
131939
  await writePublishedExtras(home2, canonical2, { host: target2.origin, bundle_id: bundleId, extras: plan.extras }).catch(() => {
131252
131940
  });
131253
131941
  const slug = identity.workspaces.find((w2) => w2.tenantId === workspace)?.slug ?? null;
@@ -131348,6 +132036,7 @@ var init_publish = __esm({
131348
132036
  init_account();
131349
132037
  init_marker();
131350
132038
  init_publish_plan();
132039
+ init_publish_staged();
131351
132040
  init_publish_state();
131352
132041
  init_invocation();
131353
132042
  init_output2();
@@ -131361,9 +132050,16 @@ Usage:
131361
132050
  [--bundle-id <id>] [--name <name>] [--with-history] [--yes] [--json]
131362
132051
 
131363
132052
  Without --yes, previews only, with no network: what travels (documents, reserved files, other
131364
- files), what stays (dot-files, links), every bound the host enforces (at most 1,000 documents,
131365
- 100 other files, 1,500 objects in all, 3 MiB in one request; 64 KiB per document, 16 KiB of
131366
- frontmatter), the history plan, and what will happen to the folder.
132053
+ files), what stays (dot-files, links), how it is sent, every bound the host enforces, the history
132054
+ plan, and what will happen to the folder.
132055
+
132056
+ A bundle within one request's bounds (at most 1,000 documents, 100 other files of 1 MB each,
132057
+ 1,000 earlier versions, 1,500 objects in all, 3 MiB) is sent in one request. A larger one is sent
132058
+ staged: a list of everything, then parts of about 1 MiB, then each file, then commits until the
132059
+ host has created it, with progress on stderr (JSON lines with --json). Staged, a bundle holds at
132060
+ most 10,000 documents, 1,000 reserved files, 1,000 other files of 16 MiB each and 64 MiB of
132061
+ current files in all, and 5,000 earlier versions (64 MiB); the list of everything is at most
132062
+ 3 MiB. Every way: 64 KiB per document and reserved file, 16 KiB of frontmatter.
131367
132063
 
131368
132064
  With --yes, signs in if needed (AUTH_REQUIRED, exit 4, carries the one link to relay and the
131369
132065
  command to re-run), creates the bundle in your workspace (only you can reach it, at write, until
@@ -131378,7 +132074,7 @@ branch, or any other folder that is its own Git working tree, is refused.
131378
132074
  --with-history imports each document's earlier Git versions as labeled, unverified history
131379
132075
  (imported:git/<commit>); without it, history starts at publish. A local bundle has only its current
131380
132076
  versions. A retry after an unknown outcome (TRANSIENT) re-sends the same request, so the host
131381
- finishes or confirms the one creation.
132077
+ finishes or confirms the one creation; a staged one resumes, sending only what the host lacks.
131382
132078
 
131383
132079
  Options:
131384
132080
  --to hosted Required: the only destination