omk-ai 1.2.2 → 1.2.3
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## [1.2.3] - 2026-09-23
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- OAuth device-id resolution honors `OMK_CODING_AGENT_DIR` (the `OMK_AGENT_DIR` name it read was never set), so a custom agent directory applies to the Kimi device-id file. The live-E2E helper resolves the same env var before falling back to the home directory.
|
|
10
|
+
|
|
3
11
|
## [1.2.2] - 2026-09-23
|
|
4
12
|
|
|
5
13
|
### Changed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kimi.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/kimi.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAShG,eAAO,MAAM,2BAA2B,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"kimi.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/kimi.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAShG,eAAO,MAAM,2BAA2B,cAAc,CAAC;AA2JvD,+DAA+D;AAC/D,wBAAsB,aAAa,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyB7F;AAED,uCAAuC;AACvC,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA6BtF;AAED,eAAO,MAAM,qBAAqB,EAAE,sBAMnC,CAAC","sourcesContent":["/**\n * Kimi Code OAuth flow (device authorization grant).\n *\n * Kimi Code is Moonshot AI's coding subscription service.\n * Uses RFC 8628 device authorization flow with a persistent device id.\n */\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.ts\";\n\nconst CLIENT_ID = \"17e5f671-d194-4dfb-9706-5516cb48c098\";\nconst DEFAULT_OAUTH_HOST = \"https://auth.kimi.com\";\nconst DEVICE_ID_FILENAME = \"kimi-device-id\";\nconst DEFAULT_POLL_INTERVAL_MS = 5000;\nconst DEFAULT_DEVICE_FLOW_TTL_MS = 15 * 60 * 1000;\nconst OAUTH_EXPIRY_SKEW_MS = 5 * 60 * 1000;\n\nexport const KIMI_CODE_OAUTH_PROVIDER_ID = \"kimi-code\";\n\ninterface DeviceAuthorizationResponse {\n\tuser_code?: string;\n\tdevice_code?: string;\n\tverification_uri?: string;\n\tverification_uri_complete?: string;\n\texpires_in?: number;\n\tinterval?: number;\n}\n\ninterface TokenResponse {\n\taccess_token?: string;\n\trefresh_token?: string;\n\texpires_in?: number;\n\terror?: string;\n\terror_description?: string;\n\tinterval?: number;\n}\n\nfunction _sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction resolveOAuthHost(): string {\n\treturn process.env.KIMI_CODE_OAUTH_HOST || process.env.KIMI_OAUTH_HOST || DEFAULT_OAUTH_HOST;\n}\n\nfunction formatDeviceModel(system: string, release: string, arch: string): string {\n\treturn [system, release, arch].filter(Boolean).join(\" \").trim();\n}\n\nfunction getDeviceModel(): string {\n\tconst platform = os.platform();\n\tconst release = os.release();\n\tconst arch = os.arch();\n\tif (platform === \"darwin\") return formatDeviceModel(\"macOS\", release, arch);\n\tif (platform === \"win32\") return formatDeviceModel(\"Windows\", release, arch);\n\tconst label = platform === \"linux\" ? \"Linux\" : platform;\n\treturn formatDeviceModel(label, release, arch);\n}\n\nfunction getDeviceIdPath(): string {\n\t// OMK_AGENT_DIR was a dead name nothing set; the canonical override is\n\t// OMK_CODING_AGENT_DIR (kept literal — omk-ai has no coding-agent dep).\n\tconst agentDir =\n\t\tprocess.env.OMK_CODING_AGENT_DIR || process.env.OMK_AGENT_DIR || path.join(os.homedir(), \".omk\", \"agent\");\n\treturn path.join(agentDir, DEVICE_ID_FILENAME);\n}\n\nfunction readPersistedDeviceId(): string | null {\n\ttry {\n\t\tconst filePath = getDeviceIdPath();\n\t\tif (fs.existsSync(filePath)) {\n\t\t\tconst id = fs.readFileSync(filePath, \"utf-8\").trim();\n\t\t\tif (id.length > 0) return id;\n\t\t}\n\t} catch {\n\t\t// Best-effort\n\t}\n\treturn null;\n}\n\nfunction persistDeviceId(deviceId: string): void {\n\ttry {\n\t\tconst filePath = getDeviceIdPath();\n\t\tfs.mkdirSync(path.dirname(filePath), { recursive: true });\n\t\tfs.writeFileSync(filePath, deviceId, \"utf-8\");\n\t} catch {\n\t\t// Best-effort\n\t}\n}\n\nfunction getOrCreateDeviceId(): string {\n\tconst existing = readPersistedDeviceId();\n\tif (existing) return existing;\n\tconst deviceId = crypto.randomUUID();\n\tpersistDeviceId(deviceId);\n\treturn deviceId;\n}\n\nasync function requestDeviceAuthorization(\n\thost: string,\n\tdeviceId: string,\n\tsignal?: AbortSignal,\n): Promise<DeviceAuthorizationResponse> {\n\tconst deviceModel = getDeviceModel();\n\tconst response = await fetch(`${host}/oauth2/device/code`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tscope: \"openid profile email offline_access\",\n\t\t\tdevice_id: deviceId,\n\t\t\tdevice_model: deviceModel,\n\t\t}),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`Kimi device authorization failed: ${response.status} ${text}`);\n\t}\n\n\treturn (await response.json()) as DeviceAuthorizationResponse;\n}\n\nasync function pollKimiToken(\n\thost: string,\n\tdeviceCode: string,\n\tsignal?: AbortSignal,\n): Promise<\n\t| { status: \"pending\" }\n\t| { status: \"slow_down\" }\n\t| { status: \"failed\"; message: string }\n\t| { status: \"complete\"; value: OAuthCredentials }\n> {\n\tconst response = await fetch(`${host}/oauth2/token`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tdevice_code: deviceCode,\n\t\t}),\n\t\tsignal,\n\t});\n\n\tconst payload = (await response.json()) as TokenResponse;\n\n\tif (response.ok && payload.access_token) {\n\t\tconst expiresInMs = (payload.expires_in ?? 3600) * 1000;\n\t\treturn {\n\t\t\tstatus: \"complete\",\n\t\t\tvalue: {\n\t\t\t\taccess: payload.access_token,\n\t\t\t\trefresh: payload.refresh_token || \"\",\n\t\t\t\texpires: Date.now() + expiresInMs - OAUTH_EXPIRY_SKEW_MS,\n\t\t\t},\n\t\t};\n\t}\n\n\tif (payload.error === \"authorization_pending\") return { status: \"pending\" };\n\tif (payload.error === \"slow_down\") return { status: \"slow_down\" };\n\n\tconst detail = payload.error_description || payload.error || String(response.status);\n\tthrow new Error(`Kimi token polling failed: ${detail}`);\n}\n\n/** Log in to Kimi Code using the device authorization flow. */\nexport async function loginKimiCode(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\tconst host = resolveOAuthHost();\n\tconst deviceId = getOrCreateDeviceId();\n\n\tconst device = await requestDeviceAuthorization(host, deviceId, callbacks.signal);\n\n\tif (!device.device_code || !device.user_code) {\n\t\tthrow new Error(\"Kimi device authorization response missing required fields\");\n\t}\n\n\tconst verificationUri =\n\t\tdevice.verification_uri_complete || `${device.verification_uri || host}/activate?user_code=${device.user_code}`;\n\n\tcallbacks.onAuth({\n\t\turl: verificationUri,\n\t\tinstructions: `Enter code: ${device.user_code}`,\n\t});\n\tcallbacks.onProgress?.(\"Waiting for Kimi device authorization...\");\n\n\treturn pollOAuthDeviceCodeFlow({\n\t\tpoll: () => pollKimiToken(host, device.device_code!, callbacks.signal),\n\t\tintervalSeconds: device.interval ?? Math.floor(DEFAULT_POLL_INTERVAL_MS / 1000),\n\t\texpiresInSeconds: device.expires_in ?? Math.floor(DEFAULT_DEVICE_FLOW_TTL_MS / 1000),\n\t\tsignal: callbacks.signal,\n\t});\n}\n\n/** Refresh a Kimi Code OAuth token. */\nexport async function refreshKimiToken(refreshToken: string): Promise<OAuthCredentials> {\n\tconst host = resolveOAuthHost();\n\n\tconst response = await fetch(`${host}/oauth2/token`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t}),\n\t});\n\n\tconst payload = (await response.json()) as TokenResponse;\n\n\tif (!response.ok || !payload.access_token) {\n\t\tconst detail = payload.error_description || payload.error || String(response.status);\n\t\tthrow new Error(`Kimi token refresh failed: ${detail}`);\n\t}\n\n\tconst expiresInMs = (payload.expires_in ?? 3600) * 1000;\n\treturn {\n\t\taccess: payload.access_token,\n\t\trefresh: payload.refresh_token || refreshToken,\n\t\texpires: Date.now() + expiresInMs - OAUTH_EXPIRY_SKEW_MS,\n\t};\n}\n\nexport const kimiCodeOAuthProvider: OAuthProviderInterface = {\n\tid: KIMI_CODE_OAUTH_PROVIDER_ID,\n\tname: \"Kimi Code (Moonshot AI)\",\n\tlogin: loginKimiCode,\n\trefreshToken: (credentials: OAuthCredentials) => refreshKimiToken(credentials.refresh),\n\tgetApiKey: (credentials: OAuthCredentials) => credentials.access,\n};\n"]}
|
package/dist/utils/oauth/kimi.js
CHANGED
|
@@ -37,7 +37,9 @@ function getDeviceModel() {
|
|
|
37
37
|
return formatDeviceModel(label, release, arch);
|
|
38
38
|
}
|
|
39
39
|
function getDeviceIdPath() {
|
|
40
|
-
|
|
40
|
+
// OMK_AGENT_DIR was a dead name nothing set; the canonical override is
|
|
41
|
+
// OMK_CODING_AGENT_DIR (kept literal — omk-ai has no coding-agent dep).
|
|
42
|
+
const agentDir = process.env.OMK_CODING_AGENT_DIR || process.env.OMK_AGENT_DIR || path.join(os.homedir(), ".omk", "agent");
|
|
41
43
|
return path.join(agentDir, DEVICE_ID_FILENAME);
|
|
42
44
|
}
|
|
43
45
|
function readPersistedDeviceId() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kimi.js","sourceRoot":"","sources":["../../../src/utils/oauth/kimi.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Kimi Code OAuth flow (device authorization grant).\n *\n * Kimi Code is Moonshot AI's coding subscription service.\n * Uses RFC 8628 device authorization flow with a persistent device id.\n */\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.ts\";\n\nconst CLIENT_ID = \"17e5f671-d194-4dfb-9706-5516cb48c098\";\nconst DEFAULT_OAUTH_HOST = \"https://auth.kimi.com\";\nconst DEVICE_ID_FILENAME = \"kimi-device-id\";\nconst DEFAULT_POLL_INTERVAL_MS = 5000;\nconst DEFAULT_DEVICE_FLOW_TTL_MS = 15 * 60 * 1000;\nconst OAUTH_EXPIRY_SKEW_MS = 5 * 60 * 1000;\n\nexport const KIMI_CODE_OAUTH_PROVIDER_ID = \"kimi-code\";\n\ninterface DeviceAuthorizationResponse {\n\tuser_code?: string;\n\tdevice_code?: string;\n\tverification_uri?: string;\n\tverification_uri_complete?: string;\n\texpires_in?: number;\n\tinterval?: number;\n}\n\ninterface TokenResponse {\n\taccess_token?: string;\n\trefresh_token?: string;\n\texpires_in?: number;\n\terror?: string;\n\terror_description?: string;\n\tinterval?: number;\n}\n\nfunction _sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction resolveOAuthHost(): string {\n\treturn process.env.KIMI_CODE_OAUTH_HOST || process.env.KIMI_OAUTH_HOST || DEFAULT_OAUTH_HOST;\n}\n\nfunction formatDeviceModel(system: string, release: string, arch: string): string {\n\treturn [system, release, arch].filter(Boolean).join(\" \").trim();\n}\n\nfunction getDeviceModel(): string {\n\tconst platform = os.platform();\n\tconst release = os.release();\n\tconst arch = os.arch();\n\tif (platform === \"darwin\") return formatDeviceModel(\"macOS\", release, arch);\n\tif (platform === \"win32\") return formatDeviceModel(\"Windows\", release, arch);\n\tconst label = platform === \"linux\" ? \"Linux\" : platform;\n\treturn formatDeviceModel(label, release, arch);\n}\n\nfunction getDeviceIdPath(): string {\n\tconst agentDir = process.env.OMK_AGENT_DIR || path.join(os.homedir(), \".omk\", \"agent\");\n\treturn path.join(agentDir, DEVICE_ID_FILENAME);\n}\n\nfunction readPersistedDeviceId(): string | null {\n\ttry {\n\t\tconst filePath = getDeviceIdPath();\n\t\tif (fs.existsSync(filePath)) {\n\t\t\tconst id = fs.readFileSync(filePath, \"utf-8\").trim();\n\t\t\tif (id.length > 0) return id;\n\t\t}\n\t} catch {\n\t\t// Best-effort\n\t}\n\treturn null;\n}\n\nfunction persistDeviceId(deviceId: string): void {\n\ttry {\n\t\tconst filePath = getDeviceIdPath();\n\t\tfs.mkdirSync(path.dirname(filePath), { recursive: true });\n\t\tfs.writeFileSync(filePath, deviceId, \"utf-8\");\n\t} catch {\n\t\t// Best-effort\n\t}\n}\n\nfunction getOrCreateDeviceId(): string {\n\tconst existing = readPersistedDeviceId();\n\tif (existing) return existing;\n\tconst deviceId = crypto.randomUUID();\n\tpersistDeviceId(deviceId);\n\treturn deviceId;\n}\n\nasync function requestDeviceAuthorization(\n\thost: string,\n\tdeviceId: string,\n\tsignal?: AbortSignal,\n): Promise<DeviceAuthorizationResponse> {\n\tconst deviceModel = getDeviceModel();\n\tconst response = await fetch(`${host}/oauth2/device/code`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tscope: \"openid profile email offline_access\",\n\t\t\tdevice_id: deviceId,\n\t\t\tdevice_model: deviceModel,\n\t\t}),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`Kimi device authorization failed: ${response.status} ${text}`);\n\t}\n\n\treturn (await response.json()) as DeviceAuthorizationResponse;\n}\n\nasync function pollKimiToken(\n\thost: string,\n\tdeviceCode: string,\n\tsignal?: AbortSignal,\n): Promise<\n\t| { status: \"pending\" }\n\t| { status: \"slow_down\" }\n\t| { status: \"failed\"; message: string }\n\t| { status: \"complete\"; value: OAuthCredentials }\n> {\n\tconst response = await fetch(`${host}/oauth2/token`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tdevice_code: deviceCode,\n\t\t}),\n\t\tsignal,\n\t});\n\n\tconst payload = (await response.json()) as TokenResponse;\n\n\tif (response.ok && payload.access_token) {\n\t\tconst expiresInMs = (payload.expires_in ?? 3600) * 1000;\n\t\treturn {\n\t\t\tstatus: \"complete\",\n\t\t\tvalue: {\n\t\t\t\taccess: payload.access_token,\n\t\t\t\trefresh: payload.refresh_token || \"\",\n\t\t\t\texpires: Date.now() + expiresInMs - OAUTH_EXPIRY_SKEW_MS,\n\t\t\t},\n\t\t};\n\t}\n\n\tif (payload.error === \"authorization_pending\") return { status: \"pending\" };\n\tif (payload.error === \"slow_down\") return { status: \"slow_down\" };\n\n\tconst detail = payload.error_description || payload.error || String(response.status);\n\tthrow new Error(`Kimi token polling failed: ${detail}`);\n}\n\n/** Log in to Kimi Code using the device authorization flow. */\nexport async function loginKimiCode(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\tconst host = resolveOAuthHost();\n\tconst deviceId = getOrCreateDeviceId();\n\n\tconst device = await requestDeviceAuthorization(host, deviceId, callbacks.signal);\n\n\tif (!device.device_code || !device.user_code) {\n\t\tthrow new Error(\"Kimi device authorization response missing required fields\");\n\t}\n\n\tconst verificationUri =\n\t\tdevice.verification_uri_complete || `${device.verification_uri || host}/activate?user_code=${device.user_code}`;\n\n\tcallbacks.onAuth({\n\t\turl: verificationUri,\n\t\tinstructions: `Enter code: ${device.user_code}`,\n\t});\n\tcallbacks.onProgress?.(\"Waiting for Kimi device authorization...\");\n\n\treturn pollOAuthDeviceCodeFlow({\n\t\tpoll: () => pollKimiToken(host, device.device_code!, callbacks.signal),\n\t\tintervalSeconds: device.interval ?? Math.floor(DEFAULT_POLL_INTERVAL_MS / 1000),\n\t\texpiresInSeconds: device.expires_in ?? Math.floor(DEFAULT_DEVICE_FLOW_TTL_MS / 1000),\n\t\tsignal: callbacks.signal,\n\t});\n}\n\n/** Refresh a Kimi Code OAuth token. */\nexport async function refreshKimiToken(refreshToken: string): Promise<OAuthCredentials> {\n\tconst host = resolveOAuthHost();\n\n\tconst response = await fetch(`${host}/oauth2/token`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t}),\n\t});\n\n\tconst payload = (await response.json()) as TokenResponse;\n\n\tif (!response.ok || !payload.access_token) {\n\t\tconst detail = payload.error_description || payload.error || String(response.status);\n\t\tthrow new Error(`Kimi token refresh failed: ${detail}`);\n\t}\n\n\tconst expiresInMs = (payload.expires_in ?? 3600) * 1000;\n\treturn {\n\t\taccess: payload.access_token,\n\t\trefresh: payload.refresh_token || refreshToken,\n\t\texpires: Date.now() + expiresInMs - OAUTH_EXPIRY_SKEW_MS,\n\t};\n}\n\nexport const kimiCodeOAuthProvider: OAuthProviderInterface = {\n\tid: KIMI_CODE_OAUTH_PROVIDER_ID,\n\tname: \"Kimi Code (Moonshot AI)\",\n\tlogin: loginKimiCode,\n\trefreshToken: (credentials: OAuthCredentials) => refreshKimiToken(credentials.refresh),\n\tgetApiKey: (credentials: OAuthCredentials) => credentials.access,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"kimi.js","sourceRoot":"","sources":["../../../src/utils/oauth/kimi.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Kimi Code OAuth flow (device authorization grant).\n *\n * Kimi Code is Moonshot AI's coding subscription service.\n * Uses RFC 8628 device authorization flow with a persistent device id.\n */\nimport * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { pollOAuthDeviceCodeFlow } from \"./device-code.ts\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.ts\";\n\nconst CLIENT_ID = \"17e5f671-d194-4dfb-9706-5516cb48c098\";\nconst DEFAULT_OAUTH_HOST = \"https://auth.kimi.com\";\nconst DEVICE_ID_FILENAME = \"kimi-device-id\";\nconst DEFAULT_POLL_INTERVAL_MS = 5000;\nconst DEFAULT_DEVICE_FLOW_TTL_MS = 15 * 60 * 1000;\nconst OAUTH_EXPIRY_SKEW_MS = 5 * 60 * 1000;\n\nexport const KIMI_CODE_OAUTH_PROVIDER_ID = \"kimi-code\";\n\ninterface DeviceAuthorizationResponse {\n\tuser_code?: string;\n\tdevice_code?: string;\n\tverification_uri?: string;\n\tverification_uri_complete?: string;\n\texpires_in?: number;\n\tinterval?: number;\n}\n\ninterface TokenResponse {\n\taccess_token?: string;\n\trefresh_token?: string;\n\texpires_in?: number;\n\terror?: string;\n\terror_description?: string;\n\tinterval?: number;\n}\n\nfunction _sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction resolveOAuthHost(): string {\n\treturn process.env.KIMI_CODE_OAUTH_HOST || process.env.KIMI_OAUTH_HOST || DEFAULT_OAUTH_HOST;\n}\n\nfunction formatDeviceModel(system: string, release: string, arch: string): string {\n\treturn [system, release, arch].filter(Boolean).join(\" \").trim();\n}\n\nfunction getDeviceModel(): string {\n\tconst platform = os.platform();\n\tconst release = os.release();\n\tconst arch = os.arch();\n\tif (platform === \"darwin\") return formatDeviceModel(\"macOS\", release, arch);\n\tif (platform === \"win32\") return formatDeviceModel(\"Windows\", release, arch);\n\tconst label = platform === \"linux\" ? \"Linux\" : platform;\n\treturn formatDeviceModel(label, release, arch);\n}\n\nfunction getDeviceIdPath(): string {\n\t// OMK_AGENT_DIR was a dead name nothing set; the canonical override is\n\t// OMK_CODING_AGENT_DIR (kept literal — omk-ai has no coding-agent dep).\n\tconst agentDir =\n\t\tprocess.env.OMK_CODING_AGENT_DIR || process.env.OMK_AGENT_DIR || path.join(os.homedir(), \".omk\", \"agent\");\n\treturn path.join(agentDir, DEVICE_ID_FILENAME);\n}\n\nfunction readPersistedDeviceId(): string | null {\n\ttry {\n\t\tconst filePath = getDeviceIdPath();\n\t\tif (fs.existsSync(filePath)) {\n\t\t\tconst id = fs.readFileSync(filePath, \"utf-8\").trim();\n\t\t\tif (id.length > 0) return id;\n\t\t}\n\t} catch {\n\t\t// Best-effort\n\t}\n\treturn null;\n}\n\nfunction persistDeviceId(deviceId: string): void {\n\ttry {\n\t\tconst filePath = getDeviceIdPath();\n\t\tfs.mkdirSync(path.dirname(filePath), { recursive: true });\n\t\tfs.writeFileSync(filePath, deviceId, \"utf-8\");\n\t} catch {\n\t\t// Best-effort\n\t}\n}\n\nfunction getOrCreateDeviceId(): string {\n\tconst existing = readPersistedDeviceId();\n\tif (existing) return existing;\n\tconst deviceId = crypto.randomUUID();\n\tpersistDeviceId(deviceId);\n\treturn deviceId;\n}\n\nasync function requestDeviceAuthorization(\n\thost: string,\n\tdeviceId: string,\n\tsignal?: AbortSignal,\n): Promise<DeviceAuthorizationResponse> {\n\tconst deviceModel = getDeviceModel();\n\tconst response = await fetch(`${host}/oauth2/device/code`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tscope: \"openid profile email offline_access\",\n\t\t\tdevice_id: deviceId,\n\t\t\tdevice_model: deviceModel,\n\t\t}),\n\t\tsignal,\n\t});\n\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new Error(`Kimi device authorization failed: ${response.status} ${text}`);\n\t}\n\n\treturn (await response.json()) as DeviceAuthorizationResponse;\n}\n\nasync function pollKimiToken(\n\thost: string,\n\tdeviceCode: string,\n\tsignal?: AbortSignal,\n): Promise<\n\t| { status: \"pending\" }\n\t| { status: \"slow_down\" }\n\t| { status: \"failed\"; message: string }\n\t| { status: \"complete\"; value: OAuthCredentials }\n> {\n\tconst response = await fetch(`${host}/oauth2/token`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tdevice_code: deviceCode,\n\t\t}),\n\t\tsignal,\n\t});\n\n\tconst payload = (await response.json()) as TokenResponse;\n\n\tif (response.ok && payload.access_token) {\n\t\tconst expiresInMs = (payload.expires_in ?? 3600) * 1000;\n\t\treturn {\n\t\t\tstatus: \"complete\",\n\t\t\tvalue: {\n\t\t\t\taccess: payload.access_token,\n\t\t\t\trefresh: payload.refresh_token || \"\",\n\t\t\t\texpires: Date.now() + expiresInMs - OAUTH_EXPIRY_SKEW_MS,\n\t\t\t},\n\t\t};\n\t}\n\n\tif (payload.error === \"authorization_pending\") return { status: \"pending\" };\n\tif (payload.error === \"slow_down\") return { status: \"slow_down\" };\n\n\tconst detail = payload.error_description || payload.error || String(response.status);\n\tthrow new Error(`Kimi token polling failed: ${detail}`);\n}\n\n/** Log in to Kimi Code using the device authorization flow. */\nexport async function loginKimiCode(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\tconst host = resolveOAuthHost();\n\tconst deviceId = getOrCreateDeviceId();\n\n\tconst device = await requestDeviceAuthorization(host, deviceId, callbacks.signal);\n\n\tif (!device.device_code || !device.user_code) {\n\t\tthrow new Error(\"Kimi device authorization response missing required fields\");\n\t}\n\n\tconst verificationUri =\n\t\tdevice.verification_uri_complete || `${device.verification_uri || host}/activate?user_code=${device.user_code}`;\n\n\tcallbacks.onAuth({\n\t\turl: verificationUri,\n\t\tinstructions: `Enter code: ${device.user_code}`,\n\t});\n\tcallbacks.onProgress?.(\"Waiting for Kimi device authorization...\");\n\n\treturn pollOAuthDeviceCodeFlow({\n\t\tpoll: () => pollKimiToken(host, device.device_code!, callbacks.signal),\n\t\tintervalSeconds: device.interval ?? Math.floor(DEFAULT_POLL_INTERVAL_MS / 1000),\n\t\texpiresInSeconds: device.expires_in ?? Math.floor(DEFAULT_DEVICE_FLOW_TTL_MS / 1000),\n\t\tsignal: callbacks.signal,\n\t});\n}\n\n/** Refresh a Kimi Code OAuth token. */\nexport async function refreshKimiToken(refreshToken: string): Promise<OAuthCredentials> {\n\tconst host = resolveOAuthHost();\n\n\tconst response = await fetch(`${host}/oauth2/token`, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t\tbody: new URLSearchParams({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t}),\n\t});\n\n\tconst payload = (await response.json()) as TokenResponse;\n\n\tif (!response.ok || !payload.access_token) {\n\t\tconst detail = payload.error_description || payload.error || String(response.status);\n\t\tthrow new Error(`Kimi token refresh failed: ${detail}`);\n\t}\n\n\tconst expiresInMs = (payload.expires_in ?? 3600) * 1000;\n\treturn {\n\t\taccess: payload.access_token,\n\t\trefresh: payload.refresh_token || refreshToken,\n\t\texpires: Date.now() + expiresInMs - OAUTH_EXPIRY_SKEW_MS,\n\t};\n}\n\nexport const kimiCodeOAuthProvider: OAuthProviderInterface = {\n\tid: KIMI_CODE_OAUTH_PROVIDER_ID,\n\tname: \"Kimi Code (Moonshot AI)\",\n\tlogin: loginKimiCode,\n\trefreshToken: (credentials: OAuthCredentials) => refreshKimiToken(credentials.refresh),\n\tgetApiKey: (credentials: OAuthCredentials) => credentials.access,\n};\n"]}
|