snow-ai 0.3.23 → 0.3.24

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.
@@ -0,0 +1,53 @@
1
+ export interface SensitiveCommand {
2
+ id: string;
3
+ pattern: string;
4
+ description: string;
5
+ enabled: boolean;
6
+ isPreset: boolean;
7
+ }
8
+ export interface SensitiveCommandsConfig {
9
+ commands: SensitiveCommand[];
10
+ }
11
+ /**
12
+ * 预设的常见敏感指令
13
+ */
14
+ export declare const PRESET_SENSITIVE_COMMANDS: SensitiveCommand[];
15
+ /**
16
+ * Load sensitive commands configuration
17
+ */
18
+ export declare function loadSensitiveCommands(): SensitiveCommandsConfig;
19
+ /**
20
+ * Save sensitive commands configuration
21
+ */
22
+ export declare function saveSensitiveCommands(config: SensitiveCommandsConfig): void;
23
+ /**
24
+ * Add a custom sensitive command
25
+ */
26
+ export declare function addSensitiveCommand(pattern: string, description: string): void;
27
+ /**
28
+ * Remove a sensitive command
29
+ */
30
+ export declare function removeSensitiveCommand(id: string): void;
31
+ /**
32
+ * Update a sensitive command
33
+ */
34
+ export declare function updateSensitiveCommand(id: string, updates: Partial<Omit<SensitiveCommand, 'id' | 'isPreset'>>): void;
35
+ /**
36
+ * Toggle a sensitive command enabled state
37
+ */
38
+ export declare function toggleSensitiveCommand(id: string): void;
39
+ /**
40
+ * Check if a command matches any enabled sensitive pattern
41
+ */
42
+ export declare function isSensitiveCommand(command: string): {
43
+ isSensitive: boolean;
44
+ matchedCommand?: SensitiveCommand;
45
+ };
46
+ /**
47
+ * Get all sensitive commands
48
+ */
49
+ export declare function getAllSensitiveCommands(): SensitiveCommand[];
50
+ /**
51
+ * Reset to default preset commands
52
+ */
53
+ export declare function resetToDefaults(): void;
@@ -0,0 +1,308 @@
1
+ import { homedir } from 'os';
2
+ import { join } from 'path';
3
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
4
+ const CONFIG_DIR = join(homedir(), '.snow');
5
+ const SENSITIVE_COMMANDS_FILE = join(CONFIG_DIR, 'sensitive-commands.json');
6
+ /**
7
+ * 预设的常见敏感指令
8
+ */
9
+ export const PRESET_SENSITIVE_COMMANDS = [
10
+ {
11
+ id: 'rm',
12
+ pattern: 'rm*',
13
+ description: 'Delete files or directories (rm, rm -rf, etc.)',
14
+ enabled: true,
15
+ isPreset: true,
16
+ },
17
+ {
18
+ id: 'rmdir',
19
+ pattern: 'rmdir*',
20
+ description: 'Remove directories',
21
+ enabled: true,
22
+ isPreset: true,
23
+ },
24
+ {
25
+ id: 'mv-to-trash',
26
+ pattern: 'mv * /tmp*',
27
+ description: 'Move files to trash/tmp (potential data loss)',
28
+ enabled: true,
29
+ isPreset: true,
30
+ },
31
+ {
32
+ id: 'chmod',
33
+ pattern: 'chmod*',
34
+ description: 'Change file permissions',
35
+ enabled: true,
36
+ isPreset: true,
37
+ },
38
+ {
39
+ id: 'chown',
40
+ pattern: 'chown*',
41
+ description: 'Change file ownership',
42
+ enabled: true,
43
+ isPreset: true,
44
+ },
45
+ {
46
+ id: 'dd',
47
+ pattern: 'dd*',
48
+ description: 'Low-level data copy (disk operations)',
49
+ enabled: true,
50
+ isPreset: true,
51
+ },
52
+ {
53
+ id: 'mkfs',
54
+ pattern: 'mkfs*',
55
+ description: 'Format filesystem',
56
+ enabled: true,
57
+ isPreset: true,
58
+ },
59
+ {
60
+ id: 'fdisk',
61
+ pattern: 'fdisk*',
62
+ description: 'Disk partition manipulation',
63
+ enabled: true,
64
+ isPreset: true,
65
+ },
66
+ {
67
+ id: 'killall',
68
+ pattern: 'killall*',
69
+ description: 'Kill all processes by name',
70
+ enabled: true,
71
+ isPreset: true,
72
+ },
73
+ {
74
+ id: 'pkill',
75
+ pattern: 'pkill*',
76
+ description: 'Kill processes by pattern',
77
+ enabled: true,
78
+ isPreset: true,
79
+ },
80
+ {
81
+ id: 'reboot',
82
+ pattern: 'reboot*',
83
+ description: 'Reboot the system',
84
+ enabled: true,
85
+ isPreset: true,
86
+ },
87
+ {
88
+ id: 'shutdown',
89
+ pattern: 'shutdown*',
90
+ description: 'Shutdown the system',
91
+ enabled: true,
92
+ isPreset: true,
93
+ },
94
+ {
95
+ id: 'sudo',
96
+ pattern: 'sudo*',
97
+ description: 'Execute commands with superuser privileges',
98
+ enabled: true,
99
+ isPreset: true,
100
+ },
101
+ {
102
+ id: 'su',
103
+ pattern: 'su*',
104
+ description: 'Switch user',
105
+ enabled: true,
106
+ isPreset: true,
107
+ },
108
+ {
109
+ id: 'curl-post',
110
+ pattern: 'curl*-X POST*',
111
+ description: 'HTTP POST requests (potential data transmission)',
112
+ enabled: false,
113
+ isPreset: true,
114
+ },
115
+ {
116
+ id: 'wget',
117
+ pattern: 'wget*',
118
+ description: 'Download files from internet',
119
+ enabled: false,
120
+ isPreset: true,
121
+ },
122
+ {
123
+ id: 'git-push',
124
+ pattern: 'git push*',
125
+ description: 'Push code to remote repository',
126
+ enabled: false,
127
+ isPreset: true,
128
+ },
129
+ {
130
+ id: 'git-force-push',
131
+ pattern: 'git push*--force*',
132
+ description: 'Force push to remote repository (destructive)',
133
+ enabled: true,
134
+ isPreset: true,
135
+ },
136
+ {
137
+ id: 'npm-publish',
138
+ pattern: 'npm publish*',
139
+ description: 'Publish package to npm registry',
140
+ enabled: true,
141
+ isPreset: true,
142
+ },
143
+ {
144
+ id: 'docker-rm',
145
+ pattern: 'docker rm*',
146
+ description: 'Remove Docker containers',
147
+ enabled: false,
148
+ isPreset: true,
149
+ },
150
+ {
151
+ id: 'docker-rmi',
152
+ pattern: 'docker rmi*',
153
+ description: 'Remove Docker images',
154
+ enabled: false,
155
+ isPreset: true,
156
+ },
157
+ ];
158
+ /**
159
+ * Ensure config directory exists
160
+ */
161
+ function ensureConfigDirectory() {
162
+ if (!existsSync(CONFIG_DIR)) {
163
+ mkdirSync(CONFIG_DIR, { recursive: true });
164
+ }
165
+ }
166
+ /**
167
+ * Load sensitive commands configuration
168
+ */
169
+ export function loadSensitiveCommands() {
170
+ ensureConfigDirectory();
171
+ if (!existsSync(SENSITIVE_COMMANDS_FILE)) {
172
+ // 首次加载,使用预设配置
173
+ const defaultConfig = {
174
+ commands: [...PRESET_SENSITIVE_COMMANDS],
175
+ };
176
+ saveSensitiveCommands(defaultConfig);
177
+ return defaultConfig;
178
+ }
179
+ try {
180
+ const configData = readFileSync(SENSITIVE_COMMANDS_FILE, 'utf8');
181
+ const config = JSON.parse(configData);
182
+ // 合并预设命令(处理新增的预设命令)
183
+ const existingIds = new Set(config.commands.map(cmd => cmd.id));
184
+ const newPresets = PRESET_SENSITIVE_COMMANDS.filter(preset => !existingIds.has(preset.id));
185
+ if (newPresets.length > 0) {
186
+ config.commands = [...config.commands, ...newPresets];
187
+ saveSensitiveCommands(config);
188
+ }
189
+ return config;
190
+ }
191
+ catch (error) {
192
+ console.error('Failed to load sensitive commands config:', error);
193
+ return { commands: [...PRESET_SENSITIVE_COMMANDS] };
194
+ }
195
+ }
196
+ /**
197
+ * Save sensitive commands configuration
198
+ */
199
+ export function saveSensitiveCommands(config) {
200
+ ensureConfigDirectory();
201
+ try {
202
+ const configData = JSON.stringify(config, null, 2);
203
+ writeFileSync(SENSITIVE_COMMANDS_FILE, configData, 'utf8');
204
+ }
205
+ catch (error) {
206
+ throw new Error(`Failed to save sensitive commands config: ${error}`);
207
+ }
208
+ }
209
+ /**
210
+ * Add a custom sensitive command
211
+ */
212
+ export function addSensitiveCommand(pattern, description) {
213
+ const config = loadSensitiveCommands();
214
+ // 生成唯一ID
215
+ const id = `custom-${Date.now()}-${Math.random()
216
+ .toString(36)
217
+ .substring(2, 9)}`;
218
+ config.commands.push({
219
+ id,
220
+ pattern,
221
+ description,
222
+ enabled: true,
223
+ isPreset: false,
224
+ });
225
+ saveSensitiveCommands(config);
226
+ }
227
+ /**
228
+ * Remove a sensitive command
229
+ */
230
+ export function removeSensitiveCommand(id) {
231
+ const config = loadSensitiveCommands();
232
+ config.commands = config.commands.filter(cmd => cmd.id !== id);
233
+ saveSensitiveCommands(config);
234
+ }
235
+ /**
236
+ * Update a sensitive command
237
+ */
238
+ export function updateSensitiveCommand(id, updates) {
239
+ const config = loadSensitiveCommands();
240
+ const commandIndex = config.commands.findIndex(cmd => cmd.id === id);
241
+ if (commandIndex === -1) {
242
+ throw new Error(`Sensitive command with id "${id}" not found`);
243
+ }
244
+ const existingCommand = config.commands[commandIndex];
245
+ config.commands[commandIndex] = {
246
+ ...existingCommand,
247
+ ...updates,
248
+ id: existingCommand.id,
249
+ isPreset: existingCommand.isPreset,
250
+ };
251
+ saveSensitiveCommands(config);
252
+ }
253
+ /**
254
+ * Toggle a sensitive command enabled state
255
+ */
256
+ export function toggleSensitiveCommand(id) {
257
+ const config = loadSensitiveCommands();
258
+ const command = config.commands.find(cmd => cmd.id === id);
259
+ if (!command) {
260
+ throw new Error(`Sensitive command with id "${id}" not found`);
261
+ }
262
+ command.enabled = !command.enabled;
263
+ saveSensitiveCommands(config);
264
+ }
265
+ /**
266
+ * 将通配符模式转换为正则表达式
267
+ * 支持 * 通配符
268
+ */
269
+ function patternToRegex(pattern) {
270
+ // 转义特殊字符,除了 * 和空格
271
+ const escaped = pattern
272
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&')
273
+ .replace(/\*/g, '.*');
274
+ // 使用 ^ 和 $ 确保完整匹配,但允许参数
275
+ return new RegExp(`^${escaped}`, 'i');
276
+ }
277
+ /**
278
+ * Check if a command matches any enabled sensitive pattern
279
+ */
280
+ export function isSensitiveCommand(command) {
281
+ const config = loadSensitiveCommands();
282
+ const enabledCommands = config.commands.filter(cmd => cmd.enabled);
283
+ // 清理命令,移除多余的空格
284
+ const cleanCommand = command.trim().replace(/\s+/g, ' ');
285
+ for (const cmd of enabledCommands) {
286
+ const regex = patternToRegex(cmd.pattern);
287
+ if (regex.test(cleanCommand)) {
288
+ return { isSensitive: true, matchedCommand: cmd };
289
+ }
290
+ }
291
+ return { isSensitive: false };
292
+ }
293
+ /**
294
+ * Get all sensitive commands
295
+ */
296
+ export function getAllSensitiveCommands() {
297
+ const config = loadSensitiveCommands();
298
+ return config.commands;
299
+ }
300
+ /**
301
+ * Reset to default preset commands
302
+ */
303
+ export function resetToDefaults() {
304
+ const config = {
305
+ commands: [...PRESET_SENSITIVE_COMMANDS],
306
+ };
307
+ saveSensitiveCommands(config);
308
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.3.23",
3
+ "version": "0.3.24",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -89,7 +89,9 @@
89
89
  "extends": "xo-react",
90
90
  "prettier": true,
91
91
  "rules": {
92
- "react/prop-types": "off"
92
+ "react/prop-types": "off",
93
+ "react-hooks/rules-of-hooks": "error",
94
+ "react-hooks/exhaustive-deps": "warn"
93
95
  }
94
96
  },
95
97
  "prettier": "@vdemedes/prettier-config"