feat(website-demo): SW mock 接口增强与演示默认深色主题
- 模型列表扩充至 5 个(K3/DeepSeek-V4-Pro/Flash/GLM-5.2/MiniMax-M3),补齐 success 字段 - 工作区「当前选中」改为会话态动态组装,修复切换工作区后 toast/高亮拿错工作区 - 演示默认主题从经典配色改为夜间配色 - 缓存版本 bump 至 v19
This commit is contained in:
parent
7fa2715ee3
commit
bd2cf0f56d
@ -56,9 +56,9 @@
|
|||||||
<script>
|
<script>
|
||||||
/* 演示引导:注册 mock SW,等 controllerchange 后换路由、再加载真实前端 */
|
/* 演示引导:注册 mock SW,等 controllerchange 后换路由、再加载真实前端 */
|
||||||
(function () {
|
(function () {
|
||||||
// 固定演示呈现状态:经典配色 + 极简模式 + 快捷窗口自动展开,不受访客浏览器缓存影响
|
// 固定演示呈现状态:夜间配色 + 极简模式 + 快捷窗口自动展开,不受访客浏览器缓存影响
|
||||||
try {
|
try {
|
||||||
localStorage.setItem('agents_ui_theme', 'classic');
|
localStorage.setItem('agents_ui_theme', 'dark');
|
||||||
localStorage.setItem('agents_personalization_experiments', JSON.stringify({ blockDisplayMode: 'minimal' }));
|
localStorage.setItem('agents_personalization_experiments', JSON.stringify({ blockDisplayMode: 'minimal' }));
|
||||||
localStorage.setItem('agents_quick_dock_auto_expand', 'true');
|
localStorage.setItem('agents_quick_dock_auto_expand', 'true');
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|||||||
@ -8,16 +8,41 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const DEMO_PAGE = '/demo.html';
|
const DEMO_PAGE = '/demo.html';
|
||||||
const CACHE = 'astrion-demo-v4';
|
const CACHE = 'astrion-demo-v19';
|
||||||
|
|
||||||
/* ── 静态响应体 ── */
|
/* ── 静态响应体 ── */
|
||||||
|
|
||||||
const MODELS = {
|
const MODELS = {
|
||||||
|
success: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
context_window: 256000, description: 'Kimi-K3', fast_only: false,
|
context_window: 1048576, description: 'Kimi-K3', fast_only: false,
|
||||||
max_output_tokens: 32768, model_key: 'Kimi-K3', multimodal: 'image,video',
|
max_output_tokens: 64000, model_key: 'Kimi-K3', multimodal: 'image,video',
|
||||||
name: 'Kimi-K3', supports_reasoning_effort: false, supports_thinking: true,
|
name: 'Kimi-K3', supports_reasoning_effort: true, supports_thinking: true,
|
||||||
|
thinking_only: false, visible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context_window: 1000000, description: 'DeepSeek-V4-Pro', fast_only: false,
|
||||||
|
max_output_tokens: 384000, model_key: 'DeepSeek-V4-Pro', multimodal: 'none',
|
||||||
|
name: 'DeepSeek-V4-Pro', supports_reasoning_effort: true, supports_thinking: true,
|
||||||
|
thinking_only: false, visible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context_window: 1000000, description: 'DeepSeek-V4-Flash', fast_only: false,
|
||||||
|
max_output_tokens: 384000, model_key: 'DeepSeek-V4-Flash', multimodal: 'none',
|
||||||
|
name: 'DeepSeek-V4-Flash', supports_reasoning_effort: true, supports_thinking: true,
|
||||||
|
thinking_only: false, visible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context_window: 262144, description: 'GLM-5.2', fast_only: false,
|
||||||
|
max_output_tokens: 64072, model_key: 'GLM-5.2', multimodal: 'none',
|
||||||
|
name: 'GLM-5.2', supports_reasoning_effort: true, supports_thinking: true,
|
||||||
|
thinking_only: false, visible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context_window: 1000000, description: 'MiniMax-M3', fast_only: false,
|
||||||
|
max_output_tokens: 131072, model_key: 'MiniMax-M3', multimodal: 'none',
|
||||||
|
name: 'MiniMax-M3', supports_reasoning_effort: false, supports_thinking: true,
|
||||||
thinking_only: false, visible: true
|
thinking_only: false, visible: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -28,18 +53,26 @@ const SESSION_STATUS = {
|
|||||||
session: { host_mode: true, logged_in: true, role: 'admin', run_mode: 'thinking', thinking_mode: true, username: 'host' }
|
session: { host_mode: true, logged_in: true, role: 'admin', run_mode: 'thinking', thinking_mode: true, username: 'host' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const WORKSPACES = {
|
// 工作区列表为静态数据;「当前工作区」是会话态——select 接口会改它,
|
||||||
success: true,
|
// 列表与 select 响应都从它动态组装;否则 current_workspace_id 永远钉死,
|
||||||
data: {
|
// 前端切换后 toast/分组高亮会拿错工作区(实测弹窗恒显示「日常使用」)。
|
||||||
current_workspace_id: 'ws-daily',
|
const WS_LIST = [
|
||||||
default_workspace_id: 'ws-daily',
|
{ workspace_id: 'ws-daily', label: '日常使用', path: '/Users/demo/daily', running_task_count: 0 },
|
||||||
workspaces: [
|
{ workspace_id: 'ws-starraid', label: 'StarRaid', path: '/Users/demo/starraid', running_task_count: 0 },
|
||||||
{ workspace_id: 'ws-daily', label: '日常使用', path: '/Users/demo/daily', is_current: true, running_task_count: 0 },
|
{ workspace_id: 'ws-dev', label: '开发实战', path: '/Users/demo/dev', running_task_count: 0 }
|
||||||
{ workspace_id: 'ws-starraid', label: 'StarRaid', path: '/Users/demo/starraid', is_current: false, running_task_count: 0 },
|
];
|
||||||
{ workspace_id: 'ws-dev', label: '开发实战', path: '/Users/demo/dev', is_current: false, running_task_count: 0 }
|
// 初始与 demo 默认落点(弹幕调研对话所属工作区)一致
|
||||||
]
|
let currentWorkspaceId = 'ws-starraid';
|
||||||
}
|
function buildWorkspaces() {
|
||||||
};
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
current_workspace_id: currentWorkspaceId,
|
||||||
|
default_workspace_id: 'ws-daily',
|
||||||
|
workspaces: WS_LIST.map((w) => ({ ...w, is_current: w.workspace_id === currentWorkspaceId }))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const UI_BLOCKS = {
|
const UI_BLOCKS = {
|
||||||
block_compress_conversation: false, block_conversation_review: false,
|
block_compress_conversation: false, block_conversation_review: false,
|
||||||
@ -52,24 +85,28 @@ const STATUS = {
|
|||||||
success: true,
|
success: true,
|
||||||
version: '2026-08-23',
|
version: '2026-08-23',
|
||||||
host_mode: true,
|
host_mode: true,
|
||||||
|
container: { username: 'demo', mode: 'host', workspace_path: '/Users/demo/daily', running: true },
|
||||||
admin_policy: { disabled_models: [], forced_category_states: {}, ui_blocks: UI_BLOCKS, version: '2026-08-23' }
|
admin_policy: { disabled_models: [], forced_category_states: {}, ui_blocks: UI_BLOCKS, version: '2026-08-23' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const PERSONALIZATION = {
|
const PERSONALIZATION = {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
theme: 'classic',
|
theme: 'dark',
|
||||||
|
tool_intent_enabled: true,
|
||||||
|
group_sidebar_by_workspace: true,
|
||||||
block_display_mode: 'minimal',
|
block_display_mode: 'minimal',
|
||||||
compact_message_display: 'brief',
|
compact_message_display: 'brief',
|
||||||
communication_style: 'auto',
|
communication_style: 'auto',
|
||||||
conversation_continuity: 'medium',
|
conversation_continuity: 'medium',
|
||||||
agents_md_auto_inject: true,
|
agents_md_auto_inject: true,
|
||||||
auto_deep_compress_enabled: true,
|
auto_deep_compress_enabled: true,
|
||||||
|
deep_compress_trigger_tokens: 200000,
|
||||||
auto_generate_title: true,
|
auto_generate_title: true,
|
||||||
auto_shallow_compress_enabled: false
|
auto_shallow_compress_enabled: false
|
||||||
},
|
},
|
||||||
context_compression_settings: {
|
context_compression_settings: {
|
||||||
context_window_tokens: 256000, deep_trigger_tokens: 256000,
|
context_window_tokens: 256000, deep_trigger_tokens: 200000,
|
||||||
shallow_keep_recent_tools: 15, shallow_keep_user_turn_tools: 3,
|
shallow_keep_recent_tools: 15, shallow_keep_user_turn_tools: 3,
|
||||||
shallow_max_replace_per_round: 10, shallow_trigger_tokens: 1000,
|
shallow_max_replace_per_round: 10, shallow_trigger_tokens: 1000,
|
||||||
shallow_trigger_tool_calls_interval: 10
|
shallow_trigger_tool_calls_interval: 10
|
||||||
@ -81,7 +118,14 @@ const PERMISSION_MODE = { success: true, mode: 'unrestricted', options: ['readon
|
|||||||
const EXECUTION_MODE = { success: true, enabled: true, options: ['sandbox', 'direct'], pending_mode: null, state: { default_mode: 'sandbox', mode: 'sandbox' } };
|
const EXECUTION_MODE = { success: true, enabled: true, options: ['sandbox', 'direct'], pending_mode: null, state: { default_mode: 'sandbox', mode: 'sandbox' } };
|
||||||
const NETWORK_PERMISSION = { success: true, enabled: true, mode: 'restricted', options: ['restricted', 'full', 'none'], pending_mode: null };
|
const NETWORK_PERMISSION = { success: true, enabled: true, mode: 'restricted', options: ['restricted', 'full', 'none'], pending_mode: null };
|
||||||
const TUTORIAL = { success: true, data: { applicable: false, should_prompt: false, tutorial_completed: true, username: 'host' } };
|
const TUTORIAL = { success: true, data: { applicable: false, should_prompt: false, tutorial_completed: true, username: 'host' } };
|
||||||
const USAGE = { success: true, data: { quotas: { fast: { count: 0, limit: 200 }, thinking: { count: 0, limit: 200 }, search: { count: 0, limit: 20 } }, role: 'user' } };
|
// 宿主机模式不统计用量:record_model_call/record_search_call 直接 return,quotas count 恒为 0、
|
||||||
|
// window_start/reset_at 为 null(get_quota_snapshot 行为),limit 为配置默认值。勿编造非 0 数字。
|
||||||
|
const USAGE = { success: true, data: { quotas: { fast: { count: 0, limit: 200, window_start: null, reset_at: null }, thinking: { count: 0, limit: 200, window_start: null, reset_at: null }, search: { count: 0, limit: 20, window_start: null, reset_at: null } }, role: 'user' } };
|
||||||
|
|
||||||
|
// ── Token 统计 mock(右下角 token 菜单/抽屉数据源)──
|
||||||
|
// 数据为每条对话的真实 token_statistics,由 build-mocks.cjs 导出为 mock/tokens-<id>.json。
|
||||||
|
// 当前上下文走 /api/conversations/{id}/tokens;统计区走 /token-statistics。上限 200k 由
|
||||||
|
// PERSONALIZATION 的 deep_compress_trigger_tokens 决定(深度压缩阈值)。
|
||||||
const EMPTY_OK = { success: true, data: {} };
|
const EMPTY_OK = { success: true, data: {} };
|
||||||
const EMPTY_LIST = { success: true, data: [] };
|
const EMPTY_LIST = { success: true, data: [] };
|
||||||
const GIT_SUMMARY = { success: true, data: { available: false } };
|
const GIT_SUMMARY = { success: true, data: { available: false } };
|
||||||
@ -128,11 +172,34 @@ self.addEventListener('fetch', (e) => {
|
|||||||
|
|
||||||
if (!url.pathname.startsWith('/api/')) return; // 静态资源放行
|
if (!url.pathname.startsWith('/api/')) return; // 静态资源放行
|
||||||
|
|
||||||
e.respondWith(routeApi(url));
|
e.respondWith(routeApi(url, e.request));
|
||||||
});
|
});
|
||||||
|
|
||||||
async function routeApi(url) {
|
// ── 模拟流式回复:演示站发送消息时,返回固定文案「实际功能请部署后体验」逐字流式吐出 ──
|
||||||
|
// 协议对齐真实后端:POST /api/tasks 创建任务;GET /api/tasks/{id}?from=N 增量拉事件
|
||||||
|
// (text_start → 逐字 text_chunk → text_end → task_complete,status running→succeeded)。
|
||||||
|
// 逐字 100ms(用户拍板):前端轮询周期 250ms,每轮稳定拿 2~3 字,节奏快而匀。
|
||||||
|
const DEMO_REPLY = '实际功能请部署后体验';
|
||||||
|
const DEMO_TASK_EVENT_INTERVAL_MS = 100;
|
||||||
|
let demoTask = null; // { taskId, convId, startTs, events };SW 休眠后丢失,重发即可
|
||||||
|
|
||||||
|
function buildDemoReplyEvents() {
|
||||||
|
const evts = [{ idx: 0, type: 'text_start', data: {} }];
|
||||||
|
Array.from(DEMO_REPLY).forEach((ch, i) => {
|
||||||
|
evts.push({ idx: i + 1, type: 'text_chunk', data: { content: ch } });
|
||||||
|
});
|
||||||
|
evts.push({ idx: DEMO_REPLY.length + 1, type: 'text_end', data: { full_content: DEMO_REPLY } });
|
||||||
|
evts.push({
|
||||||
|
idx: DEMO_REPLY.length + 2,
|
||||||
|
type: 'task_complete',
|
||||||
|
data: { has_running_sub_agents: false, has_running_background_commands: false, has_running_multi_agent: false }
|
||||||
|
});
|
||||||
|
return evts;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function routeApi(url, req) {
|
||||||
const p = url.pathname;
|
const p = url.pathname;
|
||||||
|
const method = req ? req.method : 'GET';
|
||||||
|
|
||||||
// 子智能体活动详情
|
// 子智能体活动详情
|
||||||
const actMatch = p.match(/^\/api\/sub_agents\/(sub_[\w-]+)\/activity$/);
|
const actMatch = p.match(/^\/api\/sub_agents\/(sub_[\w-]+)\/activity$/);
|
||||||
@ -150,21 +217,72 @@ async function routeApi(url) {
|
|||||||
return json({ success: true, data: [] });
|
return json({ success: true, data: [] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 多智能体活跃实例(演示无)
|
// 多智能体活跃实例:猫咪对话(conv_20260902_235153_495)有 4 个实例,其余对话为空。
|
||||||
if (p === '/api/multiagent/active_sub_agents') return json({ success: true, agents: [] });
|
// 这是 QuickDock 子智能体窗口在多智能体对话下的唯一数据源,钉空会让快捷窗口无法展开。
|
||||||
|
if (p === '/api/multiagent/active_sub_agents') {
|
||||||
|
const convId = url.searchParams.get('conversation_id') || '';
|
||||||
|
const r = await fetch(`/mock/ma-agents-${convId}.json`);
|
||||||
|
if (r.ok) return new Response(await r.text(), { headers: { 'Content-Type': 'application/json; charset=utf-8' } });
|
||||||
|
return json({ success: true, agents: [] });
|
||||||
|
}
|
||||||
|
|
||||||
// 待办 / 后台命令(演示对话为空)
|
// 待办 / 后台命令(演示对话为空)
|
||||||
if (p === '/api/todo-list') return json({ success: true, data: null });
|
// 待办列表:有 todo mock 的对话(如猫咪)返回真实内容,否则空——QuickDock 待办窗口数据源
|
||||||
|
if (p === '/api/todo-list') {
|
||||||
|
const convId = url.searchParams.get('conversation_id') || '';
|
||||||
|
if (convId) {
|
||||||
|
const r = await fetch(`/mock/todo-${convId}.json`);
|
||||||
|
if (r.ok) return new Response(await r.text(), { headers: { 'Content-Type': 'application/json; charset=utf-8' } });
|
||||||
|
}
|
||||||
|
return json({ success: true, data: null });
|
||||||
|
}
|
||||||
if (p === '/api/background_commands') return json({ success: true, data: [] });
|
if (p === '/api/background_commands') return json({ success: true, data: [] });
|
||||||
|
|
||||||
// 文件预览(快捷窗口点文件名):返回真实调研报告内容
|
// 文件预览/下载(对话内文件卡片、下载链接)
|
||||||
if (p === '/api/file/content') {
|
if (p === '/api/file/content') {
|
||||||
const name = (url.searchParams.get('path') || '').split('/').pop();
|
const name = (url.searchParams.get('path') || '').split('/').pop();
|
||||||
|
// 二进制/富文本资源放 mock/files/(按扩展名给 MIME)
|
||||||
|
const ext = (name.split('.').pop() || '').toLowerCase();
|
||||||
|
const MIME = {
|
||||||
|
pdf: 'application/pdf',
|
||||||
|
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
|
html: 'text/html; charset=utf-8',
|
||||||
|
png: 'image/png',
|
||||||
|
jpg: 'image/jpeg',
|
||||||
|
};
|
||||||
|
if (MIME[ext]) {
|
||||||
|
const rb = await fetch(`/mock/files/${name}`);
|
||||||
|
if (rb.ok) return new Response(await rb.arrayBuffer(), { headers: { 'Content-Type': MIME[ext] } });
|
||||||
|
}
|
||||||
|
// 文本资源(mock/file-*)
|
||||||
const r = await fetch(`/mock/file-${name}`);
|
const r = await fetch(`/mock/file-${name}`);
|
||||||
if (r.ok) return new Response(await r.text(), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
|
if (r.ok) return new Response(await r.text(), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
|
||||||
return new Response('演示环境没有该文件', { status: 404 });
|
return new Response('演示环境没有该文件', { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 消息内嵌媒体(view_image 截图等):mock/media/{sha}.png
|
||||||
|
const mediaMatch = p.match(/^\/api\/conversations\/media\/(.+)$/);
|
||||||
|
if (mediaMatch) {
|
||||||
|
const id = decodeURIComponent(mediaMatch[1]).replace(/^sha256:/, '');
|
||||||
|
const r = await fetch(`/mock/media/${id}.png`);
|
||||||
|
if (r.ok) return new Response(await r.arrayBuffer(), { headers: { 'Content-Type': 'image/png' } });
|
||||||
|
return new Response('演示环境没有该媒体', { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对话 token:当前上下文 / 统计区(均读真实导出的 mock/tokens-<id>.json)
|
||||||
|
const tokensMatch = p.match(/^\/api\/conversations\/(conv_[\w-]+)\/tokens$/);
|
||||||
|
if (tokensMatch) {
|
||||||
|
const r = await fetch(`/mock/tokens-${tokensMatch[1]}.json`);
|
||||||
|
const d = r.ok ? await r.json() : null;
|
||||||
|
return json({ success: true, data: { total_tokens: (d && d.current_context_tokens) || 0 } });
|
||||||
|
}
|
||||||
|
const tokenStatsMatch = p.match(/^\/api\/conversations\/(conv_[\w-]+)\/token-statistics$/);
|
||||||
|
if (tokenStatsMatch) {
|
||||||
|
const r = await fetch(`/mock/tokens-${tokenStatsMatch[1]}.json`);
|
||||||
|
if (r.ok) return json({ success: true, data: await r.json() });
|
||||||
|
return json({ success: true, data: { total_input_tokens: 0, total_output_tokens: 0, total_tokens: 0, total_cached_input_tokens: 0, cache_exempt_input_tokens: 0, current_context_tokens: 0 } });
|
||||||
|
}
|
||||||
|
|
||||||
// 对话 bootstrap
|
// 对话 bootstrap
|
||||||
const bootMatch = p.match(/^\/api\/conversations\/(conv_[\w-]+)\/bootstrap$/);
|
const bootMatch = p.match(/^\/api\/conversations\/(conv_[\w-]+)\/bootstrap$/);
|
||||||
if (bootMatch) {
|
if (bootMatch) {
|
||||||
@ -173,18 +291,41 @@ async function routeApi(url) {
|
|||||||
return json({ success: false, error: '对话不存在' }, 404);
|
return json({ success: false, error: '对话不存在' }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对话列表
|
// 版本控制状态:必须显式 mock!前端 fetchVersioningStatus 会用 payload.host_mode
|
||||||
|
// 覆盖全局 versioningHostMode;若落到兜底 EMPTY_OK(data:{}),host_mode=undefined
|
||||||
|
// 会把 host 模式打回 false,分组视图立刻消失且 persist 进 localStorage 持续作恶。
|
||||||
|
const versioningMatch = p.match(/^\/api\/conversations\/(conv_[\w-]+)\/versioning$/);
|
||||||
|
if (versioningMatch) {
|
||||||
|
return json({ success: true, data: { host_mode: true, enabled: false, mode: 'overwrite' } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对话列表(支持 workspace_id 过滤:按 WORKSPACES 的 path 匹配对话 project_path)
|
||||||
if (p === '/api/conversations') {
|
if (p === '/api/conversations') {
|
||||||
const ma = url.searchParams.get('multi_agent_mode') === '1';
|
const ma = url.searchParams.get('multi_agent_mode') === '1';
|
||||||
const r = await fetch(`/mock/conversations-${ma ? 'ma' : 'normal'}.json`);
|
const r = await fetch(`/mock/conversations-${ma ? 'ma' : 'normal'}.json`);
|
||||||
return new Response(await r.text(), { headers: { 'Content-Type': 'application/json; charset=utf-8' } });
|
const wsId = url.searchParams.get('workspace_id');
|
||||||
|
if (!wsId) {
|
||||||
|
return new Response(await r.text(), { headers: { 'Content-Type': 'application/json; charset=utf-8' } });
|
||||||
|
}
|
||||||
|
const all = await r.json();
|
||||||
|
const ws = WS_LIST.find((w) => w.workspace_id === wsId);
|
||||||
|
const list = (all.data.conversations || []).filter((c) => !ws || c.project_path === ws.path);
|
||||||
|
return json({ success: true, data: { conversations: list, has_more: false, limit: 50, offset: 0, total: list.length } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 各状态端点
|
// 各状态端点
|
||||||
if (p === '/api/session-status') return json(SESSION_STATUS);
|
if (p === '/api/session-status') return json(SESSION_STATUS);
|
||||||
if (p === '/api/csrf-token') return json({ success: true, csrf_token: 'demo' });
|
if (p === '/api/csrf-token') return json({ success: true, csrf_token: 'demo' });
|
||||||
if (p === '/api/v1/models') return json(MODELS);
|
if (p === '/api/v1/models') return json(MODELS);
|
||||||
if (p === '/api/host/workspaces') return json(WORKSPACES);
|
if (p === '/api/host/workspaces/select') {
|
||||||
|
const target = url.searchParams.get('workspace_id') || '';
|
||||||
|
const ws = WS_LIST.find((w) => w.workspace_id === target);
|
||||||
|
if (ws) currentWorkspaceId = ws.workspace_id;
|
||||||
|
// 故意不返回 project_path:前端 toast 取值顺序是 project_path || 工作区名 || id,
|
||||||
|
// 演示区路径(/Users/demo/xxx)没有展示价值,缺省后 toast 显示工作区名称。
|
||||||
|
return json({ success: true, data: { current_workspace_id: currentWorkspaceId, default_workspace_id: 'ws-daily', workspace_label: ws ? ws.label : '' } });
|
||||||
|
}
|
||||||
|
if (p === '/api/host/workspaces') return json(buildWorkspaces());
|
||||||
if (p === '/api/status') return json(STATUS);
|
if (p === '/api/status') return json(STATUS);
|
||||||
if (p === '/api/personalization') return json(PERSONALIZATION);
|
if (p === '/api/personalization') return json(PERSONALIZATION);
|
||||||
if (p === '/api/work-mode') return json(WORK_MODE);
|
if (p === '/api/work-mode') return json(WORK_MODE);
|
||||||
@ -196,6 +337,41 @@ async function routeApi(url) {
|
|||||||
if (p === '/api/project/git-summary') return json(GIT_SUMMARY);
|
if (p === '/api/project/git-summary') return json(GIT_SUMMARY);
|
||||||
if (p === '/api/health') return json({ status: 'ok' });
|
if (p === '/api/health') return json({ status: 'ok' });
|
||||||
if (p === '/api/focused') return json({});
|
if (p === '/api/focused') return json({});
|
||||||
|
|
||||||
|
// 发送消息 → 创建模拟任务(前端随后按 task_id 轮询事件)
|
||||||
|
if (p === '/api/tasks' && method === 'POST') {
|
||||||
|
const body = await req.json().catch(() => ({}));
|
||||||
|
demoTask = {
|
||||||
|
taskId: `demo_task_${Date.now()}`,
|
||||||
|
convId: String(body.conversation_id || ''),
|
||||||
|
startTs: Date.now(),
|
||||||
|
events: buildDemoReplyEvents(),
|
||||||
|
};
|
||||||
|
return json({ success: true, data: { task_id: demoTask.taskId, status: 'running', created_at: new Date().toISOString() } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟任务轮询:按经过时间逐步放事件,放完后 status=succeeded(前端停止轮询)
|
||||||
|
const demoTaskMatch = p.match(/^\/api\/tasks\/(demo_task_\d+)$/);
|
||||||
|
if (demoTaskMatch) {
|
||||||
|
if (!demoTask || demoTask.taskId !== demoTaskMatch[1]) {
|
||||||
|
return json({ success: false, error: '任务不存在或已过期' }, 404);
|
||||||
|
}
|
||||||
|
const from = Math.max(0, Number(url.searchParams.get('from') || 0));
|
||||||
|
const visible = Math.min(demoTask.events.length, Math.floor((Date.now() - demoTask.startTs) / DEMO_TASK_EVENT_INTERVAL_MS));
|
||||||
|
const done = visible >= demoTask.events.length;
|
||||||
|
return json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
task_id: demoTask.taskId,
|
||||||
|
status: done ? 'succeeded' : 'running',
|
||||||
|
events: demoTask.events.slice(from, visible),
|
||||||
|
next_offset: visible,
|
||||||
|
conversation_id: demoTask.convId,
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (p === '/api/tasks' || p === '/api/terminals') return json(EMPTY_LIST);
|
if (p === '/api/tasks' || p === '/api/terminals') return json(EMPTY_LIST);
|
||||||
if (p === '/api/tool-settings') return json({ success: true, categories: [] });
|
if (p === '/api/tool-settings') return json({ success: true, categories: [] });
|
||||||
if (p === '/api/effective-policy') return json({ success: true, data: { categories: {} } });
|
if (p === '/api/effective-policy') return json({ success: true, data: { categories: {} } });
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user