@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.
- package/dist/ansi-stripper.d.ts +11 -0
- package/dist/ansi-stripper.d.ts.map +1 -0
- package/dist/ansi-stripper.js +19 -0
- package/dist/ansi-stripper.js.map +1 -0
- package/dist/capability-detector.d.ts +5 -0
- package/dist/capability-detector.d.ts.map +1 -0
- package/dist/capability-detector.js +43 -0
- package/dist/capability-detector.js.map +1 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +144 -0
- package/dist/config.js.map +1 -0
- package/dist/executor/base.executor.d.ts +55 -0
- package/dist/executor/base.executor.d.ts.map +1 -0
- package/dist/executor/base.executor.js +30 -0
- package/dist/executor/base.executor.js.map +1 -0
- package/dist/executor/claude.executor.d.ts +23 -0
- package/dist/executor/claude.executor.d.ts.map +1 -0
- package/dist/executor/claude.executor.js +106 -0
- package/dist/executor/claude.executor.js.map +1 -0
- package/dist/executor/codex.executor.d.ts +20 -0
- package/dist/executor/codex.executor.d.ts.map +1 -0
- package/dist/executor/codex.executor.js +51 -0
- package/dist/executor/codex.executor.js.map +1 -0
- package/dist/executor/cursor.executor.d.ts +19 -0
- package/dist/executor/cursor.executor.d.ts.map +1 -0
- package/dist/executor/cursor.executor.js +46 -0
- package/dist/executor/cursor.executor.js.map +1 -0
- package/dist/executor/custom.executor.d.ts +21 -0
- package/dist/executor/custom.executor.d.ts.map +1 -0
- package/dist/executor/custom.executor.js +57 -0
- package/dist/executor/custom.executor.js.map +1 -0
- package/dist/executor/executor.factory.d.ts +9 -0
- package/dist/executor/executor.factory.d.ts.map +1 -0
- package/dist/executor/executor.factory.js +29 -0
- package/dist/executor/executor.factory.js.map +1 -0
- package/dist/git-operations.d.ts +23 -0
- package/dist/git-operations.d.ts.map +1 -0
- package/dist/git-operations.js +94 -0
- package/dist/git-operations.js.map +1 -0
- package/dist/hardware.d.ts +14 -0
- package/dist/hardware.d.ts.map +1 -0
- package/dist/hardware.js +92 -0
- package/dist/hardware.js.map +1 -0
- package/dist/healthz.d.ts +14 -0
- package/dist/healthz.d.ts.map +1 -0
- package/dist/healthz.js +104 -0
- package/dist/healthz.js.map +1 -0
- package/dist/jwt-manager.d.ts +23 -0
- package/dist/jwt-manager.d.ts.map +1 -0
- package/dist/jwt-manager.js +169 -0
- package/dist/jwt-manager.js.map +1 -0
- package/dist/lease-manager.d.ts +17 -0
- package/dist/lease-manager.d.ts.map +1 -0
- package/dist/lease-manager.js +62 -0
- package/dist/lease-manager.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +497 -0
- package/dist/main.js.map +1 -0
- package/dist/orphan-reaper.d.ts +6 -0
- package/dist/orphan-reaper.d.ts.map +1 -0
- package/dist/orphan-reaper.js +148 -0
- package/dist/orphan-reaper.js.map +1 -0
- package/dist/pipeline-parser.d.ts +14 -0
- package/dist/pipeline-parser.d.ts.map +1 -0
- package/dist/pipeline-parser.js +183 -0
- package/dist/pipeline-parser.js.map +1 -0
- package/dist/pipeline-runner.d.ts +120 -0
- package/dist/pipeline-runner.d.ts.map +1 -0
- package/dist/pipeline-runner.js +568 -0
- package/dist/pipeline-runner.js.map +1 -0
- package/dist/project-mutex.d.ts +14 -0
- package/dist/project-mutex.d.ts.map +1 -0
- package/dist/project-mutex.js +25 -0
- package/dist/project-mutex.js.map +1 -0
- package/dist/pty-manager.d.ts +50 -0
- package/dist/pty-manager.d.ts.map +1 -0
- package/dist/pty-manager.js +203 -0
- package/dist/pty-manager.js.map +1 -0
- package/dist/ringbuffer.d.ts +22 -0
- package/dist/ringbuffer.d.ts.map +1 -0
- package/dist/ringbuffer.js +57 -0
- package/dist/ringbuffer.js.map +1 -0
- package/dist/safe-env.d.ts +6 -0
- package/dist/safe-env.d.ts.map +1 -0
- package/dist/safe-env.js +19 -0
- package/dist/safe-env.js.map +1 -0
- package/dist/stage-detector.d.ts +62 -0
- package/dist/stage-detector.d.ts.map +1 -0
- package/dist/stage-detector.js +140 -0
- package/dist/stage-detector.js.map +1 -0
- package/dist/task-handler.d.ts +56 -0
- package/dist/task-handler.d.ts.map +1 -0
- package/dist/task-handler.js +296 -0
- package/dist/task-handler.js.map +1 -0
- package/dist/tunnel-manager.d.ts +34 -0
- package/dist/tunnel-manager.d.ts.map +1 -0
- package/dist/tunnel-manager.js +165 -0
- package/dist/tunnel-manager.js.map +1 -0
- package/dist/worker-client.d.ts +62 -0
- package/dist/worker-client.d.ts.map +1 -0
- package/dist/worker-client.js +303 -0
- package/dist/worker-client.js.map +1 -0
- package/dist/workspace-manager.d.ts +51 -0
- package/dist/workspace-manager.d.ts.map +1 -0
- package/dist/workspace-manager.js +276 -0
- package/dist/workspace-manager.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { IPty } from 'node-pty';
|
|
2
|
+
import type { ConfigSnapshot } from '@overlordai/protocol';
|
|
3
|
+
import { RingBuffer } from './ringbuffer.js';
|
|
4
|
+
import type { AgentExecutor } from './executor/base.executor.js';
|
|
5
|
+
export interface PtySessionParams {
|
|
6
|
+
taskId: number;
|
|
7
|
+
workspacePath: string;
|
|
8
|
+
env: Record<string, string>;
|
|
9
|
+
configSnapshot: ConfigSnapshot;
|
|
10
|
+
developer: {
|
|
11
|
+
gitName: string;
|
|
12
|
+
gitEmail: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface PtySession {
|
|
16
|
+
taskId: number;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
pty: IPty;
|
|
19
|
+
agentType: string;
|
|
20
|
+
executor: AgentExecutor;
|
|
21
|
+
ringBuffer: RingBuffer;
|
|
22
|
+
onData: (handler: (data: string) => void) => void;
|
|
23
|
+
onExit: (handler: (exitCode: number) => void) => void;
|
|
24
|
+
clearHandlers: () => void;
|
|
25
|
+
cleanOutput: string;
|
|
26
|
+
resetCleanOutput: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Manages PTY session lifecycle: creation, I/O, cancellation, and cleanup.
|
|
30
|
+
* Each task gets a single PTY session identified by a UUID sessionId.
|
|
31
|
+
*/
|
|
32
|
+
export declare class PtyManager {
|
|
33
|
+
private sessions;
|
|
34
|
+
/** Create a new PTY session and start the agent CLI. */
|
|
35
|
+
createSession(params: PtySessionParams): PtySession;
|
|
36
|
+
/** Get an active session by taskId. */
|
|
37
|
+
getSession(taskId: number): PtySession | null;
|
|
38
|
+
/** Close a session and destroy the PTY. */
|
|
39
|
+
closeSession(taskId: number): Promise<void>;
|
|
40
|
+
/** Write data to the PTY stdin. */
|
|
41
|
+
write(taskId: number, data: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Cancel a task's PTY session.
|
|
44
|
+
* Sends SIGTERM first, waits 10 seconds, then SIGKILL if still alive.
|
|
45
|
+
*/
|
|
46
|
+
cancel(taskId: number): Promise<void>;
|
|
47
|
+
/** Get the replay buffer contents for a session (used on reconnect). */
|
|
48
|
+
getReplayBuffer(taskId: number): string;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=pty-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty-manager.d.ts","sourceRoot":"","sources":["../src/pty-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAMjE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,IAAI,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;IAClD,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;IACtD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAiC;IAEjD,wDAAwD;IACxD,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IA8FnD,uCAAuC;IACvC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAI7C,2CAA2C;IACrC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBjD,mCAAmC;IACnC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAQzC;;;OAGG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6D3C,wEAAwE;IACxE,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAOxC"}
|
|
@@ -0,0 +1,203 @@
|
|
|
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.PtyManager = void 0;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const protocol_1 = require("@overlordai/protocol");
|
|
9
|
+
const pino_1 = __importDefault(require("pino"));
|
|
10
|
+
const ringbuffer_js_1 = require("./ringbuffer.js");
|
|
11
|
+
const ansi_stripper_js_1 = require("./ansi-stripper.js");
|
|
12
|
+
const executor_factory_js_1 = require("./executor/executor.factory.js");
|
|
13
|
+
const log = (0, pino_1.default)({ name: 'pty-manager' });
|
|
14
|
+
const CANCEL_GRACE_MS = 10_000;
|
|
15
|
+
/**
|
|
16
|
+
* Manages PTY session lifecycle: creation, I/O, cancellation, and cleanup.
|
|
17
|
+
* Each task gets a single PTY session identified by a UUID sessionId.
|
|
18
|
+
*/
|
|
19
|
+
class PtyManager {
|
|
20
|
+
sessions = new Map();
|
|
21
|
+
/** Create a new PTY session and start the agent CLI. */
|
|
22
|
+
createSession(params) {
|
|
23
|
+
const { taskId, workspacePath, env, configSnapshot, developer } = params;
|
|
24
|
+
if (this.sessions.has(taskId)) {
|
|
25
|
+
throw new Error(`PTY session already exists for task ${taskId}`);
|
|
26
|
+
}
|
|
27
|
+
const executor = executor_factory_js_1.ExecutorFactory.create(configSnapshot.agentType);
|
|
28
|
+
const sessionId = (0, node_crypto_1.randomUUID)();
|
|
29
|
+
// Build minimal environment — do NOT inherit Worker's process.env
|
|
30
|
+
const ptyEnv = {
|
|
31
|
+
HOME: workspacePath,
|
|
32
|
+
PATH: process.env['PATH'] ?? '/usr/local/bin:/usr/bin:/bin',
|
|
33
|
+
TERM: 'xterm-256color',
|
|
34
|
+
COLORTERM: 'truecolor',
|
|
35
|
+
GIT_AUTHOR_NAME: developer.gitName,
|
|
36
|
+
GIT_AUTHOR_EMAIL: developer.gitEmail,
|
|
37
|
+
GIT_COMMITTER_NAME: developer.gitName,
|
|
38
|
+
GIT_COMMITTER_EMAIL: developer.gitEmail,
|
|
39
|
+
OVERLORD_TASK_ID: String(taskId),
|
|
40
|
+
...env,
|
|
41
|
+
};
|
|
42
|
+
// Add GIT_SSH_COMMAND if SSH key path is available
|
|
43
|
+
if (env['GIT_SSH_COMMAND']) {
|
|
44
|
+
ptyEnv['GIT_SSH_COMMAND'] = env['GIT_SSH_COMMAND'];
|
|
45
|
+
}
|
|
46
|
+
const pty = executor.spawn(workspacePath, ptyEnv, configSnapshot);
|
|
47
|
+
const ringBuffer = new ringbuffer_js_1.RingBuffer(protocol_1.PTY_RINGBUFFER_LINES);
|
|
48
|
+
const dataHandlers = [];
|
|
49
|
+
const exitHandlers = [];
|
|
50
|
+
const session = {
|
|
51
|
+
taskId,
|
|
52
|
+
sessionId,
|
|
53
|
+
pty,
|
|
54
|
+
agentType: configSnapshot.agentType,
|
|
55
|
+
executor,
|
|
56
|
+
ringBuffer,
|
|
57
|
+
cleanOutput: '',
|
|
58
|
+
resetCleanOutput: () => {
|
|
59
|
+
session.cleanOutput = '';
|
|
60
|
+
},
|
|
61
|
+
onData: (handler) => {
|
|
62
|
+
dataHandlers.push(handler);
|
|
63
|
+
},
|
|
64
|
+
onExit: (handler) => {
|
|
65
|
+
exitHandlers.push(handler);
|
|
66
|
+
},
|
|
67
|
+
clearHandlers: () => {
|
|
68
|
+
dataHandlers.length = 0;
|
|
69
|
+
exitHandlers.length = 0;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
// Listen to PTY data events
|
|
73
|
+
pty.onData((data) => {
|
|
74
|
+
// Store stripped output in ring buffer
|
|
75
|
+
const clean = (0, ansi_stripper_js_1.stripAnsi)(data);
|
|
76
|
+
ringBuffer.pushData(clean);
|
|
77
|
+
// Keep recent clean output for prompt/stage detection.
|
|
78
|
+
// Use 8KB buffer and truncate at the last newline boundary to avoid
|
|
79
|
+
// splitting sentinel markers like [[OVERLORD_...]] across the cut point.
|
|
80
|
+
session.cleanOutput += clean;
|
|
81
|
+
if (session.cleanOutput.length > 8192) {
|
|
82
|
+
const truncated = session.cleanOutput.slice(-8192);
|
|
83
|
+
const nlIdx = truncated.indexOf('\n');
|
|
84
|
+
session.cleanOutput = nlIdx !== -1 ? truncated.slice(nlIdx + 1) : truncated;
|
|
85
|
+
}
|
|
86
|
+
// Notify registered handlers with raw data
|
|
87
|
+
for (const handler of dataHandlers) {
|
|
88
|
+
handler(data);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
// Listen to PTY exit events
|
|
92
|
+
pty.onExit(({ exitCode }) => {
|
|
93
|
+
log.info({ taskId, sessionId, exitCode }, 'PTY process exited');
|
|
94
|
+
for (const handler of exitHandlers) {
|
|
95
|
+
handler(exitCode);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
this.sessions.set(taskId, session);
|
|
99
|
+
log.info({ taskId, sessionId, agentType: configSnapshot.agentType }, 'PTY session created');
|
|
100
|
+
return session;
|
|
101
|
+
}
|
|
102
|
+
/** Get an active session by taskId. */
|
|
103
|
+
getSession(taskId) {
|
|
104
|
+
return this.sessions.get(taskId) ?? null;
|
|
105
|
+
}
|
|
106
|
+
/** Close a session and destroy the PTY. */
|
|
107
|
+
async closeSession(taskId) {
|
|
108
|
+
const session = this.sessions.get(taskId);
|
|
109
|
+
if (!session) {
|
|
110
|
+
log.warn({ taskId }, 'closeSession: no session found');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
session.pty.kill();
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Process may already be dead
|
|
118
|
+
}
|
|
119
|
+
this.sessions.delete(taskId);
|
|
120
|
+
log.info({ taskId, sessionId: session.sessionId }, 'PTY session closed');
|
|
121
|
+
}
|
|
122
|
+
/** Write data to the PTY stdin. */
|
|
123
|
+
write(taskId, data) {
|
|
124
|
+
const session = this.sessions.get(taskId);
|
|
125
|
+
if (!session) {
|
|
126
|
+
throw new Error(`No PTY session found for task ${taskId}`);
|
|
127
|
+
}
|
|
128
|
+
session.pty.write(data);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Cancel a task's PTY session.
|
|
132
|
+
* Sends SIGTERM first, waits 10 seconds, then SIGKILL if still alive.
|
|
133
|
+
*/
|
|
134
|
+
async cancel(taskId) {
|
|
135
|
+
const session = this.sessions.get(taskId);
|
|
136
|
+
if (!session) {
|
|
137
|
+
log.warn({ taskId }, 'cancel: no session found');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
log.info({ taskId, sessionId: session.sessionId }, 'Cancelling PTY session');
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
let resolved = false;
|
|
143
|
+
let killTimer = null;
|
|
144
|
+
const cleanup = () => {
|
|
145
|
+
if (resolved)
|
|
146
|
+
return;
|
|
147
|
+
resolved = true;
|
|
148
|
+
if (killTimer) {
|
|
149
|
+
clearTimeout(killTimer);
|
|
150
|
+
killTimer = null;
|
|
151
|
+
}
|
|
152
|
+
this.sessions.delete(taskId);
|
|
153
|
+
resolve();
|
|
154
|
+
};
|
|
155
|
+
// Listen for exit — use the session's onExit handler list so that
|
|
156
|
+
// clearHandlers() in the pipeline runner can remove stale listeners,
|
|
157
|
+
// and guard with a cancelled flag to prevent double-fire with the
|
|
158
|
+
// original exit handler registered by the pipeline.
|
|
159
|
+
let cancelled = false;
|
|
160
|
+
session.onExit(() => {
|
|
161
|
+
if (cancelled)
|
|
162
|
+
return;
|
|
163
|
+
cancelled = true;
|
|
164
|
+
cleanup();
|
|
165
|
+
});
|
|
166
|
+
// Send SIGTERM
|
|
167
|
+
try {
|
|
168
|
+
session.pty.kill('SIGTERM');
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// Process may already be dead
|
|
172
|
+
cleanup();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// After grace period, send SIGKILL
|
|
176
|
+
killTimer = setTimeout(() => {
|
|
177
|
+
if (resolved)
|
|
178
|
+
return;
|
|
179
|
+
log.warn({ taskId }, 'Process did not exit after SIGTERM, sending SIGKILL');
|
|
180
|
+
try {
|
|
181
|
+
session.pty.kill('SIGKILL');
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Process may already be dead
|
|
185
|
+
}
|
|
186
|
+
// Give a brief moment for SIGKILL to take effect, then cleanup
|
|
187
|
+
setTimeout(() => {
|
|
188
|
+
cleanup();
|
|
189
|
+
}, 500);
|
|
190
|
+
}, CANCEL_GRACE_MS);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/** Get the replay buffer contents for a session (used on reconnect). */
|
|
194
|
+
getReplayBuffer(taskId) {
|
|
195
|
+
const session = this.sessions.get(taskId);
|
|
196
|
+
if (!session) {
|
|
197
|
+
return '';
|
|
198
|
+
}
|
|
199
|
+
return session.ringBuffer.getContents();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
exports.PtyManager = PtyManager;
|
|
203
|
+
//# sourceMappingURL=pty-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pty-manager.js","sourceRoot":"","sources":["../src/pty-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed-size FIFO circular buffer for PTY output lines.
|
|
3
|
+
* When full, oldest lines are overwritten.
|
|
4
|
+
*/
|
|
5
|
+
export declare class RingBuffer {
|
|
6
|
+
private buffer;
|
|
7
|
+
private head;
|
|
8
|
+
private count;
|
|
9
|
+
private readonly maxLines;
|
|
10
|
+
constructor(maxLines?: number);
|
|
11
|
+
/** Push a single line into the buffer. */
|
|
12
|
+
push(line: string): void;
|
|
13
|
+
/** Push raw data that may contain multiple lines. */
|
|
14
|
+
pushData(data: string): void;
|
|
15
|
+
/** Return all stored lines in FIFO order, joined by newline. */
|
|
16
|
+
getContents(): string;
|
|
17
|
+
/** Return current number of stored lines. */
|
|
18
|
+
get size(): number;
|
|
19
|
+
/** Clear all stored lines. */
|
|
20
|
+
clear(): void;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=ringbuffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ringbuffer.d.ts","sourceRoot":"","sources":["../src/ringbuffer.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,QAAQ,GAAE,MAA6B;IAKnD,0CAA0C;IAC1C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQxB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO5B,gEAAgE;IAChE,WAAW,IAAI,MAAM;IAYrB,6CAA6C;IAC7C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,8BAA8B;IAC9B,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RingBuffer = void 0;
|
|
4
|
+
const protocol_1 = require("@overlordai/protocol");
|
|
5
|
+
/**
|
|
6
|
+
* Fixed-size FIFO circular buffer for PTY output lines.
|
|
7
|
+
* When full, oldest lines are overwritten.
|
|
8
|
+
*/
|
|
9
|
+
class RingBuffer {
|
|
10
|
+
buffer;
|
|
11
|
+
head = 0;
|
|
12
|
+
count = 0;
|
|
13
|
+
maxLines;
|
|
14
|
+
constructor(maxLines = protocol_1.PTY_RINGBUFFER_LINES) {
|
|
15
|
+
this.maxLines = maxLines;
|
|
16
|
+
this.buffer = new Array(maxLines);
|
|
17
|
+
}
|
|
18
|
+
/** Push a single line into the buffer. */
|
|
19
|
+
push(line) {
|
|
20
|
+
this.buffer[this.head] = line;
|
|
21
|
+
this.head = (this.head + 1) % this.maxLines;
|
|
22
|
+
if (this.count < this.maxLines) {
|
|
23
|
+
this.count++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Push raw data that may contain multiple lines. */
|
|
27
|
+
pushData(data) {
|
|
28
|
+
const lines = data.split('\n');
|
|
29
|
+
for (const line of lines) {
|
|
30
|
+
this.push(line);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Return all stored lines in FIFO order, joined by newline. */
|
|
34
|
+
getContents() {
|
|
35
|
+
if (this.count === 0)
|
|
36
|
+
return '';
|
|
37
|
+
const lines = [];
|
|
38
|
+
const start = this.count < this.maxLines ? 0 : this.head;
|
|
39
|
+
for (let i = 0; i < this.count; i++) {
|
|
40
|
+
const idx = (start + i) % this.maxLines;
|
|
41
|
+
lines.push(this.buffer[idx]);
|
|
42
|
+
}
|
|
43
|
+
return lines.join('\n');
|
|
44
|
+
}
|
|
45
|
+
/** Return current number of stored lines. */
|
|
46
|
+
get size() {
|
|
47
|
+
return this.count;
|
|
48
|
+
}
|
|
49
|
+
/** Clear all stored lines. */
|
|
50
|
+
clear() {
|
|
51
|
+
this.head = 0;
|
|
52
|
+
this.count = 0;
|
|
53
|
+
this.buffer = new Array(this.maxLines);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.RingBuffer = RingBuffer;
|
|
57
|
+
//# sourceMappingURL=ringbuffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ringbuffer.js","sourceRoot":"","sources":["../src/ringbuffer.ts"],"names":[],"mappings":";;;AAAA,mDAA4D;AAE5D;;;GAGG;AACH,MAAa,UAAU;IACb,MAAM,CAAW;IACjB,IAAI,GAAG,CAAC,CAAC;IACT,KAAK,GAAG,CAAC,CAAC;IACD,QAAQ,CAAS;IAElC,YAAY,WAAmB,+BAAoB;QACjD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAS,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAED,0CAA0C;IAC1C,IAAI,CAAC,IAAY;QACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC5C,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,QAAQ,CAAC,IAAY;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,WAAW;QACT,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEhC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAE,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,6CAA6C;IAC7C,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,8BAA8B;IAC9B,KAAK;QACH,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAS,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,CAAC;CACF;AApDD,gCAoDC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a sanitized environment object containing only safe variables.
|
|
3
|
+
* Prevents leaking secrets (OVERLORD_*, credentials, tokens) to child processes.
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildSafeEnv(): Record<string, string>;
|
|
6
|
+
//# sourceMappingURL=safe-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-env.d.ts","sourceRoot":"","sources":["../src/safe-env.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,wBAAgB,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASrD"}
|
package/dist/safe-env.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Builds a sanitized environment object containing only safe variables.
|
|
4
|
+
* Prevents leaking secrets (OVERLORD_*, credentials, tokens) to child processes.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.buildSafeEnv = buildSafeEnv;
|
|
8
|
+
const SAFE_KEYS = ['PATH', 'HOME', 'USER', 'SHELL', 'LANG', 'TERM', 'NODE_ENV'];
|
|
9
|
+
function buildSafeEnv() {
|
|
10
|
+
const env = {};
|
|
11
|
+
for (const key of SAFE_KEYS) {
|
|
12
|
+
const value = process.env[key];
|
|
13
|
+
if (value !== undefined) {
|
|
14
|
+
env[key] = value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return env;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=safe-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-env.js","sourceRoot":"","sources":["../src/safe-env.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAIH,oCASC;AAXD,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAU,CAAC;AAEzF,SAAgB,YAAY;IAC1B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export type DetectionType = 'sentinel' | 'exit_code' | 'prompt_idle' | 'keyword';
|
|
2
|
+
export type DetectionConfidence = 'high' | 'low';
|
|
3
|
+
export interface DetectionResult {
|
|
4
|
+
type: DetectionType;
|
|
5
|
+
complete: boolean;
|
|
6
|
+
exitCode?: number;
|
|
7
|
+
confidence?: DetectionConfidence;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Callback interface for prompt detection.
|
|
11
|
+
* Abstracts the AgentExecutor dependency.
|
|
12
|
+
*/
|
|
13
|
+
export interface PromptDetector {
|
|
14
|
+
detectPrompt(output: string): boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* StageDetector implements 4-priority stage completion detection:
|
|
18
|
+
* 1. Sentinel string in output (highest)
|
|
19
|
+
* 2. Process exit code (handled externally, reported via onProcessExit)
|
|
20
|
+
* 3. Prompt detected + idle (no output for PTY_IDLE_COMPLETE_MS) + silence window (PTY_SILENCE_WINDOW_MS)
|
|
21
|
+
* 4. Keyword matching (lowest, optional)
|
|
22
|
+
*/
|
|
23
|
+
export declare class StageDetector {
|
|
24
|
+
private stageName;
|
|
25
|
+
private promptDetector;
|
|
26
|
+
/** Timestamp of last output received */
|
|
27
|
+
private lastOutputTime;
|
|
28
|
+
/** Whether prompt has been detected in recent output */
|
|
29
|
+
private promptDetected;
|
|
30
|
+
/** Timestamp when idle condition (prompt + no output) was first met */
|
|
31
|
+
private idleDetectedAt;
|
|
32
|
+
/** Custom keywords from stage config */
|
|
33
|
+
private keywords;
|
|
34
|
+
/** Default keywords that indicate stage completion */
|
|
35
|
+
private static readonly DEFAULT_KEYWORDS;
|
|
36
|
+
constructor(stageName: string, promptDetector: PromptDetector, keywords?: string[]);
|
|
37
|
+
/**
|
|
38
|
+
* Called whenever new PTY output is received.
|
|
39
|
+
* Resets idle and silence timers.
|
|
40
|
+
*/
|
|
41
|
+
onOutput(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Detect stage completion from cleaned PTY output.
|
|
44
|
+
* Should be called periodically (e.g. on a polling interval or after output).
|
|
45
|
+
*
|
|
46
|
+
* Returns a DetectionResult if a completion signal is found, or null.
|
|
47
|
+
*/
|
|
48
|
+
detect(cleanOutput: string): DetectionResult | null;
|
|
49
|
+
/**
|
|
50
|
+
* Handle process exit event (priority 2).
|
|
51
|
+
* Called externally when the PTY process exits.
|
|
52
|
+
*/
|
|
53
|
+
onProcessExit(exitCode: number): DetectionResult;
|
|
54
|
+
/**
|
|
55
|
+
* Reset detector state for a new stage.
|
|
56
|
+
*/
|
|
57
|
+
reset(stageName: string): void;
|
|
58
|
+
private detectSentinel;
|
|
59
|
+
private detectPromptIdle;
|
|
60
|
+
private detectKeyword;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=stage-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage-detector.d.ts","sourceRoot":"","sources":["../src/stage-detector.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,SAAS,CAAC;AACjF,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,KAAK,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,mBAAmB,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;CACvC;AAED;;;;;;GAMG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAiB;IAEvC,wCAAwC;IACxC,OAAO,CAAC,cAAc,CAAa;IAEnC,wDAAwD;IACxD,OAAO,CAAC,cAAc,CAAkB;IAExC,uEAAuE;IACvE,OAAO,CAAC,cAAc,CAAa;IAEnC,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAAW;IAE3B,sDAAsD;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAyC;gBAErE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE;IAOlF;;;OAGG;IACH,QAAQ,IAAI,IAAI;IAMhB;;;;;OAKG;IACH,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAkBnD;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IAShD;;OAEG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAS9B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,gBAAgB;IA+BxB,OAAO,CAAC,aAAa;CAiBtB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
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.StageDetector = void 0;
|
|
7
|
+
const protocol_1 = require("@overlordai/protocol");
|
|
8
|
+
const pino_1 = __importDefault(require("pino"));
|
|
9
|
+
const log = (0, pino_1.default)({ name: 'stage-detector' });
|
|
10
|
+
/**
|
|
11
|
+
* StageDetector implements 4-priority stage completion detection:
|
|
12
|
+
* 1. Sentinel string in output (highest)
|
|
13
|
+
* 2. Process exit code (handled externally, reported via onProcessExit)
|
|
14
|
+
* 3. Prompt detected + idle (no output for PTY_IDLE_COMPLETE_MS) + silence window (PTY_SILENCE_WINDOW_MS)
|
|
15
|
+
* 4. Keyword matching (lowest, optional)
|
|
16
|
+
*/
|
|
17
|
+
class StageDetector {
|
|
18
|
+
stageName;
|
|
19
|
+
promptDetector;
|
|
20
|
+
/** Timestamp of last output received */
|
|
21
|
+
lastOutputTime = 0;
|
|
22
|
+
/** Whether prompt has been detected in recent output */
|
|
23
|
+
promptDetected = false;
|
|
24
|
+
/** Timestamp when idle condition (prompt + no output) was first met */
|
|
25
|
+
idleDetectedAt = 0;
|
|
26
|
+
/** Custom keywords from stage config */
|
|
27
|
+
keywords;
|
|
28
|
+
/** Default keywords that indicate stage completion */
|
|
29
|
+
static DEFAULT_KEYWORDS = /MR created|PR created|tests passed/i;
|
|
30
|
+
constructor(stageName, promptDetector, keywords) {
|
|
31
|
+
this.stageName = stageName;
|
|
32
|
+
this.promptDetector = promptDetector;
|
|
33
|
+
this.lastOutputTime = Date.now();
|
|
34
|
+
this.keywords = (keywords ?? []).map((k) => new RegExp(k, 'i'));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Called whenever new PTY output is received.
|
|
38
|
+
* Resets idle and silence timers.
|
|
39
|
+
*/
|
|
40
|
+
onOutput() {
|
|
41
|
+
this.lastOutputTime = Date.now();
|
|
42
|
+
// Reset silence window — new output means we're not idle
|
|
43
|
+
this.idleDetectedAt = 0;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Detect stage completion from cleaned PTY output.
|
|
47
|
+
* Should be called periodically (e.g. on a polling interval or after output).
|
|
48
|
+
*
|
|
49
|
+
* Returns a DetectionResult if a completion signal is found, or null.
|
|
50
|
+
*/
|
|
51
|
+
detect(cleanOutput) {
|
|
52
|
+
// Priority 1: Sentinel
|
|
53
|
+
const sentinelResult = this.detectSentinel(cleanOutput);
|
|
54
|
+
if (sentinelResult)
|
|
55
|
+
return sentinelResult;
|
|
56
|
+
// Priority 2: Exit code — handled externally via onProcessExit()
|
|
57
|
+
// Priority 3: Prompt + idle + silence
|
|
58
|
+
const idleResult = this.detectPromptIdle(cleanOutput);
|
|
59
|
+
if (idleResult)
|
|
60
|
+
return idleResult;
|
|
61
|
+
// Priority 4: Keyword matching
|
|
62
|
+
const keywordResult = this.detectKeyword(cleanOutput);
|
|
63
|
+
if (keywordResult)
|
|
64
|
+
return keywordResult;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Handle process exit event (priority 2).
|
|
69
|
+
* Called externally when the PTY process exits.
|
|
70
|
+
*/
|
|
71
|
+
onProcessExit(exitCode) {
|
|
72
|
+
log.info({ stageName: this.stageName, exitCode }, 'Process exited');
|
|
73
|
+
return {
|
|
74
|
+
type: 'exit_code',
|
|
75
|
+
complete: true,
|
|
76
|
+
exitCode,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reset detector state for a new stage.
|
|
81
|
+
*/
|
|
82
|
+
reset(stageName) {
|
|
83
|
+
this.stageName = stageName;
|
|
84
|
+
this.lastOutputTime = Date.now();
|
|
85
|
+
this.promptDetected = false;
|
|
86
|
+
this.idleDetectedAt = 0;
|
|
87
|
+
}
|
|
88
|
+
// -- Private detection methods --
|
|
89
|
+
detectSentinel(cleanOutput) {
|
|
90
|
+
const sentinel = `${protocol_1.STAGE_DONE_SENTINEL}${this.stageName}]]`;
|
|
91
|
+
if (cleanOutput.includes(sentinel)) {
|
|
92
|
+
log.info({ stageName: this.stageName }, 'Sentinel detected');
|
|
93
|
+
return { type: 'sentinel', complete: true, confidence: 'high' };
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
detectPromptIdle(cleanOutput) {
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
const idleElapsed = now - this.lastOutputTime;
|
|
100
|
+
// Check if prompt is present in output
|
|
101
|
+
if (this.promptDetector.detectPrompt(cleanOutput)) {
|
|
102
|
+
this.promptDetected = true;
|
|
103
|
+
}
|
|
104
|
+
if (!this.promptDetected)
|
|
105
|
+
return null;
|
|
106
|
+
// Check idle threshold (no output for PTY_IDLE_COMPLETE_MS = 30s)
|
|
107
|
+
if (idleElapsed < protocol_1.PTY_IDLE_COMPLETE_MS)
|
|
108
|
+
return null;
|
|
109
|
+
// Idle condition met — start silence window
|
|
110
|
+
if (this.idleDetectedAt === 0) {
|
|
111
|
+
this.idleDetectedAt = now;
|
|
112
|
+
log.debug({ stageName: this.stageName }, 'Idle condition met, starting silence window');
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
// Check silence window (PTY_SILENCE_WINDOW_MS = 5s)
|
|
116
|
+
const silenceElapsed = now - this.idleDetectedAt;
|
|
117
|
+
if (silenceElapsed >= protocol_1.PTY_SILENCE_WINDOW_MS) {
|
|
118
|
+
log.info({ stageName: this.stageName, idleMs: idleElapsed, silenceMs: silenceElapsed }, 'Prompt + idle + silence detected');
|
|
119
|
+
return { type: 'prompt_idle', complete: true, confidence: 'high' };
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
detectKeyword(cleanOutput) {
|
|
124
|
+
// Check custom keywords first
|
|
125
|
+
for (const kw of this.keywords) {
|
|
126
|
+
if (kw.test(cleanOutput)) {
|
|
127
|
+
log.info({ stageName: this.stageName, keyword: kw.source }, 'Custom keyword detected');
|
|
128
|
+
return { type: 'keyword', complete: true, confidence: 'low' };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Check default keywords
|
|
132
|
+
if (StageDetector.DEFAULT_KEYWORDS.test(cleanOutput)) {
|
|
133
|
+
log.info({ stageName: this.stageName }, 'Default keyword detected');
|
|
134
|
+
return { type: 'keyword', complete: true, confidence: 'low' };
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.StageDetector = StageDetector;
|
|
140
|
+
//# sourceMappingURL=stage-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage-detector.js","sourceRoot":"","sources":["../src/stage-detector.ts"],"names":[],"mappings":"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"}
|