@bman654/clodex 2.19.0 → 2.20.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/%40bman654%2Fclodex.svg)](https://www.npmjs.com/package/@bman654/clodex)
4
4
 
5
- **clodex** lets you use your ChatGPT/Codex plan, OpenAI API models, or OpenCode Go models with Claude Code as first-class model choices.
5
+ **clodex** lets you use your ChatGPT/Codex plan, OpenAI API models, OpenCode Go or Ollama Cloud models, or models from a custom OpenAI-compatible server with Claude Code as first-class model choices.
6
6
  You can use them anywhere you use Anthropic models like Opus and Sonnet — as the main session model, and in subagents, workflows, and agent teams. Clodex integrates them directly into Claude Code, using Claude Code's system prompt.
7
7
  It works with your existing Claude Code plan as well as your Codex plans WITHOUT violating Anthropic's ToS.
8
8
  No messing with CMUX or child codex processes or any of that stuff.
@@ -43,6 +43,7 @@ clodex claude # 5. launch Claude Code on an OpenAI model
43
43
  | OpenAI (ChatGPT / Codex plan) | OAuth | Fully supported by the clodex maintainer |
44
44
  | OpenCode Go | API key | Community-supported — maintained by its contributor |
45
45
  | Custom OpenAI-compatible server (OpenRouter, vLLM, …) | API key, or none | Community-supported — maintained by its contributor |
46
+ | Ollama Cloud | API key | Community-supported — maintained by its contributor |
46
47
 
47
48
  **Community-supported** means the maintainer holds no account for that service,
48
49
  so it cannot be exercised against the live API here or debugged when the vendor
@@ -124,7 +125,7 @@ Launch Claude Code bridged to configured model providers. Unrecognized flags (an
124
125
  | `--dry-run` | Run the wizard but print a launch preview instead of launching (never persists anything) |
125
126
  | `--trace` | Write debug logs to `~/.clodex/logs/` and show errors on exit |
126
127
  | `--fast` | Request Codex fast mode (`service_tier=priority`) for ChatGPT/Codex OAuth routes; equivalent to `CLODEX_SERVICE_TIER=fast` |
127
- | `--provider <id>` | Endpoint mode only: boot provider id (`openai`, `openai-oauth`, or `opencode-go`); with `--model`, skips the wizard |
128
+ | `--provider <id>` | Endpoint mode only: boot provider id of a configured provider (e.g. `openai`, `openai-oauth`, `opencode-go`, `ollama-cloud`, or `custom-<name>`); with `--model`, skips the wizard |
128
129
  | `--model <id>` | Endpoint mode only: boot model id; with `--provider`, skips the wizard |
129
130
  | `--context <model=stop>` | Use a different share of a model's context window for this launch only; never saved. Reaches Claude Code through the exported catalog, so a binary patched by `clodex patch` keeps its baked window until the stop is saved and the patch re-run |
130
131
  | `--help`, `--version` | Help / version |
@@ -304,7 +305,7 @@ Two things worth knowing about the numbers:
304
305
  | Subcommand | Effect |
305
306
  | --- | --- |
306
307
  | *(none)* | Provider hub wizard |
307
- | `add` | Add OpenAI or OpenCode Go with an API key, add a custom OpenAI-compatible server by base URL, or sign in with ChatGPT |
308
+ | `add` | Add OpenAI, OpenCode Go or Ollama Cloud with an API key, add a custom OpenAI-compatible server by base URL, or sign in with ChatGPT |
308
309
  | `auth openai` | Sign in with ChatGPT/Codex-plan OAuth (device code; `--browser` for workspaces that disable device codes) |
309
310
  | `list` | Show configured providers |
310
311
  | `remove <id>` | Remove a provider by id |
@@ -324,7 +325,9 @@ and their seeded flags when discovery omits them. Models not using Responses-Lit
324
325
  this version header and are not hidden by this check. This check does not establish your
325
326
  account's model entitlement.
326
327
 
327
- Providers supported: `openai` (API key, platform.openai.com), `openai-oauth` (ChatGPT/Codex plan), and `opencode-go` (OpenCode Go API key). OpenCode Go exposes its Anthropic Messages, Chat Completions, and Responses models; only entries whose transport has been verified, live or against OpenCode's per-model endpoint table, are included. See [OpenCode Go provider](docs/opencode-go.md).
328
+ Providers supported: `openai` (API key, platform.openai.com), `openai-oauth` (ChatGPT/Codex plan), `opencode-go` (OpenCode Go API key), and `ollama-cloud` (Ollama Cloud API key). OpenCode Go exposes its Anthropic Messages, Chat Completions, and Responses models; only entries whose transport has been verified, live or against OpenCode's per-model endpoint table, are included. See [OpenCode Go provider](docs/opencode-go.md).
329
+
330
+ Ollama Cloud exposes a curated set of four Chat Completions models from `https://ollama.com/v1`; other models Ollama Cloud lists are hidden. See [Ollama Cloud provider](docs/ollama-cloud.md).
328
331
 
329
332
  A **custom OpenAI-compatible server** is any OpenAI-style API, given by its base URL (the part before `/chat/completions`), such as `https://openrouter.ai/api/v1`. `providers add` asks for a name, the base URL and an API key (leave it empty for a local server without auth), lists the server's models to check the connection, and saves the provider as `custom-<name>`. Its models then appear in `clodex models`; once saved as favorites they are addressed as `clodex:custom-<name>:<model>` and can be given a short alias with `clodex models --alias`. The base URL is checked before anything is saved and again when its models are refreshed: plain `http://` is accepted only after you confirm it, and only if every address the host resolves to is on this machine, your LAN or a private network such as Tailscale (loopback, private, unique-local IPv6 or carrier-grade-NAT `100.64.0.0/10` addresses); `https://` is accepted on any of those networks as well as the public internet; and a link-local (`169.254.0.0/16`, `fe80::/10`) or well-known cloud-metadata address is refused whether or not HTTP was approved. A server on another machine in your tailnet can be added by its `100.x` address, its MagicDNS name, or the HTTPS name `tailscale serve` gives it. A model's id and name are stored with surrounding whitespace trimmed, and a model whose trimmed id or name still contains a control character is skipped, whether the list is fetched by `providers add` or by `providers refresh-models`, and error text from the server is shown with control characters replaced by spaces. Only the OpenAI-compatible kind is offered in the menu.
330
333
 
package/dist/cli.js CHANGED
@@ -354,7 +354,7 @@ import { join } from "path";
354
354
  // package.json
355
355
  var package_default = {
356
356
  name: "@bman654/clodex",
357
- version: "2.19.0",
357
+ version: "2.20.0",
358
358
  publishConfig: {
359
359
  access: "public"
360
360
  },
@@ -5437,6 +5437,55 @@ function openCodeGoSessionHeaders(model, claudeSessionId) {
5437
5437
  return { "x-opencode-session": claudeSessionId ?? OPENCODE_GO_FALLBACK_SESSION_ID };
5438
5438
  }
5439
5439
 
5440
+ // src/data/ollama-cloud-models.ts
5441
+ var OLLAMA_CLOUD_PROVIDER_ID = "ollama-cloud";
5442
+ var OLLAMA_CLOUD_PROVIDER_NAME = "Ollama Cloud";
5443
+ var OLLAMA_CLOUD_COMPLETIONS_BASE_URL = "https://ollama.com/v1";
5444
+ var OPENAI_COMPATIBLE = "@ai-sdk/openai-compatible";
5445
+ var CATALOG = [
5446
+ {
5447
+ id: "kimi-k3",
5448
+ name: "Kimi K3",
5449
+ contextWindow: 1048576,
5450
+ family: "kimi",
5451
+ modalities: ["text", "image"]
5452
+ },
5453
+ { id: "glm-5.3", name: "GLM-5.3", contextWindow: 1048576, family: "glm", modalities: ["text"] },
5454
+ {
5455
+ id: "glm-5.3-flash",
5456
+ name: "GLM-5.3 Flash",
5457
+ contextWindow: 1048576,
5458
+ family: "glm",
5459
+ modalities: ["text", "image"]
5460
+ },
5461
+ {
5462
+ id: "deepseek-v4.1-flash",
5463
+ name: "DeepSeek V4.1 Flash",
5464
+ contextWindow: 1048576,
5465
+ family: "deepseek",
5466
+ modalities: ["text", "image"]
5467
+ }
5468
+ ].map((model) => ({
5469
+ ...model,
5470
+ upstreamModelId: model.id,
5471
+ modelFormat: "openai",
5472
+ npm: OPENAI_COMPATIBLE,
5473
+ apiUrl: OLLAMA_CLOUD_COMPLETIONS_BASE_URL,
5474
+ // Ollama publishes pay-as-you-go per-token rates (https://ollama.com/pricing),
5475
+ // but clodex does not record them for this provider: no cost is set, and the
5476
+ // template's preserveModelPricing keeps the global pricing cache off it.
5477
+ compatibility: {
5478
+ supportsStore: false,
5479
+ supportsDeveloperRole: false,
5480
+ maxTokensField: "max_tokens",
5481
+ // Effort mapping is unverified against ollama.com; send none.
5482
+ supportsReasoningEffort: false
5483
+ }
5484
+ }));
5485
+ function buildOllamaCloudModels() {
5486
+ return structuredClone(CATALOG);
5487
+ }
5488
+
5440
5489
  // src/provider-templates.ts
5441
5490
  async function verifyOpenCodeGoCredential(apiKey) {
5442
5491
  const model = buildOpenCodeGoModels().find((entry) => entry.modelFormat === "openai");
@@ -5482,6 +5531,40 @@ async function verifyOpenCodeGoCredential(apiKey) {
5482
5531
  }
5483
5532
  return null;
5484
5533
  }
5534
+ async function verifyOllamaCloudCredential(apiKey) {
5535
+ const model = buildOllamaCloudModels()[0];
5536
+ if (!model) return null;
5537
+ try {
5538
+ const response = await fetch(`${OLLAMA_CLOUD_COMPLETIONS_BASE_URL}/chat/completions`, {
5539
+ method: "POST",
5540
+ headers: {
5541
+ "Content-Type": "application/json",
5542
+ Authorization: `Bearer ${apiKey}`
5543
+ },
5544
+ body: JSON.stringify({ model: model.upstreamModelId ?? model.id }),
5545
+ signal: AbortSignal.timeout(TEST_TIMEOUT_MS)
5546
+ });
5547
+ if (response.status === 401 || response.status === 403) {
5548
+ let entitlement = false;
5549
+ if (response.status === 403) {
5550
+ const text6 = await response.text().catch(() => "");
5551
+ entitlement = /\b(?:subscription|plan|upgrade|quota|model|access)\b/i.test(text6) && !/\bapi[ _-]?key\b/i.test(text6);
5552
+ } else {
5553
+ await response.body?.cancel?.().catch(() => void 0);
5554
+ }
5555
+ if (!entitlement) {
5556
+ return "Ollama Cloud rejected this API key (authentication failed). Check the key and try again.";
5557
+ }
5558
+ } else {
5559
+ try {
5560
+ await response.body?.cancel?.();
5561
+ } catch {
5562
+ }
5563
+ }
5564
+ } catch {
5565
+ }
5566
+ return null;
5567
+ }
5485
5568
  var PROVIDER_TEMPLATES = [
5486
5569
  {
5487
5570
  id: "openai",
@@ -5493,6 +5576,24 @@ var PROVIDER_TEMPLATES = [
5493
5576
  modelSource: "api-list",
5494
5577
  supported: true
5495
5578
  },
5579
+ {
5580
+ id: OLLAMA_CLOUD_PROVIDER_ID,
5581
+ name: OLLAMA_CLOUD_PROVIDER_NAME,
5582
+ authType: "api",
5583
+ npm: "@ai-sdk/openai-compatible",
5584
+ defaultBaseUrl: OLLAMA_CLOUD_COMPLETIONS_BASE_URL,
5585
+ modelsPath: "/models",
5586
+ signupUrl: "https://ollama.com",
5587
+ modelSource: "api-list",
5588
+ verifyCredential: verifyOllamaCloudCredential,
5589
+ // Getter for the same reason as opencode-go: no eager catalog clone at import.
5590
+ get staticModels() {
5591
+ return buildOllamaCloudModels();
5592
+ },
5593
+ staticModelPolicy: "allowlist",
5594
+ preserveModelPricing: true,
5595
+ supported: true
5596
+ },
5496
5597
  {
5497
5598
  id: OPENCODE_GO_PROVIDER_ID,
5498
5599
  name: OPENCODE_GO_PROVIDER_NAME,
@@ -11221,6 +11322,10 @@ async function createLanguageModel(spec) {
11221
11322
  ...spec.authType !== "none" && apiKey.trim() ? { apiKey } : {},
11222
11323
  ...spec.authType === "none" ? { fetch: fetchWithoutCredentialHeaders } : {},
11223
11324
  ...spec.headers ? { headers: spec.headers } : {},
11325
+ // ollama.com sends a streamed usage chunk only when the request carries
11326
+ // stream_options.include_usage (observed 2026-10-13); without it every
11327
+ // streamed turn reports output_tokens 0. Scoped to this provider only.
11328
+ ...spec.providerId === OLLAMA_CLOUD_PROVIDER_ID ? { includeUsage: true } : {},
11224
11329
  ...spec.compatibility ? {
11225
11330
  transformRequestBody: (body) => transformOpenAiCompatibleRequestBody(body, spec.compatibility)
11226
11331
  } : {}
@@ -22714,7 +22819,7 @@ function parseArgs(args) {
22714
22819
  }
22715
22820
  function rootHelpText() {
22716
22821
  return `${pc13.bold("clodex")} v${VERSION}
22717
- Bridge Claude Code to OpenAI and OpenCode Go models.
22822
+ Bridge Claude Code to OpenAI, OpenCode Go, Ollama Cloud and custom OpenAI-compatible models.
22718
22823
 
22719
22824
  ${pc13.bold("Usage:")}
22720
22825
  clodex claude [options] [claude-flags]
@@ -22796,6 +22901,7 @@ ${pc13.bold("Providers:")}
22796
22901
  openai OpenAI API key (platform.openai.com)
22797
22902
  openai-oauth ChatGPT/Codex plan OAuth \u2014 sign in with clodex providers auth openai
22798
22903
  opencode-go OpenCode Go API key \u2014 add with clodex providers add
22904
+ ollama-cloud Ollama Cloud API key \u2014 add with clodex providers add
22799
22905
  custom-<name> Any OpenAI-compatible server (OpenRouter, LM Studio, \u2026) \u2014 add with clodex providers add
22800
22906
 
22801
22907
  ${pc13.bold("Model switching:")}