superbee 0.3.0-pre.1 → 0.3.0-pre.2

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/SKILL.md CHANGED
@@ -86,6 +86,11 @@ asks to see it, invoke `show_document` or `show_view` in an MCP Apps host; other
86
86
  `superbee doc open <id>`.
87
87
  When the tone fits, a single 🐝 may mark a successful Superbee outcome.
88
88
 
89
+ ## Hosted checkouts
90
+
91
+ - In a folder made by `superbee checkout` the host is the authority. On `AUTH_REQUIRED` (exit 4), relay `details.sign_in_url` to the person, then re-run the same command; `superbee setup hosted` signs in and picks the default workspace in one step.
92
+ - Run `superbee sync` once at the end of a batch of edits. Any concurrent change to one document, even to different frontmatter keys, is a conflict: read `$REFS/hosted-checkout.md`, then `sync --inspect --doc <id>` and `--resolve keep|take|revise --doc <id>`. `--resolve` records a decision and never sends: run `superbee sync` after keep or revise. Deleted files sync as deletes; when sync reports `deletions_held`, never accept it yourself: name the documents and ask the person to run its `--accept-deletes` command in their own terminal (it asks them to type the count, and refuses your shell), else run `--restore-deletes`. A refusal that says to do something in the Superbee app is for the person: tell them, and never work around it.
93
+
89
94
  ## Host setup
90
95
 
91
96
  Persistent integrations require `npm install -g superbee` followed by `superbee setup`.
@@ -3701,6 +3701,7 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
3701
3701
  await delay(pollMs);
3702
3702
  continue;
3703
3703
  }
3704
+ const claimed = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
3704
3705
  try {
3705
3706
  await fs.writeFile(path.join(lockPath, OWNER_FILE), `${JSON.stringify(owner)}
3706
3707
  `, {
@@ -3709,8 +3710,11 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
3709
3710
  mode: 384
3710
3711
  });
3711
3712
  } catch (err) {
3712
- await fs.rm(lockPath, { recursive: true, force: true }).catch(() => {
3713
+ const code = err.code;
3714
+ if (code === "EEXIST") continue;
3715
+ await rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy).catch(() => {
3713
3716
  });
3717
+ if (code === "ENOENT" || code === "ENOTDIR") continue;
3714
3718
  throw err;
3715
3719
  }
3716
3720
  let inFlight;
@@ -3731,6 +3735,23 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
3731
3735
  };
3732
3736
  }
3733
3737
  }
3738
+ async function rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy) {
3739
+ const started = Date.now();
3740
+ const record = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
3741
+ if (record.state === "record") {
3742
+ if (record.owner.token === owner.token) await removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy);
3743
+ return;
3744
+ }
3745
+ if (record.state === "unreadable" || !await isClaimedDirectory(lockPath, claimed)) return;
3746
+ const remnant = releasedLockRemnantPath(lockPath, owner);
3747
+ await fs.rename(lockPath, remnant);
3748
+ await fs.rm(remnant, { recursive: true, force: true });
3749
+ }
3750
+ async function isClaimedDirectory(lockPath, claimed) {
3751
+ if (claimed === null || claimed.birthtimeNs <= 0n) return false;
3752
+ const current = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
3753
+ return current !== null && current.isDirectory() && current.dev === claimed.dev && current.ino === claimed.ino && current.birthtimeNs === claimed.birthtimeNs;
3754
+ }
3734
3755
  function changedOwnerRefusal(lockPath, current) {
3735
3756
  return new FilesystemMutationLockError(
3736
3757
  `refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`,
@@ -5894,6 +5915,7 @@ var BridgeService = class {
5894
5915
  mode: config.mode,
5895
5916
  protocol: BRIDGE_PROTOCOL,
5896
5917
  grant: launch.capability === "bundle-propose" ? "propose" : "read",
5918
+ ...config.mode === "dir" && this.options.host.kind === "oss" && launch.capability === "bundle-propose" ? { actionProtocol: "v1", actions: ["document.set-field", "document.set-body", "document.update"] } : {},
5897
5919
  host: this.hostDescriptor()
5898
5920
  })
5899
5921
  };
@@ -31655,6 +31655,7 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
31655
31655
  await delay(pollMs);
31656
31656
  continue;
31657
31657
  }
31658
+ const claimed = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
31658
31659
  try {
31659
31660
  await fs.writeFile(path.join(lockPath, OWNER_FILE), `${JSON.stringify(owner)}
31660
31661
  `, {
@@ -31663,8 +31664,11 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
31663
31664
  mode: 384
31664
31665
  });
31665
31666
  } catch (err) {
31666
- await fs.rm(lockPath, { recursive: true, force: true }).catch(() => {
31667
+ const code2 = err.code;
31668
+ if (code2 === "EEXIST") continue;
31669
+ await rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy).catch(() => {
31667
31670
  });
31671
+ if (code2 === "ENOENT" || code2 === "ENOTDIR") continue;
31668
31672
  throw err;
31669
31673
  }
31670
31674
  let inFlight;
@@ -31685,6 +31689,23 @@ async function claimLockPath(lockPath, owner, waitMs, pollMs, policy) {
31685
31689
  };
31686
31690
  }
31687
31691
  }
31692
+ async function rollBackOwnClaim(lockPath, owner, claimed, waitMs, pollMs, policy) {
31693
+ const started = Date.now();
31694
+ const record = await resolveOwnerRecord(lockPath, started, waitMs, pollMs);
31695
+ if (record.state === "record") {
31696
+ if (record.owner.token === owner.token) await removeReleasedLock(lockPath, owner, started, waitMs, pollMs, policy);
31697
+ return;
31698
+ }
31699
+ if (record.state === "unreadable" || !await isClaimedDirectory(lockPath, claimed)) return;
31700
+ const remnant = releasedLockRemnantPath(lockPath, owner);
31701
+ await fs.rename(lockPath, remnant);
31702
+ await fs.rm(remnant, { recursive: true, force: true });
31703
+ }
31704
+ async function isClaimedDirectory(lockPath, claimed) {
31705
+ if (claimed === null || claimed.birthtimeNs <= 0n) return false;
31706
+ const current = await fs.lstat(lockPath, { bigint: true }).catch(() => null);
31707
+ return current !== null && current.isDirectory() && current.dev === claimed.dev && current.ino === claimed.ino && current.birthtimeNs === claimed.birthtimeNs;
31708
+ }
31688
31709
  function changedOwnerRefusal(lockPath, current) {
31689
31710
  return new FilesystemMutationLockError(
31690
31711
  `refusing to release filesystem mutation lock '${lockPath}' because its owner token changed; the mutation may have completed, inspect the lock before retrying.`,
@@ -43094,6 +43115,7 @@ var BridgeService = class {
43094
43115
  mode: config.mode,
43095
43116
  protocol: BRIDGE_PROTOCOL,
43096
43117
  grant: launch.capability === "bundle-propose" ? "propose" : "read",
43118
+ ...config.mode === "dir" && this.options.host.kind === "oss" && launch.capability === "bundle-propose" ? { actionProtocol: "v1", actions: ["document.set-field", "document.set-body", "document.update"] } : {},
43097
43119
  host: this.hostDescriptor()
43098
43120
  })
43099
43121
  };