@dnalec/dsh-auto-approve 0.1.2 → 0.2.0
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 +21 -0
- package/README.md +34 -91
- package/README.zh.md +23 -83
- package/client.js +108 -219
- package/locales.mjs +164 -128
- package/package.json +5 -9
- package/src/index.mjs +179 -458
- package/src/preset-patch.mjs +3 -3
- package/src/rules.mjs +155 -54
- package/src/util.mjs +6 -70
- package/src/provisioning.mjs +0 -131
- package/src/qqbot.mjs +0 -313
- package/src/tickets.mjs +0 -277
package/client.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* 纯 JS:无 TS / JSX / import。React 用 createElement。
|
|
5
5
|
* 网页文案:locales.mjs 的 zh/en,apply 时 ctx.locale.register。
|
|
6
6
|
* Host API 走 connection.rpc.call('/api', 'dsh-auto-approve', …)(已鉴权),不用裸 HTTP。
|
|
7
|
-
* 设置页折叠状态用 React state 控 details,避免 snapshot
|
|
7
|
+
* 设置页折叠状态用 React state 控 details,避免 snapshot 刷新后合上。人工只走原网页审批框。
|
|
8
8
|
* 恢复默认:第一次点武装,5 秒内再点才执行。
|
|
9
9
|
*/
|
|
10
10
|
window.__ModuleLoader__.load({
|
|
11
|
-
id: 'dsh-auto-approve',
|
|
11
|
+
id: '@dnalec/dsh-auto-approve',
|
|
12
12
|
factory: (require) => {
|
|
13
13
|
var module = { exports: {} }
|
|
14
14
|
var exports = module.exports
|
|
@@ -74,7 +74,7 @@ window.__ModuleLoader__.load({
|
|
|
74
74
|
.ab-set-fold-body{padding:12px 14px 14px;display:flex;flex-direction:column;gap:12px}
|
|
75
75
|
.ab-set-fold-sum{flex:none;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;text-align:right;max-width:46%}
|
|
76
76
|
.ab-set-fold-chev{flex:none;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:20px}
|
|
77
|
-
.ab-set-fold[open] .ab-set-fold-chev{transform:rotate(180deg)}
|
|
77
|
+
.ab-set-fold[open]>summary .ab-set-fold-chev{transform:rotate(180deg)}
|
|
78
78
|
.ab-set-card-head{flex-direction:column;gap:4px;display:flex}
|
|
79
79
|
.ab-set-card-title{color:var(--dsw-alias-label-primary);align-items:center;gap:8px;font-size:14px;font-weight:500;line-height:20px;display:flex}
|
|
80
80
|
.ab-set-card-sub{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px;line-height:20px}
|
|
@@ -105,16 +105,12 @@ window.__ModuleLoader__.load({
|
|
|
105
105
|
.ab-set-crit-top{display:flex;align-items:center;gap:8px;min-width:0}
|
|
106
106
|
.ab-set-item-fields{min-width:0;flex-direction:column;gap:6px;display:flex}
|
|
107
107
|
.ab-set-item-id{flex:1 1 auto;min-width:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px;font-family:var(--ds-font-family-code)}
|
|
108
|
-
.ab-set-item-label{flex:1 1 auto;min-width:0;color:var(--dsw-alias-label-primary);font-size:12px;line-height:18px;font-family:var(--ds-font-family-code);word-break:break-all}
|
|
109
108
|
.ab-set-item-meta{flex:none;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px;white-space:nowrap}
|
|
110
109
|
.ab-set-item-del{flex:none;width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:transparent;border:none;border-radius:6px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;padding:0}
|
|
111
110
|
.ab-set-item-del:hover{background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}
|
|
112
111
|
.ab-set-empty{color:var(--dsw-alias-label-caption);margin:0;font-size:13px;line-height:20px;padding:4px 2px}
|
|
113
112
|
.ab-set-tag{box-sizing:border-box;flex:none;height:18px;border-radius:9px;align-items:center;padding:0 8px;font-size:11px;line-height:18px;display:inline-flex}
|
|
114
113
|
.ab-set-tag-blue{color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}
|
|
115
|
-
.ab-set-tag-green{color:var(--dsw-alias-state-success-primary);background:var(--dsw-alias-state-success-tertiary)}
|
|
116
|
-
.ab-set-tag-gray{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-module-platform)}
|
|
117
|
-
.ab-set-tag-red{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-interactive-bg-hover-danger)}
|
|
118
114
|
.ab-set-steps{display:flex;flex-wrap:wrap;gap:6px}
|
|
119
115
|
.ab-set-step{box-sizing:border-box;margin:0;border:1px solid var(--dsw-alias-border-l2);background:transparent;border-radius:10px;padding:8px 10px;min-width:108px;flex:1 1 108px;cursor:pointer;display:flex;flex-direction:column;gap:2px;align-items:flex-start;text-align:left;font:inherit;color:inherit}
|
|
120
116
|
.ab-set-step:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
@@ -131,18 +127,11 @@ window.__ModuleLoader__.load({
|
|
|
131
127
|
.ab-set-choice-on{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}
|
|
132
128
|
.ab-set-choice-t{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}
|
|
133
129
|
.ab-set-choice-d{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
134
|
-
.ab-set-learn-info{flex:1 1 auto;min-width:0;flex-direction:column;gap:2px;display:flex}
|
|
135
|
-
.ab-set-learn-sub{color:var(--dsw-alias-label-tertiary);font-size:11px}
|
|
136
|
-
.ab-chat{cursor:pointer}
|
|
137
|
-
.ab-chat:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
138
|
-
.ab-set-qr{text-align:center;padding:4px 0 8px}
|
|
139
|
-
.ab-set-qr img{box-sizing:border-box;border-radius:8px;background:#fff;padding:8px;max-width:240px;width:100%;height:auto}
|
|
140
|
-
.ab-set-qr-tip{color:var(--dsw-alias-label-tertiary);margin:6px 0 0;font-size:12px;line-height:18px}
|
|
141
130
|
`
|
|
142
131
|
|
|
143
132
|
const NS = "dsh-auto-approve"
|
|
144
|
-
const ZH = JSON.parse(String.raw`{"slot.notice":"自动放行提示","slot.history":"审批","slot.settings":"自动审批","action.reject":"拒绝","action.allow":"允许","action.human":"人工","verdict.keyword-allow":"关键词允许","verdict.keyword-reject":"关键词拒绝","path.keyword-reject":"关键词拒绝","path.keyword-allow":"关键词允许","path.keyword-human":"关键词转人工","path.criteria-reject":"审核表拒绝","path.criteria-allow":"审核表允许","path.criteria-human":"审核表转人工","path.judge-failed":"判定失败转人工","path.missing-payload":"缺少工具参数转人工","path.truncated-payload":"参数过长转人工","path.plugin-error":"插件异常转人工","notice.feedError":"审批提示暂时不可用","notice.feedErrorTag":"连接失败","notice.pendingTitle":"
|
|
145
|
-
const EN = JSON.parse(String.raw`{"slot.notice":"Auto-approve notice","slot.history":"Approvals","slot.settings":"Auto-approve","action.reject":"Reject","action.allow":"Allow","action.human":"Human","verdict.keyword-allow":"Keyword allow","verdict.keyword-reject":"Keyword reject","path.keyword-reject":"Keyword reject","path.keyword-allow":"Keyword allow","path.keyword-human":"Keyword → human","path.criteria-reject":"Criteria reject","path.criteria-allow":"Criteria allow","path.criteria-human":"Criteria → human","path.judge-failed":"Judge failed → human","path.missing-payload":"Missing tool args → human","path.truncated-payload":"Truncated args → human","path.plugin-error":"Plugin error → human","notice.feedError":"Approval notices unavailable","notice.feedErrorTag":"Connection failed","notice.pendingTitle":"Waiting for human
|
|
133
|
+
const ZH = JSON.parse(String.raw`{"slot.notice":"自动放行提示","slot.history":"审批","slot.settings":"自动审批","action.reject":"拒绝","action.allow":"允许","action.human":"人工","criterion.deletion":"删除/覆盖不可再生数据","criterion.credential":"凭据/密钥/授权修改","criterion.remote":"远程系统/生产环境/数据库","criterion.system":"系统级路径/配置","criterion.bulk":"批量不可回补操作","criterion.approval-config":"自动审批配置","criterion.safe":"安全/常规可回补","criterion.other":"其他(拿不准)","sandbox.read-only":"只读","sandbox.workspace-write":"工作区可写","sandbox.danger-full-access":"全权限","source.web":"网页","verdict.keyword-allow":"关键词允许","verdict.keyword-reject":"关键词拒绝","verdict.keyword-human":"关键词转人工","verdict.criteria-reject":"审核表拒绝","verdict.criteria-allow":"审核表允许","verdict.criteria-human":"审核表转人工","verdict.judge-failed":"判定失败转人工","verdict.missing-payload":"缺少工具参数转人工","verdict.truncated-payload":"参数过长转人工","verdict.plugin-error":"插件异常转人工","verdict.human":"转人工","verdict.cancelled":"人工取消","verdict.unavailable":"审批不可用","path.keyword-reject":"关键词拒绝","path.keyword-allow":"关键词允许","path.keyword-human":"关键词转人工","path.criteria-reject":"审核表拒绝","path.criteria-allow":"审核表允许","path.criteria-human":"审核表转人工","path.judge-failed":"判定失败转人工","path.missing-payload":"缺少工具参数转人工","path.truncated-payload":"参数过长转人工","path.plugin-error":"插件异常转人工","notice.feedError":"审批提示暂时不可用","notice.feedErrorTag":"连接失败","notice.pendingTitle":"等待人工审批:{preview}","notice.pendingTag":"人工审批中","notice.webApproved":"人工审批通过:{preview}","notice.webApprovedTag":"人工审批通过","notice.rejectedTitle":"已拒绝:{preview}","notice.rejectedTag":"已拒绝","notice.cancelledTitle":"已取消人工审批:{preview}","notice.unavailableTitle":"审批不可用:{preview}","notice.autoTag":"自动放行 · {verdict}","notice.autoDefault":"自动放行","notice.rejectedDefault":"已拒绝","notice.hint":"提示","notice.close":"关闭","notice.feedLoadFailed":"审批提示加载失败,将自动重试","history.noSession":"未选择会话","history.loadFailed":"加载失败:{error}","history.loading":"加载中…","history.title":"审批","history.emptySub":"当前会话还没有审批记录","history.emptyHint":"自动放行与转人工都会出现在这里","history.sub":"最新在上 · 点开看命令与详情","history.tagAuto":"自动放行","history.tagPending":"转人工","history.tagAllow":"人工批准","history.tagReject":"人工拒绝","history.tagCancel":"人工取消","history.tagUnavailable":"审批不可用","history.unknownTool":"工具","detail.request":"请求","detail.audit":"审核","detail.command":"命令","detail.path":"路径","detail.description":"描述","detail.old":"原文","detail.new":"改成","detail.content":"写入内容","detail.code":"代码","detail.url":"URL","detail.query":"查询","detail.script":"脚本","detail.sql":"SQL","detail.prompt":"提示词","detail.cwd":"工作目录","detail.sandbox":"沙箱","detail.justification":"模型理由","detail.pipe":"管道","detail.keyword":"关键词","detail.judgeModel":"审核模型","detail.judgeCategory":"审核类别","detail.judgeAction":"执行动作","detail.judgeReason":"审核理由","detail.judgeError":"审核失败","detail.judgeRaw":"审核结果原文","detail.source":"来源","detail.judgeFailed":"未完成(解析失败/超时转人工,不执行「其他」的动作)","set.title":"自动审批","set.intro":"需要审批的行为由审核模型自动判定。允许或拒绝与在网页点按钮相同;拿不准则交给原来的人工审批框。","set.allowlistCorrupt":"规则文件损坏,当前是内存默认,普通保存不会覆盖磁盘。请修好 allowlist.json,或点「恢复默认」写回出厂规则。","set.pluginCorrupt":"插件配置损坏,拒绝保存以免清空审核模型。请修好 ~/.dsh/auto-approve/config.json,或","set.pluginCorruptOverwrite":"覆盖损坏配置","set.loading":"加载中…","set.loadFailed":"加载失败:{error}","set.resetConfirm":"再点一次确认恢复","set.presetMissingTitle":"权限预设","set.presetMissingSub":"插件启动时会写入 auto-approve。当前未检测到,可手动补写。","set.presetWrite":"写入权限预设","set.presetWriteNote":"写入后需重启 dsh web","set.presetWrote":"已写入,请重启 dsh web","set.modeTitle":"自动审批模式","set.modeWs":"工作区可写(推荐)","set.modeWsHint":"工作区内直接放行,越出工作区需审核。","set.modeRo":"只读","set.modeRoHint":"工作区内写操作也需审核。","set.save":"保存","set.hotOk":"已生效(热更新,无需重启)","set.modeSaved":"已写入。请重启 dsh web,并重新选择「自动审批」或开新会话","set.overview":"审批总览","set.overviewSub":"关键词(拒绝优先于人工优先于允许)→ 审核表由模型归类,程序按表执行。解析失败转人工。","set.stepKeywords":"关键词","set.stepCriteria":"审核表","set.stepJudge":"审核模型","set.counts":"拒 {reject} · 人 {human} · 允 {allow}","set.criteriaCount":"{n} 项","set.followDefault":"跟随默认","set.unconfigured":"未配置","set.kwSub":"匹配命令、路径和工作目录。拒绝/人工词也匹配工具名;允许词不匹配工具名,避免把 bash/write 整类放行。","set.empty":"暂无","set.presetTag":"预置","set.kwEditTitle":"点击修改","set.kwPlaceholder":"新关键词","set.add":"添加","set.resetKeywords":"恢复默认关键词","set.resetKeywordsOk":"已恢复默认关键词","set.confirm":"确认","set.delete":"删除","set.criteriaSub":"审核模型以此表作为审核标准。「其他」作为兜底选项不能删除。","set.labelPlaceholder":"标签","set.descPlaceholder":"说明(写入审核提示词)","set.resetCriteria":"恢复默认审核表({lang})","set.resetCriteriaOk":"已恢复为{lang}默认审核表","set.addCriterion":"添加审核项","set.criterionId":"id","set.judgeTitle":"审核模型","set.judgeSubLead":"独立于当前会话模型。空则跟随部署默认","set.judgeSubFallback":"({provider} / {model})","set.judgeSubNoFallback":"(当前没有默认可跟随)","set.judgeSubTail":"。没有可用路由时当次转人工,不会再猜一个模型。建议思考强度 off。","set.followProvider":"跟随默认提供方","set.followModel":"跟随默认模型","set.modelDefaultEffort":"模型默认","set.effortOff":"off(建议)","set.judgeTimeoutMs":"审核超时(ms):","set.judgeSaved":"审核模型已保存","set.judgeLang":"提示词语言","set.judgeLangZh":"中文","set.judgeLangEn":"English","set.judgeLangHint":"只改发给审核模型的框架语言,不会改当前审核表。恢复默认审核表时使用这里选的语言。","set.overwriteOk":"已覆盖损坏的插件配置","err.missingPayload":"工具参数不完整,转人工","err.missingPayloadUncaptured":"未捕获工具参数,转人工","err.truncatedPayload":"工具参数过长已截断,转人工(禁止按前缀自动放行)","err.pluginError":"插件判定异常,转人工","err.allowlistCorrupt":"规则文件损坏,拒绝覆盖。请先「恢复默认」写回出厂规则,或修好磁盘上的 allowlist.json","err.allowlistWrite":"写入 allowlist 失败","err.criterionNotFound":"未找到该审核项","err.criterionNeedId":"需要 id 或 label","err.criterionIdExists":"id 已存在","err.criterionOtherLocked":"「其他」不可删除","err.criteriaOp":"审核表请用添加 / 修改 / 删除 / 恢复默认","err.opMustSet":"{kind} 只能用修改操作","err.invalidNumber":"无效数值","err.keywordEmpty":"关键词不能为空","err.keywordNotFound":"未找到该关键词","err.keywordsOp":"关键词请用添加 / 修改 / 删除 / 恢复默认","err.valueEmpty":"值不能为空","err.unknownKind":"未知规则类型:{kind}","err.unknownOp":"未知操作:{op}","err.ruleNotFound":"未找到匹配的规则","err.badBody":"请求体不是 JSON","err.needSessionId":"需要 sessionId","err.pluginCorrupt":"插件配置损坏,拒绝覆盖。请修好磁盘文件,或点「覆盖损坏配置」","err.pluginWrite":"写入配置失败","err.unknownEndpoint":"未知接口:{endpoint}","err.internal":"内部错误:{error}","err.catalog":"无法列出模型:{error}","err.info":"无法读取模型信息:{error}","err.judgeUnconfigured":"未配置审核模型","err.judgeEffort":"思考强度 {effort} 不受支持","err.judgeTimeout":"审核超时({ms}ms)","err.judgeRetryTimeout":"审核重试超时({ms}ms)","err.judgeFailed":"审核失败","err.judgeCall":"审核模型调用失败:{error}","err.judgeEmpty":"审核模型输出为空","err.judgeParse":"审核模型输出无法解析","err.judgeUpstream":"审核模型路由失败:{error}","err.noPresetsKey":"permission 条目缺少 presets 键,请手动添加","err.preset":"写入预设失败:{error}","rpc.unavailable":"connection.rpc 不可用","rpc.failed":"RPC 失败"}`)
|
|
134
|
+
const EN = JSON.parse(String.raw`{"slot.notice":"Auto-approve notice","slot.history":"Approvals","slot.settings":"Auto-approve","action.reject":"Reject","action.allow":"Allow","action.human":"Human","criterion.deletion":"Delete/overwrite irreplaceable data","criterion.credential":"Credentials/keys/auth changes","criterion.remote":"Remote/production/database","criterion.system":"System paths/config","criterion.bulk":"Bulk irreversible operations","criterion.approval-config":"Auto-approve configuration","criterion.safe":"Safe/routine reversible","criterion.other":"Other (unsure)","sandbox.read-only":"Read-only","sandbox.workspace-write":"Workspace write","sandbox.danger-full-access":"Full access","source.web":"Web","verdict.keyword-allow":"Keyword allow","verdict.keyword-reject":"Keyword reject","verdict.keyword-human":"Keyword → human","verdict.criteria-reject":"Criteria reject","verdict.criteria-allow":"Criteria allow","verdict.criteria-human":"Criteria → human","verdict.judge-failed":"Judge failed → human","verdict.missing-payload":"Missing tool args → human","verdict.truncated-payload":"Truncated args → human","verdict.plugin-error":"Plugin error → human","verdict.human":"To human","verdict.cancelled":"Human cancel","verdict.unavailable":"Unavailable","path.keyword-reject":"Keyword reject","path.keyword-allow":"Keyword allow","path.keyword-human":"Keyword → human","path.criteria-reject":"Criteria reject","path.criteria-allow":"Criteria allow","path.criteria-human":"Criteria → human","path.judge-failed":"Judge failed → human","path.missing-payload":"Missing tool args → human","path.truncated-payload":"Truncated args → human","path.plugin-error":"Plugin error → human","notice.feedError":"Approval notices unavailable","notice.feedErrorTag":"Connection failed","notice.pendingTitle":"Waiting for human: {preview}","notice.pendingTag":"Human review","notice.webApproved":"Approved: {preview}","notice.webApprovedTag":"Approved","notice.rejectedTitle":"Rejected: {preview}","notice.rejectedTag":"Rejected","notice.cancelledTitle":"Cancelled: {preview}","notice.unavailableTitle":"Unavailable: {preview}","notice.autoTag":"Auto-allowed · {verdict}","notice.autoDefault":"Auto-allowed","notice.rejectedDefault":"Rejected","notice.hint":"Notice","notice.close":"Dismiss","notice.feedLoadFailed":"Approval notices failed to load; retrying","history.noSession":"No session selected","history.loadFailed":"Failed to load: {error}","history.loading":"Loading…","history.title":"Approvals","history.emptySub":"This session has no approval records yet","history.emptyHint":"Auto-allows and human escalations appear here","history.sub":"Newest first · expand for command and details","history.tagAuto":"Auto-allowed","history.tagPending":"To human","history.tagAllow":"Human allow","history.tagReject":"Human reject","history.tagCancel":"Human cancel","history.tagUnavailable":"Unavailable","history.unknownTool":"Tool","detail.request":"Request","detail.audit":"Review","detail.command":"Command","detail.path":"Path","detail.description":"Description","detail.old":"Original","detail.new":"Replacement","detail.content":"Write contents","detail.code":"Code","detail.url":"URL","detail.query":"Query","detail.script":"Script","detail.sql":"SQL","detail.prompt":"Prompt","detail.cwd":"Working directory","detail.sandbox":"Sandbox","detail.justification":"Model justification","detail.pipe":"Pipeline","detail.keyword":"Keyword","detail.judgeModel":"Judge model","detail.judgeCategory":"Category","detail.judgeAction":"Action","detail.judgeReason":"Judge reason","detail.judgeError":"Judge error","detail.judgeRaw":"Judge raw output","detail.source":"Source","detail.judgeFailed":"Incomplete (parse failure/timeout → human; does not run Other)","set.title":"Auto-approve","set.intro":"Actions that need approval are judged automatically. Allow and reject match clicking the Web buttons; uncertainty goes to the original human dialog.","set.allowlistCorrupt":"The rules file is corrupt. This process is using in-memory defaults and will not overwrite the disk. Repair allowlist.json, or Restore defaults to write shipped rules.","set.pluginCorrupt":"Plugin config is corrupt; saves are refused so the judge model is not wiped. Repair ~/.dsh/auto-approve/config.json, or ","set.pluginCorruptOverwrite":"Overwrite corrupt config","set.loading":"Loading…","set.loadFailed":"Failed to load: {error}","set.resetConfirm":"Click again to confirm restore","set.presetMissingTitle":"Permission preset","set.presetMissingSub":"Startup writes auto-approve. It was not detected; you can write it now.","set.presetWrite":"Write permission preset","set.presetWriteNote":"Restart dsh web after writing","set.presetWrote":"Written. Restart dsh web","set.modeTitle":"Auto-approve mode","set.modeWs":"Workspace write (recommended)","set.modeWsHint":"In-workspace writes skip approval; outside the workspace still goes through the judge.","set.modeRo":"Read-only","set.modeRoHint":"In-workspace writes also go through the judge.","set.save":"Save","set.hotOk":"Applied (live; no restart)","set.modeSaved":"Written. Restart dsh web and re-select Auto-approve or start a new session","set.overview":"Approval overview","set.overviewSub":"Keywords (reject > human > allow) → the judge classifies; the table decides. Parse failure → human.","set.stepKeywords":"Keywords","set.stepCriteria":"Criteria","set.stepJudge":"Judge model","set.counts":"rej {reject} · hum {human} · all {allow}","set.criteriaCount":"{n} rows","set.followDefault":"Follow default","set.unconfigured":"Not configured","set.kwSub":"Match command, path, and workdir. Reject/human also match the tool name; allow keywords do not, so bash/write is not allowed as a class.","set.empty":"None","set.presetTag":"Shipped","set.kwEditTitle":"Click to edit","set.kwPlaceholder":"New keyword","set.add":"Add","set.resetKeywords":"Restore default keywords","set.resetKeywordsOk":"Default keywords restored","set.confirm":"Confirm","set.delete":"Delete","set.criteriaSub":"The judge uses this table. Other cannot be deleted.","set.labelPlaceholder":"Label","set.descPlaceholder":"Description (goes into the judge prompt)","set.resetCriteria":"Restore default criteria ({lang})","set.resetCriteriaOk":"Restored {lang} default criteria","set.addCriterion":"Add criterion","set.criterionId":"id","set.judgeTitle":"Judge model","set.judgeSubLead":"Independent of the session model. Empty follows the deployment default","set.judgeSubFallback":" ({provider} / {model})","set.judgeSubNoFallback":" (no default to follow)","set.judgeSubTail":". With no usable route, that request goes to a human; the plugin will not guess a model. Prefer effort off.","set.followProvider":"Follow default provider","set.followModel":"Follow default model","set.modelDefaultEffort":"Model default","set.effortOff":"off (recommended)","set.judgeTimeoutMs":"Judge timeout (ms):","set.judgeSaved":"Judge model saved","set.judgeLang":"Prompt language","set.judgeLangZh":"Chinese","set.judgeLangEn":"English","set.judgeLangHint":"Changes only the judge prompt framework, not the current criteria table. Restore default criteria uses this language.","set.overwriteOk":"Corrupt plugin config overwritten","err.missingPayload":"Tool arguments are incomplete; sending to human","err.missingPayloadUncaptured":"Tool arguments were not captured; sending to human","err.truncatedPayload":"Tool arguments were truncated; sending to human (no prefix auto-allow)","err.pluginError":"Plugin error; sending to human","err.allowlistCorrupt":"Rules file is corrupt; refusing to overwrite. Restore defaults or fix allowlist.json on disk.","err.allowlistWrite":"Failed to write allowlist","err.criterionNotFound":"Criterion not found","err.criterionNeedId":"id or label is required","err.criterionIdExists":"id already exists","err.criterionOtherLocked":"“Other” cannot be deleted","err.criteriaOp":"Criteria accept add / set / remove / reset","err.opMustSet":"{kind} only accepts set","err.invalidNumber":"Invalid number","err.keywordEmpty":"Keyword cannot be empty","err.keywordNotFound":"Keyword not found","err.keywordsOp":"Keywords accept add / set / remove / reset","err.valueEmpty":"Value cannot be empty","err.unknownKind":"Unknown rule kind: {kind}","err.unknownOp":"Unknown operation: {op}","err.ruleNotFound":"No matching rule","err.badBody":"Request body is not JSON","err.needSessionId":"sessionId is required","err.pluginCorrupt":"Plugin config is corrupt; refusing to overwrite. Fix the file on disk, or overwrite the corrupt config.","err.pluginWrite":"Failed to write plugin config","err.unknownEndpoint":"Unknown endpoint: {endpoint}","err.internal":"Internal error: {error}","err.catalog":"Could not list models: {error}","err.info":"Could not load model info: {error}","err.judgeUnconfigured":"Judge model is not configured","err.judgeEffort":"reasoningEffort {effort} is not supported","err.judgeTimeout":"Judge timed out ({ms}ms)","err.judgeRetryTimeout":"Judge retry timed out ({ms}ms)","err.judgeFailed":"Judge failed","err.judgeCall":"Judge call failed: {error}","err.judgeEmpty":"Judge output was empty","err.judgeParse":"Judge output could not be parsed","err.judgeUpstream":"Judge route failed: {error}","err.noPresetsKey":"The permission entry has no presets key; add it manually","err.preset":"Failed to write preset: {error}","rpc.unavailable":"connection.rpc is unavailable","rpc.failed":"RPC failed"}`)
|
|
146
135
|
|
|
147
136
|
function fillLocale(template, params) {
|
|
148
137
|
if (!params) return template
|
|
@@ -152,6 +141,7 @@ window.__ModuleLoader__.load({
|
|
|
152
141
|
}
|
|
153
142
|
|
|
154
143
|
function tFrom(props) {
|
|
144
|
+
if (props && typeof props.t === 'function') return props.t
|
|
155
145
|
const sp = (props && props.slotsProps) || {}
|
|
156
146
|
if (typeof sp.t === 'function') return sp.t
|
|
157
147
|
return function (key, params) {
|
|
@@ -159,42 +149,39 @@ window.__ModuleLoader__.load({
|
|
|
159
149
|
}
|
|
160
150
|
}
|
|
161
151
|
|
|
152
|
+
function lookupLabel(t, prefix, id) {
|
|
153
|
+
if (!id) return ''
|
|
154
|
+
const key = prefix + '.' + id
|
|
155
|
+
const v = t(key)
|
|
156
|
+
return v === key ? '' : v
|
|
157
|
+
}
|
|
158
|
+
|
|
162
159
|
function verdictLabel(t, verdict) {
|
|
163
160
|
if (!verdict) return t('notice.autoDefault')
|
|
164
|
-
|
|
165
|
-
const v = t(key)
|
|
166
|
-
return v === key ? String(verdict) : v
|
|
161
|
+
return lookupLabel(t, 'verdict', verdict) || lookupLabel(t, 'path', verdict) || String(verdict)
|
|
167
162
|
}
|
|
168
163
|
|
|
169
164
|
function pathLabel(t, path) {
|
|
170
|
-
|
|
171
|
-
const key = 'path.' + path
|
|
172
|
-
const v = t(key)
|
|
173
|
-
return v === key ? String(path) : v
|
|
165
|
+
return lookupLabel(t, 'path', path) || lookupLabel(t, 'verdict', path) || (path ? String(path) : '')
|
|
174
166
|
}
|
|
175
167
|
|
|
176
168
|
function actionLabel(t, action) {
|
|
177
|
-
|
|
178
|
-
const key = 'action.' + action
|
|
179
|
-
const v = t(key)
|
|
180
|
-
return v === key ? String(action) : v
|
|
169
|
+
return lookupLabel(t, 'action', action) || (action ? String(action) : '')
|
|
181
170
|
}
|
|
182
171
|
|
|
183
|
-
function
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
loggingIn: 'set.prov.loggingIn',
|
|
190
|
-
waitScan: 'set.prov.waitScan',
|
|
191
|
-
saving: 'set.prov.saving',
|
|
192
|
-
connected: 'set.prov.connected',
|
|
193
|
-
}
|
|
194
|
-
const key = map[status]
|
|
195
|
-
return key ? t(key) : String(status || '')
|
|
172
|
+
function criterionLabel(t, criterion, storedLabel) {
|
|
173
|
+
return lookupLabel(t, 'criterion', criterion) || storedLabel || (criterion ? String(criterion) : '')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function sandboxLabel(t, mode) {
|
|
177
|
+
return lookupLabel(t, 'sandbox', mode) || (mode ? String(mode) : '')
|
|
196
178
|
}
|
|
197
179
|
|
|
180
|
+
function sourceLabel(t, source) {
|
|
181
|
+
return lookupLabel(t, 'source', source) || (source ? String(source) : '')
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
198
185
|
function countsLabel(t, reject, human, allow) {
|
|
199
186
|
return t('set.counts', { reject: String(reject), human: String(human), allow: String(allow) })
|
|
200
187
|
}
|
|
@@ -276,18 +263,37 @@ window.__ModuleLoader__.load({
|
|
|
276
263
|
}
|
|
277
264
|
|
|
278
265
|
function latestUnsettledPending(evs) {
|
|
279
|
-
const settled = Object.create(null)
|
|
280
266
|
for (let i = evs.length - 1; i >= 0; i--) {
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
if (kind === 'manual-
|
|
284
|
-
if (ev.ticket != null) settled[ev.ticket] = true
|
|
285
|
-
}
|
|
286
|
-
if (kind === 'manual-pending' && (ev.ticket == null || !settled[ev.ticket])) return ev
|
|
267
|
+
const kind = evs[i].kind || ''
|
|
268
|
+
if (kind === 'manual-approved' || kind === 'manual-rejected' || kind === 'manual-cancelled' || kind === 'manual-unavailable') return null
|
|
269
|
+
if (kind === 'manual-pending') return evs[i]
|
|
287
270
|
}
|
|
288
271
|
return null
|
|
289
272
|
}
|
|
290
273
|
|
|
274
|
+
|
|
275
|
+
function formatErr(t, code, details) {
|
|
276
|
+
if (!code) return ''
|
|
277
|
+
const d = details && typeof details === 'object' ? details : {}
|
|
278
|
+
const raw = String(code)
|
|
279
|
+
const key = raw.indexOf('err.') === 0 || raw.indexOf('rpc.') === 0 ? raw : 'err.' + raw
|
|
280
|
+
const v = t(key, d)
|
|
281
|
+
if (v && v !== key) return v
|
|
282
|
+
if (raw.indexOf('err.') !== 0 && raw.indexOf('rpc.') !== 0) {
|
|
283
|
+
const direct = t(raw, d)
|
|
284
|
+
if (direct && direct !== raw) return direct
|
|
285
|
+
}
|
|
286
|
+
if (d.error) return String(d.error)
|
|
287
|
+
return raw
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function codedText(t, text, details) {
|
|
291
|
+
if (!text) return ''
|
|
292
|
+
const s = String(text)
|
|
293
|
+
if (s.indexOf('err.') === 0 || s.indexOf('rpc.') === 0) return formatErr(t, s, details || {}) || s
|
|
294
|
+
return s
|
|
295
|
+
}
|
|
296
|
+
|
|
291
297
|
function makeRpc(connection, t) {
|
|
292
298
|
const tr = typeof t === 'function' ? t : function (key) { return ZH[key] || key }
|
|
293
299
|
return function rpc(endpoint, payload) {
|
|
@@ -299,8 +305,8 @@ window.__ModuleLoader__.load({
|
|
|
299
305
|
payload: payload || {},
|
|
300
306
|
}).then(function (r) {
|
|
301
307
|
if (!r || r.ok === false) {
|
|
302
|
-
const
|
|
303
|
-
throw new Error(
|
|
308
|
+
const err = r && r.error
|
|
309
|
+
throw new Error(formatErr(tr, err && err.code, err && (err.details || {})) || tr('rpc.failed'))
|
|
304
310
|
}
|
|
305
311
|
return r.value
|
|
306
312
|
})
|
|
@@ -381,8 +387,7 @@ window.__ModuleLoader__.load({
|
|
|
381
387
|
if (!notice) return null
|
|
382
388
|
const kind = notice.kind || 'auto'
|
|
383
389
|
const isPending = kind === 'manual-pending'
|
|
384
|
-
const isManual = kind === 'manual-approved' || kind === 'manual-rejected'
|
|
385
|
-
const ticket = notice.ticket ? (' #' + notice.ticket) : ''
|
|
390
|
+
const isManual = kind === 'manual-approved' || kind === 'manual-rejected' || kind === 'manual-cancelled' || kind === 'manual-unavailable'
|
|
386
391
|
let title = ''
|
|
387
392
|
let tagText = ''
|
|
388
393
|
let glyph = null
|
|
@@ -391,17 +396,25 @@ window.__ModuleLoader__.load({
|
|
|
391
396
|
tagText = t('notice.feedErrorTag')
|
|
392
397
|
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '!')
|
|
393
398
|
} else if (isPending) {
|
|
394
|
-
title = t('notice.pendingTitle', {
|
|
395
|
-
tagText = t('notice.pendingTag'
|
|
399
|
+
title = t('notice.pendingTitle', { preview: eventPreview(notice) })
|
|
400
|
+
tagText = t('notice.pendingTag')
|
|
396
401
|
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '◔')
|
|
397
402
|
} else if (kind === 'manual-approved') {
|
|
398
|
-
title = t(
|
|
399
|
-
tagText = t(
|
|
403
|
+
title = t('notice.webApproved', { preview: eventPreview(notice) })
|
|
404
|
+
tagText = t('notice.webApprovedTag')
|
|
400
405
|
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '✓')
|
|
406
|
+
} else if (kind === 'manual-cancelled') {
|
|
407
|
+
title = t('notice.cancelledTitle', { preview: eventPreview(notice) })
|
|
408
|
+
tagText = t('history.tagCancel')
|
|
409
|
+
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '✕')
|
|
410
|
+
} else if (kind === 'manual-unavailable') {
|
|
411
|
+
title = t('notice.unavailableTitle', { preview: eventPreview(notice) })
|
|
412
|
+
tagText = t('history.tagUnavailable')
|
|
413
|
+
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '!')
|
|
401
414
|
} else if (kind === 'manual-rejected' || isAutoReject(notice.verdict)) {
|
|
402
|
-
title = t('notice.rejectedTitle', {
|
|
415
|
+
title = t('notice.rejectedTitle', { preview: eventPreview(notice) })
|
|
403
416
|
tagText = kind === 'manual-rejected'
|
|
404
|
-
? t('notice.rejectedTag'
|
|
417
|
+
? t('notice.rejectedTag')
|
|
405
418
|
: (verdictLabel(t, notice.verdict) || t('notice.rejectedDefault'))
|
|
406
419
|
glyph = React.createElement('span', { className: 'ab-notice-glyph-err' }, '✕')
|
|
407
420
|
} else {
|
|
@@ -417,11 +430,11 @@ window.__ModuleLoader__.load({
|
|
|
417
430
|
React.createElement('span', { className: 'ab-notice-glyph' }, glyph),
|
|
418
431
|
React.createElement('div', { className: 'ab-notice-body' },
|
|
419
432
|
React.createElement('div', { className: 'ab-notice-head' },
|
|
420
|
-
React.createElement('span', { className: 'ab-notice-tool' }, notice.tool || (kind === 'feed-error' ? t('notice.hint') : '
|
|
433
|
+
React.createElement('span', { className: 'ab-notice-tool' }, notice.tool || (kind === 'feed-error' ? t('notice.hint') : t('history.unknownTool'))),
|
|
421
434
|
React.createElement('span', { className: 'ab-notice-text' }, title),
|
|
422
435
|
),
|
|
423
436
|
React.createElement('div', { className: 'ab-notice-meta' },
|
|
424
|
-
React.createElement('span', { className: isPending ? 'ab-tag-warn' : (kind === 'manual-rejected' || isAutoReject(notice.verdict) || kind === 'feed-error') ? 'ab-tag-err' :
|
|
437
|
+
React.createElement('span', { className: isPending || kind === 'manual-approved' || kind === 'manual-cancelled' || kind === 'manual-unavailable' ? 'ab-tag-warn' : (kind === 'manual-rejected' || isAutoReject(notice.verdict) || kind === 'feed-error') ? 'ab-tag-err' : 'ab-tag' }, tagText),
|
|
425
438
|
React.createElement('span', { className: 'ab-time' }, fmtTime(notice.ts)),
|
|
426
439
|
),
|
|
427
440
|
),
|
|
@@ -437,7 +450,7 @@ window.__ModuleLoader__.load({
|
|
|
437
450
|
function HistoryView(props) {
|
|
438
451
|
const t = tFrom(props)
|
|
439
452
|
const rpc = props.rpc
|
|
440
|
-
const sessionId = (props &&
|
|
453
|
+
const sessionId = (props && props.sessionId) || resolveFrame(props.slotsProps || props).sessionId || null
|
|
441
454
|
const [events, setEvents] = React.useState(null)
|
|
442
455
|
const [error, setError] = React.useState(null)
|
|
443
456
|
const [openId, setOpenId] = React.useState(null)
|
|
@@ -481,22 +494,29 @@ window.__ModuleLoader__.load({
|
|
|
481
494
|
React.createElement('div', { className: 'ab-list' },
|
|
482
495
|
events.map(function (ev) {
|
|
483
496
|
const kind = ev.kind || 'auto'
|
|
484
|
-
const ticket = ev.ticket ? '#' + ev.ticket : ''
|
|
485
497
|
let glyph = React.createElement(GlyphCheck, null)
|
|
486
498
|
let tag = t('history.tagAuto')
|
|
487
499
|
let tagCls = 'ab-tag'
|
|
488
500
|
if (kind === 'manual-pending') {
|
|
489
501
|
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '◔')
|
|
490
|
-
tag = t('history.tagPending'
|
|
502
|
+
tag = t('history.tagPending')
|
|
491
503
|
tagCls = 'ab-tag-warn'
|
|
492
504
|
} else if (kind === 'manual-approved') {
|
|
493
505
|
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '✓')
|
|
494
|
-
tag = t(
|
|
506
|
+
tag = t('history.tagAllow')
|
|
495
507
|
tagCls = 'ab-tag-warn'
|
|
496
508
|
} else if (kind === 'manual-rejected') {
|
|
497
509
|
glyph = React.createElement('span', { className: 'ab-notice-glyph-err' }, '✕')
|
|
498
|
-
tag = t('history.tagReject'
|
|
510
|
+
tag = t('history.tagReject')
|
|
499
511
|
tagCls = 'ab-tag-err'
|
|
512
|
+
} else if (kind === 'manual-cancelled') {
|
|
513
|
+
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '✕')
|
|
514
|
+
tag = t('history.tagCancel')
|
|
515
|
+
tagCls = 'ab-tag-warn'
|
|
516
|
+
} else if (kind === 'manual-unavailable') {
|
|
517
|
+
glyph = React.createElement('span', { className: 'ab-notice-glyph-warn' }, '!')
|
|
518
|
+
tag = t('history.tagUnavailable')
|
|
519
|
+
tagCls = 'ab-tag-warn'
|
|
500
520
|
} else if (isAutoReject(ev.verdict)) {
|
|
501
521
|
glyph = React.createElement('span', { className: 'ab-notice-glyph-err' }, '✕')
|
|
502
522
|
tag = verdictLabel(t, ev.verdict) || t('notice.rejectedDefault')
|
|
@@ -522,7 +542,7 @@ window.__ModuleLoader__.load({
|
|
|
522
542
|
onClick: function () { setOpenId(open ? null : ev.id) },
|
|
523
543
|
},
|
|
524
544
|
React.createElement('div', { className: 'ab-row-top' },
|
|
525
|
-
React.createElement('span', { className: 'ab-row-tool' }, ev.tool || '
|
|
545
|
+
React.createElement('span', { className: 'ab-row-tool' }, ev.tool || t('history.unknownTool')),
|
|
526
546
|
React.createElement('span', { className: tagCls }, tag),
|
|
527
547
|
React.createElement('span', { className: 'ab-time' }, fmtTime(ev.ts)),
|
|
528
548
|
React.createElement('span', { className: 'ab-row-chev' }, '▾'),
|
|
@@ -545,7 +565,7 @@ window.__ModuleLoader__.load({
|
|
|
545
565
|
[t('detail.sql'), args.sql],
|
|
546
566
|
[t('detail.prompt'), args.prompt],
|
|
547
567
|
[t('detail.cwd'), ev.cwd],
|
|
548
|
-
[t('detail.sandbox'), ev.mode],
|
|
568
|
+
[t('detail.sandbox'), sandboxLabel(t, ev.mode)],
|
|
549
569
|
[t('detail.justification'), ev.justification || ev.reason],
|
|
550
570
|
]),
|
|
551
571
|
DetailSection(t('detail.audit'), [
|
|
@@ -554,13 +574,12 @@ window.__ModuleLoader__.load({
|
|
|
554
574
|
[t('detail.judgeModel'), [j.provider, j.model, j.effort].filter(Boolean).join(' / ')],
|
|
555
575
|
[t('detail.judgeCategory'), j.failed
|
|
556
576
|
? t('detail.judgeFailed')
|
|
557
|
-
: (
|
|
577
|
+
: criterionLabel(t, j.criterion || ev.category, j.label)],
|
|
558
578
|
[t('detail.judgeAction'), actionLabel(t, j.action) || j.action],
|
|
559
|
-
[t('detail.judgeReason'), j.reason || ev.judgeReason],
|
|
560
|
-
[t('detail.judgeError'), j.error],
|
|
579
|
+
[t('detail.judgeReason'), codedText(t, j.reason || ev.judgeReason)],
|
|
580
|
+
[t('detail.judgeError'), formatErr(t, j.errorCode || j.error, { ms: j.errorMs || '', error: j.errorDetail || '', effort: j.errorEffort || '' })],
|
|
561
581
|
[t('detail.judgeRaw'), j.raw],
|
|
562
|
-
[t('detail.
|
|
563
|
-
[t('detail.source'), ev.source],
|
|
582
|
+
[t('detail.source'), sourceLabel(t, ev.source)],
|
|
564
583
|
]),
|
|
565
584
|
)
|
|
566
585
|
: null,
|
|
@@ -571,7 +590,7 @@ window.__ModuleLoader__.load({
|
|
|
571
590
|
)
|
|
572
591
|
}
|
|
573
592
|
|
|
574
|
-
/** 设置 →
|
|
593
|
+
/** 设置 → 自动审批。恢复默认需连点两次。 */
|
|
575
594
|
function SettingsPage(props) {
|
|
576
595
|
const t = tFrom(props)
|
|
577
596
|
const rpc = props.rpc
|
|
@@ -584,11 +603,6 @@ window.__ModuleLoader__.load({
|
|
|
584
603
|
const [newKeywordAction, setNewKeywordAction] = React.useState('human')
|
|
585
604
|
const [newCriterion, setNewCriterion] = React.useState({ id: '', label: '', description: '', action: 'human' })
|
|
586
605
|
const [timeoutMs, setTimeoutMs] = React.useState('20000')
|
|
587
|
-
const [appId, setAppId] = React.useState('')
|
|
588
|
-
const [appSecret, setAppSecret] = React.useState('')
|
|
589
|
-
const [chatIdInput, setChatIdInput] = React.useState('')
|
|
590
|
-
const [userIdInput, setUserIdInput] = React.useState('')
|
|
591
|
-
const [timeoutSecs, setTimeoutSecs] = React.useState('120')
|
|
592
606
|
const [models, setModels] = React.useState([])
|
|
593
607
|
const [efforts, setEfforts] = React.useState([])
|
|
594
608
|
const [judgeProvider, setJudgeProvider] = React.useState('')
|
|
@@ -596,10 +610,15 @@ window.__ModuleLoader__.load({
|
|
|
596
610
|
const [judgeEffort, setJudgeEffort] = React.useState('')
|
|
597
611
|
const [judgePromptLang, setJudgePromptLang] = React.useState('zh')
|
|
598
612
|
const [presetSandbox, setPresetSandbox] = React.useState('workspace-write')
|
|
599
|
-
const [foldOpen, setFoldOpen] = React.useState({ keywords: false, criteria: false
|
|
613
|
+
const [foldOpen, setFoldOpen] = React.useState({ keywords: false, criteria: false })
|
|
600
614
|
const [kwEdit, setKwEdit] = React.useState(null)
|
|
601
615
|
const [resetArmed, setResetArmed] = React.useState(null)
|
|
602
|
-
|
|
616
|
+
const feedbackTimerRef = React.useRef(null)
|
|
617
|
+
React.useEffect(function () {
|
|
618
|
+
return function () {
|
|
619
|
+
if (feedbackTimerRef.current) clearTimeout(feedbackTimerRef.current)
|
|
620
|
+
}
|
|
621
|
+
}, [])
|
|
603
622
|
const load = function (opts) {
|
|
604
623
|
const keepEdits = opts && opts.keepEdits
|
|
605
624
|
rpc('snapshot').then(function (data) {
|
|
@@ -611,9 +630,6 @@ window.__ModuleLoader__.load({
|
|
|
611
630
|
setJudgeEffort((data.plugin.judge && data.plugin.judge.reasoningEffort) || '')
|
|
612
631
|
setJudgePromptLang((data.plugin && data.plugin.judgePromptLang) === 'en' ? 'en' : 'zh')
|
|
613
632
|
setPresetSandbox((data.plugin && data.plugin.presetSandbox) === 'read-only' ? 'read-only' : 'workspace-write')
|
|
614
|
-
setChatIdInput((data.plugin.notify && data.plugin.notify.chatId) || '')
|
|
615
|
-
setUserIdInput((data.plugin.notify && data.plugin.notify.userId) || '')
|
|
616
|
-
setTimeoutSecs(String((data.plugin.notify && data.plugin.notify.timeoutSecs) || 120))
|
|
617
633
|
}
|
|
618
634
|
setError(null)
|
|
619
635
|
}).catch(function (e) {
|
|
@@ -627,23 +643,6 @@ window.__ModuleLoader__.load({
|
|
|
627
643
|
return function () { clearTimeout(disarm) }
|
|
628
644
|
}, [resetArmed])
|
|
629
645
|
|
|
630
|
-
const provisioning = snapshot && snapshot.qq && snapshot.qq.provisioning ? snapshot.qq.provisioning : null
|
|
631
|
-
const provisioningStatus = provisioning && provisioning.status ? provisioning.status : ''
|
|
632
|
-
const provisioningActive = provisioningStatus === '登录中' || provisioningStatus === '等待扫码' || provisioningStatus === '保存凭据'
|
|
633
|
-
const qqOpenedForProvision = React.useRef(false)
|
|
634
|
-
React.useEffect(function () {
|
|
635
|
-
if (provisioningActive && !qqOpenedForProvision.current) {
|
|
636
|
-
qqOpenedForProvision.current = true
|
|
637
|
-
setFoldOpen(function (s) { return Object.assign({}, s, { qq: true }) })
|
|
638
|
-
}
|
|
639
|
-
if (!provisioningActive) qqOpenedForProvision.current = false
|
|
640
|
-
}, [provisioningActive])
|
|
641
|
-
React.useEffect(function () {
|
|
642
|
-
if (!provisioningActive) return
|
|
643
|
-
const timer = setInterval(function () { load({ keepEdits: true }) }, 2000)
|
|
644
|
-
return function () { clearInterval(timer) }
|
|
645
|
-
}, [provisioningActive])
|
|
646
|
-
|
|
647
646
|
React.useEffect(function () {
|
|
648
647
|
const provider = judgeProvider || (snapshot && snapshot.fallback && snapshot.fallback.provider)
|
|
649
648
|
if (!provider || !rpc) return
|
|
@@ -663,7 +662,8 @@ window.__ModuleLoader__.load({
|
|
|
663
662
|
|
|
664
663
|
const showFeedback = function (msg, ok) {
|
|
665
664
|
setFeedback({ msg: String(msg), ok: ok !== false })
|
|
666
|
-
|
|
665
|
+
if (feedbackTimerRef.current) clearTimeout(feedbackTimerRef.current)
|
|
666
|
+
feedbackTimerRef.current = setTimeout(function () { setFeedback(null) }, 4000)
|
|
667
667
|
}
|
|
668
668
|
|
|
669
669
|
const run = function (endpoint, payload, okMsg) {
|
|
@@ -686,9 +686,7 @@ window.__ModuleLoader__.load({
|
|
|
686
686
|
const setup = snapshot.setup || { configured: false }
|
|
687
687
|
const predefined = snapshot.predefined || {}
|
|
688
688
|
const preDeny = new Set(predefined.rejectKeywords || predefined.denyKeywords || [])
|
|
689
|
-
const qq = snapshot.qq || {}
|
|
690
689
|
const plugin = snapshot.plugin || {}
|
|
691
|
-
const notify = plugin.notify || {}
|
|
692
690
|
const providers = snapshot.providers || []
|
|
693
691
|
const fallback = snapshot.fallback || {}
|
|
694
692
|
const judgeTimeoutLabel = Math.round((Number(cfg.judgeTimeoutMs) || 20000) / 1000) + 's'
|
|
@@ -743,10 +741,6 @@ window.__ModuleLoader__.load({
|
|
|
743
741
|
}, t('set.pluginCorruptOverwrite')),
|
|
744
742
|
)
|
|
745
743
|
: null,
|
|
746
|
-
(snapshot.qq && snapshot.qq.credsCorrupt)
|
|
747
|
-
? React.createElement('div', { className: 'ab-set-err' }, t('set.qqCorrupt'))
|
|
748
|
-
: null,
|
|
749
|
-
|
|
750
744
|
!setup.configured ? React.createElement('div', { className: 'ab-set-card' },
|
|
751
745
|
React.createElement('div', { className: 'ab-set-card-head' },
|
|
752
746
|
React.createElement('div', { className: 'ab-set-card-title' }, t('set.presetMissingTitle')),
|
|
@@ -822,7 +816,7 @@ window.__ModuleLoader__.load({
|
|
|
822
816
|
return React.createElement('span', {
|
|
823
817
|
className: 'ab-set-inv ' + (c.action === 'reject' ? 'ab-set-inv-hard' : c.action === 'allow' ? 'ab-set-inv-ok' : 'ab-set-inv-mode'),
|
|
824
818
|
key: c.id, title: c.id,
|
|
825
|
-
}, (c.label || c.id) + ' · ' + (actionLabel(t, c.action) || c.action))
|
|
819
|
+
}, (criterionLabel(t, c.id, c.label) || c.id) + ' · ' + (actionLabel(t, c.action) || c.action))
|
|
826
820
|
}),
|
|
827
821
|
),
|
|
828
822
|
),
|
|
@@ -961,7 +955,7 @@ window.__ModuleLoader__.load({
|
|
|
961
955
|
}
|
|
962
956
|
return React.createElement('div', { className: 'ab-set-item ab-set-item-crit', key: c.id },
|
|
963
957
|
React.createElement('div', { className: 'ab-set-crit-top' },
|
|
964
|
-
React.createElement('span', { className: 'ab-set-item-id' }, c.id),
|
|
958
|
+
React.createElement('span', { className: 'ab-set-item-id', title: c.id }, criterionLabel(t, c.id) || c.id),
|
|
965
959
|
React.createElement('select', {
|
|
966
960
|
className: 'ab-set-select', value: c.action || 'human',
|
|
967
961
|
onChange: function (e) { commitCrit({ action: e.target.value }) },
|
|
@@ -984,7 +978,7 @@ window.__ModuleLoader__.load({
|
|
|
984
978
|
},
|
|
985
979
|
}, resetArmed === ('del-c:' + c.id) ? t('set.confirm') : '✕'),
|
|
986
980
|
),
|
|
987
|
-
React.createElement('div', { className: 'ab-set-item-fields' },
|
|
981
|
+
React.createElement('div', { className: 'ab-set-item-fields', key: c.id + ':' + (c.label || '') + ':' + (c.description || '') },
|
|
988
982
|
React.createElement('input', {
|
|
989
983
|
className: 'ab-set-input ab-set-input-ui',
|
|
990
984
|
defaultValue: c.label || '',
|
|
@@ -1136,113 +1130,8 @@ window.__ModuleLoader__.load({
|
|
|
1136
1130
|
),
|
|
1137
1131
|
),
|
|
1138
1132
|
|
|
1139
|
-
|
|
1140
|
-
className: 'ab-set-card ab-set-fold', 'data-ab-stage': 'qq',
|
|
1141
|
-
open: foldOpen.qq,
|
|
1142
|
-
onToggle: function (e) {
|
|
1143
|
-
const open = e.currentTarget.open
|
|
1144
|
-
setFoldOpen(function (prev) { return Object.assign({}, prev, { qq: open }) })
|
|
1145
|
-
},
|
|
1146
|
-
},
|
|
1147
|
-
React.createElement('summary', null,
|
|
1148
|
-
React.createElement('div', { className: 'ab-set-card-head' },
|
|
1149
|
-
React.createElement('div', { className: 'ab-set-card-title' },
|
|
1150
|
-
t('set.qqTitle'),
|
|
1151
|
-
React.createElement('span', { className: 'ab-set-fold-chev' }, '▾')),
|
|
1152
|
-
React.createElement('p', { className: 'ab-set-card-sub' }, t('set.qqSub'))),
|
|
1153
|
-
React.createElement('div', { className: 'ab-set-fold-sum' },
|
|
1154
|
-
(provisionLabel(t, qq.status) || t('set.qqUnknown')) + ' · ' + (notify.chatId ? t('set.qqChatOn') : t('set.qqChatOff')))),
|
|
1155
|
-
React.createElement('div', { className: 'ab-set-fold-body' },
|
|
1156
|
-
(provisioning && provisioning.error && !provisioningActive)
|
|
1157
|
-
? React.createElement('p', { className: 'ab-set-err' }, (provisionLabel(t, provisioningStatus) || t('set.qqScanFail')) + ':' + provisioning.error)
|
|
1158
|
-
: null,
|
|
1159
|
-
provisioningActive
|
|
1160
|
-
? React.createElement('div', null,
|
|
1161
|
-
React.createElement('p', { className: 'ab-set-card-sub' }, t('set.qqScanStatus', { status: provisionLabel(t, provisioningStatus) || provisioningStatus })),
|
|
1162
|
-
provisioning.qrDataUrl
|
|
1163
|
-
? React.createElement('div', { className: 'ab-set-qr' },
|
|
1164
|
-
React.createElement('img', { src: provisioning.qrDataUrl, alt: t('set.qqQrAlt') }),
|
|
1165
|
-
React.createElement('p', { className: 'ab-set-qr-tip' }, t('set.qqQrTip')),
|
|
1166
|
-
)
|
|
1167
|
-
: null,
|
|
1168
|
-
React.createElement('div', { className: 'ab-set-row' },
|
|
1169
|
-
React.createElement('button', {
|
|
1170
|
-
type: 'button', className: 'ab-set-btn', disabled: busy,
|
|
1171
|
-
onClick: function () { run('qq-cancel-provision', {}, t('set.qqCancelOk')) },
|
|
1172
|
-
}, t('set.qqCancelScan')),
|
|
1173
|
-
),
|
|
1174
|
-
)
|
|
1175
|
-
: React.createElement('div', { className: 'ab-set-row' },
|
|
1176
|
-
React.createElement('button', {
|
|
1177
|
-
type: 'button',
|
|
1178
|
-
className: 'ab-set-btn' + (qq.hasSecret ? '' : ' ab-set-btn-primary'),
|
|
1179
|
-
disabled: busy,
|
|
1180
|
-
onClick: function () { run('qq-provision', {}, t('set.qqScanStart')) },
|
|
1181
|
-
}, qq.hasSecret ? t('set.qqRescan') : t('set.qqScan')),
|
|
1182
|
-
provisioningStatus === '已连接'
|
|
1183
|
-
? React.createElement('span', { className: 'ab-set-ok' }, t('set.qqScanOk'))
|
|
1184
|
-
: null,
|
|
1185
|
-
),
|
|
1186
|
-
React.createElement('div', { className: 'ab-set-row' },
|
|
1187
|
-
React.createElement('input', { className: 'ab-set-input ab-set-input-grow', placeholder: t('set.qqAppId'), value: appId, onChange: function (e) { setAppId(e.target.value) } }),
|
|
1188
|
-
React.createElement('input', { className: 'ab-set-input ab-set-input-grow', type: 'password', placeholder: qq.hasSecret ? t('set.qqSecretKeep') : t('set.qqAppSecret'), value: appSecret, onChange: function (e) { setAppSecret(e.target.value) } }),
|
|
1189
|
-
React.createElement('button', { type: 'button', className: 'ab-set-btn' + (qq.hasSecret ? ' ab-set-btn-primary' : ''), disabled: busy, onClick: function () { run('save-qq-creds', { appId: appId, appSecret: appSecret }, t('set.qqCredsReconnect')) } }, t('set.qqSaveConnect')),
|
|
1190
|
-
React.createElement('button', { type: 'button', className: 'ab-set-btn', disabled: busy, onClick: function () { run('reconnect-qq', {}, t('set.qqReconnected')) } }, t('set.qqReconnect')),
|
|
1191
|
-
),
|
|
1192
|
-
React.createElement('div', { className: 'ab-set-row' },
|
|
1193
|
-
React.createElement('span', { className: 'ab-set-item-meta' }, t('set.qqPush', { state: notify.enabled !== false ? t('set.qqPushOn') : t('set.qqPushOff') })),
|
|
1194
|
-
React.createElement('button', {
|
|
1195
|
-
type: 'button', className: 'ab-set-btn', disabled: busy,
|
|
1196
|
-
onClick: function () { run('save-plugin', { notify: { enabled: notify.enabled === false } }, t('set.qqPushToggled')) },
|
|
1197
|
-
}, notify.enabled === false ? t('set.qqEnablePush') : t('set.qqPausePush')),
|
|
1198
|
-
React.createElement('span', { className: 'ab-set-item-meta' }, t('set.qqCurrentChat', { chatId: notify.chatId || t('set.qqUnset') })),
|
|
1199
|
-
),
|
|
1200
|
-
React.createElement('div', { className: 'ab-set-row' },
|
|
1201
|
-
React.createElement('input', {
|
|
1202
|
-
className: 'ab-set-input ab-set-input-grow', placeholder: t('set.qqChatPlaceholder'),
|
|
1203
|
-
value: chatIdInput, onChange: function (e) { setChatIdInput(e.target.value) },
|
|
1204
|
-
}),
|
|
1205
|
-
React.createElement('input', {
|
|
1206
|
-
className: 'ab-set-input', placeholder: t('set.qqGroupUser'),
|
|
1207
|
-
value: userIdInput, onChange: function (e) { setUserIdInput(e.target.value) },
|
|
1208
|
-
}),
|
|
1209
|
-
React.createElement('input', {
|
|
1210
|
-
className: 'ab-set-input ab-set-input-num', type: 'number', min: 30, placeholder: t('set.qqTimeoutS'),
|
|
1211
|
-
value: timeoutSecs, onChange: function (e) { setTimeoutSecs(e.target.value) },
|
|
1212
|
-
}),
|
|
1213
|
-
React.createElement('button', {
|
|
1214
|
-
type: 'button', className: 'ab-set-btn', disabled: busy,
|
|
1215
|
-
onClick: function () {
|
|
1216
|
-
run('save-plugin', {
|
|
1217
|
-
notify: {
|
|
1218
|
-
enabled: notify.enabled !== false,
|
|
1219
|
-
chatId: chatIdInput.trim(),
|
|
1220
|
-
userId: userIdInput.trim(),
|
|
1221
|
-
timeoutSecs: Number(timeoutSecs) || 120,
|
|
1222
|
-
},
|
|
1223
|
-
}, t('set.targetSaved'))
|
|
1224
|
-
},
|
|
1225
|
-
}, t('set.saveTarget')),
|
|
1226
|
-
),
|
|
1227
|
-
React.createElement('p', { className: 'ab-set-card-sub' }, t('set.recent')),
|
|
1228
|
-
(!qq.recentChats || qq.recentChats.length === 0)
|
|
1229
|
-
? React.createElement('p', { className: 'ab-set-empty' }, t('set.noMail'))
|
|
1230
|
-
: React.createElement('div', { className: 'ab-set-list' },
|
|
1231
|
-
qq.recentChats.map(function (c) {
|
|
1232
|
-
return React.createElement('div', {
|
|
1233
|
-
className: 'ab-set-item ab-chat', key: c.chatId + c.userId,
|
|
1234
|
-
onClick: function () { run('set-chat', { chatId: c.chatId, userId: c.isGroup ? c.userId : '' }, t('set.chatPicked')) },
|
|
1235
|
-
},
|
|
1236
|
-
React.createElement('span', { className: 'ab-set-item-label' },
|
|
1237
|
-
(c.isGroup ? t('set.group') + ' ' : t('set.c2c') + ' ') + (c.username || c.userId) + ' · ' + c.chatId),
|
|
1238
|
-
React.createElement('span', { className: 'ab-set-item-meta' }, c.preview || ''),
|
|
1239
|
-
)
|
|
1240
|
-
}),
|
|
1241
|
-
),
|
|
1242
|
-
),
|
|
1243
|
-
),
|
|
1133
|
+
feedback
|
|
1244
1134
|
|
|
1245
|
-
feedback
|
|
1246
1135
|
? React.createElement('div', { className: feedback.ok ? 'ab-set-ok' : 'ab-set-err' }, feedback.msg)
|
|
1247
1136
|
: null,
|
|
1248
1137
|
)
|
|
@@ -1277,7 +1166,7 @@ window.__ModuleLoader__.load({
|
|
|
1277
1166
|
slots.inject('conversation.input.dock', function () {
|
|
1278
1167
|
return slots.register(
|
|
1279
1168
|
{ name: 'conversation.input.dock', id: 'dsh-auto-approve.notice', order: 30, locale: NS, label: function () { return t('slot.notice') } },
|
|
1280
|
-
function (props) { return React.createElement(NoticeStrip, { slotsProps: props, rpc: rpc }) },
|
|
1169
|
+
function (props) { return React.createElement(NoticeStrip, { slotsProps: props, rpc: rpc, t: t }) },
|
|
1281
1170
|
)
|
|
1282
1171
|
})
|
|
1283
1172
|
|
|
@@ -1291,14 +1180,14 @@ window.__ModuleLoader__.load({
|
|
|
1291
1180
|
label: function () { return t('slot.history') },
|
|
1292
1181
|
inject: (sessionId) => ({ sessionId }),
|
|
1293
1182
|
},
|
|
1294
|
-
function (props) { return React.createElement(HistoryView, { slotsProps: props, rpc: rpc }) },
|
|
1183
|
+
function (props) { return React.createElement(HistoryView, { slotsProps: props, rpc: rpc, t: t }) },
|
|
1295
1184
|
)
|
|
1296
1185
|
})
|
|
1297
1186
|
|
|
1298
1187
|
slots.inject('settings.section', function () {
|
|
1299
1188
|
return slots.register(
|
|
1300
1189
|
{ name: 'settings.section', id: 'dsh-auto-approve.settings', order: 55, locale: NS, label: function () { return t('slot.settings') } },
|
|
1301
|
-
function (props) { return React.createElement(SettingsPage, { slotsProps: props, rpc: rpc }) },
|
|
1190
|
+
function (props) { return React.createElement(SettingsPage, { slotsProps: props, rpc: rpc, t: t }) },
|
|
1302
1191
|
)
|
|
1303
1192
|
})
|
|
1304
1193
|
},
|
|
@@ -1306,7 +1195,7 @@ window.__ModuleLoader__.load({
|
|
|
1306
1195
|
|
|
1307
1196
|
exports.apply = plugin.apply
|
|
1308
1197
|
exports.inject = plugin.inject
|
|
1309
|
-
exports.name = 'dsh-auto-approve'
|
|
1198
|
+
exports.name = '@dnalec/dsh-auto-approve'
|
|
1310
1199
|
return module.exports
|
|
1311
1200
|
},
|
|
1312
1201
|
})
|