dsh-homepage 0.1.1 → 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/README.md +1 -0
- package/README.zh.md +1 -0
- package/lib/audit-instructions.js +21 -0
- package/lib/cards/plugin-audit.js +182 -27
- package/lib/cards/sidebar-list.js +317 -42
- package/lib/client.js +567 -76
- package/lib/framework.part.js +68 -7
- package/lib/host/audit-runner.js +90 -17
- package/lib/host/audit.js +28 -25
- package/lib/index.js +8 -1
- package/package.json +2 -1
- package/screenshots.json +4 -0
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](package.json)
|
|
6
6
|
[](package.json)
|
|
7
|
+
[](https://awesome-dsh-plugin.com)
|
|
7
8
|
|
|
8
9
|
[English](README.md) | [简体中文](README.zh.md)
|
|
9
10
|
|
package/README.zh.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](package.json)
|
|
6
6
|
[](package.json)
|
|
7
|
+
[](https://awesome-dsh-plugin.com)
|
|
7
8
|
|
|
8
9
|
[English](README.md) | [简体中文](README.zh.md)
|
|
9
10
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Editable audit guidance shared by the host and the client build. */
|
|
2
|
+
export const DEFAULT_AUDIT_INSTRUCTIONS = [
|
|
3
|
+
'1. 取源码:`web_fetch` 读 `https://raw.githubusercontent.com/<owner>/<repo>/HEAD/<path>`,或利用其他本地工具获取源码;必要时读取 npm 包 tarball 的内容。先读 `package.json`,再读入口文件,再顺着依赖与被动态加载的代码走下去。',
|
|
4
|
+
'2. 读代码,不读宣言:README 说什么是它想让你以为的,代码做什么才是要报的。',
|
|
5
|
+
'3. 按下面的顺序找问题:',
|
|
6
|
+
' - 恶意行为:读取或外传凭据/令牌(`~/.dsh`、环境变量、ssh、云厂商配置)、上传本机文件、反弹 shell、挖矿、删除或加密用户文件、篡改 DSH 本身或其他插件;',
|
|
7
|
+
' - 执行链:安装期脚本(preinstall/install/postinstall)执行命令;运行期 `child_process`;`eval`/`new Function`;从网络下载再执行;写入 `~/.dsh`、profile 或系统目录;',
|
|
8
|
+
' - 隐蔽性:混淆或加密的字符串、base64 之后 eval、与功能无关的外联域名、把内容塞进资源文件里;',
|
|
9
|
+
' - 越权:绕过 DSH 的沙箱与审批,或去调用别的插件的私有服务;',
|
|
10
|
+
' - 版本:peer 范围与当前 DSH 版本是否相容(DSH 在安装时会拒绝不相容的包)。',
|
|
11
|
+
'4. 结论只有三档:',
|
|
12
|
+
' - `safe`:没有发现上述任何问题,且代码与它声称的功能一致;',
|
|
13
|
+
' - `warn`:可疑、需要用户知情(安装脚本、外部网络、权限偏大、版本不相容、源码读不到),但不像恶意;',
|
|
14
|
+
' - `unsafe`:发现明确的恶意行为或高风险执行链。',
|
|
15
|
+
'5. 每一条 `warn` 都要带 `risk`,替用户回答四个问题——这是他唯一能据以决定装不装的东西:',
|
|
16
|
+
' - `role`:这段代码在插件里做什么(功能角色);',
|
|
17
|
+
' - `why`:插件为什么需要它(合理的功能需要,还是抄来的惯例、与功能无关);',
|
|
18
|
+
' - `impact`:一旦被滥用会造成什么后果(拿到了什么、改了什么、传出了什么);',
|
|
19
|
+
' - `scenario` 取 `supply-chain`(供应链攻击:安装期/更新链上执行代码)、`privilege-escalation`(Agent 越权:绕过沙箱或审批、调用别的插件的私有服务)、`sandbox-escape`(沙箱逃逸)、`data-leak`(数据泄露:凭据/文件/环境变量被读取或外传)、`unsafe-publish`(不安全的网络发布)、`other`。',
|
|
20
|
+
' 只知道一部分就写那一部分,并在 `detail` 里说清你没看到什么;不要凭空补。`info`/`error` 不需要 `risk`。',
|
|
21
|
+
].join('\n')
|
|
@@ -331,7 +331,19 @@ function useStarter(api, view) {
|
|
|
331
331
|
var decision = chosen === MODE_REVIEW ? MODE_REVIEW : MODE_INSTALL
|
|
332
332
|
setError('')
|
|
333
333
|
setBusy('start')
|
|
334
|
-
|
|
334
|
+
var options = { target: wanted, mode: decision }
|
|
335
|
+
var prompt = String(api.control('auditPrompt') || '')
|
|
336
|
+
if (prompt) options.prompt = prompt
|
|
337
|
+
var model = String(api.control('auditModel') || '')
|
|
338
|
+
if (model) {
|
|
339
|
+
try {
|
|
340
|
+
var pair = JSON.parse(model)
|
|
341
|
+
options.selection = { provider: pair[0], model: pair[1] }
|
|
342
|
+
} catch (reason) { setBusy(''); setError(String(reason)); return }
|
|
343
|
+
}
|
|
344
|
+
var effort = String(api.control('auditEffort') || '')
|
|
345
|
+
if (effort) options.selection = Object.assign({}, options.selection || {}, { reasoningEffort: effort })
|
|
346
|
+
api.remote(START, { options: options })
|
|
335
347
|
.then(function (next) {
|
|
336
348
|
setBusy('')
|
|
337
349
|
if (next && next.auditId) api.setControl('auditId', String(next.auditId))
|
|
@@ -380,7 +392,7 @@ function TargetRow(props) {
|
|
|
380
392
|
onStart(wanted, mode)
|
|
381
393
|
setDraft('')
|
|
382
394
|
}
|
|
383
|
-
return h('div', { className: 'ph-loose' },
|
|
395
|
+
return h('div', { className: chooseMode ? 'ph-loose ph-audit-target-form' : 'ph-loose' },
|
|
384
396
|
chooseMode
|
|
385
397
|
? h('div', { className: 'ph-loose-row' },
|
|
386
398
|
h(UI.Segmented, {
|
|
@@ -414,6 +426,7 @@ function TargetRow(props) {
|
|
|
414
426
|
onKeyDown: function (event) { if (event.key === 'Enter') submit() },
|
|
415
427
|
}),
|
|
416
428
|
h(UI.Button, {
|
|
429
|
+
tone: chooseMode ? 'primary' : undefined,
|
|
417
430
|
disabled: busy || String(draft || '').trim() === '',
|
|
418
431
|
onClick: submit,
|
|
419
432
|
}, t('pluginAudit.action.start'))))
|
|
@@ -580,8 +593,9 @@ function FindingRow(props) {
|
|
|
580
593
|
h('span', { className: 'ph-audit-finding-title' }, title),
|
|
581
594
|
scenario ? h('span', { className: 'ph-audit-finding-tag' }, scenario) : null,
|
|
582
595
|
h('span', { className: 'ph-audit-finding-more' }, t(open ? 'pluginAudit.finding.collapse' : 'pluginAudit.finding.expand'))),
|
|
583
|
-
open
|
|
584
|
-
|
|
596
|
+
h('div', { className: 'ph-audit-reveal', hidden: !open },
|
|
597
|
+
h('div', { className: 'ph-audit-reveal-inner' },
|
|
598
|
+
h('div', { className: 'ph-audit-finding-body' },
|
|
585
599
|
detail ? h('p', { className: 'ph-audit-finding-detail' }, detail) : null,
|
|
586
600
|
risk
|
|
587
601
|
? h('div', { className: 'ph-audit-risk' },
|
|
@@ -592,8 +606,7 @@ function FindingRow(props) {
|
|
|
592
606
|
: (level === 'warn' ? h('p', { className: 'ph-audit-risk-missing' }, t('pluginAudit.risk.missing')) : null),
|
|
593
607
|
finding.ref
|
|
594
608
|
? h('p', { className: 'ph-audit-finding-ref' }, t('pluginAudit.finding.ref', { ref: String(finding.ref) }))
|
|
595
|
-
: null)
|
|
596
|
-
: null)
|
|
609
|
+
: null))))
|
|
597
610
|
}
|
|
598
611
|
|
|
599
612
|
/** The wall reading: where the review is, or an honest empty state. */
|
|
@@ -675,12 +688,101 @@ function HistoryList(props) {
|
|
|
675
688
|
* installs from its own row — which is how somebody asks for several reviews at once
|
|
676
689
|
* and decides afterwards.
|
|
677
690
|
*/
|
|
691
|
+
var defaultInstructions = /* @audit-instructions */ ''
|
|
692
|
+
|
|
693
|
+
function AuditModelPicker(props) {
|
|
694
|
+
var api = props.api
|
|
695
|
+
var t = props.t
|
|
696
|
+
var catalog = props.catalog
|
|
697
|
+
var data = catalog.data || {}
|
|
698
|
+
var models = Array.isArray(data.models) ? data.models : []
|
|
699
|
+
var chosen = String(api.control('auditModel') || '')
|
|
700
|
+
var effort = String(api.control('auditEffort') || '')
|
|
701
|
+
var defaults = data.defaultSelection || {}
|
|
702
|
+
var defaultKey = defaults.provider && defaults.model ? JSON.stringify([defaults.provider, defaults.model]) : ''
|
|
703
|
+
var selected = models.filter(function (item) { return JSON.stringify([item.provider, item.model]) === (chosen || defaultKey) })[0]
|
|
704
|
+
var efforts = selected && Array.isArray(selected.efforts) ? selected.efforts : []
|
|
705
|
+
function catalogLabel(value, fallback) {
|
|
706
|
+
if (typeof value === 'string') return value
|
|
707
|
+
return value && typeof value === 'object' ? (value[t('pluginAudit.catalogLanguage')] || value.en || value.zh || fallback) : fallback
|
|
708
|
+
}
|
|
709
|
+
return h('div', { className: 'ph-audit-models' },
|
|
710
|
+
h('label', { className: 'ph-audit-model-field' }, h('span', null, t('pluginAudit.model')),
|
|
711
|
+
h('select', { className: 'ph-input ph-audit-model-select', value: chosen, disabled: props.busy || !models.length,
|
|
712
|
+
onChange: function (event) { api.setControl('auditModel', event.target.value); api.setControl('auditEffort', '') },
|
|
713
|
+
}, h('option', { value: '' }, t('pluginAudit.model.default')),
|
|
714
|
+
chosen && !selected ? h('option', { value: chosen }, t('pluginAudit.model.unavailable')) : null,
|
|
715
|
+
models.map(function (item) {
|
|
716
|
+
var key = JSON.stringify([item.provider, item.model])
|
|
717
|
+
return h('option', { key: key, value: key }, catalogLabel(item.name, item.model) + ' · ' + item.provider)
|
|
718
|
+
}))),
|
|
719
|
+
h('label', { className: 'ph-audit-model-field' }, h('span', null, t('pluginAudit.effort')),
|
|
720
|
+
h('select', { className: 'ph-input ph-audit-effort-select', value: effort, disabled: props.busy || !efforts.length,
|
|
721
|
+
onChange: function (event) {
|
|
722
|
+
if (!chosen && event.target.value && defaultKey) api.setControl('auditModel', defaultKey)
|
|
723
|
+
api.setControl('auditEffort', event.target.value)
|
|
724
|
+
},
|
|
725
|
+
}, h('option', { value: '' }, t(efforts.length ? 'pluginAudit.effort.default' : 'pluginAudit.effort.none')),
|
|
726
|
+
efforts.map(function (item) { return h('option', { key: item.id, value: item.id }, catalogLabel(item.name, item.id)) }))),
|
|
727
|
+
catalog.error ? h('span', { className: 'ph-groups-error' }, t('pluginAudit.model.error')) : null)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function PromptEditor(props) {
|
|
731
|
+
var api = props.api
|
|
732
|
+
var t = props.t
|
|
733
|
+
var saved = String(api.control('auditPrompt') || '')
|
|
734
|
+
var state = React.useState(saved || defaultInstructions)
|
|
735
|
+
var draft = state[0]
|
|
736
|
+
var setDraft = state[1]
|
|
737
|
+
var dirty = draft !== (saved || defaultInstructions)
|
|
738
|
+
var expanded = React.useState(false)
|
|
739
|
+
var open = expanded[0]
|
|
740
|
+
return h('section', { className: 'ph-audit-launch' },
|
|
741
|
+
h('div', { className: 'ph-audit-launch-head' },
|
|
742
|
+
h('div', { className: 'ph-audit-launch-heading' },
|
|
743
|
+
h('span', { className: 'ph-audit-launch-icon', 'aria-hidden': true }, '↗'),
|
|
744
|
+
h('div', null,
|
|
745
|
+
h('h3', { className: 'ph-audit-launch-title' }, t('pluginAudit.section.target')))),
|
|
746
|
+
h('button', {
|
|
747
|
+
type: 'button', className: 'ph-audit-prompt-trigger', 'aria-expanded': open,
|
|
748
|
+
title: t('pluginAudit.prompt.hint'), onClick: function () { expanded[1](!open) },
|
|
749
|
+
}, h('span', { 'aria-hidden': true }, '✎'), t('pluginAudit.prompt.edit'),
|
|
750
|
+
h('span', { className: 'ph-audit-prompt-badge' }, t(saved ? 'pluginAudit.prompt.custom' : 'pluginAudit.prompt.default')))),
|
|
751
|
+
props.children,
|
|
752
|
+
h(AuditModelPicker, { api: api, t: t, busy: props.busy, catalog: props.catalog }),
|
|
753
|
+
h('div', { className: 'ph-audit-settings ph-audit-reveal', hidden: !open },
|
|
754
|
+
h('div', { className: 'ph-audit-reveal-inner ph-loose' },
|
|
755
|
+
h('div', { className: 'ph-audit-editor-head' },
|
|
756
|
+
h('strong', null, t('pluginAudit.prompt.title')),
|
|
757
|
+
h('span', { className: 'ph-note' }, String(draft.length) + ' / 20000')),
|
|
758
|
+
h('p', { className: 'ph-note' }, t('pluginAudit.prompt.hint')),
|
|
759
|
+
h('textarea', {
|
|
760
|
+
className: 'ph-input ph-audit-prompt', value: draft, maxLength: 20000,
|
|
761
|
+
'aria-label': t('pluginAudit.prompt.title'),
|
|
762
|
+
onChange: function (event) { setDraft(event.target.value) },
|
|
763
|
+
}),
|
|
764
|
+
h('div', { className: 'ph-audit-editor-footer' },
|
|
765
|
+
h('span', { className: 'ph-note', role: 'status' }, t(dirty ? 'pluginAudit.prompt.unsaved' : 'pluginAudit.prompt.ready')),
|
|
766
|
+
h(UI.Button, { tone: 'primary', disabled: !dirty, onClick: function () {
|
|
767
|
+
var value = draft.trim() ? draft : defaultInstructions
|
|
768
|
+
api.setControl('auditPrompt', value === defaultInstructions ? '' : value)
|
|
769
|
+
setDraft(value)
|
|
770
|
+
} }, t('pluginAudit.prompt.save')),
|
|
771
|
+
h(UI.Button, { disabled: !saved && draft === defaultInstructions, onClick: function () {
|
|
772
|
+
api.setControl('auditPrompt', '')
|
|
773
|
+
setDraft(defaultInstructions)
|
|
774
|
+
} }, t('pluginAudit.prompt.reset'))))))
|
|
775
|
+
}
|
|
776
|
+
|
|
678
777
|
function AuditDetail(props) {
|
|
679
778
|
var api = props.api
|
|
680
779
|
var t = props.t
|
|
681
780
|
var view = useAudit(api)
|
|
682
781
|
useAnnounce(api, 'pluginAudit', auditNotice(view.audit, t))
|
|
683
782
|
var starter = useStarter(api, view)
|
|
783
|
+
// Keep the catalog above the tab panes: returning to Current must not
|
|
784
|
+
// rediscover every provider or briefly disable the model selectors.
|
|
785
|
+
var modelCatalog = useRemote('homepage/pluginAuditModels', { options: {} }, { refreshMs: 0 })
|
|
684
786
|
var tab = React.useState('current')
|
|
685
787
|
var pane = tab[0]
|
|
686
788
|
var setPane = tab[1]
|
|
@@ -703,9 +805,15 @@ function AuditDetail(props) {
|
|
|
703
805
|
var history = listed.map(function (entry) {
|
|
704
806
|
return audit && entry && entry.auditId === audit.auditId ? audit : entry
|
|
705
807
|
})
|
|
808
|
+
function changePane(next) {
|
|
809
|
+
if (next === pane) return
|
|
810
|
+
// Update immediately; the pane's local CSS animation needs no page snapshot
|
|
811
|
+
// or animation-frame handshake before React can commit the selected tab.
|
|
812
|
+
setPane(next)
|
|
813
|
+
}
|
|
706
814
|
function openRecord(id) {
|
|
707
815
|
api.setControl('auditId', id)
|
|
708
|
-
|
|
816
|
+
changePane('current')
|
|
709
817
|
}
|
|
710
818
|
function decideFromRow(id, decision) {
|
|
711
819
|
if (pendingRow) return
|
|
@@ -723,7 +831,7 @@ function AuditDetail(props) {
|
|
|
723
831
|
setRowError(String((reason && reason.message) || reason))
|
|
724
832
|
})
|
|
725
833
|
}
|
|
726
|
-
return h('div', { className: 'ph-detail-block' },
|
|
834
|
+
return h('div', { className: 'ph-detail-block ph-audit-detail' },
|
|
727
835
|
h(UI.Segmented, {
|
|
728
836
|
options: [
|
|
729
837
|
{ value: 'current', label: t('pluginAudit.tab.current') },
|
|
@@ -735,11 +843,11 @@ function AuditDetail(props) {
|
|
|
735
843
|
// Opening the history asks the host for it: the records are the host's, and
|
|
736
844
|
// a review started while this card was closed has to be in the list.
|
|
737
845
|
if (wanted === 'history' && typeof view.reloadList === 'function') view.reloadList(true)
|
|
738
|
-
|
|
846
|
+
changePane(wanted)
|
|
739
847
|
},
|
|
740
848
|
}),
|
|
741
849
|
pane === 'history'
|
|
742
|
-
? h('div', { className: 'ph-loose' },
|
|
850
|
+
? h('div', { className: 'ph-loose ph-audit-pane', key: 'history' },
|
|
743
851
|
h('div', { className: 'ph-section-head' },
|
|
744
852
|
h('h4', { className: 'ph-section-title' }, t('pluginAudit.section.history'))),
|
|
745
853
|
h(HistoryList, {
|
|
@@ -751,16 +859,26 @@ function AuditDetail(props) {
|
|
|
751
859
|
onDecide: decideFromRow,
|
|
752
860
|
}),
|
|
753
861
|
rowError ? h('span', { className: 'ph-groups-error' }, rowError) : null)
|
|
754
|
-
: h('div', { className: 'ph-loose' },
|
|
755
|
-
h(
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
862
|
+
: h('div', { className: 'ph-loose ph-audit-pane', key: 'current' },
|
|
863
|
+
h(PromptEditor, { api: api, t: t, busy: starter.busy, catalog: modelCatalog },
|
|
864
|
+
h(TargetRow, {
|
|
865
|
+
t: t, busy: starter.busy, mode: starter.mode, setMode: starter.setMode,
|
|
866
|
+
onStart: starter.start, draft: draft, setDraft: setDraft,
|
|
867
|
+
})),
|
|
760
868
|
starter.error ? h('span', { className: 'ph-groups-error' }, starter.error) : null,
|
|
761
869
|
view.error ? h('span', { className: 'ph-groups-error' }, String(view.error)) : null,
|
|
762
870
|
audit
|
|
763
|
-
? h('div', { className: 'ph-
|
|
871
|
+
? h('div', { className: 'ph-audit-result' },
|
|
872
|
+
h('div', { className: 'ph-audit-result-head' },
|
|
873
|
+
h(UI.Chip, { tone: STATE_TONES[audit.state] || null }, stateText(t, audit.state)),
|
|
874
|
+
h('strong', { className: 'ph-audit-name' }, auditName(audit, t)),
|
|
875
|
+
plugin.version ? h('span', { className: 'ph-note' }, String(plugin.version)) : null),
|
|
876
|
+
h('p', { className: 'ph-audit-summary' }, audit.summary || progressText(t, audit)),
|
|
877
|
+
(canInstall(audit) || canDecline(audit)) ? h('div', { className: 'ph-audit-result-actions' },
|
|
878
|
+
h(VerdictActions, { api: api, t: t, audit: audit, view: view })) : null,
|
|
879
|
+
h('details', { className: 'ph-audit-metadata' },
|
|
880
|
+
h('summary', null, t('pluginAudit.metadata')),
|
|
881
|
+
h('div', { className: 'ph-kv' },
|
|
764
882
|
h('div', { className: 'ph-kv-row' },
|
|
765
883
|
h('span', { className: 'ph-kv-key' }, t('pluginAudit.kv.plugin')),
|
|
766
884
|
h('span', { className: 'ph-kv-value' }, auditName(audit, t))),
|
|
@@ -785,20 +903,18 @@ function AuditDetail(props) {
|
|
|
785
903
|
h('span', { className: 'ph-kv-value' }, stateText(t, audit.state) + (audit.stage ? ' · ' + progressText(t, audit) : ''))),
|
|
786
904
|
h('div', { className: 'ph-kv-row' },
|
|
787
905
|
h('span', { className: 'ph-kv-key' }, t('pluginAudit.kv.installed')),
|
|
788
|
-
h('span', { className: 'ph-kv-value' }, t(audit.installed ? 'pluginAudit.value.yes' : 'pluginAudit.value.no'))))
|
|
906
|
+
h('span', { className: 'ph-kv-value' }, t(audit.installed ? 'pluginAudit.value.yes' : 'pluginAudit.value.no'))))))
|
|
789
907
|
: h('p', { className: 'ph-note' }, view.error ? t('pluginAudit.error.status') : (view.busy ? t('pluginAudit.loading') : t('pluginAudit.idle'))),
|
|
790
908
|
audit && audit.detail ? h('p', { className: 'ph-note' }, String(audit.detail)) : null,
|
|
791
|
-
audit
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
h('div', { className: 'ph-section-head' }, h('h4', { className: 'ph-section-title' }, t('pluginAudit.section.findings'))),
|
|
909
|
+
audit ? h('div', { className: 'ph-section-head' },
|
|
910
|
+
h('h4', { className: 'ph-section-title' }, t('pluginAudit.section.findings')),
|
|
911
|
+
h('span', { className: 'ph-note' }, String(findings.length))) : null,
|
|
795
912
|
findings.length === 0
|
|
796
|
-
? h('p', { className: 'ph-note' }, t('pluginAudit.findings.none'))
|
|
913
|
+
? (audit ? h('p', { className: 'ph-note' }, t('pluginAudit.findings.none')) : null)
|
|
797
914
|
: h('div', { className: 'ph-audit-findings' }, sortFindings(findings).map(function (finding, index) {
|
|
798
915
|
return h(FindingRow, { t: t, finding: finding, key: 'f' + index })
|
|
799
916
|
})),
|
|
800
|
-
h(
|
|
801
|
-
audit ? h(SessionRow, { t: t, audit: audit }) : h('p', { className: 'ph-note' }, t('pluginAudit.session.none'))))
|
|
917
|
+
audit ? h(SessionRow, { t: t, audit: audit }) : null))
|
|
802
918
|
}
|
|
803
919
|
|
|
804
920
|
defineCard({
|
|
@@ -824,6 +940,9 @@ defineCard({
|
|
|
824
940
|
actions: [],
|
|
825
941
|
detailActions: [
|
|
826
942
|
{ id: 'auditId', type: 'text', hidden: true, value: '' },
|
|
943
|
+
{ id: 'auditPrompt', type: 'text', hidden: true, value: '' },
|
|
944
|
+
{ id: 'auditModel', type: 'text', hidden: true, value: '' },
|
|
945
|
+
{ id: 'auditEffort', type: 'text', hidden: true, value: '' },
|
|
827
946
|
// The changed-audit signal (see SIGNAL): a control rather than component state
|
|
828
947
|
// because the preview and the expanded body are separate instances that must
|
|
829
948
|
// agree, and a control is the one value the framework shares between them.
|
|
@@ -834,6 +953,24 @@ defineCard({
|
|
|
834
953
|
dict({
|
|
835
954
|
zh: {
|
|
836
955
|
'card.pluginAudit.title': '插件审核',
|
|
956
|
+
'pluginAudit.metadata': '插件详情',
|
|
957
|
+
'pluginAudit.catalogLanguage': 'zh',
|
|
958
|
+
'pluginAudit.model': '审核模型',
|
|
959
|
+
'pluginAudit.model.default': '跟随全局默认模型',
|
|
960
|
+
'pluginAudit.model.unavailable': '已选模型不可用,请重新选择',
|
|
961
|
+
'pluginAudit.model.error': '模型列表读取失败,仍可使用全局默认模型',
|
|
962
|
+
'pluginAudit.effort': '思考强度',
|
|
963
|
+
'pluginAudit.effort.default': '模型默认',
|
|
964
|
+
'pluginAudit.effort.none': '不支持或未提供思考强度',
|
|
965
|
+
'pluginAudit.prompt.edit': '编辑提示词',
|
|
966
|
+
'pluginAudit.prompt.title': '审核提示词',
|
|
967
|
+
'pluginAudit.prompt.custom': '自定义',
|
|
968
|
+
'pluginAudit.prompt.default': '默认',
|
|
969
|
+
'pluginAudit.prompt.hint': '保存后用于后续审核。审核对象、结果回传格式与安全约束由系统自动附加。',
|
|
970
|
+
'pluginAudit.prompt.save': '保存提示词',
|
|
971
|
+
'pluginAudit.prompt.reset': '恢复默认',
|
|
972
|
+
'pluginAudit.prompt.unsaved': '有未保存的修改',
|
|
973
|
+
'pluginAudit.prompt.ready': '已保存 · 下次审核生效',
|
|
837
974
|
|
|
838
975
|
'pluginAudit.state.none': '无审核',
|
|
839
976
|
'pluginAudit.state.running': '审核中',
|
|
@@ -912,7 +1049,7 @@ dict({
|
|
|
912
1049
|
'pluginAudit.tab.current': '当前审核',
|
|
913
1050
|
'pluginAudit.tab.history': '审核记录',
|
|
914
1051
|
'pluginAudit.history.empty': '还没有审核记录',
|
|
915
|
-
'pluginAudit.history.view': '
|
|
1052
|
+
'pluginAudit.history.view': '加载条目',
|
|
916
1053
|
|
|
917
1054
|
'pluginAudit.kv.plugin': '插件',
|
|
918
1055
|
'pluginAudit.kv.version': '版本',
|
|
@@ -930,6 +1067,24 @@ dict({
|
|
|
930
1067
|
},
|
|
931
1068
|
en: {
|
|
932
1069
|
'card.pluginAudit.title': 'Plugin audit',
|
|
1070
|
+
'pluginAudit.metadata': 'Plugin details',
|
|
1071
|
+
'pluginAudit.catalogLanguage': 'en',
|
|
1072
|
+
'pluginAudit.model': 'Review model',
|
|
1073
|
+
'pluginAudit.model.default': 'Use global default model',
|
|
1074
|
+
'pluginAudit.model.unavailable': 'Selected model unavailable — choose again',
|
|
1075
|
+
'pluginAudit.model.error': 'Could not load models. The global default is still available.',
|
|
1076
|
+
'pluginAudit.effort': 'Reasoning effort',
|
|
1077
|
+
'pluginAudit.effort.default': 'Model default',
|
|
1078
|
+
'pluginAudit.effort.none': 'Reasoning effort unavailable',
|
|
1079
|
+
'pluginAudit.prompt.edit': 'Edit prompt',
|
|
1080
|
+
'pluginAudit.prompt.title': 'Review prompt',
|
|
1081
|
+
'pluginAudit.prompt.custom': 'Custom',
|
|
1082
|
+
'pluginAudit.prompt.default': 'Default',
|
|
1083
|
+
'pluginAudit.prompt.hint': 'Applies to future reviews once saved. The target, report format and safety constraints are appended automatically.',
|
|
1084
|
+
'pluginAudit.prompt.save': 'Save prompt',
|
|
1085
|
+
'pluginAudit.prompt.reset': 'Restore default',
|
|
1086
|
+
'pluginAudit.prompt.unsaved': 'Unsaved changes',
|
|
1087
|
+
'pluginAudit.prompt.ready': 'Saved · applies to the next review',
|
|
933
1088
|
|
|
934
1089
|
'pluginAudit.state.none': 'No audit',
|
|
935
1090
|
'pluginAudit.state.running': 'Reviewing',
|
|
@@ -1008,7 +1163,7 @@ dict({
|
|
|
1008
1163
|
'pluginAudit.tab.current': 'Current review',
|
|
1009
1164
|
'pluginAudit.tab.history': 'Review history',
|
|
1010
1165
|
'pluginAudit.history.empty': 'No reviews yet',
|
|
1011
|
-
'pluginAudit.history.view': '
|
|
1166
|
+
'pluginAudit.history.view': 'Load entry',
|
|
1012
1167
|
|
|
1013
1168
|
'pluginAudit.kv.plugin': 'Plugin',
|
|
1014
1169
|
'pluginAudit.kv.version': 'Version',
|