@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.
- package/dist/index.js +1149 -193
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts +3 -0
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/bigquery.d.ts +1 -0
- package/dist/lib/adapters/bigquery/bigquery.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +11 -0
- package/dist/lib/adapters/bigquery/index.js.map +3 -3
- package/dist/lib/adapters/mysql/index.js +11 -0
- package/dist/lib/adapters/mysql/index.js.map +3 -3
- package/dist/lib/adapters/mysql/mysql.d.ts +1 -0
- package/dist/lib/adapters/mysql/mysql.d.ts.map +1 -1
- package/dist/lib/adapters/postgres/index.js +11 -0
- package/dist/lib/adapters/postgres/index.js.map +3 -3
- package/dist/lib/adapters/postgres/postgres.d.ts +1 -0
- package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +11 -0
- package/dist/lib/adapters/spreadsheet/index.js.map +3 -3
- package/dist/lib/adapters/sqlite/index.js +11 -0
- package/dist/lib/adapters/sqlite/index.js.map +3 -3
- package/dist/lib/adapters/sqlite/sqlite.d.ts +1 -0
- package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -1
- package/dist/lib/adapters/sqlserver/index.js +11 -0
- package/dist/lib/adapters/sqlserver/index.js.map +3 -3
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts +1 -0
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -1
- package/dist/lib/agents/result-tools.d.ts +9 -1
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/agents/sql.agent.d.ts.map +1 -1
- package/dist/lib/fs/index.d.ts +1 -0
- package/dist/lib/fs/index.d.ts.map +1 -1
- package/dist/lib/fs/mssql/ddl.mssql-fs.d.ts.map +1 -1
- package/dist/lib/fs/mssql/mssql-fs.d.ts +1 -0
- package/dist/lib/fs/mssql/mssql-fs.d.ts.map +1 -1
- package/dist/lib/fs/postgres/ddl.postgres-fs.d.ts +2 -0
- package/dist/lib/fs/postgres/ddl.postgres-fs.d.ts.map +1 -0
- package/dist/lib/fs/postgres/postgres-fs.d.ts +50 -0
- package/dist/lib/fs/postgres/postgres-fs.d.ts.map +1 -0
- package/dist/lib/sql.d.ts +1 -1
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +1 -9
- package/dist/lib/synthesis/index.js.map +2 -2
- 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
|
|
550
|
-
if (!
|
|
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(
|
|
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
|
|
605
|
-
if (!
|
|
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(
|
|
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
|
|
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
|
-
|
|
696
|
+
const execute = tools2.bash.execute;
|
|
697
|
+
if (!execute) {
|
|
679
698
|
throw new Error("bash tool execution is not available");
|
|
680
699
|
}
|
|
681
|
-
return
|
|
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(
|
|
949
|
+
constructor(path6, configHash, points) {
|
|
931
950
|
this.points = points;
|
|
932
|
-
this.path =
|
|
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:
|
|
941
|
-
if (existsSync(
|
|
959
|
+
const { path: path6, configHash } = options;
|
|
960
|
+
if (existsSync(path6)) {
|
|
942
961
|
try {
|
|
943
|
-
const content = readFileSync(
|
|
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(
|
|
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(
|
|
974
|
+
return new _Checkpoint(path6, configHash, points);
|
|
956
975
|
} catch {
|
|
957
976
|
console.log("\u26A0 Checkpoint corrupted, starting fresh");
|
|
958
|
-
return new _Checkpoint(
|
|
977
|
+
return new _Checkpoint(path6, configHash, {});
|
|
959
978
|
}
|
|
960
979
|
}
|
|
961
980
|
console.log("Starting new checkpoint");
|
|
962
|
-
return new _Checkpoint(
|
|
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'[${
|
|
1826
|
-
CREATE TABLE [${
|
|
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_${
|
|
1837
|
-
CREATE INDEX [idx_${
|
|
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'[${
|
|
1841
|
-
CREATE TABLE [${
|
|
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 [${
|
|
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
|
-
#
|
|
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
|
|
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
|
-
|
|
1936
|
-
|
|
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
|
-
|
|
1994
|
+
this.#ensureInitialized();
|
|
1973
1995
|
return this.#rawQuery(sql, params);
|
|
1974
1996
|
}
|
|
1975
1997
|
async #exec(sql, params) {
|
|
1976
|
-
|
|
1998
|
+
this.#ensureInitialized();
|
|
1977
1999
|
return this.#rawExec(sql, params);
|
|
1978
2000
|
}
|
|
1979
2001
|
async #useTransaction(fn) {
|
|
1980
|
-
|
|
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
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
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
|
-
|
|
2566
|
-
|
|
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
|
-
|
|
2600
|
-
|
|
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/
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
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.#
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
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
|
-
|
|
2782
|
-
|
|
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
|
-
|
|
2802
|
-
|
|
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
|
-
|
|
2805
|
-
return this.#
|
|
2887
|
+
#t(name) {
|
|
2888
|
+
return `"${this.#schema}"."${name}"`;
|
|
2806
2889
|
}
|
|
2807
|
-
async
|
|
2808
|
-
|
|
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
|
-
|
|
2811
|
-
|
|
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
|
-
|
|
2814
|
-
|
|
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
|
-
|
|
2817
|
-
|
|
2941
|
+
async #rawQuery(sql, params) {
|
|
2942
|
+
const result = await this.#pool.query(sql, params);
|
|
2943
|
+
return result.rows;
|
|
2818
2944
|
}
|
|
2819
|
-
|
|
2820
|
-
|
|
2945
|
+
async #rawExec(sql, params) {
|
|
2946
|
+
const result = await this.#pool.query(sql, params);
|
|
2947
|
+
return result.rowCount ?? 0;
|
|
2821
2948
|
}
|
|
2822
|
-
|
|
2823
|
-
|
|
2949
|
+
async #query(sql, params) {
|
|
2950
|
+
this.#ensureInitialized();
|
|
2951
|
+
return this.#rawQuery(sql, params);
|
|
2824
2952
|
}
|
|
2825
|
-
|
|
2826
|
-
|
|
2953
|
+
async #exec(sql, params) {
|
|
2954
|
+
this.#ensureInitialized();
|
|
2955
|
+
return this.#rawExec(sql, params);
|
|
2827
2956
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
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
|
-
|
|
2832
|
-
return
|
|
2972
|
+
#normalizeRoot(root) {
|
|
2973
|
+
return path5.posix.resolve("/", root.trim());
|
|
2833
2974
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
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
|
-
|
|
2838
|
-
|
|
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
|
-
|
|
2841
|
-
return
|
|
2996
|
+
#normalizePath(p) {
|
|
2997
|
+
return path5.posix.resolve("/", p);
|
|
2842
2998
|
}
|
|
2843
|
-
|
|
2844
|
-
|
|
2999
|
+
#dirname(p) {
|
|
3000
|
+
const dir = path5.posix.dirname(p);
|
|
3001
|
+
return dir === "" ? "/" : dir;
|
|
2845
3002
|
}
|
|
2846
|
-
|
|
2847
|
-
const
|
|
2848
|
-
|
|
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
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
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
|
-
|
|
2860
|
-
|
|
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
|
|
2863
|
-
|
|
2864
|
-
|
|
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
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
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
|
|
2871
|
-
|
|
3091
|
+
async close() {
|
|
3092
|
+
if (this.#ownsPool) {
|
|
3093
|
+
await this.#pool.end();
|
|
3094
|
+
}
|
|
2872
3095
|
}
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
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(
|
|
2895
|
-
return this.#base.chmod(
|
|
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(
|
|
2906
|
-
return this.#base.readFile(
|
|
3830
|
+
readFile(path6, options) {
|
|
3831
|
+
return this.#base.readFile(path6, options);
|
|
2907
3832
|
}
|
|
2908
|
-
readFileBuffer(
|
|
2909
|
-
return this.#base.readFileBuffer(
|
|
3833
|
+
readFileBuffer(path6) {
|
|
3834
|
+
return this.#base.readFileBuffer(path6);
|
|
2910
3835
|
}
|
|
2911
|
-
stat(
|
|
2912
|
-
return this.#base.stat(
|
|
3836
|
+
stat(path6) {
|
|
3837
|
+
return this.#base.stat(path6);
|
|
2913
3838
|
}
|
|
2914
|
-
lstat(
|
|
2915
|
-
return this.#base.lstat(
|
|
3839
|
+
lstat(path6) {
|
|
3840
|
+
return this.#base.lstat(path6);
|
|
2916
3841
|
}
|
|
2917
|
-
readdir(
|
|
2918
|
-
return this.#base.readdir(
|
|
3842
|
+
readdir(path6) {
|
|
3843
|
+
return this.#base.readdir(path6);
|
|
2919
3844
|
}
|
|
2920
|
-
readdirWithFileTypes(
|
|
2921
|
-
return this.#base.readdirWithFileTypes(
|
|
3845
|
+
readdirWithFileTypes(path6) {
|
|
3846
|
+
return this.#base.readdirWithFileTypes(path6);
|
|
2922
3847
|
}
|
|
2923
|
-
exists(
|
|
2924
|
-
return this.#base.exists(
|
|
3848
|
+
exists(path6) {
|
|
3849
|
+
return this.#base.exists(path6);
|
|
2925
3850
|
}
|
|
2926
|
-
readlink(
|
|
2927
|
-
return this.#base.readlink(
|
|
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(
|
|
2936
|
-
return this.#base.realpath(
|
|
3860
|
+
realpath(path6) {
|
|
3861
|
+
return this.#base.realpath(path6);
|
|
2937
3862
|
}
|
|
2938
|
-
utimes(
|
|
2939
|
-
return this.#base.utimes(
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
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,
|