snow-ai 0.4.10 → 0.4.12
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/dist/app.js +7 -4
- package/dist/i18n/lang/en.js +0 -3
- package/dist/i18n/lang/es.js +0 -3
- package/dist/i18n/lang/ja.js +0 -3
- package/dist/i18n/lang/ko.js +0 -3
- package/dist/i18n/lang/zh-TW.js +0 -3
- package/dist/i18n/lang/zh.js +0 -3
- package/dist/i18n/types.d.ts +0 -3
- package/dist/ui/pages/ConfigScreen.js +8 -6
- package/dist/ui/pages/WelcomeScreen.js +50 -32
- package/package.json +4 -2
- package/scripts/postinstall.cjs +106 -0
package/dist/app.js
CHANGED
|
@@ -2,8 +2,9 @@ import React, { useState, useEffect, Suspense } from 'react';
|
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { Alert } from '@inkjs/ui';
|
|
4
4
|
import Spinner from 'ink-spinner';
|
|
5
|
-
|
|
6
|
-
//
|
|
5
|
+
// Lazy load all page components to improve startup time
|
|
6
|
+
// Only load components when they are actually needed
|
|
7
|
+
const WelcomeScreen = React.lazy(() => import('./ui/pages/WelcomeScreen.js'));
|
|
7
8
|
const ChatScreen = React.lazy(() => import('./ui/pages/ChatScreen.js'));
|
|
8
9
|
const HeadlessModeScreen = React.lazy(() => import('./ui/pages/HeadlessModeScreen.js'));
|
|
9
10
|
const MCPConfigScreen = React.lazy(() => import('./ui/pages/MCPConfigScreen.js'));
|
|
@@ -64,7 +65,8 @@ function AppContent({ version, skipWelcome, }) {
|
|
|
64
65
|
React.createElement(Text, null, " Loading...")));
|
|
65
66
|
switch (currentView) {
|
|
66
67
|
case 'welcome':
|
|
67
|
-
return (React.createElement(
|
|
68
|
+
return (React.createElement(Suspense, { fallback: loadingFallback },
|
|
69
|
+
React.createElement(WelcomeScreen, { version: version, onMenuSelect: handleMenuSelect })));
|
|
68
70
|
case 'chat':
|
|
69
71
|
return (React.createElement(Suspense, { fallback: loadingFallback },
|
|
70
72
|
React.createElement(ChatScreen, { key: chatScreenKey, skipWelcome: skipWelcome })));
|
|
@@ -82,7 +84,8 @@ function AppContent({ version, skipWelcome, }) {
|
|
|
82
84
|
return (React.createElement(Suspense, { fallback: loadingFallback },
|
|
83
85
|
React.createElement(CustomHeadersScreen, { onBack: () => setCurrentView('welcome') })));
|
|
84
86
|
default:
|
|
85
|
-
return (React.createElement(
|
|
87
|
+
return (React.createElement(Suspense, { fallback: loadingFallback },
|
|
88
|
+
React.createElement(WelcomeScreen, { version: version, onMenuSelect: handleMenuSelect })));
|
|
86
89
|
}
|
|
87
90
|
};
|
|
88
91
|
return (React.createElement(Box, { flexDirection: "column", width: terminalWidth },
|
package/dist/i18n/lang/en.js
CHANGED
|
@@ -139,9 +139,6 @@ export const en = {
|
|
|
139
139
|
disabled: '[ ] Disabled',
|
|
140
140
|
toggleHint: '(Press Enter to toggle)',
|
|
141
141
|
enterValue: 'Enter value:',
|
|
142
|
-
low: 'Low',
|
|
143
|
-
medium: 'Medium',
|
|
144
|
-
high: 'High',
|
|
145
142
|
createNewProfile: 'Create New Profile',
|
|
146
143
|
enterProfileName: 'Enter a name for the new configuration profile',
|
|
147
144
|
profileNamePlaceholder: 'e.g., work, personal, test',
|
package/dist/i18n/lang/es.js
CHANGED
|
@@ -139,9 +139,6 @@ export const es = {
|
|
|
139
139
|
disabled: '[ ] Deshabilitado',
|
|
140
140
|
toggleHint: '(Presiona Enter para alternar)',
|
|
141
141
|
enterValue: 'Ingresa valor:',
|
|
142
|
-
low: 'Bajo',
|
|
143
|
-
medium: 'Medio',
|
|
144
|
-
high: 'Alto',
|
|
145
142
|
createNewProfile: 'Crear Nuevo Perfil',
|
|
146
143
|
enterProfileName: 'Ingresa el nombre del nuevo perfil',
|
|
147
144
|
profileNamePlaceholder: 'Ej: work, personal, test',
|
package/dist/i18n/lang/ja.js
CHANGED
|
@@ -139,9 +139,6 @@ export const ja = {
|
|
|
139
139
|
disabled: '[ ] 無効',
|
|
140
140
|
toggleHint: '(Enterキーで切り替え)',
|
|
141
141
|
enterValue: '値を入力:',
|
|
142
|
-
low: '低',
|
|
143
|
-
medium: '中',
|
|
144
|
-
high: '高',
|
|
145
142
|
createNewProfile: '新しいプロファイルを作成',
|
|
146
143
|
enterProfileName: '新しいプロファイルの名前を入力',
|
|
147
144
|
profileNamePlaceholder: '例: work, personal, test',
|
package/dist/i18n/lang/ko.js
CHANGED
|
@@ -139,9 +139,6 @@ export const ko = {
|
|
|
139
139
|
disabled: '[ ] 비활성화됨',
|
|
140
140
|
toggleHint: '(Enter 키로 토글)',
|
|
141
141
|
enterValue: '값 입력:',
|
|
142
|
-
low: '낮음',
|
|
143
|
-
medium: '중간',
|
|
144
|
-
high: '높음',
|
|
145
142
|
createNewProfile: '새 프로필 생성',
|
|
146
143
|
enterProfileName: '새 프로필 이름 입력',
|
|
147
144
|
profileNamePlaceholder: '예: work, personal, test',
|
package/dist/i18n/lang/zh-TW.js
CHANGED
|
@@ -139,9 +139,6 @@ export const zhTW = {
|
|
|
139
139
|
disabled: '[ ] 已停用',
|
|
140
140
|
toggleHint: '(按 Enter 切換)',
|
|
141
141
|
enterValue: '輸入值:',
|
|
142
|
-
low: '低',
|
|
143
|
-
medium: '中',
|
|
144
|
-
high: '高',
|
|
145
142
|
createNewProfile: '建立新配置',
|
|
146
143
|
enterProfileName: '輸入新配置的名稱',
|
|
147
144
|
profileNamePlaceholder: '例如: work, personal, test',
|
package/dist/i18n/lang/zh.js
CHANGED
|
@@ -139,9 +139,6 @@ export const zh = {
|
|
|
139
139
|
disabled: '[ ] 已禁用',
|
|
140
140
|
toggleHint: '(按 Enter 切换)',
|
|
141
141
|
enterValue: '输入值:',
|
|
142
|
-
low: '低',
|
|
143
|
-
medium: '中',
|
|
144
|
-
high: '高',
|
|
145
142
|
createNewProfile: '创建新配置',
|
|
146
143
|
enterProfileName: '输入新配置的名称',
|
|
147
144
|
profileNamePlaceholder: '例如: work, personal, test',
|
package/dist/i18n/types.d.ts
CHANGED
|
@@ -140,9 +140,6 @@ export type TranslationKeys = {
|
|
|
140
140
|
disabled: string;
|
|
141
141
|
toggleHint: string;
|
|
142
142
|
enterValue: string;
|
|
143
|
-
low: string;
|
|
144
|
-
medium: string;
|
|
145
|
-
high: string;
|
|
146
143
|
createNewProfile: string;
|
|
147
144
|
enterProfileName: string;
|
|
148
145
|
profileNamePlaceholder: string;
|
|
@@ -555,9 +555,10 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
555
555
|
React.createElement(Text, { color: theme.colors.menuSecondary }, responsesReasoningEffort.toUpperCase()))),
|
|
556
556
|
isCurrentlyEditing && (React.createElement(Box, { marginLeft: 3 },
|
|
557
557
|
React.createElement(Select, { options: [
|
|
558
|
-
{ label:
|
|
559
|
-
{ label:
|
|
560
|
-
{ label:
|
|
558
|
+
{ label: 'LOW', value: 'low' },
|
|
559
|
+
{ label: 'MEDIUM', value: 'medium' },
|
|
560
|
+
{ label: 'HIGH', value: 'high' },
|
|
561
|
+
{ label: 'XHIGH', value: 'xhigh' },
|
|
561
562
|
], defaultValue: responsesReasoningEffort, onChange: value => {
|
|
562
563
|
setResponsesReasoningEffort(value);
|
|
563
564
|
setIsEditing(false);
|
|
@@ -1046,9 +1047,10 @@ export default function ConfigScreen({ onBack, onSave, inlineMode = false, }) {
|
|
|
1046
1047
|
searchTerm),
|
|
1047
1048
|
React.createElement(Select, { options: getCurrentOptions(), defaultValue: getCurrentValue(), onChange: handleModelChange }))),
|
|
1048
1049
|
currentField === 'responsesReasoningEffort' && (React.createElement(Select, { options: [
|
|
1049
|
-
{ label:
|
|
1050
|
-
{ label:
|
|
1051
|
-
{ label:
|
|
1050
|
+
{ label: 'LOW', value: 'low' },
|
|
1051
|
+
{ label: 'MEDIUM', value: 'medium' },
|
|
1052
|
+
{ label: 'HIGH', value: 'high' },
|
|
1053
|
+
{ label: 'XHIGH', value: 'xhigh' },
|
|
1052
1054
|
], defaultValue: responsesReasoningEffort, onChange: value => {
|
|
1053
1055
|
setResponsesReasoningEffort(value);
|
|
1054
1056
|
setIsEditing(false);
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import React, { useState, useMemo, useCallback, useEffect, useRef } from 'react';
|
|
1
|
+
import React, { useState, useMemo, useCallback, useEffect, useRef, Suspense } from 'react';
|
|
2
2
|
import { Box, Text, useStdout, Static } from 'ink';
|
|
3
3
|
import { Alert } from '@inkjs/ui';
|
|
4
4
|
import Gradient from 'ink-gradient';
|
|
5
5
|
import ansiEscapes from 'ansi-escapes';
|
|
6
|
+
import Spinner from 'ink-spinner';
|
|
6
7
|
import Menu from '../components/Menu.js';
|
|
7
8
|
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
|
8
|
-
import ConfigScreen from './ConfigScreen.js';
|
|
9
|
-
import ProxyConfigScreen from './ProxyConfigScreen.js';
|
|
10
|
-
import SubAgentConfigScreen from './SubAgentConfigScreen.js';
|
|
11
|
-
import SubAgentListScreen from './SubAgentListScreen.js';
|
|
12
|
-
import SensitiveCommandConfigScreen from './SensitiveCommandConfigScreen.js';
|
|
13
|
-
import CodeBaseConfigScreen from './CodeBaseConfigScreen.js';
|
|
14
|
-
import SystemPromptConfigScreen from './SystemPromptConfigScreen.js';
|
|
15
|
-
import CustomHeadersScreen from './CustomHeadersScreen.js';
|
|
16
|
-
import LanguageSettingsScreen from './LanguageSettingsScreen.js';
|
|
17
|
-
import ThemeSettingsScreen from './ThemeSettingsScreen.js';
|
|
18
9
|
import { useI18n } from '../../i18n/index.js';
|
|
10
|
+
// Lazy load all configuration screens for better startup performance
|
|
11
|
+
const ConfigScreen = React.lazy(() => import('./ConfigScreen.js'));
|
|
12
|
+
const ProxyConfigScreen = React.lazy(() => import('./ProxyConfigScreen.js'));
|
|
13
|
+
const SubAgentConfigScreen = React.lazy(() => import('./SubAgentConfigScreen.js'));
|
|
14
|
+
const SubAgentListScreen = React.lazy(() => import('./SubAgentListScreen.js'));
|
|
15
|
+
const SensitiveCommandConfigScreen = React.lazy(() => import('./SensitiveCommandConfigScreen.js'));
|
|
16
|
+
const CodeBaseConfigScreen = React.lazy(() => import('./CodeBaseConfigScreen.js'));
|
|
17
|
+
const SystemPromptConfigScreen = React.lazy(() => import('./SystemPromptConfigScreen.js'));
|
|
18
|
+
const CustomHeadersScreen = React.lazy(() => import('./CustomHeadersScreen.js'));
|
|
19
|
+
const LanguageSettingsScreen = React.lazy(() => import('./LanguageSettingsScreen.js'));
|
|
20
|
+
const ThemeSettingsScreen = React.lazy(() => import('./ThemeSettingsScreen.js'));
|
|
19
21
|
export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
|
|
20
22
|
const { t } = useI18n();
|
|
21
23
|
const [infoText, setInfoText] = useState(t.welcome.startChatInfo);
|
|
@@ -158,6 +160,11 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
|
|
|
158
160
|
clearTimeout(handler);
|
|
159
161
|
};
|
|
160
162
|
}, [terminalWidth]); // Remove stdout from dependencies to avoid loops
|
|
163
|
+
// Loading fallback component for lazy-loaded screens
|
|
164
|
+
const loadingFallback = (React.createElement(Box, { paddingX: 1 },
|
|
165
|
+
React.createElement(Text, { color: "cyan" },
|
|
166
|
+
React.createElement(Spinner, { type: "dots" })),
|
|
167
|
+
React.createElement(Text, null, " Loading...")));
|
|
161
168
|
return (React.createElement(Box, { flexDirection: "column", width: terminalWidth },
|
|
162
169
|
React.createElement(Static, { key: remountKey, items: [
|
|
163
170
|
React.createElement(Box, { key: "welcome-header", flexDirection: "row", paddingLeft: 2, paddingTop: 1, paddingBottom: 0, width: terminalWidth },
|
|
@@ -175,25 +182,36 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
|
|
|
175
182
|
React.createElement(Menu, { options: menuOptions, onSelect: handleInlineMenuSelect, onSelectionChange: handleSelectionChange })))),
|
|
176
183
|
inlineView === 'menu' && (React.createElement(Box, { paddingX: 1 },
|
|
177
184
|
React.createElement(Alert, { variant: "info" }, infoText))),
|
|
178
|
-
inlineView === 'config' && (React.createElement(
|
|
179
|
-
React.createElement(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
inlineView === '
|
|
185
|
-
React.createElement(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
inlineView === '
|
|
191
|
-
React.createElement(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
inlineView === '
|
|
197
|
-
React.createElement(
|
|
198
|
-
|
|
185
|
+
inlineView === 'config' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
186
|
+
React.createElement(Box, { paddingX: 1 },
|
|
187
|
+
React.createElement(ConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true })))),
|
|
188
|
+
inlineView === 'proxy-config' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
189
|
+
React.createElement(Box, { paddingX: 1 },
|
|
190
|
+
React.createElement(ProxyConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true })))),
|
|
191
|
+
inlineView === 'codebase-config' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
192
|
+
React.createElement(Box, { paddingX: 1 },
|
|
193
|
+
React.createElement(CodeBaseConfigScreen, { onBack: handleBackToMenu, onSave: handleConfigSave, inlineMode: true })))),
|
|
194
|
+
inlineView === 'subagent-list' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
195
|
+
React.createElement(Box, { paddingX: 1 },
|
|
196
|
+
React.createElement(SubAgentListScreen, { onBack: handleBackToMenu, onAdd: handleSubAgentAdd, onEdit: handleSubAgentEdit, inlineMode: true })))),
|
|
197
|
+
inlineView === 'subagent-add' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
198
|
+
React.createElement(Box, { paddingX: 1 },
|
|
199
|
+
React.createElement(SubAgentConfigScreen, { onBack: () => setInlineView('subagent-list'), onSave: handleSubAgentSave, inlineMode: true })))),
|
|
200
|
+
inlineView === 'subagent-edit' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
201
|
+
React.createElement(Box, { paddingX: 1 },
|
|
202
|
+
React.createElement(SubAgentConfigScreen, { onBack: () => setInlineView('subagent-list'), onSave: handleSubAgentSave, agentId: editingAgentId, inlineMode: true })))),
|
|
203
|
+
inlineView === 'sensitive-commands' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
204
|
+
React.createElement(Box, { paddingX: 1 },
|
|
205
|
+
React.createElement(SensitiveCommandConfigScreen, { onBack: handleBackToMenu, inlineMode: true })))),
|
|
206
|
+
inlineView === 'systemprompt' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
207
|
+
React.createElement(Box, { paddingX: 1 },
|
|
208
|
+
React.createElement(SystemPromptConfigScreen, { onBack: handleBackToMenu })))),
|
|
209
|
+
inlineView === 'customheaders' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
210
|
+
React.createElement(Box, { paddingX: 1 },
|
|
211
|
+
React.createElement(CustomHeadersScreen, { onBack: handleBackToMenu })))),
|
|
212
|
+
inlineView === 'language-settings' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
213
|
+
React.createElement(Box, { paddingX: 1 },
|
|
214
|
+
React.createElement(LanguageSettingsScreen, { onBack: handleBackToMenu, inlineMode: true })))),
|
|
215
|
+
inlineView === 'theme-settings' && (React.createElement(Suspense, { fallback: loadingFallback },
|
|
216
|
+
React.createElement(ThemeSettingsScreen, { onBack: handleBackToMenu, inlineMode: true })))));
|
|
199
217
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-ai",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.12",
|
|
4
4
|
"description": "Intelligent Command Line Assistant powered by AI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -32,12 +32,14 @@
|
|
|
32
32
|
"dev": "tsc --watch",
|
|
33
33
|
"start": "node dist/cli.js",
|
|
34
34
|
"prepublishOnly": "npm run build",
|
|
35
|
+
"postinstall": "node scripts/postinstall.cjs",
|
|
35
36
|
"test": "prettier --check . && xo && ava",
|
|
36
37
|
"lint": "xo",
|
|
37
38
|
"format": "prettier --write ."
|
|
38
39
|
},
|
|
39
40
|
"files": [
|
|
40
|
-
"dist"
|
|
41
|
+
"dist",
|
|
42
|
+
"scripts"
|
|
41
43
|
],
|
|
42
44
|
"dependencies": {
|
|
43
45
|
"@inkjs/ui": "^2.0.0",
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post-install script to provide installation optimization tips for users
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const https = require('https');
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
|
|
10
|
+
// ANSI color codes
|
|
11
|
+
const colors = {
|
|
12
|
+
reset: '\x1b[0m',
|
|
13
|
+
bright: '\x1b[1m',
|
|
14
|
+
cyan: '\x1b[36m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
green: '\x1b[32m',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Detect if user is in China based on IP geolocation
|
|
21
|
+
*/
|
|
22
|
+
function detectRegion() {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const timeout = setTimeout(() => resolve('unknown'), 3000);
|
|
25
|
+
|
|
26
|
+
https.get('https://ipapi.co/json/', (res) => {
|
|
27
|
+
let data = '';
|
|
28
|
+
res.on('data', (chunk) => data += chunk);
|
|
29
|
+
res.on('end', () => {
|
|
30
|
+
clearTimeout(timeout);
|
|
31
|
+
try {
|
|
32
|
+
const info = JSON.parse(data);
|
|
33
|
+
resolve(info.country_code === 'CN' ? 'china' : 'other');
|
|
34
|
+
} catch {
|
|
35
|
+
resolve('unknown');
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}).on('error', () => {
|
|
39
|
+
clearTimeout(timeout);
|
|
40
|
+
resolve('unknown');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check current npm registry
|
|
47
|
+
*/
|
|
48
|
+
function getCurrentRegistry() {
|
|
49
|
+
try {
|
|
50
|
+
const registry = execSync('npm config get registry', { encoding: 'utf8' }).trim();
|
|
51
|
+
return registry;
|
|
52
|
+
} catch {
|
|
53
|
+
return 'https://registry.npmjs.org';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Main function
|
|
59
|
+
*/
|
|
60
|
+
async function main() {
|
|
61
|
+
// Skip if running in CI environment
|
|
62
|
+
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const currentRegistry = getCurrentRegistry();
|
|
67
|
+
const isUsingMirror = currentRegistry.includes('npmmirror.com') ||
|
|
68
|
+
currentRegistry.includes('taobao.org');
|
|
69
|
+
|
|
70
|
+
// If already using a mirror, skip the tips
|
|
71
|
+
if (isUsingMirror) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`\n${colors.cyan}${colors.bright}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}`);
|
|
76
|
+
console.log(`${colors.cyan}${colors.bright} Snow AI - Installation Optimization Tips${colors.reset}`);
|
|
77
|
+
console.log(`${colors.cyan}${colors.bright}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}\n`);
|
|
78
|
+
|
|
79
|
+
const region = await detectRegion();
|
|
80
|
+
|
|
81
|
+
if (region === 'china') {
|
|
82
|
+
console.log(`${colors.yellow}检测到您在中国大陆地区,建议配置 npm 镜像源以加速安装:${colors.reset}\n`);
|
|
83
|
+
console.log(`${colors.green}# 方案 1: 使用淘宝镜像 (推荐)${colors.reset}`);
|
|
84
|
+
console.log(` npm config set registry https://registry.npmmirror.com\n`);
|
|
85
|
+
console.log(`${colors.green}# 方案 2: 临时使用镜像安装${colors.reset}`);
|
|
86
|
+
console.log(` npm install -g snow-ai --registry=https://registry.npmmirror.com\n`);
|
|
87
|
+
console.log(`${colors.green}# 恢复官方源${colors.reset}`);
|
|
88
|
+
console.log(` npm config set registry https://registry.npmjs.org\n`);
|
|
89
|
+
} else {
|
|
90
|
+
console.log(`${colors.yellow}To speed up npm installation, you can:${colors.reset}\n`);
|
|
91
|
+
console.log(`${colors.green}# Enable parallel downloads${colors.reset}`);
|
|
92
|
+
console.log(` npm config set maxsockets 10\n`);
|
|
93
|
+
console.log(`${colors.green}# Use offline cache when possible${colors.reset}`);
|
|
94
|
+
console.log(` npm config set prefer-offline true\n`);
|
|
95
|
+
console.log(`${colors.green}# Skip unnecessary checks${colors.reset}`);
|
|
96
|
+
console.log(` npm config set audit false\n`);
|
|
97
|
+
console.log(` npm config set fund false\n`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log(`${colors.cyan}Current registry: ${currentRegistry}${colors.reset}`);
|
|
101
|
+
console.log(`${colors.cyan}${colors.bright}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${colors.reset}\n`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
main().catch(() => {
|
|
105
|
+
// Silently fail - don't break installation
|
|
106
|
+
});
|