@jayyuen66/dsh-ocr-review 0.1.2 → 0.1.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/host.js +50 -6
  2. package/package.json +1 -1
package/host.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // GENERATED by build-host.mjs — edit host.ts / lib/*.ts, then run `node build-host.mjs`.
2
2
  import { createRequire } from "node:module";
3
3
  import Schema from "@deepseek-ai/schemastery";
4
- import { chmodSync, copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
4
+ import { chmodSync, copyFileSync, existsSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
5
5
  import { homedir, tmpdir } from "node:os";
6
6
  import path from "node:path";
7
7
  import { randomUUID } from "node:crypto";
@@ -27,7 +27,7 @@ const MESSAGES = {
27
27
  detailSeparator: ":",
28
28
  stdoutTruncated: "[ocr-review] 注意:输出超过 {limit} 字节上限已截断{spill}",
29
29
  outputSpill: "(完整输出已落盘:{path})",
30
- backgroundPolling: "稍候用 ocr_session(action=list) 找本仓库最新会话(启动几秒后出现,按 scanPaths/时间匹配);completed_files == selected_files 即已完成;再 ocr_session(action=comments, id) 取全部评论、ocr_session(action=show, id) 看元数据与逐文件检查点。后台运行不设宿主超时,只在 2 小时后兜底回收(挂死不动会一直占着官方作业桶),取消本工具调用即终止该后台 OCR(宿主 kill + 命令内回收守护)。",
30
+ backgroundPolling: "先 job_output(job_id=<本回执的 jobId>) 读这条后台评审的实时输出与最终结局——按 id 走,启动即可读,一定有结果;要停它就 job_kill(job_id=<同一 id>)。会话落盘后再走 ocr_session:ocr_session(action=list) 找本仓库最新会话(启动几秒后出现,按 scanPaths/时间匹配);completed_files == selected_files 即已完成;再 ocr_session(action=comments, id) 取全部评论、ocr_session(action=show, id) 看元数据与逐文件检查点。后台运行不设宿主超时,只在 2 小时后兜底回收(挂死不动会一直占着官方作业桶),取消本工具调用即终止该后台 OCR(宿主 kill + 命令内回收守护)。",
31
31
  jobReasonAborted: "ocr-review 工具调用被中止",
32
32
  jobReasonTimeout: "ocr-review 后台评审超过 2 小时,兜底回收",
33
33
  jobReasonUnload: "ocr-review 插件卸载",
@@ -154,7 +154,7 @@ const MESSAGES = {
154
154
  detailSeparator: ": ",
155
155
  stdoutTruncated: "[ocr-review] note: output exceeded the {limit}-byte cap and was truncated{spill}",
156
156
  outputSpill: " (full output spilled to {path})",
157
- backgroundPolling: "Look up this repository's newest session shortly with ocr_session(action=list) (it appears a few seconds after start; match by scanPaths/time); completed_files == selected_files means it is done. Then ocr_session(action=comments, id) for all comments and ocr_session(action=show, id) for metadata and per-file checkpoints. Background runs get reclaimed by a 2-hour backstop (a wedged one would otherwise keep occupying the official job bucket); cancelling this tool call terminates the background OCR (host kill + the reaper guard inside the command).",
157
+ backgroundPolling: "First read this background review's live output and final outcome with job_output(job_id=<the jobId in this receipt>) — that path goes by id, so it is readable immediately after start and always has a result; job_kill(job_id=<the same id>) stops it. Once the session has landed, use ocr_session: ocr_session(action=list) finds this repository's newest session (it appears a few seconds after start; match by scanPaths/time); completed_files == selected_files means it is done. Then ocr_session(action=comments, id) for all comments and ocr_session(action=show, id) for metadata and per-file checkpoints. Background runs get reclaimed by a 2-hour backstop (a wedged one would otherwise keep occupying the official job bucket); cancelling this tool call terminates the background OCR (host kill + the reaper guard inside the command).",
158
158
  jobReasonAborted: "ocr-review tool call aborted",
159
159
  jobReasonTimeout: "ocr-review background review exceeded 2 hours and was reclaimed",
160
160
  jobReasonUnload: "ocr-review plugin unloaded",
@@ -1837,6 +1837,7 @@ const REVIEW_OUTPUT_SCHEMA = { oneOf: [
1837
1837
  "tool",
1838
1838
  "repo",
1839
1839
  "processStatus",
1840
+ "jobId",
1840
1841
  "polling"
1841
1842
  ],
1842
1843
  properties: {
@@ -1858,6 +1859,7 @@ const REVIEW_OUTPUT_SCHEMA = { oneOf: [
1858
1859
  "killed"
1859
1860
  ]
1860
1861
  },
1862
+ jobId: { type: "string" },
1861
1863
  polling: { type: "string" }
1862
1864
  }
1863
1865
  },
@@ -2103,7 +2105,7 @@ async function runForeground(host, command, workdir, run, signal) {
2103
2105
  const result = await (await host.shell.execute(spec)).result();
2104
2106
  if (result.exitCode !== 0 || result.timedOut || result.aborted) {
2105
2107
  if (result.timedOut) {
2106
- const minutes = Math.round(run.timeoutMs / 6e4);
2108
+ const minutes = Math.round(spec.timeoutMs / 6e4);
2107
2109
  throw new Error(format(messages.execTimedOut, { minutes: String(minutes) }));
2108
2110
  }
2109
2111
  if (result.aborted) throw new Error(messages.execAborted);
@@ -2266,6 +2268,7 @@ async function startBackground(host, job, signal) {
2266
2268
  tool: job.tool,
2267
2269
  repo: job.repo,
2268
2270
  processStatus: proc.status,
2271
+ jobId,
2269
2272
  polling: messages.backgroundPolling
2270
2273
  };
2271
2274
  }
@@ -2559,6 +2562,47 @@ function createGateway(host) {
2559
2562
  function ocrRepo(args, exec, messages) {
2560
2563
  return resolveRoot(args["repo"], sessionHeaderCwd(exec), messages);
2561
2564
  }
2565
+ /** git rev-parse 的墙钟上限:一条本地命令,失败即回退,不值得占用评审预算。 */
2566
+ const GIT_TOPLEVEL_TIMEOUT_MS = 1e4;
2567
+ /** realpath,失败则原样返回(路径可能已不存在:归档后的 checkout 仍要能查会话)。 */
2568
+ function realpathOrSelf(root) {
2569
+ try {
2570
+ return realpathSync(root);
2571
+ } catch {
2572
+ return root;
2573
+ }
2574
+ }
2575
+ /**
2576
+ * 把审查根归一化成 **canonical 的 git 顶层**,让 review 与 ocr_session 落到同一个会话目录。
2577
+ *
2578
+ * 为什么插件侧要管这件事:OCR 把 review 的 RepoDir 锚定在 git 顶层(`shared.go` 的
2579
+ * requireGit=true 分支),而会话目录正是由 RepoDir 编码而来。插件若把模型给的子目录
2580
+ * 原样喂给 `--repo`,`ocr review` 写进顶层、`ocr_session` 查子目录 —— 两个不同的
2581
+ * `~/.opencodereview/sessions/<encoded>` 目录,查询侧命中「目录不存在」分支静默返回
2582
+ * 空,表现为「写得出会话却永远列不出来」。符号链接前缀同理:git 报的是 realpath,
2583
+ * 而 filepath.Abs 保留 `/tmp` 这类前缀。
2584
+ *
2585
+ * 为什么不统一提升所有工具:scan 靠 workdir 限定子目录(OCR 的 scan 路径
2586
+ * requireGit=false,保留 CWD 以免 `git ls-files` 走遍整个仓库),一并提升会把扫描
2587
+ * 范围扩到整个 monorepo。scan 又不落会话(只有 review 经 agent.New → session.New
2588
+ * 建会话),所以它保持原样即可,两侧口径本来就一致。
2589
+ *
2590
+ * 非 git 目录(归档后的 checkout 等)回退到 realpath:会话仍按同一路径寻址。
2591
+ *
2592
+ * 刻意不缓存:一次本地 git 命令相对一次评审(分钟级)可忽略,换来的是没有陈旧值,
2593
+ * 也不会让「仓库被 git init / 顶层变动」后读到旧结果。
2594
+ */
2595
+ async function canonicalReviewRoot(host, root) {
2596
+ try {
2597
+ const { exitCode, output } = await runCollect(host, "git rev-parse --show-toplevel", root, {
2598
+ timeoutMs: GIT_TOPLEVEL_TIMEOUT_MS,
2599
+ stdoutMaxBytes: 4096
2600
+ });
2601
+ const top = exitCode === 0 ? output.trim() : "";
2602
+ if (top.startsWith("/")) return top;
2603
+ } catch {}
2604
+ return realpathOrSelf(root);
2605
+ }
2562
2606
  /** POST select 的响应:把 body 里的 provider/model 应用到 OCR 配置,按需附带 llm test 回执。 */
2563
2607
  async function sendSelectResponse(text, res, deps) {
2564
2608
  const msgs = deps.messages();
@@ -2769,7 +2813,7 @@ function registerReviewTool(host, config) {
2769
2813
  schema: REVIEW_OUTPUT_SCHEMA,
2770
2814
  run: async (args, exec) => {
2771
2815
  const runMsgs = localeMessages(host);
2772
- const repo = ocrRepo(args, exec, runMsgs);
2816
+ const repo = await canonicalReviewRoot(host, ocrRepo(args, exec, runMsgs));
2773
2817
  const eff = effortOf(args["effort"], config, runMsgs);
2774
2818
  const tmp = waitOf(args["wait"], runMsgs) ? mkdtempSync(path.join(tmpdir(), "ocr-review-")) : void 0;
2775
2819
  const outPath = tmp === void 0 ? void 0 : path.join(tmp, "out.json");
@@ -3057,7 +3101,7 @@ function registerSessionTool(host, config) {
3057
3101
  schema: SESSION_OUTPUT_SCHEMA,
3058
3102
  run: async (args, exec) => {
3059
3103
  const runMsgs = localeMessages(host);
3060
- const repo = ocrRepo(args, exec, runMsgs);
3104
+ const repo = await canonicalReviewRoot(host, ocrRepo(args, exec, runMsgs));
3061
3105
  const { command, workdir } = buildSessionCommand({
3062
3106
  ...args,
3063
3107
  repo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jayyuen66/dsh-ocr-review",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "dsh 工作区代码审查插件:把 open-code-review(ocr CLI)封装为 5 个模型工具 + 设置卡片(从 settings.yaml 选 provider/model 应用到 OCR 配置)。",
5
5
  "license": "MIT",
6
6
  "author": "JayYuen",