@struktoai/mirage-agents 0.0.3 → 0.0.4
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/README.md +2 -2
- package/dist/chunk-IMI7BLEG.js +142 -0
- package/dist/chunk-IMI7BLEG.js.map +1 -0
- package/dist/chunk-OLFSAPC6.js +139 -0
- package/dist/chunk-OLFSAPC6.js.map +1 -0
- package/dist/chunk-XEQB7N3F.js +79 -0
- package/dist/chunk-XEQB7N3F.js.map +1 -0
- package/dist/claude-agent-sdk/index.d.ts +4 -9
- package/dist/claude-agent-sdk/index.js +19 -110
- package/dist/claude-agent-sdk/index.js.map +1 -1
- package/dist/file-version-Clij-pms.d.ts +22 -0
- package/dist/mcp/index.d.ts +14 -0
- package/dist/mcp/index.js +100 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/openai/index.d.ts +13 -2
- package/dist/openai/index.js +81 -1
- package/dist/openai/index.js.map +1 -1
- package/dist/opencode/index.d.ts +8 -18
- package/dist/opencode/index.js +66 -67
- package/dist/opencode/index.js.map +1 -1
- package/dist/pi/index.d.ts +12 -7
- package/dist/pi/index.js +65 -36
- package/dist/pi/index.js.map +1 -1
- package/dist/tool-descriptions-7CgIovZc.d.ts +33 -0
- package/dist/vercel/index.d.ts +3 -67
- package/dist/vercel/index.js +28 -102
- package/dist/vercel/index.js.map +1 -1
- package/package.json +32 -25
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ await ws.execute('cat /s3/events/2026-05-06.parquet | jq .user')
|
|
|
60
60
|
- **Around 50 built-in backends:** RAM, Disk, Redis, S3 / R2 / OCI / Supabase / GCS, Gmail / GDrive / GDocs / GSheets / GSlides, GitHub / Linear / Notion / Trello, Slack / Discord / Email, MongoDB / Postgres / LanceDB, SSH, and more, mounted side-by-side under a single root.
|
|
61
61
|
- **Portable workspaces:** clone, snapshot, and version a workspace; agent runs move between machines without restarting or reconfiguring the system.
|
|
62
62
|
- **Embeddable:** the Python and TypeScript SDKs run in-process inside FastAPI, Express, browser apps, or any async runtime; no separate process required.
|
|
63
|
-
- **Agent integrations:** OpenAI Agents SDK, Vercel AI SDK, LangChain, Pydantic AI, CAMEL, and OpenHands via the SDKs; coding agents
|
|
63
|
+
- **Agent integrations:** OpenAI Agents SDK, Vercel AI SDK, LangChain, Pydantic AI, CAMEL, and OpenHands via the SDKs; coding agents through native adapters, installable plugins, MCP, or FUSE.
|
|
64
64
|
|
|
65
65
|
## Architecture
|
|
66
66
|
|
|
@@ -159,7 +159,7 @@ Mirage plugs into agent frameworks as a sandbox or tool layer. POSIX operations
|
|
|
159
159
|
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
160
160
|
| Python | [OpenAI Agents SDK](https://docs.mirage.strukto.ai/python/agents/openai-agents), [LangChain](https://docs.mirage.strukto.ai/python/agents/langchain), [Pydantic AI](https://docs.mirage.strukto.ai/python/agents/pydantic-ai), [CAMEL](https://docs.mirage.strukto.ai/python/agents/camel), [OpenHands](https://docs.mirage.strukto.ai/python/agents/openhands), [Agno](https://docs.mirage.strukto.ai/python/agents/agno) |
|
|
161
161
|
| TypeScript | [Vercel AI SDK](https://docs.mirage.strukto.ai/typescript/agents/vercel), [OpenAI Agents SDK](https://docs.mirage.strukto.ai/typescript/agents/openai), [LangChain](https://docs.mirage.strukto.ai/typescript/agents/langchain), [Mastra](https://docs.mirage.strukto.ai/typescript/agents/mastra) |
|
|
162
|
-
| Coding agents | [Claude Code](https://docs.mirage.strukto.ai/python/agents/claude-code), [Codex](https://docs.mirage.strukto.ai/
|
|
162
|
+
| Coding agents | [Claude Code](https://docs.mirage.strukto.ai/python/agents/claude-code), [Codex](https://docs.mirage.strukto.ai/typescript/agents/codex), [Grok Build](https://docs.mirage.strukto.ai/typescript/agents/grok-build), [OpenCode](https://docs.mirage.strukto.ai/typescript/agents/opencode), [Pi](https://docs.mirage.strukto.ai/typescript/agents/pi) |
|
|
163
163
|
|
|
164
164
|
## Cache
|
|
165
165
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/read-file.ts
|
|
2
|
+
import { detectFileType, FileType as FileType2 } from "@struktoai/mirage-core";
|
|
3
|
+
|
|
4
|
+
// src/read-file/constants.ts
|
|
5
|
+
import { FileType } from "@struktoai/mirage-core";
|
|
6
|
+
var READ_FILE_MIME = Object.freeze({
|
|
7
|
+
BINARY: "application/octet-stream",
|
|
8
|
+
CSV: "text/csv",
|
|
9
|
+
HTML: "text/html",
|
|
10
|
+
IMAGE_GIF: "image/gif",
|
|
11
|
+
IMAGE_JPEG: "image/jpeg",
|
|
12
|
+
IMAGE_PNG: "image/png",
|
|
13
|
+
IMAGE_WEBP: "image/webp",
|
|
14
|
+
JSON: "application/json",
|
|
15
|
+
MARKDOWN: "text/markdown",
|
|
16
|
+
PDF: "application/pdf",
|
|
17
|
+
PLAIN_TEXT: "text/plain",
|
|
18
|
+
SVG: "image/svg+xml"
|
|
19
|
+
});
|
|
20
|
+
var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
21
|
+
"txt",
|
|
22
|
+
"yaml",
|
|
23
|
+
"yml",
|
|
24
|
+
"tsv",
|
|
25
|
+
"xml",
|
|
26
|
+
"js",
|
|
27
|
+
"mjs",
|
|
28
|
+
"cjs",
|
|
29
|
+
"ts",
|
|
30
|
+
"tsx",
|
|
31
|
+
"jsx",
|
|
32
|
+
"py",
|
|
33
|
+
"rb",
|
|
34
|
+
"rs",
|
|
35
|
+
"go",
|
|
36
|
+
"java",
|
|
37
|
+
"c",
|
|
38
|
+
"cpp",
|
|
39
|
+
"h",
|
|
40
|
+
"hpp",
|
|
41
|
+
"sh",
|
|
42
|
+
"bash",
|
|
43
|
+
"zsh",
|
|
44
|
+
"sql",
|
|
45
|
+
"log",
|
|
46
|
+
"env",
|
|
47
|
+
"ini",
|
|
48
|
+
"toml",
|
|
49
|
+
"conf",
|
|
50
|
+
"cfg"
|
|
51
|
+
]);
|
|
52
|
+
var MIME_FOR_EXTENSION = Object.freeze({
|
|
53
|
+
json: READ_FILE_MIME.JSON,
|
|
54
|
+
jsonl: READ_FILE_MIME.JSON,
|
|
55
|
+
csv: READ_FILE_MIME.CSV,
|
|
56
|
+
html: READ_FILE_MIME.HTML,
|
|
57
|
+
htm: READ_FILE_MIME.HTML,
|
|
58
|
+
md: READ_FILE_MIME.MARKDOWN,
|
|
59
|
+
svg: READ_FILE_MIME.SVG,
|
|
60
|
+
png: READ_FILE_MIME.IMAGE_PNG,
|
|
61
|
+
jpg: READ_FILE_MIME.IMAGE_JPEG,
|
|
62
|
+
jpeg: READ_FILE_MIME.IMAGE_JPEG,
|
|
63
|
+
gif: READ_FILE_MIME.IMAGE_GIF,
|
|
64
|
+
webp: READ_FILE_MIME.IMAGE_WEBP,
|
|
65
|
+
pdf: READ_FILE_MIME.PDF
|
|
66
|
+
});
|
|
67
|
+
var MIME_FOR_FILE_TYPE = Object.freeze({
|
|
68
|
+
[FileType.JSON]: READ_FILE_MIME.JSON,
|
|
69
|
+
[FileType.CSV]: READ_FILE_MIME.CSV,
|
|
70
|
+
[FileType.TEXT]: READ_FILE_MIME.PLAIN_TEXT,
|
|
71
|
+
[FileType.IMAGE_PNG]: READ_FILE_MIME.IMAGE_PNG,
|
|
72
|
+
[FileType.IMAGE_JPEG]: READ_FILE_MIME.IMAGE_JPEG,
|
|
73
|
+
[FileType.IMAGE_GIF]: READ_FILE_MIME.IMAGE_GIF,
|
|
74
|
+
[FileType.PDF]: READ_FILE_MIME.PDF
|
|
75
|
+
});
|
|
76
|
+
var MODEL_IMAGE_MIMES = /* @__PURE__ */ new Set([
|
|
77
|
+
READ_FILE_MIME.IMAGE_JPEG,
|
|
78
|
+
READ_FILE_MIME.IMAGE_PNG,
|
|
79
|
+
READ_FILE_MIME.IMAGE_GIF,
|
|
80
|
+
READ_FILE_MIME.IMAGE_WEBP
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
// src/read-file.ts
|
|
84
|
+
function extOf(path) {
|
|
85
|
+
const dot = path.lastIndexOf(".");
|
|
86
|
+
const slash = path.lastIndexOf("/");
|
|
87
|
+
if (dot < 0 || dot < slash) return "";
|
|
88
|
+
return path.slice(dot + 1).toLowerCase();
|
|
89
|
+
}
|
|
90
|
+
function filenameOf(path) {
|
|
91
|
+
const slash = path.lastIndexOf("/");
|
|
92
|
+
const filename = slash < 0 ? path : path.slice(slash + 1);
|
|
93
|
+
return filename === "" ? "file" : filename;
|
|
94
|
+
}
|
|
95
|
+
function mimeForExtension(path) {
|
|
96
|
+
const ext = extOf(path);
|
|
97
|
+
return MIME_FOR_EXTENSION[ext] ?? (TEXT_FILE_EXTENSIONS.has(ext) ? READ_FILE_MIME.PLAIN_TEXT : void 0);
|
|
98
|
+
}
|
|
99
|
+
function mimeForDetectedType(type) {
|
|
100
|
+
return MIME_FOR_FILE_TYPE[type] ?? READ_FILE_MIME.BINARY;
|
|
101
|
+
}
|
|
102
|
+
function mimeFor(path, bytes, stat) {
|
|
103
|
+
const extensionMime = mimeForExtension(path);
|
|
104
|
+
if (extensionMime !== void 0) return extensionMime;
|
|
105
|
+
if (extOf(path) !== "") return READ_FILE_MIME.BINARY;
|
|
106
|
+
return mimeForDetectedType(detectFileType(bytes, stat));
|
|
107
|
+
}
|
|
108
|
+
function isTextMime(mimeType) {
|
|
109
|
+
return mimeType.startsWith("text/") || mimeType === READ_FILE_MIME.JSON || mimeType === READ_FILE_MIME.SVG;
|
|
110
|
+
}
|
|
111
|
+
async function readWorkspaceFile(ws, path, reader) {
|
|
112
|
+
const stat = await ws.fs.stat(path);
|
|
113
|
+
if (stat.type === FileType2.DIRECTORY) {
|
|
114
|
+
throw new Error(`Cannot read directory as a file: ${path}`);
|
|
115
|
+
}
|
|
116
|
+
const data = reader === void 0 ? await ws.fs.readFile(path, { raw: true }) : await reader(path);
|
|
117
|
+
const mimeType = mimeFor(path, data, stat);
|
|
118
|
+
const base = { path, mimeType, bytes: data.byteLength };
|
|
119
|
+
if (isTextMime(mimeType)) {
|
|
120
|
+
return {
|
|
121
|
+
...base,
|
|
122
|
+
kind: "text",
|
|
123
|
+
content: new TextDecoder("utf-8", { fatal: false }).decode(data)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (MODEL_IMAGE_MIMES.has(mimeType)) {
|
|
127
|
+
return { ...base, kind: "image", data };
|
|
128
|
+
}
|
|
129
|
+
if (mimeType === READ_FILE_MIME.PDF) {
|
|
130
|
+
return { ...base, kind: "file", data, filename: filenameOf(path) };
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
...base,
|
|
134
|
+
kind: "binary",
|
|
135
|
+
note: `Binary file ${path} (${mimeType}, ${String(data.byteLength)} bytes). Use the execute tool with shell commands (head, file, wc, od) to inspect.`
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export {
|
|
140
|
+
readWorkspaceFile
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=chunk-IMI7BLEG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/read-file.ts","../src/read-file/constants.ts"],"sourcesContent":["import { detectFileType, FileType, type FileStat, type Workspace } from '@struktoai/mirage-core'\nimport {\n MIME_FOR_EXTENSION,\n MIME_FOR_FILE_TYPE,\n MODEL_IMAGE_MIMES,\n READ_FILE_MIME,\n TEXT_FILE_EXTENSIONS,\n type ReadFileMime,\n} from './read-file/constants.ts'\n\ninterface WorkspaceFileBase {\n path: string\n mimeType: ReadFileMime\n bytes: number\n}\n\nexport type WorkspaceFileReadResult =\n | (WorkspaceFileBase & { kind: 'text'; content: string })\n | (WorkspaceFileBase & { kind: 'image'; data: Uint8Array })\n | (WorkspaceFileBase & { kind: 'file'; data: Uint8Array; filename: string })\n | (WorkspaceFileBase & { kind: 'binary'; note: string })\n\nexport type WorkspaceFileReader = (path: string) => Promise<Uint8Array>\n\nfunction extOf(path: string): string {\n const dot = path.lastIndexOf('.')\n const slash = path.lastIndexOf('/')\n if (dot < 0 || dot < slash) return ''\n return path.slice(dot + 1).toLowerCase()\n}\n\nfunction filenameOf(path: string): string {\n const slash = path.lastIndexOf('/')\n const filename = slash < 0 ? path : path.slice(slash + 1)\n return filename === '' ? 'file' : filename\n}\n\nfunction mimeForExtension(path: string): ReadFileMime | undefined {\n const ext = extOf(path)\n return (\n MIME_FOR_EXTENSION[ext] ??\n (TEXT_FILE_EXTENSIONS.has(ext) ? READ_FILE_MIME.PLAIN_TEXT : undefined)\n )\n}\n\nfunction mimeForDetectedType(type: FileType): ReadFileMime {\n return MIME_FOR_FILE_TYPE[type] ?? READ_FILE_MIME.BINARY\n}\n\nfunction mimeFor(path: string, bytes: Uint8Array, stat: FileStat): ReadFileMime {\n const extensionMime = mimeForExtension(path)\n if (extensionMime !== undefined) return extensionMime\n if (extOf(path) !== '') return READ_FILE_MIME.BINARY\n return mimeForDetectedType(detectFileType(bytes, stat))\n}\n\nfunction isTextMime(mimeType: ReadFileMime): boolean {\n return (\n mimeType.startsWith('text/') ||\n mimeType === READ_FILE_MIME.JSON ||\n mimeType === READ_FILE_MIME.SVG\n )\n}\n\nexport async function readWorkspaceFile(\n ws: Workspace,\n path: string,\n reader?: WorkspaceFileReader,\n): Promise<WorkspaceFileReadResult> {\n const stat = await ws.fs.stat(path)\n if (stat.type === FileType.DIRECTORY) {\n throw new Error(`Cannot read directory as a file: ${path}`)\n }\n const data = reader === undefined ? await ws.fs.readFile(path, { raw: true }) : await reader(path)\n const mimeType = mimeFor(path, data, stat)\n const base = { path, mimeType, bytes: data.byteLength }\n\n if (isTextMime(mimeType)) {\n return {\n ...base,\n kind: 'text',\n content: new TextDecoder('utf-8', { fatal: false }).decode(data),\n }\n }\n if (MODEL_IMAGE_MIMES.has(mimeType)) {\n return { ...base, kind: 'image', data }\n }\n if (mimeType === READ_FILE_MIME.PDF) {\n return { ...base, kind: 'file', data, filename: filenameOf(path) }\n }\n return {\n ...base,\n kind: 'binary',\n note:\n `Binary file ${path} (${mimeType}, ${String(data.byteLength)} bytes). ` +\n 'Use the execute tool with shell commands (head, file, wc, od) to inspect.',\n }\n}\n","import { FileType, type FileType as MirageFileType } from '@struktoai/mirage-core'\n\nexport const READ_FILE_MIME = Object.freeze({\n BINARY: 'application/octet-stream',\n CSV: 'text/csv',\n HTML: 'text/html',\n IMAGE_GIF: 'image/gif',\n IMAGE_JPEG: 'image/jpeg',\n IMAGE_PNG: 'image/png',\n IMAGE_WEBP: 'image/webp',\n JSON: 'application/json',\n MARKDOWN: 'text/markdown',\n PDF: 'application/pdf',\n PLAIN_TEXT: 'text/plain',\n SVG: 'image/svg+xml',\n} as const)\n\nexport type ReadFileMime = (typeof READ_FILE_MIME)[keyof typeof READ_FILE_MIME]\n\nexport const TEXT_FILE_EXTENSIONS: ReadonlySet<string> = new Set([\n 'txt',\n 'yaml',\n 'yml',\n 'tsv',\n 'xml',\n 'js',\n 'mjs',\n 'cjs',\n 'ts',\n 'tsx',\n 'jsx',\n 'py',\n 'rb',\n 'rs',\n 'go',\n 'java',\n 'c',\n 'cpp',\n 'h',\n 'hpp',\n 'sh',\n 'bash',\n 'zsh',\n 'sql',\n 'log',\n 'env',\n 'ini',\n 'toml',\n 'conf',\n 'cfg',\n])\n\nexport const MIME_FOR_EXTENSION: Readonly<Record<string, ReadFileMime>> = Object.freeze({\n json: READ_FILE_MIME.JSON,\n jsonl: READ_FILE_MIME.JSON,\n csv: READ_FILE_MIME.CSV,\n html: READ_FILE_MIME.HTML,\n htm: READ_FILE_MIME.HTML,\n md: READ_FILE_MIME.MARKDOWN,\n svg: READ_FILE_MIME.SVG,\n png: READ_FILE_MIME.IMAGE_PNG,\n jpg: READ_FILE_MIME.IMAGE_JPEG,\n jpeg: READ_FILE_MIME.IMAGE_JPEG,\n gif: READ_FILE_MIME.IMAGE_GIF,\n webp: READ_FILE_MIME.IMAGE_WEBP,\n pdf: READ_FILE_MIME.PDF,\n})\n\nexport const MIME_FOR_FILE_TYPE: Readonly<Partial<Record<MirageFileType, ReadFileMime>>> =\n Object.freeze({\n [FileType.JSON]: READ_FILE_MIME.JSON,\n [FileType.CSV]: READ_FILE_MIME.CSV,\n [FileType.TEXT]: READ_FILE_MIME.PLAIN_TEXT,\n [FileType.IMAGE_PNG]: READ_FILE_MIME.IMAGE_PNG,\n [FileType.IMAGE_JPEG]: READ_FILE_MIME.IMAGE_JPEG,\n [FileType.IMAGE_GIF]: READ_FILE_MIME.IMAGE_GIF,\n [FileType.PDF]: READ_FILE_MIME.PDF,\n })\n\nexport const MODEL_IMAGE_MIMES: ReadonlySet<ReadFileMime> = new Set([\n READ_FILE_MIME.IMAGE_JPEG,\n READ_FILE_MIME.IMAGE_PNG,\n READ_FILE_MIME.IMAGE_GIF,\n READ_FILE_MIME.IMAGE_WEBP,\n])\n"],"mappings":"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","names":["FileType","FileType"]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FileVersionTracker,
|
|
3
|
+
StaleMirageFileError
|
|
4
|
+
} from "./chunk-XEQB7N3F.js";
|
|
5
|
+
|
|
6
|
+
// src/tool-operations.ts
|
|
7
|
+
import { gnuDirname } from "@struktoai/mirage-core";
|
|
8
|
+
|
|
9
|
+
// src/io-text.ts
|
|
10
|
+
function decode(value) {
|
|
11
|
+
if (value === null || value === void 0) return "";
|
|
12
|
+
return new TextDecoder("utf-8").decode(value);
|
|
13
|
+
}
|
|
14
|
+
function ioToStr(io) {
|
|
15
|
+
const stdout = io.stdoutText;
|
|
16
|
+
const stderr = io.stderrText;
|
|
17
|
+
if (stderr) return stdout ? `${stdout}
|
|
18
|
+
${stderr}` : stderr;
|
|
19
|
+
return stdout;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/tool-operations.ts
|
|
23
|
+
function textResult(text) {
|
|
24
|
+
return { content: [{ type: "text", text }] };
|
|
25
|
+
}
|
|
26
|
+
function errorResult(text) {
|
|
27
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
28
|
+
}
|
|
29
|
+
function ioResult(io) {
|
|
30
|
+
const result = textResult(ioToStr(io));
|
|
31
|
+
if (io.exitCode !== 0) result.isError = true;
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function shQuote(value) {
|
|
35
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
36
|
+
}
|
|
37
|
+
async function ensureParents(ws, path) {
|
|
38
|
+
const parent = gnuDirname(path);
|
|
39
|
+
if (parent === "/" || parent === "" || parent === ".") return;
|
|
40
|
+
if (await ws.fs.exists(parent)) return;
|
|
41
|
+
await ensureParents(ws, parent);
|
|
42
|
+
try {
|
|
43
|
+
await ws.fs.mkdir(parent);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
if (!await ws.fs.exists(parent)) throw err;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function errorMessage(error) {
|
|
49
|
+
return error instanceof Error ? error.message : String(error);
|
|
50
|
+
}
|
|
51
|
+
var MirageToolOperations = class {
|
|
52
|
+
constructor(ws, options = {}) {
|
|
53
|
+
this.ws = ws;
|
|
54
|
+
this.versions = new FileVersionTracker(ws, options.staleWriteProtection ?? true);
|
|
55
|
+
}
|
|
56
|
+
ws;
|
|
57
|
+
versions;
|
|
58
|
+
async execute(command) {
|
|
59
|
+
return ioResult(await this.ws.execute(command));
|
|
60
|
+
}
|
|
61
|
+
async read(path, offset = 0, limit = 2e3) {
|
|
62
|
+
let data;
|
|
63
|
+
try {
|
|
64
|
+
data = await this.versions.read(path);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (!await this.ws.fs.exists(path)) {
|
|
67
|
+
return errorResult(`Error: file '${path}' not found`);
|
|
68
|
+
}
|
|
69
|
+
return errorResult(`Error: ${errorMessage(err)}`);
|
|
70
|
+
}
|
|
71
|
+
const text = decode(data);
|
|
72
|
+
const raw = text.length === 0 ? [] : text.split(/(?<=\n)/);
|
|
73
|
+
const lines = raw.length > 0 && raw[raw.length - 1] === "" ? raw.slice(0, -1) : raw;
|
|
74
|
+
const sliced = lines.slice(offset, offset + limit);
|
|
75
|
+
const numbered = sliced.map((line, i) => `${String(i + offset + 1).padStart(6)} ${line}`);
|
|
76
|
+
return textResult(numbered.join(""));
|
|
77
|
+
}
|
|
78
|
+
async write(path, content) {
|
|
79
|
+
if (await this.ws.fs.exists(path)) {
|
|
80
|
+
return errorResult(`Error: file '${path}' already exists`);
|
|
81
|
+
}
|
|
82
|
+
await ensureParents(this.ws, path);
|
|
83
|
+
await this.versions.write(path, content);
|
|
84
|
+
return textResult(`Written: ${path}`);
|
|
85
|
+
}
|
|
86
|
+
async edit(path, oldString, newString, replaceAll = false) {
|
|
87
|
+
let content;
|
|
88
|
+
try {
|
|
89
|
+
content = (await this.versions.readForEdit(path)).toString("utf8");
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (err instanceof StaleMirageFileError) return errorResult(`Error: ${err.message}`);
|
|
92
|
+
if (!await this.ws.fs.exists(path)) {
|
|
93
|
+
return errorResult(`Error: file '${path}' not found`);
|
|
94
|
+
}
|
|
95
|
+
return errorResult(`Error: ${errorMessage(err)}`);
|
|
96
|
+
}
|
|
97
|
+
const count = content.split(oldString).length - 1;
|
|
98
|
+
if (count === 0) {
|
|
99
|
+
return errorResult(`Error: string not found in file: '${oldString}'`);
|
|
100
|
+
}
|
|
101
|
+
if (count > 1 && !replaceAll) {
|
|
102
|
+
return errorResult(`Error: string appears ${String(count)} times. Pass replace_all=true`);
|
|
103
|
+
}
|
|
104
|
+
const newContent = replaceAll ? content.split(oldString).join(newString) : content.replace(oldString, newString);
|
|
105
|
+
try {
|
|
106
|
+
await this.versions.writeEdit(path, newContent);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return errorResult(`Error: ${errorMessage(err)}`);
|
|
109
|
+
}
|
|
110
|
+
const occurrences = replaceAll ? count : 1;
|
|
111
|
+
return textResult(`Edited: ${path} (${String(occurrences)} occurrence(s))`);
|
|
112
|
+
}
|
|
113
|
+
async ls(path) {
|
|
114
|
+
return ioResult(await this.ws.execute(`ls ${shQuote(path)}`));
|
|
115
|
+
}
|
|
116
|
+
async grep(pattern, path) {
|
|
117
|
+
const io = await this.ws.execute(`grep -rn ${shQuote(pattern)} ${shQuote(path)}`);
|
|
118
|
+
return textResult(ioToStr(io));
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// src/tool-descriptions.ts
|
|
123
|
+
var EXECUTE_DESCRIPTION = "Run a shell-style command on the Mirage virtual filesystem. Supports cat, grep, find, head, tail, ls, wc, sort, uniq, tee, pipe, and any other Unix command on mounted resources (S3, disk, RAM, etc.). Also supports reading structured files: cat on .parquet/.orc/.csv returns a table.";
|
|
124
|
+
var READ_DESCRIPTION = "Read the contents of a file on the Mirage virtual filesystem. Returns line-numbered text. Optionally pass 'offset' (default 0) to start at a given line and 'limit' (default 2000) to cap the number of lines returned.";
|
|
125
|
+
var WRITE_DESCRIPTION = "Write content to a new file on the Mirage virtual filesystem. Fails if the file already exists; use edit to modify an existing file.";
|
|
126
|
+
var EDIT_DESCRIPTION = "Replace a string in an existing file on the Mirage virtual filesystem. Fails if the file changed since it was last read, old_string is not found, or old_string appears more than once. Pass replace_all=true (default false) to replace every occurrence.";
|
|
127
|
+
var LS_DESCRIPTION = "List files and directories at the given path on the Mirage virtual filesystem.";
|
|
128
|
+
var GREP_DESCRIPTION = "Search for a pattern in files on the Mirage virtual filesystem. Supports regex. Searches recursively under path.";
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
MirageToolOperations,
|
|
132
|
+
EXECUTE_DESCRIPTION,
|
|
133
|
+
READ_DESCRIPTION,
|
|
134
|
+
WRITE_DESCRIPTION,
|
|
135
|
+
EDIT_DESCRIPTION,
|
|
136
|
+
LS_DESCRIPTION,
|
|
137
|
+
GREP_DESCRIPTION
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=chunk-OLFSAPC6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tool-operations.ts","../src/io-text.ts","../src/tool-descriptions.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { ExecuteResult, Workspace } from '@struktoai/mirage-core'\nimport { gnuDirname } from '@struktoai/mirage-core'\nimport { FileVersionTracker, StaleMirageFileError } from './file-version.ts'\nimport { decode, ioToStr } from './io-text.ts'\n\nexport interface ToolResult {\n [key: string]: unknown\n content: { type: 'text'; text: string }[]\n isError?: boolean\n}\n\nexport interface MirageToolOperationsOptions {\n staleWriteProtection?: boolean\n}\n\nfunction textResult(text: string): ToolResult {\n return { content: [{ type: 'text', text }] }\n}\n\nfunction errorResult(text: string): ToolResult {\n return { content: [{ type: 'text', text }], isError: true }\n}\n\nfunction ioResult(io: ExecuteResult): ToolResult {\n const result = textResult(ioToStr(io))\n if (io.exitCode !== 0) result.isError = true\n return result\n}\n\nfunction shQuote(value: string): string {\n return `'${value.replace(/'/g, \"'\\\\''\")}'`\n}\n\nasync function ensureParents(ws: Workspace, path: string): Promise<void> {\n const parent = gnuDirname(path)\n if (parent === '/' || parent === '' || parent === '.') return\n if (await ws.fs.exists(parent)) return\n await ensureParents(ws, parent)\n try {\n await ws.fs.mkdir(parent)\n } catch (err) {\n if (!(await ws.fs.exists(parent))) throw err\n }\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\nexport class MirageToolOperations {\n private readonly versions: FileVersionTracker\n\n constructor(\n private readonly ws: Workspace,\n options: MirageToolOperationsOptions = {},\n ) {\n this.versions = new FileVersionTracker(ws, options.staleWriteProtection ?? true)\n }\n\n async execute(command: string): Promise<ToolResult> {\n return ioResult(await this.ws.execute(command))\n }\n\n async read(path: string, offset = 0, limit = 2000): Promise<ToolResult> {\n let data: Uint8Array\n try {\n data = await this.versions.read(path)\n } catch (err) {\n if (!(await this.ws.fs.exists(path))) {\n return errorResult(`Error: file '${path}' not found`)\n }\n return errorResult(`Error: ${errorMessage(err)}`)\n }\n const text = decode(data)\n const raw = text.length === 0 ? [] : text.split(/(?<=\\n)/)\n const lines = raw.length > 0 && raw[raw.length - 1] === '' ? raw.slice(0, -1) : raw\n const sliced = lines.slice(offset, offset + limit)\n const numbered = sliced.map((line, i) => `${String(i + offset + 1).padStart(6)}\\t${line}`)\n return textResult(numbered.join(''))\n }\n\n async write(path: string, content: string): Promise<ToolResult> {\n if (await this.ws.fs.exists(path)) {\n return errorResult(`Error: file '${path}' already exists`)\n }\n await ensureParents(this.ws, path)\n await this.versions.write(path, content)\n return textResult(`Written: ${path}`)\n }\n\n async edit(\n path: string,\n oldString: string,\n newString: string,\n replaceAll = false,\n ): Promise<ToolResult> {\n let content: string\n try {\n content = (await this.versions.readForEdit(path)).toString('utf8')\n } catch (err) {\n if (err instanceof StaleMirageFileError) return errorResult(`Error: ${err.message}`)\n if (!(await this.ws.fs.exists(path))) {\n return errorResult(`Error: file '${path}' not found`)\n }\n return errorResult(`Error: ${errorMessage(err)}`)\n }\n const count = content.split(oldString).length - 1\n if (count === 0) {\n return errorResult(`Error: string not found in file: '${oldString}'`)\n }\n if (count > 1 && !replaceAll) {\n return errorResult(`Error: string appears ${String(count)} times. Pass replace_all=true`)\n }\n const newContent = replaceAll\n ? content.split(oldString).join(newString)\n : content.replace(oldString, newString)\n try {\n await this.versions.writeEdit(path, newContent)\n } catch (err) {\n return errorResult(`Error: ${errorMessage(err)}`)\n }\n const occurrences = replaceAll ? count : 1\n return textResult(`Edited: ${path} (${String(occurrences)} occurrence(s))`)\n }\n\n async ls(path: string): Promise<ToolResult> {\n return ioResult(await this.ws.execute(`ls ${shQuote(path)}`))\n }\n\n async grep(pattern: string, path: string): Promise<ToolResult> {\n const io = await this.ws.execute(`grep -rn ${shQuote(pattern)} ${shQuote(path)}`)\n return textResult(ioToStr(io))\n }\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport type { ExecuteResult } from '@struktoai/mirage-core'\n\nexport function decode(value: Uint8Array | null | undefined): string {\n if (value === null || value === undefined) return ''\n return new TextDecoder('utf-8').decode(value)\n}\n\nexport function ioToStr(io: ExecuteResult): string {\n const stdout = io.stdoutText\n const stderr = io.stderrText\n if (stderr) return stdout ? `${stdout}\\n${stderr}` : stderr\n return stdout\n}\n","// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nexport const EXECUTE_DESCRIPTION =\n 'Run a shell-style command on the Mirage virtual filesystem. ' +\n 'Supports cat, grep, find, head, tail, ls, wc, sort, uniq, tee, pipe, ' +\n 'and any other Unix command on mounted resources (S3, disk, RAM, etc.). ' +\n 'Also supports reading structured files: cat on .parquet/.orc/.csv returns a table.'\n\nexport const READ_DESCRIPTION =\n 'Read the contents of a file on the Mirage virtual filesystem. ' +\n 'Returns line-numbered text. ' +\n \"Optionally pass 'offset' (default 0) to start at a given line \" +\n \"and 'limit' (default 2000) to cap the number of lines returned.\"\n\nexport const WRITE_DESCRIPTION =\n 'Write content to a new file on the Mirage virtual filesystem. ' +\n 'Fails if the file already exists; use edit to modify an existing file.'\n\nexport const EDIT_DESCRIPTION =\n 'Replace a string in an existing file on the Mirage virtual filesystem. ' +\n 'Fails if the file changed since it was last read, old_string is not found, ' +\n 'or old_string appears more than once. ' +\n 'Pass replace_all=true (default false) to replace every occurrence.'\n\nexport const LS_DESCRIPTION =\n 'List files and directories at the given path on the Mirage virtual filesystem.'\n\nexport const GREP_DESCRIPTION =\n 'Search for a pattern in files on the Mirage virtual filesystem. ' +\n 'Supports regex. Searches recursively under path.'\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/file-version.ts
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
var StaleMirageFileError = class extends Error {
|
|
4
|
+
path;
|
|
5
|
+
constructor(path) {
|
|
6
|
+
super(`File changed since it was last read: ${path}. Read the file again before modifying it.`);
|
|
7
|
+
this.name = "StaleMirageFileError";
|
|
8
|
+
this.path = path;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function fingerprint(content) {
|
|
12
|
+
return createHash("sha256").update(content).digest("base64url");
|
|
13
|
+
}
|
|
14
|
+
async function readBuffer(ws, path) {
|
|
15
|
+
const bytes = await ws.fs.readFile(path, { raw: true });
|
|
16
|
+
return Buffer.from(bytes);
|
|
17
|
+
}
|
|
18
|
+
var FileVersionTracker = class {
|
|
19
|
+
constructor(ws, enabled = true) {
|
|
20
|
+
this.ws = ws;
|
|
21
|
+
this.enabled = enabled;
|
|
22
|
+
}
|
|
23
|
+
ws;
|
|
24
|
+
enabled;
|
|
25
|
+
readVersions = /* @__PURE__ */ new Map();
|
|
26
|
+
editVersions = /* @__PURE__ */ new Map();
|
|
27
|
+
async currentVersion(path) {
|
|
28
|
+
if (!await this.ws.fs.exists(path)) return null;
|
|
29
|
+
return fingerprint(await readBuffer(this.ws, path));
|
|
30
|
+
}
|
|
31
|
+
async assertVersion(path, expected) {
|
|
32
|
+
if (await this.currentVersion(path) !== expected) {
|
|
33
|
+
throw new StaleMirageFileError(path);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
recordWrite(path, content) {
|
|
37
|
+
if (!this.enabled) return;
|
|
38
|
+
this.readVersions.set(path, fingerprint(content));
|
|
39
|
+
this.editVersions.delete(path);
|
|
40
|
+
}
|
|
41
|
+
async read(path) {
|
|
42
|
+
const content = await readBuffer(this.ws, path);
|
|
43
|
+
if (this.enabled) this.readVersions.set(path, fingerprint(content));
|
|
44
|
+
return content;
|
|
45
|
+
}
|
|
46
|
+
async readForEdit(path) {
|
|
47
|
+
const content = await readBuffer(this.ws, path);
|
|
48
|
+
if (!this.enabled) return content;
|
|
49
|
+
const version = fingerprint(content);
|
|
50
|
+
const readVersion = this.readVersions.get(path);
|
|
51
|
+
if (readVersion !== void 0 && readVersion !== version) {
|
|
52
|
+
throw new StaleMirageFileError(path);
|
|
53
|
+
}
|
|
54
|
+
this.editVersions.set(path, version);
|
|
55
|
+
return content;
|
|
56
|
+
}
|
|
57
|
+
async write(path, content) {
|
|
58
|
+
if (this.enabled) {
|
|
59
|
+
const readVersion = this.readVersions.get(path);
|
|
60
|
+
if (readVersion !== void 0) await this.assertVersion(path, readVersion);
|
|
61
|
+
}
|
|
62
|
+
await this.ws.fs.writeFile(path, content);
|
|
63
|
+
this.recordWrite(path, content);
|
|
64
|
+
}
|
|
65
|
+
async writeEdit(path, content) {
|
|
66
|
+
if (this.enabled) {
|
|
67
|
+
const editVersion = this.editVersions.get(path);
|
|
68
|
+
if (editVersion !== void 0) await this.assertVersion(path, editVersion);
|
|
69
|
+
}
|
|
70
|
+
await this.ws.fs.writeFile(path, content);
|
|
71
|
+
this.recordWrite(path, content);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export {
|
|
76
|
+
StaleMirageFileError,
|
|
77
|
+
FileVersionTracker
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=chunk-XEQB7N3F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/file-version.ts"],"sourcesContent":["// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========\n\nimport { createHash } from 'node:crypto'\nimport type { Workspace } from '@struktoai/mirage-core'\n\nexport class StaleMirageFileError extends Error {\n readonly path: string\n\n constructor(path: string) {\n super(`File changed since it was last read: ${path}. Read the file again before modifying it.`)\n this.name = 'StaleMirageFileError'\n this.path = path\n }\n}\n\nfunction fingerprint(content: Uint8Array | string): string {\n return createHash('sha256').update(content).digest('base64url')\n}\n\nasync function readBuffer(ws: Workspace, path: string): Promise<Buffer> {\n const bytes = await ws.fs.readFile(path, { raw: true })\n return Buffer.from(bytes)\n}\n\nexport class FileVersionTracker {\n private readonly readVersions = new Map<string, string>()\n private readonly editVersions = new Map<string, string>()\n\n constructor(\n private readonly ws: Workspace,\n private readonly enabled = true,\n ) {}\n\n private async currentVersion(path: string): Promise<string | null> {\n if (!(await this.ws.fs.exists(path))) return null\n return fingerprint(await readBuffer(this.ws, path))\n }\n\n private async assertVersion(path: string, expected: string): Promise<void> {\n if ((await this.currentVersion(path)) !== expected) {\n throw new StaleMirageFileError(path)\n }\n }\n\n private recordWrite(path: string, content: string): void {\n if (!this.enabled) return\n this.readVersions.set(path, fingerprint(content))\n this.editVersions.delete(path)\n }\n\n async read(path: string): Promise<Buffer> {\n const content = await readBuffer(this.ws, path)\n if (this.enabled) this.readVersions.set(path, fingerprint(content))\n return content\n }\n\n async readForEdit(path: string): Promise<Buffer> {\n const content = await readBuffer(this.ws, path)\n if (!this.enabled) return content\n const version = fingerprint(content)\n const readVersion = this.readVersions.get(path)\n if (readVersion !== undefined && readVersion !== version) {\n throw new StaleMirageFileError(path)\n }\n this.editVersions.set(path, version)\n return content\n }\n\n async write(path: string, content: string): Promise<void> {\n if (this.enabled) {\n const readVersion = this.readVersions.get(path)\n if (readVersion !== undefined) await this.assertVersion(path, readVersion)\n }\n await this.ws.fs.writeFile(path, content)\n this.recordWrite(path, content)\n }\n\n async writeEdit(path: string, content: string): Promise<void> {\n if (this.enabled) {\n const editVersion = this.editVersions.get(path)\n if (editVersion !== undefined) await this.assertVersion(path, editVersion)\n }\n await this.ws.fs.writeFile(path, content)\n this.recordWrite(path, content)\n }\n}\n"],"mappings":";AAcA,SAAS,kBAAkB;AAGpB,IAAM,uBAAN,cAAmC,MAAM;AAAA,EACrC;AAAA,EAET,YAAY,MAAc;AACxB,UAAM,wCAAwC,IAAI,4CAA4C;AAC9F,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAAS,YAAY,SAAsC;AACzD,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,WAAW;AAChE;AAEA,eAAe,WAAW,IAAe,MAA+B;AACtE,QAAM,QAAQ,MAAM,GAAG,GAAG,SAAS,MAAM,EAAE,KAAK,KAAK,CAAC;AACtD,SAAO,OAAO,KAAK,KAAK;AAC1B;AAEO,IAAM,qBAAN,MAAyB;AAAA,EAI9B,YACmB,IACA,UAAU,MAC3B;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EALF,eAAe,oBAAI,IAAoB;AAAA,EACvC,eAAe,oBAAI,IAAoB;AAAA,EAOxD,MAAc,eAAe,MAAsC;AACjE,QAAI,CAAE,MAAM,KAAK,GAAG,GAAG,OAAO,IAAI,EAAI,QAAO;AAC7C,WAAO,YAAY,MAAM,WAAW,KAAK,IAAI,IAAI,CAAC;AAAA,EACpD;AAAA,EAEA,MAAc,cAAc,MAAc,UAAiC;AACzE,QAAK,MAAM,KAAK,eAAe,IAAI,MAAO,UAAU;AAClD,YAAM,IAAI,qBAAqB,IAAI;AAAA,IACrC;AAAA,EACF;AAAA,EAEQ,YAAY,MAAc,SAAuB;AACvD,QAAI,CAAC,KAAK,QAAS;AACnB,SAAK,aAAa,IAAI,MAAM,YAAY,OAAO,CAAC;AAChD,SAAK,aAAa,OAAO,IAAI;AAAA,EAC/B;AAAA,EAEA,MAAM,KAAK,MAA+B;AACxC,UAAM,UAAU,MAAM,WAAW,KAAK,IAAI,IAAI;AAC9C,QAAI,KAAK,QAAS,MAAK,aAAa,IAAI,MAAM,YAAY,OAAO,CAAC;AAClE,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,MAA+B;AAC/C,UAAM,UAAU,MAAM,WAAW,KAAK,IAAI,IAAI;AAC9C,QAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,UAAM,UAAU,YAAY,OAAO;AACnC,UAAM,cAAc,KAAK,aAAa,IAAI,IAAI;AAC9C,QAAI,gBAAgB,UAAa,gBAAgB,SAAS;AACxD,YAAM,IAAI,qBAAqB,IAAI;AAAA,IACrC;AACA,SAAK,aAAa,IAAI,MAAM,OAAO;AACnC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MAAM,MAAc,SAAgC;AACxD,QAAI,KAAK,SAAS;AAChB,YAAM,cAAc,KAAK,aAAa,IAAI,IAAI;AAC9C,UAAI,gBAAgB,OAAW,OAAM,KAAK,cAAc,MAAM,WAAW;AAAA,IAC3E;AACA,UAAM,KAAK,GAAG,GAAG,UAAU,MAAM,OAAO;AACxC,SAAK,YAAY,MAAM,OAAO;AAAA,EAChC;AAAA,EAEA,MAAM,UAAU,MAAc,SAAgC;AAC5D,QAAI,KAAK,SAAS;AAChB,YAAM,cAAc,KAAK,aAAa,IAAI,IAAI;AAC9C,UAAI,gBAAgB,OAAW,OAAM,KAAK,cAAc,MAAM,WAAW;AAAA,IAC3E;AACA,UAAM,KAAK,GAAG,GAAG,UAAU,MAAM,OAAO;AACxC,SAAK,YAAY,MAAM,OAAO;AAAA,EAChC;AACF;","names":[]}
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import * as _anthropic_ai_claude_agent_sdk from '@anthropic-ai/claude-agent-sdk';
|
|
2
2
|
import { Options } from '@anthropic-ai/claude-agent-sdk';
|
|
3
3
|
import { Workspace } from '@struktoai/mirage-core';
|
|
4
|
+
import { M as MirageToolOperationsOptions } from '../tool-descriptions-7CgIovZc.js';
|
|
5
|
+
export { E as EDIT_DESCRIPTION, a as EXECUTE_DESCRIPTION, G as GREP_DESCRIPTION, L as LS_DESCRIPTION, R as READ_DESCRIPTION, W as WRITE_DESCRIPTION } from '../tool-descriptions-7CgIovZc.js';
|
|
4
6
|
export { M as MIRAGE_SYSTEM_PROMPT, b as buildSystemPrompt } from '../prompt-Ca4hc1Ul.js';
|
|
5
7
|
|
|
6
|
-
declare function MirageServer(workspace: Workspace): _anthropic_ai_claude_agent_sdk.McpSdkServerConfigWithInstance;
|
|
8
|
+
declare function MirageServer(workspace: Workspace, options?: MirageToolOperationsOptions): _anthropic_ai_claude_agent_sdk.McpSdkServerConfigWithInstance;
|
|
7
9
|
|
|
8
10
|
interface BuildOptionsOptions {
|
|
9
11
|
systemPrompt?: string;
|
|
10
12
|
}
|
|
11
13
|
declare function buildOptions(workspace: Workspace, opts?: BuildOptionsOptions): Options;
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
declare const READ_DESCRIPTION: string;
|
|
15
|
-
declare const WRITE_DESCRIPTION: string;
|
|
16
|
-
declare const EDIT_DESCRIPTION: string;
|
|
17
|
-
declare const LS_DESCRIPTION = "List files and directories at the given path on the Mirage virtual filesystem.";
|
|
18
|
-
declare const GREP_DESCRIPTION: string;
|
|
19
|
-
|
|
20
|
-
export { type BuildOptionsOptions, EDIT_DESCRIPTION, EXECUTE_DESCRIPTION, GREP_DESCRIPTION, LS_DESCRIPTION, MirageServer, READ_DESCRIPTION, WRITE_DESCRIPTION, buildOptions };
|
|
15
|
+
export { type BuildOptionsOptions, MirageServer, buildOptions };
|
|
@@ -1,115 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EDIT_DESCRIPTION,
|
|
3
|
+
EXECUTE_DESCRIPTION,
|
|
4
|
+
GREP_DESCRIPTION,
|
|
5
|
+
LS_DESCRIPTION,
|
|
6
|
+
MirageToolOperations,
|
|
7
|
+
READ_DESCRIPTION,
|
|
8
|
+
WRITE_DESCRIPTION
|
|
9
|
+
} from "../chunk-OLFSAPC6.js";
|
|
1
10
|
import {
|
|
2
11
|
MIRAGE_SYSTEM_PROMPT,
|
|
3
12
|
buildSystemPrompt
|
|
4
13
|
} from "../chunk-ODKR5YZI.js";
|
|
14
|
+
import "../chunk-XEQB7N3F.js";
|
|
5
15
|
|
|
6
16
|
// src/claude-agent-sdk/server.ts
|
|
7
|
-
import { VERSION
|
|
17
|
+
import { VERSION } from "@struktoai/mirage-core";
|
|
8
18
|
import { createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
|
|
9
19
|
import { z } from "zod";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
function decode(value) {
|
|
13
|
-
if (value === null || value === void 0) return "";
|
|
14
|
-
return new TextDecoder("utf-8").decode(value);
|
|
15
|
-
}
|
|
16
|
-
function ioToStr(io) {
|
|
17
|
-
const stdout = io.stdoutText;
|
|
18
|
-
const stderr = io.stderrText;
|
|
19
|
-
if (stderr) return stdout ? `${stdout}
|
|
20
|
-
${stderr}` : stderr;
|
|
21
|
-
return stdout;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// src/claude-agent-sdk/descriptions.ts
|
|
25
|
-
var EXECUTE_DESCRIPTION = "Run a shell-style command on the Mirage virtual filesystem. Supports cat, grep, find, head, tail, ls, wc, sort, uniq, tee, pipe, and any other Unix command on mounted resources (S3, disk, RAM, etc.). Also supports reading structured files: cat on .parquet/.orc/.csv returns a table.";
|
|
26
|
-
var READ_DESCRIPTION = "Read the contents of a file on the Mirage virtual filesystem. Returns line-numbered text. Optionally pass 'offset' (default 0) to start at a given line and 'limit' (default 2000) to cap the number of lines returned.";
|
|
27
|
-
var WRITE_DESCRIPTION = "Write content to a new file on the Mirage virtual filesystem. Fails if the file already exists; use edit to modify an existing file.";
|
|
28
|
-
var EDIT_DESCRIPTION = "Replace a string in an existing file on the Mirage virtual filesystem. Fails if old_string is not found or appears more than once. Pass replace_all=true (default false) to replace every occurrence.";
|
|
29
|
-
var LS_DESCRIPTION = "List files and directories at the given path on the Mirage virtual filesystem.";
|
|
30
|
-
var GREP_DESCRIPTION = "Search for a pattern in files on the Mirage virtual filesystem. Supports regex. Searches recursively under path.";
|
|
31
|
-
|
|
32
|
-
// src/claude-agent-sdk/server.ts
|
|
33
|
-
function textResult(text) {
|
|
34
|
-
return { content: [{ type: "text", text }] };
|
|
35
|
-
}
|
|
36
|
-
function errorResult(text) {
|
|
37
|
-
return { content: [{ type: "text", text }], isError: true };
|
|
38
|
-
}
|
|
39
|
-
function ioResult(io) {
|
|
40
|
-
const result = textResult(ioToStr(io));
|
|
41
|
-
if (io.exitCode !== 0) result.isError = true;
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
44
|
-
function shQuote(value) {
|
|
45
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
46
|
-
}
|
|
47
|
-
async function ensureParents(ws, path) {
|
|
48
|
-
const parent = gnuDirname(path);
|
|
49
|
-
if (parent === "/" || parent === "" || parent === ".") return;
|
|
50
|
-
if (await ws.fs.exists(parent)) return;
|
|
51
|
-
await ensureParents(ws, parent);
|
|
52
|
-
try {
|
|
53
|
-
await ws.fs.mkdir(parent);
|
|
54
|
-
} catch (err) {
|
|
55
|
-
if (!await ws.fs.exists(parent)) throw err;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
async function runExecute(ws, command) {
|
|
59
|
-
return ioResult(await ws.execute(command));
|
|
60
|
-
}
|
|
61
|
-
async function runRead(ws, path, offset = 0, limit = 2e3) {
|
|
62
|
-
let data;
|
|
63
|
-
try {
|
|
64
|
-
data = await ws.fs.readFile(path);
|
|
65
|
-
} catch (err) {
|
|
66
|
-
if (!await ws.fs.exists(path)) {
|
|
67
|
-
return errorResult(`Error: file '${path}' not found`);
|
|
68
|
-
}
|
|
69
|
-
return errorResult(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
-
}
|
|
71
|
-
const text = decode(data);
|
|
72
|
-
const raw = text.length === 0 ? [] : text.split(/(?<=\n)/);
|
|
73
|
-
const lines = raw.length > 0 && raw[raw.length - 1] === "" ? raw.slice(0, -1) : raw;
|
|
74
|
-
const sliced = lines.slice(offset, offset + limit);
|
|
75
|
-
const numbered = sliced.map((line, i) => `${String(i + offset + 1).padStart(6)} ${line}`);
|
|
76
|
-
return textResult(numbered.join(""));
|
|
77
|
-
}
|
|
78
|
-
async function runWrite(ws, path, content) {
|
|
79
|
-
if (await ws.fs.exists(path)) {
|
|
80
|
-
return errorResult(`Error: file '${path}' already exists`);
|
|
81
|
-
}
|
|
82
|
-
await ensureParents(ws, path);
|
|
83
|
-
await ws.fs.writeFile(path, content);
|
|
84
|
-
return textResult(`Written: ${path}`);
|
|
85
|
-
}
|
|
86
|
-
async function runEdit(ws, path, oldString, newString, replaceAll = false) {
|
|
87
|
-
let content;
|
|
88
|
-
try {
|
|
89
|
-
content = await ws.fs.readFileText(path);
|
|
90
|
-
} catch {
|
|
91
|
-
return errorResult(`Error: file '${path}' not found`);
|
|
92
|
-
}
|
|
93
|
-
const count = content.split(oldString).length - 1;
|
|
94
|
-
if (count === 0) {
|
|
95
|
-
return errorResult(`Error: string not found in file: '${oldString}'`);
|
|
96
|
-
}
|
|
97
|
-
if (count > 1 && !replaceAll) {
|
|
98
|
-
return errorResult(`Error: string appears ${String(count)} times. Pass replace_all=true`);
|
|
99
|
-
}
|
|
100
|
-
const newContent = replaceAll ? content.split(oldString).join(newString) : content.replace(oldString, newString);
|
|
101
|
-
await ws.fs.writeFile(path, newContent);
|
|
102
|
-
const occurrences = replaceAll ? count : 1;
|
|
103
|
-
return textResult(`Edited: ${path} (${String(occurrences)} occurrence(s))`);
|
|
104
|
-
}
|
|
105
|
-
async function runLs(ws, path) {
|
|
106
|
-
return ioResult(await ws.execute(`ls ${shQuote(path)}`));
|
|
107
|
-
}
|
|
108
|
-
async function runGrep(ws, pattern, path) {
|
|
109
|
-
const io = await ws.execute(`grep -rn ${shQuote(pattern)} ${shQuote(path)}`);
|
|
110
|
-
return textResult(ioToStr(io));
|
|
111
|
-
}
|
|
112
|
-
function MirageServer(workspace) {
|
|
20
|
+
function MirageServer(workspace, options = {}) {
|
|
21
|
+
const operations = new MirageToolOperations(workspace, options);
|
|
113
22
|
return createSdkMcpServer({
|
|
114
23
|
name: "mirage",
|
|
115
24
|
version: VERSION,
|
|
@@ -119,20 +28,20 @@ function MirageServer(workspace) {
|
|
|
119
28
|
"execute_command",
|
|
120
29
|
EXECUTE_DESCRIPTION,
|
|
121
30
|
{ command: z.string() },
|
|
122
|
-
(args) =>
|
|
31
|
+
(args) => operations.execute(args.command)
|
|
123
32
|
),
|
|
124
33
|
tool(
|
|
125
34
|
"read",
|
|
126
35
|
READ_DESCRIPTION,
|
|
127
36
|
{ path: z.string(), offset: z.number().optional(), limit: z.number().optional() },
|
|
128
|
-
(args) =>
|
|
37
|
+
(args) => operations.read(args.path, args.offset, args.limit),
|
|
129
38
|
{ annotations: { readOnlyHint: true } }
|
|
130
39
|
),
|
|
131
40
|
tool(
|
|
132
41
|
"write",
|
|
133
42
|
WRITE_DESCRIPTION,
|
|
134
43
|
{ path: z.string(), content: z.string() },
|
|
135
|
-
(args) =>
|
|
44
|
+
(args) => operations.write(args.path, args.content)
|
|
136
45
|
),
|
|
137
46
|
tool(
|
|
138
47
|
"edit",
|
|
@@ -143,16 +52,16 @@ function MirageServer(workspace) {
|
|
|
143
52
|
new_string: z.string(),
|
|
144
53
|
replace_all: z.boolean().optional()
|
|
145
54
|
},
|
|
146
|
-
(args) =>
|
|
55
|
+
(args) => operations.edit(args.path, args.old_string, args.new_string, args.replace_all)
|
|
147
56
|
),
|
|
148
|
-
tool("ls", LS_DESCRIPTION, { path: z.string() }, (args) =>
|
|
57
|
+
tool("ls", LS_DESCRIPTION, { path: z.string() }, (args) => operations.ls(args.path), {
|
|
149
58
|
annotations: { readOnlyHint: true }
|
|
150
59
|
}),
|
|
151
60
|
tool(
|
|
152
61
|
"grep",
|
|
153
62
|
GREP_DESCRIPTION,
|
|
154
63
|
{ pattern: z.string(), path: z.string() },
|
|
155
|
-
(args) =>
|
|
64
|
+
(args) => operations.grep(args.pattern, args.path),
|
|
156
65
|
{ annotations: { readOnlyHint: true } }
|
|
157
66
|
)
|
|
158
67
|
]
|