@deepagents/text2sql 0.29.1 → 0.31.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 +727 -379
- 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 +310 -13
- package/dist/lib/adapters/bigquery/index.js.map +4 -4
- package/dist/lib/adapters/groundings/abstract.grounding.d.ts +23 -0
- package/dist/lib/adapters/groundings/abstract.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/index.js +48 -5
- package/dist/lib/adapters/groundings/index.js.map +2 -2
- package/dist/lib/adapters/groundings/table.grounding.d.ts +9 -1
- package/dist/lib/adapters/groundings/table.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/view.grounding.d.ts +7 -1
- package/dist/lib/adapters/groundings/view.grounding.d.ts.map +1 -1
- package/dist/lib/adapters/mysql/index.js +309 -12
- 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 +309 -12
- 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 +304 -11
- package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
- package/dist/lib/adapters/sqlite/index.js +309 -12
- 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 +309 -12
- 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
|
}
|
|
@@ -160,7 +328,18 @@ var Adapter = class {
|
|
|
160
328
|
fragments.push(this.#viewToFragment(v));
|
|
161
329
|
}
|
|
162
330
|
const tableMap = new Map(ctx.tables.map((t) => [t.name, t]));
|
|
331
|
+
const tableColumnSets = new Map(
|
|
332
|
+
ctx.tables.map((t) => [t.name, new Set(t.columns.map((c) => c.name))])
|
|
333
|
+
);
|
|
163
334
|
for (const rel of ctx.relationships) {
|
|
335
|
+
const sourceColumns = tableColumnSets.get(rel.table);
|
|
336
|
+
const targetColumns = tableColumnSets.get(rel.referenced_table);
|
|
337
|
+
if (sourceColumns && rel.from.some((column2) => !sourceColumns.has(column2))) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (targetColumns && rel.to.some((column2) => !targetColumns.has(column2))) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
164
343
|
const sourceTable = tableMap.get(rel.table);
|
|
165
344
|
const targetTable = tableMap.get(rel.referenced_table);
|
|
166
345
|
fragments.push(
|
|
@@ -215,7 +394,10 @@ var Adapter = class {
|
|
|
215
394
|
stats: col.stats
|
|
216
395
|
})
|
|
217
396
|
);
|
|
218
|
-
const
|
|
397
|
+
const presentColumns = new Set(t.columns.map((c) => c.name));
|
|
398
|
+
const indexFragments = (t.indexes ?? []).filter(
|
|
399
|
+
(idx) => idx.columns.every((column2) => presentColumns.has(column2))
|
|
400
|
+
).map(
|
|
219
401
|
(idx) => index({
|
|
220
402
|
name: idx.name,
|
|
221
403
|
columns: idx.columns,
|
|
@@ -225,6 +407,8 @@ var Adapter = class {
|
|
|
225
407
|
);
|
|
226
408
|
const constraintFragments = (t.constraints ?? []).filter(
|
|
227
409
|
(c) => c.type === "CHECK" || c.type === "UNIQUE" && (c.columns?.length ?? 0) > 1
|
|
410
|
+
).filter(
|
|
411
|
+
(c) => !c.columns?.length || c.columns.every((column2) => presentColumns.has(column2))
|
|
228
412
|
).map(
|
|
229
413
|
(c) => constraint({
|
|
230
414
|
name: c.name,
|
|
@@ -293,6 +477,78 @@ var Adapter = class {
|
|
|
293
477
|
return sql;
|
|
294
478
|
}
|
|
295
479
|
}
|
|
480
|
+
#cachedAllowedEntities = null;
|
|
481
|
+
async #resolveScope() {
|
|
482
|
+
if (this.#cachedAllowedEntities) return this.#cachedAllowedEntities;
|
|
483
|
+
this.#cachedAllowedEntities = await this.resolveAllowedEntities();
|
|
484
|
+
return this.#cachedAllowedEntities;
|
|
485
|
+
}
|
|
486
|
+
async #checkScope(sql, allowedEntities) {
|
|
487
|
+
const dialect = this.formatterLanguage;
|
|
488
|
+
const scopeDialects = {
|
|
489
|
+
sqlite: "mysql",
|
|
490
|
+
postgresql: "postgresql",
|
|
491
|
+
bigquery: "bigquery",
|
|
492
|
+
transactsql: "transactsql",
|
|
493
|
+
mysql: "mysql"
|
|
494
|
+
};
|
|
495
|
+
const scopeDialect = scopeDialects[dialect];
|
|
496
|
+
if (!scopeDialect) {
|
|
497
|
+
throw new TypeError(
|
|
498
|
+
`No scope dialect mapping for formatter language "${dialect}". Add it to the scopeDialects map in Adapter.#checkScope.`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
let references;
|
|
502
|
+
try {
|
|
503
|
+
references = extractBaseEntityReferences(sql, scopeDialect);
|
|
504
|
+
} catch (error) {
|
|
505
|
+
return buildScopeParseErrorPayload(
|
|
506
|
+
sql,
|
|
507
|
+
dialect,
|
|
508
|
+
error
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
if (references.length === 0) return null;
|
|
512
|
+
const allowedQualified = new Set(
|
|
513
|
+
allowedEntities.map((e) => e.toLowerCase())
|
|
514
|
+
);
|
|
515
|
+
const allowedUnqualified = /* @__PURE__ */ new Set();
|
|
516
|
+
for (const entity of allowedEntities) {
|
|
517
|
+
const dot = entity.lastIndexOf(".");
|
|
518
|
+
if (dot !== -1) {
|
|
519
|
+
allowedUnqualified.add(entity.slice(dot + 1).toLowerCase());
|
|
520
|
+
} else {
|
|
521
|
+
allowedUnqualified.add(entity.toLowerCase());
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const outOfScope = references.map((ref) => ref.db ? `${ref.db}.${ref.table}` : ref.table).filter((name) => {
|
|
525
|
+
const lower = name.toLowerCase();
|
|
526
|
+
if (name.includes(".")) {
|
|
527
|
+
if (allowedQualified.has(lower)) return false;
|
|
528
|
+
const parts = lower.split(".");
|
|
529
|
+
if (parts.length >= 3) {
|
|
530
|
+
const datasetTable = parts.slice(-2).join(".");
|
|
531
|
+
if (allowedQualified.has(datasetTable)) return false;
|
|
532
|
+
}
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
return !allowedQualified.has(lower) && !allowedUnqualified.has(lower);
|
|
536
|
+
});
|
|
537
|
+
if (outOfScope.length === 0) return null;
|
|
538
|
+
return buildOutOfScopePayload(sql, outOfScope, allowedEntities);
|
|
539
|
+
}
|
|
540
|
+
async validate(sql) {
|
|
541
|
+
const allowed = await this.#resolveScope();
|
|
542
|
+
const scopeError = await this.#checkScope(sql, allowed);
|
|
543
|
+
if (scopeError) return JSON.stringify(scopeError);
|
|
544
|
+
return this.validateImpl(sql);
|
|
545
|
+
}
|
|
546
|
+
async execute(sql) {
|
|
547
|
+
const allowed = await this.#resolveScope();
|
|
548
|
+
const scopeError = await this.#checkScope(sql, allowed);
|
|
549
|
+
if (scopeError) throw new SQLScopeError(scopeError);
|
|
550
|
+
return this.executeImpl(sql);
|
|
551
|
+
}
|
|
296
552
|
/**
|
|
297
553
|
* Convert unknown database value to number.
|
|
298
554
|
* Handles number, bigint, and string types.
|
|
@@ -413,48 +669,6 @@ function getTablesWithRelated(allTables, relationships, filter) {
|
|
|
413
669
|
return Array.from(result);
|
|
414
670
|
}
|
|
415
671
|
|
|
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
672
|
// packages/text2sql/src/lib/agents/result-tools.ts
|
|
459
673
|
import { tool } from "ai";
|
|
460
674
|
import { createBashTool } from "bash-tool";
|
|
@@ -463,172 +677,17 @@ import {
|
|
|
463
677
|
Bash,
|
|
464
678
|
MountableFs,
|
|
465
679
|
OverlayFs,
|
|
466
|
-
defineCommand
|
|
467
|
-
parse
|
|
680
|
+
defineCommand
|
|
468
681
|
} from "just-bash";
|
|
469
682
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
470
|
-
import * as
|
|
683
|
+
import * as path2 from "node:path";
|
|
471
684
|
import { v7 } from "uuid";
|
|
472
685
|
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
686
|
|
|
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
|
-
}
|
|
687
|
+
// packages/text2sql/src/lib/agents/sql-transform-plugins.ts
|
|
688
|
+
import { parse, serialize } from "just-bash";
|
|
689
|
+
import { createHash } from "node:crypto";
|
|
690
|
+
import * as path from "node:path";
|
|
632
691
|
function asStaticWordText(word) {
|
|
633
692
|
if (!word) {
|
|
634
693
|
return null;
|
|
@@ -672,6 +731,40 @@ function isScriptNode(value) {
|
|
|
672
731
|
const node = value;
|
|
673
732
|
return node.type === "Script" && Array.isArray(node.statements);
|
|
674
733
|
}
|
|
734
|
+
var BLOCKED_DB_CLIENT_COMMANDS = /* @__PURE__ */ new Set([
|
|
735
|
+
"psql",
|
|
736
|
+
"sqlite3",
|
|
737
|
+
"mysql",
|
|
738
|
+
"duckdb"
|
|
739
|
+
]);
|
|
740
|
+
var BLOCKED_RAW_SQL_COMMANDS = /* @__PURE__ */ new Set(["select", "with"]);
|
|
741
|
+
var ALLOWED_SQL_PROXY_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "validate"]);
|
|
742
|
+
var SHELL_INTERPRETER_COMMANDS = /* @__PURE__ */ new Set([
|
|
743
|
+
"bash",
|
|
744
|
+
"sh",
|
|
745
|
+
"zsh",
|
|
746
|
+
"dash",
|
|
747
|
+
"ksh"
|
|
748
|
+
]);
|
|
749
|
+
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["env", "command", "eval"]);
|
|
750
|
+
var SQL_PROXY_ENFORCEMENT_MESSAGE = [
|
|
751
|
+
"Direct database querying through bash is blocked.",
|
|
752
|
+
"Use SQL proxy commands in this order:",
|
|
753
|
+
'1) sql validate "SELECT ..."',
|
|
754
|
+
'2) sql run "SELECT ..."'
|
|
755
|
+
].join("\n");
|
|
756
|
+
var SqlProxyViolationError = class extends Error {
|
|
757
|
+
constructor() {
|
|
758
|
+
super(SQL_PROXY_ENFORCEMENT_MESSAGE);
|
|
759
|
+
this.name = "SqlProxyViolationError";
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
function cloneInspectionContext(context) {
|
|
763
|
+
return {
|
|
764
|
+
functionDefinitions: new Map(context.functionDefinitions),
|
|
765
|
+
callStack: new Set(context.callStack)
|
|
766
|
+
};
|
|
767
|
+
}
|
|
675
768
|
function scriptContainsBlockedCommand(script, context, mode = "blocked-only") {
|
|
676
769
|
return statementsContainBlockedCommand(script.statements, context, mode);
|
|
677
770
|
}
|
|
@@ -1236,39 +1329,288 @@ function isBlockedSimpleCommand(command, context, mode, options) {
|
|
|
1236
1329
|
}
|
|
1237
1330
|
return false;
|
|
1238
1331
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1332
|
+
var SqlProxyEnforcementPlugin = class {
|
|
1333
|
+
name = "sql-proxy-enforcement";
|
|
1334
|
+
transform(context) {
|
|
1335
|
+
const blocked = scriptContainsBlockedCommand(context.ast, {
|
|
1336
|
+
functionDefinitions: /* @__PURE__ */ new Map(),
|
|
1337
|
+
callStack: /* @__PURE__ */ new Set()
|
|
1338
|
+
});
|
|
1339
|
+
if (blocked) {
|
|
1340
|
+
throw new SqlProxyViolationError();
|
|
1341
|
+
}
|
|
1342
|
+
return {
|
|
1343
|
+
ast: context.ast,
|
|
1344
|
+
metadata: { inspected: true }
|
|
1345
|
+
};
|
|
1243
1346
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1347
|
+
};
|
|
1348
|
+
function wordPartsContainBacktickSubstitution(parts) {
|
|
1349
|
+
for (const part of parts) {
|
|
1350
|
+
if (part.type === "CommandSubstitution" && part.legacy === true) {
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1353
|
+
if (part.type === "DoubleQuoted" && Array.isArray(part.parts)) {
|
|
1354
|
+
if (wordPartsContainBacktickSubstitution(
|
|
1355
|
+
part.parts
|
|
1356
|
+
)) {
|
|
1357
|
+
return true;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
function isSqlBacktickCommand(cmd) {
|
|
1364
|
+
if (cmd.type !== "SimpleCommand") return false;
|
|
1365
|
+
if (cmd.assignments.length > 0 || cmd.redirections.length > 0) return false;
|
|
1366
|
+
if (asStaticWordText(cmd.name) !== "sql") return false;
|
|
1367
|
+
if (cmd.args.length < 2) return false;
|
|
1368
|
+
const subcommand = asStaticWordText(cmd.args[0]);
|
|
1369
|
+
if (subcommand !== "validate" && subcommand !== "run") return false;
|
|
1370
|
+
const sqlArgs = cmd.args.slice(1);
|
|
1371
|
+
return sqlArgs.some(
|
|
1372
|
+
(arg) => wordPartsContainBacktickSubstitution(
|
|
1373
|
+
arg.parts
|
|
1374
|
+
)
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
function extractWordPartText(parts) {
|
|
1378
|
+
let text = "";
|
|
1379
|
+
for (const part of parts) {
|
|
1380
|
+
const type = part.type;
|
|
1381
|
+
if (type === "Literal" || type === "SingleQuoted" || type === "Escaped") {
|
|
1382
|
+
text += part.value;
|
|
1383
|
+
} else if (type === "DoubleQuoted" && Array.isArray(part.parts)) {
|
|
1384
|
+
text += extractWordPartText(part.parts);
|
|
1385
|
+
} else if (type === "CommandSubstitution" && part.legacy === true) {
|
|
1386
|
+
text += "`" + serialize(part.body).trim() + "`";
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return text;
|
|
1390
|
+
}
|
|
1391
|
+
function extractSqlText(args) {
|
|
1392
|
+
return args.map(
|
|
1393
|
+
(arg) => extractWordPartText(
|
|
1394
|
+
arg.parts
|
|
1395
|
+
)
|
|
1396
|
+
).join(" ");
|
|
1397
|
+
}
|
|
1398
|
+
function rewriteSqlBacktickCommand(cmd, ast) {
|
|
1399
|
+
const subcommand = asStaticWordText(cmd.args[0]);
|
|
1400
|
+
const sqlArgs = cmd.args.slice(1);
|
|
1401
|
+
const unquotedSql = extractSqlText(sqlArgs);
|
|
1402
|
+
const hash2 = createHash("sha256").update(serialize(ast)).digest("hex").slice(0, 12);
|
|
1403
|
+
const sqlPath = `/tmp/sql-inline-${subcommand}-${hash2}.sql`;
|
|
1404
|
+
const heredocTag = `SQL_${hash2.toUpperCase()}`;
|
|
1405
|
+
const groupScript = [
|
|
1406
|
+
`{ cat > ${sqlPath} <<'${heredocTag}'`,
|
|
1407
|
+
unquotedSql,
|
|
1408
|
+
heredocTag,
|
|
1409
|
+
`sql ${subcommand} "$(cat ${sqlPath})"; }`
|
|
1410
|
+
].join("\n");
|
|
1411
|
+
const groupAst = parse(groupScript);
|
|
1412
|
+
return groupAst.statements[0].pipelines[0].commands[0];
|
|
1413
|
+
}
|
|
1414
|
+
function rewriteBackticksInCommands(commands, ast) {
|
|
1415
|
+
let rewritten = false;
|
|
1416
|
+
for (let i = 0; i < commands.length; i++) {
|
|
1417
|
+
const cmd = commands[i];
|
|
1418
|
+
if (isSqlBacktickCommand(cmd)) {
|
|
1419
|
+
commands[i] = rewriteSqlBacktickCommand(cmd, ast);
|
|
1420
|
+
rewritten = true;
|
|
1421
|
+
continue;
|
|
1422
|
+
}
|
|
1423
|
+
if (cmd.type === "If") {
|
|
1424
|
+
for (const clause of cmd.clauses) {
|
|
1425
|
+
if (rewriteBackticksInStatements(clause.condition, ast))
|
|
1426
|
+
rewritten = true;
|
|
1427
|
+
if (rewriteBackticksInStatements(clause.body, ast)) rewritten = true;
|
|
1428
|
+
}
|
|
1429
|
+
if (cmd.elseBody && rewriteBackticksInStatements(cmd.elseBody, ast)) {
|
|
1430
|
+
rewritten = true;
|
|
1431
|
+
}
|
|
1432
|
+
} else if (cmd.type === "While" || cmd.type === "Until") {
|
|
1433
|
+
if (rewriteBackticksInStatements(cmd.condition, ast)) rewritten = true;
|
|
1434
|
+
if (rewriteBackticksInStatements(cmd.body, ast)) rewritten = true;
|
|
1435
|
+
} else if (cmd.type === "For" || cmd.type === "CStyleFor") {
|
|
1436
|
+
if (rewriteBackticksInStatements(cmd.body, ast)) rewritten = true;
|
|
1437
|
+
} else if (cmd.type === "Case") {
|
|
1438
|
+
for (const item of cmd.items) {
|
|
1439
|
+
if (rewriteBackticksInStatements(item.body, ast)) rewritten = true;
|
|
1440
|
+
}
|
|
1441
|
+
} else if (cmd.type === "Subshell" || cmd.type === "Group") {
|
|
1442
|
+
if (rewriteBackticksInStatements(cmd.body, ast)) rewritten = true;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
return rewritten;
|
|
1446
|
+
}
|
|
1447
|
+
function rewriteBackticksInStatements(statements, ast) {
|
|
1448
|
+
let rewritten = false;
|
|
1449
|
+
for (const statement of statements) {
|
|
1450
|
+
for (const pipeline of statement.pipelines) {
|
|
1451
|
+
if (rewriteBackticksInCommands(pipeline.commands, ast)) {
|
|
1452
|
+
rewritten = true;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1249
1455
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1456
|
+
return rewritten;
|
|
1457
|
+
}
|
|
1458
|
+
var SqlBacktickRewritePlugin = class {
|
|
1459
|
+
name = "sql-backtick-rewrite";
|
|
1460
|
+
transform(context) {
|
|
1461
|
+
const ast = context.ast;
|
|
1462
|
+
const rewritten = rewriteBackticksInStatements(ast.statements, ast);
|
|
1463
|
+
return { ast, metadata: { rewritten } };
|
|
1464
|
+
}
|
|
1465
|
+
};
|
|
1466
|
+
|
|
1467
|
+
// packages/text2sql/src/lib/agents/result-tools.ts
|
|
1468
|
+
function createCommand(name, subcommands) {
|
|
1469
|
+
const usageLines = Object.entries(subcommands).map(([, def]) => ` ${name} ${def.usage.padEnd(30)} ${def.description}`).join("\n");
|
|
1470
|
+
return defineCommand(name, async (args, ctx) => {
|
|
1471
|
+
const subcommand = args[0];
|
|
1472
|
+
const restArgs = args.slice(1);
|
|
1473
|
+
if (subcommand && subcommand in subcommands) {
|
|
1474
|
+
return subcommands[subcommand].handler(restArgs, ctx);
|
|
1475
|
+
}
|
|
1476
|
+
return {
|
|
1477
|
+
stdout: "",
|
|
1478
|
+
stderr: `${name}: ${subcommand ? `unknown subcommand '${subcommand}'` : "missing subcommand"}
|
|
1479
|
+
|
|
1480
|
+
Usage:
|
|
1481
|
+
${usageLines}`,
|
|
1482
|
+
exitCode: 1
|
|
1483
|
+
};
|
|
1253
1484
|
});
|
|
1254
|
-
|
|
1255
|
-
|
|
1485
|
+
}
|
|
1486
|
+
function validateReadOnly(query) {
|
|
1487
|
+
const upper = query.toUpperCase().trim();
|
|
1488
|
+
if (!upper.startsWith("SELECT") && !upper.startsWith("WITH")) {
|
|
1489
|
+
return { valid: false, error: "only SELECT or WITH queries allowed" };
|
|
1256
1490
|
}
|
|
1257
|
-
return {
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1491
|
+
return { valid: true };
|
|
1492
|
+
}
|
|
1493
|
+
var SQL_VALIDATE_REMINDER = "Always run `sql validate` before `sql run` to catch syntax errors early.";
|
|
1494
|
+
function createSqlCommand(adapter, metaStore) {
|
|
1495
|
+
return createCommand("sql", {
|
|
1496
|
+
run: {
|
|
1497
|
+
usage: 'run "SELECT ..."',
|
|
1498
|
+
description: "Execute query and store results",
|
|
1499
|
+
handler: async (args, ctx) => {
|
|
1500
|
+
const store = metaStore.getStore();
|
|
1501
|
+
if (store) {
|
|
1502
|
+
store.value = { ...store.value, reminder: SQL_VALIDATE_REMINDER };
|
|
1503
|
+
}
|
|
1504
|
+
const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
1505
|
+
if (!rawQuery) {
|
|
1506
|
+
return {
|
|
1507
|
+
stdout: "",
|
|
1508
|
+
stderr: "sql run: no query provided",
|
|
1509
|
+
exitCode: 1
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
const validation = validateReadOnly(rawQuery);
|
|
1513
|
+
if (!validation.valid) {
|
|
1514
|
+
return {
|
|
1515
|
+
stdout: "",
|
|
1516
|
+
stderr: `sql run: ${validation.error}`,
|
|
1517
|
+
exitCode: 1
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
const query = adapter.format(rawQuery);
|
|
1521
|
+
if (store) {
|
|
1522
|
+
store.value = { ...store.value, formattedSql: query };
|
|
1523
|
+
}
|
|
1524
|
+
const syntaxError = await adapter.validate(query);
|
|
1525
|
+
if (syntaxError) {
|
|
1526
|
+
return {
|
|
1527
|
+
stdout: "",
|
|
1528
|
+
stderr: `sql run: ${syntaxError}`,
|
|
1529
|
+
exitCode: 1
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
try {
|
|
1533
|
+
const rows = await adapter.execute(query);
|
|
1534
|
+
const rowsArray = Array.isArray(rows) ? rows : [];
|
|
1535
|
+
const content = JSON.stringify(rowsArray, null, 2);
|
|
1536
|
+
const filename = `${v7()}.json`;
|
|
1537
|
+
const sqlPath = `/sql/${filename}`;
|
|
1538
|
+
await ctx.fs.mkdir("/sql", { recursive: true });
|
|
1539
|
+
await ctx.fs.writeFile(sqlPath, content);
|
|
1540
|
+
const columns = rowsArray.length > 0 ? Object.keys(rowsArray[0]) : [];
|
|
1541
|
+
return {
|
|
1542
|
+
stdout: [
|
|
1543
|
+
`results stored in ${sqlPath}`,
|
|
1544
|
+
`columns: ${columns.join(", ") || "(none)"}`,
|
|
1545
|
+
`rows: ${rowsArray.length}`
|
|
1546
|
+
].join("\n") + "\n",
|
|
1547
|
+
stderr: "",
|
|
1548
|
+
exitCode: 0
|
|
1549
|
+
};
|
|
1550
|
+
} catch (error) {
|
|
1551
|
+
return {
|
|
1552
|
+
stdout: "",
|
|
1553
|
+
stderr: `sql run: ${error instanceof Error ? error.message : String(error)}`,
|
|
1554
|
+
exitCode: 1
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
},
|
|
1559
|
+
validate: {
|
|
1560
|
+
usage: 'validate "SELECT ..."',
|
|
1561
|
+
description: "Validate query syntax",
|
|
1562
|
+
handler: async (args) => {
|
|
1563
|
+
const rawQuery = args.join(" ").trim().replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
1564
|
+
if (!rawQuery) {
|
|
1565
|
+
return {
|
|
1566
|
+
stdout: "",
|
|
1567
|
+
stderr: "sql validate: no query provided",
|
|
1568
|
+
exitCode: 1
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
const validation = validateReadOnly(rawQuery);
|
|
1572
|
+
if (!validation.valid) {
|
|
1573
|
+
return {
|
|
1574
|
+
stdout: "",
|
|
1575
|
+
stderr: `sql validate: ${validation.error}`,
|
|
1576
|
+
exitCode: 1
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
const query = adapter.format(rawQuery);
|
|
1580
|
+
const store = metaStore.getStore();
|
|
1581
|
+
if (store) store.value = { ...store.value, formattedSql: query };
|
|
1582
|
+
const syntaxError = await adapter.validate(query);
|
|
1583
|
+
if (syntaxError) {
|
|
1584
|
+
return {
|
|
1585
|
+
stdout: "",
|
|
1586
|
+
stderr: `sql validate: ${syntaxError}`,
|
|
1587
|
+
exitCode: 1
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
return {
|
|
1591
|
+
stdout: "valid\n",
|
|
1592
|
+
stderr: "",
|
|
1593
|
+
exitCode: 0
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
function toViolationResult(error) {
|
|
1600
|
+
if (error instanceof SqlProxyViolationError) {
|
|
1601
|
+
return { stdout: "", stderr: `${error.message}
|
|
1602
|
+
`, exitCode: 1 };
|
|
1603
|
+
}
|
|
1604
|
+
return null;
|
|
1263
1605
|
}
|
|
1264
1606
|
async function createResultTools(options) {
|
|
1265
1607
|
const { adapter, skillMounts, filesystem: baseFs } = options;
|
|
1266
1608
|
const metaStore = new AsyncLocalStorage();
|
|
1267
1609
|
const sqlCommand = createSqlCommand(adapter, metaStore);
|
|
1268
1610
|
const fsMounts = skillMounts.map(({ host, sandbox: sandbox2 }) => ({
|
|
1269
|
-
mountPoint:
|
|
1611
|
+
mountPoint: path2.dirname(sandbox2),
|
|
1270
1612
|
filesystem: new OverlayFs({
|
|
1271
|
-
root:
|
|
1613
|
+
root: path2.dirname(host),
|
|
1272
1614
|
mountPoint: "/",
|
|
1273
1615
|
readOnly: true
|
|
1274
1616
|
})
|
|
@@ -1281,6 +1623,8 @@ async function createResultTools(options) {
|
|
|
1281
1623
|
customCommands: [sqlCommand],
|
|
1282
1624
|
fs: filesystem
|
|
1283
1625
|
});
|
|
1626
|
+
bashInstance.registerTransformPlugin(new SqlBacktickRewritePlugin());
|
|
1627
|
+
bashInstance.registerTransformPlugin(new SqlProxyEnforcementPlugin());
|
|
1284
1628
|
const { sandbox, tools } = await createBashTool({
|
|
1285
1629
|
sandbox: bashInstance,
|
|
1286
1630
|
destination: "/",
|
|
@@ -1299,11 +1643,13 @@ async function createResultTools(options) {
|
|
|
1299
1643
|
const guardedSandbox = {
|
|
1300
1644
|
...sandbox,
|
|
1301
1645
|
executeCommand: async (command) => {
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1646
|
+
try {
|
|
1647
|
+
return await sandbox.executeCommand(command);
|
|
1648
|
+
} catch (error) {
|
|
1649
|
+
const violation = toViolationResult(error);
|
|
1650
|
+
if (violation) return violation;
|
|
1651
|
+
throw error;
|
|
1305
1652
|
}
|
|
1306
|
-
return sandbox.executeCommand(command);
|
|
1307
1653
|
}
|
|
1308
1654
|
};
|
|
1309
1655
|
const bash = tool({
|
|
@@ -1317,16 +1663,18 @@ async function createResultTools(options) {
|
|
|
1317
1663
|
if (!execute) {
|
|
1318
1664
|
throw new Error("bash tool execution is not available");
|
|
1319
1665
|
}
|
|
1320
|
-
const blockedResult = getSqlProxyEnforcementResult(command);
|
|
1321
|
-
if (blockedResult) {
|
|
1322
|
-
return blockedResult;
|
|
1323
|
-
}
|
|
1324
1666
|
return metaStore.run({}, async () => {
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1667
|
+
try {
|
|
1668
|
+
const result = await execute({ command }, execOptions);
|
|
1669
|
+
const storeValue = metaStore.getStore()?.value;
|
|
1670
|
+
if (!storeValue) return result;
|
|
1671
|
+
const { reminder, ...meta } = storeValue;
|
|
1672
|
+
return { ...result, meta, reminder };
|
|
1673
|
+
} catch (error) {
|
|
1674
|
+
const violation = toViolationResult(error);
|
|
1675
|
+
if (violation) return violation;
|
|
1676
|
+
throw error;
|
|
1677
|
+
}
|
|
1330
1678
|
});
|
|
1331
1679
|
},
|
|
1332
1680
|
toModelOutput: ({ output }) => {
|
|
@@ -1812,16 +2160,16 @@ var suggestionsAgent = agent({
|
|
|
1812
2160
|
});
|
|
1813
2161
|
|
|
1814
2162
|
// packages/text2sql/src/lib/checkpoint.ts
|
|
1815
|
-
import { createHash } from "node:crypto";
|
|
2163
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1816
2164
|
import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
1817
2165
|
import pLimit from "p-limit";
|
|
1818
2166
|
var Checkpoint = class _Checkpoint {
|
|
1819
2167
|
points;
|
|
1820
2168
|
path;
|
|
1821
2169
|
configHash;
|
|
1822
|
-
constructor(
|
|
2170
|
+
constructor(path7, configHash, points) {
|
|
1823
2171
|
this.points = points;
|
|
1824
|
-
this.path =
|
|
2172
|
+
this.path = path7;
|
|
1825
2173
|
this.configHash = configHash;
|
|
1826
2174
|
}
|
|
1827
2175
|
/**
|
|
@@ -1829,14 +2177,14 @@ var Checkpoint = class _Checkpoint {
|
|
|
1829
2177
|
* Handles corrupted files and config changes gracefully.
|
|
1830
2178
|
*/
|
|
1831
2179
|
static async load(options) {
|
|
1832
|
-
const { path:
|
|
1833
|
-
if (existsSync(
|
|
2180
|
+
const { path: path7, configHash } = options;
|
|
2181
|
+
if (existsSync(path7)) {
|
|
1834
2182
|
try {
|
|
1835
|
-
const content = readFileSync(
|
|
2183
|
+
const content = readFileSync(path7, "utf-8");
|
|
1836
2184
|
const file = JSON.parse(content);
|
|
1837
2185
|
if (configHash && file.configHash && file.configHash !== configHash) {
|
|
1838
2186
|
console.log("\u26A0 Config changed, starting fresh");
|
|
1839
|
-
return new _Checkpoint(
|
|
2187
|
+
return new _Checkpoint(path7, configHash, {});
|
|
1840
2188
|
}
|
|
1841
2189
|
const points = file.points ?? {};
|
|
1842
2190
|
const totalEntries = Object.values(points).reduce(
|
|
@@ -1844,14 +2192,14 @@ var Checkpoint = class _Checkpoint {
|
|
|
1844
2192
|
0
|
|
1845
2193
|
);
|
|
1846
2194
|
console.log(`\u2713 Resuming from checkpoint (${totalEntries} entries)`);
|
|
1847
|
-
return new _Checkpoint(
|
|
2195
|
+
return new _Checkpoint(path7, configHash, points);
|
|
1848
2196
|
} catch {
|
|
1849
2197
|
console.log("\u26A0 Checkpoint corrupted, starting fresh");
|
|
1850
|
-
return new _Checkpoint(
|
|
2198
|
+
return new _Checkpoint(path7, configHash, {});
|
|
1851
2199
|
}
|
|
1852
2200
|
}
|
|
1853
2201
|
console.log("Starting new checkpoint");
|
|
1854
|
-
return new _Checkpoint(
|
|
2202
|
+
return new _Checkpoint(path7, configHash, {});
|
|
1855
2203
|
}
|
|
1856
2204
|
/**
|
|
1857
2205
|
* Run a single computation with checkpointing.
|
|
@@ -1933,7 +2281,7 @@ var Checkpoint = class _Checkpoint {
|
|
|
1933
2281
|
}
|
|
1934
2282
|
};
|
|
1935
2283
|
function hash(value) {
|
|
1936
|
-
return
|
|
2284
|
+
return createHash2("md5").update(JSON.stringify(value)).digest("hex");
|
|
1937
2285
|
}
|
|
1938
2286
|
var Point = class {
|
|
1939
2287
|
#cache;
|
|
@@ -1977,20 +2325,20 @@ var Point = class {
|
|
|
1977
2325
|
}
|
|
1978
2326
|
};
|
|
1979
2327
|
function hashConfig(config) {
|
|
1980
|
-
return
|
|
2328
|
+
return createHash2("md5").update(JSON.stringify(config)).digest("hex");
|
|
1981
2329
|
}
|
|
1982
2330
|
|
|
1983
2331
|
// packages/text2sql/src/lib/file-cache.ts
|
|
1984
|
-
import { createHash as
|
|
2332
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
1985
2333
|
import { existsSync as existsSync2 } from "node:fs";
|
|
1986
2334
|
import { readFile, writeFile } from "node:fs/promises";
|
|
1987
2335
|
import { tmpdir } from "node:os";
|
|
1988
|
-
import
|
|
2336
|
+
import path3 from "node:path";
|
|
1989
2337
|
var FileCache = class {
|
|
1990
2338
|
path;
|
|
1991
2339
|
constructor(watermark, extension = ".txt") {
|
|
1992
|
-
const hash2 =
|
|
1993
|
-
this.path =
|
|
2340
|
+
const hash2 = createHash3("md5").update(watermark).digest("hex");
|
|
2341
|
+
this.path = path3.join(tmpdir(), `text2sql-${hash2}${extension}`);
|
|
1994
2342
|
}
|
|
1995
2343
|
async get() {
|
|
1996
2344
|
if (existsSync2(this.path)) {
|
|
@@ -2019,7 +2367,7 @@ var JsonCache = class extends FileCache {
|
|
|
2019
2367
|
};
|
|
2020
2368
|
|
|
2021
2369
|
// packages/text2sql/src/lib/fs/sqlite/sqlite-fs.ts
|
|
2022
|
-
import * as
|
|
2370
|
+
import * as path4 from "node:path";
|
|
2023
2371
|
import { DatabaseSync } from "node:sqlite";
|
|
2024
2372
|
|
|
2025
2373
|
// packages/text2sql/src/lib/fs/sqlite/ddl.sqlite-fs.sql
|
|
@@ -2067,7 +2415,7 @@ var SqliteFs = class {
|
|
|
2067
2415
|
const segments = p.split("/").filter(Boolean);
|
|
2068
2416
|
let currentPath = "/";
|
|
2069
2417
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
2070
|
-
currentPath =
|
|
2418
|
+
currentPath = path4.posix.join(currentPath, segments[i]);
|
|
2071
2419
|
const exists = this.#stmt("SELECT 1 FROM fs_entries WHERE path = ?").get(
|
|
2072
2420
|
currentPath
|
|
2073
2421
|
);
|
|
@@ -2088,7 +2436,7 @@ var SqliteFs = class {
|
|
|
2088
2436
|
return stmt;
|
|
2089
2437
|
}
|
|
2090
2438
|
#normalizeRoot(root) {
|
|
2091
|
-
return
|
|
2439
|
+
return path4.posix.resolve("/", root.trim());
|
|
2092
2440
|
}
|
|
2093
2441
|
#prefixPath(p) {
|
|
2094
2442
|
if (!this.#root) {
|
|
@@ -2097,7 +2445,7 @@ var SqliteFs = class {
|
|
|
2097
2445
|
if (p === "/") {
|
|
2098
2446
|
return this.#root;
|
|
2099
2447
|
}
|
|
2100
|
-
return
|
|
2448
|
+
return path4.posix.join(this.#root, p);
|
|
2101
2449
|
}
|
|
2102
2450
|
#unprefixPath(p) {
|
|
2103
2451
|
if (!this.#root) {
|
|
@@ -2123,10 +2471,10 @@ var SqliteFs = class {
|
|
|
2123
2471
|
}
|
|
2124
2472
|
}
|
|
2125
2473
|
#normalizePath(p) {
|
|
2126
|
-
return
|
|
2474
|
+
return path4.posix.resolve("/", p);
|
|
2127
2475
|
}
|
|
2128
2476
|
#dirname(p) {
|
|
2129
|
-
const dir =
|
|
2477
|
+
const dir = path4.posix.dirname(p);
|
|
2130
2478
|
return dir === "" ? "/" : dir;
|
|
2131
2479
|
}
|
|
2132
2480
|
#ensureParentExists(filePath) {
|
|
@@ -2189,7 +2537,7 @@ var SqliteFs = class {
|
|
|
2189
2537
|
}
|
|
2190
2538
|
seen.add(p);
|
|
2191
2539
|
const target = this.#normalizePath(
|
|
2192
|
-
|
|
2540
|
+
path4.posix.resolve(this.#dirname(p), entry.symlinkTarget)
|
|
2193
2541
|
);
|
|
2194
2542
|
return this.#resolveSymlink(target, seen);
|
|
2195
2543
|
}
|
|
@@ -2341,11 +2689,11 @@ var SqliteFs = class {
|
|
|
2341
2689
|
this.#useTransaction(() => {
|
|
2342
2690
|
if (options?.recursive) {
|
|
2343
2691
|
const rootPath = this.#root || "/";
|
|
2344
|
-
const relativePath =
|
|
2692
|
+
const relativePath = path4.posix.relative(rootPath, prefixed);
|
|
2345
2693
|
const segments = relativePath.split("/").filter(Boolean);
|
|
2346
2694
|
let currentPath = rootPath;
|
|
2347
2695
|
for (const segment of segments) {
|
|
2348
|
-
currentPath =
|
|
2696
|
+
currentPath = path4.posix.join(currentPath, segment);
|
|
2349
2697
|
const exists = this.#stmt(
|
|
2350
2698
|
"SELECT type FROM fs_entries WHERE path = ?"
|
|
2351
2699
|
).get(currentPath);
|
|
@@ -2396,7 +2744,7 @@ var SqliteFs = class {
|
|
|
2396
2744
|
AND path != ?
|
|
2397
2745
|
AND path NOT LIKE ? || '%/%'`
|
|
2398
2746
|
).all(prefix, resolved, prefix);
|
|
2399
|
-
return rows.map((row) =>
|
|
2747
|
+
return rows.map((row) => path4.posix.basename(row.path));
|
|
2400
2748
|
}
|
|
2401
2749
|
async readdirWithFileTypes(dirPath) {
|
|
2402
2750
|
const normalized = this.#normalizePath(dirPath);
|
|
@@ -2419,7 +2767,7 @@ var SqliteFs = class {
|
|
|
2419
2767
|
AND path NOT LIKE ? || '%/%'`
|
|
2420
2768
|
).all(prefix, resolved, prefix);
|
|
2421
2769
|
return rows.map((row) => ({
|
|
2422
|
-
name:
|
|
2770
|
+
name: path4.posix.basename(row.path),
|
|
2423
2771
|
isFile: row.type === "file",
|
|
2424
2772
|
isDirectory: row.type === "directory",
|
|
2425
2773
|
isSymbolicLink: row.type === "symlink"
|
|
@@ -2474,8 +2822,8 @@ var SqliteFs = class {
|
|
|
2474
2822
|
`SELECT * FROM fs_entries WHERE path = ? OR path LIKE ? || '/%'`
|
|
2475
2823
|
).all(srcPrefixed, srcPrefixed);
|
|
2476
2824
|
for (const entry of allEntries) {
|
|
2477
|
-
const relativePath =
|
|
2478
|
-
const newPath =
|
|
2825
|
+
const relativePath = path4.posix.relative(srcPrefixed, entry.path);
|
|
2826
|
+
const newPath = path4.posix.join(destPrefixed, relativePath);
|
|
2479
2827
|
this.#stmt(
|
|
2480
2828
|
`INSERT OR REPLACE INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
2481
2829
|
VALUES (?, ?, ?, ?, ?, ?)`
|
|
@@ -2542,8 +2890,8 @@ var SqliteFs = class {
|
|
|
2542
2890
|
`SELECT * FROM fs_entries WHERE path = ? OR path LIKE ? || '/%' ORDER BY path DESC`
|
|
2543
2891
|
).all(srcPrefixed, srcPrefixed);
|
|
2544
2892
|
for (const entry of [...allEntries].reverse()) {
|
|
2545
|
-
const relativePath =
|
|
2546
|
-
const newPath =
|
|
2893
|
+
const relativePath = path4.posix.relative(srcPrefixed, entry.path);
|
|
2894
|
+
const newPath = path4.posix.join(destPrefixed, relativePath);
|
|
2547
2895
|
this.#stmt(
|
|
2548
2896
|
`INSERT INTO fs_entries (path, type, mode, size, mtime, symlinkTarget)
|
|
2549
2897
|
VALUES (?, ?, ?, ?, ?, ?)`
|
|
@@ -2596,7 +2944,7 @@ var SqliteFs = class {
|
|
|
2596
2944
|
});
|
|
2597
2945
|
}
|
|
2598
2946
|
resolvePath(base, relativePath) {
|
|
2599
|
-
return
|
|
2947
|
+
return path4.posix.resolve(base, relativePath);
|
|
2600
2948
|
}
|
|
2601
2949
|
getAllPaths() {
|
|
2602
2950
|
const rows = this.#stmt(
|
|
@@ -2708,7 +3056,7 @@ var SqliteFs = class {
|
|
|
2708
3056
|
|
|
2709
3057
|
// packages/text2sql/src/lib/fs/mssql/mssql-fs.ts
|
|
2710
3058
|
import { createRequire } from "node:module";
|
|
2711
|
-
import * as
|
|
3059
|
+
import * as path5 from "node:path";
|
|
2712
3060
|
|
|
2713
3061
|
// packages/text2sql/src/lib/fs/mssql/ddl.mssql-fs.ts
|
|
2714
3062
|
function mssqlFsDDL(schema) {
|
|
@@ -2834,7 +3182,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
2834
3182
|
const segments = p.split("/").filter(Boolean);
|
|
2835
3183
|
let currentPath = "/";
|
|
2836
3184
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
2837
|
-
currentPath =
|
|
3185
|
+
currentPath = path5.posix.join(currentPath, segments[i]);
|
|
2838
3186
|
const exists = await this.#rawQuery(
|
|
2839
3187
|
`SELECT CASE WHEN EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = @p0) THEN 1 ELSE 0 END as [exists]`,
|
|
2840
3188
|
[currentPath]
|
|
@@ -2886,7 +3234,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
2886
3234
|
}
|
|
2887
3235
|
}
|
|
2888
3236
|
#normalizeRoot(root) {
|
|
2889
|
-
return
|
|
3237
|
+
return path5.posix.resolve("/", root.trim());
|
|
2890
3238
|
}
|
|
2891
3239
|
#prefixPath(p) {
|
|
2892
3240
|
if (!this.#root) {
|
|
@@ -2895,7 +3243,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
2895
3243
|
if (p === "/") {
|
|
2896
3244
|
return this.#root;
|
|
2897
3245
|
}
|
|
2898
|
-
return
|
|
3246
|
+
return path5.posix.join(this.#root, p);
|
|
2899
3247
|
}
|
|
2900
3248
|
#unprefixPath(p) {
|
|
2901
3249
|
if (!this.#root) {
|
|
@@ -2910,10 +3258,10 @@ var MssqlFs = class _MssqlFs {
|
|
|
2910
3258
|
return p;
|
|
2911
3259
|
}
|
|
2912
3260
|
#normalizePath(p) {
|
|
2913
|
-
return
|
|
3261
|
+
return path5.posix.resolve("/", p);
|
|
2914
3262
|
}
|
|
2915
3263
|
#dirname(p) {
|
|
2916
|
-
const dir =
|
|
3264
|
+
const dir = path5.posix.dirname(p);
|
|
2917
3265
|
return dir === "" ? "/" : dir;
|
|
2918
3266
|
}
|
|
2919
3267
|
async #ensureParentExists(filePath, transaction) {
|
|
@@ -3002,7 +3350,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3002
3350
|
}
|
|
3003
3351
|
seen.add(p);
|
|
3004
3352
|
const target = this.#normalizePath(
|
|
3005
|
-
|
|
3353
|
+
path5.posix.resolve(this.#dirname(p), entry.symlinkTarget)
|
|
3006
3354
|
);
|
|
3007
3355
|
return this.#resolveSymlink(target, seen);
|
|
3008
3356
|
}
|
|
@@ -3185,11 +3533,11 @@ var MssqlFs = class _MssqlFs {
|
|
|
3185
3533
|
await this.#useTransaction(async (transaction) => {
|
|
3186
3534
|
if (options?.recursive) {
|
|
3187
3535
|
const rootPath = this.#root || "/";
|
|
3188
|
-
const relativePath =
|
|
3536
|
+
const relativePath = path5.posix.relative(rootPath, prefixed);
|
|
3189
3537
|
const segments = relativePath.split("/").filter(Boolean);
|
|
3190
3538
|
let currentPath = rootPath;
|
|
3191
3539
|
for (const segment of segments) {
|
|
3192
|
-
currentPath =
|
|
3540
|
+
currentPath = path5.posix.join(currentPath, segment);
|
|
3193
3541
|
const checkReq = transaction.request();
|
|
3194
3542
|
checkReq.input("p0", currentPath);
|
|
3195
3543
|
const result = await checkReq.query(
|
|
@@ -3253,7 +3601,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3253
3601
|
AND path NOT LIKE @p0 + '%/%'`,
|
|
3254
3602
|
[prefix, resolved]
|
|
3255
3603
|
);
|
|
3256
|
-
return rows.map((row) =>
|
|
3604
|
+
return rows.map((row) => path5.posix.basename(row.path));
|
|
3257
3605
|
}
|
|
3258
3606
|
async readdirWithFileTypes(dirPath) {
|
|
3259
3607
|
const normalized = this.#normalizePath(dirPath);
|
|
@@ -3279,7 +3627,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3279
3627
|
[prefix, resolved]
|
|
3280
3628
|
);
|
|
3281
3629
|
return rows.map((row) => ({
|
|
3282
|
-
name:
|
|
3630
|
+
name: path5.posix.basename(row.path),
|
|
3283
3631
|
isFile: row.type === "file",
|
|
3284
3632
|
isDirectory: row.type === "directory",
|
|
3285
3633
|
isSymbolicLink: row.type === "symlink"
|
|
@@ -3348,8 +3696,8 @@ var MssqlFs = class _MssqlFs {
|
|
|
3348
3696
|
`SELECT * FROM ${this.#t("fs_entries")} WHERE path = @p0 OR path LIKE @p0 + '/%'`
|
|
3349
3697
|
);
|
|
3350
3698
|
for (const entry of allEntriesResult.recordset) {
|
|
3351
|
-
const relativePath =
|
|
3352
|
-
const newPath =
|
|
3699
|
+
const relativePath = path5.posix.relative(srcPrefixed, entry.path);
|
|
3700
|
+
const newPath = path5.posix.join(destPrefixed, relativePath);
|
|
3353
3701
|
const insertReq = transaction.request();
|
|
3354
3702
|
insertReq.input("p0", newPath);
|
|
3355
3703
|
insertReq.input("p1", entry.type);
|
|
@@ -3458,8 +3806,8 @@ var MssqlFs = class _MssqlFs {
|
|
|
3458
3806
|
`DELETE FROM ${this.#t("fs_entries")} WHERE path = @dp0 OR path LIKE @dp0 + '/%'`
|
|
3459
3807
|
);
|
|
3460
3808
|
for (const entry of [...allEntriesResult.recordset].reverse()) {
|
|
3461
|
-
const relativePath =
|
|
3462
|
-
const newPath =
|
|
3809
|
+
const relativePath = path5.posix.relative(srcPrefixed, entry.path);
|
|
3810
|
+
const newPath = path5.posix.join(destPrefixed, relativePath);
|
|
3463
3811
|
const insertReq = transaction.request();
|
|
3464
3812
|
insertReq.input("p0", newPath);
|
|
3465
3813
|
insertReq.input("p1", entry.type);
|
|
@@ -3552,7 +3900,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3552
3900
|
});
|
|
3553
3901
|
}
|
|
3554
3902
|
resolvePath(base, relativePath) {
|
|
3555
|
-
return
|
|
3903
|
+
return path5.posix.resolve(base, relativePath);
|
|
3556
3904
|
}
|
|
3557
3905
|
getAllPaths() {
|
|
3558
3906
|
throw new Error(
|
|
@@ -3693,7 +4041,7 @@ var MssqlFs = class _MssqlFs {
|
|
|
3693
4041
|
|
|
3694
4042
|
// packages/text2sql/src/lib/fs/postgres/postgres-fs.ts
|
|
3695
4043
|
import { createRequire as createRequire2 } from "node:module";
|
|
3696
|
-
import * as
|
|
4044
|
+
import * as path6 from "node:path";
|
|
3697
4045
|
|
|
3698
4046
|
// packages/text2sql/src/lib/fs/postgres/ddl.postgres-fs.ts
|
|
3699
4047
|
function postgresFsDDL(schema) {
|
|
@@ -3798,7 +4146,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
3798
4146
|
const segments = p.split("/").filter(Boolean);
|
|
3799
4147
|
let currentPath = "/";
|
|
3800
4148
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
3801
|
-
currentPath =
|
|
4149
|
+
currentPath = path6.posix.join(currentPath, segments[i]);
|
|
3802
4150
|
const exists = await this.#rawQuery(
|
|
3803
4151
|
`SELECT EXISTS(SELECT 1 FROM ${this.#t("fs_entries")} WHERE path = $1) AS exists`,
|
|
3804
4152
|
[currentPath]
|
|
@@ -3843,7 +4191,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
3843
4191
|
}
|
|
3844
4192
|
}
|
|
3845
4193
|
#normalizeRoot(root) {
|
|
3846
|
-
return
|
|
4194
|
+
return path6.posix.resolve("/", root.trim());
|
|
3847
4195
|
}
|
|
3848
4196
|
#prefixPath(p) {
|
|
3849
4197
|
if (!this.#root) {
|
|
@@ -3852,7 +4200,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
3852
4200
|
if (p === "/") {
|
|
3853
4201
|
return this.#root;
|
|
3854
4202
|
}
|
|
3855
|
-
return
|
|
4203
|
+
return path6.posix.join(this.#root, p);
|
|
3856
4204
|
}
|
|
3857
4205
|
#unprefixPath(p) {
|
|
3858
4206
|
if (!this.#root) {
|
|
@@ -3867,10 +4215,10 @@ var PostgresFs = class _PostgresFs {
|
|
|
3867
4215
|
return p;
|
|
3868
4216
|
}
|
|
3869
4217
|
#normalizePath(p) {
|
|
3870
|
-
return
|
|
4218
|
+
return path6.posix.resolve("/", p);
|
|
3871
4219
|
}
|
|
3872
4220
|
#dirname(p) {
|
|
3873
|
-
const dir =
|
|
4221
|
+
const dir = path6.posix.dirname(p);
|
|
3874
4222
|
return dir === "" ? "/" : dir;
|
|
3875
4223
|
}
|
|
3876
4224
|
async #ensureParentExists(filePath, client) {
|
|
@@ -3950,7 +4298,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
3950
4298
|
}
|
|
3951
4299
|
seen.add(p);
|
|
3952
4300
|
const target = this.#normalizePath(
|
|
3953
|
-
|
|
4301
|
+
path6.posix.resolve(this.#dirname(p), entry.symlink_target)
|
|
3954
4302
|
);
|
|
3955
4303
|
return this.#resolveSymlink(target, seen);
|
|
3956
4304
|
}
|
|
@@ -4116,11 +4464,11 @@ var PostgresFs = class _PostgresFs {
|
|
|
4116
4464
|
await this.#useTransaction(async (client) => {
|
|
4117
4465
|
if (options?.recursive) {
|
|
4118
4466
|
const rootPath = this.#root || "/";
|
|
4119
|
-
const relativePath =
|
|
4467
|
+
const relativePath = path6.posix.relative(rootPath, prefixed);
|
|
4120
4468
|
const segments = relativePath.split("/").filter(Boolean);
|
|
4121
4469
|
let currentPath = rootPath;
|
|
4122
4470
|
for (const segment of segments) {
|
|
4123
|
-
currentPath =
|
|
4471
|
+
currentPath = path6.posix.join(currentPath, segment);
|
|
4124
4472
|
const result = await client.query(
|
|
4125
4473
|
`SELECT type FROM ${this.#t("fs_entries")} WHERE path = $1`,
|
|
4126
4474
|
[currentPath]
|
|
@@ -4178,7 +4526,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
4178
4526
|
AND path NOT LIKE $1 || '%/%'`,
|
|
4179
4527
|
[prefix, resolved]
|
|
4180
4528
|
);
|
|
4181
|
-
return rows.map((row) =>
|
|
4529
|
+
return rows.map((row) => path6.posix.basename(row.path));
|
|
4182
4530
|
}
|
|
4183
4531
|
async readdirWithFileTypes(dirPath) {
|
|
4184
4532
|
const normalized = this.#normalizePath(dirPath);
|
|
@@ -4204,7 +4552,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
4204
4552
|
[prefix, resolved]
|
|
4205
4553
|
);
|
|
4206
4554
|
return rows.map((row) => ({
|
|
4207
|
-
name:
|
|
4555
|
+
name: path6.posix.basename(row.path),
|
|
4208
4556
|
isFile: row.type === "file",
|
|
4209
4557
|
isDirectory: row.type === "directory",
|
|
4210
4558
|
isSymbolicLink: row.type === "symlink"
|
|
@@ -4269,8 +4617,8 @@ var PostgresFs = class _PostgresFs {
|
|
|
4269
4617
|
[srcPrefixed]
|
|
4270
4618
|
);
|
|
4271
4619
|
for (const entry of allEntriesResult.rows) {
|
|
4272
|
-
const relativePath =
|
|
4273
|
-
const newPath =
|
|
4620
|
+
const relativePath = path6.posix.relative(srcPrefixed, entry.path);
|
|
4621
|
+
const newPath = path6.posix.join(destPrefixed, relativePath);
|
|
4274
4622
|
await client.query(
|
|
4275
4623
|
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
4276
4624
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
@@ -4359,8 +4707,8 @@ var PostgresFs = class _PostgresFs {
|
|
|
4359
4707
|
[destPrefixed]
|
|
4360
4708
|
);
|
|
4361
4709
|
for (const entry of [...allEntriesResult.rows].reverse()) {
|
|
4362
|
-
const relativePath =
|
|
4363
|
-
const newPath =
|
|
4710
|
+
const relativePath = path6.posix.relative(srcPrefixed, entry.path);
|
|
4711
|
+
const newPath = path6.posix.join(destPrefixed, relativePath);
|
|
4364
4712
|
await client.query(
|
|
4365
4713
|
`INSERT INTO ${this.#t("fs_entries")} (path, type, mode, size, mtime, symlink_target)
|
|
4366
4714
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
@@ -4433,7 +4781,7 @@ var PostgresFs = class _PostgresFs {
|
|
|
4433
4781
|
});
|
|
4434
4782
|
}
|
|
4435
4783
|
resolvePath(base, relativePath) {
|
|
4436
|
-
return
|
|
4784
|
+
return path6.posix.resolve(base, relativePath);
|
|
4437
4785
|
}
|
|
4438
4786
|
getAllPaths() {
|
|
4439
4787
|
throw new Error(
|
|
@@ -4569,29 +4917,29 @@ var ScopedFs = class {
|
|
|
4569
4917
|
this.#base = options.base;
|
|
4570
4918
|
this.#prefix = options.prefix.replace(/\/$/, "");
|
|
4571
4919
|
}
|
|
4572
|
-
#scope(
|
|
4573
|
-
return `${this.#prefix}${
|
|
4920
|
+
#scope(path7) {
|
|
4921
|
+
return `${this.#prefix}${path7}`;
|
|
4574
4922
|
}
|
|
4575
|
-
#unscope(
|
|
4576
|
-
if (
|
|
4923
|
+
#unscope(path7) {
|
|
4924
|
+
if (path7 === this.#prefix) {
|
|
4577
4925
|
return "/";
|
|
4578
4926
|
}
|
|
4579
|
-
if (
|
|
4580
|
-
return
|
|
4927
|
+
if (path7.startsWith(this.#prefix + "/")) {
|
|
4928
|
+
return path7.slice(this.#prefix.length) || "/";
|
|
4581
4929
|
}
|
|
4582
|
-
return
|
|
4930
|
+
return path7;
|
|
4583
4931
|
}
|
|
4584
|
-
async writeFile(
|
|
4585
|
-
await this.#base.writeFile(this.#scope(
|
|
4932
|
+
async writeFile(path7, content, options) {
|
|
4933
|
+
await this.#base.writeFile(this.#scope(path7), content, options);
|
|
4586
4934
|
}
|
|
4587
|
-
async appendFile(
|
|
4588
|
-
await this.#base.appendFile(this.#scope(
|
|
4935
|
+
async appendFile(path7, content, options) {
|
|
4936
|
+
await this.#base.appendFile(this.#scope(path7), content, options);
|
|
4589
4937
|
}
|
|
4590
|
-
async mkdir(
|
|
4591
|
-
return this.#base.mkdir(this.#scope(
|
|
4938
|
+
async mkdir(path7, options) {
|
|
4939
|
+
return this.#base.mkdir(this.#scope(path7), options);
|
|
4592
4940
|
}
|
|
4593
|
-
async rm(
|
|
4594
|
-
await this.#base.rm(this.#scope(
|
|
4941
|
+
async rm(path7, options) {
|
|
4942
|
+
await this.#base.rm(this.#scope(path7), options);
|
|
4595
4943
|
}
|
|
4596
4944
|
async cp(src, dest, options) {
|
|
4597
4945
|
await this.#base.cp(this.#scope(src), this.#scope(dest), options);
|
|
@@ -4599,8 +4947,8 @@ var ScopedFs = class {
|
|
|
4599
4947
|
async mv(src, dest) {
|
|
4600
4948
|
await this.#base.mv(this.#scope(src), this.#scope(dest));
|
|
4601
4949
|
}
|
|
4602
|
-
async chmod(
|
|
4603
|
-
return this.#base.chmod(this.#scope(
|
|
4950
|
+
async chmod(path7, mode) {
|
|
4951
|
+
return this.#base.chmod(this.#scope(path7), mode);
|
|
4604
4952
|
}
|
|
4605
4953
|
async symlink(target, linkPath) {
|
|
4606
4954
|
await this.#base.symlink(target, this.#scope(linkPath));
|
|
@@ -4608,35 +4956,35 @@ var ScopedFs = class {
|
|
|
4608
4956
|
async link(existingPath, newPath) {
|
|
4609
4957
|
await this.#base.link(this.#scope(existingPath), this.#scope(newPath));
|
|
4610
4958
|
}
|
|
4611
|
-
readFile(
|
|
4612
|
-
return this.#base.readFile(this.#scope(
|
|
4959
|
+
readFile(path7, options) {
|
|
4960
|
+
return this.#base.readFile(this.#scope(path7), options);
|
|
4613
4961
|
}
|
|
4614
|
-
readFileBuffer(
|
|
4615
|
-
return this.#base.readFileBuffer(this.#scope(
|
|
4962
|
+
readFileBuffer(path7) {
|
|
4963
|
+
return this.#base.readFileBuffer(this.#scope(path7));
|
|
4616
4964
|
}
|
|
4617
|
-
stat(
|
|
4618
|
-
return this.#base.stat(this.#scope(
|
|
4965
|
+
stat(path7) {
|
|
4966
|
+
return this.#base.stat(this.#scope(path7));
|
|
4619
4967
|
}
|
|
4620
|
-
lstat(
|
|
4621
|
-
return this.#base.lstat(this.#scope(
|
|
4968
|
+
lstat(path7) {
|
|
4969
|
+
return this.#base.lstat(this.#scope(path7));
|
|
4622
4970
|
}
|
|
4623
|
-
readdir(
|
|
4624
|
-
return this.#base.readdir(this.#scope(
|
|
4971
|
+
readdir(path7) {
|
|
4972
|
+
return this.#base.readdir(this.#scope(path7));
|
|
4625
4973
|
}
|
|
4626
|
-
readdirWithFileTypes(
|
|
4627
|
-
return this.#base.readdirWithFileTypes(this.#scope(
|
|
4974
|
+
readdirWithFileTypes(path7) {
|
|
4975
|
+
return this.#base.readdirWithFileTypes(this.#scope(path7));
|
|
4628
4976
|
}
|
|
4629
|
-
exists(
|
|
4630
|
-
return this.#base.exists(this.#scope(
|
|
4977
|
+
exists(path7) {
|
|
4978
|
+
return this.#base.exists(this.#scope(path7));
|
|
4631
4979
|
}
|
|
4632
|
-
readlink(
|
|
4633
|
-
return this.#base.readlink(this.#scope(
|
|
4980
|
+
readlink(path7) {
|
|
4981
|
+
return this.#base.readlink(this.#scope(path7));
|
|
4634
4982
|
}
|
|
4635
|
-
realpath(
|
|
4636
|
-
return this.#base.realpath(this.#scope(
|
|
4983
|
+
realpath(path7) {
|
|
4984
|
+
return this.#base.realpath(this.#scope(path7)).then((p) => this.#unscope(p));
|
|
4637
4985
|
}
|
|
4638
|
-
utimes(
|
|
4639
|
-
return this.#base.utimes(this.#scope(
|
|
4986
|
+
utimes(path7, atime, mtime) {
|
|
4987
|
+
return this.#base.utimes(this.#scope(path7), atime, mtime);
|
|
4640
4988
|
}
|
|
4641
4989
|
resolvePath(base, relativePath) {
|
|
4642
4990
|
return this.#base.resolvePath(base, relativePath);
|
|
@@ -4657,22 +5005,22 @@ var TrackedFs = class {
|
|
|
4657
5005
|
getCreatedFiles() {
|
|
4658
5006
|
return [...this.#createdFiles];
|
|
4659
5007
|
}
|
|
4660
|
-
async writeFile(
|
|
4661
|
-
await this.#base.writeFile(
|
|
4662
|
-
this.#createdFiles.add(
|
|
5008
|
+
async writeFile(path7, content, options) {
|
|
5009
|
+
await this.#base.writeFile(path7, content, options);
|
|
5010
|
+
this.#createdFiles.add(path7);
|
|
4663
5011
|
}
|
|
4664
|
-
async appendFile(
|
|
4665
|
-
await this.#base.appendFile(
|
|
4666
|
-
this.#createdFiles.add(
|
|
5012
|
+
async appendFile(path7, content, options) {
|
|
5013
|
+
await this.#base.appendFile(path7, content, options);
|
|
5014
|
+
this.#createdFiles.add(path7);
|
|
4667
5015
|
}
|
|
4668
|
-
async mkdir(
|
|
4669
|
-
return this.#base.mkdir(
|
|
5016
|
+
async mkdir(path7, options) {
|
|
5017
|
+
return this.#base.mkdir(path7, options);
|
|
4670
5018
|
}
|
|
4671
|
-
async rm(
|
|
4672
|
-
await this.#base.rm(
|
|
4673
|
-
this.#createdFiles.delete(
|
|
5019
|
+
async rm(path7, options) {
|
|
5020
|
+
await this.#base.rm(path7, options);
|
|
5021
|
+
this.#createdFiles.delete(path7);
|
|
4674
5022
|
if (options?.recursive) {
|
|
4675
|
-
const prefix =
|
|
5023
|
+
const prefix = path7.endsWith("/") ? path7 : path7 + "/";
|
|
4676
5024
|
for (const file of this.#createdFiles) {
|
|
4677
5025
|
if (file.startsWith(prefix)) {
|
|
4678
5026
|
this.#createdFiles.delete(file);
|
|
@@ -4689,8 +5037,8 @@ var TrackedFs = class {
|
|
|
4689
5037
|
this.#createdFiles.delete(src);
|
|
4690
5038
|
this.#createdFiles.add(dest);
|
|
4691
5039
|
}
|
|
4692
|
-
async chmod(
|
|
4693
|
-
return this.#base.chmod(
|
|
5040
|
+
async chmod(path7, mode) {
|
|
5041
|
+
return this.#base.chmod(path7, mode);
|
|
4694
5042
|
}
|
|
4695
5043
|
async symlink(target, linkPath) {
|
|
4696
5044
|
await this.#base.symlink(target, linkPath);
|
|
@@ -4700,29 +5048,29 @@ var TrackedFs = class {
|
|
|
4700
5048
|
await this.#base.link(existingPath, newPath);
|
|
4701
5049
|
this.#createdFiles.add(newPath);
|
|
4702
5050
|
}
|
|
4703
|
-
readFile(
|
|
4704
|
-
return this.#base.readFile(
|
|
5051
|
+
readFile(path7, options) {
|
|
5052
|
+
return this.#base.readFile(path7, options);
|
|
4705
5053
|
}
|
|
4706
|
-
readFileBuffer(
|
|
4707
|
-
return this.#base.readFileBuffer(
|
|
5054
|
+
readFileBuffer(path7) {
|
|
5055
|
+
return this.#base.readFileBuffer(path7);
|
|
4708
5056
|
}
|
|
4709
|
-
stat(
|
|
4710
|
-
return this.#base.stat(
|
|
5057
|
+
stat(path7) {
|
|
5058
|
+
return this.#base.stat(path7);
|
|
4711
5059
|
}
|
|
4712
|
-
lstat(
|
|
4713
|
-
return this.#base.lstat(
|
|
5060
|
+
lstat(path7) {
|
|
5061
|
+
return this.#base.lstat(path7);
|
|
4714
5062
|
}
|
|
4715
|
-
readdir(
|
|
4716
|
-
return this.#base.readdir(
|
|
5063
|
+
readdir(path7) {
|
|
5064
|
+
return this.#base.readdir(path7);
|
|
4717
5065
|
}
|
|
4718
|
-
readdirWithFileTypes(
|
|
4719
|
-
return this.#base.readdirWithFileTypes(
|
|
5066
|
+
readdirWithFileTypes(path7) {
|
|
5067
|
+
return this.#base.readdirWithFileTypes(path7);
|
|
4720
5068
|
}
|
|
4721
|
-
exists(
|
|
4722
|
-
return this.#base.exists(
|
|
5069
|
+
exists(path7) {
|
|
5070
|
+
return this.#base.exists(path7);
|
|
4723
5071
|
}
|
|
4724
|
-
readlink(
|
|
4725
|
-
return this.#base.readlink(
|
|
5072
|
+
readlink(path7) {
|
|
5073
|
+
return this.#base.readlink(path7);
|
|
4726
5074
|
}
|
|
4727
5075
|
resolvePath(base, relativePath) {
|
|
4728
5076
|
return this.#base.resolvePath(base, relativePath);
|
|
@@ -4730,11 +5078,11 @@ var TrackedFs = class {
|
|
|
4730
5078
|
getAllPaths() {
|
|
4731
5079
|
return this.#base.getAllPaths?.() ?? [];
|
|
4732
5080
|
}
|
|
4733
|
-
realpath(
|
|
4734
|
-
return this.#base.realpath(
|
|
5081
|
+
realpath(path7) {
|
|
5082
|
+
return this.#base.realpath(path7);
|
|
4735
5083
|
}
|
|
4736
|
-
utimes(
|
|
4737
|
-
return this.#base.utimes(
|
|
5084
|
+
utimes(path7, atime, mtime) {
|
|
5085
|
+
return this.#base.utimes(path7, atime, mtime);
|
|
4738
5086
|
}
|
|
4739
5087
|
};
|
|
4740
5088
|
|