@magic-markdown/cli 0.3.11 → 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +327 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10997,7 +10997,7 @@ var RemoteDocumentIO = class {
10997
10997
  };
10998
10998
 
10999
10999
  // src/agent.ts
11000
- var CLI_VERSION = "0.3.11";
11000
+ var CLI_VERSION = "0.3.12";
11001
11001
  var CLI_PACKAGE_NAME = "@magic-markdown/cli";
11002
11002
  var AGENT_COMMANDS = [
11003
11003
  {
@@ -11279,6 +11279,23 @@ var AGENT_COMMANDS = [
11279
11279
  "--token (or MDOCS_BRIDGE_TOKEN) still works when a scoped bridge token is already available."
11280
11280
  ]
11281
11281
  },
11282
+ {
11283
+ name: "bridge resume",
11284
+ summary: "Backfill missed Magic changes after an agent restart, then keep the bridge running.",
11285
+ usage: "mdocs bridge resume --root <path> [--url <base-url>] [--workspace <id>] [--root-id <id>] [--request-token] [--once]",
11286
+ output: "long-running",
11287
+ mutates: true,
11288
+ examples: [
11289
+ "mdocs bridge resume --root . --request-token",
11290
+ "mdocs bridge resume --root . --once --request-token"
11291
+ ],
11292
+ notes: [
11293
+ "Reads non-secret connection defaults from .mdocs/bridge.json written by bridge setup.",
11294
+ "Use this after a sandbox/container/agent session restarts. It backfills canonical Magic changes before publishing local edits.",
11295
+ "--request-token opens a human approval URL when no MDOCS_BRIDGE_TOKEN is available; do not ask users to paste bridge tokens by default.",
11296
+ "--once performs only the backfill/reconcile step and exits."
11297
+ ]
11298
+ },
11282
11299
  {
11283
11300
  name: "bridge",
11284
11301
  summary: "Sync a local Markdown root with a Magic workspace over WebSocket (long-running).",
@@ -11347,6 +11364,32 @@ Run \`mdocs help <command>\` (or \`mdocs <command> --help\`) for usage, examples
11347
11364
  5. For a document, run \`mdocs context <path|docId> --summary --json\` locally or \`mdocs remote context <path|docId> --summary --json\` for a joined share before reading full content. Then page Markdown with \`--start-line\` / \`--end-line\` and \`--no-review\` when you only need document text.
11348
11365
  6. Pull review state separately with \`mdocs review <path|docId> --json\` locally or \`mdocs remote review <path|docId> --json\` for a joined share. Use \`mdocs comment\` / \`mdocs remote comment\` for review notes and \`mdocs suggest\` / \`mdocs remote suggest\` for proposed replacements. Do not insert comments, CriticMarkup, directives, or Magic markers into Markdown files.
11349
11366
 
11367
+ ## Filesystem Bridge / Resume
11368
+
11369
+ When a human asks you to bind an editable local Markdown filesystem to Magic Markdown, use the latest official package and the human-approval token flow:
11370
+
11371
+ \`\`\`bash
11372
+ npx --yes --package=@magic-markdown/cli@latest mdocs bridge setup \\
11373
+ --root . \\
11374
+ --workspace <workspace-id> \\
11375
+ --root-id <root-id> \\
11376
+ --url <magic-url> \\
11377
+ --actor-name "<agent name>" \\
11378
+ --request-token
11379
+ \`\`\`
11380
+
11381
+ Return the Magic approval link and keep the bridge process running after approval. Success looks like \`mdocs bridge connected ...\`.
11382
+
11383
+ If your sandbox, container, terminal, or agent session restarts after setup, do not ask for a raw bridge token. Start the latest CLI again and resume from the same root:
11384
+
11385
+ \`\`\`bash
11386
+ npx --yes --package=@magic-markdown/cli@latest mdocs bridge resume --root . --request-token
11387
+ \`\`\`
11388
+
11389
+ \`bridge resume\` reads non-secret defaults from \`.mdocs/bridge.json\`, backfills missed Magic changes before publishing local edits, and then keeps polling/watching. Use \`--once\` only when the user asked for a one-shot backfill rather than a live bridge.
11390
+
11391
+ If Magic and the local root both changed the same document while you were offline, the bridge keeps the Magic canonical path, saves your local divergent version as a \`.conflict-...\` Markdown copy, and lets the conflict copy sync as a normal file. If Magic reports a server-side conflict, wait for the human to resolve it in Magic Markdown before retrying content pushes.
11392
+
11350
11393
  ## Editing Rules
11351
11394
 
11352
11395
  - Comments and suggestions are sidecar operations. They should not modify clean Markdown until a suggestion is accepted.
@@ -13402,6 +13445,7 @@ function optionalFolderTarget(value) {
13402
13445
  // src/bridge.ts
13403
13446
  import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
13404
13447
  import { basename as basename2, resolve as resolve4 } from "node:path";
13448
+ var BRIDGE_CONFIG_PATH = ".mdocs/bridge.json";
13405
13449
  function bridgeSetupIdentity(input) {
13406
13450
  const actorName = input.actorName?.trim() || "Agent";
13407
13451
  return {
@@ -13426,7 +13470,43 @@ async function runBridgeSetup(options) {
13426
13470
  actorId: identity.actorId,
13427
13471
  actorName: identity.actorName,
13428
13472
  sourceName: identity.sourceName,
13429
- requestToken: options.requestToken || !options.token
13473
+ requestToken: options.requestToken || !options.token,
13474
+ resume: true
13475
+ });
13476
+ }
13477
+ async function runBridgeResume(options) {
13478
+ const root = resolve4(options.root);
13479
+ const config2 = await readBridgeConfig(root);
13480
+ const identity = bridgeSetupIdentity({
13481
+ actorId: options.actorId ?? config2?.actorId,
13482
+ actorName: options.actorName ?? config2?.actorName,
13483
+ sourceName: options.sourceName ?? config2?.sourceName
13484
+ });
13485
+ const workspaceId = options.workspaceId ?? config2?.workspaceId;
13486
+ const rootId = options.rootId ?? options.sourceId ?? config2?.rootId;
13487
+ const baseUrl = options.baseUrl ?? config2?.baseUrl;
13488
+ if (!workspaceId || !rootId || !baseUrl) {
13489
+ throw new Error("Missing bridge resume configuration. Run mdocs bridge setup --workspace <id> --root <path> --root-id <id> --url <base-url> first.");
13490
+ }
13491
+ await runBridge({
13492
+ root,
13493
+ workspaceId,
13494
+ rootId,
13495
+ sourceId: options.sourceId,
13496
+ sourceName: identity.sourceName,
13497
+ folderId: options.folderId ?? config2?.folderId,
13498
+ canonicalPrefix: options.canonicalPrefix ?? config2?.canonicalPrefix,
13499
+ replicaPrefix: options.replicaPrefix ?? config2?.replicaPrefix,
13500
+ claimToken: options.claimToken,
13501
+ actorId: identity.actorId,
13502
+ actorName: identity.actorName,
13503
+ baseUrl,
13504
+ intervalMs: options.intervalMs ?? config2?.intervalMs ?? 1e3,
13505
+ token: options.token,
13506
+ requestToken: options.requestToken || !options.token,
13507
+ pairingTimeoutMs: options.pairingTimeoutMs,
13508
+ once: options.once,
13509
+ resume: true
13430
13510
  });
13431
13511
  }
13432
13512
  async function runBridge(options) {
@@ -13459,8 +13539,24 @@ async function runBridge(options) {
13459
13539
  const registeredRoot = token ? await fetchScopedRoot({ ...options, token }, rootId) : await registerRoot(options, root, rootId, mapping);
13460
13540
  lastAppliedHead = lastAppliedHead ?? registeredRoot?.canonical.head;
13461
13541
  await writeSourceState(lastAppliedHead);
13542
+ await writeBridgeConfig(root, {
13543
+ schemaVersion: 1,
13544
+ workspaceId: options.workspaceId,
13545
+ rootId,
13546
+ baseUrl: options.baseUrl,
13547
+ actorId: options.actorId,
13548
+ actorName: options.actorName,
13549
+ sourceName: options.sourceName,
13550
+ folderId: options.folderId,
13551
+ canonicalPrefix: options.canonicalPrefix,
13552
+ replicaPrefix: options.replicaPrefix,
13553
+ intervalMs: options.intervalMs,
13554
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13555
+ });
13556
+ const backfilled = await resumeFromCanonical();
13557
+ if (options.once) return;
13462
13558
  connect();
13463
- if (claimMode) await primeLocalSignatures();
13559
+ if (claimMode && !backfilled) await primeLocalSignatures();
13464
13560
  else await publishSnapshot("initial");
13465
13561
  const timer = setInterval(() => {
13466
13562
  void publishSnapshot("poll").catch((error) => {
@@ -13536,6 +13632,87 @@ async function runBridge(options) {
13536
13632
  }
13537
13633
  }
13538
13634
  }
13635
+ async function resumeFromCanonical() {
13636
+ if (!options.resume && !token) return false;
13637
+ const remote = await fetchCanonicalSnapshot({ ...options, token }, rootId).catch(() => void 0);
13638
+ if (!remote) return false;
13639
+ const baseDocs = lastAppliedHead ? await fetchCommitDocuments({ ...options, token }, rootId, lastAppliedHead).catch(() => /* @__PURE__ */ new Map()) : /* @__PURE__ */ new Map();
13640
+ const remoteDocIds = new Set(remote.docs.map((doc) => doc.docId));
13641
+ let applied = 0;
13642
+ let keptLocal = 0;
13643
+ let conflicted = 0;
13644
+ for (const doc of remote.docs) {
13645
+ const base = baseDocs.get(doc.docId);
13646
+ const decision = await reconcileRemoteDocument(doc, base);
13647
+ if (decision === "applied") applied += 1;
13648
+ if (decision === "kept_local") keptLocal += 1;
13649
+ if (decision === "conflict") conflicted += 1;
13650
+ }
13651
+ for (const [docId, base] of baseDocs) {
13652
+ if (remoteDocIds.has(docId)) continue;
13653
+ const decision = await reconcileRemoteDelete(docId, base);
13654
+ if (decision === "applied") applied += 1;
13655
+ if (decision === "conflict") conflicted += 1;
13656
+ }
13657
+ lastAppliedHead = remote.head ?? lastAppliedHead;
13658
+ await writeSourceState(lastAppliedHead);
13659
+ process.stdout.write(
13660
+ `mdocs bridge resume ${root}: applied ${applied}, kept local ${keptLocal}, conflicts ${conflicted}, head ${lastAppliedHead ?? "unknown"}
13661
+ `
13662
+ );
13663
+ return true;
13664
+ }
13665
+ async function reconcileRemoteDocument(remote, base) {
13666
+ const remoteSignature = documentSignature(remote.markdown, remote.sidecar);
13667
+ const baseSignature = base ? documentSignature(base.markdown, base.sidecar) : void 0;
13668
+ const localState = await localStateForCanonical(remote);
13669
+ const localSignature = localState ? documentSignature(localState.markdown, localState.sidecar) : void 0;
13670
+ if (!localState) {
13671
+ await applyCanonicalDocument(remote);
13672
+ return "applied";
13673
+ }
13674
+ if (localSignature === remoteSignature && localState.path === remote.path && localState.docId === remote.docId) {
13675
+ signatures.set(remote.docId, remoteSignature);
13676
+ seenDocs.add(remote.docId);
13677
+ return "noop";
13678
+ }
13679
+ if (localSignature === remoteSignature && localState.path === remote.path) {
13680
+ await applyCanonicalDocument(remote);
13681
+ return "applied";
13682
+ }
13683
+ if (baseSignature && localSignature === baseSignature) {
13684
+ await applyCanonicalDocument(remote);
13685
+ return "applied";
13686
+ }
13687
+ if (baseSignature && remoteSignature === baseSignature) {
13688
+ signatures.set(remote.docId, baseSignature);
13689
+ seenDocs.add(remote.docId);
13690
+ return "kept_local";
13691
+ }
13692
+ signatures.set(remote.docId, baseSignature ?? remoteSignature);
13693
+ await applyCanonicalDocument(remote);
13694
+ return "conflict";
13695
+ }
13696
+ async function reconcileRemoteDelete(docId, base) {
13697
+ const localState = await getDocumentState(io, docId).catch(() => void 0);
13698
+ if (!localState) return "noop";
13699
+ const baseSignature = documentSignature(base.markdown, base.sidecar);
13700
+ const localSignature = documentSignature(localState.markdown, localState.sidecar);
13701
+ if (localSignature !== baseSignature) {
13702
+ const copyPath = conflictCopyPath(localState.path);
13703
+ await io.writeTextAtomic(copyPath, localState.markdown);
13704
+ await seedConflictCopySidecar(copyPath, localState);
13705
+ process.stderr.write(`local conflict ${localState.path}; local version saved as ${copyPath}, canonical delete applied
13706
+ `);
13707
+ }
13708
+ await io.deleteFile(localState.path).catch(() => void 0);
13709
+ await io.deleteFile(sidecarPath(docId)).catch(() => void 0);
13710
+ await removeManifestDocument(docId, localState.path);
13711
+ signatures.delete(docId);
13712
+ seenDocs.delete(docId);
13713
+ pendingDeletes.delete(docId);
13714
+ return localSignature === baseSignature ? "applied" : "conflict";
13715
+ }
13539
13716
  async function handleIncoming(message) {
13540
13717
  if (message.type === "file-changed") {
13541
13718
  await applyCanonicalDocument(readDocumentPayload(message.payload));
@@ -13561,7 +13738,7 @@ async function runBridge(options) {
13561
13738
  `);
13562
13739
  if (!payload.docId) return;
13563
13740
  pendingDocs.delete(payload.docId);
13564
- const canonical = await fetchCanonicalDocument(payload.docId);
13741
+ const canonical = await fetchCanonicalDocument2(payload.docId);
13565
13742
  if (canonical) await applyCanonicalDocument(canonical);
13566
13743
  }
13567
13744
  if (message.type === "sync-error") {
@@ -13576,7 +13753,7 @@ async function runBridge(options) {
13576
13753
  }
13577
13754
  async function applyCanonicalDocument(payload) {
13578
13755
  const remoteSignature = documentSignature(payload.markdown, payload.sidecar);
13579
- const localState = await getDocumentState(io, payload.docId).catch(() => void 0);
13756
+ const localState = await localStateForCanonical(payload);
13580
13757
  const localSignature = localState ? documentSignature(localState.markdown, localState.sidecar) : void 0;
13581
13758
  if (localSignature && signatures.has(payload.docId) && localSignature !== signatures.get(payload.docId) && localSignature !== remoteSignature) {
13582
13759
  const copyPath = conflictCopyPath(payload.path);
@@ -13598,9 +13775,14 @@ async function runBridge(options) {
13598
13775
  if (localState && localState.path !== payload.path) {
13599
13776
  await io.deleteFile(localState.path).catch(() => void 0);
13600
13777
  }
13778
+ if (localState && localState.docId !== payload.docId) {
13779
+ await io.deleteFile(sidecarPath(localState.docId)).catch(() => void 0);
13780
+ await removeManifestDocument(localState.docId, localState.path);
13781
+ }
13601
13782
  await io.writeTextAtomic(payload.path, payload.markdown);
13602
13783
  await io.writeTextAtomic(sidecarPath(payload.docId), `${JSON.stringify(payload.sidecar, null, 2)}
13603
13784
  `);
13785
+ await upsertManifestDocument(payload);
13604
13786
  signatures.set(payload.docId, remoteSignature);
13605
13787
  deniedSignatures.delete(payload.docId);
13606
13788
  seenDocs.add(payload.docId);
@@ -13636,7 +13818,14 @@ async function runBridge(options) {
13636
13818
  updatedAt: now
13637
13819
  });
13638
13820
  }
13639
- async function fetchCanonicalDocument(docId) {
13821
+ async function localStateForCanonical(payload) {
13822
+ const byDocId = await getDocumentState(io, payload.docId).catch(() => void 0);
13823
+ if (byDocId) return byDocId;
13824
+ const map = await getWorkspaceMap(io).catch(() => void 0);
13825
+ const byPath = map?.docs.find((doc) => doc.path === payload.path);
13826
+ return byPath ? getDocumentState(io, byPath.docId).catch(() => void 0) : void 0;
13827
+ }
13828
+ async function fetchCanonicalDocument2(docId) {
13640
13829
  try {
13641
13830
  const response = await fetch(
13642
13831
  new URL(
@@ -13701,6 +13890,34 @@ async function runBridge(options) {
13701
13890
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13702
13891
  });
13703
13892
  }
13893
+ async function upsertManifestDocument(payload) {
13894
+ const manifest = await readManifest(io).catch(() => void 0);
13895
+ if (!manifest) return;
13896
+ await writeManifest(io, {
13897
+ ...manifest,
13898
+ docs: [
13899
+ ...manifest.docs.filter((doc) => doc.docId !== payload.docId && doc.path !== payload.path),
13900
+ {
13901
+ docId: payload.docId,
13902
+ path: payload.path,
13903
+ title: payload.sidecar.title || titleFromMarkdown(payload.path, payload.markdown),
13904
+ contentHash: contentHashForText(payload.markdown),
13905
+ currentSha: payload.canonicalHead ?? payload.currentSha,
13906
+ updatedAt: payload.sidecar.updatedAt
13907
+ }
13908
+ ].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0),
13909
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13910
+ });
13911
+ }
13912
+ async function removeManifestDocument(docId, path) {
13913
+ const manifest = await readManifest(io).catch(() => void 0);
13914
+ if (!manifest) return;
13915
+ await writeManifest(io, {
13916
+ ...manifest,
13917
+ docs: manifest.docs.filter((doc) => doc.docId !== docId && doc.path !== path),
13918
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13919
+ });
13920
+ }
13704
13921
  }
13705
13922
  async function requestBridgeToken(options, rootId) {
13706
13923
  const response = await fetch(new URL("/api/bridge-requests", options.baseUrl), {
@@ -13754,6 +13971,83 @@ async function requestBridgeToken(options, rootId) {
13754
13971
  }
13755
13972
  throw new Error("Bridge approval timed out before a token was issued.");
13756
13973
  }
13974
+ async function readBridgeConfig(root) {
13975
+ const io = new NodeWorkspaceIO(root);
13976
+ try {
13977
+ const config2 = JSON.parse(await io.readText(BRIDGE_CONFIG_PATH));
13978
+ if (config2.schemaVersion !== 1 || typeof config2.workspaceId !== "string" || typeof config2.rootId !== "string" || typeof config2.baseUrl !== "string" || typeof config2.actorId !== "string") {
13979
+ return void 0;
13980
+ }
13981
+ return config2;
13982
+ } catch {
13983
+ return void 0;
13984
+ }
13985
+ }
13986
+ async function writeBridgeConfig(root, config2) {
13987
+ const io = new NodeWorkspaceIO(root);
13988
+ await io.mkdir(".mdocs");
13989
+ await io.writeTextAtomic(BRIDGE_CONFIG_PATH, `${JSON.stringify(config2, null, 2)}
13990
+ `);
13991
+ }
13992
+ async function fetchCanonicalSnapshot(options, rootId) {
13993
+ const response = await fetch(new URL(`/api/workspaces/${encodeURIComponent(options.workspaceId)}/roots/${encodeURIComponent(rootId)}/sync`, options.baseUrl), {
13994
+ headers: authHeaders(options.token)
13995
+ });
13996
+ if (!response.ok) return void 0;
13997
+ const snapshot = await response.json();
13998
+ const docs = await Promise.all(
13999
+ (snapshot.tree?.docs ?? []).map((doc) => typeof doc.docId === "string" ? doc.docId : void 0).filter((docId) => Boolean(docId)).map((docId) => fetchCanonicalDocument(options, rootId, docId))
14000
+ );
14001
+ return {
14002
+ head: snapshot.tree?.root?.canonical.head,
14003
+ docs: docs.filter((doc) => Boolean(doc))
14004
+ };
14005
+ }
14006
+ async function fetchCommitDocuments(options, rootId, headId) {
14007
+ const response = await fetch(
14008
+ new URL(
14009
+ `/api/workspaces/${encodeURIComponent(options.workspaceId)}/roots/${encodeURIComponent(rootId)}/commits/${encodeURIComponent(headId)}?content=1`,
14010
+ options.baseUrl
14011
+ ),
14012
+ { headers: authHeaders(options.token) }
14013
+ );
14014
+ if (!response.ok) return /* @__PURE__ */ new Map();
14015
+ const payload = await response.json();
14016
+ const docs = (payload.docs ?? []).map((doc) => readCommitDocumentPayload(doc, options.workspaceId, rootId, headId)).filter((doc) => Boolean(doc));
14017
+ return new Map(docs.map((doc) => [doc.docId, doc]));
14018
+ }
14019
+ async function fetchCanonicalDocument(options, rootId, docId) {
14020
+ try {
14021
+ const response = await fetch(
14022
+ new URL(
14023
+ `/api/workspaces/${encodeURIComponent(options.workspaceId)}/roots/${encodeURIComponent(rootId)}/documents/${encodeURIComponent(docId)}`,
14024
+ options.baseUrl
14025
+ ),
14026
+ { headers: authHeaders(options.token) }
14027
+ );
14028
+ if (!response.ok) return void 0;
14029
+ const document = await response.json();
14030
+ return readDocumentPayload({ ...document, canonicalHead: document.currentSha });
14031
+ } catch {
14032
+ return void 0;
14033
+ }
14034
+ }
14035
+ function readCommitDocumentPayload(payload, workspaceId, rootId, headId) {
14036
+ if (!payload || typeof payload !== "object") return void 0;
14037
+ const value = payload;
14038
+ if (typeof value.markdown !== "string" || !value.sidecar || typeof value.sidecar !== "object") return void 0;
14039
+ return readDocumentPayload({
14040
+ workspaceId,
14041
+ rootId,
14042
+ docId: value.docId,
14043
+ path: value.path,
14044
+ title: value.title,
14045
+ markdown: value.markdown,
14046
+ sidecar: value.sidecar,
14047
+ currentSha: headId,
14048
+ canonicalHead: headId
14049
+ });
14050
+ }
13757
14051
  function parseBridgeSyncMessage(data) {
13758
14052
  if (!data.trim()) return void 0;
13759
14053
  try {
@@ -14063,9 +14357,9 @@ async function main() {
14063
14357
  return;
14064
14358
  }
14065
14359
  case "bridge": {
14066
- const options = {
14067
- root: resolve5(String(parsed.flags.root ?? cwd)),
14068
- workspaceId: String(parsed.flags.workspace ?? "workspace_demo"),
14360
+ const root = resolve5(String(parsed.flags.root ?? cwd));
14361
+ const baseOptions = {
14362
+ root,
14069
14363
  rootId: typeof parsed.flags["root-id"] === "string" ? parsed.flags["root-id"] : void 0,
14070
14364
  sourceId: typeof parsed.flags.source === "string" ? parsed.flags.source : void 0,
14071
14365
  sourceName: typeof parsed.flags["source-name"] === "string" ? parsed.flags["source-name"] : void 0,
@@ -14075,11 +14369,24 @@ async function main() {
14075
14369
  claimToken: typeof parsed.flags.claim === "string" ? parsed.flags.claim : void 0,
14076
14370
  actorId: typeof parsed.flags.actor === "string" ? parsed.flags.actor : void 0,
14077
14371
  actorName: typeof parsed.flags["actor-name"] === "string" ? parsed.flags["actor-name"] : void 0,
14078
- baseUrl: bridgeBaseUrl(parsed.flags),
14079
14372
  intervalMs: Number(parsed.flags.interval ?? 1e3),
14080
14373
  token: typeof parsed.flags.token === "string" ? parsed.flags.token : process.env.MDOCS_BRIDGE_TOKEN || void 0,
14081
14374
  requestToken: Boolean(parsed.flags["request-token"] || parsed.flags.pair),
14082
- pairingTimeoutMs: typeof parsed.flags["pairing-timeout-ms"] === "string" ? Number(parsed.flags["pairing-timeout-ms"]) : void 0
14375
+ pairingTimeoutMs: typeof parsed.flags["pairing-timeout-ms"] === "string" ? Number(parsed.flags["pairing-timeout-ms"]) : void 0,
14376
+ once: Boolean(parsed.flags.once)
14377
+ };
14378
+ if (subcommand === "resume") {
14379
+ await runBridgeResume({
14380
+ ...baseOptions,
14381
+ workspaceId: typeof parsed.flags.workspace === "string" ? parsed.flags.workspace : void 0,
14382
+ baseUrl: optionalBridgeBaseUrl(parsed.flags)
14383
+ });
14384
+ return;
14385
+ }
14386
+ const options = {
14387
+ ...baseOptions,
14388
+ workspaceId: typeof parsed.flags.workspace === "string" ? parsed.flags.workspace : "workspace_demo",
14389
+ baseUrl: bridgeBaseUrl(parsed.flags)
14083
14390
  };
14084
14391
  if (subcommand === "setup") await runBridgeSetup(options);
14085
14392
  else await runBridge({ ...options, actorId: options.actorId ?? "actor_local" });
@@ -14183,13 +14490,17 @@ function requiredFlag2(flags, name) {
14183
14490
  return value;
14184
14491
  }
14185
14492
  function bridgeBaseUrl(flags) {
14186
- if (typeof flags.url === "string" && flags.url.trim()) return flags.url.trim();
14187
- const configured = process.env.MDOCS_BASE_URL?.trim();
14493
+ const configured = optionalBridgeBaseUrl(flags);
14188
14494
  if (configured) return configured;
14189
14495
  throw new CliError("usage_error", "Missing --url <base-url> for mdocs bridge.", {
14190
14496
  hint: "Pass the Magic Markdown origin explicitly (for example --url https://magic.example.com) or set MDOCS_BASE_URL. The bridge does not assume a localhost server."
14191
14497
  });
14192
14498
  }
14499
+ function optionalBridgeBaseUrl(flags) {
14500
+ if (typeof flags.url === "string" && flags.url.trim()) return flags.url.trim();
14501
+ const configured = process.env.MDOCS_BASE_URL?.trim();
14502
+ return configured || void 0;
14503
+ }
14193
14504
  async function readRequiredTextFlag2(flags, cwd, names) {
14194
14505
  const value = await readOptionalTextFlag2(flags, cwd, names);
14195
14506
  if (value === void 0) {
@@ -14274,6 +14585,9 @@ Commands:
14274
14585
  bridge setup --workspace <id> --root . --url <base-url> [--folder-id <id>] --request-token
14275
14586
  Initialize, validate, request approval,
14276
14587
  and start an agent filesystem bridge
14588
+ bridge resume --root . --request-token [--once]
14589
+ Backfill missed Magic changes, then keep
14590
+ the bridge running unless --once is set
14277
14591
  bridge --workspace <id> --root . --url <base-url> --request-token
14278
14592
  Request human approval, then sync an
14279
14593
  approved local root with the workspace
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magic-markdown/cli",
3
- "version": "0.3.11",
3
+ "version": "0.3.12",
4
4
  "description": "Magic Markdown agent CLI (mdocs): read, review, comment on, suggest edits to, and sync clean Markdown workspaces.",
5
5
  "type": "module",
6
6
  "license": "MIT",