@aiyiran/myclaw 1.1.136 → 1.1.138

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.
@@ -808,8 +808,14 @@
808
808
  'background: #252536',
809
809
  ].join(';');
810
810
 
811
- // fetch → blob URL,绕过 CSP img-src 限制
811
+ // 远程环境:走 CDN(边缘缓存,速度快,不受本机出口带宽限制)
812
+ // 本地环境:走本地 API(文件尚未同步到 CDN)
813
+ // CSP img-src 限制后续在服务端统一放行,此处不做 blob 绕行
812
814
  var img = document.createElement('img');
815
+ var _imgEnv = detectEnvironment();
816
+ img.src = _imgEnv.remote
817
+ ? previewUrl + '?t=' + Date.now()
818
+ : MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(getWorkspaceId() + '/' + asset.path) + '&t=' + Date.now();
813
819
  img.alt = asset.name || asset.path;
814
820
  img.style.cssText = [
815
821
  'max-width: 100%',
@@ -820,28 +826,13 @@
820
826
  'position: relative',
821
827
  'z-index: 1',
822
828
  ].join(';');
823
-
824
- function showImgError() {
829
+ img.onerror = function () {
825
830
  img.style.display = 'none';
826
831
  var errMsg = document.createElement('div');
827
832
  errMsg.textContent = '加载失败';
828
833
  errMsg.style.cssText = 'color:#666;font-size:13px;font-family:monospace;';
829
834
  imgArea.appendChild(errMsg);
830
- }
831
-
832
- var localImgUrl = MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(getWorkspaceId() + '/' + asset.path) + '&t=' + Date.now();
833
- fetch(localImgUrl)
834
- .then(function (r) {
835
- if (!r.ok) throw new Error('HTTP ' + r.status);
836
- return r.blob();
837
- })
838
- .then(function (blob) {
839
- var blobUrl = URL.createObjectURL(blob);
840
- img.onload = function () { URL.revokeObjectURL(blobUrl); };
841
- img.onerror = showImgError;
842
- img.src = blobUrl;
843
- })
844
- .catch(showImgError);
835
+ };
845
836
 
846
837
  imgArea.appendChild(img);
847
838
  imgBox.appendChild(imgArea);
@@ -1179,11 +1170,17 @@
1179
1170
  var isVideo = VIDEO_EXTS.indexOf(assetExt) !== -1;
1180
1171
 
1181
1172
  if (isVideo) {
1182
- // 视频用 <video> 元素 + 本地 API,支持流式加载和拖拽进度条
1173
+ // <video> 元素支持 range 请求,流式加载,可拖拽进度条,不用 iframe
1174
+ // 远程环境:走 CDN(带宽快,不受本机出口限制)
1175
+ // 本地环境:走本地 API(文件尚未同步到 CDN)
1176
+ // CSP media-src 限制后续在服务端统一放行
1183
1177
  var video = document.createElement('video');
1184
1178
  video.controls = true;
1185
1179
  video.style.cssText = 'flex:1;width:100%;max-height:100%;background:#000;outline:none;';
1186
- video.src = MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(getWorkspaceId() + '/' + asset.path);
1180
+ var _videoEnv = detectEnvironment();
1181
+ video.src = _videoEnv.remote
1182
+ ? previewUrl + '?t=' + Date.now()
1183
+ : MYCLAW_API_BASE + '/api/file?path=' + encodeURIComponent(getWorkspaceId() + '/' + asset.path);
1187
1184
  box.appendChild(video);
1188
1185
  overlay.appendChild(box);
1189
1186
  document.body.appendChild(overlay);
@@ -784,6 +784,211 @@ btn.addEventListener("click", function () {
784
784
  });
785
785
  }
786
786
 
787
+ // ═══ 7.5 右下角"新会话"按钮(支持中文命名) ═══
788
+ //
789
+ // 实现思路(复用原生机制,避免重写会话切换逻辑):
790
+ // 1. 点击我们的按钮 → 弹框输入中文名
791
+ // 2. 程序点击 openclaw 原生"新会话"按钮(藏在 openclaw-app 的 shadow DOM 里),
792
+ // 由它负责"创建 + 无刷新切换"——这套逻辑(zH/BH)是模块私有的,外部无法直接调用
793
+ // 3. 轮询 openclaw-app.sessionKey,等它从旧值变为新会话的 key
794
+ // 4. 通过 openclaw-app.client(WebSocket RPC)发 sessions.patch 把 label 改成中文名
795
+ // 服务端 patch 后会广播 sessions.changed,前端订阅会自动刷新侧栏显示,无需手动刷新
796
+
797
+ var newSessionOpen = false;
798
+
799
+ // 获取 openclaw 根组件(上面挂着 WebSocket client 和 sessionKey)
800
+ function getOpenclawApp() {
801
+ return document.querySelector("openclaw-app");
802
+ }
803
+
804
+ // 在 shadow DOM(含嵌套)里深度查找元素
805
+ function deepQuerySelector(root, selector) {
806
+ if (!root) return null;
807
+ var direct = root.querySelector ? root.querySelector(selector) : null;
808
+ if (direct) return direct;
809
+ var all = root.querySelectorAll ? root.querySelectorAll("*") : [];
810
+ for (var i = 0; i < all.length; i++) {
811
+ if (all[i].shadowRoot) {
812
+ var found = deepQuerySelector(all[i].shadowRoot, selector);
813
+ if (found) return found;
814
+ }
815
+ }
816
+ return null;
817
+ }
818
+
819
+ // 找到原生"新会话"按钮(先 light DOM,再 app 的 shadow DOM 深搜)
820
+ function findNativeNewSessionBtn() {
821
+ var sel = ".sidebar-new-session";
822
+ var el = document.querySelector(sel);
823
+ if (el) return el;
824
+ var app = getOpenclawApp();
825
+ if (app && app.shadowRoot) {
826
+ return deepQuerySelector(app.shadowRoot, sel);
827
+ }
828
+ return null;
829
+ }
830
+
831
+ // 从 sessionKey 解析 agentId(格式 agent:NAME:...),非 agent 会话返回 null
832
+ function deriveAgentId(sessionKey) {
833
+ if (typeof sessionKey === "string" && sessionKey.indexOf("agent:") === 0) {
834
+ var parts = sessionKey.split(":");
835
+ if (parts.length >= 2 && parts[1]) return parts[1];
836
+ }
837
+ return null;
838
+ }
839
+
840
+ // 轮询等待 sessionKey 变化(原生按钮创建+切换后,key 会变成新会话)
841
+ function waitForSessionChange(app, prevKey, timeoutMs) {
842
+ return new Promise(function (resolve) {
843
+ var start = Date.now();
844
+ (function poll() {
845
+ if (app.sessionKey && app.sessionKey !== prevKey) { resolve(app.sessionKey); return; }
846
+ if (Date.now() - start > timeoutMs) { resolve(null); return; }
847
+ setTimeout(poll, 80);
848
+ })();
849
+ });
850
+ }
851
+
852
+ // 创建一个带中文名的新会话
853
+ function createNamedSession(label) {
854
+ var app = getOpenclawApp();
855
+ if (!app) { showToast("未找到 openclaw 应用", "error"); return; }
856
+ if (!app.client) { showToast("网关未连接", "error"); return; }
857
+
858
+ var nativeBtn = findNativeNewSessionBtn();
859
+ if (!nativeBtn) { showToast("未找到原生新会话按钮", "error"); return; }
860
+ if (nativeBtn.disabled) { showToast("当前无法新建(有进行中的任务?)", "error"); return; }
861
+
862
+ var prevKey = app.sessionKey;
863
+ showToast("正在创建:" + label);
864
+
865
+ // 由原生按钮负责创建 + 切换
866
+ nativeBtn.click();
867
+
868
+ waitForSessionChange(app, prevKey, 8000).then(function (newKey) {
869
+ if (!newKey) { showToast("新会话创建超时", "error"); return; }
870
+ // 改名
871
+ var params = { key: newKey, label: label };
872
+ var agentId = deriveAgentId(newKey);
873
+ if (agentId) params.agentId = agentId;
874
+ app.client.request("sessions.patch", params).then(function () {
875
+ showToast("已创建:" + label, "success");
876
+ console.log("[myclaw-newsession] 创建并命名成功:", newKey, label);
877
+ }).catch(function (err) {
878
+ showToast("改名失败:" + (err && err.message ? err.message : err), "error");
879
+ console.error("[myclaw-newsession] sessions.patch 失败:", err);
880
+ });
881
+ });
882
+ }
883
+
884
+ function createNewSessionButton() {
885
+ if (document.querySelector("#myclaw-newsession-btn")) return;
886
+
887
+ var btn = document.createElement("div");
888
+ btn.id = "myclaw-newsession-btn";
889
+ btn.style.cssText = [
890
+ "position: fixed",
891
+ "bottom: 8px",
892
+ "right: 335px",
893
+ "padding: 3px 10px",
894
+ "background: rgba(139, 92, 246, 0.85)",
895
+ "color: #fff",
896
+ "font-size: 11px",
897
+ "font-weight: bold",
898
+ "font-family: monospace",
899
+ "border-radius: 4px",
900
+ "z-index: 99999",
901
+ "user-select: none",
902
+ "cursor: pointer",
903
+ "transition: all 0.2s",
904
+ "letter-spacing: 0.5px",
905
+ ].join(";");
906
+ btn.textContent = "🆕 新会话";
907
+ btn.title = "创建新会话(可中文命名)";
908
+
909
+ btn.onmouseenter = function () { btn.style.background = "rgba(124, 58, 237, 1)"; btn.style.transform = "scale(1.05)"; };
910
+ btn.onmouseleave = function () { btn.style.background = "rgba(139, 92, 246, 0.85)"; btn.style.transform = "scale(1)"; };
911
+ btn.onclick = function (e) {
912
+ e.stopPropagation();
913
+ if (newSessionOpen) { closeNewSessionModal(); } else { openNewSessionModal(); }
914
+ };
915
+
916
+ document.body.appendChild(btn);
917
+ }
918
+
919
+ function closeNewSessionModal() {
920
+ newSessionOpen = false;
921
+ var m = document.querySelector("#myclaw-newsession-modal");
922
+ if (m) m.remove();
923
+ }
924
+
925
+ function openNewSessionModal() {
926
+ if (document.querySelector("#myclaw-newsession-modal")) return;
927
+ newSessionOpen = true;
928
+
929
+ var overlay = document.createElement("div");
930
+ overlay.id = "myclaw-newsession-modal";
931
+ overlay.style.cssText = [
932
+ "position: fixed", "top: 0", "left: 0", "width: 100vw", "height: 100vh",
933
+ "background: rgba(0,0,0,0.4)", "z-index: 99998",
934
+ "display: flex", "align-items: center", "justify-content: center",
935
+ "animation: myclaw-fade-in 0.15s ease",
936
+ ].join(";");
937
+ overlay.onclick = function (e) { if (e.target === overlay) closeNewSessionModal(); };
938
+
939
+ var box = document.createElement("div");
940
+ box.style.cssText = "width:360px;background:#1e1e2e;border-radius:8px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,0.5);";
941
+
942
+ var header = document.createElement("div");
943
+ header.style.cssText = "display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:#2d2d3f;color:#cdd6f4;font-size:13px;font-family:monospace;user-select:none;";
944
+ header.innerHTML = "<span>🆕 新会话</span>";
945
+ var closeBtn = document.createElement("span");
946
+ closeBtn.textContent = "✕";
947
+ closeBtn.style.cssText = "cursor:pointer;padding:2px 6px;border-radius:3px;font-size:14px;transition:background 0.15s;";
948
+ closeBtn.onmouseenter = function () { closeBtn.style.background = "rgba(255,255,255,0.1)"; };
949
+ closeBtn.onmouseleave = function () { closeBtn.style.background = "none"; };
950
+ closeBtn.onclick = function () { closeNewSessionModal(); };
951
+ header.appendChild(closeBtn);
952
+
953
+ var body = document.createElement("div");
954
+ body.style.cssText = "padding:16px;display:flex;flex-direction:column;gap:10px;";
955
+
956
+ var desc = document.createElement("div");
957
+ desc.textContent = "给新会话起个名字(支持中文),点击后自动创建并切换";
958
+ desc.style.cssText = "font-size:11px;color:#888;font-family:monospace;";
959
+ body.appendChild(desc);
960
+
961
+ var input = document.createElement("input");
962
+ input.type = "text";
963
+ input.placeholder = "如:期末复习计划";
964
+ input.style.cssText = "padding:8px 10px;background:#252536;border:1px solid #3d3d5c;border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;outline:none;";
965
+ input.onfocus = function () { input.style.borderColor = "#8b5cf6"; };
966
+ input.onblur = function () { input.style.borderColor = "#3d3d5c"; };
967
+ body.appendChild(input);
968
+
969
+ var submitBtn = document.createElement("button");
970
+ submitBtn.textContent = "创建";
971
+ submitBtn.style.cssText = "padding:8px 16px;background:#8b5cf6;border:none;border-radius:4px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;transition:opacity 0.15s;";
972
+ submitBtn.onmouseenter = function () { submitBtn.style.opacity = "0.8"; };
973
+ submitBtn.onmouseleave = function () { submitBtn.style.opacity = "1"; };
974
+
975
+ function doSubmit() {
976
+ var val = input.value.trim();
977
+ if (!val) { input.style.borderColor = "#ff4444"; input.focus(); return; }
978
+ closeNewSessionModal();
979
+ createNamedSession(val);
980
+ }
981
+ submitBtn.onclick = doSubmit;
982
+ input.onkeydown = function (e) { if (e.key === "Enter") doSubmit(); };
983
+ body.appendChild(submitBtn);
984
+
985
+ box.appendChild(header);
986
+ box.appendChild(body);
987
+ overlay.appendChild(box);
988
+ document.body.appendChild(overlay);
989
+ setTimeout(function () { input.focus(); }, 100);
990
+ }
991
+
787
992
  // ═══ 8. 右下角"命令"按钮 ═══
788
993
 
789
994
  var commandOpen = false;
@@ -1566,6 +1771,7 @@ btn.addEventListener("click", function () {
1566
1771
  createDocButton();
1567
1772
  createCmdButton();
1568
1773
  createCommandButton();
1774
+ createNewSessionButton();
1569
1775
  injectStyles();
1570
1776
 
1571
1777
  // 初始化 VoiceInput SDK
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.1.136",
3
+ "version": "1.1.138",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
package/patches/patch.js CHANGED
@@ -113,6 +113,19 @@ function getMyclawVersion() {
113
113
  }
114
114
  }
115
115
 
116
+ /**
117
+ * 获取已安装的 OpenClaw 版本号
118
+ * uiDir 形如 <pkg>/dist/control-ui,包根目录为 uiDir/../..
119
+ */
120
+ function getOpenclawVersion(uiDir) {
121
+ try {
122
+ const pkgRoot = path.resolve(uiDir, '..', '..');
123
+ return require(path.join(pkgRoot, 'package.json')).version;
124
+ } catch {
125
+ return 'unknown';
126
+ }
127
+ }
128
+
116
129
  /**
117
130
  * 执行注入
118
131
  */
@@ -250,6 +263,13 @@ function patch() {
250
263
  let patched = false;
251
264
  let foundTarget = false;
252
265
 
266
+ // img-src / media-src 放宽仅针对特定 OpenClaw 版本(2026.6.9)。
267
+ // 该版本 CSP 中 img-src/media-src 为 'self' data: blob:,会拦截 CDN 图片/视频;
268
+ // 限定版本可避免误改其它版本(CSP 结构可能不同,盲目放宽有风险)。
269
+ const ocVersion = getOpenclawVersion(uiDir);
270
+ const needsMediaCspFix = ocVersion === '2026.6.9';
271
+ console.log('[myclaw-patch] → OpenClaw 版本: ' + ocVersion + (needsMediaCspFix ? ' (将额外放宽 img-src/media-src)' : ''));
272
+
253
273
  for (const f of distFiles) {
254
274
  // 兼容多版本:gateway-cli-*.js(旧)、server-*.js(中)、control-ui-*.js(新)
255
275
  const isTarget = (f.startsWith('gateway-cli-') || f.startsWith('server-') || f.startsWith('control-ui-')) && f.endsWith('.js');
@@ -264,7 +284,9 @@ function patch() {
264
284
  const needsFrameSrc = !content.includes('"frame-src *');
265
285
  const needsConnectSrc = !content.includes("connect-src *");
266
286
  const needsFrameAncestors = !content.includes("frame-ancestors *");
267
- const needsAnyCspPatch = needsFrameSrc || needsConnectSrc || needsFrameAncestors;
287
+ const needsImgSrc = needsMediaCspFix && !content.includes('"img-src *');
288
+ const needsMediaSrc = needsMediaCspFix && !content.includes('"media-src *');
289
+ const needsAnyCspPatch = needsFrameSrc || needsConnectSrc || needsFrameAncestors || needsImgSrc || needsMediaSrc;
268
290
 
269
291
  if (needsMicrophonePatch || needsAnyCspPatch) {
270
292
  // 备份
@@ -306,6 +328,16 @@ function patch() {
306
328
  content = content.replace(/\"frame-ancestors[^"]*\"/g, '"frame-ancestors *"');
307
329
  cspPatches.push('frame-ancestors *');
308
330
  }
331
+ // img-src → 允许所有(仅 2026.6.9,放行 CDN 图片)
332
+ if (needsImgSrc) {
333
+ content = content.replace(/\"img-src[^"]*\"/g, '"img-src *"');
334
+ cspPatches.push('img-src *');
335
+ }
336
+ // media-src → 允许所有(仅 2026.6.9,放行 CDN 视频)
337
+ if (needsMediaSrc) {
338
+ content = content.replace(/\"media-src[^"]*\"/g, '"media-src *"');
339
+ cspPatches.push('media-src *');
340
+ }
309
341
  if (cspPatches.length > 0) {
310
342
  console.log('[myclaw-patch] ✅ 已修复 CSP (' + cspPatches.join(', ') + '): ' + f);
311
343
  }