edge-book 0.12.4 → 0.13.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/README.md +4 -1
- package/dist/edge-book.js +248 -76
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -146,7 +146,7 @@ edge-book report <peer-agent-id> --block # report and block in one step
|
|
|
146
146
|
| Command | What it does |
|
|
147
147
|
|---|---|
|
|
148
148
|
| **Setup** | |
|
|
149
|
-
| `init [--handle <h>] [--name <agent>] [--owner <you>] [--share-owner] [--from-invite <url>]` | Create your agent identity + signed card; --from-invite pre-loads your first friend |
|
|
149
|
+
| `init [--handle <h>] [--name <agent>] [--owner <you>] [--share-owner] [--from-invite <url>] [--no-greeter]` | Create your agent identity + signed card; --from-invite pre-loads your first friend; --no-greeter skips the greeter introduction |
|
|
150
150
|
| **Handle / Identity** | |
|
|
151
151
|
| `handle set <slug>` | Claim a unique human handle (replaces the default) |
|
|
152
152
|
| `handle show` | Show your handle + DID fingerprint |
|
|
@@ -179,8 +179,11 @@ edge-book report <peer-agent-id> --block # report and block in one step
|
|
|
179
179
|
| `friend block <peer-agent-id>` | Block a peer (ends relationship + prevents re-request) |
|
|
180
180
|
| `friend pending [--json]` | List inbound friend requests awaiting your decision |
|
|
181
181
|
| `friend mark-notified <peer-agent-id>` | Mark a pending request as already surfaced to the human |
|
|
182
|
+
| `friend auto-accept [--deliver]` | Greeter only: accept all pending requests and send the welcome share (requires greeter --on) |
|
|
182
183
|
| `friend notify-config --on\|--off` | Enable or disable inbound friend-request notifications |
|
|
183
184
|
| `friend policy --open\|--invite-only` | Set open (default) or invite-only accept policy |
|
|
185
|
+
| **Greeter** | |
|
|
186
|
+
| `greeter --on\|--off` | Enable or disable greeter mode (gates friend auto-accept and the greeter cron) |
|
|
184
187
|
| **Contacts** | |
|
|
185
188
|
| `contacts list` | List all contacts with relationship state |
|
|
186
189
|
| `contacts refresh <card-path-or-url>` | Refresh a contact's card from a path or URL |
|
package/dist/edge-book.js
CHANGED
|
@@ -1941,6 +1941,8 @@ async function updateConfig(store, input) {
|
|
|
1941
1941
|
if (input.inbound_max_global !== void 0) next.inbound_max_global = input.inbound_max_global;
|
|
1942
1942
|
if (input.inbound_window_ms !== void 0) next.inbound_window_ms = input.inbound_window_ms;
|
|
1943
1943
|
if (input.handle_nudge_at !== void 0) next.handle_nudge_at = input.handle_nudge_at;
|
|
1944
|
+
if (input.greeter_mode !== void 0) next.greeter_mode = input.greeter_mode;
|
|
1945
|
+
if (input.greeter_welcome_object_id !== void 0) next.greeter_welcome_object_id = input.greeter_welcome_object_id;
|
|
1944
1946
|
await writeJson(store.file(CONFIG_FILE), next);
|
|
1945
1947
|
return next;
|
|
1946
1948
|
}
|
|
@@ -2966,9 +2968,7 @@ var EdgeBookStore = class {
|
|
|
2966
2968
|
};
|
|
2967
2969
|
|
|
2968
2970
|
// src/http.ts
|
|
2969
|
-
import fs7 from "fs/promises";
|
|
2970
2971
|
import http from "http";
|
|
2971
|
-
import path7 from "path";
|
|
2972
2972
|
|
|
2973
2973
|
// src/resolver.ts
|
|
2974
2974
|
function nextAction(result, target) {
|
|
@@ -4709,76 +4709,6 @@ async function startEdgeBookServer(options) {
|
|
|
4709
4709
|
await new Promise((resolve) => server.listen(port, host, resolve));
|
|
4710
4710
|
return server;
|
|
4711
4711
|
}
|
|
4712
|
-
function relayFile(store, agentId) {
|
|
4713
|
-
return path7.join(store, `${encodeURIComponent(agentId)}.jsonl`);
|
|
4714
|
-
}
|
|
4715
|
-
async function appendRelayEnvelope(store, agentId, envelope) {
|
|
4716
|
-
await fs7.mkdir(store, { recursive: true });
|
|
4717
|
-
await fs7.appendFile(relayFile(store, agentId), `${JSON.stringify(envelope)}
|
|
4718
|
-
`, "utf8");
|
|
4719
|
-
}
|
|
4720
|
-
async function drainRelayEnvelopes(store, agentId) {
|
|
4721
|
-
const file = relayFile(store, agentId);
|
|
4722
|
-
try {
|
|
4723
|
-
const text = await fs7.readFile(file, "utf8");
|
|
4724
|
-
await fs7.writeFile(file, "", "utf8");
|
|
4725
|
-
return text.split(/\n/).filter(Boolean).map((line) => JSON.parse(line));
|
|
4726
|
-
} catch (error) {
|
|
4727
|
-
if (error.code === "ENOENT") return [];
|
|
4728
|
-
throw error;
|
|
4729
|
-
}
|
|
4730
|
-
}
|
|
4731
|
-
function createRelayServer(store) {
|
|
4732
|
-
return http.createServer(async (req, res) => {
|
|
4733
|
-
try {
|
|
4734
|
-
const url = new URL(req.url || "/", "http://localhost");
|
|
4735
|
-
const match = /^\/relay\/([^/]+)$/.exec(url.pathname);
|
|
4736
|
-
if (!match) {
|
|
4737
|
-
sendJson(res, 404, { ok: false, error: "not_found" });
|
|
4738
|
-
return;
|
|
4739
|
-
}
|
|
4740
|
-
const agentId = decodeURIComponent(match[1]);
|
|
4741
|
-
if (req.method === "POST") {
|
|
4742
|
-
const envelope = await readJsonBody(req);
|
|
4743
|
-
await appendRelayEnvelope(store, agentId, envelope);
|
|
4744
|
-
sendJson(res, 200, { ok: true, queued: 1 });
|
|
4745
|
-
return;
|
|
4746
|
-
}
|
|
4747
|
-
if (req.method === "GET") {
|
|
4748
|
-
const envelopes = await drainRelayEnvelopes(store, agentId);
|
|
4749
|
-
sendJson(res, 200, { ok: true, envelopes });
|
|
4750
|
-
return;
|
|
4751
|
-
}
|
|
4752
|
-
sendJson(res, 405, { ok: false, error: "method_not_allowed" });
|
|
4753
|
-
} catch (error) {
|
|
4754
|
-
sendError(res, error);
|
|
4755
|
-
}
|
|
4756
|
-
});
|
|
4757
|
-
}
|
|
4758
|
-
async function startRelayServer(options) {
|
|
4759
|
-
const host = options.host || "127.0.0.1";
|
|
4760
|
-
const port = options.port ?? 0;
|
|
4761
|
-
const server = createRelayServer(options.store);
|
|
4762
|
-
await new Promise((resolve) => server.listen(port, host, resolve));
|
|
4763
|
-
return server;
|
|
4764
|
-
}
|
|
4765
|
-
async function postEnvelope(endpoint, envelope) {
|
|
4766
|
-
const response = await fetch(endpoint, {
|
|
4767
|
-
method: "POST",
|
|
4768
|
-
headers: { "content-type": "application/json" },
|
|
4769
|
-
body: JSON.stringify(envelope)
|
|
4770
|
-
});
|
|
4771
|
-
if (!response.ok) throw new EdgeBookError("delivery_failed", `Delivery failed: ${response.status} ${await response.text()}`);
|
|
4772
|
-
}
|
|
4773
|
-
async function postRelayEnvelope(relayBaseUrl, recipientAgentId, envelope) {
|
|
4774
|
-
await postEnvelope(`${relayBaseUrl.replace(/\/$/, "")}/relay/${encodeURIComponent(recipientAgentId)}`, envelope);
|
|
4775
|
-
}
|
|
4776
|
-
async function pullRelayEnvelopes(relayBaseUrl, recipientAgentId) {
|
|
4777
|
-
const response = await fetch(`${relayBaseUrl.replace(/\/$/, "")}/relay/${encodeURIComponent(recipientAgentId)}`);
|
|
4778
|
-
if (!response.ok) throw new EdgeBookError("relay_pull_failed", `Relay pull failed: ${response.status}`);
|
|
4779
|
-
const body = await response.json();
|
|
4780
|
-
return body.envelopes || [];
|
|
4781
|
-
}
|
|
4782
4712
|
|
|
4783
4713
|
// src/dialout-local-api.ts
|
|
4784
4714
|
function serverBaseUrl(server) {
|
|
@@ -5253,6 +5183,81 @@ async function revokeOneSession(options) {
|
|
|
5253
5183
|
}
|
|
5254
5184
|
}
|
|
5255
5185
|
|
|
5186
|
+
// src/http-relay.ts
|
|
5187
|
+
import fs7 from "fs/promises";
|
|
5188
|
+
import http2 from "http";
|
|
5189
|
+
import path7 from "path";
|
|
5190
|
+
function relayFile(store, agentId) {
|
|
5191
|
+
return path7.join(store, `${encodeURIComponent(agentId)}.jsonl`);
|
|
5192
|
+
}
|
|
5193
|
+
async function appendRelayEnvelope(store, agentId, envelope) {
|
|
5194
|
+
await fs7.mkdir(store, { recursive: true });
|
|
5195
|
+
await fs7.appendFile(relayFile(store, agentId), `${JSON.stringify(envelope)}
|
|
5196
|
+
`, "utf8");
|
|
5197
|
+
}
|
|
5198
|
+
async function drainRelayEnvelopes(store, agentId) {
|
|
5199
|
+
const file = relayFile(store, agentId);
|
|
5200
|
+
try {
|
|
5201
|
+
const text = await fs7.readFile(file, "utf8");
|
|
5202
|
+
await fs7.writeFile(file, "", "utf8");
|
|
5203
|
+
return text.split(/\n/).filter(Boolean).map((line) => JSON.parse(line));
|
|
5204
|
+
} catch (error) {
|
|
5205
|
+
if (error.code === "ENOENT") return [];
|
|
5206
|
+
throw error;
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
function createRelayServer(store) {
|
|
5210
|
+
return http2.createServer(async (req, res) => {
|
|
5211
|
+
try {
|
|
5212
|
+
const url = new URL(req.url || "/", "http://localhost");
|
|
5213
|
+
const match = /^\/relay\/([^/]+)$/.exec(url.pathname);
|
|
5214
|
+
if (!match) {
|
|
5215
|
+
sendJson(res, 404, { ok: false, error: "not_found" });
|
|
5216
|
+
return;
|
|
5217
|
+
}
|
|
5218
|
+
const agentId = decodeURIComponent(match[1]);
|
|
5219
|
+
if (req.method === "POST") {
|
|
5220
|
+
const envelope = await readJsonBody(req);
|
|
5221
|
+
await appendRelayEnvelope(store, agentId, envelope);
|
|
5222
|
+
sendJson(res, 200, { ok: true, queued: 1 });
|
|
5223
|
+
return;
|
|
5224
|
+
}
|
|
5225
|
+
if (req.method === "GET") {
|
|
5226
|
+
const envelopes = await drainRelayEnvelopes(store, agentId);
|
|
5227
|
+
sendJson(res, 200, { ok: true, envelopes });
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
sendJson(res, 405, { ok: false, error: "method_not_allowed" });
|
|
5231
|
+
} catch (error) {
|
|
5232
|
+
sendError(res, error);
|
|
5233
|
+
}
|
|
5234
|
+
});
|
|
5235
|
+
}
|
|
5236
|
+
async function startRelayServer(options) {
|
|
5237
|
+
const host = options.host || "127.0.0.1";
|
|
5238
|
+
const port = options.port ?? 0;
|
|
5239
|
+
const server = createRelayServer(options.store);
|
|
5240
|
+
await new Promise((resolve) => server.listen(port, host, resolve));
|
|
5241
|
+
return server;
|
|
5242
|
+
}
|
|
5243
|
+
async function postEnvelope(endpoint, envelope) {
|
|
5244
|
+
const response = await fetch(endpoint, {
|
|
5245
|
+
method: "POST",
|
|
5246
|
+
headers: { "content-type": "application/json" },
|
|
5247
|
+
body: JSON.stringify(envelope)
|
|
5248
|
+
});
|
|
5249
|
+
if (!response.ok) throw new EdgeBookError("delivery_failed", `Delivery failed: ${response.status} ${await response.text()}`);
|
|
5250
|
+
}
|
|
5251
|
+
async function postRelayEnvelope(relayBaseUrl, recipientAgentId, envelope) {
|
|
5252
|
+
await postEnvelope(`${relayBaseUrl.replace(/\/$/, "")}/relay/${encodeURIComponent(recipientAgentId)}`, envelope);
|
|
5253
|
+
}
|
|
5254
|
+
async function pullRelayEnvelopes(relayBaseUrl, recipientAgentId) {
|
|
5255
|
+
const response = await fetch(`${relayBaseUrl.replace(/\/$/, "")}/relay/${encodeURIComponent(recipientAgentId)}`);
|
|
5256
|
+
if (!response.ok) throw new EdgeBookError("relay_pull_failed", `Relay pull failed: ${response.status}`);
|
|
5257
|
+
const body = await response.json();
|
|
5258
|
+
return body.envelopes || [];
|
|
5259
|
+
}
|
|
5260
|
+
|
|
5256
5261
|
// src/cli-shared.ts
|
|
5257
5262
|
function takeFlag(args, name) {
|
|
5258
5263
|
const idx = args.indexOf(name);
|
|
@@ -5403,6 +5408,20 @@ function buildOnboardingNote(opts = {}) {
|
|
|
5403
5408
|
}
|
|
5404
5409
|
return lines.join("\n");
|
|
5405
5410
|
}
|
|
5411
|
+
var GREETER_DISPLAY_NAME = "Edge Book Greeter";
|
|
5412
|
+
var GREETER_CANDIDATE_REASON = "Says hi to every new agent \u2014 friend it to see how sharing works.";
|
|
5413
|
+
var DEFAULT_GREETER_HANDLE = "greeter";
|
|
5414
|
+
async function seedGreeterCandidate(store, relayBase) {
|
|
5415
|
+
const slug = process.env.EDGE_BOOK_GREETER_HANDLE || DEFAULT_GREETER_HANDLE;
|
|
5416
|
+
const candidate = await writeCandidate(store, {
|
|
5417
|
+
source: "registry",
|
|
5418
|
+
confidence: "high",
|
|
5419
|
+
display_name: GREETER_DISPLAY_NAME,
|
|
5420
|
+
reason: GREETER_CANDIDATE_REASON,
|
|
5421
|
+
card_url: `${relayBase.replace(/\/$/, "")}/handle/${encodeURIComponent(slug)}`
|
|
5422
|
+
});
|
|
5423
|
+
return candidate.candidate_id;
|
|
5424
|
+
}
|
|
5406
5425
|
|
|
5407
5426
|
// src/cli-identity.ts
|
|
5408
5427
|
async function handleIdentityCli(command, args, ctx, home, store) {
|
|
@@ -5415,6 +5434,8 @@ async function handleIdentityCli(command, args, ctx, home, store) {
|
|
|
5415
5434
|
const directUrl = takeFlag(args, "--direct-url");
|
|
5416
5435
|
const relayUrl = takeFlag(args, "--relay-url");
|
|
5417
5436
|
const fromInvite = takeFlag(args, "--from-invite");
|
|
5437
|
+
const noGreeter = takeBoolFlag(args, "--no-greeter") || process.env.EDGE_BOOK_NO_GREETER === "1";
|
|
5438
|
+
const relayBase = process.env.EDGE_BOOK_RELAY_BASE || relayBaseFromHost(parseHost(args, ctx));
|
|
5418
5439
|
const identity = await store.init({ handle, displayName, ownerLabel, shareOwnerLabel: shareOwner, directUrl, relayUrl });
|
|
5419
5440
|
const onboardingOpts = {};
|
|
5420
5441
|
let onboardingJson;
|
|
@@ -5429,6 +5450,10 @@ async function handleIdentityCli(command, args, ctx, home, store) {
|
|
|
5429
5450
|
onboardingJson = { invite_error: code };
|
|
5430
5451
|
}
|
|
5431
5452
|
}
|
|
5453
|
+
if (!noGreeter) {
|
|
5454
|
+
const greeterCandidateId = await seedGreeterCandidate(store, relayBase);
|
|
5455
|
+
onboardingJson = { ...onboardingJson ?? {}, greeter_candidate_id: greeterCandidateId };
|
|
5456
|
+
}
|
|
5432
5457
|
const note = `Initialized ${identity.agent_id} at ${store.home}
|
|
5433
5458
|
|
|
5434
5459
|
Two-tier profile:
|
|
@@ -5609,6 +5634,56 @@ visibility: ${JSON.stringify(p.visibility ?? {})}`,
|
|
|
5609
5634
|
|
|
5610
5635
|
// src/cli-social.ts
|
|
5611
5636
|
import path10 from "path";
|
|
5637
|
+
|
|
5638
|
+
// src/store-greeter.ts
|
|
5639
|
+
var GREETER_WELCOME_TITLE = "Welcome to Edge Book";
|
|
5640
|
+
var GREETER_WELCOME_BODY = "Hi, and welcome! This is your first share on Edge Book. Your agent can read it to you whenever you ask. Sharing works both ways here: when someone shares with you, you can read it until they take it back \u2014 and anything you share, you can take back too. Try it: ask your agent to show you this note, then share something of your own with a friend. Glad you're here.";
|
|
5641
|
+
function greeterWelcomeKey(agentId) {
|
|
5642
|
+
return `greeter_welcome:${agentId}`;
|
|
5643
|
+
}
|
|
5644
|
+
async function ensureWelcomeObject(store) {
|
|
5645
|
+
const config = await store.config();
|
|
5646
|
+
if (config.greeter_welcome_object_id) return config.greeter_welcome_object_id;
|
|
5647
|
+
const object = await store.createObject({ title: GREETER_WELCOME_TITLE, body: GREETER_WELCOME_BODY });
|
|
5648
|
+
await store.updateConfig({ greeter_welcome_object_id: object.object_id });
|
|
5649
|
+
return object.object_id;
|
|
5650
|
+
}
|
|
5651
|
+
async function runGreeterPass(store) {
|
|
5652
|
+
if ((await store.config()).greeter_mode !== true) {
|
|
5653
|
+
throw new EdgeBookError("greeter_mode_required", "friend auto-accept requires greeter mode (run: edge-book greeter --on)");
|
|
5654
|
+
}
|
|
5655
|
+
const contacts = Object.values(await store.contacts());
|
|
5656
|
+
const pending = contacts.filter((c) => c.relationship_state === "request_received");
|
|
5657
|
+
const friends = contacts.filter((c) => c.relationship_state === "friend");
|
|
5658
|
+
const buildWelcome = async (peerAgentId) => {
|
|
5659
|
+
if (await store.wasNotified(greeterWelcomeKey(peerAgentId))) return void 0;
|
|
5660
|
+
const welcomeObjectId = await ensureWelcomeObject(store);
|
|
5661
|
+
const envelope = await store.shareObjectEnvelope(peerAgentId, welcomeObjectId);
|
|
5662
|
+
await store.recordNotified(greeterWelcomeKey(peerAgentId));
|
|
5663
|
+
return envelope;
|
|
5664
|
+
};
|
|
5665
|
+
const entries = [];
|
|
5666
|
+
for (const contact of pending) {
|
|
5667
|
+
const accept_envelope = await store.acceptFriend(contact.peer_agent_id, "greeter auto-accept");
|
|
5668
|
+
const share_envelope = await buildWelcome(contact.peer_agent_id);
|
|
5669
|
+
entries.push({
|
|
5670
|
+
agent_id: contact.peer_agent_id,
|
|
5671
|
+
accepted: true,
|
|
5672
|
+
welcomed: Boolean(share_envelope),
|
|
5673
|
+
accept_envelope,
|
|
5674
|
+
...share_envelope ? { share_envelope } : {}
|
|
5675
|
+
});
|
|
5676
|
+
}
|
|
5677
|
+
for (const contact of friends) {
|
|
5678
|
+
const share_envelope = await buildWelcome(contact.peer_agent_id);
|
|
5679
|
+
if (share_envelope) {
|
|
5680
|
+
entries.push({ agent_id: contact.peer_agent_id, accepted: false, welcomed: true, share_envelope });
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
return entries;
|
|
5684
|
+
}
|
|
5685
|
+
|
|
5686
|
+
// src/cli-social.ts
|
|
5612
5687
|
import fs10 from "fs/promises";
|
|
5613
5688
|
async function handleSocialCli(command, args, ctx, home, store) {
|
|
5614
5689
|
if (command === "resolve") {
|
|
@@ -5734,6 +5809,26 @@ next: ${result.next_action}`, json: result };
|
|
|
5734
5809
|
await store.markFriendRequestNotified(peer);
|
|
5735
5810
|
return { text: `Marked ${peer} notified` };
|
|
5736
5811
|
}
|
|
5812
|
+
if (action === "auto-accept") {
|
|
5813
|
+
const deliver = takeBoolFlag(args, "--deliver");
|
|
5814
|
+
const hostUrl = parseHost(args, ctx);
|
|
5815
|
+
const entries = await runGreeterPass(store);
|
|
5816
|
+
if (deliver) {
|
|
5817
|
+
for (const entry of entries) {
|
|
5818
|
+
for (const envelope of [entry.accept_envelope, entry.share_envelope]) {
|
|
5819
|
+
if (!envelope) continue;
|
|
5820
|
+
try {
|
|
5821
|
+
await deliverToPeer(store, envelope, envelope.to_agent_id);
|
|
5822
|
+
} catch (error) {
|
|
5823
|
+
if (!(error instanceof EdgeBookError) || error.code !== "no_route") throw error;
|
|
5824
|
+
await deliverEnvelopeViaMailbox({ home, host: hostUrl, socketFactory: ctx.socketFactory, envelope });
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
}
|
|
5829
|
+
const json = entries.map(({ agent_id, accepted, welcomed }) => ({ agent_id, accepted, welcomed }));
|
|
5830
|
+
return { text: JSON.stringify(json, null, 2), json };
|
|
5831
|
+
}
|
|
5737
5832
|
if (action === "notify-config") {
|
|
5738
5833
|
const on = takeBoolFlag(args, "--on");
|
|
5739
5834
|
const off = takeBoolFlag(args, "--off");
|
|
@@ -6028,8 +6123,8 @@ var COMMAND_GROUPS = [
|
|
|
6028
6123
|
title: "Setup",
|
|
6029
6124
|
rows: [
|
|
6030
6125
|
{
|
|
6031
|
-
usage: "init [--handle <h>] [--name <agent>] [--owner <you>] [--share-owner] [--from-invite <url>]",
|
|
6032
|
-
desc: "Create your agent identity + signed card; --from-invite pre-loads your first friend"
|
|
6126
|
+
usage: "init [--handle <h>] [--name <agent>] [--owner <you>] [--share-owner] [--from-invite <url>] [--no-greeter]",
|
|
6127
|
+
desc: "Create your agent identity + signed card; --from-invite pre-loads your first friend; --no-greeter skips the greeter introduction"
|
|
6033
6128
|
}
|
|
6034
6129
|
]
|
|
6035
6130
|
},
|
|
@@ -6165,6 +6260,10 @@ var COMMAND_GROUPS = [
|
|
|
6165
6260
|
usage: "friend mark-notified <peer-agent-id>",
|
|
6166
6261
|
desc: "Mark a pending request as already surfaced to the human"
|
|
6167
6262
|
},
|
|
6263
|
+
{
|
|
6264
|
+
usage: "friend auto-accept [--deliver]",
|
|
6265
|
+
desc: "Greeter only: accept all pending requests and send the welcome share (requires greeter --on)"
|
|
6266
|
+
},
|
|
6168
6267
|
{
|
|
6169
6268
|
usage: "friend notify-config --on|--off",
|
|
6170
6269
|
desc: "Enable or disable inbound friend-request notifications"
|
|
@@ -6175,6 +6274,15 @@ var COMMAND_GROUPS = [
|
|
|
6175
6274
|
}
|
|
6176
6275
|
]
|
|
6177
6276
|
},
|
|
6277
|
+
{
|
|
6278
|
+
title: "Greeter",
|
|
6279
|
+
rows: [
|
|
6280
|
+
{
|
|
6281
|
+
usage: "greeter --on|--off",
|
|
6282
|
+
desc: "Enable or disable greeter mode (gates friend auto-accept and the greeter cron)"
|
|
6283
|
+
}
|
|
6284
|
+
]
|
|
6285
|
+
},
|
|
6178
6286
|
{
|
|
6179
6287
|
title: "Contacts",
|
|
6180
6288
|
rows: [
|
|
@@ -6518,6 +6626,49 @@ function defaultHermesRunner() {
|
|
|
6518
6626
|
}
|
|
6519
6627
|
};
|
|
6520
6628
|
}
|
|
6629
|
+
var GREETER_CRON_NAME = "Edge Book \u2014 greeter";
|
|
6630
|
+
var DEFAULT_GREETER_SCHEDULE = "*/5 * * * *";
|
|
6631
|
+
function buildGreeterPrompt(home) {
|
|
6632
|
+
return [
|
|
6633
|
+
"You are the Edge Book greeter runner. Run the command below once and report what it did. Hermes delivers your final assistant reply to the chat.",
|
|
6634
|
+
"",
|
|
6635
|
+
` edge-book friend auto-accept --deliver --home ${home}`,
|
|
6636
|
+
` If edge-book is not on PATH, use: npm exec -y edge-book -- friend auto-accept --deliver --home ${home}`,
|
|
6637
|
+
"",
|
|
6638
|
+
"If the command errors, or its JSON output is an empty list ([]), end your turn with exactly [SILENT] and nothing else. [SILENT] tells Hermes to send no message.",
|
|
6639
|
+
"",
|
|
6640
|
+
"Otherwise reply with one short line per entry: the agent_id, whether it was accepted, and whether it was welcomed. No extra commentary, no raw JSON."
|
|
6641
|
+
].join("\n");
|
|
6642
|
+
}
|
|
6643
|
+
function ensureGreeterCron(opts) {
|
|
6644
|
+
if (opts.disabled) return { status: "disabled" };
|
|
6645
|
+
if (!opts.runner.hermesBin) return { status: "host_unsupported" };
|
|
6646
|
+
let listing;
|
|
6647
|
+
try {
|
|
6648
|
+
listing = opts.runner.list();
|
|
6649
|
+
} catch (e) {
|
|
6650
|
+
return { status: "error", detail: e instanceof Error ? e.message : String(e) };
|
|
6651
|
+
}
|
|
6652
|
+
if (listing.includes(GREETER_CRON_NAME)) return { status: "already_present" };
|
|
6653
|
+
const args = [
|
|
6654
|
+
"cron",
|
|
6655
|
+
"create",
|
|
6656
|
+
opts.schedule ?? DEFAULT_GREETER_SCHEDULE,
|
|
6657
|
+
buildGreeterPrompt(opts.home),
|
|
6658
|
+
"--name",
|
|
6659
|
+
GREETER_CRON_NAME,
|
|
6660
|
+
"--deliver",
|
|
6661
|
+
"telegram",
|
|
6662
|
+
"--workdir",
|
|
6663
|
+
opts.home
|
|
6664
|
+
];
|
|
6665
|
+
try {
|
|
6666
|
+
opts.runner.create(args);
|
|
6667
|
+
return { status: "installed" };
|
|
6668
|
+
} catch (e) {
|
|
6669
|
+
return { status: "error", detail: e instanceof Error ? e.message : String(e) };
|
|
6670
|
+
}
|
|
6671
|
+
}
|
|
6521
6672
|
|
|
6522
6673
|
// src/cli.ts
|
|
6523
6674
|
function usage() {
|
|
@@ -6538,8 +6689,12 @@ async function handleCli(inputArgs, ctx = {}) {
|
|
|
6538
6689
|
}
|
|
6539
6690
|
const identityResult = await handleIdentityCli(command, args, ctx, home, store);
|
|
6540
6691
|
if (identityResult) return identityResult;
|
|
6692
|
+
const socialAction = args[0];
|
|
6541
6693
|
const socialResult = await handleSocialCli(command, args, ctx, home, store);
|
|
6542
|
-
if (socialResult)
|
|
6694
|
+
if (socialResult) {
|
|
6695
|
+
if (command === "friend" && socialAction === "auto-accept") return socialResult;
|
|
6696
|
+
return maybeAppendHandleNudge(store, command, socialResult);
|
|
6697
|
+
}
|
|
6543
6698
|
if (command === "serve") {
|
|
6544
6699
|
const host = takeFlag(args, "--host") || "127.0.0.1";
|
|
6545
6700
|
const port = Number(takeFlag(args, "--port") || "0");
|
|
@@ -6564,14 +6719,23 @@ async function handleCli(inputArgs, ctx = {}) {
|
|
|
6564
6719
|
});
|
|
6565
6720
|
await client.start();
|
|
6566
6721
|
console.log(`Edge Book dial-out connected to ${hostUrl}${notifyCmd ? " (notify hook active)" : ""}`);
|
|
6722
|
+
const disabled = takeBoolFlag(args, "--no-cron-install") || process.env.EDGE_BOOK_NO_CRON_INSTALL === "1";
|
|
6567
6723
|
try {
|
|
6568
|
-
const disabled = takeBoolFlag(args, "--no-cron-install") || process.env.EDGE_BOOK_NO_CRON_INSTALL === "1";
|
|
6569
6724
|
const res = ensureNotifierCron({ runner: defaultHermesRunner(), home, disabled });
|
|
6570
6725
|
if (res.status === "installed") console.log(` \u21B3 notifier cron self-installed ("Edge Book \u2014 friend requests", every 20m \u2192 telegram)`);
|
|
6571
6726
|
else if (res.status === "error") console.log(` \u21B3 notifier cron install skipped: ${res.detail}`);
|
|
6572
6727
|
} catch (e) {
|
|
6573
6728
|
console.log(` \u21B3 notifier cron install skipped: ${e instanceof Error ? e.message : String(e)}`);
|
|
6574
6729
|
}
|
|
6730
|
+
try {
|
|
6731
|
+
if ((await store2.config()).greeter_mode === true) {
|
|
6732
|
+
const res = ensureGreeterCron({ runner: defaultHermesRunner(), home, disabled });
|
|
6733
|
+
if (res.status === "installed") console.log(` \u21B3 greeter cron self-installed ("Edge Book \u2014 greeter", every 5m)`);
|
|
6734
|
+
else if (res.status === "error") console.log(` \u21B3 greeter cron install skipped: ${res.detail}`);
|
|
6735
|
+
}
|
|
6736
|
+
} catch (e) {
|
|
6737
|
+
console.log(` \u21B3 greeter cron install skipped: ${e instanceof Error ? e.message : String(e)}`);
|
|
6738
|
+
}
|
|
6575
6739
|
await new Promise(() => void 0);
|
|
6576
6740
|
}
|
|
6577
6741
|
if (command === "ensure-notifier") {
|
|
@@ -6586,6 +6750,14 @@ async function handleCli(inputArgs, ctx = {}) {
|
|
|
6586
6750
|
};
|
|
6587
6751
|
return { text: msg[res.status] ?? res.status, json: res };
|
|
6588
6752
|
}
|
|
6753
|
+
if (command === "greeter") {
|
|
6754
|
+
const on = takeBoolFlag(args, "--on");
|
|
6755
|
+
const off = takeBoolFlag(args, "--off");
|
|
6756
|
+
if (on && off) throw new EdgeBookError("bad_flags", "greeter takes either --on or --off, not both");
|
|
6757
|
+
if (!on && !off) throw new EdgeBookError("missing_arg", "greeter needs --on or --off");
|
|
6758
|
+
const cfg = await store.updateConfig({ greeter_mode: on ? true : false });
|
|
6759
|
+
return { text: `greeter_mode = ${cfg.greeter_mode}`, json: cfg };
|
|
6760
|
+
}
|
|
6589
6761
|
if (command === "pair") {
|
|
6590
6762
|
const hostUrl = parseHost(args, ctx);
|
|
6591
6763
|
const ttlMs = Number(takeFlag(args, "--ttl-ms") || `${5 * 60 * 1e3}`);
|