@ottocode/sdk 0.1.201 → 0.1.203

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottocode/sdk",
3
- "version": "0.1.201",
3
+ "version": "0.1.203",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -35,29 +35,31 @@ export function getGlobalAuthPath(): string {
35
35
  return joinPath(getGlobalConfigDir(), 'auth.json');
36
36
  }
37
37
 
38
- // Secure location for auth secrets (not in config dir or project)
39
- // - Linux: $XDG_STATE_HOME/otto/auth.json or ~/.local/state/otto/auth.json
40
- // - macOS: ~/Library/Application Support/otto/auth.json
41
- // - Windows: %APPDATA%\otto\auth.json
42
- export function getSecureAuthPath(): string {
38
+ export function getSecureBaseDir(): string {
43
39
  const platform = process.platform;
44
40
  if (platform === 'darwin') {
45
- return joinPath(
46
- getHomeDir(),
47
- 'Library',
48
- 'Application Support',
49
- 'otto',
50
- 'auth.json',
51
- );
41
+ return joinPath(getHomeDir(), 'Library', 'Application Support', 'otto');
52
42
  }
53
43
  if (platform === 'win32') {
54
44
  const appData = (process.env.APPDATA || '').replace(/\\/g, '/');
55
45
  const base = appData || joinPath(getHomeDir(), 'AppData', 'Roaming');
56
- return joinPath(base, 'otto', 'auth.json');
46
+ return joinPath(base, 'otto');
57
47
  }
58
48
  const stateHome = (process.env.XDG_STATE_HOME || '').replace(/\\/g, '/');
59
49
  const base = stateHome || joinPath(getHomeDir(), '.local', 'state');
60
- return joinPath(base, 'otto', 'auth.json');
50
+ return joinPath(base, 'otto');
51
+ }
52
+
53
+ export function getSecureOAuthDir(): string {
54
+ return joinPath(getSecureBaseDir(), 'oauth');
55
+ }
56
+
57
+ // Secure location for auth secrets (not in config dir or project)
58
+ // - Linux: $XDG_STATE_HOME/otto/auth.json or ~/.local/state/otto/auth.json
59
+ // - macOS: ~/Library/Application Support/otto/auth.json
60
+ // - Windows: %APPDATA%\otto\auth.json
61
+ export function getSecureAuthPath(): string {
62
+ return joinPath(getSecureBaseDir(), 'auth.json');
61
63
  }
62
64
 
63
65
  // Global content under config dir
@@ -133,6 +133,7 @@ export type {
133
133
  MCPToolInfo,
134
134
  MCPTransport,
135
135
  MCPOAuthConfig,
136
+ MCPScope,
136
137
  StoredOAuthData,
137
138
  OttoOAuthProviderOptions,
138
139
  CallbackResult,
@@ -4,6 +4,7 @@ export type {
4
4
  MCPServerStatus,
5
5
  MCPTransport,
6
6
  MCPOAuthConfig,
7
+ MCPScope,
7
8
  } from './types.ts';
8
9
 
9
10
  export { MCPClientWrapper, type MCPToolInfo } from './client.ts';
@@ -1,5 +1,5 @@
1
1
  import { MCPServerManager } from './server-manager.ts';
2
- import type { MCPConfig, MCPServerConfig } from './types.ts';
2
+ import type { MCPConfig, MCPServerConfig, MCPScope } from './types.ts';
3
3
  import { promises as fs } from 'node:fs';
4
4
  import { join } from 'node:path';
5
5
 
@@ -11,11 +11,15 @@ export function getMCPManager(): MCPServerManager | null {
11
11
 
12
12
  export async function initializeMCP(
13
13
  config: MCPConfig,
14
+ projectRoot?: string,
14
15
  ): Promise<MCPServerManager> {
15
16
  if (globalMCPManager) {
16
17
  await globalMCPManager.stopAll();
17
18
  }
18
19
  globalMCPManager = new MCPServerManager();
20
+ if (projectRoot) {
21
+ globalMCPManager.setProjectRoot(projectRoot);
22
+ }
19
23
  await globalMCPManager.startServers(config.servers);
20
24
  return globalMCPManager;
21
25
  }
@@ -41,7 +45,7 @@ export async function loadMCPConfig(
41
45
  const globalServers = await readMCPServersFromFile(globalPath);
42
46
  for (const s of globalServers) {
43
47
  seen.add(s.name);
44
- servers.push(s);
48
+ servers.push({ ...s, scope: 'global' });
45
49
  }
46
50
  }
47
51
 
@@ -50,9 +54,9 @@ export async function loadMCPConfig(
50
54
  for (const s of projectServers) {
51
55
  if (seen.has(s.name)) {
52
56
  const idx = servers.findIndex((existing) => existing.name === s.name);
53
- if (idx >= 0) servers[idx] = s;
57
+ if (idx >= 0) servers[idx] = { ...s, scope: 'project' };
54
58
  } else {
55
- servers.push(s);
59
+ servers.push({ ...s, scope: 'project' });
56
60
  }
57
61
  }
58
62
 
@@ -81,11 +85,30 @@ async function readMCPServersFromFile(
81
85
  }
82
86
  }
83
87
 
88
+ function resolveConfigPath(
89
+ projectRoot: string,
90
+ globalConfigDir: string | undefined,
91
+ scope: MCPScope,
92
+ ): string {
93
+ if (scope === 'global' && globalConfigDir) {
94
+ return join(globalConfigDir, 'config.json');
95
+ }
96
+ return join(projectRoot, '.otto', 'config.json');
97
+ }
98
+
99
+ async function ensureConfigDir(configPath: string): Promise<void> {
100
+ const dir = configPath.replace(/[/\\][^/\\]+$/, '');
101
+ await fs.mkdir(dir, { recursive: true });
102
+ }
103
+
84
104
  export async function addMCPServerToConfig(
85
105
  projectRoot: string,
86
106
  server: MCPServerConfig,
107
+ globalConfigDir?: string,
87
108
  ): Promise<void> {
88
- const configPath = join(projectRoot, '.otto', 'config.json');
109
+ const scope: MCPScope = server.scope ?? 'global';
110
+ const configPath = resolveConfigPath(projectRoot, globalConfigDir, scope);
111
+
89
112
  let json: Record<string, unknown> = {};
90
113
  try {
91
114
  const text = await fs.readFile(configPath, 'utf-8');
@@ -98,37 +121,48 @@ export async function addMCPServerToConfig(
98
121
 
99
122
  const servers = mcp.servers as MCPServerConfig[];
100
123
  const idx = servers.findIndex((s) => s.name === server.name);
124
+
125
+ const { scope: _scope, ...serverWithoutScope } = server;
101
126
  if (idx >= 0) {
102
- servers[idx] = server;
127
+ servers[idx] = serverWithoutScope;
103
128
  } else {
104
- servers.push(server);
129
+ servers.push(serverWithoutScope);
105
130
  }
106
131
 
107
- await fs.mkdir(join(projectRoot, '.otto'), { recursive: true });
132
+ await ensureConfigDir(configPath);
108
133
  await fs.writeFile(configPath, JSON.stringify(json, null, '\t'), 'utf-8');
109
134
  }
110
135
 
111
136
  export async function removeMCPServerFromConfig(
112
137
  projectRoot: string,
113
138
  name: string,
139
+ globalConfigDir?: string,
114
140
  ): Promise<boolean> {
115
- const configPath = join(projectRoot, '.otto', 'config.json');
116
- let json: Record<string, unknown> = {};
117
- try {
118
- const text = await fs.readFile(configPath, 'utf-8');
119
- json = JSON.parse(text);
120
- } catch {
121
- return false;
122
- }
141
+ const paths = [
142
+ ...(globalConfigDir ? [join(globalConfigDir, 'config.json')] : []),
143
+ join(projectRoot, '.otto', 'config.json'),
144
+ ];
145
+
146
+ for (const configPath of paths) {
147
+ let json: Record<string, unknown> = {};
148
+ try {
149
+ const text = await fs.readFile(configPath, 'utf-8');
150
+ json = JSON.parse(text);
151
+ } catch {
152
+ continue;
153
+ }
123
154
 
124
- const mcp = json.mcp as Record<string, unknown> | undefined;
125
- if (!mcp || !Array.isArray(mcp.servers)) return false;
155
+ const mcp = json.mcp as Record<string, unknown> | undefined;
156
+ if (!mcp || !Array.isArray(mcp.servers)) continue;
126
157
 
127
- const servers = mcp.servers as MCPServerConfig[];
128
- const idx = servers.findIndex((s) => s.name === name);
129
- if (idx < 0) return false;
158
+ const servers = mcp.servers as MCPServerConfig[];
159
+ const idx = servers.findIndex((s) => s.name === name);
160
+ if (idx < 0) continue;
130
161
 
131
- servers.splice(idx, 1);
132
- await fs.writeFile(configPath, JSON.stringify(json, null, '\t'), 'utf-8');
133
- return true;
162
+ servers.splice(idx, 1);
163
+ await fs.writeFile(configPath, JSON.stringify(json, null, '\t'), 'utf-8');
164
+ return true;
165
+ }
166
+
167
+ return false;
134
168
  }
@@ -1,5 +1,6 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import { join } from 'node:path';
3
+ import { getSecureOAuthDir } from '../../../../config/src/paths.ts';
3
4
 
4
5
  export interface StoredOAuthData {
5
6
  tokens?: {
@@ -22,14 +23,7 @@ export class OAuthCredentialStore {
22
23
  private storePath: string;
23
24
 
24
25
  constructor(storePath?: string) {
25
- this.storePath =
26
- storePath ??
27
- join(
28
- process.env.HOME ?? process.env.USERPROFILE ?? '',
29
- '.config',
30
- 'otto',
31
- 'oauth',
32
- );
26
+ this.storePath = storePath ?? getSecureOAuthDir();
33
27
  }
34
28
 
35
29
  private filePath(serverName: string): string {
@@ -2,6 +2,7 @@ import { MCPClientWrapper, type MCPToolInfo } from './client.ts';
2
2
  import type { MCPServerConfig, MCPServerStatus } from './types.ts';
3
3
  import { OAuthCredentialStore } from './oauth/store.ts';
4
4
  import { OttoOAuthProvider } from './oauth/provider.ts';
5
+ import { createHash } from 'node:crypto';
5
6
 
6
7
  type IndexedTool = {
7
8
  server: string;
@@ -14,17 +15,36 @@ export class MCPServerManager {
14
15
  private authProviders = new Map<string, OttoOAuthProvider>();
15
16
  private pendingAuth = new Map<string, string>();
16
17
  private oauthStore = new OAuthCredentialStore();
18
+ private serverScopes = new Map<string, 'global' | 'project'>();
17
19
  private _started = false;
20
+ private projectRoot: string | null = null;
18
21
 
19
22
  get started(): boolean {
20
23
  return this._started;
21
24
  }
22
25
 
26
+ setProjectRoot(projectRoot: string): void {
27
+ this.projectRoot = projectRoot;
28
+ }
29
+
30
+ private oauthKey(serverName: string): string {
31
+ const scope = this.serverScopes.get(serverName);
32
+ if (scope === 'project' && this.projectRoot) {
33
+ const hash = createHash('sha256')
34
+ .update(this.projectRoot)
35
+ .digest('hex')
36
+ .slice(0, 8);
37
+ return `${serverName}_proj_${hash}`;
38
+ }
39
+ return serverName;
40
+ }
41
+
23
42
  async startServers(configs: MCPServerConfig[]): Promise<void> {
24
43
  await this.stopAll();
25
44
 
26
45
  for (const config of configs) {
27
46
  if (config.disabled) continue;
47
+ this.serverScopes.set(config.name, config.scope ?? 'global');
28
48
  await this.startSingleServer(config);
29
49
  }
30
50
  this._started = true;
@@ -38,7 +58,8 @@ export class MCPServerManager {
38
58
  const hasStaticAuth =
39
59
  config.headers?.Authorization || config.headers?.authorization;
40
60
  if (!hasStaticAuth) {
41
- const provider = new OttoOAuthProvider(config.name, this.oauthStore, {
61
+ const key = this.oauthKey(config.name);
62
+ const provider = new OttoOAuthProvider(key, this.oauthStore, {
42
63
  clientId: config.oauth?.clientId,
43
64
  callbackPort: config.oauth?.callbackPort,
44
65
  scopes: config.oauth?.scopes,
@@ -104,6 +125,7 @@ export class MCPServerManager {
104
125
  this.toolsMap.clear();
105
126
  this.authProviders.clear();
106
127
  this.pendingAuth.clear();
128
+ this.serverScopes.clear();
107
129
  this._started = false;
108
130
  }
109
131
 
@@ -137,8 +159,9 @@ export class MCPServerManager {
137
159
  .filter(([, v]) => v.server === name)
138
160
  .map(([k]) => k);
139
161
  const config = client.serverConfig;
162
+ const key = this.oauthKey(name);
140
163
  const _authenticated = this.oauthStore
141
- .isAuthenticated(name)
164
+ .isAuthenticated(key)
142
165
  .catch(() => false);
143
166
 
144
167
  statuses.push({
@@ -161,8 +184,9 @@ export class MCPServerManager {
161
184
  .filter(([, v]) => v.server === name)
162
185
  .map(([k]) => k);
163
186
  const config = client.serverConfig;
187
+ const key = this.oauthKey(name);
164
188
  const authenticated = await this.oauthStore
165
- .isAuthenticated(name)
189
+ .isAuthenticated(key)
166
190
  .catch(() => false);
167
191
 
168
192
  statuses.push({
@@ -194,7 +218,9 @@ export class MCPServerManager {
194
218
  const transport = config.transport ?? 'stdio';
195
219
  if (transport === 'stdio') return null;
196
220
 
197
- const provider = new OttoOAuthProvider(config.name, this.oauthStore, {
221
+ this.serverScopes.set(config.name, config.scope ?? 'global');
222
+ const key = this.oauthKey(config.name);
223
+ const provider = new OttoOAuthProvider(key, this.oauthStore, {
198
224
  clientId: config.oauth?.clientId,
199
225
  callbackPort: config.oauth?.callbackPort,
200
226
  scopes: config.oauth?.scopes,
@@ -272,7 +298,8 @@ export class MCPServerManager {
272
298
  async getAuthStatus(
273
299
  name: string,
274
300
  ): Promise<{ authenticated: boolean; expiresAt?: number }> {
275
- const tokens = await this.oauthStore.loadTokens(name);
301
+ const key = this.oauthKey(name);
302
+ const tokens = await this.oauthStore.loadTokens(key);
276
303
  if (!tokens?.access_token) return { authenticated: false };
277
304
  return {
278
305
  authenticated: true,
@@ -282,6 +309,7 @@ export class MCPServerManager {
282
309
 
283
310
  async restartServer(config: MCPServerConfig): Promise<void> {
284
311
  await this.stopServer(config.name);
312
+ this.serverScopes.set(config.name, config.scope ?? 'global');
285
313
  await this.startSingleServer(config);
286
314
  }
287
315
 
@@ -1,4 +1,5 @@
1
1
  export type MCPTransport = 'stdio' | 'http' | 'sse';
2
+ export type MCPScope = 'global' | 'project';
2
3
 
3
4
  export interface MCPOAuthConfig {
4
5
  clientId?: string;
@@ -21,6 +22,8 @@ export interface MCPServerConfig {
21
22
  oauth?: MCPOAuthConfig;
22
23
 
23
24
  disabled?: boolean;
25
+
26
+ scope?: MCPScope;
24
27
  }
25
28
 
26
29
  export interface MCPConfig {
package/src/index.ts CHANGED
@@ -108,6 +108,8 @@ export { createOpencodeModel } from './providers/src/index.ts';
108
108
  export type { OpencodeProviderConfig } from './providers/src/index.ts';
109
109
  export { createMoonshotModel } from './providers/src/index.ts';
110
110
  export type { MoonshotProviderConfig } from './providers/src/index.ts';
111
+ export { createMinimaxModel } from './providers/src/index.ts';
112
+ export type { MinimaxProviderConfig } from './providers/src/index.ts';
111
113
  export {
112
114
  createCopilotFetch,
113
115
  createCopilotModel,
@@ -171,6 +173,8 @@ export {
171
173
  getGlobalToolsDir,
172
174
  getGlobalCommandsDir,
173
175
  getSecureAuthPath,
176
+ getSecureBaseDir,
177
+ getSecureOAuthDir,
174
178
  getHomeDir,
175
179
  } from './config/src/paths.ts';
176
180
  export {
@@ -358,6 +362,7 @@ export type {
358
362
  MCPToolInfo,
359
363
  MCPTransport,
360
364
  MCPOAuthConfig,
365
+ MCPScope,
361
366
  StoredOAuthData,
362
367
  OttoOAuthProviderOptions,
363
368
  CallbackResult,
@@ -36,6 +36,12 @@ const isAllowedZaiModel = (id: string): boolean => {
36
36
  return false;
37
37
  };
38
38
 
39
+ const isAllowedMinimaxModel = (id: string): boolean => {
40
+ if (id === 'MiniMax-M2.5') return true;
41
+ if (id === 'MiniMax-M2.1') return true;
42
+ return false;
43
+ };
44
+
39
45
  const SETU_SOURCES: Array<{
40
46
  id: ProviderId;
41
47
  npm: string;
@@ -66,6 +72,11 @@ const SETU_SOURCES: Array<{
66
72
  npm: '@ai-sdk/openai-compatible',
67
73
  family: 'openai-compatible',
68
74
  },
75
+ {
76
+ id: 'minimax',
77
+ npm: '@ai-sdk/anthropic',
78
+ family: 'minimax',
79
+ },
69
80
  ];
70
81
 
71
82
  function cloneModel(model: ModelInfo): ModelInfo {
@@ -95,6 +106,7 @@ function buildSetuEntry(base: CatalogMap): ProviderCatalogEntry | null {
95
106
  if (id === 'anthropic') return isAllowedAnthropicModel(model.id);
96
107
  if (id === 'google') return isAllowedGoogleModel(model.id);
97
108
  if (id === 'zai') return isAllowedZaiModel(model.id);
109
+ if (id === 'minimax') return isAllowedMinimaxModel(model.id);
98
110
  return true;
99
111
  });
100
112
  return sourceModels.map((model) => {
@@ -3924,6 +3924,30 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
3924
3924
  output: 131072,
3925
3925
  },
3926
3926
  },
3927
+ {
3928
+ id: 'minimax/minimax-m2.5',
3929
+ label: 'MiniMax M2.5',
3930
+ modalities: {
3931
+ input: ['text'],
3932
+ output: ['text'],
3933
+ },
3934
+ toolCall: true,
3935
+ reasoningText: true,
3936
+ attachment: false,
3937
+ temperature: true,
3938
+ releaseDate: '2026-02-12',
3939
+ lastUpdated: '2026-02-12',
3940
+ openWeights: true,
3941
+ cost: {
3942
+ input: 0.3,
3943
+ output: 1.2,
3944
+ cacheRead: 0.03,
3945
+ },
3946
+ limit: {
3947
+ context: 204800,
3948
+ output: 131072,
3949
+ },
3950
+ },
3927
3951
  {
3928
3952
  id: 'mistralai/codestral-2508',
3929
3953
  label: 'Codestral 2508',
@@ -6986,6 +7010,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
6986
7010
  output: 131072,
6987
7011
  },
6988
7012
  },
7013
+ {
7014
+ id: 'glm-5',
7015
+ label: 'GLM-5',
7016
+ modalities: {
7017
+ input: ['text'],
7018
+ output: ['text'],
7019
+ },
7020
+ toolCall: true,
7021
+ reasoningText: true,
7022
+ attachment: false,
7023
+ temperature: true,
7024
+ knowledge: '2025-04',
7025
+ releaseDate: '2026-02-11',
7026
+ lastUpdated: '2026-02-11',
7027
+ openWeights: true,
7028
+ cost: {
7029
+ input: 1,
7030
+ output: 3.2,
7031
+ cacheRead: 0.2,
7032
+ },
7033
+ limit: {
7034
+ context: 204800,
7035
+ output: 131072,
7036
+ },
7037
+ },
6989
7038
  {
6990
7039
  id: 'gpt-5',
6991
7040
  label: 'GPT-5',
@@ -7416,6 +7465,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
7416
7465
  npm: '@ai-sdk/anthropic',
7417
7466
  },
7418
7467
  },
7468
+ {
7469
+ id: 'minimax-m2.5',
7470
+ label: 'MiniMax M2.5',
7471
+ modalities: {
7472
+ input: ['text'],
7473
+ output: ['text'],
7474
+ },
7475
+ toolCall: true,
7476
+ reasoningText: true,
7477
+ attachment: false,
7478
+ temperature: true,
7479
+ knowledge: '2025-01',
7480
+ releaseDate: '2026-02-12',
7481
+ lastUpdated: '2026-02-12',
7482
+ openWeights: true,
7483
+ cost: {
7484
+ input: 0.3,
7485
+ output: 1.2,
7486
+ cacheRead: 0.06,
7487
+ },
7488
+ limit: {
7489
+ context: 204800,
7490
+ output: 131072,
7491
+ },
7492
+ },
7419
7493
  {
7420
7494
  id: 'minimax-m2.5-free',
7421
7495
  label: 'MiniMax M2.5 Free',
@@ -8134,6 +8208,87 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
8134
8208
  api: 'https://api.moonshot.ai/v1',
8135
8209
  doc: 'https://platform.moonshot.ai/docs/api/chat',
8136
8210
  },
8211
+ minimax: {
8212
+ id: 'minimax',
8213
+ models: [
8214
+ {
8215
+ id: 'MiniMax-M2',
8216
+ label: 'MiniMax-M2',
8217
+ modalities: {
8218
+ input: ['text'],
8219
+ output: ['text'],
8220
+ },
8221
+ toolCall: true,
8222
+ reasoningText: true,
8223
+ attachment: false,
8224
+ temperature: true,
8225
+ releaseDate: '2025-10-27',
8226
+ lastUpdated: '2025-10-27',
8227
+ openWeights: true,
8228
+ cost: {
8229
+ input: 0.3,
8230
+ output: 1.2,
8231
+ },
8232
+ limit: {
8233
+ context: 196608,
8234
+ output: 128000,
8235
+ },
8236
+ },
8237
+ {
8238
+ id: 'MiniMax-M2.1',
8239
+ label: 'MiniMax-M2.1',
8240
+ modalities: {
8241
+ input: ['text'],
8242
+ output: ['text'],
8243
+ },
8244
+ toolCall: true,
8245
+ reasoningText: true,
8246
+ attachment: false,
8247
+ temperature: true,
8248
+ releaseDate: '2025-12-23',
8249
+ lastUpdated: '2025-12-23',
8250
+ openWeights: true,
8251
+ cost: {
8252
+ input: 0.3,
8253
+ output: 1.2,
8254
+ },
8255
+ limit: {
8256
+ context: 204800,
8257
+ output: 131072,
8258
+ },
8259
+ },
8260
+ {
8261
+ id: 'MiniMax-M2.5',
8262
+ label: 'MiniMax-M2.5',
8263
+ modalities: {
8264
+ input: ['text'],
8265
+ output: ['text'],
8266
+ },
8267
+ toolCall: true,
8268
+ reasoningText: true,
8269
+ attachment: false,
8270
+ temperature: true,
8271
+ releaseDate: '2026-02-12',
8272
+ lastUpdated: '2026-02-12',
8273
+ openWeights: true,
8274
+ cost: {
8275
+ input: 0.3,
8276
+ output: 1.2,
8277
+ cacheRead: 0.03,
8278
+ cacheWrite: 0.375,
8279
+ },
8280
+ limit: {
8281
+ context: 204800,
8282
+ output: 131072,
8283
+ },
8284
+ },
8285
+ ],
8286
+ label: 'MiniMax (minimax.io)',
8287
+ env: ['MINIMAX_API_KEY'],
8288
+ npm: '@ai-sdk/anthropic',
8289
+ api: 'https://api.minimax.io/anthropic/v1',
8290
+ doc: 'https://platform.minimax.io/docs/guides/quickstart',
8291
+ },
8137
8292
  copilot: {
8138
8293
  id: 'copilot',
8139
8294
  models: [
@@ -11,6 +11,7 @@ const ENV_VARS: Record<ProviderId, string> = {
11
11
  zai: 'ZAI_API_KEY',
12
12
  'zai-coding': 'ZAI_CODING_API_KEY',
13
13
  moonshot: 'MOONSHOT_API_KEY',
14
+ minimax: 'MINIMAX_API_KEY',
14
15
  };
15
16
 
16
17
  export function providerEnvVar(provider: ProviderId): string {
@@ -72,5 +72,7 @@ export { createOpencodeModel } from './opencode-client.ts';
72
72
  export type { OpencodeProviderConfig } from './opencode-client.ts';
73
73
  export { createMoonshotModel } from './moonshot-client.ts';
74
74
  export type { MoonshotProviderConfig } from './moonshot-client.ts';
75
+ export { createMinimaxModel } from './minimax-client.ts';
76
+ export type { MinimaxProviderConfig } from './minimax-client.ts';
75
77
  export { createCopilotFetch, createCopilotModel } from './copilot-client.ts';
76
78
  export type { CopilotOAuthConfig } from './copilot-client.ts';
@@ -0,0 +1,24 @@
1
+ import { createAnthropic } from '@ai-sdk/anthropic';
2
+ import { catalog } from './catalog-merged.ts';
3
+
4
+ export type MinimaxProviderConfig = {
5
+ apiKey?: string;
6
+ baseURL?: string;
7
+ };
8
+
9
+ export function createMinimaxModel(
10
+ model: string,
11
+ config?: MinimaxProviderConfig,
12
+ ) {
13
+ const entry = catalog.minimax;
14
+ const baseURL =
15
+ config?.baseURL || entry?.api || 'https://api.minimax.io/anthropic/v1';
16
+ const apiKey = config?.apiKey || process.env.MINIMAX_API_KEY || '';
17
+
18
+ const instance = createAnthropic({
19
+ apiKey,
20
+ baseURL,
21
+ });
22
+
23
+ return instance(model);
24
+ }
@@ -124,6 +124,7 @@ export type UnderlyingProviderKey =
124
124
  | 'openai'
125
125
  | 'google'
126
126
  | 'moonshot'
127
+ | 'minimax'
127
128
  | 'glm'
128
129
  | 'openai-compatible'
129
130
  | null;
@@ -136,6 +137,7 @@ export function getUnderlyingProviderKey(
136
137
  if (provider === 'openai') return 'openai';
137
138
  if (provider === 'google') return 'google';
138
139
  if (provider === 'moonshot') return 'moonshot';
140
+ if (provider === 'minimax') return 'minimax';
139
141
  if (provider === 'copilot') return 'openai';
140
142
 
141
143
  if (provider === 'zai' || provider === 'zai-coding') return 'glm';
@@ -158,6 +160,7 @@ export function getModelFamily(
158
160
  if (provider === 'openai') return 'openai';
159
161
  if (provider === 'google') return 'google';
160
162
  if (provider === 'moonshot') return 'moonshot';
163
+ if (provider === 'minimax') return 'minimax';
161
164
  if (provider === 'copilot') return 'openai';
162
165
  if (provider === 'zai' || provider === 'zai-coding') return 'glm';
163
166
 
@@ -191,6 +194,9 @@ export function getModelFamily(
191
194
  ) {
192
195
  return 'glm';
193
196
  }
197
+ if (lowerModel.includes('minimax')) {
198
+ return 'minimax';
199
+ }
194
200
  }
195
201
 
196
202
  // 2) Check model's family field in catalog
@@ -11,7 +11,8 @@ export type ProviderId =
11
11
  | 'setu'
12
12
  | 'zai'
13
13
  | 'zai-coding'
14
- | 'moonshot';
14
+ | 'moonshot'
15
+ | 'minimax';
15
16
 
16
17
  /**
17
18
  * Provider family for prompt selection
@@ -21,6 +22,7 @@ export type ProviderFamily =
21
22
  | 'anthropic'
22
23
  | 'google'
23
24
  | 'moonshot'
25
+ | 'minimax'
24
26
  | 'openai-compatible';
25
27
 
26
28
  export type ModelProviderBinding = {