@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,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/shared-command-kit/flags/validate
|
|
3
|
+
* Validate flags against schema
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
FlagValidationError,
|
|
8
|
+
type FlagSchema,
|
|
9
|
+
type FlagSchemaDefinition,
|
|
10
|
+
type SafeValidationResult,
|
|
11
|
+
} from './types';
|
|
12
|
+
import type { InferFlags } from './define';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse and coerce flag value based on type
|
|
16
|
+
*/
|
|
17
|
+
function coerceValue(value: unknown, type: FlagSchema['type']): unknown {
|
|
18
|
+
if (value === undefined || value === null) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
switch (type) {
|
|
23
|
+
case 'boolean':
|
|
24
|
+
if (typeof value === 'boolean') {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === 'string') {
|
|
28
|
+
const lower = value.toLowerCase().trim();
|
|
29
|
+
if (lower === 'true' || lower === '1' || lower === 'yes') {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (lower === 'false' || lower === '0' || lower === 'no' || lower === '') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return Boolean(value);
|
|
37
|
+
|
|
38
|
+
case 'number':
|
|
39
|
+
if (typeof value === 'number') {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value === 'string') {
|
|
43
|
+
const parsed = Number(value);
|
|
44
|
+
if (!Number.isNaN(parsed) && Number.isFinite(parsed)) {
|
|
45
|
+
return parsed;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return Number(value);
|
|
49
|
+
|
|
50
|
+
case 'array':
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === 'string') {
|
|
55
|
+
return value.split(',').map((item) => item.trim());
|
|
56
|
+
}
|
|
57
|
+
return [value];
|
|
58
|
+
|
|
59
|
+
case 'string':
|
|
60
|
+
return String(value);
|
|
61
|
+
|
|
62
|
+
default:
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Validate a single flag value against its schema
|
|
69
|
+
*/
|
|
70
|
+
async function validateFlag(
|
|
71
|
+
flagName: string,
|
|
72
|
+
value: unknown,
|
|
73
|
+
schema: FlagSchema
|
|
74
|
+
): Promise<true | string> {
|
|
75
|
+
const { type, required } = schema;
|
|
76
|
+
|
|
77
|
+
// Type-specific properties (using type guards)
|
|
78
|
+
const choices = 'choices' in schema ? schema.choices : undefined;
|
|
79
|
+
const pattern = 'pattern' in schema ? schema.pattern : undefined;
|
|
80
|
+
const min = 'min' in schema ? schema.min : undefined;
|
|
81
|
+
const max = 'max' in schema ? schema.max : undefined;
|
|
82
|
+
const validate = 'validate' in schema ? schema.validate : undefined;
|
|
83
|
+
const minLength = 'minLength' in schema ? schema.minLength : undefined;
|
|
84
|
+
const maxLength = 'maxLength' in schema ? schema.maxLength : undefined;
|
|
85
|
+
const items = 'items' in schema ? schema.items : undefined;
|
|
86
|
+
const transform = 'transform' in schema ? schema.transform : undefined;
|
|
87
|
+
|
|
88
|
+
// Check required
|
|
89
|
+
if (required && (value === undefined || value === null || value === '')) {
|
|
90
|
+
return `Flag --${flagName} is required`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Skip validation if value is undefined and not required
|
|
94
|
+
if (value === undefined || value === null) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Coerce value to correct type
|
|
99
|
+
const coerced = coerceValue(value, type);
|
|
100
|
+
|
|
101
|
+
// Type-specific validation
|
|
102
|
+
switch (type) {
|
|
103
|
+
case 'boolean':
|
|
104
|
+
if (typeof coerced !== 'boolean') {
|
|
105
|
+
return `Flag --${flagName} must be a boolean`;
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case 'string':
|
|
110
|
+
if (typeof coerced !== 'string') {
|
|
111
|
+
return `Flag --${flagName} must be a string`;
|
|
112
|
+
}
|
|
113
|
+
if (minLength !== undefined && coerced.length < minLength) {
|
|
114
|
+
return `Flag --${flagName} must be at least ${minLength} characters long`;
|
|
115
|
+
}
|
|
116
|
+
if (maxLength !== undefined && coerced.length > maxLength) {
|
|
117
|
+
return `Flag --${flagName} must be at most ${maxLength} characters long`;
|
|
118
|
+
}
|
|
119
|
+
if (choices && !choices.includes(coerced)) {
|
|
120
|
+
return `Flag --${flagName} must be one of: ${choices.join(', ')} (got: ${coerced})`;
|
|
121
|
+
}
|
|
122
|
+
if (pattern && !pattern.test(coerced)) {
|
|
123
|
+
return `Flag --${flagName} must match pattern ${pattern.toString()}`;
|
|
124
|
+
}
|
|
125
|
+
if (validate) {
|
|
126
|
+
const stringValidate = validate as (value: string) => true | string | Promise<true | string>;
|
|
127
|
+
const result = await stringValidate(coerced as string);
|
|
128
|
+
if (result !== true) {
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
case 'number':
|
|
135
|
+
if (typeof coerced !== 'number' || !Number.isFinite(coerced)) {
|
|
136
|
+
return `Flag --${flagName} must be a number`;
|
|
137
|
+
}
|
|
138
|
+
if (min !== undefined && coerced < min) {
|
|
139
|
+
return `Flag --${flagName} must be >= ${min} (got: ${coerced})`;
|
|
140
|
+
}
|
|
141
|
+
if (max !== undefined && coerced > max) {
|
|
142
|
+
return `Flag --${flagName} must be <= ${max} (got: ${coerced})`;
|
|
143
|
+
}
|
|
144
|
+
if (validate) {
|
|
145
|
+
const numberValidate = validate as (value: number) => true | string | Promise<true | string>;
|
|
146
|
+
const result = await numberValidate(coerced as number);
|
|
147
|
+
if (result !== true) {
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case 'array':
|
|
154
|
+
if (!Array.isArray(coerced)) {
|
|
155
|
+
return `Flag --${flagName} must be an array`;
|
|
156
|
+
}
|
|
157
|
+
if (minLength !== undefined && coerced.length < minLength) {
|
|
158
|
+
return `Flag --${flagName} must have at least ${minLength} items`;
|
|
159
|
+
}
|
|
160
|
+
if (maxLength !== undefined && coerced.length > maxLength) {
|
|
161
|
+
return `Flag --${flagName} must have at most ${maxLength} items`;
|
|
162
|
+
}
|
|
163
|
+
if (items) {
|
|
164
|
+
for (let i = 0; i < coerced.length; i++) {
|
|
165
|
+
const item = coerced[i];
|
|
166
|
+
if (items === 'string' && typeof item !== 'string') {
|
|
167
|
+
return `Flag --${flagName}[${i}] must be a string`;
|
|
168
|
+
}
|
|
169
|
+
if (items === 'number' && typeof item !== 'number') {
|
|
170
|
+
return `Flag --${flagName}[${i}] must be a number`;
|
|
171
|
+
}
|
|
172
|
+
if (items === 'boolean' && typeof item !== 'boolean') {
|
|
173
|
+
return `Flag --${flagName}[${i}] must be a boolean`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Validate flags against schema and return typed result
|
|
185
|
+
*
|
|
186
|
+
* @throws {FlagValidationError} If validation fails and safe mode is not used
|
|
187
|
+
*/
|
|
188
|
+
export async function validateFlags<T extends FlagSchemaDefinition>(
|
|
189
|
+
rawFlags: Record<string, unknown>,
|
|
190
|
+
schemaDefinition: { schema: T } | T
|
|
191
|
+
): Promise<InferFlags<T>> {
|
|
192
|
+
const schema = 'schema' in schemaDefinition ? schemaDefinition.schema : schemaDefinition;
|
|
193
|
+
const errors: Array<{ flag: string; message: string; value?: unknown }> = [];
|
|
194
|
+
const result: Record<string, unknown> = {};
|
|
195
|
+
const flagValues: Record<string, unknown> = { ...rawFlags };
|
|
196
|
+
|
|
197
|
+
// First pass: apply implies
|
|
198
|
+
for (const [flagName, flagSchema] of Object.entries(schema)) {
|
|
199
|
+
const value = flagValues[flagName];
|
|
200
|
+
if (value !== undefined && value !== null && value !== false && value !== '') {
|
|
201
|
+
const implies = flagSchema.implies;
|
|
202
|
+
if (implies) {
|
|
203
|
+
for (const implied of implies) {
|
|
204
|
+
if (Array.isArray(implied) && implied.length === 2) {
|
|
205
|
+
// [flagName, value] format
|
|
206
|
+
const [impliedFlag, impliedValue] = implied;
|
|
207
|
+
if (flagValues[impliedFlag] === undefined) {
|
|
208
|
+
flagValues[impliedFlag] = impliedValue;
|
|
209
|
+
}
|
|
210
|
+
} else if (typeof implied === 'string') {
|
|
211
|
+
// Just flag name - set to true
|
|
212
|
+
if (flagValues[implied] === undefined) {
|
|
213
|
+
flagValues[implied] = true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Second pass: validate each flag
|
|
222
|
+
for (const [flagName, flagSchema] of Object.entries(schema)) {
|
|
223
|
+
const value = flagValues[flagName];
|
|
224
|
+
const schemaWithName: FlagSchema = { ...flagSchema, name: flagName };
|
|
225
|
+
|
|
226
|
+
// Check conflicts
|
|
227
|
+
const conflicts = flagSchema.conflicts;
|
|
228
|
+
if (conflicts && value !== undefined && value !== null && value !== false && value !== '') {
|
|
229
|
+
for (const conflictingFlag of conflicts) {
|
|
230
|
+
const conflictingValue = flagValues[conflictingFlag];
|
|
231
|
+
if (conflictingValue !== undefined && conflictingValue !== null && conflictingValue !== false && conflictingValue !== '') {
|
|
232
|
+
errors.push({
|
|
233
|
+
flag: flagName,
|
|
234
|
+
message: `Flag --${flagName} conflicts with --${conflictingFlag}`,
|
|
235
|
+
value,
|
|
236
|
+
});
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (errors.some(e => e.flag === flagName)) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Check dependsOn
|
|
246
|
+
const dependsOn = flagSchema.dependsOn;
|
|
247
|
+
if (dependsOn && value !== undefined && value !== null && value !== '') {
|
|
248
|
+
for (const dependency of dependsOn) {
|
|
249
|
+
const dependencyValue = flagValues[dependency];
|
|
250
|
+
if (dependencyValue === undefined || dependencyValue === null || dependencyValue === '') {
|
|
251
|
+
errors.push({
|
|
252
|
+
flag: flagName,
|
|
253
|
+
message: `Flag --${flagName} depends on --${dependency}`,
|
|
254
|
+
value,
|
|
255
|
+
});
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (errors.some(e => e.flag === flagName)) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check required
|
|
265
|
+
if (flagSchema.required && (value === undefined || value === null || value === '')) {
|
|
266
|
+
errors.push({
|
|
267
|
+
flag: flagName,
|
|
268
|
+
message: `Flag --${flagName} is required`,
|
|
269
|
+
value,
|
|
270
|
+
});
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Apply default if value is missing
|
|
275
|
+
const finalValue = value !== undefined ? value : flagSchema.default;
|
|
276
|
+
|
|
277
|
+
// Skip validation if value is undefined and not required
|
|
278
|
+
if (finalValue === undefined && !flagSchema.required) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Validate flag
|
|
283
|
+
const validationResult = await validateFlag(flagName, finalValue, schemaWithName);
|
|
284
|
+
if (validationResult !== true) {
|
|
285
|
+
errors.push({
|
|
286
|
+
flag: flagName,
|
|
287
|
+
message: validationResult,
|
|
288
|
+
value: finalValue,
|
|
289
|
+
});
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Apply transform if present
|
|
294
|
+
let transformedValue = coerceValue(finalValue, flagSchema.type);
|
|
295
|
+
const transform = flagSchema.transform;
|
|
296
|
+
if (transform) {
|
|
297
|
+
transformedValue = await transform(transformedValue);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Store value
|
|
301
|
+
result[flagName] = transformedValue;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Check for unknown flags (optional - could be enabled via option)
|
|
305
|
+
// for (const flagName of Object.keys(rawFlags)) {
|
|
306
|
+
// if (!(flagName in schema)) {
|
|
307
|
+
// errors.push({
|
|
308
|
+
// flag: flagName,
|
|
309
|
+
// message: `Unknown flag: --${flagName}`,
|
|
310
|
+
// value: rawFlags[flagName],
|
|
311
|
+
// });
|
|
312
|
+
// }
|
|
313
|
+
// }
|
|
314
|
+
|
|
315
|
+
if (errors.length > 0) {
|
|
316
|
+
const firstError = errors[0]!;
|
|
317
|
+
throw new FlagValidationError(
|
|
318
|
+
firstError.flag,
|
|
319
|
+
firstError.message,
|
|
320
|
+
firstError.value,
|
|
321
|
+
schema as FlagSchemaDefinition // Pass schema for usage generation
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return result as InferFlags<T>;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Safe version of validateFlags that doesn't throw
|
|
330
|
+
*/
|
|
331
|
+
export async function validateFlagsSafe<T extends FlagSchemaDefinition>(
|
|
332
|
+
rawFlags: Record<string, unknown>,
|
|
333
|
+
schemaDefinition: { schema: T } | T
|
|
334
|
+
): Promise<SafeValidationResult<InferFlags<T>>> {
|
|
335
|
+
try {
|
|
336
|
+
const data = await validateFlags(rawFlags, schemaDefinition);
|
|
337
|
+
return {
|
|
338
|
+
success: true,
|
|
339
|
+
data,
|
|
340
|
+
errors: [],
|
|
341
|
+
};
|
|
342
|
+
} catch (error) {
|
|
343
|
+
if (error instanceof FlagValidationError) {
|
|
344
|
+
return {
|
|
345
|
+
success: false,
|
|
346
|
+
errors: [
|
|
347
|
+
{
|
|
348
|
+
flag: error.flag,
|
|
349
|
+
message: error.message,
|
|
350
|
+
value: error.value,
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/shared-command-kit/helpers/flags
|
|
3
|
+
* Helper functions for working with command flags (optional utilities)
|
|
4
|
+
*
|
|
5
|
+
* These functions are completely optional - you can access flags directly.
|
|
6
|
+
* They are provided for convenience when you need type-safe flag access.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get a flag value with type safety
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const name = getFlag<string>(flags, 'name'); // string | undefined
|
|
15
|
+
* const count = getFlag<number>(flags, 'count', 0); // number (with default)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function getFlag<T>(
|
|
19
|
+
flags: Record<string, unknown>,
|
|
20
|
+
name: string,
|
|
21
|
+
defaultValue?: T
|
|
22
|
+
): T | undefined {
|
|
23
|
+
const value = flags[name];
|
|
24
|
+
if (value === undefined) {
|
|
25
|
+
return defaultValue;
|
|
26
|
+
}
|
|
27
|
+
return value as T;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Require a flag value (throws if missing)
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const name = requireFlag<string>(flags, 'name'); // string (throws if missing)
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function requireFlag<T>(
|
|
39
|
+
flags: Record<string, unknown>,
|
|
40
|
+
name: string
|
|
41
|
+
): T {
|
|
42
|
+
const value = flags[name];
|
|
43
|
+
if (value === undefined) {
|
|
44
|
+
throw new Error(`Required flag '${name}' is missing`);
|
|
45
|
+
}
|
|
46
|
+
return value as T;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if a flag is present
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* if (hasFlag(flags, 'dry-run')) {
|
|
55
|
+
* // Flag is present
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function hasFlag(
|
|
60
|
+
flags: Record<string, unknown>,
|
|
61
|
+
name: string
|
|
62
|
+
): boolean {
|
|
63
|
+
return flags[name] !== undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get multiple flags at once
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const { name, count } = getFlags(flags, ['name', 'count']);
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function getFlags<T extends string[]>(
|
|
75
|
+
flags: Record<string, unknown>,
|
|
76
|
+
names: T
|
|
77
|
+
): Record<T[number], unknown> {
|
|
78
|
+
const result: Record<string, unknown> = {};
|
|
79
|
+
for (const name of names) {
|
|
80
|
+
result[name] = flags[name];
|
|
81
|
+
}
|
|
82
|
+
return result as Record<T[number], unknown>;
|
|
83
|
+
}
|
|
84
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/shared-command-kit/helpers
|
|
3
|
+
* Global platform access helpers
|
|
4
|
+
*
|
|
5
|
+
* Provides clean, type-safe access to platform services without context drilling.
|
|
6
|
+
* Similar to React hooks pattern, but for KB Labs platform.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { useLLM, useLogger, useAnalytics } from '@kb-labs/shared-command-kit/helpers';
|
|
11
|
+
*
|
|
12
|
+
* async handler(ctx, argv, flags) {
|
|
13
|
+
* const logger = useLogger();
|
|
14
|
+
* const llm = useLLM();
|
|
15
|
+
*
|
|
16
|
+
* await logger.info('Processing started');
|
|
17
|
+
*
|
|
18
|
+
* if (llm) {
|
|
19
|
+
* const result = await llm.complete('prompt');
|
|
20
|
+
* await logger.info('LLM result', { length: result.content.length });
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// Platform singleton
|
|
27
|
+
export { usePlatform, isPlatformConfigured } from './use-platform';
|
|
28
|
+
|
|
29
|
+
// Context types and helpers
|
|
30
|
+
export type { PluginContextV3 } from './context';
|
|
31
|
+
|
|
32
|
+
// Config access
|
|
33
|
+
export { useConfig } from './use-config';
|
|
34
|
+
|
|
35
|
+
// Core services
|
|
36
|
+
export { useLogger, useLoggerWithContext } from './use-logger.js';
|
|
37
|
+
export { useLLM, isLLMAvailable, getLLMTier, type LLMTier, type UseLLMOptions } from './use-llm.js';
|
|
38
|
+
export { useEmbeddings, isEmbeddingsAvailable } from './use-embeddings.js';
|
|
39
|
+
export { useVectorStore, isVectorStoreAvailable } from './use-vector-store.js';
|
|
40
|
+
export { useAnalytics, trackAnalyticsEvent } from './use-analytics.js';
|
|
41
|
+
export { useStorage } from './use-storage.js';
|
|
42
|
+
export { useCache, isCacheAvailable } from './use-cache.js';
|