chore: prune unused apps and services for the moeka fork
CI / Lint (push) Canceled after 0s
CI / Build Test (stage-web) (push) Canceled after 0s
CI / Build Test (ui-loading-screens) (push) Canceled after 0s
CI / Build Test (ui-transitions) (push) Canceled after 0s
CI / Unit Test (push) Canceled after 0s
CI / Type Check (push) Canceled after 0s
CI / Check Provenance (push) Canceled after 0s
Sync Labels / sync-labels (push) Successful in 1m43s

- Remove stage-tamagotchi, stage-pocket, server, and engine workspaces with their workflows, addons, and docs
- Switch the toolchain from pnpm to bun, replace lockfiles with bun.lock
- Rewrite remaining product references to Moeka
This commit is contained in:
2026-09-14 16:40:49 +07:00
parent 00c6867b7f
commit dc26878386
1981 changed files with 10974 additions and 260677 deletions
+31 -50
View File
@@ -4,16 +4,16 @@ Scope: `services/computer-use-mcp/**`
## Mission
`computer-use-mcp` is AIRI's deterministic execution substrate.
`computer-use-mcp` is Moeka's deterministic execution substrate.
- AIRI owns planning, chat UX, approval UX, provider integration, and MCP attachment.
- Moeka owns planning, chat UX, approval UX, provider integration, and MCP attachment.
- `computer-use-mcp` owns execution primitives, workflow orchestration, terminal/browser/desktop surfaces, trace, audit, and safety checks.
- Treat terminal, browser, editor, and desktop operations as one task system. Do not split them into disconnected demos.
## Agent Collaboration Policy
This policy is scoped to `services/computer-use-mcp/**`. Do not copy it into the
monorepo-level `AGENTS.md` as a global AIRI rule: other packages may not have
monorepo-level `AGENTS.md` as a global Moeka rule: other packages may not have
the same runtime risk profile, Spark agent setup, or review workflow.
Use `GPT-5.5 Controller + Spark / mini Read-only Worker Pool` as the default
@@ -44,9 +44,9 @@ The important truth is:
- `exec` is already a real mainline surface.
- `PTY` is no longer just a loose tool set; the workflow engine now has self-acquire support.
- Service-layer terminal E2Es for the current lane are present and treated as the terminal proof line.
- The AIRI chat terminal demo is now aligned with terminal lane v2 and no longer pre-creates PTY.
- The Moeka chat terminal demo is now aligned with terminal lane v2 and no longer pre-creates PTY.
- The desktop shell now distinguishes `pty_session` from `terminal_and_apps`.
- AIRI chat self-acquire is now part of the strict release gate set, so PTY mainline support is no longer intentionally held back.
- Moeka chat self-acquire is now part of the strict release gate set, so PTY mainline support is no longer intentionally held back.
Do not rely on compressed chat summaries to resume this work. Use this file as the handoff source of truth and update it when terminal-lane behavior changes materially.
@@ -132,23 +132,11 @@ It currently uses:
This is the current service-level proof for terminal lane v2.
### 5. AIRI chat self-acquire demo is now on the v2 path
### 5. Moeka chat self-acquire demo
`src/bin/e2e-airi-chat-terminal-self-acquire.ts` follows the same product story:
- no harness-side `pty_create`
- AIRI calls the real workflow
- the workflow self-acquires PTY for the interactive validation step
- AIRI finishes with a natural-language summary for demo use
The latest successful reports live under:
- `.computer-use-mcp/reports/airi-chat-terminal-self-acquire-*`
The current package commands are:
- `pnpm -F @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire`
- `pnpm -F @proj-airi/computer-use-mcp demo:terminal-self-acquire`
The chat-driven demo ran against the desktop app. That app is not part of this fork, so the demo
script and its package commands were removed. Use `e2e-terminal-self-acquire` for the same
workflow without the app UI.
### 6. Support matrix already reflects the new direction
@@ -163,32 +151,30 @@ Relevant entries in `src/support-matrix.ts`:
The current strict release gates are:
- `pnpm -F @proj-airi/computer-use-mcp e2e:developer-workflow`
- `pnpm -F @proj-airi/computer-use-mcp e2e:terminal-exec`
- `pnpm -F @proj-airi/computer-use-mcp e2e:terminal-pty`
- `pnpm -F @proj-airi/computer-use-mcp e2e:terminal-self-acquire`
- `pnpm -F @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire`
- `bun run --filter @proj-airi/computer-use-mcp e2e:developer-workflow`
- `bun run --filter @proj-airi/computer-use-mcp e2e:terminal-exec`
- `bun run --filter @proj-airi/computer-use-mcp e2e:terminal-pty`
- `bun run --filter @proj-airi/computer-use-mcp e2e:terminal-self-acquire`
## What Is Still Not Finished
These are the real gaps. Do not talk yourself into thinking terminal lane is fully shipped before they are closed.
### 1. Desktop approval semantics are improved, but still need one more explicit review
### 1. Approval semantics need a web implementation
`apps/stage-tamagotchi/src/renderer/App.vue` now distinguishes:
The desktop approval UI was removed with the desktop app. The scope model it introduced still
applies:
- `terminal_and_apps`
- `pty_session`
and it no longer pretends a PTY approval is the same thing as a generic terminal/app grant.
The intended behavior is:
The current intended behavior is:
- `terminal_exec` / `open_app` / `focus_app` keep the old session-scoped auto-approve behavior
- `terminal_exec` / `open_app` / `focus_app` keep session-scoped auto-approve behavior
- `pty_create` stores a `pty_session` grant scope
- `pty_create` does **not** auto-approve future PTY creation requests
This is much closer to the product model, but it is still worth reviewing whenever approval UX changes again.
The web app still needs an approval surface for these scopes.
## Where To Look First
@@ -198,10 +184,7 @@ If you are continuing terminal lane work, read these first:
2. `src/workflows/surface-resolver.ts`
3. `src/terminal/interactive-patterns.ts`
4. `src/bin/e2e-terminal-self-acquire.ts`
5. `src/bin/e2e-airi-chat-terminal-self-acquire.ts`
6. `src/support-matrix.ts`
7. `apps/stage-tamagotchi/src/renderer/App.vue`
8. `apps/stage-tamagotchi/src/renderer/modules/computer-use-approval.ts`
5. `src/support-matrix.ts`
That set is enough to reconstruct the current terminal-lane-v2 state without rereading the entire repo.
@@ -211,24 +194,22 @@ Use these as the baseline checks for terminal lane work:
### Service-level terminal lane
- `pnpm -F @proj-airi/computer-use-mcp e2e:terminal-exec`
- `pnpm -F @proj-airi/computer-use-mcp e2e:terminal-pty`
- `pnpm -F @proj-airi/computer-use-mcp e2e:terminal-self-acquire`
- `pnpm -F @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire`
- `bun run --filter @proj-airi/computer-use-mcp e2e:terminal-exec`
- `bun run --filter @proj-airi/computer-use-mcp e2e:terminal-pty`
- `bun run --filter @proj-airi/computer-use-mcp e2e:terminal-self-acquire`
- `bun run --filter @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire`
### Core test coverage
- `pnpm -F @proj-airi/computer-use-mcp exec vitest run --config ./vitest.config.ts`
- `bun run --filter @proj-airi/computer-use-mcp exec vitest run --config ./vitest.config.ts`
### Typecheck
- `pnpm -F @proj-airi/computer-use-mcp typecheck`
- `pnpm -F @proj-airi/stage-ui typecheck`
- `bun run --filter @proj-airi/computer-use-mcp typecheck`
- `bun run --filter @proj-airi/stage-ui typecheck`
If `pnpm -F @proj-airi/stage-tamagotchi typecheck` behaves oddly in the current environment, run the two underlying commands directly:
- `pnpm -F @proj-airi/stage-tamagotchi run typecheck:node`
- `pnpm -F @proj-airi/stage-tamagotchi run typecheck:web`
If `bun run --filter @proj-airi/stage-web typecheck` behaves oddly in the current environment, inspect the
web app scripts in `apps/stage-web/package.json`.
## Handoff Rules
@@ -237,7 +218,7 @@ If you change terminal lane behavior, update this file before stopping.
At minimum, always rewrite these four facts:
1. Is PTY self-acquire the mainline, or does any path still depend on pre-created PTY?
2. Is AIRI chat E2E aligned with the service-level terminal lane, or still on an older path?
2. Is Moeka chat E2E aligned with the service-level terminal lane, or still on an older path?
3. Is desktop approval using real `pty_session` semantics, or still old `terminal_and_apps` semantics?
4. Which terminal capabilities are `product-supported` vs only `covered` in `src/support-matrix.ts`?
@@ -245,7 +226,7 @@ If those four facts are stale, the next agent will lose time re-deriving context
## Boundary Reminder
- Keep provider-specific behavior in AIRI / `packages/stage-ui/**`.
- Keep provider-specific behavior in Moeka / `packages/stage-ui/**`.
- Keep OS-executor and workflow orchestration logic here.
- Do not expand this workstream into browser, native click/type/press, or VS Code productization until terminal lane is actually closed.
+15 -15
View File
@@ -1,25 +1,25 @@
# Feasibility Summary
This document records the validated state of the AIRI-specific macOS desktop orchestration v1 in `services/computer-use-mcp`.
This document records the validated state of the Moeka-specific macOS desktop orchestration v1 in `services/computer-use-mcp`.
## Bottom Line
The current direction is feasible and materially stronger than the earlier pure-vision-only path.
The validated architecture is now:
- AIRI keeps the control plane
- Moeka keeps the control plane
- `computer-use-mcp` keeps trace, audit, screenshot persistence, policy, and the MCP surface
- the primary execution backend is local macOS window automation
- AIRI desktop handles human approval through native dialogs
- Moeka desktop handles human approval through native dialogs
- terminal commands run in a controlled background shell runner instead of a Terminal tab script
That makes the feature an orchestration layer, not just a mouse-clicking demo.
## What Was Verified
### 1. The service still fits AIRI's MCP attachment model
### 1. The service still fits Moeka's MCP attachment model
AIRI continues to use the existing stdio MCP bridge through `mcp.json`.
Moeka continues to use the existing stdio MCP bridge through `mcp.json`.
No transport rewrite was required.
### 2. The service now exposes a tool-first desktop orchestration surface
@@ -32,7 +32,7 @@ Validated surface in this checkout:
- primitive UI interaction tools
- approval / trace / audit helpers
This is a better fit for AIRI than leading with pure screenshot-driven action selection.
This is a better fit for Moeka than leading with pure screenshot-driven action selection.
### 3. Terminal execution is now first-class and auditable
@@ -43,16 +43,16 @@ Validated by tests:
- cwd is sticky across calls unless explicitly overridden
- reset clears terminal state
This gives AIRI a deterministic execution path for many developer workflows without relying on Terminal.app scripting.
This gives Moeka a deterministic execution path for many developer workflows without relying on Terminal.app scripting.
### 4. Native approval now sits in the AIRI desktop layer
### 4. Native approval now sits in the Moeka desktop layer
Validated by implementation shape:
- MCP still returns `approval_required`
- AIRI renderer intercepts `computer_use` pending actions
- Moeka renderer intercepts `computer_use` pending actions
- Electron main shows a native approval dialog
- AIRI automatically follows up with approve/reject tool calls
- Moeka automatically follows up with approve/reject tool calls
- session-scoped approval reuse is limited to terminal and app open/focus actions
That keeps approval as a user action, not a model action.
@@ -88,16 +88,16 @@ Explicit non-goals of this pass:
## Commands Verified In This Checkout
- `pnpm -F @proj-airi/computer-use-mcp typecheck`
- `pnpm -F @proj-airi/computer-use-mcp test`
- `pnpm -F @proj-airi/stage-ui typecheck`
- `pnpm -F @proj-airi/stage-tamagotchi typecheck`
- `bun run --filter @proj-airi/computer-use-mcp typecheck`
- `bun run --filter @proj-airi/computer-use-mcp test`
- `bun run --filter @proj-airi/stage-ui typecheck`
- `bun run --filter @proj-airi/stage-web typecheck`
## Practical Interpretation
The feature is now credible as:
- a macOS desktop orchestration layer for AIRI
- a macOS desktop orchestration layer for Moeka
- a way to connect chat, MCP, terminal execution, and UI observation into one task flow
- a safer incremental path than trying to solve generic pure-vision computer use first
+40 -40
View File
@@ -1,10 +1,10 @@
# computer-use-mcp
AIRI-specific macOS desktop orchestration MCP service.
Moeka-specific macOS desktop orchestration MCP service.
## Why This Exists
This package exists because AIRI already has many useful pieces in the monorepo —
This package exists because Moeka already has many useful pieces in the monorepo —
providers, chat UX, MCP attachment, desktop app surfaces, browser integrations,
tool bridges, and workflow-related logic — but those pieces are still too easy to
use as isolated features instead of one coherent agent system.
@@ -12,7 +12,7 @@ use as isolated features instead of one coherent agent system.
`computer-use-mcp` is the missing execution substrate for that gap.
The current goal is not to add "another computer use demo". The goal is to give
AIRI a unified way to:
Moeka a unified way to:
- observe the current desktop or browser state
- choose the right execution surface for the task
@@ -22,7 +22,7 @@ AIRI a unified way to:
In short:
- AIRI remains the control plane and agent shell
- Moeka remains the control plane and agent shell
- `computer-use-mcp` is the local execution and workflow substrate
- the value is in orchestration, not in cursor movement by itself
@@ -31,7 +31,7 @@ In short:
This package is no longer positioned as a generic remote computer-use experiment.
The current v1 shape is:
- AIRI keeps the control plane:
- Moeka keeps the control plane:
- MCP tool surface
- approval queue protocol
- audit log
@@ -43,14 +43,14 @@ The current v1 shape is:
- app open/focus
- mouse/keyboard injection
- background terminal command execution
- AIRI desktop adds a native approval adapter:
- Moeka desktop adds a native approval adapter:
- `approval_required` still comes from MCP
- Electron shows a native dialog
- AIRI automatically calls approve/reject on the user's behalf
- Moeka automatically calls approve/reject on the user's behalf
The intended story is:
- AIRI uses tools first
- Moeka uses tools first
- visual observation is supplementary, not the primary execution path
- terminal commands are executed by a background shell runner, not by scripting Terminal tabs
- desktop/Electron/native apps and browser DOM are treated as different execution surfaces
@@ -65,9 +65,9 @@ What makes it different:
- it keeps action policy, approval, trace history, and audit output per run
- it distinguishes between desktop control and browser DOM control instead of forcing everything through blind clicks
- it prefers deterministic execution paths (`terminal_exec`, workflows, `browser_dom_*`) before raw coordinate actions
- it is designed to be called by AIRI automatically as part of a task flow, not merely driven by a human demo operator
- it is designed to be called by Moeka automatically as part of a task flow, not merely driven by a human demo operator
That means the package is useful only when it helps AIRI turn scattered local
That means the package is useful only when it helps Moeka turn scattered local
capabilities into one observable, controllable task system.
## Current Executor Modes
@@ -142,7 +142,7 @@ Workflow orchestration:
- `workflow_open_workspace`
- reveals a workspace in Finder and opens it in the configured IDE
- `workflow_validate_workspace`
- opens the workspace, confirms `pwd`, inspects local changes, and runs a validation command such as `pnpm typecheck`
- opens the workspace, confirms `pwd`, inspects local changes, and runs a validation command such as `bun run typecheck`
- `workflow_run_tests`
- runs a test command from the workspace root
- `workflow_inspect_failure`
@@ -160,7 +160,7 @@ The current macOS v1 boundary is intentionally narrow and explicit:
- `allowApps` is not used as a hard gate for click/type/scroll
- `denyApps` still blocks sensitive foreground apps
- `COMPUTER_USE_OPENABLE_APPS` only gates `desktop_open_app` and `desktop_focus_app`
- AIRI itself is in the default deny list to avoid self-operation
- Moeka itself is in the default deny list to avoid self-operation
- terminal commands always require approval
- app open/focus always require approval
- click/type/press/scroll still use per-action approval
@@ -186,7 +186,7 @@ macOS orchestration:
- `COMPUTER_USE_OPENABLE_APPS`
- default `Terminal,Cursor,Google Chrome`
- `COMPUTER_USE_DENY_APPS`
- default includes `1Password`, `Keychain`, `System Settings`, `Activity Monitor`, `AIRI`
- default includes `1Password`, `Keychain`, `System Settings`, `Activity Monitor`, `Moeka`
- `COMPUTER_USE_DENY_WINDOW_TITLES`
- `COMPUTER_USE_TERMINAL_SHELL`
- default current shell, otherwise `/bin/zsh`
@@ -231,16 +231,16 @@ Binary overrides:
- `COMPUTER_USE_SSH_BINARY`
- `COMPUTER_USE_TAR_BINARY`
## AIRI Integration
## Moeka Integration
AIRI still connects through `mcp.json`.
Moeka still connects through `mcp.json`.
Example local macOS entry:
```json
{
"mcpServers": {
"computer_use": {
"command": "pnpm",
"command": "bun",
"args": [
"-F",
"@proj-airi/computer-use-mcp",
@@ -257,19 +257,19 @@ Example local macOS entry:
}
```
On the AIRI desktop side, approvals are handled like this:
On the Moeka desktop side, approvals are handled like this:
1. model calls a `computer_use::*` tool
2. MCP returns `approval_required`
3. Electron shows a native approval dialog
4. AIRI automatically calls `desktop_approve_pending_action` or `desktop_reject_pending_action`
4. Moeka automatically calls `desktop_approve_pending_action` or `desktop_reject_pending_action`
5. terminal/app approvals can be reused for the current run only
For browser DOM automation, `computer-use-mcp` also exposes a local WebSocket bridge that matches the user's Chrome extension bridge pattern:
1. `computer-use-mcp` listens on `ws://127.0.0.1:8765` by default
2. the unpacked browser extension background service worker connects to that socket
3. AIRI can then call `browser_dom_*` MCP tools against the active browser tab
3. Moeka can then call `browser_dom_*` MCP tools against the active browser tab
If you override `COMPUTER_USE_BROWSER_DOM_BRIDGE_HOST` or
`COMPUTER_USE_BROWSER_DOM_BRIDGE_PORT`, mirror the same endpoint in the Chrome
@@ -278,23 +278,23 @@ so the background worker reconnects to the correct socket.
Use the two surfaces differently:
- `desktop_*` for AIRI itself, native macOS apps, Electron windows, Finder, Terminal, VS Code
- `desktop_*` for Moeka itself, native macOS apps, Electron windows, Finder, Terminal, VS Code
- `browser_dom_*` for real browser pages, cross-frame DOM reads, form filling, selector-based interaction, and iframe-heavy flows
- `browser_agent_run` for goal-driven browser tasks where AIRI should delegate the web exploration loop instead of manually hard-coding each browser step
- `browser_agent_run` for goal-driven browser tasks where Moeka should delegate the web exploration loop instead of manually hard-coding each browser step
## Validation Commands
- `pnpm -F @proj-airi/computer-use-mcp typecheck`
- `pnpm -F @proj-airi/computer-use-mcp test`
- `pnpm -F @proj-airi/computer-use-mcp smoke:stdio`
- `pnpm -F @proj-airi/computer-use-mcp smoke:macos`
- `pnpm -F @proj-airi/computer-use-mcp e2e:airi-chat`
- `pnpm -F @proj-airi/computer-use-mcp e2e:airi-discord`
- `bun run --filter @proj-airi/computer-use-mcp typecheck`
- `bun run --filter @proj-airi/computer-use-mcp test`
- `bun run --filter @proj-airi/computer-use-mcp smoke:stdio`
- `bun run --filter @proj-airi/computer-use-mcp smoke:macos`
- `bun run --filter @proj-airi/computer-use-mcp e2e:airi-chat`
- `bun run --filter @proj-airi/computer-use-mcp e2e:airi-discord`
Legacy remote validation remains available:
- `pnpm -F @proj-airi/computer-use-mcp bootstrap:remote`
- `pnpm -F @proj-airi/computer-use-mcp smoke:remote`
- `bun run --filter @proj-airi/computer-use-mcp bootstrap:remote`
- `bun run --filter @proj-airi/computer-use-mcp smoke:remote`
## Demo Story To Record
@@ -303,9 +303,9 @@ runner instead of a flashy cursor dance.
Recommended recording structure:
1. Show the AIRI desktop window, a terminal, and the generated report directory.
2. Start the local AIRI desktop app and the `computer-use-mcp` service.
3. Show that AIRI can call the MCP tools automatically instead of only listing them.
1. Show the Moeka desktop window, a terminal, and the generated report directory.
2. Start the local Moeka desktop app and the `computer-use-mcp` service.
3. Show that Moeka can call the MCP tools automatically instead of only listing them.
4. Demonstrate one short task that exercises the full loop:
- observe state
- execute a tool or workflow
@@ -315,27 +315,27 @@ Recommended recording structure:
Good first demos:
- open a workspace, confirm `pwd`, inspect local changes, and run `pnpm typecheck`
- open a workspace, confirm `pwd`, inspect local changes, and run `bun run typecheck`
- create and run a Python hello-world project through `terminal_exec`
- use desktop control for AIRI or native apps and use `browser_dom_*` only when the task truly moves into a browser page
- use desktop control for Moeka or native apps and use `browser_dom_*` only when the task truly moves into a browser page
### Discord integration demo
For a management-readable AIRI demo, the Discord settings flow is more representative than a generic hello-world reply:
For a management-readable Moeka demo, the Discord settings flow is more representative than a generic hello-world reply:
1. start AIRI desktop and `integrations/discord-bot`
1. start Moeka desktop and `integrations/discord-bot`
2. open `/settings/modules/messaging-discord`
3. enable the module and save settings
4. verify that the Discord bot receives the forwarded config from AIRI and reconnects itself
4. verify that the Discord bot receives the forwarded config from Moeka and reconnects itself
5. finish by opening `report.json`, screenshots, audit log, and `discord-bot.log`
Notes:
- for a pure local-secret run, set `AIRI_E2E_DISCORD_TOKEN`
- for a more agentic run, set `AIRI_E2E_DISCORD_TOKEN_SOURCE=portal` or `auto` and let AIRI retrieve the token from the live browser / Discord Developer Portal session
- `clipboard_read_text` / `clipboard_write_text` are the intended bridge when AIRI must move a copied token from the browser back into AIRI settings
- for a more agentic run, set `AIRI_E2E_DISCORD_TOKEN_SOURCE=portal` or `auto` and let Moeka retrieve the token from the live browser / Discord Developer Portal session
- `clipboard_read_text` / `clipboard_write_text` are the intended bridge when Moeka must move a copied token from the browser back into Moeka settings
- the observable harness keeps the token out of the desktop audit trail by applying the secret through the renderer instead of typing it through Quartz key events
- if you only want to validate the AIRI → Discord bot configuration plumbing without a real token, set `AIRI_E2E_DISCORD_ALLOW_LOGIN_FAILURE=true`
- if you only want to validate the Moeka → Discord bot configuration plumbing without a real token, set `AIRI_E2E_DISCORD_ALLOW_LOGIN_FAILURE=true`
Less convincing demos:
@@ -1,8 +1,8 @@
# AIRI Coding CLI + Chafa Architecture
# Moeka Coding CLI + Chafa Architecture
## Purpose
Define a narrow architecture for a terminal-facing AIRI coding CLI that can
Define a narrow architecture for a terminal-facing Moeka coding CLI that can
optionally render an animated avatar through `chafa`, without coupling terminal
presentation to the `computer-use-mcp` runtime.
@@ -290,4 +290,4 @@ Scope:
- `feat(cli): define coding runner event contract`
- `feat(cli): scaffold airi coding cli text renderer`
- `feat(cli): add optional terminal AIRI avatar renderer with chafa`
- `feat(cli): add optional terminal Moeka avatar renderer with chafa`
@@ -1,6 +1,6 @@
# AIRI Desktop Grounding — Chrome Extension
# Moeka Desktop Grounding — Chrome Extension
Read-only Chrome DOM observation bridge for the AIRI Desktop Grounding layer.
Read-only Chrome DOM observation bridge for the Moeka Desktop Grounding layer.
## What it does
@@ -1,12 +1,12 @@
/**
* background.js — MV3 Service Worker for AIRI Desktop Grounding
* background.js — MV3 Service Worker for Moeka Desktop Grounding
*
* Routes commands from the AIRI extension bridge → chrome.tabs.sendMessage
* Routes commands from the Moeka extension bridge → chrome.tabs.sendMessage
* → msg_bridge.js → content.js (__AIRI_DG__)
*
* IMPORTANT: This background does NOT use offscreen documents or Python bridges.
* It receives commands directly from the existing BrowserDomExtensionBridge
* WebSocket connection in the AIRI computer-use-mcp service.
* WebSocket connection in the Moeka computer-use-mcp service.
*
* Only read-only observation commands are supported.
* All DOM-mutating actions (click, type, hover, scroll) have been removed
@@ -397,10 +397,10 @@ async function readAllFramesDOMWithOffsets(tabId, frameIds, opts) {
})
}
// ---- Handle external commands (from AIRI extension bridge) ----
// ---- Handle external commands (from Moeka extension bridge) ----
/**
* Handle a command from the AIRI BrowserDomExtensionBridge.
* Handle a command from the Moeka BrowserDomExtensionBridge.
*
* Only read-only observation commands are supported:
* - getActiveTab: get the active tab info
@@ -564,7 +564,7 @@ async function handleCommand(cmd) {
}
// ---- Listen for external messages ----
// The AIRI BrowserDomExtensionBridge connects via chrome.runtime.onMessageExternal
// The Moeka BrowserDomExtensionBridge connects via chrome.runtime.onMessageExternal
// or through the existing WebSocket bridge mechanism
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
@@ -1,5 +1,5 @@
/**
* content.js AIRI Desktop Grounding: read-only DOM observation
* content.js Moeka Desktop Grounding: read-only DOM observation
*
* Injected into every frame (including cross-origin iframes) in the MAIN world.
* Namespace: window.__AIRI_DG__
@@ -1,8 +1,8 @@
{
"manifest_version": 3,
"name": "AIRI Desktop Grounding Bridge",
"name": "Moeka Desktop Grounding Bridge",
"version": "1.0.0",
"description": "Read-only DOM observation bridge for AIRI desktop grounding (no DOM mutation, no eval)",
"description": "Read-only DOM observation bridge for Moeka desktop grounding (no DOM mutation, no eval)",
"permissions": [
"activeTab",
@@ -10,7 +10,7 @@ tools, schema changes, or a `plast-mem` dependency.
`computer-use-mcp` owns current-run coding execution memory and may later expose
a governed local reviewed-coding-memory export boundary. It must not become
AIRI's long-term memory service.
Moeka's long-term memory service.
`plast-mem` owns project-level long-term memory: conversation ingestion,
episodic memory, semantic consolidation, retrieval, and memory review/decay
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AIRI Computer Use Text Target</title>
<title>Moeka Computer Use Text Target</title>
<style>
:root {
color-scheme: light;
@@ -53,7 +53,7 @@
</head>
<body>
<main>
<h1>AIRI Text Target</h1>
<h1>Moeka Text Target</h1>
<p>Use this page as the low-noise fallback target when TextEdit is unavailable inside the VM.</p>
<textarea autofocus spellcheck="false">Click inside this textarea and type the validation sentence here.</textarea>
</main>
@@ -228,7 +228,7 @@ Chika should not own these in the first slice:
- Windows support
- terminal coding workflow integration
- model deployment
- product claims that AIRI has a trained computer-use model
- product claims that Moeka has a trained computer-use model
## Non-Goals
@@ -239,7 +239,7 @@ Chika should not own these in the first slice:
- No model deployment.
- No MCP schema changes unless strictly necessary for trace serialization.
- No desktop/chafa/CLI pet integration.
- No product claim that AIRI has a trained computer-use model.
- No product claim that Moeka has a trained computer-use model.
## Reminder Trigger
+3 -8
View File
@@ -3,9 +3,9 @@
"type": "module",
"version": "0.12.0-beta.5",
"private": true,
"description": "AIRI-specific macOS desktop orchestration MCP service",
"description": "Moeka-specific macOS desktop orchestration MCP service",
"author": {
"name": "Moeru AI Project AIRI Team",
"name": "Moeka Team",
"email": "airi@moeru.ai",
"url": "https://github.com/moeru-ai"
},
@@ -28,11 +28,6 @@
"scripts": {
"dev": "tsx ./src/bin/run.ts",
"start": "tsx ./src/bin/run.ts",
"e2e:airi-chat": "tsx ./src/bin/e2e-airi-chat-observable.ts",
"e2e:airi-chat-terminal-self-acquire": "tsx ./src/bin/e2e-airi-chat-terminal-self-acquire.ts",
"demo:terminal-self-acquire": "tsx ./src/bin/e2e-airi-chat-terminal-self-acquire.ts",
"e2e:airi-discord": "tsx ./src/bin/e2e-airi-discord-observable.ts",
"e2e:airi-discord-agentic": "tsx ./src/bin/e2e-airi-discord-agentic.ts",
"e2e:developer-workflow": "tsx ./src/bin/e2e-developer-workflow.ts",
"e2e:browser-reroute": "tsx ./src/bin/e2e-browser-reroute.ts",
"e2e:terminal-exec": "tsx ./src/bin/e2e-terminal-exec.ts",
@@ -44,7 +39,7 @@
"smoke:macos": "tsx ./src/bin/smoke-macos.ts",
"smoke:desktop-v3": "tsx ./src/bin/smoke-chrome-grounding.ts",
"smoke:workflow": "tsx ./src/bin/smoke-workflow.ts",
"mcp:inspector": "pnpx @modelcontextprotocol/inspector pnpm -F @proj-airi/computer-use-mcp start",
"mcp:inspector": "bunx @modelcontextprotocol/inspector bun run --filter @proj-airi/computer-use-mcp start",
"build": "tsdown",
"typecheck": "tsc --noEmit",
"test": "vitest run --config ./vitest.config.ts"
@@ -22,7 +22,7 @@ Current repo facts:
The Planning Orchestration Layer is a future layer above individual lanes:
```text
AIRI Host / User Goal
Moeka Host / User Goal
-> Planning Orchestration Layer
-> Lane Router
-> coding / desktop / browser_dom / terminal / human lanes
@@ -13,7 +13,7 @@ const remoteInstallDir = normalizeRemoteShellPath(env.COMPUTER_USE_REMOTE_INSTAL
const remoteRunnerPath = normalizeRemoteShellPath(env.COMPUTER_USE_REMOTE_RUNNER_COMMAND?.trim() || '$HOME/.local/bin/airi-desktop-runner')
async function buildLocalBundle() {
await runProcess('pnpm', ['build'], {
await runProcess('bun', ['run', 'build'], {
cwd: packageDir,
timeoutMs: 180_000,
env: process.env,
@@ -18,7 +18,7 @@ async function main() {
console.info('🚀 Starting computer-use-mcp server …')
const transport = new StdioClientTransport({
command: 'pnpm',
command: 'bun',
args: ['start'],
cwd: packageDir,
env: {
@@ -75,11 +75,11 @@ async function main() {
arguments: {
command: `cat > "$HOME/hello-python-project/main.py" << 'PYEOF'
#!/usr/bin/env python3
"""Hello World project — created by AIRI computer-use-mcp"""
"""Hello World project — created by Moeka computer-use-mcp"""
def main():
print("Hello World! 🌍")
print("This project was created by AIRI computer-use-mcp terminal_exec tool.")
print("This project was created by Moeka computer-use-mcp terminal_exec tool.")
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -8,7 +8,7 @@
* stable accessibility path rather than a guaranteed browser surface.
*
* Usage:
* pnpm -F @proj-airi/computer-use-mcp exec tsx ./src/bin/e2e-browser-reroute.ts
* bun run --filter @proj-airi/computer-use-mcp e2e:browser-reroute
*/
import { dirname, resolve } from 'node:path'
@@ -42,7 +42,7 @@ function requireStructuredContent(result: unknown, label: string): Record<string
}
async function createClient(): Promise<Client> {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = (env.COMPUTER_USE_SMOKE_SERVER_ARGS || 'start').split(WHITESPACE_SPLIT_RE).filter(Boolean)
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -8,7 +8,7 @@
* and the chain must propagate the same projectPath end-to-end.
*
* Usage:
* pnpm -F @proj-airi/computer-use-mcp exec tsx ./src/bin/e2e-developer-workflow.ts
* bun run --filter @proj-airi/computer-use-mcp e2e:developer-workflow
*/
import { mkdtempSync, writeFileSync } from 'node:fs'
@@ -51,7 +51,7 @@ function createProjectDir(): string {
}
async function createClient(): Promise<Client> {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = (env.COMPUTER_USE_SMOKE_SERVER_ARGS || 'start').split(WHITESPACE_SPLIT_RE).filter(Boolean)
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -13,7 +13,7 @@
* `createLocalShellRunner` backed by `child_process.spawn`.
*
* Usage:
* pnpm -F @proj-airi/computer-use-mcp e2e:terminal-exec
* bun run --filter @proj-airi/computer-use-mcp e2e:terminal-exec
*/
import { mkdtempSync, writeFileSync } from 'node:fs'
@@ -56,7 +56,7 @@ function createProjectDir(): string {
}
async function createClient(): Promise<Client> {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = (env.COMPUTER_USE_SMOKE_SERVER_ARGS || 'start').split(WHITESPACE_SPLIT_RE).filter(Boolean)
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -15,7 +15,7 @@
* Node.js process on the host machine.
*
* Usage:
* pnpm -F @proj-airi/computer-use-mcp e2e:terminal-pty
* bun run --filter @proj-airi/computer-use-mcp e2e:terminal-pty
*/
import { dirname, resolve } from 'node:path'
@@ -51,7 +51,7 @@ function requireStructuredContent(result: unknown, label: string): Record<string
}
async function createClient(): Promise<Client> {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = (env.COMPUTER_USE_SMOKE_SERVER_ARGS || 'start').split(WHITESPACE_SPLIT_RE).filter(Boolean)
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -20,7 +20,7 @@
* NOTE: No pre-created PTY. The workflow self-acquires.
*
* Usage:
* pnpm -F @proj-airi/computer-use-mcp e2e:terminal-self-acquire
* bun run --filter @proj-airi/computer-use-mcp e2e:terminal-self-acquire
*/
import { mkdtempSync, writeFileSync } from 'node:fs'
@@ -63,7 +63,7 @@ function createProjectDir(): string {
}
async function createClient(): Promise<Client> {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = (env.COMPUTER_USE_SMOKE_SERVER_ARGS || 'start').split(WHITESPACE_SPLIT_RE).filter(Boolean)
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -91,7 +91,7 @@ describe('smoke-chrome-grounding helpers', () => {
id: 't_2',
source: 'chrome_dom',
role: 'AXButton',
label: 'AIRI Desktop V3 Smoke Button',
label: 'Moeka Desktop V3 Smoke Button',
interactable: true,
},
],
@@ -132,7 +132,7 @@ describe('smoke-chrome-grounding helpers', () => {
},
}
expect(() => selectDesktopV3SmokeCandidate(runState)).toThrow('desktop_observe did not return the AIRI Desktop V3 Smoke Button chrome_dom candidate')
expect(() => selectDesktopV3SmokeCandidate(runState)).toThrow('desktop_observe did not return the Moeka Desktop V3 Smoke Button chrome_dom candidate')
})
it('locks pre-click and post-click overlay state shape', () => {
@@ -8,7 +8,7 @@
* 4. `desktop_click_target` updates pointer intent and clicked-candidate state.
*
* Usage:
* pnpm -F @proj-airi/computer-use-mcp smoke:desktop-v3
* bun run --filter @proj-airi/computer-use-mcp smoke:desktop-v3
*/
import { dirname, resolve } from 'node:path'
@@ -20,20 +20,20 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), '../..')
const WHITESPACE_SPLIT_RE = /\s+/u
const SMOKE_TARGET_LABEL = 'AIRI Desktop V3 Smoke Button'
const SMOKE_TARGET_LABEL = 'Moeka Desktop V3 Smoke Button'
const DEFAULT_SMOKE_URL = `data:text/html;charset=utf-8,${encodeURIComponent(`<!doctype html>
<html>
<head>
<title>AIRI Desktop V3 Smoke</title>
<title>Moeka Desktop V3 Smoke</title>
<style>
body { font-family: sans-serif; padding: 48px; }
button { font-size: 18px; padding: 12px 18px; }
</style>
</head>
<body>
<h1>AIRI Desktop V3 Smoke</h1>
<button id="airi-desktop-v3-smoke-button">AIRI Desktop V3 Smoke Button</button>
<h1>Moeka Desktop V3 Smoke</h1>
<button id="airi-desktop-v3-smoke-button">Moeka Desktop V3 Smoke Button</button>
</body>
</html>`)}`
@@ -146,7 +146,7 @@ export function selectDesktopV3SmokeCandidate(
if (!selected) {
if (!requested) {
throw new Error('desktop_observe did not return the AIRI Desktop V3 Smoke Button chrome_dom candidate')
throw new Error('desktop_observe did not return the Moeka Desktop V3 Smoke Button chrome_dom candidate')
}
throw new Error(`desktop_observe did not return requested candidate "${requested}"`)
}
@@ -324,7 +324,7 @@ function delay(ms: number): Promise<void> {
}
export async function runDesktopV3Smoke(): Promise<Record<string, unknown>> {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = parseCommandArgs(env.COMPUTER_USE_SMOKE_SERVER_ARGS, ['start'])
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
const smokeUrl = env.COMPUTER_USE_DESKTOP_V3_SMOKE_URL?.trim() || DEFAULT_SMOKE_URL
@@ -475,7 +475,7 @@ const invokedPath = argv[1] ? pathToFileURL(argv[1]).href : undefined
if (invokedPath === import.meta.url) {
if (argv.includes('--help') || argv.includes('-h')) {
console.info(`Usage:
pnpm -F @proj-airi/computer-use-mcp smoke:desktop-v3
bun run --filter @proj-airi/computer-use-mcp smoke:desktop-v3
Environment:
COMPUTER_USE_DESKTOP_V3_SMOKE_URL Target URL. Defaults to an inline data: smoke page.
@@ -52,7 +52,7 @@ async function approveFirstPending(client: Client, expectedToolName: string) {
}
async function main() {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = parseCommandArgs(env.COMPUTER_USE_SMOKE_SERVER_ARGS, ['start'])
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -60,7 +60,7 @@ async function approveFirstPending(client: Client, label: string) {
}
async function main() {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = parseCommandArgs(env.COMPUTER_USE_SMOKE_SERVER_ARGS, ['start'])
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -159,7 +159,7 @@ async function main() {
const typeText = await client.callTool({
name: 'desktop_type_text',
arguments: {
text: 'AIRI remote linux-x11 smoke',
text: 'Moeka remote linux-x11 smoke',
pressEnter: false,
captureAfter: true,
},
@@ -55,7 +55,7 @@ function hasImageContent(result: unknown) {
}
async function main() {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = parseCommandArgs(env.COMPUTER_USE_SMOKE_SERVER_ARGS, ['start'])
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -12,7 +12,7 @@
* policy / action-executor pipeline.
*
* Usage:
* pnpm -F @proj-airi/computer-use-mcp exec tsx ./src/bin/smoke-workflow.ts
* bun run --filter @proj-airi/computer-use-mcp smoke:workflow
*/
import { mkdtempSync, writeFileSync } from 'node:fs'
@@ -57,7 +57,7 @@ function createSmokeProjectDir() {
}
async function createClient(overrides: Record<string, string> = {}): Promise<Client> {
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'pnpm'
const command = env.COMPUTER_USE_SMOKE_SERVER_COMMAND?.trim() || 'bun'
const args = (env.COMPUTER_USE_SMOKE_SERVER_ARGS || 'start').split(WHITESPACE_SPLIT_RE).filter(Boolean)
const cwd = env.COMPUTER_USE_SMOKE_SERVER_CWD?.trim() || packageDir
@@ -94,14 +94,14 @@ describe('browserDomExtensionBridge', () => {
client = result.client
mockClientAction(client, 'getActiveTab', () => ({
title: 'AIRI Demo Tab',
title: 'Moeka Demo Tab',
url: 'https://example.com/demo',
}))
const activeTab = await bridge.getActiveTab()
expect(activeTab).toEqual({
title: 'AIRI Demo Tab',
title: 'Moeka Demo Tab',
url: 'https://example.com/demo',
})
expect(bridge.getStatus().connected).toBe(true)
@@ -26,14 +26,14 @@ function makeAxSnapshot(): AXSnapshot {
const root = {
uid: 'root',
role: 'AXApplication',
title: 'AIRI',
title: 'Moeka',
children: [],
}
return {
snapshotId: 'ax_1',
pid: 123,
appName: 'AIRI',
appName: 'Moeka',
root,
uidToNode: new Map([['root', root]]),
capturedAt: new Date().toISOString(),
@@ -65,11 +65,11 @@ describe('captureDesktopGrounding', () => {
})
const genericObservation: WindowObservation = {
frontmostAppName: 'AIRI',
frontmostAppName: 'Moeka',
windows: [
{
id: 'airi:1',
appName: 'AIRI',
appName: 'Moeka',
title: 'Shared Title',
bounds: { x: 10, y: 20, width: 1200, height: 800 },
},
@@ -43,7 +43,7 @@ export interface MultiDisplaySnapshot {
}
/**
* Resolved position metadata for a point in AIRI's desktop coordinate space.
* Resolved position metadata for a point in Moeka's desktop coordinate space.
*
* The `global` coordinate is the unscaled logical point that macOS input events
* should receive. `local` and `backingPixel` are diagnostics for display-aware
@@ -129,7 +129,7 @@ export function toDisplayLocalCoord(
* - Recording display-local and backing-pixel diagnostics
*
* Expects:
* - `snapshot` uses AIRI's top-left global logical coordinate space
* - `snapshot` uses Moeka's top-left global logical coordinate space
* - `x` and `y` are not pre-scaled by Retina backing factor
*
* Returns:
@@ -20,7 +20,7 @@ describe('hasCompletedChatTurn', () => {
expect(hasCompletedChatTurn({
chat: {
lastTurnComplete: {
outputText: 'hello from AIRI',
outputText: 'hello from Moeka',
toolCallCount: 0,
toolResultCount: 0,
},
@@ -11,7 +11,7 @@ interface ChatTurnSnapshotLike {
}
}
// NOTICE: AIRI can legitimately finish a turn with tool calls/results but no
// NOTICE: Moeka can legitimately finish a turn with tool calls/results but no
// natural-language assistant text. Treat that as a completed turn so E2E
// harnesses do not hang forever waiting for output that will never arrive.
export function hasCompletedChatTurn(snapshot: ChatTurnSnapshotLike) {
@@ -34,7 +34,7 @@ describe('debug target helpers', () => {
}))).toBe(true)
})
it('prioritizes chat and main AIRI surfaces ahead of generic pages', () => {
it('prioritizes chat and main Moeka surfaces ahead of generic pages', () => {
const targets = prioritizeInspectableAiriTargets([
createTarget({
id: 'generic',
@@ -43,7 +43,7 @@ describe('debug target helpers', () => {
}),
createTarget({
id: 'main',
title: 'AIRI',
title: 'Moeka',
url: 'http://localhost:5173/',
}),
createTarget({
@@ -56,21 +56,21 @@ describe('debug target helpers', () => {
expect(targets.map(target => target.id)).toEqual(['chat', 'main', 'generic'])
})
it('detects chat surfaces from either target metadata or AIRI debug snapshots', () => {
it('detects chat surfaces from either target metadata or Moeka debug snapshots', () => {
expect(isChatSurfaceTarget(createTarget({
title: 'Chat',
url: 'http://localhost:5173/',
}))).toBe(true)
expect(isChatSurfaceTarget(createTarget({
title: 'AIRI',
title: 'Moeka',
url: 'http://localhost:5173/',
}), {
route: '#/chat',
})).toBe(true)
expect(isChatSurfaceTarget(createTarget({
title: 'AIRI',
title: 'Moeka',
url: 'http://localhost:5173/',
}), {
route: '#/',
@@ -16,7 +16,7 @@ function containsFragment(input: string, fragments: string[]) {
}
// NOTICE: The remote debug endpoint exposes multiple renderer pages for Electron.
// We only want actual AIRI app pages here; helper pages like beat-sync and devtools
// We only want actual Moeka app pages here; helper pages like beat-sync and devtools
// will never expose `window.__AIRI_DEBUG__` and should be deprioritized early.
export function isInspectableAiriRendererTarget(target: DebugTargetLike) {
if (target.type !== 'page') {
@@ -47,7 +47,7 @@ function scoreInspectableAiriTarget(target: DebugTargetLike) {
if (target.title === 'Chat') {
score += 40
}
else if (target.title === 'AIRI') {
else if (target.title === 'Moeka') {
score += 20
}
+1 -1
View File
@@ -125,7 +125,7 @@ describe('evaluateActionPolicy', () => {
config: baseConfig,
context: {
available: true,
appName: 'AIRI',
appName: 'Moeka',
platform: 'darwin',
},
operationsExecuted: 0,
@@ -54,7 +54,7 @@ export function resolveLaunchContext(config: ComputerUseConfig): LaunchContext {
* - Preserving legacy main-display fields while exposing multi-display bounds
*
* Expects:
* - `snapshot` uses AIRI's top-left global logical coordinate space
* - `snapshot` uses Moeka's top-left global logical coordinate space
*
* Returns:
* - DisplayInfo with legacy main-display fields and complete display list
+1 -1
View File
@@ -27,7 +27,7 @@ export async function createComputerUseMcpServer(config = resolveComputerUseConf
const runtime = await createRuntime(config, options)
const executeAction = createExecuteAction(runtime)
const server = new McpServer({
name: 'AIRI Computer Use',
name: 'Moeka Computer Use',
version: packageVersion,
})
@@ -221,7 +221,7 @@ describe('createExecuteAction', () => {
})
executor.getForegroundContext.mockResolvedValue({
available: true,
appName: 'AIRI',
appName: 'Moeka',
platform: 'darwin',
})
session.createPendingAction.mockReturnValue({
@@ -301,7 +301,7 @@ describe('createExecuteAction', () => {
desktopSessionController.ensureControlledAppInForeground.mockResolvedValue(true)
executor.getForegroundContext.mockResolvedValue({
available: true,
appName: 'AIRI',
appName: 'Moeka',
platform: 'darwin',
})
@@ -310,7 +310,7 @@ describe('createExecuteAction', () => {
expect(result.isError).not.toBe(true)
expect(desktopSessionController.ensureControlledAppInForeground).toHaveBeenCalledWith(expect.objectContaining({
currentForeground: expect.objectContaining({ appName: 'AIRI' }),
currentForeground: expect.objectContaining({ appName: 'Moeka' }),
}))
expect(executor.click).toHaveBeenCalledOnce()
expect(session.record).toHaveBeenCalledWith(expect.objectContaining({
@@ -321,7 +321,7 @@ describe('createExecuteAction', () => {
expect(session.record).toHaveBeenCalledWith(expect.objectContaining({
event: 'requested',
result: expect.objectContaining({
actualForegroundContext: expect.objectContaining({ appName: 'AIRI' }),
actualForegroundContext: expect.objectContaining({ appName: 'Moeka' }),
}),
}))
})
@@ -365,7 +365,7 @@ describe('createExecuteAction', () => {
})
executor.getForegroundContext.mockResolvedValue({
available: true,
appName: 'AIRI',
appName: 'Moeka',
platform: 'darwin',
})
@@ -421,7 +421,7 @@ describe('createExecuteAction', () => {
desktopSessionController.ensureControlledAppInForeground.mockResolvedValue(true)
executor.getForegroundContext.mockResolvedValue({
available: true,
appName: 'AIRI',
appName: 'Moeka',
platform: 'darwin',
})
@@ -493,7 +493,7 @@ describe('createExecuteAction', () => {
stateManager.updateGroundingSnapshot({
snapshotId: 'dg_1',
capturedAt: new Date().toISOString(),
foregroundApp: 'AIRI',
foregroundApp: 'Moeka',
windows: [],
screenshot: {
dataBase64: '',
@@ -505,7 +505,7 @@ describe('createExecuteAction', () => {
{
id: 't_0',
source: 'ax',
appName: 'AIRI',
appName: 'Moeka',
role: 'AXButton',
label: 'Submit',
bounds: { x: 100, y: 200, width: 80, height: 30 },
@@ -524,7 +524,7 @@ describe('createExecuteAction', () => {
})
executor.getForegroundContext.mockResolvedValue({
available: true,
appName: 'AIRI',
appName: 'Moeka',
platform: 'darwin',
})
@@ -575,7 +575,7 @@ describe('createExecuteAction', () => {
desktopSessionController.ensureControlledAppInForeground.mockRejectedValue(new Error('Chrome session unavailable'))
executor.getForegroundContext.mockResolvedValue({
available: true,
appName: 'AIRI',
appName: 'Moeka',
platform: 'darwin',
})
@@ -1220,7 +1220,7 @@ export function registerComputerUseTools(params: RegisterComputerUseToolsOptions
ideApp: z.string().optional().describe('IDE application to open the workspace with (default: Cursor)'),
fileManagerApp: z.string().optional().describe('File manager to reveal the workspace in (default: Finder)'),
changesCommand: z.string().optional().describe('Command to inspect local changes (default: git diff --stat)'),
checkCommand: z.string().optional().describe('Validation command to run from the workspace root (default: pnpm typecheck)'),
checkCommand: z.string().optional().describe('Validation command to run from the workspace root (default: bun run typecheck)'),
autoApprove: z.boolean().optional().describe('Skip per-step approval for workflow actions (default: true)'),
},
async ({ projectPath, ideApp, fileManagerApp, changesCommand, checkCommand, autoApprove }) => {
@@ -1252,7 +1252,7 @@ export function registerComputerUseTools(params: RegisterComputerUseToolsOptions
'workflow_run_tests',
{
projectPath: z.string().min(1).describe('Absolute path to the project directory'),
testCommand: z.string().optional().describe('Shell command to run tests (default: pnpm test:run)'),
testCommand: z.string().optional().describe('Shell command to run tests (default: bun run test:run)'),
autoApprove: z.boolean().optional().describe('Skip per-step approval for workflow actions (default: true)'),
},
async ({ projectPath, testCommand, autoApprove }) => {
@@ -118,12 +118,12 @@ describe('registerVscodeTools', () => {
registerVscodeTools({ server, runtime, executeTerminalCommand })
const result = await invoke('vscode_run_task', {
command: 'pnpm test',
command: 'bun run test',
cwd: '/tmp/project',
})
expect(executeTerminalCommand).toHaveBeenCalledWith({
command: 'pnpm test',
command: 'bun run test',
cwd: '/tmp/project',
timeoutMs: 60_000,
}, 'vscode_run_task')
@@ -133,7 +133,7 @@ describe('registerVscodeTools', () => {
it('parses problem output and writes diagnostics into run-state', async () => {
const executeTerminalCommand = vi.fn().mockResolvedValue(createExecutedTerminalResult({
command: 'pnpm typecheck 2>&1',
command: 'bun run typecheck 2>&1',
exitCode: 1,
effectiveCwd: '/tmp/project',
stdout: [
@@ -172,12 +172,12 @@ describe('registerVscodeTools', () => {
])
expect(runtime.stateManager.getState().vscode).toMatchObject({
lastTask: {
command: 'pnpm typecheck 2>&1',
command: 'bun run typecheck 2>&1',
cwd: '/tmp/project',
exitCode: 1,
},
lastProblems: {
command: 'pnpm typecheck 2>&1',
command: 'bun run typecheck 2>&1',
cwd: '/tmp/project',
problemCount: 2,
},
@@ -299,7 +299,7 @@ export function registerVscodeTools({ server, runtime, executeTerminalCommand }:
server.tool(
'vscode_run_task',
{
command: z.string().min(1).describe('Shell command to run (e.g. "pnpm typecheck", "pnpm test:run")'),
command: z.string().min(1).describe('Shell command to run (e.g. "bun run typecheck", "bun run test:run")'),
cwd: z.string().optional().describe('Working directory for the command'),
timeoutMs: z.number().int().min(1_000).max(300_000).optional().describe('Timeout in milliseconds (default: 60000)'),
},
@@ -358,11 +358,11 @@ export function registerVscodeTools({ server, runtime, executeTerminalCommand }:
'vscode_list_problems',
{
cwd: z.string().optional().describe('Project root to run diagnostics from'),
checkCommand: z.string().optional().describe('Diagnostic command (default: "pnpm typecheck 2>&1")'),
checkCommand: z.string().optional().describe('Diagnostic command (default: "bun run typecheck 2>&1")'),
maxLines: z.number().int().min(10).max(500).optional().describe('Maximum output lines to return (default: 200)'),
},
async ({ cwd, checkCommand, maxLines }) => {
const command = checkCommand ?? 'pnpm typecheck 2>&1'
const command = checkCommand ?? 'bun run typecheck 2>&1'
const limit = maxLines ?? 200
const terminal = await runTerminalCommand(executeTerminalCommand, {
command,
+3 -3
View File
@@ -33,7 +33,7 @@ describe('runStateManager', () => {
it('should track terminal results', () => {
const manager = new RunStateManager()
manager.updateTerminalResult({
command: 'pnpm test:run',
command: 'bun run test:run',
stdout: 'All tests passed',
stderr: '',
exitCode: 0,
@@ -71,7 +71,7 @@ describe('runStateManager', () => {
phase: 'executing' as const,
steps: [
{ index: 1, stepId: 'step_a', label: 'cd project' },
{ index: 2, stepId: 'step_b', label: 'pnpm test' },
{ index: 2, stepId: 'step_b', label: 'bun run test' },
],
currentStepIndex: 0,
startedAt: new Date().toISOString(),
@@ -86,7 +86,7 @@ describe('runStateManager', () => {
manager.completeCurrentStep('success')
expect(manager.getState().activeTask?.steps[0].outcome).toBe('success')
manager.advanceTaskStep({ index: 2, stepId: 'step_b', label: 'pnpm test' })
manager.advanceTaskStep({ index: 2, stepId: 'step_b', label: 'bun run test' })
manager.completeCurrentStep('failure', 'Tests failed')
expect(manager.getState().activeTask?.failureCount).toBe(1)
@@ -131,7 +131,7 @@ describe('evaluateStrategy', () => {
it('should advise read error when last terminal command failed', () => {
const state = createBaseState({
lastTerminalResult: {
command: 'pnpm test',
command: 'bun run test',
stdout: '',
stderr: 'Error: tests failed',
exitCode: 1,
@@ -141,7 +141,7 @@ describe('evaluateStrategy', () => {
},
})
const advisories = evaluateStrategy({
proposedAction: { kind: 'terminal_exec', input: { command: 'pnpm test' } },
proposedAction: { kind: 'terminal_exec', input: { command: 'bun run test' } },
state,
})
@@ -59,7 +59,7 @@ describe('support matrix', () => {
expect(entry).toBeDefined()
expect(entry?.lane).toBe('desktop-native')
expect(entry?.level).toBe('covered')
expect(entry?.smokeCommand).toBe('pnpm -F @proj-airi/computer-use-mcp smoke:desktop-v3')
expect(entry?.smokeCommand).toBe('bun run --filter @proj-airi/computer-use-mcp smoke:desktop-v3')
})
it('includes browser-dom route contract as covered, not product-supported', () => {
+25 -25
View File
@@ -37,11 +37,11 @@ export interface SupportMatrixEntry {
}
export const strictReleaseGateCommands = [
'pnpm -F @proj-airi/computer-use-mcp e2e:developer-workflow',
'pnpm -F @proj-airi/computer-use-mcp e2e:terminal-exec',
'pnpm -F @proj-airi/computer-use-mcp e2e:terminal-pty',
'pnpm -F @proj-airi/computer-use-mcp e2e:terminal-self-acquire',
'pnpm -F @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire',
'bun run --filter @proj-airi/computer-use-mcp e2e:developer-workflow',
'bun run --filter @proj-airi/computer-use-mcp e2e:terminal-exec',
'bun run --filter @proj-airi/computer-use-mcp e2e:terminal-pty',
'bun run --filter @proj-airi/computer-use-mcp e2e:terminal-self-acquire',
'bun run --filter @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire',
] as const
// ---------------------------------------------------------------------------
@@ -59,7 +59,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/workflows/engine.test.ts',
'src/server/workflow-formatter.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp e2e:developer-workflow',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp e2e:developer-workflow',
happyPath: 'open_workspace → validate_workspace → run_tests (e2e:developer-workflow, dry-run)',
},
{
@@ -71,7 +71,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/workflows/engine.test.ts',
'src/server/workflow-formatter.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp e2e:developer-workflow',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp e2e:developer-workflow',
happyPath: 'open_workspace → validate_workspace → run_tests (e2e:developer-workflow, dry-run)',
},
{
@@ -83,7 +83,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/workflows/engine.test.ts',
'src/server/workflow-formatter.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp e2e:developer-workflow',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp e2e:developer-workflow',
happyPath: 'open_workspace → validate_workspace → run_tests (e2e:developer-workflow, dry-run)',
},
{
@@ -92,7 +92,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
label: 'Inspect IDE failure panel via accessibility',
level: 'covered',
unitTests: ['src/workflows/engine.test.ts'],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:workflow',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:workflow',
},
{
lane: 'workflow',
@@ -100,7 +100,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
label: 'Resume paused workflow after approval',
level: 'covered',
unitTests: ['src/workflows/engine.test.ts'],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:workflow',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:workflow',
},
{
lane: 'workflow',
@@ -110,7 +110,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
unitTests: [
'src/server/workflow-formatter.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp e2e:browser-reroute',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp e2e:browser-reroute',
happyPath: 'workflow_browse_and_act → reroute detected → suggestedTool succeeds (secondary regression)',
},
@@ -124,7 +124,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/strategy.test.ts',
'src/server/workflow-formatter.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp e2e:browser-reroute',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp e2e:browser-reroute',
happyPath: 'Dual-stack browser selection is covered by strategy/formatter tests; secondary reroute regression remains surface-agnostic under dry-run.',
},
{
@@ -136,7 +136,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/strategy.test.ts',
'src/server/workflow-formatter.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:workflow',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:workflow',
},
{
lane: 'browser',
@@ -144,7 +144,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
label: 'Browser workflow orchestration',
level: 'covered',
unitTests: ['src/workflows/engine.test.ts'],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:workflow',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:workflow',
},
// ── Desktop/native lane ────────────────────────────────────────────────
@@ -154,7 +154,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
label: 'Focus app + screenshot + accessibility observation',
level: 'covered',
unitTests: ['src/server/action-executor.test.ts'],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:stdio',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:stdio',
happyPath: 'focus app → screenshot → accessibility_snapshot basic loop',
},
{
@@ -168,7 +168,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/server/register-desktop-grounding.test.ts',
'src/server/register-desktop-grounding-tools.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:desktop-v3',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:desktop-v3',
happyPath: 'desktop_ensure_chrome → desktop_observe → desktop_click_target → desktop_get_state updates grounding and pointer state',
},
{
@@ -199,7 +199,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
id: 'desktop_approval_queue',
label: 'Approval queue (list / approve / reject pending actions)',
level: 'covered',
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:workflow',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:workflow',
},
{
lane: 'desktop-native',
@@ -220,7 +220,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/workflows/engine.test.ts',
'src/terminal-release-gates.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp e2e:terminal-exec',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp e2e:terminal-exec',
happyPath: 'terminal_exec happy path: run command, capture stdout/stderr/exitCode, update run-state',
},
{
@@ -235,8 +235,8 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/server/workflow-prep-tools.test.ts',
'src/terminal-release-gates.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire',
happyPath: 'AIRI chat validates the repo → workflow self-acquires PTY for vim --version → PTY remains readable and run-state / audit / bindings stay consistent',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire',
happyPath: 'Moeka chat validates the repo → workflow self-acquires PTY for vim --version → PTY remains readable and run-state / audit / bindings stay consistent',
},
{
lane: 'terminal',
@@ -270,7 +270,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'src/workflows/engine.test.ts',
'src/server/workflow-prep-tools.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp e2e:airi-chat-terminal-self-acquire',
happyPath: 'workflow_validate_workspace starts on exec, self-acquires PTY for the interactive validation step, and completes without harness-side pty_create',
},
{
@@ -309,7 +309,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
unitTests: [
'src/server/register-vscode.test.ts',
],
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:stdio',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:stdio',
},
{
lane: 'terminal',
@@ -327,14 +327,14 @@ export const supportMatrix: SupportMatrixEntry[] = [
id: 'secret_read_env_value',
label: 'Read .env secrets without terminal echo',
level: 'covered',
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:stdio',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:stdio',
},
{
lane: 'handoff',
id: 'clipboard_read_write',
label: 'Clipboard read/write text handoff',
level: 'covered',
smokeCommand: 'pnpm -F @proj-airi/computer-use-mcp smoke:stdio',
smokeCommand: 'bun run --filter @proj-airi/computer-use-mcp smoke:stdio',
},
{
lane: 'handoff',
@@ -345,7 +345,7 @@ export const supportMatrix: SupportMatrixEntry[] = [
'packages/stage-ui/src/tools/mcp-reroute.test.ts',
'packages/stage-ui/src/tools/mcp.test.ts',
],
smokeCommand: 'pnpm exec vitest run packages/stage-ui/src/tools/mcp-reroute.test.ts packages/stage-ui/src/tools/mcp.test.ts',
smokeCommand: 'bunx vitest run packages/stage-ui/src/tools/mcp-reroute.test.ts packages/stage-ui/src/tools/mcp.test.ts',
happyPath: 'MCP returns reroute → stage-ui parser extracts → fixed template observation → provider paths consume the same reroute signal in tests',
},
]
@@ -110,7 +110,7 @@ describe('terminal release gates', () => {
ideApp: 'Cursor',
fileManagerApp: 'Finder',
changesCommand: 'git diff --stat',
checkCommand: 'pnpm test',
checkCommand: 'bun run test',
})
const stateManager = new RunStateManager()
@@ -140,7 +140,7 @@ describe('terminal release gates', () => {
expect(stateManager.getState().lastTerminalResult?.command).toBe('pwd')
}
if (command === 'pnpm test') {
if (command === 'bun run test') {
expect(stateManager.getState().lastTerminalResult?.command).toBe('git diff --stat')
}
@@ -151,7 +151,7 @@ describe('terminal release gates', () => {
else if (command === 'git diff --stat') {
stdout = ' packages/stage-ui/src/stores/chat.ts | 4 ++--\n'
}
else if (command === 'pnpm test') {
else if (command === 'bun run test') {
stdout = ' Test Files 29 passed (29)\n'
}
@@ -187,10 +187,10 @@ describe('terminal release gates', () => {
expect(stateManager.getState().terminalState).toMatchObject({
effectiveCwd: projectPath,
lastExitCode: 0,
lastCommandSummary: 'pnpm test',
lastCommandSummary: 'bun run test',
})
expect(stateManager.getState().lastTerminalResult).toMatchObject({
command: 'pnpm test',
command: 'bun run test',
exitCode: 0,
effectiveCwd: projectPath,
})
@@ -230,7 +230,7 @@ describe('terminal release gates', () => {
alive: true,
rows: 24,
cols: 80,
screenContent: 'pnpm dev\nwatching for changes...\n',
screenContent: 'bun run dev\nwatching for changes...\n',
pid: 4321,
})
.mockReturnValueOnce({
@@ -238,7 +238,7 @@ describe('terminal release gates', () => {
alive: true,
rows: 24,
cols: 80,
screenContent: 'pnpm dev\nwatching for changes...\n^C\n',
screenContent: 'bun run dev\nwatching for changes...\n^C\n',
pid: 4321,
})
@@ -265,7 +265,7 @@ describe('terminal release gates', () => {
sessionId: 'pty_gate_1',
approvalSessionId: 'approval_pty_gate',
})
expect((firstRead.structuredContent as Record<string, unknown>).screenContent).toBe('pnpm dev\nwatching for changes...\n')
expect((firstRead.structuredContent as Record<string, unknown>).screenContent).toBe('bun run dev\nwatching for changes...\n')
const sendInput = await invoke('pty_send_input', {
sessionId: 'pty_gate_1',
@@ -62,6 +62,8 @@ describe('interactive-patterns', () => {
'pnpm init',
'yarn create',
'yarn init',
'bun create',
'bun init',
])('matches init wizard: %s', (cmd) => {
expect(isKnownInteractiveCommand(cmd)).toBe(true)
})
@@ -73,7 +75,7 @@ describe('interactive-patterns', () => {
'node script.js',
'python3 app.py',
'npm install',
'pnpm run build',
'bun run build',
'git status',
])('does not match non-interactive: %s', (cmd) => {
expect(isKnownInteractiveCommand(cmd)).toBe(false)
@@ -22,7 +22,7 @@ export const KNOWN_INTERACTIVE_COMMAND_PATTERNS: RegExp[] = [
/^(python3?|python3?\s+-i)\s*$/,
/^(irb|rails\s+console|psql|mysql|sqlite3)\b/,
// Interactive init wizards that prompt questions
/^(npm|pnpm|yarn)\s+(create|init)\b/,
/^(npm|pnpm|yarn|bun)\s+(create|init)\b/,
]
/**
@@ -16,8 +16,7 @@ export const TERMINAL_OUTPUT_MAX_CHARS = 16_384
/**
* Grace period between SIGTERM and SIGKILL when reaping a timed-out command's
* process group. Mirrors the SIGTERM -> 5s -> SIGKILL escalation already used
* by the stage-tamagotchi desktop-overlay smoke harness.
* process group.
*/
const PROCESS_GROUP_KILL_GRACE_MS = 5_000
@@ -2,11 +2,11 @@
* Workflow: Dev Run Tests
*
* Opens a project directory, runs the test suite, and summarizes results.
* Designed for monorepo-style projects with `pnpm` / `npm` / `yarn`.
* Designed for monorepo-style projects with `bun` / `npm` / `pnpm` / `yarn`.
*
* Parameterised by:
* - projectPath: absolute path to the project root
* - testCommand: the shell command to run tests (default: `pnpm test:run`)
* - testCommand: the shell command to run tests (default: `bun run test:run`)
*/
import type { WorkflowDefinition } from './types'
@@ -16,7 +16,7 @@ export function createDevRunTestsWorkflow(params?: {
testCommand?: string
}): WorkflowDefinition {
const projectPath = params?.projectPath ?? '{projectPath}'
const testCommand = params?.testCommand ?? 'pnpm test:run'
const testCommand = params?.testCommand ?? 'bun run test:run'
return {
id: 'dev_run_tests',
@@ -8,7 +8,7 @@ describe('createDevValidateWorkspaceWorkflow', () => {
projectPath: '/tmp/workspace',
ideApp: 'VS Code',
changesCommand: 'git diff --stat',
checkCommand: 'pnpm typecheck',
checkCommand: 'bun run typecheck',
})
expect(workflow.id).toBe('dev_validate_workspace')
@@ -25,7 +25,7 @@ describe('createDevValidateWorkspaceWorkflow', () => {
expect(workflow.steps[4]?.params?.command).toBe('pwd')
expect(workflow.steps[5]?.params?.command).toBe('git diff --stat')
expect(workflow.steps[6]?.params?.command).toBe('pnpm typecheck')
expect(workflow.steps[6]?.params?.command).toBe('bun run typecheck')
expect(workflow.steps[6]?.params?.cwd).toBe('/tmp/workspace')
})
})
@@ -21,7 +21,7 @@ export function createDevValidateWorkspaceWorkflow(params?: {
const ideApp = canonicalizeKnownAppName(params?.ideApp ?? 'Cursor')
const fileManagerApp = canonicalizeKnownAppName(params?.fileManagerApp ?? 'Finder')
const changesCommand = params?.changesCommand ?? 'git diff --stat'
const checkCommand = params?.checkCommand ?? 'pnpm typecheck'
const checkCommand = params?.checkCommand ?? 'bun run typecheck'
return {
id: 'dev_validate_workspace',
@@ -15,13 +15,13 @@ Privacy note:
Commands run:
```sh
pnpm install --ignore-scripts --frozen-lockfile
bun install --ignore-scripts --frozen-lockfile
```
Result: passed. Lockfile stayed unchanged; lifecycle scripts were intentionally skipped for local verification setup.
```sh
pnpm -F @proj-airi/computer-use-mcp exec vitest run \
bun run --filter @proj-airi/computer-use-mcp exec vitest run \
src/browser-dom/browser-repair-contract.test.ts \
src/server/register-tools-pty-approval.test.ts \
--config ./vitest.config.ts
@@ -30,7 +30,7 @@ pnpm -F @proj-airi/computer-use-mcp exec vitest run \
Result: passed. 2 test files, 12 tests.
```sh
pnpm exec moeru-lint --fix \
bunx moeru-lint --fix \
services/computer-use-mcp/validation/browser-repair-contract.md \
services/computer-use-mcp/src/browser-dom/browser-repair-contract.ts \
services/computer-use-mcp/src/browser-dom/browser-repair-contract.test.ts \
@@ -47,7 +47,7 @@ git diff --check
Result: passed.
```sh
pnpm -F @proj-airi/computer-use-mcp typecheck
bun run --filter @proj-airi/computer-use-mcp typecheck
```
Result: failed on existing baseline files outside this change:
@@ -15,19 +15,19 @@ Privacy note:
Commands run:
```sh
pnpm install --ignore-scripts --frozen-lockfile
bun install --ignore-scripts --frozen-lockfile
```
Result: passed. Lockfile stayed unchanged; lifecycle scripts were intentionally skipped for local verification setup.
```sh
pnpm -F @proj-airi/computer-use-mcp exec vitest run src/server/tool-lane-hygiene.test.ts --config ./vitest.config.ts
bun run --filter @proj-airi/computer-use-mcp exec vitest run src/server/tool-lane-hygiene.test.ts --config ./vitest.config.ts
```
Result: passed. 1 test file, 9 tests.
```sh
pnpm -F @proj-airi/computer-use-mcp exec vitest run \
bun run --filter @proj-airi/computer-use-mcp exec vitest run \
src/server/tool-lane-hygiene.test.ts \
src/server/register-tools-coordinate-contract.test.ts \
src/server/register-tools-pty-approval.test.ts \
@@ -37,7 +37,7 @@ pnpm -F @proj-airi/computer-use-mcp exec vitest run \
Result: passed. 3 test files, 15 tests.
```sh
pnpm exec moeru-lint --fix \
bunx moeru-lint --fix \
services/computer-use-mcp/validation/tool-lane-hygiene.md \
services/computer-use-mcp/src/server/tool-lane-hygiene.ts \
services/computer-use-mcp/src/server/tool-lane-hygiene.test.ts \
@@ -54,7 +54,7 @@ git diff --check
Result: passed.
```sh
pnpm -F @proj-airi/computer-use-mcp typecheck
bun run --filter @proj-airi/computer-use-mcp typecheck
```
Result: failed on existing baseline files outside this change: