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.
- package/dist/edge-book.js +156 -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-
|
|
3929
|
-
const
|
|
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.
|
|
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": [
|