@ash-cloud/ash-ai 0.1.8 → 0.1.10
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 +3 -2
package/dist/index.js
CHANGED
|
@@ -2,10 +2,11 @@ import { z, ZodType } from 'zod';
|
|
|
2
2
|
import { spawn, execSync } from 'child_process';
|
|
3
3
|
import { nanoid } from 'nanoid';
|
|
4
4
|
import * as fs from 'fs/promises';
|
|
5
|
-
import * as
|
|
5
|
+
import * as path4 from 'path';
|
|
6
6
|
import { Writable } from 'stream';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import chokidar from 'chokidar';
|
|
8
|
+
import { scryptSync, randomBytes, createCipheriv, createDecipheriv, createHmac } from 'crypto';
|
|
9
|
+
import * as fs10 from 'fs';
|
|
9
10
|
import { promises } from 'fs';
|
|
10
11
|
import { Hono } from 'hono';
|
|
11
12
|
import { streamSSE } from 'hono/streaming';
|
|
@@ -18,7 +19,7 @@ import { mergePath } from 'hono/utils/url';
|
|
|
18
19
|
import { HTTPException } from 'hono/http-exception';
|
|
19
20
|
export { serve } from '@hono/node-server';
|
|
20
21
|
import * as os from 'os';
|
|
21
|
-
import { pgEnum, pgTable, timestamp, jsonb, integer, uuid, text, index } from 'drizzle-orm/pg-core';
|
|
22
|
+
import { pgEnum, pgTable, timestamp, jsonb, integer, uuid, text, index, boolean } from 'drizzle-orm/pg-core';
|
|
22
23
|
import { eq, sql, and, asc, desc, isNull, or, lt, relations } from 'drizzle-orm';
|
|
23
24
|
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
24
25
|
import postgres from 'postgres';
|
|
@@ -78,7 +79,7 @@ var init_normalized = __esm({
|
|
|
78
79
|
"src/types/normalized.ts"() {
|
|
79
80
|
}
|
|
80
81
|
});
|
|
81
|
-
var SessionStatus, AgentStatus, MessageRole, messageContentSchema, messageSchema, sessionSchema, attachmentSchema, DEFAULT_SANDBOX_PROVIDER_CONFIG, StreamEventType, QueueItemStatus;
|
|
82
|
+
var SessionStatus, AgentStatus, MessageRole, messageContentSchema, messageSchema, sessionSchema, attachmentSchema, DEFAULT_SANDBOX_PROVIDER_CONFIG, StreamEventType, QueueItemStatus, EventCategory;
|
|
82
83
|
var init_types = __esm({
|
|
83
84
|
"src/types/index.ts"() {
|
|
84
85
|
init_normalized();
|
|
@@ -175,6 +176,7 @@ var init_types = __esm({
|
|
|
175
176
|
SESSION_END: "session_end",
|
|
176
177
|
SESSION_STOPPED: "session_stopped",
|
|
177
178
|
SANDBOX_LOG: "sandbox_log",
|
|
179
|
+
MCP_STATUS: "mcp_status",
|
|
178
180
|
ERROR: "error"
|
|
179
181
|
};
|
|
180
182
|
QueueItemStatus = {
|
|
@@ -184,6 +186,22 @@ var init_types = __esm({
|
|
|
184
186
|
FAILED: "failed",
|
|
185
187
|
CANCELLED: "cancelled"
|
|
186
188
|
};
|
|
189
|
+
EventCategory = {
|
|
190
|
+
LIFECYCLE: "lifecycle",
|
|
191
|
+
// session_start, session_end, turn_complete
|
|
192
|
+
CONTENT: "content",
|
|
193
|
+
// text_stream (aggregated), thinking_stream
|
|
194
|
+
TOOL: "tool",
|
|
195
|
+
// tool_use, tool_result
|
|
196
|
+
SYSTEM: "system",
|
|
197
|
+
// mcp_status, sandbox_log
|
|
198
|
+
ERROR: "error",
|
|
199
|
+
// error events
|
|
200
|
+
FILE: "file",
|
|
201
|
+
// file_push, file_pull, file_sync (file sync operations)
|
|
202
|
+
INPUT: "input"
|
|
203
|
+
// user_input (user prompts/messages)
|
|
204
|
+
};
|
|
187
205
|
}
|
|
188
206
|
});
|
|
189
207
|
|
|
@@ -603,7 +621,7 @@ var init_mcp = __esm({
|
|
|
603
621
|
* @param path Path to the server script
|
|
604
622
|
* @param runtime Runtime to use (node, python, etc.)
|
|
605
623
|
*/
|
|
606
|
-
custom: (
|
|
624
|
+
custom: (path15, runtime = "node", args) => {
|
|
607
625
|
const commands = {
|
|
608
626
|
node: "node",
|
|
609
627
|
python: "python3",
|
|
@@ -611,7 +629,7 @@ var init_mcp = __esm({
|
|
|
611
629
|
};
|
|
612
630
|
return {
|
|
613
631
|
command: commands[runtime] ?? "node",
|
|
614
|
-
args: runtime === "deno" ? ["run", "-A",
|
|
632
|
+
args: runtime === "deno" ? ["run", "-A", path15, ...args ?? []] : [path15, ...args ?? []]
|
|
615
633
|
};
|
|
616
634
|
}
|
|
617
635
|
};
|
|
@@ -669,8 +687,8 @@ var init_mcp = __esm({
|
|
|
669
687
|
/**
|
|
670
688
|
* Add a custom stdio MCP server
|
|
671
689
|
*/
|
|
672
|
-
withCustom(name,
|
|
673
|
-
return this.add(name, McpServers.custom(
|
|
690
|
+
withCustom(name, path15, runtime, args) {
|
|
691
|
+
return this.add(name, McpServers.custom(path15, runtime, args));
|
|
674
692
|
}
|
|
675
693
|
/**
|
|
676
694
|
* Add an HTTP MCP server with typed authentication
|
|
@@ -2992,9 +3010,9 @@ var init_attachment = __esm({
|
|
|
2992
3010
|
throw new Error(`MIME type ${mimeType} is not allowed`);
|
|
2993
3011
|
}
|
|
2994
3012
|
const id = nanoid();
|
|
2995
|
-
const ext =
|
|
3013
|
+
const ext = path4.extname(filename) || this.getExtensionForMimeType(mimeType);
|
|
2996
3014
|
const storageName = `${id}${ext}`;
|
|
2997
|
-
const storagePath =
|
|
3015
|
+
const storagePath = path4.join(this.config.basePath, storageName);
|
|
2998
3016
|
await fs.writeFile(storagePath, content);
|
|
2999
3017
|
return {
|
|
3000
3018
|
id,
|
|
@@ -3011,8 +3029,8 @@ var init_attachment = __esm({
|
|
|
3011
3029
|
*/
|
|
3012
3030
|
async storeFromPath(messageId, sourcePath, mimeType) {
|
|
3013
3031
|
const content = await fs.readFile(sourcePath);
|
|
3014
|
-
const filename =
|
|
3015
|
-
const detectedMimeType = mimeType ?? this.getMimeTypeForExtension(
|
|
3032
|
+
const filename = path4.basename(sourcePath);
|
|
3033
|
+
const detectedMimeType = mimeType ?? this.getMimeTypeForExtension(path4.extname(filename));
|
|
3016
3034
|
return this.storeFromBuffer(messageId, filename, content, detectedMimeType);
|
|
3017
3035
|
}
|
|
3018
3036
|
/**
|
|
@@ -3243,8 +3261,8 @@ async function loadConfig(configPath, options = {}) {
|
|
|
3243
3261
|
return config;
|
|
3244
3262
|
}
|
|
3245
3263
|
async function loadConfigFile(filePath) {
|
|
3246
|
-
const ext =
|
|
3247
|
-
const fullPath =
|
|
3264
|
+
const ext = path4.extname(filePath);
|
|
3265
|
+
const fullPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
|
|
3248
3266
|
if (ext === ".json") {
|
|
3249
3267
|
const content = await fs.readFile(fullPath, "utf-8");
|
|
3250
3268
|
return JSON.parse(content);
|
|
@@ -3257,7 +3275,7 @@ async function loadConfigFile(filePath) {
|
|
|
3257
3275
|
}
|
|
3258
3276
|
async function findAndLoadConfig(directory) {
|
|
3259
3277
|
for (const fileName of CONFIG_FILE_NAMES) {
|
|
3260
|
-
const filePath =
|
|
3278
|
+
const filePath = path4.join(directory, fileName);
|
|
3261
3279
|
try {
|
|
3262
3280
|
await fs.access(filePath);
|
|
3263
3281
|
return await loadConfigFile(filePath);
|
|
@@ -3595,8 +3613,8 @@ var init_modal = __esm({
|
|
|
3595
3613
|
}
|
|
3596
3614
|
async writeFile(_sandboxId, _path, _content) {
|
|
3597
3615
|
}
|
|
3598
|
-
async readFile(_sandboxId,
|
|
3599
|
-
return `[Modal] File content from ${
|
|
3616
|
+
async readFile(_sandboxId, path15) {
|
|
3617
|
+
return `[Modal] File content from ${path15}`;
|
|
3600
3618
|
}
|
|
3601
3619
|
/**
|
|
3602
3620
|
* Modal-specific: Get GPU information
|
|
@@ -3743,12 +3761,12 @@ var init_e2b = __esm({
|
|
|
3743
3761
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
3744
3762
|
}
|
|
3745
3763
|
}
|
|
3746
|
-
async readFile(sandboxId,
|
|
3764
|
+
async readFile(sandboxId, path15) {
|
|
3747
3765
|
const sandbox = this.sandboxes.get(sandboxId);
|
|
3748
3766
|
if (!sandbox) {
|
|
3749
3767
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
3750
3768
|
}
|
|
3751
|
-
return `[E2B] File content from ${
|
|
3769
|
+
return `[E2B] File content from ${path15}`;
|
|
3752
3770
|
}
|
|
3753
3771
|
async listFiles(sandboxId, _path) {
|
|
3754
3772
|
const sandbox = this.sandboxes.get(sandboxId);
|
|
@@ -3777,17 +3795,17 @@ var init_e2b = __esm({
|
|
|
3777
3795
|
* E2B-specific: Upload a file from local filesystem
|
|
3778
3796
|
*/
|
|
3779
3797
|
async uploadFile(sandboxId, localPath, remotePath) {
|
|
3780
|
-
const
|
|
3781
|
-
const content = await
|
|
3798
|
+
const fs15 = await import('fs/promises');
|
|
3799
|
+
const content = await fs15.readFile(localPath);
|
|
3782
3800
|
await this.writeFile(sandboxId, remotePath, content);
|
|
3783
3801
|
}
|
|
3784
3802
|
/**
|
|
3785
3803
|
* E2B-specific: Download a file to local filesystem
|
|
3786
3804
|
*/
|
|
3787
3805
|
async downloadFile(sandboxId, remotePath, localPath) {
|
|
3788
|
-
const
|
|
3806
|
+
const fs15 = await import('fs/promises');
|
|
3789
3807
|
const content = await this.readFile(sandboxId, remotePath);
|
|
3790
|
-
await
|
|
3808
|
+
await fs15.writeFile(localPath, content);
|
|
3791
3809
|
}
|
|
3792
3810
|
};
|
|
3793
3811
|
}
|
|
@@ -4041,7 +4059,7 @@ var init_vercel = __esm({
|
|
|
4041
4059
|
}
|
|
4042
4060
|
};
|
|
4043
4061
|
}
|
|
4044
|
-
async writeFile(sandboxId,
|
|
4062
|
+
async writeFile(sandboxId, path15, content) {
|
|
4045
4063
|
const instance = this.sandboxes.get(sandboxId);
|
|
4046
4064
|
if (!instance) {
|
|
4047
4065
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
@@ -4051,34 +4069,34 @@ var init_vercel = __esm({
|
|
|
4051
4069
|
if (isBase64) {
|
|
4052
4070
|
await this.executeCommand(
|
|
4053
4071
|
sandboxId,
|
|
4054
|
-
`echo "${contentStr}" | base64 -d > "${
|
|
4072
|
+
`echo "${contentStr}" | base64 -d > "${path15}"`
|
|
4055
4073
|
);
|
|
4056
4074
|
} else {
|
|
4057
4075
|
const escaped = contentStr.replace(/'/g, "'\\''");
|
|
4058
|
-
await this.executeCommand(sandboxId, `cat > "${
|
|
4076
|
+
await this.executeCommand(sandboxId, `cat > "${path15}" << 'VERCEL_EOF'
|
|
4059
4077
|
${escaped}
|
|
4060
4078
|
VERCEL_EOF`);
|
|
4061
4079
|
}
|
|
4062
4080
|
}
|
|
4063
|
-
async readFile(sandboxId,
|
|
4081
|
+
async readFile(sandboxId, path15) {
|
|
4064
4082
|
const instance = this.sandboxes.get(sandboxId);
|
|
4065
4083
|
if (!instance) {
|
|
4066
4084
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
4067
4085
|
}
|
|
4068
|
-
const result = await this.executeCommand(sandboxId, `cat "${
|
|
4086
|
+
const result = await this.executeCommand(sandboxId, `cat "${path15}"`);
|
|
4069
4087
|
if (result.exitCode !== 0) {
|
|
4070
|
-
throw new Error(`Failed to read file ${
|
|
4088
|
+
throw new Error(`Failed to read file ${path15}: ${result.stderr}`);
|
|
4071
4089
|
}
|
|
4072
4090
|
return result.stdout;
|
|
4073
4091
|
}
|
|
4074
|
-
async listFiles(sandboxId,
|
|
4092
|
+
async listFiles(sandboxId, path15) {
|
|
4075
4093
|
const instance = this.sandboxes.get(sandboxId);
|
|
4076
4094
|
if (!instance) {
|
|
4077
4095
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
4078
4096
|
}
|
|
4079
4097
|
const result = await this.executeCommand(
|
|
4080
4098
|
sandboxId,
|
|
4081
|
-
`ls -la "${
|
|
4099
|
+
`ls -la "${path15}" 2>/dev/null || echo ""`
|
|
4082
4100
|
);
|
|
4083
4101
|
if (result.exitCode !== 0 || !result.stdout.trim()) {
|
|
4084
4102
|
return [];
|
|
@@ -4093,7 +4111,7 @@ VERCEL_EOF`);
|
|
|
4093
4111
|
const [, type, , size, , name] = match;
|
|
4094
4112
|
if (name && name !== "." && name !== ".." && size) {
|
|
4095
4113
|
files.push({
|
|
4096
|
-
path: `${
|
|
4114
|
+
path: `${path15}/${name}`.replace(/\/+/g, "/"),
|
|
4097
4115
|
name,
|
|
4098
4116
|
size: parseInt(size, 10),
|
|
4099
4117
|
isDirectory: type === "d"
|
|
@@ -4103,12 +4121,12 @@ VERCEL_EOF`);
|
|
|
4103
4121
|
}
|
|
4104
4122
|
return files;
|
|
4105
4123
|
}
|
|
4106
|
-
async deleteFile(sandboxId,
|
|
4124
|
+
async deleteFile(sandboxId, path15) {
|
|
4107
4125
|
const instance = this.sandboxes.get(sandboxId);
|
|
4108
4126
|
if (!instance) {
|
|
4109
4127
|
throw new SandboxNotFoundError(this.name, sandboxId);
|
|
4110
4128
|
}
|
|
4111
|
-
await this.executeCommand(sandboxId, `rm -rf "${
|
|
4129
|
+
await this.executeCommand(sandboxId, `rm -rf "${path15}"`);
|
|
4112
4130
|
}
|
|
4113
4131
|
async getLogs(sandboxId, _options) {
|
|
4114
4132
|
const instance = this.sandboxes.get(sandboxId);
|
|
@@ -5241,7 +5259,7 @@ function isSandboxRunning(sessionId) {
|
|
|
5241
5259
|
const cached = sandboxCache.get(sessionId);
|
|
5242
5260
|
return cached !== void 0 && !cached.isExpired;
|
|
5243
5261
|
}
|
|
5244
|
-
async function writeFileToSandbox(sessionId,
|
|
5262
|
+
async function writeFileToSandbox(sessionId, path15, content) {
|
|
5245
5263
|
const cached = sandboxCache.get(sessionId);
|
|
5246
5264
|
if (!cached) {
|
|
5247
5265
|
return { success: false, error: "No active sandbox for session" };
|
|
@@ -5251,14 +5269,14 @@ async function writeFileToSandbox(sessionId, path14, content) {
|
|
|
5251
5269
|
}
|
|
5252
5270
|
try {
|
|
5253
5271
|
const sandbox = cached.sandbox;
|
|
5254
|
-
const dir =
|
|
5272
|
+
const dir = path15.substring(0, path15.lastIndexOf("/"));
|
|
5255
5273
|
if (dir) {
|
|
5256
5274
|
await sandbox.runCommand({ cmd: "mkdir", args: ["-p", dir] });
|
|
5257
5275
|
}
|
|
5258
5276
|
const base64Content = content.toString("base64");
|
|
5259
5277
|
const result = await sandbox.runCommand({
|
|
5260
5278
|
cmd: "bash",
|
|
5261
|
-
args: ["-c", `echo '${base64Content}' | base64 -d > '${
|
|
5279
|
+
args: ["-c", `echo '${base64Content}' | base64 -d > '${path15}'`]
|
|
5262
5280
|
});
|
|
5263
5281
|
if (result.exitCode !== 0) {
|
|
5264
5282
|
const stderr = await result.stderr();
|
|
@@ -5273,7 +5291,7 @@ async function writeFileToSandbox(sessionId, path14, content) {
|
|
|
5273
5291
|
};
|
|
5274
5292
|
}
|
|
5275
5293
|
}
|
|
5276
|
-
async function readFileFromSandbox(sessionId,
|
|
5294
|
+
async function readFileFromSandbox(sessionId, path15) {
|
|
5277
5295
|
const cached = sandboxCache.get(sessionId);
|
|
5278
5296
|
if (!cached) {
|
|
5279
5297
|
return { success: false, error: "No active sandbox for session" };
|
|
@@ -5285,14 +5303,14 @@ async function readFileFromSandbox(sessionId, path14) {
|
|
|
5285
5303
|
const sandbox = cached.sandbox;
|
|
5286
5304
|
const checkResult = await sandbox.runCommand({
|
|
5287
5305
|
cmd: "test",
|
|
5288
|
-
args: ["-f",
|
|
5306
|
+
args: ["-f", path15]
|
|
5289
5307
|
});
|
|
5290
5308
|
if (checkResult.exitCode !== 0) {
|
|
5291
5309
|
return { success: false, error: "File not found" };
|
|
5292
5310
|
}
|
|
5293
5311
|
const result = await sandbox.runCommand({
|
|
5294
5312
|
cmd: "base64",
|
|
5295
|
-
args: [
|
|
5313
|
+
args: [path15]
|
|
5296
5314
|
});
|
|
5297
5315
|
if (result.exitCode !== 0) {
|
|
5298
5316
|
const stderr = await result.stderr();
|
|
@@ -5309,7 +5327,7 @@ async function readFileFromSandbox(sessionId, path14) {
|
|
|
5309
5327
|
};
|
|
5310
5328
|
}
|
|
5311
5329
|
}
|
|
5312
|
-
async function listFilesInSandbox(sessionId,
|
|
5330
|
+
async function listFilesInSandbox(sessionId, path15) {
|
|
5313
5331
|
const cached = sandboxCache.get(sessionId);
|
|
5314
5332
|
if (!cached) {
|
|
5315
5333
|
return { success: false, error: "No active sandbox for session" };
|
|
@@ -5321,14 +5339,14 @@ async function listFilesInSandbox(sessionId, path14) {
|
|
|
5321
5339
|
const sandbox = cached.sandbox;
|
|
5322
5340
|
const checkResult = await sandbox.runCommand({
|
|
5323
5341
|
cmd: "test",
|
|
5324
|
-
args: ["-d",
|
|
5342
|
+
args: ["-d", path15]
|
|
5325
5343
|
});
|
|
5326
5344
|
if (checkResult.exitCode !== 0) {
|
|
5327
5345
|
return { success: true, files: [] };
|
|
5328
5346
|
}
|
|
5329
5347
|
const result = await sandbox.runCommand({
|
|
5330
5348
|
cmd: "find",
|
|
5331
|
-
args: [
|
|
5349
|
+
args: [path15, "-type", "f", "-printf", "%P\\n"]
|
|
5332
5350
|
});
|
|
5333
5351
|
if (result.exitCode !== 0) {
|
|
5334
5352
|
const stderr = await result.stderr();
|
|
@@ -5776,8 +5794,516 @@ var init_vercel_sandbox_executor = __esm({
|
|
|
5776
5794
|
heartbeatListeners = /* @__PURE__ */ new Set();
|
|
5777
5795
|
}
|
|
5778
5796
|
});
|
|
5779
|
-
|
|
5780
|
-
|
|
5797
|
+
function createFileWatcher(options) {
|
|
5798
|
+
return new SandboxFileWatcher(options);
|
|
5799
|
+
}
|
|
5800
|
+
function getFileWatcherManager() {
|
|
5801
|
+
if (!globalWatcherManager) {
|
|
5802
|
+
globalWatcherManager = new FileWatcherManager();
|
|
5803
|
+
}
|
|
5804
|
+
return globalWatcherManager;
|
|
5805
|
+
}
|
|
5806
|
+
function createFileWatcherManager() {
|
|
5807
|
+
return new FileWatcherManager();
|
|
5808
|
+
}
|
|
5809
|
+
function createRemoteFileWatcher(options) {
|
|
5810
|
+
return new RemoteSandboxFileWatcher(options);
|
|
5811
|
+
}
|
|
5812
|
+
function getRemoteFileWatcherManager() {
|
|
5813
|
+
if (!globalRemoteWatcherManager) {
|
|
5814
|
+
globalRemoteWatcherManager = new RemoteFileWatcherManager();
|
|
5815
|
+
}
|
|
5816
|
+
return globalRemoteWatcherManager;
|
|
5817
|
+
}
|
|
5818
|
+
function createRemoteFileWatcherManager() {
|
|
5819
|
+
return new RemoteFileWatcherManager();
|
|
5820
|
+
}
|
|
5821
|
+
var SandboxFileWatcher, FileWatcherManager, globalWatcherManager, RemoteSandboxFileWatcher, RemoteFileWatcherManager, globalRemoteWatcherManager;
|
|
5822
|
+
var init_sandbox_file_watcher = __esm({
|
|
5823
|
+
"src/runtime/sandbox-file-watcher.ts"() {
|
|
5824
|
+
SandboxFileWatcher = class {
|
|
5825
|
+
sessionId;
|
|
5826
|
+
watchPath;
|
|
5827
|
+
debounceMs;
|
|
5828
|
+
patterns;
|
|
5829
|
+
ignored;
|
|
5830
|
+
emitInitialEvents;
|
|
5831
|
+
followSymlinks;
|
|
5832
|
+
usePolling;
|
|
5833
|
+
pollInterval;
|
|
5834
|
+
watcher = null;
|
|
5835
|
+
pendingEvents = /* @__PURE__ */ new Map();
|
|
5836
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
5837
|
+
isWatching = false;
|
|
5838
|
+
startedAt;
|
|
5839
|
+
watchedFileCount = 0;
|
|
5840
|
+
onError;
|
|
5841
|
+
onReady;
|
|
5842
|
+
constructor(options) {
|
|
5843
|
+
this.sessionId = options.sessionId;
|
|
5844
|
+
this.watchPath = options.watchPath;
|
|
5845
|
+
this.debounceMs = options.debounceMs ?? 300;
|
|
5846
|
+
this.patterns = options.patterns ?? ["**/*"];
|
|
5847
|
+
this.ignored = options.ignored ?? ["**/node_modules/**", "**/.git/**", "**/*.log"];
|
|
5848
|
+
this.emitInitialEvents = options.emitInitialEvents ?? false;
|
|
5849
|
+
this.followSymlinks = options.followSymlinks ?? false;
|
|
5850
|
+
this.usePolling = options.usePolling ?? false;
|
|
5851
|
+
this.pollInterval = options.pollInterval ?? 100;
|
|
5852
|
+
this.onError = options.onError;
|
|
5853
|
+
this.onReady = options.onReady;
|
|
5854
|
+
if (options.onFileChange) {
|
|
5855
|
+
this.subscribers.add(options.onFileChange);
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Start watching the directory for changes
|
|
5860
|
+
*/
|
|
5861
|
+
async start() {
|
|
5862
|
+
if (this.isWatching) {
|
|
5863
|
+
console.warn(`[FILE_WATCHER] Already watching ${this.watchPath}`);
|
|
5864
|
+
return;
|
|
5865
|
+
}
|
|
5866
|
+
try {
|
|
5867
|
+
const stat2 = await fs.stat(this.watchPath);
|
|
5868
|
+
if (!stat2.isDirectory()) {
|
|
5869
|
+
throw new Error(`Watch path is not a directory: ${this.watchPath}`);
|
|
5870
|
+
}
|
|
5871
|
+
} catch (error) {
|
|
5872
|
+
if (error.code === "ENOENT") {
|
|
5873
|
+
throw new Error(`Watch path does not exist: ${this.watchPath}`);
|
|
5874
|
+
}
|
|
5875
|
+
throw error;
|
|
5876
|
+
}
|
|
5877
|
+
const watchPaths = this.patterns.map(
|
|
5878
|
+
(pattern) => path4.join(this.watchPath, pattern)
|
|
5879
|
+
);
|
|
5880
|
+
this.watcher = chokidar.watch(watchPaths, {
|
|
5881
|
+
ignored: this.ignored,
|
|
5882
|
+
persistent: true,
|
|
5883
|
+
ignoreInitial: !this.emitInitialEvents,
|
|
5884
|
+
followSymlinks: this.followSymlinks,
|
|
5885
|
+
usePolling: this.usePolling,
|
|
5886
|
+
interval: this.pollInterval,
|
|
5887
|
+
awaitWriteFinish: {
|
|
5888
|
+
stabilityThreshold: this.debounceMs,
|
|
5889
|
+
pollInterval: 100
|
|
5890
|
+
}
|
|
5891
|
+
});
|
|
5892
|
+
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) => {
|
|
5893
|
+
console.error(`[FILE_WATCHER] Error watching ${this.watchPath}:`, error);
|
|
5894
|
+
if (this.onError) {
|
|
5895
|
+
this.onError(error);
|
|
5896
|
+
}
|
|
5897
|
+
}).on("ready", () => {
|
|
5898
|
+
console.log(`[FILE_WATCHER] Ready to watch ${this.watchPath}`);
|
|
5899
|
+
this.isWatching = true;
|
|
5900
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
5901
|
+
if (this.onReady) {
|
|
5902
|
+
this.onReady();
|
|
5903
|
+
}
|
|
5904
|
+
});
|
|
5905
|
+
this.watcher.on("add", () => this.watchedFileCount++);
|
|
5906
|
+
this.watcher.on("unlink", () => this.watchedFileCount--);
|
|
5907
|
+
}
|
|
5908
|
+
/**
|
|
5909
|
+
* Stop watching and cleanup resources
|
|
5910
|
+
*/
|
|
5911
|
+
async stop() {
|
|
5912
|
+
if (!this.isWatching || !this.watcher) {
|
|
5913
|
+
return;
|
|
5914
|
+
}
|
|
5915
|
+
for (const pending of this.pendingEvents.values()) {
|
|
5916
|
+
clearTimeout(pending.timer);
|
|
5917
|
+
}
|
|
5918
|
+
this.pendingEvents.clear();
|
|
5919
|
+
await this.watcher.close();
|
|
5920
|
+
this.watcher = null;
|
|
5921
|
+
this.isWatching = false;
|
|
5922
|
+
this.watchedFileCount = 0;
|
|
5923
|
+
console.log(`[FILE_WATCHER] Stopped watching ${this.watchPath}`);
|
|
5924
|
+
}
|
|
5925
|
+
/**
|
|
5926
|
+
* Subscribe to file change events
|
|
5927
|
+
* @returns Unsubscribe function
|
|
5928
|
+
*/
|
|
5929
|
+
subscribe(callback) {
|
|
5930
|
+
this.subscribers.add(callback);
|
|
5931
|
+
return () => this.subscribers.delete(callback);
|
|
5932
|
+
}
|
|
5933
|
+
/**
|
|
5934
|
+
* Remove a subscriber
|
|
5935
|
+
*/
|
|
5936
|
+
unsubscribe(callback) {
|
|
5937
|
+
this.subscribers.delete(callback);
|
|
5938
|
+
}
|
|
5939
|
+
/**
|
|
5940
|
+
* Get the current status of the watcher
|
|
5941
|
+
*/
|
|
5942
|
+
getStatus() {
|
|
5943
|
+
return {
|
|
5944
|
+
sessionId: this.sessionId,
|
|
5945
|
+
watchPath: this.watchPath,
|
|
5946
|
+
isWatching: this.isWatching,
|
|
5947
|
+
watchedFileCount: this.watchedFileCount,
|
|
5948
|
+
pendingEventCount: this.pendingEvents.size,
|
|
5949
|
+
startedAt: this.startedAt
|
|
5950
|
+
};
|
|
5951
|
+
}
|
|
5952
|
+
/**
|
|
5953
|
+
* Check if the watcher is currently active
|
|
5954
|
+
*/
|
|
5955
|
+
isActive() {
|
|
5956
|
+
return this.isWatching;
|
|
5957
|
+
}
|
|
5958
|
+
/**
|
|
5959
|
+
* Handle a file system event with debouncing
|
|
5960
|
+
*/
|
|
5961
|
+
handleFileEvent(type, absolutePath) {
|
|
5962
|
+
const relativePath = path4.relative(this.watchPath, absolutePath);
|
|
5963
|
+
const existing = this.pendingEvents.get(absolutePath);
|
|
5964
|
+
if (existing) {
|
|
5965
|
+
clearTimeout(existing.timer);
|
|
5966
|
+
}
|
|
5967
|
+
const timer = setTimeout(async () => {
|
|
5968
|
+
this.pendingEvents.delete(absolutePath);
|
|
5969
|
+
await this.emitEvent(type, absolutePath, relativePath);
|
|
5970
|
+
}, this.debounceMs);
|
|
5971
|
+
this.pendingEvents.set(absolutePath, {
|
|
5972
|
+
type,
|
|
5973
|
+
absolutePath,
|
|
5974
|
+
relativePath,
|
|
5975
|
+
timer
|
|
5976
|
+
});
|
|
5977
|
+
}
|
|
5978
|
+
/**
|
|
5979
|
+
* Emit a file change event to all subscribers
|
|
5980
|
+
*/
|
|
5981
|
+
async emitEvent(type, absolutePath, relativePath) {
|
|
5982
|
+
let fileSize;
|
|
5983
|
+
if (type === "add" || type === "change") {
|
|
5984
|
+
try {
|
|
5985
|
+
const stat2 = await fs.stat(absolutePath);
|
|
5986
|
+
fileSize = stat2.size;
|
|
5987
|
+
} catch {
|
|
5988
|
+
}
|
|
5989
|
+
}
|
|
5990
|
+
const event = {
|
|
5991
|
+
type,
|
|
5992
|
+
relativePath,
|
|
5993
|
+
absolutePath,
|
|
5994
|
+
sessionId: this.sessionId,
|
|
5995
|
+
fileSize,
|
|
5996
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
5997
|
+
};
|
|
5998
|
+
for (const callback of this.subscribers) {
|
|
5999
|
+
try {
|
|
6000
|
+
await callback(event);
|
|
6001
|
+
} catch (error) {
|
|
6002
|
+
console.error("[FILE_WATCHER] Error in subscriber callback:", error);
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
6006
|
+
};
|
|
6007
|
+
FileWatcherManager = class {
|
|
6008
|
+
watchers = /* @__PURE__ */ new Map();
|
|
6009
|
+
/**
|
|
6010
|
+
* Start watching a session's sandbox directory
|
|
6011
|
+
*/
|
|
6012
|
+
async startWatching(options) {
|
|
6013
|
+
const { sessionId } = options;
|
|
6014
|
+
const existing = this.watchers.get(sessionId);
|
|
6015
|
+
if (existing) {
|
|
6016
|
+
await existing.stop();
|
|
6017
|
+
}
|
|
6018
|
+
const watcher = new SandboxFileWatcher(options);
|
|
6019
|
+
await watcher.start();
|
|
6020
|
+
this.watchers.set(sessionId, watcher);
|
|
6021
|
+
return watcher;
|
|
6022
|
+
}
|
|
6023
|
+
/**
|
|
6024
|
+
* Stop watching a session's sandbox directory
|
|
6025
|
+
*/
|
|
6026
|
+
async stopWatching(sessionId) {
|
|
6027
|
+
const watcher = this.watchers.get(sessionId);
|
|
6028
|
+
if (watcher) {
|
|
6029
|
+
await watcher.stop();
|
|
6030
|
+
this.watchers.delete(sessionId);
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
/**
|
|
6034
|
+
* Get a watcher for a session
|
|
6035
|
+
*/
|
|
6036
|
+
getWatcher(sessionId) {
|
|
6037
|
+
return this.watchers.get(sessionId);
|
|
6038
|
+
}
|
|
6039
|
+
/**
|
|
6040
|
+
* Check if a session is being watched
|
|
6041
|
+
*/
|
|
6042
|
+
isWatching(sessionId) {
|
|
6043
|
+
const watcher = this.watchers.get(sessionId);
|
|
6044
|
+
return watcher?.isActive() ?? false;
|
|
6045
|
+
}
|
|
6046
|
+
/**
|
|
6047
|
+
* Get status of all watchers
|
|
6048
|
+
*/
|
|
6049
|
+
getAllStatuses() {
|
|
6050
|
+
return Array.from(this.watchers.values()).map((w) => w.getStatus());
|
|
6051
|
+
}
|
|
6052
|
+
/**
|
|
6053
|
+
* Stop all watchers and cleanup
|
|
6054
|
+
*/
|
|
6055
|
+
async stopAll() {
|
|
6056
|
+
const promises = Array.from(this.watchers.values()).map((w) => w.stop());
|
|
6057
|
+
await Promise.all(promises);
|
|
6058
|
+
this.watchers.clear();
|
|
6059
|
+
}
|
|
6060
|
+
};
|
|
6061
|
+
globalWatcherManager = null;
|
|
6062
|
+
RemoteSandboxFileWatcher = class {
|
|
6063
|
+
sessionId;
|
|
6064
|
+
sandboxOps;
|
|
6065
|
+
basePath;
|
|
6066
|
+
pollIntervalMs;
|
|
6067
|
+
ignored;
|
|
6068
|
+
pollTimer = null;
|
|
6069
|
+
previousFiles = /* @__PURE__ */ new Map();
|
|
6070
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
6071
|
+
isWatching = false;
|
|
6072
|
+
startedAt;
|
|
6073
|
+
lastPollAt;
|
|
6074
|
+
pollCount = 0;
|
|
6075
|
+
onError;
|
|
6076
|
+
constructor(options) {
|
|
6077
|
+
this.sessionId = options.sessionId;
|
|
6078
|
+
this.sandboxOps = options.sandboxOps;
|
|
6079
|
+
this.basePath = options.basePath;
|
|
6080
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
6081
|
+
this.ignored = options.ignored ?? ["**/node_modules/**", "**/.git/**"];
|
|
6082
|
+
this.onError = options.onError;
|
|
6083
|
+
if (options.onFileChange) {
|
|
6084
|
+
this.subscribers.add(options.onFileChange);
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
/**
|
|
6088
|
+
* Start polling for file changes
|
|
6089
|
+
*/
|
|
6090
|
+
async start() {
|
|
6091
|
+
if (this.isWatching) {
|
|
6092
|
+
console.warn(`[REMOTE_WATCHER] Already watching session ${this.sessionId}`);
|
|
6093
|
+
return;
|
|
6094
|
+
}
|
|
6095
|
+
if (!this.sandboxOps.isSandboxRunning(this.sessionId)) {
|
|
6096
|
+
throw new Error(`Sandbox is not running for session ${this.sessionId}`);
|
|
6097
|
+
}
|
|
6098
|
+
await this.scan(true);
|
|
6099
|
+
this.pollTimer = setInterval(async () => {
|
|
6100
|
+
try {
|
|
6101
|
+
await this.scan(false);
|
|
6102
|
+
} catch (error) {
|
|
6103
|
+
console.error(`[REMOTE_WATCHER] Poll error for session ${this.sessionId}:`, error);
|
|
6104
|
+
if (this.onError && error instanceof Error) {
|
|
6105
|
+
this.onError(error);
|
|
6106
|
+
}
|
|
6107
|
+
}
|
|
6108
|
+
}, this.pollIntervalMs);
|
|
6109
|
+
this.isWatching = true;
|
|
6110
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
6111
|
+
console.log(`[REMOTE_WATCHER] Started watching session ${this.sessionId} at ${this.basePath}`);
|
|
6112
|
+
}
|
|
6113
|
+
/**
|
|
6114
|
+
* Stop polling and cleanup
|
|
6115
|
+
*/
|
|
6116
|
+
async stop() {
|
|
6117
|
+
if (!this.isWatching) {
|
|
6118
|
+
return;
|
|
6119
|
+
}
|
|
6120
|
+
if (this.pollTimer) {
|
|
6121
|
+
clearInterval(this.pollTimer);
|
|
6122
|
+
this.pollTimer = null;
|
|
6123
|
+
}
|
|
6124
|
+
this.isWatching = false;
|
|
6125
|
+
this.previousFiles.clear();
|
|
6126
|
+
console.log(`[REMOTE_WATCHER] Stopped watching session ${this.sessionId}`);
|
|
6127
|
+
}
|
|
6128
|
+
/**
|
|
6129
|
+
* Subscribe to file change events
|
|
6130
|
+
*/
|
|
6131
|
+
subscribe(callback) {
|
|
6132
|
+
this.subscribers.add(callback);
|
|
6133
|
+
return () => this.subscribers.delete(callback);
|
|
6134
|
+
}
|
|
6135
|
+
/**
|
|
6136
|
+
* Remove a subscriber
|
|
6137
|
+
*/
|
|
6138
|
+
unsubscribe(callback) {
|
|
6139
|
+
this.subscribers.delete(callback);
|
|
6140
|
+
}
|
|
6141
|
+
/**
|
|
6142
|
+
* Check if watcher is active
|
|
6143
|
+
*/
|
|
6144
|
+
isActive() {
|
|
6145
|
+
return this.isWatching;
|
|
6146
|
+
}
|
|
6147
|
+
/**
|
|
6148
|
+
* Get watcher status
|
|
6149
|
+
*/
|
|
6150
|
+
getStatus() {
|
|
6151
|
+
return {
|
|
6152
|
+
sessionId: this.sessionId,
|
|
6153
|
+
watchPath: this.basePath,
|
|
6154
|
+
isWatching: this.isWatching,
|
|
6155
|
+
watchedFileCount: this.previousFiles.size,
|
|
6156
|
+
pendingEventCount: 0,
|
|
6157
|
+
startedAt: this.startedAt,
|
|
6158
|
+
lastPollAt: this.lastPollAt,
|
|
6159
|
+
pollCount: this.pollCount
|
|
6160
|
+
};
|
|
6161
|
+
}
|
|
6162
|
+
/**
|
|
6163
|
+
* Force an immediate scan (useful for testing or manual refresh)
|
|
6164
|
+
*/
|
|
6165
|
+
async forceScan() {
|
|
6166
|
+
await this.scan(false);
|
|
6167
|
+
}
|
|
6168
|
+
/**
|
|
6169
|
+
* Scan the sandbox for file changes
|
|
6170
|
+
*/
|
|
6171
|
+
async scan(isInitial) {
|
|
6172
|
+
if (!this.sandboxOps.isSandboxRunning(this.sessionId)) {
|
|
6173
|
+
console.warn(`[REMOTE_WATCHER] Sandbox stopped for session ${this.sessionId}`);
|
|
6174
|
+
await this.stop();
|
|
6175
|
+
return;
|
|
6176
|
+
}
|
|
6177
|
+
const listResult = await this.sandboxOps.listFiles(this.sessionId, this.basePath);
|
|
6178
|
+
if (!listResult.success || !listResult.files) {
|
|
6179
|
+
throw new Error(listResult.error ?? "Failed to list files in sandbox");
|
|
6180
|
+
}
|
|
6181
|
+
const currentFiles = /* @__PURE__ */ new Map();
|
|
6182
|
+
for (const filePath of listResult.files) {
|
|
6183
|
+
if (this.shouldIgnore(filePath)) {
|
|
6184
|
+
continue;
|
|
6185
|
+
}
|
|
6186
|
+
currentFiles.set(filePath, { path: filePath });
|
|
6187
|
+
}
|
|
6188
|
+
this.lastPollAt = /* @__PURE__ */ new Date();
|
|
6189
|
+
this.pollCount++;
|
|
6190
|
+
if (isInitial) {
|
|
6191
|
+
this.previousFiles = currentFiles;
|
|
6192
|
+
return;
|
|
6193
|
+
}
|
|
6194
|
+
const changes = [];
|
|
6195
|
+
for (const [filePath, info] of currentFiles) {
|
|
6196
|
+
const previous = this.previousFiles.get(filePath);
|
|
6197
|
+
if (!previous) {
|
|
6198
|
+
changes.push({
|
|
6199
|
+
type: "add",
|
|
6200
|
+
relativePath: filePath,
|
|
6201
|
+
absolutePath: `${this.basePath}/${filePath}`,
|
|
6202
|
+
sessionId: this.sessionId,
|
|
6203
|
+
fileSize: info.size,
|
|
6204
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
6205
|
+
});
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
for (const [filePath] of this.previousFiles) {
|
|
6209
|
+
if (!currentFiles.has(filePath)) {
|
|
6210
|
+
changes.push({
|
|
6211
|
+
type: "unlink",
|
|
6212
|
+
relativePath: filePath,
|
|
6213
|
+
absolutePath: `${this.basePath}/${filePath}`,
|
|
6214
|
+
sessionId: this.sessionId,
|
|
6215
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
6216
|
+
});
|
|
6217
|
+
}
|
|
6218
|
+
}
|
|
6219
|
+
this.previousFiles = currentFiles;
|
|
6220
|
+
for (const event of changes) {
|
|
6221
|
+
await this.emitEvent(event);
|
|
6222
|
+
}
|
|
6223
|
+
}
|
|
6224
|
+
/**
|
|
6225
|
+
* Check if a path should be ignored
|
|
6226
|
+
*/
|
|
6227
|
+
shouldIgnore(filePath) {
|
|
6228
|
+
for (const pattern of this.ignored) {
|
|
6229
|
+
if (this.matchesGlob(filePath, pattern)) {
|
|
6230
|
+
return true;
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
return false;
|
|
6234
|
+
}
|
|
6235
|
+
/**
|
|
6236
|
+
* Simple glob matching (supports ** and *)
|
|
6237
|
+
*/
|
|
6238
|
+
matchesGlob(filePath, pattern) {
|
|
6239
|
+
const regexPattern = pattern.replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/{{GLOBSTAR}}/g, ".*").replace(/\//g, "\\/");
|
|
6240
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
6241
|
+
return regex.test(filePath);
|
|
6242
|
+
}
|
|
6243
|
+
/**
|
|
6244
|
+
* Emit a file change event to all subscribers
|
|
6245
|
+
*/
|
|
6246
|
+
async emitEvent(event) {
|
|
6247
|
+
for (const callback of this.subscribers) {
|
|
6248
|
+
try {
|
|
6249
|
+
await callback(event);
|
|
6250
|
+
} catch (error) {
|
|
6251
|
+
console.error("[REMOTE_WATCHER] Error in subscriber callback:", error);
|
|
6252
|
+
}
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
};
|
|
6256
|
+
RemoteFileWatcherManager = class {
|
|
6257
|
+
watchers = /* @__PURE__ */ new Map();
|
|
6258
|
+
/**
|
|
6259
|
+
* Start watching a session's sandbox
|
|
6260
|
+
*/
|
|
6261
|
+
async startWatching(options) {
|
|
6262
|
+
const { sessionId } = options;
|
|
6263
|
+
const existing = this.watchers.get(sessionId);
|
|
6264
|
+
if (existing) {
|
|
6265
|
+
await existing.stop();
|
|
6266
|
+
}
|
|
6267
|
+
const watcher = new RemoteSandboxFileWatcher(options);
|
|
6268
|
+
await watcher.start();
|
|
6269
|
+
this.watchers.set(sessionId, watcher);
|
|
6270
|
+
return watcher;
|
|
6271
|
+
}
|
|
6272
|
+
/**
|
|
6273
|
+
* Stop watching a session
|
|
6274
|
+
*/
|
|
6275
|
+
async stopWatching(sessionId) {
|
|
6276
|
+
const watcher = this.watchers.get(sessionId);
|
|
6277
|
+
if (watcher) {
|
|
6278
|
+
await watcher.stop();
|
|
6279
|
+
this.watchers.delete(sessionId);
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
/**
|
|
6283
|
+
* Get a watcher for a session
|
|
6284
|
+
*/
|
|
6285
|
+
getWatcher(sessionId) {
|
|
6286
|
+
return this.watchers.get(sessionId);
|
|
6287
|
+
}
|
|
6288
|
+
/**
|
|
6289
|
+
* Check if a session is being watched
|
|
6290
|
+
*/
|
|
6291
|
+
isWatching(sessionId) {
|
|
6292
|
+
const watcher = this.watchers.get(sessionId);
|
|
6293
|
+
return watcher?.isActive() ?? false;
|
|
6294
|
+
}
|
|
6295
|
+
/**
|
|
6296
|
+
* Stop all watchers
|
|
6297
|
+
*/
|
|
6298
|
+
async stopAll() {
|
|
6299
|
+
const promises = Array.from(this.watchers.values()).map((w) => w.stop());
|
|
6300
|
+
await Promise.all(promises);
|
|
6301
|
+
this.watchers.clear();
|
|
6302
|
+
}
|
|
6303
|
+
};
|
|
6304
|
+
globalRemoteWatcherManager = null;
|
|
6305
|
+
}
|
|
6306
|
+
});
|
|
5781
6307
|
function extractErrorMessage(error) {
|
|
5782
6308
|
if (error === null || error === void 0) {
|
|
5783
6309
|
return "Unknown error (null/undefined)";
|
|
@@ -5823,13 +6349,256 @@ function createSandboxFileSync(options) {
|
|
|
5823
6349
|
var SandboxFileSync;
|
|
5824
6350
|
var init_sandbox_file_sync = __esm({
|
|
5825
6351
|
"src/runtime/sandbox-file-sync.ts"() {
|
|
6352
|
+
init_sandbox_file_watcher();
|
|
6353
|
+
init_types();
|
|
5826
6354
|
SandboxFileSync = class {
|
|
5827
6355
|
fileStore;
|
|
5828
6356
|
sandboxBasePath;
|
|
6357
|
+
defaultWatchOptions;
|
|
5829
6358
|
sandboxOps = null;
|
|
6359
|
+
onFileEvent;
|
|
6360
|
+
eventStorage;
|
|
6361
|
+
webhookConfig;
|
|
6362
|
+
// Watcher management
|
|
6363
|
+
remoteWatchers = /* @__PURE__ */ new Map();
|
|
6364
|
+
localWatchers = /* @__PURE__ */ new Map();
|
|
6365
|
+
fileChangeSubscribers = /* @__PURE__ */ new Set();
|
|
6366
|
+
// Sequence number cache per session (for event storage)
|
|
6367
|
+
sequenceNumbers = /* @__PURE__ */ new Map();
|
|
5830
6368
|
constructor(options) {
|
|
5831
6369
|
this.fileStore = options.fileStore;
|
|
5832
6370
|
this.sandboxBasePath = options.sandboxBasePath ?? ".claude/files";
|
|
6371
|
+
this.onFileEvent = options.onFileEvent;
|
|
6372
|
+
this.defaultWatchOptions = options.watchOptions ?? {};
|
|
6373
|
+
this.eventStorage = options.eventStorage;
|
|
6374
|
+
this.webhookConfig = options.webhook;
|
|
6375
|
+
}
|
|
6376
|
+
/**
|
|
6377
|
+
* Set the file event callback
|
|
6378
|
+
* This can be used to set or update the callback after construction
|
|
6379
|
+
*/
|
|
6380
|
+
setFileEventCallback(callback) {
|
|
6381
|
+
this.onFileEvent = callback;
|
|
6382
|
+
}
|
|
6383
|
+
/**
|
|
6384
|
+
* Set the event storage for persisting file events to the timeline
|
|
6385
|
+
* This can be used to set or update the storage after construction
|
|
6386
|
+
*/
|
|
6387
|
+
setEventStorage(storage) {
|
|
6388
|
+
this.eventStorage = storage;
|
|
6389
|
+
}
|
|
6390
|
+
/**
|
|
6391
|
+
* Set webhook configuration for external notifications
|
|
6392
|
+
* This can be used to set or update the webhook after construction
|
|
6393
|
+
*/
|
|
6394
|
+
setWebhook(config) {
|
|
6395
|
+
this.webhookConfig = config;
|
|
6396
|
+
}
|
|
6397
|
+
/**
|
|
6398
|
+
* Remove webhook configuration
|
|
6399
|
+
*/
|
|
6400
|
+
removeWebhook() {
|
|
6401
|
+
this.webhookConfig = void 0;
|
|
6402
|
+
}
|
|
6403
|
+
/**
|
|
6404
|
+
* Send a webhook notification
|
|
6405
|
+
* @param payload - The webhook payload to send
|
|
6406
|
+
*/
|
|
6407
|
+
async sendWebhook(payload) {
|
|
6408
|
+
if (!this.webhookConfig) return;
|
|
6409
|
+
const config = this.webhookConfig;
|
|
6410
|
+
const eventType = payload.fileSyncEvent?.operation ?? "file_change";
|
|
6411
|
+
if (config.events && config.events.length > 0) {
|
|
6412
|
+
const shouldSend = config.events.some(
|
|
6413
|
+
(e) => e === eventType || e === "file_change" && payload.event === "file_change"
|
|
6414
|
+
);
|
|
6415
|
+
if (!shouldSend) return;
|
|
6416
|
+
}
|
|
6417
|
+
const body = JSON.stringify(payload);
|
|
6418
|
+
const timeoutMs = config.timeoutMs ?? 1e4;
|
|
6419
|
+
const retries = config.retries ?? 3;
|
|
6420
|
+
const isAsync = config.async !== false;
|
|
6421
|
+
const headers = {
|
|
6422
|
+
"Content-Type": "application/json",
|
|
6423
|
+
"User-Agent": "Ash-FileSync/1.0",
|
|
6424
|
+
...config.headers
|
|
6425
|
+
};
|
|
6426
|
+
if (config.secret) {
|
|
6427
|
+
const signature = createHmac("sha256", config.secret).update(body).digest("hex");
|
|
6428
|
+
headers["X-Ash-Signature"] = `sha256=${signature}`;
|
|
6429
|
+
headers["X-Ash-Timestamp"] = payload.timestamp;
|
|
6430
|
+
}
|
|
6431
|
+
const sendWithRetry = async (attempt) => {
|
|
6432
|
+
try {
|
|
6433
|
+
const controller = new AbortController();
|
|
6434
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
6435
|
+
const response = await fetch(config.url, {
|
|
6436
|
+
method: "POST",
|
|
6437
|
+
headers,
|
|
6438
|
+
body,
|
|
6439
|
+
signal: controller.signal
|
|
6440
|
+
});
|
|
6441
|
+
clearTimeout(timeoutId);
|
|
6442
|
+
if (!response.ok) {
|
|
6443
|
+
throw new Error(`Webhook returned ${response.status}: ${response.statusText}`);
|
|
6444
|
+
}
|
|
6445
|
+
console.log(`[FILE_SYNC] Webhook sent successfully to ${config.url}`);
|
|
6446
|
+
} catch (error) {
|
|
6447
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
6448
|
+
if (attempt < retries) {
|
|
6449
|
+
const delay = Math.pow(2, attempt) * 1e3;
|
|
6450
|
+
console.warn(`[FILE_SYNC] Webhook failed (attempt ${attempt + 1}/${retries}), retrying in ${delay}ms: ${errorMsg}`);
|
|
6451
|
+
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
6452
|
+
return sendWithRetry(attempt + 1);
|
|
6453
|
+
}
|
|
6454
|
+
console.error(`[FILE_SYNC] Webhook failed after ${retries} attempts: ${errorMsg}`);
|
|
6455
|
+
throw error;
|
|
6456
|
+
}
|
|
6457
|
+
};
|
|
6458
|
+
if (isAsync) {
|
|
6459
|
+
sendWithRetry(0).catch((error) => {
|
|
6460
|
+
console.error("[FILE_SYNC] Async webhook failed:", error);
|
|
6461
|
+
});
|
|
6462
|
+
} else {
|
|
6463
|
+
await sendWithRetry(0);
|
|
6464
|
+
}
|
|
6465
|
+
}
|
|
6466
|
+
/**
|
|
6467
|
+
* Send a file sync event webhook
|
|
6468
|
+
*/
|
|
6469
|
+
async sendFileSyncWebhook(sessionId, event) {
|
|
6470
|
+
const payload = {
|
|
6471
|
+
event: "file_sync",
|
|
6472
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6473
|
+
sessionId,
|
|
6474
|
+
metadata: this.webhookConfig?.metadata,
|
|
6475
|
+
fileSyncEvent: event
|
|
6476
|
+
};
|
|
6477
|
+
await this.sendWebhook(payload);
|
|
6478
|
+
}
|
|
6479
|
+
/**
|
|
6480
|
+
* Send a file change event webhook (from watcher)
|
|
6481
|
+
*/
|
|
6482
|
+
async sendFileChangeWebhook(event) {
|
|
6483
|
+
const payload = {
|
|
6484
|
+
event: "file_change",
|
|
6485
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6486
|
+
sessionId: event.sessionId,
|
|
6487
|
+
metadata: this.webhookConfig?.metadata,
|
|
6488
|
+
fileChangeEvent: event
|
|
6489
|
+
};
|
|
6490
|
+
await this.sendWebhook(payload);
|
|
6491
|
+
}
|
|
6492
|
+
/**
|
|
6493
|
+
* Get the next sequence number for a session
|
|
6494
|
+
*/
|
|
6495
|
+
async getNextSequenceNumber(sessionId) {
|
|
6496
|
+
const cached = this.sequenceNumbers.get(sessionId);
|
|
6497
|
+
if (cached !== void 0) {
|
|
6498
|
+
const next = cached + 1;
|
|
6499
|
+
this.sequenceNumbers.set(sessionId, next);
|
|
6500
|
+
return next;
|
|
6501
|
+
}
|
|
6502
|
+
if (this.eventStorage) {
|
|
6503
|
+
try {
|
|
6504
|
+
const next = await this.eventStorage.getNextEventSequence(sessionId);
|
|
6505
|
+
this.sequenceNumbers.set(sessionId, next);
|
|
6506
|
+
return next;
|
|
6507
|
+
} catch (error) {
|
|
6508
|
+
console.warn("[FILE_SYNC] Failed to get next sequence number:", error);
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
this.sequenceNumbers.set(sessionId, 1);
|
|
6512
|
+
return 1;
|
|
6513
|
+
}
|
|
6514
|
+
/**
|
|
6515
|
+
* Compute a unified diff between two text contents
|
|
6516
|
+
*/
|
|
6517
|
+
computeDiff(previousContent, newContent) {
|
|
6518
|
+
if (!previousContent && !newContent) return void 0;
|
|
6519
|
+
if (!previousContent || !newContent) return void 0;
|
|
6520
|
+
try {
|
|
6521
|
+
const MAX_DIFF_SIZE = 100 * 1024;
|
|
6522
|
+
if (previousContent.length > MAX_DIFF_SIZE || newContent.length > MAX_DIFF_SIZE) {
|
|
6523
|
+
return void 0;
|
|
6524
|
+
}
|
|
6525
|
+
const prevText = previousContent.toString("utf-8");
|
|
6526
|
+
const newText = newContent.toString("utf-8");
|
|
6527
|
+
const sampleSize = Math.min(8192, prevText.length, newText.length);
|
|
6528
|
+
if (prevText.slice(0, sampleSize).includes("\0") || newText.slice(0, sampleSize).includes("\0")) {
|
|
6529
|
+
return void 0;
|
|
6530
|
+
}
|
|
6531
|
+
const prevLines = prevText.split("\n");
|
|
6532
|
+
const newLines = newText.split("\n");
|
|
6533
|
+
const diffLines = [];
|
|
6534
|
+
diffLines.push("--- a/file");
|
|
6535
|
+
diffLines.push("+++ b/file");
|
|
6536
|
+
const removed = prevLines.filter((l) => !newLines.includes(l));
|
|
6537
|
+
const added = newLines.filter((l) => !prevLines.includes(l));
|
|
6538
|
+
if (removed.length === 0 && added.length === 0) {
|
|
6539
|
+
return void 0;
|
|
6540
|
+
}
|
|
6541
|
+
diffLines.push("@@ -1 +1 @@");
|
|
6542
|
+
for (const line of removed) {
|
|
6543
|
+
diffLines.push(`-${line}`);
|
|
6544
|
+
}
|
|
6545
|
+
for (const line of added) {
|
|
6546
|
+
diffLines.push(`+${line}`);
|
|
6547
|
+
}
|
|
6548
|
+
return diffLines.join("\n");
|
|
6549
|
+
} catch {
|
|
6550
|
+
return void 0;
|
|
6551
|
+
}
|
|
6552
|
+
}
|
|
6553
|
+
/**
|
|
6554
|
+
* Emit a file event if a callback is registered
|
|
6555
|
+
* Also persists to EventStorage if configured
|
|
6556
|
+
*/
|
|
6557
|
+
async emitFileEvent(sessionId, event) {
|
|
6558
|
+
if (this.onFileEvent) {
|
|
6559
|
+
try {
|
|
6560
|
+
this.onFileEvent(event);
|
|
6561
|
+
} catch (error) {
|
|
6562
|
+
console.error("[FILE_SYNC] Error in file event callback:", error);
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
if (this.webhookConfig) {
|
|
6566
|
+
this.sendFileSyncWebhook(sessionId, event);
|
|
6567
|
+
}
|
|
6568
|
+
if (this.eventStorage) {
|
|
6569
|
+
try {
|
|
6570
|
+
const now = /* @__PURE__ */ new Date();
|
|
6571
|
+
const sequenceNumber = await this.getNextSequenceNumber(sessionId);
|
|
6572
|
+
const diff = this.computeDiff(event.previousContent, event.newContent);
|
|
6573
|
+
const isTextFile = diff !== void 0 || event.newContent && !event.newContent.slice(0, 8192).includes(0);
|
|
6574
|
+
const eventData = {
|
|
6575
|
+
operation: event.operation,
|
|
6576
|
+
direction: event.direction,
|
|
6577
|
+
source: "internal",
|
|
6578
|
+
// File sync operations are internal
|
|
6579
|
+
filePath: event.filePath,
|
|
6580
|
+
fileSize: event.fileSize,
|
|
6581
|
+
success: event.success,
|
|
6582
|
+
error: event.error,
|
|
6583
|
+
diff,
|
|
6584
|
+
isTextFile,
|
|
6585
|
+
previousSize: event.previousContent?.length
|
|
6586
|
+
};
|
|
6587
|
+
const eventType = `file_${event.operation}`;
|
|
6588
|
+
const sessionEvent = {
|
|
6589
|
+
eventType,
|
|
6590
|
+
category: EventCategory.FILE,
|
|
6591
|
+
startedAt: now,
|
|
6592
|
+
endedAt: now,
|
|
6593
|
+
durationMs: 0,
|
|
6594
|
+
eventData,
|
|
6595
|
+
sequenceNumber
|
|
6596
|
+
};
|
|
6597
|
+
await this.eventStorage.saveEvents(sessionId, [sessionEvent]);
|
|
6598
|
+
} catch (error) {
|
|
6599
|
+
console.error("[FILE_SYNC] Error saving file event to storage:", error);
|
|
6600
|
+
}
|
|
6601
|
+
}
|
|
5833
6602
|
}
|
|
5834
6603
|
/**
|
|
5835
6604
|
* Set the sandbox operations implementation
|
|
@@ -5843,8 +6612,8 @@ var init_sandbox_file_sync = __esm({
|
|
|
5843
6612
|
* @param path - The relative file path
|
|
5844
6613
|
* @param targetPath - Optional override for the base path
|
|
5845
6614
|
*/
|
|
5846
|
-
getSandboxPath(
|
|
5847
|
-
const normalizedPath =
|
|
6615
|
+
getSandboxPath(path15, targetPath) {
|
|
6616
|
+
const normalizedPath = path15.replace(/^\/+/, "");
|
|
5848
6617
|
const basePath = targetPath ?? this.sandboxBasePath;
|
|
5849
6618
|
if (basePath === ".") {
|
|
5850
6619
|
return normalizedPath;
|
|
@@ -5857,33 +6626,67 @@ var init_sandbox_file_sync = __esm({
|
|
|
5857
6626
|
* @param path - File path (stored in S3 and used as relative path in sandbox)
|
|
5858
6627
|
* @param content - File content
|
|
5859
6628
|
* @param options - Push options (e.g., targetPath to override sandbox location)
|
|
6629
|
+
* @param previousContent - Optional previous content for diff computation
|
|
5860
6630
|
*/
|
|
5861
|
-
async pushFile(sessionId,
|
|
6631
|
+
async pushFile(sessionId, path15, content, options, previousContent) {
|
|
5862
6632
|
const result = {
|
|
5863
|
-
path:
|
|
6633
|
+
path: path15,
|
|
5864
6634
|
s3Written: false,
|
|
5865
6635
|
sandboxWritten: false
|
|
5866
6636
|
};
|
|
5867
6637
|
try {
|
|
5868
|
-
await this.fileStore.writeFile(sessionId,
|
|
6638
|
+
await this.fileStore.writeFile(sessionId, path15, content);
|
|
5869
6639
|
result.s3Written = true;
|
|
6640
|
+
await this.emitFileEvent(sessionId, {
|
|
6641
|
+
operation: "push",
|
|
6642
|
+
direction: "to_s3",
|
|
6643
|
+
filePath: path15,
|
|
6644
|
+
fileSize: content.length,
|
|
6645
|
+
success: true,
|
|
6646
|
+
previousContent,
|
|
6647
|
+
newContent: content
|
|
6648
|
+
});
|
|
5870
6649
|
} catch (error) {
|
|
5871
6650
|
const errorMessage = extractErrorMessage(error);
|
|
5872
6651
|
result.error = `S3 write failed: ${errorMessage}`;
|
|
5873
|
-
console.error(`[FILE_SYNC] S3 write failed for session ${sessionId}, path ${
|
|
6652
|
+
console.error(`[FILE_SYNC] S3 write failed for session ${sessionId}, path ${path15}:`, error);
|
|
6653
|
+
await this.emitFileEvent(sessionId, {
|
|
6654
|
+
operation: "push",
|
|
6655
|
+
direction: "to_s3",
|
|
6656
|
+
filePath: path15,
|
|
6657
|
+
fileSize: content.length,
|
|
6658
|
+
success: false,
|
|
6659
|
+
error: errorMessage
|
|
6660
|
+
});
|
|
5874
6661
|
return result;
|
|
5875
6662
|
}
|
|
5876
6663
|
if (this.sandboxOps?.isSandboxRunning(sessionId)) {
|
|
5877
6664
|
try {
|
|
5878
|
-
const sandboxPath = this.getSandboxPath(
|
|
6665
|
+
const sandboxPath = this.getSandboxPath(path15, options?.targetPath);
|
|
5879
6666
|
const writeResult = await this.sandboxOps.writeFile(sessionId, sandboxPath, content);
|
|
5880
6667
|
result.sandboxWritten = writeResult.success;
|
|
6668
|
+
await this.emitFileEvent(sessionId, {
|
|
6669
|
+
operation: "push",
|
|
6670
|
+
direction: "to_sandbox",
|
|
6671
|
+
filePath: path15,
|
|
6672
|
+
fileSize: content.length,
|
|
6673
|
+
success: writeResult.success,
|
|
6674
|
+
error: writeResult.error
|
|
6675
|
+
});
|
|
5881
6676
|
if (!writeResult.success && writeResult.error) {
|
|
5882
|
-
console.warn(`[FILE_SYNC] Sandbox write failed for ${
|
|
6677
|
+
console.warn(`[FILE_SYNC] Sandbox write failed for ${path15}: ${writeResult.error}`);
|
|
5883
6678
|
}
|
|
5884
6679
|
} catch (error) {
|
|
5885
6680
|
const errorMessage = extractErrorMessage(error);
|
|
5886
|
-
console.warn(`[FILE_SYNC] Sandbox write error for ${
|
|
6681
|
+
console.warn(`[FILE_SYNC] Sandbox write error for ${path15}: ${errorMessage}`);
|
|
6682
|
+
await this.emitFileEvent(sessionId, {
|
|
6683
|
+
operation: "push",
|
|
6684
|
+
direction: "to_sandbox",
|
|
6685
|
+
filePath: path15,
|
|
6686
|
+
fileSize: content.length,
|
|
6687
|
+
success: false,
|
|
6688
|
+
error: errorMessage
|
|
6689
|
+
});
|
|
5887
6690
|
}
|
|
5888
6691
|
} else {
|
|
5889
6692
|
console.debug(`[FILE_SYNC] Sandbox not running for session ${sessionId}, skipping sandbox write`);
|
|
@@ -5908,9 +6711,9 @@ var init_sandbox_file_sync = __esm({
|
|
|
5908
6711
|
* Pull a file from sandbox to S3
|
|
5909
6712
|
* Reads from sandbox and writes to S3
|
|
5910
6713
|
*/
|
|
5911
|
-
async pullFile(sessionId,
|
|
6714
|
+
async pullFile(sessionId, path15) {
|
|
5912
6715
|
const result = {
|
|
5913
|
-
path:
|
|
6716
|
+
path: path15,
|
|
5914
6717
|
content: null,
|
|
5915
6718
|
s3Written: false
|
|
5916
6719
|
};
|
|
@@ -5919,35 +6722,73 @@ var init_sandbox_file_sync = __esm({
|
|
|
5919
6722
|
return result;
|
|
5920
6723
|
}
|
|
5921
6724
|
try {
|
|
5922
|
-
const sandboxPath = this.getSandboxPath(
|
|
6725
|
+
const sandboxPath = this.getSandboxPath(path15);
|
|
5923
6726
|
const readResult = await this.sandboxOps.readFile(sessionId, sandboxPath);
|
|
5924
6727
|
if (!readResult.success || !readResult.content) {
|
|
5925
6728
|
result.error = readResult.error ?? "File not found in sandbox";
|
|
6729
|
+
await this.emitFileEvent(sessionId, {
|
|
6730
|
+
operation: "pull",
|
|
6731
|
+
direction: "from_sandbox",
|
|
6732
|
+
filePath: path15,
|
|
6733
|
+
success: false,
|
|
6734
|
+
error: result.error
|
|
6735
|
+
});
|
|
5926
6736
|
return result;
|
|
5927
6737
|
}
|
|
5928
6738
|
result.content = readResult.content;
|
|
6739
|
+
await this.emitFileEvent(sessionId, {
|
|
6740
|
+
operation: "pull",
|
|
6741
|
+
direction: "from_sandbox",
|
|
6742
|
+
filePath: path15,
|
|
6743
|
+
fileSize: readResult.content.length,
|
|
6744
|
+
success: true,
|
|
6745
|
+
newContent: readResult.content
|
|
6746
|
+
});
|
|
5929
6747
|
} catch (error) {
|
|
5930
6748
|
const errorMessage = extractErrorMessage(error);
|
|
5931
6749
|
result.error = `Sandbox read failed: ${errorMessage}`;
|
|
6750
|
+
await this.emitFileEvent(sessionId, {
|
|
6751
|
+
operation: "pull",
|
|
6752
|
+
direction: "from_sandbox",
|
|
6753
|
+
filePath: path15,
|
|
6754
|
+
success: false,
|
|
6755
|
+
error: errorMessage
|
|
6756
|
+
});
|
|
5932
6757
|
return result;
|
|
5933
6758
|
}
|
|
5934
6759
|
try {
|
|
5935
|
-
await this.fileStore.writeFile(sessionId,
|
|
6760
|
+
await this.fileStore.writeFile(sessionId, path15, result.content);
|
|
5936
6761
|
result.s3Written = true;
|
|
6762
|
+
await this.emitFileEvent(sessionId, {
|
|
6763
|
+
operation: "pull",
|
|
6764
|
+
direction: "to_s3",
|
|
6765
|
+
filePath: path15,
|
|
6766
|
+
fileSize: result.content.length,
|
|
6767
|
+
success: true,
|
|
6768
|
+
newContent: result.content
|
|
6769
|
+
});
|
|
5937
6770
|
} catch (error) {
|
|
5938
6771
|
const errorMessage = extractErrorMessage(error);
|
|
5939
6772
|
result.error = `S3 write failed: ${errorMessage}`;
|
|
5940
|
-
console.error(`[FILE_SYNC] S3 write failed in pullFile for session ${sessionId}, path ${
|
|
6773
|
+
console.error(`[FILE_SYNC] S3 write failed in pullFile for session ${sessionId}, path ${path15}:`, error);
|
|
6774
|
+
await this.emitFileEvent(sessionId, {
|
|
6775
|
+
operation: "pull",
|
|
6776
|
+
direction: "to_s3",
|
|
6777
|
+
filePath: path15,
|
|
6778
|
+
fileSize: result.content?.length,
|
|
6779
|
+
success: false,
|
|
6780
|
+
error: errorMessage
|
|
6781
|
+
});
|
|
5941
6782
|
}
|
|
5942
6783
|
return result;
|
|
5943
6784
|
}
|
|
5944
6785
|
/**
|
|
5945
6786
|
* Read a file (tries sandbox first, falls back to S3)
|
|
5946
6787
|
*/
|
|
5947
|
-
async readFile(sessionId,
|
|
6788
|
+
async readFile(sessionId, path15) {
|
|
5948
6789
|
if (this.sandboxOps?.isSandboxRunning(sessionId)) {
|
|
5949
6790
|
try {
|
|
5950
|
-
const sandboxPath = this.getSandboxPath(
|
|
6791
|
+
const sandboxPath = this.getSandboxPath(path15);
|
|
5951
6792
|
const readResult = await this.sandboxOps.readFile(sessionId, sandboxPath);
|
|
5952
6793
|
if (readResult.success && readResult.content) {
|
|
5953
6794
|
return { content: readResult.content, source: "sandbox" };
|
|
@@ -5956,7 +6797,7 @@ var init_sandbox_file_sync = __esm({
|
|
|
5956
6797
|
}
|
|
5957
6798
|
}
|
|
5958
6799
|
try {
|
|
5959
|
-
const content = await this.fileStore.readFile(sessionId,
|
|
6800
|
+
const content = await this.fileStore.readFile(sessionId, path15);
|
|
5960
6801
|
if (content) {
|
|
5961
6802
|
return { content, source: "s3" };
|
|
5962
6803
|
}
|
|
@@ -5973,21 +6814,48 @@ var init_sandbox_file_sync = __esm({
|
|
|
5973
6814
|
/**
|
|
5974
6815
|
* Delete a file from both S3 and sandbox
|
|
5975
6816
|
*/
|
|
5976
|
-
async deleteFile(sessionId,
|
|
6817
|
+
async deleteFile(sessionId, path15) {
|
|
5977
6818
|
const result = { s3Deleted: false, sandboxDeleted: false };
|
|
5978
6819
|
try {
|
|
5979
|
-
await this.fileStore.deleteFile(sessionId,
|
|
6820
|
+
await this.fileStore.deleteFile(sessionId, path15);
|
|
5980
6821
|
result.s3Deleted = true;
|
|
6822
|
+
await this.emitFileEvent(sessionId, {
|
|
6823
|
+
operation: "delete",
|
|
6824
|
+
direction: "from_s3",
|
|
6825
|
+
filePath: path15,
|
|
6826
|
+
success: true
|
|
6827
|
+
});
|
|
5981
6828
|
} catch (error) {
|
|
5982
6829
|
const errorMessage = extractErrorMessage(error);
|
|
5983
|
-
console.warn(`[FILE_SYNC] S3 delete failed for ${
|
|
6830
|
+
console.warn(`[FILE_SYNC] S3 delete failed for ${path15}: ${errorMessage}`);
|
|
6831
|
+
await this.emitFileEvent(sessionId, {
|
|
6832
|
+
operation: "delete",
|
|
6833
|
+
direction: "from_s3",
|
|
6834
|
+
filePath: path15,
|
|
6835
|
+
success: false,
|
|
6836
|
+
error: errorMessage
|
|
6837
|
+
});
|
|
5984
6838
|
}
|
|
5985
6839
|
if (this.sandboxOps?.isSandboxRunning(sessionId)) {
|
|
5986
6840
|
try {
|
|
5987
|
-
const sandboxPath = this.getSandboxPath(
|
|
6841
|
+
const sandboxPath = this.getSandboxPath(path15);
|
|
5988
6842
|
console.log(`[FILE_SYNC] Would delete ${sandboxPath} from sandbox`);
|
|
5989
6843
|
result.sandboxDeleted = true;
|
|
5990
|
-
|
|
6844
|
+
await this.emitFileEvent(sessionId, {
|
|
6845
|
+
operation: "delete",
|
|
6846
|
+
direction: "from_sandbox",
|
|
6847
|
+
filePath: path15,
|
|
6848
|
+
success: true
|
|
6849
|
+
});
|
|
6850
|
+
} catch (error) {
|
|
6851
|
+
const errorMessage = extractErrorMessage(error);
|
|
6852
|
+
await this.emitFileEvent(sessionId, {
|
|
6853
|
+
operation: "delete",
|
|
6854
|
+
direction: "from_sandbox",
|
|
6855
|
+
filePath: path15,
|
|
6856
|
+
success: false,
|
|
6857
|
+
error: errorMessage
|
|
6858
|
+
});
|
|
5991
6859
|
}
|
|
5992
6860
|
}
|
|
5993
6861
|
return result;
|
|
@@ -6008,14 +6876,36 @@ var init_sandbox_file_sync = __esm({
|
|
|
6008
6876
|
const content = await this.fileStore.readFile(sessionId, file.path);
|
|
6009
6877
|
if (!content) {
|
|
6010
6878
|
result.errors.push({ path: file.path, error: "File not found in S3" });
|
|
6879
|
+
await this.emitFileEvent(sessionId, {
|
|
6880
|
+
operation: "sync_to_sandbox",
|
|
6881
|
+
direction: "from_s3",
|
|
6882
|
+
filePath: file.path,
|
|
6883
|
+
success: false,
|
|
6884
|
+
error: "File not found in S3"
|
|
6885
|
+
});
|
|
6011
6886
|
continue;
|
|
6012
6887
|
}
|
|
6013
6888
|
const sandboxPath = this.getSandboxPath(file.path);
|
|
6014
6889
|
const writeResult = await this.sandboxOps.writeFile(sessionId, sandboxPath, content);
|
|
6015
6890
|
if (writeResult.success) {
|
|
6016
6891
|
result.fileCount++;
|
|
6892
|
+
await this.emitFileEvent(sessionId, {
|
|
6893
|
+
operation: "sync_to_sandbox",
|
|
6894
|
+
direction: "to_sandbox",
|
|
6895
|
+
filePath: file.path,
|
|
6896
|
+
fileSize: content.length,
|
|
6897
|
+
success: true
|
|
6898
|
+
});
|
|
6017
6899
|
} else {
|
|
6018
6900
|
result.errors.push({ path: file.path, error: writeResult.error ?? "Unknown error" });
|
|
6901
|
+
await this.emitFileEvent(sessionId, {
|
|
6902
|
+
operation: "sync_to_sandbox",
|
|
6903
|
+
direction: "to_sandbox",
|
|
6904
|
+
filePath: file.path,
|
|
6905
|
+
fileSize: content.length,
|
|
6906
|
+
success: false,
|
|
6907
|
+
error: writeResult.error
|
|
6908
|
+
});
|
|
6019
6909
|
}
|
|
6020
6910
|
} catch (error) {
|
|
6021
6911
|
const errorMessage = extractErrorMessage(error);
|
|
@@ -6023,6 +6913,13 @@ var init_sandbox_file_sync = __esm({
|
|
|
6023
6913
|
path: file.path,
|
|
6024
6914
|
error: errorMessage
|
|
6025
6915
|
});
|
|
6916
|
+
await this.emitFileEvent(sessionId, {
|
|
6917
|
+
operation: "sync_to_sandbox",
|
|
6918
|
+
direction: "to_sandbox",
|
|
6919
|
+
filePath: file.path,
|
|
6920
|
+
success: false,
|
|
6921
|
+
error: errorMessage
|
|
6922
|
+
});
|
|
6026
6923
|
}
|
|
6027
6924
|
}
|
|
6028
6925
|
console.log(`[FILE_SYNC] Synced ${result.fileCount} files to sandbox for session ${sessionId}`);
|
|
@@ -6055,8 +6952,23 @@ var init_sandbox_file_sync = __esm({
|
|
|
6055
6952
|
const pullResult = await this.pullFile(sessionId, filePath);
|
|
6056
6953
|
if (pullResult.s3Written) {
|
|
6057
6954
|
result.fileCount++;
|
|
6955
|
+
await this.emitFileEvent(sessionId, {
|
|
6956
|
+
operation: "sync_from_sandbox",
|
|
6957
|
+
direction: "to_s3",
|
|
6958
|
+
filePath,
|
|
6959
|
+
fileSize: pullResult.content?.length,
|
|
6960
|
+
success: true,
|
|
6961
|
+
newContent: pullResult.content ?? void 0
|
|
6962
|
+
});
|
|
6058
6963
|
} else if (pullResult.error) {
|
|
6059
6964
|
result.errors.push({ path: filePath, error: pullResult.error });
|
|
6965
|
+
await this.emitFileEvent(sessionId, {
|
|
6966
|
+
operation: "sync_from_sandbox",
|
|
6967
|
+
direction: "to_s3",
|
|
6968
|
+
filePath,
|
|
6969
|
+
success: false,
|
|
6970
|
+
error: pullResult.error
|
|
6971
|
+
});
|
|
6060
6972
|
}
|
|
6061
6973
|
} catch (error) {
|
|
6062
6974
|
const errorMessage = extractErrorMessage(error);
|
|
@@ -6064,6 +6976,13 @@ var init_sandbox_file_sync = __esm({
|
|
|
6064
6976
|
path: filePath,
|
|
6065
6977
|
error: errorMessage
|
|
6066
6978
|
});
|
|
6979
|
+
await this.emitFileEvent(sessionId, {
|
|
6980
|
+
operation: "sync_from_sandbox",
|
|
6981
|
+
direction: "to_s3",
|
|
6982
|
+
filePath,
|
|
6983
|
+
success: false,
|
|
6984
|
+
error: errorMessage
|
|
6985
|
+
});
|
|
6067
6986
|
}
|
|
6068
6987
|
}
|
|
6069
6988
|
console.log(`[FILE_SYNC] Synced ${result.fileCount} files from sandbox to S3 for session ${sessionId}`);
|
|
@@ -6072,16 +6991,526 @@ var init_sandbox_file_sync = __esm({
|
|
|
6072
6991
|
/**
|
|
6073
6992
|
* Get a signed URL for direct file download
|
|
6074
6993
|
*/
|
|
6075
|
-
async getSignedUrl(sessionId,
|
|
6076
|
-
return this.fileStore.getSignedUrl(sessionId,
|
|
6994
|
+
async getSignedUrl(sessionId, path15, expiresIn) {
|
|
6995
|
+
return this.fileStore.getSignedUrl(sessionId, path15, expiresIn);
|
|
6077
6996
|
}
|
|
6078
6997
|
/**
|
|
6079
6998
|
* Get a signed URL for direct file upload
|
|
6080
6999
|
*/
|
|
6081
|
-
async getUploadUrl(sessionId,
|
|
6082
|
-
return this.fileStore.getUploadUrl(sessionId,
|
|
7000
|
+
async getUploadUrl(sessionId, path15, expiresIn) {
|
|
7001
|
+
return this.fileStore.getUploadUrl(sessionId, path15, expiresIn);
|
|
7002
|
+
}
|
|
7003
|
+
// ===========================================================================
|
|
7004
|
+
// File Watching API
|
|
7005
|
+
// ===========================================================================
|
|
7006
|
+
/**
|
|
7007
|
+
* Start watching a sandbox for file changes and auto-sync to S3.
|
|
7008
|
+
*
|
|
7009
|
+
* For remote sandboxes (Vercel, E2B, etc.), uses polling.
|
|
7010
|
+
* For local sandboxes, can optionally use native file watching via chokidar.
|
|
7011
|
+
*
|
|
7012
|
+
* @param sessionId - Session to watch
|
|
7013
|
+
* @param options - Watch options (overrides constructor defaults)
|
|
7014
|
+
*/
|
|
7015
|
+
async startWatching(sessionId, options) {
|
|
7016
|
+
const opts = { ...this.defaultWatchOptions, ...options };
|
|
7017
|
+
await this.stopWatching(sessionId);
|
|
7018
|
+
const handleFileChange = async (event) => {
|
|
7019
|
+
console.log(`[FILE_SYNC] File change detected: ${event.type} ${event.relativePath}`);
|
|
7020
|
+
if (this.webhookConfig) {
|
|
7021
|
+
this.sendFileChangeWebhook(event);
|
|
7022
|
+
}
|
|
7023
|
+
for (const subscriber of this.fileChangeSubscribers) {
|
|
7024
|
+
try {
|
|
7025
|
+
await subscriber(event);
|
|
7026
|
+
} catch (error) {
|
|
7027
|
+
console.error("[FILE_SYNC] Error in file change subscriber:", error);
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
if (event.type === "add" || event.type === "change") {
|
|
7031
|
+
try {
|
|
7032
|
+
const pullResult = await this.pullFile(sessionId, event.relativePath);
|
|
7033
|
+
if (pullResult.s3Written) {
|
|
7034
|
+
console.log(`[FILE_SYNC] Auto-synced ${event.relativePath} to S3`);
|
|
7035
|
+
} else if (pullResult.error) {
|
|
7036
|
+
console.warn(`[FILE_SYNC] Failed to auto-sync ${event.relativePath}: ${pullResult.error}`);
|
|
7037
|
+
}
|
|
7038
|
+
} catch (error) {
|
|
7039
|
+
console.error(`[FILE_SYNC] Error auto-syncing ${event.relativePath}:`, error);
|
|
7040
|
+
}
|
|
7041
|
+
} else if (event.type === "unlink") {
|
|
7042
|
+
try {
|
|
7043
|
+
await this.deleteFile(sessionId, event.relativePath);
|
|
7044
|
+
console.log(`[FILE_SYNC] Auto-deleted ${event.relativePath} from S3`);
|
|
7045
|
+
} catch (error) {
|
|
7046
|
+
console.error(`[FILE_SYNC] Error auto-deleting ${event.relativePath}:`, error);
|
|
7047
|
+
}
|
|
7048
|
+
}
|
|
7049
|
+
};
|
|
7050
|
+
if (opts.useLocalWatcher && opts.localPath) {
|
|
7051
|
+
const watcher = new SandboxFileWatcher({
|
|
7052
|
+
sessionId,
|
|
7053
|
+
watchPath: opts.localPath,
|
|
7054
|
+
debounceMs: opts.debounceMs ?? 300,
|
|
7055
|
+
ignored: opts.ignored ?? ["**/node_modules/**", "**/.git/**"],
|
|
7056
|
+
onFileChange: handleFileChange,
|
|
7057
|
+
onError: (error) => {
|
|
7058
|
+
console.error(`[FILE_SYNC] Local watcher error for session ${sessionId}:`, error);
|
|
7059
|
+
}
|
|
7060
|
+
});
|
|
7061
|
+
await watcher.start();
|
|
7062
|
+
this.localWatchers.set(sessionId, watcher);
|
|
7063
|
+
console.log(`[FILE_SYNC] Started local file watching for session ${sessionId}`);
|
|
7064
|
+
} else {
|
|
7065
|
+
if (!this.sandboxOps) {
|
|
7066
|
+
throw new Error("Sandbox operations not configured. Call setSandboxOperations first.");
|
|
7067
|
+
}
|
|
7068
|
+
const watcher = new RemoteSandboxFileWatcher({
|
|
7069
|
+
sessionId,
|
|
7070
|
+
sandboxOps: this.sandboxOps,
|
|
7071
|
+
basePath: this.sandboxBasePath,
|
|
7072
|
+
pollIntervalMs: opts.pollIntervalMs ?? 2e3,
|
|
7073
|
+
ignored: opts.ignored ?? ["**/node_modules/**", "**/.git/**"],
|
|
7074
|
+
onFileChange: handleFileChange,
|
|
7075
|
+
onError: (error) => {
|
|
7076
|
+
console.error(`[FILE_SYNC] Remote watcher error for session ${sessionId}:`, error);
|
|
7077
|
+
}
|
|
7078
|
+
});
|
|
7079
|
+
await watcher.start();
|
|
7080
|
+
this.remoteWatchers.set(sessionId, watcher);
|
|
7081
|
+
console.log(`[FILE_SYNC] Started remote file watching for session ${sessionId}`);
|
|
7082
|
+
}
|
|
7083
|
+
}
|
|
7084
|
+
/**
|
|
7085
|
+
* Stop watching a session's sandbox
|
|
7086
|
+
*/
|
|
7087
|
+
async stopWatching(sessionId) {
|
|
7088
|
+
const remoteWatcher = this.remoteWatchers.get(sessionId);
|
|
7089
|
+
if (remoteWatcher) {
|
|
7090
|
+
await remoteWatcher.stop();
|
|
7091
|
+
this.remoteWatchers.delete(sessionId);
|
|
7092
|
+
}
|
|
7093
|
+
const localWatcher = this.localWatchers.get(sessionId);
|
|
7094
|
+
if (localWatcher) {
|
|
7095
|
+
await localWatcher.stop();
|
|
7096
|
+
this.localWatchers.delete(sessionId);
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
/**
|
|
7100
|
+
* Check if a session is being watched
|
|
7101
|
+
*/
|
|
7102
|
+
isWatching(sessionId) {
|
|
7103
|
+
return this.remoteWatchers.has(sessionId) || this.localWatchers.has(sessionId);
|
|
7104
|
+
}
|
|
7105
|
+
/**
|
|
7106
|
+
* Subscribe to file change events across all watched sessions.
|
|
7107
|
+
* This allows external applications to react to file changes.
|
|
7108
|
+
*
|
|
7109
|
+
* @param callback - Function to call when a file changes
|
|
7110
|
+
* @returns Unsubscribe function
|
|
7111
|
+
*
|
|
7112
|
+
* @example
|
|
7113
|
+
* ```typescript
|
|
7114
|
+
* const unsubscribe = fileSync.onFileChange((event) => {
|
|
7115
|
+
* console.log(`File ${event.relativePath} was ${event.type}d`);
|
|
7116
|
+
* // Update your application state here
|
|
7117
|
+
* });
|
|
7118
|
+
*
|
|
7119
|
+
* // Later, when done
|
|
7120
|
+
* unsubscribe();
|
|
7121
|
+
* ```
|
|
7122
|
+
*/
|
|
7123
|
+
onFileChange(callback) {
|
|
7124
|
+
this.fileChangeSubscribers.add(callback);
|
|
7125
|
+
return () => this.fileChangeSubscribers.delete(callback);
|
|
7126
|
+
}
|
|
7127
|
+
/**
|
|
7128
|
+
* Remove a file change subscriber
|
|
7129
|
+
*/
|
|
7130
|
+
offFileChange(callback) {
|
|
7131
|
+
this.fileChangeSubscribers.delete(callback);
|
|
7132
|
+
}
|
|
7133
|
+
/**
|
|
7134
|
+
* Stop all watchers and cleanup
|
|
7135
|
+
*/
|
|
7136
|
+
async stopAllWatching() {
|
|
7137
|
+
const remotePromises = Array.from(this.remoteWatchers.values()).map((w) => w.stop());
|
|
7138
|
+
const localPromises = Array.from(this.localWatchers.values()).map((w) => w.stop());
|
|
7139
|
+
await Promise.all([...remotePromises, ...localPromises]);
|
|
7140
|
+
this.remoteWatchers.clear();
|
|
7141
|
+
this.localWatchers.clear();
|
|
7142
|
+
}
|
|
7143
|
+
/**
|
|
7144
|
+
* Get watching status for all sessions
|
|
7145
|
+
*/
|
|
7146
|
+
getWatchingStatus() {
|
|
7147
|
+
const statuses = [];
|
|
7148
|
+
for (const [sessionId, watcher] of this.remoteWatchers) {
|
|
7149
|
+
statuses.push({ sessionId, type: "remote", isActive: watcher.isActive() });
|
|
7150
|
+
}
|
|
7151
|
+
for (const [sessionId, watcher] of this.localWatchers) {
|
|
7152
|
+
statuses.push({ sessionId, type: "local", isActive: watcher.isActive() });
|
|
7153
|
+
}
|
|
7154
|
+
return statuses;
|
|
7155
|
+
}
|
|
7156
|
+
};
|
|
7157
|
+
}
|
|
7158
|
+
});
|
|
7159
|
+
|
|
7160
|
+
// src/runtime/in-sandbox-watcher.ts
|
|
7161
|
+
function createInSandboxWatcher(options) {
|
|
7162
|
+
return new InSandboxWatcher(options);
|
|
7163
|
+
}
|
|
7164
|
+
function getInSandboxWatcherManager() {
|
|
7165
|
+
if (!globalInSandboxManager) {
|
|
7166
|
+
globalInSandboxManager = new InSandboxWatcherManager();
|
|
7167
|
+
}
|
|
7168
|
+
return globalInSandboxManager;
|
|
7169
|
+
}
|
|
7170
|
+
function createInSandboxWatcherManager() {
|
|
7171
|
+
return new InSandboxWatcherManager();
|
|
7172
|
+
}
|
|
7173
|
+
var WATCHER_SCRIPT, InSandboxWatcher, InSandboxWatcherManager, globalInSandboxManager;
|
|
7174
|
+
var init_in_sandbox_watcher = __esm({
|
|
7175
|
+
"src/runtime/in-sandbox-watcher.ts"() {
|
|
7176
|
+
init_vercel_sandbox_executor();
|
|
7177
|
+
WATCHER_SCRIPT = `
|
|
7178
|
+
const fs = require('fs');
|
|
7179
|
+
const path = require('path');
|
|
7180
|
+
|
|
7181
|
+
const watchPath = process.argv[2] || '.';
|
|
7182
|
+
const ignored = new Set(['node_modules', '.git', '.cache', '__pycache__']);
|
|
7183
|
+
|
|
7184
|
+
// Track all watchers for cleanup
|
|
7185
|
+
const watchers = new Map();
|
|
7186
|
+
|
|
7187
|
+
// Debounce map to coalesce rapid changes
|
|
7188
|
+
const pending = new Map();
|
|
7189
|
+
const DEBOUNCE_MS = 100;
|
|
7190
|
+
|
|
7191
|
+
function emit(type, filePath) {
|
|
7192
|
+
const event = {
|
|
7193
|
+
type,
|
|
7194
|
+
path: filePath,
|
|
7195
|
+
timestamp: Date.now()
|
|
7196
|
+
};
|
|
7197
|
+
console.log(JSON.stringify(event));
|
|
7198
|
+
}
|
|
7199
|
+
|
|
7200
|
+
function shouldIgnore(name) {
|
|
7201
|
+
return ignored.has(name) || name.startsWith('.');
|
|
7202
|
+
}
|
|
7203
|
+
|
|
7204
|
+
function watchDir(dir) {
|
|
7205
|
+
try {
|
|
7206
|
+
const watcher = fs.watch(dir, { persistent: true }, (eventType, filename) => {
|
|
7207
|
+
if (!filename || shouldIgnore(filename)) return;
|
|
7208
|
+
|
|
7209
|
+
const fullPath = path.join(dir, filename);
|
|
7210
|
+
const key = fullPath;
|
|
7211
|
+
|
|
7212
|
+
// Debounce
|
|
7213
|
+
if (pending.has(key)) {
|
|
7214
|
+
clearTimeout(pending.get(key));
|
|
7215
|
+
}
|
|
7216
|
+
|
|
7217
|
+
pending.set(key, setTimeout(() => {
|
|
7218
|
+
pending.delete(key);
|
|
7219
|
+
|
|
7220
|
+
fs.stat(fullPath, (err, stats) => {
|
|
7221
|
+
if (err) {
|
|
7222
|
+
if (err.code === 'ENOENT') {
|
|
7223
|
+
emit('unlink', fullPath);
|
|
7224
|
+
// Stop watching if it was a directory
|
|
7225
|
+
if (watchers.has(fullPath)) {
|
|
7226
|
+
watchers.get(fullPath).close();
|
|
7227
|
+
watchers.delete(fullPath);
|
|
7228
|
+
}
|
|
7229
|
+
}
|
|
7230
|
+
} else {
|
|
7231
|
+
const type = eventType === 'rename' ? 'add' : 'change';
|
|
7232
|
+
emit(type, fullPath);
|
|
7233
|
+
|
|
7234
|
+
// If it's a new directory, start watching it
|
|
7235
|
+
if (stats.isDirectory() && !watchers.has(fullPath)) {
|
|
7236
|
+
watchDir(fullPath);
|
|
7237
|
+
}
|
|
7238
|
+
}
|
|
7239
|
+
});
|
|
7240
|
+
}, DEBOUNCE_MS));
|
|
7241
|
+
});
|
|
7242
|
+
|
|
7243
|
+
watchers.set(dir, watcher);
|
|
7244
|
+
|
|
7245
|
+
// Watch subdirectories
|
|
7246
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
7247
|
+
for (const entry of entries) {
|
|
7248
|
+
if (entry.isDirectory() && !shouldIgnore(entry.name)) {
|
|
7249
|
+
watchDir(path.join(dir, entry.name));
|
|
7250
|
+
}
|
|
7251
|
+
}
|
|
7252
|
+
} catch (err) {
|
|
7253
|
+
// Directory may not exist or be inaccessible
|
|
7254
|
+
console.error(JSON.stringify({ error: err.message, dir }));
|
|
7255
|
+
}
|
|
7256
|
+
}
|
|
7257
|
+
|
|
7258
|
+
// Start watching
|
|
7259
|
+
watchDir(watchPath);
|
|
7260
|
+
|
|
7261
|
+
// Keep alive
|
|
7262
|
+
process.on('SIGTERM', () => {
|
|
7263
|
+
for (const watcher of watchers.values()) {
|
|
7264
|
+
watcher.close();
|
|
7265
|
+
}
|
|
7266
|
+
process.exit(0);
|
|
7267
|
+
});
|
|
7268
|
+
|
|
7269
|
+
// Heartbeat to indicate we're running
|
|
7270
|
+
setInterval(() => {
|
|
7271
|
+
console.log(JSON.stringify({ heartbeat: true, timestamp: Date.now() }));
|
|
7272
|
+
}, 5000);
|
|
7273
|
+
|
|
7274
|
+
console.log(JSON.stringify({ started: true, path: watchPath }));
|
|
7275
|
+
`;
|
|
7276
|
+
InSandboxWatcher = class {
|
|
7277
|
+
sessionId;
|
|
7278
|
+
watchPath;
|
|
7279
|
+
outputPollIntervalMs;
|
|
7280
|
+
sandboxState = null;
|
|
7281
|
+
outputPollTimer = null;
|
|
7282
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
7283
|
+
isRunning = false;
|
|
7284
|
+
startedAt;
|
|
7285
|
+
lastHeartbeat;
|
|
7286
|
+
lastOutputPosition = 0;
|
|
7287
|
+
onError;
|
|
7288
|
+
onReady;
|
|
7289
|
+
constructor(options) {
|
|
7290
|
+
this.sessionId = options.sessionId;
|
|
7291
|
+
this.watchPath = options.watchPath;
|
|
7292
|
+
this.outputPollIntervalMs = options.outputPollIntervalMs ?? 1e3;
|
|
7293
|
+
this.onError = options.onError;
|
|
7294
|
+
this.onReady = options.onReady;
|
|
7295
|
+
if (options.onFileChange) {
|
|
7296
|
+
this.subscribers.add(options.onFileChange);
|
|
7297
|
+
}
|
|
7298
|
+
}
|
|
7299
|
+
/**
|
|
7300
|
+
* Start the watcher process inside the sandbox
|
|
7301
|
+
*/
|
|
7302
|
+
async start() {
|
|
7303
|
+
if (this.isRunning) {
|
|
7304
|
+
console.warn(`[IN_SANDBOX_WATCHER] Already running for session ${this.sessionId}`);
|
|
7305
|
+
return;
|
|
7306
|
+
}
|
|
7307
|
+
try {
|
|
7308
|
+
this.sandboxState = await getOrCreateSandbox({
|
|
7309
|
+
sessionId: this.sessionId,
|
|
7310
|
+
runtime: "node22",
|
|
7311
|
+
timeout: 600
|
|
7312
|
+
});
|
|
7313
|
+
const { sandbox } = this.sandboxState;
|
|
7314
|
+
const scriptPath = "/tmp/.file-watcher.js";
|
|
7315
|
+
const outputPath = "/tmp/.file-watcher-output.log";
|
|
7316
|
+
const writeScriptCmd = `cat > ${scriptPath} << 'WATCHER_EOF'
|
|
7317
|
+
${WATCHER_SCRIPT}
|
|
7318
|
+
WATCHER_EOF`;
|
|
7319
|
+
await sandbox.runCommand({
|
|
7320
|
+
cmd: "sh",
|
|
7321
|
+
args: ["-c", writeScriptCmd]
|
|
7322
|
+
});
|
|
7323
|
+
const startCmd = `nohup node ${scriptPath} "${this.watchPath}" > ${outputPath} 2>&1 &`;
|
|
7324
|
+
await sandbox.runCommand({
|
|
7325
|
+
cmd: "sh",
|
|
7326
|
+
args: ["-c", startCmd]
|
|
7327
|
+
});
|
|
7328
|
+
console.log(`[IN_SANDBOX_WATCHER] Started watcher in sandbox for ${this.watchPath}`);
|
|
7329
|
+
this.isRunning = true;
|
|
7330
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
7331
|
+
this.outputPollTimer = setInterval(async () => {
|
|
7332
|
+
try {
|
|
7333
|
+
await this.pollOutput(outputPath);
|
|
7334
|
+
} catch (error) {
|
|
7335
|
+
console.error("[IN_SANDBOX_WATCHER] Error polling output:", error);
|
|
7336
|
+
if (this.onError && error instanceof Error) {
|
|
7337
|
+
this.onError(error);
|
|
7338
|
+
}
|
|
7339
|
+
}
|
|
7340
|
+
}, this.outputPollIntervalMs);
|
|
7341
|
+
setTimeout(() => {
|
|
7342
|
+
this.pollOutput(outputPath).catch(console.error);
|
|
7343
|
+
}, 500);
|
|
7344
|
+
} catch (error) {
|
|
7345
|
+
console.error(`[IN_SANDBOX_WATCHER] Failed to start watcher:`, error);
|
|
7346
|
+
throw error;
|
|
7347
|
+
}
|
|
7348
|
+
}
|
|
7349
|
+
/**
|
|
7350
|
+
* Poll the output file for new events
|
|
7351
|
+
*/
|
|
7352
|
+
async pollOutput(outputPath) {
|
|
7353
|
+
if (!this.sandboxState?.sandbox) return;
|
|
7354
|
+
try {
|
|
7355
|
+
const result = await this.sandboxState.sandbox.runCommand({
|
|
7356
|
+
cmd: "sh",
|
|
7357
|
+
args: ["-c", `tail -c +${this.lastOutputPosition + 1} ${outputPath} 2>/dev/null || true`]
|
|
7358
|
+
});
|
|
7359
|
+
const output = await result.stdout();
|
|
7360
|
+
if (output && output.trim()) {
|
|
7361
|
+
const sizeResult = await this.sandboxState.sandbox.runCommand({
|
|
7362
|
+
cmd: "sh",
|
|
7363
|
+
args: ["-c", `stat -c%s ${outputPath} 2>/dev/null || echo 0`]
|
|
7364
|
+
});
|
|
7365
|
+
const sizeStr = await sizeResult.stdout();
|
|
7366
|
+
this.lastOutputPosition = parseInt(sizeStr.trim(), 10) || 0;
|
|
7367
|
+
this.processOutput(output);
|
|
7368
|
+
}
|
|
7369
|
+
} catch {
|
|
7370
|
+
}
|
|
7371
|
+
}
|
|
7372
|
+
/**
|
|
7373
|
+
* Process output from the watcher script
|
|
7374
|
+
*/
|
|
7375
|
+
processOutput(stdout) {
|
|
7376
|
+
const lines = stdout.split("\n").filter(Boolean);
|
|
7377
|
+
for (const line of lines) {
|
|
7378
|
+
try {
|
|
7379
|
+
const data = JSON.parse(line);
|
|
7380
|
+
if (data.started) {
|
|
7381
|
+
console.log(`[IN_SANDBOX_WATCHER] Watcher started for ${data.path}`);
|
|
7382
|
+
if (this.onReady) {
|
|
7383
|
+
this.onReady();
|
|
7384
|
+
}
|
|
7385
|
+
} else if (data.heartbeat) {
|
|
7386
|
+
this.lastHeartbeat = new Date(data.timestamp);
|
|
7387
|
+
} else if (data.error) {
|
|
7388
|
+
console.warn(`[IN_SANDBOX_WATCHER] Error in sandbox:`, data.error);
|
|
7389
|
+
} else if (data.type && data.path) {
|
|
7390
|
+
this.emitEvent({
|
|
7391
|
+
type: data.type,
|
|
7392
|
+
relativePath: data.path.replace(/^\.\//, ""),
|
|
7393
|
+
absolutePath: data.path,
|
|
7394
|
+
sessionId: this.sessionId,
|
|
7395
|
+
timestamp: new Date(data.timestamp)
|
|
7396
|
+
});
|
|
7397
|
+
}
|
|
7398
|
+
} catch {
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7401
|
+
}
|
|
7402
|
+
/**
|
|
7403
|
+
* Stop the watcher process
|
|
7404
|
+
*/
|
|
7405
|
+
async stop() {
|
|
7406
|
+
if (!this.isRunning) {
|
|
7407
|
+
return;
|
|
7408
|
+
}
|
|
7409
|
+
if (this.outputPollTimer) {
|
|
7410
|
+
clearInterval(this.outputPollTimer);
|
|
7411
|
+
this.outputPollTimer = null;
|
|
7412
|
+
}
|
|
7413
|
+
try {
|
|
7414
|
+
if (this.sandboxState?.sandbox) {
|
|
7415
|
+
await this.sandboxState.sandbox.runCommand({
|
|
7416
|
+
cmd: "sh",
|
|
7417
|
+
args: ["-c", "pkill -f file-watcher.js 2>/dev/null || true"]
|
|
7418
|
+
});
|
|
7419
|
+
await this.sandboxState.sandbox.runCommand({
|
|
7420
|
+
cmd: "sh",
|
|
7421
|
+
args: ["-c", "rm -f /tmp/.file-watcher.js /tmp/.file-watcher-output.log"]
|
|
7422
|
+
});
|
|
7423
|
+
}
|
|
7424
|
+
} catch {
|
|
7425
|
+
}
|
|
7426
|
+
this.isRunning = false;
|
|
7427
|
+
this.sandboxState = null;
|
|
7428
|
+
this.lastOutputPosition = 0;
|
|
7429
|
+
console.log(`[IN_SANDBOX_WATCHER] Stopped watcher for session ${this.sessionId}`);
|
|
7430
|
+
}
|
|
7431
|
+
/**
|
|
7432
|
+
* Subscribe to file change events
|
|
7433
|
+
*/
|
|
7434
|
+
subscribe(callback) {
|
|
7435
|
+
this.subscribers.add(callback);
|
|
7436
|
+
return () => this.subscribers.delete(callback);
|
|
7437
|
+
}
|
|
7438
|
+
/**
|
|
7439
|
+
* Check if watcher is running
|
|
7440
|
+
*/
|
|
7441
|
+
isActive() {
|
|
7442
|
+
return this.isRunning;
|
|
7443
|
+
}
|
|
7444
|
+
/**
|
|
7445
|
+
* Get watcher status
|
|
7446
|
+
*/
|
|
7447
|
+
getStatus() {
|
|
7448
|
+
return {
|
|
7449
|
+
sessionId: this.sessionId,
|
|
7450
|
+
watchPath: this.watchPath,
|
|
7451
|
+
isRunning: this.isRunning,
|
|
7452
|
+
startedAt: this.startedAt,
|
|
7453
|
+
lastHeartbeat: this.lastHeartbeat
|
|
7454
|
+
};
|
|
7455
|
+
}
|
|
7456
|
+
/**
|
|
7457
|
+
* Emit event to all subscribers
|
|
7458
|
+
*/
|
|
7459
|
+
async emitEvent(event) {
|
|
7460
|
+
for (const callback of this.subscribers) {
|
|
7461
|
+
try {
|
|
7462
|
+
await callback(event);
|
|
7463
|
+
} catch (error) {
|
|
7464
|
+
console.error("[IN_SANDBOX_WATCHER] Error in subscriber callback:", error);
|
|
7465
|
+
}
|
|
7466
|
+
}
|
|
7467
|
+
}
|
|
7468
|
+
};
|
|
7469
|
+
InSandboxWatcherManager = class {
|
|
7470
|
+
watchers = /* @__PURE__ */ new Map();
|
|
7471
|
+
/**
|
|
7472
|
+
* Start watching a sandbox
|
|
7473
|
+
*/
|
|
7474
|
+
async startWatching(options) {
|
|
7475
|
+
const { sessionId } = options;
|
|
7476
|
+
await this.stopWatching(sessionId);
|
|
7477
|
+
const watcher = new InSandboxWatcher(options);
|
|
7478
|
+
await watcher.start();
|
|
7479
|
+
this.watchers.set(sessionId, watcher);
|
|
7480
|
+
return watcher;
|
|
7481
|
+
}
|
|
7482
|
+
/**
|
|
7483
|
+
* Stop watching a session
|
|
7484
|
+
*/
|
|
7485
|
+
async stopWatching(sessionId) {
|
|
7486
|
+
const watcher = this.watchers.get(sessionId);
|
|
7487
|
+
if (watcher) {
|
|
7488
|
+
await watcher.stop();
|
|
7489
|
+
this.watchers.delete(sessionId);
|
|
7490
|
+
}
|
|
7491
|
+
}
|
|
7492
|
+
/**
|
|
7493
|
+
* Get watcher for a session
|
|
7494
|
+
*/
|
|
7495
|
+
getWatcher(sessionId) {
|
|
7496
|
+
return this.watchers.get(sessionId);
|
|
7497
|
+
}
|
|
7498
|
+
/**
|
|
7499
|
+
* Check if watching
|
|
7500
|
+
*/
|
|
7501
|
+
isWatching(sessionId) {
|
|
7502
|
+
return this.watchers.get(sessionId)?.isActive() ?? false;
|
|
7503
|
+
}
|
|
7504
|
+
/**
|
|
7505
|
+
* Stop all watchers
|
|
7506
|
+
*/
|
|
7507
|
+
async stopAll() {
|
|
7508
|
+
const promises = Array.from(this.watchers.values()).map((w) => w.stop());
|
|
7509
|
+
await Promise.all(promises);
|
|
7510
|
+
this.watchers.clear();
|
|
6083
7511
|
}
|
|
6084
7512
|
};
|
|
7513
|
+
globalInSandboxManager = null;
|
|
6085
7514
|
}
|
|
6086
7515
|
});
|
|
6087
7516
|
|
|
@@ -6193,8 +7622,8 @@ function checkSecurityConfig(config) {
|
|
|
6193
7622
|
}
|
|
6194
7623
|
return { issues, warnings, recommendations };
|
|
6195
7624
|
}
|
|
6196
|
-
function isSensitivePath(
|
|
6197
|
-
const normalized =
|
|
7625
|
+
function isSensitivePath(path15) {
|
|
7626
|
+
const normalized = path15.replace(/\\/g, "/").toLowerCase();
|
|
6198
7627
|
for (const sensitive of SENSITIVE_PATHS) {
|
|
6199
7628
|
const pattern = sensitive.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/~/g, ".*");
|
|
6200
7629
|
if (new RegExp(pattern).test(normalized)) {
|
|
@@ -6211,6 +7640,8 @@ var init_runtime = __esm({
|
|
|
6211
7640
|
init_vercel_sandbox_executor();
|
|
6212
7641
|
init_sandbox_file_sync();
|
|
6213
7642
|
init_sandbox_pool();
|
|
7643
|
+
init_sandbox_file_watcher();
|
|
7644
|
+
init_in_sandbox_watcher();
|
|
6214
7645
|
RuntimePresets = {
|
|
6215
7646
|
/**
|
|
6216
7647
|
* Development mode - no isolation, for local testing
|
|
@@ -6321,8 +7752,8 @@ var init_runtime = __esm({
|
|
|
6321
7752
|
this.config.pattern = pattern;
|
|
6322
7753
|
return this;
|
|
6323
7754
|
}
|
|
6324
|
-
workingDirectory(
|
|
6325
|
-
this.config.workingDirectory =
|
|
7755
|
+
workingDirectory(path15) {
|
|
7756
|
+
this.config.workingDirectory = path15;
|
|
6326
7757
|
return this;
|
|
6327
7758
|
}
|
|
6328
7759
|
sandbox(config) {
|
|
@@ -6560,7 +7991,7 @@ var init_local_provider = __esm({
|
|
|
6560
7991
|
maxFileSize;
|
|
6561
7992
|
constructor(source, options = {}) {
|
|
6562
7993
|
this.source = source;
|
|
6563
|
-
this.basePath =
|
|
7994
|
+
this.basePath = path4.resolve(source.localPath);
|
|
6564
7995
|
this.maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
6565
7996
|
}
|
|
6566
7997
|
/**
|
|
@@ -6568,8 +7999,8 @@ var init_local_provider = __esm({
|
|
|
6568
7999
|
* Prevents path traversal attacks.
|
|
6569
8000
|
*/
|
|
6570
8001
|
resolvePath(relativePath) {
|
|
6571
|
-
const normalized =
|
|
6572
|
-
const resolved =
|
|
8002
|
+
const normalized = path4.normalize(relativePath);
|
|
8003
|
+
const resolved = path4.resolve(this.basePath, normalized);
|
|
6573
8004
|
if (!resolved.startsWith(this.basePath)) {
|
|
6574
8005
|
throw new Error(`Path traversal detected: ${relativePath}`);
|
|
6575
8006
|
}
|
|
@@ -6581,8 +8012,8 @@ var init_local_provider = __esm({
|
|
|
6581
8012
|
const entries = await promises.readdir(dirPath, { withFileTypes: true });
|
|
6582
8013
|
const fileEntries = await Promise.all(
|
|
6583
8014
|
entries.map(async (entry) => {
|
|
6584
|
-
const entryPath =
|
|
6585
|
-
const fullPath =
|
|
8015
|
+
const entryPath = path4.join(relativePath || ".", entry.name);
|
|
8016
|
+
const fullPath = path4.join(dirPath, entry.name);
|
|
6586
8017
|
let size;
|
|
6587
8018
|
let modifiedAt;
|
|
6588
8019
|
try {
|
|
@@ -6674,7 +8105,7 @@ var init_local_provider = __esm({
|
|
|
6674
8105
|
}
|
|
6675
8106
|
await this.copyDirectory(sourceFullPath, targetPath);
|
|
6676
8107
|
} else {
|
|
6677
|
-
await promises.mkdir(
|
|
8108
|
+
await promises.mkdir(path4.dirname(targetPath), { recursive: true });
|
|
6678
8109
|
await promises.copyFile(sourceFullPath, targetPath);
|
|
6679
8110
|
}
|
|
6680
8111
|
} catch (error) {
|
|
@@ -6689,8 +8120,8 @@ var init_local_provider = __esm({
|
|
|
6689
8120
|
const entries = await promises.readdir(sourceDir, { withFileTypes: true });
|
|
6690
8121
|
await Promise.all(
|
|
6691
8122
|
entries.map(async (entry) => {
|
|
6692
|
-
const sourcePath =
|
|
6693
|
-
const targetPath =
|
|
8123
|
+
const sourcePath = path4.join(sourceDir, entry.name);
|
|
8124
|
+
const targetPath = path4.join(targetDir, entry.name);
|
|
6694
8125
|
if (entry.isDirectory()) {
|
|
6695
8126
|
await this.copyDirectory(sourcePath, targetPath);
|
|
6696
8127
|
} else {
|
|
@@ -6889,7 +8320,7 @@ var init_github_provider = __esm({
|
|
|
6889
8320
|
}
|
|
6890
8321
|
if (type === "file") {
|
|
6891
8322
|
const content = await this.readFile(sourcePath);
|
|
6892
|
-
await promises.mkdir(
|
|
8323
|
+
await promises.mkdir(path4.dirname(targetPath), { recursive: true });
|
|
6893
8324
|
if (content.encoding === "base64") {
|
|
6894
8325
|
await promises.writeFile(targetPath, Buffer.from(content.content, "base64"));
|
|
6895
8326
|
} else {
|
|
@@ -6907,7 +8338,7 @@ var init_github_provider = __esm({
|
|
|
6907
8338
|
entries.map(
|
|
6908
8339
|
(entry) => this.copyTo(
|
|
6909
8340
|
entry.path,
|
|
6910
|
-
|
|
8341
|
+
path4.join(targetPath, entry.name),
|
|
6911
8342
|
recursive
|
|
6912
8343
|
)
|
|
6913
8344
|
)
|
|
@@ -6990,8 +8421,8 @@ var init_manager2 = __esm({
|
|
|
6990
8421
|
if (enabledSkills.length === 0) {
|
|
6991
8422
|
return "";
|
|
6992
8423
|
}
|
|
6993
|
-
const sessionDir =
|
|
6994
|
-
const skillsDir =
|
|
8424
|
+
const sessionDir = path4.join(this.tempDir, sessionId);
|
|
8425
|
+
const skillsDir = path4.join(sessionDir, ".claude", "skills");
|
|
6995
8426
|
await promises.mkdir(skillsDir, { recursive: true });
|
|
6996
8427
|
await Promise.all(
|
|
6997
8428
|
enabledSkills.map((skill) => this.materializeSkill(skill, skillsDir, token))
|
|
@@ -7003,10 +8434,10 @@ var init_manager2 = __esm({
|
|
|
7003
8434
|
*/
|
|
7004
8435
|
async materializeSkill(skill, skillsDir, token) {
|
|
7005
8436
|
const provider = this.createFileProvider(skill.source, token);
|
|
7006
|
-
const skillDir =
|
|
8437
|
+
const skillDir = path4.join(skillsDir, skill.name);
|
|
7007
8438
|
await promises.mkdir(skillDir, { recursive: true });
|
|
7008
8439
|
for (const includePath of skill.includePaths) {
|
|
7009
|
-
const targetPath =
|
|
8440
|
+
const targetPath = path4.join(skillDir, path4.basename(includePath));
|
|
7010
8441
|
try {
|
|
7011
8442
|
await provider.copyTo(includePath, targetPath, true);
|
|
7012
8443
|
} catch (error) {
|
|
@@ -7021,7 +8452,7 @@ var init_manager2 = __esm({
|
|
|
7021
8452
|
* Clean up materialized skills for a session
|
|
7022
8453
|
*/
|
|
7023
8454
|
async cleanupSession(sessionId) {
|
|
7024
|
-
const sessionDir =
|
|
8455
|
+
const sessionDir = path4.join(this.tempDir, sessionId);
|
|
7025
8456
|
try {
|
|
7026
8457
|
await promises.rm(sessionDir, { recursive: true, force: true });
|
|
7027
8458
|
} catch (error) {
|
|
@@ -7039,7 +8470,7 @@ var init_manager2 = __esm({
|
|
|
7039
8470
|
await Promise.all(
|
|
7040
8471
|
entries.map(async (entry) => {
|
|
7041
8472
|
if (!entry.isDirectory()) return;
|
|
7042
|
-
const dirPath =
|
|
8473
|
+
const dirPath = path4.join(this.tempDir, entry.name);
|
|
7043
8474
|
try {
|
|
7044
8475
|
const stats = await promises.stat(dirPath);
|
|
7045
8476
|
if (now - stats.mtimeMs > maxAgeMs) {
|
|
@@ -9848,11 +11279,11 @@ var init_dist = __esm({
|
|
|
9848
11279
|
return isZodType(schema, "ZodEffects") ? this.cleanParameter(schema._def.schema) : schema;
|
|
9849
11280
|
}
|
|
9850
11281
|
generatePath(route) {
|
|
9851
|
-
const { method, path:
|
|
11282
|
+
const { method, path: path15, request, responses } = route, pathItemConfig = __rest(route, ["method", "path", "request", "responses"]);
|
|
9852
11283
|
const generatedResponses = mapValues(responses, (response) => {
|
|
9853
11284
|
return this.getResponse(response);
|
|
9854
11285
|
});
|
|
9855
|
-
const parameters = enhanceMissingParametersError(() => this.getParameters(request), { route: `${method} ${
|
|
11286
|
+
const parameters = enhanceMissingParametersError(() => this.getParameters(request), { route: `${method} ${path15}` });
|
|
9856
11287
|
const requestBody = this.getRequestBody(request === null || request === void 0 ? void 0 : request.body);
|
|
9857
11288
|
const routeDoc = {
|
|
9858
11289
|
[method]: Object.assign(Object.assign(Object.assign(Object.assign({}, pathItemConfig), parameters.length > 0 ? {
|
|
@@ -10047,8 +11478,8 @@ var init_dist2 = __esm({
|
|
|
10047
11478
|
});
|
|
10048
11479
|
function addBasePathToDocument(document, basePath) {
|
|
10049
11480
|
const updatedPaths = {};
|
|
10050
|
-
Object.keys(document.paths).forEach((
|
|
10051
|
-
updatedPaths[mergePath(basePath.replaceAll(/:([^\/]+)/g, "{$1}"),
|
|
11481
|
+
Object.keys(document.paths).forEach((path15) => {
|
|
11482
|
+
updatedPaths[mergePath(basePath.replaceAll(/:([^\/]+)/g, "{$1}"), path15)] = document.paths[path15];
|
|
10052
11483
|
});
|
|
10053
11484
|
return {
|
|
10054
11485
|
...document,
|
|
@@ -10192,8 +11623,8 @@ var init_dist3 = __esm({
|
|
|
10192
11623
|
const document = generator.generateDocument(config);
|
|
10193
11624
|
return this._basePath ? addBasePathToDocument(document, this._basePath) : document;
|
|
10194
11625
|
};
|
|
10195
|
-
doc = (
|
|
10196
|
-
return this.get(
|
|
11626
|
+
doc = (path15, configure) => {
|
|
11627
|
+
return this.get(path15, (c) => {
|
|
10197
11628
|
const config = typeof configure === "function" ? configure(c) : configure;
|
|
10198
11629
|
try {
|
|
10199
11630
|
const document = this.getOpenAPIDocument(config);
|
|
@@ -10203,8 +11634,8 @@ var init_dist3 = __esm({
|
|
|
10203
11634
|
}
|
|
10204
11635
|
});
|
|
10205
11636
|
};
|
|
10206
|
-
doc31 = (
|
|
10207
|
-
return this.get(
|
|
11637
|
+
doc31 = (path15, configure) => {
|
|
11638
|
+
return this.get(path15, (c) => {
|
|
10208
11639
|
const config = typeof configure === "function" ? configure(c) : configure;
|
|
10209
11640
|
try {
|
|
10210
11641
|
const document = this.getOpenAPI31Document(config);
|
|
@@ -10214,9 +11645,9 @@ var init_dist3 = __esm({
|
|
|
10214
11645
|
}
|
|
10215
11646
|
});
|
|
10216
11647
|
};
|
|
10217
|
-
route(
|
|
10218
|
-
const pathForOpenAPI =
|
|
10219
|
-
super.route(
|
|
11648
|
+
route(path15, app) {
|
|
11649
|
+
const pathForOpenAPI = path15.replaceAll(/:([^\/]+)/g, "{$1}");
|
|
11650
|
+
super.route(path15, app);
|
|
10220
11651
|
if (!(app instanceof _OpenAPIHono)) {
|
|
10221
11652
|
return this;
|
|
10222
11653
|
}
|
|
@@ -10263,8 +11694,8 @@ var init_dist3 = __esm({
|
|
|
10263
11694
|
});
|
|
10264
11695
|
return this;
|
|
10265
11696
|
}
|
|
10266
|
-
basePath(
|
|
10267
|
-
return new _OpenAPIHono({ ...super.basePath(
|
|
11697
|
+
basePath(path15) {
|
|
11698
|
+
return new _OpenAPIHono({ ...super.basePath(path15), defaultHook: this.defaultHook });
|
|
10268
11699
|
}
|
|
10269
11700
|
};
|
|
10270
11701
|
createRoute = (routeConfig) => {
|
|
@@ -12045,12 +13476,12 @@ function requestLogger(options = {}) {
|
|
|
12045
13476
|
const requestId = generateId();
|
|
12046
13477
|
const start = Date.now();
|
|
12047
13478
|
const method = c.req.method;
|
|
12048
|
-
const
|
|
13479
|
+
const path15 = c.req.path;
|
|
12049
13480
|
c.set("requestId", requestId);
|
|
12050
13481
|
logger3.debug("Request started", {
|
|
12051
13482
|
requestId,
|
|
12052
13483
|
method,
|
|
12053
|
-
path:
|
|
13484
|
+
path: path15,
|
|
12054
13485
|
userAgent: c.req.header("user-agent")
|
|
12055
13486
|
});
|
|
12056
13487
|
await next();
|
|
@@ -12060,7 +13491,7 @@ function requestLogger(options = {}) {
|
|
|
12060
13491
|
logFn("Request completed", {
|
|
12061
13492
|
requestId,
|
|
12062
13493
|
method,
|
|
12063
|
-
path:
|
|
13494
|
+
path: path15,
|
|
12064
13495
|
status,
|
|
12065
13496
|
duration: `${duration}ms`
|
|
12066
13497
|
});
|
|
@@ -12545,8 +13976,8 @@ function shellQuote3(str) {
|
|
|
12545
13976
|
async function loadWorkspaceState(workspaceId, sandbox, bundleStore, repoRoot) {
|
|
12546
13977
|
const resolvedRoot = repoRoot ?? sandbox.defaultRepoRoot;
|
|
12547
13978
|
const gitRepo = new SandboxGitRepo(sandbox, resolvedRoot);
|
|
12548
|
-
const tmpDir =
|
|
12549
|
-
const localBundle =
|
|
13979
|
+
const tmpDir = fs10.mkdtempSync(path4.join(os.tmpdir(), "ash-workspace-"));
|
|
13980
|
+
const localBundle = path4.join(tmpDir, `${workspaceId}.bundle`);
|
|
12550
13981
|
try {
|
|
12551
13982
|
const bundleExists = await Promise.resolve(
|
|
12552
13983
|
bundleStore.downloadBundle(workspaceId, localBundle)
|
|
@@ -12582,7 +14013,7 @@ async function loadWorkspaceState(workspaceId, sandbox, bundleStore, repoRoot) {
|
|
|
12582
14013
|
}
|
|
12583
14014
|
} finally {
|
|
12584
14015
|
try {
|
|
12585
|
-
|
|
14016
|
+
fs10.rmSync(tmpDir, { recursive: true, force: true });
|
|
12586
14017
|
} catch {
|
|
12587
14018
|
}
|
|
12588
14019
|
}
|
|
@@ -12602,11 +14033,11 @@ async function saveWorkspaceState(workspaceId, sandbox, gitRepo, bundleStore, co
|
|
|
12602
14033
|
}
|
|
12603
14034
|
const remoteBundlePath = BUNDLE_PATH_TEMPLATE.replace("{workspaceId}", workspaceId);
|
|
12604
14035
|
await gitRepo.createBundle(remoteBundlePath);
|
|
12605
|
-
const tmpDir =
|
|
12606
|
-
const localBundle =
|
|
14036
|
+
const tmpDir = fs10.mkdtempSync(path4.join(os.tmpdir(), "ash-workspace-"));
|
|
14037
|
+
const localBundle = path4.join(tmpDir, `${workspaceId}.bundle`);
|
|
12607
14038
|
try {
|
|
12608
14039
|
await sandbox.downloadFile(remoteBundlePath, localBundle);
|
|
12609
|
-
const bundleSize =
|
|
14040
|
+
const bundleSize = fs10.statSync(localBundle).size;
|
|
12610
14041
|
await bundleStore.uploadBundle(workspaceId, localBundle);
|
|
12611
14042
|
return {
|
|
12612
14043
|
committed,
|
|
@@ -12615,7 +14046,7 @@ async function saveWorkspaceState(workspaceId, sandbox, gitRepo, bundleStore, co
|
|
|
12615
14046
|
};
|
|
12616
14047
|
} finally {
|
|
12617
14048
|
try {
|
|
12618
|
-
|
|
14049
|
+
fs10.rmSync(tmpDir, { recursive: true, force: true });
|
|
12619
14050
|
} catch {
|
|
12620
14051
|
}
|
|
12621
14052
|
}
|
|
@@ -12628,7 +14059,7 @@ var init_persistence = __esm({
|
|
|
12628
14059
|
}
|
|
12629
14060
|
});
|
|
12630
14061
|
async function cloneRepository(url, ref, timeout = 12e4) {
|
|
12631
|
-
const tmpDir =
|
|
14062
|
+
const tmpDir = fs10.mkdtempSync(path4.join(os.tmpdir(), "ash-github-skill-"));
|
|
12632
14063
|
try {
|
|
12633
14064
|
const cloneCmd = ["git", "clone", "--depth", "1"];
|
|
12634
14065
|
if (ref) {
|
|
@@ -12643,7 +14074,7 @@ async function cloneRepository(url, ref, timeout = 12e4) {
|
|
|
12643
14074
|
return tmpDir;
|
|
12644
14075
|
} catch (error) {
|
|
12645
14076
|
try {
|
|
12646
|
-
|
|
14077
|
+
fs10.rmSync(tmpDir, { recursive: true, force: true });
|
|
12647
14078
|
} catch {
|
|
12648
14079
|
}
|
|
12649
14080
|
throw error;
|
|
@@ -12655,10 +14086,10 @@ function getRepoName(url) {
|
|
|
12655
14086
|
}
|
|
12656
14087
|
function countFiles(dir, exclude) {
|
|
12657
14088
|
let count = 0;
|
|
12658
|
-
const entries =
|
|
14089
|
+
const entries = fs10.readdirSync(dir, { withFileTypes: true });
|
|
12659
14090
|
for (const entry of entries) {
|
|
12660
14091
|
if (exclude.has(entry.name)) continue;
|
|
12661
|
-
const fullPath =
|
|
14092
|
+
const fullPath = path4.join(dir, entry.name);
|
|
12662
14093
|
if (entry.isDirectory()) {
|
|
12663
14094
|
count += countFiles(fullPath, exclude);
|
|
12664
14095
|
} else if (entry.isFile()) {
|
|
@@ -12670,15 +14101,15 @@ function countFiles(dir, exclude) {
|
|
|
12670
14101
|
async function uploadDirectory(sandbox, localPath, remotePath, exclude) {
|
|
12671
14102
|
let uploadCount = 0;
|
|
12672
14103
|
await sandbox.runCommand(`mkdir -p '${remotePath}'`);
|
|
12673
|
-
const entries =
|
|
14104
|
+
const entries = fs10.readdirSync(localPath, { withFileTypes: true });
|
|
12674
14105
|
for (const entry of entries) {
|
|
12675
14106
|
if (exclude.has(entry.name)) continue;
|
|
12676
|
-
const srcPath =
|
|
14107
|
+
const srcPath = path4.join(localPath, entry.name);
|
|
12677
14108
|
const destPath = `${remotePath}/${entry.name}`;
|
|
12678
14109
|
if (entry.isDirectory()) {
|
|
12679
14110
|
uploadCount += await uploadDirectory(sandbox, srcPath, destPath, exclude);
|
|
12680
14111
|
} else if (entry.isFile()) {
|
|
12681
|
-
const content =
|
|
14112
|
+
const content = fs10.readFileSync(srcPath, "utf-8");
|
|
12682
14113
|
await sandbox.writeFile(destPath, content);
|
|
12683
14114
|
uploadCount++;
|
|
12684
14115
|
}
|
|
@@ -12704,13 +14135,13 @@ async function loadGitHubSkill(sandbox, config, skillsBasePath) {
|
|
|
12704
14135
|
);
|
|
12705
14136
|
}
|
|
12706
14137
|
try {
|
|
12707
|
-
const sourcePath = subpath ?
|
|
12708
|
-
if (!
|
|
14138
|
+
const sourcePath = subpath ? path4.join(clonePath, subpath) : clonePath;
|
|
14139
|
+
if (!fs10.existsSync(sourcePath)) {
|
|
12709
14140
|
throw new Error(
|
|
12710
14141
|
`Path '${subpath}' not found in repository ${url}`
|
|
12711
14142
|
);
|
|
12712
14143
|
}
|
|
12713
|
-
if (!
|
|
14144
|
+
if (!fs10.statSync(sourcePath).isDirectory()) {
|
|
12714
14145
|
throw new Error(
|
|
12715
14146
|
`Path '${subpath}' in ${url} is not a directory`
|
|
12716
14147
|
);
|
|
@@ -12729,7 +14160,7 @@ async function loadGitHubSkill(sandbox, config, skillsBasePath) {
|
|
|
12729
14160
|
};
|
|
12730
14161
|
} finally {
|
|
12731
14162
|
try {
|
|
12732
|
-
|
|
14163
|
+
fs10.rmSync(clonePath, { recursive: true, force: true });
|
|
12733
14164
|
} catch {
|
|
12734
14165
|
}
|
|
12735
14166
|
}
|
|
@@ -13422,26 +14853,26 @@ var init_workspace = __esm({
|
|
|
13422
14853
|
const localPaths = normalizeToArray(skillsConfig.local);
|
|
13423
14854
|
for (const localPath of localPaths) {
|
|
13424
14855
|
try {
|
|
13425
|
-
if (!
|
|
14856
|
+
if (!fs10.existsSync(localPath)) {
|
|
13426
14857
|
errors.push({
|
|
13427
14858
|
source: localPath,
|
|
13428
14859
|
error: new Error(`Path does not exist: ${localPath}`)
|
|
13429
14860
|
});
|
|
13430
14861
|
continue;
|
|
13431
14862
|
}
|
|
13432
|
-
const
|
|
13433
|
-
if (!
|
|
14863
|
+
const stat2 = fs10.statSync(localPath);
|
|
14864
|
+
if (!stat2.isDirectory()) {
|
|
13434
14865
|
errors.push({
|
|
13435
14866
|
source: localPath,
|
|
13436
14867
|
error: new Error(`Path is not a directory: ${localPath}`)
|
|
13437
14868
|
});
|
|
13438
14869
|
continue;
|
|
13439
14870
|
}
|
|
13440
|
-
const entries =
|
|
14871
|
+
const entries = fs10.readdirSync(localPath);
|
|
13441
14872
|
for (const entry of entries) {
|
|
13442
14873
|
if (FILTERED_ITEMS2.has(entry)) continue;
|
|
13443
|
-
const entryPath =
|
|
13444
|
-
const entryStat =
|
|
14874
|
+
const entryPath = path4.join(localPath, entry);
|
|
14875
|
+
const entryStat = fs10.statSync(entryPath);
|
|
13445
14876
|
if (entryStat.isDirectory()) {
|
|
13446
14877
|
const remotePath = `${skillsBase}/${entry}`;
|
|
13447
14878
|
await this.sandbox.uploadDirectory(entryPath, remotePath, {
|
|
@@ -13526,50 +14957,50 @@ var init_bundle_store = __esm({
|
|
|
13526
14957
|
directory;
|
|
13527
14958
|
constructor(options) {
|
|
13528
14959
|
this.directory = options.directory;
|
|
13529
|
-
if (!
|
|
13530
|
-
|
|
14960
|
+
if (!fs10.existsSync(this.directory)) {
|
|
14961
|
+
fs10.mkdirSync(this.directory, { recursive: true });
|
|
13531
14962
|
}
|
|
13532
14963
|
}
|
|
13533
14964
|
keyForWorkspace(workspaceId) {
|
|
13534
|
-
return
|
|
14965
|
+
return path4.join(this.directory, `${workspaceId}.bundle`);
|
|
13535
14966
|
}
|
|
13536
14967
|
downloadBundle(workspaceId, destPath) {
|
|
13537
14968
|
const bundlePath = this.keyForWorkspace(workspaceId);
|
|
13538
|
-
if (!
|
|
14969
|
+
if (!fs10.existsSync(bundlePath)) {
|
|
13539
14970
|
return false;
|
|
13540
14971
|
}
|
|
13541
|
-
const destDir =
|
|
13542
|
-
if (!
|
|
13543
|
-
|
|
14972
|
+
const destDir = path4.dirname(destPath);
|
|
14973
|
+
if (!fs10.existsSync(destDir)) {
|
|
14974
|
+
fs10.mkdirSync(destDir, { recursive: true });
|
|
13544
14975
|
}
|
|
13545
|
-
|
|
14976
|
+
fs10.copyFileSync(bundlePath, destPath);
|
|
13546
14977
|
return true;
|
|
13547
14978
|
}
|
|
13548
14979
|
uploadBundle(workspaceId, srcPath) {
|
|
13549
|
-
if (!
|
|
14980
|
+
if (!fs10.existsSync(srcPath)) {
|
|
13550
14981
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13551
14982
|
}
|
|
13552
14983
|
const bundlePath = this.keyForWorkspace(workspaceId);
|
|
13553
|
-
const bundleDir =
|
|
13554
|
-
if (!
|
|
13555
|
-
|
|
14984
|
+
const bundleDir = path4.dirname(bundlePath);
|
|
14985
|
+
if (!fs10.existsSync(bundleDir)) {
|
|
14986
|
+
fs10.mkdirSync(bundleDir, { recursive: true });
|
|
13556
14987
|
}
|
|
13557
|
-
|
|
14988
|
+
fs10.copyFileSync(srcPath, bundlePath);
|
|
13558
14989
|
}
|
|
13559
14990
|
exists(workspaceId) {
|
|
13560
|
-
return
|
|
14991
|
+
return fs10.existsSync(this.keyForWorkspace(workspaceId));
|
|
13561
14992
|
}
|
|
13562
14993
|
deleteBundle(workspaceId) {
|
|
13563
14994
|
const bundlePath = this.keyForWorkspace(workspaceId);
|
|
13564
|
-
if (
|
|
13565
|
-
|
|
14995
|
+
if (fs10.existsSync(bundlePath)) {
|
|
14996
|
+
fs10.unlinkSync(bundlePath);
|
|
13566
14997
|
}
|
|
13567
14998
|
}
|
|
13568
14999
|
listWorkspaces() {
|
|
13569
|
-
if (!
|
|
15000
|
+
if (!fs10.existsSync(this.directory)) {
|
|
13570
15001
|
return [];
|
|
13571
15002
|
}
|
|
13572
|
-
return
|
|
15003
|
+
return fs10.readdirSync(this.directory).filter((file) => file.endsWith(".bundle")).map((file) => file.replace(".bundle", ""));
|
|
13573
15004
|
}
|
|
13574
15005
|
};
|
|
13575
15006
|
MemoryBundleStore = class {
|
|
@@ -13582,18 +15013,18 @@ var init_bundle_store = __esm({
|
|
|
13582
15013
|
if (!bundle) {
|
|
13583
15014
|
return false;
|
|
13584
15015
|
}
|
|
13585
|
-
const destDir =
|
|
13586
|
-
if (!
|
|
13587
|
-
|
|
15016
|
+
const destDir = path4.dirname(destPath);
|
|
15017
|
+
if (!fs10.existsSync(destDir)) {
|
|
15018
|
+
fs10.mkdirSync(destDir, { recursive: true });
|
|
13588
15019
|
}
|
|
13589
|
-
|
|
15020
|
+
fs10.writeFileSync(destPath, bundle);
|
|
13590
15021
|
return true;
|
|
13591
15022
|
}
|
|
13592
15023
|
uploadBundle(workspaceId, srcPath) {
|
|
13593
|
-
if (!
|
|
15024
|
+
if (!fs10.existsSync(srcPath)) {
|
|
13594
15025
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13595
15026
|
}
|
|
13596
|
-
const content =
|
|
15027
|
+
const content = fs10.readFileSync(srcPath);
|
|
13597
15028
|
this.bundles.set(workspaceId, content);
|
|
13598
15029
|
}
|
|
13599
15030
|
exists(workspaceId) {
|
|
@@ -13684,21 +15115,21 @@ var init_supabase_store = __esm({
|
|
|
13684
15115
|
if (!data) {
|
|
13685
15116
|
return false;
|
|
13686
15117
|
}
|
|
13687
|
-
const dir =
|
|
13688
|
-
if (!
|
|
13689
|
-
|
|
15118
|
+
const dir = path4.dirname(destPath);
|
|
15119
|
+
if (!fs10.existsSync(dir)) {
|
|
15120
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
13690
15121
|
}
|
|
13691
15122
|
const arrayBuffer = await data.arrayBuffer();
|
|
13692
|
-
|
|
15123
|
+
fs10.writeFileSync(destPath, Buffer.from(arrayBuffer));
|
|
13693
15124
|
return true;
|
|
13694
15125
|
}
|
|
13695
15126
|
async uploadBundle(workspaceId, srcPath) {
|
|
13696
|
-
if (!
|
|
15127
|
+
if (!fs10.existsSync(srcPath)) {
|
|
13697
15128
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13698
15129
|
}
|
|
13699
15130
|
const client = await this.ensureClient();
|
|
13700
15131
|
const key = this.keyForWorkspace(workspaceId);
|
|
13701
|
-
const content =
|
|
15132
|
+
const content = fs10.readFileSync(srcPath);
|
|
13702
15133
|
const { error } = await client.storage.from(this.bucket).upload(key, content, {
|
|
13703
15134
|
contentType: "application/octet-stream",
|
|
13704
15135
|
upsert: true
|
|
@@ -13827,11 +15258,11 @@ var init_cloud_store = __esm({
|
|
|
13827
15258
|
return false;
|
|
13828
15259
|
}
|
|
13829
15260
|
const bytes = await body.transformToByteArray();
|
|
13830
|
-
const dir =
|
|
13831
|
-
if (!
|
|
13832
|
-
|
|
15261
|
+
const dir = path4.dirname(destPath);
|
|
15262
|
+
if (!fs10.existsSync(dir)) {
|
|
15263
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
13833
15264
|
}
|
|
13834
|
-
|
|
15265
|
+
fs10.writeFileSync(destPath, Buffer.from(bytes));
|
|
13835
15266
|
return true;
|
|
13836
15267
|
} catch (error) {
|
|
13837
15268
|
const err = error;
|
|
@@ -13842,12 +15273,12 @@ var init_cloud_store = __esm({
|
|
|
13842
15273
|
}
|
|
13843
15274
|
}
|
|
13844
15275
|
async uploadBundle(workspaceId, srcPath) {
|
|
13845
|
-
if (!
|
|
15276
|
+
if (!fs10.existsSync(srcPath)) {
|
|
13846
15277
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13847
15278
|
}
|
|
13848
15279
|
const { client, module } = await this.ensureClient();
|
|
13849
15280
|
const key = this.keyForWorkspace(workspaceId);
|
|
13850
|
-
const content =
|
|
15281
|
+
const content = fs10.readFileSync(srcPath);
|
|
13851
15282
|
await client.send(
|
|
13852
15283
|
new module.PutObjectCommand({
|
|
13853
15284
|
Bucket: this.bucket,
|
|
@@ -13949,9 +15380,9 @@ var init_cloud_store = __esm({
|
|
|
13949
15380
|
if (!exists) {
|
|
13950
15381
|
return false;
|
|
13951
15382
|
}
|
|
13952
|
-
const dir =
|
|
13953
|
-
if (!
|
|
13954
|
-
|
|
15383
|
+
const dir = path4.dirname(destPath);
|
|
15384
|
+
if (!fs10.existsSync(dir)) {
|
|
15385
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
13955
15386
|
}
|
|
13956
15387
|
await file.download({ destination: destPath });
|
|
13957
15388
|
return true;
|
|
@@ -13960,12 +15391,12 @@ var init_cloud_store = __esm({
|
|
|
13960
15391
|
}
|
|
13961
15392
|
}
|
|
13962
15393
|
async uploadBundle(workspaceId, srcPath) {
|
|
13963
|
-
if (!
|
|
15394
|
+
if (!fs10.existsSync(srcPath)) {
|
|
13964
15395
|
throw new Error(`Source bundle not found: ${srcPath}`);
|
|
13965
15396
|
}
|
|
13966
15397
|
const storage = await this.ensureStorage();
|
|
13967
15398
|
const key = this.keyForWorkspace(workspaceId);
|
|
13968
|
-
const content =
|
|
15399
|
+
const content = fs10.readFileSync(srcPath);
|
|
13969
15400
|
const file = storage.bucket(this.bucket).file(key);
|
|
13970
15401
|
await file.save(content);
|
|
13971
15402
|
}
|
|
@@ -14064,25 +15495,25 @@ var init_file_store = __esm({
|
|
|
14064
15495
|
/**
|
|
14065
15496
|
* Generate the S3 key for a file
|
|
14066
15497
|
*/
|
|
14067
|
-
keyForFile(sessionId,
|
|
14068
|
-
const normalizedPath =
|
|
15498
|
+
keyForFile(sessionId, path15) {
|
|
15499
|
+
const normalizedPath = path15.replace(/^\/+/, "");
|
|
14069
15500
|
return `${this.prefix}${sessionId}/${normalizedPath}`;
|
|
14070
15501
|
}
|
|
14071
|
-
async writeFile(sessionId,
|
|
15502
|
+
async writeFile(sessionId, path15, content) {
|
|
14072
15503
|
const { client, module } = await this.ensureClient();
|
|
14073
|
-
const key = this.keyForFile(sessionId,
|
|
15504
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14074
15505
|
await client.send(
|
|
14075
15506
|
new module.PutObjectCommand({
|
|
14076
15507
|
Bucket: this.bucket,
|
|
14077
15508
|
Key: key,
|
|
14078
15509
|
Body: content,
|
|
14079
|
-
ContentType: this.getMimeType(
|
|
15510
|
+
ContentType: this.getMimeType(path15)
|
|
14080
15511
|
})
|
|
14081
15512
|
);
|
|
14082
15513
|
}
|
|
14083
|
-
async readFile(sessionId,
|
|
15514
|
+
async readFile(sessionId, path15) {
|
|
14084
15515
|
const { client, module } = await this.ensureClient();
|
|
14085
|
-
const key = this.keyForFile(sessionId,
|
|
15516
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14086
15517
|
try {
|
|
14087
15518
|
const response = await client.send(
|
|
14088
15519
|
new module.GetObjectCommand({
|
|
@@ -14104,9 +15535,9 @@ var init_file_store = __esm({
|
|
|
14104
15535
|
throw error;
|
|
14105
15536
|
}
|
|
14106
15537
|
}
|
|
14107
|
-
async exists(sessionId,
|
|
15538
|
+
async exists(sessionId, path15) {
|
|
14108
15539
|
const { client, module } = await this.ensureClient();
|
|
14109
|
-
const key = this.keyForFile(sessionId,
|
|
15540
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14110
15541
|
try {
|
|
14111
15542
|
await client.send(
|
|
14112
15543
|
new module.HeadObjectCommand({
|
|
@@ -14123,9 +15554,9 @@ var init_file_store = __esm({
|
|
|
14123
15554
|
throw error;
|
|
14124
15555
|
}
|
|
14125
15556
|
}
|
|
14126
|
-
async deleteFile(sessionId,
|
|
15557
|
+
async deleteFile(sessionId, path15) {
|
|
14127
15558
|
const { client, module } = await this.ensureClient();
|
|
14128
|
-
const key = this.keyForFile(sessionId,
|
|
15559
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14129
15560
|
await client.send(
|
|
14130
15561
|
new module.DeleteObjectCommand({
|
|
14131
15562
|
Bucket: this.bucket,
|
|
@@ -14180,30 +15611,30 @@ var init_file_store = __esm({
|
|
|
14180
15611
|
);
|
|
14181
15612
|
}
|
|
14182
15613
|
}
|
|
14183
|
-
async getSignedUrl(sessionId,
|
|
15614
|
+
async getSignedUrl(sessionId, path15, expiresIn = 3600) {
|
|
14184
15615
|
const { client, module, presigner } = await this.ensureClient();
|
|
14185
|
-
const key = this.keyForFile(sessionId,
|
|
15616
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14186
15617
|
const command = new module.GetObjectCommand({
|
|
14187
15618
|
Bucket: this.bucket,
|
|
14188
15619
|
Key: key
|
|
14189
15620
|
});
|
|
14190
15621
|
return presigner.getSignedUrl(client, command, { expiresIn });
|
|
14191
15622
|
}
|
|
14192
|
-
async getUploadUrl(sessionId,
|
|
15623
|
+
async getUploadUrl(sessionId, path15, expiresIn = 3600) {
|
|
14193
15624
|
const { client, module, presigner } = await this.ensureClient();
|
|
14194
|
-
const key = this.keyForFile(sessionId,
|
|
15625
|
+
const key = this.keyForFile(sessionId, path15);
|
|
14195
15626
|
const command = new module.PutObjectCommand({
|
|
14196
15627
|
Bucket: this.bucket,
|
|
14197
15628
|
Key: key,
|
|
14198
|
-
ContentType: this.getMimeType(
|
|
15629
|
+
ContentType: this.getMimeType(path15)
|
|
14199
15630
|
});
|
|
14200
15631
|
return presigner.getSignedUrl(client, command, { expiresIn });
|
|
14201
15632
|
}
|
|
14202
15633
|
/**
|
|
14203
15634
|
* Get MIME type based on file extension
|
|
14204
15635
|
*/
|
|
14205
|
-
getMimeType(
|
|
14206
|
-
const ext =
|
|
15636
|
+
getMimeType(path15) {
|
|
15637
|
+
const ext = path15.split(".").pop()?.toLowerCase();
|
|
14207
15638
|
const mimeTypes = {
|
|
14208
15639
|
// Text
|
|
14209
15640
|
txt: "text/plain",
|
|
@@ -14258,8 +15689,8 @@ var init_local_sandbox = __esm({
|
|
|
14258
15689
|
this.defaultRepoRoot = config.defaultRepoRoot ?? `${this.workingDirectory}/repo`;
|
|
14259
15690
|
this.env = { ...process.env, ...config.env };
|
|
14260
15691
|
this.timeout = config.timeout ?? 3e4;
|
|
14261
|
-
if (!
|
|
14262
|
-
|
|
15692
|
+
if (!fs10.existsSync(this.workingDirectory)) {
|
|
15693
|
+
fs10.mkdirSync(this.workingDirectory, { recursive: true });
|
|
14263
15694
|
}
|
|
14264
15695
|
}
|
|
14265
15696
|
runCommand(command) {
|
|
@@ -14288,51 +15719,51 @@ var init_local_sandbox = __esm({
|
|
|
14288
15719
|
}
|
|
14289
15720
|
}
|
|
14290
15721
|
uploadFile(localPath, remotePath) {
|
|
14291
|
-
const dir =
|
|
14292
|
-
if (!
|
|
14293
|
-
|
|
15722
|
+
const dir = path4.dirname(remotePath);
|
|
15723
|
+
if (!fs10.existsSync(dir)) {
|
|
15724
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
14294
15725
|
}
|
|
14295
|
-
|
|
15726
|
+
fs10.copyFileSync(localPath, remotePath);
|
|
14296
15727
|
}
|
|
14297
15728
|
downloadFile(remotePath, localPath) {
|
|
14298
|
-
const dir =
|
|
14299
|
-
if (!
|
|
14300
|
-
|
|
15729
|
+
const dir = path4.dirname(localPath);
|
|
15730
|
+
if (!fs10.existsSync(dir)) {
|
|
15731
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
14301
15732
|
}
|
|
14302
|
-
|
|
15733
|
+
fs10.copyFileSync(remotePath, localPath);
|
|
14303
15734
|
}
|
|
14304
15735
|
uploadDirectory(localPath, remotePath, options) {
|
|
14305
15736
|
const exclude = options?.exclude ?? /* @__PURE__ */ new Set();
|
|
14306
|
-
if (!
|
|
14307
|
-
|
|
15737
|
+
if (!fs10.existsSync(remotePath)) {
|
|
15738
|
+
fs10.mkdirSync(remotePath, { recursive: true });
|
|
14308
15739
|
}
|
|
14309
15740
|
const copyRecursive = (src, dest) => {
|
|
14310
|
-
const entries =
|
|
15741
|
+
const entries = fs10.readdirSync(src, { withFileTypes: true });
|
|
14311
15742
|
for (const entry of entries) {
|
|
14312
15743
|
if (exclude.has(entry.name)) continue;
|
|
14313
|
-
const srcPath =
|
|
14314
|
-
const destPath =
|
|
15744
|
+
const srcPath = path4.join(src, entry.name);
|
|
15745
|
+
const destPath = path4.join(dest, entry.name);
|
|
14315
15746
|
if (entry.isDirectory()) {
|
|
14316
|
-
if (!
|
|
14317
|
-
|
|
15747
|
+
if (!fs10.existsSync(destPath)) {
|
|
15748
|
+
fs10.mkdirSync(destPath, { recursive: true });
|
|
14318
15749
|
}
|
|
14319
15750
|
copyRecursive(srcPath, destPath);
|
|
14320
15751
|
} else if (entry.isFile()) {
|
|
14321
|
-
|
|
15752
|
+
fs10.copyFileSync(srcPath, destPath);
|
|
14322
15753
|
}
|
|
14323
15754
|
}
|
|
14324
15755
|
};
|
|
14325
15756
|
copyRecursive(localPath, remotePath);
|
|
14326
15757
|
}
|
|
14327
15758
|
readFile(remotePath) {
|
|
14328
|
-
return
|
|
15759
|
+
return fs10.readFileSync(remotePath, "utf-8");
|
|
14329
15760
|
}
|
|
14330
15761
|
writeFile(remotePath, content) {
|
|
14331
|
-
const dir =
|
|
14332
|
-
if (!
|
|
14333
|
-
|
|
15762
|
+
const dir = path4.dirname(remotePath);
|
|
15763
|
+
if (!fs10.existsSync(dir)) {
|
|
15764
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
14334
15765
|
}
|
|
14335
|
-
|
|
15766
|
+
fs10.writeFileSync(remotePath, content, "utf-8");
|
|
14336
15767
|
}
|
|
14337
15768
|
close() {
|
|
14338
15769
|
}
|
|
@@ -14340,8 +15771,8 @@ var init_local_sandbox = __esm({
|
|
|
14340
15771
|
* Clean up the working directory
|
|
14341
15772
|
*/
|
|
14342
15773
|
cleanup() {
|
|
14343
|
-
if (
|
|
14344
|
-
|
|
15774
|
+
if (fs10.existsSync(this.workingDirectory)) {
|
|
15775
|
+
fs10.rmSync(this.workingDirectory, { recursive: true, force: true });
|
|
14345
15776
|
}
|
|
14346
15777
|
}
|
|
14347
15778
|
};
|
|
@@ -14451,33 +15882,33 @@ var init_provider_sandbox = __esm({
|
|
|
14451
15882
|
}
|
|
14452
15883
|
async uploadFile(localPath, remotePath) {
|
|
14453
15884
|
await this.initialize();
|
|
14454
|
-
const content =
|
|
15885
|
+
const content = fs10.readFileSync(localPath);
|
|
14455
15886
|
await this.provider.writeFile(this.getSandboxId(), remotePath, content);
|
|
14456
15887
|
}
|
|
14457
15888
|
async downloadFile(remotePath, localPath) {
|
|
14458
15889
|
await this.initialize();
|
|
14459
15890
|
const content = await this.provider.readFile(this.getSandboxId(), remotePath);
|
|
14460
|
-
const dir =
|
|
14461
|
-
if (!
|
|
14462
|
-
|
|
15891
|
+
const dir = path4.dirname(localPath);
|
|
15892
|
+
if (!fs10.existsSync(dir)) {
|
|
15893
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
14463
15894
|
}
|
|
14464
|
-
|
|
15895
|
+
fs10.writeFileSync(localPath, content);
|
|
14465
15896
|
}
|
|
14466
15897
|
async uploadDirectory(localPath, remotePath, options) {
|
|
14467
15898
|
await this.initialize();
|
|
14468
15899
|
const exclude = options?.exclude ?? /* @__PURE__ */ new Set();
|
|
14469
15900
|
await this.runCommand(`mkdir -p '${remotePath}'`);
|
|
14470
15901
|
const uploadRecursive = async (src, dest) => {
|
|
14471
|
-
const entries =
|
|
15902
|
+
const entries = fs10.readdirSync(src, { withFileTypes: true });
|
|
14472
15903
|
for (const entry of entries) {
|
|
14473
15904
|
if (exclude.has(entry.name)) continue;
|
|
14474
|
-
const srcPath =
|
|
15905
|
+
const srcPath = path4.join(src, entry.name);
|
|
14475
15906
|
const destPath = `${dest}/${entry.name}`;
|
|
14476
15907
|
if (entry.isDirectory()) {
|
|
14477
15908
|
await this.runCommand(`mkdir -p '${destPath}'`);
|
|
14478
15909
|
await uploadRecursive(srcPath, destPath);
|
|
14479
15910
|
} else if (entry.isFile()) {
|
|
14480
|
-
const content =
|
|
15911
|
+
const content = fs10.readFileSync(srcPath);
|
|
14481
15912
|
await this.provider.writeFile(this.getSandboxId(), destPath, content);
|
|
14482
15913
|
}
|
|
14483
15914
|
}
|
|
@@ -14715,16 +16146,19 @@ __export(schema_exports, {
|
|
|
14715
16146
|
configDeploymentTriggerEnum: () => configDeploymentTriggerEnum,
|
|
14716
16147
|
configDeployments: () => configDeployments,
|
|
14717
16148
|
configDeploymentsRelations: () => configDeploymentsRelations,
|
|
16149
|
+
eventCategoryEnum: () => eventCategoryEnum,
|
|
14718
16150
|
messageRoleEnum: () => messageRoleEnum,
|
|
14719
16151
|
messages: () => messages,
|
|
14720
16152
|
messagesRelations: () => messagesRelations,
|
|
14721
16153
|
queueItemStatusEnum: () => queueItemStatusEnum,
|
|
14722
16154
|
queueItems: () => queueItems,
|
|
16155
|
+
sessionEvents: () => sessionEvents,
|
|
16156
|
+
sessionEventsRelations: () => sessionEventsRelations,
|
|
14723
16157
|
sessionStatusEnum: () => sessionStatusEnum,
|
|
14724
16158
|
sessions: () => sessions,
|
|
14725
16159
|
sessionsRelations: () => sessionsRelations
|
|
14726
16160
|
});
|
|
14727
|
-
var sessionStatusEnum, messageRoleEnum, agentStatusEnum, agentBackendEnum, queueItemStatusEnum, configDeploymentStatusEnum, configDeploymentTriggerEnum, sessions, messages, attachments, agents, queueItems, configDeployments, sessionsRelations, messagesRelations, attachmentsRelations, configDeploymentsRelations, agentsRelations;
|
|
16161
|
+
var sessionStatusEnum, messageRoleEnum, agentStatusEnum, agentBackendEnum, queueItemStatusEnum, configDeploymentStatusEnum, configDeploymentTriggerEnum, eventCategoryEnum, sessions, messages, attachments, agents, queueItems, configDeployments, sessionEvents, sessionsRelations, sessionEventsRelations, messagesRelations, attachmentsRelations, configDeploymentsRelations, agentsRelations;
|
|
14728
16162
|
var init_schema = __esm({
|
|
14729
16163
|
"src/storage-postgres/schema.ts"() {
|
|
14730
16164
|
sessionStatusEnum = pgEnum("session_status", [
|
|
@@ -14766,6 +16200,22 @@ var init_schema = __esm({
|
|
|
14766
16200
|
"initial",
|
|
14767
16201
|
"rollback"
|
|
14768
16202
|
]);
|
|
16203
|
+
eventCategoryEnum = pgEnum("event_category", [
|
|
16204
|
+
"lifecycle",
|
|
16205
|
+
// session_start, session_end, turn_complete
|
|
16206
|
+
"content",
|
|
16207
|
+
// text_stream (aggregated), thinking_stream
|
|
16208
|
+
"tool",
|
|
16209
|
+
// tool_use, tool_result
|
|
16210
|
+
"system",
|
|
16211
|
+
// mcp_status, sandbox_log
|
|
16212
|
+
"error",
|
|
16213
|
+
// error events
|
|
16214
|
+
"file",
|
|
16215
|
+
// file_push, file_pull, file_sync (file sync operations)
|
|
16216
|
+
"input"
|
|
16217
|
+
// user_input (user prompts/messages)
|
|
16218
|
+
]);
|
|
14769
16219
|
sessions = pgTable(
|
|
14770
16220
|
"sessions",
|
|
14771
16221
|
{
|
|
@@ -14921,6 +16371,32 @@ var init_schema = __esm({
|
|
|
14921
16371
|
index("idx_config_deployments_created").on(table.agentId, table.createdAt)
|
|
14922
16372
|
]
|
|
14923
16373
|
);
|
|
16374
|
+
sessionEvents = pgTable(
|
|
16375
|
+
"session_events",
|
|
16376
|
+
{
|
|
16377
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
16378
|
+
sessionId: uuid("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
16379
|
+
eventType: text("event_type").notNull(),
|
|
16380
|
+
category: eventCategoryEnum("category").notNull(),
|
|
16381
|
+
startedAt: timestamp("started_at", { withTimezone: true }).notNull(),
|
|
16382
|
+
endedAt: timestamp("ended_at", { withTimezone: true }),
|
|
16383
|
+
durationMs: integer("duration_ms"),
|
|
16384
|
+
eventData: jsonb("event_data").default({}).$type(),
|
|
16385
|
+
// Tool-specific fields
|
|
16386
|
+
toolUseId: text("tool_use_id"),
|
|
16387
|
+
toolName: text("tool_name"),
|
|
16388
|
+
// Aggregation fields (for text_stream events)
|
|
16389
|
+
isAggregated: boolean("is_aggregated").default(false),
|
|
16390
|
+
aggregatedCount: integer("aggregated_count"),
|
|
16391
|
+
// Ordering
|
|
16392
|
+
sequenceNumber: integer("sequence_number").notNull(),
|
|
16393
|
+
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull()
|
|
16394
|
+
},
|
|
16395
|
+
(table) => [
|
|
16396
|
+
index("idx_session_events_session").on(table.sessionId, table.sequenceNumber),
|
|
16397
|
+
index("idx_session_events_category").on(table.sessionId, table.category)
|
|
16398
|
+
]
|
|
16399
|
+
);
|
|
14924
16400
|
sessionsRelations = relations(sessions, ({ one, many }) => ({
|
|
14925
16401
|
parentSession: one(sessions, {
|
|
14926
16402
|
fields: [sessions.parentSessionId],
|
|
@@ -14930,7 +16406,14 @@ var init_schema = __esm({
|
|
|
14930
16406
|
childSessions: many(sessions, {
|
|
14931
16407
|
relationName: "parentSession"
|
|
14932
16408
|
}),
|
|
14933
|
-
messages: many(messages)
|
|
16409
|
+
messages: many(messages),
|
|
16410
|
+
events: many(sessionEvents)
|
|
16411
|
+
}));
|
|
16412
|
+
sessionEventsRelations = relations(sessionEvents, ({ one }) => ({
|
|
16413
|
+
session: one(sessions, {
|
|
16414
|
+
fields: [sessionEvents.sessionId],
|
|
16415
|
+
references: [sessions.id]
|
|
16416
|
+
})
|
|
14934
16417
|
}));
|
|
14935
16418
|
messagesRelations = relations(messages, ({ one, many }) => ({
|
|
14936
16419
|
session: one(sessions, {
|
|
@@ -16469,6 +17952,92 @@ var init_storage3 = __esm({
|
|
|
16469
17952
|
executionWebhookSecret: row.execution_webhook_secret ?? void 0
|
|
16470
17953
|
};
|
|
16471
17954
|
}
|
|
17955
|
+
rowToSessionEvent(row) {
|
|
17956
|
+
return {
|
|
17957
|
+
id: row.id,
|
|
17958
|
+
sessionId: row.session_id,
|
|
17959
|
+
eventType: row.event_type,
|
|
17960
|
+
category: row.category,
|
|
17961
|
+
startedAt: new Date(row.started_at),
|
|
17962
|
+
endedAt: row.ended_at ? new Date(row.ended_at) : void 0,
|
|
17963
|
+
durationMs: row.duration_ms ?? void 0,
|
|
17964
|
+
eventData: row.event_data ?? void 0,
|
|
17965
|
+
toolUseId: row.tool_use_id ?? void 0,
|
|
17966
|
+
toolName: row.tool_name ?? void 0,
|
|
17967
|
+
isAggregated: row.is_aggregated ?? void 0,
|
|
17968
|
+
aggregatedCount: row.aggregated_count ?? void 0,
|
|
17969
|
+
sequenceNumber: row.sequence_number,
|
|
17970
|
+
createdAt: new Date(row.created_at)
|
|
17971
|
+
};
|
|
17972
|
+
}
|
|
17973
|
+
// ============================================================================
|
|
17974
|
+
// Events
|
|
17975
|
+
// ============================================================================
|
|
17976
|
+
async saveEvents(sessionId, events) {
|
|
17977
|
+
if (events.length === 0) return [];
|
|
17978
|
+
const { data, error } = await this.client.from("session_events").insert(
|
|
17979
|
+
events.map((event) => ({
|
|
17980
|
+
session_id: sessionId,
|
|
17981
|
+
event_type: event.eventType,
|
|
17982
|
+
category: event.category,
|
|
17983
|
+
started_at: event.startedAt.toISOString(),
|
|
17984
|
+
ended_at: event.endedAt?.toISOString() ?? null,
|
|
17985
|
+
duration_ms: event.durationMs ?? null,
|
|
17986
|
+
event_data: event.eventData ?? {},
|
|
17987
|
+
tool_use_id: event.toolUseId ?? null,
|
|
17988
|
+
tool_name: event.toolName ?? null,
|
|
17989
|
+
is_aggregated: event.isAggregated ?? false,
|
|
17990
|
+
aggregated_count: event.aggregatedCount ?? null,
|
|
17991
|
+
sequence_number: event.sequenceNumber
|
|
17992
|
+
}))
|
|
17993
|
+
).select();
|
|
17994
|
+
if (error) {
|
|
17995
|
+
throw new Error(`Failed to save events: ${error.message}`);
|
|
17996
|
+
}
|
|
17997
|
+
return data.map((row) => this.rowToSessionEvent(row));
|
|
17998
|
+
}
|
|
17999
|
+
async getSessionEvents(sessionId, options = {}) {
|
|
18000
|
+
const limit = options.limit ?? 100;
|
|
18001
|
+
const offset = options.offset ?? 0;
|
|
18002
|
+
let query = this.client.from("session_events").select("*", { count: "exact" }).eq("session_id", sessionId);
|
|
18003
|
+
if (options.category) {
|
|
18004
|
+
query = query.eq("category", options.category);
|
|
18005
|
+
}
|
|
18006
|
+
if (options.eventType) {
|
|
18007
|
+
query = query.eq("event_type", options.eventType);
|
|
18008
|
+
}
|
|
18009
|
+
if (options.filePath) {
|
|
18010
|
+
query = query.or(
|
|
18011
|
+
`event_data->>filePath.eq.${options.filePath},and(category.eq.tool,tool_name.in.(Read,Write,Edit),event_data->input->>file_path.eq.${options.filePath})`
|
|
18012
|
+
);
|
|
18013
|
+
}
|
|
18014
|
+
const ascending = options.order !== "desc";
|
|
18015
|
+
query = query.order("sequence_number", { ascending }).range(offset, offset + limit - 1);
|
|
18016
|
+
const { data, error, count } = await query;
|
|
18017
|
+
if (error) {
|
|
18018
|
+
throw new Error(`Failed to get session events: ${error.message}`);
|
|
18019
|
+
}
|
|
18020
|
+
const total = count ?? 0;
|
|
18021
|
+
return {
|
|
18022
|
+
items: data.map((row) => this.rowToSessionEvent(row)),
|
|
18023
|
+
total,
|
|
18024
|
+
hasMore: offset + limit < total,
|
|
18025
|
+
nextCursor: offset + limit < total ? String(offset + limit) : void 0
|
|
18026
|
+
};
|
|
18027
|
+
}
|
|
18028
|
+
async deleteSessionEvents(sessionId) {
|
|
18029
|
+
const { error } = await this.client.from("session_events").delete().eq("session_id", sessionId);
|
|
18030
|
+
if (error) {
|
|
18031
|
+
throw new Error(`Failed to delete session events: ${error.message}`);
|
|
18032
|
+
}
|
|
18033
|
+
}
|
|
18034
|
+
async getNextEventSequence(sessionId) {
|
|
18035
|
+
const { data, error } = await this.client.from("session_events").select("sequence_number").eq("session_id", sessionId).order("sequence_number", { ascending: false }).limit(1).single();
|
|
18036
|
+
if (error && error.code !== "PGRST116") {
|
|
18037
|
+
throw new Error(`Failed to get next event sequence: ${error.message}`);
|
|
18038
|
+
}
|
|
18039
|
+
return data ? data.sequence_number + 1 : 1;
|
|
18040
|
+
}
|
|
16472
18041
|
};
|
|
16473
18042
|
}
|
|
16474
18043
|
});
|
|
@@ -16491,8 +18060,8 @@ var init_client = __esm({
|
|
|
16491
18060
|
/**
|
|
16492
18061
|
* Make an authenticated request to the Ash Cloud API
|
|
16493
18062
|
*/
|
|
16494
|
-
async request(method,
|
|
16495
|
-
const url = new URL(`${this.baseUrl}${
|
|
18063
|
+
async request(method, path15, options) {
|
|
18064
|
+
const url = new URL(`${this.baseUrl}${path15}`);
|
|
16496
18065
|
if (options?.query) {
|
|
16497
18066
|
for (const [key, value] of Object.entries(options.query)) {
|
|
16498
18067
|
if (value !== void 0) {
|
|
@@ -16560,8 +18129,8 @@ var init_client = __esm({
|
|
|
16560
18129
|
/**
|
|
16561
18130
|
* Make a streaming request (for SSE endpoints)
|
|
16562
18131
|
*/
|
|
16563
|
-
async *stream(method,
|
|
16564
|
-
const url = `${this.baseUrl}${
|
|
18132
|
+
async *stream(method, path15, options) {
|
|
18133
|
+
const url = `${this.baseUrl}${path15}`;
|
|
16565
18134
|
const headers = {
|
|
16566
18135
|
Authorization: `Bearer ${this.apiKey}`,
|
|
16567
18136
|
"Content-Type": "application/json",
|
|
@@ -17323,6 +18892,6 @@ var init_src = __esm({
|
|
|
17323
18892
|
});
|
|
17324
18893
|
init_src();
|
|
17325
18894
|
|
|
17326
|
-
export { AVAILABLE_MODELS, AgentConfigSchema, AgentError, AgentHarness, AgentStatus, AshCloud, AshCloudApiError, AshCloudClient, AttachmentConfigSchema, AttachmentStorage, ClaudeSdkClient, CloudSandbox, CloudStorage, ConfigBuilder, ConfigError, CredentialManager, DEFAULT_MODELS, DEFAULT_SANDBOX_PROVIDER_CONFIG, GCSBundleStore, GeminiCliClient, GitHubFileProvider, HarnessConfigSchema, HarnessError, HarnessErrorCode, HarnessEventEmitter, LocalBundleStore, LocalFileProvider, LocalSandbox, McpConfigBuilder, McpPresets, McpServers, MemoryBundleStore, MemoryCredentialStorage, MemoryQueueStorage, MemoryRateLimitStore, MemoryStorage, MessageRole, NotFoundError, PostgresQueueStorage, PostgresStorage, ProviderSandbox, QueueItemStatus, QueueProcessor, RuntimeConfigBuilder, RuntimePresets, S3BundleStore, S3FileStore, SENSITIVE_PATHS, SandboxFileSync, SandboxGitRepo, SandboxLogger, SandboxPool, ServerConfigSchema, SessionError, SessionManager, SessionStatus, SkillCatalog, SkillManager, StorageConfigSchema, StorageError, StreamEventType, SupabaseBundleStore, SupabaseStorage, ToolCallProcessor, ToolError, ValidationError, Workspace, WorkspaceManager, attachmentSchema, attachmentToDataUrl, checkSecurityConfig, claudeClient, cleanupAllSandboxes, configureMcp, configureRuntime, convertClaudeMessage, createAgentsRouter, createAshCloud, createBackendExecutor, createCloudSandbox, createConfig, createCredentialManager, createOpenAPIServer as createDocumentedServer, createE2BSandbox, createEventHandler, createEventMiddlewareChain, createGCSBundleStore, createGeminiExecutor, createGitHubFileProvider, createGitRepo, createHarnessServer, createLocalBundleStore, createLocalFileProvider, createLocalSandbox, createLogger, createMemoryBundleStore, createMinioBundleStore, createMinioFileStore, createModalSandbox, createAgentsRouter2 as createOpenAPIAgentsRouter, createOpenAPIServer, createSessionsRouter2 as createOpenAPISessionsRouter, createSkillsRouter2 as createOpenAPISkillsRouter, createProviderSandbox, createQueueProcessor, createQueueRouter, createR2BundleStore, createR2FileStore, createS3BundleStore, createS3FileStore, createSandboxFileOperations, createSandboxFileSync, createSandboxLogger, createSandboxOptions, createSessionWorkspace, createSessionsRouter, createSkillCatalog, createSkillManager, createSupabaseBundleStore, createSupabaseBundleStoreFromEnv, createToolCall, createToolCallProcessor, createVercelSandbox, createVercelSandboxExecutor, createWorkspace, createWorkspaceHooks, createWorkspaceManager, dataUrlToBuffer, defineAgent, defineConfig, ensureSandboxPoolInitialized, env, envOptional, extractTextContent, extractTextFromMessage, fileEntrySchema, formatToolName, generateDockerCommand, generateMcpServerPackage, generateMcpServers, generateProxyEnv, generateToolSummary, getActionIcon, getActionLabel, getAllHeartbeatStatuses, getApiKeyEnvVar, getDefaultModel, getHeartbeatStatus, getOrCreateSandbox, getSandboxCacheStats, getSandboxPool, getWorkspaceManager, gitHubSkillSourceSchema, globalEventEmitter, hasErrorCode, hashStartupScript, httpMcpWithAuth, initializeSandboxPool, introspectMcpServer, invalidateSandbox, isCommandRunAction, isDocumentMimeType, isErrorEntry, isFileEditAction, isFileReadAction, isFileWriteAction, isGenericToolAction, isGlobAction, isHarnessError, isHttpMcpConfig, isImageMimeType, isMcpToolAction, isSandboxExpiredError, isSandboxRunning, isSearchAction, isSensitivePath, isStdioMcpConfig, isTodoWriteAction, isToolCallEntry, isValidModel, isWebFetchAction, isWebSearchAction, listFilesInSandbox, loadConfig, loadGitHubSkill, loadGitHubSkills, loadWorkspaceState, localSkillSourceSchema, log, mapClaudeOptionsToGemini, mapToolToActionType, markConfigInstalled, markSdkInstalled, markStartupScriptRan, mcpAuthToHeaders, messageContentSchema, messageSchema, needsStartupScriptRerun, normalizeGitHubConfigs, normalizeMcpServers, normalizeMessages, normalizeToolResult, onHeartbeat, schemas_exports as openApiSchemas, parseCommandResult, parseGitHubUrl, parseMcpToolName, processStreamEvents, rateLimit, rateLimiters, readFileFromSandbox, rekeySessionId, releaseSandbox, requestLogger, saveWorkspaceState, schema_exports as schema, sessionSchema, shouldUseSandbox, shutdownSandboxPool, skillConfigSchema, skillSourceSchema, sseMcpWithAuth, startServer, updateToolCallWithResult, writeFileToSandbox };
|
|
18895
|
+
export { AVAILABLE_MODELS, AgentConfigSchema, AgentError, AgentHarness, AgentStatus, AshCloud, AshCloudApiError, AshCloudClient, AttachmentConfigSchema, AttachmentStorage, ClaudeSdkClient, CloudSandbox, CloudStorage, ConfigBuilder, ConfigError, CredentialManager, DEFAULT_MODELS, DEFAULT_SANDBOX_PROVIDER_CONFIG, EventCategory, FileWatcherManager, GCSBundleStore, GeminiCliClient, GitHubFileProvider, HarnessConfigSchema, HarnessError, HarnessErrorCode, HarnessEventEmitter, InSandboxWatcher, InSandboxWatcherManager, LocalBundleStore, LocalFileProvider, LocalSandbox, McpConfigBuilder, McpPresets, McpServers, MemoryBundleStore, MemoryCredentialStorage, MemoryQueueStorage, MemoryRateLimitStore, MemoryStorage, MessageRole, NotFoundError, PostgresQueueStorage, PostgresStorage, ProviderSandbox, QueueItemStatus, QueueProcessor, RemoteFileWatcherManager, RemoteSandboxFileWatcher, RuntimeConfigBuilder, RuntimePresets, S3BundleStore, S3FileStore, SENSITIVE_PATHS, SandboxFileSync, SandboxFileWatcher, SandboxGitRepo, SandboxLogger, SandboxPool, ServerConfigSchema, SessionError, SessionManager, SessionStatus, SkillCatalog, SkillManager, StorageConfigSchema, StorageError, StreamEventType, SupabaseBundleStore, SupabaseStorage, ToolCallProcessor, ToolError, ValidationError, Workspace, WorkspaceManager, attachmentSchema, attachmentToDataUrl, checkSecurityConfig, claudeClient, cleanupAllSandboxes, configureMcp, configureRuntime, convertClaudeMessage, createAgentsRouter, createAshCloud, createBackendExecutor, createCloudSandbox, createConfig, createCredentialManager, createOpenAPIServer as createDocumentedServer, createE2BSandbox, createEventHandler, createEventMiddlewareChain, createFileWatcher, createFileWatcherManager, createGCSBundleStore, createGeminiExecutor, createGitHubFileProvider, createGitRepo, createHarnessServer, createInSandboxWatcher, createInSandboxWatcherManager, createLocalBundleStore, createLocalFileProvider, createLocalSandbox, createLogger, createMemoryBundleStore, createMinioBundleStore, createMinioFileStore, createModalSandbox, createAgentsRouter2 as createOpenAPIAgentsRouter, createOpenAPIServer, createSessionsRouter2 as createOpenAPISessionsRouter, createSkillsRouter2 as createOpenAPISkillsRouter, createProviderSandbox, createQueueProcessor, createQueueRouter, createR2BundleStore, createR2FileStore, createRemoteFileWatcher, createRemoteFileWatcherManager, createS3BundleStore, createS3FileStore, createSandboxFileOperations, createSandboxFileSync, createSandboxLogger, createSandboxOptions, createSessionWorkspace, createSessionsRouter, createSkillCatalog, createSkillManager, createSupabaseBundleStore, createSupabaseBundleStoreFromEnv, createToolCall, createToolCallProcessor, createVercelSandbox, createVercelSandboxExecutor, createWorkspace, createWorkspaceHooks, createWorkspaceManager, dataUrlToBuffer, defineAgent, defineConfig, ensureSandboxPoolInitialized, env, envOptional, extractTextContent, extractTextFromMessage, fileEntrySchema, formatToolName, generateDockerCommand, generateMcpServerPackage, generateMcpServers, generateProxyEnv, generateToolSummary, getActionIcon, getActionLabel, getAllHeartbeatStatuses, getApiKeyEnvVar, getDefaultModel, getFileWatcherManager, getHeartbeatStatus, getInSandboxWatcherManager, getOrCreateSandbox, getRemoteFileWatcherManager, getSandboxCacheStats, getSandboxPool, getWorkspaceManager, gitHubSkillSourceSchema, globalEventEmitter, hasErrorCode, hashStartupScript, httpMcpWithAuth, initializeSandboxPool, introspectMcpServer, invalidateSandbox, isCommandRunAction, isDocumentMimeType, isErrorEntry, isFileEditAction, isFileReadAction, isFileWriteAction, isGenericToolAction, isGlobAction, isHarnessError, isHttpMcpConfig, isImageMimeType, isMcpToolAction, isSandboxExpiredError, isSandboxRunning, isSearchAction, isSensitivePath, isStdioMcpConfig, isTodoWriteAction, isToolCallEntry, isValidModel, isWebFetchAction, isWebSearchAction, listFilesInSandbox, loadConfig, loadGitHubSkill, loadGitHubSkills, loadWorkspaceState, localSkillSourceSchema, log, mapClaudeOptionsToGemini, mapToolToActionType, markConfigInstalled, markSdkInstalled, markStartupScriptRan, mcpAuthToHeaders, messageContentSchema, messageSchema, needsStartupScriptRerun, normalizeGitHubConfigs, normalizeMcpServers, normalizeMessages, normalizeToolResult, onHeartbeat, schemas_exports as openApiSchemas, parseCommandResult, parseGitHubUrl, parseMcpToolName, processStreamEvents, rateLimit, rateLimiters, readFileFromSandbox, rekeySessionId, releaseSandbox, requestLogger, saveWorkspaceState, schema_exports as schema, sessionSchema, shouldUseSandbox, shutdownSandboxPool, skillConfigSchema, skillSourceSchema, sseMcpWithAuth, startServer, updateToolCallWithResult, writeFileToSandbox };
|
|
17327
18896
|
//# sourceMappingURL=index.js.map
|
|
17328
18897
|
//# sourceMappingURL=index.js.map
|