Commit Graph

3 Commits

Author SHA1 Message Date
Devin Foley 93291df5c8 fix(plugins): move dev SDK linking out of plugin postinstall scripts (#8255)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Sandbox-provider plugins (cloudflare, daytona, e2b, exe-dev,
kubernetes, modal, novita) and `plugin-workspace-diff` are published as
standalone npm packages, but during local dev they need the in-repo
`@paperclipai/plugin-sdk` symlinked in
> - Each of these plugins shipped a `postinstall` lifecycle script that
traversed *out* of its own package directory (`node
../../../../scripts/link-plugin-dev-sdk.mjs`) to do that linking
> - The publishable manifest is built by a `prepack` whitelist that
drops the `scripts` field, so npm consumers don't see the postinstall
today — but that safety property depends entirely on `prepack` running
on every publish. A publish that skips lifecycle scripts would ship a
tarball whose postinstall escapes its package directory at consumer
install time
> - This pull request removes the escape-the-package-dir lifecycle
script from every plugin source manifest and moves the dev linking to a
single root-level postinstall that iterates the excluded plugin
directories itself
> - The benefit is that plugin tarballs can no longer carry an
install-time script that reaches outside their own directory, regardless
of whether `prepack` runs

## Linked Issues or Issue Description

This is a follow-up hardening change flagged during review of the Novita
sandbox provider PR (#7595).

**Problem (security):** Excluded plugin packages each carried
`"postinstall": "node ../../../../scripts/link-plugin-dev-sdk.mjs"`. The
relative path traverses outside the package root. Today the published
manifest is sanitized by a `prepack` whitelist that drops `scripts`, so
consumers are unaffected in the normal publish path. The risk is that
this is a defense-in-depth gap: if a publish ever skips lifecycle
scripts (e.g. `npm publish --ignore-scripts` is *not* used, or a tool
publishes the raw manifest), the tarball would ship a postinstall that
runs out-of-tree code at the consumer's install time.

## What Changed

- Added a single root `package.json` `postinstall`: `node
scripts/link-plugin-dev-sdk.mjs`.
- Rewrote `scripts/link-plugin-dev-sdk.mjs` to iterate the excluded
plugin directories itself (`packages/plugins/sandbox-providers/*` + the
orchestration smoke example) instead of relying on each plugin to invoke
it from its own cwd. Preserves both prior behaviors: leave a real
installed SDK dir alone, and skip when already correctly symlinked
(idempotent).
- Removed `scripts.postinstall` from all 7 sandbox-provider plugins
(cloudflare, daytona, e2b, exe-dev, kubernetes, modal, novita).
- Removed `scripts.postinstall` from `plugin-workspace-diff` (a pnpm
workspace member — pnpm already links the SDK, so the script was a no-op
there).

## Verification

- `node scripts/link-plugin-dev-sdk.mjs` from repo root: links the SDK
into the excluded plugins and reports skipped (already-linked) dirs;
re-running is idempotent.
- `grep -r "link-plugin-dev-sdk" packages/plugins/*/package.json
packages/plugins/sandbox-providers/*/package.json` returns no matches —
no plugin source manifest references the linker any longer.
- All affected `package.json` files re-validated as parseable JSON.

## Risks

Low risk. Dev-only tooling: the linker only runs at the repo root during
local install and only touches `node_modules/@paperclipai/plugin-sdk`
symlinks inside excluded plugin dirs. No change to published plugin
behavior or runtime code. Worst case if the root postinstall failed to
run, local dev of an excluded plugin would not find the SDK symlink —
easily re-run manually.

## Model Used

Claude Opus (claude-opus-4-8), extended reasoning, with tool use / code
execution in an agentic coding harness.

## 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 searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues OR (b) described the
issue in-PR following the relevant issue template
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable (added
`scripts/link-plugin-dev-sdk.test.js`, wired into
`test:release-registry`)
- [ ] If this change affects the UI, I have included before/after
screenshots (N/A — no UI change)
- [ ] I have updated relevant documentation to reflect my changes (N/A)
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-06-18 07:45:53 -07:00
Devin Foley 20aea356cc refactor(deps-dev): bump vitest from 3.2.4 to 4.1.8 (#7581)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - Test infrastructure across server, ui, packages/* runs on Vitest
> - Dependabot opened a narrow bump (3.2.4 → 3.2.6), but the wider
workspace is on 3.2.4 and the major-version bridge to v4 needs a
coordinated change set across configs and tests
> - Staying on 3.x indefinitely leaves us behind on Vitest 4 (perf,
pool, and config improvements) and forces repeated patch-only dependabot
churn
> - This pull request upgrades Vitest to 4.1.8 across the workspace,
updates `server/vitest.config.ts` and `scripts/run-vitest-stable.mjs`
for the new API, and adjusts two UI tests for the new assertion
semantics
> - The benefit is a single, coherent Vitest 4 upgrade that supersedes
#7570 and gets us on the supported major line

## What Changed

- Bump `vitest` from `3.2.4` to `4.1.8` across root, `server`, `ui`, and
all `packages/*` (including plugin examples and sandbox providers)
- Update `server/vitest.config.ts` for Vitest 4 config surface
- Update `scripts/run-vitest-stable.mjs` to match the new runner
behavior
- Adjust `ui/src/components/CommentThread.test.tsx` and
`ui/src/components/MarkdownEditor.test.tsx` for Vitest 4 matcher/timing
semantics
- Refresh `pnpm-lock.yaml`

## Verification

- `pnpm install` resolves cleanly with the new lockfile
- `pnpm -w -r test` (server, ui, packages) runs under Vitest 4.1.8

## Risks

- Major-version Vitest bump: behavioral changes in pools, fake timers,
and matcher strictness can surface flake. Test config and the two UI
tests were updated to match v4 semantics; broader test runs should be
watched on CI before merge.
- Supersedes dependabot PR #7570 (3.2.4 → 3.2.6); that PR should be
closed.

## Model Used

- Claude (Anthropic) — `claude-opus-4-7`, extended thinking, tool use
enabled

## 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
- [ ] I have run tests locally and they pass
- [ ] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge

Closes #7570
2026-06-05 21:11:32 -07:00
Devin Foley 486fb88a15 Add Cloudflare sandbox provider plugin (#5687)
> _Stacked on top of #5685#5686. Diff against master includes commits
from earlier PRs in the stack — review focuses on the two new commits
(`Extend sandbox callback bridge for Worker-hosted plugins` + `Add
Cloudflare sandbox provider plugin`)._

## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - Each agent runs in a sandbox environment, and operators choose which
provider backs that sandbox — today E2B and Daytona are bundled with the
platform
> - Cloudflare Workers + Durable Objects + the Sandbox SDK offer a
credible new option: globally distributed, cheap idle, and
operator-deployable as a single Worker
> - To plug it in, Paperclip needs (a) a provider plugin that speaks the
`PaperclipPluginManifestV1` lifecycle and (b) a small operator-deployed
Worker — the **bridge** — that adapts Paperclip's runtime RPCs to the
Cloudflare Sandbox SDK
> - The plugin extends the existing sandbox-callback-bridge with a
`bridge.transport: "worker"` discriminator so the platform routes
runtime RPCs through the Worker bridge instead of the in-process runner
> - This pull request adds the plugin, the bridge Worker template, and
the supporting adapter-utils + server hooks the new transport needs
> - The benefit is that operators can run sandboxes on Cloudflare's edge
with no new platform code beyond installing the plugin and deploying the
Worker

## What Changed

**Shared support (`Extend sandbox callback bridge for Worker-hosted
plugins`):**

- `packages/adapter-utils/src/sandbox-callback-bridge.{ts,test.ts}`:
expose `expectedHostHeader` so plugin-side bridge clients can verify the
canonical request envelope before forwarding.
- `packages/adapter-utils/src/command-managed-runtime.{ts,test.ts}`:
relax the always-fresh runner construction so callers can re-use a
runner across exec calls (Worker-hosted bridges hold the runner inside a
Durable Object).
- `server/src/services/environment-runtime.ts` +
`environment-runtime.test.ts`: route Worker-hosted bridges through the
same env-shaping path as E2B and pin the `requestEnv` contract.
- `server/src/services/plugin-environment-driver.ts`: thread an optional
`issueId` through the runtime descriptor so bridges can scope leases to
the originating issue (used by Cloudflare to map a sandbox to the
issue/workflow for billing and audit).
- `packages/plugins/sdk/src/protocol.ts`: add `issueId?` to
`PluginEnvironmentDriverBaseParams` and the new `bridge.transport:
"worker"` discriminator that the new plugin declares.
- `server/__tests__/heartbeat-plugin-environment.test.ts`: pin the
heartbeat path against the new runtime descriptor.

**The Cloudflare plugin itself (`Add Cloudflare sandbox provider
plugin`):**

- `packages/plugins/sandbox-providers/cloudflare/`: plugin entry,
manifest, plugin runtime (lifecycle + bridge client), config parsing,
and Vitest coverage. Manifest declares `bridge.transport: "worker"` so
the platform routes runtime RPCs through the bridge client.
- `bridge-template/`: a Worker template the operator deploys with
`wrangler`. Owns Durable Object-backed sessions (`sessions.ts`),
exec/stream routes (`exec.ts`, `routes.ts`), and an HMAC auth layer
(`auth.ts`) that pins the `Host` header surface. Includes the
SDK-contract-correct exec implementation, lease recovery, and chunked
stdout/stderr streaming.
- Tests cover lease/session handoff (`bridge-template/src/exec.test.ts`,
`routes.test.ts`), bridge client request shaping
(`src/bridge-client.test.ts`), and end-to-end plugin behavior
(`src/plugin.test.ts`) including streamed exec output. 27 tests in
total.
- `README.md` walks the operator through deploying the bridge Worker,
registering the plugin, and configuring the runtime.

## Verification

- `pnpm typecheck`
- `pnpm exec vitest run --no-coverage
packages/adapter-utils/src/sandbox-callback-bridge.test.ts
packages/adapter-utils/src/command-managed-runtime.test.ts
server/src/__tests__/environment-runtime.test.ts
server/src/__tests__/heartbeat-plugin-environment.test.ts`
- `(cd packages/plugins/sandbox-providers/cloudflare && pnpm test)` — 27
passing

For an operator-side smoke test:

1. Deploy the bridge: `cd
packages/plugins/sandbox-providers/cloudflare/bridge-template &&
wrangler deploy`
2. Register the plugin in your Paperclip instance, point its bridge URL
at the deployed Worker, set the HMAC shared secret.
3. Create a sandbox environment whose provider is `cloudflare`, then run
a Codex or Claude job against it.

## Risks

- Adds a new `bridge.transport: "worker"` code path, but the existing
E2B / Daytona transports go through the same shaped helpers and have
explicit test coverage that pins their behavior unchanged.
- The Worker bridge stores session state in a Durable Object; operator
instances must be aware of the corresponding Cloudflare costs (DO
requests, storage). Documented in the README.
- The `issueId` plumbing is optional throughout — existing plugins that
don't supply it continue to work.

## 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 — N/A, no UI change
- [x] I have updated relevant documentation to reflect my changes
(plugin README, bridge-template README)
- [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>
2026-05-11 07:33:13 -07:00