@northflare/runner 0.0.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 (154) hide show
  1. package/DEBUG_LOGGING.md +60 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION_PLAN.md +52 -0
  4. package/README.md +220 -0
  5. package/SDK_IMPLEMENTATION_GUIDE.md +1036 -0
  6. package/bin/northflare-runner +367 -0
  7. package/coverage/base.css +224 -0
  8. package/coverage/block-navigation.js +87 -0
  9. package/coverage/coverage-final.json +12 -0
  10. package/coverage/favicon.png +0 -0
  11. package/coverage/index.html +176 -0
  12. package/coverage/lib/index.html +116 -0
  13. package/coverage/lib/preload-script.js.html +964 -0
  14. package/coverage/prettify.css +1 -0
  15. package/coverage/prettify.js +2 -0
  16. package/coverage/sort-arrow-sprite.png +0 -0
  17. package/coverage/sorter.js +196 -0
  18. package/coverage/src/collections/index.html +116 -0
  19. package/coverage/src/collections/runner-messages.ts.html +312 -0
  20. package/coverage/src/components/claude-manager.ts.html +1290 -0
  21. package/coverage/src/components/index.html +146 -0
  22. package/coverage/src/components/message-handler.ts.html +730 -0
  23. package/coverage/src/components/repository-manager.ts.html +841 -0
  24. package/coverage/src/index.html +131 -0
  25. package/coverage/src/index.ts.html +448 -0
  26. package/coverage/src/runner.ts.html +1239 -0
  27. package/coverage/src/utils/config.ts.html +780 -0
  28. package/coverage/src/utils/console.ts.html +121 -0
  29. package/coverage/src/utils/index.html +161 -0
  30. package/coverage/src/utils/logger.ts.html +475 -0
  31. package/coverage/src/utils/status-line.ts.html +445 -0
  32. package/dist/collections/runner-messages.d.ts +52 -0
  33. package/dist/collections/runner-messages.d.ts.map +1 -0
  34. package/dist/collections/runner-messages.js +161 -0
  35. package/dist/collections/runner-messages.js.map +1 -0
  36. package/dist/components/claude-manager.d.ts +39 -0
  37. package/dist/components/claude-manager.d.ts.map +1 -0
  38. package/dist/components/claude-manager.js +783 -0
  39. package/dist/components/claude-manager.js.map +1 -0
  40. package/dist/components/claude-sdk-manager.d.ts +47 -0
  41. package/dist/components/claude-sdk-manager.d.ts.map +1 -0
  42. package/dist/components/claude-sdk-manager.js +1088 -0
  43. package/dist/components/claude-sdk-manager.js.map +1 -0
  44. package/dist/components/enhanced-repository-manager.d.ts +134 -0
  45. package/dist/components/enhanced-repository-manager.d.ts.map +1 -0
  46. package/dist/components/enhanced-repository-manager.js +602 -0
  47. package/dist/components/enhanced-repository-manager.js.map +1 -0
  48. package/dist/components/message-handler-sse.d.ts +46 -0
  49. package/dist/components/message-handler-sse.d.ts.map +1 -0
  50. package/dist/components/message-handler-sse.js +734 -0
  51. package/dist/components/message-handler-sse.js.map +1 -0
  52. package/dist/components/message-handler.d.ts +35 -0
  53. package/dist/components/message-handler.d.ts.map +1 -0
  54. package/dist/components/message-handler.js +689 -0
  55. package/dist/components/message-handler.js.map +1 -0
  56. package/dist/components/repository-manager.d.ts +51 -0
  57. package/dist/components/repository-manager.d.ts.map +1 -0
  58. package/dist/components/repository-manager.js +295 -0
  59. package/dist/components/repository-manager.js.map +1 -0
  60. package/dist/index.d.ts +9 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +166 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/runner-sse.d.ts +57 -0
  65. package/dist/runner-sse.d.ts.map +1 -0
  66. package/dist/runner-sse.js +698 -0
  67. package/dist/runner-sse.js.map +1 -0
  68. package/dist/runner.d.ts +51 -0
  69. package/dist/runner.d.ts.map +1 -0
  70. package/dist/runner.js +530 -0
  71. package/dist/runner.js.map +1 -0
  72. package/dist/services/RunnerAPIClient.d.ts +30 -0
  73. package/dist/services/RunnerAPIClient.d.ts.map +1 -0
  74. package/dist/services/RunnerAPIClient.js +112 -0
  75. package/dist/services/RunnerAPIClient.js.map +1 -0
  76. package/dist/services/SSEClient.d.ts +60 -0
  77. package/dist/services/SSEClient.d.ts.map +1 -0
  78. package/dist/services/SSEClient.js +204 -0
  79. package/dist/services/SSEClient.js.map +1 -0
  80. package/dist/types/claude.d.ts +45 -0
  81. package/dist/types/claude.d.ts.map +1 -0
  82. package/dist/types/claude.js +6 -0
  83. package/dist/types/claude.js.map +1 -0
  84. package/dist/types/index.d.ts +47 -0
  85. package/dist/types/index.d.ts.map +1 -0
  86. package/dist/types/index.js +23 -0
  87. package/dist/types/index.js.map +1 -0
  88. package/dist/types/messages.d.ts +31 -0
  89. package/dist/types/messages.d.ts.map +1 -0
  90. package/dist/types/messages.js +6 -0
  91. package/dist/types/messages.js.map +1 -0
  92. package/dist/types/runner-interface.d.ts +24 -0
  93. package/dist/types/runner-interface.d.ts.map +1 -0
  94. package/dist/types/runner-interface.js +6 -0
  95. package/dist/types/runner-interface.js.map +1 -0
  96. package/dist/utils/StateManager.d.ts +52 -0
  97. package/dist/utils/StateManager.d.ts.map +1 -0
  98. package/dist/utils/StateManager.js +162 -0
  99. package/dist/utils/StateManager.js.map +1 -0
  100. package/dist/utils/config.d.ts +41 -0
  101. package/dist/utils/config.d.ts.map +1 -0
  102. package/dist/utils/config.js +250 -0
  103. package/dist/utils/config.js.map +1 -0
  104. package/dist/utils/console.d.ts +11 -0
  105. package/dist/utils/console.d.ts.map +1 -0
  106. package/dist/utils/console.js +15 -0
  107. package/dist/utils/console.js.map +1 -0
  108. package/dist/utils/expand-env.d.ts +2 -0
  109. package/dist/utils/expand-env.d.ts.map +1 -0
  110. package/dist/utils/expand-env.js +20 -0
  111. package/dist/utils/expand-env.js.map +1 -0
  112. package/dist/utils/logger.d.ts +9 -0
  113. package/dist/utils/logger.d.ts.map +1 -0
  114. package/dist/utils/logger.js +108 -0
  115. package/dist/utils/logger.js.map +1 -0
  116. package/dist/utils/status-line.d.ts +37 -0
  117. package/dist/utils/status-line.d.ts.map +1 -0
  118. package/dist/utils/status-line.js +113 -0
  119. package/dist/utils/status-line.js.map +1 -0
  120. package/docs/claude-manager.md +91 -0
  121. package/exceptions.log +22 -0
  122. package/lib/preload-script.js +293 -0
  123. package/package.json +55 -0
  124. package/rejections.log +63 -0
  125. package/runner.log +488 -0
  126. package/src/components/claude-sdk-manager.ts +1354 -0
  127. package/src/components/enhanced-repository-manager.ts +823 -0
  128. package/src/components/message-handler-sse.ts +1011 -0
  129. package/src/components/repository-manager.ts +337 -0
  130. package/src/index.ts +166 -0
  131. package/src/runner-sse.ts +847 -0
  132. package/src/services/RunnerAPIClient.ts +135 -0
  133. package/src/services/SSEClient.ts +258 -0
  134. package/src/types/claude.ts +55 -0
  135. package/src/types/computer-name.d.ts +4 -0
  136. package/src/types/index.ts +63 -0
  137. package/src/types/messages.ts +39 -0
  138. package/src/types/runner-interface.ts +34 -0
  139. package/src/utils/StateManager.ts +187 -0
  140. package/src/utils/codex-sdk.js +448 -0
  141. package/src/utils/config.ts +315 -0
  142. package/src/utils/console.ts +13 -0
  143. package/src/utils/expand-env.ts +22 -0
  144. package/src/utils/logger.ts +131 -0
  145. package/src/utils/sdk-demo.js +34 -0
  146. package/src/utils/status-line.ts +121 -0
  147. package/test-debug.sh +26 -0
  148. package/tests/retry-strategies.test.ts +410 -0
  149. package/tests/sdk-integration.test.ts +329 -0
  150. package/tests/sdk-streaming.test.ts +1180 -0
  151. package/tests/setup.ts +5 -0
  152. package/tests/test-claude-manager.ts +120 -0
  153. package/tsconfig.json +36 -0
  154. package/vitest.config.ts +27 -0
@@ -0,0 +1,135 @@
1
+ /**
2
+ * HTTP API client for runner-specific endpoints
3
+ *
4
+ * Provides methods to interact with the server's runner API endpoints
5
+ * including fetching missed messages and other runner operations.
6
+ */
7
+
8
+ import { createLogger } from "../utils/logger";
9
+ import { RunnerConfig, RunnerMessage } from "../types";
10
+
11
+ const logger = createLogger("RunnerAPIClient");
12
+
13
+ export interface FetchMissedMessagesOptions {
14
+ since: Date | string;
15
+ limit?: number;
16
+ }
17
+
18
+ export class RunnerAPIClient {
19
+ private baseUrl: string;
20
+ private token: string;
21
+ private runnerId: string;
22
+
23
+ constructor(config: RunnerConfig) {
24
+ this.baseUrl = config.orchestratorUrl;
25
+ this.token = process.env["NORTHFLARE_RUNNER_TOKEN"] || "";
26
+ this.runnerId = config.runnerId || "";
27
+ }
28
+
29
+ /**
30
+ * Update the runner ID after registration
31
+ */
32
+ setRunnerId(runnerId: string): void {
33
+ this.runnerId = runnerId;
34
+ }
35
+
36
+ /**
37
+ * Fetch missed messages since a given timestamp
38
+ */
39
+ async fetchMissedMessages(
40
+ options: FetchMissedMessagesOptions
41
+ ): Promise<RunnerMessage[]> {
42
+ const since =
43
+ options.since instanceof Date
44
+ ? options.since.toISOString()
45
+ : options.since;
46
+ const limit = options.limit || 1000;
47
+
48
+ logger.debug(`Fetching missed messages since ${since} (limit: ${limit})`);
49
+
50
+ try {
51
+ const url = `${this.baseUrl}/api/runner/messages/since`;
52
+ const params = new URLSearchParams({
53
+ since,
54
+ limit: limit.toString(),
55
+ });
56
+
57
+ const response = await fetch(`${url}?${params}`, {
58
+ method: "GET",
59
+ headers: {
60
+ Authorization: `Bearer ${this.token}`,
61
+ "X-Runner-Id": this.runnerId,
62
+ "Content-Type": "application/json",
63
+ },
64
+ });
65
+
66
+ if (!response.ok) {
67
+ const error = await response.text();
68
+ throw new Error(
69
+ `Failed to fetch missed messages: ${response.status} - ${error}`
70
+ );
71
+ }
72
+
73
+ const data = (await response.json()) as any;
74
+
75
+ // Transform the raw database records to our internal RunnerMessage format
76
+ const messages: RunnerMessage[] = data.messages.map((msg: any) => ({
77
+ id: msg.id,
78
+ direction: msg.direction,
79
+ runnerId: msg.runner || msg.runnerId,
80
+ taskId: msg.task || msg.taskId || null,
81
+ workspaceId: msg.workspace || msg.workspaceId || null,
82
+ conversationId: msg.conversation || msg.conversationId || null,
83
+ conversationObjectType: msg.conversationObjectType,
84
+ conversationObjectId: msg.conversationObjectId,
85
+ payload: msg.payload,
86
+ expiresAt: msg.expiresAt,
87
+ createdAt: msg.createdAt,
88
+ }));
89
+
90
+ logger.info(`Fetched ${messages.length} missed messages`);
91
+ return messages;
92
+ } catch (error) {
93
+ logger.error("Failed to fetch missed messages:", error);
94
+ throw error;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Send a message acknowledgment to update the high-water mark
100
+ */
101
+ async acknowledgeMessage(messageTimestamp: string): Promise<void> {
102
+ logger.debug(`Acknowledging message with timestamp: ${messageTimestamp}`);
103
+
104
+ try {
105
+ const response = await fetch(`${this.baseUrl}/api/runner/messages`, {
106
+ method: "POST",
107
+ headers: {
108
+ Authorization: `Bearer ${this.token}`,
109
+ "X-Runner-Id": this.runnerId,
110
+ "Content-Type": "application/json",
111
+ },
112
+ body: JSON.stringify({
113
+ jsonrpc: "2.0",
114
+ method: "message.acknowledge",
115
+ params: {
116
+ runnerId: this.runnerId,
117
+ messageTimestamp,
118
+ },
119
+ }),
120
+ });
121
+
122
+ if (!response.ok) {
123
+ const error = await response.text();
124
+ throw new Error(
125
+ `Failed to acknowledge message: ${response.status} - ${error}`
126
+ );
127
+ }
128
+
129
+ logger.debug("Message acknowledged successfully");
130
+ } catch (error) {
131
+ logger.error("Failed to acknowledge message:", error);
132
+ throw error;
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,258 @@
1
+ /**
2
+ * SSE (Server-Sent Events) Client for real-time message streaming
3
+ *
4
+ * Connects to the server's SSE endpoint to receive real-time runner messages
5
+ * and handles automatic reconnection with exponential backoff.
6
+ */
7
+
8
+ import { createLogger } from '../utils/logger';
9
+ import { RunnerConfig } from '../types';
10
+ import { EventSource as NodeEventSource } from 'eventsource';
11
+
12
+ const logger = createLogger('SSEClient');
13
+
14
+ export interface SSEEvent {
15
+ id?: string;
16
+ type: string;
17
+ data: any;
18
+ }
19
+
20
+ export interface SSEClientOptions {
21
+ url: string;
22
+ runnerId: string;
23
+ token: string;
24
+ onMessage: (event: SSEEvent) => void;
25
+ onError?: (error: Error) => void;
26
+ onConnect?: () => void;
27
+ onDisconnect?: () => void;
28
+ reconnectInterval?: number;
29
+ maxReconnectInterval?: number;
30
+ reconnectMultiplier?: number;
31
+ maxReconnectAttempts?: number;
32
+ }
33
+
34
+ export class SSEClient {
35
+ private eventSource: EventSource | null = null;
36
+ private reconnectTimer: NodeJS.Timeout | null = null;
37
+ private reconnectAttempts = 0;
38
+ private currentReconnectInterval: number;
39
+ private isConnected = false;
40
+ private isStopped = false;
41
+ private lastEventId: string | undefined;
42
+
43
+ constructor(private options: SSEClientOptions) {
44
+ this.currentReconnectInterval = options.reconnectInterval || 1000;
45
+ }
46
+
47
+ /**
48
+ * Connect to the SSE endpoint
49
+ */
50
+ async connect(since?: string): Promise<void> {
51
+ if (this.eventSource) {
52
+ logger.warn('SSE client already connected');
53
+ return;
54
+ }
55
+
56
+ this.isStopped = false;
57
+
58
+ try {
59
+ // Build URL with query parameters
60
+ const url = new URL(this.options.url);
61
+ // Include token as query param as a fallback in case proxies strip headers
62
+ url.searchParams.set('token', this.options.token);
63
+ if (since) {
64
+ url.searchParams.set('since', since);
65
+ }
66
+
67
+ // Sanitize token from logs
68
+ const sanitized = new URL(url.toString());
69
+ if (sanitized.searchParams.has('token')) {
70
+ sanitized.searchParams.set('token', '[redacted]');
71
+ }
72
+ logger.info(`Connecting to SSE endpoint: ${sanitized.toString()}`);
73
+ logger.debug(`SSE auth header set: ${this.options.token ? 'yes' : 'no'}`);
74
+
75
+ // Create EventSource with authorization header (Node polyfill supports headers)
76
+ this.eventSource = new (NodeEventSource as any)(url.toString(), {
77
+ headers: {
78
+ 'Authorization': `Bearer ${this.options.token}`,
79
+ },
80
+ withCredentials: false,
81
+ });
82
+
83
+ // Set up event listeners
84
+ this.eventSource!.onopen = () => {
85
+ logger.info('SSE connection established');
86
+ this.isConnected = true;
87
+ this.reconnectAttempts = 0;
88
+ this.currentReconnectInterval = this.options.reconnectInterval || 1000;
89
+
90
+ if (this.options.onConnect) {
91
+ this.options.onConnect();
92
+ }
93
+ };
94
+
95
+ this.eventSource!.onmessage = (event: MessageEvent) => {
96
+ try {
97
+ const parsed = JSON.parse(event.data);
98
+ const envelope = parsed && typeof parsed === 'object' ? parsed : { data: parsed };
99
+ const inner = (envelope && 'data' in envelope) ? (envelope as any).data : parsed;
100
+ const sseEvent: SSEEvent = {
101
+ id: (envelope as any).id || (event as any).lastEventId,
102
+ type: (envelope as any).type || (event as any).type || 'message',
103
+ data: inner,
104
+ };
105
+
106
+ // Update last event ID for reconnection
107
+ if (sseEvent.id) {
108
+ this.lastEventId = sseEvent.id;
109
+ }
110
+
111
+ this.options.onMessage(sseEvent);
112
+ } catch (error) {
113
+ logger.error('Failed to parse SSE message:', error);
114
+ }
115
+ };
116
+
117
+ this.eventSource!.onerror = (error: Event) => {
118
+ logger.error('SSE connection error:', error);
119
+ this.isConnected = false;
120
+
121
+ if (this.options.onError) {
122
+ this.options.onError(new Error('SSE connection error'));
123
+ }
124
+
125
+ // Close the connection and attempt reconnection
126
+ this.disconnect();
127
+
128
+ if (!this.isStopped) {
129
+ this.scheduleReconnect();
130
+ }
131
+ };
132
+
133
+ // Listen for specific event types
134
+ this.eventSource!.addEventListener('runner.message', ((event: MessageEvent) => {
135
+ try {
136
+ const parsed = JSON.parse(event.data);
137
+ const envelope = parsed && typeof parsed === 'object' ? parsed : { data: parsed };
138
+ const inner = (envelope && 'data' in envelope) ? (envelope as any).data : parsed;
139
+ const sseEvent: SSEEvent = {
140
+ id: (envelope as any).id || (event as any).lastEventId,
141
+ type: 'runner.message',
142
+ data: inner,
143
+ };
144
+
145
+ // Update last event ID
146
+ if (sseEvent.id) {
147
+ this.lastEventId = sseEvent.id;
148
+ }
149
+
150
+ this.options.onMessage(sseEvent);
151
+ } catch (error) {
152
+ logger.error('Failed to parse runner.message event:', error);
153
+ }
154
+ }) as any);
155
+
156
+ // Listen for connection established event
157
+ this.eventSource!.addEventListener('connection.established', ((event: MessageEvent) => {
158
+ logger.debug('Received connection.established event:', event.data);
159
+ }) as any);
160
+
161
+ // Listen for error events
162
+ this.eventSource!.addEventListener('error', ((event: MessageEvent) => {
163
+ logger.error('Received error event from server:', event.data);
164
+ }) as any);
165
+
166
+ // Listen for shutdown events
167
+ this.eventSource!.addEventListener('shutdown', ((event: MessageEvent) => {
168
+ logger.warn('Server is shutting down:', event.data);
169
+ this.stop();
170
+ }) as any);
171
+
172
+ } catch (error) {
173
+ logger.error('Failed to connect to SSE endpoint:', error);
174
+
175
+ if (this.options.onError) {
176
+ this.options.onError(error instanceof Error ? error : new Error(String(error)));
177
+ }
178
+
179
+ // Schedule reconnection
180
+ if (!this.isStopped) {
181
+ this.scheduleReconnect();
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Disconnect from the SSE endpoint
188
+ */
189
+ disconnect(): void {
190
+ if (this.eventSource) {
191
+ this.eventSource.close();
192
+ this.eventSource = null;
193
+ this.isConnected = false;
194
+
195
+ if (this.options.onDisconnect) {
196
+ this.options.onDisconnect();
197
+ }
198
+ }
199
+
200
+ // Clear reconnection timer
201
+ if (this.reconnectTimer) {
202
+ clearTimeout(this.reconnectTimer);
203
+ this.reconnectTimer = null;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Stop the client and prevent reconnection
209
+ */
210
+ stop(): void {
211
+ logger.info('Stopping SSE client');
212
+ this.isStopped = true;
213
+ this.disconnect();
214
+ }
215
+
216
+ /**
217
+ * Schedule a reconnection attempt
218
+ */
219
+ private scheduleReconnect(): void {
220
+ const maxAttempts = this.options.maxReconnectAttempts || Infinity;
221
+
222
+ if (this.reconnectAttempts >= maxAttempts) {
223
+ logger.error(`Max reconnection attempts (${maxAttempts}) reached. Giving up.`);
224
+ return;
225
+ }
226
+
227
+ this.reconnectAttempts++;
228
+
229
+ logger.info(`Scheduling reconnection attempt ${this.reconnectAttempts} in ${this.currentReconnectInterval}ms`);
230
+
231
+ this.reconnectTimer = setTimeout(() => {
232
+ this.connect(this.lastEventId);
233
+ }, this.currentReconnectInterval);
234
+
235
+ // Exponential backoff
236
+ const multiplier = this.options.reconnectMultiplier || 2;
237
+ const maxInterval = this.options.maxReconnectInterval || 60000;
238
+
239
+ this.currentReconnectInterval = Math.min(
240
+ this.currentReconnectInterval * multiplier,
241
+ maxInterval
242
+ );
243
+ }
244
+
245
+ /**
246
+ * Check if the client is currently connected
247
+ */
248
+ isConnectedToServer(): boolean {
249
+ return this.isConnected;
250
+ }
251
+
252
+ /**
253
+ * Get the last received event ID
254
+ */
255
+ getLastEventId(): string | undefined {
256
+ return this.lastEventId;
257
+ }
258
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Types related to Claude SDK integration
3
+ */
4
+
5
+ import { Conversation } from "@botanicastudios/claude-code-sdk-ts";
6
+
7
+ // Repository information passed in conversation config
8
+ export interface RepositoryInfo {
9
+ url: string;
10
+ branch: string;
11
+ commit?: string;
12
+ type?: "github" | "local"; // Repository type
13
+ localPath?: string; // For local repositories
14
+ }
15
+
16
+ // Extended conversation config with repository info
17
+ export interface ConversationConfig {
18
+ systemPrompt?: string;
19
+ accessToken?: string;
20
+ accessKey?: string; // TODO remove
21
+ workspaceId?: string;
22
+ githubToken?: string;
23
+ anthropicApiKey?: string; // API key provided via runner message
24
+ mcpServers?: Record<string, any>;
25
+ repository?: RepositoryInfo; // Repository details from orchestrator
26
+ sessionId?: string; // For resume operations
27
+ runnerRepoPath?: string; // Path to local runner repo for local workspaces
28
+ }
29
+
30
+ // Internal conversation tracking
31
+ export interface ConversationContext {
32
+ conversationId: string; // Primary key - our conversation.id from the database
33
+ agentSessionId: string; // SDK session ID (from SDK's onSessionId callback)
34
+ conversationObjectType: "Task" | "TaskPlan";
35
+ conversationObjectId: string;
36
+ taskId?: string; // Optional for non-task conversations
37
+ workspaceId?: string;
38
+ status: "starting" | "active" | "stopping" | "stopped" | "error";
39
+ config: ConversationConfig;
40
+ startedAt: Date;
41
+ lastActivityAt: Date;
42
+ conversation?: Conversation; // Claude SDK conversation instance (stateful)
43
+
44
+ // Additional conversation details from database
45
+ model: string;
46
+ globalInstructions: string;
47
+ workspaceInstructions: string;
48
+ permissionsMode: string;
49
+ }
50
+
51
+ // Message type for initial messages
52
+ export interface Message {
53
+ content: string;
54
+ role?: "user" | "assistant";
55
+ }
@@ -0,0 +1,4 @@
1
+ declare module 'computer-name' {
2
+ function computerName(): Promise<string>;
3
+ export = computerName;
4
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Main type definitions for the runner app
3
+ */
4
+
5
+ export * from "./messages";
6
+ export * from "./claude";
7
+ export * from "./runner-interface";
8
+
9
+ // Retry strategy types
10
+ export type RetryStrategy = "none" | "interval" | "exponential";
11
+
12
+ // RunnerRepo type for local runner repos
13
+ export interface RunnerRepo {
14
+ uuid?: string; // UUID for identifying the repo across runner restarts
15
+ name: string;
16
+ path: string;
17
+ }
18
+
19
+ // Runner configuration (in-memory)
20
+ export interface RunnerConfig {
21
+ runnerId?: string; // Stable external ID from server (for namespacing state/config)
22
+ orchestratorUrl: string; // From process.env.NORTHFLARE_ORCHESTRATOR_URL
23
+ dataDir: string;
24
+ workspaceDir?: string; // Directory for workspace/repository operations
25
+ heartbeatInterval: number;
26
+ retryStrategy: RetryStrategy; // Strategy for registration retries
27
+ retryIntervalSecs: number; // For interval strategy
28
+ retryDurationSecs: number; // Max duration for exponential strategy
29
+ runnerRepos?: RunnerRepo[]; // In-memory: repos for this specific runner session
30
+ // Authentication via environment variable or --token CLI option
31
+ // process.env.NORTHFLARE_RUNNER_TOKEN
32
+ // This token is generated by the server and must be provided
33
+ // either as an environment variable or via the --token CLI flag
34
+ }
35
+
36
+ // Config file format (on-disk)
37
+ export interface RunnerConfigFile {
38
+ orchestratorUrl?: string;
39
+ dataDir?: string;
40
+ workspaceDir?: string;
41
+ heartbeatInterval?: number;
42
+ retryStrategy?: RetryStrategy;
43
+ retryIntervalSecs?: number;
44
+ retryDurationSecs?: number;
45
+ runnerRepos?: Record<string, RunnerRepo[]>; // On-disk: namespaced by runnerId (stable external ID)
46
+ }
47
+
48
+ // Repository management types
49
+ export interface WorkspaceRepository {
50
+ workspaceId: string;
51
+ repoUrl: string;
52
+ branch: string;
53
+ localPath: string;
54
+ lastAccessed: Date;
55
+ }
56
+
57
+ // Environment configuration
58
+ export interface EnvironmentConfig {
59
+ NORTHFLARE_RUNNER_TOKEN: string;
60
+ NORTHFLARE_WORKSPACE_DIR: string;
61
+ NORTHFLARE_ORCHESTRATOR_URL: string;
62
+ DEBUG?: string;
63
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * JSONRPC message types for communication between runner and orchestrator
3
+ */
4
+
5
+ export interface JsonRpcMessage {
6
+ jsonrpc: "2.0";
7
+ id?: string | number;
8
+ method?: string;
9
+ params?: any;
10
+ result?: any;
11
+ error?: JsonRpcError;
12
+ }
13
+
14
+ export interface JsonRpcError {
15
+ code: number;
16
+ message: string;
17
+ data?: any;
18
+ }
19
+
20
+ // Messages received from orchestrator via SSE
21
+ export interface RunnerMessage {
22
+ id: string;
23
+ direction: "to_runner" | "to_orchestrator";
24
+ runnerId: string;
25
+ taskId: string | null; // Task ID from orchestrator for correlation (optional)
26
+ workspaceId: string | null; // Workspace ID for workspace-scoped messages
27
+ conversationObjectType?: "Task" | "TaskPlan";
28
+ conversationObjectId?: string;
29
+ conversationId?: string; // Conversation ID for direct conversation management
30
+ payload: JsonRpcMessage;
31
+ createdAt: string;
32
+ expiresAt: string;
33
+ }
34
+
35
+ // Method handler type
36
+ export type MethodHandler = (
37
+ params: any,
38
+ message: RunnerMessage
39
+ ) => Promise<void>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Interface for RunnerApp to ensure compatibility between different implementations
3
+ */
4
+
5
+ import { RunnerConfig, ConversationContext, JsonRpcMessage } from './index';
6
+ import { ClaudeManager } from '../components/claude-sdk-manager';
7
+ import { EnhancedRepositoryManager } from '../components/enhanced-repository-manager';
8
+
9
+ export interface IRunnerApp {
10
+ // Core methods
11
+ notify(method: string, params: any): Promise<void>;
12
+ sendToOrchestrator(message: JsonRpcMessage): Promise<any>;
13
+
14
+ // Conversation management
15
+ getConversationContext(conversationId: string): ConversationContext | undefined;
16
+
17
+ // Configuration and state
18
+ get config_(): RunnerConfig;
19
+ get activeConversations_(): Map<string, ConversationContext>;
20
+ get claudeManager_(): ClaudeManager;
21
+ get repositoryManager_(): EnhancedRepositoryManager;
22
+
23
+ // Runner state
24
+ getRunnerId(): string | undefined;
25
+ getRunnerUid(): string | null;
26
+ getLastProcessedAt(): Date | null;
27
+ getIsActiveRunner(): boolean;
28
+ setIsActiveRunner(active: boolean): void;
29
+ setLastProcessedAt(timestamp: Date | null): void;
30
+ getPreHandoffConversations(): Set<string>;
31
+
32
+ // Optional methods for SSE implementation
33
+ updateLastProcessedAt?(timestamp: Date | null): Promise<void>;
34
+ }