@tokenaut/opentoken 1.3.5 → 1.4.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/README.md +85 -71
- package/bin/opentoken.js +5 -4
- package/package.json +22 -10
- package/src/atomcode-config.js +94 -0
- package/src/cli.js +207 -147
- package/src/codex-config.js +44 -22
- package/src/config.js +29 -28
- package/src/fetch-models.js +228 -0
- package/src/hermes-config.js +157 -0
- package/src/logo.js +62 -0
- package/src/models.js +61 -0
- package/src/openclaw-config.js +174 -0
- package/src/opencode-config.js +166 -0
- package/src/screen.js +9 -0
- package/src/tools/atomcode.js +253 -0
- package/src/tools/claude.js +302 -0
- package/src/tools/codex.js +327 -0
- package/src/tools/hermes.js +274 -0
- package/src/tools/openclaw.js +244 -0
- package/src/tools/opencode.js +258 -0
package/README.md
CHANGED
|
@@ -1,118 +1,132 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @opentoken/opentoken
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
快速接入 [https://gw.opentoken.io](https://gw.opentoken.io) 的强大模型。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
npx @opentoken/opentoken
|
|
9
|
+
npm install -g @opentoken/opentoken
|
|
9
10
|
```
|
|
10
11
|
|
|
11
|
-
##
|
|
12
|
+
## 命令
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
| 命令 | 说明 |
|
|
15
|
+
|------|------|
|
|
16
|
+
| `opentoken` | 交互主菜单 |
|
|
17
|
+
| `opentoken --help` / `-h` | 帮助 |
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
---
|
|
18
20
|
|
|
19
|
-
##
|
|
21
|
+
## 已支持的应用与配置文件
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|------|------|---------|
|
|
23
|
-
| `opentoken cc` | Claude Code | `~/.claude/settings.json` |
|
|
24
|
-
| `opentoken codex` / `cx` | Codex | `~/.codex/config.toml` + `~/.codex/auth.json` |
|
|
23
|
+
本 CLI 会为不同工具写入对应配置文件(路径均在用户目录下)。
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
| 应用 | 子命令(别名) | 配置文件 |
|
|
26
|
+
|------|----------------|----------|
|
|
27
|
+
| **Claude Code** | `cc`、`claude` | `~/.claude/settings.json`(环境变量:`ANTHROPIC_AUTH_TOKEN`、`ANTHROPIC_BASE_URL`、`ANTHROPIC_MODEL`) |
|
|
28
|
+
| **Atomcode** | `atomcode`、`ac` | `~/.atomcode/config.toml` |
|
|
29
|
+
| **OpenClaw** | `openclaw`、`oc` | `~/.openclaw/openclaw.json`(合并写入 Opentoken:`models.providers.opentoken`、`agents.defaults.model.primary` 等) |
|
|
30
|
+
| **Hermes** | `hermes`、`hm` | `~/.hermes/config.yaml`(`model.provider`、`model.base_url`、`model.default`)与 `~/.hermes/.env`(`OPENTOKEN_API_KEY`) |
|
|
31
|
+
| **OpenCode** | `opencode`、`od` | `~/.config/opencode/opencode.json`(合并写入 Opentoken:`provider.opentoken`) |
|
|
32
|
+
| **Codex** | `codex`、`cx` | `~/.codex/config.toml`(`model_provider`、`model_providers.opentoken`、`wire_api = "responses"`)与 `~/.codex/auth.json`(`OPENAI_API_KEY`) |
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
OpenClaw 会为 **`opentoken`** 提供方写入 **`baseUrl`**(`https://gw.opentoken.io/v1`)、**`api`**(`openai-completions`)、**`apiKey`** 与 **`models`**,并把 **`agents.defaults.model.primary`** 设为 `opentoken/<模型 ID>`;其它顶级字段与其它 provider **保留不删**(例如已有 `channels`、`gateway`)。Windows 下路径为 `%USERPROFILE%\.openclaw\openclaw.json`。
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|------|---------|
|
|
32
|
-
| `opus` | `claude-opus-4-8`(默认) |
|
|
33
|
-
| `sonnet` | `claude-sonnet-4-6` |
|
|
34
|
-
| `haiku` | `claude-haiku-4-5` |
|
|
36
|
+
Hermes 使用 Opentoken 时需 **`provider: custom`**,且 **`base_url` 须带 `/v1` 后缀**(本工具写入为 `https://gw.opentoken.io/v1`);密钥写入 `.env` 中的 `OPENTOKEN_API_KEY`。
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
OpenCode 会为 **`opentoken`** 提供方写入 **`npm`**(`@ai-sdk/anthropic`)、**`baseURL`**(`https://gw.opentoken.io/v1`,必须带 `/v1`)、**`apiKey`** 与 **`models`**(自动拉取可用模型列表并生成配置);其它 provider **保留不删**。配置文件支持 `$schema` 字段用于 IDE 提示。参考 [OpenCode 文档](https://help.aliyun.com/zh/model-studio/opencode-coding-plan)。
|
|
39
|
+
|
|
40
|
+
Codex 会为 **`opentoken`** 提供方写入 **`base_url`**(默认 `https://gw.opentoken.io/v1`)与 **`wire_api = "responses"`**(不写 `env_key`),并只从模型列表中选择 `supported_endpoint_types` 包含 `responses` 的模型。API Key 写入 **`~/.codex/auth.json`** 的 **`OPENAI_API_KEY`**,Codex 命令行与 IDE 插件共用,无需再 export 环境变量。
|
|
41
|
+
|
|
42
|
+
---
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
|------|---------|
|
|
40
|
-
| `5.4` / `gpt5.4` / `gpt-5.4` | `gpt-5.4`(默认) |
|
|
41
|
-
| `5.5` / `gpt5.5` / `gpt-5.5` | `gpt-5.5` |
|
|
44
|
+
## 快捷一键配置(非交互)
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
以下为各应用在命令行直接写入配置的用法。选项可单独或组合使用:**`--key` / `-k`**,**`--url` / `-u`**(仅 Claude Code),**`--model` / `-m`**。
|
|
44
47
|
|
|
45
48
|
### Claude Code
|
|
46
49
|
|
|
47
50
|
```bash
|
|
48
|
-
|
|
49
|
-
opentoken cc --
|
|
51
|
+
opentoken cc --key <api_key>
|
|
52
|
+
opentoken cc --url <base_url>
|
|
53
|
+
opentoken cc --model <model_id>
|
|
54
|
+
opentoken cc -k <api_key> -u <base_url> -m <model_id>
|
|
55
|
+
```
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
opentoken cc --model sonnet
|
|
57
|
+
示例:
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
opentoken cc
|
|
59
|
+
```bash
|
|
60
|
+
opentoken cc -k sk-xxx
|
|
61
|
+
opentoken cc -k sk-xxx -u https://gw.opentoken.io -m claude-opus-4-5
|
|
56
62
|
```
|
|
57
63
|
|
|
58
|
-
###
|
|
64
|
+
### Atomcode
|
|
65
|
+
|
|
66
|
+
Base URL 固定为 Opentoken 的 OpenAI 兼容地址,仅需 Key 与模型:
|
|
59
67
|
|
|
60
68
|
```bash
|
|
61
|
-
|
|
62
|
-
opentoken
|
|
69
|
+
opentoken atomcode --key <api_key>
|
|
70
|
+
opentoken ac --model <model_id>
|
|
71
|
+
opentoken ac -k <api_key> -m <model_id>
|
|
72
|
+
```
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
opentoken cx --model 5.5
|
|
74
|
+
### OpenClaw
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
写入(或合并)`~/.openclaw/openclaw.json` 中的 Opentoken 配置:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
opentoken openclaw --key <api_key>
|
|
80
|
+
opentoken oc --model <model_id>
|
|
81
|
+
opentoken oc -k <api_key> -m <model_id>
|
|
69
82
|
```
|
|
70
83
|
|
|
71
|
-
|
|
84
|
+
### Hermes
|
|
72
85
|
|
|
73
|
-
|
|
86
|
+
写入 `model.default`、`OPENTOKEN_API_KEY`,并强制 `provider: custom` 与 Opentoken `base_url`:
|
|
74
87
|
|
|
75
88
|
```bash
|
|
76
|
-
|
|
77
|
-
opentoken
|
|
78
|
-
|
|
79
|
-
# Codex
|
|
80
|
-
opentoken cx --key <your-api-key> --model 5.5 --url https://gw.opentoken.io/v1
|
|
89
|
+
opentoken hermes --key <api_key>
|
|
90
|
+
opentoken hm --model <model_id>
|
|
91
|
+
opentoken hm -k <api_key> -m <model_id>
|
|
81
92
|
```
|
|
82
93
|
|
|
83
|
-
###
|
|
94
|
+
### OpenCode
|
|
84
95
|
|
|
85
|
-
|
|
86
|
-
# Claude Code
|
|
87
|
-
opentoken cc --key <your-api-key> --model opus --url https://omniroute.goio.uk/v1
|
|
96
|
+
写入(或合并)`~/.config/opencode/opencode.json` 中的 Opentoken 配置:
|
|
88
97
|
|
|
89
|
-
|
|
90
|
-
opentoken
|
|
98
|
+
```bash
|
|
99
|
+
opentoken opencode --key <api_key>
|
|
100
|
+
opentoken od --model <model_id>
|
|
101
|
+
opentoken od -k <api_key> -m <model_id>
|
|
91
102
|
```
|
|
92
103
|
|
|
93
|
-
|
|
104
|
+
提供 API Key 后会自动拉取完整模型列表并写入配置。配置格式兼容 Anthropic API,使用 `@ai-sdk/anthropic` SDK。
|
|
94
105
|
|
|
95
|
-
###
|
|
106
|
+
### Codex
|
|
96
107
|
|
|
97
|
-
|
|
98
|
-
- `ANTHROPIC_BASE_URL`
|
|
99
|
-
- `ANTHROPIC_MODEL`(主模型)
|
|
100
|
-
- `ANTHROPIC_DEFAULT_SONNET_MODEL`(Sonnet 辅助模型)
|
|
101
|
-
- `ANTHROPIC_DEFAULT_HAIKU_MODEL`(Haiku 辅助模型)
|
|
102
|
-
- `API_TIMEOUT_MS`
|
|
103
|
-
- `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS`
|
|
104
|
-
- `ENABLE_TOOL_SEARCH`
|
|
108
|
+
写入(或合并)`~/.codex/config.toml` 中的 Opentoken Responses API 配置:
|
|
105
109
|
|
|
106
|
-
|
|
110
|
+
```bash
|
|
111
|
+
opentoken codex --key <api_key>
|
|
112
|
+
opentoken cx --url https://gw.opentoken.io/v1
|
|
113
|
+
opentoken cx --model <model_id>
|
|
114
|
+
opentoken cx -k <api_key> -u https://gw.opentoken.io/v1 -m <model_id>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
提供 API Key 后会自动拉取 `supported_endpoint_types` 含 `responses` 的模型,并写入 Codex profiles 与 `tui.model_availability_nux`,同时将 Key 写入 `~/.codex/auth.json`:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"auth_mode": "apikey",
|
|
122
|
+
"OPENAI_API_KEY": "<api_key>"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
107
125
|
|
|
108
|
-
|
|
126
|
+
若 `config.toml` 中仍有旧版 **`env_key`** 行,请删除以免与 `auth.json` 冲突。
|
|
109
127
|
|
|
110
|
-
|
|
111
|
-
- `model_providers.opentoken`(name、base_url、wire_api)
|
|
112
|
-
- `profiles.<model>`
|
|
113
|
-
- `tui.model_availability_nux`
|
|
114
|
-
- `OPENAI_API_KEY`(写入 auth.json)
|
|
128
|
+
---
|
|
115
129
|
|
|
116
|
-
|
|
130
|
+
## 环境
|
|
117
131
|
|
|
118
|
-
|
|
132
|
+
Node.js >= 14 · MIT
|
package/bin/opentoken.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
'use strict';
|
|
3
4
|
|
|
4
|
-
const run = require('../src/cli');
|
|
5
|
+
const { run } = require('../src/cli');
|
|
5
6
|
|
|
6
|
-
run().catch((
|
|
7
|
-
console.error(
|
|
7
|
+
run().catch((err) => {
|
|
8
|
+
console.error(err);
|
|
8
9
|
process.exit(1);
|
|
9
|
-
});
|
|
10
|
+
});
|
package/package.json
CHANGED
|
@@ -1,32 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenaut/opentoken",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "OpenToken
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "OpenToken 一键接入 AI 模型 · https://gw.opentoken.io",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opentoken": "bin/opentoken.js"
|
|
8
8
|
},
|
|
9
|
-
"files": [
|
|
10
|
-
"bin",
|
|
11
|
-
"src"
|
|
12
|
-
],
|
|
13
9
|
"scripts": {
|
|
14
10
|
"start": "node bin/opentoken.js",
|
|
15
11
|
"dev": "node bin/opentoken.js"
|
|
16
12
|
},
|
|
17
13
|
"keywords": [
|
|
18
14
|
"opentoken",
|
|
15
|
+
"openrouter",
|
|
19
16
|
"claude",
|
|
20
17
|
"claude-code",
|
|
18
|
+
"api token",
|
|
19
|
+
"token配置",
|
|
20
|
+
"openrouter token",
|
|
21
|
+
"api配置",
|
|
22
|
+
"anthropic",
|
|
23
|
+
"codex",
|
|
21
24
|
"cli",
|
|
22
|
-
"
|
|
25
|
+
"ai工具",
|
|
26
|
+
"openai",
|
|
27
|
+
"模型切换"
|
|
23
28
|
],
|
|
24
29
|
"author": "opentoken",
|
|
25
30
|
"license": "MIT",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@iarna/toml": "^2.2.5",
|
|
33
|
+
"js-yaml": "^4.1.0",
|
|
34
|
+
"chalk": "^4.1.2",
|
|
35
|
+
"inquirer": "^8.2.6",
|
|
36
|
+
"string-width": "^4.2.3"
|
|
37
|
+
},
|
|
26
38
|
"engines": {
|
|
27
39
|
"node": ">=14.0.0"
|
|
28
40
|
},
|
|
29
|
-
"
|
|
30
|
-
"
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
31
43
|
}
|
|
32
|
-
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const TOML = require('@iarna/toml');
|
|
7
|
+
const {
|
|
8
|
+
ATOMCODE_OPENTOKEN_BASE_URL,
|
|
9
|
+
ATOMCODE_CONTEXT_WINDOW,
|
|
10
|
+
} = require('./models');
|
|
11
|
+
|
|
12
|
+
const CONFIG_DIR = path.join(os.homedir(), '.atomcode');
|
|
13
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.toml');
|
|
14
|
+
|
|
15
|
+
const PROVIDER_KEY = 'Opentoken';
|
|
16
|
+
|
|
17
|
+
function readRaw() {
|
|
18
|
+
if (!fs.existsSync(CONFIG_PATH)) return '';
|
|
19
|
+
return fs.readFileSync(CONFIG_PATH, 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getCurrentAtomcodeConfig() {
|
|
23
|
+
const raw = readRaw();
|
|
24
|
+
if (!raw || !String(raw).trim()) {
|
|
25
|
+
return { apiKey: '', model: '' };
|
|
26
|
+
}
|
|
27
|
+
const data = TOML.parse(raw);
|
|
28
|
+
const p =
|
|
29
|
+
data &&
|
|
30
|
+
typeof data === 'object' &&
|
|
31
|
+
data.providers &&
|
|
32
|
+
typeof data.providers === 'object' &&
|
|
33
|
+
!Array.isArray(data.providers)
|
|
34
|
+
? data.providers[PROVIDER_KEY]
|
|
35
|
+
: null;
|
|
36
|
+
return {
|
|
37
|
+
apiKey: p && p.api_key != null ? String(p.api_key) : '',
|
|
38
|
+
model: p && p.model != null ? String(p.model) : '',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function applyAtomcodeConfig(patch) {
|
|
43
|
+
const { apiKey, model } = patch || {};
|
|
44
|
+
let data = {};
|
|
45
|
+
const raw = readRaw();
|
|
46
|
+
if (raw && String(raw).trim()) {
|
|
47
|
+
try {
|
|
48
|
+
data = TOML.parse(raw);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
'现有 config.toml 无法解析,请先备份后手动修复:' + e.message,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) {
|
|
56
|
+
throw new Error('config.toml 根节点必须是表(TOML table)');
|
|
57
|
+
}
|
|
58
|
+
if (
|
|
59
|
+
!data.providers ||
|
|
60
|
+
typeof data.providers !== 'object' ||
|
|
61
|
+
Array.isArray(data.providers)
|
|
62
|
+
) {
|
|
63
|
+
data.providers = {};
|
|
64
|
+
}
|
|
65
|
+
const prev =
|
|
66
|
+
data.providers[PROVIDER_KEY] &&
|
|
67
|
+
typeof data.providers[PROVIDER_KEY] === 'object' &&
|
|
68
|
+
!Array.isArray(data.providers[PROVIDER_KEY])
|
|
69
|
+
? { ...data.providers[PROVIDER_KEY] }
|
|
70
|
+
: {};
|
|
71
|
+
const merged = {
|
|
72
|
+
...prev,
|
|
73
|
+
type: 'openai',
|
|
74
|
+
base_url: ATOMCODE_OPENTOKEN_BASE_URL,
|
|
75
|
+
context_window: ATOMCODE_CONTEXT_WINDOW,
|
|
76
|
+
};
|
|
77
|
+
if (apiKey !== undefined) merged.api_key = apiKey;
|
|
78
|
+
if (model !== undefined) merged.model = model;
|
|
79
|
+
|
|
80
|
+
data.providers[PROVIDER_KEY] = merged;
|
|
81
|
+
data.default_provider = PROVIDER_KEY;
|
|
82
|
+
|
|
83
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
84
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
fs.writeFileSync(CONFIG_PATH, TOML.stringify(data), 'utf-8');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
CONFIG_PATH,
|
|
91
|
+
PROVIDER_KEY,
|
|
92
|
+
getCurrentAtomcodeConfig,
|
|
93
|
+
applyAtomcodeConfig,
|
|
94
|
+
};
|