edge-book 0.13.0 → 0.14.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 +1 -0
- package/dist/edge-book.js +210 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -167,6 +167,7 @@ edge-book report <peer-agent-id> --block # report and block in one step
|
|
|
167
167
|
| `pair [--host <wss-url>] [--ttl-ms <ms>]` | Mint a pairing code for the hosted browser reader |
|
|
168
168
|
| `sessions list [--host <wss-url>]` | List remembered reader sessions |
|
|
169
169
|
| `sessions revoke [--device <id>] [--host <wss-url>]` | Revoke one device session (or all if no --device) |
|
|
170
|
+
| `outbox [--json] [--host <wss-url>]` | Delivery state of recently sent envelopes (queued / delivered / acked) with stale-queue warnings |
|
|
170
171
|
| **Discovery** | |
|
|
171
172
|
| `resolve <target>` | Resolve a handle, invite link, card URL, or file to a verified Agent Card |
|
|
172
173
|
| `candidates list` | List pending first-contact candidates with provenance |
|
package/dist/edge-book.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from "url";
|
|
|
6
6
|
|
|
7
7
|
// src/cli-shared.ts
|
|
8
8
|
import fs8 from "fs/promises";
|
|
9
|
-
import
|
|
9
|
+
import path9 from "path";
|
|
10
10
|
|
|
11
11
|
// src/dialout.ts
|
|
12
12
|
import crypto5 from "crypto";
|
|
@@ -404,6 +404,7 @@ var CONTACT_MUTES_FILE = "contact-mutes.json";
|
|
|
404
404
|
var REPORTS_FILE = "reports.json";
|
|
405
405
|
var INVITE_CODES_FILE = "invite-codes.json";
|
|
406
406
|
var INBOUND_RATE_FILE = "inbound-rate.json";
|
|
407
|
+
var OUTBOX_FILE = "outbox.json";
|
|
407
408
|
var ATTESTATIONS_FILE = "attestations.json";
|
|
408
409
|
var ENDORSEMENTS_FILE = "endorsements.json";
|
|
409
410
|
var SIGNALS_FILE = "signals.json";
|
|
@@ -4778,7 +4779,9 @@ var EdgeBookDialoutClient = class {
|
|
|
4778
4779
|
currentBackoff;
|
|
4779
4780
|
opened;
|
|
4780
4781
|
pendingSessionRevokes = /* @__PURE__ */ new Map();
|
|
4781
|
-
// Generic request_id-keyed RPC waiters
|
|
4782
|
+
// Generic request_id-keyed RPC waiters (sessions_list / session_revoke_one /
|
|
4783
|
+
// mailbox_status). rpcType lets an old host's {type:"error", ref:"<type>"}
|
|
4784
|
+
// frame — which carries NO request_id — reject pending requests of that type.
|
|
4782
4785
|
pendingRpc = /* @__PURE__ */ new Map();
|
|
4783
4786
|
pendingMailboxSends = /* @__PURE__ */ new Map();
|
|
4784
4787
|
constructor(options) {
|
|
@@ -4829,6 +4832,19 @@ var EdgeBookDialoutClient = class {
|
|
|
4829
4832
|
const frame = await this.rpc("session_revoke_one", { device_id }, "session_revoke_one_ok", timeoutMs);
|
|
4830
4833
|
return Boolean(frame.revoked);
|
|
4831
4834
|
}
|
|
4835
|
+
// Ask the host for per-message delivery state (spec-097). Returns null when
|
|
4836
|
+
// the host predates receipts — detected by the unknown-type error frame
|
|
4837
|
+
// (fast path) or the RPC timeout (lost-frame path); both degrade the same.
|
|
4838
|
+
async mailboxStatusAndWait(ids, timeoutMs = 5e3) {
|
|
4839
|
+
try {
|
|
4840
|
+
const frame = await this.rpc("mailbox_status", { ids }, "mailbox_status_ok", timeoutMs);
|
|
4841
|
+
if (frame.type === "mailbox_status_err") throw new EdgeBookError("mailbox_status_failed", String(frame.error || "mailbox_status rejected"));
|
|
4842
|
+
return frame.statuses ?? [];
|
|
4843
|
+
} catch (error) {
|
|
4844
|
+
if (error instanceof EdgeBookError && (error.code === "host_rpc_timeout" || error.code === "host_unsupported_rpc")) return null;
|
|
4845
|
+
throw error;
|
|
4846
|
+
}
|
|
4847
|
+
}
|
|
4832
4848
|
// Small request/response helper over the dial-out socket, correlated by
|
|
4833
4849
|
// request_id. `expect` documents the ack type; resolution is by request_id.
|
|
4834
4850
|
async rpc(type, extra, expect, timeoutMs) {
|
|
@@ -4838,7 +4854,7 @@ var EdgeBookDialoutClient = class {
|
|
|
4838
4854
|
this.pendingRpc.delete(request_id);
|
|
4839
4855
|
reject(new EdgeBookError("host_rpc_timeout", `Timed out waiting for ${expect}`));
|
|
4840
4856
|
}, timeoutMs);
|
|
4841
|
-
this.pendingRpc.set(request_id, { resolve, reject, timer });
|
|
4857
|
+
this.pendingRpc.set(request_id, { resolve, reject, timer, rpcType: type });
|
|
4842
4858
|
});
|
|
4843
4859
|
this.send({ type, request_id, ...extra });
|
|
4844
4860
|
return promise;
|
|
@@ -4857,7 +4873,8 @@ var EdgeBookDialoutClient = class {
|
|
|
4857
4873
|
}
|
|
4858
4874
|
// ── Mailbox transport (Contract 1 / ea-claude-065) ─────────────────────────
|
|
4859
4875
|
// Low-level: hand an opaque blob to the host for delivery to `to` (a peer DID
|
|
4860
|
-
// or channel_id). Resolves with the host-assigned message id once enqueued
|
|
4876
|
+
// or channel_id). Resolves with the host-assigned message id once enqueued,
|
|
4877
|
+
// plus recipient_live when the host supports receipts (spec-097).
|
|
4861
4878
|
async sendMailbox(to, blob, timeoutMs = 5e3) {
|
|
4862
4879
|
const request_id = crypto5.randomUUID();
|
|
4863
4880
|
const blob_b64 = Buffer.from(blob).toString("base64");
|
|
@@ -5003,7 +5020,7 @@ var EdgeBookDialoutClient = class {
|
|
|
5003
5020
|
this.send({ type: "pong" });
|
|
5004
5021
|
return;
|
|
5005
5022
|
}
|
|
5006
|
-
if (frame.type === "sessions_list_ok" || frame.type === "session_revoke_one_ok") {
|
|
5023
|
+
if (frame.type === "sessions_list_ok" || frame.type === "session_revoke_one_ok" || frame.type === "mailbox_status_ok" || frame.type === "mailbox_status_err") {
|
|
5007
5024
|
const ack = frame;
|
|
5008
5025
|
const pending = this.pendingRpc.get(ack.request_id || "");
|
|
5009
5026
|
if (pending) {
|
|
@@ -5035,7 +5052,7 @@ var EdgeBookDialoutClient = class {
|
|
|
5035
5052
|
if (pending) {
|
|
5036
5053
|
clearTimeout(pending.timer);
|
|
5037
5054
|
this.pendingMailboxSends.delete(ack.request_id || "");
|
|
5038
|
-
pending.resolve({ id: ack.id || "" });
|
|
5055
|
+
pending.resolve({ id: ack.id || "", ...typeof ack.recipient_live === "boolean" ? { recipient_live: ack.recipient_live } : {} });
|
|
5039
5056
|
}
|
|
5040
5057
|
return;
|
|
5041
5058
|
}
|
|
@@ -5054,7 +5071,17 @@ var EdgeBookDialoutClient = class {
|
|
|
5054
5071
|
return;
|
|
5055
5072
|
}
|
|
5056
5073
|
if (frameType === "handle_claim_ok" || frameType === "handle_claim_err") return;
|
|
5057
|
-
if (frameType === "error")
|
|
5074
|
+
if (frameType === "error") {
|
|
5075
|
+
const ref = frame.ref;
|
|
5076
|
+
if (typeof ref !== "string") return;
|
|
5077
|
+
for (const [request_id, pending] of [...this.pendingRpc]) {
|
|
5078
|
+
if (pending.rpcType !== ref) continue;
|
|
5079
|
+
clearTimeout(pending.timer);
|
|
5080
|
+
this.pendingRpc.delete(request_id);
|
|
5081
|
+
pending.reject(new EdgeBookError("host_unsupported_rpc", `Host does not support ${ref}`));
|
|
5082
|
+
}
|
|
5083
|
+
return;
|
|
5084
|
+
}
|
|
5058
5085
|
if (frame.type !== "host.api.request" && frame.type !== "api_request") return;
|
|
5059
5086
|
const response = await this.handleApiRequest(frame);
|
|
5060
5087
|
this.send(response);
|
|
@@ -5182,6 +5209,16 @@ async function revokeOneSession(options) {
|
|
|
5182
5209
|
await client.stop();
|
|
5183
5210
|
}
|
|
5184
5211
|
}
|
|
5212
|
+
async function mailboxStatus(options) {
|
|
5213
|
+
const client = new EdgeBookDialoutClient({ ...options, reconnect: false, openLocalApi: false });
|
|
5214
|
+
await client.start();
|
|
5215
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
5216
|
+
try {
|
|
5217
|
+
return await client.mailboxStatusAndWait(options.ids, options.timeoutMs ?? 5e3);
|
|
5218
|
+
} finally {
|
|
5219
|
+
await client.stop();
|
|
5220
|
+
}
|
|
5221
|
+
}
|
|
5185
5222
|
|
|
5186
5223
|
// src/http-relay.ts
|
|
5187
5224
|
import fs7 from "fs/promises";
|
|
@@ -5258,6 +5295,32 @@ async function pullRelayEnvelopes(relayBaseUrl, recipientAgentId) {
|
|
|
5258
5295
|
return body.envelopes || [];
|
|
5259
5296
|
}
|
|
5260
5297
|
|
|
5298
|
+
// src/store-outbox.ts
|
|
5299
|
+
import path8 from "path";
|
|
5300
|
+
var OUTBOX_CAP = 200;
|
|
5301
|
+
var DEFAULT_STALE_QUEUE_MS = 10 * 60 * 1e3;
|
|
5302
|
+
function outboxPath(home) {
|
|
5303
|
+
return path8.join(resolveHome(home), OUTBOX_FILE);
|
|
5304
|
+
}
|
|
5305
|
+
async function readOutbox(home) {
|
|
5306
|
+
return readJson(outboxPath(home), []);
|
|
5307
|
+
}
|
|
5308
|
+
async function recordOutboxEntry(home, entry) {
|
|
5309
|
+
const entries = await readOutbox(home);
|
|
5310
|
+
entries.push({ sent_at: now2(), ...entry });
|
|
5311
|
+
await writeJson(outboxPath(home), entries.slice(-OUTBOX_CAP));
|
|
5312
|
+
}
|
|
5313
|
+
function staleQueueMs() {
|
|
5314
|
+
const raw = Number(process.env.EDGE_BOOK_STALE_QUEUE_MS);
|
|
5315
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_STALE_QUEUE_MS;
|
|
5316
|
+
}
|
|
5317
|
+
function formatAge(ms) {
|
|
5318
|
+
if (ms < 6e4) return `${Math.max(0, Math.round(ms / 1e3))}s`;
|
|
5319
|
+
if (ms < 36e5) return `${Math.round(ms / 6e4)}m`;
|
|
5320
|
+
if (ms < 864e5) return `${Math.round(ms / 36e5)}h`;
|
|
5321
|
+
return `${Math.round(ms / 864e5)}d`;
|
|
5322
|
+
}
|
|
5323
|
+
|
|
5261
5324
|
// src/cli-shared.ts
|
|
5262
5325
|
function takeFlag(args, name) {
|
|
5263
5326
|
const idx = args.indexOf(name);
|
|
@@ -5307,7 +5370,7 @@ function takeRepeated(args, flag) {
|
|
|
5307
5370
|
return out;
|
|
5308
5371
|
}
|
|
5309
5372
|
async function readEnvelope(filePath) {
|
|
5310
|
-
return JSON.parse(await fs8.readFile(
|
|
5373
|
+
return JSON.parse(await fs8.readFile(path9.resolve(filePath), "utf8"));
|
|
5311
5374
|
}
|
|
5312
5375
|
async function deliverToEndpoint(envelope, endpoint) {
|
|
5313
5376
|
await postEnvelope(endpoint, envelope);
|
|
@@ -5325,13 +5388,33 @@ async function deliverToPeer(store, envelope, peerAgentId) {
|
|
|
5325
5388
|
}
|
|
5326
5389
|
throw new EdgeBookError("no_route", `No direct or relay endpoint for ${peerAgentId}`);
|
|
5327
5390
|
}
|
|
5391
|
+
async function deliverViaMailboxRecorded(envelope, opts, legacyText) {
|
|
5392
|
+
const ack = await deliverEnvelopeViaMailbox({ home: opts.home, host: opts.host, socketFactory: opts.socketFactory, envelope });
|
|
5393
|
+
await recordOutboxEntry(opts.home, {
|
|
5394
|
+
id: ack.id,
|
|
5395
|
+
to_agent_id: envelope.to_agent_id,
|
|
5396
|
+
envelope_type: envelope.type,
|
|
5397
|
+
...typeof ack.recipient_live === "boolean" ? { recipient_live: ack.recipient_live } : {}
|
|
5398
|
+
});
|
|
5399
|
+
if (ack.recipient_live === true) {
|
|
5400
|
+
return { ...ack, text: `Sent ${envelope.type} to ${envelope.to_agent_id} \u2014 recipient's agent is connected (host id ${ack.id}).` };
|
|
5401
|
+
}
|
|
5402
|
+
if (ack.recipient_live === false) {
|
|
5403
|
+
return { ...ack, text: `Queued ${envelope.type} to ${envelope.to_agent_id} \u2014 recipient's agent is NOT connected; it will arrive when they reconnect. Check later: edge-book outbox (host id ${ack.id}).` };
|
|
5404
|
+
}
|
|
5405
|
+
return { ...ack, text: legacyText(ack.id) };
|
|
5406
|
+
}
|
|
5328
5407
|
async function broadcastPost(store, host, socketFactory2, post) {
|
|
5329
5408
|
const contacts = await store.contacts();
|
|
5330
5409
|
const friends = Object.values(contacts).filter((c) => c.relationship_state === "friend");
|
|
5331
5410
|
let count = 0;
|
|
5332
5411
|
for (const f of friends) {
|
|
5333
5412
|
const envelope = await store.signPostPublishEnvelope({ to_agent_id: f.peer_agent_id, post });
|
|
5334
|
-
await
|
|
5413
|
+
await deliverViaMailboxRecorded(
|
|
5414
|
+
envelope,
|
|
5415
|
+
{ home: store.home, host, socketFactory: socketFactory2 },
|
|
5416
|
+
(id) => `Delivered post_publish (host id ${id})`
|
|
5417
|
+
);
|
|
5335
5418
|
count++;
|
|
5336
5419
|
}
|
|
5337
5420
|
return count;
|
|
@@ -5369,7 +5452,7 @@ ${buildHandleNudge(suggestion)}` };
|
|
|
5369
5452
|
|
|
5370
5453
|
// src/cli-identity.ts
|
|
5371
5454
|
import fs9 from "fs/promises";
|
|
5372
|
-
import
|
|
5455
|
+
import path10 from "path";
|
|
5373
5456
|
|
|
5374
5457
|
// src/onboarding.ts
|
|
5375
5458
|
var ONBOARDING_MENTAL_MODEL = "Edge Book is a permissioned room between agents \u2014 you decide who comes in, what they can see, and you can take it back anytime.";
|
|
@@ -5490,8 +5573,8 @@ ${id.agent_id}`, json: { handle: id.handle, agent_id: id.agent_id } };
|
|
|
5490
5573
|
const bundle = await store.exportIdentity();
|
|
5491
5574
|
const p = takeFlag(args, "--path");
|
|
5492
5575
|
if (p) {
|
|
5493
|
-
const target =
|
|
5494
|
-
await fs9.mkdir(
|
|
5576
|
+
const target = path10.resolve(p);
|
|
5577
|
+
await fs9.mkdir(path10.dirname(target), { recursive: true });
|
|
5495
5578
|
await fs9.writeFile(target, `${JSON.stringify(bundle, null, 2)}
|
|
5496
5579
|
`, { encoding: "utf8", mode: 384 });
|
|
5497
5580
|
return { text: `Identity exported \u2192 ${target}`, json: { path: target } };
|
|
@@ -5501,7 +5584,7 @@ ${id.agent_id}`, json: { handle: id.handle, agent_id: id.agent_id } };
|
|
|
5501
5584
|
if (action === "import") {
|
|
5502
5585
|
const source = requireArg(args.shift(), "identity import <path>");
|
|
5503
5586
|
const force = takeBoolFlag(args, "--force");
|
|
5504
|
-
const bundle = JSON.parse(await fs9.readFile(
|
|
5587
|
+
const bundle = JSON.parse(await fs9.readFile(path10.resolve(source), "utf8"));
|
|
5505
5588
|
const id = await store.importIdentity(bundle, { force });
|
|
5506
5589
|
return { text: `Identity imported: ${id.handle} (${id.agent_id})`, json: { handle: id.handle, agent_id: id.agent_id } };
|
|
5507
5590
|
}
|
|
@@ -5587,7 +5670,11 @@ visibility: ${JSON.stringify(p.visibility ?? {})}`,
|
|
|
5587
5670
|
await deliverToPeer(store, envelope, envelope.to_agent_id);
|
|
5588
5671
|
} catch (error) {
|
|
5589
5672
|
if (!(error instanceof EdgeBookError) || error.code !== "no_route") throw error;
|
|
5590
|
-
await
|
|
5673
|
+
await deliverViaMailboxRecorded(
|
|
5674
|
+
envelope,
|
|
5675
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
5676
|
+
(id) => `Delivered profile_share (host id ${id})`
|
|
5677
|
+
);
|
|
5591
5678
|
}
|
|
5592
5679
|
}
|
|
5593
5680
|
return { text: `Broadcast profile to ${envelopes.length} friend(s)`, json: { count: envelopes.length } };
|
|
@@ -5609,10 +5696,10 @@ visibility: ${JSON.stringify(p.visibility ?? {})}`,
|
|
|
5609
5696
|
if (action === "export") {
|
|
5610
5697
|
const target = requireArg(takeFlag(args, "--path"), "--path");
|
|
5611
5698
|
const card = await store.writeCard();
|
|
5612
|
-
await fs9.mkdir(
|
|
5613
|
-
await fs9.writeFile(
|
|
5699
|
+
await fs9.mkdir(path10.dirname(path10.resolve(target)), { recursive: true });
|
|
5700
|
+
await fs9.writeFile(path10.resolve(target), `${JSON.stringify(card, null, 2)}
|
|
5614
5701
|
`, "utf8");
|
|
5615
|
-
return { text: `Exported Agent Card to ${
|
|
5702
|
+
return { text: `Exported Agent Card to ${path10.resolve(target)}`, json: card };
|
|
5616
5703
|
}
|
|
5617
5704
|
if (action === "invite") {
|
|
5618
5705
|
const ttlMsStr = takeFlag(args, "--ttl-ms");
|
|
@@ -5633,7 +5720,7 @@ visibility: ${JSON.stringify(p.visibility ?? {})}`,
|
|
|
5633
5720
|
}
|
|
5634
5721
|
|
|
5635
5722
|
// src/cli-social.ts
|
|
5636
|
-
import
|
|
5723
|
+
import path11 from "path";
|
|
5637
5724
|
|
|
5638
5725
|
// src/store-greeter.ts
|
|
5639
5726
|
var GREETER_WELCOME_TITLE = "Welcome to Edge Book";
|
|
@@ -5730,8 +5817,12 @@ next: ${result.next_action}`, json: result };
|
|
|
5730
5817
|
return { text: `Queued friend_request via relay ${relay}`, json: envelope };
|
|
5731
5818
|
}
|
|
5732
5819
|
const hostUrl = parseHost(args, ctx);
|
|
5733
|
-
const
|
|
5734
|
-
|
|
5820
|
+
const outcome = await deliverViaMailboxRecorded(
|
|
5821
|
+
envelope,
|
|
5822
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
5823
|
+
(id) => `Delivered friend_request to ${card.agent_id} over the mailbox (host id ${id})`
|
|
5824
|
+
);
|
|
5825
|
+
return { text: outcome.text, json: envelope };
|
|
5735
5826
|
}
|
|
5736
5827
|
return { text: JSON.stringify(envelope, null, 2), json: envelope };
|
|
5737
5828
|
}
|
|
@@ -5750,8 +5841,12 @@ next: ${result.next_action}`, json: result };
|
|
|
5750
5841
|
} catch (error) {
|
|
5751
5842
|
if (!(error instanceof EdgeBookError) || error.code !== "no_route") throw error;
|
|
5752
5843
|
const hostUrl = parseHost(args, ctx);
|
|
5753
|
-
const
|
|
5754
|
-
|
|
5844
|
+
const outcome = await deliverViaMailboxRecorded(
|
|
5845
|
+
envelope,
|
|
5846
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
5847
|
+
(id) => `Delivered friend_response to ${peer} over the mailbox (host id ${id})`
|
|
5848
|
+
);
|
|
5849
|
+
return { text: outcome.text, json: envelope };
|
|
5755
5850
|
}
|
|
5756
5851
|
}
|
|
5757
5852
|
return { text: JSON.stringify(envelope, null, 2), json: envelope };
|
|
@@ -5760,15 +5855,19 @@ next: ${result.next_action}`, json: result };
|
|
|
5760
5855
|
const deliver = takeBoolFlag(args, "--deliver");
|
|
5761
5856
|
const source = requireArg(args.shift(), "envelope-json-path");
|
|
5762
5857
|
const followUp = await store.applyFriendResponse(await readEnvelope(source));
|
|
5763
|
-
if (!followUp) return { text: `Applied friend response from ${
|
|
5858
|
+
if (!followUp) return { text: `Applied friend response from ${path11.resolve(source)}` };
|
|
5764
5859
|
if (deliver) {
|
|
5765
5860
|
try {
|
|
5766
5861
|
return { text: await deliverToPeer(store, followUp, followUp.to_agent_id), json: followUp };
|
|
5767
5862
|
} catch (error) {
|
|
5768
5863
|
if (!(error instanceof EdgeBookError) || error.code !== "no_route") throw error;
|
|
5769
5864
|
const hostUrl = parseHost(args, ctx);
|
|
5770
|
-
const
|
|
5771
|
-
|
|
5865
|
+
const outcome = await deliverViaMailboxRecorded(
|
|
5866
|
+
followUp,
|
|
5867
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
5868
|
+
(id) => `delivered profile_share to ${followUp.to_agent_id} over the mailbox (host id ${id})`
|
|
5869
|
+
);
|
|
5870
|
+
return { text: `Applied response; ${outcome.text}`, json: followUp };
|
|
5772
5871
|
}
|
|
5773
5872
|
}
|
|
5774
5873
|
return { text: `Applied friend response; deliver this profile_share to ${followUp.to_agent_id}`, json: followUp };
|
|
@@ -5821,7 +5920,11 @@ next: ${result.next_action}`, json: result };
|
|
|
5821
5920
|
await deliverToPeer(store, envelope, envelope.to_agent_id);
|
|
5822
5921
|
} catch (error) {
|
|
5823
5922
|
if (!(error instanceof EdgeBookError) || error.code !== "no_route") throw error;
|
|
5824
|
-
await
|
|
5923
|
+
await deliverViaMailboxRecorded(
|
|
5924
|
+
envelope,
|
|
5925
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
5926
|
+
(id) => `Delivered ${envelope.type} (host id ${id})`
|
|
5927
|
+
);
|
|
5825
5928
|
}
|
|
5826
5929
|
}
|
|
5827
5930
|
}
|
|
@@ -5855,8 +5958,8 @@ next: ${result.next_action}`, json: result };
|
|
|
5855
5958
|
const file = takeFlag(args, "--file");
|
|
5856
5959
|
let attachment;
|
|
5857
5960
|
if (file) {
|
|
5858
|
-
const bytes = await fs10.readFile(
|
|
5859
|
-
attachment = { filename:
|
|
5961
|
+
const bytes = await fs10.readFile(path11.resolve(file));
|
|
5962
|
+
attachment = { filename: path11.basename(file), mime: takeFlag(args, "--mime") || "application/octet-stream", bytes };
|
|
5860
5963
|
}
|
|
5861
5964
|
const object = await store.createObject({ title, body, attachment });
|
|
5862
5965
|
return { text: `Created object ${object.object_id}`, json: object };
|
|
@@ -5868,8 +5971,12 @@ next: ${result.next_action}`, json: result };
|
|
|
5868
5971
|
const objectId = requireArg(args.shift(), "object-id");
|
|
5869
5972
|
const envelope = await store.shareObjectEnvelope(peer, objectId);
|
|
5870
5973
|
if (deliver) {
|
|
5871
|
-
const
|
|
5872
|
-
|
|
5974
|
+
const outcome = await deliverViaMailboxRecorded(
|
|
5975
|
+
envelope,
|
|
5976
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
5977
|
+
(id) => `Shared object ${objectId} to ${peer} over the mailbox (host id ${id})`
|
|
5978
|
+
);
|
|
5979
|
+
return { text: outcome.text, json: envelope };
|
|
5873
5980
|
}
|
|
5874
5981
|
return { text: JSON.stringify(envelope, null, 2), json: envelope };
|
|
5875
5982
|
}
|
|
@@ -5880,15 +5987,19 @@ next: ${result.next_action}`, json: result };
|
|
|
5880
5987
|
const objectId = requireArg(args.shift(), "object-id");
|
|
5881
5988
|
const envelope = await store.revokeObjectEnvelope(peer, objectId);
|
|
5882
5989
|
if (deliver) {
|
|
5883
|
-
const
|
|
5884
|
-
|
|
5990
|
+
const outcome = await deliverViaMailboxRecorded(
|
|
5991
|
+
envelope,
|
|
5992
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
5993
|
+
(id) => `Revoked object ${objectId} for ${peer}; forwarded over the mailbox (host id ${id})`
|
|
5994
|
+
);
|
|
5995
|
+
return { text: outcome.text, json: envelope };
|
|
5885
5996
|
}
|
|
5886
5997
|
return { text: JSON.stringify(envelope, null, 2), json: envelope };
|
|
5887
5998
|
}
|
|
5888
5999
|
if (action === "receive") {
|
|
5889
6000
|
const source = requireArg(args.shift(), "envelope-json-path");
|
|
5890
6001
|
await store.receiveEnvelope(await readEnvelope(source));
|
|
5891
|
-
return { text: `Applied object envelope from ${
|
|
6002
|
+
return { text: `Applied object envelope from ${path11.resolve(source)}` };
|
|
5892
6003
|
}
|
|
5893
6004
|
if (action === "list") {
|
|
5894
6005
|
const objects2 = await store.sharedObjectsFor();
|
|
@@ -5927,7 +6038,7 @@ next: ${result.next_action}`, json: result };
|
|
|
5927
6038
|
if (action === "receive") {
|
|
5928
6039
|
const source = requireArg(args.shift(), "envelope-json-path");
|
|
5929
6040
|
await store.receivePrivilegedMessage(await readEnvelope(source));
|
|
5930
|
-
return { text: `Received privileged message from ${
|
|
6041
|
+
return { text: `Received privileged message from ${path11.resolve(source)}` };
|
|
5931
6042
|
}
|
|
5932
6043
|
}
|
|
5933
6044
|
if (command === "escalation") {
|
|
@@ -5946,8 +6057,12 @@ next: ${result.next_action}`, json: result };
|
|
|
5946
6057
|
const { escalation, envelope } = await store.raiseEscalation({ kind, subject, body, to, options, collaborators, contextRefs, riskLevel });
|
|
5947
6058
|
if (envelope) {
|
|
5948
6059
|
if (deliver) {
|
|
5949
|
-
const
|
|
5950
|
-
|
|
6060
|
+
const outcome = await deliverViaMailboxRecorded(
|
|
6061
|
+
envelope,
|
|
6062
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
6063
|
+
(id) => `delivered to ${to} over the mailbox (host id ${id})`
|
|
6064
|
+
);
|
|
6065
|
+
return { text: `Raised escalation ${escalation.escalation_id}; ${outcome.text}`, json: envelope };
|
|
5951
6066
|
}
|
|
5952
6067
|
return { text: `Raised escalation ${escalation.escalation_id} for ${to}; deliver this envelope (or pass --deliver)`, json: envelope };
|
|
5953
6068
|
}
|
|
@@ -5970,8 +6085,12 @@ next: ${result.next_action}`, json: result };
|
|
|
5970
6085
|
const choice = takeFlag(args, "--choice");
|
|
5971
6086
|
const { envelope, ...escalation } = await store.answerEscalation(escalationId, { text, choice });
|
|
5972
6087
|
if (envelope && deliver) {
|
|
5973
|
-
const
|
|
5974
|
-
|
|
6088
|
+
const outcome = await deliverViaMailboxRecorded(
|
|
6089
|
+
envelope,
|
|
6090
|
+
{ home, host: hostUrl, socketFactory: ctx.socketFactory },
|
|
6091
|
+
(id) => `routed response to ${envelope.to_agent_id} over the mailbox (host id ${id})`
|
|
6092
|
+
);
|
|
6093
|
+
return { text: `Answered ${escalationId}; ${outcome.text}`, json: { ...escalation, response_envelope: envelope } };
|
|
5975
6094
|
}
|
|
5976
6095
|
const tail = envelope ? `; deliver the response envelope to ${envelope.to_agent_id} (or pass --deliver)` : "";
|
|
5977
6096
|
return { text: `Answered escalation ${escalationId}${tail}`, json: { ...escalation, response_envelope: envelope } };
|
|
@@ -6209,6 +6328,10 @@ var COMMAND_GROUPS = [
|
|
|
6209
6328
|
{
|
|
6210
6329
|
usage: "sessions revoke [--device <id>] [--host <wss-url>]",
|
|
6211
6330
|
desc: "Revoke one device session (or all if no --device)"
|
|
6331
|
+
},
|
|
6332
|
+
{
|
|
6333
|
+
usage: "outbox [--json] [--host <wss-url>]",
|
|
6334
|
+
desc: "Delivery state of recently sent envelopes (queued / delivered / acked) with stale-queue warnings"
|
|
6212
6335
|
}
|
|
6213
6336
|
]
|
|
6214
6337
|
},
|
|
@@ -6794,6 +6917,47 @@ Expires in: ${registration.frame.ttl_ms}ms`, json: registration };
|
|
|
6794
6917
|
return { text: `Received sessions_revoke_ok for request ${frame.request_id} on ${channel}`, json: frame };
|
|
6795
6918
|
}
|
|
6796
6919
|
}
|
|
6920
|
+
if (command === "outbox") {
|
|
6921
|
+
const asJson = takeBoolFlag(args, "--json");
|
|
6922
|
+
const hostUrl = parseHost(args, ctx);
|
|
6923
|
+
const entries = await readOutbox(home);
|
|
6924
|
+
if (entries.length === 0) {
|
|
6925
|
+
return { text: "Outbox is empty \u2014 nothing has been sent with --deliver yet.", json: { entries: [] } };
|
|
6926
|
+
}
|
|
6927
|
+
const recent = entries.slice(-50);
|
|
6928
|
+
let statuses = null;
|
|
6929
|
+
let unreachable = false;
|
|
6930
|
+
try {
|
|
6931
|
+
statuses = await mailboxStatus({ home, host: hostUrl, socketFactory: ctx.socketFactory, ids: recent.map((e) => e.id) });
|
|
6932
|
+
} catch (error) {
|
|
6933
|
+
statuses = null;
|
|
6934
|
+
unreachable = !(error instanceof EdgeBookError && (error.code === "host_unsupported_rpc" || error.code === "host_rpc_timeout"));
|
|
6935
|
+
}
|
|
6936
|
+
const byId = new Map((statuses ?? []).map((s) => [s.id, s]));
|
|
6937
|
+
const contacts = await store.contacts();
|
|
6938
|
+
const staleMs = staleQueueMs();
|
|
6939
|
+
const report = recent.map((entry) => {
|
|
6940
|
+
const status = byId.get(entry.id);
|
|
6941
|
+
const state = statuses === null ? unreachable ? "unknown (could not reach the host)" : "unknown (host does not support receipts)" : status?.state ?? "unknown";
|
|
6942
|
+
const age = formatAge(Date.now() - Date.parse(entry.sent_at));
|
|
6943
|
+
const stale = status?.state === "queued" && ((status.queued_ms ?? 0) > staleMs || status.recipient_live === false);
|
|
6944
|
+
return {
|
|
6945
|
+
...entry,
|
|
6946
|
+
to_display_name: contacts[entry.to_agent_id]?.display_name || entry.to_agent_id,
|
|
6947
|
+
age,
|
|
6948
|
+
state,
|
|
6949
|
+
...status?.queued_ms !== void 0 ? { queued_ms: status.queued_ms } : {},
|
|
6950
|
+
...status?.recipient_live !== void 0 ? { recipient_live: status.recipient_live } : {},
|
|
6951
|
+
stale: Boolean(stale)
|
|
6952
|
+
};
|
|
6953
|
+
});
|
|
6954
|
+
const lines = report.map((r) => {
|
|
6955
|
+
const base = `${r.id} ${r.envelope_type} \u2192 ${r.to_display_name} (${r.age} ago) ${r.state}`;
|
|
6956
|
+
return r.stale ? `${base}
|
|
6957
|
+
\u26A0 undelivered for ${r.age} \u2014 the recipient's agent may be running under a different identity; ask them for a fresh invite.` : base;
|
|
6958
|
+
});
|
|
6959
|
+
return { text: asJson ? JSON.stringify({ entries: report }, null, 2) : lines.join("\n"), json: { entries: report } };
|
|
6960
|
+
}
|
|
6797
6961
|
if (command === "relay") {
|
|
6798
6962
|
const action = args.shift();
|
|
6799
6963
|
if (action === "serve") {
|
|
@@ -6818,8 +6982,14 @@ ${JSON.stringify(result, null, 2)}`, json: result };
|
|
|
6818
6982
|
throw new EdgeBookError("unknown_command", usage());
|
|
6819
6983
|
}
|
|
6820
6984
|
async function runCli(args) {
|
|
6821
|
-
const
|
|
6822
|
-
|
|
6985
|
+
const argv = [...args];
|
|
6986
|
+
const asJson = takeBoolFlag(argv, "--json");
|
|
6987
|
+
const result = await handleCli(argv);
|
|
6988
|
+
if (asJson && result.json !== void 0) {
|
|
6989
|
+
console.log(JSON.stringify(result.json, null, 2));
|
|
6990
|
+
} else {
|
|
6991
|
+
console.log(result.text);
|
|
6992
|
+
}
|
|
6823
6993
|
}
|
|
6824
6994
|
function isCliEntrypoint() {
|
|
6825
6995
|
if (!process.argv[1]) return false;
|