@wrongstack/tools 0.77.0 → 0.82.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/audit.d.ts +4 -4
- package/dist/audit.js +10 -2
- package/dist/audit.js.map +1 -1
- package/dist/{background-indexer-C70RD7LU.d.ts → background-indexer-DYm1FUxK.d.ts} +19 -19
- package/dist/bash.d.ts +4 -4
- package/dist/bash.js +18 -4
- package/dist/bash.js.map +1 -1
- package/dist/batch-tool-use.d.ts +4 -4
- package/dist/batch-tool-use.js.map +1 -1
- package/dist/builtin.js +173 -70
- package/dist/builtin.js.map +1 -1
- package/dist/circuit-breaker.d.ts +6 -6
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/codebase-index/index.d.ts +12 -12
- package/dist/codebase-index/index.js +79 -41
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/diff.d.ts +7 -7
- package/dist/diff.js.map +1 -1
- package/dist/document.d.ts +6 -6
- package/dist/document.js.map +1 -1
- package/dist/edit.d.ts +1 -1
- package/dist/edit.js.map +1 -1
- package/dist/exec.d.ts +3 -3
- package/dist/exec.js +15 -3
- package/dist/exec.js.map +1 -1
- package/dist/fetch.d.ts +1 -1
- package/dist/fetch.js +3 -1
- package/dist/fetch.js.map +1 -1
- package/dist/format.d.ts +4 -4
- package/dist/format.js +18 -4
- package/dist/format.js.map +1 -1
- package/dist/git.d.ts +10 -10
- package/dist/git.js +8 -2
- package/dist/git.js.map +1 -1
- package/dist/glob.d.ts +2 -2
- package/dist/glob.js.map +1 -1
- package/dist/grep.d.ts +6 -6
- package/dist/grep.js +10 -2
- package/dist/grep.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +173 -70
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +5 -5
- package/dist/install.js +18 -4
- package/dist/install.js.map +1 -1
- package/dist/json.d.ts +8 -8
- package/dist/json.js.map +1 -1
- package/dist/lint.d.ts +4 -4
- package/dist/lint.js +18 -4
- package/dist/lint.js.map +1 -1
- package/dist/logs.d.ts +8 -8
- package/dist/logs.js.map +1 -1
- package/dist/memory.d.ts +2 -2
- package/dist/memory.js.map +1 -1
- package/dist/mode.d.ts +3 -3
- package/dist/mode.js.map +1 -1
- package/dist/outdated.d.ts +4 -4
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +173 -70
- package/dist/pack.js.map +1 -1
- package/dist/patch.d.ts +3 -3
- package/dist/patch.js.map +1 -1
- package/dist/process-registry.d.ts +3 -3
- package/dist/process-registry.js +7 -1
- package/dist/process-registry.js.map +1 -1
- package/dist/read.d.ts +2 -2
- package/dist/read.js.map +1 -1
- package/dist/replace.d.ts +4 -4
- package/dist/replace.js +8 -2
- package/dist/replace.js.map +1 -1
- package/dist/scaffold.d.ts +2 -2
- package/dist/scaffold.js.map +1 -1
- package/dist/search.d.ts +2 -2
- package/dist/search.js +16 -8
- package/dist/search.js.map +1 -1
- package/dist/test.d.ts +7 -7
- package/dist/test.js +18 -4
- package/dist/test.js.map +1 -1
- package/dist/tool-help.d.ts +4 -4
- package/dist/tool-help.js.map +1 -1
- package/dist/tool-search.d.ts +5 -5
- package/dist/tool-search.js.map +1 -1
- package/dist/tool-use.d.ts +2 -2
- package/dist/tool-use.js.map +1 -1
- package/dist/tree.d.ts +7 -7
- package/dist/tree.js +10 -2
- package/dist/tree.js.map +1 -1
- package/dist/typecheck.d.ts +4 -4
- package/dist/typecheck.js +18 -4
- package/dist/typecheck.js.map +1 -1
- package/dist/write.d.ts +1 -1
- package/dist/write.js.map +1 -1
- package/package.json +2 -2
|
@@ -20,32 +20,32 @@ interface CircuitBreakerConfig {
|
|
|
20
20
|
* Consecutive failures before trip. Default: 5.
|
|
21
21
|
* A single success resets this counter to 0.
|
|
22
22
|
*/
|
|
23
|
-
maxConsecutiveFailures?: number;
|
|
23
|
+
maxConsecutiveFailures?: number | undefined;
|
|
24
24
|
/**
|
|
25
25
|
* Slow-call threshold in ms. A call that runs longer than this is
|
|
26
26
|
* counted as "slow". Default: 60_000 (1 minute).
|
|
27
27
|
*/
|
|
28
|
-
slowCallThresholdMs?: number;
|
|
28
|
+
slowCallThresholdMs?: number | undefined;
|
|
29
29
|
/**
|
|
30
30
|
* Max slow calls before trip (within the sliding window). Default: 3.
|
|
31
31
|
*/
|
|
32
|
-
maxSlowCalls?: number;
|
|
32
|
+
maxSlowCalls?: number | undefined;
|
|
33
33
|
/**
|
|
34
34
|
* Sliding window for rate-limit and slow-call counting, in ms.
|
|
35
35
|
* Default: 60_000 (1 minute).
|
|
36
36
|
*/
|
|
37
|
-
windowMs?: number;
|
|
37
|
+
windowMs?: number | undefined;
|
|
38
38
|
/**
|
|
39
39
|
* Max calls within the sliding window. Default: 30.
|
|
40
40
|
* Burst exceeding this trips the breaker immediately.
|
|
41
41
|
*/
|
|
42
|
-
maxCallsPerWindow?: number;
|
|
42
|
+
maxCallsPerWindow?: number | undefined;
|
|
43
43
|
/**
|
|
44
44
|
* Cooldown before auto-recovery attempt, in ms. Default: 30_000 (30s).
|
|
45
45
|
* After this the breaker enters "half-open" state and allows one call
|
|
46
46
|
* through to test whether the problem is resolved.
|
|
47
47
|
*/
|
|
48
|
-
cooldownMs?: number;
|
|
48
|
+
cooldownMs?: number | undefined;
|
|
49
49
|
}
|
|
50
50
|
interface CircuitBreakerSnapshot {
|
|
51
51
|
state: 'closed' | 'open' | 'half-open';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/circuit-breaker.ts"],"names":[],"mappings":";AA6DA,IAAM,gCAAA,GAAmC,CAAA;AACzC,IAAM,8BAAA,GAAiC,GAAA;AACvC,IAAM,sBAAA,GAAyB,CAAA;AAC/B,IAAM,iBAAA,GAAoB,GAAA;AAC1B,IAAM,4BAAA,GAA+B,EAAA;AACrC,IAAM,mBAAA,GAAsB,GAAA;AAarB,IAAM,iBAAN,MAAqB;AAAA,EACT,sBAAA;AAAA,EACA,mBAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,iBAAA;AAAA,EACA,UAAA;AAAA,EAET,KAAA,GAAsB,QAAA;AAAA,EACtB,mBAAA,GAAsB,CAAA;AAAA,EACtB,SAAuB,EAAC;AAAA,EACxB,aAAA,GAA+B,IAAA;AAAA,EAC/B,UAAA,GAA4B,IAAA;AAAA;AAAA,EAE5B,QAAA,GAA0B,IAAA;AAAA,EAElC,WAAA,CAAY,MAAA,GAA+B,EAAC,EAAG;AAC7C,IAAA,IAAA,CAAK,sBAAA,GAAyB,OAAO,sBAAA,IAA0B,gCAAA;AAC/D,IAAA,IAAA,CAAK,mBAAA,GAAsB,OAAO,mBAAA,IAAuB,8BAAA;AACzD,IAAA,IAAA,CAAK,YAAA,GAAe,OAAO,YAAA,IAAgB,sBAAA;AAC3C,IAAA,IAAA,CAAK,QAAA,GAAW,OAAO,QAAA,IAAY,iBAAA;AACnC,IAAA,IAAA,CAAK,iBAAA,GAAoB,OAAO,iBAAA,IAAqB,4BAAA;AACrD,IAAA,IAAA,CAAK,UAAA,GAAa,OAAO,UAAA,IAAc,mBAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,UAAA,GAAsB;AACxB,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAC3B,IAAA,OAAO,KAAK,KAAA,KAAU,MAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,GAAmC;AACjC,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAC3B,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,IAAI,iBAAA,GAAmC,IAAA;AACvC,IAAA,IAAI,IAAA,CAAK,QAAA,KAAa,IAAA,IAAQ,IAAA,CAAK,UAAU,MAAA,EAAQ;AACnD,MAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,QAAA;AAC3B,MAAA,iBAAA,GAAoB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,aAAa,OAAO,CAAA;AAAA,IAC3D;AACA,IAAA,OAAO;AAAA,MACL,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,qBAAqB,IAAA,CAAK,mBAAA;AAAA,MAC1B,iBAAA,EAAmB,KAAK,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA,CAAE,MAAA;AAAA,MACrD,aAAA,EAAe,KAAK,MAAA,CAAO,MAAA;AAAA,MAC3B,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,mBAAA,EAAqB,iBAAA;AAAA,MACrB,eAAe,IAAA,CAAK,aAAA;AAAA,MACpB,YAAY,IAAA,CAAK;AAAA,KACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAA,GAAsB;AACpB,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAC3B,IAAA,IAAI,IAAA,CAAK,KAAA,KAAU,MAAA,EAAQ,OAAO,KAAA;AAClC,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAA,CAAU,YAAoB,MAAA,EAAuB;AACnD,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAErB,IAAA,IAAI,IAAA,CAAK,UAAU,WAAA,EAAa;AAE9B,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,IAAA,CAAK,KAAA,EAAM;AACX,QAAA;AAAA,MACF;AAEA,MAAA,IAAA,CAAK,MAAA,EAAO;AACZ,MAAA;AAAA,IACF;AAGA,IAAA,IAAA,CAAK,aAAa,GAAG,CAAA;AAErB,IAAA,MAAM,IAAA,GAAO,cAAc,IAAA,CAAK,mBAAA;AAChC,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,EAAE,IAAI,GAAA,EAAK,MAAA,EAAQ,MAAM,CAAA;AAE1C,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,IAAA,CAAK,mBAAA,EAAA;AACL,MAAA,IAAA,CAAK,aAAA,GAAgB,GAAA;AACrB,MAAA,IAAI,IAAA,CAAK,mBAAA,IAAuB,IAAA,CAAK,sBAAA,EAAwB;AAC3D,QAAA,IAAA,CAAK,KAAA,EAAM;AAAA,MACb;AACA,MAAA;AAAA,IACF;AAGA,IAAA,IAAA,CAAK,mBAAA,GAAsB,CAAA;AAE3B,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,IAAA,CAAK,UAAA,GAAa,GAAA;AAClB,MAAA,MAAM,SAAA,GAAY,KAAK,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAA,CAAE,MAAA;AACpD,MAAA,IAAI,SAAA,IAAa,KAAK,YAAA,EAAc;AAClC,QAAA,IAAA,CAAK,KAAA,EAAM;AAAA,MACb;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,GAAY,KAAK,MAAA,CAAO,MAAA;AAC9B,IAAA,IAAI,SAAA,IAAa,KAAK,iBAAA,EAAmB;AAIvC,MAAA,IAAA,CAAK,KAAA,EAAM;AAAA,IACb;AAAA,EACF;AAAA;AAAA,EAGA,SAAA,GAAkB;AAChB,IAAA,IAAA,CAAK,KAAA,EAAM;AAAA,EACb;AAAA;AAAA,EAGA,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,MAAA,EAAO;AAAA,EACd;AAAA,EAEQ,KAAA,GAAc;AACpB,IAAA,IAAI,IAAA,CAAK,UAAU,MAAA,EAAQ;AAC3B,IAAA,IAAA,CAAK,KAAA,GAAQ,MAAA;AACb,IAAA,IAAA,CAAK,QAAA,GAAW,KAAK,GAAA,EAAI;AAAA,EAC3B;AAAA,EAEQ,MAAA,GAAe;AACrB,IAAA,IAAA,CAAK,KAAA,GAAQ,QAAA;AACb,IAAA,IAAA,CAAK,mBAAA,GAAsB,CAAA;AAC3B,IAAA,IAAA,CAAK,SAAS,EAAC;AACf,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,EAClB;AAAA;AAAA,EAGQ,qBAAA,GAA8B;AACpC,IAAA,IAAI,IAAA,CAAK,KAAA,KAAU,MAAA,IAAU,IAAA,CAAK,aAAa,IAAA,EAAM;AACrD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,EAAI,GAAI,IAAA,CAAK,QAAA;AAClC,IAAA,IAAI,OAAA,IAAW,KAAK,UAAA,EAAY;AAC9B,MAAA,IAAA,CAAK,KAAA,GAAQ,WAAA;AACb,MAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,IAClB;AAAA,EACF;AAAA,EAEQ,aAAa,GAAA,EAAmB;AACtC,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,QAAA;AAC1B,IAAA,IAAA,CAAK,MAAA,GAAS,KAAK,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,MAAM,MAAM,CAAA;AAAA,EACxD;AACF","file":"circuit-breaker.js","sourcesContent":["/**\n * CircuitBreaker — prevents runaway bash/exec tool chains by:\n *\n * - Tripping on consecutive failures (models that keep repeating the\n * same failing command, e.g. `npm install` with wrong args in a loop)\n * - Tripping on slow call ratio (too many long-running commands suggest\n * a hung subprocess that the model doesn't know how to kill)\n * - Rate-limiting bursts (rapid succession of commands without reading\n * output suggests the model isn't processing results)\n * - Auto-recovering after a cooldown period so a fixed model can resume\n *\n * The breaker is owned by the ProcessRegistry so any tool that registers\n * a process participates in the same circuit. \"Per-tool\" isolation is\n * intentionally NOT implemented — the model treats bash/exec as one\n * resource pool; isolating them would let the model route around the\n * breaker by alternating which tool it uses.\n */\n\nexport interface CircuitBreakerConfig {\n /**\n * Consecutive failures before trip. Default: 5.\n * A single success resets this counter to 0.\n */\n maxConsecutiveFailures?: number;\n /**\n * Slow-call threshold in ms. A call that runs longer than this is\n * counted as \"slow\". Default: 60_000 (1 minute).\n */\n slowCallThresholdMs?: number;\n /**\n * Max slow calls before trip (within the sliding window). Default: 3.\n */\n maxSlowCalls?: number;\n /**\n * Sliding window for rate-limit and slow-call counting, in ms.\n * Default: 60_000 (1 minute).\n */\n windowMs?: number;\n /**\n * Max calls within the sliding window. Default: 30.\n * Burst exceeding this trips the breaker immediately.\n */\n maxCallsPerWindow?: number;\n /**\n * Cooldown before auto-recovery attempt, in ms. Default: 30_000 (30s).\n * After this the breaker enters \"half-open\" state and allows one call\n * through to test whether the problem is resolved.\n */\n cooldownMs?: number;\n}\n\ninterface CallRecord {\n at: number;\n /** True if the call threw or returned an is_error result. */\n failed: boolean;\n /** True if elapsed time exceeded slowCallThresholdMs. */\n slow: boolean;\n}\n\ntype BreakerState = 'closed' | 'open' | 'half-open';\n\nconst DEFAULT_MAX_CONSECUTIVE_FAILURES = 5;\nconst DEFAULT_SLOW_CALL_THRESHOLD_MS = 60_000;\nconst DEFAULT_MAX_SLOW_CALLS = 3;\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_CALLS_PER_WINDOW = 30;\nconst DEFAULT_COOLDOWN_MS = 30_000;\n\nexport interface CircuitBreakerSnapshot {\n state: 'closed' | 'open' | 'half-open';\n consecutiveFailures: number;\n slowCallsInWindow: number;\n callsInWindow: number;\n windowMs: number;\n cooldownRemainingMs: number | null;\n lastFailureAt: number | null;\n lastSlowAt: number | null;\n}\n\nexport class CircuitBreaker {\n private readonly maxConsecutiveFailures: number;\n private readonly slowCallThresholdMs: number;\n private readonly maxSlowCalls: number;\n private readonly windowMs: number;\n private readonly maxCallsPerWindow: number;\n private readonly cooldownMs: number;\n\n private state: BreakerState = 'closed';\n private consecutiveFailures = 0;\n private window: CallRecord[] = [];\n private lastFailureAt: number | null = null;\n private lastSlowAt: number | null = null;\n /** Timestamp when the breaker was opened (for cooldown calculation). */\n private openedAt: number | null = null;\n\n constructor(config: CircuitBreakerConfig = {}) {\n this.maxConsecutiveFailures = config.maxConsecutiveFailures ?? DEFAULT_MAX_CONSECUTIVE_FAILURES;\n this.slowCallThresholdMs = config.slowCallThresholdMs ?? DEFAULT_SLOW_CALL_THRESHOLD_MS;\n this.maxSlowCalls = config.maxSlowCalls ?? DEFAULT_MAX_SLOW_CALLS;\n this.windowMs = config.windowMs ?? DEFAULT_WINDOW_MS;\n this.maxCallsPerWindow = config.maxCallsPerWindow ?? DEFAULT_MAX_CALLS_PER_WINDOW;\n this.cooldownMs = config.cooldownMs ?? DEFAULT_COOLDOWN_MS;\n }\n\n /**\n * Returns true if the circuit allows a new call to proceed.\n * When false, callers should abort the tool call and return a\n * circuit-breaker error instead of spawning a process.\n */\n get canProceed(): boolean {\n this._checkStateTransition();\n return this.state !== 'open';\n }\n\n /**\n * Snapshot of the current breaker state for observability (`/kill`).\n */\n snapshot(): CircuitBreakerSnapshot {\n this._checkStateTransition();\n const now = Date.now();\n let cooldownRemaining: number | null = null;\n if (this.openedAt !== null && this.state === 'open') {\n const elapsed = now - this.openedAt;\n cooldownRemaining = Math.max(0, this.cooldownMs - elapsed);\n }\n return {\n state: this.state,\n consecutiveFailures: this.consecutiveFailures,\n slowCallsInWindow: this.window.filter((c) => c.slow).length,\n callsInWindow: this.window.length,\n windowMs: this.windowMs,\n cooldownRemainingMs: cooldownRemaining,\n lastFailureAt: this.lastFailureAt,\n lastSlowAt: this.lastSlowAt,\n };\n }\n\n /**\n * Call this BEFORE spawning a bash/exec process.\n * Returns true if the call is allowed; false if the breaker is open.\n * When false, callers MUST NOT spawn a process.\n */\n beforeCall(): boolean {\n this._checkStateTransition();\n if (this.state === 'open') return false;\n return true;\n }\n\n /**\n * Call this AFTER a bash/exec process finishes (success or failure).\n * `durationMs` is the wall-clock time the process ran.\n * `failed` is true when the process returned a non-zero exit code or\n * threw an exception before spawning.\n */\n afterCall(durationMs: number, failed: boolean): void {\n const now = Date.now();\n\n if (this.state === 'half-open') {\n // First call through after cooldown — if it failed, go back to open.\n if (failed) {\n this._trip();\n return;\n }\n // Success in half-open → reset to closed.\n this._reset();\n return;\n }\n\n // Prune old records outside the sliding window.\n this._pruneWindow(now);\n\n const slow = durationMs >= this.slowCallThresholdMs;\n this.window.push({ at: now, failed, slow });\n\n if (failed) {\n this.consecutiveFailures++;\n this.lastFailureAt = now;\n if (this.consecutiveFailures >= this.maxConsecutiveFailures) {\n this._trip();\n }\n return;\n }\n\n // Success: reset consecutive failure counter.\n this.consecutiveFailures = 0;\n\n if (slow) {\n this.lastSlowAt = now;\n const slowCount = this.window.filter((c) => c.slow).length;\n if (slowCount >= this.maxSlowCalls) {\n this._trip();\n }\n }\n\n const callCount = this.window.length;\n if (callCount >= this.maxCallsPerWindow) {\n // Rate limit exceeded. This is a soft trip — we reset the window\n // and let the next call try immediately (the caller will still see\n // canProceed=false until the window drains naturally).\n this._trip();\n }\n }\n\n /** Force the breaker open. Used by /kill force and Ctrl+C. */\n forceOpen(): void {\n this._trip();\n }\n\n /** Force a reset to closed. Used by tests and /kill reset. */\n forceReset(): void {\n this._reset();\n }\n\n private _trip(): void {\n if (this.state === 'open') return; // already open\n this.state = 'open';\n this.openedAt = Date.now();\n }\n\n private _reset(): void {\n this.state = 'closed';\n this.consecutiveFailures = 0;\n this.window = [];\n this.openedAt = null;\n }\n\n /** Transition from open → half-open when cooldown elapses. */\n private _checkStateTransition(): void {\n if (this.state !== 'open' || this.openedAt === null) return;\n const elapsed = Date.now() - this.openedAt;\n if (elapsed >= this.cooldownMs) {\n this.state = 'half-open';\n this.openedAt = null;\n }\n }\n\n private _pruneWindow(now: number): void {\n const cutoff = now - this.windowMs;\n this.window = this.window.filter((c) => c.at >= cutoff);\n }\n}"]}
|
|
1
|
+
{"version":3,"sources":["../src/circuit-breaker.ts"],"names":[],"mappings":"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","file":"circuit-breaker.js","sourcesContent":["/**\n * CircuitBreaker — prevents runaway bash/exec tool chains by:\n *\n * - Tripping on consecutive failures (models that keep repeating the\n * same failing command, e.g. `npm install` with wrong args in a loop)\n * - Tripping on slow call ratio (too many long-running commands suggest\n * a hung subprocess that the model doesn't know how to kill)\n * - Rate-limiting bursts (rapid succession of commands without reading\n * output suggests the model isn't processing results)\n * - Auto-recovering after a cooldown period so a fixed model can resume\n *\n * The breaker is owned by the ProcessRegistry so any tool that registers\n * a process participates in the same circuit. \"Per-tool\" isolation is\n * intentionally NOT implemented — the model treats bash/exec as one\n * resource pool; isolating them would let the model route around the\n * breaker by alternating which tool it uses.\n */\n\nexport interface CircuitBreakerConfig {\n /**\n * Consecutive failures before trip. Default: 5.\n * A single success resets this counter to 0.\n */\n maxConsecutiveFailures?: number | undefined;\n /**\n * Slow-call threshold in ms. A call that runs longer than this is\n * counted as \"slow\". Default: 60_000 (1 minute).\n */\n slowCallThresholdMs?: number | undefined;\n /**\n * Max slow calls before trip (within the sliding window). Default: 3.\n */\n maxSlowCalls?: number | undefined;\n /**\n * Sliding window for rate-limit and slow-call counting, in ms.\n * Default: 60_000 (1 minute).\n */\n windowMs?: number | undefined;\n /**\n * Max calls within the sliding window. Default: 30.\n * Burst exceeding this trips the breaker immediately.\n */\n maxCallsPerWindow?: number | undefined;\n /**\n * Cooldown before auto-recovery attempt, in ms. Default: 30_000 (30s).\n * After this the breaker enters \"half-open\" state and allows one call\n * through to test whether the problem is resolved.\n */\n cooldownMs?: number | undefined;\n}\n\ninterface CallRecord {\n at: number;\n /** True if the call threw or returned an is_error result. */\n failed: boolean;\n /** True if elapsed time exceeded slowCallThresholdMs. */\n slow: boolean;\n}\n\ntype BreakerState = 'closed' | 'open' | 'half-open';\n\nconst DEFAULT_MAX_CONSECUTIVE_FAILURES = 5;\nconst DEFAULT_SLOW_CALL_THRESHOLD_MS = 60_000;\nconst DEFAULT_MAX_SLOW_CALLS = 3;\nconst DEFAULT_WINDOW_MS = 60_000;\nconst DEFAULT_MAX_CALLS_PER_WINDOW = 30;\nconst DEFAULT_COOLDOWN_MS = 30_000;\n\nexport interface CircuitBreakerSnapshot {\n state: 'closed' | 'open' | 'half-open';\n consecutiveFailures: number;\n slowCallsInWindow: number;\n callsInWindow: number;\n windowMs: number;\n cooldownRemainingMs: number | null;\n lastFailureAt: number | null;\n lastSlowAt: number | null;\n}\n\nexport class CircuitBreaker {\n private readonly maxConsecutiveFailures: number;\n private readonly slowCallThresholdMs: number;\n private readonly maxSlowCalls: number;\n private readonly windowMs: number;\n private readonly maxCallsPerWindow: number;\n private readonly cooldownMs: number;\n\n private state: BreakerState = 'closed';\n private consecutiveFailures = 0;\n private window: CallRecord[] = [];\n private lastFailureAt: number | null = null;\n private lastSlowAt: number | null = null;\n /** Timestamp when the breaker was opened (for cooldown calculation). */\n private openedAt: number | null = null;\n\n constructor(config: CircuitBreakerConfig = {}) {\n this.maxConsecutiveFailures = config.maxConsecutiveFailures ?? DEFAULT_MAX_CONSECUTIVE_FAILURES;\n this.slowCallThresholdMs = config.slowCallThresholdMs ?? DEFAULT_SLOW_CALL_THRESHOLD_MS;\n this.maxSlowCalls = config.maxSlowCalls ?? DEFAULT_MAX_SLOW_CALLS;\n this.windowMs = config.windowMs ?? DEFAULT_WINDOW_MS;\n this.maxCallsPerWindow = config.maxCallsPerWindow ?? DEFAULT_MAX_CALLS_PER_WINDOW;\n this.cooldownMs = config.cooldownMs ?? DEFAULT_COOLDOWN_MS;\n }\n\n /**\n * Returns true if the circuit allows a new call to proceed.\n * When false, callers should abort the tool call and return a\n * circuit-breaker error instead of spawning a process.\n */\n get canProceed(): boolean {\n this._checkStateTransition();\n return this.state !== 'open';\n }\n\n /**\n * Snapshot of the current breaker state for observability (`/kill`).\n */\n snapshot(): CircuitBreakerSnapshot {\n this._checkStateTransition();\n const now = Date.now();\n let cooldownRemaining: number | null = null;\n if (this.openedAt !== null && this.state === 'open') {\n const elapsed = now - this.openedAt;\n cooldownRemaining = Math.max(0, this.cooldownMs - elapsed);\n }\n return {\n state: this.state,\n consecutiveFailures: this.consecutiveFailures,\n slowCallsInWindow: this.window.filter((c) => c.slow).length,\n callsInWindow: this.window.length,\n windowMs: this.windowMs,\n cooldownRemainingMs: cooldownRemaining,\n lastFailureAt: this.lastFailureAt,\n lastSlowAt: this.lastSlowAt,\n };\n }\n\n /**\n * Call this BEFORE spawning a bash/exec process.\n * Returns true if the call is allowed; false if the breaker is open.\n * When false, callers MUST NOT spawn a process.\n */\n beforeCall(): boolean {\n this._checkStateTransition();\n if (this.state === 'open') return false;\n return true;\n }\n\n /**\n * Call this AFTER a bash/exec process finishes (success or failure).\n * `durationMs` is the wall-clock time the process ran.\n * `failed` is true when the process returned a non-zero exit code or\n * threw an exception before spawning.\n */\n afterCall(durationMs: number, failed: boolean): void {\n const now = Date.now();\n\n if (this.state === 'half-open') {\n // First call through after cooldown — if it failed, go back to open.\n if (failed) {\n this._trip();\n return;\n }\n // Success in half-open → reset to closed.\n this._reset();\n return;\n }\n\n // Prune old records outside the sliding window.\n this._pruneWindow(now);\n\n const slow = durationMs >= this.slowCallThresholdMs;\n this.window.push({ at: now, failed, slow });\n\n if (failed) {\n this.consecutiveFailures++;\n this.lastFailureAt = now;\n if (this.consecutiveFailures >= this.maxConsecutiveFailures) {\n this._trip();\n }\n return;\n }\n\n // Success: reset consecutive failure counter.\n this.consecutiveFailures = 0;\n\n if (slow) {\n this.lastSlowAt = now;\n const slowCount = this.window.filter((c) => c.slow).length;\n if (slowCount >= this.maxSlowCalls) {\n this._trip();\n }\n }\n\n const callCount = this.window.length;\n if (callCount >= this.maxCallsPerWindow) {\n // Rate limit exceeded. This is a soft trip — we reset the window\n // and let the next call try immediately (the caller will still see\n // canProceed=false until the window drains naturally).\n this._trip();\n }\n }\n\n /** Force the breaker open. Used by /kill force and Ctrl+C. */\n forceOpen(): void {\n this._trip();\n }\n\n /** Force a reset to closed. Used by tests and /kill reset. */\n forceReset(): void {\n this._reset();\n }\n\n private _trip(): void {\n if (this.state === 'open') return; // already open\n this.state = 'open';\n this.openedAt = Date.now();\n }\n\n private _reset(): void {\n this.state = 'closed';\n this.consecutiveFailures = 0;\n this.window = [];\n this.openedAt = null;\n }\n\n /** Transition from open → half-open when cooldown elapses. */\n private _checkStateTransition(): void {\n if (this.state !== 'open' || this.openedAt === null) return;\n const elapsed = Date.now() - this.openedAt;\n if (elapsed >= this.cooldownMs) {\n this.state = 'half-open';\n this.openedAt = null;\n }\n }\n\n private _pruneWindow(now: number): void {\n const cutoff = now - this.windowMs;\n this.window = this.window.filter((c) => c.at >= cutoff);\n }\n}"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as IndexResult, d as Symbol, F as FileMeta, e as SymbolKind, f as SymbolLang, c as SearchResult, b as IndexStats, R as Ref } from '../background-indexer-
|
|
2
|
-
export { a as FileSymbols, S as SCHEMA_VERSION, g as cancelPendingReindexes, h as codebaseIndexTool, i as codebaseSearchTool, j as codebaseStatsTool, k as enqueueReindex, l as getIndexState, m as isIndexReady, n as isIndexableFile, o as isIndexing, p as onIndexStateChange, r as runStartupIndex } from '../background-indexer-
|
|
1
|
+
import { I as IndexResult, d as Symbol, F as FileMeta, e as SymbolKind, f as SymbolLang, c as SearchResult, b as IndexStats, R as Ref } from '../background-indexer-DYm1FUxK.js';
|
|
2
|
+
export { a as FileSymbols, S as SCHEMA_VERSION, g as cancelPendingReindexes, h as codebaseIndexTool, i as codebaseSearchTool, j as codebaseStatsTool, k as enqueueReindex, l as getIndexState, m as isIndexReady, n as isIndexableFile, o as isIndexing, p as onIndexStateChange, r as runStartupIndex } from '../background-indexer-DYm1FUxK.js';
|
|
3
3
|
import { Context } from '@wrongstack/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -15,12 +15,12 @@ import { Context } from '@wrongstack/core';
|
|
|
15
15
|
|
|
16
16
|
interface IndexerOptions {
|
|
17
17
|
projectRoot: string;
|
|
18
|
-
files?: string[];
|
|
19
|
-
force?: boolean;
|
|
20
|
-
langs?: string[];
|
|
21
|
-
ignore?: string[];
|
|
18
|
+
files?: string[] | undefined;
|
|
19
|
+
force?: boolean | undefined;
|
|
20
|
+
langs?: string[] | undefined;
|
|
21
|
+
ignore?: string[] | undefined;
|
|
22
22
|
/** Override the index directory (default: the global per-project dir). */
|
|
23
|
-
indexDir?: string;
|
|
23
|
+
indexDir?: string | undefined;
|
|
24
24
|
}
|
|
25
25
|
/** Run a full or incremental index and return statistics. */
|
|
26
26
|
declare function runIndexer(_ctx: Context, opts: IndexerOptions): Promise<IndexResult>;
|
|
@@ -54,7 +54,7 @@ declare class IndexStore {
|
|
|
54
54
|
/** Absolute path to this project's index directory. */
|
|
55
55
|
private readonly indexDir;
|
|
56
56
|
constructor(projectRoot: string, opts?: {
|
|
57
|
-
indexDir?: string;
|
|
57
|
+
indexDir?: string | undefined;
|
|
58
58
|
});
|
|
59
59
|
private initSchema;
|
|
60
60
|
insertSymbols(symbols: Symbol[], nextId: number): number;
|
|
@@ -64,10 +64,10 @@ declare class IndexStore {
|
|
|
64
64
|
getFileMeta(file: string): FileMeta | null;
|
|
65
65
|
getAllFileMetas(): FileMeta[];
|
|
66
66
|
search(query: string, filter?: {
|
|
67
|
-
kind?: SymbolKind;
|
|
68
|
-
lang?: SymbolLang;
|
|
69
|
-
file?: string;
|
|
70
|
-
lspKind?: number;
|
|
67
|
+
kind?: SymbolKind | undefined;
|
|
68
|
+
lang?: SymbolLang | undefined;
|
|
69
|
+
file?: string | undefined;
|
|
70
|
+
lspKind?: number | undefined;
|
|
71
71
|
}): SearchResult[];
|
|
72
72
|
getAllIndexable(): Array<{
|
|
73
73
|
id: number;
|
|
@@ -81,6 +81,12 @@ function internalKindToLspKind(k) {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
// src/codebase-index/writer.ts
|
|
84
|
+
function expectDefined(value) {
|
|
85
|
+
if (value === null || value === void 0) {
|
|
86
|
+
throw new Error("Expected value to be defined");
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
84
90
|
var DB_FILE = "index.db";
|
|
85
91
|
function resolveIndexDir(projectRoot, override) {
|
|
86
92
|
return override ?? resolveWstackPaths({ projectRoot }).projectCodebaseIndex;
|
|
@@ -225,7 +231,7 @@ var IndexStore = class {
|
|
|
225
231
|
"SELECT file, lang, mtime_ms, symbol_count, last_indexed FROM files WHERE file = ?"
|
|
226
232
|
).all(file);
|
|
227
233
|
if (!rows.length) return null;
|
|
228
|
-
const r = rows[0];
|
|
234
|
+
const r = expectDefined(rows[0]);
|
|
229
235
|
return { file: r.file, lang: r.lang, mtimeMs: r.mtime_ms, symbolCount: r.symbol_count, lastIndexed: r.last_indexed };
|
|
230
236
|
}
|
|
231
237
|
getAllFileMetas() {
|
|
@@ -307,7 +313,7 @@ var IndexStore = class {
|
|
|
307
313
|
const lastRows = this.db.prepare(
|
|
308
314
|
"SELECT value FROM metadata WHERE key = 'last_indexed'"
|
|
309
315
|
).all();
|
|
310
|
-
const lastIndexed = lastRows.length ? Number(lastRows[0]
|
|
316
|
+
const lastIndexed = lastRows.length ? Number(lastRows[0]?.value) : null;
|
|
311
317
|
const totalRows = this.db.prepare("SELECT COUNT(*) FROM symbols").all();
|
|
312
318
|
const totalSymbols = totalRows[0] ? Number(totalRows[0]["COUNT(*)"]) : 0;
|
|
313
319
|
const fileRows = this.db.prepare("SELECT COUNT(*) FROM files").all();
|
|
@@ -382,8 +388,9 @@ var IndexStore = class {
|
|
|
382
388
|
let resolved = 0;
|
|
383
389
|
for (const row of unresolved) {
|
|
384
390
|
const target = this.db.prepare("SELECT id FROM symbols WHERE name = ? LIMIT 1").all(row.to_name);
|
|
385
|
-
|
|
386
|
-
|
|
391
|
+
const first = target[0];
|
|
392
|
+
if (first) {
|
|
393
|
+
this.db.prepare("UPDATE refs SET to_id = ? WHERE id = ?").run(first.id, row.id);
|
|
387
394
|
resolved++;
|
|
388
395
|
}
|
|
389
396
|
}
|
|
@@ -1137,6 +1144,12 @@ function syncPyParse(filePath, lang) {
|
|
|
1137
1144
|
return { file: filePath, lang, symbols: [], mtimeMs: Date.now() };
|
|
1138
1145
|
}
|
|
1139
1146
|
}
|
|
1147
|
+
function expectDefined2(value) {
|
|
1148
|
+
if (value === null || value === void 0) {
|
|
1149
|
+
throw new Error("Expected value to be defined");
|
|
1150
|
+
}
|
|
1151
|
+
return value;
|
|
1152
|
+
}
|
|
1140
1153
|
function parseSymbols4(opts) {
|
|
1141
1154
|
const { file, content, lang } = opts;
|
|
1142
1155
|
const nativeAvailable = checkNativeParser();
|
|
@@ -1218,14 +1231,14 @@ function regexParse(opts) {
|
|
|
1218
1231
|
const lines = content.split("\n");
|
|
1219
1232
|
const lineOffsets = [0];
|
|
1220
1233
|
for (let i = 0; i < lines.length; i++) {
|
|
1221
|
-
lineOffsets.push(lineOffsets[i] + lines[i]
|
|
1234
|
+
lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
|
|
1222
1235
|
}
|
|
1223
1236
|
function lineFromOffset(offset) {
|
|
1224
1237
|
let lo = 0;
|
|
1225
1238
|
let hi = lineOffsets.length - 1;
|
|
1226
1239
|
while (lo < hi) {
|
|
1227
1240
|
const mid = lo + hi + 1 >>> 1;
|
|
1228
|
-
if (lineOffsets[mid] <= offset) lo = mid;
|
|
1241
|
+
if (expectDefined2(lineOffsets[mid]) <= offset) lo = mid;
|
|
1229
1242
|
else hi = mid - 1;
|
|
1230
1243
|
}
|
|
1231
1244
|
return lo + 1;
|
|
@@ -1237,8 +1250,8 @@ function regexParse(opts) {
|
|
|
1237
1250
|
for (const pattern of RS_PATTERNS) {
|
|
1238
1251
|
pattern.regex.lastIndex = 0;
|
|
1239
1252
|
for (let match = pattern.regex.exec(content); match !== null; match = pattern.regex.exec(content)) {
|
|
1240
|
-
const name = match[1];
|
|
1241
|
-
const offset = match.index;
|
|
1253
|
+
const name = expectDefined2(match[1]);
|
|
1254
|
+
const offset = match.index ?? 0;
|
|
1242
1255
|
const line = lineFromOffset(offset);
|
|
1243
1256
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1244
1257
|
const lineIdx = line - 1;
|
|
@@ -1267,6 +1280,12 @@ function regexParse(opts) {
|
|
|
1267
1280
|
});
|
|
1268
1281
|
return { file, lang, symbols: deduped, mtimeMs: Date.now() };
|
|
1269
1282
|
}
|
|
1283
|
+
function expectDefined3(value) {
|
|
1284
|
+
if (value === null || value === void 0) {
|
|
1285
|
+
throw new Error("Expected value to be defined");
|
|
1286
|
+
}
|
|
1287
|
+
return value;
|
|
1288
|
+
}
|
|
1270
1289
|
function parseSymbols5(opts) {
|
|
1271
1290
|
const { file, content, lang } = opts;
|
|
1272
1291
|
try {
|
|
@@ -1286,21 +1305,21 @@ function regexParse2(opts) {
|
|
|
1286
1305
|
const lines = content.split("\n");
|
|
1287
1306
|
const lineOffsets = [0];
|
|
1288
1307
|
for (let i = 0; i < lines.length; i++) {
|
|
1289
|
-
lineOffsets.push(lineOffsets[i] + lines[i]
|
|
1308
|
+
lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
|
|
1290
1309
|
}
|
|
1291
1310
|
function lineFromOffset(offset) {
|
|
1292
1311
|
let lo = 0;
|
|
1293
1312
|
let hi = lineOffsets.length - 1;
|
|
1294
1313
|
while (lo < hi) {
|
|
1295
1314
|
const mid = lo + hi + 1 >>> 1;
|
|
1296
|
-
if (lineOffsets[mid] <= offset) lo = mid;
|
|
1315
|
+
if (expectDefined3(lineOffsets[mid]) <= offset) lo = mid;
|
|
1297
1316
|
else hi = mid - 1;
|
|
1298
1317
|
}
|
|
1299
1318
|
return lo + 1;
|
|
1300
1319
|
}
|
|
1301
1320
|
const rootMatch = content.match(/^\s*\{/m);
|
|
1302
1321
|
if (rootMatch) {
|
|
1303
|
-
const offset = rootMatch.index;
|
|
1322
|
+
const offset = expectDefined3(rootMatch.index);
|
|
1304
1323
|
const line = lineFromOffset(offset);
|
|
1305
1324
|
symbols.push(
|
|
1306
1325
|
makeSymbol({
|
|
@@ -1316,8 +1335,8 @@ function regexParse2(opts) {
|
|
|
1316
1335
|
}
|
|
1317
1336
|
const topLevelKeyRegex = /^\s*"([^"]+)"\s*:/gm;
|
|
1318
1337
|
for (let match = topLevelKeyRegex.exec(content); match !== null; match = topLevelKeyRegex.exec(content)) {
|
|
1319
|
-
const key = match[1];
|
|
1320
|
-
const offset = match.index;
|
|
1338
|
+
const key = expectDefined3(match[1]);
|
|
1339
|
+
const offset = match.index ?? 0;
|
|
1321
1340
|
const line = lineFromOffset(offset);
|
|
1322
1341
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1323
1342
|
let kind = "property";
|
|
@@ -1363,7 +1382,7 @@ function regexParse2(opts) {
|
|
|
1363
1382
|
const defsRegex = /"\$defs"\s*:|"\$defs"\s*:/g;
|
|
1364
1383
|
const defsMatch = defsRegex.exec(content);
|
|
1365
1384
|
if (defsMatch !== null) {
|
|
1366
|
-
const offset = defsMatch.index;
|
|
1385
|
+
const offset = expectDefined3(defsMatch.index);
|
|
1367
1386
|
const line = lineFromOffset(offset);
|
|
1368
1387
|
symbols.push(
|
|
1369
1388
|
makeSymbol({
|
|
@@ -1386,9 +1405,9 @@ function regexParse2(opts) {
|
|
|
1386
1405
|
for (const pat of defsPatterns) {
|
|
1387
1406
|
pat.lastIndex = 0;
|
|
1388
1407
|
for (let match = pat.exec(content); match !== null; match = pat.exec(content)) {
|
|
1389
|
-
const offset = match.index;
|
|
1408
|
+
const offset = match.index ?? 0;
|
|
1390
1409
|
const line = lineFromOffset(offset);
|
|
1391
|
-
const key = match[0]
|
|
1410
|
+
const key = match[0]?.match(/"([^"]+)"/)?.[1] ?? expectDefined3(match[0]);
|
|
1392
1411
|
symbols.push(
|
|
1393
1412
|
makeSymbol({
|
|
1394
1413
|
name: key,
|
|
@@ -1407,12 +1426,12 @@ function regexParse2(opts) {
|
|
|
1407
1426
|
function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFromOffset) {
|
|
1408
1427
|
const scriptsBlockRegex = /"scripts"\s*:\s*\{([^}]+)\}/g;
|
|
1409
1428
|
for (let match = scriptsBlockRegex.exec(content); match !== null; match = scriptsBlockRegex.exec(content)) {
|
|
1410
|
-
const blockContent = match[0];
|
|
1411
|
-
const blockOffset = match.index;
|
|
1429
|
+
const blockContent = expectDefined3(match[0]);
|
|
1430
|
+
const blockOffset = match.index ?? 0;
|
|
1412
1431
|
const scriptKeyRegex = /"(\w[\w-]*)"\s*:/g;
|
|
1413
1432
|
for (let scriptMatch = scriptKeyRegex.exec(blockContent); scriptMatch !== null; scriptMatch = scriptKeyRegex.exec(blockContent)) {
|
|
1414
|
-
const key = scriptMatch[1];
|
|
1415
|
-
const keyOffset = blockOffset + scriptMatch.index;
|
|
1433
|
+
const key = expectDefined3(scriptMatch[1]);
|
|
1434
|
+
const keyOffset = blockOffset + expectDefined3(scriptMatch.index);
|
|
1416
1435
|
const line = lineFromOffset(keyOffset);
|
|
1417
1436
|
symbols.push(
|
|
1418
1437
|
makeSymbol({
|
|
@@ -1431,12 +1450,12 @@ function extractPackageScripts(content, symbols, file, lang, lineOffsets, lineFr
|
|
|
1431
1450
|
function extractCompilerOptions(content, symbols, file, lang, lineOffsets, parentLine, lineFromOffset) {
|
|
1432
1451
|
const optsBlockRegex = /"compilerOptions"\s*:\s*\{([^}]+)\}/g;
|
|
1433
1452
|
for (let match = optsBlockRegex.exec(content); match !== null; match = optsBlockRegex.exec(content)) {
|
|
1434
|
-
const blockContent = match[0];
|
|
1435
|
-
const blockOffset = match.index;
|
|
1453
|
+
const blockContent = expectDefined3(match[0]);
|
|
1454
|
+
const blockOffset = match.index ?? 0;
|
|
1436
1455
|
const optKeyRegex = /"(\w[\w]*)"\s*:/g;
|
|
1437
1456
|
for (let optMatch = optKeyRegex.exec(blockContent); optMatch !== null; optMatch = optKeyRegex.exec(blockContent)) {
|
|
1438
|
-
const key = optMatch[1];
|
|
1439
|
-
const keyOffset = blockOffset + optMatch.index;
|
|
1457
|
+
const key = expectDefined3(optMatch[1]);
|
|
1458
|
+
const keyOffset = blockOffset + expectDefined3(optMatch.index);
|
|
1440
1459
|
const line = lineFromOffset(keyOffset);
|
|
1441
1460
|
if (line <= parentLine) continue;
|
|
1442
1461
|
symbols.push(
|
|
@@ -1470,6 +1489,12 @@ function makeSymbol(opts) {
|
|
|
1470
1489
|
}
|
|
1471
1490
|
|
|
1472
1491
|
// src/codebase-index/yaml-parser.ts
|
|
1492
|
+
function expectDefined4(value) {
|
|
1493
|
+
if (value === null || value === void 0) {
|
|
1494
|
+
throw new Error("Expected value to be defined");
|
|
1495
|
+
}
|
|
1496
|
+
return value;
|
|
1497
|
+
}
|
|
1473
1498
|
function parseSymbols6(opts) {
|
|
1474
1499
|
const { file, content, lang } = opts;
|
|
1475
1500
|
try {
|
|
@@ -1484,22 +1509,22 @@ function regexParse3(opts) {
|
|
|
1484
1509
|
const lines = content.split("\n");
|
|
1485
1510
|
const lineOffsets = [0];
|
|
1486
1511
|
for (let i = 0; i < lines.length; i++) {
|
|
1487
|
-
lineOffsets.push(lineOffsets[i] + lines[i]
|
|
1512
|
+
lineOffsets.push((lineOffsets[i] ?? 0) + (lines[i]?.length ?? 0) + 1);
|
|
1488
1513
|
}
|
|
1489
1514
|
function lineFromOffset(offset) {
|
|
1490
1515
|
let lo = 0;
|
|
1491
1516
|
let hi = lineOffsets.length - 1;
|
|
1492
1517
|
while (lo < hi) {
|
|
1493
1518
|
const mid = lo + hi + 1 >>> 1;
|
|
1494
|
-
if (lineOffsets[mid] <= offset) lo = mid;
|
|
1519
|
+
if (expectDefined4(lineOffsets[mid]) <= offset) lo = mid;
|
|
1495
1520
|
else hi = mid - 1;
|
|
1496
1521
|
}
|
|
1497
1522
|
return lo + 1;
|
|
1498
1523
|
}
|
|
1499
1524
|
const anchorRegex = /&(\w[\w-]*)/g;
|
|
1500
1525
|
for (let match = anchorRegex.exec(content); match !== null; match = anchorRegex.exec(content)) {
|
|
1501
|
-
const name = match[1];
|
|
1502
|
-
const offset = match.index;
|
|
1526
|
+
const name = expectDefined4(match[1]);
|
|
1527
|
+
const offset = match.index ?? 0;
|
|
1503
1528
|
const line = lineFromOffset(offset);
|
|
1504
1529
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1505
1530
|
symbols.push(
|
|
@@ -1516,8 +1541,8 @@ function regexParse3(opts) {
|
|
|
1516
1541
|
}
|
|
1517
1542
|
const aliasRegex = /\*(\w[\w-]*)/g;
|
|
1518
1543
|
for (let match = aliasRegex.exec(content); match !== null; match = aliasRegex.exec(content)) {
|
|
1519
|
-
const name = match[1];
|
|
1520
|
-
const offset = match.index;
|
|
1544
|
+
const name = expectDefined4(match[1]);
|
|
1545
|
+
const offset = match.index ?? 0;
|
|
1521
1546
|
const line = lineFromOffset(offset);
|
|
1522
1547
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1523
1548
|
symbols.push(
|
|
@@ -1534,27 +1559,28 @@ function regexParse3(opts) {
|
|
|
1534
1559
|
}
|
|
1535
1560
|
const kvRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:/gm;
|
|
1536
1561
|
for (let match = kvRegex.exec(content); match !== null; match = kvRegex.exec(content)) {
|
|
1537
|
-
const indent = match[1]
|
|
1562
|
+
const indent = match[1]?.length ?? 0;
|
|
1538
1563
|
const key = match[2];
|
|
1539
|
-
|
|
1564
|
+
if (!key) continue;
|
|
1565
|
+
const offset = match.index ?? 0;
|
|
1540
1566
|
const line = lineFromOffset(offset);
|
|
1541
1567
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1542
1568
|
const lineContent = lines[line - 1] ?? "";
|
|
1543
1569
|
if (/^[|&>]/.test(lineContent.trim())) continue;
|
|
1544
1570
|
if (key === "---" || key === "...") continue;
|
|
1545
1571
|
if (indent > 12) continue;
|
|
1546
|
-
const value = extractValue(content, match.index);
|
|
1572
|
+
const value = extractValue(content, match.index ?? 0);
|
|
1547
1573
|
const kind = isScalar(value) ? "literal" : "property";
|
|
1548
1574
|
const signature = `${key}: ${truncate(value, 60)}`;
|
|
1549
1575
|
symbols.push(makeSymbol2({ name: key, kind, line, col, signature, file, lang }));
|
|
1550
1576
|
}
|
|
1551
1577
|
const listItemRegex = /^-(\s+)([^:#\s][^:#\s]*)\s*:/gm;
|
|
1552
1578
|
for (let match = listItemRegex.exec(content); match !== null; match = listItemRegex.exec(content)) {
|
|
1553
|
-
const key = match[2];
|
|
1554
|
-
const offset = match.index;
|
|
1579
|
+
const key = expectDefined4(match[2]);
|
|
1580
|
+
const offset = match.index ?? 0;
|
|
1555
1581
|
const line = lineFromOffset(offset);
|
|
1556
1582
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1557
|
-
const value = extractValue(content, offset + match[0]
|
|
1583
|
+
const value = extractValue(content, offset + match[0]?.length);
|
|
1558
1584
|
const kind = isScalar(value) ? "literal" : "property";
|
|
1559
1585
|
symbols.push(
|
|
1560
1586
|
makeSymbol2({
|
|
@@ -1570,8 +1596,8 @@ function regexParse3(opts) {
|
|
|
1570
1596
|
}
|
|
1571
1597
|
const blockScalarRegex = /^(\s*)([^:#\s][^:#\s]*)\s*:\s*[|>](\s|$)/gm;
|
|
1572
1598
|
for (let match = blockScalarRegex.exec(content); match !== null; match = blockScalarRegex.exec(content)) {
|
|
1573
|
-
const key = match[2];
|
|
1574
|
-
const offset = match.index;
|
|
1599
|
+
const key = expectDefined4(match[2]);
|
|
1600
|
+
const offset = match.index ?? 0;
|
|
1575
1601
|
const line = lineFromOffset(offset);
|
|
1576
1602
|
const col = offset - (lineOffsets[line - 1] ?? 0);
|
|
1577
1603
|
symbols.push(
|
|
@@ -1790,6 +1816,12 @@ function cancelPendingReindexes() {
|
|
|
1790
1816
|
}
|
|
1791
1817
|
|
|
1792
1818
|
// src/codebase-index/indexer.ts
|
|
1819
|
+
function expectDefined5(value) {
|
|
1820
|
+
if (value === null || value === void 0) {
|
|
1821
|
+
throw new Error("Expected value to be defined");
|
|
1822
|
+
}
|
|
1823
|
+
return value;
|
|
1824
|
+
}
|
|
1793
1825
|
var YIELD_EVERY_N = 50;
|
|
1794
1826
|
function yieldEventLoop() {
|
|
1795
1827
|
return new Promise((resolve2) => setImmediate(resolve2));
|
|
@@ -1898,7 +1930,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
1898
1930
|
for (const meta of store.getAllFileMetas()) existingMeta.set(meta.file, meta);
|
|
1899
1931
|
}
|
|
1900
1932
|
for (let fi = 0; fi < files.length; fi++) {
|
|
1901
|
-
const file = files[fi];
|
|
1933
|
+
const file = expectDefined5(files[fi]);
|
|
1902
1934
|
_setIndexProgress(fi + 1, files.length);
|
|
1903
1935
|
if (fi > 0 && fi % YIELD_EVERY_N === 0) {
|
|
1904
1936
|
await yieldEventLoop();
|
|
@@ -1955,7 +1987,7 @@ async function runIndexer(_ctx, opts) {
|
|
|
1955
1987
|
langStats[lang] = (langStats[lang] ?? 0) + count;
|
|
1956
1988
|
if (parsed.refs && parsed.refs.length > 0) {
|
|
1957
1989
|
for (let i = 0; i < symbolsWithIds.length; i++) {
|
|
1958
|
-
const sym = symbolsWithIds[i];
|
|
1990
|
+
const sym = expectDefined5(symbolsWithIds[i]);
|
|
1959
1991
|
const symRefs = parsed.refs.filter((r) => r.line === sym.line);
|
|
1960
1992
|
if (symRefs.length > 0) {
|
|
1961
1993
|
const refsWithFromId = symRefs.map((r) => ({ ...r, fromId: sym.id }));
|
|
@@ -2126,6 +2158,12 @@ var Bm25Index = class {
|
|
|
2126
2158
|
};
|
|
2127
2159
|
|
|
2128
2160
|
// src/codebase-index/codebase-search-tool.ts
|
|
2161
|
+
function expectDefined6(value) {
|
|
2162
|
+
if (value === null || value === void 0) {
|
|
2163
|
+
throw new Error("Expected value to be defined");
|
|
2164
|
+
}
|
|
2165
|
+
return value;
|
|
2166
|
+
}
|
|
2129
2167
|
var codebaseSearchTool = {
|
|
2130
2168
|
name: "codebase-search",
|
|
2131
2169
|
category: "Project",
|
|
@@ -2215,7 +2253,7 @@ var codebaseSearchTool = {
|
|
|
2215
2253
|
const top = scored.slice(0, limit);
|
|
2216
2254
|
const qTokens = tokenise(input.query);
|
|
2217
2255
|
const results = top.map(({ id, score }) => {
|
|
2218
|
-
const c = candidates.find((c2) => c2.id === id);
|
|
2256
|
+
const c = expectDefined6(candidates.find((c2) => c2.id === id));
|
|
2219
2257
|
const snippet = bm25.extractSnippet(id, qTokens);
|
|
2220
2258
|
return {
|
|
2221
2259
|
...c,
|