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
16 changes: 8 additions & 8 deletions VENDORED.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ never a convenience.

## Ledger

| Vendored path | What was copied | Upstream repo @ commit | Why not a published package | Owner | Kill date | Kill-date test |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | ----------------- |
| `apps/sidecar` | Derived from upstream's own `apps/sidecar`: 11 shared modules, of which `signing-keypair.ts` is near-verbatim and the rest (`index.ts`, `config.ts`, `tool-materialization.ts`, `workflow-run-pack-client.ts`, …) are substantially rewritten, plus workbench-only modules. A living fork, not a frozen copy, so this row carries no tree hash. | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | An app is never npm-published, so no publish can cover the execution host; retired by consuming an upstream-published host, or by renewing this row deliberately | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/db` | `@intx/db` source (`src/`, `migrations/`, drizzle config, manifest, tsconfigs) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the `wire_projection` column/loader delta (CL-6324); retired when upstream absorbs the delta | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/hub-api` | `@intx/hub-api` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the `needs-you` approval-route reservation or the exported null-principal `resolveApproval` (CL-6345); retired when upstream absorbs the deltas | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/hub-sessions` | `@intx/hub-sessions` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the usage forward (CL-5879), pack-acceptance fixes, adopted deploy front, wire-projection writer, event-collector serialization, or anchor ordering | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/workflow` | `@intx/workflow` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the `onBodyFailure` trigger policy and its projection (CL-6326, CL-6324); retired when upstream absorbs the delta | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/workflow-deploy` | `@intx/workflow-deploy` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | Carries no delta of its own, but must bind against the vendored `@intx/workflow` (whose `onBodyFailure` field flows through the projection it hashes); retired with the workflow delta | sawyer | 2026-09-19 | `check:killdates` |
| Vendored path | What was copied | Upstream repo @ commit | Why not a published package | Owner | Kill date | Kill-date test |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ | ---------- | ----------------- |
| `apps/sidecar` | Derived from upstream's own `apps/sidecar`: 11 shared modules, of which `signing-keypair.ts` is near-verbatim and the rest (`index.ts`, `config.ts`, `tool-materialization.ts`, `workflow-run-pack-client.ts`, …) are substantially rewritten, plus workbench-only modules. A living fork, not a frozen copy, so this row carries no tree hash. | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | An app is never npm-published, so no publish can cover the execution host; retired by consuming an upstream-published host, or by renewing this row deliberately | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/db` | `@intx/db` source (`src/`, `migrations/`, drizzle config, manifest, tsconfigs) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the `wire_projection` column/loader delta (CL-6324); retired when upstream absorbs the delta | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/hub-api` | `@intx/hub-api` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the `needs-you` approval-route reservation or the exported null-principal `resolveApproval` (CL-6345); retired when upstream absorbs the deltas | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/hub-sessions` | `@intx/hub-sessions` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the usage forward (CL-5879), pack-acceptance fixes, adopted deploy front, wire-projection writer, event-collector serialization, or anchor ordering | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/workflow` | `@intx/workflow` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the `onBodyFailure` trigger policy and its projection (CL-6326, CL-6324); retired when upstream absorbs the delta | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/workflow-deploy` | `@intx/workflow-deploy` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | Carries no delta of its own, but must bind against the vendored `@intx/workflow` (whose `onBodyFailure` field flows through the projection it hashes); retired with the workflow delta | sawyer | 2026-09-19 | `check:killdates` |
| `vendor/intx/workflow-host` | `@intx/workflow-host` source (`src/`, manifest, tsconfig) | [faremeter/interchange](https://github.com/faremeter/interchange) @ `b5580a02` (v0.3.0) | npm 0.3.0 covers the base package but not the empty-mail drop (CL-6164), the action/loop runtime bind (CL-6325; its adapters live in `packages/workflow-host-actions` since CL-6435), or the body-spawn authorize/credential threading (CL-6448); retired when upstream absorbs the deltas | sawyer | 2026-09-19 | `check:killdates` |

The pinned commit `b5580a02` is upstream's `v0.3.0` release tag, 16 commits
Expand Down
35 changes: 30 additions & 5 deletions apps/web/src/connect-service-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
// hub's connect-settling hook posts the in-room resume message — so no
// client-side event fold is needed for the flip.
import type {
ConnectAffordance,
ConnectServiceActions,
ConnectServiceQuery,
} from "@corbits/chat-ui";
Expand All @@ -23,7 +22,11 @@ import {
listProviders,
oauthStartHref,
} from "@corbits/settings-ui";
import { connectMcpPreset, listMcpPresets } from "@corbits/plugins-ui";
import {
connectMcpPreset,
listMcpPresets,
McpServersApiError,
} from "@corbits/plugins-ui";
import { CONNECTOR_REGISTRY } from "@workbench/connections/registry";
import { mcpPresetBySlug } from "@workbench/connections/mcp-presets";

Expand All @@ -50,9 +53,17 @@ export function createChatConnectServiceActions(
const presets = await listMcpPresets(tenantId);
const listed = presets.find((entry) => entry.slug === preset.slug);
if (listed?.connected === true) return { kind: "connected" };
const affordance: ConnectAffordance =
preset.connectionMode === "keyless" ? "keyless" : "oauth";
return { kind: "disconnected", affordance };
if (preset.connectionMode === "keyless") {
return { kind: "disconnected", affordance: "keyless" };
}
if (preset.connectionMode === "token") {
return {
kind: "disconnected",
affordance: "api-key",
docsUrl: preset.docsUrl,
};
}
return { kind: "disconnected", affordance: "oauth" };
}

const descriptor = CONNECTOR_REGISTRY[slug];
Expand Down Expand Up @@ -123,6 +134,20 @@ export function createChatConnectServiceActions(
},
async submitKey(connectorId, key) {
const slug = bareConnectorId(connectorId);
const preset = mcpPresetBySlug(slug);
if (preset !== undefined && preset.connectionMode === "token") {
try {
await connectMcpPreset(tenantId, preset.slug, key);
} catch (cause) {
const message =
cause instanceof McpServersApiError
? cause.message
: "Couldn't connect with that token. Try again.";
return { ok: false, message };
}
fanOut(connectorId, { kind: "connected" });
return { ok: true };
}
try {
await completeConnectorCredential(tenantId, slug, key);
} catch (cause) {
Expand Down
109 changes: 109 additions & 0 deletions apps/web/test/connect-service-actions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// The chat connect card's host port for MCP presets: a token preset
// (GitHub MCP) resolves to the key-paste affordance with its docs link,
// and a submitted key rides the preset connect route — never the
// fixed-registry credential route.

import { afterEach, describe, expect, test } from "bun:test";

import { createChatConnectServiceActions } from "../src/connect-service-actions";

const realFetch = globalThis.fetch;

afterEach(() => {
globalThis.fetch = realFetch;
});

type RecordedCall = {
readonly path: string;
readonly method: string;
readonly body?: unknown;
};

function stubFetch(respond: (path: string, method: string) => Response) {
const calls: RecordedCall[] = [];
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
const path = typeof input === "string" ? input : String(input);
const method = init?.method ?? "GET";
calls.push({
path,
method,
...(typeof init?.body === "string"
? { body: JSON.parse(init.body) as unknown }
: {}),
});
return respond(path, method);
}) as unknown as typeof fetch;
return calls;
}

const PRESET_LIST = {
data: [
{
slug: "github-mcp",
displayName: "GitHub MCP",
description: "Search code, work with issues and pull requests.",
url: "https://api.githubcopilot.com/mcp/",
connectionMode: "token",
docsUrl: "https://github.com/settings/tokens",
connected: false,
},
],
};

describe("createChatConnectServiceActions with a token preset", () => {
test("getConnectState maps a disconnected token preset to key-paste with its docs link", async () => {
stubFetch(() => new Response(JSON.stringify(PRESET_LIST)));
const actions = createChatConnectServiceActions("tnt_1", "/bench");

const state = await actions.getConnectState("github-mcp");

expect(state).toEqual({
kind: "disconnected",
affordance: "api-key",
docsUrl: "https://github.com/settings/tokens",
});
});

test("submitKey connects the preset with the pasted token via the mcp-servers route", async () => {
const calls = stubFetch((_path, method) => {
if (method === "POST") {
return new Response(
JSON.stringify({
slug: "github-mcp",
name: "GitHub MCP",
url: "https://api.githubcopilot.com/mcp/",
toolCount: 40,
}),
);
}
return new Response(JSON.stringify(PRESET_LIST));
});
const actions = createChatConnectServiceActions("tnt_1", "/bench");

const result = await actions.submitKey("github-mcp", "ghp_pasted");

expect(result).toEqual({ ok: true });
const post = calls.find((call) => call.method === "POST");
expect(post?.path).toBe("/api/tenants/tnt_1/mcp-servers");
expect(post?.body).toMatchObject({
presetSlug: "github-mcp",
token: "ghp_pasted",
});
});

test("a connected token preset reads back connected", async () => {
stubFetch(
() =>
new Response(
JSON.stringify({
data: [{ ...PRESET_LIST.data[0], connected: true }],
}),
),
);
const actions = createChatConnectServiceActions("tnt_1", "/bench");

expect(await actions.getConnectState("github-mcp")).toEqual({
kind: "connected",
});
});
});
4 changes: 1 addition & 3 deletions apps/web/test/routes.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,7 @@ describe("routes render", () => {
expect(activeFooterLabel(markup)).toBe("Skills");
});

test.each([
["/plugins/linear", "linear", "Plugins"],
])(
test.each([["/plugins/linear", "linear", "Plugins"]])(
"%s titles the detail placeholder %s with its roster row lit",
async (path, slug, footerLabel) => {
const markup = await renderApp(path);
Expand Down
Loading
Loading