astrion-website/demo/sw.js
JOJO 64d0a377dc feat: 04版本控制动画 + 演示区5条真实对话mock + 文档同步沙箱重构
- 主页第4行特色动画(版本控制)及 01/02/03 动画迭代
- 演示区对话 mock 全部换为真实对话转换:日常使用(汽车对比新/整理周会速记)、
  StarRaid(弹幕调研/像素飞机设计)、开发实战(猫咪咖啡店多智能体)
- 子智能体 activity、消息截图媒体、文件卡片资源(docx/pdf/html)随站点发布
- sw.js: file/content 二进制 MIME、/api/conversations/media 路由、
  模型列表扩为 K3/V4-Flash/MiniMax-M3/GLM-5.2、CACHE v5
- content/ 同步 8-31 沙箱重构文档更新
2026-09-03 07:47:39 +08:00

249 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Astrion 官网演示 —— Service WorkerAPI 静态 mock
*
* 真实前端构建产物原样运行,本 SW 拦截数据层:
* - /api/* → mock/*.json 静态响应
* - /socket.io/* → 503前端自动降级 REST 轮询)
* - 导航请求 → demo.html支撑 history 路由刷新)
* - 其余 → 放行
*/
const DEMO_PAGE = '/demo.html';
const CACHE = 'astrion-demo-v5';
/* ── 静态响应体 ── */
const MODELS = {
items: [
{
context_window: 1048576, description: 'Kimi K3月之暗面最新旗舰模型', fast_only: false,
max_output_tokens: 64000, model_key: 'Kimi-K3', multimodal: 'image,video',
name: 'Kimi-K3', 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: 1000000, description: 'MiniMax M3MiniMax最新旗舰模型', 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
},
{
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
}
]
};
const SESSION_STATUS = {
success: true,
session: { host_mode: true, logged_in: true, role: 'admin', run_mode: 'thinking', thinking_mode: true, username: 'host' }
};
const WORKSPACES = {
success: true,
data: {
current_workspace_id: 'ws-daily',
default_workspace_id: 'ws-daily',
workspaces: [
{ workspace_id: 'ws-daily', label: '日常使用', path: '/Users/demo/daily', is_current: true, 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 }
]
}
};
const UI_BLOCKS = {
block_compress_conversation: false, block_conversation_review: false,
block_file_manager: false, block_focus_panel: false, block_personal_space: false,
block_realtime_terminal: false, block_token_panel: false, block_tool_toggle: false,
block_upload: false, block_virtual_monitor: false, collapse_workspace: false
};
const STATUS = {
success: true,
version: '2026-08-23',
host_mode: true,
admin_policy: { disabled_models: [], forced_category_states: {}, ui_blocks: UI_BLOCKS, version: '2026-08-23' }
};
const PERSONALIZATION = {
success: true,
data: {
theme: 'classic',
block_display_mode: 'minimal',
compact_message_display: 'brief',
communication_style: 'auto',
conversation_continuity: 'medium',
agents_md_auto_inject: true,
auto_deep_compress_enabled: true,
auto_generate_title: true,
auto_shallow_compress_enabled: false
},
context_compression_settings: {
context_window_tokens: 256000, deep_trigger_tokens: 256000,
shallow_keep_recent_tools: 15, shallow_keep_user_turn_tools: 3,
shallow_max_replace_per_round: 10, shallow_trigger_tokens: 1000,
shallow_trigger_tool_calls_interval: 10
}
};
const WORK_MODE = { success: true, mode: 'execute', options: ['plan', 'ask', 'execute'], permission_mode: 'unrestricted', execution_mode: 'sandbox' };
const PERMISSION_MODE = { success: true, mode: 'unrestricted', options: ['readonly', 'approval', 'auto_approval', 'unrestricted'], pending_mode: null };
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 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' } };
const EMPTY_OK = { success: true, data: {} };
const EMPTY_LIST = { success: true, data: [] };
const GIT_SUMMARY = { success: true, data: { available: false } };
function json(body, status = 200) {
return new Response(JSON.stringify(body), {
status,
headers: { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store' }
});
}
/* ── 安装:预缓存演示页 ── */
self.addEventListener('install', (e) => {
e.waitUntil(caches.open(CACHE).then((c) => c.add(DEMO_PAGE)).then(() => self.skipWaiting()));
});
self.addEventListener('activate', (e) => {
e.waitUntil(
caches.keys()
.then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
.then(() => self.clients.claim())
);
});
/* ── 请求路由 ── */
self.addEventListener('fetch', (e) => {
const url = new URL(e.request.url);
if (url.origin !== self.location.origin) return;
// 导航请求:仅前端对话路由(/new、/<conv_id>)回退到演示页;真实文件放行
if (e.request.mode === 'navigate') {
const isAppRoute = /^\/(?:conv_)?\d{8}_\d{6}_\d{3}$/.test(url.pathname) || url.pathname === '/new';
if (isAppRoute) {
e.respondWith(caches.match(DEMO_PAGE).then((r) => r || fetch(DEMO_PAGE)));
}
return;
}
// socket.io → 断连(前端降级 REST 轮询)
if (url.pathname.startsWith('/socket.io/')) {
e.respondWith(new Response('demo: realtime disabled', { status: 503 }));
return;
}
if (!url.pathname.startsWith('/api/')) return; // 静态资源放行
e.respondWith(routeApi(url));
});
async function routeApi(url) {
const p = url.pathname;
// 子智能体活动详情
const actMatch = p.match(/^\/api\/sub_agents\/(sub_[\w-]+)\/activity$/);
if (actMatch) {
const r = await fetch(`/mock/activity-${actMatch[1]}.json`);
if (r.ok) return new Response(await r.text(), { headers: { 'Content-Type': 'application/json; charset=utf-8' } });
return json({ success: true, data: { status: 'completed', entries: [] } });
}
// 子智能体列表
if (p === '/api/sub_agents') {
const convId = url.searchParams.get('conversation_id') || '';
const r = await fetch(`/mock/sub-agents-${convId}.json`);
if (r.ok) return new Response(await r.text(), { headers: { 'Content-Type': 'application/json; charset=utf-8' } });
return json({ success: true, data: [] });
}
// 多智能体活跃实例(演示无)
if (p === '/api/multiagent/active_sub_agents') return json({ success: true, agents: [] });
// 待办 / 后台命令(演示对话为空)
if (p === '/api/todo-list') return json({ success: true, data: null });
if (p === '/api/background_commands') return json({ success: true, data: [] });
// 文件预览/下载(对话内文件卡片、下载链接)
if (p === '/api/file/content') {
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}`);
if (r.ok) return new Response(await r.text(), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } });
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 });
}
// 对话 bootstrap
const bootMatch = p.match(/^\/api\/conversations\/(conv_[\w-]+)\/bootstrap$/);
if (bootMatch) {
const r = await fetch(`/mock/bootstrap-${bootMatch[1]}.json`);
if (r.ok) return new Response(await r.text(), { headers: { 'Content-Type': 'application/json; charset=utf-8' } });
return json({ success: false, error: '对话不存在' }, 404);
}
// 对话列表
if (p === '/api/conversations') {
const ma = url.searchParams.get('multi_agent_mode') === '1';
const r = await fetch(`/mock/conversations-${ma ? 'ma' : 'normal'}.json`);
return new Response(await r.text(), { headers: { 'Content-Type': 'application/json; charset=utf-8' } });
}
// 各状态端点
if (p === '/api/session-status') return json(SESSION_STATUS);
if (p === '/api/csrf-token') return json({ success: true, csrf_token: 'demo' });
if (p === '/api/v1/models') return json(MODELS);
if (p === '/api/host/workspaces') return json(WORKSPACES);
if (p === '/api/status') return json(STATUS);
if (p === '/api/personalization') return json(PERSONALIZATION);
if (p === '/api/work-mode') return json(WORK_MODE);
if (p === '/api/permission-mode') return json(PERMISSION_MODE);
if (p === '/api/execution-mode') return json(EXECUTION_MODE);
if (p === '/api/network-permission') return json(NETWORK_PERMISSION);
if (p === '/api/tutorial-status') return json(TUTORIAL);
if (p === '/api/usage') return json(USAGE);
if (p === '/api/project/git-summary') return json(GIT_SUMMARY);
if (p === '/api/health') return json({ status: 'ok' });
if (p === '/api/focused') return json({});
if (p === '/api/tasks' || p === '/api/terminals') return json(EMPTY_LIST);
if (p === '/api/tool-settings') return json({ success: true, categories: [] });
if (p === '/api/effective-policy') return json({ success: true, data: { categories: {} } });
if (p === '/api/project-storage') return json(EMPTY_OK);
if (p === '/api/input-draft') return json(EMPTY_OK);
// 兜底
return json(EMPTY_OK);
}