opencode-azure-setup 1.0.4 → 1.0.6
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/index.js +91 -43
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* npx opencode-azure-setup
|
|
8
|
+
* npx opencode-azure-setup -y # Non-interactive (use existing config)
|
|
8
9
|
* node install-azure.js
|
|
9
10
|
*/
|
|
10
11
|
|
|
@@ -14,6 +15,10 @@ import readline from 'readline';
|
|
|
14
15
|
import https from 'https';
|
|
15
16
|
import os from 'os';
|
|
16
17
|
|
|
18
|
+
// Parse args
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
const nonInteractive = args.includes('-y') || args.includes('--yes') || args.includes('--non-interactive');
|
|
21
|
+
|
|
17
22
|
// Colors
|
|
18
23
|
const colors = {
|
|
19
24
|
blue: '\x1b[34m',
|
|
@@ -67,15 +72,22 @@ function getExistingAzureSettings(config) {
|
|
|
67
72
|
};
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
// Only create readline interface if not in non-interactive mode
|
|
76
|
+
let rl = null;
|
|
77
|
+
function getRl() {
|
|
78
|
+
if (!rl) {
|
|
79
|
+
rl = readline.createInterface({
|
|
80
|
+
input: process.stdin,
|
|
81
|
+
output: process.stdout,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return rl;
|
|
85
|
+
}
|
|
74
86
|
|
|
75
87
|
function ask(question, defaultValue = '') {
|
|
76
88
|
return new Promise((resolve) => {
|
|
77
89
|
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
78
|
-
|
|
90
|
+
getRl().question(prompt, (answer) => resolve(answer || defaultValue));
|
|
79
91
|
});
|
|
80
92
|
}
|
|
81
93
|
|
|
@@ -240,14 +252,29 @@ async function main() {
|
|
|
240
252
|
// If existing config found, show current values
|
|
241
253
|
if (existingAzure && existingAzure.baseUrl) {
|
|
242
254
|
console.log(colors.green + '✓ Existing configuration found' + colors.reset);
|
|
243
|
-
|
|
255
|
+
if (nonInteractive) {
|
|
256
|
+
console.log(colors.dim + ' Using existing values (non-interactive mode)' + colors.reset);
|
|
257
|
+
} else {
|
|
258
|
+
console.log(colors.dim + ' Press Enter to keep current values, or type new ones' + colors.reset);
|
|
259
|
+
}
|
|
244
260
|
console.log();
|
|
245
261
|
}
|
|
246
262
|
|
|
247
263
|
// Endpoint - accepts full URL or just the base
|
|
248
|
-
let baseUrl, deployment, apiVersion;
|
|
249
|
-
|
|
250
|
-
|
|
264
|
+
let baseUrl, deployment, apiVersion, apiKey;
|
|
265
|
+
|
|
266
|
+
// Non-interactive mode with existing config - skip all prompts
|
|
267
|
+
if (nonInteractive && existingAzure?.baseUrl && existingAzure?.apiKey) {
|
|
268
|
+
baseUrl = existingAzure.baseUrl;
|
|
269
|
+
deployment = existingAzure.deployment;
|
|
270
|
+
apiVersion = existingAzure.apiVersion;
|
|
271
|
+
apiKey = existingAzure.apiKey;
|
|
272
|
+
console.log(colors.dim + ` Endpoint: ${baseUrl}` + colors.reset);
|
|
273
|
+
console.log(colors.dim + ` Deployment: ${deployment}` + colors.reset);
|
|
274
|
+
} else if (nonInteractive && !existingAzure?.baseUrl) {
|
|
275
|
+
console.log(colors.red + 'No existing config found. Run without -y flag to configure.' + colors.reset);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
} else if (existingAzure?.baseUrl) {
|
|
251
278
|
console.log('Azure OpenAI Endpoint');
|
|
252
279
|
const rawEndpoint = await ask('Endpoint', existingAzure.baseUrl);
|
|
253
280
|
|
|
@@ -263,6 +290,22 @@ async function main() {
|
|
|
263
290
|
deployment = parsed.deployment;
|
|
264
291
|
apiVersion = parsed.apiVersion;
|
|
265
292
|
}
|
|
293
|
+
|
|
294
|
+
// API Key
|
|
295
|
+
console.log();
|
|
296
|
+
apiKey = await askPassword('API Key', existingAzure?.apiKey || '');
|
|
297
|
+
if (!apiKey) {
|
|
298
|
+
console.log(colors.red + 'API Key is required' + colors.reset);
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Deployment (only ask if not using existing)
|
|
303
|
+
if (existingAzure?.deployment && deployment === existingAzure.deployment) {
|
|
304
|
+
// Keep existing
|
|
305
|
+
} else {
|
|
306
|
+
console.log();
|
|
307
|
+
deployment = await ask('Deployment name', deployment);
|
|
308
|
+
}
|
|
266
309
|
} else {
|
|
267
310
|
console.log('Paste your Azure OpenAI endpoint');
|
|
268
311
|
console.log(colors.dim + 'Tip: You can paste the full URL from Azure Portal - we\'ll extract what we need' + colors.reset);
|
|
@@ -278,20 +321,15 @@ async function main() {
|
|
|
278
321
|
baseUrl = parsed.baseUrl;
|
|
279
322
|
deployment = parsed.deployment;
|
|
280
323
|
apiVersion = parsed.apiVersion;
|
|
281
|
-
}
|
|
282
324
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
325
|
+
// API Key
|
|
326
|
+
console.log();
|
|
327
|
+
apiKey = await askPassword('API Key');
|
|
328
|
+
if (!apiKey) {
|
|
329
|
+
console.log(colors.red + 'API Key is required' + colors.reset);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
290
332
|
|
|
291
|
-
// Deployment (only ask if not using existing)
|
|
292
|
-
if (existingAzure?.deployment && deployment === existingAzure.deployment) {
|
|
293
|
-
// Keep existing
|
|
294
|
-
} else {
|
|
295
333
|
console.log();
|
|
296
334
|
deployment = await ask('Deployment name', deployment);
|
|
297
335
|
}
|
|
@@ -315,22 +353,27 @@ async function main() {
|
|
|
315
353
|
}
|
|
316
354
|
}
|
|
317
355
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
deployment = await ask('Deployment name', deployment);
|
|
322
|
-
apiVersion = await ask('API Version', apiVersion);
|
|
323
|
-
|
|
324
|
-
console.log();
|
|
325
|
-
console.log(colors.blue + 'Retrying...' + colors.reset);
|
|
326
|
-
result = await testConnection(baseUrl, apiKey, deployment, apiVersion);
|
|
327
|
-
|
|
328
|
-
if (result.ok) {
|
|
329
|
-
console.log(colors.green + '✓ Connection successful!' + colors.reset);
|
|
356
|
+
if (nonInteractive) {
|
|
357
|
+
// Non-interactive mode - just continue with existing config
|
|
358
|
+
console.log(colors.yellow + '⚠ Continuing anyway (non-interactive mode)' + colors.reset);
|
|
330
359
|
} else {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
360
|
+
// Offer to edit settings if connection failed
|
|
361
|
+
console.log();
|
|
362
|
+
console.log(colors.yellow + 'Let\'s try different settings:' + colors.reset);
|
|
363
|
+
deployment = await ask('Deployment name', deployment);
|
|
364
|
+
apiVersion = await ask('API Version', apiVersion);
|
|
365
|
+
|
|
366
|
+
console.log();
|
|
367
|
+
console.log(colors.blue + 'Retrying...' + colors.reset);
|
|
368
|
+
result = await testConnection(baseUrl, apiKey, deployment, apiVersion);
|
|
369
|
+
|
|
370
|
+
if (result.ok) {
|
|
371
|
+
console.log(colors.green + '✓ Connection successful!' + colors.reset);
|
|
372
|
+
} else {
|
|
373
|
+
console.log(colors.red + `✗ Still failing (${result.status || 'error'})` + colors.reset);
|
|
374
|
+
const cont = await ask('Save config anyway? (y/N)', 'N');
|
|
375
|
+
if (cont.toLowerCase() !== 'y') process.exit(1);
|
|
376
|
+
}
|
|
334
377
|
}
|
|
335
378
|
}
|
|
336
379
|
|
|
@@ -368,18 +411,22 @@ async function main() {
|
|
|
368
411
|
console.log();
|
|
369
412
|
console.log(colors.blue + 'Setting up MCP Marketplace...' + colors.reset);
|
|
370
413
|
|
|
371
|
-
const mcpDir = path.join(os.homedir(), '.config', 'opencode', 'mcps');
|
|
414
|
+
const mcpDir = path.join(os.homedir(), '.config', 'opencode', 'mcps', 'mcp-marketplace');
|
|
372
415
|
fs.mkdirSync(mcpDir, { recursive: true });
|
|
373
416
|
|
|
374
417
|
try {
|
|
375
418
|
const { execFileSync } = await import('child_process');
|
|
376
419
|
|
|
377
|
-
//
|
|
378
|
-
|
|
420
|
+
// Initialize package.json if not exists
|
|
421
|
+
const pkgPath = path.join(mcpDir, 'package.json');
|
|
422
|
+
if (!fs.existsSync(pkgPath)) {
|
|
423
|
+
fs.writeFileSync(pkgPath, JSON.stringify({ name: 'opencode-mcps', version: '1.0.0', type: 'module' }, null, 2));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Install mcp-marketplace package locally (no sudo needed)
|
|
427
|
+
execFileSync('npm', ['install', 'opencode-mcp-marketplace@latest'], { cwd: mcpDir, stdio: 'pipe' });
|
|
379
428
|
|
|
380
|
-
|
|
381
|
-
const npmRoot = execFileSync('npm', ['root', '-g'], { encoding: 'utf-8' }).trim();
|
|
382
|
-
const mcpPath = path.join(npmRoot, 'opencode-mcp-marketplace', 'dist', 'index.js');
|
|
429
|
+
const mcpPath = path.join(mcpDir, 'node_modules', 'opencode-mcp-marketplace', 'dist', 'index.js');
|
|
383
430
|
|
|
384
431
|
// Add to config
|
|
385
432
|
config.mcp = config.mcp || {};
|
|
@@ -391,6 +438,7 @@ async function main() {
|
|
|
391
438
|
console.log(colors.green + '✓ MCP Marketplace installed!' + colors.reset);
|
|
392
439
|
} catch (e) {
|
|
393
440
|
console.log(colors.yellow + '⚠ MCP Marketplace install skipped (npm not available or failed)' + colors.reset);
|
|
441
|
+
console.log(colors.dim + ' You can install manually: npm install -g opencode-mcp-marketplace' + colors.reset);
|
|
394
442
|
}
|
|
395
443
|
|
|
396
444
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
@@ -416,7 +464,7 @@ async function main() {
|
|
|
416
464
|
console.log(' ' + colors.blue + 'opencode' + colors.reset);
|
|
417
465
|
console.log();
|
|
418
466
|
|
|
419
|
-
rl.close();
|
|
467
|
+
if (rl) rl.close();
|
|
420
468
|
}
|
|
421
469
|
|
|
422
470
|
main().catch((err) => {
|