openclaw-aicodewith-auth 0.2.6 → 0.3.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.
- package/README.md +6 -36
- package/index.ts +106 -20
- package/lib/models/index.ts +14 -0
- package/lib/models/registry.ts +456 -0
- package/package.json +9 -1
- package/src/auth.ts +10 -15
- package/src/constants.ts +7 -12
- package/src/models.ts +0 -55
package/README.md
CHANGED
|
@@ -46,53 +46,23 @@ You will be prompted to enter your AICodewith API key.
|
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
# Use Claude Opus 4.5 (default after auth)
|
|
49
|
-
openclaw agent --
|
|
49
|
+
openclaw agent --message "Hello"
|
|
50
|
+
|
|
51
|
+
# Use a specific model
|
|
52
|
+
openclaw agent --model aicodewith-gpt/gpt-5.2-codex --message "Hello"
|
|
50
53
|
|
|
51
54
|
# List available models
|
|
52
55
|
openclaw models list | grep aicodewith
|
|
53
56
|
```
|
|
54
57
|
|
|
55
|
-
## Environment
|
|
58
|
+
## Environment Variable
|
|
56
59
|
|
|
57
|
-
You can set the API key via environment variable:
|
|
60
|
+
You can also set the API key via environment variable:
|
|
58
61
|
|
|
59
62
|
```bash
|
|
60
63
|
export AICODEWITH_API_KEY=your-api-key
|
|
61
64
|
```
|
|
62
65
|
|
|
63
|
-
### Local Testing / Development
|
|
64
|
-
|
|
65
|
-
Override base URLs and API type for local testing:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
# Override GPT base URL (e.g., local proxy)
|
|
69
|
-
export AICODEWITH_GPT_BASE_URL=http://localhost:3000/v1
|
|
70
|
-
|
|
71
|
-
# Override Claude base URL
|
|
72
|
-
export AICODEWITH_CLAUDE_BASE_URL=http://localhost:3000/v1
|
|
73
|
-
|
|
74
|
-
# Override Gemini base URL
|
|
75
|
-
export AICODEWITH_GEMINI_BASE_URL=http://localhost:3000/gemini_cli
|
|
76
|
-
|
|
77
|
-
# Switch GPT API type: "openai-responses" (default) or "openai-completions"
|
|
78
|
-
export AICODEWITH_GPT_API=openai-responses
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Local Plugin Development
|
|
82
|
-
|
|
83
|
-
Link the plugin for live editing:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
# From openclaw repo root
|
|
87
|
-
openclaw plugins install --link ../openclaw-aicodewith-auth
|
|
88
|
-
|
|
89
|
-
# Enable the plugin
|
|
90
|
-
openclaw plugins enable openclaw-aicodewith-auth
|
|
91
|
-
|
|
92
|
-
# Restart gateway to pick up changes
|
|
93
|
-
openclaw gateway restart
|
|
94
|
-
```
|
|
95
|
-
|
|
96
66
|
## License
|
|
97
67
|
|
|
98
68
|
MIT
|
package/index.ts
CHANGED
|
@@ -6,14 +6,102 @@ import {
|
|
|
6
6
|
PROVIDER_ID_GPT,
|
|
7
7
|
PROVIDER_ID_CLAUDE,
|
|
8
8
|
PROVIDER_ID_GEMINI,
|
|
9
|
-
AICODEWITH_GPT_BASE_URL,
|
|
10
|
-
AICODEWITH_CLAUDE_BASE_URL,
|
|
11
|
-
AICODEWITH_GEMINI_BASE_URL,
|
|
12
9
|
AICODEWITH_API_KEY_ENV,
|
|
13
|
-
AICODEWITH_GPT_API,
|
|
14
10
|
} from "./src/constants.js";
|
|
15
|
-
import {
|
|
11
|
+
import { buildProviderConfigs, buildModelMigrations } from "./lib/models/index.js";
|
|
16
12
|
import { createAicodewithAuthMethod } from "./src/auth.js";
|
|
13
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
|
|
17
|
+
function migrateConfigModels(): void {
|
|
18
|
+
const migrations = buildModelMigrations();
|
|
19
|
+
if (Object.keys(migrations).length === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const configDir = join(homedir(), ".openclaw");
|
|
24
|
+
const configPath = join(configDir, "openclaw.json");
|
|
25
|
+
|
|
26
|
+
if (!existsSync(configPath)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let config: Record<string, unknown>;
|
|
31
|
+
try {
|
|
32
|
+
const content = readFileSync(configPath, "utf-8");
|
|
33
|
+
config = JSON.parse(content);
|
|
34
|
+
} catch {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let changed = false;
|
|
39
|
+
|
|
40
|
+
const migrateModelField = (modelConfig: unknown): boolean => {
|
|
41
|
+
if (!modelConfig || typeof modelConfig !== "object") return false;
|
|
42
|
+
const model = modelConfig as Record<string, unknown>;
|
|
43
|
+
let fieldChanged = false;
|
|
44
|
+
|
|
45
|
+
if (typeof model.primary === "string" && migrations[model.primary]) {
|
|
46
|
+
console.log(`[${PLUGIN_ID}] Migrating primary: ${model.primary} -> ${migrations[model.primary]}`);
|
|
47
|
+
model.primary = migrations[model.primary];
|
|
48
|
+
fieldChanged = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (Array.isArray(model.fallbacks)) {
|
|
52
|
+
model.fallbacks = model.fallbacks.map((fb: unknown) => {
|
|
53
|
+
if (typeof fb === "string" && migrations[fb]) {
|
|
54
|
+
console.log(`[${PLUGIN_ID}] Migrating fallback: ${fb} -> ${migrations[fb]}`);
|
|
55
|
+
fieldChanged = true;
|
|
56
|
+
return migrations[fb];
|
|
57
|
+
}
|
|
58
|
+
return fb;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return fieldChanged;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const agents = config.agents as Record<string, unknown> | undefined;
|
|
66
|
+
if (agents) {
|
|
67
|
+
const defaults = agents.defaults as Record<string, unknown> | undefined;
|
|
68
|
+
if (defaults) {
|
|
69
|
+
if (defaults.model && migrateModelField(defaults.model)) {
|
|
70
|
+
changed = true;
|
|
71
|
+
}
|
|
72
|
+
if (defaults.imageModel && migrateModelField(defaults.imageModel)) {
|
|
73
|
+
changed = true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const agentList = agents.list;
|
|
78
|
+
if (Array.isArray(agentList)) {
|
|
79
|
+
for (const agentConfig of agentList) {
|
|
80
|
+
if (agentConfig && typeof agentConfig === "object") {
|
|
81
|
+
const agent = agentConfig as Record<string, unknown>;
|
|
82
|
+
if (agent.model && migrateModelField(agent.model)) {
|
|
83
|
+
changed = true;
|
|
84
|
+
}
|
|
85
|
+
if (agent.imageModel && migrateModelField(agent.imageModel)) {
|
|
86
|
+
changed = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (changed) {
|
|
94
|
+
try {
|
|
95
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
96
|
+
console.log(`[${PLUGIN_ID}] Migrated deprecated model IDs in config`);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.warn(
|
|
99
|
+
`[${PLUGIN_ID}] Failed to write migrated config:`,
|
|
100
|
+
error instanceof Error ? error.message : error
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
17
105
|
|
|
18
106
|
const aicodewithPlugin = {
|
|
19
107
|
id: PLUGIN_ID,
|
|
@@ -41,17 +129,23 @@ const aicodewithPlugin = {
|
|
|
41
129
|
auth: Array<ReturnType<typeof createAicodewithAuthMethod>>;
|
|
42
130
|
}) => void;
|
|
43
131
|
}) {
|
|
132
|
+
try {
|
|
133
|
+
migrateConfigModels();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.warn(
|
|
136
|
+
`[${PLUGIN_ID}] Config migration failed:`,
|
|
137
|
+
error instanceof Error ? error.message : error
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
44
141
|
const authMethod = createAicodewithAuthMethod();
|
|
142
|
+
const providerConfigs = buildProviderConfigs();
|
|
45
143
|
|
|
46
144
|
api.registerProvider({
|
|
47
145
|
id: PROVIDER_ID_GPT,
|
|
48
146
|
label: "AICodewith GPT",
|
|
49
147
|
envVars: [AICODEWITH_API_KEY_ENV],
|
|
50
|
-
models:
|
|
51
|
-
baseUrl: AICODEWITH_GPT_BASE_URL,
|
|
52
|
-
api: AICODEWITH_GPT_API,
|
|
53
|
-
models: GPT_MODELS,
|
|
54
|
-
},
|
|
148
|
+
models: providerConfigs[PROVIDER_ID_GPT],
|
|
55
149
|
auth: [authMethod],
|
|
56
150
|
});
|
|
57
151
|
|
|
@@ -59,11 +153,7 @@ const aicodewithPlugin = {
|
|
|
59
153
|
id: PROVIDER_ID_CLAUDE,
|
|
60
154
|
label: "AICodewith Claude",
|
|
61
155
|
envVars: [AICODEWITH_API_KEY_ENV],
|
|
62
|
-
models:
|
|
63
|
-
baseUrl: AICODEWITH_CLAUDE_BASE_URL,
|
|
64
|
-
api: "anthropic-messages",
|
|
65
|
-
models: CLAUDE_MODELS,
|
|
66
|
-
},
|
|
156
|
+
models: providerConfigs[PROVIDER_ID_CLAUDE],
|
|
67
157
|
auth: [authMethod],
|
|
68
158
|
});
|
|
69
159
|
|
|
@@ -71,11 +161,7 @@ const aicodewithPlugin = {
|
|
|
71
161
|
id: PROVIDER_ID_GEMINI,
|
|
72
162
|
label: "AICodewith Gemini",
|
|
73
163
|
envVars: [AICODEWITH_API_KEY_ENV],
|
|
74
|
-
models:
|
|
75
|
-
baseUrl: AICODEWITH_GEMINI_BASE_URL,
|
|
76
|
-
api: "google-generative-ai",
|
|
77
|
-
models: GEMINI_MODELS,
|
|
78
|
-
},
|
|
164
|
+
models: providerConfigs[PROVIDER_ID_GEMINI],
|
|
79
165
|
auth: [authMethod],
|
|
80
166
|
});
|
|
81
167
|
},
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export {
|
|
2
|
+
MODELS,
|
|
3
|
+
PROVIDER_IDS,
|
|
4
|
+
getActiveModels,
|
|
5
|
+
getModelById,
|
|
6
|
+
getModelsByFamily,
|
|
7
|
+
toOpenClawModel,
|
|
8
|
+
buildProviderConfigs,
|
|
9
|
+
getDefaultModel,
|
|
10
|
+
getDeprecatedModels,
|
|
11
|
+
buildModelMigrations,
|
|
12
|
+
} from "./registry.js";
|
|
13
|
+
|
|
14
|
+
export type { ModelDefinition, ModelFamily, OpenClawModel } from "./registry.js";
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file registry.ts
|
|
3
|
+
* @description Central model definitions - single source of truth for OpenClaw plugin
|
|
4
|
+
*
|
|
5
|
+
* HOW TO ADD A NEW MODEL:
|
|
6
|
+
* 1. Add entry to MODELS array below
|
|
7
|
+
* 2. Run `bun run build` to verify
|
|
8
|
+
* 3. Done!
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
PROVIDER_ID_GPT,
|
|
13
|
+
PROVIDER_ID_CLAUDE,
|
|
14
|
+
PROVIDER_ID_GEMINI,
|
|
15
|
+
AICODEWITH_GPT_BASE_URL,
|
|
16
|
+
AICODEWITH_CLAUDE_BASE_URL,
|
|
17
|
+
AICODEWITH_GEMINI_BASE_URL,
|
|
18
|
+
CODEX_USER_AGENT,
|
|
19
|
+
CODEX_ORIGINATOR,
|
|
20
|
+
} from "../../src/constants.js";
|
|
21
|
+
|
|
22
|
+
export type ModelFamily = "gpt" | "claude" | "gemini";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* OpenClaw SDK Model Interface (preserved from index.ts)
|
|
26
|
+
*/
|
|
27
|
+
export interface OpenClawModel {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
reasoning: boolean;
|
|
31
|
+
input: readonly ("text" | "image")[];
|
|
32
|
+
cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
|
|
33
|
+
contextWindow: number;
|
|
34
|
+
maxTokens: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extended Model Definition with Registry Metadata
|
|
39
|
+
*/
|
|
40
|
+
export interface ModelDefinition {
|
|
41
|
+
// OpenClaw SDK fields (preserved)
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
reasoning: boolean;
|
|
45
|
+
input: readonly ("text" | "image")[];
|
|
46
|
+
cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
|
|
47
|
+
contextWindow: number;
|
|
48
|
+
maxTokens: number;
|
|
49
|
+
|
|
50
|
+
// Registry metadata (new)
|
|
51
|
+
family: ModelFamily;
|
|
52
|
+
displayName: string;
|
|
53
|
+
version: string;
|
|
54
|
+
limit: {
|
|
55
|
+
context: number;
|
|
56
|
+
output: number;
|
|
57
|
+
};
|
|
58
|
+
modalities: {
|
|
59
|
+
input: ("text" | "image")[];
|
|
60
|
+
output: ("text")[];
|
|
61
|
+
};
|
|
62
|
+
deprecated?: boolean;
|
|
63
|
+
replacedBy?: string;
|
|
64
|
+
isDefault?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const DEFAULT_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* ============================================
|
|
71
|
+
* SINGLE SOURCE OF TRUTH - ALL MODELS DEFINED HERE
|
|
72
|
+
* ============================================
|
|
73
|
+
*/
|
|
74
|
+
export const MODELS: ModelDefinition[] = [
|
|
75
|
+
// GPT Models (Active)
|
|
76
|
+
{
|
|
77
|
+
id: "gpt-5.3-codex",
|
|
78
|
+
name: "GPT-5.3 Codex",
|
|
79
|
+
family: "gpt",
|
|
80
|
+
displayName: "GPT-5.3 Codex",
|
|
81
|
+
version: "5.3",
|
|
82
|
+
reasoning: false,
|
|
83
|
+
input: ["text", "image"] as const,
|
|
84
|
+
cost: DEFAULT_COST,
|
|
85
|
+
contextWindow: 400000,
|
|
86
|
+
maxTokens: 128000,
|
|
87
|
+
limit: { context: 400000, output: 128000 },
|
|
88
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: "gpt-5.2",
|
|
92
|
+
name: "GPT-5.2",
|
|
93
|
+
family: "gpt",
|
|
94
|
+
displayName: "GPT-5.2",
|
|
95
|
+
version: "5.2",
|
|
96
|
+
reasoning: false,
|
|
97
|
+
input: ["text", "image"] as const,
|
|
98
|
+
cost: DEFAULT_COST,
|
|
99
|
+
contextWindow: 400000,
|
|
100
|
+
maxTokens: 128000,
|
|
101
|
+
limit: { context: 400000, output: 128000 },
|
|
102
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// GPT Models (Deprecated)
|
|
106
|
+
{
|
|
107
|
+
id: "gpt-5.2-codex",
|
|
108
|
+
name: "GPT-5.2 Codex (deprecated)",
|
|
109
|
+
family: "gpt",
|
|
110
|
+
displayName: "GPT-5.2 Codex (deprecated)",
|
|
111
|
+
version: "5.2",
|
|
112
|
+
reasoning: false,
|
|
113
|
+
input: ["text", "image"] as const,
|
|
114
|
+
cost: DEFAULT_COST,
|
|
115
|
+
contextWindow: 400000,
|
|
116
|
+
maxTokens: 128000,
|
|
117
|
+
limit: { context: 400000, output: 128000 },
|
|
118
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
119
|
+
deprecated: true,
|
|
120
|
+
replacedBy: "gpt-5.3-codex",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "gpt-5.1-codex",
|
|
124
|
+
name: "GPT-5.1 Codex (deprecated)",
|
|
125
|
+
family: "gpt",
|
|
126
|
+
displayName: "GPT-5.1 Codex (deprecated)",
|
|
127
|
+
version: "5.1",
|
|
128
|
+
reasoning: false,
|
|
129
|
+
input: ["text", "image"] as const,
|
|
130
|
+
cost: DEFAULT_COST,
|
|
131
|
+
contextWindow: 400000,
|
|
132
|
+
maxTokens: 128000,
|
|
133
|
+
limit: { context: 400000, output: 128000 },
|
|
134
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
135
|
+
deprecated: true,
|
|
136
|
+
replacedBy: "gpt-5.3-codex",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "gpt-5.1-codex-max",
|
|
140
|
+
name: "GPT-5.1 Codex Max (deprecated)",
|
|
141
|
+
family: "gpt",
|
|
142
|
+
displayName: "GPT-5.1 Codex Max (deprecated)",
|
|
143
|
+
version: "5.1",
|
|
144
|
+
reasoning: false,
|
|
145
|
+
input: ["text", "image"] as const,
|
|
146
|
+
cost: DEFAULT_COST,
|
|
147
|
+
contextWindow: 400000,
|
|
148
|
+
maxTokens: 128000,
|
|
149
|
+
limit: { context: 400000, output: 128000 },
|
|
150
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
151
|
+
deprecated: true,
|
|
152
|
+
replacedBy: "gpt-5.3-codex",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "gpt-5.1-codex-mini",
|
|
156
|
+
name: "GPT-5.1 Codex Mini (deprecated)",
|
|
157
|
+
family: "gpt",
|
|
158
|
+
displayName: "GPT-5.1 Codex Mini (deprecated)",
|
|
159
|
+
version: "5.1",
|
|
160
|
+
reasoning: false,
|
|
161
|
+
input: ["text", "image"] as const,
|
|
162
|
+
cost: DEFAULT_COST,
|
|
163
|
+
contextWindow: 200000,
|
|
164
|
+
maxTokens: 64000,
|
|
165
|
+
limit: { context: 200000, output: 64000 },
|
|
166
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
167
|
+
deprecated: true,
|
|
168
|
+
replacedBy: "gpt-5.3-codex",
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: "gpt-5.1",
|
|
172
|
+
name: "GPT-5.1 (deprecated)",
|
|
173
|
+
family: "gpt",
|
|
174
|
+
displayName: "GPT-5.1 (deprecated)",
|
|
175
|
+
version: "5.1",
|
|
176
|
+
reasoning: false,
|
|
177
|
+
input: ["text", "image"] as const,
|
|
178
|
+
cost: DEFAULT_COST,
|
|
179
|
+
contextWindow: 400000,
|
|
180
|
+
maxTokens: 128000,
|
|
181
|
+
limit: { context: 400000, output: 128000 },
|
|
182
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
183
|
+
deprecated: true,
|
|
184
|
+
replacedBy: "gpt-5.2",
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
// Claude Models (Active)
|
|
188
|
+
{
|
|
189
|
+
id: "claude-opus-4-6-20260205",
|
|
190
|
+
name: "Claude Opus 4.6",
|
|
191
|
+
family: "claude",
|
|
192
|
+
displayName: "Claude Opus 4.6",
|
|
193
|
+
version: "4.6",
|
|
194
|
+
reasoning: false,
|
|
195
|
+
input: ["text", "image"] as const,
|
|
196
|
+
cost: DEFAULT_COST,
|
|
197
|
+
contextWindow: 200000,
|
|
198
|
+
maxTokens: 64000,
|
|
199
|
+
limit: { context: 200000, output: 64000 },
|
|
200
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
201
|
+
isDefault: true,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: "claude-sonnet-4-5-20250929",
|
|
205
|
+
name: "Claude Sonnet 4.5",
|
|
206
|
+
family: "claude",
|
|
207
|
+
displayName: "Claude Sonnet 4.5",
|
|
208
|
+
version: "4.5",
|
|
209
|
+
reasoning: false,
|
|
210
|
+
input: ["text", "image"] as const,
|
|
211
|
+
cost: DEFAULT_COST,
|
|
212
|
+
contextWindow: 200000,
|
|
213
|
+
maxTokens: 64000,
|
|
214
|
+
limit: { context: 200000, output: 64000 },
|
|
215
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: "claude-haiku-4-5-20251001",
|
|
219
|
+
name: "Claude Haiku 4.5",
|
|
220
|
+
family: "claude",
|
|
221
|
+
displayName: "Claude Haiku 4.5",
|
|
222
|
+
version: "4.5",
|
|
223
|
+
reasoning: false,
|
|
224
|
+
input: ["text", "image"] as const,
|
|
225
|
+
cost: DEFAULT_COST,
|
|
226
|
+
contextWindow: 200000,
|
|
227
|
+
maxTokens: 8192,
|
|
228
|
+
limit: { context: 200000, output: 8192 },
|
|
229
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// Claude Models (Deprecated)
|
|
233
|
+
{
|
|
234
|
+
id: "claude-opus-4-5-20251101",
|
|
235
|
+
name: "Claude Opus 4.5 (deprecated)",
|
|
236
|
+
family: "claude",
|
|
237
|
+
displayName: "Claude Opus 4.5 (deprecated)",
|
|
238
|
+
version: "4.5",
|
|
239
|
+
reasoning: false,
|
|
240
|
+
input: ["text", "image"] as const,
|
|
241
|
+
cost: DEFAULT_COST,
|
|
242
|
+
contextWindow: 180000,
|
|
243
|
+
maxTokens: 64000,
|
|
244
|
+
limit: { context: 180000, output: 64000 },
|
|
245
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
246
|
+
deprecated: true,
|
|
247
|
+
replacedBy: "claude-opus-4-6-20260205",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: "claude-opus-4-6-20260205-third-party",
|
|
251
|
+
name: "Claude Opus 4.6 third-party (deprecated)",
|
|
252
|
+
family: "claude",
|
|
253
|
+
displayName: "Claude Opus 4.6 third-party (deprecated)",
|
|
254
|
+
version: "4.6",
|
|
255
|
+
reasoning: false,
|
|
256
|
+
input: ["text", "image"] as const,
|
|
257
|
+
cost: DEFAULT_COST,
|
|
258
|
+
contextWindow: 200000,
|
|
259
|
+
maxTokens: 64000,
|
|
260
|
+
limit: { context: 200000, output: 64000 },
|
|
261
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
262
|
+
deprecated: true,
|
|
263
|
+
replacedBy: "claude-opus-4-6-20260205",
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
id: "claude-opus-4-5-20251101-third-party",
|
|
267
|
+
name: "Claude Opus 4.5 third-party (deprecated)",
|
|
268
|
+
family: "claude",
|
|
269
|
+
displayName: "Claude Opus 4.5 third-party (deprecated)",
|
|
270
|
+
version: "4.5",
|
|
271
|
+
reasoning: false,
|
|
272
|
+
input: ["text", "image"] as const,
|
|
273
|
+
cost: DEFAULT_COST,
|
|
274
|
+
contextWindow: 180000,
|
|
275
|
+
maxTokens: 64000,
|
|
276
|
+
limit: { context: 180000, output: 64000 },
|
|
277
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
278
|
+
deprecated: true,
|
|
279
|
+
replacedBy: "claude-opus-4-6-20260205",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: "claude-sonnet-4-5-20250929-third-party",
|
|
283
|
+
name: "Claude Sonnet 4.5 third-party (deprecated)",
|
|
284
|
+
family: "claude",
|
|
285
|
+
displayName: "Claude Sonnet 4.5 third-party (deprecated)",
|
|
286
|
+
version: "4.5",
|
|
287
|
+
reasoning: false,
|
|
288
|
+
input: ["text", "image"] as const,
|
|
289
|
+
cost: DEFAULT_COST,
|
|
290
|
+
contextWindow: 200000,
|
|
291
|
+
maxTokens: 64000,
|
|
292
|
+
limit: { context: 200000, output: 64000 },
|
|
293
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
294
|
+
deprecated: true,
|
|
295
|
+
replacedBy: "claude-sonnet-4-5-20250929",
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: "claude-haiku-4-5-20251001-third-party",
|
|
299
|
+
name: "Claude Haiku 4.5 third-party (deprecated)",
|
|
300
|
+
family: "claude",
|
|
301
|
+
displayName: "Claude Haiku 4.5 third-party (deprecated)",
|
|
302
|
+
version: "4.5",
|
|
303
|
+
reasoning: false,
|
|
304
|
+
input: ["text", "image"] as const,
|
|
305
|
+
cost: DEFAULT_COST,
|
|
306
|
+
contextWindow: 200000,
|
|
307
|
+
maxTokens: 8192,
|
|
308
|
+
limit: { context: 200000, output: 8192 },
|
|
309
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
310
|
+
deprecated: true,
|
|
311
|
+
replacedBy: "claude-haiku-4-5-20251001",
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
// Gemini Models
|
|
315
|
+
{
|
|
316
|
+
id: "gemini-3-pro",
|
|
317
|
+
name: "Gemini 3 Pro",
|
|
318
|
+
family: "gemini",
|
|
319
|
+
displayName: "Gemini 3 Pro",
|
|
320
|
+
version: "3",
|
|
321
|
+
reasoning: false,
|
|
322
|
+
input: ["text", "image"] as const,
|
|
323
|
+
cost: DEFAULT_COST,
|
|
324
|
+
contextWindow: 1048576,
|
|
325
|
+
maxTokens: 65536,
|
|
326
|
+
limit: { context: 1048576, output: 65536 },
|
|
327
|
+
modalities: { input: ["text", "image"], output: ["text"] },
|
|
328
|
+
},
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Provider IDs
|
|
333
|
+
*/
|
|
334
|
+
export const PROVIDER_IDS = {
|
|
335
|
+
GPT: PROVIDER_ID_GPT,
|
|
336
|
+
CLAUDE: PROVIDER_ID_CLAUDE,
|
|
337
|
+
GEMINI: PROVIDER_ID_GEMINI,
|
|
338
|
+
} as const;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* ============================================
|
|
342
|
+
* HELPER FUNCTIONS
|
|
343
|
+
* ============================================
|
|
344
|
+
*/
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Get all active (non-deprecated) models
|
|
348
|
+
*/
|
|
349
|
+
export const getActiveModels = (): ModelDefinition[] => {
|
|
350
|
+
return MODELS.filter((m) => !m.deprecated);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get model by ID
|
|
355
|
+
*/
|
|
356
|
+
export const getModelById = (id: string): ModelDefinition | undefined => {
|
|
357
|
+
return MODELS.find((m) => m.id === id);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Get models by family
|
|
362
|
+
*/
|
|
363
|
+
export const getModelsByFamily = (family: ModelFamily): ModelDefinition[] => {
|
|
364
|
+
return MODELS.filter((m) => m.family === family && !m.deprecated);
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Transform ModelDefinition to OpenClaw SDK format
|
|
369
|
+
*/
|
|
370
|
+
export const toOpenClawModel = (model: ModelDefinition): OpenClawModel => {
|
|
371
|
+
return {
|
|
372
|
+
id: model.id,
|
|
373
|
+
name: model.name,
|
|
374
|
+
reasoning: model.reasoning,
|
|
375
|
+
input: model.input,
|
|
376
|
+
cost: model.cost,
|
|
377
|
+
contextWindow: model.contextWindow,
|
|
378
|
+
maxTokens: model.maxTokens,
|
|
379
|
+
};
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get default model
|
|
384
|
+
*/
|
|
385
|
+
export const getDefaultModel = (): ModelDefinition => {
|
|
386
|
+
const defaultModel = MODELS.find((m) => m.isDefault);
|
|
387
|
+
if (!defaultModel) {
|
|
388
|
+
throw new Error("No default model found in registry");
|
|
389
|
+
}
|
|
390
|
+
return defaultModel;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Get all deprecated models
|
|
395
|
+
*/
|
|
396
|
+
export const getDeprecatedModels = (): ModelDefinition[] => {
|
|
397
|
+
return MODELS.filter((m) => m.deprecated);
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Build model migrations map (oldModelId -> newModelId)
|
|
402
|
+
* Used for migrating users from deprecated models to their replacements
|
|
403
|
+
* Includes both bare model IDs and provider-prefixed IDs
|
|
404
|
+
*/
|
|
405
|
+
export const buildModelMigrations = (): Record<string, string> => {
|
|
406
|
+
const migrations: Record<string, string> = {};
|
|
407
|
+
|
|
408
|
+
const getProviderForFamily = (family: ModelFamily): string => {
|
|
409
|
+
switch (family) {
|
|
410
|
+
case "gpt": return PROVIDER_ID_GPT;
|
|
411
|
+
case "claude": return PROVIDER_ID_CLAUDE;
|
|
412
|
+
case "gemini": return PROVIDER_ID_GEMINI;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
for (const model of MODELS) {
|
|
417
|
+
if (model.deprecated && model.replacedBy) {
|
|
418
|
+
migrations[model.id] = model.replacedBy;
|
|
419
|
+
|
|
420
|
+
const provider = getProviderForFamily(model.family);
|
|
421
|
+
const replacementModel = MODELS.find(m => m.id === model.replacedBy);
|
|
422
|
+
if (replacementModel) {
|
|
423
|
+
const replacementProvider = getProviderForFamily(replacementModel.family);
|
|
424
|
+
migrations[`${provider}/${model.id}`] = `${replacementProvider}/${model.replacedBy}`;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return migrations;
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Build provider configurations for all 3 providers
|
|
433
|
+
*/
|
|
434
|
+
export const buildProviderConfigs = () => {
|
|
435
|
+
const gptModels = getModelsByFamily("gpt").map(toOpenClawModel);
|
|
436
|
+
const claudeModels = getModelsByFamily("claude").map(toOpenClawModel);
|
|
437
|
+
const geminiModels = getModelsByFamily("gemini").map(toOpenClawModel);
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
[PROVIDER_ID_GPT]: {
|
|
441
|
+
baseUrl: AICODEWITH_GPT_BASE_URL,
|
|
442
|
+
api: "openai-responses" as const,
|
|
443
|
+
models: gptModels,
|
|
444
|
+
},
|
|
445
|
+
[PROVIDER_ID_CLAUDE]: {
|
|
446
|
+
baseUrl: AICODEWITH_CLAUDE_BASE_URL,
|
|
447
|
+
api: "anthropic-messages" as const,
|
|
448
|
+
models: claudeModels,
|
|
449
|
+
},
|
|
450
|
+
[PROVIDER_ID_GEMINI]: {
|
|
451
|
+
baseUrl: AICODEWITH_GEMINI_BASE_URL,
|
|
452
|
+
api: "google-generative-ai" as const,
|
|
453
|
+
models: geminiModels,
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-aicodewith-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AICodewith provider plugin for OpenClaw - Access GPT, Claude, and Gemini models via AICodewith API",
|
|
6
6
|
"author": "daneel",
|
|
@@ -27,10 +27,18 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"index.ts",
|
|
29
29
|
"src",
|
|
30
|
+
"lib",
|
|
30
31
|
"openclaw.plugin.json",
|
|
31
32
|
"README.md"
|
|
32
33
|
],
|
|
33
34
|
"peerDependencies": {
|
|
34
35
|
"openclaw": ">=2026.1.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:watch": "vitest"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"vitest": "^4.0.18"
|
|
35
43
|
}
|
|
36
44
|
}
|
package/src/auth.ts
CHANGED
|
@@ -2,13 +2,12 @@ import {
|
|
|
2
2
|
AICODEWITH_GPT_BASE_URL,
|
|
3
3
|
AICODEWITH_CLAUDE_BASE_URL,
|
|
4
4
|
AICODEWITH_GEMINI_BASE_URL,
|
|
5
|
-
AICODEWITH_GPT_API,
|
|
6
5
|
PROVIDER_ID_GPT,
|
|
7
6
|
PROVIDER_ID_CLAUDE,
|
|
8
7
|
PROVIDER_ID_GEMINI,
|
|
9
8
|
AUTH_PROFILE_ID,
|
|
10
9
|
} from "./constants.js";
|
|
11
|
-
import {
|
|
10
|
+
import { buildProviderConfigs, getDefaultModel } from "../lib/models/index.js";
|
|
12
11
|
|
|
13
12
|
function validateApiKey(value: string): string | undefined {
|
|
14
13
|
const trimmed = value.trim();
|
|
@@ -39,7 +38,9 @@ export function createAicodewithAuthMethod() {
|
|
|
39
38
|
});
|
|
40
39
|
|
|
41
40
|
const trimmedKey = apiKey.trim();
|
|
42
|
-
const
|
|
41
|
+
const providerConfigs = buildProviderConfigs();
|
|
42
|
+
const defaultModel = getDefaultModel();
|
|
43
|
+
const defaultModelRef = `${PROVIDER_ID_CLAUDE}/${defaultModel.id}`;
|
|
43
44
|
|
|
44
45
|
return {
|
|
45
46
|
profiles: [
|
|
@@ -56,22 +57,16 @@ export function createAicodewithAuthMethod() {
|
|
|
56
57
|
models: {
|
|
57
58
|
providers: {
|
|
58
59
|
[PROVIDER_ID_GPT]: {
|
|
59
|
-
|
|
60
|
+
...providerConfigs[PROVIDER_ID_GPT],
|
|
60
61
|
apiKey: trimmedKey,
|
|
61
|
-
api: AICODEWITH_GPT_API,
|
|
62
|
-
models: GPT_MODELS,
|
|
63
62
|
},
|
|
64
63
|
[PROVIDER_ID_CLAUDE]: {
|
|
65
|
-
|
|
64
|
+
...providerConfigs[PROVIDER_ID_CLAUDE],
|
|
66
65
|
apiKey: trimmedKey,
|
|
67
|
-
api: "anthropic-messages",
|
|
68
|
-
models: CLAUDE_MODELS,
|
|
69
66
|
},
|
|
70
67
|
[PROVIDER_ID_GEMINI]: {
|
|
71
|
-
|
|
68
|
+
...providerConfigs[PROVIDER_ID_GEMINI],
|
|
72
69
|
apiKey: trimmedKey,
|
|
73
|
-
api: "google-generative-ai",
|
|
74
|
-
models: GEMINI_MODELS,
|
|
75
70
|
},
|
|
76
71
|
},
|
|
77
72
|
},
|
|
@@ -80,13 +75,13 @@ export function createAicodewithAuthMethod() {
|
|
|
80
75
|
model: defaultModelRef,
|
|
81
76
|
models: {
|
|
82
77
|
...Object.fromEntries(
|
|
83
|
-
|
|
78
|
+
providerConfigs[PROVIDER_ID_GPT].models.map((m) => [`${PROVIDER_ID_GPT}/${m.id}`, {}])
|
|
84
79
|
),
|
|
85
80
|
...Object.fromEntries(
|
|
86
|
-
|
|
81
|
+
providerConfigs[PROVIDER_ID_CLAUDE].models.map((m) => [`${PROVIDER_ID_CLAUDE}/${m.id}`, {}])
|
|
87
82
|
),
|
|
88
83
|
...Object.fromEntries(
|
|
89
|
-
|
|
84
|
+
providerConfigs[PROVIDER_ID_GEMINI].models.map((m) => [`${PROVIDER_ID_GEMINI}/${m.id}`, {}])
|
|
90
85
|
),
|
|
91
86
|
},
|
|
92
87
|
},
|
package/src/constants.ts
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
export const
|
|
3
|
-
|
|
4
|
-
export const AICODEWITH_CLAUDE_BASE_URL =
|
|
5
|
-
process.env.AICODEWITH_CLAUDE_BASE_URL?.trim() || "https://api.aicodewith.com/v1";
|
|
6
|
-
export const AICODEWITH_GEMINI_BASE_URL =
|
|
7
|
-
process.env.AICODEWITH_GEMINI_BASE_URL?.trim() || "https://api.aicodewith.com/gemini_cli";
|
|
8
|
-
|
|
9
|
-
// API type for GPT - "openai-responses" (default) or "openai-completions"
|
|
10
|
-
export const AICODEWITH_GPT_API =
|
|
11
|
-
(process.env.AICODEWITH_GPT_API?.trim() as "openai-responses" | "openai-completions") ||
|
|
12
|
-
"openai-responses";
|
|
1
|
+
export const AICODEWITH_GPT_BASE_URL = "https://api.aicodewith.com/chatgpt/v1";
|
|
2
|
+
export const AICODEWITH_CLAUDE_BASE_URL = "https://api.aicodewith.com";
|
|
3
|
+
export const AICODEWITH_GEMINI_BASE_URL = "https://api.aicodewith.com/gemini_cli";
|
|
13
4
|
|
|
14
5
|
export const PROVIDER_ID_GPT = "aicodewith-gpt";
|
|
15
6
|
export const PROVIDER_ID_CLAUDE = "aicodewith-claude";
|
|
@@ -23,3 +14,7 @@ export const PLUGIN_ID = "openclaw-aicodewith-auth";
|
|
|
23
14
|
export const PLUGIN_NAME = "AICodewith";
|
|
24
15
|
export const PLUGIN_DESCRIPTION =
|
|
25
16
|
"Access GPT, Claude, and Gemini models via AICodewith API";
|
|
17
|
+
|
|
18
|
+
// Codex-specific headers (required for GPT models)
|
|
19
|
+
export const CODEX_USER_AGENT = "codex_cli_rs/0.77.0 (Mac OS 26.2.0; arm64) iTerm.app/3.6.6";
|
|
20
|
+
export const CODEX_ORIGINATOR = "codex_cli_rs";
|
package/src/models.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
const DEFAULT_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
2
|
-
|
|
3
|
-
export const GPT_MODELS = [
|
|
4
|
-
{
|
|
5
|
-
id: "gpt-5.2-codex",
|
|
6
|
-
name: "GPT-5.2 Codex",
|
|
7
|
-
reasoning: false,
|
|
8
|
-
input: ["text", "image"] as const,
|
|
9
|
-
cost: DEFAULT_COST,
|
|
10
|
-
contextWindow: 400000,
|
|
11
|
-
maxTokens: 128000,
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
id: "gpt-5.2",
|
|
15
|
-
name: "GPT-5.2",
|
|
16
|
-
reasoning: false,
|
|
17
|
-
input: ["text", "image"] as const,
|
|
18
|
-
cost: DEFAULT_COST,
|
|
19
|
-
contextWindow: 400000,
|
|
20
|
-
maxTokens: 128000,
|
|
21
|
-
},
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
export const CLAUDE_MODELS = [
|
|
25
|
-
{
|
|
26
|
-
id: "claude-sonnet-4-5-20250929",
|
|
27
|
-
name: "Claude Sonnet 4.5",
|
|
28
|
-
reasoning: false,
|
|
29
|
-
input: ["text", "image"] as const,
|
|
30
|
-
cost: DEFAULT_COST,
|
|
31
|
-
contextWindow: 200000,
|
|
32
|
-
maxTokens: 64000,
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: "claude-opus-4-5-20251101",
|
|
36
|
-
name: "Claude Opus 4.5",
|
|
37
|
-
reasoning: false,
|
|
38
|
-
input: ["text", "image"] as const,
|
|
39
|
-
cost: DEFAULT_COST,
|
|
40
|
-
contextWindow: 200000,
|
|
41
|
-
maxTokens: 64000,
|
|
42
|
-
},
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
export const GEMINI_MODELS = [
|
|
46
|
-
{
|
|
47
|
-
id: "gemini-3-pro",
|
|
48
|
-
name: "Gemini 3 Pro",
|
|
49
|
-
reasoning: false,
|
|
50
|
-
input: ["text", "image"] as const,
|
|
51
|
-
cost: DEFAULT_COST,
|
|
52
|
-
contextWindow: 1048576,
|
|
53
|
-
maxTokens: 65536,
|
|
54
|
-
},
|
|
55
|
-
];
|