tokenmix 0.2.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 +21 -0
- package/README.md +72 -0
- package/bin/tokenmix.js +12 -0
- package/dist/agents/aider.js +58 -0
- package/dist/agents/aider.js.map +1 -0
- package/dist/agents/claude.js +82 -0
- package/dist/agents/claude.js.map +1 -0
- package/dist/agents/kilo.js +47 -0
- package/dist/agents/kilo.js.map +1 -0
- package/dist/agents/opencode.js +91 -0
- package/dist/agents/opencode.js.map +1 -0
- package/dist/agents/registry.js +15 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/api/client.js +128 -0
- package/dist/api/client.js.map +1 -0
- package/dist/cli.js +54 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/agent-runner.js +84 -0
- package/dist/commands/agent-runner.js.map +1 -0
- package/dist/commands/balance.js +17 -0
- package/dist/commands/balance.js.map +1 -0
- package/dist/commands/doctor.js +48 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/list.js +23 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/login.js +114 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.js +7 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/models.js +52 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/commands/topup.js +8 -0
- package/dist/commands/topup.js.map +1 -0
- package/dist/config/paths.js +17 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/store.js +41 -0
- package/dist/config/store.js.map +1 -0
- package/dist/utils/browser.js +5 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/exec.js +22 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/logger.js +23 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/prompt.js +30 -0
- package/dist/utils/prompt.js.map +1 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TokenMix
|
|
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,72 @@
|
|
|
1
|
+
# TokenMix CLI
|
|
2
|
+
|
|
3
|
+
Zero-config CLI to use any open-source coding agent with [TokenMix](https://tokenmix.ai) as the unified LLM backend.
|
|
4
|
+
|
|
5
|
+
One account, one balance, 75+ models routed automatically across Claude / GPT / Gemini / DeepSeek / Qwen / Moonshot / ...
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 1. Log in (opens browser, confirm a short code)
|
|
11
|
+
npx tokenmix login
|
|
12
|
+
|
|
13
|
+
# 2. Launch an agent
|
|
14
|
+
npx tokenmix opencode # install + configure + start OpenCode
|
|
15
|
+
npx tokenmix claude # install + configure + start Claude Code
|
|
16
|
+
npx tokenmix aider # configure + start Aider (Python required)
|
|
17
|
+
npx tokenmix kilo # print Kilo Code VSCode configuration
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Alternative login modes
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx tokenmix login --paste # interactive paste prompt (no browser)
|
|
24
|
+
npx tokenmix login --key sk-tm-... # supply API key directly (for CI / scripts)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Supported Agents
|
|
28
|
+
|
|
29
|
+
| Agent | Install | CLI Action |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| [OpenCode](https://github.com/sst/opencode) | `npm i -g opencode-ai` | full auto |
|
|
32
|
+
| [Claude Code](https://github.com/anthropics/claude-code) | `npm i -g @anthropic-ai/claude-code` | full auto |
|
|
33
|
+
| [Aider](https://github.com/Aider-AI/aider) | `pip install aider-chat` | semi auto |
|
|
34
|
+
| [Kilo Code](https://github.com/Kilo-Org/kilocode) | VSCode extension | config-only |
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
tokenmix login [--key sk-tm-xxx] Log in
|
|
40
|
+
tokenmix logout Remove credentials
|
|
41
|
+
tokenmix balance Open dashboard to view balance
|
|
42
|
+
tokenmix topup Open browser to top up
|
|
43
|
+
tokenmix models [--type chat] List available models with prices
|
|
44
|
+
tokenmix list List supported agents
|
|
45
|
+
tokenmix doctor Diagnose configuration
|
|
46
|
+
|
|
47
|
+
tokenmix opencode [args...] Launch OpenCode via TokenMix
|
|
48
|
+
tokenmix claude [args...] Launch Claude Code via TokenMix
|
|
49
|
+
tokenmix aider [args...] Launch Aider via TokenMix
|
|
50
|
+
tokenmix kilo Print Kilo Code configuration
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Configuration Location
|
|
54
|
+
|
|
55
|
+
Your TokenMix credentials are stored locally at:
|
|
56
|
+
|
|
57
|
+
- macOS: `~/Library/Application Support/tokenmix/config.json`
|
|
58
|
+
- Linux: `~/.config/tokenmix/config.json` (respects `XDG_CONFIG_HOME`)
|
|
59
|
+
- Windows: `%APPDATA%/tokenmix/config.json`
|
|
60
|
+
|
|
61
|
+
File permissions are restricted to `0600` (owner read/write only).
|
|
62
|
+
|
|
63
|
+
## Development
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pnpm install
|
|
67
|
+
pnpm dev opencode
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT — see [LICENSE](./LICENSE)
|
package/bin/tokenmix.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Production entry: load compiled CLI from dist.
|
|
3
|
+
// For dev, run `pnpm dev <command>` which uses tsx on src/cli.ts directly.
|
|
4
|
+
import('../dist/cli.js').catch((err) => {
|
|
5
|
+
// Common case: user ran `pnpm start` before `pnpm build`.
|
|
6
|
+
if (err && (err.code === 'ERR_MODULE_NOT_FOUND' || err.code === 'MODULE_NOT_FOUND')) {
|
|
7
|
+
console.error('tokenmix: build artifacts not found. Run `pnpm build` first, or use `pnpm dev`.')
|
|
8
|
+
process.exit(1)
|
|
9
|
+
}
|
|
10
|
+
console.error(err)
|
|
11
|
+
process.exit(1)
|
|
12
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { commandExists, run, captureRun } from '../utils/exec.js';
|
|
2
|
+
const AIDER_BIN = 'aider';
|
|
3
|
+
async function installCheck() {
|
|
4
|
+
const bin = await commandExists(AIDER_BIN);
|
|
5
|
+
if (!bin) {
|
|
6
|
+
const py = (await commandExists('python3')) || (await commandExists('python'));
|
|
7
|
+
if (!py) {
|
|
8
|
+
return {
|
|
9
|
+
installed: false,
|
|
10
|
+
hint: 'Aider requires Python 3. Install Python first, then `pipx install aider-chat`.',
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
installed: false,
|
|
15
|
+
hint: 'Run `pipx install aider-chat` (preferred) or `pip install aider-chat` to install Aider.',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const v = await captureRun(AIDER_BIN, ['--version']);
|
|
20
|
+
return { installed: true, version: v.stdout.trim() };
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return { installed: true };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function configure(apiKey, baseUrl, defaultModel) {
|
|
27
|
+
// Aider reads OPENAI_API_KEY and OPENAI_API_BASE. We pass via env at launch
|
|
28
|
+
// and avoid writing to global ~/.aider.conf.yml — keeps user's existing config clean.
|
|
29
|
+
return {
|
|
30
|
+
envVars: {
|
|
31
|
+
OPENAI_API_KEY: apiKey,
|
|
32
|
+
OPENAI_API_BASE: `${baseUrl}/v1`,
|
|
33
|
+
TOKENMIX_DEFAULT_MODEL: defaultModel,
|
|
34
|
+
},
|
|
35
|
+
notes: [
|
|
36
|
+
`Aider will use TokenMix via OpenAI-compatible endpoint.`,
|
|
37
|
+
`Default model: openai/${defaultModel} — override with --model.`,
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function launch(args, env) {
|
|
42
|
+
// Inject --model only if user didn't supply one.
|
|
43
|
+
const hasModel = args.some((a) => a === '--model' || a.startsWith('--model='));
|
|
44
|
+
const finalArgs = hasModel
|
|
45
|
+
? args
|
|
46
|
+
: ['--model', `openai/${env.TOKENMIX_DEFAULT_MODEL ?? 'claude-sonnet-4.6'}`, ...args];
|
|
47
|
+
await run(AIDER_BIN, finalArgs, { env });
|
|
48
|
+
}
|
|
49
|
+
export const AiderAgent = {
|
|
50
|
+
id: 'aider',
|
|
51
|
+
displayName: 'Aider',
|
|
52
|
+
description: 'Aider-AI/aider — paired-programming CLI (requires Python)',
|
|
53
|
+
installMode: 'auto-pip',
|
|
54
|
+
installCheck,
|
|
55
|
+
configure,
|
|
56
|
+
launch,
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=aider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aider.js","sourceRoot":"","sources":["../../src/agents/aider.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAEjE,MAAM,SAAS,GAAG,OAAO,CAAA;AAEzB,KAAK,UAAU,YAAY;IACzB,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,EAAE,GAAG,CAAC,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAA;QAC9E,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,IAAI,EAAE,gFAAgF;aACvF,CAAA;QACH,CAAC;QACD,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,IAAI,EAAE,yFAAyF;SAChG,CAAA;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;QACpD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAA;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;IAC5B,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,MAAc,EACd,OAAe,EACf,YAAoB;IAEpB,4EAA4E;IAC5E,sFAAsF;IACtF,OAAO;QACL,OAAO,EAAE;YACP,cAAc,EAAE,MAAM;YACtB,eAAe,EAAE,GAAG,OAAO,KAAK;YAChC,sBAAsB,EAAE,YAAY;SACrC;QACD,KAAK,EAAE;YACL,yDAAyD;YACzD,yBAAyB,YAAY,2BAA2B;SACjE;KACF,CAAA;AACH,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,IAAc,EAAE,GAA2B;IAC/D,iDAAiD;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAA;IAC9E,MAAM,SAAS,GAAG,QAAQ;QACxB,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,CAAC,SAAS,EAAE,UAAU,GAAG,CAAC,sBAAsB,IAAI,mBAAmB,EAAE,EAAE,GAAG,IAAI,CAAC,CAAA;IACvF,MAAM,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAoB;IACzC,EAAE,EAAE,OAAO;IACX,WAAW,EAAE,OAAO;IACpB,WAAW,EAAE,2DAA2D;IACxE,WAAW,EAAE,UAAU;IACvB,YAAY;IACZ,SAAS;IACT,MAAM;CACP,CAAA"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import { commandExists, run, captureRun } from '../utils/exec.js';
|
|
5
|
+
const CLAUDE_BIN = 'claude';
|
|
6
|
+
const CLAUDE_NPM_PACKAGE = '@anthropic-ai/claude-code';
|
|
7
|
+
async function installCheck() {
|
|
8
|
+
const bin = await commandExists(CLAUDE_BIN);
|
|
9
|
+
if (!bin) {
|
|
10
|
+
return {
|
|
11
|
+
installed: false,
|
|
12
|
+
hint: `Will install via: npm install -g ${CLAUDE_NPM_PACKAGE}`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const v = await captureRun(CLAUDE_BIN, ['--version']);
|
|
17
|
+
return { installed: true, version: v.stdout.trim() };
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return { installed: true };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function install() {
|
|
24
|
+
await run('npm', ['install', '-g', CLAUDE_NPM_PACKAGE]);
|
|
25
|
+
}
|
|
26
|
+
async function configure(apiKey, baseUrl, _defaultModel) {
|
|
27
|
+
// Claude Code reads:
|
|
28
|
+
// - env: ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY
|
|
29
|
+
// - ~/.claude/settings.json (env block is honored)
|
|
30
|
+
// We write the settings.json env block AND pass env on launch (belt + suspenders).
|
|
31
|
+
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
|
32
|
+
let existing = {};
|
|
33
|
+
try {
|
|
34
|
+
const raw = await fs.readFile(settingsPath, 'utf-8');
|
|
35
|
+
existing = JSON.parse(raw);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// first run
|
|
39
|
+
}
|
|
40
|
+
const existingEnv = existing.env || {};
|
|
41
|
+
const next = {
|
|
42
|
+
...existing,
|
|
43
|
+
env: {
|
|
44
|
+
...existingEnv,
|
|
45
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
46
|
+
ANTHROPIC_API_KEY: apiKey,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
await fs.ensureDir(path.dirname(settingsPath));
|
|
50
|
+
await fs.writeFile(settingsPath, JSON.stringify(next, null, 2));
|
|
51
|
+
try {
|
|
52
|
+
await fs.chmod(settingsPath, 0o600);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// ignore
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
configPath: settingsPath,
|
|
59
|
+
envVars: {
|
|
60
|
+
ANTHROPIC_BASE_URL: baseUrl,
|
|
61
|
+
ANTHROPIC_API_KEY: apiKey,
|
|
62
|
+
},
|
|
63
|
+
notes: [
|
|
64
|
+
'Available Claude models via tokenmix: claude-opus-4.7, claude-sonnet-4.6, claude-haiku-4.5',
|
|
65
|
+
'Run `tokenmix models --type chat` for the full list.',
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
async function launch(args, env) {
|
|
70
|
+
await run(CLAUDE_BIN, args, { env });
|
|
71
|
+
}
|
|
72
|
+
export const ClaudeCodeAgent = {
|
|
73
|
+
id: 'claude',
|
|
74
|
+
displayName: 'Claude Code',
|
|
75
|
+
description: 'anthropics/claude-code — official Anthropic CLI coding agent',
|
|
76
|
+
installMode: 'auto-npm',
|
|
77
|
+
installCheck,
|
|
78
|
+
install,
|
|
79
|
+
configure,
|
|
80
|
+
launch,
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { commandExists } from '../utils/exec.js';
|
|
2
|
+
async function installCheck() {
|
|
3
|
+
const code = await commandExists('code');
|
|
4
|
+
if (!code) {
|
|
5
|
+
return {
|
|
6
|
+
installed: false,
|
|
7
|
+
hint: 'Kilo Code is a VSCode extension. Install VSCode first, then install "Kilo Code" from the marketplace.',
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
installed: true,
|
|
12
|
+
hint: 'If you have not installed the Kilo Code extension yet, search "Kilo Code" in the VSCode marketplace.',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function configure(apiKey, baseUrl, defaultModel) {
|
|
16
|
+
// Kilo Code is a VSCode extension; there is no CLI launcher.
|
|
17
|
+
// We print the configuration values for the user to paste into Kilo settings.
|
|
18
|
+
return {
|
|
19
|
+
notes: [
|
|
20
|
+
'Kilo Code is a VSCode extension and does not have a CLI launcher.',
|
|
21
|
+
'Configure Kilo Code with the following:',
|
|
22
|
+
'',
|
|
23
|
+
` Provider: OpenAI Compatible`,
|
|
24
|
+
` Base URL: ${baseUrl}/v1`,
|
|
25
|
+
` API Key: ${apiKey}`,
|
|
26
|
+
` Default Model: ${defaultModel}`,
|
|
27
|
+
'',
|
|
28
|
+
'Or paste this JSON snippet into Kilo Code settings (Settings → Providers → JSON):',
|
|
29
|
+
'',
|
|
30
|
+
JSON.stringify({
|
|
31
|
+
provider: 'openai-compatible',
|
|
32
|
+
openAiBaseUrl: `${baseUrl}/v1`,
|
|
33
|
+
openAiApiKey: apiKey,
|
|
34
|
+
defaultModelId: defaultModel,
|
|
35
|
+
}, null, 2),
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export const KiloAgent = {
|
|
40
|
+
id: 'kilo',
|
|
41
|
+
displayName: 'Kilo Code',
|
|
42
|
+
description: 'Kilo-Org/kilocode — VSCode extension (config-only)',
|
|
43
|
+
installMode: 'manual-vscode',
|
|
44
|
+
installCheck,
|
|
45
|
+
configure,
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=kilo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kilo.js","sourceRoot":"","sources":["../../src/agents/kilo.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAEhD,KAAK,UAAU,YAAY;IACzB,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,IAAI,EAAE,uGAAuG;SAC9G,CAAA;IACH,CAAC;IACD,OAAO;QACL,SAAS,EAAE,IAAI;QACf,IAAI,EAAE,sGAAsG;KAC7G,CAAA;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CACtB,MAAc,EACd,OAAe,EACf,YAAoB;IAEpB,6DAA6D;IAC7D,8EAA8E;IAC9E,OAAO;QACL,KAAK,EAAE;YACL,mEAAmE;YACnE,yCAAyC;YACzC,EAAE;YACF,oCAAoC;YACpC,oBAAoB,OAAO,KAAK;YAChC,oBAAoB,MAAM,EAAE;YAC5B,oBAAoB,YAAY,EAAE;YAClC,EAAE;YACF,mFAAmF;YACnF,EAAE;YACF,IAAI,CAAC,SAAS,CACZ;gBACE,QAAQ,EAAE,mBAAmB;gBAC7B,aAAa,EAAE,GAAG,OAAO,KAAK;gBAC9B,YAAY,EAAE,MAAM;gBACpB,cAAc,EAAE,YAAY;aAC7B,EACD,IAAI,EACJ,CAAC,CACF;SACF;KACF,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAoB;IACxC,EAAE,EAAE,MAAM;IACV,WAAW,EAAE,WAAW;IACxB,WAAW,EAAE,oDAAoD;IACjE,WAAW,EAAE,eAAe;IAC5B,YAAY;IACZ,SAAS;CACV,CAAA"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import { commandExists, run, captureRun } from '../utils/exec.js';
|
|
5
|
+
const OPENCODE_BIN = 'opencode';
|
|
6
|
+
const OPENCODE_NPM_PACKAGE = 'opencode-ai';
|
|
7
|
+
// OpenCode config search order (lowest to highest priority):
|
|
8
|
+
// ~/.config/opencode/opencode.json ← we write here
|
|
9
|
+
// $OPENCODE_CONFIG
|
|
10
|
+
// ./opencode.json (project)
|
|
11
|
+
// $OPENCODE_CONFIG_CONTENT (inline)
|
|
12
|
+
function configPath() {
|
|
13
|
+
const xdgHome = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config');
|
|
14
|
+
return path.join(xdgHome, 'opencode', 'opencode.json');
|
|
15
|
+
}
|
|
16
|
+
async function installCheck() {
|
|
17
|
+
const bin = await commandExists(OPENCODE_BIN);
|
|
18
|
+
if (!bin) {
|
|
19
|
+
return {
|
|
20
|
+
installed: false,
|
|
21
|
+
hint: `Will install via: npm install -g ${OPENCODE_NPM_PACKAGE}`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const v = await captureRun(OPENCODE_BIN, ['--version']);
|
|
26
|
+
return { installed: true, version: v.stdout.trim() };
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return { installed: true };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function install() {
|
|
33
|
+
await run('npm', ['install', '-g', OPENCODE_NPM_PACKAGE]);
|
|
34
|
+
}
|
|
35
|
+
async function configure(apiKey, baseUrl, defaultModel) {
|
|
36
|
+
const filePath = configPath();
|
|
37
|
+
let existing = {};
|
|
38
|
+
try {
|
|
39
|
+
const raw = await fs.readFile(filePath, 'utf-8');
|
|
40
|
+
existing = JSON.parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// not present yet
|
|
44
|
+
}
|
|
45
|
+
// Register tokenmix as an OpenAI-compatible provider.
|
|
46
|
+
// OpenCode uses @ai-sdk/openai-compatible under the hood for custom providers.
|
|
47
|
+
const tokenmixProvider = {
|
|
48
|
+
npm: '@ai-sdk/openai-compatible',
|
|
49
|
+
name: 'TokenMix',
|
|
50
|
+
options: {
|
|
51
|
+
baseURL: `${baseUrl}/v1`,
|
|
52
|
+
apiKey,
|
|
53
|
+
},
|
|
54
|
+
// Listed models populate /connect picker; users can still type any tokenmix short_id.
|
|
55
|
+
models: {
|
|
56
|
+
[defaultModel]: { name: defaultModel },
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const existingProvider = existing.provider || {};
|
|
60
|
+
const next = {
|
|
61
|
+
...existing,
|
|
62
|
+
model: existing.model ?? `tokenmix/${defaultModel}`,
|
|
63
|
+
provider: {
|
|
64
|
+
...existingProvider,
|
|
65
|
+
tokenmix: tokenmixProvider,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
69
|
+
await fs.writeFile(filePath, JSON.stringify(next, null, 2));
|
|
70
|
+
return {
|
|
71
|
+
configPath: filePath,
|
|
72
|
+
notes: [
|
|
73
|
+
`Default model set to tokenmix/${defaultModel}`,
|
|
74
|
+
`To switch models, run \`tokenmix models\` or use \`/connect\` inside OpenCode.`,
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function launch(args) {
|
|
79
|
+
await run(OPENCODE_BIN, args);
|
|
80
|
+
}
|
|
81
|
+
export const OpenCodeAgent = {
|
|
82
|
+
id: 'opencode',
|
|
83
|
+
displayName: 'OpenCode',
|
|
84
|
+
description: 'sst/opencode — open source AI coding agent (TUI / Desktop / IDE)',
|
|
85
|
+
installMode: 'auto-npm',
|
|
86
|
+
installCheck,
|
|
87
|
+
install,
|
|
88
|
+
configure,
|
|
89
|
+
launch,
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/agents/opencode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { OpenCodeAgent } from './opencode.js';
|
|
2
|
+
import { ClaudeCodeAgent } from './claude.js';
|
|
3
|
+
import { AiderAgent } from './aider.js';
|
|
4
|
+
import { KiloAgent } from './kilo.js';
|
|
5
|
+
// Ordered by historical ARPU on tokenmix (highest first). New agents go to the bottom.
|
|
6
|
+
export const AGENTS = [
|
|
7
|
+
OpenCodeAgent,
|
|
8
|
+
ClaudeCodeAgent,
|
|
9
|
+
AiderAgent,
|
|
10
|
+
KiloAgent,
|
|
11
|
+
];
|
|
12
|
+
export function findAgent(id) {
|
|
13
|
+
return AGENTS.find((a) => a.id === id);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/agents/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAErC,uFAAuF;AACvF,MAAM,CAAC,MAAM,MAAM,GAA+B;IAChD,aAAa;IACb,eAAe;IACf,UAAU;IACV,SAAS;CACV,CAAA;AAED,MAAM,UAAU,SAAS,CAAC,EAAU;IAClC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;AACxC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { readConfig, apiBaseUrl } from '../config/store.js';
|
|
3
|
+
export class ApiError extends Error {
|
|
4
|
+
status;
|
|
5
|
+
constructor(status, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function unwrap(resp) {
|
|
11
|
+
const body = resp.data;
|
|
12
|
+
if (body && typeof body.code === 'number' && body.code !== 0) {
|
|
13
|
+
throw new ApiError(0, body.message || 'API error');
|
|
14
|
+
}
|
|
15
|
+
return body?.data ?? body;
|
|
16
|
+
}
|
|
17
|
+
function handleAxios(err) {
|
|
18
|
+
const e = err;
|
|
19
|
+
if (e.response) {
|
|
20
|
+
const msg = e.response.data?.message ||
|
|
21
|
+
e.response.data?.error?.message ||
|
|
22
|
+
e.message;
|
|
23
|
+
throw new ApiError(e.response.status, msg);
|
|
24
|
+
}
|
|
25
|
+
throw new ApiError(0, e.message || 'network error');
|
|
26
|
+
}
|
|
27
|
+
// Public endpoint, no auth required.
|
|
28
|
+
// Note: backend pagination uses `per_page` (NOT `page_size`); max 500 (anything >500 falls back to 20).
|
|
29
|
+
// 162 active models today, so per_page=500 fetches all in one round-trip.
|
|
30
|
+
export async function listPublicModels(cfg) {
|
|
31
|
+
const c = cfg || (await readConfig());
|
|
32
|
+
try {
|
|
33
|
+
const r = await axios.get(`${apiBaseUrl(c)}/api/models`, {
|
|
34
|
+
params: { per_page: 500 },
|
|
35
|
+
timeout: 15000,
|
|
36
|
+
});
|
|
37
|
+
const list = unwrap(r);
|
|
38
|
+
return Array.isArray(list) ? list : list?.list ?? [];
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
handleAxios(err);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Verify the API key works by calling the OpenAI-compatible /v1/models endpoint.
|
|
45
|
+
// A 200 implies the key is valid and not revoked/expired/over-quota.
|
|
46
|
+
export async function verifyApiKey(apiKey, baseUrl) {
|
|
47
|
+
try {
|
|
48
|
+
const r = await axios.get(`${baseUrl || 'https://api.tokenmix.ai'}/v1/models`, {
|
|
49
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
50
|
+
timeout: 15000,
|
|
51
|
+
});
|
|
52
|
+
return r.status === 200;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class DeviceFlowError extends Error {
|
|
59
|
+
code;
|
|
60
|
+
constructor(code, message) {
|
|
61
|
+
super(message);
|
|
62
|
+
this.code = code;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export async function startDeviceAuthorization(baseUrl, clientName = 'tokenmix-cli') {
|
|
66
|
+
try {
|
|
67
|
+
const r = await axios.post(`${baseUrl}/api/auth/device/code`, { client_name: clientName }, { timeout: 15000 });
|
|
68
|
+
return unwrap(r);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
handleAxios(err);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Poll until approved, denied, or expired. Returns the API key when approved.
|
|
75
|
+
// Throws DeviceFlowError with code ∈ {expired_token, access_denied, api_key_limit_reached, timeout} on terminal failures.
|
|
76
|
+
// onTick is called once per polling iteration with the seconds remaining (for progress display).
|
|
77
|
+
export async function pollDeviceToken(baseUrl, auth, onTick) {
|
|
78
|
+
let intervalMs = Math.max(1, auth.interval) * 1000;
|
|
79
|
+
const deadline = Date.now() + auth.expires_in * 1000;
|
|
80
|
+
while (Date.now() < deadline) {
|
|
81
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
82
|
+
if (onTick) {
|
|
83
|
+
onTick(Math.max(0, Math.round((deadline - Date.now()) / 1000)));
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const r = await axios.post(`${baseUrl}/api/auth/device/token`, { device_code: auth.device_code }, { timeout: 15000, validateStatus: () => true });
|
|
87
|
+
if (r.status === 200 && r.data?.code === 0) {
|
|
88
|
+
const body = r.data.data;
|
|
89
|
+
return {
|
|
90
|
+
apiKey: body.access_token,
|
|
91
|
+
apiKeyId: body.api_key_id,
|
|
92
|
+
userEmail: body.user_email,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// backend uses `message` to carry the OAuth-standard error code
|
|
96
|
+
const code = String(r.data?.message ?? '').trim();
|
|
97
|
+
switch (code) {
|
|
98
|
+
case 'authorization_pending':
|
|
99
|
+
continue;
|
|
100
|
+
case 'slow_down': {
|
|
101
|
+
const ra = parseInt(String(r.headers['retry-after'] ?? '5'), 10);
|
|
102
|
+
if (Number.isFinite(ra) && ra > 0)
|
|
103
|
+
intervalMs = ra * 1000;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
case 'expired_token':
|
|
107
|
+
throw new DeviceFlowError('expired_token', 'Authorization expired. Run `tokenmix login` again.');
|
|
108
|
+
case 'access_denied':
|
|
109
|
+
throw new DeviceFlowError('access_denied', 'Authorization was denied or the code is invalid.');
|
|
110
|
+
case 'api_key_limit_reached':
|
|
111
|
+
throw new DeviceFlowError('api_key_limit_reached', 'You have reached the 20 API keys per account limit. Delete unused keys at https://tokenmix.ai/dashboard/keys');
|
|
112
|
+
default:
|
|
113
|
+
throw new DeviceFlowError(code || 'unknown', `Unexpected device token response: ${code || r.status}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
if (err instanceof DeviceFlowError)
|
|
118
|
+
throw err;
|
|
119
|
+
// Network glitch — retry on next iteration unless we're past the deadline.
|
|
120
|
+
if (Date.now() >= deadline) {
|
|
121
|
+
throw new DeviceFlowError('timeout', 'Authorization timed out. Run `tokenmix login` again.');
|
|
122
|
+
}
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
throw new DeviceFlowError('timeout', 'Authorization timed out before the user approved. Run `tokenmix login` again.');
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { loginCommand } from './commands/login.js';
|
|
3
|
+
import { logoutCommand } from './commands/logout.js';
|
|
4
|
+
import { balanceCommand } from './commands/balance.js';
|
|
5
|
+
import { topupCommand } from './commands/topup.js';
|
|
6
|
+
import { modelsCommand } from './commands/models.js';
|
|
7
|
+
import { listCommand } from './commands/list.js';
|
|
8
|
+
import { doctorCommand } from './commands/doctor.js';
|
|
9
|
+
import { registerAgentCommands } from './commands/agent-runner.js';
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name('tokenmix')
|
|
13
|
+
.description('Zero-config CLI to use any open-source coding agent with TokenMix as the unified LLM backend.')
|
|
14
|
+
.version('0.1.0');
|
|
15
|
+
program
|
|
16
|
+
.command('login')
|
|
17
|
+
.description('Log in to TokenMix (default: browser device authorization)')
|
|
18
|
+
.option('-k, --key <apiKey>', 'Paste an API key directly (skip browser flow, useful in CI)')
|
|
19
|
+
.option('-p, --paste', 'Force interactive paste prompt instead of browser flow')
|
|
20
|
+
.option('-u, --url <baseUrl>', 'Override API base URL (default: https://api.tokenmix.ai)')
|
|
21
|
+
.action(loginCommand);
|
|
22
|
+
program
|
|
23
|
+
.command('logout')
|
|
24
|
+
.description('Remove stored credentials from this machine')
|
|
25
|
+
.action(logoutCommand);
|
|
26
|
+
program
|
|
27
|
+
.command('balance')
|
|
28
|
+
.description('Open the dashboard to view your balance')
|
|
29
|
+
.action(balanceCommand);
|
|
30
|
+
program
|
|
31
|
+
.command('topup')
|
|
32
|
+
.description('Open the browser to top up your account')
|
|
33
|
+
.action(topupCommand);
|
|
34
|
+
program
|
|
35
|
+
.command('models')
|
|
36
|
+
.description('List available models with prices')
|
|
37
|
+
.option('-t, --type <type>', 'Filter by type: chat | embedding | image | audio | video')
|
|
38
|
+
.action(modelsCommand);
|
|
39
|
+
program
|
|
40
|
+
.command('list')
|
|
41
|
+
.description('List supported coding agents')
|
|
42
|
+
.action(listCommand);
|
|
43
|
+
program
|
|
44
|
+
.command('doctor')
|
|
45
|
+
.description('Diagnose CLI configuration and agent installation')
|
|
46
|
+
.action(doctorCommand);
|
|
47
|
+
// Register one subcommand per supported agent (opencode, claude, aider, kilo, ...).
|
|
48
|
+
registerAgentCommands(program);
|
|
49
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
50
|
+
const e = err;
|
|
51
|
+
console.error(e?.message ?? err);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAElE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,+FAA+F,CAAC;KAC5G,OAAO,CAAC,OAAO,CAAC,CAAA;AAEnB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,4DAA4D,CAAC;KACzE,MAAM,CAAC,oBAAoB,EAAE,6DAA6D,CAAC;KAC3F,MAAM,CAAC,aAAa,EAAE,wDAAwD,CAAC;KAC/E,MAAM,CAAC,qBAAqB,EAAE,0DAA0D,CAAC;KACzF,MAAM,CAAC,YAAY,CAAC,CAAA;AAEvB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,6CAA6C,CAAC;KAC1D,MAAM,CAAC,aAAa,CAAC,CAAA;AAExB,OAAO;KACJ,OAAO,CAAC,SAAS,CAAC;KAClB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,cAAc,CAAC,CAAA;AAEzB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,yCAAyC,CAAC;KACtD,MAAM,CAAC,YAAY,CAAC,CAAA;AAEvB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,mBAAmB,EAAE,0DAA0D,CAAC;KACvF,MAAM,CAAC,aAAa,CAAC,CAAA;AAExB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,8BAA8B,CAAC;KAC3C,MAAM,CAAC,WAAW,CAAC,CAAA;AAEtB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,aAAa,CAAC,CAAA;AAExB,oFAAoF;AACpF,qBAAqB,CAAC,OAAO,CAAC,CAAA;AAE9B,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtD,MAAM,CAAC,GAAG,GAA2B,CAAA;IACrC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,IAAI,GAAG,CAAC,CAAA;IAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|