@sparkleideas/shared 3.0.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +323 -0
  2. package/__tests__/hooks/bash-safety.test.ts +289 -0
  3. package/__tests__/hooks/file-organization.test.ts +335 -0
  4. package/__tests__/hooks/git-commit.test.ts +336 -0
  5. package/__tests__/hooks/index.ts +23 -0
  6. package/__tests__/hooks/session-hooks.test.ts +357 -0
  7. package/__tests__/hooks/task-hooks.test.ts +193 -0
  8. package/docs/EVENTS_IMPLEMENTATION_SUMMARY.md +388 -0
  9. package/docs/EVENTS_QUICK_REFERENCE.md +470 -0
  10. package/docs/EVENTS_README.md +352 -0
  11. package/package.json +39 -0
  12. package/src/core/config/defaults.ts +207 -0
  13. package/src/core/config/index.ts +15 -0
  14. package/src/core/config/loader.ts +271 -0
  15. package/src/core/config/schema.ts +188 -0
  16. package/src/core/config/validator.ts +209 -0
  17. package/src/core/event-bus.ts +236 -0
  18. package/src/core/index.ts +22 -0
  19. package/src/core/interfaces/agent.interface.ts +251 -0
  20. package/src/core/interfaces/coordinator.interface.ts +363 -0
  21. package/src/core/interfaces/event.interface.ts +267 -0
  22. package/src/core/interfaces/index.ts +19 -0
  23. package/src/core/interfaces/memory.interface.ts +332 -0
  24. package/src/core/interfaces/task.interface.ts +223 -0
  25. package/src/core/orchestrator/event-coordinator.ts +122 -0
  26. package/src/core/orchestrator/health-monitor.ts +214 -0
  27. package/src/core/orchestrator/index.ts +89 -0
  28. package/src/core/orchestrator/lifecycle-manager.ts +263 -0
  29. package/src/core/orchestrator/session-manager.ts +279 -0
  30. package/src/core/orchestrator/task-manager.ts +317 -0
  31. package/src/events/domain-events.ts +584 -0
  32. package/src/events/event-store.test.ts +387 -0
  33. package/src/events/event-store.ts +588 -0
  34. package/src/events/example-usage.ts +293 -0
  35. package/src/events/index.ts +90 -0
  36. package/src/events/projections.ts +561 -0
  37. package/src/events/state-reconstructor.ts +349 -0
  38. package/src/events.ts +367 -0
  39. package/src/hooks/INTEGRATION.md +658 -0
  40. package/src/hooks/README.md +532 -0
  41. package/src/hooks/example-usage.ts +499 -0
  42. package/src/hooks/executor.ts +379 -0
  43. package/src/hooks/hooks.test.ts +421 -0
  44. package/src/hooks/index.ts +131 -0
  45. package/src/hooks/registry.ts +333 -0
  46. package/src/hooks/safety/bash-safety.ts +604 -0
  47. package/src/hooks/safety/file-organization.ts +473 -0
  48. package/src/hooks/safety/git-commit.ts +623 -0
  49. package/src/hooks/safety/index.ts +46 -0
  50. package/src/hooks/session-hooks.ts +559 -0
  51. package/src/hooks/task-hooks.ts +513 -0
  52. package/src/hooks/types.ts +357 -0
  53. package/src/hooks/verify-exports.test.ts +125 -0
  54. package/src/index.ts +195 -0
  55. package/src/mcp/connection-pool.ts +438 -0
  56. package/src/mcp/index.ts +183 -0
  57. package/src/mcp/server.ts +774 -0
  58. package/src/mcp/session-manager.ts +428 -0
  59. package/src/mcp/tool-registry.ts +566 -0
  60. package/src/mcp/transport/http.ts +557 -0
  61. package/src/mcp/transport/index.ts +294 -0
  62. package/src/mcp/transport/stdio.ts +324 -0
  63. package/src/mcp/transport/websocket.ts +484 -0
  64. package/src/mcp/types.ts +565 -0
  65. package/src/plugin-interface.ts +663 -0
  66. package/src/plugin-loader.ts +638 -0
  67. package/src/plugin-registry.ts +604 -0
  68. package/src/plugins/index.ts +34 -0
  69. package/src/plugins/official/hive-mind-plugin.ts +330 -0
  70. package/src/plugins/official/index.ts +24 -0
  71. package/src/plugins/official/maestro-plugin.ts +508 -0
  72. package/src/plugins/types.ts +108 -0
  73. package/src/resilience/bulkhead.ts +277 -0
  74. package/src/resilience/circuit-breaker.ts +326 -0
  75. package/src/resilience/index.ts +26 -0
  76. package/src/resilience/rate-limiter.ts +420 -0
  77. package/src/resilience/retry.ts +224 -0
  78. package/src/security/index.ts +39 -0
  79. package/src/security/input-validation.ts +265 -0
  80. package/src/security/secure-random.ts +159 -0
  81. package/src/services/index.ts +16 -0
  82. package/src/services/v3-progress.service.ts +505 -0
  83. package/src/types/agent.types.ts +144 -0
  84. package/src/types/index.ts +22 -0
  85. package/src/types/mcp.types.ts +300 -0
  86. package/src/types/memory.types.ts +263 -0
  87. package/src/types/swarm.types.ts +255 -0
  88. package/src/types/task.types.ts +205 -0
  89. package/src/types.ts +367 -0
  90. package/src/utils/secure-logger.d.ts +69 -0
  91. package/src/utils/secure-logger.d.ts.map +1 -0
  92. package/src/utils/secure-logger.js +208 -0
  93. package/src/utils/secure-logger.js.map +1 -0
  94. package/src/utils/secure-logger.ts +257 -0
  95. package/tmp.json +0 -0
  96. package/tsconfig.json +9 -0
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Input Validation Utilities
3
+ *
4
+ * Secure input validation and sanitization.
5
+ *
6
+ * @module v3/shared/security/input-validation
7
+ */
8
+
9
+ /**
10
+ * Validation result
11
+ */
12
+ export interface ValidationResult {
13
+ valid: boolean;
14
+ error?: string;
15
+ sanitized?: unknown;
16
+ }
17
+
18
+ /**
19
+ * Validation options
20
+ */
21
+ export interface ValidationOptions {
22
+ maxLength?: number;
23
+ minLength?: number;
24
+ pattern?: RegExp;
25
+ allowedChars?: RegExp;
26
+ required?: boolean;
27
+ trim?: boolean;
28
+ }
29
+
30
+ /**
31
+ * Default validation options
32
+ */
33
+ const DEFAULT_OPTIONS: ValidationOptions = {
34
+ maxLength: 10000,
35
+ minLength: 0,
36
+ required: false,
37
+ trim: true,
38
+ };
39
+
40
+ /**
41
+ * Validate and sanitize string input
42
+ * @param input Input string
43
+ * @param options Validation options
44
+ * @returns Validation result
45
+ */
46
+ export function validateInput(
47
+ input: unknown,
48
+ options: ValidationOptions = {}
49
+ ): ValidationResult {
50
+ const opts = { ...DEFAULT_OPTIONS, ...options };
51
+
52
+ // Check if input exists
53
+ if (input === null || input === undefined) {
54
+ if (opts.required) {
55
+ return { valid: false, error: 'Input is required' };
56
+ }
57
+ return { valid: true, sanitized: null };
58
+ }
59
+
60
+ // Convert to string
61
+ if (typeof input !== 'string') {
62
+ return { valid: false, error: 'Input must be a string' };
63
+ }
64
+
65
+ let sanitized = input;
66
+
67
+ // Trim whitespace
68
+ if (opts.trim) {
69
+ sanitized = sanitized.trim();
70
+ }
71
+
72
+ // Check length
73
+ if (opts.minLength && sanitized.length < opts.minLength) {
74
+ return {
75
+ valid: false,
76
+ error: `Input must be at least ${opts.minLength} characters`,
77
+ };
78
+ }
79
+
80
+ if (opts.maxLength && sanitized.length > opts.maxLength) {
81
+ return {
82
+ valid: false,
83
+ error: `Input must be at most ${opts.maxLength} characters`,
84
+ };
85
+ }
86
+
87
+ // Check pattern
88
+ if (opts.pattern && !opts.pattern.test(sanitized)) {
89
+ return { valid: false, error: 'Input does not match required pattern' };
90
+ }
91
+
92
+ // Check allowed characters
93
+ if (opts.allowedChars && !opts.allowedChars.test(sanitized)) {
94
+ return { valid: false, error: 'Input contains invalid characters' };
95
+ }
96
+
97
+ return { valid: true, sanitized };
98
+ }
99
+
100
+ /**
101
+ * Sanitize string by removing dangerous characters
102
+ * @param input Input string
103
+ * @returns Sanitized string
104
+ */
105
+ export function sanitizeString(input: string): string {
106
+ return input
107
+ .replace(/[<>]/g, '') // Remove HTML tags
108
+ .replace(/[\x00-\x1f\x7f]/g, '') // Remove control characters
109
+ .replace(/[\u2028\u2029]/g, '') // Remove line/paragraph separators
110
+ .trim();
111
+ }
112
+
113
+ /**
114
+ * Validate file path (prevent path traversal)
115
+ * @param path File path
116
+ * @param allowedBase Allowed base directory
117
+ * @returns Validation result
118
+ */
119
+ export function validatePath(
120
+ path: string,
121
+ allowedBase?: string
122
+ ): ValidationResult {
123
+ // Normalize and check for path traversal
124
+ const normalized = path
125
+ .replace(/\\/g, '/') // Normalize Windows paths
126
+ .replace(/\/+/g, '/'); // Remove duplicate slashes
127
+
128
+ // Check for dangerous patterns
129
+ if (normalized.includes('..') || normalized.includes('~')) {
130
+ return {
131
+ valid: false,
132
+ error: 'Path contains directory traversal characters',
133
+ };
134
+ }
135
+
136
+ // Check for absolute paths outside allowed base
137
+ if (allowedBase && !normalized.startsWith(allowedBase)) {
138
+ const isAbsolute = normalized.startsWith('/') || /^[a-zA-Z]:/.test(normalized);
139
+ if (isAbsolute) {
140
+ return { valid: false, error: 'Path is outside allowed directory' };
141
+ }
142
+ }
143
+
144
+ // Check for null bytes
145
+ if (normalized.includes('\0')) {
146
+ return { valid: false, error: 'Path contains null bytes' };
147
+ }
148
+
149
+ // Check length
150
+ if (normalized.length > 4096) {
151
+ return { valid: false, error: 'Path is too long' };
152
+ }
153
+
154
+ return { valid: true, sanitized: normalized };
155
+ }
156
+
157
+ /**
158
+ * Validate command (prevent command injection)
159
+ * @param command Command string
160
+ * @param allowedCommands Optional whitelist of allowed commands
161
+ * @returns Validation result
162
+ */
163
+ export function validateCommand(
164
+ command: string,
165
+ allowedCommands?: string[]
166
+ ): ValidationResult {
167
+ // Extract base command
168
+ const parts = command.trim().split(/\s+/);
169
+ const baseCommand = parts[0]?.toLowerCase();
170
+
171
+ if (!baseCommand) {
172
+ return { valid: false, error: 'Empty command' };
173
+ }
174
+
175
+ // Check whitelist if provided
176
+ if (allowedCommands && !allowedCommands.includes(baseCommand)) {
177
+ return { valid: false, error: `Command '${baseCommand}' is not allowed` };
178
+ }
179
+
180
+ // Check for dangerous shell characters
181
+ const dangerousPatterns = [
182
+ /[;&|`$]/, // Shell operators
183
+ /\$\(/, // Command substitution
184
+ /`.*`/, // Backtick substitution
185
+ /\|\|/, // OR operator
186
+ /&&/, // AND operator
187
+ />\s*>/, // Append redirection
188
+ /<\s*</, // Here document
189
+ /\r|\n/, // Newlines (command chaining)
190
+ ];
191
+
192
+ for (const pattern of dangerousPatterns) {
193
+ if (pattern.test(command)) {
194
+ return { valid: false, error: 'Command contains dangerous characters' };
195
+ }
196
+ }
197
+
198
+ return { valid: true, sanitized: command };
199
+ }
200
+
201
+ /**
202
+ * Validate tags for safe SQL usage
203
+ * @param tags Array of tag strings
204
+ * @returns Validation result with sanitized tags
205
+ */
206
+ export function validateTags(tags: unknown): ValidationResult {
207
+ if (!Array.isArray(tags)) {
208
+ return { valid: false, error: 'Tags must be an array' };
209
+ }
210
+
211
+ const sanitized: string[] = [];
212
+ const tagPattern = /^[a-zA-Z0-9_\-.:]+$/;
213
+
214
+ for (const tag of tags) {
215
+ if (typeof tag !== 'string') {
216
+ return { valid: false, error: 'Each tag must be a string' };
217
+ }
218
+
219
+ const trimmed = tag.trim();
220
+
221
+ if (trimmed.length === 0) {
222
+ continue; // Skip empty tags
223
+ }
224
+
225
+ if (trimmed.length > 100) {
226
+ return { valid: false, error: 'Tag is too long (max 100 characters)' };
227
+ }
228
+
229
+ if (!tagPattern.test(trimmed)) {
230
+ return {
231
+ valid: false,
232
+ error: `Invalid tag: '${trimmed}'. Tags can only contain alphanumeric characters, underscores, hyphens, dots, and colons`,
233
+ };
234
+ }
235
+
236
+ sanitized.push(trimmed);
237
+ }
238
+
239
+ return { valid: true, sanitized };
240
+ }
241
+
242
+ /**
243
+ * Check if string is a valid identifier
244
+ * @param id Identifier string
245
+ * @returns True if valid
246
+ */
247
+ export function isValidIdentifier(id: string): boolean {
248
+ return /^[a-zA-Z_][a-zA-Z0-9_\-]*$/.test(id) && id.length <= 256;
249
+ }
250
+
251
+ /**
252
+ * Escape string for safe SQL usage (use parameterized queries instead when possible)
253
+ * This is a LAST RESORT - always prefer parameterized queries
254
+ * @param value String to escape
255
+ * @returns Escaped string
256
+ */
257
+ export function escapeForSql(value: string): string {
258
+ return value
259
+ .replace(/'/g, "''") // Escape single quotes
260
+ .replace(/\\/g, '\\\\') // Escape backslashes
261
+ .replace(/\x00/g, '') // Remove null bytes
262
+ .replace(/\n/g, '\\n') // Escape newlines
263
+ .replace(/\r/g, '\\r') // Escape carriage returns
264
+ .replace(/\x1a/g, '\\Z'); // Escape ctrl+Z (EOF in Windows)
265
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Secure Random Utilities
3
+ *
4
+ * Cryptographically secure random ID and token generation.
5
+ * Replaces Math.random() for security-sensitive operations.
6
+ *
7
+ * @module v3/shared/security/secure-random
8
+ */
9
+
10
+ import { randomBytes, randomUUID } from 'crypto';
11
+
12
+ /**
13
+ * Generate a cryptographically secure random ID
14
+ * @param prefix Optional prefix for the ID
15
+ * @param length Number of random bytes (default 12)
16
+ * @returns Secure random ID string
17
+ */
18
+ export function generateSecureId(prefix?: string, length: number = 12): string {
19
+ const timestamp = Date.now().toString(36);
20
+ const randomPart = randomBytes(length).toString('hex');
21
+ return prefix ? `${prefix}_${timestamp}_${randomPart}` : `${timestamp}_${randomPart}`;
22
+ }
23
+
24
+ /**
25
+ * Generate a UUID v4 (cryptographically secure)
26
+ * @returns UUID string
27
+ */
28
+ export function generateUUID(): string {
29
+ return randomUUID();
30
+ }
31
+
32
+ /**
33
+ * Generate a secure token for authentication
34
+ * @param length Number of bytes (default 32)
35
+ * @returns Hex-encoded token string
36
+ */
37
+ export function generateSecureToken(length: number = 32): string {
38
+ return randomBytes(length).toString('hex');
39
+ }
40
+
41
+ /**
42
+ * Generate a short secure ID (for display purposes)
43
+ * @param prefix Optional prefix
44
+ * @returns Short secure ID
45
+ */
46
+ export function generateShortId(prefix?: string): string {
47
+ const id = randomBytes(6).toString('base64url');
48
+ return prefix ? `${prefix}-${id}` : id;
49
+ }
50
+
51
+ /**
52
+ * Generate a secure session ID
53
+ * @returns Session ID string
54
+ */
55
+ export function generateSessionId(): string {
56
+ return generateSecureId('session', 16);
57
+ }
58
+
59
+ /**
60
+ * Generate a secure agent ID
61
+ * @returns Agent ID string
62
+ */
63
+ export function generateAgentId(): string {
64
+ return generateSecureId('agent', 12);
65
+ }
66
+
67
+ /**
68
+ * Generate a secure task ID
69
+ * @returns Task ID string
70
+ */
71
+ export function generateTaskId(): string {
72
+ return generateSecureId('task', 12);
73
+ }
74
+
75
+ /**
76
+ * Generate a secure memory ID
77
+ * @returns Memory ID string
78
+ */
79
+ export function generateMemoryId(): string {
80
+ return generateSecureId('mem', 12);
81
+ }
82
+
83
+ /**
84
+ * Generate a secure event ID
85
+ * @returns Event ID string
86
+ */
87
+ export function generateEventId(): string {
88
+ return generateSecureId('evt', 12);
89
+ }
90
+
91
+ /**
92
+ * Generate a secure swarm ID
93
+ * @returns Swarm ID string
94
+ */
95
+ export function generateSwarmId(): string {
96
+ return generateSecureId('swarm', 12);
97
+ }
98
+
99
+ /**
100
+ * Generate a secure pattern ID
101
+ * @returns Pattern ID string
102
+ */
103
+ export function generatePatternId(): string {
104
+ return generateSecureId('pat', 12);
105
+ }
106
+
107
+ /**
108
+ * Generate a secure trajectory ID
109
+ * @returns Trajectory ID string
110
+ */
111
+ export function generateTrajectoryId(): string {
112
+ return generateSecureId('traj', 12);
113
+ }
114
+
115
+ /**
116
+ * Generate a random integer in range [min, max] using crypto
117
+ * @param min Minimum value (inclusive)
118
+ * @param max Maximum value (inclusive)
119
+ * @returns Cryptographically random integer
120
+ */
121
+ export function secureRandomInt(min: number, max: number): number {
122
+ const range = max - min + 1;
123
+ const bytesNeeded = Math.ceil(Math.log2(range) / 8);
124
+ const maxValid = Math.pow(256, bytesNeeded) - (Math.pow(256, bytesNeeded) % range);
125
+
126
+ let randomValue: number;
127
+ do {
128
+ const bytes = randomBytes(bytesNeeded);
129
+ randomValue = bytes.reduce((acc, byte, i) => acc + byte * Math.pow(256, i), 0);
130
+ } while (randomValue >= maxValid);
131
+
132
+ return min + (randomValue % range);
133
+ }
134
+
135
+ /**
136
+ * Secure random selection from array
137
+ * @param array Array to select from
138
+ * @returns Random element
139
+ */
140
+ export function secureRandomChoice<T>(array: T[]): T {
141
+ if (array.length === 0) {
142
+ throw new Error('Cannot select from empty array');
143
+ }
144
+ return array[secureRandomInt(0, array.length - 1)]!;
145
+ }
146
+
147
+ /**
148
+ * Secure shuffle array (Fisher-Yates with crypto)
149
+ * @param array Array to shuffle
150
+ * @returns New shuffled array
151
+ */
152
+ export function secureShuffleArray<T>(array: T[]): T[] {
153
+ const result = [...array];
154
+ for (let i = result.length - 1; i > 0; i--) {
155
+ const j = secureRandomInt(0, i);
156
+ [result[i], result[j]] = [result[j]!, result[i]!];
157
+ }
158
+ return result;
159
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared Services
3
+ *
4
+ * @module @sparkleideas/shared/services
5
+ */
6
+
7
+ export {
8
+ V3ProgressService,
9
+ createV3ProgressService,
10
+ getV3Progress,
11
+ syncV3Progress,
12
+ getDefaultProgressService,
13
+ type V3ProgressMetrics,
14
+ type V3ProgressOptions,
15
+ type ProgressChangeEvent,
16
+ } from './v3-progress.service.js';