@deepagents/text2sql 0.15.1 → 0.17.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.
Files changed (43) hide show
  1. package/dist/index.js +1149 -193
  2. package/dist/index.js.map +4 -4
  3. package/dist/lib/adapters/adapter.d.ts +3 -0
  4. package/dist/lib/adapters/adapter.d.ts.map +1 -1
  5. package/dist/lib/adapters/bigquery/bigquery.d.ts +1 -0
  6. package/dist/lib/adapters/bigquery/bigquery.d.ts.map +1 -1
  7. package/dist/lib/adapters/bigquery/index.js +11 -0
  8. package/dist/lib/adapters/bigquery/index.js.map +3 -3
  9. package/dist/lib/adapters/mysql/index.js +11 -0
  10. package/dist/lib/adapters/mysql/index.js.map +3 -3
  11. package/dist/lib/adapters/mysql/mysql.d.ts +1 -0
  12. package/dist/lib/adapters/mysql/mysql.d.ts.map +1 -1
  13. package/dist/lib/adapters/postgres/index.js +11 -0
  14. package/dist/lib/adapters/postgres/index.js.map +3 -3
  15. package/dist/lib/adapters/postgres/postgres.d.ts +1 -0
  16. package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -1
  17. package/dist/lib/adapters/spreadsheet/index.js +11 -0
  18. package/dist/lib/adapters/spreadsheet/index.js.map +3 -3
  19. package/dist/lib/adapters/sqlite/index.js +11 -0
  20. package/dist/lib/adapters/sqlite/index.js.map +3 -3
  21. package/dist/lib/adapters/sqlite/sqlite.d.ts +1 -0
  22. package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -1
  23. package/dist/lib/adapters/sqlserver/index.js +11 -0
  24. package/dist/lib/adapters/sqlserver/index.js.map +3 -3
  25. package/dist/lib/adapters/sqlserver/sqlserver.d.ts +1 -0
  26. package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -1
  27. package/dist/lib/agents/result-tools.d.ts +9 -1
  28. package/dist/lib/agents/result-tools.d.ts.map +1 -1
  29. package/dist/lib/agents/sql.agent.d.ts.map +1 -1
  30. package/dist/lib/fs/index.d.ts +1 -0
  31. package/dist/lib/fs/index.d.ts.map +1 -1
  32. package/dist/lib/fs/mssql/ddl.mssql-fs.d.ts.map +1 -1
  33. package/dist/lib/fs/mssql/mssql-fs.d.ts +1 -0
  34. package/dist/lib/fs/mssql/mssql-fs.d.ts.map +1 -1
  35. package/dist/lib/fs/postgres/ddl.postgres-fs.d.ts +2 -0
  36. package/dist/lib/fs/postgres/ddl.postgres-fs.d.ts.map +1 -0
  37. package/dist/lib/fs/postgres/postgres-fs.d.ts +50 -0
  38. package/dist/lib/fs/postgres/postgres-fs.d.ts.map +1 -0
  39. package/dist/lib/sql.d.ts +1 -1
  40. package/dist/lib/sql.d.ts.map +1 -1
  41. package/dist/lib/synthesis/index.js +1 -9
  42. package/dist/lib/synthesis/index.js.map +2 -2
  43. package/package.json +23 -12
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // packages/text2sql/src/lib/adapters/adapter.ts
2
+ import { format as formatSql } from "sql-formatter";
3
+
1
4
  // packages/text2sql/src/lib/fragments/schema.ts
2
5
  function dialectInfo(input) {
3
6
  return {
@@ -265,6 +268,13 @@ var Adapter = class {
265
268
  cardinality
266
269
  });
267
270
  }
271
+ format(sql) {
272
+ try {
273
+ return formatSql(sql, { language: this.formatterLanguage });
274
+ } catch {
275
+ return sql;
276
+ }
277
+ }
268
278
  /**
269
279
  * Convert unknown database value to number.
270
280
  * Handles number, bigint, and string types.
@@ -512,6 +522,7 @@ import {
512
522
  OverlayFs,
513
523
  defineCommand
514
524
  } from "just-bash";
525
+ import { AsyncLocalStorage } from "node:async_hooks";
515
526
  import * as path from "node:path";
516
527
  import { v7 } from "uuid";
517
528
  import z3 from "zod";
@@ -540,21 +551,21 @@ function validateReadOnly(query) {
540
551
  }
541
552
  return { valid: true };
542
553
  }
543
- function createSqlCommand(adapter) {
554
+ function createSqlCommand(adapter, metaStore) {
544
555
  return createCommand("sql", {
545
556
  run: {
546
557
  usage: 'run "SELECT ..."',
547
558
  description: "Execute query and store results",
548
559
  handler: async (args, ctx) => {
549
- const query = args.join(" ").trim();
550
- if (!query) {
560
+ const rawQuery = args.join(" ").trim();
561
+ if (!rawQuery) {
551
562
  return {
552
563
  stdout: "",
553
564
  stderr: "sql run: no query provided",
554
565
  exitCode: 1
555
566
  };
556
567
  }
557
- const validation = validateReadOnly(query);
568
+ const validation = validateReadOnly(rawQuery);
558
569
  if (!validation.valid) {
559
570
  return {
560
571
  stdout: "",
@@ -562,6 +573,9 @@ function createSqlCommand(adapter) {
562
573
  exitCode: 1
563
574
  };
564
575
  }
576
+ const query = adapter.format(rawQuery);
577
+ const store = metaStore.getStore();
578
+ if (store) store.value = { formattedSql: query };
565
579
  const syntaxError = await adapter.validate(query);
566
580
  if (syntaxError) {
567
581
  return {
@@ -601,15 +615,15 @@ function createSqlCommand(adapter) {
601
615
  usage: 'validate "SELECT ..."',
602
616
  description: "Validate query syntax",
603
617
  handler: async (args) => {
604
- const query = args.join(" ").trim();
605
- if (!query) {
618
+ const rawQuery = args.join(" ").trim();
619
+ if (!rawQuery) {
606
620
  return {
607
621
  stdout: "",
608
622
  stderr: "sql validate: no query provided",
609
623
  exitCode: 1
610
624
  };
611
625
  }
612
- const validation = validateReadOnly(query);
626
+ const validation = validateReadOnly(rawQuery);
613
627
  if (!validation.valid) {
614
628
  return {
615
629
  stdout: "",
@@ -617,6 +631,9 @@ function createSqlCommand(adapter) {
617
631
  exitCode: 1
618
632
  };
619
633
  }
634
+ const query = adapter.format(rawQuery);
635
+ const store = metaStore.getStore();
636
+ if (store) store.value = { formattedSql: query };
620
637
  const syntaxError = await adapter.validate(query);
621
638
  if (syntaxError) {
622
639
  return {
@@ -636,7 +653,8 @@ function createSqlCommand(adapter) {
636
653
  }
637
654
  async function createResultTools(options) {
638
655
  const { adapter, skillMounts, filesystem: baseFs } = options;
639
- const sqlCommand = createSqlCommand(adapter);
656
+ const metaStore = new AsyncLocalStorage();
657
+ const sqlCommand = createSqlCommand(adapter, metaStore);
640
658
  const fsMounts = skillMounts.map(({ host, sandbox: sandbox2 }) => ({
641
659
  mountPoint: path.dirname(sandbox2),
642
660
  filesystem: new OverlayFs({
@@ -675,10 +693,19 @@ async function createResultTools(options) {
675
693
  reasoning: z3.string().trim().describe("Brief reason for executing this command")
676
694
  }),
677
695
  execute: async ({ command }, execOptions) => {
678
- if (!tools2.bash.execute) {
696
+ const execute = tools2.bash.execute;
697
+ if (!execute) {
679
698
  throw new Error("bash tool execution is not available");
680
699
  }
681
- return tools2.bash.execute({ command }, execOptions);
700
+ return metaStore.run({}, async () => {
701
+ const result = await execute({ command }, execOptions);
702
+ const meta = metaStore.getStore()?.value;
703
+ return meta ? { ...result, meta } : result;
704
+ });
705
+ },
706
+ toModelOutput: ({ output }) => {
707
+ const { meta, ...rest } = output;
708
+ return { type: "json", value: rest };
682
709
  }
683
710
  });
684
711
  return {
@@ -702,8 +729,6 @@ import {
702
729
  defaultSettingsMiddleware,
703
730
  wrapLanguageModel
704
731
  } from "ai";
705
- import { Console } from "node:console";
706
- import { createWriteStream } from "node:fs";
707
732
  import pRetry from "p-retry";
708
733
  import z4 from "zod";
709
734
  import "@deepagents/agent";
@@ -714,11 +739,6 @@ import {
714
739
  structuredOutput as structuredOutput2,
715
740
  user as user2
716
741
  } from "@deepagents/context";
717
- var logger = new Console({
718
- stdout: createWriteStream("./sql-agent.log", { flags: "a" }),
719
- stderr: createWriteStream("./sql-agent-error.log", { flags: "a" }),
720
- inspectOptions: { depth: null }
721
- });
722
742
  var RETRY_TEMPERATURES = [0, 0.2, 0.3];
723
743
  function extractSql(output) {
724
744
  const match = output.match(/```sql\n?([\s\S]*?)```/);
@@ -798,7 +818,7 @@ async function toSql(options) {
798
818
  if ("error" in output) {
799
819
  throw new UnanswerableSQLError(output.error);
800
820
  }
801
- const sql = extractSql(output.sql);
821
+ const sql = options.adapter.format(extractSql(output.sql));
802
822
  const validationError = await options.adapter.validate(sql);
803
823
  if (validationError) {
804
824
  throw new SQLValidationError(validationError);
@@ -857,7 +877,6 @@ async function withRetry(computation, options = { retries: 3 }) {
857
877
  return APICallError.isInstance(context.error) || JSONParseError.isInstance(context.error) || TypeValidationError.isInstance(context.error) || NoObjectGeneratedError.isInstance(context.error) || NoOutputGeneratedError.isInstance(context.error) || NoContentGeneratedError.isInstance(context.error);
858
878
  },
859
879
  onFailedAttempt(context) {
860
- logger.error(`toSQL`, context.error);
861
880
  console.log(
862
881
  `Attempt ${context.attemptNumber} failed. There are ${context.retriesLeft} retries left.`
863
882
  );
@@ -927,9 +946,9 @@ var Checkpoint = class _Checkpoint {
927
946
  points;
928
947
  path;
929
948
  configHash;
930
- constructor(path5, configHash, points) {
949
+ constructor(path6, configHash, points) {
931
950
  this.points = points;
932
- this.path = path5;
951
+ this.path = path6;
933
952
  this.configHash = configHash;
934
953
  }
935
954
  /**
@@ -937,14 +956,14 @@ var Checkpoint = class _Checkpoint {
937
956
  * Handles corrupted files and config changes gracefully.
938
957
  */
939
958
  static async load(options) {
940
- const { path: path5, configHash } = options;
941
- if (existsSync(path5)) {
959
+ const { path: path6, configHash } = options;
960
+ if (existsSync(path6)) {
942
961
  try {
943
- const content = readFileSync(path5, "utf-8");
962
+ const content = readFileSync(path6, "utf-8");
944
963
  const file = JSON.parse(content);
945
964
  if (configHash && file.configHash && file.configHash !== configHash) {
946
965
  console.log("\u26A0 Config changed, starting fresh");
947
- return new _Checkpoint(path5, configHash, {});
966
+ return new _Checkpoint(path6, configHash, {});
948
967
  }
949
968
  const points = file.points ?? {};
950
969
  const totalEntries = Object.values(points).reduce(
@@ -952,14 +971,14 @@ var Checkpoint = class _Checkpoint {
952
971
  0
953
972
  );
954
973
  console.log(`\u2713 Resuming from checkpoint (${totalEntries} entries)`);
955
- return new _Checkpoint(path5, configHash, points);
974
+ return new _Checkpoint(path6, configHash, points);
956
975
  } catch {
957
976
  console.log("\u26A0 Checkpoint corrupted, starting fresh");
958
- return new _Checkpoint(path5, configHash, {});
977
+ return new _Checkpoint(path6, configHash, {});
959
978
  }
960
979
  }
961
980
  console.log("Starting new checkpoint");
962
- return new _Checkpoint(path5, configHash, {});
981
+ return new _Checkpoint(path6, configHash, {});
963
982
  }
964
983
  /**
965
984
  * Run a single computation with checkpointing.
@@ -1820,10 +1839,9 @@ import * as path4 from "node:path";
1820
1839
 
1821
1840
  // packages/text2sql/src/lib/fs/mssql/ddl.mssql-fs.ts
1822
1841
  function mssqlFsDDL(schema) {
1823
- const s = schema;
1824
1842
  return `
1825
- IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[${s}].[fs_entries]') AND type = 'U')
1826
- CREATE TABLE [${s}].[fs_entries] (
1843
+ IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[${schema}].[fs_entries]') AND type = 'U')
1844
+ CREATE TABLE [${schema}].[fs_entries] (
1827
1845
  path NVARCHAR(900) PRIMARY KEY,
1828
1846
  type NVARCHAR(20) NOT NULL,
1829
1847
  mode INT NOT NULL,
@@ -1833,17 +1851,17 @@ CREATE TABLE [${s}].[fs_entries] (
1833
1851
  );
1834
1852
  GO
1835
1853
 
1836
- IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_${s}_fs_entries_type')
1837
- CREATE INDEX [idx_${s}_fs_entries_type] ON [${s}].[fs_entries](type);
1854
+ IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'idx_${schema}_fs_entries_type')
1855
+ CREATE INDEX [idx_${schema}_fs_entries_type] ON [${schema}].[fs_entries](type);
1838
1856
  GO
1839
1857
 
1840
- IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[${s}].[fs_chunks]') AND type = 'U')
1841
- CREATE TABLE [${s}].[fs_chunks] (
1858
+ IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[${schema}].[fs_chunks]') AND type = 'U')
1859
+ CREATE TABLE [${schema}].[fs_chunks] (
1842
1860
  path NVARCHAR(900) NOT NULL,
1843
1861
  chunkIndex INT NOT NULL,
1844
1862
  data VARBINARY(MAX) NOT NULL,
1845
1863
  PRIMARY KEY (path, chunkIndex),
1846
- FOREIGN KEY (path) REFERENCES [${s}].[fs_entries](path) ON DELETE CASCADE ON UPDATE CASCADE
1864
+ FOREIGN KEY (path) REFERENCES [${schema}].[fs_entries](path) ON DELETE CASCADE ON UPDATE CASCADE
1847
1865
  );
1848
1866
  GO
1849
1867
  `;
@@ -1856,7 +1874,7 @@ var MssqlFs = class _MssqlFs {
1856
1874
  #root;
1857
1875
  #schema;
1858
1876
  #ownsPool;
1859
- #initialized;
1877
+ #isInitialized = false;
1860
1878
  constructor(options) {
1861
1879
  this.#chunkSize = options.chunkSize ?? 1024 * 1024;
1862
1880
  const schema = options.schema ?? "dbo";
@@ -1874,7 +1892,6 @@ var MssqlFs = class _MssqlFs {
1874
1892
  this.#pool = new mssql.ConnectionPool(options.pool);
1875
1893
  this.#ownsPool = true;
1876
1894
  }
1877
- this.#initialized = this.#initialize();
1878
1895
  }
1879
1896
  static #requireMssql() {
1880
1897
  try {
@@ -1889,7 +1906,7 @@ var MssqlFs = class _MssqlFs {
1889
1906
  #t(name) {
1890
1907
  return `[${this.#schema}].[${name}]`;
1891
1908
  }
1892
- async #initialize() {
1909
+ async initialize() {
1893
1910
  if (this.#ownsPool) {
1894
1911
  await this.#pool.connect();
1895
1912
  }
@@ -1931,9 +1948,14 @@ var MssqlFs = class _MssqlFs {
1931
1948
  );
1932
1949
  }
1933
1950
  }
1951
+ this.#isInitialized = true;
1934
1952
  }
1935
- async #ensureInitialized() {
1936
- await this.#initialized;
1953
+ #ensureInitialized() {
1954
+ if (!this.#isInitialized) {
1955
+ throw new Error(
1956
+ "MssqlFs not initialized. Call await fs.initialize() after construction."
1957
+ );
1958
+ }
1937
1959
  }
1938
1960
  async #createParentDirs(p) {
1939
1961
  const segments = p.split("/").filter(Boolean);
@@ -1969,15 +1991,15 @@ var MssqlFs = class _MssqlFs {
1969
1991
  return result.rowsAffected[0] ?? 0;
1970
1992
  }
1971
1993
  async #query(sql, params) {
1972
- await this.#ensureInitialized();
1994
+ this.#ensureInitialized();
1973
1995
  return this.#rawQuery(sql, params);
1974
1996
  }
1975
1997
  async #exec(sql, params) {
1976
- await this.#ensureInitialized();
1998
+ this.#ensureInitialized();
1977
1999
  return this.#rawExec(sql, params);
1978
2000
  }
1979
2001
  async #useTransaction(fn) {
1980
- await this.#ensureInitialized();
2002
+ this.#ensureInitialized();
1981
2003
  const mssql = _MssqlFs.#requireMssql();
1982
2004
  const transaction = new mssql.Transaction(this.#pool);
1983
2005
  try {
@@ -2063,11 +2085,21 @@ var MssqlFs = class _MssqlFs {
2063
2085
  );
2064
2086
  }
2065
2087
  }
2066
- async #readChunks(filePath) {
2067
- const rows = await this.#query(
2068
- `SELECT data FROM ${this.#t("fs_chunks")} WHERE path = @p0 ORDER BY chunkIndex`,
2069
- [filePath]
2070
- );
2088
+ async #readChunks(filePath, transaction) {
2089
+ let rows;
2090
+ if (transaction) {
2091
+ const req = transaction.request();
2092
+ req.input("p0", filePath);
2093
+ const result2 = await req.query(
2094
+ `SELECT data FROM ${this.#t("fs_chunks")} WHERE path = @p0 ORDER BY chunkIndex`
2095
+ );
2096
+ rows = result2.recordset;
2097
+ } else {
2098
+ rows = await this.#query(
2099
+ `SELECT data FROM ${this.#t("fs_chunks")} WHERE path = @p0 ORDER BY chunkIndex`,
2100
+ [filePath]
2101
+ );
2102
+ }
2071
2103
  if (rows.length === 0) {
2072
2104
  return new Uint8Array(0);
2073
2105
  }
@@ -2109,10 +2141,6 @@ var MssqlFs = class _MssqlFs {
2109
2141
  return new Uint8Array(Buffer.from(content, enc));
2110
2142
  }
2111
2143
  async close() {
2112
- try {
2113
- await this.#initialized;
2114
- } catch {
2115
- }
2116
2144
  if (this.#ownsPool) {
2117
2145
  await this.#pool.close();
2118
2146
  }
@@ -2196,7 +2224,7 @@ var MssqlFs = class _MssqlFs {
2196
2224
  if (entry && entry.type !== "file") {
2197
2225
  throw new Error(`appendFile: not a file: ${filePath}`);
2198
2226
  }
2199
- const existing = entry ? await this.#readChunks(prefixed) : new Uint8Array(0);
2227
+ const existing = entry ? await this.#readChunks(prefixed, transaction) : new Uint8Array(0);
2200
2228
  const combined = new Uint8Array(existing.length + newData.length);
2201
2229
  combined.set(existing, 0);
2202
2230
  combined.set(newData, existing.length);
@@ -2242,9 +2270,9 @@ var MssqlFs = class _MssqlFs {
2242
2270
  isFile: entry.type === "file",
2243
2271
  isDirectory: entry.type === "directory",
2244
2272
  isSymbolicLink: false,
2245
- mode: entry.mode,
2246
- size: entry.size,
2247
- mtime: new Date(entry.mtime)
2273
+ mode: Number(entry.mode),
2274
+ size: Number(entry.size),
2275
+ mtime: new Date(Number(entry.mtime))
2248
2276
  };
2249
2277
  }
2250
2278
  async lstat(filePath) {
@@ -2262,9 +2290,9 @@ var MssqlFs = class _MssqlFs {
2262
2290
  isFile: entry.type === "file",
2263
2291
  isDirectory: entry.type === "directory",
2264
2292
  isSymbolicLink: entry.type === "symlink",
2265
- mode: entry.mode,
2266
- size: entry.size,
2267
- mtime: new Date(entry.mtime)
2293
+ mode: Number(entry.mode),
2294
+ size: Number(entry.size),
2295
+ mtime: new Date(Number(entry.mtime))
2268
2296
  };
2269
2297
  }
2270
2298
  async mkdir(dirPath, options) {
@@ -2551,6 +2579,11 @@ var MssqlFs = class _MssqlFs {
2551
2579
  const allEntriesResult = await allEntriesReq.query(
2552
2580
  `SELECT * FROM ${this.#t("fs_entries")} WHERE path = @p0 OR path LIKE @p0 + '/%' ORDER BY path DESC`
2553
2581
  );
2582
+ const destDeleteReq = transaction.request();
2583
+ destDeleteReq.input("dp0", destPrefixed);
2584
+ await destDeleteReq.query(
2585
+ `DELETE FROM ${this.#t("fs_entries")} WHERE path = @dp0 OR path LIKE @dp0 + '/%'`
2586
+ );
2554
2587
  for (const entry of [...allEntriesResult.recordset].reverse()) {
2555
2588
  const relativePath = path4.posix.relative(srcPrefixed, entry.path);
2556
2589
  const newPath = path4.posix.join(destPrefixed, relativePath);
@@ -2561,11 +2594,22 @@ var MssqlFs = class _MssqlFs {
2561
2594
  insertReq.input("p3", entry.size);
2562
2595
  insertReq.input("p4", Date.now());
2563
2596
  insertReq.input("p5", entry.symlinkTarget);
2564
- await insertReq.query(
2565
- `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlinkTarget)
2566
- VALUES (@p0, @p1, @p2, @p3, @p4, @p5)`
2567
- );
2597
+ await insertReq.query(`
2598
+ MERGE ${this.#t("fs_entries")} AS target
2599
+ USING (SELECT @p0 AS path) AS source
2600
+ ON target.path = source.path
2601
+ WHEN MATCHED THEN
2602
+ UPDATE SET type = @p1, mode = @p2, size = @p3, mtime = @p4, symlinkTarget = @p5
2603
+ WHEN NOT MATCHED THEN
2604
+ INSERT (path, type, mode, size, mtime, symlinkTarget)
2605
+ VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
2606
+ `);
2568
2607
  if (entry.type === "file") {
2608
+ const deleteChunksReq = transaction.request();
2609
+ deleteChunksReq.input("p0", newPath);
2610
+ await deleteChunksReq.query(
2611
+ `DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
2612
+ );
2569
2613
  const chunksReq = transaction.request();
2570
2614
  chunksReq.input("p0", entry.path);
2571
2615
  const chunksResult = await chunksReq.query(
@@ -2595,11 +2639,22 @@ var MssqlFs = class _MssqlFs {
2595
2639
  insertReq.input("p3", srcEntry.size);
2596
2640
  insertReq.input("p4", Date.now());
2597
2641
  insertReq.input("p5", srcEntry.symlinkTarget);
2598
- await insertReq.query(
2599
- `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlinkTarget)
2600
- VALUES (@p0, @p1, @p2, @p3, @p4, @p5)`
2601
- );
2642
+ await insertReq.query(`
2643
+ MERGE ${this.#t("fs_entries")} AS target
2644
+ USING (SELECT @p0 AS path) AS source
2645
+ ON target.path = source.path
2646
+ WHEN MATCHED THEN
2647
+ UPDATE SET type = @p1, mode = @p2, size = @p3, mtime = @p4, symlinkTarget = @p5
2648
+ WHEN NOT MATCHED THEN
2649
+ INSERT (path, type, mode, size, mtime, symlinkTarget)
2650
+ VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
2651
+ `);
2602
2652
  if (srcEntry.type === "file") {
2653
+ const deleteChunksReq = transaction.request();
2654
+ deleteChunksReq.input("p0", destPrefixed);
2655
+ await deleteChunksReq.query(
2656
+ `DELETE FROM ${this.#t("fs_chunks")} WHERE path = @p0`
2657
+ );
2603
2658
  const chunksReq = transaction.request();
2604
2659
  chunksReq.input("p0", srcPrefixed);
2605
2660
  const chunksResult = await chunksReq.query(
@@ -2763,119 +2818,989 @@ var MssqlFs = class _MssqlFs {
2763
2818
  }
2764
2819
  };
2765
2820
 
2766
- // packages/text2sql/src/lib/fs/scoped-fs.ts
2767
- var ScopedFs = class {
2768
- #base;
2769
- #prefix;
2821
+ // packages/text2sql/src/lib/fs/postgres/postgres-fs.ts
2822
+ import { createRequire as createRequire2 } from "node:module";
2823
+ import * as path5 from "node:path";
2824
+
2825
+ // packages/text2sql/src/lib/fs/postgres/ddl.postgres-fs.ts
2826
+ function postgresFsDDL(schema) {
2827
+ return `
2828
+ CREATE SCHEMA IF NOT EXISTS "${schema}";
2829
+
2830
+ CREATE TABLE IF NOT EXISTS "${schema}"."fs_entries" (
2831
+ path TEXT PRIMARY KEY,
2832
+ type TEXT NOT NULL,
2833
+ mode INTEGER NOT NULL,
2834
+ size BIGINT NOT NULL,
2835
+ mtime BIGINT NOT NULL,
2836
+ symlink_target TEXT
2837
+ );
2838
+
2839
+ CREATE INDEX IF NOT EXISTS "idx_${schema}_fs_entries_type" ON "${schema}"."fs_entries"(type);
2840
+
2841
+ CREATE TABLE IF NOT EXISTS "${schema}"."fs_chunks" (
2842
+ path TEXT NOT NULL,
2843
+ chunk_index INTEGER NOT NULL,
2844
+ data BYTEA NOT NULL,
2845
+ PRIMARY KEY (path, chunk_index),
2846
+ FOREIGN KEY (path) REFERENCES "${schema}"."fs_entries"(path) ON DELETE CASCADE ON UPDATE CASCADE
2847
+ );
2848
+ `;
2849
+ }
2850
+
2851
+ // packages/text2sql/src/lib/fs/postgres/postgres-fs.ts
2852
+ var PostgresFs = class _PostgresFs {
2853
+ #pool;
2854
+ #chunkSize;
2855
+ #root;
2856
+ #schema;
2857
+ #ownsPool;
2858
+ #isInitialized = false;
2770
2859
  constructor(options) {
2771
- this.#base = options.base;
2772
- this.#prefix = options.prefix.replace(/\/$/, "");
2773
- }
2774
- #scope(path5) {
2775
- return `${this.#prefix}${path5}`;
2776
- }
2777
- #unscope(path5) {
2778
- if (path5 === this.#prefix) {
2779
- return "/";
2860
+ this.#chunkSize = options.chunkSize ?? 1024 * 1024;
2861
+ const schema = options.schema ?? "public";
2862
+ if (!/^[a-zA-Z_]\w*$/.test(schema)) {
2863
+ throw new Error(`Invalid schema name: "${schema}"`);
2780
2864
  }
2781
- if (path5.startsWith(this.#prefix + "/")) {
2782
- return path5.slice(this.#prefix.length) || "/";
2865
+ this.#schema = schema;
2866
+ const normalizedRoot = this.#normalizeRoot(options.root);
2867
+ this.#root = normalizedRoot === "/" ? "" : normalizedRoot;
2868
+ const pg = _PostgresFs.#requirePg();
2869
+ if (options.pool instanceof pg.Pool) {
2870
+ this.#pool = options.pool;
2871
+ this.#ownsPool = false;
2872
+ } else {
2873
+ this.#pool = typeof options.pool === "string" ? new pg.Pool({ connectionString: options.pool }) : new pg.Pool(options.pool);
2874
+ this.#ownsPool = true;
2783
2875
  }
2784
- return path5;
2785
- }
2786
- async writeFile(path5, content, options) {
2787
- await this.#base.writeFile(this.#scope(path5), content, options);
2788
- }
2789
- async appendFile(path5, content, options) {
2790
- await this.#base.appendFile(this.#scope(path5), content, options);
2791
- }
2792
- async mkdir(path5, options) {
2793
- return this.#base.mkdir(this.#scope(path5), options);
2794
- }
2795
- async rm(path5, options) {
2796
- await this.#base.rm(this.#scope(path5), options);
2797
- }
2798
- async cp(src, dest, options) {
2799
- await this.#base.cp(this.#scope(src), this.#scope(dest), options);
2800
2876
  }
2801
- async mv(src, dest) {
2802
- await this.#base.mv(this.#scope(src), this.#scope(dest));
2877
+ static #requirePg() {
2878
+ try {
2879
+ const require2 = createRequire2(import.meta.url);
2880
+ return require2("pg");
2881
+ } catch {
2882
+ throw new Error(
2883
+ 'PostgresFs requires the "pg" package. Install it with: npm install pg'
2884
+ );
2885
+ }
2803
2886
  }
2804
- async chmod(path5, mode) {
2805
- return this.#base.chmod(this.#scope(path5), mode);
2887
+ #t(name) {
2888
+ return `"${this.#schema}"."${name}"`;
2806
2889
  }
2807
- async symlink(target, linkPath) {
2808
- await this.#base.symlink(target, this.#scope(linkPath));
2890
+ async initialize() {
2891
+ const ddl = postgresFsDDL(this.#schema);
2892
+ await this.#pool.query(ddl);
2893
+ const rootSlashExists = await this.#rawQuery(
2894
+ `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = '/') AS exists`
2895
+ );
2896
+ if (!rootSlashExists[0].exists) {
2897
+ await this.#rawExec(
2898
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ('/', 'directory', 493, 0, $1)`,
2899
+ [Date.now()]
2900
+ );
2901
+ }
2902
+ if (this.#root) {
2903
+ await this.#createParentDirs(this.#root);
2904
+ const rootExists = await this.#rawQuery(
2905
+ `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
2906
+ [this.#root]
2907
+ );
2908
+ if (!rootExists[0].exists) {
2909
+ await this.#rawExec(
2910
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
2911
+ [this.#root, Date.now()]
2912
+ );
2913
+ }
2914
+ }
2915
+ this.#isInitialized = true;
2809
2916
  }
2810
- async link(existingPath, newPath) {
2811
- await this.#base.link(this.#scope(existingPath), this.#scope(newPath));
2917
+ #ensureInitialized() {
2918
+ if (!this.#isInitialized) {
2919
+ throw new Error(
2920
+ "PostgresFs not initialized. Call await fs.initialize() after construction."
2921
+ );
2922
+ }
2812
2923
  }
2813
- readFile(path5, options) {
2814
- return this.#base.readFile(this.#scope(path5), options);
2924
+ async #createParentDirs(p) {
2925
+ const segments = p.split("/").filter(Boolean);
2926
+ let currentPath = "/";
2927
+ for (let i = 0; i < segments.length - 1; i++) {
2928
+ currentPath = path5.posix.join(currentPath, segments[i]);
2929
+ const exists = await this.#rawQuery(
2930
+ `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
2931
+ [currentPath]
2932
+ );
2933
+ if (!exists[0].exists) {
2934
+ await this.#rawExec(
2935
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
2936
+ [currentPath, Date.now()]
2937
+ );
2938
+ }
2939
+ }
2815
2940
  }
2816
- readFileBuffer(path5) {
2817
- return this.#base.readFileBuffer(this.#scope(path5));
2941
+ async #rawQuery(sql, params) {
2942
+ const result = await this.#pool.query(sql, params);
2943
+ return result.rows;
2818
2944
  }
2819
- stat(path5) {
2820
- return this.#base.stat(this.#scope(path5));
2945
+ async #rawExec(sql, params) {
2946
+ const result = await this.#pool.query(sql, params);
2947
+ return result.rowCount ?? 0;
2821
2948
  }
2822
- lstat(path5) {
2823
- return this.#base.lstat(this.#scope(path5));
2949
+ async #query(sql, params) {
2950
+ this.#ensureInitialized();
2951
+ return this.#rawQuery(sql, params);
2824
2952
  }
2825
- readdir(path5) {
2826
- return this.#base.readdir(this.#scope(path5));
2953
+ async #exec(sql, params) {
2954
+ this.#ensureInitialized();
2955
+ return this.#rawExec(sql, params);
2827
2956
  }
2828
- readdirWithFileTypes(path5) {
2829
- return this.#base.readdirWithFileTypes(this.#scope(path5));
2957
+ async #useTransaction(fn) {
2958
+ this.#ensureInitialized();
2959
+ const client = await this.#pool.connect();
2960
+ try {
2961
+ await client.query("BEGIN");
2962
+ const result = await fn(client);
2963
+ await client.query("COMMIT");
2964
+ return result;
2965
+ } catch (error) {
2966
+ await client.query("ROLLBACK");
2967
+ throw error;
2968
+ } finally {
2969
+ client.release();
2970
+ }
2830
2971
  }
2831
- exists(path5) {
2832
- return this.#base.exists(this.#scope(path5));
2972
+ #normalizeRoot(root) {
2973
+ return path5.posix.resolve("/", root.trim());
2833
2974
  }
2834
- readlink(path5) {
2835
- return this.#base.readlink(this.#scope(path5));
2975
+ #prefixPath(p) {
2976
+ if (!this.#root) {
2977
+ return p;
2978
+ }
2979
+ if (p === "/") {
2980
+ return this.#root;
2981
+ }
2982
+ return path5.posix.join(this.#root, p);
2836
2983
  }
2837
- realpath(path5) {
2838
- return this.#base.realpath(this.#scope(path5)).then((p) => this.#unscope(p));
2984
+ #unprefixPath(p) {
2985
+ if (!this.#root) {
2986
+ return p;
2987
+ }
2988
+ if (p === this.#root) {
2989
+ return "/";
2990
+ }
2991
+ if (p.startsWith(this.#root + "/")) {
2992
+ return p.slice(this.#root.length) || "/";
2993
+ }
2994
+ return p;
2839
2995
  }
2840
- utimes(path5, atime, mtime) {
2841
- return this.#base.utimes(this.#scope(path5), atime, mtime);
2996
+ #normalizePath(p) {
2997
+ return path5.posix.resolve("/", p);
2842
2998
  }
2843
- resolvePath(base, relativePath) {
2844
- return this.#base.resolvePath(base, relativePath);
2999
+ #dirname(p) {
3000
+ const dir = path5.posix.dirname(p);
3001
+ return dir === "" ? "/" : dir;
2845
3002
  }
2846
- getAllPaths() {
2847
- const allPaths = this.#base.getAllPaths?.() ?? [];
2848
- return allPaths.filter((p) => p.startsWith(this.#prefix)).map((p) => p.slice(this.#prefix.length) || "/");
3003
+ async #ensureParentExists(filePath, client) {
3004
+ const parent = this.#dirname(filePath);
3005
+ const rootPath = this.#root || "/";
3006
+ if (parent === rootPath || parent === "/") return;
3007
+ const result = await client.query(
3008
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3009
+ [parent]
3010
+ );
3011
+ const entry = result.rows[0];
3012
+ if (!entry) {
3013
+ await this.#ensureParentExists(parent, client);
3014
+ await client.query(
3015
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
3016
+ [parent, Date.now()]
3017
+ );
3018
+ } else if (entry.type !== "directory") {
3019
+ throw new Error(`mkdir: parent is not a directory: ${parent}`);
3020
+ }
2849
3021
  }
2850
- };
2851
-
2852
- // packages/text2sql/src/lib/fs/tracked-fs.ts
2853
- var TrackedFs = class {
2854
- #base;
2855
- #createdFiles = /* @__PURE__ */ new Set();
2856
- constructor(base) {
2857
- this.#base = base;
3022
+ async #writeChunks(filePath, content, client) {
3023
+ await client.query(`DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`, [
3024
+ filePath
3025
+ ]);
3026
+ for (let i = 0; i < content.length; i += this.#chunkSize) {
3027
+ const chunk = content.slice(
3028
+ i,
3029
+ Math.min(i + this.#chunkSize, content.length)
3030
+ );
3031
+ await client.query(
3032
+ `INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
3033
+ [filePath, Math.floor(i / this.#chunkSize), Buffer.from(chunk)]
3034
+ );
3035
+ }
2858
3036
  }
2859
- getCreatedFiles() {
2860
- return [...this.#createdFiles];
3037
+ async #readChunks(filePath, client) {
3038
+ let rows;
3039
+ if (client) {
3040
+ const result2 = await client.query(
3041
+ `SELECT data FROM ${this.#t("fs_chunks")} WHERE path = $1 ORDER BY chunk_index`,
3042
+ [filePath]
3043
+ );
3044
+ rows = result2.rows;
3045
+ } else {
3046
+ rows = await this.#query(
3047
+ `SELECT data FROM ${this.#t("fs_chunks")} WHERE path = $1 ORDER BY chunk_index`,
3048
+ [filePath]
3049
+ );
3050
+ }
3051
+ if (rows.length === 0) {
3052
+ return new Uint8Array(0);
3053
+ }
3054
+ const totalSize = rows.reduce((sum, row) => sum + row.data.length, 0);
3055
+ const result = new Uint8Array(totalSize);
3056
+ let offset = 0;
3057
+ for (const row of rows) {
3058
+ result.set(new Uint8Array(row.data), offset);
3059
+ offset += row.data.length;
3060
+ }
3061
+ return result;
2861
3062
  }
2862
- async writeFile(path5, content, options) {
2863
- await this.#base.writeFile(path5, content, options);
2864
- this.#createdFiles.add(path5);
3063
+ async #resolveSymlink(p, seen = /* @__PURE__ */ new Set()) {
3064
+ if (seen.has(p)) {
3065
+ throw new Error(`readFile: circular symlink: ${p}`);
3066
+ }
3067
+ const rows = await this.#query(
3068
+ `SELECT type, symlink_target FROM ${this.#t("fs_entries")} WHERE path = $1`,
3069
+ [p]
3070
+ );
3071
+ const entry = rows[0];
3072
+ if (!entry) {
3073
+ throw new Error(`ENOENT: no such file or directory: ${p}`);
3074
+ }
3075
+ if (entry.type !== "symlink") {
3076
+ return p;
3077
+ }
3078
+ seen.add(p);
3079
+ const target = this.#normalizePath(
3080
+ path5.posix.resolve(this.#dirname(p), entry.symlink_target)
3081
+ );
3082
+ return this.#resolveSymlink(target, seen);
2865
3083
  }
2866
- async appendFile(path5, content, options) {
2867
- await this.#base.appendFile(path5, content, options);
2868
- this.#createdFiles.add(path5);
3084
+ #toUint8Array(content, encoding) {
3085
+ if (content instanceof Uint8Array) {
3086
+ return content;
3087
+ }
3088
+ const enc = encoding ?? "utf8";
3089
+ return new Uint8Array(Buffer.from(content, enc));
2869
3090
  }
2870
- async mkdir(path5, options) {
2871
- return this.#base.mkdir(path5, options);
3091
+ async close() {
3092
+ if (this.#ownsPool) {
3093
+ await this.#pool.end();
3094
+ }
2872
3095
  }
2873
- async rm(path5, options) {
2874
- await this.#base.rm(path5, options);
2875
- this.#createdFiles.delete(path5);
2876
- if (options?.recursive) {
2877
- const prefix = path5.endsWith("/") ? path5 : path5 + "/";
2878
- for (const file of this.#createdFiles) {
3096
+ // ============================================================================
3097
+ // IFileSystem Implementation
3098
+ // ============================================================================
3099
+ async readFile(filePath, options) {
3100
+ const normalized = this.#normalizePath(filePath);
3101
+ const prefixed = this.#prefixPath(normalized);
3102
+ const resolved = await this.#resolveSymlink(prefixed);
3103
+ const rows = await this.#query(
3104
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3105
+ [resolved]
3106
+ );
3107
+ const entry = rows[0];
3108
+ if (!entry) {
3109
+ throw new Error(`ENOENT: no such file or directory: ${filePath}`);
3110
+ }
3111
+ if (entry.type === "directory") {
3112
+ throw new Error(`EISDIR: illegal operation on a directory: ${filePath}`);
3113
+ }
3114
+ const content = await this.#readChunks(resolved);
3115
+ const encoding = typeof options === "string" ? options : options?.encoding ?? "utf8";
3116
+ return Buffer.from(content).toString(encoding);
3117
+ }
3118
+ async readFileBuffer(filePath) {
3119
+ const normalized = this.#normalizePath(filePath);
3120
+ const prefixed = this.#prefixPath(normalized);
3121
+ const resolved = await this.#resolveSymlink(prefixed);
3122
+ const rows = await this.#query(
3123
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3124
+ [resolved]
3125
+ );
3126
+ const entry = rows[0];
3127
+ if (!entry) {
3128
+ throw new Error(`ENOENT: no such file or directory: ${filePath}`);
3129
+ }
3130
+ if (entry.type === "directory") {
3131
+ throw new Error(`EISDIR: illegal operation on a directory: ${filePath}`);
3132
+ }
3133
+ return this.#readChunks(resolved);
3134
+ }
3135
+ async writeFile(filePath, content, options) {
3136
+ const normalized = this.#normalizePath(filePath);
3137
+ const prefixed = this.#prefixPath(normalized);
3138
+ const encoding = typeof options === "string" ? options : options?.encoding;
3139
+ const data = this.#toUint8Array(content, encoding);
3140
+ await this.#useTransaction(async (client) => {
3141
+ await this.#ensureParentExists(prefixed, client);
3142
+ await client.query(
3143
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime)
3144
+ VALUES ($1, 'file', 420, $2, $3)
3145
+ ON CONFLICT (path) DO UPDATE SET type = 'file', size = EXCLUDED.size, mtime = EXCLUDED.mtime`,
3146
+ [prefixed, data.length, Date.now()]
3147
+ );
3148
+ await this.#writeChunks(prefixed, data, client);
3149
+ });
3150
+ }
3151
+ async appendFile(filePath, content, options) {
3152
+ const normalized = this.#normalizePath(filePath);
3153
+ const prefixed = this.#prefixPath(normalized);
3154
+ const encoding = typeof options === "string" ? options : options?.encoding;
3155
+ const newData = this.#toUint8Array(content, encoding);
3156
+ await this.#useTransaction(async (client) => {
3157
+ await this.#ensureParentExists(prefixed, client);
3158
+ const result = await client.query(
3159
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3160
+ [prefixed]
3161
+ );
3162
+ const entry = result.rows[0];
3163
+ if (entry && entry.type !== "file") {
3164
+ throw new Error(`appendFile: not a file: ${filePath}`);
3165
+ }
3166
+ const existing = entry ? await this.#readChunks(prefixed, client) : new Uint8Array(0);
3167
+ const combined = new Uint8Array(existing.length + newData.length);
3168
+ combined.set(existing, 0);
3169
+ combined.set(newData, existing.length);
3170
+ await client.query(
3171
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime)
3172
+ VALUES ($1, 'file', 420, $2, $3)
3173
+ ON CONFLICT (path) DO UPDATE SET size = EXCLUDED.size, mtime = EXCLUDED.mtime`,
3174
+ [prefixed, combined.length, Date.now()]
3175
+ );
3176
+ await this.#writeChunks(prefixed, combined, client);
3177
+ });
3178
+ }
3179
+ async exists(filePath) {
3180
+ const normalized = this.#normalizePath(filePath);
3181
+ const prefixed = this.#prefixPath(normalized);
3182
+ const rows = await this.#query(
3183
+ `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
3184
+ [prefixed]
3185
+ );
3186
+ return rows[0].exists;
3187
+ }
3188
+ async stat(filePath) {
3189
+ const normalized = this.#normalizePath(filePath);
3190
+ const prefixed = this.#prefixPath(normalized);
3191
+ const resolved = await this.#resolveSymlink(prefixed);
3192
+ const rows = await this.#query(
3193
+ `SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
3194
+ [resolved]
3195
+ );
3196
+ const entry = rows[0];
3197
+ if (!entry) {
3198
+ throw new Error(`ENOENT: no such file or directory: ${filePath}`);
3199
+ }
3200
+ return {
3201
+ isFile: entry.type === "file",
3202
+ isDirectory: entry.type === "directory",
3203
+ isSymbolicLink: false,
3204
+ mode: Number(entry.mode),
3205
+ size: Number(entry.size),
3206
+ mtime: new Date(Number(entry.mtime))
3207
+ };
3208
+ }
3209
+ async lstat(filePath) {
3210
+ const normalized = this.#normalizePath(filePath);
3211
+ const prefixed = this.#prefixPath(normalized);
3212
+ const rows = await this.#query(
3213
+ `SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
3214
+ [prefixed]
3215
+ );
3216
+ const entry = rows[0];
3217
+ if (!entry) {
3218
+ throw new Error(`ENOENT: no such file or directory: ${filePath}`);
3219
+ }
3220
+ return {
3221
+ isFile: entry.type === "file",
3222
+ isDirectory: entry.type === "directory",
3223
+ isSymbolicLink: entry.type === "symlink",
3224
+ mode: Number(entry.mode),
3225
+ size: Number(entry.size),
3226
+ mtime: new Date(Number(entry.mtime))
3227
+ };
3228
+ }
3229
+ async mkdir(dirPath, options) {
3230
+ const normalized = this.#normalizePath(dirPath);
3231
+ const prefixed = this.#prefixPath(normalized);
3232
+ const existingRows = await this.#query(
3233
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3234
+ [prefixed]
3235
+ );
3236
+ const existing = existingRows[0];
3237
+ if (existing) {
3238
+ if (options?.recursive) {
3239
+ return;
3240
+ }
3241
+ throw new Error(`EEXIST: file already exists: ${dirPath}`);
3242
+ }
3243
+ await this.#useTransaction(async (client) => {
3244
+ if (options?.recursive) {
3245
+ const rootPath = this.#root || "/";
3246
+ const relativePath = path5.posix.relative(rootPath, prefixed);
3247
+ const segments = relativePath.split("/").filter(Boolean);
3248
+ let currentPath = rootPath;
3249
+ for (const segment of segments) {
3250
+ currentPath = path5.posix.join(currentPath, segment);
3251
+ const result = await client.query(
3252
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3253
+ [currentPath]
3254
+ );
3255
+ const exists = result.rows[0];
3256
+ if (!exists) {
3257
+ await client.query(
3258
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
3259
+ [currentPath, Date.now()]
3260
+ );
3261
+ } else if (exists.type !== "directory") {
3262
+ throw new Error(`mkdir: not a directory: ${currentPath}`);
3263
+ }
3264
+ }
3265
+ } else {
3266
+ const parent = this.#dirname(prefixed);
3267
+ const parentResult = await client.query(
3268
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3269
+ [parent]
3270
+ );
3271
+ const parentEntry = parentResult.rows[0];
3272
+ if (!parentEntry) {
3273
+ throw new Error(`mkdir: parent does not exist: ${parent}`);
3274
+ }
3275
+ if (parentEntry.type !== "directory") {
3276
+ throw new Error(`mkdir: parent is not a directory: ${parent}`);
3277
+ }
3278
+ await client.query(
3279
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime) VALUES ($1, 'directory', 493, 0, $2)`,
3280
+ [prefixed, Date.now()]
3281
+ );
3282
+ }
3283
+ });
3284
+ }
3285
+ async readdir(dirPath) {
3286
+ const normalized = this.#normalizePath(dirPath);
3287
+ const prefixed = this.#prefixPath(normalized);
3288
+ const resolved = await this.#resolveSymlink(prefixed);
3289
+ const entryRows = await this.#query(
3290
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3291
+ [resolved]
3292
+ );
3293
+ const entry = entryRows[0];
3294
+ if (!entry) {
3295
+ throw new Error(`ENOENT: no such file or directory: ${dirPath}`);
3296
+ }
3297
+ if (entry.type !== "directory") {
3298
+ throw new Error(`ENOTDIR: not a directory: ${dirPath}`);
3299
+ }
3300
+ const prefix = resolved === "/" ? "/" : resolved + "/";
3301
+ const rows = await this.#query(
3302
+ `SELECT path FROM ${this.#t("fs_entries")}
3303
+ WHERE path LIKE $1 || '%'
3304
+ AND path != $2
3305
+ AND path NOT LIKE $1 || '%/%'`,
3306
+ [prefix, resolved]
3307
+ );
3308
+ return rows.map((row) => path5.posix.basename(row.path));
3309
+ }
3310
+ async readdirWithFileTypes(dirPath) {
3311
+ const normalized = this.#normalizePath(dirPath);
3312
+ const prefixed = this.#prefixPath(normalized);
3313
+ const resolved = await this.#resolveSymlink(prefixed);
3314
+ const entryRows = await this.#query(
3315
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3316
+ [resolved]
3317
+ );
3318
+ const entry = entryRows[0];
3319
+ if (!entry) {
3320
+ throw new Error(`ENOENT: no such file or directory: ${dirPath}`);
3321
+ }
3322
+ if (entry.type !== "directory") {
3323
+ throw new Error(`ENOTDIR: not a directory: ${dirPath}`);
3324
+ }
3325
+ const prefix = resolved === "/" ? "/" : resolved + "/";
3326
+ const rows = await this.#query(
3327
+ `SELECT path, type FROM ${this.#t("fs_entries")}
3328
+ WHERE path LIKE $1 || '%'
3329
+ AND path != $2
3330
+ AND path NOT LIKE $1 || '%/%'`,
3331
+ [prefix, resolved]
3332
+ );
3333
+ return rows.map((row) => ({
3334
+ name: path5.posix.basename(row.path),
3335
+ isFile: row.type === "file",
3336
+ isDirectory: row.type === "directory",
3337
+ isSymbolicLink: row.type === "symlink"
3338
+ }));
3339
+ }
3340
+ async rm(filePath, options) {
3341
+ const normalized = this.#normalizePath(filePath);
3342
+ const prefixed = this.#prefixPath(normalized);
3343
+ const rows = await this.#query(
3344
+ `SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
3345
+ [prefixed]
3346
+ );
3347
+ const entry = rows[0];
3348
+ if (!entry) {
3349
+ if (options?.force) {
3350
+ return;
3351
+ }
3352
+ throw new Error(`ENOENT: no such file or directory: ${filePath}`);
3353
+ }
3354
+ await this.#useTransaction(async (client) => {
3355
+ if (entry.type === "directory") {
3356
+ const childrenResult = await client.query(
3357
+ `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path LIKE $1 || '/%') AS exists`,
3358
+ [prefixed]
3359
+ );
3360
+ if (childrenResult.rows[0].exists && !options?.recursive) {
3361
+ throw new Error(`ENOTEMPTY: directory not empty: ${filePath}`);
3362
+ }
3363
+ await client.query(
3364
+ `DELETE FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%'`,
3365
+ [prefixed]
3366
+ );
3367
+ } else {
3368
+ await client.query(
3369
+ `DELETE FROM ${this.#t("fs_entries")} WHERE path = $1`,
3370
+ [prefixed]
3371
+ );
3372
+ }
3373
+ });
3374
+ }
3375
+ async cp(src, dest, options) {
3376
+ const srcNormalized = this.#normalizePath(src);
3377
+ const destNormalized = this.#normalizePath(dest);
3378
+ const srcPrefixed = this.#prefixPath(srcNormalized);
3379
+ const destPrefixed = this.#prefixPath(destNormalized);
3380
+ const srcRows = await this.#query(
3381
+ `SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
3382
+ [srcPrefixed]
3383
+ );
3384
+ const srcEntry = srcRows[0];
3385
+ if (!srcEntry) {
3386
+ throw new Error(`ENOENT: no such file or directory: ${src}`);
3387
+ }
3388
+ if (srcEntry.type === "directory" && !options?.recursive) {
3389
+ throw new Error(`cp: -r not specified; omitting directory: ${src}`);
3390
+ }
3391
+ await this.#useTransaction(async (client) => {
3392
+ await this.#ensureParentExists(destPrefixed, client);
3393
+ if (srcEntry.type === "directory") {
3394
+ const allEntriesResult = await client.query(
3395
+ `SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%'`,
3396
+ [srcPrefixed]
3397
+ );
3398
+ for (const entry of allEntriesResult.rows) {
3399
+ const relativePath = path5.posix.relative(srcPrefixed, entry.path);
3400
+ const newPath = path5.posix.join(destPrefixed, relativePath);
3401
+ await client.query(
3402
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
3403
+ VALUES ($1, $2, $3, $4, $5, $6)
3404
+ ON CONFLICT (path) DO UPDATE SET type = EXCLUDED.type, mode = EXCLUDED.mode, size = EXCLUDED.size, mtime = EXCLUDED.mtime, symlink_target = EXCLUDED.symlink_target`,
3405
+ [
3406
+ newPath,
3407
+ entry.type,
3408
+ entry.mode,
3409
+ entry.size,
3410
+ Date.now(),
3411
+ entry.symlink_target
3412
+ ]
3413
+ );
3414
+ if (entry.type === "file") {
3415
+ await client.query(
3416
+ `DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3417
+ [newPath]
3418
+ );
3419
+ const chunksResult = await client.query(
3420
+ `SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3421
+ [entry.path]
3422
+ );
3423
+ for (const chunk of chunksResult.rows) {
3424
+ await client.query(
3425
+ `INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
3426
+ [newPath, chunk.chunk_index, chunk.data]
3427
+ );
3428
+ }
3429
+ }
3430
+ }
3431
+ } else {
3432
+ await client.query(
3433
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
3434
+ VALUES ($1, $2, $3, $4, $5, $6)
3435
+ ON CONFLICT (path) DO UPDATE SET type = EXCLUDED.type, mode = EXCLUDED.mode, size = EXCLUDED.size, mtime = EXCLUDED.mtime, symlink_target = EXCLUDED.symlink_target`,
3436
+ [
3437
+ destPrefixed,
3438
+ srcEntry.type,
3439
+ srcEntry.mode,
3440
+ srcEntry.size,
3441
+ Date.now(),
3442
+ srcEntry.symlink_target
3443
+ ]
3444
+ );
3445
+ if (srcEntry.type === "file") {
3446
+ const chunksResult = await client.query(
3447
+ `SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3448
+ [srcPrefixed]
3449
+ );
3450
+ await client.query(
3451
+ `DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3452
+ [destPrefixed]
3453
+ );
3454
+ for (const chunk of chunksResult.rows) {
3455
+ await client.query(
3456
+ `INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
3457
+ [destPrefixed, chunk.chunk_index, chunk.data]
3458
+ );
3459
+ }
3460
+ }
3461
+ }
3462
+ });
3463
+ }
3464
+ async mv(src, dest) {
3465
+ const srcNormalized = this.#normalizePath(src);
3466
+ const destNormalized = this.#normalizePath(dest);
3467
+ const srcPrefixed = this.#prefixPath(srcNormalized);
3468
+ const destPrefixed = this.#prefixPath(destNormalized);
3469
+ const srcRows = await this.#query(
3470
+ `SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
3471
+ [srcPrefixed]
3472
+ );
3473
+ const srcEntry = srcRows[0];
3474
+ if (!srcEntry) {
3475
+ throw new Error(`ENOENT: no such file or directory: ${src}`);
3476
+ }
3477
+ await this.#useTransaction(async (client) => {
3478
+ await this.#ensureParentExists(destPrefixed, client);
3479
+ if (srcEntry.type === "directory") {
3480
+ const allEntriesResult = await client.query(
3481
+ `SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%' ORDER BY path DESC`,
3482
+ [srcPrefixed]
3483
+ );
3484
+ await client.query(
3485
+ `DELETE FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%'`,
3486
+ [destPrefixed]
3487
+ );
3488
+ for (const entry of [...allEntriesResult.rows].reverse()) {
3489
+ const relativePath = path5.posix.relative(srcPrefixed, entry.path);
3490
+ const newPath = path5.posix.join(destPrefixed, relativePath);
3491
+ await client.query(
3492
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
3493
+ VALUES ($1, $2, $3, $4, $5, $6)
3494
+ ON CONFLICT (path) DO UPDATE SET type = EXCLUDED.type, mode = EXCLUDED.mode, size = EXCLUDED.size, mtime = EXCLUDED.mtime, symlink_target = EXCLUDED.symlink_target`,
3495
+ [
3496
+ newPath,
3497
+ entry.type,
3498
+ entry.mode,
3499
+ entry.size,
3500
+ Date.now(),
3501
+ entry.symlink_target
3502
+ ]
3503
+ );
3504
+ if (entry.type === "file") {
3505
+ await client.query(
3506
+ `DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3507
+ [newPath]
3508
+ );
3509
+ const chunksResult = await client.query(
3510
+ `SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3511
+ [entry.path]
3512
+ );
3513
+ for (const chunk of chunksResult.rows) {
3514
+ await client.query(
3515
+ `INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
3516
+ [newPath, chunk.chunk_index, chunk.data]
3517
+ );
3518
+ }
3519
+ }
3520
+ }
3521
+ await client.query(
3522
+ `DELETE FROM ${this.#t("fs_entries")} WHERE path = $1 OR path LIKE $1 || '/%'`,
3523
+ [srcPrefixed]
3524
+ );
3525
+ } else {
3526
+ await client.query(
3527
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
3528
+ VALUES ($1, $2, $3, $4, $5, $6)
3529
+ ON CONFLICT (path) DO UPDATE SET type = EXCLUDED.type, mode = EXCLUDED.mode, size = EXCLUDED.size, mtime = EXCLUDED.mtime, symlink_target = EXCLUDED.symlink_target`,
3530
+ [
3531
+ destPrefixed,
3532
+ srcEntry.type,
3533
+ srcEntry.mode,
3534
+ srcEntry.size,
3535
+ Date.now(),
3536
+ srcEntry.symlink_target
3537
+ ]
3538
+ );
3539
+ if (srcEntry.type === "file") {
3540
+ await client.query(
3541
+ `DELETE FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3542
+ [destPrefixed]
3543
+ );
3544
+ const chunksResult = await client.query(
3545
+ `SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3546
+ [srcPrefixed]
3547
+ );
3548
+ for (const chunk of chunksResult.rows) {
3549
+ await client.query(
3550
+ `INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
3551
+ [destPrefixed, chunk.chunk_index, chunk.data]
3552
+ );
3553
+ }
3554
+ }
3555
+ await client.query(
3556
+ `DELETE FROM ${this.#t("fs_entries")} WHERE path = $1`,
3557
+ [srcPrefixed]
3558
+ );
3559
+ }
3560
+ });
3561
+ }
3562
+ resolvePath(base, relativePath) {
3563
+ return path5.posix.resolve(base, relativePath);
3564
+ }
3565
+ getAllPaths() {
3566
+ throw new Error(
3567
+ "getAllPaths() is not supported in PostgresFs - use getAllPathsAsync() instead"
3568
+ );
3569
+ }
3570
+ async getAllPathsAsync() {
3571
+ const rows = await this.#query(
3572
+ `SELECT path FROM ${this.#t("fs_entries")} ORDER BY path`
3573
+ );
3574
+ return rows.map((row) => row.path);
3575
+ }
3576
+ async realpath(filePath) {
3577
+ const normalized = this.#normalizePath(filePath);
3578
+ const prefixed = this.#prefixPath(normalized);
3579
+ const resolved = await this.#resolveSymlink(prefixed);
3580
+ const rows = await this.#query(
3581
+ `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
3582
+ [resolved]
3583
+ );
3584
+ if (!rows[0].exists) {
3585
+ throw new Error(`ENOENT: no such file or directory: ${filePath}`);
3586
+ }
3587
+ return this.#unprefixPath(resolved);
3588
+ }
3589
+ async utimes(filePath, _atime, mtime) {
3590
+ const normalized = this.#normalizePath(filePath);
3591
+ const prefixed = this.#prefixPath(normalized);
3592
+ const resolved = await this.#resolveSymlink(prefixed);
3593
+ const result = await this.#exec(
3594
+ `UPDATE ${this.#t("fs_entries")} SET mtime = $1 WHERE path = $2`,
3595
+ [mtime.getTime(), resolved]
3596
+ );
3597
+ if (result === 0) {
3598
+ throw new Error(`ENOENT: no such file or directory: ${filePath}`);
3599
+ }
3600
+ }
3601
+ async chmod(filePath, mode) {
3602
+ const normalized = this.#normalizePath(filePath);
3603
+ const prefixed = this.#prefixPath(normalized);
3604
+ const result = await this.#exec(
3605
+ `UPDATE ${this.#t("fs_entries")} SET mode = $1 WHERE path = $2`,
3606
+ [mode, prefixed]
3607
+ );
3608
+ if (result === 0) {
3609
+ throw new Error(`ENOENT: no such file or directory: ${filePath}`);
3610
+ }
3611
+ }
3612
+ async symlink(target, linkPath) {
3613
+ const normalized = this.#normalizePath(linkPath);
3614
+ const prefixed = this.#prefixPath(normalized);
3615
+ const existingRows = await this.#query(
3616
+ `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
3617
+ [prefixed]
3618
+ );
3619
+ if (existingRows[0].exists) {
3620
+ throw new Error(`EEXIST: file already exists: ${linkPath}`);
3621
+ }
3622
+ await this.#useTransaction(async (client) => {
3623
+ await this.#ensureParentExists(prefixed, client);
3624
+ await client.query(
3625
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
3626
+ VALUES ($1, 'symlink', 511, 0, $2, $3)`,
3627
+ [prefixed, Date.now(), target]
3628
+ );
3629
+ });
3630
+ }
3631
+ async link(existingPath, newPath) {
3632
+ const srcNormalized = this.#normalizePath(existingPath);
3633
+ const destNormalized = this.#normalizePath(newPath);
3634
+ const srcPrefixed = this.#prefixPath(srcNormalized);
3635
+ const destPrefixed = this.#prefixPath(destNormalized);
3636
+ const srcRows = await this.#query(
3637
+ `SELECT * FROM ${this.#t("fs_entries")} WHERE path = $1`,
3638
+ [srcPrefixed]
3639
+ );
3640
+ const srcEntry = srcRows[0];
3641
+ if (!srcEntry) {
3642
+ throw new Error(`ENOENT: no such file or directory: ${existingPath}`);
3643
+ }
3644
+ if (srcEntry.type !== "file") {
3645
+ throw new Error(`link: not supported for directories: ${existingPath}`);
3646
+ }
3647
+ const existingRows = await this.#query(
3648
+ `SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
3649
+ [destPrefixed]
3650
+ );
3651
+ if (existingRows[0].exists) {
3652
+ throw new Error(`EEXIST: file already exists: ${newPath}`);
3653
+ }
3654
+ await this.#useTransaction(async (client) => {
3655
+ await this.#ensureParentExists(destPrefixed, client);
3656
+ await client.query(
3657
+ `INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime)
3658
+ VALUES ($1, 'file', $2, $3, $4)`,
3659
+ [destPrefixed, srcEntry.mode, srcEntry.size, Date.now()]
3660
+ );
3661
+ const chunksResult = await client.query(
3662
+ `SELECT chunk_index, data FROM ${this.#t("fs_chunks")} WHERE path = $1`,
3663
+ [srcPrefixed]
3664
+ );
3665
+ for (const chunk of chunksResult.rows) {
3666
+ await client.query(
3667
+ `INSERT INTO ${this.#t("fs_chunks")} (path, chunk_index, data) VALUES ($1, $2, $3)`,
3668
+ [destPrefixed, chunk.chunk_index, chunk.data]
3669
+ );
3670
+ }
3671
+ });
3672
+ }
3673
+ async readlink(linkPath) {
3674
+ const normalized = this.#normalizePath(linkPath);
3675
+ const prefixed = this.#prefixPath(normalized);
3676
+ const rows = await this.#query(
3677
+ `SELECT type, symlink_target FROM ${this.#t("fs_entries")} WHERE path = $1`,
3678
+ [prefixed]
3679
+ );
3680
+ const entry = rows[0];
3681
+ if (!entry) {
3682
+ throw new Error(`ENOENT: no such file or directory: ${linkPath}`);
3683
+ }
3684
+ if (entry.type !== "symlink") {
3685
+ throw new Error(`readlink: not a symbolic link: ${linkPath}`);
3686
+ }
3687
+ return entry.symlink_target;
3688
+ }
3689
+ };
3690
+
3691
+ // packages/text2sql/src/lib/fs/scoped-fs.ts
3692
+ var ScopedFs = class {
3693
+ #base;
3694
+ #prefix;
3695
+ constructor(options) {
3696
+ this.#base = options.base;
3697
+ this.#prefix = options.prefix.replace(/\/$/, "");
3698
+ }
3699
+ #scope(path6) {
3700
+ return `${this.#prefix}${path6}`;
3701
+ }
3702
+ #unscope(path6) {
3703
+ if (path6 === this.#prefix) {
3704
+ return "/";
3705
+ }
3706
+ if (path6.startsWith(this.#prefix + "/")) {
3707
+ return path6.slice(this.#prefix.length) || "/";
3708
+ }
3709
+ return path6;
3710
+ }
3711
+ async writeFile(path6, content, options) {
3712
+ await this.#base.writeFile(this.#scope(path6), content, options);
3713
+ }
3714
+ async appendFile(path6, content, options) {
3715
+ await this.#base.appendFile(this.#scope(path6), content, options);
3716
+ }
3717
+ async mkdir(path6, options) {
3718
+ return this.#base.mkdir(this.#scope(path6), options);
3719
+ }
3720
+ async rm(path6, options) {
3721
+ await this.#base.rm(this.#scope(path6), options);
3722
+ }
3723
+ async cp(src, dest, options) {
3724
+ await this.#base.cp(this.#scope(src), this.#scope(dest), options);
3725
+ }
3726
+ async mv(src, dest) {
3727
+ await this.#base.mv(this.#scope(src), this.#scope(dest));
3728
+ }
3729
+ async chmod(path6, mode) {
3730
+ return this.#base.chmod(this.#scope(path6), mode);
3731
+ }
3732
+ async symlink(target, linkPath) {
3733
+ await this.#base.symlink(target, this.#scope(linkPath));
3734
+ }
3735
+ async link(existingPath, newPath) {
3736
+ await this.#base.link(this.#scope(existingPath), this.#scope(newPath));
3737
+ }
3738
+ readFile(path6, options) {
3739
+ return this.#base.readFile(this.#scope(path6), options);
3740
+ }
3741
+ readFileBuffer(path6) {
3742
+ return this.#base.readFileBuffer(this.#scope(path6));
3743
+ }
3744
+ stat(path6) {
3745
+ return this.#base.stat(this.#scope(path6));
3746
+ }
3747
+ lstat(path6) {
3748
+ return this.#base.lstat(this.#scope(path6));
3749
+ }
3750
+ readdir(path6) {
3751
+ return this.#base.readdir(this.#scope(path6));
3752
+ }
3753
+ readdirWithFileTypes(path6) {
3754
+ return this.#base.readdirWithFileTypes(this.#scope(path6));
3755
+ }
3756
+ exists(path6) {
3757
+ return this.#base.exists(this.#scope(path6));
3758
+ }
3759
+ readlink(path6) {
3760
+ return this.#base.readlink(this.#scope(path6));
3761
+ }
3762
+ realpath(path6) {
3763
+ return this.#base.realpath(this.#scope(path6)).then((p) => this.#unscope(p));
3764
+ }
3765
+ utimes(path6, atime, mtime) {
3766
+ return this.#base.utimes(this.#scope(path6), atime, mtime);
3767
+ }
3768
+ resolvePath(base, relativePath) {
3769
+ return this.#base.resolvePath(base, relativePath);
3770
+ }
3771
+ getAllPaths() {
3772
+ const allPaths = this.#base.getAllPaths?.() ?? [];
3773
+ return allPaths.filter((p) => p.startsWith(this.#prefix)).map((p) => p.slice(this.#prefix.length) || "/");
3774
+ }
3775
+ };
3776
+
3777
+ // packages/text2sql/src/lib/fs/tracked-fs.ts
3778
+ var TrackedFs = class {
3779
+ #base;
3780
+ #createdFiles = /* @__PURE__ */ new Set();
3781
+ constructor(base) {
3782
+ this.#base = base;
3783
+ }
3784
+ getCreatedFiles() {
3785
+ return [...this.#createdFiles];
3786
+ }
3787
+ async writeFile(path6, content, options) {
3788
+ await this.#base.writeFile(path6, content, options);
3789
+ this.#createdFiles.add(path6);
3790
+ }
3791
+ async appendFile(path6, content, options) {
3792
+ await this.#base.appendFile(path6, content, options);
3793
+ this.#createdFiles.add(path6);
3794
+ }
3795
+ async mkdir(path6, options) {
3796
+ return this.#base.mkdir(path6, options);
3797
+ }
3798
+ async rm(path6, options) {
3799
+ await this.#base.rm(path6, options);
3800
+ this.#createdFiles.delete(path6);
3801
+ if (options?.recursive) {
3802
+ const prefix = path6.endsWith("/") ? path6 : path6 + "/";
3803
+ for (const file of this.#createdFiles) {
2879
3804
  if (file.startsWith(prefix)) {
2880
3805
  this.#createdFiles.delete(file);
2881
3806
  }
@@ -2891,8 +3816,8 @@ var TrackedFs = class {
2891
3816
  this.#createdFiles.delete(src);
2892
3817
  this.#createdFiles.add(dest);
2893
3818
  }
2894
- async chmod(path5, mode) {
2895
- return this.#base.chmod(path5, mode);
3819
+ async chmod(path6, mode) {
3820
+ return this.#base.chmod(path6, mode);
2896
3821
  }
2897
3822
  async symlink(target, linkPath) {
2898
3823
  await this.#base.symlink(target, linkPath);
@@ -2902,29 +3827,29 @@ var TrackedFs = class {
2902
3827
  await this.#base.link(existingPath, newPath);
2903
3828
  this.#createdFiles.add(newPath);
2904
3829
  }
2905
- readFile(path5, options) {
2906
- return this.#base.readFile(path5, options);
3830
+ readFile(path6, options) {
3831
+ return this.#base.readFile(path6, options);
2907
3832
  }
2908
- readFileBuffer(path5) {
2909
- return this.#base.readFileBuffer(path5);
3833
+ readFileBuffer(path6) {
3834
+ return this.#base.readFileBuffer(path6);
2910
3835
  }
2911
- stat(path5) {
2912
- return this.#base.stat(path5);
3836
+ stat(path6) {
3837
+ return this.#base.stat(path6);
2913
3838
  }
2914
- lstat(path5) {
2915
- return this.#base.lstat(path5);
3839
+ lstat(path6) {
3840
+ return this.#base.lstat(path6);
2916
3841
  }
2917
- readdir(path5) {
2918
- return this.#base.readdir(path5);
3842
+ readdir(path6) {
3843
+ return this.#base.readdir(path6);
2919
3844
  }
2920
- readdirWithFileTypes(path5) {
2921
- return this.#base.readdirWithFileTypes(path5);
3845
+ readdirWithFileTypes(path6) {
3846
+ return this.#base.readdirWithFileTypes(path6);
2922
3847
  }
2923
- exists(path5) {
2924
- return this.#base.exists(path5);
3848
+ exists(path6) {
3849
+ return this.#base.exists(path6);
2925
3850
  }
2926
- readlink(path5) {
2927
- return this.#base.readlink(path5);
3851
+ readlink(path6) {
3852
+ return this.#base.readlink(path6);
2928
3853
  }
2929
3854
  resolvePath(base, relativePath) {
2930
3855
  return this.#base.resolvePath(base, relativePath);
@@ -2932,11 +3857,11 @@ var TrackedFs = class {
2932
3857
  getAllPaths() {
2933
3858
  return this.#base.getAllPaths?.() ?? [];
2934
3859
  }
2935
- realpath(path5) {
2936
- return this.#base.realpath(path5);
3860
+ realpath(path6) {
3861
+ return this.#base.realpath(path6);
2937
3862
  }
2938
- utimes(path5, atime, mtime) {
2939
- return this.#base.utimes(path5, atime, mtime);
3863
+ utimes(path6, atime, mtime) {
3864
+ return this.#base.utimes(path6, atime, mtime);
2940
3865
  }
2941
3866
  };
2942
3867
 
@@ -2961,7 +3886,7 @@ function reasoningFramework() {
2961
3886
  "You are a very strong reasoner and planner. Use these critical instructions to structure your plans, thoughts, and responses."
2962
3887
  ),
2963
3888
  fragment2(
2964
- "Meta-cognitive reasoning framework",
3889
+ "meta-cognitive-reasoning-framework",
2965
3890
  hint2(
2966
3891
  "Before taking any action (either tool calls *or* responses to the user), you must proactively, methodically, and independently plan and reason about:"
2967
3892
  ),
@@ -3078,7 +4003,7 @@ function guidelines(options = {}) {
3078
4003
  ...reasoningFramework(),
3079
4004
  // Prerequisite policies (must do X before Y)
3080
4005
  fragment2(
3081
- "Prerequisite policies",
4006
+ "prerequisite_policies",
3082
4007
  policy({
3083
4008
  rule: "YOU MUST inspect schema structure and available tables",
3084
4009
  before: "generating ANY SQL query",
@@ -3102,7 +4027,7 @@ function guidelines(options = {}) {
3102
4027
  ),
3103
4028
  // Few-shot: Applying reasoning principles
3104
4029
  fragment2(
3105
- "Reasoning examples",
4030
+ "reasoning-examples",
3106
4031
  example({
3107
4032
  question: "Show me sales last month",
3108
4033
  answer: `Applying Principle 1 (Logical dependencies):
@@ -3142,7 +4067,7 @@ Action: Ask user: "Top by what metric\u2014total revenue, number of orders, or m
3142
4067
  ),
3143
4068
  // Schema adherence - consolidated into clear rules
3144
4069
  fragment2(
3145
- "Schema adherence",
4070
+ "schema_adherence",
3146
4071
  hint2(
3147
4072
  "Use only tables and columns from the schema. For unspecified columns, use SELECT *. When showing related items, include IDs and requested details."
3148
4073
  ),
@@ -3346,6 +4271,7 @@ import {
3346
4271
  InvalidToolInputError,
3347
4272
  NoSuchToolError,
3348
4273
  ToolCallRepairError,
4274
+ createUIMessageStream,
3349
4275
  generateId
3350
4276
  } from "ai";
3351
4277
  import "just-bash";
@@ -3436,6 +4362,7 @@ var Text2Sql = class {
3436
4362
  skillMounts,
3437
4363
  filesystem: trackedFs
3438
4364
  });
4365
+ const assistantMsgId = generateId();
3439
4366
  const chatAgent = agent2({
3440
4367
  name: "text2sql",
3441
4368
  model: this.#config.model,
@@ -3451,26 +4378,54 @@ var Text2Sql = class {
3451
4378
  {},
3452
4379
  { transform: this.#config.transform }
3453
4380
  );
3454
- return result.toUIMessageStream({
4381
+ const uiStream = result.toUIMessageStream({
3455
4382
  onError: (error) => this.#formatError(error),
3456
4383
  sendStart: true,
3457
4384
  sendFinish: true,
3458
4385
  sendReasoning: true,
3459
4386
  sendSources: true,
3460
4387
  originalMessages: messages,
3461
- generateMessageId: generateId,
4388
+ generateMessageId: () => assistantMsgId,
4389
+ messageMetadata: ({ part }) => {
4390
+ if (part.type === "finish-step") {
4391
+ return {
4392
+ finishReason: part.finishReason,
4393
+ usage: part.usage
4394
+ };
4395
+ }
4396
+ if (part.type === "finish") {
4397
+ return {
4398
+ finishReason: part.finishReason,
4399
+ totalUsage: part.totalUsage
4400
+ };
4401
+ }
4402
+ return void 0;
4403
+ }
4404
+ });
4405
+ return createUIMessageStream({
4406
+ originalMessages: messages,
4407
+ generateId: () => assistantMsgId,
4408
+ onStepFinish: async ({ responseMessage }) => {
4409
+ context.set(assistant({ ...responseMessage, id: assistantMsgId }));
4410
+ await context.save({ branch: false });
4411
+ },
3462
4412
  onFinish: async ({ responseMessage }) => {
3463
4413
  const createdFiles = trackedFs.getCreatedFiles();
3464
- const messageWithMetadata = {
3465
- ...responseMessage,
3466
- metadata: {
3467
- ...responseMessage.metadata ?? {},
3468
- createdFiles
3469
- }
3470
- };
3471
- context.set(assistant(messageWithMetadata));
3472
- await context.save();
4414
+ context.set(
4415
+ assistant({
4416
+ ...responseMessage,
4417
+ id: assistantMsgId,
4418
+ metadata: {
4419
+ ...responseMessage.metadata ?? {},
4420
+ createdFiles
4421
+ }
4422
+ })
4423
+ );
4424
+ await context.save({ branch: false });
3473
4425
  await context.trackUsage(await result.totalUsage);
4426
+ },
4427
+ execute: async ({ writer }) => {
4428
+ writer.merge(uiStream);
3474
4429
  }
3475
4430
  });
3476
4431
  }
@@ -3529,6 +4484,7 @@ export {
3529
4484
  JsonCache,
3530
4485
  MssqlFs,
3531
4486
  Point,
4487
+ PostgresFs,
3532
4488
  SQLValidationError,
3533
4489
  ScopedFs,
3534
4490
  SqliteFs,