@codeworksh/aikit 0.8.0 → 0.9.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 (47) hide show
  1. package/README.md +10 -1
  2. package/cli.mjs +75 -83
  3. package/cli.mjs.map +1 -1
  4. package/{codex-MtP1574D.mjs → codex-CLDBTrFm.mjs} +132 -134
  5. package/codex-CLDBTrFm.mjs.map +1 -0
  6. package/copilot-CcD3g6H8.mjs +425 -0
  7. package/copilot-CcD3g6H8.mjs.map +1 -0
  8. package/copilot-headers-DceqXlpj.mjs +104 -0
  9. package/copilot-headers-DceqXlpj.mjs.map +1 -0
  10. package/github-copilot-BJIrxOzT.mjs +3 -0
  11. package/github-copilot-SIUCJz1G.mjs +126 -0
  12. package/github-copilot-SIUCJz1G.mjs.map +1 -0
  13. package/index.d.mts +980 -932
  14. package/index.mjs +63 -11
  15. package/index.mjs.map +1 -1
  16. package/interactive-eoNnscGB.mjs +57 -0
  17. package/interactive-eoNnscGB.mjs.map +1 -0
  18. package/{model-CpX11Oob.d.mts → model-CrVLp622.d.mts} +159 -130
  19. package/{model-CBnredXj.mjs → model-gCDbmGqT.mjs} +31 -2
  20. package/model-gCDbmGqT.mjs.map +1 -0
  21. package/{modelgen-BjSsusLQ.mjs → modelgen-DvorXOjJ.mjs} +148 -29
  22. package/modelgen-DvorXOjJ.mjs.map +1 -0
  23. package/modelgen.d.mts +2 -3
  24. package/modelgen.mjs +1 -1
  25. package/oauth/github/copilot.d.mts +169 -0
  26. package/oauth/github/copilot.mjs +2 -0
  27. package/oauth/interactive.d.mts +21 -0
  28. package/oauth/interactive.mjs +2 -0
  29. package/oauth/openai/codex.d.mts +46 -28
  30. package/oauth/openai/codex.mjs +2 -2
  31. package/oauth/summary.d.mts +67 -0
  32. package/oauth/summary.mjs +2 -0
  33. package/{openai-codex-t4B7g-gv.mjs → openai-codex-Bqzti6Mc.mjs} +3 -3
  34. package/{openai-codex-t4B7g-gv.mjs.map → openai-codex-Bqzti6Mc.mjs.map} +1 -1
  35. package/openai-codex-DA1Q7mLN.mjs +2 -0
  36. package/package.json +34 -22
  37. package/{registry-DYdYTwFt.mjs → registry-ZxwIW0gX.mjs} +5 -3
  38. package/registry-ZxwIW0gX.mjs.map +1 -0
  39. package/storage-v6qjTQ0v.mjs +178 -0
  40. package/storage-v6qjTQ0v.mjs.map +1 -0
  41. package/summary-C_QyCVWa.mjs +68 -0
  42. package/summary-C_QyCVWa.mjs.map +1 -0
  43. package/codex-MtP1574D.mjs.map +0 -1
  44. package/model-CBnredXj.mjs.map +0 -1
  45. package/modelgen-BjSsusLQ.mjs.map +0 -1
  46. package/openai-codex-BRH4Qa7l.mjs +0 -2
  47. package/registry-DYdYTwFt.mjs.map +0 -1
package/README.md CHANGED
@@ -147,20 +147,29 @@ pnpm aikit modelgen [path]
147
147
 
148
148
  ### OAuth Providers
149
149
 
150
- Manage OAuth credentials for providers that require it, such as OpenAI Codex.
150
+ Manage OAuth credentials for providers that require it, such as OpenAI Codex and GitHub Copilot.
151
151
 
152
152
  ```bash
153
153
  # Start an OAuth login flow in your browser
154
154
  pnpm aikit auth --openai-codex
155
155
 
156
+ # Codex with a device code instead: no localhost callback port, so it works
157
+ # over SSH, in containers, and when something else holds port 1455
158
+ pnpm aikit auth --openai-codex --device
159
+
160
+ # GitHub Copilot uses the device flow; add --enterprise <domain> for GitHub Enterprise
161
+ pnpm aikit auth --github-copilot
162
+
156
163
  # Check the status of your stored credentials
157
164
  pnpm aikit auth --openai-codex --status
165
+ pnpm aikit auth --github-copilot --status
158
166
 
159
167
  # Refresh your current credentials
160
168
  pnpm aikit auth --openai-codex --refresh
161
169
 
162
170
  # Clear stored credentials
163
171
  pnpm aikit auth --openai-codex --logout
172
+ pnpm aikit auth --github-copilot --logout
164
173
  ```
165
174
 
166
175
  ## Contribute
package/cli.mjs CHANGED
@@ -1,105 +1,81 @@
1
1
  #!/usr/bin/env node
2
- import { c as openAICodexHeaders, r as OpenAICodexOAuthClient, t as JsonOpenAICodexAuthStorage } from "./codex-MtP1574D.mjs";
3
- import { t as ModelgenCommand } from "./modelgen-BjSsusLQ.mjs";
2
+ import { r as OpenAICodexOAuthClient, t as JsonOpenAICodexAuthStorage } from "./codex-CLDBTrFm.mjs";
3
+ import { t as ModelgenCommand } from "./modelgen-DvorXOjJ.mjs";
4
+ import { i as JsonGitHubCopilotAuthStorage, r as GitHubCopilotOAuthClient } from "./copilot-CcD3g6H8.mjs";
5
+ import { n as promptLine, t as openBrowser } from "./interactive-eoNnscGB.mjs";
6
+ import { a as oauthNotice, c as summarizeGitHubCopilot, i as oauthLoginIssue, l as summarizeOpenAICodex, n as checkOAuthProvider, r as formatOAuthSummary, s as oauthRefreshIssue } from "./summary-C_QyCVWa.mjs";
4
7
  import yargs from "yargs";
5
8
  import { hideBin } from "yargs/helpers";
6
9
  //#region package.json
7
- var version = "0.8.0";
10
+ var version = "0.9.0";
8
11
  //#endregion
9
12
  //#region src/cli/oauth.ts
10
- async function promptLine(message) {
11
- const rl = (await import("node:readline/promises")).createInterface({
12
- input: process.stdin,
13
- output: process.stdout
14
- });
15
- try {
16
- return await rl.question(`${message} `);
17
- } finally {
18
- rl.close();
19
- }
20
- }
21
- async function openBrowser(url) {
22
- const { spawn } = await import("node:child_process");
23
- const command = process.platform === "darwin" ? {
24
- file: "open",
25
- args: [url]
26
- } : process.platform === "win32" ? {
27
- file: "cmd",
28
- args: [
29
- "/c",
30
- "start",
31
- "",
32
- url
33
- ]
34
- } : {
35
- file: "xdg-open",
36
- args: [url]
37
- };
38
- spawn(command.file, command.args, {
39
- detached: true,
40
- stdio: "ignore"
41
- }).unref();
42
- }
43
- function printCredentials(credentials, options) {
44
- if (options.json) {
45
- console.log(JSON.stringify({
46
- accountId: credentials.accountId,
47
- expires: credentials.expires,
48
- expiresAt: new Date(credentials.expires).toISOString(),
49
- headers: options.printHeaders ? openAICodexHeaders(credentials) : void 0
50
- }, null, 2));
51
- return;
52
- }
53
- console.log(`Account: ${credentials.accountId}`);
54
- console.log(`Expires: ${new Date(credentials.expires).toLocaleString()}`);
55
- if (options.printHeaders) {
56
- console.log("Headers:");
57
- for (const [name, value] of Object.entries(openAICodexHeaders(credentials))) console.log(`${name}: ${value}`);
58
- }
59
- }
60
13
  async function runOpenAICodexAuth(args) {
61
14
  const storage = new JsonOpenAICodexAuthStorage({ ...args.authFile !== void 0 && { path: args.authFile } });
62
15
  const client = new OpenAICodexOAuthClient({ storage });
16
+ const show = (credentials) => process.stdout.write(formatOAuthSummary(summarizeOpenAICodex(credentials), { json: args.json === true }));
63
17
  if (args.logout) {
64
18
  await client.logout();
65
- console.log(`Cleared OpenAI Codex credentials from ${storage.path}`);
19
+ console.warn(oauthNotice("cleared", "openai-codex", storage.path));
66
20
  return;
67
21
  }
68
- if (args.status) {
69
- const credentials = await storage.get();
22
+ if (args.status || args.refresh) {
23
+ const credentials = args.refresh ? await client.getCredentials() : await storage.get();
70
24
  if (!credentials) {
71
- console.log(`No OpenAI Codex credentials found at ${storage.path}`);
25
+ console.error(oauthNotice("missing", "openai-codex", storage.path));
72
26
  process.exitCode = 1;
73
27
  return;
74
28
  }
75
- printCredentials(credentials, args);
29
+ if (args.refresh) console.warn(oauthNotice("refreshed", "openai-codex", storage.path));
30
+ show(credentials);
31
+ return;
32
+ }
33
+ const credentials = await client.login({
34
+ device: args.device === true,
35
+ onAuth: (info) => {
36
+ console.warn(info.instructions ?? "Complete OpenAI Codex authentication in your browser.");
37
+ if (info.userCode) console.warn(`Enter code: ${info.userCode}`);
38
+ console.warn(info.url);
39
+ if (args.browser !== false) openBrowser(info.url, (error) => console.warn(`Failed to open browser: ${error.message}`));
40
+ },
41
+ onProgress: (message) => console.warn(message),
42
+ onPrompt: async (prompt) => promptLine(prompt.message)
43
+ });
44
+ console.warn(oauthNotice("saved", "openai-codex", storage.path));
45
+ show(credentials);
46
+ }
47
+ async function runGitHubCopilotAuth(args) {
48
+ const storage = new JsonGitHubCopilotAuthStorage({ ...args.authFile !== void 0 && { path: args.authFile } });
49
+ const client = new GitHubCopilotOAuthClient({ storage });
50
+ const show = (credentials) => process.stdout.write(formatOAuthSummary(summarizeGitHubCopilot(credentials), { json: args.json === true }));
51
+ if (args.logout) {
52
+ await client.logout();
53
+ console.warn(oauthNotice("cleared", "github-copilot", storage.path));
76
54
  return;
77
55
  }
78
- if (args.refresh) {
79
- const credentials = await client.getCredentials();
56
+ if (args.status) {
57
+ const credentials = await storage.get();
80
58
  if (!credentials) {
81
- console.log(`No OpenAI Codex credentials found at ${storage.path}`);
59
+ console.error(oauthNotice("missing", "github-copilot", storage.path));
82
60
  process.exitCode = 1;
83
61
  return;
84
62
  }
85
- console.log(`Refreshed OpenAI Codex credentials in ${storage.path}`);
86
- printCredentials(credentials, args);
63
+ show(credentials);
87
64
  return;
88
65
  }
89
66
  const credentials = await client.login({
90
- ...args.originator !== void 0 && { originator: args.originator },
91
- ...args.manual && { onManualCodeInput: async () => promptLine("Paste the redirect URL or authorization code, or wait for browser callback:") },
67
+ ...args.enterprise !== void 0 && { enterpriseUrl: args.enterprise },
68
+ enableModels: args.enableModels === true,
92
69
  onAuth: (info) => {
93
- console.log(info.instructions ?? "Complete OpenAI Codex authentication in your browser.");
94
- console.log(info.url);
95
- if (args.browser !== false) openBrowser(info.url).catch((error) => {
96
- console.warn(`Failed to open browser: ${error instanceof Error ? error.message : String(error)}`);
97
- });
70
+ console.warn(info.instructions ?? "Complete GitHub Copilot authentication in your browser.");
71
+ console.warn(`Enter code: ${info.userCode}`);
72
+ console.warn(info.url);
73
+ if (args.browser !== false) openBrowser(info.url, (error) => console.warn(`Failed to open browser: ${error.message}`));
98
74
  },
99
- onPrompt: async (prompt) => promptLine(prompt.message)
75
+ onProgress: (message) => console.warn(message)
100
76
  });
101
- console.log(`Saved OpenAI Codex credentials to ${storage.path}`);
102
- printCredentials(credentials, args);
77
+ console.warn(oauthNotice("saved", "github-copilot", storage.path));
78
+ show(credentials);
103
79
  }
104
80
  //#endregion
105
81
  //#region src/cli.ts
@@ -109,6 +85,15 @@ const cli = yargs(hideBin(process.argv)).scriptName("aikit").wrap(100).help("hel
109
85
  builder: (yargs) => yargs.option("openai-codex", {
110
86
  type: "boolean",
111
87
  describe: "use OpenAI Codex OAuth"
88
+ }).option("github-copilot", {
89
+ type: "boolean",
90
+ describe: "use GitHub Copilot OAuth (device flow)"
91
+ }).option("enterprise", {
92
+ type: "string",
93
+ describe: "GitHub Enterprise domain for GitHub Copilot login"
94
+ }).option("enable-models", {
95
+ type: "boolean",
96
+ describe: "enable unconfigured Copilot catalog models after login"
112
97
  }).option("auth-file", {
113
98
  type: "string",
114
99
  describe: "path to auth.json (defaults to CODEWORK_CREDENTIALS or ~/.codework/aikit/auth.json)"
@@ -116,14 +101,10 @@ const cli = yargs(hideBin(process.argv)).scriptName("aikit").wrap(100).help("hel
116
101
  type: "boolean",
117
102
  default: true,
118
103
  describe: "open the authorization URL in the default browser"
119
- }).option("manual", {
104
+ }).option("device", {
120
105
  type: "boolean",
121
106
  default: false,
122
- describe: "also prompt for a pasted redirect URL or authorization code"
123
- }).option("originator", {
124
- type: "string",
125
- default: "codework",
126
- describe: "OAuth originator value"
107
+ describe: "use the device-code flow instead of a localhost browser callback"
127
108
  }).option("status", {
128
109
  type: "boolean",
129
110
  describe: "show stored credential status without refreshing"
@@ -136,19 +117,30 @@ const cli = yargs(hideBin(process.argv)).scriptName("aikit").wrap(100).help("hel
136
117
  }).option("json", {
137
118
  type: "boolean",
138
119
  describe: "print machine-readable output"
139
- }).option("print-headers", {
140
- type: "boolean",
141
- describe: "print request headers for Codex API calls"
142
120
  }).check((args) => {
143
- if (!args.openaiCodex) throw new Error("choose an auth provider, for example: auth --openai-codex");
121
+ const checked = checkOAuthProvider({
122
+ openaiCodex: args["openai-codex"],
123
+ githubCopilot: args["github-copilot"]
124
+ });
125
+ if (!checked.ok) throw new Error(checked.message);
144
126
  if ([
145
127
  args.status,
146
128
  args.refresh,
147
129
  args.logout
148
130
  ].filter(Boolean).length > 1) throw new Error("choose only one of --status, --refresh, or --logout");
131
+ const issue = oauthLoginIssue(checked.provider, {
132
+ device: args.device,
133
+ enterprise: args.enterprise,
134
+ enableModels: args["enable-models"]
135
+ }) ?? (args.refresh ? oauthRefreshIssue(checked.provider) : void 0);
136
+ if (issue) throw new Error(issue);
149
137
  return true;
150
138
  }),
151
139
  handler: async (args) => {
140
+ if (args.githubCopilot) {
141
+ await runGitHubCopilotAuth(args);
142
+ return;
143
+ }
152
144
  await runOpenAICodexAuth(args);
153
145
  }
154
146
  }).demandCommand(1, "Choose a command").strict().fail((message, error) => {
package/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["pkg.version"],"sources":["../../package.json","../../src/cli/oauth.ts","../../src/cli.ts"],"sourcesContent":["","import type { CommandModule } from \"yargs\";\nimport {\n\tJsonOpenAICodexAuthStorage,\n\tOpenAICodexOAuthClient,\n\ttype OpenAICodexOAuthCredentials,\n\topenAICodexHeaders,\n} from \"../oauth/openai/codex.ts\";\n\n// `?: T | undefined` rather than plain `?: T`: yargs hands these through as explicit `undefined`,\n// which a bare optional property no longer accepts under `exactOptionalPropertyTypes`.\ntype AuthArgs = {\n\topenaiCodex?: boolean | undefined;\n\tauthFile?: string | undefined;\n\tbrowser?: boolean | undefined;\n\tmanual?: boolean | undefined;\n\tstatus?: boolean | undefined;\n\trefresh?: boolean | undefined;\n\tlogout?: boolean | undefined;\n\tjson?: boolean | undefined;\n\tprintHeaders?: boolean | undefined;\n\toriginator?: string | undefined;\n};\n\nasync function promptLine(message: string): Promise<string> {\n\tconst readline = await import(\"node:readline/promises\");\n\tconst rl = readline.createInterface({\n\t\tinput: process.stdin,\n\t\toutput: process.stdout,\n\t});\n\n\ttry {\n\t\treturn await rl.question(`${message} `);\n\t} finally {\n\t\trl.close();\n\t}\n}\n\nasync function openBrowser(url: string): Promise<void> {\n\tconst { spawn } = await import(\"node:child_process\");\n\tconst command =\n\t\tprocess.platform === \"darwin\"\n\t\t\t? { file: \"open\", args: [url] }\n\t\t\t: process.platform === \"win32\"\n\t\t\t\t? { file: \"cmd\", args: [\"/c\", \"start\", \"\", url] }\n\t\t\t\t: { file: \"xdg-open\", args: [url] };\n\n\tconst child = spawn(command.file, command.args, {\n\t\tdetached: true,\n\t\tstdio: \"ignore\",\n\t});\n\tchild.unref();\n}\n\nfunction printCredentials(\n\tcredentials: OpenAICodexOAuthCredentials,\n\toptions: { json?: boolean | undefined; printHeaders?: boolean | undefined },\n) {\n\tif (options.json) {\n\t\tconsole.log(\n\t\t\tJSON.stringify(\n\t\t\t\t{\n\t\t\t\t\taccountId: credentials.accountId,\n\t\t\t\t\texpires: credentials.expires,\n\t\t\t\t\texpiresAt: new Date(credentials.expires).toISOString(),\n\t\t\t\t\theaders: options.printHeaders ? openAICodexHeaders(credentials) : undefined,\n\t\t\t\t},\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t),\n\t\t);\n\t\treturn;\n\t}\n\n\tconsole.log(`Account: ${credentials.accountId}`);\n\tconsole.log(`Expires: ${new Date(credentials.expires).toLocaleString()}`);\n\tif (options.printHeaders) {\n\t\tconsole.log(\"Headers:\");\n\t\tfor (const [name, value] of Object.entries(openAICodexHeaders(credentials))) {\n\t\t\tconsole.log(`${name}: ${value}`);\n\t\t}\n\t}\n}\n\nasync function runOpenAICodexAuth(args: AuthArgs): Promise<void> {\n\tconst storage = new JsonOpenAICodexAuthStorage({\n\t\t...(args.authFile !== undefined && { path: args.authFile }),\n\t});\n\tconst client = new OpenAICodexOAuthClient({ storage });\n\n\tif (args.logout) {\n\t\tawait client.logout();\n\t\tconsole.log(`Cleared OpenAI Codex credentials from ${storage.path}`);\n\t\treturn;\n\t}\n\n\tif (args.status) {\n\t\tconst credentials = await storage.get();\n\t\tif (!credentials) {\n\t\t\tconsole.log(`No OpenAI Codex credentials found at ${storage.path}`);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tprintCredentials(credentials, args);\n\t\treturn;\n\t}\n\n\tif (args.refresh) {\n\t\tconst credentials = await client.getCredentials();\n\t\tif (!credentials) {\n\t\t\tconsole.log(`No OpenAI Codex credentials found at ${storage.path}`);\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tconsole.log(`Refreshed OpenAI Codex credentials in ${storage.path}`);\n\t\tprintCredentials(credentials, args);\n\t\treturn;\n\t}\n\n\tconst credentials = await client.login({\n\t\t...(args.originator !== undefined && { originator: args.originator }),\n\t\t...(args.manual && {\n\t\t\tonManualCodeInput: async () =>\n\t\t\t\tpromptLine(\"Paste the redirect URL or authorization code, or wait for browser callback:\"),\n\t\t}),\n\t\tonAuth: (info) => {\n\t\t\tconsole.log(info.instructions ?? \"Complete OpenAI Codex authentication in your browser.\");\n\t\t\tconsole.log(info.url);\n\t\t\tif (args.browser !== false) {\n\t\t\t\tvoid openBrowser(info.url).catch((error) => {\n\t\t\t\t\tconsole.warn(`Failed to open browser: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tonPrompt: async (prompt) => promptLine(prompt.message),\n\t});\n\n\tconsole.log(`Saved OpenAI Codex credentials to ${storage.path}`);\n\tprintCredentials(credentials, args);\n}\n\nexport const OAuthCommand: CommandModule<object, AuthArgs> = {\n\tcommand: \"auth\",\n\tdescribe: \"manage OAuth credentials\",\n\tbuilder: (yargs) =>\n\t\tyargs\n\t\t\t.option(\"openai-codex\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"use OpenAI Codex OAuth\",\n\t\t\t})\n\t\t\t.option(\"auth-file\", {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdescribe: \"path to auth.json (defaults to CODEWORK_CREDENTIALS or ~/.codework/aikit/auth.json)\",\n\t\t\t})\n\t\t\t.option(\"browser\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdefault: true,\n\t\t\t\tdescribe: \"open the authorization URL in the default browser\",\n\t\t\t})\n\t\t\t.option(\"manual\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdefault: false,\n\t\t\t\tdescribe: \"also prompt for a pasted redirect URL or authorization code\",\n\t\t\t})\n\t\t\t.option(\"originator\", {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdefault: \"codework\",\n\t\t\t\tdescribe: \"OAuth originator value\",\n\t\t\t})\n\t\t\t.option(\"status\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"show stored credential status without refreshing\",\n\t\t\t})\n\t\t\t.option(\"refresh\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"refresh stored credentials if expired\",\n\t\t\t})\n\t\t\t.option(\"logout\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"clear stored credentials\",\n\t\t\t})\n\t\t\t.option(\"json\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"print machine-readable output\",\n\t\t\t})\n\t\t\t.option(\"print-headers\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"print request headers for Codex API calls\",\n\t\t\t})\n\t\t\t.check((args) => {\n\t\t\t\tif (!args.openaiCodex) throw new Error(\"choose an auth provider, for example: auth --openai-codex\");\n\t\t\t\tconst actions = [args.status, args.refresh, args.logout].filter(Boolean).length;\n\t\t\t\tif (actions > 1) throw new Error(\"choose only one of --status, --refresh, or --logout\");\n\t\t\t\treturn true;\n\t\t\t}),\n\thandler: async (args) => {\n\t\tawait runOpenAICodexAuth(args);\n\t},\n};\n","#!/usr/bin/env node\nimport pkg from \"../package.json\" with { type: \"json\" };\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { ModelgenCommand } from \"./cli/modelgen.ts\";\nimport { OAuthCommand } from \"./cli/oauth.ts\";\n\nconst cli = yargs(hideBin(process.argv))\n\t.scriptName(\"aikit\")\n\t.wrap(100)\n\t.help(\"help\", \"show help\")\n\t.alias(\"help\", \"h\")\n\t.version(\"version\", \"show version number\", pkg.version)\n\t.alias(\"version\", \"v\")\n\t.command(ModelgenCommand)\n\t.command(OAuthCommand)\n\t.demandCommand(1, \"Choose a command\")\n\t.strict()\n\t.fail((message, error) => {\n\t\tif (error) throw error;\n\t\tif (message) throw new Error(message);\n\t});\n\ntry {\n\tawait cli.parse();\n} catch (error) {\n\tprocess.exitCode = 1;\n\tconsole.error(error instanceof Error ? error.message : String(error));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["pkg.version"],"sources":["../../package.json","../../src/cli/oauth.ts","../../src/cli.ts"],"sourcesContent":["","import type { CommandModule } from \"yargs\";\nimport {\n\tGitHubCopilotOAuthClient,\n\ttype GitHubCopilotOAuthCredentials,\n\tJsonGitHubCopilotAuthStorage,\n} from \"../oauth/github/copilot.ts\";\nimport {\n\tJsonOpenAICodexAuthStorage,\n\tOpenAICodexOAuthClient,\n\ttype OpenAICodexOAuthCredentials,\n} from \"../oauth/openai/codex.ts\";\nimport { openBrowser, promptLine } from \"../oauth/interactive.ts\";\nimport {\n\tcheckOAuthProvider,\n\tformatOAuthSummary,\n\toauthLoginIssue,\n\toauthRefreshIssue,\n\toauthNotice,\n\tsummarizeGitHubCopilot,\n\tsummarizeOpenAICodex,\n} from \"../oauth/summary.ts\";\n\n// `?: T | undefined` rather than plain `?: T`: yargs hands these through as explicit `undefined`,\n// which a bare optional property no longer accepts under `exactOptionalPropertyTypes`.\ntype AuthArgs = {\n\topenaiCodex?: boolean | undefined;\n\tgithubCopilot?: boolean | undefined;\n\tauthFile?: string | undefined;\n\tbrowser?: boolean | undefined;\n\tdevice?: boolean | undefined;\n\tstatus?: boolean | undefined;\n\trefresh?: boolean | undefined;\n\tlogout?: boolean | undefined;\n\tjson?: boolean | undefined;\n\tenterprise?: string | undefined;\n\tenableModels?: boolean | undefined;\n};\n\nasync function runOpenAICodexAuth(args: AuthArgs): Promise<void> {\n\tconst storage = new JsonOpenAICodexAuthStorage({\n\t\t...(args.authFile !== undefined && { path: args.authFile }),\n\t});\n\tconst client = new OpenAICodexOAuthClient({ storage });\n\n\tconst show = (credentials: OpenAICodexOAuthCredentials) =>\n\t\tprocess.stdout.write(formatOAuthSummary(summarizeOpenAICodex(credentials), { json: args.json === true }));\n\n\tif (args.logout) {\n\t\tawait client.logout();\n\t\tconsole.warn(oauthNotice(\"cleared\", \"openai-codex\", storage.path));\n\t\treturn;\n\t}\n\n\tif (args.status || args.refresh) {\n\t\t// --status is a read: asking the client for headers would refresh behind it.\n\t\tconst credentials = args.refresh ? await client.getCredentials() : await storage.get();\n\t\tif (!credentials) {\n\t\t\tconsole.error(oauthNotice(\"missing\", \"openai-codex\", storage.path));\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tif (args.refresh) console.warn(oauthNotice(\"refreshed\", \"openai-codex\", storage.path));\n\t\tshow(credentials);\n\t\treturn;\n\t}\n\n\tconst credentials = await client.login({\n\t\tdevice: args.device === true,\n\t\t// stderr, so `--json` stdout stays parseable.\n\t\tonAuth: (info) => {\n\t\t\tconsole.warn(info.instructions ?? \"Complete OpenAI Codex authentication in your browser.\");\n\t\t\tif (info.userCode) console.warn(`Enter code: ${info.userCode}`);\n\t\t\tconsole.warn(info.url);\n\t\t\tif (args.browser !== false) {\n\t\t\t\tvoid openBrowser(info.url, (error) => console.warn(`Failed to open browser: ${error.message}`));\n\t\t\t}\n\t\t},\n\t\tonProgress: (message) => console.warn(message),\n\t\tonPrompt: async (prompt) => promptLine(prompt.message),\n\t});\n\n\tconsole.warn(oauthNotice(\"saved\", \"openai-codex\", storage.path));\n\tshow(credentials);\n}\n\nasync function runGitHubCopilotAuth(args: AuthArgs): Promise<void> {\n\tconst storage = new JsonGitHubCopilotAuthStorage({\n\t\t...(args.authFile !== undefined && { path: args.authFile }),\n\t});\n\tconst client = new GitHubCopilotOAuthClient({ storage });\n\n\tconst show = (credentials: GitHubCopilotOAuthCredentials) =>\n\t\tprocess.stdout.write(formatOAuthSummary(summarizeGitHubCopilot(credentials), { json: args.json === true }));\n\n\tif (args.logout) {\n\t\tawait client.logout();\n\t\tconsole.warn(oauthNotice(\"cleared\", \"github-copilot\", storage.path));\n\t\treturn;\n\t}\n\n\tif (args.status) {\n\t\tconst credentials = await storage.get();\n\t\tif (!credentials) {\n\t\t\tconsole.error(oauthNotice(\"missing\", \"github-copilot\", storage.path));\n\t\t\tprocess.exitCode = 1;\n\t\t\treturn;\n\t\t}\n\t\tshow(credentials);\n\t\treturn;\n\t}\n\n\t// GitHub OAuth tokens never expire; there is no refresh path.\n\tconst credentials = await client.login({\n\t\t...(args.enterprise !== undefined && { enterpriseUrl: args.enterprise }),\n\t\tenableModels: args.enableModels === true,\n\t\t// stderr, so `--json` stdout stays parseable.\n\t\tonAuth: (info) => {\n\t\t\tconsole.warn(info.instructions ?? \"Complete GitHub Copilot authentication in your browser.\");\n\t\t\tconsole.warn(`Enter code: ${info.userCode}`);\n\t\t\tconsole.warn(info.url);\n\t\t\tif (args.browser !== false) {\n\t\t\t\tvoid openBrowser(info.url, (error) => console.warn(`Failed to open browser: ${error.message}`));\n\t\t\t}\n\t\t},\n\t\tonProgress: (message) => console.warn(message),\n\t});\n\n\tconsole.warn(oauthNotice(\"saved\", \"github-copilot\", storage.path));\n\tshow(credentials);\n}\n\nexport const OAuthCommand: CommandModule<object, AuthArgs> = {\n\tcommand: \"auth\",\n\tdescribe: \"manage OAuth credentials\",\n\tbuilder: (yargs) =>\n\t\tyargs\n\t\t\t.option(\"openai-codex\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"use OpenAI Codex OAuth\",\n\t\t\t})\n\t\t\t.option(\"github-copilot\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"use GitHub Copilot OAuth (device flow)\",\n\t\t\t})\n\t\t\t.option(\"enterprise\", {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdescribe: \"GitHub Enterprise domain for GitHub Copilot login\",\n\t\t\t})\n\t\t\t.option(\"enable-models\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"enable unconfigured Copilot catalog models after login\",\n\t\t\t})\n\t\t\t.option(\"auth-file\", {\n\t\t\t\ttype: \"string\",\n\t\t\t\tdescribe: \"path to auth.json (defaults to CODEWORK_CREDENTIALS or ~/.codework/aikit/auth.json)\",\n\t\t\t})\n\t\t\t.option(\"browser\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdefault: true,\n\t\t\t\tdescribe: \"open the authorization URL in the default browser\",\n\t\t\t})\n\t\t\t.option(\"device\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdefault: false,\n\t\t\t\tdescribe: \"use the device-code flow instead of a localhost browser callback\",\n\t\t\t})\n\t\t\t.option(\"status\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"show stored credential status without refreshing\",\n\t\t\t})\n\t\t\t.option(\"refresh\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"refresh stored credentials if expired\",\n\t\t\t})\n\t\t\t.option(\"logout\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"clear stored credentials\",\n\t\t\t})\n\t\t\t.option(\"json\", {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\tdescribe: \"print machine-readable output\",\n\t\t\t})\n\t\t\t// yargs types this callback with the kebab-case keys, so read those.\n\t\t\t.check((args) => {\n\t\t\t\tconst checked = checkOAuthProvider({\n\t\t\t\t\topenaiCodex: args[\"openai-codex\"],\n\t\t\t\t\tgithubCopilot: args[\"github-copilot\"],\n\t\t\t\t});\n\t\t\t\tif (!checked.ok) throw new Error(checked.message);\n\t\t\t\tif ([args.status, args.refresh, args.logout].filter(Boolean).length > 1) {\n\t\t\t\t\tthrow new Error(\"choose only one of --status, --refresh, or --logout\");\n\t\t\t\t}\n\t\t\t\tconst issue =\n\t\t\t\t\toauthLoginIssue(checked.provider, {\n\t\t\t\t\t\tdevice: args.device,\n\t\t\t\t\t\tenterprise: args.enterprise,\n\t\t\t\t\t\tenableModels: args[\"enable-models\"],\n\t\t\t\t\t}) ?? (args.refresh ? oauthRefreshIssue(checked.provider) : undefined);\n\t\t\t\tif (issue) throw new Error(issue);\n\t\t\t\treturn true;\n\t\t\t}),\n\thandler: async (args) => {\n\t\tif (args.githubCopilot) {\n\t\t\tawait runGitHubCopilotAuth(args);\n\t\t\treturn;\n\t\t}\n\t\tawait runOpenAICodexAuth(args);\n\t},\n};\n","#!/usr/bin/env node\nimport pkg from \"../package.json\" with { type: \"json\" };\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { ModelgenCommand } from \"./cli/modelgen.ts\";\nimport { OAuthCommand } from \"./cli/oauth.ts\";\n\nconst cli = yargs(hideBin(process.argv))\n\t.scriptName(\"aikit\")\n\t.wrap(100)\n\t.help(\"help\", \"show help\")\n\t.alias(\"help\", \"h\")\n\t.version(\"version\", \"show version number\", pkg.version)\n\t.alias(\"version\", \"v\")\n\t.command(ModelgenCommand)\n\t.command(OAuthCommand)\n\t.demandCommand(1, \"Choose a command\")\n\t.strict()\n\t.fail((message, error) => {\n\t\tif (error) throw error;\n\t\tif (message) throw new Error(message);\n\t});\n\ntry {\n\tawait cli.parse();\n} catch (error) {\n\tprocess.exitCode = 1;\n\tconsole.error(error instanceof Error ? error.message : String(error));\n}\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { i as pollDeviceCode, n as isObject, r as readEnv, t as JsonAuthStorage } from "./storage-v6qjTQ0v.mjs";
1
2
  import dedent from "dedent";
2
3
  //#region src/oauth/openai/codex.ts
3
4
  /**
@@ -18,47 +19,20 @@ import dedent from "dedent";
18
19
  * not exported.
19
20
  */
20
21
  const CODEWORK_OAUTH_CALLBACK_HOST = "CODEWORK_OAUTH_CALLBACK_HOST";
21
- const CODEWORK_CREDENTIALS = "CODEWORK_CREDENTIALS";
22
- const CODEWORK_HOME_DIR = "CODEWORK_HOME_DIR";
23
22
  const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
24
23
  const AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
25
24
  const TOKEN_URL = "https://auth.openai.com/oauth/token";
26
25
  const DEFAULT_REDIRECT_URI = "http://localhost:1455/auth/callback";
26
+ const DEVICE_USER_CODE_URL = "https://auth.openai.com/api/accounts/deviceauth/usercode";
27
+ const DEVICE_TOKEN_URL = "https://auth.openai.com/api/accounts/deviceauth/token";
28
+ const DEVICE_VERIFICATION_URI = "https://auth.openai.com/codex/device";
29
+ const DEVICE_REDIRECT_URI = "https://auth.openai.com/deviceauth/callback";
30
+ const DEVICE_TIMEOUT_SECONDS = 900;
27
31
  const DEFAULT_SCOPE = "openid profile email offline_access";
28
32
  const DEFAULT_ORIGINATOR = "codework";
29
33
  const DEFAULT_PROVIDER_ID = "openai-codex";
30
34
  const JWT_CLAIM_PATH = "https://api.openai.com/auth";
31
35
  const REFRESH_SKEW_MS = 6e4;
32
- function readEnv(name) {
33
- if (typeof process === "undefined") return void 0;
34
- return process.env[name];
35
- }
36
- function joinPath(...parts) {
37
- return parts.filter(Boolean).join("/").replaceAll(/\/+/g, "/");
38
- }
39
- function expandHome(path) {
40
- if (path === "~") return homeDirectory();
41
- if (path.startsWith("~/")) return joinPath(homeDirectory(), path.slice(2));
42
- return path;
43
- }
44
- function homeDirectory() {
45
- const home = readEnv("HOME") ?? readEnv("USERPROFILE");
46
- if (!home) throw new Error("unable to resolve home directory for OpenAI Codex auth storage");
47
- return home;
48
- }
49
- function codeworkHomeDirectory() {
50
- const override = readEnv(CODEWORK_HOME_DIR);
51
- if (override) return expandHome(override);
52
- return joinPath(homeDirectory(), ".codework");
53
- }
54
- function defaultOpenAICodexAuthFilePath() {
55
- const authFile = readEnv(CODEWORK_CREDENTIALS);
56
- if (authFile) return expandHome(authFile);
57
- return joinPath(codeworkHomeDirectory(), "aikit", "auth.json");
58
- }
59
- function isObject(value) {
60
- return typeof value === "object" && value !== null && !Array.isArray(value);
61
- }
62
36
  function isOpenAICodexCredentials(value) {
63
37
  if (!isObject(value)) return false;
64
38
  return typeof value.access === "string" && typeof value.refresh === "string" && typeof value.expires === "number" && typeof value.accountId === "string";
@@ -283,7 +257,9 @@ function oauthErrorHtml(message, details) {
283
257
  ...details !== void 0 && { details }
284
258
  });
285
259
  }
286
- async function startLocalOAuthServer(state) {
260
+ async function startLocalOAuthServer(state, redirectUri) {
261
+ const callback = new URL(redirectUri);
262
+ const port = Number(callback.port || (callback.protocol === "https:" ? 443 : 80));
287
263
  const http = await import("node:http").catch(() => void 0);
288
264
  if (!http) throw new Error("openAI codex OAuth callback server is only available in Node.js environments");
289
265
  let settleWait;
@@ -298,7 +274,7 @@ async function startLocalOAuthServer(state) {
298
274
  const server = http.createServer((req, res) => {
299
275
  try {
300
276
  const url = new URL(req.url || "", "http://localhost");
301
- if (url.pathname !== "/auth/callback") {
277
+ if (url.pathname !== callback.pathname) {
302
278
  res.statusCode = 404;
303
279
  res.setHeader("Content-Type", "text/html; charset=utf-8");
304
280
  res.end(oauthErrorHtml("Callback route not found."));
@@ -335,84 +311,139 @@ async function startLocalOAuthServer(state) {
335
311
  });
336
312
  };
337
313
  return new Promise((resolve) => {
338
- server.listen(1455, readEnv(CODEWORK_OAUTH_CALLBACK_HOST) || "127.0.0.1", () => {
314
+ server.listen(port, readEnv(CODEWORK_OAUTH_CALLBACK_HOST) || "127.0.0.1", () => {
339
315
  resolve({
340
316
  close,
341
- cancelWait: () => {
342
- settleWait?.(null);
343
- },
344
317
  waitForCode: () => waitForCodePromise
345
318
  });
346
- }).on("error", () => {
319
+ }).on("error", (error) => {
347
320
  settleWait?.(null);
348
321
  resolve({
349
322
  close,
350
- cancelWait: () => {},
351
- waitForCode: async () => null
323
+ waitForCode: async () => null,
324
+ listenError: error
352
325
  });
353
326
  });
354
327
  });
355
328
  }
356
329
  async function loginOpenAICodex(options) {
330
+ if (options.device) return loginOpenAICodexDeviceCode(options);
357
331
  const flow = await createOpenAICodexAuthorizationFlow(options);
358
- const server = await startLocalOAuthServer(flow.state);
332
+ const server = await startLocalOAuthServer(flow.state, flow.redirectUri);
359
333
  options.onAuth({
360
334
  url: flow.url,
361
335
  instructions: "Complete the browser login to finish OpenAI Codex authentication."
362
336
  });
337
+ if (server.listenError) options.onProgress?.(`Could not listen on ${flow.redirectUri}: ${server.listenError.message}. Another sign-in is probably already running on that port and would receive the callback. Finish the login in the browser, then paste the redirect URL here.`);
363
338
  let code;
364
339
  try {
365
- if (options.onManualCodeInput) {
366
- let manualCode;
367
- let manualError;
368
- const manualPromise = options.onManualCodeInput().then((input) => {
369
- manualCode = input;
370
- if (input.trim()) server.cancelWait();
371
- }).catch((error) => {
372
- manualError = error instanceof Error ? error : new Error(String(error));
373
- server.cancelWait();
374
- });
375
- const result = await server.waitForCode();
376
- if (manualError) throw manualError;
377
- if (result?.code) code = result.code;
378
- else if (manualCode) {
379
- const parsed = parseOpenAICodexAuthorizationInput(manualCode);
380
- if (parsed.state && parsed.state !== flow.state) throw new Error("state mismatch");
381
- code = parsed.code;
382
- }
383
- if (!code) {
384
- await manualPromise;
385
- if (manualError) throw manualError;
386
- if (manualCode) {
387
- const parsed = parseOpenAICodexAuthorizationInput(manualCode);
388
- if (parsed.state && parsed.state !== flow.state) throw new Error("state mismatch");
389
- code = parsed.code;
390
- }
391
- }
392
- } else {
393
- const result = await server.waitForCode();
394
- if (result?.code) code = result.code;
395
- }
340
+ const result = await server.waitForCode();
341
+ if (result?.code) code = result.code;
396
342
  if (!code) {
397
343
  const parsed = parseOpenAICodexAuthorizationInput(await options.onPrompt({ message: "Paste the authorization code or full redirect URL:" }));
398
344
  if (parsed.state && parsed.state !== flow.state) throw new Error("state mismatch");
399
345
  code = parsed.code;
400
346
  }
401
347
  if (!code) throw new Error("missing authorization code");
402
- const tokenResult = await exchangeOpenAICodexAuthorizationCode(code, flow.verifier, flow.redirectUri);
403
- if (tokenResult.type !== "success") throw new Error(tokenResult.message);
404
- const accountId = getOpenAICodexAccountId(tokenResult.access);
405
- if (!accountId) throw new Error("failed to extract accountId from token");
406
- return {
407
- access: tokenResult.access,
408
- refresh: tokenResult.refresh,
409
- expires: tokenResult.expires,
410
- accountId
411
- };
348
+ return await credentialsFromCode(code, flow.verifier, flow.redirectUri);
412
349
  } finally {
413
350
  await server.close();
414
351
  }
415
352
  }
353
+ /** The step both flows share: redeem the code, then read the account off the JWT. */
354
+ async function credentialsFromCode(code, verifier, redirectUri) {
355
+ const tokenResult = await exchangeOpenAICodexAuthorizationCode(code, verifier, redirectUri);
356
+ if (tokenResult.type !== "success") throw new Error(tokenResult.message);
357
+ const accountId = getOpenAICodexAccountId(tokenResult.access);
358
+ if (!accountId) throw new Error("failed to extract accountId from token");
359
+ return {
360
+ access: tokenResult.access,
361
+ refresh: tokenResult.refresh,
362
+ expires: tokenResult.expires,
363
+ accountId
364
+ };
365
+ }
366
+ /** Ask for a user code to type at {@link DEVICE_VERIFICATION_URI}. */
367
+ async function createOpenAICodexDeviceAuth(options = {}) {
368
+ const response = await fetch(DEVICE_USER_CODE_URL, {
369
+ method: "POST",
370
+ headers: { "Content-Type": "application/json" },
371
+ body: JSON.stringify({ client_id: CLIENT_ID }),
372
+ signal: options.signal ?? null
373
+ });
374
+ if (!response.ok) {
375
+ if (response.status === 404) throw new Error("OpenAI Codex device code login is not available for this account. Use the browser login instead.");
376
+ const body = await response.text().catch(() => "");
377
+ throw new Error(`OpenAI Codex device code request failed (${response.status})${body ? `: ${redactOAuthSecrets(body)}` : ""}`);
378
+ }
379
+ const data = await response.json();
380
+ if (!isObject(data)) throw new Error("Invalid OpenAI Codex device code response");
381
+ const interval = typeof data.interval === "string" ? Number(data.interval.trim()) : data.interval;
382
+ if (typeof data.device_auth_id !== "string" || typeof data.user_code !== "string" || typeof interval !== "number" || !Number.isFinite(interval) || interval < 0) throw new Error("Invalid OpenAI Codex device code response fields");
383
+ return {
384
+ deviceAuthId: data.device_auth_id,
385
+ userCode: data.user_code,
386
+ verificationUri: DEVICE_VERIFICATION_URI,
387
+ intervalSeconds: interval
388
+ };
389
+ }
390
+ /** Wait for the code to be approved; the server returns the code and its verifier. */
391
+ async function pollOpenAICodexDeviceAuth(device, options = {}) {
392
+ return pollDeviceCode({
393
+ intervalSeconds: device.intervalSeconds,
394
+ expiresInSeconds: DEVICE_TIMEOUT_SECONDS,
395
+ ...options.signal !== void 0 && { signal: options.signal },
396
+ poll: async () => {
397
+ const response = await fetch(DEVICE_TOKEN_URL, {
398
+ method: "POST",
399
+ headers: { "Content-Type": "application/json" },
400
+ body: JSON.stringify({
401
+ device_auth_id: device.deviceAuthId,
402
+ user_code: device.userCode
403
+ }),
404
+ signal: options.signal ?? null
405
+ });
406
+ if (response.ok) {
407
+ const data = await response.json();
408
+ if (!isObject(data) || typeof data.authorization_code !== "string" || typeof data.code_verifier !== "string") return {
409
+ status: "failed",
410
+ message: "Invalid OpenAI Codex device auth token response"
411
+ };
412
+ return {
413
+ status: "complete",
414
+ value: {
415
+ authorizationCode: data.authorization_code,
416
+ codeVerifier: data.code_verifier
417
+ }
418
+ };
419
+ }
420
+ if (response.status === 403 || response.status === 404) return { status: "pending" };
421
+ const body = await response.text().catch(() => "");
422
+ let errorCode;
423
+ try {
424
+ const parsed = JSON.parse(body);
425
+ const error = isObject(parsed) ? parsed.error : void 0;
426
+ errorCode = isObject(error) ? error.code : error;
427
+ } catch {}
428
+ if (errorCode === "deviceauth_authorization_pending") return { status: "pending" };
429
+ if (errorCode === "slow_down") return { status: "slow_down" };
430
+ return {
431
+ status: "failed",
432
+ message: `OpenAI Codex device authorization failed (${response.status})${body ? `: ${redactOAuthSecrets(body)}` : ""}`
433
+ };
434
+ }
435
+ });
436
+ }
437
+ async function loginOpenAICodexDeviceCode(options) {
438
+ const device = await createOpenAICodexDeviceAuth(options);
439
+ options.onAuth({
440
+ url: device.verificationUri,
441
+ userCode: device.userCode,
442
+ instructions: "Open the URL and enter the code to finish OpenAI Codex authentication."
443
+ });
444
+ const { authorizationCode, codeVerifier } = await pollOpenAICodexDeviceAuth(device, options);
445
+ return credentialsFromCode(authorizationCode, codeVerifier, DEVICE_REDIRECT_URI);
446
+ }
416
447
  async function refreshOpenAICodexToken(refreshToken) {
417
448
  const result = await refreshAccessToken(refreshToken);
418
449
  if (result.type !== "success") throw new Error(result.message);
@@ -433,61 +464,28 @@ function openAICodexHeaders(credentials) {
433
464
  };
434
465
  }
435
466
  var JsonOpenAICodexAuthStorage = class {
436
- path;
437
- providerId;
467
+ storage;
438
468
  constructor(options = {}) {
439
- this.path = options.path ? expandHome(options.path) : defaultOpenAICodexAuthFilePath();
440
- this.providerId = options.providerId ?? DEFAULT_PROVIDER_ID;
469
+ this.storage = new JsonAuthStorage({
470
+ providerId: options.providerId ?? DEFAULT_PROVIDER_ID,
471
+ isCredentials: isOpenAICodexCredentials,
472
+ ...options.path !== void 0 && { path: options.path }
473
+ });
441
474
  }
442
- async get() {
443
- const file = await this.readFile();
444
- if (!file) return void 0;
445
- if (isOpenAICodexCredentials(file)) return file;
446
- const direct = file[this.providerId];
447
- if (isOpenAICodexCredentials(direct)) return direct;
448
- const providers = file.providers;
449
- if (isObject(providers) && isOpenAICodexCredentials(providers[this.providerId])) return providers[this.providerId];
475
+ get path() {
476
+ return this.storage.path;
450
477
  }
451
- async set(credentials) {
452
- const current = await this.readFile();
453
- const next = isObject(current) && !isOpenAICodexCredentials(current) ? current : {};
454
- next[this.providerId] = credentials;
455
- await this.writeFile(next);
478
+ get providerId() {
479
+ return this.storage.providerId;
456
480
  }
457
- async clear() {
458
- const current = await this.readFile();
459
- if (!current) return;
460
- if (isOpenAICodexCredentials(current)) {
461
- await this.writeFile({});
462
- return;
463
- }
464
- delete current[this.providerId];
465
- const providers = current.providers;
466
- if (isObject(providers)) delete providers[this.providerId];
467
- await this.writeFile(current);
481
+ get() {
482
+ return this.storage.get();
468
483
  }
469
- async readFile() {
470
- const fs = await import("node:fs/promises");
471
- try {
472
- const text = await fs.readFile(this.path, "utf8");
473
- const parsed = JSON.parse(text);
474
- return isObject(parsed) ? parsed : void 0;
475
- } catch (error) {
476
- if (isObject(error) && error.code === "ENOENT") return void 0;
477
- throw error;
478
- }
484
+ set(credentials) {
485
+ return this.storage.set(credentials);
479
486
  }
480
- async writeFile(value) {
481
- const fs = await import("node:fs/promises");
482
- const path = await import("node:path");
483
- await fs.mkdir(path.dirname(this.path), { recursive: true });
484
- const tempPath = `${this.path}.${process.pid}.${Date.now()}.tmp`;
485
- await fs.writeFile(tempPath, `${JSON.stringify(value, null, " ")}\n`, {
486
- encoding: "utf8",
487
- mode: 384
488
- });
489
- await fs.chmod(tempPath, 384);
490
- await fs.rename(tempPath, this.path);
487
+ clear() {
488
+ return this.storage.clear();
491
489
  }
492
490
  };
493
491
  var OpenAICodexOAuthClient = class {
@@ -536,6 +534,6 @@ async function getOpenAICodexApiKey(options = {}) {
536
534
  return new OpenAICodexOAuthClient(options).getApiKey();
537
535
  }
538
536
  //#endregion
539
- export { exchangeOpenAICodexAuthorizationCode as a, openAICodexHeaders as c, createOpenAICodexAuthorizationFlow as i, parseOpenAICodexAuthorizationInput as l, OPENAI_CODEX_API_KEY_ENV as n, getOpenAICodexAccountId as o, OpenAICodexOAuthClient as r, getOpenAICodexApiKey as s, JsonOpenAICodexAuthStorage as t, refreshOpenAICodexToken as u };
537
+ export { createOpenAICodexDeviceAuth as a, getOpenAICodexApiKey as c, pollOpenAICodexDeviceAuth as d, refreshOpenAICodexToken as f, createOpenAICodexAuthorizationFlow as i, openAICodexHeaders as l, OPENAI_CODEX_API_KEY_ENV as n, exchangeOpenAICodexAuthorizationCode as o, OpenAICodexOAuthClient as r, getOpenAICodexAccountId as s, JsonOpenAICodexAuthStorage as t, parseOpenAICodexAuthorizationInput as u };
540
538
 
541
- //# sourceMappingURL=codex-MtP1574D.mjs.map
539
+ //# sourceMappingURL=codex-CLDBTrFm.mjs.map