feat(build): agent-bundle owns the package build — bin/lib, entry conventions, stdio entry shell, tools hatch, mcp run (RFC #50 Phase 1) - #52
Conversation
…amework stdio entry shell, entry conventions, tools hatch, mcp run (RFC #50 Phase 1) One agent-bundle.config.ts now produces the npm package build alongside host artifacts: bin/lib (or the src/cli.ts / src/index.ts / src/mcp/<id>.ts conventions) lower onto the internal Rslib synthesis, factory-exporting MCP entries are wrapped in the framework stdio lifecycle shell (public as agent-bundle/mcp-entry), tools.rsbuild/tools.rspack is the single bounded bundler escape hatch, and `agent-bundle mcp run` launches a built stdio server from the target manifest. audiobook-curator migrates in-PR as proof: drops rslib.config.ts, tsconfig.build.json, and the bin shim with byte-equivalent CLI behavior.
…shell entry execFile never closes the child's stdin pipe, so a real server's transport held the generated entry alive until the 30s timeout killed it. End the child's stdin so the EOF path under test actually executes.
🦋 Changeset detectedLatest commit: 4b8f35d The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 273269d167
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await writeFile(path, `${JSON.stringify({ | ||
| ...(existsSync(projectTsconfig) ? { extends: projectTsconfig } : {}), | ||
| compilerOptions: { rootDir: options.sourceDir }, | ||
| include: [`${options.sourceDir}/**/*.ts`, `${options.sourceDir}/**/*.tsx`], |
There was a problem hiding this comment.
Exclude colocated tests from the declaration program
When a package keeps tests beneath the library source directory (for example, src/index.test.ts), these broad includes add them to the declaration program, so test-only imports or type errors can fail agent-bundle build and successful builds can publish test declarations into dist. Restrict the synthesized project to the library entry and its dependency graph, or explicitly exclude recognized test files, to uphold the documented isolation from tests.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 4b8f35d: the synthesized declaration tsconfig now excludes **/*.test.*, **/*.spec.*, and **/__tests__/** under the lib source directory, with a regression test proving a colocated src/answer.test.ts type error neither fails the build nor publishes answer.test.d.ts.
| const workspaceRoot = resolve(options.root); | ||
| return temporaryArtifact({ ...options, registry }, async (artifact) => runMcpForeground({ | ||
| artifact, | ||
| pluginDataRoot: join(workspaceRoot, '.agent-bundle', 'mcp-run', options.target, options.server), |
There was a problem hiding this comment.
Contain MCP state paths derived from server names
For an accepted MCP server name containing path components such as ../shared, this direct join traverses out of the intended <target>/<server> state directory; different targets or servers can consequently share or overwrite state, and sufficiently many .. components escape .agent-bundle entirely. Since source validation only requires server names to be nonempty, encode/hash this component or assert the resulting path remains within the MCP-run root before creating it.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 4b8f35d: server names that are not a plain single path segment now map to a content-addressed server-<sha256[0:16]> state directory (mcpServerStateDirectory), so ../shared-style names can no longer traverse out of .agent-bundle/mcp-run. Unit-tested for hostile and safe names.
…try shell, entry conventions, tools hatch, mcp run (RFC #50 Phase 1) One agent-bundle.config.ts now produces the npm package build alongside host artifacts: `bin` entries become self-executing dist/bin/<name>.js bundles (shebang, exec bit, generated main(argv) envelope) and `lib` becomes a single-entry ESM build with declarations, both lowered through the existing buildWithRslib synthesis with its invariant assertions intact. Factory- exporting MCP server entries are wrapped in the new framework stdio lifecycle shell (agent-bundle/mcp-entry: console-to-stderr guard with raw stdout restore, SIGINT 130 / SIGTERM 143, stdin-EOF exit 0, bounded shutdown, heartbeat). src/cli.ts, src/index.ts, and src/mcp/<server-id>.ts conventions fill silent config with 'conventional' provenance; config always wins. tools.rsbuild/tools.rspack is the single bundler escape hatch, merged last-but-bounded into every synthesized config. `agent-bundle mcp run` resolves the content-hashed generated entry from the target manifest and runs it in the foreground. audiobook-curator migrates in-tree as the reference consumer: rslib.config.ts, tsconfig.build.json, bin/audiobook-curator.js, src/cli-entry.ts, and the dual build script chain are deleted; package.json bin/exports now point at the framework-built dist. References #50 (Phase 1).
…gram, contain MCP state paths The synthesized declaration tsconfig now excludes *.test.*, *.spec.*, and __tests__ under the lib source directory, so colocated tests can neither fail `agent-bundle build` nor publish test declarations into dist/. MCP server names that are not a plain single path segment (e.g. `../shared`) map to a content-addressed state directory instead of traversing out of .agent-bundle/mcp-run.
…cp-entry lifecycle in the self-built stdio entry (RFC #50 Phase 2) (#57) The demo's stdio MCP entry replaces its naive run().catch bootstrap (no console guard, no signals, no stdin-EOF, no bounded shutdown) with the framework's public lifecycle API: redirectConsoleToStderr installs the stdout guard before the server module and v1.30 SDK evaluate (both deferred via dynamic import, mirroring the generated shell), then runStdioServer owns SIGINT->130, SIGTERM->143, stdin-EOF->0, bounded shutdown, and heartbeat attribution over the demo's own SDK transport. The entry stays compiled by the demo's rsbuild at the stable dist/runtime/mcp/stdio.js path pinned by packaging and tests; the structural lifecycle types accept the v1.x SDK without migration and the import bundles into the entry (no lockfile change). The transport suite gains a lifecycle test (handshake-then-act exit codes plus heartbeat), the README ownership-boundary paragraph notes the entry consumes the framework lifecycle while remaining self-built, and the generated topology doc is regenerated (one line stale on main since #52).
…user-facing scripts, align check chains (#68) * chore(examples): canonicalize defineConfig imports, drop --json from user-facing scripts, align check chains The examples split on three cosmetic conventions the audit called out. Canonicalizes the defineConfig import to 'agent-bundle/config' — the root README quick start already teaches that path — across the three examples still importing from 'agent-bundle'. Drops --json from example build and validate package scripts so pnpm build/validate speak the post-#52 human writers instead of dumping machine JSON (CI examples:check consumes exit codes only). Reorders the rsc example's check to the canonical validate && build && typecheck && test chain. Also renames the deleted src/cli-entry.ts self-executing pattern to src/cli.ts with the exported-main envelope teaching in the rsc-runtime package README and its test fixture strings. * chore(examples/audiobook-curator): canonicalize the new real defineConfig import to agent-bundle/config
Implements RFC #50 Phase 1: agent-bundle owns the package build.
Summary
One
agent-bundle.config.tsnow produces the npm package build alongside host artifacts — the second bundler config, hand-maintained build tsconfig, and hand-written bin shim that every published-package consumer previously carried are gone.bin/libconfig, lowered onto the internal Rslib synthesis (packages/agent-bundle/src/build/package-build.ts):binentries become self-executingdist/bin/<name>.jsbundles (shebang, executable bit, generatedmain(argv)process envelope when the module exports one),libbecomes a single-entry ESM library with bundled declarations. Outputs are staged and published atomically with byte/SHA-256/source-input provenance on the build result, exactly like artifact files. Declaration generation synthesizes a tsconfig thatextendsthe project's own, pinsrootDir, and includes only the entry's subtree.packages/agent-bundle/src/mcp-entry.ts, public asagent-bundle/mcp-entry, modeled on movie-library'ssrc/stdio-lifecycle.ts): console-to-stderr guard installed before the consumer module evaluates (raw stdout restored for protocol frames), SIGINT 130 / SIGTERM 143, stdin-EOF exit 0 so clients can respawn, bounded shutdown race against wedged transports, heartbeat/activity logging.agent-bundle buildwraps every factory-exporting MCP server entry in this lifecycle; self-connecting entries keep their behavior byte for byte.src/cli.ts→ package bin named afterplugin.name,src/index.ts→ library with declarations,src/mcp/<server-id>.ts→ stdio entry for a declared server naming noentry/command/url.bin: false/lib: falseopt out; explicit config always suppresses the convention. Documented indocs/entry-conventions.md.tools.rsbuild/tools.rspackescape hatch: the single blessed bundler hatch, merged last into every synthesized config (scripts, MCP entries, hooks, MCP Apps, package build) and still bounded by the artifact invariant assertions.agent-bundle mcp run --server <name> --target <target>(packages/agent-bundle/src/services/mcp-run.ts): runs one built stdio server in the foreground, resolving its content-hashed generated entry from the target manifest after artifact validation; forwards SIGINT/SIGTERM and the child's exit code; refuses non-stdio servers.rslib.config.ts,tsconfig.build.json,bin/audiobook-curator.js, andsrc/cli-entry.ts;package.jsonbinnow points at the framework-builtdist/bin/audiobook-curator.js. CLI behavior is covered by the existing parity tests, all green.agent-bundleminor.Packed-suite extension
packed-consumer.test.tsnow builds a conventional bin/lib/MCP project against the installed tarball alone (no repository source tree): asserts the shebang'd executable bin, importable lib with declarations, a self-contained generated stdio entry (noagent-bundleimport in the emitted artifact), clean exit 0 on stdin EOF, andmcp listthrough the packaged CLI. One test-only fix landed during shepherding:execFilenever closes the child's stdin pipe, so the EOF assertion now delivers the EOF explicitly (child.stdin.end()) instead of hanging a real server until timeout-kill.Test plan
pnpm build— greenpnpm typecheck(root + workbench) — greenpnpm lint(rslint, 0 errors 0 warnings) — greenpnpm test:unit— greenpnpm test:packed— green (19/19, including the new tarball-only framework-build scenario)pnpm examples:check(all examples, including migrated audiobook-curator parity suites) — greenpublint— All good