opencode-catpaw-auth 1.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zu1k
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # @zu1k/opencode-catpaw-auth
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@zu1k/opencode-catpaw-auth.svg)](https://www.npmjs.com/package/@zu1k/opencode-catpaw-auth)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ CatPaw AI provider plugin for [OpenCode](https://opencode.ai/) - Auto-auth from mcopilot-cli config
7
+
8
+ ## Features
9
+
10
+ - **Zero-config auth**: Automatically loads API key from `~/.config/mcopilot-cli/.config.yaml`
11
+ - **Multi-model support**: LongCat-Flash-Chat, kimi-k2.5, glm-5, MiniMax-M2.5, claude-sonnet-4.5
12
+ - **Auto header injection**: Automatically injects `x-api-key` and `x-working-dir` headers
13
+ - **OpenAI compatible**: Uses `@ai-sdk/openai-compatible` adapter
14
+
15
+ ## Installation
16
+
17
+ ### Method 1: npm (Recommended)
18
+
19
+ Add the plugin to your OpenCode configuration:
20
+
21
+ ```json
22
+ {
23
+ "$schema": "https://opencode.ai/config.json",
24
+ "plugin": ["@zu1k/opencode-catpaw-auth"]
25
+ }
26
+ ```
27
+
28
+ ### Method 2: Local Development
29
+
30
+ ```bash
31
+ git clone https://github.com/zu1k/opencode-catpaw-auth.git
32
+ cd opencode-catpaw-auth
33
+ bun install
34
+ ```
35
+
36
+ Then use `file://` protocol:
37
+
38
+ ```json
39
+ {
40
+ "plugin": ["file:///absolute/path/to/opencode-catpaw-auth"]
41
+ }
42
+ ```
43
+
44
+ ## Configuration
45
+
46
+ ### 1. Ensure mcopilot-cli config exists
47
+
48
+ The plugin automatically reads the `AUTHORIZATION` field from `~/.config/mcopilot-cli/.config.yaml`:
49
+
50
+ ```yaml
51
+ # ~/.config/mcopilot-cli/.config.yaml
52
+ AUTHORIZATION: "your-api-key-here"
53
+ # other configurations...
54
+ ```
55
+
56
+ ### 2. Connect Provider
57
+
58
+ Start OpenCode:
59
+
60
+ ```bash
61
+ opencode
62
+ ```
63
+
64
+ Run the connect command:
65
+
66
+ ```
67
+ /connect
68
+ ```
69
+
70
+ Select **CatPaw AI** provider, then choose **"Auto-config from mcopilot-cli"** auth method.
71
+
72
+ ### 3. Select Model
73
+
74
+ ```
75
+ /models
76
+ ```
77
+
78
+ Choose from CatPaw AI models:
79
+ - **LongCat Flash Chat** - Fast response
80
+ - **Kimi K2.5** - Long context support (256K)
81
+ - **GLM-5** - Zhipu AI model
82
+ - **MiniMax M2.5** - MiniMax model
83
+ - **Claude Sonnet 4.5** - Anthropic Claude (200K context)
84
+
85
+ ## Supported Models
86
+
87
+ | Model ID | Name | Context Length | Output Length |
88
+ |---------|------|---------------|---------------|
89
+ | `LongCat-Flash-Chat` | LongCat Flash Chat | 128K | 8K |
90
+ | `kimi-k2.5` | Kimi K2.5 | 256K | 8K |
91
+ | `glm-5` | GLM-5 | 128K | 8K |
92
+ | `MiniMax-M2.5` | MiniMax M2.5 | 128K | 8K |
93
+ | `claude-sonnet-4.5` | Claude Sonnet 4.5 | 200K | 8K |
94
+
95
+ ## Troubleshooting
96
+
97
+ ### 1. Check config file
98
+
99
+ Ensure the config file exists and contains AUTHORIZATION:
100
+
101
+ ```bash
102
+ cat ~/.config/mcopilot-cli/.config.yaml
103
+ ```
104
+
105
+ ### 2. Check plugin loading
106
+
107
+ View logs when starting OpenCode:
108
+
109
+ ```bash
110
+ opencode --verbose
111
+ ```
112
+
113
+ ### 3. Test API manually
114
+
115
+ ```bash
116
+ curl https://mcli.sankuai.com/v1/models \
117
+ -H "x-api-key: $(grep AUTHORIZATION ~/.config/mcopilot-cli/.config.yaml | awk '{print $2}')" \
118
+ -H "x-working-dir: $HOME"
119
+ ```
120
+
121
+ ### Common Issues
122
+
123
+ **Q: Plugin shows "Failed to load API key"**
124
+ - Check if `~/.config/mcopilot-cli/.config.yaml` exists
125
+ - Check if file contains `AUTHORIZATION` field
126
+ - Check file permissions
127
+
128
+ **Q: Empty model list**
129
+ - Ensure `/connect` was run and CatPaw AI was selected
130
+ - Check network connection
131
+
132
+ **Q: 401/403 errors**
133
+ - API key may be expired, try updating config file
134
+ - Check if `x-working-dir` header is correct
135
+
136
+ ## Development
137
+
138
+ ### Project Structure
139
+
140
+ ```
141
+ opencode-catpaw-auth/
142
+ ├── index.ts # Plugin entry
143
+ ├── package.json # Dependencies
144
+ ├── tsconfig.json # TypeScript config
145
+ ├── src/
146
+ │ ├── plugin.ts # Core plugin logic
147
+ │ ├── config.ts # Config file reading
148
+ │ ├── request.ts # Request handling
149
+ │ └── types.ts # Type definitions
150
+ └── README.md
151
+ ```
152
+
153
+ ### Local Testing
154
+
155
+ 1. Install dependencies:
156
+
157
+ ```bash
158
+ bun install
159
+ ```
160
+
161
+ 2. Type check:
162
+
163
+ ```bash
164
+ bun run check
165
+ ```
166
+
167
+ 3. Use `file://` protocol in OpenCode config to reference local path
168
+
169
+ ### Scripts
170
+
171
+ ```bash
172
+ bun run dev # Development mode with watch
173
+ bun run check # TypeScript type check
174
+ bun run clean # Clean dependencies
175
+ ```
176
+
177
+ ## How It Works
178
+
179
+ This plugin follows the OpenCode plugin architecture:
180
+
181
+ 1. **Config Hook**: Registers CatPaw as a provider with model definitions
182
+ 2. **Auth Loader**: Reads API key from mcopilot-cli config file
183
+ 3. **Custom Fetch**: Intercepts requests to `mcli.sankuai.com` and injects required headers
184
+
185
+ Inspired by [opencode-gemini-auth](https://github.com/jenslys/opencode-gemini-auth).
186
+
187
+ ## License
188
+
189
+ MIT © [zu1k](https://github.com/zu1k)
190
+
191
+ ## Links
192
+
193
+ - [OpenCode Documentation](https://opencode.ai/docs)
194
+ - [OpenCode Providers](https://opencode.ai/docs/providers)
195
+ - [npm package](https://www.npmjs.com/package/@zu1k/opencode-catpaw-auth)
196
+ - [GitHub Repository](https://github.com/zu1k/opencode-catpaw-auth)
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { CatPawAuthPlugin } from "./src/plugin";
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "opencode-catpaw-auth",
3
+ "module": "index.ts",
4
+ "version": "1.1.0",
5
+ "description": "CatPaw AI provider plugin for OpenCode - Auto-auth from mcopilot-cli config",
6
+ "author": "zu1k",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/zu1k/opencode-catpaw-auth.git"
10
+ },
11
+ "homepage": "https://github.com/zu1k/opencode-catpaw-auth#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/zu1k/opencode-catpaw-auth/issues"
14
+ },
15
+ "keywords": [
16
+ "opencode",
17
+ "opencode-plugin",
18
+ "catpaw",
19
+ "ai",
20
+ "provider"
21
+ ],
22
+ "files": [
23
+ "index.ts",
24
+ "src"
25
+ ],
26
+ "license": "MIT",
27
+ "type": "module",
28
+ "scripts": {
29
+ "dev": "bun run --watch index.ts",
30
+ "check": "tsc --noEmit",
31
+ "clean": "rm -rf node_modules bun.lock"
32
+ },
33
+ "devDependencies": {
34
+ "@types/bun": "latest",
35
+ "@types/js-yaml": "^4.0.9"
36
+ },
37
+ "peerDependencies": {
38
+ "typescript": "^5.9.3"
39
+ },
40
+ "dependencies": {
41
+ "@opencode-ai/plugin": "^1.2.10",
42
+ "js-yaml": "^4.1.0"
43
+ }
44
+ }
package/src/config.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { readFileSync, existsSync } from "fs";
2
+ import { join, homedir } from "path";
3
+ import yaml from "js-yaml";
4
+
5
+ const CONFIG_PATH = join(homedir(), ".config", "mcopilot-cli", ".config.yaml");
6
+
7
+ /**
8
+ * 从 mcopilot-cli 配置文件中加载 API key
9
+ * 配置文件路径: ~/.config/mcopilot-cli/.config.yaml
10
+ */
11
+ export async function loadApiKeyFromConfig(): Promise<string | null> {
12
+ try {
13
+ // 检查配置文件是否存在
14
+ if (!existsSync(CONFIG_PATH)) {
15
+ console.error(`CatPaw config file not found: ${CONFIG_PATH}`);
16
+ return null;
17
+ }
18
+
19
+ // 读取并解析 YAML
20
+ const content = readFileSync(CONFIG_PATH, "utf-8");
21
+ const config = yaml.load(content) as Record<string, unknown>;
22
+
23
+ // 获取 AUTHORIZATION 字段
24
+ const apiKey = config?.AUTHORIZATION;
25
+
26
+ if (typeof apiKey !== "string" || !apiKey) {
27
+ console.error("AUTHORIZATION not found in config file");
28
+ return null;
29
+ }
30
+
31
+ return apiKey;
32
+ } catch (error) {
33
+ console.error("Failed to load CatPaw API key:", error);
34
+ return null;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * 获取配置文件路径(用于调试)
40
+ */
41
+ export function getConfigPath(): string {
42
+ return CONFIG_PATH;
43
+ }
package/src/plugin.ts ADDED
@@ -0,0 +1,116 @@
1
+ import type { PluginContext, PluginResult, Provider } from "./types";
2
+ import { loadApiKeyFromConfig } from "./config";
3
+ import { isCatPawRequest, prepareCatPawRequest } from "./request";
4
+
5
+ export const CATPAW_PROVIDER_ID = "catpaw";
6
+
7
+ /**
8
+ * CatPaw AI Provider Plugin for OpenCode
9
+ *
10
+ * This plugin automatically:
11
+ * 1. Loads API key from ~/.config/mcopilot-cli/.config.yaml
12
+ * 2. Injects required headers (x-api-key, x-working-dir)
13
+ * 3. Supports multiple models: LongCat-Flash-Chat, kimi-k2.5, glm-5, MiniMax-M2.5, claude-sonnet-4.5
14
+ */
15
+ export const CatPawAuthPlugin = async ({ client }: PluginContext): Promise<PluginResult> => ({
16
+ config: async (config) => {
17
+ // 配置 CatPaw provider
18
+ config.provider = config.provider || {};
19
+ config.provider[CATPAW_PROVIDER_ID] = {
20
+ npm: "@ai-sdk/openai-compatible",
21
+ name: "CatPaw AI",
22
+ options: {
23
+ baseURL: "https://mcli.sankuai.com/v1",
24
+ },
25
+ models: {
26
+ "LongCat-Flash-Chat": {
27
+ name: "LongCat Flash Chat",
28
+ limit: {
29
+ context: 256000,
30
+ output: 8192,
31
+ },
32
+ },
33
+ "kimi-k2.5": {
34
+ name: "Kimi K2.5",
35
+ limit: {
36
+ context: 256000,
37
+ output: 8192,
38
+ },
39
+ },
40
+ "glm-5": {
41
+ name: "GLM-5",
42
+ limit: {
43
+ context: 200000,
44
+ output: 8192,
45
+ },
46
+ },
47
+ "MiniMax-M2.5": {
48
+ name: "MiniMax M2.5",
49
+ limit: {
50
+ context: 200000,
51
+ output: 8192,
52
+ },
53
+ },
54
+ "claude-sonnet-4.5": {
55
+ name: "Claude Sonnet 4.5",
56
+ limit: {
57
+ context: 200000,
58
+ output: 8192,
59
+ },
60
+ },
61
+ },
62
+ };
63
+ },
64
+ auth: {
65
+ provider: CATPAW_PROVIDER_ID,
66
+ loader: async (getAuth, provider: Provider) => {
67
+ // 从配置文件加载 API key
68
+ const apiKey = await loadApiKeyFromConfig();
69
+
70
+ if (!apiKey) {
71
+ client.app.log({
72
+ body: {
73
+ service: "catpaw-auth",
74
+ level: "error",
75
+ message: "Failed to load API key from ~/.config/mcopilot-cli/.config.yaml",
76
+ },
77
+ });
78
+ return null;
79
+ }
80
+
81
+ const workingDir = process.env.HOME || process.env.USERPROFILE || "/";
82
+
83
+ client.app.log({
84
+ body: {
85
+ service: "catpaw-auth",
86
+ level: "info",
87
+ message: "CatPaw auth loaded successfully",
88
+ extra: { workingDir },
89
+ },
90
+ });
91
+
92
+ return {
93
+ apiKey: "", // 使用自定义 fetch,不需要标准 apiKey
94
+ async fetch(input, init) {
95
+ // 只处理 CatPaw 相关的请求
96
+ if (!isCatPawRequest(input)) {
97
+ return fetch(input, init);
98
+ }
99
+
100
+ // 准备请求,注入必要的 headers
101
+ const { request, init: requestInit } = prepareCatPawRequest(input, init, apiKey, workingDir);
102
+
103
+ // 发送请求
104
+ return fetch(request, requestInit);
105
+ },
106
+ };
107
+ },
108
+ methods: [
109
+ {
110
+ provider: CATPAW_PROVIDER_ID,
111
+ label: "Auto-config from mcopilot-cli",
112
+ type: "api",
113
+ },
114
+ ],
115
+ },
116
+ });
package/src/request.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 检查请求是否是 CatPaw API 请求
3
+ */
4
+ export function isCatPawRequest(input: RequestInfo): boolean {
5
+ const url = typeof input === "string" ? input : input.url;
6
+ return url.includes("mcli.sankuai.com");
7
+ }
8
+
9
+ /**
10
+ * 准备 CatPaw 请求,注入必要的 headers
11
+ */
12
+ export function prepareCatPawRequest(
13
+ input: RequestInfo,
14
+ init: RequestInit | undefined,
15
+ apiKey: string,
16
+ workingDir: string
17
+ ): { request: RequestInfo; init: RequestInit } {
18
+ // 解析原始 URL
19
+ const url = typeof input === "string" ? input : input.url;
20
+
21
+ // 准备 headers
22
+ const headers = new Headers(init?.headers);
23
+
24
+ // 移除可能冲突的 headers
25
+ headers.delete("host");
26
+ headers.delete("authorization");
27
+
28
+ // 注入 CatPaw 必需的 headers
29
+ headers.set("x-api-key", apiKey);
30
+ headers.set("x-working-dir", workingDir);
31
+
32
+ // 确保 Content-Type 存在
33
+ if (!headers.has("Content-Type") && !headers.has("content-type")) {
34
+ headers.set("Content-Type", "application/json");
35
+ }
36
+
37
+ // 创建新的请求配置
38
+ const requestInit: RequestInit = {
39
+ ...init,
40
+ headers,
41
+ };
42
+
43
+ return {
44
+ request: input,
45
+ init: requestInit,
46
+ };
47
+ }
package/src/types.ts ADDED
@@ -0,0 +1,85 @@
1
+ /**
2
+ * OpenCode Plugin Types
3
+ * 这些类型定义基于 @opencode-ai/plugin
4
+ */
5
+
6
+ export interface PluginContext {
7
+ project: {
8
+ id: string;
9
+ name: string;
10
+ directory: string;
11
+ };
12
+ directory: string;
13
+ worktree: string;
14
+ client: PluginClient;
15
+ }
16
+
17
+ export interface PluginClient {
18
+ app: {
19
+ log: (params: {
20
+ body: {
21
+ service: string;
22
+ level: "debug" | "info" | "warn" | "error";
23
+ message: string;
24
+ extra?: Record<string, unknown>;
25
+ };
26
+ }) => Promise<void>;
27
+ };
28
+ }
29
+
30
+ export interface PluginResult {
31
+ config?: (config: Config) => Promise<void> | void;
32
+ auth?: {
33
+ provider: string;
34
+ loader: (
35
+ getAuth: GetAuth,
36
+ provider: Provider
37
+ ) => Promise<LoaderResult | null>;
38
+ methods: AuthMethod[];
39
+ };
40
+ }
41
+
42
+ export interface Config {
43
+ provider?: Record<string, ProviderConfig>;
44
+ command?: Record<string, CommandConfig>;
45
+ }
46
+
47
+ export interface ProviderConfig {
48
+ npm?: string;
49
+ name?: string;
50
+ options?: Record<string, unknown>;
51
+ models?: Record<string, ModelConfig>;
52
+ }
53
+
54
+ export interface ModelConfig {
55
+ name?: string;
56
+ limit?: {
57
+ context?: number;
58
+ output?: number;
59
+ };
60
+ options?: Record<string, unknown>;
61
+ }
62
+
63
+ export interface CommandConfig {
64
+ description: string;
65
+ template: string;
66
+ }
67
+
68
+ export interface Provider {
69
+ options?: Record<string, unknown>;
70
+ models?: Record<string, ModelConfig>;
71
+ }
72
+
73
+ export interface AuthMethod {
74
+ provider: string;
75
+ label: string;
76
+ type: "oauth" | "api" | "token";
77
+ authorize?: () => Promise<unknown>;
78
+ }
79
+
80
+ export type GetAuth = () => Promise<unknown>;
81
+
82
+ export interface LoaderResult {
83
+ apiKey: string;
84
+ fetch: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
85
+ }