kentutai-app 1.0.8 → 1.1.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/cli/package.json +4 -4
- package/cli/src/cli/api/client.js +547 -0
- package/cli/src/cli/menus/apiKeys.js +233 -0
- package/cli/src/cli/menus/cliTools.js +618 -0
- package/cli/src/cli/menus/combos.js +477 -0
- package/cli/src/cli/menus/providers.js +845 -0
- package/cli/src/cli/menus/settings.js +207 -0
- package/cli/src/cli/terminalUI.js +121 -0
- package/cli/src/cli/tray/autostart.js +306 -0
- package/cli/src/cli/tray/icon.ico +0 -0
- package/cli/src/cli/tray/icon.png +0 -0
- package/cli/src/cli/tray/tray.js +322 -0
- package/cli/src/cli/tray/tray.ps1 +79 -0
- package/cli/src/cli/tray/trayWin.js +89 -0
- package/cli/src/cli/utils/clipboard.js +30 -0
- package/cli/src/cli/utils/display.js +42 -0
- package/cli/src/cli/utils/endpoint.js +32 -0
- package/cli/src/cli/utils/format.js +125 -0
- package/cli/src/cli/utils/input.js +130 -0
- package/cli/src/cli/utils/menuHelper.js +156 -0
- package/cli/src/cli/utils/modelSelector.js +136 -0
- package/package.json +1 -1
package/cli/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kentutai",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "KentutAI - AI Router CLI",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"cli.js",
|
|
11
|
-
"src
|
|
12
|
-
"hooks
|
|
13
|
-
"app
|
|
11
|
+
"src",
|
|
12
|
+
"hooks",
|
|
13
|
+
"app",
|
|
14
14
|
"README.md",
|
|
15
15
|
"LICENSE"
|
|
16
16
|
],
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
const http = require("http");
|
|
2
|
+
const https = require("https");
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const { machineIdSync } = require("node-machine-id");
|
|
8
|
+
|
|
9
|
+
// Default configuration
|
|
10
|
+
const DEFAULT_CONFIG = {
|
|
11
|
+
host: "localhost",
|
|
12
|
+
port: 20123,
|
|
13
|
+
protocol: "http:",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const CLI_TOKEN_HEADER = "x-9r-cli-token";
|
|
17
|
+
const CLI_TOKEN_SALT = "kt-cli-auth";
|
|
18
|
+
const APP_NAME = "kentutai";
|
|
19
|
+
|
|
20
|
+
function getDataDir() {
|
|
21
|
+
if (process.env.DATA_DIR) return process.env.DATA_DIR;
|
|
22
|
+
if (process.platform === "win32") {
|
|
23
|
+
return path.join(process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"), APP_NAME);
|
|
24
|
+
}
|
|
25
|
+
return path.join(os.homedir(), `.${APP_NAME}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const MACHINE_ID_FILE = path.join(getDataDir(), "machine-id");
|
|
29
|
+
const AUTH_DIR = path.join(getDataDir(), "auth");
|
|
30
|
+
const CLI_SECRET_FILE = path.join(AUTH_DIR, "cli-secret");
|
|
31
|
+
|
|
32
|
+
let config = { ...DEFAULT_CONFIG };
|
|
33
|
+
let cachedCliToken = null;
|
|
34
|
+
let cachedCliSecret = null;
|
|
35
|
+
|
|
36
|
+
// Read raw machineId from shared file (written by server) → guarantees token match
|
|
37
|
+
function loadRawMachineId() {
|
|
38
|
+
try {
|
|
39
|
+
const raw = fs.readFileSync(MACHINE_ID_FILE, "utf8").trim();
|
|
40
|
+
if (raw) return raw;
|
|
41
|
+
} catch {}
|
|
42
|
+
try { return machineIdSync(); } catch { return ""; }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Random secret shared with server via file → token unpredictable from machineId alone.
|
|
46
|
+
function loadCliSecret() {
|
|
47
|
+
if (cachedCliSecret) return cachedCliSecret;
|
|
48
|
+
try {
|
|
49
|
+
cachedCliSecret = fs.readFileSync(CLI_SECRET_FILE, "utf8").trim();
|
|
50
|
+
if (cachedCliSecret) return cachedCliSecret;
|
|
51
|
+
} catch {}
|
|
52
|
+
cachedCliSecret = crypto.randomBytes(32).toString("hex");
|
|
53
|
+
try {
|
|
54
|
+
fs.mkdirSync(AUTH_DIR, { recursive: true });
|
|
55
|
+
fs.writeFileSync(CLI_SECRET_FILE, cachedCliSecret, { mode: 0o600 });
|
|
56
|
+
} catch {}
|
|
57
|
+
return cachedCliSecret;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getCliToken() {
|
|
61
|
+
if (cachedCliToken !== null) return cachedCliToken;
|
|
62
|
+
const raw = loadRawMachineId();
|
|
63
|
+
const secret = loadCliSecret();
|
|
64
|
+
cachedCliToken = raw ? crypto.createHash("sha256").update(raw + CLI_TOKEN_SALT + secret).digest("hex").substring(0, 16) : "";
|
|
65
|
+
return cachedCliToken;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Configure API client
|
|
70
|
+
* @param {Object} options - Configuration options
|
|
71
|
+
* @param {string} options.host - API host
|
|
72
|
+
* @param {number} options.port - API port
|
|
73
|
+
* @param {string} options.protocol - Protocol (http: or https:)
|
|
74
|
+
*/
|
|
75
|
+
function configure(options = {}) {
|
|
76
|
+
config = { ...config, ...options };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Make HTTP request to API
|
|
81
|
+
* @param {string} method - HTTP method
|
|
82
|
+
* @param {string} path - API path
|
|
83
|
+
* @param {Object} body - Request body (optional)
|
|
84
|
+
* @returns {Promise<Object>} Response with { success, data/error }
|
|
85
|
+
*/
|
|
86
|
+
function makeRequest(method, path, body = null) {
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
const httpModule = config.protocol === "https:" ? https : http;
|
|
89
|
+
|
|
90
|
+
const options = {
|
|
91
|
+
hostname: config.host,
|
|
92
|
+
port: config.port,
|
|
93
|
+
path: path,
|
|
94
|
+
method: method,
|
|
95
|
+
headers: {
|
|
96
|
+
"Content-Type": "application/json",
|
|
97
|
+
[CLI_TOKEN_HEADER]: getCliToken(),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Add Content-Length for POST/PUT requests
|
|
102
|
+
if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
103
|
+
const bodyString = JSON.stringify(body);
|
|
104
|
+
options.headers["Content-Length"] = Buffer.byteLength(bodyString);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const req = httpModule.request(options, (res) => {
|
|
108
|
+
let data = "";
|
|
109
|
+
|
|
110
|
+
res.on("data", (chunk) => {
|
|
111
|
+
data += chunk;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
res.on("end", () => {
|
|
115
|
+
try {
|
|
116
|
+
const parsed = data ? JSON.parse(data) : {};
|
|
117
|
+
|
|
118
|
+
// Check if response indicates error
|
|
119
|
+
if (res.statusCode >= 400 || parsed.error) {
|
|
120
|
+
resolve({
|
|
121
|
+
success: false,
|
|
122
|
+
error: parsed.error || `HTTP ${res.statusCode}`,
|
|
123
|
+
statusCode: res.statusCode,
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
resolve({
|
|
127
|
+
success: true,
|
|
128
|
+
data: parsed,
|
|
129
|
+
statusCode: res.statusCode,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
resolve({
|
|
134
|
+
success: false,
|
|
135
|
+
error: `Failed to parse response: ${err.message}`,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
req.on("error", (err) => {
|
|
142
|
+
resolve({
|
|
143
|
+
success: false,
|
|
144
|
+
error: `Network error: ${err.message}`,
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
req.on("timeout", () => {
|
|
149
|
+
req.destroy();
|
|
150
|
+
resolve({
|
|
151
|
+
success: false,
|
|
152
|
+
error: "Request timeout",
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Set timeout (30 seconds)
|
|
157
|
+
req.setTimeout(30000);
|
|
158
|
+
|
|
159
|
+
// Write body if present
|
|
160
|
+
if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
161
|
+
req.write(JSON.stringify(body));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
req.end();
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// PROVIDERS API
|
|
170
|
+
// ============================================================================
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get all providers
|
|
174
|
+
* @returns {Promise<Object>} { success, data: { connections } }
|
|
175
|
+
*/
|
|
176
|
+
async function getProviders() {
|
|
177
|
+
return makeRequest("GET", "/api/providers");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get provider by ID
|
|
182
|
+
* @param {string} id - Provider ID
|
|
183
|
+
* @returns {Promise<Object>} { success, data: { connection } }
|
|
184
|
+
*/
|
|
185
|
+
async function getProviderById(id) {
|
|
186
|
+
return makeRequest("GET", `/api/providers/${id}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Test provider connection
|
|
191
|
+
* @param {string} id - Provider ID
|
|
192
|
+
* @returns {Promise<Object>} { success, data: { valid, error } }
|
|
193
|
+
*/
|
|
194
|
+
async function testProvider(id) {
|
|
195
|
+
return makeRequest("POST", `/api/providers/${id}/test`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Delete provider
|
|
200
|
+
* @param {string} id - Provider ID
|
|
201
|
+
* @returns {Promise<Object>} { success, data: { message } }
|
|
202
|
+
*/
|
|
203
|
+
async function deleteProvider(id) {
|
|
204
|
+
return makeRequest("DELETE", `/api/providers/${id}`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get provider models
|
|
209
|
+
* @param {string} id - Provider ID
|
|
210
|
+
* @returns {Promise<Object>} { success, data: { provider, connectionId, models } }
|
|
211
|
+
*/
|
|
212
|
+
async function getProviderModels(id) {
|
|
213
|
+
return makeRequest("GET", `/api/providers/${id}/models`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ============================================================================
|
|
217
|
+
// OAUTH API
|
|
218
|
+
// ============================================================================
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Get OAuth authorization URL
|
|
222
|
+
* @param {string} provider - Provider ID
|
|
223
|
+
* @returns {Promise<Object>} { success, data: { authUrl, codeVerifier, state, redirectUri } }
|
|
224
|
+
*/
|
|
225
|
+
async function getOAuthAuthUrl(provider) {
|
|
226
|
+
// Codex requires fixed port 1455 and path /auth/callback
|
|
227
|
+
const redirectUri = provider === "codex"
|
|
228
|
+
? "http://localhost:1455/auth/callback"
|
|
229
|
+
: "http://localhost:20123/callback";
|
|
230
|
+
return makeRequest("GET", `/api/oauth/${provider}/authorize?redirect_uri=${encodeURIComponent(redirectUri)}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Exchange OAuth authorization code for token
|
|
235
|
+
* @param {string} provider - Provider ID
|
|
236
|
+
* @param {Object} data - { code, redirectUri, codeVerifier, state }
|
|
237
|
+
* @returns {Promise<Object>} { success, data }
|
|
238
|
+
*/
|
|
239
|
+
async function exchangeOAuthCode(provider, data) {
|
|
240
|
+
return makeRequest("POST", `/api/oauth/${provider}/exchange`, data);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get OAuth device code
|
|
245
|
+
* @param {string} provider - Provider ID
|
|
246
|
+
* @returns {Promise<Object>} { success, data: { device_code, user_code, verification_uri, verification_uri_complete, codeVerifier, extraData } }
|
|
247
|
+
*/
|
|
248
|
+
async function getOAuthDeviceCode(provider) {
|
|
249
|
+
return makeRequest("GET", `/api/oauth/${provider}/device-code`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Poll OAuth token using device code
|
|
254
|
+
* @param {string} provider - Provider ID
|
|
255
|
+
* @param {Object} data - { deviceCode, codeVerifier, extraData }
|
|
256
|
+
* @returns {Promise<Object>} { success, data: { pending } }
|
|
257
|
+
*/
|
|
258
|
+
async function pollOAuthToken(provider, data) {
|
|
259
|
+
return makeRequest("POST", `/api/oauth/${provider}/poll`, data);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Create API key provider connection
|
|
264
|
+
* @param {Object} data - { provider, name, apiKey }
|
|
265
|
+
* @returns {Promise<Object>} { success, data }
|
|
266
|
+
*/
|
|
267
|
+
async function createApiKeyProvider(data) {
|
|
268
|
+
return makeRequest("POST", "/api/providers", data);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Update provider connection
|
|
273
|
+
* @param {string} id - Connection ID
|
|
274
|
+
* @param {Object} data - { name, priority, defaultModel, isActive }
|
|
275
|
+
* @returns {Promise<Object>} { success, data: { connection } }
|
|
276
|
+
*/
|
|
277
|
+
async function updateConnection(id, data) {
|
|
278
|
+
return makeRequest("PUT", `/api/providers/${id}`, data);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// API KEYS API
|
|
283
|
+
// ============================================================================
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get all API keys
|
|
287
|
+
* @returns {Promise<Object>} { success, data: { keys } }
|
|
288
|
+
*/
|
|
289
|
+
async function getApiKeys() {
|
|
290
|
+
return makeRequest("GET", "/api/keys");
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Create new API key
|
|
295
|
+
* @param {string} name - Key name
|
|
296
|
+
* @returns {Promise<Object>} { success, data: { key, name, id, machineId } }
|
|
297
|
+
*/
|
|
298
|
+
async function createApiKey(name) {
|
|
299
|
+
return makeRequest("POST", "/api/keys", { name });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Delete API key
|
|
304
|
+
* @param {string} id - Key ID
|
|
305
|
+
* @returns {Promise<Object>} { success, data: { success } }
|
|
306
|
+
*/
|
|
307
|
+
async function deleteApiKey(id) {
|
|
308
|
+
return makeRequest("DELETE", `/api/keys/${id}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ============================================================================
|
|
312
|
+
// COMBOS API
|
|
313
|
+
// ============================================================================
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Get all combos
|
|
317
|
+
* @returns {Promise<Object>} { success, data: { combos } }
|
|
318
|
+
*/
|
|
319
|
+
async function getCombos() {
|
|
320
|
+
return makeRequest("GET", "/api/combos");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get combo by ID
|
|
325
|
+
* @param {string} id - Combo ID
|
|
326
|
+
* @returns {Promise<Object>} { success, data: combo }
|
|
327
|
+
*/
|
|
328
|
+
async function getComboById(id) {
|
|
329
|
+
return makeRequest("GET", `/api/combos/${id}`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Create new combo
|
|
334
|
+
* @param {Object} data - Combo data { name, models }
|
|
335
|
+
* @returns {Promise<Object>} { success, data: combo }
|
|
336
|
+
*/
|
|
337
|
+
async function createCombo(data) {
|
|
338
|
+
return makeRequest("POST", "/api/combos", data);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Update combo
|
|
343
|
+
* @param {string} id - Combo ID
|
|
344
|
+
* @param {Object} data - Update data { name?, models? }
|
|
345
|
+
* @returns {Promise<Object>} { success, data: combo }
|
|
346
|
+
*/
|
|
347
|
+
async function updateCombo(id, data) {
|
|
348
|
+
return makeRequest("PUT", `/api/combos/${id}`, data);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Delete combo
|
|
353
|
+
* @param {string} id - Combo ID
|
|
354
|
+
* @returns {Promise<Object>} { success, data: { success } }
|
|
355
|
+
*/
|
|
356
|
+
async function deleteCombo(id) {
|
|
357
|
+
return makeRequest("DELETE", `/api/combos/${id}`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// CLI TOOLS API
|
|
362
|
+
// ============================================================================
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Get CLI tool settings
|
|
366
|
+
* @param {string} tool - Tool name: claude | codex | droid | openclaw
|
|
367
|
+
* @returns {Promise<Object>} { success, data: { installed, has9Router, ... } }
|
|
368
|
+
*/
|
|
369
|
+
async function getCliToolSettings(tool) {
|
|
370
|
+
return makeRequest("GET", `/api/cli-tools/${tool}-settings`);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Apply CLI tool settings (POST)
|
|
375
|
+
* @param {string} tool - Tool name: claude | codex | droid | openclaw
|
|
376
|
+
* @param {Object} body - Payload depends on tool
|
|
377
|
+
* @returns {Promise<Object>} { success, data }
|
|
378
|
+
*/
|
|
379
|
+
async function applyCliToolSettings(tool, body) {
|
|
380
|
+
return makeRequest("POST", `/api/cli-tools/${tool}-settings`, body);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Reset CLI tool settings (DELETE)
|
|
385
|
+
* @param {string} tool - Tool name: claude | codex | droid | openclaw
|
|
386
|
+
* @returns {Promise<Object>} { success, data }
|
|
387
|
+
*/
|
|
388
|
+
async function resetCliToolSettings(tool) {
|
|
389
|
+
return makeRequest("DELETE", `/api/cli-tools/${tool}-settings`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// ============================================================================
|
|
393
|
+
// SETTINGS API
|
|
394
|
+
// ============================================================================
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Get settings
|
|
398
|
+
* @returns {Promise<Object>} { success, data: settings }
|
|
399
|
+
*/
|
|
400
|
+
async function getSettings() {
|
|
401
|
+
return makeRequest("GET", "/api/settings");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Update settings
|
|
406
|
+
* @param {Object} data - Settings data
|
|
407
|
+
* @returns {Promise<Object>} { success, data: settings }
|
|
408
|
+
*/
|
|
409
|
+
async function updateSettings(data) {
|
|
410
|
+
return makeRequest("PATCH", "/api/settings", data);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ============================================================================
|
|
414
|
+
// MODELS API
|
|
415
|
+
// ============================================================================
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Get all models (internal API)
|
|
419
|
+
* @returns {Promise<Object>} { success, data: { models } }
|
|
420
|
+
*/
|
|
421
|
+
async function getModels() {
|
|
422
|
+
return makeRequest("GET", "/api/models");
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Get available models from active providers + combos (OpenAI compatible)
|
|
427
|
+
* @returns {Promise<Object>} { success, data: { object, data: [...models] } }
|
|
428
|
+
*/
|
|
429
|
+
async function getAvailableModels() {
|
|
430
|
+
return makeRequest("GET", "/v1/models");
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ============================================================================
|
|
434
|
+
// PROVIDER NODES API (custom providers)
|
|
435
|
+
// ============================================================================
|
|
436
|
+
|
|
437
|
+
async function getProviderNodes() {
|
|
438
|
+
return makeRequest("GET", "/api/provider-nodes");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async function createProviderNode(data) {
|
|
442
|
+
return makeRequest("POST", "/api/provider-nodes", data);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async function updateProviderNode(id, data) {
|
|
446
|
+
return makeRequest("PUT", `/api/provider-nodes/${id}`, data);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async function deleteProviderNode(id) {
|
|
450
|
+
return makeRequest("DELETE", `/api/provider-nodes/${id}`);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async function validateProviderNode(data) {
|
|
454
|
+
return makeRequest("POST", "/api/provider-nodes/validate", data);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// ============================================================================
|
|
458
|
+
// TUNNEL API
|
|
459
|
+
// ============================================================================
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Get tunnel status
|
|
463
|
+
* @returns {Promise<Object>} { success, data: { enabled, tunnelUrl, shortId, running } }
|
|
464
|
+
*/
|
|
465
|
+
async function getTunnelStatus() {
|
|
466
|
+
return makeRequest("GET", "/api/tunnel/status");
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Enable tunnel
|
|
471
|
+
* @returns {Promise<Object>} { success, data: { tunnelUrl, shortId } }
|
|
472
|
+
*/
|
|
473
|
+
async function enableTunnel() {
|
|
474
|
+
return makeRequest("POST", "/api/tunnel/enable");
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Disable tunnel
|
|
479
|
+
* @returns {Promise<Object>} { success, data: { success } }
|
|
480
|
+
*/
|
|
481
|
+
async function disableTunnel() {
|
|
482
|
+
return makeRequest("POST", "/api/tunnel/disable");
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// ============================================================================
|
|
486
|
+
// EXPORTS
|
|
487
|
+
// ============================================================================
|
|
488
|
+
|
|
489
|
+
module.exports = {
|
|
490
|
+
configure,
|
|
491
|
+
|
|
492
|
+
// Providers
|
|
493
|
+
getProviders,
|
|
494
|
+
getProviderById,
|
|
495
|
+
testProvider,
|
|
496
|
+
deleteProvider,
|
|
497
|
+
getProviderModels,
|
|
498
|
+
|
|
499
|
+
// Connection aliases
|
|
500
|
+
testConnection: testProvider,
|
|
501
|
+
deleteConnection: deleteProvider,
|
|
502
|
+
updateConnection,
|
|
503
|
+
|
|
504
|
+
// OAuth
|
|
505
|
+
getOAuthAuthUrl,
|
|
506
|
+
exchangeOAuthCode,
|
|
507
|
+
getOAuthDeviceCode,
|
|
508
|
+
pollOAuthToken,
|
|
509
|
+
createApiKeyProvider,
|
|
510
|
+
|
|
511
|
+
// API Keys
|
|
512
|
+
getApiKeys,
|
|
513
|
+
createApiKey,
|
|
514
|
+
deleteApiKey,
|
|
515
|
+
|
|
516
|
+
// Combos
|
|
517
|
+
getCombos,
|
|
518
|
+
getComboById,
|
|
519
|
+
createCombo,
|
|
520
|
+
updateCombo,
|
|
521
|
+
deleteCombo,
|
|
522
|
+
|
|
523
|
+
// CLI Tools
|
|
524
|
+
getCliToolSettings,
|
|
525
|
+
applyCliToolSettings,
|
|
526
|
+
resetCliToolSettings,
|
|
527
|
+
|
|
528
|
+
// Settings
|
|
529
|
+
getSettings,
|
|
530
|
+
updateSettings,
|
|
531
|
+
|
|
532
|
+
// Tunnel
|
|
533
|
+
getTunnelStatus,
|
|
534
|
+
enableTunnel,
|
|
535
|
+
disableTunnel,
|
|
536
|
+
|
|
537
|
+
// Models
|
|
538
|
+
getModels,
|
|
539
|
+
getAvailableModels,
|
|
540
|
+
|
|
541
|
+
// Provider Nodes (custom providers)
|
|
542
|
+
getProviderNodes,
|
|
543
|
+
createProviderNode,
|
|
544
|
+
updateProviderNode,
|
|
545
|
+
deleteProviderNode,
|
|
546
|
+
validateProviderNode,
|
|
547
|
+
};
|