@codeworksh/aikit 0.3.1 → 0.4.1-dev.20260530133305
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 +116 -152
- package/cli.d.mts +1 -0
- package/cli.mjs +308 -0
- package/cli.mjs.map +1 -0
- package/codex-DfYk740t.mjs +521 -0
- package/codex-DfYk740t.mjs.map +1 -0
- package/index.d.mts +7145 -19
- package/index.mjs +1217 -3120
- package/index.mjs.map +1 -1
- package/oauth/openai/codex.d.mts +107 -0
- package/oauth/openai/codex.mjs +2 -0
- package/package.json +23 -21
- package/registry-CYfzJ6lk.mjs +5136 -0
- package/registry-CYfzJ6lk.mjs.map +1 -0
- package/agent/codemode/drivers/drivers.d.mts +0 -7
- package/agent/codemode/drivers/drivers.mjs +0 -122
- package/agent/codemode/drivers/drivers.mjs.map +0 -1
- package/codemode-SiETGR3z.d.mts +0 -8410
package/README.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
# @codeworksh/aikit
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
|
|
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)
|
|
4
18
|
|
|
5
19
|
## Installation
|
|
6
20
|
|
|
@@ -10,196 +24,146 @@ npm install @codeworksh/aikit
|
|
|
10
24
|
|
|
11
25
|
Node `>=24.14.1` is required.
|
|
12
26
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
- `llm(...)` to resolve a model from the built-in registry
|
|
16
|
-
- `stream(...)` and `stream.complete(...)` for provider-normalized LLM streaming
|
|
17
|
-
- `Agent.create(...)` for a stateful agent instance
|
|
18
|
-
- `agent.loop(...)` and `agent.loopContinue(...)` for lower-level turn orchestration
|
|
19
|
-
- `Message` helpers for typed messages and generated `messageId` values
|
|
27
|
+
## Supported Providers
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
Built on top of the Vercel AI SDK, `@codeworksh/aikit` supports a wide array of providers natively:
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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`)
|
|
26
38
|
|
|
27
39
|
## Quick Start
|
|
28
40
|
|
|
29
41
|
```ts
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
const agent = await Agent.create({
|
|
33
|
-
provider: "anthropic",
|
|
34
|
-
model: "claude-haiku-4-5-20251001",
|
|
35
|
-
getApiKey: async () => process.env.ANTHROPIC_API_KEY,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
agent.setSystemPrompt("Be concise.");
|
|
39
|
-
|
|
40
|
-
await agent.prompt([{ type: "text", text: "Reply with exactly: hello" }]);
|
|
41
|
-
|
|
42
|
-
const lastMessage = agent.state.messages.at(-1);
|
|
43
|
-
console.log(lastMessage?.messageId);
|
|
44
|
-
console.log(lastMessage?.role);
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Stream Events
|
|
48
|
-
|
|
49
|
-
Use `stream(...)` when you want token and part-level events.
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
import { Message, llm, stream } from "@codeworksh/aikit";
|
|
42
|
+
import { llm, stream, Message } from "@codeworksh/aikit";
|
|
53
43
|
|
|
44
|
+
// Resolve a model from the built-in registry
|
|
54
45
|
const model = await llm("anthropic", "claude-haiku-4-5-20251001");
|
|
55
46
|
if (!model) throw new Error("Model not found");
|
|
56
47
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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 });
|
|
70
62
|
|
|
71
63
|
for await (const event of s) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
if (event.type === "text.delta") {
|
|
65
|
+
process.stdout.write(event.delta);
|
|
66
|
+
}
|
|
75
67
|
}
|
|
76
68
|
|
|
77
69
|
const finalMessage = await s.result();
|
|
78
|
-
console.log(finalMessage.messageId);
|
|
70
|
+
console.log(`\nMessage ID: ${finalMessage.messageId}`);
|
|
71
|
+
|
|
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);
|
|
79
75
|
```
|
|
80
76
|
|
|
81
|
-
##
|
|
77
|
+
## Tools
|
|
82
78
|
|
|
83
|
-
|
|
79
|
+
Tools enable LLMs to interact with external systems. `@codeworksh/aikit` provides native support for function calling and schema validation using TypeBox.
|
|
84
80
|
|
|
85
81
|
```ts
|
|
86
|
-
import {
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
82
|
+
import { Type, llm, stream, Message, validateToolArguments } from "@codeworksh/aikit";
|
|
83
|
+
|
|
84
|
+
const calculatorTool = {
|
|
85
|
+
name: "calculator",
|
|
86
|
+
description: "Evaluate arithmetic expressions",
|
|
87
|
+
parameters: Type.Object({
|
|
88
|
+
expression: Type.String(),
|
|
89
|
+
}),
|
|
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
|
+
}
|
|
115
|
+
```
|
|
93
116
|
|
|
94
|
-
|
|
117
|
+
## Providers and Models
|
|
95
118
|
|
|
96
|
-
|
|
97
|
-
if (event.type === "message.end") {
|
|
98
|
-
console.log(event.message.role, event.message.messageId);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
119
|
+
`@codeworksh/aikit` uses a registry to fetch model specifications and metadata.
|
|
101
120
|
|
|
102
|
-
|
|
121
|
+
```ts
|
|
122
|
+
import { llm } from "@codeworksh/aikit";
|
|
103
123
|
|
|
104
|
-
|
|
105
|
-
|
|
124
|
+
// Get all available providers
|
|
125
|
+
const providers = await llm.providers();
|
|
126
|
+
console.log(providers);
|
|
106
127
|
|
|
107
|
-
|
|
128
|
+
// Get all models for a specific provider
|
|
129
|
+
const anthropicModels = await llm.models("anthropic");
|
|
108
130
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
import { Agent } from "@codeworksh/aikit";
|
|
112
|
-
|
|
113
|
-
const calculatorTool = Agent.defineTool({
|
|
114
|
-
name: "calculator",
|
|
115
|
-
label: "Calculator",
|
|
116
|
-
description: "Evaluate arithmetic expressions",
|
|
117
|
-
parameters: Type.Object({
|
|
118
|
-
expression: Type.String(),
|
|
119
|
-
}),
|
|
120
|
-
async execute(_callID, params) {
|
|
121
|
-
return {
|
|
122
|
-
status: "completed",
|
|
123
|
-
result: {
|
|
124
|
-
content: [{ type: "text", text: `result for ${params.expression}` }],
|
|
125
|
-
isError: false,
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const instance = await Agent.create({
|
|
132
|
-
provider: "anthropic",
|
|
133
|
-
model: "claude-haiku-4-5-20251001",
|
|
134
|
-
getApiKey: async () => process.env.ANTHROPIC_API_KEY,
|
|
135
|
-
initialState: {
|
|
136
|
-
tools: [calculatorTool],
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
await instance.prompt([{ type: "text", text: "Use the calculator tool for 25 * 18." }]);
|
|
131
|
+
// Get a specific model directly
|
|
132
|
+
const gpt4 = await llm.model("openai", "gpt-4o");
|
|
141
133
|
```
|
|
142
134
|
|
|
143
|
-
##
|
|
135
|
+
## CLI Tools
|
|
144
136
|
|
|
145
|
-
|
|
137
|
+
`@codeworksh/aikit` ships with a CLI tool for managing local metadata and authentication.
|
|
146
138
|
|
|
147
|
-
|
|
148
|
-
import { Message, agent, llm } from "@codeworksh/aikit";
|
|
139
|
+
You can invoke it using `npx aikit` or `pnpm aikit`.
|
|
149
140
|
|
|
150
|
-
|
|
151
|
-
|
|
141
|
+
### Model Generation
|
|
142
|
+
|
|
143
|
+
Generate a `models.gen.json` file fetching the latest metadata about providers and their available models from `models.dev`.
|
|
152
144
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
model,
|
|
156
|
-
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
157
|
-
convertToLlm: async (messages) => messages,
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
systemPrompt: "Be concise.",
|
|
161
|
-
messages: [],
|
|
162
|
-
tools: [],
|
|
163
|
-
},
|
|
164
|
-
[
|
|
165
|
-
Message.createUserMessage({
|
|
166
|
-
role: "user",
|
|
167
|
-
time: { created: Date.now() },
|
|
168
|
-
parts: [{ type: "text", text: "Reply with exactly: ok" }],
|
|
169
|
-
}),
|
|
170
|
-
],
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
const messages = await run.result();
|
|
174
|
-
console.log(messages.at(-1)?.role);
|
|
145
|
+
```bash
|
|
146
|
+
pnpm aikit modelgen [path]
|
|
175
147
|
```
|
|
176
148
|
|
|
177
|
-
|
|
149
|
+
### OAuth Providers
|
|
178
150
|
|
|
179
|
-
|
|
151
|
+
Manage OAuth credentials for providers that require it, such as OpenAI Codex.
|
|
180
152
|
|
|
181
|
-
|
|
153
|
+
```bash
|
|
154
|
+
# Start an OAuth login flow in your browser
|
|
155
|
+
pnpm aikit auth --openai-codex
|
|
182
156
|
|
|
183
|
-
|
|
184
|
-
|
|
157
|
+
# Check the status of your stored credentials
|
|
158
|
+
pnpm aikit auth --openai-codex --status
|
|
185
159
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
time: { created: Date.now() },
|
|
189
|
-
parts: [{ type: "text", text: "hello" }],
|
|
190
|
-
});
|
|
160
|
+
# Refresh your current credentials
|
|
161
|
+
pnpm aikit auth --openai-codex --refresh
|
|
191
162
|
|
|
192
|
-
|
|
163
|
+
# Clear stored credentials
|
|
164
|
+
pnpm aikit auth --openai-codex --logout
|
|
193
165
|
```
|
|
194
166
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
## Lower-Level APIs
|
|
198
|
-
|
|
199
|
-
- `stream(...)`: raw provider-normalized stream
|
|
200
|
-
- `stream.complete(...)`: final assistant message without manual iteration
|
|
201
|
-
- `Agent.create(...)`: stateful agent instance
|
|
202
|
-
- `agent.loop(...)`: lower-level agent loop
|
|
203
|
-
- `agent.loopContinue(...)`: continue from an existing context
|
|
167
|
+
## Contribute
|
|
204
168
|
|
|
205
|
-
|
|
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
|