@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.
- package/host.js +50 -6
- 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: "
|
|
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: "
|
|
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(
|
|
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