@deepagents/text2sql 0.29.1 → 0.30.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 +710 -378
- package/dist/index.js.map +4 -4
- package/dist/lib/adapters/adapter.d.ts +6 -4
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/bigquery.d.ts +2 -2
- package/dist/lib/adapters/bigquery/bigquery.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +248 -8
- package/dist/lib/adapters/bigquery/index.js.map +4 -4
- package/dist/lib/adapters/mysql/index.js +247 -7
- package/dist/lib/adapters/mysql/index.js.map +4 -4
- package/dist/lib/adapters/mysql/mysql.d.ts +2 -2
- package/dist/lib/adapters/mysql/mysql.d.ts.map +1 -1
- package/dist/lib/adapters/postgres/index.js +247 -7
- package/dist/lib/adapters/postgres/index.js.map +4 -4
- package/dist/lib/adapters/postgres/postgres.d.ts +2 -2
- package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +247 -7
- package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/index.js +247 -7
- package/dist/lib/adapters/sqlite/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/sqlite.d.ts +2 -2
- package/dist/lib/adapters/sqlite/sqlite.d.ts.map +1 -1
- package/dist/lib/adapters/sqlserver/index.js +247 -7
- package/dist/lib/adapters/sqlserver/index.js.map +4 -4
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts +2 -2
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -1
- package/dist/lib/agents/result-tools.d.ts +19 -1
- package/dist/lib/agents/result-tools.d.ts.map +1 -1
- package/dist/lib/agents/sql-transform-plugins.d.ts +19 -0
- package/dist/lib/agents/sql-transform-plugins.d.ts.map +1 -0
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
// packages/text2sql/src/lib/adapters/adapter.ts
|
|
2
2
|
import { format as formatSql } from "sql-formatter";
|
|
3
3
|
|
|
4
|
+
// packages/text2sql/src/lib/agents/exceptions.ts
|
|
5
|
+
var sqlValidationMarker = Symbol("SQLValidationError");
|
|
6
|
+
var unanswerableSqlMarker = Symbol("UnanswerableSQLError");
|
|
7
|
+
var sqlScopeMarker = Symbol("SQLScopeError");
|
|
8
|
+
var SQLValidationError = class _SQLValidationError extends Error {
|
|
9
|
+
[sqlValidationMarker];
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "SQLValidationError";
|
|
13
|
+
this[sqlValidationMarker] = true;
|
|
14
|
+
}
|
|
15
|
+
static isInstance(error) {
|
|
16
|
+
return error instanceof _SQLValidationError && error[sqlValidationMarker] === true;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
|
|
20
|
+
[unanswerableSqlMarker];
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "UnanswerableSQLError";
|
|
24
|
+
this[unanswerableSqlMarker] = true;
|
|
25
|
+
}
|
|
26
|
+
static isInstance(error) {
|
|
27
|
+
return error instanceof _UnanswerableSQLError && error[unanswerableSqlMarker] === true;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var SQLScopeError = class _SQLScopeError extends Error {
|
|
31
|
+
[sqlScopeMarker];
|
|
32
|
+
payload;
|
|
33
|
+
errorType;
|
|
34
|
+
constructor(payload) {
|
|
35
|
+
super(JSON.stringify(payload));
|
|
36
|
+
this.name = "SQLScopeError";
|
|
37
|
+
this.payload = payload;
|
|
38
|
+
this.errorType = payload.error_type;
|
|
39
|
+
this[sqlScopeMarker] = true;
|
|
40
|
+
}
|
|
41
|
+
static isInstance(error) {
|
|
42
|
+
return error instanceof _SQLScopeError && error[sqlScopeMarker] === true;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
4
46
|
// packages/text2sql/src/lib/fragments/schema.ts
|
|
5
47
|
function dialectInfo(input) {
|
|
6
48
|
return {
|
|
@@ -104,6 +146,134 @@ function createGroundingContext() {
|
|
|
104
146
|
};
|
|
105
147
|
}
|
|
106
148
|
|
|
149
|
+
// packages/text2sql/src/lib/adapters/runtime-scope.ts
|
|
150
|
+
import nodeSqlParser from "node-sql-parser";
|
|
151
|
+
var { Parser } = nodeSqlParser;
|
|
152
|
+
var parser = new Parser();
|
|
153
|
+
function extractBaseEntityReferences(sql, dialect) {
|
|
154
|
+
const ast = parser.astify(sql, { database: dialect });
|
|
155
|
+
const state = {
|
|
156
|
+
cteNames: /* @__PURE__ */ new Set(),
|
|
157
|
+
references: /* @__PURE__ */ new Map()
|
|
158
|
+
};
|
|
159
|
+
visitNode(ast, state);
|
|
160
|
+
return Array.from(state.references.values());
|
|
161
|
+
}
|
|
162
|
+
function buildOutOfScopePayload(sql, referencedEntities, allowedEntities) {
|
|
163
|
+
return {
|
|
164
|
+
error: `Query references entities outside grounded scope: ${referencedEntities.join(", ")}`,
|
|
165
|
+
error_type: "OUT_OF_SCOPE",
|
|
166
|
+
suggestion: "Restrict the query to grounded tables/views or expand grounding to include the referenced entities.",
|
|
167
|
+
sql_attempted: sql,
|
|
168
|
+
referenced_entities: referencedEntities,
|
|
169
|
+
allowed_entities: allowedEntities
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function buildScopeParseErrorPayload(sql, dialect, error) {
|
|
173
|
+
const parserError = error instanceof Error ? error.message : String(error ?? "Unknown error");
|
|
174
|
+
return {
|
|
175
|
+
error: `SQL scope analysis failed before validation/execution: ${parserError}`,
|
|
176
|
+
error_type: "SQL_SCOPE_PARSE_ERROR",
|
|
177
|
+
suggestion: "Rewrite the query into simpler SQL that can be analyzed safely, or extend parser coverage for this dialect feature.",
|
|
178
|
+
sql_attempted: sql,
|
|
179
|
+
parser_dialect: dialect,
|
|
180
|
+
parser_error: parserError
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function visitNode(node, state) {
|
|
184
|
+
if (Array.isArray(node)) {
|
|
185
|
+
for (const item of node) {
|
|
186
|
+
visitNode(item, state);
|
|
187
|
+
}
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!isAstLike(node)) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (isStatementNode(node)) {
|
|
194
|
+
visitStatement(node, state);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (isTableReferenceNode(node)) {
|
|
198
|
+
addReference(node, state);
|
|
199
|
+
}
|
|
200
|
+
for (const value of Object.values(node)) {
|
|
201
|
+
visitNode(value, state);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function visitStatement(node, parentState) {
|
|
205
|
+
const localState = {
|
|
206
|
+
cteNames: new Set(parentState.cteNames),
|
|
207
|
+
references: parentState.references
|
|
208
|
+
};
|
|
209
|
+
const withItems = Array.isArray(node.with) ? node.with : [];
|
|
210
|
+
for (const item of withItems) {
|
|
211
|
+
if (!isAstLike(item)) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const cteName = readCteName(item);
|
|
215
|
+
if (cteName) {
|
|
216
|
+
localState.cteNames.add(caseFold(cteName));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
for (const item of withItems) {
|
|
220
|
+
if (!isAstLike(item)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
visitNode(item.stmt, localState);
|
|
224
|
+
}
|
|
225
|
+
for (const [key, value] of Object.entries(node)) {
|
|
226
|
+
if (key === "with") {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
visitNode(value, localState);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function addReference(node, state) {
|
|
233
|
+
const table2 = typeof node.table === "string" ? node.table : null;
|
|
234
|
+
if (!table2) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (state.cteNames.has(caseFold(table2))) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const db = typeof node.db === "string" ? node.db : null;
|
|
241
|
+
const key = db ? `${db}.${table2}` : table2;
|
|
242
|
+
if (!state.references.has(key)) {
|
|
243
|
+
state.references.set(key, { db, table: table2 });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function readCteName(node) {
|
|
247
|
+
const name = node.name;
|
|
248
|
+
if (typeof name === "string") {
|
|
249
|
+
return name;
|
|
250
|
+
}
|
|
251
|
+
if (!isAstLike(name)) {
|
|
252
|
+
return void 0;
|
|
253
|
+
}
|
|
254
|
+
const value = name.value;
|
|
255
|
+
return typeof value === "string" ? value : void 0;
|
|
256
|
+
}
|
|
257
|
+
function isStatementNode(node) {
|
|
258
|
+
const type = node.type;
|
|
259
|
+
return typeof type === "string" && ["delete", "insert", "replace", "select", "update"].includes(type);
|
|
260
|
+
}
|
|
261
|
+
function isTableReferenceNode(node) {
|
|
262
|
+
if (node.type === "column_ref") {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
if (typeof node.table !== "string") {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
return "addition" in node || "as" in node || "db" in node || "join" in node || "operator" in node || "surround" in node || "table_hint" in node || "temporal_table" in node;
|
|
269
|
+
}
|
|
270
|
+
function isAstLike(value) {
|
|
271
|
+
return typeof value === "object" && value !== null;
|
|
272
|
+
}
|
|
273
|
+
function caseFold(value) {
|
|
274
|
+
return value.toLowerCase();
|
|
275
|
+
}
|
|
276
|
+
|
|
107
277
|
// packages/text2sql/src/lib/adapters/adapter.ts
|
|
108
278
|
var Adapter = class {
|
|
109
279
|
/**
|
|
@@ -133,10 +303,7 @@ var Adapter = class {
|
|
|
133
303
|
const grounding = fn(this);
|
|
134
304
|
await grounding.execute(ctx);
|
|
135
305
|
}
|
|
136
|
-
return [
|
|
137
|
-
...ctx.tables.map((t) => t.name),
|
|
138
|
-
...ctx.views.map((v) => v.name)
|
|
139
|
-
];
|
|
306
|
+
return [...ctx.tables.map((t) => t.name), ...ctx.views.map((v) => v.name)];
|
|
140
307
|
}
|
|
141
308
|
/**
|
|
142
309
|
* Convert complete grounding context to schema fragments.
|
|
@@ -149,7 +316,8 @@ var Adapter = class {
|
|
|
149
316
|
dialectInfo({
|
|
150
317
|
dialect: ctx.info.dialect,
|
|
151
318
|
version: ctx.info.version,
|
|
152
|
-
database: ctx.info.database
|
|
319
|
+
database: ctx.info.database,
|
|
320
|
+
details: ctx.info.details
|
|
153
321
|
})
|
|
154
322
|
);
|
|
155
323
|
}
|
|
@@ -293,6 +461,78 @@ var Adapter = class {
|
|
|
293
461
|
return sql;
|
|
294
462
|
}
|
|
295
463
|
}
|
|
464
|
+
#cachedAllowedEntities = null;
|
|
465
|
+
async #resolveScope() {
|
|
466
|
+
if (this.#cachedAllowedEntities) return this.#cachedAllowedEntities;
|
|
467
|
+
this.#cachedAllowedEntities = await this.resolveAllowedEntities();
|
|
468
|
+
return this.#cachedAllowedEntities;
|
|
469
|
+
}
|
|
470
|
+
async #checkScope(sql, allowedEntities) {
|
|
471
|
+
const dialect = this.formatterLanguage;
|
|
472
|
+
const scopeDialects = {
|
|
473
|
+
sqlite: "mysql",
|
|
474
|
+
postgresql: "postgresql",
|
|
475
|
+
bigquery: "bigquery",
|
|
476
|
+
transactsql: "transactsql",
|
|
477
|
+
mysql: "mysql"
|
|
478
|
+
};
|
|
479
|
+
const scopeDialect = scopeDialects[dialect];
|
|
480
|
+
if (!scopeDialect) {
|
|
481
|
+
throw new TypeError(
|
|
482
|
+
`No scope dialect mapping for formatter language "${dialect}". Add it to the scopeDialects map in Adapter.#checkScope.`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
let references;
|
|
486
|
+
try {
|
|
487
|
+
references = extractBaseEntityReferences(sql, scopeDialect);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
return buildScopeParseErrorPayload(
|
|
490
|
+
sql,
|
|
491
|
+
dialect,
|
|
492
|
+
error
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
if (references.length === 0) return null;
|
|
496
|
+
const allowedQualified = new Set(
|
|
497
|
+
allowedEntities.map((e) => e.toLowerCase())
|
|
498
|
+
);
|
|
499
|
+
const allowedUnqualified = /* @__PURE__ */ new Set();
|
|
500
|
+
for (const entity of allowedEntities) {
|
|
501
|
+
const dot = entity.lastIndexOf(".");
|
|
502
|
+
if (dot !== -1) {
|
|
503
|
+
allowedUnqualified.add(entity.slice(dot + 1).toLowerCase());
|
|
504
|
+
} else {
|
|
505
|
+
allowedUnqualified.add(entity.toLowerCase());
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
const outOfScope = references.map((ref) => ref.db ? `${ref.db}.${ref.table}` : ref.table).filter((name) => {
|
|
509
|
+
const lower = name.toLowerCase();
|
|
510
|
+
if (name.includes(".")) {
|
|
511
|
+
if (allowedQualified.has(lower)) return false;
|
|
512
|
+
const parts = lower.split(".");
|
|
513
|
+
if (parts.length >= 3) {
|
|
514
|
+
const datasetTable = parts.slice(-2).join(".");
|
|
515
|
+
if (allowedQualified.has(datasetTable)) return false;
|
|
516
|
+
}
|
|
517
|
+
return true;
|
|
518
|
+
}
|
|
519
|
+
return !allowedQualified.has(lower) && !allowedUnqualified.has(lower);
|
|
520
|
+
});
|
|
521
|
+
if (outOfScope.length === 0) return null;
|
|
522
|
+
return buildOutOfScopePayload(sql, outOfScope, allowedEntities);
|
|
523
|
+
}
|
|
524
|
+
async validate(sql) {
|
|
525
|
+
const allowed = await this.#resolveScope();
|
|
526
|
+
const scopeError = await this.#checkScope(sql, allowed);
|
|
527
|
+
if (scopeError) return JSON.stringify(scopeError);
|
|
528
|
+
return this.validateImpl(sql);
|
|
529
|
+
}
|
|
530
|
+
async execute(sql) {
|
|
531
|
+
const allowed = await this.#resolveScope();
|
|
532
|
+
const scopeError = await this.#checkScope(sql, allowed);
|
|
533
|
+
if (scopeError) throw new SQLScopeError(scopeError);
|
|
534
|
+
return this.executeImpl(sql);
|
|
535
|
+
}
|
|
296
536
|
/**
|
|
297
537
|
* Convert unknown database value to number.
|
|
298
538
|
* Handles number, bigint, and string types.
|
|
@@ -413,48 +653,6 @@ function getTablesWithRelated(allTables, relationships, filter) {
|
|
|
413
653
|
return Array.from(result);
|
|
414
654
|
}
|
|
415
655
|
|
|
416
|
-
// packages/text2sql/src/lib/agents/exceptions.ts
|
|
417
|
-
var sqlValidationMarker = Symbol("SQLValidationError");
|
|
418
|
-
var unanswerableSqlMarker = Symbol("UnanswerableSQLError");
|
|
419
|
-
var sqlScopeMarker = Symbol("SQLScopeError");
|
|
420
|
-
var SQLValidationError = class _SQLValidationError extends Error {
|
|
421
|
-
[sqlValidationMarker];
|
|
422
|
-
constructor(message) {
|
|
423
|
-
super(message);
|
|
424
|
-
this.name = "SQLValidationError";
|
|
425
|
-
this[sqlValidationMarker] = true;
|
|
426
|
-
}
|
|
427
|
-
static isInstance(error) {
|
|
428
|
-
return error instanceof _SQLValidationError && error[sqlValidationMarker] === true;
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
var UnanswerableSQLError = class _UnanswerableSQLError extends Error {
|
|
432
|
-
[unanswerableSqlMarker];
|
|
433
|
-
constructor(message) {
|
|
434
|
-
super(message);
|
|
435
|
-
this.name = "UnanswerableSQLError";
|
|
436
|
-
this[unanswerableSqlMarker] = true;
|
|
437
|
-
}
|
|
438
|
-
static isInstance(error) {
|
|
439
|
-
return error instanceof _UnanswerableSQLError && error[unanswerableSqlMarker] === true;
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
var SQLScopeError = class _SQLScopeError extends Error {
|
|
443
|
-
[sqlScopeMarker];
|
|
444
|
-
payload;
|
|
445
|
-
errorType;
|
|
446
|
-
constructor(payload) {
|
|
447
|
-
super(JSON.stringify(payload));
|
|
448
|
-
this.name = "SQLScopeError";
|
|
449
|
-
this.payload = payload;
|
|
450
|
-
this.errorType = payload.error_type;
|
|
451
|
-
this[sqlScopeMarker] = true;
|
|
452
|
-
}
|
|
453
|
-
static isInstance(error) {
|
|
454
|
-
return error instanceof _SQLScopeError && error[sqlScopeMarker] === true;
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
|
|
458
656
|
// packages/text2sql/src/lib/agents/result-tools.ts
|
|
459
657
|
import { tool } from "ai";
|
|
460
658
|
import { createBashTool } from "bash-tool";
|
|
@@ -463,172 +661,17 @@ import {
|
|
|
463
661
|
Bash,
|
|
464
662
|
MountableFs,
|
|
465
663
|
OverlayFs,
|
|
466
|
-
defineCommand
|
|
467
|
-
parse
|
|
664
|
+
defineCommand
|
|
468
665
|
} from "just-bash";
|
|
469
666
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
470
|
-
import * as
|
|
667
|
+
import * as path2 from "node:path";
|
|
471
668
|
import { v7 } from "uuid";
|
|
472
669
|
import z from "zod";
|
|
473
|
-
function createCommand(name, subcommands) {
|
|
474
|
-
const usageLines = Object.entries(subcommands).map(([, def]) => ` ${name} ${def.usage.padEnd(30)} ${def.description}`).join("\n");
|
|
475
|
-
return defineCommand(name, async (args, ctx) => {
|
|
476
|
-
const subcommand = args[0];
|
|
477
|
-
const restArgs = args.slice(1);
|
|
478
|
-
if (subcommand && subcommand in subcommands) {
|
|
479
|
-
return subcommands[subcommand].handler(restArgs, ctx);
|
|
480
|
-
}
|
|
481
|
-
return {
|
|
482
|
-
stdout: "",
|
|
483
|
-
stderr: `${name}: ${subcommand ? `unknown subcommand '${subcommand}'` : "missing subcommand"}
|
|
484
670
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
function validateReadOnly(query) {
|
|
492
|
-
const upper = query.toUpperCase().trim();
|
|
493
|
-
if (!upper.startsWith("SELECT") && !upper.startsWith("WITH")) {
|
|
494
|
-
return { valid: false, error: "only SELECT or WITH queries allowed" };
|
|
495
|
-
}
|
|
496
|
-
return { valid: true };
|
|
497
|
-
}
|
|
498
|
-
var SQL_VALIDATE_REMINDER = "Always run `sql validate` before `sql run` to catch syntax errors early.";
|
|
499
|
-
function createSqlCommand(adapter, metaStore) {
|
|
500
|
-
return createCommand("sql", {
|
|
501
|
-
run: {
|
|
502
|
-
usage: 'run "SELECT ..."',
|
|
503
|
-
description: "Execute query and store results",
|
|
504
|
-
handler: async (args, ctx) => {
|
|
505
|
-
const store = metaStore.getStore();
|
|
506
|
-
if (store) {
|
|
507
|
-
store.value = { ...store.value, reminder: SQL_VALIDATE_REMINDER };
|
|
508
|
-
}
|
|
509
|
-
const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
510
|
-
if (!rawQuery) {
|
|
511
|
-
return {
|
|
512
|
-
stdout: "",
|
|
513
|
-
stderr: "sql run: no query provided",
|
|
514
|
-
exitCode: 1
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
const validation = validateReadOnly(rawQuery);
|
|
518
|
-
if (!validation.valid) {
|
|
519
|
-
return {
|
|
520
|
-
stdout: "",
|
|
521
|
-
stderr: `sql run: ${validation.error}`,
|
|
522
|
-
exitCode: 1
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
const query = adapter.format(rawQuery);
|
|
526
|
-
if (store) {
|
|
527
|
-
store.value = { ...store.value, formattedSql: query };
|
|
528
|
-
}
|
|
529
|
-
const syntaxError = await adapter.validate(query);
|
|
530
|
-
if (syntaxError) {
|
|
531
|
-
return {
|
|
532
|
-
stdout: "",
|
|
533
|
-
stderr: `sql run: ${syntaxError}`,
|
|
534
|
-
exitCode: 1
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
try {
|
|
538
|
-
const rows = await adapter.execute(query);
|
|
539
|
-
const rowsArray = Array.isArray(rows) ? rows : [];
|
|
540
|
-
const content = JSON.stringify(rowsArray, null, 2);
|
|
541
|
-
const filename = `${v7()}.json`;
|
|
542
|
-
const sqlPath = `/sql/${filename}`;
|
|
543
|
-
await ctx.fs.mkdir("/sql", { recursive: true });
|
|
544
|
-
await ctx.fs.writeFile(sqlPath, content);
|
|
545
|
-
const columns = rowsArray.length > 0 ? Object.keys(rowsArray[0]) : [];
|
|
546
|
-
return {
|
|
547
|
-
stdout: [
|
|
548
|
-
`results stored in ${sqlPath}`,
|
|
549
|
-
`columns: ${columns.join(", ") || "(none)"}`,
|
|
550
|
-
`rows: ${rowsArray.length}`
|
|
551
|
-
].join("\n") + "\n",
|
|
552
|
-
stderr: "",
|
|
553
|
-
exitCode: 0
|
|
554
|
-
};
|
|
555
|
-
} catch (error) {
|
|
556
|
-
return {
|
|
557
|
-
stdout: "",
|
|
558
|
-
stderr: `sql run: ${error instanceof Error ? error.message : String(error)}`,
|
|
559
|
-
exitCode: 1
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
},
|
|
564
|
-
validate: {
|
|
565
|
-
usage: 'validate "SELECT ..."',
|
|
566
|
-
description: "Validate query syntax",
|
|
567
|
-
handler: async (args) => {
|
|
568
|
-
const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
569
|
-
if (!rawQuery) {
|
|
570
|
-
return {
|
|
571
|
-
stdout: "",
|
|
572
|
-
stderr: "sql validate: no query provided",
|
|
573
|
-
exitCode: 1
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
const validation = validateReadOnly(rawQuery);
|
|
577
|
-
if (!validation.valid) {
|
|
578
|
-
return {
|
|
579
|
-
stdout: "",
|
|
580
|
-
stderr: `sql validate: ${validation.error}`,
|
|
581
|
-
exitCode: 1
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
const query = adapter.format(rawQuery);
|
|
585
|
-
const store = metaStore.getStore();
|
|
586
|
-
if (store) store.value = { ...store.value, formattedSql: query };
|
|
587
|
-
const syntaxError = await adapter.validate(query);
|
|
588
|
-
if (syntaxError) {
|
|
589
|
-
return {
|
|
590
|
-
stdout: "",
|
|
591
|
-
stderr: `sql validate: ${syntaxError}`,
|
|
592
|
-
exitCode: 1
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
return {
|
|
596
|
-
stdout: "valid\n",
|
|
597
|
-
stderr: "",
|
|
598
|
-
exitCode: 0
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
var BLOCKED_DB_CLIENT_COMMANDS = /* @__PURE__ */ new Set([
|
|
605
|
-
"psql",
|
|
606
|
-
"sqlite3",
|
|
607
|
-
"mysql",
|
|
608
|
-
"duckdb"
|
|
609
|
-
]);
|
|
610
|
-
var BLOCKED_RAW_SQL_COMMANDS = /* @__PURE__ */ new Set(["select", "with"]);
|
|
611
|
-
var ALLOWED_SQL_PROXY_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "validate"]);
|
|
612
|
-
var SHELL_INTERPRETER_COMMANDS = /* @__PURE__ */ new Set([
|
|
613
|
-
"bash",
|
|
614
|
-
"sh",
|
|
615
|
-
"zsh",
|
|
616
|
-
"dash",
|
|
617
|
-
"ksh"
|
|
618
|
-
]);
|
|
619
|
-
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["env", "command", "eval"]);
|
|
620
|
-
var SQL_PROXY_ENFORCEMENT_MESSAGE = [
|
|
621
|
-
"Direct database querying through bash is blocked.",
|
|
622
|
-
"Use SQL proxy commands in this order:",
|
|
623
|
-
'1) sql validate "SELECT ..."',
|
|
624
|
-
'2) sql run "SELECT ..."'
|
|
625
|
-
].join("\n");
|
|
626
|
-
function cloneInspectionContext(context) {
|
|
627
|
-
return {
|
|
628
|
-
functionDefinitions: new Map(context.functionDefinitions),
|
|
629
|
-
callStack: new Set(context.callStack)
|
|
630
|
-
};
|
|
631
|
-
}
|
|
671
|
+
// packages/text2sql/src/lib/agents/sql-transform-plugins.ts
|
|
672
|
+
import { parse, serialize } from "just-bash";
|
|
673
|
+
import { createHash } from "node:crypto";
|
|
674
|
+
import * as path from "node:path";
|
|
632
675
|
function asStaticWordText(word) {
|
|
633
676
|
if (!word) {
|
|
634
677
|
return null;
|
|
@@ -672,6 +715,40 @@ function isScriptNode(value) {
|
|
|
672
715
|
const node = value;
|
|
673
716
|
return node.type === "Script" && Array.isArray(node.statements);
|
|
674
717
|
}
|
|
718
|
+
var BLOCKED_DB_CLIENT_COMMANDS = /* @__PURE__ */ new Set([
|
|
719
|
+
"psql",
|
|
720
|
+
"sqlite3",
|
|
721
|
+
"mysql",
|
|
722
|
+
"duckdb"
|
|
723
|
+
]);
|
|
724
|
+
var BLOCKED_RAW_SQL_COMMANDS = /* @__PURE__ */ new Set(["select", "with"]);
|
|
725
|
+
var ALLOWED_SQL_PROXY_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "validate"]);
|
|
726
|
+
var SHELL_INTERPRETER_COMMANDS = /* @__PURE__ */ new Set([
|
|
727
|
+
"bash",
|
|
728
|
+
"sh",
|
|
729
|
+
"zsh",
|
|
730
|
+
"dash",
|
|
731
|
+
"ksh"
|
|
732
|
+
]);
|
|
733
|
+
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["env", "command", "eval"]);
|
|
734
|
+
var SQL_PROXY_ENFORCEMENT_MESSAGE = [
|
|
735
|
+
"Direct database querying through bash is blocked.",
|
|
736
|
+
"Use SQL proxy commands in this order:",
|
|
737
|
+
'1) sql validate "SELECT ..."',
|
|
738
|
+
'2) sql run "SELECT ..."'
|
|
739
|
+
].join("\n");
|
|
740
|
+
var SqlProxyViolationError = class extends Error {
|
|
741
|
+
constructor() {
|
|
742
|
+
super(SQL_PROXY_ENFORCEMENT_MESSAGE);
|
|
743
|
+
this.name = "SqlProxyViolationError";
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
function cloneInspectionContext(context) {
|
|
747
|
+
return {
|
|
748
|
+
functionDefinitions: new Map(context.functionDefinitions),
|
|
749
|
+
callStack: new Set(context.callStack)
|
|
750
|
+
};
|
|
751
|
+
}
|
|
675
752
|
function scriptContainsBlockedCommand(script, context, mode = "blocked-only") {
|
|
676
753
|
return statementsContainBlockedCommand(script.statements, context, mode);
|
|
677
754
|
}
|
|
@@ -1236,39 +1313,288 @@ function isBlockedSimpleCommand(command, context, mode, options) {
|
|
|
1236
1313
|
}
|
|
1237
1314
|
return false;
|
|
1238
1315
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1316
|
+
var SqlProxyEnforcementPlugin = class {
|
|
1317
|
+
name = "sql-proxy-enforcement";
|
|
1318
|
+
transform(context) {
|
|
1319
|
+
const blocked = scriptContainsBlockedCommand(context.ast, {
|
|
1320
|
+
functionDefinitions: /* @__PURE__ */ new Map(),
|
|
1321
|
+
callStack: /* @__PURE__ */ new Set()
|
|
1322
|
+
});
|
|
1323
|
+
if (blocked) {
|
|
1324
|
+
throw new SqlProxyViolationError();
|
|
1325
|
+
}
|
|
1326
|
+
return {
|
|
1327
|
+
ast: context.ast,
|
|
1328
|
+
metadata: { inspected: true }
|
|
1329
|
+
};
|
|
1243
1330
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1331
|
+
};
|
|
1332
|
+
function wordPartsContainBacktickSubstitution(parts) {
|
|
1333
|
+
for (const part of parts) {
|
|
1334
|
+
if (part.type === "CommandSubstitution" && part.legacy === true) {
|
|
1335
|
+
return true;
|
|
1336
|
+
}
|
|
1337
|
+
if (part.type === "DoubleQuoted" && Array.isArray(part.parts)) {
|
|
1338
|
+
if (wordPartsContainBacktickSubstitution(
|
|
1339
|
+
part.parts
|
|
1340
|
+
)) {
|
|
1341
|
+
return true;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
return false;
|
|
1346
|
+
}
|
|
1347
|
+
function isSqlBacktickCommand(cmd) {
|
|
1348
|
+
if (cmd.type !== "SimpleCommand") return false;
|
|
1349
|
+
if (cmd.assignments.length > 0 || cmd.redirections.length > 0) return false;
|
|
1350
|
+
if (asStaticWordText(cmd.name) !== "sql") return false;
|
|
1351
|
+
if (cmd.args.length < 2) return false;
|
|
1352
|
+
const subcommand = asStaticWordText(cmd.args[0]);
|
|
1353
|
+
if (subcommand !== "validate" && subcommand !== "run") return false;
|
|
1354
|
+
const sqlArgs = cmd.args.slice(1);
|
|
1355
|
+
return sqlArgs.some(
|
|
1356
|
+
(arg) => wordPartsContainBacktickSubstitution(
|
|
1357
|
+
arg.parts
|
|
1358
|
+
)
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
function extractWordPartText(parts) {
|
|
1362
|
+
let text = "";
|
|
1363
|
+
for (const part of parts) {
|
|
1364
|
+
const type = part.type;
|
|
1365
|
+
if (type === "Literal" || type === "SingleQuoted" || type === "Escaped") {
|
|
1366
|
+
text += part.value;
|
|
1367
|
+
} else if (type === "DoubleQuoted" && Array.isArray(part.parts)) {
|
|
1368
|
+
text += extractWordPartText(part.parts);
|
|
1369
|
+
} else if (type === "CommandSubstitution" && part.legacy === true) {
|
|
1370
|
+
text += "`" + serialize(part.body).trim() + "`";
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return text;
|
|
1374
|
+
}
|
|
1375
|
+
function extractSqlText(args) {
|
|
1376
|
+
return args.map(
|
|
1377
|
+
(arg) => extractWordPartText(
|
|
1378
|
+
arg.parts
|
|
1379
|
+
)
|
|
1380
|
+
).join(" ");
|
|
1381
|
+
}
|
|
1382
|
+
function rewriteSqlBacktickCommand(cmd, ast) {
|
|
1383
|
+
const subcommand = asStaticWordText(cmd.args[0]);
|
|
1384
|
+
const sqlArgs = cmd.args.slice(1);
|
|
1385
|
+
const unquotedSql = extractSqlText(sqlArgs);
|
|
1386
|
+
const hash2 = createHash("sha256").update(serialize(ast)).digest("hex").slice(0, 12);
|
|
1387
|
+
const sqlPath = `/tmp/sql-inline-${subcommand}-${hash2}.sql`;
|
|
1388
|
+
const heredocTag = `SQL_${hash2.toUpperCase()}`;
|
|
1389
|
+
const groupScript = [
|
|
1390
|
+
`{ cat > ${sqlPath} <<'${heredocTag}'`,
|
|
1391
|
+
unquotedSql,
|
|
1392
|
+
heredocTag,
|
|
1393
|
+
`sql ${subcommand} "$(cat ${sqlPath})"; }`
|
|
1394
|
+
].join("\n");
|
|
1395
|
+
const groupAst = parse(groupScript);
|
|
1396
|
+
return groupAst.statements[0].pipelines[0].commands[0];
|
|
1397
|
+
}
|
|
1398
|
+
function rewriteBackticksInCommands(commands, ast) {
|
|
1399
|
+
let rewritten = false;
|
|
1400
|
+
for (let i = 0; i < commands.length; i++) {
|
|
1401
|
+
const cmd = commands[i];
|
|
1402
|
+
if (isSqlBacktickCommand(cmd)) {
|
|
1403
|
+
commands[i] = rewriteSqlBacktickCommand(cmd, ast);
|
|
1404
|
+
rewritten = true;
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
if (cmd.type === "If") {
|
|
1408
|
+
for (const clause of cmd.clauses) {
|
|
1409
|
+
if (rewriteBackticksInStatements(clause.condition, ast))
|
|
1410
|
+
rewritten = true;
|
|
1411
|
+
if (rewriteBackticksInStatements(clause.body, ast)) rewritten = true;
|
|
1412
|
+
}
|
|
1413
|
+
if (cmd.elseBody && rewriteBackticksInStatements(cmd.elseBody, ast)) {
|
|
1414
|
+
rewritten = true;
|
|
1415
|
+
}
|
|
1416
|
+
} else if (cmd.type === "While" || cmd.type === "Until") {
|
|
1417
|
+
if (rewriteBackticksInStatements(cmd.condition, ast)) rewritten = true;
|
|
1418
|
+
if (rewriteBackticksInStatements(cmd.body, ast)) rewritten = true;
|
|
1419
|
+
} else if (cmd.type === "For" || cmd.type === "CStyleFor") {
|
|
1420
|
+
if (rewriteBackticksInStatements(cmd.body, ast)) rewritten = true;
|
|
1421
|
+
} else if (cmd.type === "Case") {
|
|
1422
|
+
for (const item of cmd.items) {
|
|
1423
|
+
if (rewriteBackticksInStatements(item.body, ast)) rewritten = true;
|
|
1424
|
+
}
|
|
1425
|
+
} else if (cmd.type === "Subshell" || cmd.type === "Group") {
|
|
1426
|
+
if (rewriteBackticksInStatements(cmd.body, ast)) rewritten = true;
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
return rewritten;
|
|
1430
|
+
}
|
|
1431
|
+
function rewriteBackticksInStatements(statements, ast) {
|
|
1432
|
+
let rewritten = false;
|
|
1433
|
+
for (const statement of statements) {
|
|
1434
|
+
for (const pipeline of statement.pipelines) {
|
|
1435
|
+
if (rewriteBackticksInCommands(pipeline.commands, ast)) {
|
|
1436
|
+
rewritten = true;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1249
1439
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1440
|
+
return rewritten;
|
|
1441
|
+
}
|
|
1442
|
+
var SqlBacktickRewritePlugin = class {
|
|
1443
|
+
name = "sql-backtick-rewrite";
|
|
1444
|
+
transform(context) {
|
|
1445
|
+
const ast = context.ast;
|
|
1446
|
+
const rewritten = rewriteBackticksInStatements(ast.statements, ast);
|
|
1447
|
+
return { ast, metadata: { rewritten } };
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1451
|
+
// packages/text2sql/src/lib/agents/result-tools.ts
|
|
1452
|
+
function createCommand(name, subcommands) {
|
|
1453
|
+
const usageLines = Object.entries(subcommands).map(([, def]) => ` ${name} ${def.usage.padEnd(30)} ${def.description}`).join("\n");
|
|
1454
|
+
return defineCommand(name, async (args, ctx) => {
|
|
1455
|
+
const subcommand = args[0];
|
|
1456
|
+
const restArgs = args.slice(1);
|
|
1457
|
+
if (subcommand && subcommand in subcommands) {
|
|
1458
|
+
return subcommands[subcommand].handler(restArgs, ctx);
|
|
1459
|
+
}
|
|
1460
|
+
return {
|
|
1461
|
+
stdout: "",
|
|
1462
|
+
stderr: `${name}: ${subcommand ? `unknown subcommand '${subcommand}'` : "missing subcommand"}
|
|
1463
|
+
|
|
1464
|
+
Usage:
|
|
1465
|
+
${usageLines}`,
|
|
1466
|
+
exitCode: 1
|
|
1467
|
+
};
|
|
1253
1468
|
});
|
|
1254
|
-
|
|
1255
|
-
|
|
1469
|
+
}
|
|
1470
|
+
function validateReadOnly(query) {
|
|
1471
|
+
const upper = query.toUpperCase().trim();
|
|
1472
|
+
if (!upper.startsWith("SELECT") && !upper.startsWith("WITH")) {
|
|
1473
|
+
return { valid: false, error: "only SELECT or WITH queries allowed" };
|
|
1256
1474
|
}
|
|
1257
|
-
return {
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1475
|
+
return { valid: true };
|
|
1476
|
+
}
|
|
1477
|
+
var SQL_VALIDATE_REMINDER = "Always run `sql validate` before `sql run` to catch syntax errors early.";
|
|
1478
|
+
function createSqlCommand(adapter, metaStore) {
|
|
1479
|
+
return createCommand("sql", {
|
|
1480
|
+
run: {
|
|
1481
|
+
usage: 'run "SELECT ..."',
|
|
1482
|
+
description: "Execute query and store results",
|
|
1483
|
+
handler: async (args, ctx) => {
|
|
1484
|
+
const store = metaStore.getStore();
|
|
1485
|
+
if (store) {
|
|
1486
|
+
store.value = { ...store.value, reminder: SQL_VALIDATE_REMINDER };
|
|
1487
|
+
}
|
|
1488
|
+
const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
1489
|
+
if (!rawQuery) {
|
|
1490
|
+
return {
|
|
1491
|
+
stdout: "",
|
|
1492
|
+
stderr: "sql run: no query provided",
|
|
1493
|
+
exitCode: 1
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
const validation = validateReadOnly(rawQuery);
|
|
1497
|
+
if (!validation.valid) {
|
|
1498
|
+
return {
|
|
1499
|
+
stdout: "",
|
|
1500
|
+
stderr: `sql run: ${validation.error}`,
|
|
1501
|
+
exitCode: 1
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
const query = adapter.format(rawQuery);
|
|
1505
|
+
if (store) {
|
|
1506
|
+
store.value = { ...store.value, formattedSql: query };
|
|
1507
|
+
}
|
|
1508
|
+
const syntaxError = await adapter.validate(query);
|
|
1509
|
+
if (syntaxError) {
|
|
1510
|
+
return {
|
|
1511
|
+
stdout: "",
|
|
1512
|
+
stderr: `sql run: ${syntaxError}`,
|
|
1513
|
+
exitCode: 1
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
try {
|
|
1517
|
+
const rows = await adapter.execute(query);
|
|
1518
|
+
const rowsArray = Array.isArray(rows) ? rows : [];
|
|
1519
|
+
const content = JSON.stringify(rowsArray, null, 2);
|
|
1520
|
+
const filename = `${v7()}.json`;
|
|
1521
|
+
const sqlPath = `/sql/${filename}`;
|
|
1522
|
+
await ctx.fs.mkdir("/sql", { recursive: true });
|
|
1523
|
+
await ctx.fs.writeFile(sqlPath, content);
|
|
1524
|
+
const columns = rowsArray.length > 0 ? Object.keys(rowsArray[0]) : [];
|
|
1525
|
+
return {
|
|
1526
|
+
stdout: [
|
|
1527
|
+
`results stored in ${sqlPath}`,
|
|
1528
|
+
`columns: ${columns.join(", ") || "(none)"}`,
|
|
1529
|
+
`rows: ${rowsArray.length}`
|
|
1530
|
+
].join("\n") + "\n",
|
|
1531
|
+
stderr: "",
|
|
1532
|
+
exitCode: 0
|
|
1533
|
+
};
|
|
1534
|
+
} catch (error) {
|
|
1535
|
+
return {
|
|
1536
|
+
stdout: "",
|
|
1537
|
+
stderr: `sql run: ${error instanceof Error ? error.message : String(error)}`,
|
|
1538
|
+
exitCode: 1
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
},
|
|
1543
|
+
validate: {
|
|
1544
|
+
usage: 'validate "SELECT ..."',
|
|
1545
|
+
description: "Validate query syntax",
|
|
1546
|
+
handler: async (args) => {
|
|
1547
|
+
const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
1548
|
+
if (!rawQuery) {
|
|
1549
|
+
return {
|
|
1550
|
+
stdout: "",
|
|
1551
|
+
stderr: "sql validate: no query provided",
|
|
1552
|
+
exitCode: 1
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
const validation = validateReadOnly(rawQuery);
|
|
1556
|
+
if (!validation.valid) {
|
|
1557
|
+
return {
|
|
1558
|
+
stdout: "",
|
|
1559
|
+
stderr: `sql validate: ${validation.error}`,
|
|
1560
|
+
exitCode: 1
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
const query = adapter.format(rawQuery);
|
|
1564
|
+
const store = metaStore.getStore();
|
|
1565
|
+
if (store) store.value = { ...store.value, formattedSql: query };
|
|
1566
|
+
const syntaxError = await adapter.validate(query);
|
|
1567
|
+
if (syntaxError) {
|
|
1568
|
+
return {
|
|
1569
|
+
stdout: "",
|
|
1570
|
+
stderr: `sql validate: ${syntaxError}`,
|
|
1571
|
+
exitCode: 1
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
return {
|
|
1575
|
+
stdout: "valid\n",
|
|
1576
|
+
stderr: "",
|
|
1577
|
+
exitCode: 0
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
function toViolationResult(error) {
|
|
1584
|
+
if (error instanceof SqlProxyViolationError) {
|
|
1585
|
+
return { stdout: "", stderr: `${error.message}
|
|
1586
|
+
`, exitCode: 1 };
|
|
1587
|
+
}
|
|
1588
|
+
return null;
|
|
1263
1589
|
}
|
|
1264
1590
|
async function createResultTools(options) {
|
|
1265
1591
|
const { adapter, skillMounts, filesystem: baseFs } = options;
|
|
1266
1592
|
const metaStore = new AsyncLocalStorage();
|
|
1267
1593
|
const sqlCommand = createSqlCommand(adapter, metaStore);
|
|
1268
1594
|
const fsMounts = skillMounts.map(({ host, sandbox: sandbox2 }) => ({
|
|
1269
|
-
mountPoint:
|
|
1595
|
+
mountPoint: path2.dirname(sandbox2),
|
|
1270
1596
|
filesystem: new OverlayFs({
|
|
1271
|
-
root:
|
|
1597
|
+
root: path2.dirname(host),
|
|
1272
1598
|
mountPoint: "/",
|
|
1273
1599
|
readOnly: true
|
|
1274
1600
|
})
|
|
@@ -1281,6 +1607,8 @@ async function createResultTools(options) {
|
|
|
1281
1607
|
customCommands: [sqlCommand],
|
|
1282
1608
|
fs: filesystem
|
|
1283
1609
|
});
|
|
1610
|
+
bashInstance.registerTransformPlugin(new SqlProxyEnforcementPlugin());
|
|
1611
|
+
bashInstance.registerTransformPlugin(new SqlBacktickRewritePlugin());
|
|
1284
1612
|
const { sandbox, tools } = await createBashTool({
|
|
1285
1613
|
sandbox: bashInstance,
|
|
1286
1614
|
destination: "/",
|
|
@@ -1299,11 +1627,13 @@ async function createResultTools(options) {
|
|
|
1299
1627
|
const guardedSandbox = {
|
|
1300
1628
|
...sandbox,
|
|
1301
1629
|
executeCommand: async (command) => {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1630
|
+
try {
|
|
1631
|
+
return await sandbox.executeCommand(command);
|
|
1632
|
+
} catch (error) {
|
|
1633
|
+
const violation = toViolationResult(error);
|
|
1634
|
+
if (violation) return violation;
|
|
1635
|
+
throw error;
|
|
1305
1636
|
}
|
|
1306
|
-
return sandbox.executeCommand(command);
|
|
1307
1637
|
}
|
|
1308
1638
|
};
|
|
1309
1639
|
const bash = tool({
|
|
@@ -1317,16 +1647,18 @@ async function createResultTools(options) {
|
|
|
1317
1647
|
if (!execute) {
|
|
1318
1648
|
throw new Error("bash tool execution is not available");
|
|
1319
1649
|
}
|
|
1320
|
-
const blockedResult = getSqlProxyEnforcementResult(command);
|
|
1321
|
-
if (blockedResult) {
|
|
1322
|
-
return blockedResult;
|
|
1323
|
-
}
|
|
1324
1650
|
return metaStore.run({}, async () => {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1651
|
+
try {
|
|
1652
|
+
const result = await execute({ command }, execOptions);
|
|
1653
|
+
const storeValue = metaStore.getStore()?.value;
|
|
1654
|
+
if (!storeValue) return result;
|
|
1655
|
+
const { reminder, ...meta } = storeValue;
|
|
1656
|
+
return { ...result, meta, reminder };
|
|
1657
|
+
} catch (error) {
|
|
1658
|
+
const violation = toViolationResult(error);
|
|
1659
|
+
if (violation) return violation;
|
|
1660
|
+
throw error;
|
|
1661
|
+
}
|
|
1330
1662
|
});
|
|
1331
1663
|
},
|
|
1332
1664
|
toModelOutput: ({ output }) => {
|
|
@@ -1812,16 +2144,16 @@ var suggestionsAgent = agent({
|
|
|
1812
2144
|
});
|
|
1813
2145
|
|
|
1814
2146
|
// packages/text2sql/src/lib/checkpoint.ts
|
|
1815
|
-
import { createHash } from "node:crypto";
|
|
2147
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1816
2148
|
import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
1817
2149
|
import pLimit from "p-limit";
|
|
1818
2150
|
var Checkpoint = class _Checkpoint {
|
|
1819
2151
|
points;
|
|
1820
2152
|
path;
|
|
1821
2153
|
configHash;
|
|
1822
|
-
constructor(
|
|
2154
|
+
constructor(path7, configHash, points) {
|
|
1823
2155
|
this.points = points;
|
|
1824
|
-
this.path =
|
|
2156
|
+
this.path = path7;
|
|
1825
2157
|
this.configHash = configHash;
|
|
1826
2158
|
}
|
|
1827
2159
|
/**
|
|
@@ -1829,14 +2161,14 @@ var Checkpoint = class _Checkpoint {
|
|
|
1829
2161
|
* Handles corrupted files and config changes gracefully.
|
|
1830
2162
|
*/
|
|
1831
2163
|
static async load(options) {
|
|
1832
|
-
const { path:
|
|
1833
|
-
if (existsSync(
|
|
2164
|
+
const { path: path7, configHash } = options;
|
|
2165
|
+
if (existsSync(path7)) {
|
|
1834
2166
|
try {
|
|
1835
|
-
const content = readFileSync(
|
|
2167
|
+
const content = readFileSync(path7, "utf-8");
|
|
1836
2168
|
const file = JSON.parse(content);
|
|
1837
2169
|
if (configHash && file.configHash && file.configHash !== configHash) {
|
|
1838
2170
|
console.log("\u26A0 Config changed, starting fresh");
|
|
1839
|
-
return new _Checkpoint(
|
|
2171
|
+
return new _Checkpoint(path7, configHash, {});
|
|
1840
2172
|
}
|
|
1841
2173
|
const points = file.points ?? {};
|
|
1842
2174
|
const totalEntries = Object.values(points).reduce(
|
|
@@ -1844,14 +2176,14 @@ var Checkpoint = class _Checkpoint {
|
|
|
1844
2176
|
0
|
|
1845
2177
|
);
|
|
1846
2178
|
console.log(`\u2713 Resuming from checkpoint (${totalEntries} entries)`);
|
|
1847
|
-
return new _Checkpoint(
|
|
2179
|
+
return new _Checkpoint(path7, configHash, points);
|
|
1848
2180
|
} catch {
|
|
1849
2181
|
console.log("\u26A0 Checkpoint corrupted, starting fresh");
|
|
1850
|
-
return new _Checkpoint(
|
|
2182
|
+
return new _Checkpoint(path7, configHash, {});
|
|
1851
2183
|
}
|
|
1852
2184
|
}
|
|
1853
2185
|
console.log("Starting new checkpoint");
|
|
1854
|
-
return new _Checkpoint(
|
|
2186
|
+
return new _Checkpoint(path7, configHash, {});
|
|
1855
2187
|
}
|
|
1856
2188
|
/**
|
|
1857
2189
|
* Run a single computation with checkpointing.
|
|
@@ -1933,7 +2265,7 @@ var Checkpoint = class _Checkpoint {
|
|
|
1933
2265
|
}
|
|
1934
2266
|
};
|
|
1935
2267
|
function hash(value) {
|
|
1936
|
-
return
|
|
2268
|
+
return createHash2("md5").update(JSON.stringify(value)).digest("hex");
|
|
1937
2269
|
}
|
|
1938
2270
|
var Point = class {
|
|
1939
2271
|
#cache;
|
|
@@ -1977,20 +2309,20 @@ var Point = class {
|
|
|
1977
2309
|
}
|
|
1978
2310
|
};
|
|
1979
2311
|
function hashConfig(config) {
|
|
1980
|
-
return
|
|
2312
|
+
return createHash2("md5").update(JSON.stringify(config)).digest("hex");
|
|
1981
2313
|
}
|
|
1982
2314
|
|
|
1983
2315
|
// packages/text2sql/src/lib/file-cache.ts
|
|
1984
|
-
import { createHash as
|
|
2316
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
1985
2317
|
import { existsSync as existsSync2 } from "node:fs";
|
|
1986
2318
|
import { readFile, writeFile } from "node:fs/promises";
|
|
1987
2319
|
import { tmpdir } from "node:os";
|
|
1988
|
-
import
|
|
2320
|
+
import path3 from "node:path";
|
|
1989
2321
|
var FileCache = class {
|
|
1990
2322
|
path;
|
|
1991
2323
|
constructor(watermark, extension = ".txt") {
|
|
1992
|
-
const hash2 =
|
|
1993
|
-
this.path =
|
|
2324
|
+
const hash2 = createHash3("md5").update(watermark).digest("hex");
|
|
2325
|
+
this.path = path3.join(tmpdir(), `text2sql-${hash2}${extension}`);
|
|
1994
2326
|
}
|
|
1995
2327
|
async get() {
|
|
1996
2328
|
if (existsSync2(this.path)) {
|
|
@@ -2019,7 +2351,7 @@ var JsonCache = class extends FileCache {
|
|
|
2019
2351
|
};
|
|
2020
2352
|
|
|
2021
2353
|
// packages/text2sql/src/lib/fs/sqlite/sqlite-fs.ts
|
|
2022
|
-
import * as
|
|
2354
|
+
import * as path4 from "node:path";
|
|
2023
2355
|
import { DatabaseSync } from "node:sqlite";
|
|
2024
2356
|
|
|
2025
2357
|
// packages/text2sql/src/lib/fs/sqlite/ddl.sqlite-fs.sql
|
|
@@ -2067,7 +2399,7 @@ var SqliteFs = class {
|
|
|
2067
2399
|
const segments = p.split("/").filter(Boolean);
|
|
2068
2400
|
let currentPath = "/";
|
|
2069
2401
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
2070
|
-
currentPath =
|
|
2402
|
+
currentPath = path4.posix.join(currentPath, segments[i]);
|
|
2071
2403
|
const exists = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
|
|
2072
2404
|
currentPath
|
|
2073
2405
|
);
|
|
@@ -2088,7 +2420,7 @@ var SqliteFs = class {
|
|
|
2088
2420
|
return stmt;
|
|
2089
2421
|
}
|
|
2090
2422
|
#normalizeRoot(root) {
|
|
2091
|
-
return
|
|
2423
|
+
return path4.posix.resolve("/", root.trim());
|
|
2092
2424
|
}
|
|
2093
2425
|
#prefixPath(p) {
|
|
2094
2426
|
if (!this.#root) {
|
|
@@ -2097,7 +2429,7 @@ var SqliteFs = class {
|
|
|
2097
2429
|
if (p === "/") {
|
|
2098
2430
|
return this.#root;
|
|
2099
2431
|
}
|
|
2100
|
-
return
|
|
2432
|
+
return path4.posix.join(this.#root, p);
|
|
2101
2433
|
}
|
|
2102
2434
|
#unprefixPath(p) {
|
|
2103
2435
|
if (!this.#root) {
|
|
@@ -2123,10 +2455,10 @@ var SqliteFs = class {
|
|
|
2123
2455
|
}
|
|
2124
2456
|
}
|
|
2125
2457
|
#normalizePath(p) {
|
|
2126
|
-
return
|
|
2458
|
+
return path4.posix.resolve("/", p);
|
|
2127
2459
|
}
|
|
2128
2460
|
#dirname(p) {
|
|
2129
|
-
const dir =
|
|
2461
|
+
const dir = path4.posix.dirname(p);
|
|
2130
2462
|
return dir === "" ? "/" : dir;
|
|
2131
2463
|
}
|
|
2132
2464
|
#ensureParentExists(filePath) {
|
|
@@ -2189,7 +2521,7 @@ var SqliteFs = class {
|
|
|
2189
2521
|
}
|
|
2190
2522
|
seen.add(p);
|
|
2191
2523
|
const target = this.#normalizePath(
|
|
2192
|
-
|
|
2524
|
+
path4.posix.resolve(this.#dirname(p), entry.symlinkTarget)
|
|
2193
2525
|
);
|
|
2194
2526
|
return this.#resolveSymlink(target, seen);
|
|
2195
2527
|
}
|
|
@@ -2341,11 +2673,11 @@ var SqliteFs = class {
|
|
|
2341
2673
|
this.#useTransaction(() => {
|
|
2342
2674
|
if (options?.recursive) {
|
|
2343
2675
|
const rootPath = this.#root || "/";
|
|
2344
|
-
const relativePath =
|
|
2676
|
+
const relativePath = path4.posix.relative(rootPath, prefixed);
|
|
2345
2677
|
const segments = relativePath.split("/").filter(Boolean);
|
|
2346
2678
|
let currentPath = rootPath;
|
|
2347
2679
|
for (const segment of segments) {
|
|
2348
|
-
currentPath =
|
|
2680
|
+
currentPath = path4.posix.join(currentPath, segment);
|
|
2349
2681
|
const exists = this.#stmt(
|
|
2350
2682
|
"SELECT type FROM fs_entries WHERE path = ?"
|
|
2351
2683
|
).get(currentPath);
|
|
@@ -2396,7 +2728,7 @@ var SqliteFs = class {
|
|
|
2396
2728
|
AND path != ?
|
|
2397
2729
|
AND path NOT LIKE ? || '%/%'`
|
|
2398
2730
|
).all(prefix, resolved, prefix);
|
|
2399
|
-
return rows.map((row) =>
|
|
2731
|
+
return rows.map((row) => path4.posix.basename(row.path));
|
|
2400
2732
|
}
|
|
2401
2733
|
async readdirWithFileTypes(dirPath) {
|
|
2402
2734
|
const normalized = this.#normalizePath(dirPath);
|
|
@@ -2419,7 +2751,7 @@ var SqliteFs = class {
|
|
|
2419
2751
|
AND path NOT LIKE ? || '%/%'`
|
|
2420
2752
|
).all(prefix, resolved, prefix);
|
|
2421
2753
|
return rows.map((row) => ({
|
|
2422
|
-
name:
|
|
2754
|
+
name: path4.posix.basename(row.path),
|
|
2423
2755
|
isFile: row.type === "file",
|
|
2424
2756
|
isDirectory: row.type === "directory",
|
|
2425
2757
|
isSymbolicLink: row.type === "symlink"
|
|
@@ -2474,8 +2806,8 @@ var SqliteFs = class {
|
|
|
2474
2806
|
`SELECT * FROM fs_entries WHERE path = ? OR path LIKE ? || '/%'`
|
|
2475
2807
|
).all(srcPrefixed, srcPrefixed);
|
|
2476
2808
|
for (const entry of allEntries) {
|
|
2477
|
-
const relativePath =
|
|
2478
|
-
const newPath =
|
|
2809
|
+
const relativePath = path4.posix.relative(srcPrefixed, entry.path);
|
|
2810
|
+
const newPath = path4.posix.join(destPrefixed, relativePath);
|
|
2479
2811
|
this.#stmt(
|
|
2480
2812
|
`INSERT OR REPLACE INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
2481
2813
|
VALUES (?, ?, ?, ?, ?, ?)`
|
|
@@ -2542,8 +2874,8 @@ var SqliteFs = class {
|
|
|
2542
2874
|
`SELECT * FROM fs_entries WHERE path = ? OR path LIKE ? || '/%' ORDER BY path DESC`
|
|
2543
2875
|
).all(srcPrefixed, srcPrefixed);
|
|
2544
2876
|
for (const entry of [...allEntries].reverse()) {
|
|
2545
|
-
const relativePath =
|
|
2546
|
-
const newPath =
|
|
2877
|
+
const relativePath = path4.posix.relative(srcPrefixed, entry.path);
|
|
2878
|
+
const newPath = path4.posix.join(destPrefixed, relativePath);
|
|
2547
2879
|
this.#stmt(
|
|
2548
2880
|
`INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
2549
2881
|
VALUES (?, ?, ?, ?, ?, ?)`
|
|
@@ -2596,7 +2928,7 @@ var SqliteFs = class {
|
|
|
2596
2928
|
});
|
|
2597
2929
|
}
|
|
2598
2930
|
resolvePath(base, relativePath) {
|
|
2599
|
-
return
|
|
2931
|
+
return path4.posix.resolve(base, relativePath);
|
|
2600
2932
|
}
|
|
2601
2933
|
getAllPaths() {
|
|
2602
2934
|
const rows = this.#stmt(
|
|
@@ -2708,7 +3040,7 @@ var SqliteFs = class {
|
|
|
2708
3040
|
|
|
2709
3041
|
// packages/text2sql/src/lib/fs/mssql/mssql-fs.ts
|
|
2710
3042
|
import { createRequire } from "node:module";
|
|
2711
|
-
import * as
|
|
3043
|
+
import * as path5 from "node:path";
|
|
2712
3044
|
|
|
2713
3045
|
// packages/text2sql/src/lib/fs/mssql/ddl.mssql-fs.ts
|
|
2714
3046
|
function mssqlFsDDL(schema) {
|
|
@@ -2834,7 +3166,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
2834
3166
|
const segments = p.split("/").filter(Boolean);
|
|
2835
3167
|
let currentPath = "/";
|
|
2836
3168
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
2837
|
-
currentPath =
|
|
3169
|
+
currentPath = path5.posix.join(currentPath, segments[i]);
|
|
2838
3170
|
const exists = await this.#rawQuery(
|
|
2839
3171
|
`SELECT CASE WHEN EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = @p0) THEN 1 ELSE 0 END as [exists]`,
|
|
2840
3172
|
[currentPath]
|
|
@@ -2886,7 +3218,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
2886
3218
|
}
|
|
2887
3219
|
}
|
|
2888
3220
|
#normalizeRoot(root) {
|
|
2889
|
-
return
|
|
3221
|
+
return path5.posix.resolve("/", root.trim());
|
|
2890
3222
|
}
|
|
2891
3223
|
#prefixPath(p) {
|
|
2892
3224
|
if (!this.#root) {
|
|
@@ -2895,7 +3227,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
2895
3227
|
if (p === "/") {
|
|
2896
3228
|
return this.#root;
|
|
2897
3229
|
}
|
|
2898
|
-
return
|
|
3230
|
+
return path5.posix.join(this.#root, p);
|
|
2899
3231
|
}
|
|
2900
3232
|
#unprefixPath(p) {
|
|
2901
3233
|
if (!this.#root) {
|
|
@@ -2910,10 +3242,10 @@ var MssqlFs = class _MssqlFs {
|
|
|
2910
3242
|
return p;
|
|
2911
3243
|
}
|
|
2912
3244
|
#normalizePath(p) {
|
|
2913
|
-
return
|
|
3245
|
+
return path5.posix.resolve("/", p);
|
|
2914
3246
|
}
|
|
2915
3247
|
#dirname(p) {
|
|
2916
|
-
const dir =
|
|
3248
|
+
const dir = path5.posix.dirname(p);
|
|
2917
3249
|
return dir === "" ? "/" : dir;
|
|
2918
3250
|
}
|
|
2919
3251
|
async #ensureParentExists(filePath, transaction) {
|
|
@@ -3002,7 +3334,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3002
3334
|
}
|
|
3003
3335
|
seen.add(p);
|
|
3004
3336
|
const target = this.#normalizePath(
|
|
3005
|
-
|
|
3337
|
+
path5.posix.resolve(this.#dirname(p), entry.symlinkTarget)
|
|
3006
3338
|
);
|
|
3007
3339
|
return this.#resolveSymlink(target, seen);
|
|
3008
3340
|
}
|
|
@@ -3185,11 +3517,11 @@ var MssqlFs = class _MssqlFs {
|
|
|
3185
3517
|
await this.#useTransaction(async (transaction) => {
|
|
3186
3518
|
if (options?.recursive) {
|
|
3187
3519
|
const rootPath = this.#root || "/";
|
|
3188
|
-
const relativePath =
|
|
3520
|
+
const relativePath = path5.posix.relative(rootPath, prefixed);
|
|
3189
3521
|
const segments = relativePath.split("/").filter(Boolean);
|
|
3190
3522
|
let currentPath = rootPath;
|
|
3191
3523
|
for (const segment of segments) {
|
|
3192
|
-
currentPath =
|
|
3524
|
+
currentPath = path5.posix.join(currentPath, segment);
|
|
3193
3525
|
const checkReq = transaction.request();
|
|
3194
3526
|
checkReq.input("p0", currentPath);
|
|
3195
3527
|
const result = await checkReq.query(
|
|
@@ -3253,7 +3585,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3253
3585
|
AND path NOT LIKE @p0 + '%/%'`,
|
|
3254
3586
|
[prefix, resolved]
|
|
3255
3587
|
);
|
|
3256
|
-
return rows.map((row) =>
|
|
3588
|
+
return rows.map((row) => path5.posix.basename(row.path));
|
|
3257
3589
|
}
|
|
3258
3590
|
async readdirWithFileTypes(dirPath) {
|
|
3259
3591
|
const normalized = this.#normalizePath(dirPath);
|
|
@@ -3279,7 +3611,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3279
3611
|
[prefix, resolved]
|
|
3280
3612
|
);
|
|
3281
3613
|
return rows.map((row) => ({
|
|
3282
|
-
name:
|
|
3614
|
+
name: path5.posix.basename(row.path),
|
|
3283
3615
|
isFile: row.type === "file",
|
|
3284
3616
|
isDirectory: row.type === "directory",
|
|
3285
3617
|
isSymbolicLink: row.type === "symlink"
|
|
@@ -3348,8 +3680,8 @@ var MssqlFs = class _MssqlFs {
|
|
|
3348
3680
|
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = @p0 OR path LIKE @p0 + '/%'`
|
|
3349
3681
|
);
|
|
3350
3682
|
for (const entry of allEntriesResult.recordset) {
|
|
3351
|
-
const relativePath =
|
|
3352
|
-
const newPath =
|
|
3683
|
+
const relativePath = path5.posix.relative(srcPrefixed, entry.path);
|
|
3684
|
+
const newPath = path5.posix.join(destPrefixed, relativePath);
|
|
3353
3685
|
const insertReq = transaction.request();
|
|
3354
3686
|
insertReq.input("p0", newPath);
|
|
3355
3687
|
insertReq.input("p1", entry.type);
|
|
@@ -3458,8 +3790,8 @@ var MssqlFs = class _MssqlFs {
|
|
|
3458
3790
|
`DELETE FROM ${this.#t("fs_entries")} WHERE path = @dp0 OR path LIKE @dp0 + '/%'`
|
|
3459
3791
|
);
|
|
3460
3792
|
for (const entry of [...allEntriesResult.recordset].reverse()) {
|
|
3461
|
-
const relativePath =
|
|
3462
|
-
const newPath =
|
|
3793
|
+
const relativePath = path5.posix.relative(srcPrefixed, entry.path);
|
|
3794
|
+
const newPath = path5.posix.join(destPrefixed, relativePath);
|
|
3463
3795
|
const insertReq = transaction.request();
|
|
3464
3796
|
insertReq.input("p0", newPath);
|
|
3465
3797
|
insertReq.input("p1", entry.type);
|
|
@@ -3552,7 +3884,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3552
3884
|
});
|
|
3553
3885
|
}
|
|
3554
3886
|
resolvePath(base, relativePath) {
|
|
3555
|
-
return
|
|
3887
|
+
return path5.posix.resolve(base, relativePath);
|
|
3556
3888
|
}
|
|
3557
3889
|
getAllPaths() {
|
|
3558
3890
|
throw new Error(
|
|
@@ -3693,7 +4025,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3693
4025
|
|
|
3694
4026
|
// packages/text2sql/src/lib/fs/postgres/postgres-fs.ts
|
|
3695
4027
|
import { createRequire as createRequire2 } from "node:module";
|
|
3696
|
-
import * as
|
|
4028
|
+
import * as path6 from "node:path";
|
|
3697
4029
|
|
|
3698
4030
|
// packages/text2sql/src/lib/fs/postgres/ddl.postgres-fs.ts
|
|
3699
4031
|
function postgresFsDDL(schema) {
|
|
@@ -3798,7 +4130,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
3798
4130
|
const segments = p.split("/").filter(Boolean);
|
|
3799
4131
|
let currentPath = "/";
|
|
3800
4132
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
3801
|
-
currentPath =
|
|
4133
|
+
currentPath = path6.posix.join(currentPath, segments[i]);
|
|
3802
4134
|
const exists = await this.#rawQuery(
|
|
3803
4135
|
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
|
|
3804
4136
|
[currentPath]
|
|
@@ -3843,7 +4175,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
3843
4175
|
}
|
|
3844
4176
|
}
|
|
3845
4177
|
#normalizeRoot(root) {
|
|
3846
|
-
return
|
|
4178
|
+
return path6.posix.resolve("/", root.trim());
|
|
3847
4179
|
}
|
|
3848
4180
|
#prefixPath(p) {
|
|
3849
4181
|
if (!this.#root) {
|
|
@@ -3852,7 +4184,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
3852
4184
|
if (p === "/") {
|
|
3853
4185
|
return this.#root;
|
|
3854
4186
|
}
|
|
3855
|
-
return
|
|
4187
|
+
return path6.posix.join(this.#root, p);
|
|
3856
4188
|
}
|
|
3857
4189
|
#unprefixPath(p) {
|
|
3858
4190
|
if (!this.#root) {
|
|
@@ -3867,10 +4199,10 @@ var PostgresFs = class _PostgresFs {
|
|
|
3867
4199
|
return p;
|
|
3868
4200
|
}
|
|
3869
4201
|
#normalizePath(p) {
|
|
3870
|
-
return
|
|
4202
|
+
return path6.posix.resolve("/", p);
|
|
3871
4203
|
}
|
|
3872
4204
|
#dirname(p) {
|
|
3873
|
-
const dir =
|
|
4205
|
+
const dir = path6.posix.dirname(p);
|
|
3874
4206
|
return dir === "" ? "/" : dir;
|
|
3875
4207
|
}
|
|
3876
4208
|
async #ensureParentExists(filePath, client) {
|
|
@@ -3950,7 +4282,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
3950
4282
|
}
|
|
3951
4283
|
seen.add(p);
|
|
3952
4284
|
const target = this.#normalizePath(
|
|
3953
|
-
|
|
4285
|
+
path6.posix.resolve(this.#dirname(p), entry.symlink_target)
|
|
3954
4286
|
);
|
|
3955
4287
|
return this.#resolveSymlink(target, seen);
|
|
3956
4288
|
}
|
|
@@ -4116,11 +4448,11 @@ var PostgresFs = class _PostgresFs {
|
|
|
4116
4448
|
await this.#useTransaction(async (client) => {
|
|
4117
4449
|
if (options?.recursive) {
|
|
4118
4450
|
const rootPath = this.#root || "/";
|
|
4119
|
-
const relativePath =
|
|
4451
|
+
const relativePath = path6.posix.relative(rootPath, prefixed);
|
|
4120
4452
|
const segments = relativePath.split("/").filter(Boolean);
|
|
4121
4453
|
let currentPath = rootPath;
|
|
4122
4454
|
for (const segment of segments) {
|
|
4123
|
-
currentPath =
|
|
4455
|
+
currentPath = path6.posix.join(currentPath, segment);
|
|
4124
4456
|
const result = await client.query(
|
|
4125
4457
|
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
4126
4458
|
[currentPath]
|
|
@@ -4178,7 +4510,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
4178
4510
|
AND path NOT LIKE $1 || '%/%'`,
|
|
4179
4511
|
[prefix, resolved]
|
|
4180
4512
|
);
|
|
4181
|
-
return rows.map((row) =>
|
|
4513
|
+
return rows.map((row) => path6.posix.basename(row.path));
|
|
4182
4514
|
}
|
|
4183
4515
|
async readdirWithFileTypes(dirPath) {
|
|
4184
4516
|
const normalized = this.#normalizePath(dirPath);
|
|
@@ -4204,7 +4536,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
4204
4536
|
[prefix, resolved]
|
|
4205
4537
|
);
|
|
4206
4538
|
return rows.map((row) => ({
|
|
4207
|
-
name:
|
|
4539
|
+
name: path6.posix.basename(row.path),
|
|
4208
4540
|
isFile: row.type === "file",
|
|
4209
4541
|
isDirectory: row.type === "directory",
|
|
4210
4542
|
isSymbolicLink: row.type === "symlink"
|
|
@@ -4269,8 +4601,8 @@ var PostgresFs = class _PostgresFs {
|
|
|
4269
4601
|
[srcPrefixed]
|
|
4270
4602
|
);
|
|
4271
4603
|
for (const entry of allEntriesResult.rows) {
|
|
4272
|
-
const relativePath =
|
|
4273
|
-
const newPath =
|
|
4604
|
+
const relativePath = path6.posix.relative(srcPrefixed, entry.path);
|
|
4605
|
+
const newPath = path6.posix.join(destPrefixed, relativePath);
|
|
4274
4606
|
await client.query(
|
|
4275
4607
|
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
4276
4608
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
@@ -4359,8 +4691,8 @@ var PostgresFs = class _PostgresFs {
|
|
|
4359
4691
|
[destPrefixed]
|
|
4360
4692
|
);
|
|
4361
4693
|
for (const entry of [...allEntriesResult.rows].reverse()) {
|
|
4362
|
-
const relativePath =
|
|
4363
|
-
const newPath =
|
|
4694
|
+
const relativePath = path6.posix.relative(srcPrefixed, entry.path);
|
|
4695
|
+
const newPath = path6.posix.join(destPrefixed, relativePath);
|
|
4364
4696
|
await client.query(
|
|
4365
4697
|
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
4366
4698
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
@@ -4433,7 +4765,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
4433
4765
|
});
|
|
4434
4766
|
}
|
|
4435
4767
|
resolvePath(base, relativePath) {
|
|
4436
|
-
return
|
|
4768
|
+
return path6.posix.resolve(base, relativePath);
|
|
4437
4769
|
}
|
|
4438
4770
|
getAllPaths() {
|
|
4439
4771
|
throw new Error(
|
|
@@ -4569,29 +4901,29 @@ var ScopedFs = class {
|
|
|
4569
4901
|
this.#base = options.base;
|
|
4570
4902
|
this.#prefix = options.prefix.replace(/\/$/, "");
|
|
4571
4903
|
}
|
|
4572
|
-
#scope(
|
|
4573
|
-
return `${this.#prefix}${
|
|
4904
|
+
#scope(path7) {
|
|
4905
|
+
return `${this.#prefix}${path7}`;
|
|
4574
4906
|
}
|
|
4575
|
-
#unscope(
|
|
4576
|
-
if (
|
|
4907
|
+
#unscope(path7) {
|
|
4908
|
+
if (path7 === this.#prefix) {
|
|
4577
4909
|
return "/";
|
|
4578
4910
|
}
|
|
4579
|
-
if (
|
|
4580
|
-
return
|
|
4911
|
+
if (path7.startsWith(this.#prefix + "/")) {
|
|
4912
|
+
return path7.slice(this.#prefix.length) || "/";
|
|
4581
4913
|
}
|
|
4582
|
-
return
|
|
4914
|
+
return path7;
|
|
4583
4915
|
}
|
|
4584
|
-
async writeFile(
|
|
4585
|
-
await this.#base.writeFile(this.#scope(
|
|
4916
|
+
async writeFile(path7, content, options) {
|
|
4917
|
+
await this.#base.writeFile(this.#scope(path7), content, options);
|
|
4586
4918
|
}
|
|
4587
|
-
async appendFile(
|
|
4588
|
-
await this.#base.appendFile(this.#scope(
|
|
4919
|
+
async appendFile(path7, content, options) {
|
|
4920
|
+
await this.#base.appendFile(this.#scope(path7), content, options);
|
|
4589
4921
|
}
|
|
4590
|
-
async mkdir(
|
|
4591
|
-
return this.#base.mkdir(this.#scope(
|
|
4922
|
+
async mkdir(path7, options) {
|
|
4923
|
+
return this.#base.mkdir(this.#scope(path7), options);
|
|
4592
4924
|
}
|
|
4593
|
-
async rm(
|
|
4594
|
-
await this.#base.rm(this.#scope(
|
|
4925
|
+
async rm(path7, options) {
|
|
4926
|
+
await this.#base.rm(this.#scope(path7), options);
|
|
4595
4927
|
}
|
|
4596
4928
|
async cp(src, dest, options) {
|
|
4597
4929
|
await this.#base.cp(this.#scope(src), this.#scope(dest), options);
|
|
@@ -4599,8 +4931,8 @@ var ScopedFs = class {
|
|
|
4599
4931
|
async mv(src, dest) {
|
|
4600
4932
|
await this.#base.mv(this.#scope(src), this.#scope(dest));
|
|
4601
4933
|
}
|
|
4602
|
-
async chmod(
|
|
4603
|
-
return this.#base.chmod(this.#scope(
|
|
4934
|
+
async chmod(path7, mode) {
|
|
4935
|
+
return this.#base.chmod(this.#scope(path7), mode);
|
|
4604
4936
|
}
|
|
4605
4937
|
async symlink(target, linkPath) {
|
|
4606
4938
|
await this.#base.symlink(target, this.#scope(linkPath));
|
|
@@ -4608,35 +4940,35 @@ var ScopedFs = class {
|
|
|
4608
4940
|
async link(existingPath, newPath) {
|
|
4609
4941
|
await this.#base.link(this.#scope(existingPath), this.#scope(newPath));
|
|
4610
4942
|
}
|
|
4611
|
-
readFile(
|
|
4612
|
-
return this.#base.readFile(this.#scope(
|
|
4943
|
+
readFile(path7, options) {
|
|
4944
|
+
return this.#base.readFile(this.#scope(path7), options);
|
|
4613
4945
|
}
|
|
4614
|
-
readFileBuffer(
|
|
4615
|
-
return this.#base.readFileBuffer(this.#scope(
|
|
4946
|
+
readFileBuffer(path7) {
|
|
4947
|
+
return this.#base.readFileBuffer(this.#scope(path7));
|
|
4616
4948
|
}
|
|
4617
|
-
stat(
|
|
4618
|
-
return this.#base.stat(this.#scope(
|
|
4949
|
+
stat(path7) {
|
|
4950
|
+
return this.#base.stat(this.#scope(path7));
|
|
4619
4951
|
}
|
|
4620
|
-
lstat(
|
|
4621
|
-
return this.#base.lstat(this.#scope(
|
|
4952
|
+
lstat(path7) {
|
|
4953
|
+
return this.#base.lstat(this.#scope(path7));
|
|
4622
4954
|
}
|
|
4623
|
-
readdir(
|
|
4624
|
-
return this.#base.readdir(this.#scope(
|
|
4955
|
+
readdir(path7) {
|
|
4956
|
+
return this.#base.readdir(this.#scope(path7));
|
|
4625
4957
|
}
|
|
4626
|
-
readdirWithFileTypes(
|
|
4627
|
-
return this.#base.readdirWithFileTypes(this.#scope(
|
|
4958
|
+
readdirWithFileTypes(path7) {
|
|
4959
|
+
return this.#base.readdirWithFileTypes(this.#scope(path7));
|
|
4628
4960
|
}
|
|
4629
|
-
exists(
|
|
4630
|
-
return this.#base.exists(this.#scope(
|
|
4961
|
+
exists(path7) {
|
|
4962
|
+
return this.#base.exists(this.#scope(path7));
|
|
4631
4963
|
}
|
|
4632
|
-
readlink(
|
|
4633
|
-
return this.#base.readlink(this.#scope(
|
|
4964
|
+
readlink(path7) {
|
|
4965
|
+
return this.#base.readlink(this.#scope(path7));
|
|
4634
4966
|
}
|
|
4635
|
-
realpath(
|
|
4636
|
-
return this.#base.realpath(this.#scope(
|
|
4967
|
+
realpath(path7) {
|
|
4968
|
+
return this.#base.realpath(this.#scope(path7)).then((p) => this.#unscope(p));
|
|
4637
4969
|
}
|
|
4638
|
-
utimes(
|
|
4639
|
-
return this.#base.utimes(this.#scope(
|
|
4970
|
+
utimes(path7, atime, mtime) {
|
|
4971
|
+
return this.#base.utimes(this.#scope(path7), atime, mtime);
|
|
4640
4972
|
}
|
|
4641
4973
|
resolvePath(base, relativePath) {
|
|
4642
4974
|
return this.#base.resolvePath(base, relativePath);
|
|
@@ -4657,22 +4989,22 @@ var TrackedFs = class {
|
|
|
4657
4989
|
getCreatedFiles() {
|
|
4658
4990
|
return [...this.#createdFiles];
|
|
4659
4991
|
}
|
|
4660
|
-
async writeFile(
|
|
4661
|
-
await this.#base.writeFile(
|
|
4662
|
-
this.#createdFiles.add(
|
|
4992
|
+
async writeFile(path7, content, options) {
|
|
4993
|
+
await this.#base.writeFile(path7, content, options);
|
|
4994
|
+
this.#createdFiles.add(path7);
|
|
4663
4995
|
}
|
|
4664
|
-
async appendFile(
|
|
4665
|
-
await this.#base.appendFile(
|
|
4666
|
-
this.#createdFiles.add(
|
|
4996
|
+
async appendFile(path7, content, options) {
|
|
4997
|
+
await this.#base.appendFile(path7, content, options);
|
|
4998
|
+
this.#createdFiles.add(path7);
|
|
4667
4999
|
}
|
|
4668
|
-
async mkdir(
|
|
4669
|
-
return this.#base.mkdir(
|
|
5000
|
+
async mkdir(path7, options) {
|
|
5001
|
+
return this.#base.mkdir(path7, options);
|
|
4670
5002
|
}
|
|
4671
|
-
async rm(
|
|
4672
|
-
await this.#base.rm(
|
|
4673
|
-
this.#createdFiles.delete(
|
|
5003
|
+
async rm(path7, options) {
|
|
5004
|
+
await this.#base.rm(path7, options);
|
|
5005
|
+
this.#createdFiles.delete(path7);
|
|
4674
5006
|
if (options?.recursive) {
|
|
4675
|
-
const prefix =
|
|
5007
|
+
const prefix = path7.endsWith("/") ? path7 : path7 + "/";
|
|
4676
5008
|
for (const file of this.#createdFiles) {
|
|
4677
5009
|
if (file.startsWith(prefix)) {
|
|
4678
5010
|
this.#createdFiles.delete(file);
|
|
@@ -4689,8 +5021,8 @@ var TrackedFs = class {
|
|
|
4689
5021
|
this.#createdFiles.delete(src);
|
|
4690
5022
|
this.#createdFiles.add(dest);
|
|
4691
5023
|
}
|
|
4692
|
-
async chmod(
|
|
4693
|
-
return this.#base.chmod(
|
|
5024
|
+
async chmod(path7, mode) {
|
|
5025
|
+
return this.#base.chmod(path7, mode);
|
|
4694
5026
|
}
|
|
4695
5027
|
async symlink(target, linkPath) {
|
|
4696
5028
|
await this.#base.symlink(target, linkPath);
|
|
@@ -4700,29 +5032,29 @@ var TrackedFs = class {
|
|
|
4700
5032
|
await this.#base.link(existingPath, newPath);
|
|
4701
5033
|
this.#createdFiles.add(newPath);
|
|
4702
5034
|
}
|
|
4703
|
-
readFile(
|
|
4704
|
-
return this.#base.readFile(
|
|
5035
|
+
readFile(path7, options) {
|
|
5036
|
+
return this.#base.readFile(path7, options);
|
|
4705
5037
|
}
|
|
4706
|
-
readFileBuffer(
|
|
4707
|
-
return this.#base.readFileBuffer(
|
|
5038
|
+
readFileBuffer(path7) {
|
|
5039
|
+
return this.#base.readFileBuffer(path7);
|
|
4708
5040
|
}
|
|
4709
|
-
stat(
|
|
4710
|
-
return this.#base.stat(
|
|
5041
|
+
stat(path7) {
|
|
5042
|
+
return this.#base.stat(path7);
|
|
4711
5043
|
}
|
|
4712
|
-
lstat(
|
|
4713
|
-
return this.#base.lstat(
|
|
5044
|
+
lstat(path7) {
|
|
5045
|
+
return this.#base.lstat(path7);
|
|
4714
5046
|
}
|
|
4715
|
-
readdir(
|
|
4716
|
-
return this.#base.readdir(
|
|
5047
|
+
readdir(path7) {
|
|
5048
|
+
return this.#base.readdir(path7);
|
|
4717
5049
|
}
|
|
4718
|
-
readdirWithFileTypes(
|
|
4719
|
-
return this.#base.readdirWithFileTypes(
|
|
5050
|
+
readdirWithFileTypes(path7) {
|
|
5051
|
+
return this.#base.readdirWithFileTypes(path7);
|
|
4720
5052
|
}
|
|
4721
|
-
exists(
|
|
4722
|
-
return this.#base.exists(
|
|
5053
|
+
exists(path7) {
|
|
5054
|
+
return this.#base.exists(path7);
|
|
4723
5055
|
}
|
|
4724
|
-
readlink(
|
|
4725
|
-
return this.#base.readlink(
|
|
5056
|
+
readlink(path7) {
|
|
5057
|
+
return this.#base.readlink(path7);
|
|
4726
5058
|
}
|
|
4727
5059
|
resolvePath(base, relativePath) {
|
|
4728
5060
|
return this.#base.resolvePath(base, relativePath);
|
|
@@ -4730,11 +5062,11 @@ var TrackedFs = class {
|
|
|
4730
5062
|
getAllPaths() {
|
|
4731
5063
|
return this.#base.getAllPaths?.() ?? [];
|
|
4732
5064
|
}
|
|
4733
|
-
realpath(
|
|
4734
|
-
return this.#base.realpath(
|
|
5065
|
+
realpath(path7) {
|
|
5066
|
+
return this.#base.realpath(path7);
|
|
4735
5067
|
}
|
|
4736
|
-
utimes(
|
|
4737
|
-
return this.#base.utimes(
|
|
5068
|
+
utimes(path7, atime, mtime) {
|
|
5069
|
+
return this.#base.utimes(path7, atime, mtime);
|
|
4738
5070
|
}
|
|
4739
5071
|
};
|
|
4740
5072
|
|