@lmthing/repl 0.0.1
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/crypto-BTVPQPPV.js +45 -0
- package/dist/csv-3IL3IKAY.js +115 -0
- package/dist/date-DL6PIRCI.js +56 -0
- package/dist/db-IVNVHDFE.js +68 -0
- package/dist/env-PPLEQ47H.js +47 -0
- package/dist/fetch-TN4ZJVQW.js +118 -0
- package/dist/fs-QRXDDXB5.js +127 -0
- package/dist/image-FGNY3CMJ.js +56 -0
- package/dist/index.d.ts +1691 -0
- package/dist/index.js +3809 -0
- package/dist/json-VPTMTXR6.js +113 -0
- package/dist/path-K3VLZVTH.js +54 -0
- package/dist/shell-NAGRIUA4.js +102 -0
- package/package.json +39 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/catalog/crypto.ts
|
|
2
|
+
import { createHash, randomBytes as nodeRandomBytes, randomUUID } from "crypto";
|
|
3
|
+
var cryptoModule = {
|
|
4
|
+
id: "crypto",
|
|
5
|
+
description: "Cryptographic utilities",
|
|
6
|
+
functions: [
|
|
7
|
+
{
|
|
8
|
+
name: "hash",
|
|
9
|
+
description: "Hash a string",
|
|
10
|
+
signature: "(data: string, algorithm?: 'sha256' | 'sha512' | 'md5') => string",
|
|
11
|
+
fn: (data, algorithm) => {
|
|
12
|
+
const algo = algorithm || "sha256";
|
|
13
|
+
return createHash(algo).update(data).digest("hex");
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "randomBytes",
|
|
18
|
+
description: "Random hex string",
|
|
19
|
+
signature: "(length: number) => string",
|
|
20
|
+
fn: (length) => nodeRandomBytes(length).toString("hex")
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "uuid",
|
|
24
|
+
description: "Generate UUID v4",
|
|
25
|
+
signature: "() => string",
|
|
26
|
+
fn: () => randomUUID()
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "base64Encode",
|
|
30
|
+
description: "Encode to base64",
|
|
31
|
+
signature: "(data: string) => string",
|
|
32
|
+
fn: (data) => Buffer.from(data).toString("base64")
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "base64Decode",
|
|
36
|
+
description: "Decode from base64",
|
|
37
|
+
signature: "(data: string) => string",
|
|
38
|
+
fn: (data) => Buffer.from(data, "base64").toString("utf-8")
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
var crypto_default = cryptoModule;
|
|
43
|
+
export {
|
|
44
|
+
crypto_default as default
|
|
45
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// src/catalog/csv.ts
|
|
2
|
+
import { readFile, writeFile } from "fs/promises";
|
|
3
|
+
function parseCsv(text, options) {
|
|
4
|
+
const delimiter = options?.delimiter ?? ",";
|
|
5
|
+
const hasHeader = options?.header !== false;
|
|
6
|
+
const lines = text.trim().split("\n");
|
|
7
|
+
if (lines.length === 0) return [];
|
|
8
|
+
const parseRow = (line) => {
|
|
9
|
+
const values = [];
|
|
10
|
+
let current = "";
|
|
11
|
+
let inQuotes = false;
|
|
12
|
+
for (let i = 0; i < line.length; i++) {
|
|
13
|
+
const ch = line[i];
|
|
14
|
+
if (inQuotes) {
|
|
15
|
+
if (ch === '"') {
|
|
16
|
+
if (line[i + 1] === '"') {
|
|
17
|
+
current += '"';
|
|
18
|
+
i++;
|
|
19
|
+
} else {
|
|
20
|
+
inQuotes = false;
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
current += ch;
|
|
24
|
+
}
|
|
25
|
+
} else if (ch === '"') {
|
|
26
|
+
inQuotes = true;
|
|
27
|
+
} else if (ch === delimiter) {
|
|
28
|
+
values.push(current.trim());
|
|
29
|
+
current = "";
|
|
30
|
+
} else {
|
|
31
|
+
current += ch;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
values.push(current.trim());
|
|
35
|
+
return values;
|
|
36
|
+
};
|
|
37
|
+
if (hasHeader && lines.length > 1) {
|
|
38
|
+
const headers = parseRow(lines[0]);
|
|
39
|
+
return lines.slice(1).filter((l) => l.trim()).map((line) => {
|
|
40
|
+
const values = parseRow(line);
|
|
41
|
+
const obj = {};
|
|
42
|
+
headers.forEach((h, i) => {
|
|
43
|
+
obj[h] = values[i] ?? "";
|
|
44
|
+
});
|
|
45
|
+
return obj;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return lines.filter((l) => l.trim()).map(parseRow);
|
|
49
|
+
}
|
|
50
|
+
function stringifyCsv(data, options) {
|
|
51
|
+
const delimiter = options?.delimiter ?? ",";
|
|
52
|
+
const showHeader = options?.header !== false;
|
|
53
|
+
if (data.length === 0) return "";
|
|
54
|
+
const first = data[0];
|
|
55
|
+
if (typeof first === "object" && first !== null && !Array.isArray(first)) {
|
|
56
|
+
const keys = Object.keys(first);
|
|
57
|
+
const lines = [];
|
|
58
|
+
if (showHeader) lines.push(keys.join(delimiter));
|
|
59
|
+
for (const row of data) {
|
|
60
|
+
const obj = row;
|
|
61
|
+
lines.push(keys.map((k) => escCsvVal(String(obj[k] ?? ""), delimiter)).join(delimiter));
|
|
62
|
+
}
|
|
63
|
+
return lines.join("\n");
|
|
64
|
+
}
|
|
65
|
+
return data.map((row) => {
|
|
66
|
+
if (Array.isArray(row)) return row.map((v) => escCsvVal(String(v), delimiter)).join(delimiter);
|
|
67
|
+
return String(row);
|
|
68
|
+
}).join("\n");
|
|
69
|
+
}
|
|
70
|
+
function escCsvVal(val, delimiter) {
|
|
71
|
+
if (val.includes(delimiter) || val.includes('"') || val.includes("\n")) {
|
|
72
|
+
return `"${val.replace(/"/g, '""')}"`;
|
|
73
|
+
}
|
|
74
|
+
return val;
|
|
75
|
+
}
|
|
76
|
+
var csvModule = {
|
|
77
|
+
id: "csv",
|
|
78
|
+
description: "CSV processing",
|
|
79
|
+
functions: [
|
|
80
|
+
{
|
|
81
|
+
name: "csvParse",
|
|
82
|
+
description: "Parse CSV to array of objects/arrays",
|
|
83
|
+
signature: "(text: string, options?: { header?: boolean, delimiter?: string }) => any[]",
|
|
84
|
+
fn: (text, options) => parseCsv(text, options)
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "csvStringify",
|
|
88
|
+
description: "Convert to CSV string",
|
|
89
|
+
signature: "(data: any[], options?: { header?: boolean, delimiter?: string }) => string",
|
|
90
|
+
fn: (data, options) => stringifyCsv(data, options)
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "csvReadFile",
|
|
94
|
+
description: "Read and parse CSV file",
|
|
95
|
+
signature: "(path: string, options?: CsvOptions) => Promise<any[]>",
|
|
96
|
+
fn: async (path, options) => {
|
|
97
|
+
const text = await readFile(path, "utf-8");
|
|
98
|
+
return parseCsv(text, options);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "csvWriteFile",
|
|
103
|
+
description: "Write data as CSV file",
|
|
104
|
+
signature: "(path: string, data: any[], options?: CsvOptions) => Promise<void>",
|
|
105
|
+
fn: async (path, data, options) => {
|
|
106
|
+
const text = stringifyCsv(data, options);
|
|
107
|
+
await writeFile(path, text, "utf-8");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
};
|
|
112
|
+
var csv_default = csvModule;
|
|
113
|
+
export {
|
|
114
|
+
csv_default as default
|
|
115
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/catalog/date.ts
|
|
2
|
+
function formatDateStr(date, format) {
|
|
3
|
+
const pad = (n, len = 2) => String(n).padStart(len, "0");
|
|
4
|
+
return format.replace("YYYY", String(date.getFullYear())).replace("MM", pad(date.getMonth() + 1)).replace("DD", pad(date.getDate())).replace("HH", pad(date.getHours())).replace("mm", pad(date.getMinutes())).replace("ss", pad(date.getSeconds()));
|
|
5
|
+
}
|
|
6
|
+
var dateModule = {
|
|
7
|
+
id: "date",
|
|
8
|
+
description: "Date/time utilities",
|
|
9
|
+
functions: [
|
|
10
|
+
{
|
|
11
|
+
name: "now",
|
|
12
|
+
description: "Current ISO 8601 timestamp",
|
|
13
|
+
signature: "() => string",
|
|
14
|
+
fn: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "parseDate",
|
|
18
|
+
description: "Parse date string",
|
|
19
|
+
signature: "(input: string) => Date",
|
|
20
|
+
fn: (input) => new Date(input)
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "formatDate",
|
|
24
|
+
description: 'Format date (e.g., "YYYY-MM-DD")',
|
|
25
|
+
signature: "(date: Date | string, format: string) => string",
|
|
26
|
+
fn: (date, format) => {
|
|
27
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
28
|
+
return formatDateStr(d, format);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "addDays",
|
|
33
|
+
description: "Date arithmetic \u2014 add days",
|
|
34
|
+
signature: "(date: Date | string, days: number) => Date",
|
|
35
|
+
fn: (date, days) => {
|
|
36
|
+
const d = date instanceof Date ? new Date(date) : new Date(date);
|
|
37
|
+
d.setDate(d.getDate() + days);
|
|
38
|
+
return d;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "diffDays",
|
|
43
|
+
description: "Days between two dates",
|
|
44
|
+
signature: "(a: Date | string, b: Date | string) => number",
|
|
45
|
+
fn: (a, b) => {
|
|
46
|
+
const da = a instanceof Date ? a : new Date(a);
|
|
47
|
+
const db = b instanceof Date ? b : new Date(b);
|
|
48
|
+
return Math.round((db.getTime() - da.getTime()) / (1e3 * 60 * 60 * 24));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
};
|
|
53
|
+
var date_default = dateModule;
|
|
54
|
+
export {
|
|
55
|
+
date_default as default
|
|
56
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// src/catalog/db.ts
|
|
2
|
+
async function getSqlite() {
|
|
3
|
+
try {
|
|
4
|
+
return (await import("better-sqlite3")).default;
|
|
5
|
+
} catch {
|
|
6
|
+
throw new Error("better-sqlite3 is required for database operations. Install with: pnpm add better-sqlite3");
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
var dbInstance = null;
|
|
10
|
+
var dbPath = "./repl.db";
|
|
11
|
+
function setDbPath(path) {
|
|
12
|
+
dbPath = path;
|
|
13
|
+
dbInstance = null;
|
|
14
|
+
}
|
|
15
|
+
async function getDb() {
|
|
16
|
+
if (!dbInstance) {
|
|
17
|
+
const Database = await getSqlite();
|
|
18
|
+
dbInstance = new Database(dbPath);
|
|
19
|
+
}
|
|
20
|
+
return dbInstance;
|
|
21
|
+
}
|
|
22
|
+
var dbModule = {
|
|
23
|
+
id: "db",
|
|
24
|
+
description: "Database queries (SQLite)",
|
|
25
|
+
functions: [
|
|
26
|
+
{
|
|
27
|
+
name: "dbQuery",
|
|
28
|
+
description: "Run SELECT query, return rows",
|
|
29
|
+
signature: "(sql: string, params?: any[]) => Promise<any[]>",
|
|
30
|
+
fn: async (sql, params) => {
|
|
31
|
+
const db = await getDb();
|
|
32
|
+
const stmt = db.prepare(sql);
|
|
33
|
+
return stmt.all(...params ?? []);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "dbExecute",
|
|
38
|
+
description: "Run INSERT/UPDATE/DELETE",
|
|
39
|
+
signature: "(sql: string, params?: any[]) => Promise<{ changes: number }>",
|
|
40
|
+
fn: async (sql, params) => {
|
|
41
|
+
const db = await getDb();
|
|
42
|
+
const stmt = db.prepare(sql);
|
|
43
|
+
const result = stmt.run(...params ?? []);
|
|
44
|
+
return { changes: result.changes };
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "dbSchema",
|
|
49
|
+
description: "Get database schema",
|
|
50
|
+
signature: "() => Promise<{ tables: Array<{ name: string, columns: any[] }> }>",
|
|
51
|
+
fn: async () => {
|
|
52
|
+
const db = await getDb();
|
|
53
|
+
const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
54
|
+
const result = [];
|
|
55
|
+
for (const table of tables) {
|
|
56
|
+
const columns = db.prepare(`PRAGMA table_info(${table.name})`).all();
|
|
57
|
+
result.push({ name: table.name, columns });
|
|
58
|
+
}
|
|
59
|
+
return { tables: result };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
};
|
|
64
|
+
var db_default = dbModule;
|
|
65
|
+
export {
|
|
66
|
+
db_default as default,
|
|
67
|
+
setDbPath
|
|
68
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// src/catalog/env.ts
|
|
2
|
+
var SECRET_PATTERNS = [/_KEY$/, /_SECRET$/, /_TOKEN$/, /_PASSWORD$/, /^PASSWORD$/, /^SECRET$/];
|
|
3
|
+
var DEFAULT_ALLOW = ["HOME", "USER", "PATH", "LANG", "TERM", "SHELL", "EDITOR", "NODE_ENV"];
|
|
4
|
+
var ALLOW_PREFIXES = ["LMTHING_"];
|
|
5
|
+
var customAllowlist = null;
|
|
6
|
+
function setEnvAllowlist(names) {
|
|
7
|
+
customAllowlist = new Set(names);
|
|
8
|
+
}
|
|
9
|
+
function isAllowed(key) {
|
|
10
|
+
if (customAllowlist?.has(key)) return true;
|
|
11
|
+
if (DEFAULT_ALLOW.includes(key)) return true;
|
|
12
|
+
if (ALLOW_PREFIXES.some((p) => key.startsWith(p))) return true;
|
|
13
|
+
if (SECRET_PATTERNS.some((p) => p.test(key))) return false;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
function getAllowedKeys() {
|
|
17
|
+
return Object.keys(process.env).filter(isAllowed).sort();
|
|
18
|
+
}
|
|
19
|
+
var envModule = {
|
|
20
|
+
id: "env",
|
|
21
|
+
description: "Environment variable access (allowlisted)",
|
|
22
|
+
functions: [
|
|
23
|
+
{
|
|
24
|
+
name: "getEnv",
|
|
25
|
+
description: "Read environment variable (allowlisted only)",
|
|
26
|
+
signature: "(key: string) => string | undefined",
|
|
27
|
+
fn: (key) => {
|
|
28
|
+
const k = key;
|
|
29
|
+
if (!isAllowed(k)) {
|
|
30
|
+
throw new Error(`Environment variable ${k} is not in the allowlist`);
|
|
31
|
+
}
|
|
32
|
+
return process.env[k];
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "listEnv",
|
|
37
|
+
description: "List available (allowlisted) variable names",
|
|
38
|
+
signature: "() => string[]",
|
|
39
|
+
fn: () => getAllowedKeys()
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
};
|
|
43
|
+
var env_default = envModule;
|
|
44
|
+
export {
|
|
45
|
+
env_default as default,
|
|
46
|
+
setEnvAllowlist
|
|
47
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// src/catalog/fetch.ts
|
|
2
|
+
import { writeFile } from "fs/promises";
|
|
3
|
+
var fetchModule = {
|
|
4
|
+
id: "fetch",
|
|
5
|
+
description: "HTTP request utilities",
|
|
6
|
+
functions: [
|
|
7
|
+
{
|
|
8
|
+
name: "httpGet",
|
|
9
|
+
description: "GET request, auto-parses JSON",
|
|
10
|
+
signature: "(url: string, headers?: Record<string, string>) => Promise<any>",
|
|
11
|
+
fn: async (url, headers) => {
|
|
12
|
+
const res = await fetch(url, { headers });
|
|
13
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
14
|
+
if (contentType.includes("json")) return res.json();
|
|
15
|
+
return res.text();
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "httpPost",
|
|
20
|
+
description: "POST with JSON body",
|
|
21
|
+
signature: "(url: string, body: any, headers?: Record<string, string>) => Promise<any>",
|
|
22
|
+
fn: async (url, body, headers) => {
|
|
23
|
+
const res = await fetch(url, {
|
|
24
|
+
method: "POST",
|
|
25
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
26
|
+
body: JSON.stringify(body)
|
|
27
|
+
});
|
|
28
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
29
|
+
if (ct.includes("json")) return res.json();
|
|
30
|
+
return res.text();
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "httpPut",
|
|
35
|
+
description: "PUT with JSON body",
|
|
36
|
+
signature: "(url: string, body: any, headers?: Record<string, string>) => Promise<any>",
|
|
37
|
+
fn: async (url, body, headers) => {
|
|
38
|
+
const res = await fetch(url, {
|
|
39
|
+
method: "PUT",
|
|
40
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
41
|
+
body: JSON.stringify(body)
|
|
42
|
+
});
|
|
43
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
44
|
+
if (ct.includes("json")) return res.json();
|
|
45
|
+
return res.text();
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "httpDelete",
|
|
50
|
+
description: "DELETE request",
|
|
51
|
+
signature: "(url: string, headers?: Record<string, string>) => Promise<any>",
|
|
52
|
+
fn: async (url, headers) => {
|
|
53
|
+
const res = await fetch(url, {
|
|
54
|
+
method: "DELETE",
|
|
55
|
+
headers
|
|
56
|
+
});
|
|
57
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
58
|
+
if (ct.includes("json")) return res.json();
|
|
59
|
+
return res.text();
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "httpRequest",
|
|
64
|
+
description: "Full control HTTP request",
|
|
65
|
+
signature: "(options: RequestOptions) => Promise<{ status: number, headers: Record<string, string>, body: any }>",
|
|
66
|
+
fn: async (options) => {
|
|
67
|
+
const opts = options;
|
|
68
|
+
const res = await fetch(opts.url, {
|
|
69
|
+
method: opts.method ?? "GET",
|
|
70
|
+
headers: opts.headers,
|
|
71
|
+
body: opts.body ? JSON.stringify(opts.body) : void 0,
|
|
72
|
+
signal: opts.timeout ? AbortSignal.timeout(opts.timeout) : void 0
|
|
73
|
+
});
|
|
74
|
+
const ct = res.headers.get("content-type") ?? "";
|
|
75
|
+
const body = ct.includes("json") ? await res.json() : await res.text();
|
|
76
|
+
const headers = {};
|
|
77
|
+
res.headers.forEach((v, k) => {
|
|
78
|
+
headers[k] = v;
|
|
79
|
+
});
|
|
80
|
+
return { status: res.status, headers, body };
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "fetchPage",
|
|
85
|
+
description: "Fetch webpage, extract readable text",
|
|
86
|
+
signature: "(url: string) => Promise<{ title: string, text: string, links: string[] }>",
|
|
87
|
+
fn: async (url) => {
|
|
88
|
+
const res = await fetch(url);
|
|
89
|
+
const html = await res.text();
|
|
90
|
+
const titleMatch = html.match(/<title[^>]*>(.*?)<\/title>/is);
|
|
91
|
+
const title = titleMatch ? titleMatch[1].trim() : "";
|
|
92
|
+
const text = html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
93
|
+
const links = [];
|
|
94
|
+
const linkRegex = /href=["']([^"']+)["']/gi;
|
|
95
|
+
let match;
|
|
96
|
+
while ((match = linkRegex.exec(html)) !== null) {
|
|
97
|
+
links.push(match[1]);
|
|
98
|
+
}
|
|
99
|
+
return { title, text: text.slice(0, 1e4), links: [...new Set(links)] };
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "downloadFile",
|
|
104
|
+
description: "Download to local file",
|
|
105
|
+
signature: "(url: string, dest: string) => Promise<{ size: number }>",
|
|
106
|
+
fn: async (url, dest) => {
|
|
107
|
+
const res = await fetch(url);
|
|
108
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
109
|
+
await writeFile(dest, buffer);
|
|
110
|
+
return { size: buffer.length };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
var fetch_default = fetchModule;
|
|
116
|
+
export {
|
|
117
|
+
fetch_default as default
|
|
118
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/catalog/fs.ts
|
|
2
|
+
import * as nodeFs from "fs/promises";
|
|
3
|
+
import * as nodePath from "path";
|
|
4
|
+
import { glob as nodeGlob } from "fs/promises";
|
|
5
|
+
var workingDir = process.cwd();
|
|
6
|
+
function setWorkingDir(dir) {
|
|
7
|
+
workingDir = dir;
|
|
8
|
+
}
|
|
9
|
+
function safePath(p) {
|
|
10
|
+
const resolved = nodePath.resolve(workingDir, p);
|
|
11
|
+
if (!resolved.startsWith(workingDir)) {
|
|
12
|
+
throw new Error(`Path traversal blocked: ${p} resolves outside working directory`);
|
|
13
|
+
}
|
|
14
|
+
return resolved;
|
|
15
|
+
}
|
|
16
|
+
var fsModule = {
|
|
17
|
+
id: "fs",
|
|
18
|
+
description: "File system operations",
|
|
19
|
+
functions: [
|
|
20
|
+
{
|
|
21
|
+
name: "readFile",
|
|
22
|
+
description: "Read file contents",
|
|
23
|
+
signature: "(path: string, encoding?: string) => Promise<string>",
|
|
24
|
+
fn: async (path, encoding) => {
|
|
25
|
+
return nodeFs.readFile(safePath(path), encoding || "utf-8");
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "writeFile",
|
|
30
|
+
description: "Write content to file",
|
|
31
|
+
signature: "(path: string, content: string) => Promise<void>",
|
|
32
|
+
fn: async (path, content) => {
|
|
33
|
+
await nodeFs.writeFile(safePath(path), content, "utf-8");
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "appendFile",
|
|
38
|
+
description: "Append to file",
|
|
39
|
+
signature: "(path: string, content: string) => Promise<void>",
|
|
40
|
+
fn: async (path, content) => {
|
|
41
|
+
await nodeFs.appendFile(safePath(path), content, "utf-8");
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "listDir",
|
|
46
|
+
description: "List directory entries",
|
|
47
|
+
signature: "(path: string, options?: { recursive?: boolean }) => Promise<string[]>",
|
|
48
|
+
fn: async (path, options) => {
|
|
49
|
+
const opts = options;
|
|
50
|
+
const entries = await nodeFs.readdir(safePath(path), { recursive: opts?.recursive });
|
|
51
|
+
return entries.map(String);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "glob",
|
|
56
|
+
description: "Glob pattern match",
|
|
57
|
+
signature: "(pattern: string, cwd?: string) => Promise<string[]>",
|
|
58
|
+
fn: async (pattern, cwd) => {
|
|
59
|
+
const dir = cwd ? safePath(cwd) : workingDir;
|
|
60
|
+
const results = [];
|
|
61
|
+
for await (const entry of nodeGlob(pattern, { cwd: dir })) {
|
|
62
|
+
results.push(entry);
|
|
63
|
+
}
|
|
64
|
+
return results;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "stat",
|
|
69
|
+
description: "File metadata",
|
|
70
|
+
signature: "(path: string) => Promise<{ size: number, modified: string, isDir: boolean }>",
|
|
71
|
+
fn: async (path) => {
|
|
72
|
+
const stats = await nodeFs.stat(safePath(path));
|
|
73
|
+
return { size: stats.size, modified: stats.mtime.toISOString(), isDir: stats.isDirectory() };
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "exists",
|
|
78
|
+
description: "Check if path exists",
|
|
79
|
+
signature: "(path: string) => Promise<boolean>",
|
|
80
|
+
fn: async (path) => {
|
|
81
|
+
try {
|
|
82
|
+
await nodeFs.access(safePath(path));
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "mkdir",
|
|
91
|
+
description: "Create directory (recursive)",
|
|
92
|
+
signature: "(path: string) => Promise<void>",
|
|
93
|
+
fn: async (path) => {
|
|
94
|
+
await nodeFs.mkdir(safePath(path), { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "remove",
|
|
99
|
+
description: "Delete file or directory",
|
|
100
|
+
signature: "(path: string) => Promise<void>",
|
|
101
|
+
fn: async (path) => {
|
|
102
|
+
await nodeFs.rm(safePath(path), { recursive: true, force: true });
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "copy",
|
|
107
|
+
description: "Copy file or directory",
|
|
108
|
+
signature: "(src: string, dest: string) => Promise<void>",
|
|
109
|
+
fn: async (src, dest) => {
|
|
110
|
+
await nodeFs.cp(safePath(src), safePath(dest), { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "move",
|
|
115
|
+
description: "Move/rename file or directory",
|
|
116
|
+
signature: "(src: string, dest: string) => Promise<void>",
|
|
117
|
+
fn: async (src, dest) => {
|
|
118
|
+
await nodeFs.rename(safePath(src), safePath(dest));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
};
|
|
123
|
+
var fs_default = fsModule;
|
|
124
|
+
export {
|
|
125
|
+
fs_default as default,
|
|
126
|
+
setWorkingDir
|
|
127
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/catalog/image.ts
|
|
2
|
+
async function getSharp() {
|
|
3
|
+
try {
|
|
4
|
+
return (await import("sharp")).default;
|
|
5
|
+
} catch {
|
|
6
|
+
throw new Error("sharp is required for image operations. Install with: pnpm add sharp");
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
var imageModule = {
|
|
10
|
+
id: "image",
|
|
11
|
+
description: "Image operations (requires sharp)",
|
|
12
|
+
functions: [
|
|
13
|
+
{
|
|
14
|
+
name: "imageResize",
|
|
15
|
+
description: "Resize image",
|
|
16
|
+
signature: "(src: string, dest: string, options: { width?: number, height?: number }) => Promise<void>",
|
|
17
|
+
fn: async (src, dest, options) => {
|
|
18
|
+
const sharp = await getSharp();
|
|
19
|
+
const opts = options;
|
|
20
|
+
await sharp(src).resize(opts.width, opts.height).toFile(dest);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "imageCrop",
|
|
25
|
+
description: "Crop image",
|
|
26
|
+
signature: "(src: string, dest: string, region: { left: number, top: number, width: number, height: number }) => Promise<void>",
|
|
27
|
+
fn: async (src, dest, region) => {
|
|
28
|
+
const sharp = await getSharp();
|
|
29
|
+
await sharp(src).extract(region).toFile(dest);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "imageConvert",
|
|
34
|
+
description: "Convert image format",
|
|
35
|
+
signature: "(src: string, dest: string, format: 'png' | 'jpg' | 'webp') => Promise<void>",
|
|
36
|
+
fn: async (src, dest, format) => {
|
|
37
|
+
const sharp = await getSharp();
|
|
38
|
+
await sharp(src).toFormat(format).toFile(dest);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "imageInfo",
|
|
43
|
+
description: "Image metadata",
|
|
44
|
+
signature: "(src: string) => Promise<{ width: number, height: number, format: string, size: number }>",
|
|
45
|
+
fn: async (src) => {
|
|
46
|
+
const sharp = await getSharp();
|
|
47
|
+
const meta = await sharp(src).metadata();
|
|
48
|
+
return { width: meta.width, height: meta.height, format: meta.format, size: meta.size };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
};
|
|
53
|
+
var image_default = imageModule;
|
|
54
|
+
export {
|
|
55
|
+
image_default as default
|
|
56
|
+
};
|