@fleettools/server 0.1.1 → 0.2.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.
@@ -1,377 +0,0 @@
1
- /**
2
- * Task Queue for FleetTools Coordination System
3
- *
4
- * Manages task queuing, assignment, and completion tracking
5
- * Uses SQLite for persistence with tsk_ prefixed IDs
6
- */
7
- import Database from 'bun:sqlite';
8
- import { randomUUID } from 'node:crypto';
9
- import path from 'node:path';
10
- // Using local types to avoid import issues
11
- export var TaskStatus;
12
- (function (TaskStatus) {
13
- TaskStatus["PENDING"] = "pending";
14
- TaskStatus["ASSIGNED"] = "assigned";
15
- TaskStatus["IN_PROGRESS"] = "in_progress";
16
- TaskStatus["COMPLETED"] = "completed";
17
- TaskStatus["FAILED"] = "failed";
18
- TaskStatus["CANCELLED"] = "cancelled";
19
- })(TaskStatus || (TaskStatus = {}));
20
- export class TaskQueue {
21
- db;
22
- config;
23
- constructor(config = {}) {
24
- this.config = {
25
- maxRetries: 3,
26
- retryDelay: 5000,
27
- dbPath: path.join(process.cwd(), '.flightline', 'tasks.db'),
28
- ...config
29
- };
30
- this.db = new Database(this.config.dbPath);
31
- this.initializeDatabase();
32
- }
33
- /**
34
- * Initialize database schema
35
- */
36
- initializeDatabase() {
37
- // Create tasks table
38
- this.db.exec(`
39
- CREATE TABLE IF NOT EXISTS tasks (
40
- id TEXT PRIMARY KEY,
41
- type TEXT NOT NULL,
42
- title TEXT NOT NULL,
43
- description TEXT,
44
- status TEXT NOT NULL DEFAULT 'pending',
45
- priority TEXT NOT NULL DEFAULT 'medium',
46
- assigned_to TEXT,
47
- mission_id TEXT,
48
- dependencies TEXT, -- JSON array
49
- metadata TEXT, -- JSON object
50
- created_at TEXT NOT NULL,
51
- updated_at TEXT NOT NULL,
52
- completed_at TEXT,
53
- retry_count INTEGER DEFAULT 0,
54
- last_retry_at TEXT
55
- )
56
- `);
57
- // Create indexes for performance
58
- this.db.exec(`
59
- CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
60
- CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
61
- CREATE INDEX IF NOT EXISTS idx_tasks_assigned_to ON tasks(assigned_to);
62
- CREATE INDEX IF NOT EXISTS idx_tasks_mission_id ON tasks(mission_id);
63
- CREATE INDEX IF NOT EXISTS idx_tasks_created_at ON tasks(created_at);
64
- `);
65
- }
66
- /**
67
- * Enqueue a new task
68
- */
69
- async enqueue(task) {
70
- const taskId = `tsk_${randomUUID()}`;
71
- const now = new Date().toISOString();
72
- try {
73
- const stmt = this.db.prepare(`
74
- INSERT INTO tasks (
75
- id, type, title, description, status, priority,
76
- assigned_to, mission_id, dependencies, metadata,
77
- created_at, updated_at
78
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
79
- `);
80
- stmt.run(taskId, task.type, task.title, task.description, task.status || TaskStatus.PENDING, task.priority, task.assignedTo || null, task.missionId || null, JSON.stringify(task.dependencies || []), JSON.stringify(task.metadata || {}), now, now);
81
- console.log(`✓ Task enqueued: ${taskId} (${task.title})`);
82
- return taskId;
83
- }
84
- catch (error) {
85
- console.error(`✗ Failed to enqueue task:`, error.message);
86
- throw new Error(`Task enqueue failed: ${error.message}`);
87
- }
88
- }
89
- /**
90
- * Dequeue next available task for agent type
91
- */
92
- async dequeue(agentType, limit = 1) {
93
- try {
94
- let whereClause = 'status = ?';
95
- const params = [TaskStatus.PENDING];
96
- if (agentType) {
97
- whereClause += ' AND (type = ? OR type = ?)';
98
- params.push(agentType, 'general'); // Allow general tasks
99
- }
100
- whereClause += ' ORDER BY priority DESC, created_at ASC LIMIT ?';
101
- params.push(limit);
102
- const stmt = this.db.prepare(`
103
- SELECT * FROM tasks
104
- WHERE ${whereClause}
105
- `);
106
- const rows = stmt.all(...params);
107
- if (rows.length === 0) {
108
- return [];
109
- }
110
- // Mark tasks as assigned
111
- const taskIds = rows.map(row => row.id);
112
- await this.markAsAssigned(taskIds);
113
- // Convert rows to Task objects
114
- const tasks = rows.map(row => this.rowToTask(row));
115
- console.log(`✓ Dequeued ${tasks.length} task(s)`);
116
- return tasks;
117
- }
118
- catch (error) {
119
- console.error(`✗ Failed to dequeue tasks:`, error.message);
120
- throw new Error(`Task dequeue failed: ${error.message}`);
121
- }
122
- }
123
- /**
124
- * Mark task as in progress
125
- */
126
- async markAsInProgress(taskId) {
127
- await this.updateTaskStatus(taskId, TaskStatus.IN_PROGRESS);
128
- }
129
- /**
130
- * Complete a task
131
- */
132
- async complete(taskId, result) {
133
- try {
134
- const now = new Date().toISOString();
135
- const stmt = this.db.prepare(`
136
- UPDATE tasks
137
- SET status = ?, updated_at = ?, completed_at = ?, metadata = ?
138
- WHERE id = ?
139
- `);
140
- // Add result to metadata
141
- const currentTask = await this.getTask(taskId);
142
- const updatedMetadata = {
143
- ...currentTask?.metadata,
144
- result: result || null
145
- };
146
- stmt.run(TaskStatus.COMPLETED, now, now, JSON.stringify(updatedMetadata), taskId);
147
- console.log(`✓ Task completed: ${taskId}`);
148
- }
149
- catch (error) {
150
- await this.fail(taskId, `Completion failed: ${error.message}`);
151
- throw new Error(`Task completion failed: ${error.message}`);
152
- }
153
- }
154
- /**
155
- * Mark task as failed
156
- */
157
- async fail(taskId, error) {
158
- try {
159
- const now = new Date().toISOString();
160
- const stmt = this.db.prepare(`
161
- UPDATE tasks
162
- SET status = ?, updated_at = ?, retry_count = retry_count + 1, last_retry_at = ?
163
- WHERE id = ?
164
- `);
165
- stmt.run(TaskStatus.FAILED, now, now, taskId);
166
- console.log(`✗ Task failed: ${taskId} - ${error}`);
167
- }
168
- catch (error) {
169
- console.error(`✗ Failed to mark task as failed:`, error.message);
170
- throw new Error(`Task failure marking failed: ${error.message}`);
171
- }
172
- }
173
- /**
174
- * Get task by ID
175
- */
176
- async getTask(taskId) {
177
- try {
178
- const stmt = this.db.prepare('SELECT * FROM tasks WHERE id = ?');
179
- const row = stmt.get(taskId);
180
- return row ? this.rowToTask(row) : null;
181
- }
182
- catch (error) {
183
- console.error(`✗ Failed to get task:`, error.message);
184
- return null;
185
- }
186
- }
187
- /**
188
- * Get tasks by status
189
- */
190
- async getTasksByStatus(status) {
191
- try {
192
- const stmt = this.db.prepare('SELECT * FROM tasks WHERE status = ? ORDER BY created_at DESC');
193
- const rows = stmt.all(status);
194
- return rows.map(row => this.rowToTask(row));
195
- }
196
- catch (error) {
197
- console.error(`✗ Failed to get tasks by status:`, error.message);
198
- return [];
199
- }
200
- }
201
- /**
202
- * Get tasks for mission
203
- */
204
- async getTasksByMission(missionId) {
205
- try {
206
- const stmt = this.db.prepare('SELECT * FROM tasks WHERE mission_id = ? ORDER BY priority DESC, created_at ASC');
207
- const rows = stmt.all(missionId);
208
- return rows.map(row => this.rowToTask(row));
209
- }
210
- catch (error) {
211
- console.error(`✗ Failed to get mission tasks:`, error.message);
212
- return [];
213
- }
214
- }
215
- /**
216
- * Get tasks assigned to agent
217
- */
218
- async getTasksByAgent(agentId) {
219
- try {
220
- const stmt = this.db.prepare('SELECT * FROM tasks WHERE assigned_to = ? ORDER BY created_at DESC');
221
- const rows = stmt.all(agentId);
222
- return rows.map(row => this.rowToTask(row));
223
- }
224
- catch (error) {
225
- console.error(`✗ Failed to get agent tasks:`, error.message);
226
- return [];
227
- }
228
- }
229
- /**
230
- * Retry failed tasks
231
- */
232
- async retryFailedTasks() {
233
- try {
234
- const stmt = this.db.prepare(`
235
- SELECT * FROM tasks
236
- WHERE status = ? AND retry_count < ?
237
- ORDER BY priority DESC, created_at ASC
238
- `);
239
- const rows = stmt.all(TaskStatus.FAILED, this.config.maxRetries);
240
- let retriedCount = 0;
241
- for (const row of rows) {
242
- // Check dependencies
243
- if (row.dependencies) {
244
- const dependencies = JSON.parse(row.dependencies);
245
- const pendingDeps = await this.checkDependencies(dependencies);
246
- if (pendingDeps.length > 0) {
247
- continue; // Skip if dependencies not met
248
- }
249
- }
250
- await this.resetTask(row.id);
251
- retriedCount++;
252
- }
253
- if (retriedCount > 0) {
254
- console.log(`✓ Retried ${retriedCount} failed tasks`);
255
- }
256
- return retriedCount;
257
- }
258
- catch (error) {
259
- console.error(`✗ Failed to retry tasks:`, error.message);
260
- return 0;
261
- }
262
- }
263
- /**
264
- * Get queue statistics
265
- */
266
- async getStats() {
267
- try {
268
- const stats = {
269
- total: 0,
270
- pending: 0,
271
- assigned: 0,
272
- inProgress: 0,
273
- completed: 0,
274
- failed: 0
275
- };
276
- const stmt = this.db.prepare('SELECT status, COUNT(*) as count FROM tasks GROUP BY status');
277
- const rows = stmt.all();
278
- rows.forEach(row => {
279
- stats.total += row.count;
280
- switch (row.status) {
281
- case TaskStatus.PENDING:
282
- stats.pending = row.count;
283
- break;
284
- case TaskStatus.ASSIGNED:
285
- stats.assigned = row.count;
286
- break;
287
- case TaskStatus.IN_PROGRESS:
288
- stats.inProgress = row.count;
289
- break;
290
- case TaskStatus.COMPLETED:
291
- stats.completed = row.count;
292
- break;
293
- case TaskStatus.FAILED:
294
- stats.failed = row.count;
295
- break;
296
- }
297
- });
298
- return stats;
299
- }
300
- catch (error) {
301
- console.error(`✗ Failed to get stats:`, error.message);
302
- return {
303
- total: 0,
304
- pending: 0,
305
- assigned: 0,
306
- inProgress: 0,
307
- completed: 0,
308
- failed: 0
309
- };
310
- }
311
- }
312
- // ========================================================================
313
- // Private Helper Methods
314
- // ========================================================================
315
- async markAsAssigned(taskIds) {
316
- if (taskIds.length === 0)
317
- return;
318
- const placeholders = taskIds.map(() => '?').join(',');
319
- const stmt = this.db.prepare(`
320
- UPDATE tasks
321
- SET status = ?, updated_at = ?
322
- WHERE id IN (${placeholders})
323
- `);
324
- const now = new Date().toISOString();
325
- stmt.run(TaskStatus.ASSIGNED, now, ...taskIds);
326
- }
327
- async updateTaskStatus(taskId, status) {
328
- const stmt = this.db.prepare(`
329
- UPDATE tasks
330
- SET status = ?, updated_at = ?
331
- WHERE id = ?
332
- `);
333
- stmt.run(status, new Date().toISOString(), taskId);
334
- }
335
- async resetTask(taskId) {
336
- const stmt = this.db.prepare(`
337
- UPDATE tasks
338
- SET status = ?, updated_at = ?, completed_at = NULL
339
- WHERE id = ?
340
- `);
341
- stmt.run(TaskStatus.PENDING, new Date().toISOString(), taskId);
342
- }
343
- async checkDependencies(dependencies) {
344
- if (dependencies.length === 0)
345
- return [];
346
- const placeholders = dependencies.map(() => '?').join(',');
347
- const stmt = this.db.prepare(`
348
- SELECT id FROM tasks
349
- WHERE id IN (${placeholders}) AND status != ?
350
- `);
351
- const incompleteRows = stmt.all(...dependencies, TaskStatus.COMPLETED);
352
- return incompleteRows.map((row) => row.id);
353
- }
354
- rowToTask(row) {
355
- return {
356
- id: row.id,
357
- type: row.type,
358
- title: row.title,
359
- description: row.description || '',
360
- status: row.status,
361
- priority: row.priority,
362
- assignedTo: row.assigned_to,
363
- missionId: row.mission_id,
364
- dependencies: row.dependencies ? JSON.parse(row.dependencies) : [],
365
- metadata: row.metadata ? JSON.parse(row.metadata) : {},
366
- createdAt: row.created_at,
367
- updatedAt: row.updated_at,
368
- completedAt: row.completed_at
369
- };
370
- }
371
- /**
372
- * Close database connection
373
- */
374
- close() {
375
- this.db.close();
376
- }
377
- }
@@ -1,188 +0,0 @@
1
- /**
2
- * Agent Types and Validation Schemas
3
- *
4
- * Zod validation schemas for agent coordination
5
- */
6
- import { z } from 'zod';
7
- // Define AgentType locally to avoid import conflicts
8
- export var AgentType;
9
- (function (AgentType) {
10
- AgentType["FRONTEND"] = "frontend";
11
- AgentType["BACKEND"] = "backend";
12
- AgentType["TESTING"] = "testing";
13
- AgentType["DOCUMENTATION"] = "documentation";
14
- AgentType["SECURITY"] = "security";
15
- AgentType["PERFORMANCE"] = "performance";
16
- })(AgentType || (AgentType = {}));
17
- // Enhanced AgentType with validation
18
- export const AgentTypeSchema = z.enum([
19
- AgentType.FRONTEND,
20
- AgentType.BACKEND,
21
- AgentType.TESTING,
22
- AgentType.DOCUMENTATION,
23
- AgentType.SECURITY,
24
- AgentType.PERFORMANCE
25
- ]);
26
- // Agent Configuration Schema
27
- export const AgentConfigSchema = z.object({
28
- timeout: z.number().int().positive().optional(),
29
- retries: z.number().int().min(0).max(10).optional(),
30
- resources: z.object({
31
- memory: z.string().optional(),
32
- cpu: z.string().optional(),
33
- }).optional(),
34
- environment: z.record(z.string()).optional(),
35
- });
36
- // Agent Spawn Request Schema
37
- export const AgentSpawnRequestSchema = z.object({
38
- type: AgentTypeSchema,
39
- task: z.string().optional(),
40
- metadata: z.record(z.unknown()).optional(),
41
- config: AgentConfigSchema.optional(),
42
- });
43
- // Agent Update Request Schema
44
- export const AgentUpdateRequestSchema = z.object({
45
- status: z.enum(['idle', 'busy', 'error']).optional(),
46
- metadata: z.record(z.unknown()).optional(),
47
- heartbeat: z.boolean().optional(),
48
- });
49
- // Agent Capability Schema
50
- export const AgentCapabilitySchema = z.object({
51
- id: z.string().uuid(),
52
- agentId: z.string(),
53
- capability: z.string(),
54
- version: z.string(),
55
- enabled: z.boolean(),
56
- config: z.record(z.unknown()).optional(),
57
- });
58
- // Agent Specialization Schema
59
- export const AgentSpecializationSchema = z.object({
60
- id: z.string().uuid(),
61
- agentId: z.string(),
62
- specialization: z.string(),
63
- proficiency: z.number().min(0).max(1), // 0-1 scale
64
- experience: z.number().int().min(0), // years
65
- certifications: z.array(z.string()).optional(),
66
- });
67
- // Task Assignment Schema
68
- export const TaskAssignmentSchema = z.object({
69
- taskId: z.string(),
70
- agentId: z.string(),
71
- assignedAt: z.string().datetime(),
72
- deadline: z.string().datetime().optional(),
73
- priority: z.enum(['low', 'medium', 'high', 'critical']),
74
- requirements: z.object({
75
- skills: z.array(z.string()).optional(),
76
- tools: z.array(z.string()).optional(),
77
- resources: z.record(z.string()).optional(),
78
- }).optional(),
79
- });
80
- // Agent Performance Schema
81
- export const AgentPerformanceSchema = z.object({
82
- agentId: z.string(),
83
- taskId: z.string().optional(),
84
- metrics: z.object({
85
- executionTime: z.number().min(0),
86
- accuracy: z.number().min(0).max(1),
87
- efficiency: z.number().min(0).max(1),
88
- resourceUsage: z.object({
89
- memory: z.number().min(0),
90
- cpu: z.number().min(0),
91
- disk: z.number().min(0).optional(),
92
- }),
93
- }),
94
- timestamp: z.string().datetime(),
95
- });
96
- // Validation functions
97
- export class AgentValidator {
98
- /**
99
- * Validate agent spawn request
100
- */
101
- static validateSpawnRequest(data) {
102
- const result = AgentSpawnRequestSchema.safeParse(data);
103
- if (!result.success) {
104
- throw new Error(`Invalid spawn request: ${result.error.message}`);
105
- }
106
- return result.data;
107
- }
108
- /**
109
- * Validate agent configuration
110
- */
111
- static validateConfig(data) {
112
- const result = AgentConfigSchema.safeParse(data);
113
- if (!result.success) {
114
- throw new Error(`Invalid agent config: ${result.error.message}`);
115
- }
116
- return result.data;
117
- }
118
- /**
119
- * Validate agent type
120
- */
121
- static validateAgentType(type) {
122
- const result = AgentTypeSchema.safeParse(type);
123
- if (!result.success) {
124
- throw new Error(`Invalid agent type: ${result.error.message}`);
125
- }
126
- return result.data;
127
- }
128
- /**
129
- * Validate agent ID format
130
- */
131
- static validateAgentId(id) {
132
- // Agent IDs should follow pattern: agt_<uuid>
133
- const agentIdPattern = /^agt_[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
134
- return agentIdPattern.test(id);
135
- }
136
- /**
137
- * Validate task assignment
138
- */
139
- static validateTaskAssignment(data) {
140
- const result = TaskAssignmentSchema.safeParse(data);
141
- if (!result.success) {
142
- throw new Error(`Invalid task assignment: ${result.error.message}`);
143
- }
144
- return result.data;
145
- }
146
- /**
147
- * Check if agent type is suitable for task type
148
- */
149
- static isAgentSuitableForTask(agentType, taskType) {
150
- const suitabilityMap = {
151
- [AgentType.FRONTEND]: ['ui', 'frontend', 'component', 'interface'],
152
- [AgentType.BACKEND]: ['api', 'backend', 'server', 'database'],
153
- [AgentType.TESTING]: ['test', 'testing', 'qa', 'validation'],
154
- [AgentType.DOCUMENTATION]: ['docs', 'documentation', 'readme', 'guide'],
155
- [AgentType.SECURITY]: ['security', 'audit', 'vulnerability', 'scan'],
156
- [AgentType.PERFORMANCE]: ['performance', 'optimization', 'benchmark', 'metrics']
157
- };
158
- const agentCapabilities = suitabilityMap[agentType] || [];
159
- return AgentCapabilities.includes(taskType.toLowerCase());
160
- }
161
- /**
162
- * Get recommended agents for task type
163
- */
164
- static getRecommendedAgents(taskType) {
165
- const taskToAgents = {
166
- 'ui': [AgentType.FRONTEND],
167
- 'frontend': [AgentType.FRONTEND],
168
- 'api': [AgentType.BACKEND],
169
- 'backend': [AgentType.BACKEND],
170
- 'test': [AgentType.TESTING],
171
- 'security': [AgentType.SECURITY],
172
- 'docs': [AgentType.DOCUMENTATION],
173
- 'performance': [AgentType.PERFORMANCE],
174
- 'general': [AgentType.BACKEND, AgentType.FRONTEND] // General tasks can go to multiple types
175
- };
176
- return taskToAgents[taskType.toLowerCase()] || [AgentType.BACKEND]; // Default to backend
177
- }
178
- }
179
- // Common task categories for validation
180
- const AgentCapabilities = [
181
- 'ui', 'frontend', 'component', 'interface',
182
- 'api', 'backend', 'server', 'database',
183
- 'test', 'testing', 'qa', 'validation',
184
- 'docs', 'documentation', 'readme', 'guide',
185
- 'security', 'audit', 'vulnerability', 'scan',
186
- 'performance', 'optimization', 'benchmark', 'metrics',
187
- 'general', 'utility', 'helper', 'tool'
188
- ];