@jayyuen66/dsh-ocr-review 0.1.9 → 0.1.11
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/CHANGELOG.md +68 -4
- package/LICENSE +1 -1
- package/README.md +14 -14
- package/README.zh-CN.md +13 -13
- package/THIRD_PARTY_NOTICES.md +36 -6
- package/client.js +101 -47
- package/docs/DETAIL.md +5 -5
- package/docs/DETAIL.zh.md +5 -5
- package/host.js +659 -313
- package/package.json +53 -42
- package/scripts/get-cred.mjs +124 -59
package/client.js
CHANGED
|
@@ -21,7 +21,6 @@ const UI_MESSAGES = {
|
|
|
21
21
|
save: "保存",
|
|
22
22
|
saving: "保存中…",
|
|
23
23
|
revert: "撤销",
|
|
24
|
-
saveFailed: "保存失败:",
|
|
25
24
|
saveRefused: "未写入(宿主拒收或跳过):{fields}",
|
|
26
25
|
loadProvidersFailed: "加载 providers 失败:{error}(远程面未就绪?重启 dsh 后重试)",
|
|
27
26
|
remoteUnavailable: "远程服务不可用:宿主没有挂上本插件的设置通道。",
|
|
@@ -74,6 +73,7 @@ const UI_MESSAGES = {
|
|
|
74
73
|
mcpSaving: "保存中…",
|
|
75
74
|
mcpSaved: "MCP servers 已保存",
|
|
76
75
|
mcpLoadFailed: "读取 MCP 配置失败:",
|
|
76
|
+
mcpNotLoaded: "还没读到当前 MCP 配置,先等读取完成(失败时先修好读取)再保存",
|
|
77
77
|
chatGptTitle: "ChatGPT(OCR auth)",
|
|
78
78
|
chatGptHint: "登录态由 ocr auth 管理;本卡片只读查询状态,并在登录后把 provider 切到 chatgpt。",
|
|
79
79
|
chatGptCheck: "检查登录态",
|
|
@@ -91,7 +91,7 @@ const UI_MESSAGES = {
|
|
|
91
91
|
binaryUnknown: "状态未取到:刷新本页,或确认 dsh 的 web 服务已起来",
|
|
92
92
|
binaryBundled: "随本插件分发的兜底入口已就位:{path}({channel})",
|
|
93
93
|
binaryPath: "用 PATH 上的全局安装:{path}({channel})",
|
|
94
|
-
binaryNone: "未找到 ocr:PATH
|
|
94
|
+
binaryNone: "未找到 ocr:PATH 与随包兜底两条路都没有可执行候选",
|
|
95
95
|
channelBrew: "brew formula",
|
|
96
96
|
channelNpm: "npm 全局包",
|
|
97
97
|
channelUnknown: "装法未识别",
|
|
@@ -122,7 +122,6 @@ const UI_MESSAGES = {
|
|
|
122
122
|
save: "Save",
|
|
123
123
|
saving: "Saving…",
|
|
124
124
|
revert: "Revert",
|
|
125
|
-
saveFailed: "save failed: ",
|
|
126
125
|
saveRefused: "not written (host refused or skipped): {fields}",
|
|
127
126
|
loadProvidersFailed: "failed to load providers: {error} (remote face not ready? restart dsh and retry)",
|
|
128
127
|
remoteUnavailable: "remote service unavailable: the host did not mount this plugin's settings channel",
|
|
@@ -175,6 +174,7 @@ const UI_MESSAGES = {
|
|
|
175
174
|
mcpSaving: "Saving…",
|
|
176
175
|
mcpSaved: "MCP servers saved",
|
|
177
176
|
mcpLoadFailed: "Failed to read the MCP config: ",
|
|
177
|
+
mcpNotLoaded: "The current MCP config has not loaded yet; wait for the read (or fix it) before saving",
|
|
178
178
|
chatGptTitle: "ChatGPT (OCR auth)",
|
|
179
179
|
chatGptHint: "Sign-in is managed by ocr auth; this card only reports the status and switches OCR's provider to chatgpt after you sign in.",
|
|
180
180
|
chatGptCheck: "Check sign-in",
|
|
@@ -192,7 +192,7 @@ const UI_MESSAGES = {
|
|
|
192
192
|
binaryUnknown: "status not reported: reload this page, or check that the dsh web server is up",
|
|
193
193
|
binaryBundled: "bundled fallback entry in place: {path} ({channel})",
|
|
194
194
|
binaryPath: "using the global install on PATH: {path} ({channel})",
|
|
195
|
-
binaryNone: "ocr not found: neither PATH nor the bundled fallback has an executable candidate
|
|
195
|
+
binaryNone: "ocr not found: neither PATH nor the bundled fallback has an executable candidate",
|
|
196
196
|
channelBrew: "brew formula",
|
|
197
197
|
channelNpm: "npm global package",
|
|
198
198
|
channelUnknown: "install method unrecognised",
|
|
@@ -201,7 +201,7 @@ const UI_MESSAGES = {
|
|
|
201
201
|
versionDiverged: "this machine reports {actual}, which is not the verified {expected}: comment wording and severity may drift between versions",
|
|
202
202
|
badgeDiverged: "ocr version differs",
|
|
203
203
|
detectCli: "Detect version",
|
|
204
|
-
detecting: "Detecting
|
|
204
|
+
detecting: "Detecting…",
|
|
205
205
|
probeVersion: "ocr version: {version}",
|
|
206
206
|
probeReason: "no version read: {reason}",
|
|
207
207
|
badgeBundled: "ocr from bundled fallback",
|
|
@@ -827,7 +827,10 @@ const SELECT_SCHEMA = zod.z.object({
|
|
|
827
827
|
model: zod.z.string({ error: SELECT_MESSAGE })
|
|
828
828
|
}, { error: "select request must be an object" });
|
|
829
829
|
const MCP_SAVE_MESSAGE = "mcp save request requires a string json";
|
|
830
|
-
const MCP_SAVE_SCHEMA = zod.z.object({
|
|
830
|
+
const MCP_SAVE_SCHEMA = zod.z.object({
|
|
831
|
+
json: zod.z.string({ error: MCP_SAVE_MESSAGE }),
|
|
832
|
+
revision: zod.z.string().optional()
|
|
833
|
+
}, { error: MCP_SAVE_MESSAGE });
|
|
831
834
|
const CHAT_GPT_MESSAGE = "chatgpt request requires a string model";
|
|
832
835
|
const CHAT_GPT_SCHEMA = zod.z.object({ model: zod.z.string({ error: CHAT_GPT_MESSAGE }) }, { error: CHAT_GPT_MESSAGE });
|
|
833
836
|
/** 把宿主那九个方法挂到客户端远程面上的描述符(`id` 的形状照生成器:`<包>#<命名空间>/<方法>`)。
|
|
@@ -953,9 +956,10 @@ const OCR_DESCRIPTORS = [
|
|
|
953
956
|
const isRemoteMounter = (value) => e$3(value) && typeof value["$mount"] === "function";
|
|
954
957
|
/** 同上:命名空间服务(挂载之后才有,缺它说明网关没认这枚贡献)。 */
|
|
955
958
|
const isOcrNamespace = (value) => e$3(value) && typeof value["providers"] === "function" && typeof value["select"] === "function" && typeof value["migrate"] === "function" && typeof value["test"] === "function" && typeof value["probe"] === "function" && typeof value["mcp"] === "function" && typeof value["saveMcp"] === "function" && typeof value["auth"] === "function" && typeof value["chatGpt"] === "function";
|
|
956
|
-
/** 网关回执 → 值;载体/网关失败抛(卡片那层按「请求失败」显示)。
|
|
959
|
+
/** 网关回执 → 值;载体/网关失败抛(卡片那层按「请求失败」显示)。
|
|
960
|
+
* 机器码放 `cause`:卡片只展示 message,`REMOTE_...` 这类内部标识不是给用户看的。 */
|
|
957
961
|
function unwrap(result) {
|
|
958
|
-
if (!result.ok) throw new Error(
|
|
962
|
+
if (!result.ok) throw new Error(result.error.message, { cause: { code: result.error.code } });
|
|
959
963
|
return result.value;
|
|
960
964
|
}
|
|
961
965
|
/**
|
|
@@ -971,7 +975,7 @@ function buildOcrReader(ctx) {
|
|
|
971
975
|
let mounted;
|
|
972
976
|
const remoteOf = () => {
|
|
973
977
|
const remote = ctx.get?.("remote", false);
|
|
974
|
-
if (!isRemoteMounter(remote)) throw new Error("remote
|
|
978
|
+
if (!isRemoteMounter(remote)) throw new Error("remote 未提供:当前部署没有远程读面");
|
|
975
979
|
return remote;
|
|
976
980
|
};
|
|
977
981
|
const namespaceOf = async () => {
|
|
@@ -987,7 +991,10 @@ function buildOcrReader(ctx) {
|
|
|
987
991
|
throw error;
|
|
988
992
|
}
|
|
989
993
|
const namespace = ctx.get?.(OCR_SERVICE_KEY, false);
|
|
990
|
-
if (!isOcrNamespace(namespace))
|
|
994
|
+
if (!isOcrNamespace(namespace)) {
|
|
995
|
+
mounted = void 0;
|
|
996
|
+
throw new Error(`${OCR_SERVICE_KEY} 未提供:宿主网关没有挂上这枚命名空间`);
|
|
997
|
+
}
|
|
991
998
|
return namespace;
|
|
992
999
|
};
|
|
993
1000
|
return {
|
|
@@ -1012,8 +1019,11 @@ function buildOcrReader(ctx) {
|
|
|
1012
1019
|
async mcp() {
|
|
1013
1020
|
return unwrap(await (await namespaceOf()).mcp());
|
|
1014
1021
|
},
|
|
1015
|
-
async saveMcp(json) {
|
|
1016
|
-
return unwrap(await (await namespaceOf()).saveMcp({ json }
|
|
1022
|
+
async saveMcp(json, revision) {
|
|
1023
|
+
return unwrap(await (await namespaceOf()).saveMcp(revision === void 0 ? { json } : {
|
|
1024
|
+
json,
|
|
1025
|
+
revision
|
|
1026
|
+
}));
|
|
1017
1027
|
},
|
|
1018
1028
|
async auth() {
|
|
1019
1029
|
return unwrap(await (await namespaceOf()).auth());
|
|
@@ -1253,14 +1263,13 @@ function currentConfigText(t, current) {
|
|
|
1253
1263
|
keyPart: t(current.keyIsDynamic ? "dynamicKey" : "plainKeyWarning")
|
|
1254
1264
|
});
|
|
1255
1265
|
}
|
|
1256
|
-
/**
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
npm: "channelNpm"
|
|
1260
|
-
};
|
|
1261
|
-
/** 那份 CLI 是怎么装上的,翻成人话。判不出来源(含 host 没报这一档)不猜。 */
|
|
1266
|
+
/** 那份 CLI 是怎么装上的,翻成人话。判不出来源(含 host 没报这一档)不猜。
|
|
1267
|
+
* 逐个比对已知值而不是查一张普通对象表:`channel` 来自 wire,只校验了「是字符串」,
|
|
1268
|
+
* 用属性查表时 `"toString"`、`"constructor"` 一类会命中原型上的成员,把函数当文案键传下去。 */
|
|
1262
1269
|
function channelText(t, channel) {
|
|
1263
|
-
|
|
1270
|
+
if (channel === "brew") return t("channelBrew");
|
|
1271
|
+
if (channel === "npm") return t("channelNpm");
|
|
1272
|
+
return t("channelUnknown");
|
|
1264
1273
|
}
|
|
1265
1274
|
/**
|
|
1266
1275
|
* 外部 CLI 可得性那一行的文本。三档来源与「宿主没报」四种说法,判据全在 host 侧
|
|
@@ -1719,7 +1728,7 @@ function buildMcpAdmin(props) {
|
|
|
1719
1728
|
const textarea = (0, react.createElement)("textarea", {
|
|
1720
1729
|
"data-field": "mcpJson",
|
|
1721
1730
|
value: props.mcpJson,
|
|
1722
|
-
disabled: !props.writable || props.mcpBusy || !props.available,
|
|
1731
|
+
disabled: !props.writable || props.mcpBusy || !props.available || !props.mcpReady,
|
|
1723
1732
|
"aria-label": t("mcpTitle"),
|
|
1724
1733
|
className: "dsu-input dsu-textarea dsu-mono ocr-adminTextarea",
|
|
1725
1734
|
onChange: (event) => {
|
|
@@ -1730,7 +1739,7 @@ function buildMcpAdmin(props) {
|
|
|
1730
1739
|
type: "button",
|
|
1731
1740
|
className: "dsu-btn",
|
|
1732
1741
|
"data-field": "mcpSave",
|
|
1733
|
-
disabled: !props.writable || props.mcpBusy || !props.available,
|
|
1742
|
+
disabled: !props.writable || props.mcpBusy || !props.available || !props.mcpReady,
|
|
1734
1743
|
onClick: props.onMcpSave
|
|
1735
1744
|
}, props.mcpBusy ? t("mcpSaving") : t("mcpSave"));
|
|
1736
1745
|
const message = props.mcpMsg === null ? null : (0, react.createElement)("p", { className: "dsu-hint" }, props.mcpMsg);
|
|
@@ -1825,7 +1834,8 @@ function readerPost(reader, t) {
|
|
|
1825
1834
|
}
|
|
1826
1835
|
};
|
|
1827
1836
|
}
|
|
1828
|
-
/** POST select:把选中的 provider/model 写进 OCR 配置,回执里按 autoVerify 决定是否带 llm test。
|
|
1837
|
+
/** POST select:把选中的 provider/model 写进 OCR 配置,回执里按 autoVerify 决定是否带 llm test。
|
|
1838
|
+
* 导出与 `runProbe` 同因:卡片上的动作以纯函数形态可测(回执着色与文案都在这里定)。 */
|
|
1829
1839
|
async function runApplySelection(deps) {
|
|
1830
1840
|
const { t, effectiveProvider, effectiveModel } = deps;
|
|
1831
1841
|
if (!effectiveProvider || !effectiveModel) {
|
|
@@ -1844,10 +1854,12 @@ async function runApplySelection(deps) {
|
|
|
1844
1854
|
})).json();
|
|
1845
1855
|
if (i$1(selJson, "ok") === true) {
|
|
1846
1856
|
let testText;
|
|
1857
|
+
let verified = true;
|
|
1847
1858
|
const llmTestRaw = i$1(selJson, "llmTest");
|
|
1848
1859
|
if (e$3(llmTestRaw)) {
|
|
1849
1860
|
const testOutput = strOf(i$1(llmTestRaw, "output")).slice(0, 400);
|
|
1850
|
-
|
|
1861
|
+
verified = i$1(llmTestRaw, "ok") === true;
|
|
1862
|
+
testText = verified ? t("llmTestPassed") : `${t("llmTestFailed")}${testOutput}`;
|
|
1851
1863
|
} else testText = t("notVerified");
|
|
1852
1864
|
const appliedText = t("applied", {
|
|
1853
1865
|
provider: effectiveProvider,
|
|
@@ -1855,7 +1867,7 @@ async function runApplySelection(deps) {
|
|
|
1855
1867
|
test: testText
|
|
1856
1868
|
});
|
|
1857
1869
|
deps.setActionMsg({
|
|
1858
|
-
ok:
|
|
1870
|
+
ok: verified,
|
|
1859
1871
|
text: appliedText
|
|
1860
1872
|
});
|
|
1861
1873
|
deps.refresh();
|
|
@@ -1888,7 +1900,9 @@ async function runMigrateKeys(deps) {
|
|
|
1888
1900
|
const migrated = Array.isArray(migratedVal) ? migratedVal.filter((item) => typeof item === "string") : [];
|
|
1889
1901
|
const skipped = Array.isArray(skippedVal) ? skippedVal.filter((item) => typeof item === "string") : [];
|
|
1890
1902
|
const ok = i$1(json, "ok") === true;
|
|
1891
|
-
const
|
|
1903
|
+
const migratedText = migrated.length > 0 ? t("migratedPrefix") + migrated.join(", ") : t("noPlainKey");
|
|
1904
|
+
const skippedText = skipped.length > 0 ? t("skippedPrefix") + skipped.join(", ") : "";
|
|
1905
|
+
const head = ok ? migratedText + skippedText : t("migrateFailed");
|
|
1892
1906
|
deps.setActionMsg({
|
|
1893
1907
|
ok,
|
|
1894
1908
|
text: head
|
|
@@ -2026,16 +2040,14 @@ function buildProviderPanel(deps) {
|
|
|
2026
2040
|
const { actionMsg } = deps;
|
|
2027
2041
|
return [buildProviderSection(deps), actionMsg ? (0, react.createElement)("p", { className: statusClass(actionMsg.ok) }, actionMsg.text) : null];
|
|
2028
2042
|
}
|
|
2029
|
-
/**
|
|
2030
|
-
function
|
|
2043
|
+
/** MCP 配置编辑器的状态与动作:读、写,以及「没读成功不许写」的判据。 */
|
|
2044
|
+
function useMcpPanel(reader, t) {
|
|
2031
2045
|
const [mcpJson, setMcpJson] = (0, react.useState)("{}");
|
|
2032
2046
|
const [mcpBusy, setMcpBusy] = (0, react.useState)(false);
|
|
2033
2047
|
const [mcpMsg, setMcpMsg] = (0, react.useState)(null);
|
|
2034
|
-
const [
|
|
2035
|
-
const
|
|
2036
|
-
const
|
|
2037
|
-
const [authModel, setAuthModel] = (0, react.useState)("");
|
|
2038
|
-
const [loginCommand, setLoginCommand] = (0, react.useState)("ocr auth login --provider chatgpt");
|
|
2048
|
+
const [mcpReady, setMcpReady] = (0, react.useState)(false);
|
|
2049
|
+
const mcpDirtyRef = (0, react.useRef)(false);
|
|
2050
|
+
const mcpRevisionRef = (0, react.useRef)(void 0);
|
|
2039
2051
|
const loadMcp = (0, react.useCallback)(async () => {
|
|
2040
2052
|
if (reader === void 0) {
|
|
2041
2053
|
setMcpMsg(t("adminUnavailable"));
|
|
@@ -2043,10 +2055,17 @@ function useAdminPanel(reader, t) {
|
|
|
2043
2055
|
}
|
|
2044
2056
|
try {
|
|
2045
2057
|
const reply = await reader.mcp();
|
|
2046
|
-
if ("status" in reply)
|
|
2047
|
-
|
|
2058
|
+
if ("status" in reply) {
|
|
2059
|
+
setMcpMsg(reply.error);
|
|
2060
|
+
setMcpReady(false);
|
|
2061
|
+
} else {
|
|
2062
|
+
if (!mcpDirtyRef.current) setMcpJson(reply.json);
|
|
2063
|
+
mcpRevisionRef.current = reply.revision;
|
|
2064
|
+
setMcpReady(true);
|
|
2065
|
+
}
|
|
2048
2066
|
} catch (error) {
|
|
2049
2067
|
setMcpMsg(`${t("mcpLoadFailed")}${String(error)}`);
|
|
2068
|
+
setMcpReady(false);
|
|
2050
2069
|
}
|
|
2051
2070
|
}, [reader, t]);
|
|
2052
2071
|
(0, react.useEffect)(() => {
|
|
@@ -2057,17 +2076,48 @@ function useAdminPanel(reader, t) {
|
|
|
2057
2076
|
setMcpMsg(t("adminUnavailable"));
|
|
2058
2077
|
return;
|
|
2059
2078
|
}
|
|
2079
|
+
if (!mcpReady) {
|
|
2080
|
+
setMcpMsg(t("mcpNotLoaded"));
|
|
2081
|
+
return;
|
|
2082
|
+
}
|
|
2060
2083
|
setMcpBusy(true);
|
|
2061
2084
|
setMcpMsg(null);
|
|
2062
2085
|
try {
|
|
2063
|
-
const reply = await reader.saveMcp(mcpJson);
|
|
2064
|
-
|
|
2086
|
+
const reply = await reader.saveMcp(mcpJson, mcpRevisionRef.current);
|
|
2087
|
+
if ("status" in reply) setMcpMsg(reply.error);
|
|
2088
|
+
else {
|
|
2089
|
+
mcpDirtyRef.current = false;
|
|
2090
|
+
mcpRevisionRef.current = reply.revision;
|
|
2091
|
+
setMcpMsg(t("mcpSaved"));
|
|
2092
|
+
}
|
|
2065
2093
|
} catch (error) {
|
|
2066
2094
|
setMcpMsg(String(error));
|
|
2067
2095
|
} finally {
|
|
2068
2096
|
setMcpBusy(false);
|
|
2069
2097
|
}
|
|
2070
2098
|
}
|
|
2099
|
+
return {
|
|
2100
|
+
mcpJson,
|
|
2101
|
+
mcpReady,
|
|
2102
|
+
mcpBusy,
|
|
2103
|
+
mcpMsg,
|
|
2104
|
+
onMcpChange: (value) => {
|
|
2105
|
+
mcpDirtyRef.current = true;
|
|
2106
|
+
setMcpJson(value);
|
|
2107
|
+
},
|
|
2108
|
+
onMcpSave: () => {
|
|
2109
|
+
saveMcp();
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
/** 管理区的状态与动作(MCP 配置 + ChatGPT 登录态/切换);OcrCard 只负责摆位置。 */
|
|
2114
|
+
function useAdminPanel(reader, t) {
|
|
2115
|
+
const mcp = useMcpPanel(reader, t);
|
|
2116
|
+
const [authOutput, setAuthOutput] = (0, react.useState)(null);
|
|
2117
|
+
const [authBusy, setAuthBusy] = (0, react.useState)(false);
|
|
2118
|
+
const [authMsg, setAuthMsg] = (0, react.useState)(null);
|
|
2119
|
+
const [authModel, setAuthModel] = (0, react.useState)("");
|
|
2120
|
+
const [loginCommand, setLoginCommand] = (0, react.useState)("ocr auth login --provider chatgpt");
|
|
2071
2121
|
async function checkAuth() {
|
|
2072
2122
|
if (reader === void 0) {
|
|
2073
2123
|
setAuthMsg(t("adminUnavailable"));
|
|
@@ -2107,18 +2157,12 @@ function useAdminPanel(reader, t) {
|
|
|
2107
2157
|
}
|
|
2108
2158
|
return {
|
|
2109
2159
|
available: reader !== void 0,
|
|
2110
|
-
|
|
2111
|
-
mcpBusy,
|
|
2112
|
-
mcpMsg,
|
|
2160
|
+
...mcp,
|
|
2113
2161
|
authOutput,
|
|
2114
2162
|
authBusy,
|
|
2115
2163
|
authMsg,
|
|
2116
2164
|
authModel,
|
|
2117
2165
|
loginCommand,
|
|
2118
|
-
onMcpChange: setMcpJson,
|
|
2119
|
-
onMcpSave: () => {
|
|
2120
|
-
saveMcp();
|
|
2121
|
-
},
|
|
2122
2166
|
onAuthModelChange: setAuthModel,
|
|
2123
2167
|
onCheckAuth: () => {
|
|
2124
2168
|
checkAuth();
|
|
@@ -2140,10 +2184,19 @@ function OcrCard(props) {
|
|
|
2140
2184
|
const [busy, setBusy] = (0, react.useState)(null);
|
|
2141
2185
|
const [actionMsg, setActionMsg] = (0, react.useState)(null);
|
|
2142
2186
|
const [probeMsg, setProbeMsg] = (0, react.useState)(null);
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2187
|
+
const refreshSeq = (0, react.useRef)(0);
|
|
2188
|
+
const refresh = (0, react.useCallback)(async () => {
|
|
2189
|
+
refreshSeq.current += 1;
|
|
2190
|
+
const seq = refreshSeq.current;
|
|
2191
|
+
return refreshProviders(props.reader, t, {
|
|
2192
|
+
setData: (payload) => {
|
|
2193
|
+
if (seq === refreshSeq.current) setData(payload);
|
|
2194
|
+
},
|
|
2195
|
+
setFetchError: (message) => {
|
|
2196
|
+
if (seq === refreshSeq.current) setFetchError(message);
|
|
2197
|
+
}
|
|
2198
|
+
});
|
|
2199
|
+
}, [props.reader, t]);
|
|
2147
2200
|
(0, react.useEffect)(() => {
|
|
2148
2201
|
refresh();
|
|
2149
2202
|
}, [refresh]);
|
|
@@ -2323,6 +2376,7 @@ exports.inject = inject;
|
|
|
2323
2376
|
exports.isOwnSubject = isOwnSubject;
|
|
2324
2377
|
exports.loadCliStatus = loadCliStatus;
|
|
2325
2378
|
exports.providerOptionLabel = providerOptionLabel;
|
|
2379
|
+
exports.runApplySelection = runApplySelection;
|
|
2326
2380
|
exports.runProbe = runProbe;
|
|
2327
2381
|
exports.saveSettingsDraft = saveSettingsDraft;
|
|
2328
2382
|
return module.exports;
|
package/docs/DETAIL.md
CHANGED
|
@@ -4,10 +4,10 @@ Runs the external `ocr` command line over a change set, reports the comments it
|
|
|
4
4
|
|
|
5
5
|
## What the card changes
|
|
6
6
|
|
|
7
|
-
- **Model** — `ocrProvider` and `ocrModel` are not stored settings: they project the host's model namespace, and entries without a usable key are shown but disabled. `chatGptModel`
|
|
8
|
-
- **Review parameters** — `effort` (`auto`/`low`/`medium`/`high`) is how many review passes run: deeper review costs more time. `language` chooses Chinese or English output. `autoVerify` (on) runs `ocr llm test` after you pick a provider and model, so a wrong choice fails at pick time rather than at first review. `maxComments` (default 12; 0 means all, including comments that cannot be anchored to a line). `timeoutMinutes` (0 requests the host's ceiling; a positive value is minutes, still clamped by the host). `ocrConfigPath` — empty means `~/.opencodereview/config.json`; a non-empty value must be `<dir>/.opencodereview/config.json` and is honoured by redirecting `HOME
|
|
7
|
+
- **Model** — `ocrProvider` and `ocrModel` are not stored settings: they project the host's model namespace, and entries without a usable key are shown but disabled. `chatGptModel` is a slug this plugin writes into the CLI's own config (provider `chatgpt`, with `api_key` / `api_key_cmd` removed); the credentials behind that provider come from the CLI's own login state, which the plugin only queries (`ocr auth`) and never stores.
|
|
8
|
+
- **Review parameters** — `effort` (`auto`/`low`/`medium`/`high`) is how many review passes run: deeper review costs more time. `language` chooses Chinese or English output. `autoVerify` (on) runs `ocr llm test` after you pick a provider and model, so a wrong choice fails at pick time rather than at first review. `maxComments` (default 12; 0 means all, including comments that cannot be anchored to a line). `timeoutMinutes` (0 requests the host's ceiling; a positive value is minutes, still clamped by the host). `ocrConfigPath` — empty means `~/.opencodereview/config.json`; a non-empty value must be `<dir>/.opencodereview/config.json` and is honoured by redirecting `HOME`; on Windows the value passes the shape check but the redirection does not happen, and the CLI keeps its default path.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`effort`, `maxComments`, `timeoutMinutes` and `ocrConfigPath` are read per call, so a change applies on the next review without restarting the host. `language` and `autoVerify` are read when a provider/model selection is applied: `language` is written into the CLI config at that moment, and `autoVerify` only decides whether `ocr llm test` runs then.
|
|
11
11
|
|
|
12
12
|
## Two separate things called MCP
|
|
13
13
|
|
|
@@ -15,11 +15,11 @@ The card's MCP-servers editor writes the **external `ocr` tool's own** configura
|
|
|
15
15
|
|
|
16
16
|
## Restart-only values
|
|
17
17
|
|
|
18
|
-
`llmTestTimeoutMs` (90000), `startupVerify` (on: the endpoint self-check
|
|
18
|
+
`llmTestTimeoutMs` (90000), `startupVerify` (on: the load-time endpoint self-check and the one CLI version measurement both run at apply — the switch gates any process spawn at load), `ocrBackgroundMaxMs` (0 = no backstop window), `stdoutMaxBytes` (400000), `ocrNoAutoUpdate` (false: upstream auto-upgrade stays on unless you say otherwise) and `ocrInstallHint` (`both`/`brew`/`npm`). Set them in the `config:` block of this plugin's row in `cordis.patch.yml`.
|
|
19
19
|
|
|
20
20
|
## What it needs from outside
|
|
21
21
|
|
|
22
|
-
The `ocr` command line, expected at version **1.13.
|
|
22
|
+
The `ocr` command line, expected at version **1.13.4**; the expectation is declared once as `ocrCliVersion` in `package.json`, and the code constant plus both READMEs mirror that string, so a mismatch is reported (as a hint, never a hard failure) rather than papered over. Install paths are `brew install open-code-review` or `npm i -g @alibaba-group/open-code-review@1.13.4`. With no candidate on PATH at all, the pre-check reports "no ocr candidate on either path" plus an install hint; "command not found" (exit 127) is the different case where a candidate exists but the binary cannot actually be run. The version probe always forces `OCR_NO_UPDATE=1` so probing cannot trigger an upgrade.
|
|
23
23
|
|
|
24
24
|
API credentials are not stored here: the plugin writes a `api_key_cmd` that calls a helper script, so the key is fetched at run time instead of landing in a config file. If that script is missing, the card warns rather than sending an unauthenticated request.
|
|
25
25
|
|
package/docs/DETAIL.zh.md
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
## 设置卡上改的是什么
|
|
6
6
|
|
|
7
|
-
- **模型** — `ocrProvider` 与 `ocrModel` 不是本插件存的值:它们是宿主模型命名空间的投影,没有可用密钥的条目照样列出但禁用。`chatGptModel`
|
|
8
|
-
- **审查参数** — `effort`(`auto`/`low`/`medium`/`high`)是跑几轮审查,越深越费时。`language` 选中文或英文输出。`autoVerify`(默认开)在选完提供方与模型后跑一次 `ocr llm test`,选错当场就知道,而不是等第一次评审失败。`maxComments`(默认 12;0 表示全部,包括钉不到行上的意见)。`timeoutMinutes`(0 表示向宿主要上限;正数是分钟数,仍由宿主收口)。`ocrConfigPath`——留空就是 `~/.opencodereview/config.json`;填了就必须是 `<目录>/.opencodereview/config.json` 的形状,生效方式是重定向 `HOME
|
|
7
|
+
- **模型** — `ocrProvider` 与 `ocrModel` 不是本插件存的值:它们是宿主模型命名空间的投影,没有可用密钥的条目照样列出但禁用。`chatGptModel` 是本插件写进外部命令行自己配置的 model slug(provider 置为 `chatgpt`,同时删掉 `api_key` / `api_key_cmd`);该提供方背后的凭据来自命令行自己的登录态,本插件只查询 `ocr auth`,不保存。
|
|
8
|
+
- **审查参数** — `effort`(`auto`/`low`/`medium`/`high`)是跑几轮审查,越深越费时。`language` 选中文或英文输出。`autoVerify`(默认开)在选完提供方与模型后跑一次 `ocr llm test`,选错当场就知道,而不是等第一次评审失败。`maxComments`(默认 12;0 表示全部,包括钉不到行上的意见)。`timeoutMinutes`(0 表示向宿主要上限;正数是分钟数,仍由宿主收口)。`ocrConfigPath`——留空就是 `~/.opencodereview/config.json`;填了就必须是 `<目录>/.opencodereview/config.json` 的形状,生效方式是重定向 `HOME`;Windows 上值能通过形状校验但不会重定向,命令行仍读默认路径。
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`effort`、`maxComments`、`timeoutMinutes` 与 `ocrConfigPath` 每次调用现读,改完下一次评审生效,不需要重启宿主。`language` 与 `autoVerify` 在应用一次 provider/model 选择时读取:`language` 当场写进命令行配置,`autoVerify` 只决定那时是否跑 `ocr llm test`。
|
|
11
11
|
|
|
12
12
|
## 两处都叫 MCP,但不是同一件事
|
|
13
13
|
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
|
|
16
16
|
## 要重启的值
|
|
17
17
|
|
|
18
|
-
`llmTestTimeoutMs`(90000)、`startupVerify`(默认开:装载时自检端点)、`ocrBackgroundMaxMs`(0 表示不设兜底窗口)、`stdoutMaxBytes`(400000)、`ocrNoAutoUpdate`(默认 false,即保留上游的自动升级,除非你明确关掉)、`ocrInstallHint`(`both`/`brew`/`npm
|
|
18
|
+
`llmTestTimeoutMs`(90000)、`startupVerify`(默认开:装载时自检端点)、`ocrBackgroundMaxMs`(0 表示不设兜底窗口)、`stdoutMaxBytes`(400000)、`ocrNoAutoUpdate`(默认 false,即保留上游的自动升级,除非你明确关掉)、`ocrInstallHint`(`both`/`brew`/`npm`)。写在本插件于 `cordis.patch.yml` 注册行的 `config:` 块,改值随重启生效。
|
|
19
19
|
|
|
20
20
|
## 需要外部提供什么
|
|
21
21
|
|
|
22
|
-
`ocr` 命令行,期望版本 **1.13.
|
|
22
|
+
`ocr` 命令行,期望版本 **1.13.4**;这个期望唯一声明在 `package.json` 的 `ocrCliVersion`,代码常量与两份 README 镜像同一个字符串,版本不符只作为提示报出,从不中断评审。装法是 `brew install open-code-review` 或 `npm i -g @alibaba-group/open-code-review@1.13.4`。PATH 上一个候选都没有时,预检报「no ocr candidate on either path」并附安装提示;「command not found」(exit 127)是另一种情况:候选在,但真二进制跑不起来。版本探测一律强制 `OCR_NO_UPDATE=1`,以免探测顺手触发升级。
|
|
23
23
|
|
|
24
24
|
API 凭据不存这里:本插件写的是一个 `api_key_cmd`,运行时的密钥由那支辅助脚本现取,不进配置文件。脚本缺失时卡上给出告警,而不是发一次没有鉴权的请求。
|
|
25
25
|
|