@ai-sdlc/orchestrator 0.1.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/adapters.d.ts +60 -0
- package/dist/adapters.js +160 -0
- package/dist/admission.d.ts +49 -0
- package/dist/admission.js +90 -0
- package/dist/analysis/analyzer.d.ts +10 -0
- package/dist/analysis/analyzer.js +67 -0
- package/dist/analysis/complexity-scorer.d.ts +24 -0
- package/dist/analysis/complexity-scorer.js +39 -0
- package/dist/analysis/context-builder.d.ts +14 -0
- package/dist/analysis/context-builder.js +79 -0
- package/dist/analysis/convention-detector.d.ts +9 -0
- package/dist/analysis/convention-detector.js +177 -0
- package/dist/analysis/dependency-parser.d.ts +17 -0
- package/dist/analysis/dependency-parser.js +167 -0
- package/dist/analysis/file-walker.d.ts +18 -0
- package/dist/analysis/file-walker.js +158 -0
- package/dist/analysis/hotspot-analyzer.d.ts +28 -0
- package/dist/analysis/hotspot-analyzer.js +85 -0
- package/dist/analysis/index.d.ts +13 -0
- package/dist/analysis/index.js +12 -0
- package/dist/analysis/pattern-detector.d.ts +10 -0
- package/dist/analysis/pattern-detector.js +86 -0
- package/dist/analysis/types.d.ts +94 -0
- package/dist/analysis/types.js +5 -0
- package/dist/audit-archival.d.ts +48 -0
- package/dist/audit-archival.js +72 -0
- package/dist/audit-export.d.ts +45 -0
- package/dist/audit-export.js +100 -0
- package/dist/audit-extended.d.ts +28 -0
- package/dist/audit-extended.js +38 -0
- package/dist/audit-scheduler.d.ts +32 -0
- package/dist/audit-scheduler.js +52 -0
- package/dist/audit-sqlite-sink.d.ts +11 -0
- package/dist/audit-sqlite-sink.js +51 -0
- package/dist/autonomy-tracker.d.ts +76 -0
- package/dist/autonomy-tracker.js +277 -0
- package/dist/check-runs.d.ts +37 -0
- package/dist/check-runs.js +72 -0
- package/dist/cli/commands/agents.d.ts +6 -0
- package/dist/cli/commands/agents.js +33 -0
- package/dist/cli/commands/complexity.d.ts +6 -0
- package/dist/cli/commands/complexity.js +35 -0
- package/dist/cli/commands/cost.d.ts +6 -0
- package/dist/cli/commands/cost.js +53 -0
- package/dist/cli/commands/dashboard.d.ts +9 -0
- package/dist/cli/commands/dashboard.js +50 -0
- package/dist/cli/commands/health.d.ts +6 -0
- package/dist/cli/commands/health.js +33 -0
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.js +127 -0
- package/dist/cli/commands/routing.d.ts +6 -0
- package/dist/cli/commands/routing.js +46 -0
- package/dist/cli/commands/run.d.ts +6 -0
- package/dist/cli/commands/run.js +36 -0
- package/dist/cli/commands/start.d.ts +6 -0
- package/dist/cli/commands/start.js +26 -0
- package/dist/cli/commands/status.d.ts +6 -0
- package/dist/cli/commands/status.js +34 -0
- package/dist/cli/dashboard-renderer.d.ts +24 -0
- package/dist/cli/dashboard-renderer.js +129 -0
- package/dist/cli/formatters/index.d.ts +6 -0
- package/dist/cli/formatters/index.js +18 -0
- package/dist/cli/formatters/json.d.ts +5 -0
- package/dist/cli/formatters/json.js +7 -0
- package/dist/cli/formatters/minimal.d.ts +5 -0
- package/dist/cli/formatters/minimal.js +36 -0
- package/dist/cli/formatters/table.d.ts +5 -0
- package/dist/cli/formatters/table.js +184 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +35 -0
- package/dist/compliance-extended.d.ts +41 -0
- package/dist/compliance-extended.js +58 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.js +66 -0
- package/dist/context-enrichment.d.ts +33 -0
- package/dist/context-enrichment.js +125 -0
- package/dist/cost-governance.d.ts +22 -0
- package/dist/cost-governance.js +106 -0
- package/dist/cost-tracker.d.ts +75 -0
- package/dist/cost-tracker.js +177 -0
- package/dist/defaults.d.ts +140 -0
- package/dist/defaults.js +218 -0
- package/dist/deploy/flyio-target.d.ts +19 -0
- package/dist/deploy/flyio-target.js +114 -0
- package/dist/deploy/index.d.ts +15 -0
- package/dist/deploy/index.js +11 -0
- package/dist/deploy/kubernetes-target.d.ts +25 -0
- package/dist/deploy/kubernetes-target.js +124 -0
- package/dist/deploy/metrics-collector.d.ts +31 -0
- package/dist/deploy/metrics-collector.js +75 -0
- package/dist/deploy/rollout-controller.d.ts +42 -0
- package/dist/deploy/rollout-controller.js +192 -0
- package/dist/deploy/rollout-types.d.ts +86 -0
- package/dist/deploy/rollout-types.js +5 -0
- package/dist/deploy/types.d.ts +47 -0
- package/dist/deploy/types.js +5 -0
- package/dist/deploy/vercel-target.d.ts +16 -0
- package/dist/deploy/vercel-target.js +115 -0
- package/dist/discovery.d.ts +26 -0
- package/dist/discovery.js +46 -0
- package/dist/episodic-enhanced.d.ts +88 -0
- package/dist/episodic-enhanced.js +212 -0
- package/dist/execute.d.ts +81 -0
- package/dist/execute.js +703 -0
- package/dist/fix-ci.d.ts +61 -0
- package/dist/fix-ci.js +380 -0
- package/dist/handoff-executor.d.ts +61 -0
- package/dist/handoff-executor.js +119 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +87 -0
- package/dist/instrumented.d.ts +27 -0
- package/dist/instrumented.js +34 -0
- package/dist/logger.d.ts +17 -0
- package/dist/logger.js +37 -0
- package/dist/multi-repo/impact-analyzer.d.ts +22 -0
- package/dist/multi-repo/impact-analyzer.js +95 -0
- package/dist/multi-repo/index.d.ts +5 -0
- package/dist/multi-repo/index.js +4 -0
- package/dist/multi-repo/monorepo-detector.d.ts +16 -0
- package/dist/multi-repo/monorepo-detector.js +246 -0
- package/dist/multi-repo/service-map-builder.d.ts +24 -0
- package/dist/multi-repo/service-map-builder.js +235 -0
- package/dist/multi-repo/types.d.ts +54 -0
- package/dist/multi-repo/types.js +5 -0
- package/dist/notifications/index.d.ts +4 -0
- package/dist/notifications/index.js +4 -0
- package/dist/notifications/notification-router.d.ts +62 -0
- package/dist/notifications/notification-router.js +206 -0
- package/dist/notifications/slack-messenger.d.ts +26 -0
- package/dist/notifications/slack-messenger.js +69 -0
- package/dist/notifications/teams-messenger.d.ts +21 -0
- package/dist/notifications/teams-messenger.js +85 -0
- package/dist/notifications.d.ts +13 -0
- package/dist/notifications.js +15 -0
- package/dist/orchestration.d.ts +20 -0
- package/dist/orchestration.js +53 -0
- package/dist/orchestrator.d.ts +160 -0
- package/dist/orchestrator.js +372 -0
- package/dist/otel-exporter.d.ts +37 -0
- package/dist/otel-exporter.js +128 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/plugin.js +8 -0
- package/dist/policy-evaluators.d.ts +40 -0
- package/dist/policy-evaluators.js +57 -0
- package/dist/process-escalation.d.ts +38 -0
- package/dist/process-escalation.js +129 -0
- package/dist/progressive-gates.d.ts +54 -0
- package/dist/progressive-gates.js +135 -0
- package/dist/provenance.d.ts +29 -0
- package/dist/provenance.js +61 -0
- package/dist/reconcilers.d.ts +28 -0
- package/dist/reconcilers.js +37 -0
- package/dist/runners/claude-code.d.ts +17 -0
- package/dist/runners/claude-code.js +200 -0
- package/dist/runners/codex.d.ts +19 -0
- package/dist/runners/codex.js +153 -0
- package/dist/runners/copilot-stub.d.ts +9 -0
- package/dist/runners/copilot-stub.js +15 -0
- package/dist/runners/copilot.d.ts +15 -0
- package/dist/runners/copilot.js +119 -0
- package/dist/runners/cursor-stub.d.ts +9 -0
- package/dist/runners/cursor-stub.js +15 -0
- package/dist/runners/cursor.d.ts +20 -0
- package/dist/runners/cursor.js +140 -0
- package/dist/runners/devin-stub.d.ts +9 -0
- package/dist/runners/devin-stub.js +15 -0
- package/dist/runners/generic-llm.d.ts +51 -0
- package/dist/runners/generic-llm.js +125 -0
- package/dist/runners/index.d.ts +8 -0
- package/dist/runners/index.js +7 -0
- package/dist/runners/runner-registry.d.ts +50 -0
- package/dist/runners/runner-registry.js +156 -0
- package/dist/runners/types.d.ts +59 -0
- package/dist/runners/types.js +7 -0
- package/dist/security.d.ts +51 -0
- package/dist/security.js +70 -0
- package/dist/shared.d.ts +134 -0
- package/dist/shared.js +284 -0
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.js +3 -0
- package/dist/state/schema.d.ts +16 -0
- package/dist/state/schema.js +264 -0
- package/dist/state/store.d.ts +109 -0
- package/dist/state/store.js +644 -0
- package/dist/state/types.d.ts +190 -0
- package/dist/state/types.js +5 -0
- package/dist/structured-logger.d.ts +20 -0
- package/dist/structured-logger.js +64 -0
- package/dist/task-decomposer.d.ts +61 -0
- package/dist/task-decomposer.js +265 -0
- package/dist/telemetry-extended.d.ts +24 -0
- package/dist/telemetry-extended.js +31 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.js +7 -0
- package/dist/validate-agent-output.d.ts +30 -0
- package/dist/validate-agent-output.js +81 -0
- package/dist/validate-issue.d.ts +25 -0
- package/dist/validate-issue.js +102 -0
- package/dist/watch.d.ts +43 -0
- package/dist/watch.js +99 -0
- package/dist/webhook-manager.d.ts +38 -0
- package/dist/webhook-manager.js +66 -0
- package/package.json +59 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staged rollout controller — manages canary, blue-green, and rolling
|
|
3
|
+
* deployment strategies with auto-rollback on metric degradation.
|
|
4
|
+
*/
|
|
5
|
+
import type { RolloutControllerConfig, RolloutStatus } from './rollout-types.js';
|
|
6
|
+
export declare class RolloutController {
|
|
7
|
+
private config;
|
|
8
|
+
private status;
|
|
9
|
+
private _paused;
|
|
10
|
+
private _aborted;
|
|
11
|
+
constructor(config: RolloutControllerConfig);
|
|
12
|
+
/**
|
|
13
|
+
* Execute the full rollout for a given version and environment.
|
|
14
|
+
*/
|
|
15
|
+
execute(version: string, environment: string): Promise<RolloutStatus>;
|
|
16
|
+
/**
|
|
17
|
+
* Get the current rollout status.
|
|
18
|
+
*/
|
|
19
|
+
getStatus(): RolloutStatus;
|
|
20
|
+
/**
|
|
21
|
+
* Pause the rollout at the current step.
|
|
22
|
+
*/
|
|
23
|
+
pause(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Resume a paused rollout.
|
|
26
|
+
*/
|
|
27
|
+
resume(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Abort the rollout and trigger rollback.
|
|
30
|
+
* The actual rollback is performed by the executing strategy loop
|
|
31
|
+
* which detects the _aborted flag.
|
|
32
|
+
*/
|
|
33
|
+
abort(): void;
|
|
34
|
+
private executeCanary;
|
|
35
|
+
private executeBlueGreen;
|
|
36
|
+
private executeRolling;
|
|
37
|
+
private setPhase;
|
|
38
|
+
private complete;
|
|
39
|
+
private fail;
|
|
40
|
+
private triggerRollback;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=rollout-controller.d.ts.map
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staged rollout controller — manages canary, blue-green, and rolling
|
|
3
|
+
* deployment strategies with auto-rollback on metric degradation.
|
|
4
|
+
*/
|
|
5
|
+
export class RolloutController {
|
|
6
|
+
config;
|
|
7
|
+
status;
|
|
8
|
+
_paused = false;
|
|
9
|
+
_aborted = false;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.status = {
|
|
13
|
+
id: `rollout-${Date.now()}`,
|
|
14
|
+
deploymentId: '',
|
|
15
|
+
phase: 'pending',
|
|
16
|
+
currentStep: 0,
|
|
17
|
+
currentWeightPercent: 0,
|
|
18
|
+
startedAt: new Date().toISOString(),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Execute the full rollout for a given version and environment.
|
|
23
|
+
*/
|
|
24
|
+
async execute(version, environment) {
|
|
25
|
+
try {
|
|
26
|
+
// Deploy the new version
|
|
27
|
+
const deployment = await this.config.target.deploy(version, environment);
|
|
28
|
+
this.status.deploymentId = deployment.id;
|
|
29
|
+
if (deployment.state === 'failed') {
|
|
30
|
+
return this.fail(`Deployment failed: ${deployment.error ?? 'unknown'}`);
|
|
31
|
+
}
|
|
32
|
+
this.setPhase('progressing');
|
|
33
|
+
switch (this.config.strategy.type) {
|
|
34
|
+
case 'canary':
|
|
35
|
+
return await this.executeCanary(deployment);
|
|
36
|
+
case 'blue-green':
|
|
37
|
+
return await this.executeBlueGreen(deployment);
|
|
38
|
+
case 'rolling':
|
|
39
|
+
return await this.executeRolling(deployment);
|
|
40
|
+
default:
|
|
41
|
+
return this.fail(`Unknown strategy type`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
return this.fail(err instanceof Error ? err.message : String(err));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the current rollout status.
|
|
50
|
+
*/
|
|
51
|
+
getStatus() {
|
|
52
|
+
return { ...this.status };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Pause the rollout at the current step.
|
|
56
|
+
*/
|
|
57
|
+
pause() {
|
|
58
|
+
this._paused = true;
|
|
59
|
+
this.setPhase('paused');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resume a paused rollout.
|
|
63
|
+
*/
|
|
64
|
+
resume() {
|
|
65
|
+
this._paused = false;
|
|
66
|
+
this.setPhase('progressing');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Abort the rollout and trigger rollback.
|
|
70
|
+
* The actual rollback is performed by the executing strategy loop
|
|
71
|
+
* which detects the _aborted flag.
|
|
72
|
+
*/
|
|
73
|
+
abort() {
|
|
74
|
+
this._aborted = true;
|
|
75
|
+
}
|
|
76
|
+
// ── Canary strategy ─────────────────────────────────────────────
|
|
77
|
+
async executeCanary(deployment) {
|
|
78
|
+
const strategy = this.config.strategy;
|
|
79
|
+
for (let i = 0; i < strategy.steps.length; i++) {
|
|
80
|
+
if (this._aborted)
|
|
81
|
+
return this.triggerRollback('Manual abort requested');
|
|
82
|
+
// Wait while paused
|
|
83
|
+
while (this._paused && !this._aborted) {
|
|
84
|
+
await sleep(500);
|
|
85
|
+
}
|
|
86
|
+
if (this._aborted)
|
|
87
|
+
return this.triggerRollback('Manual abort requested');
|
|
88
|
+
const step = strategy.steps[i];
|
|
89
|
+
this.status.currentStep = i;
|
|
90
|
+
this.status.currentWeightPercent = step.weightPercent;
|
|
91
|
+
this.setPhase('progressing');
|
|
92
|
+
// Soak period — collect metrics at intervals
|
|
93
|
+
this.setPhase('soaking');
|
|
94
|
+
const soakEnd = Date.now() + step.soakDurationMs;
|
|
95
|
+
while (Date.now() < soakEnd && !this._aborted) {
|
|
96
|
+
if (this._paused) {
|
|
97
|
+
await sleep(500);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const metrics = await this.config.metricsSource.collect(deployment.id);
|
|
101
|
+
this.status.metrics = metrics;
|
|
102
|
+
// Check thresholds
|
|
103
|
+
if (metrics.errorRate > strategy.maxErrorRate) {
|
|
104
|
+
return this.triggerRollback(`Error rate ${(metrics.errorRate * 100).toFixed(1)}% exceeds threshold ${(strategy.maxErrorRate * 100).toFixed(1)}%`);
|
|
105
|
+
}
|
|
106
|
+
if (metrics.latencyP95Ms > strategy.maxLatencyP95Ms) {
|
|
107
|
+
return this.triggerRollback(`P95 latency ${metrics.latencyP95Ms}ms exceeds threshold ${strategy.maxLatencyP95Ms}ms`);
|
|
108
|
+
}
|
|
109
|
+
// Sample every 1/5 of soak duration, minimum 100ms
|
|
110
|
+
await sleep(Math.max(100, Math.floor(step.soakDurationMs / 5)));
|
|
111
|
+
}
|
|
112
|
+
if (this._aborted)
|
|
113
|
+
return this.triggerRollback('Manual abort requested');
|
|
114
|
+
}
|
|
115
|
+
// All steps passed — complete at 100%
|
|
116
|
+
this.status.currentWeightPercent = 100;
|
|
117
|
+
return this.complete();
|
|
118
|
+
}
|
|
119
|
+
// ── Blue-green strategy ─────────────────────────────────────────
|
|
120
|
+
async executeBlueGreen(deployment) {
|
|
121
|
+
const strategy = this.config.strategy;
|
|
122
|
+
// Health check the new deployment
|
|
123
|
+
this.setPhase('soaking');
|
|
124
|
+
const checkEnd = Date.now() + strategy.healthCheckDurationMs;
|
|
125
|
+
while (Date.now() < checkEnd && !this._aborted) {
|
|
126
|
+
if (this._paused) {
|
|
127
|
+
await sleep(500);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const metrics = await this.config.metricsSource.collect(deployment.id);
|
|
131
|
+
this.status.metrics = metrics;
|
|
132
|
+
if (metrics.healthyInstances === 0) {
|
|
133
|
+
return this.triggerRollback('No healthy instances detected');
|
|
134
|
+
}
|
|
135
|
+
await sleep(Math.max(100, Math.floor(strategy.healthCheckDurationMs / 5)));
|
|
136
|
+
}
|
|
137
|
+
if (this._aborted)
|
|
138
|
+
return this.triggerRollback('Manual abort requested');
|
|
139
|
+
// Switch traffic 0% -> 100%
|
|
140
|
+
this.status.currentWeightPercent = 100;
|
|
141
|
+
return this.complete();
|
|
142
|
+
}
|
|
143
|
+
// ── Rolling strategy ────────────────────────────────────────────
|
|
144
|
+
async executeRolling(deployment) {
|
|
145
|
+
// For rolling updates, the deployment target itself handles the rollout.
|
|
146
|
+
// We just monitor the health.
|
|
147
|
+
this.setPhase('soaking');
|
|
148
|
+
const metrics = await this.config.metricsSource.collect(deployment.id);
|
|
149
|
+
this.status.metrics = metrics;
|
|
150
|
+
if (metrics.healthyInstances === 0) {
|
|
151
|
+
return this.triggerRollback('No healthy instances after rolling update');
|
|
152
|
+
}
|
|
153
|
+
this.status.currentWeightPercent = 100;
|
|
154
|
+
return this.complete();
|
|
155
|
+
}
|
|
156
|
+
// ── Helpers ─────────────────────────────────────────────────────
|
|
157
|
+
setPhase(phase) {
|
|
158
|
+
this.status.phase = phase;
|
|
159
|
+
this.config.onPhaseChange?.(this.getStatus());
|
|
160
|
+
}
|
|
161
|
+
complete() {
|
|
162
|
+
this.status.phase = 'completed';
|
|
163
|
+
this.status.completedAt = new Date().toISOString();
|
|
164
|
+
this.config.onPhaseChange?.(this.getStatus());
|
|
165
|
+
return this.getStatus();
|
|
166
|
+
}
|
|
167
|
+
fail(error) {
|
|
168
|
+
this.status.phase = 'failed';
|
|
169
|
+
this.status.error = error;
|
|
170
|
+
this.status.completedAt = new Date().toISOString();
|
|
171
|
+
this.config.onPhaseChange?.(this.getStatus());
|
|
172
|
+
return this.getStatus();
|
|
173
|
+
}
|
|
174
|
+
async triggerRollback(reason) {
|
|
175
|
+
this.config.onAutoRollback?.(this.getStatus(), reason);
|
|
176
|
+
try {
|
|
177
|
+
await this.config.target.rollback(this.status.deploymentId);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Rollback itself failed — still mark as rolled-back with error
|
|
181
|
+
}
|
|
182
|
+
this.status.phase = 'rolled-back';
|
|
183
|
+
this.status.error = reason;
|
|
184
|
+
this.status.completedAt = new Date().toISOString();
|
|
185
|
+
this.config.onPhaseChange?.(this.getStatus());
|
|
186
|
+
return this.getStatus();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function sleep(ms) {
|
|
190
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=rollout-controller.js.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for staged rollout strategies (canary, blue-green, rolling).
|
|
3
|
+
*/
|
|
4
|
+
import type { DeploymentTarget } from './types.js';
|
|
5
|
+
export interface CanaryStep {
|
|
6
|
+
/** Traffic weight percentage for this step (0-100). */
|
|
7
|
+
weightPercent: number;
|
|
8
|
+
/** Soak duration in milliseconds before advancing. */
|
|
9
|
+
soakDurationMs: number;
|
|
10
|
+
}
|
|
11
|
+
export interface CanaryConfig {
|
|
12
|
+
type: 'canary';
|
|
13
|
+
/** Steps defining traffic percentage progression. */
|
|
14
|
+
steps: CanaryStep[];
|
|
15
|
+
/** Maximum error rate (0-1) before auto-rollback. */
|
|
16
|
+
maxErrorRate: number;
|
|
17
|
+
/** Maximum P95 latency in milliseconds before auto-rollback. */
|
|
18
|
+
maxLatencyP95Ms: number;
|
|
19
|
+
}
|
|
20
|
+
export interface BlueGreenConfig {
|
|
21
|
+
type: 'blue-green';
|
|
22
|
+
/** Health check duration in ms before switching traffic. */
|
|
23
|
+
healthCheckDurationMs: number;
|
|
24
|
+
/** Whether to keep the old version running after switch. */
|
|
25
|
+
keepOldVersion?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface RollingConfig {
|
|
28
|
+
type: 'rolling';
|
|
29
|
+
/** Maximum number of instances updating concurrently. */
|
|
30
|
+
maxSurge: number;
|
|
31
|
+
/** Maximum number of instances unavailable during update. */
|
|
32
|
+
maxUnavailable: number;
|
|
33
|
+
}
|
|
34
|
+
export type RolloutStrategy = CanaryConfig | BlueGreenConfig | RollingConfig;
|
|
35
|
+
export type RolloutPhase = 'pending' | 'progressing' | 'soaking' | 'paused' | 'completed' | 'rolled-back' | 'failed';
|
|
36
|
+
export interface RolloutStatus {
|
|
37
|
+
/** Unique rollout identifier. */
|
|
38
|
+
id: string;
|
|
39
|
+
/** The deployment this rollout manages. */
|
|
40
|
+
deploymentId: string;
|
|
41
|
+
/** Current phase. */
|
|
42
|
+
phase: RolloutPhase;
|
|
43
|
+
/** Current step index (for canary). */
|
|
44
|
+
currentStep: number;
|
|
45
|
+
/** Current traffic weight percent. */
|
|
46
|
+
currentWeightPercent: number;
|
|
47
|
+
/** Latest collected metrics. */
|
|
48
|
+
metrics?: RolloutMetrics;
|
|
49
|
+
/** Error message if failed. */
|
|
50
|
+
error?: string;
|
|
51
|
+
/** Start time. */
|
|
52
|
+
startedAt: string;
|
|
53
|
+
/** Completion time. */
|
|
54
|
+
completedAt?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface RolloutMetrics {
|
|
57
|
+
/** Error rate (0-1). */
|
|
58
|
+
errorRate: number;
|
|
59
|
+
/** P95 latency in milliseconds. */
|
|
60
|
+
latencyP95Ms: number;
|
|
61
|
+
/** Requests per second. */
|
|
62
|
+
requestsPerSecond: number;
|
|
63
|
+
/** Number of healthy instances. */
|
|
64
|
+
healthyInstances: number;
|
|
65
|
+
/** Total instances. */
|
|
66
|
+
totalInstances: number;
|
|
67
|
+
/** Timestamp of collection. */
|
|
68
|
+
collectedAt: string;
|
|
69
|
+
}
|
|
70
|
+
export interface MetricsSource {
|
|
71
|
+
/** Collect current metrics for the deployment. */
|
|
72
|
+
collect(deploymentId: string): Promise<RolloutMetrics>;
|
|
73
|
+
}
|
|
74
|
+
export interface RolloutControllerConfig {
|
|
75
|
+
/** The deployment target to manage. */
|
|
76
|
+
target: DeploymentTarget;
|
|
77
|
+
/** Metrics collection source. */
|
|
78
|
+
metricsSource: MetricsSource;
|
|
79
|
+
/** Rollout strategy configuration. */
|
|
80
|
+
strategy: RolloutStrategy;
|
|
81
|
+
/** Callback on phase changes. */
|
|
82
|
+
onPhaseChange?: (status: RolloutStatus) => void;
|
|
83
|
+
/** Callback when auto-rollback is triggered. */
|
|
84
|
+
onAutoRollback?: (status: RolloutStatus, reason: string) => void;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=rollout-types.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment target types shared across all deployment providers.
|
|
3
|
+
*/
|
|
4
|
+
export interface DeploymentTargetConfig {
|
|
5
|
+
/** Target name identifier. */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Target provider type. */
|
|
8
|
+
provider: 'kubernetes' | 'vercel' | 'flyio';
|
|
9
|
+
/** Provider-specific configuration. */
|
|
10
|
+
config: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface HealthCheckConfig {
|
|
13
|
+
/** URL to check for health. */
|
|
14
|
+
url: string;
|
|
15
|
+
/** Expected HTTP status code (defaults to 200). */
|
|
16
|
+
expectedStatus?: number;
|
|
17
|
+
/** Timeout in milliseconds (defaults to 5000). */
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
/** Number of consecutive healthy checks required (defaults to 3). */
|
|
20
|
+
healthyThreshold?: number;
|
|
21
|
+
}
|
|
22
|
+
export type DeploymentState = 'pending' | 'deploying' | 'healthy' | 'unhealthy' | 'rolled-back' | 'failed';
|
|
23
|
+
export interface DeploymentResult {
|
|
24
|
+
id: string;
|
|
25
|
+
state: DeploymentState;
|
|
26
|
+
url?: string;
|
|
27
|
+
version: string;
|
|
28
|
+
startedAt: string;
|
|
29
|
+
completedAt?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface DeploymentTarget {
|
|
33
|
+
/** Deploy a new version. */
|
|
34
|
+
deploy(version: string, environment: string): Promise<DeploymentResult>;
|
|
35
|
+
/** Get the current deployment status. */
|
|
36
|
+
getStatus(id: string): Promise<DeploymentResult>;
|
|
37
|
+
/** Rollback to a previous version. */
|
|
38
|
+
rollback(id: string): Promise<DeploymentResult>;
|
|
39
|
+
}
|
|
40
|
+
/** Shell command executor for CLI mode. */
|
|
41
|
+
export type ExecFn = (file: string, args: string[]) => Promise<{
|
|
42
|
+
stdout: string;
|
|
43
|
+
stderr: string;
|
|
44
|
+
}>;
|
|
45
|
+
/** HTTP client for API mode. */
|
|
46
|
+
export type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;
|
|
47
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel deployment target — deploys via Vercel REST API v13.
|
|
3
|
+
*/
|
|
4
|
+
import type { DeploymentTarget, FetchFn } from './types.js';
|
|
5
|
+
export interface VercelConfig {
|
|
6
|
+
/** Vercel API token. */
|
|
7
|
+
token: string;
|
|
8
|
+
/** Vercel project ID. */
|
|
9
|
+
projectId: string;
|
|
10
|
+
/** Vercel team ID (optional). */
|
|
11
|
+
teamId?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function createVercelTarget(config: VercelConfig, opts?: {
|
|
14
|
+
fetch?: FetchFn;
|
|
15
|
+
}): DeploymentTarget;
|
|
16
|
+
//# sourceMappingURL=vercel-target.d.ts.map
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vercel deployment target — deploys via Vercel REST API v13.
|
|
3
|
+
*/
|
|
4
|
+
const VERCEL_API = 'https://api.vercel.com';
|
|
5
|
+
export function createVercelTarget(config, opts) {
|
|
6
|
+
const httpFetch = opts?.fetch ?? globalThis.fetch;
|
|
7
|
+
const deployments = new Map();
|
|
8
|
+
function headers() {
|
|
9
|
+
return {
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
Authorization: `Bearer ${config.token}`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function teamParam() {
|
|
15
|
+
return config.teamId ? `?teamId=${config.teamId}` : '';
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
async deploy(version, environment) {
|
|
19
|
+
const id = `vercel-${Date.now()}-${environment}`;
|
|
20
|
+
const result = {
|
|
21
|
+
id,
|
|
22
|
+
state: 'deploying',
|
|
23
|
+
version,
|
|
24
|
+
startedAt: new Date().toISOString(),
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
const res = await httpFetch(`${VERCEL_API}/v13/deployments${teamParam()}`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: headers(),
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
name: config.projectId,
|
|
32
|
+
target: environment === 'production' ? 'production' : 'preview',
|
|
33
|
+
gitSource: { ref: version, type: 'branch' },
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
throw new Error(`Vercel deploy failed: ${res.status}`);
|
|
38
|
+
const data = (await res.json());
|
|
39
|
+
result.id = data.id ?? id;
|
|
40
|
+
result.url = data.url ? `https://${data.url}` : undefined;
|
|
41
|
+
result.state = mapVercelState(data.readyState);
|
|
42
|
+
result.completedAt = new Date().toISOString();
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
result.state = 'failed';
|
|
46
|
+
result.error = err instanceof Error ? err.message : String(err);
|
|
47
|
+
result.completedAt = new Date().toISOString();
|
|
48
|
+
}
|
|
49
|
+
deployments.set(result.id, result);
|
|
50
|
+
return result;
|
|
51
|
+
},
|
|
52
|
+
async getStatus(id) {
|
|
53
|
+
try {
|
|
54
|
+
const res = await httpFetch(`${VERCEL_API}/v13/deployments/${id}${teamParam()}`, {
|
|
55
|
+
headers: headers(),
|
|
56
|
+
});
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
const cached = deployments.get(id);
|
|
59
|
+
return cached ?? { id, state: 'pending', version: 'unknown', startedAt: '' };
|
|
60
|
+
}
|
|
61
|
+
const data = (await res.json());
|
|
62
|
+
return {
|
|
63
|
+
id,
|
|
64
|
+
state: mapVercelState(data.readyState),
|
|
65
|
+
url: data.url ? `https://${data.url}` : undefined,
|
|
66
|
+
version: data.meta?.version ?? 'unknown',
|
|
67
|
+
startedAt: deployments.get(id)?.startedAt ?? '',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
const cached = deployments.get(id);
|
|
72
|
+
return cached ?? { id, state: 'pending', version: 'unknown', startedAt: '' };
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
async rollback(id) {
|
|
76
|
+
// Vercel rollback is done by promoting a previous deployment to production
|
|
77
|
+
const rollbackId = `${id}-rollback`;
|
|
78
|
+
const result = {
|
|
79
|
+
id: rollbackId,
|
|
80
|
+
state: 'deploying',
|
|
81
|
+
version: deployments.get(id)?.version ?? 'unknown',
|
|
82
|
+
startedAt: new Date().toISOString(),
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
// Promote-as-rollback: re-create the deployment
|
|
86
|
+
const res = await httpFetch(`${VERCEL_API}/v13/deployments/${id}/promote${teamParam()}`, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
headers: headers(),
|
|
89
|
+
});
|
|
90
|
+
if (!res.ok)
|
|
91
|
+
throw new Error(`Vercel rollback failed: ${res.status}`);
|
|
92
|
+
result.state = 'rolled-back';
|
|
93
|
+
result.completedAt = new Date().toISOString();
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
result.state = 'failed';
|
|
97
|
+
result.error = err instanceof Error ? err.message : String(err);
|
|
98
|
+
result.completedAt = new Date().toISOString();
|
|
99
|
+
}
|
|
100
|
+
deployments.set(rollbackId, result);
|
|
101
|
+
return result;
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function mapVercelState(state) {
|
|
106
|
+
const map = {
|
|
107
|
+
QUEUED: 'pending',
|
|
108
|
+
BUILDING: 'deploying',
|
|
109
|
+
READY: 'healthy',
|
|
110
|
+
ERROR: 'failed',
|
|
111
|
+
CANCELED: 'failed',
|
|
112
|
+
};
|
|
113
|
+
return map[state] ?? 'pending';
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=vercel-target.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent discovery module — integrates A2A agent discovery
|
|
3
|
+
* and skill-based routing into the dogfood pipeline.
|
|
4
|
+
*/
|
|
5
|
+
import { matchAgentBySkill, createStubAgentCardFetcher, type AgentDiscovery, type AgentRole, type AgentFilter, type AgentCardFetcher, type A2AAgentCard } from '@ai-sdlc/reference';
|
|
6
|
+
/**
|
|
7
|
+
* Create a pipeline-scoped agent discovery service.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createPipelineDiscovery(): AgentDiscovery;
|
|
10
|
+
/**
|
|
11
|
+
* Find the best matching agent for a given skill query.
|
|
12
|
+
* Returns the first agent whose skills match.
|
|
13
|
+
*/
|
|
14
|
+
export declare function findMatchingAgent(discovery: AgentDiscovery, skillQuery: string): AgentRole | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Route an issue to the most appropriate agent based on issue labels.
|
|
17
|
+
* Maps common labels to skill queries.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveAgentForIssue(discovery: AgentDiscovery, labels: string[]): AgentRole | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Create a stub agent card fetcher for testing A2A discovery.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createPipelineAgentCardFetcher(cards?: Map<string, A2AAgentCard>): AgentCardFetcher;
|
|
24
|
+
export { matchAgentBySkill, createStubAgentCardFetcher };
|
|
25
|
+
export type { AgentDiscovery, AgentFilter, AgentCardFetcher, A2AAgentCard };
|
|
26
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent discovery module — integrates A2A agent discovery
|
|
3
|
+
* and skill-based routing into the dogfood pipeline.
|
|
4
|
+
*/
|
|
5
|
+
import { createAgentDiscovery, matchAgentBySkill, createStubAgentCardFetcher, } from '@ai-sdlc/reference';
|
|
6
|
+
import { DEFAULT_LABEL_TO_SKILL_MAP } from './defaults.js';
|
|
7
|
+
/**
|
|
8
|
+
* Create a pipeline-scoped agent discovery service.
|
|
9
|
+
*/
|
|
10
|
+
export function createPipelineDiscovery() {
|
|
11
|
+
return createAgentDiscovery();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Find the best matching agent for a given skill query.
|
|
15
|
+
* Returns the first agent whose skills match.
|
|
16
|
+
*/
|
|
17
|
+
export function findMatchingAgent(discovery, skillQuery) {
|
|
18
|
+
const agents = discovery.list({ skill: skillQuery });
|
|
19
|
+
return agents[0];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Route an issue to the most appropriate agent based on issue labels.
|
|
23
|
+
* Maps common labels to skill queries.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveAgentForIssue(discovery, labels) {
|
|
26
|
+
const labelToSkill = DEFAULT_LABEL_TO_SKILL_MAP;
|
|
27
|
+
for (const label of labels) {
|
|
28
|
+
const skill = labelToSkill[label.toLowerCase()];
|
|
29
|
+
if (skill) {
|
|
30
|
+
const agent = findMatchingAgent(discovery, skill);
|
|
31
|
+
if (agent)
|
|
32
|
+
return agent;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Fallback to first available agent
|
|
36
|
+
const all = discovery.list();
|
|
37
|
+
return all[0];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a stub agent card fetcher for testing A2A discovery.
|
|
41
|
+
*/
|
|
42
|
+
export function createPipelineAgentCardFetcher(cards) {
|
|
43
|
+
return createStubAgentCardFetcher(cards ?? new Map());
|
|
44
|
+
}
|
|
45
|
+
export { matchAgentBySkill, createStubAgentCardFetcher };
|
|
46
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced episodic memory — records enriched pipeline history to SQLite,
|
|
3
|
+
* detects regressions, and extracts failure patterns.
|
|
4
|
+
*
|
|
5
|
+
* RFC reference: Phase 2 episodic memory enrichment.
|
|
6
|
+
*/
|
|
7
|
+
import type { StateStore } from './state/store.js';
|
|
8
|
+
import type { EpisodicRecord } from './state/types.js';
|
|
9
|
+
export interface EnhancedEpisodicInput {
|
|
10
|
+
issueNumber?: number;
|
|
11
|
+
prNumber?: number;
|
|
12
|
+
pipelineType: string;
|
|
13
|
+
outcome: 'success' | 'failure' | 'partial';
|
|
14
|
+
durationMs?: number;
|
|
15
|
+
filesChanged?: number;
|
|
16
|
+
errorMessage?: string;
|
|
17
|
+
metadata?: string;
|
|
18
|
+
agentName?: string;
|
|
19
|
+
complexityScore?: number;
|
|
20
|
+
routingStrategy?: string;
|
|
21
|
+
gatePassCount?: number;
|
|
22
|
+
gateFailCount?: number;
|
|
23
|
+
costUsd?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface RegressionInfo {
|
|
26
|
+
isRegression: boolean;
|
|
27
|
+
relatedEpisodeIds: number[];
|
|
28
|
+
reason?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface FailurePattern {
|
|
31
|
+
pattern: string;
|
|
32
|
+
count: number;
|
|
33
|
+
lastSeen: string;
|
|
34
|
+
affectedFiles: string[];
|
|
35
|
+
agentName?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface EpisodicSummary {
|
|
38
|
+
totalEpisodes: number;
|
|
39
|
+
successRate: number;
|
|
40
|
+
avgDurationMs: number;
|
|
41
|
+
avgCostUsd: number;
|
|
42
|
+
regressionCount: number;
|
|
43
|
+
topFailurePatterns: FailurePattern[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create an enhanced episodic memory manager backed by the state store.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createEnhancedEpisodicMemory(store: StateStore): {
|
|
49
|
+
/**
|
|
50
|
+
* Record an enhanced episodic memory entry, auto-detecting regressions.
|
|
51
|
+
*/
|
|
52
|
+
record(input: EnhancedEpisodicInput): {
|
|
53
|
+
id: number;
|
|
54
|
+
regression: RegressionInfo;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Get recent episodes with optional filtering.
|
|
58
|
+
*/
|
|
59
|
+
getRecent(limit?: number): EpisodicRecord[];
|
|
60
|
+
/**
|
|
61
|
+
* Search episodes by criteria.
|
|
62
|
+
*/
|
|
63
|
+
search(opts: {
|
|
64
|
+
agentName?: string;
|
|
65
|
+
outcome?: string;
|
|
66
|
+
since?: string;
|
|
67
|
+
files?: string;
|
|
68
|
+
limit?: number;
|
|
69
|
+
}): EpisodicRecord[];
|
|
70
|
+
/**
|
|
71
|
+
* Extract failure patterns from recent history.
|
|
72
|
+
*/
|
|
73
|
+
extractPatterns(limit?: number): FailurePattern[];
|
|
74
|
+
/**
|
|
75
|
+
* Get a summary of episodic history.
|
|
76
|
+
*/
|
|
77
|
+
summarize(since?: string): EpisodicSummary;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Detect if a new record represents a regression:
|
|
81
|
+
* Same files failed before with the same error pattern within 7 days.
|
|
82
|
+
*/
|
|
83
|
+
export declare function detectRegressions(store: StateStore, input: EnhancedEpisodicInput): RegressionInfo;
|
|
84
|
+
/**
|
|
85
|
+
* Extract failure patterns from recent episodes.
|
|
86
|
+
*/
|
|
87
|
+
export declare function extractEpisodicPatterns(store: StateStore, limit?: number): FailurePattern[];
|
|
88
|
+
//# sourceMappingURL=episodic-enhanced.d.ts.map
|