duojie-helper 0.2.21 → 0.2.23

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.
@@ -1,154 +1,154 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import os from 'os';
4
- import TOML from '@iarna/toml';
5
- import { getApiBaseForProtocol } from '../index.js';
6
-
7
- const DUOJIE_PROVIDER_ID = 'duojie';
8
- const DEFAULT_MODEL = 'gpt-5.4';
9
-
10
- /**
11
- * 获取 Codex CLI 配置路径
12
- */
13
- function getCodexConfigPaths() {
14
- const home = os.homedir();
15
- return {
16
- configDir: path.join(home, '.codex'),
17
- configFile: path.join(home, '.codex', 'config.toml'),
18
- };
19
- }
20
-
21
- function getWireApi(model) {
22
- return model.startsWith('gpt') ? 'responses' : 'chat';
23
- }
24
-
25
- async function readConfigToml(configFile) {
26
- if (!(await fs.pathExists(configFile))) {
27
- return {};
28
- }
29
-
30
- const content = await fs.readFile(configFile, 'utf-8');
31
- if (!content.trim()) {
32
- return {};
33
- }
34
-
35
- return TOML.parse(content);
36
- }
37
-
38
- /**
39
- * 配置 Codex CLI
40
- * 使用官方 config.toml 结构
41
- */
42
- export async function configureCodex(apiKey) {
43
- const paths = getCodexConfigPaths();
44
-
45
- try {
46
- await fs.ensureDir(paths.configDir);
47
-
48
- let existingConfig = {};
49
- try {
50
- existingConfig = await readConfigToml(paths.configFile);
51
- } catch {
52
- existingConfig = {};
53
- }
54
-
55
- const wireApi = getWireApi(DEFAULT_MODEL);
56
-
57
- const modelProviders = {
58
- ...(existingConfig.model_providers || {}),
59
- [DUOJIE_PROVIDER_ID]: {
60
- ...(existingConfig.model_providers?.[DUOJIE_PROVIDER_ID] || {}),
61
- name: 'Duojie',
62
- base_url: getApiBaseForProtocol(wireApi),
63
- experimental_bearer_token: apiKey,
64
- wire_api: wireApi,
65
- },
66
- };
67
-
68
- const newConfig = {
69
- ...existingConfig,
70
- model: DEFAULT_MODEL,
71
- model_provider: DUOJIE_PROVIDER_ID,
72
- model_providers: modelProviders,
73
- };
74
-
75
- const tomlContent = `# Codex CLI Configuration\n# Generated by duojie-helper\n# Docs: https://developers.openai.com/codex/config-reference/\n\n${TOML.stringify(newConfig)}`;
76
-
77
- await fs.writeFile(paths.configFile, tomlContent, 'utf-8');
78
-
79
- return {
80
- success: true,
81
- message: `已配置 → ${paths.configFile}`,
82
- configPath: paths.configFile,
83
- hint: '已写入 config.toml(纯配置,无需额外环境变量)',
84
- };
85
- } catch (error) {
86
- return {
87
- success: false,
88
- message: `配置失败: ${error.message}`,
89
- };
90
- }
91
- }
92
-
93
- /**
94
- * 检查 Codex CLI 配置状态
95
- */
96
- configureCodex.checkStatus = async function() {
97
- const paths = getCodexConfigPaths();
98
-
99
- if (await fs.pathExists(paths.configFile)) {
100
- try {
101
- const config = await readConfigToml(paths.configFile);
102
- const provider = config.model_providers?.[DUOJIE_PROVIDER_ID];
103
-
104
- if (provider?.base_url?.includes('duojie')) {
105
- return { configured: true, message: '已配置 Duojie API' };
106
- }
107
-
108
- if (config.model_provider || Object.keys(config.model_providers || {}).length > 0) {
109
- return { configured: true, message: '已配置(非 Duojie)' };
110
- }
111
- } catch {
112
- return { configured: true, message: '配置文件存在但格式无效' };
113
- }
114
- }
115
-
116
- return { configured: false };
117
- };
118
-
119
- /**
120
- * 撤销 Codex CLI 配置
121
- */
122
- configureCodex.revoke = async function() {
123
- const paths = getCodexConfigPaths();
124
-
125
- if (!(await fs.pathExists(paths.configFile))) {
126
- return;
127
- }
128
-
129
- try {
130
- const config = await readConfigToml(paths.configFile);
131
-
132
- if (config.model_providers?.[DUOJIE_PROVIDER_ID]) {
133
- delete config.model_providers[DUOJIE_PROVIDER_ID];
134
- if (Object.keys(config.model_providers).length === 0) {
135
- delete config.model_providers;
136
- }
137
- }
138
-
139
- if (config.model_provider === DUOJIE_PROVIDER_ID) {
140
- delete config.model_provider;
141
- }
142
-
143
- if (config.model === DEFAULT_MODEL) {
144
- delete config.model;
145
- }
146
-
147
- const tomlContent = TOML.stringify(config);
148
- await fs.writeFile(paths.configFile, tomlContent ? `${tomlContent}` : '', 'utf-8');
149
- } catch {
150
- // ignore
151
- }
152
- };
153
-
154
- export default configureCodex;
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import TOML from '@iarna/toml';
5
+ import { getApiBaseForProtocol } from '../index.js';
6
+
7
+ const DUOJIE_PROVIDER_ID = 'duojie';
8
+ const DEFAULT_MODEL = 'gpt-5.4';
9
+
10
+ /**
11
+ * 获取 Codex CLI 配置路径
12
+ */
13
+ function getCodexConfigPaths() {
14
+ const home = os.homedir();
15
+ return {
16
+ configDir: path.join(home, '.codex'),
17
+ configFile: path.join(home, '.codex', 'config.toml'),
18
+ };
19
+ }
20
+
21
+ function getWireApi(model) {
22
+ return model.startsWith('gpt') ? 'responses' : 'chat';
23
+ }
24
+
25
+ async function readConfigToml(configFile) {
26
+ if (!(await fs.pathExists(configFile))) {
27
+ return {};
28
+ }
29
+
30
+ const content = await fs.readFile(configFile, 'utf-8');
31
+ if (!content.trim()) {
32
+ return {};
33
+ }
34
+
35
+ return TOML.parse(content);
36
+ }
37
+
38
+ /**
39
+ * 配置 Codex CLI
40
+ * 使用官方 config.toml 结构
41
+ */
42
+ export async function configureCodex(apiKey) {
43
+ const paths = getCodexConfigPaths();
44
+
45
+ try {
46
+ await fs.ensureDir(paths.configDir);
47
+
48
+ let existingConfig = {};
49
+ try {
50
+ existingConfig = await readConfigToml(paths.configFile);
51
+ } catch {
52
+ existingConfig = {};
53
+ }
54
+
55
+ const wireApi = getWireApi(DEFAULT_MODEL);
56
+
57
+ const modelProviders = {
58
+ ...(existingConfig.model_providers || {}),
59
+ [DUOJIE_PROVIDER_ID]: {
60
+ ...(existingConfig.model_providers?.[DUOJIE_PROVIDER_ID] || {}),
61
+ name: 'Duojie',
62
+ base_url: getApiBaseForProtocol(wireApi),
63
+ experimental_bearer_token: apiKey,
64
+ wire_api: wireApi,
65
+ },
66
+ };
67
+
68
+ const newConfig = {
69
+ ...existingConfig,
70
+ model: DEFAULT_MODEL,
71
+ model_provider: DUOJIE_PROVIDER_ID,
72
+ model_providers: modelProviders,
73
+ };
74
+
75
+ const tomlContent = `# Codex CLI Configuration\n# Generated by duojie-helper\n# Docs: https://developers.openai.com/codex/config-reference/\n\n${TOML.stringify(newConfig)}`;
76
+
77
+ await fs.writeFile(paths.configFile, tomlContent, 'utf-8');
78
+
79
+ return {
80
+ success: true,
81
+ message: `已配置 → ${paths.configFile}`,
82
+ configPath: paths.configFile,
83
+ hint: '已写入 config.toml(纯配置,无需额外环境变量)',
84
+ };
85
+ } catch (error) {
86
+ return {
87
+ success: false,
88
+ message: `配置失败: ${error.message}`,
89
+ };
90
+ }
91
+ }
92
+
93
+ /**
94
+ * 检查 Codex CLI 配置状态
95
+ */
96
+ configureCodex.checkStatus = async function() {
97
+ const paths = getCodexConfigPaths();
98
+
99
+ if (await fs.pathExists(paths.configFile)) {
100
+ try {
101
+ const config = await readConfigToml(paths.configFile);
102
+ const provider = config.model_providers?.[DUOJIE_PROVIDER_ID];
103
+
104
+ if (provider?.base_url?.includes('duojie')) {
105
+ return { configured: true, message: '已配置 Duojie API' };
106
+ }
107
+
108
+ if (config.model_provider || Object.keys(config.model_providers || {}).length > 0) {
109
+ return { configured: true, message: '已配置(非 Duojie)' };
110
+ }
111
+ } catch {
112
+ return { configured: true, message: '配置文件存在但格式无效' };
113
+ }
114
+ }
115
+
116
+ return { configured: false };
117
+ };
118
+
119
+ /**
120
+ * 撤销 Codex CLI 配置
121
+ */
122
+ configureCodex.revoke = async function() {
123
+ const paths = getCodexConfigPaths();
124
+
125
+ if (!(await fs.pathExists(paths.configFile))) {
126
+ return;
127
+ }
128
+
129
+ try {
130
+ const config = await readConfigToml(paths.configFile);
131
+
132
+ if (config.model_providers?.[DUOJIE_PROVIDER_ID]) {
133
+ delete config.model_providers[DUOJIE_PROVIDER_ID];
134
+ if (Object.keys(config.model_providers).length === 0) {
135
+ delete config.model_providers;
136
+ }
137
+ }
138
+
139
+ if (config.model_provider === DUOJIE_PROVIDER_ID) {
140
+ delete config.model_provider;
141
+ }
142
+
143
+ if (config.model === DEFAULT_MODEL) {
144
+ delete config.model;
145
+ }
146
+
147
+ const tomlContent = TOML.stringify(config);
148
+ await fs.writeFile(paths.configFile, tomlContent ? `${tomlContent}` : '', 'utf-8');
149
+ } catch {
150
+ // ignore
151
+ }
152
+ };
153
+
154
+ export default configureCodex;
@@ -1,158 +1,158 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import os from 'os';
4
- import { getModels, getApiBaseForProtocol } from '../index.js';
5
-
6
- /**
7
- * 获取 Continue 配置路径
8
- */
9
- function getContinueConfigPaths() {
10
- const home = os.homedir();
11
- return {
12
- configDir: path.join(home, '.continue'),
13
- configFile: path.join(home, '.continue', 'config.json'),
14
- };
15
- }
16
-
17
- /**
18
- * 配置 Continue
19
- * 自动配置所有可用模型
20
- */
21
- export async function configureContinue(apiKey) {
22
- const paths = getContinueConfigPaths();
23
-
24
- try {
25
- await fs.ensureDir(paths.configDir);
26
-
27
- let config = {};
28
- if (await fs.pathExists(paths.configFile)) {
29
- try {
30
- config = await fs.readJson(paths.configFile);
31
- } catch {
32
- config = {};
33
- }
34
- }
35
-
36
- // 获取可用模型列表
37
- const models = getModels();
38
- const duojieModels = [];
39
-
40
- // 添加 Claude 模型
41
- if (models.claude && models.claude.length > 0) {
42
- for (const m of models.claude) {
43
- duojieModels.push({
44
- title: `Duojie ${m.name}`,
45
- provider: "anthropic",
46
- model: m.id,
47
- apiKey: apiKey,
48
- apiBase: getApiBaseForProtocol('anthropic'),
49
- completionOptions: {
50
- maxTokens: 16384,
51
- },
52
- });
53
- }
54
- }
55
-
56
- // 添加 GPT 模型
57
- if (models.gpt && models.gpt.length > 0) {
58
- for (const m of models.gpt) {
59
- duojieModels.push({
60
- title: `Duojie ${m.name}`,
61
- provider: "anthropic",
62
- model: m.id,
63
- apiKey: apiKey,
64
- apiBase: getApiBaseForProtocol('anthropic'),
65
- completionOptions: {
66
- maxTokens: 16384,
67
- },
68
- });
69
- }
70
- }
71
-
72
- // 添加其他模型
73
- const otherModels = [...(models.gemini || []), ...(models.other || [])];
74
- for (const m of otherModels) {
75
- duojieModels.push({
76
- title: `Duojie ${m.name}`,
77
- provider: "anthropic",
78
- model: m.id,
79
- apiKey: apiKey,
80
- apiBase: getApiBaseForProtocol('anthropic'),
81
- completionOptions: {
82
- maxTokens: 8192,
83
- },
84
- });
85
- }
86
-
87
- // 把默认模型尽量设为 gpt-5.2(如果存在),否则保持当前顺序
88
- const gpt52 = duojieModels.find(m => m.model === 'gpt-5.2');
89
- if (gpt52) {
90
- const others = duojieModels.filter(m => m !== gpt52);
91
- duojieModels.length = 0;
92
- duojieModels.push(gpt52, ...others);
93
- }
94
-
95
- // 合并模型配置 - 移除旧的 Duojie 配置
96
- const existingModels = config.models || [];
97
- const filteredModels = existingModels.filter(m =>
98
- !m.title?.startsWith('Duojie')
99
- );
100
-
101
- const newConfig = {
102
- ...config,
103
- models: [...duojieModels, ...filteredModels],
104
- _duojie: {
105
- configured: true,
106
- configuredAt: new Date().toISOString(),
107
- },
108
- };
109
-
110
- await fs.writeJson(paths.configFile, newConfig, { spaces: 2 });
111
-
112
- return {
113
- success: true,
114
- message: `已配置 ${duojieModels.length} 个模型 → ${paths.configFile}`,
115
- configPath: paths.configFile,
116
- hint: '重启 VS Code/JetBrains 使配置生效',
117
- };
118
- } catch (error) {
119
- return {
120
- success: false,
121
- message: `配置失败: ${error.message}`,
122
- };
123
- }
124
- }
125
-
126
- configureContinue.checkStatus = async function() {
127
- const paths = getContinueConfigPaths();
128
-
129
- if (await fs.pathExists(paths.configFile)) {
130
- try {
131
- const config = await fs.readJson(paths.configFile);
132
- if (config._duojie?.configured) {
133
- return { configured: true, message: '已配置 Duojie API' };
134
- } else if (config.models?.length > 0) {
135
- return { configured: true, message: '已配置(非 Duojie)' };
136
- }
137
- } catch {}
138
- }
139
- return { configured: false };
140
- };
141
-
142
- configureContinue.revoke = async function() {
143
- const paths = getContinueConfigPaths();
144
-
145
- if (await fs.pathExists(paths.configFile)) {
146
- try {
147
- const config = await fs.readJson(paths.configFile);
148
- // 移除 Duojie 模型
149
- config.models = (config.models || []).filter(m =>
150
- !m.title?.startsWith('Duojie')
151
- );
152
- delete config._duojie;
153
- await fs.writeJson(paths.configFile, config, { spaces: 2 });
154
- } catch {}
155
- }
156
- };
157
-
158
- export default configureContinue;
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { getModels, getApiBaseForProtocol } from '../index.js';
5
+
6
+ /**
7
+ * 获取 Continue 配置路径
8
+ */
9
+ function getContinueConfigPaths() {
10
+ const home = os.homedir();
11
+ return {
12
+ configDir: path.join(home, '.continue'),
13
+ configFile: path.join(home, '.continue', 'config.json'),
14
+ };
15
+ }
16
+
17
+ /**
18
+ * 配置 Continue
19
+ * 自动配置所有可用模型
20
+ */
21
+ export async function configureContinue(apiKey) {
22
+ const paths = getContinueConfigPaths();
23
+
24
+ try {
25
+ await fs.ensureDir(paths.configDir);
26
+
27
+ let config = {};
28
+ if (await fs.pathExists(paths.configFile)) {
29
+ try {
30
+ config = await fs.readJson(paths.configFile);
31
+ } catch {
32
+ config = {};
33
+ }
34
+ }
35
+
36
+ // 获取可用模型列表
37
+ const models = getModels();
38
+ const duojieModels = [];
39
+
40
+ // 添加 Claude 模型
41
+ if (models.claude && models.claude.length > 0) {
42
+ for (const m of models.claude) {
43
+ duojieModels.push({
44
+ title: `Duojie ${m.name}`,
45
+ provider: "anthropic",
46
+ model: m.id,
47
+ apiKey: apiKey,
48
+ apiBase: getApiBaseForProtocol('anthropic'),
49
+ completionOptions: {
50
+ maxTokens: 16384,
51
+ },
52
+ });
53
+ }
54
+ }
55
+
56
+ // 添加 GPT 模型
57
+ if (models.gpt && models.gpt.length > 0) {
58
+ for (const m of models.gpt) {
59
+ duojieModels.push({
60
+ title: `Duojie ${m.name}`,
61
+ provider: "anthropic",
62
+ model: m.id,
63
+ apiKey: apiKey,
64
+ apiBase: getApiBaseForProtocol('anthropic'),
65
+ completionOptions: {
66
+ maxTokens: 16384,
67
+ },
68
+ });
69
+ }
70
+ }
71
+
72
+ // 添加其他模型
73
+ const otherModels = [...(models.gemini || []), ...(models.other || [])];
74
+ for (const m of otherModels) {
75
+ duojieModels.push({
76
+ title: `Duojie ${m.name}`,
77
+ provider: "anthropic",
78
+ model: m.id,
79
+ apiKey: apiKey,
80
+ apiBase: getApiBaseForProtocol('anthropic'),
81
+ completionOptions: {
82
+ maxTokens: 8192,
83
+ },
84
+ });
85
+ }
86
+
87
+ // 把默认模型尽量设为 gpt-5.2(如果存在),否则保持当前顺序
88
+ const gpt52 = duojieModels.find(m => m.model === 'gpt-5.2');
89
+ if (gpt52) {
90
+ const others = duojieModels.filter(m => m !== gpt52);
91
+ duojieModels.length = 0;
92
+ duojieModels.push(gpt52, ...others);
93
+ }
94
+
95
+ // 合并模型配置 - 移除旧的 Duojie 配置
96
+ const existingModels = config.models || [];
97
+ const filteredModels = existingModels.filter(m =>
98
+ !m.title?.startsWith('Duojie')
99
+ );
100
+
101
+ const newConfig = {
102
+ ...config,
103
+ models: [...duojieModels, ...filteredModels],
104
+ _duojie: {
105
+ configured: true,
106
+ configuredAt: new Date().toISOString(),
107
+ },
108
+ };
109
+
110
+ await fs.writeJson(paths.configFile, newConfig, { spaces: 2 });
111
+
112
+ return {
113
+ success: true,
114
+ message: `已配置 ${duojieModels.length} 个模型 → ${paths.configFile}`,
115
+ configPath: paths.configFile,
116
+ hint: '重启 VS Code/JetBrains 使配置生效',
117
+ };
118
+ } catch (error) {
119
+ return {
120
+ success: false,
121
+ message: `配置失败: ${error.message}`,
122
+ };
123
+ }
124
+ }
125
+
126
+ configureContinue.checkStatus = async function() {
127
+ const paths = getContinueConfigPaths();
128
+
129
+ if (await fs.pathExists(paths.configFile)) {
130
+ try {
131
+ const config = await fs.readJson(paths.configFile);
132
+ if (config._duojie?.configured) {
133
+ return { configured: true, message: '已配置 Duojie API' };
134
+ } else if (config.models?.length > 0) {
135
+ return { configured: true, message: '已配置(非 Duojie)' };
136
+ }
137
+ } catch {}
138
+ }
139
+ return { configured: false };
140
+ };
141
+
142
+ configureContinue.revoke = async function() {
143
+ const paths = getContinueConfigPaths();
144
+
145
+ if (await fs.pathExists(paths.configFile)) {
146
+ try {
147
+ const config = await fs.readJson(paths.configFile);
148
+ // 移除 Duojie 模型
149
+ config.models = (config.models || []).filter(m =>
150
+ !m.title?.startsWith('Duojie')
151
+ );
152
+ delete config._duojie;
153
+ await fs.writeJson(paths.configFile, config, { spaces: 2 });
154
+ } catch {}
155
+ }
156
+ };
157
+
158
+ export default configureContinue;