weifuwu 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/dist/index.js CHANGED
@@ -2081,11 +2081,11 @@ function evaluateExpression(expr, ctx) {
2081
2081
  let bestIdx = -1;
2082
2082
  let bestOp = null;
2083
2083
  let bestFn = null;
2084
- for (const { op, fn } of operators) {
2085
- const idx = expr.indexOf(op);
2084
+ for (const { op: op2, fn } of operators) {
2085
+ const idx = expr.indexOf(op2);
2086
2086
  if (idx > 0 && (bestIdx === -1 || idx < bestIdx)) {
2087
2087
  bestIdx = idx;
2088
- bestOp = op;
2088
+ bestOp = op2;
2089
2089
  bestFn = fn;
2090
2090
  }
2091
2091
  }
@@ -2363,6 +2363,9 @@ function textArray(name) {
2363
2363
  function vector(name, dims) {
2364
2364
  return col(name, `vector(${dims})`);
2365
2365
  }
2366
+ function partitionBy(type, column) {
2367
+ return { type: type.toUpperCase(), column };
2368
+ }
2366
2369
  function toDDL(col2) {
2367
2370
  const parts = [`"${col2.name}"`, col2.sqlType];
2368
2371
  if (col2.isPrimaryKey) parts.push("PRIMARY KEY");
@@ -2376,6 +2379,44 @@ function toDDL(col2) {
2376
2379
  return parts.join(" ");
2377
2380
  }
2378
2381
 
2382
+ // postgres/schema/where.ts
2383
+ function op(col2, sqlOp, val) {
2384
+ return new SQL([`"${col2}" ${sqlOp} `, ""], [val]);
2385
+ }
2386
+ function eq(col2, val) {
2387
+ return op(col2, "=", val);
2388
+ }
2389
+ function gte(col2, val) {
2390
+ return op(col2, ">=", val);
2391
+ }
2392
+ function lt(col2, val) {
2393
+ return op(col2, "<", val);
2394
+ }
2395
+ function contains(col2, val) {
2396
+ return new SQL([`"${col2}" @> `, ""], [val]);
2397
+ }
2398
+ function combine(conditions, joiner) {
2399
+ if (conditions.length === 0) return new SQL([""], []);
2400
+ const strings = ["("];
2401
+ const values = [];
2402
+ for (let i = 0; i < conditions.length; i++) {
2403
+ if (i > 0) strings[strings.length - 1] += ` ${joiner} `;
2404
+ const s = conditions[i];
2405
+ for (let j = 0; j < s.strings.length; j++) {
2406
+ strings[strings.length - 1] += s.strings[j];
2407
+ if (j < s.values.length) {
2408
+ strings.push("");
2409
+ values.push(s.values[j]);
2410
+ }
2411
+ }
2412
+ }
2413
+ strings[strings.length - 1] += ")";
2414
+ return new SQL(strings, values);
2415
+ }
2416
+ function and(...conditions) {
2417
+ return combine(conditions, "AND");
2418
+ }
2419
+
2379
2420
  // postgres/schema/table.ts
2380
2421
  var Table = class {
2381
2422
  tableName;
@@ -2390,18 +2431,21 @@ var Table = class {
2390
2431
  auto: col2.isAutoGenerate
2391
2432
  }));
2392
2433
  }
2393
- async create(sql2) {
2434
+ async create(sql4, opts) {
2394
2435
  const colDDL = this.columns.map(toDDL);
2395
- const ddl = `CREATE TABLE IF NOT EXISTS "${this.tableName}" (
2436
+ let ddl = `CREATE TABLE IF NOT EXISTS "${this.tableName}" (
2396
2437
  ${colDDL.join(",\n ")}
2397
2438
  )`;
2398
- await sql2.unsafe(ddl);
2439
+ if (opts?.partitionBy) {
2440
+ ddl += ` PARTITION BY ${opts.partitionBy.type} ("${opts.partitionBy.column}")`;
2441
+ }
2442
+ await sql4.unsafe(ddl);
2399
2443
  }
2400
- async drop(sql2, opts) {
2444
+ async drop(sql4, opts) {
2401
2445
  const cascade = opts?.cascade ? " CASCADE" : "";
2402
- await sql2.unsafe(`DROP TABLE IF EXISTS "${this.tableName}"${cascade}`);
2446
+ await sql4.unsafe(`DROP TABLE IF EXISTS "${this.tableName}"${cascade}`);
2403
2447
  }
2404
- async createIndex(sql2, columns, opts) {
2448
+ async createIndex(sql4, columns, opts) {
2405
2449
  const cols = Array.isArray(columns) ? columns : [columns];
2406
2450
  const name = `"${this.tableName}_${cols.join("_")}${opts?.unique ? "_uidx" : "_idx"}"`;
2407
2451
  const unique = opts?.unique ? "UNIQUE" : "";
@@ -2409,13 +2453,13 @@ var Table = class {
2409
2453
  const colList = cols.map((c) => opts?.desc ? `"${c}" DESC` : `"${c}"`).join(", ");
2410
2454
  const operator = opts?.operator ? ` ${opts.operator}` : "";
2411
2455
  const ddl = `CREATE ${unique} INDEX IF NOT EXISTS ${name} ON "${this.tableName}" ${using} (${colList}${operator})`.replace(/\s+/g, " ");
2412
- await sql2.unsafe(ddl);
2456
+ await sql4.unsafe(ddl);
2413
2457
  }
2414
- async createUniqueIndex(sql2, columns) {
2415
- await this.createIndex(sql2, columns, { unique: true });
2458
+ async createUniqueIndex(sql4, columns) {
2459
+ await this.createIndex(sql4, columns, { unique: true });
2416
2460
  }
2417
2461
  // --- CRUD ---
2418
- async insert(sql2, data) {
2462
+ async insert(sql4, data) {
2419
2463
  const filtered = {};
2420
2464
  for (const { prop, db, auto } of this.colEntries) {
2421
2465
  if (auto) continue;
@@ -2423,30 +2467,70 @@ var Table = class {
2423
2467
  filtered[db] = data[prop];
2424
2468
  }
2425
2469
  }
2426
- const [row] = await sql2`
2427
- INSERT INTO ${sql2(this.tableName)} ${sql2(filtered)} RETURNING *
2470
+ const [row] = await sql4`
2471
+ INSERT INTO ${sql4(this.tableName)} ${sql4(filtered)} RETURNING *
2428
2472
  `;
2429
2473
  return row;
2430
2474
  }
2431
- async findById(sql2, id2) {
2432
- const [row] = await sql2`
2433
- SELECT * FROM ${sql2(this.tableName)}
2434
- WHERE ${sql2("id")} = ${id2} LIMIT 1
2475
+ async insertMany(sql4, data) {
2476
+ const filtered = [];
2477
+ for (const item of data) {
2478
+ const row = {};
2479
+ for (const { prop, db, auto } of this.colEntries) {
2480
+ if (auto) continue;
2481
+ if (prop in item) {
2482
+ row[db] = item[prop];
2483
+ }
2484
+ }
2485
+ filtered.push(row);
2486
+ }
2487
+ const rows = await sql4`
2488
+ INSERT INTO ${sql4(this.tableName)} ${sql4(filtered)} RETURNING *
2489
+ `;
2490
+ return rows;
2491
+ }
2492
+ async read(sql4, id2) {
2493
+ const [row] = await sql4`
2494
+ SELECT * FROM ${sql4(this.tableName)}
2495
+ WHERE ${sql4("id")} = ${id2} LIMIT 1
2435
2496
  `;
2436
2497
  return row ?? void 0;
2437
2498
  }
2438
- async find(sql2, where, opts) {
2499
+ async readMany(sql4, where, opts) {
2439
2500
  const conditions = [];
2440
2501
  const values = [];
2441
- for (const [prop, value] of Object.entries(where || {})) {
2442
- if (value === void 0) continue;
2443
- const entry = this.colEntries.find((e) => e.prop === prop);
2444
- const db = entry ? entry.db : prop;
2445
- conditions.push(`"${db}" = $${conditions.length + 1}`);
2446
- values.push(value);
2502
+ let w = where;
2503
+ if (Array.isArray(w)) {
2504
+ w = w.length > 0 ? and(...w) : void 0;
2505
+ }
2506
+ if (w instanceof SQL) {
2507
+ let fragment = "";
2508
+ for (let i = 0; i < w.strings.length; i++) {
2509
+ fragment += w.strings[i];
2510
+ if (i < w.values.length) {
2511
+ fragment += `$${values.length + 1}`;
2512
+ values.push(w.values[i]);
2513
+ }
2514
+ }
2515
+ conditions.push(fragment);
2516
+ } else {
2517
+ for (const [prop, value] of Object.entries(w || {})) {
2518
+ if (value === void 0) continue;
2519
+ const entry = this.colEntries.find((e) => e.prop === prop);
2520
+ const db = entry ? entry.db : prop;
2521
+ conditions.push(`"${db}" = $${conditions.length + 1}`);
2522
+ values.push(value);
2523
+ }
2524
+ }
2525
+ const whereClause = conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "";
2526
+ const [countRow] = await sql4.unsafe(`SELECT COUNT(*) AS _total FROM "${this.tableName}"${whereClause}`, values);
2527
+ const count = Number(countRow._total);
2528
+ if (conditions.length === 0 && !opts?.orderBy && !opts?.limit && !opts?.offset && !opts?.select) {
2529
+ const rows2 = await sql4`SELECT * FROM ${sql4(this.tableName)}`;
2530
+ return { count, data: rows2 };
2447
2531
  }
2448
- let query = `SELECT * FROM "${this.tableName}"`;
2449
- if (conditions.length > 0) query += ` WHERE ${conditions.join(" AND ")}`;
2532
+ const columns = opts?.select?.length ? opts.select.map((c) => `"${c}"`).join(", ") : "*";
2533
+ let query = `SELECT ${columns} FROM "${this.tableName}"${whereClause}`;
2450
2534
  if (opts?.orderBy) {
2451
2535
  const orders = Object.entries(opts.orderBy).map(([prop, dir]) => {
2452
2536
  const entry = this.colEntries.find((e) => e.prop === prop);
@@ -2456,14 +2540,29 @@ var Table = class {
2456
2540
  }
2457
2541
  if (opts?.limit) query += ` LIMIT ${opts.limit}`;
2458
2542
  if (opts?.offset) query += ` OFFSET ${opts.offset}`;
2459
- if (conditions.length > 0 || opts?.orderBy || opts?.limit !== void 0 || opts?.offset !== void 0) {
2460
- const rows2 = await sql2.unsafe(query, values);
2461
- return rows2;
2543
+ const rows = await sql4.unsafe(query, values);
2544
+ return { count, data: rows };
2545
+ }
2546
+ async update(sql4, id2, data) {
2547
+ const sets = [];
2548
+ const setValues = [];
2549
+ for (const { prop, db } of this.colEntries) {
2550
+ if (prop in data && data[prop] !== void 0) {
2551
+ const val = data[prop];
2552
+ if (val instanceof SQL) {
2553
+ sets.push(`"${db}" = ${val.toSQL()}`);
2554
+ } else {
2555
+ sets.push(`"${db}" = $${sets.length + 1}`);
2556
+ setValues.push(val);
2557
+ }
2558
+ }
2462
2559
  }
2463
- const rows = await sql2`SELECT * FROM ${sql2(this.tableName)}`;
2464
- return rows;
2560
+ if (sets.length === 0) return void 0;
2561
+ const query = `UPDATE "${this.tableName}" AS t SET ${sets.join(", ")} FROM (SELECT ctid FROM "${this.tableName}" WHERE id = $${setValues.length + 1} LIMIT 1) AS sub WHERE t.ctid = sub.ctid RETURNING t.*`;
2562
+ const rows = await sql4.unsafe(query, [...setValues, id2]);
2563
+ return rows[0] ?? void 0;
2465
2564
  }
2466
- async update(sql2, where, data) {
2565
+ async updateMany(sql4, where, data) {
2467
2566
  const sets = [];
2468
2567
  const setValues = [];
2469
2568
  for (const { prop, db } of this.colEntries) {
@@ -2486,12 +2585,14 @@ var Table = class {
2486
2585
  wConditions.push(`"${db}" = $${values.length + 1}`);
2487
2586
  values.push(value);
2488
2587
  }
2489
- if (sets.length === 0 || wConditions.length === 0) return void 0;
2490
- const query = `UPDATE "${this.tableName}" SET ${sets.join(", ")} WHERE ${wConditions.join(" AND ")} RETURNING *`;
2491
- const rows = await sql2.unsafe(query, values);
2492
- return rows[0] ?? void 0;
2588
+ if (sets.length === 0 || wConditions.length === 0) return 0;
2589
+ const rows = await sql4.unsafe(
2590
+ `UPDATE "${this.tableName}" SET ${sets.join(", ")} WHERE ${wConditions.join(" AND ")} RETURNING 1`,
2591
+ values
2592
+ );
2593
+ return rows.length;
2493
2594
  }
2494
- async delete(sql2, where) {
2595
+ async deleteMany(sql4, where) {
2495
2596
  const conditions = [];
2496
2597
  const values = [];
2497
2598
  for (const [prop, value] of Object.entries(where)) {
@@ -2501,18 +2602,26 @@ var Table = class {
2501
2602
  conditions.push(`"${db}" = $${conditions.length + 1}`);
2502
2603
  values.push(value);
2503
2604
  }
2504
- if (conditions.length === 0) return false;
2505
- const query = `DELETE FROM "${this.tableName}" WHERE ${conditions.join(" AND ")} RETURNING 1`;
2506
- const rows = await sql2.unsafe(query, values);
2507
- return rows.length > 0;
2605
+ if (conditions.length === 0) return 0;
2606
+ const rows = await sql4.unsafe(
2607
+ `DELETE FROM "${this.tableName}" WHERE ${conditions.join(" AND ")} RETURNING 1`,
2608
+ values
2609
+ );
2610
+ return rows.length;
2611
+ }
2612
+ async delete(sql4, id2) {
2613
+ const [row] = await sql4`
2614
+ DELETE FROM ${sql4(this.tableName)} WHERE ${sql4("id")} = ${id2} RETURNING *
2615
+ `;
2616
+ return row ?? void 0;
2508
2617
  }
2509
2618
  };
2510
2619
  var BoundTable = class {
2511
2620
  inner;
2512
2621
  sql;
2513
- constructor(sql2, tableName, builders) {
2622
+ constructor(sql4, tableName, builders) {
2514
2623
  this.inner = new Table(tableName, builders);
2515
- this.sql = sql2;
2624
+ this.sql = sql4;
2516
2625
  }
2517
2626
  async create() {
2518
2627
  await this.inner.create(this.sql);
@@ -2529,17 +2638,26 @@ var BoundTable = class {
2529
2638
  async insert(data) {
2530
2639
  return await this.inner.insert(this.sql, data);
2531
2640
  }
2532
- async findById(id2) {
2533
- return await this.inner.findById(this.sql, id2);
2641
+ async insertMany(data) {
2642
+ return await this.inner.insertMany(this.sql, data);
2643
+ }
2644
+ async read(id2) {
2645
+ return await this.inner.read(this.sql, id2);
2646
+ }
2647
+ async readMany(where, opts) {
2648
+ return await this.inner.readMany(this.sql, where, opts);
2649
+ }
2650
+ async update(id2, data) {
2651
+ return await this.inner.update(this.sql, id2, data);
2534
2652
  }
2535
- async find(where, opts) {
2536
- return await this.inner.find(this.sql, where, opts);
2653
+ async updateMany(where, data) {
2654
+ return await this.inner.updateMany(this.sql, where, data);
2537
2655
  }
2538
- async update(where, data) {
2539
- return await this.inner.update(this.sql, where, data);
2656
+ async delete(id2) {
2657
+ return await this.inner.delete(this.sql, id2);
2540
2658
  }
2541
- async delete(where) {
2542
- return await this.inner.delete(this.sql, where);
2659
+ async deleteMany(where) {
2660
+ return await this.inner.deleteMany(this.sql, where);
2543
2661
  }
2544
2662
  };
2545
2663
  function pgTable(tableName, builders) {
@@ -2555,7 +2673,7 @@ function postgres(opts) {
2555
2673
  "postgres: DATABASE_URL is not set. Pass a connection string or set the DATABASE_URL environment variable."
2556
2674
  );
2557
2675
  }
2558
- const sql2 = postgresFactory(connection, {
2676
+ const sql4 = postgresFactory(connection, {
2559
2677
  max: options.max,
2560
2678
  ssl: options.ssl,
2561
2679
  idle_timeout: options.idle_timeout,
@@ -2563,24 +2681,24 @@ function postgres(opts) {
2563
2681
  });
2564
2682
  if (options.signal) {
2565
2683
  options.signal.addEventListener("abort", () => {
2566
- sql2.end();
2684
+ sql4.end();
2567
2685
  }, { once: true });
2568
2686
  }
2569
2687
  const closeTimeout = options.closeTimeout ?? 5;
2570
2688
  const mw = ((req, ctx, next) => {
2571
- ctx.sql = sql2;
2689
+ ctx.sql = sql4;
2572
2690
  return next(req, ctx);
2573
2691
  });
2574
- mw.sql = sql2;
2692
+ mw.sql = sql4;
2575
2693
  mw.table = ((tableName, builders) => {
2576
- return new BoundTable(sql2, tableName, builders);
2694
+ return new BoundTable(sql4, tableName, builders);
2577
2695
  });
2578
2696
  mw.migrate = async () => {
2579
2697
  };
2580
2698
  mw.transaction = (async (fn) => {
2581
- return await sql2.begin(fn);
2699
+ return await sql4.begin(fn);
2582
2700
  });
2583
- mw.close = () => sql2.end({ timeout: closeTimeout });
2701
+ mw.close = () => sql4.end({ timeout: closeTimeout });
2584
2702
  return mw;
2585
2703
  }
2586
2704
 
@@ -2894,7 +3012,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
2894
3012
  }
2895
3013
  }
2896
3014
  await pg.sql`UPDATE "_oauth2_codes" SET "used" = TRUE WHERE "id" = ${stored.id}`;
2897
- const user2 = await users.findById(stored.user_id);
3015
+ const user2 = await users.read(stored.user_id);
2898
3016
  if (!user2) {
2899
3017
  return Response.json({ error: "invalid_grant" }, { status: 400 });
2900
3018
  }
@@ -2982,7 +3100,7 @@ function user(options) {
2982
3100
  if (oauth2Enabled) {
2983
3101
  oauth2 = createOAuth2Server({ pg, users, jwtSecret: secret, expiresIn });
2984
3102
  }
2985
- async function migrate6() {
3103
+ async function migrate7() {
2986
3104
  await migrate({ pg, usersTable: table, oauth2: oauth2Enabled });
2987
3105
  }
2988
3106
  function signToken(user2) {
@@ -2997,11 +3115,11 @@ function user(options) {
2997
3115
  return user2;
2998
3116
  }
2999
3117
  async function findByEmail(email) {
3000
- const rows = await users.find({ email });
3118
+ const { data: rows } = await users.readMany({ email });
3001
3119
  return rows[0];
3002
3120
  }
3003
3121
  async function findById(id2) {
3004
- return await users.findById(id2);
3122
+ return await users.read(id2);
3005
3123
  }
3006
3124
  async function register(data) {
3007
3125
  const { email, password, name } = RegisterSchema.parse(data);
@@ -3019,7 +3137,7 @@ function user(options) {
3019
3137
  }
3020
3138
  async function login(data) {
3021
3139
  const { email, password } = LoginSchema.parse(data);
3022
- const rows = await users.find({ email });
3140
+ const { data: rows } = await users.readMany({ email });
3023
3141
  const row = rows[0];
3024
3142
  if (!row) {
3025
3143
  const err = new Error("Invalid email or password");
@@ -3101,7 +3219,7 @@ function user(options) {
3101
3219
  const mod = {
3102
3220
  router,
3103
3221
  middleware,
3104
- migrate: migrate6,
3222
+ migrate: migrate7,
3105
3223
  register,
3106
3224
  login,
3107
3225
  verify,
@@ -3284,14 +3402,14 @@ function queue(opts) {
3284
3402
 
3285
3403
  // tenant/migrate.ts
3286
3404
  async function migrate2(opts) {
3287
- const { sql: sql2 } = opts;
3288
- await sql2.unsafe(`CREATE EXTENSION IF NOT EXISTS "vector"`);
3405
+ const { sql: sql4 } = opts;
3406
+ await sql4.unsafe(`CREATE EXTENSION IF NOT EXISTS "vector"`);
3289
3407
  const tenants = pgTable("_tenants", {
3290
3408
  id: text("id").primaryKey().default(sql`gen_random_uuid()`),
3291
3409
  name: text("name").notNull(),
3292
3410
  created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3293
3411
  });
3294
- await tenants.create(sql2);
3412
+ await tenants.create(sql4);
3295
3413
  const members = pgTable("_tenant_members", {
3296
3414
  id: serial("id").primaryKey(),
3297
3415
  tenant_id: text("tenant_id").notNull().references("_tenants", "id", "cascade"),
@@ -3299,9 +3417,9 @@ async function migrate2(opts) {
3299
3417
  role: text("role").notNull().default("member"),
3300
3418
  created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3301
3419
  });
3302
- await members.create(sql2);
3303
- await members.createIndex(sql2, "user_id");
3304
- await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_tenant_members_unique_idx" ON "_tenant_members" ("tenant_id", "user_id")`);
3420
+ await members.create(sql4);
3421
+ await members.createIndex(sql4, "user_id");
3422
+ await sql4.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_tenant_members_unique_idx" ON "_tenant_members" ("tenant_id", "user_id")`);
3305
3423
  const tables = pgTable("_user_tables", {
3306
3424
  id: serial("id").primaryKey(),
3307
3425
  tenant_id: text("tenant_id").notNull().references("_tenants", "id", "cascade"),
@@ -3310,9 +3428,9 @@ async function migrate2(opts) {
3310
3428
  fields: jsonb("fields").notNull().default(sql`'[]'::jsonb`),
3311
3429
  created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3312
3430
  });
3313
- await tables.create(sql2);
3314
- await tables.createIndex(sql2, "tenant_id");
3315
- await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_user_tables_unique_idx" ON "_user_tables" ("tenant_id", "slug")`);
3431
+ await tables.create(sql4);
3432
+ await tables.createIndex(sql4, "tenant_id");
3433
+ await sql4.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_user_tables_unique_idx" ON "_user_tables" ("tenant_id", "slug")`);
3316
3434
  }
3317
3435
 
3318
3436
  // tenant/rest.ts
@@ -3506,8 +3624,8 @@ function zodType(field) {
3506
3624
  }
3507
3625
  return t;
3508
3626
  }
3509
- async function getUserTable(sql2, tenantId, slug) {
3510
- const [row] = await sql2`
3627
+ async function getUserTable(sql4, tenantId, slug) {
3628
+ const [row] = await sql4`
3511
3629
  SELECT * FROM "_user_tables"
3512
3630
  WHERE tenant_id = ${tenantId} AND slug = ${slug}
3513
3631
  LIMIT 1
@@ -3518,24 +3636,24 @@ function requireAdmin(ctx) {
3518
3636
  if (ctx.tenant?.role !== "admin") return Response.json({ error: "Forbidden" }, { status: 403 });
3519
3637
  return null;
3520
3638
  }
3521
- function buildRouter(sql2, usersTable) {
3639
+ function buildRouter(sql4, usersTable) {
3522
3640
  const r = new Router();
3523
3641
  r.post("/sys/tenants", async (req, ctx) => {
3524
3642
  const { name } = await req.json();
3525
3643
  if (!name || typeof name !== "string") {
3526
3644
  return Response.json({ error: "name is required" }, { status: 400 });
3527
3645
  }
3528
- const [tenant2] = await sql2`
3646
+ const [tenant2] = await sql4`
3529
3647
  INSERT INTO "_tenants" ("name") VALUES (${name}) RETURNING *
3530
3648
  `;
3531
- await sql2`
3649
+ await sql4`
3532
3650
  INSERT INTO "_tenant_members" ("tenant_id", "user_id", "role")
3533
3651
  VALUES (${tenant2.id}, ${ctx.user.id}, 'admin')
3534
3652
  `;
3535
3653
  return Response.json(tenant2, { status: 201 });
3536
3654
  });
3537
3655
  r.get("/sys/tenants", async (_req, ctx) => {
3538
- const rows = await sql2`
3656
+ const rows = await sql4`
3539
3657
  SELECT t.*, tm.role FROM "_tenants" t
3540
3658
  JOIN "_tenant_members" tm ON tm.tenant_id = t.id
3541
3659
  WHERE tm.user_id = ${ctx.user.id}
@@ -3546,16 +3664,16 @@ function buildRouter(sql2, usersTable) {
3546
3664
  const err = requireAdmin(ctx);
3547
3665
  if (err) return err;
3548
3666
  const { email, role = "member" } = await req.json();
3549
- const [user2] = await sql2`
3550
- SELECT id FROM ${sql2(usersTable)} WHERE "email" = ${email} LIMIT 1
3667
+ const [user2] = await sql4`
3668
+ SELECT id FROM ${sql4(usersTable)} WHERE "email" = ${email} LIMIT 1
3551
3669
  `;
3552
3670
  if (!user2) return Response.json({ error: "User not found" }, { status: 404 });
3553
- const [existing] = await sql2`
3671
+ const [existing] = await sql4`
3554
3672
  SELECT id FROM "_tenant_members"
3555
3673
  WHERE tenant_id = ${ctx.tenant.id} AND user_id = ${user2.id} LIMIT 1
3556
3674
  `;
3557
3675
  if (existing) return Response.json({ error: "Already a member" }, { status: 409 });
3558
- await sql2`
3676
+ await sql4`
3559
3677
  INSERT INTO "_tenant_members" ("tenant_id", "user_id", "role")
3560
3678
  VALUES (${ctx.tenant.id}, ${user2.id}, ${role})
3561
3679
  `;
@@ -3565,7 +3683,7 @@ function buildRouter(sql2, usersTable) {
3565
3683
  const err = requireAdmin(ctx);
3566
3684
  if (err) return err;
3567
3685
  const userId = parseInt(ctx.params.userId, 10);
3568
- await sql2`
3686
+ await sql4`
3569
3687
  DELETE FROM "_tenant_members"
3570
3688
  WHERE tenant_id = ${ctx.tenant.id} AND user_id = ${userId}
3571
3689
  `;
@@ -3581,17 +3699,17 @@ function buildRouter(sql2, usersTable) {
3581
3699
  if (fieldErrs.length) {
3582
3700
  return Response.json({ error: "Invalid fields", details: fieldErrs }, { status: 400 });
3583
3701
  }
3584
- const [existing] = await sql2`
3702
+ const [existing] = await sql4`
3585
3703
  SELECT id FROM "_user_tables"
3586
3704
  WHERE tenant_id = ${ctx.tenant.id} AND slug = ${body.slug} LIMIT 1
3587
3705
  `;
3588
3706
  if (existing) return Response.json({ error: "Table already exists" }, { status: 409 });
3589
3707
  const createSQL = createTableSQL(ctx.tenant.id, body.slug, body.fields);
3590
- await sql2.unsafe(createSQL);
3708
+ await sql4.unsafe(createSQL);
3591
3709
  for (const stmt of createIndexesSQL(ctx.tenant.id, body.slug, body.fields)) {
3592
- await sql2.unsafe(stmt);
3710
+ await sql4.unsafe(stmt);
3593
3711
  }
3594
- const [row] = await sql2`
3712
+ const [row] = await sql4`
3595
3713
  INSERT INTO "_user_tables" ("tenant_id", "slug", "label", "fields")
3596
3714
  VALUES (${ctx.tenant.id}, ${body.slug}, ${body.label || ""}, ${body.fields})
3597
3715
  RETURNING *
@@ -3599,7 +3717,7 @@ function buildRouter(sql2, usersTable) {
3599
3717
  return Response.json(row, { status: 201 });
3600
3718
  });
3601
3719
  r.get("/sys/tables", async (_req, ctx) => {
3602
- const rows = await sql2`
3720
+ const rows = await sql4`
3603
3721
  SELECT * FROM "_user_tables"
3604
3722
  WHERE tenant_id = ${ctx.tenant.id}
3605
3723
  ORDER BY created_at DESC
@@ -3607,7 +3725,7 @@ function buildRouter(sql2, usersTable) {
3607
3725
  return Response.json(rows);
3608
3726
  });
3609
3727
  r.get("/sys/tables/:slug", async (_req, ctx) => {
3610
- const table = await getUserTable(sql2, ctx.tenant.id, ctx.params.slug);
3728
+ const table = await getUserTable(sql4, ctx.tenant.id, ctx.params.slug);
3611
3729
  if (!table) return Response.json({ error: "Table not found" }, { status: 404 });
3612
3730
  return Response.json(table);
3613
3731
  });
@@ -3618,15 +3736,15 @@ function buildRouter(sql2, usersTable) {
3618
3736
  if (!body.fields || !Array.isArray(body.fields)) {
3619
3737
  return Response.json({ error: "fields array required" }, { status: 400 });
3620
3738
  }
3621
- const table = await getUserTable(sql2, ctx.tenant.id, ctx.params.slug);
3739
+ const table = await getUserTable(sql4, ctx.tenant.id, ctx.params.slug);
3622
3740
  if (!table) return Response.json({ error: "Table not found" }, { status: 404 });
3623
3741
  const existingNames = new Set(table.fields.map((f) => f.name));
3624
3742
  const newFields = body.fields.filter((f) => !existingNames.has(f.name));
3625
3743
  for (const f of newFields) {
3626
- await sql2.unsafe(addColumnSQL(ctx.tenant.id, ctx.params.slug, f));
3744
+ await sql4.unsafe(addColumnSQL(ctx.tenant.id, ctx.params.slug, f));
3627
3745
  }
3628
3746
  const merged = [...table.fields, ...newFields];
3629
- await sql2`
3747
+ await sql4`
3630
3748
  UPDATE "_user_tables"
3631
3749
  SET fields = ${merged}
3632
3750
  WHERE id = ${table.id}
@@ -3636,15 +3754,15 @@ function buildRouter(sql2, usersTable) {
3636
3754
  r.delete("/sys/tables/:slug", async (_req, ctx) => {
3637
3755
  const err = requireAdmin(ctx);
3638
3756
  if (err) return err;
3639
- await sql2.unsafe(dropTableSQL(ctx.tenant.id, ctx.params.slug));
3640
- await sql2`
3757
+ await sql4.unsafe(dropTableSQL(ctx.tenant.id, ctx.params.slug));
3758
+ await sql4`
3641
3759
  DELETE FROM "_user_tables"
3642
3760
  WHERE tenant_id = ${ctx.tenant.id} AND slug = ${ctx.params.slug}
3643
3761
  `;
3644
3762
  return Response.json({ ok: true });
3645
3763
  });
3646
3764
  async function resolveTable(ctx) {
3647
- return getUserTable(sql2, ctx.tenant.id, ctx.params["_slug"]);
3765
+ return getUserTable(sql4, ctx.tenant.id, ctx.params["_slug"]);
3648
3766
  }
3649
3767
  function internalName(ctx) {
3650
3768
  return internalTableName(ctx.tenant.id, ctx.params["_slug"]);
@@ -3667,11 +3785,11 @@ function buildRouter(sql2, usersTable) {
3667
3785
  try {
3668
3786
  const parsed = JSON.parse(searchVector);
3669
3787
  const [rows2, countResult2] = await Promise.all([
3670
- sql2.unsafe(
3788
+ sql4.unsafe(
3671
3789
  `SELECT *, "${searchField}" ${operator} $1::vector AS "_distance" FROM "${name2}" WHERE tenant_id = $2 ORDER BY "_distance" LIMIT $3 OFFSET $4`,
3672
3790
  [parsed, ctx.tenant.id, limit, offset]
3673
3791
  ),
3674
- sql2.unsafe(
3792
+ sql4.unsafe(
3675
3793
  `SELECT count(*) as count FROM "${name2}" WHERE tenant_id = $1`,
3676
3794
  [ctx.tenant.id]
3677
3795
  )
@@ -3683,11 +3801,11 @@ function buildRouter(sql2, usersTable) {
3683
3801
  }
3684
3802
  const name = internalName(ctx);
3685
3803
  const [rows, countResult] = await Promise.all([
3686
- sql2.unsafe(
3804
+ sql4.unsafe(
3687
3805
  `SELECT * FROM "${name}" WHERE tenant_id = $1 ORDER BY "${orderCol}" ${orderDir} LIMIT $2 OFFSET $3`,
3688
3806
  [ctx.tenant.id, limit, offset]
3689
3807
  ),
3690
- sql2.unsafe(
3808
+ sql4.unsafe(
3691
3809
  `SELECT count(*) as count FROM "${name}" WHERE tenant_id = $1`,
3692
3810
  [ctx.tenant.id]
3693
3811
  )
@@ -3707,15 +3825,15 @@ function buildRouter(sql2, usersTable) {
3707
3825
  parsed.tenant_id = ctx.tenant.id;
3708
3826
  delete parsed.id;
3709
3827
  const name = internalName(ctx);
3710
- const [row] = await sql2`INSERT INTO ${sql2(name)} ${sql2(parsed)} RETURNING *`;
3828
+ const [row] = await sql4`INSERT INTO ${sql4(name)} ${sql4(parsed)} RETURNING *`;
3711
3829
  return Response.json(row, { status: 201 });
3712
3830
  });
3713
3831
  r.get("/:_slug/:id", async (_req, ctx) => {
3714
3832
  const table = await resolveTable(ctx);
3715
3833
  if (!table) return Response.json({ error: "Table not found" }, { status: 404 });
3716
3834
  const name = internalName(ctx);
3717
- const [row] = await sql2`
3718
- SELECT * FROM ${sql2(name)}
3835
+ const [row] = await sql4`
3836
+ SELECT * FROM ${sql4(name)}
3719
3837
  WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
3720
3838
  LIMIT 1
3721
3839
  `;
@@ -3736,16 +3854,16 @@ function buildRouter(sql2, usersTable) {
3736
3854
  delete parsed.tenant_id;
3737
3855
  if (Object.keys(parsed).length === 0) {
3738
3856
  const name2 = internalName(ctx);
3739
- const [row2] = await sql2`
3740
- SELECT * FROM ${sql2(name2)}
3857
+ const [row2] = await sql4`
3858
+ SELECT * FROM ${sql4(name2)}
3741
3859
  WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
3742
3860
  LIMIT 1
3743
3861
  `;
3744
3862
  return Response.json(row2 ?? { error: "Not found" }, { status: row2 ? 200 : 404 });
3745
3863
  }
3746
3864
  const name = internalName(ctx);
3747
- const [row] = await sql2`
3748
- UPDATE ${sql2(name)} SET ${sql2(parsed)}
3865
+ const [row] = await sql4`
3866
+ UPDATE ${sql4(name)} SET ${sql4(parsed)}
3749
3867
  WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
3750
3868
  RETURNING *
3751
3869
  `;
@@ -3754,8 +3872,8 @@ function buildRouter(sql2, usersTable) {
3754
3872
  });
3755
3873
  r.delete("/:_slug/:id", async (_req, ctx) => {
3756
3874
  const name = internalName(ctx);
3757
- const result = await sql2`
3758
- DELETE FROM ${sql2(name)}
3875
+ const result = await sql4`
3876
+ DELETE FROM ${sql4(name)}
3759
3877
  WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
3760
3878
  RETURNING 1
3761
3879
  `;
@@ -3766,11 +3884,11 @@ function buildRouter(sql2, usersTable) {
3766
3884
  });
3767
3885
  async function handleNested(req, ctx, method) {
3768
3886
  const [parentTable, nestedSlug] = await Promise.all([
3769
- getUserTable(sql2, ctx.tenant.id, ctx.params["_slug"]),
3887
+ getUserTable(sql4, ctx.tenant.id, ctx.params["_slug"]),
3770
3888
  ctx.params["_nested"]
3771
3889
  ]);
3772
3890
  if (!parentTable) return Response.json({ error: "Parent table not found" }, { status: 404 });
3773
- const childTable = await getUserTable(sql2, ctx.tenant.id, nestedSlug);
3891
+ const childTable = await getUserTable(sql4, ctx.tenant.id, nestedSlug);
3774
3892
  if (!childTable) return Response.json({ error: "Nested table not found" }, { status: 404 });
3775
3893
  const relField = findRelation(childTable.fields, ctx.params["_slug"]);
3776
3894
  if (!relField) {
@@ -3780,18 +3898,18 @@ function buildRouter(sql2, usersTable) {
3780
3898
  if (relFields.length === 2) {
3781
3899
  const otherRel = relFields.find((f) => f.name !== relField.name);
3782
3900
  const targetSlug = otherRel.relation.table;
3783
- const targetTable = await getUserTable(sql2, ctx.tenant.id, targetSlug);
3901
+ const targetTable = await getUserTable(sql4, ctx.tenant.id, targetSlug);
3784
3902
  if (!targetTable) return Response.json({ error: "Target table not found" }, { status: 404 });
3785
3903
  const childName2 = internalTableName(ctx.tenant.id, nestedSlug);
3786
3904
  const targetName = internalTableName(ctx.tenant.id, targetSlug);
3787
3905
  const parentId2 = parseInt(ctx.params.id, 10);
3788
3906
  if (method === "GET") {
3789
3907
  const [rows, countResult] = await Promise.all([
3790
- sql2.unsafe(
3908
+ sql4.unsafe(
3791
3909
  `SELECT t.* FROM "${targetName}" t JOIN "${childName2}" j ON j."${otherRel.name}" = t.id WHERE j."${relField.name}" = $1 AND t.tenant_id = $2 ORDER BY t.id DESC`,
3792
3910
  [parentId2, ctx.tenant.id]
3793
3911
  ),
3794
- sql2.unsafe(
3912
+ sql4.unsafe(
3795
3913
  `SELECT count(*) as count FROM "${targetName}" t JOIN "${childName2}" j ON j."${otherRel.name}" = t.id WHERE j."${relField.name}" = $1 AND t.tenant_id = $2`,
3796
3914
  [parentId2, ctx.tenant.id]
3797
3915
  )
@@ -3804,11 +3922,11 @@ function buildRouter(sql2, usersTable) {
3804
3922
  const parentId = parseInt(ctx.params.id, 10);
3805
3923
  if (method === "GET") {
3806
3924
  const [rows, countResult] = await Promise.all([
3807
- sql2.unsafe(
3925
+ sql4.unsafe(
3808
3926
  `SELECT * FROM "${childName}" WHERE "${relField.name}" = $1 AND tenant_id = $2 ORDER BY id DESC`,
3809
3927
  [parentId, ctx.tenant.id]
3810
3928
  ),
3811
- sql2.unsafe(
3929
+ sql4.unsafe(
3812
3930
  `SELECT count(*) as count FROM "${childName}" WHERE "${relField.name}" = $1 AND tenant_id = $2`,
3813
3931
  [parentId, ctx.tenant.id]
3814
3932
  )
@@ -3825,7 +3943,7 @@ function buildRouter(sql2, usersTable) {
3825
3943
  parsed.tenant_id = ctx.tenant.id;
3826
3944
  parsed[relField.name] = parentId;
3827
3945
  delete parsed.id;
3828
- const [row] = await sql2`INSERT INTO ${sql2(childName)} ${sql2(parsed)} RETURNING *`;
3946
+ const [row] = await sql4`INSERT INTO ${sql4(childName)} ${sql4(parsed)} RETURNING *`;
3829
3947
  return Response.json(row, { status: 201 });
3830
3948
  }
3831
3949
  r.get("/:_slug/:id/:_nested", async (req, ctx) => handleNested(req, ctx, "GET"));
@@ -4067,15 +4185,15 @@ function buildMutationFields(tables, ctx) {
4067
4185
  }
4068
4186
  return fields;
4069
4187
  }
4070
- function buildGraphQLHandler(sql2) {
4188
+ function buildGraphQLHandler(sql4) {
4071
4189
  const r = new Router();
4072
4190
  r.post("/", async (req, ctx) => {
4073
- const tables = await sql2`
4191
+ const tables = await sql4`
4074
4192
  SELECT * FROM "_user_tables"
4075
4193
  WHERE tenant_id = ${ctx.tenant.id}
4076
4194
  ORDER BY created_at ASC
4077
4195
  `;
4078
- const buildCtx = { sql: sql2, tenantId: ctx.tenant.id, tables };
4196
+ const buildCtx = { sql: sql4, tenantId: ctx.tenant.id, tables };
4079
4197
  const schema = new GraphQLSchema({
4080
4198
  query: new GraphQLObjectType({
4081
4199
  name: "Query",
@@ -4110,12 +4228,12 @@ function buildGraphQLHandler(sql2) {
4110
4228
  });
4111
4229
  });
4112
4230
  async function handleGET(req, _ctx) {
4113
- const tables = await sql2`
4231
+ const tables = await sql4`
4114
4232
  SELECT * FROM "_user_tables"
4115
4233
  WHERE tenant_id = ${_ctx.tenant.id}
4116
4234
  ORDER BY created_at ASC
4117
4235
  `;
4118
- const buildCtx = { sql: sql2, tenantId: _ctx.tenant.id, tables };
4236
+ const buildCtx = { sql: sql4, tenantId: _ctx.tenant.id, tables };
4119
4237
  const schema = new GraphQLSchema({
4120
4238
  query: new GraphQLObjectType({
4121
4239
  name: "Query",
@@ -4145,18 +4263,18 @@ function buildGraphQLHandler(sql2) {
4145
4263
  // tenant/client.ts
4146
4264
  function tenant(options) {
4147
4265
  const pg = options.pg;
4148
- const sql2 = pg.sql;
4266
+ const sql4 = pg.sql;
4149
4267
  const usersTable = options.usersTable;
4150
4268
  const base = new PgModule(pg);
4151
4269
  return {
4152
- migrate: () => migrate2({ sql: sql2, usersTable }),
4270
+ migrate: () => migrate2({ sql: sql4, usersTable }),
4153
4271
  middleware() {
4154
4272
  return async (req, ctx, next) => {
4155
4273
  const user2 = ctx.user;
4156
4274
  if (!user2) {
4157
4275
  return new Response("Unauthorized", { status: 401 });
4158
4276
  }
4159
- const members = await sql2`
4277
+ const members = await sql4`
4160
4278
  SELECT tm.role, t.id, t.name
4161
4279
  FROM "_tenant_members" tm
4162
4280
  JOIN "_tenants" t ON t.id = tm.tenant_id
@@ -4185,8 +4303,8 @@ function tenant(options) {
4185
4303
  return next(req, ctx);
4186
4304
  };
4187
4305
  },
4188
- router: () => buildRouter(sql2, usersTable),
4189
- graphql: () => buildGraphQLHandler(sql2),
4306
+ router: () => buildRouter(sql4, usersTable),
4307
+ graphql: () => buildGraphQLHandler(sql4),
4190
4308
  close: () => base.close()
4191
4309
  };
4192
4310
  }
@@ -4196,7 +4314,7 @@ import { createOpenAI } from "@ai-sdk/openai";
4196
4314
 
4197
4315
  // agent/migrate.ts
4198
4316
  async function migrate3(opts) {
4199
- const { sql: sql2, embeddingDimension } = opts;
4317
+ const { sql: sql4, embeddingDimension } = opts;
4200
4318
  const agents = pgTable("_agents", {
4201
4319
  id: serial("id").primaryKey(),
4202
4320
  tenant_id: text("tenant_id"),
@@ -4210,8 +4328,8 @@ async function migrate3(opts) {
4210
4328
  created_at: timestamptz("created_at").notNull().default(sql`NOW()`),
4211
4329
  updated_at: timestamptz("updated_at").notNull().default(sql`NOW()`)
4212
4330
  });
4213
- await agents.create(sql2);
4214
- await agents.createIndex(sql2, "tenant_id");
4331
+ await agents.create(sql4);
4332
+ await agents.createIndex(sql4, "tenant_id");
4215
4333
  const docs = pgTable("_knowledge_documents", {
4216
4334
  id: serial("id").primaryKey(),
4217
4335
  agent_id: integer("agent_id").notNull().references("_agents", "id", "cascade"),
@@ -4221,22 +4339,22 @@ async function migrate3(opts) {
4221
4339
  metadata: jsonb("metadata").notNull().default(sql`'{}'::jsonb`),
4222
4340
  created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4223
4341
  });
4224
- await docs.create(sql2);
4225
- await docs.createIndex(sql2, "agent_id");
4342
+ await docs.create(sql4);
4343
+ await docs.createIndex(sql4, "agent_id");
4226
4344
  }
4227
4345
 
4228
4346
  // agent/rest.ts
4229
- async function getAgent(sql2, id2) {
4230
- const [row] = await sql2`SELECT * FROM "_agents" WHERE id = ${id2} LIMIT 1`;
4231
- return row ?? null;
4232
- }
4233
4347
  function buildRouter2(deps) {
4234
- const { sql: sql2, runner } = deps;
4348
+ const { sql: sql4, agents: agentsTable, runner } = deps;
4349
+ async function getAgent(id2) {
4350
+ const row = await agentsTable.read(id2);
4351
+ return row ?? null;
4352
+ }
4235
4353
  const r = new Router();
4236
4354
  r.post("/agents", async (req) => {
4237
4355
  const body = await req.json();
4238
4356
  if (!body.name) return Response.json({ error: "name is required" }, { status: 400 });
4239
- const [row] = await sql2`
4357
+ const [row] = await sql4`
4240
4358
  INSERT INTO "_agents" ("name", "description", "type", "model", "system_prompt", "owner_id")
4241
4359
  VALUES (${body.name}, ${body.description || ""}, ${body.type || "chat"}, ${body.model || ""}, ${body.system_prompt || ""}, ${body.owner_id || 1})
4242
4360
  RETURNING *
@@ -4244,17 +4362,17 @@ function buildRouter2(deps) {
4244
4362
  return Response.json(row, { status: 201 });
4245
4363
  });
4246
4364
  r.get("/agents", async () => {
4247
- const rows = await sql2`SELECT * FROM "_agents" ORDER BY created_at DESC`;
4365
+ const { data: rows } = await agentsTable.readMany(void 0, { orderBy: { created_at: "desc" } });
4248
4366
  return Response.json(rows);
4249
4367
  });
4250
4368
  r.get("/agents/:id", async (_req, ctx) => {
4251
- const agent2 = await getAgent(sql2, parseInt(ctx.params.id, 10));
4369
+ const agent2 = await getAgent(parseInt(ctx.params.id, 10));
4252
4370
  if (!agent2) return Response.json({ error: "Agent not found" }, { status: 404 });
4253
4371
  return Response.json(agent2);
4254
4372
  });
4255
4373
  r.patch("/agents/:id", async (req, ctx) => {
4256
4374
  const id2 = parseInt(ctx.params.id, 10);
4257
- const agent2 = await getAgent(sql2, id2);
4375
+ const agent2 = await getAgent(id2);
4258
4376
  if (!agent2) return Response.json({ error: "Agent not found" }, { status: 404 });
4259
4377
  const body = await req.json();
4260
4378
  const fields = [];
@@ -4269,7 +4387,7 @@ function buildRouter2(deps) {
4269
4387
  if (fields.length === 0) return Response.json({ error: "No fields to update" }, { status: 400 });
4270
4388
  values.push(id2);
4271
4389
  fields.push(`"updated_at" = NOW()`);
4272
- const [row] = await sql2.unsafe(
4390
+ const [row] = await sql4.unsafe(
4273
4391
  `UPDATE "_agents" SET ${fields.join(", ")} WHERE id = $${idx} RETURNING *`,
4274
4392
  values
4275
4393
  );
@@ -4277,7 +4395,7 @@ function buildRouter2(deps) {
4277
4395
  });
4278
4396
  r.delete("/agents/:id", async (_req, ctx) => {
4279
4397
  const id2 = parseInt(ctx.params.id, 10);
4280
- const [row] = await sql2`DELETE FROM "_agents" WHERE id = ${id2} RETURNING 1`;
4398
+ const [row] = await sql4`DELETE FROM "_agents" WHERE id = ${id2} RETURNING 1`;
4281
4399
  if (!row) return Response.json({ error: "Agent not found" }, { status: 404 });
4282
4400
  return Response.json({ ok: true });
4283
4401
  });
@@ -4301,7 +4419,7 @@ function buildRouter2(deps) {
4301
4419
  });
4302
4420
  r.post("/agents/:id/knowledge", async (req, ctx) => {
4303
4421
  const agentId = parseInt(ctx.params.id, 10);
4304
- const agent2 = await getAgent(sql2, agentId);
4422
+ const agent2 = await getAgent(agentId);
4305
4423
  if (!agent2) return Response.json({ error: "Agent not found" }, { status: 404 });
4306
4424
  const body = await req.json();
4307
4425
  if (!body.content) return Response.json({ error: "content is required" }, { status: 400 });
@@ -4314,7 +4432,7 @@ function buildRouter2(deps) {
4314
4432
  });
4315
4433
  r.get("/agents/:id/knowledge", async (_req, ctx) => {
4316
4434
  const agentId = parseInt(ctx.params.id, 10);
4317
- const rows = await sql2`
4435
+ const rows = await sql4`
4318
4436
  SELECT id, title, created_at FROM "_knowledge_documents"
4319
4437
  WHERE agent_id = ${agentId}
4320
4438
  ORDER BY created_at DESC
@@ -4324,7 +4442,7 @@ function buildRouter2(deps) {
4324
4442
  r.delete("/agents/:id/knowledge/:docId", async (_req, ctx) => {
4325
4443
  const agentId = parseInt(ctx.params.id, 10);
4326
4444
  const docId = parseInt(ctx.params.docId, 10);
4327
- await sql2`DELETE FROM "_knowledge_documents" WHERE id = ${docId} AND agent_id = ${agentId}`;
4445
+ await sql4`DELETE FROM "_knowledge_documents" WHERE id = ${docId} AND agent_id = ${agentId}`;
4328
4446
  return Response.json({ ok: true });
4329
4447
  });
4330
4448
  return r;
@@ -4380,6 +4498,9 @@ function createSSEStream(iterable, opts) {
4380
4498
  }
4381
4499
 
4382
4500
  // agent/run.ts
4501
+ function hasKnowledgeDocs(sql4, agentId) {
4502
+ return sql4`SELECT 1 FROM "_knowledge_documents" WHERE agent_id = ${agentId} LIMIT 1`.then((r) => r.length > 0);
4503
+ }
4383
4504
  function chunkContent(content, chunkSize = 512, overlap = 64) {
4384
4505
  const paragraphs = content.split(/\n\n+/);
4385
4506
  const chunks = [];
@@ -4394,31 +4515,28 @@ function chunkContent(content, chunkSize = 512, overlap = 64) {
4394
4515
  if (current) chunks.push(current);
4395
4516
  return chunks;
4396
4517
  }
4397
- function hasKnowledgeDocs(sql2, agentId) {
4398
- return sql2`SELECT 1 FROM "_knowledge_documents" WHERE agent_id = ${agentId} LIMIT 1`.then((r) => r.length > 0);
4399
- }
4400
- async function searchKnowledge(sql2, embedModel, agentId, query, limit = 5) {
4518
+ async function searchKnowledge(sql4, embedModel, agentId, query, limit = 5) {
4401
4519
  const { embedding } = await embed({ model: embedModel, value: query });
4402
4520
  const vec = `[${embedding.join(",")}]`;
4403
- const docs = await sql2.unsafe(
4521
+ const docs = await sql4.unsafe(
4404
4522
  `SELECT id, title, content, metadata, embedding <=> $1::vector AS _score FROM "_knowledge_documents" WHERE agent_id = $2 ORDER BY embedding <=> $1::vector LIMIT $3`,
4405
4523
  [vec, agentId, limit]
4406
4524
  );
4407
4525
  return docs.map((d) => ({ id: d.id, title: d.title, content: d.content, score: d._score }));
4408
4526
  }
4409
- async function loadAgent(sql2, agentId) {
4410
- const [row] = await sql2`SELECT * FROM "_agents" WHERE id = ${agentId} LIMIT 1`;
4527
+ async function loadAgent(agents, agentId) {
4528
+ const row = await agents.read(agentId);
4411
4529
  return row ?? null;
4412
4530
  }
4413
4531
  function createRunner(deps) {
4414
- const { sql: sql2, getModel, getEmbeddingModel, userTools } = deps;
4532
+ const { sql: sql4, agents, getModel, getEmbeddingModel, userTools } = deps;
4415
4533
  async function run(agentId, params) {
4416
- const agent2 = await loadAgent(sql2, agentId);
4534
+ const agent2 = await loadAgent(agents, agentId);
4417
4535
  if (!agent2 || !agent2.active) throw new Error("Agent not found or inactive");
4418
4536
  const model = getModel();
4419
4537
  const embedModel = getEmbeddingModel();
4420
4538
  const start = Date.now();
4421
- const hasKB = await hasKnowledgeDocs(sql2, agentId);
4539
+ const hasKB = await hasKnowledgeDocs(sql4, agentId);
4422
4540
  const messages2 = params.messages ?? [];
4423
4541
  if (messages2.length === 0) {
4424
4542
  messages2.push({ role: "user", content: params.input });
@@ -4432,7 +4550,7 @@ function createRunner(deps) {
4432
4550
  limit: z4.number().default(5).describe("\u8FD4\u56DE\u7ED3\u679C\u6570\u91CF")
4433
4551
  }),
4434
4552
  execute: async ({ query, limit }) => {
4435
- return searchKnowledge(sql2, embedModel, agentId, query, limit);
4553
+ return searchKnowledge(sql4, embedModel, agentId, query, limit);
4436
4554
  }
4437
4555
  };
4438
4556
  }
@@ -4481,13 +4599,13 @@ function createRunner(deps) {
4481
4599
  const [first] = chunks;
4482
4600
  const { embedding } = await embed({ model: embedModel, value: first });
4483
4601
  const vec = `[${embedding.join(",")}]`;
4484
- const [doc] = await sql2.unsafe(
4602
+ const [doc] = await sql4.unsafe(
4485
4603
  `INSERT INTO "_knowledge_documents" ("agent_id", "title", "content", "embedding") VALUES ($1, $2, $3, $4::vector) RETURNING *`,
4486
4604
  [agentId, title, first, vec]
4487
4605
  );
4488
4606
  for (let i = 1; i < chunks.length; i++) {
4489
4607
  const { embedding: emb } = await embed({ model: embedModel, value: chunks[i] });
4490
- await sql2.unsafe(
4608
+ await sql4.unsafe(
4491
4609
  `INSERT INTO "_knowledge_documents" ("agent_id", "title", "content", "embedding") VALUES ($1, $2, $3, $4::vector)`,
4492
4610
  [agentId, `${title} (${i + 1})`, chunks[i], `[${emb.join(",")}]`]
4493
4611
  );
@@ -4512,7 +4630,7 @@ function createModelsFromEnv() {
4512
4630
  }
4513
4631
  function agent(options) {
4514
4632
  const pg = options.pg;
4515
- const sql2 = pg.sql;
4633
+ const sql4 = pg.sql;
4516
4634
  const model = options.model;
4517
4635
  const embeddingModel = options.embeddingModel;
4518
4636
  const dimension = options.embeddingDimension ?? 1024;
@@ -4527,11 +4645,33 @@ function agent(options) {
4527
4645
  if (!defaultModels) defaultModels = createModelsFromEnv();
4528
4646
  return defaultModels.embeddingModel;
4529
4647
  }
4530
- const runner = createRunner({ sql: sql2, getModel, getEmbeddingModel, userTools: options.tools });
4648
+ const agentsTable = pg.table("_agents", {
4649
+ id: serial("id"),
4650
+ tenant_id: text("tenant_id"),
4651
+ name: text("name"),
4652
+ description: text("description"),
4653
+ type: text("type"),
4654
+ model: text("model"),
4655
+ system_prompt: text("system_prompt"),
4656
+ owner_id: integer("owner_id"),
4657
+ active: boolean_("active"),
4658
+ created_at: timestamptz("created_at"),
4659
+ updated_at: timestamptz("updated_at")
4660
+ });
4661
+ const knowledgeTable = pg.table("_knowledge_documents", {
4662
+ id: serial("id"),
4663
+ agent_id: integer("agent_id"),
4664
+ title: text("title"),
4665
+ content: text("content"),
4666
+ embedding: vector("embedding", dimension),
4667
+ metadata: jsonb("metadata"),
4668
+ created_at: timestamptz("created_at")
4669
+ });
4670
+ const runner = createRunner({ sql: sql4, agents: agentsTable, knowledge: knowledgeTable, getModel, getEmbeddingModel, userTools: options.tools });
4531
4671
  const base = new PgModule(pg);
4532
4672
  return {
4533
- migrate: () => migrate3({ sql: sql2, embeddingDimension: dimension }),
4534
- router: () => buildRouter2({ sql: sql2, runner }),
4673
+ migrate: () => migrate3({ sql: sql4, embeddingDimension: dimension }),
4674
+ router: () => buildRouter2({ sql: sql4, agents: agentsTable, runner }),
4535
4675
  run: (agentId, params) => runner.run(agentId, params),
4536
4676
  addKnowledge: (agentId, title, content) => runner.addKnowledge(agentId, title, content),
4537
4677
  close: () => base.close()
@@ -4539,7 +4679,7 @@ function agent(options) {
4539
4679
  }
4540
4680
 
4541
4681
  // messager/migrate.ts
4542
- async function migrate4(sql2) {
4682
+ async function migrate4(sql4) {
4543
4683
  const channels2 = pgTable("_channels", {
4544
4684
  id: serial("id").primaryKey(),
4545
4685
  tenant_id: text("tenant_id"),
@@ -4548,8 +4688,8 @@ async function migrate4(sql2) {
4548
4688
  created_by: integer("created_by").notNull(),
4549
4689
  created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4550
4690
  });
4551
- await channels2.create(sql2);
4552
- await channels2.createIndex(sql2, "tenant_id");
4691
+ await channels2.create(sql4);
4692
+ await channels2.createIndex(sql4, "tenant_id");
4553
4693
  const members = pgTable("_channel_members", {
4554
4694
  id: serial("id").primaryKey(),
4555
4695
  channel_id: integer("channel_id").notNull().references("_channels", "id", "cascade"),
@@ -4559,9 +4699,9 @@ async function migrate4(sql2) {
4559
4699
  last_read_id: integer("last_read_id"),
4560
4700
  last_read_at: timestamptz("last_read_at")
4561
4701
  });
4562
- await members.create(sql2);
4563
- await members.createIndex(sql2, "member_id");
4564
- await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_channel_members_unique_idx" ON "_channel_members" ("channel_id", "member_id", "member_type")`);
4702
+ await members.create(sql4);
4703
+ await members.createIndex(sql4, "member_id");
4704
+ await sql4.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_channel_members_unique_idx" ON "_channel_members" ("channel_id", "member_id", "member_type")`);
4565
4705
  const messages2 = pgTable("_messages", {
4566
4706
  id: serial("id").primaryKey(),
4567
4707
  channel_id: integer("channel_id").notNull().references("_channels", "id", "cascade"),
@@ -4575,8 +4715,8 @@ async function migrate4(sql2) {
4575
4715
  mime_type: text("mime_type"),
4576
4716
  created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4577
4717
  });
4578
- await messages2.create(sql2);
4579
- await messages2.createIndex(sql2, ["channel_id", "created_at"], { desc: true });
4718
+ await messages2.create(sql4);
4719
+ await messages2.createIndex(sql4, ["channel_id", "created_at"], { desc: true });
4580
4720
  }
4581
4721
 
4582
4722
  // messager/ws.ts
@@ -4613,7 +4753,7 @@ function unsubscribe(ws) {
4613
4753
  }
4614
4754
  }
4615
4755
  function createWSHandler(deps) {
4616
- const { sql: sql2, agents } = deps;
4756
+ const { sql: sql4, agents } = deps;
4617
4757
  return {
4618
4758
  open(ws, ctx) {
4619
4759
  const userId = ctx.user?.id;
@@ -4636,7 +4776,7 @@ function createWSHandler(deps) {
4636
4776
  switch (type) {
4637
4777
  case "message": {
4638
4778
  if (!content || !channel_id) return;
4639
- const [row] = await sql2`
4779
+ const [row] = await sql4`
4640
4780
  INSERT INTO "_messages" ("channel_id", "sender_id", "sender_type", "content")
4641
4781
  VALUES (${channel_id}, ${userId}, 'user', ${content})
4642
4782
  RETURNING *
@@ -4645,14 +4785,14 @@ function createWSHandler(deps) {
4645
4785
  broadcastToChannel(channel_id, { type: "message", data: message });
4646
4786
  subscribe(ws, userId, channel_id);
4647
4787
  if (agents) {
4648
- const agentMembers = await sql2`
4788
+ const agentMembers = await sql4`
4649
4789
  SELECT member_id FROM "_channel_members"
4650
4790
  WHERE channel_id = ${channel_id} AND member_type = 'agent'
4651
4791
  `;
4652
4792
  for (const am of agentMembers) {
4653
4793
  agents.run(am.member_id, { input: content, stream: false }).then((result) => {
4654
4794
  if ("output" in result && result.output) {
4655
- sql2`
4795
+ sql4`
4656
4796
  INSERT INTO "_messages" ("channel_id", "sender_id", "sender_type", "content")
4657
4797
  VALUES (${channel_id}, ${am.member_id}, 'agent', ${result.output})
4658
4798
  `.then(([r]) => {
@@ -4681,7 +4821,7 @@ function createWSHandler(deps) {
4681
4821
  case "read": {
4682
4822
  if (!channel_id || !last_message_id) return;
4683
4823
  subscribe(ws, userId, channel_id);
4684
- await sql2`
4824
+ await sql4`
4685
4825
  UPDATE "_channel_members"
4686
4826
  SET last_read_id = ${last_message_id}, last_read_at = NOW()
4687
4827
  WHERE channel_id = ${channel_id} AND member_id = ${userId} AND member_type = 'user'
@@ -4707,24 +4847,24 @@ function createWSHandler(deps) {
4707
4847
 
4708
4848
  // messager/rest.ts
4709
4849
  function buildRouter3(deps) {
4710
- const { sql: sql2, agents } = deps;
4850
+ const { sql: sql4, channels: channels2, members, messages: messages2, agents } = deps;
4711
4851
  const r = new Router();
4712
4852
  r.post("/channels", async (req) => {
4713
4853
  const body = await req.json();
4714
4854
  if (!body.name) return Response.json({ error: "name is required" }, { status: 400 });
4715
- const [ch] = await sql2`
4855
+ const [ch] = await sql4`
4716
4856
  INSERT INTO "_channels" ("name", "type", "created_by")
4717
4857
  VALUES (${body.name}, ${body.type || "channel"}, ${body.created_by || 1})
4718
4858
  RETURNING *
4719
4859
  `;
4720
4860
  const channel = ch;
4721
- await sql2`
4861
+ await sql4`
4722
4862
  INSERT INTO "_channel_members" ("channel_id", "member_id", "member_type", "role")
4723
4863
  VALUES (${channel.id}, ${channel.created_by}, 'user', 'admin')
4724
4864
  `;
4725
4865
  if (Array.isArray(body.members)) {
4726
4866
  for (const m of body.members) {
4727
- await sql2`
4867
+ await sql4`
4728
4868
  INSERT INTO "_channel_members" ("channel_id", "member_id", "member_type", "role")
4729
4869
  VALUES (${channel.id}, ${m.member_id ?? m}, ${m.member_type ?? "user"}, ${m.role ?? "member"})
4730
4870
  ON CONFLICT DO NOTHING
@@ -4735,7 +4875,7 @@ function buildRouter3(deps) {
4735
4875
  });
4736
4876
  r.get("/channels", async (_req, ctx) => {
4737
4877
  const userId = ctx.user?.id ?? 1;
4738
- const rows = await sql2`
4878
+ const rows = await sql4`
4739
4879
  SELECT c.*, (
4740
4880
  SELECT content FROM "_messages"
4741
4881
  WHERE channel_id = c.id
@@ -4750,22 +4890,20 @@ function buildRouter3(deps) {
4750
4890
  });
4751
4891
  r.get("/channels/:id", async (_req, ctx) => {
4752
4892
  const id2 = parseInt(ctx.params.id, 10);
4753
- const [ch] = await sql2`SELECT * FROM "_channels" WHERE id = ${id2} LIMIT 1`;
4893
+ const ch = await channels2.read(id2);
4754
4894
  if (!ch) return Response.json({ error: "Channel not found" }, { status: 404 });
4755
- const members = await sql2`
4756
- SELECT * FROM "_channel_members" WHERE channel_id = ${id2}
4757
- `;
4758
- return Response.json({ channel: ch, members });
4895
+ const { data: memberRows } = await members.readMany({ channel_id: id2 });
4896
+ return Response.json({ channel: ch, members: memberRows });
4759
4897
  });
4760
4898
  r.delete("/channels/:id", async (_req, ctx) => {
4761
4899
  const id2 = parseInt(ctx.params.id, 10);
4762
- await sql2`DELETE FROM "_channels" WHERE id = ${id2}`;
4900
+ await sql4`DELETE FROM "_channels" WHERE id = ${id2}`;
4763
4901
  return Response.json({ ok: true });
4764
4902
  });
4765
4903
  r.post("/channels/:id/members", async (req, ctx) => {
4766
4904
  const channelId = parseInt(ctx.params.id, 10);
4767
4905
  const body = await req.json();
4768
- await sql2`
4906
+ await sql4`
4769
4907
  INSERT INTO "_channel_members" ("channel_id", "member_id", "member_type", "role")
4770
4908
  VALUES (${channelId}, ${body.member_id}, ${body.member_type || "user"}, ${body.role || "member"})
4771
4909
  ON CONFLICT DO NOTHING
@@ -4775,7 +4913,7 @@ function buildRouter3(deps) {
4775
4913
  r.delete("/channels/:id/members/:memberId", async (_req, ctx) => {
4776
4914
  const channelId = parseInt(ctx.params.id, 10);
4777
4915
  const memberId = parseInt(ctx.params.memberId, 10);
4778
- await sql2`
4916
+ await sql4`
4779
4917
  DELETE FROM "_channel_members"
4780
4918
  WHERE channel_id = ${channelId} AND member_id = ${memberId}
4781
4919
  `;
@@ -4787,25 +4925,23 @@ function buildRouter3(deps) {
4787
4925
  const limit = Math.min(parseInt(url.searchParams.get("limit") || "50", 10), 100);
4788
4926
  const before = url.searchParams.get("before");
4789
4927
  if (before) {
4790
- const rows2 = await sql2`
4791
- SELECT * FROM "_messages"
4792
- WHERE channel_id = ${channelId} AND id < ${parseInt(before, 10)}
4793
- ORDER BY created_at DESC LIMIT ${limit}
4794
- `;
4928
+ const { data: rows2 } = await messages2.readMany(
4929
+ [eq("channel_id", channelId), lt("id", parseInt(before, 10))],
4930
+ { orderBy: { created_at: "desc" }, limit }
4931
+ );
4795
4932
  return Response.json({ rows: rows2.reverse(), count: rows2.length });
4796
4933
  }
4797
- const rows = await sql2`
4798
- SELECT * FROM "_messages"
4799
- WHERE channel_id = ${channelId}
4800
- ORDER BY created_at DESC LIMIT ${limit}
4801
- `;
4934
+ const { data: rows } = await messages2.readMany(
4935
+ { channel_id: channelId },
4936
+ { orderBy: { created_at: "desc" }, limit }
4937
+ );
4802
4938
  return Response.json({ rows: rows.reverse(), count: rows.length });
4803
4939
  });
4804
4940
  r.post("/channels/:id/messages", async (req, ctx) => {
4805
4941
  const channelId = parseInt(ctx.params.id, 10);
4806
4942
  const body = await req.json();
4807
4943
  if (!body.content) return Response.json({ error: "content is required" }, { status: 400 });
4808
- const [row] = await sql2`
4944
+ const [row] = await sql4`
4809
4945
  INSERT INTO "_messages" ("channel_id", "sender_id", "sender_type", "type", "content")
4810
4946
  VALUES (${channelId}, ${body.sender_id ?? 1}, ${body.sender_type || "user"}, ${body.type || "text"}, ${body.content})
4811
4947
  RETURNING *
@@ -4813,14 +4949,14 @@ function buildRouter3(deps) {
4813
4949
  const msg = row;
4814
4950
  broadcastToChannel(channelId, { type: "message", data: msg });
4815
4951
  if (agents) {
4816
- const agentMembers = await sql2`
4952
+ const agentMembers = await sql4`
4817
4953
  SELECT member_id FROM "_channel_members"
4818
4954
  WHERE channel_id = ${channelId} AND member_type = 'agent'
4819
4955
  `;
4820
4956
  for (const am of agentMembers) {
4821
4957
  agents.run(am.member_id, { input: body.content, stream: false }).then((result) => {
4822
4958
  if ("output" in result && result.output) {
4823
- sql2`
4959
+ sql4`
4824
4960
  INSERT INTO "_messages" ("channel_id", "sender_id", "sender_type", "content")
4825
4961
  VALUES (${channelId}, ${am.member_id}, 'agent', ${result.output})
4826
4962
  `.then(([r2]) => {
@@ -4840,7 +4976,7 @@ function buildRouter3(deps) {
4840
4976
  const channelId = parseInt(ctx.params.id, 10);
4841
4977
  const body = await req.json();
4842
4978
  const userId = body.user_id ?? ctx.user?.id ?? 1;
4843
- await sql2`
4979
+ await sql4`
4844
4980
  UPDATE "_channel_members"
4845
4981
  SET last_read_id = ${body.last_message_id}, last_read_at = NOW()
4846
4982
  WHERE channel_id = ${channelId} AND member_id = ${userId} AND member_type = 'user'
@@ -4857,15 +4993,45 @@ function buildRouter3(deps) {
4857
4993
  // messager/client.ts
4858
4994
  function messager(options) {
4859
4995
  const pg = options.pg;
4860
- const sql2 = pg.sql;
4996
+ const sql4 = pg.sql;
4861
4997
  const agents = options.agents;
4862
4998
  const base = new PgModule(pg);
4999
+ const channels2 = pg.table("_channels", {
5000
+ id: serial("id"),
5001
+ tenant_id: text("tenant_id"),
5002
+ name: text("name"),
5003
+ type: text("type"),
5004
+ created_by: integer("created_by"),
5005
+ created_at: timestamptz("created_at")
5006
+ });
5007
+ const members = pg.table("_channel_members", {
5008
+ id: serial("id"),
5009
+ channel_id: integer("channel_id"),
5010
+ member_id: integer("member_id"),
5011
+ member_type: text("member_type"),
5012
+ role: text("role"),
5013
+ last_read_id: integer("last_read_id"),
5014
+ last_read_at: timestamptz("last_read_at")
5015
+ });
5016
+ const messages2 = pg.table("_messages", {
5017
+ id: serial("id"),
5018
+ channel_id: integer("channel_id"),
5019
+ sender_id: integer("sender_id"),
5020
+ sender_type: text("sender_type"),
5021
+ type: text("type"),
5022
+ content: text("content"),
5023
+ file_url: text("file_url"),
5024
+ file_name: text("file_name"),
5025
+ file_size: integer("file_size"),
5026
+ mime_type: text("mime_type"),
5027
+ created_at: timestamptz("created_at")
5028
+ });
4863
5029
  return {
4864
- migrate: () => migrate4(sql2),
4865
- router: () => buildRouter3({ sql: sql2, agents }),
4866
- wsHandler: () => createWSHandler({ sql: sql2, agents }),
5030
+ migrate: () => migrate4(sql4),
5031
+ router: () => buildRouter3({ sql: sql4, channels: channels2, members, messages: messages2, agents }),
5032
+ wsHandler: () => createWSHandler({ sql: sql4, agents }),
4867
5033
  async send(channelId, content, opts) {
4868
- const [row] = await sql2`
5034
+ const [row] = await sql4`
4869
5035
  INSERT INTO "_messages" ("channel_id", "sender_id", "sender_type", "type", "content")
4870
5036
  VALUES (${channelId}, ${opts?.sender_id ?? 0}, ${opts?.sender_type ?? "system"}, ${opts?.type ?? "text"}, ${content})
4871
5037
  RETURNING *
@@ -5432,7 +5598,7 @@ function ensureCertificates(config) {
5432
5598
  import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
5433
5599
 
5434
5600
  // opencode/migrate.ts
5435
- async function migrate5(sql2) {
5601
+ async function migrate5(sql4) {
5436
5602
  const sessions2 = pgTable("_opencode_sessions", {
5437
5603
  id: uuid("id").default(sql`gen_random_uuid()`).primaryKey(),
5438
5604
  tenant_id: text("tenant_id"),
@@ -5447,8 +5613,8 @@ async function migrate5(sql2) {
5447
5613
  created_at: timestamptz("created_at").default(sql`NOW()`),
5448
5614
  updated_at: timestamptz("updated_at").default(sql`NOW()`)
5449
5615
  });
5450
- await sessions2.create(sql2);
5451
- await sessions2.createIndex(sql2, "user_id");
5616
+ await sessions2.create(sql4);
5617
+ await sessions2.createIndex(sql4, "user_id");
5452
5618
  const messages2 = pgTable("_opencode_messages", {
5453
5619
  id: serial("id").primaryKey(),
5454
5620
  session_id: uuid("session_id").notNull().references("_opencode_sessions", "id", "cascade"),
@@ -5460,8 +5626,8 @@ async function migrate5(sql2) {
5460
5626
  tokens_out: integer("tokens_out").default(0),
5461
5627
  created_at: timestamptz("created_at").default(sql`NOW()`)
5462
5628
  });
5463
- await messages2.create(sql2);
5464
- await messages2.createIndex(sql2, ["session_id", "created_at"]);
5629
+ await messages2.create(sql4);
5630
+ await messages2.createIndex(sql4, ["session_id", "created_at"]);
5465
5631
  }
5466
5632
 
5467
5633
  // opencode/session.ts
@@ -5490,11 +5656,11 @@ var messages = pgTable("_opencode_messages", {
5490
5656
  tokens_out: integer("tokens_out"),
5491
5657
  created_at: timestamptz("created_at")
5492
5658
  });
5493
- async function createSession(sql2, opts, cwd, mountPath) {
5659
+ async function createSession(sql4, opts, cwd, mountPath) {
5494
5660
  const id2 = randomUUID2();
5495
5661
  const ws = computeSessionWorkspace(cwd, mountPath, id2);
5496
5662
  await mkdir2(ws, { recursive: true });
5497
- const [row] = await sql2`
5663
+ const [row] = await sql4`
5498
5664
  INSERT INTO "_opencode_sessions" ("id", "user_id", "title", "model", "workspace", "system_prompt")
5499
5665
  VALUES (${id2}, ${opts.userId ?? 0}, ${opts.title ?? null}, ${opts.model ?? "deepseek-v4-flash"}, ${ws}, ${opts.systemPrompt ?? null})
5500
5666
  RETURNING *
@@ -5505,39 +5671,39 @@ function computeSessionWorkspace(cwd, mountPath, sessionId) {
5505
5671
  const name = !mountPath || mountPath === "/" ? "default" : mountPath.replace(/^\//, "");
5506
5672
  return join3(cwd, ".sessions", name, sessionId);
5507
5673
  }
5508
- async function getSession(sql2, id2) {
5509
- const rows = await sessions.find(sql2, { id: id2, active: true });
5674
+ async function getSession(sql4, id2) {
5675
+ const { data: rows } = await sessions.readMany(sql4, { id: id2, active: true });
5510
5676
  return rows[0] ?? null;
5511
5677
  }
5512
- async function listSessions(sql2, userId) {
5678
+ async function listSessions(sql4, userId) {
5513
5679
  const opts = { orderBy: { updated_at: "desc" } };
5514
5680
  if (userId !== void 0) {
5515
- const rows2 = await sessions.find(sql2, { user_id: userId, active: true }, opts);
5681
+ const { data: rows2 } = await sessions.readMany(sql4, { user_id: userId, active: true }, opts);
5516
5682
  return rows2;
5517
5683
  }
5518
- const rows = await sessions.find(sql2, { active: true }, opts);
5684
+ const { data: rows } = await sessions.readMany(sql4, { active: true }, opts);
5519
5685
  return rows;
5520
5686
  }
5521
- async function deleteSession(sql2, id2) {
5522
- await sessions.update(sql2, { id: id2 }, { active: false, updated_at: sql`NOW()` });
5687
+ async function deleteSession(sql4, id2) {
5688
+ await sessions.update(sql4, id2, { active: false, updated_at: sql`NOW()` });
5523
5689
  }
5524
- async function getHistory(sql2, sessionId, limit = 50) {
5525
- const rows = await messages.find(sql2, { session_id: sessionId }, {
5690
+ async function getHistory(sql4, sessionId, limit = 50) {
5691
+ const { data: rows } = await messages.readMany(sql4, { session_id: sessionId }, {
5526
5692
  orderBy: { created_at: "asc" },
5527
5693
  limit
5528
5694
  });
5529
5695
  return rows;
5530
5696
  }
5531
- async function addTextMessage(sql2, sessionId, role, content, tokensIn = 0, tokensOut = 0) {
5532
- const [row] = await sql2`
5697
+ async function addTextMessage(sql4, sessionId, role, content, tokensIn = 0, tokensOut = 0) {
5698
+ const [row] = await sql4`
5533
5699
  INSERT INTO "_opencode_messages" ("session_id", "role", "content", "tokens_in", "tokens_out")
5534
5700
  VALUES (${sessionId}, ${role}, ${content}, ${tokensIn}, ${tokensOut})
5535
5701
  RETURNING *
5536
5702
  `;
5537
5703
  return row;
5538
5704
  }
5539
- async function addToolMessages(sql2, sessionId, toolCalls, toolResults) {
5540
- const [row] = await sql2`
5705
+ async function addToolMessages(sql4, sessionId, toolCalls, toolResults) {
5706
+ const [row] = await sql4`
5541
5707
  INSERT INTO "_opencode_messages" ("session_id", "role", "tool_calls", "tool_results")
5542
5708
  VALUES (${sessionId}, 'tool', ${JSON.stringify(toolCalls)}, ${JSON.stringify(toolResults)})
5543
5709
  RETURNING *
@@ -5548,7 +5714,7 @@ async function addToolMessages(sql2, sessionId, toolCalls, toolResults) {
5548
5714
  // opencode/run.ts
5549
5715
  import { streamText as streamText2, stepCountIs } from "ai";
5550
5716
  async function* executeGenerator(opts) {
5551
- const { sessionId, input, model, tools, systemPrompt, messages: messages2, sql: sql2, abortSignal } = opts;
5717
+ const { sessionId, input, model, tools, systemPrompt, messages: messages2, sql: sql4, abortSignal } = opts;
5552
5718
  const lastStepToolCalls = [];
5553
5719
  let currentAssistantText = "";
5554
5720
  let currentUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
@@ -5577,7 +5743,7 @@ async function* executeGenerator(opts) {
5577
5743
  }
5578
5744
  if (step.toolResults.length > 0) {
5579
5745
  try {
5580
- await addToolMessages(sql2, sessionId, lastStepToolCalls, step.toolResults);
5746
+ await addToolMessages(sql4, sessionId, lastStepToolCalls, step.toolResults);
5581
5747
  } catch (e) {
5582
5748
  console.error("[opencode] save tool messages failed:", e);
5583
5749
  }
@@ -5591,7 +5757,7 @@ async function* executeGenerator(opts) {
5591
5757
  totalTokens: result2.usage?.totalTokens ?? 0
5592
5758
  };
5593
5759
  try {
5594
- await addTextMessage(sql2, sessionId, "assistant", currentAssistantText, currentUsage.promptTokens, currentUsage.completionTokens);
5760
+ await addTextMessage(sql4, sessionId, "assistant", currentAssistantText, currentUsage.promptTokens, currentUsage.completionTokens);
5595
5761
  } catch (e) {
5596
5762
  console.error("[opencode] save message failed:", e);
5597
5763
  }
@@ -6024,11 +6190,11 @@ function createTools(ctx) {
6024
6190
 
6025
6191
  // opencode/rest.ts
6026
6192
  async function buildRouter4(deps) {
6027
- const { sql: sql2, model, workspace, systemPrompt, skills, skillsRegistry, permissions, pendingQuestions } = deps;
6193
+ const { sql: sql4, model, workspace, systemPrompt, skills, skillsRegistry, permissions, pendingQuestions } = deps;
6028
6194
  const router = new Router();
6029
6195
  router.post("/sessions", async (req, ctx) => {
6030
6196
  const body = await req.json().catch(() => ({}));
6031
- const session = await createSession(sql2, {
6197
+ const session = await createSession(sql4, {
6032
6198
  title: body.title,
6033
6199
  model: body.model,
6034
6200
  systemPrompt: body.systemPrompt || systemPrompt
@@ -6036,24 +6202,24 @@ async function buildRouter4(deps) {
6036
6202
  return Response.json(session, { status: 201 });
6037
6203
  });
6038
6204
  router.get("/sessions", async () => {
6039
- const sessions2 = await listSessions(sql2);
6205
+ const sessions2 = await listSessions(sql4);
6040
6206
  return Response.json(sessions2);
6041
6207
  });
6042
6208
  router.get("/sessions/:id", async (_req, ctx) => {
6043
6209
  const id2 = ctx.params.id;
6044
- const session = await getSession(sql2, id2);
6210
+ const session = await getSession(sql4, id2);
6045
6211
  if (!session) return new Response("Not Found", { status: 404 });
6046
- const messages2 = await getHistory(sql2, id2);
6212
+ const messages2 = await getHistory(sql4, id2);
6047
6213
  return Response.json({ session, messages: messages2 });
6048
6214
  });
6049
6215
  router.delete("/sessions/:id", async (_req, ctx) => {
6050
6216
  const id2 = ctx.params.id;
6051
- await deleteSession(sql2, id2);
6217
+ await deleteSession(sql4, id2);
6052
6218
  return new Response(null, { status: 204 });
6053
6219
  });
6054
6220
  router.post("/sessions/:id/message", async (req, ctx) => {
6055
6221
  const sessionId = ctx.params.id;
6056
- const session = await getSession(sql2, sessionId);
6222
+ const session = await getSession(sql4, sessionId);
6057
6223
  if (!session) return new Response("Session Not Found", { status: 404 });
6058
6224
  const { content } = await req.json();
6059
6225
  if (!content) return new Response("Missing content", { status: 400 });
@@ -6071,8 +6237,8 @@ async function buildRouter4(deps) {
6071
6237
  skills: allSkills,
6072
6238
  systemPrompt: session.system_prompt || systemPrompt
6073
6239
  });
6074
- const history = await getHistory(sql2, sessionId);
6075
- await addTextMessage(sql2, sessionId, "user", content);
6240
+ const history = await getHistory(sql4, sessionId);
6241
+ await addTextMessage(sql4, sessionId, "user", content);
6076
6242
  const stream = executeGenerator({
6077
6243
  sessionId,
6078
6244
  input: content,
@@ -6080,7 +6246,7 @@ async function buildRouter4(deps) {
6080
6246
  tools,
6081
6247
  systemPrompt: sysPrompt,
6082
6248
  messages: history,
6083
- sql: sql2
6249
+ sql: sql4
6084
6250
  });
6085
6251
  return createSSEStream(stream);
6086
6252
  });
@@ -6097,7 +6263,7 @@ async function buildRouter4(deps) {
6097
6263
  // opencode/ws.ts
6098
6264
  var clients = /* @__PURE__ */ new WeakMap();
6099
6265
  function createWSHandler2(deps) {
6100
- const { sql: sql2, model, workspace, systemPrompt, skills, skillsRegistry, permissions, pendingQuestions } = deps;
6266
+ const { sql: sql4, model, workspace, systemPrompt, skills, skillsRegistry, permissions, pendingQuestions } = deps;
6101
6267
  return {
6102
6268
  open(ws, ctx) {
6103
6269
  const userId = ctx.user?.id ?? 0;
@@ -6117,7 +6283,7 @@ function createWSHandler2(deps) {
6117
6283
  switch (msg.type) {
6118
6284
  case "create": {
6119
6285
  try {
6120
- const session = await createSession(sql2, {
6286
+ const session = await createSession(sql4, {
6121
6287
  userId: client.userId,
6122
6288
  title: msg.title,
6123
6289
  model: msg.model,
@@ -6140,7 +6306,7 @@ function createWSHandler2(deps) {
6140
6306
  client.abortController = controller;
6141
6307
  client.currentSessionId = session_id;
6142
6308
  try {
6143
- const session = await getSession(sql2, session_id);
6309
+ const session = await getSession(sql4, session_id);
6144
6310
  if (!session) {
6145
6311
  ws.send(JSON.stringify({ type: "error", error: "Session not found" }));
6146
6312
  return;
@@ -6159,8 +6325,8 @@ function createWSHandler2(deps) {
6159
6325
  skills: allSkills,
6160
6326
  systemPrompt: session.system_prompt || systemPrompt
6161
6327
  });
6162
- const history = await getHistory(sql2, session_id);
6163
- await addTextMessage(sql2, session_id, "user", content);
6328
+ const history = await getHistory(sql4, session_id);
6329
+ await addTextMessage(sql4, session_id, "user", content);
6164
6330
  const stream = executeGenerator({
6165
6331
  sessionId: session_id,
6166
6332
  input: content,
@@ -6168,7 +6334,7 @@ function createWSHandler2(deps) {
6168
6334
  tools,
6169
6335
  systemPrompt: sysPrompt,
6170
6336
  messages: history,
6171
- sql: sql2,
6337
+ sql: sql4,
6172
6338
  abortSignal: controller.signal
6173
6339
  });
6174
6340
  for await (const event of stream) {
@@ -6303,7 +6469,7 @@ function buildSkillRegistry(discovered, manual) {
6303
6469
  // opencode/client.ts
6304
6470
  async function opencode(options) {
6305
6471
  const pg = options.pg;
6306
- const sql2 = pg.sql;
6472
+ const sql4 = pg.sql;
6307
6473
  const baseURL = options.baseURL || process.env.DEEPSEEK_BASE_URL || "https://api.deepseek.com/v1";
6308
6474
  const apiKey = options.apiKey || process.env.DEEPSEEK_API_KEY;
6309
6475
  const workspace = options.workspace || process.cwd();
@@ -6318,9 +6484,9 @@ async function opencode(options) {
6318
6484
  const pendingQuestions = /* @__PURE__ */ new Map();
6319
6485
  const base = new PgModule(pg);
6320
6486
  return {
6321
- migrate: () => migrate5(sql2),
6322
- router: () => buildRouter4({ sql: sql2, model, workspace, systemPrompt, skills: manualSkills, skillsRegistry, permissions, pendingQuestions }),
6323
- wsHandler: () => createWSHandler2({ sql: sql2, model, workspace, systemPrompt, skills: manualSkills, skillsRegistry, permissions, pendingQuestions }),
6487
+ migrate: () => migrate5(sql4),
6488
+ router: () => buildRouter4({ sql: sql4, model, workspace, systemPrompt, skills: manualSkills, skillsRegistry, permissions, pendingQuestions }),
6489
+ wsHandler: () => createWSHandler2({ sql: sql4, model, workspace, systemPrompt, skills: manualSkills, skillsRegistry, permissions, pendingQuestions }),
6324
6490
  close: () => base.close()
6325
6491
  };
6326
6492
  }
@@ -6424,6 +6590,183 @@ function mailer(options) {
6424
6590
  }
6425
6591
  return { send, close };
6426
6592
  }
6593
+
6594
+ // logdb/migrate.ts
6595
+ function pad(n) {
6596
+ return n < 10 ? "0" + n : String(n);
6597
+ }
6598
+ function startOfMonth(year, month) {
6599
+ return new Date(year, month, 1);
6600
+ }
6601
+ function formatYM(d) {
6602
+ return `${d.getFullYear()}_${pad(d.getMonth() + 1)}`;
6603
+ }
6604
+ function toISO(d) {
6605
+ return d.toISOString().slice(0, 19) + "+00:00";
6606
+ }
6607
+ async function ensurePartitions(sql4, tableName) {
6608
+ const now = /* @__PURE__ */ new Date();
6609
+ for (let i = 0; i < 13; i++) {
6610
+ const start = startOfMonth(now.getFullYear(), now.getMonth() + i);
6611
+ const end = startOfMonth(now.getFullYear(), now.getMonth() + i + 1);
6612
+ const partName = `${tableName}_${formatYM(start)}`;
6613
+ await sql4.unsafe(`
6614
+ CREATE TABLE IF NOT EXISTS "${partName}"
6615
+ PARTITION OF "${tableName}"
6616
+ FOR VALUES FROM ('${toISO(start)}') TO ('${toISO(end)}')
6617
+ `);
6618
+ }
6619
+ }
6620
+ async function migrate6(pg, tableName) {
6621
+ const entries = pgTable(tableName, {
6622
+ id: serial("id"),
6623
+ level: text("level").notNull(),
6624
+ source: text("source").notNull(),
6625
+ message: text("message").notNull(),
6626
+ metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
6627
+ created_at: timestamptz("created_at").default(sql`NOW()`)
6628
+ });
6629
+ await entries.create(pg.sql, { partitionBy: partitionBy("range", "created_at") });
6630
+ await entries.createIndex(pg.sql, ["created_at", "id"]);
6631
+ await entries.createIndex(pg.sql, ["level"]);
6632
+ await entries.createIndex(pg.sql, ["source"]);
6633
+ await entries.createIndex(pg.sql, ["level", "created_at"]);
6634
+ await ensurePartitions(pg.sql, tableName);
6635
+ }
6636
+
6637
+ // logdb/rest.ts
6638
+ function createHandler(entries) {
6639
+ return async (req, ctx) => {
6640
+ const body = await req.json();
6641
+ if (!body.level || !body.source || !body.message) {
6642
+ return Response.json({ error: "level, source, message are required" }, { status: 400 });
6643
+ }
6644
+ const metadata = body.metadata ?? {};
6645
+ if (ctx.user) {
6646
+ metadata.user_id = ctx.user.id;
6647
+ }
6648
+ const row = await entries.insert({
6649
+ level: body.level,
6650
+ source: body.source,
6651
+ message: body.message,
6652
+ metadata
6653
+ });
6654
+ if (typeof row.metadata === "string") {
6655
+ try {
6656
+ row.metadata = JSON.parse(row.metadata);
6657
+ } catch {
6658
+ }
6659
+ }
6660
+ return Response.json(row, { status: 201 });
6661
+ };
6662
+ }
6663
+ function listHandler(entries) {
6664
+ return async (req) => {
6665
+ const url = new URL(req.url);
6666
+ const conditions = [];
6667
+ const level = url.searchParams.get("level");
6668
+ if (level) conditions.push(eq("level", level));
6669
+ const source = url.searchParams.get("source");
6670
+ if (source) conditions.push(eq("source", source));
6671
+ for (const [key, value] of url.searchParams) {
6672
+ if (key.startsWith("meta.")) {
6673
+ conditions.push(contains("metadata", { [key.slice(5)]: value }));
6674
+ }
6675
+ }
6676
+ const after = url.searchParams.get("after");
6677
+ if (after) conditions.push(gte("created_at", after));
6678
+ const before = url.searchParams.get("before");
6679
+ if (before) conditions.push(lt("created_at", before));
6680
+ const limit = parseInt(url.searchParams.get("limit") ?? "50", 10);
6681
+ const offset = parseInt(url.searchParams.get("offset") ?? "0", 10);
6682
+ const { count, data } = await entries.readMany(
6683
+ conditions.length > 0 ? conditions : void 0,
6684
+ { orderBy: { created_at: "desc" }, limit, offset }
6685
+ );
6686
+ for (const row of data) {
6687
+ if (typeof row.metadata === "string") {
6688
+ try {
6689
+ row.metadata = JSON.parse(row.metadata);
6690
+ } catch {
6691
+ }
6692
+ }
6693
+ }
6694
+ return Response.json({ entries: data, total: count });
6695
+ };
6696
+ }
6697
+ function getHandler(entries) {
6698
+ return async (_req, ctx) => {
6699
+ const id2 = ctx.params?.id;
6700
+ if (!id2) return Response.json({ error: "id is required" }, { status: 400 });
6701
+ const row = await entries.read(parseInt(id2));
6702
+ if (!row) return Response.json({ error: "not found" }, { status: 404 });
6703
+ if (typeof row.metadata === "string") {
6704
+ try {
6705
+ row.metadata = JSON.parse(row.metadata);
6706
+ } catch {
6707
+ }
6708
+ }
6709
+ return Response.json(row);
6710
+ };
6711
+ }
6712
+
6713
+ // logdb/client.ts
6714
+ function logdb(options) {
6715
+ const pg = options.pg;
6716
+ const sql4 = pg.sql;
6717
+ const tableName = options.table ?? "_log_entries";
6718
+ const entries = pg.table(tableName, {
6719
+ id: serial("id"),
6720
+ level: text("level").notNull(),
6721
+ source: text("source").notNull(),
6722
+ message: text("message").notNull(),
6723
+ metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
6724
+ created_at: timestamptz("created_at").default(sql`NOW()`)
6725
+ });
6726
+ async function log(input) {
6727
+ const row = await entries.insert({
6728
+ level: input.level,
6729
+ source: input.source,
6730
+ message: input.message,
6731
+ metadata: input.metadata ?? {}
6732
+ });
6733
+ return row;
6734
+ }
6735
+ function router() {
6736
+ const r = new Router();
6737
+ r.post("/", createHandler(entries));
6738
+ r.get("/", listHandler(entries));
6739
+ r.get("/:id", getHandler(entries));
6740
+ return r;
6741
+ }
6742
+ async function clean(retentionMonths) {
6743
+ const cutoff = /* @__PURE__ */ new Date();
6744
+ cutoff.setMonth(cutoff.getMonth() - retentionMonths);
6745
+ const partitions = await sql4.unsafe(`
6746
+ SELECT relid::regclass::text AS name
6747
+ FROM pg_partition_tree('"${tableName}"'::regclass)
6748
+ WHERE relid IS DISTINCT FROM '"${tableName}"'::regclass
6749
+ `);
6750
+ let dropped = 0;
6751
+ for (const { name } of partitions) {
6752
+ const match = name.match(/_(\d{4})_(\d{2})$/);
6753
+ if (!match) continue;
6754
+ const partDate = new Date(parseInt(match[1]), parseInt(match[2]) - 1);
6755
+ if (partDate < cutoff) {
6756
+ await sql4.unsafe(`DROP TABLE IF EXISTS "${name}"`);
6757
+ dropped++;
6758
+ }
6759
+ }
6760
+ return dropped;
6761
+ }
6762
+ return {
6763
+ log,
6764
+ router,
6765
+ migrate: () => migrate6(pg, tableName),
6766
+ clean,
6767
+ close: () => pg.close()
6768
+ };
6769
+ }
6427
6770
  export {
6428
6771
  Router,
6429
6772
  TsxContext,
@@ -6441,6 +6784,7 @@ export {
6441
6784
  health,
6442
6785
  i18n,
6443
6786
  loadEnv,
6787
+ logdb,
6444
6788
  logger,
6445
6789
  mailer,
6446
6790
  messager,