@corsair-dev/studio 0.1.1 → 0.1.3

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.
@@ -341,6 +341,367 @@ var exchangeOAuth = async (ctx) => {
341
341
  return { ok: true };
342
342
  };
343
343
 
344
+ // src/server/handlers/chat.ts
345
+ import { buildCorsairToolDefs } from "@corsair-dev/mcp";
346
+ import { streamText, tool } from "ai";
347
+ import { z } from "zod";
348
+
349
+ // src/server/chat-store.ts
350
+ import Database from "better-sqlite3";
351
+ import { Kysely, SqliteDialect } from "kysely";
352
+ var sqlite = new Database(":memory:");
353
+ var db = new Kysely({
354
+ dialect: new SqliteDialect({ database: sqlite })
355
+ });
356
+ var schemaReady = initSchema();
357
+ async function initSchema() {
358
+ await db.schema.createTable("chats").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("title", "text", (col) => col.notNull()).addColumn("created_at", "integer", (col) => col.notNull()).execute();
359
+ await db.schema.createTable("chat_messages").ifNotExists().addColumn("id", "text", (col) => col.primaryKey()).addColumn("chat_id", "text", (col) => col.notNull()).addColumn("role", "text", (col) => col.notNull()).addColumn("blocks", "text", (col) => col.notNull()).addColumn("error", "text").addColumn("seq", "integer", (col) => col.notNull()).execute();
360
+ }
361
+ var _seq = 0;
362
+ function getTextFromBlocks(blocks) {
363
+ return blocks.filter(
364
+ (block) => block.type === "text"
365
+ ).map((block) => block.content).join("");
366
+ }
367
+ async function createChat() {
368
+ await schemaReady;
369
+ const chat = {
370
+ id: crypto.randomUUID(),
371
+ title: "New chat",
372
+ created_at: Date.now()
373
+ };
374
+ await db.insertInto("chats").values(chat).execute();
375
+ return chat;
376
+ }
377
+ async function listChats() {
378
+ await schemaReady;
379
+ return db.selectFrom("chats").select(["id", "title", "created_at"]).orderBy("created_at", "desc").execute();
380
+ }
381
+ async function chatExists(chatId) {
382
+ await schemaReady;
383
+ const row = await db.selectFrom("chats").select("id").where("id", "=", chatId).executeTakeFirst();
384
+ return !!row;
385
+ }
386
+ async function getMessages(chatId) {
387
+ await schemaReady;
388
+ const rows = await db.selectFrom("chat_messages").select(["id", "chat_id", "role", "blocks", "error"]).where("chat_id", "=", chatId).orderBy("seq", "asc").execute();
389
+ return rows.map((row) => ({
390
+ id: row.id,
391
+ chat_id: row.chat_id,
392
+ role: row.role,
393
+ blocks: JSON.parse(row.blocks),
394
+ error: row.error
395
+ }));
396
+ }
397
+ async function appendMessage(chatId, id, role, blocks, error) {
398
+ await schemaReady;
399
+ await db.insertInto("chat_messages").values({
400
+ id,
401
+ chat_id: chatId,
402
+ role,
403
+ blocks: JSON.stringify(blocks),
404
+ error: error ?? null,
405
+ seq: ++_seq
406
+ }).execute();
407
+ if (role === "user") {
408
+ const row = await db.selectFrom("chats").select("title").where("id", "=", chatId).executeTakeFirst();
409
+ if (row?.title === "New chat") {
410
+ const text = getTextFromBlocks(blocks);
411
+ const newTitle = text.slice(0, 60).trim();
412
+ if (newTitle) {
413
+ await db.updateTable("chats").set({ title: newTitle }).where("id", "=", chatId).execute();
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ // src/server/handlers/chat.ts
420
+ function isTextBlock(block) {
421
+ return block.type === "text";
422
+ }
423
+ function isCompletedToolBlock(block) {
424
+ return block.type === "tool" && block.result !== void 0;
425
+ }
426
+ function isPendingToolBlock(block) {
427
+ return block.type === "tool" && block.result === void 0;
428
+ }
429
+ function joinTextBlocks(blocks) {
430
+ return blocks.map((block) => block.content).join("");
431
+ }
432
+ function toToolArgs(args) {
433
+ if (args && typeof args === "object") {
434
+ const toolArgs = {};
435
+ for (const [key, value] of Object.entries(args)) {
436
+ toolArgs[key] = value;
437
+ }
438
+ return toolArgs;
439
+ }
440
+ return {};
441
+ }
442
+ function makeTextPart(block) {
443
+ return { type: "text", text: block.content };
444
+ }
445
+ function makeToolCallPart(toolCallId, block) {
446
+ return {
447
+ type: "tool-call",
448
+ toolCallId,
449
+ toolName: block.name,
450
+ args: toToolArgs(block.args)
451
+ };
452
+ }
453
+ function makeToolResultPart(toolCallId, block) {
454
+ return {
455
+ type: "tool-result",
456
+ toolCallId,
457
+ toolName: block.name,
458
+ result: block.result
459
+ };
460
+ }
461
+ function findPendingToolBlockIndex(blocks, toolName) {
462
+ return blocks.findLastIndex(
463
+ (block) => isPendingToolBlock(block) && block.name === toolName
464
+ );
465
+ }
466
+ function toAiMessages(stored) {
467
+ const result = [];
468
+ for (const msg of stored) {
469
+ const textBlocks = msg.blocks.filter(isTextBlock);
470
+ const text = joinTextBlocks(textBlocks);
471
+ if (msg.role === "user") {
472
+ result.push({ role: "user", content: text });
473
+ continue;
474
+ }
475
+ const toolBlocks = msg.blocks.filter(isCompletedToolBlock);
476
+ if (toolBlocks.length === 0) {
477
+ result.push({ role: "assistant", content: text });
478
+ continue;
479
+ }
480
+ const toolCallIds = toolBlocks.map(() => crypto.randomUUID());
481
+ const assistantParts = textBlocks.map(makeTextPart);
482
+ const toolParts = [];
483
+ for (const [index, block] of toolBlocks.entries()) {
484
+ const toolCallId = toolCallIds[index];
485
+ if (!toolCallId) {
486
+ continue;
487
+ }
488
+ assistantParts.push(makeToolCallPart(toolCallId, block));
489
+ toolParts.push(makeToolResultPart(toolCallId, block));
490
+ }
491
+ result.push({ role: "assistant", content: assistantParts });
492
+ result.push({ role: "tool", content: toolParts });
493
+ }
494
+ return result;
495
+ }
496
+ function buildAiTools(corsairClient) {
497
+ const defs = buildCorsairToolDefs({ corsair: corsairClient, setup: false });
498
+ const tools = {};
499
+ for (const def of defs) {
500
+ tools[def.name] = tool({
501
+ description: def.description,
502
+ parameters: z.object(def.shape),
503
+ execute: async (args) => {
504
+ const result = await def.handler(args);
505
+ const texts = result.content.filter((c) => c.type === "text");
506
+ if (result.isError) {
507
+ throw new Error(texts.map((c) => c.text).join("\n"));
508
+ }
509
+ const text = texts[0]?.text ?? "";
510
+ try {
511
+ return JSON.parse(text);
512
+ } catch {
513
+ return text;
514
+ }
515
+ }
516
+ });
517
+ }
518
+ return tools;
519
+ }
520
+ async function resolveModel() {
521
+ const model = process.env.CORSAIR_CHAT_MODEL;
522
+ console.log(
523
+ "[corsair:chat] resolving model \u2014 OPENAI_API_KEY:",
524
+ !!process.env.OPENAI_API_KEY,
525
+ "| ANTHROPIC_API_KEY:",
526
+ !!process.env.ANTHROPIC_API_KEY,
527
+ "| CORSAIR_CHAT_MODEL:",
528
+ model ?? "(default)"
529
+ );
530
+ if (process.env.OPENAI_API_KEY) {
531
+ const { openai } = await import("@ai-sdk/openai");
532
+ console.log("[corsair:chat] using openai:", model ?? "gpt-4o-mini");
533
+ return openai(model ?? "gpt-4o-mini");
534
+ }
535
+ if (process.env.ANTHROPIC_API_KEY) {
536
+ const { anthropic } = await import("@ai-sdk/anthropic");
537
+ console.log(
538
+ "[corsair:chat] using anthropic:",
539
+ model ?? "claude-sonnet-4-6"
540
+ );
541
+ return anthropic(model ?? "claude-sonnet-4-6");
542
+ }
543
+ if (process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
544
+ const { google } = await import("@ai-sdk/google");
545
+ return google(model ?? "gemini-2.0-flash");
546
+ }
547
+ if (process.env.GROQ_API_KEY) {
548
+ const { createGroq } = await import("@ai-sdk/groq");
549
+ return createGroq()(model ?? "llama-3.3-70b-versatile");
550
+ }
551
+ throw new Error(
552
+ "No AI provider configured. Set one of: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, or GROQ_API_KEY."
553
+ );
554
+ }
555
+ function errorMessage(err) {
556
+ return err instanceof Error ? err.message : String(err);
557
+ }
558
+ function isObject(value) {
559
+ return typeof value === "object" && value !== null;
560
+ }
561
+ function parseStreamPart(raw) {
562
+ if (!isObject(raw) || typeof raw.type !== "string") {
563
+ return null;
564
+ }
565
+ const textDelta = typeof raw.textDelta === "string" ? raw.textDelta : void 0;
566
+ const toolName = typeof raw.toolName === "string" ? raw.toolName : void 0;
567
+ return {
568
+ type: raw.type,
569
+ textDelta,
570
+ toolName,
571
+ args: raw.args,
572
+ result: raw.result
573
+ };
574
+ }
575
+ var chatHandler = async (ctx) => {
576
+ console.log("[corsair:chat] request received");
577
+ const body = await readJsonBody(ctx.req);
578
+ const tenant = body.tenant ? String(body.tenant) : void 0;
579
+ const chatId = body.chatId ? String(body.chatId) : void 0;
580
+ const newUserText = body.message ? String(body.message) : void 0;
581
+ const storedHistory = chatId && await chatExists(chatId) ? await getMessages(chatId) : [];
582
+ const messages = toAiMessages(storedHistory);
583
+ if (newUserText) {
584
+ messages.push({ role: "user", content: newUserText });
585
+ }
586
+ console.log(
587
+ "[corsair:chat] history:",
588
+ storedHistory.length,
589
+ "stored msgs \u2192",
590
+ messages.length,
591
+ "AI msgs | tenant:",
592
+ tenant ?? "(none)",
593
+ "| chatId:",
594
+ chatId ?? "(none)"
595
+ );
596
+ let model;
597
+ try {
598
+ model = await resolveModel();
599
+ } catch (err) {
600
+ const message = errorMessage(err);
601
+ console.error("[corsair:chat] model resolution failed:", message);
602
+ ctx.res.writeHead(400, { "content-type": "application/json" });
603
+ ctx.res.end(JSON.stringify({ error: message }));
604
+ return;
605
+ }
606
+ const handle = await ctx.getCorsair();
607
+ const client = handle.resolveClient(tenant);
608
+ const tools = buildAiTools(client);
609
+ const userMsgId = crypto.randomUUID();
610
+ if (chatId && await chatExists(chatId) && newUserText) {
611
+ try {
612
+ await appendMessage(chatId, userMsgId, "user", [
613
+ { type: "text", content: newUserText }
614
+ ]);
615
+ } catch (err) {
616
+ console.error("[corsair:chat] failed to save user message:", err);
617
+ }
618
+ }
619
+ ctx.res.writeHead(200, {
620
+ "content-type": "text/event-stream",
621
+ "cache-control": "no-cache",
622
+ connection: "keep-alive"
623
+ });
624
+ const send = (data) => {
625
+ ctx.res.write(`data: ${JSON.stringify(data)}
626
+
627
+ `);
628
+ };
629
+ const assistantBlocks = [];
630
+ console.log("[corsair:chat] starting stream");
631
+ try {
632
+ const result = streamText({
633
+ model,
634
+ system: "You are a helpful assistant with access to Corsair tools. Use the tools to answer questions about the user's integrations and data. Use list_operations to get the exact endpoint names.",
635
+ messages,
636
+ tools,
637
+ maxSteps: 10
638
+ });
639
+ for await (const raw of result.fullStream) {
640
+ const part = parseStreamPart(raw);
641
+ if (!part) {
642
+ continue;
643
+ }
644
+ if (part.type === "text-delta") {
645
+ send({ type: "text", text: part.textDelta });
646
+ const last = assistantBlocks[assistantBlocks.length - 1];
647
+ if (last?.type === "text") {
648
+ last.content += part.textDelta ?? "";
649
+ } else {
650
+ assistantBlocks.push({ type: "text", content: part.textDelta ?? "" });
651
+ }
652
+ } else if (part.type === "tool-call") {
653
+ send({ type: "tool-start", name: part.toolName, args: part.args });
654
+ assistantBlocks.push({
655
+ type: "tool",
656
+ name: part.toolName ?? "",
657
+ args: part.args
658
+ });
659
+ } else if (part.type === "tool-result") {
660
+ send({ type: "tool-end", name: part.toolName, result: part.result });
661
+ const idx = findPendingToolBlockIndex(assistantBlocks, part.toolName);
662
+ const block = idx >= 0 ? assistantBlocks[idx] : void 0;
663
+ if (block && isPendingToolBlock(block)) {
664
+ block.result = part.result;
665
+ }
666
+ } else if (part.type === "finish") {
667
+ send({ type: "done" });
668
+ }
669
+ }
670
+ } catch (err) {
671
+ const message = errorMessage(err);
672
+ console.error("[corsair:chat] stream error:", message);
673
+ send({ type: "error", message });
674
+ } finally {
675
+ if (chatId && await chatExists(chatId) && assistantBlocks.length > 0) {
676
+ try {
677
+ await appendMessage(
678
+ chatId,
679
+ crypto.randomUUID(),
680
+ "assistant",
681
+ assistantBlocks
682
+ );
683
+ } catch (err) {
684
+ console.error("[corsair:chat] failed to save assistant message:", err);
685
+ }
686
+ }
687
+ console.log("[corsair:chat] done");
688
+ ctx.res.end();
689
+ }
690
+ };
691
+
692
+ // src/server/handlers/chats.ts
693
+ var listChatsHandler = async () => {
694
+ return { chats: await listChats() };
695
+ };
696
+ var createChatHandler = async () => {
697
+ return { chat: await createChat() };
698
+ };
699
+ var getChatMessagesHandler = async (ctx) => {
700
+ const chatId = ctx.url.searchParams.get("chatId");
701
+ if (!chatId) throw new Error("chatId is required");
702
+ return { messages: await getMessages(chatId) };
703
+ };
704
+
344
705
  // src/server/handlers/credentials-internal.ts
345
706
  var BASE_FIELDS = {
346
707
  oauth_2: {
@@ -533,9 +894,9 @@ function getKyselyDb(database) {
533
894
  var listDbTables = async (ctx) => {
534
895
  const { internal } = await ctx.getCorsair();
535
896
  if (!internal.database) throw new Error("No database configured.");
536
- const db = getKyselyDb(internal.database);
537
- if (!db) throw new Error("Could not access kysely db handle.");
538
- const existing = await db.introspection.getTables();
897
+ const db2 = getKyselyDb(internal.database);
898
+ if (!db2) throw new Error("Could not access kysely db handle.");
899
+ const existing = await db2.introspection.getTables();
539
900
  const existingNames = new Set(existing.map((t) => t.name));
540
901
  return {
541
902
  core: CORE_TABLES.filter((t) => existingNames.has(t)),
@@ -551,14 +912,14 @@ var listDbRows = async (ctx) => {
551
912
  if (!table) throw new Error("Missing table.");
552
913
  const { internal } = await ctx.getCorsair();
553
914
  if (!internal.database) throw new Error("No database configured.");
554
- const db = getKyselyDb(internal.database);
555
- if (!db) throw new Error("Could not access kysely db handle.");
915
+ const db2 = getKyselyDb(internal.database);
916
+ if (!db2) throw new Error("Could not access kysely db handle.");
556
917
  let rows;
557
918
  try {
558
- const q = db.selectFrom(table).selectAll().limit(limit).offset(offset);
919
+ const q = db2.selectFrom(table).selectAll().limit(limit).offset(offset);
559
920
  rows = await q.orderBy("created_at", "desc").execute();
560
921
  } catch {
561
- rows = await db.selectFrom(table).selectAll().limit(limit).offset(offset).execute();
922
+ rows = await db2.selectFrom(table).selectAll().limit(limit).offset(offset).execute();
562
923
  }
563
924
  const safeRows = rows.map(redactSensitive);
564
925
  return { rows: safeRows, limit, offset };
@@ -580,9 +941,9 @@ var queryEntityData = async (ctx) => {
580
941
  if (!entity) throw new Error("Missing entity.");
581
942
  const { internal } = await ctx.getCorsair();
582
943
  if (!internal.database) throw new Error("No database configured.");
583
- const db = getKyselyDb(internal.database);
584
- if (!db) throw new Error("Could not access kysely db handle.");
585
- const base = db.selectFrom("corsair_entities as e").innerJoin("corsair_accounts as a", "a.id", "e.account_id").innerJoin("corsair_integrations as i", "i.id", "a.integration_id").where("a.tenant_id", "=", tenant).where("i.name", "=", integration).where("e.entity_type", "=", entity);
944
+ const db2 = getKyselyDb(internal.database);
945
+ if (!db2) throw new Error("Could not access kysely db handle.");
946
+ const base = db2.selectFrom("corsair_entities as e").innerJoin("corsair_accounts as a", "a.id", "e.account_id").innerJoin("corsair_integrations as i", "i.id", "a.integration_id").where("a.tenant_id", "=", tenant).where("i.name", "=", integration).where("e.entity_type", "=", entity);
586
947
  let rowsRaw = [];
587
948
  let hasMore = false;
588
949
  let total = 0;
@@ -682,14 +1043,14 @@ function collectPrimitiveValues(value) {
682
1043
  var listPermissions = async (ctx) => {
683
1044
  const { internal } = await ctx.getCorsair();
684
1045
  if (!internal.database) throw new Error("No database configured.");
685
- const db = getKyselyDb(internal.database);
686
- if (!db) throw new Error("Could not access kysely db handle.");
1046
+ const db2 = getKyselyDb(internal.database);
1047
+ if (!db2) throw new Error("Could not access kysely db handle.");
687
1048
  const limit = Math.min(
688
1049
  Math.max(Number(ctx.url.searchParams.get("limit") ?? 100), 1),
689
1050
  500
690
1051
  );
691
1052
  try {
692
- const rows = await db.selectFrom("corsair_permissions").selectAll().limit(limit).offset(0).orderBy("created_at", "desc").execute();
1053
+ const rows = await db2.selectFrom("corsair_permissions").selectAll().limit(limit).offset(0).orderBy("created_at", "desc").execute();
693
1054
  return { rows };
694
1055
  } catch (err) {
695
1056
  return { rows: [], note: err.message };
@@ -697,6 +1058,10 @@ var listPermissions = async (ctx) => {
697
1058
  };
698
1059
 
699
1060
  // src/server/handlers/operations.ts
1061
+ import {
1062
+ getSchema as getCorsairSchema,
1063
+ listOperations as listCorsairOperations
1064
+ } from "corsair";
700
1065
  function navigateToEndpoint(client, path) {
701
1066
  const parts = path.split(".");
702
1067
  let current = client;
@@ -711,25 +1076,17 @@ var listOperations = async (ctx) => {
711
1076
  const plugin = body.plugin ? String(body.plugin) : void 0;
712
1077
  const type = body.type ? String(body.type) : void 0;
713
1078
  const { instance } = await ctx.getCorsair();
714
- const corsair = instance;
715
- if (typeof corsair.list_operations !== "function") {
716
- throw new Error("list_operations not available on this Corsair instance.");
717
- }
718
1079
  const opts = {};
719
1080
  if (plugin) opts.plugin = plugin;
720
1081
  if (type === "api" || type === "webhooks" || type === "db") opts.type = type;
721
- return corsair.list_operations(opts);
1082
+ return listCorsairOperations(instance, opts);
722
1083
  };
723
1084
  var schemaForOperation = async (ctx) => {
724
1085
  const body = await readJsonBody(ctx.req);
725
1086
  const path = String(body.path ?? "");
726
1087
  if (!path) throw new Error("Missing path.");
727
1088
  const { instance } = await ctx.getCorsair();
728
- const corsair = instance;
729
- if (typeof corsair.get_schema !== "function") {
730
- throw new Error("get_schema not available on this Corsair instance.");
731
- }
732
- const schema = corsair.get_schema(path);
1089
+ const schema = getCorsairSchema(instance, path);
733
1090
  return { schema };
734
1091
  };
735
1092
  var runOperation = async (ctx) => {
@@ -813,19 +1170,19 @@ var setupPlugin = async (ctx) => {
813
1170
  if (!hasAuthConfig(internal, pluginId)) {
814
1171
  throw new Error(`Plugin '${pluginId}' has no credential setup.`);
815
1172
  }
816
- const db = asDb(internal.database);
1173
+ const db2 = asDb(internal.database);
817
1174
  const now = /* @__PURE__ */ new Date();
818
- let integration = await db.selectFrom("corsair_integrations").selectAll().where("name", "=", pluginId).executeTakeFirst();
1175
+ let integration = await db2.selectFrom("corsair_integrations").selectAll().where("name", "=", pluginId).executeTakeFirst();
819
1176
  if (!integration) {
820
1177
  const id = randomUUID();
821
- await db.insertInto("corsair_integrations").values({
1178
+ await db2.insertInto("corsair_integrations").values({
822
1179
  id,
823
1180
  name: pluginId,
824
1181
  config: {},
825
1182
  created_at: now,
826
1183
  updated_at: now
827
1184
  }).execute();
828
- integration = await db.selectFrom("corsair_integrations").selectAll().where("id", "=", id).executeTakeFirst();
1185
+ integration = await db2.selectFrom("corsair_integrations").selectAll().where("id", "=", id).executeTakeFirst();
829
1186
  }
830
1187
  if (!integration) {
831
1188
  throw new Error(`Failed to create integration for '${pluginId}'.`);
@@ -834,9 +1191,9 @@ var setupPlugin = async (ctx) => {
834
1191
  if (typeof integrationId !== "string") {
835
1192
  throw new Error(`Invalid integration row for '${pluginId}'.`);
836
1193
  }
837
- let account = await db.selectFrom("corsair_accounts").selectAll().where("tenant_id", "=", tenantId).where("integration_id", "=", integrationId).executeTakeFirst();
1194
+ let account = await db2.selectFrom("corsair_accounts").selectAll().where("tenant_id", "=", tenantId).where("integration_id", "=", integrationId).executeTakeFirst();
838
1195
  if (!account) {
839
- await db.insertInto("corsair_accounts").values({
1196
+ await db2.insertInto("corsair_accounts").values({
840
1197
  id: randomUUID(),
841
1198
  tenant_id: tenantId,
842
1199
  integration_id: integrationId,
@@ -844,7 +1201,7 @@ var setupPlugin = async (ctx) => {
844
1201
  created_at: now,
845
1202
  updated_at: now
846
1203
  }).execute();
847
- account = await db.selectFrom("corsair_accounts").selectAll().where("tenant_id", "=", tenantId).where("integration_id", "=", integrationId).executeTakeFirst();
1204
+ account = await db2.selectFrom("corsair_accounts").selectAll().where("tenant_id", "=", tenantId).where("integration_id", "=", integrationId).executeTakeFirst();
848
1205
  }
849
1206
  const rootKeys = instance.keys ?? null;
850
1207
  const integrationNamespace = rootKeys?.[pluginId] ?? null;
@@ -952,8 +1309,8 @@ var listTenants = async (ctx) => {
952
1309
  return { tenants: ["default"] };
953
1310
  }
954
1311
  if (!internal.database) throw new Error("No database configured.");
955
- const db = asDb2(internal.database);
956
- const rows = await db.selectFrom("corsair_accounts").selectAll().execute();
1312
+ const db2 = asDb2(internal.database);
1313
+ const rows = await db2.selectFrom("corsair_accounts").selectAll().execute();
957
1314
  const ids = /* @__PURE__ */ new Set(["default"]);
958
1315
  for (const row of rows) {
959
1316
  const tenantId = row.tenant_id;
@@ -977,13 +1334,13 @@ var createTenant = async (ctx) => {
977
1334
  throw new Error("Multi-tenancy is not enabled for this Corsair instance.");
978
1335
  }
979
1336
  if (!internal.database) throw new Error("No database configured.");
980
- const db = asDb2(internal.database);
1337
+ const db2 = asDb2(internal.database);
981
1338
  const now = /* @__PURE__ */ new Date();
982
1339
  const authPluginIds = getAuthPluginIds(internal);
983
1340
  if (authPluginIds.length === 0) {
984
1341
  return { ok: true, created: false };
985
1342
  }
986
- const integrations = await db.selectFrom("corsair_integrations").selectAll().execute();
1343
+ const integrations = await db2.selectFrom("corsair_integrations").selectAll().execute();
987
1344
  const integrationByName = /* @__PURE__ */ new Map();
988
1345
  for (const row of integrations) {
989
1346
  const name = row.name;
@@ -999,10 +1356,10 @@ var createTenant = async (ctx) => {
999
1356
  created_at: now,
1000
1357
  updated_at: now
1001
1358
  };
1002
- await db.insertInto("corsair_integrations").values(row).execute();
1359
+ await db2.insertInto("corsair_integrations").values(row).execute();
1003
1360
  integrationByName.set(pluginId, row);
1004
1361
  }
1005
- const existingAccounts = await db.selectFrom("corsair_accounts").selectAll().where("tenant_id", "=", tenantId).execute();
1362
+ const existingAccounts = await db2.selectFrom("corsair_accounts").selectAll().where("tenant_id", "=", tenantId).execute();
1006
1363
  const accountByIntegrationId = /* @__PURE__ */ new Map();
1007
1364
  for (const row of existingAccounts) {
1008
1365
  const integrationId = row.integration_id;
@@ -1020,7 +1377,7 @@ var createTenant = async (ctx) => {
1020
1377
  if (!existing) {
1021
1378
  createdAny = true;
1022
1379
  pluginsMissingDek.add(pluginId);
1023
- await db.insertInto("corsair_accounts").values({
1380
+ await db2.insertInto("corsair_accounts").values({
1024
1381
  id: randomUUID2(),
1025
1382
  tenant_id: tenantId,
1026
1383
  integration_id: integrationId,
@@ -1070,7 +1427,11 @@ var routes = [
1070
1427
  { method: "GET", path: "/api/db/tables", handler: listDbTables },
1071
1428
  { method: "POST", path: "/api/db/rows", handler: listDbRows },
1072
1429
  { method: "POST", path: "/api/db/entities/query", handler: queryEntityData },
1073
- { method: "GET", path: "/api/db/permissions", handler: listPermissions }
1430
+ { method: "GET", path: "/api/db/permissions", handler: listPermissions },
1431
+ { method: "GET", path: "/api/chats", handler: listChatsHandler },
1432
+ { method: "POST", path: "/api/chats", handler: createChatHandler },
1433
+ { method: "GET", path: "/api/chats/messages", handler: getChatMessagesHandler },
1434
+ { method: "POST", path: "/api/chat", handler: chatHandler }
1074
1435
  ];
1075
1436
  async function handleApi(req, res, baseCtx) {
1076
1437
  const url = new URL(req.url ?? "/", "http://localhost");
@@ -0,0 +1,37 @@
1
+ export type StoredChat = {
2
+ id: string;
3
+ title: string;
4
+ created_at: number;
5
+ };
6
+ export type StoredMsgBlock = {
7
+ type: 'text';
8
+ content: string;
9
+ } | {
10
+ type: 'tool';
11
+ name: string;
12
+ args: unknown;
13
+ result?: unknown;
14
+ };
15
+ export type StoredMessage = {
16
+ id: string;
17
+ chat_id: string;
18
+ role: 'user' | 'assistant';
19
+ blocks: StoredMsgBlock[];
20
+ error: string | null;
21
+ };
22
+ export declare function createChat(): Promise<StoredChat>;
23
+ export declare function listChats(): Promise<{
24
+ id: string;
25
+ title: string;
26
+ created_at: number;
27
+ }[]>;
28
+ export declare function chatExists(chatId: string): Promise<boolean>;
29
+ export declare function getMessages(chatId: string): Promise<{
30
+ id: string;
31
+ chat_id: string;
32
+ role: "user" | "assistant";
33
+ blocks: StoredMsgBlock[];
34
+ error: string | null;
35
+ }[]>;
36
+ export declare function appendMessage(chatId: string, id: string, role: 'user' | 'assistant', blocks: StoredMsgBlock[], error?: string): Promise<void>;
37
+ //# sourceMappingURL=chat-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-store.d.ts","sourceRoot":"","sources":["../../../src/server/chat-store.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,aAAa,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAAC;AA4DF,wBAAsB,UAAU,wBAW/B;AAED,wBAAsB,SAAS;;;;KAQ9B;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,oBAS9C;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM;;;;YAcZ,cAAc,EAAE;;KAGnD;AAED,wBAAsB,aAAa,CAClC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,MAAM,EAAE,cAAc,EAAE,EACxB,KAAK,CAAC,EAAE,MAAM,iBAmCd"}
@@ -0,0 +1,3 @@
1
+ import type { HandlerFn } from '../types.js';
2
+ export declare const chatHandler: HandlerFn;
3
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../../src/server/handlers/chat.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA6N7C,eAAO,MAAM,WAAW,EAAE,SAgIzB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { HandlerFn } from '../types.js';
2
+ export declare const listChatsHandler: HandlerFn;
3
+ export declare const createChatHandler: HandlerFn;
4
+ export declare const getChatMessagesHandler: HandlerFn;
5
+ //# sourceMappingURL=chats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chats.d.ts","sourceRoot":"","sources":["../../../../src/server/handlers/chats.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,eAAO,MAAM,gBAAgB,EAAE,SAE9B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,SAE/B,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,SAIpC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../../src/server/handlers/operations.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAiB1C,eAAO,MAAM,cAAc,EAAE,SAc5B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,SAYhC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,SA6B1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,SA+BvB,CAAC"}
1
+ {"version":3,"file":"operations.d.ts","sourceRoot":"","sources":["../../../../src/server/handlers/operations.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAiB1C,eAAO,MAAM,cAAc,EAAE,SAa5B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,SAQhC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,SA6B1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,SA+BvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/server/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAmBjE,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,SAAS,CAAC;AAoCrD,wBAAsB,SAAS,CAC9B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,GAC9C,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAsB,YAAY,CACjC,GAAG,EAAE,eAAe,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAoBlC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/server/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAyBjE,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,SAAS,CAAC;AA0CrD,wBAAsB,SAAS,CAC9B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,GAC9C,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAsB,YAAY,CACjC,GAAG,EAAE,eAAe,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAoBlC"}