@primusclaw/personal-agent-runtime 0.2.0 → 0.3.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/index.js CHANGED
@@ -3360,6 +3360,7 @@ var SqliteIdempotencyRepository = class {
3360
3360
  };
3361
3361
 
3362
3362
  // packages/runtime/src/sqlite/link-repository.ts
3363
+ import { createHash as createHash2 } from "node:crypto";
3363
3364
  import {
3364
3365
  CODE_ALPHABET,
3365
3366
  CODE_LENGTH,
@@ -3378,8 +3379,12 @@ function forgetRepos(db, userId) {
3378
3379
  }
3379
3380
 
3380
3381
  // packages/runtime/src/sqlite/link-repository.ts
3382
+ function tokenHash(credential) {
3383
+ return createHash2("sha256").update(credential).digest("hex");
3384
+ }
3381
3385
  var PERSON_TABLES = [
3382
3386
  "links",
3387
+ "bound_users",
3383
3388
  "sessions",
3384
3389
  "conversation_refs",
3385
3390
  "held_events",
@@ -3478,6 +3483,7 @@ var SqliteLinkRepository = class {
3478
3483
  col.bytes(row, "key_enc"),
3479
3484
  now
3480
3485
  );
3486
+ db.run("DELETE FROM bound_users WHERE aad_id = ?", userId);
3481
3487
  forgetNotifySub(db, userId);
3482
3488
  forgetRepos(db, userId);
3483
3489
  db.run("DELETE FROM sessions WHERE aad_id = ?", userId);
@@ -3510,12 +3516,39 @@ var SqliteLinkRepository = class {
3510
3516
  setClawUserId(userId, clawUserId) {
3511
3517
  this.ctx.db.run("UPDATE links SET claw_user_id = ? WHERE aad_id = ?", clawUserId, userId);
3512
3518
  }
3519
+ boundClawUser(userId, credential) {
3520
+ const row = this.ctx.db.get("SELECT claw_user_id, token_hash FROM bound_users WHERE aad_id = ?", userId);
3521
+ if (row === void 0) return null;
3522
+ return { clawUserId: col.str(row, "claw_user_id"), current: col.str(row, "token_hash") === tokenHash(credential) };
3523
+ }
3524
+ /** Ignored for a linked person (a link's id is read for its key). Another account than before forgets the subscription. */
3525
+ setBoundClawUser(userId, clawUserId, credential) {
3526
+ const { db } = this.ctx;
3527
+ if (db.get("SELECT 1 AS one FROM links WHERE aad_id = ?", userId) !== void 0) return;
3528
+ const before = db.get("SELECT claw_user_id FROM bound_users WHERE aad_id = ?", userId);
3529
+ if (before !== void 0 && col.str(before, "claw_user_id") !== clawUserId) forgetNotifySub(db, userId);
3530
+ db.run(
3531
+ "INSERT OR REPLACE INTO bound_users (aad_id, claw_user_id, token_hash, updated_at) VALUES (?, ?, ?, ?)",
3532
+ userId,
3533
+ clawUserId,
3534
+ tokenHash(credential),
3535
+ this.ctx.now()
3536
+ );
3537
+ }
3538
+ /** Ignored for a linked person: their link keeps its own subscription. */
3539
+ forgetBound(userId) {
3540
+ const { db } = this.ctx;
3541
+ if (db.get("SELECT 1 AS one FROM links WHERE aad_id = ?", userId) !== void 0) return;
3542
+ if (db.run("DELETE FROM bound_users WHERE aad_id = ?", userId) > 0) forgetNotifySub(db, userId);
3543
+ }
3513
3544
  needsClawUserId(userId) {
3514
3545
  const row = this.ctx.db.get("SELECT claw_user_id FROM links WHERE aad_id = ?", userId);
3515
3546
  return row !== void 0 && !col.bool(row, "claw_user_id");
3516
3547
  }
3548
+ /** The link's (a linked person's id is never taken from anywhere else), else the bound person's. */
3517
3549
  clawUserIdOf(userId) {
3518
- const row = this.ctx.db.get("SELECT claw_user_id FROM links WHERE aad_id = ?", userId);
3550
+ const { db } = this.ctx;
3551
+ const row = db.get("SELECT claw_user_id FROM links WHERE aad_id = ?", userId) ?? db.get("SELECT claw_user_id FROM bound_users WHERE aad_id = ?", userId);
3519
3552
  return row && col.bool(row, "claw_user_id") ? col.str(row, "claw_user_id") : null;
3520
3553
  }
3521
3554
  linkedUsers() {
@@ -122,3 +122,5 @@ CREATE TABLE IF NOT EXISTS observe_cards (
122
122
  CREATE INDEX IF NOT EXISTS observe_cards_by_suggestion ON observe_cards (aad_id, run_id, seq);
123
123
  CREATE TABLE IF NOT EXISTS group_context_off (
124
124
  conversation_id TEXT PRIMARY KEY, updated_at REAL NOT NULL);
125
+ CREATE TABLE IF NOT EXISTS bound_users (
126
+ aad_id TEXT PRIMARY KEY, claw_user_id TEXT NOT NULL, token_hash TEXT NOT NULL, updated_at REAL NOT NULL);
@@ -1,4 +1,3 @@
1
- /** LinkRepository on SQLite (tables `links`, `pending_keys`, `link_attempts`). */
2
1
  import { type LinkLimits, type LinkRepository, type RedeemOutcome } from "@primusclaw/personal-agent";
3
2
  import { type StoreContext, type SecurityEventSink } from "./context.js";
4
3
  export declare class SqliteLinkRepository implements LinkRepository {
@@ -19,7 +18,16 @@ export declare class SqliteLinkRepository implements LinkRepository {
19
18
  getKey(userId: string): string | null;
20
19
  unlink(userId: string): void;
21
20
  setClawUserId(userId: string, clawUserId: string): void;
21
+ boundClawUser(userId: string, credential: string): {
22
+ clawUserId: string;
23
+ current: boolean;
24
+ } | null;
25
+ /** Ignored for a linked person (a link's id is read for its key). Another account than before forgets the subscription. */
26
+ setBoundClawUser(userId: string, clawUserId: string, credential: string): void;
27
+ /** Ignored for a linked person: their link keeps its own subscription. */
28
+ forgetBound(userId: string): void;
22
29
  needsClawUserId(userId: string): boolean;
30
+ /** The link's (a linked person's id is never taken from anywhere else), else the bound person's. */
23
31
  clawUserIdOf(userId: string): string | null;
24
32
  linkedUsers(): string[];
25
33
  userForClawUser(clawUserId: string): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primusclaw/personal-agent-runtime",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Node adapters for the personal agent's ports: clock, randomness, logging (and, as the port lands, storage, Claw, metrics, HTTP).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@primusclaw/personal-agent": "0.2.0",
32
+ "@primusclaw/personal-agent": "0.3.0",
33
33
  "fastify": "^5.12.8",
34
34
  "zod": "^4.6.7"
35
35
  }