@xpert-ai/plugin-sdk 3.7.2 → 3.8.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.
- package/index.cjs.js +1282 -0
- package/index.esm.js +1256 -1
- package/package.json +2 -1
- package/src/index.d.ts +2 -0
- package/src/lib/agent/handoff/agent-chat.contract.d.ts +20 -0
- package/src/lib/agent/handoff/handoff-processor.decorator.d.ts +11 -0
- package/src/lib/agent/handoff/handoff-processor.registry.d.ts +6 -0
- package/src/lib/agent/handoff/handoff.interface.d.ts +17 -0
- package/src/lib/agent/handoff/index.d.ts +6 -0
- package/src/lib/agent/handoff/message-type.d.ts +24 -0
- package/src/lib/agent/handoff/types.d.ts +110 -0
- package/src/lib/agent/index.d.ts +1 -0
- package/src/lib/agent/middleware/runtime.d.ts +2 -0
- package/src/lib/agent/middleware/strategy.interface.d.ts +5 -1
- package/src/lib/agent/middleware/types.d.ts +2 -2
- package/src/lib/ai-model/utils/index.d.ts +1 -0
- package/src/lib/ai-model/utils/limits.d.ts +4 -0
- package/src/lib/channel/cancel-conversation.command.d.ts +14 -0
- package/src/lib/channel/index.d.ts +4 -0
- package/src/lib/channel/strategy.decorator.d.ts +23 -0
- package/src/lib/channel/strategy.interface.d.ts +345 -0
- package/src/lib/channel/strategy.registry.d.ts +30 -0
- package/src/lib/core/permissions/analytics.d.ts +46 -0
- package/src/lib/core/{permissions.d.ts → permissions/general.d.ts} +9 -12
- package/src/lib/core/permissions/handoff.d.ts +36 -0
- package/src/lib/core/permissions/index.d.ts +24 -0
- package/src/lib/core/permissions/operation.d.ts +8 -0
- package/src/lib/core/permissions/user.d.ts +29 -0
- package/src/lib/integration/strategy.interface.d.ts +3 -1
- package/src/lib/sandbox/index.d.ts +8 -0
- package/src/lib/sandbox/protocol.d.ts +383 -0
- package/src/lib/sandbox/sandbox.d.ts +103 -0
- package/src/lib/sandbox/sandbox.decorator.d.ts +2 -0
- package/src/lib/sandbox/sandbox.interface.d.ts +38 -0
- package/src/lib/sandbox/sandbox.registry.d.ts +6 -0
- package/src/lib/toolset/strategy.interface.d.ts +2 -6
- package/src/lib/types.d.ts +7 -1
- package/src/lib/workflow/node/strategy.interface.d.ts +4 -2
- package/src/lib/workflow/trigger/strategy.interface.d.ts +13 -0
package/index.cjs.js
CHANGED
|
@@ -30,6 +30,7 @@ var chat_models = require('@langchain/core/language_models/chat_models');
|
|
|
30
30
|
var messages = require('@langchain/core/messages');
|
|
31
31
|
var env = require('@langchain/core/utils/env');
|
|
32
32
|
require('js-tiktoken');
|
|
33
|
+
var base = require('@langchain/core/language_models/base');
|
|
33
34
|
|
|
34
35
|
function _interopNamespaceDefault(e) {
|
|
35
36
|
var n = Object.create(null);
|
|
@@ -530,6 +531,7 @@ exports.WorkflowNodeRegistry = __decorate([
|
|
|
530
531
|
])
|
|
531
532
|
], exports.WorkflowNodeRegistry);
|
|
532
533
|
|
|
534
|
+
const COMMAND_METADATA$1 = '__command__';
|
|
533
535
|
/**
|
|
534
536
|
* Wrap Workflow Node Execution Command
|
|
535
537
|
*/ class WrapWorkflowNodeExecutionCommand extends cqrs.Command {
|
|
@@ -540,6 +542,9 @@ exports.WorkflowNodeRegistry = __decorate([
|
|
|
540
542
|
}
|
|
541
543
|
}
|
|
542
544
|
WrapWorkflowNodeExecutionCommand.type = '[Workflow] Wrap Workflow Node Execution';
|
|
545
|
+
Reflect.defineMetadata(COMMAND_METADATA$1, {
|
|
546
|
+
id: WrapWorkflowNodeExecutionCommand.type
|
|
547
|
+
}, WrapWorkflowNodeExecutionCommand);
|
|
543
548
|
|
|
544
549
|
const VECTOR_STORE_STRATEGY = 'VECTOR_STORE_STRATEGY';
|
|
545
550
|
const VectorStoreStrategy = (provider)=>common.applyDecorators(common.SetMetadata(VECTOR_STORE_STRATEGY, provider), common.SetMetadata(STRATEGY_META_KEY, VECTOR_STORE_STRATEGY));
|
|
@@ -951,6 +956,52 @@ BuiltinToolset.provider = '';
|
|
|
951
956
|
}
|
|
952
957
|
}
|
|
953
958
|
|
|
959
|
+
/**
|
|
960
|
+
* System token for resolving integration read service from plugin context.
|
|
961
|
+
*/ const INTEGRATION_PERMISSION_SERVICE_TOKEN = 'XPERT_PLUGIN_INTEGRATION_PERMISSION_SERVICE';
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* System token for resolving analytics permission service from plugin context.
|
|
965
|
+
*/ const ANALYTICS_PERMISSION_SERVICE_TOKEN = 'XPERT_PLUGIN_ANALYTICS_PERMISSION_SERVICE';
|
|
966
|
+
|
|
967
|
+
const PERMISSION_OPERATION_METADATA_KEY = 'XPERT_PLUGIN_PERMISSION_OPERATION';
|
|
968
|
+
function RequirePermissionOperation(permissionType, operation) {
|
|
969
|
+
return (_target, _propertyKey, descriptor)=>{
|
|
970
|
+
const method = descriptor.value;
|
|
971
|
+
if (typeof method !== 'function') {
|
|
972
|
+
throw new Error('RequirePermissionOperation can only be applied to methods');
|
|
973
|
+
}
|
|
974
|
+
Reflect.defineMetadata(PERMISSION_OPERATION_METADATA_KEY, {
|
|
975
|
+
permissionType,
|
|
976
|
+
operation
|
|
977
|
+
}, method);
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function getPermissionOperationMetadata(method) {
|
|
981
|
+
if (typeof method !== 'function') {
|
|
982
|
+
return undefined;
|
|
983
|
+
}
|
|
984
|
+
return Reflect.getMetadata(PERMISSION_OPERATION_METADATA_KEY, method);
|
|
985
|
+
}
|
|
986
|
+
function getRequiredPermissionOperation(method, permissionType) {
|
|
987
|
+
const metadata = getPermissionOperationMetadata(method);
|
|
988
|
+
if (!metadata || metadata.permissionType !== permissionType) {
|
|
989
|
+
return undefined;
|
|
990
|
+
}
|
|
991
|
+
return metadata.operation;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* System token for resolving handoff queue service from plugin context.
|
|
996
|
+
*/ const HANDOFF_PERMISSION_SERVICE_TOKEN = 'XPERT_PLUGIN_HANDOFF_PERMISSION_SERVICE';
|
|
997
|
+
/**
|
|
998
|
+
* Internal system token used by core to expose the handoff queue bridge.
|
|
999
|
+
*/ const HANDOFF_QUEUE_SERVICE_TOKEN = 'XPERT_HANDOFF_QUEUE_SERVICE';
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* System token for resolving user permission service from plugin context.
|
|
1003
|
+
*/ const USER_PERMISSION_SERVICE_TOKEN = 'XPERT_PLUGIN_USER_PERMISSION_SERVICE';
|
|
1004
|
+
|
|
954
1005
|
async function createI18nInstance(pluginDir, language) {
|
|
955
1006
|
const instance = i18next.createInstance();
|
|
956
1007
|
const i18nDir = path.join(pluginDir, 'i18n');
|
|
@@ -1365,6 +1416,18 @@ function AIModelProviderStrategy(provider) {
|
|
|
1365
1416
|
if (file == null ? void 0 : file.startsWith('file:///')) {
|
|
1366
1417
|
file = file.replace('file://', '');
|
|
1367
1418
|
}
|
|
1419
|
+
// Strip :line:col suffix (e.g. "/path/file.js:37:5" -> "/path/file.js")
|
|
1420
|
+
if (file) {
|
|
1421
|
+
file = file.replace(/:\d+:\d+$/, '');
|
|
1422
|
+
}
|
|
1423
|
+
// Decode URL-encoded paths (e.g. Chinese characters: %E9%A1%B9%E7%9B%AE -> 项目)
|
|
1424
|
+
if (file) {
|
|
1425
|
+
try {
|
|
1426
|
+
file = decodeURIComponent(file);
|
|
1427
|
+
} catch (e) {
|
|
1428
|
+
// keep as-is if decode fails
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1368
1431
|
const dir = file ? path.dirname(file) : process.cwd();
|
|
1369
1432
|
return function(target) {
|
|
1370
1433
|
common.SetMetadata(STRATEGY_META_KEY, AI_MODEL_PROVIDER)(target);
|
|
@@ -2283,6 +2346,49 @@ class Speech2TextChatModel extends chat_models.BaseChatModel {
|
|
|
2283
2346
|
return 0;
|
|
2284
2347
|
}
|
|
2285
2348
|
|
|
2349
|
+
function getModelContextSize(input) {
|
|
2350
|
+
if (isCopilotModel(input)) {
|
|
2351
|
+
var _input_options;
|
|
2352
|
+
return normalizeContextSize((_input_options = input.options) == null ? void 0 : _input_options[contracts.ModelPropertyKey.CONTEXT_SIZE]);
|
|
2353
|
+
}
|
|
2354
|
+
// Backward compatibility for langchain <1.0.0
|
|
2355
|
+
if (input.metadata && "profile" in input.metadata) {
|
|
2356
|
+
const profile = input.metadata['profile'];
|
|
2357
|
+
if ("maxInputTokens" in profile && (typeof profile.maxInputTokens === "number" || profile.maxInputTokens == null)) {
|
|
2358
|
+
var _profile_maxInputTokens;
|
|
2359
|
+
return (_profile_maxInputTokens = profile.maxInputTokens) != null ? _profile_maxInputTokens : undefined;
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
// Langchain v1.0.0+
|
|
2363
|
+
if ("profile" in input && typeof input.profile === "object" && input.profile && "maxInputTokens" in input.profile && (typeof input.profile.maxInputTokens === "number" || input.profile.maxInputTokens == null)) {
|
|
2364
|
+
var _input_profile_maxInputTokens;
|
|
2365
|
+
return (_input_profile_maxInputTokens = input.profile.maxInputTokens) != null ? _input_profile_maxInputTokens : undefined;
|
|
2366
|
+
}
|
|
2367
|
+
if ("model" in input && typeof input.model === "string") {
|
|
2368
|
+
return base.getModelContextSize(input.model);
|
|
2369
|
+
}
|
|
2370
|
+
if ("modelName" in input && typeof input.modelName === "string") {
|
|
2371
|
+
return base.getModelContextSize(input.modelName);
|
|
2372
|
+
}
|
|
2373
|
+
return undefined;
|
|
2374
|
+
}
|
|
2375
|
+
function isCopilotModel(input) {
|
|
2376
|
+
return typeof input === "object" && input !== null && !("invoke" in input);
|
|
2377
|
+
}
|
|
2378
|
+
function normalizeContextSize(value) {
|
|
2379
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
2380
|
+
return Math.floor(value);
|
|
2381
|
+
}
|
|
2382
|
+
if (typeof value === "string") {
|
|
2383
|
+
const parsed = Number.parseInt(value, 10);
|
|
2384
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
2385
|
+
return parsed;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
return undefined;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
const COMMAND_METADATA = '__command__';
|
|
2286
2392
|
/**
|
|
2287
2393
|
* Get a Chat Model of copilot model and check it's token limitation, record the token usage
|
|
2288
2394
|
*/ class CreateModelClientCommand extends cqrs.Command {
|
|
@@ -2293,6 +2399,9 @@ class Speech2TextChatModel extends chat_models.BaseChatModel {
|
|
|
2293
2399
|
}
|
|
2294
2400
|
}
|
|
2295
2401
|
CreateModelClientCommand.type = '[AI Model] Create Model Client';
|
|
2402
|
+
Reflect.defineMetadata(COMMAND_METADATA, {
|
|
2403
|
+
id: CreateModelClientCommand.type
|
|
2404
|
+
}, CreateModelClientCommand);
|
|
2296
2405
|
|
|
2297
2406
|
const AGENT_MIDDLEWARE_STRATEGY = 'AGENT_MIDDLEWARE_STRATEGY';
|
|
2298
2407
|
const AgentMiddlewareStrategy = (provider)=>common.applyDecorators(common.SetMetadata(AGENT_MIDDLEWARE_STRATEGY, provider), common.SetMetadata(STRATEGY_META_KEY, AGENT_MIDDLEWARE_STRATEGY));
|
|
@@ -2319,6 +2428,1152 @@ exports.AgentMiddlewareRegistry = __decorate([
|
|
|
2319
2428
|
"end"
|
|
2320
2429
|
];
|
|
2321
2430
|
|
|
2431
|
+
/**
|
|
2432
|
+
* Structured message types (suggested format):
|
|
2433
|
+
* - channel.{provider}.{action}.v{number}
|
|
2434
|
+
* - agent.{action}.v{number}
|
|
2435
|
+
* - system.{action}.v{number}
|
|
2436
|
+
* - plugin.{domain}.{action}.v{number}
|
|
2437
|
+
*
|
|
2438
|
+
* Note: At runtime, any string type is still allowed, facilitating dynamic extension by plugins.
|
|
2439
|
+
*/ const SEGMENT_PATTERN = /^[a-z][a-z0-9_-]*$/i;
|
|
2440
|
+
const VERSION_PATTERN = /^v[1-9][0-9]*$/;
|
|
2441
|
+
function assertSegment(input, name) {
|
|
2442
|
+
if (!SEGMENT_PATTERN.test(input)) {
|
|
2443
|
+
throw new Error(`Invalid ${name} segment: "${input}"`);
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* Unified construction of channel message types to avoid manual string errors.
|
|
2448
|
+
* Example: channel.lark.inbound.v1
|
|
2449
|
+
*/ function defineChannelMessageType(provider, action, version) {
|
|
2450
|
+
assertSegment(provider, 'provider');
|
|
2451
|
+
assertSegment(action, 'action');
|
|
2452
|
+
if (!Number.isInteger(version) || version <= 0) {
|
|
2453
|
+
throw new Error(`Invalid version: "${version}"`);
|
|
2454
|
+
}
|
|
2455
|
+
return `channel.${provider}.${action}.v${version}`;
|
|
2456
|
+
}
|
|
2457
|
+
/**
|
|
2458
|
+
* Unified construction of agent message types.
|
|
2459
|
+
* Example: agent.handoff.v1
|
|
2460
|
+
*/ function defineAgentMessageType(action, version) {
|
|
2461
|
+
assertSegment(action, 'action');
|
|
2462
|
+
if (!Number.isInteger(version) || version <= 0) {
|
|
2463
|
+
throw new Error(`Invalid version: "${version}"`);
|
|
2464
|
+
}
|
|
2465
|
+
return `agent.${action}.v${version}`;
|
|
2466
|
+
}
|
|
2467
|
+
/**
|
|
2468
|
+
* Check if a type conforms to the structured naming convention (format only, no semantic validation).
|
|
2469
|
+
*/ function isStructuredMessageType(type) {
|
|
2470
|
+
const parts = type.split('.');
|
|
2471
|
+
if (parts.length < 3) {
|
|
2472
|
+
return false;
|
|
2473
|
+
}
|
|
2474
|
+
const version = parts[parts.length - 1];
|
|
2475
|
+
if (!VERSION_PATTERN.test(version)) {
|
|
2476
|
+
return false;
|
|
2477
|
+
}
|
|
2478
|
+
return parts.slice(0, -1).every((part)=>SEGMENT_PATTERN.test(part));
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
const AGENT_CHAT_DISPATCH_MESSAGE_TYPE = defineAgentMessageType('chat_dispatch', 1);
|
|
2482
|
+
|
|
2483
|
+
/**
|
|
2484
|
+
* Handoff Processor meta key。
|
|
2485
|
+
*/ const HANDOFF_PROCESSOR_STRATEGY = 'HANDOFF_PROCESSOR_STRATEGY';
|
|
2486
|
+
/**
|
|
2487
|
+
* Declare on a provider:
|
|
2488
|
+
* 1) Which message types it handles
|
|
2489
|
+
* 2) Default execution policy (lane/timeout)
|
|
2490
|
+
*/ function HandoffProcessorStrategy(provider, metadata) {
|
|
2491
|
+
var _metadata_types;
|
|
2492
|
+
if (!(metadata == null ? void 0 : (_metadata_types = metadata.types) == null ? void 0 : _metadata_types.length)) {
|
|
2493
|
+
throw new Error('HandoffProcessor requires at least one message type');
|
|
2494
|
+
}
|
|
2495
|
+
return common.applyDecorators(common.SetMetadata(HANDOFF_PROCESSOR_STRATEGY, provider), common.SetMetadata(STRATEGY_META_KEY, HANDOFF_PROCESSOR_STRATEGY));
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
exports.HandoffProcessorRegistry = class HandoffProcessorRegistry extends BaseStrategyRegistry {
|
|
2499
|
+
constructor(discoveryService, reflector){
|
|
2500
|
+
super(HANDOFF_PROCESSOR_STRATEGY, discoveryService, reflector);
|
|
2501
|
+
}
|
|
2502
|
+
};
|
|
2503
|
+
exports.HandoffProcessorRegistry = __decorate([
|
|
2504
|
+
common.Injectable(),
|
|
2505
|
+
__metadata("design:type", Function),
|
|
2506
|
+
__metadata("design:paramtypes", [
|
|
2507
|
+
typeof core.DiscoveryService === "undefined" ? Object : core.DiscoveryService,
|
|
2508
|
+
typeof core.Reflector === "undefined" ? Object : core.Reflector
|
|
2509
|
+
])
|
|
2510
|
+
], exports.HandoffProcessorRegistry);
|
|
2511
|
+
|
|
2512
|
+
/**
|
|
2513
|
+
* Handoff execution types.
|
|
2514
|
+
*
|
|
2515
|
+
* Note:
|
|
2516
|
+
* - Two-gate lane/session permit logic has been removed.
|
|
2517
|
+
* - Lane is now an execution tag for observability and soft policy only.
|
|
2518
|
+
*/ const DEFAULT_EXECUTION_CONFIG = {
|
|
2519
|
+
lanes: {
|
|
2520
|
+
main: 8,
|
|
2521
|
+
subagent: 16,
|
|
2522
|
+
cron: 4,
|
|
2523
|
+
nested: 8
|
|
2524
|
+
},
|
|
2525
|
+
runTtlMs: 10 * 60 * 1000
|
|
2526
|
+
};
|
|
2527
|
+
|
|
2528
|
+
/**
|
|
2529
|
+
* Common platform message length limits
|
|
2530
|
+
*/ const CHAT_CHANNEL_TEXT_LIMITS = {
|
|
2531
|
+
lark: 4000,
|
|
2532
|
+
wecom: 2048,
|
|
2533
|
+
dingtalk: 4000,
|
|
2534
|
+
slack: 4000,
|
|
2535
|
+
telegram: 4096,
|
|
2536
|
+
discord: 2000
|
|
2537
|
+
};
|
|
2538
|
+
/**
|
|
2539
|
+
* Chunk long text by specified length
|
|
2540
|
+
*
|
|
2541
|
+
* @param text - Text to chunk
|
|
2542
|
+
* @param limit - Maximum characters per chunk
|
|
2543
|
+
* @param mode - Chunking mode: 'text' splits by character, 'markdown' tries to preserve paragraphs
|
|
2544
|
+
* @returns Array of chunked text
|
|
2545
|
+
*/ function chunkText(text, limit, mode = 'text') {
|
|
2546
|
+
if (!text || text.length <= limit) {
|
|
2547
|
+
return [
|
|
2548
|
+
text
|
|
2549
|
+
];
|
|
2550
|
+
}
|
|
2551
|
+
const chunks = [];
|
|
2552
|
+
if (mode === 'markdown') {
|
|
2553
|
+
// Markdown mode: try to split at paragraph boundaries
|
|
2554
|
+
const paragraphs = text.split(/\n\n+/);
|
|
2555
|
+
let currentChunk = '';
|
|
2556
|
+
for (const para of paragraphs){
|
|
2557
|
+
if (currentChunk.length + para.length + 2 <= limit) {
|
|
2558
|
+
currentChunk += (currentChunk ? '\n\n' : '') + para;
|
|
2559
|
+
} else if (para.length > limit) {
|
|
2560
|
+
// Paragraph itself is too long, need hard split
|
|
2561
|
+
if (currentChunk) {
|
|
2562
|
+
chunks.push(currentChunk);
|
|
2563
|
+
currentChunk = '';
|
|
2564
|
+
}
|
|
2565
|
+
let remaining = para;
|
|
2566
|
+
while(remaining.length > limit){
|
|
2567
|
+
chunks.push(remaining.slice(0, limit));
|
|
2568
|
+
remaining = remaining.slice(limit);
|
|
2569
|
+
}
|
|
2570
|
+
currentChunk = remaining;
|
|
2571
|
+
} else {
|
|
2572
|
+
if (currentChunk) {
|
|
2573
|
+
chunks.push(currentChunk);
|
|
2574
|
+
}
|
|
2575
|
+
currentChunk = para;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
if (currentChunk) {
|
|
2579
|
+
chunks.push(currentChunk);
|
|
2580
|
+
}
|
|
2581
|
+
} else {
|
|
2582
|
+
// Plain text mode: split directly by length
|
|
2583
|
+
let remaining = text;
|
|
2584
|
+
while(remaining.length > limit){
|
|
2585
|
+
chunks.push(remaining.slice(0, limit));
|
|
2586
|
+
remaining = remaining.slice(limit);
|
|
2587
|
+
}
|
|
2588
|
+
if (remaining) {
|
|
2589
|
+
chunks.push(remaining);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
return chunks;
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
/**
|
|
2596
|
+
* Metadata key for chat channel
|
|
2597
|
+
*/ const CHAT_CHANNEL = 'CHAT_CHANNEL';
|
|
2598
|
+
/**
|
|
2599
|
+
* Decorator for chat channel implementations
|
|
2600
|
+
*
|
|
2601
|
+
* Use this decorator to register a chat channel implementation.
|
|
2602
|
+
* The decorated class will be automatically discovered and registered
|
|
2603
|
+
* to ChatChannelRegistry on module initialization.
|
|
2604
|
+
*
|
|
2605
|
+
* @param type - The channel type identifier (e.g., 'lark', 'wecom', 'dingtalk')
|
|
2606
|
+
*
|
|
2607
|
+
* @example
|
|
2608
|
+
* ```typescript
|
|
2609
|
+
* @Injectable()
|
|
2610
|
+
* @ChatChannel('lark')
|
|
2611
|
+
* export class LarkChatChannel implements IChatChannel {
|
|
2612
|
+
* // Implementation
|
|
2613
|
+
* }
|
|
2614
|
+
* ```
|
|
2615
|
+
*/ const ChatChannel = (type)=>common.SetMetadata(CHAT_CHANNEL, type);
|
|
2616
|
+
|
|
2617
|
+
exports.ChatChannelRegistry = class ChatChannelRegistry extends BaseStrategyRegistry {
|
|
2618
|
+
constructor(discoveryService, reflector){
|
|
2619
|
+
super(CHAT_CHANNEL, discoveryService, reflector);
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
exports.ChatChannelRegistry = __decorate([
|
|
2623
|
+
common.Injectable(),
|
|
2624
|
+
__metadata("design:type", Function),
|
|
2625
|
+
__metadata("design:paramtypes", [
|
|
2626
|
+
typeof core.DiscoveryService === "undefined" ? Object : core.DiscoveryService,
|
|
2627
|
+
typeof core.Reflector === "undefined" ? Object : core.Reflector
|
|
2628
|
+
])
|
|
2629
|
+
], exports.ChatChannelRegistry);
|
|
2630
|
+
|
|
2631
|
+
class CancelConversationCommand {
|
|
2632
|
+
constructor(input){
|
|
2633
|
+
this.input = input;
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
CancelConversationCommand.type = '[Chat Conversation] Cancel';
|
|
2637
|
+
|
|
2638
|
+
/**
|
|
2639
|
+
* Protocol definition for pluggable memory backends.
|
|
2640
|
+
*
|
|
2641
|
+
* This module defines the BackendProtocol that all backend implementations
|
|
2642
|
+
* must follow. Backends can store files in different locations (state, filesystem,
|
|
2643
|
+
* database, etc.) and provide a uniform interface for file operations.
|
|
2644
|
+
*/ /**
|
|
2645
|
+
* Type guard to check if a backend supports execution.
|
|
2646
|
+
*
|
|
2647
|
+
* @param backend - Backend instance to check
|
|
2648
|
+
* @returns True if the backend implements SandboxBackendProtocol
|
|
2649
|
+
*/ function isSandboxBackend(backend) {
|
|
2650
|
+
return typeof backend.execute === "function" && typeof backend.id === "string";
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
/**
|
|
2654
|
+
* BaseSandbox: Abstract base class for sandbox backends with command execution.
|
|
2655
|
+
*
|
|
2656
|
+
* This class provides default implementations for all SandboxBackendProtocol
|
|
2657
|
+
* methods using shell commands executed via execute(). Concrete implementations
|
|
2658
|
+
* only need to implement the execute() method.
|
|
2659
|
+
*
|
|
2660
|
+
* Requires Node.js 20+ on the sandbox host.
|
|
2661
|
+
*/ const MAX_LINE_LENGTH = 500;
|
|
2662
|
+
const MAX_GREP_LINE_LENGTH = 2000;
|
|
2663
|
+
const GREP_RESULT_LIMIT = 100;
|
|
2664
|
+
const GLOB_RESULT_LIMIT = 100;
|
|
2665
|
+
const WRITE_EXISTS_OUTPUT = "Error: File already exists";
|
|
2666
|
+
/**
|
|
2667
|
+
* UTF-8 safe Base64 encoding function.
|
|
2668
|
+
* Replaces btoa() which only supports Latin1 characters.
|
|
2669
|
+
*/ function utf8ToBase64(str) {
|
|
2670
|
+
return Buffer.from(str, 'utf-8').toString('base64');
|
|
2671
|
+
}
|
|
2672
|
+
/**
|
|
2673
|
+
* Format glob results into human-readable output.
|
|
2674
|
+
*/ function formatGlobOutput(files, pattern) {
|
|
2675
|
+
const limit = GLOB_RESULT_LIMIT;
|
|
2676
|
+
const truncated = files.length > limit;
|
|
2677
|
+
const finalFiles = truncated ? files.slice(0, limit) : files;
|
|
2678
|
+
if (finalFiles.length === 0) {
|
|
2679
|
+
return "No files found";
|
|
2680
|
+
}
|
|
2681
|
+
const lines = [];
|
|
2682
|
+
for (const file of finalFiles){
|
|
2683
|
+
lines.push(file.path);
|
|
2684
|
+
}
|
|
2685
|
+
if (truncated) {
|
|
2686
|
+
lines.push("");
|
|
2687
|
+
lines.push("(Results truncated. Consider using a more specific path or pattern.)");
|
|
2688
|
+
}
|
|
2689
|
+
return lines.join("\n");
|
|
2690
|
+
}
|
|
2691
|
+
/**
|
|
2692
|
+
* Format grep matches into human-readable output grouped by file.
|
|
2693
|
+
*/ function formatGrepOutput(matches) {
|
|
2694
|
+
const limit = GREP_RESULT_LIMIT;
|
|
2695
|
+
const truncated = matches.length > limit;
|
|
2696
|
+
const finalMatches = truncated ? matches.slice(0, limit) : matches;
|
|
2697
|
+
if (finalMatches.length === 0) {
|
|
2698
|
+
return "No matches found";
|
|
2699
|
+
}
|
|
2700
|
+
const lines = [
|
|
2701
|
+
`Found ${finalMatches.length} matches`
|
|
2702
|
+
];
|
|
2703
|
+
let currentFile = "";
|
|
2704
|
+
for (const match of finalMatches){
|
|
2705
|
+
if (currentFile !== match.path) {
|
|
2706
|
+
if (currentFile !== "") {
|
|
2707
|
+
lines.push("");
|
|
2708
|
+
}
|
|
2709
|
+
currentFile = match.path;
|
|
2710
|
+
lines.push(`${match.path}:`);
|
|
2711
|
+
}
|
|
2712
|
+
const text = match.text.length > MAX_GREP_LINE_LENGTH ? match.text.substring(0, MAX_GREP_LINE_LENGTH) + "..." : match.text;
|
|
2713
|
+
lines.push(` Line ${match.line}: ${text}`);
|
|
2714
|
+
}
|
|
2715
|
+
if (truncated) {
|
|
2716
|
+
lines.push("");
|
|
2717
|
+
lines.push("(Results truncated. Consider using a more specific path or pattern.)");
|
|
2718
|
+
}
|
|
2719
|
+
return lines.join("\n");
|
|
2720
|
+
}
|
|
2721
|
+
/**
|
|
2722
|
+
* Node.js command template for glob operations.
|
|
2723
|
+
* Uses web-standard atob() for base64 decoding.
|
|
2724
|
+
*/ function buildGlobCommand(searchPath, pattern) {
|
|
2725
|
+
const pathB64 = utf8ToBase64(searchPath);
|
|
2726
|
+
const patternB64 = utf8ToBase64(pattern);
|
|
2727
|
+
return `node -e "
|
|
2728
|
+
const fs = require('fs');
|
|
2729
|
+
const path = require('path');
|
|
2730
|
+
|
|
2731
|
+
const searchPath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2732
|
+
const pattern = Buffer.from('${patternB64}', 'base64').toString('utf-8');
|
|
2733
|
+
|
|
2734
|
+
function globMatch(relativePath, pattern) {
|
|
2735
|
+
const regexPattern = pattern
|
|
2736
|
+
.replace(/\\*\\*/g, '<<<GLOBSTAR>>>')
|
|
2737
|
+
.replace(/\\*/g, '[^/]*')
|
|
2738
|
+
.replace(/\\?/g, '.')
|
|
2739
|
+
.replace(/<<<GLOBSTAR>>>/g, '.*');
|
|
2740
|
+
return new RegExp('^' + regexPattern + '$').test(relativePath);
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
function walkDir(dir, baseDir, results) {
|
|
2744
|
+
try {
|
|
2745
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
2746
|
+
for (const entry of entries) {
|
|
2747
|
+
const fullPath = path.join(dir, entry.name);
|
|
2748
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
2749
|
+
if (entry.isDirectory()) {
|
|
2750
|
+
walkDir(fullPath, baseDir, results);
|
|
2751
|
+
} else if (globMatch(relativePath, pattern)) {
|
|
2752
|
+
const stat = fs.statSync(fullPath);
|
|
2753
|
+
console.log(JSON.stringify({
|
|
2754
|
+
path: relativePath,
|
|
2755
|
+
size: stat.size,
|
|
2756
|
+
mtime: stat.mtimeMs,
|
|
2757
|
+
isDir: false
|
|
2758
|
+
}));
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
} catch (e) {
|
|
2762
|
+
// Silent failure for non-existent paths
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
try {
|
|
2767
|
+
process.chdir(searchPath);
|
|
2768
|
+
walkDir('.', '.', []);
|
|
2769
|
+
} catch (e) {
|
|
2770
|
+
// Silent failure for non-existent paths
|
|
2771
|
+
}
|
|
2772
|
+
"`;
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Node.js command template for listing directory contents.
|
|
2776
|
+
*/ function buildLsCommand(dirPath) {
|
|
2777
|
+
const pathB64 = utf8ToBase64(dirPath);
|
|
2778
|
+
return `node -e "
|
|
2779
|
+
const fs = require('fs');
|
|
2780
|
+
const path = require('path');
|
|
2781
|
+
|
|
2782
|
+
const dirPath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2783
|
+
|
|
2784
|
+
try {
|
|
2785
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
2786
|
+
for (const entry of entries) {
|
|
2787
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
2788
|
+
const stat = fs.statSync(fullPath);
|
|
2789
|
+
console.log(JSON.stringify({
|
|
2790
|
+
path: entry.isDirectory() ? fullPath + '/' : fullPath,
|
|
2791
|
+
size: stat.size,
|
|
2792
|
+
mtime: stat.mtimeMs,
|
|
2793
|
+
isDir: entry.isDirectory()
|
|
2794
|
+
}));
|
|
2795
|
+
}
|
|
2796
|
+
} catch (e) {
|
|
2797
|
+
console.error('Error: ' + e.message);
|
|
2798
|
+
process.exit(1);
|
|
2799
|
+
}
|
|
2800
|
+
"`;
|
|
2801
|
+
}
|
|
2802
|
+
const INDENTATION_SPACES = 2;
|
|
2803
|
+
const MAX_ENTRY_LENGTH = 500;
|
|
2804
|
+
/**
|
|
2805
|
+
* Node.js command template for recursive directory listing with depth control.
|
|
2806
|
+
*/ function buildListDirCommand(dirPath, offset, limit, depth) {
|
|
2807
|
+
const pathB64 = utf8ToBase64(dirPath);
|
|
2808
|
+
const safeOffset = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 1;
|
|
2809
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 25;
|
|
2810
|
+
const safeDepth = Number.isFinite(depth) && depth > 0 ? Math.floor(depth) : 2;
|
|
2811
|
+
return `node -e "
|
|
2812
|
+
const fs = require('fs');
|
|
2813
|
+
const path = require('path');
|
|
2814
|
+
|
|
2815
|
+
const dirPath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2816
|
+
const offset = ${safeOffset};
|
|
2817
|
+
const limit = ${safeLimit};
|
|
2818
|
+
const maxDepth = ${safeDepth};
|
|
2819
|
+
const INDENTATION_SPACES = ${INDENTATION_SPACES};
|
|
2820
|
+
const MAX_ENTRY_LENGTH = ${MAX_ENTRY_LENGTH};
|
|
2821
|
+
|
|
2822
|
+
if (!path.isAbsolute(dirPath)) {
|
|
2823
|
+
console.error('Error: dir_path must be an absolute path');
|
|
2824
|
+
process.exit(1);
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
if (!fs.existsSync(dirPath)) {
|
|
2828
|
+
console.error('Error: Directory not found');
|
|
2829
|
+
process.exit(1);
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
if (!fs.statSync(dirPath).isDirectory()) {
|
|
2833
|
+
console.error('Error: Path is not a directory');
|
|
2834
|
+
process.exit(1);
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2837
|
+
function collectEntries(currentPath, relativePrefix, remainingDepth, entries) {
|
|
2838
|
+
if (remainingDepth === 0) return;
|
|
2839
|
+
|
|
2840
|
+
try {
|
|
2841
|
+
const dirEntries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
2842
|
+
const sortedEntries = dirEntries
|
|
2843
|
+
.map(entry => {
|
|
2844
|
+
const fullPath = path.join(currentPath, entry.name);
|
|
2845
|
+
const relativePath = path.join(relativePrefix, entry.name);
|
|
2846
|
+
const displayName = entry.name.length > MAX_ENTRY_LENGTH
|
|
2847
|
+
? entry.name.substring(0, MAX_ENTRY_LENGTH)
|
|
2848
|
+
: entry.name;
|
|
2849
|
+
|
|
2850
|
+
let kind = 'file';
|
|
2851
|
+
if (entry.isSymbolicLink()) kind = 'symlink';
|
|
2852
|
+
else if (entry.isDirectory()) kind = 'directory';
|
|
2853
|
+
|
|
2854
|
+
return {
|
|
2855
|
+
fullPath,
|
|
2856
|
+
relativePath,
|
|
2857
|
+
displayName,
|
|
2858
|
+
kind,
|
|
2859
|
+
depth: relativePrefix.split(path.sep).filter(Boolean).length
|
|
2860
|
+
};
|
|
2861
|
+
})
|
|
2862
|
+
.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
2863
|
+
|
|
2864
|
+
for (const entry of sortedEntries) {
|
|
2865
|
+
entries.push(entry);
|
|
2866
|
+
if (entry.kind === 'directory' && remainingDepth > 1) {
|
|
2867
|
+
collectEntries(entry.fullPath, entry.relativePath, remainingDepth - 1, entries);
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
} catch (e) {
|
|
2871
|
+
// Skip unreadable directories
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
const allEntries = [];
|
|
2876
|
+
collectEntries(dirPath, '', maxDepth, allEntries);
|
|
2877
|
+
|
|
2878
|
+
if (allEntries.length === 0) {
|
|
2879
|
+
console.log('Absolute path: ' + dirPath);
|
|
2880
|
+
console.log('(Empty directory)');
|
|
2881
|
+
process.exit(0);
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
const startIndex = offset - 1;
|
|
2885
|
+
if (startIndex >= allEntries.length) {
|
|
2886
|
+
console.error('Error: offset exceeds directory entry count');
|
|
2887
|
+
process.exit(1);
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
const endIndex = Math.min(startIndex + limit, allEntries.length);
|
|
2891
|
+
const selectedEntries = allEntries.slice(startIndex, endIndex);
|
|
2892
|
+
|
|
2893
|
+
console.log('Absolute path: ' + dirPath);
|
|
2894
|
+
|
|
2895
|
+
for (const entry of selectedEntries) {
|
|
2896
|
+
const indent = ' '.repeat(entry.depth * INDENTATION_SPACES);
|
|
2897
|
+
let name = entry.displayName;
|
|
2898
|
+
if (entry.kind === 'directory') name += '/';
|
|
2899
|
+
else if (entry.kind === 'symlink') name += '@';
|
|
2900
|
+
console.log(indent + name);
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
if (endIndex < allEntries.length) {
|
|
2904
|
+
console.log('More than ' + limit + ' entries found');
|
|
2905
|
+
}
|
|
2906
|
+
"`;
|
|
2907
|
+
}
|
|
2908
|
+
const TAB_WIDTH = 4;
|
|
2909
|
+
const COMMENT_PREFIXES = [
|
|
2910
|
+
'#',
|
|
2911
|
+
'//',
|
|
2912
|
+
'--'
|
|
2913
|
+
];
|
|
2914
|
+
/**
|
|
2915
|
+
* Node.js command template for slice mode reading.
|
|
2916
|
+
*/ function buildSliceReadCommand(filePath, offset, limit) {
|
|
2917
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
2918
|
+
const safeOffset = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) - 1 : 0;
|
|
2919
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER ? Math.floor(limit) : 2000;
|
|
2920
|
+
return `node -e "
|
|
2921
|
+
const fs = require('fs');
|
|
2922
|
+
const MAX_LINE_LENGTH = ${MAX_LINE_LENGTH};
|
|
2923
|
+
|
|
2924
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2925
|
+
const offset = ${safeOffset};
|
|
2926
|
+
const limit = ${safeLimit};
|
|
2927
|
+
|
|
2928
|
+
if (!fs.existsSync(filePath)) {
|
|
2929
|
+
console.log('Error: File not found');
|
|
2930
|
+
process.exit(1);
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
const stat = fs.statSync(filePath);
|
|
2934
|
+
if (stat.size === 0) {
|
|
2935
|
+
console.log('System reminder: File exists but has empty contents');
|
|
2936
|
+
process.exit(0);
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
2940
|
+
const lines = content.split('\\n');
|
|
2941
|
+
|
|
2942
|
+
if (offset >= lines.length) {
|
|
2943
|
+
console.log('Error: offset exceeds file length');
|
|
2944
|
+
process.exit(1);
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
const selected = lines.slice(offset, offset + limit);
|
|
2948
|
+
|
|
2949
|
+
for (let i = 0; i < selected.length; i++) {
|
|
2950
|
+
const lineNum = offset + i + 1;
|
|
2951
|
+
let line = selected[i];
|
|
2952
|
+
if (line.length > MAX_LINE_LENGTH) {
|
|
2953
|
+
line = line.substring(0, MAX_LINE_LENGTH);
|
|
2954
|
+
}
|
|
2955
|
+
console.log('L' + lineNum + ': ' + line);
|
|
2956
|
+
}
|
|
2957
|
+
"`;
|
|
2958
|
+
}
|
|
2959
|
+
/**
|
|
2960
|
+
* Node.js command template for indentation mode reading.
|
|
2961
|
+
*/ function buildIndentationReadCommand(filePath, offset, limit, options) {
|
|
2962
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
2963
|
+
const safeOffset = Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 1;
|
|
2964
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 && limit < Number.MAX_SAFE_INTEGER ? Math.floor(limit) : 2000;
|
|
2965
|
+
var _options_anchor_line;
|
|
2966
|
+
const anchorLine = (_options_anchor_line = options.anchor_line) != null ? _options_anchor_line : safeOffset;
|
|
2967
|
+
var _options_max_levels;
|
|
2968
|
+
const maxLevels = (_options_max_levels = options.max_levels) != null ? _options_max_levels : 0;
|
|
2969
|
+
var _options_include_siblings;
|
|
2970
|
+
const includeSiblings = (_options_include_siblings = options.include_siblings) != null ? _options_include_siblings : false;
|
|
2971
|
+
var _options_include_header;
|
|
2972
|
+
const includeHeader = (_options_include_header = options.include_header) != null ? _options_include_header : true;
|
|
2973
|
+
var _options_max_lines;
|
|
2974
|
+
const maxLines = (_options_max_lines = options.max_lines) != null ? _options_max_lines : safeLimit;
|
|
2975
|
+
return `node -e "
|
|
2976
|
+
const fs = require('fs');
|
|
2977
|
+
const MAX_LINE_LENGTH = ${MAX_LINE_LENGTH};
|
|
2978
|
+
const TAB_WIDTH = ${TAB_WIDTH};
|
|
2979
|
+
const COMMENT_PREFIXES = ${JSON.stringify(COMMENT_PREFIXES)};
|
|
2980
|
+
|
|
2981
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
2982
|
+
const anchorLine = ${anchorLine};
|
|
2983
|
+
const limit = ${safeLimit};
|
|
2984
|
+
const maxLevels = ${maxLevels};
|
|
2985
|
+
const includeSiblings = ${includeSiblings};
|
|
2986
|
+
const includeHeader = ${includeHeader};
|
|
2987
|
+
const maxLines = ${maxLines};
|
|
2988
|
+
|
|
2989
|
+
if (!fs.existsSync(filePath)) {
|
|
2990
|
+
console.log('Error: File not found');
|
|
2991
|
+
process.exit(1);
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
2995
|
+
const rawLines = content.split('\\n');
|
|
2996
|
+
|
|
2997
|
+
if (rawLines.length === 0 || anchorLine > rawLines.length) {
|
|
2998
|
+
console.log('Error: anchor_line exceeds file length');
|
|
2999
|
+
process.exit(1);
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
function measureIndent(line) {
|
|
3003
|
+
let indent = 0;
|
|
3004
|
+
for (const c of line) {
|
|
3005
|
+
if (c === ' ') indent++;
|
|
3006
|
+
else if (c === '\\t') indent += TAB_WIDTH;
|
|
3007
|
+
else break;
|
|
3008
|
+
}
|
|
3009
|
+
return indent;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
function isBlank(line) { return line.trim() === ''; }
|
|
3013
|
+
function isComment(line) { return COMMENT_PREFIXES.some(p => line.trim().startsWith(p)); }
|
|
3014
|
+
function formatLine(line) { return line.length > MAX_LINE_LENGTH ? line.substring(0, MAX_LINE_LENGTH) : line; }
|
|
3015
|
+
|
|
3016
|
+
const records = rawLines.map((raw, i) => ({
|
|
3017
|
+
number: i + 1,
|
|
3018
|
+
raw,
|
|
3019
|
+
display: formatLine(raw),
|
|
3020
|
+
indent: measureIndent(raw)
|
|
3021
|
+
}));
|
|
3022
|
+
|
|
3023
|
+
const effectiveIndents = [];
|
|
3024
|
+
let prevIndent = 0;
|
|
3025
|
+
for (const r of records) {
|
|
3026
|
+
if (isBlank(r.raw)) {
|
|
3027
|
+
effectiveIndents.push(prevIndent);
|
|
3028
|
+
} else {
|
|
3029
|
+
prevIndent = r.indent;
|
|
3030
|
+
effectiveIndents.push(prevIndent);
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
const anchorIndex = anchorLine - 1;
|
|
3035
|
+
const anchorIndent = effectiveIndents[anchorIndex];
|
|
3036
|
+
const minIndent = maxLevels === 0 ? 0 : Math.max(0, anchorIndent - maxLevels * TAB_WIDTH);
|
|
3037
|
+
const finalLimit = Math.min(limit, maxLines, records.length);
|
|
3038
|
+
|
|
3039
|
+
if (finalLimit === 1) {
|
|
3040
|
+
console.log('L' + records[anchorIndex].number + ': ' + records[anchorIndex].display);
|
|
3041
|
+
process.exit(0);
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
const out = [records[anchorIndex]];
|
|
3045
|
+
let i = anchorIndex - 1;
|
|
3046
|
+
let j = anchorIndex + 1;
|
|
3047
|
+
let iCounterMinIndent = 0;
|
|
3048
|
+
let jCounterMinIndent = 0;
|
|
3049
|
+
|
|
3050
|
+
while (out.length < finalLimit) {
|
|
3051
|
+
let progressed = 0;
|
|
3052
|
+
|
|
3053
|
+
if (i >= 0) {
|
|
3054
|
+
if (effectiveIndents[i] >= minIndent) {
|
|
3055
|
+
out.unshift(records[i]);
|
|
3056
|
+
progressed++;
|
|
3057
|
+
const curI = i;
|
|
3058
|
+
i--;
|
|
3059
|
+
|
|
3060
|
+
if (effectiveIndents[curI] === minIndent && !includeSiblings) {
|
|
3061
|
+
const allowHeaderComment = includeHeader && isComment(records[curI].raw);
|
|
3062
|
+
const canTakeLine = allowHeaderComment || iCounterMinIndent === 0;
|
|
3063
|
+
if (canTakeLine) {
|
|
3064
|
+
iCounterMinIndent++;
|
|
3065
|
+
} else {
|
|
3066
|
+
out.shift();
|
|
3067
|
+
progressed--;
|
|
3068
|
+
i = -1;
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
if (out.length >= finalLimit) break;
|
|
3073
|
+
} else {
|
|
3074
|
+
i = -1;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
if (j < records.length) {
|
|
3079
|
+
if (effectiveIndents[j] >= minIndent) {
|
|
3080
|
+
out.push(records[j]);
|
|
3081
|
+
progressed++;
|
|
3082
|
+
const curJ = j;
|
|
3083
|
+
j++;
|
|
3084
|
+
|
|
3085
|
+
if (effectiveIndents[curJ] === minIndent && !includeSiblings) {
|
|
3086
|
+
if (jCounterMinIndent > 0) {
|
|
3087
|
+
out.pop();
|
|
3088
|
+
progressed--;
|
|
3089
|
+
j = records.length;
|
|
3090
|
+
}
|
|
3091
|
+
jCounterMinIndent++;
|
|
3092
|
+
}
|
|
3093
|
+
} else {
|
|
3094
|
+
j = records.length;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
if (progressed === 0) break;
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
while (out.length > 0 && isBlank(out[0].raw)) out.shift();
|
|
3102
|
+
while (out.length > 0 && isBlank(out[out.length - 1].raw)) out.pop();
|
|
3103
|
+
|
|
3104
|
+
for (const r of out) {
|
|
3105
|
+
console.log('L' + r.number + ': ' + r.display);
|
|
3106
|
+
}
|
|
3107
|
+
"`;
|
|
3108
|
+
}
|
|
3109
|
+
/**
|
|
3110
|
+
* Node.js command template for writing files.
|
|
3111
|
+
*/ function buildWriteCommand(filePath, content) {
|
|
3112
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3113
|
+
const contentB64 = utf8ToBase64(content);
|
|
3114
|
+
return `node -e "
|
|
3115
|
+
const fs = require('fs');
|
|
3116
|
+
const path = require('path');
|
|
3117
|
+
|
|
3118
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3119
|
+
const content = Buffer.from('${contentB64}', 'base64').toString('utf-8');
|
|
3120
|
+
|
|
3121
|
+
if (fs.existsSync(filePath)) {
|
|
3122
|
+
console.error('${WRITE_EXISTS_OUTPUT}');
|
|
3123
|
+
process.exit(1);
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
const parentDir = path.dirname(filePath) || '.';
|
|
3127
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
3128
|
+
|
|
3129
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
3130
|
+
console.log('OK');
|
|
3131
|
+
"`;
|
|
3132
|
+
}
|
|
3133
|
+
function buildExistsCommand(filePath) {
|
|
3134
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3135
|
+
return `node -e "
|
|
3136
|
+
const fs = require('fs');
|
|
3137
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3138
|
+
process.exit(fs.existsSync(filePath) ? 0 : 1);
|
|
3139
|
+
"`;
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* Node.js command template for appending to files.
|
|
3143
|
+
*/ function buildAppendCommand(filePath, content) {
|
|
3144
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3145
|
+
const contentB64 = utf8ToBase64(content);
|
|
3146
|
+
return `node -e "
|
|
3147
|
+
const fs = require('fs');
|
|
3148
|
+
const path = require('path');
|
|
3149
|
+
|
|
3150
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3151
|
+
const content = Buffer.from('${contentB64}', 'base64').toString('utf-8');
|
|
3152
|
+
|
|
3153
|
+
const parentDir = path.dirname(filePath) || '.';
|
|
3154
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
3155
|
+
|
|
3156
|
+
fs.appendFileSync(filePath, content, 'utf-8');
|
|
3157
|
+
console.log('OK');
|
|
3158
|
+
"`;
|
|
3159
|
+
}
|
|
3160
|
+
/**
|
|
3161
|
+
* Node.js command template for editing files.
|
|
3162
|
+
*/ function buildEditCommand(filePath, oldStr, newStr, replaceAll) {
|
|
3163
|
+
const pathB64 = utf8ToBase64(filePath);
|
|
3164
|
+
const oldB64 = utf8ToBase64(oldStr);
|
|
3165
|
+
const newB64 = utf8ToBase64(newStr);
|
|
3166
|
+
return `node -e "
|
|
3167
|
+
const fs = require('fs');
|
|
3168
|
+
|
|
3169
|
+
const filePath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3170
|
+
const oldStr = Buffer.from('${oldB64}', 'base64').toString('utf-8');
|
|
3171
|
+
const newStr = Buffer.from('${newB64}', 'base64').toString('utf-8');
|
|
3172
|
+
const replaceAll = ${Boolean(replaceAll)};
|
|
3173
|
+
|
|
3174
|
+
let text;
|
|
3175
|
+
try {
|
|
3176
|
+
text = fs.readFileSync(filePath, 'utf-8');
|
|
3177
|
+
} catch (e) {
|
|
3178
|
+
process.exit(3);
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
const count = text.split(oldStr).length - 1;
|
|
3182
|
+
|
|
3183
|
+
if (count === 0) {
|
|
3184
|
+
process.exit(1);
|
|
3185
|
+
}
|
|
3186
|
+
if (count > 1 && !replaceAll) {
|
|
3187
|
+
process.exit(2);
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
const result = text.split(oldStr).join(newStr);
|
|
3191
|
+
fs.writeFileSync(filePath, result, 'utf-8');
|
|
3192
|
+
console.log(count);
|
|
3193
|
+
"`;
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Node.js command template for grep operations.
|
|
3197
|
+
*/ function buildGrepCommand(pattern, searchPath, globPattern) {
|
|
3198
|
+
const patternB64 = utf8ToBase64(pattern);
|
|
3199
|
+
const pathB64 = utf8ToBase64(searchPath);
|
|
3200
|
+
const globB64 = globPattern ? utf8ToBase64(globPattern) : "";
|
|
3201
|
+
return `node -e "
|
|
3202
|
+
const fs = require('fs');
|
|
3203
|
+
const path = require('path');
|
|
3204
|
+
|
|
3205
|
+
const pattern = Buffer.from('${patternB64}', 'base64').toString('utf-8');
|
|
3206
|
+
const searchPath = Buffer.from('${pathB64}', 'base64').toString('utf-8');
|
|
3207
|
+
const globPattern = ${globPattern ? `Buffer.from('${globB64}', 'base64').toString('utf-8')` : "null"};
|
|
3208
|
+
|
|
3209
|
+
let regex;
|
|
3210
|
+
try {
|
|
3211
|
+
regex = new RegExp(pattern);
|
|
3212
|
+
} catch (e) {
|
|
3213
|
+
console.error('Invalid regex: ' + e.message);
|
|
3214
|
+
process.exit(1);
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3217
|
+
function globMatch(filePath, pattern) {
|
|
3218
|
+
if (!pattern) return true;
|
|
3219
|
+
const regexPattern = pattern
|
|
3220
|
+
.replace(/\\*\\*/g, '<<<GLOBSTAR>>>')
|
|
3221
|
+
.replace(/\\*/g, '[^/]*')
|
|
3222
|
+
.replace(/\\?/g, '.')
|
|
3223
|
+
.replace(/<<<GLOBSTAR>>>/g, '.*');
|
|
3224
|
+
return new RegExp('^' + regexPattern + '$').test(filePath);
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
function walkDir(dir, results) {
|
|
3228
|
+
try {
|
|
3229
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
3230
|
+
for (const entry of entries) {
|
|
3231
|
+
const fullPath = path.join(dir, entry.name);
|
|
3232
|
+
if (entry.isDirectory()) {
|
|
3233
|
+
walkDir(fullPath, results);
|
|
3234
|
+
} else {
|
|
3235
|
+
const relativePath = path.relative(searchPath, fullPath);
|
|
3236
|
+
if (globMatch(relativePath, globPattern)) {
|
|
3237
|
+
try {
|
|
3238
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
3239
|
+
const lines = content.split('\\n');
|
|
3240
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3241
|
+
if (regex.test(lines[i])) {
|
|
3242
|
+
console.log(JSON.stringify({
|
|
3243
|
+
path: fullPath,
|
|
3244
|
+
line: i + 1,
|
|
3245
|
+
text: lines[i]
|
|
3246
|
+
}));
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
} catch (e) {
|
|
3250
|
+
// Skip unreadable files
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
} catch (e) {
|
|
3256
|
+
// Skip unreadable directories
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
try {
|
|
3261
|
+
walkDir(searchPath, []);
|
|
3262
|
+
} catch (e) {
|
|
3263
|
+
// Silent failure
|
|
3264
|
+
}
|
|
3265
|
+
"`;
|
|
3266
|
+
}
|
|
3267
|
+
/**
|
|
3268
|
+
* Base sandbox implementation with execute() as the only abstract method.
|
|
3269
|
+
*
|
|
3270
|
+
* This class provides default implementations for all SandboxBackendProtocol
|
|
3271
|
+
* methods using shell commands executed via execute(). Concrete implementations
|
|
3272
|
+
* only need to implement the execute() method.
|
|
3273
|
+
*
|
|
3274
|
+
* Requires Node.js 20+ on the sandbox host.
|
|
3275
|
+
*/ class BaseSandbox {
|
|
3276
|
+
async streamExecute(command, onLine) {
|
|
3277
|
+
const result = await this.execute(command);
|
|
3278
|
+
if (result.output) {
|
|
3279
|
+
onLine(result.output);
|
|
3280
|
+
}
|
|
3281
|
+
return result;
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* List files and directories in the specified directory (non-recursive).
|
|
3285
|
+
*
|
|
3286
|
+
* @param path - Absolute path to directory
|
|
3287
|
+
* @returns List of FileInfo objects for files and directories directly in the directory.
|
|
3288
|
+
*/ async lsInfo(path) {
|
|
3289
|
+
const command = buildLsCommand(path);
|
|
3290
|
+
const result = await this.execute(command);
|
|
3291
|
+
if (result.exitCode !== 0) {
|
|
3292
|
+
return [];
|
|
3293
|
+
}
|
|
3294
|
+
const infos = [];
|
|
3295
|
+
const lines = result.output.trim().split("\n").filter(Boolean);
|
|
3296
|
+
for (const line of lines){
|
|
3297
|
+
try {
|
|
3298
|
+
const parsed = JSON.parse(line);
|
|
3299
|
+
infos.push({
|
|
3300
|
+
path: parsed.path,
|
|
3301
|
+
is_dir: parsed.isDir,
|
|
3302
|
+
size: parsed.size,
|
|
3303
|
+
modified_at: parsed.mtime ? new Date(parsed.mtime).toISOString() : undefined
|
|
3304
|
+
});
|
|
3305
|
+
} catch (e) {
|
|
3306
|
+
// Skip invalid JSON lines
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
return infos;
|
|
3310
|
+
}
|
|
3311
|
+
/**
|
|
3312
|
+
* List directory contents recursively with depth control and pagination.
|
|
3313
|
+
*
|
|
3314
|
+
* @param dirPath - Absolute path to directory
|
|
3315
|
+
* @param offset - 1-indexed entry number to start from (default: 1)
|
|
3316
|
+
* @param limit - Maximum number of entries to return (default: 25)
|
|
3317
|
+
* @param depth - Maximum depth to traverse (default: 2)
|
|
3318
|
+
* @returns Human-readable directory tree with indentation
|
|
3319
|
+
*/ async listDir(dirPath, offset = 1, limit = 25, depth = 2) {
|
|
3320
|
+
if (offset < 1) {
|
|
3321
|
+
return "Error: offset must be a 1-indexed entry number";
|
|
3322
|
+
}
|
|
3323
|
+
if (limit < 1) {
|
|
3324
|
+
return "Error: limit must be greater than zero";
|
|
3325
|
+
}
|
|
3326
|
+
if (depth < 1) {
|
|
3327
|
+
return "Error: depth must be greater than zero";
|
|
3328
|
+
}
|
|
3329
|
+
const command = buildListDirCommand(dirPath, offset, limit, depth);
|
|
3330
|
+
const result = await this.execute(command);
|
|
3331
|
+
if (result.exitCode !== 0) {
|
|
3332
|
+
return result.output || `Error: Failed to list directory '${dirPath}'`;
|
|
3333
|
+
}
|
|
3334
|
+
return result.output;
|
|
3335
|
+
}
|
|
3336
|
+
/**
|
|
3337
|
+
* Read file content with line numbers.
|
|
3338
|
+
*
|
|
3339
|
+
* @param filePath - Absolute file path
|
|
3340
|
+
* @param offset - Line offset to start reading from (1-indexed)
|
|
3341
|
+
* @param limit - Maximum number of lines to read
|
|
3342
|
+
* @param mode - Read mode: 'slice' (default) or 'indentation'
|
|
3343
|
+
* @param indentation - Configuration for indentation mode
|
|
3344
|
+
* @returns Formatted file content with line numbers, or error message
|
|
3345
|
+
*/ async read(filePath, offset = 1, limit = 2000, mode = 'slice', indentation) {
|
|
3346
|
+
const command = mode === 'indentation' ? buildIndentationReadCommand(filePath, offset, limit, indentation != null ? indentation : {}) : buildSliceReadCommand(filePath, offset, limit);
|
|
3347
|
+
const result = await this.execute(command);
|
|
3348
|
+
if (result.exitCode !== 0) {
|
|
3349
|
+
return result.output || `Error: File '${filePath}' not found`;
|
|
3350
|
+
}
|
|
3351
|
+
return result.output;
|
|
3352
|
+
}
|
|
3353
|
+
/**
|
|
3354
|
+
* Structured search results or error string for invalid input.
|
|
3355
|
+
*/ async grepRaw(pattern, path = "/", include = null) {
|
|
3356
|
+
const command = buildGrepCommand(pattern, path, include);
|
|
3357
|
+
const result = await this.execute(command);
|
|
3358
|
+
if (result.exitCode === 1) {
|
|
3359
|
+
// Check if it's a regex error
|
|
3360
|
+
if (result.output.includes("Invalid regex:")) {
|
|
3361
|
+
return result.output.trim();
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
const matches = [];
|
|
3365
|
+
const lines = result.output.trim().split("\n").filter(Boolean);
|
|
3366
|
+
for (const line of lines){
|
|
3367
|
+
try {
|
|
3368
|
+
const parsed = JSON.parse(line);
|
|
3369
|
+
matches.push({
|
|
3370
|
+
path: parsed.path,
|
|
3371
|
+
line: parsed.line,
|
|
3372
|
+
text: parsed.text
|
|
3373
|
+
});
|
|
3374
|
+
} catch (e) {
|
|
3375
|
+
// Skip invalid JSON lines
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
return matches;
|
|
3379
|
+
}
|
|
3380
|
+
/**
|
|
3381
|
+
* Search file contents for a regex pattern, returning human-readable output.
|
|
3382
|
+
*/ async grep(pattern, path = "/", include = null) {
|
|
3383
|
+
const result = await this.grepRaw(pattern, path, include);
|
|
3384
|
+
if (typeof result === "string") {
|
|
3385
|
+
return result;
|
|
3386
|
+
}
|
|
3387
|
+
if (result.length === 0) {
|
|
3388
|
+
return "No matches found";
|
|
3389
|
+
}
|
|
3390
|
+
return formatGrepOutput(result);
|
|
3391
|
+
}
|
|
3392
|
+
/**
|
|
3393
|
+
* Structured glob matching returning FileInfo objects.
|
|
3394
|
+
*/ async globInfo(pattern, path = "/") {
|
|
3395
|
+
const command = buildGlobCommand(path, pattern);
|
|
3396
|
+
const result = await this.execute(command);
|
|
3397
|
+
const infos = [];
|
|
3398
|
+
const lines = result.output.trim().split("\n").filter(Boolean);
|
|
3399
|
+
for (const line of lines){
|
|
3400
|
+
try {
|
|
3401
|
+
const parsed = JSON.parse(line);
|
|
3402
|
+
infos.push({
|
|
3403
|
+
path: parsed.path,
|
|
3404
|
+
is_dir: parsed.isDir,
|
|
3405
|
+
size: parsed.size,
|
|
3406
|
+
modified_at: parsed.mtime ? new Date(parsed.mtime).toISOString() : undefined
|
|
3407
|
+
});
|
|
3408
|
+
} catch (e) {
|
|
3409
|
+
// Skip invalid JSON lines
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
return infos;
|
|
3413
|
+
}
|
|
3414
|
+
/**
|
|
3415
|
+
* Find files matching a glob pattern, returning human-readable output.
|
|
3416
|
+
*/ async glob(pattern, path = "/") {
|
|
3417
|
+
const files = await this.globInfo(pattern, path);
|
|
3418
|
+
if (files.length === 0) {
|
|
3419
|
+
return "No files found";
|
|
3420
|
+
}
|
|
3421
|
+
return formatGlobOutput(files);
|
|
3422
|
+
}
|
|
3423
|
+
/**
|
|
3424
|
+
* Create a new file with content.
|
|
3425
|
+
*/ async write(filePath, content) {
|
|
3426
|
+
const command = buildWriteCommand(filePath, content);
|
|
3427
|
+
const result = await this.execute(command);
|
|
3428
|
+
if (result.exitCode !== 0) {
|
|
3429
|
+
const output = (result.output || "").trim();
|
|
3430
|
+
if (output.includes(WRITE_EXISTS_OUTPUT)) {
|
|
3431
|
+
return {
|
|
3432
|
+
error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
const fallback = await this.writeViaUpload(filePath, content);
|
|
3436
|
+
if (!fallback.error) {
|
|
3437
|
+
return fallback;
|
|
3438
|
+
}
|
|
3439
|
+
return {
|
|
3440
|
+
error: output ? `Failed to write file '${filePath}': ${output}` : `Failed to write file '${filePath}'.`
|
|
3441
|
+
};
|
|
3442
|
+
}
|
|
3443
|
+
return {
|
|
3444
|
+
path: filePath,
|
|
3445
|
+
filesUpdate: null
|
|
3446
|
+
};
|
|
3447
|
+
}
|
|
3448
|
+
async writeViaUpload(filePath, content) {
|
|
3449
|
+
const existsCheck = await this.execute(buildExistsCommand(filePath));
|
|
3450
|
+
if (existsCheck.exitCode === 0) {
|
|
3451
|
+
return {
|
|
3452
|
+
error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
|
|
3453
|
+
};
|
|
3454
|
+
}
|
|
3455
|
+
const uploads = await this.uploadFiles([
|
|
3456
|
+
[
|
|
3457
|
+
filePath,
|
|
3458
|
+
Buffer.from(content, "utf-8")
|
|
3459
|
+
]
|
|
3460
|
+
]);
|
|
3461
|
+
const first = uploads[0];
|
|
3462
|
+
if (!first) {
|
|
3463
|
+
return {
|
|
3464
|
+
error: `Failed to write file '${filePath}': upload returned no result.`
|
|
3465
|
+
};
|
|
3466
|
+
}
|
|
3467
|
+
if (first.error) {
|
|
3468
|
+
return {
|
|
3469
|
+
error: `Failed to write file '${filePath}': ${first.error}`
|
|
3470
|
+
};
|
|
3471
|
+
}
|
|
3472
|
+
return {
|
|
3473
|
+
path: filePath,
|
|
3474
|
+
filesUpdate: null
|
|
3475
|
+
};
|
|
3476
|
+
}
|
|
3477
|
+
/**
|
|
3478
|
+
* Append content to a file. Creates the file if it doesn't exist.
|
|
3479
|
+
*/ async append(filePath, content) {
|
|
3480
|
+
const command = buildAppendCommand(filePath, content);
|
|
3481
|
+
const result = await this.execute(command);
|
|
3482
|
+
if (result.exitCode !== 0) {
|
|
3483
|
+
const output = (result.output || "").trim();
|
|
3484
|
+
return {
|
|
3485
|
+
error: output ? `Failed to append to file '${filePath}': ${output}` : `Failed to append to file '${filePath}'.`
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3488
|
+
return {
|
|
3489
|
+
path: filePath,
|
|
3490
|
+
filesUpdate: null
|
|
3491
|
+
};
|
|
3492
|
+
}
|
|
3493
|
+
/**
|
|
3494
|
+
* Edit a file by replacing string occurrences.
|
|
3495
|
+
*/ async edit(filePath, oldString, newString, replaceAll = false) {
|
|
3496
|
+
const command = buildEditCommand(filePath, oldString, newString, replaceAll);
|
|
3497
|
+
const result = await this.execute(command);
|
|
3498
|
+
switch(result.exitCode){
|
|
3499
|
+
case 0:
|
|
3500
|
+
{
|
|
3501
|
+
const occurrences = parseInt(result.output.trim(), 10) || 1;
|
|
3502
|
+
return {
|
|
3503
|
+
path: filePath,
|
|
3504
|
+
filesUpdate: null,
|
|
3505
|
+
occurrences
|
|
3506
|
+
};
|
|
3507
|
+
}
|
|
3508
|
+
case 1:
|
|
3509
|
+
return {
|
|
3510
|
+
error: `String not found in file '${filePath}'`
|
|
3511
|
+
};
|
|
3512
|
+
case 2:
|
|
3513
|
+
return {
|
|
3514
|
+
error: `Multiple occurrences found in '${filePath}'. Use replaceAll=true to replace all.`
|
|
3515
|
+
};
|
|
3516
|
+
case 3:
|
|
3517
|
+
return {
|
|
3518
|
+
error: `Error: File '${filePath}' not found`
|
|
3519
|
+
};
|
|
3520
|
+
default:
|
|
3521
|
+
return {
|
|
3522
|
+
error: `Unknown error editing file '${filePath}'`
|
|
3523
|
+
};
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
/**
|
|
3527
|
+
* Perform multiple sequential edits on a single file.
|
|
3528
|
+
* All edits are applied sequentially, with each edit operating on the result of the previous edit.
|
|
3529
|
+
* All edits must succeed for the operation to succeed (atomic).
|
|
3530
|
+
*/ async multiEdit(filePath, edits) {
|
|
3531
|
+
if (!edits || edits.length === 0) {
|
|
3532
|
+
return {
|
|
3533
|
+
error: "No edits provided"
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
3536
|
+
const results = [];
|
|
3537
|
+
for (const edit of edits){
|
|
3538
|
+
var _edit_replaceAll;
|
|
3539
|
+
const result = await this.edit(filePath, edit.oldString, edit.newString, (_edit_replaceAll = edit.replaceAll) != null ? _edit_replaceAll : false);
|
|
3540
|
+
results.push(result);
|
|
3541
|
+
// If any edit fails, return immediately with error
|
|
3542
|
+
if (result.error) {
|
|
3543
|
+
return {
|
|
3544
|
+
error: `Multi-edit failed at edit ${results.length}: ${result.error}`,
|
|
3545
|
+
path: filePath,
|
|
3546
|
+
filesUpdate: null,
|
|
3547
|
+
results
|
|
3548
|
+
};
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
// All edits succeeded
|
|
3552
|
+
return {
|
|
3553
|
+
path: filePath,
|
|
3554
|
+
filesUpdate: null,
|
|
3555
|
+
results
|
|
3556
|
+
};
|
|
3557
|
+
}
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3560
|
+
const SANDBOX_PROVIDER = 'SANDBOX_PROVIDER';
|
|
3561
|
+
const SandboxProviderStrategy = (provider)=>common.applyDecorators(common.SetMetadata(SANDBOX_PROVIDER, provider), common.SetMetadata(STRATEGY_META_KEY, SANDBOX_PROVIDER));
|
|
3562
|
+
|
|
3563
|
+
exports.SandboxProviderRegistry = class SandboxProviderRegistry extends BaseStrategyRegistry {
|
|
3564
|
+
constructor(discoveryService, reflector){
|
|
3565
|
+
super(SANDBOX_PROVIDER, discoveryService, reflector);
|
|
3566
|
+
}
|
|
3567
|
+
};
|
|
3568
|
+
exports.SandboxProviderRegistry = __decorate([
|
|
3569
|
+
common.Injectable(),
|
|
3570
|
+
__metadata("design:type", Function),
|
|
3571
|
+
__metadata("design:paramtypes", [
|
|
3572
|
+
typeof core.DiscoveryService === "undefined" ? Object : core.DiscoveryService,
|
|
3573
|
+
typeof core.Reflector === "undefined" ? Object : core.Reflector
|
|
3574
|
+
])
|
|
3575
|
+
], exports.SandboxProviderRegistry);
|
|
3576
|
+
|
|
2322
3577
|
Object.defineProperty(exports, "IColumnDef", {
|
|
2323
3578
|
enumerable: true,
|
|
2324
3579
|
get: function () { return contracts.IColumnDef; }
|
|
@@ -2335,32 +3590,45 @@ Object.defineProperty(exports, "TDocumentAsset", {
|
|
|
2335
3590
|
enumerable: true,
|
|
2336
3591
|
get: function () { return contracts.TDocumentAsset; }
|
|
2337
3592
|
});
|
|
3593
|
+
exports.AGENT_CHAT_DISPATCH_MESSAGE_TYPE = AGENT_CHAT_DISPATCH_MESSAGE_TYPE;
|
|
2338
3594
|
exports.AGENT_MIDDLEWARE_STRATEGY = AGENT_MIDDLEWARE_STRATEGY;
|
|
2339
3595
|
exports.AIModelProviderNotFoundException = AIModelProviderNotFoundException;
|
|
2340
3596
|
exports.AIModelProviderStrategy = AIModelProviderStrategy;
|
|
2341
3597
|
exports.AI_MODEL_PROVIDER = AI_MODEL_PROVIDER;
|
|
3598
|
+
exports.ANALYTICS_PERMISSION_SERVICE_TOKEN = ANALYTICS_PERMISSION_SERVICE_TOKEN;
|
|
2342
3599
|
exports.AdapterDataSourceStrategy = AdapterDataSourceStrategy;
|
|
2343
3600
|
exports.AgentMiddlewareStrategy = AgentMiddlewareStrategy;
|
|
2344
3601
|
exports.AiModelNotFoundException = AiModelNotFoundException;
|
|
2345
3602
|
exports.BaseHTTPQueryRunner = BaseHTTPQueryRunner;
|
|
2346
3603
|
exports.BaseQueryRunner = BaseQueryRunner;
|
|
2347
3604
|
exports.BaseSQLQueryRunner = BaseSQLQueryRunner;
|
|
3605
|
+
exports.BaseSandbox = BaseSandbox;
|
|
2348
3606
|
exports.BaseStrategyRegistry = BaseStrategyRegistry;
|
|
2349
3607
|
exports.BaseTool = BaseTool;
|
|
2350
3608
|
exports.BaseToolset = BaseToolset;
|
|
2351
3609
|
exports.BuiltinToolset = BuiltinToolset;
|
|
3610
|
+
exports.CHAT_CHANNEL = CHAT_CHANNEL;
|
|
3611
|
+
exports.CHAT_CHANNEL_TEXT_LIMITS = CHAT_CHANNEL_TEXT_LIMITS;
|
|
3612
|
+
exports.CancelConversationCommand = CancelConversationCommand;
|
|
3613
|
+
exports.ChatChannel = ChatChannel;
|
|
2352
3614
|
exports.ChatOAICompatReasoningModel = ChatOAICompatReasoningModel;
|
|
2353
3615
|
exports.CommonParameterRules = CommonParameterRules;
|
|
2354
3616
|
exports.CreateModelClientCommand = CreateModelClientCommand;
|
|
2355
3617
|
exports.CredentialsValidateFailedError = CredentialsValidateFailedError;
|
|
2356
3618
|
exports.DATASOURCE_STRATEGY = DATASOURCE_STRATEGY;
|
|
3619
|
+
exports.DEFAULT_EXECUTION_CONFIG = DEFAULT_EXECUTION_CONFIG;
|
|
2357
3620
|
exports.DOCUMENT_SOURCE_STRATEGY = DOCUMENT_SOURCE_STRATEGY;
|
|
2358
3621
|
exports.DOCUMENT_TRANSFORMER_STRATEGY = DOCUMENT_TRANSFORMER_STRATEGY;
|
|
2359
3622
|
exports.DataSourceStrategy = DataSourceStrategy;
|
|
2360
3623
|
exports.DocumentSourceStrategy = DocumentSourceStrategy;
|
|
2361
3624
|
exports.DocumentTransformerStrategy = DocumentTransformerStrategy;
|
|
2362
3625
|
exports.GLOBAL_ORGANIZATION_SCOPE = GLOBAL_ORGANIZATION_SCOPE;
|
|
3626
|
+
exports.HANDOFF_PERMISSION_SERVICE_TOKEN = HANDOFF_PERMISSION_SERVICE_TOKEN;
|
|
3627
|
+
exports.HANDOFF_PROCESSOR_STRATEGY = HANDOFF_PROCESSOR_STRATEGY;
|
|
3628
|
+
exports.HANDOFF_QUEUE_SERVICE_TOKEN = HANDOFF_QUEUE_SERVICE_TOKEN;
|
|
3629
|
+
exports.HandoffProcessorStrategy = HandoffProcessorStrategy;
|
|
2363
3630
|
exports.IMAGE_UNDERSTANDING_STRATEGY = IMAGE_UNDERSTANDING_STRATEGY;
|
|
3631
|
+
exports.INTEGRATION_PERMISSION_SERVICE_TOKEN = INTEGRATION_PERMISSION_SERVICE_TOKEN;
|
|
2364
3632
|
exports.INTEGRATION_STRATEGY = INTEGRATION_STRATEGY;
|
|
2365
3633
|
exports.ImageUnderstandingStrategy = ImageUnderstandingStrategy;
|
|
2366
3634
|
exports.IntegrationStrategyKey = IntegrationStrategyKey;
|
|
@@ -2372,6 +3640,7 @@ exports.LLMUsage = LLMUsage;
|
|
|
2372
3640
|
exports.LargeLanguageModel = LargeLanguageModel;
|
|
2373
3641
|
exports.ORGANIZATION_METADATA_KEY = ORGANIZATION_METADATA_KEY;
|
|
2374
3642
|
exports.OpenAICompatibleReranker = OpenAICompatibleReranker;
|
|
3643
|
+
exports.PERMISSION_OPERATION_METADATA_KEY = PERMISSION_OPERATION_METADATA_KEY;
|
|
2375
3644
|
exports.PLUGIN_METADATA = PLUGIN_METADATA;
|
|
2376
3645
|
exports.PLUGIN_METADATA_KEY = PLUGIN_METADATA_KEY;
|
|
2377
3646
|
exports.PROVIDE_AI_MODEL_LLM = PROVIDE_AI_MODEL_LLM;
|
|
@@ -2382,9 +3651,12 @@ exports.PROVIDE_AI_MODEL_TEXT_EMBEDDING = PROVIDE_AI_MODEL_TEXT_EMBEDDING;
|
|
|
2382
3651
|
exports.PROVIDE_AI_MODEL_TTS = PROVIDE_AI_MODEL_TTS;
|
|
2383
3652
|
exports.RETRIEVER_STRATEGY = RETRIEVER_STRATEGY;
|
|
2384
3653
|
exports.RequestContext = RequestContext;
|
|
3654
|
+
exports.RequirePermissionOperation = RequirePermissionOperation;
|
|
2385
3655
|
exports.RerankModel = RerankModel;
|
|
2386
3656
|
exports.RetrieverStrategy = RetrieverStrategy;
|
|
3657
|
+
exports.SANDBOX_PROVIDER = SANDBOX_PROVIDER;
|
|
2387
3658
|
exports.STRATEGY_META_KEY = STRATEGY_META_KEY;
|
|
3659
|
+
exports.SandboxProviderStrategy = SandboxProviderStrategy;
|
|
2388
3660
|
exports.Speech2TextChatModel = Speech2TextChatModel;
|
|
2389
3661
|
exports.SpeechToTextModel = SpeechToTextModel;
|
|
2390
3662
|
exports.TEXT_SPLITTER_STRATEGY = TEXT_SPLITTER_STRATEGY;
|
|
@@ -2393,6 +3665,7 @@ exports.TextEmbeddingModelManager = TextEmbeddingModelManager;
|
|
|
2393
3665
|
exports.TextSplitterStrategy = TextSplitterStrategy;
|
|
2394
3666
|
exports.TextToSpeechModel = TextToSpeechModel;
|
|
2395
3667
|
exports.ToolsetStrategy = ToolsetStrategy;
|
|
3668
|
+
exports.USER_PERMISSION_SERVICE_TOKEN = USER_PERMISSION_SERVICE_TOKEN;
|
|
2396
3669
|
exports.VECTOR_STORE_STRATEGY = VECTOR_STORE_STRATEGY;
|
|
2397
3670
|
exports.VectorStoreStrategy = VectorStoreStrategy;
|
|
2398
3671
|
exports.WORKFLOW_NODE_STRATEGY = WORKFLOW_NODE_STRATEGY;
|
|
@@ -2404,17 +3677,26 @@ exports.XpFileSystem = XpFileSystem;
|
|
|
2404
3677
|
exports.XpertServerPlugin = XpertServerPlugin;
|
|
2405
3678
|
exports.als = als;
|
|
2406
3679
|
exports.calcTokenUsage = calcTokenUsage;
|
|
3680
|
+
exports.chunkText = chunkText;
|
|
2407
3681
|
exports.countTokensSafe = countTokensSafe;
|
|
2408
3682
|
exports.createI18nInstance = createI18nInstance;
|
|
2409
3683
|
exports.createPluginLogger = createPluginLogger;
|
|
3684
|
+
exports.defineAgentMessageType = defineAgentMessageType;
|
|
3685
|
+
exports.defineChannelMessageType = defineChannelMessageType;
|
|
2410
3686
|
exports.downloadRemoteFile = downloadRemoteFile;
|
|
2411
3687
|
exports.getErrorMessage = getErrorMessage;
|
|
3688
|
+
exports.getModelContextSize = getModelContextSize;
|
|
3689
|
+
exports.getPermissionOperationMetadata = getPermissionOperationMetadata;
|
|
2412
3690
|
exports.getPositionList = getPositionList;
|
|
2413
3691
|
exports.getPositionMap = getPositionMap;
|
|
2414
3692
|
exports.getRequestContext = getRequestContext;
|
|
3693
|
+
exports.getRequiredPermissionOperation = getRequiredPermissionOperation;
|
|
2415
3694
|
exports.isRemoteFile = isRemoteFile;
|
|
3695
|
+
exports.isSandboxBackend = isSandboxBackend;
|
|
3696
|
+
exports.isStructuredMessageType = isStructuredMessageType;
|
|
2416
3697
|
exports.loadYamlFile = loadYamlFile;
|
|
2417
3698
|
exports.mergeCredentials = mergeCredentials;
|
|
2418
3699
|
exports.mergeParentChildChunks = mergeParentChildChunks;
|
|
3700
|
+
exports.normalizeContextSize = normalizeContextSize;
|
|
2419
3701
|
exports.runWithRequestContext = runWithRequestContext;
|
|
2420
3702
|
exports.sumTokenUsage = sumTokenUsage;
|