@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.
- package/package.json +1 -1
- package/restrict.js +293 -41
package/package.json
CHANGED
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
|
-
*
|
|
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
|
|
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
|
-
//
|
|
56
|
-
function
|
|
62
|
+
// 在 WSL 中执行命令,返回输出
|
|
63
|
+
function wslExec(cmd) {
|
|
57
64
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
74
|
+
// 在 WSL 中以 root 执行命令
|
|
75
|
+
function wslExecSudo(cmd) {
|
|
67
76
|
try {
|
|
68
|
-
|
|
69
|
-
execSync(
|
|
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
|
-
|
|
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
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
//
|
|
104
|
-
if (!
|
|
105
|
-
printError('
|
|
106
|
-
|
|
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
|
-
|
|
114
|
-
|
|
352
|
+
// 步骤 3
|
|
353
|
+
if (!step3_createShortcut(info.wslUser)) {
|
|
354
|
+
printWarning('步骤 3 失败,但不影响权限封锁');
|
|
355
|
+
}
|
|
115
356
|
|
|
116
|
-
//
|
|
117
|
-
|
|
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 };
|