@weibaohui/experts-management 0.3.0 → 0.4.1

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/client/bundle.js CHANGED
@@ -19,7 +19,8 @@ window.__ModuleLoader__.load({
19
19
  * title / hint / rows: [[label, value], ...] / initialPrompt
20
20
  * run: async (prompt) => { jobId } — 发起执行
21
21
  * poll: async (jobId) => { status, output, code }
22
- * labels: { copy, copied, run, running, done, failed, outputLabel, close }
22
+ * labels: { copy, copied, run, running, done, failed, outputLabel, openSession, close }
23
+ * onOpenSession: (sessionId) => void — 可选;job 出现 sessionId 时渲染「打开会话」
23
24
  * onClose
24
25
  *
25
26
  * 全部样式内联(主题 token + 回退值),消费者无需自带 CSS。
@@ -80,6 +81,8 @@ window.__ModuleLoader__.load({
80
81
  setJob({ jobId: r.jobId, status: 'running', output: '', code: null })
81
82
  }).catch(function (e) { setError(String(e && e.message)) }).finally(function () { setBusy(false) })
82
83
  }
84
+ var canOpenSession = typeof props.onOpenSession === 'function' && job !== null && job.sessionId
85
+ var openSession = function () { props.onOpenSession(job.sessionId) }
83
86
  var copy = function () {
84
87
  if (typeof navigator !== 'undefined' && navigator.clipboard && navigator.clipboard.writeText) {
85
88
  navigator.clipboard.writeText(prompt).then(function () { setCopied(true); setTimeout(function () { setCopied(false) }, 1500) }).catch(function () {})
@@ -103,6 +106,7 @@ window.__ModuleLoader__.load({
103
106
  h('div', { style: { fontSize: 12, opacity: .7, margin: '4px 0' } }, (labels.outputLabel || 'Output') + ' · ' + statusText),
104
107
  h('pre', { style: { maxHeight: 220, margin: 0, overflow: 'auto', whiteSpace: 'pre-wrap', fontSize: 12, background: 'var(--dsw-alias-bg-layer-2,transparent)', border: '1px solid var(--dsw-alias-border-l2,rgba(128,128,128,.2))', borderRadius: '8px', padding: '8px' } }, job.output || '…')) : null,
105
108
  h('div', { style: { display: 'flex', gap: 8 } },
109
+ canOpenSession ? h('button', { onClick: openSession, style: btnStyle }, labels.openSession || 'Open chat') : null,
106
110
  h('button', { onClick: copy, style: btnStyle }, copied ? (labels.copied || 'Copied') : (labels.copy || 'Copy')),
107
111
  h('button', { onClick: doRun, disabled: busy || (job !== null && job.status === 'running'), style: primaryStyle }, job !== null && job.status === 'running' ? (labels.running || 'Running…') : (labels.run || 'Run')))))
108
112
  }
@@ -196,6 +200,7 @@ window.__ModuleLoader__.load({
196
200
  installing: '安装中…',
197
201
  installedDone: '已安装到用户库',
198
202
  shareBtn: '分享',
203
+ openChat: '打开对话',
199
204
  shareTitle: '分享专家到官方仓库',
200
205
  shareHint: 'AI 将读取本机令牌,fork 官方仓库 → 建分支 → 提交该专家目录 → 创建 PR。确认或修改提示词后,复制到当前会话发送执行。',
201
206
  shareParamName: '专家名',
@@ -293,6 +298,7 @@ window.__ModuleLoader__.load({
293
298
  installing: 'Installing…',
294
299
  installedDone: 'Installed to the user library',
295
300
  shareBtn: 'Share',
301
+ openChat: 'Open chat',
296
302
  shareTitle: 'Share expert to the official repo',
297
303
  shareHint: 'AI will read the local token, fork the official repo → create a branch → commit the expert directory → open a PR. Review or edit the prompt, then copy it into the conversation to run.',
298
304
  shareParamName: 'Expert',
@@ -791,6 +797,8 @@ window.__ModuleLoader__.load({
791
797
  return shareDialogComponent
792
798
  }
793
799
 
800
+ let sessionsApi = null // 「打开对话」用的宿主 sessions 服务(apply 时动态注入捕获)
801
+
794
802
  /** 专家分享提示词:提交到 ntd-resource 的 experts/ 子树(与技能分享同管线、同 token)。 */
795
803
  const EXPERT_SHARE_PROMPT = [
796
804
  '请把本地专家「{{expertName}}」{{version}}打包提交到 GitCode 官方仓库 weibaohui/ntd-resource 的 experts/ 子树,作为一个 PR 供维护者审核。',
@@ -817,7 +825,7 @@ window.__ModuleLoader__.load({
817
825
  '- 全程与最终汇报都使用中文。',
818
826
  ].join('\n')
819
827
 
820
- function DetailModal({ name, source, t, onClose, onInstalled, onDeleted, onToast }) {
828
+ function DetailModal({ name, source, t, onClose, onInstalled, onDeleted, onToast, onOpenSession }) {
821
829
  const [detail, setDetail] = useState(null)
822
830
  const [error, setError] = useState('')
823
831
  const [busy, setBusy] = useState(false)
@@ -1032,9 +1040,10 @@ window.__ModuleLoader__.load({
1032
1040
  title: t('shareTitle'), hint: t('shareHint'),
1033
1041
  rows: [[t('shareParamName'), detail.name], [t('shareParamVersion'), detail.version || '1.0.0'], [t('shareParamDir'), detail.dir]],
1034
1042
  initialPrompt: PluginKit.substituteParams(EXPERT_SHARE_PROMPT, { expertName: detail.name, version: detail.version || '1.0.0', resourceDir: detail.dir, settingsFile: shareSettingsFile }),
1035
- labels: { copy: t('copyPrompt'), copied: t('copied'), run: t('runBtn'), running: t('running'), done: t('runDone'), failed: t('runFailed'), outputLabel: t('outputLabel') },
1043
+ labels: { copy: t('copyPrompt'), copied: t('copied'), run: t('runBtn'), running: t('running'), done: t('runDone'), failed: t('runFailed'), outputLabel: t('outputLabel'), openSession: t('openChat') },
1036
1044
  run: (prompt) => fetchJson(`${API}/share/run`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, dir: detail.dir }) }),
1037
1045
  poll: (id) => fetchJson(`${API}/share/run?id=${encodeURIComponent(id)}`),
1046
+ onOpenSession: (sessionId) => { setShareOpen(false); if (onOpenSession) onOpenSession(sessionId) },
1038
1047
  onClose: () => setShareOpen(false),
1039
1048
  }) : null))
1040
1049
  }
@@ -1188,6 +1197,7 @@ window.__ModuleLoader__.load({
1188
1197
  : null,
1189
1198
  selected !== null ? h(DetailModal, {
1190
1199
  name: selected.name, source: selected.source, t, onClose: () => setSelected(null),
1200
+ onOpenSession: (sessionId) => { try { sessionsApi.open(sessionId) } catch (e) { showToast(String(e && e.message)) } },
1191
1201
  onInstalled: () => { setSelected(null); showToast(t('installedDone')); reload() },
1192
1202
  onDeleted: () => { setSelected(null); showToast(t('removedDone')); reload() },
1193
1203
  }) : null,
package/client/index.js CHANGED
@@ -83,6 +83,7 @@ const ZH = {
83
83
  installing: '安装中…',
84
84
  installedDone: '已安装到用户库',
85
85
  shareBtn: '分享',
86
+ openChat: '打开对话',
86
87
  shareTitle: '分享专家到官方仓库',
87
88
  shareHint: 'AI 将读取本机令牌,fork 官方仓库 → 建分支 → 提交该专家目录 → 创建 PR。确认或修改提示词后,复制到当前会话发送执行。',
88
89
  shareParamName: '专家名',
@@ -180,6 +181,7 @@ const EN = {
180
181
  installing: 'Installing…',
181
182
  installedDone: 'Installed to the user library',
182
183
  shareBtn: 'Share',
184
+ openChat: 'Open chat',
183
185
  shareTitle: 'Share expert to the official repo',
184
186
  shareHint: 'AI will read the local token, fork the official repo → create a branch → commit the expert directory → open a PR. Review or edit the prompt, then copy it into the conversation to run.',
185
187
  shareParamName: 'Expert',
@@ -678,6 +680,8 @@ function getShareDialogComponent() {
678
680
  return shareDialogComponent
679
681
  }
680
682
 
683
+ let sessionsApi = null // 「打开对话」用的宿主 sessions 服务(apply 时动态注入捕获)
684
+
681
685
  /** 专家分享提示词:提交到 ntd-resource 的 experts/ 子树(与技能分享同管线、同 token)。 */
682
686
  const EXPERT_SHARE_PROMPT = [
683
687
  '请把本地专家「{{expertName}}」{{version}}打包提交到 GitCode 官方仓库 weibaohui/ntd-resource 的 experts/ 子树,作为一个 PR 供维护者审核。',
@@ -704,7 +708,7 @@ const EXPERT_SHARE_PROMPT = [
704
708
  '- 全程与最终汇报都使用中文。',
705
709
  ].join('\n')
706
710
 
707
- function DetailModal({ name, source, t, onClose, onInstalled, onDeleted, onToast }) {
711
+ function DetailModal({ name, source, t, onClose, onInstalled, onDeleted, onToast, onOpenSession }) {
708
712
  const [detail, setDetail] = useState(null)
709
713
  const [error, setError] = useState('')
710
714
  const [busy, setBusy] = useState(false)
@@ -919,9 +923,10 @@ function DetailModal({ name, source, t, onClose, onInstalled, onDeleted, onToast
919
923
  title: t('shareTitle'), hint: t('shareHint'),
920
924
  rows: [[t('shareParamName'), detail.name], [t('shareParamVersion'), detail.version || '1.0.0'], [t('shareParamDir'), detail.dir]],
921
925
  initialPrompt: PluginKit.substituteParams(EXPERT_SHARE_PROMPT, { expertName: detail.name, version: detail.version || '1.0.0', resourceDir: detail.dir, settingsFile: shareSettingsFile }),
922
- labels: { copy: t('copyPrompt'), copied: t('copied'), run: t('runBtn'), running: t('running'), done: t('runDone'), failed: t('runFailed'), outputLabel: t('outputLabel') },
926
+ labels: { copy: t('copyPrompt'), copied: t('copied'), run: t('runBtn'), running: t('running'), done: t('runDone'), failed: t('runFailed'), outputLabel: t('outputLabel'), openSession: t('openChat') },
923
927
  run: (prompt) => fetchJson(`${API}/share/run`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, dir: detail.dir }) }),
924
928
  poll: (id) => fetchJson(`${API}/share/run?id=${encodeURIComponent(id)}`),
929
+ onOpenSession: (sessionId) => { setShareOpen(false); if (onOpenSession) onOpenSession(sessionId) },
925
930
  onClose: () => setShareOpen(false),
926
931
  }) : null))
927
932
  }
@@ -1075,6 +1080,7 @@ function ExpertsPage({ t, embedded, onClose }) {
1075
1080
  : null,
1076
1081
  selected !== null ? h(DetailModal, {
1077
1082
  name: selected.name, source: selected.source, t, onClose: () => setSelected(null),
1083
+ onOpenSession: (sessionId) => { try { sessionsApi.open(sessionId) } catch (e) { showToast(String(e && e.message)) } },
1078
1084
  onInstalled: () => { setSelected(null); showToast(t('installedDone')); reload() },
1079
1085
  onDeleted: () => { setSelected(null); showToast(t('removedDone')); reload() },
1080
1086
  }) : null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weibaohui/experts-management",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "dsh 插件 · 专家管理:管理 ntd 格式的专家与专家团队(plugin.json + Agent MD + 技能集),浏览/安装 50+ 内置专家市场;每个专家注册为「仅用户可调用」的技能,在对话输入框用 /expert-名称 即可以该专家的身份执行任务(宿主确定性注入角色定义与技能清单,不占用模型目录 token)。",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -44,7 +44,7 @@
44
44
  "node": ">=22.5"
45
45
  },
46
46
  "dependencies": {
47
- "@weibaohui/dsh-plugin-kit": "^0.1.0",
47
+ "@weibaohui/dsh-plugin-kit": "^0.2.0",
48
48
  "yaml": "^2.9.0"
49
49
  },
50
50
  "devDependencies": {
package/src/index.js CHANGED
@@ -557,7 +557,7 @@ const MD_MAX_CHARS = 512 * 1024
557
557
 
558
558
  module.exports = {
559
559
  name: 'experts-management',
560
- inject: ['skills', 'webServer', 'settings'],
560
+ inject: ['skills', 'webServer', 'settings', 'agents', 'agentDefaultModel', 'sessions'],
561
561
  __internals: {
562
562
  extractFrontmatter, parseFrontmatter, parseAgentMd, parseSkillMd, parsePluginJson,
563
563
  localized, truncateDescription, resolveWithin, isSafeExpertName,
@@ -811,14 +811,10 @@ module.exports = {
811
811
  })
812
812
 
813
813
  // ── HTTP API ─────────────────────────────────────────────────────────
814
- // 分享执行:进程内 agents 服务(web app 自身)可用则流式,否则 headless spawn
815
814
  const shareRunJobs = new Map()
816
- let shareServices = null
817
- try {
818
- if (ctx.inject && typeof ctx.inject === 'function') {
819
- ctx.inject(['agents', 'agentDefaultModel', 'sessions'], (svcs) => { shareServices = svcs })
820
- }
821
- } catch {}
815
+ // 分享执行:进程内 agents 服务(静态注入,apply 时已就绪)。动态 ctx.inject
816
+ // 在 apply 内不触发是平台 gotcha(skills-management 同款教训)——改静态捕获
817
+ const shareServices = { agents: ctx.agents, agentDefaultModel: ctx.agentDefaultModel, sessions: ctx.sessions }
822
818
  ctx.effect(() => ctx.webServer.register({
823
819
  kind: 'prefix',
824
820
  path: '/experts-management/api',