opuszen2 1.3.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/index.js +259 -0
- package/package.json +26 -0
package/index.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const https = require('https');
|
|
9
|
+
const http = require('http');
|
|
10
|
+
|
|
11
|
+
// ── Color helpers ──────────────────────────────────────────────────────────────
|
|
12
|
+
const C = {
|
|
13
|
+
magenta: (s) => `\x1b[35m${s}\x1b[0m`,
|
|
14
|
+
bright: (s) => `\x1b[97m${s}\x1b[0m`,
|
|
15
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
16
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
17
|
+
purple: (s) => `\x1b[38;5;141m${s}\x1b[0m`,
|
|
18
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
19
|
+
reset: '\x1b[0m',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ── JSON helpers ───────────────────────────────────────────────────────────────
|
|
23
|
+
function readJson(filePath) {
|
|
24
|
+
try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); }
|
|
25
|
+
catch { return {}; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function writeJson(filePath, data) {
|
|
29
|
+
const dir = path.dirname(filePath);
|
|
30
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function deepMerge(target, source) {
|
|
35
|
+
for (const key of Object.keys(source)) {
|
|
36
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
37
|
+
if (!target[key]) target[key] = {};
|
|
38
|
+
deepMerge(target[key], source[key]);
|
|
39
|
+
} else {
|
|
40
|
+
target[key] = source[key];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return target;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Platform paths ─────────────────────────────────────────────────────────────
|
|
47
|
+
const HOME = os.homedir();
|
|
48
|
+
|
|
49
|
+
function getVSCodeSettingsPath() {
|
|
50
|
+
switch (process.platform) {
|
|
51
|
+
case 'win32':
|
|
52
|
+
return path.join(process.env.APPDATA || path.join(HOME, 'AppData', 'Roaming'), 'Code', 'User', 'settings.json');
|
|
53
|
+
case 'darwin':
|
|
54
|
+
return path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json');
|
|
55
|
+
default:
|
|
56
|
+
return path.join(HOME, '.config', 'Code', 'User', 'settings.json');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Readline helper ────────────────────────────────────────────────────────────
|
|
61
|
+
function createRL() {
|
|
62
|
+
return readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function ask(rl, question) {
|
|
66
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ── HTTPS verification ─────────────────────────────────────────────────────────
|
|
70
|
+
function verifyConnection(apiKey) {
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
const url = new URL('https://api.opuszen.shop/v1/models');
|
|
73
|
+
const options = {
|
|
74
|
+
hostname: url.hostname,
|
|
75
|
+
port: url.port || 443,
|
|
76
|
+
path: url.pathname,
|
|
77
|
+
method: 'GET',
|
|
78
|
+
headers: { 'x-api-key': apiKey },
|
|
79
|
+
timeout: 10000,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const req = https.request(options, (res) => {
|
|
83
|
+
let body = '';
|
|
84
|
+
res.on('data', (chunk) => { body += chunk; });
|
|
85
|
+
res.on('end', () => resolve({ status: res.statusCode, body }));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
req.on('error', (err) => resolve({ status: 0, error: err.message }));
|
|
89
|
+
req.on('timeout', () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
|
|
90
|
+
req.end();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── IDE configurators ──────────────────────────────────────────────────────────
|
|
95
|
+
function configureClaudeCodeCLI(apiKey) {
|
|
96
|
+
const settingsPath = path.join(HOME, '.claude', 'settings.json');
|
|
97
|
+
|
|
98
|
+
// settings.json
|
|
99
|
+
const settings = readJson(settingsPath);
|
|
100
|
+
deepMerge(settings, {
|
|
101
|
+
env: {
|
|
102
|
+
ANTHROPIC_AUTH_TOKEN: apiKey,
|
|
103
|
+
ANTHROPIC_BASE_URL: 'https://api.opuszen.shop',
|
|
104
|
+
// `[1m]` suffix on Opus tells Claude Code's /context this is a 1M-window
|
|
105
|
+
// model. Claude Code strips it client-side before the request reaches the
|
|
106
|
+
// proxy, so the proxy still sees a clean "Opus 4.7".
|
|
107
|
+
ANTHROPIC_MODEL: 'Opus 4.8[1m]',
|
|
108
|
+
ANTHROPIC_SMALL_FAST_MODEL: 'Haiku 4.5',
|
|
109
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: 'Sonnet 4.6',
|
|
110
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: 'Opus 4.8[1m]',
|
|
111
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'Haiku 4.5',
|
|
112
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
|
|
113
|
+
},
|
|
114
|
+
hasCompletedOnboarding: true,
|
|
115
|
+
});
|
|
116
|
+
writeJson(settingsPath, settings);
|
|
117
|
+
console.log(` ${C.purple('\u2713')} Wrote ${C.magenta(settingsPath)}`);
|
|
118
|
+
// Web search & image analysis are built-in — no extra setup needed
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function configureVSCodeClaude(apiKey) {
|
|
122
|
+
configureClaudeCodeCLI(apiKey);
|
|
123
|
+
console.log(` ${C.purple('i')} VS Code Claude extension uses the same config as Claude Code CLI.`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function configureCursor(apiKey) {
|
|
127
|
+
// Web search & image analysis are built-in — no extra setup needed
|
|
128
|
+
console.log(` ${C.purple('i')} For API routing, open Cursor Settings > Models > Add OpenAI-compatible model with base URL: ${C.bold('https://api.opuszen.shop/v1')}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function configureWindsurf(apiKey) {
|
|
132
|
+
// Web search & image analysis are built-in — no extra setup needed
|
|
133
|
+
console.log(` ${C.purple('i')} For API routing, open Windsurf Settings > AI Provider and set base URL: ${C.bold('https://api.opuszen.shop/v1')}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function configureCline(apiKey) {
|
|
137
|
+
const settingsPath = getVSCodeSettingsPath();
|
|
138
|
+
const existing = readJson(settingsPath);
|
|
139
|
+
deepMerge(existing, {
|
|
140
|
+
'cline.apiProvider': 'anthropic',
|
|
141
|
+
'cline.anthropicBaseUrl': 'https://api.opuszen.shop/v1',
|
|
142
|
+
'cline.apiKey': apiKey,
|
|
143
|
+
});
|
|
144
|
+
writeJson(settingsPath, existing);
|
|
145
|
+
console.log(` ${C.purple('\u2713')} Wrote ${C.magenta(settingsPath)}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function configureRooCode(apiKey) {
|
|
149
|
+
const settingsPath = getVSCodeSettingsPath();
|
|
150
|
+
const existing = readJson(settingsPath);
|
|
151
|
+
deepMerge(existing, {
|
|
152
|
+
'roo-cline.apiProvider': 'anthropic',
|
|
153
|
+
'roo-cline.anthropicBaseUrl': 'https://api.opuszen.shop/v1',
|
|
154
|
+
'roo-cline.apiKey': apiKey,
|
|
155
|
+
});
|
|
156
|
+
writeJson(settingsPath, existing);
|
|
157
|
+
console.log(` ${C.purple('\u2713')} Wrote ${C.magenta(settingsPath)}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── IDE registry ───────────────────────────────────────────────────────────────
|
|
161
|
+
const IDES = [
|
|
162
|
+
{ id: 1, name: 'Claude Code (CLI)', configure: configureClaudeCodeCLI },
|
|
163
|
+
{ id: 2, name: 'VS Code (Claude Extension)', configure: configureVSCodeClaude },
|
|
164
|
+
{ id: 3, name: 'Cursor', configure: configureCursor },
|
|
165
|
+
{ id: 4, name: 'Windsurf', configure: configureWindsurf },
|
|
166
|
+
{ id: 5, name: 'Cline (VS Code Extension)', configure: configureCline },
|
|
167
|
+
{ id: 6, name: 'Roo Code (VS Code Extension)', configure: configureRooCode },
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
// ── Main ───────────────────────────────────────────────────────────────────────
|
|
171
|
+
async function main() {
|
|
172
|
+
const rl = createRL();
|
|
173
|
+
|
|
174
|
+
// 1. Banner
|
|
175
|
+
console.log('');
|
|
176
|
+
console.log(C.magenta('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557'));
|
|
177
|
+
console.log(C.magenta('\u2551') + C.bold(' \u2726 Opus Zen 2.0 Setup ') + C.magenta('\u2551'));
|
|
178
|
+
console.log(C.magenta('\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D'));
|
|
179
|
+
console.log('');
|
|
180
|
+
|
|
181
|
+
// 2. API key prompt
|
|
182
|
+
let apiKey = '';
|
|
183
|
+
while (!apiKey.trim()) {
|
|
184
|
+
apiKey = await ask(rl, C.bold('Enter your Opus Zen 2.0 API key: '));
|
|
185
|
+
if (!apiKey.trim()) {
|
|
186
|
+
console.log(C.red('\u2717 API key cannot be empty. Please try again.'));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
apiKey = apiKey.trim();
|
|
190
|
+
console.log('');
|
|
191
|
+
|
|
192
|
+
// 3. IDE selection
|
|
193
|
+
console.log(C.bold('Select IDEs to configure (space-separated numbers, or \'a\' for all):\n'));
|
|
194
|
+
for (const ide of IDES) {
|
|
195
|
+
console.log(` ${C.purple('[' + ide.id + ']')} ${ide.name}`);
|
|
196
|
+
}
|
|
197
|
+
console.log('');
|
|
198
|
+
|
|
199
|
+
const choice = await ask(rl, C.bold('Your choice: '));
|
|
200
|
+
console.log('');
|
|
201
|
+
|
|
202
|
+
let selectedIds;
|
|
203
|
+
if (choice.trim().toLowerCase() === 'a') {
|
|
204
|
+
selectedIds = IDES.map((ide) => ide.id);
|
|
205
|
+
} else {
|
|
206
|
+
selectedIds = choice
|
|
207
|
+
.trim()
|
|
208
|
+
.split(/[\s,]+/)
|
|
209
|
+
.map(Number)
|
|
210
|
+
.filter((n) => n >= 1 && n <= IDES.length);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (selectedIds.length === 0) {
|
|
214
|
+
console.log(C.yellow('\u26A0 No valid IDEs selected. Exiting.'));
|
|
215
|
+
rl.close();
|
|
216
|
+
process.exit(0);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 4. Configure each selected IDE
|
|
220
|
+
const selectedIDEs = IDES.filter((ide) => selectedIds.includes(ide.id));
|
|
221
|
+
|
|
222
|
+
for (const ide of selectedIDEs) {
|
|
223
|
+
console.log(C.bold(`\nConfiguring ${C.magenta(ide.name)}...`));
|
|
224
|
+
try {
|
|
225
|
+
ide.configure(apiKey);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
console.log(` ${C.red('\u2717')} Failed to configure ${ide.name}: ${err.message}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log('');
|
|
232
|
+
|
|
233
|
+
// 5. Verify connection
|
|
234
|
+
console.log(C.bold('Verifying connection to Opus Zen 2.0 API...'));
|
|
235
|
+
const result = await verifyConnection(apiKey);
|
|
236
|
+
|
|
237
|
+
if (result.status === 200) {
|
|
238
|
+
console.log(` ${C.purple('\u2713')} Connected \u2014 API key is valid.`);
|
|
239
|
+
} else if (result.status > 0) {
|
|
240
|
+
console.log(` ${C.yellow('\u26A0')} HTTP ${result.status} \u2014 the server responded, but the key may be invalid.`);
|
|
241
|
+
} else {
|
|
242
|
+
console.log(` ${C.yellow('\u26A0')} Could not reach the API (${result.error}). Check your network and try again.`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 6. Summary
|
|
246
|
+
console.log('');
|
|
247
|
+
console.log(C.purple('\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557'));
|
|
248
|
+
console.log(C.purple('\u2551') + C.bold(' \u2713 Setup complete! ') + C.purple('\u2551'));
|
|
249
|
+
console.log(C.purple('\u2551') + ' Restart your IDE(s) to apply. ' + C.purple('\u2551'));
|
|
250
|
+
console.log(C.purple('\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D'));
|
|
251
|
+
console.log('');
|
|
252
|
+
|
|
253
|
+
rl.close();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
main().catch((err) => {
|
|
257
|
+
console.error(C.red(`\nFatal error: ${err.message}`));
|
|
258
|
+
process.exit(1);
|
|
259
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opuszen2",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Opus Zen 2.0 setup wizard — configure AI IDEs to use Opus Zen 2.0 API gateway",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"opuszen2": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"opuszen2",
|
|
11
|
+
"claude",
|
|
12
|
+
"ai",
|
|
13
|
+
"ide",
|
|
14
|
+
"setup",
|
|
15
|
+
"anthropic",
|
|
16
|
+
"mcp"
|
|
17
|
+
],
|
|
18
|
+
"author": "Prem Kumar",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18.0.0"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
}
|
|
26
|
+
}
|