@xpert-ai/plugin-sdk 3.8.0 → 3.8.3
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/index.cjs.js +661 -63
- package/index.esm.js +641 -64
- package/package.json +1 -1
- package/src/index.d.ts +1 -0
- package/src/lib/core/index.d.ts +1 -0
- package/src/lib/core/plugin-config.d.ts +9 -0
- package/src/lib/file/file-storage/index.d.ts +3 -0
- package/src/lib/file/file-storage/provider.decorator.d.ts +2 -0
- package/src/lib/file/file-storage/provider.interface.d.ts +14 -0
- package/src/lib/file/file-storage/provider.registry.d.ts +6 -0
- package/src/lib/file/file-upload/index.d.ts +3 -0
- package/src/lib/file/file-upload/strategy.decorator.d.ts +2 -0
- package/src/lib/file/file-upload/strategy.interface.d.ts +25 -0
- package/src/lib/file/file-upload/strategy.registry.d.ts +6 -0
- package/src/lib/file/index.d.ts +2 -0
- package/src/lib/sandbox/execution.d.ts +27 -0
- package/src/lib/sandbox/index.d.ts +1 -0
- package/src/lib/sandbox/protocol.d.ts +128 -7
- package/src/lib/sandbox/sandbox.d.ts +39 -5
- package/src/lib/types.d.ts +3 -1
package/index.esm.js
CHANGED
|
@@ -511,6 +511,7 @@ WorkflowNodeRegistry = __decorate([
|
|
|
511
511
|
])
|
|
512
512
|
], WorkflowNodeRegistry);
|
|
513
513
|
|
|
514
|
+
const COMMAND_METADATA$1 = '__command__';
|
|
514
515
|
/**
|
|
515
516
|
* Wrap Workflow Node Execution Command
|
|
516
517
|
*/ class WrapWorkflowNodeExecutionCommand extends Command {
|
|
@@ -521,6 +522,9 @@ WorkflowNodeRegistry = __decorate([
|
|
|
521
522
|
}
|
|
522
523
|
}
|
|
523
524
|
WrapWorkflowNodeExecutionCommand.type = '[Workflow] Wrap Workflow Node Execution';
|
|
525
|
+
Reflect.defineMetadata(COMMAND_METADATA$1, {
|
|
526
|
+
id: WrapWorkflowNodeExecutionCommand.type
|
|
527
|
+
}, WrapWorkflowNodeExecutionCommand);
|
|
524
528
|
|
|
525
529
|
const VECTOR_STORE_STRATEGY = 'VECTOR_STORE_STRATEGY';
|
|
526
530
|
const VectorStoreStrategy = (provider)=>applyDecorators(SetMetadata(VECTOR_STORE_STRATEGY, provider), SetMetadata(STRATEGY_META_KEY, VECTOR_STORE_STRATEGY));
|
|
@@ -932,6 +936,8 @@ BuiltinToolset.provider = '';
|
|
|
932
936
|
}
|
|
933
937
|
}
|
|
934
938
|
|
|
939
|
+
const PLUGIN_CONFIG_RESOLVER_TOKEN = 'XPERT_PLUGIN_CONFIG_RESOLVER';
|
|
940
|
+
|
|
935
941
|
/**
|
|
936
942
|
* System token for resolving integration read service from plugin context.
|
|
937
943
|
*/ const INTEGRATION_PERMISSION_SERVICE_TOKEN = 'XPERT_PLUGIN_INTEGRATION_PERMISSION_SERVICE';
|
|
@@ -2364,6 +2370,7 @@ function normalizeContextSize(value) {
|
|
|
2364
2370
|
return undefined;
|
|
2365
2371
|
}
|
|
2366
2372
|
|
|
2373
|
+
const COMMAND_METADATA = '__command__';
|
|
2367
2374
|
/**
|
|
2368
2375
|
* Get a Chat Model of copilot model and check it's token limitation, record the token usage
|
|
2369
2376
|
*/ class CreateModelClientCommand extends Command {
|
|
@@ -2374,6 +2381,9 @@ function normalizeContextSize(value) {
|
|
|
2374
2381
|
}
|
|
2375
2382
|
}
|
|
2376
2383
|
CreateModelClientCommand.type = '[AI Model] Create Model Client';
|
|
2384
|
+
Reflect.defineMetadata(COMMAND_METADATA, {
|
|
2385
|
+
id: CreateModelClientCommand.type
|
|
2386
|
+
}, CreateModelClientCommand);
|
|
2377
2387
|
|
|
2378
2388
|
const AGENT_MIDDLEWARE_STRATEGY = 'AGENT_MIDDLEWARE_STRATEGY';
|
|
2379
2389
|
const AgentMiddlewareStrategy = (provider)=>applyDecorators(SetMetadata(AGENT_MIDDLEWARE_STRATEGY, provider), SetMetadata(STRATEGY_META_KEY, AGENT_MIDDLEWARE_STRATEGY));
|
|
@@ -2607,41 +2617,167 @@ class CancelConversationCommand {
|
|
|
2607
2617
|
}
|
|
2608
2618
|
CancelConversationCommand.type = '[Chat Conversation] Cancel';
|
|
2609
2619
|
|
|
2620
|
+
const FILE_STORAGE_PROVIDER = 'FILE_STORAGE_PROVIDER';
|
|
2621
|
+
const FileStorageProvider = (provider)=>applyDecorators(SetMetadata(FILE_STORAGE_PROVIDER, provider), SetMetadata(STRATEGY_META_KEY, FILE_STORAGE_PROVIDER));
|
|
2622
|
+
|
|
2623
|
+
let FileStorageProviderRegistry = class FileStorageProviderRegistry extends BaseStrategyRegistry {
|
|
2624
|
+
constructor(discoveryService, reflector){
|
|
2625
|
+
super(FILE_STORAGE_PROVIDER, discoveryService, reflector);
|
|
2626
|
+
}
|
|
2627
|
+
};
|
|
2628
|
+
FileStorageProviderRegistry = __decorate([
|
|
2629
|
+
Injectable(),
|
|
2630
|
+
__metadata("design:type", Function),
|
|
2631
|
+
__metadata("design:paramtypes", [
|
|
2632
|
+
typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
|
|
2633
|
+
typeof Reflector === "undefined" ? Object : Reflector
|
|
2634
|
+
])
|
|
2635
|
+
], FileStorageProviderRegistry);
|
|
2636
|
+
|
|
2637
|
+
const FILE_UPLOAD_TARGET_STRATEGY = 'FILE_UPLOAD_TARGET_STRATEGY';
|
|
2638
|
+
const FileUploadTargetStrategy = (type)=>applyDecorators(SetMetadata(FILE_UPLOAD_TARGET_STRATEGY, type), SetMetadata(STRATEGY_META_KEY, FILE_UPLOAD_TARGET_STRATEGY));
|
|
2639
|
+
|
|
2640
|
+
let FileUploadTargetRegistry = class FileUploadTargetRegistry extends BaseStrategyRegistry {
|
|
2641
|
+
constructor(discoveryService, reflector){
|
|
2642
|
+
super(FILE_UPLOAD_TARGET_STRATEGY, discoveryService, reflector);
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
FileUploadTargetRegistry = __decorate([
|
|
2646
|
+
Injectable(),
|
|
2647
|
+
__metadata("design:type", Function),
|
|
2648
|
+
__metadata("design:paramtypes", [
|
|
2649
|
+
typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
|
|
2650
|
+
typeof Reflector === "undefined" ? Object : Reflector
|
|
2651
|
+
])
|
|
2652
|
+
], FileUploadTargetRegistry);
|
|
2653
|
+
|
|
2654
|
+
const SANDBOX_SHELL_TIMEOUT_LIMITS_SEC = {
|
|
2655
|
+
min: 1,
|
|
2656
|
+
max: 3600
|
|
2657
|
+
};
|
|
2658
|
+
const DEFAULT_SANDBOX_SHELL_TIMEOUT_SEC = 600;
|
|
2659
|
+
const DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_SEC = 120;
|
|
2660
|
+
const DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_SEC = 120;
|
|
2661
|
+
const DEFAULT_SANDBOX_SHELL_TIMEOUT_MS = DEFAULT_SANDBOX_SHELL_TIMEOUT_SEC * 1000;
|
|
2662
|
+
const DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_MS = DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_SEC * 1000;
|
|
2663
|
+
const DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_MS = DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_SEC * 1000;
|
|
2664
|
+
const DEFAULT_SANDBOX_EXECUTION_MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
2665
|
+
const DEFAULT_SANDBOX_SHELL_EXECUTION_OPTIONS = {
|
|
2666
|
+
timeoutMs: DEFAULT_SANDBOX_SHELL_TIMEOUT_MS,
|
|
2667
|
+
maxOutputBytes: DEFAULT_SANDBOX_EXECUTION_MAX_OUTPUT_BYTES
|
|
2668
|
+
};
|
|
2669
|
+
const DEFAULT_SANDBOX_FILE_SEARCH_EXECUTION_OPTIONS = {
|
|
2670
|
+
timeoutMs: DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_MS,
|
|
2671
|
+
maxOutputBytes: DEFAULT_SANDBOX_EXECUTION_MAX_OUTPUT_BYTES
|
|
2672
|
+
};
|
|
2673
|
+
const DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS = {
|
|
2674
|
+
timeoutMs: DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_MS,
|
|
2675
|
+
maxOutputBytes: DEFAULT_SANDBOX_EXECUTION_MAX_OUTPUT_BYTES
|
|
2676
|
+
};
|
|
2677
|
+
function secondsToMilliseconds(seconds) {
|
|
2678
|
+
return Math.trunc(seconds * 1000);
|
|
2679
|
+
}
|
|
2680
|
+
function formatSandboxTimeout(timeoutMs) {
|
|
2681
|
+
const timeoutSeconds = timeoutMs / 1000;
|
|
2682
|
+
const secondsLabel = Number.isInteger(timeoutSeconds) ? String(timeoutSeconds) : Number(timeoutSeconds.toFixed(3)).toString();
|
|
2683
|
+
return `${secondsLabel}s (${timeoutMs}ms)`;
|
|
2684
|
+
}
|
|
2685
|
+
function buildSandboxTimeoutMessage(subject, timeoutMs) {
|
|
2686
|
+
return `${subject} timed out after ${formatSandboxTimeout(timeoutMs)}`;
|
|
2687
|
+
}
|
|
2688
|
+
function appendSandboxMessage(output, message) {
|
|
2689
|
+
return output ? `${output}\n${message}` : message;
|
|
2690
|
+
}
|
|
2691
|
+
function resolveSandboxExecutionOptions(options, defaults = DEFAULT_SANDBOX_SHELL_EXECUTION_OPTIONS) {
|
|
2692
|
+
const timeoutMs = typeof (options == null ? void 0 : options.timeoutMs) === 'number' && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? Math.trunc(options.timeoutMs) : typeof defaults.timeoutMs === 'number' && Number.isFinite(defaults.timeoutMs) && defaults.timeoutMs > 0 ? Math.trunc(defaults.timeoutMs) : DEFAULT_SANDBOX_SHELL_TIMEOUT_MS;
|
|
2693
|
+
const maxOutputBytes = typeof (options == null ? void 0 : options.maxOutputBytes) === 'number' && Number.isFinite(options.maxOutputBytes) && options.maxOutputBytes > 0 ? Math.trunc(options.maxOutputBytes) : typeof defaults.maxOutputBytes === 'number' && Number.isFinite(defaults.maxOutputBytes) && defaults.maxOutputBytes > 0 ? Math.trunc(defaults.maxOutputBytes) : DEFAULT_SANDBOX_EXECUTION_MAX_OUTPUT_BYTES;
|
|
2694
|
+
return {
|
|
2695
|
+
timeoutMs,
|
|
2696
|
+
maxOutputBytes
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2610
2700
|
/**
|
|
2611
|
-
* Protocol definition for pluggable memory backends.
|
|
2612
|
-
*
|
|
2613
|
-
* This module defines the BackendProtocol that all backend implementations
|
|
2614
|
-
* must follow. Backends can store files in different locations (state, filesystem,
|
|
2615
|
-
* database, etc.) and provide a uniform interface for file operations.
|
|
2616
|
-
*/ /**
|
|
2617
2701
|
* Type guard to check if a backend supports execution.
|
|
2618
2702
|
*
|
|
2619
2703
|
* @param backend - Backend instance to check
|
|
2620
2704
|
* @returns True if the backend implements SandboxBackendProtocol
|
|
2621
2705
|
*/ function isSandboxBackend(backend) {
|
|
2622
|
-
return typeof backend.execute ===
|
|
2706
|
+
return typeof backend.execute === 'function' && typeof backend.id === 'string';
|
|
2623
2707
|
}
|
|
2624
2708
|
|
|
2709
|
+
const MAX_LINE_LENGTH = 500;
|
|
2710
|
+
const MAX_GREP_LINE_LENGTH = 2000;
|
|
2711
|
+
const GREP_RESULT_LIMIT = 100;
|
|
2712
|
+
const GLOB_RESULT_LIMIT = 100;
|
|
2713
|
+
const WRITE_EXISTS_OUTPUT = 'Error: File already exists';
|
|
2714
|
+
/**
|
|
2715
|
+
* UTF-8 safe Base64 encoding function.
|
|
2716
|
+
* Replaces btoa() which only supports Latin1 characters.
|
|
2717
|
+
*/ function utf8ToBase64(str) {
|
|
2718
|
+
return Buffer.from(str, 'utf-8').toString('base64');
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Format glob results into human-readable output.
|
|
2722
|
+
*/ function formatGlobOutput(files, pattern) {
|
|
2723
|
+
const limit = GLOB_RESULT_LIMIT;
|
|
2724
|
+
const truncated = files.length > limit;
|
|
2725
|
+
const finalFiles = truncated ? files.slice(0, limit) : files;
|
|
2726
|
+
if (finalFiles.length === 0) {
|
|
2727
|
+
return 'No files found';
|
|
2728
|
+
}
|
|
2729
|
+
const lines = [];
|
|
2730
|
+
for (const file of finalFiles){
|
|
2731
|
+
lines.push(file.path);
|
|
2732
|
+
}
|
|
2733
|
+
if (truncated) {
|
|
2734
|
+
lines.push('');
|
|
2735
|
+
lines.push('(Results truncated. Consider using a more specific path or pattern.)');
|
|
2736
|
+
}
|
|
2737
|
+
return lines.join('\n');
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Format grep matches into human-readable output grouped by file.
|
|
2741
|
+
*/ function formatGrepOutput(matches) {
|
|
2742
|
+
const limit = GREP_RESULT_LIMIT;
|
|
2743
|
+
const truncated = matches.length > limit;
|
|
2744
|
+
const finalMatches = truncated ? matches.slice(0, limit) : matches;
|
|
2745
|
+
if (finalMatches.length === 0) {
|
|
2746
|
+
return 'No matches found';
|
|
2747
|
+
}
|
|
2748
|
+
const lines = [
|
|
2749
|
+
`Found ${finalMatches.length} matches`
|
|
2750
|
+
];
|
|
2751
|
+
let currentFile = '';
|
|
2752
|
+
for (const match of finalMatches){
|
|
2753
|
+
if (currentFile !== match.path) {
|
|
2754
|
+
if (currentFile !== '') {
|
|
2755
|
+
lines.push('');
|
|
2756
|
+
}
|
|
2757
|
+
currentFile = match.path;
|
|
2758
|
+
lines.push(`${match.path}:`);
|
|
2759
|
+
}
|
|
2760
|
+
const text = match.text.length > MAX_GREP_LINE_LENGTH ? match.text.substring(0, MAX_GREP_LINE_LENGTH) + '...' : match.text;
|
|
2761
|
+
lines.push(` Line ${match.line}: ${text}`);
|
|
2762
|
+
}
|
|
2763
|
+
if (truncated) {
|
|
2764
|
+
lines.push('');
|
|
2765
|
+
lines.push('(Results truncated. Consider using a more specific path or pattern.)');
|
|
2766
|
+
}
|
|
2767
|
+
return lines.join('\n');
|
|
2768
|
+
}
|
|
2625
2769
|
/**
|
|
2626
|
-
* BaseSandbox: Abstract base class for sandbox backends with command execution.
|
|
2627
|
-
*
|
|
2628
|
-
* This class provides default implementations for all SandboxBackendProtocol
|
|
2629
|
-
* methods using shell commands executed via execute(). Concrete implementations
|
|
2630
|
-
* only need to implement the execute() method.
|
|
2631
|
-
*
|
|
2632
|
-
* Requires Node.js 20+ on the sandbox host.
|
|
2633
|
-
*/ /**
|
|
2634
2770
|
* Node.js command template for glob operations.
|
|
2635
2771
|
* Uses web-standard atob() for base64 decoding.
|
|
2636
2772
|
*/ function buildGlobCommand(searchPath, pattern) {
|
|
2637
|
-
const pathB64 =
|
|
2638
|
-
const patternB64 =
|
|
2773
|
+
const pathB64 = utf8ToBase64(searchPath);
|
|
2774
|
+
const patternB64 = utf8ToBase64(pattern);
|
|
2639
2775
|
return `node -e "
|
|
2640
2776
|
const fs = require('fs');
|
|
2641
2777
|
const path = require('path');
|
|
2642
2778
|
|
|
2643
|
-
const searchPath =
|
|
2644
|
-
const pattern =
|
|
2779
|
+
const searchPath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2780
|
+
const pattern = Buffer.from('${patternB64}', 'base64').toString('utf-8');
|
|
2645
2781
|
|
|
2646
2782
|
function globMatch(relativePath, pattern) {
|
|
2647
2783
|
const regexPattern = pattern
|
|
@@ -2686,12 +2822,12 @@ try {
|
|
|
2686
2822
|
/**
|
|
2687
2823
|
* Node.js command template for listing directory contents.
|
|
2688
2824
|
*/ function buildLsCommand(dirPath) {
|
|
2689
|
-
const pathB64 =
|
|
2825
|
+
const pathB64 = utf8ToBase64(dirPath);
|
|
2690
2826
|
return `node -e "
|
|
2691
2827
|
const fs = require('fs');
|
|
2692
2828
|
const path = require('path');
|
|
2693
2829
|
|
|
2694
|
-
const dirPath =
|
|
2830
|
+
const dirPath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2695
2831
|
|
|
2696
2832
|
try {
|
|
2697
2833
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
@@ -2711,17 +2847,129 @@ try {
|
|
|
2711
2847
|
}
|
|
2712
2848
|
"`;
|
|
2713
2849
|
}
|
|
2850
|
+
const INDENTATION_SPACES = 2;
|
|
2851
|
+
const MAX_ENTRY_LENGTH = 500;
|
|
2852
|
+
/**
|
|
2853
|
+
* Node.js command template for recursive directory listing with depth control.
|
|
2854
|
+
*/ function buildListDirCommand(dirPath, offset, limit, depth) {
|
|
2855
|
+
const pathB64 = utf8ToBase64(dirPath);
|
|
2856
|
+
const safeOffset = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 1;
|
|
2857
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 25;
|
|
2858
|
+
const safeDepth = Number.isFinite(depth) && depth > 0 ? Math.floor(depth) : 2;
|
|
2859
|
+
return `node -e "
|
|
2860
|
+
const fs = require('fs');
|
|
2861
|
+
const path = require('path');
|
|
2862
|
+
|
|
2863
|
+
const dirPath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2864
|
+
const offset = ${safeOffset};
|
|
2865
|
+
const limit = ${safeLimit};
|
|
2866
|
+
const maxDepth = ${safeDepth};
|
|
2867
|
+
const INDENTATION_SPACES = ${INDENTATION_SPACES};
|
|
2868
|
+
const MAX_ENTRY_LENGTH = ${MAX_ENTRY_LENGTH};
|
|
2869
|
+
|
|
2870
|
+
if (!path.isAbsolute(dirPath)) {
|
|
2871
|
+
console.error('Error: dir_path must be an absolute path');
|
|
2872
|
+
process.exit(1);
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
if (!fs.existsSync(dirPath)) {
|
|
2876
|
+
console.error('Error: Directory not found');
|
|
2877
|
+
process.exit(1);
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
if (!fs.statSync(dirPath).isDirectory()) {
|
|
2881
|
+
console.error('Error: Path is not a directory');
|
|
2882
|
+
process.exit(1);
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
function collectEntries(currentPath, relativePrefix, remainingDepth, entries) {
|
|
2886
|
+
if (remainingDepth === 0) return;
|
|
2887
|
+
|
|
2888
|
+
try {
|
|
2889
|
+
const dirEntries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
2890
|
+
const sortedEntries = dirEntries
|
|
2891
|
+
.map(entry => {
|
|
2892
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
2893
|
+
const relativePath = path.join(relativePrefix, entry.name);
|
|
2894
|
+
const displayName = entry.name.length > MAX_ENTRY_LENGTH
|
|
2895
|
+
? entry.name.substring(0, MAX_ENTRY_LENGTH)
|
|
2896
|
+
: entry.name;
|
|
2897
|
+
|
|
2898
|
+
let kind = 'file';
|
|
2899
|
+
if (entry.isSymbolicLink()) kind = 'symlink';
|
|
2900
|
+
else if (entry.isDirectory()) kind = 'directory';
|
|
2901
|
+
|
|
2902
|
+
return {
|
|
2903
|
+
fullPath,
|
|
2904
|
+
relativePath,
|
|
2905
|
+
displayName,
|
|
2906
|
+
kind,
|
|
2907
|
+
depth: relativePrefix.split(path.sep).filter(Boolean).length
|
|
2908
|
+
};
|
|
2909
|
+
})
|
|
2910
|
+
.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
2911
|
+
|
|
2912
|
+
for (const entry of sortedEntries) {
|
|
2913
|
+
entries.push(entry);
|
|
2914
|
+
if (entry.kind === 'directory' && remainingDepth > 1) {
|
|
2915
|
+
collectEntries(entry.fullPath, entry.relativePath, remainingDepth - 1, entries);
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
} catch (e) {
|
|
2919
|
+
// Skip unreadable directories
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
const allEntries = [];
|
|
2924
|
+
collectEntries(dirPath, '', maxDepth, allEntries);
|
|
2925
|
+
|
|
2926
|
+
if (allEntries.length === 0) {
|
|
2927
|
+
console.log('Absolute path: ' + dirPath);
|
|
2928
|
+
console.log('(Empty directory)');
|
|
2929
|
+
process.exit(0);
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
const startIndex = offset - 1;
|
|
2933
|
+
if (startIndex >= allEntries.length) {
|
|
2934
|
+
console.error('Error: offset exceeds directory entry count');
|
|
2935
|
+
process.exit(1);
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
const endIndex = Math.min(startIndex + limit, allEntries.length);
|
|
2939
|
+
const selectedEntries = allEntries.slice(startIndex, endIndex);
|
|
2940
|
+
|
|
2941
|
+
console.log('Absolute path: ' + dirPath);
|
|
2942
|
+
|
|
2943
|
+
for (const entry of selectedEntries) {
|
|
2944
|
+
const indent = ' '.repeat(entry.depth * INDENTATION_SPACES);
|
|
2945
|
+
let name = entry.displayName;
|
|
2946
|
+
if (entry.kind === 'directory') name += '/';
|
|
2947
|
+
else if (entry.kind === 'symlink') name += '@';
|
|
2948
|
+
console.log(indent + name);
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
if (endIndex < allEntries.length) {
|
|
2952
|
+
console.log('More than ' + limit + ' entries found');
|
|
2953
|
+
}
|
|
2954
|
+
"`;
|
|
2955
|
+
}
|
|
2956
|
+
const TAB_WIDTH = 4;
|
|
2957
|
+
const COMMENT_PREFIXES = [
|
|
2958
|
+
'#',
|
|
2959
|
+
'//',
|
|
2960
|
+
'--'
|
|
2961
|
+
];
|
|
2714
2962
|
/**
|
|
2715
|
-
* Node.js command template for reading
|
|
2716
|
-
*/ function
|
|
2717
|
-
const pathB64 =
|
|
2718
|
-
|
|
2719
|
-
const
|
|
2720
|
-
const safeLimit = Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER ? Math.floor(limit) : 0;
|
|
2963
|
+
* Node.js command template for slice mode reading.
|
|
2964
|
+
*/ function buildSliceReadCommand(filePath, offset, limit) {
|
|
2965
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
2966
|
+
const safeOffset = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) - 1 : 0;
|
|
2967
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER ? Math.floor(limit) : 2000;
|
|
2721
2968
|
return `node -e "
|
|
2722
2969
|
const fs = require('fs');
|
|
2970
|
+
const MAX_LINE_LENGTH = ${MAX_LINE_LENGTH};
|
|
2723
2971
|
|
|
2724
|
-
const filePath =
|
|
2972
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2725
2973
|
const offset = ${safeOffset};
|
|
2726
2974
|
const limit = ${safeLimit};
|
|
2727
2975
|
|
|
@@ -2738,28 +2986,188 @@ if (stat.size === 0) {
|
|
|
2738
2986
|
|
|
2739
2987
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
2740
2988
|
const lines = content.split('\\n');
|
|
2989
|
+
|
|
2990
|
+
if (offset >= lines.length) {
|
|
2991
|
+
console.log('Error: offset exceeds file length');
|
|
2992
|
+
process.exit(1);
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2741
2995
|
const selected = lines.slice(offset, offset + limit);
|
|
2742
2996
|
|
|
2743
2997
|
for (let i = 0; i < selected.length; i++) {
|
|
2744
2998
|
const lineNum = offset + i + 1;
|
|
2745
|
-
|
|
2999
|
+
let line = selected[i];
|
|
3000
|
+
if (line.length > MAX_LINE_LENGTH) {
|
|
3001
|
+
line = line.substring(0, MAX_LINE_LENGTH);
|
|
3002
|
+
}
|
|
3003
|
+
console.log('L' + lineNum + ': ' + line);
|
|
3004
|
+
}
|
|
3005
|
+
"`;
|
|
3006
|
+
}
|
|
3007
|
+
/**
|
|
3008
|
+
* Node.js command template for indentation mode reading.
|
|
3009
|
+
*/ function buildIndentationReadCommand(filePath, offset, limit, options) {
|
|
3010
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3011
|
+
const safeOffset = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 1;
|
|
3012
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER ? Math.floor(limit) : 2000;
|
|
3013
|
+
var _options_anchor_line;
|
|
3014
|
+
const anchorLine = (_options_anchor_line = options.anchor_line) != null ? _options_anchor_line : safeOffset;
|
|
3015
|
+
var _options_max_levels;
|
|
3016
|
+
const maxLevels = (_options_max_levels = options.max_levels) != null ? _options_max_levels : 0;
|
|
3017
|
+
var _options_include_siblings;
|
|
3018
|
+
const includeSiblings = (_options_include_siblings = options.include_siblings) != null ? _options_include_siblings : false;
|
|
3019
|
+
var _options_include_header;
|
|
3020
|
+
const includeHeader = (_options_include_header = options.include_header) != null ? _options_include_header : true;
|
|
3021
|
+
var _options_max_lines;
|
|
3022
|
+
const maxLines = (_options_max_lines = options.max_lines) != null ? _options_max_lines : safeLimit;
|
|
3023
|
+
return `node -e "
|
|
3024
|
+
const fs = require('fs');
|
|
3025
|
+
const MAX_LINE_LENGTH = ${MAX_LINE_LENGTH};
|
|
3026
|
+
const TAB_WIDTH = ${TAB_WIDTH};
|
|
3027
|
+
const COMMENT_PREFIXES = ${JSON.stringify(COMMENT_PREFIXES)};
|
|
3028
|
+
|
|
3029
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3030
|
+
const anchorLine = ${anchorLine};
|
|
3031
|
+
const limit = ${safeLimit};
|
|
3032
|
+
const maxLevels = ${maxLevels};
|
|
3033
|
+
const includeSiblings = ${includeSiblings};
|
|
3034
|
+
const includeHeader = ${includeHeader};
|
|
3035
|
+
const maxLines = ${maxLines};
|
|
3036
|
+
|
|
3037
|
+
if (!fs.existsSync(filePath)) {
|
|
3038
|
+
console.log('Error: File not found');
|
|
3039
|
+
process.exit(1);
|
|
3040
|
+
}
|
|
3041
|
+
|
|
3042
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
3043
|
+
const rawLines = content.split('\\n');
|
|
3044
|
+
|
|
3045
|
+
if (rawLines.length === 0 || anchorLine > rawLines.length) {
|
|
3046
|
+
console.log('Error: anchor_line exceeds file length');
|
|
3047
|
+
process.exit(1);
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
function measureIndent(line) {
|
|
3051
|
+
let indent = 0;
|
|
3052
|
+
for (const c of line) {
|
|
3053
|
+
if (c === ' ') indent++;
|
|
3054
|
+
else if (c === '\\t') indent += TAB_WIDTH;
|
|
3055
|
+
else break;
|
|
3056
|
+
}
|
|
3057
|
+
return indent;
|
|
3058
|
+
}
|
|
3059
|
+
|
|
3060
|
+
function isBlank(line) { return line.trim() === ''; }
|
|
3061
|
+
function isComment(line) { return COMMENT_PREFIXES.some(p => line.trim().startsWith(p)); }
|
|
3062
|
+
function formatLine(line) { return line.length > MAX_LINE_LENGTH ? line.substring(0, MAX_LINE_LENGTH) : line; }
|
|
3063
|
+
|
|
3064
|
+
const records = rawLines.map((raw, i) => ({
|
|
3065
|
+
number: i + 1,
|
|
3066
|
+
raw,
|
|
3067
|
+
display: formatLine(raw),
|
|
3068
|
+
indent: measureIndent(raw)
|
|
3069
|
+
}));
|
|
3070
|
+
|
|
3071
|
+
const effectiveIndents = [];
|
|
3072
|
+
let prevIndent = 0;
|
|
3073
|
+
for (const r of records) {
|
|
3074
|
+
if (isBlank(r.raw)) {
|
|
3075
|
+
effectiveIndents.push(prevIndent);
|
|
3076
|
+
} else {
|
|
3077
|
+
prevIndent = r.indent;
|
|
3078
|
+
effectiveIndents.push(prevIndent);
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
const anchorIndex = anchorLine - 1;
|
|
3083
|
+
const anchorIndent = effectiveIndents[anchorIndex];
|
|
3084
|
+
const minIndent = maxLevels === 0 ? 0 : Math.max(0, anchorIndent - maxLevels * TAB_WIDTH);
|
|
3085
|
+
const finalLimit = Math.min(limit, maxLines, records.length);
|
|
3086
|
+
|
|
3087
|
+
if (finalLimit === 1) {
|
|
3088
|
+
console.log('L' + records[anchorIndex].number + ': ' + records[anchorIndex].display);
|
|
3089
|
+
process.exit(0);
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
const out = [records[anchorIndex]];
|
|
3093
|
+
let i = anchorIndex - 1;
|
|
3094
|
+
let j = anchorIndex + 1;
|
|
3095
|
+
let iCounterMinIndent = 0;
|
|
3096
|
+
let jCounterMinIndent = 0;
|
|
3097
|
+
|
|
3098
|
+
while (out.length < finalLimit) {
|
|
3099
|
+
let progressed = 0;
|
|
3100
|
+
|
|
3101
|
+
if (i >= 0) {
|
|
3102
|
+
if (effectiveIndents[i] >= minIndent) {
|
|
3103
|
+
out.unshift(records[i]);
|
|
3104
|
+
progressed++;
|
|
3105
|
+
const curI = i;
|
|
3106
|
+
i--;
|
|
3107
|
+
|
|
3108
|
+
if (effectiveIndents[curI] === minIndent && !includeSiblings) {
|
|
3109
|
+
const allowHeaderComment = includeHeader && isComment(records[curI].raw);
|
|
3110
|
+
const canTakeLine = allowHeaderComment || iCounterMinIndent === 0;
|
|
3111
|
+
if (canTakeLine) {
|
|
3112
|
+
iCounterMinIndent++;
|
|
3113
|
+
} else {
|
|
3114
|
+
out.shift();
|
|
3115
|
+
progressed--;
|
|
3116
|
+
i = -1;
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
if (out.length >= finalLimit) break;
|
|
3121
|
+
} else {
|
|
3122
|
+
i = -1;
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
if (j < records.length) {
|
|
3127
|
+
if (effectiveIndents[j] >= minIndent) {
|
|
3128
|
+
out.push(records[j]);
|
|
3129
|
+
progressed++;
|
|
3130
|
+
const curJ = j;
|
|
3131
|
+
j++;
|
|
3132
|
+
|
|
3133
|
+
if (effectiveIndents[curJ] === minIndent && !includeSiblings) {
|
|
3134
|
+
if (jCounterMinIndent > 0) {
|
|
3135
|
+
out.pop();
|
|
3136
|
+
progressed--;
|
|
3137
|
+
j = records.length;
|
|
3138
|
+
}
|
|
3139
|
+
jCounterMinIndent++;
|
|
3140
|
+
}
|
|
3141
|
+
} else {
|
|
3142
|
+
j = records.length;
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
if (progressed === 0) break;
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
while (out.length > 0 && isBlank(out[0].raw)) out.shift();
|
|
3150
|
+
while (out.length > 0 && isBlank(out[out.length - 1].raw)) out.pop();
|
|
3151
|
+
|
|
3152
|
+
for (const r of out) {
|
|
3153
|
+
console.log('L' + r.number + ': ' + r.display);
|
|
2746
3154
|
}
|
|
2747
3155
|
"`;
|
|
2748
3156
|
}
|
|
2749
3157
|
/**
|
|
2750
3158
|
* Node.js command template for writing files.
|
|
2751
3159
|
*/ function buildWriteCommand(filePath, content) {
|
|
2752
|
-
const pathB64 =
|
|
2753
|
-
const contentB64 =
|
|
3160
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3161
|
+
const contentB64 = utf8ToBase64(content);
|
|
2754
3162
|
return `node -e "
|
|
2755
3163
|
const fs = require('fs');
|
|
2756
3164
|
const path = require('path');
|
|
2757
3165
|
|
|
2758
|
-
const filePath =
|
|
2759
|
-
const content =
|
|
3166
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3167
|
+
const content = Buffer.from('${contentB64}', 'base64').toString('utf-8');
|
|
2760
3168
|
|
|
2761
3169
|
if (fs.existsSync(filePath)) {
|
|
2762
|
-
console.error('
|
|
3170
|
+
console.error('${WRITE_EXISTS_OUTPUT}');
|
|
2763
3171
|
process.exit(1);
|
|
2764
3172
|
}
|
|
2765
3173
|
|
|
@@ -2770,18 +3178,45 @@ fs.writeFileSync(filePath, content, 'utf-8');
|
|
|
2770
3178
|
console.log('OK');
|
|
2771
3179
|
"`;
|
|
2772
3180
|
}
|
|
3181
|
+
function buildExistsCommand(filePath) {
|
|
3182
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3183
|
+
return `node -e "
|
|
3184
|
+
const fs = require('fs');
|
|
3185
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3186
|
+
process.exit(fs.existsSync(filePath) ? 0 : 1);
|
|
3187
|
+
"`;
|
|
3188
|
+
}
|
|
3189
|
+
/**
|
|
3190
|
+
* Node.js command template for appending to files.
|
|
3191
|
+
*/ function buildAppendCommand(filePath, content) {
|
|
3192
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3193
|
+
const contentB64 = utf8ToBase64(content);
|
|
3194
|
+
return `node -e "
|
|
3195
|
+
const fs = require('fs');
|
|
3196
|
+
const path = require('path');
|
|
3197
|
+
|
|
3198
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3199
|
+
const content = Buffer.from('${contentB64}', 'base64').toString('utf-8');
|
|
3200
|
+
|
|
3201
|
+
const parentDir = path.dirname(filePath) || '.';
|
|
3202
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
3203
|
+
|
|
3204
|
+
fs.appendFileSync(filePath, content, 'utf-8');
|
|
3205
|
+
console.log('OK');
|
|
3206
|
+
"`;
|
|
3207
|
+
}
|
|
2773
3208
|
/**
|
|
2774
3209
|
* Node.js command template for editing files.
|
|
2775
3210
|
*/ function buildEditCommand(filePath, oldStr, newStr, replaceAll) {
|
|
2776
|
-
const pathB64 =
|
|
2777
|
-
const oldB64 =
|
|
2778
|
-
const newB64 =
|
|
3211
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3212
|
+
const oldB64 = utf8ToBase64(oldStr);
|
|
3213
|
+
const newB64 = utf8ToBase64(newStr);
|
|
2779
3214
|
return `node -e "
|
|
2780
3215
|
const fs = require('fs');
|
|
2781
3216
|
|
|
2782
|
-
const filePath =
|
|
2783
|
-
const oldStr =
|
|
2784
|
-
const newStr =
|
|
3217
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3218
|
+
const oldStr = Buffer.from('${oldB64}', 'base64').toString('utf-8');
|
|
3219
|
+
const newStr = Buffer.from('${newB64}', 'base64').toString('utf-8');
|
|
2785
3220
|
const replaceAll = ${Boolean(replaceAll)};
|
|
2786
3221
|
|
|
2787
3222
|
let text;
|
|
@@ -2808,16 +3243,16 @@ console.log(count);
|
|
|
2808
3243
|
/**
|
|
2809
3244
|
* Node.js command template for grep operations.
|
|
2810
3245
|
*/ function buildGrepCommand(pattern, searchPath, globPattern) {
|
|
2811
|
-
const patternB64 =
|
|
2812
|
-
const pathB64 =
|
|
2813
|
-
const globB64 = globPattern ?
|
|
3246
|
+
const patternB64 = utf8ToBase64(pattern);
|
|
3247
|
+
const pathB64 = utf8ToBase64(searchPath);
|
|
3248
|
+
const globB64 = globPattern ? utf8ToBase64(globPattern) : '';
|
|
2814
3249
|
return `node -e "
|
|
2815
3250
|
const fs = require('fs');
|
|
2816
3251
|
const path = require('path');
|
|
2817
3252
|
|
|
2818
|
-
const pattern =
|
|
2819
|
-
const searchPath =
|
|
2820
|
-
const globPattern = ${globPattern ? `
|
|
3253
|
+
const pattern = Buffer.from('${patternB64}', 'base64').toString('utf-8');
|
|
3254
|
+
const searchPath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3255
|
+
const globPattern = ${globPattern ? `Buffer.from('${globB64}', 'base64').toString('utf-8')` : 'null'};
|
|
2821
3256
|
|
|
2822
3257
|
let regex;
|
|
2823
3258
|
try {
|
|
@@ -2886,6 +3321,13 @@ try {
|
|
|
2886
3321
|
*
|
|
2887
3322
|
* Requires Node.js 20+ on the sandbox host.
|
|
2888
3323
|
*/ class BaseSandbox {
|
|
3324
|
+
async streamExecute(command, onLine, options) {
|
|
3325
|
+
const result = await this.execute(command, options);
|
|
3326
|
+
if (result.output) {
|
|
3327
|
+
onLine(result.output);
|
|
3328
|
+
}
|
|
3329
|
+
return result;
|
|
3330
|
+
}
|
|
2889
3331
|
/**
|
|
2890
3332
|
* List files and directories in the specified directory (non-recursive).
|
|
2891
3333
|
*
|
|
@@ -2893,12 +3335,12 @@ try {
|
|
|
2893
3335
|
* @returns List of FileInfo objects for files and directories directly in the directory.
|
|
2894
3336
|
*/ async lsInfo(path) {
|
|
2895
3337
|
const command = buildLsCommand(path);
|
|
2896
|
-
const result = await this.execute(command);
|
|
3338
|
+
const result = await this.execute(command, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS);
|
|
2897
3339
|
if (result.exitCode !== 0) {
|
|
2898
3340
|
return [];
|
|
2899
3341
|
}
|
|
2900
3342
|
const infos = [];
|
|
2901
|
-
const lines = result.output.trim().split(
|
|
3343
|
+
const lines = result.output.trim().split('\n').filter(Boolean);
|
|
2902
3344
|
for (const line of lines){
|
|
2903
3345
|
try {
|
|
2904
3346
|
const parsed = JSON.parse(line);
|
|
@@ -2915,33 +3357,60 @@ try {
|
|
|
2915
3357
|
return infos;
|
|
2916
3358
|
}
|
|
2917
3359
|
/**
|
|
3360
|
+
* List directory contents recursively with depth control and pagination.
|
|
3361
|
+
*
|
|
3362
|
+
* @param dirPath - Absolute path to directory
|
|
3363
|
+
* @param offset - 1-indexed entry number to start from (default: 1)
|
|
3364
|
+
* @param limit - Maximum number of entries to return (default: 25)
|
|
3365
|
+
* @param depth - Maximum depth to traverse (default: 2)
|
|
3366
|
+
* @returns Human-readable directory tree with indentation
|
|
3367
|
+
*/ async listDir(dirPath, offset = 1, limit = 25, depth = 2) {
|
|
3368
|
+
if (offset < 1) {
|
|
3369
|
+
return 'Error: offset must be a 1-indexed entry number';
|
|
3370
|
+
}
|
|
3371
|
+
if (limit < 1) {
|
|
3372
|
+
return 'Error: limit must be greater than zero';
|
|
3373
|
+
}
|
|
3374
|
+
if (depth < 1) {
|
|
3375
|
+
return 'Error: depth must be greater than zero';
|
|
3376
|
+
}
|
|
3377
|
+
const command = buildListDirCommand(dirPath, offset, limit, depth);
|
|
3378
|
+
const result = await this.execute(command, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS);
|
|
3379
|
+
if (result.exitCode !== 0) {
|
|
3380
|
+
return result.output || `Error: Failed to list directory '${dirPath}'`;
|
|
3381
|
+
}
|
|
3382
|
+
return result.output;
|
|
3383
|
+
}
|
|
3384
|
+
/**
|
|
2918
3385
|
* Read file content with line numbers.
|
|
2919
3386
|
*
|
|
2920
3387
|
* @param filePath - Absolute file path
|
|
2921
|
-
* @param offset - Line offset to start reading from (
|
|
3388
|
+
* @param offset - Line offset to start reading from (1-indexed)
|
|
2922
3389
|
* @param limit - Maximum number of lines to read
|
|
3390
|
+
* @param mode - Read mode: 'slice' (default) or 'indentation'
|
|
3391
|
+
* @param indentation - Configuration for indentation mode
|
|
2923
3392
|
* @returns Formatted file content with line numbers, or error message
|
|
2924
|
-
*/ async read(filePath, offset =
|
|
2925
|
-
const command =
|
|
2926
|
-
const result = await this.execute(command);
|
|
3393
|
+
*/ async read(filePath, offset = 1, limit = 2000, mode = 'slice', indentation) {
|
|
3394
|
+
const command = mode === 'indentation' ? buildIndentationReadCommand(filePath, offset, limit, indentation != null ? indentation : {}) : buildSliceReadCommand(filePath, offset, limit);
|
|
3395
|
+
const result = await this.execute(command, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS);
|
|
2927
3396
|
if (result.exitCode !== 0) {
|
|
2928
|
-
return `Error: File '${filePath}' not found`;
|
|
3397
|
+
return result.output || `Error: File '${filePath}' not found`;
|
|
2929
3398
|
}
|
|
2930
3399
|
return result.output;
|
|
2931
3400
|
}
|
|
2932
3401
|
/**
|
|
2933
3402
|
* Structured search results or error string for invalid input.
|
|
2934
|
-
*/ async grepRaw(pattern, path =
|
|
2935
|
-
const command = buildGrepCommand(pattern, path,
|
|
2936
|
-
const result = await this.execute(command);
|
|
3403
|
+
*/ async grepRaw(pattern, path = '/', include = null) {
|
|
3404
|
+
const command = buildGrepCommand(pattern, path, include);
|
|
3405
|
+
const result = await this.execute(command, DEFAULT_SANDBOX_FILE_SEARCH_EXECUTION_OPTIONS);
|
|
2937
3406
|
if (result.exitCode === 1) {
|
|
2938
3407
|
// Check if it's a regex error
|
|
2939
|
-
if (result.output.includes(
|
|
3408
|
+
if (result.output.includes('Invalid regex:')) {
|
|
2940
3409
|
return result.output.trim();
|
|
2941
3410
|
}
|
|
2942
3411
|
}
|
|
2943
3412
|
const matches = [];
|
|
2944
|
-
const lines = result.output.trim().split(
|
|
3413
|
+
const lines = result.output.trim().split('\n').filter(Boolean);
|
|
2945
3414
|
for (const line of lines){
|
|
2946
3415
|
try {
|
|
2947
3416
|
const parsed = JSON.parse(line);
|
|
@@ -2957,12 +3426,24 @@ try {
|
|
|
2957
3426
|
return matches;
|
|
2958
3427
|
}
|
|
2959
3428
|
/**
|
|
3429
|
+
* Search file contents for a regex pattern, returning human-readable output.
|
|
3430
|
+
*/ async grep(pattern, path = '/', include = null) {
|
|
3431
|
+
const result = await this.grepRaw(pattern, path, include);
|
|
3432
|
+
if (typeof result === 'string') {
|
|
3433
|
+
return result;
|
|
3434
|
+
}
|
|
3435
|
+
if (result.length === 0) {
|
|
3436
|
+
return 'No matches found';
|
|
3437
|
+
}
|
|
3438
|
+
return formatGrepOutput(result);
|
|
3439
|
+
}
|
|
3440
|
+
/**
|
|
2960
3441
|
* Structured glob matching returning FileInfo objects.
|
|
2961
|
-
*/ async globInfo(pattern, path =
|
|
3442
|
+
*/ async globInfo(pattern, path = '/') {
|
|
2962
3443
|
const command = buildGlobCommand(path, pattern);
|
|
2963
|
-
const result = await this.execute(command);
|
|
3444
|
+
const result = await this.execute(command, DEFAULT_SANDBOX_FILE_SEARCH_EXECUTION_OPTIONS);
|
|
2964
3445
|
const infos = [];
|
|
2965
|
-
const lines = result.output.trim().split(
|
|
3446
|
+
const lines = result.output.trim().split('\n').filter(Boolean);
|
|
2966
3447
|
for (const line of lines){
|
|
2967
3448
|
try {
|
|
2968
3449
|
const parsed = JSON.parse(line);
|
|
@@ -2979,15 +3460,79 @@ try {
|
|
|
2979
3460
|
return infos;
|
|
2980
3461
|
}
|
|
2981
3462
|
/**
|
|
3463
|
+
* Find files matching a glob pattern, returning human-readable output.
|
|
3464
|
+
*/ async glob(pattern, path = '/') {
|
|
3465
|
+
const files = await this.globInfo(pattern, path);
|
|
3466
|
+
if (files.length === 0) {
|
|
3467
|
+
return 'No files found';
|
|
3468
|
+
}
|
|
3469
|
+
return formatGlobOutput(files);
|
|
3470
|
+
}
|
|
3471
|
+
/**
|
|
2982
3472
|
* Create a new file with content.
|
|
2983
3473
|
*/ async write(filePath, content) {
|
|
2984
3474
|
const command = buildWriteCommand(filePath, content);
|
|
2985
|
-
const result = await this.execute(command);
|
|
3475
|
+
const result = await this.execute(command, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS);
|
|
2986
3476
|
if (result.exitCode !== 0) {
|
|
3477
|
+
const output = (result.output || '').trim();
|
|
3478
|
+
if (output.includes(WRITE_EXISTS_OUTPUT)) {
|
|
3479
|
+
return {
|
|
3480
|
+
error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3483
|
+
const fallback = await this.writeViaUpload(filePath, content);
|
|
3484
|
+
if (!fallback.error) {
|
|
3485
|
+
return fallback;
|
|
3486
|
+
}
|
|
3487
|
+
return {
|
|
3488
|
+
error: output ? `Failed to write file '${filePath}': ${output}` : `Failed to write file '${filePath}'.`
|
|
3489
|
+
};
|
|
3490
|
+
}
|
|
3491
|
+
return {
|
|
3492
|
+
path: filePath,
|
|
3493
|
+
filesUpdate: null
|
|
3494
|
+
};
|
|
3495
|
+
}
|
|
3496
|
+
async writeViaUpload(filePath, content) {
|
|
3497
|
+
const existsCheck = await this.execute(buildExistsCommand(filePath), DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS);
|
|
3498
|
+
if (existsCheck.exitCode === 0) {
|
|
2987
3499
|
return {
|
|
2988
3500
|
error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
|
|
2989
3501
|
};
|
|
2990
3502
|
}
|
|
3503
|
+
const uploads = await this.uploadFiles([
|
|
3504
|
+
[
|
|
3505
|
+
filePath,
|
|
3506
|
+
Buffer.from(content, 'utf-8')
|
|
3507
|
+
]
|
|
3508
|
+
]);
|
|
3509
|
+
const first = uploads[0];
|
|
3510
|
+
if (!first) {
|
|
3511
|
+
return {
|
|
3512
|
+
error: `Failed to write file '${filePath}': upload returned no result.`
|
|
3513
|
+
};
|
|
3514
|
+
}
|
|
3515
|
+
if (first.error) {
|
|
3516
|
+
return {
|
|
3517
|
+
error: `Failed to write file '${filePath}': ${first.error}`
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
return {
|
|
3521
|
+
path: filePath,
|
|
3522
|
+
filesUpdate: null
|
|
3523
|
+
};
|
|
3524
|
+
}
|
|
3525
|
+
/**
|
|
3526
|
+
* Append content to a file. Creates the file if it doesn't exist.
|
|
3527
|
+
*/ async append(filePath, content) {
|
|
3528
|
+
const command = buildAppendCommand(filePath, content);
|
|
3529
|
+
const result = await this.execute(command, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS);
|
|
3530
|
+
if (result.exitCode !== 0) {
|
|
3531
|
+
const output = (result.output || '').trim();
|
|
3532
|
+
return {
|
|
3533
|
+
error: output ? `Failed to append to file '${filePath}': ${output}` : `Failed to append to file '${filePath}'.`
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
2991
3536
|
return {
|
|
2992
3537
|
path: filePath,
|
|
2993
3538
|
filesUpdate: null
|
|
@@ -2997,7 +3542,7 @@ try {
|
|
|
2997
3542
|
* Edit a file by replacing string occurrences.
|
|
2998
3543
|
*/ async edit(filePath, oldString, newString, replaceAll = false) {
|
|
2999
3544
|
const command = buildEditCommand(filePath, oldString, newString, replaceAll);
|
|
3000
|
-
const result = await this.execute(command);
|
|
3545
|
+
const result = await this.execute(command, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS);
|
|
3001
3546
|
switch(result.exitCode){
|
|
3002
3547
|
case 0:
|
|
3003
3548
|
{
|
|
@@ -3026,6 +3571,38 @@ try {
|
|
|
3026
3571
|
};
|
|
3027
3572
|
}
|
|
3028
3573
|
}
|
|
3574
|
+
/**
|
|
3575
|
+
* Perform multiple sequential edits on a single file.
|
|
3576
|
+
* All edits are applied sequentially, with each edit operating on the result of the previous edit.
|
|
3577
|
+
* All edits must succeed for the operation to succeed (atomic).
|
|
3578
|
+
*/ async multiEdit(filePath, edits) {
|
|
3579
|
+
if (!edits || edits.length === 0) {
|
|
3580
|
+
return {
|
|
3581
|
+
error: 'No edits provided'
|
|
3582
|
+
};
|
|
3583
|
+
}
|
|
3584
|
+
const results = [];
|
|
3585
|
+
for (const edit of edits){
|
|
3586
|
+
var _edit_replaceAll;
|
|
3587
|
+
const result = await this.edit(filePath, edit.oldString, edit.newString, (_edit_replaceAll = edit.replaceAll) != null ? _edit_replaceAll : false);
|
|
3588
|
+
results.push(result);
|
|
3589
|
+
// If any edit fails, return immediately with error
|
|
3590
|
+
if (result.error) {
|
|
3591
|
+
return {
|
|
3592
|
+
error: `Multi-edit failed at edit ${results.length}: ${result.error}`,
|
|
3593
|
+
path: filePath,
|
|
3594
|
+
filesUpdate: null,
|
|
3595
|
+
results
|
|
3596
|
+
};
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
// All edits succeeded
|
|
3600
|
+
return {
|
|
3601
|
+
path: filePath,
|
|
3602
|
+
filesUpdate: null,
|
|
3603
|
+
results
|
|
3604
|
+
};
|
|
3605
|
+
}
|
|
3029
3606
|
}
|
|
3030
3607
|
|
|
3031
3608
|
const SANDBOX_PROVIDER = 'SANDBOX_PROVIDER';
|
|
@@ -3045,4 +3622,4 @@ SandboxProviderRegistry = __decorate([
|
|
|
3045
3622
|
])
|
|
3046
3623
|
], SandboxProviderRegistry);
|
|
3047
3624
|
|
|
3048
|
-
export { AGENT_CHAT_DISPATCH_MESSAGE_TYPE, AGENT_MIDDLEWARE_STRATEGY, AIModelProviderNotFoundException, AIModelProviderRegistry, AIModelProviderStrategy, AI_MODEL_PROVIDER, ANALYTICS_PERMISSION_SERVICE_TOKEN, AdapterDataSourceStrategy, AgentMiddlewareRegistry, AgentMiddlewareStrategy, AiModelNotFoundException, BaseHTTPQueryRunner, BaseQueryRunner, BaseSQLQueryRunner, BaseSandbox, BaseStrategyRegistry, BaseTool, BaseToolset, BuiltinToolset, CHAT_CHANNEL, CHAT_CHANNEL_TEXT_LIMITS, CancelConversationCommand, ChatChannel, ChatChannelRegistry, ChatOAICompatReasoningModel, CommonParameterRules, CreateModelClientCommand, CredentialsValidateFailedError, DATASOURCE_STRATEGY, DBCreateTableMode, DBProtocolEnum, DBSyntaxEnum, DBTableAction, DBTableDataAction, DEFAULT_EXECUTION_CONFIG, DOCUMENT_SOURCE_STRATEGY, DOCUMENT_TRANSFORMER_STRATEGY, DataSourceStrategy, DataSourceStrategyRegistry, DocumentSourceRegistry, DocumentSourceStrategy, DocumentTransformerRegistry, DocumentTransformerStrategy, GLOBAL_ORGANIZATION_SCOPE, HANDOFF_PERMISSION_SERVICE_TOKEN, HANDOFF_PROCESSOR_STRATEGY, HANDOFF_QUEUE_SERVICE_TOKEN, HandoffProcessorRegistry, HandoffProcessorStrategy, IMAGE_UNDERSTANDING_STRATEGY, INTEGRATION_PERMISSION_SERVICE_TOKEN, INTEGRATION_STRATEGY, ImageUnderstandingRegistry, ImageUnderstandingStrategy, IntegrationStrategyKey, IntegrationStrategyRegistry, JUMP_TO_TARGETS, JsonSchemaValidator, KNOWLEDGE_STRATEGY, KnowledgeStrategyKey, KnowledgeStrategyRegistry, LLMUsage, LargeLanguageModel, ModelProvider, ORGANIZATION_METADATA_KEY, OpenAICompatibleReranker, PERMISSION_OPERATION_METADATA_KEY, PLUGIN_METADATA, PLUGIN_METADATA_KEY, PROVIDE_AI_MODEL_LLM, PROVIDE_AI_MODEL_MODERATION, PROVIDE_AI_MODEL_RERANK, PROVIDE_AI_MODEL_SPEECH2TEXT, PROVIDE_AI_MODEL_TEXT_EMBEDDING, PROVIDE_AI_MODEL_TTS, RETRIEVER_STRATEGY, RequestContext, RequestContextMiddleware, RequirePermissionOperation, RerankModel, RetrieverRegistry, RetrieverStrategy, SANDBOX_PROVIDER, STRATEGY_META_KEY, SandboxProviderRegistry, SandboxProviderStrategy, Speech2TextChatModel, SpeechToTextModel, StrategyBus, TEXT_SPLITTER_STRATEGY, TOOLSET_STRATEGY, TextEmbeddingModelManager, TextSplitterRegistry, TextSplitterStrategy, TextToSpeechModel, ToolsetRegistry, ToolsetStrategy, USER_PERMISSION_SERVICE_TOKEN, VECTOR_STORE_STRATEGY, VectorStoreRegistry, VectorStoreStrategy, WORKFLOW_NODE_STRATEGY, WORKFLOW_TRIGGER_STRATEGY, WorkflowNodeRegistry, WorkflowNodeStrategy, WorkflowTriggerRegistry, WorkflowTriggerStrategy, WrapWorkflowNodeExecutionCommand, XpFileSystem, XpertServerPlugin, als, calcTokenUsage, chunkText, countTokensSafe, createI18nInstance, createPluginLogger, defineAgentMessageType, defineChannelMessageType, downloadRemoteFile, getErrorMessage, getModelContextSize, getPermissionOperationMetadata, getPositionList, getPositionMap, getRequestContext, getRequiredPermissionOperation, isRemoteFile, isSandboxBackend, isStructuredMessageType, loadYamlFile, mergeCredentials, mergeParentChildChunks, normalizeContextSize, runWithRequestContext, sumTokenUsage };
|
|
3625
|
+
export { AGENT_CHAT_DISPATCH_MESSAGE_TYPE, AGENT_MIDDLEWARE_STRATEGY, AIModelProviderNotFoundException, AIModelProviderRegistry, AIModelProviderStrategy, AI_MODEL_PROVIDER, ANALYTICS_PERMISSION_SERVICE_TOKEN, AdapterDataSourceStrategy, AgentMiddlewareRegistry, AgentMiddlewareStrategy, AiModelNotFoundException, BaseHTTPQueryRunner, BaseQueryRunner, BaseSQLQueryRunner, BaseSandbox, BaseStrategyRegistry, BaseTool, BaseToolset, BuiltinToolset, CHAT_CHANNEL, CHAT_CHANNEL_TEXT_LIMITS, CancelConversationCommand, ChatChannel, ChatChannelRegistry, ChatOAICompatReasoningModel, CommonParameterRules, CreateModelClientCommand, CredentialsValidateFailedError, DATASOURCE_STRATEGY, DBCreateTableMode, DBProtocolEnum, DBSyntaxEnum, DBTableAction, DBTableDataAction, DEFAULT_EXECUTION_CONFIG, DEFAULT_SANDBOX_EXECUTION_MAX_OUTPUT_BYTES, DEFAULT_SANDBOX_FILE_OPERATION_EXECUTION_OPTIONS, DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_MS, DEFAULT_SANDBOX_FILE_OPERATION_TIMEOUT_SEC, DEFAULT_SANDBOX_FILE_SEARCH_EXECUTION_OPTIONS, DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_MS, DEFAULT_SANDBOX_FILE_SEARCH_TIMEOUT_SEC, DEFAULT_SANDBOX_SHELL_EXECUTION_OPTIONS, DEFAULT_SANDBOX_SHELL_TIMEOUT_MS, DEFAULT_SANDBOX_SHELL_TIMEOUT_SEC, DOCUMENT_SOURCE_STRATEGY, DOCUMENT_TRANSFORMER_STRATEGY, DataSourceStrategy, DataSourceStrategyRegistry, DocumentSourceRegistry, DocumentSourceStrategy, DocumentTransformerRegistry, DocumentTransformerStrategy, FILE_STORAGE_PROVIDER, FILE_UPLOAD_TARGET_STRATEGY, FileStorageProvider, FileStorageProviderRegistry, FileUploadTargetRegistry, FileUploadTargetStrategy, GLOBAL_ORGANIZATION_SCOPE, HANDOFF_PERMISSION_SERVICE_TOKEN, HANDOFF_PROCESSOR_STRATEGY, HANDOFF_QUEUE_SERVICE_TOKEN, HandoffProcessorRegistry, HandoffProcessorStrategy, IMAGE_UNDERSTANDING_STRATEGY, INTEGRATION_PERMISSION_SERVICE_TOKEN, INTEGRATION_STRATEGY, ImageUnderstandingRegistry, ImageUnderstandingStrategy, IntegrationStrategyKey, IntegrationStrategyRegistry, JUMP_TO_TARGETS, JsonSchemaValidator, KNOWLEDGE_STRATEGY, KnowledgeStrategyKey, KnowledgeStrategyRegistry, LLMUsage, LargeLanguageModel, ModelProvider, ORGANIZATION_METADATA_KEY, OpenAICompatibleReranker, PERMISSION_OPERATION_METADATA_KEY, PLUGIN_CONFIG_RESOLVER_TOKEN, PLUGIN_METADATA, PLUGIN_METADATA_KEY, PROVIDE_AI_MODEL_LLM, PROVIDE_AI_MODEL_MODERATION, PROVIDE_AI_MODEL_RERANK, PROVIDE_AI_MODEL_SPEECH2TEXT, PROVIDE_AI_MODEL_TEXT_EMBEDDING, PROVIDE_AI_MODEL_TTS, RETRIEVER_STRATEGY, RequestContext, RequestContextMiddleware, RequirePermissionOperation, RerankModel, RetrieverRegistry, RetrieverStrategy, SANDBOX_PROVIDER, SANDBOX_SHELL_TIMEOUT_LIMITS_SEC, STRATEGY_META_KEY, SandboxProviderRegistry, SandboxProviderStrategy, Speech2TextChatModel, SpeechToTextModel, StrategyBus, TEXT_SPLITTER_STRATEGY, TOOLSET_STRATEGY, TextEmbeddingModelManager, TextSplitterRegistry, TextSplitterStrategy, TextToSpeechModel, ToolsetRegistry, ToolsetStrategy, USER_PERMISSION_SERVICE_TOKEN, VECTOR_STORE_STRATEGY, VectorStoreRegistry, VectorStoreStrategy, WORKFLOW_NODE_STRATEGY, WORKFLOW_TRIGGER_STRATEGY, WorkflowNodeRegistry, WorkflowNodeStrategy, WorkflowTriggerRegistry, WorkflowTriggerStrategy, WrapWorkflowNodeExecutionCommand, XpFileSystem, XpertServerPlugin, als, appendSandboxMessage, buildSandboxTimeoutMessage, calcTokenUsage, chunkText, countTokensSafe, createI18nInstance, createPluginLogger, defineAgentMessageType, defineChannelMessageType, downloadRemoteFile, formatSandboxTimeout, getErrorMessage, getModelContextSize, getPermissionOperationMetadata, getPositionList, getPositionMap, getRequestContext, getRequiredPermissionOperation, isRemoteFile, isSandboxBackend, isStructuredMessageType, loadYamlFile, mergeCredentials, mergeParentChildChunks, normalizeContextSize, resolveSandboxExecutionOptions, runWithRequestContext, secondsToMilliseconds, sumTokenUsage };
|