fix: close plugin workspace acceptance gaps
This commit is contained in:
1 parent
1b1f206dd8
commit
c5020ae22c
10 files changed
+885
-53
No files matched your search
@@ -14,6 +14,96 @@ import {
|
||||
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<string, DetailOperation>();
|
||||
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<string, DetailOperation[]>();
|
||||
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: '本机',
|
||||
@@ -60,6 +150,7 @@ function commandLabel(command: PluginWorkspaceCommand, title: string): string {
|
||||
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}插件设置`;
|
||||
}
|
||||
@@ -126,6 +217,7 @@ export function PluginDetails(props: PluginDetailsProps) {
|
||||
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 showDataService = project?.enabled
|
||||
&& project.settingsSurface === 'data-service';
|
||||
const confirmCopy = confirmation
|
||||
@@ -149,7 +241,10 @@ export function PluginDetails(props: PluginDetailsProps) {
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Badge variant="secondary">{SOURCE_TEXT[props.item.source]}</Badge>
|
||||
{props.item.stale ? <Badge variant="warning">缓存</Badge> : null}
|
||||
{props.item.unavailable ? <Badge variant="warning">暂不可用</Badge> : null}
|
||||
{props.item.suspended ? <Badge variant="warning">已暂停</Badge> : null}
|
||||
{props.item.retired ? <Badge variant="warning">已退役</Badge> : null}
|
||||
{props.item.unavailable && !props.item.suspended && !props.item.retired
|
||||
? <Badge variant="warning">暂不可用</Badge> : null}
|
||||
</div>
|
||||
<DialogTitle className="text-balance text-2xl">{props.item.title}</DialogTitle>
|
||||
<DialogDescription>
|
||||
@@ -190,6 +285,11 @@ export function PluginDetails(props: PluginDetailsProps) {
|
||||
<dd className="mt-1 text-sm font-semibold">{PROJECT_TEXT[props.item.projectState]}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
{props.item.deviceReason ? (
|
||||
<p className="mt-3 rounded-xl bg-warning/10 p-3 text-pretty text-sm text-warning">
|
||||
{installation?.version ? '旧设备版本已保留' : '设备包不可用'}:{props.item.deviceReason}
|
||||
</p>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="plugin-actions-heading">
|
||||
@@ -217,20 +317,38 @@ export function PluginDetails(props: PluginDetailsProps) {
|
||||
|
||||
<section aria-labelledby="plugin-capabilities-heading">
|
||||
<h3 id="plugin-capabilities-heading" className="text-lg font-semibold">能力与权限</h3>
|
||||
{project?.capabilities.length ? (
|
||||
{operations.length ? (
|
||||
<div className="mt-3 space-y-3">
|
||||
{project.capabilities.map((capability) => (
|
||||
<article key={capability.id} className="rounded-xl border border-border/70 p-4">
|
||||
<h4 className="font-mono text-sm font-semibold">{capability.id}</h4>
|
||||
{operationGroups(operations).map((capability) => (
|
||||
<article key={capability.capabilityId} className="rounded-xl border border-border/70 p-4">
|
||||
<h4 className="font-mono text-sm font-semibold">{capability.capabilityId}</h4>
|
||||
<ul className="mt-3 space-y-3">
|
||||
{capability.operations.map((operation) => (
|
||||
<li key={operation.id} className="rounded-lg bg-surface-subtle p-3 text-sm">
|
||||
<li key={operation.operationId} className="rounded-lg bg-surface-subtle p-3 text-sm">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<code className="font-semibold">{operation.id}</code>
|
||||
{operation.tool ? <Badge variant="outline">{operation.tool.mutation}</Badge> : <Badge variant="outline">SDK</Badge>}
|
||||
<code className="font-semibold">{operation.operationId}</code>
|
||||
{operation.mutation
|
||||
? <Badge variant="outline">{operation.mutation}</Badge>
|
||||
: operation.executionMode
|
||||
? <Badge variant="outline">{operation.executionMode === 'job' ? '异步任务' : '同步'}</Badge>
|
||||
: <Badge variant="outline">SDK</Badge>}
|
||||
{operation.enabled === false ? <Badge variant="warning">服务端已停用</Badge> : null}
|
||||
{operation.billing.availability === 'unavailable'
|
||||
|| operation.billing.status === 'billing_unavailable'
|
||||
? <Badge variant="warning">计费暂不可用</Badge> : null}
|
||||
</div>
|
||||
{operation.tool ? <p className="mt-2 text-pretty text-muted-foreground">{operation.tool.description}</p> : null}
|
||||
{operation.description ? <p className="mt-2 text-pretty text-muted-foreground">{operation.description}</p> : null}
|
||||
<p className="mt-2 text-xs">{operation.billing.notice}</p>
|
||||
<dl className="mt-2 grid gap-x-4 gap-y-1 text-xs text-muted-foreground sm:grid-cols-2">
|
||||
<div><dt className="inline">计费方式:</dt><dd className="inline">{operationBillingMode(operation.billing.mode)}</dd></div>
|
||||
{operation.billing.entitlementScope ? <div><dt className="inline">权益范围:</dt><dd className="inline">{operation.billing.entitlementScope}</dd></div> : null}
|
||||
{operation.billing.unitName ? <div><dt className="inline">计费单位:</dt><dd className="inline">{operation.billing.unitName}</dd></div> : null}
|
||||
{operation.billing.unitSize !== null ? <div><dt className="inline">单位大小:</dt><dd className="inline">{operation.billing.unitSize}</dd></div> : null}
|
||||
{operation.billing.ratePoints !== null ? <div><dt className="inline">每单位 Token Point:</dt><dd className="inline">{operation.billing.ratePoints}</dd></div> : null}
|
||||
{operation.billing.minimumChargePoints !== null ? <div><dt className="inline">最低扣点:</dt><dd className="inline">{operation.billing.minimumChargePoints}</dd></div> : null}
|
||||
{operation.billing.pricingVersion !== null ? <div><dt className="inline">价格版本:</dt><dd className="inline">{operation.billing.pricingVersion}</dd></div> : null}
|
||||
{operation.billing.roundingMode === 'ceil' ? <div><dt className="inline">舍入方式:</dt><dd className="inline">向上取整</dd></div> : null}
|
||||
</dl>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
Reference in new issue
Block a user