@waterplus-ai/waterbuddy 0.2.6 → 0.2.8
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/lib/client/index.js +3 -57
- package/lib/host/index.js +16 -7
- package/package.json +1 -1
package/lib/client/index.js
CHANGED
|
@@ -617,41 +617,6 @@ window.__ModuleLoader__.load({
|
|
|
617
617
|
return null
|
|
618
618
|
}
|
|
619
619
|
|
|
620
|
-
/**
|
|
621
|
-
* 写剪贴板。
|
|
622
|
-
*
|
|
623
|
-
* `navigator.clipboard` 只在安全上下文可用,而且在内嵌 webview 里可能被权限策略挡掉;
|
|
624
|
-
* 这里落到 `execCommand('copy')` 兜底(已废弃,但仍是 webview 里最可靠的一条路)。
|
|
625
|
-
* @param text - 要复制的内容。
|
|
626
|
-
* @throws 两条路都不通时抛出,由调用方提示用户。
|
|
627
|
-
*/
|
|
628
|
-
async function writeClipboard(text) {
|
|
629
|
-
if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
|
|
630
|
-
try {
|
|
631
|
-
await navigator.clipboard.writeText(text)
|
|
632
|
-
return
|
|
633
|
-
} catch { /* 落到下面的兜底 */ }
|
|
634
|
-
}
|
|
635
|
-
// 兜底:WebKit 对"完全不可见"的 textarea 常常拒绝复制,所以留 1px 与极小透明度,
|
|
636
|
-
// 并且必须 focus + select 之后再 execCommand。整段仍是同步的,手势不会丢。
|
|
637
|
-
const area = document.createElement('textarea')
|
|
638
|
-
area.value = text
|
|
639
|
-
area.setAttribute('readonly', '')
|
|
640
|
-
area.style.position = 'fixed'
|
|
641
|
-
area.style.top = '0'
|
|
642
|
-
area.style.left = '0'
|
|
643
|
-
area.style.width = '1px'
|
|
644
|
-
area.style.height = '1px'
|
|
645
|
-
area.style.padding = '0'
|
|
646
|
-
area.style.border = '0'
|
|
647
|
-
area.style.opacity = '0.01'
|
|
648
|
-
document.body.append(area)
|
|
649
|
-
area.focus()
|
|
650
|
-
area.select()
|
|
651
|
-
const ok = typeof document.execCommand === 'function' && document.execCommand('copy')
|
|
652
|
-
area.remove()
|
|
653
|
-
if (!ok) throw new Error('clipboard-unavailable')
|
|
654
|
-
}
|
|
655
620
|
|
|
656
621
|
/**
|
|
657
622
|
* 新会话 hero 的品牌标记:用我们左上角那个 mark,替换宿主默认的鸟形图标。
|
|
@@ -1184,20 +1149,6 @@ window.__ModuleLoader__.load({
|
|
|
1184
1149
|
const avatar = expert.avatar
|
|
1185
1150
|
? jsx('img', { src: expert.avatar, alt: '', style: { width: 48, height: 48, borderRadius: '50%', objectFit: 'cover', flexShrink: 0 } })
|
|
1186
1151
|
: jsx('span', { style: { width: 48, height: 48, borderRadius: '50%', display: 'grid', placeItems: 'center', background: '#eef4ff', fontSize: 27, flexShrink: 0 }, children: expert.emoji || '🧠' })
|
|
1187
|
-
const copySummary = () => {
|
|
1188
|
-
const lines = [
|
|
1189
|
-
expert.name,
|
|
1190
|
-
expert.profession ? `职业:${expert.profession}` : '',
|
|
1191
|
-
expert.description,
|
|
1192
|
-
expert.version ? `版本:${expert.version}` : '',
|
|
1193
|
-
expert.defaultInitPrompt ? `默认开场:${expert.defaultInitPrompt}` : '',
|
|
1194
|
-
members.length > 0 ? `团队成员:${members.map(member => `${member.name || member.slug}(${member.role === 'lead' ? '负责人' : '成员'})`).join('、')}` : '',
|
|
1195
|
-
skills.length > 0 ? `关联技能:${skills.map(skill => skill.skillName).join('、')}` : '',
|
|
1196
|
-
knowledgeBases.length > 0 ? `关联知识库:${knowledgeBases.map(item => item.name || item.id || item).join('、')}` : '',
|
|
1197
|
-
...quickPrompts.map(item => `- ${item}`),
|
|
1198
|
-
].filter(Boolean)
|
|
1199
|
-
void writeClipboard(lines.join('\n')).catch(() => {})
|
|
1200
|
-
}
|
|
1201
1152
|
return jsx('div', { style: EXPERT_STYLE.overlay, onClick: event => { if (event.target === event.currentTarget) onClose() }, children:
|
|
1202
1153
|
jsxs('div', { style: { ...EXPERT_STYLE.modal, width: 'min(860px, 100%)', maxHeight: '86vh' }, children: [
|
|
1203
1154
|
jsxs('div', { style: EXPERT_STYLE.modalHead, children: [
|
|
@@ -1273,11 +1224,7 @@ window.__ModuleLoader__.load({
|
|
|
1273
1224
|
jsx('summary', { style: { cursor: 'pointer', color: '#64748b', fontSize: 12 }, children: '查看角色定义全文' }),
|
|
1274
1225
|
jsx('pre', { style: { ...EXPERT_STYLE.pre, marginTop: 8, padding: 10, borderRadius: 9, background: '#f8fafc', maxHeight: 360, overflow: 'auto' }, children: dialog.body || '暂无角色定义' }),
|
|
1275
1226
|
] }),
|
|
1276
|
-
] }) })
|
|
1277
|
-
jsxs('div', { style: EXPERT_STYLE.modalFoot, children: [
|
|
1278
|
-
jsx('button', { type: 'button', onClick: copySummary, style: EXPERT_STYLE.ghost, children: '分享' }),
|
|
1279
|
-
jsx('button', { type: 'button', onClick: () => { void writeClipboard(dialog.body || '').catch(() => {}) }, style: EXPERT_STYLE.ghost, children: '复制提示词' }),
|
|
1280
|
-
] }),
|
|
1227
|
+
] }) })
|
|
1281
1228
|
] }) })
|
|
1282
1229
|
}
|
|
1283
1230
|
|
|
@@ -1433,9 +1380,8 @@ window.__ModuleLoader__.load({
|
|
|
1433
1380
|
}
|
|
1434
1381
|
}
|
|
1435
1382
|
|
|
1436
|
-
//
|
|
1437
|
-
//
|
|
1438
|
-
// 复制入口保留在「查看提示词」弹窗内——正文已经拿到手,点击即同步写入。
|
|
1383
|
+
// 不提供剪贴板导出能力:WebKit(Tauri 用的就是它)要求剪贴板写入发生在用户手势的同步
|
|
1384
|
+
// 调用栈里,而角色正文要先按 slug 异步取回,异步路径下写入常被拒(`NotAllowedError`)。
|
|
1439
1385
|
|
|
1440
1386
|
const experts = snapshot ? snapshot.experts || [] : []
|
|
1441
1387
|
const teams = snapshot
|
package/lib/host/index.js
CHANGED
|
@@ -664,14 +664,18 @@ function setupExpertTools(ctx, config, { getCatalog, getEnabled }) {
|
|
|
664
664
|
return disposers
|
|
665
665
|
}
|
|
666
666
|
|
|
667
|
-
|
|
667
|
+
// 只列本插件**必需**的服务:cordis 的 inject 没有「可选」语义(对象形式只是拦截配置),
|
|
668
|
+
// 少一个就整条 fiber 永久 pending,品牌、登录、额度、MCP 全都不激活。
|
|
669
|
+
// `tools`/`subagents` 只在专家功能里用,且宿主缺失时本就按「专家禁用」降级;`settings`
|
|
670
|
+
// 从未被使用。三者一律改为延迟注入(见 apply)。
|
|
671
|
+
export const inject = ['webServer', 'systemPrompt', 'credentials']
|
|
668
672
|
export { normalizeAvatar, setUpstreamTransport }
|
|
669
673
|
|
|
670
674
|
export function apply(ctx, config) {
|
|
671
675
|
readPersistedAuth()
|
|
672
676
|
ctx.effect(() => {
|
|
673
|
-
//
|
|
674
|
-
//
|
|
677
|
+
// 内置名册读一次(异步、失败只记日志);自定义专家与启用状态由 expertStore 合并进快照
|
|
678
|
+
// (文件存储,不走宿主 settings 命名空间),用户在设置页改完立刻生效,不需要重启。
|
|
675
679
|
const builtinCatalog = loadExpertCatalog(config?.expertsDir || undefined)
|
|
676
680
|
builtinCatalog.catch(error => console.warn('[WaterPlusAI] expert catalog failed to load:', error.message))
|
|
677
681
|
// 状态存在 $DSH_HOME/waterbuddy-experts.json(`expertsStateFile` 可覆盖,测试用临时文件)。
|
|
@@ -725,9 +729,14 @@ export function apply(ctx, config) {
|
|
|
725
729
|
const disposers = routes.map(route => ctx.webServer.register(route))
|
|
726
730
|
const disposeTitle = ctx.webServer.tapIndex(html => html.replace(/<title>[^<]*<\/title>/i, '<title>WaterPlusAI</title>').replace(/<link[^>]+rel=["']icon["'][^>]*>/i, '<link rel="icon" type="image/png" href="/waterplus-mark.png" />'))
|
|
727
731
|
const disposePersona = ctx.systemPrompt.section({ name: 'waterplus:persona', order: 100, text: WATERPLUS_PERSONA })
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
732
|
+
// 专家是附加能力:`tools`/`subagents` 延迟注入,服务到位才挂工具。宿主没有它们时本插件
|
|
733
|
+
// 照常激活(品牌、登录、额度、MCP 都在),只是专家功能不出现——附加能力绝不能挡住启动。
|
|
734
|
+
ctx.inject(['tools', 'subagents'], expertCtx => {
|
|
735
|
+
const expertDisposers = setupExpertTools(expertCtx, config, {
|
|
736
|
+
getCatalog: async () => (await expertSnapshot()).catalog,
|
|
737
|
+
getEnabled: () => expertStore.read().enabled,
|
|
738
|
+
})
|
|
739
|
+
expertCtx.effect(() => () => { for (const dispose of expertDisposers) dispose() })
|
|
731
740
|
})
|
|
732
741
|
// 尽力而为:宿主没有 credentials 服务(例如测试替身)时直接跳过,不发网络请求。
|
|
733
742
|
if (ctx.credentials?.set) {
|
|
@@ -735,6 +744,6 @@ export function apply(ctx, config) {
|
|
|
735
744
|
llmLogoutHook = () => restoreDeviceLlmCredential(ctx, { config })
|
|
736
745
|
void bootstrapLlmCredential(ctx, config)
|
|
737
746
|
}
|
|
738
|
-
return () => { for (const dispose of disposers) dispose(); disposeTitle(); disposePersona();
|
|
747
|
+
return () => { for (const dispose of disposers) dispose(); disposeTitle(); disposePersona(); sessions.clear(); currentUser = undefined; persistedAuth = undefined }
|
|
739
748
|
}, 'waterbuddy: identity and auth')
|
|
740
749
|
}
|