truecourse 0.1.13 → 0.1.15

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/server.mjs CHANGED
@@ -97,7 +97,7 @@ var require_depd = __commonJS({
97
97
  var site = callSiteLocation(stack[1]);
98
98
  var file2 = site[0];
99
99
  function deprecate(message) {
100
- log.call(deprecate, message);
100
+ log3.call(deprecate, message);
101
101
  }
102
102
  deprecate._file = file2;
103
103
  deprecate._ignored = isignored(namespace);
@@ -126,7 +126,7 @@ var require_depd = __commonJS({
126
126
  var str = process.env.TRACE_DEPRECATION || "";
127
127
  return containsNamespace(str, namespace);
128
128
  }
129
- function log(message, site) {
129
+ function log3(message, site) {
130
130
  var haslisteners = eehaslisteners(process, "deprecation");
131
131
  if (!haslisteners && this._ignored) {
132
132
  return;
@@ -266,7 +266,7 @@ var require_depd = __commonJS({
266
266
  "message",
267
267
  "site",
268
268
  '"use strict"\nreturn function (' + args + ") {log.call(deprecate, message, site)\nreturn fn.apply(this, arguments)\n}"
269
- )(fn, log, this, message, site);
269
+ )(fn, log3, this, message, site);
270
270
  return deprecatedfn;
271
271
  }
272
272
  function wrapproperty(obj, prop, message) {
@@ -291,13 +291,13 @@ var require_depd = __commonJS({
291
291
  var set2 = descriptor.set;
292
292
  if (typeof get === "function") {
293
293
  descriptor.get = function getter() {
294
- log.call(deprecate, message, site);
294
+ log3.call(deprecate, message, site);
295
295
  return get.apply(this, arguments);
296
296
  };
297
297
  }
298
298
  if (typeof set2 === "function") {
299
299
  descriptor.set = function setter() {
300
- log.call(deprecate, message, site);
300
+ log3.call(deprecate, message, site);
301
301
  return set2.apply(this, arguments);
302
302
  };
303
303
  }
@@ -1134,7 +1134,7 @@ var require_debug = __commonJS({
1134
1134
  var require_browser = __commonJS({
1135
1135
  "node_modules/.pnpm/debug@2.6.9/node_modules/debug/src/browser.js"(exports2, module2) {
1136
1136
  exports2 = module2.exports = require_debug();
1137
- exports2.log = log;
1137
+ exports2.log = log3;
1138
1138
  exports2.formatArgs = formatArgs;
1139
1139
  exports2.save = save;
1140
1140
  exports2.load = load;
@@ -1182,7 +1182,7 @@ var require_browser = __commonJS({
1182
1182
  });
1183
1183
  args.splice(lastC, 0, c);
1184
1184
  }
1185
- function log() {
1185
+ function log3() {
1186
1186
  return "object" === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments);
1187
1187
  }
1188
1188
  function save(namespaces) {
@@ -1223,7 +1223,7 @@ var require_node = __commonJS({
1223
1223
  var util2 = __require("util");
1224
1224
  exports2 = module2.exports = require_debug();
1225
1225
  exports2.init = init;
1226
- exports2.log = log;
1226
+ exports2.log = log3;
1227
1227
  exports2.formatArgs = formatArgs;
1228
1228
  exports2.save = save;
1229
1229
  exports2.load = load;
@@ -1274,7 +1274,7 @@ var require_node = __commonJS({
1274
1274
  args[0] = (/* @__PURE__ */ new Date()).toUTCString() + " " + name21 + " " + args[0];
1275
1275
  }
1276
1276
  }
1277
- function log() {
1277
+ function log3() {
1278
1278
  return stream.write(util2.format.apply(util2, arguments) + "\n");
1279
1279
  }
1280
1280
  function save(namespaces) {
@@ -32859,7 +32859,7 @@ var require_node2 = __commonJS({
32859
32859
  var tty = __require("tty");
32860
32860
  var util2 = __require("util");
32861
32861
  exports2.init = init;
32862
- exports2.log = log;
32862
+ exports2.log = log3;
32863
32863
  exports2.formatArgs = formatArgs;
32864
32864
  exports2.save = save;
32865
32865
  exports2.load = load;
@@ -32994,7 +32994,7 @@ var require_node2 = __commonJS({
32994
32994
  }
32995
32995
  return (/* @__PURE__ */ new Date()).toISOString() + " ";
32996
32996
  }
32997
- function log(...args) {
32997
+ function log3(...args) {
32998
32998
  return process.stderr.write(util2.formatWithOptions(exports2.inspectOpts, ...args) + "\n");
32999
32999
  }
33000
33000
  function save(namespaces) {
@@ -48420,27 +48420,27 @@ var require_src3 = __commonJS({
48420
48420
  Object.defineProperty(exports2, "__esModule", { value: true });
48421
48421
  var fs_1 = __require("fs");
48422
48422
  var debug_1 = __importDefault(require_src2());
48423
- var log = debug_1.default("@kwsites/file-exists");
48423
+ var log3 = debug_1.default("@kwsites/file-exists");
48424
48424
  function check2(path10, isFile, isDirectory) {
48425
- log(`checking %s`, path10);
48425
+ log3(`checking %s`, path10);
48426
48426
  try {
48427
48427
  const stat = fs_1.statSync(path10);
48428
48428
  if (stat.isFile() && isFile) {
48429
- log(`[OK] path represents a file`);
48429
+ log3(`[OK] path represents a file`);
48430
48430
  return true;
48431
48431
  }
48432
48432
  if (stat.isDirectory() && isDirectory) {
48433
- log(`[OK] path represents a directory`);
48433
+ log3(`[OK] path represents a directory`);
48434
48434
  return true;
48435
48435
  }
48436
- log(`[FAIL] path represents something other than a file or directory`);
48436
+ log3(`[FAIL] path represents something other than a file or directory`);
48437
48437
  return false;
48438
48438
  } catch (e) {
48439
48439
  if (e.code === "ENOENT") {
48440
- log(`[FAIL] path is not accessible: %o`, e);
48440
+ log3(`[FAIL] path is not accessible: %o`, e);
48441
48441
  return false;
48442
48442
  }
48443
- log(`[FATAL] %o`, e);
48443
+ log3(`[FATAL] %o`, e);
48444
48444
  throw e;
48445
48445
  }
48446
48446
  }
@@ -106511,6 +106511,52 @@ var init_esm5 = __esm({
106511
106511
  }
106512
106512
  });
106513
106513
 
106514
+ // apps/server/src/services/analysis-registry.ts
106515
+ function registerAnalysis(repoId, analysisId) {
106516
+ const existing = activeAnalyses2.get(repoId);
106517
+ if (existing) {
106518
+ existing.abortController.abort();
106519
+ for (const child of existing.childProcesses) {
106520
+ if (!child.killed) child.kill("SIGTERM");
106521
+ }
106522
+ }
106523
+ const abortController = new AbortController();
106524
+ activeAnalyses2.set(repoId, {
106525
+ analysisId,
106526
+ abortController,
106527
+ childProcesses: /* @__PURE__ */ new Set()
106528
+ });
106529
+ return abortController;
106530
+ }
106531
+ function registerChildProcess(repoId, child) {
106532
+ const entry = activeAnalyses2.get(repoId);
106533
+ if (entry) entry.childProcesses.add(child);
106534
+ }
106535
+ function unregisterChildProcess(repoId, child) {
106536
+ const entry = activeAnalyses2.get(repoId);
106537
+ if (entry) entry.childProcesses.delete(child);
106538
+ }
106539
+ function cancelAnalysis(repoId) {
106540
+ const entry = activeAnalyses2.get(repoId);
106541
+ if (!entry) return false;
106542
+ entry.abortController.abort();
106543
+ for (const child of entry.childProcesses) {
106544
+ if (!child.killed) child.kill("SIGTERM");
106545
+ }
106546
+ activeAnalyses2.delete(repoId);
106547
+ return true;
106548
+ }
106549
+ function unregisterAnalysis(repoId) {
106550
+ activeAnalyses2.delete(repoId);
106551
+ }
106552
+ var activeAnalyses2;
106553
+ var init_analysis_registry = __esm({
106554
+ "apps/server/src/services/analysis-registry.ts"() {
106555
+ "use strict";
106556
+ activeAnalyses2 = /* @__PURE__ */ new Map();
106557
+ }
106558
+ });
106559
+
106514
106560
  // node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Options.js
106515
106561
  var ignoreOverride2, defaultOptions3, getDefaultOptions2;
106516
106562
  var init_Options = __esm({
@@ -108552,7 +108598,7 @@ function assert2(truthyValue, message) {
108552
108598
  function removeTrailingSlash(url2) {
108553
108599
  return url2?.replace(/\/+$/, "");
108554
108600
  }
108555
- async function retriable(fn, props = {}, log) {
108601
+ async function retriable(fn, props = {}, log3) {
108556
108602
  const {
108557
108603
  retryCount = 3,
108558
108604
  retryDelay = 5e3,
@@ -108562,7 +108608,7 @@ async function retriable(fn, props = {}, log) {
108562
108608
  for (let i = 0; i < retryCount + 1; i++) {
108563
108609
  if (i > 0) {
108564
108610
  await new Promise((resolve6) => setTimeout(resolve6, retryDelay));
108565
- log(`Retrying ${i + 1} of ${retryCount + 1}`);
108611
+ log3(`Retrying ${i + 1} of ${retryCount + 1}`);
108566
108612
  }
108567
108613
  try {
108568
108614
  const res = await fn();
@@ -108572,7 +108618,7 @@ async function retriable(fn, props = {}, log) {
108572
108618
  if (!retryCheck(e)) {
108573
108619
  throw e;
108574
108620
  }
108575
- log(`Retriable error: ${JSON.stringify(e)}`);
108621
+ log3(`Retriable error: ${JSON.stringify(e)}`);
108576
108622
  }
108577
108623
  }
108578
108624
  throw lastError;
@@ -112835,11 +112881,16 @@ import { spawn as spawn2 } from "node:child_process";
112835
112881
  import { mkdirSync, writeFileSync } from "node:fs";
112836
112882
  import { join as join6 } from "node:path";
112837
112883
  import { tmpdir } from "node:os";
112884
+ function log(msg) {
112885
+ process.stderr.write(`${msg}
112886
+ `);
112887
+ }
112838
112888
  var BaseCLIProvider, ClaudeCodeProvider;
112839
112889
  var init_cli_provider = __esm({
112840
112890
  "apps/server/src/services/llm/cli-provider.ts"() {
112841
112891
  "use strict";
112842
112892
  init_esm5();
112893
+ init_analysis_registry();
112843
112894
  init_esm6();
112844
112895
  init_config();
112845
112896
  init_prompts();
@@ -112850,11 +112901,20 @@ var init_cli_provider = __esm({
112850
112901
  debugDir = null;
112851
112902
  callCounter = 0;
112852
112903
  _analysisId = null;
112904
+ _repoId = null;
112905
+ _abortSignal = null;
112853
112906
  _usageRecords = [];
112854
112907
  setAnalysisId(id) {
112855
112908
  this._analysisId = id;
112856
112909
  this._usageRecords = [];
112857
112910
  }
112911
+ setAbortSignal(signal) {
112912
+ this._abortSignal = signal;
112913
+ }
112914
+ /** Set repoId for child process tracking in the analysis registry. */
112915
+ setRepoId(repoId) {
112916
+ this._repoId = repoId;
112917
+ }
112858
112918
  async flushUsage() {
112859
112919
  if (!this._analysisId || this._usageRecords.length === 0) return;
112860
112920
  const records = this._usageRecords.map((r) => ({
@@ -112882,7 +112942,7 @@ var init_cli_provider = __esm({
112882
112942
  if (process.env.TRUECOURSE_CLI_DEBUG) {
112883
112943
  this.debugDir = join6(tmpdir(), "truecourse-cli-debug");
112884
112944
  mkdirSync(this.debugDir, { recursive: true });
112885
- console.log(`[CLI] Debug output: ${this.debugDir}`);
112945
+ log(`[CLI] Debug output: ${this.debugDir}`);
112886
112946
  }
112887
112947
  }
112888
112948
  /** Write input prompt, schema, and raw output to debug files. */
@@ -112911,7 +112971,11 @@ var init_cli_provider = __esm({
112911
112971
  }
112912
112972
  /** Spawn CLI subprocess, pipe prompt via stdin, collect stdout. */
112913
112973
  spawnCLI(prompt, jsonSchemaStr, opts) {
112974
+ if (this._abortSignal?.aborted) {
112975
+ return Promise.reject(new DOMException("Analysis cancelled", "AbortError"));
112976
+ }
112914
112977
  const timeout = opts?.timeoutMs ?? config.claudeCodeTimeoutMs ?? 12e4;
112978
+ const label = opts?.label ?? "call";
112915
112979
  const args = [
112916
112980
  ...this.baseArgs,
112917
112981
  ...this.modelFlag,
@@ -112924,13 +112988,23 @@ var init_cli_provider = __esm({
112924
112988
  env: this.getCleanEnv(),
112925
112989
  stdio: ["pipe", "pipe", "pipe"]
112926
112990
  });
112991
+ if (this._repoId) {
112992
+ registerChildProcess(this._repoId, child);
112993
+ }
112927
112994
  let stdout = "";
112928
112995
  let stderr = "";
112929
112996
  let timedOut = false;
112997
+ let aborted2 = false;
112930
112998
  const timer = setTimeout(() => {
112931
112999
  timedOut = true;
112932
113000
  child.kill("SIGTERM");
112933
113001
  }, timeout);
113002
+ const onAbort = () => {
113003
+ aborted2 = true;
113004
+ clearTimeout(timer);
113005
+ if (!child.killed) child.kill("SIGTERM");
113006
+ };
113007
+ this._abortSignal?.addEventListener("abort", onAbort, { once: true });
112934
113008
  child.stdout.on("data", (chunk) => {
112935
113009
  stdout += chunk.toString();
112936
113010
  });
@@ -112939,8 +113013,14 @@ var init_cli_provider = __esm({
112939
113013
  });
112940
113014
  child.on("close", (code) => {
112941
113015
  clearTimeout(timer);
113016
+ this._abortSignal?.removeEventListener("abort", onAbort);
113017
+ if (this._repoId) unregisterChildProcess(this._repoId, child);
113018
+ if (aborted2) {
113019
+ reject(new DOMException("Analysis cancelled", "AbortError"));
113020
+ return;
113021
+ }
112942
113022
  if (timedOut) {
112943
- reject(new Error(`[CLI] ${this.binaryName} timed out after ${timeout}ms`));
113023
+ reject(new Error(`[CLI] ${label} timed out after ${timeout}ms`));
112944
113024
  return;
112945
113025
  }
112946
113026
  if (code !== 0) {
@@ -112952,6 +113032,8 @@ var init_cli_provider = __esm({
112952
113032
  });
112953
113033
  child.on("error", (err) => {
112954
113034
  clearTimeout(timer);
113035
+ this._abortSignal?.removeEventListener("abort", onAbort);
113036
+ if (this._repoId) unregisterChildProcess(this._repoId, child);
112955
113037
  reject(new Error(`[CLI] Failed to spawn ${this.binaryName}: ${err.message}`));
112956
113038
  });
112957
113039
  child.stdin.write(prompt);
@@ -113008,7 +113090,7 @@ var init_cli_provider = __esm({
113008
113090
  } catch (err) {
113009
113091
  lastError = err;
113010
113092
  if (attempt < this.maxRetries) {
113011
- console.warn(`[CLI] Attempt ${attempt + 1} failed, retrying... (${lastError.message})`);
113093
+ log(`[CLI] Attempt ${attempt + 1} failed, retrying... (${lastError.message})`);
113012
113094
  }
113013
113095
  }
113014
113096
  }
@@ -113020,14 +113102,14 @@ var init_cli_provider = __esm({
113020
113102
  async generateServiceViolations(context2) {
113021
113103
  const { vars, idMap } = buildServiceTemplateVars(context2);
113022
113104
  const { text: prompt } = await getPrompt("violations-service", vars);
113023
- console.log("[CLI] Service violations call starting...");
113105
+ log("[CLI] Service violations call starting...");
113024
113106
  const t0 = Date.now();
113025
113107
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, ServiceViolationOutputSchema, {
113026
113108
  extraArgs: ["--tools", ""],
113027
113109
  label: "service"
113028
113110
  });
113029
113111
  const dur = Date.now() - t0;
113030
- console.log(`[CLI] Service violations call done in ${dur}ms \u2014 ${object3.violations.length} violations`);
113112
+ log(`[CLI] Service violations call done in ${dur}ms \u2014 ${object3.violations.length} violations`);
113031
113113
  this.collectUsage("service", cliUsage, dur);
113032
113114
  return {
113033
113115
  violations: object3.violations.map((v) => ({
@@ -113050,14 +113132,14 @@ var init_cli_provider = __esm({
113050
113132
  async generateDatabaseViolations(context2) {
113051
113133
  const { vars, idMap } = buildDatabaseTemplateVars(context2);
113052
113134
  const { text: prompt } = await getPrompt("violations-database", vars);
113053
- console.log("[CLI] Database violations call starting...");
113135
+ log("[CLI] Database violations call starting...");
113054
113136
  const t0 = Date.now();
113055
113137
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, DatabaseViolationOutputSchema, {
113056
113138
  extraArgs: ["--tools", ""],
113057
113139
  label: "database"
113058
113140
  });
113059
113141
  const dur = Date.now() - t0;
113060
- console.log(`[CLI] Database violations call done in ${dur}ms \u2014 ${object3.violations.length} violations`);
113142
+ log(`[CLI] Database violations call done in ${dur}ms \u2014 ${object3.violations.length} violations`);
113061
113143
  this.collectUsage("database", cliUsage, dur);
113062
113144
  return {
113063
113145
  violations: object3.violations.map((v) => ({
@@ -113080,14 +113162,14 @@ var init_cli_provider = __esm({
113080
113162
  const moduleIdToServiceId = new Map(
113081
113163
  context2.modules.filter((m) => m.serviceId).map((m) => [m.id, m.serviceId])
113082
113164
  );
113083
- console.log("[CLI] Module violations call starting...");
113165
+ log("[CLI] Module violations call starting...");
113084
113166
  const t0 = Date.now();
113085
113167
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, ModuleViolationOutputSchema, {
113086
113168
  extraArgs: ["--tools", ""],
113087
113169
  label: "module"
113088
113170
  });
113089
113171
  const dur = Date.now() - t0;
113090
- console.log(`[CLI] Module violations call done in ${dur}ms \u2014 ${object3.violations.length} violations`);
113172
+ log(`[CLI] Module violations call done in ${dur}ms \u2014 ${object3.violations.length} violations`);
113091
113173
  this.collectUsage("module", cliUsage, dur);
113092
113174
  return {
113093
113175
  violations: object3.violations.map((v) => {
@@ -113139,7 +113221,7 @@ var init_cli_provider = __esm({
113139
113221
  if (outcome.status === "fulfilled") {
113140
113222
  result[key] = outcome.value;
113141
113223
  } else {
113142
- console.error(`[CLI Violations] ${key} call failed:`, outcome.reason);
113224
+ log(`[CLI Violations] ${key} call failed: ${outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason)}`);
113143
113225
  }
113144
113226
  }
113145
113227
  return result;
@@ -113157,14 +113239,14 @@ var init_cli_provider = __esm({
113157
113239
  const { vars, idMap } = buildServiceTemplateVars(ctx);
113158
113240
  idMaps.service = idMap;
113159
113241
  const { text: prompt } = await getPrompt("violations-service-lifecycle", vars);
113160
- console.log("[CLI] Lifecycle service call starting...");
113242
+ log("[CLI] Lifecycle service call starting...");
113161
113243
  const t0 = Date.now();
113162
113244
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, LifecycleServiceOutputSchema, {
113163
113245
  extraArgs: ["--tools", ""],
113164
113246
  label: "service-lifecycle"
113165
113247
  });
113166
113248
  const dur = Date.now() - t0;
113167
- console.log(`[CLI] Lifecycle service call done in ${dur}ms \u2014 resolved: ${object3.resolvedViolationIds.length}, new: ${object3.newViolations.length}`);
113249
+ log(`[CLI] Lifecycle service call done in ${dur}ms \u2014 resolved: ${object3.resolvedViolationIds.length}, new: ${object3.newViolations.length}`);
113168
113250
  this.collectUsage("service", cliUsage, dur);
113169
113251
  return object3;
113170
113252
  })()]);
@@ -113179,14 +113261,14 @@ var init_cli_provider = __esm({
113179
113261
  const { vars, idMap } = buildDatabaseTemplateVars(ctx);
113180
113262
  idMaps.database = idMap;
113181
113263
  const { text: prompt } = await getPrompt("violations-database-lifecycle", vars);
113182
- console.log("[CLI] Lifecycle database call starting...");
113264
+ log("[CLI] Lifecycle database call starting...");
113183
113265
  const t0 = Date.now();
113184
113266
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, DiffViolationOutputSchema, {
113185
113267
  extraArgs: ["--tools", ""],
113186
113268
  label: "database-lifecycle"
113187
113269
  });
113188
113270
  const dur = Date.now() - t0;
113189
- console.log(`[CLI] Lifecycle database call done in ${dur}ms \u2014 resolved: ${object3.resolvedViolationIds.length}, new: ${object3.newViolations.length}`);
113271
+ log(`[CLI] Lifecycle database call done in ${dur}ms \u2014 resolved: ${object3.resolvedViolationIds.length}, new: ${object3.newViolations.length}`);
113190
113272
  this.collectUsage("database", cliUsage, dur);
113191
113273
  return object3;
113192
113274
  })()]);
@@ -113204,14 +113286,14 @@ var init_cli_provider = __esm({
113204
113286
  const { vars, idMap } = buildModuleTemplateVars(ctx);
113205
113287
  idMaps.module = idMap;
113206
113288
  const { text: prompt } = await getPrompt("violations-module-lifecycle", vars);
113207
- console.log("[CLI] Lifecycle module call starting...");
113289
+ log("[CLI] Lifecycle module call starting...");
113208
113290
  const t0 = Date.now();
113209
113291
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, DiffViolationOutputSchema, {
113210
113292
  extraArgs: ["--tools", ""],
113211
113293
  label: "module-lifecycle"
113212
113294
  });
113213
113295
  const dur = Date.now() - t0;
113214
- console.log(`[CLI] Lifecycle module call done in ${dur}ms \u2014 resolved: ${object3.resolvedViolationIds.length}, new: ${object3.newViolations.length}`);
113296
+ log(`[CLI] Lifecycle module call done in ${dur}ms \u2014 resolved: ${object3.resolvedViolationIds.length}, new: ${object3.newViolations.length}`);
113215
113297
  this.collectUsage("module", cliUsage, dur);
113216
113298
  return {
113217
113299
  resolvedViolationIds: resolveIds(object3.resolvedViolationIds, idMap),
@@ -113250,7 +113332,7 @@ var init_cli_provider = __esm({
113250
113332
  const [key] = promises[i];
113251
113333
  const outcome = settled[i];
113252
113334
  if (outcome.status !== "fulfilled") {
113253
- console.error(`[CLI ViolationsLifecycle] ${key} call failed:`, outcome.reason);
113335
+ log(`[CLI ViolationsLifecycle] ${key} call failed: ${outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason)}`);
113254
113336
  continue;
113255
113337
  }
113256
113338
  if (key === "service") {
@@ -113341,16 +113423,18 @@ var init_cli_provider = __esm({
113341
113423
  const promptName = hasExisting ? "violations-code-lifecycle" : "violations-code";
113342
113424
  const { vars, idMap } = buildCodeTemplateVars(context2, { useFilePaths: true });
113343
113425
  const { text: prompt } = await getPrompt(promptName, vars);
113344
- console.log(`[CLI] Code violations call starting (${context2.files.length} files, ${hasExisting ? "lifecycle" : "first-run"})...`);
113426
+ log(`[CLI] Code violations call starting (${context2.files.length} files, ${hasExisting ? "lifecycle" : "first-run"})...`);
113345
113427
  const t0 = Date.now();
113346
113428
  const codeExtraArgs = ["--allowedTools", "Read"];
113429
+ const codeTimeoutMs = 3e5;
113347
113430
  if (hasExisting) {
113348
113431
  const { data: object4, usage: cliUsage2 } = await this.spawnAndParse(prompt, CodeViolationLifecycleOutputSchema, {
113349
113432
  extraArgs: codeExtraArgs,
113350
- label: "code-lifecycle"
113433
+ label: "code-lifecycle",
113434
+ timeoutMs: codeTimeoutMs
113351
113435
  });
113352
113436
  const dur2 = Date.now() - t0;
113353
- console.log(`[CLI] Code violations call done in ${dur2}ms \u2014 new: ${object4.newViolations.length}, resolved: ${object4.resolvedViolationIds.length}, unchanged: ${object4.unchangedViolationIds.length}`);
113437
+ log(`[CLI] Code violations call done in ${dur2}ms \u2014 new: ${object4.newViolations.length}, resolved: ${object4.resolvedViolationIds.length}, unchanged: ${object4.unchangedViolationIds.length}`);
113354
113438
  this.collectUsage("code", cliUsage2, dur2);
113355
113439
  return {
113356
113440
  violations: object4.newViolations.map((v) => ({
@@ -113369,10 +113453,11 @@ var init_cli_provider = __esm({
113369
113453
  }
113370
113454
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, CodeViolationOutputSchema, {
113371
113455
  extraArgs: codeExtraArgs,
113372
- label: "code"
113456
+ label: "code",
113457
+ timeoutMs: codeTimeoutMs
113373
113458
  });
113374
113459
  const dur = Date.now() - t0;
113375
- console.log(`[CLI] Code violations call done in ${dur}ms \u2014 ${object3.violations.length} violations`);
113460
+ log(`[CLI] Code violations call done in ${dur}ms \u2014 ${object3.violations.length} violations`);
113376
113461
  this.collectUsage("code", cliUsage, dur);
113377
113462
  return {
113378
113463
  violations: object3.violations.map((v) => ({
@@ -113389,7 +113474,7 @@ var init_cli_provider = __esm({
113389
113474
  }
113390
113475
  async generateAllCodeViolations(batches) {
113391
113476
  if (batches.length === 0) return { violations: [] };
113392
- console.log(`[CLI] Code violations: ${batches.length} batch(es) starting...`);
113477
+ log(`[CLI] Code violations: ${batches.length} batch(es) starting...`);
113393
113478
  const t0 = Date.now();
113394
113479
  const results = await Promise.allSettled(
113395
113480
  batches.map((batch) => this.generateCodeViolations(batch))
@@ -113403,10 +113488,10 @@ var init_cli_provider = __esm({
113403
113488
  if (result.value.resolvedViolationIds) allResolved.push(...result.value.resolvedViolationIds);
113404
113489
  if (result.value.unchangedViolationIds) allUnchanged.push(...result.value.unchangedViolationIds);
113405
113490
  } else {
113406
- console.error("[CLI CodeViolations] Batch call failed:", result.reason);
113491
+ log(`[CLI CodeViolations] Batch call failed: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`);
113407
113492
  }
113408
113493
  }
113409
- console.log(`[CLI] Code violations total: ${Date.now() - t0}ms \u2014 new: ${allViolations.length}, resolved: ${allResolved.length}, unchanged: ${allUnchanged.length}`);
113494
+ log(`[CLI] Code violations total: ${Date.now() - t0}ms \u2014 new: ${allViolations.length}, resolved: ${allResolved.length}, unchanged: ${allUnchanged.length}`);
113410
113495
  return {
113411
113496
  violations: allViolations,
113412
113497
  resolvedViolationIds: allResolved.length > 0 ? allResolved : void 0,
@@ -113417,14 +113502,14 @@ var init_cli_provider = __esm({
113417
113502
  if (detections.length === 0) return { enrichedViolations: [] };
113418
113503
  const { vars, idMap } = buildEnrichmentTemplateVars(detections, architectureContext);
113419
113504
  const { text: prompt } = await getPrompt("violations-enrich-deterministic", vars);
113420
- console.log(`[CLI] Enrichment call starting for ${detections.length} detections...`);
113505
+ log(`[CLI] Enrichment call starting for ${detections.length} detections...`);
113421
113506
  const t0 = Date.now();
113422
113507
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, EnrichmentOutputSchema, {
113423
113508
  extraArgs: ["--tools", ""],
113424
113509
  label: "enrichment"
113425
113510
  });
113426
113511
  const dur = Date.now() - t0;
113427
- console.log(`[CLI] Enrichment call done in ${dur}ms \u2014 ${object3.enrichedViolations.length} enriched`);
113512
+ log(`[CLI] Enrichment call done in ${dur}ms \u2014 ${object3.enrichedViolations.length} enriched`);
113428
113513
  this.collectUsage("enrichment", cliUsage, dur);
113429
113514
  return {
113430
113515
  enrichedViolations: object3.enrichedViolations.map((e) => ({
@@ -113435,14 +113520,14 @@ var init_cli_provider = __esm({
113435
113520
  }
113436
113521
  async enrichFlow(context2) {
113437
113522
  const { text: prompt } = await getPrompt("flow-enrichment", buildFlowTemplateVars(context2));
113438
- console.log(`[CLI] Flow enrichment call starting for ${context2.flowName}...`);
113523
+ log(`[CLI] Flow enrichment call starting for ${context2.flowName}...`);
113439
113524
  const t0 = Date.now();
113440
113525
  const { data: object3, usage: cliUsage } = await this.spawnAndParse(prompt, FlowEnrichmentOutputSchema, {
113441
113526
  extraArgs: ["--tools", ""],
113442
113527
  label: "flow"
113443
113528
  });
113444
113529
  const dur = Date.now() - t0;
113445
- console.log(`[CLI] Flow enrichment done in ${dur}ms`);
113530
+ log(`[CLI] Flow enrichment done in ${dur}ms`);
113446
113531
  this.collectUsage("flow", cliUsage, dur);
113447
113532
  return {
113448
113533
  name: object3.name,
@@ -113577,10 +113662,16 @@ var init_provider = __esm({
113577
113662
  AISDKProvider = class {
113578
113663
  _analysisId = null;
113579
113664
  _usageRecords = [];
113665
+ _abortSignal = null;
113580
113666
  setAnalysisId(id) {
113581
113667
  this._analysisId = id;
113582
113668
  this._usageRecords = [];
113583
113669
  }
113670
+ setRepoId(_repoId) {
113671
+ }
113672
+ setAbortSignal(signal) {
113673
+ this._abortSignal = signal;
113674
+ }
113584
113675
  async flushUsage() {
113585
113676
  if (!this._analysisId || this._usageRecords.length === 0) return;
113586
113677
  const records = this._usageRecords.map((r) => ({
@@ -113610,6 +113701,7 @@ var init_provider = __esm({
113610
113701
  model,
113611
113702
  output: output_exports.object({ schema: ServiceViolationOutputSchema }),
113612
113703
  prompt,
113704
+ abortSignal: this._abortSignal ?? void 0,
113613
113705
  experimental_telemetry: telemetry("violations-service", langfusePrompt)
113614
113706
  });
113615
113707
  const dur = Date.now() - t0;
@@ -113643,6 +113735,7 @@ var init_provider = __esm({
113643
113735
  model,
113644
113736
  output: output_exports.object({ schema: DatabaseViolationOutputSchema }),
113645
113737
  prompt,
113738
+ abortSignal: this._abortSignal ?? void 0,
113646
113739
  experimental_telemetry: telemetry("violations-database", langfusePrompt)
113647
113740
  });
113648
113741
  const dur = Date.now() - t0;
@@ -113676,6 +113769,7 @@ var init_provider = __esm({
113676
113769
  model,
113677
113770
  output: output_exports.object({ schema: ModuleViolationOutputSchema }),
113678
113771
  prompt,
113772
+ abortSignal: this._abortSignal ?? void 0,
113679
113773
  experimental_telemetry: telemetry("violations-module", langfusePrompt)
113680
113774
  });
113681
113775
  const dur = Date.now() - t0;
@@ -113764,6 +113858,7 @@ var init_provider = __esm({
113764
113858
  model,
113765
113859
  output: output_exports.object({ schema: LifecycleServiceOutputSchema }),
113766
113860
  prompt,
113861
+ abortSignal: this._abortSignal ?? void 0,
113767
113862
  experimental_telemetry: telemetry("violations-service-lifecycle", langfusePrompt)
113768
113863
  });
113769
113864
  const dur = Date.now() - t0;
@@ -113788,6 +113883,7 @@ var init_provider = __esm({
113788
113883
  model,
113789
113884
  output: output_exports.object({ schema: DiffViolationOutputSchema }),
113790
113885
  prompt,
113886
+ abortSignal: this._abortSignal ?? void 0,
113791
113887
  experimental_telemetry: telemetry("violations-database-lifecycle", langfusePrompt)
113792
113888
  });
113793
113889
  const dur = Date.now() - t0;
@@ -113815,6 +113911,7 @@ var init_provider = __esm({
113815
113911
  model,
113816
113912
  output: output_exports.object({ schema: DiffViolationOutputSchema }),
113817
113913
  prompt,
113914
+ abortSignal: this._abortSignal ?? void 0,
113818
113915
  experimental_telemetry: telemetry("violations-module-lifecycle", langfusePrompt)
113819
113916
  });
113820
113917
  const dur = Date.now() - t0;
@@ -113958,6 +114055,7 @@ var init_provider = __esm({
113958
114055
  model,
113959
114056
  output: output_exports.object({ schema: CodeViolationLifecycleOutputSchema }),
113960
114057
  prompt,
114058
+ abortSignal: this._abortSignal ?? void 0,
113961
114059
  experimental_telemetry: telemetry("violations-code-lifecycle", langfusePrompt)
113962
114060
  });
113963
114061
  const dur2 = Date.now() - t0;
@@ -113982,6 +114080,7 @@ var init_provider = __esm({
113982
114080
  model,
113983
114081
  output: output_exports.object({ schema: CodeViolationOutputSchema }),
113984
114082
  prompt,
114083
+ abortSignal: this._abortSignal ?? void 0,
113985
114084
  experimental_telemetry: telemetry("violations-code", langfusePrompt)
113986
114085
  });
113987
114086
  const dur = Date.now() - t0;
@@ -114040,6 +114139,7 @@ var init_provider = __esm({
114040
114139
  model,
114041
114140
  output: output_exports.object({ schema: EnrichmentOutputSchema }),
114042
114141
  prompt,
114142
+ abortSignal: this._abortSignal ?? void 0,
114043
114143
  experimental_telemetry: telemetry("violations-enrich-deterministic", langfusePrompt)
114044
114144
  });
114045
114145
  const dur = Date.now() - t0;
@@ -114061,6 +114161,7 @@ var init_provider = __esm({
114061
114161
  model,
114062
114162
  output: output_exports.object({ schema: FlowEnrichmentOutputSchema }),
114063
114163
  prompt,
114164
+ abortSignal: this._abortSignal ?? void 0,
114064
114165
  experimental_telemetry: telemetry("flow-enrichment", langfusePrompt)
114065
114166
  });
114066
114167
  const dur = Date.now() - t0;
@@ -114081,6 +114182,7 @@ var init_provider = __esm({
114081
114182
  role: m.role,
114082
114183
  content: m.content
114083
114184
  })),
114185
+ abortSignal: this._abortSignal ?? void 0,
114084
114186
  experimental_telemetry: telemetry("chat")
114085
114187
  });
114086
114188
  for await (const chunk of textStream) {
@@ -144314,7 +144416,7 @@ var require_internal3 = __commonJS({
144314
144416
  scopeLogs: Array.from(ismMap, ([, scopeLogs]) => {
144315
144417
  return {
144316
144418
  scope: (0, internal_1.createInstrumentationScope)(scopeLogs[0].instrumentationScope),
144317
- logRecords: scopeLogs.map((log) => toLogRecord(log, encoder)),
144419
+ logRecords: scopeLogs.map((log3) => toLogRecord(log3, encoder)),
144318
144420
  schemaUrl: scopeLogs[0].instrumentationScope.schemaUrl
144319
144421
  };
144320
144422
  }),
@@ -144322,19 +144424,19 @@ var require_internal3 = __commonJS({
144322
144424
  };
144323
144425
  });
144324
144426
  }
144325
- function toLogRecord(log, encoder) {
144427
+ function toLogRecord(log3, encoder) {
144326
144428
  return {
144327
- timeUnixNano: encoder.encodeHrTime(log.hrTime),
144328
- observedTimeUnixNano: encoder.encodeHrTime(log.hrTimeObserved),
144329
- severityNumber: toSeverityNumber(log.severityNumber),
144330
- severityText: log.severityText,
144331
- body: (0, internal_1.toAnyValue)(log.body, encoder),
144332
- eventName: log.eventName,
144333
- attributes: toLogAttributes(log.attributes, encoder),
144334
- droppedAttributesCount: log.droppedAttributesCount,
144335
- flags: log.spanContext?.traceFlags,
144336
- traceId: encoder.encodeOptionalSpanContext(log.spanContext?.traceId),
144337
- spanId: encoder.encodeOptionalSpanContext(log.spanContext?.spanId)
144429
+ timeUnixNano: encoder.encodeHrTime(log3.hrTime),
144430
+ observedTimeUnixNano: encoder.encodeHrTime(log3.hrTimeObserved),
144431
+ severityNumber: toSeverityNumber(log3.severityNumber),
144432
+ severityText: log3.severityText,
144433
+ body: (0, internal_1.toAnyValue)(log3.body, encoder),
144434
+ eventName: log3.eventName,
144435
+ attributes: toLogAttributes(log3.attributes, encoder),
144436
+ droppedAttributesCount: log3.droppedAttributesCount,
144437
+ flags: log3.spanContext?.traceFlags,
144438
+ traceId: encoder.encodeOptionalSpanContext(log3.spanContext?.traceId),
144439
+ spanId: encoder.encodeOptionalSpanContext(log3.spanContext?.spanId)
144338
144440
  };
144339
144441
  }
144340
144442
  function toSeverityNumber(severityNumber) {
@@ -145837,7 +145939,7 @@ var require_logging = __commonJS({
145837
145939
  _logVerbosity = verbosity;
145838
145940
  };
145839
145941
  exports2.setLoggerVerbosity = setLoggerVerbosity;
145840
- var log = (severity, ...args) => {
145942
+ var log3 = (severity, ...args) => {
145841
145943
  let logFunction;
145842
145944
  if (severity >= _logVerbosity) {
145843
145945
  switch (severity) {
@@ -145859,7 +145961,7 @@ var require_logging = __commonJS({
145859
145961
  }
145860
145962
  }
145861
145963
  };
145862
- exports2.log = log;
145964
+ exports2.log = log3;
145863
145965
  var tracersString = (_d = (_c = process.env.GRPC_NODE_TRACE) !== null && _c !== void 0 ? _c : process.env.GRPC_TRACE) !== null && _d !== void 0 ? _d : "";
145864
145966
  var enabledTracers = /* @__PURE__ */ new Set();
145865
145967
  var disabledTracers = /* @__PURE__ */ new Set();
@@ -157528,7 +157630,7 @@ var require_umd = __commonJS({
157528
157630
  rem = this;
157529
157631
  while (rem.gte(divisor)) {
157530
157632
  approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber()));
157531
- var log2 = Math.ceil(Math.log(approx) / Math.LN2), delta = log2 <= 48 ? 1 : pow_dbl(2, log2 - 48), approxRes = fromNumber(approx), approxRem = approxRes.mul(divisor);
157633
+ var log22 = Math.ceil(Math.log(approx) / Math.LN2), delta = log22 <= 48 ? 1 : pow_dbl(2, log22 - 48), approxRes = fromNumber(approx), approxRem = approxRes.mul(divisor);
157532
157634
  while (approxRem.isNegative() || approxRem.gt(rem)) {
157533
157635
  approx -= delta;
157534
157636
  approxRes = fromNumber(approx, this.unsigned);
@@ -173450,7 +173552,7 @@ var require_merge3 = __commonJS({
173450
173552
  var require_addPairToJSMap = __commonJS({
173451
173553
  "node_modules/.pnpm/yaml@2.8.2/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports2) {
173452
173554
  "use strict";
173453
- var log = require_log();
173555
+ var log3 = require_log();
173454
173556
  var merge2 = require_merge3();
173455
173557
  var stringify = require_stringify2();
173456
173558
  var identity = require_identity2();
@@ -173499,7 +173601,7 @@ var require_addPairToJSMap = __commonJS({
173499
173601
  let jsonStr = JSON.stringify(strKey);
173500
173602
  if (jsonStr.length > 40)
173501
173603
  jsonStr = jsonStr.substring(0, 36) + '..."';
173502
- log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
173604
+ log3.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);
173503
173605
  ctx.mapKeyWarned = true;
173504
173606
  }
173505
173607
  return strKey;
@@ -178883,7 +178985,7 @@ var require_public_api = __commonJS({
178883
178985
  var composer = require_composer();
178884
178986
  var Document = require_Document();
178885
178987
  var errors = require_errors();
178886
- var log = require_log();
178988
+ var log3 = require_log();
178887
178989
  var identity = require_identity2();
178888
178990
  var lineCounter = require_line_counter();
178889
178991
  var parser4 = require_parser();
@@ -178935,7 +179037,7 @@ var require_public_api = __commonJS({
178935
179037
  const doc = parseDocument(src, options);
178936
179038
  if (!doc)
178937
179039
  return null;
178938
- doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning));
179040
+ doc.warnings.forEach((warning) => log3.warn(doc.options.logLevel, warning));
178939
179041
  if (doc.errors.length > 0) {
178940
179042
  if (doc.options.logLevel !== "silent")
178941
179043
  throw doc.errors[0];
@@ -180243,11 +180345,11 @@ var { Server, Namespace, Socket } = import_dist.default;
180243
180345
  var activeAnalyses = /* @__PURE__ */ new Map();
180244
180346
  function setupHandlers(io3) {
180245
180347
  io3.on("connection", (socket) => {
180246
- console.log(`[Socket] Client connected: ${socket.id}`);
180348
+ console.error(`[Socket] Client connected: ${socket.id}`);
180247
180349
  socket.on("joinRepo", async (repoId) => {
180248
180350
  const room = `repo:${repoId}`;
180249
180351
  await socket.join(room);
180250
- console.log(`[Socket] ${socket.id} joined room ${room}`);
180352
+ console.error(`[Socket] ${socket.id} joined room ${room}`);
180251
180353
  const progress = activeAnalyses.get(repoId);
180252
180354
  if (progress) {
180253
180355
  socket.emit("analysis:progress", { repoId, ...progress });
@@ -180256,10 +180358,10 @@ function setupHandlers(io3) {
180256
180358
  socket.on("leaveRepo", async (repoId) => {
180257
180359
  const room = `repo:${repoId}`;
180258
180360
  await socket.leave(room);
180259
- console.log(`[Socket] ${socket.id} left room ${room}`);
180361
+ console.error(`[Socket] ${socket.id} left room ${room}`);
180260
180362
  });
180261
180363
  socket.on("disconnect", () => {
180262
- console.log(`[Socket] Client disconnected: ${socket.id}`);
180364
+ console.error(`[Socket] Client disconnected: ${socket.id}`);
180263
180365
  });
180264
180366
  });
180265
180367
  }
@@ -180282,6 +180384,11 @@ function emitViolationsReady(repoId, analysisId) {
180282
180384
  const io3 = getIO();
180283
180385
  io3.to(`repo:${repoId}`).emit("violations:ready", { repoId, analysisId });
180284
180386
  }
180387
+ function emitAnalysisCanceled(repoId) {
180388
+ activeAnalyses.delete(repoId);
180389
+ const io3 = getIO();
180390
+ io3.to(`repo:${repoId}`).emit("analysis:canceled", { repoId });
180391
+ }
180285
180392
 
180286
180393
  // apps/server/src/socket/index.ts
180287
180394
  var io2 = null;
@@ -182249,11 +182356,11 @@ function taskCallback(task, response, callback = NOOP) {
182249
182356
  response.then(onSuccess, onError2);
182250
182357
  }
182251
182358
  function addDeprecationNoticeToError(err) {
182252
- let log = (name21) => {
182359
+ let log3 = (name21) => {
182253
182360
  console.warn(
182254
182361
  `simple-git deprecation notice: accessing GitResponseError.${name21} should be GitResponseError.git.${name21}, this will no longer be available in version 3`
182255
182362
  );
182256
- log = NOOP;
182363
+ log3 = NOOP;
182257
182364
  };
182258
182365
  return Object.create(err, Object.getOwnPropertyNames(err.git).reduce(descriptorReducer, {}));
182259
182366
  function descriptorReducer(all, name21) {
@@ -182264,7 +182371,7 @@ function addDeprecationNoticeToError(err) {
182264
182371
  enumerable: false,
182265
182372
  configurable: false,
182266
182373
  get() {
182267
- log(name21);
182374
+ log3(name21);
182268
182375
  return err.git[name21];
182269
182376
  }
182270
182377
  };
@@ -185901,7 +186008,7 @@ async function detectAndPersistFlows(analysisId, result) {
185901
186008
  );
185902
186009
  }
185903
186010
  }
185904
- console.log(`[Flows] Detected and persisted ${traced.length} flows for analysis ${analysisId}`);
186011
+ console.error(`[Flows] Detected and persisted ${traced.length} flows for analysis ${analysisId}`);
185905
186012
  return { flowCount: traced.length };
185906
186013
  }
185907
186014
  function buildRouteHandlerLookup(result) {
@@ -186065,6 +186172,9 @@ async function enrichFlowWithLLM(flowId) {
186065
186172
  }
186066
186173
  }
186067
186174
 
186175
+ // apps/server/src/routes/analysis.ts
186176
+ init_analysis_registry();
186177
+
186068
186178
  // apps/server/src/services/graph.service.ts
186069
186179
  var import_dagre = __toESM(require_dagre(), 1);
186070
186180
  init_dist();
@@ -187051,6 +187161,13 @@ async function persistCodeViolationsWithLifecycle(params) {
187051
187161
  }
187052
187162
 
187053
187163
  // apps/server/src/services/violation-pipeline.service.ts
187164
+ function log2(msg) {
187165
+ process.stderr.write(`${msg}
187166
+ `);
187167
+ }
187168
+ function throwIfAborted(signal) {
187169
+ if (signal?.aborted) throw new DOMException("Analysis cancelled", "AbortError");
187170
+ }
187054
187171
  function getDetComparisonKey(v) {
187055
187172
  return `${v.ruleKey}::${v.serviceName}::${v.moduleName || ""}::${v.methodName || ""}::${v.title}`;
187056
187173
  }
@@ -187092,9 +187209,11 @@ async function runViolationPipeline(input) {
187092
187209
  previousDeterministicViolations,
187093
187210
  changedFileSet,
187094
187211
  onProgress,
187095
- provider: externalProvider
187212
+ provider: externalProvider,
187213
+ signal
187096
187214
  } = input;
187097
187215
  const allRules = await getEnabledRules();
187216
+ throwIfAborted(signal);
187098
187217
  onProgress?.({ step: "analyzing", percent: 80, detail: "Running deterministic checks..." });
187099
187218
  const enabledDeterministic = allRules.filter((r) => r.type === "deterministic");
187100
187219
  const serviceViolationResults = runDeterministicServiceChecks(result, enabledDeterministic);
@@ -187159,6 +187278,7 @@ async function runViolationPipeline(input) {
187159
187278
  }
187160
187279
  }
187161
187280
  }
187281
+ throwIfAborted(signal);
187162
187282
  onProgress?.({ step: "analyzing", percent: 84, detail: "Code checks done" });
187163
187283
  const MAX_CHARS_PER_BATCH = 1e5;
187164
187284
  const HALF_BATCH = MAX_CHARS_PER_BATCH / 2;
@@ -187218,6 +187338,7 @@ async function runViolationPipeline(input) {
187218
187338
  const emitted = highWaterMark;
187219
187339
  onProgress?.({ step: "analyzing", percent: emitted, detail });
187220
187340
  };
187341
+ throwIfAborted(signal);
187221
187342
  emitProgress(85, "Enriching & analyzing violations...");
187222
187343
  const allDetEntries = [];
187223
187344
  for (const v of serviceViolationResults) {
@@ -187564,10 +187685,21 @@ Services: ${result.services.map((s) => `${s.name} (${s.type})`).join(", ")}`;
187564
187685
  emitProgress(94, "Code analysis done");
187565
187686
  return r;
187566
187687
  });
187567
- const [, , codeResult] = await Promise.all([deterministicPromise, llmRulePromise, codePromise]);
187688
+ const [detResult, llmResult, codeSettled] = await Promise.allSettled([deterministicPromise, llmRulePromise, codePromise]);
187689
+ if (detResult.status === "rejected") {
187690
+ log2(`[Violations] Deterministic enrichment failed: ${detResult.reason instanceof Error ? detResult.reason.message : String(detResult.reason)}`);
187691
+ }
187692
+ if (llmResult.status === "rejected") {
187693
+ log2(`[Violations] LLM rule analysis failed: ${llmResult.reason instanceof Error ? llmResult.reason.message : String(llmResult.reason)}`);
187694
+ }
187695
+ if (codeSettled.status === "rejected") {
187696
+ log2(`[Violations] Code analysis failed: ${codeSettled.reason instanceof Error ? codeSettled.reason.message : String(codeSettled.reason)}`);
187697
+ }
187698
+ const codeResult = codeSettled.status === "fulfilled" ? codeSettled.value : { violations: [] };
187568
187699
  processLlmCodeViolations(codeResult, validFilePaths, fileContents, allCodeViolations);
187569
187700
  llmCodeResolvedIds = codeResult.resolvedViolationIds || [];
187570
187701
  llmCodeUnchangedIds = codeResult.unchangedViolationIds || [];
187702
+ throwIfAborted(signal);
187571
187703
  emitProgress(95, "Analysis complete");
187572
187704
  for (const desc2 of serviceDescriptions) {
187573
187705
  if (desc2.id) {
@@ -187718,6 +187850,7 @@ router2.post(
187718
187850
  const git = simpleGit(repo.path);
187719
187851
  const branch = (await git.branch()).current || null;
187720
187852
  res.status(202).json({ message: "Analysis started", repoId: id, branch });
187853
+ const abortController = registerAnalysis(id, "pending");
187721
187854
  try {
187722
187855
  emitAnalysisProgress(id, {
187723
187856
  step: "starting",
@@ -187853,6 +187986,8 @@ router2.post(
187853
187986
  await db.update(repos).set({ lastAnalyzedAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() }).where(eq(repos.id, id));
187854
187987
  const provider = createLLMProvider();
187855
187988
  provider.setAnalysisId(newAnalysisId);
187989
+ provider.setRepoId(id);
187990
+ provider.setAbortSignal(abortController.signal);
187856
187991
  try {
187857
187992
  await runViolationPipeline({
187858
187993
  repoId: id,
@@ -187867,14 +188002,20 @@ router2.post(
187867
188002
  previousActiveCodeViolations,
187868
188003
  previousDeterministicViolations,
187869
188004
  onProgress: (progress) => emitAnalysisProgress(id, progress),
187870
- provider
188005
+ provider,
188006
+ signal: abortController.signal
187871
188007
  });
187872
188008
  emitViolationsReady(id, analysis.id);
187873
188009
  } catch (violationError) {
187874
- console.error(
187875
- `[Violations] Failed for repo ${id}:`,
187876
- violationError instanceof Error ? violationError.message : String(violationError)
187877
- );
188010
+ if (violationError instanceof DOMException && violationError.name === "AbortError") {
188011
+ console.log(`[Violations] Cancelled for repo ${id}`);
188012
+ } else {
188013
+ console.error(
188014
+ `[Violations] Failed for repo ${id}:`,
188015
+ violationError instanceof Error ? violationError.message : String(violationError)
188016
+ );
188017
+ }
188018
+ emitViolationsReady(id, analysis.id);
187878
188019
  }
187879
188020
  try {
187880
188021
  await provider.flushUsage();
@@ -187883,15 +188024,38 @@ router2.post(
187883
188024
  }
187884
188025
  emitAnalysisComplete(id, analysis.id);
187885
188026
  } catch (error40) {
187886
- console.error(
187887
- `[Analysis] Failed for repo ${id}:`,
187888
- error40 instanceof Error ? error40.message : String(error40)
187889
- );
187890
- emitAnalysisProgress(id, {
187891
- step: "error",
187892
- percent: -1,
187893
- detail: error40 instanceof Error ? error40.message : "Analysis failed"
187894
- });
188027
+ if (error40 instanceof DOMException && error40.name === "AbortError") {
188028
+ console.log(`[Analysis] Cancelled for repo ${id}`);
188029
+ } else {
188030
+ console.error(
188031
+ `[Analysis] Failed for repo ${id}:`,
188032
+ error40 instanceof Error ? error40.message : String(error40)
188033
+ );
188034
+ emitAnalysisProgress(id, {
188035
+ step: "error",
188036
+ percent: -1,
188037
+ detail: error40 instanceof Error ? error40.message : "Analysis failed"
188038
+ });
188039
+ }
188040
+ } finally {
188041
+ unregisterAnalysis(id);
188042
+ }
188043
+ } catch (error40) {
188044
+ next(error40);
188045
+ }
188046
+ }
188047
+ );
188048
+ router2.post(
188049
+ "/:id/analyze/cancel",
188050
+ async (req, res, next) => {
188051
+ try {
188052
+ const id = req.params.id;
188053
+ const canceled = cancelAnalysis(id);
188054
+ if (canceled) {
188055
+ emitAnalysisCanceled(id);
188056
+ res.json({ message: "Analysis cancelled" });
188057
+ } else {
188058
+ throw createAppError("No active analysis for this repo", 404);
187895
188059
  }
187896
188060
  } catch (error40) {
187897
188061
  next(error40);