Expose the Captun worker as a package subpath#14
Merged
Conversation
commit: |
mmkal
commented
May 22, 2026
mmkal
added a commit
to iterate/iterate
that referenced
this pull request
May 26, 2026
## Summary
Tighten OS's deployed e2e lane around codemode, MCP, and project egress.
After this merges, preview CI can create disposable OS projects, run
codemode scripts against the deployed worker, intercept project egress
with Captun, and prove that a third-party MCP server is reachable
through a real public OS-hosted tunnel.
## What Changes
- Consolidates OS e2e entrypoints around `pnpm e2e`, moves the preview
smoke into the Vitest lane, and updates the e2e docs to use
`APP_CONFIG_BASE_URL`/Doppler-backed config instead of bespoke
`test:e2e:*` scripts.
- Reworks the disposable OS project fixture so tests get per-test
project slugs, stream paths, typed codemode script helpers, stream
append helpers, project egress intercept tunnels, and public OS-hosted
Captun tunnels.
- Refactors codemode/MCP e2e coverage onto the fixture, including
codemode execution, provider instructions, project egress interception,
secret-withholding behavior, OpenAPI providers, OS/oRPC tools, AI calls,
repo/workspace tools, subagent calls, and true deployed project routing.
- Keeps the deployed project MCP smoke focused on real client behavior
while generating the codemode proof script from inline TypeScript
instead of maintaining a large hand-written string.
- Adds internal e2e lint rules for the testing style we want: `test`
over `it`, no lifecycle hooks, no `vi.mock`, helpers after tests,
object-level property matchers, and required `.ts` extensions for
relative imports.
- Improves validation diagnostics by adding local Standard Schema
helpers and logging prettier schema failure details from the oRPC
OpenAPI handler.
- Uses the published/pkg-pr-new Captun package instead of local type
shims and wires Captun into OS as an internal worker route at
`/__iterate/captun/<name>`, protected by the OS admin token for
WebSocket tunnel setup.
- Updates the MCP tunnel e2e to define an inline
`@modelcontextprotocol/sdk` `McpServer` with
`WebStandardStreamableHTTPServerTransport`, then exposes that server
through the OS-hosted Captun tunnel before codemode calls its tool.
- Caps Alchemy-generated Worker metadata tags at Cloudflare's 10-tag
limit so adding the Captun Durable Object binding does not break preview
deploys.
## Examples
The fixture now lets a test create a project and intercept codemode
egress without standing up a separate public mock service:
```ts
await using fixture = await createTestProjectFixture({
egressFetch: async (request) => Response.json({ url: request.url }),
});
const result = await fixture.executeCodemodeScript(async () => {
const response = await fetch("https://example.com/from-codemode");
return response.json();
});
expect(result.success()).toMatchObject({
url: "https://example.com/from-codemode",
});
```
And the MCP e2e now uses the real SDK server/transport inline:
```ts
const mcpServer = new McpServer({ name: "e2e-public-tunnel-mcp", version: "1.0.0" });
const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });
await mcpServer.connect(transport);
using tunnel = await createOsCaptunTunnel({
fetch: (request) => transport.handleRequest(request),
});
// Codemode registers an MCP provider using tunnel.url + "/mcp" and calls its tools.
```
## Dependency
This temporarily depends on iterate/captun#14 through
`https://pkg.pr.new/captun@14`. Once Captun publishes a release with the
`captun/worker` export, this branch can move back to a semver
dependency.
## Verification
- `pnpm --dir apps/os typecheck`
- `pnpm lint`
- from `apps/os`: `doppler run --config preview_2 -- pnpm e2e -t "third
party mcp and call tools"`
- GitHub checks passed for generate, lint/typecheck, tests, preview
deploy, and preview e2e.
<!-- CLOUDFLARE_PREVIEW -->
## Environment Config Lease
<!-- CLOUDFLARE_PREVIEW_STATE -->
<!--
{
"apps": {
"os": {
"appDisplayName": "OS",
"appSlug": "os",
"status": "deployed",
"updatedAt": "2026-05-23T13:12:26.705Z",
"headSha": "faff89524a44f7889b8a50fb94b59059c8e98c20",
"message": null,
"publicUrl": "https://os.iterate-preview-2.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/26333548637",
"shortSha": "faff895"
},
"example": {
"appDisplayName": "Example",
"appSlug": "example",
"status": "deployed",
"updatedAt": "2026-05-22T09:27:22.250Z",
"headSha": "c64118518f74b7ec354b46456211bd9b40f82db9",
"message": null,
"publicUrl": "https://example.iterate-preview-2.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/26279600545",
"shortSha": "c641185"
},
"semaphore": {
"appDisplayName": "Semaphore",
"appSlug": "semaphore",
"status": "deployed",
"updatedAt": "2026-05-22T09:27:27.114Z",
"headSha": "c64118518f74b7ec354b46456211bd9b40f82db9",
"message": null,
"publicUrl": "https://semaphore.iterate-preview-2.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/26279600545",
"shortSha": "c641185"
},
"events": {
"appDisplayName": "Events",
"appSlug": "events",
"status": "deployed",
"updatedAt": "2026-05-22T09:27:46.105Z",
"headSha": "c64118518f74b7ec354b46456211bd9b40f82db9",
"message": null,
"publicUrl": "https://events.iterate-preview-2.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/26279600545",
"shortSha": "c641185"
}
},
"environmentConfigLease": {
"dopplerConfig": "preview_2",
"leasedUntil": 1779545406542,
"leaseId": "faed0ede-4270-4732-9212-518b95b39ed0",
"slug": "preview-2",
"type": "environment-config-lease"
}
}
-->
<!-- /CLOUDFLARE_PREVIEW_STATE -->
Lease: `preview-2`
Doppler config: `preview_2`
Type: `environment-config-lease`
Leased until: 2026-05-23T14:10:06.542Z
### Events
Status: deployed
Commit: `c641185`
Preview: https://events.iterate-preview-2.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/26279600545)
Updated: 2026-05-22T09:27:46.105Z
### Example
Status: deployed
Commit: `c641185`
Preview: https://example.iterate-preview-2.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/26279600545)
Updated: 2026-05-22T09:27:22.250Z
### OS
Status: deployed
Commit: `faff895`
Preview: https://os.iterate-preview-2.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/26333548637)
Updated: 2026-05-23T13:12:26.705Z
### Semaphore
Status: deployed
Commit: `c641185`
Preview: https://semaphore.iterate-preview-2.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/26279600545)
Updated: 2026-05-22T09:27:27.114Z
<!-- /CLOUDFLARE_PREVIEW -->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches project egress request handling and secret-substitution error
behavior, which is security-sensitive and can affect outbound request
headers; also updates deployed e2e coverage, so failures could be
environment-dependent.
>
> **Overview**
> **Egress secret substitution is refactored** to return a tuple of
`[Response|null, substitutedHeaders]` (instead of an `ok/error` object
and `Headers`), emitting a ready-to-return `400` JSON `Response` on
parse/missing-secret failures while still returning any substitutions
performed before the failure.
>
> **Project egress fetch now applies substitutions by overlaying
returned header values** onto the outbound request headers, and returns
the new error `Response` directly when substitution fails.
>
> **Deployed codemode/MCP e2e coverage is tightened** by switching the
MCP provider-stack test to create a disposable project fixture, derive
the project MCP URL from `APP_CONFIG_PROJECT_HOSTNAME_BASES` (with
optional `OS_E2E_MCP_URL` override), and simplifying the codemode proof
to use repo creation/getInfo and fixture-based codemode script
generation. Lint config is updated to allow additional globals
(`crypto`, `setTimeout`) and adjust isolated-function selectors for
fixture codemode helpers.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
faff895. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This was referenced Jun 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Expose the existing Captun Worker entrypoint as a package subpath. Published consumers can now import
captun/workerand get the default Worker handler plus theCaptunServerShardDurable Object class from the compiled package.Intended effect
Downstream Cloudflare stacks can deploy Captun inside their own app instead of depending on a separate Captun deployment or threading custom egress fetch hooks through tests:
The source export points to
src/worker.tsfor repo-local usage, and the publish export points todist/worker.js/dist/worker.d.tsso packed and published packages expose the same subpath.Verification
pnpm checkpnpm testpnpm pack --pack-destination ignoreme-packtar -xOf ignoreme-pack/captun-0.0.2.tgz package/package.jsonconfirmed./workermaps to./dist/worker.jsand./dist/worker.d.ts.pnpm publish:dry-runrancheck,test, andprepack, then completed successfully forcaptun@0.0.2.Note
Low Risk
Low risk packaging/type-surface change that primarily affects how consumers import the Worker; minimal runtime logic changes beyond the env type definition.
Overview
Exposes the existing Worker entrypoint as a new
captun/workerpackage subpath for both source usage and published builds, mapping tosrc/worker.tsanddist/worker.*respectively.Updates
src/worker.tsto define its ownCaptunEnvshape (including theCaptunServerShardDO namespace) instead of extending a repo-localEnvtype, improving portability of the generated.d.tsfor downstream deployments.Reviewed by Cursor Bugbot for commit 5f1a8f4. Bugbot is set up for automated code reviews on this repo. Configure here.