@codeworksh/aikit 0.4.0 → 0.4.1-dev.20260530133433

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -137
  3. package/cli.d.mts +1 -0
  4. package/cli.mjs +308 -0
  5. package/cli.mjs.map +1 -0
  6. package/codex-DfYk740t.mjs +521 -0
  7. package/codex-DfYk740t.mjs.map +1 -0
  8. package/index.d.mts +7197 -0
  9. package/index.mjs +1478 -0
  10. package/index.mjs.map +1 -0
  11. package/oauth/openai/codex.d.mts +107 -0
  12. package/oauth/openai/codex.mjs +2 -0
  13. package/package.json +28 -33
  14. package/{dist/pack/index.mjs → registry-CYfzJ6lk.mjs} +210 -3300
  15. package/registry-CYfzJ6lk.mjs.map +1 -0
  16. package/dist/pack/agent/codemode/drivers/drivers.d.mts +0 -7
  17. package/dist/pack/agent/codemode/drivers/drivers.mjs +0 -122
  18. package/dist/pack/agent/codemode/drivers/drivers.mjs.map +0 -1
  19. package/dist/pack/codemode-BEDXG7WN.d.mts +0 -8736
  20. package/dist/pack/index.d.mts +0 -71
  21. package/dist/pack/index.mjs.map +0 -1
  22. package/src/agent/agent.ts +0 -790
  23. package/src/agent/codemode/codemode.ts +0 -561
  24. package/src/agent/codemode/drivers/drivers.ts +0 -1
  25. package/src/agent/codemode/drivers/quickjs-wasi-driver.ts +0 -149
  26. package/src/agent/codemode/index.ts +0 -1
  27. package/src/agent/loop.ts +0 -903
  28. package/src/agent.ts +0 -11
  29. package/src/event/event.ts +0 -277
  30. package/src/index.ts +0 -6
  31. package/src/llm.ts +0 -31
  32. package/src/message/message.ts +0 -311
  33. package/src/model/catalog.ts +0 -94
  34. package/src/model/model.ts +0 -250
  35. package/src/model/transform.ts +0 -59
  36. package/src/provider/provider.ts +0 -60
  37. package/src/provider/providers/anthropic/index.ts +0 -792
  38. package/src/provider/providers/github/github.ts +0 -34
  39. package/src/provider/providers/openai/completions.ts +0 -953
  40. package/src/provider/providers/utils.ts +0 -7
  41. package/src/provider/register.ts +0 -6
  42. package/src/provider/stream.ts +0 -278
  43. package/src/stream.ts +0 -72
  44. package/src/utils/eventstream.ts +0 -95
  45. package/src/utils/helpers.ts +0 -24
  46. package/src/utils/jsonparse.ts +0 -32
  47. package/src/utils/overflow.ts +0 -121
  48. package/src/utils/sanitize.ts +0 -25
  49. package/src/utils/validation.ts +0 -87
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sanchit Kudari
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,8 +1,20 @@
1
1
  # @codeworksh/aikit
2
2
 
3
- @codeworksh/aikit is **TypeScript SDK** for building AI agents and agentic harness. Provides a unified API for working with multiple LLM providers and support for tool calling(function calling) required for agentic workflows.
3
+ @codeworksh/aikit is a **TypeScript SDK** for building AI agents and agentic harnesses. It provides a unified API for working with multiple LLM providers, built on top of the Vercel AI SDK, with full support for tool calling (function calling) required for agentic workflows.
4
4
 
5
- @codeworksh/aikit isn't just another SDK. It gives you the basic primitives for streaming LLM responses without the extra bloat, letting you handle the agentic orchestration yourself.
5
+ It gives you the basic primitives for streaming LLM responses without the extra bloat, letting you handle the orchestration yourself.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Installation](#installation)
10
+ - [Supported Providers](#supported-providers)
11
+ - [Quick Start](#quick-start)
12
+ - [Tools](#tools)
13
+ - [Providers and Models](#providers-and-models)
14
+ - [CLI Tools](#cli-tools)
15
+ - [Model Generation](#model-generation)
16
+ - [OAuth Providers](#oauth-providers)
17
+ - [Contribute](#contribute)
6
18
 
7
19
  ## Installation
8
20
 
@@ -12,57 +24,41 @@ npm install @codeworksh/aikit
12
24
 
13
25
  Node `>=24.14.1` is required.
14
26
 
15
- ## API Overview
16
-
17
- - `llm(...)` to resolve a model from the built-in registry
18
- - `stream(...)` and `stream.complete(...)` for provider-normalized LLM streaming
19
- - `Agent.create(...)` for a stateful agent instance
20
- - `agent.loop(...)` and `agent.loopContinue(...)` for lower-level turn orchestration
21
- - `Message` helpers for typed messages and generated `messageId` values
22
-
23
- ## Quick Start
24
-
25
- ```ts
26
- import { Agent } from "@codeworksh/aikit";
27
-
28
- const agent = await Agent.create({
29
- provider: "anthropic",
30
- model: "claude-haiku-4-5-20251001",
31
- getApiKey: async () => process.env.ANTHROPIC_API_KEY,
32
- });
33
-
34
- agent.setSystemPrompt("Be concise.");
27
+ ## Supported Providers
35
28
 
36
- await agent.prompt([{ type: "text", text: "Reply with exactly: hello" }]);
37
-
38
- const lastMessage = agent.state.messages.at(-1);
39
- console.log(lastMessage?.messageId);
40
- console.log(lastMessage?.role);
41
- ```
29
+ Built on top of the Vercel AI SDK, `@codeworksh/aikit` supports a wide array of providers natively:
42
30
 
43
- ## Stream Events
31
+ - **Anthropic** (`@ai-sdk/anthropic`)
32
+ - **OpenAI** (`@ai-sdk/openai`)
33
+ - **Google** (`@ai-sdk/google`)
34
+ - **Google Vertex AI** (`@ai-sdk/google-vertex`)
35
+ - **xAI** (`@ai-sdk/xai`)
36
+ - **OpenRouter** (`@openrouter/ai-sdk-provider`)
37
+ - **OpenAI Compatible APIs** (`@ai-sdk/openai-compatible`)
44
38
 
45
- Use `stream(...)` when you want token and part-level events.
39
+ ## Quick Start
46
40
 
47
41
  ```ts
48
- import { Message, llm, stream } from "@codeworksh/aikit";
42
+ import { llm, stream, Message } from "@codeworksh/aikit";
49
43
 
44
+ // Resolve a model from the built-in registry
50
45
  const model = await llm("anthropic", "claude-haiku-4-5-20251001");
51
46
  if (!model) throw new Error("Model not found");
52
47
 
53
- const s = stream(
54
- model,
55
- {
56
- messages: [
57
- Message.createUserMessage({
58
- role: "user",
59
- time: { created: Date.now() },
60
- parts: [{ type: "text", text: "Count from 1 to 3" }],
61
- }),
62
- ],
63
- },
64
- { apiKey: process.env.ANTHROPIC_API_KEY },
65
- );
48
+ // Setup conversation context
49
+ const context: Message.Context = {
50
+ systemPrompt: "You are a helpful coding assistant.",
51
+ messages: [
52
+ Message.createUserMessage({
53
+ role: "user",
54
+ time: { created: Date.now() },
55
+ parts: [{ type: "text", text: "Write a simple loop in TypeScript." }],
56
+ }),
57
+ ],
58
+ };
59
+
60
+ // Option 1: Stream events
61
+ const s = stream(model, context, { apiKey: process.env.ANTHROPIC_API_KEY });
66
62
 
67
63
  for await (const event of s) {
68
64
  if (event.type === "text.delta") {
@@ -71,127 +67,103 @@ for await (const event of s) {
71
67
  }
72
68
 
73
69
  const finalMessage = await s.result();
74
- console.log(finalMessage.messageId);
75
- ```
76
-
77
- ## Agent Instance
78
-
79
- `Agent.create(...)` is the simplest way to work with a stateful agent.
80
-
81
- ```ts
82
- import { Agent } from "@codeworksh/aikit";
83
-
84
- const agent = await Agent.create({
85
- provider: "anthropic",
86
- model: "claude-haiku-4-5-20251001",
87
- getApiKey: async () => process.env.ANTHROPIC_API_KEY,
88
- });
89
-
90
- agent.setSystemPrompt("Be concise.");
91
-
92
- agent.subscribe((event) => {
93
- if (event.type === "message.end") {
94
- console.log(event.message.role, event.message.messageId);
95
- }
96
- });
70
+ console.log(`\nMessage ID: ${finalMessage.messageId}`);
97
71
 
98
- await agent.prompt([{ type: "text", text: "Say hello in one line." }]);
99
-
100
- console.log(agent.state.messages);
72
+ // Option 2: Get complete response without streaming
73
+ const message = await stream.complete(model, context, { apiKey: process.env.ANTHROPIC_API_KEY });
74
+ console.log(message.parts);
101
75
  ```
102
76
 
103
- ## With Tools
77
+ ## Tools
78
+
79
+ Tools enable LLMs to interact with external systems. `@codeworksh/aikit` provides native support for function calling and schema validation using TypeBox.
104
80
 
105
81
  ```ts
106
- import { Type } from "@sinclair/typebox";
107
- import { Agent } from "@codeworksh/aikit";
82
+ import { Type, llm, stream, Message, validateToolArguments } from "@codeworksh/aikit";
108
83
 
109
- const calculatorTool = Agent.defineTool({
84
+ const calculatorTool = {
110
85
  name: "calculator",
111
- label: "Calculator",
112
86
  description: "Evaluate arithmetic expressions",
113
87
  parameters: Type.Object({
114
88
  expression: Type.String(),
115
89
  }),
116
- async execute(_callID, params) {
117
- return {
118
- status: "completed",
119
- result: {
120
- content: [{ type: "text", text: `result for ${params.expression}` }],
121
- isError: false,
122
- },
123
- };
124
- },
125
- });
126
-
127
- const instance = await Agent.create({
128
- provider: "anthropic",
129
- model: "claude-haiku-4-5-20251001",
130
- getApiKey: async () => process.env.ANTHROPIC_API_KEY,
131
- initialState: {
132
- tools: [calculatorTool],
133
- },
134
- });
135
-
136
- await instance.prompt([
137
- { type: "text", text: "Use the calculator tool for 25 * 18." },
138
- ]);
90
+ };
91
+
92
+ const context: Message.Context = {
93
+ messages: [
94
+ Message.createUserMessage({
95
+ role: "user",
96
+ time: { created: Date.now() },
97
+ parts: [{ type: "text", text: "What is 25 * 18?" }],
98
+ }),
99
+ ],
100
+ tools: [calculatorTool],
101
+ };
102
+
103
+ const response = await stream.complete(model, context, { apiKey: process.env.ANTHROPIC_API_KEY });
104
+
105
+ // Check for tool calls in the response
106
+ for (const part of response.parts) {
107
+ if (part.type === "toolCall") {
108
+ console.log(`Executing tool: ${part.name}`);
109
+
110
+ // Validates arguments against TypeBox schema automatically
111
+ const args = validateToolArguments(calculatorTool.parameters, part.arguments);
112
+ // Execute your tool logic here...
113
+ }
114
+ }
139
115
  ```
140
116
 
141
- ## Lower-Level Loop
117
+ ## Providers and Models
142
118
 
143
- Use `agent.loop(...)` when you want loop execution without creating a stateful instance.
119
+ `@codeworksh/aikit` uses a registry to fetch model specifications and metadata.
144
120
 
145
121
  ```ts
146
- import { Message, agent, llm } from "@codeworksh/aikit";
122
+ import { llm } from "@codeworksh/aikit";
147
123
 
148
- const model = await llm("anthropic", "claude-haiku-4-5-20251001");
149
- if (!model) throw new Error("Model not found");
124
+ // Get all available providers
125
+ const providers = await llm.providers();
126
+ console.log(providers);
150
127
 
151
- const run = agent.loop(
152
- {
153
- model,
154
- apiKey: process.env.ANTHROPIC_API_KEY,
155
- convertToLlm: async (messages) => messages,
156
- },
157
- {
158
- systemPrompt: "Be concise.",
159
- messages: [],
160
- tools: [],
161
- },
162
- [
163
- Message.createUserMessage({
164
- role: "user",
165
- time: { created: Date.now() },
166
- parts: [{ type: "text", text: "Reply with exactly: ok" }],
167
- }),
168
- ],
169
- );
128
+ // Get all models for a specific provider
129
+ const anthropicModels = await llm.models("anthropic");
170
130
 
171
- const messages = await run.result();
172
- console.log(messages.at(-1)?.role);
131
+ // Get a specific model directly
132
+ const gpt4 = await llm.model("openai", "gpt-4o");
173
133
  ```
174
134
 
175
- ## Message IDs
135
+ ## CLI Tools
176
136
 
177
- Every user and assistant message has a `messageId`.
137
+ `@codeworksh/aikit` ships with a CLI tool for managing local metadata and authentication.
178
138
 
179
- Use the helpers when constructing messages yourself:
139
+ You can invoke it using `npx aikit` or `pnpm aikit`.
180
140
 
181
- ```ts
182
- import { Message } from "@codeworksh/aikit";
141
+ ### Model Generation
183
142
 
184
- const userMessage = Message.createUserMessage({
185
- role: "user",
186
- time: { created: Date.now() },
187
- parts: [{ type: "text", text: "hello" }],
188
- });
143
+ Generate a `models.gen.json` file fetching the latest metadata about providers and their available models from `models.dev`.
189
144
 
190
- console.log(userMessage.messageId);
145
+ ```bash
146
+ pnpm aikit modelgen [path]
191
147
  ```
192
148
 
193
- This is useful for persistence layers like SQLite, event reconciliation, and updating stored messages by identity instead of array position.
149
+ ### OAuth Providers
150
+
151
+ Manage OAuth credentials for providers that require it, such as OpenAI Codex.
152
+
153
+ ```bash
154
+ # Start an OAuth login flow in your browser
155
+ pnpm aikit auth --openai-codex
156
+
157
+ # Check the status of your stored credentials
158
+ pnpm aikit auth --openai-codex --status
159
+
160
+ # Refresh your current credentials
161
+ pnpm aikit auth --openai-codex --refresh
162
+
163
+ # Clear stored credentials
164
+ pnpm aikit auth --openai-codex --logout
165
+ ```
194
166
 
195
167
  ## Contribute
196
168
 
197
- @codeworksh/aikit is open to community contribution. Please ensure you submit an issue before submitting a pull request. The @codeworksh/aikit project prefers open community discussion before accepting new features.
169
+ `@codeworksh/aikit` is open to community contribution. Please ensure you submit an issue before submitting a pull request. The project prefers open community discussion before accepting new features.
package/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/cli.mjs ADDED
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+ import { a as protocolForPackage, c as ModelCatalog, l as lazy, n as isAISDKPackage, s as Model, u as Filesystem } from "./registry-CYfzJ6lk.mjs";
3
+ import { c as openAICodexHeaders, n as OpenAICodexOAuthClient, t as JsonOpenAICodexAuthStorage } from "./codex-DfYk740t.mjs";
4
+ import { resolve } from "node:path";
5
+ import yargs from "yargs";
6
+ import { hideBin } from "yargs/helpers";
7
+ import { mapValues, pickBy, pipe } from "remeda";
8
+ //#region package.json
9
+ var version = "0.4.0";
10
+ //#endregion
11
+ //#region src/cli/modelgen.ts
12
+ const DEFAULT_PROVIDER_BASE_URLS = {
13
+ [Model.KnownProviderEnum.anthropic]: "https://api.anthropic.com/v1",
14
+ [Model.KnownProviderEnum.openai]: "https://api.openai.com/v1",
15
+ [Model.KnownProviderEnum.openrouter]: "https://openrouter.ai/api/v1",
16
+ [Model.KnownProviderEnum.xai]: "https://api.x.ai/v1"
17
+ };
18
+ function modelsDevURL() {
19
+ return process.env.OPENCODE_MODELS_URL || "https://models.dev";
20
+ }
21
+ function modelsDevPath() {
22
+ return process.env.CODEWORK_MODELS_DEV;
23
+ }
24
+ const modelsDevData = lazy(async () => {
25
+ const path = modelsDevPath();
26
+ if (path) {
27
+ const result = await Filesystem.readJson(path).catch(() => void 0);
28
+ if (result) return result;
29
+ }
30
+ const json = await fetch(`${modelsDevURL()}/api.json`).then((x) => x.text());
31
+ return JSON.parse(json);
32
+ });
33
+ async function pullModelsDevData() {
34
+ return modelsDevData();
35
+ }
36
+ function toModelValue(providerId, provider, model) {
37
+ return applyModification(providerId, provider, model);
38
+ }
39
+ async function loadBuiltInFromModelsDev() {
40
+ return pipe(await pullModelsDevData(), pickBy((provider) => Object.values(provider.models).some((model) => Boolean(model.tool_call) && isAISDKPackage(resolveModelNpm(provider, model)))), mapValues((provider, providerId) => pipe(provider.models, pickBy((model) => Boolean(model.tool_call) && isAISDKPackage(resolveModelNpm(provider, model))), mapValues((model) => toModelValue(providerId, provider, model)), pickBy((model) => model !== void 0))));
41
+ }
42
+ function resolveModelNpm(provider, model) {
43
+ return model.provider?.npm ?? provider.npm ?? "@ai-sdk/openai-compatible";
44
+ }
45
+ function resolveModelBaseUrl(provider, model) {
46
+ const protocol = protocolForPackage(resolveModelNpm(provider, model));
47
+ return model.baseUrl ?? model.provider?.api ?? provider.api ?? DEFAULT_PROVIDER_BASE_URLS[protocol] ?? "";
48
+ }
49
+ function resolveModelAPIMethod(npm) {
50
+ const protocol = protocolForPackage(npm);
51
+ if (protocol === Model.KnownProviderEnum.openai || protocol === Model.KnownProviderEnum.xai) return Model.APIMethodEnum.responses;
52
+ return Model.APIMethodEnum.languageModel;
53
+ }
54
+ function resolveProviderOptionsKey(protocol) {
55
+ if (protocol === Model.KnownProviderEnum.openaiCompatible) return "openai-compatible";
56
+ if (protocol === Model.KnownProviderEnum.googleVertex) return "google-vertex";
57
+ if (protocol === Model.KnownProviderEnum.googleVertexAnthropic) return "google-vertex-anthropic";
58
+ return protocol;
59
+ }
60
+ const OPENAI_RESPONSES_NONE_REASONING_MODELS = new Set([
61
+ "gpt-5.1",
62
+ "gpt-5.2",
63
+ "gpt-5.3-codex",
64
+ "gpt-5.4",
65
+ "gpt-5.4-mini",
66
+ "gpt-5.4-nano",
67
+ "gpt-5.5"
68
+ ]);
69
+ function supportsOpenAiXhigh(modelId) {
70
+ return modelId.includes("gpt-5.2") || modelId.includes("gpt-5.3") || modelId.includes("gpt-5.4") || modelId.includes("gpt-5.5");
71
+ }
72
+ function mergeThinkingLevelMap(model, map) {
73
+ model.thinkingLevelMap = {
74
+ ...model.thinkingLevelMap,
75
+ ...map
76
+ };
77
+ }
78
+ function applyThinkingLevelMetadata(model) {
79
+ if (model.protocol === Model.KnownProviderEnum.openai && model.id.startsWith("gpt-5")) mergeThinkingLevelMap(model, { off: null });
80
+ if (model.protocol === Model.KnownProviderEnum.openai && model.provider.id === "openai" && OPENAI_RESPONSES_NONE_REASONING_MODELS.has(model.id)) mergeThinkingLevelMap(model, { off: "none" });
81
+ if (supportsOpenAiXhigh(model.id)) mergeThinkingLevelMap(model, { xhigh: "xhigh" });
82
+ if (model.id.includes("opus-4-6") || model.id.includes("opus-4.6")) mergeThinkingLevelMap(model, { xhigh: "max" });
83
+ if (model.id.includes("opus-4-7") || model.id.includes("opus-4.7")) mergeThinkingLevelMap(model, { xhigh: "xhigh" });
84
+ }
85
+ async function generateModels(args = {}) {
86
+ const path = resolve(args.path ?? ModelCatalog.path());
87
+ const models = await loadBuiltInFromModelsDev();
88
+ await Filesystem.writeJson(path, models);
89
+ return path;
90
+ }
91
+ const ModelgenCommand = {
92
+ command: "modelgen [path]",
93
+ describe: "generate models.gen.json",
94
+ builder: (yargs) => yargs.positional("path", {
95
+ type: "string",
96
+ describe: "output path (defaults to CODEWORK_MODELGEN_PATH or ./models.gen.json)"
97
+ }),
98
+ handler: async (args) => {
99
+ const path = await generateModels(args);
100
+ console.log(`Generated model catalog at ${path}`);
101
+ }
102
+ };
103
+ function applyModification(providerId, provider, model) {
104
+ const npm = resolveModelNpm(provider, model);
105
+ if (!isAISDKPackage(npm)) return;
106
+ const protocol = protocolForPackage(npm);
107
+ const baseUrl = resolveModelBaseUrl(provider, model);
108
+ const providerInfo = {
109
+ id: providerId,
110
+ name: provider.name,
111
+ source: "api",
112
+ env: provider.env
113
+ };
114
+ if (provider.key) providerInfo.key = provider.key;
115
+ const api = {
116
+ id: model.id,
117
+ method: resolveModelAPIMethod(npm)
118
+ };
119
+ if (baseUrl) api.url = baseUrl;
120
+ const info = {
121
+ id: model.id,
122
+ name: model.name,
123
+ provider: providerInfo,
124
+ baseUrl,
125
+ reasoning: Boolean(model.reasoning),
126
+ input: Model.normalizeInput(model.modalities.input),
127
+ cost: {
128
+ input: model.cost?.input ?? 0,
129
+ output: model.cost?.output ?? 0,
130
+ cacheRead: model.cost?.cache_read ?? 0,
131
+ cacheWrite: model.cost?.cache_write ?? 0
132
+ },
133
+ contextWindow: model.limit?.context ?? 4096,
134
+ maxTokens: model.limit?.output ?? 4096,
135
+ headers: {
136
+ ...provider.headers,
137
+ ...model.provider?.headers,
138
+ ...model.headers
139
+ },
140
+ npm,
141
+ api,
142
+ providerOptionsKey: resolveProviderOptionsKey(protocol),
143
+ protocol
144
+ };
145
+ if (Object.keys(info.headers ?? {}).length === 0) delete info.headers;
146
+ applyThinkingLevelMetadata(info);
147
+ return info;
148
+ }
149
+ //#endregion
150
+ //#region src/cli/oauth.ts
151
+ async function promptLine(message) {
152
+ const rl = (await import("node:readline/promises")).createInterface({
153
+ input: process.stdin,
154
+ output: process.stdout
155
+ });
156
+ try {
157
+ return await rl.question(`${message} `);
158
+ } finally {
159
+ rl.close();
160
+ }
161
+ }
162
+ async function openBrowser(url) {
163
+ const { spawn } = await import("node:child_process");
164
+ const command = process.platform === "darwin" ? {
165
+ file: "open",
166
+ args: [url]
167
+ } : process.platform === "win32" ? {
168
+ file: "cmd",
169
+ args: [
170
+ "/c",
171
+ "start",
172
+ "",
173
+ url
174
+ ]
175
+ } : {
176
+ file: "xdg-open",
177
+ args: [url]
178
+ };
179
+ spawn(command.file, command.args, {
180
+ detached: true,
181
+ stdio: "ignore"
182
+ }).unref();
183
+ }
184
+ function printCredentials(credentials, options) {
185
+ if (options.json) {
186
+ console.log(JSON.stringify({
187
+ accountId: credentials.accountId,
188
+ expires: credentials.expires,
189
+ expiresAt: new Date(credentials.expires).toISOString(),
190
+ headers: options.printHeaders ? openAICodexHeaders(credentials) : void 0
191
+ }, null, 2));
192
+ return;
193
+ }
194
+ console.log(`Account: ${credentials.accountId}`);
195
+ console.log(`Expires: ${new Date(credentials.expires).toLocaleString()}`);
196
+ if (options.printHeaders) {
197
+ console.log("Headers:");
198
+ for (const [name, value] of Object.entries(openAICodexHeaders(credentials))) console.log(`${name}: ${value}`);
199
+ }
200
+ }
201
+ async function runOpenAICodexAuth(args) {
202
+ const storage = new JsonOpenAICodexAuthStorage({ path: args.authFile });
203
+ const client = new OpenAICodexOAuthClient({ storage });
204
+ if (args.logout) {
205
+ await client.logout();
206
+ console.log(`Cleared OpenAI Codex credentials from ${storage.path}`);
207
+ return;
208
+ }
209
+ if (args.status) {
210
+ const credentials = await storage.get();
211
+ if (!credentials) {
212
+ console.log(`No OpenAI Codex credentials found at ${storage.path}`);
213
+ process.exitCode = 1;
214
+ return;
215
+ }
216
+ printCredentials(credentials, args);
217
+ return;
218
+ }
219
+ if (args.refresh) {
220
+ const credentials = await client.getCredentials();
221
+ if (!credentials) {
222
+ console.log(`No OpenAI Codex credentials found at ${storage.path}`);
223
+ process.exitCode = 1;
224
+ return;
225
+ }
226
+ console.log(`Refreshed OpenAI Codex credentials in ${storage.path}`);
227
+ printCredentials(credentials, args);
228
+ return;
229
+ }
230
+ const credentials = await client.login({
231
+ originator: args.originator,
232
+ onAuth: (info) => {
233
+ console.log(info.instructions ?? "Complete OpenAI Codex authentication in your browser.");
234
+ console.log(info.url);
235
+ if (args.browser !== false) openBrowser(info.url).catch((error) => {
236
+ console.warn(`Failed to open browser: ${error instanceof Error ? error.message : String(error)}`);
237
+ });
238
+ },
239
+ onPrompt: async (prompt) => promptLine(prompt.message),
240
+ onManualCodeInput: args.manual ? async () => promptLine("Paste the redirect URL or authorization code, or wait for browser callback:") : void 0
241
+ });
242
+ console.log(`Saved OpenAI Codex credentials to ${storage.path}`);
243
+ printCredentials(credentials, args);
244
+ }
245
+ //#endregion
246
+ //#region src/cli.ts
247
+ const cli = yargs(hideBin(process.argv)).scriptName("aikit").wrap(100).help("help", "show help").alias("help", "h").version("version", "show version number", version).alias("version", "v").command(ModelgenCommand).command({
248
+ command: "auth",
249
+ describe: "manage OAuth credentials",
250
+ builder: (yargs) => yargs.option("openai-codex", {
251
+ type: "boolean",
252
+ describe: "use OpenAI Codex OAuth"
253
+ }).option("auth-file", {
254
+ type: "string",
255
+ describe: "path to auth.json (defaults to CODEWORK_CREDENTIALS or ~/.codework/aikit/auth.json)"
256
+ }).option("browser", {
257
+ type: "boolean",
258
+ default: true,
259
+ describe: "open the authorization URL in the default browser"
260
+ }).option("manual", {
261
+ type: "boolean",
262
+ default: false,
263
+ describe: "also prompt for a pasted redirect URL or authorization code"
264
+ }).option("originator", {
265
+ type: "string",
266
+ default: "codework",
267
+ describe: "OAuth originator value"
268
+ }).option("status", {
269
+ type: "boolean",
270
+ describe: "show stored credential status without refreshing"
271
+ }).option("refresh", {
272
+ type: "boolean",
273
+ describe: "refresh stored credentials if expired"
274
+ }).option("logout", {
275
+ type: "boolean",
276
+ describe: "clear stored credentials"
277
+ }).option("json", {
278
+ type: "boolean",
279
+ describe: "print machine-readable output"
280
+ }).option("print-headers", {
281
+ type: "boolean",
282
+ describe: "print request headers for Codex API calls"
283
+ }).check((args) => {
284
+ if (!args.openaiCodex) throw new Error("Choose an auth provider, for example: auth --openai-codex");
285
+ if ([
286
+ args.status,
287
+ args.refresh,
288
+ args.logout
289
+ ].filter(Boolean).length > 1) throw new Error("Choose only one of --status, --refresh, or --logout");
290
+ return true;
291
+ }),
292
+ handler: async (args) => {
293
+ await runOpenAICodexAuth(args);
294
+ }
295
+ }).demandCommand(1, "Choose a command").strict().fail((message, error) => {
296
+ if (error) throw error;
297
+ if (message) throw new Error(message);
298
+ });
299
+ try {
300
+ await cli.parse();
301
+ } catch (error) {
302
+ process.exitCode = 1;
303
+ console.error(error instanceof Error ? error.message : String(error));
304
+ }
305
+ //#endregion
306
+ export {};
307
+
308
+ //# sourceMappingURL=cli.mjs.map