aegis-bridge 2.2.2

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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +244 -0
  3. package/dashboard/dist/assets/index-CijFoeRu.css +32 -0
  4. package/dashboard/dist/assets/index-QtT4j0ht.js +262 -0
  5. package/dashboard/dist/index.html +14 -0
  6. package/dist/auth.d.ts +76 -0
  7. package/dist/auth.js +219 -0
  8. package/dist/channels/index.d.ts +8 -0
  9. package/dist/channels/index.js +9 -0
  10. package/dist/channels/manager.d.ts +39 -0
  11. package/dist/channels/manager.js +101 -0
  12. package/dist/channels/telegram-style.d.ts +118 -0
  13. package/dist/channels/telegram-style.js +203 -0
  14. package/dist/channels/telegram.d.ts +76 -0
  15. package/dist/channels/telegram.js +1396 -0
  16. package/dist/channels/types.d.ts +77 -0
  17. package/dist/channels/types.js +9 -0
  18. package/dist/channels/webhook.d.ts +58 -0
  19. package/dist/channels/webhook.js +162 -0
  20. package/dist/cli.d.ts +8 -0
  21. package/dist/cli.js +223 -0
  22. package/dist/config.d.ts +60 -0
  23. package/dist/config.js +188 -0
  24. package/dist/dashboard/assets/index-CijFoeRu.css +32 -0
  25. package/dist/dashboard/assets/index-QtT4j0ht.js +262 -0
  26. package/dist/dashboard/index.html +14 -0
  27. package/dist/events.d.ts +86 -0
  28. package/dist/events.js +258 -0
  29. package/dist/hook-settings.d.ts +67 -0
  30. package/dist/hook-settings.js +138 -0
  31. package/dist/hook.d.ts +18 -0
  32. package/dist/hook.js +199 -0
  33. package/dist/hooks.d.ts +32 -0
  34. package/dist/hooks.js +279 -0
  35. package/dist/jsonl-watcher.d.ts +57 -0
  36. package/dist/jsonl-watcher.js +159 -0
  37. package/dist/mcp-server.d.ts +60 -0
  38. package/dist/mcp-server.js +788 -0
  39. package/dist/metrics.d.ts +104 -0
  40. package/dist/metrics.js +226 -0
  41. package/dist/monitor.d.ts +84 -0
  42. package/dist/monitor.js +553 -0
  43. package/dist/permission-guard.d.ts +51 -0
  44. package/dist/permission-guard.js +197 -0
  45. package/dist/pipeline.d.ts +84 -0
  46. package/dist/pipeline.js +218 -0
  47. package/dist/screenshot.d.ts +26 -0
  48. package/dist/screenshot.js +57 -0
  49. package/dist/server.d.ts +10 -0
  50. package/dist/server.js +1577 -0
  51. package/dist/session.d.ts +297 -0
  52. package/dist/session.js +1275 -0
  53. package/dist/sse-limiter.d.ts +47 -0
  54. package/dist/sse-limiter.js +62 -0
  55. package/dist/sse-writer.d.ts +31 -0
  56. package/dist/sse-writer.js +95 -0
  57. package/dist/ssrf.d.ts +57 -0
  58. package/dist/ssrf.js +169 -0
  59. package/dist/swarm-monitor.d.ts +114 -0
  60. package/dist/swarm-monitor.js +267 -0
  61. package/dist/terminal-parser.d.ts +16 -0
  62. package/dist/terminal-parser.js +343 -0
  63. package/dist/tmux.d.ts +161 -0
  64. package/dist/tmux.js +725 -0
  65. package/dist/transcript.d.ts +47 -0
  66. package/dist/transcript.js +244 -0
  67. package/dist/validation.d.ts +222 -0
  68. package/dist/validation.js +268 -0
  69. package/dist/ws-terminal.d.ts +32 -0
  70. package/dist/ws-terminal.js +297 -0
  71. package/package.json +71 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * jsonl-watcher.ts — fs.watch()-based JSONL file watcher.
3
+ *
4
+ * Replaces polling-based JSONL reading in the monitor with near-instant
5
+ * file change detection. Uses fs.watch() with debouncing to avoid
6
+ * duplicate events from rapid writes.
7
+ *
8
+ * Issue #84: Replace JSONL polling with fs.watch.
9
+ */
10
+ import { watch } from 'node:fs';
11
+ import { existsSync } from 'node:fs';
12
+ import { readNewEntries } from './transcript.js';
13
+ const DEFAULT_CONFIG = {
14
+ debounceMs: 100,
15
+ };
16
+ /**
17
+ * Watches JSONL files for changes and emits parsed entries.
18
+ *
19
+ * Usage:
20
+ * const watcher = new JsonlWatcher();
21
+ * watcher.onEntries((event) => { ... });
22
+ * watcher.watch('session-123', '/path/to/session.jsonl', 0);
23
+ * watcher.unwatch('session-123');
24
+ * watcher.destroy();
25
+ */
26
+ export class JsonlWatcher {
27
+ entries = new Map();
28
+ listeners = new Array();
29
+ config;
30
+ constructor(config) {
31
+ this.config = { ...DEFAULT_CONFIG, ...config };
32
+ }
33
+ /** Register a callback for new entries. */
34
+ onEntries(listener) {
35
+ this.listeners.push(listener);
36
+ return () => {
37
+ const idx = this.listeners.indexOf(listener);
38
+ if (idx >= 0)
39
+ this.listeners.splice(idx, 1);
40
+ };
41
+ }
42
+ /** Start watching a JSONL file for a session.
43
+ * @param initialOffset - byte offset to start reading from (usually 0 or current session.monitorOffset).
44
+ */
45
+ watch(sessionId, jsonlPath, initialOffset) {
46
+ // Don't double-watch
47
+ if (this.entries.has(sessionId)) {
48
+ this.unwatch(sessionId);
49
+ }
50
+ if (!existsSync(jsonlPath))
51
+ return;
52
+ const fsWatcher = watch(jsonlPath, (eventType) => {
53
+ // 'rename' can mean file was deleted or replaced — check if it still exists
54
+ if (eventType === 'rename') {
55
+ if (!existsSync(jsonlPath)) {
56
+ // File deleted — session likely ended, stop watching
57
+ this.unwatch(sessionId);
58
+ return;
59
+ }
60
+ // File was replaced (e.g. rotated) — re-read from current offset
61
+ this.scheduleRead(sessionId);
62
+ return;
63
+ }
64
+ // 'change' — new data written
65
+ this.scheduleRead(sessionId);
66
+ });
67
+ fsWatcher.on('error', (err) => {
68
+ console.error(`JsonlWatcher: error watching ${jsonlPath}:`, err.message);
69
+ // Stop watching on persistent errors
70
+ this.unwatch(sessionId);
71
+ });
72
+ this.entries.set(sessionId, {
73
+ sessionId,
74
+ jsonlPath,
75
+ fsWatcher,
76
+ debounceTimer: null,
77
+ offset: initialOffset,
78
+ });
79
+ }
80
+ /** Stop watching a session's JSONL file. */
81
+ unwatch(sessionId) {
82
+ const entry = this.entries.get(sessionId);
83
+ if (!entry)
84
+ return;
85
+ if (entry.debounceTimer) {
86
+ clearTimeout(entry.debounceTimer);
87
+ }
88
+ entry.fsWatcher.close();
89
+ this.entries.delete(sessionId);
90
+ }
91
+ /** Update the offset for a session (e.g. after manual read during discovery). */
92
+ setOffset(sessionId, offset) {
93
+ const entry = this.entries.get(sessionId);
94
+ if (entry) {
95
+ entry.offset = offset;
96
+ }
97
+ }
98
+ /** Check if a session is being watched. */
99
+ isWatching(sessionId) {
100
+ return this.entries.has(sessionId);
101
+ }
102
+ /** Get the current offset for a watched session. */
103
+ getOffset(sessionId) {
104
+ return this.entries.get(sessionId)?.offset;
105
+ }
106
+ /** Stop all watchers and clean up. */
107
+ destroy() {
108
+ for (const sessionId of this.entries.keys()) {
109
+ this.unwatch(sessionId);
110
+ }
111
+ this.listeners.length = 0;
112
+ }
113
+ /** Schedule a debounced read for a session. */
114
+ scheduleRead(sessionId) {
115
+ const entry = this.entries.get(sessionId);
116
+ if (!entry)
117
+ return;
118
+ if (entry.debounceTimer) {
119
+ clearTimeout(entry.debounceTimer);
120
+ }
121
+ entry.debounceTimer = setTimeout(() => {
122
+ entry.debounceTimer = null;
123
+ void this.readAndEmit(entry);
124
+ }, this.config.debounceMs);
125
+ }
126
+ /** Read new bytes from the JSONL file and emit entries. */
127
+ async readAndEmit(entry) {
128
+ // Session may have been removed during debounce
129
+ if (!this.entries.has(entry.sessionId))
130
+ return;
131
+ if (!existsSync(entry.jsonlPath)) {
132
+ this.unwatch(entry.sessionId);
133
+ return;
134
+ }
135
+ try {
136
+ const previousOffset = entry.offset;
137
+ const result = await readNewEntries(entry.jsonlPath, previousOffset);
138
+ entry.offset = result.newOffset;
139
+ if (result.entries.length > 0 || result.newOffset < previousOffset) {
140
+ // Detect truncation: newOffset went backwards
141
+ const truncated = result.newOffset < previousOffset;
142
+ // Emit to all listeners
143
+ const event = {
144
+ sessionId: entry.sessionId,
145
+ messages: result.entries,
146
+ newOffset: result.newOffset,
147
+ truncated,
148
+ };
149
+ for (const listener of this.listeners) {
150
+ listener(event);
151
+ }
152
+ }
153
+ }
154
+ catch {
155
+ // File may be temporarily unavailable — ignore
156
+ }
157
+ }
158
+ }
159
+ //# sourceMappingURL=jsonl-watcher.js.map
@@ -0,0 +1,60 @@
1
+ /**
2
+ * mcp-server.ts — MCP server mode for Aegis.
3
+ *
4
+ * Exposes Aegis session orchestration as MCP tools via stdio transport.
5
+ * CC sessions can natively discover and communicate with sibling sessions.
6
+ *
7
+ * Usage:
8
+ * aegis-bridge mcp # default port 9100
9
+ * aegis-bridge mcp --port 3000 # custom port
10
+ * claude mcp add --scope user aegis -- npx aegis-bridge mcp
11
+ *
12
+ * Issue #48: https://github.com/OneStepAt4time/aegis/issues/48
13
+ */
14
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
15
+ export declare class AegisClient {
16
+ private baseUrl;
17
+ private authToken?;
18
+ constructor(baseUrl: string, authToken?: string | undefined);
19
+ private validateSessionId;
20
+ private request;
21
+ listSessions(filter?: {
22
+ status?: string;
23
+ workDir?: string;
24
+ }): Promise<any[]>;
25
+ getSession(id: string): Promise<any>;
26
+ getHealth(id: string): Promise<any>;
27
+ getTranscript(id: string): Promise<any>;
28
+ sendMessage(id: string, text: string): Promise<any>;
29
+ createSession(opts: {
30
+ workDir: string;
31
+ name?: string;
32
+ prompt?: string;
33
+ }): Promise<any>;
34
+ killSession(id: string): Promise<any>;
35
+ approvePermission(id: string): Promise<any>;
36
+ rejectPermission(id: string): Promise<any>;
37
+ getServerHealth(): Promise<any>;
38
+ escapeSession(id: string): Promise<any>;
39
+ interruptSession(id: string): Promise<any>;
40
+ capturePane(id: string): Promise<any>;
41
+ getSessionMetrics(id: string): Promise<any>;
42
+ getSessionSummary(id: string): Promise<any>;
43
+ sendBash(id: string, command: string): Promise<any>;
44
+ sendCommand(id: string, command: string): Promise<any>;
45
+ getSessionLatency(id: string): Promise<any>;
46
+ batchCreateSessions(sessions: Array<{
47
+ workDir: string;
48
+ name?: string;
49
+ prompt?: string;
50
+ }>): Promise<any>;
51
+ listPipelines(): Promise<any>;
52
+ createPipeline(config: {
53
+ name: string;
54
+ workDir: string;
55
+ steps: any[];
56
+ }): Promise<any>;
57
+ getSwarm(): Promise<any>;
58
+ }
59
+ export declare function createMcpServer(aegisPort: number, authToken?: string): McpServer;
60
+ export declare function startMcpServer(port: number, authToken?: string): Promise<void>;