@aiyiran/myclaw 1.0.242 → 1.0.244
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/assets/myclaw-artifacts.js +139 -37
- package/assets/myclaw-inject.js +218 -1
- package/index.js +75 -5
- package/package.json +1 -1
- package/patches/patch.js +20 -22
- package/server/sync_workspace.py +138 -0
- package/skills/yiran-skill-media/SKILL.md +44 -13
- package/skills/yiran-skill-media/config.json +36 -9
- package/skills/yiran-skill-media/scripts/generate.py +43 -8
- package/skills/yiran-skill-media/scripts/i2v.sh +47 -0
- package/skills/yiran-skill-media/scripts/providers/__init__.py +8 -0
- package/skills/yiran-skill-media/scripts/providers/jimeng_image.py +75 -0
- package/skills/yiran-skill-media/scripts/providers/jimeng_video.py +115 -0
- package/skills/yiran-skill-media/scripts/providers/minimax_video.py +115 -0
- package/skills/yiran-skill-media/scripts/video.sh +47 -0
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
var panelVisible = false;
|
|
31
31
|
var cachedData = null;
|
|
32
32
|
var pollTimer = null;
|
|
33
|
+
var cachedConfig = null; // { claw, base_url }
|
|
34
|
+
var envInfo = null; // { remote: bool, clawName: string|null }
|
|
35
|
+
var MYCLAW_API_PORT = 18800;
|
|
36
|
+
var MYCLAW_API_BASE = 'http://127.0.0.1:' + MYCLAW_API_PORT;
|
|
33
37
|
|
|
34
38
|
// ═══ 工具:从 URL 解析 agent 名称 ═══
|
|
35
39
|
function getAgentName() {
|
|
@@ -47,18 +51,82 @@
|
|
|
47
51
|
return '';
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
// ═══
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
// ═══ 环境检测 ═══
|
|
55
|
+
// 判断当前是远程服务器还是本地环境
|
|
56
|
+
// 远程:https://claw.yiranlaoshi.com → { remote: true, clawName: 'clawdev' }
|
|
57
|
+
// 远程:https://claw3.kekouen.cn → { remote: true, clawName: 'claw3' }
|
|
58
|
+
// 本地:http://127.0.0.1:18789 → { remote: false, clawName: null }
|
|
59
|
+
function detectEnvironment() {
|
|
60
|
+
var hostname = window.location.hostname;
|
|
61
|
+
|
|
62
|
+
// claw.yiranlaoshi.com → 特殊映射:clawdev
|
|
63
|
+
if (hostname === 'claw.yiranlaoshi.com') {
|
|
64
|
+
return { remote: true, clawName: 'clawdev' };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// *.kekouen.cn → 子域名即 clawName
|
|
68
|
+
if (hostname.endsWith('.kekouen.cn')) {
|
|
69
|
+
var clawName = hostname.split('.')[0];
|
|
70
|
+
return { remote: true, clawName: clawName };
|
|
55
71
|
}
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
|
|
73
|
+
// 本地或无法判定
|
|
74
|
+
return { remote: false, clawName: null };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ═══ 初始化配置 ═══
|
|
78
|
+
// 远程环境直接用 hostname 中的 clawName
|
|
79
|
+
// 本地环境从 sync_workspace.py 的 HTTP API 获取
|
|
80
|
+
function initConfig() {
|
|
81
|
+
envInfo = detectEnvironment();
|
|
82
|
+
|
|
83
|
+
// 解析 agentName 和 workspaceName(与环境无关,纯粹从 URL 参数获取)
|
|
84
|
+
var agent = getAgentName() || 'main';
|
|
85
|
+
var workspace = agent === 'main' ? 'workspace' : 'workspace-' + agent;
|
|
86
|
+
|
|
87
|
+
if (envInfo.remote) {
|
|
88
|
+
// 远程:直接拿到 clawName,不需要请求
|
|
89
|
+
cachedConfig = {
|
|
90
|
+
claw: envInfo.clawName,
|
|
91
|
+
base_url: 'https://cdn.yiranlaoshi.com/' + envInfo.clawName,
|
|
92
|
+
agentName: agent,
|
|
93
|
+
workspaceName: workspace,
|
|
94
|
+
};
|
|
95
|
+
console.log('[myclaw-artifacts] ✅ 远程环境:', envInfo.clawName, '| agent:', agent, '| workspace:', workspace);
|
|
96
|
+
return Promise.resolve(cachedConfig);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 本地:从 API 获取 claw 配置
|
|
100
|
+
return fetch(MYCLAW_API_BASE + '/api/config')
|
|
101
|
+
.then(function (res) {
|
|
102
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
103
|
+
return res.json();
|
|
104
|
+
})
|
|
105
|
+
.then(function (data) {
|
|
106
|
+
data.agentName = agent;
|
|
107
|
+
data.workspaceName = workspace;
|
|
108
|
+
cachedConfig = data;
|
|
109
|
+
console.log('[myclaw-artifacts] ✅ 本地环境 | claw:', data.claw, '| agent:', agent, '| workspace:', workspace);
|
|
110
|
+
return data;
|
|
111
|
+
})
|
|
112
|
+
.catch(function (err) {
|
|
113
|
+
// API 不可用时仍保留 agent/workspace 信息
|
|
114
|
+
cachedConfig = { claw: null, base_url: null, agentName: agent, workspaceName: workspace };
|
|
115
|
+
console.warn('[myclaw-artifacts] ⚠ 本地 API 不可用:', err.message);
|
|
116
|
+
return cachedConfig;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ═══ 构建预览 URL ═══
|
|
121
|
+
// 统一走 CDN,clawName 和 workspaceName 由 cachedConfig 提供
|
|
122
|
+
function buildPreviewUrl(data, assetPath) {
|
|
123
|
+
var clawName = cachedConfig ? cachedConfig.claw : null;
|
|
124
|
+
var wsPrefix = cachedConfig ? cachedConfig.workspaceName : null;
|
|
125
|
+
if (!clawName || !wsPrefix) {
|
|
126
|
+
console.error('[myclaw-artifacts] ❌ 配置未就绪,无法构建预览链接');
|
|
58
127
|
return null;
|
|
59
128
|
}
|
|
60
|
-
|
|
61
|
-
return 'https://cdn.yiranlaoshi.com/' + claw + '/' + wsName + '/' + assetPath;
|
|
129
|
+
return 'https://cdn.yiranlaoshi.com/' + clawName + '/' + wsPrefix + '/' + assetPath;
|
|
62
130
|
}
|
|
63
131
|
|
|
64
132
|
// ═══ 创建按钮 ═══
|
|
@@ -197,20 +265,45 @@
|
|
|
197
265
|
}
|
|
198
266
|
|
|
199
267
|
// ═══ 请求数据 ═══
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
var wsPrefix = agentName === 'main' ? 'workspace' : 'workspace-' + agentName;
|
|
203
|
-
return window.location.origin + '/cmd/api/preview?path=' + wsPrefix + '/.myclaw/__MY_ARTIFACTS__.json';
|
|
268
|
+
function getWorkspaceId() {
|
|
269
|
+
return cachedConfig ? cachedConfig.workspaceName : 'workspace';
|
|
204
270
|
}
|
|
205
271
|
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
-
if (!url) return;
|
|
209
|
-
fetch(url)
|
|
272
|
+
function fetchArtifactsFromLocalAPI(wsPrefix) {
|
|
273
|
+
return fetch(MYCLAW_API_BASE + '/api/artifacts?workspace=' + encodeURIComponent(wsPrefix))
|
|
210
274
|
.then(function (res) {
|
|
211
275
|
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
212
276
|
return res.json();
|
|
213
|
-
})
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function fetchArtifactsFromCDN(wsPrefix) {
|
|
281
|
+
var clawName = cachedConfig ? cachedConfig.claw : null;
|
|
282
|
+
if (!clawName) return Promise.reject(new Error('no claw name'));
|
|
283
|
+
var url = 'https://cdn.yiranlaoshi.com/' + clawName + '/' + wsPrefix + '/.myclaw/__MY_ARTIFACTS__.json?t=' + Date.now();
|
|
284
|
+
return fetch(url).then(function (res) {
|
|
285
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
286
|
+
return res.text();
|
|
287
|
+
}).then(function (text) {
|
|
288
|
+
if (text.trim().indexOf('<') === 0) throw new Error('Not JSON');
|
|
289
|
+
return JSON.parse(text);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function fetchArtifacts(contentEl) {
|
|
294
|
+
var wsPrefix = getWorkspaceId();
|
|
295
|
+
var fetcher;
|
|
296
|
+
|
|
297
|
+
if (envInfo && envInfo.remote) {
|
|
298
|
+
// 远程环境 → 走 CDN
|
|
299
|
+
fetcher = fetchArtifactsFromCDN(wsPrefix);
|
|
300
|
+
} else {
|
|
301
|
+
// 本地环境 → 优先本地 API,失败降级 CDN
|
|
302
|
+
fetcher = fetchArtifactsFromLocalAPI(wsPrefix)
|
|
303
|
+
.catch(function () { return fetchArtifactsFromCDN(wsPrefix); });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
fetcher
|
|
214
307
|
.then(function (data) {
|
|
215
308
|
cachedData = data;
|
|
216
309
|
if (!contentEl) return;
|
|
@@ -220,10 +313,9 @@
|
|
|
220
313
|
}
|
|
221
314
|
renderArtifactsList(contentEl, data);
|
|
222
315
|
})
|
|
223
|
-
.catch(function (
|
|
224
|
-
console.error('[myclaw-artifacts] 加载失败:', err);
|
|
316
|
+
.catch(function () {
|
|
225
317
|
if (contentEl) {
|
|
226
|
-
contentEl.innerHTML = '<div style="text-align:center;padding:32px;color:#
|
|
318
|
+
contentEl.innerHTML = '<div style="text-align:center;padding:32px;color:#888;">暂无作品</div>';
|
|
227
319
|
}
|
|
228
320
|
});
|
|
229
321
|
}
|
|
@@ -618,15 +710,12 @@
|
|
|
618
710
|
titleInput.style.borderColor = '#ff4444';
|
|
619
711
|
return;
|
|
620
712
|
}
|
|
621
|
-
var agentName = getAgentName() || 'main';
|
|
622
|
-
var wsField = agentName === 'main' ? 'workspace' : 'workspace-' + agentName;
|
|
623
|
-
var clawVal = window.location.hostname.split('.')[0];
|
|
624
713
|
var payload = {
|
|
625
714
|
title: titleVal,
|
|
626
|
-
workspace:
|
|
715
|
+
workspace: cachedConfig ? cachedConfig.workspaceName : 'workspace',
|
|
627
716
|
cover_path: coverSelect.value || '',
|
|
628
717
|
entry_path: entrySelect.value || '',
|
|
629
|
-
claw:
|
|
718
|
+
claw: cachedConfig ? cachedConfig.claw : '',
|
|
630
719
|
};
|
|
631
720
|
submitBtn.disabled = true;
|
|
632
721
|
submitBtn.textContent = '\u53D1\u5E03\u4E2D...';
|
|
@@ -834,9 +923,9 @@
|
|
|
834
923
|
qrSection.style.cssText = 'display:flex;flex-direction:column;align-items:center;gap:6px;padding-top:4px;';
|
|
835
924
|
|
|
836
925
|
var qrCanvas = document.createElement('canvas');
|
|
837
|
-
qrCanvas.style.cssText = 'width:140px;height:140px;border-radius:6px;
|
|
926
|
+
qrCanvas.style.cssText = 'width:140px;height:140px;border-radius:6px;';
|
|
838
927
|
try {
|
|
839
|
-
generateQR(qrCanvas, data.permanent_url,
|
|
928
|
+
generateQR(qrCanvas, data.permanent_url, 280);
|
|
840
929
|
} catch (e) {
|
|
841
930
|
console.warn('[myclaw-artifacts] QR generate error:', e);
|
|
842
931
|
qrCanvas.style.display = 'none';
|
|
@@ -858,11 +947,11 @@
|
|
|
858
947
|
footer.style.cssText = 'padding: 0 24px 20px;text-align:center;display:flex;gap:10px;justify-content:center;';
|
|
859
948
|
|
|
860
949
|
var showcaseBtn = document.createElement('a');
|
|
861
|
-
var
|
|
862
|
-
var
|
|
863
|
-
showcaseBtn.href = 'https://www.yiranlaoshi.com/showcase?workspace=' +
|
|
950
|
+
var cfgAgent = cachedConfig ? cachedConfig.agentName : 'main';
|
|
951
|
+
var cfgWs = cachedConfig ? cachedConfig.workspaceName : 'workspace';
|
|
952
|
+
showcaseBtn.href = 'https://www.yiranlaoshi.com/showcase?workspace=' + cfgWs;
|
|
864
953
|
showcaseBtn.target = '_blank';
|
|
865
|
-
showcaseBtn.textContent = '\uD83D\uDC41 \u67E5\u770B' +
|
|
954
|
+
showcaseBtn.textContent = '\uD83D\uDC41 \u67E5\u770B' + cfgAgent + '\u9879\u76EE\u96C6';
|
|
866
955
|
showcaseBtn.style.cssText = [
|
|
867
956
|
'padding: 10px 20px',
|
|
868
957
|
'background: #a78bfa',
|
|
@@ -1253,17 +1342,27 @@
|
|
|
1253
1342
|
if (!qr) throw new Error("QR code too large");
|
|
1254
1343
|
|
|
1255
1344
|
var moduleCount = qr.getModuleCount();
|
|
1256
|
-
|
|
1345
|
+
// quiet zone: QR 规范要求至少 4 模块的留白
|
|
1346
|
+
var quietZone = 4;
|
|
1347
|
+
var totalModules = moduleCount + quietZone * 2;
|
|
1348
|
+
var cellSize = size / totalModules;
|
|
1257
1349
|
canvas.width = size;
|
|
1258
1350
|
canvas.height = size;
|
|
1259
1351
|
var ctx = canvas.getContext('2d');
|
|
1352
|
+
// 白色背景(含 quiet zone)
|
|
1260
1353
|
ctx.fillStyle = '#ffffff';
|
|
1261
1354
|
ctx.fillRect(0, 0, size, size);
|
|
1262
|
-
|
|
1355
|
+
// 纯黑前景,确保扫码对比度
|
|
1356
|
+
ctx.fillStyle = '#000000';
|
|
1263
1357
|
for (var row = 0; row < moduleCount; row++) {
|
|
1264
1358
|
for (var col = 0; col < moduleCount; col++) {
|
|
1265
1359
|
if (qr.isDark(row, col)) {
|
|
1266
|
-
ctx.fillRect(
|
|
1360
|
+
ctx.fillRect(
|
|
1361
|
+
Math.round((col + quietZone) * cellSize),
|
|
1362
|
+
Math.round((row + quietZone) * cellSize),
|
|
1363
|
+
Math.ceil(cellSize),
|
|
1364
|
+
Math.ceil(cellSize)
|
|
1365
|
+
);
|
|
1267
1366
|
}
|
|
1268
1367
|
}
|
|
1269
1368
|
}
|
|
@@ -1300,8 +1399,11 @@
|
|
|
1300
1399
|
function init() {
|
|
1301
1400
|
injectStyles();
|
|
1302
1401
|
createArtifactsButton();
|
|
1303
|
-
|
|
1304
|
-
|
|
1402
|
+
// 检测环境 → 获取配置 → 启动轮询
|
|
1403
|
+
initConfig().then(function () {
|
|
1404
|
+
startPolling();
|
|
1405
|
+
console.log('[myclaw-artifacts] ✅ 初始化完成 (' + (envInfo.remote ? '远程: ' + envInfo.clawName : '本地') + ')');
|
|
1406
|
+
});
|
|
1305
1407
|
}
|
|
1306
1408
|
|
|
1307
1409
|
if (document.readyState === 'loading') {
|
package/assets/myclaw-inject.js
CHANGED
|
@@ -522,6 +522,51 @@
|
|
|
522
522
|
}
|
|
523
523
|
// ═══ 6. 拦截发送按钮 ═══
|
|
524
524
|
|
|
525
|
+
var voiceEnterListening = false;
|
|
526
|
+
var voiceEnterTimeout = null;
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* 拦截 Enter 键:语音态下按回车 → 显示"等待中...",2秒后自动发送
|
|
530
|
+
*/
|
|
531
|
+
function hookVoiceEnter() {
|
|
532
|
+
if (voiceEnterListening) return;
|
|
533
|
+
voiceEnterListening = true;
|
|
534
|
+
|
|
535
|
+
document.addEventListener("keydown", function (e) {
|
|
536
|
+
// 只拦截 textarea 上的 Enter
|
|
537
|
+
if (e.key !== "Enter") return;
|
|
538
|
+
var ta = e.target.closest ? e.target.closest(".agent-chat__input textarea") : null;
|
|
539
|
+
if (!ta) return;
|
|
540
|
+
if (!recording) return;
|
|
541
|
+
|
|
542
|
+
// 阻止默认行为(不在 textarea 里插入换行)
|
|
543
|
+
e.preventDefault();
|
|
544
|
+
e.stopPropagation();
|
|
545
|
+
|
|
546
|
+
// 1) 显示"等待中..."
|
|
547
|
+
setTextareaValue("\u5F85\u6B3A\u4E2D...");
|
|
548
|
+
// 把光标移到末尾
|
|
549
|
+
try {
|
|
550
|
+
ta.setSelectionRange(ta.value.length, ta.value.length);
|
|
551
|
+
} catch (err) {}
|
|
552
|
+
|
|
553
|
+
// 2) 停止录音
|
|
554
|
+
stopVoice();
|
|
555
|
+
|
|
556
|
+
// 3) 2秒后自动点击发送按钮
|
|
557
|
+
if (voiceEnterTimeout) clearTimeout(voiceEnterTimeout);
|
|
558
|
+
voiceEnterTimeout = setTimeout(function () {
|
|
559
|
+
voiceEnterTimeout = null;
|
|
560
|
+
var sendBtn = document.querySelector("button.chat-send-btn, button[title=\"Send\"]");
|
|
561
|
+
if (sendBtn) {
|
|
562
|
+
sendBtn.click();
|
|
563
|
+
}
|
|
564
|
+
console.log("[myclaw-voice] Enter\u89E6\u53D1\u81EA\u52A8\u53D1\u9001");
|
|
565
|
+
}, 2000);
|
|
566
|
+
|
|
567
|
+
}, true); // 捕获阶段,确保早于原生处理
|
|
568
|
+
}
|
|
569
|
+
|
|
525
570
|
var sendHooked = false;
|
|
526
571
|
|
|
527
572
|
function hookSendButton() {
|
|
@@ -853,7 +898,7 @@
|
|
|
853
898
|
{ label: "\uD83D\uDCAC \u6DFB\u52A0\u5BF9\u8BDD", desc: "\u6253\u5F00\u5DF2\u6709\u4F19\u4F34\u7684\u5BF9\u8BDD\u7A97\u53E3", hasInput: true, inputTitle: "\u6DFB\u52A0\u5BF9\u8BDD", placeholder: "\u8F93\u5165\u4F19\u4F34\u540D\u79F0\uFF0C\u5982 kakaxi", hint: "\u8F93\u5165\u4F60\u7684\u4F19\u4F34\u7684\u540D\u79F0\uFF08\u82F1\u6587\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF09\uFF0C\u70B9\u51FB\u540E\u4F1A\u6253\u5F00\u5BF9\u8BDD\u7A97\u53E3", cmd: "mc tui {name}", color: "#10b981" },
|
|
854
899
|
{ label: "\uD83D\uDE80 \u5347\u7EA7", desc: "\u5347\u7EA7 myclaw \u5230\u6700\u65B0\u7248\u672C", hasInput: false, cmd: "mc up", color: "#8b5cf6" },
|
|
855
900
|
{ label: "\uD83D\uDD04 \u91CD\u542F", desc: "\u91CD\u542F\u670D\u52A1\uFF0C\u4FEE\u590D\u5927\u591A\u6570\u95EE\u9898", hasInput: false, cmd: "mc restart", color: "#ef4444" },
|
|
856
|
-
{ label: "\uD83E\uDD1D \u65B0\u4F19\u4F34", desc: "\u521B\u5EFA\u4E00\u4E2A\u65B0\u7684 AI \u4F19\u4F34", hasInput: true, inputTitle: "\u65B0\u5EFA\u4F19\u4F34", placeholder: "\u8F93\u5165\u65B0\u4F19\u4F34\u540D\u79F0\uFF0C\u5982 my-cat", hint: "\u7ED9\u4F60\u7684\u65B0 AI \u4F19\u4F34\u8D77\u4E2A\u540D\u5B57\uFF08\u82F1\u6587\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF09\uFF0C\u70B9\u51FB\u540E\u4F1A\u81EA\u52A8\u521B\u5EFA", cmd: "mc
|
|
901
|
+
{ label: "\uD83E\uDD1D \u65B0\u4F19\u4F34", desc: "\u521B\u5EFA\u4E00\u4E2A\u65B0\u7684 AI \u4F19\u4F34", hasInput: true, inputTitle: "\u65B0\u5EFA\u4F19\u4F34", placeholder: "\u8F93\u5165\u65B0\u4F19\u4F34\u540D\u79F0\uFF0C\u5982 my-cat", hint: "\u7ED9\u4F60\u7684\u65B0 AI \u4F19\u4F34\u8D77\u4E2A\u540D\u5B57\uFF08\u82F1\u6587\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u8FDE\u5B57\u7B26\uFF09\uFF0C\u70B9\u51FB\u540E\u4F1A\u81EA\u52A8\u521B\u5EFA", cmd: "mc new {name}", color: "#3b82f6" },
|
|
857
902
|
];
|
|
858
903
|
|
|
859
904
|
btns.forEach(function (item) {
|
|
@@ -908,6 +953,175 @@
|
|
|
908
953
|
form.appendChild(row);
|
|
909
954
|
});
|
|
910
955
|
|
|
956
|
+
// ── 删除伙伴按钮 ──
|
|
957
|
+
var delRow = document.createElement("div");
|
|
958
|
+
delRow.style.cssText = [
|
|
959
|
+
"padding:10px 14px",
|
|
960
|
+
"background:#252536",
|
|
961
|
+
"border-radius:6px",
|
|
962
|
+
"cursor:pointer",
|
|
963
|
+
"transition:background 0.15s",
|
|
964
|
+
"display:flex",
|
|
965
|
+
"align-items:center",
|
|
966
|
+
"gap:10px",
|
|
967
|
+
].join(";");
|
|
968
|
+
delRow.onmouseenter = function () { delRow.style.background = "#2f2f4a"; };
|
|
969
|
+
delRow.onmouseleave = function () { delRow.style.background = "#252536"; };
|
|
970
|
+
|
|
971
|
+
var delBar = document.createElement("div");
|
|
972
|
+
delBar.style.cssText = "width:3px;height:28px;border-radius:2px;background:#ef4444;flex-shrink:0;";
|
|
973
|
+
delRow.appendChild(delBar);
|
|
974
|
+
|
|
975
|
+
var delInfo = document.createElement("div");
|
|
976
|
+
delInfo.style.cssText = "flex:1;display:flex;flex-direction:column;gap:2px;";
|
|
977
|
+
var delName = document.createElement("div");
|
|
978
|
+
delName.textContent = "\uD83D\uDDD1 \u5220\u9664\u4F19\u4F34";
|
|
979
|
+
delName.style.cssText = "font-size:13px;font-weight:bold;color:#ef4444;";
|
|
980
|
+
delInfo.appendChild(delName);
|
|
981
|
+
var delDesc = document.createElement("div");
|
|
982
|
+
delDesc.textContent = "\u5220\u9664\u4E00\u4E2A AI \u4F19\u4F34\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u6062\u590D";
|
|
983
|
+
delDesc.style.cssText = "font-size:11px;color:#888;";
|
|
984
|
+
delInfo.appendChild(delDesc);
|
|
985
|
+
delRow.appendChild(delInfo);
|
|
986
|
+
|
|
987
|
+
var delArrow = document.createElement("div");
|
|
988
|
+
delArrow.textContent = "\u25B6";
|
|
989
|
+
delArrow.style.cssText = "color:#555;font-size:10px;";
|
|
990
|
+
delRow.appendChild(delArrow);
|
|
991
|
+
|
|
992
|
+
delRow.onclick = function () {
|
|
993
|
+
showDeleteConfirm();
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
form.appendChild(delRow);
|
|
997
|
+
|
|
998
|
+
// 删除伙伴 - 双重确认弹框
|
|
999
|
+
function showDeleteConfirm() {
|
|
1000
|
+
var mask = document.createElement("div");
|
|
1001
|
+
mask.style.cssText = [
|
|
1002
|
+
"position:fixed",
|
|
1003
|
+
"top:0;left:0;width:100vw;height:100vh",
|
|
1004
|
+
"background:rgba(0,0,0,0.3)",
|
|
1005
|
+
"z-index:999999",
|
|
1006
|
+
"display:flex",
|
|
1007
|
+
"align-items:center",
|
|
1008
|
+
"justify-content:center",
|
|
1009
|
+
"animation:myclaw-fade-in 0.15s ease",
|
|
1010
|
+
].join(";");
|
|
1011
|
+
|
|
1012
|
+
var box = document.createElement("div");
|
|
1013
|
+
box.style.cssText = [
|
|
1014
|
+
"width:360px",
|
|
1015
|
+
"background:#1e1e2e",
|
|
1016
|
+
"border-radius:8px",
|
|
1017
|
+
"overflow:hidden",
|
|
1018
|
+
"box-shadow:0 8px 32px rgba(0,0,0,0.5)",
|
|
1019
|
+
].join(";");
|
|
1020
|
+
|
|
1021
|
+
// 标题
|
|
1022
|
+
var h = document.createElement("div");
|
|
1023
|
+
h.style.cssText = "padding:10px 14px;background:#ef4444;color:#fff;font-size:13px;display:flex;justify-content:space-between;align-items:center;";
|
|
1024
|
+
h.innerHTML = '<span>\uD83D\uDDD1 \u5220\u9664\u4F19\u4F34</span>';
|
|
1025
|
+
var x = document.createElement("span");
|
|
1026
|
+
x.textContent = "\u2715";
|
|
1027
|
+
x.style.cssText = "cursor:pointer;padding:2px 6px;border-radius:3px;";
|
|
1028
|
+
x.onclick = function () { mask.remove(); };
|
|
1029
|
+
h.appendChild(x);
|
|
1030
|
+
box.appendChild(h);
|
|
1031
|
+
|
|
1032
|
+
// body
|
|
1033
|
+
var body = document.createElement("div");
|
|
1034
|
+
body.style.cssText = "padding:16px;display:flex;flex-direction:column;gap:12px;";
|
|
1035
|
+
|
|
1036
|
+
var hint1 = document.createElement("div");
|
|
1037
|
+
hint1.textContent = "\u8BF7\u8F93\u5165\u8981\u5220\u9664\u7684\u4F19\u4F34 ID\uFF1A";
|
|
1038
|
+
hint1.style.cssText = "font-size:12px;color:#888;";
|
|
1039
|
+
body.appendChild(hint1);
|
|
1040
|
+
|
|
1041
|
+
var input1 = document.createElement("input");
|
|
1042
|
+
input1.type = "text";
|
|
1043
|
+
input1.placeholder = "\u4F19\u4F34 ID";
|
|
1044
|
+
input1.style.cssText = "padding:8px 10px;background:#252536;border:1px solid #3d3d5c;border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;outline:none;";
|
|
1045
|
+
body.appendChild(input1);
|
|
1046
|
+
|
|
1047
|
+
var warn = document.createElement("div");
|
|
1048
|
+
warn.textContent = "\u26A0 \u6B64\u64CD\u4F5C\u65E0\u6CD5\u6062\u590D\uFF0C\u786E\u8BA4\u540E\u5C06\u6C38\u4E45\u5220\u9664\uFF01";
|
|
1049
|
+
warn.style.cssText = "font-size:11px;color:#ef4444;padding:8px;background:rgba(239,68,68,0.1);border-radius:4px;";
|
|
1050
|
+
body.appendChild(warn);
|
|
1051
|
+
|
|
1052
|
+
var confirmHint = document.createElement("div");
|
|
1053
|
+
confirmHint.textContent = '\u8BF7\u8F93\u5165 "YES" \u786E\u8BA4\u5220\u9664\uFF1A';
|
|
1054
|
+
confirmHint.style.cssText = "font-size:12px;color:#888;";
|
|
1055
|
+
confirmHint.style.display = "none";
|
|
1056
|
+
body.appendChild(confirmHint);
|
|
1057
|
+
|
|
1058
|
+
var input2 = document.createElement("input");
|
|
1059
|
+
input2.type = "text";
|
|
1060
|
+
input2.placeholder = "YES";
|
|
1061
|
+
input2.style.cssText = "padding:8px 10px;background:#252536;border:1px solid #3d3d5c;border-radius:4px;color:#cdd6f4;font-size:13px;font-family:monospace;outline:none;";
|
|
1062
|
+
input2.style.display = "none";
|
|
1063
|
+
body.appendChild(input2);
|
|
1064
|
+
|
|
1065
|
+
var submitBtn = document.createElement("button");
|
|
1066
|
+
submitBtn.textContent = "\u7EE7\u7EED";
|
|
1067
|
+
submitBtn.style.cssText = "padding:8px 16px;background:#ef4444;border:none;border-radius:4px;color:#fff;font-size:12px;font-family:monospace;cursor:pointer;";
|
|
1068
|
+
body.appendChild(submitBtn);
|
|
1069
|
+
|
|
1070
|
+
var cancelBtn = document.createElement("button");
|
|
1071
|
+
cancelBtn.textContent = "\u53D6\u6D88";
|
|
1072
|
+
cancelBtn.style.cssText = "padding:8px 16px;background:#3d3d5c;border:none;border-radius:4px;color:#cdd6f4;font-size:12px;font-family:monospace;cursor:pointer;";
|
|
1073
|
+
body.appendChild(cancelBtn);
|
|
1074
|
+
|
|
1075
|
+
box.appendChild(body);
|
|
1076
|
+
mask.appendChild(box);
|
|
1077
|
+
document.body.appendChild(mask);
|
|
1078
|
+
|
|
1079
|
+
input1.focus();
|
|
1080
|
+
|
|
1081
|
+
var agentId = "";
|
|
1082
|
+
submitBtn.onclick = function () {
|
|
1083
|
+
if (!agentId) {
|
|
1084
|
+
// 第一步:输入 agent ID
|
|
1085
|
+
agentId = input1.value.trim();
|
|
1086
|
+
if (!agentId) {
|
|
1087
|
+
input1.style.borderColor = "#ef4444";
|
|
1088
|
+
input1.focus();
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
// 显示第二步确认
|
|
1092
|
+
input1.style.display = "none";
|
|
1093
|
+
hint1.style.display = "none";
|
|
1094
|
+
confirmHint.style.display = "block";
|
|
1095
|
+
input2.style.display = "block";
|
|
1096
|
+
submitBtn.textContent = "\u786E\u8BA4\u5220\u9664";
|
|
1097
|
+
input2.value = "";
|
|
1098
|
+
input2.focus();
|
|
1099
|
+
} else {
|
|
1100
|
+
// 第二步:确认删除
|
|
1101
|
+
var confirmVal = input2.value.trim();
|
|
1102
|
+
if (confirmVal.toUpperCase() !== "YES") {
|
|
1103
|
+
input2.style.borderColor = "#ef4444";
|
|
1104
|
+
input2.focus();
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
submitBtn.disabled = true;
|
|
1108
|
+
submitBtn.textContent = "\u6267\u884C\u4E2D...";
|
|
1109
|
+
runCommand("openclaw agents delete " + agentId + " --force");
|
|
1110
|
+
setTimeout(function () {
|
|
1111
|
+
mask.remove();
|
|
1112
|
+
}, 1000);
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
|
|
1116
|
+
cancelBtn.onclick = function () {
|
|
1117
|
+
mask.remove();
|
|
1118
|
+
};
|
|
1119
|
+
|
|
1120
|
+
input2.onkeydown = function (e) {
|
|
1121
|
+
if (e.key === "Enter") submitBtn.click();
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
|
|
911
1125
|
box.appendChild(header);
|
|
912
1126
|
box.appendChild(form);
|
|
913
1127
|
overlay.appendChild(box);
|
|
@@ -939,6 +1153,9 @@
|
|
|
939
1153
|
// 初始化 VoiceInput SDK
|
|
940
1154
|
initVoice();
|
|
941
1155
|
|
|
1156
|
+
// 拦截语音态 Enter 键
|
|
1157
|
+
hookVoiceEnter();
|
|
1158
|
+
|
|
942
1159
|
// 持续监听 DOM 变化,确保按钮始终在
|
|
943
1160
|
new MutationObserver(function () {
|
|
944
1161
|
if (!document.querySelector("#myclaw-voice-btn")) {
|
package/index.js
CHANGED
|
@@ -1172,7 +1172,53 @@ function padRight(str, len) {
|
|
|
1172
1172
|
// 交互式菜单(上下键选择)
|
|
1173
1173
|
// ============================================================================
|
|
1174
1174
|
|
|
1175
|
+
// ============================================================================
|
|
1176
|
+
// 机器选择配置
|
|
1177
|
+
// ============================================================================
|
|
1178
|
+
const MACHINE_CONFIG = [
|
|
1179
|
+
{ name: 'kendy', claw: 'claw1', desc: 'kendy 的机器' },
|
|
1180
|
+
{ name: 'Ethan', claw: 'claw2', desc: 'Ethan 的机器' },
|
|
1181
|
+
{ name: 'HENRY', claw: 'claw3', desc: 'HENRY 的机器' },
|
|
1182
|
+
{ name: 'Mo靖宇', claw: 'claw4', desc: 'Mo靖宇 的机器' },
|
|
1183
|
+
{ name: '高兴', claw: 'claw5', desc: '高兴 的机器' },
|
|
1184
|
+
{ name: '伊伊', claw: 'claw6', desc: '伊伊 的机器' },
|
|
1185
|
+
{ name: '雨熙', claw: 'claw7', desc: '雨熙 的机器' },
|
|
1186
|
+
{ name: '绍博', claw: 'claw8', desc: '绍博 的机器' },
|
|
1187
|
+
];
|
|
1188
|
+
|
|
1189
|
+
function showMachineMenu() {
|
|
1190
|
+
console.log('');
|
|
1191
|
+
console.log(' ' + colors.blue + '机器选择' + colors.nc);
|
|
1192
|
+
console.log('----------------------------------------');
|
|
1193
|
+
console.log('');
|
|
1194
|
+
MACHINE_CONFIG.forEach((m, i) => {
|
|
1195
|
+
console.log(' ' + colors.cyan + (i + 1) + '.' + colors.nc + ' ' + m.name + ' (' + colors.dim + m.claw + colors.nc + ')');
|
|
1196
|
+
console.log(' ' + colors.dim + m.desc + colors.nc);
|
|
1197
|
+
console.log('');
|
|
1198
|
+
});
|
|
1199
|
+
console.log(' ' + colors.cyan + '0.' + colors.nc + ' 返回');
|
|
1200
|
+
console.log('');
|
|
1201
|
+
|
|
1202
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1203
|
+
rl.question('请选择机器 [0-' + MACHINE_CONFIG.length + ']: ', function (answer) {
|
|
1204
|
+
rl.close();
|
|
1205
|
+
const choice = parseInt(answer.trim());
|
|
1206
|
+
if (choice === 0) return;
|
|
1207
|
+
if (choice >= 1 && choice <= MACHINE_CONFIG.length) {
|
|
1208
|
+
const selected = MACHINE_CONFIG[choice - 1];
|
|
1209
|
+
console.log('');
|
|
1210
|
+
console.log(colors.green + ' → 已选择: ' + selected.name + ' (' + selected.claw + ')' + colors.nc);
|
|
1211
|
+
console.log(' ' + colors.dim + '→ 正在用 Chrome 打开...' + colors.nc);
|
|
1212
|
+
const { execSync } = require('child_process');
|
|
1213
|
+
execSync('open -a "Google Chrome" "https://' + selected.claw + '.kekouen.cn?token=aiyiran"', { stdio: 'ignore' });
|
|
1214
|
+
} else {
|
|
1215
|
+
console.log('[' + colors.red + '错误' + colors.nc + '] 无效选择');
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1175
1220
|
const MENU_ITEMS = [
|
|
1221
|
+
{ key: 'machine', label: '💻机器', cmd: 'mc machine', desc: '选择不同的机器', action: showMachineMenu },
|
|
1176
1222
|
{ key: 'start', label: '🦞启动🦞', cmd: 'mc start', desc: '把你的 AI 助手叫醒,让它开始工作', action: () => { const start = require('./start'); start.run(); } },
|
|
1177
1223
|
{ key: 'restart', label: '重启', cmd: 'mc restart', desc: 'AI 助手卡住了?让它重新启动一下', action: runRestart },
|
|
1178
1224
|
{ key: 'new', label: '😊新伙伴', cmd: 'mc new', desc: '创建一个新的 AI 助手,给它取个名字', action: runNew },
|
|
@@ -1484,7 +1530,7 @@ function runSync(workspaceName) {
|
|
|
1484
1530
|
}
|
|
1485
1531
|
|
|
1486
1532
|
async function runServer(name) {
|
|
1487
|
-
const { spawn } = require('child_process');
|
|
1533
|
+
const { spawn, execSync: execSyncLocal } = require('child_process');
|
|
1488
1534
|
const fs = require('fs');
|
|
1489
1535
|
|
|
1490
1536
|
// 用户目录下的服务目录
|
|
@@ -1492,6 +1538,19 @@ async function runServer(name) {
|
|
|
1492
1538
|
const targetPyPath = path.join(targetDir, 'sync_workspace.py');
|
|
1493
1539
|
const targetConfigPath = path.join(targetDir, 'config.json');
|
|
1494
1540
|
const sourcePyPath = path.join(__dirname, 'server', 'sync_workspace.py');
|
|
1541
|
+
const pidFile = path.join(os.homedir(), '.openclaw', '.myclaw-sync.pid');
|
|
1542
|
+
|
|
1543
|
+
// 0. 杀死旧的 sync_workspace 进程(通过 PID 文件)
|
|
1544
|
+
if (fs.existsSync(pidFile)) {
|
|
1545
|
+
try {
|
|
1546
|
+
const oldPid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
|
|
1547
|
+
if (oldPid && !isNaN(oldPid)) {
|
|
1548
|
+
try { process.kill(oldPid, 'SIGTERM'); } catch (e) { /* 不存在则忽略 */ }
|
|
1549
|
+
console.log('[Server] 已终止旧进程 PID=' + oldPid);
|
|
1550
|
+
}
|
|
1551
|
+
} catch (e) { /* 读取失败忽略 */ }
|
|
1552
|
+
try { fs.unlinkSync(pidFile); } catch (e) { }
|
|
1553
|
+
}
|
|
1495
1554
|
|
|
1496
1555
|
// 1. 创建目标目录
|
|
1497
1556
|
if (!fs.existsSync(targetDir)) {
|
|
@@ -1499,7 +1558,7 @@ async function runServer(name) {
|
|
|
1499
1558
|
console.log('[Server] 创建目录: ' + targetDir);
|
|
1500
1559
|
}
|
|
1501
1560
|
|
|
1502
|
-
// 2. 覆盖 py
|
|
1561
|
+
// 2. 覆盖 py 文件(确保最新)
|
|
1503
1562
|
fs.copyFileSync(sourcePyPath, targetPyPath);
|
|
1504
1563
|
console.log('[Server] 同步脚本: ' + targetPyPath);
|
|
1505
1564
|
|
|
@@ -1550,14 +1609,25 @@ async function runServer(name) {
|
|
|
1550
1609
|
console.error('[' + colors.red + '错误' + colors.nc + '] 启动失败: ' + err.message);
|
|
1551
1610
|
});
|
|
1552
1611
|
|
|
1553
|
-
child.on('exit', (code) => {
|
|
1612
|
+
child.on('exit', (code, signal) => {
|
|
1613
|
+
// 被 SIGTERM 杀死(下一次 mc server 启动时)→ 不重启
|
|
1614
|
+
if (signal === 'SIGTERM') {
|
|
1615
|
+
console.log('[Server] 服务已被外部终止,不再重启');
|
|
1616
|
+
process.exit(0);
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1554
1619
|
if (code !== null && code !== 0) {
|
|
1555
1620
|
console.log('[' + colors.yellow + '警告' + colors.nc + '] 服务异常退出,代码: ' + code + ',3秒后重启...');
|
|
1621
|
+
setTimeout(startProcess, 3000);
|
|
1556
1622
|
} else {
|
|
1557
|
-
console.log('[Server]
|
|
1623
|
+
console.log('[Server] 服务已正常停止');
|
|
1558
1624
|
}
|
|
1559
|
-
setTimeout(startProcess, 3000);
|
|
1560
1625
|
});
|
|
1626
|
+
|
|
1627
|
+
// 记录子进程 PID 到文件
|
|
1628
|
+
if (child.pid) {
|
|
1629
|
+
fs.writeFileSync(pidFile, String(child.pid), 'utf-8');
|
|
1630
|
+
}
|
|
1561
1631
|
}
|
|
1562
1632
|
|
|
1563
1633
|
startProcess();
|