@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.
- package/.cursorrules +32 -0
- package/.github/workflows/ci.yml +13 -0
- package/.github/workflows/deploy.yml +28 -0
- package/.github/workflows/docker-build.yml +25 -0
- package/.github/workflows/drift-check.yml +10 -0
- package/.github/workflows/profiles-validate.yml +16 -0
- package/.github/workflows/release.yml +8 -0
- package/.kb/devkit/agents/devkit-maintainer/context.globs +15 -0
- package/.kb/devkit/agents/devkit-maintainer/permissions.yml +17 -0
- package/.kb/devkit/agents/devkit-maintainer/prompt.md +28 -0
- package/.kb/devkit/agents/devkit-maintainer/runbook.md +31 -0
- package/.kb/devkit/agents/docs-crafter/prompt.md +24 -0
- package/.kb/devkit/agents/docs-crafter/runbook.md +18 -0
- package/.kb/devkit/agents/release-manager/context.globs +7 -0
- package/.kb/devkit/agents/release-manager/prompt.md +27 -0
- package/.kb/devkit/agents/release-manager/runbook.md +17 -0
- package/.kb/devkit/agents/test-generator/context.globs +7 -0
- package/.kb/devkit/agents/test-generator/prompt.md +27 -0
- package/.kb/devkit/agents/test-generator/runbook.md +18 -0
- package/CONTRIBUTING.md +90 -0
- package/IMPLEMENTATION_COMPLETE.md +416 -0
- package/LICENSE +186 -0
- package/README-TEMPLATE.md +179 -0
- package/README.md +306 -0
- package/docs/DOCUMENTATION.md +74 -0
- package/docs/adr/0000-template.md +49 -0
- package/docs/adr/0001-architecture-and-repository-layout.md +33 -0
- package/docs/adr/0002-plugins-and-extensibility.md +46 -0
- package/docs/adr/0003-package-and-module-boundaries.md +37 -0
- package/docs/adr/0004-versioning-and-release-policy.md +38 -0
- package/docs/adr/0005-use-devkit-for-shared-tooling.md +48 -0
- package/docs/adr/0006-adopt-devkit-sync.md +47 -0
- package/docs/adr/0007-drift-kit-check.md +72 -0
- package/docs/adr/0008-devkit-sync-wrapper-strategy.md +67 -0
- package/docs/naming-convention.md +272 -0
- package/eslint.config.js +27 -0
- package/kb-labs.config.json +5 -0
- package/package.json +84 -0
- package/package.json.bin +25 -0
- package/package.json.lib +30 -0
- package/packages/adapters-analytics-duckdb/package.json +54 -0
- package/packages/adapters-analytics-duckdb/scripts/migrate-from-jsonl.mjs +253 -0
- package/packages/adapters-analytics-duckdb/src/index.ts +380 -0
- package/packages/adapters-analytics-duckdb/src/manifest.ts +36 -0
- package/packages/adapters-analytics-duckdb/src/schema.ts +161 -0
- package/packages/adapters-analytics-duckdb/tsconfig.build.json +15 -0
- package/packages/adapters-analytics-duckdb/tsconfig.json +9 -0
- package/packages/adapters-analytics-duckdb/tsup.config.ts +9 -0
- package/packages/adapters-analytics-file/README.md +32 -0
- package/packages/adapters-analytics-file/eslint.config.js +27 -0
- package/packages/adapters-analytics-file/package.json +50 -0
- package/packages/adapters-analytics-file/src/__tests__/daily-stats.spec.ts +287 -0
- package/packages/adapters-analytics-file/src/__tests__/scoped-analytics.test.ts +233 -0
- package/packages/adapters-analytics-file/src/index.test.ts +214 -0
- package/packages/adapters-analytics-file/src/index.ts +830 -0
- package/packages/adapters-analytics-file/src/manifest.ts +45 -0
- package/packages/adapters-analytics-file/tsconfig.build.json +15 -0
- package/packages/adapters-analytics-file/tsconfig.json +9 -0
- package/packages/adapters-analytics-file/tsup.config.ts +9 -0
- package/packages/adapters-analytics-sqlite/package.json +55 -0
- package/packages/adapters-analytics-sqlite/scripts/migrate-from-jsonl.mjs +194 -0
- package/packages/adapters-analytics-sqlite/src/index.ts +460 -0
- package/packages/adapters-analytics-sqlite/src/manifest.ts +41 -0
- package/packages/adapters-analytics-sqlite/tsconfig.build.json +15 -0
- package/packages/adapters-analytics-sqlite/tsconfig.json +9 -0
- package/packages/adapters-analytics-sqlite/tsup.config.ts +9 -0
- package/packages/adapters-environment-docker/README.md +28 -0
- package/packages/adapters-environment-docker/eslint.config.js +5 -0
- package/packages/adapters-environment-docker/package.json +49 -0
- package/packages/adapters-environment-docker/src/index.test.ts +138 -0
- package/packages/adapters-environment-docker/src/index.ts +439 -0
- package/packages/adapters-environment-docker/src/manifest.ts +65 -0
- package/packages/adapters-environment-docker/tsconfig.build.json +15 -0
- package/packages/adapters-environment-docker/tsconfig.json +16 -0
- package/packages/adapters-environment-docker/tsup.config.ts +9 -0
- package/packages/adapters-eventbus-cache/README.md +242 -0
- package/packages/adapters-eventbus-cache/eslint.config.js +27 -0
- package/packages/adapters-eventbus-cache/package.json +46 -0
- package/packages/adapters-eventbus-cache/src/index.test.ts +235 -0
- package/packages/adapters-eventbus-cache/src/index.ts +215 -0
- package/packages/adapters-eventbus-cache/src/manifest.ts +50 -0
- package/packages/adapters-eventbus-cache/src/types.ts +58 -0
- package/packages/adapters-eventbus-cache/tsconfig.build.json +15 -0
- package/packages/adapters-eventbus-cache/tsconfig.json +9 -0
- package/packages/adapters-eventbus-cache/tsup.config.ts +9 -0
- package/packages/adapters-fs/README.md +171 -0
- package/packages/adapters-fs/allowed.txt +1 -0
- package/packages/adapters-fs/conflict.txt +1 -0
- package/packages/adapters-fs/dest.txt +1 -0
- package/packages/adapters-fs/eslint.config.js +27 -0
- package/packages/adapters-fs/exists.txt +1 -0
- package/packages/adapters-fs/not-allowed.txt +1 -0
- package/packages/adapters-fs/other.txt +1 -0
- package/packages/adapters-fs/package.json +55 -0
- package/packages/adapters-fs/public/file1.txt +1 -0
- package/packages/adapters-fs/public/file2.txt +1 -0
- package/packages/adapters-fs/secret.txt +1 -0
- package/packages/adapters-fs/secrets/key.txt +1 -0
- package/packages/adapters-fs/src/index.test.ts +243 -0
- package/packages/adapters-fs/src/index.ts +258 -0
- package/packages/adapters-fs/src/manifest.ts +35 -0
- package/packages/adapters-fs/src/secure-storage.test.ts +380 -0
- package/packages/adapters-fs/src/secure-storage.ts +268 -0
- package/packages/adapters-fs/test.json +1 -0
- package/packages/adapters-fs/test.txt +1 -0
- package/packages/adapters-fs/test.xyz +1 -0
- package/packages/adapters-fs/test1.txt +1 -0
- package/packages/adapters-fs/test2.txt +1 -0
- package/packages/adapters-fs/tsconfig.build.json +15 -0
- package/packages/adapters-fs/tsconfig.json +9 -0
- package/packages/adapters-fs/tsup.config.ts +8 -0
- package/packages/adapters-fs/vitest.config.ts +19 -0
- package/packages/adapters-log-ringbuffer/README.md +228 -0
- package/packages/adapters-log-ringbuffer/eslint.config.js +27 -0
- package/packages/adapters-log-ringbuffer/package.json +47 -0
- package/packages/adapters-log-ringbuffer/src/__tests__/ring-buffer.test.ts +450 -0
- package/packages/adapters-log-ringbuffer/src/index.ts +212 -0
- package/packages/adapters-log-ringbuffer/src/manifest.ts +30 -0
- package/packages/adapters-log-ringbuffer/tsconfig.build.json +15 -0
- package/packages/adapters-log-ringbuffer/tsconfig.json +9 -0
- package/packages/adapters-log-ringbuffer/tsup.config.ts +9 -0
- package/packages/adapters-log-ringbuffer/vitest.config.ts +14 -0
- package/packages/adapters-log-sqlite/README.md +396 -0
- package/packages/adapters-log-sqlite/eslint.config.js +27 -0
- package/packages/adapters-log-sqlite/package.json +49 -0
- package/packages/adapters-log-sqlite/src/__tests__/log-persistence.test.ts +718 -0
- package/packages/adapters-log-sqlite/src/index.ts +1068 -0
- package/packages/adapters-log-sqlite/src/manifest.ts +36 -0
- package/packages/adapters-log-sqlite/src/schema.sql +46 -0
- package/packages/adapters-log-sqlite/tsconfig.build.json +15 -0
- package/packages/adapters-log-sqlite/tsconfig.json +9 -0
- package/packages/adapters-log-sqlite/tsup.config.ts +9 -0
- package/packages/adapters-log-sqlite/vitest.config.ts +15 -0
- package/packages/adapters-mongodb/README.md +147 -0
- package/packages/adapters-mongodb/eslint.config.js +27 -0
- package/packages/adapters-mongodb/package.json +53 -0
- package/packages/adapters-mongodb/src/index.ts +428 -0
- package/packages/adapters-mongodb/src/manifest.ts +45 -0
- package/packages/adapters-mongodb/src/secure-document.ts +231 -0
- package/packages/adapters-mongodb/tsconfig.build.json +15 -0
- package/packages/adapters-mongodb/tsconfig.json +9 -0
- package/packages/adapters-mongodb/tsup.config.ts +8 -0
- package/packages/adapters-openai/README.md +151 -0
- package/packages/adapters-openai/embeddings.ts +37 -0
- package/packages/adapters-openai/eslint.config.js +26 -0
- package/packages/adapters-openai/index.ts +22 -0
- package/packages/adapters-openai/package.json +57 -0
- package/packages/adapters-openai/src/embeddings-manifest.ts +45 -0
- package/packages/adapters-openai/src/embeddings.ts +104 -0
- package/packages/adapters-openai/src/index.ts +13 -0
- package/packages/adapters-openai/src/llm.ts +304 -0
- package/packages/adapters-openai/src/manifest.ts +47 -0
- package/packages/adapters-openai/tsconfig.build.json +15 -0
- package/packages/adapters-openai/tsconfig.json +9 -0
- package/packages/adapters-openai/tsup.config.ts +8 -0
- package/packages/adapters-pino/README.md +152 -0
- package/packages/adapters-pino/eslint.config.js +27 -0
- package/packages/adapters-pino/package.json +49 -0
- package/packages/adapters-pino/src/index.test.ts +44 -0
- package/packages/adapters-pino/src/index.ts +322 -0
- package/packages/adapters-pino/src/log-ring-buffer.ts +142 -0
- package/packages/adapters-pino/src/manifest.ts +49 -0
- package/packages/adapters-pino/tsconfig.build.json +15 -0
- package/packages/adapters-pino/tsconfig.json +9 -0
- package/packages/adapters-pino/tsup.config.ts +9 -0
- package/packages/adapters-pino-http/README.md +141 -0
- package/packages/adapters-pino-http/eslint.config.js +27 -0
- package/packages/adapters-pino-http/package.json +46 -0
- package/packages/adapters-pino-http/src/index.ts +229 -0
- package/packages/adapters-pino-http/tsconfig.build.json +15 -0
- package/packages/adapters-pino-http/tsconfig.json +9 -0
- package/packages/adapters-pino-http/tsup.config.ts +9 -0
- package/packages/adapters-qdrant/README.md +166 -0
- package/packages/adapters-qdrant/eslint.config.js +27 -0
- package/packages/adapters-qdrant/package.json +49 -0
- package/packages/adapters-qdrant/src/index.ts +490 -0
- package/packages/adapters-qdrant/src/manifest.ts +54 -0
- package/packages/adapters-qdrant/src/retry.ts +204 -0
- package/packages/adapters-qdrant/tsconfig.build.json +15 -0
- package/packages/adapters-qdrant/tsconfig.json +9 -0
- package/packages/adapters-qdrant/tsup.config.ts +9 -0
- package/packages/adapters-redis/README.md +159 -0
- package/packages/adapters-redis/eslint.config.js +27 -0
- package/packages/adapters-redis/package.json +49 -0
- package/packages/adapters-redis/src/index.ts +164 -0
- package/packages/adapters-redis/src/manifest.ts +49 -0
- package/packages/adapters-redis/tsconfig.build.json +15 -0
- package/packages/adapters-redis/tsconfig.json +9 -0
- package/packages/adapters-redis/tsup.config.ts +9 -0
- package/packages/adapters-snapshot-localfs/README.md +10 -0
- package/packages/adapters-snapshot-localfs/eslint.config.js +2 -0
- package/packages/adapters-snapshot-localfs/package.json +46 -0
- package/packages/adapters-snapshot-localfs/src/index.test.ts +40 -0
- package/packages/adapters-snapshot-localfs/src/index.ts +292 -0
- package/packages/adapters-snapshot-localfs/src/manifest.ts +32 -0
- package/packages/adapters-snapshot-localfs/tsconfig.build.json +15 -0
- package/packages/adapters-snapshot-localfs/tsconfig.json +16 -0
- package/packages/adapters-snapshot-localfs/tsup.config.ts +11 -0
- package/packages/adapters-sqlite/README.md +163 -0
- package/packages/adapters-sqlite/eslint.config.js +27 -0
- package/packages/adapters-sqlite/package.json +54 -0
- package/packages/adapters-sqlite/src/index.test.ts +245 -0
- package/packages/adapters-sqlite/src/index.ts +382 -0
- package/packages/adapters-sqlite/src/manifest.ts +47 -0
- package/packages/adapters-sqlite/src/secure-sql.test.ts +290 -0
- package/packages/adapters-sqlite/src/secure-sql.ts +281 -0
- package/packages/adapters-sqlite/tsconfig.build.json +15 -0
- package/packages/adapters-sqlite/tsconfig.json +9 -0
- package/packages/adapters-sqlite/tsup.config.ts +8 -0
- package/packages/adapters-sqlite/vitest.config.ts +19 -0
- package/packages/adapters-transport/README.md +170 -0
- package/packages/adapters-transport/eslint.config.js +27 -0
- package/packages/adapters-transport/package.json +49 -0
- package/packages/adapters-transport/src/__tests__/unix-socket-server.test.ts +550 -0
- package/packages/adapters-transport/src/index.ts +101 -0
- package/packages/adapters-transport/src/ipc-transport.ts +228 -0
- package/packages/adapters-transport/src/transport.ts +224 -0
- package/packages/adapters-transport/src/types.ts +92 -0
- package/packages/adapters-transport/src/unix-socket-server.ts +193 -0
- package/packages/adapters-transport/src/unix-socket-transport.ts +280 -0
- package/packages/adapters-transport/tsconfig.build.json +15 -0
- package/packages/adapters-transport/tsconfig.json +9 -0
- package/packages/adapters-transport/tsup.config.ts +9 -0
- package/packages/adapters-vibeproxy/README.md +159 -0
- package/packages/adapters-vibeproxy/eslint.config.js +27 -0
- package/packages/adapters-vibeproxy/package.json +51 -0
- package/packages/adapters-vibeproxy/src/index.ts +13 -0
- package/packages/adapters-vibeproxy/src/llm.ts +437 -0
- package/packages/adapters-vibeproxy/src/manifest.ts +51 -0
- package/packages/adapters-vibeproxy/tsconfig.build.json +15 -0
- package/packages/adapters-vibeproxy/tsconfig.json +9 -0
- package/packages/adapters-vibeproxy/tsup.config.ts +8 -0
- package/packages/adapters-workspace-agent/package.json +46 -0
- package/packages/adapters-workspace-agent/src/__tests__/adapter.test.ts +212 -0
- package/packages/adapters-workspace-agent/src/index.ts +220 -0
- package/packages/adapters-workspace-agent/src/manifest.ts +36 -0
- package/packages/adapters-workspace-agent/tsconfig.build.json +15 -0
- package/packages/adapters-workspace-agent/tsconfig.json +16 -0
- package/packages/adapters-workspace-agent/tsup.config.ts +11 -0
- package/packages/adapters-workspace-localfs/README.md +9 -0
- package/packages/adapters-workspace-localfs/eslint.config.js +2 -0
- package/packages/adapters-workspace-localfs/package.json +46 -0
- package/packages/adapters-workspace-localfs/src/index.test.ts +27 -0
- package/packages/adapters-workspace-localfs/src/index.ts +172 -0
- package/packages/adapters-workspace-localfs/src/manifest.ts +32 -0
- package/packages/adapters-workspace-localfs/tsconfig.build.json +15 -0
- package/packages/adapters-workspace-localfs/tsconfig.json +16 -0
- package/packages/adapters-workspace-localfs/tsup.config.ts +11 -0
- package/packages/adapters-workspace-worktree/README.md +9 -0
- package/packages/adapters-workspace-worktree/eslint.config.js +2 -0
- package/packages/adapters-workspace-worktree/package.json +46 -0
- package/packages/adapters-workspace-worktree/src/index.test.ts +38 -0
- package/packages/adapters-workspace-worktree/src/index.ts +245 -0
- package/packages/adapters-workspace-worktree/src/manifest.ts +38 -0
- package/packages/adapters-workspace-worktree/tsconfig.build.json +15 -0
- package/packages/adapters-workspace-worktree/tsconfig.json +16 -0
- package/packages/adapters-workspace-worktree/tsup.config.ts +11 -0
- package/pnpm-workspace.yaml +2800 -0
- package/prettierrc.json +1 -0
- package/scripts/devkit-sync.mjs +37 -0
- package/scripts/hooks/post-push +9 -0
- package/scripts/hooks/pre-commit +9 -0
- package/scripts/hooks/pre-push +9 -0
- package/test-integration.ts +242 -0
- package/test.txt +1 -0
- package/tsconfig.base.json +6 -0
- package/tsconfig.build.json +15 -0
- package/tsconfig.json +9 -0
- package/tsconfig.paths.json +26 -0
- package/tsconfig.tools.json +17 -0
- package/tsup.config.bin.ts +34 -0
- package/tsup.config.cli.ts +41 -0
- package/tsup.config.dual.ts +46 -0
- package/tsup.config.ts +36 -0
- package/tsup.external.json +103 -0
- 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;
|