skalpel 2.0.17 → 2.0.18

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/dist/cli/index.js CHANGED
@@ -4,6 +4,54 @@ var __esm = (fn, res) => function __init() {
4
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
5
  };
6
6
 
7
+ // src/proxy/codex-oauth.ts
8
+ import { readFileSync } from "fs";
9
+ import { homedir } from "os";
10
+ import { join } from "path";
11
+ function authFilePath() {
12
+ return join(homedir(), ".codex", "auth.json");
13
+ }
14
+ function readCodexAuth() {
15
+ const path17 = authFilePath();
16
+ let raw;
17
+ try {
18
+ raw = readFileSync(path17, "utf-8");
19
+ } catch (err) {
20
+ const code = err?.code;
21
+ if (code !== "ENOENT") {
22
+ process.stderr.write(`skalpel: codex-oauth: cannot read auth file (${code ?? "unknown"})
23
+ `);
24
+ }
25
+ return null;
26
+ }
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(raw);
30
+ } catch {
31
+ process.stderr.write("skalpel: codex-oauth: auth file is not valid JSON\n");
32
+ return null;
33
+ }
34
+ if (parsed === null || typeof parsed !== "object" || typeof parsed.access_token !== "string" || typeof parsed.refresh_token !== "string" || typeof parsed.expires_at !== "string") {
35
+ process.stderr.write("skalpel: codex-oauth: auth file missing required fields\n");
36
+ return null;
37
+ }
38
+ const obj = parsed;
39
+ const auth = {
40
+ access_token: obj.access_token,
41
+ refresh_token: obj.refresh_token,
42
+ expires_at: obj.expires_at
43
+ };
44
+ if (typeof obj.account_id === "string") {
45
+ auth.account_id = obj.account_id;
46
+ }
47
+ return auth;
48
+ }
49
+ var init_codex_oauth = __esm({
50
+ "src/proxy/codex-oauth.ts"() {
51
+ "use strict";
52
+ }
53
+ });
54
+
7
55
  // src/proxy/dispatcher.ts
8
56
  import { Agent } from "undici";
9
57
  var skalpelDispatcher;
@@ -89,6 +137,7 @@ import WebSocket2 from "ws";
89
137
  var init_ws_client = __esm({
90
138
  "src/proxy/ws-client.ts"() {
91
139
  "use strict";
140
+ init_codex_oauth();
92
141
  }
93
142
  });
94
143
 
@@ -100,6 +149,7 @@ var init_handler = __esm({
100
149
  init_dispatcher();
101
150
  init_envelope();
102
151
  init_ws_client();
152
+ init_codex_oauth();
103
153
  init_recovery();
104
154
  init_fetch_error();
105
155
  }
@@ -115,6 +165,7 @@ import * as fs2 from "fs";
115
165
  import * as path2 from "path";
116
166
 
117
167
  // src/cli/utils.ts
168
+ init_codex_oauth();
118
169
  import * as fs from "fs";
119
170
  import * as path from "path";
120
171
  function detectProjectType() {
@@ -157,6 +208,25 @@ function detectAiSdks(projectType) {
157
208
  function validateApiKey(key) {
158
209
  return key.startsWith("sk-skalpel-") && key.length >= 20;
159
210
  }
211
+ function validateCodexOAuth() {
212
+ try {
213
+ const auth = readCodexAuth();
214
+ if (auth !== null) {
215
+ return { present: true };
216
+ }
217
+ } catch (err) {
218
+ return { present: false, reason: `other: ${err.message}` };
219
+ }
220
+ const candidatePath = path.join(
221
+ process.env.HOME ?? process.env.USERPROFILE ?? "",
222
+ ".codex",
223
+ "auth.json"
224
+ );
225
+ if (!fs.existsSync(candidatePath)) {
226
+ return { present: false, reason: "file missing" };
227
+ }
228
+ return { present: false, reason: "malformed JSON" };
229
+ }
160
230
  function generateCodeSample(config) {
161
231
  if (config.integrationMethod === "wrapper") {
162
232
  if (config.providers.includes("openai")) {
@@ -1525,6 +1595,12 @@ function configureCodex(agent, proxyConfig, direct = false) {
1525
1595
  content = removeCodexDirectProvider(content);
1526
1596
  }
1527
1597
  fs9.writeFileSync(configPath, content);
1598
+ const oauth = validateCodexOAuth();
1599
+ if (!oauth.present) {
1600
+ process.stderr.write("OAuth not configured. Run: codex login\n");
1601
+ process.stderr.write(" Then re-run: skalpel configure\n");
1602
+ process.stderr.write(" (Skalpel will fall back to OPENAI_API_KEY env var if OAuth missing.)\n");
1603
+ }
1528
1604
  }
1529
1605
  function getCursorConfigDir() {
1530
1606
  if (process.platform === "darwin") {
@@ -1833,6 +1909,7 @@ var wss = new WebSocketServer({
1833
1909
  });
1834
1910
 
1835
1911
  // src/proxy/server.ts
1912
+ init_codex_oauth();
1836
1913
  var proxyStartTime = 0;
1837
1914
  function stopProxy(config) {
1838
1915
  const pid = readPid(config.pidFile);
@@ -2240,10 +2317,13 @@ async function runWizard(options) {
2240
2317
  }
2241
2318
  print11("");
2242
2319
  const codexConfigured = agentsToConfigure.some((a) => a.name === "codex");
2243
- if (codexConfigured && !process.env.OPENAI_API_KEY) {
2244
- print11(" [!] Codex expects OPENAI_API_KEY to be set. The Skalpel proxy ignores the value,");
2245
- print11(" so any non-empty string works, e.g.:");
2246
- print11(" export OPENAI_API_KEY=sk-codex-placeholder-skalpel");
2320
+ if (codexConfigured) {
2321
+ print11(" [!] Codex auth: recommended -> run 'codex login' first to enable ChatGPT plan billing.");
2322
+ print11(" The OPENAI_API_KEY env var (placeholder OK) is still required for Codex startup");
2323
+ print11(" but is ignored by Skalpel when OAuth is present.");
2324
+ if (!process.env.OPENAI_API_KEY) {
2325
+ print11(" example: export OPENAI_API_KEY=sk-codex-placeholder-skalpel");
2326
+ }
2247
2327
  print11("");
2248
2328
  }
2249
2329
  }