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.
- package/README.md +54 -23
- package/package.json +1 -1
- package/server/dist/client/assets/{highlighted-body-OFNGDK62-DVn0rkjL.js → highlighted-body-OFNGDK62-DVsDtUg7.js} +1 -1
- package/server/dist/client/assets/index-BbfkzqpQ.css +1 -0
- package/server/dist/client/assets/index-BejCAhC3.js +251 -0
- package/server/dist/client/index.html +2 -2
- package/server/dist/index.mjs +247 -31
- package/server/dist/client/assets/index-4qMZFL9L.js +0 -251
- package/server/dist/client/assets/index-DweMyov5.css +0 -1
|
@@ -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-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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>
|
package/server/dist/index.mjs
CHANGED
|
@@ -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
|
|
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 ||
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3615
|
+
import { promises as fs19, watch } from "node:fs";
|
|
3401
3616
|
import path10 from "node:path";
|
|
3402
|
-
import { createHash as
|
|
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
|
|
3636
|
+
content = await fs19.readFile(filePath2, "utf8");
|
|
3422
3637
|
} catch {
|
|
3423
3638
|
return;
|
|
3424
3639
|
}
|
|
3425
|
-
const sha =
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
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(
|
|
3770
|
+
serveClient(app9);
|
|
3555
3771
|
const preferredPort = Number(process.env.PORT ?? DEFAULT_PORT);
|
|
3556
|
-
const server = await listen(
|
|
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}`;
|