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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +72 -0
  3. package/bin/tokenmix.js +12 -0
  4. package/dist/agents/aider.js +58 -0
  5. package/dist/agents/aider.js.map +1 -0
  6. package/dist/agents/claude.js +82 -0
  7. package/dist/agents/claude.js.map +1 -0
  8. package/dist/agents/kilo.js +47 -0
  9. package/dist/agents/kilo.js.map +1 -0
  10. package/dist/agents/opencode.js +91 -0
  11. package/dist/agents/opencode.js.map +1 -0
  12. package/dist/agents/registry.js +15 -0
  13. package/dist/agents/registry.js.map +1 -0
  14. package/dist/agents/types.js +2 -0
  15. package/dist/agents/types.js.map +1 -0
  16. package/dist/api/client.js +128 -0
  17. package/dist/api/client.js.map +1 -0
  18. package/dist/cli.js +54 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/commands/agent-runner.js +84 -0
  21. package/dist/commands/agent-runner.js.map +1 -0
  22. package/dist/commands/balance.js +17 -0
  23. package/dist/commands/balance.js.map +1 -0
  24. package/dist/commands/doctor.js +48 -0
  25. package/dist/commands/doctor.js.map +1 -0
  26. package/dist/commands/list.js +23 -0
  27. package/dist/commands/list.js.map +1 -0
  28. package/dist/commands/login.js +114 -0
  29. package/dist/commands/login.js.map +1 -0
  30. package/dist/commands/logout.js +7 -0
  31. package/dist/commands/logout.js.map +1 -0
  32. package/dist/commands/models.js +52 -0
  33. package/dist/commands/models.js.map +1 -0
  34. package/dist/commands/topup.js +8 -0
  35. package/dist/commands/topup.js.map +1 -0
  36. package/dist/config/paths.js +17 -0
  37. package/dist/config/paths.js.map +1 -0
  38. package/dist/config/store.js +41 -0
  39. package/dist/config/store.js.map +1 -0
  40. package/dist/utils/browser.js +5 -0
  41. package/dist/utils/browser.js.map +1 -0
  42. package/dist/utils/exec.js +22 -0
  43. package/dist/utils/exec.js.map +1 -0
  44. package/dist/utils/logger.js +23 -0
  45. package/dist/utils/logger.js.map +1 -0
  46. package/dist/utils/prompt.js +30 -0
  47. package/dist/utils/prompt.js.map +1 -0
  48. 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)
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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
@@ -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"}