@heyditto/cli 2.0.0 → 2.1.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 +121 -0
- package/dist/agents/claude.d.ts +11 -0
- package/dist/agents/claude.js +48 -0
- package/dist/agents/claude.js.map +1 -0
- package/dist/agents/codex.d.ts +13 -0
- package/dist/agents/codex.js +48 -0
- package/dist/agents/codex.js.map +1 -0
- package/dist/agents/launch.d.ts +20 -0
- package/dist/agents/launch.js +274 -0
- package/dist/agents/launch.js.map +1 -0
- package/dist/agents/sessions.d.ts +28 -0
- package/dist/agents/sessions.js +63 -0
- package/dist/agents/sessions.js.map +1 -0
- package/dist/agents/types.d.ts +50 -0
- package/dist/agents/types.js +44 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/agents/worktree.d.ts +19 -0
- package/dist/agents/worktree.js +72 -0
- package/dist/agents/worktree.js.map +1 -0
- package/dist/api.d.ts +110 -0
- package/dist/api.js +119 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.js +80 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +30 -0
- package/dist/commands.js +293 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/device-login.d.ts +12 -0
- package/dist/device-login.js +34 -0
- package/dist/device-login.js.map +1 -0
- package/dist/mcp-session.d.ts +40 -0
- package/dist/mcp-session.js +133 -0
- package/dist/mcp-session.js.map +1 -0
- package/dist/store.d.ts +10 -0
- package/dist/store.js +14 -0
- package/dist/store.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -42,6 +42,17 @@ You can also reorder your `PATH` so the npm global bin comes before `/usr/bin`,
|
|
|
42
42
|
|
|
43
43
|
## Auth
|
|
44
44
|
|
|
45
|
+
Humans sign in through the browser:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
heyditto login
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This prints a short code, opens `https://heyditto.ai/device` (pre-filled with the
|
|
52
|
+
code), and saves the resulting key to your CLI config directory once you approve
|
|
53
|
+
it. Pass a key as an argument (`heyditto login ditto_mcp_…`), pipe it with
|
|
54
|
+
`--stdin`, or use `--paste` for the older copy-a-key page.
|
|
55
|
+
|
|
45
56
|
Agents can self-provision without a browser, email, or OTP:
|
|
46
57
|
|
|
47
58
|
```bash
|
|
@@ -111,6 +122,17 @@ heyditto graphs subscribers
|
|
|
111
122
|
heyditto graphs sharing (--enable|--disable) [--title <t>] [--description <d>]
|
|
112
123
|
heyditto init [--name <name>] [--subscribe <@graph>]... [<@graph>...] [--json]
|
|
113
124
|
heyditto login [<key>] [--paste] [--stdin]
|
|
125
|
+
heyditto endpoints [--set-default <slug>] [--clear-default]
|
|
126
|
+
heyditto claude [options] [-- <claude args>]
|
|
127
|
+
heyditto codex [options] [-- <codex args>]
|
|
128
|
+
heyditto sessions [--json] [--all]
|
|
129
|
+
heyditto sessions rm <id>
|
|
130
|
+
heyditto session new [<name>...] [--id <id>]
|
|
131
|
+
heyditto session list [--all]
|
|
132
|
+
heyditto session use <id>
|
|
133
|
+
heyditto session current
|
|
134
|
+
heyditto session end
|
|
135
|
+
heyditto agents [--output <format>]
|
|
114
136
|
heyditto logout
|
|
115
137
|
heyditto status [--output <format>]
|
|
116
138
|
heyditto config
|
|
@@ -289,10 +311,109 @@ graph. Choose it up front — renaming after init isn't yet supported. See
|
|
|
289
311
|
|
|
290
312
|
Print a Claude Desktop / Cursor / generic-MCP-client config snippet for the Ditto memory server.
|
|
291
313
|
|
|
314
|
+
## Coding agents
|
|
315
|
+
|
|
316
|
+
`heyditto claude` and `heyditto codex` launch Claude Code or Codex through one of
|
|
317
|
+
your Ditto **inference endpoints** (Settings → Developer → Inference endpoints
|
|
318
|
+
in the Ditto app). Each launch:
|
|
319
|
+
|
|
320
|
+
1. lists your endpoints and lets you pick one (or uses `--endpoint` / the saved default),
|
|
321
|
+
2. mints a temporary endpoint key (optionally capped with `--budget <tokens>`),
|
|
322
|
+
3. starts the agent with that key and a fresh `X-Ditto-Session-Id`, so the
|
|
323
|
+
session becomes its own thread with full traces under the endpoint,
|
|
324
|
+
4. **revokes the key when the agent exits** (Ctrl+C included). The thread and
|
|
325
|
+
traces are kept; `--keep-key` opts out, `--expires` sets a server-side
|
|
326
|
+
safety expiry (default `1d`).
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
npx -y @heyditto/cli login
|
|
330
|
+
npx -y @heyditto/cli claude --endpoint my-endpoint
|
|
331
|
+
|
|
332
|
+
heyditto endpoints --set-default my-endpoint # skip the picker next time
|
|
333
|
+
heyditto claude # interactive Claude Code
|
|
334
|
+
heyditto codex --yellow # Codex, auto-accept edits
|
|
335
|
+
heyditto claude --yolo --worktree fix-login # bypass prompts in <repo>/.worktrees/fix-login
|
|
336
|
+
heyditto codex -p "summarize this repo" --json # headless (codex exec)
|
|
337
|
+
heyditto claude -p "list TODOs" --output-format json --max-turns 3
|
|
338
|
+
heyditto claude --resume # reopen the last session in the same thread
|
|
339
|
+
heyditto claude -- --verbose # anything after -- goes to the agent
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Options shared by both commands:
|
|
343
|
+
|
|
344
|
+
| Flag | Meaning |
|
|
345
|
+
| --- | --- |
|
|
346
|
+
| `-e, --endpoint <slug>` | endpoint to route through (default: saved default, else a picker) |
|
|
347
|
+
| `--budget <tokens>` | spend cap for this session's key, in Ditto tokens |
|
|
348
|
+
| `--expires <1h…never>` | server-side key expiry; the key is still revoked on exit unless `--keep-key` |
|
|
349
|
+
| `--session <id>` | reuse a Ditto session id so traces land in an existing thread |
|
|
350
|
+
| `--resume [id]` / `-c, --continue` | resume a local session (`heyditto sessions`) / the agent's most recent conversation |
|
|
351
|
+
| `--yolo` / `--yellow` / `--plan` | bypass permissions / auto-accept edits / plan mode (Claude only) |
|
|
352
|
+
| `-p, --prompt <text>` | headless run: `claude -p` or `codex exec` |
|
|
353
|
+
| `-m, --model <id>` | model to request (Codex defaults to the endpoint slug; Claude's ids follow the endpoint's routes) |
|
|
354
|
+
| `-w, --worktree [name]` | run inside `<repo>/.worktrees/<name>` on a branch of that name; `.worktrees/` is added to `.gitignore` |
|
|
355
|
+
| `--name <label>` | key name shown in the app (default `cli:<agent>:<hostname>`) |
|
|
356
|
+
| `--dry-run` | print the command, args and env with the key masked; mints nothing |
|
|
357
|
+
|
|
358
|
+
Unknown flags and everything after `--` are forwarded to the agent, so Claude's
|
|
359
|
+
and Codex's own options keep working.
|
|
360
|
+
|
|
361
|
+
How the wiring works:
|
|
362
|
+
|
|
363
|
+
- **Claude Code** gets `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN` (a bearer
|
|
364
|
+
token, which skips Claude's "use this API key?" prompt) and
|
|
365
|
+
`ANTHROPIC_CUSTOM_HEADERS` with the session id. Any inherited
|
|
366
|
+
`ANTHROPIC_API_KEY` is removed from the child environment.
|
|
367
|
+
- **Codex** speaks the Responses API only, so the endpoint is injected as a
|
|
368
|
+
`ditto` model provider via `-c` overrides (nothing is written to
|
|
369
|
+
`~/.codex/config.toml`) with the key in `DITTO_INFERENCE_API_KEY`.
|
|
370
|
+
|
|
371
|
+
### `endpoints`
|
|
372
|
+
|
|
373
|
+
List your inference endpoints with model and spend. `--set-default <slug>` picks
|
|
374
|
+
the one used when `--endpoint` is omitted; `--output json` includes the gateway
|
|
375
|
+
base URL.
|
|
376
|
+
|
|
377
|
+
### `sessions`
|
|
378
|
+
|
|
379
|
+
List the coding-agent sessions launched from this machine (stored under
|
|
380
|
+
`~/.config/heyditto/cli/sessions/`). `heyditto sessions rm <id>` forgets a local
|
|
381
|
+
record; the Ditto thread and traces are unaffected.
|
|
382
|
+
|
|
383
|
+
### `session`
|
|
384
|
+
|
|
385
|
+
Explicit MCP sessions. Without one, the server groups your saves and searches
|
|
386
|
+
into a time-based *implicit* session (activity within a cooldown, auto-named
|
|
387
|
+
when it goes quiet). `heyditto session new [name]` pins an explicit session:
|
|
388
|
+
every MCP request from then on carries `X-Ditto-Session-Id`, and the name goes
|
|
389
|
+
out once as `X-Ditto-Session-Name` so the thread gets that title. Saves and
|
|
390
|
+
searches land in one thread inside the agent your key is attached to.
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
heyditto session new "refactor auth module" # prints the id and makes it active
|
|
394
|
+
heyditto save "Decided to keep JWT refresh in the gateway"
|
|
395
|
+
heyditto session current # the active id (exit 1 when none)
|
|
396
|
+
heyditto session list # local history, * marks the active one
|
|
397
|
+
heyditto session end # back to implicit sessions
|
|
398
|
+
heyditto session use 9e9a93c3 # reactivate by id or unique prefix
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
`DITTO_SESSION_ID=<id>` pins a session for one shell or script (it overrides the
|
|
402
|
+
saved one and never sends a name). Sessions are tracked locally in
|
|
403
|
+
`~/.config/heyditto/cli/mcp-sessions.json`; the server keeps the threads.
|
|
404
|
+
|
|
405
|
+
### `agents`
|
|
406
|
+
|
|
407
|
+
List your Ditto agents (`GET /api/v5/chat-agents`): id, kind (`main`, `chat`,
|
|
408
|
+
`inference_endpoint`, `mcp`, `connector`), name, thread count, last activity and
|
|
409
|
+
the live connections (API keys, OAuth grants, endpoints) writing into each.
|
|
410
|
+
|
|
292
411
|
## Environment
|
|
293
412
|
|
|
294
413
|
- `DITTO_API_KEY` (optional) — MCP API key override. Agents can instead run `heyditto init --json` for no-human setup.
|
|
295
414
|
- `DITTO_API_BASE` (optional) — API base URL. Defaults to `https://api.heyditto.ai`. Useful for local dev (`http://localhost:3400`).
|
|
415
|
+
- `DITTO_SESSION_ID` (optional) — pin an explicit MCP session id for this shell (see `session`).
|
|
416
|
+
- `DITTO_CONFIG_DIR` (optional) — config directory for the saved key, default endpoint and session records. Defaults to `$XDG_CONFIG_HOME/heyditto/cli` or `~/.config/heyditto/cli`.
|
|
296
417
|
|
|
297
418
|
## Output
|
|
298
419
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type HarnessPlan, type PlanInput } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the `claude` invocation.
|
|
4
|
+
*
|
|
5
|
+
* Auth goes through ANTHROPIC_AUTH_TOKEN (sent as `Authorization: Bearer`):
|
|
6
|
+
* unlike ANTHROPIC_API_KEY it never triggers the interactive "use this API
|
|
7
|
+
* key? No (recommended)" approval prompt, so the same plan works headless.
|
|
8
|
+
* Any inherited ANTHROPIC_API_KEY is removed so it cannot re-trigger that
|
|
9
|
+
* prompt or add a stray x-api-key header.
|
|
10
|
+
*/
|
|
11
|
+
export declare function planClaude(input: PlanInput): HarnessPlan;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SESSION_HEADER, apiRootOf } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Builds the `claude` invocation.
|
|
4
|
+
*
|
|
5
|
+
* Auth goes through ANTHROPIC_AUTH_TOKEN (sent as `Authorization: Bearer`):
|
|
6
|
+
* unlike ANTHROPIC_API_KEY it never triggers the interactive "use this API
|
|
7
|
+
* key? No (recommended)" approval prompt, so the same plan works headless.
|
|
8
|
+
* Any inherited ANTHROPIC_API_KEY is removed so it cannot re-trigger that
|
|
9
|
+
* prompt or add a stray x-api-key header.
|
|
10
|
+
*/
|
|
11
|
+
export function planClaude(input) {
|
|
12
|
+
const args = [];
|
|
13
|
+
if (input.yolo)
|
|
14
|
+
args.push("--dangerously-skip-permissions");
|
|
15
|
+
else if (input.yellow)
|
|
16
|
+
args.push("--permission-mode", "acceptEdits");
|
|
17
|
+
else if (input.plan)
|
|
18
|
+
args.push("--permission-mode", "plan");
|
|
19
|
+
if (input.resumeId)
|
|
20
|
+
args.push("--resume", input.resumeId);
|
|
21
|
+
else if (input.resumeLast)
|
|
22
|
+
args.push("--continue");
|
|
23
|
+
else
|
|
24
|
+
args.push("--session-id", input.sessionId);
|
|
25
|
+
if (input.model)
|
|
26
|
+
args.push("--model", input.model);
|
|
27
|
+
if (input.prompt !== undefined)
|
|
28
|
+
args.push("-p", input.prompt);
|
|
29
|
+
args.push(...input.passthrough);
|
|
30
|
+
const headers = [input.env.ANTHROPIC_CUSTOM_HEADERS?.trim(), `${SESSION_HEADER}: ${input.sessionId}`]
|
|
31
|
+
.filter((h) => Boolean(h))
|
|
32
|
+
.join("\n");
|
|
33
|
+
const envSet = {
|
|
34
|
+
ANTHROPIC_BASE_URL: apiRootOf(input.baseUrl),
|
|
35
|
+
ANTHROPIC_AUTH_TOKEN: input.apiKey,
|
|
36
|
+
ANTHROPIC_CUSTOM_HEADERS: headers,
|
|
37
|
+
};
|
|
38
|
+
if (input.model)
|
|
39
|
+
envSet.ANTHROPIC_MODEL = input.model;
|
|
40
|
+
return {
|
|
41
|
+
command: "claude",
|
|
42
|
+
args,
|
|
43
|
+
envSet,
|
|
44
|
+
envUnset: ["ANTHROPIC_API_KEY"],
|
|
45
|
+
installHint: "install Claude Code: npm i -g @anthropic-ai/claude-code (or see https://code.claude.com)",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzF;;;;;;;;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,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;;QAC9C,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAEhD,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,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;IAEhC,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,wBAAwB,EAAE,IAAI,EAAE,EAAE,GAAG,cAAc,KAAK,KAAK,CAAC,SAAS,EAAE,CAAC;SAClG,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;IAEtD,OAAO;QACL,OAAO,EAAE,QAAQ;QACjB,IAAI;QACJ,MAAM;QACN,QAAQ,EAAE,CAAC,mBAAmB,CAAC;QAC/B,WAAW,EAAE,0FAA0F;KACxG,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type HarnessPlan, type PlanInput } from "./types.js";
|
|
2
|
+
/** Quotes a string as a TOML basic string for `codex -c key=value`. */
|
|
3
|
+
export declare function tomlString(value: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Builds the `codex` invocation.
|
|
6
|
+
*
|
|
7
|
+
* Codex ignores OPENAI_BASE_URL while a ChatGPT login exists and only speaks
|
|
8
|
+
* the Responses wire, so the endpoint is injected as a dedicated `ditto`
|
|
9
|
+
* model provider through `-c` overrides (nothing is written to
|
|
10
|
+
* ~/.codex/config.toml). The session header rides on the provider's
|
|
11
|
+
* http_headers so every request lands in the same Ditto thread.
|
|
12
|
+
*/
|
|
13
|
+
export declare function planCodex(input: PlanInput): HarnessPlan;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CODEX_KEY_ENV, SESSION_HEADER } from "./types.js";
|
|
2
|
+
/** Quotes a string as a TOML basic string for `codex -c key=value`. */
|
|
3
|
+
export function tomlString(value) {
|
|
4
|
+
return JSON.stringify(value);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Builds the `codex` invocation.
|
|
8
|
+
*
|
|
9
|
+
* Codex ignores OPENAI_BASE_URL while a ChatGPT login exists and only speaks
|
|
10
|
+
* the Responses wire, so the endpoint is injected as a dedicated `ditto`
|
|
11
|
+
* model provider through `-c` overrides (nothing is written to
|
|
12
|
+
* ~/.codex/config.toml). The session header rides on the provider's
|
|
13
|
+
* http_headers so every request lands in the same Ditto thread.
|
|
14
|
+
*/
|
|
15
|
+
export function planCodex(input) {
|
|
16
|
+
const args = [];
|
|
17
|
+
if (input.prompt !== undefined)
|
|
18
|
+
args.push("exec");
|
|
19
|
+
else if (input.resumeId || input.resumeLast)
|
|
20
|
+
args.push("resume");
|
|
21
|
+
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={${tomlString(SESSION_HEADER)}=${tomlString(input.sessionId)}}`);
|
|
22
|
+
if (input.model)
|
|
23
|
+
args.push("-m", input.model);
|
|
24
|
+
if (input.yolo)
|
|
25
|
+
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
26
|
+
else if (input.yellow)
|
|
27
|
+
args.push("-a", "on-request", "-s", "workspace-write");
|
|
28
|
+
if (input.prompt !== undefined) {
|
|
29
|
+
args.push("--skip-git-repo-check", ...input.passthrough, input.prompt);
|
|
30
|
+
}
|
|
31
|
+
else if (input.resumeId) {
|
|
32
|
+
args.push(...input.passthrough, input.resumeId);
|
|
33
|
+
}
|
|
34
|
+
else if (input.resumeLast) {
|
|
35
|
+
args.push("--last", ...input.passthrough);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
args.push(...input.passthrough);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
command: "codex",
|
|
42
|
+
args,
|
|
43
|
+
envSet: { [CODEX_KEY_ENV]: input.apiKey },
|
|
44
|
+
envUnset: [],
|
|
45
|
+
installHint: "install Codex: npm i -g @openai/codex (or brew install codex)",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/agents/codex.ts"],"names":[],"mappings":"AAAA,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;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,KAAgB;IACxC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SAC7C,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,UAAU;QAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEjE,IAAI,CAAC,IAAI,CACP,IAAI,EACJ,kBAAkB,UAAU,CAAC,OAAO,CAAC,EAAE,EACvC,IAAI,EACJ,8BAA8B,UAAU,CAAC,OAAO,CAAC,EAAE,EACnD,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,uCAAuC,UAAU,CAAC,cAAc,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CACpG,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,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACzE,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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Harness } from "./types.js";
|
|
2
|
+
export interface LaunchOptions {
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
budget?: string;
|
|
5
|
+
expires?: string;
|
|
6
|
+
keepKey?: boolean;
|
|
7
|
+
session?: string;
|
|
8
|
+
resume?: string | boolean;
|
|
9
|
+
continue?: boolean;
|
|
10
|
+
yolo?: boolean;
|
|
11
|
+
yellow?: boolean;
|
|
12
|
+
plan?: boolean;
|
|
13
|
+
prompt?: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
worktree?: string | boolean;
|
|
16
|
+
name?: string;
|
|
17
|
+
dryRun?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** Launches a coding harness against a Ditto inference endpoint with a temporary key. */
|
|
20
|
+
export declare function launchHarness(harness: Harness, rawArgs: string[], options: LaunchOptions): Promise<void>;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { createInterface } from "node:readline/promises";
|
|
5
|
+
import { createKey, listEndpoints, revokeKey, } from "../api.js";
|
|
6
|
+
import { readStoredAuth } from "../store.js";
|
|
7
|
+
import { planClaude } from "./claude.js";
|
|
8
|
+
import { planCodex } from "./codex.js";
|
|
9
|
+
import { latestSession, readSession, writeSession } from "./sessions.js";
|
|
10
|
+
import { KEY_EXPIRIES, childEnv, maskKey, stripSeparator, } from "./types.js";
|
|
11
|
+
import { defaultWorktreeName, ensureWorktree } from "./worktree.js";
|
|
12
|
+
const DRY_RUN_KEY = "ditto_inf_<minted-at-launch>";
|
|
13
|
+
function log(line) {
|
|
14
|
+
process.stderr.write(`ditto: ${line}\n`);
|
|
15
|
+
}
|
|
16
|
+
function planFor(harness, input) {
|
|
17
|
+
return harness === "claude" ? planClaude(input) : planCodex(input);
|
|
18
|
+
}
|
|
19
|
+
function binaryAvailable(command) {
|
|
20
|
+
const probe = process.platform === "win32" ? "where" : "which";
|
|
21
|
+
return spawnSync(probe, [command], { stdio: "ignore" }).status === 0;
|
|
22
|
+
}
|
|
23
|
+
function parseBudget(raw) {
|
|
24
|
+
if (raw === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
const n = Number(raw.replace(/[_,]/g, ""));
|
|
27
|
+
if (!Number.isInteger(n) || n <= 0)
|
|
28
|
+
throw new Error(`--budget must be a positive integer token count, got "${raw}"`);
|
|
29
|
+
return n;
|
|
30
|
+
}
|
|
31
|
+
function parseExpiry(raw) {
|
|
32
|
+
const value = (raw ?? "1d").trim();
|
|
33
|
+
if (KEY_EXPIRIES.includes(value))
|
|
34
|
+
return value;
|
|
35
|
+
throw new Error(`--expires must be one of: ${KEY_EXPIRIES.join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
function formatSpend(e) {
|
|
38
|
+
const used = e.spentTokens ?? 0;
|
|
39
|
+
if (e.spendLimitTokens == null || e.spendLimitTokens < 0)
|
|
40
|
+
return `${used.toLocaleString()} tokens used, no limit`;
|
|
41
|
+
return `${used.toLocaleString()} / ${e.spendLimitTokens.toLocaleString()} tokens${e.spendPeriod && e.spendPeriod !== "never" ? ` per ${e.spendPeriod.replace(/ly$/, "")}` : ""}`;
|
|
42
|
+
}
|
|
43
|
+
async function pickEndpoint(endpoints) {
|
|
44
|
+
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
45
|
+
throw new Error("no endpoint selected. Pass --endpoint <slug>, or set a default with `heyditto endpoints --set-default <slug>`.");
|
|
46
|
+
}
|
|
47
|
+
process.stderr.write("Ditto inference endpoints:\n\n");
|
|
48
|
+
endpoints.forEach((e, i) => {
|
|
49
|
+
process.stderr.write(` ${i + 1}) ${e.slug} ${e.name !== e.slug ? `(${e.name}) ` : ""}model=${e.model}\n`);
|
|
50
|
+
process.stderr.write(` ${formatSpend(e)}${e.recordTrace ? " · traces on" : " · traces off"}\n`);
|
|
51
|
+
});
|
|
52
|
+
process.stderr.write("\n");
|
|
53
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
54
|
+
try {
|
|
55
|
+
for (;;) {
|
|
56
|
+
const answer = (await rl.question(`Connect to which endpoint? [1-${endpoints.length}] `)).trim();
|
|
57
|
+
const idx = Number(answer);
|
|
58
|
+
if (Number.isInteger(idx) && idx >= 1 && idx <= endpoints.length)
|
|
59
|
+
return endpoints[idx - 1];
|
|
60
|
+
const bySlug = endpoints.find((e) => e.slug === answer || e.id === answer);
|
|
61
|
+
if (bySlug)
|
|
62
|
+
return bySlug;
|
|
63
|
+
process.stderr.write("Pick a number from the list.\n");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
rl.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function resolveEndpoint(wanted, endpoints) {
|
|
71
|
+
if (endpoints.length === 0) {
|
|
72
|
+
throw new Error("you have no inference endpoints yet. Create one in the Ditto app: Settings → Developer → Inference endpoints.");
|
|
73
|
+
}
|
|
74
|
+
const stored = (await readStoredAuth())?.defaultEndpoint;
|
|
75
|
+
const target = wanted?.trim() || stored?.trim();
|
|
76
|
+
if (target) {
|
|
77
|
+
const match = endpoints.find((e) => e.slug === target || e.id === target);
|
|
78
|
+
if (match)
|
|
79
|
+
return match;
|
|
80
|
+
if (wanted) {
|
|
81
|
+
throw new Error(`no endpoint named "${target}". Available: ${endpoints.map((e) => e.slug).join(", ")}`);
|
|
82
|
+
}
|
|
83
|
+
log(`default endpoint "${target}" no longer exists; pick another`);
|
|
84
|
+
}
|
|
85
|
+
if (endpoints.length === 1)
|
|
86
|
+
return endpoints[0];
|
|
87
|
+
return pickEndpoint(endpoints);
|
|
88
|
+
}
|
|
89
|
+
function forwardSignals(child) {
|
|
90
|
+
// Ctrl+C reaches the child directly through the shared terminal; the
|
|
91
|
+
// wrapper only has to survive it so it can revoke the key afterwards.
|
|
92
|
+
const onInt = () => {
|
|
93
|
+
/* keep running until the harness exits */
|
|
94
|
+
};
|
|
95
|
+
const onTerm = () => child.kill("SIGTERM");
|
|
96
|
+
const onHup = () => child.kill("SIGHUP");
|
|
97
|
+
process.on("SIGINT", onInt);
|
|
98
|
+
process.on("SIGTERM", onTerm);
|
|
99
|
+
process.on("SIGHUP", onHup);
|
|
100
|
+
return () => {
|
|
101
|
+
process.off("SIGINT", onInt);
|
|
102
|
+
process.off("SIGTERM", onTerm);
|
|
103
|
+
process.off("SIGHUP", onHup);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function waitForExit(child) {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
child.on("error", reject);
|
|
109
|
+
child.on("exit", (code, signal) => resolve(code ?? (signal ? 128 + (os.constants.signals[signal] ?? 0) : null)));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/** Launches a coding harness against a Ditto inference endpoint with a temporary key. */
|
|
113
|
+
export async function launchHarness(harness, rawArgs, options) {
|
|
114
|
+
const passthrough = stripSeparator(rawArgs);
|
|
115
|
+
const budget = parseBudget(options.budget);
|
|
116
|
+
const expiresIn = parseExpiry(options.expires);
|
|
117
|
+
if (options.plan && harness !== "claude")
|
|
118
|
+
throw new Error("--plan is only supported for claude");
|
|
119
|
+
if ([options.yolo, options.yellow, options.plan].filter(Boolean).length > 1) {
|
|
120
|
+
throw new Error("choose only one of --yolo, --yellow, --plan");
|
|
121
|
+
}
|
|
122
|
+
if (options.resume !== undefined && options.continue)
|
|
123
|
+
throw new Error("use either --resume or --continue");
|
|
124
|
+
if (options.resume !== undefined && options.session)
|
|
125
|
+
throw new Error("--session cannot be combined with --resume");
|
|
126
|
+
if (!options.dryRun && !binaryAvailable(harness)) {
|
|
127
|
+
const hint = planFor(harness, {
|
|
128
|
+
baseUrl: "",
|
|
129
|
+
apiKey: "",
|
|
130
|
+
sessionId: "",
|
|
131
|
+
passthrough: [],
|
|
132
|
+
env: {},
|
|
133
|
+
}).installHint;
|
|
134
|
+
throw new Error(`"${harness}" is not on your PATH; ${hint}`);
|
|
135
|
+
}
|
|
136
|
+
// Resume: reuse the local record's Ditto session id so the traces stay in
|
|
137
|
+
// the same thread; a fresh key is minted every launch.
|
|
138
|
+
let record;
|
|
139
|
+
let resumeLast = Boolean(options.continue);
|
|
140
|
+
if (options.resume !== undefined) {
|
|
141
|
+
record =
|
|
142
|
+
typeof options.resume === "string"
|
|
143
|
+
? await readSession(options.resume)
|
|
144
|
+
: await latestSession(harness, process.cwd());
|
|
145
|
+
if (!record) {
|
|
146
|
+
throw new Error(typeof options.resume === "string"
|
|
147
|
+
? `no local session "${options.resume}"; see \`heyditto sessions\``
|
|
148
|
+
: `no previous ${harness} session to resume; see \`heyditto sessions\``);
|
|
149
|
+
}
|
|
150
|
+
if (record.harness !== harness)
|
|
151
|
+
throw new Error(`session ${record.id} was a ${record.harness} session`);
|
|
152
|
+
if (!record.harnessSessionId)
|
|
153
|
+
resumeLast = true;
|
|
154
|
+
}
|
|
155
|
+
const catalog = await listEndpoints();
|
|
156
|
+
const endpoint = await resolveEndpoint(options.endpoint ?? record?.endpointSlug, catalog.endpoints);
|
|
157
|
+
let cwd = record?.worktree ?? process.cwd();
|
|
158
|
+
let worktreePath = record?.worktree;
|
|
159
|
+
if (options.worktree !== undefined && options.worktree !== false) {
|
|
160
|
+
const name = typeof options.worktree === "string" ? options.worktree : defaultWorktreeName(harness);
|
|
161
|
+
if (options.dryRun) {
|
|
162
|
+
log(`would create worktree .worktrees/${name}`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const wt = await ensureWorktree(process.cwd(), name);
|
|
166
|
+
log(`${wt.created ? "created" : "reusing"} worktree ${wt.path} (branch ${wt.branch})`);
|
|
167
|
+
cwd = wt.path;
|
|
168
|
+
worktreePath = wt.path;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const sessionId = record?.id ?? options.session?.trim() ?? randomUUID();
|
|
172
|
+
if (!/^[A-Za-z0-9._:@-]{1,128}$/.test(sessionId)) {
|
|
173
|
+
throw new Error("--session must match ^[A-Za-z0-9._:@-]{1,128}$");
|
|
174
|
+
}
|
|
175
|
+
const harnessSessionId = record?.harnessSessionId ?? (harness === "claude" && !resumeLast ? sessionId : undefined);
|
|
176
|
+
const model = options.model ?? record?.model ?? (harness === "codex" ? endpoint.slug : undefined);
|
|
177
|
+
const now = new Date().toISOString();
|
|
178
|
+
const keyName = options.name?.trim() || `cli:${harness}:${os.hostname()}`;
|
|
179
|
+
let key;
|
|
180
|
+
if (!options.dryRun) {
|
|
181
|
+
key = await createKey(endpoint.id, {
|
|
182
|
+
name: keyName,
|
|
183
|
+
expiresIn,
|
|
184
|
+
...(budget !== undefined ? { spendLimitTokens: budget, spendPeriod: "never" } : {}),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const plan = planFor(harness, {
|
|
188
|
+
baseUrl: catalog.baseUrl,
|
|
189
|
+
apiKey: key?.key ?? DRY_RUN_KEY,
|
|
190
|
+
sessionId,
|
|
191
|
+
model,
|
|
192
|
+
resumeId: record ? record.harnessSessionId : undefined,
|
|
193
|
+
resumeLast,
|
|
194
|
+
prompt: options.prompt,
|
|
195
|
+
yolo: options.yolo,
|
|
196
|
+
yellow: options.yellow,
|
|
197
|
+
plan: options.plan,
|
|
198
|
+
passthrough,
|
|
199
|
+
env: process.env,
|
|
200
|
+
});
|
|
201
|
+
const banner = [
|
|
202
|
+
`endpoint=${endpoint.slug}`,
|
|
203
|
+
`model=${model ?? "(harness default → endpoint routes)"}`,
|
|
204
|
+
`key=${key ? `…${key.keyHint || maskKey(key.key ?? "")}` : "(dry run)"}`,
|
|
205
|
+
`expires=${expiresIn}${options.keepKey ? "" : " (revoked on exit)"}`,
|
|
206
|
+
budget !== undefined ? `budget=${budget.toLocaleString()} tokens` : undefined,
|
|
207
|
+
`session=${sessionId}`,
|
|
208
|
+
]
|
|
209
|
+
.filter(Boolean)
|
|
210
|
+
.join(" ");
|
|
211
|
+
log(banner);
|
|
212
|
+
log(`traces: Ditto app → Settings → Developer → Inference endpoints → ${endpoint.slug}`);
|
|
213
|
+
if (options.dryRun) {
|
|
214
|
+
const env = Object.fromEntries(Object.entries(plan.envSet).map(([k, v]) => [k, v === DRY_RUN_KEY ? "<key>" : v]));
|
|
215
|
+
process.stdout.write(`${JSON.stringify({
|
|
216
|
+
harness,
|
|
217
|
+
command: plan.command,
|
|
218
|
+
args: plan.args,
|
|
219
|
+
env,
|
|
220
|
+
unsetEnv: plan.envUnset,
|
|
221
|
+
cwd,
|
|
222
|
+
endpoint: { id: endpoint.id, slug: endpoint.slug, model: endpoint.model },
|
|
223
|
+
sessionId,
|
|
224
|
+
key: { name: keyName, expiresIn, spendLimitTokens: budget ?? null },
|
|
225
|
+
}, null, 2)}\n`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (!key)
|
|
229
|
+
throw new Error("internal: key was not minted");
|
|
230
|
+
const nextRecord = {
|
|
231
|
+
id: sessionId,
|
|
232
|
+
harness,
|
|
233
|
+
endpointId: endpoint.id,
|
|
234
|
+
endpointSlug: endpoint.slug,
|
|
235
|
+
keyId: key.id,
|
|
236
|
+
keyHint: key.keyHint,
|
|
237
|
+
harnessSessionId,
|
|
238
|
+
cwd: record?.cwd ?? process.cwd(),
|
|
239
|
+
worktree: worktreePath,
|
|
240
|
+
model,
|
|
241
|
+
createdAt: record?.createdAt ?? now,
|
|
242
|
+
lastLaunchedAt: now,
|
|
243
|
+
launches: (record?.launches ?? 0) + 1,
|
|
244
|
+
};
|
|
245
|
+
await writeSession(nextRecord);
|
|
246
|
+
const child = spawn(plan.command, plan.args, {
|
|
247
|
+
stdio: "inherit",
|
|
248
|
+
env: childEnv(plan, process.env),
|
|
249
|
+
cwd,
|
|
250
|
+
});
|
|
251
|
+
const restore = forwardSignals(child);
|
|
252
|
+
let exitCode = null;
|
|
253
|
+
try {
|
|
254
|
+
exitCode = await waitForExit(child);
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
restore();
|
|
258
|
+
if (options.keepKey) {
|
|
259
|
+
log(`kept key …${key.keyHint} (expires ${expiresIn}); revoke it from the Ditto app when done`);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
try {
|
|
263
|
+
await revokeKey(endpoint.id, key.id);
|
|
264
|
+
log(`revoked session key …${key.keyHint}; thread kept (resume with: heyditto ${harness} --resume ${sessionId})`);
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
log(`could not revoke key …${key.keyHint} (${err instanceof Error ? err.message : String(err)}); it expires in ${expiresIn}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
await writeSession({ ...nextRecord, endedAt: new Date().toISOString(), exitCode });
|
|
271
|
+
}
|
|
272
|
+
process.exitCode = exitCode ?? 1;
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/agents/launch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Harness } from "./types.js";
|
|
2
|
+
/** One coding-agent session launched by the CLI; the Ditto session id is the file name. */
|
|
3
|
+
export interface SessionRecord {
|
|
4
|
+
id: string;
|
|
5
|
+
harness: Harness;
|
|
6
|
+
endpointId: string;
|
|
7
|
+
endpointSlug: string;
|
|
8
|
+
/** Last key minted for this session; revoked on exit unless --keep-key. */
|
|
9
|
+
keyId?: string;
|
|
10
|
+
keyHint?: string;
|
|
11
|
+
/** Harness-native id used to resume (Claude's --session-id; Codex thread id when known). */
|
|
12
|
+
harnessSessionId?: string;
|
|
13
|
+
cwd: string;
|
|
14
|
+
worktree?: string;
|
|
15
|
+
model?: string;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
lastLaunchedAt: string;
|
|
18
|
+
endedAt?: string;
|
|
19
|
+
exitCode?: number | null;
|
|
20
|
+
launches: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function writeSession(record: SessionRecord): Promise<void>;
|
|
23
|
+
export declare function readSession(id: string): Promise<SessionRecord | undefined>;
|
|
24
|
+
export declare function removeSession(id: string): Promise<boolean>;
|
|
25
|
+
/** All local records, most recently launched first. */
|
|
26
|
+
export declare function listSessions(): Promise<SessionRecord[]>;
|
|
27
|
+
/** Most recent record for a harness, preferring one launched from the same directory. */
|
|
28
|
+
export declare function latestSession(harness: Harness, cwd: string): Promise<SessionRecord | undefined>;
|