os-dpt 0.2.0 → 0.2.1

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.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/dpt.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Data Profile Tool</title>
8
- <script type="module" crossorigin src="/assets/index-4qMZFL9L.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-DweMyov5.css">
8
+ <script type="module" crossorigin src="/assets/index-BejCAhC3.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-BbfkzqpQ.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -9,7 +9,7 @@ for (const envPath of [".env", "../.env"]) {
9
9
  // server/index.ts
10
10
  import { pathToFileURL } from "node:url";
11
11
  import { serve } from "@hono/node-server";
12
- import { Hono as Hono10 } from "hono";
12
+ import { Hono as Hono11 } from "hono";
13
13
  import { HTTPException as HTTPException4 } from "hono/http-exception";
14
14
 
15
15
  // server/api/ai-providers.ts
@@ -221,6 +221,7 @@ var CONTEXT_DOC_NAMES = CONTEXT_DOCS.map((d) => d.name);
221
221
  // server/workspace.ts
222
222
  var exec = promisify(execFile);
223
223
  var WORKSHEETS_DIR = "worksheets";
224
+ var DASHBOARDS_DIR = "dashboards";
224
225
  var CONTEXT_DIR = "context";
225
226
  var CONTEXT_BY_SOURCE = "by-source";
226
227
  var OSDPT_DIR = ".os-dpt";
@@ -244,6 +245,7 @@ function resolveWorkspace(argv) {
244
245
  async function initWorkspace(argv = process.argv.slice(2)) {
245
246
  const root = resolveWorkspace(argv);
246
247
  await fs4.mkdir(path4.join(root, WORKSHEETS_DIR), { recursive: true });
248
+ await fs4.mkdir(path4.join(root, DASHBOARDS_DIR), { recursive: true });
247
249
  await fs4.mkdir(path4.join(root, DRAFTS_DIR), { recursive: true });
248
250
  await fs4.mkdir(path4.join(root, CHATS_DIR), { recursive: true });
249
251
  await fs4.mkdir(path4.join(root, CONTEXT_DIR), { recursive: true });
@@ -300,6 +302,8 @@ function workspacePath(...segments) {
300
302
  var paths = {
301
303
  worksheets: () => workspacePath(WORKSHEETS_DIR),
302
304
  worksheet: (slug) => workspacePath(WORKSHEETS_DIR, `${slug}.sql`),
305
+ dashboards: () => workspacePath(DASHBOARDS_DIR),
306
+ dashboard: (slug) => workspacePath(DASHBOARDS_DIR, `${slug}.json`),
303
307
  drafts: () => workspacePath(DRAFTS_DIR),
304
308
  draft: (slug) => workspacePath(DRAFTS_DIR, `${slug}.sql`),
305
309
  schemas: () => workspacePath(SCHEMAS_DIR),
@@ -634,6 +638,7 @@ async function introspect(pool) {
634
638
 
635
639
  // server/db/postgres.ts
636
640
  import pg from "pg";
641
+ pg.types.setTypeParser(pg.types.builtins.DATE, (value) => value);
637
642
  function clientConfig(conn, password) {
638
643
  return {
639
644
  host: conn.host,
@@ -1183,9 +1188,9 @@ import { promises as fs9 } from "node:fs";
1183
1188
  import path8 from "node:path";
1184
1189
 
1185
1190
  // server/lib/slug.ts
1186
- function slugify(name) {
1191
+ function slugify(name, fallback = "worksheet") {
1187
1192
  const base = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1188
- return base || "worksheet";
1193
+ return base || fallback;
1189
1194
  }
1190
1195
  function dedupeSlug(slug, existing) {
1191
1196
  if (!existing.has(slug)) return slug;
@@ -1969,6 +1974,14 @@ import { Hono as Hono8 } from "hono";
1969
1974
  import { streamSSE } from "hono/streaming";
1970
1975
 
1971
1976
  // shared/agent.ts
1977
+ var CHART_TYPES = [
1978
+ "bar",
1979
+ "stacked-bar",
1980
+ "line",
1981
+ "area",
1982
+ "pie",
1983
+ "funnel"
1984
+ ];
1972
1985
  function emptyTotals() {
1973
1986
  return {
1974
1987
  inputTokens: 0,
@@ -2080,16 +2093,17 @@ function buildSystemPrompt(session) {
2080
2093
  }
2081
2094
  function buildQuickEditPrompt(session) {
2082
2095
  const { worksheetSlug, connectionId } = session.meta;
2096
+ const noun = worksheetSlug ? "worksheet" : "query";
2083
2097
  const lines = [
2084
- "You are os-dpt's inline SQL-editing agent. The user prompts you from a small floating box inside their SQL editor. Your ONLY deliverable is SQL staged into their worksheet via write_sql \u2014 the user never reads chat output in this mode, they watch the editor update.",
2098
+ worksheetSlug ? "You are os-dpt's inline SQL-editing agent. The user prompts you from a small floating box inside their SQL editor. Your ONLY deliverable is SQL staged into their worksheet via write_sql \u2014 the user never reads chat output in this mode, they watch the editor update." : "You are os-dpt's inline SQL-editing agent. The user is editing a standalone SQL query (e.g. a saved chart's source query). Your ONLY deliverable is SQL staged back into their editor via write_sql \u2014 the user never reads chat output in this mode, they watch the editor update.",
2085
2099
  "",
2086
2100
  "Output rules (hard requirements):",
2087
2101
  "- Emit NO prose. No preamble, no explanation, no closing summary \u2014 no text blocks at all.",
2088
2102
  "- Anything worth flagging (an assumption you made, a caveat, a data-quality quirk) goes in a SQL comment inside the query itself.",
2089
- "- Always finish by calling write_sql with the COMPLETE worksheet contents (drafts are overwrites, not patches), then end the turn.",
2103
+ `- Always finish by calling write_sql with the COMPLETE ${noun} contents (writes are overwrites, not patches), then end the turn.`,
2090
2104
  "",
2091
2105
  "Method:",
2092
- "- Each user message ends with the worksheet's current contents. Treat the request as an edit to that SQL unless it clearly asks for something new; preserve parts of the worksheet the request doesn't touch.",
2106
+ `- Each user message ends with the ${noun}'s current contents. Treat the request as an edit to that SQL unless it clearly asks for something new; preserve parts of the ${noun} the request doesn't touch.`,
2093
2107
  "- Check get_context first when the request depends on schema, business terms, or conventions you have not verified this session; call get_schema when tables or columns are uncertain. Do not guess names.",
2094
2108
  "- Verify your SQL with run_sql before staging it. If it errors, fix and re-run until it works. Treat verification as read-only \u2014 SELECT only; never run DDL or DML in this mode.",
2095
2109
  "- If no connection is bound or verification is impossible, stage your best SQL with a leading '-- not verified' comment instead of stopping.",
@@ -2531,7 +2545,7 @@ var askUserQuestionTool = {
2531
2545
  // server/agent/tools/write_sql.ts
2532
2546
  var writeSqlTool = {
2533
2547
  name: "write_sql",
2534
- description: "Stage SQL into the editor. Writes to the worksheet's draft (autosave channel), so the user sees the SQL appear in the editor without losing their saved version until they hit Cmd+S. Defaults to the chat's bound worksheet. Pass the COMPLETE worksheet contents \u2014 drafts are overwrites, not patches.",
2548
+ description: "Stage SQL into the editor. Writes to the worksheet's draft (autosave channel), so the user sees the SQL appear in the editor without losing their saved version until they hit Cmd+S. Defaults to the chat's bound worksheet. Pass the COMPLETE worksheet contents \u2014 drafts are overwrites, not patches. When no worksheet is bound, the SQL streams straight back to the editor that launched this session.",
2535
2549
  input_schema: {
2536
2550
  type: "object",
2537
2551
  required: ["sql"],
@@ -2557,6 +2571,14 @@ var writeSqlTool = {
2557
2571
  }
2558
2572
  const slug = input.worksheet_slug ?? ctx.session.meta.worksheetSlug;
2559
2573
  if (!slug) {
2574
+ if (ctx.session.meta.mode === "quick-edit") {
2575
+ return {
2576
+ toolResult: `Staged ${input.sql.length} bytes back to the editor.`,
2577
+ isError: false,
2578
+ uiSummary: `Staged ${input.sql.length} chars into the editor`,
2579
+ events: [{ type: "sql_written", worksheetSlug: null, sql: input.sql }]
2580
+ };
2581
+ }
2560
2582
  return {
2561
2583
  toolResult: "No worksheet bound. Ask the user which worksheet to write to, or set worksheet_slug.",
2562
2584
  isError: true,
@@ -2702,7 +2724,6 @@ var runSqlTool = {
2702
2724
  };
2703
2725
 
2704
2726
  // server/agent/tools/render_chart.ts
2705
- var CHART_TYPES = ["bar", "stacked-bar", "line", "area", "pie", "funnel"];
2706
2727
  var MAX_ROWS2 = 500;
2707
2728
  var MAX_SERIES = 8;
2708
2729
  function parseSeries(raw) {
@@ -2920,7 +2941,10 @@ async function runTurn(opts) {
2920
2941
  }
2921
2942
  const system = buildSystemPrompt(session);
2922
2943
  const tools = anthropicToolDefs({
2923
- worksheetBound: !!session.meta.worksheetSlug,
2944
+ // Quick-edit always keeps write_sql — it's the mode's only deliverable.
2945
+ // Without a worksheet bound the tool skips the draft write and streams
2946
+ // the SQL back to the launching editor via the sql_written event.
2947
+ worksheetBound: !!session.meta.worksheetSlug || session.meta.mode === "quick-edit",
2924
2948
  mode: session.meta.mode
2925
2949
  });
2926
2950
  for (let step = 0; step < MAX_STEPS; step += 1) {
@@ -3396,10 +3420,201 @@ app7.put("/:name", async (c) => {
3396
3420
  });
3397
3421
  var context_default = app7;
3398
3422
 
3423
+ // server/api/dashboards.ts
3424
+ import { Hono as Hono10 } from "hono";
3425
+ import { createHash as createHash2, randomUUID } from "node:crypto";
3426
+ import { promises as fs18 } from "node:fs";
3427
+ var app8 = new Hono10();
3428
+ var MAX_NAME_LEN2 = 80;
3429
+ var MAX_TITLE_LEN = 120;
3430
+ async function readDashboard(slug) {
3431
+ try {
3432
+ const raw = await fs18.readFile(paths.dashboard(slug), "utf8");
3433
+ const parsed = JSON.parse(raw);
3434
+ return normalize2(slug, parsed);
3435
+ } catch {
3436
+ return null;
3437
+ }
3438
+ }
3439
+ function normalize2(slug, d) {
3440
+ return {
3441
+ slug,
3442
+ name: typeof d.name === "string" && d.name.trim() !== "" ? d.name : slug,
3443
+ createdAt: d.createdAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
3444
+ updatedAt: d.updatedAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
3445
+ charts: (Array.isArray(d.charts) ? d.charts : []).flatMap((chart, i) => {
3446
+ const parsed = parseChart(chart);
3447
+ if (typeof parsed === "string") return [];
3448
+ const c = chart;
3449
+ return [
3450
+ {
3451
+ ...parsed,
3452
+ id: typeof c.id === "string" && c.id !== "" ? c.id : deriveChartId(slug, i, chart),
3453
+ position: typeof c.position === "number" ? c.position : i
3454
+ }
3455
+ ];
3456
+ })
3457
+ };
3458
+ }
3459
+ function deriveChartId(slug, index, chart) {
3460
+ const hash = createHash2("sha256").update(`${slug}
3461
+ ${index}
3462
+ ${JSON.stringify(chart)}`).digest("hex");
3463
+ return `gen-${hash.slice(0, 12)}`;
3464
+ }
3465
+ async function persist2(dashboard) {
3466
+ dashboard.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3467
+ await writeAtomic(paths.dashboard(dashboard.slug), JSON.stringify(dashboard, null, 2));
3468
+ return dashboard;
3469
+ }
3470
+ async function listSlugs() {
3471
+ const entries = await fs18.readdir(paths.dashboards()).catch(() => []);
3472
+ return entries.filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5));
3473
+ }
3474
+ var mutationTails = /* @__PURE__ */ new Map();
3475
+ function withDashboardLock(slug, fn) {
3476
+ const prev = mutationTails.get(slug) ?? Promise.resolve();
3477
+ const result = prev.then(fn);
3478
+ const tail = result.then(
3479
+ () => void 0,
3480
+ () => void 0
3481
+ );
3482
+ mutationTails.set(slug, tail);
3483
+ void tail.then(() => {
3484
+ if (mutationTails.get(slug) === tail) mutationTails.delete(slug);
3485
+ });
3486
+ return result;
3487
+ }
3488
+ function parseChart(raw) {
3489
+ if (!raw || typeof raw !== "object") return "chart must be an object";
3490
+ const c = raw;
3491
+ if (!CHART_TYPES.includes(c.type)) {
3492
+ return `type must be one of: ${CHART_TYPES.join(", ")}`;
3493
+ }
3494
+ if (typeof c.x !== "string" || c.x.trim() === "") return "x must be a non-empty string";
3495
+ if (typeof c.sql !== "string" || c.sql.trim() === "") return "sql must be a non-empty string";
3496
+ if (!Array.isArray(c.series) || c.series.length === 0) {
3497
+ return "series must be a non-empty array of { key, label? }";
3498
+ }
3499
+ const series = [];
3500
+ for (const item of c.series) {
3501
+ if (!item || typeof item !== "object" || typeof item.key !== "string") {
3502
+ return "each series must be an object with a string `key`";
3503
+ }
3504
+ const s = item;
3505
+ series.push(typeof s.label === "string" && s.label !== "" ? { key: s.key, label: s.label } : { key: s.key });
3506
+ }
3507
+ const title = typeof c.title === "string" && c.title.trim() !== "" ? c.title.trim() : "Untitled chart";
3508
+ const connectionId = typeof c.connectionId === "string" && c.connectionId !== "" ? c.connectionId : null;
3509
+ return {
3510
+ title: title.length > MAX_TITLE_LEN ? title.slice(0, MAX_TITLE_LEN) : title,
3511
+ type: c.type,
3512
+ x: c.x,
3513
+ series,
3514
+ sql: c.sql,
3515
+ connectionId
3516
+ };
3517
+ }
3518
+ app8.get("/", async (c) => {
3519
+ const slugs = await listSlugs();
3520
+ const metas = [];
3521
+ for (const slug of slugs) {
3522
+ const d = await readDashboard(slug);
3523
+ if (!d) continue;
3524
+ metas.push({ slug, name: d.name, updatedAt: d.updatedAt, chartCount: d.charts.length });
3525
+ }
3526
+ metas.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
3527
+ return c.json({ dashboards: metas });
3528
+ });
3529
+ app8.post("/", async (c) => {
3530
+ const body = await c.req.json().catch(() => ({}));
3531
+ const raw = (body.name ?? "").trim();
3532
+ const name = raw === "" ? "Dashboard" : raw.length > MAX_NAME_LEN2 ? raw.slice(0, MAX_NAME_LEN2) : raw;
3533
+ const existing = new Set(await listSlugs());
3534
+ const slug = dedupeSlug(slugify(name, "dashboard"), existing);
3535
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3536
+ const dashboard = { slug, name, createdAt: now, updatedAt: now, charts: [] };
3537
+ await writeAtomic(paths.dashboard(slug), JSON.stringify(dashboard, null, 2));
3538
+ return c.json(dashboard, 201);
3539
+ });
3540
+ app8.get("/:slug", async (c) => {
3541
+ const slug = c.req.param("slug");
3542
+ assertSafeSlug(slug);
3543
+ const dashboard = await readDashboard(slug);
3544
+ if (!dashboard) return c.json({ error: "not_found" }, 404);
3545
+ return c.json(dashboard);
3546
+ });
3547
+ app8.patch("/:slug", async (c) => {
3548
+ const slug = c.req.param("slug");
3549
+ assertSafeSlug(slug);
3550
+ const body = await c.req.json().catch(() => ({}));
3551
+ const raw = (body.name ?? "").trim();
3552
+ if (!raw) return c.json({ error: "empty_name" }, 400);
3553
+ return withDashboardLock(slug, async () => {
3554
+ const dashboard = await readDashboard(slug);
3555
+ if (!dashboard) return c.json({ error: "not_found" }, 404);
3556
+ dashboard.name = raw.length > MAX_NAME_LEN2 ? raw.slice(0, MAX_NAME_LEN2) : raw;
3557
+ return c.json(await persist2(dashboard));
3558
+ });
3559
+ });
3560
+ app8.delete("/:slug", async (c) => {
3561
+ const slug = c.req.param("slug");
3562
+ assertSafeSlug(slug);
3563
+ return withDashboardLock(slug, async () => {
3564
+ await fs18.rm(paths.dashboard(slug), { force: true });
3565
+ return c.json({ ok: true });
3566
+ });
3567
+ });
3568
+ app8.post("/:slug/charts", async (c) => {
3569
+ const slug = c.req.param("slug");
3570
+ assertSafeSlug(slug);
3571
+ const body = await c.req.json().catch(() => null);
3572
+ const parsed = parseChart(body);
3573
+ if (typeof parsed === "string") return c.json({ error: parsed }, 400);
3574
+ return withDashboardLock(slug, async () => {
3575
+ const dashboard = await readDashboard(slug);
3576
+ if (!dashboard) return c.json({ error: "not_found" }, 404);
3577
+ const position = dashboard.charts.reduce((max, ch) => Math.max(max, ch.position), -1) + 1;
3578
+ const chart = { ...parsed, id: randomUUID(), position };
3579
+ dashboard.charts.push(chart);
3580
+ return c.json(await persist2(dashboard));
3581
+ });
3582
+ });
3583
+ app8.put("/:slug/charts/:chartId", async (c) => {
3584
+ const slug = c.req.param("slug");
3585
+ assertSafeSlug(slug);
3586
+ const chartId = c.req.param("chartId");
3587
+ const body = await c.req.json().catch(() => ({}));
3588
+ return withDashboardLock(slug, async () => {
3589
+ const dashboard = await readDashboard(slug);
3590
+ if (!dashboard) return c.json({ error: "not_found" }, 404);
3591
+ const chart = dashboard.charts.find((ch) => ch.id === chartId);
3592
+ if (!chart) return c.json({ error: "not_found" }, 404);
3593
+ const parsed = parseChart({ ...chart, ...body });
3594
+ if (typeof parsed === "string") return c.json({ error: parsed }, 400);
3595
+ Object.assign(chart, parsed);
3596
+ return c.json(await persist2(dashboard));
3597
+ });
3598
+ });
3599
+ app8.delete("/:slug/charts/:chartId", async (c) => {
3600
+ const slug = c.req.param("slug");
3601
+ assertSafeSlug(slug);
3602
+ const chartId = c.req.param("chartId");
3603
+ return withDashboardLock(slug, async () => {
3604
+ const dashboard = await readDashboard(slug);
3605
+ if (!dashboard) return c.json({ error: "not_found" }, 404);
3606
+ const before = dashboard.charts.length;
3607
+ dashboard.charts = dashboard.charts.filter((ch) => ch.id !== chartId);
3608
+ if (dashboard.charts.length === before) return c.json({ error: "not_found" }, 404);
3609
+ return c.json(await persist2(dashboard));
3610
+ });
3611
+ });
3612
+ var dashboards_default = app8;
3613
+
3399
3614
  // server/history/watcher.ts
3400
- import { promises as fs18, watch } from "node:fs";
3615
+ import { promises as fs19, watch } from "node:fs";
3401
3616
  import path10 from "node:path";
3402
- import { createHash as createHash2 } from "node:crypto";
3617
+ import { createHash as createHash3 } from "node:crypto";
3403
3618
  var SLUG_RE2 = /^[a-z0-9][a-z0-9_-]*$/i;
3404
3619
  var watcher = null;
3405
3620
  function startWorksheetsWatcher() {
@@ -3418,11 +3633,11 @@ function startWorksheetsWatcher() {
3418
3633
  async function handleEvent(slug, filePath2) {
3419
3634
  let content;
3420
3635
  try {
3421
- content = await fs18.readFile(filePath2, "utf8");
3636
+ content = await fs19.readFile(filePath2, "utf8");
3422
3637
  } catch {
3423
3638
  return;
3424
3639
  }
3425
- const sha = createHash2("sha256").update(content, "utf8").digest("hex");
3640
+ const sha = createHash3("sha256").update(content, "utf8").digest("hex");
3426
3641
  if (isRecentWrite(slug, sha)) return;
3427
3642
  recordHistory({ worksheet: slug, source: "external", content });
3428
3643
  }
@@ -3464,10 +3679,10 @@ function contentType(file) {
3464
3679
  function cacheControl(urlPath) {
3465
3680
  return urlPath.startsWith("/assets/") ? "public, max-age=31536000, immutable" : "no-cache";
3466
3681
  }
3467
- function serveClient(app8, clientDir = defaultClientDir()) {
3682
+ function serveClient(app9, clientDir = defaultClientDir()) {
3468
3683
  if (!existsSync(path11.join(clientDir, "index.html"))) return false;
3469
3684
  const indexHtml = path11.join(clientDir, "index.html");
3470
- app8.get("*", async (c) => {
3685
+ app9.get("*", async (c) => {
3471
3686
  if (c.req.path.startsWith("/api")) return c.notFound();
3472
3687
  const rel = decodeURIComponent(c.req.path).replace(/^\/+/, "");
3473
3688
  const candidate = path11.resolve(clientDir, rel);
@@ -3511,11 +3726,11 @@ function openBrowser(url) {
3511
3726
 
3512
3727
  // server/index.ts
3513
3728
  var DEFAULT_PORT = 3756;
3514
- function listen(app8, port) {
3729
+ function listen(app9, port) {
3515
3730
  return new Promise((resolve, reject) => {
3516
3731
  let retried = false;
3517
3732
  const attempt = (p) => {
3518
- const server = serve({ fetch: app8.fetch, port: p, hostname: "127.0.0.1" });
3733
+ const server = serve({ fetch: app9.fetch, port: p, hostname: "127.0.0.1" });
3519
3734
  server.once("listening", () => resolve(server));
3520
3735
  server.once("error", (err) => {
3521
3736
  if (err.code === "EADDRINUSE" && !retried) {
@@ -3535,25 +3750,26 @@ async function startServer(argv = process.argv.slice(2)) {
3535
3750
  openHistoryDb();
3536
3751
  startWorksheetsWatcher();
3537
3752
  void autoConnectAll(workspace);
3538
- const app8 = new Hono10();
3539
- app8.get("/api/health", (c) => c.json({ ok: true, workspace }));
3540
- app8.route("/api/connections", connectionsRouter(workspace));
3541
- app8.route("/api/ai-providers", aiProvidersRouter(workspace));
3542
- app8.route("/api/worksheets", worksheets_default);
3543
- app8.route("/api/drafts", drafts_default);
3544
- app8.route("/api/session", session_default);
3545
- app8.route("/api/schema", schema_default);
3546
- app8.route("/api/history", history_default);
3547
- app8.route("/api/agent", agent_default);
3548
- app8.route("/api/context", context_default);
3549
- app8.onError((err, c) => {
3753
+ const app9 = new Hono11();
3754
+ app9.get("/api/health", (c) => c.json({ ok: true, workspace }));
3755
+ app9.route("/api/connections", connectionsRouter(workspace));
3756
+ app9.route("/api/ai-providers", aiProvidersRouter(workspace));
3757
+ app9.route("/api/worksheets", worksheets_default);
3758
+ app9.route("/api/drafts", drafts_default);
3759
+ app9.route("/api/session", session_default);
3760
+ app9.route("/api/schema", schema_default);
3761
+ app9.route("/api/history", history_default);
3762
+ app9.route("/api/agent", agent_default);
3763
+ app9.route("/api/context", context_default);
3764
+ app9.route("/api/dashboards", dashboards_default);
3765
+ app9.onError((err, c) => {
3550
3766
  if (err instanceof HTTPException4) return err.getResponse();
3551
3767
  console.error("[os-dpt]", err);
3552
3768
  return c.json({ error: err.message }, 500);
3553
3769
  });
3554
- serveClient(app8);
3770
+ serveClient(app9);
3555
3771
  const preferredPort = Number(process.env.PORT ?? DEFAULT_PORT);
3556
- const server = await listen(app8, preferredPort);
3772
+ const server = await listen(app9, preferredPort);
3557
3773
  const addr = server.address();
3558
3774
  const port = typeof addr === "object" && addr ? addr.port : preferredPort;
3559
3775
  const url = `http://127.0.0.1:${port}`;