Skip to content

Expose the Captun worker as a package subpath#14

Merged
mmkal merged 3 commits into
mainfrom
mmkal/26/05/22/export-worker-module
May 23, 2026
Merged

Expose the Captun worker as a package subpath#14
mmkal merged 3 commits into
mainfrom
mmkal/26/05/22/export-worker-module

Conversation

@mmkal

@mmkal mmkal commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Expose the existing Captun Worker entrypoint as a package subpath. Published consumers can now import captun/worker and get the default Worker handler plus the CaptunServerShard Durable 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:

import worker, { CaptunServerShard } from "captun/worker";

export { CaptunServerShard };
export default worker;

The source export points to src/worker.ts for repo-local usage, and the publish export points to dist/worker.js/dist/worker.d.ts so packed and published packages expose the same subpath.

Verification

  • pnpm check
  • pnpm test
  • pnpm pack --pack-destination ignoreme-pack
  • tar -xOf ignoreme-pack/captun-0.0.2.tgz package/package.json confirmed ./worker maps to ./dist/worker.js and ./dist/worker.d.ts.
  • pnpm publish:dry-run ran check, test, and prepack, then completed successfully for captun@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/worker package subpath for both source usage and published builds, mapping to src/worker.ts and dist/worker.* respectively.

Updates src/worker.ts to define its own CaptunEnv shape (including the CaptunServerShard DO namespace) instead of extending a repo-local Env type, improving portability of the generated .d.ts for downstream deployments.

Reviewed by Cursor Bugbot for commit 5f1a8f4. Bugbot is set up for automated code reviews on this repo. Configure here.

@mmkal mmkal marked this pull request as ready for review May 21, 2026 23:27
@pkg-pr-new

pkg-pr-new Bot commented May 21, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/captun@14

commit: 5f1a8f4

Comment thread src/worker.ts
@mmkal mmkal merged commit 9db4f37 into main May 23, 2026
5 checks passed
@mmkal mmkal deleted the mmkal/26/05/22/export-worker-module branch May 23, 2026 11:28
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant