praisonai 1.7.0 → 1.7.2

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.
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.CODE_MODE_METADATA = void 0;
10
10
  exports.codeMode = codeMode;
11
11
  exports.createCodeModeTool = createCodeModeTool;
12
+ const node_vm_1 = require("node:vm");
12
13
  exports.CODE_MODE_METADATA = {
13
14
  id: 'code-mode',
14
15
  displayName: 'Code Mode',
@@ -69,6 +70,10 @@ function codeMode(config) {
69
70
  /import\s+.*from\s+['"]child_process['"]/,
70
71
  /process\.exit/,
71
72
  /eval\s*\(/,
73
+ /Function\s*\(/,
74
+ /\.constructor/,
75
+ /globalThis/,
76
+ /\bprocess\b/,
72
77
  ];
73
78
  if (!settings.allowNetwork) {
74
79
  blockedPatterns.push(/require\s*\(\s*['"]http['"]\s*\)/, /require\s*\(\s*['"]https['"]\s*\)/, /require\s*\(\s*['"]net['"]\s*\)/, /fetch\s*\(/);
@@ -101,37 +106,32 @@ function codeMode(config) {
101
106
  }
102
107
  }
103
108
  try {
104
- // Create a sandboxed execution context
105
- // In a real implementation, this would use a proper sandbox like vm2 or isolated-vm
106
- // For now, we provide a safe execution wrapper
107
- const sandbox = {
109
+ const stdout = [];
110
+ const stderr = [];
111
+ const sandboxContext = {
108
112
  console: {
109
113
  log: (...args) => stdout.push(args.map(String).join(' ')),
110
114
  error: (...args) => stderr.push(args.map(String).join(' ')),
111
115
  warn: (...args) => stderr.push(args.map(String).join(' ')),
112
116
  },
113
- setTimeout: undefined,
114
- setInterval: undefined,
115
- setImmediate: undefined,
116
- process: undefined,
117
- require: undefined,
118
117
  __dirname: '/sandbox',
119
118
  __filename: '/sandbox/index.js',
120
119
  env: env || {},
121
120
  files: files || {},
122
121
  };
123
- const stdout = [];
124
- const stderr = [];
125
- // Execute with timeout
126
122
  const timeoutPromise = new Promise((_, reject) => {
127
123
  setTimeout(() => reject(new Error('Execution timeout')), settings.timeoutMs);
128
124
  });
129
125
  const executePromise = new Promise((resolve, reject) => {
130
126
  try {
131
- // Create a function from the code
132
- const fn = new Function('sandbox', `with (sandbox) { ${code} }`);
133
- const result = fn(sandbox);
134
- resolve(String(result ?? ''));
127
+ const wrappedCode = `(async function() { ${code} })()`;
128
+ const result = (0, node_vm_1.runInNewContext)(wrappedCode, sandboxContext, {
129
+ timeout: settings.timeoutMs,
130
+ displayErrors: true,
131
+ });
132
+ Promise.resolve(result)
133
+ .then((value) => resolve(String(value ?? '')))
134
+ .catch(reject);
135
135
  }
136
136
  catch (error) {
137
137
  reject(error);
@@ -1,8 +1,11 @@
1
1
  export { BaseTool, ToolResult, ToolValidationError, validateTool, createTool, type ToolParameters } from './base';
2
- export * from './decorator';
2
+ export { tool, FunctionTool, ToolRegistry, getRegistry, registerTool, getTool, type ToolConfig, type ToolContext, } from './decorator';
3
3
  export * from './arxivTools';
4
4
  export * from './mcpSse';
5
- export * from './registry';
5
+ export { ToolsRegistry, getToolsRegistry, createToolsRegistry, resetToolsRegistry, get_registry, get_tool, register_tool, validate_tool, } from './registry';
6
+ export type { ToolExecutionContext, ToolLimits, RedactionHooks, ToolLogger, ToolCapabilities, InstallHints, ToolMetadata, ToolExecutionResult, PraisonTool, ToolParameterSchema, ToolParameterProperty, ToolMiddleware, ToolHooks, ToolFactory, RegisteredTool, ToolInstallStatus, } from './registry';
7
+ export { MissingDependencyError, MissingEnvVarError, BudgetExceededError } from './registry';
8
+ export { createLoggingMiddleware, createTimeoutMiddleware, createRedactionMiddleware, createRateLimitMiddleware, createRetryMiddleware, createTracingMiddleware, createValidationMiddleware, composeMiddleware, } from './registry';
6
9
  export * from './builtins';
7
10
  export { tools, registerBuiltinTools } from './tools';
8
11
  export type { default as ToolsFacade } from './tools';
@@ -14,20 +14,47 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.createDelegator = exports.createSubagentTools = exports.createSubagentTool = exports.SubagentTool = exports.registerBuiltinTools = exports.tools = exports.createTool = exports.validateTool = exports.ToolValidationError = exports.BaseTool = void 0;
17
+ exports.createDelegator = exports.createSubagentTools = exports.createSubagentTool = exports.SubagentTool = exports.registerBuiltinTools = exports.tools = exports.composeMiddleware = exports.createValidationMiddleware = exports.createTracingMiddleware = exports.createRetryMiddleware = exports.createRateLimitMiddleware = exports.createRedactionMiddleware = exports.createTimeoutMiddleware = exports.createLoggingMiddleware = exports.BudgetExceededError = exports.MissingEnvVarError = exports.MissingDependencyError = exports.validate_tool = exports.register_tool = exports.get_tool = exports.get_registry = exports.resetToolsRegistry = exports.createToolsRegistry = exports.getToolsRegistry = exports.ToolsRegistry = exports.getTool = exports.registerTool = exports.getRegistry = exports.ToolRegistry = exports.FunctionTool = exports.tool = exports.createTool = exports.validateTool = exports.ToolValidationError = exports.BaseTool = void 0;
18
18
  // Export base tool interfaces and classes
19
19
  var base_1 = require("./base");
20
20
  Object.defineProperty(exports, "BaseTool", { enumerable: true, get: function () { return base_1.BaseTool; } });
21
21
  Object.defineProperty(exports, "ToolValidationError", { enumerable: true, get: function () { return base_1.ToolValidationError; } });
22
22
  Object.defineProperty(exports, "validateTool", { enumerable: true, get: function () { return base_1.validateTool; } });
23
23
  Object.defineProperty(exports, "createTool", { enumerable: true, get: function () { return base_1.createTool; } });
24
- // Export decorator and registry (legacy)
25
- __exportStar(require("./decorator"), exports);
24
+ // Legacy @tool decorator registry (camelCase names reserved for this API)
25
+ var decorator_1 = require("./decorator");
26
+ Object.defineProperty(exports, "tool", { enumerable: true, get: function () { return decorator_1.tool; } });
27
+ Object.defineProperty(exports, "FunctionTool", { enumerable: true, get: function () { return decorator_1.FunctionTool; } });
28
+ Object.defineProperty(exports, "ToolRegistry", { enumerable: true, get: function () { return decorator_1.ToolRegistry; } });
29
+ Object.defineProperty(exports, "getRegistry", { enumerable: true, get: function () { return decorator_1.getRegistry; } });
30
+ Object.defineProperty(exports, "registerTool", { enumerable: true, get: function () { return decorator_1.registerTool; } });
31
+ Object.defineProperty(exports, "getTool", { enumerable: true, get: function () { return decorator_1.getTool; } });
26
32
  // Export all tool modules
27
33
  __exportStar(require("./arxivTools"), exports);
28
34
  __exportStar(require("./mcpSse"), exports);
29
- // Export new registry system
30
- __exportStar(require("./registry"), exports);
35
+ // New AI SDK tools registry — snake_case parity names (avoid camelCase clash with decorator)
36
+ var registry_1 = require("./registry");
37
+ Object.defineProperty(exports, "ToolsRegistry", { enumerable: true, get: function () { return registry_1.ToolsRegistry; } });
38
+ Object.defineProperty(exports, "getToolsRegistry", { enumerable: true, get: function () { return registry_1.getToolsRegistry; } });
39
+ Object.defineProperty(exports, "createToolsRegistry", { enumerable: true, get: function () { return registry_1.createToolsRegistry; } });
40
+ Object.defineProperty(exports, "resetToolsRegistry", { enumerable: true, get: function () { return registry_1.resetToolsRegistry; } });
41
+ Object.defineProperty(exports, "get_registry", { enumerable: true, get: function () { return registry_1.get_registry; } });
42
+ Object.defineProperty(exports, "get_tool", { enumerable: true, get: function () { return registry_1.get_tool; } });
43
+ Object.defineProperty(exports, "register_tool", { enumerable: true, get: function () { return registry_1.register_tool; } });
44
+ Object.defineProperty(exports, "validate_tool", { enumerable: true, get: function () { return registry_1.validate_tool; } });
45
+ var registry_2 = require("./registry");
46
+ Object.defineProperty(exports, "MissingDependencyError", { enumerable: true, get: function () { return registry_2.MissingDependencyError; } });
47
+ Object.defineProperty(exports, "MissingEnvVarError", { enumerable: true, get: function () { return registry_2.MissingEnvVarError; } });
48
+ Object.defineProperty(exports, "BudgetExceededError", { enumerable: true, get: function () { return registry_2.BudgetExceededError; } });
49
+ var registry_3 = require("./registry");
50
+ Object.defineProperty(exports, "createLoggingMiddleware", { enumerable: true, get: function () { return registry_3.createLoggingMiddleware; } });
51
+ Object.defineProperty(exports, "createTimeoutMiddleware", { enumerable: true, get: function () { return registry_3.createTimeoutMiddleware; } });
52
+ Object.defineProperty(exports, "createRedactionMiddleware", { enumerable: true, get: function () { return registry_3.createRedactionMiddleware; } });
53
+ Object.defineProperty(exports, "createRateLimitMiddleware", { enumerable: true, get: function () { return registry_3.createRateLimitMiddleware; } });
54
+ Object.defineProperty(exports, "createRetryMiddleware", { enumerable: true, get: function () { return registry_3.createRetryMiddleware; } });
55
+ Object.defineProperty(exports, "createTracingMiddleware", { enumerable: true, get: function () { return registry_3.createTracingMiddleware; } });
56
+ Object.defineProperty(exports, "createValidationMiddleware", { enumerable: true, get: function () { return registry_3.createValidationMiddleware; } });
57
+ Object.defineProperty(exports, "composeMiddleware", { enumerable: true, get: function () { return registry_3.composeMiddleware; } });
31
58
  // Export built-in tools
32
59
  __exportStar(require("./builtins"), exports);
33
60
  // Export tools facade
@@ -4,6 +4,6 @@
4
4
  * Lazy-loaded exports for the tools registry system.
5
5
  */
6
6
  export type { ToolExecutionContext, ToolLimits, RedactionHooks, ToolLogger, ToolCapabilities, InstallHints, ToolMetadata, ToolExecutionResult, PraisonTool, ToolParameterSchema, ToolParameterProperty, ToolMiddleware, ToolHooks, ToolFactory, RegisteredTool, ToolInstallStatus, } from './types';
7
- export { MissingDependencyError, MissingEnvVarError } from './types';
8
- export { ToolsRegistry, getToolsRegistry, createToolsRegistry, resetToolsRegistry, } from './registry';
7
+ export { MissingDependencyError, MissingEnvVarError, BudgetExceededError } from './types';
8
+ export { ToolsRegistry, getToolsRegistry, createToolsRegistry, resetToolsRegistry, get_registry, getRegistry, get_tool, getTool, register_tool, registerTool, validate_tool, validateTool, } from './registry';
9
9
  export { createLoggingMiddleware, createTimeoutMiddleware, createRedactionMiddleware, createRateLimitMiddleware, createRetryMiddleware, createTracingMiddleware, createValidationMiddleware, composeMiddleware, } from './middleware';
@@ -5,17 +5,26 @@
5
5
  * Lazy-loaded exports for the tools registry system.
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.composeMiddleware = exports.createValidationMiddleware = exports.createTracingMiddleware = exports.createRetryMiddleware = exports.createRateLimitMiddleware = exports.createRedactionMiddleware = exports.createTimeoutMiddleware = exports.createLoggingMiddleware = exports.resetToolsRegistry = exports.createToolsRegistry = exports.getToolsRegistry = exports.ToolsRegistry = exports.MissingEnvVarError = exports.MissingDependencyError = void 0;
8
+ exports.composeMiddleware = exports.createValidationMiddleware = exports.createTracingMiddleware = exports.createRetryMiddleware = exports.createRateLimitMiddleware = exports.createRedactionMiddleware = exports.createTimeoutMiddleware = exports.createLoggingMiddleware = exports.validateTool = exports.validate_tool = exports.registerTool = exports.register_tool = exports.getTool = exports.get_tool = exports.getRegistry = exports.get_registry = exports.resetToolsRegistry = exports.createToolsRegistry = exports.getToolsRegistry = exports.ToolsRegistry = exports.BudgetExceededError = exports.MissingEnvVarError = exports.MissingDependencyError = void 0;
9
9
  // Errors
10
10
  var types_1 = require("./types");
11
11
  Object.defineProperty(exports, "MissingDependencyError", { enumerable: true, get: function () { return types_1.MissingDependencyError; } });
12
12
  Object.defineProperty(exports, "MissingEnvVarError", { enumerable: true, get: function () { return types_1.MissingEnvVarError; } });
13
+ Object.defineProperty(exports, "BudgetExceededError", { enumerable: true, get: function () { return types_1.BudgetExceededError; } });
13
14
  // Registry
14
15
  var registry_1 = require("./registry");
15
16
  Object.defineProperty(exports, "ToolsRegistry", { enumerable: true, get: function () { return registry_1.ToolsRegistry; } });
16
17
  Object.defineProperty(exports, "getToolsRegistry", { enumerable: true, get: function () { return registry_1.getToolsRegistry; } });
17
18
  Object.defineProperty(exports, "createToolsRegistry", { enumerable: true, get: function () { return registry_1.createToolsRegistry; } });
18
19
  Object.defineProperty(exports, "resetToolsRegistry", { enumerable: true, get: function () { return registry_1.resetToolsRegistry; } });
20
+ Object.defineProperty(exports, "get_registry", { enumerable: true, get: function () { return registry_1.get_registry; } });
21
+ Object.defineProperty(exports, "getRegistry", { enumerable: true, get: function () { return registry_1.getRegistry; } });
22
+ Object.defineProperty(exports, "get_tool", { enumerable: true, get: function () { return registry_1.get_tool; } });
23
+ Object.defineProperty(exports, "getTool", { enumerable: true, get: function () { return registry_1.getTool; } });
24
+ Object.defineProperty(exports, "register_tool", { enumerable: true, get: function () { return registry_1.register_tool; } });
25
+ Object.defineProperty(exports, "registerTool", { enumerable: true, get: function () { return registry_1.registerTool; } });
26
+ Object.defineProperty(exports, "validate_tool", { enumerable: true, get: function () { return registry_1.validate_tool; } });
27
+ Object.defineProperty(exports, "validateTool", { enumerable: true, get: function () { return registry_1.validateTool; } });
19
28
  // Middleware
20
29
  var middleware_1 = require("./middleware");
21
30
  Object.defineProperty(exports, "createLoggingMiddleware", { enumerable: true, get: function () { return middleware_1.createLoggingMiddleware; } });
@@ -90,3 +90,36 @@ export declare function createToolsRegistry(): ToolsRegistry;
90
90
  * Reset the global registry (mainly for testing)
91
91
  */
92
92
  export declare function resetToolsRegistry(): void;
93
+ /**
94
+ * Get the global registry instance (alias for getToolsRegistry)
95
+ * For Python SDK parity: get_registry()
96
+ */
97
+ export declare function get_registry(): ToolsRegistry;
98
+ /** camelCase alias for get_registry — idiomatic TypeScript */
99
+ export declare const getRegistry: typeof get_registry;
100
+ /**
101
+ * Get a single tool by name from the global registry
102
+ * For Python SDK parity: get_tool()
103
+ */
104
+ export declare function get_tool<TConfig = unknown, TInput = unknown, TOutput = unknown>(id: string, config?: TConfig): PraisonTool<TInput, TOutput> | null;
105
+ /** camelCase alias for get_tool — idiomatic TypeScript */
106
+ export declare const getTool: typeof get_tool;
107
+ /**
108
+ * Register a tool with the global registry
109
+ * For Python SDK parity: register_tool()
110
+ */
111
+ export declare function register_tool(metadata: ToolMetadata, factory: ToolFactory): void;
112
+ /** camelCase alias for register_tool — idiomatic TypeScript */
113
+ export declare const registerTool: typeof register_tool;
114
+ /**
115
+ * Validate a tool's configuration and dependencies
116
+ * For Python SDK parity: validate_tool()
117
+ */
118
+ export declare function validate_tool(id: string): Promise<{
119
+ valid: boolean;
120
+ installed: boolean;
121
+ missingEnvVars: string[];
122
+ errors: string[];
123
+ }>;
124
+ /** camelCase alias for validate_tool — idiomatic TypeScript */
125
+ export declare const validateTool: typeof validate_tool;
@@ -39,10 +39,14 @@ var __importStar = (this && this.__importStar) || (function () {
39
39
  };
40
40
  })();
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.ToolsRegistry = void 0;
42
+ exports.validateTool = exports.registerTool = exports.getTool = exports.getRegistry = exports.ToolsRegistry = void 0;
43
43
  exports.getToolsRegistry = getToolsRegistry;
44
44
  exports.createToolsRegistry = createToolsRegistry;
45
45
  exports.resetToolsRegistry = resetToolsRegistry;
46
+ exports.get_registry = get_registry;
47
+ exports.get_tool = get_tool;
48
+ exports.register_tool = register_tool;
49
+ exports.validate_tool = validate_tool;
46
50
  /**
47
51
  * Tools Registry - Manages tool registration, lookup, and instantiation
48
52
  */
@@ -278,3 +282,77 @@ function resetToolsRegistry() {
278
282
  }
279
283
  globalRegistry = null;
280
284
  }
285
+ /**
286
+ * Get the global registry instance (alias for getToolsRegistry)
287
+ * For Python SDK parity: get_registry()
288
+ */
289
+ function get_registry() {
290
+ return getToolsRegistry();
291
+ }
292
+ /** camelCase alias for get_registry — idiomatic TypeScript */
293
+ exports.getRegistry = get_registry;
294
+ /**
295
+ * Get a single tool by name from the global registry
296
+ * For Python SDK parity: get_tool()
297
+ */
298
+ function get_tool(id, config) {
299
+ try {
300
+ const registry = getToolsRegistry();
301
+ return registry.create(id, config);
302
+ }
303
+ catch {
304
+ return null;
305
+ }
306
+ }
307
+ /** camelCase alias for get_tool — idiomatic TypeScript */
308
+ exports.getTool = get_tool;
309
+ /**
310
+ * Register a tool with the global registry
311
+ * For Python SDK parity: register_tool()
312
+ */
313
+ function register_tool(metadata, factory) {
314
+ const registry = getToolsRegistry();
315
+ registry.register(metadata, factory);
316
+ }
317
+ /** camelCase alias for register_tool — idiomatic TypeScript */
318
+ exports.registerTool = register_tool;
319
+ /**
320
+ * Validate a tool's configuration and dependencies
321
+ * For Python SDK parity: validate_tool()
322
+ */
323
+ async function validate_tool(id) {
324
+ const registry = getToolsRegistry();
325
+ if (!registry.has(id)) {
326
+ return {
327
+ valid: false,
328
+ installed: false,
329
+ missingEnvVars: [],
330
+ errors: [`Tool "${id}" is not registered`]
331
+ };
332
+ }
333
+ const status = await registry.getInstallStatus(id);
334
+ if (!status) {
335
+ return {
336
+ valid: false,
337
+ installed: false,
338
+ missingEnvVars: [],
339
+ errors: [`Failed to get status for tool "${id}"`]
340
+ };
341
+ }
342
+ const errors = [];
343
+ if (!status.installed) {
344
+ const installCmd = status.installCommand ?? `npm install ${registry.getMetadata(id)?.packageName ?? id}`;
345
+ errors.push(`Package dependency not installed. Run: ${installCmd}`);
346
+ }
347
+ if (status.missingEnvVars.length > 0) {
348
+ errors.push(`Missing environment variables: ${status.missingEnvVars.join(', ')}`);
349
+ }
350
+ return {
351
+ valid: status.installed && status.missingEnvVars.length === 0,
352
+ installed: status.installed,
353
+ missingEnvVars: status.missingEnvVars,
354
+ errors
355
+ };
356
+ }
357
+ /** camelCase alias for validate_tool — idiomatic TypeScript */
358
+ exports.validateTool = validate_tool;
@@ -215,3 +215,20 @@ export declare class MissingEnvVarError extends Error {
215
215
  readonly docsSlug: string;
216
216
  constructor(toolId: string, envVar: string, docsSlug: string);
217
217
  }
218
+ /**
219
+ * Error thrown when an agent exceeds its budget/cost limit.
220
+ * Mirrors Python SDK: BudgetExceededError(agent_name, total_cost, max_budget)
221
+ *
222
+ * Usage:
223
+ * try { await agent.start("..."); }
224
+ * catch (e) {
225
+ * if (e instanceof BudgetExceededError)
226
+ * console.log(`Agent '${e.agentName}' spent $${e.totalCost} of $${e.maxBudget}`);
227
+ * }
228
+ */
229
+ export declare class BudgetExceededError extends Error {
230
+ readonly agentName: string;
231
+ readonly totalCost: number;
232
+ readonly maxBudget: number;
233
+ constructor(agentName: string, totalCost: number, maxBudget: number);
234
+ }
@@ -5,7 +5,7 @@
5
5
  * Standard interfaces for tool registration, execution, and middleware.
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.MissingEnvVarError = exports.MissingDependencyError = void 0;
8
+ exports.BudgetExceededError = exports.MissingEnvVarError = exports.MissingDependencyError = void 0;
9
9
  /**
10
10
  * Error thrown when optional dependency is missing
11
11
  */
@@ -47,3 +47,24 @@ class MissingEnvVarError extends Error {
47
47
  }
48
48
  }
49
49
  exports.MissingEnvVarError = MissingEnvVarError;
50
+ /**
51
+ * Error thrown when an agent exceeds its budget/cost limit.
52
+ * Mirrors Python SDK: BudgetExceededError(agent_name, total_cost, max_budget)
53
+ *
54
+ * Usage:
55
+ * try { await agent.start("..."); }
56
+ * catch (e) {
57
+ * if (e instanceof BudgetExceededError)
58
+ * console.log(`Agent '${e.agentName}' spent $${e.totalCost} of $${e.maxBudget}`);
59
+ * }
60
+ */
61
+ class BudgetExceededError extends Error {
62
+ constructor(agentName, totalCost, maxBudget) {
63
+ super(`Agent '${agentName}' exceeded budget: $${totalCost.toFixed(4)} >= $${maxBudget.toFixed(4)}`);
64
+ this.agentName = agentName;
65
+ this.totalCost = totalCost;
66
+ this.maxBudget = maxBudget;
67
+ this.name = 'BudgetExceededError';
68
+ }
69
+ }
70
+ exports.BudgetExceededError = BudgetExceededError;
@@ -250,22 +250,52 @@ async function scrapeUrl(url) {
250
250
  // ============================================================================
251
251
  // UTILITY TOOLS
252
252
  // ============================================================================
253
+ /** Shell metacharacters that enable command chaining or substitution */
254
+ const SHELL_METACHAR_PATTERN = /[;|&`><]|\$\([^)]*\)|\$\{/;
255
+ function containsShellMetacharacters(command) {
256
+ return SHELL_METACHAR_PATTERN.test(command);
257
+ }
258
+ function parseCommandParts(command) {
259
+ const parts = command.trim().match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [];
260
+ return parts.map((part) => part.replace(/^["']|["']$/g, ''));
261
+ }
253
262
  /**
254
263
  * Execute shell command (safe version - read-only commands)
255
264
  */
256
265
  async function shell(command) {
257
- // Only allow safe read-only commands
258
266
  const safeCommands = ['ls', 'cat', 'head', 'tail', 'wc', 'grep', 'find', 'echo', 'date', 'pwd', 'which'];
259
- const firstWord = command.split(/\s+/)[0];
260
- if (!safeCommands.includes(firstWord)) {
261
- return { success: false, error: `Command not allowed: ${firstWord}` };
267
+ const trimmed = command.trim();
268
+ if (!trimmed) {
269
+ return { success: false, error: 'Empty command' };
270
+ }
271
+ if (containsShellMetacharacters(trimmed)) {
272
+ return { success: false, error: 'Shell metacharacters are not allowed' };
273
+ }
274
+ const parts = parseCommandParts(trimmed);
275
+ const cmd = parts[0];
276
+ if (!safeCommands.includes(cmd)) {
277
+ return { success: false, error: `Command not allowed: ${cmd}` };
262
278
  }
263
279
  try {
264
- const { exec } = await Promise.resolve().then(() => __importStar(require('child_process')));
265
- const { promisify } = await Promise.resolve().then(() => __importStar(require('util')));
266
- const execAsync = promisify(exec);
267
- const { stdout, stderr } = await execAsync(command, { timeout: 5000 });
268
- return { success: true, data: stdout || stderr };
280
+ const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
281
+ const result = await new Promise((resolve, reject) => {
282
+ const proc = spawn(cmd, parts.slice(1), {
283
+ shell: false,
284
+ timeout: 5000,
285
+ });
286
+ let stdout = '';
287
+ let stderr = '';
288
+ proc.stdout?.on('data', (data) => { stdout += data.toString(); });
289
+ proc.stderr?.on('data', (data) => { stderr += data.toString(); });
290
+ proc.on('close', (code) => {
291
+ resolve({ stdout, stderr, code: code ?? 1 });
292
+ });
293
+ proc.on('error', reject);
294
+ });
295
+ if (result.code !== 0) {
296
+ return { success: false, error: result.stderr || `Exit code ${result.code}` };
297
+ }
298
+ return { success: true, data: result.stdout || result.stderr };
269
299
  }
270
300
  catch (error) {
271
301
  return { success: false, error: error.message ?? String(error) };
@@ -41,7 +41,10 @@ export declare function createWorkflowFromYAML(definition: YAMLWorkflowDefinitio
41
41
  /**
42
42
  * Load workflow from YAML file
43
43
  */
44
- export declare function loadWorkflowFromFile(filePath: string, agents?: Record<string, any>, tools?: Record<string, any>): Promise<ParsedWorkflow>;
44
+ export declare function loadWorkflowFromFile(filePath: string, agents?: Record<string, any>, tools?: Record<string, any>, options?: {
45
+ basePath?: string;
46
+ maxFileSizeBytes?: number;
47
+ }): Promise<ParsedWorkflow>;
45
48
  /**
46
49
  * Validate YAML workflow definition
47
50
  */
@@ -42,12 +42,19 @@ exports.createWorkflowFromYAML = createWorkflowFromYAML;
42
42
  exports.loadWorkflowFromFile = loadWorkflowFromFile;
43
43
  exports.validateWorkflowDefinition = validateWorkflowDefinition;
44
44
  const index_1 = require("./index");
45
+ const path = __importStar(require("path"));
46
+ // Whitelist of allowed step keys to prevent injection
47
+ const ALLOWED_STEP_KEYS = new Set([
48
+ 'type', 'agent', 'tool', 'input', 'output', 'condition',
49
+ 'onError', 'maxRetries', 'timeout', 'loopCondition', 'maxIterations',
50
+ ]);
45
51
  /**
46
52
  * Parse YAML string into workflow definition
47
53
  */
48
54
  function parseYAMLWorkflow(yamlContent) {
49
- // Simple YAML parser for workflow definitions
50
- // For production, use js-yaml package
55
+ // SECURITY: If migrating to js-yaml, you MUST use:
56
+ // yaml.load(content, { schema: yaml.JSON_SCHEMA })
57
+ // Never use yaml.load() with DEFAULT_SCHEMA — it enables arbitrary JS execution.
51
58
  const lines = yamlContent.split('\n');
52
59
  const result = {
53
60
  name: '',
@@ -90,7 +97,11 @@ function parseYAMLWorkflow(yamlContent) {
90
97
  };
91
98
  }
92
99
  else if (currentStep) {
93
- // Step properties
100
+ // Step properties — whitelist allowed keys to prevent injection
101
+ if (!ALLOWED_STEP_KEYS.has(key)) {
102
+ // Ignore unknown keys — do not allow arbitrary property injection
103
+ continue;
104
+ }
94
105
  if (key === 'type')
95
106
  currentStep.type = value;
96
107
  else if (key === 'agent')
@@ -262,9 +273,35 @@ function parseValue(value) {
262
273
  /**
263
274
  * Load workflow from YAML file
264
275
  */
265
- async function loadWorkflowFromFile(filePath, agents = {}, tools = {}) {
276
+ async function loadWorkflowFromFile(filePath, agents = {}, tools = {}, options = {}) {
266
277
  const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
267
- const content = await fs.readFile(filePath, 'utf-8');
278
+ // SECURITY: Prevent path traversal
279
+ const normalizedPath = path.normalize(filePath);
280
+ // Check for '..' as path segments (not just substring)
281
+ const pathSegments = normalizedPath.split(path.sep);
282
+ if (pathSegments.includes('..')) {
283
+ throw new Error('Path traversal detected: ".." path segments are not allowed');
284
+ }
285
+ let effectivePath;
286
+ // If basePath is specified, ensure resolvedPath stays within it
287
+ if (options.basePath) {
288
+ const resolvedBase = path.resolve(options.basePath);
289
+ const resolvedFile = path.resolve(options.basePath, normalizedPath);
290
+ if (!resolvedFile.startsWith(resolvedBase + path.sep) && resolvedFile !== resolvedBase) {
291
+ throw new Error(`File path must be within base directory: ${options.basePath}`);
292
+ }
293
+ effectivePath = resolvedFile;
294
+ }
295
+ else {
296
+ effectivePath = path.resolve(normalizedPath);
297
+ }
298
+ // SECURITY: Enforce file size limit (default 1 MB)
299
+ const maxSize = options.maxFileSizeBytes ?? 1048576;
300
+ const stat = await fs.stat(effectivePath);
301
+ if (stat.size > maxSize) {
302
+ throw new Error(`File too large: ${stat.size} bytes exceeds limit of ${maxSize} bytes`);
303
+ }
304
+ const content = await fs.readFile(effectivePath, 'utf-8');
268
305
  const definition = parseYAMLWorkflow(content);
269
306
  return createWorkflowFromYAML(definition, agents, tools);
270
307
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "praisonai",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "PraisonAI TypeScript AI Agents Framework - Node.js, npm, and Javascript AI Agents Framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -65,7 +65,7 @@
65
65
  },
66
66
  "dependencies": {
67
67
  "@modelcontextprotocol/sdk": "^1.12.1",
68
- "axios": "^1.7.9",
68
+ "axios": "1.8.4",
69
69
  "dotenv": "^16.4.7",
70
70
  "fast-xml-parser": "^4.5.1",
71
71
  "node-fetch": "^2.6.9",