@standardagents/builder 0.17.3 → 0.18.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/runtime.js CHANGED
@@ -12556,7 +12556,7 @@ function isValidUserToken(token) {
12556
12556
  return token.startsWith("agtuser_") && token.length > 10;
12557
12557
  }
12558
12558
  function isValidApiKey(key) {
12559
- return key.startsWith("agtbldr_") && key.length > 10;
12559
+ return key.startsWith("agtbldr_") && key.length > 10 || key.startsWith("sak_live_") && key.length > 10;
12560
12560
  }
12561
12561
  async function verifySignedToken(signedToken, encryptionKey) {
12562
12562
  try {
@@ -12680,7 +12680,11 @@ async function authenticate(request, env) {
12680
12680
  user: {
12681
12681
  id: user.id,
12682
12682
  username: user.username,
12683
- role: user.role
12683
+ role: user.role,
12684
+ platform_user_id: user.platform_user_id ?? null,
12685
+ email: user.email ?? null,
12686
+ display_name: user.display_name ?? null,
12687
+ avatar_url: user.avatar_url ?? null
12684
12688
  },
12685
12689
  authType: "session"
12686
12690
  };
@@ -12698,7 +12702,11 @@ async function authenticate(request, env) {
12698
12702
  user: {
12699
12703
  id: user.id,
12700
12704
  username: user.username,
12701
- role: user.role
12705
+ role: user.role,
12706
+ platform_user_id: user.platform_user_id ?? null,
12707
+ email: user.email ?? null,
12708
+ display_name: user.display_name ?? null,
12709
+ avatar_url: user.avatar_url ?? null
12702
12710
  },
12703
12711
  authType: "api_key"
12704
12712
  };
@@ -15488,9 +15496,9 @@ var DurableThread = class extends DurableObject {
15488
15496
  * Each migration is run in order, starting from the current version + 1.
15489
15497
  */
15490
15498
  async runMigrations(fromVersion) {
15491
- for (const migration37 of migrations) {
15492
- if (migration37.version > fromVersion) {
15493
- await migration37.up(this.ctx.storage.sql);
15499
+ for (const migration38 of migrations) {
15500
+ if (migration38.version > fromVersion) {
15501
+ await migration38.up(this.ctx.storage.sql);
15494
15502
  }
15495
15503
  }
15496
15504
  }
@@ -18925,9 +18933,38 @@ var migration36 = {
18925
18933
  }
18926
18934
  };
18927
18935
 
18936
+ // src/durable-objects/agentbuilder-migrations/0007_platform_identity_replica.ts
18937
+ var migration37 = {
18938
+ version: 7,
18939
+ async up(sql) {
18940
+ sql.exec(`ALTER TABLE users ADD COLUMN platform_user_id TEXT`);
18941
+ sql.exec(`ALTER TABLE users ADD COLUMN email TEXT`);
18942
+ sql.exec(`ALTER TABLE users ADD COLUMN display_name TEXT`);
18943
+ sql.exec(`ALTER TABLE users ADD COLUMN avatar_url TEXT`);
18944
+ sql.exec(`ALTER TABLE users ADD COLUMN instance_role TEXT NOT NULL DEFAULT 'admin' CHECK (instance_role IN ('admin', 'user'))`);
18945
+ sql.exec(`ALTER TABLE users ADD COLUMN source TEXT NOT NULL DEFAULT 'local' CHECK (source IN ('local', 'standard_agents'))`);
18946
+ sql.exec(`ALTER TABLE users ADD COLUMN replica_active INTEGER NOT NULL DEFAULT 1`);
18947
+ sql.exec(`ALTER TABLE users ADD COLUMN replica_updated_at INTEGER`);
18948
+ sql.exec(`ALTER TABLE users ADD COLUMN deleted_at INTEGER`);
18949
+ sql.exec(`CREATE INDEX IF NOT EXISTS idx_users_platform_user_id ON users(platform_user_id)`);
18950
+ sql.exec(`CREATE INDEX IF NOT EXISTS idx_users_replica_active ON users(replica_active)`);
18951
+ sql.exec(`CREATE INDEX IF NOT EXISTS idx_users_instance_role ON users(instance_role)`);
18952
+ sql.exec(`ALTER TABLE api_keys ADD COLUMN platform_key_id TEXT`);
18953
+ sql.exec(`ALTER TABLE api_keys ADD COLUMN scope TEXT`);
18954
+ sql.exec(`ALTER TABLE api_keys ADD COLUMN source TEXT NOT NULL DEFAULT 'local' CHECK (source IN ('local', 'standard_agents'))`);
18955
+ sql.exec(`ALTER TABLE api_keys ADD COLUMN replica_active INTEGER NOT NULL DEFAULT 1`);
18956
+ sql.exec(`ALTER TABLE api_keys ADD COLUMN replica_updated_at INTEGER`);
18957
+ sql.exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_platform_key_id ON api_keys(platform_key_id)`);
18958
+ sql.exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_replica_active ON api_keys(replica_active)`);
18959
+ sql.exec(`
18960
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '7')
18961
+ `);
18962
+ }
18963
+ };
18964
+
18928
18965
  // src/durable-objects/agentbuilder-migrations/index.ts
18929
- var migrations2 = [migration31, migration32, migration33, migration34, migration35, migration36];
18930
- var LATEST_SCHEMA_VERSION2 = 6;
18966
+ var migrations2 = [migration31, migration32, migration33, migration34, migration35, migration36, migration37];
18967
+ var LATEST_SCHEMA_VERSION2 = 7;
18931
18968
 
18932
18969
  // src/utils/crypto.ts
18933
18970
  var CryptoUtil = class {
@@ -19551,9 +19588,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
19551
19588
  }
19552
19589
  }
19553
19590
  async runMigrations(fromVersion) {
19554
- for (const migration37 of migrations2) {
19555
- if (migration37.version > fromVersion) {
19556
- await migration37.up(this.ctx.storage.sql);
19591
+ for (const migration38 of migrations2) {
19592
+ if (migration38.version > fromVersion) {
19593
+ await migration38.up(this.ctx.storage.sql);
19557
19594
  }
19558
19595
  }
19559
19596
  }
@@ -20586,27 +20623,54 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20586
20623
  // ============================================================
20587
20624
  // User Authentication Methods
20588
20625
  // ============================================================
20626
+ normalizeReplicaUsername(input, fallback) {
20627
+ const normalized = (input || fallback).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 50);
20628
+ return normalized || fallback;
20629
+ }
20630
+ async availableReplicaUsername(candidate, platformUserId) {
20631
+ const existing = await this.ctx.storage.sql.exec(
20632
+ `SELECT id, platform_user_id FROM users WHERE username = ? LIMIT 1`,
20633
+ candidate
20634
+ );
20635
+ const row = existing.toArray()[0];
20636
+ if (!row || row.platform_user_id === platformUserId) {
20637
+ return candidate;
20638
+ }
20639
+ const suffix = platformUserId.replace(/[^a-zA-Z0-9]/g, "").slice(0, 8) || "user";
20640
+ const trimmed = candidate.slice(0, Math.max(1, 50 - suffix.length - 1)).replace(/-+$/g, "");
20641
+ return `${trimmed || "sa"}-${suffix}`;
20642
+ }
20643
+ userFromRow(row) {
20644
+ return {
20645
+ id: row.id,
20646
+ username: row.username,
20647
+ password_hash: row.password_hash,
20648
+ role: row.role === "user" ? "user" : "admin",
20649
+ platform_user_id: row.platform_user_id ?? null,
20650
+ email: row.email ?? null,
20651
+ display_name: row.display_name ?? null,
20652
+ avatar_url: row.avatar_url ?? null,
20653
+ source: row.source ?? "local",
20654
+ replica_active: row.replica_active ?? 1,
20655
+ created_at: row.created_at,
20656
+ updated_at: row.updated_at
20657
+ };
20658
+ }
20589
20659
  /**
20590
20660
  * Get a user by username.
20591
20661
  */
20592
20662
  async getUserByUsername(username) {
20593
20663
  await this.ensureMigrated();
20594
20664
  const cursor = await this.ctx.storage.sql.exec(
20595
- `SELECT id, username, password_hash, role, created_at, updated_at
20596
- FROM users WHERE username = ?`,
20665
+ `SELECT id, username, password_hash, COALESCE(instance_role, role) AS role,
20666
+ platform_user_id, email, display_name, avatar_url, source, replica_active,
20667
+ created_at, updated_at
20668
+ FROM users WHERE username = ? AND deleted_at IS NULL AND replica_active != 0`,
20597
20669
  username
20598
20670
  );
20599
20671
  const rows = cursor.toArray();
20600
20672
  if (rows.length === 0) return null;
20601
- const row = rows[0];
20602
- return {
20603
- id: row.id,
20604
- username: row.username,
20605
- password_hash: row.password_hash,
20606
- role: row.role,
20607
- created_at: row.created_at,
20608
- updated_at: row.updated_at
20609
- };
20673
+ return this.userFromRow(rows[0]);
20610
20674
  }
20611
20675
  /**
20612
20676
  * Get a user by ID.
@@ -20614,21 +20678,15 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20614
20678
  async getUserById(id) {
20615
20679
  await this.ensureMigrated();
20616
20680
  const cursor = await this.ctx.storage.sql.exec(
20617
- `SELECT id, username, password_hash, role, created_at, updated_at
20618
- FROM users WHERE id = ?`,
20681
+ `SELECT id, username, password_hash, COALESCE(instance_role, role) AS role,
20682
+ platform_user_id, email, display_name, avatar_url, source, replica_active,
20683
+ created_at, updated_at
20684
+ FROM users WHERE id = ? AND deleted_at IS NULL AND replica_active != 0`,
20619
20685
  id
20620
20686
  );
20621
20687
  const rows = cursor.toArray();
20622
20688
  if (rows.length === 0) return null;
20623
- const row = rows[0];
20624
- return {
20625
- id: row.id,
20626
- username: row.username,
20627
- password_hash: row.password_hash,
20628
- role: row.role,
20629
- created_at: row.created_at,
20630
- updated_at: row.updated_at
20631
- };
20689
+ return this.userFromRow(rows[0]);
20632
20690
  }
20633
20691
  /**
20634
20692
  * Create a new user.
@@ -20638,12 +20696,23 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20638
20696
  const id = crypto.randomUUID();
20639
20697
  const now = Math.floor(Date.now() / 1e3);
20640
20698
  await this.ctx.storage.sql.exec(
20641
- `INSERT INTO users (id, username, password_hash, role, created_at, updated_at)
20642
- VALUES (?, ?, ?, ?, ?, ?)`,
20699
+ `INSERT INTO users (
20700
+ id, username, password_hash, role, instance_role, platform_user_id,
20701
+ email, display_name, avatar_url, source, replica_active,
20702
+ replica_updated_at, created_at, updated_at
20703
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
20643
20704
  id,
20644
20705
  params.username,
20645
20706
  params.password_hash,
20707
+ "admin",
20646
20708
  params.role || "admin",
20709
+ params.platform_user_id ?? null,
20710
+ params.email ?? null,
20711
+ params.display_name ?? null,
20712
+ params.avatar_url ?? null,
20713
+ params.source ?? "local",
20714
+ 1,
20715
+ params.source === "standard_agents" ? now : null,
20647
20716
  now,
20648
20717
  now
20649
20718
  );
@@ -20652,6 +20721,12 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20652
20721
  username: params.username,
20653
20722
  password_hash: params.password_hash,
20654
20723
  role: params.role || "admin",
20724
+ platform_user_id: params.platform_user_id ?? null,
20725
+ email: params.email ?? null,
20726
+ display_name: params.display_name ?? null,
20727
+ avatar_url: params.avatar_url ?? null,
20728
+ source: params.source ?? "local",
20729
+ replica_active: 1,
20655
20730
  created_at: now,
20656
20731
  updated_at: now
20657
20732
  };
@@ -20671,11 +20746,24 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20671
20746
  */
20672
20747
  async listUsers() {
20673
20748
  await this.ensureMigrated();
20674
- const cursor = await this.ctx.storage.sql.exec(`SELECT id, username, role, created_at, updated_at FROM users ORDER BY created_at DESC`);
20749
+ const cursor = await this.ctx.storage.sql.exec(
20750
+ `SELECT id, username, COALESCE(instance_role, role) AS role,
20751
+ platform_user_id, email, display_name, avatar_url, source, replica_active,
20752
+ created_at, updated_at
20753
+ FROM users
20754
+ WHERE deleted_at IS NULL
20755
+ ORDER BY created_at DESC`
20756
+ );
20675
20757
  return cursor.toArray().map((row) => ({
20676
20758
  id: row.id,
20677
20759
  username: row.username,
20678
- role: row.role,
20760
+ role: row.role === "user" ? "user" : "admin",
20761
+ platform_user_id: row.platform_user_id ?? null,
20762
+ email: row.email ?? null,
20763
+ display_name: row.display_name ?? null,
20764
+ avatar_url: row.avatar_url ?? null,
20765
+ source: row.source ?? "local",
20766
+ replica_active: row.replica_active ?? 1,
20679
20767
  created_at: row.created_at,
20680
20768
  updated_at: row.updated_at
20681
20769
  }));
@@ -20699,14 +20787,42 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20699
20787
  values.push(params.password_hash);
20700
20788
  }
20701
20789
  if (params.role !== void 0) {
20702
- updates.push("role = ?");
20790
+ updates.push("instance_role = ?");
20703
20791
  values.push(params.role);
20704
20792
  }
20793
+ if (params.platform_user_id !== void 0) {
20794
+ updates.push("platform_user_id = ?");
20795
+ values.push(params.platform_user_id);
20796
+ }
20797
+ if (params.email !== void 0) {
20798
+ updates.push("email = ?");
20799
+ values.push(params.email);
20800
+ }
20801
+ if (params.display_name !== void 0) {
20802
+ updates.push("display_name = ?");
20803
+ values.push(params.display_name);
20804
+ }
20805
+ if (params.avatar_url !== void 0) {
20806
+ updates.push("avatar_url = ?");
20807
+ values.push(params.avatar_url);
20808
+ }
20809
+ if (params.source !== void 0) {
20810
+ updates.push("source = ?");
20811
+ values.push(params.source);
20812
+ }
20813
+ if (params.replica_active !== void 0) {
20814
+ updates.push("replica_active = ?");
20815
+ values.push(params.replica_active);
20816
+ }
20705
20817
  if (updates.length === 0) {
20706
20818
  return existing;
20707
20819
  }
20708
20820
  updates.push("updated_at = ?");
20709
20821
  values.push(now);
20822
+ if (params.source === "standard_agents" || params.replica_active !== void 0) {
20823
+ updates.push("replica_updated_at = ?");
20824
+ values.push(now);
20825
+ }
20710
20826
  values.push(id);
20711
20827
  await this.ctx.storage.sql.exec(
20712
20828
  `UPDATE users SET ${updates.join(", ")} WHERE id = ?`,
@@ -20727,9 +20843,223 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20727
20843
  `DELETE FROM api_keys WHERE user_id = ?`,
20728
20844
  id
20729
20845
  );
20730
- await this.ctx.storage.sql.exec(`DELETE FROM users WHERE id = ?`, id);
20846
+ await this.ctx.storage.sql.exec(
20847
+ `UPDATE users SET deleted_at = ?, replica_active = 0 WHERE id = ?`,
20848
+ Math.floor(Date.now() / 1e3),
20849
+ id
20850
+ );
20731
20851
  return true;
20732
20852
  }
20853
+ /**
20854
+ * Upsert a platform-replicated identity and return the local user row.
20855
+ */
20856
+ async upsertPlatformReplicaUser(replica) {
20857
+ await this.ensureMigrated();
20858
+ const now = Math.floor(Date.now() / 1e3);
20859
+ const fallback = this.normalizeReplicaUsername(
20860
+ replica.email?.split("@")[0] ?? null,
20861
+ `sa-${replica.platform_user_id.slice(0, 12)}`
20862
+ );
20863
+ const username = this.normalizeReplicaUsername(
20864
+ replica.username ?? replica.display_name ?? replica.email ?? null,
20865
+ fallback
20866
+ );
20867
+ const existingCursor = await this.ctx.storage.sql.exec(
20868
+ `SELECT id FROM users WHERE platform_user_id = ? LIMIT 1`,
20869
+ replica.platform_user_id
20870
+ );
20871
+ const existing = existingCursor.toArray()[0];
20872
+ const availableUsername = await this.availableReplicaUsername(username, replica.platform_user_id);
20873
+ if (existing) {
20874
+ await this.ctx.storage.sql.exec(
20875
+ `UPDATE users SET
20876
+ username = ?,
20877
+ instance_role = ?,
20878
+ email = ?,
20879
+ display_name = ?,
20880
+ avatar_url = ?,
20881
+ source = 'standard_agents',
20882
+ replica_active = 1,
20883
+ replica_updated_at = ?,
20884
+ deleted_at = NULL,
20885
+ updated_at = ?
20886
+ WHERE id = ?`,
20887
+ availableUsername,
20888
+ replica.role,
20889
+ replica.email ?? null,
20890
+ replica.display_name ?? null,
20891
+ replica.avatar_url ?? null,
20892
+ now,
20893
+ now,
20894
+ existing.id
20895
+ );
20896
+ const updated = await this.getUserById(existing.id);
20897
+ if (!updated) {
20898
+ throw new Error("Failed to load replicated user after update");
20899
+ }
20900
+ return updated;
20901
+ }
20902
+ return this.createUser({
20903
+ username: availableUsername,
20904
+ password_hash: `platform-replica:${crypto.randomUUID()}`,
20905
+ role: replica.role,
20906
+ platform_user_id: replica.platform_user_id,
20907
+ email: replica.email ?? null,
20908
+ display_name: replica.display_name ?? null,
20909
+ avatar_url: replica.avatar_url ?? null,
20910
+ source: "standard_agents"
20911
+ });
20912
+ }
20913
+ /**
20914
+ * Apply a full platform read-replica snapshot for this instance.
20915
+ */
20916
+ async applyPlatformReplicaSnapshot(snapshot) {
20917
+ await this.ensureMigrated();
20918
+ const activeUserIds = /* @__PURE__ */ new Set();
20919
+ for (const replicaUser of snapshot.users) {
20920
+ const user = await this.upsertPlatformReplicaUser(replicaUser);
20921
+ activeUserIds.add(user.id);
20922
+ }
20923
+ const activeList = Array.from(activeUserIds);
20924
+ if (activeList.length > 0) {
20925
+ const placeholders = activeList.map(() => "?").join(", ");
20926
+ const inactive = await this.ctx.storage.sql.exec(
20927
+ `SELECT id FROM users
20928
+ WHERE source = 'standard_agents'
20929
+ AND replica_active != 0
20930
+ AND id NOT IN (${placeholders})`,
20931
+ ...activeList
20932
+ );
20933
+ for (const row of inactive.toArray()) {
20934
+ await this.ctx.storage.sql.exec(`DELETE FROM sessions WHERE user_id = ?`, row.id);
20935
+ await this.ctx.storage.sql.exec(
20936
+ `UPDATE users SET replica_active = 0, deleted_at = ?, replica_updated_at = ?, updated_at = ? WHERE id = ?`,
20937
+ Math.floor(Date.now() / 1e3),
20938
+ Math.floor(Date.now() / 1e3),
20939
+ Math.floor(Date.now() / 1e3),
20940
+ row.id
20941
+ );
20942
+ }
20943
+ } else {
20944
+ const inactive = await this.ctx.storage.sql.exec(
20945
+ `SELECT id FROM users WHERE source = 'standard_agents' AND replica_active != 0`
20946
+ );
20947
+ for (const row of inactive.toArray()) {
20948
+ await this.ctx.storage.sql.exec(`DELETE FROM sessions WHERE user_id = ?`, row.id);
20949
+ }
20950
+ const now2 = Math.floor(Date.now() / 1e3);
20951
+ await this.ctx.storage.sql.exec(
20952
+ `UPDATE users SET replica_active = 0, deleted_at = ?, replica_updated_at = ?, updated_at = ?
20953
+ WHERE source = 'standard_agents'`,
20954
+ now2,
20955
+ now2,
20956
+ now2
20957
+ );
20958
+ }
20959
+ const activePlatformKeyIds = /* @__PURE__ */ new Set();
20960
+ const keys = snapshot.api_keys ?? [];
20961
+ for (const key of keys) {
20962
+ const user = key.user_platform_id ? await this.getUserByPlatformUserId(key.user_platform_id) : await this.getFirstReplicaAdminUser();
20963
+ const keyUser = user ?? await this.getFirstReplicaAdminUser();
20964
+ if (!keyUser) continue;
20965
+ activePlatformKeyIds.add(key.id);
20966
+ await this.upsertPlatformReplicaApiKey(key, keyUser.id);
20967
+ }
20968
+ const now = Math.floor(Date.now() / 1e3);
20969
+ if (activePlatformKeyIds.size > 0) {
20970
+ const ids = Array.from(activePlatformKeyIds);
20971
+ const placeholders = ids.map(() => "?").join(", ");
20972
+ await this.ctx.storage.sql.exec(
20973
+ `UPDATE api_keys SET replica_active = 0, replica_updated_at = ?
20974
+ WHERE source = 'standard_agents' AND platform_key_id NOT IN (${placeholders})`,
20975
+ now,
20976
+ ...ids
20977
+ );
20978
+ } else {
20979
+ await this.ctx.storage.sql.exec(
20980
+ `UPDATE api_keys SET replica_active = 0, replica_updated_at = ?
20981
+ WHERE source = 'standard_agents'`,
20982
+ now
20983
+ );
20984
+ }
20985
+ return { users: activeUserIds.size, api_keys: activePlatformKeyIds.size };
20986
+ }
20987
+ async getUserByPlatformUserId(platformUserId) {
20988
+ await this.ensureMigrated();
20989
+ const cursor = await this.ctx.storage.sql.exec(
20990
+ `SELECT id FROM users
20991
+ WHERE platform_user_id = ? AND deleted_at IS NULL AND replica_active != 0
20992
+ LIMIT 1`,
20993
+ platformUserId
20994
+ );
20995
+ const row = cursor.toArray()[0];
20996
+ return row ? this.getUserById(row.id) : null;
20997
+ }
20998
+ async getFirstReplicaAdminUser() {
20999
+ await this.ensureMigrated();
21000
+ const cursor = await this.ctx.storage.sql.exec(
21001
+ `SELECT id FROM users
21002
+ WHERE source = 'standard_agents'
21003
+ AND instance_role = 'admin'
21004
+ AND deleted_at IS NULL
21005
+ AND replica_active != 0
21006
+ ORDER BY created_at ASC
21007
+ LIMIT 1`
21008
+ );
21009
+ const row = cursor.toArray()[0];
21010
+ return row ? this.getUserById(row.id) : null;
21011
+ }
21012
+ async upsertPlatformReplicaApiKey(key, userId) {
21013
+ await this.ensureMigrated();
21014
+ const now = Math.floor(Date.now() / 1e3);
21015
+ const existing = await this.ctx.storage.sql.exec(
21016
+ `SELECT id FROM api_keys WHERE platform_key_id = ? LIMIT 1`,
21017
+ key.id
21018
+ );
21019
+ const row = existing.toArray()[0];
21020
+ const name = key.name || `Standard Agents ${key.key_prefix}`;
21021
+ const lastFive = key.last_five || key.key_prefix.slice(-5);
21022
+ if (row) {
21023
+ await this.ctx.storage.sql.exec(
21024
+ `UPDATE api_keys SET
21025
+ name = ?,
21026
+ key_hash = ?,
21027
+ key_prefix = ?,
21028
+ last_five = ?,
21029
+ user_id = ?,
21030
+ scope = ?,
21031
+ source = 'standard_agents',
21032
+ replica_active = 1,
21033
+ replica_updated_at = ?
21034
+ WHERE id = ?`,
21035
+ name,
21036
+ key.key_hash,
21037
+ key.key_prefix,
21038
+ lastFive,
21039
+ userId,
21040
+ key.scope ?? null,
21041
+ now,
21042
+ row.id
21043
+ );
21044
+ return;
21045
+ }
21046
+ await this.ctx.storage.sql.exec(
21047
+ `INSERT INTO api_keys (
21048
+ id, name, key_hash, key_prefix, last_five, user_id, created_at,
21049
+ platform_key_id, scope, source, replica_active, replica_updated_at
21050
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'standard_agents', 1, ?)`,
21051
+ `platform:${key.id}`,
21052
+ name,
21053
+ key.key_hash,
21054
+ key.key_prefix,
21055
+ lastFive,
21056
+ userId,
21057
+ key.created_at ?? now,
21058
+ key.id,
21059
+ key.scope ?? null,
21060
+ now
21061
+ );
21062
+ }
20733
21063
  // ============================================================
20734
21064
  // Session Methods
20735
21065
  // ============================================================
@@ -20758,8 +21088,13 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20758
21088
  await this.ensureMigrated();
20759
21089
  const now = Math.floor(Date.now() / 1e3);
20760
21090
  const cursor = await this.ctx.storage.sql.exec(
20761
- `SELECT user_id, expires_at FROM sessions
20762
- WHERE token_hash = ? AND expires_at > ?`,
21091
+ `SELECT sessions.user_id, sessions.expires_at
21092
+ FROM sessions
21093
+ JOIN users ON users.id = sessions.user_id
21094
+ WHERE sessions.token_hash = ?
21095
+ AND sessions.expires_at > ?
21096
+ AND users.deleted_at IS NULL
21097
+ AND users.replica_active != 0`,
20763
21098
  tokenHash,
20764
21099
  now
20765
21100
  );
@@ -20816,14 +21151,23 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20816
21151
  */
20817
21152
  async validateApiKey(keyHash) {
20818
21153
  await this.ensureMigrated();
20819
- const cursor = await this.ctx.storage.sql.exec(`SELECT id, user_id FROM api_keys WHERE key_hash = ?`, keyHash);
21154
+ const cursor = await this.ctx.storage.sql.exec(
21155
+ `SELECT api_keys.id, api_keys.user_id
21156
+ FROM api_keys
21157
+ JOIN users ON users.id = api_keys.user_id
21158
+ WHERE api_keys.key_hash = ?
21159
+ AND api_keys.replica_active != 0
21160
+ AND users.deleted_at IS NULL
21161
+ AND users.replica_active != 0`,
21162
+ keyHash
21163
+ );
20820
21164
  const rows = cursor.toArray();
20821
21165
  if (rows.length === 0) {
20822
21166
  return null;
20823
21167
  }
20824
21168
  const now = Math.floor(Date.now() / 1e3);
20825
21169
  await this.ctx.storage.sql.exec(
20826
- `UPDATE api_keys SET last_used_at = ? WHERE key_hash = ?`,
21170
+ `UPDATE api_keys SET last_used_at = ? WHERE key_hash = ? AND replica_active != 0`,
20827
21171
  now,
20828
21172
  keyHash
20829
21173
  );
@@ -20835,8 +21179,10 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20835
21179
  async listApiKeys(userId) {
20836
21180
  await this.ensureMigrated();
20837
21181
  const cursor = await this.ctx.storage.sql.exec(
20838
- `SELECT id, name, key_prefix, last_five, created_at, last_used_at
20839
- FROM api_keys WHERE user_id = ? ORDER BY created_at DESC`,
21182
+ `SELECT id, name, key_prefix, last_five, source, created_at, last_used_at
21183
+ FROM api_keys
21184
+ WHERE user_id = ? AND replica_active != 0
21185
+ ORDER BY created_at DESC`,
20840
21186
  userId
20841
21187
  );
20842
21188
  return cursor.toArray();