---
title: Run on Plue
description: Execute a Smithers workflow on Plue workspaces through the Plue sandbox provider.
---

# Run on Plue

`run-on-plue` executes a Smithers workflow script on Plue infrastructure instead of the local machine. It uses the [`<Sandbox>`](/components/sandbox) provider seam with `createPlueSandboxProvider`, and the provider shells through the `plue` CLI so Plue owns workspace lifecycle, authentication, quotas, and Freestyle VM access.

Use it when a child workflow needs a real Plue workspace: a repo-bound VM with SSH, `git`, `jj`, Node, network egress, and bootstrapped Claude Code and Codex CLIs.

## What it does

The workflow lives at `.smithers/workflows/run-on-plue.tsx`. It accepts a path to any Smithers `.tsx` workflow, reads that file, and sends it to a Plue-backed sandbox provider.

The provider:

1. Creates a Plue workspace with `plue workspace create --repo <owner>/<repo>`.
2. Polls `plue workspace view --format json` until the workspace is running and has an SSH command.
3. Connects over SSH in batch mode.
4. Installs Bun if missing, then installs `@anthropic-ai/claude-code` and `@openai/codex` when those CLIs are absent.
5. Seeds Claude and Codex auth via `plue workspace exec --seed-agent-auth claude,codex`.
6. Ships a small remote project containing the child workflow source, `agents.ts`, `package.json`, and `input.json`.
7. Runs `smithers-orchestrator up` in the workspace and reads the remote run result back over SSH.
8. Deletes the workspace unless `keepWorkspace` is true.

The parent workflow receives:

```ts
{
  status: "finished" | "failed" | "cancelled";
  output?: unknown;
  remoteRunId?: string;
  workspaceId?: string;
}
```

## Requirements

- `plue` CLI installed and authenticated on the host running Smithers. Verify with `plue auth status`.
- The target Plue server must have workspaces enabled. Local Plue uses `SMITHERS_FEATURE_FLAGS_WORKSPACES=true`.
- The Plue server must be configured for real Freestyle workspaces, including the Freestyle API key required by that server (`SMITHERS_FREESTYLE_API_KEY` or the environment wiring used by your Plue deployment).
- The repo passed as `repo` must exist in Plue and be accessible to the authenticated user.
- Claude auth must be available locally. The preferred path is Claude Code subscription OAuth in the OS credential store; `ANTHROPIC_API_KEY` only works when that key has usable API billing.
- Codex auth must be available locally. The preferred path is `~/.codex/auth.json`; `OPENAI_API_KEY` is only a fallback for setups where the Codex CLI honors API-key auth.
- If `plue` is not on `PATH`, set `PLUE_BIN` or pass `plueBin` in the workflow input.

Secrets are seeded at run time by the Plue CLI and written into the VM with restrictive file permissions. Do not bake Claude, Codex, OpenAI, Anthropic, or Freestyle credentials into workspace snapshots or committed files.

## Run a workflow remotely

Run the included demo child workflow:

```bash
bunx smithers-orchestrator up .smithers/workflows/run-on-plue.tsx --input '{
  "script": ".smithers/workflows/plue-demo-child.tsx",
  "repo": "alice/smoke-test"
}'
```

Pass input through to the remote child:

```bash
bunx smithers-orchestrator up .smithers/workflows/run-on-plue.tsx --input '{
  "script": "workflows/review-child.tsx",
  "repo": "acme/app",
  "input": {
    "ticketId": "APP-123"
  }
}'
```

Use a local or custom CLI binary and keep the workspace for inspection:

```bash
bunx smithers-orchestrator up .smithers/workflows/run-on-plue.tsx --input '{
  "script": ".smithers/workflows/plue-demo-child.tsx",
  "repo": "alice/smoke-test",
  "plueBin": "/path/to/plue",
  "keepWorkspace": true
}'
```

## Use the provider directly

`run-on-plue` is the default wrapper, but workflows can pass the provider object to `<Sandbox>` directly:

```tsx
/** @jsxImportSource smithers-orchestrator */
import { createSmithers } from "smithers-orchestrator";
import { Sandbox } from "@smithers-orchestrator/components";
import { createPlueSandboxProvider } from "../lib/plue-provider";
import { childWorkflow } from "./child";
import { z } from "zod/v4";

const { Workflow, smithers, outputs } = createSmithers({
  result: z.object({ summary: z.string() }),
});

const provider = createPlueSandboxProvider({
  repo: "acme/app",
  keepWorkspace: false,
});

export default smithers((ctx) => (
  <Workflow name="remote-child">
    <Sandbox
      id="plue-run"
      provider={provider}
      workflow={childWorkflow}
      input={{ prompt: ctx.input.prompt }}
      output={outputs.result}
      reviewDiffs={false}
      timeoutMs={30 * 60_000}
    />
  </Workflow>
));
```

## Non-interactive caveat

Keep `reviewDiffs={false}` for Plue runs. `<Sandbox>` defaults to fail-closed diff review, which expects a local review/approval interaction before applying file changes. The v1 Plue provider is non-interactive: it runs the remote child, maps the remote result into the parent run, and does not open a human approval loop inside the VM.

Design child workflows so they return structured output or artifacts. If you need to inspect the remote VM after a failure, run with `keepWorkspace: true`, then use `plue workspace view` or `plue workspace ssh` to connect.

## Claude and Codex bootstrap

Remote workspaces may not have all agent CLIs preinstalled. The provider's bootstrap is idempotent:

- `bun` is installed from the official installer when missing.
- `claude` is installed with `bun i -g @anthropic-ai/claude-code` when missing.
- `codex` is installed with `bun i -g @openai/codex` when missing.
- `~/.bun/bin` is prepended to `PATH` for remote commands.
- `plue workspace exec --seed-agent-auth claude,codex` stages Claude Code and Codex credentials before the child workflow runs.

The remote mini-project includes an `agents.ts` that exports both `ClaudeCodeAgent` and `CodexAgent`, so child workflows can import or define tasks that use either CLI once bootstrap completes.
