@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,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/core-runtime/transport
|
|
3
|
+
* IPC transport implementation using process.send/process.on('message').
|
|
4
|
+
*
|
|
5
|
+
* This transport is used for communication between parent (CLI) and
|
|
6
|
+
* child (sandbox worker) processes created by child_process.fork().
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Thread-safe: Uses request IDs to match responses with requests
|
|
10
|
+
* - Timeout enforcement: Rejects calls that exceed timeout
|
|
11
|
+
* - Error propagation: Deserializes errors from parent
|
|
12
|
+
* - Cleanup: Properly removes listeners on close()
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { IPCTransport } from '@kb-labs/core-runtime/transport';
|
|
17
|
+
*
|
|
18
|
+
* // In child process
|
|
19
|
+
* const transport = new IPCTransport({ timeout: 10000 });
|
|
20
|
+
*
|
|
21
|
+
* const response = await transport.send({
|
|
22
|
+
* type: 'adapter:call',
|
|
23
|
+
* requestId: 'uuid-123',
|
|
24
|
+
* adapter: 'vectorStore',
|
|
25
|
+
* method: 'search',
|
|
26
|
+
* args: [[0.1, 0.2, 0.3], 10],
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* await transport.close();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import type { AdapterCall, AdapterResponse } from "./types.js";
|
|
34
|
+
import { isAdapterResponse } from "./types.js";
|
|
35
|
+
import {
|
|
36
|
+
type ITransport,
|
|
37
|
+
type TransportConfig,
|
|
38
|
+
type PendingRequest,
|
|
39
|
+
TransportError,
|
|
40
|
+
TimeoutError,
|
|
41
|
+
} from "./transport.js";
|
|
42
|
+
|
|
43
|
+
// Re-export TransportConfig for convenience
|
|
44
|
+
export type { TransportConfig };
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* IPC transport using process.send/process.on('message').
|
|
48
|
+
*
|
|
49
|
+
* Requires:
|
|
50
|
+
* - process.send() must be available (running as forked child)
|
|
51
|
+
* - Parent process must listen for messages and respond
|
|
52
|
+
*
|
|
53
|
+
* Thread-safety:
|
|
54
|
+
* - Uses Map for pending requests (safe for concurrent access in single thread)
|
|
55
|
+
* - Uses request IDs to match responses (no race conditions)
|
|
56
|
+
*
|
|
57
|
+
* Limitations:
|
|
58
|
+
* - Only works in forked child processes (not worker threads)
|
|
59
|
+
* - Parent must implement IPCServer to handle calls
|
|
60
|
+
* - Message size limited by Node.js IPC buffer (typically 1MB)
|
|
61
|
+
*/
|
|
62
|
+
export class IPCTransport implements ITransport {
|
|
63
|
+
private pending = new Map<string, PendingRequest>();
|
|
64
|
+
private messageHandler: (msg: unknown) => void;
|
|
65
|
+
private closed = false;
|
|
66
|
+
|
|
67
|
+
constructor(private config: TransportConfig = {}) {
|
|
68
|
+
// Bind handler to preserve 'this' context
|
|
69
|
+
this.messageHandler = this.handleMessage.bind(this);
|
|
70
|
+
|
|
71
|
+
// Listen for responses from parent
|
|
72
|
+
process.on("message", this.messageHandler);
|
|
73
|
+
|
|
74
|
+
// Check if IPC channel is available
|
|
75
|
+
if (!process.send) {
|
|
76
|
+
throw new TransportError(
|
|
77
|
+
"No IPC channel available. IPCTransport can only be used in forked child processes.",
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async send(call: AdapterCall): Promise<AdapterResponse> {
|
|
83
|
+
if (this.closed) {
|
|
84
|
+
throw new TransportError("Transport is closed");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Determine timeout (call-specific > config default > 30s)
|
|
88
|
+
const timeout = call.timeout ?? this.config.timeout ?? 30000;
|
|
89
|
+
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
// Create timeout timer
|
|
92
|
+
const timer = setTimeout(() => {
|
|
93
|
+
this.pending.delete(call.requestId);
|
|
94
|
+
reject(
|
|
95
|
+
new TimeoutError(
|
|
96
|
+
`Adapter call timed out after ${timeout}ms`,
|
|
97
|
+
timeout,
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
}, timeout);
|
|
101
|
+
|
|
102
|
+
// Store pending request
|
|
103
|
+
this.pending.set(call.requestId, { resolve, reject, timer });
|
|
104
|
+
|
|
105
|
+
// Try to send message, with backpressure handling
|
|
106
|
+
this.trySendWithBackpressure(call, 0).catch((error) => {
|
|
107
|
+
const pending = this.pending.get(call.requestId);
|
|
108
|
+
if (pending) {
|
|
109
|
+
clearTimeout(pending.timer);
|
|
110
|
+
this.pending.delete(call.requestId);
|
|
111
|
+
reject(error);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Try to send IPC message with backpressure handling.
|
|
119
|
+
* If backpressure detected, waits and retries with exponential backoff.
|
|
120
|
+
*
|
|
121
|
+
* Note: Node.js IPC 'drain' event is unreliable, so we use sleep-based backoff instead.
|
|
122
|
+
*
|
|
123
|
+
* @param call - Adapter call to send
|
|
124
|
+
* @param retryCount - Current retry attempt (for exponential backoff)
|
|
125
|
+
*/
|
|
126
|
+
private async trySendWithBackpressure(
|
|
127
|
+
call: AdapterCall,
|
|
128
|
+
retryCount: number,
|
|
129
|
+
): Promise<void> {
|
|
130
|
+
const maxRetries = 20;
|
|
131
|
+
const baseDelayMs = 50;
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
// process.send() is guaranteed to exist (checked in constructor)
|
|
135
|
+
// Returns false if backpressure detected (buffer full)
|
|
136
|
+
const sent = process.send!(call);
|
|
137
|
+
|
|
138
|
+
if (sent) {
|
|
139
|
+
// Message sent successfully
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Backpressure detected
|
|
144
|
+
if (retryCount >= maxRetries) {
|
|
145
|
+
throw new TransportError(
|
|
146
|
+
`Failed to send IPC message after ${maxRetries} retries: persistent backpressure`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Wait with exponential backoff (sleep instead of drain event)
|
|
151
|
+
const delay = Math.min(baseDelayMs * Math.pow(1.5, retryCount), 5000); // Cap at 5s
|
|
152
|
+
await new Promise((resolve) => {
|
|
153
|
+
setTimeout(resolve, delay);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Retry sending
|
|
157
|
+
return this.trySendWithBackpressure(call, retryCount + 1);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
// Synchronous error (e.g., message too large, serialization error, channel closed)
|
|
160
|
+
if (error instanceof TransportError) {
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
throw new TransportError(
|
|
164
|
+
`Failed to send IPC message: ${error}`,
|
|
165
|
+
error as Error,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private handleMessage(msg: unknown) {
|
|
171
|
+
// Ignore non-response messages
|
|
172
|
+
if (!isAdapterResponse(msg)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Find pending request
|
|
177
|
+
const pending = this.pending.get(msg.requestId);
|
|
178
|
+
if (!pending) {
|
|
179
|
+
// Response for unknown request (may have timed out)
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Clear timeout and remove from pending
|
|
184
|
+
clearTimeout(pending.timer);
|
|
185
|
+
this.pending.delete(msg.requestId);
|
|
186
|
+
|
|
187
|
+
// Resolve with response (caller will check for error field)
|
|
188
|
+
pending.resolve(msg);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async close(): Promise<void> {
|
|
192
|
+
if (this.closed) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
this.closed = true;
|
|
197
|
+
|
|
198
|
+
// Remove message listener
|
|
199
|
+
process.off("message", this.messageHandler);
|
|
200
|
+
|
|
201
|
+
// Reject all pending requests
|
|
202
|
+
for (const [_requestId, pending] of this.pending) {
|
|
203
|
+
clearTimeout(pending.timer);
|
|
204
|
+
pending.reject(new TransportError("Transport closed"));
|
|
205
|
+
}
|
|
206
|
+
this.pending.clear();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
isClosed(): boolean {
|
|
210
|
+
return this.closed;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Create IPCTransport with default configuration.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* import { createIPCTransport } from '@kb-labs/core-runtime/transport';
|
|
220
|
+
*
|
|
221
|
+
* const transport = createIPCTransport();
|
|
222
|
+
* // Use transport...
|
|
223
|
+
* await transport.close();
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
export function createIPCTransport(config?: TransportConfig): IPCTransport {
|
|
227
|
+
return new IPCTransport(config);
|
|
228
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/core-runtime/transport
|
|
3
|
+
* Abstract transport layer for cross-process adapter communication.
|
|
4
|
+
*
|
|
5
|
+
* Provides a transport-agnostic interface for sending adapter calls
|
|
6
|
+
* between parent and child processes. Implementations can use:
|
|
7
|
+
* - IPC (process.send/process.on('message'))
|
|
8
|
+
* - HTTP REST API
|
|
9
|
+
* - Docker exec
|
|
10
|
+
* - gRPC
|
|
11
|
+
* - WebSockets
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { ITransport, IPCTransport } from '@kb-labs/core-runtime/transport';
|
|
16
|
+
*
|
|
17
|
+
* const transport: ITransport = new IPCTransport();
|
|
18
|
+
* const response = await transport.send({
|
|
19
|
+
* type: 'adapter:call',
|
|
20
|
+
* requestId: 'uuid-123',
|
|
21
|
+
* adapter: 'vectorStore',
|
|
22
|
+
* method: 'search',
|
|
23
|
+
* args: [[0.1, 0.2, 0.3], 10],
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { AdapterCall, AdapterResponse } from "./types.js";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Transport configuration options.
|
|
32
|
+
*/
|
|
33
|
+
export interface TransportConfig {
|
|
34
|
+
/**
|
|
35
|
+
* Default timeout for adapter calls in milliseconds.
|
|
36
|
+
* @default 30000 (30 seconds)
|
|
37
|
+
*/
|
|
38
|
+
timeout?: number;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Number of retry attempts for transient errors.
|
|
42
|
+
* @default 0 (no retries)
|
|
43
|
+
*/
|
|
44
|
+
retries?: number;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Delay between retry attempts in milliseconds.
|
|
48
|
+
* @default 1000 (1 second)
|
|
49
|
+
*/
|
|
50
|
+
retryDelay?: number;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Exponential backoff multiplier for retries.
|
|
54
|
+
* @default 2 (doubles delay each retry)
|
|
55
|
+
*/
|
|
56
|
+
backoffMultiplier?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Abstract transport for sending adapter calls.
|
|
61
|
+
*
|
|
62
|
+
* Implementations must handle:
|
|
63
|
+
* - Reliable message delivery
|
|
64
|
+
* - Timeout enforcement
|
|
65
|
+
* - Error propagation
|
|
66
|
+
* - Resource cleanup
|
|
67
|
+
*
|
|
68
|
+
* Thread-safety: Implementations must be safe to call concurrently
|
|
69
|
+
* from multiple async contexts.
|
|
70
|
+
*/
|
|
71
|
+
export interface ITransport {
|
|
72
|
+
/**
|
|
73
|
+
* Send adapter call and await response.
|
|
74
|
+
*
|
|
75
|
+
* @param call - Adapter method call to send
|
|
76
|
+
* @returns Response with result or error
|
|
77
|
+
* @throws TransportError if communication fails
|
|
78
|
+
* @throws TimeoutError if timeout exceeded
|
|
79
|
+
* @throws SerializationError if message cannot be serialized
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const call: AdapterCall = {
|
|
84
|
+
* type: 'adapter:call',
|
|
85
|
+
* requestId: 'uuid-123',
|
|
86
|
+
* adapter: 'vectorStore',
|
|
87
|
+
* method: 'search',
|
|
88
|
+
* args: [[0.1, 0.2, 0.3], 10],
|
|
89
|
+
* timeout: 5000,
|
|
90
|
+
* };
|
|
91
|
+
*
|
|
92
|
+
* const response = await transport.send(call);
|
|
93
|
+
* if (response.error) {
|
|
94
|
+
* throw deserialize(response.error);
|
|
95
|
+
* }
|
|
96
|
+
* return deserialize(response.result);
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
send(call: AdapterCall): Promise<AdapterResponse>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Close transport and cleanup resources.
|
|
103
|
+
*
|
|
104
|
+
* After close():
|
|
105
|
+
* - No new calls can be sent
|
|
106
|
+
* - Pending calls are rejected with TransportError
|
|
107
|
+
* - Listeners/connections are cleaned up
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* await transport.close();
|
|
112
|
+
* // All pending calls rejected
|
|
113
|
+
* // transport.send() will throw TransportError
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
close(): Promise<void>;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Check if transport is closed.
|
|
120
|
+
*/
|
|
121
|
+
isClosed(): boolean;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Base error for transport failures.
|
|
126
|
+
*/
|
|
127
|
+
export class TransportError extends Error {
|
|
128
|
+
public override readonly cause?: Error;
|
|
129
|
+
|
|
130
|
+
constructor(message: string, cause?: Error) {
|
|
131
|
+
super(message);
|
|
132
|
+
this.name = "TransportError";
|
|
133
|
+
this.cause = cause;
|
|
134
|
+
if (cause) {
|
|
135
|
+
this.stack = `${this.stack}\nCaused by: ${cause.stack}`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Error thrown when adapter call times out.
|
|
142
|
+
*/
|
|
143
|
+
export class TimeoutError extends TransportError {
|
|
144
|
+
public readonly timeoutMs: number;
|
|
145
|
+
|
|
146
|
+
constructor(message: string, timeoutMs: number) {
|
|
147
|
+
super(message);
|
|
148
|
+
this.name = "TimeoutError";
|
|
149
|
+
this.timeoutMs = timeoutMs;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Error thrown when circuit breaker is open.
|
|
155
|
+
*/
|
|
156
|
+
export class CircuitOpenError extends TransportError {
|
|
157
|
+
constructor(message: string) {
|
|
158
|
+
super(message);
|
|
159
|
+
this.name = "CircuitOpenError";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Check if error is retryable (transient failure).
|
|
165
|
+
*
|
|
166
|
+
* Retryable errors:
|
|
167
|
+
* - Network timeouts (TimeoutError)
|
|
168
|
+
* - Connection errors (ECONNRESET, ECONNREFUSED, ETIMEDOUT)
|
|
169
|
+
* - Temporary server errors (503 Service Unavailable)
|
|
170
|
+
*
|
|
171
|
+
* Non-retryable errors:
|
|
172
|
+
* - Invalid requests (400 Bad Request)
|
|
173
|
+
* - Authentication failures (401, 403)
|
|
174
|
+
* - Not found (404)
|
|
175
|
+
* - Application errors from adapter (e.g., VectorStoreError)
|
|
176
|
+
*
|
|
177
|
+
* @param error - Error to check
|
|
178
|
+
* @returns true if error should be retried
|
|
179
|
+
*/
|
|
180
|
+
export function isRetryableError(error: Error): boolean {
|
|
181
|
+
// Timeout errors are retryable
|
|
182
|
+
if (error instanceof TimeoutError) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Circuit breaker open is NOT retryable (wait for half-open state)
|
|
187
|
+
if (error instanceof CircuitOpenError) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check error code for network errors
|
|
192
|
+
const code = (error as any).code;
|
|
193
|
+
if (code) {
|
|
194
|
+
const retryableCodes = [
|
|
195
|
+
"ECONNRESET", // Connection reset
|
|
196
|
+
"ECONNREFUSED", // Connection refused
|
|
197
|
+
"ETIMEDOUT", // Operation timed out
|
|
198
|
+
"ENOTFOUND", // DNS lookup failed
|
|
199
|
+
"EAI_AGAIN", // DNS temporary failure
|
|
200
|
+
];
|
|
201
|
+
return retryableCodes.includes(code);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check HTTP status for server errors
|
|
205
|
+
const status = (error as any).status || (error as any).statusCode;
|
|
206
|
+
if (status) {
|
|
207
|
+
// 503 Service Unavailable is retryable
|
|
208
|
+
// 429 Too Many Requests is retryable
|
|
209
|
+
return status === 503 || status === 429;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Default: not retryable
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Internal pending request tracking.
|
|
218
|
+
* Used by transport implementations to match responses with requests.
|
|
219
|
+
*/
|
|
220
|
+
export interface PendingRequest {
|
|
221
|
+
resolve: (response: AdapterResponse) => void;
|
|
222
|
+
reject: (error: Error) => void;
|
|
223
|
+
timer: NodeJS.Timeout;
|
|
224
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for transport layer.
|
|
3
|
+
* Extracted from @kb-labs/core-platform to avoid circular dependencies.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface SerializableBuffer {
|
|
7
|
+
__type: "Buffer";
|
|
8
|
+
data: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SerializableDate {
|
|
12
|
+
__type: "Date";
|
|
13
|
+
iso: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SerializableError {
|
|
17
|
+
__type: "Error";
|
|
18
|
+
name: string;
|
|
19
|
+
message: string;
|
|
20
|
+
stack?: string;
|
|
21
|
+
code?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type SerializableValue =
|
|
25
|
+
| null
|
|
26
|
+
| boolean
|
|
27
|
+
| number
|
|
28
|
+
| string
|
|
29
|
+
| SerializableBuffer
|
|
30
|
+
| SerializableDate
|
|
31
|
+
| SerializableError
|
|
32
|
+
| SerializableArray
|
|
33
|
+
| SerializableObject;
|
|
34
|
+
|
|
35
|
+
export type SerializableArray = SerializableValue[];
|
|
36
|
+
export type SerializableObject = { [key: string]: SerializableValue };
|
|
37
|
+
|
|
38
|
+
export type AdapterType =
|
|
39
|
+
| "vectorStore"
|
|
40
|
+
| "cache"
|
|
41
|
+
| "llm"
|
|
42
|
+
| "embeddings"
|
|
43
|
+
| "storage"
|
|
44
|
+
| "logger"
|
|
45
|
+
| "analytics"
|
|
46
|
+
| "eventBus"
|
|
47
|
+
| "invoke"
|
|
48
|
+
| "artifacts";
|
|
49
|
+
|
|
50
|
+
export interface AdapterCallContext {
|
|
51
|
+
traceId?: string;
|
|
52
|
+
sessionId?: string;
|
|
53
|
+
pluginId?: string;
|
|
54
|
+
workspaceId?: string;
|
|
55
|
+
tenantId?: string;
|
|
56
|
+
permissions?: {
|
|
57
|
+
adapters?: string[];
|
|
58
|
+
storagePaths?: string[];
|
|
59
|
+
networkHosts?: string[];
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const IPC_PROTOCOL_VERSION = 2;
|
|
64
|
+
|
|
65
|
+
export interface AdapterCall {
|
|
66
|
+
version: number;
|
|
67
|
+
type: "adapter:call";
|
|
68
|
+
requestId: string;
|
|
69
|
+
adapter: AdapterType;
|
|
70
|
+
method: string;
|
|
71
|
+
args: SerializableValue[];
|
|
72
|
+
timeout?: number;
|
|
73
|
+
context?: AdapterCallContext;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface AdapterResponse {
|
|
77
|
+
type: "adapter:response";
|
|
78
|
+
requestId: string;
|
|
79
|
+
result?: SerializableValue;
|
|
80
|
+
error?: SerializableError;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function isAdapterResponse(msg: unknown): msg is AdapterResponse {
|
|
84
|
+
return (
|
|
85
|
+
typeof msg === "object" &&
|
|
86
|
+
msg !== null &&
|
|
87
|
+
"type" in msg &&
|
|
88
|
+
(msg as any).type === "adapter:response" &&
|
|
89
|
+
"requestId" in msg &&
|
|
90
|
+
typeof (msg as any).requestId === "string"
|
|
91
|
+
);
|
|
92
|
+
}
|