@sowonai/crewx-cli 0.4.0-dev.9 → 0.4.0-rc.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.
Files changed (91) hide show
  1. package/README.md +34 -12
  2. package/dist/ai-provider.service.d.ts +7 -12
  3. package/dist/ai-provider.service.js +42 -20
  4. package/dist/ai-provider.service.js.map +1 -1
  5. package/dist/ai.service.d.ts +5 -45
  6. package/dist/ai.service.js +10 -587
  7. package/dist/ai.service.js.map +1 -1
  8. package/dist/app.module.js +67 -11
  9. package/dist/app.module.js.map +1 -1
  10. package/dist/cli/agent.handler.js +3 -4
  11. package/dist/cli/agent.handler.js.map +1 -1
  12. package/dist/cli/chat.handler.d.ts +2 -1
  13. package/dist/cli/chat.handler.js +17 -6
  14. package/dist/cli/chat.handler.js.map +1 -1
  15. package/dist/cli/cli.handler.js +4 -0
  16. package/dist/cli/cli.handler.js.map +1 -1
  17. package/dist/cli/doctor.handler.js +8 -40
  18. package/dist/cli/doctor.handler.js.map +1 -1
  19. package/dist/cli/execute.handler.js +8 -6
  20. package/dist/cli/execute.handler.js.map +1 -1
  21. package/dist/cli/log.handler.d.ts +2 -0
  22. package/dist/cli/log.handler.js +69 -0
  23. package/dist/cli/log.handler.js.map +1 -0
  24. package/dist/cli/query.handler.js +4 -2
  25. package/dist/cli/query.handler.js.map +1 -1
  26. package/dist/cli-options.d.ts +4 -2
  27. package/dist/cli-options.js +19 -12
  28. package/dist/cli-options.js.map +1 -1
  29. package/dist/crewx.tool.d.ts +18 -2
  30. package/dist/crewx.tool.js +542 -105
  31. package/dist/crewx.tool.js.map +1 -1
  32. package/dist/providers/dynamic-provider.factory.d.ts +9 -51
  33. package/dist/providers/dynamic-provider.factory.js +44 -506
  34. package/dist/providers/dynamic-provider.factory.js.map +1 -1
  35. package/dist/services/agent-loader.service.d.ts +6 -2
  36. package/dist/services/agent-loader.service.js +210 -26
  37. package/dist/services/agent-loader.service.js.map +1 -1
  38. package/dist/services/config.service.d.ts +7 -27
  39. package/dist/services/config.service.js +80 -38
  40. package/dist/services/config.service.js.map +1 -1
  41. package/dist/services/document-loader.service.d.ts +9 -4
  42. package/dist/services/document-loader.service.js +26 -7
  43. package/dist/services/document-loader.service.js.map +1 -1
  44. package/dist/services/help.service.js +6 -0
  45. package/dist/services/help.service.js.map +1 -1
  46. package/dist/services/parallel-processing.service.d.ts +2 -0
  47. package/dist/services/parallel-processing.service.js +40 -6
  48. package/dist/services/parallel-processing.service.js.map +1 -1
  49. package/dist/services/provider-bridge.service.d.ts +35 -0
  50. package/dist/services/provider-bridge.service.js +224 -0
  51. package/dist/services/provider-bridge.service.js.map +1 -0
  52. package/dist/services/remote-agent.service.d.ts +1 -1
  53. package/dist/services/task-management.service.d.ts +3 -3
  54. package/dist/services/task-management.service.js +2 -1
  55. package/dist/services/task-management.service.js.map +1 -1
  56. package/dist/services/template.service.d.ts +2 -0
  57. package/dist/services/template.service.js +46 -1
  58. package/dist/services/template.service.js.map +1 -1
  59. package/dist/utils/stdin-utils.d.ts +4 -25
  60. package/dist/utils/stdin-utils.js +2 -23
  61. package/dist/utils/stdin-utils.js.map +1 -1
  62. package/dist/utils/template-processor.d.ts +1 -29
  63. package/dist/utils/template-processor.js +38 -11
  64. package/dist/utils/template-processor.js.map +1 -1
  65. package/package.json +2 -3
  66. package/scripts/postbuild-cli.mjs +20 -1
  67. package/templates/agents/default.yaml +455 -0
  68. package/templates/agents/minimal.yaml +16 -0
  69. package/templates/documents/crewx-manual.md +390 -0
  70. package/templates/versions.json +19 -0
  71. package/dist/providers/base-ai.provider.d.ts +0 -1
  72. package/dist/providers/base-ai.provider.js +0 -6
  73. package/dist/providers/base-ai.provider.js.map +0 -1
  74. package/dist/providers/claude.provider.d.ts +0 -5
  75. package/dist/providers/claude.provider.js +0 -32
  76. package/dist/providers/claude.provider.js.map +0 -1
  77. package/dist/providers/codex.provider.d.ts +0 -4
  78. package/dist/providers/codex.provider.js +0 -30
  79. package/dist/providers/codex.provider.js.map +0 -1
  80. package/dist/providers/copilot.provider.d.ts +0 -5
  81. package/dist/providers/copilot.provider.js +0 -32
  82. package/dist/providers/copilot.provider.js.map +0 -1
  83. package/dist/providers/gemini.provider.d.ts +0 -5
  84. package/dist/providers/gemini.provider.js +0 -32
  85. package/dist/providers/gemini.provider.js.map +0 -1
  86. package/dist/services/context-enhancement.service.d.ts +0 -13
  87. package/dist/services/context-enhancement.service.js +0 -169
  88. package/dist/services/context-enhancement.service.js.map +0 -1
  89. package/dist/utils/mention-parser.d.ts +0 -18
  90. package/dist/utils/mention-parser.js +0 -136
  91. package/dist/utils/mention-parser.js.map +0 -1
@@ -1,54 +1,25 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
2
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
5
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
23
7
  };
24
- var __importStar = (this && this.__importStar) || (function () {
25
- var ownKeys = function(o) {
26
- ownKeys = Object.getOwnPropertyNames || function (o) {
27
- var ar = [];
28
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
- return ar;
30
- };
31
- return ownKeys(o);
32
- };
33
- return function (mod) {
34
- if (mod && mod.__esModule) return mod;
35
- var result = {};
36
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
- __setModuleDefault(result, mod);
38
- return result;
39
- };
40
- })();
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
41
11
  var DynamicProviderFactory_1;
42
12
  Object.defineProperty(exports, "__esModule", { value: true });
43
13
  exports.DynamicProviderFactory = void 0;
44
14
  const common_1 = require("@nestjs/common");
45
- const base_ai_provider_1 = require("./base-ai.provider");
46
15
  const crewx_sdk_1 = require("@sowonai/crewx-sdk");
47
- let DynamicProviderFactory = DynamicProviderFactory_1 = class DynamicProviderFactory {
16
+ const logger_adapter_1 = require("./logger.adapter");
17
+ let DynamicProviderFactory = DynamicProviderFactory_1 = class DynamicProviderFactory extends crewx_sdk_1.BaseDynamicProviderFactory {
48
18
  constructor() {
49
- this.logger = new common_1.Logger(DynamicProviderFactory_1.name);
50
- this.timeoutConfig = (0, crewx_sdk_1.getTimeoutConfig)();
51
- this.BLOCKED_CLI_COMMANDS = [
19
+ super({
20
+ logger: (0, logger_adapter_1.createLoggerAdapter)(DynamicProviderFactory_1.name),
21
+ });
22
+ this.blockedCommands = [
52
23
  'bash', 'sh', 'zsh', 'fish', 'ksh', 'tcsh', 'csh',
53
24
  'cmd', 'powershell', 'pwsh', 'command',
54
25
  'python', 'python3', 'node', 'ruby', 'perl', 'php',
@@ -58,529 +29,96 @@ let DynamicProviderFactory = DynamicProviderFactory_1 = class DynamicProviderFac
58
29
  'eval', 'exec', 'source',
59
30
  ];
60
31
  }
61
- createProvider(config) {
62
- this.logger.log(`Creating dynamic provider: ${config.id}`);
63
- if (config.type === 'plugin') {
64
- return this.createPluginProvider(config);
65
- }
66
- else if (config.type === 'remote') {
67
- return this.createRemoteProvider(config);
68
- }
69
- else {
70
- throw new Error(`Unknown provider type: ${config.type}`);
71
- }
72
- }
73
- createPluginProvider(config) {
74
- this.validateCliCommand(config.cli_command);
75
- this.validateCliArgs(config.query_args);
76
- this.validateCliArgs(config.execute_args);
77
- this.validateErrorPatterns(config.error_patterns);
78
- this.validateEnv(config.env);
79
- class DynamicAIProvider extends base_ai_provider_1.BaseAIProvider {
80
- constructor() {
81
- super(`DynamicProvider:${crewx_sdk_1.ProviderNamespace.PLUGIN}/${config.id}`);
82
- this.name = `${crewx_sdk_1.ProviderNamespace.PLUGIN}/${config.id}`;
83
- }
84
- getCliCommand() {
85
- return config.cli_command;
86
- }
87
- getDefaultArgs() {
88
- return config.query_args || [];
89
- }
90
- getExecuteArgs() {
91
- return config.execute_args || [];
92
- }
93
- getDefaultModel() {
94
- return config.default_model || 'default';
95
- }
96
- getPromptInArgs() {
97
- return config.prompt_in_args ?? false;
98
- }
99
- shouldPipeContext() {
100
- if (this.getPromptInArgs()) {
101
- return false;
102
- }
103
- return super.shouldPipeContext();
104
- }
105
- getNotInstalledMessage() {
106
- return (config.not_installed_message ||
107
- `${config.display_name || config.id} CLI is not installed.`);
108
- }
109
- getDefaultQueryTimeout() {
110
- return config.timeout?.query ?? 600000;
111
- }
112
- getDefaultExecuteTimeout() {
113
- return config.timeout?.execute ?? this.timeoutConfig.parallel;
114
- }
115
- getEnv() {
116
- return config.env || {};
117
- }
118
- async isAvailable() {
119
- const cliCommand = this.getCliCommand();
120
- if (cliCommand.includes('/') || cliCommand.includes('\\')) {
121
- try {
122
- const { access } = await Promise.resolve().then(() => __importStar(require('fs/promises')));
123
- const { constants } = await Promise.resolve().then(() => __importStar(require('fs')));
124
- const { resolve } = await Promise.resolve().then(() => __importStar(require('path')));
125
- const absolutePath = resolve(process.cwd(), cliCommand);
126
- await access(absolutePath, constants.X_OK);
127
- return true;
128
- }
129
- catch {
130
- return false;
131
- }
132
- }
133
- return super.isAvailable();
134
- }
135
- parseProviderError(stderr, stdout) {
136
- if (!config.error_patterns) {
137
- return super.parseProviderError(stderr, stdout);
138
- }
139
- const combinedOutput = stderr || stdout;
140
- for (const errorPattern of config.error_patterns) {
141
- if (combinedOutput.includes(errorPattern.pattern)) {
142
- return {
143
- error: true,
144
- message: errorPattern.message,
145
- };
146
- }
147
- }
148
- return super.parseProviderError(stderr, stdout);
149
- }
150
- }
151
- return new DynamicAIProvider();
152
- }
153
- createRemoteProvider(config) {
154
- this.logger.log(`Creating remote provider: ${config.id}`);
155
- this.validateRemoteConfig(config);
156
- class RemoteAIProvider extends base_ai_provider_1.BaseAIProvider {
157
- constructor() {
158
- super(`RemoteProvider:${crewx_sdk_1.ProviderNamespace.REMOTE}/${config.id}`);
159
- this.name = `${crewx_sdk_1.ProviderNamespace.REMOTE}/${config.id}`;
160
- }
161
- getCliCommand() {
162
- if (config.location.startsWith('file://')) {
163
- return 'crewx';
164
- }
165
- return 'curl';
166
- }
167
- getDefaultArgs() {
168
- if (config.location.startsWith('file://')) {
169
- const configPath = config.location.replace('file://', '');
170
- return [
171
- 'query',
172
- '--raw',
173
- `--config=${configPath}`
174
- ];
175
- }
176
- const args = [
177
- '-X', 'POST',
178
- config.location + '/mcp/query',
179
- '-H', 'Content-Type: application/json'
180
- ];
181
- const authHeader = this.getAuthHeader();
182
- if (authHeader) {
183
- args.push('-H', `Authorization: ${authHeader}`);
184
- }
185
- return args;
186
- }
187
- getExecuteArgs() {
188
- if (config.location.startsWith('file://')) {
189
- const configPath = config.location.replace('file://', '');
190
- return [
191
- 'execute',
192
- '--raw',
193
- `--config=${configPath}`
194
- ];
195
- }
196
- const args = [
197
- '-X', 'POST',
198
- config.location + '/mcp/execute',
199
- '-H', 'Content-Type: application/json'
200
- ];
201
- const authHeader = this.getAuthHeader();
202
- if (authHeader) {
203
- args.push('-H', `Authorization: ${authHeader}`);
204
- }
205
- return args;
206
- }
207
- getDefaultModel() {
208
- return config.default_model || 'default';
209
- }
210
- getPromptInArgs() {
211
- return true;
212
- }
213
- getNotInstalledMessage() {
214
- if (config.location.startsWith('file://')) {
215
- const configPath = config.location.replace('file://', '');
216
- return `Remote CrewX configuration not found: ${configPath}`;
217
- }
218
- return `Remote CrewX server not accessible: ${config.location}`;
219
- }
220
- getDefaultQueryTimeout() {
221
- return config.timeout?.query ?? 300000;
222
- }
223
- getDefaultExecuteTimeout() {
224
- return config.timeout?.execute ?? 600000;
225
- }
226
- getAuthHeader() {
227
- if (!config.auth || config.auth.type === 'none') {
228
- return '';
229
- }
230
- if (config.auth.type === 'bearer' && config.auth.token) {
231
- return `Bearer ${config.auth.token}`;
232
- }
233
- if (config.auth.type === 'api_key' && config.auth.token) {
234
- return `Api-Key ${config.auth.token}`;
235
- }
236
- return '';
237
- }
238
- async isAvailable() {
239
- if (config.location.startsWith('file://')) {
240
- try {
241
- const { access } = await Promise.resolve().then(() => __importStar(require('fs/promises')));
242
- const { constants } = await Promise.resolve().then(() => __importStar(require('fs')));
243
- const configPath = config.location.replace('file://', '');
244
- await access(configPath, constants.R_OK);
245
- const cliAvailable = await super.isAvailable();
246
- return cliAvailable;
247
- }
248
- catch {
249
- return false;
250
- }
251
- }
252
- try {
253
- const controller = new AbortController();
254
- const timeoutId = setTimeout(() => controller.abort(), 5000);
255
- const healthHeaders = {
256
- ...(config.headers || {}),
257
- ...this.getAuthHeaders(),
258
- };
259
- const response = await fetch(config.location + '/health', {
260
- method: 'GET',
261
- signal: controller.signal,
262
- headers: healthHeaders
263
- });
264
- clearTimeout(timeoutId);
265
- return response.ok;
266
- }
267
- catch {
268
- return false;
269
- }
270
- }
271
- async query(prompt, options) {
272
- if (config.location.startsWith('http')) {
273
- return this.httpQuery(prompt, options);
274
- }
275
- const validated = await this.ensureFileConfigExists(options);
276
- if (!validated.ok) {
277
- return validated.response;
278
- }
279
- const userQuery = this.parseUserQueryForRemote(prompt);
280
- const formattedPrompt = userQuery
281
- ? `@${config.external_agent_id} ${userQuery}`
282
- : `@${config.external_agent_id}`;
283
- const structuredPayload = this.normalizeStructuredPayload(prompt, options);
284
- const extendedOptions = {
285
- ...options,
286
- };
287
- if (structuredPayload) {
288
- extendedOptions.pipedContext = structuredPayload;
289
- }
290
- else {
291
- delete extendedOptions.pipedContext;
292
- }
293
- return super.query(formattedPrompt, extendedOptions);
294
- }
295
- async execute(prompt, options) {
296
- if (config.location.startsWith('http')) {
297
- return super.execute(prompt, options);
298
- }
299
- const validated = await this.ensureFileConfigExists(options);
300
- if (!validated.ok) {
301
- return validated.response;
302
- }
303
- const userQuery = this.parseUserQueryForRemote(prompt);
304
- const formattedPrompt = userQuery
305
- ? `@${config.external_agent_id} ${userQuery}`
306
- : `@${config.external_agent_id}`;
307
- const structuredPayload = this.normalizeStructuredPayload(prompt, options);
308
- const extendedOptions = {
309
- ...options,
310
- };
311
- if (structuredPayload) {
312
- extendedOptions.pipedContext = structuredPayload;
313
- }
314
- else {
315
- delete extendedOptions.pipedContext;
316
- }
317
- return super.execute(formattedPrompt, extendedOptions);
318
- }
319
- async httpQuery(prompt, options) {
320
- const url = options?.taskId
321
- ? `${config.location}/mcp/query`
322
- : `${config.location}/mcp/query`;
323
- const structuredPayload = this.normalizeStructuredPayload(prompt, options);
324
- const requestBody = {
325
- prompt,
326
- agent_id: config.external_agent_id,
327
- task_id: options?.taskId,
328
- model: options?.model || this.getDefaultModel(),
329
- working_directory: options?.workingDirectory,
330
- };
331
- if (structuredPayload) {
332
- requestBody.structured_payload = structuredPayload;
333
- }
334
- if (options?.messages && options.messages.length > 0) {
335
- requestBody.messages = options.messages;
336
- }
337
- if (options?.pipedContext) {
338
- const trimmed = options.pipedContext.trim();
339
- if (!this.isStructuredPayload(trimmed)) {
340
- requestBody.context = trimmed;
341
- }
342
- }
343
- try {
344
- const controller = new AbortController();
345
- const timeoutMs = options?.timeout || this.getDefaultQueryTimeout();
346
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
347
- const response = await fetch(url, {
348
- method: 'POST',
349
- headers: {
350
- 'Content-Type': 'application/json',
351
- ...this.getAuthHeaders(),
352
- ...(config.headers || {})
353
- },
354
- body: JSON.stringify(requestBody),
355
- signal: controller.signal
356
- });
357
- clearTimeout(timeoutId);
358
- if (!response.ok) {
359
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
360
- }
361
- const result = await response.json();
362
- return {
363
- content: result.content,
364
- provider: this.name,
365
- command: `remote query to ${config.location}`,
366
- success: result.success !== false,
367
- error: result.error,
368
- taskId: result.task_id,
369
- toolCall: result.tool_call
370
- };
371
- }
372
- catch (error) {
373
- return {
374
- content: '',
375
- provider: this.name,
376
- command: `remote query to ${config.location}`,
377
- success: false,
378
- error: error.message,
379
- taskId: options?.taskId
380
- };
381
- }
382
- }
383
- parseUserQueryForRemote(prompt) {
384
- if (!prompt) {
385
- return '';
386
- }
387
- const match = prompt.match(/<user_query key="[^"]+">\s*([\s\S]*?)\s*<\/user_query>/i);
388
- if (match && match[1]) {
389
- return match[1].trim();
390
- }
391
- return prompt.trim();
392
- }
393
- normalizeStructuredPayload(prompt, options) {
394
- const existing = options?.pipedContext?.trim();
395
- if (existing) {
396
- if (this.isStructuredPayload(existing)) {
397
- return existing;
398
- }
399
- return this.createStructuredPayload(prompt, existing, options);
400
- }
401
- if (options?.messages && options.messages.length > 0) {
402
- return this.createStructuredPayload(prompt, null, options);
403
- }
404
- return null;
405
- }
406
- async ensureFileConfigExists(options) {
407
- if (!config.location.startsWith('file://')) {
408
- return { ok: true };
409
- }
410
- const configPath = config.location.replace('file://', '');
411
- try {
412
- const { access } = await Promise.resolve().then(() => __importStar(require('fs/promises')));
413
- const { constants } = await Promise.resolve().then(() => __importStar(require('fs')));
414
- await access(configPath, constants.R_OK);
415
- return { ok: true };
416
- }
417
- catch {
418
- return {
419
- ok: false,
420
- response: {
421
- content: `Remote CrewX configuration not found: ${configPath}`,
422
- provider: this.name,
423
- command: `crewx --config=${configPath}`,
424
- success: false,
425
- error: `Remote CrewX configuration not found: ${configPath}`,
426
- taskId: options?.taskId,
427
- },
428
- };
429
- }
430
- }
431
- getAuthHeaders() {
432
- const headers = {};
433
- if (!config.auth || config.auth.type === 'none') {
434
- return headers;
435
- }
436
- if (config.auth.token) {
437
- if (config.auth.type === 'bearer') {
438
- headers['Authorization'] = `Bearer ${config.auth.token}`;
439
- }
440
- else if (config.auth.type === 'api_key') {
441
- headers['Api-Key'] = config.auth.token;
442
- }
443
- }
444
- return headers;
445
- }
446
- }
447
- return new RemoteAIProvider();
448
- }
449
32
  validateCliCommand(cliCommand) {
33
+ super.validateCliCommand(cliCommand);
450
34
  const normalized = cliCommand.toLowerCase().trim();
451
35
  const commandName = normalized.split('/').pop() || normalized;
452
- if (this.BLOCKED_CLI_COMMANDS.includes(commandName)) {
36
+ if (this.blockedCommands.includes(commandName)) {
453
37
  throw new Error(`Security: CLI command '${cliCommand}' is blocked for security reasons. ` +
454
38
  `This command is considered dangerous and cannot be used as a plugin provider.`);
455
39
  }
456
40
  if (normalized.startsWith('/') || normalized.startsWith('\\')) {
457
- throw new Error(`Security: Absolute paths are not allowed. ` +
458
- `Use relative paths from project root (e.g., 'test-tools/howling') or command names in PATH (e.g., 'aider').`);
41
+ throw new Error('Security: Absolute paths are not allowed. ' +
42
+ "Use relative paths from project root (e.g., 'test-tools/howling') or command names in PATH (e.g., 'aider').");
459
43
  }
460
44
  if (normalized.includes('..')) {
461
- throw new Error(`Security: Path traversal (..) is not allowed. ` +
462
- `Use relative paths within the project directory only.`);
45
+ throw new Error('Security: Path traversal (..) is not allowed. ' +
46
+ 'Use relative paths within the project directory only.');
463
47
  }
464
48
  const shellMetaChars = /[;&|<>`$(){}[\]!]/;
465
49
  if (shellMetaChars.test(cliCommand)) {
466
50
  throw new Error(`Security: CLI command '${cliCommand}' contains shell metacharacters. ` +
467
- `Only alphanumeric characters, hyphens, underscores, dots, and forward slashes are allowed.`);
51
+ 'Only alphanumeric characters, hyphens, underscores, dots, and forward slashes are allowed.');
468
52
  }
469
53
  if (cliCommand.includes('\0')) {
470
- throw new Error(`Security: CLI command contains null bytes (potential path injection).`);
54
+ throw new Error('Security: CLI command contains null bytes (potential path injection).');
471
55
  }
472
56
  }
473
57
  validateCliArgs(args) {
58
+ super.validateCliArgs(args);
474
59
  for (const arg of args) {
475
60
  const dangerousChars = /[;&|<>`$()!]/;
476
61
  if (dangerousChars.test(arg)) {
477
62
  throw new Error(`Security: CLI argument '${arg}' contains dangerous shell metacharacters. ` +
478
- `Arguments with ;, &, |, <, >, \`, $, (), ! are not allowed.`);
63
+ 'Arguments with ;, &, |, <, >, `, $, (), ! are not allowed.');
479
64
  }
480
65
  if (arg.includes('$(') || arg.includes('`')) {
481
66
  throw new Error(`Security: CLI argument '${arg}' contains command substitution pattern. ` +
482
- `$() and backticks are not allowed.`);
67
+ '$() and backticks are not allowed.');
483
68
  }
484
69
  if (arg.includes('\0')) {
485
- throw new Error(`Security: CLI argument contains null bytes (potential injection).`);
70
+ throw new Error('Security: CLI argument contains null bytes (potential injection).');
486
71
  }
487
72
  }
488
73
  }
489
74
  validateErrorPatterns(patterns) {
490
- if (!patterns)
75
+ if (!patterns) {
491
76
  return;
77
+ }
78
+ const redosPatterns = [
79
+ /\(.*\+.*\)\+/,
80
+ /\(.*\*.*\)\*/,
81
+ /\(.*\+.*\)\*/,
82
+ /\(.*\*.*\)\+/,
83
+ ];
492
84
  for (const { pattern } of patterns) {
493
- const redosPatterns = [
494
- /\(.*\+.*\)\+/,
495
- /\(.*\*.*\)\*/,
496
- /\(.*\+.*\)\*/,
497
- /\(.*\*.*\)\+/,
498
- ];
499
85
  for (const redosPattern of redosPatterns) {
500
86
  if (redosPattern.test(pattern)) {
501
87
  throw new Error(`Security: Error pattern '${pattern}' may cause ReDoS (catastrophic backtracking). ` +
502
- `Avoid nested quantifiers like (a+)+, (a*)*, etc.`);
88
+ 'Avoid nested quantifiers like (a+)+, (a*)*, etc.');
503
89
  }
504
90
  }
505
- try {
506
- new RegExp(pattern);
507
- }
508
- catch (error) {
509
- throw new Error(`Invalid regex pattern '${pattern}': ${error.message}`);
510
- }
511
91
  }
92
+ super.validateErrorPatterns(patterns);
512
93
  }
513
94
  validateEnv(env) {
514
- if (!env)
95
+ super.validateEnv(env);
96
+ if (!env) {
515
97
  return;
98
+ }
99
+ const dangerousEnvVars = [
100
+ 'PATH', 'LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH',
101
+ 'LD_PRELOAD', 'DYLD_INSERT_LIBRARIES',
102
+ 'IFS', 'BASH_ENV', 'ENV',
103
+ ];
516
104
  for (const [key, value] of Object.entries(env)) {
517
- const dangerousEnvVars = [
518
- 'PATH', 'LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH',
519
- 'LD_PRELOAD', 'DYLD_INSERT_LIBRARIES',
520
- 'IFS', 'BASH_ENV', 'ENV',
521
- ];
522
105
  if (dangerousEnvVars.includes(key.toUpperCase())) {
523
106
  throw new Error(`Security: Environment variable '${key}' is blocked for security reasons. ` +
524
- `Modifying system paths or library loading variables is not allowed.`);
107
+ 'Modifying system paths or library loading variables is not allowed.');
525
108
  }
526
109
  if (key.includes('\0') || value.includes('\0')) {
527
- throw new Error(`Security: Environment variable contains null bytes (potential injection).`);
110
+ throw new Error('Security: Environment variable contains null bytes (potential injection).');
528
111
  }
529
112
  const dangerousChars = /[;&|<>`$()]/;
530
113
  if (dangerousChars.test(value)) {
531
- this.logger.warn(`Warning: Environment variable '${key}' contains shell metacharacters. ` +
532
- `Value: ${value}`);
533
- }
534
- }
535
- }
536
- validateRemoteConfig(config) {
537
- if (!config.location) {
538
- throw new Error('Remote provider requires a location (file:// or http:// URL)');
539
- }
540
- if (!config.external_agent_id) {
541
- throw new Error('Remote provider requires an external_agent_id');
542
- }
543
- if (!config.location.startsWith('file://') && !config.location.startsWith('http://') && !config.location.startsWith('https://')) {
544
- throw new Error('Remote location must start with file://, http://, or https://');
545
- }
546
- if (config.location.startsWith('file://')) {
547
- const filePath = config.location.replace('file://', '');
548
- if (filePath.includes('..')) {
549
- throw new Error('Path traversal (..) is not allowed in remote file locations');
550
- }
551
- }
552
- if (config.auth) {
553
- const validAuthTypes = ['bearer', 'api_key', 'none'];
554
- if (!validAuthTypes.includes(config.auth.type)) {
555
- throw new Error(`Invalid auth type: ${config.auth.type}. Must be one of: ${validAuthTypes.join(', ')}`);
556
- }
557
- if (config.auth.type !== 'none' && !config.auth.token) {
558
- throw new Error(`Auth type '${config.auth.type}' requires a token`);
114
+ this.logger.warn(`Warning: Environment variable '${key}' contains shell metacharacters. Value: ${value}`);
559
115
  }
560
116
  }
561
117
  }
562
- validateConfig(config) {
563
- if (!config.id || typeof config.id !== 'string') {
564
- return false;
565
- }
566
- if (config.type === 'plugin') {
567
- return (config.cli_command &&
568
- typeof config.cli_command === 'string' &&
569
- Array.isArray(config.query_args) &&
570
- Array.isArray(config.execute_args) &&
571
- typeof config.prompt_in_args === 'boolean');
572
- }
573
- if (config.type === 'remote') {
574
- return (config.location &&
575
- typeof config.location === 'string' &&
576
- config.external_agent_id &&
577
- typeof config.external_agent_id === 'string');
578
- }
579
- return false;
580
- }
581
118
  };
582
119
  exports.DynamicProviderFactory = DynamicProviderFactory;
583
120
  exports.DynamicProviderFactory = DynamicProviderFactory = DynamicProviderFactory_1 = __decorate([
584
- (0, common_1.Injectable)()
121
+ (0, common_1.Injectable)(),
122
+ __metadata("design:paramtypes", [])
585
123
  ], DynamicProviderFactory);
586
124
  //# sourceMappingURL=dynamic-provider.factory.js.map