@gajae-code/ai 0.5.2 → 0.5.4

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.
@@ -4,23 +4,44 @@
4
4
  * Xiaomi MiMo provides OpenAI-compatible models via
5
5
  * https://api.xiaomimimo.com/v1.
6
6
  *
7
- * This is not OAuth - it's a simple API key flow:
8
- * 1. Open browser to Xiaomi MiMo API key console
9
- * 2. User copies their API key
10
- * 3. User pastes the API key into the CLI
7
+ * Standard Xiaomi login opens the pay-as-you-go API key console. Token Plan
8
+ * login opens plan management so users copy the regional `tp-...` key.
11
9
  */
12
10
 
11
+ import type { FetchImpl } from "../../types";
13
12
  import type { OAuthController } from "./types";
14
13
 
15
14
  const PROVIDER_ID = "xiaomi";
16
15
  const PROVIDER_NAME = "Xiaomi MiMo";
17
16
  const STANDARD_AUTH_URL = "https://platform.xiaomimimo.com/#/console/api-keys";
17
+ const TOKEN_PLAN_AUTH_URL = "https://platform.xiaomimimo.com/console/plan-manage";
18
18
  const STANDARD_API_BASE_URL = "https://api.xiaomimimo.com/v1";
19
- const TOKEN_PLAN_SGP_API_BASE_URL = "https://token-plan-sgp.xiaomimimo.com/v1";
20
- const TOKEN_PLAN_AMS_API_BASE_URL = "https://token-plan-ams.xiaomimimo.com/v1";
21
19
  const TOKEN_PLAN_KEY_PREFIX = "tp-";
22
20
  const STANDARD_VALIDATION_MODEL = "mimo-v2-flash";
23
21
  const TOKEN_PLAN_VALIDATION_MODEL = "mimo-v2.5";
22
+ const TOKEN_PLAN_SGP_API_BASE_URL = "https://token-plan-sgp.xiaomimimo.com/v1";
23
+ const TOKEN_PLAN_AMS_API_BASE_URL = "https://token-plan-ams.xiaomimimo.com/v1";
24
+ const TOKEN_PLAN_CN_API_BASE_URL = "https://token-plan-cn.xiaomimimo.com/v1";
25
+
26
+ /** Region codes accepted by the Xiaomi Token Plan login flow. */
27
+ export type XiaomiTokenPlanRegion = "sgp" | "ams" | "cn";
28
+
29
+ type XiaomiValidationEndpoint = {
30
+ baseUrl: string;
31
+ model: string;
32
+ };
33
+
34
+ const TOKEN_PLAN_VALIDATION_ENDPOINTS: Record<XiaomiTokenPlanRegion, XiaomiValidationEndpoint> = {
35
+ sgp: { baseUrl: TOKEN_PLAN_SGP_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
36
+ ams: { baseUrl: TOKEN_PLAN_AMS_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
37
+ cn: { baseUrl: TOKEN_PLAN_CN_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
38
+ };
39
+
40
+ const TOKEN_PLAN_REGION_NAMES: Record<XiaomiTokenPlanRegion, string> = {
41
+ sgp: "Singapore",
42
+ ams: "Europe",
43
+ cn: "China",
44
+ };
24
45
 
25
46
  function isTokenPlanKey(apiKey: string): boolean {
26
47
  return apiKey.startsWith(TOKEN_PLAN_KEY_PREFIX);
@@ -28,15 +49,24 @@ function isTokenPlanKey(apiKey: string): boolean {
28
49
 
29
50
  const VALIDATION_TIMEOUT_MS = 15_000;
30
51
 
31
- async function validateXiaomiApiKey(apiKey: string, signal?: AbortSignal): Promise<void> {
32
- // For token-plan keys try SGP first, then AMS as fallback.
33
- // Standard sk- keys only hit the one endpoint.
34
- const endpoints = isTokenPlanKey(apiKey)
35
- ? [
36
- { baseUrl: TOKEN_PLAN_SGP_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
37
- { baseUrl: TOKEN_PLAN_AMS_API_BASE_URL, model: TOKEN_PLAN_VALIDATION_MODEL },
38
- ]
39
- : [{ baseUrl: STANDARD_API_BASE_URL, model: STANDARD_VALIDATION_MODEL }];
52
+ async function validateXiaomiApiKey(
53
+ apiKey: string,
54
+ tokenPlanRegion: XiaomiTokenPlanRegion | undefined,
55
+ signal?: AbortSignal,
56
+ fetchOverride?: FetchImpl,
57
+ ): Promise<void> {
58
+ const fetchImpl = fetchOverride ?? fetch;
59
+ // Region-specific Token Plan logins must validate against the selected
60
+ // cluster. Generic Xiaomi login keeps the historical SGP → AMS → CN fallback.
61
+ const endpoints = tokenPlanRegion
62
+ ? [TOKEN_PLAN_VALIDATION_ENDPOINTS[tokenPlanRegion]]
63
+ : isTokenPlanKey(apiKey)
64
+ ? [
65
+ TOKEN_PLAN_VALIDATION_ENDPOINTS.sgp,
66
+ TOKEN_PLAN_VALIDATION_ENDPOINTS.ams,
67
+ TOKEN_PLAN_VALIDATION_ENDPOINTS.cn,
68
+ ]
69
+ : [{ baseUrl: STANDARD_API_BASE_URL, model: STANDARD_VALIDATION_MODEL }];
40
70
 
41
71
  let lastError: Error | null = null;
42
72
 
@@ -47,11 +77,11 @@ async function validateXiaomiApiKey(apiKey: string, signal?: AbortSignal): Promi
47
77
  const timeoutSignal = AbortSignal.timeout(VALIDATION_TIMEOUT_MS);
48
78
  const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
49
79
  try {
50
- const response = await fetch(`${ep.baseUrl}/chat/completions`, {
80
+ const response = await fetchImpl(`${ep.baseUrl}/chat/completions`, {
51
81
  method: "POST",
52
82
  headers: {
53
83
  "Content-Type": "application/json",
54
- "x-api-key": apiKey,
84
+ Authorization: `Bearer ${apiKey}`,
55
85
  },
56
86
  body: JSON.stringify({
57
87
  model: ep.model,
@@ -112,6 +142,7 @@ async function validateXiaomiApiKey(apiKey: string, signal?: AbortSignal): Promi
112
142
  * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
113
143
  */
114
144
  export async function loginXiaomi(options: OAuthController): Promise<string> {
145
+ const fetchImpl = options.fetch ?? fetch;
115
146
  if (!options.onPrompt) {
116
147
  throw new Error(`${PROVIDER_NAME} login requires onPrompt callback`);
117
148
  }
@@ -132,6 +163,37 @@ export async function loginXiaomi(options: OAuthController): Promise<string> {
132
163
  }
133
164
 
134
165
  options.onProgress?.(`Validating ${PROVIDER_ID} API key...`);
135
- await validateXiaomiApiKey(trimmed, options.signal);
166
+ await validateXiaomiApiKey(trimmed, undefined, options.signal, fetchImpl);
167
+ return trimmed;
168
+ }
169
+
170
+ /**
171
+ * Login to a regional Xiaomi Token Plan endpoint.
172
+ *
173
+ * Prompts for a token-plan API key and validates it against the selected region.
174
+ */
175
+ export async function loginXiaomiTokenPlan(options: OAuthController, region: XiaomiTokenPlanRegion): Promise<string> {
176
+ const fetchImpl = options.fetch ?? fetch;
177
+ if (!options.onPrompt) {
178
+ throw new Error(`Xiaomi Token Plan (${TOKEN_PLAN_REGION_NAMES[region]}) login requires onPrompt callback`);
179
+ }
180
+ options.onAuth?.({
181
+ url: TOKEN_PLAN_AUTH_URL,
182
+ instructions: `Copy your token-plan API key for the ${TOKEN_PLAN_REGION_NAMES[region]} region`,
183
+ });
184
+ const apiKey = await options.onPrompt({
185
+ message: `Paste your Xiaomi Token Plan ${TOKEN_PLAN_REGION_NAMES[region]} API key (tp-...)`,
186
+ placeholder: "tp-...",
187
+ });
188
+ if (options.signal?.aborted) {
189
+ throw new Error("Login cancelled");
190
+ }
191
+ const trimmed = apiKey.trim();
192
+ if (!trimmed) {
193
+ throw new Error("API key is required");
194
+ }
195
+
196
+ options.onProgress?.(`Validating Xiaomi Token Plan (${TOKEN_PLAN_REGION_NAMES[region]}) API key...`);
197
+ await validateXiaomiApiKey(trimmed, region, options.signal, fetchImpl);
136
198
  return trimmed;
137
199
  }