@ash-cloud/ash-ai 0.1.13 → 0.1.16
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 +430 -557
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -145
- package/dist/index.d.ts +35 -145
- package/dist/index.js +429 -555
- package/dist/index.js.map +1 -1
- package/dist/playground/components/ChatInput.d.ts.map +1 -1
- package/dist/playground/components/NormalizedMessageList.d.ts +4 -11
- package/dist/playground/components/NormalizedMessageList.d.ts.map +1 -1
- package/dist/playground/components/ToolCallCard.d.ts +20 -5
- package/dist/playground/components/ToolCallCard.d.ts.map +1 -1
- package/dist/playground/index.d.ts +19 -4
- package/dist/playground/index.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 +3090 -2296
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -4233,6 +4233,50 @@ VERCEL_EOF`);
|
|
|
4233
4233
|
}
|
|
4234
4234
|
});
|
|
4235
4235
|
|
|
4236
|
+
// src/runtime/providers/firecracker/types.ts
|
|
4237
|
+
var init_types3 = __esm({
|
|
4238
|
+
"src/runtime/providers/firecracker/types.ts"() {
|
|
4239
|
+
}
|
|
4240
|
+
});
|
|
4241
|
+
|
|
4242
|
+
// src/runtime/providers/firecracker/image-manager.ts
|
|
4243
|
+
var init_image_manager = __esm({
|
|
4244
|
+
"src/runtime/providers/firecracker/image-manager.ts"() {
|
|
4245
|
+
}
|
|
4246
|
+
});
|
|
4247
|
+
|
|
4248
|
+
// src/runtime/providers/firecracker/network-manager.ts
|
|
4249
|
+
var init_network_manager = __esm({
|
|
4250
|
+
"src/runtime/providers/firecracker/network-manager.ts"() {
|
|
4251
|
+
}
|
|
4252
|
+
});
|
|
4253
|
+
|
|
4254
|
+
// src/runtime/providers/firecracker/ssh-executor.ts
|
|
4255
|
+
var init_ssh_executor = __esm({
|
|
4256
|
+
"src/runtime/providers/firecracker/ssh-executor.ts"() {
|
|
4257
|
+
}
|
|
4258
|
+
});
|
|
4259
|
+
|
|
4260
|
+
// src/runtime/providers/firecracker/vm-manager.ts
|
|
4261
|
+
var init_vm_manager = __esm({
|
|
4262
|
+
"src/runtime/providers/firecracker/vm-manager.ts"() {
|
|
4263
|
+
init_types3();
|
|
4264
|
+
}
|
|
4265
|
+
});
|
|
4266
|
+
|
|
4267
|
+
// src/runtime/providers/firecracker.ts
|
|
4268
|
+
var init_firecracker = __esm({
|
|
4269
|
+
"src/runtime/providers/firecracker.ts"() {
|
|
4270
|
+
init_types2();
|
|
4271
|
+
init_types3();
|
|
4272
|
+
init_image_manager();
|
|
4273
|
+
init_network_manager();
|
|
4274
|
+
init_ssh_executor();
|
|
4275
|
+
init_vm_manager();
|
|
4276
|
+
init_types3();
|
|
4277
|
+
}
|
|
4278
|
+
});
|
|
4279
|
+
|
|
4236
4280
|
// src/runtime/providers/index.ts
|
|
4237
4281
|
var init_providers = __esm({
|
|
4238
4282
|
"src/runtime/providers/index.ts"() {
|
|
@@ -4244,6 +4288,7 @@ var init_providers = __esm({
|
|
|
4244
4288
|
init_fly();
|
|
4245
4289
|
init_daytona();
|
|
4246
4290
|
init_vercel();
|
|
4291
|
+
init_firecracker();
|
|
4247
4292
|
init_local();
|
|
4248
4293
|
init_modal();
|
|
4249
4294
|
init_e2b();
|
|
@@ -4251,6 +4296,7 @@ var init_providers = __esm({
|
|
|
4251
4296
|
init_fly();
|
|
4252
4297
|
init_daytona();
|
|
4253
4298
|
init_vercel();
|
|
4299
|
+
init_firecracker();
|
|
4254
4300
|
}
|
|
4255
4301
|
});
|
|
4256
4302
|
|
|
@@ -5289,6 +5335,23 @@ function isSandboxRunning(sessionId) {
|
|
|
5289
5335
|
const cached = sandboxCache.get(sessionId);
|
|
5290
5336
|
return cached !== void 0 && !cached.isExpired;
|
|
5291
5337
|
}
|
|
5338
|
+
function getCachedSandbox(sessionId) {
|
|
5339
|
+
const cached = sandboxCache.get(sessionId);
|
|
5340
|
+
if (!cached || cached.isExpired) {
|
|
5341
|
+
return null;
|
|
5342
|
+
}
|
|
5343
|
+
cached.lastUsedAt = Date.now();
|
|
5344
|
+
return {
|
|
5345
|
+
sandbox: cached.sandbox,
|
|
5346
|
+
sandboxId: cached.sandbox.sandboxId,
|
|
5347
|
+
sdkInstalled: cached.sdkInstalled,
|
|
5348
|
+
startupScriptRan: cached.startupScriptRan,
|
|
5349
|
+
startupScriptHash: cached.startupScriptHash,
|
|
5350
|
+
isNew: false,
|
|
5351
|
+
configFileUrl: cached.configFileUrl,
|
|
5352
|
+
configInstalledAt: cached.configInstalledAt
|
|
5353
|
+
};
|
|
5354
|
+
}
|
|
5292
5355
|
async function writeFileToSandbox(sessionId, path15, content) {
|
|
5293
5356
|
const cached = sandboxCache.get(sessionId);
|
|
5294
5357
|
if (!cached) {
|
|
@@ -5357,6 +5420,54 @@ async function readFileFromSandbox(sessionId, path15) {
|
|
|
5357
5420
|
};
|
|
5358
5421
|
}
|
|
5359
5422
|
}
|
|
5423
|
+
async function executeCommandInSandbox(sessionId, command, options) {
|
|
5424
|
+
const cached = sandboxCache.get(sessionId);
|
|
5425
|
+
if (!cached) {
|
|
5426
|
+
return { success: false, error: "No active sandbox for session" };
|
|
5427
|
+
}
|
|
5428
|
+
if (cached.isExpired) {
|
|
5429
|
+
return { success: false, error: "Sandbox has expired" };
|
|
5430
|
+
}
|
|
5431
|
+
const startTime = Date.now();
|
|
5432
|
+
try {
|
|
5433
|
+
const sandbox = cached.sandbox;
|
|
5434
|
+
let fullCommand = command;
|
|
5435
|
+
if (options?.cwd) {
|
|
5436
|
+
fullCommand = `cd ${JSON.stringify(options.cwd)} && ${command}`;
|
|
5437
|
+
}
|
|
5438
|
+
const result = await sandbox.runCommand({
|
|
5439
|
+
cmd: "bash",
|
|
5440
|
+
args: ["-c", fullCommand],
|
|
5441
|
+
env: options?.env
|
|
5442
|
+
});
|
|
5443
|
+
const stdout = await result.stdout();
|
|
5444
|
+
const stderr = await result.stderr();
|
|
5445
|
+
const durationMs = Date.now() - startTime;
|
|
5446
|
+
cached.lastUsedAt = Date.now();
|
|
5447
|
+
return {
|
|
5448
|
+
success: result.exitCode === 0,
|
|
5449
|
+
exitCode: result.exitCode,
|
|
5450
|
+
stdout,
|
|
5451
|
+
stderr,
|
|
5452
|
+
durationMs
|
|
5453
|
+
};
|
|
5454
|
+
} catch (error) {
|
|
5455
|
+
const durationMs = Date.now() - startTime;
|
|
5456
|
+
if (isSandboxExpiredError(error)) {
|
|
5457
|
+
cached.isExpired = true;
|
|
5458
|
+
return {
|
|
5459
|
+
success: false,
|
|
5460
|
+
error: "Sandbox has expired",
|
|
5461
|
+
durationMs
|
|
5462
|
+
};
|
|
5463
|
+
}
|
|
5464
|
+
return {
|
|
5465
|
+
success: false,
|
|
5466
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
5467
|
+
durationMs
|
|
5468
|
+
};
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5360
5471
|
async function listFilesInSandbox(sessionId, path15) {
|
|
5361
5472
|
const cached = sandboxCache.get(sessionId);
|
|
5362
5473
|
if (!cached) {
|
|
@@ -5836,19 +5947,7 @@ function getFileWatcherManager() {
|
|
|
5836
5947
|
function createFileWatcherManager() {
|
|
5837
5948
|
return new exports.FileWatcherManager();
|
|
5838
5949
|
}
|
|
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;
|
|
5950
|
+
exports.SandboxFileWatcher = void 0; exports.FileWatcherManager = void 0; var globalWatcherManager;
|
|
5852
5951
|
var init_sandbox_file_watcher = __esm({
|
|
5853
5952
|
"src/runtime/sandbox-file-watcher.ts"() {
|
|
5854
5953
|
exports.SandboxFileWatcher = class {
|
|
@@ -5874,7 +5973,18 @@ var init_sandbox_file_watcher = __esm({
|
|
|
5874
5973
|
this.watchPath = options.watchPath;
|
|
5875
5974
|
this.debounceMs = options.debounceMs ?? 300;
|
|
5876
5975
|
this.patterns = options.patterns ?? ["**/*"];
|
|
5877
|
-
this.ignored = options.ignored ?? [
|
|
5976
|
+
this.ignored = options.ignored ?? [
|
|
5977
|
+
"**/node_modules/**",
|
|
5978
|
+
"**/.git/**",
|
|
5979
|
+
"**/*.log",
|
|
5980
|
+
// Temporary files from atomic saves and editors
|
|
5981
|
+
"**/*.tmp",
|
|
5982
|
+
"**/*.tmp.*",
|
|
5983
|
+
"**/*~",
|
|
5984
|
+
"**/*.swp",
|
|
5985
|
+
"**/*.swo",
|
|
5986
|
+
"**/.DS_Store"
|
|
5987
|
+
];
|
|
5878
5988
|
this.emitInitialEvents = options.emitInitialEvents ?? false;
|
|
5879
5989
|
this.followSymlinks = options.followSymlinks ?? false;
|
|
5880
5990
|
this.usePolling = options.usePolling ?? false;
|
|
@@ -6090,214 +6200,343 @@ var init_sandbox_file_watcher = __esm({
|
|
|
6090
6200
|
}
|
|
6091
6201
|
};
|
|
6092
6202
|
globalWatcherManager = null;
|
|
6093
|
-
|
|
6203
|
+
}
|
|
6204
|
+
});
|
|
6205
|
+
|
|
6206
|
+
// src/runtime/in-sandbox-watcher.ts
|
|
6207
|
+
function createInSandboxWatcher(options) {
|
|
6208
|
+
return new exports.InSandboxWatcher(options);
|
|
6209
|
+
}
|
|
6210
|
+
function getInSandboxWatcherManager() {
|
|
6211
|
+
if (!globalInSandboxManager) {
|
|
6212
|
+
globalInSandboxManager = new exports.InSandboxWatcherManager();
|
|
6213
|
+
}
|
|
6214
|
+
return globalInSandboxManager;
|
|
6215
|
+
}
|
|
6216
|
+
function createInSandboxWatcherManager() {
|
|
6217
|
+
return new exports.InSandboxWatcherManager();
|
|
6218
|
+
}
|
|
6219
|
+
var WATCHER_SCRIPT; exports.InSandboxWatcher = void 0; exports.InSandboxWatcherManager = void 0; var globalInSandboxManager;
|
|
6220
|
+
var init_in_sandbox_watcher = __esm({
|
|
6221
|
+
"src/runtime/in-sandbox-watcher.ts"() {
|
|
6222
|
+
init_vercel_sandbox_executor();
|
|
6223
|
+
WATCHER_SCRIPT = `
|
|
6224
|
+
const fs = require('fs');
|
|
6225
|
+
const path = require('path');
|
|
6226
|
+
|
|
6227
|
+
const watchPath = process.argv[2] || '.';
|
|
6228
|
+
const ignoredDirs = new Set(['node_modules', '.git', '.cache', '__pycache__']);
|
|
6229
|
+
|
|
6230
|
+
// Track all watchers for cleanup
|
|
6231
|
+
const watchers = new Map();
|
|
6232
|
+
|
|
6233
|
+
// Debounce map to coalesce rapid changes
|
|
6234
|
+
const pending = new Map();
|
|
6235
|
+
const DEBOUNCE_MS = 100;
|
|
6236
|
+
|
|
6237
|
+
function emit(type, filePath) {
|
|
6238
|
+
const event = {
|
|
6239
|
+
type,
|
|
6240
|
+
path: filePath,
|
|
6241
|
+
timestamp: Date.now()
|
|
6242
|
+
};
|
|
6243
|
+
console.log(JSON.stringify(event));
|
|
6244
|
+
}
|
|
6245
|
+
|
|
6246
|
+
function shouldIgnore(name) {
|
|
6247
|
+
// Ignore hidden files/dirs (starting with .)
|
|
6248
|
+
if (name.startsWith('.')) return true;
|
|
6249
|
+
|
|
6250
|
+
// Ignore known directories
|
|
6251
|
+
if (ignoredDirs.has(name)) return true;
|
|
6252
|
+
|
|
6253
|
+
// Ignore temporary files from atomic saves and editors:
|
|
6254
|
+
// - filename.tmp (ends with .tmp)
|
|
6255
|
+
// - filename.tmp.123456789 (has .tmp. in middle - atomic write pattern)
|
|
6256
|
+
// - filename~ (editor backup files)
|
|
6257
|
+
// - filename.swp / filename.swo (Vim swap files)
|
|
6258
|
+
if (name.endsWith('.tmp')) return true;
|
|
6259
|
+
if (name.includes('.tmp.')) return true;
|
|
6260
|
+
if (name.endsWith('~')) return true;
|
|
6261
|
+
if (name.endsWith('.swp') || name.endsWith('.swo')) return true;
|
|
6262
|
+
|
|
6263
|
+
return false;
|
|
6264
|
+
}
|
|
6265
|
+
|
|
6266
|
+
function watchDir(dir) {
|
|
6267
|
+
try {
|
|
6268
|
+
const watcher = fs.watch(dir, { persistent: true }, (eventType, filename) => {
|
|
6269
|
+
if (!filename || shouldIgnore(filename)) return;
|
|
6270
|
+
|
|
6271
|
+
const fullPath = path.join(dir, filename);
|
|
6272
|
+
const key = fullPath;
|
|
6273
|
+
|
|
6274
|
+
// Debounce
|
|
6275
|
+
if (pending.has(key)) {
|
|
6276
|
+
clearTimeout(pending.get(key));
|
|
6277
|
+
}
|
|
6278
|
+
|
|
6279
|
+
pending.set(key, setTimeout(() => {
|
|
6280
|
+
pending.delete(key);
|
|
6281
|
+
|
|
6282
|
+
fs.stat(fullPath, (err, stats) => {
|
|
6283
|
+
if (err) {
|
|
6284
|
+
if (err.code === 'ENOENT') {
|
|
6285
|
+
emit('unlink', fullPath);
|
|
6286
|
+
// Stop watching if it was a directory
|
|
6287
|
+
if (watchers.has(fullPath)) {
|
|
6288
|
+
watchers.get(fullPath).close();
|
|
6289
|
+
watchers.delete(fullPath);
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
6292
|
+
} else {
|
|
6293
|
+
const type = eventType === 'rename' ? 'add' : 'change';
|
|
6294
|
+
emit(type, fullPath);
|
|
6295
|
+
|
|
6296
|
+
// If it's a new directory, start watching it
|
|
6297
|
+
if (stats.isDirectory() && !watchers.has(fullPath)) {
|
|
6298
|
+
watchDir(fullPath);
|
|
6299
|
+
}
|
|
6300
|
+
}
|
|
6301
|
+
});
|
|
6302
|
+
}, DEBOUNCE_MS));
|
|
6303
|
+
});
|
|
6304
|
+
|
|
6305
|
+
watchers.set(dir, watcher);
|
|
6306
|
+
|
|
6307
|
+
// Watch subdirectories
|
|
6308
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
6309
|
+
for (const entry of entries) {
|
|
6310
|
+
if (entry.isDirectory() && !shouldIgnore(entry.name)) {
|
|
6311
|
+
watchDir(path.join(dir, entry.name));
|
|
6312
|
+
}
|
|
6313
|
+
}
|
|
6314
|
+
} catch (err) {
|
|
6315
|
+
// Directory may not exist or be inaccessible
|
|
6316
|
+
console.error(JSON.stringify({ error: err.message, dir }));
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
6319
|
+
|
|
6320
|
+
// Start watching
|
|
6321
|
+
watchDir(watchPath);
|
|
6322
|
+
|
|
6323
|
+
// Keep alive
|
|
6324
|
+
process.on('SIGTERM', () => {
|
|
6325
|
+
for (const watcher of watchers.values()) {
|
|
6326
|
+
watcher.close();
|
|
6327
|
+
}
|
|
6328
|
+
process.exit(0);
|
|
6329
|
+
});
|
|
6330
|
+
|
|
6331
|
+
// Heartbeat to indicate we're running
|
|
6332
|
+
setInterval(() => {
|
|
6333
|
+
console.log(JSON.stringify({ heartbeat: true, timestamp: Date.now() }));
|
|
6334
|
+
}, 5000);
|
|
6335
|
+
|
|
6336
|
+
console.log(JSON.stringify({ started: true, path: watchPath }));
|
|
6337
|
+
`;
|
|
6338
|
+
exports.InSandboxWatcher = class {
|
|
6094
6339
|
sessionId;
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
pollTimer = null;
|
|
6100
|
-
previousFiles = /* @__PURE__ */ new Map();
|
|
6340
|
+
watchPath;
|
|
6341
|
+
outputPollIntervalMs;
|
|
6342
|
+
sandboxState = null;
|
|
6343
|
+
outputPollTimer = null;
|
|
6101
6344
|
subscribers = /* @__PURE__ */ new Set();
|
|
6102
|
-
|
|
6345
|
+
isRunning = false;
|
|
6103
6346
|
startedAt;
|
|
6104
|
-
|
|
6105
|
-
|
|
6347
|
+
lastHeartbeat;
|
|
6348
|
+
lastOutputPosition = 0;
|
|
6106
6349
|
onError;
|
|
6350
|
+
onReady;
|
|
6107
6351
|
constructor(options) {
|
|
6108
6352
|
this.sessionId = options.sessionId;
|
|
6109
|
-
this.
|
|
6110
|
-
this.
|
|
6111
|
-
this.pollIntervalMs = options.pollIntervalMs ?? 2e3;
|
|
6112
|
-
this.ignored = options.ignored ?? ["**/node_modules/**", "**/.git/**"];
|
|
6353
|
+
this.watchPath = options.watchPath;
|
|
6354
|
+
this.outputPollIntervalMs = options.outputPollIntervalMs ?? 1e3;
|
|
6113
6355
|
this.onError = options.onError;
|
|
6356
|
+
this.onReady = options.onReady;
|
|
6114
6357
|
if (options.onFileChange) {
|
|
6115
6358
|
this.subscribers.add(options.onFileChange);
|
|
6116
6359
|
}
|
|
6117
6360
|
}
|
|
6118
6361
|
/**
|
|
6119
|
-
* Start
|
|
6362
|
+
* Start the watcher process inside the sandbox
|
|
6120
6363
|
*/
|
|
6121
6364
|
async start() {
|
|
6122
|
-
if (this.
|
|
6123
|
-
console.warn(`[
|
|
6365
|
+
if (this.isRunning) {
|
|
6366
|
+
console.warn(`[IN_SANDBOX_WATCHER] Already running for session ${this.sessionId}`);
|
|
6124
6367
|
return;
|
|
6125
6368
|
}
|
|
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
|
-
}
|
|
6369
|
+
try {
|
|
6370
|
+
this.sandboxState = getCachedSandbox(this.sessionId);
|
|
6371
|
+
if (!this.sandboxState) {
|
|
6372
|
+
throw new Error(`No active sandbox found for session ${this.sessionId}. Sandbox must be created before starting file watcher.`);
|
|
6138
6373
|
}
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6374
|
+
const { sandbox } = this.sandboxState;
|
|
6375
|
+
const scriptPath = "/tmp/.file-watcher.js";
|
|
6376
|
+
const outputPath = "/tmp/.file-watcher-output.log";
|
|
6377
|
+
const writeScriptCmd = `cat > ${scriptPath} << 'WATCHER_EOF'
|
|
6378
|
+
${WATCHER_SCRIPT}
|
|
6379
|
+
WATCHER_EOF`;
|
|
6380
|
+
await sandbox.runCommand({
|
|
6381
|
+
cmd: "sh",
|
|
6382
|
+
args: ["-c", writeScriptCmd]
|
|
6383
|
+
});
|
|
6384
|
+
const startCmd = `nohup node ${scriptPath} "${this.watchPath}" > ${outputPath} 2>&1 &`;
|
|
6385
|
+
await sandbox.runCommand({
|
|
6386
|
+
cmd: "sh",
|
|
6387
|
+
args: ["-c", startCmd]
|
|
6388
|
+
});
|
|
6389
|
+
console.log(`[IN_SANDBOX_WATCHER] Started watcher in sandbox for ${this.watchPath}`);
|
|
6390
|
+
this.isRunning = true;
|
|
6391
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
6392
|
+
this.outputPollTimer = setInterval(async () => {
|
|
6393
|
+
try {
|
|
6394
|
+
await this.pollOutput(outputPath);
|
|
6395
|
+
} catch (error) {
|
|
6396
|
+
console.error("[IN_SANDBOX_WATCHER] Error polling output:", error);
|
|
6397
|
+
if (this.onError && error instanceof Error) {
|
|
6398
|
+
this.onError(error);
|
|
6399
|
+
}
|
|
6400
|
+
}
|
|
6401
|
+
}, this.outputPollIntervalMs);
|
|
6402
|
+
setTimeout(() => {
|
|
6403
|
+
this.pollOutput(outputPath).catch(console.error);
|
|
6404
|
+
}, 500);
|
|
6405
|
+
} catch (error) {
|
|
6406
|
+
console.error(`[IN_SANDBOX_WATCHER] Failed to start watcher:`, error);
|
|
6407
|
+
throw error;
|
|
6154
6408
|
}
|
|
6155
|
-
this.isWatching = false;
|
|
6156
|
-
this.previousFiles.clear();
|
|
6157
|
-
console.log(`[REMOTE_WATCHER] Stopped watching session ${this.sessionId}`);
|
|
6158
6409
|
}
|
|
6159
6410
|
/**
|
|
6160
|
-
*
|
|
6411
|
+
* Poll the output file for new events
|
|
6161
6412
|
*/
|
|
6162
|
-
|
|
6163
|
-
this.
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
getStatus() {
|
|
6182
|
-
return {
|
|
6183
|
-
sessionId: this.sessionId,
|
|
6184
|
-
watchPath: this.basePath,
|
|
6185
|
-
isWatching: this.isWatching,
|
|
6186
|
-
watchedFileCount: this.previousFiles.size,
|
|
6187
|
-
pendingEventCount: 0,
|
|
6188
|
-
startedAt: this.startedAt,
|
|
6189
|
-
lastPollAt: this.lastPollAt,
|
|
6190
|
-
pollCount: this.pollCount
|
|
6191
|
-
};
|
|
6413
|
+
async pollOutput(outputPath) {
|
|
6414
|
+
if (!this.sandboxState?.sandbox) return;
|
|
6415
|
+
try {
|
|
6416
|
+
const result = await this.sandboxState.sandbox.runCommand({
|
|
6417
|
+
cmd: "sh",
|
|
6418
|
+
args: ["-c", `tail -c +${this.lastOutputPosition + 1} ${outputPath} 2>/dev/null || true`]
|
|
6419
|
+
});
|
|
6420
|
+
const output = await result.stdout();
|
|
6421
|
+
if (output && output.trim()) {
|
|
6422
|
+
const sizeResult = await this.sandboxState.sandbox.runCommand({
|
|
6423
|
+
cmd: "sh",
|
|
6424
|
+
args: ["-c", `stat -c%s ${outputPath} 2>/dev/null || echo 0`]
|
|
6425
|
+
});
|
|
6426
|
+
const sizeStr = await sizeResult.stdout();
|
|
6427
|
+
this.lastOutputPosition = parseInt(sizeStr.trim(), 10) || 0;
|
|
6428
|
+
this.processOutput(output);
|
|
6429
|
+
}
|
|
6430
|
+
} catch {
|
|
6431
|
+
}
|
|
6192
6432
|
}
|
|
6193
6433
|
/**
|
|
6194
|
-
*
|
|
6434
|
+
* Process output from the watcher script
|
|
6195
6435
|
*/
|
|
6196
|
-
|
|
6197
|
-
|
|
6436
|
+
processOutput(stdout) {
|
|
6437
|
+
const lines = stdout.split("\n").filter(Boolean);
|
|
6438
|
+
for (const line of lines) {
|
|
6439
|
+
try {
|
|
6440
|
+
const data = JSON.parse(line);
|
|
6441
|
+
if (data.started) {
|
|
6442
|
+
console.log(`[IN_SANDBOX_WATCHER] Watcher started for ${data.path}`);
|
|
6443
|
+
if (this.onReady) {
|
|
6444
|
+
this.onReady();
|
|
6445
|
+
}
|
|
6446
|
+
} else if (data.heartbeat) {
|
|
6447
|
+
this.lastHeartbeat = new Date(data.timestamp);
|
|
6448
|
+
} else if (data.error) {
|
|
6449
|
+
console.warn(`[IN_SANDBOX_WATCHER] Error in sandbox:`, data.error);
|
|
6450
|
+
} else if (data.type && data.path) {
|
|
6451
|
+
this.emitEvent({
|
|
6452
|
+
type: data.type,
|
|
6453
|
+
relativePath: data.path.replace(/^\.\//, ""),
|
|
6454
|
+
absolutePath: data.path,
|
|
6455
|
+
basePath: this.watchPath,
|
|
6456
|
+
sessionId: this.sessionId,
|
|
6457
|
+
timestamp: new Date(data.timestamp)
|
|
6458
|
+
});
|
|
6459
|
+
}
|
|
6460
|
+
} catch {
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6198
6463
|
}
|
|
6199
6464
|
/**
|
|
6200
|
-
*
|
|
6465
|
+
* Stop the watcher process
|
|
6201
6466
|
*/
|
|
6202
|
-
async
|
|
6203
|
-
if (!this.
|
|
6204
|
-
console.warn(`[REMOTE_WATCHER] Sandbox stopped for session ${this.sessionId}`);
|
|
6205
|
-
await this.stop();
|
|
6467
|
+
async stop() {
|
|
6468
|
+
if (!this.isRunning) {
|
|
6206
6469
|
return;
|
|
6207
6470
|
}
|
|
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;
|
|
6471
|
+
if (this.outputPollTimer) {
|
|
6472
|
+
clearInterval(this.outputPollTimer);
|
|
6473
|
+
this.outputPollTimer = null;
|
|
6224
6474
|
}
|
|
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()
|
|
6475
|
+
try {
|
|
6476
|
+
if (this.sandboxState?.sandbox) {
|
|
6477
|
+
await this.sandboxState.sandbox.runCommand({
|
|
6478
|
+
cmd: "sh",
|
|
6479
|
+
args: ["-c", "pkill -f file-watcher.js 2>/dev/null || true"]
|
|
6237
6480
|
});
|
|
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()
|
|
6481
|
+
await this.sandboxState.sandbox.runCommand({
|
|
6482
|
+
cmd: "sh",
|
|
6483
|
+
args: ["-c", "rm -f /tmp/.file-watcher.js /tmp/.file-watcher-output.log"]
|
|
6249
6484
|
});
|
|
6250
6485
|
}
|
|
6486
|
+
} catch {
|
|
6251
6487
|
}
|
|
6252
|
-
this.
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
}
|
|
6488
|
+
this.isRunning = false;
|
|
6489
|
+
this.sandboxState = null;
|
|
6490
|
+
this.lastOutputPosition = 0;
|
|
6491
|
+
console.log(`[IN_SANDBOX_WATCHER] Stopped watcher for session ${this.sessionId}`);
|
|
6256
6492
|
}
|
|
6257
6493
|
/**
|
|
6258
|
-
*
|
|
6494
|
+
* Subscribe to file change events
|
|
6259
6495
|
*/
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
return true;
|
|
6264
|
-
}
|
|
6265
|
-
}
|
|
6266
|
-
return false;
|
|
6496
|
+
subscribe(callback) {
|
|
6497
|
+
this.subscribers.add(callback);
|
|
6498
|
+
return () => this.subscribers.delete(callback);
|
|
6267
6499
|
}
|
|
6268
6500
|
/**
|
|
6269
|
-
*
|
|
6501
|
+
* Check if watcher is running
|
|
6270
6502
|
*/
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
6274
|
-
return regex.test(filePath);
|
|
6503
|
+
isActive() {
|
|
6504
|
+
return this.isRunning;
|
|
6275
6505
|
}
|
|
6276
6506
|
/**
|
|
6277
|
-
*
|
|
6507
|
+
* Get watcher status
|
|
6508
|
+
*/
|
|
6509
|
+
getStatus() {
|
|
6510
|
+
return {
|
|
6511
|
+
sessionId: this.sessionId,
|
|
6512
|
+
watchPath: this.watchPath,
|
|
6513
|
+
isRunning: this.isRunning,
|
|
6514
|
+
startedAt: this.startedAt,
|
|
6515
|
+
lastHeartbeat: this.lastHeartbeat
|
|
6516
|
+
};
|
|
6517
|
+
}
|
|
6518
|
+
/**
|
|
6519
|
+
* Emit event to all subscribers
|
|
6278
6520
|
*/
|
|
6279
6521
|
async emitEvent(event) {
|
|
6280
6522
|
for (const callback of this.subscribers) {
|
|
6281
6523
|
try {
|
|
6282
6524
|
await callback(event);
|
|
6283
6525
|
} catch (error) {
|
|
6284
|
-
console.error("[
|
|
6526
|
+
console.error("[IN_SANDBOX_WATCHER] Error in subscriber callback:", error);
|
|
6285
6527
|
}
|
|
6286
6528
|
}
|
|
6287
6529
|
}
|
|
6288
6530
|
};
|
|
6289
|
-
exports.
|
|
6531
|
+
exports.InSandboxWatcherManager = class {
|
|
6290
6532
|
watchers = /* @__PURE__ */ new Map();
|
|
6291
6533
|
/**
|
|
6292
|
-
* Start watching a
|
|
6534
|
+
* Start watching a sandbox
|
|
6293
6535
|
*/
|
|
6294
6536
|
async startWatching(options) {
|
|
6295
6537
|
const { sessionId } = options;
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
await existing.stop();
|
|
6299
|
-
}
|
|
6300
|
-
const watcher = new exports.RemoteSandboxFileWatcher(options);
|
|
6538
|
+
await this.stopWatching(sessionId);
|
|
6539
|
+
const watcher = new exports.InSandboxWatcher(options);
|
|
6301
6540
|
await watcher.start();
|
|
6302
6541
|
this.watchers.set(sessionId, watcher);
|
|
6303
6542
|
return watcher;
|
|
@@ -6313,17 +6552,16 @@ var init_sandbox_file_watcher = __esm({
|
|
|
6313
6552
|
}
|
|
6314
6553
|
}
|
|
6315
6554
|
/**
|
|
6316
|
-
* Get
|
|
6555
|
+
* Get watcher for a session
|
|
6317
6556
|
*/
|
|
6318
6557
|
getWatcher(sessionId) {
|
|
6319
6558
|
return this.watchers.get(sessionId);
|
|
6320
6559
|
}
|
|
6321
6560
|
/**
|
|
6322
|
-
* Check if
|
|
6561
|
+
* Check if watching
|
|
6323
6562
|
*/
|
|
6324
6563
|
isWatching(sessionId) {
|
|
6325
|
-
|
|
6326
|
-
return watcher?.isActive() ?? false;
|
|
6564
|
+
return this.watchers.get(sessionId)?.isActive() ?? false;
|
|
6327
6565
|
}
|
|
6328
6566
|
/**
|
|
6329
6567
|
* Stop all watchers
|
|
@@ -6334,7 +6572,7 @@ var init_sandbox_file_watcher = __esm({
|
|
|
6334
6572
|
this.watchers.clear();
|
|
6335
6573
|
}
|
|
6336
6574
|
};
|
|
6337
|
-
|
|
6575
|
+
globalInSandboxManager = null;
|
|
6338
6576
|
}
|
|
6339
6577
|
});
|
|
6340
6578
|
function extractErrorMessage(error) {
|
|
@@ -6383,6 +6621,7 @@ exports.SandboxFileSync = void 0;
|
|
|
6383
6621
|
var init_sandbox_file_sync = __esm({
|
|
6384
6622
|
"src/runtime/sandbox-file-sync.ts"() {
|
|
6385
6623
|
init_sandbox_file_watcher();
|
|
6624
|
+
init_in_sandbox_watcher();
|
|
6386
6625
|
init_types();
|
|
6387
6626
|
exports.SandboxFileSync = class {
|
|
6388
6627
|
fileStore;
|
|
@@ -6393,7 +6632,7 @@ var init_sandbox_file_sync = __esm({
|
|
|
6393
6632
|
eventStorage;
|
|
6394
6633
|
webhookConfig;
|
|
6395
6634
|
// Watcher management
|
|
6396
|
-
|
|
6635
|
+
inSandboxWatchers = /* @__PURE__ */ new Map();
|
|
6397
6636
|
localWatchers = /* @__PURE__ */ new Map();
|
|
6398
6637
|
fileChangeSubscribers = /* @__PURE__ */ new Set();
|
|
6399
6638
|
// Sequence number cache per session (for event storage)
|
|
@@ -7184,34 +7423,27 @@ var init_sandbox_file_sync = __esm({
|
|
|
7184
7423
|
this.localWatchers.set(sessionId, watcher);
|
|
7185
7424
|
console.log(`[FILE_SYNC] Started local file watching for session ${sessionId} at ${opts.localPath}`);
|
|
7186
7425
|
} else {
|
|
7187
|
-
|
|
7188
|
-
throw new Error("Sandbox operations not configured. Call setSandboxOperations first.");
|
|
7189
|
-
}
|
|
7190
|
-
const watcher = new exports.RemoteSandboxFileWatcher({
|
|
7426
|
+
const watcher = new exports.InSandboxWatcher({
|
|
7191
7427
|
sessionId,
|
|
7192
|
-
|
|
7193
|
-
basePath: watchPath,
|
|
7194
|
-
// Use watchPath instead of sandboxBasePath
|
|
7195
|
-
pollIntervalMs: opts.pollIntervalMs ?? 2e3,
|
|
7196
|
-
ignored: opts.ignored ?? ["**/node_modules/**", "**/.git/**"],
|
|
7428
|
+
watchPath,
|
|
7197
7429
|
onFileChange: handleFileChange,
|
|
7198
7430
|
onError: (error) => {
|
|
7199
|
-
console.error(`[FILE_SYNC]
|
|
7431
|
+
console.error(`[FILE_SYNC] In-sandbox watcher error for session ${sessionId}:`, error);
|
|
7200
7432
|
}
|
|
7201
7433
|
});
|
|
7202
7434
|
await watcher.start();
|
|
7203
|
-
this.
|
|
7204
|
-
console.log(`[FILE_SYNC] Started
|
|
7435
|
+
this.inSandboxWatchers.set(sessionId, watcher);
|
|
7436
|
+
console.log(`[FILE_SYNC] Started in-sandbox file watching for session ${sessionId} at path: ${watchPath}`);
|
|
7205
7437
|
}
|
|
7206
7438
|
}
|
|
7207
7439
|
/**
|
|
7208
7440
|
* Stop watching a session's sandbox
|
|
7209
7441
|
*/
|
|
7210
7442
|
async stopWatching(sessionId) {
|
|
7211
|
-
const
|
|
7212
|
-
if (
|
|
7213
|
-
await
|
|
7214
|
-
this.
|
|
7443
|
+
const inSandboxWatcher = this.inSandboxWatchers.get(sessionId);
|
|
7444
|
+
if (inSandboxWatcher) {
|
|
7445
|
+
await inSandboxWatcher.stop();
|
|
7446
|
+
this.inSandboxWatchers.delete(sessionId);
|
|
7215
7447
|
}
|
|
7216
7448
|
const localWatcher = this.localWatchers.get(sessionId);
|
|
7217
7449
|
if (localWatcher) {
|
|
@@ -7223,7 +7455,7 @@ var init_sandbox_file_sync = __esm({
|
|
|
7223
7455
|
* Check if a session is being watched
|
|
7224
7456
|
*/
|
|
7225
7457
|
isWatching(sessionId) {
|
|
7226
|
-
return this.
|
|
7458
|
+
return this.inSandboxWatchers.has(sessionId) || this.localWatchers.has(sessionId);
|
|
7227
7459
|
}
|
|
7228
7460
|
/**
|
|
7229
7461
|
* Subscribe to file change events across all watched sessions.
|
|
@@ -7257,10 +7489,10 @@ var init_sandbox_file_sync = __esm({
|
|
|
7257
7489
|
* Stop all watchers and cleanup
|
|
7258
7490
|
*/
|
|
7259
7491
|
async stopAllWatching() {
|
|
7260
|
-
const
|
|
7492
|
+
const inSandboxPromises = Array.from(this.inSandboxWatchers.values()).map((w) => w.stop());
|
|
7261
7493
|
const localPromises = Array.from(this.localWatchers.values()).map((w) => w.stop());
|
|
7262
|
-
await Promise.all([...
|
|
7263
|
-
this.
|
|
7494
|
+
await Promise.all([...inSandboxPromises, ...localPromises]);
|
|
7495
|
+
this.inSandboxWatchers.clear();
|
|
7264
7496
|
this.localWatchers.clear();
|
|
7265
7497
|
}
|
|
7266
7498
|
/**
|
|
@@ -7268,8 +7500,8 @@ var init_sandbox_file_sync = __esm({
|
|
|
7268
7500
|
*/
|
|
7269
7501
|
getWatchingStatus() {
|
|
7270
7502
|
const statuses = [];
|
|
7271
|
-
for (const [sessionId, watcher] of this.
|
|
7272
|
-
statuses.push({ sessionId, type: "
|
|
7503
|
+
for (const [sessionId, watcher] of this.inSandboxWatchers) {
|
|
7504
|
+
statuses.push({ sessionId, type: "in-sandbox", isActive: watcher.isActive() });
|
|
7273
7505
|
}
|
|
7274
7506
|
for (const [sessionId, watcher] of this.localWatchers) {
|
|
7275
7507
|
statuses.push({ sessionId, type: "local", isActive: watcher.isActive() });
|
|
@@ -7280,364 +7512,6 @@ var init_sandbox_file_sync = __esm({
|
|
|
7280
7512
|
}
|
|
7281
7513
|
});
|
|
7282
7514
|
|
|
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
7515
|
// src/runtime/index.ts
|
|
7642
7516
|
function generateDockerCommand(config) {
|
|
7643
7517
|
const args = ["docker", "run"];
|
|
@@ -8070,7 +7944,7 @@ var init_credentials = __esm({
|
|
|
8070
7944
|
}
|
|
8071
7945
|
});
|
|
8072
7946
|
exports.gitHubSkillSourceSchema = void 0; exports.localSkillSourceSchema = void 0; exports.skillSourceSchema = void 0; exports.skillConfigSchema = void 0; exports.fileEntrySchema = void 0;
|
|
8073
|
-
var
|
|
7947
|
+
var init_types4 = __esm({
|
|
8074
7948
|
"src/skills/types.ts"() {
|
|
8075
7949
|
exports.gitHubSkillSourceSchema = zod.z.object({
|
|
8076
7950
|
type: zod.z.literal("github"),
|
|
@@ -8614,7 +8488,7 @@ var init_manager2 = __esm({
|
|
|
8614
8488
|
// src/skills/index.ts
|
|
8615
8489
|
var init_skills = __esm({
|
|
8616
8490
|
"src/skills/index.ts"() {
|
|
8617
|
-
|
|
8491
|
+
init_types4();
|
|
8618
8492
|
init_local_provider();
|
|
8619
8493
|
init_github_provider();
|
|
8620
8494
|
init_manager2();
|
|
@@ -18134,7 +18008,7 @@ var init_storage3 = __esm({
|
|
|
18134
18008
|
}
|
|
18135
18009
|
if (options.filePath) {
|
|
18136
18010
|
query = query.or(
|
|
18137
|
-
`event_data->>filePath.eq.${options.filePath},and(category.eq.tool,tool_name.in.(Read,Write,Edit),event_data->input->>file_path.eq.${options.filePath})`
|
|
18011
|
+
`event_data->>filePath.eq.${options.filePath},event_data->>canonicalPath.eq.${options.filePath},and(category.eq.tool,tool_name.in.(Read,Write,Edit),event_data->input->>file_path.eq.${options.filePath})`
|
|
18138
18012
|
);
|
|
18139
18013
|
}
|
|
18140
18014
|
const ascending = options.order !== "desc";
|
|
@@ -19064,8 +18938,6 @@ exports.createQueueProcessor = createQueueProcessor;
|
|
|
19064
18938
|
exports.createQueueRouter = createQueueRouter;
|
|
19065
18939
|
exports.createR2BundleStore = createR2BundleStore;
|
|
19066
18940
|
exports.createR2FileStore = createR2FileStore;
|
|
19067
|
-
exports.createRemoteFileWatcher = createRemoteFileWatcher;
|
|
19068
|
-
exports.createRemoteFileWatcherManager = createRemoteFileWatcherManager;
|
|
19069
18941
|
exports.createS3BundleStore = createS3BundleStore;
|
|
19070
18942
|
exports.createS3FileStore = createS3FileStore;
|
|
19071
18943
|
exports.createSandboxFileOperations = createSandboxFileOperations;
|
|
@@ -19091,6 +18963,7 @@ exports.defineConfig = defineConfig;
|
|
|
19091
18963
|
exports.ensureSandboxPoolInitialized = ensureSandboxPoolInitialized;
|
|
19092
18964
|
exports.env = env;
|
|
19093
18965
|
exports.envOptional = envOptional;
|
|
18966
|
+
exports.executeCommandInSandbox = executeCommandInSandbox;
|
|
19094
18967
|
exports.extractTextContent = extractTextContent;
|
|
19095
18968
|
exports.extractTextFromMessage = extractTextFromMessage;
|
|
19096
18969
|
exports.formatToolName = formatToolName;
|
|
@@ -19103,12 +18976,12 @@ exports.getActionIcon = getActionIcon;
|
|
|
19103
18976
|
exports.getActionLabel = getActionLabel;
|
|
19104
18977
|
exports.getAllHeartbeatStatuses = getAllHeartbeatStatuses;
|
|
19105
18978
|
exports.getApiKeyEnvVar = getApiKeyEnvVar;
|
|
18979
|
+
exports.getCachedSandbox = getCachedSandbox;
|
|
19106
18980
|
exports.getDefaultModel = getDefaultModel;
|
|
19107
18981
|
exports.getFileWatcherManager = getFileWatcherManager;
|
|
19108
18982
|
exports.getHeartbeatStatus = getHeartbeatStatus;
|
|
19109
18983
|
exports.getInSandboxWatcherManager = getInSandboxWatcherManager;
|
|
19110
18984
|
exports.getOrCreateSandbox = getOrCreateSandbox;
|
|
19111
|
-
exports.getRemoteFileWatcherManager = getRemoteFileWatcherManager;
|
|
19112
18985
|
exports.getSandboxCacheStats = getSandboxCacheStats;
|
|
19113
18986
|
exports.getSandboxPool = getSandboxPool;
|
|
19114
18987
|
exports.getWorkspaceManager = getWorkspaceManager;
|