@kb-labs/adapters 0.5.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 (276) hide show
  1. package/.cursorrules +32 -0
  2. package/.github/workflows/ci.yml +13 -0
  3. package/.github/workflows/deploy.yml +28 -0
  4. package/.github/workflows/docker-build.yml +25 -0
  5. package/.github/workflows/drift-check.yml +10 -0
  6. package/.github/workflows/profiles-validate.yml +16 -0
  7. package/.github/workflows/release.yml +8 -0
  8. package/.kb/devkit/agents/devkit-maintainer/context.globs +15 -0
  9. package/.kb/devkit/agents/devkit-maintainer/permissions.yml +17 -0
  10. package/.kb/devkit/agents/devkit-maintainer/prompt.md +28 -0
  11. package/.kb/devkit/agents/devkit-maintainer/runbook.md +31 -0
  12. package/.kb/devkit/agents/docs-crafter/prompt.md +24 -0
  13. package/.kb/devkit/agents/docs-crafter/runbook.md +18 -0
  14. package/.kb/devkit/agents/release-manager/context.globs +7 -0
  15. package/.kb/devkit/agents/release-manager/prompt.md +27 -0
  16. package/.kb/devkit/agents/release-manager/runbook.md +17 -0
  17. package/.kb/devkit/agents/test-generator/context.globs +7 -0
  18. package/.kb/devkit/agents/test-generator/prompt.md +27 -0
  19. package/.kb/devkit/agents/test-generator/runbook.md +18 -0
  20. package/CONTRIBUTING.md +90 -0
  21. package/IMPLEMENTATION_COMPLETE.md +416 -0
  22. package/LICENSE +186 -0
  23. package/README-TEMPLATE.md +179 -0
  24. package/README.md +306 -0
  25. package/docs/DOCUMENTATION.md +74 -0
  26. package/docs/adr/0000-template.md +49 -0
  27. package/docs/adr/0001-architecture-and-repository-layout.md +33 -0
  28. package/docs/adr/0002-plugins-and-extensibility.md +46 -0
  29. package/docs/adr/0003-package-and-module-boundaries.md +37 -0
  30. package/docs/adr/0004-versioning-and-release-policy.md +38 -0
  31. package/docs/adr/0005-use-devkit-for-shared-tooling.md +48 -0
  32. package/docs/adr/0006-adopt-devkit-sync.md +47 -0
  33. package/docs/adr/0007-drift-kit-check.md +72 -0
  34. package/docs/adr/0008-devkit-sync-wrapper-strategy.md +67 -0
  35. package/docs/naming-convention.md +272 -0
  36. package/eslint.config.js +27 -0
  37. package/kb-labs.config.json +5 -0
  38. package/package.json +84 -0
  39. package/package.json.bin +25 -0
  40. package/package.json.lib +30 -0
  41. package/packages/adapters-analytics-duckdb/package.json +54 -0
  42. package/packages/adapters-analytics-duckdb/scripts/migrate-from-jsonl.mjs +253 -0
  43. package/packages/adapters-analytics-duckdb/src/index.ts +380 -0
  44. package/packages/adapters-analytics-duckdb/src/manifest.ts +36 -0
  45. package/packages/adapters-analytics-duckdb/src/schema.ts +161 -0
  46. package/packages/adapters-analytics-duckdb/tsconfig.build.json +15 -0
  47. package/packages/adapters-analytics-duckdb/tsconfig.json +9 -0
  48. package/packages/adapters-analytics-duckdb/tsup.config.ts +9 -0
  49. package/packages/adapters-analytics-file/README.md +32 -0
  50. package/packages/adapters-analytics-file/eslint.config.js +27 -0
  51. package/packages/adapters-analytics-file/package.json +50 -0
  52. package/packages/adapters-analytics-file/src/__tests__/daily-stats.spec.ts +287 -0
  53. package/packages/adapters-analytics-file/src/__tests__/scoped-analytics.test.ts +233 -0
  54. package/packages/adapters-analytics-file/src/index.test.ts +214 -0
  55. package/packages/adapters-analytics-file/src/index.ts +830 -0
  56. package/packages/adapters-analytics-file/src/manifest.ts +45 -0
  57. package/packages/adapters-analytics-file/tsconfig.build.json +15 -0
  58. package/packages/adapters-analytics-file/tsconfig.json +9 -0
  59. package/packages/adapters-analytics-file/tsup.config.ts +9 -0
  60. package/packages/adapters-analytics-sqlite/package.json +55 -0
  61. package/packages/adapters-analytics-sqlite/scripts/migrate-from-jsonl.mjs +194 -0
  62. package/packages/adapters-analytics-sqlite/src/index.ts +460 -0
  63. package/packages/adapters-analytics-sqlite/src/manifest.ts +41 -0
  64. package/packages/adapters-analytics-sqlite/tsconfig.build.json +15 -0
  65. package/packages/adapters-analytics-sqlite/tsconfig.json +9 -0
  66. package/packages/adapters-analytics-sqlite/tsup.config.ts +9 -0
  67. package/packages/adapters-environment-docker/README.md +28 -0
  68. package/packages/adapters-environment-docker/eslint.config.js +5 -0
  69. package/packages/adapters-environment-docker/package.json +49 -0
  70. package/packages/adapters-environment-docker/src/index.test.ts +138 -0
  71. package/packages/adapters-environment-docker/src/index.ts +439 -0
  72. package/packages/adapters-environment-docker/src/manifest.ts +65 -0
  73. package/packages/adapters-environment-docker/tsconfig.build.json +15 -0
  74. package/packages/adapters-environment-docker/tsconfig.json +16 -0
  75. package/packages/adapters-environment-docker/tsup.config.ts +9 -0
  76. package/packages/adapters-eventbus-cache/README.md +242 -0
  77. package/packages/adapters-eventbus-cache/eslint.config.js +27 -0
  78. package/packages/adapters-eventbus-cache/package.json +46 -0
  79. package/packages/adapters-eventbus-cache/src/index.test.ts +235 -0
  80. package/packages/adapters-eventbus-cache/src/index.ts +215 -0
  81. package/packages/adapters-eventbus-cache/src/manifest.ts +50 -0
  82. package/packages/adapters-eventbus-cache/src/types.ts +58 -0
  83. package/packages/adapters-eventbus-cache/tsconfig.build.json +15 -0
  84. package/packages/adapters-eventbus-cache/tsconfig.json +9 -0
  85. package/packages/adapters-eventbus-cache/tsup.config.ts +9 -0
  86. package/packages/adapters-fs/README.md +171 -0
  87. package/packages/adapters-fs/allowed.txt +1 -0
  88. package/packages/adapters-fs/conflict.txt +1 -0
  89. package/packages/adapters-fs/dest.txt +1 -0
  90. package/packages/adapters-fs/eslint.config.js +27 -0
  91. package/packages/adapters-fs/exists.txt +1 -0
  92. package/packages/adapters-fs/not-allowed.txt +1 -0
  93. package/packages/adapters-fs/other.txt +1 -0
  94. package/packages/adapters-fs/package.json +55 -0
  95. package/packages/adapters-fs/public/file1.txt +1 -0
  96. package/packages/adapters-fs/public/file2.txt +1 -0
  97. package/packages/adapters-fs/secret.txt +1 -0
  98. package/packages/adapters-fs/secrets/key.txt +1 -0
  99. package/packages/adapters-fs/src/index.test.ts +243 -0
  100. package/packages/adapters-fs/src/index.ts +258 -0
  101. package/packages/adapters-fs/src/manifest.ts +35 -0
  102. package/packages/adapters-fs/src/secure-storage.test.ts +380 -0
  103. package/packages/adapters-fs/src/secure-storage.ts +268 -0
  104. package/packages/adapters-fs/test.json +1 -0
  105. package/packages/adapters-fs/test.txt +1 -0
  106. package/packages/adapters-fs/test.xyz +1 -0
  107. package/packages/adapters-fs/test1.txt +1 -0
  108. package/packages/adapters-fs/test2.txt +1 -0
  109. package/packages/adapters-fs/tsconfig.build.json +15 -0
  110. package/packages/adapters-fs/tsconfig.json +9 -0
  111. package/packages/adapters-fs/tsup.config.ts +8 -0
  112. package/packages/adapters-fs/vitest.config.ts +19 -0
  113. package/packages/adapters-log-ringbuffer/README.md +228 -0
  114. package/packages/adapters-log-ringbuffer/eslint.config.js +27 -0
  115. package/packages/adapters-log-ringbuffer/package.json +47 -0
  116. package/packages/adapters-log-ringbuffer/src/__tests__/ring-buffer.test.ts +450 -0
  117. package/packages/adapters-log-ringbuffer/src/index.ts +212 -0
  118. package/packages/adapters-log-ringbuffer/src/manifest.ts +30 -0
  119. package/packages/adapters-log-ringbuffer/tsconfig.build.json +15 -0
  120. package/packages/adapters-log-ringbuffer/tsconfig.json +9 -0
  121. package/packages/adapters-log-ringbuffer/tsup.config.ts +9 -0
  122. package/packages/adapters-log-ringbuffer/vitest.config.ts +14 -0
  123. package/packages/adapters-log-sqlite/README.md +396 -0
  124. package/packages/adapters-log-sqlite/eslint.config.js +27 -0
  125. package/packages/adapters-log-sqlite/package.json +49 -0
  126. package/packages/adapters-log-sqlite/src/__tests__/log-persistence.test.ts +718 -0
  127. package/packages/adapters-log-sqlite/src/index.ts +1068 -0
  128. package/packages/adapters-log-sqlite/src/manifest.ts +36 -0
  129. package/packages/adapters-log-sqlite/src/schema.sql +46 -0
  130. package/packages/adapters-log-sqlite/tsconfig.build.json +15 -0
  131. package/packages/adapters-log-sqlite/tsconfig.json +9 -0
  132. package/packages/adapters-log-sqlite/tsup.config.ts +9 -0
  133. package/packages/adapters-log-sqlite/vitest.config.ts +15 -0
  134. package/packages/adapters-mongodb/README.md +147 -0
  135. package/packages/adapters-mongodb/eslint.config.js +27 -0
  136. package/packages/adapters-mongodb/package.json +53 -0
  137. package/packages/adapters-mongodb/src/index.ts +428 -0
  138. package/packages/adapters-mongodb/src/manifest.ts +45 -0
  139. package/packages/adapters-mongodb/src/secure-document.ts +231 -0
  140. package/packages/adapters-mongodb/tsconfig.build.json +15 -0
  141. package/packages/adapters-mongodb/tsconfig.json +9 -0
  142. package/packages/adapters-mongodb/tsup.config.ts +8 -0
  143. package/packages/adapters-openai/README.md +151 -0
  144. package/packages/adapters-openai/embeddings.ts +37 -0
  145. package/packages/adapters-openai/eslint.config.js +26 -0
  146. package/packages/adapters-openai/index.ts +22 -0
  147. package/packages/adapters-openai/package.json +57 -0
  148. package/packages/adapters-openai/src/embeddings-manifest.ts +45 -0
  149. package/packages/adapters-openai/src/embeddings.ts +104 -0
  150. package/packages/adapters-openai/src/index.ts +13 -0
  151. package/packages/adapters-openai/src/llm.ts +304 -0
  152. package/packages/adapters-openai/src/manifest.ts +47 -0
  153. package/packages/adapters-openai/tsconfig.build.json +15 -0
  154. package/packages/adapters-openai/tsconfig.json +9 -0
  155. package/packages/adapters-openai/tsup.config.ts +8 -0
  156. package/packages/adapters-pino/README.md +152 -0
  157. package/packages/adapters-pino/eslint.config.js +27 -0
  158. package/packages/adapters-pino/package.json +49 -0
  159. package/packages/adapters-pino/src/index.test.ts +44 -0
  160. package/packages/adapters-pino/src/index.ts +322 -0
  161. package/packages/adapters-pino/src/log-ring-buffer.ts +142 -0
  162. package/packages/adapters-pino/src/manifest.ts +49 -0
  163. package/packages/adapters-pino/tsconfig.build.json +15 -0
  164. package/packages/adapters-pino/tsconfig.json +9 -0
  165. package/packages/adapters-pino/tsup.config.ts +9 -0
  166. package/packages/adapters-pino-http/README.md +141 -0
  167. package/packages/adapters-pino-http/eslint.config.js +27 -0
  168. package/packages/adapters-pino-http/package.json +46 -0
  169. package/packages/adapters-pino-http/src/index.ts +229 -0
  170. package/packages/adapters-pino-http/tsconfig.build.json +15 -0
  171. package/packages/adapters-pino-http/tsconfig.json +9 -0
  172. package/packages/adapters-pino-http/tsup.config.ts +9 -0
  173. package/packages/adapters-qdrant/README.md +166 -0
  174. package/packages/adapters-qdrant/eslint.config.js +27 -0
  175. package/packages/adapters-qdrant/package.json +49 -0
  176. package/packages/adapters-qdrant/src/index.ts +490 -0
  177. package/packages/adapters-qdrant/src/manifest.ts +54 -0
  178. package/packages/adapters-qdrant/src/retry.ts +204 -0
  179. package/packages/adapters-qdrant/tsconfig.build.json +15 -0
  180. package/packages/adapters-qdrant/tsconfig.json +9 -0
  181. package/packages/adapters-qdrant/tsup.config.ts +9 -0
  182. package/packages/adapters-redis/README.md +159 -0
  183. package/packages/adapters-redis/eslint.config.js +27 -0
  184. package/packages/adapters-redis/package.json +49 -0
  185. package/packages/adapters-redis/src/index.ts +164 -0
  186. package/packages/adapters-redis/src/manifest.ts +49 -0
  187. package/packages/adapters-redis/tsconfig.build.json +15 -0
  188. package/packages/adapters-redis/tsconfig.json +9 -0
  189. package/packages/adapters-redis/tsup.config.ts +9 -0
  190. package/packages/adapters-snapshot-localfs/README.md +10 -0
  191. package/packages/adapters-snapshot-localfs/eslint.config.js +2 -0
  192. package/packages/adapters-snapshot-localfs/package.json +46 -0
  193. package/packages/adapters-snapshot-localfs/src/index.test.ts +40 -0
  194. package/packages/adapters-snapshot-localfs/src/index.ts +292 -0
  195. package/packages/adapters-snapshot-localfs/src/manifest.ts +32 -0
  196. package/packages/adapters-snapshot-localfs/tsconfig.build.json +15 -0
  197. package/packages/adapters-snapshot-localfs/tsconfig.json +16 -0
  198. package/packages/adapters-snapshot-localfs/tsup.config.ts +11 -0
  199. package/packages/adapters-sqlite/README.md +163 -0
  200. package/packages/adapters-sqlite/eslint.config.js +27 -0
  201. package/packages/adapters-sqlite/package.json +54 -0
  202. package/packages/adapters-sqlite/src/index.test.ts +245 -0
  203. package/packages/adapters-sqlite/src/index.ts +382 -0
  204. package/packages/adapters-sqlite/src/manifest.ts +47 -0
  205. package/packages/adapters-sqlite/src/secure-sql.test.ts +290 -0
  206. package/packages/adapters-sqlite/src/secure-sql.ts +281 -0
  207. package/packages/adapters-sqlite/tsconfig.build.json +15 -0
  208. package/packages/adapters-sqlite/tsconfig.json +9 -0
  209. package/packages/adapters-sqlite/tsup.config.ts +8 -0
  210. package/packages/adapters-sqlite/vitest.config.ts +19 -0
  211. package/packages/adapters-transport/README.md +170 -0
  212. package/packages/adapters-transport/eslint.config.js +27 -0
  213. package/packages/adapters-transport/package.json +49 -0
  214. package/packages/adapters-transport/src/__tests__/unix-socket-server.test.ts +550 -0
  215. package/packages/adapters-transport/src/index.ts +101 -0
  216. package/packages/adapters-transport/src/ipc-transport.ts +228 -0
  217. package/packages/adapters-transport/src/transport.ts +224 -0
  218. package/packages/adapters-transport/src/types.ts +92 -0
  219. package/packages/adapters-transport/src/unix-socket-server.ts +193 -0
  220. package/packages/adapters-transport/src/unix-socket-transport.ts +280 -0
  221. package/packages/adapters-transport/tsconfig.build.json +15 -0
  222. package/packages/adapters-transport/tsconfig.json +9 -0
  223. package/packages/adapters-transport/tsup.config.ts +9 -0
  224. package/packages/adapters-vibeproxy/README.md +159 -0
  225. package/packages/adapters-vibeproxy/eslint.config.js +27 -0
  226. package/packages/adapters-vibeproxy/package.json +51 -0
  227. package/packages/adapters-vibeproxy/src/index.ts +13 -0
  228. package/packages/adapters-vibeproxy/src/llm.ts +437 -0
  229. package/packages/adapters-vibeproxy/src/manifest.ts +51 -0
  230. package/packages/adapters-vibeproxy/tsconfig.build.json +15 -0
  231. package/packages/adapters-vibeproxy/tsconfig.json +9 -0
  232. package/packages/adapters-vibeproxy/tsup.config.ts +8 -0
  233. package/packages/adapters-workspace-agent/package.json +46 -0
  234. package/packages/adapters-workspace-agent/src/__tests__/adapter.test.ts +212 -0
  235. package/packages/adapters-workspace-agent/src/index.ts +220 -0
  236. package/packages/adapters-workspace-agent/src/manifest.ts +36 -0
  237. package/packages/adapters-workspace-agent/tsconfig.build.json +15 -0
  238. package/packages/adapters-workspace-agent/tsconfig.json +16 -0
  239. package/packages/adapters-workspace-agent/tsup.config.ts +11 -0
  240. package/packages/adapters-workspace-localfs/README.md +9 -0
  241. package/packages/adapters-workspace-localfs/eslint.config.js +2 -0
  242. package/packages/adapters-workspace-localfs/package.json +46 -0
  243. package/packages/adapters-workspace-localfs/src/index.test.ts +27 -0
  244. package/packages/adapters-workspace-localfs/src/index.ts +172 -0
  245. package/packages/adapters-workspace-localfs/src/manifest.ts +32 -0
  246. package/packages/adapters-workspace-localfs/tsconfig.build.json +15 -0
  247. package/packages/adapters-workspace-localfs/tsconfig.json +16 -0
  248. package/packages/adapters-workspace-localfs/tsup.config.ts +11 -0
  249. package/packages/adapters-workspace-worktree/README.md +9 -0
  250. package/packages/adapters-workspace-worktree/eslint.config.js +2 -0
  251. package/packages/adapters-workspace-worktree/package.json +46 -0
  252. package/packages/adapters-workspace-worktree/src/index.test.ts +38 -0
  253. package/packages/adapters-workspace-worktree/src/index.ts +245 -0
  254. package/packages/adapters-workspace-worktree/src/manifest.ts +38 -0
  255. package/packages/adapters-workspace-worktree/tsconfig.build.json +15 -0
  256. package/packages/adapters-workspace-worktree/tsconfig.json +16 -0
  257. package/packages/adapters-workspace-worktree/tsup.config.ts +11 -0
  258. package/pnpm-workspace.yaml +2800 -0
  259. package/prettierrc.json +1 -0
  260. package/scripts/devkit-sync.mjs +37 -0
  261. package/scripts/hooks/post-push +9 -0
  262. package/scripts/hooks/pre-commit +9 -0
  263. package/scripts/hooks/pre-push +9 -0
  264. package/test-integration.ts +242 -0
  265. package/test.txt +1 -0
  266. package/tsconfig.base.json +6 -0
  267. package/tsconfig.build.json +15 -0
  268. package/tsconfig.json +9 -0
  269. package/tsconfig.paths.json +26 -0
  270. package/tsconfig.tools.json +17 -0
  271. package/tsup.config.bin.ts +34 -0
  272. package/tsup.config.cli.ts +41 -0
  273. package/tsup.config.dual.ts +46 -0
  274. package/tsup.config.ts +36 -0
  275. package/tsup.external.json +103 -0
  276. package/vitest.config.ts +2 -0
@@ -0,0 +1,550 @@
1
+ /**
2
+ * @module @kb-labs/adapters-transport/__tests__/unix-socket-server
3
+ *
4
+ * Tests for UnixSocketServer (parent process side of IPC).
5
+ *
6
+ * Tests:
7
+ * - Server start/stop lifecycle
8
+ * - Client connection handling
9
+ * - Adapter call execution
10
+ * - Error handling and serialization
11
+ * - Multiple concurrent clients
12
+ * - Socket cleanup
13
+ */
14
+
15
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
16
+ import * as net from "net";
17
+ import * as fs from "fs";
18
+ import * as os from "os";
19
+ import * as path from "path";
20
+ import { UnixSocketServer } from "../unix-socket-server.js";
21
+ import type { AdapterCall, AdapterResponse } from "../types.js";
22
+
23
+ describe("UnixSocketServer", () => {
24
+ let socketPath: string;
25
+ let server: UnixSocketServer;
26
+
27
+ beforeEach(() => {
28
+ // Use unique socket path for each test
29
+ const testId = Math.random().toString(36).slice(2, 9);
30
+ socketPath = path.join(os.tmpdir(), `kb-test-${testId}.sock`);
31
+ });
32
+
33
+ afterEach(async () => {
34
+ // Cleanup server
35
+ if (server) {
36
+ await server.close();
37
+ }
38
+
39
+ // Cleanup socket file if exists
40
+ if (fs.existsSync(socketPath)) {
41
+ fs.unlinkSync(socketPath);
42
+ }
43
+ });
44
+
45
+ describe("Server Lifecycle", () => {
46
+ it("should start server and create socket file", async () => {
47
+ server = new UnixSocketServer({ socketPath });
48
+
49
+ server.onCall(async (call) => ({
50
+ type: "adapter:response",
51
+ requestId: call.requestId,
52
+ result: null,
53
+ }));
54
+
55
+ await server.start();
56
+
57
+ // Socket file should exist
58
+ expect(fs.existsSync(socketPath)).toBe(true);
59
+
60
+ // Should be readable/writable
61
+ const stats = fs.statSync(socketPath);
62
+ expect(stats.isSocket()).toBe(true);
63
+ });
64
+
65
+ it("should remove existing socket file on start", async () => {
66
+ // Create dummy socket file
67
+ fs.writeFileSync(socketPath, "");
68
+
69
+ server = new UnixSocketServer({ socketPath });
70
+
71
+ server.onCall(async (call) => ({
72
+ type: "adapter:response",
73
+ requestId: call.requestId,
74
+ result: null,
75
+ }));
76
+
77
+ await server.start();
78
+
79
+ // Should still work (old file removed)
80
+ expect(fs.existsSync(socketPath)).toBe(true);
81
+
82
+ const stats = fs.statSync(socketPath);
83
+ expect(stats.isSocket()).toBe(true);
84
+ });
85
+
86
+ it("should clean up socket file on close", async () => {
87
+ server = new UnixSocketServer({ socketPath });
88
+
89
+ server.onCall(async (call) => ({
90
+ type: "adapter:response",
91
+ requestId: call.requestId,
92
+ result: null,
93
+ }));
94
+
95
+ await server.start();
96
+ expect(fs.existsSync(socketPath)).toBe(true);
97
+
98
+ await server.close();
99
+
100
+ // Socket file should be removed
101
+ expect(fs.existsSync(socketPath)).toBe(false);
102
+ });
103
+
104
+ it("should use default socket path if not specified", async () => {
105
+ server = new UnixSocketServer(); // No config
106
+
107
+ server.onCall(async (call) => ({
108
+ type: "adapter:response",
109
+ requestId: call.requestId,
110
+ result: null,
111
+ }));
112
+
113
+ await server.start();
114
+
115
+ // Default path is /tmp/kb-ipc.sock
116
+ const defaultPath = "/tmp/kb-ipc.sock";
117
+ expect(fs.existsSync(defaultPath)).toBe(true);
118
+
119
+ await server.close();
120
+ expect(fs.existsSync(defaultPath)).toBe(false);
121
+ });
122
+ });
123
+
124
+ describe("Client Connections", () => {
125
+ it("should accept client connections", async () => {
126
+ server = new UnixSocketServer({ socketPath });
127
+
128
+ server.onCall(async (call) => ({
129
+ type: "adapter:response",
130
+ requestId: call.requestId,
131
+ result: { success: true },
132
+ }));
133
+
134
+ await server.start();
135
+
136
+ // Connect as client
137
+ const client = net.connect(socketPath);
138
+
139
+ await new Promise<void>((resolve, reject) => {
140
+ client.on("connect", () => resolve());
141
+ client.on("error", reject);
142
+ });
143
+
144
+ client.destroy();
145
+ });
146
+
147
+ it("should handle multiple concurrent clients", async () => {
148
+ server = new UnixSocketServer({ socketPath });
149
+
150
+ let callCount = 0;
151
+
152
+ server.onCall(async (call) => {
153
+ callCount++;
154
+ return {
155
+ type: "adapter:response",
156
+ requestId: call.requestId,
157
+ result: { callNumber: callCount },
158
+ };
159
+ });
160
+
161
+ await server.start();
162
+
163
+ // Connect 3 clients concurrently
164
+ const clients = await Promise.all([
165
+ connectClient(socketPath),
166
+ connectClient(socketPath),
167
+ connectClient(socketPath),
168
+ ]);
169
+
170
+ // Each should be able to send messages
171
+ const results = await Promise.all(
172
+ clients.map((client, i) =>
173
+ sendAdapterCall(client, {
174
+ version: 2,
175
+ type: "adapter:call",
176
+ requestId: `req-${i}`,
177
+ adapter: "cache",
178
+ method: "get",
179
+ args: [`key-${i}`],
180
+ }),
181
+ ),
182
+ );
183
+
184
+ // All should get responses
185
+ expect(results).toHaveLength(3);
186
+ expect(callCount).toBe(3);
187
+
188
+ // Cleanup clients
189
+ clients.forEach((c) => c.destroy());
190
+ });
191
+
192
+ it("should clean up client on disconnect", async () => {
193
+ server = new UnixSocketServer({ socketPath });
194
+
195
+ server.onCall(async (call) => ({
196
+ type: "adapter:response",
197
+ requestId: call.requestId,
198
+ result: null,
199
+ }));
200
+
201
+ await server.start();
202
+
203
+ const client = await connectClient(socketPath);
204
+
205
+ // Disconnect client
206
+ client.destroy();
207
+
208
+ // Wait for cleanup
209
+ await new Promise((resolve) => {
210
+ setTimeout(resolve, 100);
211
+ });
212
+
213
+ // Server should still be running
214
+ const client2 = await connectClient(socketPath);
215
+ client2.destroy();
216
+ });
217
+ });
218
+
219
+ describe("Adapter Call Handling", () => {
220
+ it("should execute adapter call and return result", async () => {
221
+ server = new UnixSocketServer({ socketPath });
222
+
223
+ server.onCall(async (call) => {
224
+ // Simulate adapter execution
225
+ if (call.adapter === "cache" && call.method === "get") {
226
+ const key = call.args[0] as string;
227
+ return {
228
+ type: "adapter:response",
229
+ requestId: call.requestId,
230
+ result: { key, value: "cached-value" },
231
+ };
232
+ }
233
+
234
+ return {
235
+ type: "adapter:response",
236
+ requestId: call.requestId,
237
+ result: null,
238
+ };
239
+ });
240
+
241
+ await server.start();
242
+
243
+ const client = await connectClient(socketPath);
244
+
245
+ const response = await sendAdapterCall(client, {
246
+ version: 2,
247
+ type: "adapter:call",
248
+ requestId: "test-123",
249
+ adapter: "cache",
250
+ method: "get",
251
+ args: ["my-key"],
252
+ });
253
+
254
+ expect(response.type).toBe("adapter:response");
255
+ expect(response.requestId).toBe("test-123");
256
+ expect(response.result).toEqual({ key: "my-key", value: "cached-value" });
257
+
258
+ client.destroy();
259
+ });
260
+
261
+ it("should handle errors in adapter call handler", async () => {
262
+ server = new UnixSocketServer({ socketPath });
263
+
264
+ server.onCall(async (_call) => {
265
+ // Simulate adapter error
266
+ throw new Error("Adapter failed");
267
+ });
268
+
269
+ await server.start();
270
+
271
+ const client = await connectClient(socketPath);
272
+
273
+ const response = await sendAdapterCall(client, {
274
+ version: 2,
275
+ type: "adapter:call",
276
+ requestId: "error-test",
277
+ adapter: "llm",
278
+ method: "chat",
279
+ args: [{ messages: [] }],
280
+ });
281
+
282
+ expect(response.type).toBe("adapter:response");
283
+ expect(response.requestId).toBe("error-test");
284
+ expect(response.error).toBeDefined();
285
+ expect(response.error?.__type).toBe("Error");
286
+ expect(response.error?.message).toBe("Adapter failed");
287
+
288
+ client.destroy();
289
+ });
290
+
291
+ it("should handle non-Error exceptions", async () => {
292
+ server = new UnixSocketServer({ socketPath });
293
+
294
+ server.onCall(async (_call) => {
295
+ throw new Error("String error");
296
+ });
297
+
298
+ await server.start();
299
+
300
+ const client = await connectClient(socketPath);
301
+
302
+ const response = await sendAdapterCall(client, {
303
+ version: 2,
304
+ type: "adapter:call",
305
+ requestId: "string-error",
306
+ adapter: "cache",
307
+ method: "get",
308
+ args: ["key"],
309
+ });
310
+
311
+ expect(response.error).toBeDefined();
312
+ expect(response.error?.message).toBe("String error");
313
+
314
+ client.destroy();
315
+ });
316
+
317
+ it("should preserve error stack traces", async () => {
318
+ server = new UnixSocketServer({ socketPath });
319
+
320
+ server.onCall(async (_call) => {
321
+ const error = new Error("Test error");
322
+ error.stack = "Error: Test error\n at CustomLocation";
323
+ throw error;
324
+ });
325
+
326
+ await server.start();
327
+
328
+ const client = await connectClient(socketPath);
329
+
330
+ const response = await sendAdapterCall(client, {
331
+ version: 2,
332
+ type: "adapter:call",
333
+ requestId: "stack-test",
334
+ adapter: "storage",
335
+ method: "read",
336
+ args: ["/path"],
337
+ });
338
+
339
+ expect(response.error?.stack).toContain("CustomLocation");
340
+
341
+ client.destroy();
342
+ });
343
+ });
344
+
345
+ describe("Message Protocol", () => {
346
+ it("should parse newline-delimited JSON messages", async () => {
347
+ server = new UnixSocketServer({ socketPath });
348
+
349
+ const receivedCalls: AdapterCall[] = [];
350
+
351
+ server.onCall(async (call) => {
352
+ receivedCalls.push(call);
353
+ return {
354
+ type: "adapter:response",
355
+ requestId: call.requestId,
356
+ result: null,
357
+ };
358
+ });
359
+
360
+ await server.start();
361
+
362
+ const client = await connectClient(socketPath);
363
+
364
+ // Send 3 messages in one write (newline-delimited)
365
+ const msg1: AdapterCall = {
366
+ version: 2,
367
+ type: "adapter:call",
368
+ requestId: "msg1",
369
+ adapter: "cache",
370
+ method: "get",
371
+ args: ["key1"],
372
+ };
373
+ const msg2: AdapterCall = {
374
+ version: 2,
375
+ type: "adapter:call",
376
+ requestId: "msg2",
377
+ adapter: "cache",
378
+ method: "get",
379
+ args: ["key2"],
380
+ };
381
+ const msg3: AdapterCall = {
382
+ version: 2,
383
+ type: "adapter:call",
384
+ requestId: "msg3",
385
+ adapter: "cache",
386
+ method: "get",
387
+ args: ["key3"],
388
+ };
389
+
390
+ client.write(JSON.stringify(msg1) + "\n");
391
+ client.write(JSON.stringify(msg2) + "\n");
392
+ client.write(JSON.stringify(msg3) + "\n");
393
+
394
+ // Wait for processing
395
+ await new Promise((resolve) => {
396
+ setTimeout(resolve, 100);
397
+ });
398
+
399
+ expect(receivedCalls).toHaveLength(3);
400
+ expect(receivedCalls[0]!.requestId).toBe("msg1");
401
+ expect(receivedCalls[1]!.requestId).toBe("msg2");
402
+ expect(receivedCalls[2]!.requestId).toBe("msg3");
403
+
404
+ client.destroy();
405
+ });
406
+
407
+ it("should ignore empty lines", async () => {
408
+ server = new UnixSocketServer({ socketPath });
409
+
410
+ const receivedCalls: AdapterCall[] = [];
411
+
412
+ server.onCall(async (call) => {
413
+ receivedCalls.push(call);
414
+ return {
415
+ type: "adapter:response",
416
+ requestId: call.requestId,
417
+ result: null,
418
+ };
419
+ });
420
+
421
+ await server.start();
422
+
423
+ const client = await connectClient(socketPath);
424
+
425
+ // Send with empty lines
426
+ client.write("\n\n");
427
+ client.write(
428
+ JSON.stringify({
429
+ version: 2,
430
+ type: "adapter:call",
431
+ requestId: "valid",
432
+ adapter: "cache",
433
+ method: "get",
434
+ args: [],
435
+ }) + "\n",
436
+ );
437
+ client.write("\n");
438
+
439
+ await new Promise((resolve) => {
440
+ setTimeout(resolve, 100);
441
+ });
442
+
443
+ expect(receivedCalls).toHaveLength(1);
444
+ expect(receivedCalls[0]!.requestId).toBe("valid");
445
+
446
+ client.destroy();
447
+ });
448
+
449
+ it("should handle malformed JSON gracefully", async () => {
450
+ server = new UnixSocketServer({ socketPath });
451
+
452
+ const receivedCalls: AdapterCall[] = [];
453
+
454
+ server.onCall(async (call) => {
455
+ receivedCalls.push(call);
456
+ return {
457
+ type: "adapter:response",
458
+ requestId: call.requestId,
459
+ result: null,
460
+ };
461
+ });
462
+
463
+ await server.start();
464
+
465
+ const client = await connectClient(socketPath);
466
+
467
+ // Send malformed JSON
468
+ client.write("{ invalid json }\n");
469
+
470
+ // Send valid message after
471
+ client.write(
472
+ JSON.stringify({
473
+ version: 2,
474
+ type: "adapter:call",
475
+ requestId: "after-error",
476
+ adapter: "cache",
477
+ method: "get",
478
+ args: [],
479
+ }) + "\n",
480
+ );
481
+
482
+ await new Promise((resolve) => {
483
+ setTimeout(resolve, 100);
484
+ });
485
+
486
+ // Should process valid message despite earlier error
487
+ expect(receivedCalls).toHaveLength(1);
488
+ expect(receivedCalls[0]!.requestId).toBe("after-error");
489
+
490
+ client.destroy();
491
+ });
492
+ });
493
+ });
494
+
495
+ /**
496
+ * Helper: Connect to Unix socket server
497
+ */
498
+ async function connectClient(socketPath: string): Promise<net.Socket> {
499
+ const client = net.connect(socketPath);
500
+
501
+ return new Promise<net.Socket>((resolve, reject) => {
502
+ client.on("connect", () => resolve(client));
503
+ client.on("error", reject);
504
+ });
505
+ }
506
+
507
+ /**
508
+ * Helper: Send adapter call and wait for response
509
+ */
510
+ async function sendAdapterCall(
511
+ client: net.Socket,
512
+ call: AdapterCall,
513
+ ): Promise<AdapterResponse> {
514
+ return new Promise<AdapterResponse>((resolve, reject) => {
515
+ let buffer = "";
516
+
517
+ const onData = (data: Buffer) => {
518
+ buffer += data.toString("utf8");
519
+
520
+ const newlineIndex = buffer.indexOf("\n");
521
+ if (newlineIndex !== -1) {
522
+ const line = buffer.slice(0, newlineIndex);
523
+ try {
524
+ const response = JSON.parse(line) as AdapterResponse;
525
+ client.off("data", onData);
526
+ resolve(response);
527
+ } catch (error) {
528
+ reject(error);
529
+ }
530
+ }
531
+ };
532
+
533
+ client.on("data", onData);
534
+
535
+ // Send call
536
+ const message = JSON.stringify(call) + "\n";
537
+ client.write(message, "utf8", (error) => {
538
+ if (error) {
539
+ client.off("data", onData);
540
+ reject(error);
541
+ }
542
+ });
543
+
544
+ // Timeout after 5s
545
+ setTimeout(() => {
546
+ client.off("data", onData);
547
+ reject(new Error("Response timeout"));
548
+ }, 5000);
549
+ });
550
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @module @kb-labs/adapters-transport
3
+ * Transport adapters for inter-process communication.
4
+ *
5
+ * Provides pluggable transport layer for adapter calls between
6
+ * parent and child processes. Supports IPC and Unix Sockets.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createAdapter } from '@kb-labs/adapters-transport';
11
+ *
12
+ * // Auto-select best transport (Unix Socket with IPC fallback)
13
+ * const transport = createAdapter({ type: 'auto' });
14
+ *
15
+ * // Or explicitly choose
16
+ * const ipcTransport = createAdapter({ type: 'ipc' });
17
+ * const socketTransport = createAdapter({ type: 'unix-socket', socketPath: '/tmp/kb.sock' });
18
+ * ```
19
+ */
20
+
21
+ export * from "./transport.js";
22
+ export * from "./ipc-transport.js";
23
+ export * from "./unix-socket-transport.js";
24
+ export * from "./unix-socket-server.js";
25
+ export * from "./types.js";
26
+
27
+ import type { ITransport } from "./transport.js";
28
+ import { IPCTransport, type TransportConfig } from "./ipc-transport.js";
29
+ import {
30
+ UnixSocketTransport,
31
+ type UnixSocketConfig,
32
+ } from "./unix-socket-transport.js";
33
+
34
+ /**
35
+ * Transport adapter configuration.
36
+ */
37
+ export interface TransportAdapterConfig {
38
+ /** Transport type */
39
+ type: "ipc" | "unix-socket" | "auto";
40
+ /** Socket path for Unix socket transport */
41
+ socketPath?: string;
42
+ /** Timeout for adapter calls */
43
+ timeout?: number;
44
+ /** Auto-reconnect on disconnect (Unix socket only) */
45
+ autoReconnect?: boolean;
46
+ }
47
+
48
+ /**
49
+ * Create transport adapter based on configuration.
50
+ *
51
+ * Auto mode selects Unix Socket if available, falls back to IPC.
52
+ *
53
+ * @param config - Transport configuration
54
+ * @returns Transport adapter instance
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // Auto-select best transport
59
+ * const transport = createAdapter({ type: 'auto' });
60
+ *
61
+ * // Force IPC (legacy compatibility)
62
+ * const ipcTransport = createAdapter({ type: 'ipc' });
63
+ *
64
+ * // Force Unix Socket (max performance)
65
+ * const socketTransport = createAdapter({
66
+ * type: 'unix-socket',
67
+ * socketPath: '/tmp/kb-ipc.sock',
68
+ * });
69
+ * ```
70
+ */
71
+ export function createAdapter(config: TransportAdapterConfig): ITransport {
72
+ if (config.type === "unix-socket") {
73
+ return new UnixSocketTransport({
74
+ socketPath: config.socketPath,
75
+ timeout: config.timeout,
76
+ autoReconnect: config.autoReconnect,
77
+ } as UnixSocketConfig);
78
+ }
79
+
80
+ if (config.type === "ipc") {
81
+ return new IPCTransport({
82
+ timeout: config.timeout,
83
+ } as TransportConfig);
84
+ }
85
+
86
+ // Auto mode: try Unix Socket, fallback to IPC
87
+ if (config.type === "auto") {
88
+ // For now, default to Unix Socket for bulk operations
89
+ // TODO: Add runtime detection and fallback
90
+ return new UnixSocketTransport({
91
+ socketPath: config.socketPath ?? "/tmp/kb-ipc.sock",
92
+ timeout: config.timeout,
93
+ autoReconnect: config.autoReconnect ?? true,
94
+ } as UnixSocketConfig);
95
+ }
96
+
97
+ throw new Error(`Unknown transport type: ${config.type}`);
98
+ }
99
+
100
+ // Default export for standard adapter pattern
101
+ export default createAdapter;