399 lines
9.5 KiB
Markdown
399 lines
9.5 KiB
Markdown
# External WP-CLI Testing - Implementation Reference
|
|
|
|
## Complete Data Flow
|
|
|
|
### 1. User Enables Toggle in Builder
|
|
|
|
**File:** `chat/public/builder.js`
|
|
|
|
```javascript
|
|
// Line ~36
|
|
builderState.externalTestingEnabled = false // default
|
|
|
|
// User clicks toggle → sets to true
|
|
builderState.externalTestingEnabled = true
|
|
|
|
// When sending message (line ~3967)
|
|
const payload = {
|
|
content: messageContent,
|
|
model,
|
|
cli,
|
|
externalTestingEnabled: !!builderState.externalTestingEnabled
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Chat Server Receives Message
|
|
|
|
**File:** `chat/server.js`
|
|
|
|
```javascript
|
|
// Line ~8804
|
|
const wpCliMcpEnabled = message?.externalTestingEnabled === true
|
|
|
|
// Line ~8885-8901: If enabled, inject MCP server
|
|
if (wpCliMcpEnabled) {
|
|
const wpMcpServerPath = path.resolve(__dirname, '../opencode/mcp-servers/wp-cli-testing/index.js')
|
|
|
|
executionEnv.OPENCODE_EXTRA_MCP_SERVERS = JSON.stringify([
|
|
{
|
|
name: 'wp-cli-testing',
|
|
command: 'node',
|
|
args: [wpMcpServerPath],
|
|
disabled: false
|
|
}
|
|
])
|
|
} else {
|
|
delete executionEnv.OPENCODE_EXTRA_MCP_SERVERS // Safety gate
|
|
}
|
|
|
|
// Pass env to OpenCode
|
|
await opencodeManager.executeInSession(..., { env: executionEnv })
|
|
```
|
|
|
|
---
|
|
|
|
### 3. OpenCode Reads Environment Variable
|
|
|
|
**File:** `opencode/packages/opencode/src/config/config.ts`
|
|
|
|
```typescript
|
|
// Line ~233-294: Parse OPENCODE_EXTRA_MCP_SERVERS
|
|
const extraMcpRaw = process.env.OPENCODE_EXTRA_MCP_SERVERS
|
|
if (extraMcpRaw && typeof extraMcpRaw === "string") {
|
|
const parsed = JSON.parse(extraMcpRaw)
|
|
if (Array.isArray(parsed)) {
|
|
result.mcp ??= {}
|
|
for (const entry of parsed) {
|
|
result.mcp[entry.name] = {
|
|
type: "local",
|
|
command: [...commandParts, ...argsParts],
|
|
env: envRecord,
|
|
enabled: true,
|
|
...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Result: OpenCode config now has `mcp['wp-cli-testing']` configured
|
|
|
|
---
|
|
|
|
### 4. OpenCode Starts MCP Server
|
|
|
|
**File:** `opencode/packages/opencode/src/mcp/index.ts`
|
|
|
|
OpenCode's MCP system automatically:
|
|
1. Reads `config.mcp['wp-cli-testing']`
|
|
2. Spawns: `node opencode/mcp-servers/wp-cli-testing/index.js`
|
|
3. Connects via stdio transport
|
|
4. Calls `listTools()` to get available tools
|
|
|
|
---
|
|
|
|
### 5. MCP Server Registers Tools
|
|
|
|
**File:** `opencode/mcp-servers/wp-cli-testing/index.js`
|
|
|
|
```javascript
|
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
return {
|
|
tools: [
|
|
{
|
|
name: "test_plugin_external_wp",
|
|
description: "Run WP-CLI based verification...",
|
|
inputSchema: { ... }
|
|
},
|
|
{
|
|
name: "external_wp_testing_config",
|
|
description: "Return resolved config...",
|
|
inputSchema: { ... }
|
|
}
|
|
]
|
|
}
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
### 6. AI Invokes test_plugin_external_wp Tool
|
|
|
|
**File:** `opencode/packages/opencode/src/session/prompt.ts`
|
|
|
|
OpenCode's session handler:
|
|
1. Tool calls go through `resolveTools()` (line ~668)
|
|
2. MCP tools added via `MCP.tools()` (line ~745)
|
|
3. AI invokes tool with parameters
|
|
|
|
```javascript
|
|
test_plugin_external_wp({
|
|
plugin_path: "/workspace/my-plugin",
|
|
plugin_slug: "my-plugin",
|
|
test_scenarios: [...]
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
### 7. MCP Server Handles Tool Call
|
|
|
|
**File:** `opencode/mcp-servers/wp-cli-testing/index.js`
|
|
|
|
```javascript
|
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
const toolName = req.params.name
|
|
const args = req.params.arguments
|
|
|
|
// Import external-wp-testing module
|
|
const externalTesting = await import("../../../chat/external-wp-testing.js")
|
|
const mod = externalTesting.default ?? externalTesting
|
|
|
|
const tester = mod.createExternalWpTester({})
|
|
|
|
const result = await tester.runTest(
|
|
{
|
|
plugin_path: args.plugin_path,
|
|
plugin_slug: args.plugin_slug,
|
|
...
|
|
},
|
|
{ configOverrides: {} }
|
|
)
|
|
|
|
return { content: [{ type: "text", text: JSON.stringify(result) }] }
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
### 8. External WP Testing Executes
|
|
|
|
**File:** `chat/external-wp-testing.js`
|
|
|
|
```javascript
|
|
async function runTest(input, opts) {
|
|
const config = getExternalTestingConfig(opts.configOverrides)
|
|
|
|
// Queue the test
|
|
return queue.enqueue(async () => {
|
|
// 1. Create subsite (if multisite enabled)
|
|
if (config.enableMultisite) {
|
|
subsite = await createSubsite(config, sessionId)
|
|
// Runs: wp site create --slug=test-abc123
|
|
}
|
|
|
|
// 2. Upload plugin via SCP
|
|
if (pluginPath) {
|
|
await installPluginFromLocal(config, pluginPath, pluginSlug, sessionId)
|
|
}
|
|
|
|
// 3. Install required plugins
|
|
const installedPlugins = await installRequiredPlugins(config, subsite.url, requiredPlugins)
|
|
|
|
// 4. Activate test plugin
|
|
if (pluginSlug) {
|
|
await runSshCommand(config,
|
|
buildWpCliCommand(config, subsite.url, `plugin activate ${pluginSlug}`)
|
|
)
|
|
}
|
|
|
|
// 5. Run test scenarios
|
|
const cliTests = await runTestScenarios(config, subsite.url, scenarios)
|
|
|
|
// 6. Schedule cleanup
|
|
if (config.autoCleanup && subsite.slug) {
|
|
setTimeout(() => { deleteSubsite(config, subsite.slug) }, config.cleanupDelayMs)
|
|
}
|
|
|
|
return { ok: true, test_results: { cli_tests: cliTests }, ... }
|
|
})
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 9. SSH Commands Execute on WordPress Server
|
|
|
|
**Functions:**
|
|
- `buildWpCliCommand(config, url, command)` → `wp --path=/var/www/html --url=https://site.com/test-abc command`
|
|
- `runSshCommand(config, command)` → Executes via SSH with key auth
|
|
|
|
**Example Commands Executed:**
|
|
```bash
|
|
# Create subsite
|
|
ssh -i ~/.ssh/wp-key wordpress@wp-test.com "wp --path=/var/www/html site create --slug=test-abc123"
|
|
|
|
# Copy plugin
|
|
scp -i ~/.ssh/wp-key -r /local/plugin wordpress@wp-test.com:/var/www/html/wp-content/plugins/
|
|
|
|
# Activate plugin
|
|
ssh -i ~/.ssh/wp-key wordpress@wp-test.com "wp --path=/var/www/html --url=https://wp-test.com/test-abc123 plugin activate my-plugin"
|
|
|
|
# Run test
|
|
ssh -i ~/.ssh/wp-key wordpress@wp-test.com "wp --path=/var/www/html --url=https://wp-test.com/test-abc123 plugin status my-plugin"
|
|
|
|
# Later: Delete subsite
|
|
ssh -i ~/.ssh/wp-key wordpress@wp-test.com "wp --path=/var/www/html site delete --slug=test-abc123 --yes"
|
|
```
|
|
|
|
---
|
|
|
|
### 10. Results Return to AI
|
|
|
|
**Return Path:**
|
|
```
|
|
WordPress Server (via SSH)
|
|
↓ command output
|
|
external-wp-testing.js runTest()
|
|
↓ formatted result
|
|
MCP Server CallToolRequestSchema handler
|
|
↓ JSON response
|
|
OpenCode MCP client
|
|
↓ tool result
|
|
AI Session Handler
|
|
↓ assistant message
|
|
Chat Server
|
|
↓ SSE stream
|
|
Builder UI
|
|
```
|
|
|
|
**Result Format:**
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"session_id": "abc123",
|
|
"subsite_url": "https://wp-test.example.com/test-abc123",
|
|
"test_results": {
|
|
"mode": "cli",
|
|
"cli_tests": {
|
|
"passed": 3,
|
|
"failed": 0,
|
|
"results": [...]
|
|
}
|
|
},
|
|
"installed_plugins": [...],
|
|
"duration": 15230,
|
|
"cleanup_scheduled": "2026-02-08T11:30:00Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Key Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `chat/public/builder.js` | Builder UI toggle state |
|
|
| `chat/server.js` | Gate MCP injection via env var |
|
|
| `opencode/packages/opencode/src/config/config.ts` | Parse OPENCODE_EXTRA_MCP_SERVERS |
|
|
| `opencode/packages/opencode/src/mcp/index.ts` | Load and connect MCP servers |
|
|
| `opencode/mcp-servers/wp-cli-testing/index.js` | MCP server implementation |
|
|
| `chat/external-wp-testing.js` | Core testing logic |
|
|
|
|
---
|
|
|
|
## Environment Variables Flow
|
|
|
|
```
|
|
Host Environment
|
|
↓
|
|
Chat Server process.env
|
|
↓
|
|
executionEnv = {...process.env, OPENCODE_EXTRA_MCP_SERVERS: "..."}
|
|
↓
|
|
OpenCode spawned with executionEnv
|
|
↓
|
|
process.env.TEST_WP_HOST → external-wp-testing.js getExternalTestingConfig()
|
|
↓
|
|
SSH connection using TEST_WP_SSH_KEY
|
|
↓
|
|
Commands execute on WordPress server
|
|
```
|
|
|
|
**Required Environment Variables:**
|
|
- `TEST_WP_HOST`
|
|
- `TEST_WP_SSH_USER`
|
|
- `TEST_WP_SSH_KEY`
|
|
- `TEST_WP_PATH`
|
|
- `TEST_WP_BASE_URL`
|
|
- etc. (see EXTERNAL_WP_CLI_TESTING_SETUP.md)
|
|
|
|
---
|
|
|
|
## Security Gates
|
|
|
|
1. **Builder Toggle** - User must explicitly enable
|
|
2. **Environment Variable Gate** - Only set when toggle is on
|
|
3. **MCP Loading** - Only loads when env var present
|
|
4. **SSH Key Auth** - Requires valid key file
|
|
5. **Tool Gating** - Tools not available if toggle off
|
|
|
|
When toggle is OFF:
|
|
- `externalTestingEnabled: false` in payload
|
|
- `wpCliMcpEnabled = false` on server
|
|
- `OPENCODE_EXTRA_MCP_SERVERS` is deleted from env
|
|
- MCP server never loads
|
|
- Tools never available to AI
|
|
|
|
---
|
|
|
|
## Testing The Flow
|
|
|
|
### 1. Check Environment
|
|
```bash
|
|
echo $TEST_WP_HOST
|
|
echo $TEST_WP_SSH_KEY
|
|
```
|
|
|
|
### 2. Test SSH Connection
|
|
```bash
|
|
ssh -i $TEST_WP_SSH_KEY $TEST_WP_SSH_USER@$TEST_WP_HOST "wp site list"
|
|
```
|
|
|
|
### 3. Enable Toggle in Builder
|
|
- Open Builder
|
|
- Enable "External WP CLI testing"
|
|
- Verify toggle shows enabled state
|
|
|
|
### 4. Check Server Logs
|
|
```bash
|
|
tail -f logs/chat-server.log | grep "WP CLI Testing"
|
|
# Should see: "Enabling WP CLI Testing MCP server"
|
|
```
|
|
|
|
### 5. Ask AI to Test
|
|
```
|
|
"Create a plugin and test it on the external WordPress server"
|
|
```
|
|
|
|
### 6. Verify Subsite Created
|
|
```bash
|
|
ssh -i $TEST_WP_SSH_KEY $TEST_WP_SSH_USER@$TEST_WP_HOST "wp site list | grep test-"
|
|
```
|
|
|
|
---
|
|
|
|
## Multisite Architecture
|
|
|
|
```
|
|
WordPress Multisite Network
|
|
├── Main Site (/)
|
|
│ └── Core WP, Plugins, Themes (shared)
|
|
├── Subsite: /test-abc123 (Session 1)
|
|
│ ├── Isolated content/options
|
|
│ ├── Test Plugin A activated
|
|
│ └── Tests running
|
|
├── Subsite: /test-def456 (Session 2)
|
|
│ ├── Isolated content/options
|
|
│ ├── Test Plugin B activated
|
|
│ └── Tests running
|
|
└── Subsite: /test-xyz789 (Session 3)
|
|
└── ...
|
|
```
|
|
|
|
**Concurrency:** 20+ simultaneous tests via queue system + isolated subsites
|
|
|
|
**Cleanup:** Each subsite auto-deleted after 1 hour (configurable via `TEST_CLEANUP_DELAY`)
|