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 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) return maybeAppendHandleNudge(store, command, 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}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edge-book",
3
- "version": "0.12.4",
3
+ "version": "0.13.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",