myagent-ai 1.23.15 → 1.23.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myagent-ai",
3
- "version": "1.23.15",
3
+ "version": "1.23.17",
4
4
  "description": "本地桌面端执行型AI助手 - Open Interpreter 风格 | Local Desktop Execution-Oriented AI Assistant",
5
5
  "main": "main.py",
6
6
  "bin": {
package/web/api_server.py CHANGED
@@ -1007,16 +1007,76 @@ class ApiServer:
1007
1007
  </div>
1008
1008
  </div>
1009
1009
 
1010
- <!-- [v1.20.11] noVNC 组件加载:本地优先,CDN 回退 -->
1010
+ <!-- [v1.23.16] noVNC 组件加载:本地优先,CDN 回退 -->
1011
1011
  <script>
1012
- // [v1.20.11] 本地 noVNC 加载器:通过动态 <script> 标签加载 CommonJS 模块
1013
- // 本地 data/novnc/lib/ 中的文件是 CommonJS 格式,不能直接用 import()
1012
+ // [v1.23.16] 本地 noVNC 加载器 支持 ../ 相对路径解析
1014
1013
  async function loadLocalNoVNC() {{
1015
1014
  const basePath = '/vnc/lib/';
1016
- // 加载 noVNC 依赖模块(按依赖顺序)
1015
+ const _modules = {{}};
1016
+
1017
+ // 规范化路径:处理 ./ 和 ../ 相对路径
1018
+ function resolvePath(fromModId, depId) {{
1019
+ if (!depId.startsWith('.')) return depId;
1020
+ // 取 fromModId 所在目录
1021
+ const baseParts = fromModId.split('/');
1022
+ baseParts.pop(); // 去掉文件名,得到目录
1023
+ const depParts = depId.split('/');
1024
+ for (const part of depParts) {{
1025
+ if (part === '.') continue;
1026
+ else if (part === '..') baseParts.pop();
1027
+ else baseParts.push(part);
1028
+ }}
1029
+ return baseParts.join('/');
1030
+ }}
1031
+
1032
+ // 去掉 .js 后缀(模块 ID 不含后缀)
1033
+ function stripJs(p) {{ return p.endsWith('.js') ? p.slice(0, -3) : p; }}
1034
+
1035
+ // 加载并注册单个模块
1036
+ async function loadModule(filePath) {{
1037
+ const modId = stripJs(filePath);
1038
+ if (_modules[modId]) return _modules[modId]; // 已加载
1039
+ const resp = await fetch(basePath + filePath);
1040
+ if (!resp.ok) throw new Error('Failed to fetch ' + filePath + ': ' + resp.status);
1041
+ const code = await resp.text();
1042
+ const modExports = {{}};
1043
+ _modules[modId] = modExports;
1044
+ try {{
1045
+ const fn = new Function('exports', 'require', 'module', code);
1046
+ fn(modExports, function(depId) {{
1047
+ const resolved = stripJs(resolvePath(modId, depId));
1048
+ return _modules[resolved] || {{}};
1049
+ }}, {{ exports: modExports }});
1050
+ }} catch(e) {{
1051
+ console.warn('[noVNC] Module error:', filePath, e);
1052
+ }}
1053
+ return modExports;
1054
+ }}
1055
+
1056
+ // 按依赖顺序加载所有 noVNC 模块
1017
1057
  const depFiles = [
1058
+ // pako vendor (zlib 依赖)
1059
+ 'vendor/pako/lib/utils/common.js',
1060
+ 'vendor/pako/lib/zlib/adler32.js',
1061
+ 'vendor/pako/lib/zlib/crc32.js',
1062
+ 'vendor/pako/lib/zlib/messages.js',
1063
+ 'vendor/pako/lib/zlib/inffast.js',
1064
+ 'vendor/pako/lib/zlib/inftrees.js',
1065
+ 'vendor/pako/lib/zlib/trees.js',
1066
+ 'vendor/pako/lib/zlib/inflate.js',
1067
+ 'vendor/pako/lib/zlib/deflate.js',
1068
+ 'vendor/pako/lib/zlib/zstream.js',
1018
1069
  'vendor/pako/lib/inflate.js',
1019
1070
  'vendor/pako/lib/deflate.js',
1071
+ // noVNC core
1072
+ 'util/logging.js',
1073
+ 'util/events.js',
1074
+ 'util/eventtarget.js',
1075
+ 'util/strings.js',
1076
+ 'util/browser.js',
1077
+ 'util/int.js',
1078
+ 'util/element.js',
1079
+ 'util/cursor.js',
1020
1080
  'base64.js',
1021
1081
  'websock.js',
1022
1082
  'display.js',
@@ -1030,97 +1090,31 @@ async function loadLocalNoVNC() {{
1030
1090
  'decoders/tightpng.js',
1031
1091
  'decoders/zrle.js',
1032
1092
  'decoders/jpeg.js',
1033
- 'util/events.js',
1034
- 'util/eventtarget.js',
1035
- 'util/logging.js',
1036
- 'util/strings.js',
1037
- 'util/browser.js',
1038
- 'util/int.js',
1039
- 'util/element.js',
1040
- 'util/cursor.js',
1093
+ 'encodings.js',
1094
+ 'input/keysymdef.js',
1041
1095
  'input/keysym.js',
1042
1096
  'input/xtscancodes.js',
1043
- 'input/util.js',
1044
- 'input/domkeytable.js',
1045
1097
  'input/vkeys.js',
1098
+ 'input/fixedkeys.js',
1099
+ 'input/domkeytable.js',
1100
+ 'input/util.js',
1046
1101
  'input/keyboard.js',
1047
1102
  'input/gesturehandler.js',
1048
- 'encodings.js',
1103
+ // crypto (rfb.js → ra2.js → crypto/)
1104
+ 'crypto/bigint.js',
1105
+ 'crypto/dh.js',
1106
+ 'crypto/md5.js',
1107
+ 'crypto/des.js',
1108
+ 'crypto/aes.js',
1109
+ 'crypto/rsa.js',
1110
+ 'crypto/crypto.js',
1111
+ 'ra2.js',
1049
1112
  ];
1050
- // 加载所有依赖文件为全局变量
1051
1113
  for (const file of depFiles) {{
1052
- const resp = await fetch(basePath + file);
1053
- if (!resp.ok) throw new Error('Failed to fetch ' + file + ': ' + resp.status);
1054
- const code = await resp.text();
1055
- // 使用 Function 构造器执行 CommonJS 代码,注入 require 和 exports
1056
- const _modules = window.__novnc_modules = window.__novnc_modules || {{}};
1057
- const modId = file.replace('.js', '');
1058
- const modExports = {{}};
1059
- _modules[modId] = modExports;
1060
- try {{
1061
- const fn = new Function('exports', 'require', 'module', code);
1062
- fn(modExports, function(depId) {{
1063
- // 解析相对路径
1064
- let resolved = depId;
1065
- if (depId.startsWith('./')) {{
1066
- resolved = (modId.substring(0, modId.lastIndexOf('/') + 1) || '') + depId.substring(2);
1067
- }}
1068
- return _modules[resolved] || {{}};
1069
- }}, {{ exports: modExports }});
1070
- }} catch(e) {{
1071
- console.warn('[noVNC] Failed to load module:', file, e);
1072
- }}
1114
+ await loadModule(file);
1073
1115
  }}
1074
1116
  // 最后加载 rfb.js
1075
- const resp = await fetch(basePath + 'rfb.js');
1076
- if (!resp.ok) throw new Error('Failed to fetch rfb.js: ' + resp.status);
1077
- const code = await resp.text();
1078
- const _modules = window.__novnc_modules || {{}};
1079
- const rfbExports = {{}};
1080
- _modules['rfb'] = rfbExports;
1081
- const fn = new Function('exports', 'require', 'module', code);
1082
- fn(rfbExports, function(depId) {{
1083
- let resolved = depId;
1084
- if (depId.startsWith('./')) {{
1085
- resolved = 'rfb/' + depId.substring(2);
1086
- // 映射到实际模块路径
1087
- const pathMap = {{
1088
- './util/int': 'util/int',
1089
- './util/logging': 'util/logging',
1090
- './util/strings': 'util/strings',
1091
- './util/browser': 'util/browser',
1092
- './util/element': 'util/element',
1093
- './util/events': 'util/events',
1094
- './util/eventtarget': 'util/eventtarget',
1095
- './display': 'display',
1096
- './inflator': 'inflator',
1097
- './deflator': 'deflator',
1098
- './websock': 'websock',
1099
- './decoders/copyrect': 'decoders/copyrect',
1100
- './decoders/raw': 'decoders/raw',
1101
- './decoders/rre': 'decoders/rre',
1102
- './decoders/hextile': 'decoders/hextile',
1103
- './decoders/tight': 'decoders/tight',
1104
- './decoders/tightpng': 'decoders/tightpng',
1105
- './decoders/zrle': 'decoders/zrle',
1106
- './decoders/jpeg': 'decoders/jpeg',
1107
- './encodings': 'encodings',
1108
- './input/keysym': 'input/keysym',
1109
- './input/keyboard': 'input/keyboard',
1110
- './input/xtscancodes': 'input/xtscancodes',
1111
- './input/util': 'input/util',
1112
- './input/domkeytable': 'input/domkeytable',
1113
- './input/vkeys': 'input/vkeys',
1114
- './input/gesturehandler': 'input/gesturehandler',
1115
- './base64': 'base64',
1116
- }};
1117
- if (depId.startsWith('./')) {{
1118
- const mapped = pathMap[depId];
1119
- if (mapped) resolved = mapped;
1120
- }}
1121
- }}
1122
- return _modules[resolved] || {{}};
1123
- }}, {{ exports: rfbExports }});
1117
+ const rfbExports = await loadModule('rfb.js');
1124
1118
  return rfbExports.default || rfbExports;
1125
1119
  }}
1126
1120
  </script>
@@ -1129,34 +1123,16 @@ try {{
1129
1123
  let RFB = null;
1130
1124
  let loadError = '';
1131
1125
 
1132
- // [v1.20.11] 优先从本地服务器加载 noVNC(data/novnc/lib/)
1126
+ // [v1.23.16] 从本地服务器加载 noVNC(data/novnc/lib/)
1133
1127
  try {{
1134
1128
  RFB = await loadLocalNoVNC();
1135
1129
  }} catch (localErr) {{
1136
1130
  loadError = localErr.message;
1137
- console.warn('[VNC] 本地加载失败,尝试 CDN:', localErr.message);
1138
- // 本地加载失败,尝试 CDN 回退(多源容错)
1139
- const CDN_SOURCES = [
1140
- 'https://cdn.jsdelivr.net/npm/@novnc/novnc@1.5.0/core/rfb.js',
1141
- 'https://fastly.jsdelivr.net/npm/@novnc/novnc@1.5.0/core/rfb.js',
1142
- 'https://unpkg.com/@novnc/novnc@1.5.0/core/rfb.js',
1143
- 'https://registry.npmmirror.com/@novnc/novnc/1.5.0/files/core/rfb.js',
1144
- 'https://cdn.staticfile.net/novnc/novnc/1.5.0/files/core/rfb.js',
1145
- ];
1146
- for (const src of CDN_SOURCES) {{
1147
- try {{
1148
- const mod = await import(src);
1149
- RFB = mod.default || mod.RFB;
1150
- if (RFB) break;
1151
- }} catch (e) {{
1152
- loadError = e.message;
1153
- continue;
1154
- }}
1155
- }}
1131
+ console.error('[VNC] 本地 noVNC 加载失败:', localErr);
1156
1132
  }}
1157
1133
 
1158
1134
  if (!RFB) {{
1159
- throw new Error('noVNC 加载失败: ' + loadError);
1135
+ throw new Error('noVNC 本地加载失败: ' + loadError);
1160
1136
  }}
1161
1137
 
1162
1138
  initVNC(RFB);
@@ -1166,7 +1142,8 @@ try {{
1166
1142
  document.getElementById('statusTitle').textContent = 'VNC 组件加载失败';
1167
1143
  document.getElementById('statusMsg').innerHTML =
1168
1144
  'noVNC 组件加载失败: ' + e.message + '<br><br>' +
1169
- '请确保 myagent/data/novnc/lib/ 目录存在。';
1145
+ '请确保 myagent/data/novnc/lib/ 目录存在。<br>' +
1146
+ '在服务器上运行: myagent-ai reinstall';
1170
1147
  document.getElementById('statusActions').style.display = '';
1171
1148
  console.error('[VNC] 组件加载失败:', e);
1172
1149
  }}
@@ -606,6 +606,47 @@ function updateStreamingMessage(msgIdx) {
606
606
  msg._lastStreamRenderedLen = 0;
607
607
  }
608
608
 
609
+ // ── [v1.23.15] 增量渲染文件卡片(v2_file 事件到达时立即显示) ──
610
+ if (msg._files && msg._files.length > 0) {
611
+ var existingFiles = bubbleWrapper ? bubbleWrapper.querySelectorAll(':scope > .msg-attachments') : contentArea.querySelectorAll(':scope > .msg-attachments');
612
+ var fileContainer = existingFiles.length > 0 ? existingFiles[0] : null;
613
+ if (!fileContainer) {
614
+ fileContainer = document.createElement('div');
615
+ fileContainer.className = 'msg-attachments msg-attachments-files';
616
+ if (bubbleWrapper) {
617
+ bubbleWrapper.appendChild(fileContainer);
618
+ } else {
619
+ contentArea.appendChild(fileContainer);
620
+ }
621
+ }
622
+ // 只添加未渲染的新文件
623
+ var renderedIds = fileContainer._renderedFileIds || [];
624
+ for (var _fIdx = 0; _fIdx < msg._files.length; _fIdx++) {
625
+ var _f = msg._files[_fIdx];
626
+ var _fId = _f.id || _f.file_id || '';
627
+ if (renderedIds.indexOf(_fId) >= 0) continue;
628
+ var _isImg = _f.type && _f.type.indexOf('image/') === 0;
629
+ var _isAud = _f.type && _f.type.indexOf('audio/') === 0;
630
+ var _isVid = _f.type && _f.type.indexOf('video/') === 0;
631
+ if (_isImg || _isAud || _isVid) { renderedIds.push(_fId); continue; }
632
+ var _icon = _getFileIcon(_f.name || _f.type || '');
633
+ var _sizeStr = _f.size ? formatFileSize(_f.size) : '';
634
+ var _fDiv = document.createElement('div');
635
+ _fDiv.className = 'msg-file-item agent-file';
636
+ _fDiv.title = '点击预览';
637
+ _fDiv.innerHTML = '<span class="msg-file-icon">' + _icon + '</span>' +
638
+ '<span class="msg-file-info"><span class="msg-file-name">' + escapeHtml(_f.name) + '</span>' +
639
+ (_sizeStr ? '<span class="msg-file-size">' + _sizeStr + '</span>' : '') +
640
+ '</span>' +
641
+ '<span class="msg-file-actions">' +
642
+ '<a class="msg-file-download" href="/api/file/' + (_fId || '') + '?name=' + encodeURIComponent(_f.name || 'file') + '" download="' + escapeHtml(_f.name) + '" title="下载" onclick="event.stopPropagation()">⬇</a>' +
643
+ '</span>';
644
+ fileContainer.appendChild(_fDiv);
645
+ renderedIds.push(_fId);
646
+ }
647
+ fileContainer._renderedFileIds = renderedIds;
648
+ }
649
+
609
650
  // Remove exec events panel if present (events are now inline in timeline)
610
651
  const execPanel = contentArea.querySelector('.exec-events-panel');
611
652
  if (execPanel) execPanel.remove();
@@ -1747,6 +1788,10 @@ async function sendMessage(opts) {
1747
1788
  // Flush reasoning text BEFORE tool call to create speak→tool pattern
1748
1789
  flushV2Reasoning();
1749
1790
  flushCurrentText();
1791
+ // [v1.23.15] 清除 _streamingText,防止推理文本在 msgParts(文本段) 和
1792
+ // streaming-segment 中同时渲染导致重复显示
1793
+ state.messages[msgIdx]._streamingText = '';
1794
+ state.messages[msgIdx]._v2Reasoning = '';
1750
1795
  var toolEvent = {
1751
1796
  type: 'v2_tool',
1752
1797
  data: {