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.
Files changed (2) hide show
  1. package/index.js +259 -0
  2. 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
+ }