@xcanwin/manyoyo 7.0.26 → 7.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/web/server.js CHANGED
@@ -102,36 +102,6 @@ const DEFAULT_WEB_CONFIG_TEMPLATE = `{
102
102
  }
103
103
  `;
104
104
 
105
- let XTERM_JS_FILE = null;
106
- let XTERM_CSS_FILE = null;
107
- let XTERM_ADDON_FIT_JS_FILE = null;
108
- let MARKED_MIN_JS_FILE = null;
109
- try {
110
- const xtermPackageDir = path.dirname(require.resolve('@xterm/xterm/package.json'));
111
- XTERM_JS_FILE = path.join(xtermPackageDir, 'lib', 'xterm.js');
112
- XTERM_CSS_FILE = path.join(xtermPackageDir, 'css', 'xterm.css');
113
- } catch (e) {
114
- XTERM_JS_FILE = null;
115
- XTERM_CSS_FILE = null;
116
- }
117
- try {
118
- const xtermAddonFitPackageDir = path.dirname(require.resolve('@xterm/addon-fit/package.json'));
119
- XTERM_ADDON_FIT_JS_FILE = path.join(xtermAddonFitPackageDir, 'lib', 'addon-fit.js');
120
- } catch (e) {
121
- XTERM_ADDON_FIT_JS_FILE = null;
122
- }
123
- try {
124
- const markedPackageDir = path.dirname(require.resolve('marked/package.json'));
125
- MARKED_MIN_JS_FILE = path.join(markedPackageDir, 'lib', 'marked.umd.js');
126
- } catch (e) {
127
- MARKED_MIN_JS_FILE = null;
128
- }
129
-
130
- const MIME_TYPES = {
131
- '.css': 'text/css; charset=utf-8',
132
- '.js': 'application/javascript; charset=utf-8',
133
- '.html': 'text/html; charset=utf-8'
134
- };
135
105
  const FILE_LANGUAGE_MAP = {
136
106
  '.cjs': 'javascript',
137
107
  '.css': 'css',
@@ -4189,49 +4159,6 @@ function resolveStaticAsset(name) {
4189
4159
  return fs.existsSync(fullPath) ? fullPath : null;
4190
4160
  }
4191
4161
 
4192
- function resolveVendorAsset(name) {
4193
- if (!isSafeStaticAssetName(name)) {
4194
- return null;
4195
- }
4196
- if (name === 'xterm.js') {
4197
- return XTERM_JS_FILE && fs.existsSync(XTERM_JS_FILE) ? XTERM_JS_FILE : null;
4198
- }
4199
- if (name === 'xterm.css') {
4200
- return XTERM_CSS_FILE && fs.existsSync(XTERM_CSS_FILE) ? XTERM_CSS_FILE : null;
4201
- }
4202
- if (name === 'xterm-addon-fit.js') {
4203
- return XTERM_ADDON_FIT_JS_FILE && fs.existsSync(XTERM_ADDON_FIT_JS_FILE) ? XTERM_ADDON_FIT_JS_FILE : null;
4204
- }
4205
- if (name === 'marked.min.js') {
4206
- return MARKED_MIN_JS_FILE && fs.existsSync(MARKED_MIN_JS_FILE) ? MARKED_MIN_JS_FILE : null;
4207
- }
4208
- return null;
4209
- }
4210
-
4211
- function sendFileAsset(res, filePath) {
4212
- if (!filePath || !fs.existsSync(filePath)) {
4213
- sendHtml(res, 404, '<h1>404 Not Found</h1>');
4214
- return;
4215
- }
4216
-
4217
- const ext = path.extname(filePath).toLowerCase();
4218
- const contentType = MIME_TYPES[ext] || 'application/octet-stream';
4219
- const content = fs.readFileSync(filePath);
4220
- res.writeHead(200, {
4221
- 'Content-Type': contentType,
4222
- 'Cache-Control': 'no-store'
4223
- });
4224
- res.end(content);
4225
- }
4226
-
4227
- function sendStaticAsset(res, assetName) {
4228
- sendFileAsset(res, resolveStaticAsset(assetName));
4229
- }
4230
-
4231
- function sendVendorAsset(res, assetName) {
4232
- sendFileAsset(res, resolveVendorAsset(assetName));
4233
- }
4234
-
4235
4162
  function loadTemplate(name) {
4236
4163
  const filePath = resolveStaticAsset(name);
4237
4164
  if (!filePath) {
@@ -4247,42 +4174,20 @@ function escapeHtmlText(value) {
4247
4174
  .replace(/>/g, '&gt;');
4248
4175
  }
4249
4176
 
4250
- // JSON.stringify 不转义 "</script>",且作为 String.replace 的第二个参数时 $&/$'/$` 等会被
4251
- // 当成特殊替换模式重新解释;用替换函数 + 转义 "<" 双重防护,避免 serveTitle 里的内容
4252
- // 提前闭合 <script> 标签或拼接进原始 HTML。
4253
- function jsonForInlineScript(value) {
4254
- return JSON.stringify(value).replace(/</g, '\\u003c');
4255
- }
4256
-
4257
4177
  function applyServeTitle(html, ctx) {
4258
4178
  if (typeof ctx.serveTitle !== 'string') {
4259
4179
  return html;
4260
4180
  }
4261
- // 正则匹配任意 <title> 内容而非硬编码 "MANYOYO Web",兼容 app.html/shadcn.html 各自的默认标题
4181
+ // 正则匹配任意 <title> 内容而非硬编码 "MANYOYO Web",前端换了默认标题也不用改这里
4262
4182
  return html.replace(/<title>[^<]*<\/title>/, () => `<title>${escapeHtmlText(ctx.serveTitle)}</title>`);
4263
4183
  }
4264
4184
 
4265
- function injectServeTitleFlag(html, ctx) {
4266
- if (typeof ctx.serveTitle !== 'string') {
4267
- return html;
4268
- }
4269
- // 告知 app.js 跳过按会话动态改写 document.title
4270
- return html.replace(
4271
- '</head>',
4272
- () => ` <script>window.__MANYOYO_SERVE_TITLE__ = ${jsonForInlineScript(ctx.serveTitle)};</script>\n</head>`
4273
- );
4274
- }
4275
-
4276
- function renderIndexHtml(ctx) {
4277
- return injectServeTitleFlag(applyServeTitle(loadTemplate('app.html'), ctx), ctx);
4278
- }
4279
-
4280
4185
  function renderShadcnHtml(ctx) {
4281
4186
  // shadcn.html 是 vite singlefile 打包产物,全部依赖内联成一个 <script>,
4282
- // 用 .replace('</head>', ...) 有极小概率命中某个库源码里字面量出现的
4283
- // "</head>",把注入脚本插进内联脚本中间导致整页解析错乱;所以不复用
4284
- // injectServeTitleFlag 那个 </head> 定位方式,改成跟 applyServeTitle 同款
4285
- // 更窄、更安全的 <title> 定位,插一个 meta 标记告知前端跳过动态改写 document.title
4187
+ // 用 .replace('</head>', ...) 注入标记有极小概率命中某个库源码里字面量出现的
4188
+ // "</head>",把注入内容插进内联脚本中间导致整页解析错乱;所以改成跟
4189
+ // applyServeTitle 同款更窄、更安全的 <title> 定位,插一个 meta 标记
4190
+ // 告知前端跳过动态改写 document.title
4286
4191
  let html = applyServeTitle(loadTemplate('shadcn.html'), ctx);
4287
4192
  if (typeof ctx.serveTitle === 'string') {
4288
4193
  html = html.replace(
@@ -4293,10 +4198,6 @@ function renderShadcnHtml(ctx) {
4293
4198
  return html;
4294
4199
  }
4295
4200
 
4296
- function renderLoginHtml(ctx) {
4297
- return applyServeTitle(loadTemplate('login.html'), ctx);
4298
- }
4299
-
4300
4201
  function toPositiveInt(value, fallback) {
4301
4202
  const parsed = Number.parseInt(value, 10);
4302
4203
  if (!Number.isFinite(parsed) || parsed <= 0) {
@@ -4521,27 +4422,16 @@ async function handleWebAuthRoutes(req, res, pathname, ctx, state) {
4521
4422
  return true;
4522
4423
  }
4523
4424
 
4524
- // 旧版默认主题(现为 /legacy 兜底路由)的登录页
4525
- if (req.method === 'GET' && pathname === '/auth/login') {
4526
- sendHtml(res, 200, renderLoginHtml(ctx));
4527
- return true;
4528
- }
4529
-
4530
- // 默认主题(shadcn/ui 版)的登录页:与 / 、/shadcn 复用同一份构建产物,
4531
- // 由前端按 pathname 自行渲染登录表单,登录逻辑仍复用 /auth/login 接口
4425
+ // 登录页:与 / 、/shadcn 复用同一份构建产物,由前端按 pathname 自行渲染
4426
+ // 登录表单,登录逻辑走下面的 POST /auth/login 接口
4532
4427
  if (req.method === 'GET' && pathname === '/shadcn/auth/login') {
4533
4428
  sendHtml(res, 200, applyServeTitle(loadTemplate('shadcn.html'), ctx));
4534
4429
  return true;
4535
4430
  }
4536
4431
 
4537
- const authFrontendMatch = pathname.match(/^\/auth\/frontend\/([A-Za-z0-9._-]+)$/);
4538
- if (req.method === 'GET' && authFrontendMatch) {
4539
- const assetName = authFrontendMatch[1];
4540
- if (!(assetName === 'login.css' || assetName === 'login.js')) {
4541
- sendHtml(res, 404, '<h1>404 Not Found</h1>');
4542
- return true;
4543
- }
4544
- sendStaticAsset(res, assetName);
4432
+ // GET /auth/login 曾经是独立的登录页面,现在只留一个跳转,接住老书签
4433
+ if (req.method === 'GET' && pathname === '/auth/login') {
4434
+ sendRedirect(res, 302, '/shadcn/auth/login');
4545
4435
  return true;
4546
4436
  }
4547
4437
 
@@ -4586,25 +4476,14 @@ async function handleWebAuthRoutes(req, res, pathname, ctx, state) {
4586
4476
  return false;
4587
4477
  }
4588
4478
 
4589
- function sendWebUnauthorized(res, pathname, ctx) {
4479
+ function sendWebUnauthorized(res, pathname) {
4590
4480
  if (pathname.startsWith('/api/') || pathname.startsWith('/auth/')) {
4591
4481
  sendJson(res, 401, { error: 'UNAUTHORIZED' });
4592
4482
  return;
4593
4483
  }
4594
- if (pathname === '' || pathname === '/' || pathname === '/shadcn') {
4595
- sendRedirect(res, 302, '/shadcn/auth/login', { 'Set-Cookie': getWebAuthClearCookie() });
4596
- return;
4597
- }
4598
- if (pathname === '/legacy') {
4599
- sendRedirect(res, 302, '/auth/login', { 'Set-Cookie': getWebAuthClearCookie() });
4600
- return;
4601
- }
4602
- sendHtml(
4603
- res,
4604
- 401,
4605
- renderLoginHtml(ctx),
4606
- { 'Set-Cookie': getWebAuthClearCookie() }
4607
- );
4484
+ // 其余都是页面请求:一律打回登录页。登录页本身在认证网关之前就被
4485
+ // handleWebAuthRoutes 放行,不会在这里形成重定向循环
4486
+ sendRedirect(res, 302, '/shadcn/auth/login', { 'Set-Cookie': getWebAuthClearCookie() });
4608
4487
  }
4609
4488
 
4610
4489
  // 运行日志查看接口的支撑函数。日志目录以 serve 实际使用的 logger.path 为准,
@@ -5945,7 +5824,7 @@ async function startWebServer(options) {
5945
5824
 
5946
5825
  const authSession = getWebAuthSession(state, req);
5947
5826
  if (!authSession) {
5948
- sendWebUnauthorized(res, pathname, ctx);
5827
+ sendWebUnauthorized(res, pathname);
5949
5828
  return;
5950
5829
  }
5951
5830
 
@@ -5961,40 +5840,6 @@ async function startWebServer(options) {
5961
5840
  return;
5962
5841
  }
5963
5842
 
5964
- // 旧版默认主题:不再是默认路由,移到 /legacy 继续保留一段时间作为兜底
5965
- if (req.method === 'GET' && pathname === '/legacy') {
5966
- sendHtml(res, 200, renderIndexHtml(ctx));
5967
- return;
5968
- }
5969
-
5970
- const appFrontendMatch = pathname.match(/^\/app\/frontend\/([A-Za-z0-9._-]+)$/);
5971
- if (req.method === 'GET' && appFrontendMatch) {
5972
- const assetName = appFrontendMatch[1];
5973
- if (!(assetName === 'app.css'
5974
- || assetName === 'app.js'
5975
- || assetName === 'markdown.css'
5976
- || assetName === 'markdown-renderer.js'
5977
- || assetName === 'chat-behavior.js'
5978
- || assetName === 'file-browser.js'
5979
- || assetName === 'codemirror.bundle.js')) {
5980
- sendHtml(res, 404, '<h1>404 Not Found</h1>');
5981
- return;
5982
- }
5983
- sendStaticAsset(res, assetName);
5984
- return;
5985
- }
5986
-
5987
- const appVendorMatch = pathname.match(/^\/app\/vendor\/([A-Za-z0-9._-]+)$/);
5988
- if (req.method === 'GET' && appVendorMatch) {
5989
- const assetName = appVendorMatch[1];
5990
- if (!(assetName === 'xterm.css' || assetName === 'xterm.js' || assetName === 'xterm-addon-fit.js' || assetName === 'marked.min.js')) {
5991
- sendHtml(res, 404, '<h1>404 Not Found</h1>');
5992
- return;
5993
- }
5994
- sendVendorAsset(res, assetName);
5995
- return;
5996
- }
5997
-
5998
5843
  if (pathname === '/healthz') {
5999
5844
  sendJson(res, 200, { ok: true });
6000
5845
  return;
@@ -6130,7 +5975,7 @@ async function startWebServer(options) {
6130
5975
  const { GREEN, CYAN, YELLOW, NC } = ctx.colors;
6131
5976
  const listenHost = formatUrlHost(ctx.serverHost);
6132
5977
  console.log(`${GREEN}✅ MANYOYO Web 服务已启动: http://${listenHost}:${listenPort}${NC}`);
6133
- console.log(`${CYAN}提示: 左侧是 manyoyo 容器会话列表,中间是活动/终端/配置/检查工作台,右侧显示当前会话上下文。${NC}`);
5978
+ console.log(`${CYAN}提示: 左侧是 manyoyo 容器与 AGENT 列表,右侧是聊天/终端/文件/详情/配置/检查工作台。${NC}`);
6134
5979
  if (ctx.serverHost === '0.0.0.0') {
6135
5980
  console.log(`${CYAN}提示: 当前监听全部网卡,请用本机局域网 IP 访问。${NC}`);
6136
5981
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "7.0.26",
3
+ "version": "7.1.2",
4
4
  "imageVersion": "1.9.1-common",
5
5
  "playwrightCliVersion": "0.1.18",
6
6
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",
@@ -31,12 +31,10 @@
31
31
  },
32
32
  "scripts": {
33
33
  "dev:release": "node scripts/dev-release.js",
34
- "build:web-editor": "node scripts/build-web-code-editor.js",
35
34
  "build:web-shadcn": "node scripts/build-web-shadcn.js",
36
35
  "dev:web-shadcn": "node scripts/dev-web-shadcn.js",
37
36
  "test:web-shadcn": "node scripts/test-web-shadcn.js",
38
- "prepack": "npm run build:web-editor && npm run build:web-shadcn",
39
- "prepare": "npm run build:web-editor",
37
+ "prepack": "npm run build:web-shadcn",
40
38
  "install-link": "npm link",
41
39
  "test": "jest --coverage && npm run test:web-shadcn",
42
40
  "test:unit": "jest test/ && npm run test:web-shadcn",
@@ -63,29 +61,12 @@
63
61
  ],
64
62
  "dependencies": {
65
63
  "@playwright/mcp": "0.0.79",
66
- "@xterm/addon-fit": "^0.11.0",
67
- "@xterm/xterm": "^6.0.0",
68
64
  "commander": "^15.0.0",
69
65
  "json5": "^2.2.3",
70
- "marked": "^18.0.9",
71
66
  "playwright": "1.62.1",
72
67
  "ws": "^8.21.3"
73
68
  },
74
69
  "devDependencies": {
75
- "@codemirror/commands": "^6.10.4",
76
- "@codemirror/lang-css": "^6.3.1",
77
- "@codemirror/lang-html": "^6.4.12",
78
- "@codemirror/lang-javascript": "^6.2.5",
79
- "@codemirror/lang-json": "^6.0.2",
80
- "@codemirror/lang-markdown": "^6.5.2",
81
- "@codemirror/lang-python": "^6.2.1",
82
- "@codemirror/lang-yaml": "^6.1.3",
83
- "@codemirror/language": "^6.12.4",
84
- "@codemirror/search": "^6.7.1",
85
- "@codemirror/state": "^6.7.1",
86
- "@codemirror/view": "^6.43.8",
87
- "codemirror": "^6.0.2",
88
- "esbuild": "^0.28.2",
89
70
  "jest": "^30.4.2",
90
71
  "vitepress": "^1.6.4"
91
72
  },