@inkeep/agents-run-api 0.22.12 → 0.23.1
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,227 @@ 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
|
+
* Note: Currently creates empty placeholders. Values will be populated in the future.
|
|
648
|
+
*/
|
|
649
|
+
createEnvFileContent(envVarNames) {
|
|
650
|
+
const envLines = [];
|
|
651
|
+
for (const varName of envVarNames) {
|
|
652
|
+
envLines.push(`${varName}=""`);
|
|
653
|
+
logger2.debug({ varName }, "Adding environment variable placeholder to sandbox");
|
|
654
|
+
}
|
|
655
|
+
return envLines.join("\n");
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Execute a function tool in Vercel Sandbox with pooling
|
|
492
659
|
*/
|
|
493
660
|
async executeFunctionTool(functionId, args, toolConfig) {
|
|
494
661
|
const startTime = Date.now();
|
|
495
662
|
const logs = [];
|
|
663
|
+
const dependencies = toolConfig.dependencies || {};
|
|
664
|
+
const dependencyHash = this.generateDependencyHash(dependencies);
|
|
496
665
|
try {
|
|
497
666
|
logger2.info(
|
|
498
667
|
{
|
|
499
668
|
functionId,
|
|
500
|
-
functionName: toolConfig.name
|
|
669
|
+
functionName: toolConfig.name,
|
|
670
|
+
dependencyHash,
|
|
671
|
+
poolSize: this.sandboxPool.size
|
|
501
672
|
},
|
|
502
673
|
"Executing function in Vercel Sandbox"
|
|
503
674
|
);
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
675
|
+
let sandbox = this.getCachedSandbox(dependencyHash);
|
|
676
|
+
let isNewSandbox = false;
|
|
677
|
+
if (!sandbox) {
|
|
678
|
+
isNewSandbox = true;
|
|
679
|
+
sandbox = await Sandbox.create({
|
|
680
|
+
token: this.config.token,
|
|
681
|
+
teamId: this.config.teamId,
|
|
682
|
+
projectId: this.config.projectId,
|
|
683
|
+
timeout: this.config.timeout,
|
|
684
|
+
resources: {
|
|
685
|
+
vcpus: this.config.vcpus || 1
|
|
686
|
+
},
|
|
687
|
+
runtime: this.config.runtime
|
|
688
|
+
});
|
|
689
|
+
logger2.info(
|
|
690
|
+
{
|
|
691
|
+
functionId,
|
|
692
|
+
sandboxId: sandbox.sandboxId,
|
|
693
|
+
dependencyHash
|
|
694
|
+
},
|
|
695
|
+
`New sandbox created for function ${functionId}`
|
|
696
|
+
);
|
|
697
|
+
this.addToPool(dependencyHash, sandbox, dependencies);
|
|
698
|
+
} else {
|
|
699
|
+
logger2.info(
|
|
700
|
+
{
|
|
701
|
+
functionId,
|
|
702
|
+
sandboxId: sandbox.sandboxId,
|
|
703
|
+
dependencyHash
|
|
704
|
+
},
|
|
705
|
+
`Reusing cached sandbox for function ${functionId}`
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
this.incrementUseCount(dependencyHash);
|
|
521
709
|
try {
|
|
522
|
-
if (toolConfig.dependencies && Object.keys(toolConfig.dependencies).length > 0) {
|
|
710
|
+
if (isNewSandbox && toolConfig.dependencies && Object.keys(toolConfig.dependencies).length > 0) {
|
|
523
711
|
logger2.debug(
|
|
524
712
|
{
|
|
525
713
|
functionId,
|
|
526
714
|
functionName: toolConfig.name,
|
|
527
715
|
dependencies: toolConfig.dependencies
|
|
528
716
|
},
|
|
529
|
-
"Installing dependencies"
|
|
717
|
+
"Installing dependencies in new sandbox"
|
|
530
718
|
);
|
|
531
719
|
const packageJson = {
|
|
532
720
|
dependencies: toolConfig.dependencies
|
|
@@ -553,27 +741,61 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
553
741
|
if (installCmd.exitCode !== 0) {
|
|
554
742
|
throw new Error(`Failed to install dependencies: ${installStderr}`);
|
|
555
743
|
}
|
|
744
|
+
logger2.info(
|
|
745
|
+
{
|
|
746
|
+
functionId,
|
|
747
|
+
dependencyHash
|
|
748
|
+
},
|
|
749
|
+
"Dependencies installed successfully"
|
|
750
|
+
);
|
|
556
751
|
}
|
|
557
752
|
const executionCode = createExecutionWrapper(toolConfig.executeCode, args);
|
|
753
|
+
const envVars = this.extractEnvVars(toolConfig.executeCode);
|
|
754
|
+
const filesToWrite = [];
|
|
558
755
|
const filename = this.config.runtime === "typescript" ? "execute.ts" : "execute.js";
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
756
|
+
filesToWrite.push({
|
|
757
|
+
path: filename,
|
|
758
|
+
content: Buffer.from(executionCode, "utf-8")
|
|
759
|
+
});
|
|
760
|
+
if (envVars.size > 0) {
|
|
761
|
+
const envFileContent = this.createEnvFileContent(envVars);
|
|
762
|
+
if (envFileContent) {
|
|
763
|
+
filesToWrite.push({
|
|
764
|
+
path: ".env",
|
|
765
|
+
content: Buffer.from(envFileContent, "utf-8")
|
|
766
|
+
});
|
|
767
|
+
logger2.info(
|
|
768
|
+
{
|
|
769
|
+
functionId,
|
|
770
|
+
envVarCount: envVars.size,
|
|
771
|
+
envVars: Array.from(envVars)
|
|
772
|
+
},
|
|
773
|
+
"Creating environment variable placeholders in sandbox"
|
|
774
|
+
);
|
|
563
775
|
}
|
|
564
|
-
|
|
776
|
+
}
|
|
777
|
+
await sandbox.writeFiles(filesToWrite);
|
|
565
778
|
logger2.info(
|
|
566
779
|
{
|
|
567
780
|
functionId,
|
|
568
|
-
runtime: this.config.runtime === "typescript" ? "tsx" : "node"
|
|
781
|
+
runtime: this.config.runtime === "typescript" ? "tsx" : "node",
|
|
782
|
+
hasEnvVars: envVars.size > 0
|
|
569
783
|
},
|
|
570
784
|
`Execution code written to file for runtime ${this.config.runtime}`
|
|
571
785
|
);
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
786
|
+
const executeCmd = await (async () => {
|
|
787
|
+
if (envVars.size > 0) {
|
|
788
|
+
return sandbox.runCommand({
|
|
789
|
+
cmd: "npx",
|
|
790
|
+
args: this.config.runtime === "typescript" ? ["--yes", "dotenv-cli", "--", "npx", "tsx", filename] : ["--yes", "dotenv-cli", "--", "node", filename]
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
const runtime = this.config.runtime === "typescript" ? "tsx" : "node";
|
|
794
|
+
return sandbox.runCommand({
|
|
795
|
+
cmd: runtime,
|
|
796
|
+
args: [filename]
|
|
797
|
+
});
|
|
798
|
+
})();
|
|
577
799
|
const executeStdout = await executeCmd.stdout();
|
|
578
800
|
const executeStderr = await executeCmd.stderr();
|
|
579
801
|
if (executeStdout) {
|
|
@@ -613,8 +835,9 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
613
835
|
logs,
|
|
614
836
|
executionTime
|
|
615
837
|
};
|
|
616
|
-
}
|
|
617
|
-
await
|
|
838
|
+
} catch (innerError) {
|
|
839
|
+
await this.removeSandbox(dependencyHash);
|
|
840
|
+
throw innerError;
|
|
618
841
|
}
|
|
619
842
|
} catch (error) {
|
|
620
843
|
const executionTime = Date.now() - startTime;
|
|
@@ -635,12 +858,6 @@ var _VercelSandboxExecutor = class _VercelSandboxExecutor {
|
|
|
635
858
|
};
|
|
636
859
|
}
|
|
637
860
|
}
|
|
638
|
-
/**
|
|
639
|
-
* Clean up resources
|
|
640
|
-
*/
|
|
641
|
-
async cleanup() {
|
|
642
|
-
logger2.info({}, "VercelSandboxExecutor cleanup completed");
|
|
643
|
-
}
|
|
644
861
|
};
|
|
645
862
|
__publicField(_VercelSandboxExecutor, "instance");
|
|
646
863
|
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,227 @@ 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
|
+
* Note: Currently creates empty placeholders. Values will be populated in the future.
|
|
1041
|
+
*/
|
|
1042
|
+
createEnvFileContent(envVarNames) {
|
|
1043
|
+
const envLines = [];
|
|
1044
|
+
for (const varName of envVarNames) {
|
|
1045
|
+
envLines.push(`${varName}=""`);
|
|
1046
|
+
logger17.debug({ varName }, "Adding environment variable placeholder to sandbox");
|
|
1047
|
+
}
|
|
1048
|
+
return envLines.join("\n");
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Execute a function tool in Vercel Sandbox with pooling
|
|
884
1052
|
*/
|
|
885
1053
|
async executeFunctionTool(functionId, args, toolConfig) {
|
|
886
1054
|
const startTime = Date.now();
|
|
887
1055
|
const logs = [];
|
|
1056
|
+
const dependencies = toolConfig.dependencies || {};
|
|
1057
|
+
const dependencyHash = this.generateDependencyHash(dependencies);
|
|
888
1058
|
try {
|
|
889
1059
|
logger17.info(
|
|
890
1060
|
{
|
|
891
1061
|
functionId,
|
|
892
|
-
functionName: toolConfig.name
|
|
1062
|
+
functionName: toolConfig.name,
|
|
1063
|
+
dependencyHash,
|
|
1064
|
+
poolSize: this.sandboxPool.size
|
|
893
1065
|
},
|
|
894
1066
|
"Executing function in Vercel Sandbox"
|
|
895
1067
|
);
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1068
|
+
let sandbox$1 = this.getCachedSandbox(dependencyHash);
|
|
1069
|
+
let isNewSandbox = false;
|
|
1070
|
+
if (!sandbox$1) {
|
|
1071
|
+
isNewSandbox = true;
|
|
1072
|
+
sandbox$1 = await sandbox.Sandbox.create({
|
|
1073
|
+
token: this.config.token,
|
|
1074
|
+
teamId: this.config.teamId,
|
|
1075
|
+
projectId: this.config.projectId,
|
|
1076
|
+
timeout: this.config.timeout,
|
|
1077
|
+
resources: {
|
|
1078
|
+
vcpus: this.config.vcpus || 1
|
|
1079
|
+
},
|
|
1080
|
+
runtime: this.config.runtime
|
|
1081
|
+
});
|
|
1082
|
+
logger17.info(
|
|
1083
|
+
{
|
|
1084
|
+
functionId,
|
|
1085
|
+
sandboxId: sandbox$1.sandboxId,
|
|
1086
|
+
dependencyHash
|
|
1087
|
+
},
|
|
1088
|
+
`New sandbox created for function ${functionId}`
|
|
1089
|
+
);
|
|
1090
|
+
this.addToPool(dependencyHash, sandbox$1, dependencies);
|
|
1091
|
+
} else {
|
|
1092
|
+
logger17.info(
|
|
1093
|
+
{
|
|
1094
|
+
functionId,
|
|
1095
|
+
sandboxId: sandbox$1.sandboxId,
|
|
1096
|
+
dependencyHash
|
|
1097
|
+
},
|
|
1098
|
+
`Reusing cached sandbox for function ${functionId}`
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
this.incrementUseCount(dependencyHash);
|
|
913
1102
|
try {
|
|
914
|
-
if (toolConfig.dependencies && Object.keys(toolConfig.dependencies).length > 0) {
|
|
1103
|
+
if (isNewSandbox && toolConfig.dependencies && Object.keys(toolConfig.dependencies).length > 0) {
|
|
915
1104
|
logger17.debug(
|
|
916
1105
|
{
|
|
917
1106
|
functionId,
|
|
918
1107
|
functionName: toolConfig.name,
|
|
919
1108
|
dependencies: toolConfig.dependencies
|
|
920
1109
|
},
|
|
921
|
-
"Installing dependencies"
|
|
1110
|
+
"Installing dependencies in new sandbox"
|
|
922
1111
|
);
|
|
923
1112
|
const packageJson = {
|
|
924
1113
|
dependencies: toolConfig.dependencies
|
|
@@ -945,27 +1134,61 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
945
1134
|
if (installCmd.exitCode !== 0) {
|
|
946
1135
|
throw new Error(`Failed to install dependencies: ${installStderr}`);
|
|
947
1136
|
}
|
|
1137
|
+
logger17.info(
|
|
1138
|
+
{
|
|
1139
|
+
functionId,
|
|
1140
|
+
dependencyHash
|
|
1141
|
+
},
|
|
1142
|
+
"Dependencies installed successfully"
|
|
1143
|
+
);
|
|
948
1144
|
}
|
|
949
1145
|
const executionCode = createExecutionWrapper(toolConfig.executeCode, args);
|
|
1146
|
+
const envVars = this.extractEnvVars(toolConfig.executeCode);
|
|
1147
|
+
const filesToWrite = [];
|
|
950
1148
|
const filename = this.config.runtime === "typescript" ? "execute.ts" : "execute.js";
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1149
|
+
filesToWrite.push({
|
|
1150
|
+
path: filename,
|
|
1151
|
+
content: Buffer.from(executionCode, "utf-8")
|
|
1152
|
+
});
|
|
1153
|
+
if (envVars.size > 0) {
|
|
1154
|
+
const envFileContent = this.createEnvFileContent(envVars);
|
|
1155
|
+
if (envFileContent) {
|
|
1156
|
+
filesToWrite.push({
|
|
1157
|
+
path: ".env",
|
|
1158
|
+
content: Buffer.from(envFileContent, "utf-8")
|
|
1159
|
+
});
|
|
1160
|
+
logger17.info(
|
|
1161
|
+
{
|
|
1162
|
+
functionId,
|
|
1163
|
+
envVarCount: envVars.size,
|
|
1164
|
+
envVars: Array.from(envVars)
|
|
1165
|
+
},
|
|
1166
|
+
"Creating environment variable placeholders in sandbox"
|
|
1167
|
+
);
|
|
955
1168
|
}
|
|
956
|
-
|
|
1169
|
+
}
|
|
1170
|
+
await sandbox$1.writeFiles(filesToWrite);
|
|
957
1171
|
logger17.info(
|
|
958
1172
|
{
|
|
959
1173
|
functionId,
|
|
960
|
-
runtime: this.config.runtime === "typescript" ? "tsx" : "node"
|
|
1174
|
+
runtime: this.config.runtime === "typescript" ? "tsx" : "node",
|
|
1175
|
+
hasEnvVars: envVars.size > 0
|
|
961
1176
|
},
|
|
962
1177
|
`Execution code written to file for runtime ${this.config.runtime}`
|
|
963
1178
|
);
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1179
|
+
const executeCmd = await (async () => {
|
|
1180
|
+
if (envVars.size > 0) {
|
|
1181
|
+
return sandbox$1.runCommand({
|
|
1182
|
+
cmd: "npx",
|
|
1183
|
+
args: this.config.runtime === "typescript" ? ["--yes", "dotenv-cli", "--", "npx", "tsx", filename] : ["--yes", "dotenv-cli", "--", "node", filename]
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
const runtime = this.config.runtime === "typescript" ? "tsx" : "node";
|
|
1187
|
+
return sandbox$1.runCommand({
|
|
1188
|
+
cmd: runtime,
|
|
1189
|
+
args: [filename]
|
|
1190
|
+
});
|
|
1191
|
+
})();
|
|
969
1192
|
const executeStdout = await executeCmd.stdout();
|
|
970
1193
|
const executeStderr = await executeCmd.stderr();
|
|
971
1194
|
if (executeStdout) {
|
|
@@ -1005,8 +1228,9 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
1005
1228
|
logs,
|
|
1006
1229
|
executionTime
|
|
1007
1230
|
};
|
|
1008
|
-
}
|
|
1009
|
-
await
|
|
1231
|
+
} catch (innerError) {
|
|
1232
|
+
await this.removeSandbox(dependencyHash);
|
|
1233
|
+
throw innerError;
|
|
1010
1234
|
}
|
|
1011
1235
|
} catch (error) {
|
|
1012
1236
|
const executionTime = Date.now() - startTime;
|
|
@@ -1027,12 +1251,6 @@ var init_VercelSandboxExecutor = __esm({
|
|
|
1027
1251
|
};
|
|
1028
1252
|
}
|
|
1029
1253
|
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Clean up resources
|
|
1032
|
-
*/
|
|
1033
|
-
async cleanup() {
|
|
1034
|
-
logger17.info({}, "VercelSandboxExecutor cleanup completed");
|
|
1035
|
-
}
|
|
1036
1254
|
};
|
|
1037
1255
|
__publicField(_VercelSandboxExecutor, "instance");
|
|
1038
1256
|
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-QVNCS6YN.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.1",
|
|
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.1"
|
|
56
56
|
},
|
|
57
57
|
"optionalDependencies": {
|
|
58
58
|
"keytar": "^7.9.0"
|