@deepagents/text2sql 0.13.1 → 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.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1060 -218
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/groundings/index.js +166 -154
- package/dist/lib/adapters/groundings/index.js.map +4 -4
- package/dist/lib/adapters/mysql/index.js +166 -154
- package/dist/lib/adapters/mysql/index.js.map +4 -4
- package/dist/lib/adapters/postgres/index.js +168 -155
- package/dist/lib/adapters/postgres/index.js.map +4 -4
- package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +22 -2
- package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/index.js +166 -154
- package/dist/lib/adapters/sqlite/index.js.map +4 -4
- package/dist/lib/adapters/sqlserver/index.js +166 -154
- package/dist/lib/adapters/sqlserver/index.js.map +4 -4
- package/dist/lib/agents/result-tools.d.ts +20 -23
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/fs/index.d.ts +4 -0
- package/dist/lib/fs/index.d.ts.map +1 -0
- package/dist/lib/fs/scoped-fs.d.ts +53 -0
- package/dist/lib/fs/scoped-fs.d.ts.map +1 -0
- package/dist/lib/fs/sqlite-fs.d.ts +66 -0
- package/dist/lib/fs/sqlite-fs.d.ts.map +1 -0
- package/dist/lib/fs/tracked-fs.d.ts +40 -0
- package/dist/lib/fs/tracked-fs.d.ts.map +1 -0
- package/dist/lib/sql.d.ts +3 -4
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +181 -181
- package/dist/lib/synthesis/index.js.map +4 -4
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -387,7 +387,7 @@ function getTablesWithRelated(allTables, relationships, filter) {
|
|
|
387
387
|
|
|
388
388
|
// packages/text2sql/src/lib/agents/developer.agent.ts
|
|
389
389
|
import { tool } from "ai";
|
|
390
|
-
import
|
|
390
|
+
import dedent2 from "dedent";
|
|
391
391
|
import z2 from "zod";
|
|
392
392
|
import { toState } from "@deepagents/agent";
|
|
393
393
|
|
|
@@ -405,6 +405,7 @@ import spawn2 from "nano-spawn";
|
|
|
405
405
|
import {
|
|
406
406
|
createBashTool
|
|
407
407
|
} from "bash-tool";
|
|
408
|
+
import dedent from "dedent";
|
|
408
409
|
import YAML from "yaml";
|
|
409
410
|
import { DatabaseSync } from "node:sqlite";
|
|
410
411
|
import { groq } from "@ai-sdk/groq";
|
|
@@ -626,7 +627,7 @@ function assistantText(content, options) {
|
|
|
626
627
|
parts: [{ type: "text", text: content }]
|
|
627
628
|
});
|
|
628
629
|
}
|
|
629
|
-
var LAZY_ID = Symbol("lazy-id");
|
|
630
|
+
var LAZY_ID = Symbol.for("@deepagents/context:lazy-id");
|
|
630
631
|
function isLazyFragment(fragment2) {
|
|
631
632
|
return LAZY_ID in fragment2;
|
|
632
633
|
}
|
|
@@ -1013,7 +1014,7 @@ var ContextEngine = class {
|
|
|
1013
1014
|
async #createBranchFrom(messageId, switchTo) {
|
|
1014
1015
|
const branches = await this.#store.listBranches(this.#chatId);
|
|
1015
1016
|
const samePrefix = branches.filter(
|
|
1016
|
-
(
|
|
1017
|
+
(it) => it.name === this.#branchName || it.name.startsWith(`${this.#branchName}-v`)
|
|
1017
1018
|
);
|
|
1018
1019
|
const newBranchName = `${this.#branchName}-v${samePrefix.length + 1}`;
|
|
1019
1020
|
const newBranch = {
|
|
@@ -1041,6 +1042,15 @@ var ContextEngine = class {
|
|
|
1041
1042
|
createdAt: newBranch.createdAt
|
|
1042
1043
|
};
|
|
1043
1044
|
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Rewind to a message without clearing pending messages.
|
|
1047
|
+
* Used internally when saving an update to an existing message.
|
|
1048
|
+
*/
|
|
1049
|
+
async #rewindForUpdate(messageId) {
|
|
1050
|
+
const pendingBackup = [...this.#pendingMessages];
|
|
1051
|
+
await this.rewind(messageId);
|
|
1052
|
+
this.#pendingMessages = pendingBackup;
|
|
1053
|
+
}
|
|
1044
1054
|
/**
|
|
1045
1055
|
* Get the current chat ID.
|
|
1046
1056
|
*/
|
|
@@ -1125,7 +1135,18 @@ var ContextEngine = class {
|
|
|
1125
1135
|
messages.push(message(msg.data).codec?.decode());
|
|
1126
1136
|
}
|
|
1127
1137
|
}
|
|
1138
|
+
for (let i = 0; i < this.#pendingMessages.length; i++) {
|
|
1139
|
+
const fragment2 = this.#pendingMessages[i];
|
|
1140
|
+
if (isLazyFragment(fragment2)) {
|
|
1141
|
+
this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1128
1144
|
for (const fragment2 of this.#pendingMessages) {
|
|
1145
|
+
if (!fragment2.codec) {
|
|
1146
|
+
throw new Error(
|
|
1147
|
+
`Fragment "${fragment2.name}" is missing codec. Lazy fragments must be resolved before decode.`
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1129
1150
|
const decoded = fragment2.codec.decode();
|
|
1130
1151
|
messages.push(decoded);
|
|
1131
1152
|
}
|
|
@@ -1156,9 +1177,24 @@ var ContextEngine = class {
|
|
|
1156
1177
|
this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
|
|
1157
1178
|
}
|
|
1158
1179
|
}
|
|
1180
|
+
for (const fragment2 of this.#pendingMessages) {
|
|
1181
|
+
if (fragment2.id) {
|
|
1182
|
+
const existing = await this.#store.getMessage(fragment2.id);
|
|
1183
|
+
if (existing && existing.parentId) {
|
|
1184
|
+
await this.#rewindForUpdate(existing.parentId);
|
|
1185
|
+
fragment2.id = crypto.randomUUID();
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1159
1190
|
let parentId = this.#branch.headMessageId;
|
|
1160
1191
|
const now = Date.now();
|
|
1161
1192
|
for (const fragment2 of this.#pendingMessages) {
|
|
1193
|
+
if (!fragment2.codec) {
|
|
1194
|
+
throw new Error(
|
|
1195
|
+
`Fragment "${fragment2.name}" is missing codec. Lazy fragments must be resolved before encode.`
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1162
1198
|
const messageData = {
|
|
1163
1199
|
id: fragment2.id ?? crypto.randomUUID(),
|
|
1164
1200
|
chatId: this.#chatId,
|
|
@@ -1518,33 +1554,28 @@ var ContextEngine = class {
|
|
|
1518
1554
|
return void 0;
|
|
1519
1555
|
}
|
|
1520
1556
|
/**
|
|
1521
|
-
* Extract skill
|
|
1522
|
-
* Returns
|
|
1523
|
-
*
|
|
1524
|
-
* Reads the original `paths` configuration stored in fragment metadata
|
|
1525
|
-
* by the skills() fragment helper.
|
|
1557
|
+
* Extract skill mounts from available_skills fragments.
|
|
1558
|
+
* Returns unified mount array where entries with `name` are individual skills.
|
|
1526
1559
|
*
|
|
1527
1560
|
* @example
|
|
1528
1561
|
* ```ts
|
|
1529
1562
|
* const context = new ContextEngine({ store, chatId, userId })
|
|
1530
1563
|
* .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));
|
|
1531
1564
|
*
|
|
1532
|
-
* const mounts = context.getSkillMounts();
|
|
1533
|
-
* // [{ host: './skills', sandbox: '/skills' }]
|
|
1565
|
+
* const { mounts } = context.getSkillMounts();
|
|
1566
|
+
* // mounts: [{ name: 'bi-dashboards', host: './skills/bi-dashboards/SKILL.md', sandbox: '/skills/bi-dashboards/SKILL.md' }]
|
|
1567
|
+
*
|
|
1568
|
+
* // Extract skills only (entries with name)
|
|
1569
|
+
* const skills = mounts.filter(m => m.name);
|
|
1534
1570
|
* ```
|
|
1535
1571
|
*/
|
|
1536
1572
|
getSkillMounts() {
|
|
1537
|
-
const mounts = [];
|
|
1538
1573
|
for (const fragment2 of this.#fragments) {
|
|
1539
|
-
if (fragment2.name === "available_skills" && fragment2.metadata
|
|
1540
|
-
|
|
1541
|
-
if (typeof mapping === "object" && mapping !== null && typeof mapping.host === "string" && typeof mapping.sandbox === "string") {
|
|
1542
|
-
mounts.push({ host: mapping.host, sandbox: mapping.sandbox });
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1574
|
+
if (fragment2.name === "available_skills" && fragment2.metadata?.mounts) {
|
|
1575
|
+
return { mounts: fragment2.metadata.mounts };
|
|
1545
1576
|
}
|
|
1546
1577
|
}
|
|
1547
|
-
return mounts;
|
|
1578
|
+
return { mounts: [] };
|
|
1548
1579
|
}
|
|
1549
1580
|
/**
|
|
1550
1581
|
* Inspect the full context state for debugging.
|
|
@@ -1715,11 +1746,14 @@ function pass(part) {
|
|
|
1715
1746
|
function fail(feedback) {
|
|
1716
1747
|
return { type: "fail", feedback };
|
|
1717
1748
|
}
|
|
1749
|
+
function stop(part) {
|
|
1750
|
+
return { type: "stop", part };
|
|
1751
|
+
}
|
|
1718
1752
|
function runGuardrailChain(part, guardrails, context) {
|
|
1719
1753
|
let currentPart = part;
|
|
1720
1754
|
for (const guardrail2 of guardrails) {
|
|
1721
1755
|
const result = guardrail2.handle(currentPart, context);
|
|
1722
|
-
if (result.type === "fail") {
|
|
1756
|
+
if (result.type === "fail" || result.type === "stop") {
|
|
1723
1757
|
return result;
|
|
1724
1758
|
}
|
|
1725
1759
|
currentPart = result.part;
|
|
@@ -1762,6 +1796,15 @@ var errorRecoveryGuardrail = {
|
|
|
1762
1796
|
if (errorText.includes("not in request.tools") || errorText.includes("tool") && errorText.includes("not found")) {
|
|
1763
1797
|
const toolMatch = errorText.match(/tool '([^']+)'/);
|
|
1764
1798
|
const toolName = toolMatch ? toolMatch[1] : "unknown";
|
|
1799
|
+
const matchingSkill = context.availableSkills.find(
|
|
1800
|
+
(skill) => skill.name === toolName
|
|
1801
|
+
);
|
|
1802
|
+
if (matchingSkill) {
|
|
1803
|
+
return logAndFail(
|
|
1804
|
+
`Skill confused as tool: ${toolName}`,
|
|
1805
|
+
`"${toolName}" is a skill, not a tool. Read the skill at ${matchingSkill.sandbox} to use it.`
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1765
1808
|
if (context.availableTools.length > 0) {
|
|
1766
1809
|
return logAndFail(
|
|
1767
1810
|
`Unregistered tool: ${toolName}`,
|
|
@@ -1779,97 +1822,79 @@ var errorRecoveryGuardrail = {
|
|
|
1779
1822
|
"I generated malformed JSON for the tool arguments. Let me format my tool call properly with valid JSON."
|
|
1780
1823
|
);
|
|
1781
1824
|
}
|
|
1825
|
+
if (errorText.includes("validation failed") && errorText.includes("did not match schema")) {
|
|
1826
|
+
const toolMatch = errorText.match(/parameters for tool (\w+)/);
|
|
1827
|
+
const toolName = toolMatch ? toolMatch[1] : "unknown";
|
|
1828
|
+
const schemaErrors = errorText.match(/errors: \[([^\]]+)\]/)?.[1] || "";
|
|
1829
|
+
return logAndFail(
|
|
1830
|
+
`Schema validation: ${toolName}`,
|
|
1831
|
+
`I called "${toolName}" with invalid parameters. Schema errors: ${schemaErrors}. Let me fix the parameters and try again.`
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1782
1834
|
if (errorText.includes("Parsing failed")) {
|
|
1783
1835
|
return logAndFail(
|
|
1784
1836
|
"Parsing failed",
|
|
1785
1837
|
"My response format was invalid. Let me try again with a properly formatted response."
|
|
1786
1838
|
);
|
|
1787
1839
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1840
|
+
if (errorText.includes("Failed to call a function") || errorText.includes("failed_generation")) {
|
|
1841
|
+
if (context.availableTools.length > 0) {
|
|
1842
|
+
return logAndFail(
|
|
1843
|
+
"Failed function call",
|
|
1844
|
+
`My function call was malformed. Available tools: ${context.availableTools.join(", ")}. Let me format my tool call correctly.`
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
return logAndFail(
|
|
1848
|
+
"Failed function call (no tools)",
|
|
1849
|
+
"My function call was malformed. Let me respond with plain text instead."
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
console.log(
|
|
1853
|
+
`${prefix} ${chalk.yellow("Unknown error - stopping without retry")}`
|
|
1792
1854
|
);
|
|
1855
|
+
return stop(part);
|
|
1793
1856
|
}
|
|
1794
1857
|
};
|
|
1795
|
-
var
|
|
1796
|
-
-- Chats table
|
|
1797
|
-
-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
|
|
1798
|
-
CREATE TABLE IF NOT EXISTS chats (
|
|
1799
|
-
id TEXT PRIMARY KEY,
|
|
1800
|
-
userId TEXT NOT NULL,
|
|
1801
|
-
title TEXT,
|
|
1802
|
-
metadata TEXT,
|
|
1803
|
-
createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
|
|
1804
|
-
updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
|
|
1805
|
-
);
|
|
1806
|
-
|
|
1807
|
-
CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
|
|
1808
|
-
CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
|
|
1809
|
-
|
|
1810
|
-
-- Messages table (nodes in the DAG)
|
|
1811
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
1812
|
-
id TEXT PRIMARY KEY,
|
|
1813
|
-
chatId TEXT NOT NULL,
|
|
1814
|
-
parentId TEXT,
|
|
1815
|
-
name TEXT NOT NULL,
|
|
1816
|
-
type TEXT,
|
|
1817
|
-
data TEXT NOT NULL,
|
|
1818
|
-
createdAt INTEGER NOT NULL,
|
|
1819
|
-
FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
|
|
1820
|
-
FOREIGN KEY (parentId) REFERENCES messages(id)
|
|
1821
|
-
);
|
|
1822
|
-
|
|
1823
|
-
CREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);
|
|
1824
|
-
CREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);
|
|
1825
|
-
|
|
1826
|
-
-- Branches table (pointers to head messages)
|
|
1827
|
-
CREATE TABLE IF NOT EXISTS branches (
|
|
1828
|
-
id TEXT PRIMARY KEY,
|
|
1829
|
-
chatId TEXT NOT NULL,
|
|
1830
|
-
name TEXT NOT NULL,
|
|
1831
|
-
headMessageId TEXT,
|
|
1832
|
-
isActive INTEGER NOT NULL DEFAULT 0,
|
|
1833
|
-
createdAt INTEGER NOT NULL,
|
|
1834
|
-
FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
|
|
1835
|
-
FOREIGN KEY (headMessageId) REFERENCES messages(id),
|
|
1836
|
-
UNIQUE(chatId, name)
|
|
1837
|
-
);
|
|
1858
|
+
var SKILLS_INSTRUCTIONS = dedent`A skill is a set of local instructions to follow that is stored in a \`SKILL.md\` file. Below is the list of skills that can be used. Each entry includes a name, description, and file path so you can open the source for full instructions when using a specific skill.
|
|
1838
1859
|
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)
|
|
1857
|
-
-- Only 'content' is indexed for full-text search
|
|
1858
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
|
|
1859
|
-
messageId UNINDEXED,
|
|
1860
|
-
chatId UNINDEXED,
|
|
1861
|
-
name UNINDEXED,
|
|
1862
|
-
content,
|
|
1863
|
-
tokenize='porter unicode61'
|
|
1864
|
-
);
|
|
1865
|
-
`;
|
|
1860
|
+
### How to use skills
|
|
1861
|
+
- Discovery: The list below shows the skills available in this session (name + description + file path). Skill bodies live on disk at the listed paths.
|
|
1862
|
+
- Trigger rules: If the user names a skill (with \`$SkillName\` or plain text) OR the task clearly matches a skill's description shown below, you must use that skill for that turn before doing anything else. Multiple mentions mean use them all. Do not carry skills across turns unless re-mentioned.
|
|
1863
|
+
- Missing/blocked: If a named skill isn't in the list or the path can't be read, say so briefly and continue with the best fallback.
|
|
1864
|
+
- How to use a skill (progressive disclosure):
|
|
1865
|
+
1) After deciding to use a skill, open its \`SKILL.md\`. Read only enough to follow the workflow.
|
|
1866
|
+
2) If \`SKILL.md\` points to extra folders such as \`references/\`, load only the specific files needed for the request; don't bulk-load everything.
|
|
1867
|
+
3) If \`scripts/\` exist, prefer running or patching them instead of retyping large code blocks.
|
|
1868
|
+
4) If \`assets/\` or templates exist, reuse them instead of recreating from scratch.
|
|
1869
|
+
- Coordination and sequencing:
|
|
1870
|
+
- If multiple skills apply, choose the minimal set that covers the request and state the order you'll use them.
|
|
1871
|
+
- Announce which skill(s) you're using and why (one short line). If you skip an obvious skill, say why.
|
|
1872
|
+
- Context hygiene:
|
|
1873
|
+
- Keep context small: summarize long sections instead of pasting them; only load extra files when needed.
|
|
1874
|
+
- Avoid deep reference-chasing: prefer opening only files directly linked from \`SKILL.md\` unless you're blocked.
|
|
1875
|
+
- When variants exist (frameworks, providers, domains), pick only the relevant reference file(s) and note that choice.
|
|
1876
|
+
- Safety and fallback: If a skill can't be applied cleanly (missing files, unclear instructions), state the issue, pick the next-best approach, and continue.`;
|
|
1877
|
+
var ddl_sqlite_default = "-- Context Store DDL for SQLite\n-- This schema implements a DAG-based message history with branching and checkpoints.\n\n-- Performance PRAGMAs (session-level, run on each connection)\nPRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA cache_size = -64000;\nPRAGMA temp_store = MEMORY;\nPRAGMA mmap_size = 268435456;\n\n-- Integrity\nPRAGMA foreign_keys = ON;\n\n-- Chats table\n-- createdAt/updatedAt: DEFAULT for insert, inline SET for updates\nCREATE TABLE IF NOT EXISTS chats (\n id TEXT PRIMARY KEY,\n userId TEXT NOT NULL,\n title TEXT,\n metadata TEXT,\n createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),\n updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)\n);\n\nCREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);\nCREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);\n-- Composite index for listChats(): WHERE userId = ? ORDER BY updatedAt DESC\nCREATE INDEX IF NOT EXISTS idx_chats_userId_updatedAt ON chats(userId, updatedAt DESC);\n\n-- Messages table (nodes in the DAG)\nCREATE TABLE IF NOT EXISTS messages (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n parentId TEXT,\n name TEXT NOT NULL,\n type TEXT,\n data TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (parentId) REFERENCES messages(id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);\nCREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);\n-- Composite index for recursive CTE parent traversal in getMessageChain()\nCREATE INDEX IF NOT EXISTS idx_messages_chatId_parentId ON messages(chatId, parentId);\n\n-- Branches table (pointers to head messages)\nCREATE TABLE IF NOT EXISTS branches (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n headMessageId TEXT,\n isActive INTEGER NOT NULL DEFAULT 0,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (headMessageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);\n-- Composite index for getActiveBranch(): WHERE chatId = ? AND isActive = 1\nCREATE INDEX IF NOT EXISTS idx_branches_chatId_isActive ON branches(chatId, isActive);\n\n-- Checkpoints table (pointers to message nodes)\nCREATE TABLE IF NOT EXISTS checkpoints (\n id TEXT PRIMARY KEY,\n chatId TEXT NOT NULL,\n name TEXT NOT NULL,\n messageId TEXT NOT NULL,\n createdAt INTEGER NOT NULL,\n FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,\n FOREIGN KEY (messageId) REFERENCES messages(id),\n UNIQUE(chatId, name)\n);\n\nCREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);\n\n-- FTS5 virtual table for full-text search\n-- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)\n-- Only 'content' is indexed for full-text search\nCREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(\n messageId UNINDEXED,\n chatId UNINDEXED,\n name UNINDEXED,\n content,\n tokenize='porter unicode61'\n);\n";
|
|
1866
1878
|
var SqliteContextStore = class extends ContextStore {
|
|
1867
1879
|
#db;
|
|
1868
|
-
|
|
1880
|
+
#statements = /* @__PURE__ */ new Map();
|
|
1881
|
+
/**
|
|
1882
|
+
* Get or create a prepared statement.
|
|
1883
|
+
* Statements are cached for the lifetime of the store to avoid
|
|
1884
|
+
* repeated SQL parsing and compilation overhead.
|
|
1885
|
+
*/
|
|
1886
|
+
#stmt(sql) {
|
|
1887
|
+
let stmt = this.#statements.get(sql);
|
|
1888
|
+
if (!stmt) {
|
|
1889
|
+
stmt = this.#db.prepare(sql);
|
|
1890
|
+
this.#statements.set(sql, stmt);
|
|
1891
|
+
}
|
|
1892
|
+
return stmt;
|
|
1893
|
+
}
|
|
1894
|
+
constructor(path32) {
|
|
1869
1895
|
super();
|
|
1870
|
-
this.#db = new DatabaseSync(
|
|
1871
|
-
this.#db.exec(
|
|
1872
|
-
this.#db.exec(STORE_DDL);
|
|
1896
|
+
this.#db = new DatabaseSync(path32);
|
|
1897
|
+
this.#db.exec(ddl_sqlite_default);
|
|
1873
1898
|
}
|
|
1874
1899
|
/**
|
|
1875
1900
|
* Execute a function within a transaction.
|
|
@@ -1890,11 +1915,12 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1890
1915
|
// Chat Operations
|
|
1891
1916
|
// ==========================================================================
|
|
1892
1917
|
async createChat(chat) {
|
|
1893
|
-
this.#useTransaction(() => {
|
|
1894
|
-
this.#db.prepare(
|
|
1918
|
+
return this.#useTransaction(() => {
|
|
1919
|
+
const row = this.#db.prepare(
|
|
1895
1920
|
`INSERT INTO chats (id, userId, title, metadata)
|
|
1896
|
-
VALUES (?, ?, ?, ?)
|
|
1897
|
-
|
|
1921
|
+
VALUES (?, ?, ?, ?)
|
|
1922
|
+
RETURNING *`
|
|
1923
|
+
).get(
|
|
1898
1924
|
chat.id,
|
|
1899
1925
|
chat.userId,
|
|
1900
1926
|
chat.title ?? null,
|
|
@@ -1904,6 +1930,14 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
1904
1930
|
`INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
|
|
1905
1931
|
VALUES (?, ?, 'main', NULL, 1, ?)`
|
|
1906
1932
|
).run(crypto.randomUUID(), chat.id, Date.now());
|
|
1933
|
+
return {
|
|
1934
|
+
id: row.id,
|
|
1935
|
+
userId: row.userId,
|
|
1936
|
+
title: row.title ?? void 0,
|
|
1937
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
1938
|
+
createdAt: row.createdAt,
|
|
1939
|
+
updatedAt: row.updatedAt
|
|
1940
|
+
};
|
|
1907
1941
|
});
|
|
1908
1942
|
}
|
|
1909
1943
|
async upsertChat(chat) {
|
|
@@ -2046,21 +2080,16 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
2046
2080
|
// Message Operations (Graph Nodes)
|
|
2047
2081
|
// ==========================================================================
|
|
2048
2082
|
async addMessage(message2) {
|
|
2049
|
-
|
|
2083
|
+
if (message2.parentId === message2.id) {
|
|
2084
|
+
throw new Error(`Message ${message2.id} cannot be its own parent`);
|
|
2085
|
+
}
|
|
2086
|
+
this.#stmt(
|
|
2050
2087
|
`INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
?5,
|
|
2057
|
-
?6,
|
|
2058
|
-
?7
|
|
2059
|
-
)
|
|
2060
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
2061
|
-
name = excluded.name,
|
|
2062
|
-
type = excluded.type,
|
|
2063
|
-
data = excluded.data`
|
|
2088
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
2089
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
2090
|
+
name = excluded.name,
|
|
2091
|
+
type = excluded.type,
|
|
2092
|
+
data = excluded.data`
|
|
2064
2093
|
).run(
|
|
2065
2094
|
message2.id,
|
|
2066
2095
|
message2.chatId,
|
|
@@ -2071,14 +2100,16 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
2071
2100
|
message2.createdAt
|
|
2072
2101
|
);
|
|
2073
2102
|
const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
|
|
2074
|
-
this.#
|
|
2075
|
-
this.#
|
|
2103
|
+
this.#stmt(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
|
|
2104
|
+
this.#stmt(
|
|
2076
2105
|
`INSERT INTO messages_fts(messageId, chatId, name, content)
|
|
2077
|
-
|
|
2106
|
+
VALUES (?, ?, ?, ?)`
|
|
2078
2107
|
).run(message2.id, message2.chatId, message2.name, content);
|
|
2079
2108
|
}
|
|
2080
2109
|
async getMessage(messageId) {
|
|
2081
|
-
const row = this.#
|
|
2110
|
+
const row = this.#stmt("SELECT * FROM messages WHERE id = ?").get(
|
|
2111
|
+
messageId
|
|
2112
|
+
);
|
|
2082
2113
|
if (!row) {
|
|
2083
2114
|
return void 0;
|
|
2084
2115
|
}
|
|
@@ -2093,15 +2124,16 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
2093
2124
|
};
|
|
2094
2125
|
}
|
|
2095
2126
|
async getMessageChain(headId) {
|
|
2096
|
-
const rows = this.#
|
|
2127
|
+
const rows = this.#stmt(
|
|
2097
2128
|
`WITH RECURSIVE chain AS (
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2129
|
+
SELECT *, 0 as depth FROM messages WHERE id = ?
|
|
2130
|
+
UNION ALL
|
|
2131
|
+
SELECT m.*, c.depth + 1 FROM messages m
|
|
2132
|
+
INNER JOIN chain c ON m.id = c.parentId
|
|
2133
|
+
WHERE c.depth < 100000
|
|
2134
|
+
)
|
|
2135
|
+
SELECT * FROM chain
|
|
2136
|
+
ORDER BY depth DESC`
|
|
2105
2137
|
).all(headId);
|
|
2106
2138
|
return rows.map((row) => ({
|
|
2107
2139
|
id: row.id,
|
|
@@ -2114,7 +2146,7 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
2114
2146
|
}));
|
|
2115
2147
|
}
|
|
2116
2148
|
async hasChildren(messageId) {
|
|
2117
|
-
const row = this.#
|
|
2149
|
+
const row = this.#stmt(
|
|
2118
2150
|
"SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ?) as hasChildren"
|
|
2119
2151
|
).get(messageId);
|
|
2120
2152
|
return row.hasChildren === 1;
|
|
@@ -2161,7 +2193,9 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
2161
2193
|
};
|
|
2162
2194
|
}
|
|
2163
2195
|
async getActiveBranch(chatId) {
|
|
2164
|
-
const row = this.#
|
|
2196
|
+
const row = this.#stmt(
|
|
2197
|
+
"SELECT * FROM branches WHERE chatId = ? AND isActive = 1"
|
|
2198
|
+
).get(chatId);
|
|
2165
2199
|
if (!row) {
|
|
2166
2200
|
return void 0;
|
|
2167
2201
|
}
|
|
@@ -2179,45 +2213,43 @@ var SqliteContextStore = class extends ContextStore {
|
|
|
2179
2213
|
this.#db.prepare("UPDATE branches SET isActive = 1 WHERE id = ?").run(branchId);
|
|
2180
2214
|
}
|
|
2181
2215
|
async updateBranchHead(branchId, messageId) {
|
|
2182
|
-
this.#
|
|
2216
|
+
this.#stmt("UPDATE branches SET headMessageId = ? WHERE id = ?").run(
|
|
2217
|
+
messageId,
|
|
2218
|
+
branchId
|
|
2219
|
+
);
|
|
2183
2220
|
}
|
|
2184
2221
|
async listBranches(chatId) {
|
|
2185
|
-
const
|
|
2222
|
+
const rows = this.#db.prepare(
|
|
2186
2223
|
`SELECT
|
|
2187
2224
|
b.id,
|
|
2188
2225
|
b.name,
|
|
2189
2226
|
b.headMessageId,
|
|
2190
2227
|
b.isActive,
|
|
2191
|
-
b.createdAt
|
|
2228
|
+
b.createdAt,
|
|
2229
|
+
COALESCE(
|
|
2230
|
+
(
|
|
2231
|
+
WITH RECURSIVE chain AS (
|
|
2232
|
+
SELECT id, parentId FROM messages WHERE id = b.headMessageId
|
|
2233
|
+
UNION ALL
|
|
2234
|
+
SELECT m.id, m.parentId FROM messages m
|
|
2235
|
+
INNER JOIN chain c ON m.id = c.parentId
|
|
2236
|
+
)
|
|
2237
|
+
SELECT COUNT(*) FROM chain
|
|
2238
|
+
),
|
|
2239
|
+
0
|
|
2240
|
+
) as messageCount
|
|
2192
2241
|
FROM branches b
|
|
2193
2242
|
WHERE b.chatId = ?
|
|
2194
2243
|
ORDER BY b.createdAt ASC`
|
|
2195
2244
|
).all(chatId);
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
SELECT m.id, m.parentId FROM messages m
|
|
2205
|
-
INNER JOIN chain c ON m.id = c.parentId
|
|
2206
|
-
)
|
|
2207
|
-
SELECT COUNT(*) as count FROM chain`
|
|
2208
|
-
).get(branch.headMessageId);
|
|
2209
|
-
messageCount = countRow.count;
|
|
2210
|
-
}
|
|
2211
|
-
result.push({
|
|
2212
|
-
id: branch.id,
|
|
2213
|
-
name: branch.name,
|
|
2214
|
-
headMessageId: branch.headMessageId,
|
|
2215
|
-
isActive: branch.isActive === 1,
|
|
2216
|
-
messageCount,
|
|
2217
|
-
createdAt: branch.createdAt
|
|
2218
|
-
});
|
|
2219
|
-
}
|
|
2220
|
-
return result;
|
|
2245
|
+
return rows.map((row) => ({
|
|
2246
|
+
id: row.id,
|
|
2247
|
+
name: row.name,
|
|
2248
|
+
headMessageId: row.headMessageId,
|
|
2249
|
+
isActive: row.isActive === 1,
|
|
2250
|
+
messageCount: row.messageCount,
|
|
2251
|
+
createdAt: row.createdAt
|
|
2252
|
+
}));
|
|
2221
2253
|
}
|
|
2222
2254
|
// ==========================================================================
|
|
2223
2255
|
// Checkpoint Operations
|
|
@@ -2485,8 +2517,10 @@ var Agent = class _Agent {
|
|
|
2485
2517
|
execute: async ({ writer }) => {
|
|
2486
2518
|
let currentResult = result;
|
|
2487
2519
|
let attempt = 0;
|
|
2520
|
+
const { mounts } = context.getSkillMounts();
|
|
2488
2521
|
const guardrailContext = {
|
|
2489
|
-
availableTools: Object.keys(this.tools)
|
|
2522
|
+
availableTools: Object.keys(this.tools),
|
|
2523
|
+
availableSkills: mounts
|
|
2490
2524
|
};
|
|
2491
2525
|
while (attempt < maxRetries) {
|
|
2492
2526
|
if (config?.abortSignal?.aborted) {
|
|
@@ -2514,10 +2548,20 @@ var Agent = class _Agent {
|
|
|
2514
2548
|
);
|
|
2515
2549
|
break;
|
|
2516
2550
|
}
|
|
2551
|
+
if (checkResult.type === "stop") {
|
|
2552
|
+
console.log(
|
|
2553
|
+
chalk2.red(
|
|
2554
|
+
`[${this.#options.name}] Guardrail stopped - unrecoverable error, no retry`
|
|
2555
|
+
)
|
|
2556
|
+
);
|
|
2557
|
+
writer.write(part);
|
|
2558
|
+
writer.write({ type: "finish" });
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2517
2561
|
if (checkResult.part.type === "text-delta") {
|
|
2518
2562
|
accumulatedText += checkResult.part.delta;
|
|
2519
2563
|
}
|
|
2520
|
-
writer.write(
|
|
2564
|
+
writer.write(part);
|
|
2521
2565
|
}
|
|
2522
2566
|
if (!guardrailFailed) {
|
|
2523
2567
|
writer.write({ type: "finish" });
|
|
@@ -2625,10 +2669,10 @@ var repairToolCall = async ({
|
|
|
2625
2669
|
if (NoSuchToolError.isInstance(error)) {
|
|
2626
2670
|
return null;
|
|
2627
2671
|
}
|
|
2628
|
-
const
|
|
2672
|
+
const tool3 = tools3[toolCall.toolName];
|
|
2629
2673
|
const { output } = await generateText({
|
|
2630
2674
|
model: groq("openai/gpt-oss-20b"),
|
|
2631
|
-
output: Output.object({ schema:
|
|
2675
|
+
output: Output.object({ schema: tool3.inputSchema }),
|
|
2632
2676
|
prompt: [
|
|
2633
2677
|
`The model tried to call the tool "${toolCall.toolName}" with the following inputs:`,
|
|
2634
2678
|
JSON.stringify(toolCall.input),
|
|
@@ -2730,7 +2774,7 @@ var tools = {
|
|
|
2730
2774
|
* Get plain-English explanation of a SQL query.
|
|
2731
2775
|
*/
|
|
2732
2776
|
explain_sql: tool({
|
|
2733
|
-
description:
|
|
2777
|
+
description: dedent2`
|
|
2734
2778
|
Get a plain-English explanation of a SQL query.
|
|
2735
2779
|
Use this to help the user understand what a query does.
|
|
2736
2780
|
|
|
@@ -2759,18 +2803,14 @@ var fragments = [
|
|
|
2759
2803
|
var developer_agent_default = { tools, fragments };
|
|
2760
2804
|
|
|
2761
2805
|
// packages/text2sql/src/lib/agents/result-tools.ts
|
|
2762
|
-
import "ai";
|
|
2763
2806
|
import { createBashTool as createBashTool2 } from "bash-tool";
|
|
2764
2807
|
import chalk3 from "chalk";
|
|
2765
2808
|
import {
|
|
2766
2809
|
Bash,
|
|
2767
|
-
InMemoryFs,
|
|
2768
2810
|
MountableFs,
|
|
2769
2811
|
OverlayFs,
|
|
2770
|
-
ReadWriteFs,
|
|
2771
2812
|
defineCommand as defineCommand2
|
|
2772
2813
|
} from "just-bash";
|
|
2773
|
-
import * as fs from "node:fs/promises";
|
|
2774
2814
|
import * as path from "node:path";
|
|
2775
2815
|
import { v7 } from "uuid";
|
|
2776
2816
|
function createCommand(name, subcommands) {
|
|
@@ -2825,18 +2865,13 @@ function createSqlCommand(adapter) {
|
|
|
2825
2865
|
const rowsArray = Array.isArray(rows) ? rows : [];
|
|
2826
2866
|
const content = JSON.stringify(rowsArray, null, 2);
|
|
2827
2867
|
const filename = `${v7()}.json`;
|
|
2828
|
-
const
|
|
2829
|
-
|
|
2830
|
-
await
|
|
2831
|
-
ctx.fs.writeFile(isolatedPath, content),
|
|
2832
|
-
// Current turn's isolated copy
|
|
2833
|
-
ctx.fs.writeFile(sharedPath, content)
|
|
2834
|
-
// Shared copy for cross-turn access
|
|
2835
|
-
]);
|
|
2868
|
+
const sqlPath = `/sql/${filename}`;
|
|
2869
|
+
await ctx.fs.mkdir("/sql", { recursive: true });
|
|
2870
|
+
await ctx.fs.writeFile(sqlPath, content);
|
|
2836
2871
|
const columns = rowsArray.length > 0 ? Object.keys(rowsArray[0]) : [];
|
|
2837
2872
|
return {
|
|
2838
2873
|
stdout: [
|
|
2839
|
-
`results stored in ${
|
|
2874
|
+
`results stored in ${sqlPath}`,
|
|
2840
2875
|
`columns: ${columns.join(", ") || "(none)"}`,
|
|
2841
2876
|
`rows: ${rowsArray.length}`
|
|
2842
2877
|
].join("\n") + "\n",
|
|
@@ -2882,39 +2917,25 @@ function createSqlCommand(adapter) {
|
|
|
2882
2917
|
});
|
|
2883
2918
|
}
|
|
2884
2919
|
async function createResultTools(options) {
|
|
2885
|
-
const { adapter,
|
|
2920
|
+
const { adapter, skillMounts, filesystem: baseFs } = options;
|
|
2886
2921
|
const sqlCommand = createSqlCommand(adapter);
|
|
2887
|
-
const root = process.env.TEXT2SQL_FS_ROOT || process.cwd();
|
|
2888
|
-
const chatDir = path.join(root, "artifacts", chatId);
|
|
2889
|
-
const resultsDir = path.join(chatDir, messageId, "results");
|
|
2890
|
-
await fs.mkdir(resultsDir, { recursive: true });
|
|
2891
2922
|
const fsMounts = skillMounts.map(({ host, sandbox: sandbox2 }) => ({
|
|
2892
|
-
mountPoint: sandbox2,
|
|
2923
|
+
mountPoint: path.dirname(sandbox2),
|
|
2893
2924
|
filesystem: new OverlayFs({
|
|
2894
|
-
root: host,
|
|
2925
|
+
root: path.dirname(host),
|
|
2895
2926
|
mountPoint: "/",
|
|
2896
2927
|
readOnly: true
|
|
2897
2928
|
})
|
|
2898
2929
|
}));
|
|
2899
2930
|
const filesystem = new MountableFs({
|
|
2900
|
-
base:
|
|
2901
|
-
mounts:
|
|
2902
|
-
...fsMounts,
|
|
2903
|
-
{
|
|
2904
|
-
mountPoint: "/results",
|
|
2905
|
-
filesystem: new ReadWriteFs({ root: resultsDir })
|
|
2906
|
-
},
|
|
2907
|
-
{
|
|
2908
|
-
mountPoint: "/artifacts",
|
|
2909
|
-
filesystem: new ReadWriteFs({ root: chatDir })
|
|
2910
|
-
}
|
|
2911
|
-
]
|
|
2931
|
+
base: baseFs,
|
|
2932
|
+
mounts: fsMounts
|
|
2912
2933
|
});
|
|
2913
2934
|
const bashInstance = new Bash({
|
|
2914
2935
|
customCommands: [sqlCommand],
|
|
2915
2936
|
fs: filesystem
|
|
2916
2937
|
});
|
|
2917
|
-
const {
|
|
2938
|
+
const { sandbox, tools: tools3 } = await createBashTool2({
|
|
2918
2939
|
sandbox: bashInstance,
|
|
2919
2940
|
destination: "/",
|
|
2920
2941
|
onBeforeBashCall: ({ command }) => {
|
|
@@ -2928,7 +2949,7 @@ async function createResultTools(options) {
|
|
|
2928
2949
|
return { result };
|
|
2929
2950
|
}
|
|
2930
2951
|
});
|
|
2931
|
-
return {
|
|
2952
|
+
return { sandbox, tools: tools3 };
|
|
2932
2953
|
}
|
|
2933
2954
|
|
|
2934
2955
|
// packages/text2sql/src/lib/agents/sql.agent.ts
|
|
@@ -3103,7 +3124,7 @@ async function withRetry(computation, options = { retries: 3 }) {
|
|
|
3103
3124
|
|
|
3104
3125
|
// packages/text2sql/src/lib/agents/suggestions.agents.ts
|
|
3105
3126
|
import { groq as groq4 } from "@ai-sdk/groq";
|
|
3106
|
-
import
|
|
3127
|
+
import dedent3 from "dedent";
|
|
3107
3128
|
import z4 from "zod";
|
|
3108
3129
|
import { agent as agent2, thirdPersonPrompt } from "@deepagents/agent";
|
|
3109
3130
|
var suggestionsAgent = agent2({
|
|
@@ -3119,7 +3140,7 @@ var suggestionsAgent = agent2({
|
|
|
3119
3140
|
).min(1).max(5).describe("A set of up to two advanced question + SQL pairs.")
|
|
3120
3141
|
}),
|
|
3121
3142
|
prompt: (state) => {
|
|
3122
|
-
return
|
|
3143
|
+
return dedent3`
|
|
3123
3144
|
${thirdPersonPrompt()}
|
|
3124
3145
|
|
|
3125
3146
|
<identity>
|
|
@@ -3154,12 +3175,12 @@ var suggestionsAgent = agent2({
|
|
|
3154
3175
|
});
|
|
3155
3176
|
|
|
3156
3177
|
// packages/text2sql/src/lib/agents/text2sql.agent.ts
|
|
3157
|
-
import { tool as
|
|
3178
|
+
import { tool as tool2 } from "ai";
|
|
3158
3179
|
import z5 from "zod";
|
|
3159
3180
|
import { toState as toState2 } from "@deepagents/agent";
|
|
3160
3181
|
import { scratchpad_tool } from "@deepagents/toolbox";
|
|
3161
3182
|
var tools2 = {
|
|
3162
|
-
validate_query:
|
|
3183
|
+
validate_query: tool2({
|
|
3163
3184
|
description: `Validate SQL query syntax before execution. Use this to check if your SQL is valid before running db_query. This helps catch errors early and allows you to correct the query if needed.`,
|
|
3164
3185
|
inputSchema: z5.object({
|
|
3165
3186
|
sql: z5.string().describe("The SQL query to validate.")
|
|
@@ -3173,7 +3194,7 @@ var tools2 = {
|
|
|
3173
3194
|
return "Query is valid.";
|
|
3174
3195
|
}
|
|
3175
3196
|
}),
|
|
3176
|
-
db_query:
|
|
3197
|
+
db_query: tool2({
|
|
3177
3198
|
description: `Internal tool to fetch data from the store's database. Write a SQL query to retrieve the information needed to answer the user's question. The results will be returned as data that you can then present to the user in natural language.`,
|
|
3178
3199
|
inputSchema: z5.object({
|
|
3179
3200
|
reasoning: z5.string().describe(
|
|
@@ -3202,9 +3223,9 @@ var Checkpoint = class _Checkpoint {
|
|
|
3202
3223
|
points;
|
|
3203
3224
|
path;
|
|
3204
3225
|
configHash;
|
|
3205
|
-
constructor(
|
|
3226
|
+
constructor(path4, configHash, points) {
|
|
3206
3227
|
this.points = points;
|
|
3207
|
-
this.path =
|
|
3228
|
+
this.path = path4;
|
|
3208
3229
|
this.configHash = configHash;
|
|
3209
3230
|
}
|
|
3210
3231
|
/**
|
|
@@ -3212,14 +3233,14 @@ var Checkpoint = class _Checkpoint {
|
|
|
3212
3233
|
* Handles corrupted files and config changes gracefully.
|
|
3213
3234
|
*/
|
|
3214
3235
|
static async load(options) {
|
|
3215
|
-
const { path:
|
|
3216
|
-
if (existsSync(
|
|
3236
|
+
const { path: path4, configHash } = options;
|
|
3237
|
+
if (existsSync(path4)) {
|
|
3217
3238
|
try {
|
|
3218
|
-
const content = readFileSync(
|
|
3239
|
+
const content = readFileSync(path4, "utf-8");
|
|
3219
3240
|
const file = JSON.parse(content);
|
|
3220
3241
|
if (configHash && file.configHash && file.configHash !== configHash) {
|
|
3221
3242
|
console.log("\u26A0 Config changed, starting fresh");
|
|
3222
|
-
return new _Checkpoint(
|
|
3243
|
+
return new _Checkpoint(path4, configHash, {});
|
|
3223
3244
|
}
|
|
3224
3245
|
const points = file.points ?? {};
|
|
3225
3246
|
const totalEntries = Object.values(points).reduce(
|
|
@@ -3227,14 +3248,14 @@ var Checkpoint = class _Checkpoint {
|
|
|
3227
3248
|
0
|
|
3228
3249
|
);
|
|
3229
3250
|
console.log(`\u2713 Resuming from checkpoint (${totalEntries} entries)`);
|
|
3230
|
-
return new _Checkpoint(
|
|
3251
|
+
return new _Checkpoint(path4, configHash, points);
|
|
3231
3252
|
} catch {
|
|
3232
3253
|
console.log("\u26A0 Checkpoint corrupted, starting fresh");
|
|
3233
|
-
return new _Checkpoint(
|
|
3254
|
+
return new _Checkpoint(path4, configHash, {});
|
|
3234
3255
|
}
|
|
3235
3256
|
}
|
|
3236
3257
|
console.log("Starting new checkpoint");
|
|
3237
|
-
return new _Checkpoint(
|
|
3258
|
+
return new _Checkpoint(path4, configHash, {});
|
|
3238
3259
|
}
|
|
3239
3260
|
/**
|
|
3240
3261
|
* Run a single computation with checkpointing.
|
|
@@ -3401,6 +3422,815 @@ var JsonCache = class extends FileCache {
|
|
|
3401
3422
|
}
|
|
3402
3423
|
};
|
|
3403
3424
|
|
|
3425
|
+
// packages/text2sql/src/lib/fs/sqlite-fs.ts
|
|
3426
|
+
import * as path3 from "node:path";
|
|
3427
|
+
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
3428
|
+
|
|
3429
|
+
// packages/text2sql/src/lib/fs/ddl.sqlite-fs.sql
|
|
3430
|
+
var ddl_sqlite_fs_default = "-- SQLite-based filesystem schema for artifact storage\n-- Tables: fs_entries (file/directory metadata), fs_chunks (file content)\n\n-- Performance PRAGMAs (session-level, run on each connection)\nPRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA cache_size = -64000;\nPRAGMA temp_store = MEMORY;\nPRAGMA mmap_size = 268435456;\n\n-- Integrity\nPRAGMA foreign_keys = ON;\n\n-- Filesystem entries table (files, directories, symlinks)\nCREATE TABLE IF NOT EXISTS fs_entries (\n path TEXT PRIMARY KEY, -- Normalized absolute path (e.g., '/results/uuid.json')\n type TEXT NOT NULL, -- 'file' | 'directory' | 'symlink'\n mode INTEGER NOT NULL, -- Unix permissions (e.g., 0o644 = 420)\n size INTEGER NOT NULL, -- File size in bytes (0 for directories)\n mtime INTEGER NOT NULL, -- Modified time (milliseconds since epoch)\n symlinkTarget TEXT -- Target path for symlinks (NULL for files/dirs)\n);\n\nCREATE INDEX IF NOT EXISTS idx_fs_entries_type ON fs_entries(type);\n\n-- File content chunks table (for handling large files)\n-- Files are split into 1MB chunks to avoid SQLite BLOB performance issues\nCREATE TABLE IF NOT EXISTS fs_chunks (\n path TEXT NOT NULL, -- Reference to fs_entries.path\n chunkIndex INTEGER NOT NULL, -- 0-based chunk sequence\n data BLOB NOT NULL, -- Chunk content (up to 1MB default)\n PRIMARY KEY (path, chunkIndex),\n FOREIGN KEY (path) REFERENCES fs_entries(path) ON DELETE CASCADE ON UPDATE CASCADE\n);\n";
|
|
3431
|
+
|
|
3432
|
+
// packages/text2sql/src/lib/fs/sqlite-fs.ts
|
|
3433
|
+
var SqliteFs = class {
|
|
3434
|
+
#db;
|
|
3435
|
+
#statements = /* @__PURE__ */ new Map();
|
|
3436
|
+
#chunkSize;
|
|
3437
|
+
#root;
|
|
3438
|
+
constructor(options) {
|
|
3439
|
+
this.#chunkSize = options.chunkSize ?? 1024 * 1024;
|
|
3440
|
+
const normalizedRoot = this.#normalizeRoot(options.root);
|
|
3441
|
+
this.#root = normalizedRoot === "/" ? "" : normalizedRoot;
|
|
3442
|
+
this.#db = new DatabaseSync2(options.dbPath);
|
|
3443
|
+
this.#db.exec(ddl_sqlite_fs_default);
|
|
3444
|
+
const rootSlashExists = this.#stmt(
|
|
3445
|
+
"SELECT 1 FROM fs_entries WHERE path = ?"
|
|
3446
|
+
).get("/");
|
|
3447
|
+
if (!rootSlashExists) {
|
|
3448
|
+
this.#stmt(
|
|
3449
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
3450
|
+
VALUES ('/', 'directory', 493, 0, ?)`
|
|
3451
|
+
).run(Date.now());
|
|
3452
|
+
}
|
|
3453
|
+
if (this.#root) {
|
|
3454
|
+
this.#createParentDirs(this.#root);
|
|
3455
|
+
const rootExists = this.#stmt(
|
|
3456
|
+
"SELECT 1 FROM fs_entries WHERE path = ?"
|
|
3457
|
+
).get(this.#root);
|
|
3458
|
+
if (!rootExists) {
|
|
3459
|
+
this.#stmt(
|
|
3460
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
3461
|
+
VALUES (?, 'directory', 493, 0, ?)`
|
|
3462
|
+
).run(this.#root, Date.now());
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Create parent directories for a path (used during initialization).
|
|
3468
|
+
* Creates all segments EXCEPT the last one (the path itself).
|
|
3469
|
+
*/
|
|
3470
|
+
#createParentDirs(p) {
|
|
3471
|
+
const segments = p.split("/").filter(Boolean);
|
|
3472
|
+
let currentPath = "/";
|
|
3473
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
3474
|
+
currentPath = path3.posix.join(currentPath, segments[i]);
|
|
3475
|
+
const exists = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
|
|
3476
|
+
currentPath
|
|
3477
|
+
);
|
|
3478
|
+
if (!exists) {
|
|
3479
|
+
this.#stmt(
|
|
3480
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
3481
|
+
VALUES (?, 'directory', 493, 0, ?)`
|
|
3482
|
+
).run(currentPath, Date.now());
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
#stmt(sql) {
|
|
3487
|
+
let stmt = this.#statements.get(sql);
|
|
3488
|
+
if (!stmt) {
|
|
3489
|
+
stmt = this.#db.prepare(sql);
|
|
3490
|
+
this.#statements.set(sql, stmt);
|
|
3491
|
+
}
|
|
3492
|
+
return stmt;
|
|
3493
|
+
}
|
|
3494
|
+
#normalizeRoot(root) {
|
|
3495
|
+
return path3.posix.resolve("/", root.trim());
|
|
3496
|
+
}
|
|
3497
|
+
#prefixPath(p) {
|
|
3498
|
+
if (!this.#root) {
|
|
3499
|
+
return p;
|
|
3500
|
+
}
|
|
3501
|
+
if (p === "/") {
|
|
3502
|
+
return this.#root;
|
|
3503
|
+
}
|
|
3504
|
+
return path3.posix.join(this.#root, p);
|
|
3505
|
+
}
|
|
3506
|
+
#useTransaction(fn) {
|
|
3507
|
+
this.#db.exec("BEGIN TRANSACTION");
|
|
3508
|
+
try {
|
|
3509
|
+
const result = fn();
|
|
3510
|
+
this.#db.exec("COMMIT");
|
|
3511
|
+
return result;
|
|
3512
|
+
} catch (error) {
|
|
3513
|
+
this.#db.exec("ROLLBACK");
|
|
3514
|
+
throw error;
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
#normalizePath(p) {
|
|
3518
|
+
return path3.posix.resolve("/", p);
|
|
3519
|
+
}
|
|
3520
|
+
#dirname(p) {
|
|
3521
|
+
const dir = path3.posix.dirname(p);
|
|
3522
|
+
return dir === "" ? "/" : dir;
|
|
3523
|
+
}
|
|
3524
|
+
#ensureParentExists(filePath) {
|
|
3525
|
+
const parent = this.#dirname(filePath);
|
|
3526
|
+
const rootPath = this.#root || "/";
|
|
3527
|
+
if (parent === rootPath || parent === "/") return;
|
|
3528
|
+
const entry = this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
|
|
3529
|
+
parent
|
|
3530
|
+
);
|
|
3531
|
+
if (!entry) {
|
|
3532
|
+
this.#ensureParentExists(parent);
|
|
3533
|
+
this.#stmt(
|
|
3534
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
3535
|
+
VALUES (?, 'directory', 493, 0, ?)`
|
|
3536
|
+
).run(parent, Date.now());
|
|
3537
|
+
} else if (entry.type !== "directory") {
|
|
3538
|
+
throw new Error(`mkdir: parent is not a directory: ${parent}`);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
#writeChunks(filePath, content) {
|
|
3542
|
+
this.#stmt("DELETE FROM fs_chunks WHERE path = ?").run(filePath);
|
|
3543
|
+
for (let i = 0; i < content.length; i += this.#chunkSize) {
|
|
3544
|
+
const chunk = content.slice(
|
|
3545
|
+
i,
|
|
3546
|
+
Math.min(i + this.#chunkSize, content.length)
|
|
3547
|
+
);
|
|
3548
|
+
this.#stmt(
|
|
3549
|
+
"INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
|
|
3550
|
+
).run(filePath, Math.floor(i / this.#chunkSize), chunk);
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
#readChunks(filePath) {
|
|
3554
|
+
const rows = this.#stmt(
|
|
3555
|
+
"SELECT data FROM fs_chunks WHERE path = ? ORDER BY chunkIndex"
|
|
3556
|
+
).all(filePath);
|
|
3557
|
+
if (rows.length === 0) {
|
|
3558
|
+
return new Uint8Array(0);
|
|
3559
|
+
}
|
|
3560
|
+
const totalSize = rows.reduce((sum, row) => sum + row.data.length, 0);
|
|
3561
|
+
const result = new Uint8Array(totalSize);
|
|
3562
|
+
let offset = 0;
|
|
3563
|
+
for (const row of rows) {
|
|
3564
|
+
result.set(row.data, offset);
|
|
3565
|
+
offset += row.data.length;
|
|
3566
|
+
}
|
|
3567
|
+
return result;
|
|
3568
|
+
}
|
|
3569
|
+
#resolveSymlink(p, seen = /* @__PURE__ */ new Set()) {
|
|
3570
|
+
if (seen.has(p)) {
|
|
3571
|
+
throw new Error(`readFile: circular symlink: ${p}`);
|
|
3572
|
+
}
|
|
3573
|
+
const entry = this.#stmt(
|
|
3574
|
+
"SELECT type, symlinkTarget FROM fs_entries WHERE path = ?"
|
|
3575
|
+
).get(p);
|
|
3576
|
+
if (!entry) {
|
|
3577
|
+
throw new Error(`ENOENT: no such file or directory: ${p}`);
|
|
3578
|
+
}
|
|
3579
|
+
if (entry.type !== "symlink") {
|
|
3580
|
+
return p;
|
|
3581
|
+
}
|
|
3582
|
+
seen.add(p);
|
|
3583
|
+
const target = this.#normalizePath(
|
|
3584
|
+
path3.posix.resolve(this.#dirname(p), entry.symlinkTarget)
|
|
3585
|
+
);
|
|
3586
|
+
return this.#resolveSymlink(target, seen);
|
|
3587
|
+
}
|
|
3588
|
+
#toUint8Array(content, encoding) {
|
|
3589
|
+
if (content instanceof Uint8Array) {
|
|
3590
|
+
return content;
|
|
3591
|
+
}
|
|
3592
|
+
const enc = encoding ?? "utf8";
|
|
3593
|
+
return new Uint8Array(Buffer.from(content, enc));
|
|
3594
|
+
}
|
|
3595
|
+
// ============================================================================
|
|
3596
|
+
// IFileSystem Implementation
|
|
3597
|
+
// ============================================================================
|
|
3598
|
+
async readFile(filePath, options) {
|
|
3599
|
+
const normalized = this.#normalizePath(filePath);
|
|
3600
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3601
|
+
const resolved = this.#resolveSymlink(prefixed);
|
|
3602
|
+
const entry = this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
|
|
3603
|
+
resolved
|
|
3604
|
+
);
|
|
3605
|
+
if (!entry) {
|
|
3606
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3607
|
+
}
|
|
3608
|
+
if (entry.type === "directory") {
|
|
3609
|
+
throw new Error(`EISDIR: illegal operation on a directory: ${filePath}`);
|
|
3610
|
+
}
|
|
3611
|
+
const content = this.#readChunks(resolved);
|
|
3612
|
+
const encoding = typeof options === "string" ? options : options?.encoding ?? "utf8";
|
|
3613
|
+
return Buffer.from(content).toString(encoding);
|
|
3614
|
+
}
|
|
3615
|
+
async readFileBuffer(filePath) {
|
|
3616
|
+
const normalized = this.#normalizePath(filePath);
|
|
3617
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3618
|
+
const resolved = this.#resolveSymlink(prefixed);
|
|
3619
|
+
const entry = this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
|
|
3620
|
+
resolved
|
|
3621
|
+
);
|
|
3622
|
+
if (!entry) {
|
|
3623
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3624
|
+
}
|
|
3625
|
+
if (entry.type === "directory") {
|
|
3626
|
+
throw new Error(`EISDIR: illegal operation on a directory: ${filePath}`);
|
|
3627
|
+
}
|
|
3628
|
+
return this.#readChunks(resolved);
|
|
3629
|
+
}
|
|
3630
|
+
async writeFile(filePath, content, options) {
|
|
3631
|
+
const normalized = this.#normalizePath(filePath);
|
|
3632
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3633
|
+
const encoding = typeof options === "string" ? options : options?.encoding;
|
|
3634
|
+
const data = this.#toUint8Array(content, encoding);
|
|
3635
|
+
this.#useTransaction(() => {
|
|
3636
|
+
this.#ensureParentExists(prefixed);
|
|
3637
|
+
this.#stmt(
|
|
3638
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
3639
|
+
VALUES (?, 'file', 420, ?, ?)
|
|
3640
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
3641
|
+
type = 'file',
|
|
3642
|
+
size = excluded.size,
|
|
3643
|
+
mtime = excluded.mtime`
|
|
3644
|
+
).run(prefixed, data.length, Date.now());
|
|
3645
|
+
this.#writeChunks(prefixed, data);
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
async appendFile(filePath, content, options) {
|
|
3649
|
+
const normalized = this.#normalizePath(filePath);
|
|
3650
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3651
|
+
const encoding = typeof options === "string" ? options : options?.encoding;
|
|
3652
|
+
const newData = this.#toUint8Array(content, encoding);
|
|
3653
|
+
this.#useTransaction(() => {
|
|
3654
|
+
this.#ensureParentExists(prefixed);
|
|
3655
|
+
const entry = this.#stmt(
|
|
3656
|
+
"SELECT type FROM fs_entries WHERE path = ?"
|
|
3657
|
+
).get(prefixed);
|
|
3658
|
+
if (entry && entry.type !== "file") {
|
|
3659
|
+
throw new Error(`appendFile: not a file: ${filePath}`);
|
|
3660
|
+
}
|
|
3661
|
+
const existing = entry ? this.#readChunks(prefixed) : new Uint8Array(0);
|
|
3662
|
+
const combined = new Uint8Array(existing.length + newData.length);
|
|
3663
|
+
combined.set(existing, 0);
|
|
3664
|
+
combined.set(newData, existing.length);
|
|
3665
|
+
this.#stmt(
|
|
3666
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
3667
|
+
VALUES (?, 'file', 420, ?, ?)
|
|
3668
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
3669
|
+
size = excluded.size,
|
|
3670
|
+
mtime = excluded.mtime`
|
|
3671
|
+
).run(prefixed, combined.length, Date.now());
|
|
3672
|
+
this.#writeChunks(prefixed, combined);
|
|
3673
|
+
});
|
|
3674
|
+
}
|
|
3675
|
+
async exists(filePath) {
|
|
3676
|
+
const normalized = this.#normalizePath(filePath);
|
|
3677
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3678
|
+
const row = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
|
|
3679
|
+
prefixed
|
|
3680
|
+
);
|
|
3681
|
+
return row !== void 0;
|
|
3682
|
+
}
|
|
3683
|
+
async stat(filePath) {
|
|
3684
|
+
const normalized = this.#normalizePath(filePath);
|
|
3685
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3686
|
+
const resolved = this.#resolveSymlink(prefixed);
|
|
3687
|
+
const entry = this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(
|
|
3688
|
+
resolved
|
|
3689
|
+
);
|
|
3690
|
+
if (!entry) {
|
|
3691
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3692
|
+
}
|
|
3693
|
+
return {
|
|
3694
|
+
isFile: entry.type === "file",
|
|
3695
|
+
isDirectory: entry.type === "directory",
|
|
3696
|
+
isSymbolicLink: false,
|
|
3697
|
+
// stat follows symlinks
|
|
3698
|
+
mode: entry.mode,
|
|
3699
|
+
size: entry.size,
|
|
3700
|
+
mtime: new Date(entry.mtime)
|
|
3701
|
+
};
|
|
3702
|
+
}
|
|
3703
|
+
async lstat(filePath) {
|
|
3704
|
+
const normalized = this.#normalizePath(filePath);
|
|
3705
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3706
|
+
const entry = this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(
|
|
3707
|
+
prefixed
|
|
3708
|
+
);
|
|
3709
|
+
if (!entry) {
|
|
3710
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3711
|
+
}
|
|
3712
|
+
return {
|
|
3713
|
+
isFile: entry.type === "file",
|
|
3714
|
+
isDirectory: entry.type === "directory",
|
|
3715
|
+
isSymbolicLink: entry.type === "symlink",
|
|
3716
|
+
mode: entry.mode,
|
|
3717
|
+
size: entry.size,
|
|
3718
|
+
mtime: new Date(entry.mtime)
|
|
3719
|
+
};
|
|
3720
|
+
}
|
|
3721
|
+
async mkdir(dirPath, options) {
|
|
3722
|
+
const normalized = this.#normalizePath(dirPath);
|
|
3723
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3724
|
+
const existing = this.#stmt(
|
|
3725
|
+
"SELECT type FROM fs_entries WHERE path = ?"
|
|
3726
|
+
).get(prefixed);
|
|
3727
|
+
if (existing) {
|
|
3728
|
+
if (options?.recursive) {
|
|
3729
|
+
return;
|
|
3730
|
+
}
|
|
3731
|
+
throw new Error(`EEXIST: file already exists: ${dirPath}`);
|
|
3732
|
+
}
|
|
3733
|
+
this.#useTransaction(() => {
|
|
3734
|
+
if (options?.recursive) {
|
|
3735
|
+
const rootPath = this.#root || "/";
|
|
3736
|
+
const relativePath = path3.posix.relative(rootPath, prefixed);
|
|
3737
|
+
const segments = relativePath.split("/").filter(Boolean);
|
|
3738
|
+
let currentPath = rootPath;
|
|
3739
|
+
for (const segment of segments) {
|
|
3740
|
+
currentPath = path3.posix.join(currentPath, segment);
|
|
3741
|
+
const exists = this.#stmt(
|
|
3742
|
+
"SELECT type FROM fs_entries WHERE path = ?"
|
|
3743
|
+
).get(currentPath);
|
|
3744
|
+
if (!exists) {
|
|
3745
|
+
this.#stmt(
|
|
3746
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
3747
|
+
VALUES (?, 'directory', 493, 0, ?)`
|
|
3748
|
+
).run(currentPath, Date.now());
|
|
3749
|
+
} else if (exists.type !== "directory") {
|
|
3750
|
+
throw new Error(`mkdir: not a directory: ${currentPath}`);
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
} else {
|
|
3754
|
+
const parent = this.#dirname(prefixed);
|
|
3755
|
+
const parentEntry = this.#stmt(
|
|
3756
|
+
"SELECT type FROM fs_entries WHERE path = ?"
|
|
3757
|
+
).get(parent);
|
|
3758
|
+
if (!parentEntry) {
|
|
3759
|
+
throw new Error(`mkdir: parent does not exist: ${parent}`);
|
|
3760
|
+
}
|
|
3761
|
+
if (parentEntry.type !== "directory") {
|
|
3762
|
+
throw new Error(`mkdir: parent is not a directory: ${parent}`);
|
|
3763
|
+
}
|
|
3764
|
+
this.#stmt(
|
|
3765
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
3766
|
+
VALUES (?, 'directory', 493, 0, ?)`
|
|
3767
|
+
).run(prefixed, Date.now());
|
|
3768
|
+
}
|
|
3769
|
+
});
|
|
3770
|
+
}
|
|
3771
|
+
async readdir(dirPath) {
|
|
3772
|
+
const normalized = this.#normalizePath(dirPath);
|
|
3773
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3774
|
+
const resolved = this.#resolveSymlink(prefixed);
|
|
3775
|
+
const entry = this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
|
|
3776
|
+
resolved
|
|
3777
|
+
);
|
|
3778
|
+
if (!entry) {
|
|
3779
|
+
throw new Error(`ENOENT: no such file or directory: ${dirPath}`);
|
|
3780
|
+
}
|
|
3781
|
+
if (entry.type !== "directory") {
|
|
3782
|
+
throw new Error(`ENOTDIR: not a directory: ${dirPath}`);
|
|
3783
|
+
}
|
|
3784
|
+
const prefix = resolved === "/" ? "/" : resolved + "/";
|
|
3785
|
+
const rows = this.#stmt(
|
|
3786
|
+
`SELECT path FROM fs_entries
|
|
3787
|
+
WHERE path LIKE ? || '%'
|
|
3788
|
+
AND path != ?
|
|
3789
|
+
AND path NOT LIKE ? || '%/%'`
|
|
3790
|
+
).all(prefix, resolved, prefix);
|
|
3791
|
+
return rows.map((row) => path3.posix.basename(row.path));
|
|
3792
|
+
}
|
|
3793
|
+
async readdirWithFileTypes(dirPath) {
|
|
3794
|
+
const normalized = this.#normalizePath(dirPath);
|
|
3795
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3796
|
+
const resolved = this.#resolveSymlink(prefixed);
|
|
3797
|
+
const entry = this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
|
|
3798
|
+
resolved
|
|
3799
|
+
);
|
|
3800
|
+
if (!entry) {
|
|
3801
|
+
throw new Error(`ENOENT: no such file or directory: ${dirPath}`);
|
|
3802
|
+
}
|
|
3803
|
+
if (entry.type !== "directory") {
|
|
3804
|
+
throw new Error(`ENOTDIR: not a directory: ${dirPath}`);
|
|
3805
|
+
}
|
|
3806
|
+
const prefix = resolved === "/" ? "/" : resolved + "/";
|
|
3807
|
+
const rows = this.#stmt(
|
|
3808
|
+
`SELECT path, type FROM fs_entries
|
|
3809
|
+
WHERE path LIKE ? || '%'
|
|
3810
|
+
AND path != ?
|
|
3811
|
+
AND path NOT LIKE ? || '%/%'`
|
|
3812
|
+
).all(prefix, resolved, prefix);
|
|
3813
|
+
return rows.map((row) => ({
|
|
3814
|
+
name: path3.posix.basename(row.path),
|
|
3815
|
+
isFile: row.type === "file",
|
|
3816
|
+
isDirectory: row.type === "directory",
|
|
3817
|
+
isSymbolicLink: row.type === "symlink"
|
|
3818
|
+
}));
|
|
3819
|
+
}
|
|
3820
|
+
async rm(filePath, options) {
|
|
3821
|
+
const normalized = this.#normalizePath(filePath);
|
|
3822
|
+
const prefixed = this.#prefixPath(normalized);
|
|
3823
|
+
const entry = this.#stmt("SELECT type FROM fs_entries WHERE path = ?").get(
|
|
3824
|
+
prefixed
|
|
3825
|
+
);
|
|
3826
|
+
if (!entry) {
|
|
3827
|
+
if (options?.force) {
|
|
3828
|
+
return;
|
|
3829
|
+
}
|
|
3830
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
3831
|
+
}
|
|
3832
|
+
this.#useTransaction(() => {
|
|
3833
|
+
if (entry.type === "directory") {
|
|
3834
|
+
const children = this.#stmt(
|
|
3835
|
+
`SELECT 1 FROM fs_entries WHERE path LIKE ? || '/%' LIMIT 1`
|
|
3836
|
+
).get(prefixed);
|
|
3837
|
+
if (children && !options?.recursive) {
|
|
3838
|
+
throw new Error(`ENOTEMPTY: directory not empty: ${filePath}`);
|
|
3839
|
+
}
|
|
3840
|
+
this.#stmt(
|
|
3841
|
+
`DELETE FROM fs_entries WHERE path = ? OR path LIKE ? || '/%'`
|
|
3842
|
+
).run(prefixed, prefixed);
|
|
3843
|
+
} else {
|
|
3844
|
+
this.#stmt("DELETE FROM fs_entries WHERE path = ?").run(prefixed);
|
|
3845
|
+
}
|
|
3846
|
+
});
|
|
3847
|
+
}
|
|
3848
|
+
async cp(src, dest, options) {
|
|
3849
|
+
const srcNormalized = this.#normalizePath(src);
|
|
3850
|
+
const destNormalized = this.#normalizePath(dest);
|
|
3851
|
+
const srcPrefixed = this.#prefixPath(srcNormalized);
|
|
3852
|
+
const destPrefixed = this.#prefixPath(destNormalized);
|
|
3853
|
+
const srcEntry = this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(
|
|
3854
|
+
srcPrefixed
|
|
3855
|
+
);
|
|
3856
|
+
if (!srcEntry) {
|
|
3857
|
+
throw new Error(`ENOENT: no such file or directory: ${src}`);
|
|
3858
|
+
}
|
|
3859
|
+
if (srcEntry.type === "directory" && !options?.recursive) {
|
|
3860
|
+
throw new Error(`cp: -r not specified; omitting directory: ${src}`);
|
|
3861
|
+
}
|
|
3862
|
+
this.#useTransaction(() => {
|
|
3863
|
+
this.#ensureParentExists(destPrefixed);
|
|
3864
|
+
if (srcEntry.type === "directory") {
|
|
3865
|
+
const allEntries = this.#stmt(
|
|
3866
|
+
`SELECT * FROM fs_entries WHERE path = ? OR path LIKE ? || '/%'`
|
|
3867
|
+
).all(srcPrefixed, srcPrefixed);
|
|
3868
|
+
for (const entry of allEntries) {
|
|
3869
|
+
const relativePath = path3.posix.relative(srcPrefixed, entry.path);
|
|
3870
|
+
const newPath = path3.posix.join(destPrefixed, relativePath);
|
|
3871
|
+
this.#stmt(
|
|
3872
|
+
`INSERT OR REPLACE INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
3873
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
3874
|
+
).run(
|
|
3875
|
+
newPath,
|
|
3876
|
+
entry.type,
|
|
3877
|
+
entry.mode,
|
|
3878
|
+
entry.size,
|
|
3879
|
+
Date.now(),
|
|
3880
|
+
entry.symlinkTarget
|
|
3881
|
+
);
|
|
3882
|
+
if (entry.type === "file") {
|
|
3883
|
+
const chunks = this.#stmt(
|
|
3884
|
+
"SELECT chunkIndex, data FROM fs_chunks WHERE path = ?"
|
|
3885
|
+
).all(entry.path);
|
|
3886
|
+
for (const chunk of chunks) {
|
|
3887
|
+
this.#stmt(
|
|
3888
|
+
"INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
|
|
3889
|
+
).run(newPath, chunk.chunkIndex, chunk.data);
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
} else {
|
|
3894
|
+
this.#stmt(
|
|
3895
|
+
`INSERT OR REPLACE INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
3896
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
3897
|
+
).run(
|
|
3898
|
+
destPrefixed,
|
|
3899
|
+
srcEntry.type,
|
|
3900
|
+
srcEntry.mode,
|
|
3901
|
+
srcEntry.size,
|
|
3902
|
+
Date.now(),
|
|
3903
|
+
srcEntry.symlinkTarget
|
|
3904
|
+
);
|
|
3905
|
+
if (srcEntry.type === "file") {
|
|
3906
|
+
const chunks = this.#stmt(
|
|
3907
|
+
"SELECT chunkIndex, data FROM fs_chunks WHERE path = ?"
|
|
3908
|
+
).all(srcPrefixed);
|
|
3909
|
+
this.#stmt("DELETE FROM fs_chunks WHERE path = ?").run(destPrefixed);
|
|
3910
|
+
for (const chunk of chunks) {
|
|
3911
|
+
this.#stmt(
|
|
3912
|
+
"INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
|
|
3913
|
+
).run(destPrefixed, chunk.chunkIndex, chunk.data);
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
});
|
|
3918
|
+
}
|
|
3919
|
+
async mv(src, dest) {
|
|
3920
|
+
const srcNormalized = this.#normalizePath(src);
|
|
3921
|
+
const destNormalized = this.#normalizePath(dest);
|
|
3922
|
+
const srcPrefixed = this.#prefixPath(srcNormalized);
|
|
3923
|
+
const destPrefixed = this.#prefixPath(destNormalized);
|
|
3924
|
+
const srcEntry = this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(
|
|
3925
|
+
srcPrefixed
|
|
3926
|
+
);
|
|
3927
|
+
if (!srcEntry) {
|
|
3928
|
+
throw new Error(`ENOENT: no such file or directory: ${src}`);
|
|
3929
|
+
}
|
|
3930
|
+
this.#useTransaction(() => {
|
|
3931
|
+
this.#ensureParentExists(destPrefixed);
|
|
3932
|
+
if (srcEntry.type === "directory") {
|
|
3933
|
+
const allEntries = this.#stmt(
|
|
3934
|
+
`SELECT * FROM fs_entries WHERE path = ? OR path LIKE ? || '/%' ORDER BY path DESC`
|
|
3935
|
+
).all(srcPrefixed, srcPrefixed);
|
|
3936
|
+
for (const entry of [...allEntries].reverse()) {
|
|
3937
|
+
const relativePath = path3.posix.relative(srcPrefixed, entry.path);
|
|
3938
|
+
const newPath = path3.posix.join(destPrefixed, relativePath);
|
|
3939
|
+
this.#stmt(
|
|
3940
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
3941
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
3942
|
+
).run(
|
|
3943
|
+
newPath,
|
|
3944
|
+
entry.type,
|
|
3945
|
+
entry.mode,
|
|
3946
|
+
entry.size,
|
|
3947
|
+
Date.now(),
|
|
3948
|
+
entry.symlinkTarget
|
|
3949
|
+
);
|
|
3950
|
+
if (entry.type === "file") {
|
|
3951
|
+
const chunks = this.#stmt(
|
|
3952
|
+
"SELECT chunkIndex, data FROM fs_chunks WHERE path = ?"
|
|
3953
|
+
).all(entry.path);
|
|
3954
|
+
for (const chunk of chunks) {
|
|
3955
|
+
this.#stmt(
|
|
3956
|
+
"INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
|
|
3957
|
+
).run(newPath, chunk.chunkIndex, chunk.data);
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
this.#stmt(
|
|
3962
|
+
`DELETE FROM fs_entries WHERE path = ? OR path LIKE ? || '/%'`
|
|
3963
|
+
).run(srcPrefixed, srcPrefixed);
|
|
3964
|
+
} else {
|
|
3965
|
+
this.#stmt(
|
|
3966
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
3967
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
3968
|
+
).run(
|
|
3969
|
+
destPrefixed,
|
|
3970
|
+
srcEntry.type,
|
|
3971
|
+
srcEntry.mode,
|
|
3972
|
+
srcEntry.size,
|
|
3973
|
+
Date.now(),
|
|
3974
|
+
srcEntry.symlinkTarget
|
|
3975
|
+
);
|
|
3976
|
+
if (srcEntry.type === "file") {
|
|
3977
|
+
const chunks = this.#stmt(
|
|
3978
|
+
"SELECT chunkIndex, data FROM fs_chunks WHERE path = ?"
|
|
3979
|
+
).all(srcPrefixed);
|
|
3980
|
+
for (const chunk of chunks) {
|
|
3981
|
+
this.#stmt(
|
|
3982
|
+
"INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
|
|
3983
|
+
).run(destPrefixed, chunk.chunkIndex, chunk.data);
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
this.#stmt("DELETE FROM fs_entries WHERE path = ?").run(srcPrefixed);
|
|
3987
|
+
}
|
|
3988
|
+
});
|
|
3989
|
+
}
|
|
3990
|
+
resolvePath(base, relativePath) {
|
|
3991
|
+
return path3.posix.resolve(base, relativePath);
|
|
3992
|
+
}
|
|
3993
|
+
getAllPaths() {
|
|
3994
|
+
const rows = this.#stmt(
|
|
3995
|
+
"SELECT path FROM fs_entries ORDER BY path"
|
|
3996
|
+
).all();
|
|
3997
|
+
return rows.map((row) => row.path);
|
|
3998
|
+
}
|
|
3999
|
+
async chmod(filePath, mode) {
|
|
4000
|
+
const normalized = this.#normalizePath(filePath);
|
|
4001
|
+
const prefixed = this.#prefixPath(normalized);
|
|
4002
|
+
const result = this.#stmt(
|
|
4003
|
+
"UPDATE fs_entries SET mode = ? WHERE path = ?"
|
|
4004
|
+
).run(mode, prefixed);
|
|
4005
|
+
if (result.changes === 0) {
|
|
4006
|
+
throw new Error(`ENOENT: no such file or directory: ${filePath}`);
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
async symlink(target, linkPath) {
|
|
4010
|
+
const normalized = this.#normalizePath(linkPath);
|
|
4011
|
+
const prefixed = this.#prefixPath(normalized);
|
|
4012
|
+
const existing = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
|
|
4013
|
+
prefixed
|
|
4014
|
+
);
|
|
4015
|
+
if (existing) {
|
|
4016
|
+
throw new Error(`EEXIST: file already exists: ${linkPath}`);
|
|
4017
|
+
}
|
|
4018
|
+
this.#useTransaction(() => {
|
|
4019
|
+
this.#ensureParentExists(prefixed);
|
|
4020
|
+
this.#stmt(
|
|
4021
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
4022
|
+
VALUES (?, 'symlink', 511, 0, ?, ?)`
|
|
4023
|
+
).run(prefixed, Date.now(), target);
|
|
4024
|
+
});
|
|
4025
|
+
}
|
|
4026
|
+
async link(existingPath, newPath) {
|
|
4027
|
+
const srcNormalized = this.#normalizePath(existingPath);
|
|
4028
|
+
const destNormalized = this.#normalizePath(newPath);
|
|
4029
|
+
const srcPrefixed = this.#prefixPath(srcNormalized);
|
|
4030
|
+
const destPrefixed = this.#prefixPath(destNormalized);
|
|
4031
|
+
const srcEntry = this.#stmt("SELECT * FROM fs_entries WHERE path = ?").get(
|
|
4032
|
+
srcPrefixed
|
|
4033
|
+
);
|
|
4034
|
+
if (!srcEntry) {
|
|
4035
|
+
throw new Error(`ENOENT: no such file or directory: ${existingPath}`);
|
|
4036
|
+
}
|
|
4037
|
+
if (srcEntry.type !== "file") {
|
|
4038
|
+
throw new Error(`link: not supported for directories: ${existingPath}`);
|
|
4039
|
+
}
|
|
4040
|
+
const existing = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
|
|
4041
|
+
destPrefixed
|
|
4042
|
+
);
|
|
4043
|
+
if (existing) {
|
|
4044
|
+
throw new Error(`EEXIST: file already exists: ${newPath}`);
|
|
4045
|
+
}
|
|
4046
|
+
this.#useTransaction(() => {
|
|
4047
|
+
this.#ensureParentExists(destPrefixed);
|
|
4048
|
+
this.#stmt(
|
|
4049
|
+
`INSERT INTO fs_entries (path, type, mode, size, mtime)
|
|
4050
|
+
VALUES (?, 'file', ?, ?, ?)`
|
|
4051
|
+
).run(destPrefixed, srcEntry.mode, srcEntry.size, Date.now());
|
|
4052
|
+
const chunks = this.#stmt(
|
|
4053
|
+
"SELECT chunkIndex, data FROM fs_chunks WHERE path = ?"
|
|
4054
|
+
).all(srcPrefixed);
|
|
4055
|
+
for (const chunk of chunks) {
|
|
4056
|
+
this.#stmt(
|
|
4057
|
+
"INSERT INTO fs_chunks (path, chunkIndex, data) VALUES (?, ?, ?)"
|
|
4058
|
+
).run(destPrefixed, chunk.chunkIndex, chunk.data);
|
|
4059
|
+
}
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
4062
|
+
async readlink(linkPath) {
|
|
4063
|
+
const normalized = this.#normalizePath(linkPath);
|
|
4064
|
+
const prefixed = this.#prefixPath(normalized);
|
|
4065
|
+
const entry = this.#stmt(
|
|
4066
|
+
"SELECT type, symlinkTarget FROM fs_entries WHERE path = ?"
|
|
4067
|
+
).get(prefixed);
|
|
4068
|
+
if (!entry) {
|
|
4069
|
+
throw new Error(`ENOENT: no such file or directory: ${linkPath}`);
|
|
4070
|
+
}
|
|
4071
|
+
if (entry.type !== "symlink") {
|
|
4072
|
+
throw new Error(`readlink: not a symbolic link: ${linkPath}`);
|
|
4073
|
+
}
|
|
4074
|
+
return entry.symlinkTarget;
|
|
4075
|
+
}
|
|
4076
|
+
};
|
|
4077
|
+
|
|
4078
|
+
// packages/text2sql/src/lib/fs/scoped-fs.ts
|
|
4079
|
+
var ScopedFs = class {
|
|
4080
|
+
#base;
|
|
4081
|
+
#prefix;
|
|
4082
|
+
constructor(options) {
|
|
4083
|
+
this.#base = options.base;
|
|
4084
|
+
this.#prefix = options.prefix.replace(/\/$/, "");
|
|
4085
|
+
}
|
|
4086
|
+
#scope(path4) {
|
|
4087
|
+
return `${this.#prefix}${path4}`;
|
|
4088
|
+
}
|
|
4089
|
+
async writeFile(path4, content, options) {
|
|
4090
|
+
await this.#base.writeFile(this.#scope(path4), content, options);
|
|
4091
|
+
}
|
|
4092
|
+
async appendFile(path4, content, options) {
|
|
4093
|
+
await this.#base.appendFile(this.#scope(path4), content, options);
|
|
4094
|
+
}
|
|
4095
|
+
async mkdir(path4, options) {
|
|
4096
|
+
return this.#base.mkdir(this.#scope(path4), options);
|
|
4097
|
+
}
|
|
4098
|
+
async rm(path4, options) {
|
|
4099
|
+
await this.#base.rm(this.#scope(path4), options);
|
|
4100
|
+
}
|
|
4101
|
+
async cp(src, dest, options) {
|
|
4102
|
+
await this.#base.cp(this.#scope(src), this.#scope(dest), options);
|
|
4103
|
+
}
|
|
4104
|
+
async mv(src, dest) {
|
|
4105
|
+
await this.#base.mv(this.#scope(src), this.#scope(dest));
|
|
4106
|
+
}
|
|
4107
|
+
async chmod(path4, mode) {
|
|
4108
|
+
return this.#base.chmod(this.#scope(path4), mode);
|
|
4109
|
+
}
|
|
4110
|
+
async symlink(target, linkPath) {
|
|
4111
|
+
await this.#base.symlink(target, this.#scope(linkPath));
|
|
4112
|
+
}
|
|
4113
|
+
async link(existingPath, newPath) {
|
|
4114
|
+
await this.#base.link(this.#scope(existingPath), this.#scope(newPath));
|
|
4115
|
+
}
|
|
4116
|
+
readFile(path4, options) {
|
|
4117
|
+
return this.#base.readFile(this.#scope(path4), options);
|
|
4118
|
+
}
|
|
4119
|
+
readFileBuffer(path4) {
|
|
4120
|
+
return this.#base.readFileBuffer(this.#scope(path4));
|
|
4121
|
+
}
|
|
4122
|
+
stat(path4) {
|
|
4123
|
+
return this.#base.stat(this.#scope(path4));
|
|
4124
|
+
}
|
|
4125
|
+
lstat(path4) {
|
|
4126
|
+
return this.#base.lstat(this.#scope(path4));
|
|
4127
|
+
}
|
|
4128
|
+
readdir(path4) {
|
|
4129
|
+
return this.#base.readdir(this.#scope(path4));
|
|
4130
|
+
}
|
|
4131
|
+
readdirWithFileTypes(path4) {
|
|
4132
|
+
return this.#base.readdirWithFileTypes(this.#scope(path4));
|
|
4133
|
+
}
|
|
4134
|
+
exists(path4) {
|
|
4135
|
+
return this.#base.exists(this.#scope(path4));
|
|
4136
|
+
}
|
|
4137
|
+
readlink(path4) {
|
|
4138
|
+
return this.#base.readlink(this.#scope(path4));
|
|
4139
|
+
}
|
|
4140
|
+
resolvePath(base, relativePath) {
|
|
4141
|
+
return this.#base.resolvePath(base, relativePath);
|
|
4142
|
+
}
|
|
4143
|
+
getAllPaths() {
|
|
4144
|
+
const allPaths = this.#base.getAllPaths?.() ?? [];
|
|
4145
|
+
return allPaths.filter((p) => p.startsWith(this.#prefix)).map((p) => p.slice(this.#prefix.length) || "/");
|
|
4146
|
+
}
|
|
4147
|
+
};
|
|
4148
|
+
|
|
4149
|
+
// packages/text2sql/src/lib/fs/tracked-fs.ts
|
|
4150
|
+
var TrackedFs = class {
|
|
4151
|
+
#base;
|
|
4152
|
+
#createdFiles = /* @__PURE__ */ new Set();
|
|
4153
|
+
constructor(base) {
|
|
4154
|
+
this.#base = base;
|
|
4155
|
+
}
|
|
4156
|
+
getCreatedFiles() {
|
|
4157
|
+
return [...this.#createdFiles];
|
|
4158
|
+
}
|
|
4159
|
+
async writeFile(path4, content, options) {
|
|
4160
|
+
await this.#base.writeFile(path4, content, options);
|
|
4161
|
+
this.#createdFiles.add(path4);
|
|
4162
|
+
}
|
|
4163
|
+
async appendFile(path4, content, options) {
|
|
4164
|
+
await this.#base.appendFile(path4, content, options);
|
|
4165
|
+
this.#createdFiles.add(path4);
|
|
4166
|
+
}
|
|
4167
|
+
async mkdir(path4, options) {
|
|
4168
|
+
return this.#base.mkdir(path4, options);
|
|
4169
|
+
}
|
|
4170
|
+
async rm(path4, options) {
|
|
4171
|
+
await this.#base.rm(path4, options);
|
|
4172
|
+
this.#createdFiles.delete(path4);
|
|
4173
|
+
if (options?.recursive) {
|
|
4174
|
+
const prefix = path4.endsWith("/") ? path4 : path4 + "/";
|
|
4175
|
+
for (const file of this.#createdFiles) {
|
|
4176
|
+
if (file.startsWith(prefix)) {
|
|
4177
|
+
this.#createdFiles.delete(file);
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
async cp(src, dest, options) {
|
|
4183
|
+
await this.#base.cp(src, dest, options);
|
|
4184
|
+
this.#createdFiles.add(dest);
|
|
4185
|
+
}
|
|
4186
|
+
async mv(src, dest) {
|
|
4187
|
+
await this.#base.mv(src, dest);
|
|
4188
|
+
this.#createdFiles.delete(src);
|
|
4189
|
+
this.#createdFiles.add(dest);
|
|
4190
|
+
}
|
|
4191
|
+
async chmod(path4, mode) {
|
|
4192
|
+
return this.#base.chmod(path4, mode);
|
|
4193
|
+
}
|
|
4194
|
+
async symlink(target, linkPath) {
|
|
4195
|
+
await this.#base.symlink(target, linkPath);
|
|
4196
|
+
this.#createdFiles.add(linkPath);
|
|
4197
|
+
}
|
|
4198
|
+
async link(existingPath, newPath) {
|
|
4199
|
+
await this.#base.link(existingPath, newPath);
|
|
4200
|
+
this.#createdFiles.add(newPath);
|
|
4201
|
+
}
|
|
4202
|
+
readFile(path4, options) {
|
|
4203
|
+
return this.#base.readFile(path4, options);
|
|
4204
|
+
}
|
|
4205
|
+
readFileBuffer(path4) {
|
|
4206
|
+
return this.#base.readFileBuffer(path4);
|
|
4207
|
+
}
|
|
4208
|
+
stat(path4) {
|
|
4209
|
+
return this.#base.stat(path4);
|
|
4210
|
+
}
|
|
4211
|
+
lstat(path4) {
|
|
4212
|
+
return this.#base.lstat(path4);
|
|
4213
|
+
}
|
|
4214
|
+
readdir(path4) {
|
|
4215
|
+
return this.#base.readdir(path4);
|
|
4216
|
+
}
|
|
4217
|
+
readdirWithFileTypes(path4) {
|
|
4218
|
+
return this.#base.readdirWithFileTypes(path4);
|
|
4219
|
+
}
|
|
4220
|
+
exists(path4) {
|
|
4221
|
+
return this.#base.exists(path4);
|
|
4222
|
+
}
|
|
4223
|
+
readlink(path4) {
|
|
4224
|
+
return this.#base.readlink(path4);
|
|
4225
|
+
}
|
|
4226
|
+
resolvePath(base, relativePath) {
|
|
4227
|
+
return this.#base.resolvePath(base, relativePath);
|
|
4228
|
+
}
|
|
4229
|
+
getAllPaths() {
|
|
4230
|
+
return this.#base.getAllPaths?.() ?? [];
|
|
4231
|
+
}
|
|
4232
|
+
};
|
|
4233
|
+
|
|
3404
4234
|
// packages/text2sql/src/lib/instructions.ts
|
|
3405
4235
|
function reasoningFramework() {
|
|
3406
4236
|
return [
|
|
@@ -3779,6 +4609,7 @@ import {
|
|
|
3779
4609
|
ToolCallRepairError,
|
|
3780
4610
|
generateId as generateId3
|
|
3781
4611
|
} from "ai";
|
|
4612
|
+
import "just-bash";
|
|
3782
4613
|
import "@deepagents/agent";
|
|
3783
4614
|
var Text2Sql = class {
|
|
3784
4615
|
#config;
|
|
@@ -3790,6 +4621,7 @@ var Text2Sql = class {
|
|
|
3790
4621
|
tools: config.tools ?? {},
|
|
3791
4622
|
model: config.model,
|
|
3792
4623
|
transform: config.transform,
|
|
4624
|
+
filesystem: config.filesystem,
|
|
3793
4625
|
introspection: new JsonCache(
|
|
3794
4626
|
"introspection-" + config.version
|
|
3795
4627
|
)
|
|
@@ -3862,6 +4694,7 @@ var Text2Sql = class {
|
|
|
3862
4694
|
];
|
|
3863
4695
|
}
|
|
3864
4696
|
async chat(messages) {
|
|
4697
|
+
const trackedFs = new TrackedFs(this.#config.filesystem);
|
|
3865
4698
|
const context = this.#config.context(
|
|
3866
4699
|
...guidelines(this.#config.teachingsOptions),
|
|
3867
4700
|
...await this.index(),
|
|
@@ -3872,20 +4705,18 @@ var Text2Sql = class {
|
|
|
3872
4705
|
context.set(message(userMsg));
|
|
3873
4706
|
await context.save();
|
|
3874
4707
|
}
|
|
3875
|
-
const
|
|
3876
|
-
const
|
|
3877
|
-
const { bash } = await createResultTools({
|
|
4708
|
+
const { mounts: skillMounts } = context.getSkillMounts();
|
|
4709
|
+
const { tools: tools3 } = await createResultTools({
|
|
3878
4710
|
adapter: this.#config.adapter,
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
skillMounts
|
|
4711
|
+
skillMounts,
|
|
4712
|
+
filesystem: trackedFs
|
|
3882
4713
|
});
|
|
3883
4714
|
const chatAgent = agent({
|
|
3884
4715
|
name: "text2sql",
|
|
3885
4716
|
model: this.#config.model,
|
|
3886
4717
|
context,
|
|
3887
4718
|
tools: {
|
|
3888
|
-
|
|
4719
|
+
...tools3,
|
|
3889
4720
|
...this.#config.tools
|
|
3890
4721
|
},
|
|
3891
4722
|
guardrails: [errorRecoveryGuardrail],
|
|
@@ -3904,7 +4735,15 @@ var Text2Sql = class {
|
|
|
3904
4735
|
originalMessages: messages,
|
|
3905
4736
|
generateMessageId: generateId3,
|
|
3906
4737
|
onFinish: async ({ responseMessage }) => {
|
|
3907
|
-
|
|
4738
|
+
const createdFiles = trackedFs.getCreatedFiles();
|
|
4739
|
+
const messageWithMetadata = {
|
|
4740
|
+
...responseMessage,
|
|
4741
|
+
metadata: {
|
|
4742
|
+
...responseMessage.metadata ?? {},
|
|
4743
|
+
createdFiles
|
|
4744
|
+
}
|
|
4745
|
+
};
|
|
4746
|
+
context.set(assistant(messageWithMetadata));
|
|
3908
4747
|
await context.save();
|
|
3909
4748
|
await context.trackUsage(await result.totalUsage);
|
|
3910
4749
|
}
|
|
@@ -3966,7 +4805,10 @@ export {
|
|
|
3966
4805
|
JsonCache,
|
|
3967
4806
|
Point,
|
|
3968
4807
|
SQLValidationError,
|
|
4808
|
+
ScopedFs,
|
|
4809
|
+
SqliteFs,
|
|
3969
4810
|
Text2Sql,
|
|
4811
|
+
TrackedFs,
|
|
3970
4812
|
UnanswerableSQLError,
|
|
3971
4813
|
applyTablesFilter,
|
|
3972
4814
|
column,
|