@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
package/dist/defaults.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized default values for the AI-SDLC orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* All magic numbers, fallback strings, and environment-driven defaults live
|
|
5
|
+
* here so they can be tuned from a single location. Every value can be
|
|
6
|
+
* overridden by an environment variable, a Pipeline/AgentRole YAML field,
|
|
7
|
+
* or an explicit function parameter — these are only the last-resort
|
|
8
|
+
* fallbacks.
|
|
9
|
+
*/
|
|
10
|
+
import { parseDuration } from '@ai-sdlc/reference';
|
|
11
|
+
// ── LLM Model ────────────────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* Default model name (literal fallback).
|
|
14
|
+
* Consumers should read `AI_SDLC_MODEL` env var at call time and fall back
|
|
15
|
+
* to this constant, so tests that set env vars at runtime see the override.
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_MODEL = 'claude-sonnet-4-5-20250929';
|
|
18
|
+
// ── GitHub ───────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Fallback values for GitHub org/repo — empty strings require explicit config.
|
|
21
|
+
* Consumers should read `GITHUB_REPOSITORY_OWNER` / `GITHUB_REPOSITORY`
|
|
22
|
+
* at call time and fall back to these constants.
|
|
23
|
+
*/
|
|
24
|
+
export const DEFAULT_GITHUB_ORG = '';
|
|
25
|
+
export const DEFAULT_GITHUB_REPO = '';
|
|
26
|
+
export const DEFAULT_GITHUB_REPOSITORY = '';
|
|
27
|
+
// ── Config directory ─────────────────────────────────────────────────
|
|
28
|
+
export const DEFAULT_CONFIG_DIR_NAME = process.env.AI_SDLC_CONFIG_DIR ?? '.ai-sdlc';
|
|
29
|
+
// ── Sandbox constraints ──────────────────────────────────────────────
|
|
30
|
+
export const DEFAULT_SANDBOX_MEMORY_MB = 512;
|
|
31
|
+
export const DEFAULT_SANDBOX_CPU_PERCENT = 80;
|
|
32
|
+
export const DEFAULT_SANDBOX_NETWORK_POLICY = 'egress-only';
|
|
33
|
+
export const DEFAULT_SANDBOX_TIMEOUT_MS = process.env.AI_SDLC_SANDBOX_TIMEOUT
|
|
34
|
+
? parseDuration(process.env.AI_SDLC_SANDBOX_TIMEOUT) || 1_800_000
|
|
35
|
+
: 1_800_000; // 30 minutes
|
|
36
|
+
/** Build a SandboxConstraints object from defaults, optionally overriding timeout and workDir. */
|
|
37
|
+
export function defaultSandboxConstraints(workDir, timeoutMs) {
|
|
38
|
+
return {
|
|
39
|
+
maxMemoryMb: DEFAULT_SANDBOX_MEMORY_MB,
|
|
40
|
+
maxCpuPercent: DEFAULT_SANDBOX_CPU_PERCENT,
|
|
41
|
+
networkPolicy: DEFAULT_SANDBOX_NETWORK_POLICY,
|
|
42
|
+
timeoutMs: timeoutMs ?? DEFAULT_SANDBOX_TIMEOUT_MS,
|
|
43
|
+
allowedPaths: [workDir],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// ── Runner ───────────────────────────────────────────────────────────
|
|
47
|
+
export const DEFAULT_RUNNER_TIMEOUT_MS = process.env.AI_SDLC_RUNNER_TIMEOUT
|
|
48
|
+
? parseDuration(process.env.AI_SDLC_RUNNER_TIMEOUT) || 900_000
|
|
49
|
+
: 900_000; // 15 minutes
|
|
50
|
+
export const DEFAULT_ALLOWED_TOOLS = 'Edit,Write,Read,Glob,Grep,Bash';
|
|
51
|
+
// ── Agent constraints ────────────────────────────────────────────────
|
|
52
|
+
export const DEFAULT_MAX_FILES_PER_CHANGE = 15;
|
|
53
|
+
export const DEFAULT_REQUIRE_TESTS = true;
|
|
54
|
+
export const DEFAULT_BLOCKED_PATHS = ['.github/workflows/**', `${DEFAULT_CONFIG_DIR_NAME}/**`];
|
|
55
|
+
// ── Agent prompt commands ────────────────────────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Lint command injected into agent prompts.
|
|
58
|
+
* Undefined by default — users opt in via AI_SDLC_LINT_COMMAND env var
|
|
59
|
+
* or by passing `lintCommand` in AgentContext.
|
|
60
|
+
*/
|
|
61
|
+
export const DEFAULT_LINT_COMMAND = process.env.AI_SDLC_LINT_COMMAND ?? undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Format command injected into agent prompts.
|
|
64
|
+
* Undefined by default — users opt in via AI_SDLC_FORMAT_COMMAND env var
|
|
65
|
+
* or by passing `formatCommand` in AgentContext.
|
|
66
|
+
*/
|
|
67
|
+
export const DEFAULT_FORMAT_COMMAND = process.env.AI_SDLC_FORMAT_COMMAND ?? undefined;
|
|
68
|
+
// ── Commit message ──────────────────────────────────────────────────
|
|
69
|
+
export const DEFAULT_COMMIT_MESSAGE_TEMPLATE = process.env.AI_SDLC_COMMIT_MESSAGE_TEMPLATE ??
|
|
70
|
+
'fix: resolve issue #{issueNumber}\n\n{issueTitle}';
|
|
71
|
+
export const DEFAULT_COMMIT_CO_AUTHOR = process.env.AI_SDLC_COMMIT_CO_AUTHOR ?? 'Claude <noreply@anthropic.com>';
|
|
72
|
+
// ── Runner registry API defaults ────────────────────────────────────
|
|
73
|
+
export const DEFAULT_OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions';
|
|
74
|
+
export const DEFAULT_OPENAI_MODEL = 'gpt-4';
|
|
75
|
+
export const DEFAULT_ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
|
|
76
|
+
export const DEFAULT_ANTHROPIC_MODEL = 'claude-sonnet-4-5-20250929';
|
|
77
|
+
export const DEFAULT_GENERIC_LLM_MODEL = 'default';
|
|
78
|
+
// ── CLI runner model overrides ────────────────────────────────────────
|
|
79
|
+
export const DEFAULT_COPILOT_MODEL = process.env.AI_SDLC_COPILOT_MODEL;
|
|
80
|
+
export const DEFAULT_CURSOR_MODEL = process.env.AI_SDLC_CURSOR_MODEL;
|
|
81
|
+
export const DEFAULT_CODEX_MODEL = process.env.AI_SDLC_CODEX_MODEL;
|
|
82
|
+
// ── Generic LLM defaults ───────────────────────────────────────────
|
|
83
|
+
export const DEFAULT_LLM_TIMEOUT_MS = 120_000;
|
|
84
|
+
export const DEFAULT_LLM_MAX_TOKENS = 4096;
|
|
85
|
+
export const DEFAULT_LLM_SYSTEM_PROMPT = 'You are a software engineering agent. Implement code changes as instructed.';
|
|
86
|
+
// ── Docker / CI adapter defaults ────────────────────────────────────
|
|
87
|
+
export const DEFAULT_DOCKER_IMAGE = 'node:20-slim';
|
|
88
|
+
export const DEFAULT_WORKFLOW_FILE = 'ci.yml';
|
|
89
|
+
// ── Discovery ───────────────────────────────────────────────────────
|
|
90
|
+
export const DEFAULT_LABEL_TO_SKILL_MAP = {
|
|
91
|
+
bug: 'debugging',
|
|
92
|
+
feature: 'implementation',
|
|
93
|
+
docs: 'documentation',
|
|
94
|
+
test: 'testing',
|
|
95
|
+
refactor: 'refactoring',
|
|
96
|
+
security: 'security-analysis',
|
|
97
|
+
performance: 'optimization',
|
|
98
|
+
};
|
|
99
|
+
// ── Analysis cache ──────────────────────────────────────────────────
|
|
100
|
+
export const DEFAULT_ANALYSIS_CACHE_TTL_MS = process.env.AI_SDLC_ANALYSIS_CACHE_TTL
|
|
101
|
+
? Number(process.env.AI_SDLC_ANALYSIS_CACHE_TTL)
|
|
102
|
+
: 86_400_000; // 24 hours
|
|
103
|
+
// ── Fix-CI ───────────────────────────────────────────────────────────
|
|
104
|
+
export const DEFAULT_MAX_FIX_ATTEMPTS = 2;
|
|
105
|
+
export const DEFAULT_MAX_LOG_LINES = 150;
|
|
106
|
+
export const DEFAULT_GH_CLI_TIMEOUT_MS = process.env.AI_SDLC_GH_CLI_TIMEOUT
|
|
107
|
+
? parseDuration(process.env.AI_SDLC_GH_CLI_TIMEOUT) || 30_000
|
|
108
|
+
: 30_000;
|
|
109
|
+
// ── JIT credentials ──────────────────────────────────────────────────
|
|
110
|
+
export const DEFAULT_JIT_TTL_MS = process.env.AI_SDLC_JIT_TTL
|
|
111
|
+
? parseDuration(process.env.AI_SDLC_JIT_TTL) || 600_000
|
|
112
|
+
: 600_000; // 10 minutes
|
|
113
|
+
export const DEFAULT_JIT_SCOPE = ['repo:read', 'repo:write'];
|
|
114
|
+
// ── Branch naming ────────────────────────────────────────────────────
|
|
115
|
+
export const DEFAULT_BRANCH_TEMPLATE = 'ai-sdlc/issue-{issueNumber}';
|
|
116
|
+
export const DEFAULT_BRANCH_PATTERN = /^ai-sdlc\/issue-(\d+)$/;
|
|
117
|
+
// ── PR templates ─────────────────────────────────────────────────────
|
|
118
|
+
export const DEFAULT_PR_TITLE_TEMPLATE = 'fix: {issueTitle} (#{issueNumber})';
|
|
119
|
+
// ── PR footer ────────────────────────────────────────────────────────
|
|
120
|
+
export const DEFAULT_PR_FOOTER = '*This PR was generated by [AI-SDLC](https://github.com/ai-sdlc-framework/ai-sdlc).*';
|
|
121
|
+
// ── Complexity routing ───────────────────────────────────────────────
|
|
122
|
+
export const DEFAULT_COMPLEXITY_THRESHOLDS = {
|
|
123
|
+
'fully-autonomous': { min: 1, max: 3, strategy: 'fully-autonomous' },
|
|
124
|
+
'ai-with-review': { min: 4, max: 5, strategy: 'ai-with-review' },
|
|
125
|
+
'ai-assisted': { min: 6, max: 8, strategy: 'ai-assisted' },
|
|
126
|
+
'human-led': { min: 9, max: 10, strategy: 'human-led' },
|
|
127
|
+
};
|
|
128
|
+
// ── Autonomy guardrails ──────────────────────────────────────────────
|
|
129
|
+
export const DEFAULT_MAX_LINES_PER_PR = {
|
|
130
|
+
level0: 100,
|
|
131
|
+
level1: 300,
|
|
132
|
+
level2: 500,
|
|
133
|
+
};
|
|
134
|
+
// ── Notification titles ──────────────────────────────────────────────
|
|
135
|
+
// ── Codebase analysis ───────────────────────────────────────────
|
|
136
|
+
export const DEFAULT_ANALYSIS_INCLUDE = ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'];
|
|
137
|
+
export const DEFAULT_ANALYSIS_EXCLUDE = [
|
|
138
|
+
'node_modules/**',
|
|
139
|
+
'.git/**',
|
|
140
|
+
'dist/**',
|
|
141
|
+
'build/**',
|
|
142
|
+
'coverage/**',
|
|
143
|
+
'**/*.d.ts',
|
|
144
|
+
'**/*.min.js',
|
|
145
|
+
'**/*.map',
|
|
146
|
+
];
|
|
147
|
+
export const DEFAULT_GIT_HISTORY_DAYS = 90;
|
|
148
|
+
export const DEFAULT_HOTSPOT_THRESHOLD = 0.3;
|
|
149
|
+
// ── Notification titles ──────────────────────────────────────────
|
|
150
|
+
// ── Model cost mapping ──────────────────────────────────────────
|
|
151
|
+
/** Cost per million tokens for known models (input/output/cache-read). */
|
|
152
|
+
export const DEFAULT_MODEL_COSTS = {
|
|
153
|
+
'claude-opus-4-6': { inputPer1M: 15.0, outputPer1M: 75.0, cacheReadPer1M: 1.5 },
|
|
154
|
+
'claude-sonnet-4-5-20250929': { inputPer1M: 3.0, outputPer1M: 15.0, cacheReadPer1M: 0.3 },
|
|
155
|
+
'claude-haiku-4-5-20251001': { inputPer1M: 0.8, outputPer1M: 4.0, cacheReadPer1M: 0.08 },
|
|
156
|
+
'claude-sonnet-4-20250514': { inputPer1M: 3.0, outputPer1M: 15.0, cacheReadPer1M: 0.3 },
|
|
157
|
+
'claude-3-5-haiku-20241022': { inputPer1M: 1.0, outputPer1M: 5.0, cacheReadPer1M: 0.1 },
|
|
158
|
+
};
|
|
159
|
+
/** Default monthly cost budget in USD. */
|
|
160
|
+
export const DEFAULT_COST_BUDGET_USD = 500;
|
|
161
|
+
/** Dashboard refresh interval in milliseconds. */
|
|
162
|
+
export const DEFAULT_DASHBOARD_REFRESH_MS = 2_000;
|
|
163
|
+
/** Static gate profiles per complexity band (RFC 217-228). */
|
|
164
|
+
export const PROGRESSIVE_GATE_PROFILES = [
|
|
165
|
+
{
|
|
166
|
+
band: 'trivial',
|
|
167
|
+
minScore: 1,
|
|
168
|
+
maxScore: 3,
|
|
169
|
+
defaultEnforcement: 'advisory',
|
|
170
|
+
testCoverageThreshold: 60,
|
|
171
|
+
reviewRequired: false,
|
|
172
|
+
securityScanRequired: false,
|
|
173
|
+
documentationRequired: false,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
band: 'standard',
|
|
177
|
+
minScore: 4,
|
|
178
|
+
maxScore: 6,
|
|
179
|
+
defaultEnforcement: 'soft-mandatory',
|
|
180
|
+
testCoverageThreshold: 75,
|
|
181
|
+
reviewRequired: true,
|
|
182
|
+
securityScanRequired: false,
|
|
183
|
+
documentationRequired: false,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
band: 'complex',
|
|
187
|
+
minScore: 7,
|
|
188
|
+
maxScore: 8,
|
|
189
|
+
defaultEnforcement: 'hard-mandatory',
|
|
190
|
+
testCoverageThreshold: 85,
|
|
191
|
+
reviewRequired: true,
|
|
192
|
+
securityScanRequired: true,
|
|
193
|
+
documentationRequired: true,
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
band: 'critical',
|
|
197
|
+
minScore: 9,
|
|
198
|
+
maxScore: 10,
|
|
199
|
+
defaultEnforcement: 'hard-mandatory',
|
|
200
|
+
testCoverageThreshold: 90,
|
|
201
|
+
reviewRequired: true,
|
|
202
|
+
securityScanRequired: true,
|
|
203
|
+
documentationRequired: true,
|
|
204
|
+
},
|
|
205
|
+
];
|
|
206
|
+
// ── Notification titles ──────────────────────────────────────────
|
|
207
|
+
export const NOTIFICATION_TITLES = {
|
|
208
|
+
issueValidationFailed: 'AI-SDLC: Issue Validation Failed',
|
|
209
|
+
complexityTooHigh: 'AI-SDLC: Complexity Too High',
|
|
210
|
+
agentFailed: 'AI-SDLC: Agent Failed',
|
|
211
|
+
guardrailViolations: 'AI-SDLC: Guardrail Violations',
|
|
212
|
+
prCreated: 'AI-SDLC: PR Created',
|
|
213
|
+
fixCIRetryLimit: 'AI-SDLC: Fix-CI Retry Limit Reached',
|
|
214
|
+
fixCIAgentFailed: 'AI-SDLC: Fix-CI Agent Failed',
|
|
215
|
+
fixCIGuardrailViolations: 'AI-SDLC: Fix-CI Guardrail Violations',
|
|
216
|
+
fixCIApplied: 'AI-SDLC: Fix-CI Applied',
|
|
217
|
+
};
|
|
218
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fly.io deployment target — deploys via Machines API or flyctl CLI.
|
|
3
|
+
*/
|
|
4
|
+
import type { DeploymentTarget, FetchFn, ExecFn } from './types.js';
|
|
5
|
+
export interface FlyioConfig {
|
|
6
|
+
/** Fly.io API token. */
|
|
7
|
+
token: string;
|
|
8
|
+
/** Fly.io application name. */
|
|
9
|
+
appName: string;
|
|
10
|
+
/** Use flyctl CLI mode instead of API. */
|
|
11
|
+
useCliMode?: boolean;
|
|
12
|
+
/** Fly.io API base URL (defaults to https://api.machines.dev). */
|
|
13
|
+
apiBase?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function createFlyioTarget(config: FlyioConfig, opts?: {
|
|
16
|
+
fetch?: FetchFn;
|
|
17
|
+
exec?: ExecFn;
|
|
18
|
+
}): DeploymentTarget;
|
|
19
|
+
//# sourceMappingURL=flyio-target.d.ts.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fly.io deployment target — deploys via Machines API or flyctl CLI.
|
|
3
|
+
*/
|
|
4
|
+
export function createFlyioTarget(config, opts) {
|
|
5
|
+
const httpFetch = opts?.fetch ?? globalThis.fetch;
|
|
6
|
+
const exec = opts?.exec;
|
|
7
|
+
const apiBase = config.apiBase ?? 'https://api.machines.dev';
|
|
8
|
+
const deployments = new Map();
|
|
9
|
+
function headers() {
|
|
10
|
+
return {
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
Authorization: `Bearer ${config.token}`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
async deploy(version, environment) {
|
|
17
|
+
const id = `fly-${Date.now()}-${environment}`;
|
|
18
|
+
const result = {
|
|
19
|
+
id,
|
|
20
|
+
state: 'deploying',
|
|
21
|
+
version,
|
|
22
|
+
startedAt: new Date().toISOString(),
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
if (config.useCliMode && exec) {
|
|
26
|
+
const { stdout } = await exec('flyctl', [
|
|
27
|
+
'deploy',
|
|
28
|
+
'--app',
|
|
29
|
+
config.appName,
|
|
30
|
+
'--image',
|
|
31
|
+
version,
|
|
32
|
+
'--now',
|
|
33
|
+
]);
|
|
34
|
+
result.url = extractFlyUrl(stdout, config.appName);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// Machines API: list machines, then update each
|
|
38
|
+
const listRes = await httpFetch(`${apiBase}/v1/apps/${config.appName}/machines`, {
|
|
39
|
+
headers: headers(),
|
|
40
|
+
});
|
|
41
|
+
if (!listRes.ok)
|
|
42
|
+
throw new Error(`Fly.io list machines failed: ${listRes.status}`);
|
|
43
|
+
const machines = (await listRes.json());
|
|
44
|
+
for (const machine of machines) {
|
|
45
|
+
const updateRes = await httpFetch(`${apiBase}/v1/apps/${config.appName}/machines/${machine.id}`, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: headers(),
|
|
48
|
+
body: JSON.stringify({
|
|
49
|
+
config: { ...machine.config, image: version },
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
if (!updateRes.ok)
|
|
53
|
+
throw new Error(`Fly.io update machine ${machine.id} failed: ${updateRes.status}`);
|
|
54
|
+
}
|
|
55
|
+
result.url = `https://${config.appName}.fly.dev`;
|
|
56
|
+
}
|
|
57
|
+
result.state = 'healthy';
|
|
58
|
+
result.completedAt = new Date().toISOString();
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
result.state = 'failed';
|
|
62
|
+
result.error = err instanceof Error ? err.message : String(err);
|
|
63
|
+
result.completedAt = new Date().toISOString();
|
|
64
|
+
}
|
|
65
|
+
deployments.set(id, result);
|
|
66
|
+
return result;
|
|
67
|
+
},
|
|
68
|
+
async getStatus(id) {
|
|
69
|
+
const cached = deployments.get(id);
|
|
70
|
+
if (cached)
|
|
71
|
+
return cached;
|
|
72
|
+
return { id, state: 'pending', version: 'unknown', startedAt: '' };
|
|
73
|
+
},
|
|
74
|
+
async rollback(id) {
|
|
75
|
+
const rollbackId = `${id}-rollback`;
|
|
76
|
+
const original = deployments.get(id);
|
|
77
|
+
const result = {
|
|
78
|
+
id: rollbackId,
|
|
79
|
+
state: 'deploying',
|
|
80
|
+
version: original?.version ?? 'unknown',
|
|
81
|
+
startedAt: new Date().toISOString(),
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
if (config.useCliMode && exec) {
|
|
85
|
+
await exec('flyctl', ['releases', '--app', config.appName, '--json']);
|
|
86
|
+
// In CLI mode, trigger a rollback by deploying the previous image
|
|
87
|
+
await exec('flyctl', ['deploy', '--app', config.appName, '--strategy', 'rolling']);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// API mode: re-deploy with the original version metadata
|
|
91
|
+
const listRes = await httpFetch(`${apiBase}/v1/apps/${config.appName}/machines`, {
|
|
92
|
+
headers: headers(),
|
|
93
|
+
});
|
|
94
|
+
if (!listRes.ok)
|
|
95
|
+
throw new Error(`Fly.io list machines failed: ${listRes.status}`);
|
|
96
|
+
}
|
|
97
|
+
result.state = 'rolled-back';
|
|
98
|
+
result.completedAt = new Date().toISOString();
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
result.state = 'failed';
|
|
102
|
+
result.error = err instanceof Error ? err.message : String(err);
|
|
103
|
+
result.completedAt = new Date().toISOString();
|
|
104
|
+
}
|
|
105
|
+
deployments.set(rollbackId, result);
|
|
106
|
+
return result;
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function extractFlyUrl(stdout, appName) {
|
|
111
|
+
const urlMatch = stdout.match(/https?:\/\/[^\s]+/);
|
|
112
|
+
return urlMatch ? urlMatch[0] : `https://${appName}.fly.dev`;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=flyio-target.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment targets barrel export.
|
|
3
|
+
*/
|
|
4
|
+
export type { DeploymentTargetConfig, HealthCheckConfig, DeploymentState, DeploymentResult, DeploymentTarget, ExecFn, FetchFn, } from './types.js';
|
|
5
|
+
export { createKubernetesTarget } from './kubernetes-target.js';
|
|
6
|
+
export type { KubernetesConfig } from './kubernetes-target.js';
|
|
7
|
+
export { createVercelTarget } from './vercel-target.js';
|
|
8
|
+
export type { VercelConfig } from './vercel-target.js';
|
|
9
|
+
export { createFlyioTarget } from './flyio-target.js';
|
|
10
|
+
export type { FlyioConfig } from './flyio-target.js';
|
|
11
|
+
export type { CanaryStep, CanaryConfig, BlueGreenConfig, RollingConfig, RolloutStrategy, RolloutPhase, RolloutStatus, RolloutMetrics, MetricsSource, RolloutControllerConfig, } from './rollout-types.js';
|
|
12
|
+
export { createHttpMetricsCollector, createStubMetricsCollector } from './metrics-collector.js';
|
|
13
|
+
export type { HttpMetricsConfig } from './metrics-collector.js';
|
|
14
|
+
export { RolloutController } from './rollout-controller.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment targets barrel export.
|
|
3
|
+
*/
|
|
4
|
+
export { createKubernetesTarget } from './kubernetes-target.js';
|
|
5
|
+
export { createVercelTarget } from './vercel-target.js';
|
|
6
|
+
export { createFlyioTarget } from './flyio-target.js';
|
|
7
|
+
// Metrics collection
|
|
8
|
+
export { createHttpMetricsCollector, createStubMetricsCollector } from './metrics-collector.js';
|
|
9
|
+
// Rollout controller
|
|
10
|
+
export { RolloutController } from './rollout-controller.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kubernetes deployment target — deploys via K8s API or kubectl CLI.
|
|
3
|
+
*/
|
|
4
|
+
import type { DeploymentTarget, FetchFn, ExecFn } from './types.js';
|
|
5
|
+
export interface KubernetesConfig {
|
|
6
|
+
/** K8s API server URL (for API mode). */
|
|
7
|
+
apiServer?: string;
|
|
8
|
+
/** Bearer token for K8s API auth. */
|
|
9
|
+
token?: string;
|
|
10
|
+
/** Namespace to deploy into. */
|
|
11
|
+
namespace: string;
|
|
12
|
+
/** Deployment name. */
|
|
13
|
+
deploymentName: string;
|
|
14
|
+
/** Container name within the deployment. */
|
|
15
|
+
containerName: string;
|
|
16
|
+
/** Image repository (e.g. 'registry.io/app'). */
|
|
17
|
+
imageRepo: string;
|
|
18
|
+
/** Use kubectl CLI mode instead of API. */
|
|
19
|
+
useCliMode?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function createKubernetesTarget(config: KubernetesConfig, opts?: {
|
|
22
|
+
fetch?: FetchFn;
|
|
23
|
+
exec?: ExecFn;
|
|
24
|
+
}): DeploymentTarget;
|
|
25
|
+
//# sourceMappingURL=kubernetes-target.d.ts.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kubernetes deployment target — deploys via K8s API or kubectl CLI.
|
|
3
|
+
*/
|
|
4
|
+
export function createKubernetesTarget(config, opts) {
|
|
5
|
+
const httpFetch = opts?.fetch ?? globalThis.fetch;
|
|
6
|
+
const exec = opts?.exec;
|
|
7
|
+
const deployments = new Map();
|
|
8
|
+
function apiUrl(path) {
|
|
9
|
+
return `${config.apiServer}/apis/apps/v1/namespaces/${config.namespace}${path}`;
|
|
10
|
+
}
|
|
11
|
+
function headers() {
|
|
12
|
+
return {
|
|
13
|
+
'Content-Type': 'application/strategic-merge-patch+json',
|
|
14
|
+
...(config.token ? { Authorization: `Bearer ${config.token}` } : {}),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
async deploy(version, environment) {
|
|
19
|
+
const id = `k8s-${Date.now()}-${environment}`;
|
|
20
|
+
const image = `${config.imageRepo}:${version}`;
|
|
21
|
+
const result = {
|
|
22
|
+
id,
|
|
23
|
+
state: 'deploying',
|
|
24
|
+
version,
|
|
25
|
+
startedAt: new Date().toISOString(),
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
if (config.useCliMode && exec) {
|
|
29
|
+
await exec('kubectl', [
|
|
30
|
+
'set',
|
|
31
|
+
'image',
|
|
32
|
+
`deployment/${config.deploymentName}`,
|
|
33
|
+
`${config.containerName}=${image}`,
|
|
34
|
+
'-n',
|
|
35
|
+
config.namespace,
|
|
36
|
+
]);
|
|
37
|
+
await exec('kubectl', [
|
|
38
|
+
'rollout',
|
|
39
|
+
'status',
|
|
40
|
+
`deployment/${config.deploymentName}`,
|
|
41
|
+
'-n',
|
|
42
|
+
config.namespace,
|
|
43
|
+
'--timeout=120s',
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const patch = {
|
|
48
|
+
spec: {
|
|
49
|
+
template: {
|
|
50
|
+
spec: {
|
|
51
|
+
containers: [{ name: config.containerName, image }],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
const res = await httpFetch(apiUrl(`/deployments/${config.deploymentName}`), {
|
|
57
|
+
method: 'PATCH',
|
|
58
|
+
headers: headers(),
|
|
59
|
+
body: JSON.stringify(patch),
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok)
|
|
62
|
+
throw new Error(`K8s API error: ${res.status}`);
|
|
63
|
+
}
|
|
64
|
+
result.state = 'healthy';
|
|
65
|
+
result.completedAt = new Date().toISOString();
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
result.state = 'failed';
|
|
69
|
+
result.error = err instanceof Error ? err.message : String(err);
|
|
70
|
+
result.completedAt = new Date().toISOString();
|
|
71
|
+
}
|
|
72
|
+
deployments.set(id, result);
|
|
73
|
+
return result;
|
|
74
|
+
},
|
|
75
|
+
async getStatus(id) {
|
|
76
|
+
const cached = deployments.get(id);
|
|
77
|
+
if (cached)
|
|
78
|
+
return cached;
|
|
79
|
+
return { id, state: 'pending', version: 'unknown', startedAt: '' };
|
|
80
|
+
},
|
|
81
|
+
async rollback(id) {
|
|
82
|
+
const original = deployments.get(id);
|
|
83
|
+
const rollbackId = `${id}-rollback`;
|
|
84
|
+
const result = {
|
|
85
|
+
id: rollbackId,
|
|
86
|
+
state: 'deploying',
|
|
87
|
+
version: original?.version ?? 'unknown',
|
|
88
|
+
startedAt: new Date().toISOString(),
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
if (config.useCliMode && exec) {
|
|
92
|
+
await exec('kubectl', [
|
|
93
|
+
'rollout',
|
|
94
|
+
'undo',
|
|
95
|
+
`deployment/${config.deploymentName}`,
|
|
96
|
+
'-n',
|
|
97
|
+
config.namespace,
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const res = await httpFetch(apiUrl(`/deployments/${config.deploymentName}`) + '?dryRun=false', {
|
|
102
|
+
method: 'PATCH',
|
|
103
|
+
headers: headers(),
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
spec: { template: { metadata: { annotations: { 'ai-sdlc/rollback': 'true' } } } },
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
if (!res.ok)
|
|
109
|
+
throw new Error(`K8s rollback API error: ${res.status}`);
|
|
110
|
+
}
|
|
111
|
+
result.state = 'rolled-back';
|
|
112
|
+
result.completedAt = new Date().toISOString();
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
result.state = 'failed';
|
|
116
|
+
result.error = err instanceof Error ? err.message : String(err);
|
|
117
|
+
result.completedAt = new Date().toISOString();
|
|
118
|
+
}
|
|
119
|
+
deployments.set(rollbackId, result);
|
|
120
|
+
return result;
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=kubernetes-target.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics collection implementations for rollout monitoring.
|
|
3
|
+
*/
|
|
4
|
+
import type { RolloutMetrics, MetricsSource } from './rollout-types.js';
|
|
5
|
+
import type { FetchFn } from './types.js';
|
|
6
|
+
export interface HttpMetricsConfig {
|
|
7
|
+
/** URL to check for health/metrics. */
|
|
8
|
+
healthUrl: string;
|
|
9
|
+
/** Expected HTTP status code (defaults to 200). */
|
|
10
|
+
expectedStatus?: number;
|
|
11
|
+
/** Request timeout in ms (defaults to 5000). */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
/** Number of sample requests for latency calculation. */
|
|
14
|
+
sampleCount?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Collects metrics by probing an HTTP health endpoint.
|
|
18
|
+
* Measures latency, error rate based on HTTP status, and reports
|
|
19
|
+
* basic availability metrics.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createHttpMetricsCollector(config: HttpMetricsConfig, opts?: {
|
|
22
|
+
fetch?: FetchFn;
|
|
23
|
+
}): MetricsSource;
|
|
24
|
+
/**
|
|
25
|
+
* Creates a metrics source that returns configurable static metrics.
|
|
26
|
+
* Useful for testing rollout controllers without real infrastructure.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createStubMetricsCollector(defaults?: Partial<RolloutMetrics>): MetricsSource & {
|
|
29
|
+
setMetrics(m: Partial<RolloutMetrics>): void;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=metrics-collector.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics collection implementations for rollout monitoring.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Collects metrics by probing an HTTP health endpoint.
|
|
6
|
+
* Measures latency, error rate based on HTTP status, and reports
|
|
7
|
+
* basic availability metrics.
|
|
8
|
+
*/
|
|
9
|
+
export function createHttpMetricsCollector(config, opts) {
|
|
10
|
+
const httpFetch = opts?.fetch ?? globalThis.fetch;
|
|
11
|
+
const expectedStatus = config.expectedStatus ?? 200;
|
|
12
|
+
const timeoutMs = config.timeoutMs ?? 5000;
|
|
13
|
+
const sampleCount = config.sampleCount ?? 5;
|
|
14
|
+
return {
|
|
15
|
+
async collect(_deploymentId) {
|
|
16
|
+
const latencies = [];
|
|
17
|
+
let errors = 0;
|
|
18
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
19
|
+
const start = Date.now();
|
|
20
|
+
try {
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
23
|
+
const res = await httpFetch(config.healthUrl, { signal: controller.signal });
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
const elapsed = Date.now() - start;
|
|
26
|
+
latencies.push(elapsed);
|
|
27
|
+
if (res.status !== expectedStatus)
|
|
28
|
+
errors++;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
errors++;
|
|
32
|
+
latencies.push(timeoutMs);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Sort latencies for P95
|
|
36
|
+
latencies.sort((a, b) => a - b);
|
|
37
|
+
const p95Index = Math.min(Math.ceil(latencies.length * 0.95) - 1, latencies.length - 1);
|
|
38
|
+
const p95 = latencies[p95Index] ?? 0;
|
|
39
|
+
const successCount = sampleCount - errors;
|
|
40
|
+
return {
|
|
41
|
+
errorRate: errors / sampleCount,
|
|
42
|
+
latencyP95Ms: p95,
|
|
43
|
+
requestsPerSecond: successCount, // approximation from sample
|
|
44
|
+
healthyInstances: successCount > 0 ? 1 : 0,
|
|
45
|
+
totalInstances: 1,
|
|
46
|
+
collectedAt: new Date().toISOString(),
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// ── Stub Metrics Collector (for testing) ────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Creates a metrics source that returns configurable static metrics.
|
|
54
|
+
* Useful for testing rollout controllers without real infrastructure.
|
|
55
|
+
*/
|
|
56
|
+
export function createStubMetricsCollector(defaults) {
|
|
57
|
+
let current = {
|
|
58
|
+
errorRate: 0,
|
|
59
|
+
latencyP95Ms: 50,
|
|
60
|
+
requestsPerSecond: 100,
|
|
61
|
+
healthyInstances: 3,
|
|
62
|
+
totalInstances: 3,
|
|
63
|
+
collectedAt: new Date().toISOString(),
|
|
64
|
+
...defaults,
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
async collect(_deploymentId) {
|
|
68
|
+
return { ...current, collectedAt: new Date().toISOString() };
|
|
69
|
+
},
|
|
70
|
+
setMetrics(m) {
|
|
71
|
+
current = { ...current, ...m };
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=metrics-collector.js.map
|