mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-16 10:50:38 +09:00
> _Stacked on top of #5685 → #5686 → #5687. Diff against master includes commits from earlier PRs in the stack — review focuses on the two new commits (`Add long-secret textarea variant to JsonSchemaForm SecretField` + `Add exe.dev sandbox provider plugin`)._ ## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - Each agent runs in a sandbox environment, and operators choose the provider — today E2B, Daytona, and (in this stack) Cloudflare > - exe.dev offers per-VM sandboxes via a small CLI / HTTP API — useful for operators who want full Linux VMs (vs container/runtime-only sandboxes) > - The plugin shape mirrors the e2b plugin: lifecycle hooks (`new`, `ls`, `rm`) drive exe.dev's CLI; SSH plumbing handles direct VM access for adapters that need it > - exe.dev VMs come up bare — `node` is not preinstalled, so the Paperclip sandbox callback bridge (a Node script) needs Node 20 installed at VM init via `--setup-script`. The plugin defaults the setup script to a Nodesource install > - The auth field accepts long SSH private keys, which need a textarea variant of the existing `SecretField` in `JsonSchemaForm` — added behind a `maxLength > THRESHOLD` opt-in so other secret fields are unaffected > - The benefit is that operators get exe.dev as a fully working sandbox provider out of the box, with no manual VM provisioning required ## What Changed **Shared UI support (`Add long-secret textarea variant to JsonSchemaForm SecretField`):** - `ui/src/components/JsonSchemaForm.tsx` + new `JsonSchemaForm.test.tsx`: when a secret-formatted field declares `maxLength` larger than the existing single-line threshold, render a monospace textarea instead of the masked input. Short secrets (API keys, tokens) keep the existing masked-input + show/hide toggle behavior. **The exe.dev plugin (`Add exe.dev sandbox provider plugin`):** - `packages/plugins/sandbox-providers/exe-dev/`: plugin entry, manifest, plugin runtime, README, and 19-test Vitest suite. - Manifest fields: API token (with `secret-ref` + `/exec` permission notes — needs `new`, `ls`, `rm`), API URL override, optional SSH username, optional SSH private key (uses the new `JsonSchemaForm` textarea variant via `maxLength: 4096`), optional SSH identity-file path, optional setup script. - Default `--setup-script` is a Nodesource Node 20 install. exe.dev VMs come up bare and the Paperclip sandbox callback bridge is a Node script, so without Node preinstalled the bridge can't start. Operators can override by supplying their own setup script. - `runLifecycleCommand` redacts env values from the executed command before surfacing it in error messages, so secrets passed via `--env=KEY=VALUE` don't leak into operator-visible failures. - The plugin distinguishes exe.dev's SSH onboarding failures (`Please complete registration by running: ssh exe.dev`) from general SSH failures and surfaces a clear remediation message. - `scripts/release-package-manifest.json`: register the new plugin for CI publish alongside the existing daytona / e2b providers. ## Verification - `pnpm typecheck` - `pnpm exec vitest run --no-coverage ui/src/components/JsonSchemaForm.test.tsx` - `(cd packages/plugins/sandbox-providers/exe-dev && pnpm test)` — 19 passing For an operator-side smoke test: 1. Get an exe.dev API token with `/exec` permission for `new`, `ls`, `rm`. 2. Register the plugin in your Paperclip instance, configure an environment with the token. 3. Create a sandbox env whose provider is `exe-dev`, then run a Codex or Claude job against it. The default Node 20 setup script should bring the VM up automatically. ## Risks - Adds a new sandbox provider plugin that follows the existing daytona / e2b shape; behavior on existing providers is unchanged. - The `JsonSchemaForm` textarea variant only engages for fields that opt in via `maxLength` larger than the existing threshold. All existing secret fields (which don't declare a `maxLength`) keep their current rendering. Test coverage pins both paths. - The redaction in `runLifecycleCommand` is a defense-in-depth measure; the test suite exercises the redaction path. If the redaction misses a future env-arg shape, the worst case is restored behavior (secrets in error messages), which is what the existing daytona / e2b plugins also do today. - Default setup script downloads from `deb.nodesource.com` over HTTPS at VM init. Operators on air-gapped networks or with a different package strategy can override the setup script. ## Model Used - Provider: Anthropic - Model: Claude Opus 4.7 (1M context) - Capabilities used: extended reasoning, tool use (Read/Edit/Bash/Grep) ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots — UI change is a textarea variant of an existing secret field; will attach screenshots before requesting merge - [x] I have updated relevant documentation to reflect my changes (plugin README, manifest descriptions) - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
136 lines
5 KiB
TypeScript
136 lines
5 KiB
TypeScript
import type { PaperclipPluginManifestV1 } from "@paperclipai/plugin-sdk";
|
|
|
|
const PLUGIN_ID = "paperclip.exe-dev-sandbox-provider";
|
|
const PLUGIN_VERSION = "0.1.0";
|
|
|
|
const manifest: PaperclipPluginManifestV1 = {
|
|
id: PLUGIN_ID,
|
|
apiVersion: 1,
|
|
version: PLUGIN_VERSION,
|
|
displayName: "exe.dev Sandbox Provider",
|
|
description:
|
|
"Sandbox provider plugin that provisions exe.dev VMs as Paperclip execution environments.",
|
|
author: "Paperclip",
|
|
categories: ["automation"],
|
|
capabilities: ["environment.drivers.register"],
|
|
entrypoints: {
|
|
worker: "./dist/worker.js",
|
|
},
|
|
environmentDrivers: [
|
|
{
|
|
driverKey: "exe-dev",
|
|
kind: "sandbox_provider",
|
|
displayName: "exe.dev VM",
|
|
description:
|
|
"Provisions exe.dev VMs through the HTTPS API, then runs commands over direct SSH for long-lived Paperclip workloads.",
|
|
configSchema: {
|
|
type: "object",
|
|
properties: {
|
|
apiKey: {
|
|
type: "string",
|
|
format: "secret-ref",
|
|
description:
|
|
"Environment-specific exe.dev API token. Needs `/exec` permission for at least `new`, `ls`, and `rm`. Paste a token or an existing Paperclip secret reference; saved environments store pasted values as company secrets. Falls back to EXE_API_KEY if omitted.",
|
|
},
|
|
apiUrl: {
|
|
type: "string",
|
|
description:
|
|
"Optional exe.dev HTTPS API base URL or /exec endpoint. Defaults to https://exe.dev/exec.",
|
|
},
|
|
namePrefix: {
|
|
type: "string",
|
|
description: "Optional prefix used when generating VM names.",
|
|
default: "paperclip",
|
|
},
|
|
image: {
|
|
type: "string",
|
|
description: "Optional container image to use when creating the VM.",
|
|
},
|
|
command: {
|
|
type: "string",
|
|
description: "Optional container command passed to `exe.dev new --command`.",
|
|
},
|
|
cpu: {
|
|
type: "number",
|
|
description: "Optional CPU count passed to `exe.dev new --cpu`.",
|
|
},
|
|
memory: {
|
|
type: "string",
|
|
description: "Optional memory size such as `4GB`.",
|
|
},
|
|
disk: {
|
|
type: "string",
|
|
description: "Optional disk size such as `20GB`.",
|
|
},
|
|
comment: {
|
|
type: "string",
|
|
description: "Optional short note attached to created VMs.",
|
|
},
|
|
env: {
|
|
type: "object",
|
|
description: "Optional environment variables applied at VM creation time.",
|
|
additionalProperties: { type: "string" },
|
|
},
|
|
integrations: {
|
|
type: "array",
|
|
description: "Optional exe.dev integrations to attach during VM creation.",
|
|
items: { type: "string" },
|
|
},
|
|
tags: {
|
|
type: "array",
|
|
description: "Optional tags to apply during VM creation.",
|
|
items: { type: "string" },
|
|
},
|
|
setupScript: {
|
|
type: "string",
|
|
description: "Optional first-boot setup script passed to `exe.dev new --setup-script`.",
|
|
},
|
|
prompt: {
|
|
type: "string",
|
|
description: "Optional Shelley prompt passed to `exe.dev new --prompt`.",
|
|
},
|
|
timeoutMs: {
|
|
type: "number",
|
|
description: "Timeout for VM lifecycle and SSH operations in milliseconds.",
|
|
default: 300000,
|
|
},
|
|
reuseLease: {
|
|
type: "boolean",
|
|
description:
|
|
"Whether to keep the VM alive between runs instead of deleting it on release.",
|
|
default: false,
|
|
},
|
|
sshUser: {
|
|
type: "string",
|
|
description: "Optional SSH username for direct VM access.",
|
|
},
|
|
sshPrivateKey: {
|
|
type: "string",
|
|
format: "secret-ref",
|
|
maxLength: 4096,
|
|
description:
|
|
"Optional exe.dev-registered SSH private key. Paste the private key or an existing Paperclip secret reference; saved environments store pasted values as company secrets. If omitted, Paperclip falls back to sshIdentityFile, then the host's default SSH agent/keychain.",
|
|
},
|
|
sshIdentityFile: {
|
|
type: "string",
|
|
description:
|
|
"Optional absolute path to the SSH private key the Paperclip host should use for VM access when sshPrivateKey is omitted. Leave both blank to rely on the host's default SSH agent/keychain.",
|
|
},
|
|
sshPort: {
|
|
type: "number",
|
|
description: "SSH port for direct VM access.",
|
|
default: 22,
|
|
},
|
|
strictHostKeyChecking: {
|
|
type: "string",
|
|
description:
|
|
"Host key policy passed to ssh via StrictHostKeyChecking. Typical values are `accept-new`, `yes`, or `no`.",
|
|
default: "accept-new",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
export default manifest;
|