diff --git a/.gitignore b/.gitignore index 08f5590..30df580 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ node_modules/ # OS / editor .DS_Store + +# Local operator notes / specs (not product source) +/spec/ +/需求整理-*.md diff --git a/hub/admin-web/src/lib/api.ts b/hub/admin-web/src/lib/api.ts index 87d65c3..1c923c1 100644 --- a/hub/admin-web/src/lib/api.ts +++ b/hub/admin-web/src/lib/api.ts @@ -480,7 +480,18 @@ export const api = { rotateCapabilityConnection: ( slug: string, capabilityId: string, - body: { accessKeyId: string; accessKeySecret: string; endpoint: string }, + body: + | { kind?: 'docmind'; accessKeyId: string; accessKeySecret: string; endpoint: string } + | { + kind: 'pbank'; + baseUrl: string; + username: string; + password: string; + rightsStatus?: string; + rightsHolder?: string; + rightsScope?: string; + rightsNote?: string; + }, ) => put(`${orgBase(slug)}/capability-connections/${encodeURIComponent(capabilityId)}`, body) as Promise, disableCapabilityConnection: (slug: string, capabilityId: string) => diff --git a/hub/admin-web/src/lib/constants.ts b/hub/admin-web/src/lib/constants.ts index cd84320..857981a 100644 --- a/hub/admin-web/src/lib/constants.ts +++ b/hub/admin-web/src/lib/constants.ts @@ -18,6 +18,8 @@ export const TOOL_OPTIONS: ToolOption[] = [ { id: 'feishu_read_context', label: '读飞书上下文', group: '飞书' }, { id: 'feishu_download_resource', label: '下载飞书资源', group: '飞书' }, { id: 'request_approval', label: '请求审批', group: '飞书' }, + { id: 'convert_pdf_to_md', label: 'PDF→Markdown', group: '能力' }, + { id: 'pbank', label: '题库 (PBank)', group: '能力' }, ]; /** 组织成员角色(接口枚举保持英文,界面用 orgRoleLabel) */ diff --git a/hub/admin-web/src/routes/admin/capabilities/+page.svelte b/hub/admin-web/src/routes/admin/capabilities/+page.svelte index 5a1573b..6c3c1b2 100644 --- a/hub/admin-web/src/routes/admin/capabilities/+page.svelte +++ b/hub/admin-web/src/routes/admin/capabilities/+page.svelte @@ -13,9 +13,26 @@ const org = $derived(resolveOrg($session.me, page.url.search)); const slug = $derived(org?.slug ?? ''); + type CapKind = 'docmind' | 'pbank'; const KNOWN_CAPABILITIES = [ - { id: 'pdf_to_md_bundle', label: 'PDF → Markdown', description: '将 PDF 转换为带图片的 Markdown bundle(阿里云文档智能,含公式 LaTeX 识别)' }, - { id: 'audio_video_to_text', label: '音视频 → 文本', description: '将音频/视频转写为文本(阿里云文档智能,按秒计费)' }, + { + id: 'pdf_to_md_bundle', + kind: 'docmind' as const, + label: 'PDF → Markdown', + description: '将 PDF 转换为带图片的 Markdown bundle(阿里云文档智能,含公式 LaTeX 识别)' + }, + { + id: 'audio_video_to_text', + kind: 'docmind' as const, + label: '音视频 → 文本', + description: '将音频/视频转写为文本(阿里云文档智能,按秒计费)' + }, + { + id: 'pbank', + kind: 'pbank' as const, + label: '题库 (PBank)', + description: '搜索/拉取 Paradigm 题库题目与源工程;Agent 通过 pbank_* 工具访问,凭据不下发到 Agent 进程' + } ] as const; let connections = $state>(new Map()); @@ -23,9 +40,17 @@ let error = $state(null); let editingCap = $state(null); + let editingKind = $state('docmind'); let accessKeyId = $state(''); let accessKeySecret = $state(''); let endpoint = $state('docmind-api.cn-hangzhou.aliyuncs.com'); + let baseUrl = $state('https://pbank.paradigm-edu.net/api'); + let username = $state(''); + let password = $state(''); + let rightsStatus = $state('owned'); + let rightsHolder = $state('Paradigm Education'); + let rightsScope = $state('internal teaching-material production'); + let rightsNote = $state(''); let saving = $state(false); let disabling = $state(null); @@ -42,11 +67,19 @@ } } - function startEdit(capId: string) { + function startEdit(capId: string, kind: CapKind) { editingCap = capId; + editingKind = kind; accessKeyId = ''; accessKeySecret = ''; endpoint = 'docmind-api.cn-hangzhou.aliyuncs.com'; + baseUrl = 'https://pbank.paradigm-edu.net/api'; + username = ''; + password = ''; + rightsStatus = 'owned'; + rightsHolder = 'Paradigm Education'; + rightsScope = 'internal teaching-material production'; + rightsNote = ''; } function cancelEdit() { @@ -54,17 +87,36 @@ } async function save(capId: string) { - if (accessKeyId.trim() === '' || accessKeySecret.trim() === '' || endpoint.trim() === '') { - toastError('AccessKey ID、AccessKey Secret、Endpoint 均为必填'); - return; - } saving = true; try { - const result = await api.rotateCapabilityConnection(slug, capId, { - accessKeyId: accessKeyId.trim(), - accessKeySecret: accessKeySecret.trim(), - endpoint: endpoint.trim(), - }); + let result: CapabilityConnection; + if (editingKind === 'docmind') { + if (accessKeyId.trim() === '' || accessKeySecret.trim() === '' || endpoint.trim() === '') { + toastError('AccessKey ID、AccessKey Secret、Endpoint 均为必填'); + return; + } + result = await api.rotateCapabilityConnection(slug, capId, { + kind: 'docmind', + accessKeyId: accessKeyId.trim(), + accessKeySecret: accessKeySecret.trim(), + endpoint: endpoint.trim() + }); + } else { + if (baseUrl.trim() === '' || username.trim() === '' || password.trim() === '') { + toastError('Base URL、用户名、密码均为必填'); + return; + } + result = await api.rotateCapabilityConnection(slug, capId, { + kind: 'pbank', + baseUrl: baseUrl.trim(), + username: username.trim(), + password: password.trim(), + ...(rightsStatus.trim() !== '' ? { rightsStatus: rightsStatus.trim() } : {}), + ...(rightsHolder.trim() !== '' ? { rightsHolder: rightsHolder.trim() } : {}), + ...(rightsScope.trim() !== '' ? { rightsScope: rightsScope.trim() } : {}), + ...(rightsNote.trim() !== '' ? { rightsNote: rightsNote.trim() } : {}) + }); + } connections.set(capId, result); connections = new Map(connections); editingCap = null; @@ -110,7 +162,7 @@ {#if loading} @@ -147,7 +199,7 @@ {/if}