@ash-cloud/ash-ai 0.1.8 → 0.1.11
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/ash-widget.js +1 -1
- package/dist/ash-widget.js.map +1 -1
- package/dist/index.cjs +1829 -250
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +831 -6
- package/dist/index.d.ts +831 -6
- package/dist/index.js +1800 -231
- package/dist/index.js.map +1 -1
- package/dist/playground.js +259 -255
- package/dist/{schema-B_CVsJm5.d.cts → schema-B7RbjHWi.d.cts} +261 -2
- package/dist/{schema-B_CVsJm5.d.ts → schema-B7RbjHWi.d.ts} +261 -2
- package/dist/schema.cjs +53 -1
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +52 -3
- package/dist/schema.js.map +1 -1
- package/package.json +14 -13
package/dist/index.cjs
CHANGED
|
@@ -4,10 +4,11 @@ var zod = require('zod');
|
|
|
4
4
|
var child_process = require('child_process');
|
|
5
5
|
var nanoid = require('nanoid');
|
|
6
6
|
var fs = require('fs/promises');
|
|
7
|
-
var
|
|
7
|
+
var path4 = require('path');
|
|
8
8
|
var stream = require('stream');
|
|
9
|
+
var chokidar = require('chokidar');
|
|
9
10
|
var crypto = require('crypto');
|
|
10
|
-
var
|
|
11
|
+
var fs10 = require('fs');
|
|
11
12
|
var hono = require('hono');
|
|
12
13
|
var streaming = require('hono/streaming');
|
|
13
14
|
var cors = require('hono/cors');
|
|
@@ -46,8 +47,9 @@ function _interopNamespace(e) {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
49
|
-
var
|
|
50
|
-
var
|
|
50
|
+
var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
|
|
51
|
+
var chokidar__default = /*#__PURE__*/_interopDefault(chokidar);
|
|
52
|
+
var fs10__namespace = /*#__PURE__*/_interopNamespace(fs10);
|
|
51
53
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
52
54
|
var postgres__default = /*#__PURE__*/_interopDefault(postgres);
|
|
53
55
|
|
|
@@ -105,7 +107,7 @@ var init_normalized = __esm({
|
|
|
105
107
|
"src/types/normalized.ts"() {
|
|
106
108
|
}
|
|
107
109
|
});
|
|
108
|
-
exports.SessionStatus = void 0; exports.AgentStatus = void 0; exports.MessageRole = void 0; exports.messageContentSchema = void 0; exports.messageSchema = void 0; exports.sessionSchema = void 0; exports.attachmentSchema = void 0; exports.DEFAULT_SANDBOX_PROVIDER_CONFIG = void 0; exports.StreamEventType = void 0; exports.QueueItemStatus = void 0;
|
|
110
|
+
exports.SessionStatus = void 0; exports.AgentStatus = void 0; exports.MessageRole = void 0; exports.messageContentSchema = void 0; exports.messageSchema = void 0; exports.sessionSchema = void 0; exports.attachmentSchema = void 0; exports.DEFAULT_SANDBOX_PROVIDER_CONFIG = void 0; exports.StreamEventType = void 0; exports.QueueItemStatus = void 0; exports.EventCategory = void 0;
|
|
109
111
|
var init_types = __esm({
|
|
110
112
|
"src/types/index.ts"() {
|
|
111
113
|
init_normalized();
|
|
@@ -202,6 +204,7 @@ var init_types = __esm({
|
|
|
202
204
|
SESSION_END: "session_end",
|
|
203
205
|
SESSION_STOPPED: "session_stopped",
|
|
204
206
|
SANDBOX_LOG: "sandbox_log",
|
|
207
|
+
MCP_STATUS: "mcp_status",
|
|
205
208
|
ERROR: "error"
|
|
206
209
|
};
|
|
207
210
|
exports.QueueItemStatus = {
|
|
@@ -211,6 +214,22 @@ var init_types = __esm({
|
|
|
211
214
|
FAILED: "failed",
|
|
212
215
|
CANCELLED: "cancelled"
|
|
213
216
|
};
|
|
217
|
+
exports.EventCategory = {
|
|
218
|
+
LIFECYCLE: "lifecycle",
|
|
219
|
+
// session_start, session_end, turn_complete
|
|
220
|
+
CONTENT: "content",
|
|
221
|
+
// text_stream (aggregated), thinking_stream
|
|
222
|
+
TOOL: "tool",
|
|
223
|
+
// tool_use, tool_result
|
|
224
|
+
SYSTEM: "system",
|
|
225
|
+
// mcp_status, sandbox_log
|
|
226
|
+
ERROR: "error",
|
|
227
|
+
// error events
|
|
228
|
+
FILE: "file",
|
|
229
|
+
// file_push, file_pull, file_sync (file sync operations)
|
|
230
|
+
INPUT: "input"
|
|
231
|
+
// user_input (user prompts/messages)
|
|
232
|
+
};
|
|
214
233
|
}
|
|
215
234
|
});
|
|
216
235
|
|
|
@@ -630,7 +649,7 @@ var init_mcp = __esm({
|
|
|
630
649
|
* @param path Path to the server script
|
|
631
650
|
* @param runtime Runtime to use (node, python, etc.)
|
|
632
651
|
*/
|
|
633
|
-
custom: (
|
|
652
|
+
custom: (path15, runtime = "node", args) => {
|
|
634
653
|
const commands = {
|
|
635
654
|
node: "node",
|
|
636
655
|
python: "python3",
|
|
@@ -638,7 +657,7 @@ var init_mcp = __esm({
|
|
|
638
657
|
};
|
|
639
658
|
return {
|
|
640
659
|
command: commands[runtime] ?? "node",
|
|
641
|
-
args: runtime === "deno" ? ["run", "-A",
|
|
660
|
+
args: runtime === "deno" ? ["run", "-A", path15, ...args ?? []] : [path15, ...args ?? []]
|
|
642
661
|
};
|
|
643
662
|
}
|
|
644
663
|
};
|
|
@@ -696,8 +715,8 @@ var init_mcp = __esm({
|
|
|
696
715
|
/**
|
|
697
716
|
* Add a custom stdio MCP server
|
|
698
717
|
*/
|
|
699
|
-
withCustom(name,
|
|
700
|
-
return this.add(name, exports.McpServers.custom(
|
|
718
|
+
withCustom(name, path15, runtime, args) {
|
|
719
|
+
return this.add(name, exports.McpServers.custom(path15, runtime, args));
|
|
701
720
|
}
|
|
702
721
|
/**
|
|
703
722
|
* Add an HTTP MCP server with typed authentication
|
|
@@ -3019,9 +3038,9 @@ var init_attachment = __esm({
|
|
|
3019
3038
|
throw new Error(`MIME type ${mimeType} is not allowed`);
|
|
3020
3039
|
}
|
|
3021
3040
|
const id = nanoid.nanoid();
|
|
3022
|
-
const ext =
|
|
3041
|
+
const ext = path4__namespace.extname(filename) || this.getExtensionForMimeType(mimeType);
|
|
3023
3042
|
const storageName = `${id}${ext}`;
|
|
3024
|
-
const storagePath =
|
|
3043
|
+
const storagePath = path4__namespace.join(this.config.basePath, storageName);
|
|
3025
3044
|
await fs__namespace.writeFile(storagePath, content);
|
|
3026
3045
|
return {
|
|
3027
3046
|
id,
|
|
@@ -3038,8 +3057,8 @@ var init_attachment = __esm({
|
|
|
3038
3057
|
*/
|
|
3039
3058
|
async storeFromPath(messageId, sourcePath, mimeType) {
|
|
3040
3059
|
const content = await fs__namespace.readFile(sourcePath);
|
|
3041
|
-
const filename =
|
|
3042
|
-
const detectedMimeType = mimeType ?? this.getMimeTypeForExtension(
|
|
3060
|
+
const filename = path4__namespace.basename(sourcePath);
|
|
3061
|
+
const detectedMimeType = mimeType ?? this.getMimeTypeForExtension(path4__namespace.extname(filename));
|
|
3043
3062
|
return this.storeFromBuffer(messageId, filename, content, detectedMimeType);
|
|
3044
3063
|
}
|
|
3045
3064
|
/**
|
|
@@ -3270,8 +3289,8 @@ async function loadConfig(configPath, options = {}) {
|
|
|
3270
3289
|
return config;
|
|
3271
3290
|
}
|
|
3272
3291
|
async function loadConfigFile(filePath) {
|
|
3273
|
-
const ext =
|
|
3274
|
-
const fullPath =
|
|
3292
|
+
const ext = path4__namespace.extname(filePath);
|
|
3293
|
+
const fullPath = path4__namespace.isAbsolute(filePath) ? filePath : path4__namespace.resolve(process.cwd(), filePath);
|
|
3275
3294
|
if (ext === ".json") {
|
|
3276
3295
|
const content = await fs__namespace.readFile(fullPath, "utf-8");
|
|
3277
3296
|
return JSON.parse(content);
|
|
@@ -3284,7 +3303,7 @@ async function loadConfigFile(filePath) {
|
|
|
3284
3303
|
}
|
|
3285
3304
|
async function findAndLoadConfig(directory) {
|
|
3286
3305
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
3287
|
-
const filePath =
|
|
3306
|
+
const filePath = path4__namespace.join(directory, fileName);
|
|
3288
3307
|
try {
|
|
3289
3308
|
await fs__namespace.access(filePath);
|
|
3290
3309
|
return await loadConfigFile(filePath);
|
|
@@ -3622,8 +3641,8 @@ var init_modal = __esm({
|
|
|
3622
3641
|
}
|
|
3623
3642
|
async writeFile(_sandboxId, _path, _content) {
|
|
3624
3643
|
}
|
|
3625
|
-
async readFile(_sandboxId,
|
|
3626
|
-
return `[Modal] File content from ${
|
|
3644
|
+
async readFile(_sandboxId, path15) {
|
|
3645
|
+
return `[Modal] File content from ${path15}`;
|
|
3627
3646
|
}
|
|
3628
3647
|
/**
|
|
3629
3648
|
* Modal-specific: Get GPU information
|
|
@@ -3770,12 +3789,12 @@ var init_e2b = __esm({
|
|
|
3770
3789
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
3771
3790
|
}
|
|
3772
3791
|
}
|
|
3773
|
-
async readFile(sandboxId,
|
|
3792
|
+
async readFile(sandboxId, path15) {
|
|
3774
3793
|
const sandbox = this.sandboxes.get(sandboxId);
|
|
3775
3794
|
if (!sandbox) {
|
|
3776
3795
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
3777
3796
|
}
|
|
3778
|
-
return `[E2B] File content from ${
|
|
3797
|
+
return `[E2B] File content from ${path15}`;
|
|
3779
3798
|
}
|
|
3780
3799
|
async listFiles(sandboxId, _path) {
|
|
3781
3800
|
const sandbox = this.sandboxes.get(sandboxId);
|
|
@@ -3804,17 +3823,17 @@ var init_e2b = __esm({
|
|
|
3804
3823
|
* E2B-specific: Upload a file from local filesystem
|
|
3805
3824
|
*/
|
|
3806
3825
|
async uploadFile(sandboxId, localPath, remotePath) {
|
|
3807
|
-
const
|
|
3808
|
-
const content = await
|
|
3826
|
+
const fs15 = await import('fs/promises');
|
|
3827
|
+
const content = await fs15.readFile(localPath);
|
|
3809
3828
|
await this.writeFile(sandboxId, remotePath, content);
|
|
3810
3829
|
}
|
|
3811
3830
|
/**
|
|
3812
3831
|
* E2B-specific: Download a file to local filesystem
|
|
3813
3832
|
*/
|
|
3814
3833
|
async downloadFile(sandboxId, remotePath, localPath) {
|
|
3815
|
-
const
|
|
3834
|
+
const fs15 = await import('fs/promises');
|
|
3816
3835
|
const content = await this.readFile(sandboxId, remotePath);
|
|
3817
|
-
await
|
|
3836
|
+
await fs15.writeFile(localPath, content);
|
|
3818
3837
|
}
|
|
3819
3838
|
};
|
|
3820
3839
|
}
|
|
@@ -4068,7 +4087,7 @@ var init_vercel = __esm({
|
|
|
4068
4087
|
}
|
|
4069
4088
|
};
|
|
4070
4089
|
}
|
|
4071
|
-
async writeFile(sandboxId,
|
|
4090
|
+
async writeFile(sandboxId, path15, content) {
|
|
4072
4091
|
const instance = this.sandboxes.get(sandboxId);
|
|
4073
4092
|
if (!instance) {
|
|
4074
4093
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
@@ -4078,34 +4097,34 @@ var init_vercel = __esm({
|
|
|
4078
4097
|
if (isBase64) {
|
|
4079
4098
|
await this.executeCommand(
|
|
4080
4099
|
sandboxId,
|
|
4081
|
-
`echo "${contentStr}" | base64 -d > "${
|
|
4100
|
+
`echo "${contentStr}" | base64 -d > "${path15}"`
|
|
4082
4101
|
);
|
|
4083
4102
|
} else {
|
|
4084
4103
|
const escaped = contentStr.replace(/'/g, "'\\''");
|
|
4085
|
-
await this.executeCommand(sandboxId, `cat > "${
|
|
4104
|
+
await this.executeCommand(sandboxId, `cat > "${path15}" << 'VERCEL_EOF'
|
|
4086
4105
|
${escaped}
|
|
4087
4106
|
VERCEL_EOF`);
|
|
4088
4107
|
}
|
|
4089
4108
|
}
|
|
4090
|
-
async readFile(sandboxId,
|
|
4109
|
+
async readFile(sandboxId, path15) {
|
|
4091
4110
|
const instance = this.sandboxes.get(sandboxId);
|
|
4092
4111
|
if (!instance) {
|
|
4093
4112
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
4094
4113
|
}
|
|
4095
|
-
const result = await this.executeCommand(sandboxId, `cat "${
|
|
4114
|
+
const result = await this.executeCommand(sandboxId, `cat "${path15}"`);
|
|
4096
4115
|
if (result.exitCode !== 0) {
|
|
4097
|
-
throw new Error(`Failed to read file ${
|
|
4116
|
+
throw new Error(`Failed to read file ${path15}: ${result.stderr}`);
|
|
4098
4117
|
}
|
|
4099
4118
|
return result.stdout;
|
|
4100
4119
|
}
|
|
4101
|
-
async listFiles(sandboxId,
|
|
4120
|
+
async listFiles(sandboxId, path15) {
|
|
4102
4121
|
const instance = this.sandboxes.get(sandboxId);
|
|
4103
4122
|
if (!instance) {
|
|
4104
4123
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
4105
4124
|
}
|
|
4106
4125
|
const result = await this.executeCommand(
|
|
4107
4126
|
sandboxId,
|
|
4108
|
-
`ls -la "${
|
|
4127
|
+
`ls -la "${path15}" 2>/dev/null || echo ""`
|
|
4109
4128
|
);
|
|
4110
4129
|
if (result.exitCode !== 0 || !result.stdout.trim()) {
|
|
4111
4130
|
return [];
|
|
@@ -4120,7 +4139,7 @@ VERCEL_EOF`);
|
|
|
4120
4139
|
const [, type, , size, , name] = match;
|
|
4121
4140
|
if (name && name !== "." && name !== ".." && size) {
|
|
4122
4141
|
files.push({
|
|
4123
|
-
path: `${
|
|
4142
|
+
path: `${path15}/${name}`.replace(/\/+/g, "/"),
|
|
4124
4143
|
name,
|
|
4125
4144
|
size: parseInt(size, 10),
|
|
4126
4145
|
isDirectory: type === "d"
|
|
@@ -4130,12 +4149,12 @@ VERCEL_EOF`);
|
|
|
4130
4149
|
}
|
|
4131
4150
|
return files;
|
|
4132
4151
|
}
|
|
4133
|
-
async deleteFile(sandboxId,
|
|
4152
|
+
async deleteFile(sandboxId, path15) {
|
|
4134
4153
|
const instance = this.sandboxes.get(sandboxId);
|
|
4135
4154
|
if (!instance) {
|
|
4136
4155
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
4137
4156
|
}
|
|
4138
|
-
await this.executeCommand(sandboxId, `rm -rf "${
|
|
4157
|
+
await this.executeCommand(sandboxId, `rm -rf "${path15}"`);
|
|
4139
4158
|
}
|
|
4140
4159
|
async getLogs(sandboxId, _options) {
|
|
4141
4160
|
const instance = this.sandboxes.get(sandboxId);
|
|
@@ -5268,7 +5287,7 @@ function isSandboxRunning(sessionId) {
|
|
|
5268
5287
|
const cached = sandboxCache.get(sessionId);
|
|
5269
5288
|
return cached !== void 0 && !cached.isExpired;
|
|
5270
5289
|
}
|
|
5271
|
-
async function writeFileToSandbox(sessionId,
|
|
5290
|
+
async function writeFileToSandbox(sessionId, path15, content) {
|
|
5272
5291
|
const cached = sandboxCache.get(sessionId);
|
|
5273
5292
|
if (!cached) {
|
|
5274
5293
|
return { success: false, error: "No active sandbox for session" };
|
|
@@ -5278,14 +5297,14 @@ async function writeFileToSandbox(sessionId, path14, content) {
|
|
|
5278
5297
|
}
|
|
5279
5298
|
try {
|
|
5280
5299
|
const sandbox = cached.sandbox;
|
|
5281
|
-
const dir =
|
|
5300
|
+
const dir = path15.substring(0, path15.lastIndexOf("/"));
|
|
5282
5301
|
if (dir) {
|
|
5283
5302
|
await sandbox.runCommand({ cmd: "mkdir", args: ["-p", dir] });
|
|
5284
5303
|
}
|
|
5285
5304
|
const base64Content = content.toString("base64");
|
|
5286
5305
|
const result = await sandbox.runCommand({
|
|
5287
5306
|
cmd: "bash",
|
|
5288
|
-
args: ["-c", `echo '${base64Content}' | base64 -d > '${
|
|
5307
|
+
args: ["-c", `echo '${base64Content}' | base64 -d > '${path15}'`]
|
|
5289
5308
|
});
|
|
5290
5309
|
if (result.exitCode !== 0) {
|
|
5291
5310
|
const stderr = await result.stderr();
|
|
@@ -5300,7 +5319,7 @@ async function writeFileToSandbox(sessionId, path14, content) {
|
|
|
5300
5319
|
};
|
|
5301
5320
|
}
|
|
5302
5321
|
}
|
|
5303
|
-
async function readFileFromSandbox(sessionId,
|
|
5322
|
+
async function readFileFromSandbox(sessionId, path15) {
|
|
5304
5323
|
const cached = sandboxCache.get(sessionId);
|
|
5305
5324
|
if (!cached) {
|
|
5306
5325
|
return { success: false, error: "No active sandbox for session" };
|
|
@@ -5312,14 +5331,14 @@ async function readFileFromSandbox(sessionId, path14) {
|
|
|
5312
5331
|
const sandbox = cached.sandbox;
|
|
5313
5332
|
const checkResult = await sandbox.runCommand({
|
|
5314
5333
|
cmd: "test",
|
|
5315
|
-
args: ["-f",
|
|
5334
|
+
args: ["-f", path15]
|
|
5316
5335
|
});
|
|
5317
5336
|
if (checkResult.exitCode !== 0) {
|
|
5318
5337
|
return { success: false, error: "File not found" };
|
|
5319
5338
|
}
|
|
5320
5339
|
const result = await sandbox.runCommand({
|
|
5321
5340
|
cmd: "base64",
|
|
5322
|
-
args: [
|
|
5341
|
+
args: [path15]
|
|
5323
5342
|
});
|
|
5324
5343
|
if (result.exitCode !== 0) {
|
|
5325
5344
|
const stderr = await result.stderr();
|
|
@@ -5336,7 +5355,7 @@ async function readFileFromSandbox(sessionId, path14) {
|
|
|
5336
5355
|
};
|
|
5337
5356
|
}
|
|
5338
5357
|
}
|
|
5339
|
-
async function listFilesInSandbox(sessionId,
|
|
5358
|
+
async function listFilesInSandbox(sessionId, path15) {
|
|
5340
5359
|
const cached = sandboxCache.get(sessionId);
|
|
5341
5360
|
if (!cached) {
|
|
5342
5361
|
return { success: false, error: "No active sandbox for session" };
|
|
@@ -5348,14 +5367,14 @@ async function listFilesInSandbox(sessionId, path14) {
|
|
|
5348
5367
|
const sandbox = cached.sandbox;
|
|
5349
5368
|
const checkResult = await sandbox.runCommand({
|
|
5350
5369
|
cmd: "test",
|
|
5351
|
-
args: ["-d",
|
|
5370
|
+
args: ["-d", path15]
|
|
5352
5371
|
});
|
|
5353
5372
|
if (checkResult.exitCode !== 0) {
|
|
5354
5373
|
return { success: true, files: [] };
|
|
5355
5374
|
}
|
|
5356
5375
|
const result = await sandbox.runCommand({
|
|
5357
5376
|
cmd: "find",
|
|
5358
|
-
args: [
|
|
5377
|
+
args: [path15, "-type", "f", "-printf", "%P\\n"]
|
|
5359
5378
|
});
|
|
5360
5379
|
if (result.exitCode !== 0) {
|
|
5361
5380
|
const stderr = await result.stderr();
|
|
@@ -5803,8 +5822,516 @@ var init_vercel_sandbox_executor = __esm({
|
|
|
5803
5822
|
heartbeatListeners = /* @__PURE__ */ new Set();
|
|
5804
5823
|
}
|
|
5805
5824
|
});
|
|
5806
|
-
|
|
5807
|
-
|
|
5825
|
+
function createFileWatcher(options) {
|
|
5826
|
+
return new exports.SandboxFileWatcher(options);
|
|
5827
|
+
}
|
|
5828
|
+
function getFileWatcherManager() {
|
|
5829
|
+
if (!globalWatcherManager) {
|
|
5830
|
+
globalWatcherManager = new exports.FileWatcherManager();
|
|
5831
|
+
}
|
|
5832
|
+
return globalWatcherManager;
|
|
5833
|
+
}
|
|
5834
|
+
function createFileWatcherManager() {
|
|
5835
|
+
return new exports.FileWatcherManager();
|
|
5836
|
+
}
|
|
5837
|
+
function createRemoteFileWatcher(options) {
|
|
5838
|
+
return new exports.RemoteSandboxFileWatcher(options);
|
|
5839
|
+
}
|
|
5840
|
+
function getRemoteFileWatcherManager() {
|
|
5841
|
+
if (!globalRemoteWatcherManager) {
|
|
5842
|
+
globalRemoteWatcherManager = new exports.RemoteFileWatcherManager();
|
|
5843
|
+
}
|
|
5844
|
+
return globalRemoteWatcherManager;
|
|
5845
|
+
}
|
|
5846
|
+
function createRemoteFileWatcherManager() {
|
|
5847
|
+
return new exports.RemoteFileWatcherManager();
|
|
5848
|
+
}
|
|
5849
|
+
exports.SandboxFileWatcher = void 0; exports.FileWatcherManager = void 0; var globalWatcherManager; exports.RemoteSandboxFileWatcher = void 0; exports.RemoteFileWatcherManager = void 0; var globalRemoteWatcherManager;
|
|
5850
|
+
var init_sandbox_file_watcher = __esm({
|
|
5851
|
+
"src/runtime/sandbox-file-watcher.ts"() {
|
|
5852
|
+
exports.SandboxFileWatcher = class {
|
|
5853
|
+
sessionId;
|
|
5854
|
+
watchPath;
|
|
5855
|
+
debounceMs;
|
|
5856
|
+
patterns;
|
|
5857
|
+
ignored;
|
|
5858
|
+
emitInitialEvents;
|
|
5859
|
+
followSymlinks;
|
|
5860
|
+
usePolling;
|
|
5861
|
+
pollInterval;
|
|
5862
|
+
watcher = null;
|
|
5863
|
+
pendingEvents = /* @__PURE__ */ new Map();
|
|
5864
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
5865
|
+
isWatching = false;
|
|
5866
|
+
startedAt;
|
|
5867
|
+
watchedFileCount = 0;
|
|
5868
|
+
onError;
|
|
5869
|
+
onReady;
|
|
5870
|
+
constructor(options) {
|
|
5871
|
+
this.sessionId = options.sessionId;
|
|
5872
|
+
this.watchPath = options.watchPath;
|
|
5873
|
+
this.debounceMs = options.debounceMs ?? 300;
|
|
5874
|
+
this.patterns = options.patterns ?? ["**/*"];
|
|
5875
|
+
this.ignored = options.ignored ?? ["**/node_modules/**", "**/.git/**", "**/*.log"];
|
|
5876
|
+
this.emitInitialEvents = options.emitInitialEvents ?? false;
|
|
5877
|
+
this.followSymlinks = options.followSymlinks ?? false;
|
|
5878
|
+
this.usePolling = options.usePolling ?? false;
|
|
5879
|
+
this.pollInterval = options.pollInterval ?? 100;
|
|
5880
|
+
this.onError = options.onError;
|
|
5881
|
+
this.onReady = options.onReady;
|
|
5882
|
+
if (options.onFileChange) {
|
|
5883
|
+
this.subscribers.add(options.onFileChange);
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
/**
|
|
5887
|
+
* Start watching the directory for changes
|
|
5888
|
+
*/
|
|
5889
|
+
async start() {
|
|
5890
|
+
if (this.isWatching) {
|
|
5891
|
+
console.warn(`[FILE_WATCHER] Already watching ${this.watchPath}`);
|
|
5892
|
+
return;
|
|
5893
|
+
}
|
|
5894
|
+
try {
|
|
5895
|
+
const stat2 = await fs__namespace.stat(this.watchPath);
|
|
5896
|
+
if (!stat2.isDirectory()) {
|
|
5897
|
+
throw new Error(`Watch path is not a directory: ${this.watchPath}`);
|
|
5898
|
+
}
|
|
5899
|
+
} catch (error) {
|
|
5900
|
+
if (error.code === "ENOENT") {
|
|
5901
|
+
throw new Error(`Watch path does not exist: ${this.watchPath}`);
|
|
5902
|
+
}
|
|
5903
|
+
throw error;
|
|
5904
|
+
}
|
|
5905
|
+
const watchPaths = this.patterns.map(
|
|
5906
|
+
(pattern) => path4__namespace.join(this.watchPath, pattern)
|
|
5907
|
+
);
|
|
5908
|
+
this.watcher = chokidar__default.default.watch(watchPaths, {
|
|
5909
|
+
ignored: this.ignored,
|
|
5910
|
+
persistent: true,
|
|
5911
|
+
ignoreInitial: !this.emitInitialEvents,
|
|
5912
|
+
followSymlinks: this.followSymlinks,
|
|
5913
|
+
usePolling: this.usePolling,
|
|
5914
|
+
interval: this.pollInterval,
|
|
5915
|
+
awaitWriteFinish: {
|
|
5916
|
+
stabilityThreshold: this.debounceMs,
|
|
5917
|
+
pollInterval: 100
|
|
5918
|
+
}
|
|
5919
|
+
});
|
|
5920
|
+
this.watcher.on("add", (filePath) => this.handleFileEvent("add", filePath)).on("change", (filePath) => this.handleFileEvent("change", filePath)).on("unlink", (filePath) => this.handleFileEvent("unlink", filePath)).on("addDir", (dirPath) => this.handleFileEvent("addDir", dirPath)).on("unlinkDir", (dirPath) => this.handleFileEvent("unlinkDir", dirPath)).on("error", (error) => {
|
|
5921
|
+
console.error(`[FILE_WATCHER] Error watching ${this.watchPath}:`, error);
|
|
5922
|
+
if (this.onError) {
|
|
5923
|
+
this.onError(error);
|
|
5924
|
+
}
|
|
5925
|
+
}).on("ready", () => {
|
|
5926
|
+
console.log(`[FILE_WATCHER] Ready to watch ${this.watchPath}`);
|
|
5927
|
+
this.isWatching = true;
|
|
5928
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
5929
|
+
if (this.onReady) {
|
|
5930
|
+
this.onReady();
|
|
5931
|
+
}
|
|
5932
|
+
});
|
|
5933
|
+
this.watcher.on("add", () => this.watchedFileCount++);
|
|
5934
|
+
this.watcher.on("unlink", () => this.watchedFileCount--);
|
|
5935
|
+
}
|
|
5936
|
+
/**
|
|
5937
|
+
* Stop watching and cleanup resources
|
|
5938
|
+
*/
|
|
5939
|
+
async stop() {
|
|
5940
|
+
if (!this.isWatching || !this.watcher) {
|
|
5941
|
+
return;
|
|
5942
|
+
}
|
|
5943
|
+
for (const pending of this.pendingEvents.values()) {
|
|
5944
|
+
clearTimeout(pending.timer);
|
|
5945
|
+
}
|
|
5946
|
+
this.pendingEvents.clear();
|
|
5947
|
+
await this.watcher.close();
|
|
5948
|
+
this.watcher = null;
|
|
5949
|
+
this.isWatching = false;
|
|
5950
|
+
this.watchedFileCount = 0;
|
|
5951
|
+
console.log(`[FILE_WATCHER] Stopped watching ${this.watchPath}`);
|
|
5952
|
+
}
|
|
5953
|
+
/**
|
|
5954
|
+
* Subscribe to file change events
|
|
5955
|
+
* @returns Unsubscribe function
|
|
5956
|
+
*/
|
|
5957
|
+
subscribe(callback) {
|
|
5958
|
+
this.subscribers.add(callback);
|
|
5959
|
+
return () => this.subscribers.delete(callback);
|
|
5960
|
+
}
|
|
5961
|
+
/**
|
|
5962
|
+
* Remove a subscriber
|
|
5963
|
+
*/
|
|
5964
|
+
unsubscribe(callback) {
|
|
5965
|
+
this.subscribers.delete(callback);
|
|
5966
|
+
}
|
|
5967
|
+
/**
|
|
5968
|
+
* Get the current status of the watcher
|
|
5969
|
+
*/
|
|
5970
|
+
getStatus() {
|
|
5971
|
+
return {
|
|
5972
|
+
sessionId: this.sessionId,
|
|
5973
|
+
watchPath: this.watchPath,
|
|
5974
|
+
isWatching: this.isWatching,
|
|
5975
|
+
watchedFileCount: this.watchedFileCount,
|
|
5976
|
+
pendingEventCount: this.pendingEvents.size,
|
|
5977
|
+
startedAt: this.startedAt
|
|
5978
|
+
};
|
|
5979
|
+
}
|
|
5980
|
+
/**
|
|
5981
|
+
* Check if the watcher is currently active
|
|
5982
|
+
*/
|
|
5983
|
+
isActive() {
|
|
5984
|
+
return this.isWatching;
|
|
5985
|
+
}
|
|
5986
|
+
/**
|
|
5987
|
+
* Handle a file system event with debouncing
|
|
5988
|
+
*/
|
|
5989
|
+
handleFileEvent(type, absolutePath) {
|
|
5990
|
+
const relativePath = path4__namespace.relative(this.watchPath, absolutePath);
|
|
5991
|
+
const existing = this.pendingEvents.get(absolutePath);
|
|
5992
|
+
if (existing) {
|
|
5993
|
+
clearTimeout(existing.timer);
|
|
5994
|
+
}
|
|
5995
|
+
const timer = setTimeout(async () => {
|
|
5996
|
+
this.pendingEvents.delete(absolutePath);
|
|
5997
|
+
await this.emitEvent(type, absolutePath, relativePath);
|
|
5998
|
+
}, this.debounceMs);
|
|
5999
|
+
this.pendingEvents.set(absolutePath, {
|
|
6000
|
+
type,
|
|
6001
|
+
absolutePath,
|
|
6002
|
+
relativePath,
|
|
6003
|
+
timer
|
|
6004
|
+
});
|
|
6005
|
+
}
|
|
6006
|
+
/**
|
|
6007
|
+
* Emit a file change event to all subscribers
|
|
6008
|
+
*/
|
|
6009
|
+
async emitEvent(type, absolutePath, relativePath) {
|
|
6010
|
+
let fileSize;
|
|
6011
|
+
if (type === "add" || type === "change") {
|
|
6012
|
+
try {
|
|
6013
|
+
const stat2 = await fs__namespace.stat(absolutePath);
|
|
6014
|
+
fileSize = stat2.size;
|
|
6015
|
+
} catch {
|
|
6016
|
+
}
|
|
6017
|
+
}
|
|
6018
|
+
const event = {
|
|
6019
|
+
type,
|
|
6020
|
+
relativePath,
|
|
6021
|
+
absolutePath,
|
|
6022
|
+
sessionId: this.sessionId,
|
|
6023
|
+
fileSize,
|
|
6024
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
6025
|
+
};
|
|
6026
|
+
for (const callback of this.subscribers) {
|
|
6027
|
+
try {
|
|
6028
|
+
await callback(event);
|
|
6029
|
+
} catch (error) {
|
|
6030
|
+
console.error("[FILE_WATCHER] Error in subscriber callback:", error);
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
};
|
|
6035
|
+
exports.FileWatcherManager = class {
|
|
6036
|
+
watchers = /* @__PURE__ */ new Map();
|
|
6037
|
+
/**
|
|
6038
|
+
* Start watching a session's sandbox directory
|
|
6039
|
+
*/
|
|
6040
|
+
async startWatching(options) {
|
|
6041
|
+
const { sessionId } = options;
|
|
6042
|
+
const existing = this.watchers.get(sessionId);
|
|
6043
|
+
if (existing) {
|
|
6044
|
+
await existing.stop();
|
|
6045
|
+
}
|
|
6046
|
+
const watcher = new exports.SandboxFileWatcher(options);
|
|
6047
|
+
await watcher.start();
|
|
6048
|
+
this.watchers.set(sessionId, watcher);
|
|
6049
|
+
return watcher;
|
|
6050
|
+
}
|
|
6051
|
+
/**
|
|
6052
|
+
* Stop watching a session's sandbox directory
|
|
6053
|
+
*/
|
|
6054
|
+
async stopWatching(sessionId) {
|
|
6055
|
+
const watcher = this.watchers.get(sessionId);
|
|
6056
|
+
if (watcher) {
|
|
6057
|
+
await watcher.stop();
|
|
6058
|
+
this.watchers.delete(sessionId);
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
/**
|
|
6062
|
+
* Get a watcher for a session
|
|
6063
|
+
*/
|
|
6064
|
+
getWatcher(sessionId) {
|
|
6065
|
+
return this.watchers.get(sessionId);
|
|
6066
|
+
}
|
|
6067
|
+
/**
|
|
6068
|
+
* Check if a session is being watched
|
|
6069
|
+
*/
|
|
6070
|
+
isWatching(sessionId) {
|
|
6071
|
+
const watcher = this.watchers.get(sessionId);
|
|
6072
|
+
return watcher?.isActive() ?? false;
|
|
6073
|
+
}
|
|
6074
|
+
/**
|
|
6075
|
+
* Get status of all watchers
|
|
6076
|
+
*/
|
|
6077
|
+
getAllStatuses() {
|
|
6078
|
+
return Array.from(this.watchers.values()).map((w) => w.getStatus());
|
|
6079
|
+
}
|
|
6080
|
+
/**
|
|
6081
|
+
* Stop all watchers and cleanup
|
|
6082
|
+
*/
|
|
6083
|
+
async stopAll() {
|
|
6084
|
+
const promises = Array.from(this.watchers.values()).map((w) => w.stop());
|
|
6085
|
+
await Promise.all(promises);
|
|
6086
|
+
this.watchers.clear();
|
|
6087
|
+
}
|
|
6088
|
+
};
|
|
6089
|
+
globalWatcherManager = null;
|
|
6090
|
+
exports.RemoteSandboxFileWatcher = class {
|
|
6091
|
+
sessionId;
|
|
6092
|
+
sandboxOps;
|
|
6093
|
+
basePath;
|
|
6094
|
+
pollIntervalMs;
|
|
6095
|
+
ignored;
|
|
6096
|
+
pollTimer = null;
|
|
6097
|
+
previousFiles = /* @__PURE__ */ new Map();
|
|
6098
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
6099
|
+
isWatching = false;
|
|
6100
|
+
startedAt;
|
|
6101
|
+
lastPollAt;
|
|
6102
|
+
pollCount = 0;
|
|
6103
|
+
onError;
|
|
6104
|
+
constructor(options) {
|
|
6105
|
+
this.sessionId = options.sessionId;
|
|
6106
|
+
this.sandboxOps = options.sandboxOps;
|
|
6107
|
+
this.basePath = options.basePath;
|
|
6108
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
6109
|
+
this.ignored = options.ignored ?? ["**/node_modules/**", "**/.git/**"];
|
|
6110
|
+
this.onError = options.onError;
|
|
6111
|
+
if (options.onFileChange) {
|
|
6112
|
+
this.subscribers.add(options.onFileChange);
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
/**
|
|
6116
|
+
* Start polling for file changes
|
|
6117
|
+
*/
|
|
6118
|
+
async start() {
|
|
6119
|
+
if (this.isWatching) {
|
|
6120
|
+
console.warn(`[REMOTE_WATCHER] Already watching session ${this.sessionId}`);
|
|
6121
|
+
return;
|
|
6122
|
+
}
|
|
6123
|
+
if (!this.sandboxOps.isSandboxRunning(this.sessionId)) {
|
|
6124
|
+
throw new Error(`Sandbox is not running for session ${this.sessionId}`);
|
|
6125
|
+
}
|
|
6126
|
+
await this.scan(true);
|
|
6127
|
+
this.pollTimer = setInterval(async () => {
|
|
6128
|
+
try {
|
|
6129
|
+
await this.scan(false);
|
|
6130
|
+
} catch (error) {
|
|
6131
|
+
console.error(`[REMOTE_WATCHER] Poll error for session ${this.sessionId}:`, error);
|
|
6132
|
+
if (this.onError && error instanceof Error) {
|
|
6133
|
+
this.onError(error);
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
6136
|
+
}, this.pollIntervalMs);
|
|
6137
|
+
this.isWatching = true;
|
|
6138
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
6139
|
+
console.log(`[REMOTE_WATCHER] Started watching session ${this.sessionId} at ${this.basePath}`);
|
|
6140
|
+
}
|
|
6141
|
+
/**
|
|
6142
|
+
* Stop polling and cleanup
|
|
6143
|
+
*/
|
|
6144
|
+
async stop() {
|
|
6145
|
+
if (!this.isWatching) {
|
|
6146
|
+
return;
|
|
6147
|
+
}
|
|
6148
|
+
if (this.pollTimer) {
|
|
6149
|
+
clearInterval(this.pollTimer);
|
|
6150
|
+
this.pollTimer = null;
|
|
6151
|
+
}
|
|
6152
|
+
this.isWatching = false;
|
|
6153
|
+
this.previousFiles.clear();
|
|
6154
|
+
console.log(`[REMOTE_WATCHER] Stopped watching session ${this.sessionId}`);
|
|
6155
|
+
}
|
|
6156
|
+
/**
|
|
6157
|
+
* Subscribe to file change events
|
|
6158
|
+
*/
|
|
6159
|
+
subscribe(callback) {
|
|
6160
|
+
this.subscribers.add(callback);
|
|
6161
|
+
return () => this.subscribers.delete(callback);
|
|
6162
|
+
}
|
|
6163
|
+
/**
|
|
6164
|
+
* Remove a subscriber
|
|
6165
|
+
*/
|
|
6166
|
+
unsubscribe(callback) {
|
|
6167
|
+
this.subscribers.delete(callback);
|
|
6168
|
+
}
|
|
6169
|
+
/**
|
|
6170
|
+
* Check if watcher is active
|
|
6171
|
+
*/
|
|
6172
|
+
isActive() {
|
|
6173
|
+
return this.isWatching;
|
|
6174
|
+
}
|
|
6175
|
+
/**
|
|
6176
|
+
* Get watcher status
|
|
6177
|
+
*/
|
|
6178
|
+
getStatus() {
|
|
6179
|
+
return {
|
|
6180
|
+
sessionId: this.sessionId,
|
|
6181
|
+
watchPath: this.basePath,
|
|
6182
|
+
isWatching: this.isWatching,
|
|
6183
|
+
watchedFileCount: this.previousFiles.size,
|
|
6184
|
+
pendingEventCount: 0,
|
|
6185
|
+
startedAt: this.startedAt,
|
|
6186
|
+
lastPollAt: this.lastPollAt,
|
|
6187
|
+
pollCount: this.pollCount
|
|
6188
|
+
};
|
|
6189
|
+
}
|
|
6190
|
+
/**
|
|
6191
|
+
* Force an immediate scan (useful for testing or manual refresh)
|
|
6192
|
+
*/
|
|
6193
|
+
async forceScan() {
|
|
6194
|
+
await this.scan(false);
|
|
6195
|
+
}
|
|
6196
|
+
/**
|
|
6197
|
+
* Scan the sandbox for file changes
|
|
6198
|
+
*/
|
|
6199
|
+
async scan(isInitial) {
|
|
6200
|
+
if (!this.sandboxOps.isSandboxRunning(this.sessionId)) {
|
|
6201
|
+
console.warn(`[REMOTE_WATCHER] Sandbox stopped for session ${this.sessionId}`);
|
|
6202
|
+
await this.stop();
|
|
6203
|
+
return;
|
|
6204
|
+
}
|
|
6205
|
+
const listResult = await this.sandboxOps.listFiles(this.sessionId, this.basePath);
|
|
6206
|
+
if (!listResult.success || !listResult.files) {
|
|
6207
|
+
throw new Error(listResult.error ?? "Failed to list files in sandbox");
|
|
6208
|
+
}
|
|
6209
|
+
const currentFiles = /* @__PURE__ */ new Map();
|
|
6210
|
+
for (const filePath of listResult.files) {
|
|
6211
|
+
if (this.shouldIgnore(filePath)) {
|
|
6212
|
+
continue;
|
|
6213
|
+
}
|
|
6214
|
+
currentFiles.set(filePath, { path: filePath });
|
|
6215
|
+
}
|
|
6216
|
+
this.lastPollAt = /* @__PURE__ */ new Date();
|
|
6217
|
+
this.pollCount++;
|
|
6218
|
+
if (isInitial) {
|
|
6219
|
+
this.previousFiles = currentFiles;
|
|
6220
|
+
return;
|
|
6221
|
+
}
|
|
6222
|
+
const changes = [];
|
|
6223
|
+
for (const [filePath, info] of currentFiles) {
|
|
6224
|
+
const previous = this.previousFiles.get(filePath);
|
|
6225
|
+
if (!previous) {
|
|
6226
|
+
changes.push({
|
|
6227
|
+
type: "add",
|
|
6228
|
+
relativePath: filePath,
|
|
6229
|
+
absolutePath: `${this.basePath}/${filePath}`,
|
|
6230
|
+
sessionId: this.sessionId,
|
|
6231
|
+
fileSize: info.size,
|
|
6232
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
6233
|
+
});
|
|
6234
|
+
}
|
|
6235
|
+
}
|
|
6236
|
+
for (const [filePath] of this.previousFiles) {
|
|
6237
|
+
if (!currentFiles.has(filePath)) {
|
|
6238
|
+
changes.push({
|
|
6239
|
+
type: "unlink",
|
|
6240
|
+
relativePath: filePath,
|
|
6241
|
+
absolutePath: `${this.basePath}/${filePath}`,
|
|
6242
|
+
sessionId: this.sessionId,
|
|
6243
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
6244
|
+
});
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
this.previousFiles = currentFiles;
|
|
6248
|
+
for (const event of changes) {
|
|
6249
|
+
await this.emitEvent(event);
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
6252
|
+
/**
|
|
6253
|
+
* Check if a path should be ignored
|
|
6254
|
+
*/
|
|
6255
|
+
shouldIgnore(filePath) {
|
|
6256
|
+
for (const pattern of this.ignored) {
|
|
6257
|
+
if (this.matchesGlob(filePath, pattern)) {
|
|
6258
|
+
return true;
|
|
6259
|
+
}
|
|
6260
|
+
}
|
|
6261
|
+
return false;
|
|
6262
|
+
}
|
|
6263
|
+
/**
|
|
6264
|
+
* Simple glob matching (supports ** and *)
|
|
6265
|
+
*/
|
|
6266
|
+
matchesGlob(filePath, pattern) {
|
|
6267
|
+
const regexPattern = pattern.replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/{{GLOBSTAR}}/g, ".*").replace(/\//g, "\\/");
|
|
6268
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
6269
|
+
return regex.test(filePath);
|
|
6270
|
+
}
|
|
6271
|
+
/**
|
|
6272
|
+
* Emit a file change event to all subscribers
|
|
6273
|
+
*/
|
|
6274
|
+
async emitEvent(event) {
|
|
6275
|
+
for (const callback of this.subscribers) {
|
|
6276
|
+
try {
|
|
6277
|
+
await callback(event);
|
|
6278
|
+
} catch (error) {
|
|
6279
|
+
console.error("[REMOTE_WATCHER] Error in subscriber callback:", error);
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
};
|
|
6284
|
+
exports.RemoteFileWatcherManager = class {
|
|
6285
|
+
watchers = /* @__PURE__ */ new Map();
|
|
6286
|
+
/**
|
|
6287
|
+
* Start watching a session's sandbox
|
|
6288
|
+
*/
|
|
6289
|
+
async startWatching(options) {
|
|
6290
|
+
const { sessionId } = options;
|
|
6291
|
+
const existing = this.watchers.get(sessionId);
|
|
6292
|
+
if (existing) {
|
|
6293
|
+
await existing.stop();
|
|
6294
|
+
}
|
|
6295
|
+
const watcher = new exports.RemoteSandboxFileWatcher(options);
|
|
6296
|
+
await watcher.start();
|
|
6297
|
+
this.watchers.set(sessionId, watcher);
|
|
6298
|
+
return watcher;
|
|
6299
|
+
}
|
|
6300
|
+
/**
|
|
6301
|
+
* Stop watching a session
|
|
6302
|
+
*/
|
|
6303
|
+
async stopWatching(sessionId) {
|
|
6304
|
+
const watcher = this.watchers.get(sessionId);
|
|
6305
|
+
if (watcher) {
|
|
6306
|
+
await watcher.stop();
|
|
6307
|
+
this.watchers.delete(sessionId);
|
|
6308
|
+
}
|
|
6309
|
+
}
|
|
6310
|
+
/**
|
|
6311
|
+
* Get a watcher for a session
|
|
6312
|
+
*/
|
|
6313
|
+
getWatcher(sessionId) {
|
|
6314
|
+
return this.watchers.get(sessionId);
|
|
6315
|
+
}
|
|
6316
|
+
/**
|
|
6317
|
+
* Check if a session is being watched
|
|
6318
|
+
*/
|
|
6319
|
+
isWatching(sessionId) {
|
|
6320
|
+
const watcher = this.watchers.get(sessionId);
|
|
6321
|
+
return watcher?.isActive() ?? false;
|
|
6322
|
+
}
|
|
6323
|
+
/**
|
|
6324
|
+
* Stop all watchers
|
|
6325
|
+
*/
|
|
6326
|
+
async stopAll() {
|
|
6327
|
+
const promises = Array.from(this.watchers.values()).map((w) => w.stop());
|
|
6328
|
+
await Promise.all(promises);
|
|
6329
|
+
this.watchers.clear();
|
|
6330
|
+
}
|
|
6331
|
+
};
|
|
6332
|
+
globalRemoteWatcherManager = null;
|
|
6333
|
+
}
|
|
6334
|
+
});
|
|
5808
6335
|
function extractErrorMessage(error) {
|
|
5809
6336
|
if (error === null || error === void 0) {
|
|
5810
6337
|
return "Unknown error (null/undefined)";
|
|
@@ -5850,13 +6377,256 @@ function createSandboxFileSync(options) {
|
|
|
5850
6377
|
exports.SandboxFileSync = void 0;
|
|
5851
6378
|
var init_sandbox_file_sync = __esm({
|
|
5852
6379
|
"src/runtime/sandbox-file-sync.ts"() {
|
|
6380
|
+
init_sandbox_file_watcher();
|
|
6381
|
+
init_types();
|
|
5853
6382
|
exports.SandboxFileSync = class {
|
|
5854
6383
|
fileStore;
|
|
5855
6384
|
sandboxBasePath;
|
|
6385
|
+
defaultWatchOptions;
|
|
5856
6386
|
sandboxOps = null;
|
|
6387
|
+
onFileEvent;
|
|
6388
|
+
eventStorage;
|
|
6389
|
+
webhookConfig;
|
|
6390
|
+
// Watcher management
|
|
6391
|
+
remoteWatchers = /* @__PURE__ */ new Map();
|
|
6392
|
+
localWatchers = /* @__PURE__ */ new Map();
|
|
6393
|
+
fileChangeSubscribers = /* @__PURE__ */ new Set();
|
|
6394
|
+
// Sequence number cache per session (for event storage)
|
|
6395
|
+
sequenceNumbers = /* @__PURE__ */ new Map();
|
|
5857
6396
|
constructor(options) {
|
|
5858
6397
|
this.fileStore = options.fileStore;
|
|
5859
6398
|
this.sandboxBasePath = options.sandboxBasePath ?? ".claude/files";
|
|
6399
|
+
this.onFileEvent = options.onFileEvent;
|
|
6400
|
+
this.defaultWatchOptions = options.watchOptions ?? {};
|
|
6401
|
+
this.eventStorage = options.eventStorage;
|
|
6402
|
+
this.webhookConfig = options.webhook;
|
|
6403
|
+
}
|
|
6404
|
+
/**
|
|
6405
|
+
* Set the file event callback
|
|
6406
|
+
* This can be used to set or update the callback after construction
|
|
6407
|
+
*/
|
|
6408
|
+
setFileEventCallback(callback) {
|
|
6409
|
+
this.onFileEvent = callback;
|
|
6410
|
+
}
|
|
6411
|
+
/**
|
|
6412
|
+
* Set the event storage for persisting file events to the timeline
|
|
6413
|
+
* This can be used to set or update the storage after construction
|
|
6414
|
+
*/
|
|
6415
|
+
setEventStorage(storage) {
|
|
6416
|
+
this.eventStorage = storage;
|
|
6417
|
+
}
|
|
6418
|
+
/**
|
|
6419
|
+
* Set webhook configuration for external notifications
|
|
6420
|
+
* This can be used to set or update the webhook after construction
|
|
6421
|
+
*/
|
|
6422
|
+
setWebhook(config) {
|
|
6423
|
+
this.webhookConfig = config;
|
|
6424
|
+
}
|
|
6425
|
+
/**
|
|
6426
|
+
* Remove webhook configuration
|
|
6427
|
+
*/
|
|
6428
|
+
removeWebhook() {
|
|
6429
|
+
this.webhookConfig = void 0;
|
|
6430
|
+
}
|
|
6431
|
+
/**
|
|
6432
|
+
* Send a webhook notification
|
|
6433
|
+
* @param payload - The webhook payload to send
|
|
6434
|
+
*/
|
|
6435
|
+
async sendWebhook(payload) {
|
|
6436
|
+
if (!this.webhookConfig) return;
|
|
6437
|
+
const config = this.webhookConfig;
|
|
6438
|
+
const eventType = payload.fileSyncEvent?.operation ?? "file_change";
|
|
6439
|
+
if (config.events && config.events.length > 0) {
|
|
6440
|
+
const shouldSend = config.events.some(
|
|
6441
|
+
(e) => e === eventType || e === "file_change" && payload.event === "file_change"
|
|
6442
|
+
);
|
|
6443
|
+
if (!shouldSend) return;
|
|
6444
|
+
}
|
|
6445
|
+
const body = JSON.stringify(payload);
|
|
6446
|
+
const timeoutMs = config.timeoutMs ?? 1e4;
|
|
6447
|
+
const retries = config.retries ?? 3;
|
|
6448
|
+
const isAsync = config.async !== false;
|
|
6449
|
+
const headers = {
|
|
6450
|
+
"Content-Type": "application/json",
|
|
6451
|
+
"User-Agent": "Ash-FileSync/1.0",
|
|
6452
|
+
...config.headers
|
|
6453
|
+
};
|
|
6454
|
+
if (config.secret) {
|
|
6455
|
+
const signature = crypto.createHmac("sha256", config.secret).update(body).digest("hex");
|
|
6456
|
+
headers["X-Ash-Signature"] = `sha256=${signature}`;
|
|
6457
|
+
headers["X-Ash-Timestamp"] = payload.timestamp;
|
|
6458
|
+
}
|
|
6459
|
+
const sendWithRetry = async (attempt) => {
|
|
6460
|
+
try {
|
|
6461
|
+
const controller = new AbortController();
|
|
6462
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
6463
|
+
const response = await fetch(config.url, {
|
|
6464
|
+
method: "POST",
|
|
6465
|
+
headers,
|
|
6466
|
+
body,
|
|
6467
|
+
signal: controller.signal
|
|
6468
|
+
});
|
|
6469
|
+
clearTimeout(timeoutId);
|
|
6470
|
+
if (!response.ok) {
|
|
6471
|
+
throw new Error(`Webhook returned ${response.status}: ${response.statusText}`);
|
|
6472
|
+
}
|
|
6473
|
+
console.log(`[FILE_SYNC] Webhook sent successfully to ${config.url}`);
|
|
6474
|
+
} catch (error) {
|
|
6475
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
6476
|
+
if (attempt < retries) {
|
|
6477
|
+
const delay = Math.pow(2, attempt) * 1e3;
|
|
6478
|
+
console.warn(`[FILE_SYNC] Webhook failed (attempt ${attempt + 1}/${retries}), retrying in ${delay}ms: ${errorMsg}`);
|
|
6479
|
+
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
6480
|
+
return sendWithRetry(attempt + 1);
|
|
6481
|
+
}
|
|
6482
|
+
console.error(`[FILE_SYNC] Webhook failed after ${retries} attempts: ${errorMsg}`);
|
|
6483
|
+
throw error;
|
|
6484
|
+
}
|
|
6485
|
+
};
|
|
6486
|
+
if (isAsync) {
|
|
6487
|
+
sendWithRetry(0).catch((error) => {
|
|
6488
|
+
console.error("[FILE_SYNC] Async webhook failed:", error);
|
|
6489
|
+
});
|
|
6490
|
+
} else {
|
|
6491
|
+
await sendWithRetry(0);
|
|
6492
|
+
}
|
|
6493
|
+
}
|
|
6494
|
+
/**
|
|
6495
|
+
* Send a file sync event webhook
|
|
6496
|
+
*/
|
|
6497
|
+
async sendFileSyncWebhook(sessionId, event) {
|
|
6498
|
+
const payload = {
|
|
6499
|
+
event: "file_sync",
|
|
6500
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6501
|
+
sessionId,
|
|
6502
|
+
metadata: this.webhookConfig?.metadata,
|
|
6503
|
+
fileSyncEvent: event
|
|
6504
|
+
};
|
|
6505
|
+
await this.sendWebhook(payload);
|
|
6506
|
+
}
|
|
6507
|
+
/**
|
|
6508
|
+
* Send a file change event webhook (from watcher)
|
|
6509
|
+
*/
|
|
6510
|
+
async sendFileChangeWebhook(event) {
|
|
6511
|
+
const payload = {
|
|
6512
|
+
event: "file_change",
|
|
6513
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6514
|
+
sessionId: event.sessionId,
|
|
6515
|
+
metadata: this.webhookConfig?.metadata,
|
|
6516
|
+
fileChangeEvent: event
|
|
6517
|
+
};
|
|
6518
|
+
await this.sendWebhook(payload);
|
|
6519
|
+
}
|
|
6520
|
+
/**
|
|
6521
|
+
* Get the next sequence number for a session
|
|
6522
|
+
*/
|
|
6523
|
+
async getNextSequenceNumber(sessionId) {
|
|
6524
|
+
const cached = this.sequenceNumbers.get(sessionId);
|
|
6525
|
+
if (cached !== void 0) {
|
|
6526
|
+
const next = cached + 1;
|
|
6527
|
+
this.sequenceNumbers.set(sessionId, next);
|
|
6528
|
+
return next;
|
|
6529
|
+
}
|
|
6530
|
+
if (this.eventStorage) {
|
|
6531
|
+
try {
|
|
6532
|
+
const next = await this.eventStorage.getNextEventSequence(sessionId);
|
|
6533
|
+
this.sequenceNumbers.set(sessionId, next);
|
|
6534
|
+
return next;
|
|
6535
|
+
} catch (error) {
|
|
6536
|
+
console.warn("[FILE_SYNC] Failed to get next sequence number:", error);
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
this.sequenceNumbers.set(sessionId, 1);
|
|
6540
|
+
return 1;
|
|
6541
|
+
}
|
|
6542
|
+
/**
|
|
6543
|
+
* Compute a unified diff between two text contents
|
|
6544
|
+
*/
|
|
6545
|
+
computeDiff(previousContent, newContent) {
|
|
6546
|
+
if (!previousContent && !newContent) return void 0;
|
|
6547
|
+
if (!previousContent || !newContent) return void 0;
|
|
6548
|
+
try {
|
|
6549
|
+
const MAX_DIFF_SIZE = 100 * 1024;
|
|
6550
|
+
if (previousContent.length > MAX_DIFF_SIZE || newContent.length > MAX_DIFF_SIZE) {
|
|
6551
|
+
return void 0;
|
|
6552
|
+
}
|
|
6553
|
+
const prevText = previousContent.toString("utf-8");
|
|
6554
|
+
const newText = newContent.toString("utf-8");
|
|
6555
|
+
const sampleSize = Math.min(8192, prevText.length, newText.length);
|
|
6556
|
+
if (prevText.slice(0, sampleSize).includes("\0") || newText.slice(0, sampleSize).includes("\0")) {
|
|
6557
|
+
return void 0;
|
|
6558
|
+
}
|
|
6559
|
+
const prevLines = prevText.split("\n");
|
|
6560
|
+
const newLines = newText.split("\n");
|
|
6561
|
+
const diffLines = [];
|
|
6562
|
+
diffLines.push("--- a/file");
|
|
6563
|
+
diffLines.push("+++ b/file");
|
|
6564
|
+
const removed = prevLines.filter((l) => !newLines.includes(l));
|
|
6565
|
+
const added = newLines.filter((l) => !prevLines.includes(l));
|
|
6566
|
+
if (removed.length === 0 && added.length === 0) {
|
|
6567
|
+
return void 0;
|
|
6568
|
+
}
|
|
6569
|
+
diffLines.push("@@ -1 +1 @@");
|
|
6570
|
+
for (const line of removed) {
|
|
6571
|
+
diffLines.push(`-${line}`);
|
|
6572
|
+
}
|
|
6573
|
+
for (const line of added) {
|
|
6574
|
+
diffLines.push(`+${line}`);
|
|
6575
|
+
}
|
|
6576
|
+
return diffLines.join("\n");
|
|
6577
|
+
} catch {
|
|
6578
|
+
return void 0;
|
|
6579
|
+
}
|
|
6580
|
+
}
|
|
6581
|
+
/**
|
|
6582
|
+
* Emit a file event if a callback is registered
|
|
6583
|
+
* Also persists to EventStorage if configured
|
|
6584
|
+
*/
|
|
6585
|
+
async emitFileEvent(sessionId, event) {
|
|
6586
|
+
if (this.onFileEvent) {
|
|
6587
|
+
try {
|
|
6588
|
+
this.onFileEvent(event);
|
|
6589
|
+
} catch (error) {
|
|
6590
|
+
console.error("[FILE_SYNC] Error in file event callback:", error);
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6593
|
+
if (this.webhookConfig) {
|
|
6594
|
+
this.sendFileSyncWebhook(sessionId, event);
|
|
6595
|
+
}
|
|
6596
|
+
if (this.eventStorage) {
|
|
6597
|
+
try {
|
|
6598
|
+
const now = /* @__PURE__ */ new Date();
|
|
6599
|
+
const sequenceNumber = await this.getNextSequenceNumber(sessionId);
|
|
6600
|
+
const diff = this.computeDiff(event.previousContent, event.newContent);
|
|
6601
|
+
const isTextFile = diff !== void 0 || event.newContent && !event.newContent.slice(0, 8192).includes(0);
|
|
6602
|
+
const eventData = {
|
|
6603
|
+
operation: event.operation,
|
|
6604
|
+
direction: event.direction,
|
|
6605
|
+
source: "internal",
|
|
6606
|
+
// File sync operations are internal
|
|
6607
|
+
filePath: event.filePath,
|
|
6608
|
+
fileSize: event.fileSize,
|
|
6609
|
+
success: event.success,
|
|
6610
|
+
error: event.error,
|
|
6611
|
+
diff,
|
|
6612
|
+
isTextFile,
|
|
6613
|
+
previousSize: event.previousContent?.length
|
|
6614
|
+
};
|
|
6615
|
+
const eventType = `file_${event.operation}`;
|
|
6616
|
+
const sessionEvent = {
|
|
6617
|
+
eventType,
|
|
6618
|
+
category: exports.EventCategory.FILE,
|
|
6619
|
+
startedAt: now,
|
|
6620
|
+
endedAt: now,
|
|
6621
|
+
durationMs: 0,
|
|
6622
|
+
eventData,
|
|
6623
|
+
sequenceNumber
|
|
6624
|
+
};
|
|
6625
|
+
await this.eventStorage.saveEvents(sessionId, [sessionEvent]);
|
|
6626
|
+
} catch (error) {
|
|
6627
|
+
console.error("[FILE_SYNC] Error saving file event to storage:", error);
|
|
6628
|
+
}
|
|
6629
|
+
}
|
|
5860
6630
|
}
|
|
5861
6631
|
/**
|
|
5862
6632
|
* Set the sandbox operations implementation
|
|
@@ -5870,8 +6640,8 @@ var init_sandbox_file_sync = __esm({
|
|
|
5870
6640
|
* @param path - The relative file path
|
|
5871
6641
|
* @param targetPath - Optional override for the base path
|
|
5872
6642
|
*/
|
|
5873
|
-
getSandboxPath(
|
|
5874
|
-
const normalizedPath =
|
|
6643
|
+
getSandboxPath(path15, targetPath) {
|
|
6644
|
+
const normalizedPath = path15.replace(/^\/+/, "");
|
|
5875
6645
|
const basePath = targetPath ?? this.sandboxBasePath;
|
|
5876
6646
|
if (basePath === ".") {
|
|
5877
6647
|
return normalizedPath;
|
|
@@ -5884,33 +6654,67 @@ var init_sandbox_file_sync = __esm({
|
|
|
5884
6654
|
* @param path - File path (stored in S3 and used as relative path in sandbox)
|
|
5885
6655
|
* @param content - File content
|
|
5886
6656
|
* @param options - Push options (e.g., targetPath to override sandbox location)
|
|
6657
|
+
* @param previousContent - Optional previous content for diff computation
|
|
5887
6658
|
*/
|
|
5888
|
-
async pushFile(sessionId,
|
|
6659
|
+
async pushFile(sessionId, path15, content, options, previousContent) {
|
|
5889
6660
|
const result = {
|
|
5890
|
-
path:
|
|
6661
|
+
path: path15,
|
|
5891
6662
|
s3Written: false,
|
|
5892
6663
|
sandboxWritten: false
|
|
5893
6664
|
};
|
|
5894
6665
|
try {
|
|
5895
|
-
await this.fileStore.writeFile(sessionId,
|
|
6666
|
+
await this.fileStore.writeFile(sessionId, path15, content);
|
|
5896
6667
|
result.s3Written = true;
|
|
6668
|
+
await this.emitFileEvent(sessionId, {
|
|
6669
|
+
operation: "push",
|
|
6670
|
+
direction: "to_s3",
|
|
6671
|
+
filePath: path15,
|
|
6672
|
+
fileSize: content.length,
|
|
6673
|
+
success: true,
|
|
6674
|
+
previousContent,
|
|
6675
|
+
newContent: content
|
|
6676
|
+
});
|
|
5897
6677
|
} catch (error) {
|
|
5898
6678
|
const errorMessage = extractErrorMessage(error);
|
|
5899
6679
|
result.error = `S3 write failed: ${errorMessage}`;
|
|
5900
|
-
console.error(`[FILE_SYNC] S3 write failed for session ${sessionId}, path ${
|
|
6680
|
+
console.error(`[FILE_SYNC] S3 write failed for session ${sessionId}, path ${path15}:`, error);
|
|
6681
|
+
await this.emitFileEvent(sessionId, {
|
|
6682
|
+
operation: "push",
|
|
6683
|
+
direction: "to_s3",
|
|
6684
|
+
filePath: path15,
|
|
6685
|
+
fileSize: content.length,
|
|
6686
|
+
success: false,
|
|
6687
|
+
error: errorMessage
|
|
6688
|
+
});
|
|
5901
6689
|
return result;
|
|
5902
6690
|
}
|
|
5903
6691
|
if (this.sandboxOps?.isSandboxRunning(sessionId)) {
|
|
5904
6692
|
try {
|
|
5905
|
-
const sandboxPath = this.getSandboxPath(
|
|
6693
|
+
const sandboxPath = this.getSandboxPath(path15, options?.targetPath);
|
|
5906
6694
|
const writeResult = await this.sandboxOps.writeFile(sessionId, sandboxPath, content);
|
|
5907
6695
|
result.sandboxWritten = writeResult.success;
|
|
6696
|
+
await this.emitFileEvent(sessionId, {
|
|
6697
|
+
operation: "push",
|
|
6698
|
+
direction: "to_sandbox",
|
|
6699
|
+
filePath: path15,
|
|
6700
|
+
fileSize: content.length,
|
|
6701
|
+
success: writeResult.success,
|
|
6702
|
+
error: writeResult.error
|
|
6703
|
+
});
|
|
5908
6704
|
if (!writeResult.success && writeResult.error) {
|
|
5909
|
-
console.warn(`[FILE_SYNC] Sandbox write failed for ${
|
|
6705
|
+
console.warn(`[FILE_SYNC] Sandbox write failed for ${path15}: ${writeResult.error}`);
|
|
5910
6706
|
}
|
|
5911
6707
|
} catch (error) {
|
|
5912
6708
|
const errorMessage = extractErrorMessage(error);
|
|
5913
|
-
console.warn(`[FILE_SYNC] Sandbox write error for ${
|
|
6709
|
+
console.warn(`[FILE_SYNC] Sandbox write error for ${path15}: ${errorMessage}`);
|
|
6710
|
+
await this.emitFileEvent(sessionId, {
|
|
6711
|
+
operation: "push",
|
|
6712
|
+
direction: "to_sandbox",
|
|
6713
|
+
filePath: path15,
|
|
6714
|
+
fileSize: content.length,
|
|
6715
|
+
success: false,
|
|
6716
|
+
error: errorMessage
|
|
6717
|
+
});
|
|
5914
6718
|
}
|
|
5915
6719
|
} else {
|
|
5916
6720
|
console.debug(`[FILE_SYNC] Sandbox not running for session ${sessionId}, skipping sandbox write`);
|
|
@@ -5935,9 +6739,9 @@ var init_sandbox_file_sync = __esm({
|
|
|
5935
6739
|
* Pull a file from sandbox to S3
|
|
5936
6740
|
* Reads from sandbox and writes to S3
|
|
5937
6741
|
*/
|
|
5938
|
-
async pullFile(sessionId,
|
|
6742
|
+
async pullFile(sessionId, path15) {
|
|
5939
6743
|
const result = {
|
|
5940
|
-
path:
|
|
6744
|
+
path: path15,
|
|
5941
6745
|
content: null,
|
|
5942
6746
|
s3Written: false
|
|
5943
6747
|
};
|
|
@@ -5946,35 +6750,73 @@ var init_sandbox_file_sync = __esm({
|
|
|
5946
6750
|
return result;
|
|
5947
6751
|
}
|
|
5948
6752
|
try {
|
|
5949
|
-
const sandboxPath = this.getSandboxPath(
|
|
6753
|
+
const sandboxPath = this.getSandboxPath(path15);
|
|
5950
6754
|
const readResult = await this.sandboxOps.readFile(sessionId, sandboxPath);
|
|
5951
6755
|
if (!readResult.success || !readResult.content) {
|
|
5952
6756
|
result.error = readResult.error ?? "File not found in sandbox";
|
|
6757
|
+
await this.emitFileEvent(sessionId, {
|
|
6758
|
+
operation: "pull",
|
|
6759
|
+
direction: "from_sandbox",
|
|
6760
|
+
filePath: path15,
|
|
6761
|
+
success: false,
|
|
6762
|
+
error: result.error
|
|
6763
|
+
});
|
|
5953
6764
|
return result;
|
|
5954
6765
|
}
|
|
5955
6766
|
result.content = readResult.content;
|
|
6767
|
+
await this.emitFileEvent(sessionId, {
|
|
6768
|
+
operation: "pull",
|
|
6769
|
+
direction: "from_sandbox",
|
|
6770
|
+
filePath: path15,
|
|
6771
|
+
fileSize: readResult.content.length,
|
|
6772
|
+
success: true,
|
|
6773
|
+
newContent: readResult.content
|
|
6774
|
+
});
|
|
5956
6775
|
} catch (error) {
|
|
5957
6776
|
const errorMessage = extractErrorMessage(error);
|
|
5958
6777
|
result.error = `Sandbox read failed: ${errorMessage}`;
|
|
6778
|
+
await this.emitFileEvent(sessionId, {
|
|
6779
|
+
operation: "pull",
|
|
6780
|
+
direction: "from_sandbox",
|
|
6781
|
+
filePath: path15,
|
|
6782
|
+
success: false,
|
|
6783
|
+
error: errorMessage
|
|
6784
|
+
});
|
|
5959
6785
|
return result;
|
|
5960
6786
|
}
|
|
5961
6787
|
try {
|
|
5962
|
-
await this.fileStore.writeFile(sessionId,
|
|
6788
|
+
await this.fileStore.writeFile(sessionId, path15, result.content);
|
|
5963
6789
|
result.s3Written = true;
|
|
6790
|
+
await this.emitFileEvent(sessionId, {
|
|
6791
|
+
operation: "pull",
|
|
6792
|
+
direction: "to_s3",
|
|
6793
|
+
filePath: path15,
|
|
6794
|
+
fileSize: result.content.length,
|
|
6795
|
+
success: true,
|
|
6796
|
+
newContent: result.content
|
|
6797
|
+
});
|
|
5964
6798
|
} catch (error) {
|
|
5965
6799
|
const errorMessage = extractErrorMessage(error);
|
|
5966
6800
|
result.error = `S3 write failed: ${errorMessage}`;
|
|
5967
|
-
console.error(`[FILE_SYNC] S3 write failed in pullFile for session ${sessionId}, path ${
|
|
6801
|
+
console.error(`[FILE_SYNC] S3 write failed in pullFile for session ${sessionId}, path ${path15}:`, error);
|
|
6802
|
+
await this.emitFileEvent(sessionId, {
|
|
6803
|
+
operation: "pull",
|
|
6804
|
+
direction: "to_s3",
|
|
6805
|
+
filePath: path15,
|
|
6806
|
+
fileSize: result.content?.length,
|
|
6807
|
+
success: false,
|
|
6808
|
+
error: errorMessage
|
|
6809
|
+
});
|
|
5968
6810
|
}
|
|
5969
6811
|
return result;
|
|
5970
6812
|
}
|
|
5971
6813
|
/**
|
|
5972
6814
|
* Read a file (tries sandbox first, falls back to S3)
|
|
5973
6815
|
*/
|
|
5974
|
-
async readFile(sessionId,
|
|
6816
|
+
async readFile(sessionId, path15) {
|
|
5975
6817
|
if (this.sandboxOps?.isSandboxRunning(sessionId)) {
|
|
5976
6818
|
try {
|
|
5977
|
-
const sandboxPath = this.getSandboxPath(
|
|
6819
|
+
const sandboxPath = this.getSandboxPath(path15);
|
|
5978
6820
|
const readResult = await this.sandboxOps.readFile(sessionId, sandboxPath);
|
|
5979
6821
|
if (readResult.success && readResult.content) {
|
|
5980
6822
|
return { content: readResult.content, source: "sandbox" };
|
|
@@ -5983,7 +6825,7 @@ var init_sandbox_file_sync = __esm({
|
|
|
5983
6825
|
}
|
|
5984
6826
|
}
|
|
5985
6827
|
try {
|
|
5986
|
-
const content = await this.fileStore.readFile(sessionId,
|
|
6828
|
+
const content = await this.fileStore.readFile(sessionId, path15);
|
|
5987
6829
|
if (content) {
|
|
5988
6830
|
return { content, source: "s3" };
|
|
5989
6831
|
}
|
|
@@ -6000,21 +6842,48 @@ var init_sandbox_file_sync = __esm({
|
|
|
6000
6842
|
/**
|
|
6001
6843
|
* Delete a file from both S3 and sandbox
|
|
6002
6844
|
*/
|
|
6003
|
-
async deleteFile(sessionId,
|
|
6845
|
+
async deleteFile(sessionId, path15) {
|
|
6004
6846
|
const result = { s3Deleted: false, sandboxDeleted: false };
|
|
6005
6847
|
try {
|
|
6006
|
-
await this.fileStore.deleteFile(sessionId,
|
|
6848
|
+
await this.fileStore.deleteFile(sessionId, path15);
|
|
6007
6849
|
result.s3Deleted = true;
|
|
6850
|
+
await this.emitFileEvent(sessionId, {
|
|
6851
|
+
operation: "delete",
|
|
6852
|
+
direction: "from_s3",
|
|
6853
|
+
filePath: path15,
|
|
6854
|
+
success: true
|
|
6855
|
+
});
|
|
6008
6856
|
} catch (error) {
|
|
6009
6857
|
const errorMessage = extractErrorMessage(error);
|
|
6010
|
-
console.warn(`[FILE_SYNC] S3 delete failed for ${
|
|
6858
|
+
console.warn(`[FILE_SYNC] S3 delete failed for ${path15}: ${errorMessage}`);
|
|
6859
|
+
await this.emitFileEvent(sessionId, {
|
|
6860
|
+
operation: "delete",
|
|
6861
|
+
direction: "from_s3",
|
|
6862
|
+
filePath: path15,
|
|
6863
|
+
success: false,
|
|
6864
|
+
error: errorMessage
|
|
6865
|
+
});
|
|
6011
6866
|
}
|
|
6012
6867
|
if (this.sandboxOps?.isSandboxRunning(sessionId)) {
|
|
6013
6868
|
try {
|
|
6014
|
-
const sandboxPath = this.getSandboxPath(
|
|
6869
|
+
const sandboxPath = this.getSandboxPath(path15);
|
|
6015
6870
|
console.log(`[FILE_SYNC] Would delete ${sandboxPath} from sandbox`);
|
|
6016
6871
|
result.sandboxDeleted = true;
|
|
6017
|
-
|
|
6872
|
+
await this.emitFileEvent(sessionId, {
|
|
6873
|
+
operation: "delete",
|
|
6874
|
+
direction: "from_sandbox",
|
|
6875
|
+
filePath: path15,
|
|
6876
|
+
success: true
|
|
6877
|
+
});
|
|
6878
|
+
} catch (error) {
|
|
6879
|
+
const errorMessage = extractErrorMessage(error);
|
|
6880
|
+
await this.emitFileEvent(sessionId, {
|
|
6881
|
+
operation: "delete",
|
|
6882
|
+
direction: "from_sandbox",
|
|
6883
|
+
filePath: path15,
|
|
6884
|
+
success: false,
|
|
6885
|
+
error: errorMessage
|
|
6886
|
+
});
|
|
6018
6887
|
}
|
|
6019
6888
|
}
|
|
6020
6889
|
return result;
|
|
@@ -6035,14 +6904,36 @@ var init_sandbox_file_sync = __esm({
|
|
|
6035
6904
|
const content = await this.fileStore.readFile(sessionId, file.path);
|
|
6036
6905
|
if (!content) {
|
|
6037
6906
|
result.errors.push({ path: file.path, error: "File not found in S3" });
|
|
6907
|
+
await this.emitFileEvent(sessionId, {
|
|
6908
|
+
operation: "sync_to_sandbox",
|
|
6909
|
+
direction: "from_s3",
|
|
6910
|
+
filePath: file.path,
|
|
6911
|
+
success: false,
|
|
6912
|
+
error: "File not found in S3"
|
|
6913
|
+
});
|
|
6038
6914
|
continue;
|
|
6039
6915
|
}
|
|
6040
6916
|
const sandboxPath = this.getSandboxPath(file.path);
|
|
6041
6917
|
const writeResult = await this.sandboxOps.writeFile(sessionId, sandboxPath, content);
|
|
6042
6918
|
if (writeResult.success) {
|
|
6043
6919
|
result.fileCount++;
|
|
6920
|
+
await this.emitFileEvent(sessionId, {
|
|
6921
|
+
operation: "sync_to_sandbox",
|
|
6922
|
+
direction: "to_sandbox",
|
|
6923
|
+
filePath: file.path,
|
|
6924
|
+
fileSize: content.length,
|
|
6925
|
+
success: true
|
|
6926
|
+
});
|
|
6044
6927
|
} else {
|
|
6045
6928
|
result.errors.push({ path: file.path, error: writeResult.error ?? "Unknown error" });
|
|
6929
|
+
await this.emitFileEvent(sessionId, {
|
|
6930
|
+
operation: "sync_to_sandbox",
|
|
6931
|
+
direction: "to_sandbox",
|
|
6932
|
+
filePath: file.path,
|
|
6933
|
+
fileSize: content.length,
|
|
6934
|
+
success: false,
|
|
6935
|
+
error: writeResult.error
|
|
6936
|
+
});
|
|
6046
6937
|
}
|
|
6047
6938
|
} catch (error) {
|
|
6048
6939
|
const errorMessage = extractErrorMessage(error);
|
|
@@ -6050,6 +6941,13 @@ var init_sandbox_file_sync = __esm({
|
|
|
6050
6941
|
path: file.path,
|
|
6051
6942
|
error: errorMessage
|
|
6052
6943
|
});
|
|
6944
|
+
await this.emitFileEvent(sessionId, {
|
|
6945
|
+
operation: "sync_to_sandbox",
|
|
6946
|
+
direction: "to_sandbox",
|
|
6947
|
+
filePath: file.path,
|
|
6948
|
+
success: false,
|
|
6949
|
+
error: errorMessage
|
|
6950
|
+
});
|
|
6053
6951
|
}
|
|
6054
6952
|
}
|
|
6055
6953
|
console.log(`[FILE_SYNC] Synced ${result.fileCount} files to sandbox for session ${sessionId}`);
|
|
@@ -6082,8 +6980,23 @@ var init_sandbox_file_sync = __esm({
|
|
|
6082
6980
|
const pullResult = await this.pullFile(sessionId, filePath);
|
|
6083
6981
|
if (pullResult.s3Written) {
|
|
6084
6982
|
result.fileCount++;
|
|
6983
|
+
await this.emitFileEvent(sessionId, {
|
|
6984
|
+
operation: "sync_from_sandbox",
|
|
6985
|
+
direction: "to_s3",
|
|
6986
|
+
filePath,
|
|
6987
|
+
fileSize: pullResult.content?.length,
|
|
6988
|
+
success: true,
|
|
6989
|
+
newContent: pullResult.content ?? void 0
|
|
6990
|
+
});
|
|
6085
6991
|
} else if (pullResult.error) {
|
|
6086
6992
|
result.errors.push({ path: filePath, error: pullResult.error });
|
|
6993
|
+
await this.emitFileEvent(sessionId, {
|
|
6994
|
+
operation: "sync_from_sandbox",
|
|
6995
|
+
direction: "to_s3",
|
|
6996
|
+
filePath,
|
|
6997
|
+
success: false,
|
|
6998
|
+
error: pullResult.error
|
|
6999
|
+
});
|
|
6087
7000
|
}
|
|
6088
7001
|
} catch (error) {
|
|
6089
7002
|
const errorMessage = extractErrorMessage(error);
|
|
@@ -6091,6 +7004,13 @@ var init_sandbox_file_sync = __esm({
|
|
|
6091
7004
|
path: filePath,
|
|
6092
7005
|
error: errorMessage
|
|
6093
7006
|
});
|
|
7007
|
+
await this.emitFileEvent(sessionId, {
|
|
7008
|
+
operation: "sync_from_sandbox",
|
|
7009
|
+
direction: "to_s3",
|
|
7010
|
+
filePath,
|
|
7011
|
+
success: false,
|
|
7012
|
+
error: errorMessage
|
|
7013
|
+
});
|
|
6094
7014
|
}
|
|
6095
7015
|
}
|
|
6096
7016
|
console.log(`[FILE_SYNC] Synced ${result.fileCount} files from sandbox to S3 for session ${sessionId}`);
|
|
@@ -6099,16 +7019,526 @@ var init_sandbox_file_sync = __esm({
|
|
|
6099
7019
|
/**
|
|
6100
7020
|
* Get a signed URL for direct file download
|
|
6101
7021
|
*/
|
|
6102
|
-
async getSignedUrl(sessionId,
|
|
6103
|
-
return this.fileStore.getSignedUrl(sessionId,
|
|
7022
|
+
async getSignedUrl(sessionId, path15, expiresIn) {
|
|
7023
|
+
return this.fileStore.getSignedUrl(sessionId, path15, expiresIn);
|
|
6104
7024
|
}
|
|
6105
7025
|
/**
|
|
6106
7026
|
* Get a signed URL for direct file upload
|
|
6107
7027
|
*/
|
|
6108
|
-
async getUploadUrl(sessionId,
|
|
6109
|
-
return this.fileStore.getUploadUrl(sessionId,
|
|
7028
|
+
async getUploadUrl(sessionId, path15, expiresIn) {
|
|
7029
|
+
return this.fileStore.getUploadUrl(sessionId, path15, expiresIn);
|
|
7030
|
+
}
|
|
7031
|
+
// ===========================================================================
|
|
7032
|
+
// File Watching API
|
|
7033
|
+
// ===========================================================================
|
|
7034
|
+
/**
|
|
7035
|
+
* Start watching a sandbox for file changes and auto-sync to S3.
|
|
7036
|
+
*
|
|
7037
|
+
* For remote sandboxes (Vercel, E2B, etc.), uses polling.
|
|
7038
|
+
* For local sandboxes, can optionally use native file watching via chokidar.
|
|
7039
|
+
*
|
|
7040
|
+
* @param sessionId - Session to watch
|
|
7041
|
+
* @param options - Watch options (overrides constructor defaults)
|
|
7042
|
+
*/
|
|
7043
|
+
async startWatching(sessionId, options) {
|
|
7044
|
+
const opts = { ...this.defaultWatchOptions, ...options };
|
|
7045
|
+
await this.stopWatching(sessionId);
|
|
7046
|
+
const handleFileChange = async (event) => {
|
|
7047
|
+
console.log(`[FILE_SYNC] File change detected: ${event.type} ${event.relativePath}`);
|
|
7048
|
+
if (this.webhookConfig) {
|
|
7049
|
+
this.sendFileChangeWebhook(event);
|
|
7050
|
+
}
|
|
7051
|
+
for (const subscriber of this.fileChangeSubscribers) {
|
|
7052
|
+
try {
|
|
7053
|
+
await subscriber(event);
|
|
7054
|
+
} catch (error) {
|
|
7055
|
+
console.error("[FILE_SYNC] Error in file change subscriber:", error);
|
|
7056
|
+
}
|
|
7057
|
+
}
|
|
7058
|
+
if (event.type === "add" || event.type === "change") {
|
|
7059
|
+
try {
|
|
7060
|
+
const pullResult = await this.pullFile(sessionId, event.relativePath);
|
|
7061
|
+
if (pullResult.s3Written) {
|
|
7062
|
+
console.log(`[FILE_SYNC] Auto-synced ${event.relativePath} to S3`);
|
|
7063
|
+
} else if (pullResult.error) {
|
|
7064
|
+
console.warn(`[FILE_SYNC] Failed to auto-sync ${event.relativePath}: ${pullResult.error}`);
|
|
7065
|
+
}
|
|
7066
|
+
} catch (error) {
|
|
7067
|
+
console.error(`[FILE_SYNC] Error auto-syncing ${event.relativePath}:`, error);
|
|
7068
|
+
}
|
|
7069
|
+
} else if (event.type === "unlink") {
|
|
7070
|
+
try {
|
|
7071
|
+
await this.deleteFile(sessionId, event.relativePath);
|
|
7072
|
+
console.log(`[FILE_SYNC] Auto-deleted ${event.relativePath} from S3`);
|
|
7073
|
+
} catch (error) {
|
|
7074
|
+
console.error(`[FILE_SYNC] Error auto-deleting ${event.relativePath}:`, error);
|
|
7075
|
+
}
|
|
7076
|
+
}
|
|
7077
|
+
};
|
|
7078
|
+
if (opts.useLocalWatcher && opts.localPath) {
|
|
7079
|
+
const watcher = new exports.SandboxFileWatcher({
|
|
7080
|
+
sessionId,
|
|
7081
|
+
watchPath: opts.localPath,
|
|
7082
|
+
debounceMs: opts.debounceMs ?? 300,
|
|
7083
|
+
ignored: opts.ignored ?? ["**/node_modules/**", "**/.git/**"],
|
|
7084
|
+
onFileChange: handleFileChange,
|
|
7085
|
+
onError: (error) => {
|
|
7086
|
+
console.error(`[FILE_SYNC] Local watcher error for session ${sessionId}:`, error);
|
|
7087
|
+
}
|
|
7088
|
+
});
|
|
7089
|
+
await watcher.start();
|
|
7090
|
+
this.localWatchers.set(sessionId, watcher);
|
|
7091
|
+
console.log(`[FILE_SYNC] Started local file watching for session ${sessionId}`);
|
|
7092
|
+
} else {
|
|
7093
|
+
if (!this.sandboxOps) {
|
|
7094
|
+
throw new Error("Sandbox operations not configured. Call setSandboxOperations first.");
|
|
7095
|
+
}
|
|
7096
|
+
const watcher = new exports.RemoteSandboxFileWatcher({
|
|
7097
|
+
sessionId,
|
|
7098
|
+
sandboxOps: this.sandboxOps,
|
|
7099
|
+
basePath: this.sandboxBasePath,
|
|
7100
|
+
pollIntervalMs: opts.pollIntervalMs ?? 2e3,
|
|
7101
|
+
ignored: opts.ignored ?? ["**/node_modules/**", "**/.git/**"],
|
|
7102
|
+
onFileChange: handleFileChange,
|
|
7103
|
+
onError: (error) => {
|
|
7104
|
+
console.error(`[FILE_SYNC] Remote watcher error for session ${sessionId}:`, error);
|
|
7105
|
+
}
|
|
7106
|
+
});
|
|
7107
|
+
await watcher.start();
|
|
7108
|
+
this.remoteWatchers.set(sessionId, watcher);
|
|
7109
|
+
console.log(`[FILE_SYNC] Started remote file watching for session ${sessionId}`);
|
|
7110
|
+
}
|
|
7111
|
+
}
|
|
7112
|
+
/**
|
|
7113
|
+
* Stop watching a session's sandbox
|
|
7114
|
+
*/
|
|
7115
|
+
async stopWatching(sessionId) {
|
|
7116
|
+
const remoteWatcher = this.remoteWatchers.get(sessionId);
|
|
7117
|
+
if (remoteWatcher) {
|
|
7118
|
+
await remoteWatcher.stop();
|
|
7119
|
+
this.remoteWatchers.delete(sessionId);
|
|
7120
|
+
}
|
|
7121
|
+
const localWatcher = this.localWatchers.get(sessionId);
|
|
7122
|
+
if (localWatcher) {
|
|
7123
|
+
await localWatcher.stop();
|
|
7124
|
+
this.localWatchers.delete(sessionId);
|
|
7125
|
+
}
|
|
7126
|
+
}
|
|
7127
|
+
/**
|
|
7128
|
+
* Check if a session is being watched
|
|
7129
|
+
*/
|
|
7130
|
+
isWatching(sessionId) {
|
|
7131
|
+
return this.remoteWatchers.has(sessionId) || this.localWatchers.has(sessionId);
|
|
7132
|
+
}
|
|
7133
|
+
/**
|
|
7134
|
+
* Subscribe to file change events across all watched sessions.
|
|
7135
|
+
* This allows external applications to react to file changes.
|
|
7136
|
+
*
|
|
7137
|
+
* @param callback - Function to call when a file changes
|
|
7138
|
+
* @returns Unsubscribe function
|
|
7139
|
+
*
|
|
7140
|
+
* @example
|
|
7141
|
+
* ```typescript
|
|
7142
|
+
* const unsubscribe = fileSync.onFileChange((event) => {
|
|
7143
|
+
* console.log(`File ${event.relativePath} was ${event.type}d`);
|
|
7144
|
+
* // Update your application state here
|
|
7145
|
+
* });
|
|
7146
|
+
*
|
|
7147
|
+
* // Later, when done
|
|
7148
|
+
* unsubscribe();
|
|
7149
|
+
* ```
|
|
7150
|
+
*/
|
|
7151
|
+
onFileChange(callback) {
|
|
7152
|
+
this.fileChangeSubscribers.add(callback);
|
|
7153
|
+
return () => this.fileChangeSubscribers.delete(callback);
|
|
7154
|
+
}
|
|
7155
|
+
/**
|
|
7156
|
+
* Remove a file change subscriber
|
|
7157
|
+
*/
|
|
7158
|
+
offFileChange(callback) {
|
|
7159
|
+
this.fileChangeSubscribers.delete(callback);
|
|
7160
|
+
}
|
|
7161
|
+
/**
|
|
7162
|
+
* Stop all watchers and cleanup
|
|
7163
|
+
*/
|
|
7164
|
+
async stopAllWatching() {
|
|
7165
|
+
const remotePromises = Array.from(this.remoteWatchers.values()).map((w) => w.stop());
|
|
7166
|
+
const localPromises = Array.from(this.localWatchers.values()).map((w) => w.stop());
|
|
7167
|
+
await Promise.all([...remotePromises, ...localPromises]);
|
|
7168
|
+
this.remoteWatchers.clear();
|
|
7169
|
+
this.localWatchers.clear();
|
|
7170
|
+
}
|
|
7171
|
+
/**
|
|
7172
|
+
* Get watching status for all sessions
|
|
7173
|
+
*/
|
|
7174
|
+
getWatchingStatus() {
|
|
7175
|
+
const statuses = [];
|
|
7176
|
+
for (const [sessionId, watcher] of this.remoteWatchers) {
|
|
7177
|
+
statuses.push({ sessionId, type: "remote", isActive: watcher.isActive() });
|
|
7178
|
+
}
|
|
7179
|
+
for (const [sessionId, watcher] of this.localWatchers) {
|
|
7180
|
+
statuses.push({ sessionId, type: "local", isActive: watcher.isActive() });
|
|
7181
|
+
}
|
|
7182
|
+
return statuses;
|
|
7183
|
+
}
|
|
7184
|
+
};
|
|
7185
|
+
}
|
|
7186
|
+
});
|
|
7187
|
+
|
|
7188
|
+
// src/runtime/in-sandbox-watcher.ts
|
|
7189
|
+
function createInSandboxWatcher(options) {
|
|
7190
|
+
return new exports.InSandboxWatcher(options);
|
|
7191
|
+
}
|
|
7192
|
+
function getInSandboxWatcherManager() {
|
|
7193
|
+
if (!globalInSandboxManager) {
|
|
7194
|
+
globalInSandboxManager = new exports.InSandboxWatcherManager();
|
|
7195
|
+
}
|
|
7196
|
+
return globalInSandboxManager;
|
|
7197
|
+
}
|
|
7198
|
+
function createInSandboxWatcherManager() {
|
|
7199
|
+
return new exports.InSandboxWatcherManager();
|
|
7200
|
+
}
|
|
7201
|
+
var WATCHER_SCRIPT; exports.InSandboxWatcher = void 0; exports.InSandboxWatcherManager = void 0; var globalInSandboxManager;
|
|
7202
|
+
var init_in_sandbox_watcher = __esm({
|
|
7203
|
+
"src/runtime/in-sandbox-watcher.ts"() {
|
|
7204
|
+
init_vercel_sandbox_executor();
|
|
7205
|
+
WATCHER_SCRIPT = `
|
|
7206
|
+
const fs = require('fs');
|
|
7207
|
+
const path = require('path');
|
|
7208
|
+
|
|
7209
|
+
const watchPath = process.argv[2] || '.';
|
|
7210
|
+
const ignored = new Set(['node_modules', '.git', '.cache', '__pycache__']);
|
|
7211
|
+
|
|
7212
|
+
// Track all watchers for cleanup
|
|
7213
|
+
const watchers = new Map();
|
|
7214
|
+
|
|
7215
|
+
// Debounce map to coalesce rapid changes
|
|
7216
|
+
const pending = new Map();
|
|
7217
|
+
const DEBOUNCE_MS = 100;
|
|
7218
|
+
|
|
7219
|
+
function emit(type, filePath) {
|
|
7220
|
+
const event = {
|
|
7221
|
+
type,
|
|
7222
|
+
path: filePath,
|
|
7223
|
+
timestamp: Date.now()
|
|
7224
|
+
};
|
|
7225
|
+
console.log(JSON.stringify(event));
|
|
7226
|
+
}
|
|
7227
|
+
|
|
7228
|
+
function shouldIgnore(name) {
|
|
7229
|
+
return ignored.has(name) || name.startsWith('.');
|
|
7230
|
+
}
|
|
7231
|
+
|
|
7232
|
+
function watchDir(dir) {
|
|
7233
|
+
try {
|
|
7234
|
+
const watcher = fs.watch(dir, { persistent: true }, (eventType, filename) => {
|
|
7235
|
+
if (!filename || shouldIgnore(filename)) return;
|
|
7236
|
+
|
|
7237
|
+
const fullPath = path.join(dir, filename);
|
|
7238
|
+
const key = fullPath;
|
|
7239
|
+
|
|
7240
|
+
// Debounce
|
|
7241
|
+
if (pending.has(key)) {
|
|
7242
|
+
clearTimeout(pending.get(key));
|
|
7243
|
+
}
|
|
7244
|
+
|
|
7245
|
+
pending.set(key, setTimeout(() => {
|
|
7246
|
+
pending.delete(key);
|
|
7247
|
+
|
|
7248
|
+
fs.stat(fullPath, (err, stats) => {
|
|
7249
|
+
if (err) {
|
|
7250
|
+
if (err.code === 'ENOENT') {
|
|
7251
|
+
emit('unlink', fullPath);
|
|
7252
|
+
// Stop watching if it was a directory
|
|
7253
|
+
if (watchers.has(fullPath)) {
|
|
7254
|
+
watchers.get(fullPath).close();
|
|
7255
|
+
watchers.delete(fullPath);
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
7258
|
+
} else {
|
|
7259
|
+
const type = eventType === 'rename' ? 'add' : 'change';
|
|
7260
|
+
emit(type, fullPath);
|
|
7261
|
+
|
|
7262
|
+
// If it's a new directory, start watching it
|
|
7263
|
+
if (stats.isDirectory() && !watchers.has(fullPath)) {
|
|
7264
|
+
watchDir(fullPath);
|
|
7265
|
+
}
|
|
7266
|
+
}
|
|
7267
|
+
});
|
|
7268
|
+
}, DEBOUNCE_MS));
|
|
7269
|
+
});
|
|
7270
|
+
|
|
7271
|
+
watchers.set(dir, watcher);
|
|
7272
|
+
|
|
7273
|
+
// Watch subdirectories
|
|
7274
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
7275
|
+
for (const entry of entries) {
|
|
7276
|
+
if (entry.isDirectory() && !shouldIgnore(entry.name)) {
|
|
7277
|
+
watchDir(path.join(dir, entry.name));
|
|
7278
|
+
}
|
|
7279
|
+
}
|
|
7280
|
+
} catch (err) {
|
|
7281
|
+
// Directory may not exist or be inaccessible
|
|
7282
|
+
console.error(JSON.stringify({ error: err.message, dir }));
|
|
7283
|
+
}
|
|
7284
|
+
}
|
|
7285
|
+
|
|
7286
|
+
// Start watching
|
|
7287
|
+
watchDir(watchPath);
|
|
7288
|
+
|
|
7289
|
+
// Keep alive
|
|
7290
|
+
process.on('SIGTERM', () => {
|
|
7291
|
+
for (const watcher of watchers.values()) {
|
|
7292
|
+
watcher.close();
|
|
7293
|
+
}
|
|
7294
|
+
process.exit(0);
|
|
7295
|
+
});
|
|
7296
|
+
|
|
7297
|
+
// Heartbeat to indicate we're running
|
|
7298
|
+
setInterval(() => {
|
|
7299
|
+
console.log(JSON.stringify({ heartbeat: true, timestamp: Date.now() }));
|
|
7300
|
+
}, 5000);
|
|
7301
|
+
|
|
7302
|
+
console.log(JSON.stringify({ started: true, path: watchPath }));
|
|
7303
|
+
`;
|
|
7304
|
+
exports.InSandboxWatcher = class {
|
|
7305
|
+
sessionId;
|
|
7306
|
+
watchPath;
|
|
7307
|
+
outputPollIntervalMs;
|
|
7308
|
+
sandboxState = null;
|
|
7309
|
+
outputPollTimer = null;
|
|
7310
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
7311
|
+
isRunning = false;
|
|
7312
|
+
startedAt;
|
|
7313
|
+
lastHeartbeat;
|
|
7314
|
+
lastOutputPosition = 0;
|
|
7315
|
+
onError;
|
|
7316
|
+
onReady;
|
|
7317
|
+
constructor(options) {
|
|
7318
|
+
this.sessionId = options.sessionId;
|
|
7319
|
+
this.watchPath = options.watchPath;
|
|
7320
|
+
this.outputPollIntervalMs = options.outputPollIntervalMs ?? 1e3;
|
|
7321
|
+
this.onError = options.onError;
|
|
7322
|
+
this.onReady = options.onReady;
|
|
7323
|
+
if (options.onFileChange) {
|
|
7324
|
+
this.subscribers.add(options.onFileChange);
|
|
7325
|
+
}
|
|
7326
|
+
}
|
|
7327
|
+
/**
|
|
7328
|
+
* Start the watcher process inside the sandbox
|
|
7329
|
+
*/
|
|
7330
|
+
async start() {
|
|
7331
|
+
if (this.isRunning) {
|
|
7332
|
+
console.warn(`[IN_SANDBOX_WATCHER] Already running for session ${this.sessionId}`);
|
|
7333
|
+
return;
|
|
7334
|
+
}
|
|
7335
|
+
try {
|
|
7336
|
+
this.sandboxState = await getOrCreateSandbox({
|
|
7337
|
+
sessionId: this.sessionId,
|
|
7338
|
+
runtime: "node22",
|
|
7339
|
+
timeout: 600
|
|
7340
|
+
});
|
|
7341
|
+
const { sandbox } = this.sandboxState;
|
|
7342
|
+
const scriptPath = "/tmp/.file-watcher.js";
|
|
7343
|
+
const outputPath = "/tmp/.file-watcher-output.log";
|
|
7344
|
+
const writeScriptCmd = `cat > ${scriptPath} << 'WATCHER_EOF'
|
|
7345
|
+
${WATCHER_SCRIPT}
|
|
7346
|
+
WATCHER_EOF`;
|
|
7347
|
+
await sandbox.runCommand({
|
|
7348
|
+
cmd: "sh",
|
|
7349
|
+
args: ["-c", writeScriptCmd]
|
|
7350
|
+
});
|
|
7351
|
+
const startCmd = `nohup node ${scriptPath} "${this.watchPath}" > ${outputPath} 2>&1 &`;
|
|
7352
|
+
await sandbox.runCommand({
|
|
7353
|
+
cmd: "sh",
|
|
7354
|
+
args: ["-c", startCmd]
|
|
7355
|
+
});
|
|
7356
|
+
console.log(`[IN_SANDBOX_WATCHER] Started watcher in sandbox for ${this.watchPath}`);
|
|
7357
|
+
this.isRunning = true;
|
|
7358
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
7359
|
+
this.outputPollTimer = setInterval(async () => {
|
|
7360
|
+
try {
|
|
7361
|
+
await this.pollOutput(outputPath);
|
|
7362
|
+
} catch (error) {
|
|
7363
|
+
console.error("[IN_SANDBOX_WATCHER] Error polling output:", error);
|
|
7364
|
+
if (this.onError && error instanceof Error) {
|
|
7365
|
+
this.onError(error);
|
|
7366
|
+
}
|
|
7367
|
+
}
|
|
7368
|
+
}, this.outputPollIntervalMs);
|
|
7369
|
+
setTimeout(() => {
|
|
7370
|
+
this.pollOutput(outputPath).catch(console.error);
|
|
7371
|
+
}, 500);
|
|
7372
|
+
} catch (error) {
|
|
7373
|
+
console.error(`[IN_SANDBOX_WATCHER] Failed to start watcher:`, error);
|
|
7374
|
+
throw error;
|
|
7375
|
+
}
|
|
7376
|
+
}
|
|
7377
|
+
/**
|
|
7378
|
+
* Poll the output file for new events
|
|
7379
|
+
*/
|
|
7380
|
+
async pollOutput(outputPath) {
|
|
7381
|
+
if (!this.sandboxState?.sandbox) return;
|
|
7382
|
+
try {
|
|
7383
|
+
const result = await this.sandboxState.sandbox.runCommand({
|
|
7384
|
+
cmd: "sh",
|
|
7385
|
+
args: ["-c", `tail -c +${this.lastOutputPosition + 1} ${outputPath} 2>/dev/null || true`]
|
|
7386
|
+
});
|
|
7387
|
+
const output = await result.stdout();
|
|
7388
|
+
if (output && output.trim()) {
|
|
7389
|
+
const sizeResult = await this.sandboxState.sandbox.runCommand({
|
|
7390
|
+
cmd: "sh",
|
|
7391
|
+
args: ["-c", `stat -c%s ${outputPath} 2>/dev/null || echo 0`]
|
|
7392
|
+
});
|
|
7393
|
+
const sizeStr = await sizeResult.stdout();
|
|
7394
|
+
this.lastOutputPosition = parseInt(sizeStr.trim(), 10) || 0;
|
|
7395
|
+
this.processOutput(output);
|
|
7396
|
+
}
|
|
7397
|
+
} catch {
|
|
7398
|
+
}
|
|
7399
|
+
}
|
|
7400
|
+
/**
|
|
7401
|
+
* Process output from the watcher script
|
|
7402
|
+
*/
|
|
7403
|
+
processOutput(stdout) {
|
|
7404
|
+
const lines = stdout.split("\n").filter(Boolean);
|
|
7405
|
+
for (const line of lines) {
|
|
7406
|
+
try {
|
|
7407
|
+
const data = JSON.parse(line);
|
|
7408
|
+
if (data.started) {
|
|
7409
|
+
console.log(`[IN_SANDBOX_WATCHER] Watcher started for ${data.path}`);
|
|
7410
|
+
if (this.onReady) {
|
|
7411
|
+
this.onReady();
|
|
7412
|
+
}
|
|
7413
|
+
} else if (data.heartbeat) {
|
|
7414
|
+
this.lastHeartbeat = new Date(data.timestamp);
|
|
7415
|
+
} else if (data.error) {
|
|
7416
|
+
console.warn(`[IN_SANDBOX_WATCHER] Error in sandbox:`, data.error);
|
|
7417
|
+
} else if (data.type && data.path) {
|
|
7418
|
+
this.emitEvent({
|
|
7419
|
+
type: data.type,
|
|
7420
|
+
relativePath: data.path.replace(/^\.\//, ""),
|
|
7421
|
+
absolutePath: data.path,
|
|
7422
|
+
sessionId: this.sessionId,
|
|
7423
|
+
timestamp: new Date(data.timestamp)
|
|
7424
|
+
});
|
|
7425
|
+
}
|
|
7426
|
+
} catch {
|
|
7427
|
+
}
|
|
7428
|
+
}
|
|
7429
|
+
}
|
|
7430
|
+
/**
|
|
7431
|
+
* Stop the watcher process
|
|
7432
|
+
*/
|
|
7433
|
+
async stop() {
|
|
7434
|
+
if (!this.isRunning) {
|
|
7435
|
+
return;
|
|
7436
|
+
}
|
|
7437
|
+
if (this.outputPollTimer) {
|
|
7438
|
+
clearInterval(this.outputPollTimer);
|
|
7439
|
+
this.outputPollTimer = null;
|
|
7440
|
+
}
|
|
7441
|
+
try {
|
|
7442
|
+
if (this.sandboxState?.sandbox) {
|
|
7443
|
+
await this.sandboxState.sandbox.runCommand({
|
|
7444
|
+
cmd: "sh",
|
|
7445
|
+
args: ["-c", "pkill -f file-watcher.js 2>/dev/null || true"]
|
|
7446
|
+
});
|
|
7447
|
+
await this.sandboxState.sandbox.runCommand({
|
|
7448
|
+
cmd: "sh",
|
|
7449
|
+
args: ["-c", "rm -f /tmp/.file-watcher.js /tmp/.file-watcher-output.log"]
|
|
7450
|
+
});
|
|
7451
|
+
}
|
|
7452
|
+
} catch {
|
|
7453
|
+
}
|
|
7454
|
+
this.isRunning = false;
|
|
7455
|
+
this.sandboxState = null;
|
|
7456
|
+
this.lastOutputPosition = 0;
|
|
7457
|
+
console.log(`[IN_SANDBOX_WATCHER] Stopped watcher for session ${this.sessionId}`);
|
|
7458
|
+
}
|
|
7459
|
+
/**
|
|
7460
|
+
* Subscribe to file change events
|
|
7461
|
+
*/
|
|
7462
|
+
subscribe(callback) {
|
|
7463
|
+
this.subscribers.add(callback);
|
|
7464
|
+
return () => this.subscribers.delete(callback);
|
|
7465
|
+
}
|
|
7466
|
+
/**
|
|
7467
|
+
* Check if watcher is running
|
|
7468
|
+
*/
|
|
7469
|
+
isActive() {
|
|
7470
|
+
return this.isRunning;
|
|
7471
|
+
}
|
|
7472
|
+
/**
|
|
7473
|
+
* Get watcher status
|
|
7474
|
+
*/
|
|
7475
|
+
getStatus() {
|
|
7476
|
+
return {
|
|
7477
|
+
sessionId: this.sessionId,
|
|
7478
|
+
watchPath: this.watchPath,
|
|
7479
|
+
isRunning: this.isRunning,
|
|
7480
|
+
startedAt: this.startedAt,
|
|
7481
|
+
lastHeartbeat: this.lastHeartbeat
|
|
7482
|
+
};
|
|
7483
|
+
}
|
|
7484
|
+
/**
|
|
7485
|
+
* Emit event to all subscribers
|
|
7486
|
+
*/
|
|
7487
|
+
async emitEvent(event) {
|
|
7488
|
+
for (const callback of this.subscribers) {
|
|
7489
|
+
try {
|
|
7490
|
+
await callback(event);
|
|
7491
|
+
} catch (error) {
|
|
7492
|
+
console.error("[IN_SANDBOX_WATCHER] Error in subscriber callback:", error);
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
}
|
|
7496
|
+
};
|
|
7497
|
+
exports.InSandboxWatcherManager = class {
|
|
7498
|
+
watchers = /* @__PURE__ */ new Map();
|
|
7499
|
+
/**
|
|
7500
|
+
* Start watching a sandbox
|
|
7501
|
+
*/
|
|
7502
|
+
async startWatching(options) {
|
|
7503
|
+
const { sessionId } = options;
|
|
7504
|
+
await this.stopWatching(sessionId);
|
|
7505
|
+
const watcher = new exports.InSandboxWatcher(options);
|
|
7506
|
+
await watcher.start();
|
|
7507
|
+
this.watchers.set(sessionId, watcher);
|
|
7508
|
+
return watcher;
|
|
7509
|
+
}
|
|
7510
|
+
/**
|
|
7511
|
+
* Stop watching a session
|
|
7512
|
+
*/
|
|
7513
|
+
async stopWatching(sessionId) {
|
|
7514
|
+
const watcher = this.watchers.get(sessionId);
|
|
7515
|
+
if (watcher) {
|
|
7516
|
+
await watcher.stop();
|
|
7517
|
+
this.watchers.delete(sessionId);
|
|
7518
|
+
}
|
|
7519
|
+
}
|
|
7520
|
+
/**
|
|
7521
|
+
* Get watcher for a session
|
|
7522
|
+
*/
|
|
7523
|
+
getWatcher(sessionId) {
|
|
7524
|
+
return this.watchers.get(sessionId);
|
|
7525
|
+
}
|
|
7526
|
+
/**
|
|
7527
|
+
* Check if watching
|
|
7528
|
+
*/
|
|
7529
|
+
isWatching(sessionId) {
|
|
7530
|
+
return this.watchers.get(sessionId)?.isActive() ?? false;
|
|
7531
|
+
}
|
|
7532
|
+
/**
|
|
7533
|
+
* Stop all watchers
|
|
7534
|
+
*/
|
|
7535
|
+
async stopAll() {
|
|
7536
|
+
const promises = Array.from(this.watchers.values()).map((w) => w.stop());
|
|
7537
|
+
await Promise.all(promises);
|
|
7538
|
+
this.watchers.clear();
|
|
6110
7539
|
}
|
|
6111
7540
|
};
|
|
7541
|
+
globalInSandboxManager = null;
|
|
6112
7542
|
}
|
|
6113
7543
|
});
|
|
6114
7544
|
|
|
@@ -6220,8 +7650,8 @@ function checkSecurityConfig(config) {
|
|
|
6220
7650
|
}
|
|
6221
7651
|
return { issues, warnings, recommendations };
|
|
6222
7652
|
}
|
|
6223
|
-
function isSensitivePath(
|
|
6224
|
-
const normalized =
|
|
7653
|
+
function isSensitivePath(path15) {
|
|
7654
|
+
const normalized = path15.replace(/\\/g, "/").toLowerCase();
|
|
6225
7655
|
for (const sensitive of exports.SENSITIVE_PATHS) {
|
|
6226
7656
|
const pattern = sensitive.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/~/g, ".*");
|
|
6227
7657
|
if (new RegExp(pattern).test(normalized)) {
|
|
@@ -6238,6 +7668,8 @@ var init_runtime = __esm({
|
|
|
6238
7668
|
init_vercel_sandbox_executor();
|
|
6239
7669
|
init_sandbox_file_sync();
|
|
6240
7670
|
init_sandbox_pool();
|
|
7671
|
+
init_sandbox_file_watcher();
|
|
7672
|
+
init_in_sandbox_watcher();
|
|
6241
7673
|
exports.RuntimePresets = {
|
|
6242
7674
|
/**
|
|
6243
7675
|
* Development mode - no isolation, for local testing
|
|
@@ -6348,8 +7780,8 @@ var init_runtime = __esm({
|
|
|
6348
7780
|
this.config.pattern = pattern;
|
|
6349
7781
|
return this;
|
|
6350
7782
|
}
|
|
6351
|
-
workingDirectory(
|
|
6352
|
-
this.config.workingDirectory =
|
|
7783
|
+
workingDirectory(path15) {
|
|
7784
|
+
this.config.workingDirectory = path15;
|
|
6353
7785
|
return this;
|
|
6354
7786
|
}
|
|
6355
7787
|
sandbox(config) {
|
|
@@ -6587,7 +8019,7 @@ var init_local_provider = __esm({
|
|
|
6587
8019
|
maxFileSize;
|
|
6588
8020
|
constructor(source, options = {}) {
|
|
6589
8021
|
this.source = source;
|
|
6590
|
-
this.basePath =
|
|
8022
|
+
this.basePath = path4__namespace.resolve(source.localPath);
|
|
6591
8023
|
this.maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
6592
8024
|
}
|
|
6593
8025
|
/**
|
|
@@ -6595,8 +8027,8 @@ var init_local_provider = __esm({
|
|
|
6595
8027
|
* Prevents path traversal attacks.
|
|
6596
8028
|
*/
|
|
6597
8029
|
resolvePath(relativePath) {
|
|
6598
|
-
const normalized =
|
|
6599
|
-
const resolved =
|
|
8030
|
+
const normalized = path4__namespace.normalize(relativePath);
|
|
8031
|
+
const resolved = path4__namespace.resolve(this.basePath, normalized);
|
|
6600
8032
|
if (!resolved.startsWith(this.basePath)) {
|
|
6601
8033
|
throw new Error(`Path traversal detected: ${relativePath}`);
|
|
6602
8034
|
}
|
|
@@ -6605,15 +8037,15 @@ var init_local_provider = __esm({
|
|
|
6605
8037
|
async listFiles(relativePath) {
|
|
6606
8038
|
const dirPath = this.resolvePath(relativePath || ".");
|
|
6607
8039
|
try {
|
|
6608
|
-
const entries = await
|
|
8040
|
+
const entries = await fs10.promises.readdir(dirPath, { withFileTypes: true });
|
|
6609
8041
|
const fileEntries = await Promise.all(
|
|
6610
8042
|
entries.map(async (entry) => {
|
|
6611
|
-
const entryPath =
|
|
6612
|
-
const fullPath =
|
|
8043
|
+
const entryPath = path4__namespace.join(relativePath || ".", entry.name);
|
|
8044
|
+
const fullPath = path4__namespace.join(dirPath, entry.name);
|
|
6613
8045
|
let size;
|
|
6614
8046
|
let modifiedAt;
|
|
6615
8047
|
try {
|
|
6616
|
-
const stats = await
|
|
8048
|
+
const stats = await fs10.promises.stat(fullPath);
|
|
6617
8049
|
size = entry.isFile() ? stats.size : void 0;
|
|
6618
8050
|
modifiedAt = stats.mtime;
|
|
6619
8051
|
} catch {
|
|
@@ -6647,7 +8079,7 @@ var init_local_provider = __esm({
|
|
|
6647
8079
|
async readFile(relativePath) {
|
|
6648
8080
|
const filePath = this.resolvePath(relativePath);
|
|
6649
8081
|
try {
|
|
6650
|
-
const stats = await
|
|
8082
|
+
const stats = await fs10.promises.stat(filePath);
|
|
6651
8083
|
if (stats.isDirectory()) {
|
|
6652
8084
|
throw new Error(`Cannot read directory as file: ${relativePath}`);
|
|
6653
8085
|
}
|
|
@@ -6657,7 +8089,7 @@ var init_local_provider = __esm({
|
|
|
6657
8089
|
);
|
|
6658
8090
|
}
|
|
6659
8091
|
try {
|
|
6660
|
-
const content = await
|
|
8092
|
+
const content = await fs10.promises.readFile(filePath, "utf-8");
|
|
6661
8093
|
return {
|
|
6662
8094
|
path: relativePath,
|
|
6663
8095
|
content,
|
|
@@ -6665,7 +8097,7 @@ var init_local_provider = __esm({
|
|
|
6665
8097
|
size: stats.size
|
|
6666
8098
|
};
|
|
6667
8099
|
} catch {
|
|
6668
|
-
const buffer = await
|
|
8100
|
+
const buffer = await fs10.promises.readFile(filePath);
|
|
6669
8101
|
return {
|
|
6670
8102
|
path: relativePath,
|
|
6671
8103
|
content: buffer.toString("base64"),
|
|
@@ -6683,7 +8115,7 @@ var init_local_provider = __esm({
|
|
|
6683
8115
|
async exists(relativePath) {
|
|
6684
8116
|
try {
|
|
6685
8117
|
const fullPath = this.resolvePath(relativePath);
|
|
6686
|
-
const stats = await
|
|
8118
|
+
const stats = await fs10.promises.stat(fullPath);
|
|
6687
8119
|
return stats.isDirectory() ? "directory" : "file";
|
|
6688
8120
|
} catch {
|
|
6689
8121
|
return null;
|
|
@@ -6692,7 +8124,7 @@ var init_local_provider = __esm({
|
|
|
6692
8124
|
async copyTo(sourcePath, targetPath, recursive = true) {
|
|
6693
8125
|
const sourceFullPath = this.resolvePath(sourcePath);
|
|
6694
8126
|
try {
|
|
6695
|
-
const stats = await
|
|
8127
|
+
const stats = await fs10.promises.stat(sourceFullPath);
|
|
6696
8128
|
if (stats.isDirectory()) {
|
|
6697
8129
|
if (!recursive) {
|
|
6698
8130
|
throw new Error(
|
|
@@ -6701,8 +8133,8 @@ var init_local_provider = __esm({
|
|
|
6701
8133
|
}
|
|
6702
8134
|
await this.copyDirectory(sourceFullPath, targetPath);
|
|
6703
8135
|
} else {
|
|
6704
|
-
await
|
|
6705
|
-
await
|
|
8136
|
+
await fs10.promises.mkdir(path4__namespace.dirname(targetPath), { recursive: true });
|
|
8137
|
+
await fs10.promises.copyFile(sourceFullPath, targetPath);
|
|
6706
8138
|
}
|
|
6707
8139
|
} catch (error) {
|
|
6708
8140
|
if (error.code === "ENOENT") {
|
|
@@ -6712,16 +8144,16 @@ var init_local_provider = __esm({
|
|
|
6712
8144
|
}
|
|
6713
8145
|
}
|
|
6714
8146
|
async copyDirectory(sourceDir, targetDir) {
|
|
6715
|
-
await
|
|
6716
|
-
const entries = await
|
|
8147
|
+
await fs10.promises.mkdir(targetDir, { recursive: true });
|
|
8148
|
+
const entries = await fs10.promises.readdir(sourceDir, { withFileTypes: true });
|
|
6717
8149
|
await Promise.all(
|
|
6718
8150
|
entries.map(async (entry) => {
|
|
6719
|
-
const sourcePath =
|
|
6720
|
-
const targetPath =
|
|
8151
|
+
const sourcePath = path4__namespace.join(sourceDir, entry.name);
|
|
8152
|
+
const targetPath = path4__namespace.join(targetDir, entry.name);
|
|
6721
8153
|
if (entry.isDirectory()) {
|
|
6722
8154
|
await this.copyDirectory(sourcePath, targetPath);
|
|
6723
8155
|
} else {
|
|
6724
|
-
await
|
|
8156
|
+
await fs10.promises.copyFile(sourcePath, targetPath);
|
|
6725
8157
|
}
|
|
6726
8158
|
})
|
|
6727
8159
|
);
|
|
@@ -6916,11 +8348,11 @@ var init_github_provider = __esm({
|
|
|
6916
8348
|
}
|
|
6917
8349
|
if (type === "file") {
|
|
6918
8350
|
const content = await this.readFile(sourcePath);
|
|
6919
|
-
await
|
|
8351
|
+
await fs10.promises.mkdir(path4__namespace.dirname(targetPath), { recursive: true });
|
|
6920
8352
|
if (content.encoding === "base64") {
|
|
6921
|
-
await
|
|
8353
|
+
await fs10.promises.writeFile(targetPath, Buffer.from(content.content, "base64"));
|
|
6922
8354
|
} else {
|
|
6923
|
-
await
|
|
8355
|
+
await fs10.promises.writeFile(targetPath, content.content, "utf-8");
|
|
6924
8356
|
}
|
|
6925
8357
|
} else {
|
|
6926
8358
|
if (!recursive) {
|
|
@@ -6928,13 +8360,13 @@ var init_github_provider = __esm({
|
|
|
6928
8360
|
`Cannot copy directory without recursive flag: ${sourcePath}`
|
|
6929
8361
|
);
|
|
6930
8362
|
}
|
|
6931
|
-
await
|
|
8363
|
+
await fs10.promises.mkdir(targetPath, { recursive: true });
|
|
6932
8364
|
const entries = await this.listFiles(sourcePath);
|
|
6933
8365
|
await Promise.all(
|
|
6934
8366
|
entries.map(
|
|
6935
8367
|
(entry) => this.copyTo(
|
|
6936
8368
|
entry.path,
|
|
6937
|
-
|
|
8369
|
+
path4__namespace.join(targetPath, entry.name),
|
|
6938
8370
|
recursive
|
|
6939
8371
|
)
|
|
6940
8372
|
)
|
|
@@ -7017,9 +8449,9 @@ var init_manager2 = __esm({
|
|
|
7017
8449
|
if (enabledSkills.length === 0) {
|
|
7018
8450
|
return "";
|
|
7019
8451
|
}
|
|
7020
|
-
const sessionDir =
|
|
7021
|
-
const skillsDir =
|
|
7022
|
-
await
|
|
8452
|
+
const sessionDir = path4__namespace.join(this.tempDir, sessionId);
|
|
8453
|
+
const skillsDir = path4__namespace.join(sessionDir, ".claude", "skills");
|
|
8454
|
+
await fs10.promises.mkdir(skillsDir, { recursive: true });
|
|
7023
8455
|
await Promise.all(
|
|
7024
8456
|
enabledSkills.map((skill) => this.materializeSkill(skill, skillsDir, token))
|
|
7025
8457
|
);
|
|
@@ -7030,10 +8462,10 @@ var init_manager2 = __esm({
|
|
|
7030
8462
|
*/
|
|
7031
8463
|
async materializeSkill(skill, skillsDir, token) {
|
|
7032
8464
|
const provider = this.createFileProvider(skill.source, token);
|
|
7033
|
-
const skillDir =
|
|
7034
|
-
await
|
|
8465
|
+
const skillDir = path4__namespace.join(skillsDir, skill.name);
|
|
8466
|
+
await fs10.promises.mkdir(skillDir, { recursive: true });
|
|
7035
8467
|
for (const includePath of skill.includePaths) {
|
|
7036
|
-
const targetPath =
|
|
8468
|
+
const targetPath = path4__namespace.join(skillDir, path4__namespace.basename(includePath));
|
|
7037
8469
|
try {
|
|
7038
8470
|
await provider.copyTo(includePath, targetPath, true);
|
|
7039
8471
|
} catch (error) {
|
|
@@ -7048,9 +8480,9 @@ var init_manager2 = __esm({
|
|
|
7048
8480
|
* Clean up materialized skills for a session
|
|
7049
8481
|
*/
|
|
7050
8482
|
async cleanupSession(sessionId) {
|
|
7051
|
-
const sessionDir =
|
|
8483
|
+
const sessionDir = path4__namespace.join(this.tempDir, sessionId);
|
|
7052
8484
|
try {
|
|
7053
|
-
await
|
|
8485
|
+
await fs10.promises.rm(sessionDir, { recursive: true, force: true });
|
|
7054
8486
|
} catch (error) {
|
|
7055
8487
|
console.warn(`Failed to cleanup session directory: ${sessionDir}`, error);
|
|
7056
8488
|
}
|
|
@@ -7061,16 +8493,16 @@ var init_manager2 = __esm({
|
|
|
7061
8493
|
*/
|
|
7062
8494
|
async cleanupStale(maxAgeMs = 24 * 60 * 60 * 1e3) {
|
|
7063
8495
|
try {
|
|
7064
|
-
const entries = await
|
|
8496
|
+
const entries = await fs10.promises.readdir(this.tempDir, { withFileTypes: true });
|
|
7065
8497
|
const now = Date.now();
|
|
7066
8498
|
await Promise.all(
|
|
7067
8499
|
entries.map(async (entry) => {
|
|
7068
8500
|
if (!entry.isDirectory()) return;
|
|
7069
|
-
const dirPath =
|
|
8501
|
+
const dirPath = path4__namespace.join(this.tempDir, entry.name);
|
|
7070
8502
|
try {
|
|
7071
|
-
const stats = await
|
|
8503
|
+
const stats = await fs10.promises.stat(dirPath);
|
|
7072
8504
|
if (now - stats.mtimeMs > maxAgeMs) {
|
|
7073
|
-
await
|
|
8505
|
+
await fs10.promises.rm(dirPath, { recursive: true, force: true });
|
|
7074
8506
|
}
|
|
7075
8507
|
} catch {
|
|
7076
8508
|
}
|
|
@@ -9875,11 +11307,11 @@ var init_dist = __esm({
|
|
|
9875
11307
|
return isZodType(schema, "ZodEffects") ? this.cleanParameter(schema._def.schema) : schema;
|
|
9876
11308
|
}
|
|
9877
11309
|
generatePath(route) {
|
|
9878
|
-
const { method, path:
|
|
11310
|
+
const { method, path: path15, request, responses } = route, pathItemConfig = __rest(route, ["method", "path", "request", "responses"]);
|
|
9879
11311
|
const generatedResponses = mapValues(responses, (response) => {
|
|
9880
11312
|
return this.getResponse(response);
|
|
9881
11313
|
});
|
|
9882
|
-
const parameters = enhanceMissingParametersError(() => this.getParameters(request), { route: `${method} ${
|
|
11314
|
+
const parameters = enhanceMissingParametersError(() => this.getParameters(request), { route: `${method} ${path15}` });
|
|
9883
11315
|
const requestBody = this.getRequestBody(request === null || request === void 0 ? void 0 : request.body);
|
|
9884
11316
|
const routeDoc = {
|
|
9885
11317
|
[method]: Object.assign(Object.assign(Object.assign(Object.assign({}, pathItemConfig), parameters.length > 0 ? {
|
|
@@ -10074,8 +11506,8 @@ var init_dist2 = __esm({
|
|
|
10074
11506
|
});
|
|
10075
11507
|
function addBasePathToDocument(document, basePath) {
|
|
10076
11508
|
const updatedPaths = {};
|
|
10077
|
-
Object.keys(document.paths).forEach((
|
|
10078
|
-
updatedPaths[url.mergePath(basePath.replaceAll(/:([^\/]+)/g, "{$1}"),
|
|
11509
|
+
Object.keys(document.paths).forEach((path15) => {
|
|
11510
|
+
updatedPaths[url.mergePath(basePath.replaceAll(/:([^\/]+)/g, "{$1}"), path15)] = document.paths[path15];
|
|
10079
11511
|
});
|
|
10080
11512
|
return {
|
|
10081
11513
|
...document,
|
|
@@ -10219,8 +11651,8 @@ var init_dist3 = __esm({
|
|
|
10219
11651
|
const document = generator.generateDocument(config);
|
|
10220
11652
|
return this._basePath ? addBasePathToDocument(document, this._basePath) : document;
|
|
10221
11653
|
};
|
|
10222
|
-
doc = (
|
|
10223
|
-
return this.get(
|
|
11654
|
+
doc = (path15, configure) => {
|
|
11655
|
+
return this.get(path15, (c) => {
|
|
10224
11656
|
const config = typeof configure === "function" ? configure(c) : configure;
|
|
10225
11657
|
try {
|
|
10226
11658
|
const document = this.getOpenAPIDocument(config);
|
|
@@ -10230,8 +11662,8 @@ var init_dist3 = __esm({
|
|
|
10230
11662
|
}
|
|
10231
11663
|
});
|
|
10232
11664
|
};
|
|
10233
|
-
doc31 = (
|
|
10234
|
-
return this.get(
|
|
11665
|
+
doc31 = (path15, configure) => {
|
|
11666
|
+
return this.get(path15, (c) => {
|
|
10235
11667
|
const config = typeof configure === "function" ? configure(c) : configure;
|
|
10236
11668
|
try {
|
|
10237
11669
|
const document = this.getOpenAPI31Document(config);
|
|
@@ -10241,9 +11673,9 @@ var init_dist3 = __esm({
|
|
|
10241
11673
|
}
|
|
10242
11674
|
});
|
|
10243
11675
|
};
|
|
10244
|
-
route(
|
|
10245
|
-
const pathForOpenAPI =
|
|
10246
|
-
super.route(
|
|
11676
|
+
route(path15, app) {
|
|
11677
|
+
const pathForOpenAPI = path15.replaceAll(/:([^\/]+)/g, "{$1}");
|
|
11678
|
+
super.route(path15, app);
|
|
10247
11679
|
if (!(app instanceof _OpenAPIHono)) {
|
|
10248
11680
|
return this;
|
|
10249
11681
|
}
|
|
@@ -10290,8 +11722,8 @@ var init_dist3 = __esm({
|
|
|
10290
11722
|
});
|
|
10291
11723
|
return this;
|
|
10292
11724
|
}
|
|
10293
|
-
basePath(
|
|
10294
|
-
return new _OpenAPIHono({ ...super.basePath(
|
|
11725
|
+
basePath(path15) {
|
|
11726
|
+
return new _OpenAPIHono({ ...super.basePath(path15), defaultHook: this.defaultHook });
|
|
10295
11727
|
}
|
|
10296
11728
|
};
|
|
10297
11729
|
createRoute = (routeConfig) => {
|
|
@@ -12072,12 +13504,12 @@ function requestLogger(options = {}) {
|
|
|
12072
13504
|
const requestId = generateId();
|
|
12073
13505
|
const start = Date.now();
|
|
12074
13506
|
const method = c.req.method;
|
|
12075
|
-
const
|
|
13507
|
+
const path15 = c.req.path;
|
|
12076
13508
|
c.set("requestId", requestId);
|
|
12077
13509
|
logger3.debug("Request started", {
|
|
12078
13510
|
requestId,
|
|
12079
13511
|
method,
|
|
12080
|
-
path:
|
|
13512
|
+
path: path15,
|
|
12081
13513
|
userAgent: c.req.header("user-agent")
|
|
12082
13514
|
});
|
|
12083
13515
|
await next();
|
|
@@ -12087,7 +13519,7 @@ function requestLogger(options = {}) {
|
|
|
12087
13519
|
logFn("Request completed", {
|
|
12088
13520
|
requestId,
|
|
12089
13521
|
method,
|
|
12090
|
-
path:
|
|
13522
|
+
path: path15,
|
|
12091
13523
|
status,
|
|
12092
13524
|
duration: `${duration}ms`
|
|
12093
13525
|
});
|
|
@@ -12572,8 +14004,8 @@ function shellQuote3(str) {
|
|
|
12572
14004
|
async function loadWorkspaceState(workspaceId, sandbox, bundleStore, repoRoot) {
|
|
12573
14005
|
const resolvedRoot = repoRoot ?? sandbox.defaultRepoRoot;
|
|
12574
14006
|
const gitRepo = new exports.SandboxGitRepo(sandbox, resolvedRoot);
|
|
12575
|
-
const tmpDir =
|
|
12576
|
-
const localBundle =
|
|
14007
|
+
const tmpDir = fs10__namespace.mkdtempSync(path4__namespace.join(os__namespace.tmpdir(), "ash-workspace-"));
|
|
14008
|
+
const localBundle = path4__namespace.join(tmpDir, `${workspaceId}.bundle`);
|
|
12577
14009
|
try {
|
|
12578
14010
|
const bundleExists = await Promise.resolve(
|
|
12579
14011
|
bundleStore.downloadBundle(workspaceId, localBundle)
|
|
@@ -12609,7 +14041,7 @@ async function loadWorkspaceState(workspaceId, sandbox, bundleStore, repoRoot) {
|
|
|
12609
14041
|
}
|
|
12610
14042
|
} finally {
|
|
12611
14043
|
try {
|
|
12612
|
-
|
|
14044
|
+
fs10__namespace.rmSync(tmpDir, { recursive: true, force: true });
|
|
12613
14045
|
} catch {
|
|
12614
14046
|
}
|
|
12615
14047
|
}
|
|
@@ -12629,11 +14061,11 @@ async function saveWorkspaceState(workspaceId, sandbox, gitRepo, bundleStore, co
|
|
|
12629
14061
|
}
|
|
12630
14062
|
const remoteBundlePath = BUNDLE_PATH_TEMPLATE.replace("{workspaceId}", workspaceId);
|
|
12631
14063
|
await gitRepo.createBundle(remoteBundlePath);
|
|
12632
|
-
const tmpDir =
|
|
12633
|
-
const localBundle =
|
|
14064
|
+
const tmpDir = fs10__namespace.mkdtempSync(path4__namespace.join(os__namespace.tmpdir(), "ash-workspace-"));
|
|
14065
|
+
const localBundle = path4__namespace.join(tmpDir, `${workspaceId}.bundle`);
|
|
12634
14066
|
try {
|
|
12635
14067
|
await sandbox.downloadFile(remoteBundlePath, localBundle);
|
|
12636
|
-
const bundleSize =
|
|
14068
|
+
const bundleSize = fs10__namespace.statSync(localBundle).size;
|
|
12637
14069
|
await bundleStore.uploadBundle(workspaceId, localBundle);
|
|
12638
14070
|
return {
|
|
12639
14071
|
committed,
|
|
@@ -12642,7 +14074,7 @@ async function saveWorkspaceState(workspaceId, sandbox, gitRepo, bundleStore, co
|
|
|
12642
14074
|
};
|
|
12643
14075
|
} finally {
|
|
12644
14076
|
try {
|
|
12645
|
-
|
|
14077
|
+
fs10__namespace.rmSync(tmpDir, { recursive: true, force: true });
|
|
12646
14078
|
} catch {
|
|
12647
14079
|
}
|
|
12648
14080
|
}
|
|
@@ -12655,7 +14087,7 @@ var init_persistence = __esm({
|
|
|
12655
14087
|
}
|
|
12656
14088
|
});
|
|
12657
14089
|
async function cloneRepository(url, ref, timeout = 12e4) {
|
|
12658
|
-
const tmpDir =
|
|
14090
|
+
const tmpDir = fs10__namespace.mkdtempSync(path4__namespace.join(os__namespace.tmpdir(), "ash-github-skill-"));
|
|
12659
14091
|
try {
|
|
12660
14092
|
const cloneCmd = ["git", "clone", "--depth", "1"];
|
|
12661
14093
|
if (ref) {
|
|
@@ -12670,7 +14102,7 @@ async function cloneRepository(url, ref, timeout = 12e4) {
|
|
|
12670
14102
|
return tmpDir;
|
|
12671
14103
|
} catch (error) {
|
|
12672
14104
|
try {
|
|
12673
|
-
|
|
14105
|
+
fs10__namespace.rmSync(tmpDir, { recursive: true, force: true });
|
|
12674
14106
|
} catch {
|
|
12675
14107
|
}
|
|
12676
14108
|
throw error;
|
|
@@ -12682,10 +14114,10 @@ function getRepoName(url) {
|
|
|
12682
14114
|
}
|
|
12683
14115
|
function countFiles(dir, exclude) {
|
|
12684
14116
|
let count = 0;
|
|
12685
|
-
const entries =
|
|
14117
|
+
const entries = fs10__namespace.readdirSync(dir, { withFileTypes: true });
|
|
12686
14118
|
for (const entry of entries) {
|
|
12687
14119
|
if (exclude.has(entry.name)) continue;
|
|
12688
|
-
const fullPath =
|
|
14120
|
+
const fullPath = path4__namespace.join(dir, entry.name);
|
|
12689
14121
|
if (entry.isDirectory()) {
|
|
12690
14122
|
count += countFiles(fullPath, exclude);
|
|
12691
14123
|
} else if (entry.isFile()) {
|
|
@@ -12697,15 +14129,15 @@ function countFiles(dir, exclude) {
|
|
|
12697
14129
|
async function uploadDirectory(sandbox, localPath, remotePath, exclude) {
|
|
12698
14130
|
let uploadCount = 0;
|
|
12699
14131
|
await sandbox.runCommand(`mkdir -p '${remotePath}'`);
|
|
12700
|
-
const entries =
|
|
14132
|
+
const entries = fs10__namespace.readdirSync(localPath, { withFileTypes: true });
|
|
12701
14133
|
for (const entry of entries) {
|
|
12702
14134
|
if (exclude.has(entry.name)) continue;
|
|
12703
|
-
const srcPath =
|
|
14135
|
+
const srcPath = path4__namespace.join(localPath, entry.name);
|
|
12704
14136
|
const destPath = `${remotePath}/${entry.name}`;
|
|
12705
14137
|
if (entry.isDirectory()) {
|
|
12706
14138
|
uploadCount += await uploadDirectory(sandbox, srcPath, destPath, exclude);
|
|
12707
14139
|
} else if (entry.isFile()) {
|
|
12708
|
-
const content =
|
|
14140
|
+
const content = fs10__namespace.readFileSync(srcPath, "utf-8");
|
|
12709
14141
|
await sandbox.writeFile(destPath, content);
|
|
12710
14142
|
uploadCount++;
|
|
12711
14143
|
}
|
|
@@ -12731,13 +14163,13 @@ async function loadGitHubSkill(sandbox, config, skillsBasePath) {
|
|
|
12731
14163
|
);
|
|
12732
14164
|
}
|
|
12733
14165
|
try {
|
|
12734
|
-
const sourcePath = subpath ?
|
|
12735
|
-
if (!
|
|
14166
|
+
const sourcePath = subpath ? path4__namespace.join(clonePath, subpath) : clonePath;
|
|
14167
|
+
if (!fs10__namespace.existsSync(sourcePath)) {
|
|
12736
14168
|
throw new Error(
|
|
12737
14169
|
`Path '${subpath}' not found in repository ${url}`
|
|
12738
14170
|
);
|
|
12739
14171
|
}
|
|
12740
|
-
if (!
|
|
14172
|
+
if (!fs10__namespace.statSync(sourcePath).isDirectory()) {
|
|
12741
14173
|
throw new Error(
|
|
12742
14174
|
`Path '${subpath}' in ${url} is not a directory`
|
|
12743
14175
|
);
|
|
@@ -12756,7 +14188,7 @@ async function loadGitHubSkill(sandbox, config, skillsBasePath) {
|
|
|
12756
14188
|
};
|
|
12757
14189
|
} finally {
|
|
12758
14190
|
try {
|
|
12759
|
-
|
|
14191
|
+
fs10__namespace.rmSync(clonePath, { recursive: true, force: true });
|
|
12760
14192
|
} catch {
|
|
12761
14193
|
}
|
|
12762
14194
|
}
|
|
@@ -13449,26 +14881,26 @@ var init_workspace = __esm({
|
|
|
13449
14881
|
const localPaths = normalizeToArray(skillsConfig.local);
|
|
13450
14882
|
for (const localPath of localPaths) {
|
|
13451
14883
|
try {
|
|
13452
|
-
if (!
|
|
14884
|
+
if (!fs10__namespace.existsSync(localPath)) {
|
|
13453
14885
|
errors.push({
|
|
13454
14886
|
source: localPath,
|
|
13455
14887
|
error: new Error(`Path does not exist: ${localPath}`)
|
|
13456
14888
|
});
|
|
13457
14889
|
continue;
|
|
13458
14890
|
}
|
|
13459
|
-
const
|
|
13460
|
-
if (!
|
|
14891
|
+
const stat2 = fs10__namespace.statSync(localPath);
|
|
14892
|
+
if (!stat2.isDirectory()) {
|
|
13461
14893
|
errors.push({
|
|
13462
14894
|
source: localPath,
|
|
13463
14895
|
error: new Error(`Path is not a directory: ${localPath}`)
|
|
13464
14896
|
});
|
|
13465
14897
|
continue;
|
|
13466
14898
|
}
|
|
13467
|
-
const entries =
|
|
14899
|
+
const entries = fs10__namespace.readdirSync(localPath);
|
|
13468
14900
|
for (const entry of entries) {
|
|
13469
14901
|
if (FILTERED_ITEMS2.has(entry)) continue;
|
|
13470
|
-
const entryPath =
|
|
13471
|
-
const entryStat =
|
|
14902
|
+
const entryPath = path4__namespace.join(localPath, entry);
|
|
14903
|
+
const entryStat = fs10__namespace.statSync(entryPath);
|
|
13472
14904
|
if (entryStat.isDirectory()) {
|
|
13473
14905
|
const remotePath = `${skillsBase}/${entry}`;
|
|
13474
14906
|
await this.sandbox.uploadDirectory(entryPath, remotePath, {
|
|
@@ -13553,50 +14985,50 @@ var init_bundle_store = __esm({
|
|
|
13553
14985
|
directory;
|
|
13554
14986
|
constructor(options) {
|
|
13555
14987
|
this.directory = options.directory;
|
|
13556
|
-
if (!
|
|
13557
|
-
|
|
14988
|
+
if (!fs10__namespace.existsSync(this.directory)) {
|
|
14989
|
+
fs10__namespace.mkdirSync(this.directory, { recursive: true });
|
|
13558
14990
|
}
|
|
13559
14991
|
}
|
|
13560
14992
|
keyForWorkspace(workspaceId) {
|
|
13561
|
-
return
|
|
14993
|
+
return path4__namespace.join(this.directory, `${workspaceId}.bundle`);
|
|
13562
14994
|
}
|
|
13563
14995
|
downloadBundle(workspaceId, destPath) {
|
|
13564
14996
|
const bundlePath = this.keyForWorkspace(workspaceId);
|
|
13565
|
-
if (!
|
|
14997
|
+
if (!fs10__namespace.existsSync(bundlePath)) {
|
|
13566
14998
|
return false;
|
|
13567
14999
|
}
|
|
13568
|
-
const destDir =
|
|
13569
|
-
if (!
|
|
13570
|
-
|
|
15000
|
+
const destDir = path4__namespace.dirname(destPath);
|
|
15001
|
+
if (!fs10__namespace.existsSync(destDir)) {
|
|
15002
|
+
fs10__namespace.mkdirSync(destDir, { recursive: true });
|
|
13571
15003
|
}
|
|
13572
|
-
|
|
15004
|
+
fs10__namespace.copyFileSync(bundlePath, destPath);
|
|
13573
15005
|
return true;
|
|
13574
15006
|
}
|
|
13575
15007
|
uploadBundle(workspaceId, srcPath) {
|
|
13576
|
-
if (!
|
|
15008
|
+
if (!fs10__namespace.existsSync(srcPath)) {
|
|
13577
15009
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13578
15010
|
}
|
|
13579
15011
|
const bundlePath = this.keyForWorkspace(workspaceId);
|
|
13580
|
-
const bundleDir =
|
|
13581
|
-
if (!
|
|
13582
|
-
|
|
15012
|
+
const bundleDir = path4__namespace.dirname(bundlePath);
|
|
15013
|
+
if (!fs10__namespace.existsSync(bundleDir)) {
|
|
15014
|
+
fs10__namespace.mkdirSync(bundleDir, { recursive: true });
|
|
13583
15015
|
}
|
|
13584
|
-
|
|
15016
|
+
fs10__namespace.copyFileSync(srcPath, bundlePath);
|
|
13585
15017
|
}
|
|
13586
15018
|
exists(workspaceId) {
|
|
13587
|
-
return
|
|
15019
|
+
return fs10__namespace.existsSync(this.keyForWorkspace(workspaceId));
|
|
13588
15020
|
}
|
|
13589
15021
|
deleteBundle(workspaceId) {
|
|
13590
15022
|
const bundlePath = this.keyForWorkspace(workspaceId);
|
|
13591
|
-
if (
|
|
13592
|
-
|
|
15023
|
+
if (fs10__namespace.existsSync(bundlePath)) {
|
|
15024
|
+
fs10__namespace.unlinkSync(bundlePath);
|
|
13593
15025
|
}
|
|
13594
15026
|
}
|
|
13595
15027
|
listWorkspaces() {
|
|
13596
|
-
if (!
|
|
15028
|
+
if (!fs10__namespace.existsSync(this.directory)) {
|
|
13597
15029
|
return [];
|
|
13598
15030
|
}
|
|
13599
|
-
return
|
|
15031
|
+
return fs10__namespace.readdirSync(this.directory).filter((file) => file.endsWith(".bundle")).map((file) => file.replace(".bundle", ""));
|
|
13600
15032
|
}
|
|
13601
15033
|
};
|
|
13602
15034
|
exports.MemoryBundleStore = class {
|
|
@@ -13609,18 +15041,18 @@ var init_bundle_store = __esm({
|
|
|
13609
15041
|
if (!bundle) {
|
|
13610
15042
|
return false;
|
|
13611
15043
|
}
|
|
13612
|
-
const destDir =
|
|
13613
|
-
if (!
|
|
13614
|
-
|
|
15044
|
+
const destDir = path4__namespace.dirname(destPath);
|
|
15045
|
+
if (!fs10__namespace.existsSync(destDir)) {
|
|
15046
|
+
fs10__namespace.mkdirSync(destDir, { recursive: true });
|
|
13615
15047
|
}
|
|
13616
|
-
|
|
15048
|
+
fs10__namespace.writeFileSync(destPath, bundle);
|
|
13617
15049
|
return true;
|
|
13618
15050
|
}
|
|
13619
15051
|
uploadBundle(workspaceId, srcPath) {
|
|
13620
|
-
if (!
|
|
15052
|
+
if (!fs10__namespace.existsSync(srcPath)) {
|
|
13621
15053
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13622
15054
|
}
|
|
13623
|
-
const content =
|
|
15055
|
+
const content = fs10__namespace.readFileSync(srcPath);
|
|
13624
15056
|
this.bundles.set(workspaceId, content);
|
|
13625
15057
|
}
|
|
13626
15058
|
exists(workspaceId) {
|
|
@@ -13711,21 +15143,21 @@ var init_supabase_store = __esm({
|
|
|
13711
15143
|
if (!data) {
|
|
13712
15144
|
return false;
|
|
13713
15145
|
}
|
|
13714
|
-
const dir =
|
|
13715
|
-
if (!
|
|
13716
|
-
|
|
15146
|
+
const dir = path4__namespace.dirname(destPath);
|
|
15147
|
+
if (!fs10__namespace.existsSync(dir)) {
|
|
15148
|
+
fs10__namespace.mkdirSync(dir, { recursive: true });
|
|
13717
15149
|
}
|
|
13718
15150
|
const arrayBuffer = await data.arrayBuffer();
|
|
13719
|
-
|
|
15151
|
+
fs10__namespace.writeFileSync(destPath, Buffer.from(arrayBuffer));
|
|
13720
15152
|
return true;
|
|
13721
15153
|
}
|
|
13722
15154
|
async uploadBundle(workspaceId, srcPath) {
|
|
13723
|
-
if (!
|
|
15155
|
+
if (!fs10__namespace.existsSync(srcPath)) {
|
|
13724
15156
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13725
15157
|
}
|
|
13726
15158
|
const client = await this.ensureClient();
|
|
13727
15159
|
const key = this.keyForWorkspace(workspaceId);
|
|
13728
|
-
const content =
|
|
15160
|
+
const content = fs10__namespace.readFileSync(srcPath);
|
|
13729
15161
|
const { error } = await client.storage.from(this.bucket).upload(key, content, {
|
|
13730
15162
|
contentType: "application/octet-stream",
|
|
13731
15163
|
upsert: true
|
|
@@ -13854,11 +15286,11 @@ var init_cloud_store = __esm({
|
|
|
13854
15286
|
return false;
|
|
13855
15287
|
}
|
|
13856
15288
|
const bytes = await body.transformToByteArray();
|
|
13857
|
-
const dir =
|
|
13858
|
-
if (!
|
|
13859
|
-
|
|
15289
|
+
const dir = path4__namespace.dirname(destPath);
|
|
15290
|
+
if (!fs10__namespace.existsSync(dir)) {
|
|
15291
|
+
fs10__namespace.mkdirSync(dir, { recursive: true });
|
|
13860
15292
|
}
|
|
13861
|
-
|
|
15293
|
+
fs10__namespace.writeFileSync(destPath, Buffer.from(bytes));
|
|
13862
15294
|
return true;
|
|
13863
15295
|
} catch (error) {
|
|
13864
15296
|
const err = error;
|
|
@@ -13869,12 +15301,12 @@ var init_cloud_store = __esm({
|
|
|
13869
15301
|
}
|
|
13870
15302
|
}
|
|
13871
15303
|
async uploadBundle(workspaceId, srcPath) {
|
|
13872
|
-
if (!
|
|
15304
|
+
if (!fs10__namespace.existsSync(srcPath)) {
|
|
13873
15305
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13874
15306
|
}
|
|
13875
15307
|
const { client, module } = await this.ensureClient();
|
|
13876
15308
|
const key = this.keyForWorkspace(workspaceId);
|
|
13877
|
-
const content =
|
|
15309
|
+
const content = fs10__namespace.readFileSync(srcPath);
|
|
13878
15310
|
await client.send(
|
|
13879
15311
|
new module.PutObjectCommand({
|
|
13880
15312
|
Bucket: this.bucket,
|
|
@@ -13976,9 +15408,9 @@ var init_cloud_store = __esm({
|
|
|
13976
15408
|
if (!exists) {
|
|
13977
15409
|
return false;
|
|
13978
15410
|
}
|
|
13979
|
-
const dir =
|
|
13980
|
-
if (!
|
|
13981
|
-
|
|
15411
|
+
const dir = path4__namespace.dirname(destPath);
|
|
15412
|
+
if (!fs10__namespace.existsSync(dir)) {
|
|
15413
|
+
fs10__namespace.mkdirSync(dir, { recursive: true });
|
|
13982
15414
|
}
|
|
13983
15415
|
await file.download({ destination: destPath });
|
|
13984
15416
|
return true;
|
|
@@ -13987,12 +15419,12 @@ var init_cloud_store = __esm({
|
|
|
13987
15419
|
}
|
|
13988
15420
|
}
|
|
13989
15421
|
async uploadBundle(workspaceId, srcPath) {
|
|
13990
|
-
if (!
|
|
15422
|
+
if (!fs10__namespace.existsSync(srcPath)) {
|
|
13991
15423
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13992
15424
|
}
|
|
13993
15425
|
const storage = await this.ensureStorage();
|
|
13994
15426
|
const key = this.keyForWorkspace(workspaceId);
|
|
13995
|
-
const content =
|
|
15427
|
+
const content = fs10__namespace.readFileSync(srcPath);
|
|
13996
15428
|
const file = storage.bucket(this.bucket).file(key);
|
|
13997
15429
|
await file.save(content);
|
|
13998
15430
|
}
|
|
@@ -14091,25 +15523,25 @@ var init_file_store = __esm({
|
|
|
14091
15523
|
/**
|
|
14092
15524
|
* Generate the S3 key for a file
|
|
14093
15525
|
*/
|
|
14094
|
-
keyForFile(sessionId,
|
|
14095
|
-
const normalizedPath =
|
|
15526
|
+
keyForFile(sessionId, path15) {
|
|
15527
|
+
const normalizedPath = path15.replace(/^\/+/, "");
|
|
14096
15528
|
return `${this.prefix}${sessionId}/${normalizedPath}`;
|
|
14097
15529
|
}
|
|
14098
|
-
async writeFile(sessionId,
|
|
15530
|
+
async writeFile(sessionId, path15, content) {
|
|
14099
15531
|
const { client, module } = await this.ensureClient();
|
|
14100
|
-
const key = this.keyForFile(sessionId,
|
|
15532
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14101
15533
|
await client.send(
|
|
14102
15534
|
new module.PutObjectCommand({
|
|
14103
15535
|
Bucket: this.bucket,
|
|
14104
15536
|
Key: key,
|
|
14105
15537
|
Body: content,
|
|
14106
|
-
ContentType: this.getMimeType(
|
|
15538
|
+
ContentType: this.getMimeType(path15)
|
|
14107
15539
|
})
|
|
14108
15540
|
);
|
|
14109
15541
|
}
|
|
14110
|
-
async readFile(sessionId,
|
|
15542
|
+
async readFile(sessionId, path15) {
|
|
14111
15543
|
const { client, module } = await this.ensureClient();
|
|
14112
|
-
const key = this.keyForFile(sessionId,
|
|
15544
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14113
15545
|
try {
|
|
14114
15546
|
const response = await client.send(
|
|
14115
15547
|
new module.GetObjectCommand({
|
|
@@ -14131,9 +15563,9 @@ var init_file_store = __esm({
|
|
|
14131
15563
|
throw error;
|
|
14132
15564
|
}
|
|
14133
15565
|
}
|
|
14134
|
-
async exists(sessionId,
|
|
15566
|
+
async exists(sessionId, path15) {
|
|
14135
15567
|
const { client, module } = await this.ensureClient();
|
|
14136
|
-
const key = this.keyForFile(sessionId,
|
|
15568
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14137
15569
|
try {
|
|
14138
15570
|
await client.send(
|
|
14139
15571
|
new module.HeadObjectCommand({
|
|
@@ -14150,9 +15582,9 @@ var init_file_store = __esm({
|
|
|
14150
15582
|
throw error;
|
|
14151
15583
|
}
|
|
14152
15584
|
}
|
|
14153
|
-
async deleteFile(sessionId,
|
|
15585
|
+
async deleteFile(sessionId, path15) {
|
|
14154
15586
|
const { client, module } = await this.ensureClient();
|
|
14155
|
-
const key = this.keyForFile(sessionId,
|
|
15587
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14156
15588
|
await client.send(
|
|
14157
15589
|
new module.DeleteObjectCommand({
|
|
14158
15590
|
Bucket: this.bucket,
|
|
@@ -14207,30 +15639,30 @@ var init_file_store = __esm({
|
|
|
14207
15639
|
);
|
|
14208
15640
|
}
|
|
14209
15641
|
}
|
|
14210
|
-
async getSignedUrl(sessionId,
|
|
15642
|
+
async getSignedUrl(sessionId, path15, expiresIn = 3600) {
|
|
14211
15643
|
const { client, module, presigner } = await this.ensureClient();
|
|
14212
|
-
const key = this.keyForFile(sessionId,
|
|
15644
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14213
15645
|
const command = new module.GetObjectCommand({
|
|
14214
15646
|
Bucket: this.bucket,
|
|
14215
15647
|
Key: key
|
|
14216
15648
|
});
|
|
14217
15649
|
return presigner.getSignedUrl(client, command, { expiresIn });
|
|
14218
15650
|
}
|
|
14219
|
-
async getUploadUrl(sessionId,
|
|
15651
|
+
async getUploadUrl(sessionId, path15, expiresIn = 3600) {
|
|
14220
15652
|
const { client, module, presigner } = await this.ensureClient();
|
|
14221
|
-
const key = this.keyForFile(sessionId,
|
|
15653
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14222
15654
|
const command = new module.PutObjectCommand({
|
|
14223
15655
|
Bucket: this.bucket,
|
|
14224
15656
|
Key: key,
|
|
14225
|
-
ContentType: this.getMimeType(
|
|
15657
|
+
ContentType: this.getMimeType(path15)
|
|
14226
15658
|
});
|
|
14227
15659
|
return presigner.getSignedUrl(client, command, { expiresIn });
|
|
14228
15660
|
}
|
|
14229
15661
|
/**
|
|
14230
15662
|
* Get MIME type based on file extension
|
|
14231
15663
|
*/
|
|
14232
|
-
getMimeType(
|
|
14233
|
-
const ext =
|
|
15664
|
+
getMimeType(path15) {
|
|
15665
|
+
const ext = path15.split(".").pop()?.toLowerCase();
|
|
14234
15666
|
const mimeTypes = {
|
|
14235
15667
|
// Text
|
|
14236
15668
|
txt: "text/plain",
|
|
@@ -14285,8 +15717,8 @@ var init_local_sandbox = __esm({
|
|
|
14285
15717
|
this.defaultRepoRoot = config.defaultRepoRoot ?? `${this.workingDirectory}/repo`;
|
|
14286
15718
|
this.env = { ...process.env, ...config.env };
|
|
14287
15719
|
this.timeout = config.timeout ?? 3e4;
|
|
14288
|
-
if (!
|
|
14289
|
-
|
|
15720
|
+
if (!fs10__namespace.existsSync(this.workingDirectory)) {
|
|
15721
|
+
fs10__namespace.mkdirSync(this.workingDirectory, { recursive: true });
|
|
14290
15722
|
}
|
|
14291
15723
|
}
|
|
14292
15724
|
runCommand(command) {
|
|
@@ -14315,51 +15747,51 @@ var init_local_sandbox = __esm({
|
|
|
14315
15747
|
}
|
|
14316
15748
|
}
|
|
14317
15749
|
uploadFile(localPath, remotePath) {
|
|
14318
|
-
const dir =
|
|
14319
|
-
if (!
|
|
14320
|
-
|
|
15750
|
+
const dir = path4__namespace.dirname(remotePath);
|
|
15751
|
+
if (!fs10__namespace.existsSync(dir)) {
|
|
15752
|
+
fs10__namespace.mkdirSync(dir, { recursive: true });
|
|
14321
15753
|
}
|
|
14322
|
-
|
|
15754
|
+
fs10__namespace.copyFileSync(localPath, remotePath);
|
|
14323
15755
|
}
|
|
14324
15756
|
downloadFile(remotePath, localPath) {
|
|
14325
|
-
const dir =
|
|
14326
|
-
if (!
|
|
14327
|
-
|
|
15757
|
+
const dir = path4__namespace.dirname(localPath);
|
|
15758
|
+
if (!fs10__namespace.existsSync(dir)) {
|
|
15759
|
+
fs10__namespace.mkdirSync(dir, { recursive: true });
|
|
14328
15760
|
}
|
|
14329
|
-
|
|
15761
|
+
fs10__namespace.copyFileSync(remotePath, localPath);
|
|
14330
15762
|
}
|
|
14331
15763
|
uploadDirectory(localPath, remotePath, options) {
|
|
14332
15764
|
const exclude = options?.exclude ?? /* @__PURE__ */ new Set();
|
|
14333
|
-
if (!
|
|
14334
|
-
|
|
15765
|
+
if (!fs10__namespace.existsSync(remotePath)) {
|
|
15766
|
+
fs10__namespace.mkdirSync(remotePath, { recursive: true });
|
|
14335
15767
|
}
|
|
14336
15768
|
const copyRecursive = (src, dest) => {
|
|
14337
|
-
const entries =
|
|
15769
|
+
const entries = fs10__namespace.readdirSync(src, { withFileTypes: true });
|
|
14338
15770
|
for (const entry of entries) {
|
|
14339
15771
|
if (exclude.has(entry.name)) continue;
|
|
14340
|
-
const srcPath =
|
|
14341
|
-
const destPath =
|
|
15772
|
+
const srcPath = path4__namespace.join(src, entry.name);
|
|
15773
|
+
const destPath = path4__namespace.join(dest, entry.name);
|
|
14342
15774
|
if (entry.isDirectory()) {
|
|
14343
|
-
if (!
|
|
14344
|
-
|
|
15775
|
+
if (!fs10__namespace.existsSync(destPath)) {
|
|
15776
|
+
fs10__namespace.mkdirSync(destPath, { recursive: true });
|
|
14345
15777
|
}
|
|
14346
15778
|
copyRecursive(srcPath, destPath);
|
|
14347
15779
|
} else if (entry.isFile()) {
|
|
14348
|
-
|
|
15780
|
+
fs10__namespace.copyFileSync(srcPath, destPath);
|
|
14349
15781
|
}
|
|
14350
15782
|
}
|
|
14351
15783
|
};
|
|
14352
15784
|
copyRecursive(localPath, remotePath);
|
|
14353
15785
|
}
|
|
14354
15786
|
readFile(remotePath) {
|
|
14355
|
-
return
|
|
15787
|
+
return fs10__namespace.readFileSync(remotePath, "utf-8");
|
|
14356
15788
|
}
|
|
14357
15789
|
writeFile(remotePath, content) {
|
|
14358
|
-
const dir =
|
|
14359
|
-
if (!
|
|
14360
|
-
|
|
15790
|
+
const dir = path4__namespace.dirname(remotePath);
|
|
15791
|
+
if (!fs10__namespace.existsSync(dir)) {
|
|
15792
|
+
fs10__namespace.mkdirSync(dir, { recursive: true });
|
|
14361
15793
|
}
|
|
14362
|
-
|
|
15794
|
+
fs10__namespace.writeFileSync(remotePath, content, "utf-8");
|
|
14363
15795
|
}
|
|
14364
15796
|
close() {
|
|
14365
15797
|
}
|
|
@@ -14367,8 +15799,8 @@ var init_local_sandbox = __esm({
|
|
|
14367
15799
|
* Clean up the working directory
|
|
14368
15800
|
*/
|
|
14369
15801
|
cleanup() {
|
|
14370
|
-
if (
|
|
14371
|
-
|
|
15802
|
+
if (fs10__namespace.existsSync(this.workingDirectory)) {
|
|
15803
|
+
fs10__namespace.rmSync(this.workingDirectory, { recursive: true, force: true });
|
|
14372
15804
|
}
|
|
14373
15805
|
}
|
|
14374
15806
|
};
|
|
@@ -14478,33 +15910,33 @@ var init_provider_sandbox = __esm({
|
|
|
14478
15910
|
}
|
|
14479
15911
|
async uploadFile(localPath, remotePath) {
|
|
14480
15912
|
await this.initialize();
|
|
14481
|
-
const content =
|
|
15913
|
+
const content = fs10__namespace.readFileSync(localPath);
|
|
14482
15914
|
await this.provider.writeFile(this.getSandboxId(), remotePath, content);
|
|
14483
15915
|
}
|
|
14484
15916
|
async downloadFile(remotePath, localPath) {
|
|
14485
15917
|
await this.initialize();
|
|
14486
15918
|
const content = await this.provider.readFile(this.getSandboxId(), remotePath);
|
|
14487
|
-
const dir =
|
|
14488
|
-
if (!
|
|
14489
|
-
|
|
15919
|
+
const dir = path4__namespace.dirname(localPath);
|
|
15920
|
+
if (!fs10__namespace.existsSync(dir)) {
|
|
15921
|
+
fs10__namespace.mkdirSync(dir, { recursive: true });
|
|
14490
15922
|
}
|
|
14491
|
-
|
|
15923
|
+
fs10__namespace.writeFileSync(localPath, content);
|
|
14492
15924
|
}
|
|
14493
15925
|
async uploadDirectory(localPath, remotePath, options) {
|
|
14494
15926
|
await this.initialize();
|
|
14495
15927
|
const exclude = options?.exclude ?? /* @__PURE__ */ new Set();
|
|
14496
15928
|
await this.runCommand(`mkdir -p '${remotePath}'`);
|
|
14497
15929
|
const uploadRecursive = async (src, dest) => {
|
|
14498
|
-
const entries =
|
|
15930
|
+
const entries = fs10__namespace.readdirSync(src, { withFileTypes: true });
|
|
14499
15931
|
for (const entry of entries) {
|
|
14500
15932
|
if (exclude.has(entry.name)) continue;
|
|
14501
|
-
const srcPath =
|
|
15933
|
+
const srcPath = path4__namespace.join(src, entry.name);
|
|
14502
15934
|
const destPath = `${dest}/${entry.name}`;
|
|
14503
15935
|
if (entry.isDirectory()) {
|
|
14504
15936
|
await this.runCommand(`mkdir -p '${destPath}'`);
|
|
14505
15937
|
await uploadRecursive(srcPath, destPath);
|
|
14506
15938
|
} else if (entry.isFile()) {
|
|
14507
|
-
const content =
|
|
15939
|
+
const content = fs10__namespace.readFileSync(srcPath);
|
|
14508
15940
|
await this.provider.writeFile(this.getSandboxId(), destPath, content);
|
|
14509
15941
|
}
|
|
14510
15942
|
}
|
|
@@ -14742,16 +16174,19 @@ __export(schema_exports, {
|
|
|
14742
16174
|
configDeploymentTriggerEnum: () => configDeploymentTriggerEnum,
|
|
14743
16175
|
configDeployments: () => configDeployments,
|
|
14744
16176
|
configDeploymentsRelations: () => configDeploymentsRelations,
|
|
16177
|
+
eventCategoryEnum: () => eventCategoryEnum,
|
|
14745
16178
|
messageRoleEnum: () => messageRoleEnum,
|
|
14746
16179
|
messages: () => messages,
|
|
14747
16180
|
messagesRelations: () => messagesRelations,
|
|
14748
16181
|
queueItemStatusEnum: () => queueItemStatusEnum,
|
|
14749
16182
|
queueItems: () => queueItems,
|
|
16183
|
+
sessionEvents: () => sessionEvents,
|
|
16184
|
+
sessionEventsRelations: () => sessionEventsRelations,
|
|
14750
16185
|
sessionStatusEnum: () => sessionStatusEnum,
|
|
14751
16186
|
sessions: () => sessions,
|
|
14752
16187
|
sessionsRelations: () => sessionsRelations
|
|
14753
16188
|
});
|
|
14754
|
-
var sessionStatusEnum, messageRoleEnum, agentStatusEnum, agentBackendEnum, queueItemStatusEnum, configDeploymentStatusEnum, configDeploymentTriggerEnum, sessions, messages, attachments, agents, queueItems, configDeployments, sessionsRelations, messagesRelations, attachmentsRelations, configDeploymentsRelations, agentsRelations;
|
|
16189
|
+
var sessionStatusEnum, messageRoleEnum, agentStatusEnum, agentBackendEnum, queueItemStatusEnum, configDeploymentStatusEnum, configDeploymentTriggerEnum, eventCategoryEnum, sessions, messages, attachments, agents, queueItems, configDeployments, sessionEvents, sessionsRelations, sessionEventsRelations, messagesRelations, attachmentsRelations, configDeploymentsRelations, agentsRelations;
|
|
14755
16190
|
var init_schema = __esm({
|
|
14756
16191
|
"src/storage-postgres/schema.ts"() {
|
|
14757
16192
|
sessionStatusEnum = pgCore.pgEnum("session_status", [
|
|
@@ -14793,6 +16228,22 @@ var init_schema = __esm({
|
|
|
14793
16228
|
"initial",
|
|
14794
16229
|
"rollback"
|
|
14795
16230
|
]);
|
|
16231
|
+
eventCategoryEnum = pgCore.pgEnum("event_category", [
|
|
16232
|
+
"lifecycle",
|
|
16233
|
+
// session_start, session_end, turn_complete
|
|
16234
|
+
"content",
|
|
16235
|
+
// text_stream (aggregated), thinking_stream
|
|
16236
|
+
"tool",
|
|
16237
|
+
// tool_use, tool_result
|
|
16238
|
+
"system",
|
|
16239
|
+
// mcp_status, sandbox_log
|
|
16240
|
+
"error",
|
|
16241
|
+
// error events
|
|
16242
|
+
"file",
|
|
16243
|
+
// file_push, file_pull, file_sync (file sync operations)
|
|
16244
|
+
"input"
|
|
16245
|
+
// user_input (user prompts/messages)
|
|
16246
|
+
]);
|
|
14796
16247
|
sessions = pgCore.pgTable(
|
|
14797
16248
|
"sessions",
|
|
14798
16249
|
{
|
|
@@ -14948,6 +16399,32 @@ var init_schema = __esm({
|
|
|
14948
16399
|
pgCore.index("idx_config_deployments_created").on(table.agentId, table.createdAt)
|
|
14949
16400
|
]
|
|
14950
16401
|
);
|
|
16402
|
+
sessionEvents = pgCore.pgTable(
|
|
16403
|
+
"session_events",
|
|
16404
|
+
{
|
|
16405
|
+
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
16406
|
+
sessionId: pgCore.uuid("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
16407
|
+
eventType: pgCore.text("event_type").notNull(),
|
|
16408
|
+
category: eventCategoryEnum("category").notNull(),
|
|
16409
|
+
startedAt: pgCore.timestamp("started_at", { withTimezone: true }).notNull(),
|
|
16410
|
+
endedAt: pgCore.timestamp("ended_at", { withTimezone: true }),
|
|
16411
|
+
durationMs: pgCore.integer("duration_ms"),
|
|
16412
|
+
eventData: pgCore.jsonb("event_data").default({}).$type(),
|
|
16413
|
+
// Tool-specific fields
|
|
16414
|
+
toolUseId: pgCore.text("tool_use_id"),
|
|
16415
|
+
toolName: pgCore.text("tool_name"),
|
|
16416
|
+
// Aggregation fields (for text_stream events)
|
|
16417
|
+
isAggregated: pgCore.boolean("is_aggregated").default(false),
|
|
16418
|
+
aggregatedCount: pgCore.integer("aggregated_count"),
|
|
16419
|
+
// Ordering
|
|
16420
|
+
sequenceNumber: pgCore.integer("sequence_number").notNull(),
|
|
16421
|
+
createdAt: pgCore.timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
|
|
16422
|
+
},
|
|
16423
|
+
(table) => [
|
|
16424
|
+
pgCore.index("idx_session_events_session").on(table.sessionId, table.sequenceNumber),
|
|
16425
|
+
pgCore.index("idx_session_events_category").on(table.sessionId, table.category)
|
|
16426
|
+
]
|
|
16427
|
+
);
|
|
14951
16428
|
sessionsRelations = drizzleOrm.relations(sessions, ({ one, many }) => ({
|
|
14952
16429
|
parentSession: one(sessions, {
|
|
14953
16430
|
fields: [sessions.parentSessionId],
|
|
@@ -14957,7 +16434,14 @@ var init_schema = __esm({
|
|
|
14957
16434
|
childSessions: many(sessions, {
|
|
14958
16435
|
relationName: "parentSession"
|
|
14959
16436
|
}),
|
|
14960
|
-
messages: many(messages)
|
|
16437
|
+
messages: many(messages),
|
|
16438
|
+
events: many(sessionEvents)
|
|
16439
|
+
}));
|
|
16440
|
+
sessionEventsRelations = drizzleOrm.relations(sessionEvents, ({ one }) => ({
|
|
16441
|
+
session: one(sessions, {
|
|
16442
|
+
fields: [sessionEvents.sessionId],
|
|
16443
|
+
references: [sessions.id]
|
|
16444
|
+
})
|
|
14961
16445
|
}));
|
|
14962
16446
|
messagesRelations = drizzleOrm.relations(messages, ({ one, many }) => ({
|
|
14963
16447
|
session: one(sessions, {
|
|
@@ -16496,6 +17980,92 @@ var init_storage3 = __esm({
|
|
|
16496
17980
|
executionWebhookSecret: row.execution_webhook_secret ?? void 0
|
|
16497
17981
|
};
|
|
16498
17982
|
}
|
|
17983
|
+
rowToSessionEvent(row) {
|
|
17984
|
+
return {
|
|
17985
|
+
id: row.id,
|
|
17986
|
+
sessionId: row.session_id,
|
|
17987
|
+
eventType: row.event_type,
|
|
17988
|
+
category: row.category,
|
|
17989
|
+
startedAt: new Date(row.started_at),
|
|
17990
|
+
endedAt: row.ended_at ? new Date(row.ended_at) : void 0,
|
|
17991
|
+
durationMs: row.duration_ms ?? void 0,
|
|
17992
|
+
eventData: row.event_data ?? void 0,
|
|
17993
|
+
toolUseId: row.tool_use_id ?? void 0,
|
|
17994
|
+
toolName: row.tool_name ?? void 0,
|
|
17995
|
+
isAggregated: row.is_aggregated ?? void 0,
|
|
17996
|
+
aggregatedCount: row.aggregated_count ?? void 0,
|
|
17997
|
+
sequenceNumber: row.sequence_number,
|
|
17998
|
+
createdAt: new Date(row.created_at)
|
|
17999
|
+
};
|
|
18000
|
+
}
|
|
18001
|
+
// ============================================================================
|
|
18002
|
+
// Events
|
|
18003
|
+
// ============================================================================
|
|
18004
|
+
async saveEvents(sessionId, events) {
|
|
18005
|
+
if (events.length === 0) return [];
|
|
18006
|
+
const { data, error } = await this.client.from("session_events").insert(
|
|
18007
|
+
events.map((event) => ({
|
|
18008
|
+
session_id: sessionId,
|
|
18009
|
+
event_type: event.eventType,
|
|
18010
|
+
category: event.category,
|
|
18011
|
+
started_at: event.startedAt.toISOString(),
|
|
18012
|
+
ended_at: event.endedAt?.toISOString() ?? null,
|
|
18013
|
+
duration_ms: event.durationMs ?? null,
|
|
18014
|
+
event_data: event.eventData ?? {},
|
|
18015
|
+
tool_use_id: event.toolUseId ?? null,
|
|
18016
|
+
tool_name: event.toolName ?? null,
|
|
18017
|
+
is_aggregated: event.isAggregated ?? false,
|
|
18018
|
+
aggregated_count: event.aggregatedCount ?? null,
|
|
18019
|
+
sequence_number: event.sequenceNumber
|
|
18020
|
+
}))
|
|
18021
|
+
).select();
|
|
18022
|
+
if (error) {
|
|
18023
|
+
throw new Error(`Failed to save events: ${error.message}`);
|
|
18024
|
+
}
|
|
18025
|
+
return data.map((row) => this.rowToSessionEvent(row));
|
|
18026
|
+
}
|
|
18027
|
+
async getSessionEvents(sessionId, options = {}) {
|
|
18028
|
+
const limit = options.limit ?? 100;
|
|
18029
|
+
const offset = options.offset ?? 0;
|
|
18030
|
+
let query = this.client.from("session_events").select("*", { count: "exact" }).eq("session_id", sessionId);
|
|
18031
|
+
if (options.category) {
|
|
18032
|
+
query = query.eq("category", options.category);
|
|
18033
|
+
}
|
|
18034
|
+
if (options.eventType) {
|
|
18035
|
+
query = query.eq("event_type", options.eventType);
|
|
18036
|
+
}
|
|
18037
|
+
if (options.filePath) {
|
|
18038
|
+
query = query.or(
|
|
18039
|
+
`event_data->>filePath.eq.${options.filePath},and(category.eq.tool,tool_name.in.(Read,Write,Edit),event_data->input->>file_path.eq.${options.filePath})`
|
|
18040
|
+
);
|
|
18041
|
+
}
|
|
18042
|
+
const ascending = options.order !== "desc";
|
|
18043
|
+
query = query.order("sequence_number", { ascending }).range(offset, offset + limit - 1);
|
|
18044
|
+
const { data, error, count } = await query;
|
|
18045
|
+
if (error) {
|
|
18046
|
+
throw new Error(`Failed to get session events: ${error.message}`);
|
|
18047
|
+
}
|
|
18048
|
+
const total = count ?? 0;
|
|
18049
|
+
return {
|
|
18050
|
+
items: data.map((row) => this.rowToSessionEvent(row)),
|
|
18051
|
+
total,
|
|
18052
|
+
hasMore: offset + limit < total,
|
|
18053
|
+
nextCursor: offset + limit < total ? String(offset + limit) : void 0
|
|
18054
|
+
};
|
|
18055
|
+
}
|
|
18056
|
+
async deleteSessionEvents(sessionId) {
|
|
18057
|
+
const { error } = await this.client.from("session_events").delete().eq("session_id", sessionId);
|
|
18058
|
+
if (error) {
|
|
18059
|
+
throw new Error(`Failed to delete session events: ${error.message}`);
|
|
18060
|
+
}
|
|
18061
|
+
}
|
|
18062
|
+
async getNextEventSequence(sessionId) {
|
|
18063
|
+
const { data, error } = await this.client.from("session_events").select("sequence_number").eq("session_id", sessionId).order("sequence_number", { ascending: false }).limit(1).single();
|
|
18064
|
+
if (error && error.code !== "PGRST116") {
|
|
18065
|
+
throw new Error(`Failed to get next event sequence: ${error.message}`);
|
|
18066
|
+
}
|
|
18067
|
+
return data ? data.sequence_number + 1 : 1;
|
|
18068
|
+
}
|
|
16499
18069
|
};
|
|
16500
18070
|
}
|
|
16501
18071
|
});
|
|
@@ -16518,8 +18088,8 @@ var init_client = __esm({
|
|
|
16518
18088
|
/**
|
|
16519
18089
|
* Make an authenticated request to the Ash Cloud API
|
|
16520
18090
|
*/
|
|
16521
|
-
async request(method,
|
|
16522
|
-
const url = new URL(`${this.baseUrl}${
|
|
18091
|
+
async request(method, path15, options) {
|
|
18092
|
+
const url = new URL(`${this.baseUrl}${path15}`);
|
|
16523
18093
|
if (options?.query) {
|
|
16524
18094
|
for (const [key, value] of Object.entries(options.query)) {
|
|
16525
18095
|
if (value !== void 0) {
|
|
@@ -16587,8 +18157,8 @@ var init_client = __esm({
|
|
|
16587
18157
|
/**
|
|
16588
18158
|
* Make a streaming request (for SSE endpoints)
|
|
16589
18159
|
*/
|
|
16590
|
-
async *stream(method,
|
|
16591
|
-
const url = `${this.baseUrl}${
|
|
18160
|
+
async *stream(method, path15, options) {
|
|
18161
|
+
const url = `${this.baseUrl}${path15}`;
|
|
16592
18162
|
const headers = {
|
|
16593
18163
|
Authorization: `Bearer ${this.apiKey}`,
|
|
16594
18164
|
"Content-Type": "application/json",
|
|
@@ -17370,11 +18940,15 @@ exports.createDocumentedServer = createOpenAPIServer;
|
|
|
17370
18940
|
exports.createE2BSandbox = createE2BSandbox;
|
|
17371
18941
|
exports.createEventHandler = createEventHandler;
|
|
17372
18942
|
exports.createEventMiddlewareChain = createEventMiddlewareChain;
|
|
18943
|
+
exports.createFileWatcher = createFileWatcher;
|
|
18944
|
+
exports.createFileWatcherManager = createFileWatcherManager;
|
|
17373
18945
|
exports.createGCSBundleStore = createGCSBundleStore;
|
|
17374
18946
|
exports.createGeminiExecutor = createGeminiExecutor;
|
|
17375
18947
|
exports.createGitHubFileProvider = createGitHubFileProvider;
|
|
17376
18948
|
exports.createGitRepo = createGitRepo;
|
|
17377
18949
|
exports.createHarnessServer = createHarnessServer;
|
|
18950
|
+
exports.createInSandboxWatcher = createInSandboxWatcher;
|
|
18951
|
+
exports.createInSandboxWatcherManager = createInSandboxWatcherManager;
|
|
17378
18952
|
exports.createLocalBundleStore = createLocalBundleStore;
|
|
17379
18953
|
exports.createLocalFileProvider = createLocalFileProvider;
|
|
17380
18954
|
exports.createLocalSandbox = createLocalSandbox;
|
|
@@ -17392,6 +18966,8 @@ exports.createQueueProcessor = createQueueProcessor;
|
|
|
17392
18966
|
exports.createQueueRouter = createQueueRouter;
|
|
17393
18967
|
exports.createR2BundleStore = createR2BundleStore;
|
|
17394
18968
|
exports.createR2FileStore = createR2FileStore;
|
|
18969
|
+
exports.createRemoteFileWatcher = createRemoteFileWatcher;
|
|
18970
|
+
exports.createRemoteFileWatcherManager = createRemoteFileWatcherManager;
|
|
17395
18971
|
exports.createS3BundleStore = createS3BundleStore;
|
|
17396
18972
|
exports.createS3FileStore = createS3FileStore;
|
|
17397
18973
|
exports.createSandboxFileOperations = createSandboxFileOperations;
|
|
@@ -17430,8 +19006,11 @@ exports.getActionLabel = getActionLabel;
|
|
|
17430
19006
|
exports.getAllHeartbeatStatuses = getAllHeartbeatStatuses;
|
|
17431
19007
|
exports.getApiKeyEnvVar = getApiKeyEnvVar;
|
|
17432
19008
|
exports.getDefaultModel = getDefaultModel;
|
|
19009
|
+
exports.getFileWatcherManager = getFileWatcherManager;
|
|
17433
19010
|
exports.getHeartbeatStatus = getHeartbeatStatus;
|
|
19011
|
+
exports.getInSandboxWatcherManager = getInSandboxWatcherManager;
|
|
17434
19012
|
exports.getOrCreateSandbox = getOrCreateSandbox;
|
|
19013
|
+
exports.getRemoteFileWatcherManager = getRemoteFileWatcherManager;
|
|
17435
19014
|
exports.getSandboxCacheStats = getSandboxCacheStats;
|
|
17436
19015
|
exports.getSandboxPool = getSandboxPool;
|
|
17437
19016
|
exports.getWorkspaceManager = getWorkspaceManager;
|