@standardagents/builder 0.17.2 → 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/built-in-routes.js +527 -266
- package/dist/built-in-routes.js.map +1 -1
- package/dist/client/ApiKeysView.js +1 -1
- package/dist/client/CenteredContentView.js +1 -1
- package/dist/client/CompositionView.js +1 -1
- package/dist/client/ConfirmDialog.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/client/CopyButton.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/client/DataTable.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/client/JsonViewer.js +1 -1
- package/dist/client/LoginView.js +1 -1
- package/dist/client/MarketplaceView.js +1 -1
- package/dist/client/Modal.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/client/ModelModal.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/client/ModelsView.js +1 -1
- package/dist/client/PromptEditView.js +1 -1
- package/dist/client/PromptModal.js +1 -1
- package/dist/client/PromptsView.js +1 -1
- package/dist/client/ProvidersView.js +2 -2
- package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/client/ToolsView.js +1 -1
- package/dist/client/UsersView.js +1 -1
- package/dist/client/VariablesView.js +1 -1
- package/dist/client/assets/index.css +1 -1
- package/dist/client/index.js +4 -4
- package/dist/index.js +500 -52
- package/dist/index.js.map +1 -1
- package/dist/plugin.js +92 -7
- package/dist/plugin.js.map +1 -1
- package/dist/runtime.d.ts +72 -4
- package/dist/runtime.js +408 -45
- package/dist/runtime.js.map +1 -1
- package/package.json +4 -4
package/dist/runtime.js
CHANGED
|
@@ -12539,11 +12539,24 @@ async function hashToken(token) {
|
|
|
12539
12539
|
const hashArray = new Uint8Array(hashBuffer);
|
|
12540
12540
|
return Array.from(hashArray, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
12541
12541
|
}
|
|
12542
|
+
var SESSION_COOKIE_NAME = "agtuser_session";
|
|
12543
|
+
function readSessionCookie(request) {
|
|
12544
|
+
const header = request.headers.get("Cookie");
|
|
12545
|
+
if (!header) return null;
|
|
12546
|
+
for (const part of header.split(";")) {
|
|
12547
|
+
const eq = part.indexOf("=");
|
|
12548
|
+
if (eq === -1) continue;
|
|
12549
|
+
if (part.slice(0, eq).trim() === SESSION_COOKIE_NAME) {
|
|
12550
|
+
return decodeURIComponent(part.slice(eq + 1).trim()) || null;
|
|
12551
|
+
}
|
|
12552
|
+
}
|
|
12553
|
+
return null;
|
|
12554
|
+
}
|
|
12542
12555
|
function isValidUserToken(token) {
|
|
12543
12556
|
return token.startsWith("agtuser_") && token.length > 10;
|
|
12544
12557
|
}
|
|
12545
12558
|
function isValidApiKey(key) {
|
|
12546
|
-
return key.startsWith("agtbldr_") && key.length > 10;
|
|
12559
|
+
return key.startsWith("agtbldr_") && key.length > 10 || key.startsWith("sak_live_") && key.length > 10;
|
|
12547
12560
|
}
|
|
12548
12561
|
async function verifySignedToken(signedToken, encryptionKey) {
|
|
12549
12562
|
try {
|
|
@@ -12612,6 +12625,10 @@ function extractBearerToken(request) {
|
|
|
12612
12625
|
if (authHeader && authHeader.startsWith("Bearer ")) {
|
|
12613
12626
|
return authHeader.substring(7);
|
|
12614
12627
|
}
|
|
12628
|
+
const cookieToken = readSessionCookie(request);
|
|
12629
|
+
if (cookieToken) {
|
|
12630
|
+
return cookieToken;
|
|
12631
|
+
}
|
|
12615
12632
|
const isWebSocket = request.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
12616
12633
|
if (isWebSocket) {
|
|
12617
12634
|
try {
|
|
@@ -12663,7 +12680,11 @@ async function authenticate(request, env) {
|
|
|
12663
12680
|
user: {
|
|
12664
12681
|
id: user.id,
|
|
12665
12682
|
username: user.username,
|
|
12666
|
-
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
|
|
12667
12688
|
},
|
|
12668
12689
|
authType: "session"
|
|
12669
12690
|
};
|
|
@@ -12681,7 +12702,11 @@ async function authenticate(request, env) {
|
|
|
12681
12702
|
user: {
|
|
12682
12703
|
id: user.id,
|
|
12683
12704
|
username: user.username,
|
|
12684
|
-
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
|
|
12685
12710
|
},
|
|
12686
12711
|
authType: "api_key"
|
|
12687
12712
|
};
|
|
@@ -15471,9 +15496,9 @@ var DurableThread = class extends DurableObject {
|
|
|
15471
15496
|
* Each migration is run in order, starting from the current version + 1.
|
|
15472
15497
|
*/
|
|
15473
15498
|
async runMigrations(fromVersion) {
|
|
15474
|
-
for (const
|
|
15475
|
-
if (
|
|
15476
|
-
await
|
|
15499
|
+
for (const migration38 of migrations) {
|
|
15500
|
+
if (migration38.version > fromVersion) {
|
|
15501
|
+
await migration38.up(this.ctx.storage.sql);
|
|
15477
15502
|
}
|
|
15478
15503
|
}
|
|
15479
15504
|
}
|
|
@@ -18908,9 +18933,38 @@ var migration36 = {
|
|
|
18908
18933
|
}
|
|
18909
18934
|
};
|
|
18910
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
|
+
|
|
18911
18965
|
// src/durable-objects/agentbuilder-migrations/index.ts
|
|
18912
|
-
var migrations2 = [migration31, migration32, migration33, migration34, migration35, migration36];
|
|
18913
|
-
var LATEST_SCHEMA_VERSION2 =
|
|
18966
|
+
var migrations2 = [migration31, migration32, migration33, migration34, migration35, migration36, migration37];
|
|
18967
|
+
var LATEST_SCHEMA_VERSION2 = 7;
|
|
18914
18968
|
|
|
18915
18969
|
// src/utils/crypto.ts
|
|
18916
18970
|
var CryptoUtil = class {
|
|
@@ -19534,9 +19588,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
19534
19588
|
}
|
|
19535
19589
|
}
|
|
19536
19590
|
async runMigrations(fromVersion) {
|
|
19537
|
-
for (const
|
|
19538
|
-
if (
|
|
19539
|
-
await
|
|
19591
|
+
for (const migration38 of migrations2) {
|
|
19592
|
+
if (migration38.version > fromVersion) {
|
|
19593
|
+
await migration38.up(this.ctx.storage.sql);
|
|
19540
19594
|
}
|
|
19541
19595
|
}
|
|
19542
19596
|
}
|
|
@@ -20569,27 +20623,54 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20569
20623
|
// ============================================================
|
|
20570
20624
|
// User Authentication Methods
|
|
20571
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
|
+
}
|
|
20572
20659
|
/**
|
|
20573
20660
|
* Get a user by username.
|
|
20574
20661
|
*/
|
|
20575
20662
|
async getUserByUsername(username) {
|
|
20576
20663
|
await this.ensureMigrated();
|
|
20577
20664
|
const cursor = await this.ctx.storage.sql.exec(
|
|
20578
|
-
`SELECT id, username, password_hash,
|
|
20579
|
-
|
|
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`,
|
|
20580
20669
|
username
|
|
20581
20670
|
);
|
|
20582
20671
|
const rows = cursor.toArray();
|
|
20583
20672
|
if (rows.length === 0) return null;
|
|
20584
|
-
|
|
20585
|
-
return {
|
|
20586
|
-
id: row.id,
|
|
20587
|
-
username: row.username,
|
|
20588
|
-
password_hash: row.password_hash,
|
|
20589
|
-
role: row.role,
|
|
20590
|
-
created_at: row.created_at,
|
|
20591
|
-
updated_at: row.updated_at
|
|
20592
|
-
};
|
|
20673
|
+
return this.userFromRow(rows[0]);
|
|
20593
20674
|
}
|
|
20594
20675
|
/**
|
|
20595
20676
|
* Get a user by ID.
|
|
@@ -20597,21 +20678,15 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20597
20678
|
async getUserById(id) {
|
|
20598
20679
|
await this.ensureMigrated();
|
|
20599
20680
|
const cursor = await this.ctx.storage.sql.exec(
|
|
20600
|
-
`SELECT id, username, password_hash,
|
|
20601
|
-
|
|
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`,
|
|
20602
20685
|
id
|
|
20603
20686
|
);
|
|
20604
20687
|
const rows = cursor.toArray();
|
|
20605
20688
|
if (rows.length === 0) return null;
|
|
20606
|
-
|
|
20607
|
-
return {
|
|
20608
|
-
id: row.id,
|
|
20609
|
-
username: row.username,
|
|
20610
|
-
password_hash: row.password_hash,
|
|
20611
|
-
role: row.role,
|
|
20612
|
-
created_at: row.created_at,
|
|
20613
|
-
updated_at: row.updated_at
|
|
20614
|
-
};
|
|
20689
|
+
return this.userFromRow(rows[0]);
|
|
20615
20690
|
}
|
|
20616
20691
|
/**
|
|
20617
20692
|
* Create a new user.
|
|
@@ -20621,12 +20696,23 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20621
20696
|
const id = crypto.randomUUID();
|
|
20622
20697
|
const now = Math.floor(Date.now() / 1e3);
|
|
20623
20698
|
await this.ctx.storage.sql.exec(
|
|
20624
|
-
`INSERT INTO users (
|
|
20625
|
-
|
|
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
20626
20704
|
id,
|
|
20627
20705
|
params.username,
|
|
20628
20706
|
params.password_hash,
|
|
20707
|
+
"admin",
|
|
20629
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,
|
|
20630
20716
|
now,
|
|
20631
20717
|
now
|
|
20632
20718
|
);
|
|
@@ -20635,6 +20721,12 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20635
20721
|
username: params.username,
|
|
20636
20722
|
password_hash: params.password_hash,
|
|
20637
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,
|
|
20638
20730
|
created_at: now,
|
|
20639
20731
|
updated_at: now
|
|
20640
20732
|
};
|
|
@@ -20654,11 +20746,24 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20654
20746
|
*/
|
|
20655
20747
|
async listUsers() {
|
|
20656
20748
|
await this.ensureMigrated();
|
|
20657
|
-
const cursor = await this.ctx.storage.sql.exec(
|
|
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
|
+
);
|
|
20658
20757
|
return cursor.toArray().map((row) => ({
|
|
20659
20758
|
id: row.id,
|
|
20660
20759
|
username: row.username,
|
|
20661
|
-
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,
|
|
20662
20767
|
created_at: row.created_at,
|
|
20663
20768
|
updated_at: row.updated_at
|
|
20664
20769
|
}));
|
|
@@ -20682,14 +20787,42 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20682
20787
|
values.push(params.password_hash);
|
|
20683
20788
|
}
|
|
20684
20789
|
if (params.role !== void 0) {
|
|
20685
|
-
updates.push("
|
|
20790
|
+
updates.push("instance_role = ?");
|
|
20686
20791
|
values.push(params.role);
|
|
20687
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
|
+
}
|
|
20688
20817
|
if (updates.length === 0) {
|
|
20689
20818
|
return existing;
|
|
20690
20819
|
}
|
|
20691
20820
|
updates.push("updated_at = ?");
|
|
20692
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
|
+
}
|
|
20693
20826
|
values.push(id);
|
|
20694
20827
|
await this.ctx.storage.sql.exec(
|
|
20695
20828
|
`UPDATE users SET ${updates.join(", ")} WHERE id = ?`,
|
|
@@ -20710,9 +20843,223 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20710
20843
|
`DELETE FROM api_keys WHERE user_id = ?`,
|
|
20711
20844
|
id
|
|
20712
20845
|
);
|
|
20713
|
-
await this.ctx.storage.sql.exec(
|
|
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
|
+
);
|
|
20714
20851
|
return true;
|
|
20715
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
|
+
}
|
|
20716
21063
|
// ============================================================
|
|
20717
21064
|
// Session Methods
|
|
20718
21065
|
// ============================================================
|
|
@@ -20741,8 +21088,13 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20741
21088
|
await this.ensureMigrated();
|
|
20742
21089
|
const now = Math.floor(Date.now() / 1e3);
|
|
20743
21090
|
const cursor = await this.ctx.storage.sql.exec(
|
|
20744
|
-
`SELECT user_id, expires_at
|
|
20745
|
-
|
|
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`,
|
|
20746
21098
|
tokenHash,
|
|
20747
21099
|
now
|
|
20748
21100
|
);
|
|
@@ -20799,14 +21151,23 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20799
21151
|
*/
|
|
20800
21152
|
async validateApiKey(keyHash) {
|
|
20801
21153
|
await this.ensureMigrated();
|
|
20802
|
-
const cursor = await this.ctx.storage.sql.exec(
|
|
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
|
+
);
|
|
20803
21164
|
const rows = cursor.toArray();
|
|
20804
21165
|
if (rows.length === 0) {
|
|
20805
21166
|
return null;
|
|
20806
21167
|
}
|
|
20807
21168
|
const now = Math.floor(Date.now() / 1e3);
|
|
20808
21169
|
await this.ctx.storage.sql.exec(
|
|
20809
|
-
`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`,
|
|
20810
21171
|
now,
|
|
20811
21172
|
keyHash
|
|
20812
21173
|
);
|
|
@@ -20818,8 +21179,10 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
20818
21179
|
async listApiKeys(userId) {
|
|
20819
21180
|
await this.ensureMigrated();
|
|
20820
21181
|
const cursor = await this.ctx.storage.sql.exec(
|
|
20821
|
-
`SELECT id, name, key_prefix, last_five, created_at, last_used_at
|
|
20822
|
-
FROM api_keys
|
|
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`,
|
|
20823
21186
|
userId
|
|
20824
21187
|
);
|
|
20825
21188
|
return cursor.toArray();
|