@esotech/contextuate 2.0.0 → 2.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.
Files changed (105) hide show
  1. package/README.md +169 -1
  2. package/dist/commands/claude.d.ts +21 -0
  3. package/dist/commands/claude.js +213 -0
  4. package/dist/commands/context.d.ts +1 -0
  5. package/dist/commands/create.d.ts +3 -0
  6. package/dist/commands/index.d.ts +4 -0
  7. package/dist/commands/init.d.ts +7 -0
  8. package/dist/commands/init.js +67 -6
  9. package/dist/commands/install.d.ts +28 -0
  10. package/dist/commands/install.js +116 -11
  11. package/dist/commands/monitor.d.ts +55 -0
  12. package/dist/commands/monitor.js +1007 -0
  13. package/dist/commands/remove.d.ts +3 -0
  14. package/dist/commands/run.d.ts +6 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +113 -1
  17. package/dist/monitor/daemon/circuit-breaker.d.ts +121 -0
  18. package/dist/monitor/daemon/circuit-breaker.js +552 -0
  19. package/dist/monitor/daemon/cli.d.ts +8 -0
  20. package/dist/monitor/daemon/cli.js +82 -0
  21. package/dist/monitor/daemon/index.d.ts +137 -0
  22. package/dist/monitor/daemon/index.js +695 -0
  23. package/dist/monitor/daemon/notifier.d.ts +25 -0
  24. package/dist/monitor/daemon/notifier.js +98 -0
  25. package/dist/monitor/daemon/processor.d.ts +89 -0
  26. package/dist/monitor/daemon/processor.js +455 -0
  27. package/dist/monitor/daemon/state.d.ts +80 -0
  28. package/dist/monitor/daemon/state.js +162 -0
  29. package/dist/monitor/daemon/watcher.d.ts +47 -0
  30. package/dist/monitor/daemon/watcher.js +171 -0
  31. package/dist/monitor/daemon/wrapper-manager.d.ts +106 -0
  32. package/dist/monitor/daemon/wrapper-manager.js +374 -0
  33. package/dist/monitor/hooks/emit-event.js +652 -0
  34. package/dist/monitor/persistence/file-store.d.ts +88 -0
  35. package/dist/monitor/persistence/file-store.js +335 -0
  36. package/dist/monitor/persistence/index.d.ts +7 -0
  37. package/dist/monitor/persistence/index.js +10 -0
  38. package/dist/monitor/server/adapters/redis.d.ts +38 -0
  39. package/dist/monitor/server/adapters/redis.js +213 -0
  40. package/dist/monitor/server/adapters/unix-socket.d.ts +33 -0
  41. package/dist/monitor/server/adapters/unix-socket.js +182 -0
  42. package/dist/monitor/server/broker.d.ts +135 -0
  43. package/dist/monitor/server/broker.js +475 -0
  44. package/dist/monitor/server/cli.d.ts +8 -0
  45. package/dist/monitor/server/cli.js +98 -0
  46. package/dist/monitor/server/fastify.d.ts +16 -0
  47. package/dist/monitor/server/fastify.js +184 -0
  48. package/dist/monitor/server/index.d.ts +36 -0
  49. package/dist/monitor/server/index.js +153 -0
  50. package/dist/monitor/server/websocket.d.ts +80 -0
  51. package/dist/monitor/server/websocket.js +453 -0
  52. package/dist/monitor/ui/assets/index-4IssW9On.js +59 -0
  53. package/dist/monitor/ui/assets/index-vo9hLe5R.css +32 -0
  54. package/dist/monitor/ui/favicon.png +0 -0
  55. package/dist/monitor/ui/index.html +14 -0
  56. package/dist/monitor/ui/logo.png +0 -0
  57. package/dist/monitor/ui/logo.svg +1 -0
  58. package/dist/runtime/driver.d.ts +16 -0
  59. package/dist/runtime/tools.d.ts +10 -0
  60. package/dist/templates/README.md +33 -7
  61. package/dist/templates/agents/aegis.md +4 -0
  62. package/dist/templates/agents/archon.md +13 -22
  63. package/dist/templates/agents/atlas.md +4 -0
  64. package/dist/templates/agents/canvas.md +4 -0
  65. package/dist/templates/agents/chronicle.md +4 -0
  66. package/dist/templates/agents/chronos.md +4 -0
  67. package/dist/templates/agents/cipher.md +4 -0
  68. package/dist/templates/agents/crucible.md +4 -0
  69. package/dist/templates/agents/echo.md +4 -0
  70. package/dist/templates/agents/forge.md +4 -0
  71. package/dist/templates/agents/ledger.md +4 -0
  72. package/dist/templates/agents/meridian.md +4 -0
  73. package/dist/templates/agents/nexus.md +4 -0
  74. package/dist/templates/agents/pythia.md +217 -0
  75. package/dist/templates/agents/scribe.md +4 -0
  76. package/dist/templates/agents/sentinel.md +4 -0
  77. package/dist/templates/agents/{oracle.md → thoth.md} +11 -7
  78. package/dist/templates/agents/unity.md +4 -0
  79. package/dist/templates/agents/vox.md +4 -0
  80. package/dist/templates/agents/weaver.md +4 -0
  81. package/dist/templates/framework-agents/documentation-expert.md +3 -3
  82. package/dist/templates/framework-agents/tools-expert.md +8 -8
  83. package/dist/templates/skills/consult.md +138 -0
  84. package/dist/templates/skills/orchestrate.md +173 -0
  85. package/dist/templates/skills/pythia.md +37 -0
  86. package/dist/templates/standards/agent-roles.md +68 -21
  87. package/dist/templates/standards/coding-standards.md +9 -26
  88. package/dist/templates/templates/context.md +17 -2
  89. package/dist/templates/templates/contextuate.md +21 -28
  90. package/dist/templates/templates/standards/go.md +167 -0
  91. package/dist/templates/templates/standards/java.md +167 -0
  92. package/dist/templates/templates/standards/javascript.md +292 -0
  93. package/dist/templates/templates/standards/php.md +181 -0
  94. package/dist/templates/templates/standards/python.md +175 -0
  95. package/dist/templates/tools/agent-creator.md +252 -0
  96. package/dist/templates/tools/agent-creator.tool.md +2 -2
  97. package/dist/templates/tools/quickref.md +216 -0
  98. package/dist/templates/tools/spawn.md +31 -0
  99. package/dist/templates/tools/standards-detector.md +301 -0
  100. package/dist/templates/version.json +1 -1
  101. package/dist/types/monitor.d.ts +660 -0
  102. package/dist/types/monitor.js +75 -0
  103. package/dist/utils/git.d.ts +9 -0
  104. package/dist/utils/tokens.d.ts +10 -0
  105. package/package.json +18 -5
@@ -0,0 +1,88 @@
1
+ /**
2
+ * File-based Persistence Store
3
+ *
4
+ * Stores session data and events in JSON files.
5
+ * Uses JSON Lines format for event logs (one JSON object per line).
6
+ */
7
+ import type { PersistenceStore, MonitorEvent, SessionMeta, SessionStatus } from '../../types/monitor';
8
+ export interface FileStoreOptions {
9
+ baseDir?: string;
10
+ }
11
+ export declare class FileStore implements PersistenceStore {
12
+ private paths;
13
+ private baseDir;
14
+ private sessionsDir;
15
+ private processedDir;
16
+ constructor(options?: FileStoreOptions);
17
+ /**
18
+ * Initialize the store - create directories if needed
19
+ */
20
+ init(): Promise<void>;
21
+ /**
22
+ * Close the store (no-op for file store)
23
+ */
24
+ close(): Promise<void>;
25
+ /**
26
+ * Save an event to the session's event log
27
+ */
28
+ saveEvent(event: MonitorEvent): Promise<void>;
29
+ /**
30
+ * Get events for a session
31
+ */
32
+ getEvents(sessionId: string, options?: {
33
+ limit?: number;
34
+ before?: number;
35
+ after?: number;
36
+ }): Promise<MonitorEvent[]>;
37
+ /**
38
+ * Get a single event by ID
39
+ */
40
+ getEventById(sessionId: string, eventId: string): Promise<MonitorEvent | null>;
41
+ /**
42
+ * Save session metadata
43
+ */
44
+ saveSession(session: SessionMeta): Promise<void>;
45
+ /**
46
+ * Get session metadata
47
+ */
48
+ getSession(sessionId: string): Promise<SessionMeta | null>;
49
+ /**
50
+ * Get all sessions
51
+ */
52
+ getSessions(options?: {
53
+ status?: SessionStatus;
54
+ limit?: number;
55
+ }): Promise<SessionMeta[]>;
56
+ /**
57
+ * Update session metadata
58
+ */
59
+ updateSession(sessionId: string, updates: Partial<SessionMeta>): Promise<void>;
60
+ /**
61
+ * Delete a session
62
+ */
63
+ deleteSession(sessionId: string): Promise<void>;
64
+ /**
65
+ * Delete all sessions
66
+ */
67
+ deleteAllSessions(): Promise<void>;
68
+ /**
69
+ * Get all recent events across all sessions
70
+ */
71
+ getAllRecentEvents(limit?: number): Promise<MonitorEvent[]>;
72
+ /**
73
+ * Delete old sessions
74
+ */
75
+ pruneOldSessions(olderThan: number): Promise<number>;
76
+ /**
77
+ * Get event count for a session
78
+ */
79
+ getEventCount(sessionId: string): Promise<number>;
80
+ /**
81
+ * Get the base directory path
82
+ */
83
+ getBaseDir(): string;
84
+ /**
85
+ * Get list of processed event files (for recovery)
86
+ */
87
+ getProcessedFiles(): Promise<string[]>;
88
+ }
@@ -0,0 +1,335 @@
1
+ "use strict";
2
+ /**
3
+ * File-based Persistence Store
4
+ *
5
+ * Stores session data and events in JSON files.
6
+ * Uses JSON Lines format for event logs (one JSON object per line).
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.FileStore = void 0;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const readline = __importStar(require("readline"));
46
+ const monitor_js_1 = require("../../types/monitor.js");
47
+ class FileStore {
48
+ constructor(options) {
49
+ this.paths = (0, monitor_js_1.getDefaultMonitorPaths)();
50
+ this.baseDir = options?.baseDir || this.paths.baseDir;
51
+ this.sessionsDir = path.join(this.baseDir, 'sessions');
52
+ this.processedDir = path.join(this.baseDir, 'processed');
53
+ }
54
+ /**
55
+ * Initialize the store - create directories if needed
56
+ */
57
+ async init() {
58
+ await fs.promises.mkdir(this.sessionsDir, { recursive: true });
59
+ await fs.promises.mkdir(this.processedDir, { recursive: true });
60
+ console.log(`[FileStore] Initialized at ${this.baseDir}`);
61
+ }
62
+ /**
63
+ * Close the store (no-op for file store)
64
+ */
65
+ async close() {
66
+ // Nothing to clean up
67
+ }
68
+ /**
69
+ * Save an event to the session's event log
70
+ */
71
+ async saveEvent(event) {
72
+ const sessionDir = path.join(this.sessionsDir, event.sessionId);
73
+ await fs.promises.mkdir(sessionDir, { recursive: true });
74
+ const eventsFile = path.join(sessionDir, 'events.jsonl');
75
+ const line = JSON.stringify(event) + '\n';
76
+ await fs.promises.appendFile(eventsFile, line);
77
+ }
78
+ /**
79
+ * Get events for a session
80
+ */
81
+ async getEvents(sessionId, options) {
82
+ const eventsFile = path.join(this.sessionsDir, sessionId, 'events.jsonl');
83
+ if (!fs.existsSync(eventsFile)) {
84
+ return [];
85
+ }
86
+ const events = [];
87
+ const limit = options?.limit || 1000;
88
+ const fileStream = fs.createReadStream(eventsFile);
89
+ const rl = readline.createInterface({
90
+ input: fileStream,
91
+ crlfDelay: Infinity,
92
+ });
93
+ for await (const line of rl) {
94
+ if (!line.trim())
95
+ continue;
96
+ try {
97
+ const event = JSON.parse(line);
98
+ // Apply filters
99
+ if (options?.before && event.timestamp >= options.before)
100
+ continue;
101
+ if (options?.after && event.timestamp <= options.after)
102
+ continue;
103
+ events.push(event);
104
+ }
105
+ catch (err) {
106
+ console.error('[FileStore] Failed to parse event line:', err);
107
+ }
108
+ }
109
+ // Sort by timestamp and limit
110
+ events.sort((a, b) => a.timestamp - b.timestamp);
111
+ if (events.length > limit) {
112
+ return events.slice(-limit);
113
+ }
114
+ return events;
115
+ }
116
+ /**
117
+ * Get a single event by ID
118
+ */
119
+ async getEventById(sessionId, eventId) {
120
+ const eventsFile = path.join(this.sessionsDir, sessionId, 'events.jsonl');
121
+ if (!fs.existsSync(eventsFile)) {
122
+ return null;
123
+ }
124
+ const content = await fs.promises.readFile(eventsFile, 'utf8');
125
+ const lines = content.split('\n');
126
+ for (const line of lines) {
127
+ if (!line.trim())
128
+ continue;
129
+ try {
130
+ const event = JSON.parse(line);
131
+ if (event.id === eventId) {
132
+ return event;
133
+ }
134
+ }
135
+ catch (err) {
136
+ // Skip malformed lines
137
+ }
138
+ }
139
+ return null;
140
+ }
141
+ /**
142
+ * Save session metadata
143
+ */
144
+ async saveSession(session) {
145
+ const sessionDir = path.join(this.sessionsDir, session.sessionId);
146
+ await fs.promises.mkdir(sessionDir, { recursive: true });
147
+ const metaFile = path.join(sessionDir, 'meta.json');
148
+ await fs.promises.writeFile(metaFile, JSON.stringify(session, null, 2));
149
+ }
150
+ /**
151
+ * Get session metadata
152
+ */
153
+ async getSession(sessionId) {
154
+ const metaFile = path.join(this.sessionsDir, sessionId, 'meta.json');
155
+ if (!fs.existsSync(metaFile)) {
156
+ return null;
157
+ }
158
+ try {
159
+ const content = await fs.promises.readFile(metaFile, 'utf-8');
160
+ return JSON.parse(content);
161
+ }
162
+ catch (err) {
163
+ console.error('[FileStore] Failed to read session:', err);
164
+ return null;
165
+ }
166
+ }
167
+ /**
168
+ * Get all sessions
169
+ */
170
+ async getSessions(options) {
171
+ if (!fs.existsSync(this.sessionsDir)) {
172
+ return [];
173
+ }
174
+ const sessions = [];
175
+ const entries = await fs.promises.readdir(this.sessionsDir, { withFileTypes: true });
176
+ for (const entry of entries) {
177
+ if (!entry.isDirectory())
178
+ continue;
179
+ const session = await this.getSession(entry.name);
180
+ if (!session)
181
+ continue;
182
+ // Apply status filter
183
+ if (options?.status && session.status !== options.status)
184
+ continue;
185
+ sessions.push(session);
186
+ }
187
+ // Sort by start time (newest first)
188
+ sessions.sort((a, b) => b.startTime - a.startTime);
189
+ // Apply limit
190
+ if (options?.limit && sessions.length > options.limit) {
191
+ return sessions.slice(0, options.limit);
192
+ }
193
+ return sessions;
194
+ }
195
+ /**
196
+ * Update session metadata
197
+ */
198
+ async updateSession(sessionId, updates) {
199
+ const session = await this.getSession(sessionId);
200
+ if (!session) {
201
+ throw new Error(`Session not found: ${sessionId}`);
202
+ }
203
+ const updated = { ...session, ...updates };
204
+ await this.saveSession(updated);
205
+ }
206
+ /**
207
+ * Delete a session
208
+ */
209
+ async deleteSession(sessionId) {
210
+ const sessionDir = path.join(this.sessionsDir, sessionId);
211
+ if (!fs.existsSync(sessionDir)) {
212
+ throw new Error(`Session not found: ${sessionId}`);
213
+ }
214
+ try {
215
+ await fs.promises.rm(sessionDir, { recursive: true });
216
+ console.log(`[FileStore] Deleted session: ${sessionId}`);
217
+ }
218
+ catch (err) {
219
+ console.error(`[FileStore] Failed to delete session ${sessionId}:`, err);
220
+ throw err;
221
+ }
222
+ }
223
+ /**
224
+ * Delete all sessions
225
+ */
226
+ async deleteAllSessions() {
227
+ if (!fs.existsSync(this.sessionsDir)) {
228
+ return;
229
+ }
230
+ try {
231
+ const entries = await fs.promises.readdir(this.sessionsDir, { withFileTypes: true });
232
+ let deleted = 0;
233
+ for (const entry of entries) {
234
+ if (!entry.isDirectory())
235
+ continue;
236
+ const sessionDir = path.join(this.sessionsDir, entry.name);
237
+ try {
238
+ await fs.promises.rm(sessionDir, { recursive: true });
239
+ deleted++;
240
+ }
241
+ catch (err) {
242
+ console.error(`[FileStore] Failed to delete session ${entry.name}:`, err);
243
+ }
244
+ }
245
+ console.log(`[FileStore] Deleted all sessions (${deleted} total)`);
246
+ }
247
+ catch (err) {
248
+ console.error('[FileStore] Failed to delete all sessions:', err);
249
+ throw err;
250
+ }
251
+ }
252
+ /**
253
+ * Get all recent events across all sessions
254
+ */
255
+ async getAllRecentEvents(limit = 200) {
256
+ if (!fs.existsSync(this.sessionsDir)) {
257
+ return [];
258
+ }
259
+ const allEvents = [];
260
+ const entries = await fs.promises.readdir(this.sessionsDir, { withFileTypes: true });
261
+ // Read events from all sessions
262
+ for (const entry of entries) {
263
+ if (!entry.isDirectory())
264
+ continue;
265
+ const sessionEvents = await this.getEvents(entry.name, { limit: 100 });
266
+ allEvents.push(...sessionEvents);
267
+ }
268
+ // Sort by timestamp (newest first) and limit
269
+ allEvents.sort((a, b) => b.timestamp - a.timestamp);
270
+ return allEvents.slice(0, limit);
271
+ }
272
+ /**
273
+ * Delete old sessions
274
+ */
275
+ async pruneOldSessions(olderThan) {
276
+ const sessions = await this.getSessions();
277
+ let pruned = 0;
278
+ for (const session of sessions) {
279
+ const endTime = session.endTime || session.startTime;
280
+ if (endTime < olderThan) {
281
+ const sessionDir = path.join(this.sessionsDir, session.sessionId);
282
+ try {
283
+ await fs.promises.rm(sessionDir, { recursive: true });
284
+ pruned++;
285
+ }
286
+ catch (err) {
287
+ console.error(`[FileStore] Failed to prune session ${session.sessionId}:`, err);
288
+ }
289
+ }
290
+ }
291
+ if (pruned > 0) {
292
+ console.log(`[FileStore] Pruned ${pruned} old sessions`);
293
+ }
294
+ return pruned;
295
+ }
296
+ /**
297
+ * Get event count for a session
298
+ */
299
+ async getEventCount(sessionId) {
300
+ const eventsFile = path.join(this.sessionsDir, sessionId, 'events.jsonl');
301
+ if (!fs.existsSync(eventsFile)) {
302
+ return 0;
303
+ }
304
+ let count = 0;
305
+ const fileStream = fs.createReadStream(eventsFile);
306
+ const rl = readline.createInterface({
307
+ input: fileStream,
308
+ crlfDelay: Infinity,
309
+ });
310
+ for await (const line of rl) {
311
+ if (line.trim())
312
+ count++;
313
+ }
314
+ return count;
315
+ }
316
+ /**
317
+ * Get the base directory path
318
+ */
319
+ getBaseDir() {
320
+ return this.baseDir;
321
+ }
322
+ /**
323
+ * Get list of processed event files (for recovery)
324
+ */
325
+ async getProcessedFiles() {
326
+ try {
327
+ const files = await fs.promises.readdir(this.processedDir);
328
+ return files.filter(f => f.endsWith('.json')).sort();
329
+ }
330
+ catch {
331
+ return [];
332
+ }
333
+ }
334
+ }
335
+ exports.FileStore = FileStore;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Persistence Module
3
+ *
4
+ * Exports persistence store implementations.
5
+ */
6
+ export { FileStore, type FileStoreOptions } from './file-store';
7
+ export type { PersistenceStore } from '../../types/monitor';
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * Persistence Module
4
+ *
5
+ * Exports persistence store implementations.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.FileStore = void 0;
9
+ var file_store_1 = require("./file-store");
10
+ Object.defineProperty(exports, "FileStore", { enumerable: true, get: function () { return file_store_1.FileStore; } });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Redis Adapter
3
+ *
4
+ * Uses Redis pub/sub for distributed event handling.
5
+ * Allows multiple monitor instances and remote hook scripts.
6
+ */
7
+ import type { IPCAdapter, EventHandler, MonitorEvent, RedisConfig } from '../../../types/monitor';
8
+ export interface RedisAdapterOptions {
9
+ config: RedisConfig;
10
+ }
11
+ export declare class RedisAdapter implements IPCAdapter {
12
+ private subscriber;
13
+ private publisher;
14
+ private handlers;
15
+ private running;
16
+ private config;
17
+ constructor(options: RedisAdapterOptions);
18
+ start(): Promise<void>;
19
+ stop(): Promise<void>;
20
+ onEvent(handler: EventHandler): void;
21
+ offEvent(handler: EventHandler): void;
22
+ isRunning(): boolean;
23
+ private emitEvent;
24
+ /**
25
+ * Publish an event to the Redis channel
26
+ * This is used by hook scripts in Redis mode
27
+ */
28
+ publishEvent(event: MonitorEvent): Promise<void>;
29
+ /**
30
+ * Get the Redis configuration for clients
31
+ */
32
+ getConfig(): RedisConfig;
33
+ }
34
+ /**
35
+ * Create a standalone publisher for hook scripts
36
+ * This doesn't subscribe, just publishes events
37
+ */
38
+ export declare function publishEventToRedis(config: RedisConfig, event: MonitorEvent): Promise<void>;
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ /**
3
+ * Redis Adapter
4
+ *
5
+ * Uses Redis pub/sub for distributed event handling.
6
+ * Allows multiple monitor instances and remote hook scripts.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.RedisAdapter = void 0;
43
+ exports.publishEventToRedis = publishEventToRedis;
44
+ // Dynamic import for ioredis to make it optional
45
+ let Redis;
46
+ class RedisAdapter {
47
+ constructor(options) {
48
+ this.subscriber = null;
49
+ this.publisher = null;
50
+ this.handlers = new Set();
51
+ this.running = false;
52
+ this.config = options.config;
53
+ }
54
+ async start() {
55
+ if (this.running) {
56
+ return;
57
+ }
58
+ // Dynamically import ioredis
59
+ try {
60
+ const ioredis = await Promise.resolve().then(() => __importStar(require('ioredis')));
61
+ Redis = ioredis.default;
62
+ }
63
+ catch (err) {
64
+ throw new Error('Redis adapter requires ioredis package. Install with: npm install ioredis');
65
+ }
66
+ const redisOptions = {
67
+ host: this.config.host,
68
+ port: this.config.port,
69
+ password: this.config.password || undefined,
70
+ retryStrategy: (times) => {
71
+ const delay = Math.min(times * 100, 3000);
72
+ console.log(`[Redis] Reconnecting in ${delay}ms (attempt ${times}/10)...`);
73
+ if (times > 10) {
74
+ console.error('[Redis] Max reconnection attempts reached');
75
+ return null; // Stop retrying
76
+ }
77
+ return delay;
78
+ },
79
+ lazyConnect: true, // Don't auto-connect, we'll do it manually
80
+ };
81
+ // Create subscriber connection
82
+ this.subscriber = new Redis(redisOptions);
83
+ this.publisher = new Redis(redisOptions);
84
+ // Set up error handlers before connecting
85
+ this.subscriber.on('error', (err) => {
86
+ console.error('[Redis Subscriber] Connection error:', err.message);
87
+ });
88
+ this.publisher.on('error', (err) => {
89
+ console.error('[Redis Publisher] Connection error:', err.message);
90
+ });
91
+ this.subscriber.on('reconnecting', () => {
92
+ console.log('[Redis Subscriber] Reconnecting...');
93
+ });
94
+ this.publisher.on('reconnecting', () => {
95
+ console.log('[Redis Publisher] Reconnecting...');
96
+ });
97
+ // Connect both clients
98
+ try {
99
+ await Promise.all([
100
+ this.subscriber.connect(),
101
+ this.publisher.connect(),
102
+ ]);
103
+ }
104
+ catch (err) {
105
+ // Clean up on connection failure
106
+ this.subscriber.disconnect();
107
+ this.publisher.disconnect();
108
+ throw new Error(`Failed to connect to Redis: ${err instanceof Error ? err.message : String(err)}`);
109
+ }
110
+ // Subscribe to the channel
111
+ await this.subscriber.subscribe(this.config.channel);
112
+ // Handle incoming messages
113
+ this.subscriber.on('message', (channel, message) => {
114
+ if (channel === this.config.channel) {
115
+ try {
116
+ const event = JSON.parse(message);
117
+ console.log(`[Redis] Received event from ${event.machineId}/${event.sessionId}: ${event.eventType}`);
118
+ this.emitEvent(event);
119
+ }
120
+ catch (err) {
121
+ console.error('[Redis] Failed to parse event:', err);
122
+ }
123
+ }
124
+ });
125
+ this.running = true;
126
+ console.log(`[Redis] Connected to ${this.config.host}:${this.config.port}, channel: ${this.config.channel}`);
127
+ console.log('[Redis] Listening for events from multiple sources...');
128
+ }
129
+ async stop() {
130
+ if (!this.running) {
131
+ return;
132
+ }
133
+ if (this.subscriber) {
134
+ await this.subscriber.unsubscribe(this.config.channel);
135
+ this.subscriber.disconnect();
136
+ this.subscriber = null;
137
+ }
138
+ if (this.publisher) {
139
+ this.publisher.disconnect();
140
+ this.publisher = null;
141
+ }
142
+ this.running = false;
143
+ console.log('[Redis] Disconnected');
144
+ }
145
+ onEvent(handler) {
146
+ this.handlers.add(handler);
147
+ }
148
+ offEvent(handler) {
149
+ this.handlers.delete(handler);
150
+ }
151
+ isRunning() {
152
+ return this.running;
153
+ }
154
+ emitEvent(event) {
155
+ for (const handler of this.handlers) {
156
+ try {
157
+ const result = handler(event);
158
+ if (result instanceof Promise) {
159
+ result.catch((err) => {
160
+ console.error('[Redis] Event handler error:', err);
161
+ });
162
+ }
163
+ }
164
+ catch (err) {
165
+ console.error('[Redis] Event handler error:', err);
166
+ }
167
+ }
168
+ }
169
+ /**
170
+ * Publish an event to the Redis channel
171
+ * This is used by hook scripts in Redis mode
172
+ */
173
+ async publishEvent(event) {
174
+ if (!this.publisher) {
175
+ throw new Error('Redis adapter not started');
176
+ }
177
+ const message = JSON.stringify(event);
178
+ const subscriberCount = await this.publisher.publish(this.config.channel, message);
179
+ console.log(`[Redis] Published event ${event.eventType} from ${event.machineId}/${event.sessionId} (${subscriberCount} subscribers)`);
180
+ }
181
+ /**
182
+ * Get the Redis configuration for clients
183
+ */
184
+ getConfig() {
185
+ return this.config;
186
+ }
187
+ }
188
+ exports.RedisAdapter = RedisAdapter;
189
+ /**
190
+ * Create a standalone publisher for hook scripts
191
+ * This doesn't subscribe, just publishes events
192
+ */
193
+ async function publishEventToRedis(config, event) {
194
+ let RedisClient;
195
+ try {
196
+ const ioredis = await Promise.resolve().then(() => __importStar(require('ioredis')));
197
+ RedisClient = ioredis.default;
198
+ }
199
+ catch (err) {
200
+ throw new Error('Redis requires ioredis package');
201
+ }
202
+ const client = new RedisClient({
203
+ host: config.host,
204
+ port: config.port,
205
+ password: config.password || undefined,
206
+ });
207
+ try {
208
+ await client.publish(config.channel, JSON.stringify(event));
209
+ }
210
+ finally {
211
+ client.disconnect();
212
+ }
213
+ }