@kb-labs/shared 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/.vscode/settings.json +23 -0
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTING.md +117 -0
- package/LICENSE +21 -0
- package/README.md +306 -0
- package/docs/DECLARATIVE-FLAGS-AND-ENV.md +622 -0
- package/docs/DOCUMENTATION.md +70 -0
- package/docs/adr/0000-template.md +52 -0
- package/docs/adr/0001-architecture-and-repository-layout.md +31 -0
- package/docs/adr/0002-plugins-and-extensibility.md +44 -0
- package/docs/adr/0003-package-and-module-boundaries.md +35 -0
- package/docs/adr/0004-versioning-and-release-policy.md +36 -0
- package/docs/adr/0005-reactive-loader-pattern.md +179 -0
- package/docs/adr/0006-declarative-flags-and-env-systems.md +376 -0
- package/eslint.config.js +27 -0
- package/kb-labs.config.json +5 -0
- package/package.json +88 -0
- package/package.json.bin +25 -0
- package/package.json.lib +30 -0
- package/packages/shared-cli-ui/CHANGELOG.md +20 -0
- package/packages/shared-cli-ui/README.md +342 -0
- package/packages/shared-cli-ui/docs/ARCHITECTURE.md +105 -0
- package/packages/shared-cli-ui/eslint.config.js +27 -0
- package/packages/shared-cli-ui/package.json +72 -0
- package/packages/shared-cli-ui/src/__tests__/artifacts-display.spec.ts +89 -0
- package/packages/shared-cli-ui/src/__tests__/format.spec.ts +44 -0
- package/packages/shared-cli-ui/src/__tests__/loader-json-mode.test.ts +119 -0
- package/packages/shared-cli-ui/src/artifacts-display.ts +266 -0
- package/packages/shared-cli-ui/src/cli-auto-discovery.ts +120 -0
- package/packages/shared-cli-ui/src/colors.ts +142 -0
- package/packages/shared-cli-ui/src/command-discovery.ts +72 -0
- package/packages/shared-cli-ui/src/command-output.ts +153 -0
- package/packages/shared-cli-ui/src/command-result.ts +267 -0
- package/packages/shared-cli-ui/src/command-runner.ts +310 -0
- package/packages/shared-cli-ui/src/command-suggestions.ts +204 -0
- package/packages/shared-cli-ui/src/debug/components/output.ts +141 -0
- package/packages/shared-cli-ui/src/debug/components/trace.ts +101 -0
- package/packages/shared-cli-ui/src/debug/components/tree.ts +88 -0
- package/packages/shared-cli-ui/src/debug/formatters/ai.ts +17 -0
- package/packages/shared-cli-ui/src/debug/formatters/human.ts +98 -0
- package/packages/shared-cli-ui/src/debug/formatters/timeline.ts +94 -0
- package/packages/shared-cli-ui/src/debug/index.ts +56 -0
- package/packages/shared-cli-ui/src/debug/types.ts +57 -0
- package/packages/shared-cli-ui/src/debug/utilities.ts +203 -0
- package/packages/shared-cli-ui/src/dynamic-command-discovery.ts +131 -0
- package/packages/shared-cli-ui/src/format.ts +412 -0
- package/packages/shared-cli-ui/src/index.ts +34 -0
- package/packages/shared-cli-ui/src/loader.ts +196 -0
- package/packages/shared-cli-ui/src/manifest-parser.ts +151 -0
- package/packages/shared-cli-ui/src/modern-format.ts +271 -0
- package/packages/shared-cli-ui/src/multi-cli-suggestions.ts +159 -0
- package/packages/shared-cli-ui/src/table.ts +134 -0
- package/packages/shared-cli-ui/src/timing-tracker.ts +68 -0
- package/packages/shared-cli-ui/src/utils/context.ts +12 -0
- package/packages/shared-cli-ui/src/utils/env.ts +164 -0
- package/packages/shared-cli-ui/src/utils/flags.ts +269 -0
- package/packages/shared-cli-ui/src/utils/path.ts +8 -0
- package/packages/shared-cli-ui/tsconfig.build.json +15 -0
- package/packages/shared-cli-ui/tsconfig.json +9 -0
- package/packages/shared-cli-ui/tsup.config.ts +11 -0
- package/packages/shared-cli-ui/vitest.config.ts +15 -0
- package/packages/shared-command-kit/CHANGELOG.md +20 -0
- package/packages/shared-command-kit/LICENSE +22 -0
- package/packages/shared-command-kit/README.md +1030 -0
- package/packages/shared-command-kit/docs/HIGH-LEVEL-API.md +89 -0
- package/packages/shared-command-kit/docs/LOW-LEVEL-API.md +105 -0
- package/packages/shared-command-kit/docs/MIGRATION-GUIDE.md +135 -0
- package/packages/shared-command-kit/eslint.config.js +27 -0
- package/packages/shared-command-kit/eslint.config.ts +14 -0
- package/packages/shared-command-kit/package.json +76 -0
- package/packages/shared-command-kit/prettierrc.json +5 -0
- package/packages/shared-command-kit/src/__tests__/define-command.spec.ts +294 -0
- package/packages/shared-command-kit/src/__tests__/define-route.test.ts +285 -0
- package/packages/shared-command-kit/src/__tests__/define-system-command.spec.ts +508 -0
- package/packages/shared-command-kit/src/__tests__/define-webhook.test.ts +156 -0
- package/packages/shared-command-kit/src/__tests__/define-websocket.test.ts +316 -0
- package/packages/shared-command-kit/src/__tests__/errors.spec.ts +45 -0
- package/packages/shared-command-kit/src/__tests__/flags.spec.ts +353 -0
- package/packages/shared-command-kit/src/__tests__/platform-api.test.ts +135 -0
- package/packages/shared-command-kit/src/__tests__/plugin-context-v3.snapshot.spec.ts +240 -0
- package/packages/shared-command-kit/src/__tests__/ws-types.test.ts +359 -0
- package/packages/shared-command-kit/src/analytics/index.ts +6 -0
- package/packages/shared-command-kit/src/analytics/with-analytics.ts +195 -0
- package/packages/shared-command-kit/src/define-action.ts +100 -0
- package/packages/shared-command-kit/src/define-command.ts +113 -0
- package/packages/shared-command-kit/src/define-route.ts +113 -0
- package/packages/shared-command-kit/src/define-system-command.ts +362 -0
- package/packages/shared-command-kit/src/define-webhook.ts +115 -0
- package/packages/shared-command-kit/src/define-websocket.ts +308 -0
- package/packages/shared-command-kit/src/errors/factory.ts +282 -0
- package/packages/shared-command-kit/src/errors/format-validation.ts +144 -0
- package/packages/shared-command-kit/src/errors/format.ts +92 -0
- package/packages/shared-command-kit/src/errors/index.ts +9 -0
- package/packages/shared-command-kit/src/errors/types.ts +32 -0
- package/packages/shared-command-kit/src/flags/define.ts +92 -0
- package/packages/shared-command-kit/src/flags/index.ts +9 -0
- package/packages/shared-command-kit/src/flags/types.ts +153 -0
- package/packages/shared-command-kit/src/flags/validate.ts +358 -0
- package/packages/shared-command-kit/src/helpers/context.ts +8 -0
- package/packages/shared-command-kit/src/helpers/flags.ts +84 -0
- package/packages/shared-command-kit/src/helpers/index.ts +42 -0
- package/packages/shared-command-kit/src/helpers/patterns.ts +464 -0
- package/packages/shared-command-kit/src/helpers/platform.ts +335 -0
- package/packages/shared-command-kit/src/helpers/use-analytics.ts +95 -0
- package/packages/shared-command-kit/src/helpers/use-cache.ts +97 -0
- package/packages/shared-command-kit/src/helpers/use-config.ts +99 -0
- package/packages/shared-command-kit/src/helpers/use-embeddings.ts +49 -0
- package/packages/shared-command-kit/src/helpers/use-llm.ts +316 -0
- package/packages/shared-command-kit/src/helpers/use-logger.ts +77 -0
- package/packages/shared-command-kit/src/helpers/use-platform.ts +111 -0
- package/packages/shared-command-kit/src/helpers/use-resource-broker.ts +106 -0
- package/packages/shared-command-kit/src/helpers/use-storage.ts +71 -0
- package/packages/shared-command-kit/src/helpers/use-vector-store.ts +49 -0
- package/packages/shared-command-kit/src/helpers/validation.ts +398 -0
- package/packages/shared-command-kit/src/index.ts +410 -0
- package/packages/shared-command-kit/src/jobs.ts +132 -0
- package/packages/shared-command-kit/src/lifecycle/define-handlers.ts +366 -0
- package/packages/shared-command-kit/src/lifecycle/index.ts +6 -0
- package/packages/shared-command-kit/src/manifest.ts +127 -0
- package/packages/shared-command-kit/src/rest/define-handler.ts +187 -0
- package/packages/shared-command-kit/src/rest/index.ts +11 -0
- package/packages/shared-command-kit/src/studio/index.ts +12 -0
- package/packages/shared-command-kit/src/validation/index.ts +6 -0
- package/packages/shared-command-kit/src/validation/schema-builders.ts +409 -0
- package/packages/shared-command-kit/src/ws-types.ts +106 -0
- package/packages/shared-command-kit/tsconfig.build.json +15 -0
- package/packages/shared-command-kit/tsconfig.json +9 -0
- package/packages/shared-command-kit/tsup.config.ts +30 -0
- package/packages/shared-command-kit/vitest.config.ts +4 -0
- package/packages/shared-http/package.json +67 -0
- package/packages/shared-http/src/__tests__/log-correlation.test.ts +81 -0
- package/packages/shared-http/src/__tests__/operation-metrics-tracker.test.ts +55 -0
- package/packages/shared-http/src/http-observability-collector.ts +363 -0
- package/packages/shared-http/src/index.ts +36 -0
- package/packages/shared-http/src/log-correlation.ts +89 -0
- package/packages/shared-http/src/operation-metrics-tracker.ts +107 -0
- package/packages/shared-http/src/register-openapi.ts +108 -0
- package/packages/shared-http/src/resolve-schema-ref.ts +75 -0
- package/packages/shared-http/src/schemas.ts +29 -0
- package/packages/shared-http/src/service-observability.ts +63 -0
- package/packages/shared-http/tsconfig.build.json +15 -0
- package/packages/shared-http/tsconfig.json +9 -0
- package/packages/shared-http/tsup.config.ts +23 -0
- package/packages/shared-http/vitest.config.ts +13 -0
- package/packages/shared-perm-presets/CHANGELOG.md +20 -0
- package/packages/shared-perm-presets/README.md +78 -0
- package/packages/shared-perm-presets/eslint.config.js +27 -0
- package/packages/shared-perm-presets/package.json +45 -0
- package/packages/shared-perm-presets/src/__tests__/combine.test.ts +403 -0
- package/packages/shared-perm-presets/src/__tests__/presets.test.ts +205 -0
- package/packages/shared-perm-presets/src/combine.ts +278 -0
- package/packages/shared-perm-presets/src/index.ts +18 -0
- package/packages/shared-perm-presets/src/presets/ci-environment.ts +34 -0
- package/packages/shared-perm-presets/src/presets/full-env.ts +16 -0
- package/packages/shared-perm-presets/src/presets/git-workflow.ts +40 -0
- package/packages/shared-perm-presets/src/presets/index.ts +8 -0
- package/packages/shared-perm-presets/src/presets/kb-platform.ts +30 -0
- package/packages/shared-perm-presets/src/presets/llm-access.ts +29 -0
- package/packages/shared-perm-presets/src/presets/minimal.ts +21 -0
- package/packages/shared-perm-presets/src/presets/npm-publish.ts +48 -0
- package/packages/shared-perm-presets/src/presets/vector-store.ts +40 -0
- package/packages/shared-perm-presets/src/types.ts +192 -0
- package/packages/shared-perm-presets/tsconfig.build.json +15 -0
- package/packages/shared-perm-presets/tsconfig.json +9 -0
- package/packages/shared-perm-presets/tsup.config.ts +8 -0
- package/packages/shared-perm-presets/vitest.config.ts +9 -0
- package/packages/shared-testing/CHANGELOG.md +20 -0
- package/packages/shared-testing/README.md +430 -0
- package/packages/shared-testing/package.json +51 -0
- package/packages/shared-testing/src/__tests__/create-test-context.test.ts +199 -0
- package/packages/shared-testing/src/__tests__/mock-cache.test.ts +174 -0
- package/packages/shared-testing/src/__tests__/mock-llm.test.ts +212 -0
- package/packages/shared-testing/src/__tests__/setup-platform.test.ts +90 -0
- package/packages/shared-testing/src/__tests__/test-command.test.ts +557 -0
- package/packages/shared-testing/src/create-test-context.ts +550 -0
- package/packages/shared-testing/src/index.ts +77 -0
- package/packages/shared-testing/src/mock-cache.ts +179 -0
- package/packages/shared-testing/src/mock-llm.ts +319 -0
- package/packages/shared-testing/src/mock-logger.ts +97 -0
- package/packages/shared-testing/src/mock-storage.ts +108 -0
- package/packages/shared-testing/src/setup-platform.ts +101 -0
- package/packages/shared-testing/src/test-command.ts +288 -0
- package/packages/shared-testing/tsconfig.build.json +15 -0
- package/packages/shared-testing/tsconfig.json +9 -0
- package/packages/shared-testing/tsup.config.ts +20 -0
- package/packages/shared-testing/vitest.config.ts +3 -0
- package/packages/shared-tool-kit/CHANGELOG.md +20 -0
- package/packages/shared-tool-kit/package.json +47 -0
- package/packages/shared-tool-kit/src/__tests__/factory.test.ts +103 -0
- package/packages/shared-tool-kit/src/__tests__/mock-tool.test.ts +95 -0
- package/packages/shared-tool-kit/src/factory.ts +126 -0
- package/packages/shared-tool-kit/src/index.ts +32 -0
- package/packages/shared-tool-kit/src/testing/index.ts +84 -0
- package/packages/shared-tool-kit/tsconfig.build.json +15 -0
- package/packages/shared-tool-kit/tsconfig.json +9 -0
- package/packages/shared-tool-kit/tsup.config.ts +21 -0
- package/pnpm-workspace.yaml +11070 -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/tsconfig.base.json +9 -0
- package/tsconfig.build.json +15 -0
- package/tsconfig.json +9 -0
- package/tsconfig.paths.json +50 -0
- package/tsconfig.tools.json +18 -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 +104 -0
- package/vitest.config.ts +48 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform service permissions
|
|
3
|
+
*/
|
|
4
|
+
export interface PlatformPermissions {
|
|
5
|
+
/** LLM access */
|
|
6
|
+
llm?: boolean | { models?: string[] };
|
|
7
|
+
/** Vector store access */
|
|
8
|
+
vectorStore?: boolean | { collections?: string[] };
|
|
9
|
+
/** Cache access */
|
|
10
|
+
cache?: boolean | string[]; // true = all, string[] = namespaces
|
|
11
|
+
/**
|
|
12
|
+
* Storage access (file/blob storage)
|
|
13
|
+
*
|
|
14
|
+
* **Formats:**
|
|
15
|
+
* - `true` - full access (all read/write)
|
|
16
|
+
* - `false` - no access
|
|
17
|
+
* - `string[]` - path prefixes (both read + write)
|
|
18
|
+
* - `{ read?: string[]; write?: string[] }` - granular read/write
|
|
19
|
+
*
|
|
20
|
+
* **Examples:**
|
|
21
|
+
* ```typescript
|
|
22
|
+
* storage: true // Full access
|
|
23
|
+
* storage: ['.kb/artifacts/**'] // Read + write .kb/artifacts
|
|
24
|
+
* storage: { read: ['**'], write: ['.kb/artifacts/**'] } // Read all, write only .kb/artifacts
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
storage?: boolean | string[] | { read?: string[]; write?: string[] };
|
|
28
|
+
/**
|
|
29
|
+
* Database access (SQL, Document, KV, TimeSeries)
|
|
30
|
+
*
|
|
31
|
+
* **Formats:**
|
|
32
|
+
* - `true` - full access to all databases
|
|
33
|
+
* - `false` - no access
|
|
34
|
+
* - Granular per database type:
|
|
35
|
+
* - `sql?: { tables?: string[] }` - SQL table access
|
|
36
|
+
* - `document?: { collections?: string[] }` - Document collection access
|
|
37
|
+
* - `kv?: { prefixes?: string[] }` - KV key prefix access
|
|
38
|
+
* - `timeseries?: { metrics?: string[] }` - TimeSeries metric access
|
|
39
|
+
*
|
|
40
|
+
* **Examples:**
|
|
41
|
+
* ```typescript
|
|
42
|
+
* database: true // Full access to all databases
|
|
43
|
+
* database: {
|
|
44
|
+
* sql: { tables: ['incidents', 'audit_log'] },
|
|
45
|
+
* document: { collections: ['workflows'] }
|
|
46
|
+
* }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
database?:
|
|
50
|
+
| boolean
|
|
51
|
+
| {
|
|
52
|
+
sql?: boolean | { tables?: string[] };
|
|
53
|
+
document?: boolean | { collections?: string[] };
|
|
54
|
+
kv?: boolean | { prefixes?: string[] };
|
|
55
|
+
timeseries?: boolean | { metrics?: string[] };
|
|
56
|
+
};
|
|
57
|
+
/** Analytics access */
|
|
58
|
+
analytics?: boolean;
|
|
59
|
+
/** Embeddings access */
|
|
60
|
+
embeddings?: boolean;
|
|
61
|
+
/** Event bus access */
|
|
62
|
+
eventBus?: boolean | { publish?: string[]; subscribe?: string[] };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Permission specification for preset building (declarative format)
|
|
67
|
+
* This is the format used when defining presets and combining them.
|
|
68
|
+
*
|
|
69
|
+
* Design:
|
|
70
|
+
* - Presets declare what plugin WANTS (allow-list only)
|
|
71
|
+
* - No deny - platform enforces hardcoded security patterns
|
|
72
|
+
*/
|
|
73
|
+
export interface PermissionSpec {
|
|
74
|
+
/** File system permissions */
|
|
75
|
+
fs?: {
|
|
76
|
+
mode?: 'read' | 'readWrite';
|
|
77
|
+
allow?: string[];
|
|
78
|
+
};
|
|
79
|
+
/** Environment variable permissions */
|
|
80
|
+
env?: {
|
|
81
|
+
read?: string[];
|
|
82
|
+
};
|
|
83
|
+
/** Network permissions */
|
|
84
|
+
network?: {
|
|
85
|
+
fetch?: string[];
|
|
86
|
+
};
|
|
87
|
+
/** Platform service permissions */
|
|
88
|
+
platform?: PlatformPermissions;
|
|
89
|
+
/** Shell execution permissions */
|
|
90
|
+
shell?: {
|
|
91
|
+
allow?: string[];
|
|
92
|
+
};
|
|
93
|
+
/** Resource quotas */
|
|
94
|
+
quotas?: {
|
|
95
|
+
timeoutMs?: number;
|
|
96
|
+
memoryMb?: number;
|
|
97
|
+
cpuMs?: number;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Runtime permission specification (explicit format)
|
|
103
|
+
* This is the format used by plugin-runtime fs-shim.
|
|
104
|
+
* Matches PermissionSpec from @kb-labs/plugin-contracts.
|
|
105
|
+
*/
|
|
106
|
+
export interface RuntimePermissionSpec {
|
|
107
|
+
/** File system permissions - explicit read/write lists */
|
|
108
|
+
fs?: {
|
|
109
|
+
read?: string[];
|
|
110
|
+
write?: string[];
|
|
111
|
+
};
|
|
112
|
+
/** Environment variable permissions */
|
|
113
|
+
env?: {
|
|
114
|
+
read?: string[];
|
|
115
|
+
};
|
|
116
|
+
/** Network permissions */
|
|
117
|
+
network?: {
|
|
118
|
+
fetch?: string[];
|
|
119
|
+
};
|
|
120
|
+
/** Platform service permissions */
|
|
121
|
+
platform?: PlatformPermissions;
|
|
122
|
+
/** Shell execution permissions */
|
|
123
|
+
shell?: {
|
|
124
|
+
allow?: string[];
|
|
125
|
+
};
|
|
126
|
+
/** Resource quotas */
|
|
127
|
+
quotas?: {
|
|
128
|
+
timeoutMs?: number;
|
|
129
|
+
memoryMb?: number;
|
|
130
|
+
cpuMs?: number;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* A preset is a named permission configuration
|
|
136
|
+
*/
|
|
137
|
+
export interface PermissionPreset {
|
|
138
|
+
/** Unique preset identifier */
|
|
139
|
+
id: string;
|
|
140
|
+
/** Human-readable description */
|
|
141
|
+
description: string;
|
|
142
|
+
/** The permission spec this preset provides */
|
|
143
|
+
permissions: PermissionSpec;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Builder for combining multiple presets
|
|
148
|
+
*/
|
|
149
|
+
export interface PresetBuilder {
|
|
150
|
+
/** Add a preset to the combination */
|
|
151
|
+
with(preset: PermissionPreset | PermissionSpec): PresetBuilder;
|
|
152
|
+
/** Add environment variables to read */
|
|
153
|
+
withEnv(vars: string[]): PresetBuilder;
|
|
154
|
+
/** Add file system permissions */
|
|
155
|
+
withFs(fs: PermissionSpec['fs']): PresetBuilder;
|
|
156
|
+
/** Add network permissions */
|
|
157
|
+
withNetwork(network: PermissionSpec['network']): PresetBuilder;
|
|
158
|
+
/** Add platform service permissions */
|
|
159
|
+
withPlatform(platform: PlatformPermissions): PresetBuilder;
|
|
160
|
+
/** Add shell execution permissions */
|
|
161
|
+
withShell(shell: PermissionSpec['shell']): PresetBuilder;
|
|
162
|
+
/** Add quotas */
|
|
163
|
+
withQuotas(quotas: PermissionSpec['quotas']): PresetBuilder;
|
|
164
|
+
/**
|
|
165
|
+
* Add storage permissions.
|
|
166
|
+
*
|
|
167
|
+
* @param storage - Storage permissions (true, string[], or { read, write })
|
|
168
|
+
* @returns Builder for chaining
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* .withStorage({ read: ['**'], write: ['.kb/artifacts/**'] })
|
|
173
|
+
* .withStorage(['.kb/data/**']) // Both read + write
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
withStorage(storage: PlatformPermissions['storage']): PresetBuilder;
|
|
177
|
+
/**
|
|
178
|
+
* Add database permissions.
|
|
179
|
+
*
|
|
180
|
+
* @param database - Database permissions (true or granular per type)
|
|
181
|
+
* @returns Builder for chaining
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* .withDatabase({ sql: { tables: ['incidents'] } })
|
|
186
|
+
* .withDatabase(true) // Full database access
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
withDatabase(database: PlatformPermissions['database']): PresetBuilder;
|
|
190
|
+
/** Build the final permission spec (converts to runtime format) */
|
|
191
|
+
build(): RuntimePermissionSpec;
|
|
192
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## [1.1.0] - 2026-03-24
|
|
2
|
+
|
|
3
|
+
**5 packages** bumped to v1.1.0
|
|
4
|
+
|
|
5
|
+
| Package | Previous | Bump |
|
|
6
|
+
|---------|----------|------|
|
|
7
|
+
| `@kb-labs/perm-presets` | 1.0.0 | minor |
|
|
8
|
+
| `@kb-labs/shared-command-kit` | 1.0.0 | minor |
|
|
9
|
+
| `@kb-labs/shared-tool-kit` | 1.0.0 | minor |
|
|
10
|
+
| `@kb-labs/shared-cli-ui` | 1.0.0 | minor |
|
|
11
|
+
| `@kb-labs/shared-testing` | 1.0.0 | minor |
|
|
12
|
+
|
|
13
|
+
### ✨ New Features
|
|
14
|
+
|
|
15
|
+
- **config**: Introduces new configuration files for package management, allowing users to easily manage dependencies and ensure consistent environments across projects.
|
|
16
|
+
- **github**: Implements GitHub workflows for CI/CD, streamlining the development process and enabling quicker, more reliable software updates for users.
|
|
17
|
+
|
|
18
|
+
### 🐛 Bug Fixes
|
|
19
|
+
|
|
20
|
+
- **tests**: Improves code clarity by using an object type instead of a placeholder, which helps prevent misunderstandings in the codebase and enhances maintainability. Additionally, it resolves a linting issue that could lead to confusion when no value is returned from certain functions.
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# @kb-labs/shared-testing
|
|
2
|
+
|
|
3
|
+
Test utilities for KB Labs plugin development — mock builders, platform setup, test context.
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
Testing plugins is hard because of the **singleton gap**: composables like `useLLM()`, `useCache()`, `useLogger()` read from the global platform singleton (`@kb-labs/core-runtime`), but `createTestContext()` only populates `ctx.platform`. Code inside handlers that uses composables gets the uninitialized singleton instead of test mocks.
|
|
8
|
+
|
|
9
|
+
Additionally, existing mocks are noop functions — no `vi.fn()` spies, no way to configure specific responses, no call tracking.
|
|
10
|
+
|
|
11
|
+
## Solution
|
|
12
|
+
|
|
13
|
+
This package provides:
|
|
14
|
+
|
|
15
|
+
- **`setupTestPlatform()`** — bridges `ctx.platform` and the global singleton
|
|
16
|
+
- **`mockLLM()`** — fluent builder with prompt matching, streaming, error simulation, tool calls
|
|
17
|
+
- **`mockCache()`** — working in-memory cache with TTL and sorted sets
|
|
18
|
+
- **`mockStorage()`** — virtual filesystem on `Map<string, Buffer>`
|
|
19
|
+
- **`mockLogger()`** — logger with message recording
|
|
20
|
+
- **`createTestContext()`** — enhanced context factory that syncs everything automatically
|
|
21
|
+
|
|
22
|
+
All methods are `vi.fn()` spies — you get full assertion capabilities out of the box.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Available as workspace dependency:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@kb-labs/shared-testing": "link:../../../kb-labs-shared/packages/shared-testing"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or via SDK re-export:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { mockLLM, createTestContext } from '@kb-labs/sdk/testing';
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { createTestContext, mockLLM } from '@kb-labs/sdk/testing';
|
|
46
|
+
import { useLLM } from '@kb-labs/sdk';
|
|
47
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
48
|
+
|
|
49
|
+
describe('my handler', () => {
|
|
50
|
+
let cleanup: () => void;
|
|
51
|
+
|
|
52
|
+
afterEach(() => cleanup());
|
|
53
|
+
|
|
54
|
+
it('generates a commit message', async () => {
|
|
55
|
+
const llm = mockLLM()
|
|
56
|
+
.onComplete('commit').respondWith('feat: add login page')
|
|
57
|
+
.onAnyComplete().respondWith('ok');
|
|
58
|
+
|
|
59
|
+
const { ctx, cleanup: c } = createTestContext({ platform: { llm } });
|
|
60
|
+
cleanup = c;
|
|
61
|
+
|
|
62
|
+
// Both work — same mock instance:
|
|
63
|
+
expect(ctx.platform.llm).toBe(llm);
|
|
64
|
+
expect(useLLM()).toBe(llm); // singleton gap solved!
|
|
65
|
+
|
|
66
|
+
const res = await ctx.platform.llm.complete('Generate commit message');
|
|
67
|
+
expect(res.content).toBe('feat: add login page');
|
|
68
|
+
expect(llm.complete).toHaveBeenCalledOnce();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## API Reference
|
|
74
|
+
|
|
75
|
+
### `setupTestPlatform(options)`
|
|
76
|
+
|
|
77
|
+
Sets mock adapters into the global platform singleton. Call `cleanup()` in `afterEach()`.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { setupTestPlatform, mockLLM, mockCache } from '@kb-labs/shared-testing';
|
|
81
|
+
|
|
82
|
+
const { platform, cleanup } = setupTestPlatform({
|
|
83
|
+
llm: mockLLM(),
|
|
84
|
+
cache: mockCache(),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Now useLLM() and useCache() return these mocks
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Options:** `llm`, `cache`, `embeddings`, `vectorStore`, `storage`, `analytics`, `logger`, `eventBus` — all optional.
|
|
91
|
+
|
|
92
|
+
### `mockLLM()`
|
|
93
|
+
|
|
94
|
+
Fluent builder that creates an `ILLM` instance with `vi.fn()` spies and call tracking.
|
|
95
|
+
|
|
96
|
+
#### Prompt matching
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const llm = mockLLM()
|
|
100
|
+
// String match (substring)
|
|
101
|
+
.onComplete('commit').respondWith('feat: add feature')
|
|
102
|
+
// Regex match
|
|
103
|
+
.onComplete(/explain/i).respondWith('This code does X')
|
|
104
|
+
// Function match
|
|
105
|
+
.onComplete(p => p.length > 100).respondWith('Long prompt handled')
|
|
106
|
+
// Default fallback
|
|
107
|
+
.onAnyComplete().respondWith('default answer');
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Dynamic responses
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const llm = mockLLM()
|
|
114
|
+
.onAnyComplete().respondWith(prompt => `Echo: ${prompt}`);
|
|
115
|
+
|
|
116
|
+
// Or return a full LLMResponse object
|
|
117
|
+
const llm = mockLLM()
|
|
118
|
+
.onAnyComplete().respondWith({
|
|
119
|
+
content: 'hello',
|
|
120
|
+
model: 'gpt-4',
|
|
121
|
+
usage: { promptTokens: 10, completionTokens: 5 },
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### Streaming
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const llm = mockLLM().streaming(['chunk1', ' ', 'chunk2']);
|
|
129
|
+
|
|
130
|
+
for await (const chunk of llm.stream('test')) {
|
|
131
|
+
console.log(chunk); // 'chunk1', ' ', 'chunk2'
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### Error simulation
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const llm = mockLLM().failing(new Error('rate limit exceeded'));
|
|
139
|
+
|
|
140
|
+
await llm.complete('test'); // throws Error('rate limit exceeded')
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### Tool calling
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const llm = mockLLM().withToolCalls([
|
|
147
|
+
{ id: 'call-1', name: 'search', input: { query: 'test' } },
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
const res = await llm.chatWithTools!(messages, { tools });
|
|
151
|
+
expect(res.toolCalls).toHaveLength(1);
|
|
152
|
+
expect(res.toolCalls![0].name).toBe('search');
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### Call tracking
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
const llm = mockLLM();
|
|
159
|
+
await llm.complete('hello');
|
|
160
|
+
await llm.complete('world');
|
|
161
|
+
|
|
162
|
+
// vi.fn() assertions
|
|
163
|
+
expect(llm.complete).toHaveBeenCalledTimes(2);
|
|
164
|
+
expect(llm.complete).toHaveBeenCalledWith('hello');
|
|
165
|
+
|
|
166
|
+
// Structured call history
|
|
167
|
+
expect(llm.calls).toHaveLength(2);
|
|
168
|
+
expect(llm.calls[0].prompt).toBe('hello');
|
|
169
|
+
expect(llm.calls[0].response.content).toBe('mock response');
|
|
170
|
+
expect(llm.lastCall?.prompt).toBe('world');
|
|
171
|
+
|
|
172
|
+
// Tool call history
|
|
173
|
+
expect(llm.toolCalls).toHaveLength(0);
|
|
174
|
+
|
|
175
|
+
// Reset everything
|
|
176
|
+
llm.resetCalls();
|
|
177
|
+
expect(llm.calls).toHaveLength(0);
|
|
178
|
+
expect(llm.complete).not.toHaveBeenCalled();
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `mockCache()`
|
|
182
|
+
|
|
183
|
+
Working in-memory cache with TTL support and sorted sets. All methods are `vi.fn()` spies.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const cache = mockCache();
|
|
187
|
+
|
|
188
|
+
await cache.set('key', { data: 123 }, 5000); // TTL: 5 seconds
|
|
189
|
+
const val = await cache.get('key');
|
|
190
|
+
expect(val).toEqual({ data: 123 });
|
|
191
|
+
|
|
192
|
+
// Sorted sets
|
|
193
|
+
await cache.zadd('scores', 100, 'alice');
|
|
194
|
+
await cache.zadd('scores', 200, 'bob');
|
|
195
|
+
const top = await cache.zrangebyscore('scores', 0, 150);
|
|
196
|
+
expect(top).toEqual(['alice']);
|
|
197
|
+
|
|
198
|
+
// Spy assertions
|
|
199
|
+
expect(cache.set).toHaveBeenCalledWith('key', { data: 123 }, 5000);
|
|
200
|
+
|
|
201
|
+
// Direct state access
|
|
202
|
+
expect(cache.data.size).toBe(1);
|
|
203
|
+
|
|
204
|
+
// Reset
|
|
205
|
+
cache.reset();
|
|
206
|
+
expect(cache.data.size).toBe(0);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### `mockStorage()`
|
|
210
|
+
|
|
211
|
+
Virtual filesystem backed by `Map<string, Buffer>`.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// Initialize with files
|
|
215
|
+
const storage = mockStorage({
|
|
216
|
+
'config.json': '{"key": "value"}',
|
|
217
|
+
'data.bin': Buffer.from([0x01, 0x02]),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const content = await storage.read('config.json');
|
|
221
|
+
expect(content).toBe('{"key": "value"}');
|
|
222
|
+
|
|
223
|
+
await storage.write('new.txt', 'hello');
|
|
224
|
+
expect(await storage.exists('new.txt')).toBe(true);
|
|
225
|
+
|
|
226
|
+
const files = await storage.list('/');
|
|
227
|
+
expect(files).toContain('new.txt');
|
|
228
|
+
|
|
229
|
+
// Direct state access
|
|
230
|
+
expect(storage.files.size).toBe(3);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### `mockLogger()`
|
|
234
|
+
|
|
235
|
+
Logger with message recording and `vi.fn()` spies.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const logger = mockLogger();
|
|
239
|
+
|
|
240
|
+
logger.info('Server started', { port: 3000 });
|
|
241
|
+
logger.error('Connection failed', new Error('timeout'));
|
|
242
|
+
logger.warn('Deprecated API');
|
|
243
|
+
|
|
244
|
+
// Message history
|
|
245
|
+
expect(logger.messages).toHaveLength(3);
|
|
246
|
+
expect(logger.messages[0]).toEqual({
|
|
247
|
+
level: 'info',
|
|
248
|
+
message: 'Server started',
|
|
249
|
+
meta: { port: 3000 },
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Spy assertions
|
|
253
|
+
expect(logger.info).toHaveBeenCalledWith('Server started', { port: 3000 });
|
|
254
|
+
expect(logger.error).toHaveBeenCalledWith('Connection failed', expect.any(Error));
|
|
255
|
+
|
|
256
|
+
// Child loggers share the same messages array
|
|
257
|
+
const child = logger.child({ service: 'db' });
|
|
258
|
+
child.info('Connected');
|
|
259
|
+
expect(logger.messages).toHaveLength(4);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### `testCommand(handler, options)`
|
|
263
|
+
|
|
264
|
+
Single-function test runner for plugin command handlers. No boilerplate — import your handler, call `testCommand()`, assert on result.
|
|
265
|
+
|
|
266
|
+
Works with any handler type: `CommandHandlerV3` (from `defineCommand`), `RouteHandler` (from `defineRoute`), `Handler` (from `defineHandler`), or any object with `execute(ctx, input)`.
|
|
267
|
+
|
|
268
|
+
#### CLI command
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { testCommand } from '@kb-labs/sdk/testing';
|
|
272
|
+
import handler from '../src/commands/greet.js';
|
|
273
|
+
|
|
274
|
+
it('greets the user', async () => {
|
|
275
|
+
const result = await testCommand(handler, {
|
|
276
|
+
flags: { name: 'Alice' },
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
expect(result.exitCode).toBe(0);
|
|
280
|
+
expect(result.result).toEqual({ message: 'Hello, Alice!' });
|
|
281
|
+
expect(result.ui.success).toHaveBeenCalledWith('Hello, Alice!');
|
|
282
|
+
result.cleanup();
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### With LLM mock
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { testCommand, mockLLM } from '@kb-labs/sdk/testing';
|
|
290
|
+
import handler from '../src/commands/analyze.js';
|
|
291
|
+
|
|
292
|
+
it('analyzes file with LLM', async () => {
|
|
293
|
+
const llm = mockLLM().onAnyComplete().respondWith('looks good');
|
|
294
|
+
|
|
295
|
+
const result = await testCommand(handler, {
|
|
296
|
+
flags: { file: 'index.ts' },
|
|
297
|
+
platform: { llm },
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
expect(result.exitCode).toBe(0);
|
|
301
|
+
expect(llm.complete).toHaveBeenCalled();
|
|
302
|
+
result.cleanup();
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
#### REST handler
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { testCommand } from '@kb-labs/sdk/testing';
|
|
310
|
+
import handler from '../src/routes/create-plan.js';
|
|
311
|
+
|
|
312
|
+
it('creates a plan', async () => {
|
|
313
|
+
const result = await testCommand(handler, {
|
|
314
|
+
host: 'rest',
|
|
315
|
+
body: { name: 'v2.0' },
|
|
316
|
+
query: { workspace: 'root' },
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
expect(result.result).toMatchObject({ name: 'v2.0' });
|
|
320
|
+
result.cleanup();
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### Raw input (custom shape)
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const result = await testCommand(handler, {
|
|
328
|
+
input: { custom: 'data', items: [1, 2, 3] },
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Options:**
|
|
333
|
+
|
|
334
|
+
| Option | Type | Default | Description |
|
|
335
|
+
|--------|------|---------|-------------|
|
|
336
|
+
| `flags` | `Record<string, unknown>` | `{}` | CLI flags (builds `{ flags, argv }` input) |
|
|
337
|
+
| `argv` | `string[]` | `[]` | CLI positional arguments |
|
|
338
|
+
| `query` | `Record<string, unknown>` | — | REST query params (builds `{ query, body, params }` input) |
|
|
339
|
+
| `body` | `unknown` | — | REST request body |
|
|
340
|
+
| `params` | `Record<string, unknown>` | — | REST route params |
|
|
341
|
+
| `input` | `unknown` | — | Raw input (overrides flags/query/body) |
|
|
342
|
+
| `host` | `'cli' \| 'rest' \| 'workflow' \| 'webhook'` | `'cli'` | Host type |
|
|
343
|
+
| `config` | `TConfig` | `undefined` | Plugin config (`ctx.config`) |
|
|
344
|
+
| `cwd` | `string` | `process.cwd()` | Working directory |
|
|
345
|
+
| `tenantId` | `string` | `undefined` | Tenant identifier |
|
|
346
|
+
| `signal` | `AbortSignal` | `undefined` | Abort signal |
|
|
347
|
+
| `platform` | `Partial<PlatformServices>` | all mocks | Override platform services |
|
|
348
|
+
| `ui` | `Partial<UIFacade>` | all vi.fn() | Override UI methods |
|
|
349
|
+
| `syncSingleton` | `boolean` | `true` | Sync to global singleton |
|
|
350
|
+
|
|
351
|
+
**Result (`TestCommandResult`):**
|
|
352
|
+
|
|
353
|
+
| Field | Type | Description |
|
|
354
|
+
|-------|------|-------------|
|
|
355
|
+
| `exitCode` | `number` | Exit code (0 = success), extracted from `CommandResult` or 0 for raw data |
|
|
356
|
+
| `result` | `TResult \| undefined` | Data from `CommandResult.result` or raw return value |
|
|
357
|
+
| `meta` | `Record<string, unknown> \| undefined` | Custom metadata from `CommandResult.meta` |
|
|
358
|
+
| `raw` | `unknown` | Unprocessed return value from `handler.execute()` |
|
|
359
|
+
| `ui` | `UIFacade` | UI facade with `vi.fn()` spies on all methods |
|
|
360
|
+
| `ctx` | `PluginContextV3` | Full context passed to the handler |
|
|
361
|
+
| `cleanup` | `() => void` | Call in `afterEach()` to reset global singleton |
|
|
362
|
+
|
|
363
|
+
### `createTestContext(options)`
|
|
364
|
+
|
|
365
|
+
Enhanced test context factory. Replaces the legacy SDK `createTestContext()`.
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
const { ctx, cleanup } = createTestContext({
|
|
369
|
+
pluginId: 'my-plugin',
|
|
370
|
+
host: 'cli',
|
|
371
|
+
config: { apiKey: 'test' },
|
|
372
|
+
platform: {
|
|
373
|
+
llm: mockLLM().onAnyComplete().respondWith('ok'),
|
|
374
|
+
cache: mockCache(),
|
|
375
|
+
},
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// ctx.platform has the mocks
|
|
379
|
+
await ctx.platform.llm.complete('test');
|
|
380
|
+
|
|
381
|
+
// Global singleton also has them (syncSingleton defaults to true)
|
|
382
|
+
const llm = useLLM();
|
|
383
|
+
expect(llm).toBe(ctx.platform.llm);
|
|
384
|
+
|
|
385
|
+
// UI methods are vi.fn() spies too
|
|
386
|
+
ctx.ui.info('hello');
|
|
387
|
+
expect(ctx.ui.info).toHaveBeenCalledWith('hello');
|
|
388
|
+
|
|
389
|
+
// Runtime, API, trace — all mocked
|
|
390
|
+
await ctx.runtime.fs.readFile('test.txt');
|
|
391
|
+
expect(ctx.runtime.fs.readFile).toHaveBeenCalled();
|
|
392
|
+
|
|
393
|
+
// ALWAYS call cleanup in afterEach()
|
|
394
|
+
cleanup();
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Options:**
|
|
398
|
+
|
|
399
|
+
| Option | Type | Default | Description |
|
|
400
|
+
|--------|------|---------|-------------|
|
|
401
|
+
| `pluginId` | `string` | `'test-plugin'` | Plugin identifier |
|
|
402
|
+
| `pluginVersion` | `string` | `'0.0.0'` | Plugin version |
|
|
403
|
+
| `host` | `'cli' \| 'rest' \| 'workflow' \| 'webhook'` | `'cli'` | Host type |
|
|
404
|
+
| `hostContext` | `HostContext` | auto-generated | Override host context |
|
|
405
|
+
| `config` | `unknown` | `undefined` | Plugin config |
|
|
406
|
+
| `cwd` | `string` | `process.cwd()` | Working directory |
|
|
407
|
+
| `outdir` | `string` | `{cwd}/.kb/output` | Output directory |
|
|
408
|
+
| `tenantId` | `string` | `undefined` | Tenant identifier |
|
|
409
|
+
| `signal` | `AbortSignal` | `undefined` | Abort signal |
|
|
410
|
+
| `platform` | `Partial<PlatformServices>` | all mocks | Override platform adapters |
|
|
411
|
+
| `ui` | `Partial<UIFacade>` | all vi.fn() | Override UI methods |
|
|
412
|
+
| `syncSingleton` | `boolean` | `true` | Sync to global singleton |
|
|
413
|
+
|
|
414
|
+
## Architecture
|
|
415
|
+
|
|
416
|
+
```
|
|
417
|
+
@kb-labs/shared-testing
|
|
418
|
+
├── setup-platform.ts ← resetPlatform() + setAdapter() for each mock
|
|
419
|
+
├── mock-llm.ts ← Proxy-based builder + ILLM instance
|
|
420
|
+
├── mock-cache.ts ← In-memory Map with TTL + sorted sets
|
|
421
|
+
├── mock-storage.ts ← Virtual FS on Map<string, Buffer>
|
|
422
|
+
├── mock-logger.ts ← Message recording + child logger support
|
|
423
|
+
├── create-test-context.ts ← PluginContextV3 factory + singleton sync
|
|
424
|
+
├── test-command.ts ← testCommand() — single-function handler runner
|
|
425
|
+
└── index.ts ← Barrel exports
|
|
426
|
+
|
|
427
|
+
@kb-labs/sdk/testing ← Thin re-export layer
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The key insight: `setupTestPlatform()` calls `resetPlatform()` (clears the global singleton) then `platform.setAdapter()` for each provided mock. This ensures `useLLM()`, `useCache()`, etc. return the test mocks. `createTestContext()` calls `setupTestPlatform()` automatically when `syncSingleton: true` (the default).
|