@ken-jo/agent-connector 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -16
- package/dist/action-WT7GVRCC.js +55 -0
- package/dist/action-WT7GVRCC.js.map +1 -0
- package/dist/amazon-q-COAL3764.js +276 -0
- package/dist/amazon-q-COAL3764.js.map +1 -0
- package/dist/{amp-YGV66GJT.js → amp-4U33CU7E.js} +81 -5
- package/dist/amp-4U33CU7E.js.map +1 -0
- package/dist/{antigravity-7IBYFO6P.js → antigravity-AMKKKHS4.js} +4 -4
- package/dist/{antigravity-cli-JEOUP2VN.js → antigravity-cli-XERXEH5W.js} +4 -4
- package/dist/{chunk-3JCRUROU.js → chunk-24IDL5RW.js} +13 -4
- package/dist/chunk-24IDL5RW.js.map +1 -0
- package/dist/chunk-2AVLX7GH.js +351 -0
- package/dist/chunk-2AVLX7GH.js.map +1 -0
- package/dist/{chunk-GSGDG2BV.js → chunk-4ENJK5S2.js} +106 -176
- package/dist/chunk-4ENJK5S2.js.map +1 -0
- package/dist/{chunk-ZAM4M7LI.js → chunk-5OWS5CMA.js} +136 -296
- package/dist/chunk-5OWS5CMA.js.map +1 -0
- package/dist/{chunk-PD4VFEBY.js → chunk-CPLK7GTE.js} +79 -5
- package/dist/chunk-CPLK7GTE.js.map +1 -0
- package/dist/chunk-DCKFYWGM.js +237 -0
- package/dist/chunk-DCKFYWGM.js.map +1 -0
- package/dist/chunk-EGCBZ2DC.js +83 -0
- package/dist/chunk-EGCBZ2DC.js.map +1 -0
- package/dist/chunk-F5RX62T7.js +21 -0
- package/dist/chunk-F5RX62T7.js.map +1 -0
- package/dist/chunk-FAHIMJRU.js +177 -0
- package/dist/chunk-FAHIMJRU.js.map +1 -0
- package/dist/{chunk-MEH7GC7R.js → chunk-I5MLE6S7.js} +33 -3
- package/dist/chunk-I5MLE6S7.js.map +1 -0
- package/dist/{chunk-KVAAFYRR.js → chunk-KHO5WNTP.js} +2 -18
- package/dist/chunk-KHO5WNTP.js.map +1 -0
- package/dist/{chunk-KQSNDFBN.js → chunk-LQRAPXS4.js} +3 -3
- package/dist/{chunk-3RBLIHPZ.js → chunk-O5TWCIJN.js} +27 -17
- package/dist/chunk-O5TWCIJN.js.map +1 -0
- package/dist/{chunk-WNREHWKL.js → chunk-QREL7LZJ.js} +5 -4
- package/dist/chunk-QREL7LZJ.js.map +1 -0
- package/dist/{chunk-KMAKDD3Z.js → chunk-RIMVWCZY.js} +37 -12
- package/dist/chunk-RIMVWCZY.js.map +1 -0
- package/dist/{openclaw-UZ5BMCGQ.js → chunk-URHQMFB3.js} +210 -41
- package/dist/chunk-URHQMFB3.js.map +1 -0
- package/dist/chunk-WZA4CSGJ.js +28 -0
- package/dist/chunk-WZA4CSGJ.js.map +1 -0
- package/dist/chunk-XJ4S47CT.js +44 -0
- package/dist/chunk-XJ4S47CT.js.map +1 -0
- package/dist/{claude-code-4275HQU4.js → claude-code-W4PUFZL5.js} +194 -52
- package/dist/claude-code-W4PUFZL5.js.map +1 -0
- package/dist/cli/sdk.js +5 -3
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cline-WRYMNS7Z.js +442 -0
- package/dist/cline-WRYMNS7Z.js.map +1 -0
- package/dist/{codebuff-LE3KBZGM.js → codebuff-DH5Y7GAT.js} +106 -5
- package/dist/codebuff-DH5Y7GAT.js.map +1 -0
- package/dist/{codex-4BY2ZJIW.js → codex-DDHIV767.js} +43 -11
- package/dist/codex-DDHIV767.js.map +1 -0
- package/dist/continue-K43SUHLQ.js +790 -0
- package/dist/continue-K43SUHLQ.js.map +1 -0
- package/dist/{copilot-cli-IPISKO3V.js → copilot-cli-Q3SSPT4V.js} +70 -12
- package/dist/copilot-cli-Q3SSPT4V.js.map +1 -0
- package/dist/{crush-X3E25EQG.js → crush-MBEH2R3I.js} +130 -6
- package/dist/crush-MBEH2R3I.js.map +1 -0
- package/dist/{cursor-UH3W622V.js → cursor-TMBXDYR4.js} +119 -16
- package/dist/cursor-TMBXDYR4.js.map +1 -0
- package/dist/{detect-U3E7NVNW.js → detect-KG5FG3EV.js} +5 -3
- package/dist/{detect-U3E7NVNW.js.map → detect-KG5FG3EV.js.map} +1 -1
- package/dist/{doctor-DZTWED4O.js → doctor-FLZF34Y3.js} +226 -96
- package/dist/doctor-FLZF34Y3.js.map +1 -0
- package/dist/{droid-T5VBRXZF.js → droid-JC2NT6S4.js} +298 -10
- package/dist/droid-JC2NT6S4.js.map +1 -0
- package/dist/{gemini-cli-ZSUK55UA.js → gemini-cli-ILR7M5X3.js} +62 -10
- package/dist/gemini-cli-ILR7M5X3.js.map +1 -0
- package/dist/{goose-M3RR3CZG.js → goose-SFO5BBGW.js} +112 -10
- package/dist/goose-SFO5BBGW.js.map +1 -0
- package/dist/{hermes-CUNKIAR5.js → hermes-3N7WN4WS.js} +329 -15
- package/dist/hermes-3N7WN4WS.js.map +1 -0
- package/dist/{hook-EVY7LOUQ.js → hook-XR3Z45FB.js} +16 -10
- package/dist/hook-XR3Z45FB.js.map +1 -0
- package/dist/index.d.ts +71 -3
- package/dist/index.js +23 -3
- package/dist/{install-OWAS5UIE.js → install-BSR62UHJ.js} +12 -9
- package/dist/{install-OWAS5UIE.js.map → install-BSR62UHJ.js.map} +1 -1
- package/dist/introspect-Nw8hTReX.d.ts +53 -0
- package/dist/{jetbrains-copilot-C3HDGGML.js → jetbrains-copilot-NRZHI5PC.js} +85 -18
- package/dist/jetbrains-copilot-NRZHI5PC.js.map +1 -0
- package/dist/{kilo-PKAJCWPX.js → kilo-5NWOXVSD.js} +16 -16
- package/dist/kilo-5NWOXVSD.js.map +1 -0
- package/dist/{kilo-cli-QWUVXG2O.js → kilo-cli-UZ7R5UOP.js} +3 -3
- package/dist/{kimi-EVD7YKXS.js → kimi-2YFK2GBA.js} +87 -25
- package/dist/kimi-2YFK2GBA.js.map +1 -0
- package/dist/{kiro-UQRY6EIO.js → kiro-YL6AWUYG.js} +3 -3
- package/dist/{leaderboard-CSWHI4UY.js → leaderboard-MJG24O35.js} +2 -2
- package/dist/mimo-code-FTMGSKMQ.js +790 -0
- package/dist/mimo-code-FTMGSKMQ.js.map +1 -0
- package/dist/{mux-5CI5FP2W.js → mux-5XZFB44C.js} +156 -6
- package/dist/mux-5XZFB44C.js.map +1 -0
- package/dist/nemoclaw-W5KEJ3WX.js +58 -0
- package/dist/nemoclaw-W5KEJ3WX.js.map +1 -0
- package/dist/{omp-AXA7XY22.js → omp-2QMEY6HD.js} +138 -18
- package/dist/omp-2QMEY6HD.js.map +1 -0
- package/dist/openclaw-EXJEFNZI.js +18 -0
- package/dist/openclaw-EXJEFNZI.js.map +1 -0
- package/dist/{opencode-NZBRLJN3.js → opencode-WNXYJR4X.js} +84 -27
- package/dist/opencode-WNXYJR4X.js.map +1 -0
- package/dist/{package-WFOS4QVU.js → package-ACZXJHWB.js} +8 -6
- package/dist/{package-WFOS4QVU.js.map → package-ACZXJHWB.js.map} +1 -1
- package/dist/{pi-WMTUX6ES.js → pi-PVVPVWKG.js} +3 -2
- package/dist/{pi-WMTUX6ES.js.map → pi-PVVPVWKG.js.map} +1 -1
- package/dist/{qwen-code-MRRIUQ3P.js → qwen-code-G4CXKUHR.js} +426 -10
- package/dist/qwen-code-G4CXKUHR.js.map +1 -0
- package/dist/{roo-code-NI5UU6JB.js → roo-code-67TYJIQR.js} +137 -4
- package/dist/roo-code-67TYJIQR.js.map +1 -0
- package/dist/runtime/index.d.ts +96 -2
- package/dist/runtime/index.js +19 -10
- package/dist/sdk/index.d.ts +52 -0
- package/dist/sdk/index.js +82 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/test.d.ts +74 -0
- package/dist/sdk/test.js +292 -0
- package/dist/sdk/test.js.map +1 -0
- package/dist/{serve-3C2VFPAY.js → serve-GER7URPR.js} +13 -9
- package/dist/{serve-3C2VFPAY.js.map → serve-GER7URPR.js.map} +1 -1
- package/dist/{status-MKQYX5MW.js → status-LAJE3BM5.js} +10 -7
- package/dist/{status-MKQYX5MW.js.map → status-LAJE3BM5.js.map} +1 -1
- package/dist/statusline-L376ZPZU.js +60 -0
- package/dist/statusline-L376ZPZU.js.map +1 -0
- package/dist/{telemetry-3DN6XGYB.js → telemetry-B3DX3PWU.js} +6 -4
- package/dist/{telemetry-3DN6XGYB.js.map → telemetry-B3DX3PWU.js.map} +1 -1
- package/dist/{trae-CQC72RHH.js → trae-7CZZGOGI.js} +99 -5
- package/dist/trae-7CZZGOGI.js.map +1 -0
- package/dist/{types-CNigfEuR.d.ts → types-CRVrFWeU.d.ts} +322 -18
- package/dist/{uninstall-WG6PFWF3.js → uninstall-ZYTW7YMZ.js} +12 -9
- package/dist/{uninstall-WG6PFWF3.js.map → uninstall-ZYTW7YMZ.js.map} +1 -1
- package/dist/{upgrade-32NC2RKI.js → upgrade-4LUEEOZW.js} +12 -9
- package/dist/{upgrade-32NC2RKI.js.map → upgrade-4LUEEOZW.js.map} +1 -1
- package/dist/{usage-CJOM2WQM.js → usage-ZFBVQF3H.js} +2 -2
- package/dist/{usage-event-472X3DOY.js → usage-event-GZ32AGEB.js} +12 -8
- package/dist/{usage-event-472X3DOY.js.map → usage-event-GZ32AGEB.js.map} +1 -1
- package/dist/{vscode-copilot-4RVRTCKV.js → vscode-copilot-MF2CDNMT.js} +23 -10
- package/dist/vscode-copilot-MF2CDNMT.js.map +1 -0
- package/dist/{warp-X6MB67NT.js → warp-VKG55TAT.js} +55 -4
- package/dist/warp-VKG55TAT.js.map +1 -0
- package/dist/windsurf-FU5G4SHF.js +443 -0
- package/dist/windsurf-FU5G4SHF.js.map +1 -0
- package/dist/{zed-NTCYW4V2.js → zed-SFUHL3GS.js} +3 -3
- package/package.json +10 -2
- package/dist/amp-YGV66GJT.js.map +0 -1
- package/dist/chunk-3JCRUROU.js.map +0 -1
- package/dist/chunk-3RBLIHPZ.js.map +0 -1
- package/dist/chunk-GSGDG2BV.js.map +0 -1
- package/dist/chunk-KMAKDD3Z.js.map +0 -1
- package/dist/chunk-KVAAFYRR.js.map +0 -1
- package/dist/chunk-MEH7GC7R.js.map +0 -1
- package/dist/chunk-NJU275US.js +0 -238
- package/dist/chunk-NJU275US.js.map +0 -1
- package/dist/chunk-PD4VFEBY.js.map +0 -1
- package/dist/chunk-WNREHWKL.js.map +0 -1
- package/dist/chunk-ZAM4M7LI.js.map +0 -1
- package/dist/claude-code-4275HQU4.js.map +0 -1
- package/dist/codebuff-LE3KBZGM.js.map +0 -1
- package/dist/codex-4BY2ZJIW.js.map +0 -1
- package/dist/copilot-cli-IPISKO3V.js.map +0 -1
- package/dist/crush-X3E25EQG.js.map +0 -1
- package/dist/cursor-UH3W622V.js.map +0 -1
- package/dist/doctor-DZTWED4O.js.map +0 -1
- package/dist/droid-T5VBRXZF.js.map +0 -1
- package/dist/gemini-cli-ZSUK55UA.js.map +0 -1
- package/dist/goose-M3RR3CZG.js.map +0 -1
- package/dist/hermes-CUNKIAR5.js.map +0 -1
- package/dist/hook-EVY7LOUQ.js.map +0 -1
- package/dist/jetbrains-copilot-C3HDGGML.js.map +0 -1
- package/dist/kilo-PKAJCWPX.js.map +0 -1
- package/dist/kimi-EVD7YKXS.js.map +0 -1
- package/dist/mux-5CI5FP2W.js.map +0 -1
- package/dist/omp-AXA7XY22.js.map +0 -1
- package/dist/openclaw-UZ5BMCGQ.js.map +0 -1
- package/dist/opencode-NZBRLJN3.js.map +0 -1
- package/dist/qwen-code-MRRIUQ3P.js.map +0 -1
- package/dist/roo-code-NI5UU6JB.js.map +0 -1
- package/dist/trae-CQC72RHH.js.map +0 -1
- package/dist/vscode-copilot-4RVRTCKV.js.map +0 -1
- package/dist/warp-X6MB67NT.js.map +0 -1
- /package/dist/{antigravity-7IBYFO6P.js.map → antigravity-AMKKKHS4.js.map} +0 -0
- /package/dist/{antigravity-cli-JEOUP2VN.js.map → antigravity-cli-XERXEH5W.js.map} +0 -0
- /package/dist/{chunk-KQSNDFBN.js.map → chunk-LQRAPXS4.js.map} +0 -0
- /package/dist/{kilo-cli-QWUVXG2O.js.map → kilo-cli-UZ7R5UOP.js.map} +0 -0
- /package/dist/{kiro-UQRY6EIO.js.map → kiro-YL6AWUYG.js.map} +0 -0
- /package/dist/{leaderboard-CSWHI4UY.js.map → leaderboard-MJG24O35.js.map} +0 -0
- /package/dist/{usage-CJOM2WQM.js.map → usage-ZFBVQF3H.js.map} +0 -0
- /package/dist/{zed-NTCYW4V2.js.map → zed-SFUHL3GS.js.map} +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export { ConnectorConfigError, defineAction, defineCommand, defineConfigPatch, defineConnector, defineHook, defineMemory, defineNativeHook, defineSkill, defineStatusline, defineSubagent } from '../index.js';
|
|
2
|
+
export { S as SURFACE_PREDICATES, a as SurfaceName, c as capabilitiesOf, h as hostsSupporting, s as surfaceSupport } from '../introspect-Nw8hTReX.js';
|
|
3
|
+
import { P as PlatformId } from '../types-CRVrFWeU.js';
|
|
4
|
+
export { A as ActionDef, a as ActionResult, b as AuthSpec, C as CommandDef, c as ConfigPatchDef, d as ConnectorConfig, E as EventPayloadMap, H as HookDefinition, e as HookEventName, f as HookParadigm, g as HookResponse, h as HooksConfig, i as HostCtx, I as InstallScope, J as JsonValue, M as MemoryDef, N as NativeHookDef, j as NativeHookEvent, k as NotificationEvent, l as PermissionRequestEvent, m as PlatformCapabilities, n as PlatformMemoryOverride, o as PlatformOverride, p as PostToolUseEvent, q as PostToolUseFailureEvent, r as PreCompactEvent, s as PreToolUseEvent, t as PublishConfig, R as ResolvedConnector, S as ServerDef, u as SessionEndEvent, v as SessionStartEvent, w as SkillDef, x as StatuslineContext, y as StatuslineDef, z as StopEvent, B as SubagentDef, D as SubagentStartEvent, F as SubagentStopEvent, G as SurfaceToolPolicy, T as TelemetryAccessor, K as TelemetryConfig, L as TelemetryUsageSummary, O as ToolFilter, Q as Transport, U as UserPromptSubmitEvent } from '../types-CRVrFWeU.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* sdk/helpers — host-aware authoring helpers for connector handlers.
|
|
8
|
+
*
|
|
9
|
+
* A handler (a hook, a statusline render, an action run) sees the host it is
|
|
10
|
+
* dispatched for via the {@link HostCtx} (`ctx.host`). These two helpers let a
|
|
11
|
+
* handler render a host-correct value without hardcoding a per-host branch:
|
|
12
|
+
*
|
|
13
|
+
* • {@link toolName} — resolve a bare MCP tool name to the host's native
|
|
14
|
+
* reference form (some hosts namespace MCP tools as `mcp__<id>__<tool>`).
|
|
15
|
+
* • {@link style} — wrap text in an ANSI escape ONLY on hosts that render
|
|
16
|
+
* ANSI in a status line / output; plain everywhere else.
|
|
17
|
+
*
|
|
18
|
+
* Both are deliberately MODEST, documented best-effort lists — NOT a capability
|
|
19
|
+
* query. The per-host convention each encodes is not centrally modeled in
|
|
20
|
+
* {@link PlatformCapabilities} yet; if either set grows it should migrate to a
|
|
21
|
+
* capability flag (a `mcpToolPrefix` / `rendersAnsi` flag) the adapter declares.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolve a bare MCP tool name to the host's native reference form.
|
|
26
|
+
*
|
|
27
|
+
* On a {@link PREFIXED_HOSTS} host the result is `mcp__<connectorId>__<baseName>`
|
|
28
|
+
* (how those hosts surface a connector's MCP tools); on every other host it is
|
|
29
|
+
* the bare `baseName`. When the host prefixes but no `connectorId` is supplied
|
|
30
|
+
* the helper falls back to the bare name rather than emitting a malformed
|
|
31
|
+
* `mcp__undefined__…` reference — it NEVER throws.
|
|
32
|
+
*/
|
|
33
|
+
declare function toolName(ctx: {
|
|
34
|
+
host: PlatformId;
|
|
35
|
+
connectorId?: string;
|
|
36
|
+
}, baseName: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Style `text` with an ANSI SGR escape on hosts that render it (a status line /
|
|
39
|
+
* terminal output), plain everywhere else.
|
|
40
|
+
*
|
|
41
|
+
* When `ctx.host` is in {@link ANSI_HOSTS} and `codes.ansi` is provided, returns
|
|
42
|
+
* `\x1b[<ansi>m<text>\x1b[0m`; otherwise returns `text` unchanged (an
|
|
43
|
+
* IDE-embedded host, or no `ansi` code given). `codes.ansi` is the SGR
|
|
44
|
+
* parameter(s), e.g. `"1"` (bold) or `"1;32"` (bold green).
|
|
45
|
+
*/
|
|
46
|
+
declare function style(ctx: {
|
|
47
|
+
host: PlatformId;
|
|
48
|
+
}, text: string, codes: {
|
|
49
|
+
ansi?: string;
|
|
50
|
+
}): string;
|
|
51
|
+
|
|
52
|
+
export { PlatformId, style, toolName };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
SURFACE_PREDICATES,
|
|
4
|
+
capabilitiesOf,
|
|
5
|
+
hostsSupporting,
|
|
6
|
+
surfaceSupport
|
|
7
|
+
} from "../chunk-XJ4S47CT.js";
|
|
8
|
+
import {
|
|
9
|
+
ConnectorConfigError,
|
|
10
|
+
defineAction,
|
|
11
|
+
defineCommand,
|
|
12
|
+
defineConfigPatch,
|
|
13
|
+
defineConnector,
|
|
14
|
+
defineHook,
|
|
15
|
+
defineMemory,
|
|
16
|
+
defineNativeHook,
|
|
17
|
+
defineSkill,
|
|
18
|
+
defineStatusline,
|
|
19
|
+
defineSubagent
|
|
20
|
+
} from "../chunk-4ENJK5S2.js";
|
|
21
|
+
import "../chunk-DCKFYWGM.js";
|
|
22
|
+
import "../chunk-FAHIMJRU.js";
|
|
23
|
+
import "../chunk-N6HH7K6P.js";
|
|
24
|
+
import "../chunk-TRQKHP7D.js";
|
|
25
|
+
|
|
26
|
+
// src/sdk/helpers.ts
|
|
27
|
+
var PREFIXED_HOSTS = /* @__PURE__ */ new Set([
|
|
28
|
+
"claude-code",
|
|
29
|
+
"codex",
|
|
30
|
+
"kimi",
|
|
31
|
+
"cursor",
|
|
32
|
+
"copilot-cli",
|
|
33
|
+
"vscode-copilot",
|
|
34
|
+
"jetbrains-copilot",
|
|
35
|
+
"gemini-cli",
|
|
36
|
+
"qwen-code"
|
|
37
|
+
]);
|
|
38
|
+
function toolName(ctx, baseName) {
|
|
39
|
+
if (PREFIXED_HOSTS.has(ctx.host) && ctx.connectorId) {
|
|
40
|
+
return `mcp__${ctx.connectorId}__${baseName}`;
|
|
41
|
+
}
|
|
42
|
+
return baseName;
|
|
43
|
+
}
|
|
44
|
+
var ANSI_HOSTS = /* @__PURE__ */ new Set([
|
|
45
|
+
"claude-code",
|
|
46
|
+
"codex",
|
|
47
|
+
"gemini-cli",
|
|
48
|
+
"qwen-code",
|
|
49
|
+
"cursor",
|
|
50
|
+
"copilot-cli",
|
|
51
|
+
"opencode",
|
|
52
|
+
"droid",
|
|
53
|
+
"crush",
|
|
54
|
+
"goose",
|
|
55
|
+
"amp"
|
|
56
|
+
]);
|
|
57
|
+
function style(ctx, text, codes) {
|
|
58
|
+
if (codes.ansi && ANSI_HOSTS.has(ctx.host)) {
|
|
59
|
+
return `\x1B[${codes.ansi}m${text}\x1B[0m`;
|
|
60
|
+
}
|
|
61
|
+
return text;
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
ConnectorConfigError,
|
|
65
|
+
SURFACE_PREDICATES,
|
|
66
|
+
capabilitiesOf,
|
|
67
|
+
defineAction,
|
|
68
|
+
defineCommand,
|
|
69
|
+
defineConfigPatch,
|
|
70
|
+
defineConnector,
|
|
71
|
+
defineHook,
|
|
72
|
+
defineMemory,
|
|
73
|
+
defineNativeHook,
|
|
74
|
+
defineSkill,
|
|
75
|
+
defineStatusline,
|
|
76
|
+
defineSubagent,
|
|
77
|
+
hostsSupporting,
|
|
78
|
+
style,
|
|
79
|
+
surfaceSupport,
|
|
80
|
+
toolName
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sdk/helpers.ts"],"sourcesContent":["/**\n * sdk/helpers — host-aware authoring helpers for connector handlers.\n *\n * A handler (a hook, a statusline render, an action run) sees the host it is\n * dispatched for via the {@link HostCtx} (`ctx.host`). These two helpers let a\n * handler render a host-correct value without hardcoding a per-host branch:\n *\n * • {@link toolName} — resolve a bare MCP tool name to the host's native\n * reference form (some hosts namespace MCP tools as `mcp__<id>__<tool>`).\n * • {@link style} — wrap text in an ANSI escape ONLY on hosts that render\n * ANSI in a status line / output; plain everywhere else.\n *\n * Both are deliberately MODEST, documented best-effort lists — NOT a capability\n * query. The per-host convention each encodes is not centrally modeled in\n * {@link PlatformCapabilities} yet; if either set grows it should migrate to a\n * capability flag (a `mcpToolPrefix` / `rendersAnsi` flag) the adapter declares.\n */\n\nimport type { PlatformId } from \"../core/types.js\";\n\n/**\n * Hosts that namespace MCP tools as `mcp__<connectorId>__<tool>` — the\n * `mcp__`-prefix convention Claude Code popularized and most MCP-client agents\n * adopted. CONFIRMED in-repo via the adapters' own `mcp__` PreToolUse matchers\n * for claude-code, codex, and kimi; the remaining members are MCP-client hosts\n * the convention applies to but are not separately verified here (a wrong\n * inclusion only ever yields a name the host doesn't resolve, the same outcome\n * as wrongly EXCLUDING a prefixing host — and an author can always construct the\n * reference themselves). This is a SMALL explicit set, not a capability query;\n * if it grows, migrate it to a per-adapter `mcpToolPrefix` capability flag.\n * (Follow-up: spot-verify gemini-cli / a copilot variant on a live box.)\n */\nconst PREFIXED_HOSTS: ReadonlySet<PlatformId> = new Set<PlatformId>([\n \"claude-code\",\n \"codex\",\n \"kimi\",\n \"cursor\",\n \"copilot-cli\",\n \"vscode-copilot\",\n \"jetbrains-copilot\",\n \"gemini-cli\",\n \"qwen-code\",\n]);\n\n/**\n * Resolve a bare MCP tool name to the host's native reference form.\n *\n * On a {@link PREFIXED_HOSTS} host the result is `mcp__<connectorId>__<baseName>`\n * (how those hosts surface a connector's MCP tools); on every other host it is\n * the bare `baseName`. When the host prefixes but no `connectorId` is supplied\n * the helper falls back to the bare name rather than emitting a malformed\n * `mcp__undefined__…` reference — it NEVER throws.\n */\nexport function toolName(\n ctx: { host: PlatformId; connectorId?: string },\n baseName: string,\n): string {\n if (PREFIXED_HOSTS.has(ctx.host) && ctx.connectorId) {\n return `mcp__${ctx.connectorId}__${baseName}`;\n }\n return baseName;\n}\n\n/**\n * Hosts that render ANSI escape sequences in a status line / terminal output —\n * the CLI/TUI hosts. IDE-embedded hosts (where output lands in a webview /\n * editor panel that shows the raw escape) are deliberately EXCLUDED. Like\n * {@link PREFIXED_HOSTS} this is a documented best-effort list, not a capability\n * query; migrate it to a `rendersAnsi` capability flag if it grows.\n */\nconst ANSI_HOSTS: ReadonlySet<PlatformId> = new Set<PlatformId>([\n \"claude-code\",\n \"codex\",\n \"gemini-cli\",\n \"qwen-code\",\n \"cursor\",\n \"copilot-cli\",\n \"opencode\",\n \"droid\",\n \"crush\",\n \"goose\",\n \"amp\",\n]);\n\n/**\n * Style `text` with an ANSI SGR escape on hosts that render it (a status line /\n * terminal output), plain everywhere else.\n *\n * When `ctx.host` is in {@link ANSI_HOSTS} and `codes.ansi` is provided, returns\n * `\\x1b[<ansi>m<text>\\x1b[0m`; otherwise returns `text` unchanged (an\n * IDE-embedded host, or no `ansi` code given). `codes.ansi` is the SGR\n * parameter(s), e.g. `\"1\"` (bold) or `\"1;32\"` (bold green).\n */\nexport function style(\n ctx: { host: PlatformId },\n text: string,\n codes: { ansi?: string },\n): string {\n if (codes.ansi && ANSI_HOSTS.has(ctx.host)) {\n return `\\x1b[${codes.ansi}m${text}\\x1b[0m`;\n }\n return text;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,iBAA0C,oBAAI,IAAgB;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAWM,SAAS,SACd,KACA,UACQ;AACR,MAAI,eAAe,IAAI,IAAI,IAAI,KAAK,IAAI,aAAa;AACnD,WAAO,QAAQ,IAAI,WAAW,KAAK,QAAQ;AAAA,EAC7C;AACA,SAAO;AACT;AASA,IAAM,aAAsC,oBAAI,IAAgB;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAWM,SAAS,MACd,KACA,MACA,OACQ;AACR,MAAI,MAAM,QAAQ,WAAW,IAAI,IAAI,IAAI,GAAG;AAC1C,WAAO,QAAQ,MAAM,IAAI,IAAI,IAAI;AAAA,EACnC;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { P as PlatformId, e as HookEventName, R as ResolvedConnector } from '../types-CRVrFWeU.js';
|
|
2
|
+
import { a as SurfaceName } from '../introspect-Nw8hTReX.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* sdk/test-harness — the SHIPPED, OFFLINE behavioral harness.
|
|
6
|
+
*
|
|
7
|
+
* Two read-only tools a connector author runs WITHOUT installing anything or
|
|
8
|
+
* touching the host's filesystem:
|
|
9
|
+
*
|
|
10
|
+
* • `explain(connector)` — the static per-host × per-DECLARED-surface matrix:
|
|
11
|
+
* for every registered host and every surface the connector actually ships,
|
|
12
|
+
* is it `native` (the host can honor it), `disabled` (the connector opted
|
|
13
|
+
* out for that host via `platforms[host].<surface> === false`), or
|
|
14
|
+
* `skip-warn` (the host cannot honor it → install would skip-warn)?
|
|
15
|
+
*
|
|
16
|
+
* • `simulate(connector, opts)` — runs the REAL adapter parse → handler →
|
|
17
|
+
* format chain INLINE against a host-shaped raw payload and reports whether
|
|
18
|
+
* the host actually HONORS the connector's response (the trustworthy part:
|
|
19
|
+
* it surfaces the documented degradations, e.g. Codex silently dropping a
|
|
20
|
+
* UserPromptSubmit "context" injection that has no stdout path).
|
|
21
|
+
*
|
|
22
|
+
* It deliberately does NOT import the runtime entrypoints (runHook /
|
|
23
|
+
* runStatusline) — those load the REGISTERED connector from disk and record
|
|
24
|
+
* telemetry. The harness instead re-implements the tiny parse→handler→format
|
|
25
|
+
* chain against the in-memory connector + the adapter, mirroring the runtime's
|
|
26
|
+
* input tolerance (empty/malformed payload → `{}`) and its fail-safe envelope.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** One cell of the {@link explain} matrix: a host × a declared surface. */
|
|
30
|
+
interface ExplainRow {
|
|
31
|
+
host: PlatformId;
|
|
32
|
+
surface: SurfaceName;
|
|
33
|
+
/**
|
|
34
|
+
* - "native" — the host's capabilities pass the surface predicate.
|
|
35
|
+
* - "disabled" — the connector opted out for this host
|
|
36
|
+
* (`platforms[host].<surface> === false`).
|
|
37
|
+
* - "skip-warn" — the host cannot honor it; install would skip-warn.
|
|
38
|
+
*/
|
|
39
|
+
support: "native" | "skip-warn" | "disabled";
|
|
40
|
+
reason: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The static support matrix: every registered host × every surface the
|
|
44
|
+
* connector declares. Rows are sorted by host then surface for stable output.
|
|
45
|
+
* Pure read of the adapter capabilities — installs nothing, writes nothing.
|
|
46
|
+
*/
|
|
47
|
+
declare function explain(connector: ResolvedConnector): Promise<ExplainRow[]>;
|
|
48
|
+
/** What to drive through the harness: one host, one surface, one raw payload. */
|
|
49
|
+
interface SimulateOptions {
|
|
50
|
+
surface: "hooks" | "statusline";
|
|
51
|
+
host: PlatformId | string;
|
|
52
|
+
/** REQUIRED when `surface === "hooks"`: which normalized lifecycle event. */
|
|
53
|
+
event?: HookEventName;
|
|
54
|
+
/** The host-shaped RAW payload (what the host would pipe in on stdin). */
|
|
55
|
+
input: unknown;
|
|
56
|
+
}
|
|
57
|
+
/** The harness verdict: did the host actually honor the connector's response? */
|
|
58
|
+
interface SimulateResult {
|
|
59
|
+
/** True when the host's native reply carries the connector's intent. */
|
|
60
|
+
honored: boolean;
|
|
61
|
+
/** The host-native stdout the adapter would emit (when there is one). */
|
|
62
|
+
hostReply?: string;
|
|
63
|
+
/** Human-readable why — names the documented degradation when honored:false. */
|
|
64
|
+
reason: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Run the connector against ONE host-shaped payload through the REAL adapter
|
|
68
|
+
* path and report whether the host honors the response. Self-contained (does not
|
|
69
|
+
* touch the registered-connector loader or telemetry) and fail-safe: any throw
|
|
70
|
+
* in the chain degrades to `honored:false` with the error message, never rejects.
|
|
71
|
+
*/
|
|
72
|
+
declare function simulate(connector: ResolvedConnector, opts: SimulateOptions): Promise<SimulateResult>;
|
|
73
|
+
|
|
74
|
+
export { type ExplainRow, type SimulateOptions, type SimulateResult, explain, simulate };
|
package/dist/sdk/test.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
compileMatcher,
|
|
4
|
+
eventMatcherSubject
|
|
5
|
+
} from "../chunk-2AVLX7GH.js";
|
|
6
|
+
import "../chunk-BCHWFTV4.js";
|
|
7
|
+
import "../chunk-KHO5WNTP.js";
|
|
8
|
+
import {
|
|
9
|
+
SURFACE_PREDICATES
|
|
10
|
+
} from "../chunk-XJ4S47CT.js";
|
|
11
|
+
import "../chunk-24IDL5RW.js";
|
|
12
|
+
import "../chunk-4ENJK5S2.js";
|
|
13
|
+
import {
|
|
14
|
+
allAdapters,
|
|
15
|
+
loadAdapter
|
|
16
|
+
} from "../chunk-DCKFYWGM.js";
|
|
17
|
+
import "../chunk-FAHIMJRU.js";
|
|
18
|
+
import "../chunk-N6HH7K6P.js";
|
|
19
|
+
import "../chunk-TRQKHP7D.js";
|
|
20
|
+
|
|
21
|
+
// src/sdk/test-harness.ts
|
|
22
|
+
function declaredGlobalSurfaces(connector) {
|
|
23
|
+
const out = [];
|
|
24
|
+
if (connector.server) out.push("server");
|
|
25
|
+
if (Object.keys(connector.hooks).length > 0) out.push("hooks");
|
|
26
|
+
if (connector.commands.length > 0) out.push("commands");
|
|
27
|
+
if (connector.skills.length > 0) out.push("skills");
|
|
28
|
+
if (connector.subagents.length > 0) out.push("subagents");
|
|
29
|
+
if (connector.memory.length > 0) out.push("memory");
|
|
30
|
+
if (connector.statusline) out.push("statusline");
|
|
31
|
+
if (connector.actions.length > 0) out.push("actions");
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
function platformScopedSurfacesFor(override) {
|
|
35
|
+
const out = [];
|
|
36
|
+
if (!override) return out;
|
|
37
|
+
if ((override.configPatch?.length ?? 0) > 0) out.push("configPatch");
|
|
38
|
+
if (override.nativeHooks != null && Object.keys(override.nativeHooks).length > 0) {
|
|
39
|
+
out.push("nativeHooks");
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
function isDisabledForHost(connector, host, surface) {
|
|
44
|
+
const override = connector.platforms[host];
|
|
45
|
+
if (!override) return false;
|
|
46
|
+
switch (surface) {
|
|
47
|
+
case "server":
|
|
48
|
+
return override.server === false;
|
|
49
|
+
case "hooks":
|
|
50
|
+
return override.hooks === false;
|
|
51
|
+
case "commands":
|
|
52
|
+
return override.commands === false;
|
|
53
|
+
case "skills":
|
|
54
|
+
return override.skills === false;
|
|
55
|
+
case "subagents":
|
|
56
|
+
return override.subagents === false;
|
|
57
|
+
case "memory":
|
|
58
|
+
return override.memory === false;
|
|
59
|
+
case "statusline":
|
|
60
|
+
return override.statusline === false;
|
|
61
|
+
case "actions":
|
|
62
|
+
case "configPatch":
|
|
63
|
+
case "nativeHooks":
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function explain(connector) {
|
|
68
|
+
const globalSurfaces = declaredGlobalSurfaces(connector);
|
|
69
|
+
const adapters = await allAdapters();
|
|
70
|
+
const rows = [];
|
|
71
|
+
for (const adapter of adapters) {
|
|
72
|
+
const override = connector.platforms[adapter.id];
|
|
73
|
+
const surfaces = [
|
|
74
|
+
...globalSurfaces,
|
|
75
|
+
...platformScopedSurfacesFor(override)
|
|
76
|
+
];
|
|
77
|
+
for (const surface of surfaces) {
|
|
78
|
+
if (isDisabledForHost(connector, adapter.id, surface)) {
|
|
79
|
+
rows.push({
|
|
80
|
+
host: adapter.id,
|
|
81
|
+
surface,
|
|
82
|
+
support: "disabled",
|
|
83
|
+
reason: `connector opted out for ${adapter.id} (platforms.${adapter.id}.${surface} = false)`
|
|
84
|
+
});
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const native = SURFACE_PREDICATES[surface](adapter.capabilities);
|
|
88
|
+
rows.push({
|
|
89
|
+
host: adapter.id,
|
|
90
|
+
surface,
|
|
91
|
+
support: native ? "native" : "skip-warn",
|
|
92
|
+
reason: explainReason(adapter.id, surface, native)
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
rows.sort(
|
|
97
|
+
(a, b) => a.host === b.host ? a.surface.localeCompare(b.surface) : a.host.localeCompare(b.host)
|
|
98
|
+
);
|
|
99
|
+
return rows;
|
|
100
|
+
}
|
|
101
|
+
function explainReason(host, surface, native) {
|
|
102
|
+
if (surface === "server") {
|
|
103
|
+
return native ? `${host} advertises an MCP transport (capability-based; registration may be host-managed)` : `${host} advertises no MCP transport; install skip-warns`;
|
|
104
|
+
}
|
|
105
|
+
return native ? `${host} natively supports ${surface}` : `${host} cannot honor ${surface}; install skip-warns`;
|
|
106
|
+
}
|
|
107
|
+
function coerceInput(input) {
|
|
108
|
+
if (input == null) return {};
|
|
109
|
+
if (typeof input === "string") {
|
|
110
|
+
const trimmed = input.trim();
|
|
111
|
+
if (trimmed === "") return {};
|
|
112
|
+
try {
|
|
113
|
+
return JSON.parse(trimmed);
|
|
114
|
+
} catch {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return input;
|
|
119
|
+
}
|
|
120
|
+
function normalizeResponse(value) {
|
|
121
|
+
if (value == null || typeof value !== "object") return {};
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
function parseReplyStdout(stdout) {
|
|
125
|
+
if (stdout == null || stdout.trim() === "") return void 0;
|
|
126
|
+
try {
|
|
127
|
+
const value = JSON.parse(stdout);
|
|
128
|
+
return value != null && typeof value === "object" ? value : void 0;
|
|
129
|
+
} catch {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function deepEqual(a, b) {
|
|
134
|
+
if (a === b) return true;
|
|
135
|
+
if (typeof a !== typeof b || a == null || b == null) return false;
|
|
136
|
+
if (typeof a !== "object") return false;
|
|
137
|
+
const aArr = Array.isArray(a);
|
|
138
|
+
if (aArr !== Array.isArray(b)) return false;
|
|
139
|
+
if (aArr) {
|
|
140
|
+
const x2 = a;
|
|
141
|
+
const y2 = b;
|
|
142
|
+
return x2.length === y2.length && x2.every((v, i) => deepEqual(v, y2[i]));
|
|
143
|
+
}
|
|
144
|
+
const x = a;
|
|
145
|
+
const y = b;
|
|
146
|
+
const xk = Object.keys(x);
|
|
147
|
+
const yk = Object.keys(y);
|
|
148
|
+
return xk.length === yk.length && xk.every((k) => deepEqual(x[k], y[k]));
|
|
149
|
+
}
|
|
150
|
+
function judgeHookHonor(host, event, response, stdout) {
|
|
151
|
+
const decision = response.decision;
|
|
152
|
+
if (decision === void 0 || decision === "allow") {
|
|
153
|
+
return { honored: true, reason: "pass-through allow" };
|
|
154
|
+
}
|
|
155
|
+
const reply = parseReplyStdout(stdout);
|
|
156
|
+
const hso = reply?.hookSpecificOutput;
|
|
157
|
+
if (decision === "context") {
|
|
158
|
+
const want = response.additionalContext;
|
|
159
|
+
const got = hso?.additionalContext ?? reply?.systemMessage;
|
|
160
|
+
if (reply && got === want) {
|
|
161
|
+
return { honored: true, reason: `${host} injects context on ${event}` };
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
honored: false,
|
|
165
|
+
reason: `${host} drops context on ${event} (no stdout path)`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
if (decision === "deny") {
|
|
169
|
+
if (!reply) {
|
|
170
|
+
return {
|
|
171
|
+
honored: false,
|
|
172
|
+
reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (hso?.permissionDecision === "deny" || hso?.decision?.behavior === "deny") {
|
|
176
|
+
return { honored: true, reason: `${host} blocks ${event}` };
|
|
177
|
+
}
|
|
178
|
+
if (reply.decision === "block") {
|
|
179
|
+
return {
|
|
180
|
+
honored: true,
|
|
181
|
+
reason: `${host} continues the session/subagent on ${event} (persistence) \u2014 NOT a block`
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (hso?.additionalContext != null) {
|
|
185
|
+
return {
|
|
186
|
+
honored: false,
|
|
187
|
+
reason: `${host} degrades deny to a context note on ${event}; spawn/failure not blocked`
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
honored: false,
|
|
192
|
+
reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (decision === "ask") {
|
|
196
|
+
if (hso?.permissionDecision === "ask") {
|
|
197
|
+
return { honored: true, reason: `${host} asks on ${event}` };
|
|
198
|
+
}
|
|
199
|
+
if (event === "PermissionRequest" && !reply) {
|
|
200
|
+
return {
|
|
201
|
+
honored: true,
|
|
202
|
+
reason: `${host} shows the native confirmation dialog on ${event} (the ask)`
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
honored: false,
|
|
207
|
+
reason: `${host} drops ask on ${event} (no ask path)`
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const wrote = hso?.updatedInput ?? hso?.decision?.updatedInput;
|
|
211
|
+
if (reply && wrote !== void 0 && deepEqual(wrote, response.updatedInput)) {
|
|
212
|
+
return { honored: true, reason: `${host} rewrites tool input on ${event}` };
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
honored: false,
|
|
216
|
+
reason: `${host} drops modify on ${event} (modify not honored)`
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async function simulate(connector, opts) {
|
|
220
|
+
try {
|
|
221
|
+
const adapter = await loadAdapter(opts.host);
|
|
222
|
+
if (!adapter) {
|
|
223
|
+
return { honored: false, reason: `unknown host "${opts.host}"` };
|
|
224
|
+
}
|
|
225
|
+
if (opts.surface === "statusline") {
|
|
226
|
+
if (!(adapter.capabilities.supportsStatusline ?? false) || !adapter.parseStatusInput || !adapter.formatStatusOutput) {
|
|
227
|
+
return { honored: false, reason: `${opts.host} has no statusline surface` };
|
|
228
|
+
}
|
|
229
|
+
if (!connector.statusline || typeof connector.statusline.render !== "function") {
|
|
230
|
+
return { honored: false, reason: "connector declares no statusline" };
|
|
231
|
+
}
|
|
232
|
+
const ctx = adapter.parseStatusInput(coerceInput(opts.input));
|
|
233
|
+
ctx.connectorId = connector.id;
|
|
234
|
+
if (ctx.capabilities == null) ctx.capabilities = adapter.capabilities;
|
|
235
|
+
const perHost2 = connector.statusline.hosts?.[opts.host]?.render;
|
|
236
|
+
const render = typeof perHost2 === "function" ? perHost2 : connector.statusline.render;
|
|
237
|
+
const rendered = await render(ctx);
|
|
238
|
+
const line = rendered == null ? "" : String(rendered);
|
|
239
|
+
const reply2 = adapter.formatStatusOutput(line);
|
|
240
|
+
return {
|
|
241
|
+
honored: true,
|
|
242
|
+
...reply2.stdout !== void 0 ? { hostReply: reply2.stdout } : {},
|
|
243
|
+
reason: "rendered"
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const event = opts.event;
|
|
247
|
+
if (!event) {
|
|
248
|
+
return { honored: false, reason: 'event is required when surface === "hooks"' };
|
|
249
|
+
}
|
|
250
|
+
if (!adapter.parseEvent || !adapter.formatReply) {
|
|
251
|
+
return { honored: false, reason: `${opts.host} has no hook runtime (mcp-only)` };
|
|
252
|
+
}
|
|
253
|
+
const evt = adapter.parseEvent(event, coerceInput(opts.input));
|
|
254
|
+
evt.connectorId = connector.id;
|
|
255
|
+
evt.capabilities = adapter.capabilities;
|
|
256
|
+
const definition = connector.hooks[event];
|
|
257
|
+
if (!definition || typeof definition.handler !== "function") {
|
|
258
|
+
return { honored: false, reason: `connector declares no ${event} handler` };
|
|
259
|
+
}
|
|
260
|
+
const subject = eventMatcherSubject(evt);
|
|
261
|
+
if (subject !== void 0) {
|
|
262
|
+
const re = compileMatcher(definition.matcher);
|
|
263
|
+
if (re && !re.test(subject)) {
|
|
264
|
+
return {
|
|
265
|
+
honored: true,
|
|
266
|
+
reason: `matcher excludes ${subject} (handler not run)`
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const perHost = definition.hosts?.[opts.host]?.handler;
|
|
271
|
+
const handler = typeof perHost === "function" ? perHost : definition.handler;
|
|
272
|
+
const response = normalizeResponse(
|
|
273
|
+
await handler(evt)
|
|
274
|
+
);
|
|
275
|
+
const reply = adapter.formatReply(event, response);
|
|
276
|
+
const verdict = judgeHookHonor(String(opts.host), event, response, reply.stdout);
|
|
277
|
+
return {
|
|
278
|
+
honored: verdict.honored,
|
|
279
|
+
...reply.stdout !== void 0 ? { hostReply: reply.stdout } : {},
|
|
280
|
+
reason: verdict.reason
|
|
281
|
+
};
|
|
282
|
+
} catch (err) {
|
|
283
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
284
|
+
const verb = opts.surface === "statusline" ? "render" : "handler";
|
|
285
|
+
return { honored: false, reason: `${verb} threw: ${message}` };
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
export {
|
|
289
|
+
explain,
|
|
290
|
+
simulate
|
|
291
|
+
};
|
|
292
|
+
//# sourceMappingURL=test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sdk/test-harness.ts"],"sourcesContent":["/**\n * sdk/test-harness — the SHIPPED, OFFLINE behavioral harness.\n *\n * Two read-only tools a connector author runs WITHOUT installing anything or\n * touching the host's filesystem:\n *\n * • `explain(connector)` — the static per-host × per-DECLARED-surface matrix:\n * for every registered host and every surface the connector actually ships,\n * is it `native` (the host can honor it), `disabled` (the connector opted\n * out for that host via `platforms[host].<surface> === false`), or\n * `skip-warn` (the host cannot honor it → install would skip-warn)?\n *\n * • `simulate(connector, opts)` — runs the REAL adapter parse → handler →\n * format chain INLINE against a host-shaped raw payload and reports whether\n * the host actually HONORS the connector's response (the trustworthy part:\n * it surfaces the documented degradations, e.g. Codex silently dropping a\n * UserPromptSubmit \"context\" injection that has no stdout path).\n *\n * It deliberately does NOT import the runtime entrypoints (runHook /\n * runStatusline) — those load the REGISTERED connector from disk and record\n * telemetry. The harness instead re-implements the tiny parse→handler→format\n * chain against the in-memory connector + the adapter, mirroring the runtime's\n * input tolerance (empty/malformed payload → `{}`) and its fail-safe envelope.\n */\n\nimport type {\n HookEventName,\n HookResponse,\n PlatformId,\n PlatformOverride,\n ResolvedConnector,\n} from \"../core/types.js\";\nimport { allAdapters, loadAdapter } from \"../adapters/registry.js\";\nimport type { NormalizedEvent } from \"../adapters/spi.js\";\nimport {\n compileMatcher,\n eventMatcherSubject,\n} from \"../runtime/hook-entrypoint.js\";\nimport type { SurfaceName } from \"./introspect.js\";\nimport { SURFACE_PREDICATES } from \"./introspect.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// explain — the static per-host × per-declared-surface matrix\n// ─────────────────────────────────────────────────────────────────────────\n\n/** One cell of the {@link explain} matrix: a host × a declared surface. */\nexport interface ExplainRow {\n host: PlatformId;\n surface: SurfaceName;\n /**\n * - \"native\" — the host's capabilities pass the surface predicate.\n * - \"disabled\" — the connector opted out for this host\n * (`platforms[host].<surface> === false`).\n * - \"skip-warn\" — the host cannot honor it; install would skip-warn.\n */\n support: \"native\" | \"skip-warn\" | \"disabled\";\n reason: string;\n}\n\n/**\n * The GLOBAL surfaces a connector declares (these apply to every host — only\n * declared surfaces get a matrix row). The two platform-scoped surfaces\n * (`configPatch` / `nativeHooks`) are deliberately EXCLUDED here: they are\n * declared under one specific `platforms[host]`, so each is added PER HOST in\n * {@link explain} (see {@link platformScopedSurfacesFor}) — adding them globally\n * would falsely promise a configPatch row on hosts that never declared it.\n */\nfunction declaredGlobalSurfaces(connector: ResolvedConnector): SurfaceName[] {\n const out: SurfaceName[] = [];\n if (connector.server) out.push(\"server\");\n if (Object.keys(connector.hooks).length > 0) out.push(\"hooks\");\n if (connector.commands.length > 0) out.push(\"commands\");\n if (connector.skills.length > 0) out.push(\"skills\");\n if (connector.subagents.length > 0) out.push(\"subagents\");\n if (connector.memory.length > 0) out.push(\"memory\");\n if (connector.statusline) out.push(\"statusline\");\n if (connector.actions.length > 0) out.push(\"actions\");\n return out;\n}\n\n/**\n * The platform-scoped surfaces (`configPatch` / `nativeHooks`) THIS host\n * actually declares — keyed off the host's own `platforms[host]` override, so a\n * configPatch declared under claude-code never shows up as a row for codex.\n */\nfunction platformScopedSurfacesFor(\n override: PlatformOverride | undefined,\n): SurfaceName[] {\n const out: SurfaceName[] = [];\n if (!override) return out;\n if ((override.configPatch?.length ?? 0) > 0) out.push(\"configPatch\");\n if (override.nativeHooks != null && Object.keys(override.nativeHooks).length > 0) {\n out.push(\"nativeHooks\");\n }\n return out;\n}\n\n/**\n * Is the surface explicitly DISABLED for this host? Mirrors the per-host opt-out\n * the installer honors: a boolean-or-object surface override is \"disabled\" only\n * when set to the literal `false` (the object form is a tuning, not an opt-out).\n * `configPatch` / `nativeHooks` have no boolean opt-out form — declaring them is\n * the opt-in, so they are never \"disabled\".\n */\nfunction isDisabledForHost(\n connector: ResolvedConnector,\n host: PlatformId,\n surface: SurfaceName,\n): boolean {\n const override = connector.platforms[host];\n if (!override) return false;\n switch (surface) {\n case \"server\":\n return override.server === false;\n case \"hooks\":\n return override.hooks === false;\n case \"commands\":\n return override.commands === false;\n case \"skills\":\n return override.skills === false;\n case \"subagents\":\n return override.subagents === false;\n case \"memory\":\n return override.memory === false;\n case \"statusline\":\n return override.statusline === false;\n case \"actions\":\n case \"configPatch\":\n case \"nativeHooks\":\n // No per-host boolean opt-out form (declaring them is the opt-in).\n return false;\n }\n}\n\n/**\n * The static support matrix: every registered host × every surface the\n * connector declares. Rows are sorted by host then surface for stable output.\n * Pure read of the adapter capabilities — installs nothing, writes nothing.\n */\nexport async function explain(connector: ResolvedConnector): Promise<ExplainRow[]> {\n const globalSurfaces = declaredGlobalSurfaces(connector);\n const adapters = await allAdapters();\n const rows: ExplainRow[] = [];\n\n for (const adapter of adapters) {\n // Per-host surface set = the global surfaces every host gets a row for, plus\n // the platform-scoped surfaces THIS host actually declares (configPatch /\n // nativeHooks live under platforms[host], so they are host-local).\n const override = connector.platforms[adapter.id];\n const surfaces = [\n ...globalSurfaces,\n ...platformScopedSurfacesFor(override),\n ];\n for (const surface of surfaces) {\n if (isDisabledForHost(connector, adapter.id, surface)) {\n rows.push({\n host: adapter.id,\n surface,\n support: \"disabled\",\n reason: `connector opted out for ${adapter.id} (platforms.${adapter.id}.${surface} = false)`,\n });\n continue;\n }\n const native = SURFACE_PREDICATES[surface](adapter.capabilities);\n rows.push({\n host: adapter.id,\n surface,\n support: native ? \"native\" : \"skip-warn\",\n reason: explainReason(adapter.id, surface, native),\n });\n }\n }\n\n rows.sort((a, b) =>\n a.host === b.host ? a.surface.localeCompare(b.surface) : a.host.localeCompare(b.host),\n );\n return rows;\n}\n\n/**\n * The human-readable reason for a native/skip-warn cell. `server` is a special\n * case: the predicate is CAPABILITY-based (the host advertises ≥1 MCP\n * transport), but advertising a transport does NOT guarantee a writable\n * registration — e.g. jetbrains-copilot advertises stdio yet its installServer\n * skip-warns because MCP is managed in IDE settings. So the server reason states\n * the verdict is capability-based and registration may be host-managed, rather\n * than promising a write. (A precise per-adapter installServer dry-run is a\n * Phase-2 enhancement — until then this avoids over-promising.)\n */\nfunction explainReason(host: PlatformId, surface: SurfaceName, native: boolean): string {\n if (surface === \"server\") {\n return native\n ? `${host} advertises an MCP transport (capability-based; registration may be host-managed)`\n : `${host} advertises no MCP transport; install skip-warns`;\n }\n return native\n ? `${host} natively supports ${surface}`\n : `${host} cannot honor ${surface}; install skip-warns`;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// simulate — the real adapter parse → handler → format chain, inline\n// ─────────────────────────────────────────────────────────────────────────\n\n/** What to drive through the harness: one host, one surface, one raw payload. */\nexport interface SimulateOptions {\n surface: \"hooks\" | \"statusline\";\n host: PlatformId | string;\n /** REQUIRED when `surface === \"hooks\"`: which normalized lifecycle event. */\n event?: HookEventName;\n /** The host-shaped RAW payload (what the host would pipe in on stdin). */\n input: unknown;\n}\n\n/** The harness verdict: did the host actually honor the connector's response? */\nexport interface SimulateResult {\n /** True when the host's native reply carries the connector's intent. */\n honored: boolean;\n /** The host-native stdout the adapter would emit (when there is one). */\n hostReply?: string;\n /** Human-readable why — names the documented degradation when honored:false. */\n reason: string;\n}\n\n/**\n * Tolerantly coerce the raw payload, mirroring the runtime's parseStdin: a\n * string is JSON-parsed (empty/malformed → `{}`); a nullish value → `{}`; any\n * other value (already-parsed object) is passed through untouched.\n */\nfunction coerceInput(input: unknown): unknown {\n if (input == null) return {};\n if (typeof input === \"string\") {\n const trimmed = input.trim();\n if (trimmed === \"\") return {};\n try {\n return JSON.parse(trimmed);\n } catch {\n return {};\n }\n }\n return input;\n}\n\n/** Normalize a handler's return into a concrete response (void/non-object → `{}`). */\nfunction normalizeResponse(value: HookResponse | void): HookResponse {\n if (value == null || typeof value !== \"object\") return {};\n return value;\n}\n\n/**\n * The decoded shape of a host's native hook reply on stdout. Every json-stdio\n * adapter writes a SUBSET of these fields (claude-code / codex share the\n * `hookSpecificOutput` envelope; Stop-class continuation uses the TOP-LEVEL\n * `decision:\"block\"`). We read the DECODED fields — never substring-match the\n * serialized JSON, which a path/newline/quote in the payload would break.\n */\ninterface ParsedReply {\n /** Top-level Stop-class continuation: {\"decision\":\"block\",\"reason\"}. */\n decision?: unknown;\n reason?: unknown;\n /** Top-level Claude context channel (no adapter emits it yet; future-proofing). */\n systemMessage?: unknown;\n hookSpecificOutput?: {\n hookEventName?: unknown;\n additionalContext?: unknown;\n permissionDecision?: unknown;\n permissionDecisionReason?: unknown;\n updatedInput?: unknown;\n /** PermissionRequest nested envelope: decision:{behavior:\"allow\"|\"deny\", updatedInput?}. */\n decision?: { behavior?: unknown; updatedInput?: unknown };\n };\n}\n\n/**\n * Tolerantly decode a reply's stdout into {@link ParsedReply}. Empty / absent /\n * unparseable stdout is a legitimate \"no payload\" signal (the adapter wrote a\n * bare exit-0) → `undefined`, the honest \"this decision had no stdout path\"\n * verdict input.\n */\nfunction parseReplyStdout(stdout: string | undefined): ParsedReply | undefined {\n if (stdout == null || stdout.trim() === \"\") return undefined;\n try {\n const value = JSON.parse(stdout);\n return value != null && typeof value === \"object\"\n ? (value as ParsedReply)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Structural deep-equal for plain JSON values (objects/arrays/primitives). */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== typeof b || a == null || b == null) return false;\n if (typeof a !== \"object\") return false;\n const aArr = Array.isArray(a);\n if (aArr !== Array.isArray(b)) return false;\n if (aArr) {\n const x = a as unknown[];\n const y = b as unknown[];\n return x.length === y.length && x.every((v, i) => deepEqual(v, y[i]));\n }\n const x = a as Record<string, unknown>;\n const y = b as Record<string, unknown>;\n const xk = Object.keys(x);\n const yk = Object.keys(y);\n return xk.length === yk.length && xk.every((k) => deepEqual(x[k], y[k]));\n}\n\n/**\n * Decide whether the host HONORED a decision-bearing response, judging the\n * PARSED reply against each adapter's ACTUAL (event, decision) contract — never\n * by substring-matching the serialized JSON. `allow`/no-decision is always\n * pass-through honored; every other decision is honored only when the decoded\n * reply carries that exact intent, and the verdict names the documented\n * degradation when it does not.\n */\nfunction judgeHookHonor(\n host: string,\n event: HookEventName,\n response: HookResponse,\n stdout: string | undefined,\n): { honored: boolean; reason: string } {\n const decision = response.decision;\n if (decision === undefined || decision === \"allow\") {\n return { honored: true, reason: \"pass-through allow\" };\n }\n\n const reply = parseReplyStdout(stdout);\n const hso = reply?.hookSpecificOutput;\n\n if (decision === \"context\") {\n // Honored only when the DECODED additionalContext equals what the handler\n // asked to inject (claude-code's hookSpecificOutput.additionalContext, or a\n // future top-level systemMessage). codex only emits this on\n // SessionStart/PostToolUse — every other event silently drops it.\n const want = response.additionalContext;\n const got = hso?.additionalContext ?? reply?.systemMessage;\n if (reply && got === want) {\n return { honored: true, reason: `${host} injects context on ${event}` };\n }\n return {\n honored: false,\n reason: `${host} drops context on ${event} (no stdout path)`,\n };\n }\n\n if (decision === \"deny\") {\n if (!reply) {\n return {\n honored: false,\n reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`,\n };\n }\n // A real block: PreToolUse's permissionDecision:\"deny\", or the\n // PermissionRequest nested decision.behavior:\"deny\".\n if (hso?.permissionDecision === \"deny\" || hso?.decision?.behavior === \"deny\") {\n return { honored: true, reason: `${host} blocks ${event}` };\n }\n // Stop-class continuation: top-level decision:\"block\" KEEPS the session /\n // subagent running (persistence) — the SEMANTIC OPPOSITE of a block. Honored\n // (the reply carried the intent), but the reason must not say \"blocks\".\n if (reply.decision === \"block\") {\n return {\n honored: true,\n reason: `${host} continues the session/subagent on ${event} (persistence) — NOT a block`,\n };\n }\n // SubagentStart / PostToolUseFailure DEGRADE a deny to a context note — the\n // spawn/failure is not actually blocked.\n if (hso?.additionalContext != null) {\n return {\n honored: false,\n reason: `${host} degrades deny to a context note on ${event}; spawn/failure not blocked`,\n };\n }\n return {\n honored: false,\n reason: `${host} drops deny on ${event} (deny not honored; fails open to allow)`,\n };\n }\n\n if (decision === \"ask\") {\n if (hso?.permissionDecision === \"ask\") {\n return { honored: true, reason: `${host} asks on ${event}` };\n }\n // On PermissionRequest, ask/void falls through to the host's NATIVE dialog\n // (the dialog IS the ask), which the adapter expresses as a bare exit-0.\n if (event === \"PermissionRequest\" && !reply) {\n return {\n honored: true,\n reason: `${host} shows the native confirmation dialog on ${event} (the ask)`,\n };\n }\n return {\n honored: false,\n reason: `${host} drops ask on ${event} (no ask path)`,\n };\n }\n\n // decision === \"modify\": honored only when the decoded updatedInput deep-equals\n // what the handler asked to write (hookSpecificOutput.updatedInput, or the\n // PermissionRequest nested decision.updatedInput).\n const wrote = hso?.updatedInput ?? hso?.decision?.updatedInput;\n if (reply && wrote !== undefined && deepEqual(wrote, response.updatedInput)) {\n return { honored: true, reason: `${host} rewrites tool input on ${event}` };\n }\n return {\n honored: false,\n reason: `${host} drops modify on ${event} (modify not honored)`,\n };\n}\n\n/**\n * Run the connector against ONE host-shaped payload through the REAL adapter\n * path and report whether the host honors the response. Self-contained (does not\n * touch the registered-connector loader or telemetry) and fail-safe: any throw\n * in the chain degrades to `honored:false` with the error message, never rejects.\n */\nexport async function simulate(\n connector: ResolvedConnector,\n opts: SimulateOptions,\n): Promise<SimulateResult> {\n try {\n const adapter = await loadAdapter(opts.host);\n if (!adapter) {\n return { honored: false, reason: `unknown host \"${opts.host}\"` };\n }\n\n if (opts.surface === \"statusline\") {\n if (\n !(adapter.capabilities.supportsStatusline ?? false) ||\n !adapter.parseStatusInput ||\n !adapter.formatStatusOutput\n ) {\n return { honored: false, reason: `${opts.host} has no statusline surface` };\n }\n // Guarding on the TOP-LEVEL render is sufficient: a `hosts`-only statusline\n // (a hosts: map with no top-level render) is rejected at defineConnector, so\n // a connector that targets `opts.host` only via hosts[] still has a top-level\n // render here. (If that author-time invariant ever relaxes, revisit this.)\n if (!connector.statusline || typeof connector.statusline.render !== \"function\") {\n return { honored: false, reason: \"connector declares no statusline\" };\n }\n const ctx = adapter.parseStatusInput(coerceInput(opts.input));\n ctx.connectorId = connector.id;\n // Defensive: parseStatusInput now sets ctx.capabilities (it is required on\n // StatuslineContext), but a future adapter could omit it — backfill from\n // the adapter so render() can always read ctx.capabilities.\n if (ctx.capabilities == null) ctx.capabilities = adapter.capabilities;\n // Per-host render override, mirroring the runtime: hosts[host].render wins\n // over the top-level render; an unlisted host falls back to top-level.\n const perHost = connector.statusline.hosts?.[opts.host as PlatformId]?.render;\n const render =\n typeof perHost === \"function\" ? perHost : connector.statusline.render;\n const rendered = await render(ctx);\n const line = rendered == null ? \"\" : String(rendered);\n const reply = adapter.formatStatusOutput(line);\n return {\n honored: true,\n ...(reply.stdout !== undefined ? { hostReply: reply.stdout } : {}),\n reason: \"rendered\",\n };\n }\n\n // surface === \"hooks\"\n const event = opts.event;\n if (!event) {\n return { honored: false, reason: \"event is required when surface === \\\"hooks\\\"\" };\n }\n if (!adapter.parseEvent || !adapter.formatReply) {\n return { honored: false, reason: `${opts.host} has no hook runtime (mcp-only)` };\n }\n\n const evt = adapter.parseEvent(event, coerceInput(opts.input));\n evt.connectorId = connector.id;\n // Mirror the runtime: runHook backfills evt.capabilities from the adapter so\n // a handler reading evt.capabilities behaves the same here. (scope/telemetry\n // are runtime-only — recovered from the registered meta / store, which the\n // offline harness deliberately never touches — so they stay undefined.)\n evt.capabilities = adapter.capabilities;\n\n // Guarding on the TOP-LEVEL handler is sufficient: a `hosts`-only hook (a\n // hosts: map with no top-level handler) is rejected at defineConnector, so a\n // host targeted only via hosts[] still has a top-level handler here. (If that\n // author-time invariant ever relaxes, revisit this.)\n const definition = connector.hooks[event];\n if (!definition || typeof definition.handler !== \"function\") {\n return { honored: false, reason: `connector declares no ${event} handler` };\n }\n\n // Matcher filtering, in lockstep with runHook: when the connector's matcher\n // is set and the event's subject (tool name, or agent type for Subagent*\n // events) does not match, the host never runs the handler — so the harness\n // must NOT run it either, and reports the (honored) exclusion. Reuses the\n // runtime helpers directly so the two stay byte-for-byte aligned.\n const subject = eventMatcherSubject(evt as NormalizedEvent);\n if (subject !== undefined) {\n const re = compileMatcher(definition.matcher);\n if (re && !re.test(subject)) {\n return {\n honored: true,\n reason: `matcher excludes ${subject} (handler not run)`,\n };\n }\n }\n\n // Per-host handler override, mirroring runHook: hosts[host].handler wins\n // over the top-level handler; an unlisted host falls back to top-level.\n const perHost = definition.hosts?.[opts.host as PlatformId]?.handler;\n const handler = typeof perHost === \"function\" ? perHost : definition.handler;\n const response = normalizeResponse(\n await handler(evt as never),\n );\n const reply = adapter.formatReply(event, response);\n const verdict = judgeHookHonor(String(opts.host), event, response, reply.stdout);\n return {\n honored: verdict.honored,\n ...(reply.stdout !== undefined ? { hostReply: reply.stdout } : {}),\n reason: verdict.reason,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const verb = opts.surface === \"statusline\" ? \"render\" : \"handler\";\n return { honored: false, reason: `${verb} threw: ${message}` };\n }\n}\n"],"mappings":"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","names":["x","y","perHost","reply"]}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
fail
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-O5TWCIJN.js";
|
|
5
5
|
import {
|
|
6
6
|
runServe
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-BCHWFTV4.js";
|
|
7
|
+
} from "./chunk-5OWS5CMA.js";
|
|
9
8
|
import "./chunk-OP4KBXUO.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-F5RX62T7.js";
|
|
10
|
+
import "./chunk-EGCBZ2DC.js";
|
|
11
|
+
import "./chunk-2AVLX7GH.js";
|
|
12
|
+
import "./chunk-BCHWFTV4.js";
|
|
13
|
+
import "./chunk-KHO5WNTP.js";
|
|
14
|
+
import "./chunk-24IDL5RW.js";
|
|
15
|
+
import "./chunk-4ENJK5S2.js";
|
|
16
|
+
import "./chunk-DCKFYWGM.js";
|
|
17
|
+
import "./chunk-FAHIMJRU.js";
|
|
18
|
+
import "./chunk-I5MLE6S7.js";
|
|
15
19
|
import "./chunk-N6HH7K6P.js";
|
|
16
20
|
import "./chunk-TRQKHP7D.js";
|
|
17
21
|
|
|
@@ -63,4 +67,4 @@ async function run(argv) {
|
|
|
63
67
|
export {
|
|
64
68
|
run
|
|
65
69
|
};
|
|
66
|
-
//# sourceMappingURL=serve-
|
|
70
|
+
//# sourceMappingURL=serve-GER7URPR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/serve.ts"],"sourcesContent":["/**\n * cli/commands/serve — the telemetry-wrapping MCP stdio proxy entrypoint.\n *\n * agent-connector serve --connector <id> [--scope <user|project>] [--host <platformId>] -- <realCommand> <realArgs...>\n *\n * A stdio MCP server that opts into transparent telemetry has its host config\n * rewritten to launch THIS instead of the server directly. We split argv at the\n * literal `--` separator: flags on the left, the real server command + args on\n * the right (taken verbatim). The optional `--scope` flag records the install\n * dimension (global user vs project-local) onto telemetry; it is absent for\n * configs written before scope plumbing existed → the runtime reads \"unknown\".\n * The optional `--host` flag carries the install TARGET platform id so the proxy\n * stamps hostPlatform correctly under a headless spawn; absent configs fall back\n * to runtime env detection.\n * runServe stands up the per-session telemetry context and proxies bytes both\n * ways, resolving with the child's exit code.\n */\n\nimport { parseArgs } from \"node:util\";\n\nimport type { PlatformId } from \"../../core/types.js\";\nimport type { TelemetryInstallScope } from \"../../telemetry/types.js\";\nimport { runServe } from \"../../runtime/index.js\";\nimport { fail } from \"../app.js\";\n\nexport async function run(argv: string[]): Promise<number> {\n // Split at the FIRST literal \"--\": everything after it is the real server\n // invocation and must be passed through verbatim (never re-parsed as flags).\n const sepIndex = argv.indexOf(\"--\");\n if (sepIndex === -1) {\n return fail(\n \"usage: agent-connector serve --connector <id> [--scope <user|project>] [--host <platformId>] -- <command> [args...]\",\n );\n }\n const flagArgs = argv.slice(0, sepIndex);\n const serverInvocation = argv.slice(sepIndex + 1);\n\n // strict:false so an unknown FUTURE flag before `--` (written by a newer host\n // config) is ignored instead of throwing — a parse throw here would prevent the\n // real MCP server from ever spawning and wedge the host's tool call. We only\n // read the two flags we understand; everything else is tolerated.\n const { values } = parseArgs({\n args: flagArgs,\n options: {\n connector: { type: \"string\" },\n scope: { type: \"string\" },\n // `--host <platformId>` bakes the install TARGET platform into the wrapper\n // so the proxy stamps hostPlatform correctly under a headless spawn (where\n // runtime env markers are absent). Optional + tolerated (strict:false).\n host: { type: \"string\" },\n },\n allowPositionals: true,\n strict: false,\n });\n\n // With strict:false, parseArgs types each value as string | boolean; we only\n // accept the string form (a bare `--connector` flag with no value is invalid).\n const connectorId = typeof values.connector === \"string\" ? values.connector : undefined;\n if (!connectorId || connectorId.trim() === \"\") {\n return fail(\"serve requires --connector <id>\");\n }\n\n // `--scope` is optional and only ever \"user\" | \"project\" (the wrapper emits\n // exactly those). An unrecognized value is ignored rather than fatal so a\n // future/older wrapper can never break a real tool call over a flag mismatch.\n const rawScope = values.scope;\n const installScope: TelemetryInstallScope | undefined =\n rawScope === \"user\" || rawScope === \"project\" ? rawScope : undefined;\n\n // `--host` carries the install TARGET platform id (string only). runServe\n // validates it against the registry and falls back to runtime detection when\n // it is absent or unrecognized — so a future/older wrapper never breaks here.\n const hostPlatformOverride =\n typeof values.host === \"string\" ? (values.host as PlatformId) : undefined;\n\n const serverCommand = serverInvocation[0];\n if (!serverCommand) {\n return fail(\"serve requires a command after `--`\");\n }\n const serverArgs = serverInvocation.slice(1);\n\n const code = await runServe({\n connectorId,\n serverCommand,\n serverArgs,\n installScope,\n hostPlatformOverride,\n });\n process.exit(code);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/serve.ts"],"sourcesContent":["/**\n * cli/commands/serve — the telemetry-wrapping MCP stdio proxy entrypoint.\n *\n * agent-connector serve --connector <id> [--scope <user|project>] [--host <platformId>] -- <realCommand> <realArgs...>\n *\n * A stdio MCP server that opts into transparent telemetry has its host config\n * rewritten to launch THIS instead of the server directly. We split argv at the\n * literal `--` separator: flags on the left, the real server command + args on\n * the right (taken verbatim). The optional `--scope` flag records the install\n * dimension (global user vs project-local) onto telemetry; it is absent for\n * configs written before scope plumbing existed → the runtime reads \"unknown\".\n * The optional `--host` flag carries the install TARGET platform id so the proxy\n * stamps hostPlatform correctly under a headless spawn; absent configs fall back\n * to runtime env detection.\n * runServe stands up the per-session telemetry context and proxies bytes both\n * ways, resolving with the child's exit code.\n */\n\nimport { parseArgs } from \"node:util\";\n\nimport type { PlatformId } from \"../../core/types.js\";\nimport type { TelemetryInstallScope } from \"../../telemetry/types.js\";\nimport { runServe } from \"../../runtime/index.js\";\nimport { fail } from \"../app.js\";\n\nexport async function run(argv: string[]): Promise<number> {\n // Split at the FIRST literal \"--\": everything after it is the real server\n // invocation and must be passed through verbatim (never re-parsed as flags).\n const sepIndex = argv.indexOf(\"--\");\n if (sepIndex === -1) {\n return fail(\n \"usage: agent-connector serve --connector <id> [--scope <user|project>] [--host <platformId>] -- <command> [args...]\",\n );\n }\n const flagArgs = argv.slice(0, sepIndex);\n const serverInvocation = argv.slice(sepIndex + 1);\n\n // strict:false so an unknown FUTURE flag before `--` (written by a newer host\n // config) is ignored instead of throwing — a parse throw here would prevent the\n // real MCP server from ever spawning and wedge the host's tool call. We only\n // read the two flags we understand; everything else is tolerated.\n const { values } = parseArgs({\n args: flagArgs,\n options: {\n connector: { type: \"string\" },\n scope: { type: \"string\" },\n // `--host <platformId>` bakes the install TARGET platform into the wrapper\n // so the proxy stamps hostPlatform correctly under a headless spawn (where\n // runtime env markers are absent). Optional + tolerated (strict:false).\n host: { type: \"string\" },\n },\n allowPositionals: true,\n strict: false,\n });\n\n // With strict:false, parseArgs types each value as string | boolean; we only\n // accept the string form (a bare `--connector` flag with no value is invalid).\n const connectorId = typeof values.connector === \"string\" ? values.connector : undefined;\n if (!connectorId || connectorId.trim() === \"\") {\n return fail(\"serve requires --connector <id>\");\n }\n\n // `--scope` is optional and only ever \"user\" | \"project\" (the wrapper emits\n // exactly those). An unrecognized value is ignored rather than fatal so a\n // future/older wrapper can never break a real tool call over a flag mismatch.\n const rawScope = values.scope;\n const installScope: TelemetryInstallScope | undefined =\n rawScope === \"user\" || rawScope === \"project\" ? rawScope : undefined;\n\n // `--host` carries the install TARGET platform id (string only). runServe\n // validates it against the registry and falls back to runtime detection when\n // it is absent or unrecognized — so a future/older wrapper never breaks here.\n const hostPlatformOverride =\n typeof values.host === \"string\" ? (values.host as PlatformId) : undefined;\n\n const serverCommand = serverInvocation[0];\n if (!serverCommand) {\n return fail(\"serve requires a command after `--`\");\n }\n const serverArgs = serverInvocation.slice(1);\n\n const code = await runServe({\n connectorId,\n serverCommand,\n serverArgs,\n installScope,\n hostPlatformOverride,\n });\n process.exit(code);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAkBA,SAAS,iBAAiB;AAO1B,eAAsB,IAAI,MAAiC;AAGzD,QAAM,WAAW,KAAK,QAAQ,IAAI;AAClC,MAAI,aAAa,IAAI;AACnB,WAAO;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACA,QAAM,WAAW,KAAK,MAAM,GAAG,QAAQ;AACvC,QAAM,mBAAmB,KAAK,MAAM,WAAW,CAAC;AAMhD,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,MAAM;AAAA,IACN,SAAS;AAAA,MACP,WAAW,EAAE,MAAM,SAAS;AAAA,MAC5B,OAAO,EAAE,MAAM,SAAS;AAAA;AAAA;AAAA;AAAA,MAIxB,MAAM,EAAE,MAAM,SAAS;AAAA,IACzB;AAAA,IACA,kBAAkB;AAAA,IAClB,QAAQ;AAAA,EACV,CAAC;AAID,QAAM,cAAc,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;AAC9E,MAAI,CAAC,eAAe,YAAY,KAAK,MAAM,IAAI;AAC7C,WAAO,KAAK,iCAAiC;AAAA,EAC/C;AAKA,QAAM,WAAW,OAAO;AACxB,QAAM,eACJ,aAAa,UAAU,aAAa,YAAY,WAAW;AAK7D,QAAM,uBACJ,OAAO,OAAO,SAAS,WAAY,OAAO,OAAsB;AAElE,QAAM,gBAAgB,iBAAiB,CAAC;AACxC,MAAI,CAAC,eAAe;AAClB,WAAO,KAAK,qCAAqC;AAAA,EACnD;AACA,QAAM,aAAa,iBAAiB,MAAM,CAAC;AAE3C,QAAM,OAAO,MAAM,SAAS;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,UAAQ,KAAK,IAAI;AACnB;","names":[]}
|