@sammorrowdrums/mcpi-ext 0.1.0

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.
Files changed (213) hide show
  1. package/.dockerignore +6 -0
  2. package/.github/dependabot.yml +25 -0
  3. package/.github/workflows/ci.yml +50 -0
  4. package/.hadolint.yaml +6 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc +7 -0
  7. package/AGENTS.md +118 -0
  8. package/DECISIONS.md +99 -0
  9. package/Dockerfile +39 -0
  10. package/README.md +278 -0
  11. package/aube-lock.yaml +4146 -0
  12. package/dist/code-mode/code-mode-integration.test.d.ts +2 -0
  13. package/dist/code-mode/code-mode-integration.test.d.ts.map +1 -0
  14. package/dist/code-mode/code-mode-integration.test.js +116 -0
  15. package/dist/code-mode/code-mode-integration.test.js.map +1 -0
  16. package/dist/code-mode/eligibility.d.ts +10 -0
  17. package/dist/code-mode/eligibility.d.ts.map +1 -0
  18. package/dist/code-mode/eligibility.js +14 -0
  19. package/dist/code-mode/eligibility.js.map +1 -0
  20. package/dist/code-mode/eligibility.test.d.ts +2 -0
  21. package/dist/code-mode/eligibility.test.d.ts.map +1 -0
  22. package/dist/code-mode/eligibility.test.js +53 -0
  23. package/dist/code-mode/eligibility.test.js.map +1 -0
  24. package/dist/code-mode/executor.d.ts +35 -0
  25. package/dist/code-mode/executor.d.ts.map +1 -0
  26. package/dist/code-mode/executor.js +120 -0
  27. package/dist/code-mode/executor.js.map +1 -0
  28. package/dist/code-mode/executor.test.d.ts +2 -0
  29. package/dist/code-mode/executor.test.d.ts.map +1 -0
  30. package/dist/code-mode/executor.test.js +130 -0
  31. package/dist/code-mode/executor.test.js.map +1 -0
  32. package/dist/code-mode/index.d.ts +65 -0
  33. package/dist/code-mode/index.d.ts.map +1 -0
  34. package/dist/code-mode/index.js +175 -0
  35. package/dist/code-mode/index.js.map +1 -0
  36. package/dist/code-mode/tools.d.ts +45 -0
  37. package/dist/code-mode/tools.d.ts.map +1 -0
  38. package/dist/code-mode/tools.js +70 -0
  39. package/dist/code-mode/tools.js.map +1 -0
  40. package/dist/code-mode/type-hints.d.ts +20 -0
  41. package/dist/code-mode/type-hints.d.ts.map +1 -0
  42. package/dist/code-mode/type-hints.js +189 -0
  43. package/dist/code-mode/type-hints.js.map +1 -0
  44. package/dist/code-mode/type-hints.test.d.ts +2 -0
  45. package/dist/code-mode/type-hints.test.d.ts.map +1 -0
  46. package/dist/code-mode/type-hints.test.js +157 -0
  47. package/dist/code-mode/type-hints.test.js.map +1 -0
  48. package/dist/docker-e2e.d.ts +55 -0
  49. package/dist/docker-e2e.d.ts.map +1 -0
  50. package/dist/docker-e2e.js +235 -0
  51. package/dist/docker-e2e.js.map +1 -0
  52. package/dist/docker-e2e.test.d.ts +2 -0
  53. package/dist/docker-e2e.test.d.ts.map +1 -0
  54. package/dist/docker-e2e.test.js +176 -0
  55. package/dist/docker-e2e.test.js.map +1 -0
  56. package/dist/index.d.ts +3 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +128 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/index.test.d.ts +2 -0
  61. package/dist/index.test.d.ts.map +1 -0
  62. package/dist/index.test.js +8 -0
  63. package/dist/index.test.js.map +1 -0
  64. package/dist/mcp/client-manager.d.ts +46 -0
  65. package/dist/mcp/client-manager.d.ts.map +1 -0
  66. package/dist/mcp/client-manager.js +148 -0
  67. package/dist/mcp/client-manager.js.map +1 -0
  68. package/dist/mcp/client-manager.test.d.ts +2 -0
  69. package/dist/mcp/client-manager.test.d.ts.map +1 -0
  70. package/dist/mcp/client-manager.test.js +204 -0
  71. package/dist/mcp/client-manager.test.js.map +1 -0
  72. package/dist/mcp/config-loader.d.ts +8 -0
  73. package/dist/mcp/config-loader.d.ts.map +1 -0
  74. package/dist/mcp/config-loader.js +43 -0
  75. package/dist/mcp/config-loader.js.map +1 -0
  76. package/dist/mcp/config-loader.test.d.ts +2 -0
  77. package/dist/mcp/config-loader.test.d.ts.map +1 -0
  78. package/dist/mcp/config-loader.test.js +93 -0
  79. package/dist/mcp/config-loader.test.js.map +1 -0
  80. package/dist/mcp/config.d.ts +46 -0
  81. package/dist/mcp/config.d.ts.map +1 -0
  82. package/dist/mcp/config.js +28 -0
  83. package/dist/mcp/config.js.map +1 -0
  84. package/dist/mcp/index.d.ts +5 -0
  85. package/dist/mcp/index.d.ts.map +1 -0
  86. package/dist/mcp/index.js +4 -0
  87. package/dist/mcp/index.js.map +1 -0
  88. package/dist/skills/discover.d.ts +10 -0
  89. package/dist/skills/discover.d.ts.map +1 -0
  90. package/dist/skills/discover.js +60 -0
  91. package/dist/skills/discover.js.map +1 -0
  92. package/dist/skills/format.d.ts +10 -0
  93. package/dist/skills/format.d.ts.map +1 -0
  94. package/dist/skills/format.js +38 -0
  95. package/dist/skills/format.js.map +1 -0
  96. package/dist/skills/format.test.d.ts +2 -0
  97. package/dist/skills/format.test.d.ts.map +1 -0
  98. package/dist/skills/format.test.js +63 -0
  99. package/dist/skills/format.test.js.map +1 -0
  100. package/dist/skills/index.d.ts +6 -0
  101. package/dist/skills/index.d.ts.map +1 -0
  102. package/dist/skills/index.js +6 -0
  103. package/dist/skills/index.js.map +1 -0
  104. package/dist/skills/load-skill-tool.d.ts +40 -0
  105. package/dist/skills/load-skill-tool.d.ts.map +1 -0
  106. package/dist/skills/load-skill-tool.js +104 -0
  107. package/dist/skills/load-skill-tool.js.map +1 -0
  108. package/dist/skills/mcp-tool-proxy.d.ts +11 -0
  109. package/dist/skills/mcp-tool-proxy.d.ts.map +1 -0
  110. package/dist/skills/mcp-tool-proxy.js +116 -0
  111. package/dist/skills/mcp-tool-proxy.js.map +1 -0
  112. package/dist/skills/skill-integration.test.d.ts +2 -0
  113. package/dist/skills/skill-integration.test.d.ts.map +1 -0
  114. package/dist/skills/skill-integration.test.js +119 -0
  115. package/dist/skills/skill-integration.test.js.map +1 -0
  116. package/dist/skills/skill-registry.d.ts +39 -0
  117. package/dist/skills/skill-registry.d.ts.map +1 -0
  118. package/dist/skills/skill-registry.js +48 -0
  119. package/dist/skills/skill-registry.js.map +1 -0
  120. package/dist/skills/skill-registry.test.d.ts +2 -0
  121. package/dist/skills/skill-registry.test.d.ts.map +1 -0
  122. package/dist/skills/skill-registry.test.js +67 -0
  123. package/dist/skills/skill-registry.test.js.map +1 -0
  124. package/dist/test-servers/weather-server.d.ts +12 -0
  125. package/dist/test-servers/weather-server.d.ts.map +1 -0
  126. package/dist/test-servers/weather-server.js +139 -0
  127. package/dist/test-servers/weather-server.js.map +1 -0
  128. package/dist/test-servers/weather-stdio.d.ts +2 -0
  129. package/dist/test-servers/weather-stdio.d.ts.map +1 -0
  130. package/dist/test-servers/weather-stdio.js +11 -0
  131. package/dist/test-servers/weather-stdio.js.map +1 -0
  132. package/dist/tool-cli/cli.d.ts +3 -0
  133. package/dist/tool-cli/cli.d.ts.map +1 -0
  134. package/dist/tool-cli/cli.js +179 -0
  135. package/dist/tool-cli/cli.js.map +1 -0
  136. package/dist/tool-cli/constants.d.ts +7 -0
  137. package/dist/tool-cli/constants.d.ts.map +1 -0
  138. package/dist/tool-cli/constants.js +15 -0
  139. package/dist/tool-cli/constants.js.map +1 -0
  140. package/dist/tool-cli/format.d.ts +8 -0
  141. package/dist/tool-cli/format.d.ts.map +1 -0
  142. package/dist/tool-cli/format.js +57 -0
  143. package/dist/tool-cli/format.js.map +1 -0
  144. package/dist/tool-cli/format.test.d.ts +2 -0
  145. package/dist/tool-cli/format.test.d.ts.map +1 -0
  146. package/dist/tool-cli/format.test.js +30 -0
  147. package/dist/tool-cli/format.test.js.map +1 -0
  148. package/dist/tool-cli/index.d.ts +4 -0
  149. package/dist/tool-cli/index.d.ts.map +1 -0
  150. package/dist/tool-cli/index.js +4 -0
  151. package/dist/tool-cli/index.js.map +1 -0
  152. package/dist/tool-cli/rpc-client.d.ts +6 -0
  153. package/dist/tool-cli/rpc-client.d.ts.map +1 -0
  154. package/dist/tool-cli/rpc-client.js +25 -0
  155. package/dist/tool-cli/rpc-client.js.map +1 -0
  156. package/dist/tool-cli/rpc-server.d.ts +48 -0
  157. package/dist/tool-cli/rpc-server.d.ts.map +1 -0
  158. package/dist/tool-cli/rpc-server.js +220 -0
  159. package/dist/tool-cli/rpc-server.js.map +1 -0
  160. package/dist/tool-cli/rpc-server.test.d.ts +2 -0
  161. package/dist/tool-cli/rpc-server.test.d.ts.map +1 -0
  162. package/dist/tool-cli/rpc-server.test.js +154 -0
  163. package/dist/tool-cli/rpc-server.test.js.map +1 -0
  164. package/eslint.config.js +27 -0
  165. package/images/banner.webp +0 -0
  166. package/images/code-c-maude.webp +0 -0
  167. package/images/nuclear-mcp-football.webp +0 -0
  168. package/images/the-skill-dealer.webp +0 -0
  169. package/mise.toml +65 -0
  170. package/package.json +46 -0
  171. package/scripts/docker-entrypoint.sh +29 -0
  172. package/scripts/test-echo-server.mjs +28 -0
  173. package/scripts/test-mcp-integration.mjs +59 -0
  174. package/scripts/test-tool-cli-smoke.mjs +62 -0
  175. package/src/code-mode/code-mode-integration.test.ts +135 -0
  176. package/src/code-mode/eligibility.test.ts +60 -0
  177. package/src/code-mode/eligibility.ts +16 -0
  178. package/src/code-mode/executor.test.ts +168 -0
  179. package/src/code-mode/executor.ts +163 -0
  180. package/src/code-mode/index.ts +208 -0
  181. package/src/code-mode/tools.ts +110 -0
  182. package/src/code-mode/type-hints.test.ts +187 -0
  183. package/src/code-mode/type-hints.ts +249 -0
  184. package/src/docker-e2e.test.ts +195 -0
  185. package/src/docker-e2e.ts +277 -0
  186. package/src/index.test.ts +8 -0
  187. package/src/index.ts +158 -0
  188. package/src/mcp/client-manager.test.ts +250 -0
  189. package/src/mcp/client-manager.ts +199 -0
  190. package/src/mcp/config-loader.test.ts +115 -0
  191. package/src/mcp/config-loader.ts +46 -0
  192. package/src/mcp/config.ts +40 -0
  193. package/src/mcp/index.ts +4 -0
  194. package/src/skills/discover.ts +81 -0
  195. package/src/skills/format.test.ts +70 -0
  196. package/src/skills/format.ts +42 -0
  197. package/src/skills/index.ts +9 -0
  198. package/src/skills/load-skill-tool.ts +136 -0
  199. package/src/skills/mcp-tool-proxy.ts +143 -0
  200. package/src/skills/skill-integration.test.ts +139 -0
  201. package/src/skills/skill-registry.test.ts +83 -0
  202. package/src/skills/skill-registry.ts +69 -0
  203. package/src/test-servers/weather-server.ts +171 -0
  204. package/src/test-servers/weather-stdio.ts +11 -0
  205. package/src/tool-cli/cli.ts +232 -0
  206. package/src/tool-cli/constants.ts +15 -0
  207. package/src/tool-cli/format.test.ts +33 -0
  208. package/src/tool-cli/format.ts +56 -0
  209. package/src/tool-cli/index.ts +3 -0
  210. package/src/tool-cli/rpc-client.ts +39 -0
  211. package/src/tool-cli/rpc-server.test.ts +193 -0
  212. package/src/tool-cli/rpc-server.ts +287 -0
  213. package/tsconfig.json +21 -0
@@ -0,0 +1,157 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { generateTypeHints, jsonSchemaToTypeString, sanitizeToolName } from "./type-hints.js";
3
+ describe("jsonSchemaToTypeString", () => {
4
+ it("converts string type", () => {
5
+ expect(jsonSchemaToTypeString({ type: "string" })).toBe("string");
6
+ });
7
+ it("converts number type", () => {
8
+ expect(jsonSchemaToTypeString({ type: "number" })).toBe("number");
9
+ });
10
+ it("converts integer type", () => {
11
+ expect(jsonSchemaToTypeString({ type: "integer" })).toBe("number");
12
+ });
13
+ it("converts boolean type", () => {
14
+ expect(jsonSchemaToTypeString({ type: "boolean" })).toBe("boolean");
15
+ });
16
+ it("converts null type", () => {
17
+ expect(jsonSchemaToTypeString({ type: "null" })).toBe("null");
18
+ });
19
+ it("converts type array", () => {
20
+ expect(jsonSchemaToTypeString({ type: ["string", "null"] })).toBe("string | null");
21
+ });
22
+ it("converts enum to union", () => {
23
+ expect(jsonSchemaToTypeString({ enum: ["a", "b", "c"] })).toBe('"a" | "b" | "c"');
24
+ });
25
+ it("converts const", () => {
26
+ expect(jsonSchemaToTypeString({ const: "fixed" })).toBe('"fixed"');
27
+ });
28
+ it("converts simple object", () => {
29
+ const result = jsonSchemaToTypeString({
30
+ type: "object",
31
+ properties: {
32
+ name: { type: "string" },
33
+ age: { type: "number" },
34
+ },
35
+ required: ["name"],
36
+ });
37
+ expect(result).toContain("name: string;");
38
+ expect(result).toContain("age?: number;");
39
+ });
40
+ it("converts empty object to Record", () => {
41
+ expect(jsonSchemaToTypeString({ type: "object" })).toBe("Record<string, unknown>");
42
+ });
43
+ it("converts array with items", () => {
44
+ expect(jsonSchemaToTypeString({ type: "array", items: { type: "string" } })).toBe("string[]");
45
+ });
46
+ it("converts array without items", () => {
47
+ expect(jsonSchemaToTypeString({ type: "array" })).toBe("unknown[]");
48
+ });
49
+ it("handles anyOf", () => {
50
+ const result = jsonSchemaToTypeString({
51
+ anyOf: [{ type: "string" }, { type: "number" }],
52
+ });
53
+ expect(result).toBe("string | number");
54
+ });
55
+ it("handles oneOf", () => {
56
+ const result = jsonSchemaToTypeString({
57
+ oneOf: [{ type: "boolean" }, { type: "null" }],
58
+ });
59
+ expect(result).toBe("boolean | null");
60
+ });
61
+ it("handles allOf", () => {
62
+ const result = jsonSchemaToTypeString({
63
+ allOf: [
64
+ { type: "object", properties: { a: { type: "string" } }, required: ["a"] },
65
+ { type: "object", properties: { b: { type: "number" } }, required: ["b"] },
66
+ ],
67
+ });
68
+ expect(result).toContain("a: string;");
69
+ expect(result).toContain("b: number;");
70
+ expect(result).toContain("&");
71
+ });
72
+ it("resolves $ref", () => {
73
+ const result = jsonSchemaToTypeString({ $ref: "#/definitions/Foo" }, { Foo: { type: "string" } });
74
+ expect(result).toBe("string");
75
+ });
76
+ it("handles unknown $ref gracefully", () => {
77
+ expect(jsonSchemaToTypeString({ $ref: "#/definitions/Missing" }, {})).toBe("unknown");
78
+ });
79
+ it("guards against deep recursion", () => {
80
+ // Create a deeply nested schema
81
+ let schema = { type: "string" };
82
+ for (let i = 0; i < 25; i++) {
83
+ schema = { type: "object", properties: { nested: schema }, required: ["nested"] };
84
+ }
85
+ const result = jsonSchemaToTypeString(schema);
86
+ expect(result).toContain("unknown"); // Should hit depth guard
87
+ });
88
+ it("includes description as JSDoc in object properties", () => {
89
+ const result = jsonSchemaToTypeString({
90
+ type: "object",
91
+ properties: {
92
+ query: { type: "string", description: "Search term" },
93
+ },
94
+ required: ["query"],
95
+ });
96
+ expect(result).toContain("/** Search term */");
97
+ });
98
+ });
99
+ describe("sanitizeToolName", () => {
100
+ it("replaces hyphens with underscores", () => {
101
+ expect(sanitizeToolName("list-issues")).toBe("list_issues");
102
+ });
103
+ it("replaces dots with underscores", () => {
104
+ expect(sanitizeToolName("github.search")).toBe("github_search");
105
+ });
106
+ it("keeps valid identifiers unchanged", () => {
107
+ expect(sanitizeToolName("searchDocs")).toBe("searchDocs");
108
+ });
109
+ });
110
+ describe("generateTypeHints", () => {
111
+ it("returns empty string for no tools", () => {
112
+ expect(generateTypeHints([])).toBe("");
113
+ });
114
+ it("generates declaration for a simple tool", () => {
115
+ const tools = [
116
+ {
117
+ name: "search_docs",
118
+ description: "Search documentation",
119
+ inputSchema: {
120
+ type: "object",
121
+ properties: {
122
+ query: { type: "string", description: "Search term" },
123
+ },
124
+ required: ["query"],
125
+ },
126
+ outputSchema: {
127
+ type: "object",
128
+ properties: {
129
+ results: { type: "array", items: { type: "string" } },
130
+ },
131
+ required: ["results"],
132
+ },
133
+ serverName: "docs",
134
+ },
135
+ ];
136
+ const result = generateTypeHints(tools);
137
+ expect(result).toContain("declare const codemode");
138
+ expect(result).toContain("search_docs:");
139
+ expect(result).toContain("Promise<");
140
+ expect(result).toContain("listTools");
141
+ expect(result).toContain("describeTools");
142
+ });
143
+ it("sanitizes tool names with special characters", () => {
144
+ const tools = [
145
+ {
146
+ name: "github.list-repos",
147
+ description: "List repos",
148
+ inputSchema: { type: "object", properties: {} },
149
+ outputSchema: { type: "object", properties: {} },
150
+ serverName: "github",
151
+ },
152
+ ];
153
+ const result = generateTypeHints(tools);
154
+ expect(result).toContain("github_list_repos:");
155
+ });
156
+ });
157
+ //# sourceMappingURL=type-hints.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-hints.test.js","sourceRoot":"","sources":["../../src/code-mode/type-hints.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { AgentToolResult, AgentToolUpdateCallback, ExtensionContext } from "@sammorrowdrums/mcpi";
2
+ import { Type, type Static } from "typebox";
3
+ declare const DockerE2EParams: Type.TObject<{
4
+ task: Type.TString;
5
+ token: Type.TString;
6
+ model: Type.TOptional<Type.TString>;
7
+ timeout: Type.TOptional<Type.TNumber>;
8
+ }>;
9
+ type DockerE2EInput = Static<typeof DockerE2EParams>;
10
+ interface RunLogEntry {
11
+ type: string;
12
+ timestamp: number;
13
+ raw: string;
14
+ }
15
+ export interface DockerE2EDetails {
16
+ exitCode: number | null;
17
+ log: RunLogEntry[];
18
+ duration: number;
19
+ model?: string;
20
+ usage?: {
21
+ input: number;
22
+ output: number;
23
+ cacheRead: number;
24
+ cacheWrite: number;
25
+ cost: number;
26
+ turns: number;
27
+ };
28
+ }
29
+ export declare function parseJsonlLine(line: string): RunLogEntry | null;
30
+ export declare function extractAssistantText(log: RunLogEntry[]): string;
31
+ export declare function extractUsage(log: RunLogEntry[]): {
32
+ input: number;
33
+ output: number;
34
+ cacheRead: number;
35
+ cacheWrite: number;
36
+ cost: number;
37
+ turns: number;
38
+ };
39
+ export declare function extractModel(log: RunLogEntry[]): string | undefined;
40
+ export declare function buildDockerArgs(image: string, params: DockerE2EInput): string[];
41
+ export declare const dockerE2ETool: {
42
+ name: string;
43
+ label: string;
44
+ description: string;
45
+ promptSnippet: string;
46
+ parameters: Type.TObject<{
47
+ task: Type.TString;
48
+ token: Type.TString;
49
+ model: Type.TOptional<Type.TString>;
50
+ timeout: Type.TOptional<Type.TNumber>;
51
+ }>;
52
+ execute(_toolCallId: string, params: DockerE2EInput, signal: AbortSignal | undefined, onUpdate: AgentToolUpdateCallback<DockerE2EDetails> | undefined, _ctx: ExtensionContext): Promise<AgentToolResult<DockerE2EDetails>>;
53
+ };
54
+ export {};
55
+ //# sourceMappingURL=docker-e2e.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-e2e.d.ts","sourceRoot":"","sources":["../src/docker-e2e.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAI5C,QAAA,MAAM,eAAe;;;;;EAenB,CAAC;AAEH,KAAK,cAAc,GAAG,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;AAErD,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,WAAW,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAS/D;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,MAAM,CAgB/D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE;;;;;;;EAsB9C;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,SAAS,CAWnE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,EAAE,CAS/E;AAED,eAAO,MAAM,aAAa;;;;;;;;;;;yBAST,MAAM,UACX,cAAc,UACd,WAAW,GAAG,SAAS,YACrB,uBAAuB,CAAC,gBAAgB,CAAC,GAAG,SAAS,QACzD,gBAAgB,GACrB,OAAO,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC;CAuI9C,CAAC"}
@@ -0,0 +1,235 @@
1
+ import { spawn } from "node:child_process";
2
+ import { Type } from "typebox";
3
+ const DOCKER_IMAGE = "mcpi-ext-e2e";
4
+ const DockerE2EParams = Type.Object({
5
+ task: Type.String({ description: "The task/prompt to run inside the sandbox" }),
6
+ token: Type.String({
7
+ description: "GitHub token for Copilot model access",
8
+ }),
9
+ model: Type.Optional(Type.String({
10
+ description: "Model to use (e.g. claude-sonnet-4-5). Defaults to the provider default.",
11
+ })),
12
+ timeout: Type.Optional(Type.Number({
13
+ description: "Timeout in seconds. Defaults to 300 (5 minutes).",
14
+ })),
15
+ });
16
+ export function parseJsonlLine(line) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed)
19
+ return null;
20
+ try {
21
+ const parsed = JSON.parse(trimmed);
22
+ return { type: parsed.type ?? "unknown", timestamp: Date.now(), raw: trimmed };
23
+ }
24
+ catch {
25
+ return { type: "raw", timestamp: Date.now(), raw: trimmed };
26
+ }
27
+ }
28
+ export function extractAssistantText(log) {
29
+ const parts = [];
30
+ for (const entry of log) {
31
+ if (entry.type !== "message_end")
32
+ continue;
33
+ try {
34
+ const event = JSON.parse(entry.raw);
35
+ const msg = event.message;
36
+ if (msg?.role !== "assistant")
37
+ continue;
38
+ for (const part of msg.content ?? []) {
39
+ if (part.type === "text")
40
+ parts.push(part.text);
41
+ }
42
+ }
43
+ catch {
44
+ // skip malformed
45
+ }
46
+ }
47
+ return parts.join("\n");
48
+ }
49
+ export function extractUsage(log) {
50
+ const usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
51
+ for (const entry of log) {
52
+ if (entry.type !== "message_end")
53
+ continue;
54
+ try {
55
+ const event = JSON.parse(entry.raw);
56
+ const msg = event.message;
57
+ if (msg?.role !== "assistant")
58
+ continue;
59
+ usage.turns++;
60
+ const u = msg.usage;
61
+ if (u) {
62
+ usage.input += u.input ?? 0;
63
+ usage.output += u.output ?? 0;
64
+ usage.cacheRead += u.cacheRead ?? 0;
65
+ usage.cacheWrite += u.cacheWrite ?? 0;
66
+ usage.cost += u.cost?.total ?? 0;
67
+ }
68
+ }
69
+ catch {
70
+ // skip
71
+ }
72
+ }
73
+ return usage;
74
+ }
75
+ export function extractModel(log) {
76
+ for (const entry of log) {
77
+ if (entry.type !== "message_end")
78
+ continue;
79
+ try {
80
+ const event = JSON.parse(entry.raw);
81
+ if (event.message?.model)
82
+ return event.message.model;
83
+ }
84
+ catch {
85
+ // skip
86
+ }
87
+ }
88
+ return undefined;
89
+ }
90
+ export function buildDockerArgs(image, params) {
91
+ const args = ["run", "--rm", "-e", `GITHUB_TOKEN=${params.token}`];
92
+ if (params.model) {
93
+ args.push("-e", `PI_MODEL=${params.model}`);
94
+ }
95
+ args.push(image, params.task);
96
+ return args;
97
+ }
98
+ export const dockerE2ETool = {
99
+ name: "docker_e2e",
100
+ label: "Docker E2E",
101
+ description: "Run a task end-to-end inside an isolated Docker container with pi and this project's extensions. Returns the full run log for evaluation.",
102
+ promptSnippet: "Run an isolated e2e task in Docker. Requires a GitHub token and task prompt.",
103
+ parameters: DockerE2EParams,
104
+ async execute(_toolCallId, params, signal, onUpdate, _ctx) {
105
+ const startTime = Date.now();
106
+ const timeoutMs = (params.timeout ?? 300) * 1000;
107
+ const log = [];
108
+ let lastAssistantText = "";
109
+ const dockerArgs = buildDockerArgs(DOCKER_IMAGE, params);
110
+ const exitCode = await new Promise((resolve) => {
111
+ const proc = spawn("docker", dockerArgs, {
112
+ stdio: ["ignore", "pipe", "pipe"],
113
+ });
114
+ let buffer = "";
115
+ let timedOut = false;
116
+ let timeoutHandle;
117
+ if (timeoutMs > 0) {
118
+ timeoutHandle = setTimeout(() => {
119
+ timedOut = true;
120
+ proc.kill("SIGTERM");
121
+ setTimeout(() => {
122
+ if (!proc.killed)
123
+ proc.kill("SIGKILL");
124
+ }, 5000);
125
+ }, timeoutMs);
126
+ }
127
+ const processLine = (line) => {
128
+ const entry = parseJsonlLine(line);
129
+ if (!entry)
130
+ return;
131
+ log.push(entry);
132
+ if (entry.type === "message_end") {
133
+ const text = extractAssistantText(log);
134
+ if (text)
135
+ lastAssistantText = text;
136
+ }
137
+ onUpdate?.({
138
+ content: [
139
+ {
140
+ type: "text",
141
+ text: lastAssistantText || `(running… ${log.length} events)`,
142
+ },
143
+ ],
144
+ details: {
145
+ exitCode: null,
146
+ log,
147
+ duration: Date.now() - startTime,
148
+ model: extractModel(log),
149
+ usage: extractUsage(log),
150
+ },
151
+ });
152
+ };
153
+ proc.stdout.on("data", (data) => {
154
+ buffer += data.toString();
155
+ const lines = buffer.split("\n");
156
+ buffer = lines.pop() ?? "";
157
+ for (const line of lines)
158
+ processLine(line);
159
+ });
160
+ proc.stderr.on("data", (data) => {
161
+ const text = data.toString().trim();
162
+ if (text) {
163
+ log.push({ type: "stderr", timestamp: Date.now(), raw: text });
164
+ }
165
+ });
166
+ proc.on("close", (code) => {
167
+ if (timeoutHandle)
168
+ clearTimeout(timeoutHandle);
169
+ if (buffer.trim())
170
+ processLine(buffer);
171
+ if (timedOut) {
172
+ log.push({
173
+ type: "timeout",
174
+ timestamp: Date.now(),
175
+ raw: `Timed out after ${params.timeout ?? 300}s`,
176
+ });
177
+ }
178
+ resolve(timedOut ? null : (code ?? 1));
179
+ });
180
+ proc.on("error", (err) => {
181
+ if (timeoutHandle)
182
+ clearTimeout(timeoutHandle);
183
+ log.push({
184
+ type: "error",
185
+ timestamp: Date.now(),
186
+ raw: err.message,
187
+ });
188
+ resolve(null);
189
+ });
190
+ if (signal) {
191
+ const onAbort = () => {
192
+ proc.kill("SIGTERM");
193
+ setTimeout(() => {
194
+ if (!proc.killed)
195
+ proc.kill("SIGKILL");
196
+ }, 5000);
197
+ };
198
+ if (signal.aborted)
199
+ onAbort();
200
+ else
201
+ signal.addEventListener("abort", onAbort, { once: true });
202
+ }
203
+ });
204
+ const duration = Date.now() - startTime;
205
+ const assistantText = extractAssistantText(log);
206
+ const usage = extractUsage(log);
207
+ const model = extractModel(log);
208
+ const fullLog = log.map((e) => e.raw).join("\n");
209
+ const summary = [
210
+ `## Docker E2E Run Complete`,
211
+ ``,
212
+ `**Exit code:** ${exitCode ?? "killed"}`,
213
+ `**Duration:** ${(duration / 1000).toFixed(1)}s`,
214
+ model ? `**Model:** ${model}` : null,
215
+ usage.turns > 0
216
+ ? `**Usage:** ${usage.turns} turns, ↑${usage.input} ↓${usage.output} tokens, $${usage.cost.toFixed(4)}`
217
+ : null,
218
+ ``,
219
+ `### Output`,
220
+ assistantText || "(no assistant output)",
221
+ ``,
222
+ `### Full Log (${log.length} events)`,
223
+ "```",
224
+ fullLog,
225
+ "```",
226
+ ]
227
+ .filter((l) => l !== null)
228
+ .join("\n");
229
+ return {
230
+ content: [{ type: "text", text: summary }],
231
+ details: { exitCode, log, duration, model, usage },
232
+ };
233
+ },
234
+ };
235
+ //# sourceMappingURL=docker-e2e.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-e2e.js","sourceRoot":"","sources":["../src/docker-e2e.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=docker-e2e.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-e2e.test.d.ts","sourceRoot":"","sources":["../src/docker-e2e.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,176 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { parseJsonlLine, extractAssistantText, extractUsage, extractModel, buildDockerArgs, } from "./docker-e2e.js";
3
+ describe("parseJsonlLine", () => {
4
+ it("parses valid JSON event", () => {
5
+ const line = '{"type":"message_start","message":{"role":"assistant"}}';
6
+ const result = parseJsonlLine(line);
7
+ expect(result).not.toBeNull();
8
+ expect(result?.type).toBe("message_start");
9
+ expect(result?.raw).toBe(line);
10
+ });
11
+ it("returns raw type for non-JSON", () => {
12
+ const result = parseJsonlLine("some plain text output");
13
+ expect(result).not.toBeNull();
14
+ expect(result?.type).toBe("raw");
15
+ expect(result?.raw).toBe("some plain text output");
16
+ });
17
+ it("returns null for empty lines", () => {
18
+ expect(parseJsonlLine("")).toBeNull();
19
+ expect(parseJsonlLine(" ")).toBeNull();
20
+ });
21
+ it("defaults to unknown type when type field missing", () => {
22
+ const result = parseJsonlLine('{"data":"hello"}');
23
+ expect(result?.type).toBe("unknown");
24
+ });
25
+ });
26
+ describe("extractAssistantText", () => {
27
+ it("extracts text from message_end events", () => {
28
+ const log = [
29
+ {
30
+ type: "message_end",
31
+ timestamp: 0,
32
+ raw: JSON.stringify({
33
+ type: "message_end",
34
+ message: {
35
+ role: "assistant",
36
+ content: [{ type: "text", text: "Hello world" }],
37
+ },
38
+ }),
39
+ },
40
+ ];
41
+ expect(extractAssistantText(log)).toBe("Hello world");
42
+ });
43
+ it("concatenates multiple assistant messages", () => {
44
+ const makeEnd = (text) => ({
45
+ type: "message_end",
46
+ timestamp: 0,
47
+ raw: JSON.stringify({
48
+ type: "message_end",
49
+ message: {
50
+ role: "assistant",
51
+ content: [{ type: "text", text }],
52
+ },
53
+ }),
54
+ });
55
+ const log = [makeEnd("First"), makeEnd("Second")];
56
+ expect(extractAssistantText(log)).toBe("First\nSecond");
57
+ });
58
+ it("ignores non-assistant messages", () => {
59
+ const log = [
60
+ {
61
+ type: "message_end",
62
+ timestamp: 0,
63
+ raw: JSON.stringify({
64
+ type: "message_end",
65
+ message: {
66
+ role: "user",
67
+ content: [{ type: "text", text: "ignored" }],
68
+ },
69
+ }),
70
+ },
71
+ ];
72
+ expect(extractAssistantText(log)).toBe("");
73
+ });
74
+ it("ignores non-message_end events", () => {
75
+ const log = [{ type: "message_start", timestamp: 0, raw: "{}" }];
76
+ expect(extractAssistantText(log)).toBe("");
77
+ });
78
+ });
79
+ describe("extractUsage", () => {
80
+ it("accumulates usage from assistant messages", () => {
81
+ const log = [
82
+ {
83
+ type: "message_end",
84
+ timestamp: 0,
85
+ raw: JSON.stringify({
86
+ type: "message_end",
87
+ message: {
88
+ role: "assistant",
89
+ content: [],
90
+ usage: {
91
+ input: 100,
92
+ output: 50,
93
+ cacheRead: 10,
94
+ cacheWrite: 5,
95
+ cost: { total: 0.01 },
96
+ },
97
+ },
98
+ }),
99
+ },
100
+ {
101
+ type: "message_end",
102
+ timestamp: 0,
103
+ raw: JSON.stringify({
104
+ type: "message_end",
105
+ message: {
106
+ role: "assistant",
107
+ content: [],
108
+ usage: {
109
+ input: 200,
110
+ output: 100,
111
+ cacheRead: 20,
112
+ cacheWrite: 10,
113
+ cost: { total: 0.02 },
114
+ },
115
+ },
116
+ }),
117
+ },
118
+ ];
119
+ const usage = extractUsage(log);
120
+ expect(usage.turns).toBe(2);
121
+ expect(usage.input).toBe(300);
122
+ expect(usage.output).toBe(150);
123
+ expect(usage.cacheRead).toBe(30);
124
+ expect(usage.cacheWrite).toBe(15);
125
+ expect(usage.cost).toBeCloseTo(0.03);
126
+ });
127
+ it("returns zeroes for empty log", () => {
128
+ const usage = extractUsage([]);
129
+ expect(usage.turns).toBe(0);
130
+ expect(usage.input).toBe(0);
131
+ });
132
+ });
133
+ describe("extractModel", () => {
134
+ it("extracts model from message_end", () => {
135
+ const log = [
136
+ {
137
+ type: "message_end",
138
+ timestamp: 0,
139
+ raw: JSON.stringify({
140
+ type: "message_end",
141
+ message: { model: "claude-sonnet-4-5", role: "assistant", content: [] },
142
+ }),
143
+ },
144
+ ];
145
+ expect(extractModel(log)).toBe("claude-sonnet-4-5");
146
+ });
147
+ it("returns undefined for empty log", () => {
148
+ expect(extractModel([])).toBeUndefined();
149
+ });
150
+ });
151
+ describe("buildDockerArgs", () => {
152
+ it("builds basic args", () => {
153
+ const args = buildDockerArgs("my-image", {
154
+ task: "Fix the bug",
155
+ token: "ghp_abc123",
156
+ });
157
+ expect(args).toEqual([
158
+ "run",
159
+ "--rm",
160
+ "-e",
161
+ "GITHUB_TOKEN=ghp_abc123",
162
+ "my-image",
163
+ "Fix the bug",
164
+ ]);
165
+ });
166
+ it("includes model env var when specified", () => {
167
+ const args = buildDockerArgs("my-image", {
168
+ task: "Fix it",
169
+ token: "ghp_abc",
170
+ model: "claude-opus-4-5",
171
+ });
172
+ expect(args).toContain("-e");
173
+ expect(args).toContain("PI_MODEL=claude-opus-4-5");
174
+ });
175
+ });
176
+ //# sourceMappingURL=docker-e2e.test.js.map