@anh3d0nic/qwen-code-termux-ice 16.0.5 → 16.0.8

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 CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/data/data/com.termux/files/usr/bin/node
2
2
  /**
3
- * ❄️ Qwen Code ICE v16.0.0 — Main Binary
3
+ * ❄️ Qwen Code ICE v16.0.6 — Main Binary
4
+ * Fixed: Auto-runs postinstall if core dir missing on fresh install
4
5
  */
5
6
 
6
7
  const fs = require('fs');
7
8
  const path = require('path');
8
- const { pathToFileURL } = require('url');
9
9
  const readline = require('readline');
10
10
  const { spawn } = require('child_process');
11
11
 
12
- const VERSION = '16.0.0';
12
+ const VERSION = '16.0.6';
13
13
  const INSTALL_DIR = process.env.HOME + '/.qwen-ice';
14
14
  const CONFIG_DIR = path.join(INSTALL_DIR, 'config');
15
15
  const MEMORY_DIR = path.join(INSTALL_DIR, 'memory');
@@ -281,14 +281,30 @@ function printColdStart() {
281
281
  }
282
282
  }
283
283
 
284
- function processQuery(query) {
284
+ function getLatestIceVersion() {
285
+ // Check if core directory exists, run postinstall if not
286
+ if (!fs.existsSync(CORE_DIR)) {
287
+ const { execSync } = require('child_process');
288
+ execSync('node ' + path.join(__dirname, '../scripts/postinstall.cjs'), { stdio: 'inherit' });
289
+ }
290
+ const versions = fs.readdirSync(CORE_DIR)
291
+ .filter(f => f.startsWith('ice-v') && f.endsWith('.js'))
292
+ .sort();
293
+ if (!versions || versions.length === 0) {
294
+ console.error('ICE core not found. Run: node scripts/postinstall.cjs');
295
+ process.exit(1);
296
+ }
297
+ return versions[versions.length - 1] || 'ice-v15.js';
298
+ }
299
+
300
+ async function processQuery(query) {
285
301
  try {
286
302
  // Dynamically load latest ICE version
287
- const versions = fs.readdirSync(CORE_DIR).filter(f => f.startsWith('ice-v') && f.endsWith('.js')).sort();
288
- const latest = versions[versions.length - 1] || 'ice-v15.js';
303
+ const latest = getLatestIceVersion();
289
304
  const iceModule = require(path.join(CORE_DIR, latest));
290
305
  const IceV16 = iceModule.IceV16 || iceModule.UnifiedPipeline;
291
- const ice = new IceV16({ yolo, model: selectedModel }); return ice.process(query);
306
+ const ice = new IceV16({ yolo, model: selectedModel });
307
+ return ice.process(query);
292
308
  } catch (err) {
293
309
  return { error: true, message: `Failed to process query: ${err.message}` };
294
310
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anh3d0nic/qwen-code-termux-ice",
3
- "version": "16.0.5",
3
+ "version": "16.0.8",
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": {
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Simple API caller for ICE v15
4
+ * Supports: Qwen (DashScope), Gemini, Groq
5
+ */
6
+
7
+ const https = require('https');
8
+
9
+ const TERMUX = {
10
+ PREFIX: '/data/data/com.termux/files/usr',
11
+ HOME: '/data/data/com.termux/files/home'
12
+ };
13
+
14
+ const API_CONFIG = {
15
+ qwen: {
16
+ url: 'dashscope.aliyuncs.com',
17
+ path: '/compatible-mode/v1/chat/completions',
18
+ model: 'qwen3-coder-plus'
19
+ },
20
+ gemini: {
21
+ url: 'generativelanguage.googleapis.com',
22
+ path: '/v1beta/models/gemini-2.0-flash:generateContent',
23
+ model: 'gemini-2.0-flash'
24
+ },
25
+ groq: {
26
+ url: 'api.groq.com',
27
+ path: '/openai/v1/chat/completions',
28
+ model: 'llama-3.1-70b-versatile'
29
+ }
30
+ };
31
+
32
+ function loadApiKeys() {
33
+ const fs = require('fs');
34
+ const path = require('path');
35
+ const keysFile = path.join(TERMUX.HOME, '.qwen-ice', 'config', 'api-keys.json');
36
+ try {
37
+ if (fs.existsSync(keysFile)) {
38
+ return JSON.parse(fs.readFileSync(keysFile, 'utf8'));
39
+ }
40
+ } catch (e) {}
41
+ return { keys: [], default_provider: 'qwen' };
42
+ }
43
+
44
+ function callQwenAPI(apiKey, messages) {
45
+ return new Promise((resolve, reject) => {
46
+ const data = JSON.stringify({
47
+ model: API_CONFIG.qwen.model,
48
+ messages: messages,
49
+ max_tokens: 4096
50
+ });
51
+
52
+ const options = {
53
+ hostname: API_CONFIG.qwen.url,
54
+ port: 443,
55
+ path: API_CONFIG.qwen.path,
56
+ method: 'POST',
57
+ headers: {
58
+ 'Content-Type': 'application/json',
59
+ 'Authorization': `Bearer ${apiKey}`,
60
+ 'Content-Length': data.length
61
+ }
62
+ };
63
+
64
+ const req = https.request(options, (res) => {
65
+ let responseData = '';
66
+ res.on('data', chunk => responseData += chunk);
67
+ res.on('end', () => {
68
+ if (res.statusCode >= 400) {
69
+ reject(new Error(`Qwen API error: ${res.statusCode}`));
70
+ } else {
71
+ try {
72
+ const parsed = JSON.parse(responseData);
73
+ resolve(parsed.choices?.[0]?.message?.content || '');
74
+ } catch (e) {
75
+ reject(new Error('Failed to parse Qwen response'));
76
+ }
77
+ }
78
+ });
79
+ });
80
+
81
+ req.on('error', reject);
82
+ req.setTimeout(60000, () => reject(new Error('Qwen API timeout')));
83
+ req.write(data);
84
+ req.end();
85
+ });
86
+ }
87
+
88
+ function callGeminiAPI(apiKey, messages) {
89
+ return new Promise((resolve, reject) => {
90
+ const lastMessage = messages[messages.length - 1]?.content || '';
91
+ const data = JSON.stringify({
92
+ contents: [{ parts: [{ text: lastMessage }] }],
93
+ generationConfig: { maxOutputTokens: 4096 }
94
+ });
95
+
96
+ const options = {
97
+ hostname: API_CONFIG.gemini.url,
98
+ port: 443,
99
+ path: `${API_CONFIG.gemini.path}?key=${apiKey}`,
100
+ method: 'POST',
101
+ headers: {
102
+ 'Content-Type': 'application/json',
103
+ 'Content-Length': data.length
104
+ }
105
+ };
106
+
107
+ const req = https.request(options, (res) => {
108
+ let responseData = '';
109
+ res.on('data', chunk => responseData += chunk);
110
+ res.on('end', () => {
111
+ if (res.statusCode === 429) {
112
+ reject(new Error('Gemini rate limit exceeded'));
113
+ } else if (res.statusCode >= 400) {
114
+ reject(new Error(`Gemini API error: ${res.statusCode}`));
115
+ } else {
116
+ try {
117
+ const parsed = JSON.parse(responseData);
118
+ resolve(parsed.candidates?.[0]?.content?.parts?.[0]?.text || '');
119
+ } catch (e) {
120
+ reject(new Error('Failed to parse Gemini response'));
121
+ }
122
+ }
123
+ });
124
+ });
125
+
126
+ req.on('error', reject);
127
+ req.setTimeout(60000, () => reject(new Error('Gemini API timeout')));
128
+ req.write(data);
129
+ req.end();
130
+ });
131
+ }
132
+
133
+ function callGroqAPI(apiKey, messages) {
134
+ return new Promise((resolve, reject) => {
135
+ const data = JSON.stringify({
136
+ model: API_CONFIG.groq.model,
137
+ messages: messages,
138
+ max_tokens: 4096
139
+ });
140
+
141
+ const options = {
142
+ hostname: API_CONFIG.groq.url,
143
+ port: 443,
144
+ path: API_CONFIG.groq.path,
145
+ method: 'POST',
146
+ headers: {
147
+ 'Content-Type': 'application/json',
148
+ 'Authorization': `Bearer ${apiKey}`,
149
+ 'Content-Length': data.length
150
+ }
151
+ };
152
+
153
+ const req = https.request(options, (res) => {
154
+ let responseData = '';
155
+ res.on('data', chunk => responseData += chunk);
156
+ res.on('end', () => {
157
+ if (res.statusCode >= 400) {
158
+ reject(new Error(`Groq API error: ${res.statusCode}`));
159
+ } else {
160
+ try {
161
+ const parsed = JSON.parse(responseData);
162
+ resolve(parsed.choices?.[0]?.message?.content || '');
163
+ } catch (e) {
164
+ reject(new Error('Failed to parse Groq response'));
165
+ }
166
+ }
167
+ });
168
+ });
169
+
170
+ req.on('error', reject);
171
+ req.setTimeout(60000, () => reject(new Error('Groq API timeout')));
172
+ req.write(data);
173
+ req.end();
174
+ });
175
+ }
176
+
177
+ async function callLLM(messages, provider = null) {
178
+ const config = loadApiKeys();
179
+ const selectedProvider = provider || config.default_provider || 'qwen';
180
+
181
+ const keyEntry = config.keys?.find(k => k.provider === selectedProvider && k.type !== 'oauth');
182
+
183
+ if (!keyEntry || !keyEntry.key) {
184
+ // No API key - return stub response
185
+ const lastMessage = messages[messages.length - 1]?.content || '';
186
+ return `[No API key configured for ${selectedProvider}]\n\nI understand your query: "${lastMessage.substring(0, 100)}${lastMessage.length > 100 ? '...' : ''}"\n\nTo get real AI responses, run: qwen-ice api-setup`;
187
+ }
188
+
189
+ try {
190
+ switch (selectedProvider) {
191
+ case 'qwen':
192
+ return await callQwenAPI(keyEntry.key, messages);
193
+ case 'gemini':
194
+ return await callGeminiAPI(keyEntry.key, messages);
195
+ case 'groq':
196
+ return await callGroqAPI(keyEntry.key, messages);
197
+ default:
198
+ return await callQwenAPI(keyEntry.key, messages);
199
+ }
200
+ } catch (err) {
201
+ return `[API Error: ${err.message}]\n\nYour query was: "${messages[messages.length - 1]?.content.substring(0, 100)}"`;
202
+ }
203
+ }
204
+
205
+ module.exports = { callLLM, loadApiKeys };
@@ -1244,7 +1244,7 @@ class UnifiedPipeline {
1244
1244
 
1245
1245
  // Step 4: Generate response
1246
1246
  console.log('✏️ Step 4: Generate Response');
1247
- let response = this.generateResponse(enhancedInput, intent);
1247
+ let response = await this.generateResponse(enhancedInput, intent);
1248
1248
  console.log(' Response generated\n');
1249
1249
 
1250
1250
  // Step 5: Self-Critique
@@ -1358,14 +1358,18 @@ class UnifiedPipeline {
1358
1358
  return response;
1359
1359
  }
1360
1360
 
1361
- generateResponse(input, intent) {
1362
- if (input.includes('sudo')) {
1363
- return `To fix this on Termux, use pkg instead:\n\`\`\`bash\npkg install python\n\`\`\`\nNote: sudo is not available on Termux.`;
1364
- }
1365
- if (input.includes('function')) {
1366
- return `Here's the function:\n\`\`\`javascript\nfunction example() {\n return 'Hello from Termux';\n}\n\`\`\`\nThis follows Termux best practices.`;
1367
- }
1368
- return `I understand you want to ${intent.real}. The deep goal is: ${intent.deep}.\n\nHere's how to proceed...`;
1361
+ async generateResponse(input, intent) {
1362
+ const { callLLM } = require('./api-caller.js');
1363
+
1364
+ const systemPrompt = 'You are Qwen Code ICE v15.0 - a Termux-optimized AI assistant. Always provide Termux-compatible solutions (no sudo, use pkg instead of apt-get, use ' + TERMUX.PREFIX + ' paths).';
1365
+
1366
+ const messages = [
1367
+ { role: 'system', content: systemPrompt },
1368
+ { role: 'user', content: input }
1369
+ ];
1370
+
1371
+ const response = await callLLM(messages);
1372
+ return response;
1369
1373
  }
1370
1374
 
1371
1375
  printSessionSummary() {