@ash-cloud/ash-ai 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +353 -553
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -144
- package/dist/index.d.ts +34 -144
- package/dist/index.js +352 -551
- package/dist/index.js.map +1 -1
- package/dist/playground/components/ChatInput.d.ts.map +1 -1
- package/dist/playground/pages/ChatPage.d.ts.map +1 -1
- package/dist/playground.css +1 -1
- package/dist/playground.js +897 -817
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5289,6 +5289,23 @@ function isSandboxRunning(sessionId) {
|
|
|
5289
5289
|
const cached = sandboxCache.get(sessionId);
|
|
5290
5290
|
return cached !== void 0 && !cached.isExpired;
|
|
5291
5291
|
}
|
|
5292
|
+
function getCachedSandbox(sessionId) {
|
|
5293
|
+
const cached = sandboxCache.get(sessionId);
|
|
5294
|
+
if (!cached || cached.isExpired) {
|
|
5295
|
+
return null;
|
|
5296
|
+
}
|
|
5297
|
+
cached.lastUsedAt = Date.now();
|
|
5298
|
+
return {
|
|
5299
|
+
sandbox: cached.sandbox,
|
|
5300
|
+
sandboxId: cached.sandbox.sandboxId,
|
|
5301
|
+
sdkInstalled: cached.sdkInstalled,
|
|
5302
|
+
startupScriptRan: cached.startupScriptRan,
|
|
5303
|
+
startupScriptHash: cached.startupScriptHash,
|
|
5304
|
+
isNew: false,
|
|
5305
|
+
configFileUrl: cached.configFileUrl,
|
|
5306
|
+
configInstalledAt: cached.configInstalledAt
|
|
5307
|
+
};
|
|
5308
|
+
}
|
|
5292
5309
|
async function writeFileToSandbox(sessionId, path15, content) {
|
|
5293
5310
|
const cached = sandboxCache.get(sessionId);
|
|
5294
5311
|
if (!cached) {
|
|
@@ -5357,6 +5374,54 @@ async function readFileFromSandbox(sessionId, path15) {
|
|
|
5357
5374
|
};
|
|
5358
5375
|
}
|
|
5359
5376
|
}
|
|
5377
|
+
async function executeCommandInSandbox(sessionId, command, options) {
|
|
5378
|
+
const cached = sandboxCache.get(sessionId);
|
|
5379
|
+
if (!cached) {
|
|
5380
|
+
return { success: false, error: "No active sandbox for session" };
|
|
5381
|
+
}
|
|
5382
|
+
if (cached.isExpired) {
|
|
5383
|
+
return { success: false, error: "Sandbox has expired" };
|
|
5384
|
+
}
|
|
5385
|
+
const startTime = Date.now();
|
|
5386
|
+
try {
|
|
5387
|
+
const sandbox = cached.sandbox;
|
|
5388
|
+
let fullCommand = command;
|
|
5389
|
+
if (options?.cwd) {
|
|
5390
|
+
fullCommand = `cd ${JSON.stringify(options.cwd)} && ${command}`;
|
|
5391
|
+
}
|
|
5392
|
+
const result = await sandbox.runCommand({
|
|
5393
|
+
cmd: "bash",
|
|
5394
|
+
args: ["-c", fullCommand],
|
|
5395
|
+
env: options?.env
|
|
5396
|
+
});
|
|
5397
|
+
const stdout = await result.stdout();
|
|
5398
|
+
const stderr = await result.stderr();
|
|
5399
|
+
const durationMs = Date.now() - startTime;
|
|
5400
|
+
cached.lastUsedAt = Date.now();
|
|
5401
|
+
return {
|
|
5402
|
+
success: result.exitCode === 0,
|
|
5403
|
+
exitCode: result.exitCode,
|
|
5404
|
+
stdout,
|
|
5405
|
+
stderr,
|
|
5406
|
+
durationMs
|
|
5407
|
+
};
|
|
5408
|
+
} catch (error) {
|
|
5409
|
+
const durationMs = Date.now() - startTime;
|
|
5410
|
+
if (isSandboxExpiredError(error)) {
|
|
5411
|
+
cached.isExpired = true;
|
|
5412
|
+
return {
|
|
5413
|
+
success: false,
|
|
5414
|
+
error: "Sandbox has expired",
|
|
5415
|
+
durationMs
|
|
5416
|
+
};
|
|
5417
|
+
}
|
|
5418
|
+
return {
|
|
5419
|
+
success: false,
|
|
5420
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
5421
|
+
durationMs
|
|
5422
|
+
};
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5360
5425
|
async function listFilesInSandbox(sessionId, path15) {
|
|
5361
5426
|
const cached = sandboxCache.get(sessionId);
|
|
5362
5427
|
if (!cached) {
|
|
@@ -5836,19 +5901,7 @@ function getFileWatcherManager() {
|
|
|
5836
5901
|
function createFileWatcherManager() {
|
|
5837
5902
|
return new exports.FileWatcherManager();
|
|
5838
5903
|
}
|
|
5839
|
-
|
|
5840
|
-
return new exports.RemoteSandboxFileWatcher(options);
|
|
5841
|
-
}
|
|
5842
|
-
function getRemoteFileWatcherManager() {
|
|
5843
|
-
if (!globalRemoteWatcherManager) {
|
|
5844
|
-
globalRemoteWatcherManager = new exports.RemoteFileWatcherManager();
|
|
5845
|
-
}
|
|
5846
|
-
return globalRemoteWatcherManager;
|
|
5847
|
-
}
|
|
5848
|
-
function createRemoteFileWatcherManager() {
|
|
5849
|
-
return new exports.RemoteFileWatcherManager();
|
|
5850
|
-
}
|
|
5851
|
-
exports.SandboxFileWatcher = void 0; exports.FileWatcherManager = void 0; var globalWatcherManager; exports.RemoteSandboxFileWatcher = void 0; exports.RemoteFileWatcherManager = void 0; var globalRemoteWatcherManager;
|
|
5904
|
+
exports.SandboxFileWatcher = void 0; exports.FileWatcherManager = void 0; var globalWatcherManager;
|
|
5852
5905
|
var init_sandbox_file_watcher = __esm({
|
|
5853
5906
|
"src/runtime/sandbox-file-watcher.ts"() {
|
|
5854
5907
|
exports.SandboxFileWatcher = class {
|
|
@@ -6090,214 +6143,327 @@ var init_sandbox_file_watcher = __esm({
|
|
|
6090
6143
|
}
|
|
6091
6144
|
};
|
|
6092
6145
|
globalWatcherManager = null;
|
|
6093
|
-
|
|
6146
|
+
}
|
|
6147
|
+
});
|
|
6148
|
+
|
|
6149
|
+
// src/runtime/in-sandbox-watcher.ts
|
|
6150
|
+
function createInSandboxWatcher(options) {
|
|
6151
|
+
return new exports.InSandboxWatcher(options);
|
|
6152
|
+
}
|
|
6153
|
+
function getInSandboxWatcherManager() {
|
|
6154
|
+
if (!globalInSandboxManager) {
|
|
6155
|
+
globalInSandboxManager = new exports.InSandboxWatcherManager();
|
|
6156
|
+
}
|
|
6157
|
+
return globalInSandboxManager;
|
|
6158
|
+
}
|
|
6159
|
+
function createInSandboxWatcherManager() {
|
|
6160
|
+
return new exports.InSandboxWatcherManager();
|
|
6161
|
+
}
|
|
6162
|
+
var WATCHER_SCRIPT; exports.InSandboxWatcher = void 0; exports.InSandboxWatcherManager = void 0; var globalInSandboxManager;
|
|
6163
|
+
var init_in_sandbox_watcher = __esm({
|
|
6164
|
+
"src/runtime/in-sandbox-watcher.ts"() {
|
|
6165
|
+
init_vercel_sandbox_executor();
|
|
6166
|
+
WATCHER_SCRIPT = `
|
|
6167
|
+
const fs = require('fs');
|
|
6168
|
+
const path = require('path');
|
|
6169
|
+
|
|
6170
|
+
const watchPath = process.argv[2] || '.';
|
|
6171
|
+
const ignored = new Set(['node_modules', '.git', '.cache', '__pycache__']);
|
|
6172
|
+
|
|
6173
|
+
// Track all watchers for cleanup
|
|
6174
|
+
const watchers = new Map();
|
|
6175
|
+
|
|
6176
|
+
// Debounce map to coalesce rapid changes
|
|
6177
|
+
const pending = new Map();
|
|
6178
|
+
const DEBOUNCE_MS = 100;
|
|
6179
|
+
|
|
6180
|
+
function emit(type, filePath) {
|
|
6181
|
+
const event = {
|
|
6182
|
+
type,
|
|
6183
|
+
path: filePath,
|
|
6184
|
+
timestamp: Date.now()
|
|
6185
|
+
};
|
|
6186
|
+
console.log(JSON.stringify(event));
|
|
6187
|
+
}
|
|
6188
|
+
|
|
6189
|
+
function shouldIgnore(name) {
|
|
6190
|
+
return ignored.has(name) || name.startsWith('.');
|
|
6191
|
+
}
|
|
6192
|
+
|
|
6193
|
+
function watchDir(dir) {
|
|
6194
|
+
try {
|
|
6195
|
+
const watcher = fs.watch(dir, { persistent: true }, (eventType, filename) => {
|
|
6196
|
+
if (!filename || shouldIgnore(filename)) return;
|
|
6197
|
+
|
|
6198
|
+
const fullPath = path.join(dir, filename);
|
|
6199
|
+
const key = fullPath;
|
|
6200
|
+
|
|
6201
|
+
// Debounce
|
|
6202
|
+
if (pending.has(key)) {
|
|
6203
|
+
clearTimeout(pending.get(key));
|
|
6204
|
+
}
|
|
6205
|
+
|
|
6206
|
+
pending.set(key, setTimeout(() => {
|
|
6207
|
+
pending.delete(key);
|
|
6208
|
+
|
|
6209
|
+
fs.stat(fullPath, (err, stats) => {
|
|
6210
|
+
if (err) {
|
|
6211
|
+
if (err.code === 'ENOENT') {
|
|
6212
|
+
emit('unlink', fullPath);
|
|
6213
|
+
// Stop watching if it was a directory
|
|
6214
|
+
if (watchers.has(fullPath)) {
|
|
6215
|
+
watchers.get(fullPath).close();
|
|
6216
|
+
watchers.delete(fullPath);
|
|
6217
|
+
}
|
|
6218
|
+
}
|
|
6219
|
+
} else {
|
|
6220
|
+
const type = eventType === 'rename' ? 'add' : 'change';
|
|
6221
|
+
emit(type, fullPath);
|
|
6222
|
+
|
|
6223
|
+
// If it's a new directory, start watching it
|
|
6224
|
+
if (stats.isDirectory() && !watchers.has(fullPath)) {
|
|
6225
|
+
watchDir(fullPath);
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
});
|
|
6229
|
+
}, DEBOUNCE_MS));
|
|
6230
|
+
});
|
|
6231
|
+
|
|
6232
|
+
watchers.set(dir, watcher);
|
|
6233
|
+
|
|
6234
|
+
// Watch subdirectories
|
|
6235
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
6236
|
+
for (const entry of entries) {
|
|
6237
|
+
if (entry.isDirectory() && !shouldIgnore(entry.name)) {
|
|
6238
|
+
watchDir(path.join(dir, entry.name));
|
|
6239
|
+
}
|
|
6240
|
+
}
|
|
6241
|
+
} catch (err) {
|
|
6242
|
+
// Directory may not exist or be inaccessible
|
|
6243
|
+
console.error(JSON.stringify({ error: err.message, dir }));
|
|
6244
|
+
}
|
|
6245
|
+
}
|
|
6246
|
+
|
|
6247
|
+
// Start watching
|
|
6248
|
+
watchDir(watchPath);
|
|
6249
|
+
|
|
6250
|
+
// Keep alive
|
|
6251
|
+
process.on('SIGTERM', () => {
|
|
6252
|
+
for (const watcher of watchers.values()) {
|
|
6253
|
+
watcher.close();
|
|
6254
|
+
}
|
|
6255
|
+
process.exit(0);
|
|
6256
|
+
});
|
|
6257
|
+
|
|
6258
|
+
// Heartbeat to indicate we're running
|
|
6259
|
+
setInterval(() => {
|
|
6260
|
+
console.log(JSON.stringify({ heartbeat: true, timestamp: Date.now() }));
|
|
6261
|
+
}, 5000);
|
|
6262
|
+
|
|
6263
|
+
console.log(JSON.stringify({ started: true, path: watchPath }));
|
|
6264
|
+
`;
|
|
6265
|
+
exports.InSandboxWatcher = class {
|
|
6094
6266
|
sessionId;
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
pollTimer = null;
|
|
6100
|
-
previousFiles = /* @__PURE__ */ new Map();
|
|
6267
|
+
watchPath;
|
|
6268
|
+
outputPollIntervalMs;
|
|
6269
|
+
sandboxState = null;
|
|
6270
|
+
outputPollTimer = null;
|
|
6101
6271
|
subscribers = /* @__PURE__ */ new Set();
|
|
6102
|
-
|
|
6272
|
+
isRunning = false;
|
|
6103
6273
|
startedAt;
|
|
6104
|
-
|
|
6105
|
-
|
|
6274
|
+
lastHeartbeat;
|
|
6275
|
+
lastOutputPosition = 0;
|
|
6106
6276
|
onError;
|
|
6277
|
+
onReady;
|
|
6107
6278
|
constructor(options) {
|
|
6108
6279
|
this.sessionId = options.sessionId;
|
|
6109
|
-
this.
|
|
6110
|
-
this.
|
|
6111
|
-
this.pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
6112
|
-
this.ignored = options.ignored ?? ["**/node_modules/**", "**/.git/**"];
|
|
6280
|
+
this.watchPath = options.watchPath;
|
|
6281
|
+
this.outputPollIntervalMs = options.outputPollIntervalMs ?? 1e3;
|
|
6113
6282
|
this.onError = options.onError;
|
|
6283
|
+
this.onReady = options.onReady;
|
|
6114
6284
|
if (options.onFileChange) {
|
|
6115
6285
|
this.subscribers.add(options.onFileChange);
|
|
6116
6286
|
}
|
|
6117
6287
|
}
|
|
6118
6288
|
/**
|
|
6119
|
-
* Start
|
|
6289
|
+
* Start the watcher process inside the sandbox
|
|
6120
6290
|
*/
|
|
6121
6291
|
async start() {
|
|
6122
|
-
if (this.
|
|
6123
|
-
console.warn(`[
|
|
6292
|
+
if (this.isRunning) {
|
|
6293
|
+
console.warn(`[IN_SANDBOX_WATCHER] Already running for session ${this.sessionId}`);
|
|
6124
6294
|
return;
|
|
6125
6295
|
}
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
this.pollTimer = setInterval(async () => {
|
|
6131
|
-
try {
|
|
6132
|
-
await this.scan(false);
|
|
6133
|
-
} catch (error) {
|
|
6134
|
-
console.error(`[REMOTE_WATCHER] Poll error for session ${this.sessionId}:`, error);
|
|
6135
|
-
if (this.onError && error instanceof Error) {
|
|
6136
|
-
this.onError(error);
|
|
6137
|
-
}
|
|
6296
|
+
try {
|
|
6297
|
+
this.sandboxState = getCachedSandbox(this.sessionId);
|
|
6298
|
+
if (!this.sandboxState) {
|
|
6299
|
+
throw new Error(`No active sandbox found for session ${this.sessionId}. Sandbox must be created before starting file watcher.`);
|
|
6138
6300
|
}
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6301
|
+
const { sandbox } = this.sandboxState;
|
|
6302
|
+
const scriptPath = "/tmp/.file-watcher.js";
|
|
6303
|
+
const outputPath = "/tmp/.file-watcher-output.log";
|
|
6304
|
+
const writeScriptCmd = `cat > ${scriptPath} << 'WATCHER_EOF'
|
|
6305
|
+
${WATCHER_SCRIPT}
|
|
6306
|
+
WATCHER_EOF`;
|
|
6307
|
+
await sandbox.runCommand({
|
|
6308
|
+
cmd: "sh",
|
|
6309
|
+
args: ["-c", writeScriptCmd]
|
|
6310
|
+
});
|
|
6311
|
+
const startCmd = `nohup node ${scriptPath} "${this.watchPath}" > ${outputPath} 2>&1 &`;
|
|
6312
|
+
await sandbox.runCommand({
|
|
6313
|
+
cmd: "sh",
|
|
6314
|
+
args: ["-c", startCmd]
|
|
6315
|
+
});
|
|
6316
|
+
console.log(`[IN_SANDBOX_WATCHER] Started watcher in sandbox for ${this.watchPath}`);
|
|
6317
|
+
this.isRunning = true;
|
|
6318
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
6319
|
+
this.outputPollTimer = setInterval(async () => {
|
|
6320
|
+
try {
|
|
6321
|
+
await this.pollOutput(outputPath);
|
|
6322
|
+
} catch (error) {
|
|
6323
|
+
console.error("[IN_SANDBOX_WATCHER] Error polling output:", error);
|
|
6324
|
+
if (this.onError && error instanceof Error) {
|
|
6325
|
+
this.onError(error);
|
|
6326
|
+
}
|
|
6327
|
+
}
|
|
6328
|
+
}, this.outputPollIntervalMs);
|
|
6329
|
+
setTimeout(() => {
|
|
6330
|
+
this.pollOutput(outputPath).catch(console.error);
|
|
6331
|
+
}, 500);
|
|
6332
|
+
} catch (error) {
|
|
6333
|
+
console.error(`[IN_SANDBOX_WATCHER] Failed to start watcher:`, error);
|
|
6334
|
+
throw error;
|
|
6154
6335
|
}
|
|
6155
|
-
this.isWatching = false;
|
|
6156
|
-
this.previousFiles.clear();
|
|
6157
|
-
console.log(`[REMOTE_WATCHER] Stopped watching session ${this.sessionId}`);
|
|
6158
|
-
}
|
|
6159
|
-
/**
|
|
6160
|
-
* Subscribe to file change events
|
|
6161
|
-
*/
|
|
6162
|
-
subscribe(callback) {
|
|
6163
|
-
this.subscribers.add(callback);
|
|
6164
|
-
return () => this.subscribers.delete(callback);
|
|
6165
6336
|
}
|
|
6166
6337
|
/**
|
|
6167
|
-
*
|
|
6338
|
+
* Poll the output file for new events
|
|
6168
6339
|
*/
|
|
6169
|
-
|
|
6170
|
-
this.
|
|
6340
|
+
async pollOutput(outputPath) {
|
|
6341
|
+
if (!this.sandboxState?.sandbox) return;
|
|
6342
|
+
try {
|
|
6343
|
+
const result = await this.sandboxState.sandbox.runCommand({
|
|
6344
|
+
cmd: "sh",
|
|
6345
|
+
args: ["-c", `tail -c +${this.lastOutputPosition + 1} ${outputPath} 2>/dev/null || true`]
|
|
6346
|
+
});
|
|
6347
|
+
const output = await result.stdout();
|
|
6348
|
+
if (output && output.trim()) {
|
|
6349
|
+
const sizeResult = await this.sandboxState.sandbox.runCommand({
|
|
6350
|
+
cmd: "sh",
|
|
6351
|
+
args: ["-c", `stat -c%s ${outputPath} 2>/dev/null || echo 0`]
|
|
6352
|
+
});
|
|
6353
|
+
const sizeStr = await sizeResult.stdout();
|
|
6354
|
+
this.lastOutputPosition = parseInt(sizeStr.trim(), 10) || 0;
|
|
6355
|
+
this.processOutput(output);
|
|
6356
|
+
}
|
|
6357
|
+
} catch {
|
|
6358
|
+
}
|
|
6171
6359
|
}
|
|
6172
6360
|
/**
|
|
6173
|
-
*
|
|
6361
|
+
* Process output from the watcher script
|
|
6174
6362
|
*/
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6363
|
+
processOutput(stdout) {
|
|
6364
|
+
const lines = stdout.split("\n").filter(Boolean);
|
|
6365
|
+
for (const line of lines) {
|
|
6366
|
+
try {
|
|
6367
|
+
const data = JSON.parse(line);
|
|
6368
|
+
if (data.started) {
|
|
6369
|
+
console.log(`[IN_SANDBOX_WATCHER] Watcher started for ${data.path}`);
|
|
6370
|
+
if (this.onReady) {
|
|
6371
|
+
this.onReady();
|
|
6372
|
+
}
|
|
6373
|
+
} else if (data.heartbeat) {
|
|
6374
|
+
this.lastHeartbeat = new Date(data.timestamp);
|
|
6375
|
+
} else if (data.error) {
|
|
6376
|
+
console.warn(`[IN_SANDBOX_WATCHER] Error in sandbox:`, data.error);
|
|
6377
|
+
} else if (data.type && data.path) {
|
|
6378
|
+
this.emitEvent({
|
|
6379
|
+
type: data.type,
|
|
6380
|
+
relativePath: data.path.replace(/^\.\//, ""),
|
|
6381
|
+
absolutePath: data.path,
|
|
6382
|
+
basePath: this.watchPath,
|
|
6383
|
+
sessionId: this.sessionId,
|
|
6384
|
+
timestamp: new Date(data.timestamp)
|
|
6385
|
+
});
|
|
6386
|
+
}
|
|
6387
|
+
} catch {
|
|
6388
|
+
}
|
|
6389
|
+
}
|
|
6198
6390
|
}
|
|
6199
6391
|
/**
|
|
6200
|
-
*
|
|
6392
|
+
* Stop the watcher process
|
|
6201
6393
|
*/
|
|
6202
|
-
async
|
|
6203
|
-
if (!this.
|
|
6204
|
-
console.warn(`[REMOTE_WATCHER] Sandbox stopped for session ${this.sessionId}`);
|
|
6205
|
-
await this.stop();
|
|
6394
|
+
async stop() {
|
|
6395
|
+
if (!this.isRunning) {
|
|
6206
6396
|
return;
|
|
6207
6397
|
}
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
}
|
|
6212
|
-
const currentFiles = /* @__PURE__ */ new Map();
|
|
6213
|
-
for (const filePath of listResult.files) {
|
|
6214
|
-
if (this.shouldIgnore(filePath)) {
|
|
6215
|
-
continue;
|
|
6216
|
-
}
|
|
6217
|
-
currentFiles.set(filePath, { path: filePath });
|
|
6218
|
-
}
|
|
6219
|
-
this.lastPollAt = /* @__PURE__ */ new Date();
|
|
6220
|
-
this.pollCount++;
|
|
6221
|
-
if (isInitial) {
|
|
6222
|
-
this.previousFiles = currentFiles;
|
|
6223
|
-
return;
|
|
6398
|
+
if (this.outputPollTimer) {
|
|
6399
|
+
clearInterval(this.outputPollTimer);
|
|
6400
|
+
this.outputPollTimer = null;
|
|
6224
6401
|
}
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
type: "add",
|
|
6231
|
-
relativePath: filePath,
|
|
6232
|
-
absolutePath: `${this.basePath}/${filePath}`,
|
|
6233
|
-
basePath: this.basePath,
|
|
6234
|
-
sessionId: this.sessionId,
|
|
6235
|
-
fileSize: info.size,
|
|
6236
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
6402
|
+
try {
|
|
6403
|
+
if (this.sandboxState?.sandbox) {
|
|
6404
|
+
await this.sandboxState.sandbox.runCommand({
|
|
6405
|
+
cmd: "sh",
|
|
6406
|
+
args: ["-c", "pkill -f file-watcher.js 2>/dev/null || true"]
|
|
6237
6407
|
});
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
if (!currentFiles.has(filePath)) {
|
|
6242
|
-
changes.push({
|
|
6243
|
-
type: "unlink",
|
|
6244
|
-
relativePath: filePath,
|
|
6245
|
-
absolutePath: `${this.basePath}/${filePath}`,
|
|
6246
|
-
basePath: this.basePath,
|
|
6247
|
-
sessionId: this.sessionId,
|
|
6248
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
6408
|
+
await this.sandboxState.sandbox.runCommand({
|
|
6409
|
+
cmd: "sh",
|
|
6410
|
+
args: ["-c", "rm -f /tmp/.file-watcher.js /tmp/.file-watcher-output.log"]
|
|
6249
6411
|
});
|
|
6250
6412
|
}
|
|
6413
|
+
} catch {
|
|
6251
6414
|
}
|
|
6252
|
-
this.
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
}
|
|
6415
|
+
this.isRunning = false;
|
|
6416
|
+
this.sandboxState = null;
|
|
6417
|
+
this.lastOutputPosition = 0;
|
|
6418
|
+
console.log(`[IN_SANDBOX_WATCHER] Stopped watcher for session ${this.sessionId}`);
|
|
6256
6419
|
}
|
|
6257
6420
|
/**
|
|
6258
|
-
*
|
|
6421
|
+
* Subscribe to file change events
|
|
6259
6422
|
*/
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
return true;
|
|
6264
|
-
}
|
|
6265
|
-
}
|
|
6266
|
-
return false;
|
|
6423
|
+
subscribe(callback) {
|
|
6424
|
+
this.subscribers.add(callback);
|
|
6425
|
+
return () => this.subscribers.delete(callback);
|
|
6267
6426
|
}
|
|
6268
6427
|
/**
|
|
6269
|
-
*
|
|
6428
|
+
* Check if watcher is running
|
|
6270
6429
|
*/
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
6274
|
-
return regex.test(filePath);
|
|
6430
|
+
isActive() {
|
|
6431
|
+
return this.isRunning;
|
|
6275
6432
|
}
|
|
6276
6433
|
/**
|
|
6277
|
-
*
|
|
6434
|
+
* Get watcher status
|
|
6435
|
+
*/
|
|
6436
|
+
getStatus() {
|
|
6437
|
+
return {
|
|
6438
|
+
sessionId: this.sessionId,
|
|
6439
|
+
watchPath: this.watchPath,
|
|
6440
|
+
isRunning: this.isRunning,
|
|
6441
|
+
startedAt: this.startedAt,
|
|
6442
|
+
lastHeartbeat: this.lastHeartbeat
|
|
6443
|
+
};
|
|
6444
|
+
}
|
|
6445
|
+
/**
|
|
6446
|
+
* Emit event to all subscribers
|
|
6278
6447
|
*/
|
|
6279
6448
|
async emitEvent(event) {
|
|
6280
6449
|
for (const callback of this.subscribers) {
|
|
6281
6450
|
try {
|
|
6282
6451
|
await callback(event);
|
|
6283
6452
|
} catch (error) {
|
|
6284
|
-
console.error("[
|
|
6453
|
+
console.error("[IN_SANDBOX_WATCHER] Error in subscriber callback:", error);
|
|
6285
6454
|
}
|
|
6286
6455
|
}
|
|
6287
6456
|
}
|
|
6288
6457
|
};
|
|
6289
|
-
exports.
|
|
6458
|
+
exports.InSandboxWatcherManager = class {
|
|
6290
6459
|
watchers = /* @__PURE__ */ new Map();
|
|
6291
6460
|
/**
|
|
6292
|
-
* Start watching a
|
|
6461
|
+
* Start watching a sandbox
|
|
6293
6462
|
*/
|
|
6294
6463
|
async startWatching(options) {
|
|
6295
6464
|
const { sessionId } = options;
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
await existing.stop();
|
|
6299
|
-
}
|
|
6300
|
-
const watcher = new exports.RemoteSandboxFileWatcher(options);
|
|
6465
|
+
await this.stopWatching(sessionId);
|
|
6466
|
+
const watcher = new exports.InSandboxWatcher(options);
|
|
6301
6467
|
await watcher.start();
|
|
6302
6468
|
this.watchers.set(sessionId, watcher);
|
|
6303
6469
|
return watcher;
|
|
@@ -6313,17 +6479,16 @@ var init_sandbox_file_watcher = __esm({
|
|
|
6313
6479
|
}
|
|
6314
6480
|
}
|
|
6315
6481
|
/**
|
|
6316
|
-
* Get
|
|
6482
|
+
* Get watcher for a session
|
|
6317
6483
|
*/
|
|
6318
6484
|
getWatcher(sessionId) {
|
|
6319
6485
|
return this.watchers.get(sessionId);
|
|
6320
6486
|
}
|
|
6321
6487
|
/**
|
|
6322
|
-
* Check if
|
|
6488
|
+
* Check if watching
|
|
6323
6489
|
*/
|
|
6324
6490
|
isWatching(sessionId) {
|
|
6325
|
-
|
|
6326
|
-
return watcher?.isActive() ?? false;
|
|
6491
|
+
return this.watchers.get(sessionId)?.isActive() ?? false;
|
|
6327
6492
|
}
|
|
6328
6493
|
/**
|
|
6329
6494
|
* Stop all watchers
|
|
@@ -6334,7 +6499,7 @@ var init_sandbox_file_watcher = __esm({
|
|
|
6334
6499
|
this.watchers.clear();
|
|
6335
6500
|
}
|
|
6336
6501
|
};
|
|
6337
|
-
|
|
6502
|
+
globalInSandboxManager = null;
|
|
6338
6503
|
}
|
|
6339
6504
|
});
|
|
6340
6505
|
function extractErrorMessage(error) {
|
|
@@ -6383,6 +6548,7 @@ exports.SandboxFileSync = void 0;
|
|
|
6383
6548
|
var init_sandbox_file_sync = __esm({
|
|
6384
6549
|
"src/runtime/sandbox-file-sync.ts"() {
|
|
6385
6550
|
init_sandbox_file_watcher();
|
|
6551
|
+
init_in_sandbox_watcher();
|
|
6386
6552
|
init_types();
|
|
6387
6553
|
exports.SandboxFileSync = class {
|
|
6388
6554
|
fileStore;
|
|
@@ -6393,7 +6559,7 @@ var init_sandbox_file_sync = __esm({
|
|
|
6393
6559
|
eventStorage;
|
|
6394
6560
|
webhookConfig;
|
|
6395
6561
|
// Watcher management
|
|
6396
|
-
|
|
6562
|
+
inSandboxWatchers = /* @__PURE__ */ new Map();
|
|
6397
6563
|
localWatchers = /* @__PURE__ */ new Map();
|
|
6398
6564
|
fileChangeSubscribers = /* @__PURE__ */ new Set();
|
|
6399
6565
|
// Sequence number cache per session (for event storage)
|
|
@@ -7184,34 +7350,27 @@ var init_sandbox_file_sync = __esm({
|
|
|
7184
7350
|
this.localWatchers.set(sessionId, watcher);
|
|
7185
7351
|
console.log(`[FILE_SYNC] Started local file watching for session ${sessionId} at ${opts.localPath}`);
|
|
7186
7352
|
} else {
|
|
7187
|
-
|
|
7188
|
-
throw new Error("Sandbox operations not configured. Call setSandboxOperations first.");
|
|
7189
|
-
}
|
|
7190
|
-
const watcher = new exports.RemoteSandboxFileWatcher({
|
|
7353
|
+
const watcher = new exports.InSandboxWatcher({
|
|
7191
7354
|
sessionId,
|
|
7192
|
-
|
|
7193
|
-
basePath: watchPath,
|
|
7194
|
-
// Use watchPath instead of sandboxBasePath
|
|
7195
|
-
pollIntervalMs: opts.pollIntervalMs ?? 2e3,
|
|
7196
|
-
ignored: opts.ignored ?? ["**/node_modules/**", "**/.git/**"],
|
|
7355
|
+
watchPath,
|
|
7197
7356
|
onFileChange: handleFileChange,
|
|
7198
7357
|
onError: (error) => {
|
|
7199
|
-
console.error(`[FILE_SYNC]
|
|
7358
|
+
console.error(`[FILE_SYNC] In-sandbox watcher error for session ${sessionId}:`, error);
|
|
7200
7359
|
}
|
|
7201
7360
|
});
|
|
7202
7361
|
await watcher.start();
|
|
7203
|
-
this.
|
|
7204
|
-
console.log(`[FILE_SYNC] Started
|
|
7362
|
+
this.inSandboxWatchers.set(sessionId, watcher);
|
|
7363
|
+
console.log(`[FILE_SYNC] Started in-sandbox file watching for session ${sessionId} at path: ${watchPath}`);
|
|
7205
7364
|
}
|
|
7206
7365
|
}
|
|
7207
7366
|
/**
|
|
7208
7367
|
* Stop watching a session's sandbox
|
|
7209
7368
|
*/
|
|
7210
7369
|
async stopWatching(sessionId) {
|
|
7211
|
-
const
|
|
7212
|
-
if (
|
|
7213
|
-
await
|
|
7214
|
-
this.
|
|
7370
|
+
const inSandboxWatcher = this.inSandboxWatchers.get(sessionId);
|
|
7371
|
+
if (inSandboxWatcher) {
|
|
7372
|
+
await inSandboxWatcher.stop();
|
|
7373
|
+
this.inSandboxWatchers.delete(sessionId);
|
|
7215
7374
|
}
|
|
7216
7375
|
const localWatcher = this.localWatchers.get(sessionId);
|
|
7217
7376
|
if (localWatcher) {
|
|
@@ -7223,7 +7382,7 @@ var init_sandbox_file_sync = __esm({
|
|
|
7223
7382
|
* Check if a session is being watched
|
|
7224
7383
|
*/
|
|
7225
7384
|
isWatching(sessionId) {
|
|
7226
|
-
return this.
|
|
7385
|
+
return this.inSandboxWatchers.has(sessionId) || this.localWatchers.has(sessionId);
|
|
7227
7386
|
}
|
|
7228
7387
|
/**
|
|
7229
7388
|
* Subscribe to file change events across all watched sessions.
|
|
@@ -7257,10 +7416,10 @@ var init_sandbox_file_sync = __esm({
|
|
|
7257
7416
|
* Stop all watchers and cleanup
|
|
7258
7417
|
*/
|
|
7259
7418
|
async stopAllWatching() {
|
|
7260
|
-
const
|
|
7419
|
+
const inSandboxPromises = Array.from(this.inSandboxWatchers.values()).map((w) => w.stop());
|
|
7261
7420
|
const localPromises = Array.from(this.localWatchers.values()).map((w) => w.stop());
|
|
7262
|
-
await Promise.all([...
|
|
7263
|
-
this.
|
|
7421
|
+
await Promise.all([...inSandboxPromises, ...localPromises]);
|
|
7422
|
+
this.inSandboxWatchers.clear();
|
|
7264
7423
|
this.localWatchers.clear();
|
|
7265
7424
|
}
|
|
7266
7425
|
/**
|
|
@@ -7268,8 +7427,8 @@ var init_sandbox_file_sync = __esm({
|
|
|
7268
7427
|
*/
|
|
7269
7428
|
getWatchingStatus() {
|
|
7270
7429
|
const statuses = [];
|
|
7271
|
-
for (const [sessionId, watcher] of this.
|
|
7272
|
-
statuses.push({ sessionId, type: "
|
|
7430
|
+
for (const [sessionId, watcher] of this.inSandboxWatchers) {
|
|
7431
|
+
statuses.push({ sessionId, type: "in-sandbox", isActive: watcher.isActive() });
|
|
7273
7432
|
}
|
|
7274
7433
|
for (const [sessionId, watcher] of this.localWatchers) {
|
|
7275
7434
|
statuses.push({ sessionId, type: "local", isActive: watcher.isActive() });
|
|
@@ -7280,364 +7439,6 @@ var init_sandbox_file_sync = __esm({
|
|
|
7280
7439
|
}
|
|
7281
7440
|
});
|
|
7282
7441
|
|
|
7283
|
-
// src/runtime/in-sandbox-watcher.ts
|
|
7284
|
-
function createInSandboxWatcher(options) {
|
|
7285
|
-
return new exports.InSandboxWatcher(options);
|
|
7286
|
-
}
|
|
7287
|
-
function getInSandboxWatcherManager() {
|
|
7288
|
-
if (!globalInSandboxManager) {
|
|
7289
|
-
globalInSandboxManager = new exports.InSandboxWatcherManager();
|
|
7290
|
-
}
|
|
7291
|
-
return globalInSandboxManager;
|
|
7292
|
-
}
|
|
7293
|
-
function createInSandboxWatcherManager() {
|
|
7294
|
-
return new exports.InSandboxWatcherManager();
|
|
7295
|
-
}
|
|
7296
|
-
var WATCHER_SCRIPT; exports.InSandboxWatcher = void 0; exports.InSandboxWatcherManager = void 0; var globalInSandboxManager;
|
|
7297
|
-
var init_in_sandbox_watcher = __esm({
|
|
7298
|
-
"src/runtime/in-sandbox-watcher.ts"() {
|
|
7299
|
-
init_vercel_sandbox_executor();
|
|
7300
|
-
WATCHER_SCRIPT = `
|
|
7301
|
-
const fs = require('fs');
|
|
7302
|
-
const path = require('path');
|
|
7303
|
-
|
|
7304
|
-
const watchPath = process.argv[2] || '.';
|
|
7305
|
-
const ignored = new Set(['node_modules', '.git', '.cache', '__pycache__']);
|
|
7306
|
-
|
|
7307
|
-
// Track all watchers for cleanup
|
|
7308
|
-
const watchers = new Map();
|
|
7309
|
-
|
|
7310
|
-
// Debounce map to coalesce rapid changes
|
|
7311
|
-
const pending = new Map();
|
|
7312
|
-
const DEBOUNCE_MS = 100;
|
|
7313
|
-
|
|
7314
|
-
function emit(type, filePath) {
|
|
7315
|
-
const event = {
|
|
7316
|
-
type,
|
|
7317
|
-
path: filePath,
|
|
7318
|
-
timestamp: Date.now()
|
|
7319
|
-
};
|
|
7320
|
-
console.log(JSON.stringify(event));
|
|
7321
|
-
}
|
|
7322
|
-
|
|
7323
|
-
function shouldIgnore(name) {
|
|
7324
|
-
return ignored.has(name) || name.startsWith('.');
|
|
7325
|
-
}
|
|
7326
|
-
|
|
7327
|
-
function watchDir(dir) {
|
|
7328
|
-
try {
|
|
7329
|
-
const watcher = fs.watch(dir, { persistent: true }, (eventType, filename) => {
|
|
7330
|
-
if (!filename || shouldIgnore(filename)) return;
|
|
7331
|
-
|
|
7332
|
-
const fullPath = path.join(dir, filename);
|
|
7333
|
-
const key = fullPath;
|
|
7334
|
-
|
|
7335
|
-
// Debounce
|
|
7336
|
-
if (pending.has(key)) {
|
|
7337
|
-
clearTimeout(pending.get(key));
|
|
7338
|
-
}
|
|
7339
|
-
|
|
7340
|
-
pending.set(key, setTimeout(() => {
|
|
7341
|
-
pending.delete(key);
|
|
7342
|
-
|
|
7343
|
-
fs.stat(fullPath, (err, stats) => {
|
|
7344
|
-
if (err) {
|
|
7345
|
-
if (err.code === 'ENOENT') {
|
|
7346
|
-
emit('unlink', fullPath);
|
|
7347
|
-
// Stop watching if it was a directory
|
|
7348
|
-
if (watchers.has(fullPath)) {
|
|
7349
|
-
watchers.get(fullPath).close();
|
|
7350
|
-
watchers.delete(fullPath);
|
|
7351
|
-
}
|
|
7352
|
-
}
|
|
7353
|
-
} else {
|
|
7354
|
-
const type = eventType === 'rename' ? 'add' : 'change';
|
|
7355
|
-
emit(type, fullPath);
|
|
7356
|
-
|
|
7357
|
-
// If it's a new directory, start watching it
|
|
7358
|
-
if (stats.isDirectory() && !watchers.has(fullPath)) {
|
|
7359
|
-
watchDir(fullPath);
|
|
7360
|
-
}
|
|
7361
|
-
}
|
|
7362
|
-
});
|
|
7363
|
-
}, DEBOUNCE_MS));
|
|
7364
|
-
});
|
|
7365
|
-
|
|
7366
|
-
watchers.set(dir, watcher);
|
|
7367
|
-
|
|
7368
|
-
// Watch subdirectories
|
|
7369
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
7370
|
-
for (const entry of entries) {
|
|
7371
|
-
if (entry.isDirectory() && !shouldIgnore(entry.name)) {
|
|
7372
|
-
watchDir(path.join(dir, entry.name));
|
|
7373
|
-
}
|
|
7374
|
-
}
|
|
7375
|
-
} catch (err) {
|
|
7376
|
-
// Directory may not exist or be inaccessible
|
|
7377
|
-
console.error(JSON.stringify({ error: err.message, dir }));
|
|
7378
|
-
}
|
|
7379
|
-
}
|
|
7380
|
-
|
|
7381
|
-
// Start watching
|
|
7382
|
-
watchDir(watchPath);
|
|
7383
|
-
|
|
7384
|
-
// Keep alive
|
|
7385
|
-
process.on('SIGTERM', () => {
|
|
7386
|
-
for (const watcher of watchers.values()) {
|
|
7387
|
-
watcher.close();
|
|
7388
|
-
}
|
|
7389
|
-
process.exit(0);
|
|
7390
|
-
});
|
|
7391
|
-
|
|
7392
|
-
// Heartbeat to indicate we're running
|
|
7393
|
-
setInterval(() => {
|
|
7394
|
-
console.log(JSON.stringify({ heartbeat: true, timestamp: Date.now() }));
|
|
7395
|
-
}, 5000);
|
|
7396
|
-
|
|
7397
|
-
console.log(JSON.stringify({ started: true, path: watchPath }));
|
|
7398
|
-
`;
|
|
7399
|
-
exports.InSandboxWatcher = class {
|
|
7400
|
-
sessionId;
|
|
7401
|
-
watchPath;
|
|
7402
|
-
outputPollIntervalMs;
|
|
7403
|
-
sandboxState = null;
|
|
7404
|
-
outputPollTimer = null;
|
|
7405
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
7406
|
-
isRunning = false;
|
|
7407
|
-
startedAt;
|
|
7408
|
-
lastHeartbeat;
|
|
7409
|
-
lastOutputPosition = 0;
|
|
7410
|
-
onError;
|
|
7411
|
-
onReady;
|
|
7412
|
-
constructor(options) {
|
|
7413
|
-
this.sessionId = options.sessionId;
|
|
7414
|
-
this.watchPath = options.watchPath;
|
|
7415
|
-
this.outputPollIntervalMs = options.outputPollIntervalMs ?? 1e3;
|
|
7416
|
-
this.onError = options.onError;
|
|
7417
|
-
this.onReady = options.onReady;
|
|
7418
|
-
if (options.onFileChange) {
|
|
7419
|
-
this.subscribers.add(options.onFileChange);
|
|
7420
|
-
}
|
|
7421
|
-
}
|
|
7422
|
-
/**
|
|
7423
|
-
* Start the watcher process inside the sandbox
|
|
7424
|
-
*/
|
|
7425
|
-
async start() {
|
|
7426
|
-
if (this.isRunning) {
|
|
7427
|
-
console.warn(`[IN_SANDBOX_WATCHER] Already running for session ${this.sessionId}`);
|
|
7428
|
-
return;
|
|
7429
|
-
}
|
|
7430
|
-
try {
|
|
7431
|
-
this.sandboxState = await getOrCreateSandbox({
|
|
7432
|
-
sessionId: this.sessionId,
|
|
7433
|
-
runtime: "node22",
|
|
7434
|
-
timeout: 600
|
|
7435
|
-
});
|
|
7436
|
-
const { sandbox } = this.sandboxState;
|
|
7437
|
-
const scriptPath = "/tmp/.file-watcher.js";
|
|
7438
|
-
const outputPath = "/tmp/.file-watcher-output.log";
|
|
7439
|
-
const writeScriptCmd = `cat > ${scriptPath} << 'WATCHER_EOF'
|
|
7440
|
-
${WATCHER_SCRIPT}
|
|
7441
|
-
WATCHER_EOF`;
|
|
7442
|
-
await sandbox.runCommand({
|
|
7443
|
-
cmd: "sh",
|
|
7444
|
-
args: ["-c", writeScriptCmd]
|
|
7445
|
-
});
|
|
7446
|
-
const startCmd = `nohup node ${scriptPath} "${this.watchPath}" > ${outputPath} 2>&1 &`;
|
|
7447
|
-
await sandbox.runCommand({
|
|
7448
|
-
cmd: "sh",
|
|
7449
|
-
args: ["-c", startCmd]
|
|
7450
|
-
});
|
|
7451
|
-
console.log(`[IN_SANDBOX_WATCHER] Started watcher in sandbox for ${this.watchPath}`);
|
|
7452
|
-
this.isRunning = true;
|
|
7453
|
-
this.startedAt = /* @__PURE__ */ new Date();
|
|
7454
|
-
this.outputPollTimer = setInterval(async () => {
|
|
7455
|
-
try {
|
|
7456
|
-
await this.pollOutput(outputPath);
|
|
7457
|
-
} catch (error) {
|
|
7458
|
-
console.error("[IN_SANDBOX_WATCHER] Error polling output:", error);
|
|
7459
|
-
if (this.onError && error instanceof Error) {
|
|
7460
|
-
this.onError(error);
|
|
7461
|
-
}
|
|
7462
|
-
}
|
|
7463
|
-
}, this.outputPollIntervalMs);
|
|
7464
|
-
setTimeout(() => {
|
|
7465
|
-
this.pollOutput(outputPath).catch(console.error);
|
|
7466
|
-
}, 500);
|
|
7467
|
-
} catch (error) {
|
|
7468
|
-
console.error(`[IN_SANDBOX_WATCHER] Failed to start watcher:`, error);
|
|
7469
|
-
throw error;
|
|
7470
|
-
}
|
|
7471
|
-
}
|
|
7472
|
-
/**
|
|
7473
|
-
* Poll the output file for new events
|
|
7474
|
-
*/
|
|
7475
|
-
async pollOutput(outputPath) {
|
|
7476
|
-
if (!this.sandboxState?.sandbox) return;
|
|
7477
|
-
try {
|
|
7478
|
-
const result = await this.sandboxState.sandbox.runCommand({
|
|
7479
|
-
cmd: "sh",
|
|
7480
|
-
args: ["-c", `tail -c +${this.lastOutputPosition + 1} ${outputPath} 2>/dev/null || true`]
|
|
7481
|
-
});
|
|
7482
|
-
const output = await result.stdout();
|
|
7483
|
-
if (output && output.trim()) {
|
|
7484
|
-
const sizeResult = await this.sandboxState.sandbox.runCommand({
|
|
7485
|
-
cmd: "sh",
|
|
7486
|
-
args: ["-c", `stat -c%s ${outputPath} 2>/dev/null || echo 0`]
|
|
7487
|
-
});
|
|
7488
|
-
const sizeStr = await sizeResult.stdout();
|
|
7489
|
-
this.lastOutputPosition = parseInt(sizeStr.trim(), 10) || 0;
|
|
7490
|
-
this.processOutput(output);
|
|
7491
|
-
}
|
|
7492
|
-
} catch {
|
|
7493
|
-
}
|
|
7494
|
-
}
|
|
7495
|
-
/**
|
|
7496
|
-
* Process output from the watcher script
|
|
7497
|
-
*/
|
|
7498
|
-
processOutput(stdout) {
|
|
7499
|
-
const lines = stdout.split("\n").filter(Boolean);
|
|
7500
|
-
for (const line of lines) {
|
|
7501
|
-
try {
|
|
7502
|
-
const data = JSON.parse(line);
|
|
7503
|
-
if (data.started) {
|
|
7504
|
-
console.log(`[IN_SANDBOX_WATCHER] Watcher started for ${data.path}`);
|
|
7505
|
-
if (this.onReady) {
|
|
7506
|
-
this.onReady();
|
|
7507
|
-
}
|
|
7508
|
-
} else if (data.heartbeat) {
|
|
7509
|
-
this.lastHeartbeat = new Date(data.timestamp);
|
|
7510
|
-
} else if (data.error) {
|
|
7511
|
-
console.warn(`[IN_SANDBOX_WATCHER] Error in sandbox:`, data.error);
|
|
7512
|
-
} else if (data.type && data.path) {
|
|
7513
|
-
this.emitEvent({
|
|
7514
|
-
type: data.type,
|
|
7515
|
-
relativePath: data.path.replace(/^\.\//, ""),
|
|
7516
|
-
absolutePath: data.path,
|
|
7517
|
-
basePath: this.watchPath,
|
|
7518
|
-
sessionId: this.sessionId,
|
|
7519
|
-
timestamp: new Date(data.timestamp)
|
|
7520
|
-
});
|
|
7521
|
-
}
|
|
7522
|
-
} catch {
|
|
7523
|
-
}
|
|
7524
|
-
}
|
|
7525
|
-
}
|
|
7526
|
-
/**
|
|
7527
|
-
* Stop the watcher process
|
|
7528
|
-
*/
|
|
7529
|
-
async stop() {
|
|
7530
|
-
if (!this.isRunning) {
|
|
7531
|
-
return;
|
|
7532
|
-
}
|
|
7533
|
-
if (this.outputPollTimer) {
|
|
7534
|
-
clearInterval(this.outputPollTimer);
|
|
7535
|
-
this.outputPollTimer = null;
|
|
7536
|
-
}
|
|
7537
|
-
try {
|
|
7538
|
-
if (this.sandboxState?.sandbox) {
|
|
7539
|
-
await this.sandboxState.sandbox.runCommand({
|
|
7540
|
-
cmd: "sh",
|
|
7541
|
-
args: ["-c", "pkill -f file-watcher.js 2>/dev/null || true"]
|
|
7542
|
-
});
|
|
7543
|
-
await this.sandboxState.sandbox.runCommand({
|
|
7544
|
-
cmd: "sh",
|
|
7545
|
-
args: ["-c", "rm -f /tmp/.file-watcher.js /tmp/.file-watcher-output.log"]
|
|
7546
|
-
});
|
|
7547
|
-
}
|
|
7548
|
-
} catch {
|
|
7549
|
-
}
|
|
7550
|
-
this.isRunning = false;
|
|
7551
|
-
this.sandboxState = null;
|
|
7552
|
-
this.lastOutputPosition = 0;
|
|
7553
|
-
console.log(`[IN_SANDBOX_WATCHER] Stopped watcher for session ${this.sessionId}`);
|
|
7554
|
-
}
|
|
7555
|
-
/**
|
|
7556
|
-
* Subscribe to file change events
|
|
7557
|
-
*/
|
|
7558
|
-
subscribe(callback) {
|
|
7559
|
-
this.subscribers.add(callback);
|
|
7560
|
-
return () => this.subscribers.delete(callback);
|
|
7561
|
-
}
|
|
7562
|
-
/**
|
|
7563
|
-
* Check if watcher is running
|
|
7564
|
-
*/
|
|
7565
|
-
isActive() {
|
|
7566
|
-
return this.isRunning;
|
|
7567
|
-
}
|
|
7568
|
-
/**
|
|
7569
|
-
* Get watcher status
|
|
7570
|
-
*/
|
|
7571
|
-
getStatus() {
|
|
7572
|
-
return {
|
|
7573
|
-
sessionId: this.sessionId,
|
|
7574
|
-
watchPath: this.watchPath,
|
|
7575
|
-
isRunning: this.isRunning,
|
|
7576
|
-
startedAt: this.startedAt,
|
|
7577
|
-
lastHeartbeat: this.lastHeartbeat
|
|
7578
|
-
};
|
|
7579
|
-
}
|
|
7580
|
-
/**
|
|
7581
|
-
* Emit event to all subscribers
|
|
7582
|
-
*/
|
|
7583
|
-
async emitEvent(event) {
|
|
7584
|
-
for (const callback of this.subscribers) {
|
|
7585
|
-
try {
|
|
7586
|
-
await callback(event);
|
|
7587
|
-
} catch (error) {
|
|
7588
|
-
console.error("[IN_SANDBOX_WATCHER] Error in subscriber callback:", error);
|
|
7589
|
-
}
|
|
7590
|
-
}
|
|
7591
|
-
}
|
|
7592
|
-
};
|
|
7593
|
-
exports.InSandboxWatcherManager = class {
|
|
7594
|
-
watchers = /* @__PURE__ */ new Map();
|
|
7595
|
-
/**
|
|
7596
|
-
* Start watching a sandbox
|
|
7597
|
-
*/
|
|
7598
|
-
async startWatching(options) {
|
|
7599
|
-
const { sessionId } = options;
|
|
7600
|
-
await this.stopWatching(sessionId);
|
|
7601
|
-
const watcher = new exports.InSandboxWatcher(options);
|
|
7602
|
-
await watcher.start();
|
|
7603
|
-
this.watchers.set(sessionId, watcher);
|
|
7604
|
-
return watcher;
|
|
7605
|
-
}
|
|
7606
|
-
/**
|
|
7607
|
-
* Stop watching a session
|
|
7608
|
-
*/
|
|
7609
|
-
async stopWatching(sessionId) {
|
|
7610
|
-
const watcher = this.watchers.get(sessionId);
|
|
7611
|
-
if (watcher) {
|
|
7612
|
-
await watcher.stop();
|
|
7613
|
-
this.watchers.delete(sessionId);
|
|
7614
|
-
}
|
|
7615
|
-
}
|
|
7616
|
-
/**
|
|
7617
|
-
* Get watcher for a session
|
|
7618
|
-
*/
|
|
7619
|
-
getWatcher(sessionId) {
|
|
7620
|
-
return this.watchers.get(sessionId);
|
|
7621
|
-
}
|
|
7622
|
-
/**
|
|
7623
|
-
* Check if watching
|
|
7624
|
-
*/
|
|
7625
|
-
isWatching(sessionId) {
|
|
7626
|
-
return this.watchers.get(sessionId)?.isActive() ?? false;
|
|
7627
|
-
}
|
|
7628
|
-
/**
|
|
7629
|
-
* Stop all watchers
|
|
7630
|
-
*/
|
|
7631
|
-
async stopAll() {
|
|
7632
|
-
const promises = Array.from(this.watchers.values()).map((w) => w.stop());
|
|
7633
|
-
await Promise.all(promises);
|
|
7634
|
-
this.watchers.clear();
|
|
7635
|
-
}
|
|
7636
|
-
};
|
|
7637
|
-
globalInSandboxManager = null;
|
|
7638
|
-
}
|
|
7639
|
-
});
|
|
7640
|
-
|
|
7641
7442
|
// src/runtime/index.ts
|
|
7642
7443
|
function generateDockerCommand(config) {
|
|
7643
7444
|
const args = ["docker", "run"];
|
|
@@ -19064,8 +18865,6 @@ exports.createQueueProcessor = createQueueProcessor;
|
|
|
19064
18865
|
exports.createQueueRouter = createQueueRouter;
|
|
19065
18866
|
exports.createR2BundleStore = createR2BundleStore;
|
|
19066
18867
|
exports.createR2FileStore = createR2FileStore;
|
|
19067
|
-
exports.createRemoteFileWatcher = createRemoteFileWatcher;
|
|
19068
|
-
exports.createRemoteFileWatcherManager = createRemoteFileWatcherManager;
|
|
19069
18868
|
exports.createS3BundleStore = createS3BundleStore;
|
|
19070
18869
|
exports.createS3FileStore = createS3FileStore;
|
|
19071
18870
|
exports.createSandboxFileOperations = createSandboxFileOperations;
|
|
@@ -19091,6 +18890,7 @@ exports.defineConfig = defineConfig;
|
|
|
19091
18890
|
exports.ensureSandboxPoolInitialized = ensureSandboxPoolInitialized;
|
|
19092
18891
|
exports.env = env;
|
|
19093
18892
|
exports.envOptional = envOptional;
|
|
18893
|
+
exports.executeCommandInSandbox = executeCommandInSandbox;
|
|
19094
18894
|
exports.extractTextContent = extractTextContent;
|
|
19095
18895
|
exports.extractTextFromMessage = extractTextFromMessage;
|
|
19096
18896
|
exports.formatToolName = formatToolName;
|
|
@@ -19103,12 +18903,12 @@ exports.getActionIcon = getActionIcon;
|
|
|
19103
18903
|
exports.getActionLabel = getActionLabel;
|
|
19104
18904
|
exports.getAllHeartbeatStatuses = getAllHeartbeatStatuses;
|
|
19105
18905
|
exports.getApiKeyEnvVar = getApiKeyEnvVar;
|
|
18906
|
+
exports.getCachedSandbox = getCachedSandbox;
|
|
19106
18907
|
exports.getDefaultModel = getDefaultModel;
|
|
19107
18908
|
exports.getFileWatcherManager = getFileWatcherManager;
|
|
19108
18909
|
exports.getHeartbeatStatus = getHeartbeatStatus;
|
|
19109
18910
|
exports.getInSandboxWatcherManager = getInSandboxWatcherManager;
|
|
19110
18911
|
exports.getOrCreateSandbox = getOrCreateSandbox;
|
|
19111
|
-
exports.getRemoteFileWatcherManager = getRemoteFileWatcherManager;
|
|
19112
18912
|
exports.getSandboxCacheStats = getSandboxCacheStats;
|
|
19113
18913
|
exports.getSandboxPool = getSandboxPool;
|
|
19114
18914
|
exports.getWorkspaceManager = getWorkspaceManager;
|