@x-all-in-one/coding-helper 0.0.7 → 0.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.
Files changed (63) hide show
  1. package/README.md +90 -57
  2. package/dist/commands/auth.js +1 -1
  3. package/dist/commands/config.js +11 -7
  4. package/dist/commands/doctor.js +2 -2
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/lib/config.d.ts +3 -0
  8. package/dist/lib/config.js +6 -0
  9. package/dist/lib/model-service.d.ts +49 -0
  10. package/dist/lib/model-service.js +129 -0
  11. package/dist/lib/plugins/ccline-plugin.d.ts +40 -0
  12. package/dist/lib/plugins/ccline-plugin.js +116 -0
  13. package/dist/lib/plugins/index.d.ts +2 -0
  14. package/dist/lib/plugins/index.js +3 -0
  15. package/dist/lib/plugins/oh-my-opencode-plugin.d.ts +41 -0
  16. package/dist/lib/plugins/oh-my-opencode-plugin.js +123 -0
  17. package/dist/lib/tool-manager.d.ts +70 -2
  18. package/dist/lib/tool-manager.js +149 -113
  19. package/dist/lib/tools/base-tool.d.ts +73 -0
  20. package/dist/lib/tools/base-tool.js +65 -0
  21. package/dist/lib/{claude-code-manager.d.ts → tools/claude-code-tool.d.ts} +31 -4
  22. package/dist/lib/{claude-code-manager.js → tools/claude-code-tool.js} +52 -13
  23. package/dist/lib/tools/codex-tool.d.ts +58 -0
  24. package/dist/lib/tools/codex-tool.js +142 -0
  25. package/dist/lib/tools/index.d.ts +4 -0
  26. package/dist/lib/tools/index.js +6 -0
  27. package/dist/lib/{opencode-manager.d.ts → tools/opencode-tool.d.ts} +35 -7
  28. package/dist/lib/{opencode-manager.js → tools/opencode-tool.js} +63 -14
  29. package/dist/lib/wizard/flows/api-key-flow.d.ts +17 -0
  30. package/dist/lib/wizard/flows/api-key-flow.js +120 -0
  31. package/dist/lib/wizard/flows/index.d.ts +3 -0
  32. package/dist/lib/wizard/flows/index.js +3 -0
  33. package/dist/lib/wizard/flows/language-flow.d.ts +14 -0
  34. package/dist/lib/wizard/flows/language-flow.js +60 -0
  35. package/dist/lib/wizard/flows/model-selection-flow.d.ts +48 -0
  36. package/dist/lib/wizard/flows/model-selection-flow.js +148 -0
  37. package/dist/lib/wizard/index.d.ts +5 -0
  38. package/dist/lib/wizard/index.js +10 -0
  39. package/dist/lib/wizard/installers/bun-installer.d.ts +24 -0
  40. package/dist/lib/wizard/installers/bun-installer.js +90 -0
  41. package/dist/lib/wizard/installers/index.d.ts +1 -0
  42. package/dist/lib/wizard/installers/index.js +1 -0
  43. package/dist/lib/wizard/menus/index.d.ts +3 -0
  44. package/dist/lib/wizard/menus/index.js +3 -0
  45. package/dist/lib/wizard/menus/main-menu.d.ts +25 -0
  46. package/dist/lib/wizard/menus/main-menu.js +105 -0
  47. package/dist/lib/wizard/menus/plugin-menu.d.ts +26 -0
  48. package/dist/lib/wizard/menus/plugin-menu.js +131 -0
  49. package/dist/lib/wizard/menus/tool-menu.d.ts +59 -0
  50. package/dist/lib/wizard/menus/tool-menu.js +517 -0
  51. package/dist/lib/wizard/ui/index.d.ts +2 -0
  52. package/dist/lib/wizard/ui/index.js +2 -0
  53. package/dist/lib/wizard/ui/prompt-helper.d.ts +27 -0
  54. package/dist/lib/wizard/ui/prompt-helper.js +63 -0
  55. package/dist/lib/wizard/ui/ui-renderer.d.ts +24 -0
  56. package/dist/lib/wizard/ui/ui-renderer.js +71 -0
  57. package/dist/lib/wizard/wizard.d.ts +63 -0
  58. package/dist/lib/wizard/wizard.js +209 -0
  59. package/dist/lib/wizard.d.ts +5 -51
  60. package/dist/lib/wizard.js +5 -1017
  61. package/dist/locales/en_US.json +32 -1
  62. package/dist/locales/zh_CN.json +32 -1
  63. package/package.json +5 -2
package/README.md CHANGED
@@ -1,91 +1,124 @@
1
- # coding-helper
1
+ # Coding Helper
2
2
 
3
- ## Getting started
3
+ CLI 工具,用于配置 AI 编程助手(Claude Code、OpenCode、Codex)的 API 端点和模型。
4
4
 
5
- To make it easy for you to get started with GitLab, here's a list of recommended next steps.
5
+ ## 安装
6
6
 
7
- Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
7
+ ```bash
8
+ npm install -g @x-all-in-one/coding-helper
9
+ # 或
10
+ pnpm add -g @x-all-in-one/coding-helper
11
+ ```
8
12
 
9
- ## Add your files
13
+ ## 使用
10
14
 
11
- - [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
12
- - [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
15
+ ```bash
16
+ # 启动交互式配置向导
17
+ chelper
13
18
 
19
+ # 查看帮助
20
+ chelper --help
21
+
22
+ # 切换语言
23
+ chelper lang
14
24
  ```
15
- cd existing_repo
16
- git remote add origin https://gitlab.yeaosound.com/xaio-ai/coding-helper.git
17
- git branch -M main
18
- git push -uf origin main
19
- ```
20
25
 
21
- ## Integrate with your tools
26
+ ## 开发
27
+
28
+ ### 环境要求
29
+
30
+ - Node.js >= 18
31
+ - pnpm >= 10
22
32
 
23
- - [ ] [Set up project integrations](https://gitlab.yeaosound.com/xaio-ai/coding-helper/-/settings/integrations)
33
+ ### 常用命令
24
34
 
25
- ## Collaborate with your team
35
+ ```bash
36
+ pnpm install # 安装依赖
37
+ pnpm dev # 开发模式(热重载)
38
+ pnpm build # 构建项目
39
+ pnpm lint # 代码检查
40
+ pnpm lint:fix # 自动修复
41
+ ```
26
42
 
27
- - [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
28
- - [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
29
- - [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
30
- - [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
31
- - [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
43
+ ### 目录结构
32
44
 
33
- ## Test and Deploy
45
+ ```
46
+ src/
47
+ ├── cli.ts # 入口文件
48
+ ├── lib/
49
+ │ ├── config.ts # 配置管理 (~/.chelper/config.yaml)
50
+ │ ├── i18n.ts # 国际化
51
+ │ ├── tool-manager.ts # 工具注册管理
52
+ │ ├── wizard/ # 交互式 TUI
53
+ │ ├── tools/ # 工具实现 (Claude Code, OpenCode, Codex)
54
+ │ └── plugins/ # 插件实现
55
+ └── locales/ # 语言文件
56
+ ```
34
57
 
35
- Use the built-in continuous integration in GitLab.
58
+ ## 发版流程
36
59
 
37
- - [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
38
- - [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
39
- - [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
40
- - [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
41
- - [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
60
+ 本项目使用 [Changeset](https://github.com/changesets/changesets) 进行版本管理。
42
61
 
43
- ***
62
+ ### 1. 创建 Changeset
44
63
 
45
- # Editing this README
64
+ 开发完成后,运行以下命令声明版本变更:
46
65
 
47
- When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
66
+ ```bash
67
+ pnpm changeset
68
+ ```
48
69
 
49
- ## Suggestions for a good README
70
+ 交互式选择:
71
+ - **patch** - 修复 bug (0.0.x)
72
+ - **minor** - 新功能 (0.x.0)
73
+ - **major** - 破坏性变更 (x.0.0)
50
74
 
51
- Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
75
+ 这会在 `.changeset/` 目录下生成一个 markdown 文件。
52
76
 
53
- ## Name
54
- Choose a self-explaining name for your project.
77
+ ### 2. 提交代码
55
78
 
56
- ## Description
57
- Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
79
+ ```bash
80
+ git add .
81
+ git commit -m "feat: 添加新功能"
82
+ git push origin feature/xxx
83
+ ```
58
84
 
59
- ## Badges
60
- On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
85
+ ### 3. 创建 MR 并合并
61
86
 
62
- ## Visuals
63
- Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
87
+ 创建 Merge Request 合并到 `main` 分支。
64
88
 
65
- ## Installation
66
- Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
89
+ ### 4. 自动发版
67
90
 
68
- ## Usage
69
- Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
91
+ 合并后,GitLab CI 会自动:
92
+ 1. 执行 `changeset version` 更新版本号和 CHANGELOG
93
+ 2. 执行 `changeset publish` 发布到 npm
94
+ 3. 提交版本变更并推送
70
95
 
71
- ## Support
72
- Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
96
+ ## Commit 规范
73
97
 
74
- ## Roadmap
75
- If you have ideas for releases in the future, it is a good idea to list them in the README.
98
+ 使用 [Conventional Commits](https://www.conventionalcommits.org/) 规范:
76
99
 
77
- ## Contributing
78
- State if you are open to contributions and what your requirements are for accepting them.
100
+ | 类型 | 说明 |
101
+ |------|------|
102
+ | `feat` | 新功能 |
103
+ | `fix` | 修复 bug |
104
+ | `docs` | 文档变更 |
105
+ | `style` | 代码格式(不影响功能) |
106
+ | `refactor` | 重构(不是新功能也不是修复) |
107
+ | `perf` | 性能优化 |
108
+ | `test` | 测试相关 |
109
+ | `chore` | 构建/工具变更 |
79
110
 
80
- For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
111
+ ## GitLab CI
81
112
 
82
- You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
113
+ 流水线包含以下阶段:
83
114
 
84
- ## Authors and acknowledgment
85
- Show your appreciation to those who have contributed to the project.
115
+ | 阶段 | 说明 |
116
+ |------|------|
117
+ | `install` | 安装依赖 |
118
+ | `lint` | 代码检查 |
119
+ | `build` | 构建项目 |
120
+ | `release` | 发版(仅 main 分支) |
86
121
 
87
122
  ## License
88
- For open source projects, say how it is licensed.
89
123
 
90
- ## Project status
91
- If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
124
+ MIT
@@ -2,10 +2,10 @@ import chalk from 'chalk';
2
2
  import inquirer from 'inquirer';
3
3
  import ora from 'ora';
4
4
  import { validateApiKey } from '../lib/api-validator.js';
5
- import { DEFAULT_CONFIG } from '../lib/claude-code-manager.js';
6
5
  import { configManager } from '../lib/config.js';
7
6
  import { i18n } from '../lib/i18n.js';
8
7
  import { toolManager } from '../lib/tool-manager.js';
8
+ import { DEFAULT_CONFIG } from '../lib/tools/claude-code-tool.js';
9
9
  /**
10
10
  * 支持的工具列表
11
11
  */
@@ -1,27 +1,29 @@
1
1
  import chalk from 'chalk';
2
2
  import inquirer from 'inquirer';
3
3
  import { i18n } from '../lib/i18n.js';
4
- import { SUPPORTED_TOOLS, toolManager } from '../lib/tool-manager.js';
4
+ import { toolManager, toolRegistry } from '../lib/tool-manager.js';
5
5
  import { wizard } from '../lib/wizard.js';
6
6
  export async function configCommand(args) {
7
7
  const subCommand = args[0];
8
8
  const toolName = args[1];
9
9
  // 如果没有参数,进入交互式选择
10
10
  if (!subCommand) {
11
+ const supportedTools = toolRegistry.getSupportedTools();
11
12
  const { selectedTool } = await inquirer.prompt([
12
13
  {
13
14
  type: 'list',
14
15
  name: 'selectedTool',
15
16
  message: i18n.t('wizard.select_tool'),
16
- choices: Object.values(SUPPORTED_TOOLS).map(tool => ({
17
+ choices: supportedTools.map(tool => ({
17
18
  name: `${tool.displayName} (${tool.name})`,
18
19
  value: tool.name,
19
20
  })),
20
21
  },
21
22
  ]);
23
+ const tool = toolRegistry.getTool(selectedTool);
22
24
  // 检查工具是否安装
23
25
  if (!toolManager.isToolInstalled(selectedTool)) {
24
- console.log(chalk.yellow(`\n${i18n.t('wizard.tool_not_installed', { tool: SUPPORTED_TOOLS[selectedTool].displayName })}`));
26
+ console.log(chalk.yellow(`\n${i18n.t('wizard.tool_not_installed', { tool: tool?.displayName || selectedTool })}`));
25
27
  const { shouldInstall } = await inquirer.prompt([
26
28
  {
27
29
  type: 'confirm',
@@ -116,9 +118,10 @@ export async function configCommand(args) {
116
118
  return;
117
119
  }
118
120
  // 其他工具的支持
119
- if (SUPPORTED_TOOLS[subCommand]) {
121
+ const tool = toolRegistry.getTool(subCommand);
122
+ if (tool) {
120
123
  if (!toolManager.isToolInstalled(subCommand)) {
121
- console.log(chalk.yellow(i18n.t('wizard.tool_not_installed', { tool: SUPPORTED_TOOLS[subCommand].displayName })));
124
+ console.log(chalk.yellow(i18n.t('wizard.tool_not_installed', { tool: tool.displayName })));
122
125
  return;
123
126
  }
124
127
  await wizard.showToolMenu(subCommand);
@@ -127,7 +130,8 @@ export async function configCommand(args) {
127
130
  console.error(chalk.red(`Unknown command: ${subCommand}`));
128
131
  console.log(chalk.gray('Usage: chelper config [tool-name]'));
129
132
  console.log(chalk.gray('Available tools:'));
130
- Object.values(SUPPORTED_TOOLS).forEach((tool) => {
131
- console.log(chalk.gray(` - ${tool.name}: ${tool.displayName}`));
133
+ const supportedTools = toolRegistry.getSupportedTools();
134
+ supportedTools.forEach((t) => {
135
+ console.log(chalk.gray(` - ${t.name}: ${t.displayName}`));
132
136
  });
133
137
  }
@@ -1,10 +1,10 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { validateApiKey } from '../lib/api-validator.js';
4
- import { claudeCodeManager } from '../lib/claude-code-manager.js';
5
4
  import { configManager } from '../lib/config.js';
6
5
  import { i18n } from '../lib/i18n.js';
7
6
  import { toolManager } from '../lib/tool-manager.js';
7
+ import { claudeCodeTool } from '../lib/tools/claude-code-tool.js';
8
8
  export async function doctorCommand() {
9
9
  const spinner = ora(i18n.t('doctor.checking')).start();
10
10
  const results = [];
@@ -73,7 +73,7 @@ export async function doctorCommand() {
73
73
  }
74
74
  // Check 4: Configuration status
75
75
  spinner.text = i18n.t('doctor.check_config');
76
- claudeCodeManager.getModelConfig();
76
+ claudeCodeTool.getModelConfig();
77
77
  const models = configManager.getModels();
78
78
  const configComplete = !!apiKey && !!(models.haikuModel || models.sonnetModel || models.opusModel);
79
79
  let configMessage = '';
package/dist/index.d.ts CHANGED
@@ -2,6 +2,6 @@
2
2
  export * from './lib/command.js';
3
3
  export * from './lib/config.js';
4
4
  export * from './lib/i18n.js';
5
- export * from './lib/opencode-manager.js';
6
5
  export * from './lib/tool-manager.js';
6
+ export * from './lib/tools/index.js';
7
7
  export * from './lib/wizard.js';
package/dist/index.js CHANGED
@@ -2,6 +2,6 @@
2
2
  export * from './lib/command.js';
3
3
  export * from './lib/config.js';
4
4
  export * from './lib/i18n.js';
5
- export * from './lib/opencode-manager.js';
6
5
  export * from './lib/tool-manager.js';
6
+ export * from './lib/tools/index.js';
7
7
  export * from './lib/wizard.js';
@@ -6,6 +6,7 @@ export interface ChelperConfig {
6
6
  opusModel?: string;
7
7
  openCodeModel?: string;
8
8
  openCodeSmallModel?: string;
9
+ codexModel?: string;
9
10
  }
10
11
  export declare class ConfigManager {
11
12
  private static instance;
@@ -43,5 +44,7 @@ export declare class ConfigManager {
43
44
  openCodeModel?: string;
44
45
  openCodeSmallModel?: string;
45
46
  }): void;
47
+ getCodexModel(): string | undefined;
48
+ setCodexModel(model: string): void;
46
49
  }
47
50
  export declare const configManager: ConfigManager;
@@ -102,5 +102,11 @@ export class ConfigManager {
102
102
  setOpenCodeModels(models) {
103
103
  this.updateConfig(models);
104
104
  }
105
+ getCodexModel() {
106
+ return this.config.codexModel;
107
+ }
108
+ setCodexModel(model) {
109
+ this.updateConfig({ codexModel: model });
110
+ }
105
111
  }
106
112
  export const configManager = ConfigManager.getInstance();
@@ -0,0 +1,49 @@
1
+ export interface ModelInfo {
2
+ id: string;
3
+ name?: string;
4
+ context_length?: number;
5
+ max_output_tokens?: number;
6
+ }
7
+ /**
8
+ * 模型服务
9
+ * 负责模型列表的获取、缓存和同步
10
+ */
11
+ export declare class ModelService {
12
+ private static instance;
13
+ private cachedModels;
14
+ private cachedModelsWithInfo;
15
+ private constructor();
16
+ static getInstance(): ModelService;
17
+ /**
18
+ * 获取可用模型列表(带缓存)
19
+ */
20
+ fetchModels(): Promise<string[]>;
21
+ /**
22
+ * 获取带详细信息的模型列表
23
+ */
24
+ fetchModelsWithInfo(apiKey: string): Promise<ModelInfo[]>;
25
+ /**
26
+ * 清除缓存
27
+ */
28
+ clearCache(): void;
29
+ /**
30
+ * 刷新模型列表(仅获取最新数据)
31
+ */
32
+ refreshModels(): Promise<{
33
+ success: boolean;
34
+ count: number;
35
+ }>;
36
+ /**
37
+ * 刷新并同步模型列表到 OpenCode 配置
38
+ * 只更新 provider.xaio.models,不改变当前选择的模型
39
+ */
40
+ refreshAndSyncToOpenCode(): Promise<{
41
+ success: boolean;
42
+ count: number;
43
+ }>;
44
+ /**
45
+ * 获取缓存的模型数量
46
+ */
47
+ getCachedCount(): number;
48
+ }
49
+ export declare const modelService: ModelService;
@@ -0,0 +1,129 @@
1
+ import { configManager } from './config.js';
2
+ import { openCodeTool } from './tools/opencode-tool.js';
3
+ /**
4
+ * 模型服务
5
+ * 负责模型列表的获取、缓存和同步
6
+ */
7
+ export class ModelService {
8
+ static instance;
9
+ cachedModels = [];
10
+ cachedModelsWithInfo = [];
11
+ constructor() { }
12
+ static getInstance() {
13
+ if (!ModelService.instance) {
14
+ ModelService.instance = new ModelService();
15
+ }
16
+ return ModelService.instance;
17
+ }
18
+ /**
19
+ * 获取可用模型列表(带缓存)
20
+ */
21
+ async fetchModels() {
22
+ const apiKey = configManager.getApiKey();
23
+ if (!apiKey) {
24
+ return [];
25
+ }
26
+ if (this.cachedModels.length > 0) {
27
+ return this.cachedModels;
28
+ }
29
+ const modelsWithInfo = await this.fetchModelsWithInfo(apiKey);
30
+ this.cachedModels = modelsWithInfo.map(m => m.id);
31
+ return this.cachedModels;
32
+ }
33
+ /**
34
+ * 获取带详细信息的模型列表
35
+ */
36
+ async fetchModelsWithInfo(apiKey) {
37
+ if (this.cachedModelsWithInfo.length > 0) {
38
+ return this.cachedModelsWithInfo;
39
+ }
40
+ const controller = new AbortController();
41
+ const timeoutId = setTimeout(() => controller.abort(), 30000);
42
+ try {
43
+ const response = await fetch('https://code-api.x-aio.com/v1/models', {
44
+ method: 'GET',
45
+ headers: {
46
+ 'Authorization': `Bearer ${apiKey}`,
47
+ 'Content-Type': 'application/json',
48
+ },
49
+ signal: controller.signal,
50
+ });
51
+ clearTimeout(timeoutId);
52
+ if (!response.ok) {
53
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
54
+ }
55
+ const data = await response.json();
56
+ if (data && data.data && Array.isArray(data.data)) {
57
+ this.cachedModelsWithInfo = data.data.map((m) => ({
58
+ id: m.id,
59
+ name: m.name || m.id,
60
+ context_length: m.context_length,
61
+ max_output_tokens: m.max_output_tokens,
62
+ }));
63
+ this.cachedModels = this.cachedModelsWithInfo.map(m => m.id);
64
+ return this.cachedModelsWithInfo;
65
+ }
66
+ return [];
67
+ }
68
+ catch (error) {
69
+ clearTimeout(timeoutId);
70
+ throw error;
71
+ }
72
+ }
73
+ /**
74
+ * 清除缓存
75
+ */
76
+ clearCache() {
77
+ this.cachedModels = [];
78
+ this.cachedModelsWithInfo = [];
79
+ }
80
+ /**
81
+ * 刷新模型列表(仅获取最新数据)
82
+ */
83
+ async refreshModels() {
84
+ const apiKey = configManager.getApiKey();
85
+ if (!apiKey) {
86
+ return { success: false, count: 0 };
87
+ }
88
+ this.clearCache();
89
+ try {
90
+ const models = await this.fetchModelsWithInfo(apiKey);
91
+ return { success: models.length > 0, count: models.length };
92
+ }
93
+ catch {
94
+ return { success: false, count: 0 };
95
+ }
96
+ }
97
+ /**
98
+ * 刷新并同步模型列表到 OpenCode 配置
99
+ * 只更新 provider.xaio.models,不改变当前选择的模型
100
+ */
101
+ async refreshAndSyncToOpenCode() {
102
+ const apiKey = configManager.getApiKey();
103
+ if (!apiKey) {
104
+ return { success: false, count: 0 };
105
+ }
106
+ this.clearCache();
107
+ try {
108
+ // 获取最新模型
109
+ const models = await this.fetchModelsWithInfo(apiKey);
110
+ if (models.length === 0) {
111
+ return { success: false, count: 0 };
112
+ }
113
+ // 同步到 OpenCode 配置
114
+ openCodeTool.syncModelsToConfig(models);
115
+ return { success: true, count: models.length };
116
+ }
117
+ catch {
118
+ return { success: false, count: 0 };
119
+ }
120
+ }
121
+ /**
122
+ * 获取缓存的模型数量
123
+ */
124
+ getCachedCount() {
125
+ return this.cachedModels.length;
126
+ }
127
+ }
128
+ // 单例导出
129
+ export const modelService = ModelService.getInstance();
@@ -0,0 +1,40 @@
1
+ import type { IPlugin } from '../tools/base-tool.js';
2
+ /**
3
+ * CCLine 插件实现
4
+ * CCLine 是 Claude Code 的增强插件
5
+ */
6
+ export declare class CCLinePlugin implements IPlugin {
7
+ readonly name = "ccline";
8
+ readonly displayName = "CCLine";
9
+ private settingsPath;
10
+ constructor();
11
+ /**
12
+ * 检查 CCLine 是否已安装
13
+ */
14
+ isInstalled(): boolean;
15
+ /**
16
+ * 安装 CCLine
17
+ */
18
+ install(): Promise<void>;
19
+ /**
20
+ * 装载 CCLine(添加到 Claude Code 配置)
21
+ */
22
+ load(): void;
23
+ /**
24
+ * 取消装载 CCLine(从配置中移除)
25
+ */
26
+ unload(): void;
27
+ /**
28
+ * 检查 CCLine 是否已装载到配置
29
+ */
30
+ isLoaded(): boolean;
31
+ /**
32
+ * 读取 Claude Code settings.json
33
+ */
34
+ private getSettings;
35
+ /**
36
+ * 保存 Claude Code settings.json
37
+ */
38
+ private saveSettings;
39
+ }
40
+ export declare const cclinePlugin: CCLinePlugin;
@@ -0,0 +1,116 @@
1
+ import { execSync, spawn } from 'node:child_process';
2
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ /**
6
+ * CCLine 插件实现
7
+ * CCLine 是 Claude Code 的增强插件
8
+ */
9
+ export class CCLinePlugin {
10
+ name = 'ccline';
11
+ displayName = 'CCLine';
12
+ settingsPath;
13
+ constructor() {
14
+ this.settingsPath = join(homedir(), '.claude', 'settings.json');
15
+ }
16
+ /**
17
+ * 检查 CCLine 是否已安装
18
+ */
19
+ isInstalled() {
20
+ try {
21
+ // 检查 ccline 命令是否可用
22
+ const cmd = process.platform === 'win32' ? 'where ccline' : 'which ccline';
23
+ execSync(cmd, { stdio: 'pipe' });
24
+ return true;
25
+ }
26
+ catch {
27
+ // 如果命令不存在,尝试检查 npm 全局包
28
+ try {
29
+ execSync('npm list -g @cometix/ccline', { stdio: 'pipe' });
30
+ return true;
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ }
37
+ /**
38
+ * 安装 CCLine
39
+ */
40
+ async install() {
41
+ const isWindows = process.platform === 'win32';
42
+ const command = isWindows ? 'npm.cmd' : 'npm';
43
+ const args = ['install', '-g', '@cometix/ccline'];
44
+ return new Promise((resolve, reject) => {
45
+ const child = spawn(command, args, {
46
+ stdio: 'inherit',
47
+ shell: true,
48
+ });
49
+ child.on('close', (code) => {
50
+ if (code === 0) {
51
+ resolve();
52
+ }
53
+ else {
54
+ reject(new Error(`CCLine install failed with code ${code}`));
55
+ }
56
+ });
57
+ child.on('error', (error) => {
58
+ reject(error);
59
+ });
60
+ });
61
+ }
62
+ /**
63
+ * 装载 CCLine(添加到 Claude Code 配置)
64
+ */
65
+ load() {
66
+ const settings = this.getSettings();
67
+ // CCLine 使用 statusLine 配置
68
+ settings.statusLine = {
69
+ type: 'command',
70
+ command: 'ccline',
71
+ padding: 0,
72
+ };
73
+ this.saveSettings(settings);
74
+ }
75
+ /**
76
+ * 取消装载 CCLine(从配置中移除)
77
+ */
78
+ unload() {
79
+ const settings = this.getSettings();
80
+ // 检查 statusLine 是否是 ccline 配置
81
+ if (settings.statusLine && settings.statusLine.command === 'ccline') {
82
+ delete settings.statusLine;
83
+ this.saveSettings(settings);
84
+ }
85
+ }
86
+ /**
87
+ * 检查 CCLine 是否已装载到配置
88
+ */
89
+ isLoaded() {
90
+ const settings = this.getSettings();
91
+ return !!(settings.statusLine && settings.statusLine.command === 'ccline');
92
+ }
93
+ /**
94
+ * 读取 Claude Code settings.json
95
+ */
96
+ getSettings() {
97
+ try {
98
+ if (existsSync(this.settingsPath)) {
99
+ const content = readFileSync(this.settingsPath, 'utf-8');
100
+ return JSON.parse(content);
101
+ }
102
+ }
103
+ catch {
104
+ // ignore
105
+ }
106
+ return {};
107
+ }
108
+ /**
109
+ * 保存 Claude Code settings.json
110
+ */
111
+ saveSettings(settings) {
112
+ writeFileSync(this.settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
113
+ }
114
+ }
115
+ // 单例导出
116
+ export const cclinePlugin = new CCLinePlugin();
@@ -0,0 +1,2 @@
1
+ export * from './ccline-plugin.js';
2
+ export * from './oh-my-opencode-plugin.js';
@@ -0,0 +1,3 @@
1
+ // 插件导出
2
+ export * from './ccline-plugin.js';
3
+ export * from './oh-my-opencode-plugin.js';