@aipanel/dsh-plugin 1.3.1 → 1.3.3

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.
Files changed (2) hide show
  1. package/dist/index.js +166 -67
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -329,23 +329,48 @@ var DIAGNOSTICS_TOOL_DESCRIPTION = [
329
329
  "- \u6392\u67E5\u7F16\u8F91\u5668\u672A\u663E\u793A\u4F46\u5B9E\u9645\u5B58\u5728\u7684\u7C7B\u578B\u95EE\u9898",
330
330
  "- \u4E0D\u4F20\u53C2\u6570\u53EF\u5168\u91CF\u8BCA\u65AD\u6574\u4E2A\u9879\u76EE"
331
331
  ].join("\n");
332
- var ESLintClass;
332
+ function cachedResolve(cache, key, resolve) {
333
+ const hit = cache.get(key);
334
+ if (hit !== void 0) return hit;
335
+ const value = resolve();
336
+ cache.set(key, value);
337
+ return value;
338
+ }
339
+ var _packageBinCache = /* @__PURE__ */ new Map();
340
+ function resolvePackageBin(basePkgJson, pkgName, binName) {
341
+ return cachedResolve(_packageBinCache, `${basePkgJson}\0${pkgName}`, () => {
342
+ try {
343
+ const req = createRequire(basePkgJson);
344
+ const pkgJsonPath = req.resolve(`${pkgName}/package.json`);
345
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
346
+ const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.[binName];
347
+ return binRel ? path.join(path.dirname(pkgJsonPath), binRel) : null;
348
+ } catch {
349
+ return null;
350
+ }
351
+ });
352
+ }
353
+ var _eslintClassByBase = /* @__PURE__ */ new Map();
333
354
  function loadESLint(workspace) {
334
- if (ESLintClass) return;
335
- log2.debug("Loading eslint", { workspace });
336
- try {
337
- const req = createRequire(path.join(workspace, "package.json"));
338
- const eslintModule = req("eslint");
339
- ESLintClass ?? (ESLintClass = eslintModule.ESLint ?? eslintModule.FlatESLint);
340
- log2.debug("eslint loaded", { hasClass: !!ESLintClass });
341
- } catch (e) {
342
- log2.warn("eslint not found", { error: e.message });
343
- }
355
+ const basePkgJson = path.join(workspace, "package.json");
356
+ return cachedResolve(_eslintClassByBase, basePkgJson, () => {
357
+ log2.debug("Loading eslint", { workspace });
358
+ try {
359
+ const req = createRequire(basePkgJson);
360
+ const eslintModule = req("eslint");
361
+ const eslintClass = eslintModule.ESLint ?? eslintModule.FlatESLint ?? null;
362
+ log2.debug("eslint loaded", { hasClass: !!eslintClass });
363
+ return eslintClass;
364
+ } catch (e) {
365
+ log2.warn("eslint not found", { error: e.message });
366
+ return null;
367
+ }
368
+ });
344
369
  }
345
370
  async function lintFiles(pattern, cwd, warnLimit = 5) {
346
- loadESLint(cwd);
371
+ const eslintClass = loadESLint(cwd);
347
372
  const [eslintOutput, oxlintOutput] = await Promise.all([
348
- ESLintClass ? runEslintFiles(pattern, cwd, warnLimit) : Promise.resolve(null),
373
+ eslintClass ? runEslintFiles(eslintClass, pattern, cwd, warnLimit) : Promise.resolve(null),
349
374
  runOxlintFiles(pattern, cwd, warnLimit)
350
375
  ]);
351
376
  const engines = [];
@@ -405,9 +430,9 @@ function formatLintMessages(messages, engine, warnLimit) {
405
430
  engines: [engine.label]
406
431
  };
407
432
  }
408
- async function runEslintFiles(pattern, cwd, warnLimit) {
433
+ async function runEslintFiles(eslintClass, pattern, cwd, warnLimit) {
409
434
  try {
410
- const eslint = new ESLintClass({ cwd });
435
+ const eslint = new eslintClass({ cwd });
411
436
  const results = await eslint.lintFiles(pattern);
412
437
  const messages = results.flatMap(
413
438
  (r) => (r.messages ?? []).map((m) => ({ ...m, filePath: r.filePath }))
@@ -427,19 +452,8 @@ async function runEslintFiles(pattern, cwd, warnLimit) {
427
452
  };
428
453
  }
429
454
  }
430
- var _oxlintBin;
431
455
  function resolveOxlintBin(workspace) {
432
- if (_oxlintBin !== void 0) return _oxlintBin;
433
- try {
434
- const req = createRequire(path.join(workspace, "package.json"));
435
- const pkgJsonPath = req.resolve("oxlint/package.json");
436
- const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
437
- const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.oxlint;
438
- _oxlintBin = binRel ? path.join(path.dirname(pkgJsonPath), binRel) : null;
439
- } catch {
440
- _oxlintBin = null;
441
- }
442
- return _oxlintBin;
456
+ return resolvePackageBin(path.join(workspace, "package.json"), "oxlint", "oxlint");
443
457
  }
444
458
  function oxlintRuleName(code) {
445
459
  if (!code) return null;
@@ -516,18 +530,9 @@ async function runOxlintFiles(pattern, cwd, warnLimit) {
516
530
  };
517
531
  }
518
532
  }
519
- var _vueTscBin;
520
- function resolveVueTscBin() {
521
- if (_vueTscBin !== void 0) return _vueTscBin;
522
- try {
523
- const req = createRequire(import.meta.url);
524
- _vueTscBin = req.resolve("vue-tsc/bin/vue-tsc.js");
525
- } catch {
526
- _vueTscBin = null;
527
- }
528
- return _vueTscBin;
529
- }
530
- var _tscBinByPkgDir = /* @__PURE__ */ new Map();
533
+ var BUNDLED_ENGINE_KEY = "<bundled>";
534
+ var _engineCache = /* @__PURE__ */ new Map();
535
+ var _tsVersionCache = /* @__PURE__ */ new Map();
531
536
  function nearestPackageJsonDir(startDir) {
532
537
  let dir = path.resolve(startDir);
533
538
  while (true) {
@@ -546,26 +551,53 @@ function isVuePackage(pkgDir) {
546
551
  return false;
547
552
  }
548
553
  }
549
- function resolveTypeCheckBin(projectDir) {
550
- const pkgDir = nearestPackageJsonDir(projectDir);
551
- if (!pkgDir || isVuePackage(pkgDir)) {
552
- const bin2 = resolveVueTscBin();
553
- return bin2 ? { bin: bin2, source: "vue-tsc" } : null;
554
+ function readPackageMeta(pkgJsonPath) {
555
+ try {
556
+ return JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
557
+ } catch {
558
+ return null;
554
559
  }
555
- let bin = _tscBinByPkgDir.get(pkgDir);
556
- if (bin === void 0) {
560
+ }
561
+ function resolveEngineTsVersion(bin) {
562
+ const pkgDir = nearestPackageJsonDir(path.dirname(bin));
563
+ if (!pkgDir) return null;
564
+ return cachedResolve(_tsVersionCache, pkgDir, () => {
565
+ const own = readPackageMeta(path.join(pkgDir, "package.json"));
566
+ if (own?.name === "typescript" && own.version) return own.version;
557
567
  try {
558
568
  const req = createRequire(path.join(pkgDir, "package.json"));
559
- bin = req.resolve("typescript/bin/tsc");
569
+ return readPackageMeta(req.resolve("typescript/package.json"))?.version ?? null;
560
570
  } catch {
561
- bin = null;
571
+ return null;
562
572
  }
563
- _tscBinByPkgDir.set(pkgDir, bin);
564
- if (!bin) log2.debug("workspace tsc not resolvable, fallback to vue-tsc", { pkgDir });
565
- }
566
- if (bin) return { bin, source: "tsc" };
567
- const vueBin = resolveVueTscBin();
568
- return vueBin ? { bin: vueBin, source: "vue-tsc" } : null;
573
+ });
574
+ }
575
+ function toEngine(bin, source, origin) {
576
+ return bin ? { bin, source, origin, tsVersion: resolveEngineTsVersion(bin) } : null;
577
+ }
578
+ function resolveWorkspaceEngine(pkgDir, kind) {
579
+ const basePkgJson = path.join(pkgDir, "package.json");
580
+ return kind === "tsc" ? toEngine(resolvePackageBin(basePkgJson, "typescript", "tsc"), "tsc", "workspace") : toEngine(resolvePackageBin(basePkgJson, "vue-tsc", "vue-tsc"), "vue-tsc", "workspace");
581
+ }
582
+ function resolveBundledVueTscEngine() {
583
+ return toEngine(resolvePackageBin(import.meta.url, "vue-tsc", "vue-tsc"), "vue-tsc", "bundled");
584
+ }
585
+ function resolveTypeCheckEngine(projectDir) {
586
+ const pkgDir = nearestPackageJsonDir(projectDir);
587
+ return cachedResolve(_engineCache, pkgDir ?? BUNDLED_ENGINE_KEY, () => {
588
+ if (!pkgDir) return resolveBundledVueTscEngine();
589
+ const candidates = isVuePackage(pkgDir) ? [resolveWorkspaceEngine(pkgDir, "vue-tsc")] : [resolveWorkspaceEngine(pkgDir, "tsc"), resolveWorkspaceEngine(pkgDir, "vue-tsc")];
590
+ for (const engine of candidates) {
591
+ if (engine) return engine;
592
+ }
593
+ return resolveBundledVueTscEngine();
594
+ });
595
+ }
596
+ var BUILD_NO_EMIT_MIN_TS = { major: 5, minor: 6 };
597
+ function supportsBuildNoEmit(tsVersion) {
598
+ if (!tsVersion) return false;
599
+ const [major, minor] = tsVersion.split(".").map((part) => Number.parseInt(part, 10));
600
+ return major > BUILD_NO_EMIT_MIN_TS.major || major === BUILD_NO_EMIT_MIN_TS.major && minor >= BUILD_NO_EMIT_MIN_TS.minor;
569
601
  }
570
602
  function findTsconfigDir(filePath) {
571
603
  const resolved = path.resolve(filePath);
@@ -655,6 +687,77 @@ function filterTscOutputForFile(rawOutput, filePath, projectDir) {
655
687
  }
656
688
  return filtered.join("\n");
657
689
  }
690
+ async function readResolvedTsconfig(engine, configPath, projectDir) {
691
+ const result = await execa(
692
+ "node",
693
+ [
694
+ engine.bin,
695
+ "-p",
696
+ projectRelativeConfig(projectDir, configPath),
697
+ "--showConfig",
698
+ "--pretty",
699
+ "false"
700
+ ],
701
+ { cwd: projectDir, timeout: 3e4, maxBuffer: 10 * 1024 * 1024, reject: false }
702
+ ).catch(() => null);
703
+ if (!result || result.timedOut || result.isTerminated || result.isMaxBuffer) return null;
704
+ const stdout = result.stdout ?? "";
705
+ const start = stdout.indexOf("{");
706
+ const end = stdout.lastIndexOf("}");
707
+ if (start < 0 || end <= start) return null;
708
+ try {
709
+ return JSON.parse(stdout.slice(start, end + 1));
710
+ } catch {
711
+ return null;
712
+ }
713
+ }
714
+ async function resolveProjectConfigs(engine, projectDir) {
715
+ const configs = [];
716
+ const visited = /* @__PURE__ */ new Set();
717
+ const visit = async (configPath) => {
718
+ const resolved = path.resolve(configPath);
719
+ if (visited.has(resolved)) return;
720
+ visited.add(resolved);
721
+ const config = await readResolvedTsconfig(engine, resolved, projectDir);
722
+ const hasProgram = !config || (config.files?.length ?? 0) > 0 || (config.include?.length ?? 0) > 0;
723
+ if (hasProgram) configs.push(resolved);
724
+ for (const ref of config?.references ?? []) {
725
+ if (ref.path) await visit(path.resolve(path.dirname(resolved), ref.path));
726
+ }
727
+ };
728
+ const rootConfig = path.join(projectDir, "tsconfig.json");
729
+ await visit(fs.existsSync(rootConfig) ? rootConfig : projectDir);
730
+ return configs;
731
+ }
732
+ function projectRelativeConfig(projectDir, configPath) {
733
+ return path.relative(projectDir, configPath) || ".";
734
+ }
735
+ async function typeCheckArgGroups(engine, projectDir) {
736
+ const pretty = ["--pretty", "false"];
737
+ if (supportsBuildNoEmit(engine.tsVersion)) return [["--build", "--noEmit", ...pretty]];
738
+ const configs = await resolveProjectConfigs(engine, projectDir);
739
+ return configs.map((config) => [
740
+ "-p",
741
+ projectRelativeConfig(projectDir, config),
742
+ "--noEmit",
743
+ ...pretty
744
+ ]);
745
+ }
746
+ async function runTypeCheckCli(engine, args, cwd, timeout, maxBuffer) {
747
+ const result = await execa("node", [engine.bin, ...args], {
748
+ cwd,
749
+ timeout,
750
+ maxBuffer,
751
+ reject: false
752
+ });
753
+ let rawOutput = result.stdout + result.stderr;
754
+ const killed = result.timedOut || result.isTerminated || result.isMaxBuffer;
755
+ const exitCode = typeof result.exitCode === "number" ? result.exitCode : killed ? 1 : 0;
756
+ if (killed && !rawOutput) {
757
+ rawOutput = `${engine.source} \u68C0\u67E5\u8D85\u65F6\uFF0C\u8BF7\u5C1D\u8BD5\u7F29\u5C0F\u68C0\u67E5\u8303\u56F4\u6216\u4F18\u5316\u9879\u76EE\u914D\u7F6E\u3002`;
758
+ }
759
+ return { rawOutput, exitCode };
760
+ }
658
761
  async function runTypeCheck(filePath, cwd) {
659
762
  const dir = cwd;
660
763
  const projectDir = filePath ? findTsconfigDir(filePath) ?? dir : dir;
@@ -664,31 +767,27 @@ async function runTypeCheck(filePath, cwd) {
664
767
  projectDir,
665
768
  processCwd: process.cwd()
666
769
  });
667
- const engine = resolveTypeCheckBin(projectDir);
770
+ const engine = resolveTypeCheckEngine(projectDir);
668
771
  if (!engine) {
669
772
  log2.warn("type-check bin not found", { projectDir });
670
773
  return { rawOutput: "", exitCode: 0 };
671
774
  }
672
775
  const timeout = filePath ? 6e4 : 12e4;
673
776
  const maxBuffer = filePath ? 10 * 1024 * 1024 : 50 * 1024 * 1024;
674
- const result = await execa("node", [engine.bin, "--build", "--noEmit", "--pretty", "false"], {
675
- cwd: projectDir,
676
- timeout,
677
- maxBuffer,
678
- reject: false
679
- });
680
- let rawOutput = result.stdout + result.stderr;
681
- const killed = result.timedOut || result.isTerminated || result.isMaxBuffer;
682
- const exitCode = typeof result.exitCode === "number" ? result.exitCode : killed ? 1 : 0;
683
- if (killed && !rawOutput) {
684
- rawOutput = `${engine.source} \u68C0\u67E5\u8D85\u65F6\uFF0C\u8BF7\u5C1D\u8BD5\u7F29\u5C0F\u68C0\u67E5\u8303\u56F4\u6216\u4F18\u5316\u9879\u76EE\u914D\u7F6E\u3002`;
685
- }
777
+ const argGroups = await typeCheckArgGroups(engine, projectDir);
778
+ const results = await Promise.all(
779
+ argGroups.map((args) => runTypeCheckCli(engine, args, projectDir, timeout, maxBuffer))
780
+ );
781
+ let rawOutput = results.map((result) => result.rawOutput).filter(Boolean).join("\n");
782
+ const exitCode = results.reduce((max, result) => Math.max(max, result.exitCode), 0);
686
783
  const diagnostics = parseTscDiags(rawOutput, filePath, projectDir, engine.source);
687
784
  if (filePath) {
688
785
  rawOutput = filterTscOutputForFile(rawOutput, filePath, projectDir);
689
786
  }
690
787
  log2.debug("type-check finished", {
691
788
  engine: engine.source,
789
+ engineOrigin: engine.origin,
790
+ tsVersion: engine.tsVersion,
692
791
  filePath: filePath || "(all)",
693
792
  exitCode,
694
793
  outputLength: rawOutput.length
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/dsh-plugin",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "type": "module",
5
5
  "description": "AIPanel for DeepSeek Harness (dsh):注入审查工具 run_diagnostics、编辑后自动诊断。",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "vue-tsc": "^3.3.9"
17
17
  },
18
18
  "devDependencies": {
19
- "@aipanel/core": "1.3.1",
19
+ "@aipanel/core": "1.3.3",
20
20
  "@deepseek-ai/cordis": "^4.0.2",
21
21
  "@deepseek-ai/dsh-agent": "^0.1.6-alpha.2",
22
22
  "@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",