feat(freestyle): add Freestyle VM integration for Virtual MCP repos#3079
feat(freestyle): add Freestyle VM integration for Virtual MCP repos#3079
Conversation
🧪 BenchmarkShould we run the Virtual MCP strategy benchmark for this PR? React with 👍 to run the benchmark.
Benchmark will run on the next push after you react. |
Release OptionsSuggested: Minor ( React with an emoji to override the release type:
Current version:
|
There was a problem hiding this comment.
8 issues found across 23 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/web/routes/agent-home.tsx">
<violation number="1" location="apps/mesh/src/web/routes/agent-home.tsx:119">
P2: `browser-inspector` is not handled in `useResolvedMainView`, so metadata defaults to chat instead of opening the inspector.</violation>
</file>
<file name="apps/mesh/src/tools/virtual/add-repo.ts">
<violation number="1" location="apps/mesh/src/tools/virtual/add-repo.ts:66">
P1: The catch block restores stale metadata after cleanup, which can persist deleted Freestyle IDs and mismatched repo/runtime data.</violation>
</file>
<file name="apps/mesh/src/freestyle/runtime.ts">
<violation number="1" location="apps/mesh/src/freestyle/runtime.ts:28">
P1: `stopScript` should surface VM deletion failures instead of ignoring them; otherwise stop operations can falsely succeed.</violation>
</file>
<file name="apps/mesh/src/freestyle/setup.ts">
<violation number="1" location="apps/mesh/src/freestyle/setup.ts:30">
P1: `setupRepo` lacks compensating cleanup on partial failure, which can orphan Freestyle repos/VMs.</violation>
<violation number="2" location="apps/mesh/src/freestyle/setup.ts:69">
P2: Cleanup currently swallows deletion failures, making leaked Freestyle resources invisible.</violation>
</file>
<file name="apps/mesh/src/tools/virtual/run-script.ts">
<violation number="1" location="apps/mesh/src/tools/virtual/run-script.ts:64">
P1: The “optimistic lock” is not actually atomic; concurrent run requests can both start VMs and leak orphaned instances.</violation>
<violation number="2" location="apps/mesh/src/tools/virtual/run-script.ts:91">
P1: If metadata persistence fails after VM creation, the catch path does not delete the created VM, which can leak running Freestyle instances.</violation>
</file>
<file name="apps/mesh/src/tools/virtual/delete.ts">
<violation number="1" location="apps/mesh/src/tools/virtual/delete.ts:64">
P2: Do not swallow Freestyle cleanup failures with an empty catch; at minimum, surface/log the error so orphaned resource leaks are detectable.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| } | ||
|
|
||
| if (metadata.freestyle_vm_id) { | ||
| await freestyle.vms |
There was a problem hiding this comment.
P1: stopScript should surface VM deletion failures instead of ignoring them; otherwise stop operations can falsely succeed.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/freestyle/runtime.ts, line 28:
<comment>`stopScript` should surface VM deletion failures instead of ignoring them; otherwise stop operations can falsely succeed.</comment>
<file context>
@@ -0,0 +1,80 @@
+ }
+
+ if (metadata.freestyle_vm_id) {
+ await freestyle.vms
+ .delete({ vmId: metadata.freestyle_vm_id })
+ .catch(() => {});
</file context>
|
|
||
| if (metadata.freestyle_vm_id) { | ||
| promises.push( | ||
| freestyle.vms.delete({ vmId: metadata.freestyle_vm_id }).catch(() => {}), |
There was a problem hiding this comment.
P2: Cleanup currently swallows deletion failures, making leaked Freestyle resources invisible.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/freestyle/setup.ts, line 69:
<comment>Cleanup currently swallows deletion failures, making leaked Freestyle resources invisible.</comment>
<file context>
@@ -0,0 +1,82 @@
+
+ if (metadata.freestyle_vm_id) {
+ promises.push(
+ freestyle.vms.delete({ vmId: metadata.freestyle_vm_id }).catch(() => {}),
+ );
+ }
</file context>
| await cleanupFreestyleResources( | ||
| freestyle, | ||
| existing.metadata as Record<string, unknown>, | ||
| ).catch(() => {}); |
There was a problem hiding this comment.
P2: Do not swallow Freestyle cleanup failures with an empty catch; at minimum, surface/log the error so orphaned resource leaks are detectable.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/tools/virtual/delete.ts, line 64:
<comment>Do not swallow Freestyle cleanup failures with an empty catch; at minimum, surface/log the error so orphaned resource leaks are detectable.</comment>
<file context>
@@ -53,6 +55,15 @@ export const COLLECTION_VIRTUAL_MCP_DELETE = defineTool({
+ await cleanupFreestyleResources(
+ freestyle,
+ existing.metadata as Record<string, unknown>,
+ ).catch(() => {});
+ }
+
</file context>
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/web/views/virtual-mcp/index.tsx">
<violation number="1" location="apps/mesh/src/web/views/virtual-mcp/index.tsx:1059">
P2: Unwrap the `VIRTUAL_MCP_ADD_REPO` tool response so structured tool errors are not silently treated as success.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/tools/virtual/add-repo.ts">
<violation number="1" location="apps/mesh/src/tools/virtual/add-repo.ts:113">
P2: Do not swallow the metadata reset failure; silently ignoring this update can leave the Virtual MCP stuck in `installing` state and hide the real storage issue.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/freestyle/setup.ts">
<violation number="1" location="apps/mesh/src/freestyle/setup.ts:73">
P2: Clean up created Freestyle resources when dependency installation fails; otherwise failed setup attempts can leak orphaned VM/repo resources.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| } | ||
|
|
||
| try { | ||
| await vm.vm.js.install({ directory: "/app" }); |
There was a problem hiding this comment.
P2: Clean up created Freestyle resources when dependency installation fails; otherwise failed setup attempts can leak orphaned VM/repo resources.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/freestyle/setup.ts, line 73:
<comment>Clean up created Freestyle resources when dependency installation fails; otherwise failed setup attempts can leak orphaned VM/repo resources.</comment>
<file context>
@@ -56,30 +56,28 @@ export async function setupRepo(
- await vm.suspend();
+ try {
+ await vm.vm.js.install({ directory: "/app" });
+ } catch (e) {
+ throw new Error(
</file context>
There was a problem hiding this comment.
4 issues found across 14 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/web/views/virtual-mcp/github-tab-content.tsx">
<violation number="1" location="apps/mesh/src/web/views/virtual-mcp/github-tab-content.tsx:163">
P1: Do not ignore stop-script failures during unlink; clearing metadata after a failed stop can orphan a running VM.</violation>
<violation number="2" location="apps/mesh/src/web/views/virtual-mcp/github-tab-content.tsx:295">
P2: Persisting preview_port on each keystroke causes repeated mutations/invalidation; use a local draft value and save on blur/submit instead.</violation>
</file>
<file name="apps/mesh/src/freestyle/runtime.ts">
<violation number="1" location="apps/mesh/src/freestyle/runtime.ts:41">
P2: Validate `preview_port` before using it as the VM target port and in the shell command; persisted metadata is not guaranteed to be a valid port at runtime.</violation>
</file>
<file name="apps/mesh/src/freestyle/parse-metadata.ts">
<violation number="1" location="apps/mesh/src/freestyle/parse-metadata.ts:30">
P2: Validate `scripts` object entries before casting; currently non-string values are accepted as `Record<string, string>` and can break downstream string usage.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| name: "VIRTUAL_MCP_STOP_SCRIPT", | ||
| arguments: { virtual_mcp_id: virtualMcp.id }, | ||
| }) | ||
| .catch(() => {}); |
There was a problem hiding this comment.
P1: Do not ignore stop-script failures during unlink; clearing metadata after a failed stop can orphan a running VM.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/views/virtual-mcp/github-tab-content.tsx, line 163:
<comment>Do not ignore stop-script failures during unlink; clearing metadata after a failed stop can orphan a running VM.</comment>
<file context>
@@ -0,0 +1,334 @@
+ name: "VIRTUAL_MCP_STOP_SCRIPT",
+ arguments: { virtual_mcp_id: virtualMcp.id },
+ })
+ .catch(() => {});
+ }
+
</file context>
| .workdir("/app") | ||
| .waitForReadySignal(true); | ||
|
|
||
| const targetPort = metadata.preview_port ?? 3000; |
There was a problem hiding this comment.
P2: Validate preview_port before using it as the VM target port and in the shell command; persisted metadata is not guaranteed to be a valid port at runtime.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/freestyle/runtime.ts, line 41:
<comment>Validate `preview_port` before using it as the VM target port and in the shell command; persisted metadata is not guaranteed to be a valid port at runtime.</comment>
<file context>
@@ -38,18 +38,20 @@ export async function runScript(
.workdir("/app")
.waitForReadySignal(true);
+ const targetPort = metadata.preview_port ?? 3000;
+
const { vm, vmId, domains } = await freestyle.vms.create({
</file context>
| const targetPort = metadata.preview_port ?? 3000; | |
| const targetPort = | |
| typeof metadata.preview_port === "number" && | |
| Number.isInteger(metadata.preview_port) && | |
| metadata.preview_port >= 1 && | |
| metadata.preview_port <= 65535 | |
| ? metadata.preview_port | |
| : 3000; |
| running_script: | ||
| typeof m.running_script === "string" ? m.running_script : null, | ||
| vm_domain: typeof m.vm_domain === "string" ? m.vm_domain : null, | ||
| scripts: |
There was a problem hiding this comment.
P2: Validate scripts object entries before casting; currently non-string values are accepted as Record<string, string> and can break downstream string usage.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/freestyle/parse-metadata.ts, line 30:
<comment>Validate `scripts` object entries before casting; currently non-string values are accepted as `Record<string, string>` and can break downstream string usage.</comment>
<file context>
@@ -0,0 +1,63 @@
+ running_script:
+ typeof m.running_script === "string" ? m.running_script : null,
+ vm_domain: typeof m.vm_domain === "string" ? m.vm_domain : null,
+ scripts:
+ m.scripts && typeof m.scripts === "object" && !Array.isArray(m.scripts)
+ ? (m.scripts as Record<string, string>)
</file context>
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/web/views/virtual-mcp/github-tab-content.tsx">
<violation number="1" location="apps/mesh/src/web/views/virtual-mcp/github-tab-content.tsx:148">
P2: `portInput` is seeded from `fm.preview_port` but never re-synced when server metadata changes, so the Preview port field can display stale values.
(Based on your team's feedback about syncing props-derived draft inputs when async server values change.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const fm = parseFreestyleMetadata(virtualMcp.metadata); | ||
| const [unlinkOpen, setUnlinkOpen] = useState(false); | ||
| const [unlinking, setUnlinking] = useState(false); | ||
| const [portInput, setPortInput] = useState( |
There was a problem hiding this comment.
P2: portInput is seeded from fm.preview_port but never re-synced when server metadata changes, so the Preview port field can display stale values.
(Based on your team's feedback about syncing props-derived draft inputs when async server values change.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/views/virtual-mcp/github-tab-content.tsx, line 148:
<comment>`portInput` is seeded from `fm.preview_port` but never re-synced when server metadata changes, so the Preview port field can display stale values.
(Based on your team's feedback about syncing props-derived draft inputs when async server values change.) </comment>
<file context>
@@ -145,6 +145,9 @@ function PopulatedState({ virtualMcp }: { virtualMcp: VirtualMCPEntity }) {
const fm = parseFreestyleMetadata(virtualMcp.metadata);
const [unlinkOpen, setUnlinkOpen] = useState(false);
const [unlinking, setUnlinking] = useState(false);
+ const [portInput, setPortInput] = useState(
+ fm.preview_port != null ? String(fm.preview_port) : "",
+ );
</file context>
There was a problem hiding this comment.
4 issues found across 10 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/mesh/src/freestyle/detect.test.ts">
<violation number="1" location="apps/mesh/src/freestyle/detect.test.ts:78">
P2: The `deno.jsonc` test uses JSON instead of JSONC, so it doesn’t actually validate JSONC parsing.</violation>
</file>
<file name="packages/mesh-sdk/src/types/virtual-mcp.ts">
<violation number="1" location="packages/mesh-sdk/src/types/virtual-mcp.ts:216">
P2: Create/update now allow `runtime: "deno"`, but the entity schema still only allows `"bun"`, causing inconsistent validation and potential parse failures for stored entities.</violation>
</file>
<file name="apps/mesh/src/freestyle/setup.ts">
<violation number="1" location="apps/mesh/src/freestyle/setup.ts:11">
P2: `SetupResult.runtime` now claims Deno support, but `setupRepo` still provisions and installs with `VmBun` unconditionally. This creates a runtime/contract mismatch for Deno-detected repositories.</violation>
</file>
<file name="apps/mesh/src/freestyle/detect.ts">
<violation number="1" location="apps/mesh/src/freestyle/detect.ts:83">
P2: `deno.jsonc` is parsed with `JSON.parse`, so valid JSONC configs can be ignored and task scripts will appear empty.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const result = await detectRepo( | ||
| "owner/repo", | ||
| mockReader({ | ||
| "deno.jsonc": JSON.stringify({ tasks: { dev: "deno run dev.ts" } }), |
There was a problem hiding this comment.
P2: The deno.jsonc test uses JSON instead of JSONC, so it doesn’t actually validate JSONC parsing.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/freestyle/detect.test.ts, line 78:
<comment>The `deno.jsonc` test uses JSON instead of JSONC, so it doesn’t actually validate JSONC parsing.</comment>
<file context>
@@ -49,16 +49,91 @@ describe("detectRepo", () => {
+ const result = await detectRepo(
+ "owner/repo",
+ mockReader({
+ "deno.jsonc": JSON.stringify({ tasks: { dev: "deno run dev.ts" } }),
+ }),
+ );
</file context>
| "deno.jsonc": JSON.stringify({ tasks: { dev: "deno run dev.ts" } }), | |
| "deno.jsonc": `{ | |
| // dev task | |
| "tasks": { | |
| "dev": "deno run dev.ts", | |
| }, | |
| }`, |
| repoId: string; | ||
| snapshotId: string; | ||
| vmId: string; | ||
| runtime: "bun" | "deno"; |
There was a problem hiding this comment.
P2: SetupResult.runtime now claims Deno support, but setupRepo still provisions and installs with VmBun unconditionally. This creates a runtime/contract mismatch for Deno-detected repositories.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/freestyle/setup.ts, line 11:
<comment>`SetupResult.runtime` now claims Deno support, but `setupRepo` still provisions and installs with `VmBun` unconditionally. This creates a runtime/contract mismatch for Deno-detected repositories.</comment>
<file context>
@@ -8,7 +8,7 @@ export interface SetupResult {
snapshotId: string;
vmId: string;
- runtime: "bun";
+ runtime: "bun" | "deno";
scripts: Record<string, string>;
instructions: string | null;
</file context>
| const raw = denoJsonRaw ?? denoJsoncRaw; | ||
| if (raw) { | ||
| try { | ||
| const denoConfig = JSON.parse(raw); |
There was a problem hiding this comment.
P2: deno.jsonc is parsed with JSON.parse, so valid JSONC configs can be ignored and task scripts will appear empty.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/freestyle/detect.ts, line 83:
<comment>`deno.jsonc` is parsed with `JSON.parse`, so valid JSONC configs can be ignored and task scripts will appear empty.</comment>
<file context>
@@ -52,6 +56,51 @@ function parseDecoJson(raw: string): DecoJson | null {
+ const raw = denoJsonRaw ?? denoJsoncRaw;
+ if (raw) {
+ try {
+ const denoConfig = JSON.parse(raw);
+ return denoConfig.tasks ?? {};
+ } catch {
</file context>
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
Virtual MCPs can now be linked to GitHub repos via Freestyle VMs. Adds tools (ADD_REPO, RUN_SCRIPT, STOP_SCRIPT), Play button in topbar, browser inspector view, and resource cleanup on deletion. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r tool calls Adds owner/repo input field in the Virtual MCP instructions tab. Play/Stop/AddRepo tool calls now invalidate the entity query cache so the UI updates immediately after state changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wraps each Freestyle API call (detect, repo create, github sync, VM create) in its own try/catch so errors surface which step failed instead of a generic "Unknown error code" message. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GitHub sync requires a GitHub App to be installed on the target repo. Skip silently if it fails — the repo is already cloned via source URL. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…r deps systemd services can't find bun binary (installed at /opt/bun/bin/bun). Use vm.js.install() from VmBun integration for dep installation, and use full bun path with nohup for running scripts in the background. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move repo configuration from Instructions tab to a dedicated GitHub tab with empty state (connect input) and populated state (runtime, autorun, preview port settings). Add Preview button in tasks panel sidebar that opens browser inspector when preview port is configured. Key changes: - GitHub tab always visible in settings (Instructions/GitHub/Connections/Layout) - preview_port passed as targetPort to Freestyle VM (ports 443 mapping) - deco.json read from repo during setup with validation - Shared useInvalidateVirtualMcp hook and parseFreestyleMetadata utility - Unlink action with confirmation dialog clears all 11 freestyle fields - External link in browser inspector uses noopener/noreferrer Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… every keystroke Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detect deno.json, deno.jsonc, and deno.lock for Deno projects. Uses @freestyle-sh/with-deno VmDeno integration and `deno task` for script execution. Runtime auto-detected from lockfiles, overridable via deco.json or the GitHub tab dropdown. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The entity schema (used for LIST/GET output validation) was still ["bun"] only — the previous replace_all missed it due to different formatting (multiline vs inline). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows existing scripts with delete buttons, and an inline form to add new entries. Labels adapt to runtime (scripts for bun, tasks for deno). This makes the play button work for manually configured repos where VIRTUAL_MCP_ADD_REPO wasn't used. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scripts are populated automatically from repo detection via VIRTUAL_MCP_ADD_REPO (package.json scripts or deno.json tasks). The GitHub tab shows them as read-only badges, not an editable form. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…style setup Detection (GitHub API) and infra setup (Freestyle VM) are now separate steps. Scripts, runtime, and instructions are persisted immediately after detection, so even if Freestyle setup fails (no API key, VM error), the play button still works because scripts are populated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scripts are only shown in the play button dropdown, not duplicated in the settings panel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Preview button in tasks panel now shows when repo_url is set (no preview_port requirement) - Removed FreestylePlayButton from the topbar header - Browser inspector empty state now shows script/task buttons to start the dev server, with loading and installing states - Toolbar shows stop button and green status dot when running Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Toolbar always renders with URL bar, refresh, and play/stop controls. Content area swaps between iframe (when running) and contextual empty state (with play icon + hint text when idle). Play dropdown with script selection lives in the toolbar top-right. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parent layout uses items-center which vertically centered the view. Added self-stretch to override and fill from top. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… vm_domain If runtime_status is "running" but vm_domain is null, the state is stale from a previous failed run. Allow starting a new script instead of blocking with an error. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Freestyle domains[0] can be empty string when no domain is assigned. parseFreestyleMetadata now treats "" as null, runtime returns null instead of "", and run-script output schema accepts nullable domain. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Freestyle API expects { snapshot, ports, ... } for the object form of
vms.create(). Using { spec } caused domains to be empty array since
the API didn't recognize the spec parameter for port/domain assignment.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VM creation can take 30-60s (Freestyle cache miss). The run-script tool now returns immediately with installing status, and the VM creation runs in the background. The browser inspector polls every 3s while installing to detect when the VM is ready and render the iframe. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n assignment VmSpec as snapshot wasn't returning domains. Switched to the flat object form with `with`, `gitRepos`, `workdir`, and `ports` — this matches the documented API that returns domains in the response. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logs all keys from create result, tries vms.get() as fallback to find the domain if create doesn't return it. This will tell us exactly what Freestyle returns and where the domain lives. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… fallback Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…log after start Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The VmDeno integration must be registered with key "deno" not "js", and deno binary should be on PATH (use `deno task` not `/root/.deno/bin/deno`). Also fixed type overload issue by splitting create calls per runtime. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Knip detected this file as unused — it's not imported anywhere. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the spinner shown during VM install/startup with a live streaming terminal (ttyd) via Freestyle's VmWebTerminal integration. Users now see real-time output of bun install and dev server startup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
243e5c6 to
a42fadf
Compare
Restore shell-layout.tsx to match main (was incorrectly polluted with agent-specific code during rebase). Fix browser-inspector-view import to use agent-shell-layout. Add browser-inspector view type to agent-shell-layout. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n field The test expected 11 fields but emptyFreestyleMetadata now returns 12 after terminal_domain was added. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split runScript into createVm + waitForApp so the terminal domain is stored in metadata immediately after VM creation. This lets the UI show the terminal iframe during install instead of the spinner. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
VIRTUAL_MCP_ADD_REPO,VIRTUAL_MCP_RUN_SCRIPT,VIRTUAL_MCP_STOP_SCRIPTpackage.jsonFREESTYLE_API_KEYenv var wired through MeshContext (followsfirecrawlApiKeypattern)New files
apps/mesh/src/freestyle/— client, detect, setup, runtime, types modulesapps/mesh/src/tools/virtual/add-repo.ts,run-script.ts,stop-script.tsapps/mesh/src/web/components/freestyle-play-button.tsxapps/mesh/src/web/components/browser-inspector-view.tsxTest plan
FREESTYLE_API_KEYenv var, add a Bun repo viaVIRTUAL_MCP_ADD_REPOtool🤖 Generated with Claude Code
Summary by cubic
Adds Freestyle VM support so Virtual MCPs can link a GitHub repo, auto-detect Bun/Deno scripts, and run them with a live Preview in the in‑app Browser Inspector, with streaming terminal logs during install/startup. Start/stop from the Preview; the sidebar shows a Preview button once a repo is linked.
New Features
FREESTYLE_API_KEY; entity cache auto‑refreshes after tool calls.@freestyle-sh/with-bunor@freestyle-sh/with-deno; mappreview_portto 443; clean up on MCP delete or unlink. Tools:VIRTUAL_MCP_ADD_REPO,VIRTUAL_MCP_RUN_SCRIPT,VIRTUAL_MCP_STOP_SCRIPT; depsfreestyle-sandboxes,@freestyle-sh/with-bun,@freestyle-sh/with-deno,@freestyle-sh/with-web-terminal; new envFREESTYLE_API_KEY(exposed asfreestyleApiKey).Bug Fixes
vm_domainis missing (stale state); treat emptyvm_domainas null;run-scriptreturns a nullable domain.deno task; split VM creation per runtime; switch to flatvms.createoptions andsnapshotso domains resolve.terminal_domainimmediately after VM creation so Preview shows the live terminal during install; adjust tests to includeterminal_domain.freestyle-play-buttoncomponent.Written for commit 7f4bc6b. Summary will update on new commits.