mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-21 18:52:56 +00:00
Add non-interactive mode for agent execution (#35)
* refactor: extract shared test helpers and utilities Created centralized test-helpers.ts module to eliminate duplication across test files: **Extracted Helpers:** - createMockManifest() - Reusable mock manifest data - createEmptyManifest() - Empty manifest for edge cases - createConsoleMocks() - Console spy setup - createProcessExitMock() - Process exit mock - restoreMocks() - Mock cleanup utility - mockSuccessfulFetch() - Simplified successful fetch mock - mockFailedFetch() - Simplified failed fetch mock - mockFetchWithStatus() - Fetch mock with custom status - setupTestEnvironment() - Test directory and env setup - teardownTestEnvironment() - Cleanup utility **Deduplication Impact:** - commands.test.ts: Removed 50+ lines of duplicate mock setup - manifest.test.ts: Removed 80+ lines of duplicate manifest data and setup code - integration.test.ts: Removed 40+ lines of duplicate setup/teardown **Benefits:** - Single source of truth for test fixtures - Consistent mock patterns across all tests - Easier maintenance - changes to test setup in one place - Improved test readability Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: Add non-interactive mode for agent execution Implements --prompt and --prompt-file flags to enable non-interactive agent execution. This allows users to: - Execute agents with a prompt and exit automatically - Use spawn in CI/CD pipelines and automation scripts - Pass prompts via command line or file Changes: - TypeScript CLI: Parse --prompt/-p and --prompt-file flags - Security: Add validatePrompt() to prevent command injection - Commands: Pass prompt via SPAWN_PROMPT env var to bash scripts - Bash scripts: Detect SPAWN_PROMPT and fork interactive/non-interactive - Help text: Document new flags with examples Implementation: - claude.sh: Use 'claude -p' for non-interactive execution - aider.sh: Use 'aider -m' for non-interactive execution - shared/common.sh: Add execute_agent_non_interactive() helper Security: - Validates prompts for command injection patterns - Length limit: 10KB max - Blocks $(), backticks, piping to bash/sh - Uses printf %q for proper shell escaping Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * docs: Add testing guide for non-interactive mode Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Sprite <noreply@sprite.dev> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
580424189a
commit
c09e714cc7
8 changed files with 394 additions and 83 deletions
|
|
@ -74,3 +74,40 @@ export function validateScriptContent(script: string): void {
|
|||
throw new Error("Script must start with a valid shebang (e.g., #!/bin/bash)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a prompt string for non-interactive agent execution.
|
||||
* SECURITY-CRITICAL: Prevents command injection via prompt parameter.
|
||||
*
|
||||
* @param prompt - The user-provided prompt to validate
|
||||
* @throws Error if validation fails
|
||||
*/
|
||||
export function validatePrompt(prompt: string): void {
|
||||
if (!prompt || prompt.trim() === "") {
|
||||
throw new Error("Prompt cannot be empty");
|
||||
}
|
||||
|
||||
// Check length constraints (10KB max to prevent DoS)
|
||||
const MAX_PROMPT_LENGTH = 10 * 1024;
|
||||
if (prompt.length > MAX_PROMPT_LENGTH) {
|
||||
throw new Error(`Prompt exceeds maximum length of ${MAX_PROMPT_LENGTH} characters`);
|
||||
}
|
||||
|
||||
// Check for obvious command injection patterns
|
||||
// These patterns would break out of the shell quoting used in bash scripts
|
||||
const dangerousPatterns: Array<{ pattern: RegExp; description: string }> = [
|
||||
{ pattern: /\$\(.*\)/, description: "command substitution $()" },
|
||||
{ pattern: /`[^`]*`/, description: "command substitution backticks" },
|
||||
{ pattern: /;\s*rm\s+-rf/, description: "command chaining with rm -rf" },
|
||||
{ pattern: /\|\s*bash/, description: "piping to bash" },
|
||||
{ pattern: /\|\s*sh/, description: "piping to sh" },
|
||||
];
|
||||
|
||||
for (const { pattern, description } of dangerousPatterns) {
|
||||
if (pattern.test(prompt)) {
|
||||
throw new Error(
|
||||
`Prompt blocked: contains potentially dangerous pattern (${description}). If this is a false positive, please use --prompt-file instead.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue