@aiyiran/myclaw 1.0.160 → 1.0.161

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/restrict.js +293 -41
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiyiran/myclaw",
3
- "version": "1.0.160",
3
+ "version": "1.0.161",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
package/restrict.js CHANGED
@@ -2,18 +2,24 @@
2
2
 
3
3
  /**
4
4
  * ============================================================================
5
- * MyClaw WSL2 权限封锁
5
+ * MyClaw WSL2 权限封锁(Windows 侧)
6
6
  * ============================================================================
7
7
  *
8
- * 功能:逐步封锁 WSL2 对 Windows 文件系统的访问权限
8
+ * Windows 一次性完成:
9
+ * 步骤 1: 在 WSL 内创建工作目录 ~/workspace
10
+ * 步骤 2: 编辑 /etc/wsl.conf 禁用 automount(Linux 看不到 Windows 盘符)
11
+ * 步骤 3: 在 Windows 桌面创建快捷方式,指向 \\wsl.localhost\OpenClaw\<workspace>
9
12
  *
10
13
  * 使用方法:
11
14
  * myclaw restrict
15
+ *
16
+ * 前提:OpenClaw WSL2 发行版已安装(myclaw wsl2 完成)
12
17
  * ============================================================================
13
18
  */
14
19
 
15
20
  const { execSync } = require('child_process');
16
21
  const os = require('os');
22
+ const fs = require('fs');
17
23
  const path = require('path');
18
24
 
19
25
  // ============================================================================
@@ -26,7 +32,8 @@ const C = isWindows
26
32
  ? { r: '', g: '', y: '', b: '', nc: '' }
27
33
  : { r: '\x1b[31m', g: '\x1b[32m', y: '\x1b[33m', b: '\x1b[34m', nc: '\x1b[0m' };
28
34
 
29
- const SCRIPT_PATH = path.join(__dirname, 'scripts', 'restrict-wsl-access.sh');
35
+ const DISTRO_NAME = 'OpenClaw';
36
+ const WORKSPACE_DIR = 'workspace'; // WSL 内的 ~/workspace
30
37
 
31
38
  // ============================================================================
32
39
  // 工具函数
@@ -52,28 +59,261 @@ function printError(msg) {
52
59
  console.error(C.r + '[错误]' + C.nc + ' ' + msg);
53
60
  }
54
61
 
55
- // 检查是否在 WSL 环境中
56
- function isWSL() {
62
+ // WSL 中执行命令,返回输出
63
+ function wslExec(cmd) {
57
64
  try {
58
- const version = require('fs').readFileSync('/proc/version', 'utf8');
59
- return /microsoft|wsl/i.test(version);
60
- } catch {
61
- return false;
65
+ return execSync(`wsl -d ${DISTRO_NAME} -- bash -c "${cmd}"`, {
66
+ encoding: 'utf8',
67
+ stdio: ['pipe', 'pipe', 'pipe'],
68
+ }).trim();
69
+ } catch (err) {
70
+ return null;
62
71
  }
63
72
  }
64
73
 
65
- // 在 WSL 中执行脚本
66
- function runInWSL(scriptPath) {
74
+ // 在 WSL 中以 root 执行命令
75
+ function wslExecSudo(cmd) {
67
76
  try {
68
- const cmd = `bash "${scriptPath}"`;
69
- execSync(cmd, { stdio: 'inherit' });
77
+ // OpenClaw 默认用户可能是 root,如果不是则需要 sudo
78
+ execSync(`wsl -d ${DISTRO_NAME} --user root -- bash -c "${cmd}"`, {
79
+ encoding: 'utf8',
80
+ stdio: 'pipe',
81
+ });
70
82
  return true;
71
83
  } catch (err) {
72
- printError('执行失败: ' + err.message);
84
+ // 回退尝试普通用户 + sudo
85
+ try {
86
+ execSync(`wsl -d ${DISTRO_NAME} -- bash -c "sudo ${cmd}"`, {
87
+ encoding: 'utf8',
88
+ stdio: 'pipe',
89
+ });
90
+ return true;
91
+ } catch (err2) {
92
+ printError('WSL 命令执行失败: ' + (err2.stderr || err2.message));
93
+ return false;
94
+ }
95
+ }
96
+ }
97
+
98
+ // ============================================================================
99
+ // 前置检查
100
+ // ============================================================================
101
+
102
+ function checkPrerequisites() {
103
+ if (!isWindows) {
104
+ printError('此命令只能在 Windows 上运行');
105
+ console.log('');
106
+ console.log('Mac/Linux 不需要 WSL2 权限封锁。');
107
+ process.exit(1);
108
+ }
109
+
110
+ // 检查 wsl 命令是否存在
111
+ try {
112
+ execSync('wsl --version', { stdio: 'pipe' });
113
+ } catch {
114
+ printError('未检测到 WSL。请先运行: myclaw wsl2');
115
+ process.exit(1);
116
+ }
117
+
118
+ // 检查 OpenClaw 发行版是否存在
119
+ try {
120
+ const list = execSync('wsl -l -q', { encoding: 'utf16le' }).trim();
121
+ if (!list.includes(DISTRO_NAME)) {
122
+ printError('未找到 ' + DISTRO_NAME + ' 发行版。请先运行: myclaw wsl2');
123
+ process.exit(1);
124
+ }
125
+ } catch {
126
+ printError('无法读取 WSL 发行版列表。请先运行: myclaw wsl2');
127
+ process.exit(1);
128
+ }
129
+
130
+ printSuccess('前置检查通过');
131
+ }
132
+
133
+ // ============================================================================
134
+ // 步骤 1: 在 WSL 内创建工作目录
135
+ // ============================================================================
136
+
137
+ function step1_createWorkspace() {
138
+ printStep('步骤 1/3: 在 WSL 内创建工作目录');
139
+
140
+ // 获取 WSL 用户名
141
+ const wslUser = wslExec('whoami');
142
+ if (!wslUser) {
143
+ printError('无法获取 WSL 用户名');
144
+ return null;
145
+ }
146
+
147
+ printInfo('WSL 用户: ' + wslUser);
148
+
149
+ // 创建工作目录
150
+ const workspacePath = `/home/${wslUser}/${WORKSPACE_DIR}`;
151
+ const result = wslExec(`mkdir -p ${workspacePath} && echo ${workspacePath}`);
152
+
153
+ if (!result) {
154
+ printError('创建工作目录失败');
155
+ return null;
156
+ }
157
+
158
+ printSuccess('工作目录已创建: ' + workspacePath);
159
+ return { wslUser, workspacePath };
160
+ }
161
+
162
+ // ============================================================================
163
+ // 步骤 2: 禁用 automount
164
+ // ============================================================================
165
+
166
+ function step2_disableAutomount(wslUser) {
167
+ printStep('步骤 2/3: 禁用自动挂载 Windows 盘符');
168
+
169
+ // 先备份(如果文件存在)
170
+ wslExecSudo('cp /etc/wsl.conf /etc/wsl.conf.bak 2>/dev/null; true');
171
+
172
+ // 检查现有配置中是否已有 automount 设置
173
+ const existingConf = wslExec('cat /etc/wsl.conf 2>/dev/null') || '';
174
+
175
+ let newContent;
176
+
177
+ if (existingConf.includes('[automount]')) {
178
+ // 替换现有的 automount 部分
179
+ newContent = existingConf.replace(
180
+ /\[automount\][^\[]*/,
181
+ '[automount]\nenabled = false\n'
182
+ );
183
+ } else {
184
+ // 追加 automount 配置
185
+ newContent = existingConf + (existingConf ? '\n' : '') + '[automount]\nenabled = false\n';
186
+ }
187
+
188
+ // 写入新配置
189
+ const escaped = newContent.replace(/"/g, '\\"').replace(/`/g, '\\`').replace(/\$/g, '\\$');
190
+ const writeCmd = `cat > /etc/wsl.conf << 'WSLEOF'\n${newContent}WSLEOF`;
191
+
192
+ const ok = wslExecSudo(`bash -c '${writeCmd.replace(/'/g, "'\\''")}'`);
193
+
194
+ if (!ok) {
195
+ // 回退方案:直接用 tee 写入
196
+ const heredocOk = wslExecSudo(
197
+ `bash -c "echo '${escaped}' > /etc/wsl.conf"`
198
+ );
199
+ if (!heredocOk) {
200
+ printError('写入 wsl.conf 失败');
201
+ return false;
202
+ }
203
+ }
204
+
205
+ // 验证
206
+ const verify = wslExec('cat /etc/wsl.conf 2>/dev/null') || '';
207
+ if (verify.includes('enabled = false') || verify.includes('enabled=false')) {
208
+ printSuccess('automount 已禁用');
209
+ return true;
210
+ } else {
211
+ printError('配置验证失败,请手动检查 /etc/wsl.conf');
73
212
  return false;
74
213
  }
75
214
  }
76
215
 
216
+ // ============================================================================
217
+ // 步骤 3: 在桌面创建快捷方式
218
+ // ============================================================================
219
+
220
+ function step3_createShortcut(wslUser) {
221
+ printStep('步骤 3/3: 创建桌面快捷方式');
222
+
223
+ const wslPath = `\\\\wsl.localhost\\${DISTRO_NAME}\\home\\${wslUser}\\${WORKSPACE_DIR}`;
224
+ const desktopPath = path.join(os.homedir(), 'Desktop');
225
+
226
+ // 检查桌面路径是否存在
227
+ if (!fs.existsSync(desktopPath)) {
228
+ // 尝试中文桌面路径
229
+ const desktopPathCN = path.join(os.homedir(), '桌面');
230
+ if (fs.existsSync(desktopPathCN)) {
231
+ var shortcutDir = desktopPathCN;
232
+ } else {
233
+ printError('找不到桌面目录');
234
+ return false;
235
+ }
236
+ } else {
237
+ var shortcutDir = desktopPath;
238
+ }
239
+
240
+ const shortcutName = 'OpenClaw 工作目录.lnk';
241
+ const shortcutPath = path.join(shortcutDir, shortcutName);
242
+
243
+ // PowerShell 脚本创建快捷方式
244
+ const psScript = `
245
+ $ws = New-Object -ComObject WScript.Shell
246
+ $sc = $ws.CreateShortcut("${shortcutPath}")
247
+ $sc.TargetPath = "explorer.exe"
248
+ $sc.Arguments = "${wslPath}"
249
+ $sc.WorkingDirectory = "${wslPath}"
250
+ $sc.Description = "打开 OpenClaw 工作目录"
251
+ $sc.Save()
252
+
253
+ # 尝试下载图标
254
+ $iconDir = "${path.join(os.tmpdir(), 'myclaw')}"
255
+ New-Item -ItemType Directory -Force -Path $iconDir | Out-Null
256
+ $iconPath = "$iconDir\\openclaw.ico"
257
+ try {
258
+ Invoke-WebRequest -Uri "https://cdn.yiranlaoshi.com/software/myclaw/openclaw.ico" -OutFile $iconPath -UseBasicParsing
259
+ $sc.IconLocation = $iconPath
260
+ $sc.Save()
261
+ } catch {}
262
+
263
+ # 刷新桌面
264
+ [void] [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
265
+ [System.IntPtr] ([System.Runtime.InteropServices.Marshal]::ReadIntPtr(
266
+ [System.IntPtr] ([System.Runtime.InteropServices.Marshal]::GetProcAddress(
267
+ [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer(
268
+ [System.Runtime.InteropServices.Marshal]::GetProcAddress(
269
+ [System.Runtime.InteropServices.Marshal]::LoadLibrary('shell32.dll'),
270
+ 'SHChangeNotify'),
271
+ [System.Type] [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer)))
272
+ ))
273
+ )
274
+
275
+ Write-Host "OK"
276
+ `.trim();
277
+
278
+ // 简化:直接用 PowerShell 创建
279
+ const psCmd = `
280
+ $ws = New-Object -ComObject WScript.Shell
281
+ $sc = $ws.CreateShortcut("${shortcutPath.replace(/\\/g, '\\\\')}")
282
+ $sc.TargetPath = "explorer.exe"
283
+ $sc.Arguments = "${wslPath}"
284
+ $sc.WorkingDirectory = "${wslPath}"
285
+ $sc.Description = "OpenClaw Workspace"
286
+ $sc.Save()
287
+ Write-Host "OK"
288
+ `;
289
+
290
+ try {
291
+ const result = execSync(`powershell -NoProfile -Command "${psCmd.replace(/"/g, '\\"').replace(/\n/g, ' ')}"`, {
292
+ encoding: 'utf8',
293
+ stdio: 'pipe',
294
+ }).trim();
295
+
296
+ if (result.includes('OK')) {
297
+ printSuccess('桌面快捷方式已创建: ' + shortcutName);
298
+ printInfo('双击即可打开工作目录(通过 \\\\wsl.localhost 访问)');
299
+ return true;
300
+ }
301
+ } catch (err) {
302
+ // 回退:写一个 .url 文件
303
+ try {
304
+ const urlContent = `[InternetShortcut]\r\nURL=file://${wslPath}\r\nIconIndex=0\r\n`;
305
+ fs.writeFileSync(path.join(shortcutDir, 'OpenClaw 工作目录.url'), urlContent, 'utf8');
306
+ printSuccess('桌面快捷方式已创建 (.url)');
307
+ return true;
308
+ } catch (err2) {
309
+ printError('创建快捷方式失败: ' + err2.message);
310
+ return false;
311
+ }
312
+ }
313
+
314
+ return false;
315
+ }
316
+
77
317
  // ============================================================================
78
318
  // 主函数
79
319
  // ============================================================================
@@ -84,40 +324,52 @@ function run() {
84
324
  console.log(' MyClaw WSL2 权限封锁');
85
325
  console.log('========================================');
86
326
  console.log('');
327
+ console.log('此操作将:');
328
+ console.log(' 1. 在 WSL 内创建工作目录 ~/workspace');
329
+ console.log(' 2. 禁用 WSL 自动挂载 Windows 盘符');
330
+ console.log(' 3. 在桌面创建快捷方式(指向 WSL 工作目录)');
331
+ console.log('');
332
+ console.log('效果:Linux 看不到 Windows 文件,但 Windows');
333
+ console.log(' 可以通过快捷方式访问 Linux 工作目录。');
334
+ console.log('');
87
335
 
88
- // 如果在 Windows 上,提示需要在 WSL 中运行
89
- if (isWindows) {
90
- printWarning('此命令需要在 WSL2 环境中运行');
91
- console.log('');
92
- console.log('请按以下步骤操作:');
93
- console.log(' 1. 打开 WSL2 终端(在 Windows Terminal 中选择 OpenClaw)');
94
- console.log(' 2. WSL2 终端中运行: ' + C.y + 'myclaw restrict' + C.nc);
95
- console.log('');
96
- console.log('或者,在 Windows PowerShell 中运行:');
97
- console.log(' ' + C.y + 'wsl -d OpenClaw -- myclaw restrict' + C.nc);
98
- console.log(' (将 OpenClaw 替换为你的 WSL 发行版名称)');
99
- console.log('');
100
- return;
336
+ // 前置检查
337
+ checkPrerequisites();
338
+
339
+ // 步骤 1
340
+ const info = step1_createWorkspace();
341
+ if (!info) {
342
+ printError('步骤 1 失败,终止');
343
+ process.exit(1);
101
344
  }
102
345
 
103
- // 检查是否在 WSL 环境中
104
- if (!isWSL()) {
105
- printError('此脚本只能在 WSL2 环境中运行');
106
- console.log('');
107
- console.log('如果你在 Windows 上,请先安装 WSL2:');
108
- console.log(' ' + C.y + 'myclaw wsl2' + C.nc);
109
- console.log('');
110
- return;
346
+ // 步骤 2
347
+ if (!step2_disableAutomount(info.wslUser)) {
348
+ printError('步骤 2 失败,终止');
349
+ process.exit(1);
111
350
  }
112
351
 
113
- printSuccess('检测到 WSL2 环境');
114
- console.log('');
352
+ // 步骤 3
353
+ if (!step3_createShortcut(info.wslUser)) {
354
+ printWarning('步骤 3 失败,但不影响权限封锁');
355
+ }
115
356
 
116
- // 执行权限封锁脚本
117
- printStep('启动权限封锁脚本...');
357
+ // 完成提示
358
+ console.log('');
359
+ console.log('========================================');
360
+ printSuccess('权限封锁完成!');
361
+ console.log('========================================');
362
+ console.log('');
363
+ printWarning('需要重启 WSL2 才能使禁用挂载生效!');
364
+ console.log('');
365
+ console.log(' 请在 PowerShell 中运行:');
366
+ console.log(' ' + C.y + 'wsl --shutdown' + C.nc);
367
+ console.log(' 然后重新点击桌面图标启动。');
368
+ console.log('');
369
+ console.log('验证方法:');
370
+ console.log(' - WSL 内 ls /mnt/c 应该显示目录不存在');
371
+ console.log(' - 双击桌面「OpenClaw 工作目录」可打开工作文件夹');
118
372
  console.log('');
119
-
120
- runInWSL(SCRIPT_PATH);
121
373
  }
122
374
 
123
375
  module.exports = { run };