opencode-azure-setup 1.0.0 → 1.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/index.js +93 -41
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -33,13 +33,10 @@ const logo = `
|
|
|
33
33
|
|_| Azure Edition
|
|
34
34
|
`;
|
|
35
35
|
|
|
36
|
-
// Get config path
|
|
37
36
|
function getConfigPath() {
|
|
38
|
-
|
|
39
|
-
return path.join(home, '.config', 'opencode', 'opencode.json');
|
|
37
|
+
return path.join(os.homedir(), '.config', 'opencode', 'opencode.json');
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
// Readline interface
|
|
43
40
|
const rl = readline.createInterface({
|
|
44
41
|
input: process.stdin,
|
|
45
42
|
output: process.stdout,
|
|
@@ -55,13 +52,11 @@ function ask(question, defaultValue = '') {
|
|
|
55
52
|
function askPassword(question) {
|
|
56
53
|
return new Promise((resolve) => {
|
|
57
54
|
process.stdout.write(`${question}: `);
|
|
58
|
-
|
|
59
55
|
if (process.stdin.isTTY) {
|
|
60
56
|
const stdin = process.stdin;
|
|
61
57
|
stdin.setRawMode(true);
|
|
62
58
|
stdin.resume();
|
|
63
59
|
stdin.setEncoding('utf8');
|
|
64
|
-
|
|
65
60
|
let password = '';
|
|
66
61
|
const onData = (char) => {
|
|
67
62
|
if (char === '\n' || char === '\r' || char === '\u0004') {
|
|
@@ -84,13 +79,61 @@ function askPassword(question) {
|
|
|
84
79
|
});
|
|
85
80
|
}
|
|
86
81
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
// Parse Azure endpoint - handles both full URL and base URL
|
|
83
|
+
function parseAzureEndpoint(input) {
|
|
84
|
+
const result = {
|
|
85
|
+
baseUrl: '',
|
|
86
|
+
deployment: 'model-router',
|
|
87
|
+
apiVersion: '2025-01-01-preview', // Latest API version
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const url = new URL(input);
|
|
92
|
+
|
|
93
|
+
// Extract deployment from path: /openai/deployments/{deployment}/...
|
|
94
|
+
const deploymentMatch = url.pathname.match(/\/deployments\/([^/]+)/);
|
|
95
|
+
if (deploymentMatch) {
|
|
96
|
+
result.deployment = deploymentMatch[1];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Extract api-version from query params
|
|
100
|
+
const apiVersion = url.searchParams.get('api-version');
|
|
101
|
+
if (apiVersion) {
|
|
102
|
+
result.apiVersion = apiVersion;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Build base URL: https://host/openai
|
|
106
|
+
const pathParts = url.pathname.split('/');
|
|
107
|
+
const openaiIndex = pathParts.indexOf('openai');
|
|
108
|
+
if (openaiIndex !== -1) {
|
|
109
|
+
url.pathname = pathParts.slice(0, openaiIndex + 1).join('/');
|
|
110
|
+
} else {
|
|
111
|
+
url.pathname = '/openai';
|
|
112
|
+
}
|
|
113
|
+
url.search = '';
|
|
114
|
+
result.baseUrl = url.toString().replace(/\/$/, '');
|
|
115
|
+
|
|
116
|
+
} catch {
|
|
117
|
+
// Not a valid URL, assume it's just the host
|
|
118
|
+
let cleaned = input.replace(/\/$/, '');
|
|
119
|
+
if (!cleaned.startsWith('https://')) {
|
|
120
|
+
cleaned = 'https://' + cleaned;
|
|
121
|
+
}
|
|
122
|
+
if (!cleaned.endsWith('/openai')) {
|
|
123
|
+
cleaned += '/openai';
|
|
124
|
+
}
|
|
125
|
+
result.baseUrl = cleaned;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
90
130
|
|
|
131
|
+
async function testConnection(endpoint, apiKey, deployment, apiVersion) {
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
const url = new URL(`${endpoint}/deployments/${deployment}/chat/completions?api-version=${apiVersion}`);
|
|
91
134
|
const options = {
|
|
92
135
|
hostname: url.hostname,
|
|
93
|
-
port:
|
|
136
|
+
port: 443,
|
|
94
137
|
path: url.pathname + url.search,
|
|
95
138
|
method: 'POST',
|
|
96
139
|
headers: {
|
|
@@ -111,7 +154,7 @@ async function testConnection(endpoint, apiKey, deployment) {
|
|
|
111
154
|
});
|
|
112
155
|
|
|
113
156
|
req.on('error', (e) => resolve({ ok: false, status: 0, body: e.message }));
|
|
114
|
-
req.setTimeout(
|
|
157
|
+
req.setTimeout(15000, () => {
|
|
115
158
|
req.destroy();
|
|
116
159
|
resolve({ ok: false, status: 0, body: 'Timeout' });
|
|
117
160
|
});
|
|
@@ -127,39 +170,37 @@ async function main() {
|
|
|
127
170
|
console.log('─'.repeat(40));
|
|
128
171
|
console.log();
|
|
129
172
|
|
|
130
|
-
// Endpoint
|
|
131
|
-
console.log('
|
|
132
|
-
console.log(colors.dim + '
|
|
133
|
-
|
|
173
|
+
// Endpoint - accepts full URL or just the base
|
|
174
|
+
console.log('Paste your Azure OpenAI endpoint');
|
|
175
|
+
console.log(colors.dim + 'Tip: You can paste the full URL from Azure Portal - we\'ll extract what we need' + colors.reset);
|
|
176
|
+
console.log();
|
|
177
|
+
const rawEndpoint = await ask('Endpoint');
|
|
134
178
|
|
|
135
|
-
if (!
|
|
179
|
+
if (!rawEndpoint) {
|
|
136
180
|
console.log(colors.red + 'Endpoint is required' + colors.reset);
|
|
137
181
|
process.exit(1);
|
|
138
182
|
}
|
|
139
183
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
console.log();
|
|
184
|
+
// Parse the endpoint - extracts base URL, deployment, and api-version automatically
|
|
185
|
+
const parsed = parseAzureEndpoint(rawEndpoint);
|
|
144
186
|
|
|
145
187
|
// API Key
|
|
188
|
+
console.log();
|
|
146
189
|
const apiKey = await askPassword('API Key');
|
|
147
190
|
if (!apiKey) {
|
|
148
191
|
console.log(colors.red + 'API Key is required' + colors.reset);
|
|
149
192
|
process.exit(1);
|
|
150
193
|
}
|
|
151
194
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
console.log('Enter your deployment name');
|
|
156
|
-
console.log(colors.dim + '(default: model-router for Azure APIM setups)' + colors.reset);
|
|
157
|
-
const deployment = await ask('Deployment', 'model-router');
|
|
195
|
+
// Use auto-detected values
|
|
196
|
+
let deployment = parsed.deployment;
|
|
197
|
+
let apiVersion = parsed.apiVersion;
|
|
158
198
|
|
|
159
199
|
console.log();
|
|
160
200
|
console.log(colors.blue + 'Testing connection...' + colors.reset);
|
|
201
|
+
console.log(colors.dim + ` ${parsed.baseUrl}/deployments/${deployment}` + colors.reset);
|
|
161
202
|
|
|
162
|
-
|
|
203
|
+
let result = await testConnection(parsed.baseUrl, apiKey, deployment, apiVersion);
|
|
163
204
|
|
|
164
205
|
if (result.ok) {
|
|
165
206
|
console.log(colors.green + '✓ Connection successful!' + colors.reset);
|
|
@@ -173,16 +214,29 @@ async function main() {
|
|
|
173
214
|
console.log(colors.dim + result.body.slice(0, 200) + colors.reset);
|
|
174
215
|
}
|
|
175
216
|
}
|
|
217
|
+
|
|
218
|
+
// Offer to edit settings if connection failed
|
|
176
219
|
console.log();
|
|
177
|
-
|
|
178
|
-
|
|
220
|
+
console.log(colors.yellow + 'Let\'s try different settings:' + colors.reset);
|
|
221
|
+
deployment = await ask('Deployment name', deployment);
|
|
222
|
+
apiVersion = await ask('API Version', apiVersion);
|
|
223
|
+
|
|
224
|
+
console.log();
|
|
225
|
+
console.log(colors.blue + 'Retrying...' + colors.reset);
|
|
226
|
+
result = await testConnection(parsed.baseUrl, apiKey, deployment, apiVersion);
|
|
227
|
+
|
|
228
|
+
if (result.ok) {
|
|
229
|
+
console.log(colors.green + '✓ Connection successful!' + colors.reset);
|
|
230
|
+
} else {
|
|
231
|
+
console.log(colors.red + `✗ Still failing (${result.status || 'error'})` + colors.reset);
|
|
232
|
+
const cont = await ask('Save config anyway? (y/N)', 'N');
|
|
233
|
+
if (cont.toLowerCase() !== 'y') process.exit(1);
|
|
234
|
+
}
|
|
179
235
|
}
|
|
180
236
|
|
|
181
237
|
// Create config
|
|
182
238
|
const configPath = getConfigPath();
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
239
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
186
240
|
|
|
187
241
|
const config = {
|
|
188
242
|
$schema: 'https://opencode.ai/config.json',
|
|
@@ -192,10 +246,10 @@ async function main() {
|
|
|
192
246
|
npm: '@ai-sdk/azure',
|
|
193
247
|
name: 'Azure OpenAI',
|
|
194
248
|
options: {
|
|
195
|
-
baseURL:
|
|
249
|
+
baseURL: parsed.baseUrl,
|
|
196
250
|
apiKey: apiKey,
|
|
197
251
|
useDeploymentBasedUrls: true,
|
|
198
|
-
apiVersion:
|
|
252
|
+
apiVersion: apiVersion,
|
|
199
253
|
},
|
|
200
254
|
models: {
|
|
201
255
|
[deployment]: {
|
|
@@ -210,15 +264,13 @@ async function main() {
|
|
|
210
264
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
211
265
|
|
|
212
266
|
console.log();
|
|
213
|
-
console.log(colors.green +
|
|
267
|
+
console.log(colors.green + '✓ Configuration saved!' + colors.reset);
|
|
268
|
+
console.log(colors.dim + ` ${configPath}` + colors.reset);
|
|
214
269
|
console.log();
|
|
215
|
-
console.log(
|
|
216
|
-
console.log();
|
|
217
|
-
console.log(' opencode');
|
|
270
|
+
console.log('─'.repeat(40));
|
|
271
|
+
console.log(colors.green + 'You\'re all set! Run:' + colors.reset);
|
|
218
272
|
console.log();
|
|
219
|
-
console.log(colors.
|
|
220
|
-
console.log(' • View config: opencode azure status');
|
|
221
|
-
console.log(' • Reconfigure: opencode azure');
|
|
273
|
+
console.log(' ' + colors.blue + 'opencode' + colors.reset);
|
|
222
274
|
console.log();
|
|
223
275
|
|
|
224
276
|
rl.close();
|