edge-book 0.2.2 → 0.2.4

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.
Files changed (2) hide show
  1. package/dist/edge-book.js +112 -3
  2. package/package.json +1 -1
package/dist/edge-book.js CHANGED
@@ -179,6 +179,19 @@ var EdgeBookStore = class {
179
179
  if (!identity) throw new EdgeBookError("not_initialized", `Edge Book is not initialized at ${this.home}`);
180
180
  return identity;
181
181
  }
182
+ // Update profile fields on an existing identity without rotating keys, so the
183
+ // agent_id (and any pairing built on it) survives. `owner_label` is the human
184
+ // who owns the agent; `display_name` is the agent's own name.
185
+ async setProfile(input) {
186
+ const identity = await this.identity();
187
+ if (input.displayName !== void 0 && input.displayName !== "") identity.display_name = input.displayName;
188
+ if (input.ownerLabel !== void 0) identity.owner_label = input.ownerLabel;
189
+ identity.updated_at = now();
190
+ await writeJson(this.file(IDENTITY_FILE), identity, 384);
191
+ await this.writeCard();
192
+ await this.audit("identity.update", identity.agent_id, { display_name: identity.display_name, owner_label: identity.owner_label });
193
+ return identity;
194
+ }
182
195
  async config() {
183
196
  return readJson(this.file(CONFIG_FILE), {});
184
197
  }
@@ -1249,6 +1262,7 @@ function publicIdentity(identity) {
1249
1262
  handle: identity.handle,
1250
1263
  name: identity.display_name,
1251
1264
  display_name: identity.display_name,
1265
+ owner_label: identity.owner_label,
1252
1266
  public_key: compactPem(identity.public_key_pem)
1253
1267
  };
1254
1268
  }
@@ -2778,6 +2792,8 @@ var EdgeBookDialoutClient = class {
2778
2792
  currentBackoff;
2779
2793
  opened;
2780
2794
  pendingSessionRevokes = /* @__PURE__ */ new Map();
2795
+ // Generic request_id-keyed RPC waiters for sessions_list / session_revoke_one.
2796
+ pendingRpc = /* @__PURE__ */ new Map();
2781
2797
  pendingMailboxSends = /* @__PURE__ */ new Map();
2782
2798
  constructor(options) {
2783
2799
  this.options = {
@@ -2817,6 +2833,30 @@ var EdgeBookDialoutClient = class {
2817
2833
  this.send(frame);
2818
2834
  return frame;
2819
2835
  }
2836
+ // List this agent's remembered devices on the host (ea-claude-057).
2837
+ async listSessionsAndWait(timeoutMs = 5e3) {
2838
+ const frame = await this.rpc("sessions_list", {}, "sessions_list_ok", timeoutMs);
2839
+ return frame.devices || [];
2840
+ }
2841
+ // Revoke ONE device by its public device_id (ea-claude-057).
2842
+ async revokeOneSessionAndWait(device_id, timeoutMs = 5e3) {
2843
+ const frame = await this.rpc("session_revoke_one", { device_id }, "session_revoke_one_ok", timeoutMs);
2844
+ return Boolean(frame.revoked);
2845
+ }
2846
+ // Small request/response helper over the dial-out socket, correlated by
2847
+ // request_id. `expect` documents the ack type; resolution is by request_id.
2848
+ async rpc(type, extra, expect, timeoutMs) {
2849
+ const request_id = crypto2.randomUUID();
2850
+ const promise = new Promise((resolve, reject) => {
2851
+ const timer = setTimeout(() => {
2852
+ this.pendingRpc.delete(request_id);
2853
+ reject(new EdgeBookError("host_rpc_timeout", `Timed out waiting for ${expect}`));
2854
+ }, timeoutMs);
2855
+ this.pendingRpc.set(request_id, { resolve, reject, timer });
2856
+ });
2857
+ this.send({ type, request_id, ...extra });
2858
+ return promise;
2859
+ }
2820
2860
  async revokeSessionsAndWait(timeoutMs = 5e3) {
2821
2861
  const frame = await createSessionsRevokeFrame(this.store);
2822
2862
  const ackPromise = new Promise((resolve, reject) => {
@@ -2959,6 +2999,16 @@ var EdgeBookDialoutClient = class {
2959
2999
  this.send({ type: "pong" });
2960
3000
  return;
2961
3001
  }
3002
+ if (frame.type === "sessions_list_ok" || frame.type === "session_revoke_one_ok") {
3003
+ const ack = frame;
3004
+ const pending = this.pendingRpc.get(ack.request_id || "");
3005
+ if (pending) {
3006
+ clearTimeout(pending.timer);
3007
+ this.pendingRpc.delete(ack.request_id || "");
3008
+ pending.resolve(frame);
3009
+ }
3010
+ return;
3011
+ }
2962
3012
  if (frame.type === "stand_down" || frame.type === "dialout_idle") {
2963
3013
  await this.standDown(frame);
2964
3014
  return;
@@ -3082,18 +3132,41 @@ async function sendSessionsRevoke(options) {
3082
3132
  await client.stop();
3083
3133
  return { ...frame, channel_id: ack.channel_id };
3084
3134
  }
3135
+ async function listSessions(options) {
3136
+ const client = new EdgeBookDialoutClient({ ...options, reconnect: false, openLocalApi: false });
3137
+ await client.start();
3138
+ await new Promise((resolve) => setTimeout(resolve, 0));
3139
+ try {
3140
+ return await client.listSessionsAndWait();
3141
+ } finally {
3142
+ await client.stop();
3143
+ }
3144
+ }
3145
+ async function revokeOneSession(options) {
3146
+ const client = new EdgeBookDialoutClient({ ...options, reconnect: false, openLocalApi: false });
3147
+ await client.start();
3148
+ await new Promise((resolve) => setTimeout(resolve, 0));
3149
+ try {
3150
+ return await client.revokeOneSessionAndWait(options.deviceId);
3151
+ } finally {
3152
+ await client.stop();
3153
+ }
3154
+ }
3085
3155
 
3086
3156
  // src/cli.ts
3087
3157
  function usage() {
3088
3158
  return `Edge Book
3089
3159
 
3090
3160
  Usage:
3091
- edge-book init [--home <dir>] [--handle <handle>] [--name <display>]
3161
+ edge-book init [--home <dir>] [--handle <handle>] [--name <agent name>] [--owner <human owner>]
3162
+ edge-book profile show [--home <dir>]
3163
+ edge-book profile set [--name <agent name>] [--owner <human owner>] [--home <dir>]
3092
3164
 
3093
3165
  Hosted reader:
3094
3166
  edge-book dialout [--host <ws-url>] [--home <dir>]
3095
3167
  edge-book pair [--host <ws-url>] [--ttl-ms <ms>] [--home <dir>]
3096
- edge-book sessions revoke [--host <ws-url>] [--home <dir>]
3168
+ edge-book sessions list [--host <ws-url>] [--home <dir>]
3169
+ edge-book sessions revoke [--device <id>] [--host <ws-url>] [--home <dir>]
3097
3170
 
3098
3171
  Local agent:
3099
3172
  edge-book doctor [--home <dir>]
@@ -3179,11 +3252,36 @@ async function handleCli(inputArgs, ctx = {}) {
3179
3252
  if (command === "init") {
3180
3253
  const handle = takeFlag(args, "--handle");
3181
3254
  const displayName = takeFlag(args, "--name");
3255
+ const ownerLabel = takeFlag(args, "--owner");
3182
3256
  const directUrl = takeFlag(args, "--direct-url");
3183
3257
  const relayUrl = takeFlag(args, "--relay-url");
3184
- const identity = await store.init({ handle, displayName, directUrl, relayUrl });
3258
+ const identity = await store.init({ handle, displayName, ownerLabel, directUrl, relayUrl });
3185
3259
  return { text: `Initialized ${identity.agent_id} at ${store.home}`, json: identity };
3186
3260
  }
3261
+ if (command === "profile") {
3262
+ const action = args.shift() || "show";
3263
+ if (action === "show") {
3264
+ const id = await store.identity();
3265
+ return {
3266
+ text: `display_name: ${id.display_name}
3267
+ owner_label: ${id.owner_label || "(unset)"}`,
3268
+ json: { agent_id: id.agent_id, display_name: id.display_name, owner_label: id.owner_label }
3269
+ };
3270
+ }
3271
+ if (action === "set") {
3272
+ const displayName = takeFlag(args, "--name");
3273
+ const ownerLabel = takeFlag(args, "--owner");
3274
+ if (displayName === void 0 && ownerLabel === void 0) {
3275
+ throw new EdgeBookError("missing_arg", "profile set needs --name (agent name) and/or --owner (human owner)");
3276
+ }
3277
+ const id = await store.setProfile({ displayName, ownerLabel });
3278
+ return {
3279
+ text: `Updated profile: display_name=${id.display_name} owner_label=${id.owner_label || "(unset)"}`,
3280
+ json: { agent_id: id.agent_id, display_name: id.display_name, owner_label: id.owner_label }
3281
+ };
3282
+ }
3283
+ throw new EdgeBookError("unknown_action", `Unknown profile action: ${action} (use "show" or "set")`);
3284
+ }
3187
3285
  if (command === "doctor") {
3188
3286
  const result = await store.doctor();
3189
3287
  return { text: JSON.stringify(result, null, 2), json: result };
@@ -3396,8 +3494,19 @@ Expires in: ${registration.frame.ttl_ms}ms`, json: registration };
3396
3494
  }
3397
3495
  if (command === "sessions") {
3398
3496
  const action = args.shift();
3497
+ if (action === "list") {
3498
+ const hostUrl = parseHost(args, ctx);
3499
+ const devices = await listSessions({ home, host: hostUrl, socketFactory: ctx.socketFactory });
3500
+ const lines = devices.length ? devices.map((d) => `${d.device_id} ${d.label} (added ${new Date(d.created_at).toISOString()}, last seen ${new Date(d.last_seen_at).toISOString()})`).join("\n") : "No remembered devices.";
3501
+ return { text: lines, json: { devices } };
3502
+ }
3399
3503
  if (action === "revoke") {
3400
3504
  const hostUrl = parseHost(args, ctx);
3505
+ const deviceId = takeFlag(args, "--device");
3506
+ if (deviceId) {
3507
+ const revoked = await revokeOneSession({ home, host: hostUrl, socketFactory: ctx.socketFactory, deviceId });
3508
+ return { text: revoked ? `Revoked device ${deviceId}` : `No device ${deviceId} found on your channel`, json: { device_id: deviceId, revoked } };
3509
+ }
3401
3510
  const frame = await sendSessionsRevoke({ home, host: hostUrl, socketFactory: ctx.socketFactory });
3402
3511
  const channel = frame.channel_id || "unknown-channel";
3403
3512
  return { text: `Received sessions_revoke_ok for request ${frame.request_id} on ${channel}`, json: frame };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edge-book",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Run your own Edge Book agent and connect it to the hosted reader.",
5
5
  "license": "MIT",
6
6
  "type": "module",