@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.
- package/README.md +34 -12
- package/dist/ai-provider.service.d.ts +7 -12
- package/dist/ai-provider.service.js +42 -20
- package/dist/ai-provider.service.js.map +1 -1
- package/dist/ai.service.d.ts +5 -45
- package/dist/ai.service.js +10 -587
- package/dist/ai.service.js.map +1 -1
- package/dist/app.module.js +67 -11
- package/dist/app.module.js.map +1 -1
- package/dist/cli/agent.handler.js +3 -4
- package/dist/cli/agent.handler.js.map +1 -1
- package/dist/cli/chat.handler.d.ts +2 -1
- package/dist/cli/chat.handler.js +17 -6
- package/dist/cli/chat.handler.js.map +1 -1
- package/dist/cli/cli.handler.js +4 -0
- package/dist/cli/cli.handler.js.map +1 -1
- package/dist/cli/doctor.handler.js +8 -40
- package/dist/cli/doctor.handler.js.map +1 -1
- package/dist/cli/execute.handler.js +8 -6
- package/dist/cli/execute.handler.js.map +1 -1
- package/dist/cli/log.handler.d.ts +2 -0
- package/dist/cli/log.handler.js +69 -0
- package/dist/cli/log.handler.js.map +1 -0
- package/dist/cli/query.handler.js +4 -2
- package/dist/cli/query.handler.js.map +1 -1
- package/dist/cli-options.d.ts +4 -2
- package/dist/cli-options.js +19 -12
- package/dist/cli-options.js.map +1 -1
- package/dist/crewx.tool.d.ts +18 -2
- package/dist/crewx.tool.js +542 -105
- package/dist/crewx.tool.js.map +1 -1
- package/dist/providers/dynamic-provider.factory.d.ts +9 -51
- package/dist/providers/dynamic-provider.factory.js +44 -506
- package/dist/providers/dynamic-provider.factory.js.map +1 -1
- package/dist/services/agent-loader.service.d.ts +6 -2
- package/dist/services/agent-loader.service.js +210 -26
- package/dist/services/agent-loader.service.js.map +1 -1
- package/dist/services/config.service.d.ts +7 -27
- package/dist/services/config.service.js +80 -38
- package/dist/services/config.service.js.map +1 -1
- package/dist/services/document-loader.service.d.ts +9 -4
- package/dist/services/document-loader.service.js +26 -7
- package/dist/services/document-loader.service.js.map +1 -1
- package/dist/services/help.service.js +6 -0
- package/dist/services/help.service.js.map +1 -1
- package/dist/services/parallel-processing.service.d.ts +2 -0
- package/dist/services/parallel-processing.service.js +40 -6
- package/dist/services/parallel-processing.service.js.map +1 -1
- package/dist/services/provider-bridge.service.d.ts +35 -0
- package/dist/services/provider-bridge.service.js +224 -0
- package/dist/services/provider-bridge.service.js.map +1 -0
- package/dist/services/remote-agent.service.d.ts +1 -1
- package/dist/services/task-management.service.d.ts +3 -3
- package/dist/services/task-management.service.js +2 -1
- package/dist/services/task-management.service.js.map +1 -1
- package/dist/services/template.service.d.ts +2 -0
- package/dist/services/template.service.js +46 -1
- package/dist/services/template.service.js.map +1 -1
- package/dist/utils/stdin-utils.d.ts +4 -25
- package/dist/utils/stdin-utils.js +2 -23
- package/dist/utils/stdin-utils.js.map +1 -1
- package/dist/utils/template-processor.d.ts +1 -29
- package/dist/utils/template-processor.js +38 -11
- package/dist/utils/template-processor.js.map +1 -1
- package/package.json +2 -3
- package/scripts/postbuild-cli.mjs +20 -1
- package/templates/agents/default.yaml +455 -0
- package/templates/agents/minimal.yaml +16 -0
- package/templates/documents/crewx-manual.md +390 -0
- package/templates/versions.json +19 -0
- package/dist/providers/base-ai.provider.d.ts +0 -1
- package/dist/providers/base-ai.provider.js +0 -6
- package/dist/providers/base-ai.provider.js.map +0 -1
- package/dist/providers/claude.provider.d.ts +0 -5
- package/dist/providers/claude.provider.js +0 -32
- package/dist/providers/claude.provider.js.map +0 -1
- package/dist/providers/codex.provider.d.ts +0 -4
- package/dist/providers/codex.provider.js +0 -30
- package/dist/providers/codex.provider.js.map +0 -1
- package/dist/providers/copilot.provider.d.ts +0 -5
- package/dist/providers/copilot.provider.js +0 -32
- package/dist/providers/copilot.provider.js.map +0 -1
- package/dist/providers/gemini.provider.d.ts +0 -5
- package/dist/providers/gemini.provider.js +0 -32
- package/dist/providers/gemini.provider.js.map +0 -1
- package/dist/services/context-enhancement.service.d.ts +0 -13
- package/dist/services/context-enhancement.service.js +0 -169
- package/dist/services/context-enhancement.service.js.map +0 -1
- package/dist/utils/mention-parser.d.ts +0 -18
- package/dist/utils/mention-parser.js +0 -136
- 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
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
16
|
+
const logger_adapter_1 = require("./logger.adapter");
|
|
17
|
+
let DynamicProviderFactory = DynamicProviderFactory_1 = class DynamicProviderFactory extends crewx_sdk_1.BaseDynamicProviderFactory {
|
|
48
18
|
constructor() {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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.
|
|
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(
|
|
458
|
-
|
|
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(
|
|
462
|
-
|
|
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
|
-
|
|
51
|
+
'Only alphanumeric characters, hyphens, underscores, dots, and forward slashes are allowed.');
|
|
468
52
|
}
|
|
469
53
|
if (cliCommand.includes('\0')) {
|
|
470
|
-
throw new Error(
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
'$() and backticks are not allowed.');
|
|
483
68
|
}
|
|
484
69
|
if (arg.includes('\0')) {
|
|
485
|
-
throw new Error(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|