@generacy-ai/orchestrator 0.0.0-preview-20260304013206
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/LICENSE +191 -0
- package/dist/auth/api-key.d.ts +63 -0
- package/dist/auth/api-key.d.ts.map +1 -0
- package/dist/auth/api-key.js +102 -0
- package/dist/auth/api-key.js.map +1 -0
- package/dist/auth/github-oauth.d.ts +29 -0
- package/dist/auth/github-oauth.d.ts.map +1 -0
- package/dist/auth/github-oauth.js +136 -0
- package/dist/auth/github-oauth.js.map +1 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +9 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/jwt.d.ts +34 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +74 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/middleware.d.ts +43 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +101 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +5 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +21 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +295 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +733 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +174 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +30 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +173 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +32 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +92 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +31 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +89 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/routes/agents.d.ts +7 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +78 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/dispatch.d.ts +8 -0
- package/dist/routes/dispatch.d.ts.map +1 -0
- package/dist/routes/dispatch.js +46 -0
- package/dist/routes/dispatch.js.map +1 -0
- package/dist/routes/events.d.ts +14 -0
- package/dist/routes/events.d.ts.map +1 -0
- package/dist/routes/events.js +240 -0
- package/dist/routes/events.js.map +1 -0
- package/dist/routes/health.d.ts +14 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +132 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +36 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +34 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/integrations.d.ts +34 -0
- package/dist/routes/integrations.d.ts.map +1 -0
- package/dist/routes/integrations.js +81 -0
- package/dist/routes/integrations.js.map +1 -0
- package/dist/routes/metrics.d.ts +31 -0
- package/dist/routes/metrics.d.ts.map +1 -0
- package/dist/routes/metrics.js +157 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/routes/pr-webhooks.d.ts +15 -0
- package/dist/routes/pr-webhooks.d.ts.map +1 -0
- package/dist/routes/pr-webhooks.js +114 -0
- package/dist/routes/pr-webhooks.js.map +1 -0
- package/dist/routes/queue.d.ts +7 -0
- package/dist/routes/queue.d.ts.map +1 -0
- package/dist/routes/queue.js +94 -0
- package/dist/routes/queue.js.map +1 -0
- package/dist/routes/webhooks.d.ts +15 -0
- package/dist/routes/webhooks.d.ts.map +1 -0
- package/dist/routes/webhooks.js +117 -0
- package/dist/routes/webhooks.js.map +1 -0
- package/dist/routes/workflows.d.ts +7 -0
- package/dist/routes/workflows.d.ts.map +1 -0
- package/dist/routes/workflows.js +138 -0
- package/dist/routes/workflows.js.map +1 -0
- package/dist/server.d.ts +35 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +264 -0
- package/dist/server.js.map +1 -0
- package/dist/services/agent-registry.d.ts +89 -0
- package/dist/services/agent-registry.d.ts.map +1 -0
- package/dist/services/agent-registry.js +173 -0
- package/dist/services/agent-registry.js.map +1 -0
- package/dist/services/epic-completion-monitor-service.d.ts +69 -0
- package/dist/services/epic-completion-monitor-service.d.ts.map +1 -0
- package/dist/services/epic-completion-monitor-service.js +200 -0
- package/dist/services/epic-completion-monitor-service.js.map +1 -0
- package/dist/services/identity.d.ts +41 -0
- package/dist/services/identity.d.ts.map +1 -0
- package/dist/services/identity.js +91 -0
- package/dist/services/identity.js.map +1 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +13 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/label-monitor-service.d.ts +95 -0
- package/dist/services/label-monitor-service.d.ts.map +1 -0
- package/dist/services/label-monitor-service.js +414 -0
- package/dist/services/label-monitor-service.js.map +1 -0
- package/dist/services/label-sync-service.d.ts +76 -0
- package/dist/services/label-sync-service.d.ts.map +1 -0
- package/dist/services/label-sync-service.js +108 -0
- package/dist/services/label-sync-service.js.map +1 -0
- package/dist/services/phase-tracker-service.d.ts +32 -0
- package/dist/services/phase-tracker-service.d.ts.map +1 -0
- package/dist/services/phase-tracker-service.js +82 -0
- package/dist/services/phase-tracker-service.js.map +1 -0
- package/dist/services/pr-feedback-monitor-service.d.ts +86 -0
- package/dist/services/pr-feedback-monitor-service.d.ts.map +1 -0
- package/dist/services/pr-feedback-monitor-service.js +423 -0
- package/dist/services/pr-feedback-monitor-service.js.map +1 -0
- package/dist/services/queue-service.d.ts +62 -0
- package/dist/services/queue-service.d.ts.map +1 -0
- package/dist/services/queue-service.js +147 -0
- package/dist/services/queue-service.js.map +1 -0
- package/dist/services/redis-queue-adapter.d.ts +35 -0
- package/dist/services/redis-queue-adapter.d.ts.map +1 -0
- package/dist/services/redis-queue-adapter.js +218 -0
- package/dist/services/redis-queue-adapter.js.map +1 -0
- package/dist/services/smee-receiver.d.ts +87 -0
- package/dist/services/smee-receiver.d.ts.map +1 -0
- package/dist/services/smee-receiver.js +225 -0
- package/dist/services/smee-receiver.js.map +1 -0
- package/dist/services/webhook-setup-service.d.ts +253 -0
- package/dist/services/webhook-setup-service.d.ts.map +1 -0
- package/dist/services/webhook-setup-service.js +369 -0
- package/dist/services/webhook-setup-service.js.map +1 -0
- package/dist/services/worker-dispatcher.d.ts +57 -0
- package/dist/services/worker-dispatcher.d.ts.map +1 -0
- package/dist/services/worker-dispatcher.js +211 -0
- package/dist/services/worker-dispatcher.js.map +1 -0
- package/dist/services/workflow-service.d.ts +65 -0
- package/dist/services/workflow-service.d.ts.map +1 -0
- package/dist/services/workflow-service.js +164 -0
- package/dist/services/workflow-service.js.map +1 -0
- package/dist/sse/events.d.ts +50 -0
- package/dist/sse/events.d.ts.map +1 -0
- package/dist/sse/events.js +152 -0
- package/dist/sse/events.js.map +1 -0
- package/dist/sse/index.d.ts +4 -0
- package/dist/sse/index.d.ts.map +1 -0
- package/dist/sse/index.js +7 -0
- package/dist/sse/index.js.map +1 -0
- package/dist/sse/stream.d.ts +109 -0
- package/dist/sse/stream.d.ts.map +1 -0
- package/dist/sse/stream.js +239 -0
- package/dist/sse/stream.js.map +1 -0
- package/dist/sse/subscriptions.d.ts +91 -0
- package/dist/sse/subscriptions.d.ts.map +1 -0
- package/dist/sse/subscriptions.js +299 -0
- package/dist/sse/subscriptions.js.map +1 -0
- package/dist/types/api.d.ts +651 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +169 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +27 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/monitor.d.ts +243 -0
- package/dist/types/monitor.d.ts.map +1 -0
- package/dist/types/monitor.js +2 -0
- package/dist/types/monitor.js.map +1 -0
- package/dist/types/problem-details.d.ts +63 -0
- package/dist/types/problem-details.d.ts.map +1 -0
- package/dist/types/problem-details.js +37 -0
- package/dist/types/problem-details.js.map +1 -0
- package/dist/types/sse.d.ts +224 -0
- package/dist/types/sse.d.ts.map +1 -0
- package/dist/types/sse.js +103 -0
- package/dist/types/sse.js.map +1 -0
- package/dist/types/webhook.d.ts +59 -0
- package/dist/types/webhook.d.ts.map +1 -0
- package/dist/types/webhook.js +2 -0
- package/dist/types/webhook.js.map +1 -0
- package/dist/utils/correlation.d.ts +30 -0
- package/dist/utils/correlation.d.ts.map +1 -0
- package/dist/utils/correlation.js +34 -0
- package/dist/utils/correlation.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/shutdown.d.ts +28 -0
- package/dist/utils/shutdown.d.ts.map +1 -0
- package/dist/utils/shutdown.js +86 -0
- package/dist/utils/shutdown.js.map +1 -0
- package/dist/worker/claude-cli-worker.d.ts +61 -0
- package/dist/worker/claude-cli-worker.d.ts.map +1 -0
- package/dist/worker/claude-cli-worker.js +330 -0
- package/dist/worker/claude-cli-worker.js.map +1 -0
- package/dist/worker/cli-spawner.d.ts +42 -0
- package/dist/worker/cli-spawner.d.ts.map +1 -0
- package/dist/worker/cli-spawner.js +183 -0
- package/dist/worker/cli-spawner.js.map +1 -0
- package/dist/worker/config.d.ts +76 -0
- package/dist/worker/config.d.ts.map +1 -0
- package/dist/worker/config.js +39 -0
- package/dist/worker/config.js.map +1 -0
- package/dist/worker/epic-post-tasks.d.ts +73 -0
- package/dist/worker/epic-post-tasks.d.ts.map +1 -0
- package/dist/worker/epic-post-tasks.js +266 -0
- package/dist/worker/epic-post-tasks.js.map +1 -0
- package/dist/worker/gate-checker.d.ts +22 -0
- package/dist/worker/gate-checker.d.ts.map +1 -0
- package/dist/worker/gate-checker.js +41 -0
- package/dist/worker/gate-checker.js.map +1 -0
- package/dist/worker/index.d.ts +13 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +19 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/label-manager.d.ts +89 -0
- package/dist/worker/label-manager.d.ts.map +1 -0
- package/dist/worker/label-manager.js +200 -0
- package/dist/worker/label-manager.js.map +1 -0
- package/dist/worker/output-capture.d.ts +58 -0
- package/dist/worker/output-capture.d.ts.map +1 -0
- package/dist/worker/output-capture.js +153 -0
- package/dist/worker/output-capture.js.map +1 -0
- package/dist/worker/phase-loop.d.ts +64 -0
- package/dist/worker/phase-loop.d.ts.map +1 -0
- package/dist/worker/phase-loop.js +185 -0
- package/dist/worker/phase-loop.js.map +1 -0
- package/dist/worker/phase-resolver.d.ts +62 -0
- package/dist/worker/phase-resolver.d.ts.map +1 -0
- package/dist/worker/phase-resolver.js +144 -0
- package/dist/worker/phase-resolver.js.map +1 -0
- package/dist/worker/pr-feedback-handler.d.ts +105 -0
- package/dist/worker/pr-feedback-handler.d.ts.map +1 -0
- package/dist/worker/pr-feedback-handler.js +391 -0
- package/dist/worker/pr-feedback-handler.js.map +1 -0
- package/dist/worker/pr-linker.d.ts +69 -0
- package/dist/worker/pr-linker.d.ts.map +1 -0
- package/dist/worker/pr-linker.js +108 -0
- package/dist/worker/pr-linker.js.map +1 -0
- package/dist/worker/pr-manager.d.ts +61 -0
- package/dist/worker/pr-manager.d.ts.map +1 -0
- package/dist/worker/pr-manager.js +140 -0
- package/dist/worker/pr-manager.js.map +1 -0
- package/dist/worker/repo-checkout.d.ts +63 -0
- package/dist/worker/repo-checkout.d.ts.map +1 -0
- package/dist/worker/repo-checkout.js +160 -0
- package/dist/worker/repo-checkout.js.map +1 -0
- package/dist/worker/stage-comment-manager.d.ts +34 -0
- package/dist/worker/stage-comment-manager.d.ts.map +1 -0
- package/dist/worker/stage-comment-manager.js +114 -0
- package/dist/worker/stage-comment-manager.js.map +1 -0
- package/dist/worker/types.d.ts +192 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +53 -0
- package/dist/worker/types.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { QueueItem } from '../types/index.js';
|
|
2
|
+
import type { ProcessFactory, Logger } from './types.js';
|
|
3
|
+
import type { WorkerConfig } from './config.js';
|
|
4
|
+
import type { SSEEventEmitter } from './output-capture.js';
|
|
5
|
+
/**
|
|
6
|
+
* Dependencies that can be injected for testing.
|
|
7
|
+
*/
|
|
8
|
+
export interface ClaudeCliWorkerDeps {
|
|
9
|
+
processFactory?: ProcessFactory;
|
|
10
|
+
sseEmitter?: SSEEventEmitter;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Top-level worker class that composes all sub-components to process
|
|
14
|
+
* a QueueItem through the full speckit phase loop or route to specialized handlers.
|
|
15
|
+
*
|
|
16
|
+
* This class implements the WorkerHandler signature:
|
|
17
|
+
* `(item: QueueItem) => Promise<void>`
|
|
18
|
+
*
|
|
19
|
+
* It orchestrates:
|
|
20
|
+
* - Repository checkout
|
|
21
|
+
* - Command routing (address-pr-feedback → PrFeedbackHandler)
|
|
22
|
+
* - Phase resolution from issue labels (for process/continue commands)
|
|
23
|
+
* - Phase loop execution (CLI spawning, label management, gate checking)
|
|
24
|
+
* - SSE event emission for dashboard streaming
|
|
25
|
+
* - Error handling with structured label reporting
|
|
26
|
+
*/
|
|
27
|
+
export declare class ClaudeCliWorker {
|
|
28
|
+
private readonly config;
|
|
29
|
+
private readonly logger;
|
|
30
|
+
private readonly processFactory;
|
|
31
|
+
private readonly sseEmitter?;
|
|
32
|
+
private readonly repoCheckout;
|
|
33
|
+
private readonly phaseResolver;
|
|
34
|
+
constructor(config: WorkerConfig, logger: Logger, deps?: ClaudeCliWorkerDeps);
|
|
35
|
+
/**
|
|
36
|
+
* Process a queue item through the full phase loop or route to specialized handlers.
|
|
37
|
+
*
|
|
38
|
+
* This is the entry point invoked by the WorkerDispatcher.
|
|
39
|
+
*
|
|
40
|
+
* Command routing (T020):
|
|
41
|
+
* - `address-pr-feedback`: Routes to PrFeedbackHandler for PR review feedback
|
|
42
|
+
* - `process` / `continue`: Standard phase loop processing
|
|
43
|
+
*
|
|
44
|
+
* For phase loop processing:
|
|
45
|
+
* - Creates a WorkerContext
|
|
46
|
+
* - Resolves the starting phase from issue labels
|
|
47
|
+
* - Runs the phase loop to completion (or gate/error)
|
|
48
|
+
*/
|
|
49
|
+
handle(item: QueueItem): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Find the feature branch for an issue by checking for an existing PR.
|
|
52
|
+
*
|
|
53
|
+
* When resuming a workflow, the feature branch was created during the first
|
|
54
|
+
* run's setup phase. We find it by searching for an open draft PR that
|
|
55
|
+
* references the issue number.
|
|
56
|
+
*
|
|
57
|
+
* @returns The branch name, or undefined if no feature branch was found.
|
|
58
|
+
*/
|
|
59
|
+
private resolveFeatureBranch;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=claude-cli-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-cli-worker.d.ts","sourceRoot":"","sources":["../../src/worker/claude-cli-worker.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAiB,cAAc,EAAsB,MAAM,EAAE,MAAM,YAAY,CAAC;AAE5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAyC3D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,eAAe;IAOxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;gBAG3B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,EAC/B,IAAI,GAAE,mBAAwB;IAQhC;;;;;;;;;;;;;OAaG;IACG,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAiT5C;;;;;;;;OAQG;YACW,oBAAoB;CA2BnC"}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createGitHubClient } from '@generacy-ai/workflow-engine';
|
|
3
|
+
import { getPhaseSequence } from './types.js';
|
|
4
|
+
import { PhaseResolver } from './phase-resolver.js';
|
|
5
|
+
import { LabelManager } from './label-manager.js';
|
|
6
|
+
import { StageCommentManager } from './stage-comment-manager.js';
|
|
7
|
+
import { GateChecker } from './gate-checker.js';
|
|
8
|
+
import { CliSpawner } from './cli-spawner.js';
|
|
9
|
+
import { OutputCapture } from './output-capture.js';
|
|
10
|
+
import { RepoCheckout } from './repo-checkout.js';
|
|
11
|
+
import { PhaseLoop } from './phase-loop.js';
|
|
12
|
+
import { PrManager } from './pr-manager.js';
|
|
13
|
+
import { PrFeedbackHandler } from './pr-feedback-handler.js';
|
|
14
|
+
import { EpicPostTasks } from './epic-post-tasks.js';
|
|
15
|
+
/**
|
|
16
|
+
* Default ProcessFactory that uses Node's child_process.spawn.
|
|
17
|
+
*/
|
|
18
|
+
const defaultProcessFactory = {
|
|
19
|
+
spawn(command, args, options) {
|
|
20
|
+
const child = spawn(command, args, {
|
|
21
|
+
cwd: options.cwd,
|
|
22
|
+
env: { ...process.env, ...options.env },
|
|
23
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
24
|
+
});
|
|
25
|
+
const exitPromise = new Promise((resolve) => {
|
|
26
|
+
child.on('exit', (code) => {
|
|
27
|
+
resolve(code);
|
|
28
|
+
});
|
|
29
|
+
child.on('error', () => {
|
|
30
|
+
resolve(1);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
stdout: child.stdout,
|
|
35
|
+
stderr: child.stderr,
|
|
36
|
+
pid: child.pid,
|
|
37
|
+
kill: (signal) => child.kill(signal),
|
|
38
|
+
exitPromise,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Top-level worker class that composes all sub-components to process
|
|
44
|
+
* a QueueItem through the full speckit phase loop or route to specialized handlers.
|
|
45
|
+
*
|
|
46
|
+
* This class implements the WorkerHandler signature:
|
|
47
|
+
* `(item: QueueItem) => Promise<void>`
|
|
48
|
+
*
|
|
49
|
+
* It orchestrates:
|
|
50
|
+
* - Repository checkout
|
|
51
|
+
* - Command routing (address-pr-feedback → PrFeedbackHandler)
|
|
52
|
+
* - Phase resolution from issue labels (for process/continue commands)
|
|
53
|
+
* - Phase loop execution (CLI spawning, label management, gate checking)
|
|
54
|
+
* - SSE event emission for dashboard streaming
|
|
55
|
+
* - Error handling with structured label reporting
|
|
56
|
+
*/
|
|
57
|
+
export class ClaudeCliWorker {
|
|
58
|
+
config;
|
|
59
|
+
logger;
|
|
60
|
+
processFactory;
|
|
61
|
+
sseEmitter;
|
|
62
|
+
repoCheckout;
|
|
63
|
+
phaseResolver;
|
|
64
|
+
constructor(config, logger, deps = {}) {
|
|
65
|
+
this.config = config;
|
|
66
|
+
this.logger = logger;
|
|
67
|
+
this.processFactory = deps.processFactory ?? defaultProcessFactory;
|
|
68
|
+
this.sseEmitter = deps.sseEmitter;
|
|
69
|
+
this.repoCheckout = new RepoCheckout(config.workspaceDir, logger);
|
|
70
|
+
this.phaseResolver = new PhaseResolver();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Process a queue item through the full phase loop or route to specialized handlers.
|
|
74
|
+
*
|
|
75
|
+
* This is the entry point invoked by the WorkerDispatcher.
|
|
76
|
+
*
|
|
77
|
+
* Command routing (T020):
|
|
78
|
+
* - `address-pr-feedback`: Routes to PrFeedbackHandler for PR review feedback
|
|
79
|
+
* - `process` / `continue`: Standard phase loop processing
|
|
80
|
+
*
|
|
81
|
+
* For phase loop processing:
|
|
82
|
+
* - Creates a WorkerContext
|
|
83
|
+
* - Resolves the starting phase from issue labels
|
|
84
|
+
* - Runs the phase loop to completion (or gate/error)
|
|
85
|
+
*/
|
|
86
|
+
async handle(item) {
|
|
87
|
+
const workerId = crypto.randomUUID();
|
|
88
|
+
const workflowId = `${item.owner}/${item.repo}#${item.issueNumber}`;
|
|
89
|
+
const workerLogger = this.logger.child({
|
|
90
|
+
workerId,
|
|
91
|
+
owner: item.owner,
|
|
92
|
+
repo: item.repo,
|
|
93
|
+
issue: item.issueNumber,
|
|
94
|
+
workflowName: item.workflowName,
|
|
95
|
+
});
|
|
96
|
+
workerLogger.info('Worker started processing queue item');
|
|
97
|
+
// Emit SSE workflow:started event
|
|
98
|
+
this.sseEmitter?.({
|
|
99
|
+
type: 'workflow:started',
|
|
100
|
+
workflowId,
|
|
101
|
+
data: {
|
|
102
|
+
owner: item.owner,
|
|
103
|
+
repo: item.repo,
|
|
104
|
+
issueNumber: item.issueNumber,
|
|
105
|
+
workflowName: item.workflowName,
|
|
106
|
+
command: item.command,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
// Create a GitHub client scoped to the checkout directory
|
|
110
|
+
let checkoutPath;
|
|
111
|
+
const abortController = new AbortController();
|
|
112
|
+
let labelManager;
|
|
113
|
+
let phasesCompleted = false;
|
|
114
|
+
let gateHit = false;
|
|
115
|
+
try {
|
|
116
|
+
// 1. Clone the default branch first (always works, even on first run)
|
|
117
|
+
const defaultBranch = await this.repoCheckout.getDefaultBranch(item.owner, item.repo);
|
|
118
|
+
checkoutPath = await this.repoCheckout.ensureCheckout(workerId, item.owner, item.repo, defaultBranch);
|
|
119
|
+
// Create GitHub client scoped to checkout dir
|
|
120
|
+
const github = createGitHubClient(checkoutPath);
|
|
121
|
+
// 2. Route address-pr-feedback command to PrFeedbackHandler (T020)
|
|
122
|
+
if (item.command === 'address-pr-feedback') {
|
|
123
|
+
workerLogger.info('Routing to PrFeedbackHandler for PR feedback addressing');
|
|
124
|
+
const prFeedbackHandler = new PrFeedbackHandler(this.config, workerLogger, this.processFactory, this.sseEmitter);
|
|
125
|
+
await prFeedbackHandler.handle(item, checkoutPath);
|
|
126
|
+
workerLogger.info('PR feedback addressing completed');
|
|
127
|
+
this.sseEmitter?.({
|
|
128
|
+
type: 'workflow:completed',
|
|
129
|
+
workflowId,
|
|
130
|
+
data: {
|
|
131
|
+
command: 'address-pr-feedback',
|
|
132
|
+
lastPhase: 'address-pr-feedback',
|
|
133
|
+
totalPhases: 1,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// 3. Get issue labels to resolve starting phase
|
|
139
|
+
const issue = await github.getIssue(item.owner, item.repo, item.issueNumber);
|
|
140
|
+
const labels = issue.labels.map((l) => typeof l === 'string' ? l : l.name);
|
|
141
|
+
// 4. Resolve starting phase (for process/continue commands)
|
|
142
|
+
const startPhase = this.phaseResolver.resolveStartPhase(labels, item.command, item.workflowName);
|
|
143
|
+
workerLogger.info({ startPhase, labels }, 'Resolved starting phase');
|
|
144
|
+
// 5. If resuming (has completed phases), find and checkout the feature branch
|
|
145
|
+
if (startPhase !== 'specify') {
|
|
146
|
+
const featureBranch = await this.resolveFeatureBranch(github, item.owner, item.repo, item.issueNumber, workerLogger);
|
|
147
|
+
if (featureBranch) {
|
|
148
|
+
workerLogger.info({ featureBranch }, 'Switching to existing feature branch for resume');
|
|
149
|
+
await this.repoCheckout.switchBranch(checkoutPath, featureBranch);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// 6. Build WorkerContext
|
|
153
|
+
const context = {
|
|
154
|
+
workerId,
|
|
155
|
+
item,
|
|
156
|
+
startPhase,
|
|
157
|
+
github,
|
|
158
|
+
logger: workerLogger,
|
|
159
|
+
signal: abortController.signal,
|
|
160
|
+
checkoutPath,
|
|
161
|
+
issueUrl: `https://github.com/${item.owner}/${item.repo}/issues/${item.issueNumber}`,
|
|
162
|
+
};
|
|
163
|
+
// 7. Create sub-components
|
|
164
|
+
labelManager = new LabelManager(github, item.owner, item.repo, item.issueNumber, workerLogger);
|
|
165
|
+
const stageCommentManager = new StageCommentManager(github, item.owner, item.repo, item.issueNumber, workerLogger);
|
|
166
|
+
const gateChecker = new GateChecker(workerLogger);
|
|
167
|
+
const cliSpawner = new CliSpawner(this.processFactory, workerLogger, this.config.shutdownGracePeriodMs);
|
|
168
|
+
const outputCapture = new OutputCapture(workflowId, workerLogger, this.sseEmitter);
|
|
169
|
+
const prManager = new PrManager(github, item.owner, item.repo, item.issueNumber, workerLogger);
|
|
170
|
+
// 7b. On resume, clean up gate labels before starting the phase loop
|
|
171
|
+
if (item.command === 'continue') {
|
|
172
|
+
await labelManager.onResumeStart();
|
|
173
|
+
}
|
|
174
|
+
// 7c. Handle tasks-review gate resume for epics (T015)
|
|
175
|
+
// When an epic resumes after tasks-review approval, run post-tasks directly
|
|
176
|
+
// instead of re-entering the phase loop. The phase loop already completed
|
|
177
|
+
// (specify → clarify → plan → tasks); we just need to create child issues.
|
|
178
|
+
if (item.workflowName === 'speckit-epic' && item.command === 'continue') {
|
|
179
|
+
if (labels.includes('completed:tasks-review')) {
|
|
180
|
+
workerLogger.info('Epic tasks-review gate satisfied — running post-tasks directly');
|
|
181
|
+
const epicPostTasks = new EpicPostTasks(workerLogger);
|
|
182
|
+
const postTasksResult = await epicPostTasks.execute(context);
|
|
183
|
+
if (postTasksResult.success) {
|
|
184
|
+
workerLogger.info({ childIssues: postTasksResult.childIssues.length }, 'Epic post-tasks complete after tasks-review resume');
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
workerLogger.error('Epic post-tasks failed after tasks-review resume');
|
|
188
|
+
}
|
|
189
|
+
this.sseEmitter?.({
|
|
190
|
+
type: 'workflow:completed',
|
|
191
|
+
workflowId,
|
|
192
|
+
data: {
|
|
193
|
+
command: item.command,
|
|
194
|
+
lastPhase: 'tasks',
|
|
195
|
+
totalPhases: 4,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// 8. Execute the phase loop
|
|
202
|
+
const phaseSequence = getPhaseSequence(item.workflowName);
|
|
203
|
+
const phaseLoop = new PhaseLoop(workerLogger);
|
|
204
|
+
const loopResult = await phaseLoop.executeLoop(context, this.config, {
|
|
205
|
+
labelManager,
|
|
206
|
+
stageCommentManager,
|
|
207
|
+
gateChecker,
|
|
208
|
+
cliSpawner,
|
|
209
|
+
outputCapture,
|
|
210
|
+
prManager,
|
|
211
|
+
}, phaseSequence);
|
|
212
|
+
// 9. Handle completion
|
|
213
|
+
if (loopResult.completed) {
|
|
214
|
+
phasesCompleted = true;
|
|
215
|
+
if (item.workflowName === 'speckit-epic') {
|
|
216
|
+
// Epic workflows: create child issues and pause (do NOT complete workflow or mark PR ready)
|
|
217
|
+
workerLogger.info('Epic phase loop complete — running post-tasks');
|
|
218
|
+
const epicPostTasks = new EpicPostTasks(workerLogger);
|
|
219
|
+
const postTasksResult = await epicPostTasks.execute(context);
|
|
220
|
+
if (postTasksResult.success) {
|
|
221
|
+
workerLogger.info({ childIssues: postTasksResult.childIssues.length }, 'Epic post-tasks complete — epic is now waiting for children');
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
workerLogger.error('Epic post-tasks failed — epic may need manual intervention');
|
|
225
|
+
}
|
|
226
|
+
this.sseEmitter?.({
|
|
227
|
+
type: 'workflow:completed',
|
|
228
|
+
workflowId,
|
|
229
|
+
data: {
|
|
230
|
+
command: item.command,
|
|
231
|
+
lastPhase: loopResult.lastPhase,
|
|
232
|
+
totalPhases: loopResult.results.length,
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// Non-epic workflows: standard completion flow
|
|
238
|
+
await labelManager.onWorkflowComplete();
|
|
239
|
+
workerLogger.info('Marking PR as ready for review');
|
|
240
|
+
await prManager.markReadyForReview();
|
|
241
|
+
workerLogger.info('Workflow completed successfully — all phases done');
|
|
242
|
+
this.sseEmitter?.({
|
|
243
|
+
type: 'workflow:completed',
|
|
244
|
+
workflowId,
|
|
245
|
+
data: {
|
|
246
|
+
command: item.command,
|
|
247
|
+
lastPhase: loopResult.lastPhase,
|
|
248
|
+
totalPhases: loopResult.results.length,
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else if (loopResult.gateHit) {
|
|
254
|
+
gateHit = true;
|
|
255
|
+
workerLogger.info({ lastPhase: loopResult.lastPhase }, 'Workflow paused at review gate');
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// Phase failure
|
|
259
|
+
workerLogger.error({ lastPhase: loopResult.lastPhase }, 'Workflow stopped due to phase failure');
|
|
260
|
+
this.sseEmitter?.({
|
|
261
|
+
type: 'workflow:failed',
|
|
262
|
+
workflowId,
|
|
263
|
+
data: {
|
|
264
|
+
command: item.command,
|
|
265
|
+
lastPhase: loopResult.lastPhase,
|
|
266
|
+
totalPhases: loopResult.results.length,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (phasesCompleted) {
|
|
273
|
+
// All phases completed successfully — the failure is in post-completion
|
|
274
|
+
// work (e.g. markReadyForReview, SSE emission). Log at warn level and
|
|
275
|
+
// do NOT re-throw, so WorkerDispatcher calls queue.complete() instead
|
|
276
|
+
// of queue.release().
|
|
277
|
+
workerLogger.warn({ error: String(error) }, 'Post-completion step failed (all phases completed successfully)');
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
workerLogger.error({ error: String(error) }, 'Worker encountered an unhandled error');
|
|
281
|
+
this.sseEmitter?.({
|
|
282
|
+
type: 'workflow:failed',
|
|
283
|
+
workflowId,
|
|
284
|
+
data: {
|
|
285
|
+
command: item.command,
|
|
286
|
+
error: error instanceof Error ? error.message : String(error),
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
finally {
|
|
293
|
+
// Cleanup: abort any in-flight operations
|
|
294
|
+
abortController.abort();
|
|
295
|
+
// Ensure agent:in-progress is cleaned up on every exit path.
|
|
296
|
+
// This is a no-op if onWorkflowComplete() or onError() already removed it.
|
|
297
|
+
// Gate hits intentionally leave agent:in-progress — the guard prevents unwanted cleanup.
|
|
298
|
+
if (labelManager && !gateHit) {
|
|
299
|
+
await labelManager.ensureCleanup();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Find the feature branch for an issue by checking for an existing PR.
|
|
305
|
+
*
|
|
306
|
+
* When resuming a workflow, the feature branch was created during the first
|
|
307
|
+
* run's setup phase. We find it by searching for an open draft PR that
|
|
308
|
+
* references the issue number.
|
|
309
|
+
*
|
|
310
|
+
* @returns The branch name, or undefined if no feature branch was found.
|
|
311
|
+
*/
|
|
312
|
+
async resolveFeatureBranch(github, owner, repo, issueNumber, logger) {
|
|
313
|
+
try {
|
|
314
|
+
// Search remote branches for one starting with the issue number
|
|
315
|
+
const branches = await github.listBranches(owner, repo);
|
|
316
|
+
const featureBranch = branches.find((b) => b.startsWith(`${issueNumber}-`));
|
|
317
|
+
if (featureBranch) {
|
|
318
|
+
logger.info({ featureBranch, issueNumber }, 'Found feature branch by issue number prefix');
|
|
319
|
+
return featureBranch;
|
|
320
|
+
}
|
|
321
|
+
logger.info({ issueNumber }, 'No feature branch found for issue');
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
logger.warn({ error: String(error), issueNumber }, 'Failed to resolve feature branch (non-fatal)');
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=claude-cli-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-cli-worker.js","sourceRoot":"","sources":["../../src/worker/claude-cli-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAIlE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;GAEG;AACH,MAAM,qBAAqB,GAAmB;IAC5C,KAAK,CACH,OAAe,EACf,IAAc,EACd,OAA2E;QAE3E,MAAM,KAAK,GAAiB,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YAC/C,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE;YACvC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;YACzD,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;gBACxB,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACrB,OAAO,CAAC,CAAC,CAAC,CAAC;YACb,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI,EAAE,CAAC,MAAuB,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;YACrD,WAAW;SACZ,CAAC;IACJ,CAAC;CACF,CAAC;AAUF;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,eAAe;IAOP;IACA;IAPF,cAAc,CAAiB;IAC/B,UAAU,CAAmB;IAC7B,YAAY,CAAe;IAC3B,aAAa,CAAgB;IAE9C,YACmB,MAAoB,EACpB,MAAc,EAC/B,OAA4B,EAAE;QAFb,WAAM,GAAN,MAAM,CAAc;QACpB,WAAM,GAAN,MAAM,CAAQ;QAG/B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,qBAAqB,CAAC;QACnE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAClE,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,MAAM,CAAC,IAAe;QAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QAEpE,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACrC,QAAQ;YACR,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,WAAW;YACvB,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC,CAAC;QAEH,YAAY,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QAE1D,kCAAkC;QAClC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,IAAI,EAAE,kBAAkB;YACxB,UAAU;YACV,IAAI,EAAE;gBACJ,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;gBAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;aACtB;SACF,CAAC,CAAC;QAEH,0DAA0D;QAC1D,IAAI,YAAgC,CAAC;QACrC,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,IAAI,YAAsC,CAAC;QAC3C,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,IAAI,CAAC;YACH,sEAAsE;YACtE,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YACtF,YAAY,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,cAAc,CACnD,QAAQ,EACR,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,IAAI,EACT,aAAa,CACd,CAAC;YAEF,8CAA8C;YAC9C,MAAM,MAAM,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAEhD,mEAAmE;YACnE,IAAI,IAAI,CAAC,OAAO,KAAK,qBAAqB,EAAE,CAAC;gBAC3C,YAAY,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;gBAE7E,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAC7C,IAAI,CAAC,MAAM,EACX,YAAY,EACZ,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,UAAU,CAChB,CAAC;gBAEF,MAAM,iBAAiB,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;gBAEnD,YAAY,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;gBAEtD,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,IAAI,EAAE,oBAAoB;oBAC1B,UAAU;oBACV,IAAI,EAAE;wBACJ,OAAO,EAAE,qBAAqB;wBAC9B,SAAS,EAAE,qBAAqB;wBAChC,WAAW,EAAE,CAAC;qBACf;iBACF,CAAC,CAAC;gBAEH,OAAO;YACT,CAAC;YAED,gDAAgD;YAChD,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YAC7E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACpC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACnC,CAAC;YAEF,4DAA4D;YAC5D,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,OAAiC,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YAC3H,YAAY,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC;YAErE,8EAA8E;YAC9E,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,oBAAoB,CACnD,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAC9D,CAAC;gBACF,IAAI,aAAa,EAAE,CAAC;oBAClB,YAAY,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,EAAE,iDAAiD,CAAC,CAAC;oBACxF,MAAM,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;gBACpE,CAAC;YACH,CAAC;YAED,yBAAyB;YACzB,MAAM,OAAO,GAAkB;gBAC7B,QAAQ;gBACR,IAAI;gBACJ,UAAU;gBACV,MAAM;gBACN,MAAM,EAAE,YAAY;gBACpB,MAAM,EAAE,eAAe,CAAC,MAAM;gBAC9B,YAAY;gBACZ,QAAQ,EAAE,sBAAsB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,WAAW,EAAE;aACrF,CAAC;YAEF,2BAA2B;YAC3B,YAAY,GAAG,IAAI,YAAY,CAC7B,MAAM,EACN,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,WAAW,EAChB,YAAY,CACb,CAAC;YAEF,MAAM,mBAAmB,GAAG,IAAI,mBAAmB,CACjD,MAAM,EACN,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,WAAW,EAChB,YAAY,CACb,CAAC;YAEF,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,YAAY,CAAC,CAAC;YAElD,MAAM,UAAU,GAAG,IAAI,UAAU,CAC/B,IAAI,CAAC,cAAc,EACnB,YAAY,EACZ,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAClC,CAAC;YAEF,MAAM,aAAa,GAAG,IAAI,aAAa,CACrC,UAAU,EACV,YAAY,EACZ,IAAI,CAAC,UAAU,CAChB,CAAC;YAEF,MAAM,SAAS,GAAG,IAAI,SAAS,CAC7B,MAAM,EACN,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,WAAW,EAChB,YAAY,CACb,CAAC;YAEF,qEAAqE;YACrE,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;gBAChC,MAAM,YAAY,CAAC,aAAa,EAAE,CAAC;YACrC,CAAC;YAED,uDAAuD;YACvD,4EAA4E;YAC5E,0EAA0E;YAC1E,2EAA2E;YAC3E,IAAI,IAAI,CAAC,YAAY,KAAK,cAAc,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;gBACxE,IAAI,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;oBAC9C,YAAY,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;oBACpF,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,YAAY,CAAC,CAAC;oBACtD,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;oBAE7D,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;wBAC5B,YAAY,CAAC,IAAI,CACf,EAAE,WAAW,EAAE,eAAe,CAAC,WAAW,CAAC,MAAM,EAAE,EACnD,oDAAoD,CACrD,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBACN,YAAY,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;oBACzE,CAAC;oBAED,IAAI,CAAC,UAAU,EAAE,CAAC;wBAChB,IAAI,EAAE,oBAAoB;wBAC1B,UAAU;wBACV,IAAI,EAAE;4BACJ,OAAO,EAAE,IAAI,CAAC,OAAO;4BACrB,SAAS,EAAE,OAAO;4BAClB,WAAW,EAAE,CAAC;yBACf;qBACF,CAAC,CAAC;oBAEH,OAAO;gBACT,CAAC;YACH,CAAC;YAED,4BAA4B;YAC5B,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1D,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE;gBACnE,YAAY;gBACZ,mBAAmB;gBACnB,WAAW;gBACX,UAAU;gBACV,aAAa;gBACb,SAAS;aACV,EAAE,aAAa,CAAC,CAAC;YAElB,uBAAuB;YACvB,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;gBACzB,eAAe,GAAG,IAAI,CAAC;gBAEvB,IAAI,IAAI,CAAC,YAAY,KAAK,cAAc,EAAE,CAAC;oBACzC,4FAA4F;oBAC5F,YAAY,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;oBACnE,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,YAAY,CAAC,CAAC;oBACtD,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;oBAE7D,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;wBAC5B,YAAY,CAAC,IAAI,CACf,EAAE,WAAW,EAAE,eAAe,CAAC,WAAW,CAAC,MAAM,EAAE,EACnD,6DAA6D,CAC9D,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBACN,YAAY,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;oBACnF,CAAC;oBAED,IAAI,CAAC,UAAU,EAAE,CAAC;wBAChB,IAAI,EAAE,oBAAoB;wBAC1B,UAAU;wBACV,IAAI,EAAE;4BACJ,OAAO,EAAE,IAAI,CAAC,OAAO;4BACrB,SAAS,EAAE,UAAU,CAAC,SAAS;4BAC/B,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM;yBACvC;qBACF,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,+CAA+C;oBAC/C,MAAM,YAAY,CAAC,kBAAkB,EAAE,CAAC;oBACxC,YAAY,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;oBACpD,MAAM,SAAS,CAAC,kBAAkB,EAAE,CAAC;oBACrC,YAAY,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;oBAEvE,IAAI,CAAC,UAAU,EAAE,CAAC;wBAChB,IAAI,EAAE,oBAAoB;wBAC1B,UAAU;wBACV,IAAI,EAAE;4BACJ,OAAO,EAAE,IAAI,CAAC,OAAO;4BACrB,SAAS,EAAE,UAAU,CAAC,SAAS;4BAC/B,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM;yBACvC;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;iBAAM,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;gBAC9B,OAAO,GAAG,IAAI,CAAC;gBACf,YAAY,CAAC,IAAI,CACf,EAAE,SAAS,EAAE,UAAU,CAAC,SAAS,EAAE,EACnC,gCAAgC,CACjC,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,gBAAgB;gBAChB,YAAY,CAAC,KAAK,CAChB,EAAE,SAAS,EAAE,UAAU,CAAC,SAAS,EAAE,EACnC,uCAAuC,CACxC,CAAC;gBAEF,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,IAAI,EAAE,iBAAiB;oBACvB,UAAU;oBACV,IAAI,EAAE;wBACJ,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,SAAS,EAAE,UAAU,CAAC,SAAS;wBAC/B,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM;qBACvC;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,eAAe,EAAE,CAAC;gBACpB,wEAAwE;gBACxE,sEAAsE;gBACtE,sEAAsE;gBACtE,sBAAsB;gBACtB,YAAY,CAAC,IAAI,CACf,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,EACxB,iEAAiE,CAClE,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,YAAY,CAAC,KAAK,CAChB,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,EACxB,uCAAuC,CACxC,CAAC;gBAEF,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,IAAI,EAAE,iBAAiB;oBACvB,UAAU;oBACV,IAAI,EAAE;wBACJ,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;qBAC9D;iBACF,CAAC,CAAC;gBAEH,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,0CAA0C;YAC1C,eAAe,CAAC,KAAK,EAAE,CAAC;YAExB,6DAA6D;YAC7D,2EAA2E;YAC3E,yFAAyF;YACzF,IAAI,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC7B,MAAM,YAAY,CAAC,aAAa,EAAE,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACK,KAAK,CAAC,oBAAoB,CAChC,MAAoB,EACpB,KAAa,EACb,IAAY,EACZ,WAAmB,EACnB,MAAc;QAEd,IAAI,CAAC;YACH,gEAAgE;YAChE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACxD,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC;YAE5E,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,EAAE,6CAA6C,CAAC,CAAC;gBAC3F,OAAO,aAAa,CAAC;YACvB,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,EAAE,mCAAmC,CAAC,CAAC;YAClE,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CACT,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,EACrC,8CAA8C,CAC/C,CAAC;YACF,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CliSpawnOptions, Logger, PhaseResult, ProcessFactory, WorkflowPhase } from './types.js';
|
|
2
|
+
import type { OutputCapture } from './output-capture.js';
|
|
3
|
+
/**
|
|
4
|
+
* Spawns Claude CLI processes (or shell commands for validation) and manages
|
|
5
|
+
* their lifecycle: stdout/stderr capture, timeout with SIGTERM then SIGKILL,
|
|
6
|
+
* and abort signal propagation.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CliSpawner {
|
|
9
|
+
private readonly processFactory;
|
|
10
|
+
private readonly logger;
|
|
11
|
+
private readonly shutdownGracePeriodMs;
|
|
12
|
+
constructor(processFactory: ProcessFactory, logger: Logger, shutdownGracePeriodMs?: number);
|
|
13
|
+
/**
|
|
14
|
+
* Spawn a Claude CLI process for the given workflow phase.
|
|
15
|
+
*
|
|
16
|
+
* Builds the command line from PHASE_TO_COMMAND, captures stdout via the
|
|
17
|
+
* provided OutputCapture, and handles timeout / abort with a
|
|
18
|
+
* SIGTERM -> grace period -> SIGKILL sequence.
|
|
19
|
+
*/
|
|
20
|
+
spawnPhase(phase: WorkflowPhase, options: CliSpawnOptions, capture: OutputCapture): Promise<PhaseResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Run a shell validation command (e.g., `pnpm test && pnpm build`).
|
|
23
|
+
*
|
|
24
|
+
* Unlike `spawnPhase`, this does not invoke the Claude CLI; it runs an
|
|
25
|
+
* arbitrary shell command via `sh -c`.
|
|
26
|
+
*/
|
|
27
|
+
runValidatePhase(checkoutPath: string, validateCommand: string, signal: AbortSignal): Promise<PhaseResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Manages the full lifecycle of a spawned child process:
|
|
30
|
+
* - Capture stdout (via OutputCapture if provided) and stderr
|
|
31
|
+
* - Enforce timeout with SIGTERM -> grace period -> SIGKILL
|
|
32
|
+
* - Propagate abort signal
|
|
33
|
+
* - Return a PhaseResult once the process exits
|
|
34
|
+
*/
|
|
35
|
+
private manageProcess;
|
|
36
|
+
/**
|
|
37
|
+
* Send SIGTERM, wait the grace period, then send SIGKILL if the process
|
|
38
|
+
* is still alive.
|
|
39
|
+
*/
|
|
40
|
+
private gracefulKill;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=cli-spawner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-spawner.d.ts","sourceRoot":"","sources":["../../src/worker/cli-spawner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAEf,MAAM,EACN,WAAW,EACX,cAAc,EACd,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKzD;;;;GAIG;AACH,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBAFrB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,MAAM,EACd,qBAAqB,GAAE,MAAa;IAGvD;;;;;;OAMG;IACG,UAAU,CACd,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,WAAW,CAAC;IA2CvB;;;;;OAKG;IACG,gBAAgB,CACpB,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,WAAW,CAAC;IAqBvB;;;;;;OAMG;YACW,aAAa;IA4G3B;;;OAGG;IACH,OAAO,CAAC,YAAY;CAgBrB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { PHASE_TO_COMMAND } from './types.js';
|
|
2
|
+
/** Default timeout for the validate phase (10 minutes). */
|
|
3
|
+
const DEFAULT_VALIDATE_TIMEOUT_MS = 600_000;
|
|
4
|
+
/**
|
|
5
|
+
* Spawns Claude CLI processes (or shell commands for validation) and manages
|
|
6
|
+
* their lifecycle: stdout/stderr capture, timeout with SIGTERM then SIGKILL,
|
|
7
|
+
* and abort signal propagation.
|
|
8
|
+
*/
|
|
9
|
+
export class CliSpawner {
|
|
10
|
+
processFactory;
|
|
11
|
+
logger;
|
|
12
|
+
shutdownGracePeriodMs;
|
|
13
|
+
constructor(processFactory, logger, shutdownGracePeriodMs = 5000) {
|
|
14
|
+
this.processFactory = processFactory;
|
|
15
|
+
this.logger = logger;
|
|
16
|
+
this.shutdownGracePeriodMs = shutdownGracePeriodMs;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Spawn a Claude CLI process for the given workflow phase.
|
|
20
|
+
*
|
|
21
|
+
* Builds the command line from PHASE_TO_COMMAND, captures stdout via the
|
|
22
|
+
* provided OutputCapture, and handles timeout / abort with a
|
|
23
|
+
* SIGTERM -> grace period -> SIGKILL sequence.
|
|
24
|
+
*/
|
|
25
|
+
async spawnPhase(phase, options, capture) {
|
|
26
|
+
const command = PHASE_TO_COMMAND[phase];
|
|
27
|
+
if (command === null) {
|
|
28
|
+
throw new Error(`Phase "${phase}" has no CLI command (use runValidatePhase instead)`);
|
|
29
|
+
}
|
|
30
|
+
const prompt = `${command} ${options.prompt}`;
|
|
31
|
+
const args = [
|
|
32
|
+
'--headless',
|
|
33
|
+
'--output', 'json',
|
|
34
|
+
'--print', 'all',
|
|
35
|
+
'--max-turns', String(options.maxTurns),
|
|
36
|
+
];
|
|
37
|
+
// Resume a previous session to keep MCP servers warm and carry context
|
|
38
|
+
if (options.resumeSessionId) {
|
|
39
|
+
args.push('--resume', options.resumeSessionId);
|
|
40
|
+
}
|
|
41
|
+
args.push('--prompt', prompt);
|
|
42
|
+
this.logger.info({
|
|
43
|
+
phase,
|
|
44
|
+
cwd: options.cwd,
|
|
45
|
+
maxTurns: options.maxTurns,
|
|
46
|
+
timeoutMs: options.timeoutMs,
|
|
47
|
+
resumeSessionId: options.resumeSessionId ?? null,
|
|
48
|
+
}, options.resumeSessionId
|
|
49
|
+
? 'Resuming Claude CLI session for phase'
|
|
50
|
+
: 'Spawning new Claude CLI session for phase');
|
|
51
|
+
const child = this.processFactory.spawn('claude', args, {
|
|
52
|
+
cwd: options.cwd,
|
|
53
|
+
env: options.env,
|
|
54
|
+
});
|
|
55
|
+
return this.manageProcess(child, phase, options.timeoutMs, options.signal, capture);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Run a shell validation command (e.g., `pnpm test && pnpm build`).
|
|
59
|
+
*
|
|
60
|
+
* Unlike `spawnPhase`, this does not invoke the Claude CLI; it runs an
|
|
61
|
+
* arbitrary shell command via `sh -c`.
|
|
62
|
+
*/
|
|
63
|
+
async runValidatePhase(checkoutPath, validateCommand, signal) {
|
|
64
|
+
const phase = 'validate';
|
|
65
|
+
const timeoutMs = DEFAULT_VALIDATE_TIMEOUT_MS;
|
|
66
|
+
this.logger.info({ phase, cwd: checkoutPath, validateCommand, timeoutMs }, 'Spawning validation command');
|
|
67
|
+
const child = this.processFactory.spawn('sh', ['-c', validateCommand], {
|
|
68
|
+
cwd: checkoutPath,
|
|
69
|
+
env: {},
|
|
70
|
+
});
|
|
71
|
+
return this.manageProcess(child, phase, timeoutMs, signal, undefined);
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Private helpers
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Manages the full lifecycle of a spawned child process:
|
|
78
|
+
* - Capture stdout (via OutputCapture if provided) and stderr
|
|
79
|
+
* - Enforce timeout with SIGTERM -> grace period -> SIGKILL
|
|
80
|
+
* - Propagate abort signal
|
|
81
|
+
* - Return a PhaseResult once the process exits
|
|
82
|
+
*/
|
|
83
|
+
async manageProcess(child, phase, timeoutMs, signal, capture) {
|
|
84
|
+
const startTime = Date.now();
|
|
85
|
+
let stderrBuffer = '';
|
|
86
|
+
// ---- stdout ----
|
|
87
|
+
if (child.stdout && capture) {
|
|
88
|
+
child.stdout.on('data', (data) => {
|
|
89
|
+
capture.processChunk(typeof data === 'string' ? data : data.toString('utf-8'));
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// For runValidatePhase we still want to capture stdout even without OutputCapture,
|
|
93
|
+
// so we attach a no-op listener to avoid back-pressure issues.
|
|
94
|
+
if (child.stdout && !capture) {
|
|
95
|
+
child.stdout.on('data', () => {
|
|
96
|
+
// intentionally empty
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// ---- stderr ----
|
|
100
|
+
if (child.stderr) {
|
|
101
|
+
child.stderr.on('data', (data) => {
|
|
102
|
+
stderrBuffer += typeof data === 'string' ? data : data.toString('utf-8');
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// ---- Timeout handling ----
|
|
106
|
+
let timedOut = false;
|
|
107
|
+
const timeoutTimer = setTimeout(() => {
|
|
108
|
+
timedOut = true;
|
|
109
|
+
this.logger.warn({ phase, pid: child.pid, timeoutMs }, 'Phase timed out, sending SIGTERM');
|
|
110
|
+
this.gracefulKill(child, phase);
|
|
111
|
+
}, timeoutMs);
|
|
112
|
+
// ---- Abort signal handling ----
|
|
113
|
+
let aborted = false;
|
|
114
|
+
const onAbort = () => {
|
|
115
|
+
aborted = true;
|
|
116
|
+
this.logger.warn({ phase, pid: child.pid }, 'Abort signal received, sending SIGTERM');
|
|
117
|
+
this.gracefulKill(child, phase);
|
|
118
|
+
};
|
|
119
|
+
// Avoid attaching listener if already aborted.
|
|
120
|
+
if (signal.aborted) {
|
|
121
|
+
onAbort();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
125
|
+
}
|
|
126
|
+
// ---- Wait for exit ----
|
|
127
|
+
let exitCode;
|
|
128
|
+
try {
|
|
129
|
+
exitCode = await child.exitPromise;
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
clearTimeout(timeoutTimer);
|
|
133
|
+
signal.removeEventListener('abort', onAbort);
|
|
134
|
+
}
|
|
135
|
+
// ---- Flush capture ----
|
|
136
|
+
if (capture) {
|
|
137
|
+
capture.flush();
|
|
138
|
+
}
|
|
139
|
+
const durationMs = Date.now() - startTime;
|
|
140
|
+
const resolvedExitCode = exitCode ?? 1;
|
|
141
|
+
const success = resolvedExitCode === 0;
|
|
142
|
+
this.logger.info({ phase, exitCode: resolvedExitCode, durationMs, success, timedOut, aborted }, 'Phase process exited');
|
|
143
|
+
const result = {
|
|
144
|
+
phase,
|
|
145
|
+
success,
|
|
146
|
+
exitCode: resolvedExitCode,
|
|
147
|
+
durationMs,
|
|
148
|
+
output: capture ? capture.getOutput() : [],
|
|
149
|
+
sessionId: capture?.sessionId,
|
|
150
|
+
};
|
|
151
|
+
if (!success) {
|
|
152
|
+
let message = `Phase "${phase}" failed with exit code ${resolvedExitCode}`;
|
|
153
|
+
if (timedOut) {
|
|
154
|
+
message = `Phase "${phase}" timed out after ${timeoutMs}ms`;
|
|
155
|
+
}
|
|
156
|
+
else if (aborted) {
|
|
157
|
+
message = `Phase "${phase}" was aborted`;
|
|
158
|
+
}
|
|
159
|
+
result.error = {
|
|
160
|
+
message,
|
|
161
|
+
stderr: stderrBuffer,
|
|
162
|
+
phase,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Send SIGTERM, wait the grace period, then send SIGKILL if the process
|
|
169
|
+
* is still alive.
|
|
170
|
+
*/
|
|
171
|
+
gracefulKill(child, phase) {
|
|
172
|
+
child.kill('SIGTERM');
|
|
173
|
+
const killTimer = setTimeout(() => {
|
|
174
|
+
this.logger.warn({ phase, pid: child.pid }, 'Grace period expired, sending SIGKILL');
|
|
175
|
+
child.kill('SIGKILL');
|
|
176
|
+
}, this.shutdownGracePeriodMs);
|
|
177
|
+
// Clear the SIGKILL timer once the process exits on its own.
|
|
178
|
+
void child.exitPromise.then(() => {
|
|
179
|
+
clearTimeout(killTimer);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=cli-spawner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-spawner.js","sourceRoot":"","sources":["../../src/worker/cli-spawner.ts"],"names":[],"mappings":"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"}
|