agim-cli 1.2.1 → 1.2.17
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 +52 -0
- package/README.md +18 -5
- package/README.zh-CN.md +20 -7
- package/dist/cli-ui/entry-menu.d.ts +2 -0
- package/dist/cli-ui/entry-menu.d.ts.map +1 -1
- package/dist/cli-ui/entry-menu.js +1 -0
- package/dist/cli-ui/entry-menu.js.map +1 -1
- package/dist/cli-ui/i18n.d.ts +5 -0
- package/dist/cli-ui/i18n.d.ts.map +1 -1
- package/dist/cli-ui/i18n.js +10 -0
- package/dist/cli-ui/i18n.js.map +1 -1
- package/dist/cli.js +117 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/admin-bootstrap.d.ts +14 -0
- package/dist/core/admin-bootstrap.d.ts.map +1 -1
- package/dist/core/admin-bootstrap.js +21 -0
- package/dist/core/admin-bootstrap.js.map +1 -1
- package/dist/core/commands/builtin.d.ts.map +1 -1
- package/dist/core/commands/builtin.js +2 -0
- package/dist/core/commands/builtin.js.map +1 -1
- package/dist/core/commands/router.js +1 -1
- package/dist/core/commands/router.js.map +1 -1
- package/dist/core/commands/web.d.ts +3 -0
- package/dist/core/commands/web.d.ts.map +1 -0
- package/dist/core/commands/web.js +28 -0
- package/dist/core/commands/web.js.map +1 -0
- package/dist/core/intent.d.ts +11 -2
- package/dist/core/intent.d.ts.map +1 -1
- package/dist/core/intent.js +26 -4
- package/dist/core/intent.js.map +1 -1
- package/dist/core/memory.js.map +1 -1
- package/dist/core/render-router.d.ts.map +1 -1
- package/dist/core/render-router.js +3 -2
- package/dist/core/render-router.js.map +1 -1
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +8 -1
- package/dist/core/router.js.map +1 -1
- package/dist/core/types.d.ts +3 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/plugins/agents/acp/acp-client.d.ts.map +1 -1
- package/dist/plugins/agents/acp/acp-client.js +7 -0
- package/dist/plugins/agents/acp/acp-client.js.map +1 -1
- package/dist/plugins/agents/acp/discovery.d.ts.map +1 -1
- package/dist/plugins/agents/acp/discovery.js +4 -0
- package/dist/plugins/agents/acp/discovery.js.map +1 -1
- package/dist/plugins/agents/acp/url-guard.d.ts +44 -0
- package/dist/plugins/agents/acp/url-guard.d.ts.map +1 -0
- package/dist/plugins/agents/acp/url-guard.js +109 -0
- package/dist/plugins/agents/acp/url-guard.js.map +1 -0
- package/dist/web/env-mask.d.ts +21 -0
- package/dist/web/env-mask.d.ts.map +1 -0
- package/dist/web/env-mask.js +44 -0
- package/dist/web/env-mask.js.map +1 -0
- package/dist/web/public/assets/a2a-Dk2fSs33.js +7 -0
- package/dist/web/public/assets/a2a-Dk2fSs33.js.map +1 -0
- package/dist/web/public/assets/activity-eiIPshcV.js +7 -0
- package/dist/web/public/assets/activity-eiIPshcV.js.map +1 -0
- package/dist/web/public/assets/admins-DlbQYdW_.js +12 -0
- package/dist/web/public/assets/admins-DlbQYdW_.js.map +1 -0
- package/dist/web/public/assets/agents-BMI1WbZj.js +12 -0
- package/dist/web/public/assets/agents-BMI1WbZj.js.map +1 -0
- package/dist/web/public/assets/approvals-DlXS_sKD.js +10 -0
- package/dist/web/public/assets/approvals-DlXS_sKD.js.map +1 -0
- package/dist/web/public/assets/audit-C8I8xC_6.js +2 -0
- package/dist/web/public/assets/audit-C8I8xC_6.js.map +1 -0
- package/dist/web/public/assets/bgjobs-PFYinH7D.js +7 -0
- package/dist/web/public/assets/bgjobs-PFYinH7D.js.map +1 -0
- package/dist/web/public/assets/brain-DEEJttEL.js +7 -0
- package/dist/web/public/assets/brain-DEEJttEL.js.map +1 -0
- package/dist/web/public/assets/briefcase-BlMy8gI6.js +7 -0
- package/dist/web/public/assets/briefcase-BlMy8gI6.js.map +1 -0
- package/dist/web/public/assets/browser-ponyfill-BOcGq8h9.js +3 -0
- package/dist/web/public/assets/browser-ponyfill-BOcGq8h9.js.map +1 -0
- package/dist/web/public/assets/chevron-right-DmABPvoA.js +7 -0
- package/dist/web/public/assets/chevron-right-DmABPvoA.js.map +1 -0
- package/dist/web/public/assets/circle-check-C0Qpg1vL.js +7 -0
- package/dist/web/public/assets/circle-check-C0Qpg1vL.js.map +1 -0
- package/dist/web/public/assets/circle-check-big-C8LG3beV.js +7 -0
- package/dist/web/public/assets/circle-check-big-C8LG3beV.js.map +1 -0
- package/dist/web/public/assets/circle-x-D_cRHcHK.js +7 -0
- package/dist/web/public/assets/circle-x-D_cRHcHK.js.map +1 -0
- package/dist/web/public/assets/confirm-dialog-Baz_xFle.js +2 -0
- package/dist/web/public/assets/confirm-dialog-Baz_xFle.js.map +1 -0
- package/dist/web/public/assets/data-table--I_ktDF4.js +17 -0
- package/dist/web/public/assets/data-table--I_ktDF4.js.map +1 -0
- package/dist/web/public/assets/dialog-DZpoEskO.js +6 -0
- package/dist/web/public/assets/dialog-DZpoEskO.js.map +1 -0
- package/dist/web/public/assets/download-DbFGHwZ5.js +7 -0
- package/dist/web/public/assets/download-DbFGHwZ5.js.map +1 -0
- package/dist/web/public/assets/email-BB1Hq8eE.js +7 -0
- package/dist/web/public/assets/email-BB1Hq8eE.js.map +1 -0
- package/dist/web/public/assets/empty-state-DXNa90pP.js +2 -0
- package/dist/web/public/assets/empty-state-DXNa90pP.js.map +1 -0
- package/dist/web/public/assets/env-Bqrb9XkC.js +2 -0
- package/dist/web/public/assets/env-Bqrb9XkC.js.map +1 -0
- package/dist/web/public/assets/external-link-nhnJN0qg.js +7 -0
- package/dist/web/public/assets/external-link-nhnJN0qg.js.map +1 -0
- package/dist/web/public/assets/eye-IKkn_oUo.js +12 -0
- package/dist/web/public/assets/eye-IKkn_oUo.js.map +1 -0
- package/dist/web/public/assets/facts-C7Qy9vTw.js +2 -0
- package/dist/web/public/assets/facts-C7Qy9vTw.js.map +1 -0
- package/dist/web/public/assets/health-CMRdeNEW.js +2 -0
- package/dist/web/public/assets/health-CMRdeNEW.js.map +1 -0
- package/dist/web/public/assets/hot-Bh5Nrc7i.js +17 -0
- package/dist/web/public/assets/hot-Bh5Nrc7i.js.map +1 -0
- package/dist/web/public/assets/index-CpGWCLE5.js +166 -0
- package/dist/web/public/assets/index-CpGWCLE5.js.map +1 -0
- package/dist/web/public/assets/index-GpceOxum.css +1 -0
- package/dist/web/public/assets/installed-FYLkPij2.js +7 -0
- package/dist/web/public/assets/installed-FYLkPij2.js.map +1 -0
- package/dist/web/public/assets/jobs-BmqLUzHp.js +2 -0
- package/dist/web/public/assets/jobs-BmqLUzHp.js.map +1 -0
- package/dist/web/public/assets/layout-9Gp_myEd.js +2 -0
- package/dist/web/public/assets/layout-9Gp_myEd.js.map +1 -0
- package/dist/web/public/assets/layout-BZaHqf69.js +2 -0
- package/dist/web/public/assets/layout-BZaHqf69.js.map +1 -0
- package/dist/web/public/assets/layout-CXsUyEpG.js +2 -0
- package/dist/web/public/assets/layout-CXsUyEpG.js.map +1 -0
- package/dist/web/public/assets/layout-DFxtpNut.js +2 -0
- package/dist/web/public/assets/layout-DFxtpNut.js.map +1 -0
- package/dist/web/public/assets/layout-d8qxPKQk.js +2 -0
- package/dist/web/public/assets/layout-d8qxPKQk.js.map +1 -0
- package/dist/web/public/assets/loader-circle-JaKY-xMt.js +7 -0
- package/dist/web/public/assets/loader-circle-JaKY-xMt.js.map +1 -0
- package/dist/web/public/assets/map-pin-hFFSWZ3B.js +7 -0
- package/dist/web/public/assets/map-pin-hFFSWZ3B.js.map +1 -0
- package/dist/web/public/assets/memos-EhjMUvVZ.js +12 -0
- package/dist/web/public/assets/memos-EhjMUvVZ.js.map +1 -0
- package/dist/web/public/assets/messengers-BRV1IVGX.js +7 -0
- package/dist/web/public/assets/messengers-BRV1IVGX.js.map +1 -0
- package/dist/web/public/assets/network-DtCI2ZUU.js +7 -0
- package/dist/web/public/assets/network-DtCI2ZUU.js.map +1 -0
- package/dist/web/public/assets/outbox-CxUbMp6o.js +7 -0
- package/dist/web/public/assets/outbox-CxUbMp6o.js.map +1 -0
- package/dist/web/public/assets/pagination-CkZY8YNa.js +17 -0
- package/dist/web/public/assets/pagination-CkZY8YNa.js.map +1 -0
- package/dist/web/public/assets/persona-B6TFMSnI.js +2 -0
- package/dist/web/public/assets/persona-B6TFMSnI.js.map +1 -0
- package/dist/web/public/assets/play-BxRcWaH5.js +7 -0
- package/dist/web/public/assets/play-BxRcWaH5.js.map +1 -0
- package/dist/web/public/assets/policy-ndE1Y8zD.js +2 -0
- package/dist/web/public/assets/policy-ndE1Y8zD.js.map +1 -0
- package/dist/web/public/assets/react-C9F3QeMB.js +33 -0
- package/dist/web/public/assets/react-C9F3QeMB.js.map +1 -0
- package/dist/web/public/assets/refresh-ccw-Bx817_KW.js +7 -0
- package/dist/web/public/assets/refresh-ccw-Bx817_KW.js.map +1 -0
- package/dist/web/public/assets/reminders-XynkGQc5.js +17 -0
- package/dist/web/public/assets/reminders-XynkGQc5.js.map +1 -0
- package/dist/web/public/assets/save-CqMcATrh.js +7 -0
- package/dist/web/public/assets/save-CqMcATrh.js.map +1 -0
- package/dist/web/public/assets/schedules-VM02w_Om.js +7 -0
- package/dist/web/public/assets/schedules-VM02w_Om.js.map +1 -0
- package/dist/web/public/assets/search-Ba-e1t1P.js +7 -0
- package/dist/web/public/assets/search-Ba-e1t1P.js.map +1 -0
- package/dist/web/public/assets/service-C-wnwJ-b.js +7 -0
- package/dist/web/public/assets/service-C-wnwJ-b.js.map +1 -0
- package/dist/web/public/assets/status-badge-CsdJ6k8Q.js +2 -0
- package/dist/web/public/assets/status-badge-CsdJ6k8Q.js.map +1 -0
- package/dist/web/public/assets/subtasks-mGRKpF0G.js +7 -0
- package/dist/web/public/assets/subtasks-mGRKpF0G.js.map +1 -0
- package/dist/web/public/assets/table-vmLMgj6_.js +2 -0
- package/dist/web/public/assets/table-vmLMgj6_.js.map +1 -0
- package/dist/web/public/assets/topn-nu66Fotx.js +7 -0
- package/dist/web/public/assets/topn-nu66Fotx.js.map +1 -0
- package/dist/web/public/assets/trash-2-ZIitN_U3.js +7 -0
- package/dist/web/public/assets/trash-2-ZIitN_U3.js.map +1 -0
- package/dist/web/public/assets/use-event-stream-BGeFcayX.js +2 -0
- package/dist/web/public/assets/use-event-stream-BGeFcayX.js.map +1 -0
- package/dist/web/public/assets/use-memory-DgEqHEca.js +2 -0
- package/dist/web/public/assets/use-memory-DgEqHEca.js.map +1 -0
- package/dist/web/public/assets/use-observability-CQev_A8e.js +2 -0
- package/dist/web/public/assets/use-observability-CQev_A8e.js.map +1 -0
- package/dist/web/public/assets/use-settings-CU-UcrVD.js +2 -0
- package/dist/web/public/assets/use-settings-CU-UcrVD.js.map +1 -0
- package/dist/web/public/assets/use-skills-Dr77CXLA.js +2 -0
- package/dist/web/public/assets/use-skills-Dr77CXLA.js.map +1 -0
- package/dist/web/public/assets/use-workspace-PNv9Z4de.js +2 -0
- package/dist/web/public/assets/use-workspace-PNv9Z4de.js.map +1 -0
- package/dist/web/public/assets/useQuery-BTyugXYV.js +2 -0
- package/dist/web/public/assets/useQuery-BTyugXYV.js.map +1 -0
- package/dist/web/public/assets/vector-w-Ea3pg6.js +2 -0
- package/dist/web/public/assets/vector-w-Ea3pg6.js.map +1 -0
- package/dist/web/public/assets/viewer-DKA7QP9U.js +12 -0
- package/dist/web/public/assets/viewer-DKA7QP9U.js.map +1 -0
- package/dist/web/public/assets/workspace-DVLZca7t.js +17 -0
- package/dist/web/public/assets/workspace-DVLZca7t.js.map +1 -0
- package/dist/web/public/assets/workspaces-DYZsMmY-.js +7 -0
- package/dist/web/public/assets/workspaces-DYZsMmY-.js.map +1 -0
- package/dist/web/public/assets/x-Ru3rHT82.js +7 -0
- package/dist/web/public/assets/x-Ru3rHT82.js.map +1 -0
- package/dist/web/public/favicon.svg +4 -0
- package/dist/web/public/index.html +37 -928
- package/dist/web/public/manifest.webmanifest +19 -0
- package/dist/web/public/tasks.html +362 -6
- package/dist/web/public/vendor/chart.umd.min.js +20 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +694 -60
- package/dist/web/server.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Agim",
|
|
3
|
+
"short_name": "Agim",
|
|
4
|
+
"description": "agim — multi-agent gateway console",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"scope": "/",
|
|
7
|
+
"display": "standalone",
|
|
8
|
+
"orientation": "any",
|
|
9
|
+
"theme_color": "#4f46e5",
|
|
10
|
+
"background_color": "#f7f8fa",
|
|
11
|
+
"icons": [
|
|
12
|
+
{
|
|
13
|
+
"src": "/favicon.svg",
|
|
14
|
+
"sizes": "any",
|
|
15
|
+
"type": "image/svg+xml",
|
|
16
|
+
"purpose": "any maskable"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -233,6 +233,22 @@
|
|
|
233
233
|
// ── v1.2 Cost & Health tab ──
|
|
234
234
|
tabsCost: '💰 Cost & Health',
|
|
235
235
|
tabsMemory: '🧠 Memory',
|
|
236
|
+
tabsSkills: '🛠 Skills',
|
|
237
|
+
skillsH2: '🛠 Installed Skills',
|
|
238
|
+
skillsIntro: 'Read-only inventory of skills installed for claude-code / opencode on this host. Click a row for the full description. To install a new skill, send <code>/find-skills</code> in IM, or browse the skillhub.cn hot list below.',
|
|
239
|
+
skillsSearchPh: 'Search (name / description)',
|
|
240
|
+
skillsCatAll: 'All categories',
|
|
241
|
+
skillsRemoteH2: 'Hot on skillhub.cn',
|
|
242
|
+
skillsRemoteOpen: 'Open skillhub.cn ↗',
|
|
243
|
+
skillsRemoteIntro: 'Live top-50 hot downloads from skillhub.cn (5-min cache). Click any entry for the description + install instructions. agim never auto-runs installs — you copy the command and paste into shell or IM.',
|
|
244
|
+
skillsRemoteCached: 'cache hit',
|
|
245
|
+
skillsRemoteStale: 'upstream down, serving cached',
|
|
246
|
+
skillsCol: 'slug',
|
|
247
|
+
skillsName: 'name / description',
|
|
248
|
+
skillsStars: '⭐ stars',
|
|
249
|
+
skillsInstalls: '⬇ installs',
|
|
250
|
+
skillsHowInstall: 'Install (pick one; agim never auto-runs)',
|
|
251
|
+
skillsHostHint: 'The first two require skillhub CLI on the host. See docs/skills.md or skillhub.cn.',
|
|
236
252
|
costH2: '💰 Cost & Health',
|
|
237
253
|
costWindow: 'Window',
|
|
238
254
|
costDays1: '1 day',
|
|
@@ -259,6 +275,7 @@
|
|
|
259
275
|
memoryPersonaPlaceholder: '(No persona for this user yet — will be auto-generated at next consolidation, or write one manually.)',
|
|
260
276
|
memoryPersonaSave: 'Save persona',
|
|
261
277
|
memoryPersonaDelete: 'Delete persona',
|
|
278
|
+
memoryPersonaConsolidate: '🔁 Rebuild persona now',
|
|
262
279
|
memoryFactsCard: 'Facts',
|
|
263
280
|
memoryFactsSearch: 'Search (FTS5)',
|
|
264
281
|
memoryFactsCatAll: 'All types',
|
|
@@ -506,6 +523,22 @@
|
|
|
506
523
|
// ── v1.2 Cost & Health tab ──
|
|
507
524
|
tabsCost: '💰 成本与健康',
|
|
508
525
|
tabsMemory: '🧠 记忆',
|
|
526
|
+
tabsSkills: '🛠 技能',
|
|
527
|
+
skillsH2: '🛠 已安装的技能',
|
|
528
|
+
skillsIntro: '本机已安装的 claude / opencode skills 索引。点击行查看完整描述。要安装新 skill 请在 IM 里发 <code>/find-skills</code>,或浏览下方 skillhub.cn 热门榜。',
|
|
529
|
+
skillsSearchPh: '搜索(名称 / 描述)',
|
|
530
|
+
skillsCatAll: '所有类别',
|
|
531
|
+
skillsRemoteH2: 'skillhub.cn 热门',
|
|
532
|
+
skillsRemoteOpen: '前往 skillhub.cn ↗',
|
|
533
|
+
skillsRemoteIntro: '实时拉取 skillhub.cn top 50 热门下载(5 分钟缓存)。点击任意条目查看描述 + 安装命令。agim 不会自动跑安装——命令复制后由你贴去 shell 或 IM 触发。',
|
|
534
|
+
skillsRemoteCached: '缓存命中',
|
|
535
|
+
skillsRemoteStale: '上游不可达,用旧数据',
|
|
536
|
+
skillsCol: 'slug',
|
|
537
|
+
skillsName: '名称 / 描述',
|
|
538
|
+
skillsStars: '⭐ stars',
|
|
539
|
+
skillsInstalls: '⬇ installs',
|
|
540
|
+
skillsHowInstall: '安装方式(任选一个;agim 不会自动跑)',
|
|
541
|
+
skillsHostHint: '前两条要先在 host 上跑 skillhub install.sh。详见 docs/skills.md 或 skillhub.cn。',
|
|
509
542
|
costH2: '💰 成本与健康',
|
|
510
543
|
costWindow: '窗口',
|
|
511
544
|
costDays1: '1 天',
|
|
@@ -532,6 +565,7 @@
|
|
|
532
565
|
memoryPersonaPlaceholder: '(此用户暂无记忆画像 — 等下次 consolidation 自动生成,或手动写入)',
|
|
533
566
|
memoryPersonaSave: '保存记忆画像',
|
|
534
567
|
memoryPersonaDelete: '删除记忆画像',
|
|
568
|
+
memoryPersonaConsolidate: '🔁 立刻重建画像',
|
|
535
569
|
memoryFactsCard: 'Facts',
|
|
536
570
|
memoryFactsSearch: '搜索(FTS5)',
|
|
537
571
|
memoryFactsCatAll: '所有类型',
|
|
@@ -910,6 +944,7 @@
|
|
|
910
944
|
<button type="button" class="tab" data-tab="a2a" id="tab-a2a">A2A</button>
|
|
911
945
|
<button type="button" class="tab" data-tab="cost" id="tab-cost">💰 Cost & Health</button>
|
|
912
946
|
<button type="button" class="tab" data-tab="memory" id="tab-memory">🧠 Memory</button>
|
|
947
|
+
<button type="button" class="tab" data-tab="skills" id="tab-skills">🛠 Skills</button>
|
|
913
948
|
</div>
|
|
914
949
|
|
|
915
950
|
<section id="jobs-pane">
|
|
@@ -1244,9 +1279,10 @@
|
|
|
1244
1279
|
<span class="muted" id="mem-persona-meta" style="font-size:12px"></span>
|
|
1245
1280
|
</div>
|
|
1246
1281
|
<textarea id="mem-persona-text" rows="6" style="width:100%;font-family:ui-monospace,monospace;font-size:13px;box-sizing:border-box" data-i18n-attr="placeholder:memoryPersonaPlaceholder" placeholder="(此用户暂无记忆画像 — 等下次 consolidation 自动生成,或手动写入)"></textarea>
|
|
1247
|
-
<div class="actions" style="margin-top:8px;display:flex;gap:8px">
|
|
1282
|
+
<div class="actions" style="margin-top:8px;display:flex;gap:8px;flex-wrap:wrap">
|
|
1248
1283
|
<button type="button" id="btn-mem-persona-save" class="btn btn-primary" data-i18n="memoryPersonaSave">保存记忆画像</button>
|
|
1249
1284
|
<button type="button" id="btn-mem-persona-delete" class="btn" data-i18n="memoryPersonaDelete">删除记忆画像</button>
|
|
1285
|
+
<button type="button" id="btn-mem-persona-consolidate" class="btn" title="立刻跑一次 consolidation 重建画像,不等 24h 周期" data-i18n="memoryPersonaConsolidate">🔁 立刻重建画像</button>
|
|
1250
1286
|
</div>
|
|
1251
1287
|
<p class="muted" id="mem-persona-status" style="margin-top:6px;font-size:12px"></p>
|
|
1252
1288
|
</div>
|
|
@@ -1283,6 +1319,43 @@
|
|
|
1283
1319
|
<p class="muted" id="mem-facts-status" style="margin-top:6px;font-size:12px"></p>
|
|
1284
1320
|
</div>
|
|
1285
1321
|
</section>
|
|
1322
|
+
|
|
1323
|
+
<!-- v1.2.3 — Skills browser: read-only inventory of installed claude /
|
|
1324
|
+
opencode skills + a remote "hot on skillhub.cn" panel. -->
|
|
1325
|
+
<section id="skills-pane" hidden>
|
|
1326
|
+
<h2 style="margin-top:0" data-i18n="skillsH2">🛠 已安装的 Skills</h2>
|
|
1327
|
+
<p class="muted" style="margin-top:-4px;margin-bottom:12px;font-size:13px" data-i18n="skillsIntro">
|
|
1328
|
+
本机已安装的 claude / opencode skills 索引。点击行查看完整描述。
|
|
1329
|
+
要安装新 skill 请在 IM 里发 <code>/find-skills</code>,或浏览下方 skillhub.cn 热门榜。
|
|
1330
|
+
</p>
|
|
1331
|
+
<div class="toolbar" style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:12px">
|
|
1332
|
+
<input type="text" id="skills-search" placeholder="搜索(名称 / 描述)" data-i18n-attr="placeholder:skillsSearchPh" style="width:240px;font-size:13px" />
|
|
1333
|
+
<select id="skills-cat-filter" style="font-size:13px;min-width:160px">
|
|
1334
|
+
<option value="" data-i18n="skillsCatAll">所有类别</option>
|
|
1335
|
+
</select>
|
|
1336
|
+
<button type="button" id="btn-skills-refresh" data-i18n="refresh">刷新</button>
|
|
1337
|
+
<span id="skills-stats" class="muted" style="font-size:12px;margin-left:8px"></span>
|
|
1338
|
+
</div>
|
|
1339
|
+
<div id="skills-list">…</div>
|
|
1340
|
+
|
|
1341
|
+
<!-- Remote: skillhub.cn hot showcase -->
|
|
1342
|
+
<div style="margin-top:32px;border-top:1px solid var(--border,#d0d7de);padding-top:16px">
|
|
1343
|
+
<div style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:6px">
|
|
1344
|
+
<h2 style="margin:0">🔥 <span data-i18n="skillsRemoteH2">skillhub.cn 热门</span>
|
|
1345
|
+
<span id="skills-remote-meta" class="muted" style="font-weight:400;font-size:12px"></span>
|
|
1346
|
+
</h2>
|
|
1347
|
+
<div>
|
|
1348
|
+
<a href="https://skillhub.cn" target="_blank" rel="noopener" class="btn" data-i18n="skillsRemoteOpen">前往 skillhub.cn ↗</a>
|
|
1349
|
+
<button type="button" id="btn-skills-remote-refresh" class="btn" data-i18n="refresh" style="margin-left:6px">刷新</button>
|
|
1350
|
+
</div>
|
|
1351
|
+
</div>
|
|
1352
|
+
<p class="muted" style="font-size:12px;margin:0 0 12px" data-i18n="skillsRemoteIntro">
|
|
1353
|
+
实时拉取 skillhub.cn top 50 热门下载(5 分钟缓存)。点击任意条目查看描述。
|
|
1354
|
+
安装提示见弹窗末端 — 用 skillhub CLI 或在 IM 里告诉 agent 通过 skillhub 安装。
|
|
1355
|
+
</p>
|
|
1356
|
+
<div id="skills-remote-list"><p class="muted">…</p></div>
|
|
1357
|
+
</div>
|
|
1358
|
+
</section>
|
|
1286
1359
|
</main>
|
|
1287
1360
|
|
|
1288
1361
|
<div class="modal-bg" id="modal-bg">
|
|
@@ -1316,6 +1389,7 @@
|
|
|
1316
1389
|
document.getElementById('tab-a2a').textContent = T.tabsA2A;
|
|
1317
1390
|
if (T.tabsCost) document.getElementById('tab-cost').textContent = T.tabsCost;
|
|
1318
1391
|
if (T.tabsMemory) document.getElementById('tab-memory').textContent = T.tabsMemory;
|
|
1392
|
+
if (T.tabsSkills) document.getElementById('tab-skills').textContent = T.tabsSkills;
|
|
1319
1393
|
document.getElementById('hdr-outbox').textContent = T.tabsOutbox;
|
|
1320
1394
|
document.getElementById('hdr-a2a').textContent = T.tabsA2A;
|
|
1321
1395
|
|
|
@@ -1417,6 +1491,7 @@
|
|
|
1417
1491
|
document.getElementById('a2a-pane').hidden = tab !== 'a2a';
|
|
1418
1492
|
document.getElementById('cost-pane').hidden = tab !== 'cost';
|
|
1419
1493
|
document.getElementById('memory-pane').hidden = tab !== 'memory';
|
|
1494
|
+
document.getElementById('skills-pane').hidden = tab !== 'skills';
|
|
1420
1495
|
// Lazy-load on first activation; auto-refresh hooks below kick in too.
|
|
1421
1496
|
if (tab === 'schedules') loadSchedules();
|
|
1422
1497
|
if (tab === 'background') { ensureBgRootsLoaded().then(loadBgjobs); }
|
|
@@ -1436,6 +1511,7 @@
|
|
|
1436
1511
|
checkMemoryEnabledThen(() => { loadMemVecConfig(); loadMemVecStatus(); });
|
|
1437
1512
|
}
|
|
1438
1513
|
if (tab !== 'memory') stopMemVecPolling();
|
|
1514
|
+
if (tab === 'skills') loadSkills();
|
|
1439
1515
|
// Pause/resume auto-refresh so hidden tabs don't poll.
|
|
1440
1516
|
setupBgAutoRefresh();
|
|
1441
1517
|
setupApprovalsAutoRefresh();
|
|
@@ -2744,14 +2820,19 @@
|
|
|
2744
2820
|
function ensureChartJs() {
|
|
2745
2821
|
if (window.Chart) return Promise.resolve();
|
|
2746
2822
|
if (costChartJsLoaded) return costChartJsLoaded;
|
|
2747
|
-
|
|
2823
|
+
// v1.2.3 — try the vendored copy first (no CDN dependency, works in
|
|
2824
|
+
// CN where jsdelivr is often blocked). Fall back to jsdelivr if the
|
|
2825
|
+
// vendor file is missing (e.g. dev hot-reload before build copied it).
|
|
2826
|
+
const tryLoad = (src) => new Promise((resolve, reject) => {
|
|
2748
2827
|
const s = document.createElement('script');
|
|
2749
|
-
s.src =
|
|
2828
|
+
s.src = src;
|
|
2750
2829
|
s.crossOrigin = 'anonymous';
|
|
2751
2830
|
s.onload = () => resolve();
|
|
2752
|
-
s.onerror = () => reject(new Error('
|
|
2831
|
+
s.onerror = () => reject(new Error('failed: ' + src));
|
|
2753
2832
|
document.head.appendChild(s);
|
|
2754
2833
|
});
|
|
2834
|
+
costChartJsLoaded = tryLoad('/vendor/chart.umd.min.js')
|
|
2835
|
+
.catch(() => tryLoad('https://cdn.jsdelivr.net/npm/chart.js@4.4.4/dist/chart.umd.min.js'));
|
|
2755
2836
|
return costChartJsLoaded;
|
|
2756
2837
|
}
|
|
2757
2838
|
|
|
@@ -2769,8 +2850,22 @@
|
|
|
2769
2850
|
const data = await api('/api/health/summary?days=' + days);
|
|
2770
2851
|
renderCostKpis(data);
|
|
2771
2852
|
renderCostRange(data);
|
|
2772
|
-
|
|
2773
|
-
|
|
2853
|
+
let chartLoadErr = null;
|
|
2854
|
+
await ensureChartJs().catch((e) => { chartLoadErr = e; });
|
|
2855
|
+
if (chartLoadErr) {
|
|
2856
|
+
// Visible hint when both the vendored copy AND CDN fail
|
|
2857
|
+
// (cached browser, blocked CDN, etc). Without this, the chart
|
|
2858
|
+
// area is empty + silent.
|
|
2859
|
+
const c = document.getElementById('cost-trend');
|
|
2860
|
+
if (c && c.parentElement) {
|
|
2861
|
+
c.parentElement.innerHTML = `<p style="color:#d00;font-size:12px">
|
|
2862
|
+
chart.js 加载失败:${escapeHtmlSafe(String(chartLoadErr.message || chartLoadErr))}
|
|
2863
|
+
(检查 /vendor/chart.umd.min.js 是否存在,或浏览器是否屏蔽了 jsdelivr)
|
|
2864
|
+
</p>`;
|
|
2865
|
+
}
|
|
2866
|
+
} else {
|
|
2867
|
+
renderCostTrend(data);
|
|
2868
|
+
}
|
|
2774
2869
|
} catch (err) {
|
|
2775
2870
|
document.getElementById('cost-kpi').innerHTML = '<div class="error">加载失败: ' + (err.message || err) + '</div>';
|
|
2776
2871
|
}
|
|
@@ -3329,6 +3424,57 @@
|
|
|
3329
3424
|
if (status) status.textContent = '删除失败: ' + (err.message || err);
|
|
3330
3425
|
}
|
|
3331
3426
|
});
|
|
3427
|
+
document.getElementById('btn-mem-persona-consolidate')?.addEventListener('click', async () => {
|
|
3428
|
+
const status = document.getElementById('mem-persona-status');
|
|
3429
|
+
const btn = document.getElementById('btn-mem-persona-consolidate');
|
|
3430
|
+
if (btn) btn.disabled = true;
|
|
3431
|
+
if (status) status.textContent = '⏳ 已提交后台任务(每用户 1 次 LLM 调用,10–60 秒)…';
|
|
3432
|
+
let pollTimer = null;
|
|
3433
|
+
const stopPoll = () => { if (pollTimer) { clearInterval(pollTimer); pollTimer = null; } };
|
|
3434
|
+
try {
|
|
3435
|
+
// Async kickoff: server returns 202 + jobId immediately, then the
|
|
3436
|
+
// LLM call runs in the background. Polling avoids cloudflare /
|
|
3437
|
+
// nginx / browser fetch timeouts on long-held POSTs.
|
|
3438
|
+
const r = await api('/api/memory/consolidate', { method: 'POST' });
|
|
3439
|
+
const targetJobId = r.jobId;
|
|
3440
|
+
if (!targetJobId) throw new Error('server did not return jobId');
|
|
3441
|
+
const startedAt = Date.now();
|
|
3442
|
+
pollTimer = setInterval(async () => {
|
|
3443
|
+
try {
|
|
3444
|
+
const s = await api('/api/memory/consolidate/status');
|
|
3445
|
+
const job = (s.jobs || []).find(j => j.id === targetJobId);
|
|
3446
|
+
if (!job) return;
|
|
3447
|
+
const elapsed = ((Date.now() - startedAt) / 1000).toFixed(0);
|
|
3448
|
+
if (job.phase === 'running') {
|
|
3449
|
+
if (status) status.textContent = `⏳ 重建中 (${elapsed}s)…`;
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
stopPoll();
|
|
3453
|
+
if (btn) btn.disabled = false;
|
|
3454
|
+
if (job.phase === 'done') {
|
|
3455
|
+
const u = (job.result && job.result.updated) || 0;
|
|
3456
|
+
const n = (job.result && job.result.users) || 0;
|
|
3457
|
+
if (status) status.textContent = `✓ 完成 (${elapsed}s) · 处理 ${n} 个用户、更新 ${u} 个画像`;
|
|
3458
|
+
await loadMemoryUserDetail();
|
|
3459
|
+
} else {
|
|
3460
|
+
const e = (job.result && job.result.error) || 'unknown error';
|
|
3461
|
+
if (status) status.textContent = '失败: ' + e;
|
|
3462
|
+
}
|
|
3463
|
+
} catch (pollErr) {
|
|
3464
|
+
// Poll failures are non-fatal; UI keeps trying. Stop after 5 min.
|
|
3465
|
+
if (Date.now() - startedAt > 300_000) {
|
|
3466
|
+
stopPoll();
|
|
3467
|
+
if (btn) btn.disabled = false;
|
|
3468
|
+
if (status) status.textContent = '轮询超时(>5min),请刷新查看实际结果';
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
}, 2000);
|
|
3472
|
+
} catch (err) {
|
|
3473
|
+
if (status) status.textContent = '失败: ' + (err.message || err);
|
|
3474
|
+
if (btn) btn.disabled = false;
|
|
3475
|
+
stopPoll();
|
|
3476
|
+
}
|
|
3477
|
+
});
|
|
3332
3478
|
document.getElementById('btn-mem-facts-clear-lowconf')?.addEventListener('click', async () => {
|
|
3333
3479
|
if (!memCurrentUser) return;
|
|
3334
3480
|
if (!confirm('删除 confidence ≤ 40% 的所有事实?')) return;
|
|
@@ -3361,6 +3507,216 @@
|
|
|
3361
3507
|
}
|
|
3362
3508
|
});
|
|
3363
3509
|
|
|
3510
|
+
// ============================================
|
|
3511
|
+
// v1.2.3 — Skills tab
|
|
3512
|
+
// ============================================
|
|
3513
|
+
let skillsCache = null; // {total, byCategory, skills}
|
|
3514
|
+
async function loadSkills() {
|
|
3515
|
+
const listEl = document.getElementById('skills-list');
|
|
3516
|
+
if (listEl) listEl.innerHTML = '<p class="muted">' + (T.loading || 'Loading…') + '</p>';
|
|
3517
|
+
try {
|
|
3518
|
+
const data = await api('/api/skills');
|
|
3519
|
+
skillsCache = data;
|
|
3520
|
+
renderSkillsCatFilter(data);
|
|
3521
|
+
renderSkills();
|
|
3522
|
+
const stats = document.getElementById('skills-stats');
|
|
3523
|
+
if (stats) {
|
|
3524
|
+
const cats = Object.keys(data.byCategory || {}).length;
|
|
3525
|
+
stats.textContent = `· ${data.total} skills · ${cats} categories`;
|
|
3526
|
+
}
|
|
3527
|
+
} catch (err) {
|
|
3528
|
+
if (listEl) listEl.innerHTML = '<p style="color:#d00">' + escapeHtmlSafe(String(err.message || err)) + '</p>';
|
|
3529
|
+
}
|
|
3530
|
+
// Kick off remote hot panel in parallel — fire-and-forget; its own
|
|
3531
|
+
// error rendering is in loadSkillsRemote.
|
|
3532
|
+
loadSkillsRemote();
|
|
3533
|
+
}
|
|
3534
|
+
function renderSkillsCatFilter(data) {
|
|
3535
|
+
const sel = document.getElementById('skills-cat-filter');
|
|
3536
|
+
if (!sel) return;
|
|
3537
|
+
const prev = sel.value;
|
|
3538
|
+
// Keep first option (all), append discovered categories
|
|
3539
|
+
const allOpt = sel.options[0];
|
|
3540
|
+
sel.innerHTML = '';
|
|
3541
|
+
sel.appendChild(allOpt);
|
|
3542
|
+
const cats = Object.keys(data.byCategory || {}).sort();
|
|
3543
|
+
for (const c of cats) {
|
|
3544
|
+
const o = document.createElement('option');
|
|
3545
|
+
o.value = c;
|
|
3546
|
+
o.textContent = `${c} (${data.byCategory[c]})`;
|
|
3547
|
+
sel.appendChild(o);
|
|
3548
|
+
}
|
|
3549
|
+
sel.value = prev;
|
|
3550
|
+
}
|
|
3551
|
+
function renderSkills() {
|
|
3552
|
+
if (!skillsCache) return;
|
|
3553
|
+
const listEl = document.getElementById('skills-list');
|
|
3554
|
+
if (!listEl) return;
|
|
3555
|
+
const q = (document.getElementById('skills-search')?.value || '').toLowerCase().trim();
|
|
3556
|
+
const cat = document.getElementById('skills-cat-filter')?.value || '';
|
|
3557
|
+
const filtered = skillsCache.skills.filter(s => {
|
|
3558
|
+
if (cat && s.category !== cat) return false;
|
|
3559
|
+
if (!q) return true;
|
|
3560
|
+
return s.slug.toLowerCase().includes(q)
|
|
3561
|
+
|| (s.name || '').toLowerCase().includes(q)
|
|
3562
|
+
|| (s.description || '').toLowerCase().includes(q);
|
|
3563
|
+
});
|
|
3564
|
+
if (filtered.length === 0) {
|
|
3565
|
+
listEl.innerHTML = '<p class="muted">(no skills match)</p>';
|
|
3566
|
+
return;
|
|
3567
|
+
}
|
|
3568
|
+
// Group by category for clearer scan
|
|
3569
|
+
const byCat = {};
|
|
3570
|
+
for (const s of filtered) {
|
|
3571
|
+
if (!byCat[s.category]) byCat[s.category] = [];
|
|
3572
|
+
byCat[s.category].push(s);
|
|
3573
|
+
}
|
|
3574
|
+
const html = [];
|
|
3575
|
+
for (const cat of Object.keys(byCat).sort()) {
|
|
3576
|
+
html.push(`<h3 style="margin:18px 0 8px;font-size:14px;color:var(--muted,#656d76)">${escapeHtmlSafe(cat)} <span style="font-weight:400">(${byCat[cat].length})</span></h3>`);
|
|
3577
|
+
html.push('<table class="data-table"><tbody>');
|
|
3578
|
+
for (const s of byCat[cat]) {
|
|
3579
|
+
const agentBadge = s.agents.map(a => `<span style="display:inline-block;padding:1px 6px;margin-right:4px;border-radius:3px;background:${a==='claude'?'#fde68a':'#bfdbfe'};font-size:11px;color:#1f2328">${a}</span>`).join('');
|
|
3580
|
+
const descPreview = (s.description || '').slice(0, 140) + ((s.description || '').length > 140 ? '…' : '');
|
|
3581
|
+
html.push(`<tr style="cursor:pointer" data-skill="${escapeHtmlSafe(s.slug)}">
|
|
3582
|
+
<td style="width:240px;vertical-align:top"><code>${escapeHtmlSafe(s.slug)}</code></td>
|
|
3583
|
+
<td style="width:120px;vertical-align:top">${agentBadge}</td>
|
|
3584
|
+
<td style="vertical-align:top;color:var(--muted,#656d76)">${escapeHtmlSafe(descPreview)}</td>
|
|
3585
|
+
</tr>`);
|
|
3586
|
+
}
|
|
3587
|
+
html.push('</tbody></table>');
|
|
3588
|
+
}
|
|
3589
|
+
listEl.innerHTML = html.join('');
|
|
3590
|
+
listEl.querySelectorAll('[data-skill]').forEach(tr => {
|
|
3591
|
+
tr.addEventListener('click', () => openSkillModal(tr.getAttribute('data-skill')));
|
|
3592
|
+
});
|
|
3593
|
+
}
|
|
3594
|
+
async function openSkillModal(slug) {
|
|
3595
|
+
const m = document.getElementById('modal');
|
|
3596
|
+
const bg = document.getElementById('modal-bg');
|
|
3597
|
+
m.innerHTML = `<p class="muted">Loading ${escapeHtmlSafe(slug)}…</p>`;
|
|
3598
|
+
bg.classList.add('show');
|
|
3599
|
+
try {
|
|
3600
|
+
const data = await api('/api/skills/' + encodeURIComponent(slug));
|
|
3601
|
+
// Render the SKILL.md content. Strip the frontmatter block, then
|
|
3602
|
+
// run through marked() if available, otherwise plain <pre>.
|
|
3603
|
+
let body = data.content || '';
|
|
3604
|
+
if (body.startsWith('---')) {
|
|
3605
|
+
const end = body.indexOf('\n---', 3);
|
|
3606
|
+
if (end >= 0) body = body.slice(end + 4).trimStart();
|
|
3607
|
+
}
|
|
3608
|
+
const rendered = (window.marked && typeof marked.parse === 'function')
|
|
3609
|
+
? marked.parse(body)
|
|
3610
|
+
: `<pre style="white-space:pre-wrap;font-size:12px">${escapeHtmlSafe(body)}</pre>`;
|
|
3611
|
+
const agentBadge = data.agents.map(a => `<span style="display:inline-block;padding:1px 6px;margin-right:4px;border-radius:3px;background:${a==='claude'?'#fde68a':'#bfdbfe'};font-size:11px;color:#1f2328">${a}</span>`).join('');
|
|
3612
|
+
m.innerHTML = `
|
|
3613
|
+
<div style="display:flex;justify-content:space-between;align-items:start;margin-bottom:8px">
|
|
3614
|
+
<h2 style="margin:0"><code>${escapeHtmlSafe(data.slug)}</code></h2>
|
|
3615
|
+
<button type="button" class="btn" onclick="document.getElementById('modal-bg').classList.remove('show')">${T.close || 'Close'}</button>
|
|
3616
|
+
</div>
|
|
3617
|
+
<p class="muted" style="font-size:12px;margin:0 0 8px">
|
|
3618
|
+
${agentBadge} · <strong>${escapeHtmlSafe(data.category)}</strong>
|
|
3619
|
+
· <code style="font-size:11px">${escapeHtmlSafe(data.path)}</code>
|
|
3620
|
+
</p>
|
|
3621
|
+
<p style="margin:0 0 16px"><strong>${escapeHtmlSafe(data.name)}</strong> — ${escapeHtmlSafe(data.description)}</p>
|
|
3622
|
+
<div class="markdown-body" style="border-top:1px solid var(--border,#d0d7de);padding-top:12px;max-height:60vh;overflow-y:auto">${rendered}</div>
|
|
3623
|
+
`;
|
|
3624
|
+
} catch (err) {
|
|
3625
|
+
m.innerHTML = `<p style="color:#d00">${escapeHtmlSafe(String(err.message || err))}</p>
|
|
3626
|
+
<button type="button" class="btn" onclick="document.getElementById('modal-bg').classList.remove('show')">${T.close || 'Close'}</button>`;
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
document.getElementById('skills-search')?.addEventListener('input', renderSkills);
|
|
3630
|
+
document.getElementById('skills-cat-filter')?.addEventListener('change', renderSkills);
|
|
3631
|
+
document.getElementById('btn-skills-refresh')?.addEventListener('click', loadSkills);
|
|
3632
|
+
|
|
3633
|
+
// ─── skillhub.cn remote hot ───
|
|
3634
|
+
async function loadSkillsRemote() {
|
|
3635
|
+
const listEl = document.getElementById('skills-remote-list');
|
|
3636
|
+
const metaEl = document.getElementById('skills-remote-meta');
|
|
3637
|
+
if (listEl) listEl.innerHTML = '<p class="muted">' + (T.loading || 'Loading…') + '</p>';
|
|
3638
|
+
try {
|
|
3639
|
+
const data = await api('/api/skills/remote/hot');
|
|
3640
|
+
const skills = (data && data.skills) || [];
|
|
3641
|
+
if (metaEl) {
|
|
3642
|
+
const cacheStr = data.cached
|
|
3643
|
+
? ` · ${T.skillsRemoteCached || '缓存命中'}`
|
|
3644
|
+
: '';
|
|
3645
|
+
const staleStr = data.stale ? ` · ⚠️ ${T.skillsRemoteStale || '上游不可达,用旧数据'}` : '';
|
|
3646
|
+
metaEl.textContent = `(${skills.length}${cacheStr}${staleStr})`;
|
|
3647
|
+
}
|
|
3648
|
+
const html = ['<table class="data-table"><thead><tr>',
|
|
3649
|
+
`<th style="width:200px">${T.skillsCol || 'slug'}</th>`,
|
|
3650
|
+
`<th>${T.skillsName || '名称 / 描述'}</th>`,
|
|
3651
|
+
`<th style="width:100px;text-align:right">${T.skillsStars || '⭐ stars'}</th>`,
|
|
3652
|
+
`<th style="width:110px;text-align:right">${T.skillsInstalls || '⬇ installs'}</th>`,
|
|
3653
|
+
'</tr></thead><tbody>'];
|
|
3654
|
+
for (const s of skills) {
|
|
3655
|
+
const desc = (s.description || '').replace(/\n+/g, ' ').slice(0, 150);
|
|
3656
|
+
const more = (s.description || '').length > 150 ? '…' : '';
|
|
3657
|
+
html.push(`<tr style="cursor:pointer" data-remote-slug="${escapeHtmlSafe(s.slug)}">
|
|
3658
|
+
<td style="vertical-align:top"><code>${escapeHtmlSafe(s.slug)}</code><div class="muted" style="font-size:11px">${escapeHtmlSafe(s.category||'')}</div></td>
|
|
3659
|
+
<td style="vertical-align:top">
|
|
3660
|
+
<div><strong>${escapeHtmlSafe(s.name||s.slug)}</strong> <span class="muted" style="font-size:11px">v${escapeHtmlSafe(s.version||'?')}</span></div>
|
|
3661
|
+
<div class="muted" style="font-size:12px;margin-top:2px">${escapeHtmlSafe(desc)}${more}</div>
|
|
3662
|
+
</td>
|
|
3663
|
+
<td style="vertical-align:top;text-align:right">${(s.stars||0).toLocaleString()}</td>
|
|
3664
|
+
<td style="vertical-align:top;text-align:right">${(s.installs||0).toLocaleString()}</td>
|
|
3665
|
+
</tr>`);
|
|
3666
|
+
}
|
|
3667
|
+
html.push('</tbody></table>');
|
|
3668
|
+
if (skills.length === 0) listEl.innerHTML = '<p class="muted">(empty)</p>';
|
|
3669
|
+
else listEl.innerHTML = html.join('');
|
|
3670
|
+
listEl.querySelectorAll('[data-remote-slug]').forEach(tr => {
|
|
3671
|
+
tr.addEventListener('click', () => openRemoteSkillModal(tr.getAttribute('data-remote-slug'), skills));
|
|
3672
|
+
});
|
|
3673
|
+
} catch (err) {
|
|
3674
|
+
if (listEl) listEl.innerHTML = '<p style="color:#d00">' + escapeHtmlSafe(String(err.message || err)) + '</p>';
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
function openRemoteSkillModal(slug, skills) {
|
|
3678
|
+
const s = skills.find(x => x.slug === slug);
|
|
3679
|
+
if (!s) return;
|
|
3680
|
+
const m = document.getElementById('modal');
|
|
3681
|
+
const bg = document.getElementById('modal-bg');
|
|
3682
|
+
const desc = escapeHtmlSafe(s.description || '');
|
|
3683
|
+
// Three install paths the user can pick. We never auto-execute —
|
|
3684
|
+
// operator's choice to copy + paste into shell or forward to agent.
|
|
3685
|
+
const installCmds = [
|
|
3686
|
+
{ label: 'CLI (host)', cmd: 'skillhub install ' + s.slug },
|
|
3687
|
+
{ label: 'CLI dry-run', cmd: 'skillhub search ' + s.slug },
|
|
3688
|
+
{ label: 'IM 让 agent 安装', cmd: '请用 skillhub 安装 ' + s.slug + ' 技能' },
|
|
3689
|
+
];
|
|
3690
|
+
const cmdRows = installCmds.map(c =>
|
|
3691
|
+
`<div style="margin-top:6px"><span class="muted" style="font-size:11px;display:inline-block;width:130px">${escapeHtmlSafe(c.label)}</span>
|
|
3692
|
+
<code style="font-size:12px">${escapeHtmlSafe(c.cmd)}</code>
|
|
3693
|
+
<button type="button" class="btn" style="font-size:11px;padding:1px 6px;margin-left:6px" onclick="navigator.clipboard.writeText('${c.cmd.replace(/'/g,"\\'")}').then(()=>{this.textContent='✓'; setTimeout(()=>{this.textContent='复制';},1500);})">复制</button>
|
|
3694
|
+
</div>`).join('');
|
|
3695
|
+
m.innerHTML = `
|
|
3696
|
+
<div style="display:flex;justify-content:space-between;align-items:start;margin-bottom:8px">
|
|
3697
|
+
<h2 style="margin:0">${escapeHtmlSafe(s.name || s.slug)} <span class="muted" style="font-size:13px;font-weight:400">v${escapeHtmlSafe(s.version || '?')}</span></h2>
|
|
3698
|
+
<button type="button" class="btn" onclick="document.getElementById('modal-bg').classList.remove('show')">${T.close || 'Close'}</button>
|
|
3699
|
+
</div>
|
|
3700
|
+
<p class="muted" style="font-size:12px;margin:0 0 10px">
|
|
3701
|
+
<code>${escapeHtmlSafe(s.slug)}</code>
|
|
3702
|
+
${s.category ? ' · ' + escapeHtmlSafe(s.category) : ''}
|
|
3703
|
+
· ⭐ ${(s.stars||0).toLocaleString()}
|
|
3704
|
+
· ⬇ ${(s.installs||0).toLocaleString()} installs
|
|
3705
|
+
· ${(s.downloads||0).toLocaleString()} downloads
|
|
3706
|
+
</p>
|
|
3707
|
+
<div style="white-space:pre-wrap;border-top:1px solid var(--border,#d0d7de);padding-top:10px;font-size:13px">${desc}</div>
|
|
3708
|
+
<div style="margin-top:14px;padding-top:10px;border-top:1px solid var(--border,#d0d7de)">
|
|
3709
|
+
<strong style="font-size:13px">${T.skillsHowInstall || '安装方式(任选一个;agim 不会自动跑)'}</strong>
|
|
3710
|
+
${cmdRows}
|
|
3711
|
+
<p class="muted" style="font-size:11px;margin-top:8px">
|
|
3712
|
+
${T.skillsHostHint || '前两条要先在 host 上跑 skillhub install.sh。详见 docs/memory-and-vector.md 或 skillhub.cn。'}
|
|
3713
|
+
</p>
|
|
3714
|
+
</div>
|
|
3715
|
+
`;
|
|
3716
|
+
bg.classList.add('show');
|
|
3717
|
+
}
|
|
3718
|
+
document.getElementById('btn-skills-remote-refresh')?.addEventListener('click', loadSkillsRemote);
|
|
3719
|
+
|
|
3364
3720
|
// Initial load
|
|
3365
3721
|
loadJobs();
|
|
3366
3722
|
</script>
|