Remove selected-user sharing from cloud agent access UI

This commit is contained in:
2026-09-19 08:52:04 +08:00
parent 7fb4c60492
commit e04787763b
8 changed files with 94 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { Copy, Link as LinkIcon, Plus } from 'lucide-react';
import { Plus } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { usePendingCloudInput } from './CloudPending';
@@ -15,8 +15,6 @@ import { cloudStatus } from './CloudChat';
const errorText = (e: unknown) => e instanceof Error ? e.message : '操作失败,请重试';
export function CloudAccessPanel({ slug, revision, publication, onChanged, activeView = true }: { slug: string; revision: number; publication: CloudPublication; onChanged?: (draft: CloudAgentDraft) => void; activeView?: boolean }) {
const { access, refresh, publish } = publication;
const [query, setQuery] = useState('');
const [users, setUsers] = useState<{ account_id: string; display_name: string; username: string }[]>([]);
const [name, setName] = useState('');
const [acting, setBusy] = useState(false);
const busy = acting || publication.busy;
@@ -43,18 +41,14 @@ export function CloudAccessPanel({ slug, revision, publication, onChanged, activ
applicationOperation.current = null;
setName('');
}, '应用已创建,可为它生成 API 凭据');
const copy = async (text: string) => {
try { await navigator.clipboard.writeText(text); setNotice('已复制'); }
catch { setError('复制失败,请手动选择文本复制'); }
};
return <div className="space-y-8">
<section className="rounded-xl bg-violet-50/60 p-5">
<div className="flex flex-wrap items-center justify-between gap-4"><div>
<h2 className="font-medium">{access?.published_version ? '助手已准备好' : '准备好使用自己的助手了吗?'}</h2>
<p className="mt-2 text-sm text-muted-foreground">{access?.published_version ? '应用最新修改后,新的对话会使用这次保存的要求。已有对话保留原来的版本。' : '开始使用后,只有你自己能用。以后还可以回来修改要求。'}</p>
<p className="mt-2 text-xs text-muted-foreground">分享给别人或接入应用,需要另外开启。</p>
<p className="mt-2 text-xs text-muted-foreground">微信渠道和应用接入需要单独配置。</p>
</div><Button disabled={busy || !access || (!publication.pending && publication.appliedRevision === revision)} onClick={() => void publish()}>{publication.busy ? '正在应用…' : publication.pending ? '重试本次发布' : publication.appliedRevision === revision ? '已应用最新修改' : access?.published_version ? '应用最新修改' : '确认开始使用'}</Button></div>
<p className="mt-4 text-sm leading-6">自己使用、分享使用、API 调用、自动任务和个人微信渠道产生的费用,均从你的个人词元点数扣除。你可以随时停用智能体或撤销访问。</p>
<p className="mt-4 text-sm leading-6">自己使用、API 调用、自动任务和个人微信渠道产生的费用,均从你的个人词元点数扣除。你可以随时停用智能体或撤销应用凭据。</p>
{access?.published_version && <div className="mt-4 flex items-center gap-3 text-sm"><span>{access.enabled ? '智能体已启用' : '智能体已停用'}</span>
<Button variant="outline" size="sm" disabled={busy} onClick={() => void act(() => cloudAgentsApi.call('setEnabled', { slug, enabled: !access.enabled }))}>
{access.enabled ? '停用智能体' : '启用智能体'}</Button></div>}
@@ -64,22 +58,6 @@ export function CloudAccessPanel({ slug, revision, publication, onChanged, activ
{notice && <p role="status" className="text-sm text-muted-foreground">{notice}</p>}
{access?.published_version && <>
<LinkedChannels slug={slug} activeView={activeView} />
<section className="space-y-4"><h2 className="font-medium">分享给指定用户</h2>
<p className="text-sm text-muted-foreground">选择获准使用的账号,再发送分享链接。每个人的对话和文件分别保存。</p>
<div className="flex gap-2"><Input aria-label="查找分享用户" value={query} onChange={e => setQuery(e.target.value)} placeholder="用户名、显示名或账号 ID" maxLength={100} />
<Button variant="outline" disabled={busy || query.trim().length < 2 || !access?.published_version}
onClick={() => void act(async () => { const result = await cloudAgentsApi.call('users', { query: query.trim() }); setUsers(result.users); })}>查找</Button></div>
{users.map(user => <div key={user.account_id} className="flex items-center justify-between gap-3 rounded-lg bg-muted/30 p-3 text-sm">
<div><p>{user.display_name} <span className="text-muted-foreground">@{user.username}</span></p><p className="mt-1 break-all text-xs text-muted-foreground">{user.account_id}</p></div>
<Button variant="outline" disabled={busy} onClick={() => void act(() => cloudAgentsApi.call('share', { slug, account_id: user.account_id, enabled: true }), '已授权此账号')}>授权使用</Button>
</div>)}
{access?.grants.filter(g => g.enabled).map(grant => <div key={grant.account_id} className="flex items-center justify-between gap-3 text-sm">
<span className="break-all">{grant.account_id}</span><Button variant="ghost" disabled={busy} onClick={() => void act(() => cloudAgentsApi.call('share', { slug, account_id: grant.account_id, enabled: false }))}>撤销</Button>
</div>)}
{access?.published_version && <div className="flex items-center gap-3 rounded-lg border border-border p-3">
<LinkIcon className="h-4 w-4 shrink-0" /><code className="min-w-0 flex-1 break-all text-xs">{access.share_url}</code>
<Button aria-label="复制分享链接" variant="ghost" onClick={() => void copy(access.share_url)}><Copy className="h-4 w-4" /></Button></div>}
</section>
<section className="space-y-4"><h2 className="font-medium">应用与 API</h2>
<p className="text-sm text-muted-foreground">为网站或脚本创建独立应用。应用通过凭据调用此智能体,并拥有自己的会话空间。</p>
<div className="flex gap-2"><Input aria-label="应用名称" value={name} disabled={busy || Boolean(applicationOperation.current)} onChange={e => setName(e.target.value)} maxLength={100} placeholder="例如:我的个人网站" />

View File

@@ -73,7 +73,7 @@ export function ConfigurationFields({ slug, value, disabled, onChange, section =
</fieldset>;
})}
<div hidden={section !== 'all' && section !== 'capabilities'} className="space-y-4">
<p className="text-xs leading-5 text-muted-foreground">未选择的资源不会开放给此智能体。分享使用时仍按本配置限制能力。</p>
<p className="text-xs leading-5 text-muted-foreground">未选择的资源不会开放给此智能体。通过微信或 API 使用时也遵循本配置。</p>
<CloudResources onChanged={() => setRefresh(v => v + 1)} />
</div>
<div hidden={section !== 'all' && section !== 'knowledge'}>

View File

@@ -140,7 +140,7 @@ export function DraftEditor({ initial, recent, onBack, onSaved, startingPrompt }
<nav aria-label="智能体工作区" className="agent-workspace-nav">
{([['configuration', '编辑'], ['chat', '对话'], ['tasks', '自动任务']] as const).map(([key, label]) =>
<Button key={key} variant={tab === key ? 'secondary' : 'ghost'} aria-current={tab === key ? 'page' : undefined} onClick={() => navigate(key)}>{label}</Button>)}
<Button className="ml-auto" variant={tab === 'access' ? 'secondary' : 'ghost'} aria-current={tab === 'access' ? 'page' : undefined} onClick={() => navigate('access')}>使用与分享</Button>
<Button className="ml-auto" variant={tab === 'access' ? 'secondary' : 'ghost'} aria-current={tab === 'access' ? 'page' : undefined} onClick={() => navigate('access')}>使用与接入</Button>
</nav>
{saved.published_version && (needsApply || dirty) && <div className="shrink-0 border-b border-amber-200 bg-amber-50 px-6 py-3 text-sm text-amber-900">
<p className="font-medium">{dirty ? '修改尚未保存和应用' : '这次修改还没有应用到正在使用的助手'}</p>