@anh3d0nic/qwen-code-termux-ice 21.0.0 → 21.1.0
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/bin/qwen-ice +127 -331
- package/core/ice-core.js +101 -0
- package/package.json +2 -1
- package/scripts/postinstall.cjs +80 -207
package/bin/qwen-ice
CHANGED
|
@@ -1,363 +1,159 @@
|
|
|
1
1
|
#!/data/data/com.termux/files/usr/bin/node
|
|
2
2
|
/**
|
|
3
|
-
* ❄️ Qwen Code ICE
|
|
4
|
-
*
|
|
3
|
+
* ❄️ Qwen Code ICE — Launcher
|
|
4
|
+
* Wraps global Qwen Code CLI with ICE enhancements
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const readline = require('readline');
|
|
10
7
|
const { spawn } = require('child_process');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const MEMORY_DIR = path.join(
|
|
16
|
-
const LOGS_DIR = path.join(INSTALL_DIR, 'logs');
|
|
17
|
-
const SCRIPTS_DIR = path.join(INSTALL_DIR, 'scripts');
|
|
18
|
-
const CORE_DIR = path.join(INSTALL_DIR, 'core');
|
|
19
|
-
|
|
20
|
-
const API_KEYS_FILE = path.join(CONFIG_DIR, 'api-keys.json');
|
|
21
|
-
const SESSION_MEMORY_FILE = path.join(MEMORY_DIR, 'session_memory.json');
|
|
22
|
-
const QUALITY_HISTORY_FILE = path.join(MEMORY_DIR, 'quality_history.json');
|
|
12
|
+
// User data directory
|
|
13
|
+
const HOME = process.env.HOME || '/data/data/com.termux/files/home';
|
|
14
|
+
const USER_ICE_DIR = path.join(HOME, '.qwen-ice-data');
|
|
15
|
+
const MEMORY_DIR = path.join(USER_ICE_DIR, 'memory');
|
|
23
16
|
const SESSION_SUMMARY_FILE = path.join(MEMORY_DIR, 'session_summary.json');
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
// Parse CLI arguments
|
|
27
|
-
const args = process.argv.slice(2);
|
|
28
|
-
const yolo = args.includes('-y') || args.includes('--yolo');
|
|
29
|
-
const versionFlag = args.includes('--version');
|
|
30
|
-
const helpFlag = args.includes('--help');
|
|
31
|
-
const statusFlag = args.includes('--status');
|
|
32
|
-
const modelIndex = args.indexOf('--model');
|
|
33
|
-
const selectedModel = modelIndex > -1 ? args[modelIndex + 1] : null;
|
|
34
|
-
|
|
35
|
-
const command = args.find(arg => !arg.startsWith('-') && args.indexOf(arg) !== modelIndex && args.indexOf(arg) !== modelIndex + 1);
|
|
36
|
-
|
|
37
|
-
function ensureDirs() {
|
|
38
|
-
[CONFIG_DIR, MEMORY_DIR, LOGS_DIR].forEach(dir => {
|
|
39
|
-
if (!fs.existsSync(dir)) {
|
|
40
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function loadApiConfig() {
|
|
46
|
-
try {
|
|
47
|
-
if (fs.existsSync(API_KEYS_FILE)) {
|
|
48
|
-
return JSON.parse(fs.readFileSync(API_KEYS_FILE, 'utf8'));
|
|
49
|
-
}
|
|
50
|
-
} catch (err) {}
|
|
51
|
-
return {
|
|
52
|
-
keys: [{
|
|
53
|
-
id: 'qwen-oauth',
|
|
54
|
-
provider: 'qwen',
|
|
55
|
-
type: 'oauth',
|
|
56
|
-
model: 'qwen3-coder-plus',
|
|
57
|
-
active: true,
|
|
58
|
-
note: 'Uses existing Qwen OAuth — do not modify'
|
|
59
|
-
}],
|
|
60
|
-
default_provider: 'qwen',
|
|
61
|
-
fallback_order: ['qwen', 'gemini', 'groq', 'openai', 'dashscope']
|
|
62
|
-
};
|
|
63
|
-
}
|
|
17
|
+
const QUALITY_FILE = path.join(MEMORY_DIR, 'quality_history.json');
|
|
18
|
+
const MEMORY_FILE = path.join(MEMORY_DIR, 'session_memory.json');
|
|
64
19
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return JSON.parse(fs.readFileSync(SESSION_MEMORY_FILE, 'utf8'));
|
|
69
|
-
}
|
|
70
|
-
} catch (err) {}
|
|
71
|
-
return { patterns: [], known_errors: [] };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function loadQualityHistory() {
|
|
75
|
-
try {
|
|
76
|
-
if (fs.existsSync(QUALITY_HISTORY_FILE)) {
|
|
77
|
-
const data = JSON.parse(fs.readFileSync(QUALITY_HISTORY_FILE, 'utf8')); return Array.isArray(data) ? { scores: data } : data;
|
|
78
|
-
}
|
|
79
|
-
} catch (err) {}
|
|
80
|
-
return { scores: [] };
|
|
81
|
-
}
|
|
20
|
+
// Ensure user data directory exists
|
|
21
|
+
if (!fs.existsSync(USER_ICE_DIR)) fs.mkdirSync(USER_ICE_DIR, { recursive: true });
|
|
22
|
+
if (!fs.existsSync(MEMORY_DIR)) fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
82
23
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
} catch (err) {}
|
|
89
|
-
return { last_session: null, total_queries: 0, total_errors: 0 };
|
|
24
|
+
// ICE Core Functions
|
|
25
|
+
function loadJson(file, defaultVal) {
|
|
26
|
+
try { if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (e) {}
|
|
27
|
+
return defaultVal;
|
|
90
28
|
}
|
|
91
29
|
|
|
92
|
-
function
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
const logLine = `[${timestamp}] ${message}\n`;
|
|
96
|
-
try { fs.appendFileSync(YOLO_LOG, logLine); } catch (e) {}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function printVersion() {
|
|
101
|
-
const config = loadApiConfig();
|
|
102
|
-
const memory = loadSessionMemory();
|
|
103
|
-
const quality = loadQualityHistory();
|
|
104
|
-
const summary = loadSessionSummary();
|
|
105
|
-
|
|
106
|
-
const providers = [...new Set((config.keys || []).map(k => k.provider))];
|
|
107
|
-
const avgQuality = quality.scores.length > 0
|
|
108
|
-
? (quality.scores.reduce((a, b) => a + (b.score || b), 0) / quality.scores.length).toFixed(1)
|
|
109
|
-
: 'N/A';
|
|
110
|
-
|
|
111
|
-
console.log(`
|
|
112
|
-
❄️ Qwen Code ICE v${VERSION}
|
|
113
|
-
📍 ${INSTALL_DIR}/
|
|
114
|
-
🤖 Default model: ${config.keys?.find(k => k.provider === config.default_provider)?.model || 'qwen3-coder-plus'} (OAuth)
|
|
115
|
-
🔑 APIs configured: ${providers.join(', ')}
|
|
116
|
-
🔄 Fallback order: ${config.fallback_order.join(' → ')}
|
|
117
|
-
🧠 Memory: ${memory.patterns?.length || 0} patterns learned
|
|
118
|
-
📊 Avg quality: ${avgQuality}/10 (last ${quality.scores.length} sessions)
|
|
119
|
-
📅 Built: 2026-03-22
|
|
120
|
-
`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function printHelp() {
|
|
124
|
-
console.log(`
|
|
125
|
-
❄️ Qwen Code ICE v${VERSION} — Help
|
|
126
|
-
|
|
127
|
-
USAGE:
|
|
128
|
-
qwen-ice Interactive mode
|
|
129
|
-
qwen-ice "query" Single query
|
|
130
|
-
qwen-ice [options] With options
|
|
131
|
-
|
|
132
|
-
OPTIONS:
|
|
133
|
-
--version Show version info
|
|
134
|
-
--help Show this help
|
|
135
|
-
--status Show pipeline status
|
|
136
|
-
-y, --yolo YOLO mode (auto-approve all)
|
|
137
|
-
--model <provider> Use specific model (qwen, groq, gemini, openai, dashscope)
|
|
138
|
-
|
|
139
|
-
COMMANDS:
|
|
140
|
-
status Show status (keys, models, memory)
|
|
141
|
-
reset Clear memory (keep API keys)
|
|
142
|
-
uninstall Clean uninstall with backup
|
|
143
|
-
|
|
144
|
-
EXAMPLES:
|
|
145
|
-
qwen-ice "fix this error" Run single query
|
|
146
|
-
qwen-ice -y "npm install" YOLO mode (auto-approve)
|
|
147
|
-
qwen-ice --model groq Use Groq provider
|
|
148
|
-
qwen-ice status Check status
|
|
149
|
-
`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function printStatus() {
|
|
153
|
-
const config = loadApiConfig();
|
|
154
|
-
const memory = loadSessionMemory();
|
|
155
|
-
const quality = loadQualityHistory();
|
|
156
|
-
const summary = loadSessionSummary();
|
|
157
|
-
|
|
158
|
-
const avgQuality = quality.scores.length > 0
|
|
159
|
-
? (quality.scores.reduce((a, b) => a + (b.score || b), 0) / quality.scores.length).toFixed(1)
|
|
160
|
-
: 'N/A';
|
|
161
|
-
|
|
162
|
-
const getDirSize = (dir) => {
|
|
163
|
-
if (!fs.existsSync(dir)) return 0;
|
|
164
|
-
let size = 0;
|
|
165
|
-
try {
|
|
166
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
167
|
-
for (const entry of entries) {
|
|
168
|
-
const filePath = path.join(dir, entry.name);
|
|
169
|
-
if (entry.isDirectory()) size += getDirSize(filePath);
|
|
170
|
-
else size += fs.statSync(filePath).size;
|
|
171
|
-
}
|
|
172
|
-
} catch (e) {}
|
|
173
|
-
return size;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const configSize = (getDirSize(CONFIG_DIR) / 1024).toFixed(1);
|
|
177
|
-
const memorySize = (getDirSize(MEMORY_DIR) / 1024).toFixed(1);
|
|
178
|
-
const logsSize = (getDirSize(LOGS_DIR) / 1024).toFixed(1);
|
|
179
|
-
|
|
180
|
-
console.log(`
|
|
181
|
-
❄️ Qwen Code ICE v${VERSION} — Status
|
|
182
|
-
|
|
183
|
-
API KEYS:
|
|
184
|
-
`);
|
|
185
|
-
|
|
186
|
-
for (const key of config.keys || []) {
|
|
187
|
-
const status = key.active ? '✅' : '❌';
|
|
188
|
-
const label = key.type === 'oauth' ? '(OAuth)' : '';
|
|
189
|
-
const calls = key.call_count || 0;
|
|
190
|
-
const lastUsed = key.last_used ? new Date(key.last_used).toLocaleDateString() : 'never';
|
|
191
|
-
console.log(`${status} ${key.id.padEnd(15)} ${label.padEnd(8)} — active, ${calls} calls${invalidMark}`);
|
|
192
|
-
if (key.last_used) console.log(` last used: ${lastUsed}`);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
console.log(`
|
|
196
|
-
MEMORY:
|
|
197
|
-
🧠 Patterns learned: ${memory.patterns?.length || 0}
|
|
198
|
-
⚠️ Known errors: ${memory.known_errors?.length || 0}
|
|
199
|
-
📊 Avg quality score: ${avgQuality}/10
|
|
200
|
-
🕐 Last session: ${summary.last_session || 'N/A'}
|
|
201
|
-
|
|
202
|
-
INSTALL:
|
|
203
|
-
📁 ${INSTALL_DIR}/
|
|
204
|
-
💾 Config: ${configSize} KB
|
|
205
|
-
🗃️ Memory: ${memorySize} KB
|
|
206
|
-
📋 Logs: ${logsSize} KB
|
|
207
|
-
|
|
208
|
-
PIPELINE:
|
|
209
|
-
1. ColdStartIntelligence
|
|
210
|
-
2. PatternPreventer
|
|
211
|
-
3. IntentLadder
|
|
212
|
-
4. ConfidenceGating
|
|
213
|
-
5. SmartContext
|
|
214
|
-
6. ApiRouter
|
|
215
|
-
7. SelfCritiqueLoop
|
|
216
|
-
8. CodeQualityGate
|
|
217
|
-
9. AutoRunLoop
|
|
218
|
-
10. QualityBlocker
|
|
219
|
-
11. DomainMemory
|
|
220
|
-
12. QualityScorer
|
|
221
|
-
13. ConfidenceRecord
|
|
222
|
-
14. SessionSummary
|
|
223
|
-
`);
|
|
30
|
+
function saveJson(file, data) {
|
|
31
|
+
if (!fs.existsSync(path.dirname(file))) fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
32
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
|
224
33
|
}
|
|
225
34
|
|
|
226
|
-
|
|
227
|
-
if (!fs.existsSync(scriptPath)) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
const child = spawn('node', [scriptPath], { stdio: 'inherit' });
|
|
231
|
-
child.on('close', (code) => process.exit(code));
|
|
232
|
-
}
|
|
35
|
+
let sessionStats = { queries: 0, patternsLearned: 0, errorsFixed: 0, intentCounts: {} };
|
|
233
36
|
|
|
234
|
-
function
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
37
|
+
function showWelcome() {
|
|
38
|
+
const memory = loadJson(MEMORY_FILE, { user_patterns: [], known_errors: [] });
|
|
39
|
+
const quality = loadJson(QUALITY_FILE, { history: [] });
|
|
40
|
+
const summary = loadJson(SESSION_SUMMARY_FILE, { total_queries: 0, avg_quality: 0 });
|
|
41
|
+
const patternsCount = memory.user_patterns?.length || 0;
|
|
42
|
+
const avgQuality = quality.history?.length > 0 ? (quality.history.reduce((a, b) => a + (b.score || b), 0) / quality.history.length).toFixed(1) : 'N/A';
|
|
43
|
+
const totalQueries = summary.total_queries || 0;
|
|
44
|
+
if (patternsCount === 0 && totalQueries === 0) {
|
|
45
|
+
return '\n❄️ Welcome to Qwen Code ICE\n🚀 Real Qwen Code session with ICE enhancements\n🔑 Qwen OAuth enabled by default\n';
|
|
239
46
|
}
|
|
240
|
-
|
|
241
|
-
child.on('close', (code) => process.exit(code));
|
|
47
|
+
return `\n❄️ Qwen Code ICE — Session ${totalQueries + 1}\n📊 Last: ${totalQueries} queries, ${avgQuality}/10 | 🧠 ${patternsCount} patterns\n`;
|
|
242
48
|
}
|
|
243
49
|
|
|
244
|
-
function
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
fs.writeFileSync(path.join(MEMORY_DIR, 'confidence_history.json'), JSON.stringify({ thresholds: {} }, null, 2));
|
|
251
|
-
fs.writeFileSync(SESSION_SUMMARY_FILE, JSON.stringify({ last_session: null, total_queries: 0, total_errors: 0 }, null, 2));
|
|
252
|
-
console.log('✅ Memory cleared (API keys preserved)');
|
|
253
|
-
} catch (err) {
|
|
254
|
-
console.error('❌ Failed to reset memory:', err.message);
|
|
50
|
+
function preventPatterns(query) {
|
|
51
|
+
let enhanced = query;
|
|
52
|
+
const warnings = [];
|
|
53
|
+
if (query.toLowerCase().includes('sudo')) {
|
|
54
|
+
enhanced = enhanced.replace(/\bsudo\b\s*/gi, '');
|
|
55
|
+
warnings.push('🛡️ Auto-removed sudo (Termux uses pkg)');
|
|
255
56
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const memory = loadSessionMemory();
|
|
260
|
-
const quality = loadQualityHistory();
|
|
261
|
-
const summary = loadSessionSummary();
|
|
262
|
-
|
|
263
|
-
const avgQuality = quality.scores.length > 0
|
|
264
|
-
? (quality.scores.reduce((a, b) => a + (b.score || b), 0) / quality.scores.length).toFixed(1)
|
|
265
|
-
: 'N/A';
|
|
266
|
-
|
|
267
|
-
const patternsCount = memory.patterns?.length || 0;
|
|
268
|
-
const providers = loadApiConfig().keys?.filter(k => k.active).length || 0;
|
|
269
|
-
|
|
270
|
-
if (patternsCount === 0 && summary.total_queries === 0) {
|
|
271
|
-
} else {
|
|
272
|
-
console.log(`\n❄️ Qwen Code ICE v${VERSION} ready`);
|
|
273
|
-
console.log(`🧠 ${patternsCount} patterns loaded | 📊 Avg quality: ${avgQuality}/10 | 🔑 ${providers} providers active`);
|
|
274
|
-
console.log(`Last session: ${summary.total_queries || 0} queries, ${summary.total_errors || 0} errors\n`);
|
|
57
|
+
if (query.includes('/usr/bin') || query.includes('/bin/')) {
|
|
58
|
+
enhanced = enhanced.replace(/\/usr\/bin/g, '$PREFIX/bin').replace(/\/bin\//g, '$PREFIX/bin/');
|
|
59
|
+
warnings.push('🛡️ Auto-fixed paths for Termux');
|
|
275
60
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
// Check if core directory exists, run postinstall if not
|
|
280
|
-
if (!fs.existsSync(CORE_DIR)) {
|
|
281
|
-
const { execSync } = require('child_process');
|
|
282
|
-
execSync('node ' + path.join(__dirname, '../scripts/postinstall.cjs'), { stdio: 'inherit' });
|
|
61
|
+
if (query.includes('apt-get')) {
|
|
62
|
+
enhanced = enhanced.replace(/apt-get/g, 'pkg');
|
|
63
|
+
warnings.push('🛡️ Replaced apt-get with pkg');
|
|
283
64
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
65
|
+
return { enhanced, warnings };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function analyzeIntent(query) {
|
|
69
|
+
let real = 'assistance';
|
|
70
|
+
if (query.includes('how to')) real = 'step-by-step';
|
|
71
|
+
else if (query.includes('fix') || query.includes('error')) real = 'working solution';
|
|
72
|
+
else if (query.includes('write') || query.includes('create')) real = 'code implementation';
|
|
73
|
+
let deep = 'Complete request successfully';
|
|
74
|
+
if (query.includes('fix') || query.includes('error')) deep = 'stable production code';
|
|
75
|
+
else if (query.includes('write') || query.includes('create')) deep = 'ship working feature';
|
|
76
|
+
if (!sessionStats.intentCounts[deep]) sessionStats.intentCounts[deep] = 0;
|
|
77
|
+
sessionStats.intentCounts[deep]++;
|
|
78
|
+
return { surface: query, real, deep };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function saveSessionSummary() {
|
|
82
|
+
const quality = loadJson(QUALITY_FILE, { history: [] });
|
|
83
|
+
const avgQuality = quality.history?.length > 0 ? (quality.history.reduce((a, b) => a + (b.score || b), 0) / quality.history.length).toFixed(1) : '0';
|
|
84
|
+
const topIntent = Object.entries(sessionStats.intentCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'assistance';
|
|
85
|
+
const summary = {
|
|
86
|
+
date: new Date().toISOString(),
|
|
87
|
+
total_queries: sessionStats.queries,
|
|
88
|
+
avg_quality: parseFloat(avgQuality),
|
|
89
|
+
patterns_learned: sessionStats.patternsLearned,
|
|
90
|
+
errors_fixed: sessionStats.errorsFixed,
|
|
91
|
+
top_intent: topIntent
|
|
92
|
+
};
|
|
93
|
+
saveJson(SESSION_SUMMARY_FILE, summary);
|
|
94
|
+
console.log('\n📊 Session Summary:');
|
|
95
|
+
console.log(` Queries: ${summary.total_queries}`);
|
|
96
|
+
console.log(` Avg Quality: ${summary.avg_quality}/10`);
|
|
97
|
+
console.log(` Top Intent: ${summary.top_intent}\n`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Show ICE welcome
|
|
101
|
+
console.log(showWelcome());
|
|
102
|
+
|
|
103
|
+
// Get user query
|
|
104
|
+
const userQuery = process.argv.slice(2).join(' ');
|
|
105
|
+
let finalQuery = userQuery;
|
|
106
|
+
|
|
107
|
+
if (userQuery) {
|
|
108
|
+
// Apply ICE pre-processing
|
|
109
|
+
const patternResult = preventPatterns(userQuery);
|
|
110
|
+
finalQuery = patternResult.enhanced;
|
|
111
|
+
if (patternResult.warnings.length > 0) {
|
|
112
|
+
console.log('🛡️ ICE Pattern Prevention:');
|
|
113
|
+
patternResult.warnings.forEach(w => console.log(' ' + w));
|
|
114
|
+
console.log();
|
|
290
115
|
}
|
|
291
|
-
|
|
116
|
+
const intent = analyzeIntent(patternResult.enhanced);
|
|
117
|
+
console.log('🎯 ICE Intent Analysis:');
|
|
118
|
+
console.log(` Surface: "${intent.surface}"`);
|
|
119
|
+
console.log(` Real Need: ${intent.real}`);
|
|
120
|
+
console.log(` Deep Goal: ${intent.deep}`);
|
|
121
|
+
console.log();
|
|
292
122
|
}
|
|
293
123
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const ice = new IceV16({ yolo, model: selectedModel });
|
|
301
|
-
return ice.process(query);
|
|
302
|
-
} catch (err) {
|
|
303
|
-
return { error: true, message: `Failed to process query: ${err.message}` };
|
|
304
|
-
}
|
|
124
|
+
// Find global Qwen Code CLI
|
|
125
|
+
let qwenCliPath;
|
|
126
|
+
try {
|
|
127
|
+
qwenCliPath = execSync('which qwen', { encoding: 'utf8' }).trim();
|
|
128
|
+
} catch (e) {
|
|
129
|
+
qwenCliPath = '/data/data/com.termux/files/usr/lib/node_modules/@mmmbuto/qwen-code-termux/cli.js';
|
|
305
130
|
}
|
|
306
131
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
312
|
-
|
|
313
|
-
const ask = () => {
|
|
314
|
-
rl.question('> ', (query) => {
|
|
315
|
-
if (query.toLowerCase() === 'exit' || query.toLowerCase() === 'quit') {
|
|
316
|
-
console.log('👋 Goodbye!');
|
|
317
|
-
rl.close();
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
logYolo(`Query: ${query}`);
|
|
321
|
-
processQuery(query).then(result => {
|
|
322
|
-
if (result.error) console.log(`❌ ${result.message}`);
|
|
323
|
-
else console.log(result.content);
|
|
324
|
-
ask();
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
ask();
|
|
132
|
+
if (!fs.existsSync(qwenCliPath)) {
|
|
133
|
+
console.error('❌ Error: Qwen Code CLI not found');
|
|
134
|
+
console.error(' Please install: npm install -g @mmmbuto/qwen-code-termux');
|
|
135
|
+
process.exit(1);
|
|
330
136
|
}
|
|
331
137
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
console.log(result.content);
|
|
138
|
+
// Spawn Qwen Code CLI with ICE enhancements
|
|
139
|
+
const args = finalQuery ? ['-p', finalQuery] : [];
|
|
140
|
+
const qwen = spawn(process.execPath, [qwenCliPath, ...args], {
|
|
141
|
+
stdio: 'inherit',
|
|
142
|
+
env: {
|
|
143
|
+
...process.env,
|
|
144
|
+
ICE_ENABLED: 'true',
|
|
145
|
+
ICE_SESSION_ID: Date.now().toString()
|
|
341
146
|
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
async function main() {
|
|
345
|
-
ensureDirs();
|
|
346
|
-
|
|
347
|
-
if (versionFlag) { printVersion(); process.exit(0); }
|
|
348
|
-
if (helpFlag) { printHelp(); process.exit(0); }
|
|
349
|
-
if (statusFlag) { printStatus(); process.exit(0); }
|
|
350
|
-
|
|
351
|
-
if (command === 'status' || args[0] === 'status') { printStatus(); process.exit(0); }
|
|
352
|
-
if (command === 'reset' || args[0] === 'reset') { resetMemory(); process.exit(0); }
|
|
353
|
-
if (command === 'uninstall' || args[0] === 'uninstall') { runUninstall(); return; }
|
|
147
|
+
});
|
|
354
148
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
149
|
+
qwen.on('close', (code) => {
|
|
150
|
+
sessionStats.queries++;
|
|
151
|
+
saveSessionSummary();
|
|
152
|
+
process.exit(code);
|
|
153
|
+
});
|
|
359
154
|
|
|
360
|
-
|
|
361
|
-
console.error('❌
|
|
155
|
+
qwen.on('error', (err) => {
|
|
156
|
+
console.error('❌ Failed to launch Qwen Code:', err.message);
|
|
157
|
+
console.error(' CLI Path:', qwenCliPath);
|
|
362
158
|
process.exit(1);
|
|
363
159
|
});
|
package/core/ice-core.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ❄️ ICE Core - CommonJS Module
|
|
3
|
+
*/
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const HOME = process.env.HOME || '/data/data/com.termux/files/home';
|
|
8
|
+
const ICE_DIR = path.join(HOME, '.qwen-ice');
|
|
9
|
+
const MEMORY_DIR = path.join(ICE_DIR, 'memory');
|
|
10
|
+
|
|
11
|
+
const FILES = {
|
|
12
|
+
sessionMemory: path.join(MEMORY_DIR, 'session_memory.json'),
|
|
13
|
+
qualityHistory: path.join(MEMORY_DIR, 'quality_history.json'),
|
|
14
|
+
sessionSummary: path.join(MEMORY_DIR, 'session_summary.json'),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const TERMUX = { PREFIX: '/data/data/com.termux/files/usr', HOME: HOME };
|
|
18
|
+
|
|
19
|
+
let sessionStats = { queries: 0, errorsFixed: 0, patternsLearned: 0, intentCounts: {}, sessionId: Date.now() };
|
|
20
|
+
|
|
21
|
+
function loadJson(file, defaultVal) {
|
|
22
|
+
try { if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8')); } catch (e) {}
|
|
23
|
+
return defaultVal;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function saveJson(file, data) {
|
|
27
|
+
const dir = path.dirname(file);
|
|
28
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function showColdStartWelcome() {
|
|
33
|
+
const memory = loadJson(FILES.sessionMemory, { user_patterns: [], known_errors: [] });
|
|
34
|
+
const quality = loadJson(FILES.qualityHistory, { history: [] });
|
|
35
|
+
const summary = loadJson(FILES.sessionSummary, { total_queries: 0, avg_quality: 0 });
|
|
36
|
+
const patternsCount = memory.user_patterns?.length || 0;
|
|
37
|
+
const avgQuality = quality.history?.length > 0 ? (quality.history.reduce((a, b) => a + (b.score || b), 0) / quality.history.length).toFixed(1) : 'N/A';
|
|
38
|
+
const totalQueries = summary.total_queries || 0;
|
|
39
|
+
if (patternsCount === 0 && totalQueries === 0) return '\n❄️ Welcome to Qwen Code ICE v21.0.1\n🔑 Qwen OAuth enabled by default\n';
|
|
40
|
+
return `\n❄️ Qwen Code ICE v21.0.1\n📊 Last: ${totalQueries} queries, ${avgQuality}/10 | 🧠 ${patternsCount} patterns\n`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function preventPatterns(query) {
|
|
44
|
+
let enhancedQuery = query;
|
|
45
|
+
const warnings = [];
|
|
46
|
+
const preAppliedFixes = [];
|
|
47
|
+
if (query.toLowerCase().includes('sudo')) {
|
|
48
|
+
enhancedQuery = enhancedQuery.replace(/\bsudo\b\s*/gi, '');
|
|
49
|
+
warnings.push('⚠️ Auto-removed sudo');
|
|
50
|
+
preAppliedFixes.push('sudo_stripped');
|
|
51
|
+
}
|
|
52
|
+
if (query.includes('/usr/bin') || query.includes('/bin/')) {
|
|
53
|
+
enhancedQuery = enhancedQuery.replace(/\/usr\/bin/g, TERMUX.PREFIX + '/bin').replace(/\/bin\//g, TERMUX.PREFIX + '/bin/');
|
|
54
|
+
warnings.push('⚠️ Auto-fixed paths');
|
|
55
|
+
preAppliedFixes.push('paths_fixed');
|
|
56
|
+
}
|
|
57
|
+
return { enhancedQuery, warnings, preAppliedFixes };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function analyzeIntent(query) {
|
|
61
|
+
const surface = query;
|
|
62
|
+
let real = 'assistance';
|
|
63
|
+
if (surface.includes('how to')) real = 'step-by-step';
|
|
64
|
+
else if (surface.includes('fix') || surface.includes('error')) real = 'working solution';
|
|
65
|
+
else if (surface.includes('write') || surface.includes('create')) real = 'code implementation';
|
|
66
|
+
let deep = real;
|
|
67
|
+
if (surface.includes('fix') || surface.includes('error')) deep = 'stable production code';
|
|
68
|
+
else if (surface.includes('write') || surface.includes('create')) deep = 'ship working feature';
|
|
69
|
+
if (!sessionStats.intentCounts[deep]) sessionStats.intentCounts[deep] = 0;
|
|
70
|
+
sessionStats.intentCounts[deep]++;
|
|
71
|
+
return { surface, real, deep };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function scoreResponse(response, codeSuccess) {
|
|
75
|
+
let score = 5;
|
|
76
|
+
if (codeSuccess) score += 3;
|
|
77
|
+
const tokenCount = Math.ceil(response.length / 4);
|
|
78
|
+
if (tokenCount < 200) score += 1;
|
|
79
|
+
if (!response.includes('sudo') && !response.includes('/usr/bin/')) score += 1;
|
|
80
|
+
score = Math.max(1, Math.min(10, score));
|
|
81
|
+
const history = loadJson(FILES.qualityHistory, { history: [] });
|
|
82
|
+
history.history.push({ score, timestamp: Date.now() });
|
|
83
|
+
if (history.history.length > 20) history.history.shift();
|
|
84
|
+
saveJson(FILES.qualityHistory, history);
|
|
85
|
+
return score;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function saveSessionSummary() {
|
|
89
|
+
const quality = loadJson(FILES.qualityHistory, { history: [] });
|
|
90
|
+
const avgQuality = quality.history?.length > 0 ? (quality.history.reduce((a, b) => a + (b.score || b), 0) / quality.history.length).toFixed(1) : '0';
|
|
91
|
+
const topIntent = Object.entries(sessionStats.intentCounts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'assistance';
|
|
92
|
+
const summary = { date: new Date().toISOString(), total_queries: sessionStats.queries, avg_quality: parseFloat(avgQuality), patterns_learned: sessionStats.patternsLearned, errors_fixed: sessionStats.errorsFixed, top_intent: topIntent };
|
|
93
|
+
saveJson(FILES.sessionSummary, summary);
|
|
94
|
+
console.log('\n📊 Session:', summary.total_queries, 'queries,', summary.avg_quality + '/10 avg\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getSessionStats() { return sessionStats; }
|
|
98
|
+
function incrementQueries() { sessionStats.queries++; }
|
|
99
|
+
function incrementErrorsFixed(count) { sessionStats.errorsFixed += count; }
|
|
100
|
+
|
|
101
|
+
module.exports = { showColdStartWelcome, preventPatterns, analyzeIntent, scoreResponse, saveSessionSummary, getSessionStats, incrementQueries, incrementErrorsFixed, loadJson, saveJson, TERMUX, FILES };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anh3d0nic/qwen-code-termux-ice",
|
|
3
|
-
"version": "21.
|
|
3
|
+
"version": "21.1.0",
|
|
4
4
|
"description": "Qwen Code ICE v16.0.5 — Compounding Intelligence with proper npm install structure",
|
|
5
5
|
"main": "core/ice-v16.js",
|
|
6
6
|
"bin": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
],
|
|
44
44
|
"preferGlobal": true,
|
|
45
45
|
"files": [
|
|
46
|
+
"cli-dist/",
|
|
46
47
|
"bin/",
|
|
47
48
|
"core/",
|
|
48
49
|
"config/",
|
package/scripts/postinstall.cjs
CHANGED
|
@@ -1,234 +1,107 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* ❄️ ICE
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Creates ~/.qwen-ice/ directory structure and copies ICE scripts
|
|
3
|
+
* ❄️ ICE Postinstall Script
|
|
4
|
+
* Sets up self-contained Qwen Code ICE installation
|
|
7
5
|
*/
|
|
8
6
|
|
|
9
7
|
const fs = require('fs');
|
|
10
8
|
const path = require('path');
|
|
11
9
|
|
|
12
|
-
// Paths
|
|
13
10
|
const HOME = process.env.HOME || '/data/data/com.termux/files/home';
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
11
|
+
const PREFIX = process.env.PREFIX || '/data/data/com.termux/files/usr';
|
|
12
|
+
const PACKAGE_DIR = path.join(__dirname, '..');
|
|
13
|
+
const INSTALL_DIR = path.join(HOME, '.qwen-ice-package');
|
|
14
|
+
const CLI_DIST_DIR = path.join(INSTALL_DIR, 'cli-dist');
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
const DIRS = [
|
|
20
|
-
INSTALL_DIR,
|
|
21
|
-
path.join(INSTALL_DIR, 'core'),
|
|
22
|
-
path.join(INSTALL_DIR, 'bin'),
|
|
23
|
-
path.join(INSTALL_DIR, 'memory'),
|
|
24
|
-
path.join(INSTALL_DIR, 'sessions'),
|
|
25
|
-
path.join(INSTALL_DIR, 'config'),
|
|
26
|
-
path.join(INSTALL_DIR, 'logs')
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
// Files to copy from package to install dir
|
|
30
|
-
const FILES_TO_COPY = [
|
|
31
|
-
{ src: 'scripts/ice-v15.js', dest: 'core/ice-v15.js' },
|
|
32
|
-
{ src: 'scripts/api-caller.js', dest: 'core/api-caller.js' },
|
|
33
|
-
{ src: 'scripts/ice-v14.js', dest: 'core/ice-v14.js' },
|
|
34
|
-
{ src: 'scripts/ice-v13.js', dest: 'core/ice-v13.js' },
|
|
35
|
-
{ src: 'bin/qwen-ice', dest: 'bin/qwen-ice' },
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
// JSON files to preserve from ~/.qwen/
|
|
39
|
-
const JSON_FILES_TO_PRESERVE = [
|
|
40
|
-
'session_memory.json',
|
|
41
|
-
'quality_history.json',
|
|
42
|
-
'confidence_history.json',
|
|
43
|
-
'session_summary.json',
|
|
44
|
-
'trend_history.json',
|
|
45
|
-
'ice_session.json',
|
|
46
|
-
'ice_active_context.json'
|
|
47
|
-
];
|
|
16
|
+
console.log('\n❄️ Qwen Code ICE — Postinstall\n');
|
|
48
17
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
18
|
+
// Create installation directory
|
|
19
|
+
if (!fs.existsSync(INSTALL_DIR)) {
|
|
20
|
+
fs.mkdirSync(INSTALL_DIR, { recursive: true });
|
|
21
|
+
console.log('✅ Created', INSTALL_DIR);
|
|
54
22
|
}
|
|
55
23
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (fs.existsSync(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
24
|
+
// Copy CLI dist
|
|
25
|
+
const srcCliDist = path.join(PACKAGE_DIR, 'packages', 'cli', 'dist');
|
|
26
|
+
if (fs.existsSync(srcCliDist)) {
|
|
27
|
+
const destCliDist = path.join(INSTALL_DIR, 'cli-dist');
|
|
28
|
+
if (!fs.existsSync(destCliDist)) fs.mkdirSync(destCliDist, { recursive: true });
|
|
29
|
+
|
|
30
|
+
// Copy files recursively
|
|
31
|
+
function copyDir(src, dest) {
|
|
32
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
33
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
34
|
+
for (const entry of entries) {
|
|
35
|
+
const srcPath = path.join(src, entry.name);
|
|
36
|
+
const destPath = path.join(dest, entry.name);
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
copyDir(srcPath, destPath);
|
|
39
|
+
} else {
|
|
40
|
+
fs.copyFileSync(srcPath, destPath);
|
|
70
41
|
}
|
|
71
42
|
}
|
|
72
|
-
|
|
73
|
-
fs.writeFileSync(destPath, fixedContent);
|
|
74
|
-
fs.chmodSync(destPath, 0o755);
|
|
75
|
-
console.log(`✅ Copied ${src} → ${dest}`);
|
|
76
|
-
} else {
|
|
77
|
-
console.log(`⚠️ Source not found: ${srcPath}`);
|
|
78
43
|
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function preserveUserMemory() {
|
|
82
|
-
let preserved = 0;
|
|
83
44
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (fs.existsSync(srcPath)) {
|
|
89
|
-
try {
|
|
90
|
-
const content = fs.readFileSync(srcPath, 'utf8');
|
|
91
|
-
// Validate JSON
|
|
92
|
-
JSON.parse(content);
|
|
93
|
-
fs.writeFileSync(destPath, content);
|
|
94
|
-
console.log(`✅ Preserved ${file}`);
|
|
95
|
-
preserved++;
|
|
96
|
-
} catch (e) {
|
|
97
|
-
console.log(`⚠️ Corrupted ${file}, skipping`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
return preserved;
|
|
45
|
+
copyDir(srcCliDist, destCliDist);
|
|
46
|
+
console.log('✅ Copied Qwen Code CLI to cli-dist/');
|
|
47
|
+
} else {
|
|
48
|
+
console.log('⚠️ CLI dist not found at', srcCliDist);
|
|
103
49
|
}
|
|
104
50
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (!fs.existsSync(apiKeysFile)) {
|
|
112
|
-
const defaultKeys = {
|
|
113
|
-
keys: [
|
|
114
|
-
{
|
|
115
|
-
id: 'qwen-oauth',
|
|
116
|
-
provider: 'qwen',
|
|
117
|
-
type: 'oauth',
|
|
118
|
-
model: 'qwen3-coder-plus',
|
|
119
|
-
active: true,
|
|
120
|
-
note: 'Uses existing Qwen OAuth — do not modify'
|
|
121
|
-
}
|
|
122
|
-
],
|
|
123
|
-
default_provider: 'qwen',
|
|
124
|
-
fallback_order: ['qwen', 'gemini', 'groq', 'openai', 'dashscope']
|
|
125
|
-
};
|
|
126
|
-
fs.writeFileSync(apiKeysFile, JSON.stringify(defaultKeys, null, 2));
|
|
127
|
-
console.log('✅ Created config/api-keys.json');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Default settings
|
|
131
|
-
const settingsFile = path.join(configDir, 'settings.json');
|
|
132
|
-
if (!fs.existsSync(settingsFile)) {
|
|
133
|
-
const defaultSettings = {
|
|
134
|
-
version: '15.0.0',
|
|
135
|
-
theme: 'default',
|
|
136
|
-
yolo_mode: false,
|
|
137
|
-
auto_approve_commands: [],
|
|
138
|
-
max_context_length: 8192,
|
|
139
|
-
quality_threshold: 5,
|
|
140
|
-
created_at: new Date().toISOString()
|
|
141
|
-
};
|
|
142
|
-
fs.writeFileSync(settingsFile, JSON.stringify(defaultSettings, null, 2));
|
|
143
|
-
console.log('✅ Created config/settings.json');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Default memory files if not preserved
|
|
147
|
-
const memoryFiles = {
|
|
148
|
-
'session_memory.json': { user_patterns: [], known_errors: [], project_state: {}, user_preferences: [] },
|
|
149
|
-
'quality_history.json': { history: [] },
|
|
150
|
-
'confidence_history.json': { history: [] },
|
|
151
|
-
'session_summary.json': { total_queries: 0, avg_quality: 0, patterns_learned: 0, errors_fixed: 0, top_intent: 'none' },
|
|
152
|
-
'trend_history.json': { trends: [], alerts: [] }
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
Object.entries(memoryFiles).forEach(([file, data]) => {
|
|
156
|
-
const filePath = path.join(memoryDir, file);
|
|
157
|
-
if (!fs.existsSync(filePath)) {
|
|
158
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
159
|
-
}
|
|
160
|
-
});
|
|
51
|
+
// Create user data directory
|
|
52
|
+
const userDataDir = path.join(HOME, '.qwen-ice-data');
|
|
53
|
+
const memoryDir = path.join(userDataDir, 'memory');
|
|
54
|
+
if (!fs.existsSync(memoryDir)) {
|
|
55
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
56
|
+
console.log('✅ Created user data directory');
|
|
161
57
|
}
|
|
162
58
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Create symlinks
|
|
178
|
-
const symlinks = [
|
|
179
|
-
{ target: path.join(INSTALL_DIR, 'bin', 'qwen-ice'), link: path.join(binDir, 'qwen-ice') },
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
symlinks.forEach(({ target, link }) => {
|
|
183
|
-
try {
|
|
184
|
-
if (fs.existsSync(link)) {
|
|
185
|
-
fs.unlinkSync(link);
|
|
186
|
-
}
|
|
187
|
-
fs.symlinkSync(target, link);
|
|
188
|
-
console.log(`✅ Symlink: ${link} → ${target}`);
|
|
189
|
-
} catch (e) {
|
|
190
|
-
console.log(`⚠️ Failed to create symlink ${link}: ${e.message}`);
|
|
59
|
+
// Create default config
|
|
60
|
+
const configFile = path.join(userDataDir, 'config.json');
|
|
61
|
+
if (!fs.existsSync(configFile)) {
|
|
62
|
+
const defaultConfig = {
|
|
63
|
+
provider: 'qwen',
|
|
64
|
+
model: 'qwen3-coder-plus',
|
|
65
|
+
oauth_enabled: true,
|
|
66
|
+
ice_enhancements: {
|
|
67
|
+
pattern_prevention: true,
|
|
68
|
+
intent_analysis: true,
|
|
69
|
+
session_summary: true
|
|
191
70
|
}
|
|
192
|
-
}
|
|
71
|
+
};
|
|
72
|
+
fs.writeFileSync(configFile, JSON.stringify(defaultConfig, null, 2));
|
|
73
|
+
console.log('✅ Created default config');
|
|
193
74
|
}
|
|
194
75
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
DIRS.forEach(ensureDir);
|
|
200
|
-
|
|
201
|
-
// Step 2: Copy ICE scripts
|
|
202
|
-
FILES_TO_COPY.forEach(({ src, dest }) => copyFile(src, dest));
|
|
203
|
-
|
|
204
|
-
// Step 3: Preserve user memory
|
|
205
|
-
const preserved = preserveUserMemory();
|
|
206
|
-
if (preserved === 0) {
|
|
207
|
-
console.log('ℹ️ No existing memory files found, creating defaults');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Step 4: Create default config files
|
|
211
|
-
createDefaultConfigFiles();
|
|
212
|
-
|
|
213
|
-
// Step 5: Create symlinks
|
|
214
|
-
createSymlinks();
|
|
215
|
-
|
|
216
|
-
// Final message
|
|
217
|
-
console.log('\n' + '='.repeat(60));
|
|
218
|
-
console.log('✅ ICE v15.0.0 installed to ~/.qwen-ice');
|
|
219
|
-
console.log('='.repeat(60));
|
|
220
|
-
console.log('\n🚀 Commands:');
|
|
221
|
-
console.log(' qwen-ice — Interactive mode');
|
|
222
|
-
console.log(' qwen-ice "query" — Single query');
|
|
223
|
-
console.log(' qwen-ice --version — Show version');
|
|
224
|
-
console.log(' qwen-ice --help — Show help');
|
|
225
|
-
|
|
226
|
-
console.log(' qwen-ice exit — Show session summary\n');
|
|
76
|
+
// Create symlinks
|
|
77
|
+
const binDir = path.join(PREFIX, 'bin');
|
|
78
|
+
if (!fs.existsSync(binDir)) {
|
|
79
|
+
try { fs.mkdirSync(binDir, { recursive: true }); } catch (e) {}
|
|
227
80
|
}
|
|
228
81
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
82
|
+
const symlinks = [
|
|
83
|
+
{ target: path.join(PACKAGE_DIR, 'bin', 'qwen-ice'), link: path.join(binDir, 'qwen-ice') }
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
for (const { target, link } of symlinks) {
|
|
87
|
+
try {
|
|
88
|
+
if (fs.existsSync(link)) fs.unlinkSync(link);
|
|
89
|
+
fs.symlinkSync(target, link);
|
|
90
|
+
console.log('✅ Symlink:', link);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
console.log('⚠️ Symlink failed:', e.message);
|
|
93
|
+
}
|
|
232
94
|
}
|
|
233
95
|
|
|
234
|
-
|
|
96
|
+
console.log('\n' + '='.repeat(60));
|
|
97
|
+
console.log('✅ Qwen Code ICE installed successfully');
|
|
98
|
+
console.log('='.repeat(60));
|
|
99
|
+
console.log('\n🚀 Usage:');
|
|
100
|
+
console.log(' qwen-ice # Interactive session');
|
|
101
|
+
console.log(' qwen-ice "your query" # Single query');
|
|
102
|
+
console.log(' qwen-ice --version # Show version');
|
|
103
|
+
console.log(' qwen-ice --help # Show help');
|
|
104
|
+
console.log('\n📁 Installation:');
|
|
105
|
+
console.log(' Package:', INSTALL_DIR);
|
|
106
|
+
console.log(' User Data:', userDataDir);
|
|
107
|
+
console.log();
|