@inkeep/agents-run-api 0.22.12 → 0.23.0
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getLogger as getLogger$1 } from './chunk-A2S7GSHL.js';
|
|
2
2
|
import { __publicField } from './chunk-PKBMQBKP.js';
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
|
-
import { createHash } from 'crypto';
|
|
4
|
+
import crypto, { createHash } from 'crypto';
|
|
5
5
|
import { mkdirSync, existsSync, rmSync, writeFileSync } from 'fs';
|
|
6
6
|
import { tmpdir } from 'os';
|
|
7
7
|
import { join } from 'path';
|
|
@@ -466,6 +466,11 @@ var logger2 = getLogger$1("VercelSandboxExecutor");
|
|
|
466
466
|
var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
467
467
|
constructor(config) {
|
|
468
468
|
__publicField(this, "config");
|
|
469
|
+
__publicField(this, "sandboxPool", /* @__PURE__ */ new Map());
|
|
470
|
+
__publicField(this, "POOL_TTL", 5 * 60 * 1e3);
|
|
471
|
+
// 5 minutes
|
|
472
|
+
__publicField(this, "MAX_USE_COUNT", 50);
|
|
473
|
+
__publicField(this, "cleanupInterval", null);
|
|
469
474
|
this.config = config;
|
|
470
475
|
logger2.info(
|
|
471
476
|
{
|
|
@@ -475,8 +480,9 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
475
480
|
timeout: config.timeout,
|
|
476
481
|
vcpus: config.vcpus
|
|
477
482
|
},
|
|
478
|
-
"VercelSandboxExecutor initialized"
|
|
483
|
+
"VercelSandboxExecutor initialized with pooling"
|
|
479
484
|
);
|
|
485
|
+
this.startPoolCleanup();
|
|
480
486
|
}
|
|
481
487
|
/**
|
|
482
488
|
* Get singleton instance of VercelSandboxExecutor
|
|
@@ -488,45 +494,235 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
488
494
|
return _VercelSandboxExecutor.instance;
|
|
489
495
|
}
|
|
490
496
|
/**
|
|
491
|
-
*
|
|
497
|
+
* Generate a hash for dependencies to use as cache key
|
|
498
|
+
*/
|
|
499
|
+
generateDependencyHash(dependencies) {
|
|
500
|
+
const sorted = Object.keys(dependencies).sort().map((key) => `${key}@${dependencies[key]}`).join(",");
|
|
501
|
+
return crypto.createHash("md5").update(sorted).digest("hex").substring(0, 8);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Get a cached sandbox if available and still valid
|
|
505
|
+
*/
|
|
506
|
+
getCachedSandbox(dependencyHash) {
|
|
507
|
+
const cached = this.sandboxPool.get(dependencyHash);
|
|
508
|
+
if (!cached) return null;
|
|
509
|
+
const now = Date.now();
|
|
510
|
+
const age = now - cached.createdAt;
|
|
511
|
+
if (age > this.POOL_TTL || cached.useCount >= this.MAX_USE_COUNT) {
|
|
512
|
+
logger2.debug(
|
|
513
|
+
{
|
|
514
|
+
dependencyHash,
|
|
515
|
+
age,
|
|
516
|
+
useCount: cached.useCount,
|
|
517
|
+
ttl: this.POOL_TTL,
|
|
518
|
+
maxUseCount: this.MAX_USE_COUNT
|
|
519
|
+
},
|
|
520
|
+
"Sandbox expired, will create new one"
|
|
521
|
+
);
|
|
522
|
+
this.removeSandbox(dependencyHash);
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
logger2.debug(
|
|
526
|
+
{
|
|
527
|
+
dependencyHash,
|
|
528
|
+
useCount: cached.useCount,
|
|
529
|
+
age
|
|
530
|
+
},
|
|
531
|
+
"Reusing cached sandbox"
|
|
532
|
+
);
|
|
533
|
+
return cached.sandbox;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Add sandbox to pool
|
|
537
|
+
*/
|
|
538
|
+
addToPool(dependencyHash, sandbox, dependencies) {
|
|
539
|
+
this.sandboxPool.set(dependencyHash, {
|
|
540
|
+
sandbox,
|
|
541
|
+
createdAt: Date.now(),
|
|
542
|
+
useCount: 0,
|
|
543
|
+
dependencies
|
|
544
|
+
});
|
|
545
|
+
logger2.debug(
|
|
546
|
+
{
|
|
547
|
+
dependencyHash,
|
|
548
|
+
poolSize: this.sandboxPool.size
|
|
549
|
+
},
|
|
550
|
+
"Sandbox added to pool"
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Increment use count for a sandbox
|
|
555
|
+
*/
|
|
556
|
+
incrementUseCount(dependencyHash) {
|
|
557
|
+
const cached = this.sandboxPool.get(dependencyHash);
|
|
558
|
+
if (cached) {
|
|
559
|
+
cached.useCount++;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Remove and clean up a sandbox
|
|
564
|
+
*/
|
|
565
|
+
async removeSandbox(dependencyHash) {
|
|
566
|
+
const cached = this.sandboxPool.get(dependencyHash);
|
|
567
|
+
if (cached) {
|
|
568
|
+
try {
|
|
569
|
+
await cached.sandbox.stop();
|
|
570
|
+
logger2.debug({ dependencyHash }, "Sandbox stopped");
|
|
571
|
+
} catch (error) {
|
|
572
|
+
logger2.warn({ error, dependencyHash }, "Error stopping sandbox");
|
|
573
|
+
}
|
|
574
|
+
this.sandboxPool.delete(dependencyHash);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Start periodic cleanup of expired sandboxes
|
|
579
|
+
*/
|
|
580
|
+
startPoolCleanup() {
|
|
581
|
+
this.cleanupInterval = setInterval(
|
|
582
|
+
() => {
|
|
583
|
+
const now = Date.now();
|
|
584
|
+
const toRemove = [];
|
|
585
|
+
for (const [hash, cached] of this.sandboxPool.entries()) {
|
|
586
|
+
const age = now - cached.createdAt;
|
|
587
|
+
if (age > this.POOL_TTL || cached.useCount >= this.MAX_USE_COUNT) {
|
|
588
|
+
toRemove.push(hash);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (toRemove.length > 0) {
|
|
592
|
+
logger2.info(
|
|
593
|
+
{
|
|
594
|
+
count: toRemove.length,
|
|
595
|
+
poolSize: this.sandboxPool.size
|
|
596
|
+
},
|
|
597
|
+
"Cleaning up expired sandboxes"
|
|
598
|
+
);
|
|
599
|
+
for (const hash of toRemove) {
|
|
600
|
+
this.removeSandbox(hash);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
60 * 1e3
|
|
605
|
+
// Run every minute
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Cleanup all sandboxes and stop cleanup interval
|
|
610
|
+
*/
|
|
611
|
+
async cleanup() {
|
|
612
|
+
if (this.cleanupInterval) {
|
|
613
|
+
clearInterval(this.cleanupInterval);
|
|
614
|
+
this.cleanupInterval = null;
|
|
615
|
+
}
|
|
616
|
+
logger2.info(
|
|
617
|
+
{
|
|
618
|
+
poolSize: this.sandboxPool.size
|
|
619
|
+
},
|
|
620
|
+
"Cleaning up all sandboxes"
|
|
621
|
+
);
|
|
622
|
+
const promises = Array.from(this.sandboxPool.keys()).map((hash) => this.removeSandbox(hash));
|
|
623
|
+
await Promise.all(promises);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Extract environment variable names from code
|
|
627
|
+
* Matches patterns like process.env.VAR_NAME or process.env['VAR_NAME']
|
|
628
|
+
*/
|
|
629
|
+
extractEnvVars(code) {
|
|
630
|
+
const envVars = /* @__PURE__ */ new Set();
|
|
631
|
+
const dotNotationRegex = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
|
|
632
|
+
let match = dotNotationRegex.exec(code);
|
|
633
|
+
while (match !== null) {
|
|
634
|
+
envVars.add(match[1]);
|
|
635
|
+
match = dotNotationRegex.exec(code);
|
|
636
|
+
}
|
|
637
|
+
const bracketNotationRegex = /process\.env\[['"]([A-Z_][A-Z0-9_]*)['"]\]/g;
|
|
638
|
+
match = bracketNotationRegex.exec(code);
|
|
639
|
+
while (match !== null) {
|
|
640
|
+
envVars.add(match[1]);
|
|
641
|
+
match = bracketNotationRegex.exec(code);
|
|
642
|
+
}
|
|
643
|
+
return envVars;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Create .env file content from environment variables
|
|
647
|
+
*/
|
|
648
|
+
createEnvFileContent(envVarNames) {
|
|
649
|
+
const envLines = [];
|
|
650
|
+
for (const varName of envVarNames) {
|
|
651
|
+
const value = process.env[varName];
|
|
652
|
+
if (value !== void 0) {
|
|
653
|
+
const escapedValue = value.replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
654
|
+
envLines.push(`${varName}="${escapedValue}"`);
|
|
655
|
+
logger2.debug({ varName }, "Adding environment variable to sandbox");
|
|
656
|
+
} else {
|
|
657
|
+
logger2.warn(
|
|
658
|
+
{ varName },
|
|
659
|
+
"Environment variable referenced in code but not found in host environment"
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return envLines.join("\n");
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Execute a function tool in Vercel Sandbox with pooling
|
|
492
667
|
*/
|
|
493
668
|
async executeFunctionTool(functionId, args, toolConfig) {
|
|
494
669
|
const startTime = Date.now();
|
|
495
670
|
const logs = [];
|
|
671
|
+
const dependencies = toolConfig.dependencies || {};
|
|
672
|
+
const dependencyHash = this.generateDependencyHash(dependencies);
|
|
496
673
|
try {
|
|
497
674
|
logger2.info(
|
|
498
675
|
{
|
|
499
676
|
functionId,
|
|
500
|
-
functionName: toolConfig.name
|
|
677
|
+
functionName: toolConfig.name,
|
|
678
|
+
dependencyHash,
|
|
679
|
+
poolSize: this.sandboxPool.size
|
|
501
680
|
},
|
|
502
681
|
"Executing function in Vercel Sandbox"
|
|
503
682
|
);
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
683
|
+
let sandbox = this.getCachedSandbox(dependencyHash);
|
|
684
|
+
let isNewSandbox = false;
|
|
685
|
+
if (!sandbox) {
|
|
686
|
+
isNewSandbox = true;
|
|
687
|
+
sandbox = await Sandbox.create({
|
|
688
|
+
token: this.config.token,
|
|
689
|
+
teamId: this.config.teamId,
|
|
690
|
+
projectId: this.config.projectId,
|
|
691
|
+
timeout: this.config.timeout,
|
|
692
|
+
resources: {
|
|
693
|
+
vcpus: this.config.vcpus || 1
|
|
694
|
+
},
|
|
695
|
+
runtime: this.config.runtime
|
|
696
|
+
});
|
|
697
|
+
logger2.info(
|
|
698
|
+
{
|
|
699
|
+
functionId,
|
|
700
|
+
sandboxId: sandbox.sandboxId,
|
|
701
|
+
dependencyHash
|
|
702
|
+
},
|
|
703
|
+
`New sandbox created for function ${functionId}`
|
|
704
|
+
);
|
|
705
|
+
this.addToPool(dependencyHash, sandbox, dependencies);
|
|
706
|
+
} else {
|
|
707
|
+
logger2.info(
|
|
708
|
+
{
|
|
709
|
+
functionId,
|
|
710
|
+
sandboxId: sandbox.sandboxId,
|
|
711
|
+
dependencyHash
|
|
712
|
+
},
|
|
713
|
+
`Reusing cached sandbox for function ${functionId}`
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
this.incrementUseCount(dependencyHash);
|
|
521
717
|
try {
|
|
522
|
-
if (toolConfig.dependencies && Object.keys(toolConfig.dependencies).length > 0) {
|
|
718
|
+
if (isNewSandbox && toolConfig.dependencies && Object.keys(toolConfig.dependencies).length > 0) {
|
|
523
719
|
logger2.debug(
|
|
524
720
|
{
|
|
525
721
|
functionId,
|
|
526
722
|
functionName: toolConfig.name,
|
|
527
723
|
dependencies: toolConfig.dependencies
|
|
528
724
|
},
|
|
529
|
-
"Installing dependencies"
|
|
725
|
+
"Installing dependencies in new sandbox"
|
|
530
726
|
);
|
|
531
727
|
const packageJson = {
|
|
532
728
|
dependencies: toolConfig.dependencies
|
|
@@ -553,15 +749,40 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
553
749
|
if (installCmd.exitCode !== 0) {
|
|
554
750
|
throw new Error(`Failed to install dependencies: ${installStderr}`);
|
|
555
751
|
}
|
|
752
|
+
logger2.info(
|
|
753
|
+
{
|
|
754
|
+
functionId,
|
|
755
|
+
dependencyHash
|
|
756
|
+
},
|
|
757
|
+
"Dependencies installed successfully"
|
|
758
|
+
);
|
|
556
759
|
}
|
|
557
760
|
const executionCode = createExecutionWrapper(toolConfig.executeCode, args);
|
|
761
|
+
const envVars = this.extractEnvVars(toolConfig.executeCode);
|
|
762
|
+
const filesToWrite = [];
|
|
558
763
|
const filename = this.config.runtime === "typescript" ? "execute.ts" : "execute.js";
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
764
|
+
filesToWrite.push({
|
|
765
|
+
path: filename,
|
|
766
|
+
content: Buffer.from(executionCode, "utf-8")
|
|
767
|
+
});
|
|
768
|
+
if (envVars.size > 0) {
|
|
769
|
+
const envFileContent = this.createEnvFileContent(envVars);
|
|
770
|
+
if (envFileContent) {
|
|
771
|
+
filesToWrite.push({
|
|
772
|
+
path: ".env",
|
|
773
|
+
content: Buffer.from(envFileContent, "utf-8")
|
|
774
|
+
});
|
|
775
|
+
logger2.info(
|
|
776
|
+
{
|
|
777
|
+
functionId,
|
|
778
|
+
envVarCount: envVars.size,
|
|
779
|
+
envVars: Array.from(envVars)
|
|
780
|
+
},
|
|
781
|
+
"Injecting environment variables into sandbox"
|
|
782
|
+
);
|
|
563
783
|
}
|
|
564
|
-
|
|
784
|
+
}
|
|
785
|
+
await sandbox.writeFiles(filesToWrite);
|
|
565
786
|
logger2.info(
|
|
566
787
|
{
|
|
567
788
|
functionId,
|
|
@@ -569,11 +790,19 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
569
790
|
},
|
|
570
791
|
`Execution code written to file for runtime ${this.config.runtime}`
|
|
571
792
|
);
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
793
|
+
const executeCmd = await (async () => {
|
|
794
|
+
if (envVars.size > 0) {
|
|
795
|
+
return sandbox.runCommand({
|
|
796
|
+
cmd: "npx",
|
|
797
|
+
args: this.config.runtime === "typescript" ? ["--yes", "dotenv-cli", "--", "npx", "tsx", filename] : ["--yes", "dotenv-cli", "--", "node", filename]
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
const runtime = this.config.runtime === "typescript" ? "tsx" : "node";
|
|
801
|
+
return sandbox.runCommand({
|
|
802
|
+
cmd: runtime,
|
|
803
|
+
args: [filename]
|
|
804
|
+
});
|
|
805
|
+
})();
|
|
577
806
|
const executeStdout = await executeCmd.stdout();
|
|
578
807
|
const executeStderr = await executeCmd.stderr();
|
|
579
808
|
if (executeStdout) {
|
|
@@ -613,8 +842,9 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
613
842
|
logs,
|
|
614
843
|
executionTime
|
|
615
844
|
};
|
|
616
|
-
}
|
|
617
|
-
await
|
|
845
|
+
} catch (innerError) {
|
|
846
|
+
await this.removeSandbox(dependencyHash);
|
|
847
|
+
throw innerError;
|
|
618
848
|
}
|
|
619
849
|
} catch (error) {
|
|
620
850
|
const executionTime = Date.now() - startTime;
|
|
@@ -635,12 +865,6 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
635
865
|
};
|
|
636
866
|
}
|
|
637
867
|
}
|
|
638
|
-
/**
|
|
639
|
-
* Clean up resources
|
|
640
|
-
*/
|
|
641
|
-
async cleanup() {
|
|
642
|
-
logger2.info({}, "VercelSandboxExecutor cleanup completed");
|
|
643
|
-
}
|
|
644
868
|
};
|
|
645
869
|
__publicField(_VercelSandboxExecutor, "instance");
|
|
646
870
|
var VercelSandboxExecutor = _VercelSandboxExecutor;
|
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,7 @@ var fetchToNode = require('fetch-to-node');
|
|
|
48
48
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
49
49
|
|
|
50
50
|
var z6__default = /*#__PURE__*/_interopDefault(z6);
|
|
51
|
+
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
51
52
|
var jmespath__default = /*#__PURE__*/_interopDefault(jmespath);
|
|
52
53
|
var Ajv__default = /*#__PURE__*/_interopDefault(Ajv);
|
|
53
54
|
var destr__default = /*#__PURE__*/_interopDefault(destr);
|
|
@@ -858,6 +859,11 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
858
859
|
_VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
859
860
|
constructor(config) {
|
|
860
861
|
__publicField(this, "config");
|
|
862
|
+
__publicField(this, "sandboxPool", /* @__PURE__ */ new Map());
|
|
863
|
+
__publicField(this, "POOL_TTL", 5 * 60 * 1e3);
|
|
864
|
+
// 5 minutes
|
|
865
|
+
__publicField(this, "MAX_USE_COUNT", 50);
|
|
866
|
+
__publicField(this, "cleanupInterval", null);
|
|
861
867
|
this.config = config;
|
|
862
868
|
logger17.info(
|
|
863
869
|
{
|
|
@@ -867,8 +873,9 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
867
873
|
timeout: config.timeout,
|
|
868
874
|
vcpus: config.vcpus
|
|
869
875
|
},
|
|
870
|
-
"VercelSandboxExecutor initialized"
|
|
876
|
+
"VercelSandboxExecutor initialized with pooling"
|
|
871
877
|
);
|
|
878
|
+
this.startPoolCleanup();
|
|
872
879
|
}
|
|
873
880
|
/**
|
|
874
881
|
* Get singleton instance of VercelSandboxExecutor
|
|
@@ -880,45 +887,235 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
880
887
|
return _VercelSandboxExecutor.instance;
|
|
881
888
|
}
|
|
882
889
|
/**
|
|
883
|
-
*
|
|
890
|
+
* Generate a hash for dependencies to use as cache key
|
|
891
|
+
*/
|
|
892
|
+
generateDependencyHash(dependencies) {
|
|
893
|
+
const sorted = Object.keys(dependencies).sort().map((key) => `${key}@${dependencies[key]}`).join(",");
|
|
894
|
+
return crypto__default.default.createHash("md5").update(sorted).digest("hex").substring(0, 8);
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Get a cached sandbox if available and still valid
|
|
898
|
+
*/
|
|
899
|
+
getCachedSandbox(dependencyHash) {
|
|
900
|
+
const cached = this.sandboxPool.get(dependencyHash);
|
|
901
|
+
if (!cached) return null;
|
|
902
|
+
const now = Date.now();
|
|
903
|
+
const age = now - cached.createdAt;
|
|
904
|
+
if (age > this.POOL_TTL || cached.useCount >= this.MAX_USE_COUNT) {
|
|
905
|
+
logger17.debug(
|
|
906
|
+
{
|
|
907
|
+
dependencyHash,
|
|
908
|
+
age,
|
|
909
|
+
useCount: cached.useCount,
|
|
910
|
+
ttl: this.POOL_TTL,
|
|
911
|
+
maxUseCount: this.MAX_USE_COUNT
|
|
912
|
+
},
|
|
913
|
+
"Sandbox expired, will create new one"
|
|
914
|
+
);
|
|
915
|
+
this.removeSandbox(dependencyHash);
|
|
916
|
+
return null;
|
|
917
|
+
}
|
|
918
|
+
logger17.debug(
|
|
919
|
+
{
|
|
920
|
+
dependencyHash,
|
|
921
|
+
useCount: cached.useCount,
|
|
922
|
+
age
|
|
923
|
+
},
|
|
924
|
+
"Reusing cached sandbox"
|
|
925
|
+
);
|
|
926
|
+
return cached.sandbox;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Add sandbox to pool
|
|
930
|
+
*/
|
|
931
|
+
addToPool(dependencyHash, sandbox, dependencies) {
|
|
932
|
+
this.sandboxPool.set(dependencyHash, {
|
|
933
|
+
sandbox,
|
|
934
|
+
createdAt: Date.now(),
|
|
935
|
+
useCount: 0,
|
|
936
|
+
dependencies
|
|
937
|
+
});
|
|
938
|
+
logger17.debug(
|
|
939
|
+
{
|
|
940
|
+
dependencyHash,
|
|
941
|
+
poolSize: this.sandboxPool.size
|
|
942
|
+
},
|
|
943
|
+
"Sandbox added to pool"
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Increment use count for a sandbox
|
|
948
|
+
*/
|
|
949
|
+
incrementUseCount(dependencyHash) {
|
|
950
|
+
const cached = this.sandboxPool.get(dependencyHash);
|
|
951
|
+
if (cached) {
|
|
952
|
+
cached.useCount++;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Remove and clean up a sandbox
|
|
957
|
+
*/
|
|
958
|
+
async removeSandbox(dependencyHash) {
|
|
959
|
+
const cached = this.sandboxPool.get(dependencyHash);
|
|
960
|
+
if (cached) {
|
|
961
|
+
try {
|
|
962
|
+
await cached.sandbox.stop();
|
|
963
|
+
logger17.debug({ dependencyHash }, "Sandbox stopped");
|
|
964
|
+
} catch (error) {
|
|
965
|
+
logger17.warn({ error, dependencyHash }, "Error stopping sandbox");
|
|
966
|
+
}
|
|
967
|
+
this.sandboxPool.delete(dependencyHash);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Start periodic cleanup of expired sandboxes
|
|
972
|
+
*/
|
|
973
|
+
startPoolCleanup() {
|
|
974
|
+
this.cleanupInterval = setInterval(
|
|
975
|
+
() => {
|
|
976
|
+
const now = Date.now();
|
|
977
|
+
const toRemove = [];
|
|
978
|
+
for (const [hash, cached] of this.sandboxPool.entries()) {
|
|
979
|
+
const age = now - cached.createdAt;
|
|
980
|
+
if (age > this.POOL_TTL || cached.useCount >= this.MAX_USE_COUNT) {
|
|
981
|
+
toRemove.push(hash);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
if (toRemove.length > 0) {
|
|
985
|
+
logger17.info(
|
|
986
|
+
{
|
|
987
|
+
count: toRemove.length,
|
|
988
|
+
poolSize: this.sandboxPool.size
|
|
989
|
+
},
|
|
990
|
+
"Cleaning up expired sandboxes"
|
|
991
|
+
);
|
|
992
|
+
for (const hash of toRemove) {
|
|
993
|
+
this.removeSandbox(hash);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
60 * 1e3
|
|
998
|
+
// Run every minute
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Cleanup all sandboxes and stop cleanup interval
|
|
1003
|
+
*/
|
|
1004
|
+
async cleanup() {
|
|
1005
|
+
if (this.cleanupInterval) {
|
|
1006
|
+
clearInterval(this.cleanupInterval);
|
|
1007
|
+
this.cleanupInterval = null;
|
|
1008
|
+
}
|
|
1009
|
+
logger17.info(
|
|
1010
|
+
{
|
|
1011
|
+
poolSize: this.sandboxPool.size
|
|
1012
|
+
},
|
|
1013
|
+
"Cleaning up all sandboxes"
|
|
1014
|
+
);
|
|
1015
|
+
const promises = Array.from(this.sandboxPool.keys()).map((hash) => this.removeSandbox(hash));
|
|
1016
|
+
await Promise.all(promises);
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Extract environment variable names from code
|
|
1020
|
+
* Matches patterns like process.env.VAR_NAME or process.env['VAR_NAME']
|
|
1021
|
+
*/
|
|
1022
|
+
extractEnvVars(code) {
|
|
1023
|
+
const envVars = /* @__PURE__ */ new Set();
|
|
1024
|
+
const dotNotationRegex = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
|
|
1025
|
+
let match = dotNotationRegex.exec(code);
|
|
1026
|
+
while (match !== null) {
|
|
1027
|
+
envVars.add(match[1]);
|
|
1028
|
+
match = dotNotationRegex.exec(code);
|
|
1029
|
+
}
|
|
1030
|
+
const bracketNotationRegex = /process\.env\[['"]([A-Z_][A-Z0-9_]*)['"]\]/g;
|
|
1031
|
+
match = bracketNotationRegex.exec(code);
|
|
1032
|
+
while (match !== null) {
|
|
1033
|
+
envVars.add(match[1]);
|
|
1034
|
+
match = bracketNotationRegex.exec(code);
|
|
1035
|
+
}
|
|
1036
|
+
return envVars;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Create .env file content from environment variables
|
|
1040
|
+
*/
|
|
1041
|
+
createEnvFileContent(envVarNames) {
|
|
1042
|
+
const envLines = [];
|
|
1043
|
+
for (const varName of envVarNames) {
|
|
1044
|
+
const value = process.env[varName];
|
|
1045
|
+
if (value !== void 0) {
|
|
1046
|
+
const escapedValue = value.replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
1047
|
+
envLines.push(`${varName}="${escapedValue}"`);
|
|
1048
|
+
logger17.debug({ varName }, "Adding environment variable to sandbox");
|
|
1049
|
+
} else {
|
|
1050
|
+
logger17.warn(
|
|
1051
|
+
{ varName },
|
|
1052
|
+
"Environment variable referenced in code but not found in host environment"
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
return envLines.join("\n");
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Execute a function tool in Vercel Sandbox with pooling
|
|
884
1060
|
*/
|
|
885
1061
|
async executeFunctionTool(functionId, args, toolConfig) {
|
|
886
1062
|
const startTime = Date.now();
|
|
887
1063
|
const logs = [];
|
|
1064
|
+
const dependencies = toolConfig.dependencies || {};
|
|
1065
|
+
const dependencyHash = this.generateDependencyHash(dependencies);
|
|
888
1066
|
try {
|
|
889
1067
|
logger17.info(
|
|
890
1068
|
{
|
|
891
1069
|
functionId,
|
|
892
|
-
functionName: toolConfig.name
|
|
1070
|
+
functionName: toolConfig.name,
|
|
1071
|
+
dependencyHash,
|
|
1072
|
+
poolSize: this.sandboxPool.size
|
|
893
1073
|
},
|
|
894
1074
|
"Executing function in Vercel Sandbox"
|
|
895
1075
|
);
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1076
|
+
let sandbox$1 = this.getCachedSandbox(dependencyHash);
|
|
1077
|
+
let isNewSandbox = false;
|
|
1078
|
+
if (!sandbox$1) {
|
|
1079
|
+
isNewSandbox = true;
|
|
1080
|
+
sandbox$1 = await sandbox.Sandbox.create({
|
|
1081
|
+
token: this.config.token,
|
|
1082
|
+
teamId: this.config.teamId,
|
|
1083
|
+
projectId: this.config.projectId,
|
|
1084
|
+
timeout: this.config.timeout,
|
|
1085
|
+
resources: {
|
|
1086
|
+
vcpus: this.config.vcpus || 1
|
|
1087
|
+
},
|
|
1088
|
+
runtime: this.config.runtime
|
|
1089
|
+
});
|
|
1090
|
+
logger17.info(
|
|
1091
|
+
{
|
|
1092
|
+
functionId,
|
|
1093
|
+
sandboxId: sandbox$1.sandboxId,
|
|
1094
|
+
dependencyHash
|
|
1095
|
+
},
|
|
1096
|
+
`New sandbox created for function ${functionId}`
|
|
1097
|
+
);
|
|
1098
|
+
this.addToPool(dependencyHash, sandbox$1, dependencies);
|
|
1099
|
+
} else {
|
|
1100
|
+
logger17.info(
|
|
1101
|
+
{
|
|
1102
|
+
functionId,
|
|
1103
|
+
sandboxId: sandbox$1.sandboxId,
|
|
1104
|
+
dependencyHash
|
|
1105
|
+
},
|
|
1106
|
+
`Reusing cached sandbox for function ${functionId}`
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
this.incrementUseCount(dependencyHash);
|
|
913
1110
|
try {
|
|
914
|
-
if (toolConfig.dependencies && Object.keys(toolConfig.dependencies).length > 0) {
|
|
1111
|
+
if (isNewSandbox && toolConfig.dependencies && Object.keys(toolConfig.dependencies).length > 0) {
|
|
915
1112
|
logger17.debug(
|
|
916
1113
|
{
|
|
917
1114
|
functionId,
|
|
918
1115
|
functionName: toolConfig.name,
|
|
919
1116
|
dependencies: toolConfig.dependencies
|
|
920
1117
|
},
|
|
921
|
-
"Installing dependencies"
|
|
1118
|
+
"Installing dependencies in new sandbox"
|
|
922
1119
|
);
|
|
923
1120
|
const packageJson = {
|
|
924
1121
|
dependencies: toolConfig.dependencies
|
|
@@ -945,15 +1142,40 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
945
1142
|
if (installCmd.exitCode !== 0) {
|
|
946
1143
|
throw new Error(`Failed to install dependencies: ${installStderr}`);
|
|
947
1144
|
}
|
|
1145
|
+
logger17.info(
|
|
1146
|
+
{
|
|
1147
|
+
functionId,
|
|
1148
|
+
dependencyHash
|
|
1149
|
+
},
|
|
1150
|
+
"Dependencies installed successfully"
|
|
1151
|
+
);
|
|
948
1152
|
}
|
|
949
1153
|
const executionCode = createExecutionWrapper(toolConfig.executeCode, args);
|
|
1154
|
+
const envVars = this.extractEnvVars(toolConfig.executeCode);
|
|
1155
|
+
const filesToWrite = [];
|
|
950
1156
|
const filename = this.config.runtime === "typescript" ? "execute.ts" : "execute.js";
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1157
|
+
filesToWrite.push({
|
|
1158
|
+
path: filename,
|
|
1159
|
+
content: Buffer.from(executionCode, "utf-8")
|
|
1160
|
+
});
|
|
1161
|
+
if (envVars.size > 0) {
|
|
1162
|
+
const envFileContent = this.createEnvFileContent(envVars);
|
|
1163
|
+
if (envFileContent) {
|
|
1164
|
+
filesToWrite.push({
|
|
1165
|
+
path: ".env",
|
|
1166
|
+
content: Buffer.from(envFileContent, "utf-8")
|
|
1167
|
+
});
|
|
1168
|
+
logger17.info(
|
|
1169
|
+
{
|
|
1170
|
+
functionId,
|
|
1171
|
+
envVarCount: envVars.size,
|
|
1172
|
+
envVars: Array.from(envVars)
|
|
1173
|
+
},
|
|
1174
|
+
"Injecting environment variables into sandbox"
|
|
1175
|
+
);
|
|
955
1176
|
}
|
|
956
|
-
|
|
1177
|
+
}
|
|
1178
|
+
await sandbox$1.writeFiles(filesToWrite);
|
|
957
1179
|
logger17.info(
|
|
958
1180
|
{
|
|
959
1181
|
functionId,
|
|
@@ -961,11 +1183,19 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
961
1183
|
},
|
|
962
1184
|
`Execution code written to file for runtime ${this.config.runtime}`
|
|
963
1185
|
);
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1186
|
+
const executeCmd = await (async () => {
|
|
1187
|
+
if (envVars.size > 0) {
|
|
1188
|
+
return sandbox$1.runCommand({
|
|
1189
|
+
cmd: "npx",
|
|
1190
|
+
args: this.config.runtime === "typescript" ? ["--yes", "dotenv-cli", "--", "npx", "tsx", filename] : ["--yes", "dotenv-cli", "--", "node", filename]
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
const runtime = this.config.runtime === "typescript" ? "tsx" : "node";
|
|
1194
|
+
return sandbox$1.runCommand({
|
|
1195
|
+
cmd: runtime,
|
|
1196
|
+
args: [filename]
|
|
1197
|
+
});
|
|
1198
|
+
})();
|
|
969
1199
|
const executeStdout = await executeCmd.stdout();
|
|
970
1200
|
const executeStderr = await executeCmd.stderr();
|
|
971
1201
|
if (executeStdout) {
|
|
@@ -1005,8 +1235,9 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
1005
1235
|
logs,
|
|
1006
1236
|
executionTime
|
|
1007
1237
|
};
|
|
1008
|
-
}
|
|
1009
|
-
await
|
|
1238
|
+
} catch (innerError) {
|
|
1239
|
+
await this.removeSandbox(dependencyHash);
|
|
1240
|
+
throw innerError;
|
|
1010
1241
|
}
|
|
1011
1242
|
} catch (error) {
|
|
1012
1243
|
const executionTime = Date.now() - startTime;
|
|
@@ -1027,12 +1258,6 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
1027
1258
|
};
|
|
1028
1259
|
}
|
|
1029
1260
|
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Clean up resources
|
|
1032
|
-
*/
|
|
1033
|
-
async cleanup() {
|
|
1034
|
-
logger17.info({}, "VercelSandboxExecutor cleanup completed");
|
|
1035
|
-
}
|
|
1036
1261
|
};
|
|
1037
1262
|
__publicField(_VercelSandboxExecutor, "instance");
|
|
1038
1263
|
VercelSandboxExecutor = _VercelSandboxExecutor;
|
package/dist/index.js
CHANGED
|
@@ -7048,7 +7048,7 @@ var Agent = class {
|
|
|
7048
7048
|
if (functionToolsData.length === 0) {
|
|
7049
7049
|
return functionTools;
|
|
7050
7050
|
}
|
|
7051
|
-
const { SandboxExecutorFactory } = await import('./SandboxExecutorFactory-
|
|
7051
|
+
const { SandboxExecutorFactory } = await import('./SandboxExecutorFactory-VKLSUHMW.js');
|
|
7052
7052
|
const sandboxExecutor = SandboxExecutorFactory.getInstance();
|
|
7053
7053
|
for (const functionToolDef of functionToolsData) {
|
|
7054
7054
|
const functionId = functionToolDef.functionId;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/agents-run-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Agents Run API for Inkeep Agent Framework - handles chat, agent execution, and streaming",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"traverse": "^0.6.11",
|
|
53
53
|
"ts-pattern": "^5.7.1",
|
|
54
54
|
"zod": "^4.1.11",
|
|
55
|
-
"@inkeep/agents-core": "^0.
|
|
55
|
+
"@inkeep/agents-core": "^0.23.0"
|
|
56
56
|
},
|
|
57
57
|
"optionalDependencies": {
|
|
58
58
|
"keytar": "^7.9.0"
|