Remove selected-user sharing from cloud agent access UI
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
# Task: Remove selected-user sharing from cloud agent access UI
|
||||
|
||||
## Identity
|
||||
|
||||
- Task ID: 20260919-remove-agent-user-share-64b7c21e
|
||||
- Mode: Feature
|
||||
- Branch: codex/20260919-remove-agent-user-share-64b7c21e-remove-agent-user-share
|
||||
- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20260919-remove-agent-user-share-64b7c21e
|
||||
- Base commit: 7fb4c60492da1cbbdd1b5b4fcf4198dcf1973684
|
||||
- Owner: codex
|
||||
- Status: Ready for Integration
|
||||
|
||||
## Scope
|
||||
|
||||
- Remove the selected-user sharing section from the cloud-agent access page, including user lookup, grants controls, and the share link. Rename the visible entry to 使用与接入 and align related guidance.
|
||||
- Update README and focused unit/Electron interaction coverage. Leave CloudCosts implementation owned by the usage-filter-alignment peer unchanged.
|
||||
|
||||
## Intent And Constraints
|
||||
|
||||
- The user explicitly requested removal of the highlighted 分享给指定用户 module. This is a client UI removal; existing server grants, backend contracts, authentication, API applications, WeChat routing, and billing remain unchanged.
|
||||
- Isolated feature work from main 7fb4c60492da1cbbdd1b5b4fcf4198dcf1973684. No merge, deployment, package, or cleanup included.
|
||||
- Concurrent Task Gate and Planning Gate passed. Peer CloudCosts work has no conflicting semantics; historical incomplete peer records remain read-only with unknown coordination state.
|
||||
|
||||
## Outcome
|
||||
|
||||
- Removed the selected-user sharing block and its user-search state, grant actions, copy handler and icons from CloudAccessPanel.
|
||||
- Renamed 使用与分享 to 使用与接入. Related access/capability guidance and README now describe WeChat and application API entry points.
|
||||
- Existing backend grants are not revoked; the client no longer offers grant controls or a share link. Applications, publication, channels, costs, and lifecycle controls remain.
|
||||
- Reviewed the complete diff and the Electron screenshot: associated channels flow directly into application API management, without a sharing section.
|
||||
|
||||
## Verification
|
||||
|
||||
- Exact pinned pnpm 10.33.4 dependency install with frozen lockfile: passed; lockfile unchanged.
|
||||
- pnpm run typecheck: passed.
|
||||
- Focused ESLint on three changed UI modules and three changed test files: passed.
|
||||
- Vitest cloud-agents-workflows, cloud-agents-page, cloud-agents-guidance: 3 files, 50 tests passed. Regression includes populated legacy grants/share URL, no sharing controls or mutation calls, and successful application creation.
|
||||
- pnpm run build:vite: passed (existing bundle-size warning).
|
||||
- Electron cloud-agents.spec.ts: 1 passed, including renamed navigation, sharing controls absent, and channel/application entries present; publication and conversation flows continue to pass.
|
||||
- Visually inspected test-results/cloud-agents-personal-clou-f6c7b-eave-protection-in-Electron/cloud-agent-access-without-user-sharing.png.
|
||||
- git diff --check and check_doc_drift.py --task-id 20260919-remove-agent-user-share-64b7c21e: passed.
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- Merge and deployment are outside this task; the change becomes visible after shipping the updated client.
|
||||
|
||||
## Promotion Candidates
|
||||
|
||||
- Targets: .project-docs/10-decisions/ADR-2026-09-11-personal-cloud-agents.md and .project-docs/current-state.md. Update the desktop access workflow: it offers WeChat and application API management, and no longer offers selected-user sharing controls or a share link.
|
||||
- Evidence: explicit user request and the resulting UI/test changes. Future impact: avoid reintroducing the removed sharing module in desktop flows.
|
||||
- Semantic conflict: supersedes the accepted decision's selected-user-sharing UI entry only; server grants and historical billing data remain unchanged. Human confirmation: already provided by the user's explicit removal request; no additional decision needed for this scope.
|
||||
@@ -7,11 +7,11 @@
|
||||
Makelore 是一个面向软件、视觉创作、智能机器人与个人云智能体的 AI 桌面工作台。当前版本为 `2.0.0`,源码提供四个模块入口;云智能体需配套配置 WS/Yuxi 服务。模块入口页按 Agents、Code、Canvas、Robot 排列,统一采用横向插画卡片;Agents 标语为“打造你想象中的AI助手”。工作区左上角入口点击后返回模块入口页:
|
||||
|
||||
- `Makelore Code|AI 编程`:管理本地项目、项目智能体、对话、文件上下文、代码变更和运行时。
|
||||
- `Makelore Agents|AI 智能体`:配置个人云智能体并试聊,发布后正式对话、按账号分享或创建应用 API Key。支持流式回复、审批、排队请求、附件与产物、自动任务和活动历史;重新进入时恢复最近智能体与对话,未确认操作可按原输入重试。聊天可提出日程建议,由创建者核对能力、时间和费用后启用。知识库上传、替换及导入本人会话附件后自动解析并建立索引;文档卡片区分排队、解析、索引、可用和失败,持续刷新已加载文档,失败可原地重试;零文本内容不会显示为可用,新文档可用后才移除被替换的旧文档。支持删除知识内容;个人 MCP 凭据、Skills、子智能体可自助管理。支持智能体和会话归档、发布版本比较及恢复为草稿。除知识库 embedding 向量化外,模型调用(包括知识处理触发的对话模型调用)仍按原规则由创建者个人词元点数支付,可按智能体设置每次任务和每日上限,费用按时间、来源、应用分页统计;embedding 向量化由平台承担,不计入用户用量、词元点数或智能体费用上限;分享/API 调用者保有自己的内容空间。Main 管理云会话和本机文件,Renderer 通过 Host API 操作。配套服务接入见 [Yuxi MakeLore 说明](https://xerrors.github.io/Yuxi/advanced/makelore-agents.html)。 编辑采用全高桌面分栏,支持拖动调整与展开、独立滚动、小窗口配置/试用切换。首次创建按选用途、说要求、试一试引导,提供科学问答、故事、英语和笔记整理起点;回答方式可多选,特别要求直接编辑,仍以草稿的 system_prompt 保存。新助手预选云端目录的第一个可用模型并明确显示,可自行更换;已有助手不自动换模型。编辑区以“我的要求”和“高级设置”两个 Tab 切换,默认显示基础要求;高级设置包含能力、知识与限制,返回时保留上次分类,切换保留未保存的输入及当前试用。已开始使用的助手在顶部显示“修改已保存,尚未应用”或“已应用最新修改”,并提供显眼的“应用最新修改”按钮;保存只更新草稿,应用沿用同一发布操作,失败重试保留原操作身份。草稿明确保存(Ctrl/⌘+S),保存与切换页面保留当前试用及未发送文字;“保存并重新试用”使用最新保存的要求,保留上次试用供对比,并可复用上次问题,不自动发送。示例问题仅填入输入框;有运行、排队、审批或未确认操作时先处理当前试用。工具确认先展示实际操作说明与参数,完整技术详情可展开。确认“开始使用”会创建仅自己可用的发布版本并进入独立对话,分享与应用仍单独开启。费用上限在限制中独立保存。Enter 发送、Shift+Enter 换行,F6 切换面板焦点、Esc 恢复分栏。
|
||||
- `Makelore Agents|AI 智能体`:配置个人云智能体并试聊,发布后正式对话、接入个人微信或创建应用 API Key。支持流式回复、审批、排队请求、附件与产物、自动任务和活动历史;重新进入时恢复最近智能体与对话,未确认操作可按原输入重试。聊天可提出日程建议,由创建者核对能力、时间和费用后启用。知识库上传、替换及导入本人会话附件后自动解析并建立索引;文档卡片区分排队、解析、索引、可用和失败,持续刷新已加载文档,失败可原地重试;零文本内容不会显示为可用,新文档可用后才移除被替换的旧文档。支持删除知识内容;个人 MCP 凭据、Skills、子智能体可自助管理。支持智能体和会话归档、发布版本比较及恢复为草稿。除知识库 embedding 向量化外,模型调用(包括知识处理触发的对话模型调用)仍按原规则由创建者个人词元点数支付,可按智能体设置每次任务和每日上限,费用按时间、来源、应用分页统计;embedding 向量化由平台承担,不计入用户用量、词元点数或智能体费用上限;应用调用者保有自己的内容空间。Main 管理云会话和本机文件,Renderer 通过 Host API 操作。配套服务接入见 [Yuxi MakeLore 说明](https://xerrors.github.io/Yuxi/advanced/makelore-agents.html)。 编辑采用全高桌面分栏,支持拖动调整与展开、独立滚动、小窗口配置/试用切换。首次创建按选用途、说要求、试一试引导,提供科学问答、故事、英语和笔记整理起点;回答方式可多选,特别要求直接编辑,仍以草稿的 system_prompt 保存。新助手预选云端目录的第一个可用模型并明确显示,可自行更换;已有助手不自动换模型。编辑区以“我的要求”和“高级设置”两个 Tab 切换,默认显示基础要求;高级设置包含能力、知识与限制,返回时保留上次分类,切换保留未保存的输入及当前试用。已开始使用的助手在顶部显示“修改已保存,尚未应用”或“已应用最新修改”,并提供显眼的“应用最新修改”按钮;保存只更新草稿,应用沿用同一发布操作,失败重试保留原操作身份。草稿明确保存(Ctrl/⌘+S),保存与切换页面保留当前试用及未发送文字;“保存并重新试用”使用最新保存的要求,保留上次试用供对比,并可复用上次问题,不自动发送。示例问题仅填入输入框;有运行、排队、审批或未确认操作时先处理当前试用。工具确认先展示实际操作说明与参数,完整技术详情可展开。确认“开始使用”会创建仅自己可用的发布版本并进入独立对话,微信渠道与应用接入仍单独配置。费用上限在限制中独立保存。Enter 发送、Shift+Enter 换行,F6 切换面板焦点、Esc 恢复分栏。
|
||||
|
||||
云知识库的 embedding 模型只由平台管理员在 Yuxi 后台维护:在「智能体管理 → 模型供应商」启用供应商,按供应商要求填写有效的服务地址和 API Key,添加并启用 embedding 模型并填写实际向量维度;embedding 向量化费用由平台承担,不计入用户用量、词元点数或智能体费用上限;one-api 不参与 embedding 配置,Works Square 只负责其他应计费模型调用的创建者词元点数账本。客户端区分目录加载中、加载失败与无可用模型,空状态提供管理员配置说明;「刷新知识库与模型」保留未提交名称,未确认的创建请求沿用原操作身份。
|
||||
|
||||
Agents 侧栏的「渠道」统一管理个人微信账号。每个账号由唯一扫码微信身份连接,可以在发布智能体前先扫码,之后选择或更换目标智能体;多个微信账号可以使用同一个已发布智能体,切换目标不需要重新扫码。账号可单独启用、暂停、重新连接或断开,不提供联系人邀请或授权调用者管理。Agent 的「使用与分享」只展示已关联渠道并提供定位入口。选中账号的二级详情保留运行/投递活动、失败文件补发和微信对话;对话可审批、停止、新开一段并保存实际产物,不同账号的会话相互隔离。自动任务可明确选择把结果发送到已经开始对话的微信账号,暂停保留目标;渠道触发的聊天等应计费模型调用仍由智能体创建者支付,费用统计包含微信渠道。渠道需要配套版本的 AgentBus Core/微信 Adapter、Yuxi 和 Works Square,桌面用户无需填写服务凭据。Main 保管操作恢复记录,重开界面不会自动重发绑定或执行请求;验证码不写入恢复记录,已移除的联系人权限操作记录只能丢弃。用户与应用分享不属于微信渠道账号管理范围。
|
||||
Agents 侧栏的「渠道」统一管理个人微信账号。每个账号由唯一扫码微信身份连接,可以在发布智能体前先扫码,之后选择或更换目标智能体;多个微信账号可以使用同一个已发布智能体,切换目标不需要重新扫码。账号可单独启用、暂停、重新连接或断开,不提供联系人邀请或授权调用者管理。Agent 的「使用与接入」展示已关联渠道并提供定位入口,保留应用 API 管理,不再提供指定用户授权和分享链接。选中账号的二级详情保留运行/投递活动、失败文件补发和微信对话;对话可审批、停止、新开一段并保存实际产物,不同账号的会话相互隔离。自动任务可明确选择把结果发送到已经开始对话的微信账号,暂停保留目标;渠道触发的聊天等应计费模型调用仍由智能体创建者支付,费用统计包含微信渠道。渠道需要配套版本的 AgentBus Core/微信 Adapter、Yuxi 和 Works Square,桌面用户无需填写服务凭据。Main 保管操作恢复记录,重开界面不会自动重发绑定或执行请求;验证码不写入恢复记录,已移除的联系人权限操作记录只能丢弃。应用 API 管理独立于微信渠道账号管理。
|
||||
|
||||
- `Makelore Canvas|AI 绘画`:每个设计项目(Workspace)维护一份从创建起就存在的 Living Form。左侧项目栏负责新建、切换和管理 Workspace,并在桌面设计模式下以 256px 宽度常驻展开;中央沿用 AI 编程的安静对话画布、自然消息流和底部悬浮输入器,AI 整理出的制作方案作为对话内的轻量可编辑稿持续更新;桌面端右侧同为 256px 的全高历史作品栏集中展示当前项目的制作记录与生成结果。任务中的已生成图片可通过“放大查看图片”按钮直接打开大图,支持适应窗口、原始尺寸及 Esc 关闭,无需先下载。紧凑窗口通过左侧抽屉访问项目列表,历史记录保留在时间线中。参考图从本地上传后以 `@图片N` 绑定,具体用法只写在创作提示词中。
|
||||
- `Makelore Robot|AI 机器`:管理机器人智能体、设备激活绑定、智能体配置与设备分配;机器人工作台的智能体位于 Robot 全局侧栏,选中后在内容区先查看绑定设备、再查看基础设置,当前智能体通过 URL 参数保持可分享选择;绑定设备时默认先选择“引导配网”或“已有激活码”。在 Windows 与 macOS 的引导路径中,Makelore 可在弹窗内扫描并连接附近开放的 `Xiaozhi-*` 配网热点,失败时仍可通过系统 Wi-Fi 手动连接;后续继续复用机器人现有热点配网页面,不修改固件,也不由 Makelore 接收 Wi-Fi 密码。
|
||||
|
||||
@@ -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="例如:我的个人网站" />
|
||||
|
||||
@@ -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'}>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -267,7 +267,7 @@ test('personal cloud Agent creation, draft save and leave protection in Electron
|
||||
await expect(page.getByLabel('每日上限(词元点数)')).toHaveValue('10.00');
|
||||
await page.getByRole('button', { name: '保存费用上限' }).click();
|
||||
await expect(page.getByRole('button', { name: '保存费用上限' })).toBeDisabled();
|
||||
await page.getByRole('button', { name: '使用与分享', exact: true }).click();
|
||||
await page.getByRole('button', { name: '使用与接入', exact: true }).click();
|
||||
await expect(page.getByLabel('应用名称')).toHaveCount(0);
|
||||
await page.screenshot({ path: testInfo.outputPath('cloud-agent-start-private-use.png') });
|
||||
await page.getByRole('button', { name: '确认开始使用' }).click();
|
||||
@@ -288,6 +288,14 @@ test('personal cloud Agent creation, draft save and leave protection in Electron
|
||||
await expect(page.getByRole('button', { name: '已应用', exact: true })).toBeDisabled();
|
||||
await expect(page.getByText('修改已保存,尚未应用', { exact: true })).toHaveCount(0);
|
||||
await app.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows()[0].setSize(1366, 768));
|
||||
await page.getByRole('button', { name: '使用与接入', exact: true }).click();
|
||||
await expect(page.getByRole('heading', { name: '应用与 API', exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: '打开渠道', exact: true })).toBeVisible();
|
||||
await expect(page.getByLabel('应用名称')).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: '分享给指定用户', exact: true })).toHaveCount(0);
|
||||
await expect(page.getByLabel('查找分享用户')).toHaveCount(0);
|
||||
await expect(page.getByRole('button', { name: '复制分享链接', exact: true })).toHaveCount(0);
|
||||
await page.screenshot({ path: testInfo.outputPath('cloud-agent-access-without-user-sharing.png') });
|
||||
await page.getByRole('button', { name: '对话', exact: true }).click();
|
||||
await page.getByRole('region', { name: '智能体对话', exact: true }).getByLabel('消息', { exact: true }).fill('每天早上八点半给我三个创作方向');
|
||||
await page.getByRole('region', { name: '智能体对话', exact: true }).getByRole('button', { name: '发送', exact: true }).click();
|
||||
|
||||
@@ -118,7 +118,7 @@ it('shares the original failed publication between the header and access page',
|
||||
fireEvent.click(screen.getByRole('button', { name: '应用最新修改', exact: true }));
|
||||
await screen.findByText(/连接中断/);
|
||||
expect(screen.getByRole('button', { name: '重试本次应用' })).toBeEnabled();
|
||||
fireEvent.click(screen.getByRole('button', { name: '使用与分享' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '使用与接入' }));
|
||||
fireEvent.click(await screen.findByRole('button', { name: '重试本次发布' }));
|
||||
await waitFor(() => expect(screen.getByRole('button', { name: '已应用', exact: true })).toBeDisabled());
|
||||
const calls = api.call.mock.calls.filter(([operation]) => operation === 'publish');
|
||||
|
||||
@@ -187,6 +187,33 @@ it('restores a durable question and follows the new resume run', async () => {
|
||||
expect(api.call.mock.calls.find(call => call[0] === 'resume')?.[1].decision).toEqual({ direction: '海洋' });
|
||||
});
|
||||
|
||||
it('keeps channel and application access without offering selected-user sharing', async () => {
|
||||
const base = api.call.getMockImplementation()!;
|
||||
api.call.mockImplementation(async (operation, input) => {
|
||||
if (operation === 'access') return {
|
||||
published_version: 1, enabled: true, share_url: 'niancode://agents/' + slug,
|
||||
versions: [], grants: [{ account_id: 'previously-shared-user', enabled: true }], applications: [],
|
||||
};
|
||||
if (operation === 'costs') return { items: [], next_offset: null, unit: '词元点数', summary: { count: 0, settled_points: '0.00', pending_points: '0.00' } };
|
||||
if (operation === 'createApplication') return { id: 'application-1', name: input.name, enabled: true };
|
||||
return base(operation, input);
|
||||
});
|
||||
render(<CloudAccessPanel slug={slug} revision={2} onPublished={() => undefined} />);
|
||||
expect(await screen.findByRole('heading', { name: '应用与 API' })).toBeVisible();
|
||||
expect(screen.getByRole('button', { name: '打开渠道' })).toBeEnabled();
|
||||
expect(screen.getByRole('button', { name: '停用智能体' })).toBeEnabled();
|
||||
expect(screen.queryByRole('heading', { name: '分享给指定用户' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText('查找分享用户')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: '复制分享链接' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('previously-shared-user')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('niancode://agents/' + slug)).not.toBeInTheDocument();
|
||||
fireEvent.change(screen.getByLabelText('应用名称'), { target: { value: '我的网站' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: '创建应用' }));
|
||||
await screen.findByText('应用已创建,可为它生成 API 凭据');
|
||||
expect(api.call).toHaveBeenCalledWith('createApplication', expect.objectContaining({ slug, name: '我的网站' }));
|
||||
expect(api.call.mock.calls.some(([operation]) => operation === 'users' || operation === 'share')).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps publication and access controls usable when cost history is unavailable', async () => {
|
||||
api.call.mockImplementation(async (operation) => {
|
||||
if (operation === 'access') return { published_version: 1, enabled: true, share_url: 'niancode://agents/' + slug, versions: [], grants: [], applications: [] };
|
||||
|
||||
Reference in New Issue
Block a user