Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .changeset/sdk-no-private-api-leak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
"@executor-js/sdk": minor
"@executor-js/plugin-mcp": minor
"@executor-js/plugin-graphql": minor
"@executor-js/plugin-openapi": minor
"@executor-js/plugin-onepassword": minor
"@executor-js/plugin-google-discovery": minor
---

Stop the published plugin bundles from importing `@executor-js/api`. The
private server package was being pulled into every SDK chunk via
`import { InternalError } from "@executor-js/api"` (in each plugin's
group definition) and `import { addGroup, capture } from
"@executor-js/api"` (via the SDK's transitive import of its own
handlers). Because `@executor-js/api` is `private: true`, plain Node
ESM consumers hit `Cannot find package '@executor-js/api'` on
`import("@executor-js/plugin-mcp/core")` (and the same for graphql /
openapi).

Fix:

- `InternalError` (the wire-level 500 schema) moved to
`@executor-js/sdk/core`. `@executor-js/api` re-exports it for
back-compat, so server code is unaffected.
- The plugin SDK factories (`mcpPlugin`, `graphqlPlugin`,
`openApiPlugin`, `onepasswordPlugin`, `googleDiscoveryPlugin`) no
longer carry HTTP `routes` / `handlers` / `extensionService`. The
optional fields are layered on by a new HTTP-augmented variant
exposed from the `/api` subpath (`mcpHttpPlugin`,
`graphqlHttpPlugin`, `openApiHttpPlugin`, `onepasswordHttpPlugin`,
`googleDiscoveryHttpPlugin`).
- Hosts that mount plugin HTTP routes should switch their imports to
the `/api` subpath and the `*HttpPlugin` factory name.
- SDK-only consumers keep importing from the package root and no
longer transitively require `@executor-js/api`.

Breaking for hosts that read `mcpPlugin(opts).routes` /
`.handlers` / `.extensionService` directly off the SDK factory's
return value — switch to the `*HttpPlugin` factory from
`@executor-js/plugin-*/api`.
4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ jobs:
# secret is configured.
GITHUB_TOKEN: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}

- name: Smoke test packed @executor-js library packages
if: steps.changesets.outputs.hasChangesets == 'false'
run: bun run release:smoke:packages

- name: Publish @executor-js library packages
if: steps.changesets.outputs.hasChangesets == 'false'
run: bun run release:publish:packages
Expand Down
12 changes: 6 additions & 6 deletions apps/cloud/executor.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineExecutorConfig } from "@executor-js/sdk";
import { openApiPlugin } from "@executor-js/plugin-openapi";
import { mcpPlugin } from "@executor-js/plugin-mcp";
import { graphqlPlugin } from "@executor-js/plugin-graphql";
import { openApiHttpPlugin } from "@executor-js/plugin-openapi/api";
import { mcpHttpPlugin } from "@executor-js/plugin-mcp/api";
import { graphqlHttpPlugin } from "@executor-js/plugin-graphql/api";
import { workosVaultPlugin, type WorkOSVaultClient } from "@executor-js/plugin-workos-vault";

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -39,11 +39,11 @@ export default defineExecutorConfig({
dialect: "pg",
plugins: ({ workosCredentials, workosVaultClient }: CloudPluginDeps = {}) =>
[
openApiPlugin(),
mcpPlugin({
openApiHttpPlugin(),
mcpHttpPlugin({
dangerouslyAllowStdioMCP: false,
}),
graphqlPlugin(),
graphqlHttpPlugin(),
workosVaultPlugin({
credentials: workosCredentials ?? { apiKey: "", clientId: "" },
...(workosVaultClient ? { client: workosVaultClient } : {}),
Expand Down
20 changes: 10 additions & 10 deletions apps/local/executor.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { defineExecutorConfig } from "@executor-js/sdk";
import type { ConfigFileSink } from "@executor-js/config";
import { openApiPlugin } from "@executor-js/plugin-openapi";
import { mcpPlugin } from "@executor-js/plugin-mcp";
import { googleDiscoveryPlugin } from "@executor-js/plugin-google-discovery";
import { graphqlPlugin } from "@executor-js/plugin-graphql";
import { openApiHttpPlugin } from "@executor-js/plugin-openapi/api";
import { mcpHttpPlugin } from "@executor-js/plugin-mcp/api";
import { googleDiscoveryHttpPlugin } from "@executor-js/plugin-google-discovery/api";
import { graphqlHttpPlugin } from "@executor-js/plugin-graphql/api";
import { keychainPlugin } from "@executor-js/plugin-keychain";
import { fileSecretsPlugin } from "@executor-js/plugin-file-secrets";
import { onepasswordPlugin } from "@executor-js/plugin-onepassword";
import { onepasswordHttpPlugin } from "@executor-js/plugin-onepassword/api";

// ---------------------------------------------------------------------------
// Single source of truth for the local app's plugin list.
Expand All @@ -27,12 +27,12 @@ export default defineExecutorConfig({
dialect: "sqlite",
plugins: ({ configFile }: LocalPluginDeps = {}) =>
[
openApiPlugin({ configFile }),
mcpPlugin({ dangerouslyAllowStdioMCP: true, configFile }),
googleDiscoveryPlugin(),
graphqlPlugin({ configFile }),
openApiHttpPlugin({ configFile }),
mcpHttpPlugin({ dangerouslyAllowStdioMCP: true, configFile }),
googleDiscoveryHttpPlugin(),
graphqlHttpPlugin({ configFile }),
keychainPlugin(),
fileSecretsPlugin(),
onepasswordPlugin(),
onepasswordHttpPlugin(),
] as const,
});
8 changes: 4 additions & 4 deletions apps/marketing/src/pages/api/detect.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { APIRoute } from "astro";
import { Effect } from "effect";
import { createExecutor, makeTestConfig, type Tool } from "@executor-js/sdk";
import { openApiPlugin } from "@executor-js/plugin-openapi";
import { graphqlPlugin } from "@executor-js/plugin-graphql";
import { googleDiscoveryPlugin } from "@executor-js/plugin-google-discovery";
import { openApiHttpPlugin } from "@executor-js/plugin-openapi/api";
import { graphqlHttpPlugin } from "@executor-js/plugin-graphql/api";
import { googleDiscoveryHttpPlugin } from "@executor-js/plugin-google-discovery/api";

export const prerender = false;

Expand Down Expand Up @@ -68,7 +68,7 @@ export const POST: APIRoute = async ({ request }) => {

const program = Effect.gen(function* () {
const config = makeTestConfig({
plugins: [openApiPlugin(), graphqlPlugin(), googleDiscoveryPlugin()],
plugins: [openApiHttpPlugin(), graphqlHttpPlugin(), googleDiscoveryHttpPlugin()],
});
const executor = yield* createExecutor(config);

Expand Down
8 changes: 6 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"release:publish:packages": "bun run scripts/publish-packages.ts",
"release:publish:packages:dry-run": "bun run scripts/publish-packages.ts --dry-run",
"release:publish:packages:prepare": "bun run scripts/publish-packages.ts --prepare-only",
"release:smoke:packages": "bun run scripts/smoke-test-packed.ts",
"clean": "bun run scripts/clean.ts",
"prepare": "effect-language-service patch && effect-tsgo patch"
},
Expand Down
15 changes: 6 additions & 9 deletions packages/core/api/src/observability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,13 @@ import { Cause, Context, Effect, Layer, Option, Result, Schema } from "effect";
import { HttpServerResponse } from "effect/unstable/http";
import { HttpApiMiddleware, type HttpApi, type HttpApiGroup } from "effect/unstable/httpapi";
import type { StorageFailure } from "@executor-js/storage-core";
import { InternalError } from "@executor-js/sdk/core";

/** Public 500 surface. Opaque by schema. */
export class InternalError extends Schema.TaggedErrorClass<InternalError>()(
"InternalError",
{
/** Opaque correlation id for backend lookup (Sentry event id, log line, etc.). */
traceId: Schema.String,
},
{ httpApiStatus: 500 },
) {}
// Re-export so existing `@executor-js/api` consumers keep working.
// The schema lives in the SDK so plugin `HttpApiGroup` definitions can
// reference it without dragging this server-only package into their
// SDK chunks.
export { InternalError };

export interface ErrorCaptureShape {
/**
Expand Down
20 changes: 20 additions & 0 deletions packages/core/sdk/src/api-errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ---------------------------------------------------------------------------
// Wire-level HTTP errors. Lives in the SDK so plugin `HttpApiGroup`
// definitions (which sit on the SDK side of the dependency graph and
// must stay publishable) can declare them without dragging in the
// server-only `@executor-js/api` package. The HTTP edge in
// `@executor-js/api` re-exports these and pairs them with the
// translation/capture helpers used by handlers.
// ---------------------------------------------------------------------------

import { Schema } from "effect";

/** Public 500 surface. Opaque by schema — only `traceId` crosses the wire. */
export class InternalError extends Schema.TaggedErrorClass<InternalError>()(
"InternalError",
{
/** Opaque correlation id for backend lookup (Sentry event id, log line, etc.). */
traceId: Schema.String,
},
{ httpApiStatus: 500 },
) {}
3 changes: 3 additions & 0 deletions packages/core/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,6 @@ export {
type TypeScriptRenderOptions,
type TypeScriptSchemaPreview,
} from "./schema-types";

// Wire-level HTTP error schemas usable by plugin HttpApiGroup definitions.
export { InternalError } from "./api-errors";
6 changes: 5 additions & 1 deletion packages/plugins/google-discovery/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@
"typecheck:slow": "bunx tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"@executor-js/api": "workspace:*",
"@executor-js/sdk": "workspace:*",
"effect": "catalog:"
},
"devDependencies": {
"@effect/atom-react": "catalog:",
"@effect/vitest": "catalog:",
"@executor-js/api": "workspace:*",
"@executor-js/react": "workspace:*",
"@types/node": "catalog:",
"@types/react": "catalog:",
Expand All @@ -71,6 +71,7 @@
},
"peerDependencies": {
"@effect/atom-react": "catalog:",
"@executor-js/api": "workspace:*",
"@executor-js/react": "workspace:*",
"@tanstack/react-router": "catalog:",
"react": "catalog:"
Expand All @@ -85,6 +86,9 @@
"@tanstack/react-router": {
"optional": true
},
"@executor-js/api": {
"optional": true
},
"@executor-js/react": {
"optional": true
}
Expand Down
3 changes: 1 addition & 2 deletions packages/plugins/google-discovery/src/api/group.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi";
import { Schema } from "effect";
import { ScopeId, SecretBackedValue } from "@executor-js/sdk/core";
import { InternalError } from "@executor-js/api";
import { InternalError, ScopeId, SecretBackedValue } from "@executor-js/sdk/core";
import { GoogleDiscoveryParseError, GoogleDiscoverySourceError } from "../sdk/errors";
import { GoogleDiscoveryStoredSourceSchema } from "../sdk/stored-source";

Expand Down
21 changes: 21 additions & 0 deletions packages/plugins/google-discovery/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,23 @@
import { definePlugin } from "@executor-js/sdk/core";

import { googleDiscoveryPlugin } from "../sdk/plugin";
import { GoogleDiscoveryGroup } from "./group";
import { GoogleDiscoveryExtensionService, GoogleDiscoveryHandlers } from "./handlers";

export { GoogleDiscoveryGroup } from "./group";
export { GoogleDiscoveryExtensionService, GoogleDiscoveryHandlers } from "./handlers";

// HTTP-augmented variant of `googleDiscoveryPlugin`. The returned
// plugin carries the HTTP `routes`, `handlers`, and `extensionService`
// so a host can mount the Google Discovery HTTP surface. Hosts that
// compose an `HttpApi` should import this. SDK-only consumers stay on
// `@executor-js/plugin-google-discovery` and never load
// `@executor-js/api`.
export const googleDiscoveryHttpPlugin = definePlugin(
(options?: Parameters<typeof googleDiscoveryPlugin>[0]) => ({
...googleDiscoveryPlugin(options),
routes: () => GoogleDiscoveryGroup,
handlers: () => GoogleDiscoveryHandlers,
extensionService: GoogleDiscoveryExtensionService,
}),
);
10 changes: 4 additions & 6 deletions packages/plugins/google-discovery/src/sdk/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import {
type ToolAnnotations,
} from "@executor-js/sdk/core";

import { GoogleDiscoveryGroup } from "../api/group";
import { GoogleDiscoveryExtensionService, GoogleDiscoveryHandlers } from "../api/handlers";

import {
googleDiscoverySchema,
makeGoogleDiscoveryStore,
Expand Down Expand Up @@ -546,7 +543,8 @@ export const googleDiscoveryPlugin = definePlugin(() => ({
// the `authorization-code` strategy's tokenEndpoint), so refresh
// reaches Google through the unified code path.

routes: () => GoogleDiscoveryGroup,
handlers: () => GoogleDiscoveryHandlers,
extensionService: GoogleDiscoveryExtensionService,
// HTTP transport (routes/handlers/extensionService) is layered on by
// the api-aware factory in `@executor-js/plugin-google-discovery/api`.
// Hosts that want the HTTP surface import the plugin from there;
// SDK-only consumers stay on this entry and avoid the server-only deps.
}));
3 changes: 1 addition & 2 deletions packages/plugins/graphql/src/api/group.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
import { Schema } from "effect";
import { ScopeId } from "@executor-js/sdk/core";
import { InternalError } from "@executor-js/api";
import { InternalError, ScopeId } from "@executor-js/sdk/core";

import { GraphqlIntrospectionError, GraphqlExtractionError } from "../sdk/errors";
import {
Expand Down
18 changes: 18 additions & 0 deletions packages/plugins/graphql/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
import { definePlugin } from "@executor-js/sdk/core";

import { graphqlPlugin, type GraphqlPluginOptions } from "../sdk/plugin";
import { GraphqlGroup } from "./group";
import { GraphqlHandlers, GraphqlExtensionService } from "./handlers";

export { GraphqlGroup } from "./group";
export { GraphqlHandlers, GraphqlExtensionService } from "./handlers";

// HTTP-augmented variant of `graphqlPlugin`. The returned plugin
// carries the HTTP `routes`, `handlers`, and `extensionService` so a
// host can mount the GraphQL HTTP surface. Hosts that compose an
// `HttpApi` should import this. SDK-only consumers stay on
// `@executor-js/plugin-graphql` and never load `@executor-js/api`.
export const graphqlHttpPlugin = definePlugin((options?: GraphqlPluginOptions) => ({
...graphqlPlugin(options),
routes: () => GraphqlGroup,
handlers: () => GraphqlHandlers,
extensionService: GraphqlExtensionService,
}));
11 changes: 4 additions & 7 deletions packages/plugins/graphql/src/sdk/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ import { Effect, Option, Schema } from "effect";
import type { Layer } from "effect";
import { HttpClient } from "effect/unstable/http";

import { GraphqlGroup } from "../api/group";
import { GraphqlExtensionService, GraphqlHandlers } from "../api/handlers";

import {
ConnectionId,
ConfiguredCredentialBinding,
Expand Down Expand Up @@ -1183,9 +1180,9 @@ export const graphqlPlugin = definePlugin((options?: GraphqlPluginOptions) => {

return null;
}),

routes: () => GraphqlGroup,
handlers: () => GraphqlHandlers,
extensionService: GraphqlExtensionService,
};
// HTTP transport (routes/handlers/extensionService) is layered on by
// the api-aware factory in `@executor-js/plugin-graphql/api`. Hosts that
// want the HTTP surface import the plugin from there; SDK-only
// consumers stay on this entry and avoid the server-only deps.
});
Loading
Loading