@overlordai/worker 1.0.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 (109) hide show
  1. package/dist/ansi-stripper.d.ts +11 -0
  2. package/dist/ansi-stripper.d.ts.map +1 -0
  3. package/dist/ansi-stripper.js +19 -0
  4. package/dist/ansi-stripper.js.map +1 -0
  5. package/dist/capability-detector.d.ts +5 -0
  6. package/dist/capability-detector.d.ts.map +1 -0
  7. package/dist/capability-detector.js +43 -0
  8. package/dist/capability-detector.js.map +1 -0
  9. package/dist/config.d.ts +27 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +144 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/executor/base.executor.d.ts +55 -0
  14. package/dist/executor/base.executor.d.ts.map +1 -0
  15. package/dist/executor/base.executor.js +30 -0
  16. package/dist/executor/base.executor.js.map +1 -0
  17. package/dist/executor/claude.executor.d.ts +23 -0
  18. package/dist/executor/claude.executor.d.ts.map +1 -0
  19. package/dist/executor/claude.executor.js +106 -0
  20. package/dist/executor/claude.executor.js.map +1 -0
  21. package/dist/executor/codex.executor.d.ts +20 -0
  22. package/dist/executor/codex.executor.d.ts.map +1 -0
  23. package/dist/executor/codex.executor.js +51 -0
  24. package/dist/executor/codex.executor.js.map +1 -0
  25. package/dist/executor/cursor.executor.d.ts +19 -0
  26. package/dist/executor/cursor.executor.d.ts.map +1 -0
  27. package/dist/executor/cursor.executor.js +46 -0
  28. package/dist/executor/cursor.executor.js.map +1 -0
  29. package/dist/executor/custom.executor.d.ts +21 -0
  30. package/dist/executor/custom.executor.d.ts.map +1 -0
  31. package/dist/executor/custom.executor.js +57 -0
  32. package/dist/executor/custom.executor.js.map +1 -0
  33. package/dist/executor/executor.factory.d.ts +9 -0
  34. package/dist/executor/executor.factory.d.ts.map +1 -0
  35. package/dist/executor/executor.factory.js +29 -0
  36. package/dist/executor/executor.factory.js.map +1 -0
  37. package/dist/git-operations.d.ts +23 -0
  38. package/dist/git-operations.d.ts.map +1 -0
  39. package/dist/git-operations.js +94 -0
  40. package/dist/git-operations.js.map +1 -0
  41. package/dist/hardware.d.ts +14 -0
  42. package/dist/hardware.d.ts.map +1 -0
  43. package/dist/hardware.js +92 -0
  44. package/dist/hardware.js.map +1 -0
  45. package/dist/healthz.d.ts +14 -0
  46. package/dist/healthz.d.ts.map +1 -0
  47. package/dist/healthz.js +104 -0
  48. package/dist/healthz.js.map +1 -0
  49. package/dist/jwt-manager.d.ts +23 -0
  50. package/dist/jwt-manager.d.ts.map +1 -0
  51. package/dist/jwt-manager.js +169 -0
  52. package/dist/jwt-manager.js.map +1 -0
  53. package/dist/lease-manager.d.ts +17 -0
  54. package/dist/lease-manager.d.ts.map +1 -0
  55. package/dist/lease-manager.js +62 -0
  56. package/dist/lease-manager.js.map +1 -0
  57. package/dist/main.d.ts +3 -0
  58. package/dist/main.d.ts.map +1 -0
  59. package/dist/main.js +497 -0
  60. package/dist/main.js.map +1 -0
  61. package/dist/orphan-reaper.d.ts +6 -0
  62. package/dist/orphan-reaper.d.ts.map +1 -0
  63. package/dist/orphan-reaper.js +148 -0
  64. package/dist/orphan-reaper.js.map +1 -0
  65. package/dist/pipeline-parser.d.ts +14 -0
  66. package/dist/pipeline-parser.d.ts.map +1 -0
  67. package/dist/pipeline-parser.js +183 -0
  68. package/dist/pipeline-parser.js.map +1 -0
  69. package/dist/pipeline-runner.d.ts +120 -0
  70. package/dist/pipeline-runner.d.ts.map +1 -0
  71. package/dist/pipeline-runner.js +568 -0
  72. package/dist/pipeline-runner.js.map +1 -0
  73. package/dist/project-mutex.d.ts +14 -0
  74. package/dist/project-mutex.d.ts.map +1 -0
  75. package/dist/project-mutex.js +25 -0
  76. package/dist/project-mutex.js.map +1 -0
  77. package/dist/pty-manager.d.ts +50 -0
  78. package/dist/pty-manager.d.ts.map +1 -0
  79. package/dist/pty-manager.js +203 -0
  80. package/dist/pty-manager.js.map +1 -0
  81. package/dist/ringbuffer.d.ts +22 -0
  82. package/dist/ringbuffer.d.ts.map +1 -0
  83. package/dist/ringbuffer.js +57 -0
  84. package/dist/ringbuffer.js.map +1 -0
  85. package/dist/safe-env.d.ts +6 -0
  86. package/dist/safe-env.d.ts.map +1 -0
  87. package/dist/safe-env.js +19 -0
  88. package/dist/safe-env.js.map +1 -0
  89. package/dist/stage-detector.d.ts +62 -0
  90. package/dist/stage-detector.d.ts.map +1 -0
  91. package/dist/stage-detector.js +140 -0
  92. package/dist/stage-detector.js.map +1 -0
  93. package/dist/task-handler.d.ts +56 -0
  94. package/dist/task-handler.d.ts.map +1 -0
  95. package/dist/task-handler.js +296 -0
  96. package/dist/task-handler.js.map +1 -0
  97. package/dist/tunnel-manager.d.ts +34 -0
  98. package/dist/tunnel-manager.d.ts.map +1 -0
  99. package/dist/tunnel-manager.js +165 -0
  100. package/dist/tunnel-manager.js.map +1 -0
  101. package/dist/worker-client.d.ts +62 -0
  102. package/dist/worker-client.d.ts.map +1 -0
  103. package/dist/worker-client.js +303 -0
  104. package/dist/worker-client.js.map +1 -0
  105. package/dist/workspace-manager.d.ts +51 -0
  106. package/dist/workspace-manager.d.ts.map +1 -0
  107. package/dist/workspace-manager.js +276 -0
  108. package/dist/workspace-manager.js.map +1 -0
  109. package/package.json +30 -0
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.WorkerClient = void 0;
7
+ const ws_1 = __importDefault(require("ws"));
8
+ const protocol_1 = require("@overlordai/protocol");
9
+ const hardware_js_1 = require("./hardware.js");
10
+ const pino_1 = __importDefault(require("pino"));
11
+ const log = (0, pino_1.default)({ name: 'worker-client' });
12
+ const MAX_RECONNECT_DELAY_MS = 30_000;
13
+ const INITIAL_RECONNECT_DELAY_MS = 1_000;
14
+ const DEDUP_CLEANUP_INTERVAL_MS = 60_000;
15
+ class WorkerClient {
16
+ ws = null;
17
+ options;
18
+ handlers;
19
+ heartbeatTimer = null;
20
+ reconnectTimer = null;
21
+ reconnectDelay = INITIAL_RECONNECT_DELAY_MS;
22
+ connected = false;
23
+ intentionalClose = false;
24
+ dedupSet = new Map();
25
+ dedupCleanupTimer = null;
26
+ constructor(options, handlers) {
27
+ this.options = options;
28
+ this.handlers = handlers;
29
+ }
30
+ isConnected() {
31
+ return this.connected;
32
+ }
33
+ connect() {
34
+ if (this.ws) {
35
+ try {
36
+ this.ws.removeAllListeners();
37
+ this.ws.close();
38
+ }
39
+ catch { }
40
+ this.ws = null;
41
+ }
42
+ this.intentionalClose = false;
43
+ const wsUrl = this.buildWsUrl();
44
+ log.info({ url: wsUrl }, 'Connecting to dispatcher');
45
+ try {
46
+ this.ws = new ws_1.default(wsUrl, {
47
+ headers: {
48
+ 'Authorization': `Bearer ${this.options.getToken()}`,
49
+ },
50
+ });
51
+ }
52
+ catch (err) {
53
+ log.error({ err }, 'Failed to create WebSocket');
54
+ this.scheduleReconnect();
55
+ return;
56
+ }
57
+ this.ws.on('open', () => {
58
+ log.info('WebSocket connected');
59
+ this.connected = true;
60
+ this.reconnectDelay = INITIAL_RECONNECT_DELAY_MS;
61
+ // Send auth frame as first message
62
+ this.sendFrame({
63
+ type: 'auth',
64
+ token: this.options.getToken(),
65
+ protocolVersion: protocol_1.PROTOCOL_VERSION,
66
+ });
67
+ // Start heartbeat
68
+ this.startHeartbeat();
69
+ // Start dedup cleanup
70
+ this.startDedupCleanup();
71
+ this.handlers.onConnected();
72
+ });
73
+ this.ws.on('message', (data) => {
74
+ this.handleMessage(data);
75
+ });
76
+ this.ws.on('close', (code, reason) => {
77
+ log.info({ code, reason: reason.toString() }, 'WebSocket closed');
78
+ this.cleanup();
79
+ this.handlers.onDisconnected();
80
+ if (!this.intentionalClose) {
81
+ this.scheduleReconnect();
82
+ }
83
+ });
84
+ this.ws.on('error', (err) => {
85
+ log.error({ err }, 'WebSocket error');
86
+ // The close event will fire after this, triggering reconnect
87
+ });
88
+ }
89
+ disconnect() {
90
+ this.intentionalClose = true;
91
+ this.cleanup();
92
+ if (this.ws) {
93
+ this.ws.close(1000, 'Worker shutting down');
94
+ this.ws = null;
95
+ }
96
+ }
97
+ sendHeartbeat() {
98
+ const metrics = (0, hardware_js_1.collectRuntimeMetrics)();
99
+ const frame = {
100
+ type: 'heartbeat',
101
+ machineId: this.options.machineId,
102
+ timestamp: new Date().toISOString(),
103
+ metrics: {
104
+ cpuUsage: metrics.cpuUsage,
105
+ memoryUsage: metrics.memoryUsage,
106
+ diskUsage: metrics.diskUsage,
107
+ activeSlots: this.options.getActiveSlots(),
108
+ },
109
+ capabilities: this.options.getCapabilities(),
110
+ };
111
+ this.sendFrame(frame);
112
+ }
113
+ sendAck(msgId, payload) {
114
+ const frame = {
115
+ type: 'ack',
116
+ msgId,
117
+ payload,
118
+ };
119
+ this.sendFrame(frame);
120
+ }
121
+ sendProgress(taskId, stage, status, message) {
122
+ const frame = {
123
+ type: 'progress',
124
+ taskId,
125
+ stage,
126
+ status,
127
+ message,
128
+ };
129
+ this.sendFrame(frame);
130
+ }
131
+ sendReconnect(tasks) {
132
+ const frame = {
133
+ type: 'reconnect',
134
+ machineId: this.options.machineId,
135
+ tasks,
136
+ };
137
+ this.sendFrame(frame);
138
+ }
139
+ sendRecoverySecretAck() {
140
+ const frame = {
141
+ type: 'recovery_secret_ack',
142
+ };
143
+ this.sendFrame(frame);
144
+ }
145
+ sendStageConfirmRequest(taskId, stageIndex, mode, timeout, prompt, choices) {
146
+ const frame = {
147
+ type: 'stage_confirm_request',
148
+ taskId,
149
+ stageIndex,
150
+ mode,
151
+ timeout,
152
+ ...(prompt !== undefined && { prompt }),
153
+ ...(choices !== undefined && { choices }),
154
+ };
155
+ this.sendFrame(frame);
156
+ }
157
+ sendFrame(frame) {
158
+ if (!this.ws || this.ws.readyState !== ws_1.default.OPEN) {
159
+ log.warn({ frameType: frame.type }, 'Cannot send frame, WebSocket not open');
160
+ return;
161
+ }
162
+ try {
163
+ this.ws.send(JSON.stringify(frame));
164
+ }
165
+ catch (err) {
166
+ log.error({ err, frameType: frame.type }, 'Failed to send frame');
167
+ }
168
+ }
169
+ handleMessage(data) {
170
+ let frame;
171
+ try {
172
+ frame = JSON.parse(data.toString());
173
+ }
174
+ catch (err) {
175
+ log.error({ err }, 'Failed to parse incoming frame');
176
+ return;
177
+ }
178
+ // Dedup messages with msgId
179
+ if ('msgId' in frame && frame.msgId) {
180
+ const msgId = frame.msgId;
181
+ if (this.dedupSet.has(msgId)) {
182
+ log.debug({ msgId, type: frame.type }, 'Duplicate message, ignoring');
183
+ return;
184
+ }
185
+ this.dedupSet.set(msgId, Date.now());
186
+ }
187
+ log.debug({ type: frame.type }, 'Received frame');
188
+ switch (frame.type) {
189
+ case 'heartbeat_ack':
190
+ this.handlers.onHeartbeatAck(frame);
191
+ break;
192
+ case 'execute':
193
+ this.handlers.onExecute(frame);
194
+ break;
195
+ case 'cancel':
196
+ this.handlers.onCancel(frame);
197
+ break;
198
+ case 'cleanup_workspace':
199
+ this.handlers.onCleanupWorkspace(frame);
200
+ break;
201
+ case 'resume':
202
+ this.handlers.onResume(frame);
203
+ break;
204
+ case 'server_shutting_down':
205
+ this.handlers.onServerShuttingDown(frame);
206
+ break;
207
+ case 'recovery_secret_bootstrap':
208
+ this.handlers.onRecoverySecretBootstrap(frame);
209
+ break;
210
+ case 'start_tunnel':
211
+ this.handlers.onStartTunnel(frame);
212
+ break;
213
+ case 'stop_tunnel':
214
+ this.handlers.onStopTunnel(frame);
215
+ break;
216
+ case 'stage_confirm_response':
217
+ this.handlers.onStageConfirmResponse(frame);
218
+ break;
219
+ default:
220
+ log.error({ frame }, 'Unknown frame type received from server');
221
+ log.warn({ frame }, 'Unknown frame type');
222
+ }
223
+ }
224
+ buildWsUrl() {
225
+ const host = this.options.overlordHost;
226
+ let wsBase;
227
+ if (host.startsWith('https://')) {
228
+ wsBase = 'wss://' + host.slice(8);
229
+ }
230
+ else if (host.startsWith('http://')) {
231
+ wsBase = 'ws://' + host.slice(7);
232
+ }
233
+ else {
234
+ wsBase = 'ws://' + host;
235
+ }
236
+ // Remove trailing slash
237
+ if (wsBase.endsWith('/')) {
238
+ wsBase = wsBase.slice(0, -1);
239
+ }
240
+ return `${wsBase}/api/workers/channel`;
241
+ }
242
+ startHeartbeat() {
243
+ this.stopHeartbeat();
244
+ this.heartbeatTimer = setInterval(() => {
245
+ this.sendHeartbeat();
246
+ }, protocol_1.HEARTBEAT_INTERVAL_MS);
247
+ if (this.heartbeatTimer.unref) {
248
+ this.heartbeatTimer.unref();
249
+ }
250
+ // Send first heartbeat immediately
251
+ this.sendHeartbeat();
252
+ }
253
+ stopHeartbeat() {
254
+ if (this.heartbeatTimer) {
255
+ clearInterval(this.heartbeatTimer);
256
+ this.heartbeatTimer = null;
257
+ }
258
+ }
259
+ startDedupCleanup() {
260
+ this.stopDedupCleanup();
261
+ this.dedupCleanupTimer = setInterval(() => {
262
+ const cutoff = Date.now() - protocol_1.MSGID_DEDUP_TTL_SEC * 1000;
263
+ for (const [msgId, timestamp] of this.dedupSet) {
264
+ if (timestamp < cutoff) {
265
+ this.dedupSet.delete(msgId);
266
+ }
267
+ }
268
+ }, DEDUP_CLEANUP_INTERVAL_MS);
269
+ if (this.dedupCleanupTimer.unref) {
270
+ this.dedupCleanupTimer.unref();
271
+ }
272
+ }
273
+ stopDedupCleanup() {
274
+ if (this.dedupCleanupTimer) {
275
+ clearInterval(this.dedupCleanupTimer);
276
+ this.dedupCleanupTimer = null;
277
+ }
278
+ }
279
+ scheduleReconnect() {
280
+ if (this.reconnectTimer)
281
+ return;
282
+ const delay = this.reconnectDelay;
283
+ log.info({ delayMs: delay }, 'Scheduling reconnect');
284
+ this.reconnectTimer = setTimeout(() => {
285
+ this.reconnectTimer = null;
286
+ this.connect();
287
+ }, delay);
288
+ // Exponential backoff with jitter
289
+ const jitter = Math.random() * 0.3 * this.reconnectDelay;
290
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2 + jitter, MAX_RECONNECT_DELAY_MS);
291
+ }
292
+ cleanup() {
293
+ this.connected = false;
294
+ this.stopHeartbeat();
295
+ this.stopDedupCleanup();
296
+ if (this.reconnectTimer) {
297
+ clearTimeout(this.reconnectTimer);
298
+ this.reconnectTimer = null;
299
+ }
300
+ }
301
+ }
302
+ exports.WorkerClient = WorkerClient;
303
+ //# sourceMappingURL=worker-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-client.js","sourceRoot":"","sources":["../src/worker-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { ConfigSnapshot } from '@overlordai/protocol';
2
+ export interface WorkspaceInitParams {
3
+ taskId: number;
4
+ projectKey: string;
5
+ configSnapshot: ConfigSnapshot;
6
+ developer: {
7
+ gitName: string;
8
+ gitEmail: string;
9
+ };
10
+ }
11
+ export interface WorkspaceInfo {
12
+ path: string;
13
+ branch: string;
14
+ env: Record<string, string>;
15
+ }
16
+ export declare class WorkspaceManager {
17
+ private config;
18
+ private git;
19
+ private mutex;
20
+ private lastFetchTimes;
21
+ constructor(config: {
22
+ workspaceRoot: string;
23
+ sshKeyPath?: string;
24
+ });
25
+ /**
26
+ * Initialize a workspace for a task:
27
+ * 1. Ensure bare clone exists
28
+ * 2. Fetch (throttled)
29
+ * 3. Create worktree
30
+ * 4. Configure git identity
31
+ * 5. Run setup commands
32
+ * 6. Return workspace info with PTY env
33
+ */
34
+ initialize(params: WorkspaceInitParams): Promise<WorkspaceInfo>;
35
+ /**
36
+ * Clean up a workspace:
37
+ * 1. Remove the git worktree
38
+ * 2. Remove leftover directory
39
+ * 3. Prune stale worktree metadata
40
+ */
41
+ cleanup(taskId: number, workspacePath: string): Promise<void>;
42
+ /**
43
+ * Scan the workspace root and remove worktrees older than 24 hours.
44
+ */
45
+ cleanupExpired(activeTaskIds?: Set<number>): Promise<void>;
46
+ /**
47
+ * Check whether a bare clone exists for a given project key.
48
+ */
49
+ hasBaseRepo(projectKey: string): boolean;
50
+ }
51
+ //# sourceMappingURL=workspace-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-manager.d.ts","sourceRoot":"","sources":["../src/workspace-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAY3D,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAID,qBAAa,gBAAgB;IAKf,OAAO,CAAC,MAAM;IAJ1B,OAAO,CAAC,GAAG,CAAgB;IAC3B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,cAAc,CAA6B;gBAE/B,MAAM,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAK1E;;;;;;;;OAQG;IACG,UAAU,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;IAqErE;;;;;OAKG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BnE;;OAEG;IACG,cAAc,CAAC,aAAa,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAiD3E;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;CAIzC"}
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.WorkspaceManager = void 0;
40
+ const node_child_process_1 = require("node:child_process");
41
+ const fs = __importStar(require("node:fs"));
42
+ const path = __importStar(require("node:path"));
43
+ const pino_1 = __importDefault(require("pino"));
44
+ const git_operations_js_1 = require("./git-operations.js");
45
+ const project_mutex_js_1 = require("./project-mutex.js");
46
+ const safe_env_js_1 = require("./safe-env.js");
47
+ const logger = (0, pino_1.default)({ name: 'workspace-manager' });
48
+ const FETCH_THROTTLE_MS = 60_000; // 60 seconds
49
+ const WORKTREE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
50
+ // ─── WorkspaceManager ────────────────────────────────────────
51
+ class WorkspaceManager {
52
+ config;
53
+ git;
54
+ mutex;
55
+ lastFetchTimes = new Map();
56
+ constructor(config) {
57
+ this.config = config;
58
+ this.git = new git_operations_js_1.GitOperations(config.sshKeyPath);
59
+ this.mutex = new project_mutex_js_1.ProjectMutex();
60
+ }
61
+ /**
62
+ * Initialize a workspace for a task:
63
+ * 1. Ensure bare clone exists
64
+ * 2. Fetch (throttled)
65
+ * 3. Create worktree
66
+ * 4. Configure git identity
67
+ * 5. Run setup commands
68
+ * 6. Return workspace info with PTY env
69
+ */
70
+ async initialize(params) {
71
+ const { taskId, projectKey, configSnapshot, developer } = params;
72
+ const projectDir = path.join(this.config.workspaceRoot, projectKey);
73
+ const baseDir = path.join(projectDir, '_base');
74
+ const branch = `feat/overlord-${taskId}`;
75
+ const worktreePath = path.join(projectDir, `task-${taskId}`);
76
+ // 1–5. Acquire per-project mutex for bare clone + fetch + worktree add
77
+ const release = await this.mutex.acquire(projectKey);
78
+ try {
79
+ // 1. Clone bare repo if it doesn't exist (inside mutex to avoid race)
80
+ if (!fs.existsSync(baseDir)) {
81
+ logger.info({ projectKey, baseDir }, 'Base repo missing — cloning bare');
82
+ fs.mkdirSync(projectDir, { recursive: true });
83
+ this.git.cloneBare(configSnapshot.repoUrl, baseDir);
84
+ }
85
+ // 3. Fetch with throttle (prune stale entries to bound memory)
86
+ if (this.lastFetchTimes.size > 500) {
87
+ const oldest = [...this.lastFetchTimes.entries()]
88
+ .sort((a, b) => a[1] - b[1])
89
+ .slice(0, Math.floor(this.lastFetchTimes.size / 2));
90
+ for (const [k] of oldest)
91
+ this.lastFetchTimes.delete(k);
92
+ }
93
+ const lastFetch = this.lastFetchTimes.get(projectKey) ?? 0;
94
+ if (Date.now() - lastFetch >= FETCH_THROTTLE_MS) {
95
+ this.git.fetch(baseDir);
96
+ this.lastFetchTimes.set(projectKey, Date.now());
97
+ }
98
+ else {
99
+ logger.debug({ projectKey }, 'Skipping fetch — throttled');
100
+ }
101
+ // 4. Create worktree
102
+ const startPoint = `origin/${configSnapshot.defaultBranch}`;
103
+ this.git.addWorktree(baseDir, worktreePath, branch, startPoint);
104
+ }
105
+ finally {
106
+ // 5. Release mutex
107
+ release();
108
+ }
109
+ // 6. Configure git user in worktree
110
+ this.git.setLocalConfig(worktreePath, 'user.name', developer.gitName);
111
+ this.git.setLocalConfig(worktreePath, 'user.email', developer.gitEmail);
112
+ // 7. Run setup commands (JSON array, sequential)
113
+ if (configSnapshot.setupCommands) {
114
+ const commands = JSON.parse(configSnapshot.setupCommands);
115
+ for (const cmd of commands) {
116
+ logger.info({ taskId, cmd }, 'Running setup command');
117
+ try {
118
+ (0, node_child_process_1.execFileSync)('sh', ['-c', cmd], {
119
+ cwd: worktreePath,
120
+ timeout: 120_000,
121
+ stdio: 'pipe',
122
+ env: (0, safe_env_js_1.buildSafeEnv)(),
123
+ });
124
+ }
125
+ catch (err) {
126
+ logger.error({ taskId, cmd, err }, 'Setup command failed');
127
+ throw new Error(`Setup command failed: ${cmd} — ${err.message}`);
128
+ }
129
+ }
130
+ }
131
+ // 8–9. Build PTY env and return
132
+ const env = buildPtyEnv(worktreePath, taskId, configSnapshot, developer, this.config.sshKeyPath);
133
+ return { path: worktreePath, branch, env };
134
+ }
135
+ /**
136
+ * Clean up a workspace:
137
+ * 1. Remove the git worktree
138
+ * 2. Remove leftover directory
139
+ * 3. Prune stale worktree metadata
140
+ */
141
+ async cleanup(taskId, workspacePath) {
142
+ const projectDir = path.dirname(workspacePath);
143
+ const baseDir = path.join(projectDir, '_base');
144
+ const projectKey = path.basename(projectDir);
145
+ const release = await this.mutex.acquire(projectKey);
146
+ try {
147
+ // Try git worktree remove first
148
+ try {
149
+ this.git.removeWorktree(baseDir, workspacePath);
150
+ }
151
+ catch (err) {
152
+ logger.warn({ taskId, workspacePath, err }, 'git worktree remove failed — will force-remove directory');
153
+ }
154
+ // Force-remove directory if it still exists
155
+ if (fs.existsSync(workspacePath)) {
156
+ fs.rmSync(workspacePath, { recursive: true, force: true });
157
+ }
158
+ // Prune stale worktree refs
159
+ this.git.pruneWorktrees(baseDir);
160
+ }
161
+ finally {
162
+ release();
163
+ }
164
+ logger.info({ taskId, workspacePath }, 'Workspace cleaned up');
165
+ }
166
+ /**
167
+ * Scan the workspace root and remove worktrees older than 24 hours.
168
+ */
169
+ async cleanupExpired(activeTaskIds = new Set()) {
170
+ const root = this.config.workspaceRoot;
171
+ if (!fs.existsSync(root)) {
172
+ return;
173
+ }
174
+ const projectDirs = fs.readdirSync(root, { withFileTypes: true })
175
+ .filter((d) => d.isDirectory());
176
+ for (const projectDirent of projectDirs) {
177
+ const projectDir = path.join(root, projectDirent.name);
178
+ const baseDir = path.join(projectDir, '_base');
179
+ if (!fs.existsSync(baseDir)) {
180
+ continue;
181
+ }
182
+ const entries = fs.readdirSync(projectDir, { withFileTypes: true })
183
+ .filter((d) => d.isDirectory() && d.name.startsWith('task-'));
184
+ for (const entry of entries) {
185
+ const worktreePath = path.join(projectDir, entry.name);
186
+ try {
187
+ // Extract task ID from directory name
188
+ const taskIdMatch = entry.name.match(/^task-(\d+)$/);
189
+ const taskId = taskIdMatch ? Number(taskIdMatch[1]) : 0;
190
+ // Skip directories whose task is still active
191
+ if (taskId > 0 && activeTaskIds.has(taskId)) {
192
+ logger.debug({ worktreePath, taskId }, 'Skipping cleanup — task is still active');
193
+ continue;
194
+ }
195
+ const stat = fs.statSync(worktreePath);
196
+ const ageMs = Date.now() - stat.mtimeMs;
197
+ if (ageMs > WORKTREE_MAX_AGE_MS) {
198
+ logger.info({ worktreePath, ageMs }, 'Removing expired worktree');
199
+ await this.cleanup(taskId, worktreePath);
200
+ }
201
+ }
202
+ catch (err) {
203
+ logger.error({ worktreePath, err }, 'Error checking/cleaning expired worktree');
204
+ }
205
+ }
206
+ }
207
+ }
208
+ /**
209
+ * Check whether a bare clone exists for a given project key.
210
+ */
211
+ hasBaseRepo(projectKey) {
212
+ const baseDir = path.join(this.config.workspaceRoot, projectKey, '_base');
213
+ return fs.existsSync(baseDir);
214
+ }
215
+ }
216
+ exports.WorkspaceManager = WorkspaceManager;
217
+ // ─── PTY Environment Builder ─────────────────────────────────
218
+ function buildPtyEnv(workspacePath, taskId, config, developer, sshKeyPath) {
219
+ const env = {
220
+ HOME: workspacePath,
221
+ PATH: process.env.PATH ?? '/usr/local/bin:/usr/bin:/bin',
222
+ TERM: 'xterm-256color',
223
+ COLORTERM: 'truecolor',
224
+ GIT_AUTHOR_NAME: developer.gitName,
225
+ GIT_AUTHOR_EMAIL: developer.gitEmail,
226
+ GIT_COMMITTER_NAME: developer.gitName,
227
+ GIT_COMMITTER_EMAIL: developer.gitEmail,
228
+ OVERLORD_TASK_ID: String(taskId),
229
+ };
230
+ if (sshKeyPath) {
231
+ // Shell-safe single-quote escaping: replace ' with '\'' (end quote, escaped quote, start quote)
232
+ const escapedPath = sshKeyPath.replace(/'/g, "'\\''");
233
+ env.GIT_SSH_COMMAND = `ssh -i '${escapedPath}' -o IdentitiesOnly=yes`;
234
+ }
235
+ if (config.agentEnv) {
236
+ // Allowlist approach: only permit keys that look like safe user-defined env vars.
237
+ // This prevents injection via dangerous vars like BASH_ENV, LD_AUDIT,
238
+ // PYTHONSTARTUP, RUBYOPT, NODE_EXTRA_CA_CERTS, etc.
239
+ const SAFE_KEY_PATTERN = /^[A-Z][A-Z0-9_]*$/;
240
+ const RESERVED_PREFIXES = ['LD_', 'DYLD_', 'BASH_', 'GIT_', 'OVERLORD_', 'NODE_', 'PYTHON', 'RUBY', 'PERL'];
241
+ const RESERVED_EXACT = new Set([
242
+ 'PATH', 'HOME', 'SHELL', 'USER', 'LOGNAME', 'TERM', 'COLORTERM',
243
+ 'ENV', 'IFS', 'CDPATH', 'GLOBIGNORE', 'SHELLOPTS', 'BASHOPTS',
244
+ 'PROMPT_COMMAND', 'MAIL', 'MAILPATH', 'MAILCHECK',
245
+ 'HOSTALIASES', 'LOCALDOMAIN', 'RES_OPTIONS',
246
+ 'LD_PRELOAD', 'LD_LIBRARY_PATH', 'LD_AUDIT',
247
+ ]);
248
+ const isAllowed = (key) => {
249
+ if (!SAFE_KEY_PATTERN.test(key))
250
+ return false;
251
+ if (RESERVED_EXACT.has(key))
252
+ return false;
253
+ for (const prefix of RESERVED_PREFIXES) {
254
+ if (key.startsWith(prefix))
255
+ return false;
256
+ }
257
+ return true;
258
+ };
259
+ try {
260
+ const parsed = JSON.parse(config.agentEnv);
261
+ for (const [key, value] of Object.entries(parsed)) {
262
+ if (isAllowed(key) && typeof value === 'string') {
263
+ env[key] = value;
264
+ }
265
+ else {
266
+ logger.warn({ taskId, key }, 'agentEnv key blocked by allowlist');
267
+ }
268
+ }
269
+ }
270
+ catch (err) {
271
+ logger.warn({ taskId, err }, 'Failed to parse agentEnv JSON, using empty object');
272
+ }
273
+ }
274
+ return env;
275
+ }
276
+ //# sourceMappingURL=workspace-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-manager.js","sourceRoot":"","sources":["../src/workspace-manager.ts"],"names":[],"mappings":"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"}