openai-oauth 2.0.0-beta.2 → 2.0.0-beta.4
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/README.md +25 -2
- package/dist/{chunk-UZ3AHAWX.js → chunk-INHW7GRB.js} +602 -635
- package/dist/cli.js +1066 -47
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/package.json +10 -7
package/dist/cli.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_PORT,
|
|
4
|
-
resolveOpenAIOAuthModels,
|
|
5
4
|
runOpenAIOAuthLogin,
|
|
6
5
|
startOpenAIOAuthServer
|
|
7
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-INHW7GRB.js";
|
|
8
7
|
|
|
9
8
|
// src/cli-app.ts
|
|
10
9
|
import { access } from "fs/promises";
|
|
11
10
|
import { createInterface } from "readline/promises";
|
|
12
11
|
import {
|
|
13
|
-
createCodexOAuthClient,
|
|
14
12
|
resolveAuthFileCandidates,
|
|
15
13
|
resolveCodexAuthFilePath
|
|
16
|
-
} from "@openai-oauth/
|
|
17
|
-
import { openaiCredentials } from "@openai-oauth/local";
|
|
14
|
+
} from "@openai-oauth/local/auth-file";
|
|
18
15
|
import yargs from "yargs";
|
|
19
16
|
import { hideBin } from "yargs/helpers";
|
|
20
17
|
|
|
@@ -61,6 +58,53 @@ var toStartupMessage = (baseUrl, availableModels, options) => [
|
|
|
61
58
|
"",
|
|
62
59
|
`Available Models: ${availableModels.join(", ")}`
|
|
63
60
|
].join("\n");
|
|
61
|
+
var notRunning = "OpenAI OAuth is not running.";
|
|
62
|
+
var cliMessages = {
|
|
63
|
+
foregroundControls: "[d] Run in background [q] Quit",
|
|
64
|
+
backgroundActions: "Stop with `npx openai-oauth stop` or see logs with `npx openai-oauth logs --follow`",
|
|
65
|
+
notRunning,
|
|
66
|
+
notRunningWithStart: [notRunning, "", "Start with `npx openai-oauth`"].join(
|
|
67
|
+
"\n"
|
|
68
|
+
),
|
|
69
|
+
noLogs: "No OpenAI OAuth logs found.",
|
|
70
|
+
followingLogs: "Following OpenAI OAuth logs. Press Ctrl+C to stop following.",
|
|
71
|
+
stopped: "OpenAI OAuth stopped.",
|
|
72
|
+
stoppedRemotely: "OpenAI OAuth was stopped from another terminal.",
|
|
73
|
+
couldNotStop: "Could not stop OpenAI OAuth.",
|
|
74
|
+
couldNotDetach: (details) => {
|
|
75
|
+
if (details?.startsWith("Could not move OpenAI OAuth to the background")) {
|
|
76
|
+
return details;
|
|
77
|
+
}
|
|
78
|
+
return details ? `Could not move OpenAI OAuth to the background: ${details}` : "Could not move OpenAI OAuth to the background.";
|
|
79
|
+
},
|
|
80
|
+
stoppedUnexpectedly: (details) => `OpenAI OAuth stopped unexpectedly (${details}).`,
|
|
81
|
+
workerStarted: (baseUrl) => `OpenAI OAuth started at ${baseUrl}`
|
|
82
|
+
};
|
|
83
|
+
var toForegroundStartupMessage = (baseUrl, availableModels, options) => [
|
|
84
|
+
toStartupMessage(baseUrl, availableModels, options),
|
|
85
|
+
"",
|
|
86
|
+
dim(cliMessages.foregroundControls, options)
|
|
87
|
+
].join("\n");
|
|
88
|
+
var toBackgroundStartupMessage = (baseUrl, availableModels, options) => [
|
|
89
|
+
toStartupMessage(baseUrl, availableModels, options),
|
|
90
|
+
"",
|
|
91
|
+
cliMessages.backgroundActions
|
|
92
|
+
].join("\n");
|
|
93
|
+
var toDetachedMessage = (baseUrl) => [
|
|
94
|
+
`OpenAI OAuth is now running in the background at ${baseUrl}`,
|
|
95
|
+
"",
|
|
96
|
+
cliMessages.backgroundActions
|
|
97
|
+
].join("\n");
|
|
98
|
+
var toAlreadyRunningMessage = (baseUrl) => [
|
|
99
|
+
`OpenAI OAuth is already running at ${baseUrl}`,
|
|
100
|
+
"",
|
|
101
|
+
cliMessages.backgroundActions
|
|
102
|
+
].join("\n");
|
|
103
|
+
var toRunningMessage = (baseUrl) => [
|
|
104
|
+
`OpenAI OAuth is running at ${baseUrl}`,
|
|
105
|
+
"",
|
|
106
|
+
cliMessages.backgroundActions
|
|
107
|
+
].join("\n");
|
|
64
108
|
var installCliWarningLogger = () => {
|
|
65
109
|
let hasLoggedWarningSystemMessage = false;
|
|
66
110
|
globalThis.AI_SDK_LOG_WARNINGS = ({ warnings, provider, model }) => {
|
|
@@ -80,18 +124,650 @@ var installCliWarningLogger = () => {
|
|
|
80
124
|
};
|
|
81
125
|
};
|
|
82
126
|
|
|
127
|
+
// src/cli-runtime.ts
|
|
128
|
+
import { spawn } from "child_process";
|
|
129
|
+
import { randomBytes } from "crypto";
|
|
130
|
+
import { promises as fs } from "fs";
|
|
131
|
+
import { createServer } from "http";
|
|
132
|
+
import os from "os";
|
|
133
|
+
import path from "path";
|
|
134
|
+
import { setTimeout as delay } from "timers/promises";
|
|
135
|
+
var runtimeSchemaVersion = 1;
|
|
136
|
+
var controlHost = "127.0.0.1";
|
|
137
|
+
var internalRuntimeDirectoryVariable = "OPENAI_OAUTH_INTERNAL_RUNTIME_DIR";
|
|
138
|
+
var internalDetachedFlag = "--internal-detached-child";
|
|
139
|
+
var waitForChildExit = (child, timeoutMs) => {
|
|
140
|
+
if (child.exitCode != null || child.signalCode != null) {
|
|
141
|
+
return Promise.resolve(true);
|
|
142
|
+
}
|
|
143
|
+
return new Promise((resolve) => {
|
|
144
|
+
const onExit = () => {
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
resolve(true);
|
|
147
|
+
};
|
|
148
|
+
const timeout = setTimeout(() => {
|
|
149
|
+
child.off("exit", onExit);
|
|
150
|
+
resolve(false);
|
|
151
|
+
}, timeoutMs);
|
|
152
|
+
child.once("exit", onExit);
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
var isNodeError = (error) => error instanceof Error;
|
|
156
|
+
var resolveRuntimeDirectory = () => {
|
|
157
|
+
const override = process.env[internalRuntimeDirectoryVariable];
|
|
158
|
+
if (override) {
|
|
159
|
+
return override;
|
|
160
|
+
}
|
|
161
|
+
if (process.platform === "darwin") {
|
|
162
|
+
return path.join(
|
|
163
|
+
os.homedir(),
|
|
164
|
+
"Library",
|
|
165
|
+
"Application Support",
|
|
166
|
+
"openai-oauth"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
if (process.platform === "win32") {
|
|
170
|
+
return path.join(
|
|
171
|
+
process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local"),
|
|
172
|
+
"openai-oauth"
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return path.join(
|
|
176
|
+
process.env.XDG_STATE_HOME ?? path.join(os.homedir(), ".local", "state"),
|
|
177
|
+
"openai-oauth"
|
|
178
|
+
);
|
|
179
|
+
};
|
|
180
|
+
var resolveCliRuntimePaths = (directory = resolveRuntimeDirectory()) => ({
|
|
181
|
+
directory,
|
|
182
|
+
state: path.join(directory, "runtime.json"),
|
|
183
|
+
lock: path.join(directory, "runtime.lock"),
|
|
184
|
+
log: path.join(directory, "server.log")
|
|
185
|
+
});
|
|
186
|
+
var ensureRuntimeDirectory = async (paths) => {
|
|
187
|
+
await fs.mkdir(paths.directory, { recursive: true, mode: 448 });
|
|
188
|
+
try {
|
|
189
|
+
await fs.chmod(paths.directory, 448);
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var isRuntimeState = (value) => {
|
|
194
|
+
if (typeof value !== "object" || value == null) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
const state = value;
|
|
198
|
+
return state.schemaVersion === runtimeSchemaVersion && typeof state.instanceId === "string" && typeof state.pid === "number" && (state.mode === "foreground" || state.mode === "background") && typeof state.url === "string" && typeof state.startedAt === "string" && typeof state.version === "string" && typeof state.controlPort === "number" && typeof state.controlToken === "string";
|
|
199
|
+
};
|
|
200
|
+
var readJson = async (filePath) => {
|
|
201
|
+
try {
|
|
202
|
+
return JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
203
|
+
} catch {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
var readRuntimeState = async (paths) => {
|
|
208
|
+
const parsed = await readJson(paths.state);
|
|
209
|
+
return isRuntimeState(parsed) ? parsed : void 0;
|
|
210
|
+
};
|
|
211
|
+
var writeRuntimeState = async (paths, state) => {
|
|
212
|
+
const temporaryPath = `${paths.state}.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
213
|
+
await fs.writeFile(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
214
|
+
`, {
|
|
215
|
+
mode: 384
|
|
216
|
+
});
|
|
217
|
+
await fs.rename(temporaryPath, paths.state);
|
|
218
|
+
try {
|
|
219
|
+
await fs.chmod(paths.state, 384);
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
var isProcessAlive = (pid) => {
|
|
224
|
+
try {
|
|
225
|
+
process.kill(pid, 0);
|
|
226
|
+
return true;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
return isNodeError(error) && error.code === "EPERM";
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var readLockPid = async (paths) => {
|
|
232
|
+
const parsed = await readJson(paths.lock);
|
|
233
|
+
if (typeof parsed !== "object" || parsed == null) {
|
|
234
|
+
return void 0;
|
|
235
|
+
}
|
|
236
|
+
const pid = parsed.pid;
|
|
237
|
+
return typeof pid === "number" ? pid : void 0;
|
|
238
|
+
};
|
|
239
|
+
var removeIfPresent = async (filePath) => {
|
|
240
|
+
await fs.rm(filePath, { force: true }).catch(() => void 0);
|
|
241
|
+
};
|
|
242
|
+
var fetchWithTimeout = async (input, init, timeoutMs = 750) => {
|
|
243
|
+
const controller = new AbortController();
|
|
244
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
245
|
+
if (typeof timeout === "object" && "unref" in timeout) {
|
|
246
|
+
timeout.unref();
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
return await fetch(input, { ...init, signal: controller.signal });
|
|
250
|
+
} finally {
|
|
251
|
+
clearTimeout(timeout);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var probeRuntime = async (state) => {
|
|
255
|
+
try {
|
|
256
|
+
const response = await fetchWithTimeout(
|
|
257
|
+
`http://${controlHost}:${state.controlPort}/state`,
|
|
258
|
+
{
|
|
259
|
+
headers: { authorization: `Bearer ${state.controlToken}` }
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
if (!response.ok) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
const body = await response.json();
|
|
266
|
+
return body.instanceId === state.instanceId;
|
|
267
|
+
} catch {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
var findRunningCliRuntime = async (directory) => {
|
|
272
|
+
const paths = resolveCliRuntimePaths(directory);
|
|
273
|
+
const state = await readRuntimeState(paths);
|
|
274
|
+
if (!state) {
|
|
275
|
+
return void 0;
|
|
276
|
+
}
|
|
277
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
278
|
+
if (await probeRuntime(state)) {
|
|
279
|
+
return state;
|
|
280
|
+
}
|
|
281
|
+
await delay(50);
|
|
282
|
+
}
|
|
283
|
+
await removeIfPresent(paths.state);
|
|
284
|
+
await removeIfPresent(paths.lock);
|
|
285
|
+
return void 0;
|
|
286
|
+
};
|
|
287
|
+
var CliRuntimeAlreadyRunningError = class extends Error {
|
|
288
|
+
state;
|
|
289
|
+
constructor(state) {
|
|
290
|
+
super(toAlreadyRunningMessage(state.url));
|
|
291
|
+
this.name = "CliRuntimeAlreadyRunningError";
|
|
292
|
+
this.state = state;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
var acquireCliRuntimeLock = async (directory) => {
|
|
296
|
+
const paths = resolveCliRuntimePaths(directory);
|
|
297
|
+
await ensureRuntimeDirectory(paths);
|
|
298
|
+
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
299
|
+
try {
|
|
300
|
+
const handle = await fs.open(paths.lock, "wx", 384);
|
|
301
|
+
await handle.writeFile(
|
|
302
|
+
`${JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
303
|
+
`
|
|
304
|
+
);
|
|
305
|
+
await handle.close();
|
|
306
|
+
let released = false;
|
|
307
|
+
return {
|
|
308
|
+
paths,
|
|
309
|
+
release: async () => {
|
|
310
|
+
if (released) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
released = true;
|
|
314
|
+
await removeIfPresent(paths.lock);
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
} catch (error) {
|
|
318
|
+
if (!isNodeError(error) || error.code !== "EEXIST") {
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
const running = await findRunningCliRuntime(directory);
|
|
322
|
+
if (running) {
|
|
323
|
+
throw new CliRuntimeAlreadyRunningError(running);
|
|
324
|
+
}
|
|
325
|
+
const ownerPid = await readLockPid(paths);
|
|
326
|
+
if (ownerPid == null || !isProcessAlive(ownerPid)) {
|
|
327
|
+
await removeIfPresent(paths.lock);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
await delay(100);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
throw new Error("OpenAI OAuth is already starting in another process.");
|
|
334
|
+
};
|
|
335
|
+
var closeServer = (server) => new Promise((resolve, reject) => {
|
|
336
|
+
if (!server.listening) {
|
|
337
|
+
resolve();
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
server.close((error) => {
|
|
341
|
+
if (error) {
|
|
342
|
+
reject(error);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
resolve();
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
var activateCliRuntime = async (lock, options) => {
|
|
349
|
+
const instanceId = randomBytes(16).toString("hex");
|
|
350
|
+
const controlToken = randomBytes(32).toString("hex");
|
|
351
|
+
let state;
|
|
352
|
+
const controlServer = createServer((request, response) => {
|
|
353
|
+
if (request.headers.authorization !== `Bearer ${controlToken}`) {
|
|
354
|
+
response.writeHead(401).end();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (request.method === "GET" && request.url === "/state") {
|
|
358
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
359
|
+
response.end(JSON.stringify({ instanceId: state.instanceId }));
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (request.method === "POST" && request.url === "/stop") {
|
|
363
|
+
response.writeHead(202).end();
|
|
364
|
+
setImmediate(options.onStop);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
response.writeHead(404).end();
|
|
368
|
+
});
|
|
369
|
+
try {
|
|
370
|
+
await new Promise((resolve, reject) => {
|
|
371
|
+
controlServer.once("error", reject);
|
|
372
|
+
controlServer.listen(0, controlHost, () => {
|
|
373
|
+
controlServer.off("error", reject);
|
|
374
|
+
resolve();
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
const address = controlServer.address();
|
|
378
|
+
state = {
|
|
379
|
+
schemaVersion: runtimeSchemaVersion,
|
|
380
|
+
instanceId,
|
|
381
|
+
pid: process.pid,
|
|
382
|
+
mode: options.mode,
|
|
383
|
+
url: options.url,
|
|
384
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
385
|
+
version: options.version,
|
|
386
|
+
controlPort: address.port,
|
|
387
|
+
controlToken
|
|
388
|
+
};
|
|
389
|
+
await writeRuntimeState(lock.paths, state);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
await closeServer(controlServer).catch(() => void 0);
|
|
392
|
+
await lock.release();
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
let closed = false;
|
|
396
|
+
return {
|
|
397
|
+
state,
|
|
398
|
+
logPath: lock.paths.log,
|
|
399
|
+
setMode: async (mode) => {
|
|
400
|
+
if (state.mode === mode) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
state = { ...state, mode };
|
|
404
|
+
await writeRuntimeState(lock.paths, state);
|
|
405
|
+
},
|
|
406
|
+
close: async () => {
|
|
407
|
+
if (closed) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
closed = true;
|
|
411
|
+
const current = await readRuntimeState(lock.paths);
|
|
412
|
+
if (current?.instanceId === state.instanceId) {
|
|
413
|
+
await removeIfPresent(lock.paths.state);
|
|
414
|
+
}
|
|
415
|
+
await closeServer(controlServer).catch(() => void 0);
|
|
416
|
+
await lock.release();
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
var stopCliRuntime = async (state, directory) => {
|
|
421
|
+
try {
|
|
422
|
+
const response = await fetchWithTimeout(
|
|
423
|
+
`http://${controlHost}:${state.controlPort}/stop`,
|
|
424
|
+
{
|
|
425
|
+
method: "POST",
|
|
426
|
+
headers: { authorization: `Bearer ${state.controlToken}` }
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
if (!response.ok) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
} catch {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
436
|
+
if (!await findRunningCliRuntime(directory)) {
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
await delay(100);
|
|
440
|
+
}
|
|
441
|
+
return false;
|
|
442
|
+
};
|
|
443
|
+
var isCliWorkerMessage = (value) => {
|
|
444
|
+
if (typeof value !== "object" || value == null) {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
const message = value;
|
|
448
|
+
return message.type === "openai-oauth:ready" && typeof message.url === "string" && Array.isArray(message.models) || message.type === "openai-oauth:error" && typeof message.message === "string" || message.type === "openai-oauth:detached" || message.type === "openai-oauth:stopping" && message.source === "remote";
|
|
449
|
+
};
|
|
450
|
+
var waitForWorkerMessage = (child, predicate, timeoutMs) => new Promise((resolve, reject) => {
|
|
451
|
+
let settled = false;
|
|
452
|
+
const timeout = setTimeout(() => {
|
|
453
|
+
finish(new Error(`OpenAI OAuth did not respond within ${timeoutMs}ms.`));
|
|
454
|
+
}, timeoutMs);
|
|
455
|
+
const cleanup = () => {
|
|
456
|
+
clearTimeout(timeout);
|
|
457
|
+
child.off("message", onMessage);
|
|
458
|
+
child.off("error", onError);
|
|
459
|
+
child.off("exit", onExit);
|
|
460
|
+
};
|
|
461
|
+
const finish = (error, message) => {
|
|
462
|
+
if (settled) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
settled = true;
|
|
466
|
+
cleanup();
|
|
467
|
+
if (error) {
|
|
468
|
+
reject(error);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (!message) {
|
|
472
|
+
reject(new Error("OpenAI OAuth worker returned an empty response."));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
resolve(message);
|
|
476
|
+
};
|
|
477
|
+
const onMessage = (value) => {
|
|
478
|
+
if (!isCliWorkerMessage(value)) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (value.type === "openai-oauth:error") {
|
|
482
|
+
finish(new Error(value.message));
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
if (predicate(value)) {
|
|
486
|
+
finish(void 0, value);
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
const onError = (error) => finish(error);
|
|
490
|
+
const onExit = (code, signal) => finish(
|
|
491
|
+
new Error(
|
|
492
|
+
`OpenAI OAuth exited unexpectedly (${signal ?? code ?? "unknown"}).`
|
|
493
|
+
)
|
|
494
|
+
);
|
|
495
|
+
child.on("message", onMessage);
|
|
496
|
+
child.once("error", onError);
|
|
497
|
+
child.once("exit", onExit);
|
|
498
|
+
});
|
|
499
|
+
var startCliWorker = async (argv, options = {}) => {
|
|
500
|
+
const paths = resolveCliRuntimePaths(options.runtimeDirectory);
|
|
501
|
+
await ensureRuntimeDirectory(paths);
|
|
502
|
+
const logHandle = await fs.open(paths.log, "a", 384);
|
|
503
|
+
let child;
|
|
504
|
+
const entryPath = options.entryPath ?? process.argv[1];
|
|
505
|
+
if (!entryPath) {
|
|
506
|
+
await logHandle.close();
|
|
507
|
+
throw new Error("Could not resolve the OpenAI OAuth CLI entrypoint.");
|
|
508
|
+
}
|
|
509
|
+
try {
|
|
510
|
+
child = spawn(
|
|
511
|
+
process.execPath,
|
|
512
|
+
[
|
|
513
|
+
entryPath,
|
|
514
|
+
...argv.filter((argument) => argument !== internalDetachedFlag),
|
|
515
|
+
internalDetachedFlag
|
|
516
|
+
],
|
|
517
|
+
{
|
|
518
|
+
detached: true,
|
|
519
|
+
windowsHide: true,
|
|
520
|
+
stdio: ["ignore", logHandle.fd, logHandle.fd, "ipc"],
|
|
521
|
+
env: {
|
|
522
|
+
...process.env,
|
|
523
|
+
...options.runtimeDirectory ? {
|
|
524
|
+
[internalRuntimeDirectoryVariable]: options.runtimeDirectory
|
|
525
|
+
} : {}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
);
|
|
529
|
+
} finally {
|
|
530
|
+
await logHandle.close();
|
|
531
|
+
}
|
|
532
|
+
let ready;
|
|
533
|
+
try {
|
|
534
|
+
ready = await waitForWorkerMessage(
|
|
535
|
+
child,
|
|
536
|
+
(message) => message.type === "openai-oauth:ready",
|
|
537
|
+
options.timeoutMs ?? 15e3
|
|
538
|
+
);
|
|
539
|
+
} catch (error) {
|
|
540
|
+
if (child.pid) {
|
|
541
|
+
try {
|
|
542
|
+
process.kill(child.pid, "SIGTERM");
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (child.connected) {
|
|
547
|
+
child.disconnect();
|
|
548
|
+
}
|
|
549
|
+
child.unref();
|
|
550
|
+
throw error;
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
child,
|
|
554
|
+
url: ready.url,
|
|
555
|
+
models: ready.models,
|
|
556
|
+
logPath: paths.log,
|
|
557
|
+
detach: async () => {
|
|
558
|
+
const acknowledged = waitForWorkerMessage(
|
|
559
|
+
child,
|
|
560
|
+
(message) => message.type === "openai-oauth:detached",
|
|
561
|
+
5e3
|
|
562
|
+
);
|
|
563
|
+
child.send?.({ type: "openai-oauth:detach" });
|
|
564
|
+
await acknowledged;
|
|
565
|
+
if (child.connected) {
|
|
566
|
+
child.disconnect();
|
|
567
|
+
}
|
|
568
|
+
child.unref();
|
|
569
|
+
},
|
|
570
|
+
stop: async () => {
|
|
571
|
+
child.send?.({ type: "openai-oauth:stop" });
|
|
572
|
+
if (await waitForChildExit(child, 5e3)) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
child.kill("SIGTERM");
|
|
576
|
+
if (await waitForChildExit(child, 1e3)) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (child.connected) {
|
|
580
|
+
child.disconnect();
|
|
581
|
+
}
|
|
582
|
+
child.unref();
|
|
583
|
+
throw new Error("OpenAI OAuth did not stop within 6000ms.");
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
};
|
|
587
|
+
var readLogSlice = async (filePath, start) => {
|
|
588
|
+
try {
|
|
589
|
+
const stat = await fs.stat(filePath);
|
|
590
|
+
const safeStart = stat.size < start ? 0 : start;
|
|
591
|
+
if (stat.size === safeStart) {
|
|
592
|
+
return { text: "", size: stat.size };
|
|
593
|
+
}
|
|
594
|
+
const handle = await fs.open(filePath, "r");
|
|
595
|
+
try {
|
|
596
|
+
const buffer = Buffer.alloc(stat.size - safeStart);
|
|
597
|
+
await handle.read(buffer, 0, buffer.length, safeStart);
|
|
598
|
+
return { text: buffer.toString("utf8"), size: stat.size };
|
|
599
|
+
} finally {
|
|
600
|
+
await handle.close();
|
|
601
|
+
}
|
|
602
|
+
} catch (error) {
|
|
603
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
604
|
+
return { text: "", size: 0 };
|
|
605
|
+
}
|
|
606
|
+
throw error;
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
var readRecentCliLogs = async (directory, lineCount = 50) => {
|
|
610
|
+
const { log } = resolveCliRuntimePaths(directory);
|
|
611
|
+
try {
|
|
612
|
+
const stat = await fs.stat(log);
|
|
613
|
+
const start = Math.max(0, stat.size - 64 * 1024);
|
|
614
|
+
const { text } = await readLogSlice(log, start);
|
|
615
|
+
return text.split("\n").slice(-lineCount - 1).join("\n").trimEnd();
|
|
616
|
+
} catch (error) {
|
|
617
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
618
|
+
return "";
|
|
619
|
+
}
|
|
620
|
+
throw error;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
var followCliLogs = async (write, signal, directory, fromStart = false) => {
|
|
624
|
+
const { log } = resolveCliRuntimePaths(directory);
|
|
625
|
+
let offset = 0;
|
|
626
|
+
if (!fromStart) {
|
|
627
|
+
try {
|
|
628
|
+
offset = (await fs.stat(log)).size;
|
|
629
|
+
} catch {
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
while (!signal.aborted) {
|
|
633
|
+
try {
|
|
634
|
+
await delay(250, void 0, { signal });
|
|
635
|
+
} catch {
|
|
636
|
+
break;
|
|
637
|
+
}
|
|
638
|
+
const next = await readLogSlice(log, offset);
|
|
639
|
+
offset = next.size;
|
|
640
|
+
if (next.text) {
|
|
641
|
+
write(next.text);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
var sendDetachedReady = (url, models) => {
|
|
646
|
+
process.send?.({ type: "openai-oauth:ready", url, models });
|
|
647
|
+
};
|
|
648
|
+
var sendDetachedAcknowledgement = () => {
|
|
649
|
+
process.send?.({ type: "openai-oauth:detached" });
|
|
650
|
+
};
|
|
651
|
+
var sendRemoteStopping = () => new Promise((resolve) => {
|
|
652
|
+
if (!process.send) {
|
|
653
|
+
resolve();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
process.send(
|
|
657
|
+
{ type: "openai-oauth:stopping", source: "remote" },
|
|
658
|
+
() => resolve()
|
|
659
|
+
);
|
|
660
|
+
});
|
|
661
|
+
var sendDetachedError = (message) => new Promise((resolve) => {
|
|
662
|
+
if (!process.send) {
|
|
663
|
+
resolve();
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
process.send({ type: "openai-oauth:error", message }, () => resolve());
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// src/cli-worker.ts
|
|
670
|
+
var isWorkerCommand = (value) => {
|
|
671
|
+
if (typeof value !== "object" || value == null) {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
const type = value.type;
|
|
675
|
+
return type === "openai-oauth:detach" || type === "openai-oauth:stop";
|
|
676
|
+
};
|
|
677
|
+
var startCliInstance = async (options, mode, version, onRemoteStop) => {
|
|
678
|
+
const lock = await acquireCliRuntimeLock();
|
|
679
|
+
let server;
|
|
680
|
+
try {
|
|
681
|
+
server = await startOpenAIOAuthServer(options);
|
|
682
|
+
const runtime = await activateCliRuntime(lock, {
|
|
683
|
+
mode,
|
|
684
|
+
url: server.url,
|
|
685
|
+
version,
|
|
686
|
+
onStop: onRemoteStop
|
|
687
|
+
});
|
|
688
|
+
return { server, runtime, models: server.models };
|
|
689
|
+
} catch (error) {
|
|
690
|
+
await server?.close().catch(() => void 0);
|
|
691
|
+
await lock.release();
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
var runCliWorker = async (options, initialMode, version) => {
|
|
696
|
+
if (typeof process.send !== "function") {
|
|
697
|
+
throw new Error("OpenAI OAuth worker requires an IPC channel.");
|
|
698
|
+
}
|
|
699
|
+
let detached = false;
|
|
700
|
+
let stopping = false;
|
|
701
|
+
let instance;
|
|
702
|
+
const shutdown = async (source) => {
|
|
703
|
+
if (stopping) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
stopping = true;
|
|
707
|
+
if (source === "remote") {
|
|
708
|
+
await sendRemoteStopping();
|
|
709
|
+
}
|
|
710
|
+
await instance?.server.close().catch(() => void 0);
|
|
711
|
+
await instance?.runtime.close().catch(() => void 0);
|
|
712
|
+
if (process.connected) {
|
|
713
|
+
process.disconnect();
|
|
714
|
+
}
|
|
715
|
+
process.exit(0);
|
|
716
|
+
};
|
|
717
|
+
instance = await startCliInstance(
|
|
718
|
+
options,
|
|
719
|
+
initialMode,
|
|
720
|
+
version,
|
|
721
|
+
() => shutdown("remote")
|
|
722
|
+
);
|
|
723
|
+
const onMessage = (value) => {
|
|
724
|
+
if (!isWorkerCommand(value) || stopping) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
if (value.type === "openai-oauth:stop") {
|
|
728
|
+
void shutdown("parent");
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
void (async () => {
|
|
732
|
+
try {
|
|
733
|
+
await instance?.runtime.setMode("background");
|
|
734
|
+
detached = true;
|
|
735
|
+
sendDetachedAcknowledgement();
|
|
736
|
+
} catch (error) {
|
|
737
|
+
await sendDetachedError(
|
|
738
|
+
error instanceof Error ? error.message : cliMessages.couldNotDetach()
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
})();
|
|
742
|
+
};
|
|
743
|
+
const onDisconnect = () => {
|
|
744
|
+
if (!detached) {
|
|
745
|
+
void shutdown("disconnect");
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
const onSignal = () => shutdown("signal");
|
|
749
|
+
process.on("message", onMessage);
|
|
750
|
+
process.once("disconnect", onDisconnect);
|
|
751
|
+
process.once("SIGINT", onSignal);
|
|
752
|
+
process.once("SIGTERM", onSignal);
|
|
753
|
+
console.log(cliMessages.workerStarted(instance.server.url));
|
|
754
|
+
sendDetachedReady(instance.server.url, instance.models);
|
|
755
|
+
};
|
|
756
|
+
|
|
83
757
|
// src/update-check.ts
|
|
84
|
-
var
|
|
758
|
+
var registryUrl = "https://registry.npmjs.org/openai-oauth/latest";
|
|
85
759
|
var normalizeVersion = (value) => {
|
|
86
760
|
if (typeof value !== "string") {
|
|
87
761
|
return void 0;
|
|
88
762
|
}
|
|
89
|
-
const match = value.trim().match(/^\d+\.\d+\.\d
|
|
763
|
+
const match = value.trim().match(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/);
|
|
90
764
|
return match?.[0];
|
|
91
765
|
};
|
|
92
766
|
var compareSemver = (left, right) => {
|
|
93
|
-
const
|
|
94
|
-
const
|
|
767
|
+
const [leftCore = "", leftPrerelease] = left.split("-", 2);
|
|
768
|
+
const [rightCore = "", rightPrerelease] = right.split("-", 2);
|
|
769
|
+
const leftParts = leftCore.split(".").map(Number);
|
|
770
|
+
const rightParts = rightCore.split(".").map(Number);
|
|
95
771
|
for (let index = 0; index < 3; index += 1) {
|
|
96
772
|
const leftPart = leftParts[index] ?? 0;
|
|
97
773
|
const rightPart = rightParts[index] ?? 0;
|
|
@@ -102,6 +778,26 @@ var compareSemver = (left, right) => {
|
|
|
102
778
|
return 1;
|
|
103
779
|
}
|
|
104
780
|
}
|
|
781
|
+
if (leftPrerelease == null) return rightPrerelease == null ? 0 : 1;
|
|
782
|
+
if (rightPrerelease == null) return -1;
|
|
783
|
+
const leftIdentifiers = leftPrerelease.split(".");
|
|
784
|
+
const rightIdentifiers = rightPrerelease.split(".");
|
|
785
|
+
const length = Math.max(leftIdentifiers.length, rightIdentifiers.length);
|
|
786
|
+
for (let index = 0; index < length; index += 1) {
|
|
787
|
+
const leftIdentifier = leftIdentifiers[index];
|
|
788
|
+
const rightIdentifier = rightIdentifiers[index];
|
|
789
|
+
if (leftIdentifier == null) return -1;
|
|
790
|
+
if (rightIdentifier == null) return 1;
|
|
791
|
+
if (leftIdentifier === rightIdentifier) continue;
|
|
792
|
+
const leftNumber = /^\d+$/.test(leftIdentifier) ? Number(leftIdentifier) : void 0;
|
|
793
|
+
const rightNumber = /^\d+$/.test(rightIdentifier) ? Number(rightIdentifier) : void 0;
|
|
794
|
+
if (leftNumber != null && rightNumber != null) {
|
|
795
|
+
return leftNumber < rightNumber ? -1 : 1;
|
|
796
|
+
}
|
|
797
|
+
if (leftNumber != null) return -1;
|
|
798
|
+
if (rightNumber != null) return 1;
|
|
799
|
+
return leftIdentifier < rightIdentifier ? -1 : 1;
|
|
800
|
+
}
|
|
105
801
|
return 0;
|
|
106
802
|
};
|
|
107
803
|
var fetchLatestVersion = async (fetchImpl, signal) => {
|
|
@@ -114,7 +810,7 @@ var fetchLatestVersion = async (fetchImpl, signal) => {
|
|
|
114
810
|
if (signal) {
|
|
115
811
|
requestInit.signal = signal;
|
|
116
812
|
}
|
|
117
|
-
const response = await fetchImpl(
|
|
813
|
+
const response = await fetchImpl(registryUrl, requestInit);
|
|
118
814
|
if (!response.ok) {
|
|
119
815
|
return void 0;
|
|
120
816
|
}
|
|
@@ -153,10 +849,82 @@ Run \`npx openai-oauth@latest\` to use the newest version.`
|
|
|
153
849
|
);
|
|
154
850
|
};
|
|
155
851
|
|
|
852
|
+
// package.json
|
|
853
|
+
var package_default = {
|
|
854
|
+
name: "openai-oauth",
|
|
855
|
+
version: "2.0.0-beta.4",
|
|
856
|
+
description: "Free OpenAI API access with your ChatGPT account.",
|
|
857
|
+
keywords: [
|
|
858
|
+
"openai",
|
|
859
|
+
"oauth",
|
|
860
|
+
"chatgpt",
|
|
861
|
+
"codex",
|
|
862
|
+
"cli",
|
|
863
|
+
"openai-compatible"
|
|
864
|
+
],
|
|
865
|
+
type: "module",
|
|
866
|
+
engines: {
|
|
867
|
+
node: ">=20"
|
|
868
|
+
},
|
|
869
|
+
sideEffects: false,
|
|
870
|
+
main: "./dist/index.js",
|
|
871
|
+
types: "./dist/index.d.ts",
|
|
872
|
+
bin: {
|
|
873
|
+
"openai-oauth": "dist/cli.js"
|
|
874
|
+
},
|
|
875
|
+
exports: {
|
|
876
|
+
".": {
|
|
877
|
+
types: "./dist/index.d.ts",
|
|
878
|
+
import: "./dist/index.js",
|
|
879
|
+
default: "./dist/index.js"
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
files: [
|
|
883
|
+
"dist",
|
|
884
|
+
"README.md",
|
|
885
|
+
"LICENSE"
|
|
886
|
+
],
|
|
887
|
+
scripts: {
|
|
888
|
+
build: "tsup src/index.ts src/cli.ts --format esm --dts --outDir dist --clean --loader .html=text",
|
|
889
|
+
dev: "bun ./src/cli.ts",
|
|
890
|
+
prepublishOnly: "bun run build",
|
|
891
|
+
typecheck: "tsc --noEmit",
|
|
892
|
+
test: "vitest run",
|
|
893
|
+
"test:live": "LIVE_CODEX_E2E=1 vitest run test/live.e2e.test.ts"
|
|
894
|
+
},
|
|
895
|
+
dependencies: {
|
|
896
|
+
"@openai-oauth/ai-sdk": "workspace:*",
|
|
897
|
+
"@openai-oauth/core": "workspace:*",
|
|
898
|
+
"@openai-oauth/local": "workspace:*",
|
|
899
|
+
ai: "6.0.116",
|
|
900
|
+
yargs: "17.7.3"
|
|
901
|
+
},
|
|
902
|
+
devDependencies: {
|
|
903
|
+
"@ai-sdk/openai": "3.0.41",
|
|
904
|
+
"@types/node": "20.17.24",
|
|
905
|
+
"@types/yargs": "^17.0.33",
|
|
906
|
+
typescript: "5.9.2",
|
|
907
|
+
vitest: "4.1.10",
|
|
908
|
+
zod: "^3.25.76"
|
|
909
|
+
},
|
|
910
|
+
author: "EvanZhouDev",
|
|
911
|
+
license: "AGPL-3.0-only",
|
|
912
|
+
repository: {
|
|
913
|
+
type: "git",
|
|
914
|
+
url: "git+https://github.com/EvanZhouDev/openai-oauth.git",
|
|
915
|
+
directory: "packages/openai-oauth"
|
|
916
|
+
},
|
|
917
|
+
bugs: {
|
|
918
|
+
url: "https://github.com/EvanZhouDev/openai-oauth/issues"
|
|
919
|
+
},
|
|
920
|
+
homepage: "https://github.com/EvanZhouDev/openai-oauth#readme"
|
|
921
|
+
};
|
|
922
|
+
|
|
156
923
|
// src/version.ts
|
|
157
|
-
var packageVersion =
|
|
924
|
+
var packageVersion = package_default.version;
|
|
158
925
|
|
|
159
926
|
// src/cli-app.ts
|
|
927
|
+
var cliCommand = "npx openai-oauth@latest";
|
|
160
928
|
var defaultUpdateCheckWarning = (message) => {
|
|
161
929
|
console.error(message);
|
|
162
930
|
};
|
|
@@ -171,8 +939,12 @@ var helpLines = [
|
|
|
171
939
|
"Free OpenAI API access with your ChatGPT account.",
|
|
172
940
|
"",
|
|
173
941
|
"Usage",
|
|
174
|
-
|
|
175
|
-
|
|
942
|
+
` ${cliCommand} [options]`,
|
|
943
|
+
` ${cliCommand} --detach [options]`,
|
|
944
|
+
` ${cliCommand} status`,
|
|
945
|
+
` ${cliCommand} logs [--follow]`,
|
|
946
|
+
` ${cliCommand} stop`,
|
|
947
|
+
` ${cliCommand} login [options]`,
|
|
176
948
|
"",
|
|
177
949
|
"Options",
|
|
178
950
|
" --host <host> Proxy host. Login callback always listens on loopback.",
|
|
@@ -187,6 +959,8 @@ var helpLines = [
|
|
|
187
959
|
" --login-timeout-ms <ms> Login timeout. Default: 300000",
|
|
188
960
|
"",
|
|
189
961
|
"Flags",
|
|
962
|
+
" -d, --detach Run in the background",
|
|
963
|
+
" -f, --follow Follow logs",
|
|
190
964
|
" --help Show help",
|
|
191
965
|
` --version Show version (${packageVersion})`,
|
|
192
966
|
"",
|
|
@@ -226,14 +1000,31 @@ var createCliParser = (argv) => yargs(argv).scriptName("openai-oauth").strict().
|
|
|
226
1000
|
}).option("login-timeout-ms", {
|
|
227
1001
|
type: "number",
|
|
228
1002
|
describe: "Login timeout in milliseconds. Default: 300000"
|
|
229
|
-
})
|
|
1003
|
+
}).option("detach", {
|
|
1004
|
+
alias: "d",
|
|
1005
|
+
type: "boolean",
|
|
1006
|
+
default: false,
|
|
1007
|
+
describe: "Run in the background."
|
|
1008
|
+
}).option("follow", {
|
|
1009
|
+
alias: "f",
|
|
1010
|
+
type: "boolean",
|
|
1011
|
+
default: false,
|
|
1012
|
+
describe: "Follow logs."
|
|
1013
|
+
}).option("internal-detached-child", {
|
|
1014
|
+
type: "boolean",
|
|
1015
|
+
default: false
|
|
1016
|
+
}).hide("internal-detached-child");
|
|
230
1017
|
var isHelpFlag = (argv) => argv.includes("--help") || argv.includes("-h");
|
|
231
1018
|
var isVersionFlag = (argv) => argv.includes("--version");
|
|
232
1019
|
var toHelpMessage = () => helpLines.join("\n");
|
|
233
1020
|
var parseCliArgs = (argv) => {
|
|
234
|
-
const
|
|
1021
|
+
const first = argv[0];
|
|
1022
|
+
if (first && !first.startsWith("-") && first !== "serve" && first !== "login" && first !== "logs" && first !== "status" && first !== "stop") {
|
|
1023
|
+
throw new Error(`Unknown command: ${first}`);
|
|
1024
|
+
}
|
|
1025
|
+
const command = first === "login" || first === "logs" || first === "status" || first === "stop" ? first : "serve";
|
|
235
1026
|
const parsed = createCliParser(
|
|
236
|
-
command === "
|
|
1027
|
+
command === "serve" && first !== "serve" ? argv : argv.slice(1)
|
|
237
1028
|
).parseSync();
|
|
238
1029
|
return {
|
|
239
1030
|
command,
|
|
@@ -246,7 +1037,10 @@ var parseCliArgs = (argv) => {
|
|
|
246
1037
|
tokenUrl: parsed.oauthTokenUrl,
|
|
247
1038
|
authFilePath: parsed.oauthFile,
|
|
248
1039
|
openBrowser: parsed.open,
|
|
249
|
-
loginTimeoutMs: parsed.loginTimeoutMs
|
|
1040
|
+
loginTimeoutMs: parsed.loginTimeoutMs,
|
|
1041
|
+
detach: parsed.detach,
|
|
1042
|
+
follow: parsed.follow,
|
|
1043
|
+
internalDetachedChild: parsed.internalDetachedChild
|
|
250
1044
|
};
|
|
251
1045
|
};
|
|
252
1046
|
var toServerOptions = (args) => ({
|
|
@@ -313,8 +1107,9 @@ var toMissingAuthFilePrompt = (authFilePath) => {
|
|
|
313
1107
|
`Sign in with ChatGPT now? This will write credentials to ${writePath}.`
|
|
314
1108
|
].join("\n");
|
|
315
1109
|
};
|
|
1110
|
+
var toExistingAuthFileMessage = (authFilePath) => `OpenAI OAuth credentials already exist at ${authFilePath}.`;
|
|
316
1111
|
var toOverwriteAuthFilePrompt = (authFilePath) => [
|
|
317
|
-
|
|
1112
|
+
toExistingAuthFileMessage(authFilePath),
|
|
318
1113
|
"Sign in with ChatGPT again and overwrite them?"
|
|
319
1114
|
].join("\n");
|
|
320
1115
|
var toMissingAuthFileMessage = (authFilePath) => {
|
|
@@ -333,6 +1128,13 @@ var toMissingAuthFileMessage = (authFilePath) => {
|
|
|
333
1128
|
var runUpdateCheck = () => checkForOpenAIOAuthUpdates(packageVersion, {
|
|
334
1129
|
onWarning: defaultUpdateCheckWarning
|
|
335
1130
|
});
|
|
1131
|
+
var warnAboutNetworkBinding = (host) => {
|
|
1132
|
+
if (host && host !== "127.0.0.1" && host !== "localhost" && host !== "::1") {
|
|
1133
|
+
console.warn(
|
|
1134
|
+
`Warning: --host ${host} exposes OpenAI OAuth to your network. Anyone who can reach this port can make requests with your ChatGPT account.`
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
336
1138
|
var createLoginCancellation = () => {
|
|
337
1139
|
const abortController = new AbortController();
|
|
338
1140
|
let signalName;
|
|
@@ -373,6 +1175,200 @@ var runLoginWithCancellation = async (options) => {
|
|
|
373
1175
|
cancellation.dispose();
|
|
374
1176
|
}
|
|
375
1177
|
};
|
|
1178
|
+
var runLogsCommand = async (follow) => {
|
|
1179
|
+
const recent = await readRecentCliLogs();
|
|
1180
|
+
const running = await findRunningCliRuntime();
|
|
1181
|
+
if (!running) {
|
|
1182
|
+
console.log(cliMessages.notRunningWithStart);
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
if (!follow) {
|
|
1186
|
+
console.log(recent || cliMessages.noLogs);
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
console.log(cliMessages.followingLogs);
|
|
1190
|
+
if (recent) {
|
|
1191
|
+
console.log(recent);
|
|
1192
|
+
}
|
|
1193
|
+
const controller = new AbortController();
|
|
1194
|
+
const abort = () => controller.abort();
|
|
1195
|
+
process.once("SIGINT", abort);
|
|
1196
|
+
process.once("SIGTERM", abort);
|
|
1197
|
+
try {
|
|
1198
|
+
await followCliLogs((text) => process.stdout.write(text), controller.signal);
|
|
1199
|
+
} finally {
|
|
1200
|
+
process.off("SIGINT", abort);
|
|
1201
|
+
process.off("SIGTERM", abort);
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
var runStopCommand = async () => {
|
|
1205
|
+
const running = await findRunningCliRuntime();
|
|
1206
|
+
if (!running) {
|
|
1207
|
+
console.log(cliMessages.notRunning);
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
if (!await stopCliRuntime(running)) {
|
|
1211
|
+
throw new Error(cliMessages.couldNotStop);
|
|
1212
|
+
}
|
|
1213
|
+
console.log(cliMessages.stopped);
|
|
1214
|
+
};
|
|
1215
|
+
var runStatusCommand = async () => {
|
|
1216
|
+
const running = await findRunningCliRuntime();
|
|
1217
|
+
if (!running) {
|
|
1218
|
+
console.log(cliMessages.notRunningWithStart);
|
|
1219
|
+
process.exitCode = 1;
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
console.log(toRunningMessage(running.url));
|
|
1223
|
+
};
|
|
1224
|
+
var runForegroundController = async (worker) => new Promise((resolve, reject) => {
|
|
1225
|
+
const interactive = canPrompt();
|
|
1226
|
+
const stdin = process.stdin;
|
|
1227
|
+
const logController = new AbortController();
|
|
1228
|
+
const previousRawMode = stdin.isRaw;
|
|
1229
|
+
let menuShown = false;
|
|
1230
|
+
let completing = false;
|
|
1231
|
+
let remoteStopping = false;
|
|
1232
|
+
let rawModeEnabled = false;
|
|
1233
|
+
const cleanup = () => {
|
|
1234
|
+
logController.abort();
|
|
1235
|
+
stdin.off("data", onData);
|
|
1236
|
+
process.off("SIGINT", onSigint);
|
|
1237
|
+
process.off("SIGTERM", onSigterm);
|
|
1238
|
+
worker.child.off("message", onWorkerMessage);
|
|
1239
|
+
worker.child.off("error", onWorkerError);
|
|
1240
|
+
worker.child.off("exit", onWorkerExit);
|
|
1241
|
+
if (rawModeEnabled && stdin.setRawMode) {
|
|
1242
|
+
stdin.setRawMode(previousRawMode ?? false);
|
|
1243
|
+
}
|
|
1244
|
+
if (interactive) {
|
|
1245
|
+
stdin.pause();
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
const finish = (error) => {
|
|
1249
|
+
cleanup();
|
|
1250
|
+
if (error) {
|
|
1251
|
+
reject(error);
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
resolve();
|
|
1255
|
+
};
|
|
1256
|
+
const quit = async (showMessage) => {
|
|
1257
|
+
if (completing) {
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
completing = true;
|
|
1261
|
+
try {
|
|
1262
|
+
await worker.stop();
|
|
1263
|
+
if (showMessage) {
|
|
1264
|
+
console.log(`
|
|
1265
|
+
${cliMessages.stopped}`);
|
|
1266
|
+
}
|
|
1267
|
+
finish();
|
|
1268
|
+
} catch (error) {
|
|
1269
|
+
finish(
|
|
1270
|
+
error instanceof Error ? error : new Error(cliMessages.couldNotStop)
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
const detach = async () => {
|
|
1275
|
+
if (completing) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
completing = true;
|
|
1279
|
+
try {
|
|
1280
|
+
await worker.detach();
|
|
1281
|
+
console.log(`
|
|
1282
|
+
${toDetachedMessage(worker.url)}`);
|
|
1283
|
+
finish();
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
completing = false;
|
|
1286
|
+
console.error(
|
|
1287
|
+
`
|
|
1288
|
+
${cliMessages.couldNotDetach(
|
|
1289
|
+
error instanceof Error ? error.message : void 0
|
|
1290
|
+
)}`
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
const handleInterrupt = () => {
|
|
1295
|
+
if (!interactive || menuShown) {
|
|
1296
|
+
void quit(interactive);
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
menuShown = true;
|
|
1300
|
+
console.log(`
|
|
1301
|
+
${cliMessages.foregroundControls}`);
|
|
1302
|
+
};
|
|
1303
|
+
function onData(value) {
|
|
1304
|
+
const input = value.toString().toLowerCase();
|
|
1305
|
+
if (input.includes("")) {
|
|
1306
|
+
handleInterrupt();
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (input.includes("d")) {
|
|
1310
|
+
void detach();
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
if (input.includes("q")) {
|
|
1314
|
+
void quit(true);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
function onSigint() {
|
|
1318
|
+
handleInterrupt();
|
|
1319
|
+
}
|
|
1320
|
+
function onSigterm() {
|
|
1321
|
+
void quit(false);
|
|
1322
|
+
}
|
|
1323
|
+
function onWorkerMessage(value) {
|
|
1324
|
+
const message = value;
|
|
1325
|
+
if (message.type === "openai-oauth:stopping" && message.source === "remote") {
|
|
1326
|
+
remoteStopping = true;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
function onWorkerError(error) {
|
|
1330
|
+
if (!completing) {
|
|
1331
|
+
finish(error);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
function onWorkerExit(code, signal) {
|
|
1335
|
+
if (completing) {
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
if (remoteStopping) {
|
|
1339
|
+
console.log(`
|
|
1340
|
+
${cliMessages.stoppedRemotely}`);
|
|
1341
|
+
finish();
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
finish(
|
|
1345
|
+
new Error(
|
|
1346
|
+
cliMessages.stoppedUnexpectedly(signal ?? `${code ?? "unknown"}`)
|
|
1347
|
+
)
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
if (interactive) {
|
|
1351
|
+
if (stdin.setRawMode) {
|
|
1352
|
+
stdin.setRawMode(true);
|
|
1353
|
+
rawModeEnabled = true;
|
|
1354
|
+
}
|
|
1355
|
+
stdin.resume();
|
|
1356
|
+
stdin.on("data", onData);
|
|
1357
|
+
}
|
|
1358
|
+
process.on("SIGINT", onSigint);
|
|
1359
|
+
process.on("SIGTERM", onSigterm);
|
|
1360
|
+
worker.child.on("message", onWorkerMessage);
|
|
1361
|
+
worker.child.once("error", onWorkerError);
|
|
1362
|
+
worker.child.once("exit", onWorkerExit);
|
|
1363
|
+
void followCliLogs(
|
|
1364
|
+
(text) => process.stdout.write(text),
|
|
1365
|
+
logController.signal
|
|
1366
|
+
).catch((error) => {
|
|
1367
|
+
if (!logController.signal.aborted) {
|
|
1368
|
+
console.error(error instanceof Error ? error.message : error);
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
});
|
|
376
1372
|
var runCli = async (argv = hideBin(process.argv)) => {
|
|
377
1373
|
if (isHelpFlag(argv)) {
|
|
378
1374
|
console.log(toHelpMessage());
|
|
@@ -384,9 +1380,36 @@ var runCli = async (argv = hideBin(process.argv)) => {
|
|
|
384
1380
|
}
|
|
385
1381
|
installCliWarningLogger();
|
|
386
1382
|
const args = parseCliArgs(argv);
|
|
387
|
-
|
|
1383
|
+
if (args.internalDetachedChild) {
|
|
1384
|
+
try {
|
|
1385
|
+
await runCliWorker(
|
|
1386
|
+
toServerOptions(args),
|
|
1387
|
+
args.detach ? "background" : "foreground",
|
|
1388
|
+
packageVersion
|
|
1389
|
+
);
|
|
1390
|
+
} catch (error) {
|
|
1391
|
+
await sendDetachedError(
|
|
1392
|
+
error instanceof Error ? error.message : "Failed to start OpenAI OAuth."
|
|
1393
|
+
);
|
|
1394
|
+
throw error;
|
|
1395
|
+
}
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
if (args.command === "logs") {
|
|
1399
|
+
await runLogsCommand(Boolean(args.follow));
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
if (args.command === "stop") {
|
|
1403
|
+
await runStopCommand();
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
if (args.command === "status") {
|
|
1407
|
+
await runStatusCommand();
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
388
1410
|
if (args.command === "login") {
|
|
389
|
-
|
|
1411
|
+
const updateCheck2 = runUpdateCheck();
|
|
1412
|
+
await updateCheck2;
|
|
390
1413
|
const loginOptions = toLoginOptions(args);
|
|
391
1414
|
const existingAuthFile2 = await findExistingCodexAuthFile(
|
|
392
1415
|
loginOptions.authFilePath
|
|
@@ -395,7 +1418,7 @@ var runCli = async (argv = hideBin(process.argv)) => {
|
|
|
395
1418
|
if (!canPrompt()) {
|
|
396
1419
|
throw new Error(
|
|
397
1420
|
[
|
|
398
|
-
|
|
1421
|
+
toExistingAuthFileMessage(existingAuthFile2),
|
|
399
1422
|
"Run `npx openai-oauth login` in an interactive terminal to confirm overwrite."
|
|
400
1423
|
].join("\n")
|
|
401
1424
|
);
|
|
@@ -412,7 +1435,14 @@ var runCli = async (argv = hideBin(process.argv)) => {
|
|
|
412
1435
|
await runLoginWithCancellation(loginOptions);
|
|
413
1436
|
return;
|
|
414
1437
|
}
|
|
1438
|
+
const running = await findRunningCliRuntime();
|
|
1439
|
+
if (running) {
|
|
1440
|
+
console.log(toAlreadyRunningMessage(running.url));
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
const updateCheck = runUpdateCheck();
|
|
415
1444
|
const options = toServerOptions(args);
|
|
1445
|
+
warnAboutNetworkBinding(options.host);
|
|
416
1446
|
const existingAuthFile = await findExistingAuthFile(options.authFilePath);
|
|
417
1447
|
if (!existingAuthFile) {
|
|
418
1448
|
await updateCheck;
|
|
@@ -432,34 +1462,23 @@ var runCli = async (argv = hideBin(process.argv)) => {
|
|
|
432
1462
|
return;
|
|
433
1463
|
}
|
|
434
1464
|
}
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const availableModels = await resolveOpenAIOAuthModels(client, options.models);
|
|
442
|
-
const server = await startOpenAIOAuthServer(options);
|
|
443
|
-
console.log(
|
|
444
|
-
toStartupMessage(
|
|
445
|
-
`http://${server.host}:${server.port}/v1`,
|
|
446
|
-
availableModels,
|
|
447
|
-
{
|
|
1465
|
+
const worker = await startCliWorker(argv);
|
|
1466
|
+
await updateCheck;
|
|
1467
|
+
if (args.detach) {
|
|
1468
|
+
await worker.detach();
|
|
1469
|
+
console.log(
|
|
1470
|
+
toBackgroundStartupMessage(worker.url, worker.models, {
|
|
448
1471
|
useColor: process.stdout.isTTY
|
|
449
|
-
}
|
|
450
|
-
)
|
|
1472
|
+
})
|
|
1473
|
+
);
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
console.log(
|
|
1477
|
+
toForegroundStartupMessage(worker.url, worker.models, {
|
|
1478
|
+
useColor: process.stdout.isTTY
|
|
1479
|
+
})
|
|
451
1480
|
);
|
|
452
|
-
|
|
453
|
-
const shutdown = async () => {
|
|
454
|
-
await server.close();
|
|
455
|
-
process.exit(0);
|
|
456
|
-
};
|
|
457
|
-
process.on("SIGINT", () => {
|
|
458
|
-
void shutdown();
|
|
459
|
-
});
|
|
460
|
-
process.on("SIGTERM", () => {
|
|
461
|
-
void shutdown();
|
|
462
|
-
});
|
|
1481
|
+
await runForegroundController(worker);
|
|
463
1482
|
};
|
|
464
1483
|
|
|
465
1484
|
// src/cli.ts
|