edge-book 0.6.0 → 0.7.0

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/edge-book.js +156 -2
  2. package/package.json +4 -2
package/dist/edge-book.js CHANGED
@@ -485,6 +485,7 @@ var EdgeBookStore = class {
485
485
  const contacts = await this.contacts();
486
486
  const contact = contacts[peerAgentId];
487
487
  if (!contact) throw new EdgeBookError("unknown_contact", `Unknown contact: ${peerAgentId}`);
488
+ if (contact.relationship_state === "blocked") throw new EdgeBookError("blocked", `Peer ${peerAgentId} is blocked`);
488
489
  if (contact.relationship_state !== "friend") {
489
490
  throw new EdgeBookError("not_friend", `Cannot send friend-gated message to relationship_state=${contact.relationship_state}`);
490
491
  }
@@ -1557,6 +1558,7 @@ var EdgeBookStore = class {
1557
1558
  const contacts = await this.contacts();
1558
1559
  const contact = contacts[peerAgentId];
1559
1560
  if (!contact) throw new EdgeBookError("unknown_contact", `Unknown contact: ${peerAgentId}`);
1561
+ if (contact.relationship_state === "blocked") throw new EdgeBookError("blocked", `Peer ${peerAgentId} is blocked`);
1560
1562
  if (contact.relationship_state !== "friend") throw new EdgeBookError("not_friend", `Feed denied for relationship_state=${contact.relationship_state}`);
1561
1563
  const grants = await this.grants();
1562
1564
  const grant = Object.values(grants).find(
@@ -3735,6 +3737,138 @@ async function revokeOneSession(options) {
3735
3737
  }
3736
3738
  }
3737
3739
 
3740
+ // src/resolver.ts
3741
+ function nextAction(result, target) {
3742
+ switch (result.status) {
3743
+ case "resolved":
3744
+ return `friend request ${target} --deliver`;
3745
+ case "approval_required":
3746
+ case "candidates": {
3747
+ const first = result.candidates?.[0];
3748
+ return first ? `candidates list # then: friend request ${first.candidate_id}` : "candidates list";
3749
+ }
3750
+ default:
3751
+ return "(no match \u2014 check the target)";
3752
+ }
3753
+ }
3754
+ var localContactProvider = {
3755
+ name: "local",
3756
+ priority: 100,
3757
+ async resolve(store, target) {
3758
+ const contacts = await store.contacts();
3759
+ const match = Object.values(contacts).find(
3760
+ (c) => c.peer_agent_id === target || c.aliases.includes(target) || c.display_name === target
3761
+ );
3762
+ if (!match) return null;
3763
+ return {
3764
+ kind: "card",
3765
+ agent_id: match.peer_agent_id,
3766
+ provenance: {
3767
+ source: "local",
3768
+ confidence: "high",
3769
+ display_name: match.display_name,
3770
+ reason: `known contact (relationship_state=${match.relationship_state})`
3771
+ }
3772
+ };
3773
+ }
3774
+ };
3775
+ function cardProvider(name, source, match) {
3776
+ return {
3777
+ name,
3778
+ priority: 90,
3779
+ async resolve(_store, target) {
3780
+ if (!match(target)) return null;
3781
+ const card = await loadCard(target);
3782
+ return {
3783
+ kind: "card",
3784
+ card,
3785
+ agent_id: card.agent_id,
3786
+ provenance: { source, confidence: "high", display_name: card.handle, reason: `${source} card verified` }
3787
+ };
3788
+ }
3789
+ };
3790
+ }
3791
+ var inviteProvider = cardProvider("invite", "invite", (t) => t.startsWith("edgebook:invite:"));
3792
+ var cardUrlProvider = cardProvider("card_url", "card_url", (t) => /^https?:\/\//.test(t));
3793
+ var cardFileProvider = cardProvider(
3794
+ "card_file",
3795
+ "card_file",
3796
+ (t) => t.startsWith("file://") || t.startsWith("/") || t.startsWith("./") || t.endsWith(".json")
3797
+ );
3798
+ var CANDIDATES_FILE = "candidates.json";
3799
+ function candidateKey(c) {
3800
+ return `${c.source}:${c.card_url ?? c.agent_id ?? ""}`;
3801
+ }
3802
+ async function readCandidates(store) {
3803
+ return readJson(store.file(CANDIDATES_FILE), {});
3804
+ }
3805
+ async function listCandidates(store) {
3806
+ return Object.values(await readCandidates(store));
3807
+ }
3808
+ async function getCandidate(store, id) {
3809
+ return (await readCandidates(store))[id];
3810
+ }
3811
+ async function writeCandidate(store, input) {
3812
+ const map = await readCandidates(store);
3813
+ const existing = Object.values(map).find((c) => candidateKey(c) === candidateKey(input));
3814
+ if (existing) return existing;
3815
+ const candidate = {
3816
+ candidate_id: randomId("cand"),
3817
+ approved: false,
3818
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
3819
+ ...input
3820
+ };
3821
+ map[candidate.candidate_id] = candidate;
3822
+ await writeJson(store.file(CANDIDATES_FILE), map);
3823
+ await store.audit("candidate.write", candidate.agent_id ?? "", { candidate_id: candidate.candidate_id, source: candidate.source });
3824
+ return candidate;
3825
+ }
3826
+ function defaultProviders(registryLookup = async () => null) {
3827
+ return [localContactProvider, inviteProvider, cardUrlProvider, cardFileProvider, makeRegistryProvider(registryLookup)];
3828
+ }
3829
+ async function resolveTarget(store, target, opts) {
3830
+ const ordered = [...opts.providers].sort((a, b) => b.priority - a.priority);
3831
+ for (const provider of ordered) {
3832
+ const r = await provider.resolve(store, target);
3833
+ if (!r) continue;
3834
+ if (r.kind === "card") {
3835
+ const result2 = { status: "resolved", card: r.card, agent_id: r.agent_id, provenance: r.provenance, next_action: "" };
3836
+ result2.next_action = nextAction(result2, target);
3837
+ return result2;
3838
+ }
3839
+ const candidate = await writeCandidate(store, r.candidate);
3840
+ const result = { status: "approval_required", candidates: [candidate], provenance: r.provenance, next_action: "" };
3841
+ result.next_action = nextAction(result, target);
3842
+ return result;
3843
+ }
3844
+ return { status: "not_found", next_action: "(no match \u2014 check the target)" };
3845
+ }
3846
+ async function markCandidateApproved(store, candidateId, agentId) {
3847
+ const map = await readJson(store.file(CANDIDATES_FILE), {});
3848
+ if (!map[candidateId]) return;
3849
+ map[candidateId].approved = true;
3850
+ map[candidateId].agent_id = agentId;
3851
+ await writeJson(store.file(CANDIDATES_FILE), map);
3852
+ }
3853
+ function makeRegistryProvider(lookup) {
3854
+ return {
3855
+ name: "registry",
3856
+ priority: 50,
3857
+ async resolve(_store, target) {
3858
+ if (!target.startsWith("registry:")) return null;
3859
+ const cardTarget = await lookup(target);
3860
+ if (!cardTarget) return null;
3861
+ const card = await loadCard(cardTarget);
3862
+ return {
3863
+ kind: "card",
3864
+ card,
3865
+ agent_id: card.agent_id,
3866
+ provenance: { source: "registry", confidence: "medium", display_name: card.handle, reason: "registry handle lookup" }
3867
+ };
3868
+ }
3869
+ };
3870
+ }
3871
+
3738
3872
  // src/cli.ts
3739
3873
  function usage() {
3740
3874
  return `Edge Book
@@ -3921,13 +4055,33 @@ share_owner_label: ${id.share_owner_label ? "true" : "false"} (${shared})`,
3921
4055
  return { text: inviteUrl, json: { invite_url: inviteUrl, agent_id: card.agent_id } };
3922
4056
  }
3923
4057
  }
4058
+ if (command === "resolve") {
4059
+ const target = requireArg(args.shift(), "target");
4060
+ const result = await resolveTarget(store, target, { providers: defaultProviders() });
4061
+ const label = result.agent_id ?? result.candidates?.[0]?.candidate_id ?? "";
4062
+ return { text: `${result.status} ${label}
4063
+ next: ${result.next_action}`, json: result };
4064
+ }
4065
+ if (command === "candidates") {
4066
+ const action = args.shift() || "list";
4067
+ if (action === "list") {
4068
+ const candidates = await listCandidates(store);
4069
+ const text = candidates.length ? candidates.map((c) => `${c.candidate_id} ${c.source} ${c.display_name} ${c.approved ? "[approved]" : ""}`).join("\n") : "No candidates.";
4070
+ return { text, json: { candidates } };
4071
+ }
4072
+ }
3924
4073
  if (command === "friend") {
3925
4074
  const action = args.shift();
3926
4075
  if (action === "request") {
3927
4076
  const deliver = takeBoolFlag(args, "--deliver");
3928
- const target = requireArg(args.shift(), "card-path-or-url");
3929
- const card = await loadCard(target);
4077
+ const target = requireArg(args.shift(), "card-path-url-or-candidate");
4078
+ const candidate = await getCandidate(store, target);
4079
+ if (candidate && !candidate.card_url) {
4080
+ throw new EdgeBookError("candidate_not_resolvable", "Candidate has no card_url to verify; cannot request");
4081
+ }
4082
+ const card = candidate ? await loadCard(candidate.card_url) : await loadCard(target);
3930
4083
  const envelope = await store.createFriendRequest(card);
4084
+ if (candidate) await markCandidateApproved(store, candidate.candidate_id, card.agent_id);
3931
4085
  if (deliver) {
3932
4086
  const direct = card.transports.find((entry) => entry.mode === "direct")?.endpoint;
3933
4087
  if (direct) return { text: await deliverToEndpoint(envelope, direct), json: envelope };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edge-book",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Run your own Edge Book agent and connect it to the hosted reader.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,7 +12,9 @@
12
12
  "test": "node --test test/*.test.ts",
13
13
  "harness": "node bin/edge-book.js harness two-agent",
14
14
  "harness:e2e": "node scripts/convergence-e2e.ts",
15
- "prepublishOnly": "npm run build"
15
+ "prepublishOnly": "npm run build",
16
+ "smoke": "node scripts/smoke-2agent.ts",
17
+ "smoke:host": "node scripts/smoke-2agent.ts --host"
16
18
  },
17
19
  "openclaw": {
18
20
  "extensions": [