-
Notifications
You must be signed in to change notification settings - Fork 27
🤖 Add basic support for SSH runtime #178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2ae9b82 to
0874776
Compare
✅ Runtime Integration Tests AddedAdded comprehensive integration tests for both Test InfrastructureDocker SSH Server:
Test Matrix Pattern:
Test CoverageCore Operations: (26 tests)
Edge Cases: (26 tests)
ResultsTest run: TEST_INTEGRATION=1 bun x jest tests/runtime/runtime.test.ts
# ~25 seconds for all 52 testsImplementation NotesLocalRuntime fixes:
SSHRuntime enhancements:
Key design decisions:
Files AddedReady for review! 🚀 |
|
Added macOS runtime integration tests to CI. The main integration suite covers Linux, but runtime code is particularly prone to system incompatibilities (process spawning, streams, file ops) so we run these tests on macOS as well. Full suite on matrix would be wasteful - this targets just the platform-sensitive code. |
fd95d45 to
20a2bad
Compare
|
Updated macOS runtime test job:
|
fb9c373 to
c27e082
Compare
ced92d9 to
ae86f06
Compare
Implements Phase 1 of pluggable runtime system with minimal Runtime interface that allows tools to execute in different environments (local, docker, ssh). Changes: - Add Runtime interface with 5 core methods: exec, readFile, writeFile, stat, exists - Implement LocalRuntime using Node.js APIs (spawn, fs/promises) - Refactor file tools (file_read, file_edit_*) to use runtime abstraction - Update ToolConfiguration to include runtime field - Inject LocalRuntime in aiService and ipcMain - Update tsconfig to ES2023 for Disposable type support - Update all tests to inject LocalRuntime (90 tests pass) This is a pure refactoring with zero user-facing changes. All existing functionality remains identical. Sets foundation for Docker and SSH runtimes. _Generated with `cmux`_
- Add SSHRuntime class implementing Runtime interface - Add runtime configuration types (local, ssh) - Add runtime factory to create runtime based on config - Use native ssh2 SFTP for file operations - Support SSH key and password authentication - Connection pooling and automatic reconnection
- Add runtimeConfig to WorkspaceMetadata - Update AIService to use runtime factory with workspace config - Update all tests and ipcMain to use runtime factory - Default to local runtime if no config specified
cae63b8 to
b408a7a
Compare
- Enables faster CI iteration by filtering all test types - Add comment explaining test_filter helps keep test times low - E2E tests use --grep for pattern matching - Storybook tests pass filter directly to test-storybook
When running workflow_dispatch with a test filter for debugging specific unit/integration tests, skip the expensive storybook and e2e test jobs to keep iteration times fast.
Improves separation of concerns by delegating workspace deletion to Runtime implementations, following the same pattern as rename. Changes: - Runtime.ts: Add deleteWorkspace() method to interface - LocalRuntime.ts: Implement with git worktree remove - Auto-uses --force for clean worktrees with submodules - Respects force flag for dirty worktrees - SSHRuntime.ts: Implement with rm -rf command - ipcMain.ts: Refactor removeWorkspaceInternal to delegate to runtime - Removed direct git operations and getMainWorktreeFromWorktree - ~40 LoC reduction from better abstraction - tests: Add matrix tests for both local and SSH runtimes - 6 new tests covering success, force-delete, and error cases - All 86 runtime tests passing - All 5 removeWorkspace IPC tests passing Design decisions: - Runtime computes paths from projectPath + workspaceName + srcDir - LocalRuntime handles submodule edge case (git requires --force) - SSHRuntime ignores force flag (rm -rf is always forceful) - Maintains backward compatibility with existing workspaces Net change: +150 lines (tests), -40 lines (ipcMain simplification)
- Remove unused result variables in execAsync calls - Remove unused execAsync import from SSHRuntime - Replace || with ?? for nullish coalescing - Remove unused catch parameter
Restore preloadAISDKProviders() functionality to eagerly load @ai-sdk/anthropic and @ai-sdk/openai modules before tests run concurrently. This prevents Jest's module sandboxing from blocking dynamic imports with 'You are trying to import a file outside of the scope of the test code' errors. The function was previously converted to a no-op under the assumption that 'Jest concurrency has been stabilized elsewhere', but tests still failed when run together. Preloading ensures providers are in the module cache before concurrent tests access them via dynamic imports in createModel(). Also add note to AGENTS.md warning that tests/ipcMain takes a long time locally.
d598a78 to
d05d645
Compare
- Increase TEST_TIMEOUT_SSH_MS from 45s to 60s to account for network latency - Add separate stream timeout constants (15s local, 25s SSH) - Pass runtime-specific stream timeout to sendMessageAndWait in all tests - Add preloadAISDKProviders to runtimeFileEditing tests to prevent race conditions SSH tests perform multiple AI calls per test, and with network overhead in CI, the previous 15s timeout was too tight. Now SSH tests get 25s per stream.
d05d645 to
f16addb
Compare
**Root cause:** config.getWorkspacePath() duplicated Runtime.getWorkspacePath(), causing workspace paths to be computed inconsistently. This broke workspace deletion and bash execution in tests. **Changes:** - **Deleted config.getWorkspacePath()** and config.getWorkspacePaths() - Replaced all uses with Runtime.getWorkspacePath() - Fixed IPC WORKSPACE_EXECUTE_BASH to use correct workspace path via Runtime - Fixed bash tool to pass cwd to runtime.exec() (was ignoring it) - Renamed deleteWorkspace.test.ts → removeWorkspace.test.ts for IPC parity - Added waitForInitComplete() helper using init-end events (no static sleeps) **Test improvements:** - 7/12 workspace deletion tests passing (up from 0/12) - All local runtime tests pass - SSH tests have pre-existing issues unrelated to this fix **Architecture:** - Runtime owns all path computation logic - Config only stores paths for legacy migration - IPC layer computes paths via Runtime, not Config _Generated with _
- SSHRuntime.initWorkspace now uses workspacePath for git operations - SSHRuntime.syncProjectToRemote clones to workspace path, not srcBaseDir - SSHRuntime.runInitHook runs hook in workspace directory - SSHRuntime.createWorkspace creates parent of workspace path, not parent of srcBaseDir - Enhanced waitForInitComplete to show init output on failure - Fixed all type errors: workdir → srcBaseDir - Updated parseRuntimeString to use correct srcBaseDir (without workspace name) - Updated test fixtures to use correct srcBaseDir values All 12 removeWorkspace tests passing (7 local + 5 SSH) All 16 createWorkspace tests passing
- Update chatCommands.test.ts to expect srcBaseDir instead of workdir - Remove workspace name from srcBaseDir in parseRuntimeString - Add srcDir to gitService.test.ts mockConfig to fix createWorktree calls
- Check if directory exists before checking for uncommitted changes - Return success for deleting non-existent workspace (rm -rf is no-op) - Prevents false 'uncommitted changes' error when workspace doesn't exist
- Close exec stream stdin right after spawning - Prevents runtime from waiting forever for input - Fixes sendMessage.test.ts hanging after tool calls
- AIService now passes workspacePath (not srcBaseDir) to tools - Fixes file_read/file_edit tools resolving paths from wrong directory - Enhanced EventCollector.logEventDiagnostics() with detailed output: * Tool call args and results * Error details * Stream deltas and content * Event type counts - assertStreamSuccess() and waitForEvent() now call logEventDiagnostics() - Fixes sendMessage integration test that was timing out
- SSHRuntime.renameWorkspace now uses expandTildeForSSH() before mv - expandTildeForSSH already handles quoting, so removed shescape.quote() - Refactored renameWorkspace.test.ts to use runtime matrix pattern: * Tests both local and SSH runtimes * Extracted test helpers for code reuse * Added waitForInitComplete() before rename operations - All rename tests now pass for both runtimes (4/4 tests) Bug was: mv command received quoted paths with unexpanded tilde (~), causing "No such file or directory" errors. Also, test was trying to rename before init hook completed, causing directory to not exist yet.
After the SSH runtime commit (#178), testing infrastructure had significant duplication across helper modules and inline functions. Changes: - Consolidated shared utilities into tests/ipcMain/helpers.ts - Added extractTextFromEvents(), sendMessageAndWait(), createWorkspaceWithInit() - Centralized test constants (INIT_HOOK_WAIT_MS, SSH_INIT_WAIT_MS, HAIKU_MODEL, etc.) - Deleted tests/ipcMain/test-helpers/runtimeTestHelpers.ts (149 lines) - Removed inline helper duplicates from test files - runtimeFileEditing.test.ts: -120 lines - removeWorkspace.test.ts: -39 lines - renameWorkspace.test.ts: -45 lines - runtimeExecuteBash.test.ts: simplified imports Result: 240 net lines removed, improved consistency across integration tests. All tests use consistent patterns from single source of truth.
After the SSH runtime commit (#178), testing infrastructure had significant duplication across helper modules and inline functions. Changes: - Consolidated shared utilities into tests/ipcMain/helpers.ts - Added extractTextFromEvents(), sendMessageAndWait(), createWorkspaceWithInit() - Centralized test constants (INIT_HOOK_WAIT_MS, SSH_INIT_WAIT_MS, HAIKU_MODEL, etc.) - Deleted tests/ipcMain/test-helpers/runtimeTestHelpers.ts (149 lines) - Removed inline helper duplicates from test files - runtimeFileEditing.test.ts: -120 lines - removeWorkspace.test.ts: -39 lines - renameWorkspace.test.ts: -45 lines - runtimeExecuteBash.test.ts: simplified imports Result: 240 net lines removed, improved consistency across integration tests. All tests use consistent patterns from single source of truth.
After the SSH runtime commit (#178), testing infrastructure had significant duplication across helper modules and inline functions. Changes: - Consolidated shared utilities into tests/ipcMain/helpers.ts - Added extractTextFromEvents(), sendMessageAndWait(), createWorkspaceWithInit() - Centralized test constants (INIT_HOOK_WAIT_MS, SSH_INIT_WAIT_MS, HAIKU_MODEL, etc.) - Deleted tests/ipcMain/test-helpers/runtimeTestHelpers.ts (149 lines) - Removed inline helper duplicates from test files - runtimeFileEditing.test.ts: -120 lines - removeWorkspace.test.ts: -39 lines - renameWorkspace.test.ts: -45 lines - runtimeExecuteBash.test.ts: simplified imports Result: 240 net lines removed, improved consistency across integration tests. All tests use consistent patterns from single source of truth.
After the SSH runtime commit (#178, commit 5f200a6), there was significant duplication in test infrastructure and repeated copy-to-clipboard patterns across components. ## Test Infrastructure Consolidation Consolidated 3 overlapping test helper modules and inline duplicates into `tests/ipcMain/helpers.ts`: - Added `extractTextFromEvents()`, `sendMessageAndWait()`, `createWorkspaceWithInit()` - Centralized test constants (INIT_HOOK_WAIT_MS, SSH_INIT_WAIT_MS, HAIKU_MODEL, timeouts) - Updated 4 test files to use consolidated helpers - Deleted `tests/ipcMain/test-helpers/runtimeTestHelpers.ts` (149 lines) **Result:** -240 lines, single source of truth for ipcMain integration test utilities. ## Copy-to-Clipboard Deduplication The copy feedback pattern (`copied` state + 2000ms timeout) was duplicated across 4 components with identical implementations. - Created `src/hooks/useCopyToClipboard.ts` for reusable copy functionality - Added `COPY_FEEDBACK_DURATION_MS` constant to `src/constants/ui.ts` - Updated AssistantMessage, UserMessage, ProposePlanToolCall, FileEditToolCall **Result:** 54 lines of duplicated code replaced with single 33-line hook. _Generated with `cmux`_
Overview
Implements pluggable runtime abstraction layer with both LocalRuntime and SSHRuntime implementations. Includes comprehensive integration testing for file editing tools across both runtimes.
Changes
Runtime Abstraction
exec(),readFile(),writeFile(),stat(), workspace lifecycleFile Tools Integration
file_read.ts,file_edit_replace.ts,file_edit_insert.tsto use runtimebash.tsto support both local and SSH executionSSH Runtime Bug Fixes
escapeShellArg()helper using single-quote escaping for reliable shell argument handlingIntegration Testing
runtimeFileEditing.test.ts(6 tests, ~42s)workspaceInitHook.test.tsto expect workspace creation logsWorkspace Init Events
Architecture Benefits
Easy to test: Runtime implementations isolated, matrix testing verifies both paths work identically.
Easy to extend: Adding new runtime = implement interface. Tools automatically work.
Battle-tested: SSH runtime validated with real AI file operations, not just unit tests.
Testing
Next Steps
Foundation ready for:
Generated with
cmux