@morehao/dsh-skills-console 0.3.8 → 0.4.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/README.md +55 -65
- package/client.js +206 -267
- package/cordis.patch.yml +2 -1
- package/lib/agent-table.json +133 -64
- package/lib/core/agents.js +68 -9
- package/lib/core/exporter.js +137 -0
- package/lib/core/inventory.js +73 -39
- package/lib/core/migrations.js +178 -0
- package/lib/core/paths.js +13 -0
- package/lib/core/policy.js +373 -0
- package/lib/core/remote.js +3 -1
- package/lib/core/roots.js +182 -0
- package/lib/core/service.js +391 -137
- package/lib/core/shadow.js +195 -0
- package/lib/core/skill-md.js +23 -4
- package/lib/core/state.js +293 -42
- package/lib/core/visibility.js +145 -0
- package/lib/index.js +27 -0
- package/lib/routes.js +13 -3
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -86,18 +86,19 @@ var DICT = {
|
|
|
86
86
|
"action.unsubscribe": "取消订阅",
|
|
87
87
|
"action.view": "查看",
|
|
88
88
|
"action.hide": "收起",
|
|
89
|
-
"action.
|
|
89
|
+
"action.exports": "导出设置",
|
|
90
90
|
"action.install": "安装",
|
|
91
91
|
"action.expand": "展开",
|
|
92
92
|
"action.collapse": "收起",
|
|
93
93
|
"action.adopt": "接管",
|
|
94
94
|
"action.save": "保存",
|
|
95
95
|
"action.cancel": "取消",
|
|
96
|
+
"action.close": "关闭",
|
|
96
97
|
"action.choose": "选择目录",
|
|
97
98
|
"action.retry": "重新加载",
|
|
98
99
|
"view.flat": "平铺",
|
|
99
100
|
"view.agent": "按 Agent",
|
|
100
|
-
|
|
101
|
+
exportDefault: "新安装时自动导出",
|
|
101
102
|
"settings.title": "插件设置",
|
|
102
103
|
"placement.label": "侧边栏入口",
|
|
103
104
|
"placement.effect": "切换后立即生效",
|
|
@@ -123,23 +124,20 @@ var DICT = {
|
|
|
123
124
|
"toast.checked": "检查完成",
|
|
124
125
|
"mode.on": "启用",
|
|
125
126
|
"mode.off": "停用",
|
|
126
|
-
"mode.offHint": "
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
"autoLink.detected": "安装后自动接入",
|
|
141
|
-
"autoLink.none": "本机没有检测到其他 Agent",
|
|
142
|
-
"autoLink.hint": "列表里的「dsh(全局)」固定勾选、不能取消;这里只影响之后安装时的自动接入,已安装的技能请在「接入设置」里改。",
|
|
127
|
+
"mode.offHint": "已停用:DSH 的模型调用与 `/` 手动调用都看不到它;技能文件、导出关系与其他 Agent 都不受影响",
|
|
128
|
+
"visibility.hiddenHint": "已启用,但扫描目录里没有它:确认 ~/.agents/skills 下真有这个技能,或检查 DSH_AGENTS_HOME。",
|
|
129
|
+
"visibility.manualOnlyHint": "该技能自己声明了 `disable-model-invocation`:模型调用看不到它,手动 `/` 调用仍可用。",
|
|
130
|
+
"visibility.scopeMismatch": "当前有另一份同名技能排在前面(项目或用户目录);停用只影响 canonical,不会隐藏它。",
|
|
131
|
+
"visibility.foreignWinner": "模型看到的是另一份同名技能:{path}(它的扫描根 rank 更小,会遮住这一份)",
|
|
132
|
+
"exports.targets": "导出到哪些 Agent",
|
|
133
|
+
"exports.detectedHint": "只列出本机已安装(目录存在)的 Agent。导出只影响它们;DSH 自己看不看得见由 DSH 决定,和导出无关。",
|
|
134
|
+
"exports.canonical": "技能本体:{path};下面的链只是把它暴露给不直接读该目录的 Agent。",
|
|
135
|
+
"exports.none": "本机没有可导出的 Agent 目录",
|
|
136
|
+
"exports.hint": "保存后按勾选建立或移除软链;技能本体留在原位置,不会移动。",
|
|
137
|
+
"exports.sharedUnknown": "共享根未验证",
|
|
138
|
+
"exportDefault.detected": "装完后默认导出到",
|
|
139
|
+
"exportDefault.none": "本机没有需要导出的 Agent",
|
|
140
|
+
"exportDefault.hint": "默认一个都不勾:技能装进 ~/.agents/skills 后 DSH 本来就能看见。导出只用于仍只认私有目录的 Agent,且只影响之后的安装。",
|
|
143
141
|
"empty.skills": "还没有安装任何技能",
|
|
144
142
|
"empty.skillsHint": "在终端跑 `npx skills add owner/repo`,或点右上角「添加技能」",
|
|
145
143
|
"empty.subs": "还没有添加订阅",
|
|
@@ -167,17 +165,19 @@ var DICT = {
|
|
|
167
165
|
"form.subHint": "只记录你关注这个仓库,不会安装任何技能。",
|
|
168
166
|
"subscription.defaultRef": "默认分支",
|
|
169
167
|
"confirm.deleteTitle": "删除技能",
|
|
170
|
-
"confirm.deleteBody": "
|
|
168
|
+
"confirm.deleteBody": "会从本机移除,断开本插件建立的软链并删除 canonical / lock 条目;目录先备份到回收站(保留 7 天)。不会删除你的源仓库。",
|
|
171
169
|
"confirm.updateTitle": "本地有改动",
|
|
172
170
|
"confirm.updateBody": "更新会覆盖这些本地改动;旧内容已备份,可回滚。",
|
|
173
171
|
"error.conflictTitle": "删除被拦截",
|
|
174
172
|
"busy.working": "处理中…",
|
|
175
173
|
"meta.from": "来自",
|
|
176
174
|
"meta.noUpstream": "无远端来源 · 不检测更新",
|
|
177
|
-
"meta.
|
|
178
|
-
"meta.
|
|
179
|
-
"meta.
|
|
180
|
-
"meta.
|
|
175
|
+
"meta.noExport": "未导出",
|
|
176
|
+
"meta.noExportHint": "没有给不支持共享根的 Agent 建导出链;直读 ~/.agents/skills 的 Agent 装好即见,无需导出。",
|
|
177
|
+
"meta.modelVisible": "模型可见",
|
|
178
|
+
"meta.modelHidden": "模型不可见",
|
|
179
|
+
"meta.manualOnly": "仅手动",
|
|
180
|
+
"meta.agentsAll": "已导出的 Agent",
|
|
181
181
|
"meta.none": "无",
|
|
182
182
|
"filter.all": "全部",
|
|
183
183
|
"filter.update": "可更新",
|
|
@@ -189,16 +189,15 @@ var DICT = {
|
|
|
189
189
|
"empty.filtered": "没有匹配的技能",
|
|
190
190
|
"empty.filteredHint": "换个关键词,或清除筛选条件",
|
|
191
191
|
"hint.unknown": "还有 {n} 个技能未检查更新。",
|
|
192
|
-
"group.
|
|
193
|
-
"group.dshProject": "DSH · 仅特定项目",
|
|
192
|
+
"group.visible": "模型可见",
|
|
194
193
|
"group.off": "已停用",
|
|
195
|
-
"group.
|
|
194
|
+
"group.hidden": "启用但不可见",
|
|
196
195
|
installed: "已安装",
|
|
197
196
|
notInstalled: "未安装",
|
|
198
197
|
skillCount: "个技能",
|
|
199
198
|
visibleOnly: "模型可见,但不在本地技能库",
|
|
200
|
-
visibleOnlyOther: "
|
|
201
|
-
visibleOnlyBuiltin: "本插件仅管理 ~/.agents/skills
|
|
199
|
+
visibleOnlyOther: "模型能看到,但不在 ~/.agents/skills",
|
|
200
|
+
visibleOnlyBuiltin: "本插件仅管理 ~/.agents/skills 下的技能,内置技能不在管理范围内",
|
|
202
201
|
"builtin.view": "查看内置技能({n})",
|
|
203
202
|
"builtin.hide": "收起内置技能",
|
|
204
203
|
external: "非本插件创建",
|
|
@@ -250,10 +249,8 @@ var DICT = {
|
|
|
250
249
|
"viewer.info.localChangeNone": "无(与安装时一致)",
|
|
251
250
|
"viewer.info.localChangeYes": "有本地改动",
|
|
252
251
|
"viewer.info.links": "接入",
|
|
253
|
-
"viewer.info.
|
|
254
|
-
"viewer.info.
|
|
255
|
-
"viewer.info.scopeGlobal": "全局",
|
|
256
|
-
"viewer.info.scopeProject": "仅项目",
|
|
252
|
+
"viewer.info.exported": "已导出",
|
|
253
|
+
"viewer.info.visibility": "可见性",
|
|
257
254
|
"viewer.info.mode": "状态",
|
|
258
255
|
"viewer.info.status": "更新",
|
|
259
256
|
"viewer.info.none": "无"
|
|
@@ -273,18 +270,19 @@ var DICT = {
|
|
|
273
270
|
"action.unsubscribe": "Unsubscribe",
|
|
274
271
|
"action.view": "View",
|
|
275
272
|
"action.hide": "Hide",
|
|
276
|
-
"action.
|
|
273
|
+
"action.exports": "Export settings",
|
|
277
274
|
"action.install": "Install",
|
|
278
275
|
"action.expand": "Expand",
|
|
279
276
|
"action.collapse": "Collapse",
|
|
280
277
|
"action.adopt": "Adopt",
|
|
281
278
|
"action.save": "Save",
|
|
282
279
|
"action.cancel": "Cancel",
|
|
280
|
+
"action.close": "Close",
|
|
283
281
|
"action.choose": "Choose folder",
|
|
284
282
|
"action.retry": "Reload",
|
|
285
283
|
"view.flat": "Flat",
|
|
286
284
|
"view.agent": "By agent",
|
|
287
|
-
"
|
|
285
|
+
"exportDefault": "Auto-export on install",
|
|
288
286
|
"settings.title": "Plugin settings",
|
|
289
287
|
"placement.label": "Sidebar entry",
|
|
290
288
|
"placement.effect": "Applies immediately",
|
|
@@ -309,23 +307,20 @@ var DICT = {
|
|
|
309
307
|
"toast.checked": "Checked",
|
|
310
308
|
"mode.on": "Enabled",
|
|
311
309
|
"mode.off": "Disabled",
|
|
312
|
-
"mode.offHint": "Disabled
|
|
313
|
-
"
|
|
314
|
-
"
|
|
315
|
-
"
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
"
|
|
319
|
-
"
|
|
320
|
-
"
|
|
321
|
-
"
|
|
322
|
-
"
|
|
323
|
-
"
|
|
324
|
-
"
|
|
325
|
-
"
|
|
326
|
-
"autoLink.detected": "Auto-link after install",
|
|
327
|
-
"autoLink.none": "No other agents detected on this machine",
|
|
328
|
-
"autoLink.hint": 'The "dsh (global)" entry is always checked and cannot be unchecked; this only affects future installs. Change existing skills under "Link settings".',
|
|
310
|
+
"mode.offHint": "Disabled for DSH’s model and `/` calls; the skill files, exports, and other agents are untouched",
|
|
311
|
+
"visibility.hiddenHint": "Enabled, but no copy sits in a scan directory: check that the skill really exists under ~/.agents/skills, or inspect DSH_AGENTS_HOME.",
|
|
312
|
+
"visibility.manualOnlyHint": "This skill declares `disable-model-invocation`: the model cannot invoke it, but manual `/` invocation still works.",
|
|
313
|
+
"visibility.scopeMismatch": "Another copy of this skill ranks first (project or user directory); disabling only affects the canonical copy.",
|
|
314
|
+
"visibility.foreignWinner": "The model sees a different copy of this skill: {path} (its scan root ranks higher, so it shadows this one)",
|
|
315
|
+
"exports.targets": "Export to agents",
|
|
316
|
+
"exports.detectedHint": "Only installed agents (existing directories) are listed. Exports only affect them; whether DSH sees the skill is up to DSH, not exports.",
|
|
317
|
+
"exports.canonical": "Skill source: {path}; the entries below only expose it to agents that do not read that directory directly.",
|
|
318
|
+
"exports.none": "No agent directories available for export on this machine",
|
|
319
|
+
"exports.hint": "Saving only adds or removes links; the skill itself stays where it is.",
|
|
320
|
+
"exports.sharedUnknown": "shared root unverified",
|
|
321
|
+
"exportDefault.detected": "Export new installs to",
|
|
322
|
+
"exportDefault.none": "No agents need exporting on this machine",
|
|
323
|
+
"exportDefault.hint": "Nothing is checked by default: once a skill lands in ~/.agents/skills, DSH already sees it. Exports only matter for agents that still read their own directory, and only affect future installs.",
|
|
329
324
|
"empty.skills": "No skills installed yet",
|
|
330
325
|
"empty.skillsHint": 'Run `npx skills add owner/repo` or use "Add skill".',
|
|
331
326
|
"empty.subs": "No subscriptions yet",
|
|
@@ -353,17 +348,19 @@ var DICT = {
|
|
|
353
348
|
"form.subHint": "Only records the repository; installs nothing.",
|
|
354
349
|
"subscription.defaultRef": "default branch",
|
|
355
350
|
"confirm.deleteTitle": "Delete skill",
|
|
356
|
-
"confirm.deleteBody": "Removes it locally, disconnects this plugin’s links and
|
|
351
|
+
"confirm.deleteBody": "Removes it locally, disconnects this plugin’s links, and deletes the canonical directory and lock entry. A snapshot stays in trash for 7 days. Your source repo is untouched.",
|
|
357
352
|
"confirm.updateTitle": "Local changes",
|
|
358
353
|
"confirm.updateBody": "Updating overwrites local changes; the old content is backed up and can be rolled back.",
|
|
359
354
|
"error.conflictTitle": "Delete blocked",
|
|
360
355
|
"busy.working": "Working…",
|
|
361
356
|
"meta.from": "from",
|
|
362
357
|
"meta.noUpstream": "no upstream · updates not checked",
|
|
363
|
-
"meta.
|
|
364
|
-
"meta.
|
|
365
|
-
"meta.
|
|
366
|
-
"meta.
|
|
358
|
+
"meta.noExport": "Not exported",
|
|
359
|
+
"meta.noExportHint": "No export links to agents that need them; agents that read ~/.agents/skills can use it as-is.",
|
|
360
|
+
"meta.modelVisible": "visible to the model",
|
|
361
|
+
"meta.modelHidden": "hidden from the model",
|
|
362
|
+
"meta.manualOnly": "Manual only",
|
|
363
|
+
"meta.agentsAll": "Exported agents",
|
|
367
364
|
"meta.none": "none",
|
|
368
365
|
"filter.all": "All",
|
|
369
366
|
"filter.update": "Updates",
|
|
@@ -375,16 +372,15 @@ var DICT = {
|
|
|
375
372
|
"empty.filtered": "No matching skills",
|
|
376
373
|
"empty.filteredHint": "Try another keyword, or clear the filters",
|
|
377
374
|
"hint.unknown": "{n} skills have not been checked for updates.",
|
|
378
|
-
"group.
|
|
379
|
-
"group.dshProject": "DSH · project only",
|
|
375
|
+
"group.visible": "Model visible",
|
|
380
376
|
"group.off": "Disabled",
|
|
381
|
-
"group.
|
|
377
|
+
"group.hidden": "Enabled but hidden",
|
|
382
378
|
installed: "installed",
|
|
383
379
|
notInstalled: "not installed",
|
|
384
380
|
skillCount: "skills",
|
|
385
381
|
visibleOnly: "Visible to the model but not in the local skill library",
|
|
386
|
-
visibleOnlyOther: "
|
|
387
|
-
visibleOnlyBuiltin: "This plugin only manages skills under ~/.agents/skills;
|
|
382
|
+
visibleOnlyOther: "The model can see these, but they are not under ~/.agents/skills",
|
|
383
|
+
visibleOnlyBuiltin: "This plugin only manages skills under ~/.agents/skills; built-in skills are out of scope",
|
|
388
384
|
"builtin.view": "View built-ins ({n})",
|
|
389
385
|
"builtin.hide": "Hide built-ins",
|
|
390
386
|
external: "created outside this plugin",
|
|
@@ -437,10 +433,8 @@ var DICT = {
|
|
|
437
433
|
"viewer.info.localChangeNone": "None (matches install)",
|
|
438
434
|
"viewer.info.localChangeYes": "Modified locally",
|
|
439
435
|
"viewer.info.links": "Linked",
|
|
440
|
-
"viewer.info.
|
|
441
|
-
"viewer.info.
|
|
442
|
-
"viewer.info.scopeGlobal": "Global",
|
|
443
|
-
"viewer.info.scopeProject": "Project only",
|
|
436
|
+
"viewer.info.exported": "Exported",
|
|
437
|
+
"viewer.info.visibility": "Visibility",
|
|
444
438
|
"viewer.info.mode": "State",
|
|
445
439
|
"viewer.info.status": "Updates",
|
|
446
440
|
"viewer.info.none": "None"
|
|
@@ -1023,6 +1017,10 @@ var CSS = `
|
|
|
1023
1017
|
.dshsc-row.off { opacity: .7; }
|
|
1024
1018
|
.dshsc-cell { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
|
|
1025
1019
|
.dshsc-namerow { display: flex; align-items: center; gap: 7px; min-width: 0; }
|
|
1020
|
+
/* 可见性状态点:中性表达(实心=模型可见,空心=模型不可见),不绑定任何 Agent 身份 */
|
|
1021
|
+
.dshsc-vdot { flex: none; width: 6px; height: 6px; border-radius: 50%; box-sizing: border-box;
|
|
1022
|
+
background: var(--dsw-alias-state-success-primary); opacity: .75; }
|
|
1023
|
+
.dshsc-vdot.hidden { background: transparent; border: 1px solid var(--dsw-alias-label-tertiary); opacity: .9; }
|
|
1026
1024
|
.dshsc-name { font-size: 13px; font-weight: 600; letter-spacing: -.005em; min-width: 0;
|
|
1027
1025
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
1028
1026
|
/* 技能名本身是查看入口:清掉 button 默认外观,保留 hover 下划线提示可点 */
|
|
@@ -1048,9 +1046,6 @@ var CSS = `
|
|
|
1048
1046
|
.dshsc-agent { display: inline-flex; align-items: center; gap: 5px; flex: none; height: 20px; padding: 0 7px;
|
|
1049
1047
|
border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; color: var(--dsw-alias-label-secondary);
|
|
1050
1048
|
background: color-mix(in srgb, var(--dsw-alias-bg-layer-2) 55%, transparent); font-size: 11px; white-space: nowrap; }
|
|
1051
|
-
.dshsc-agent.dsh { color: var(--dsw-alias-brand-primary); border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 30%, var(--dsw-alias-border-l1));
|
|
1052
|
-
background: color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent); }
|
|
1053
|
-
.dshsc-agent .dshsc-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: .8; }
|
|
1054
1049
|
.dshsc-agent.more, .dshsc-agent.scope { border-style: dashed; background: transparent; color: var(--dsw-alias-label-tertiary); }
|
|
1055
1050
|
.dshsc-agent.more { margin: 0; font: inherit; font-size: 11px; line-height: 1; cursor: pointer;
|
|
1056
1051
|
-webkit-appearance: none; appearance: none; }
|
|
@@ -1467,7 +1462,7 @@ function useSkillActions({ t, rows, reload, run, setBusy, setToast, setModal })
|
|
|
1467
1462
|
});
|
|
1468
1463
|
}
|
|
1469
1464
|
async function toggleMode(row) {
|
|
1470
|
-
const mode = row.
|
|
1465
|
+
const mode = row.enabled === false ? "on" : "off";
|
|
1471
1466
|
await run(`mode:${row.name}`, () => apiCall("POST", `/skills/${encodeURIComponent(row.name)}/mode`, { mode }));
|
|
1472
1467
|
}
|
|
1473
1468
|
async function updateOne(row, overwrite) {
|
|
@@ -1530,17 +1525,20 @@ function useSkillActions({ t, rows, reload, run, setBusy, setToast, setModal })
|
|
|
1530
1525
|
}
|
|
1531
1526
|
});
|
|
1532
1527
|
}
|
|
1533
|
-
async function
|
|
1534
|
-
const current = new Set(row.
|
|
1535
|
-
const desired = new Set(
|
|
1528
|
+
async function saveExports(row, targets) {
|
|
1529
|
+
const current = new Set(row.exports.map((item) => item.target));
|
|
1530
|
+
const desired = new Set(targets);
|
|
1536
1531
|
const toRemove = [...current].filter((target) => !desired.has(target));
|
|
1537
|
-
|
|
1532
|
+
const toAdd = [...desired].filter((target) => !current.has(target));
|
|
1533
|
+
await run(`exports:${row.name}`, async () => {
|
|
1538
1534
|
if (toRemove.length > 0) {
|
|
1539
|
-
await apiCall("POST", "/
|
|
1535
|
+
await apiCall("POST", "/exports", { skill: row.name, targets: toRemove, action: "remove" });
|
|
1536
|
+
}
|
|
1537
|
+
if (toAdd.length > 0) {
|
|
1538
|
+
await apiCall("POST", "/exports", { skill: row.name, targets: toAdd, action: "add" });
|
|
1540
1539
|
}
|
|
1541
|
-
await apiCall("POST", "/links", { skill: row.name, targets: [...desired], action: "add", scopeProjects });
|
|
1542
1540
|
});
|
|
1543
|
-
setToast({ kind: "ok", text: `${row.name} · ${t("action.
|
|
1541
|
+
setToast({ kind: "ok", text: `${row.name} · ${t("action.exports")} ✓` });
|
|
1544
1542
|
}
|
|
1545
1543
|
function requestAddSkill() {
|
|
1546
1544
|
setModal({ type: "add-skill" });
|
|
@@ -1598,7 +1596,7 @@ function useSkillActions({ t, rows, reload, run, setBusy, setToast, setModal })
|
|
|
1598
1596
|
updateAll,
|
|
1599
1597
|
checkUpdates,
|
|
1600
1598
|
requestDelete,
|
|
1601
|
-
|
|
1599
|
+
saveExports,
|
|
1602
1600
|
requestAddSkill,
|
|
1603
1601
|
requestAddSubscription,
|
|
1604
1602
|
probeSkills,
|
|
@@ -1945,10 +1943,10 @@ function Header({
|
|
|
1945
1943
|
h("button", {
|
|
1946
1944
|
className: "dshsc-btn",
|
|
1947
1945
|
disabled: !writable,
|
|
1948
|
-
title: `${t("
|
|
1949
|
-
"aria-label": `${t("
|
|
1946
|
+
title: `${t("exportDefault")}:${autoLinkLabel}`,
|
|
1947
|
+
"aria-label": `${t("exportDefault")}:${autoLinkLabel}`,
|
|
1950
1948
|
onClick: onOpenAutoLink
|
|
1951
|
-
}, `${t("
|
|
1949
|
+
}, `${t("exportDefault")} · ${autoLinkCount} ▾`),
|
|
1952
1950
|
h("button", {
|
|
1953
1951
|
className: "dshsc-btn primary",
|
|
1954
1952
|
disabled: !writable,
|
|
@@ -2038,6 +2036,7 @@ function InstalledTab({
|
|
|
2038
2036
|
actions
|
|
2039
2037
|
}) {
|
|
2040
2038
|
const updatable = (row) => UPDATABLE.has(row.status);
|
|
2039
|
+
const sharedTargets = new Set((data?.agents?.detected || []).filter((agent) => agent.kind === "agent" && agent.sharedHome === "yes").map((agent) => agent.target));
|
|
2041
2040
|
function statusTag(row) {
|
|
2042
2041
|
if (row.status === "unknown") return null;
|
|
2043
2042
|
if (row.status === "no-upstream") {
|
|
@@ -2069,21 +2068,25 @@ function InstalledTab({
|
|
|
2069
2068
|
);
|
|
2070
2069
|
}
|
|
2071
2070
|
function agentChips(row) {
|
|
2072
|
-
const
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
if (
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2071
|
+
const exported = (row.exports || []).filter(
|
|
2072
|
+
(item) => !item.broken && !sharedTargets.has(item.target)
|
|
2073
|
+
);
|
|
2074
|
+
if (exported.length === 0) {
|
|
2075
|
+
return [h("span", {
|
|
2076
|
+
key: "none",
|
|
2077
|
+
className: "dshsc-unlinked",
|
|
2078
|
+
title: t("meta.noExportHint")
|
|
2079
|
+
}, t("meta.noExport"))];
|
|
2080
2080
|
}
|
|
2081
|
-
const chips =
|
|
2082
|
-
|
|
2081
|
+
const chips = [];
|
|
2082
|
+
const labels = exported.map((item) => item.label);
|
|
2083
|
+
const shown = exported.slice(0, 3);
|
|
2084
|
+
shown.forEach((item, index) => chips.push(h("span", {
|
|
2085
|
+
className: "dshsc-agent",
|
|
2083
2086
|
key: `agent-${index}`,
|
|
2084
|
-
title: label
|
|
2085
|
-
}, label
|
|
2086
|
-
if (
|
|
2087
|
+
title: item.label
|
|
2088
|
+
}, item.label)));
|
|
2089
|
+
if (exported.length > shown.length) {
|
|
2087
2090
|
const open = agentTip?.rowName === row.name;
|
|
2088
2091
|
chips.push(h("button", {
|
|
2089
2092
|
type: "button",
|
|
@@ -2097,21 +2100,17 @@ function InstalledTab({
|
|
|
2097
2100
|
onFocus: (event) => actions.onOpenAgentTip(event, row.name, labels),
|
|
2098
2101
|
onMouseLeave: actions.onCloseAgentTip,
|
|
2099
2102
|
onBlur: actions.onCloseAgentTip
|
|
2100
|
-
}, `+${
|
|
2101
|
-
}
|
|
2102
|
-
if (scopeNames.length > 0) {
|
|
2103
|
-
chips.push(h("span", {
|
|
2104
|
-
className: "dshsc-agent scope",
|
|
2105
|
-
key: "scope",
|
|
2106
|
-
title: `${t("meta.scope")}:${projects.length > 0 ? projects.join("、") : scopeNames.join("、")}`
|
|
2107
|
-
}, `${t("meta.onlyProject")} ${scopeNames.join("、")}`));
|
|
2103
|
+
}, `+${exported.length - shown.length}`));
|
|
2108
2104
|
}
|
|
2109
2105
|
return chips;
|
|
2110
2106
|
}
|
|
2111
2107
|
function rowView(row) {
|
|
2112
|
-
const off = row.
|
|
2108
|
+
const off = row.enabled === false;
|
|
2113
2109
|
const tracked = row.status !== "unmanaged";
|
|
2114
2110
|
const canUpdate = writable && updatable(row);
|
|
2111
|
+
const hidden = !off && !row.visible;
|
|
2112
|
+
const manualOnly = hidden && Boolean(row.visibility?.selfHidden);
|
|
2113
|
+
const visLabel = hidden ? t("meta.modelHidden") : t("meta.modelVisible");
|
|
2115
2114
|
return h(
|
|
2116
2115
|
"div",
|
|
2117
2116
|
{ className: `dshsc-row${off ? " off" : ""}`, key: row.name },
|
|
@@ -2121,6 +2120,12 @@ function InstalledTab({
|
|
|
2121
2120
|
h(
|
|
2122
2121
|
"div",
|
|
2123
2122
|
{ className: "dshsc-namerow" },
|
|
2123
|
+
off ? null : h("span", {
|
|
2124
|
+
className: `dshsc-vdot${hidden ? " hidden" : ""}`,
|
|
2125
|
+
role: "img",
|
|
2126
|
+
title: visLabel,
|
|
2127
|
+
"aria-label": visLabel
|
|
2128
|
+
}),
|
|
2124
2129
|
h("button", {
|
|
2125
2130
|
type: "button",
|
|
2126
2131
|
className: "dshsc-name dshsc-namebtn",
|
|
@@ -2129,10 +2134,25 @@ function InstalledTab({
|
|
|
2129
2134
|
onClick: () => actions.onView(row)
|
|
2130
2135
|
}, row.name),
|
|
2131
2136
|
statusTag(row),
|
|
2137
|
+
manualOnly ? h("span", {
|
|
2138
|
+
className: "dshsc-tag off",
|
|
2139
|
+
title: t("visibility.manualOnlyHint"),
|
|
2140
|
+
"aria-label": t("visibility.manualOnlyHint")
|
|
2141
|
+
}, t("meta.manualOnly")) : null,
|
|
2132
2142
|
off ? h("span", { className: "dshsc-tag off" }, t("status.off")) : null
|
|
2133
2143
|
),
|
|
2134
2144
|
sourceLine(row),
|
|
2135
2145
|
off ? h("div", { className: "dshsc-note" }, t("mode.offHint")) : null,
|
|
2146
|
+
// manual-only 用名字旁的「仅手动」标志表达,不占一行警告;
|
|
2147
|
+
// 被更靠前的同名副本盖住时由下面的 foreignWinner 点名,这里不再补「扫描目录里没有它」。
|
|
2148
|
+
hidden && !manualOnly && row.visibility?.winnerIsCanonical !== false ? h("div", { className: "dshsc-note warn" }, t("visibility.hiddenHint")) : null,
|
|
2149
|
+
row.visibility?.shadowScopeMismatch ? h("div", { className: "dshsc-note warn" }, t("visibility.scopeMismatch")) : null,
|
|
2150
|
+
// 同名副本在更小的 rank 上:模型看到的不是这一份,必须点名路径
|
|
2151
|
+
row.visibility?.winnerIsCanonical === false ? h(
|
|
2152
|
+
"div",
|
|
2153
|
+
{ className: "dshsc-note warn" },
|
|
2154
|
+
t("visibility.foreignWinner").replace("{path}", row.visibility.winnerPath || "?")
|
|
2155
|
+
) : null,
|
|
2136
2156
|
row.status === "broken-link" ? h("div", { className: "dshsc-note warn" }, t("status.brokenHint")) : null
|
|
2137
2157
|
),
|
|
2138
2158
|
h("div", { className: "dshsc-agents" }, agentChips(row)),
|
|
@@ -2142,12 +2162,12 @@ function InstalledTab({
|
|
|
2142
2162
|
h("button", {
|
|
2143
2163
|
className: "dshsc-switch",
|
|
2144
2164
|
role: "switch",
|
|
2145
|
-
"aria-checked": String(tracked && row.
|
|
2165
|
+
"aria-checked": String(tracked && row.enabled !== false),
|
|
2146
2166
|
"aria-label": `${row.name} ${t("mode.on")}/${t("mode.off")}`,
|
|
2147
2167
|
// 未纳入追踪时调 setMode 只会报「没有安装记录」,不如直接禁用
|
|
2148
2168
|
disabled: busy !== null || !tracked,
|
|
2149
2169
|
onClick: () => void actions.onToggleMode(row),
|
|
2150
|
-
title: tracked ? row.
|
|
2170
|
+
title: tracked ? row.enabled !== false ? t("mode.off") : t("mode.on") : t("adopt.switchHint")
|
|
2151
2171
|
}, h("span", { className: "dshsc-knob" })),
|
|
2152
2172
|
h("button", {
|
|
2153
2173
|
className: "dshsc-btn ghost dshsc-view",
|
|
@@ -2166,7 +2186,7 @@ function InstalledTab({
|
|
|
2166
2186
|
disabled: !canUpdate || busy !== null,
|
|
2167
2187
|
onClick: () => actions.onUpdate(row)
|
|
2168
2188
|
}, t("action.update")),
|
|
2169
|
-
h("button", { className: "dshsc-btn ghost", disabled: busy !== null, onClick: () => actions.
|
|
2189
|
+
h("button", { className: "dshsc-btn ghost", disabled: busy !== null, onClick: () => actions.onExports(row) }, t("action.exports")),
|
|
2170
2190
|
h("button", {
|
|
2171
2191
|
className: "dshsc-btn ghost danger sq",
|
|
2172
2192
|
disabled: !writable || busy !== null,
|
|
@@ -2179,18 +2199,17 @@ function InstalledTab({
|
|
|
2179
2199
|
}
|
|
2180
2200
|
function groupedRows() {
|
|
2181
2201
|
const groups = [];
|
|
2182
|
-
const
|
|
2183
|
-
const
|
|
2184
|
-
const
|
|
2185
|
-
|
|
2186
|
-
const unlinkedRows = visibleRows.filter((row) => row.mode === "on" && !row.scopeProjects?.length && !row.links.some((link) => !link.broken));
|
|
2187
|
-
groups.push({ key: "dsh-global", title: t("group.dshGlobal"), rows: dshRows, dot: true });
|
|
2188
|
-
if (projectRows.length) groups.push({ key: "dsh-project", title: t("group.dshProject"), rows: projectRows, dot: true });
|
|
2202
|
+
const visibleGroup = visibleRows.filter((row) => row.enabled !== false && row.visible);
|
|
2203
|
+
const offRows = visibleRows.filter((row) => row.enabled === false);
|
|
2204
|
+
const hiddenRows = visibleRows.filter((row) => row.enabled !== false && !row.visible);
|
|
2205
|
+
groups.push({ key: "visible", title: t("group.visible"), rows: visibleGroup, dot: true });
|
|
2189
2206
|
if (offRows.length) groups.push({ key: "off", title: t("group.off"), rows: offRows });
|
|
2190
|
-
if (
|
|
2207
|
+
if (hiddenRows.length) groups.push({ key: "hidden", title: t("group.hidden"), rows: hiddenRows });
|
|
2191
2208
|
for (const agent of data?.agents?.detected || []) {
|
|
2192
2209
|
if (agent.kind !== "agent") continue;
|
|
2193
|
-
const matched = visibleRows.filter(
|
|
2210
|
+
const matched = visibleRows.filter(
|
|
2211
|
+
(row) => (row.exports || []).some((item) => item.target === agent.target && !item.broken)
|
|
2212
|
+
);
|
|
2194
2213
|
if (matched.length) groups.push({ key: agent.target, title: agent.label, rows: matched });
|
|
2195
2214
|
}
|
|
2196
2215
|
return groups.filter((group) => group.rows.length > 0);
|
|
@@ -2345,6 +2364,7 @@ function InstalledTab({
|
|
|
2345
2364
|
|
|
2346
2365
|
// client/views/agent-tip.js
|
|
2347
2366
|
function AgentTip({ t, agentTip, agentTipPos, nodeRef }) {
|
|
2367
|
+
const labels = agentTip.labels || [];
|
|
2348
2368
|
const node = h(
|
|
2349
2369
|
"div",
|
|
2350
2370
|
{
|
|
@@ -2358,15 +2378,16 @@ function AgentTip({ t, agentTip, agentTipPos, nodeRef }) {
|
|
|
2358
2378
|
"div",
|
|
2359
2379
|
{ className: "dshsc-agenttip-head" },
|
|
2360
2380
|
h("span", null, t("meta.agentsAll")),
|
|
2361
|
-
h("span", { className: "dshsc-agenttip-n" },
|
|
2381
|
+
h("span", { className: "dshsc-agenttip-n" }, labels.length)
|
|
2362
2382
|
),
|
|
2363
2383
|
h(
|
|
2364
2384
|
"div",
|
|
2365
2385
|
{ className: "dshsc-agenttip-list" },
|
|
2366
|
-
|
|
2367
|
-
className:
|
|
2368
|
-
key: `agenttip-${index}
|
|
2369
|
-
|
|
2386
|
+
labels.map((label, index) => h("span", {
|
|
2387
|
+
className: "dshsc-agent",
|
|
2388
|
+
key: `agenttip-${index}`,
|
|
2389
|
+
title: label
|
|
2390
|
+
}, label))
|
|
2370
2391
|
)
|
|
2371
2392
|
);
|
|
2372
2393
|
return portalize(node);
|
|
@@ -2687,124 +2708,65 @@ function FormModal({ t, title, hint, fields, onCancel, onSubmit }) {
|
|
|
2687
2708
|
))
|
|
2688
2709
|
);
|
|
2689
2710
|
}
|
|
2690
|
-
function ConfirmModal({ t, title, body, onCancel, onConfirm, danger, confirmLabel }) {
|
|
2711
|
+
function ConfirmModal({ t, title, body, onCancel, onConfirm, danger, confirmLabel, hideCancel = false }) {
|
|
2691
2712
|
return h(Overlay, {
|
|
2692
2713
|
t,
|
|
2693
2714
|
title,
|
|
2694
2715
|
onCancel,
|
|
2695
2716
|
footer: [
|
|
2696
|
-
h("button", { key: "c", className: "dshsc-btn ghost", onClick: onCancel }, t("action.cancel")),
|
|
2717
|
+
...hideCancel ? [] : [h("button", { key: "c", className: "dshsc-btn ghost", onClick: onCancel }, t("action.cancel"))],
|
|
2697
2718
|
h("button", { key: "o", className: `dshsc-btn${danger ? " danger" : " primary"}`, onClick: onConfirm }, confirmLabel || t("action.save"))
|
|
2698
2719
|
]
|
|
2699
2720
|
}, h("p", { className: "dshsc-sub", style: { whiteSpace: "pre-wrap" } }, body));
|
|
2700
2721
|
}
|
|
2701
|
-
function
|
|
2722
|
+
function ExportsModal({ t, row, agents, onCancel, onSave }) {
|
|
2702
2723
|
const candidates = agents.filter(
|
|
2703
|
-
(agent) => agent.kind === "
|
|
2724
|
+
(agent) => agent.kind === "agent" && agent.exists && agent.exportable
|
|
2704
2725
|
);
|
|
2705
2726
|
const [targets, setTargets] = import_react4.default.useState(() => {
|
|
2706
|
-
const
|
|
2707
|
-
|
|
2708
|
-
initial.add("dsh-global");
|
|
2709
|
-
return initial;
|
|
2727
|
+
const available = new Set(candidates.map((agent) => agent.target));
|
|
2728
|
+
return new Set((row.exports || []).map((item) => item.target).filter((target) => available.has(target)));
|
|
2710
2729
|
});
|
|
2711
|
-
const [scopeProjects, setScopeProjects] = import_react4.default.useState(row.scopeProjects || []);
|
|
2712
|
-
const [scopeMode, setScopeMode] = import_react4.default.useState((row.scopeProjects || []).length > 0 ? "projects" : "global");
|
|
2713
|
-
const workspacePaths = new Set(workspaces.map((workspace) => workspace.path));
|
|
2714
|
-
const otherProjects = scopeProjects.filter((project) => !workspacePaths.has(project));
|
|
2715
2730
|
const toggle = (target) => setTargets((prev) => {
|
|
2716
2731
|
const next = new Set(prev);
|
|
2717
2732
|
if (next.has(target)) next.delete(target);
|
|
2718
2733
|
else next.add(target);
|
|
2719
2734
|
return next;
|
|
2720
2735
|
});
|
|
2721
|
-
const toggleProject = (project) => setScopeProjects((prev) => prev.includes(project) ? prev.filter((item) => item !== project) : [...prev, project]);
|
|
2722
|
-
const chooseDirectory = async () => {
|
|
2723
|
-
const dir = await pickDirectory?.();
|
|
2724
|
-
if (!dir) return;
|
|
2725
|
-
setScopeProjects((prev) => prev.includes(dir) ? prev : [...prev, dir]);
|
|
2726
|
-
};
|
|
2727
2736
|
return h(
|
|
2728
2737
|
Overlay,
|
|
2729
2738
|
{
|
|
2730
2739
|
t,
|
|
2731
|
-
title: `${t("action.
|
|
2740
|
+
title: `${t("action.exports")} · ${row.name}`,
|
|
2732
2741
|
onCancel,
|
|
2733
2742
|
footer: [
|
|
2734
2743
|
h("button", { key: "c", className: "dshsc-btn ghost", onClick: onCancel }, t("action.cancel")),
|
|
2735
|
-
h("button", {
|
|
2736
|
-
key: "s",
|
|
2737
|
-
className: "dshsc-btn primary",
|
|
2738
|
-
onClick: () => onSave([...targets], scopeMode === "projects" ? scopeProjects.filter((item) => item.trim()) : [])
|
|
2739
|
-
}, t("action.save"))
|
|
2744
|
+
h("button", { key: "s", className: "dshsc-btn primary", onClick: () => onSave([...targets]) }, t("action.save"))
|
|
2740
2745
|
]
|
|
2741
2746
|
},
|
|
2742
|
-
h("div", { className: "dshsc-group" }, t("
|
|
2743
|
-
h("p", { className: "dshsc-sub" }, t("
|
|
2744
|
-
h("p", { className: "dshsc-sub" }, t("
|
|
2747
|
+
h("div", { className: "dshsc-group" }, t("exports.targets")),
|
|
2748
|
+
h("p", { className: "dshsc-sub" }, t("exports.detectedHint")),
|
|
2749
|
+
h("p", { className: "dshsc-sub" }, t("exports.canonical").replace("{path}", `~/.agents/skills/${row.name}`)),
|
|
2750
|
+
candidates.length === 0 ? h("p", { className: "dshsc-sub" }, t("exports.none")) : null,
|
|
2745
2751
|
...candidates.map((agent) => h(
|
|
2746
2752
|
"label",
|
|
2747
|
-
{ className:
|
|
2753
|
+
{ className: "dshsc-check", key: agent.target },
|
|
2748
2754
|
h("input", {
|
|
2749
2755
|
type: "checkbox",
|
|
2750
|
-
checked:
|
|
2751
|
-
disabled: agent.target === "dsh-global",
|
|
2756
|
+
checked: targets.has(agent.target),
|
|
2752
2757
|
onChange: () => toggle(agent.target)
|
|
2753
2758
|
}),
|
|
2754
|
-
h("span", null, agent.
|
|
2755
|
-
|
|
2759
|
+
h("span", null, agent.label),
|
|
2760
|
+
// 候选里已经没有「读共享根」的 agent 了;'no' 是常态不必标注,只提示没验证过的
|
|
2761
|
+
agent.sharedHome === "unknown" ? h("small", null, t("exports.sharedUnknown")) : null,
|
|
2756
2762
|
h("small", { title: agent.dir }, agent.dir)
|
|
2757
2763
|
)),
|
|
2758
|
-
h("
|
|
2759
|
-
h(
|
|
2760
|
-
"label",
|
|
2761
|
-
{ className: "dshsc-check" },
|
|
2762
|
-
h("input", { type: "radio", name: `scope-${row.name}`, checked: scopeMode === "global", onChange: () => setScopeMode("global") }),
|
|
2763
|
-
h("span", null, t("links.scopeGlobal"))
|
|
2764
|
-
),
|
|
2765
|
-
h(
|
|
2766
|
-
"label",
|
|
2767
|
-
{ className: "dshsc-check" },
|
|
2768
|
-
h("input", { type: "radio", name: `scope-${row.name}`, checked: scopeMode === "projects", onChange: () => setScopeMode("projects") }),
|
|
2769
|
-
h("span", null, t("links.scopeProjects"))
|
|
2770
|
-
),
|
|
2771
|
-
scopeMode === "projects" ? h(
|
|
2772
|
-
"div",
|
|
2773
|
-
null,
|
|
2774
|
-
h("div", { className: "dshsc-group" }, t("links.workspaces")),
|
|
2775
|
-
workspaces.length === 0 ? h("p", { className: "dshsc-sub" }, t("links.noWorkspaces")) : h("div", { className: "dshsc-panel" }, workspaces.map((workspace) => h(
|
|
2776
|
-
"label",
|
|
2777
|
-
{ className: "dshsc-check", key: workspace.id },
|
|
2778
|
-
h("input", {
|
|
2779
|
-
type: "checkbox",
|
|
2780
|
-
checked: scopeProjects.includes(workspace.path),
|
|
2781
|
-
onChange: () => toggleProject(workspace.path)
|
|
2782
|
-
}),
|
|
2783
|
-
h("span", null, workspace.title),
|
|
2784
|
-
h("small", null, workspace.path)
|
|
2785
|
-
))),
|
|
2786
|
-
otherProjects.length > 0 ? h("div", { className: "dshsc-group" }, t("links.selected")) : null,
|
|
2787
|
-
...otherProjects.map((project) => h(
|
|
2788
|
-
"div",
|
|
2789
|
-
{ className: "dshsc-sub-entry", key: project },
|
|
2790
|
-
h("span", { className: "dshsc-code" }, project),
|
|
2791
|
-
h(
|
|
2792
|
-
"div",
|
|
2793
|
-
{ className: "dshsc-actions" },
|
|
2794
|
-
h("button", {
|
|
2795
|
-
className: "dshsc-btn ghost danger",
|
|
2796
|
-
onClick: () => setScopeProjects((prev) => prev.filter((item) => item !== project))
|
|
2797
|
-
}, t("action.delete"))
|
|
2798
|
-
)
|
|
2799
|
-
)),
|
|
2800
|
-
h("button", { className: "dshsc-btn", style: { alignSelf: "flex-start", marginTop: 6 }, onClick: () => void chooseDirectory() }, t("links.pickDirectory"))
|
|
2801
|
-
) : null,
|
|
2802
|
-
h("p", { className: "dshsc-sub" }, t("links.hint"))
|
|
2764
|
+
h("p", { className: "dshsc-sub" }, t("exports.hint"))
|
|
2803
2765
|
);
|
|
2804
2766
|
}
|
|
2805
|
-
function
|
|
2767
|
+
function DefaultExportModal({ t, settings, agents, onCancel, onSave }) {
|
|
2806
2768
|
const [targets, setTargets] = import_react4.default.useState(() => new Set(
|
|
2807
|
-
Array.isArray(settings.
|
|
2769
|
+
Array.isArray(settings.exportTargets) ? settings.exportTargets : []
|
|
2808
2770
|
));
|
|
2809
2771
|
const toggle = (target) => setTargets((prev) => {
|
|
2810
2772
|
const next = new Set(prev);
|
|
@@ -2816,50 +2778,29 @@ function AutoLinkModal({ t, settings, agents, dsh, onCancel, onSave }) {
|
|
|
2816
2778
|
Overlay,
|
|
2817
2779
|
{
|
|
2818
2780
|
t,
|
|
2819
|
-
title: t("
|
|
2781
|
+
title: t("exportDefault"),
|
|
2820
2782
|
onCancel,
|
|
2821
2783
|
footer: [
|
|
2822
2784
|
h("button", { key: "c", className: "dshsc-btn ghost", onClick: onCancel }, t("action.cancel")),
|
|
2823
2785
|
h("button", { key: "s", className: "dshsc-btn primary", onClick: () => onSave([...targets]) }, t("action.save"))
|
|
2824
2786
|
]
|
|
2825
2787
|
},
|
|
2826
|
-
h("div", { className: "dshsc-group" }, t("
|
|
2827
|
-
h(
|
|
2828
|
-
"
|
|
2829
|
-
{ className: "dshsc-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
onChange: () => {
|
|
2840
|
-
}
|
|
2841
|
-
}),
|
|
2842
|
-
h("span", null, dsh?.label || t("links.dshGlobal")),
|
|
2843
|
-
h("small", null, t("links.required")),
|
|
2844
|
-
dsh?.dir ? h("small", { title: dsh.dir }, dsh.dir) : null
|
|
2845
|
-
),
|
|
2846
|
-
...agents.map((agent) => h(
|
|
2847
|
-
"label",
|
|
2848
|
-
{ className: "dshsc-check", key: agent.target },
|
|
2849
|
-
h("input", {
|
|
2850
|
-
type: "checkbox",
|
|
2851
|
-
checked: targets.has(agent.target),
|
|
2852
|
-
onChange: () => toggle(agent.target)
|
|
2853
|
-
}),
|
|
2854
|
-
h("span", null, agent.label),
|
|
2855
|
-
h("small", null, agent.dir)
|
|
2856
|
-
))
|
|
2857
|
-
),
|
|
2858
|
-
agents.length === 0 ? h("p", { className: "dshsc-sub" }, t("autoLink.none")) : null,
|
|
2859
|
-
h("p", { className: "dshsc-sub" }, t("autoLink.hint"))
|
|
2788
|
+
h("div", { className: "dshsc-group" }, t("exportDefault.detected")),
|
|
2789
|
+
agents.length === 0 ? h("p", { className: "dshsc-sub" }, t("exportDefault.none")) : h("div", { className: "dshsc-panel" }, agents.map((agent) => h(
|
|
2790
|
+
"label",
|
|
2791
|
+
{ className: "dshsc-check", key: agent.target },
|
|
2792
|
+
h("input", {
|
|
2793
|
+
type: "checkbox",
|
|
2794
|
+
checked: targets.has(agent.target),
|
|
2795
|
+
onChange: () => toggle(agent.target)
|
|
2796
|
+
}),
|
|
2797
|
+
h("span", null, agent.label),
|
|
2798
|
+
h("small", null, agent.dir)
|
|
2799
|
+
))),
|
|
2800
|
+
h("p", { className: "dshsc-sub" }, t("exportDefault.hint"))
|
|
2860
2801
|
);
|
|
2861
2802
|
}
|
|
2862
|
-
function Modals({ t, modal, data, autoLinkAgents,
|
|
2803
|
+
function Modals({ t, modal, data, autoLinkAgents, actions, onClose, onSaveAutoLink }) {
|
|
2863
2804
|
if (modal.type === "add-skill") {
|
|
2864
2805
|
return h(AddSkillModal, {
|
|
2865
2806
|
t,
|
|
@@ -2882,26 +2823,23 @@ function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, ac
|
|
|
2882
2823
|
onSubmit: (form) => void actions.addSubscription(form)
|
|
2883
2824
|
});
|
|
2884
2825
|
}
|
|
2885
|
-
if (modal.type === "
|
|
2886
|
-
return h(
|
|
2826
|
+
if (modal.type === "exports") {
|
|
2827
|
+
return h(ExportsModal, {
|
|
2887
2828
|
t,
|
|
2888
2829
|
row: modal.row,
|
|
2889
2830
|
agents: data?.agents?.all || [],
|
|
2890
|
-
workspaces: data?.workspaces || [],
|
|
2891
|
-
pickDirectory,
|
|
2892
2831
|
onCancel: onClose,
|
|
2893
|
-
onSave: (targets
|
|
2832
|
+
onSave: (targets) => {
|
|
2894
2833
|
onClose();
|
|
2895
|
-
void actions.
|
|
2834
|
+
void actions.saveExports(modal.row, targets);
|
|
2896
2835
|
}
|
|
2897
2836
|
});
|
|
2898
2837
|
}
|
|
2899
2838
|
if (modal.type === "autolink") {
|
|
2900
|
-
return h(
|
|
2839
|
+
return h(DefaultExportModal, {
|
|
2901
2840
|
t,
|
|
2902
2841
|
settings: data?.settings || {},
|
|
2903
2842
|
agents: autoLinkAgents,
|
|
2904
|
-
dsh: autoLinkDsh,
|
|
2905
2843
|
onCancel: onClose,
|
|
2906
2844
|
onSave: (targets) => void onSaveAutoLink(targets)
|
|
2907
2845
|
});
|
|
@@ -2912,6 +2850,7 @@ function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, ac
|
|
|
2912
2850
|
title: t("confirm.deleteTitle"),
|
|
2913
2851
|
body: t("confirm.deleteBody"),
|
|
2914
2852
|
danger: true,
|
|
2853
|
+
confirmLabel: t("action.delete"),
|
|
2915
2854
|
onCancel: onClose,
|
|
2916
2855
|
onConfirm: modal.onConfirm
|
|
2917
2856
|
});
|
|
@@ -2921,6 +2860,7 @@ function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, ac
|
|
|
2921
2860
|
t,
|
|
2922
2861
|
title: t("confirm.updateTitle"),
|
|
2923
2862
|
body: t("confirm.updateBody"),
|
|
2863
|
+
confirmLabel: t("action.update"),
|
|
2924
2864
|
onCancel: onClose,
|
|
2925
2865
|
onConfirm: modal.onConfirm
|
|
2926
2866
|
});
|
|
@@ -2934,7 +2874,9 @@ function Modals({ t, modal, data, autoLinkAgents, autoLinkDsh, pickDirectory, ac
|
|
|
2934
2874
|
${conflicts.map((item) => `${item.label}: ${item.linkPath}`).join("\n")}`,
|
|
2935
2875
|
onCancel: onClose,
|
|
2936
2876
|
onConfirm: onClose,
|
|
2937
|
-
confirmLabel: t("action.
|
|
2877
|
+
confirmLabel: t("action.close"),
|
|
2878
|
+
danger: true,
|
|
2879
|
+
hideCancel: true
|
|
2938
2880
|
});
|
|
2939
2881
|
}
|
|
2940
2882
|
return null;
|
|
@@ -3433,7 +3375,7 @@ function SkillReader({
|
|
|
3433
3375
|
onClose,
|
|
3434
3376
|
onOpenPage,
|
|
3435
3377
|
onUpdate,
|
|
3436
|
-
|
|
3378
|
+
onExports,
|
|
3437
3379
|
onToast
|
|
3438
3380
|
}) {
|
|
3439
3381
|
const { state, file, loadFile, retry } = useSkillReader(row);
|
|
@@ -3591,7 +3533,7 @@ function SkillReader({
|
|
|
3591
3533
|
{ className: "dshsc-reader-title" },
|
|
3592
3534
|
h("h2", { id: "dshsc-reader-name", tabIndex: -1 }, name || t("viewer.title")),
|
|
3593
3535
|
row?.status && STATUS_KEY[row.status] ? h("span", { className: `dshsc-tag ${row.status === "current" ? "ok" : "warn"}` }, t(STATUS_KEY[row.status])) : null,
|
|
3594
|
-
row?.
|
|
3536
|
+
row?.enabled === false ? h("span", { className: "dshsc-tag off" }, t("status.off")) : null,
|
|
3595
3537
|
variant === "drawer" ? h("button", {
|
|
3596
3538
|
type: "button",
|
|
3597
3539
|
className: "dshsc-reader-close",
|
|
@@ -3830,8 +3772,7 @@ function SkillReader({
|
|
|
3830
3772
|
);
|
|
3831
3773
|
}
|
|
3832
3774
|
function infoPanel() {
|
|
3833
|
-
const
|
|
3834
|
-
const projects = row?.scopeProjects || [];
|
|
3775
|
+
const exportLabels = (row?.exports || []).filter((item) => !item.broken).map((item) => item.label);
|
|
3835
3776
|
const hash = row?.lockHash || row?.currentHash || null;
|
|
3836
3777
|
const rows = [];
|
|
3837
3778
|
rows.push(["viewer.info.name", h("span", { className: "mono" }, name), "mono"]);
|
|
@@ -3850,9 +3791,9 @@ function SkillReader({
|
|
|
3850
3791
|
["viewer.info.localChange", row?.status === "locally-modified" || row?.status === "conflict" ? t("viewer.info.localChangeYes") : t("viewer.info.localChangeNone")]
|
|
3851
3792
|
];
|
|
3852
3793
|
const linkRows = [
|
|
3853
|
-
["viewer.info.
|
|
3854
|
-
["viewer.info.
|
|
3855
|
-
["viewer.info.mode", row?.
|
|
3794
|
+
["viewer.info.visibility", row?.visible ? t("meta.modelVisible") : t("meta.modelHidden")],
|
|
3795
|
+
["viewer.info.exported", exportLabels.length > 0 ? exportLabels.join("、") : t("viewer.info.none")],
|
|
3796
|
+
["viewer.info.mode", row?.enabled === false ? t("status.off") : t("mode.on")],
|
|
3856
3797
|
["viewer.info.status", STATUS_KEY[row?.status] ? t(STATUS_KEY[row.status]) : t("status.unknown")]
|
|
3857
3798
|
];
|
|
3858
3799
|
return h(
|
|
@@ -3899,7 +3840,7 @@ function SkillReader({
|
|
|
3899
3840
|
onClick: () => void copyTextWithToast(row?.path || "", "viewer.copied")
|
|
3900
3841
|
}, t("viewer.copyPath")),
|
|
3901
3842
|
variant === "drawer" && onOpenPage ? h("button", { type: "button", className: "dshsc-btn ghost", onClick: () => onOpenPage() }, t("viewer.openPage")) : null,
|
|
3902
|
-
|
|
3843
|
+
onExports ? h("button", { type: "button", className: "dshsc-btn", onClick: () => onExports?.(row) }, t("action.exports")) : null,
|
|
3903
3844
|
onUpdate ? h("button", {
|
|
3904
3845
|
type: "button",
|
|
3905
3846
|
className: "dshsc-btn tint",
|
|
@@ -3991,15 +3932,15 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
3991
3932
|
const updateCount = rows.filter(updatable).length;
|
|
3992
3933
|
const bulkCount = rows.filter((row) => row.status === "update-available").length;
|
|
3993
3934
|
const unknownCount = rows.filter((row) => row.status === "unknown").length;
|
|
3994
|
-
const offCount = rows.filter((row) => row.
|
|
3935
|
+
const offCount = rows.filter((row) => row.enabled === false).length;
|
|
3995
3936
|
const counts = { all: rows.length, update: updateCount, unknown: unknownCount, off: offCount };
|
|
3996
3937
|
const queryText = query.trim().toLowerCase();
|
|
3997
3938
|
const visibleRows = rows.filter((row) => {
|
|
3998
3939
|
if (filter === "update" && !updatable(row)) return false;
|
|
3999
3940
|
if (filter === "unknown" && row.status !== "unknown") return false;
|
|
4000
|
-
if (filter === "off" && row.
|
|
3941
|
+
if (filter === "off" && row.enabled !== false) return false;
|
|
4001
3942
|
if (!queryText) return true;
|
|
4002
|
-
const agentLabels = row.
|
|
3943
|
+
const agentLabels = row.exports.map((item) => item.label);
|
|
4003
3944
|
return [row.name, row.source || "", ...agentLabels].join(" ").toLowerCase().includes(queryText);
|
|
4004
3945
|
});
|
|
4005
3946
|
import_react6.default.useEffect(() => {
|
|
@@ -4008,11 +3949,10 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
4008
3949
|
setViewer(null);
|
|
4009
3950
|
setToast({ kind: "err", text: t("viewer.gone") });
|
|
4010
3951
|
}, [data, rows, t, viewer]);
|
|
4011
|
-
const
|
|
4012
|
-
const
|
|
4013
|
-
const autoLinkAgents = Array.isArray(
|
|
4014
|
-
const
|
|
4015
|
-
const autoLinkLabel = [t("links.dshGlobal"), ...autoLinkAgents.map((agent) => agent.label)].join("、");
|
|
3952
|
+
const exportTargetOptions = (data?.agents?.detected || []).filter((agent) => agent.kind === "agent" && agent.exportable);
|
|
3953
|
+
const exportSelection = data?.settings?.exportTargets;
|
|
3954
|
+
const autoLinkAgents = Array.isArray(exportSelection) ? exportTargetOptions.filter((agent) => exportSelection.includes(agent.target)) : [];
|
|
3955
|
+
const autoLinkLabel = autoLinkAgents.length > 0 ? autoLinkAgents.map((agent) => agent.label).join("、") : t("exportDefault.none");
|
|
4016
3956
|
const placement = (placementOverride ?? data?.settings?.placement) === "sidebar" ? "sidebar" : "settings";
|
|
4017
3957
|
function listScroller() {
|
|
4018
3958
|
const node = rootRef.current?.querySelector?.(".dshsc-listscroll");
|
|
@@ -4060,7 +4000,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
4060
4000
|
}
|
|
4061
4001
|
async function saveAutoLink(targets) {
|
|
4062
4002
|
setModal(null);
|
|
4063
|
-
await run("settings", () => apiCall("POST", "/settings", {
|
|
4003
|
+
await run("settings", () => apiCall("POST", "/settings", { exportTargets: targets }));
|
|
4064
4004
|
}
|
|
4065
4005
|
const installedActions = {
|
|
4066
4006
|
onView: (row) => openViewer(row),
|
|
@@ -4068,7 +4008,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
4068
4008
|
onToggleMode: actions.toggleMode,
|
|
4069
4009
|
onUpdate: actions.requestUpdate,
|
|
4070
4010
|
onDelete: actions.requestDelete,
|
|
4071
|
-
|
|
4011
|
+
onExports: (row) => setModal({ type: "exports", row }),
|
|
4072
4012
|
onCheckUpdates: actions.checkUpdates,
|
|
4073
4013
|
onUpdateAll: actions.updateAll,
|
|
4074
4014
|
onAddSkill: actions.requestAddSkill,
|
|
@@ -4100,7 +4040,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
4100
4040
|
onClose: closeViewer,
|
|
4101
4041
|
onOpenPage: viewer.mode === "drawer" ? openPage : null,
|
|
4102
4042
|
onUpdate: actions.requestUpdate,
|
|
4103
|
-
|
|
4043
|
+
onExports: (row) => setModal({ type: "exports", row }),
|
|
4104
4044
|
onToast: (next) => setToast(next)
|
|
4105
4045
|
}) : null;
|
|
4106
4046
|
const listNode = h(
|
|
@@ -4112,7 +4052,7 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
4112
4052
|
plugin: data?.plugin,
|
|
4113
4053
|
writable,
|
|
4114
4054
|
autoLinkLabel,
|
|
4115
|
-
autoLinkCount: autoLinkAgents.length
|
|
4055
|
+
autoLinkCount: autoLinkAgents.length,
|
|
4116
4056
|
tab,
|
|
4117
4057
|
settingsOpen,
|
|
4118
4058
|
settingsRef,
|
|
@@ -4177,14 +4117,13 @@ function Section({ t, page = false, pickDirectory, onPlacementChange }) {
|
|
|
4177
4117
|
t,
|
|
4178
4118
|
modal,
|
|
4179
4119
|
data,
|
|
4180
|
-
autoLinkAgents:
|
|
4181
|
-
autoLinkDsh,
|
|
4120
|
+
autoLinkAgents: exportTargetOptions,
|
|
4182
4121
|
pickDirectory,
|
|
4183
4122
|
actions: {
|
|
4184
4123
|
probeSkills: actions.probeSkills,
|
|
4185
4124
|
installSkills: actions.installSkills,
|
|
4186
4125
|
addSubscription: actions.addSubscription,
|
|
4187
|
-
|
|
4126
|
+
saveExports: actions.saveExports
|
|
4188
4127
|
},
|
|
4189
4128
|
onClose: () => setModal(null),
|
|
4190
4129
|
onSaveAutoLink: saveAutoLink
|