edge-book 0.5.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 +333 -6
- package/package.json +4 -2
package/dist/edge-book.js
CHANGED
|
@@ -54,6 +54,7 @@ var SIGNALS_FILE = "signals.json";
|
|
|
54
54
|
var CAPABILITIES_FILE = "capabilities.json";
|
|
55
55
|
var EPHEMERAL_FILE = "ephemeral-posts.json";
|
|
56
56
|
var ANSWERS_FILE = "answers.json";
|
|
57
|
+
var RECEIVED_POSTS_FILE = "received-posts.json";
|
|
57
58
|
var DEFAULT_SIGNAL_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
58
59
|
var DEFAULT_EPHEMERAL_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
59
60
|
function resolveHome(home) {
|
|
@@ -484,6 +485,7 @@ var EdgeBookStore = class {
|
|
|
484
485
|
const contacts = await this.contacts();
|
|
485
486
|
const contact = contacts[peerAgentId];
|
|
486
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`);
|
|
487
489
|
if (contact.relationship_state !== "friend") {
|
|
488
490
|
throw new EdgeBookError("not_friend", `Cannot send friend-gated message to relationship_state=${contact.relationship_state}`);
|
|
489
491
|
}
|
|
@@ -672,6 +674,18 @@ var EdgeBookStore = class {
|
|
|
672
674
|
const { signature, lifecycle: _lc, ...signedPayload } = sig;
|
|
673
675
|
return verifyPayload(signedPayload, signature, pub);
|
|
674
676
|
}
|
|
677
|
+
// Verify an Endorsement signature. Endorsements have no lifecycle field.
|
|
678
|
+
async verifyEndorsement(e) {
|
|
679
|
+
const identity = await this.identity();
|
|
680
|
+
let pub = identity.agent_id === e.endorser_agent_id ? identity.public_key_pem : void 0;
|
|
681
|
+
if (!pub) {
|
|
682
|
+
const c = (await this.contacts())[e.endorser_agent_id];
|
|
683
|
+
pub = c?.public_keys?.[0]?.public_key_pem;
|
|
684
|
+
}
|
|
685
|
+
if (!pub) return false;
|
|
686
|
+
const { signature, ...rest } = e;
|
|
687
|
+
return verifyPayload(rest, signature, pub);
|
|
688
|
+
}
|
|
675
689
|
// Class 3: Endorse — actor-owned reified edge, strongRef parent, evidence link (R5, R8)
|
|
676
690
|
async endorsements() {
|
|
677
691
|
return readJson(this.file(ENDORSEMENTS_FILE), {});
|
|
@@ -1119,6 +1133,123 @@ var EdgeBookStore = class {
|
|
|
1119
1133
|
throw new EdgeBookError("invalid_grant_signature", "Grant signature does not verify against the issuer key");
|
|
1120
1134
|
}
|
|
1121
1135
|
}
|
|
1136
|
+
// ─── Received posts (peer posts delivered via mailbox) ──────────────────────
|
|
1137
|
+
async receivedPosts() {
|
|
1138
|
+
return readJson(this.file(RECEIVED_POSTS_FILE), {});
|
|
1139
|
+
}
|
|
1140
|
+
async saveReceivedPosts(posts) {
|
|
1141
|
+
await writeJson(this.file(RECEIVED_POSTS_FILE), posts);
|
|
1142
|
+
}
|
|
1143
|
+
/** Grouped view for `/api/received` and the reader. */
|
|
1144
|
+
async receivedByCategory() {
|
|
1145
|
+
const all = await this.receivedPosts();
|
|
1146
|
+
const out = {
|
|
1147
|
+
signals: {},
|
|
1148
|
+
ephemeral: {},
|
|
1149
|
+
answers: {},
|
|
1150
|
+
endorsements: {}
|
|
1151
|
+
};
|
|
1152
|
+
for (const id of Object.keys(all)) {
|
|
1153
|
+
const p = all[id];
|
|
1154
|
+
if (p.post_type === "signal") out.signals[id] = p;
|
|
1155
|
+
else if (p.post_type === "answer") out.answers[id] = p;
|
|
1156
|
+
else if (p.post_type === "endorse") out.endorsements[id] = p;
|
|
1157
|
+
else out.ephemeral[id] = p;
|
|
1158
|
+
}
|
|
1159
|
+
return out;
|
|
1160
|
+
}
|
|
1161
|
+
async verifyReceivedPost(p) {
|
|
1162
|
+
switch (p.post_type) {
|
|
1163
|
+
case "signal":
|
|
1164
|
+
return this.verifySignal(p);
|
|
1165
|
+
case "answer":
|
|
1166
|
+
return this.verifyAnswer(p);
|
|
1167
|
+
case "endorse":
|
|
1168
|
+
return this.verifyEndorsement(p);
|
|
1169
|
+
case "query":
|
|
1170
|
+
case "share":
|
|
1171
|
+
case "coordinate":
|
|
1172
|
+
case "delegation_request":
|
|
1173
|
+
return this.verifyEphemeral(p);
|
|
1174
|
+
default:
|
|
1175
|
+
return false;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
receivedPostId(p) {
|
|
1179
|
+
return p.signal_id || p.post_id || p.answer_id || p.endorse_id || "";
|
|
1180
|
+
}
|
|
1181
|
+
receivedPostAuthor(p) {
|
|
1182
|
+
switch (p.post_type) {
|
|
1183
|
+
case "answer":
|
|
1184
|
+
return p.answerer_agent_id ?? "";
|
|
1185
|
+
case "endorse":
|
|
1186
|
+
return p.endorser_agent_id ?? "";
|
|
1187
|
+
case "signal":
|
|
1188
|
+
case "query":
|
|
1189
|
+
case "share":
|
|
1190
|
+
case "coordinate":
|
|
1191
|
+
case "delegation_request":
|
|
1192
|
+
return p.from_agent ?? "";
|
|
1193
|
+
default:
|
|
1194
|
+
return "";
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Receive a `post_publish` envelope from a friend.
|
|
1199
|
+
* Security order:
|
|
1200
|
+
* 1. verifyEnvelope (recipient/expiry/replay/sender-key + envelope sig)
|
|
1201
|
+
* 2. type guard: must be "post_publish"
|
|
1202
|
+
* 3. sender must be a known contact with relationship_state === "friend"
|
|
1203
|
+
* 4. inner post author must match envelope.from_agent_id
|
|
1204
|
+
* 5. inner post signature must verify
|
|
1205
|
+
* Only then store.
|
|
1206
|
+
*/
|
|
1207
|
+
async receivePostPublish(envelope) {
|
|
1208
|
+
await this.verifyEnvelope(envelope);
|
|
1209
|
+
if (envelope.type !== "post_publish") {
|
|
1210
|
+
throw new EdgeBookError("wrong_message_type", "Expected post_publish envelope");
|
|
1211
|
+
}
|
|
1212
|
+
const contact = (await this.contacts())[envelope.from_agent_id];
|
|
1213
|
+
if (!contact || contact.relationship_state !== "friend") {
|
|
1214
|
+
throw new EdgeBookError("not_friend", "post_publish only accepted from friends");
|
|
1215
|
+
}
|
|
1216
|
+
const post = envelope.body.post;
|
|
1217
|
+
if (!post || !post.post_type) {
|
|
1218
|
+
throw new EdgeBookError("malformed_post_publish", "missing or malformed post in envelope body");
|
|
1219
|
+
}
|
|
1220
|
+
if (this.receivedPostAuthor(post) !== envelope.from_agent_id) {
|
|
1221
|
+
throw new EdgeBookError("author_mismatch", "post author does not match envelope sender");
|
|
1222
|
+
}
|
|
1223
|
+
const id = this.receivedPostId(post);
|
|
1224
|
+
if (!id) {
|
|
1225
|
+
throw new EdgeBookError("malformed_post_publish", "post missing id");
|
|
1226
|
+
}
|
|
1227
|
+
if (!await this.verifyReceivedPost(post)) {
|
|
1228
|
+
throw new EdgeBookError("invalid_signature", "inner post signature invalid");
|
|
1229
|
+
}
|
|
1230
|
+
const all = await this.receivedPosts();
|
|
1231
|
+
const key = envelope.from_agent_id + ":" + id;
|
|
1232
|
+
all[key] = post;
|
|
1233
|
+
await this.saveReceivedPosts(all);
|
|
1234
|
+
await this.audit("post.receive", envelope.from_agent_id, {
|
|
1235
|
+
post_type: post.post_type,
|
|
1236
|
+
id
|
|
1237
|
+
});
|
|
1238
|
+
return post;
|
|
1239
|
+
}
|
|
1240
|
+
/** Build a signed `post_publish` envelope wrapping any post type. */
|
|
1241
|
+
async signPostPublishEnvelope(input) {
|
|
1242
|
+
const identity = await this.identity();
|
|
1243
|
+
return this.signEnvelope({
|
|
1244
|
+
type: "post_publish",
|
|
1245
|
+
to_agent_id: input.to_agent_id,
|
|
1246
|
+
relationship_id: relationshipId(identity.agent_id, input.to_agent_id),
|
|
1247
|
+
capability_id: "",
|
|
1248
|
+
ref: "",
|
|
1249
|
+
transport: "direct",
|
|
1250
|
+
body: { post: input.post }
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1122
1253
|
async signEnvelope(input) {
|
|
1123
1254
|
const identity = await this.identity();
|
|
1124
1255
|
const unsigned = {
|
|
@@ -1168,6 +1299,10 @@ var EdgeBookStore = class {
|
|
|
1168
1299
|
await this.receiveObjectRevoke(envelope);
|
|
1169
1300
|
return;
|
|
1170
1301
|
}
|
|
1302
|
+
if (envelope.type === "post_publish") {
|
|
1303
|
+
await this.receivePostPublish(envelope);
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1171
1306
|
throw new EdgeBookError("unsupported_envelope", `Unsupported envelope type: ${envelope.type}`);
|
|
1172
1307
|
}
|
|
1173
1308
|
async audit(action, peerAgentId, details) {
|
|
@@ -1423,6 +1558,7 @@ var EdgeBookStore = class {
|
|
|
1423
1558
|
const contacts = await this.contacts();
|
|
1424
1559
|
const contact = contacts[peerAgentId];
|
|
1425
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`);
|
|
1426
1562
|
if (contact.relationship_state !== "friend") throw new EdgeBookError("not_friend", `Feed denied for relationship_state=${contact.relationship_state}`);
|
|
1427
1563
|
const grants = await this.grants();
|
|
1428
1564
|
const grant = Object.values(grants).find(
|
|
@@ -1774,6 +1910,10 @@ async function handleOwnerApi(req, res, url, adapters) {
|
|
|
1774
1910
|
sendJson(res, 200, { ephemeral: await store.ephemeralPosts() });
|
|
1775
1911
|
return true;
|
|
1776
1912
|
}
|
|
1913
|
+
if (req.method === "GET" && url.pathname === "/api/received") {
|
|
1914
|
+
sendJson(res, 200, await store.receivedByCategory());
|
|
1915
|
+
return true;
|
|
1916
|
+
}
|
|
1777
1917
|
if (req.method === "GET" && url.pathname === "/api/answers") {
|
|
1778
1918
|
sendJson(res, 200, { answers: await store.answers() });
|
|
1779
1919
|
return true;
|
|
@@ -3597,6 +3737,138 @@ async function revokeOneSession(options) {
|
|
|
3597
3737
|
}
|
|
3598
3738
|
}
|
|
3599
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
|
+
|
|
3600
3872
|
// src/cli.ts
|
|
3601
3873
|
function usage() {
|
|
3602
3874
|
return `Edge Book
|
|
@@ -3697,6 +3969,17 @@ async function deliverToPeer(store, envelope, peerAgentId) {
|
|
|
3697
3969
|
}
|
|
3698
3970
|
throw new EdgeBookError("no_route", `No direct or relay endpoint for ${peerAgentId}`);
|
|
3699
3971
|
}
|
|
3972
|
+
async function broadcastPost(store, host, socketFactory2, post) {
|
|
3973
|
+
const contacts = await store.contacts();
|
|
3974
|
+
const friends = Object.values(contacts).filter((c) => c.relationship_state === "friend");
|
|
3975
|
+
let count = 0;
|
|
3976
|
+
for (const f of friends) {
|
|
3977
|
+
const envelope = await store.signPostPublishEnvelope({ to_agent_id: f.peer_agent_id, post });
|
|
3978
|
+
await deliverEnvelopeViaMailbox({ home: store.home, host, socketFactory: socketFactory2, envelope });
|
|
3979
|
+
count++;
|
|
3980
|
+
}
|
|
3981
|
+
return count;
|
|
3982
|
+
}
|
|
3700
3983
|
function serverAddress(server) {
|
|
3701
3984
|
const address = server.address();
|
|
3702
3985
|
if (!address || typeof address === "string") return String(address);
|
|
@@ -3772,13 +4055,33 @@ share_owner_label: ${id.share_owner_label ? "true" : "false"} (${shared})`,
|
|
|
3772
4055
|
return { text: inviteUrl, json: { invite_url: inviteUrl, agent_id: card.agent_id } };
|
|
3773
4056
|
}
|
|
3774
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
|
+
}
|
|
3775
4073
|
if (command === "friend") {
|
|
3776
4074
|
const action = args.shift();
|
|
3777
4075
|
if (action === "request") {
|
|
3778
4076
|
const deliver = takeBoolFlag(args, "--deliver");
|
|
3779
|
-
const target = requireArg(args.shift(), "card-path-or-
|
|
3780
|
-
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);
|
|
3781
4083
|
const envelope = await store.createFriendRequest(card);
|
|
4084
|
+
if (candidate) await markCandidateApproved(store, candidate.candidate_id, card.agent_id);
|
|
3782
4085
|
if (deliver) {
|
|
3783
4086
|
const direct = card.transports.find((entry) => entry.mode === "direct")?.endpoint;
|
|
3784
4087
|
if (direct) return { text: await deliverToEndpoint(envelope, direct), json: envelope };
|
|
@@ -4007,22 +4310,34 @@ ${JSON.stringify(result, null, 2)}`, json: result };
|
|
|
4007
4310
|
return { text: `Attestation ${id.attestation_id}`, json: id };
|
|
4008
4311
|
}
|
|
4009
4312
|
if (command === "endorse") {
|
|
4313
|
+
const deliver = takeBoolFlag(args, "--deliver");
|
|
4314
|
+
const hostUrl = parseHost(args, ctx);
|
|
4010
4315
|
const subject = requireArg(args.shift(), "<subject-agent-id>");
|
|
4011
4316
|
const evAtt = takeFlag(args, "--evidence-attestation");
|
|
4012
4317
|
const evTask = takeFlag(args, "--evidence-task");
|
|
4013
|
-
const
|
|
4318
|
+
const post = await store.createEndorsement({
|
|
4014
4319
|
subject_agent_id: subject,
|
|
4015
4320
|
parent: { uri: requireArg(takeFlag(args, "--parent-uri"), "--parent-uri"), hash: requireArg(takeFlag(args, "--parent-hash"), "--parent-hash") },
|
|
4016
4321
|
...evAtt ? { evidence_ref: { uri: `edgebook:attestation:${evAtt}`, hash: evAtt } } : {},
|
|
4017
4322
|
...evTask ? { evidence_task_id: evTask } : {},
|
|
4018
4323
|
statement: requireArg(takeFlag(args, "--statement"), "--statement")
|
|
4019
4324
|
});
|
|
4020
|
-
|
|
4325
|
+
if (deliver) {
|
|
4326
|
+
const n = await broadcastPost(store, hostUrl, ctx.socketFactory, post);
|
|
4327
|
+
return { text: `Endorsement ${post.endorse_id} \u2014 delivered to ${n} friend(s)`, json: { post, delivered: n } };
|
|
4328
|
+
}
|
|
4329
|
+
return { text: `Endorsement ${post.endorse_id}`, json: post };
|
|
4021
4330
|
}
|
|
4022
4331
|
if (command === "signal") {
|
|
4332
|
+
const deliver = takeBoolFlag(args, "--deliver");
|
|
4333
|
+
const hostUrl = parseHost(args, ctx);
|
|
4023
4334
|
const ttl = takeFlag(args, "--ttl-ms");
|
|
4024
|
-
const
|
|
4025
|
-
|
|
4335
|
+
const post = await store.createSignal({ body: requireArg(takeFlag(args, "--body"), "--body"), ttlMs: ttl ? Number(ttl) : void 0 });
|
|
4336
|
+
if (deliver) {
|
|
4337
|
+
const n = await broadcastPost(store, hostUrl, ctx.socketFactory, post);
|
|
4338
|
+
return { text: `Signal ${post.signal_id} \u2014 delivered to ${n} friend(s)`, json: { post, delivered: n } };
|
|
4339
|
+
}
|
|
4340
|
+
return { text: `Signal ${post.signal_id}`, json: post };
|
|
4026
4341
|
}
|
|
4027
4342
|
if (command === "capability") {
|
|
4028
4343
|
const action = args.shift() || "list";
|
|
@@ -4045,15 +4360,23 @@ ${JSON.stringify(result, null, 2)}`, json: result };
|
|
|
4045
4360
|
throw new EdgeBookError("unknown_action", `Unknown capability action: ${action}`);
|
|
4046
4361
|
}
|
|
4047
4362
|
if (command === "query" || command === "share" || command === "coordinate" || command === "delegate") {
|
|
4363
|
+
const deliver = takeBoolFlag(args, "--deliver");
|
|
4364
|
+
const hostUrl = parseHost(args, ctx);
|
|
4048
4365
|
const type = command === "delegate" ? "delegation_request" : command;
|
|
4049
4366
|
const body = requireArg(takeFlag(args, "--body"), "--body");
|
|
4050
4367
|
const to = takeFlag(args, "--to") || takeFlag(args, "--with");
|
|
4051
4368
|
const ref = takeFlag(args, "--ref");
|
|
4052
4369
|
const ttl = takeFlag(args, "--ttl-ms");
|
|
4053
4370
|
const post = await store.createEphemeral(type, { body, subject_agent_id: to, ref, ttlMs: ttl ? Number(ttl) : void 0 });
|
|
4371
|
+
if (deliver) {
|
|
4372
|
+
const n = await broadcastPost(store, hostUrl, ctx.socketFactory, post);
|
|
4373
|
+
return { text: `${post.post_type} ${post.post_id} \u2014 delivered to ${n} friend(s)`, json: { post, delivered: n } };
|
|
4374
|
+
}
|
|
4054
4375
|
return { text: `${post.post_type} ${post.post_id}`, json: post };
|
|
4055
4376
|
}
|
|
4056
4377
|
if (command === "answer") {
|
|
4378
|
+
const deliver = takeBoolFlag(args, "--deliver");
|
|
4379
|
+
const hostUrl = parseHost(args, ctx);
|
|
4057
4380
|
const queryId = requireArg(args.shift(), "<query-id>");
|
|
4058
4381
|
const ephemeral = await store.ephemeralPosts();
|
|
4059
4382
|
const query = ephemeral[queryId];
|
|
@@ -4063,6 +4386,10 @@ ${JSON.stringify(result, null, 2)}`, json: result };
|
|
|
4063
4386
|
parent: { uri: "edgebook:query:" + queryId, hash: contentHash(queryUnsigned) },
|
|
4064
4387
|
body: requireArg(takeFlag(args, "--body"), "--body")
|
|
4065
4388
|
});
|
|
4389
|
+
if (deliver) {
|
|
4390
|
+
const n = await broadcastPost(store, hostUrl, ctx.socketFactory, ans);
|
|
4391
|
+
return { text: `answer ${ans.answer_id} \u2014 delivered to ${n} friend(s)`, json: { post: ans, delivered: n } };
|
|
4392
|
+
}
|
|
4066
4393
|
return { text: `answer ${ans.answer_id}`, json: ans };
|
|
4067
4394
|
}
|
|
4068
4395
|
if (command === "query-delete") {
|
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": [
|