@heyditto/cli 3.4.0 → 3.5.1

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
@@ -1281,6 +1281,71 @@ says so. Tracked artifact directories are flagged rather than claimed excluded.
1281
1281
  `heyditto offload` refuses to delete a folder when unrelated files, unknown projects or
1282
1282
  escaping symlinks beneath it would be lost.
1283
1283
 
1284
+ ### Cloud-assisted CLIProxy inference
1285
+
1286
+ To use subscription inference with Router optimizations, cloud session recording
1287
+ and remote control, connect a local CLIProxy through an outbound relay. Use a
1288
+ dedicated **personal** endpoint and a model exposed by your local proxy:
1289
+
1290
+ ```sh
1291
+ heyditto endpoints create my-local-models
1292
+ heyditto cliproxy relay --endpoint my-local-models --enable --model gpt-6.1-sol
1293
+ # In another terminal, while the relay stays running:
1294
+ heyditto claude --endpoint my-local-models
1295
+ ```
1296
+
1297
+ The first `--enable` changes that endpoint to relay-only BYOK inference, enables
1298
+ cloud trace recording and selects Router inference instead of a native-account
1299
+ default. Later relay starts omit `--enable`. The CLI keeps an outbound
1300
+ authenticated connection; your local proxy access key and provider credentials
1301
+ stay on your computer. Stopping the relay makes requests fail without switching
1302
+ to paid inference. Existing Router optimization/tool resource costs still apply.
1303
+
1304
+ The first release supports one relay per personal endpoint and requires the API
1305
+ and inference request to reach the same backend process. Organization endpoints
1306
+ and routing across multiple backend processes are not supported yet. The local
1307
+ model IDs must match the endpoint's routes/aliases; the cloud model list does not
1308
+ discover laptop providers. Deploy the matching backend before enabling an endpoint.
1309
+
1310
+ For the official T3 Code app, see [the Router setup guide](docs/t3-code.md).
1311
+
1312
+ ### Direct local CLIProxy inference
1313
+
1314
+ Use a locally installed CLIProxy with Claude Code or Codex instead of Ditto Router
1315
+ inference:
1316
+
1317
+ ```sh
1318
+ heyditto claude --cliproxy --endpoint my-tools
1319
+ heyditto codex --cliproxy --endpoint my-tools --model gpt-6-luna
1320
+ heyditto claude --cliproxy http://127.0.0.1:8317/v1 --dry-run --endpoint my-tools
1321
+ ```
1322
+
1323
+ `--cliproxy` detects EasyCLIProxyAPI's saved host, port and API access key. It
1324
+ reads the GUI configuration from the platform's `com.cpa.gui` application-data
1325
+ directory. Set `CLIPROXY_CONFIG` for another EasyCLIProxyAPI TOML file, or use
1326
+ `CLIPROXY_BASE_URL` and `CLIPROXY_API_KEY` for a standalone CLIProxy installation.
1327
+ The URL must be loopback (`127.0.0.1`, `localhost` or `::1`); discovery does not
1328
+ accept LAN/public hosts, URL credentials or redirects. An API key is never
1329
+ copied between two different proxy addresses automatically.
1330
+
1331
+ Before launching, the CLI authenticates `/v1/models` and selects a Claude/GPT
1332
+ model exposed by the proxy, or checks your explicit `--model`. Local failure
1333
+ stops the launch without switching to paid Router inference. `--dry-run` checks
1334
+ the proxy and prints a plan with credentials masked; it mints no Ditto key.
1335
+ Resume remembers the proxy URL and reads its credential again, so `--resume`
1336
+ keeps local inference. Use `--no-cliproxy` to return to the normal inference mode.
1337
+
1338
+ This mode still uses a Ditto login and endpoint for selected Claude MCP tools
1339
+ and remote control. The local API access key stays local; Ditto MCP uses a
1340
+ separate temporary endpoint key, which is revoked on exit. `--no-mcp` omits the
1341
+ selected Claude MCP tools; `--no-remote-control` omits cloud session registration.
1342
+ Those flags do not remove the current launcher's Ditto login/endpoint dependency.
1343
+ Local inference bypasses Router compression, routing and inference recording;
1344
+ cloud-assisted relay is a separate mode. Cloud MCP calls and remote-control
1345
+ activity still reach Ditto when enabled. `--mcp-only` keeps using the native
1346
+ Claude account instead; it cannot be combined with `--cliproxy`. `--budget`
1347
+ cannot cap subscription inference and is rejected in direct CLIProxy mode.
1348
+
1284
1349
  ### Native Claude inference and remote delivery
1285
1350
 
1286
1351
  `heyditto claude` keeps using Ditto Router for inference. `--mcp` changes
@@ -53,7 +53,7 @@ export function planClaude(input) {
53
53
  if (input.model)
54
54
  envSet.ANTHROPIC_MODEL = input.model;
55
55
  if (input.mcp)
56
- envSet[MCP_KEY_ENV] = input.apiKey;
56
+ envSet[MCP_KEY_ENV] = input.mcpApiKey ?? input.apiKey;
57
57
  if (input.mcpOnly) {
58
58
  // Endpoint keys are only MCP credentials in this mode. Never forward one
59
59
  // to Anthropic or inherit a parent launch's inference gateway settings.
@@ -64,11 +64,20 @@ export function planClaude(input) {
64
64
  installHint: "install Claude Code and sign in with `claude auth login`",
65
65
  };
66
66
  }
67
- // Claude Code's server-side auto-mode classifier needs the dangerous-tool-use
68
- // beta and `safeguards` field, which the router does not forward; a rejection
69
- // denies every auto-mode tool use. Decide locally unless the caller chose.
67
+ // Intermediaries do not forward Claude Code's auto-mode classifier fields.
68
+ // Decide locally for Router and CLIProxy inference unless the caller chose.
70
69
  if (input.env.CLAUDE_CODE_AUTO_MODE_SERVER === undefined)
71
70
  envSet.CLAUDE_CODE_AUTO_MODE_SERVER = "0";
71
+ if (input.localInference) {
72
+ // Cloud MCP auth never becomes the local inference credential, and a
73
+ // parent Router launch's custom headers/other provider modes stay out.
74
+ delete envSet.ANTHROPIC_CUSTOM_HEADERS;
75
+ return {
76
+ command: "claude", args: finalArgs, envSet,
77
+ envUnset: ["ANTHROPIC_API_KEY", "ANTHROPIC_CUSTOM_HEADERS", "CLAUDE_CODE_OAUTH_TOKEN", "CLAUDE_CODE_USE_BEDROCK", "CLAUDE_CODE_USE_VERTEX", "CLAUDE_CODE_USE_FOUNDRY", ...(!input.model ? ["ANTHROPIC_MODEL"] : []), ...(!input.mcp ? [MCP_KEY_ENV] : [])],
78
+ installHint: "install Claude Code: npm i -g @anthropic-ai/claude-code",
79
+ };
80
+ }
72
81
  // Claude Code only labels requests (x-claude-code-request-class: main,
73
82
  // subagent, auxiliary) for gateways when asked. The router uses the label to
74
83
  // skip memory recall on housekeeping calls such as the auto-mode classifier.
@@ -1 +1 @@
1
- {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC7H,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAoC,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEjI;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,KAAgB;IACzC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,KAAK,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;SACvD,IAAI,KAAK,CAAC,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,aAAa,CAAC,CAAC;SAChE,IAAI,KAAK,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAE5D,uEAAuE;IACvE,2EAA2E;IAC3E,iBAAiB;IACjB,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,WAAW,IAAI,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC;IACzJ,IAAI,KAAK,CAAC,QAAQ;QAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;SACrD,IAAI,KAAK,CAAC,UAAU;QAAE,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;SAC9C,IAAI,CAAC,QAAQ;QAAE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAE/D,IAAI,KAAK,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IACnD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9D,0EAA0E;IAC1E,oDAAoD;IACpD,IAAI,KAAK,CAAC,GAAG;QAAE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEjH,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,gBAAgB,KAAK,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACrG,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,mBAAmB,KAAK,gBAAgB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;IAC5G,MAAM,OAAO,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE,GAAG,cAAc,KAAK,KAAK,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;SACxI,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;SACtC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,MAAM,GAA2B;QACrC,kBAAkB,EAAE,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;QAC5C,oBAAoB,EAAE,KAAK,CAAC,MAAM;QAClC,wBAAwB,EAAE,OAAO;KAClC,CAAC;IACF,IAAI,KAAK,CAAC,KAAK;QAAE,MAAM,CAAC,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC;IACtD,IAAI,KAAK,CAAC,GAAG;QAAE,MAAM,CAAC,WAAW,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IAClD,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,yEAAyE;QACzE,wEAAwE;QACxE,OAAO;YACL,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS;YAClC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE;YACxD,QAAQ,EAAE,CAAC,oBAAoB,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,mBAAmB,CAAC;YAC5H,WAAW,EAAE,0DAA0D;SACxE,CAAC;IACJ,CAAC;IACD,8EAA8E;IAC9E,8EAA8E;IAC9E,2EAA2E;IAC3E,IAAI,KAAK,CAAC,GAAG,CAAC,4BAA4B,KAAK,SAAS;QAAE,MAAM,CAAC,4BAA4B,GAAG,GAAG,CAAC;IACpG,uEAAuE;IACvE,6EAA6E;IAC7E,6EAA6E;IAC7E,IAAI,KAAK,CAAC,GAAG,CAAC,gCAAgC,KAAK,SAAS;QAAE,MAAM,CAAC,gCAAgC,GAAG,GAAG,CAAC;IAE5G,OAAO;QACL,OAAO,EAAE,QAAQ;QACjB,IAAI,EAAE,SAAS;QACf,MAAM;QACN,QAAQ,EAAE,CAAC,mBAAmB,CAAC;QAC/B,WAAW,EAAE,0FAA0F;KACxG,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAAc;IAC5C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,MAAiC,CAAC;IACtC,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBAChD,MAAM,GAAG,IAAI,CAAC;gBACd,CAAC,IAAI,CAAC,CAAC;YACT,CAAC;;gBAAM,MAAM,GAAG,IAAI,CAAC;QACvB,CAAC;aAAM,IAAI,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;aAC9E,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,YAAY;YAAE,IAAI,GAAG,IAAI,CAAC;;YAClD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACzG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,sBAAsB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AAE1I,gEAAgE;AAChE,SAAS,gBAAgB,CAAC,GAAuB;IAC/C,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC;SACrB,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACpG,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC1D,CAAC"}
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface CliProxy {
2
+ /** Canonical loopback OpenAI/Responses root. */
3
+ baseUrl: string;
4
+ /** Local credential, never a Ditto endpoint key. */
5
+ apiKey: string;
6
+ models: string[];
7
+ }
8
+ /** Deliberately no public/LAN destinations, embedded credentials or redirects. */
9
+ export declare function cliProxyBaseUrl(value: string): string;
10
+ export declare function cliProxyConfigPaths(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, home?: string): string[];
11
+ /** Reads only the API access key; OAuth/provider/management credentials stay untouched. */
12
+ export declare function resolveCliProxy(requested: string | true, env?: NodeJS.ProcessEnv): Promise<CliProxy>;
13
+ export declare function cliProxyModel(proxy: Pick<CliProxy, "models">, harness: "claude" | "codex", requested?: string): string;
@@ -0,0 +1,115 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { parse } from "smol-toml";
5
+ /** Deliberately no public/LAN destinations, embedded credentials or redirects. */
6
+ export function cliProxyBaseUrl(value) {
7
+ let url;
8
+ try {
9
+ url = new URL(value);
10
+ }
11
+ catch {
12
+ throw new Error("CLIProxy URL must be an http(s) loopback URL, such as http://127.0.0.1:8317/v1");
13
+ }
14
+ if (!["http:", "https:"].includes(url.protocol) || !["127.0.0.1", "[::1]", "localhost"].includes(url.hostname) ||
15
+ url.username || url.password || url.search || url.hash || !["", "/", "/v1", "/v1/"].includes(url.pathname)) {
16
+ throw new Error("CLIProxy requires a loopback URL with no credentials, query or fragment, ending in /v1 or /");
17
+ }
18
+ // Pin localhost rather than resolving it through potentially modified DNS.
19
+ if (url.hostname === "localhost")
20
+ url.hostname = "127.0.0.1";
21
+ url.pathname = "/v1";
22
+ return url.toString().replace(/\/$/, "");
23
+ }
24
+ export function cliProxyConfigPaths(env = process.env, platform = process.platform, home = os.homedir()) {
25
+ if (env.CLIPROXY_CONFIG)
26
+ return [env.CLIPROXY_CONFIG];
27
+ if (platform === "darwin")
28
+ return [path.join(home, "Library", "Application Support", "com.cpa.gui", "config.toml")];
29
+ if (platform === "win32")
30
+ return [path.join(env.APPDATA || path.join(home, "AppData", "Roaming"), "com.cpa.gui", "config.toml")];
31
+ return [
32
+ path.join(env.XDG_DATA_HOME || path.join(home, ".local", "share"), "com.cpa.gui", "config.toml"),
33
+ path.join(env.XDG_CONFIG_HOME || path.join(home, ".config"), "com.cpa.gui", "config.toml"),
34
+ ];
35
+ }
36
+ /** Reads only the API access key; OAuth/provider/management credentials stay untouched. */
37
+ export async function resolveCliProxy(requested, env = process.env) {
38
+ let config;
39
+ const explicitUrl = typeof requested === "string" ? requested : env.CLIPROXY_BASE_URL;
40
+ for (const file of explicitUrl && env.CLIPROXY_API_KEY ? [] : cliProxyConfigPaths(env)) {
41
+ try {
42
+ config = parse(await readFile(file, "utf8"));
43
+ break;
44
+ }
45
+ catch (err) {
46
+ if (err.code === "ENOENT" && !env.CLIPROXY_CONFIG)
47
+ continue;
48
+ throw new Error("Could not read CLIProxy configuration; check CLIPROXY_CONFIG or set CLIPROXY_BASE_URL and CLIPROXY_API_KEY");
49
+ }
50
+ }
51
+ let detectedUrl;
52
+ if (config) {
53
+ const port = config.port;
54
+ const host = config.host === "::1" ? "[::1]" : (config.host || "127.0.0.1");
55
+ if (typeof host === "string" && typeof port === "number" && Number.isInteger(port) && port > 0 && port <= 65535) {
56
+ try {
57
+ detectedUrl = cliProxyBaseUrl(`http://${host}:${port}`);
58
+ }
59
+ catch { /* A LAN-configured application is not eligible for auto-discovery. */ }
60
+ }
61
+ }
62
+ const baseUrl = cliProxyBaseUrl(typeof requested === "string" ? requested : env.CLIPROXY_BASE_URL || detectedUrl || "http://127.0.0.1:8317/v1");
63
+ const keys = config?.["api-keys"];
64
+ // EasyCLIProxyAPI also writes annotated entries as [[api-keys]] tables.
65
+ // Only their access-key field is relevant; remarks are display metadata.
66
+ const detectedKey = detectedUrl === baseUrl && Array.isArray(keys)
67
+ ? keys.map((entry) => typeof entry === "string" ? entry : entry && typeof entry === "object" && "key" in entry ? entry.key : undefined)
68
+ .find((key) => typeof key === "string" && Boolean(key.trim()))
69
+ : undefined;
70
+ const apiKey = env.CLIPROXY_API_KEY || detectedKey || "";
71
+ if (!apiKey || /[\r\n]/.test(apiKey)) {
72
+ throw new Error("No CLIProxy API access key found; set CLIPROXY_API_KEY or configure an API key in EasyCLIProxyAPI");
73
+ }
74
+ let response;
75
+ try {
76
+ response = await fetch(`${baseUrl}/models`, {
77
+ headers: { Authorization: `Bearer ${apiKey}` },
78
+ redirect: "error",
79
+ signal: AbortSignal.timeout(5_000),
80
+ });
81
+ }
82
+ catch {
83
+ throw new Error("CLIProxy is not reachable at the configured loopback address; start the proxy and check its port. Ditto Router inference was not used");
84
+ }
85
+ if (!response.ok) {
86
+ await response.body?.cancel();
87
+ throw new Error(`CLIProxy model check failed (HTTP ${response.status}); check its API access key and provider setup. Ditto Router inference was not used`);
88
+ }
89
+ let data;
90
+ try {
91
+ data = await response.json();
92
+ }
93
+ catch {
94
+ throw new Error("CLIProxy returned an invalid model catalog; Ditto Router inference was not used");
95
+ }
96
+ if (data?.object !== "list" || !Array.isArray(data.data))
97
+ throw new Error("CLIProxy returned an invalid model catalog; Ditto Router inference was not used");
98
+ const models = data.data.flatMap((model) => typeof model?.id === "string" && model.id.trim() ? [model.id] : []);
99
+ if (!models.length)
100
+ throw new Error("CLIProxy has no models; connect a provider in the proxy before launching");
101
+ return { baseUrl, apiKey, models };
102
+ }
103
+ export function cliProxyModel(proxy, harness, requested) {
104
+ if (requested) {
105
+ if (!proxy.models.includes(requested))
106
+ throw new Error("The requested model is not in CLIProxy's model catalog; select a model exposed by your local proxy");
107
+ return requested;
108
+ }
109
+ const pattern = harness === "claude" ? /^claude-/ : /^(gpt-|o[1-9])/;
110
+ const model = proxy.models.find((id) => pattern.test(id));
111
+ if (!model)
112
+ throw new Error(`CLIProxy has no default ${harness} model; pass --model with an ID exposed by your local proxy`);
113
+ return model;
114
+ }
115
+ //# sourceMappingURL=cliproxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cliproxy.js","sourceRoot":"","sources":["../../src/agents/cliproxy.ts"],"names":[],"mappings":"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"}
@@ -33,7 +33,7 @@ export function planCodex(input) {
33
33
  else if (resuming) {
34
34
  args.push("resume");
35
35
  }
36
- args.push("-c", `model_provider=${tomlString("ditto")}`, "-c", `model_providers.ditto.name=${tomlString("Ditto")}`, "-c", `model_providers.ditto.base_url=${tomlString(input.baseUrl)}`, "-c", `model_providers.ditto.env_key=${tomlString(CODEX_KEY_ENV)}`, "-c", `model_providers.ditto.wire_api=${tomlString("responses")}`, "-c", `model_providers.ditto.http_headers=${tomlInlineTable([[SESSION_HEADER, input.sessionId], ...harnessHeaders("codex", input.harnessVersion)])}`);
36
+ args.push("-c", `model_provider=${tomlString("ditto")}`, "-c", `model_providers.ditto.name=${tomlString(input.localInference ? "CLIProxy" : "Ditto")}`, "-c", `model_providers.ditto.base_url=${tomlString(input.baseUrl)}`, "-c", `model_providers.ditto.env_key=${tomlString(CODEX_KEY_ENV)}`, "-c", `model_providers.ditto.wire_api=${tomlString("responses")}`, "-c", `model_providers.ditto.http_headers=${tomlInlineTable(input.localInference ? [] : [[SESSION_HEADER, input.sessionId], ...harnessHeaders("codex", input.harnessVersion)])}`);
37
37
  if (input.model)
38
38
  args.push("-m", input.model);
39
39
  if (input.yolo)
@@ -1 +1 @@
1
- {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAoC,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7F,uEAAuE;AACvE,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,eAAe,CAAC,OAAgC;IAC9D,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACvF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,KAAgB;IACxC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,2EAA2E;IAC3E,0EAA0E;IAC1E,sEAAsE;IACtE,2EAA2E;IAC3E,qBAAqB;IACrB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC;IAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClB,IAAI,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;SAAM,IAAI,QAAQ,EAAE,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC;IAED,IAAI,CAAC,IAAI,CACP,IAAI,EACJ,kBAAkB,UAAU,CAAC,OAAO,CAAC,EAAE,EACvC,IAAI,EACJ,8BAA8B,UAAU,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EACvF,IAAI,EACJ,kCAAkC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAC7D,IAAI,EACJ,iCAAiC,UAAU,CAAC,aAAa,CAAC,EAAE,EAC5D,IAAI,EACJ,kCAAkC,UAAU,CAAC,WAAW,CAAC,EAAE,EAC3D,IAAI,EACJ,sCAAsC,eAAe,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAC3K,CAAC;IACF,IAAI,KAAK,CAAC,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAE9C,IAAI,KAAK,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;SACnE,IAAI,KAAK,CAAC,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC;IAE9E,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;QACzD,8EAA8E;QAC9E,gCAAgC;QAChC,IAAI,KAAK,CAAC,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;aACzC,IAAI,KAAK,CAAC,UAAU;YAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;SAAM,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,IAAI;QACJ,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE;QACzC,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,+DAA+D;KAC7E,CAAC;AACJ,CAAC"}
@@ -40,6 +40,8 @@ export interface LaunchOptions {
40
40
  */
41
41
  mcp?: boolean;
42
42
  mcpOnly?: boolean;
43
+ /** Direct inference through a loopback CLIProxy; URL is persisted, keys are not. */
44
+ cliproxy?: string | boolean;
43
45
  remoteTransport?: "cli" | "channels";
44
46
  }
45
47
  /** Explicit flags (or resumed choices) win over repo/cloud launch defaults. */
@@ -20,6 +20,7 @@ import { resolveClaudeResume, transcriptCwd } from "./claude-resume.js";
20
20
  import { findTranscriptById, isUuid, listTranscriptsIn } from "./claude-transcripts.js";
21
21
  import { watchClaudeSessionTitle } from "./claude-title-sync.js";
22
22
  import { planCodex } from "./codex.js";
23
+ import { cliProxyModel, resolveCliProxy } from "./cliproxy.js";
23
24
  import { readHarnessVersion } from "./harness-headers.js";
24
25
  import { ensureGrokHome, planGrok } from "./grok.js";
25
26
  import { latestSession, readSession, sessionForHarnessId, writeSession } from "./sessions.js";
@@ -33,7 +34,7 @@ import { loadPty, spawnInPty } from "../remote/pty.js";
33
34
  import { checkpointPush } from "../teleport/commands.js";
34
35
  /** Explicit flags (or resumed choices) win over repo/cloud launch defaults. */
35
36
  export function resolveClaudeLaunchDefaults(options, defaults = {}) {
36
- const mcpOnly = options.mcpOnly ?? (defaults.claude_inference === "native");
37
+ const mcpOnly = options.cliproxy ? false : options.mcpOnly ?? (defaults.claude_inference === "native");
37
38
  const channelsUsable = !options.headless && remoteMode(options) === "tui";
38
39
  const remoteTransport = options.remoteTransport ?? (mcpOnly && channelsUsable && defaults.claude_remote_transport === "channels" ? "channels" : "cli");
39
40
  return { ...options, mcpOnly, remoteTransport };
@@ -297,6 +298,12 @@ function waitForExit(child, command, cwd) {
297
298
  }
298
299
  /** Launches a coding harness against a Ditto inference endpoint with a temporary key. */
299
300
  export async function launchHarness(harness, rawArgs, options) {
301
+ // An explicit empty value (often an unset shell variable) still requests
302
+ // CLIProxy. Reject it before any launch/setup work rather than treating it
303
+ // as a false boolean and silently using Router inference.
304
+ if (typeof options.cliproxy === "string" && !options.cliproxy.trim()) {
305
+ throw new Error("--cliproxy requires a loopback URL; pass --cliproxy alone to auto-detect");
306
+ }
300
307
  let passthrough = stripSeparator(rawArgs);
301
308
  if (harness === "claude") {
302
309
  const lifted = liftResumeFlags(passthrough);
@@ -377,7 +384,26 @@ export async function launchHarness(harness, rawArgs, options) {
377
384
  if (!record.harnessSessionId)
378
385
  resumeLast = true;
379
386
  }
380
- options = { ...options, mcpOnly: options.mcpOnly ?? record?.mcpOnly, remoteTransport: options.remoteTransport ?? (remoteMode(options) === "tui" ? record?.remoteTransport : undefined) };
387
+ options = {
388
+ ...options,
389
+ cliproxy: options.cliproxy ?? (options.mcpOnly !== undefined ? false : record?.cliproxy),
390
+ mcpOnly: options.cliproxy ? options.mcpOnly : options.mcpOnly ?? record?.mcpOnly,
391
+ remoteTransport: options.remoteTransport ?? (remoteMode(options) === "tui" ? record?.remoteTransport : undefined),
392
+ };
393
+ if (typeof options.cliproxy === "string" && !options.cliproxy.trim()) {
394
+ throw new Error("The saved CLIProxy URL is empty; pass --cliproxy to auto-detect or --no-cliproxy to change inference mode");
395
+ }
396
+ if (options.cliproxy && options.mcpOnly)
397
+ throw new Error("choose either --cliproxy or --mcp-only");
398
+ if (options.cliproxy && harness === "grok")
399
+ throw new Error("--cliproxy is supported for claude and codex");
400
+ if (options.cliproxy && options.budget !== undefined)
401
+ throw new Error("--budget cannot cap CLIProxy subscription inference; configure limits in your local proxy instead");
402
+ // Check the local proxy before sign-in or minting any cloud key. A failure
403
+ // must never silently switch inference back to the paid Router.
404
+ const proxy = options.cliproxy ? await resolveCliProxy(options.cliproxy) : undefined;
405
+ if (proxy)
406
+ options = { ...options, cliproxy: proxy.baseUrl, mcpOnly: false };
381
407
  const requestedEndpoint = options.endpoint ?? record?.endpointSlug;
382
408
  let selected = options.dryRun ? undefined : await ensureLoggedIn(harness, requestedEndpoint);
383
409
  let catalog;
@@ -462,7 +488,7 @@ export async function launchHarness(harness, rawArgs, options) {
462
488
  endpoint = { ...endpoint, providerOptions: { ...endpoint.providerOptions, ...repoLaunchDefaults } };
463
489
  options = resolveClaudeLaunchDefaults(options, endpoint.providerOptions);
464
490
  }
465
- if (!options.mcpOnly)
491
+ if (!options.mcpOnly && !proxy)
466
492
  await assertEndpointActive(endpoint);
467
493
  if (options.mcpOnly && (harness !== "claude" || options.mcp === false))
468
494
  throw new Error("--mcp-only requires Claude Code with MCP tools enabled");
@@ -484,7 +510,7 @@ export async function launchHarness(harness, rawArgs, options) {
484
510
  // claude-* ids). Earlier releases pinned Codex sessions to the endpoint
485
511
  // slug; that pin is dropped on resume so they pass through too.
486
512
  const recordedModel = record?.model === endpoint.slug ? undefined : record?.model;
487
- const model = options.model ?? recordedModel;
513
+ const model = proxy ? cliProxyModel(proxy, harness, options.model ?? recordedModel) : options.model ?? recordedModel;
488
514
  const now = new Date().toISOString();
489
515
  if (!(await isDirectory(cwd)))
490
516
  throw new Error(`launch directory ${cwd} does not exist`);
@@ -538,7 +564,7 @@ export async function launchHarness(harness, rawArgs, options) {
538
564
  log(c("yellow", line));
539
565
  }
540
566
  catch (err) {
541
- if (options.mcpOnly) {
567
+ if (options.mcpOnly || proxy) {
542
568
  if (!options.keepKey) {
543
569
  try {
544
570
  await revokeKey(endpoint.id, key.id);
@@ -547,7 +573,7 @@ export async function launchHarness(harness, rawArgs, options) {
547
573
  log(`could not revoke session key …${key.keyHint}; it expires in ${expiresIn}`);
548
574
  }
549
575
  }
550
- throw new Error(`MCP-only setup failed; no inference Router fallback: ${err.message}`);
576
+ throw new Error(`${proxy ? "CLIProxy MCP" : "MCP-only"} setup failed; no inference Router fallback: ${err.message}`);
551
577
  }
552
578
  const note = c(chosen ? "yellow" : "dim", "native MCP unavailable");
553
579
  log(`${note} (${err.message}); the router keeps this endpoint's tools`);
@@ -556,8 +582,10 @@ export async function launchHarness(harness, rawArgs, options) {
556
582
  // Read once per launch and reused by every turn.
557
583
  const harnessVersion = await harnessVersionProbe;
558
584
  const plan = planFor(harness, {
559
- baseUrl: catalog.baseUrl,
560
- apiKey: key?.key ?? DRY_RUN_KEY,
585
+ baseUrl: proxy?.baseUrl ?? catalog.baseUrl,
586
+ apiKey: proxy?.apiKey ?? key?.key ?? DRY_RUN_KEY,
587
+ mcpApiKey: key?.key ?? DRY_RUN_KEY,
588
+ localInference: Boolean(proxy),
561
589
  sessionId,
562
590
  harnessVersion,
563
591
  model,
@@ -578,7 +606,7 @@ export async function launchHarness(harness, rawArgs, options) {
578
606
  const field = (name, value) => `${c("dim", `${name}=`)}${value}`;
579
607
  const banner = [
580
608
  field("endpoint", c(["bold", "cyan"], endpoint.slug)),
581
- field("inference", options.mcpOnly ? "native Claude Code account (MCP only)" : "Ditto Router"),
609
+ field("inference", proxy ? `CLIProxy (${proxy.baseUrl})` : options.mcpOnly ? "native Claude Code account (MCP only)" : "Ditto Router"),
582
610
  field("model", model ?? c("dim", options.mcpOnly ? "(native Claude Code default)" : "(harness default → endpoint routes)")),
583
611
  field("key", key ? `…${key.keyHint || maskKey(key.key ?? "")}` : "(dry run)"),
584
612
  field("expires", `${expiresIn}${options.keepKey ? "" : c("dim", " (revoked on exit)")}`),
@@ -589,9 +617,12 @@ export async function launchHarness(harness, rawArgs, options) {
589
617
  .filter(Boolean)
590
618
  .join(" ");
591
619
  log(banner);
592
- log(`traces: ${c(["underline", "cyan"], endpointURL(endpoint.id))}`);
620
+ if (proxy)
621
+ log("inference stays on CLIProxy; Ditto provides MCP tools/remote control, without Router inference optimizations or inference traces");
622
+ else
623
+ log(`traces: ${c(["underline", "cyan"], endpointURL(endpoint.id))}`);
593
624
  if (options.dryRun) {
594
- const env = Object.fromEntries(Object.entries(plan.envSet).map(([k, v]) => [k, v === DRY_RUN_KEY ? "<key>" : v]));
625
+ const env = Object.fromEntries(Object.entries(plan.envSet).map(([k, v]) => [k, v === DRY_RUN_KEY || v === proxy?.apiKey ? "<key>" : v]));
595
626
  process.stdout.write(`${JSON.stringify({
596
627
  harness,
597
628
  command: plan.command,
@@ -603,9 +634,9 @@ export async function launchHarness(harness, rawArgs, options) {
603
634
  sessionId,
604
635
  // A dry run reads no catalog, so the plan above is the router's; say
605
636
  // which surface a real launch would ask for, separately.
606
- toolSurface: mcp ? "mcp" : "router",
607
- toolSurfaceRequested: wantMcp ? "mcp" : "router",
608
- inference: options.mcpOnly ? "native" : "router",
637
+ toolSurface: mcp ? "mcp" : proxy ? "none" : "router",
638
+ toolSurfaceRequested: wantMcp ? "mcp" : proxy ? "none" : "router",
639
+ inference: proxy ? "cliproxy" : options.mcpOnly ? "native" : "router",
609
640
  remoteTransport: options.remoteTransport ?? "cli",
610
641
  key: { name: keyName, expiresIn, spendLimitTokens: budget ?? null },
611
642
  }, null, 2)}\n`);
@@ -629,6 +660,7 @@ export async function launchHarness(harness, rawArgs, options) {
629
660
  lastLaunchedAt: now,
630
661
  launches: (record?.launches ?? 0) + 1,
631
662
  mcpOnly: options.mcpOnly,
663
+ cliproxy: proxy?.baseUrl,
632
664
  remoteTransport: options.remoteTransport,
633
665
  ...(explicitLaunchTitle ? { explicitLaunchTitle } : {}),
634
666
  };
@@ -636,7 +668,7 @@ export async function launchHarness(harness, rawArgs, options) {
636
668
  const remote = remoteMode(options);
637
669
  const launchRegistration = {
638
670
  sessionId, endpointId: endpoint.id, harness: harness === "claude" ? "claude-code" : harness,
639
- inference: options.mcpOnly ? "native" : "router", transport: options.remoteTransport ?? "cli",
671
+ inference: proxy || options.mcpOnly ? "native" : "router", transport: options.remoteTransport ?? "cli",
640
672
  title: explicitLaunchTitle || `${path.basename(cwd)} · ${sessionId.slice(0, 8)}`,
641
673
  ...(harness === "claude" && harnessSessionId && isUuid(harnessSessionId) ? { nativeSessionId: harnessSessionId } : {}),
642
674
  };
@@ -671,8 +703,10 @@ export async function launchHarness(harness, rawArgs, options) {
671
703
  cwd,
672
704
  sessionId,
673
705
  harnessSessionId,
674
- baseUrl: catalog.baseUrl,
675
- apiKey: key.key ?? "",
706
+ baseUrl: proxy?.baseUrl ?? catalog.baseUrl,
707
+ apiKey: proxy?.apiKey ?? key.key ?? "",
708
+ mcpApiKey: key.key ?? "",
709
+ localInference: Boolean(proxy),
676
710
  harnessVersion,
677
711
  model,
678
712
  options,
@@ -896,6 +930,8 @@ async function runHeadless(harness, ctx) {
896
930
  base: {
897
931
  baseUrl: ctx.baseUrl,
898
932
  apiKey: ctx.apiKey,
933
+ mcpApiKey: ctx.mcpApiKey,
934
+ localInference: ctx.localInference,
899
935
  sessionId: ctx.sessionId,
900
936
  harnessVersion: ctx.harnessVersion,
901
937
  model: ctx.model,