@fleettools/squawk 0.1.0 → 0.1.1

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 (66) hide show
  1. package/package.json +1 -1
  2. package/AGENTS.md +0 -28
  3. package/dist/src/db/checkpoint-storage.d.ts +0 -19
  4. package/dist/src/db/checkpoint-storage.d.ts.map +0 -1
  5. package/dist/src/db/checkpoint-storage.js +0 -355
  6. package/dist/src/db/checkpoint-storage.js.map +0 -1
  7. package/dist/src/db/index.d.ts +0 -30
  8. package/dist/src/db/index.d.ts.map +0 -1
  9. package/dist/src/db/index.js +0 -329
  10. package/dist/src/db/index.js.map +0 -1
  11. package/dist/src/db/sqlite.d.ts +0 -31
  12. package/dist/src/db/sqlite.d.ts.map +0 -1
  13. package/dist/src/db/sqlite.js +0 -558
  14. package/dist/src/db/sqlite.js.map +0 -1
  15. package/dist/src/db/types.d.ts +0 -611
  16. package/dist/src/db/types.d.ts.map +0 -1
  17. package/dist/src/db/types.js +0 -4
  18. package/dist/src/db/types.js.map +0 -1
  19. package/dist/src/index.d.ts +0 -2
  20. package/dist/src/index.d.ts.map +0 -1
  21. package/dist/src/index.js +0 -285
  22. package/dist/src/index.js.map +0 -1
  23. package/dist/src/recovery/checkpointing.d.ts +0 -244
  24. package/dist/src/recovery/checkpointing.d.ts.map +0 -1
  25. package/dist/src/recovery/checkpointing.js +0 -511
  26. package/dist/src/recovery/checkpointing.js.map +0 -1
  27. package/dist/src/recovery/detection.d.ts +0 -137
  28. package/dist/src/recovery/detection.d.ts.map +0 -1
  29. package/dist/src/recovery/detection.js +0 -240
  30. package/dist/src/recovery/detection.js.map +0 -1
  31. package/dist/src/recovery/detector.d.ts +0 -34
  32. package/dist/src/recovery/detector.d.ts.map +0 -1
  33. package/dist/src/recovery/detector.js +0 -42
  34. package/dist/src/recovery/detector.js.map +0 -1
  35. package/dist/src/recovery/index.d.ts +0 -3
  36. package/dist/src/recovery/index.d.ts.map +0 -1
  37. package/dist/src/recovery/index.js +0 -3
  38. package/dist/src/recovery/index.js.map +0 -1
  39. package/dist/src/recovery/restorer.d.ts +0 -51
  40. package/dist/src/recovery/restorer.d.ts.map +0 -1
  41. package/dist/src/recovery/restorer.js +0 -266
  42. package/dist/src/recovery/restorer.js.map +0 -1
  43. package/dist/src/schemas.d.ts +0 -142
  44. package/dist/src/schemas.d.ts.map +0 -1
  45. package/dist/src/schemas.js +0 -110
  46. package/dist/src/schemas.js.map +0 -1
  47. package/src/db/checkpoint-storage.ts +0 -443
  48. package/src/db/index.d.ts +0 -30
  49. package/src/db/index.d.ts.map +0 -1
  50. package/src/db/index.js.map +0 -1
  51. package/src/db/index.ts +0 -417
  52. package/src/db/schema.sql +0 -112
  53. package/src/db/sqlite.d.ts +0 -31
  54. package/src/db/sqlite.d.ts.map +0 -1
  55. package/src/db/sqlite.js +0 -667
  56. package/src/db/sqlite.js.map +0 -1
  57. package/src/db/sqlite.ts +0 -677
  58. package/src/db/types.d.ts +0 -612
  59. package/src/db/types.d.ts.map +0 -1
  60. package/src/db/types.js +0 -4
  61. package/src/db/types.js.map +0 -1
  62. package/src/db/types.ts +0 -771
  63. package/src/index.ts +0 -332
  64. package/src/recovery/detector.ts +0 -82
  65. package/src/recovery/index.ts +0 -3
  66. package/src/recovery/restorer.ts +0 -377
@@ -1,377 +0,0 @@
1
-
2
- import type {
3
- Checkpoint,
4
- RecoveryContext,
5
- SortieSnapshot,
6
- LockSnapshot,
7
- MessageSnapshot,
8
- LockResult,
9
- AppendEventInput
10
- } from '../db/types.js';
11
-
12
- export interface RestoreOptions {
13
- dryRun?: boolean;
14
- forceLocks?: boolean;
15
- }
16
-
17
- export interface RestoreResult {
18
- success: boolean;
19
- checkpoint_id: string;
20
- mission_id: string;
21
- recovery_context: RecoveryContext;
22
- restored: {
23
- sorties: number;
24
- locks: number;
25
- messages: number;
26
- };
27
- errors: string[];
28
- warnings: string[];
29
- }
30
-
31
- export class StateRestorer {
32
- constructor(
33
- private db: {
34
- checkpoints: {
35
- getById: (id: string) => Promise<Checkpoint | null>;
36
- getLatestByMission: (missionId: string) => Promise<Checkpoint | null>;
37
- markConsumed: (id: string) => Promise<void>;
38
- };
39
- sorties: {
40
- update: (id: string, data: any) => Promise<void>;
41
- };
42
- locks: {
43
- acquire: (input: any) => Promise<LockResult>;
44
- forceRelease: (id: string) => Promise<void>;
45
- };
46
- messages: {
47
- requeue: (id: string) => Promise<void>;
48
- };
49
- events: {
50
- append: (input: AppendEventInput) => Promise<any>;
51
- };
52
- beginTransaction: () => Promise<void>;
53
- commitTransaction: () => Promise<void>;
54
- rollbackTransaction: () => Promise<void>;
55
- }
56
- ) {}
57
-
58
- async restoreFromCheckpoint(checkpointId: string, options: RestoreOptions = {}): Promise<RestoreResult> {
59
- const { dryRun = false, forceLocks = false } = options;
60
-
61
- try {
62
- const checkpoint = await this.db.checkpoints.getById(checkpointId);
63
- if (!checkpoint) {
64
- return {
65
- success: false,
66
- checkpoint_id: checkpointId,
67
- mission_id: '',
68
- recovery_context: {} as RecoveryContext,
69
- restored: { sorties: 0, locks: 0, messages: 0 },
70
- errors: [`Checkpoint not found: ${checkpointId}`],
71
- warnings: []
72
- };
73
- }
74
-
75
- return await this.restore(checkpoint, { dryRun, forceLocks });
76
- } catch (error) {
77
- return {
78
- success: false,
79
- checkpoint_id: checkpointId,
80
- mission_id: '',
81
- recovery_context: {} as RecoveryContext,
82
- restored: { sorties: 0, locks: 0, messages: 0 },
83
- errors: [`Restore failed: ${error instanceof Error ? error.message : String(error)}`],
84
- warnings: []
85
- };
86
- }
87
- }
88
-
89
- async restoreLatest(missionId: string, options: RestoreOptions = {}): Promise<RestoreResult> {
90
- const { dryRun = false, forceLocks = false } = options;
91
-
92
- try {
93
- const checkpoint = await this.db.checkpoints.getLatestByMission(missionId);
94
- if (!checkpoint) {
95
- return {
96
- success: false,
97
- checkpoint_id: '',
98
- mission_id: missionId,
99
- recovery_context: {} as RecoveryContext,
100
- restored: { sorties: 0, locks: 0, messages: 0 },
101
- errors: [`No checkpoints found for mission: ${missionId}`],
102
- warnings: []
103
- };
104
- }
105
-
106
- return await this.restore(checkpoint, { dryRun, forceLocks });
107
- } catch (error) {
108
- return {
109
- success: false,
110
- checkpoint_id: '',
111
- mission_id: missionId,
112
- recovery_context: {} as RecoveryContext,
113
- restored: { sorties: 0, locks: 0, messages: 0 },
114
- errors: [`Restore failed: ${error instanceof Error ? error.message : String(error)}`],
115
- warnings: []
116
- };
117
- }
118
- }
119
-
120
- private async restore(checkpoint: Checkpoint, options: RestoreOptions): Promise<RestoreResult> {
121
- const { dryRun = false, forceLocks = false } = options;
122
- const now = new Date().toISOString();
123
-
124
- const result: RestoreResult = {
125
- success: true,
126
- checkpoint_id: checkpoint.id,
127
- mission_id: checkpoint.mission_id,
128
- recovery_context: {
129
- last_action: 'Restored from checkpoint',
130
- next_steps: this.extractNextSteps(checkpoint),
131
- blockers: [],
132
- files_modified: this.extractModifiedFiles(checkpoint),
133
- mission_summary: `${checkpoint.mission_id} checkpoint restoration`,
134
- elapsed_time_ms: Date.now() - new Date(checkpoint.timestamp).getTime(),
135
- last_activity_at: checkpoint.timestamp
136
- },
137
- restored: { sorties: 0, locks: 0, messages: 0 },
138
- errors: [],
139
- warnings: []
140
- };
141
-
142
- try {
143
- if (!dryRun) {
144
- await this.db.beginTransaction();
145
- }
146
-
147
- // 1. Restore sortie states
148
- for (const sortie of checkpoint.sorties || []) {
149
- try {
150
- if (!dryRun) {
151
- await this.db.sorties.update(sortie.id, {
152
- status: sortie.status,
153
- assigned_to: sortie.assigned_to,
154
- files: sortie.files,
155
- progress: sortie.progress,
156
- progress_notes: sortie.progress_notes
157
- });
158
- }
159
- result.restored.sorties++;
160
- } catch (error) {
161
- result.errors.push(`Failed to restore sortie ${sortie.id}: ${error}`);
162
- }
163
- }
164
-
165
-
166
- for (const lock of checkpoint.active_locks || []) {
167
- try {
168
- const lockAge = Date.now() - new Date(lock.acquired_at).getTime();
169
- const isExpired = lockAge > (lock.timeout_ms || 30000);
170
-
171
- if (isExpired) {
172
- result.warnings.push(`Lock ${lock.id} expired, skipping re-acquisition`);
173
- result.recovery_context.blockers.push(`Expired lock on ${lock.file} (held by ${lock.held_by})`);
174
- continue;
175
- }
176
-
177
- if (!dryRun) {
178
- const lockResult = await this.db.locks.acquire({
179
- file: lock.file,
180
- specialist_id: lock.held_by,
181
- timeout_ms: lock.timeout_ms,
182
- purpose: lock.purpose
183
- });
184
-
185
- if (lockResult.conflict) {
186
- if (forceLocks) {
187
- await this.db.locks.forceRelease(lockResult.existing_lock?.id || '');
188
- await this.db.locks.acquire({
189
- file: lock.file,
190
- specialist_id: lock.held_by,
191
- timeout_ms: lock.timeout_ms,
192
- purpose: lock.purpose
193
- });
194
- } else {
195
- result.warnings.push(`Lock conflict on ${lock.file}, skipping re-acquisition`);
196
- result.recovery_context.blockers.push(`Lock conflict on ${lock.file} (held by ${lock.held_by})`);
197
- continue;
198
- }
199
- }
200
- }
201
- result.restored.locks++;
202
- } catch (error) {
203
- result.errors.push(`Failed to restore lock ${lock.id}: ${error}`);
204
- }
205
- }
206
-
207
- // 3. Requeue pending messages
208
- for (const message of checkpoint.pending_messages || []) {
209
- try {
210
- if (!message.delivered) {
211
- if (!dryRun) {
212
- await this.db.messages.requeue(message.id);
213
- }
214
- result.restored.messages++;
215
- }
216
- } catch (error) {
217
- result.errors.push(`Failed to requeue message ${message.id}: ${error}`);
218
- }
219
- }
220
-
221
- // 4. Mark checkpoint as consumed (only if not dry run and successful)
222
- if (!dryRun && result.errors.length === 0) {
223
- await this.db.checkpoints.markConsumed(checkpoint.id);
224
- }
225
-
226
- // 5. Emit fleet_recovered event
227
- if (!dryRun && result.errors.length === 0) {
228
- await this.db.events.append({
229
- event_type: 'fleet_recovered',
230
- stream_type: 'mission',
231
- stream_id: checkpoint.mission_id,
232
- data: {
233
- checkpoint_id: checkpoint.id,
234
- restored_at: now,
235
- sorties_restored: result.restored.sorties,
236
- locks_restored: result.restored.locks,
237
- messages_requeued: result.restored.messages,
238
- warnings: result.warnings.length
239
- },
240
- occurred_at: now
241
- });
242
- }
243
-
244
- if (!dryRun) {
245
- await this.db.commitTransaction();
246
- }
247
-
248
- return result;
249
- } catch (error) {
250
- if (!dryRun) {
251
- try {
252
- await this.db.rollbackTransaction();
253
- } catch (rollbackError) {
254
- result.errors.push(`Failed to rollback transaction: ${rollbackError}`);
255
- }
256
- }
257
-
258
- result.success = false;
259
- result.errors.push(`Transaction failed: ${error instanceof Error ? error.message : String(error)}`);
260
- return result;
261
- }
262
- }
263
-
264
- private extractNextSteps(checkpoint: Checkpoint): string[] {
265
- const steps: string[] = [];
266
-
267
- const inProgressSorties = checkpoint.sorties?.filter((s: any) => s.status === 'in_progress') || [];
268
- const blockedSorties = checkpoint.sorties?.filter((s: any) =>
269
- s.progress_notes && s.progress_notes.toLowerCase().includes('blocked')
270
- ) || [];
271
-
272
- if (inProgressSorties && inProgressSorties.length > 0) {
273
- steps.push('Continue work on in-progress sorties');
274
- }
275
-
276
- if (blockedSorties && blockedSorties.length > 0) {
277
- steps.push('Resolve blockers for stuck sorties');
278
- }
279
-
280
- if (checkpoint.active_locks && checkpoint.active_locks.length > 0) {
281
- steps.push('Verify file lock integrity');
282
- }
283
-
284
- if (checkpoint.pending_messages && checkpoint.pending_messages.length > 0) {
285
- steps.push('Process pending messages');
286
- }
287
-
288
- if (steps.length === 0) {
289
- steps.push('Review mission status and continue');
290
- }
291
-
292
- return steps;
293
- }
294
-
295
- private extractModifiedFiles(checkpoint: Checkpoint): string[] {
296
- const files: Set<string> = new Set();
297
-
298
- checkpoint.sorties?.forEach((sortie: any) => {
299
- sortie.files?.forEach((file: any) => files.add(file));
300
- });
301
-
302
- checkpoint.active_locks?.forEach((lock: any) => {
303
- files.add(lock.file);
304
- });
305
-
306
- return Array.from(files);
307
- }
308
-
309
- formatRecoveryPrompt(result: RestoreResult): string {
310
- const { recovery_context, restored, errors, warnings } = result;
311
-
312
- const sections = [
313
- '# Mission Recovery Context',
314
- '',
315
- '## Mission Summary',
316
- recovery_context.mission_summary,
317
- '',
318
- '## Last Action',
319
- recovery_context.last_action,
320
- '',
321
- '## Time Context',
322
- `- Last activity: ${recovery_context.last_activity_at}`,
323
- `- Elapsed time: ${Math.round(recovery_context.elapsed_time_ms / 60000)} minutes`,
324
- '',
325
- '## Next Steps',
326
- ...recovery_context.next_steps.map((step: any) => `- ${step}`),
327
- ''
328
- ];
329
-
330
- if (recovery_context.files_modified.length > 0) {
331
- sections.push(
332
- '## Files Modified',
333
- ...recovery_context.files_modified.map((file: any) => `- ${file}`),
334
- ''
335
- );
336
- }
337
-
338
- if (recovery_context.blockers.length > 0) {
339
- sections.push(
340
- '## Blockers',
341
- ...recovery_context.blockers.map((blocker: any) => `- ${blocker}`),
342
- ''
343
- );
344
- }
345
-
346
- sections.push(
347
- '## Restoration Summary',
348
- `- Sorties restored: ${restored.sorties}`,
349
- `- Locks restored: ${restored.locks}`,
350
- `- Messages requeued: ${restored.messages}`,
351
- ''
352
- );
353
-
354
- if (warnings.length > 0) {
355
- sections.push(
356
- '## Warnings',
357
- ...warnings.map(warning => `- ${warning}`),
358
- ''
359
- );
360
- }
361
-
362
- if (errors.length > 0) {
363
- sections.push(
364
- '## Errors',
365
- ...errors.map(error => `- ${error}`),
366
- ''
367
- );
368
- }
369
-
370
- sections.push(
371
- '---',
372
- '*This context was automatically generated from checkpoint restoration.*'
373
- );
374
-
375
- return sections.join('\n');
376
- }
377
- }