@bradygaster/squad-cli 0.9.2-insider.5 → 0.9.2-insider.6
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/cli/commands/watch/capabilities/budget-check.d.ts +29 -0
- package/dist/cli/commands/watch/capabilities/budget-check.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/budget-check.js +38 -0
- package/dist/cli/commands/watch/capabilities/budget-check.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/circuit-breaker.d.ts +52 -0
- package/dist/cli/commands/watch/capabilities/circuit-breaker.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/circuit-breaker.js +152 -0
- package/dist/cli/commands/watch/capabilities/circuit-breaker.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/execute.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/execute.js +38 -1
- package/dist/cli/commands/watch/capabilities/execute.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/health-check.d.ts +29 -0
- package/dist/cli/commands/watch/capabilities/health-check.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/health-check.js +139 -0
- package/dist/cli/commands/watch/capabilities/health-check.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/heartbeat.d.ts +48 -0
- package/dist/cli/commands/watch/capabilities/heartbeat.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/heartbeat.js +115 -0
- package/dist/cli/commands/watch/capabilities/heartbeat.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/index.d.ts +2 -0
- package/dist/cli/commands/watch/capabilities/index.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/index.js +12 -0
- package/dist/cli/commands/watch/capabilities/index.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/lockfile.d.ts +30 -0
- package/dist/cli/commands/watch/capabilities/lockfile.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/lockfile.js +100 -0
- package/dist/cli/commands/watch/capabilities/lockfile.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/machine-capabilities.d.ts +30 -0
- package/dist/cli/commands/watch/capabilities/machine-capabilities.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/machine-capabilities.js +103 -0
- package/dist/cli/commands/watch/capabilities/machine-capabilities.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/post-failure.d.ts +19 -0
- package/dist/cli/commands/watch/capabilities/post-failure.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/post-failure.js +58 -0
- package/dist/cli/commands/watch/capabilities/post-failure.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/priority.d.ts +59 -0
- package/dist/cli/commands/watch/capabilities/priority.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/priority.js +101 -0
- package/dist/cli/commands/watch/capabilities/priority.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/rate-pool.d.ts +67 -0
- package/dist/cli/commands/watch/capabilities/rate-pool.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/rate-pool.js +187 -0
- package/dist/cli/commands/watch/capabilities/rate-pool.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/stale-reclaim.d.ts +23 -0
- package/dist/cli/commands/watch/capabilities/stale-reclaim.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/stale-reclaim.js +87 -0
- package/dist/cli/commands/watch/capabilities/stale-reclaim.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/webhook-alerts.d.ts +29 -0
- package/dist/cli/commands/watch/capabilities/webhook-alerts.d.ts.map +1 -0
- package/dist/cli/commands/watch/capabilities/webhook-alerts.js +114 -0
- package/dist/cli/commands/watch/capabilities/webhook-alerts.js.map +1 -0
- package/dist/cli/commands/watch/config.d.ts +15 -0
- package/dist/cli/commands/watch/config.d.ts.map +1 -1
- package/dist/cli/commands/watch/config.js +48 -1
- package/dist/cli/commands/watch/config.js.map +1 -1
- package/dist/cli/commands/watch/index.d.ts +14 -0
- package/dist/cli/commands/watch/index.d.ts.map +1 -1
- package/dist/cli/commands/watch/index.js +55 -2
- package/dist/cli/commands/watch/index.js.map +1 -1
- package/dist/cli/core/nap.d.ts.map +1 -1
- package/dist/cli/core/nap.js +9 -4
- package/dist/cli/core/nap.js.map +1 -1
- package/dist/cli-entry.js +53 -0
- package/dist/cli-entry.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget check utility — enforce configurable per-round limits.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ralph-watch.ps1 rate-pool / budget tracking logic.
|
|
5
|
+
*
|
|
6
|
+
* Config (via squad.config.ts → watch.capabilities or CLI flags):
|
|
7
|
+
* maxIssuesPerRound – max issues to execute in a single round (default: 5)
|
|
8
|
+
* maxCostPerRound – cost cap (abstract units) per round (default: Infinity)
|
|
9
|
+
*
|
|
10
|
+
* This is a utility module — not a WatchCapability.
|
|
11
|
+
* Called by the execute capability before spawning agent sessions.
|
|
12
|
+
*/
|
|
13
|
+
export interface BudgetConfig {
|
|
14
|
+
maxIssuesPerRound?: number;
|
|
15
|
+
maxCostPerRound?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface BudgetResult {
|
|
18
|
+
allowed: number;
|
|
19
|
+
reason: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Determine how many issues can be executed this round given budget constraints.
|
|
23
|
+
*
|
|
24
|
+
* @param requested Number of issues the execute phase wants to process.
|
|
25
|
+
* @param config Budget limits from config or CLI.
|
|
26
|
+
* @returns How many are allowed + explanation.
|
|
27
|
+
*/
|
|
28
|
+
export declare function checkBudget(requested: number, config?: BudgetConfig): BudgetResult;
|
|
29
|
+
//# sourceMappingURL=budget-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget-check.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/budget-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,YAAY,CAmBlF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Budget check utility — enforce configurable per-round limits.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ralph-watch.ps1 rate-pool / budget tracking logic.
|
|
5
|
+
*
|
|
6
|
+
* Config (via squad.config.ts → watch.capabilities or CLI flags):
|
|
7
|
+
* maxIssuesPerRound – max issues to execute in a single round (default: 5)
|
|
8
|
+
* maxCostPerRound – cost cap (abstract units) per round (default: Infinity)
|
|
9
|
+
*
|
|
10
|
+
* This is a utility module — not a WatchCapability.
|
|
11
|
+
* Called by the execute capability before spawning agent sessions.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_MAX_ISSUES = 5;
|
|
14
|
+
/**
|
|
15
|
+
* Determine how many issues can be executed this round given budget constraints.
|
|
16
|
+
*
|
|
17
|
+
* @param requested Number of issues the execute phase wants to process.
|
|
18
|
+
* @param config Budget limits from config or CLI.
|
|
19
|
+
* @returns How many are allowed + explanation.
|
|
20
|
+
*/
|
|
21
|
+
export function checkBudget(requested, config) {
|
|
22
|
+
const maxIssues = config?.maxIssuesPerRound ?? DEFAULT_MAX_ISSUES;
|
|
23
|
+
const maxCost = config?.maxCostPerRound ?? Infinity;
|
|
24
|
+
// Simple issue-count gate
|
|
25
|
+
if (requested <= maxIssues && maxCost === Infinity) {
|
|
26
|
+
return { allowed: requested, reason: `within budget (${requested}/${maxIssues})` };
|
|
27
|
+
}
|
|
28
|
+
const allowed = Math.min(requested, maxIssues);
|
|
29
|
+
const reasons = [];
|
|
30
|
+
if (requested > maxIssues) {
|
|
31
|
+
reasons.push(`capped to ${maxIssues} issues/round`);
|
|
32
|
+
}
|
|
33
|
+
if (maxCost !== Infinity) {
|
|
34
|
+
reasons.push(`cost cap: ${maxCost}`);
|
|
35
|
+
}
|
|
36
|
+
return { allowed, reason: reasons.join('; ') };
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=budget-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget-check.js","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/budget-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,MAAqB;IAClE,MAAM,SAAS,GAAG,MAAM,EAAE,iBAAiB,IAAI,kBAAkB,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,EAAE,eAAe,IAAI,QAAQ,CAAC;IAEpD,0BAA0B;IAC1B,IAAI,SAAS,IAAI,SAAS,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACnD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,kBAAkB,SAAS,IAAI,SAAS,GAAG,EAAE,CAAC;IACrF,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,SAAS,GAAG,SAAS,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,aAAa,SAAS,eAAe,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Circuit Breaker capability — model-level fallback with cooldown.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ralph-watch.ps1 `Get-CircuitBreakerState` / `Update-CircuitBreakerOn*`.
|
|
5
|
+
* Tracks model failures, auto-fallback through a configurable chain, and
|
|
6
|
+
* cooldown timer. State persisted to `.squad/ralph-circuit-breaker.json`.
|
|
7
|
+
*
|
|
8
|
+
* This is a **utility module** consumed by the main watch loop, not a
|
|
9
|
+
* phase-based capability (it gates every round, not a specific phase).
|
|
10
|
+
*
|
|
11
|
+
* Config (via squad.config.ts → watch.circuitBreaker):
|
|
12
|
+
* preferredModel – default model to use (default: "claude-sonnet-4")
|
|
13
|
+
* fallbackChain – ordered list of fallback models
|
|
14
|
+
* cooldownMinutes – minutes before half-open probe (default: 10)
|
|
15
|
+
* requiredSuccessesToClose – successes in half-open before closing (default: 2)
|
|
16
|
+
*/
|
|
17
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
18
|
+
export interface ModelCircuitBreakerState {
|
|
19
|
+
state: CircuitState;
|
|
20
|
+
preferredModel: string;
|
|
21
|
+
currentModel: string;
|
|
22
|
+
fallbackChain: string[];
|
|
23
|
+
lastRateLimitHit: string | null;
|
|
24
|
+
cooldownMinutes: number;
|
|
25
|
+
consecutiveSuccesses: number;
|
|
26
|
+
requiredSuccessesToClose: number;
|
|
27
|
+
totalFallbacks: number;
|
|
28
|
+
totalRecoveries: number;
|
|
29
|
+
}
|
|
30
|
+
export interface CircuitBreakerConfig {
|
|
31
|
+
preferredModel?: string;
|
|
32
|
+
fallbackChain?: string[];
|
|
33
|
+
cooldownMinutes?: number;
|
|
34
|
+
requiredSuccessesToClose?: number;
|
|
35
|
+
}
|
|
36
|
+
export declare class ModelCircuitBreaker {
|
|
37
|
+
private statePath;
|
|
38
|
+
constructor(squadDir: string, config?: CircuitBreakerConfig);
|
|
39
|
+
load(): ModelCircuitBreakerState;
|
|
40
|
+
save(state: ModelCircuitBreakerState): void;
|
|
41
|
+
/** Get the model to use for the current round. */
|
|
42
|
+
getCurrentModel(): string;
|
|
43
|
+
/** Call after a successful round. */
|
|
44
|
+
onSuccess(): void;
|
|
45
|
+
/** Call when a rate limit or model error is detected. */
|
|
46
|
+
onRateLimit(): void;
|
|
47
|
+
/** Reset to defaults (used by post-failure remediation). */
|
|
48
|
+
reset(): void;
|
|
49
|
+
/** Get full state for diagnostics. */
|
|
50
|
+
getState(): ModelCircuitBreakerState;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,YAAY,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wBAAwB,EAAE,MAAM,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAeD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,SAAS,CAAS;gBAEd,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,oBAAoB;IAkB3D,IAAI,IAAI,wBAAwB;IAuBhC,IAAI,CAAC,KAAK,EAAE,wBAAwB,GAAG,IAAI;IAU3C,kDAAkD;IAClD,eAAe,IAAI,MAAM;IAmBzB,qCAAqC;IACrC,SAAS,IAAI,IAAI;IAcjB,yDAAyD;IACzD,WAAW,IAAI,IAAI;IAanB,4DAA4D;IAC5D,KAAK,IAAI,IAAI;IAIb,sCAAsC;IACtC,QAAQ,IAAI,wBAAwB;CAGrC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Circuit Breaker capability — model-level fallback with cooldown.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ralph-watch.ps1 `Get-CircuitBreakerState` / `Update-CircuitBreakerOn*`.
|
|
5
|
+
* Tracks model failures, auto-fallback through a configurable chain, and
|
|
6
|
+
* cooldown timer. State persisted to `.squad/ralph-circuit-breaker.json`.
|
|
7
|
+
*
|
|
8
|
+
* This is a **utility module** consumed by the main watch loop, not a
|
|
9
|
+
* phase-based capability (it gates every round, not a specific phase).
|
|
10
|
+
*
|
|
11
|
+
* Config (via squad.config.ts → watch.circuitBreaker):
|
|
12
|
+
* preferredModel – default model to use (default: "claude-sonnet-4")
|
|
13
|
+
* fallbackChain – ordered list of fallback models
|
|
14
|
+
* cooldownMinutes – minutes before half-open probe (default: 10)
|
|
15
|
+
* requiredSuccessesToClose – successes in half-open before closing (default: 2)
|
|
16
|
+
*/
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
const DEFAULT_STATE = {
|
|
20
|
+
state: 'closed',
|
|
21
|
+
preferredModel: 'claude-sonnet-4',
|
|
22
|
+
currentModel: 'claude-sonnet-4',
|
|
23
|
+
fallbackChain: ['gpt-4.1', 'claude-haiku-4.5'],
|
|
24
|
+
lastRateLimitHit: null,
|
|
25
|
+
cooldownMinutes: 10,
|
|
26
|
+
consecutiveSuccesses: 0,
|
|
27
|
+
requiredSuccessesToClose: 2,
|
|
28
|
+
totalFallbacks: 0,
|
|
29
|
+
totalRecoveries: 0,
|
|
30
|
+
};
|
|
31
|
+
export class ModelCircuitBreaker {
|
|
32
|
+
statePath;
|
|
33
|
+
constructor(squadDir, config) {
|
|
34
|
+
this.statePath = path.join(squadDir, 'ralph-circuit-breaker.json');
|
|
35
|
+
// Ensure defaults incorporate any user config
|
|
36
|
+
if (config) {
|
|
37
|
+
const state = this.load();
|
|
38
|
+
let dirty = false;
|
|
39
|
+
if (config.preferredModel && state.preferredModel !== config.preferredModel) {
|
|
40
|
+
state.preferredModel = config.preferredModel;
|
|
41
|
+
if (state.state === 'closed')
|
|
42
|
+
state.currentModel = config.preferredModel;
|
|
43
|
+
dirty = true;
|
|
44
|
+
}
|
|
45
|
+
if (config.fallbackChain) {
|
|
46
|
+
state.fallbackChain = config.fallbackChain;
|
|
47
|
+
dirty = true;
|
|
48
|
+
}
|
|
49
|
+
if (config.cooldownMinutes) {
|
|
50
|
+
state.cooldownMinutes = config.cooldownMinutes;
|
|
51
|
+
dirty = true;
|
|
52
|
+
}
|
|
53
|
+
if (config.requiredSuccessesToClose) {
|
|
54
|
+
state.requiredSuccessesToClose = config.requiredSuccessesToClose;
|
|
55
|
+
dirty = true;
|
|
56
|
+
}
|
|
57
|
+
if (dirty)
|
|
58
|
+
this.save(state);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
load() {
|
|
62
|
+
try {
|
|
63
|
+
if (!fs.existsSync(this.statePath))
|
|
64
|
+
return { ...DEFAULT_STATE };
|
|
65
|
+
const raw = fs.readFileSync(this.statePath, 'utf-8');
|
|
66
|
+
if (!raw)
|
|
67
|
+
return { ...DEFAULT_STATE };
|
|
68
|
+
const parsed = JSON.parse(raw);
|
|
69
|
+
// Handle legacy nested schema (model_fallback wrapper)
|
|
70
|
+
const legacy = parsed;
|
|
71
|
+
if (!parsed.preferredModel && legacy['model_fallback']) {
|
|
72
|
+
const mf = legacy['model_fallback'];
|
|
73
|
+
return {
|
|
74
|
+
...DEFAULT_STATE,
|
|
75
|
+
preferredModel: mf['preferred'] ?? DEFAULT_STATE.preferredModel,
|
|
76
|
+
currentModel: mf['preferred'] ?? DEFAULT_STATE.currentModel,
|
|
77
|
+
fallbackChain: mf['fallback_chain'] ?? DEFAULT_STATE.fallbackChain,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return { ...DEFAULT_STATE, ...parsed };
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return { ...DEFAULT_STATE };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
save(state) {
|
|
87
|
+
try {
|
|
88
|
+
const dir = path.dirname(this.statePath);
|
|
89
|
+
if (!fs.existsSync(dir)) {
|
|
90
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
fs.writeFileSync(this.statePath, JSON.stringify(state, null, 2), 'utf-8');
|
|
93
|
+
}
|
|
94
|
+
catch { /* best-effort */ }
|
|
95
|
+
}
|
|
96
|
+
/** Get the model to use for the current round. */
|
|
97
|
+
getCurrentModel() {
|
|
98
|
+
const state = this.load();
|
|
99
|
+
if (state.state === 'closed')
|
|
100
|
+
return state.preferredModel;
|
|
101
|
+
if (state.state === 'open') {
|
|
102
|
+
if (state.lastRateLimitHit) {
|
|
103
|
+
const elapsed = Date.now() - new Date(state.lastRateLimitHit).getTime();
|
|
104
|
+
if (elapsed >= state.cooldownMinutes * 60_000) {
|
|
105
|
+
state.state = 'half-open';
|
|
106
|
+
state.currentModel = state.preferredModel;
|
|
107
|
+
this.save(state);
|
|
108
|
+
return state.preferredModel;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return state.currentModel;
|
|
112
|
+
}
|
|
113
|
+
// half-open — probe preferred
|
|
114
|
+
return state.preferredModel;
|
|
115
|
+
}
|
|
116
|
+
/** Call after a successful round. */
|
|
117
|
+
onSuccess() {
|
|
118
|
+
const state = this.load();
|
|
119
|
+
if (state.state === 'half-open') {
|
|
120
|
+
state.consecutiveSuccesses++;
|
|
121
|
+
if (state.consecutiveSuccesses >= state.requiredSuccessesToClose) {
|
|
122
|
+
state.state = 'closed';
|
|
123
|
+
state.currentModel = state.preferredModel;
|
|
124
|
+
state.consecutiveSuccesses = 0;
|
|
125
|
+
state.totalRecoveries++;
|
|
126
|
+
}
|
|
127
|
+
this.save(state);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Call when a rate limit or model error is detected. */
|
|
131
|
+
onRateLimit() {
|
|
132
|
+
const state = this.load();
|
|
133
|
+
state.state = 'open';
|
|
134
|
+
state.lastRateLimitHit = new Date().toISOString();
|
|
135
|
+
state.consecutiveSuccesses = 0;
|
|
136
|
+
state.totalFallbacks++;
|
|
137
|
+
// Pick first fallback
|
|
138
|
+
if (state.fallbackChain.length > 0) {
|
|
139
|
+
state.currentModel = state.fallbackChain[0];
|
|
140
|
+
}
|
|
141
|
+
this.save(state);
|
|
142
|
+
}
|
|
143
|
+
/** Reset to defaults (used by post-failure remediation). */
|
|
144
|
+
reset() {
|
|
145
|
+
this.save({ ...DEFAULT_STATE });
|
|
146
|
+
}
|
|
147
|
+
/** Get full state for diagnostics. */
|
|
148
|
+
getState() {
|
|
149
|
+
return this.load();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/execute.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/execute.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAGrF,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChC,SAAS,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrC;AAiCD,qDAAqD;AACrD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,EACnE,YAAY,EAAE,mBAAmB,GAAG,IAAI,EACxC,MAAM,EAAE,kBAAkB,EAAE,GAC3B,kBAAkB,EAAE,CAStB;AAsCD,qBAAa,iBAAkB,YAAW,eAAe;IACvD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,WAAW,uDAAuD;IAC3E,QAAQ,CAAC,WAAW,EAAG,SAAS,CAAU;IAC1C,QAAQ,CAAC,QAAQ,WAAU;IAC3B,QAAQ,CAAC,KAAK,EAAG,cAAc,CAAU;IAEnC,SAAS,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAQ3D,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA8EhE"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Execute capability — spawns Copilot sessions for eligible issues.
|
|
3
3
|
*/
|
|
4
4
|
import { execFile } from 'node:child_process';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { RatePool } from './rate-pool.js';
|
|
5
7
|
/** Labels that block autonomous execution. */
|
|
6
8
|
const BLOCKED_LABELS = new Set([
|
|
7
9
|
'status:blocked',
|
|
@@ -102,7 +104,42 @@ export class ExecuteCapability {
|
|
|
102
104
|
if (batch.length === 0) {
|
|
103
105
|
return { success: true, summary: 'no executable issues' };
|
|
104
106
|
}
|
|
105
|
-
|
|
107
|
+
// Rate pool gate — respect shared API budget across instances
|
|
108
|
+
const poolCfg = context.config['ratePool'];
|
|
109
|
+
const squadDir = path.join(context.teamRoot, '.squad');
|
|
110
|
+
const ratePool = new RatePool(squadDir, {
|
|
111
|
+
maxCallsPerInterval: poolCfg?.['maxCallsPerInterval'] ?? undefined,
|
|
112
|
+
intervalSeconds: poolCfg?.['intervalSeconds'] ?? undefined,
|
|
113
|
+
poolFile: poolCfg?.['poolFile'] ?? undefined,
|
|
114
|
+
});
|
|
115
|
+
const admitted = [];
|
|
116
|
+
const skipped = [];
|
|
117
|
+
for (const issue of batch) {
|
|
118
|
+
if (ratePool.acquireSlot()) {
|
|
119
|
+
admitted.push(issue);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
skipped.push(issue.number);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (admitted.length === 0) {
|
|
126
|
+
const status = ratePool.getPoolStatus();
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
summary: `rate pool exhausted (${status.slotsUsed}/${status.maxSlots}), ${skipped.length} skipped`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (skipped.length > 0) {
|
|
133
|
+
console.log(`[rate-pool] skipped issues due to budget: ${skipped.join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
const results = await Promise.all(admitted.map(async (issue) => {
|
|
136
|
+
try {
|
|
137
|
+
return await executeOne(issue, context, timeout);
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
ratePool.releaseSlot();
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
106
143
|
const succeeded = results.filter(r => r.success).length;
|
|
107
144
|
const failed = results.length - succeeded;
|
|
108
145
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/execute.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAqB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/execute.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health-check capability — pre-round watchdog.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ralph-watch.ps1 `Invoke-RalphHealthCheck`.
|
|
5
|
+
* Runs in the `pre-scan` phase and verifies:
|
|
6
|
+
* 1. gh CLI authenticated
|
|
7
|
+
* 2. Circuit breaker state file is valid
|
|
8
|
+
* 3. Disk space above threshold (configurable, default 500 MB)
|
|
9
|
+
* 4. Git branch matches expected (optional)
|
|
10
|
+
*
|
|
11
|
+
* Config (via squad.config.ts → watch.capabilities["health-check"]):
|
|
12
|
+
* diskThresholdMB – minimum free disk in MB (default: 500)
|
|
13
|
+
* expectedBranch – warn if not on this branch (optional)
|
|
14
|
+
*/
|
|
15
|
+
import type { WatchCapability, WatchContext, PreflightResult, CapabilityResult } from '../types.js';
|
|
16
|
+
export interface HealthCheckResult {
|
|
17
|
+
healed: string[];
|
|
18
|
+
warnings: string[];
|
|
19
|
+
}
|
|
20
|
+
export declare class HealthCheckCapability implements WatchCapability {
|
|
21
|
+
readonly name = "health-check";
|
|
22
|
+
readonly description = "Pre-round watchdog: verify auth, disk space, branch, CB state";
|
|
23
|
+
readonly configShape: "object";
|
|
24
|
+
readonly requires: string[];
|
|
25
|
+
readonly phase: "pre-scan";
|
|
26
|
+
preflight(_context: WatchContext): Promise<PreflightResult>;
|
|
27
|
+
execute(context: WatchContext): Promise<CapabilityResult>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=health-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-check.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/health-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpG,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,WAAW,mEAAmE;IACvF,QAAQ,CAAC,WAAW,EAAG,QAAQ,CAAU;IACzC,QAAQ,CAAC,QAAQ,WAAU;IAC3B,QAAQ,CAAC,KAAK,EAAG,UAAU,CAAU;IAE/B,SAAS,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAI3D,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA0EhE"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health-check capability — pre-round watchdog.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ralph-watch.ps1 `Invoke-RalphHealthCheck`.
|
|
5
|
+
* Runs in the `pre-scan` phase and verifies:
|
|
6
|
+
* 1. gh CLI authenticated
|
|
7
|
+
* 2. Circuit breaker state file is valid
|
|
8
|
+
* 3. Disk space above threshold (configurable, default 500 MB)
|
|
9
|
+
* 4. Git branch matches expected (optional)
|
|
10
|
+
*
|
|
11
|
+
* Config (via squad.config.ts → watch.capabilities["health-check"]):
|
|
12
|
+
* diskThresholdMB – minimum free disk in MB (default: 500)
|
|
13
|
+
* expectedBranch – warn if not on this branch (optional)
|
|
14
|
+
*/
|
|
15
|
+
import { execFile } from 'node:child_process';
|
|
16
|
+
import { promisify } from 'node:util';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
const execFileAsync = promisify(execFile);
|
|
20
|
+
export class HealthCheckCapability {
|
|
21
|
+
name = 'health-check';
|
|
22
|
+
description = 'Pre-round watchdog: verify auth, disk space, branch, CB state';
|
|
23
|
+
configShape = 'object';
|
|
24
|
+
requires = ['gh'];
|
|
25
|
+
phase = 'pre-scan';
|
|
26
|
+
async preflight(_context) {
|
|
27
|
+
return { ok: true };
|
|
28
|
+
}
|
|
29
|
+
async execute(context) {
|
|
30
|
+
const healed = [];
|
|
31
|
+
const warnings = [];
|
|
32
|
+
const config = context.config;
|
|
33
|
+
const diskThresholdMB = config['diskThresholdMB'] ?? 500;
|
|
34
|
+
const expectedBranch = config['expectedBranch'];
|
|
35
|
+
// 1. Verify gh auth
|
|
36
|
+
try {
|
|
37
|
+
await execFileAsync('gh', ['auth', 'status'], { timeout: 10_000 });
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Try extracting auth info from git remote (P3 auth fallback)
|
|
41
|
+
const patFromRemote = await extractPatFromRemote(context.teamRoot);
|
|
42
|
+
if (patFromRemote) {
|
|
43
|
+
healed.push('Detected PAT in git remote URL — gh auth may need refresh');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
warnings.push('gh auth: not authenticated — run "gh auth login"');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// 2. Validate circuit breaker state file
|
|
50
|
+
const cbPath = path.join(context.teamRoot, '.squad', 'ralph-circuit-breaker.json');
|
|
51
|
+
try {
|
|
52
|
+
if (fs.existsSync(cbPath)) {
|
|
53
|
+
const rawContent = fs.readFileSync(cbPath, 'utf-8');
|
|
54
|
+
const parsed = JSON.parse(rawContent);
|
|
55
|
+
// Check for nested schema (legacy)
|
|
56
|
+
if (!parsed.preferredModel && parsed.model_fallback) {
|
|
57
|
+
healed.push('CB schema: would convert nested→flat on next use');
|
|
58
|
+
}
|
|
59
|
+
// Check for empty model
|
|
60
|
+
if (parsed.currentModel === '' || parsed.currentModel === null) {
|
|
61
|
+
warnings.push('CB state has empty currentModel — circuit-breaker will auto-reset');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// No CB file is fine — will be created on demand
|
|
67
|
+
}
|
|
68
|
+
// 3. Disk space check
|
|
69
|
+
try {
|
|
70
|
+
const freeBytes = await getFreeDiskSpace(context.teamRoot);
|
|
71
|
+
const freeMB = Math.floor(freeBytes / (1024 * 1024));
|
|
72
|
+
if (freeMB < diskThresholdMB) {
|
|
73
|
+
warnings.push(`Low disk space: ${freeMB} MB free (threshold: ${diskThresholdMB} MB)`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Disk check failed — non-blocking
|
|
78
|
+
}
|
|
79
|
+
// 4. Branch drift check
|
|
80
|
+
if (expectedBranch) {
|
|
81
|
+
try {
|
|
82
|
+
const { stdout } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
83
|
+
cwd: context.teamRoot,
|
|
84
|
+
timeout: 5_000,
|
|
85
|
+
});
|
|
86
|
+
const current = stdout.trim();
|
|
87
|
+
if (current && current !== expectedBranch) {
|
|
88
|
+
warnings.push(`Branch drift: on "${current}" (expected "${expectedBranch}")`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch { /* not a git repo or other error */ }
|
|
92
|
+
}
|
|
93
|
+
const summary = healed.length > 0 || warnings.length > 0
|
|
94
|
+
? `healed: ${healed.length}, warnings: ${warnings.length}`
|
|
95
|
+
: 'all checks passed';
|
|
96
|
+
return {
|
|
97
|
+
success: warnings.length === 0,
|
|
98
|
+
summary,
|
|
99
|
+
data: { healed, warnings },
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Try extracting a PAT from git remote URL (https://<pat>@github.com/...) */
|
|
104
|
+
async function extractPatFromRemote(cwd) {
|
|
105
|
+
try {
|
|
106
|
+
const { stdout } = await execFileAsync('git', ['config', '--get', 'remote.origin.url'], {
|
|
107
|
+
cwd,
|
|
108
|
+
timeout: 5_000,
|
|
109
|
+
});
|
|
110
|
+
const url = stdout.trim();
|
|
111
|
+
// Pattern: https://<token>@github.com/...
|
|
112
|
+
const match = url.match(/https:\/\/([^@]+)@github\.com/);
|
|
113
|
+
if (match && match[1] && match[1] !== 'oauth2') {
|
|
114
|
+
return match[1];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch { /* not available */ }
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/** Get free disk space in bytes for the drive containing the given path. */
|
|
121
|
+
async function getFreeDiskSpace(dirPath) {
|
|
122
|
+
const platform = process.platform;
|
|
123
|
+
if (platform === 'win32') {
|
|
124
|
+
const drive = path.parse(path.resolve(dirPath)).root;
|
|
125
|
+
const { stdout } = await execFileAsync('wmic', [
|
|
126
|
+
'logicaldisk', 'where', `DeviceID='${drive.replace('\\', '')}'`,
|
|
127
|
+
'get', 'FreeSpace', '/value',
|
|
128
|
+
], { timeout: 5_000 });
|
|
129
|
+
const match = stdout.match(/FreeSpace=(\d+)/);
|
|
130
|
+
return match ? parseInt(match[1], 10) : Infinity;
|
|
131
|
+
}
|
|
132
|
+
// Unix: use df
|
|
133
|
+
const { stdout } = await execFileAsync('df', ['--output=avail', '-B1', dirPath], {
|
|
134
|
+
timeout: 5_000,
|
|
135
|
+
});
|
|
136
|
+
const lines = stdout.trim().split('\n');
|
|
137
|
+
return parseInt(lines[lines.length - 1].trim(), 10) || Infinity;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=health-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-check.js","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/health-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heartbeat capability — write status JSON every round.
|
|
3
|
+
*
|
|
4
|
+
* Ported from ralph-watch.ps1 `Update-Heartbeat`.
|
|
5
|
+
* Writes to `.squad/ralph-heartbeat.json` every round with:
|
|
6
|
+
* - round number, status, PID, timestamp
|
|
7
|
+
* - consecutive failures counter
|
|
8
|
+
* - duration of last round
|
|
9
|
+
*
|
|
10
|
+
* Also maintains a structured log at `.squad/ralph-watch.log` with rotation.
|
|
11
|
+
*
|
|
12
|
+
* Runs in the `housekeeping` phase.
|
|
13
|
+
*
|
|
14
|
+
* Config (via squad.config.ts → watch.capabilities["heartbeat"]):
|
|
15
|
+
* heartbeatPath – override heartbeat file location (default: .squad/ralph-heartbeat.json)
|
|
16
|
+
* logPath – override log file location (default: .squad/ralph-watch.log)
|
|
17
|
+
* maxLogEntries – max log lines before rotation (default: 500)
|
|
18
|
+
* maxLogBytes – max log file size before rotation (default: 1MB)
|
|
19
|
+
*/
|
|
20
|
+
import type { WatchCapability, WatchContext, PreflightResult, CapabilityResult } from '../types.js';
|
|
21
|
+
export interface HeartbeatData {
|
|
22
|
+
lastRun: string;
|
|
23
|
+
lastHeartbeat: string;
|
|
24
|
+
round: number;
|
|
25
|
+
status: string;
|
|
26
|
+
exitCode: number;
|
|
27
|
+
durationSeconds: number;
|
|
28
|
+
consecutiveFailures: number;
|
|
29
|
+
pid: number;
|
|
30
|
+
}
|
|
31
|
+
export declare class HeartbeatCapability implements WatchCapability {
|
|
32
|
+
readonly name = "heartbeat";
|
|
33
|
+
readonly description = "Write heartbeat JSON and structured log every round";
|
|
34
|
+
readonly configShape: "object";
|
|
35
|
+
readonly requires: never[];
|
|
36
|
+
readonly phase: "housekeeping";
|
|
37
|
+
preflight(_context: WatchContext): Promise<PreflightResult>;
|
|
38
|
+
execute(context: WatchContext): Promise<CapabilityResult>;
|
|
39
|
+
}
|
|
40
|
+
/** Increment consecutive failure counter (called by the main loop on error). */
|
|
41
|
+
export declare function recordFailure(): void;
|
|
42
|
+
/** Reset consecutive failure counter (called by the main loop on success). */
|
|
43
|
+
export declare function recordSuccess(): void;
|
|
44
|
+
/** Get current consecutive failure count. */
|
|
45
|
+
export declare function getConsecutiveFailures(): number;
|
|
46
|
+
/** Mark round start time for duration tracking. */
|
|
47
|
+
export declare function markRoundStart(): void;
|
|
48
|
+
//# sourceMappingURL=heartbeat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/watch/capabilities/heartbeat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpG,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,MAAM,CAAC;CACb;AAMD,qBAAa,mBAAoB,YAAW,eAAe;IACzD,QAAQ,CAAC,IAAI,eAAe;IAC5B,QAAQ,CAAC,WAAW,yDAAyD;IAC7E,QAAQ,CAAC,WAAW,EAAG,QAAQ,CAAU;IACzC,QAAQ,CAAC,QAAQ,UAAM;IACvB,QAAQ,CAAC,KAAK,EAAG,cAAc,CAAU;IAEnC,SAAS,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAI3D,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA2ChE;AAED,gFAAgF;AAChF,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,IAAI,IAAI,CAEpC;AAED,6CAA6C;AAC7C,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,mDAAmD;AACnD,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
|