import { useState } from 'react'; import { AlertTriangle, ExternalLink, ShieldCheck, X } from 'lucide-react'; import { DataServicePluginSettings } from '@/components/plugins/data-service-settings'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import type { DataServiceInstanceState } from '../../../shared/data-service'; import type { PluginWorkspaceCommand, PluginWorkspaceItem } from './plugin-workspace-model'; interface DetailOperation { capabilityId: string; operationId: string; executionMode: 'synchronous' | 'job' | null; enabled: boolean | null; mutation: 'read' | 'write' | 'destructive' | null; description: string | null; billing: { mode: 'included' | 'platform_metered' | 'external_account'; availability: 'available' | 'unavailable' | null; status: 'billing_unavailable' | null; notice: string; entitlementScope: string | null; unitName: string | null; unitSize: number | null; ratePoints: string | null; minimumChargePoints: string | null; roundingMode: 'ceil' | null; pricingVersion: string | number | null; }; } function detailOperations(item: PluginWorkspaceItem): DetailOperation[] { const operations = new Map(); for (const operation of item.official?.detail?.operations ?? []) { operations.set(`${operation.capabilityId}:${operation.operation}`, { capabilityId: operation.capabilityId, operationId: operation.operation, executionMode: operation.executionMode, enabled: operation.enabled, mutation: null, description: null, billing: { ...operation.billing, availability: null, }, }); } for (const capability of item.official?.project?.capabilities ?? []) { for (const operation of capability.operations) { const key = `${capability.id}:${operation.id}`; const catalogOperation = operations.get(key); operations.set(key, { capabilityId: capability.id, operationId: operation.id, executionMode: catalogOperation?.executionMode ?? null, enabled: catalogOperation?.enabled ?? null, mutation: operation.tool?.mutation ?? null, description: operation.tool?.description ?? null, billing: { mode: operation.billing.mode, availability: operation.billing.availability, status: null, notice: operation.billing.notice, entitlementScope: null, unitName: operation.billing.unitName ?? null, unitSize: operation.billing.unitSize ?? null, ratePoints: operation.billing.ratePoints ?? null, minimumChargePoints: operation.billing.minimumChargePoints ?? null, roundingMode: operation.billing.roundingMode ?? null, pricingVersion: operation.billing.pricingVersion ?? null, }, }); } } return [...operations.values()]; } function operationGroups(operations: readonly DetailOperation[]): Array<{ capabilityId: string; operations: DetailOperation[]; }> { const groups = new Map(); for (const operation of operations) { const group = groups.get(operation.capabilityId) ?? []; group.push(operation); groups.set(operation.capabilityId, group); } return [...groups].map(([capabilityId, groupOperations]) => ({ capabilityId, operations: groupOperations, })); } function operationBillingMode(mode: DetailOperation['billing']['mode']): string { if (mode === 'included') return '包含'; if (mode === 'platform_metered') return '按 Token Point'; return '外部账号'; } const SOURCE_TEXT = { official: '官方', local: '本机', retained: '配置保留', } as const; const DELIVERY_TEXT = { system_included: '随 MakeLore 提供', account_acquired: '账号已获取', account_removed: '已从账号移除', account_unknown: '账号状态未知', local_installed: '本机已安装', not_acquired: '尚未获取', retained: '仅保留项目配置', } as const; const PROJECT_TEXT = { enabled: '当前项目已启用', disabled: '当前项目未启用', unavailable: '当前项目暂不可用', unknown: '当前项目状态未知', not_applicable: '不属于项目启用范围', } as const; const PROJECT_POLICY_NOTICE = { stale: '当前项目策略使用缓存,所示能力与价格可能不是最新状态。', unavailable: '当前项目策略不可用,无法确认最新能力与价格。', } as const; function billingText(item: PluginWorkspaceItem): string { if (item.billing === 'included') return '按平台包含,不按单次插件调用扣点。'; if (item.billing === 'token_point') return '按 Token Point 实际用量计费,价格与回执以服务端为准。'; if (item.billing === 'mixed') return '部分能力包含,部分能力按 Token Point 用量计费。'; if (item.billing === 'none') return '无 MakeLore 平台插件计费投影;外部服务或模型调用可能另行计费。'; return '当前没有可信的插件计费投影。'; } function commandLabel(command: PluginWorkspaceCommand, title: string): string { switch (command.kind) { case 'acquire': return `免费获取${title}`; case 'reacquire': return `重新免费获取${title}`; case 'remove_from_library': return `从账号移除${title}`; case 'install_stable': return `下载${title}稳定版`; case 'install_beta': return `安装${title} Beta`; case 'update_official': return `更新${title}设备包`; case 'remove_official_device_package': return `删除${title}官方设备包`; case 'enable_project': return `启用${title}到当前项目`; case 'disable_project': return `从当前项目禁用${title}`; case 'enable_local': return `本机全局启用${title}`; case 'disable_local': return `本机全局停用${title}`; case 'remove_local': return `移除本机包${title}`; case 'sign_in': return `登录后免费获取${title}`; case 'open_agent_assignment': return `分配${title}给伙伴`; case 'open_settings': return `查看${title}插件设置`; } } function destructive(command: PluginWorkspaceCommand): boolean { return command.kind === 'remove_from_library' || command.kind === 'remove_official_device_package' || command.kind === 'disable_project' || command.kind === 'remove_local'; } function confirmationCopy( command: PluginWorkspaceCommand, item: PluginWorkspaceItem, projectName: string | null, ): { title: string; description: string; confirm: string } { switch (command.kind) { case 'remove_from_library': return { title: `从账号移除${item.title}?`, description: '这会移除账号插件库记录,不会自动删除已经下载的设备包或项目配置。', confirm: '确认从账号移除', }; case 'remove_official_device_package': return { title: `删除${item.title}的官方设备包?`, description: '这只删除当前设备上的官方包,不会移除账号插件库或项目配置。', confirm: '确认删除官方设备包', }; case 'disable_project': return { title: `从当前项目禁用${item.title}?`, description: `插件能力会从项目“${projectName ?? '当前项目'}”停止,但插件数据与账号记录会保留。`, confirm: '确认从当前项目禁用', }; case 'remove_local': return { title: `移除本机包${item.title}?`, description: '这会从本机全局移除该包,新建或空闲的主伙伴与 worker 将不再加载它。', confirm: '确认移除本机包', }; default: return { title: '', description: '', confirm: '' }; } } export interface PluginDetailsProps { item: PluginWorkspaceItem; activeProjectName: string | null; dataService: DataServiceInstanceState | null; dataServicePending: Record; isCommandPending(command: PluginWorkspaceCommand): boolean; onClose(): void; onCommand(command: PluginWorkspaceCommand): void | Promise; onConfigureDataService(collections: string[]): void | Promise; onResetDataService(): void | Promise; onRemoveDataServiceCollection(collection: string): void | Promise; onRemoveDataServiceProject(): void | Promise; } export function PluginDetails(props: PluginDetailsProps) { const [confirmation, setConfirmation] = useState(null); const project = props.item.official?.project ?? null; const detail = props.item.official?.detail ?? null; const installation = props.item.official?.installation ?? null; const operations = detailOperations(props.item); const projectPolicyNotice = props.item.projectPolicyStatus === 'stale' || props.item.projectPolicyStatus === 'unavailable' ? PROJECT_POLICY_NOTICE[props.item.projectPolicyStatus] : null; const showDataService = project?.enabled && project.settingsSurface === 'data-service'; const confirmCopy = confirmation ? confirmationCopy(confirmation, props.item, props.activeProjectName) : null; const run = (command: PluginWorkspaceCommand) => { if (destructive(command)) { setConfirmation(command); return; } void props.onCommand(command); }; return ( <> { if (!open) props.onClose(); }}>
{SOURCE_TEXT[props.item.source]} {props.item.stale ? 缓存 : null} {props.item.suspended ? 已暂停 : null} {props.item.retired ? 已退役 : null} {props.item.unavailable && !props.item.suspended && !props.item.retired ? 暂不可用 : null}
{props.item.title} {props.item.publisher ? `${props.item.publisher} · ` : ''} {props.item.version ? `版本 ${props.item.version}` : '版本未知'}

介绍

{detail?.descriptionMarkdown || props.item.summary || '暂无介绍。'}

账号、设备与项目状态

交付
{DELIVERY_TEXT[props.item.delivery]}
当前设备
{props.item.source === 'local' ? (props.item.localEnabled ? '本机全局已启用' : '本机全局已停用') : installation?.version ? `官方包 ${installation.version}` : props.item.delivery === 'system_included' ? '随应用提供' : '未下载官方包'}
当前项目
{PROJECT_TEXT[props.item.projectState]}
{props.item.deviceReason ? (

{installation?.version ? '旧设备版本已保留' : '设备包不可用'}:{props.item.deviceReason}

) : null}

可用操作

{props.item.commands.length ? (
{props.item.commands.map((command) => ( ))}
) :

当前状态没有可执行操作。

}

能力与权限

{operations.length ? (
{operationGroups(operations).map((capability) => (

{capability.capabilityId}

    {capability.operations.map((operation) => (
  • {operation.operationId} {operation.mutation ? {operation.mutation} : operation.executionMode ? {operation.executionMode === 'job' ? '异步任务' : '同步'} : SDK} {operation.enabled === false ? 服务端已停用 : null} {operation.billing.availability === 'unavailable' || operation.billing.status === 'billing_unavailable' ? 计费暂不可用 : null}
    {operation.description ?

    {operation.description}

    : null}

    {operation.billing.notice}

    计费方式:
    {operationBillingMode(operation.billing.mode)}
    {operation.billing.entitlementScope ?
    权益范围:
    {operation.billing.entitlementScope}
    : null} {operation.billing.unitName ?
    计费单位:
    {operation.billing.unitName}
    : null} {operation.billing.unitSize !== null ?
    单位大小:
    {operation.billing.unitSize}
    : null} {operation.billing.ratePoints !== null ?
    每单位 Token Point:
    {operation.billing.ratePoints}
    : null} {operation.billing.minimumChargePoints !== null ?
    最低扣点:
    {operation.billing.minimumChargePoints}
    : null} {operation.billing.pricingVersion !== null ?
    价格版本:
    {operation.billing.pricingVersion}
    : null} {operation.billing.roundingMode === 'ceil' ?
    舍入方式:
    向上取整
    : null}
  • ))}
))}
) : props.item.local ? (

{props.item.local.kind === 'skill-only' ? 'Skill' : props.item.local.kind === 'pi-extension' ? 'Pi extension' : 'Skill + Pi extension'}

{props.item.local.skillEntries.length} 个 Skill · {props.item.local.extensionEntries.length} 个 extension

{props.item.local.confirmedExecutableCode ? (

{props.item.local.kind === 'skill-only' ? '包含可执行 Skill 脚本;调用时可能使用当前桌面用户可用的文件、网络和进程权限。' : '包含可执行代码,运行时拥有完整桌面权限。'}

) : null}
) :

当前没有可显示的能力投影。

} {detail?.permissions.length ? (

{detail.permissions.join('、')}

) : null}

Token Point 与计费

{projectPolicyNotice ? (

{projectPolicyNotice}

) : null}

{billingText(props.item)}

伙伴分配

{props.item.source === 'local' ?

本机包全局生效,不作为项目 Agent Skill 分配项;符合条件的新建或空闲主伙伴及其 worker 会按既有生命周期加载。

: props.item.assignedAgentNames.length ?
{props.item.assignedAgentNames.map((name) => {name})}
:

尚未分配给伙伴。

}
{showDataService ? (

插件专属设置

) : null}
{ if (!open) setConfirmation(null); }}> {confirmCopy?.title} {confirmCopy?.description} ); }