@anh3d0nic/qwen-code-termux-ice 20.0.2 → 21.0.1

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
@@ -137,7 +137,6 @@ OPTIONS:
137
137
  --model <provider> Use specific model (qwen, groq, gemini, openai, dashscope)
138
138
 
139
139
  COMMANDS:
140
- api-setup API key manager
141
140
  status Show status (keys, models, memory)
142
141
  reset Clear memory (keep API keys)
143
142
  uninstall Clean uninstall with backup
@@ -146,7 +145,6 @@ EXAMPLES:
146
145
  qwen-ice "fix this error" Run single query
147
146
  qwen-ice -y "npm install" YOLO mode (auto-approve)
148
147
  qwen-ice --model groq Use Groq provider
149
- qwen-ice api-setup Configure API keys
150
148
  qwen-ice status Check status
151
149
  `);
152
150
  }
@@ -190,7 +188,6 @@ API KEYS:
190
188
  const label = key.type === 'oauth' ? '(OAuth)' : '';
191
189
  const calls = key.call_count || 0;
192
190
  const lastUsed = key.last_used ? new Date(key.last_used).toLocaleDateString() : 'never';
193
- const invalidMark = !key.active && key.type !== 'oauth' ? ' (invalid key - run api-setup to fix)' : '';
194
191
  console.log(`${status} ${key.id.padEnd(15)} ${label.padEnd(8)} — active, ${calls} calls${invalidMark}`);
195
192
  if (key.last_used) console.log(` last used: ${lastUsed}`);
196
193
  }
@@ -227,9 +224,7 @@ PIPELINE:
227
224
  }
228
225
 
229
226
  function runApiSetup() {
230
- const scriptPath = path.join(INSTALL_DIR, 'bin', 'qwen-ice-api-setup');
231
227
  if (!fs.existsSync(scriptPath)) {
232
- console.error('❌ api-setup not found. Reinstall ICE.');
233
228
  return;
234
229
  }
235
230
  const child = spawn('node', [scriptPath], { stdio: 'inherit' });
@@ -273,7 +268,6 @@ function printColdStart() {
273
268
  const providers = loadApiConfig().keys?.filter(k => k.active).length || 0;
274
269
 
275
270
  if (patternsCount === 0 && summary.total_queries === 0) {
276
- console.log(`\n❄️ Welcome to Qwen Code ICE v${VERSION}\n🔑 Run: qwen-ice api-setup to configure your API keys\n`);
277
271
  } else {
278
272
  console.log(`\n❄️ Qwen Code ICE v${VERSION} ready`);
279
273
  console.log(`🧠 ${patternsCount} patterns loaded | 📊 Avg quality: ${avgQuality}/10 | 🔑 ${providers} providers active`);
@@ -354,7 +348,6 @@ async function main() {
354
348
  if (helpFlag) { printHelp(); process.exit(0); }
355
349
  if (statusFlag) { printStatus(); process.exit(0); }
356
350
 
357
- if (command === 'api-setup' || args[0] === 'api-setup') { runApiSetup(); return; }
358
351
  if (command === 'status' || args[0] === 'status') { printStatus(); process.exit(0); }
359
352
  if (command === 'reset' || args[0] === 'reset') { resetMemory(); process.exit(0); }
360
353
  if (command === 'uninstall' || args[0] === 'uninstall') { runUninstall(); return; }
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@anh3d0nic/qwen-code-termux-ice",
3
- "version": "20.0.2",
3
+ "version": "21.0.1",
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": {
7
- "qwen-ice": "./bin/qwen-ice",
8
- "qwen-ice-api-setup": "./bin/qwen-ice-api-setup"
7
+ "qwen-ice": "./bin/qwen-ice"
9
8
  },
10
9
  "scripts": {
11
10
  "postinstall": "node scripts/postinstall.cjs",
@@ -33,7 +33,6 @@ const FILES_TO_COPY = [
33
33
  { src: 'scripts/ice-v14.js', dest: 'core/ice-v14.js' },
34
34
  { src: 'scripts/ice-v13.js', dest: 'core/ice-v13.js' },
35
35
  { src: 'bin/qwen-ice', dest: 'bin/qwen-ice' },
36
- { src: 'bin/qwen-ice-api-setup', dest: 'bin/qwen-ice-api-setup' }
37
36
  ];
38
37
 
39
38
  // JSON files to preserve from ~/.qwen/
@@ -178,7 +177,6 @@ function createSymlinks() {
178
177
  // Create symlinks
179
178
  const symlinks = [
180
179
  { target: path.join(INSTALL_DIR, 'bin', 'qwen-ice'), link: path.join(binDir, 'qwen-ice') },
181
- { target: path.join(INSTALL_DIR, 'bin', 'qwen-ice-api-setup'), link: path.join(binDir, 'qwen-ice-api-setup') }
182
180
  ];
183
181
 
184
182
  symlinks.forEach(({ target, link }) => {
@@ -224,7 +222,7 @@ function main() {
224
222
  console.log(' qwen-ice "query" — Single query');
225
223
  console.log(' qwen-ice --version — Show version');
226
224
  console.log(' qwen-ice --help — Show help');
227
- console.log(' qwen-ice api-setup — Configure API keys');
225
+
228
226
  console.log(' qwen-ice exit — Show session summary\n');
229
227
  }
230
228
 
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env node
2
- const { execSync } = require('child_process');
3
- const path = require('path');
4
-
5
- const ICE_HOME = '/data/data/com.termux/files/.qwen-ice';
6
- const isTermux = process.env.PREFIX?.includes('com.termux') || process.env.TERMUX_VERSION !== undefined;
7
- const actualIceHome = isTermux ? ICE_HOME : path.join(process.env.HOME || '~', '.qwen-ice');
8
-
9
- try {
10
- execSync(`node "${path.join(actualIceHome, 'bin', 'qwen-ice-api-setup')}"`, { stdio: 'inherit' });
11
- } catch (e) {
12
- console.error('❌ Qwen ICE not installed. Run: npm install -g @anh3d0nic/qwen-code-termux-ice');
13
- process.exit(1);
14
- }
@@ -1,340 +0,0 @@
1
- #!/data/data/com.termux/files/usr/bin/node
2
- /**
3
- * ❄️ ICE v16.0.0 — API Key Manager
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const readline = require('readline');
9
- const https = require('https');
10
-
11
- const INSTALL_DIR = '/data/data/com.termux/files/.qwen-ice';
12
- const CONFIG_DIR = path.join(INSTALL_DIR, 'config');
13
- const API_KEYS_FILE = path.join(CONFIG_DIR, 'api-keys.json');
14
-
15
- function loadApiConfig() {
16
- try {
17
- if (fs.existsSync(API_KEYS_FILE)) {
18
- return JSON.parse(fs.readFileSync(API_KEYS_FILE, 'utf8'));
19
- }
20
- } catch (err) {}
21
- return {
22
- keys: [{
23
- id: 'qwen-oauth',
24
- provider: 'qwen',
25
- type: 'oauth',
26
- model: 'qwen3-coder-plus',
27
- active: true,
28
- note: 'Uses existing Qwen OAuth — do not modify'
29
- }],
30
- default_provider: 'qwen',
31
- fallback_order: ['qwen', 'gemini', 'groq', 'openai', 'dashscope']
32
- };
33
- }
34
-
35
- function saveApiConfig(config) {
36
- fs.writeFileSync(API_KEYS_FILE, JSON.stringify(config, null, 2));
37
- }
38
-
39
- function createInterface() {
40
- return readline.createInterface({ input: process.stdin, output: process.stdout });
41
- }
42
-
43
- function printMenu() {
44
- console.log(`
45
- ┌─────────────────────────────────┐
46
- │ Qwen ICE API Key Manager │
47
- ├─────────────────────────────────┤
48
- │ 1. View configured APIs │
49
- │ 2. Add new API key │
50
- │ 3. Remove API key │
51
- │ 4. Test all API keys │
52
- │ 5. Set default provider │
53
- │ 6. Exit │
54
- └─────────────────────────────────┘
55
- `);
56
- }
57
-
58
- function viewConfiguredAPIs(config) {
59
- console.log('\n📋 Configured API Keys:\n');
60
- for (const key of config.keys) {
61
- const status = key.active ? '✅' : '❌';
62
- const oauth = key.type === 'oauth' ? ' (OAuth)' : '';
63
- const defaultMark = key.provider === config.default_provider ? ' [DEFAULT]' : '';
64
- const calls = key.call_count || 0;
65
- const lastUsed = key.last_used ? new Date(key.last_used).toLocaleString() : 'never';
66
- console.log(`${status} ${key.id}${oauth}${defaultMark}`);
67
- console.log(` Provider: ${key.provider}`);
68
- console.log(` Model: ${key.model || 'N/A'}`);
69
- console.log(` Calls: ${calls} | Last used: ${lastUsed}`);
70
- if (key.note) console.log(` Note: ${key.note}`);
71
- console.log('');
72
- }
73
- console.log(`Fallback order: ${config.fallback_order.join(' → ')}\n`);
74
- }
75
-
76
- async function addNewApiKey(config) {
77
- const rl = createInterface();
78
- try {
79
- console.log('\nSelect provider:');
80
- console.log('1. groq');
81
- console.log('2. gemini');
82
- console.log('3. openai');
83
- console.log('4. dashscope');
84
- console.log('5. qwen (API key, not OAuth)');
85
-
86
- const provider = await new Promise(resolve => {
87
- rl.question('Provider (1-5): ', answer => {
88
- const providers = { '1': 'groq', '2': 'gemini', '3': 'openai', '4': 'dashscope', '5': 'qwen' };
89
- resolve(providers[answer] || 'groq');
90
- });
91
- });
92
-
93
- const defaultModels = {
94
- groq: 'llama-3.1-70b-versatile',
95
- gemini: 'gemini-2.0-flash',
96
- openai: 'gpt-4o',
97
- dashscope: 'qwen-turbo',
98
- qwen: 'qwen3-coder-plus'
99
- };
100
-
101
- const model = await new Promise(resolve => {
102
- rl.question(`Model [${defaultModels[provider]}]: `, answer => {
103
- resolve(answer.trim() || defaultModels[provider]);
104
- });
105
- });
106
-
107
- const apiKey = await new Promise(resolve => {
108
- const keyNames = { groq: 'gsk_...', gemini: 'AIza...', openai: 'sk-...', dashscope: 'sk-...', qwen: 'sk-...' };
109
- rl.question(`Enter ${provider} API key (${keyNames[provider]}): `, resolve);
110
- });
111
-
112
- if (!apiKey) {
113
- console.log('❌ API key cannot be empty');
114
- return;
115
- }
116
-
117
- const id = `${provider}-${Date.now().toString(36)}`;
118
- const newKey = { id, provider, key: apiKey, model, active: true, added: new Date().toISOString(), last_used: null, call_count: 0 };
119
-
120
- config.keys.push(newKey);
121
- saveApiConfig(config);
122
- console.log(`\n✅ Added ${provider} API key: ${id}`);
123
- } finally {
124
- rl.close();
125
- }
126
- }
127
-
128
- async function removeApiKey(config) {
129
- const rl = createInterface();
130
- try {
131
- console.log('\nKeys to remove (enter ID):');
132
- for (const key of config.keys) {
133
- if (key.type !== 'oauth') {
134
- console.log(` - ${key.id} (${key.provider})`);
135
- }
136
- }
137
-
138
- const keyId = await new Promise(resolve => {
139
- rl.question('Enter key ID to remove (or "cancel"): ', resolve);
140
- });
141
-
142
- if (keyId.toLowerCase() === 'cancel') {
143
- console.log('Cancelled');
144
- return;
145
- }
146
-
147
- const index = config.keys.findIndex(k => k.id === keyId);
148
- if (index === -1) {
149
- console.log('❌ Key not found');
150
- return;
151
- }
152
-
153
- if (config.keys[index].type === 'oauth') {
154
- console.log('❌ Cannot remove OAuth key');
155
- return;
156
- }
157
-
158
- config.keys.splice(index, 1);
159
- saveApiConfig(config);
160
- console.log(`✅ Removed key: ${keyId}`);
161
- } finally {
162
- rl.close();
163
- }
164
- }
165
-
166
- function testProvider(key, provider, model) {
167
- return new Promise((resolve) => {
168
- let req;
169
-
170
- if (provider === 'groq') {
171
- const data = JSON.stringify({
172
- model: model || 'llama-3.1-70b-versatile',
173
- messages: [{ role: 'user', content: 'Hi' }],
174
- max_tokens: 10
175
- });
176
- const options = {
177
- hostname: 'api.groq.com',
178
- port: 443,
179
- path: '/openai/v1/chat/completions',
180
- method: 'POST',
181
- headers: {
182
- 'Content-Type': 'application/json',
183
- 'Authorization': `Bearer ${key}`,
184
- 'Content-Length': data.length
185
- }
186
- };
187
- req = https.request(options, (res) => {
188
- let responseData = '';
189
- res.on('data', chunk => responseData += chunk);
190
- res.on('end', () => {
191
- if (res.statusCode === 200) resolve({ success: true, message: 'OK' });
192
- else if (res.statusCode === 401) resolve({ success: false, message: 'Invalid API key' });
193
- else if (res.statusCode === 429) resolve({ success: false, message: 'Rate limit exceeded' });
194
- else resolve({ success: false, message: `Error ${res.statusCode}` });
195
- });
196
- });
197
- } else if (provider === 'gemini') {
198
- const data = JSON.stringify({ contents: [{ parts: [{ text: 'Hi' }] }] });
199
- const options = {
200
- hostname: 'generativelanguage.googleapis.com',
201
- port: 443,
202
- path: `/v1beta/models/${model || 'gemini-2.0-flash'}:generateContent?key=${key}`,
203
- method: 'POST',
204
- headers: {
205
- 'Content-Type': 'application/json',
206
- 'Content-Length': data.length
207
- }
208
- };
209
- req = https.request(options, (res) => {
210
- let responseData = '';
211
- res.on('data', chunk => responseData += chunk);
212
- res.on('end', () => {
213
- if (res.statusCode === 200) resolve({ success: true, message: 'OK' });
214
- else if (res.statusCode === 400) resolve({ success: false, message: 'Invalid API key or model' });
215
- else if (res.statusCode === 429) resolve({ success: false, message: 'Rate limit exceeded' });
216
- else resolve({ success: false, message: `Error ${res.statusCode}` });
217
- });
218
- });
219
- } else if (provider === 'openai') {
220
- const data = JSON.stringify({
221
- model: model || 'gpt-4o',
222
- messages: [{ role: 'user', content: 'Hi' }],
223
- max_tokens: 10
224
- });
225
- const options = {
226
- hostname: 'api.openai.com',
227
- port: 443,
228
- path: '/v1/chat/completions',
229
- method: 'POST',
230
- headers: {
231
- 'Content-Type': 'application/json',
232
- 'Authorization': `Bearer ${key}`,
233
- 'Content-Length': data.length
234
- }
235
- };
236
- req = https.request(options, (res) => {
237
- let responseData = '';
238
- res.on('data', chunk => responseData += chunk);
239
- res.on('end', () => {
240
- if (res.statusCode === 200) resolve({ success: true, message: 'OK' });
241
- else if (res.statusCode === 401) resolve({ success: false, message: 'Invalid API key' });
242
- else if (res.statusCode === 429) resolve({ success: false, message: 'Rate limit exceeded' });
243
- else resolve({ success: false, message: `Error ${res.statusCode}` });
244
- });
245
- });
246
- } else {
247
- resolve({ success: false, message: 'Provider not supported for testing' });
248
- return;
249
- }
250
-
251
- req.on('error', (err) => resolve({ success: false, message: err.message }));
252
- req.setTimeout(10000, () => {
253
- resolve({ success: false, message: 'Timeout' });
254
- req.destroy();
255
- });
256
- req.write(data);
257
- req.end();
258
- });
259
- }
260
-
261
- async function testAllApiKeys(config) {
262
- console.log('\n🧪 Testing API keys...\n');
263
- for (const key of config.keys) {
264
- if (key.type === 'oauth') {
265
- console.log(`✅ ${key.id} (OAuth) - Skipped (uses built-in auth)`);
266
- continue;
267
- }
268
- process.stdout.write(`Testing ${key.id} (${key.provider})... `);
269
- try {
270
- const result = await testProvider(key.key, key.provider, key.model);
271
- if (result.success) {
272
- console.log('✅ OK');
273
- key.active = true;
274
- } else {
275
- console.log(`❌ ${result.message}`);
276
- if (result.message.includes('Invalid')) key.active = false;
277
- }
278
- } catch (err) {
279
- console.log(`❌ ${err.message}`);
280
- }
281
- }
282
- saveApiConfig(config);
283
- console.log('\n');
284
- }
285
-
286
- async function setDefaultProvider(config) {
287
- const rl = createInterface();
288
- try {
289
- console.log('\nAvailable providers:');
290
- const providers = [...new Set(config.keys.filter(k => k.active).map(k => k.provider))];
291
- providers.forEach((p, i) => console.log(` ${i + 1}. ${p}`));
292
-
293
- const answer = await new Promise(resolve => {
294
- rl.question('Select default provider (name or number): ', resolve);
295
- });
296
-
297
- let selected = answer.trim();
298
- const num = parseInt(selected) - 1;
299
- if (!isNaN(num) && providers[num]) selected = providers[num];
300
-
301
- if (!providers.includes(selected)) {
302
- console.log('❌ Invalid provider');
303
- return;
304
- }
305
-
306
- config.default_provider = selected;
307
- saveApiConfig(config);
308
- console.log(`✅ Default provider set to: ${selected}`);
309
- } finally {
310
- rl.close();
311
- }
312
- }
313
-
314
- async function main() {
315
- const config = loadApiConfig();
316
- printMenu();
317
- const rl = createInterface();
318
-
319
- const ask = async () => {
320
- rl.question('Select option (1-6): ', async (answer) => {
321
- switch (answer.trim()) {
322
- case '1': viewConfiguredAPIs(config); printMenu(); ask(); break;
323
- case '2': await addNewApiKey(config); printMenu(); ask(); break;
324
- case '3': await removeApiKey(config); printMenu(); ask(); break;
325
- case '4': await testAllApiKeys(config); printMenu(); ask(); break;
326
- case '5': await setDefaultProvider(config); printMenu(); ask(); break;
327
- case '6': console.log('👋 Goodbye!'); rl.close(); break;
328
- default: console.log('Invalid option. Please enter 1-6.'); printMenu(); ask();
329
- }
330
- });
331
- };
332
-
333
- ask();
334
- }
335
-
336
- if (require.main === module) {
337
- main();
338
- }
339
-
340
- module.exports = { loadApiConfig, saveApiConfig, viewConfiguredAPIs };