@idcloudhost/ai-connect 0.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.
Files changed (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -0
  3. package/dist/adapters/aider.d.ts +11 -0
  4. package/dist/adapters/aider.d.ts.map +1 -0
  5. package/dist/adapters/aider.js +69 -0
  6. package/dist/adapters/aider.js.map +1 -0
  7. package/dist/adapters/claude-code.d.ts +17 -0
  8. package/dist/adapters/claude-code.d.ts.map +1 -0
  9. package/dist/adapters/claude-code.js +73 -0
  10. package/dist/adapters/claude-code.js.map +1 -0
  11. package/dist/adapters/codex.d.ts +11 -0
  12. package/dist/adapters/codex.d.ts.map +1 -0
  13. package/dist/adapters/codex.js +71 -0
  14. package/dist/adapters/codex.js.map +1 -0
  15. package/dist/adapters/continue.d.ts +7 -0
  16. package/dist/adapters/continue.d.ts.map +1 -0
  17. package/dist/adapters/continue.js +79 -0
  18. package/dist/adapters/continue.js.map +1 -0
  19. package/dist/adapters/crush.d.ts +4 -0
  20. package/dist/adapters/crush.d.ts.map +1 -0
  21. package/dist/adapters/crush.js +72 -0
  22. package/dist/adapters/crush.js.map +1 -0
  23. package/dist/adapters/goose.d.ts +11 -0
  24. package/dist/adapters/goose.d.ts.map +1 -0
  25. package/dist/adapters/goose.js +68 -0
  26. package/dist/adapters/goose.js.map +1 -0
  27. package/dist/adapters/helpers.d.ts +33 -0
  28. package/dist/adapters/helpers.d.ts.map +1 -0
  29. package/dist/adapters/helpers.js +117 -0
  30. package/dist/adapters/helpers.js.map +1 -0
  31. package/dist/adapters/hermes.d.ts +11 -0
  32. package/dist/adapters/hermes.d.ts.map +1 -0
  33. package/dist/adapters/hermes.js +100 -0
  34. package/dist/adapters/hermes.js.map +1 -0
  35. package/dist/adapters/index.d.ts +20 -0
  36. package/dist/adapters/index.d.ts.map +1 -0
  37. package/dist/adapters/index.js +60 -0
  38. package/dist/adapters/index.js.map +1 -0
  39. package/dist/adapters/manual.d.ts +6 -0
  40. package/dist/adapters/manual.d.ts.map +1 -0
  41. package/dist/adapters/manual.js +87 -0
  42. package/dist/adapters/manual.js.map +1 -0
  43. package/dist/adapters/omp.d.ts +8 -0
  44. package/dist/adapters/omp.d.ts.map +1 -0
  45. package/dist/adapters/omp.js +64 -0
  46. package/dist/adapters/omp.js.map +1 -0
  47. package/dist/adapters/openclaw.d.ts +10 -0
  48. package/dist/adapters/openclaw.d.ts.map +1 -0
  49. package/dist/adapters/openclaw.js +107 -0
  50. package/dist/adapters/openclaw.js.map +1 -0
  51. package/dist/adapters/opencode.d.ts +11 -0
  52. package/dist/adapters/opencode.d.ts.map +1 -0
  53. package/dist/adapters/opencode.js +86 -0
  54. package/dist/adapters/opencode.js.map +1 -0
  55. package/dist/adapters/pi-family.d.ts +14 -0
  56. package/dist/adapters/pi-family.d.ts.map +1 -0
  57. package/dist/adapters/pi-family.js +27 -0
  58. package/dist/adapters/pi-family.js.map +1 -0
  59. package/dist/adapters/pi.d.ts +8 -0
  60. package/dist/adapters/pi.d.ts.map +1 -0
  61. package/dist/adapters/pi.js +56 -0
  62. package/dist/adapters/pi.js.map +1 -0
  63. package/dist/adapters/shell-env.d.ts +12 -0
  64. package/dist/adapters/shell-env.d.ts.map +1 -0
  65. package/dist/adapters/shell-env.js +81 -0
  66. package/dist/adapters/shell-env.js.map +1 -0
  67. package/dist/adapters/zed.d.ts +10 -0
  68. package/dist/adapters/zed.d.ts.map +1 -0
  69. package/dist/adapters/zed.js +91 -0
  70. package/dist/adapters/zed.js.map +1 -0
  71. package/dist/cli.d.ts +3 -0
  72. package/dist/cli.d.ts.map +1 -0
  73. package/dist/cli.js +207 -0
  74. package/dist/cli.js.map +1 -0
  75. package/dist/commands/connect.d.ts +3 -0
  76. package/dist/commands/connect.d.ts.map +1 -0
  77. package/dist/commands/connect.js +179 -0
  78. package/dist/commands/connect.js.map +1 -0
  79. package/dist/commands/context.d.ts +21 -0
  80. package/dist/commands/context.d.ts.map +1 -0
  81. package/dist/commands/context.js +4 -0
  82. package/dist/commands/context.js.map +1 -0
  83. package/dist/commands/doctor.d.ts +11 -0
  84. package/dist/commands/doctor.d.ts.map +1 -0
  85. package/dist/commands/doctor.js +85 -0
  86. package/dist/commands/doctor.js.map +1 -0
  87. package/dist/commands/env.d.ts +7 -0
  88. package/dist/commands/env.d.ts.map +1 -0
  89. package/dist/commands/env.js +37 -0
  90. package/dist/commands/env.js.map +1 -0
  91. package/dist/commands/list.d.ts +3 -0
  92. package/dist/commands/list.d.ts.map +1 -0
  93. package/dist/commands/list.js +39 -0
  94. package/dist/commands/list.js.map +1 -0
  95. package/dist/commands/login.d.ts +7 -0
  96. package/dist/commands/login.d.ts.map +1 -0
  97. package/dist/commands/login.js +130 -0
  98. package/dist/commands/login.js.map +1 -0
  99. package/dist/commands/models.d.ts +9 -0
  100. package/dist/commands/models.d.ts.map +1 -0
  101. package/dist/commands/models.js +51 -0
  102. package/dist/commands/models.js.map +1 -0
  103. package/dist/commands/remove.d.ts +3 -0
  104. package/dist/commands/remove.d.ts.map +1 -0
  105. package/dist/commands/remove.js +43 -0
  106. package/dist/commands/remove.js.map +1 -0
  107. package/dist/commands/snippet.d.ts +7 -0
  108. package/dist/commands/snippet.d.ts.map +1 -0
  109. package/dist/commands/snippet.js +91 -0
  110. package/dist/commands/snippet.js.map +1 -0
  111. package/dist/core/detect.d.ts +12 -0
  112. package/dist/core/detect.d.ts.map +1 -0
  113. package/dist/core/detect.js +59 -0
  114. package/dist/core/detect.js.map +1 -0
  115. package/dist/core/discovery.d.ts +46 -0
  116. package/dist/core/discovery.d.ts.map +1 -0
  117. package/dist/core/discovery.js +124 -0
  118. package/dist/core/discovery.js.map +1 -0
  119. package/dist/core/gateway.d.ts +59 -0
  120. package/dist/core/gateway.d.ts.map +1 -0
  121. package/dist/core/gateway.js +385 -0
  122. package/dist/core/gateway.js.map +1 -0
  123. package/dist/core/http.d.ts +18 -0
  124. package/dist/core/http.d.ts.map +1 -0
  125. package/dist/core/http.js +73 -0
  126. package/dist/core/http.js.map +1 -0
  127. package/dist/core/management.d.ts +47 -0
  128. package/dist/core/management.d.ts.map +1 -0
  129. package/dist/core/management.js +88 -0
  130. package/dist/core/management.js.map +1 -0
  131. package/dist/core/store.d.ts +25 -0
  132. package/dist/core/store.d.ts.map +1 -0
  133. package/dist/core/store.js +48 -0
  134. package/dist/core/store.js.map +1 -0
  135. package/dist/fsutil/edit.d.ts +18 -0
  136. package/dist/fsutil/edit.d.ts.map +1 -0
  137. package/dist/fsutil/edit.js +102 -0
  138. package/dist/fsutil/edit.js.map +1 -0
  139. package/dist/fsutil/formats.d.ts +32 -0
  140. package/dist/fsutil/formats.d.ts.map +1 -0
  141. package/dist/fsutil/formats.js +154 -0
  142. package/dist/fsutil/formats.js.map +1 -0
  143. package/dist/index.d.ts +23 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +8 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/types.d.ts +134 -0
  148. package/dist/types.d.ts.map +1 -0
  149. package/dist/types.js +3 -0
  150. package/dist/types.js.map +1 -0
  151. package/dist/ui.d.ts +20 -0
  152. package/dist/ui.d.ts.map +1 -0
  153. package/dist/ui.js +69 -0
  154. package/dist/ui.js.map +1 -0
  155. package/package.json +75 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PT Cloud Hosting Indonesia (IDCloudHost)
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,287 @@
1
+ # @idcloudhost/ai-connect
2
+
3
+ Point any AI agent at the IDCloudHost PaaS AI gateway in one command.
4
+
5
+ The gateway is OpenAI-compatible, so in principle every agent below already works
6
+ with it. In practice each one wants the endpoint spelled out in its own config
7
+ file, in its own format, under its own key names — five different config
8
+ languages across the tools people actually use. This does that part for you, and
9
+ then proves the result works instead of assuming it.
10
+
11
+ ```bash
12
+ npx @idcloudhost/ai-connect login # save your gateway URL + key once
13
+ npx @idcloudhost/ai-connect connect # configure every agent it finds
14
+ npx @idcloudhost/ai-connect doctor # prove it works
15
+ ```
16
+
17
+ ## What it supports
18
+
19
+ | Agent | Written to | Notes |
20
+ |---|---|---|
21
+ | [opencode](https://opencode.ai) | `~/.config/opencode/opencode.json` | via `@ai-sdk/openai-compatible` |
22
+ | [pi](https://github.com/badlogic/pi-mono) | `~/.pi/agent/models.json` | |
23
+ | [omp (oh-my-pi)](https://github.com/can1357/oh-my-pi) | `~/.omp/agent/models.yml` | |
24
+ | [Hermes Agent](https://github.com/NousResearch/hermes-agent) | `~/.hermes/config.yaml` | one active model at a time |
25
+ | [OpenClaw](https://docs.openclaw.ai) | `~/.openclaw/openclaw.json` | provider **and** `agents.defaults.models` allowlist |
26
+ | [Codex CLI](https://developers.openai.com/codex) | `~/.codex/config.toml` | key must come from the environment |
27
+ | [Claude Code](https://docs.claude.com/en/docs/claude-code) | `~/.claude/settings.json` | needs the Anthropic `/v1/messages` route |
28
+ | [Crush](https://github.com/charmbracelet/crush) | `~/.config/crush/crush.json` | |
29
+ | [Goose](https://block.github.io/goose) | `~/.config/goose/config.yaml` | key lives in your OS keyring |
30
+ | [Aider](https://aider.chat) | `~/.aider.conf.yml` | key deliberately kept out of the file |
31
+ | [Continue](https://continue.dev) | `~/.continue/config.yaml` | |
32
+ | [Zed](https://zed.dev) | `~/.config/zed/settings.json` | key is entered in the Agent panel |
33
+ | Shell / anything else | `~/.config/idch-ai/env.sh` | `OPENAI_BASE_URL` + `OPENAI_API_KEY` |
34
+
35
+ Cline (and its forks Roo Code and Kilo Code), Cursor, and the Gemini-CLI forks
36
+ like Qwen Code keep their settings somewhere a CLI has no business writing —
37
+ VS Code's encrypted global state, a GUI keychain. For those it prints the exact
38
+ values to paste, filled in with your real URL and models.
39
+
40
+ ## Install
41
+
42
+ ```bash
43
+ npm i -g @idcloudhost/ai-connect # then: idch-ai …
44
+ # or just: npx @idcloudhost/ai-connect …
45
+ ```
46
+
47
+ Node 18.17 or newer. No native dependencies.
48
+
49
+ ## Getting a key
50
+
51
+ An organization admin enables AI and creates a virtual key in the IDCloudHost
52
+ dashboard. The secret is shown exactly once, so save it when it appears.
53
+
54
+ `idch-ai login` can do the lookup for you if you have a dashboard token — it
55
+ reads the gateway URL from the dashboard and can mint a key if you are an admin.
56
+ Otherwise paste the URL and key you already have.
57
+
58
+ The gateway URL is **not** hard-coded in this package. It is per-environment and
59
+ published by the dashboard at runtime, so the tool discovers it rather than
60
+ guessing.
61
+
62
+ ## Commands
63
+
64
+ ```
65
+ idch-ai connect [agents…] Write provider config for one, several, or all detected agents
66
+ idch-ai list Show supported agents and which are installed here
67
+ idch-ai models List the models this key may use, with context sizes and pricing
68
+ idch-ai doctor Probe the live gateway and check every agent config
69
+ idch-ai remove [agents…] Strip the provider block back out
70
+ idch-ai login / logout Manage the saved profile
71
+ idch-ai env [--shell fish|powershell|dotenv]
72
+ idch-ai snippet [curl|node|python|vercel-ai|langchain]
73
+ ```
74
+
75
+ Run `idch-ai connect` with no arguments and it detects what you have installed
76
+ and asks which to configure. Name them to skip the prompt:
77
+
78
+ ```bash
79
+ idch-ai connect opencode
80
+ idch-ai connect pi,omp,hermes
81
+ idch-ai connect all --model deepseek-chat
82
+ ```
83
+
84
+ ## How it treats your config files
85
+
86
+ These are files you hand-edit, so it behaves like a careful colleague rather
87
+ than a code generator:
88
+
89
+ - **Nothing is clobbered.** Only the provider block keyed by `--provider-id`
90
+ (default `idcloudhost`) is touched. Your themes, keybindings, other providers
91
+ and unrelated settings are left exactly as they were.
92
+ - **Comments survive.** YAML and JSONC files are edited in place, not
93
+ reserialised, so `# do not lose this` is still there afterwards.
94
+ - **Backups, always.** Overwriting an existing file first writes
95
+ `<file>.idch-bak-<timestamp>` beside it.
96
+ - **Atomic writes.** Temp file plus rename, so an interrupted run can never
97
+ leave you with half a config.
98
+ - **Idempotent.** Running it twice changes nothing the second time — no backup
99
+ churn.
100
+ - **Reversible.** `idch-ai remove` takes the block back out and prunes the empty
101
+ containers it leaves behind.
102
+
103
+ Preview anything before it happens:
104
+
105
+ ```bash
106
+ idch-ai connect openclaw --dry-run --print
107
+ ```
108
+
109
+ ## Where the key ends up
110
+
111
+ Some agents can only read a key from the environment (Codex, Goose, Zed), some
112
+ prefer a file, most accept either. Choose with `--secret`:
113
+
114
+ ```bash
115
+ idch-ai connect opencode --secret env # writes {env:IDCH_AI_API_KEY}
116
+ idch-ai connect opencode --secret inline # writes the key into the file
117
+ ```
118
+
119
+ Adapters that cannot honour your choice fall back to what the agent supports and
120
+ say so in the output. The saved profile at `~/.config/idch-ai/config.json` is
121
+ written `chmod 600`.
122
+
123
+ `idch-ai remove` strips config blocks; `idch-ai logout` forgets the profile. Do
124
+ both if you are rotating a key.
125
+
126
+ ## doctor: measured, not assumed
127
+
128
+ The gateway is a LiteLLM proxy in front of whatever upstream models your
129
+ environment has configured. Which OpenAI features work is therefore a property
130
+ of your deployment and the model you picked — not something a compatibility
131
+ table can tell you. `doctor` makes real calls and reports what came back.
132
+
133
+ Illustrative output — the exact statuses and wording are your gateway's, not
134
+ ours:
135
+
136
+ ```
137
+ Gateway capabilities
138
+ CHECK RESULT DETAIL
139
+ GET /models yes 7 models visible to this key
140
+ POST /chat/completions yes replied "ok"
141
+ Server-sent event streaming (stream: true) yes 14 chunks
142
+ Tool / function calling yes model returned a tool_call
143
+ response_format: json_object yes accepted
144
+ response_format: json_schema (structured output) no HTTP 4xx: <whatever your gateway said>
145
+ Image input (multimodal content parts) yes accepted
146
+ POST /embeddings yes 1 vector(s)
147
+ POST /responses (OpenAI Responses API) no HTTP 404: Not Found
148
+ POST /messages (Anthropic wire format) yes accepted
149
+ Rate limit / quota headers yes x-ratelimit-remaining-requests
150
+ ```
151
+
152
+ A `no` means that gateway or that model does not support it. It is information,
153
+ not a failure of this tool. Claude Code in particular depends on the Anthropic
154
+ `/messages` row being `yes` — check before relying on it.
155
+
156
+ Classification is deliberately coarse: a `2xx` is a `yes`, anything else is a
157
+ `no`, and the gateway's own status and message are passed through verbatim in the
158
+ detail column. Nothing keys on matching a particular error string, because the
159
+ wording a gateway uses for an unsupported feature is not something this package
160
+ can know in advance — you read what your gateway actually said.
161
+
162
+ ### What is known about `ai.paas.id` without a key
163
+
164
+ Verified 2026-08-30 by unauthenticated probing, which distinguishes a registered
165
+ route (LiteLLM's `auth_error` envelope) from an unregistered one (FastAPI's
166
+ `{"detail":"Not Found"}`):
167
+
168
+ - Registered under `/v1`: `messages`, `chat/completions`, `embeddings`,
169
+ `responses`, `completions`, `models`, `model/info`.
170
+ - The non-`/v1` mounts are **not** symmetric. `/chat/completions` and
171
+ `/embeddings` answer at the root too, `/models` answers `GET` only (405 to a
172
+ POST), and `/messages` is **`/v1` only** — it 404s at the root.
173
+
174
+ That last point is why the Claude Code adapter sets `ANTHROPIC_BASE_URL` to the
175
+ origin rather than the `/v1` base: Claude Code appends `/v1/messages` itself.
176
+
177
+ This tells you the routes are *registered*, not that any particular model behind
178
+ them answers. LiteLLM mounts `/v1/messages` regardless of what the upstream
179
+ supports. The functional question is what `doctor` answers, with your key and
180
+ your model.
181
+
182
+ ### The base-URL probe checks its own assumption
183
+
184
+ Finding the right base URL means asking `/models` at a few candidate paths and
185
+ reading the status code. A `401` there normally means "the route exists, your key
186
+ is the problem" — but that inference only holds if the host returns `404` for
187
+ paths that *don't* exist. Put a proxy, WAF or login page in front that answers
188
+ before routing, and every path looks identical; the inference silently becomes
189
+ worthless and the probe would pick whichever candidate it tried first.
190
+
191
+ So it doesn't assume. Whenever a candidate answers with something other than
192
+ `200` or `404`, the probe issues one unauthenticated request to a path that
193
+ cannot exist (`/__idch-probe-<random>`; the random segment stops a cache or WAF
194
+ from holding a stored answer). If that returns `404`, the discrimination is live
195
+ and the verdict stands. If it returns anything uniform, the tool refuses to
196
+ guess:
197
+
198
+ ```
199
+ ✖ could not verify https://gateway.example: this host answers the same way for
200
+ paths that do not exist, so an auth error proves nothing about whether the
201
+ OpenAI routes are there. Something is likely replying before routing (a proxy,
202
+ WAF, or login page).
203
+ ```
204
+
205
+ A `200` settles things outright, so the extra request costs nothing on the happy
206
+ path with a valid key.
207
+
208
+ ## Programmatic use
209
+
210
+ Every command is available as a library, for devcontainer bootstraps and
211
+ internal installers:
212
+
213
+ ```ts
214
+ import { resolveSource, buildTarget, getAdapter } from '@idcloudhost/ai-connect';
215
+
216
+ const source = await resolveSource({ baseUrl: process.env.IDCH_AI_BASE_URL, apiKey: key });
217
+ const target = await buildTarget(source, {});
218
+
219
+ await getAdapter('opencode')!.apply({
220
+ target,
221
+ dryRun: false,
222
+ secretMode: 'inline',
223
+ scope: 'user',
224
+ cwd: process.cwd(),
225
+ });
226
+ ```
227
+
228
+ `probeCapabilities`, `listModels`, `resolveApiBase` and the `ManagementClient`
229
+ are exported too.
230
+
231
+ ## Configuration reference
232
+
233
+ | Flag | Environment | Meaning |
234
+ |---|---|---|
235
+ | `--base-url` | `IDCH_AI_BASE_URL` | Gateway base URL |
236
+ | `--api-key` | `IDCH_AI_API_KEY` | Virtual key |
237
+ | `--profile` | | Named saved profile |
238
+ | `--dashboard-url`, `--token`, `--org` | | Discover the gateway through the dashboard API |
239
+ | `--model` | | Model to make the default |
240
+ | `--only a,b` | | Register only these models |
241
+ | `--provider-id` | | Key the block is written under (default `idcloudhost`) |
242
+ | `--secret inline\|env` | | Where the key goes |
243
+ | `--scope user\|project` | | Home directory or the current project |
244
+ | `--config-path` | | Override the target file (single agent only) |
245
+ | `--dry-run`, `--print` | | Preview, with a diff |
246
+ | `--no-verify`, `--no-save` | | Skip the live test call / profile save |
247
+ | `--json` | | Machine-readable output |
248
+ | `--yes` | | Never prompt |
249
+
250
+ Values resolve in this order: flag, environment, saved profile, dashboard
251
+ lookup, interactive prompt.
252
+
253
+ ## Adding an agent
254
+
255
+ An adapter is one file in `src/adapters/` implementing `detect`, `configPath`,
256
+ `apply` and `remove`, registered in `src/adapters/index.ts`. The integration
257
+ suite automatically covers every registered adapter: it checks the written
258
+ config points at the gateway, that a second run is a no-op, and that `remove`
259
+ leaves no trace.
260
+
261
+ ```bash
262
+ npm install
263
+ npm run build
264
+ npm test
265
+ ```
266
+
267
+ Tests run against a mock OpenAI-compatible server and a temporary `HOME`, so
268
+ they never touch your own agent configs.
269
+
270
+ ## Known limits
271
+
272
+ - The gateway URL differs per environment. Always discover it; never hard-code it.
273
+ - Route detection relies on the gateway answering `404` for unknown paths. That
274
+ is checked on every run rather than assumed — see above — but if the check
275
+ fails you get an error, not a guess.
276
+ - Model ids are passed through verbatim. There is no provider prefix to add, and
277
+ the id you send must be one your key is allowed to use — the allowlist is set
278
+ per team and per key by an organization admin.
279
+ - Deleting a config block can take an adjacent comment with it if the comment was
280
+ attached to the key being removed. The backup path is printed for that reason.
281
+ - A route existing is not the same as a model serving it. Claude Code, the
282
+ Responses API and embeddings all need an upstream that actually answers, not
283
+ just a mounted path. `doctor` tells you which you have.
284
+
285
+ ## License
286
+
287
+ MIT
@@ -0,0 +1,11 @@
1
+ import type { Adapter } from '../types.js';
2
+ /**
3
+ * Aider routes through LiteLLM internally, so an OpenAI-compatible gateway is
4
+ * addressed as `openai/<model>` with `openai-api-base` repointed.
5
+ *
6
+ * The key is deliberately NOT written to the config file: aider's own docs push
7
+ * people to `.env` / the environment, and `~/.aider.conf.yml` is a file users
8
+ * commonly commit into dotfile repos.
9
+ */
10
+ export declare const aiderAdapter: Adapter;
11
+ //# sourceMappingURL=aider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aider.d.ts","sourceRoot":"","sources":["../../src/adapters/aider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,aAAa,CAAC;AAUtE;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,EAAE,OAqD1B,CAAC"}
@@ -0,0 +1,69 @@
1
+ import path from 'node:path';
2
+ import { readIfExists, shortenHome, writeFileSafely } from '../fsutil/edit.js';
3
+ import { editYaml, editYamlPruned, parseYaml } from '../fsutil/formats.js';
4
+ import { detectByBinOrConfig } from '../core/detect.js';
5
+ import { home, result } from './helpers.js';
6
+ function userConfig() {
7
+ return home('.aider.conf.yml');
8
+ }
9
+ /**
10
+ * Aider routes through LiteLLM internally, so an OpenAI-compatible gateway is
11
+ * addressed as `openai/<model>` with `openai-api-base` repointed.
12
+ *
13
+ * The key is deliberately NOT written to the config file: aider's own docs push
14
+ * people to `.env` / the environment, and `~/.aider.conf.yml` is a file users
15
+ * commonly commit into dotfile repos.
16
+ */
17
+ export const aiderAdapter = {
18
+ id: 'aider',
19
+ name: 'Aider',
20
+ summary: 'Pair-programming CLI. Config in ~/.aider.conf.yml, key from the environment.',
21
+ docsUrl: 'https://aider.chat/docs/config/aider_conf.html',
22
+ bins: ['aider'],
23
+ mode: 'auto',
24
+ supportsProjectScope: true,
25
+ secretModes: ['env'],
26
+ async detect() {
27
+ return detectByBinOrConfig(['aider'], [userConfig()]);
28
+ },
29
+ configPath({ scope, cwd, configPathOverride }) {
30
+ if (configPathOverride)
31
+ return configPathOverride;
32
+ return scope === 'project' ? path.join(cwd, '.aider.conf.yml') : userConfig();
33
+ },
34
+ async apply(opts) {
35
+ const file = this.configPath(opts);
36
+ const before = await readIfExists(file);
37
+ const { target } = opts;
38
+ const after = editYaml(before, (doc) => {
39
+ doc.setIn(['openai-api-base'], target.apiBase);
40
+ if (target.defaultModel)
41
+ doc.setIn(['model'], `openai/${target.defaultModel}`);
42
+ });
43
+ const change = await writeFileSafely(file, after, { dryRun: opts.dryRun });
44
+ return result(this.id, [change], {
45
+ envVars: { OPENAI_API_KEY: target.apiKey },
46
+ notes: [
47
+ 'Export OPENAI_API_KEY before running aider — the key is kept out of the config file on purpose.',
48
+ 'Models are addressed as openai/<model-id>; aider resolves them through LiteLLM.',
49
+ 'Unknown model ids make aider warn about missing metadata. That is cosmetic; add .aider.model.metadata.json to silence it.',
50
+ ],
51
+ tryCommand: target.defaultModel ? `aider --model openai/${target.defaultModel}` : 'aider',
52
+ });
53
+ },
54
+ async remove(opts) {
55
+ const file = this.configPath(opts);
56
+ const before = await readIfExists(file);
57
+ if (before === null)
58
+ return result(this.id, []);
59
+ const model = parseYaml(before).model;
60
+ const after = editYamlPruned(before, (doc) => {
61
+ doc.deleteIn(['openai-api-base']);
62
+ if (typeof model === 'string' && model.startsWith('openai/'))
63
+ doc.deleteIn(['model']);
64
+ });
65
+ const change = await writeFileSafely(file, after, { dryRun: opts.dryRun });
66
+ return result(this.id, [change], { notes: [`Cleared the gateway keys from ${shortenHome(file)}.`] });
67
+ },
68
+ };
69
+ //# sourceMappingURL=aider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aider.js","sourceRoot":"","sources":["../../src/adapters/aider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Adapter } from '../types.js';
2
+ /**
3
+ * Claude Code speaks the Anthropic wire format, not OpenAI's. It works against
4
+ * our gateway only through LiteLLM's Anthropic-compatible `/v1/messages` route.
5
+ *
6
+ * `ANTHROPIC_BASE_URL` must be the ORIGIN, because Claude Code appends
7
+ * `/v1/messages` itself. This is not a guess: on ai.paas.id the Anthropic route
8
+ * is mounted under `/v1` only — `/v1/messages` exists while `/messages` returns
9
+ * 404, unlike `/chat/completions` and `/embeddings` which answer at both. Setting
10
+ * the base to the `/v1` URL would request `/v1/v1/messages` and 404.
11
+ *
12
+ * That the route is registered does not mean the model behind it serves Anthropic
13
+ * requests — LiteLLM mounts /v1/messages regardless of upstream config. Only an
14
+ * authenticated call settles it, which is what `idch-ai doctor` makes.
15
+ */
16
+ export declare const claudeCodeAdapter: Adapter;
17
+ //# sourceMappingURL=claude-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/adapters/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,aAAa,CAAC;AAUtE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAuD/B,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { readIfExists, shortenHome, writeFileSafely } from '../fsutil/edit.js';
2
+ import { parseJson, stringifyJson } from '../fsutil/formats.js';
3
+ import { detectByBinOrConfig } from '../core/detect.js';
4
+ import { deleteIn, home, result, setIn } from './helpers.js';
5
+ function userConfig() {
6
+ return home('.claude', 'settings.json');
7
+ }
8
+ /**
9
+ * Claude Code speaks the Anthropic wire format, not OpenAI's. It works against
10
+ * our gateway only through LiteLLM's Anthropic-compatible `/v1/messages` route.
11
+ *
12
+ * `ANTHROPIC_BASE_URL` must be the ORIGIN, because Claude Code appends
13
+ * `/v1/messages` itself. This is not a guess: on ai.paas.id the Anthropic route
14
+ * is mounted under `/v1` only — `/v1/messages` exists while `/messages` returns
15
+ * 404, unlike `/chat/completions` and `/embeddings` which answer at both. Setting
16
+ * the base to the `/v1` URL would request `/v1/v1/messages` and 404.
17
+ *
18
+ * That the route is registered does not mean the model behind it serves Anthropic
19
+ * requests — LiteLLM mounts /v1/messages regardless of upstream config. Only an
20
+ * authenticated call settles it, which is what `idch-ai doctor` makes.
21
+ */
22
+ export const claudeCodeAdapter = {
23
+ id: 'claude-code',
24
+ name: 'Claude Code',
25
+ summary: "Anthropic's terminal agent. Uses the gateway's Anthropic-compatible /v1/messages route.",
26
+ docsUrl: 'https://docs.claude.com/en/docs/claude-code/settings',
27
+ bins: ['claude'],
28
+ mode: 'auto',
29
+ secretModes: ['inline', 'env'],
30
+ async detect() {
31
+ return detectByBinOrConfig(['claude'], [userConfig(), home('.claude')]);
32
+ },
33
+ configPath({ configPathOverride }) {
34
+ return configPathOverride ?? userConfig();
35
+ },
36
+ async apply(opts) {
37
+ const file = this.configPath(opts);
38
+ const before = await readIfExists(file);
39
+ const doc = parseJson(before);
40
+ const { target } = opts;
41
+ const env = {
42
+ ANTHROPIC_BASE_URL: target.origin,
43
+ ANTHROPIC_AUTH_TOKEN: target.apiKey,
44
+ };
45
+ if (target.defaultModel)
46
+ env.ANTHROPIC_MODEL = target.defaultModel;
47
+ const existing = doc.env;
48
+ setIn(doc, ['env'], { ...(existing && typeof existing === 'object' ? existing : {}), ...env });
49
+ const change = await writeFileSafely(file, stringifyJson(doc, before), { dryRun: opts.dryRun });
50
+ return result(this.id, [change], {
51
+ notes: [
52
+ 'Claude Code needs a model that answers on the Anthropic /v1/messages route. The route existing is not enough — confirm with: idch-ai doctor',
53
+ `ANTHROPIC_BASE_URL is set to the origin (${target.origin}) on purpose — Claude Code appends /v1/messages itself, and that route is mounted under /v1 only.`,
54
+ 'This writes a live token into ~/.claude/settings.json. Use `idch-ai env --shell` instead if you would rather keep it out of a file.',
55
+ 'Run `claude` in a new shell so the settings are picked up.',
56
+ ],
57
+ tryCommand: 'claude',
58
+ });
59
+ },
60
+ async remove(opts) {
61
+ const file = this.configPath(opts);
62
+ const before = await readIfExists(file);
63
+ if (before === null)
64
+ return result(this.id, []);
65
+ const doc = parseJson(before);
66
+ for (const key of ['ANTHROPIC_BASE_URL', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_MODEL']) {
67
+ deleteIn(doc, ['env', key]);
68
+ }
69
+ const change = await writeFileSafely(file, stringifyJson(doc, before), { dryRun: opts.dryRun });
70
+ return result(this.id, [change], { notes: [`Cleared the gateway env vars from ${shortenHome(file)}.`] });
71
+ },
72
+ };
73
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/adapters/claude-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Adapter } from '../types.js';
2
+ /**
3
+ * OpenAI Codex CLI, ~/.codex/config.toml.
4
+ *
5
+ * Codex reads the key from an environment variable named by `env_key` and has no
6
+ * inline-secret form, so this adapter is env-only by design.
7
+ * `wire_api = "chat"` selects /chat/completions; "responses" selects /responses,
8
+ * which our gateway only serves if the upstream supports it — check with `doctor`.
9
+ */
10
+ export declare const codexAdapter: Adapter;
11
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/adapters/codex.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,aAAa,CAAC;AAUtE;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,EAAE,OAwD1B,CAAC"}
@@ -0,0 +1,71 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import { readIfExists, shortenHome, writeFileSafely } from '../fsutil/edit.js';
4
+ import { parseToml, stringifyToml } from '../fsutil/formats.js';
5
+ import { detectByBinOrConfig } from '../core/detect.js';
6
+ import { KEY_ENV, deleteIn, result, setIn } from './helpers.js';
7
+ function userConfig() {
8
+ return path.join(process.env.CODEX_HOME ?? path.join(os.homedir(), '.codex'), 'config.toml');
9
+ }
10
+ /**
11
+ * OpenAI Codex CLI, ~/.codex/config.toml.
12
+ *
13
+ * Codex reads the key from an environment variable named by `env_key` and has no
14
+ * inline-secret form, so this adapter is env-only by design.
15
+ * `wire_api = "chat"` selects /chat/completions; "responses" selects /responses,
16
+ * which our gateway only serves if the upstream supports it — check with `doctor`.
17
+ */
18
+ export const codexAdapter = {
19
+ id: 'codex',
20
+ name: 'Codex CLI',
21
+ summary: "OpenAI's terminal coding agent. Providers live in ~/.codex/config.toml.",
22
+ docsUrl: 'https://developers.openai.com/codex/config-reference',
23
+ bins: ['codex'],
24
+ mode: 'auto',
25
+ secretModes: ['env'],
26
+ async detect() {
27
+ return detectByBinOrConfig(['codex'], [userConfig()]);
28
+ },
29
+ configPath({ configPathOverride }) {
30
+ return configPathOverride ?? userConfig();
31
+ },
32
+ async apply(opts) {
33
+ const file = this.configPath(opts);
34
+ const before = await readIfExists(file);
35
+ const doc = parseToml(before);
36
+ const { target } = opts;
37
+ setIn(doc, ['model_providers', target.providerId], {
38
+ name: target.providerLabel,
39
+ base_url: target.apiBase,
40
+ env_key: KEY_ENV,
41
+ wire_api: 'chat',
42
+ });
43
+ doc.model_provider = target.providerId;
44
+ if (target.defaultModel)
45
+ doc.model = target.defaultModel;
46
+ const change = await writeFileSafely(file, stringifyToml(doc), { dryRun: opts.dryRun });
47
+ return result(this.id, [change], {
48
+ // Codex has no inline-key form; the env var is mandatory regardless of --secret.
49
+ envVars: { [KEY_ENV]: target.apiKey },
50
+ notes: [
51
+ `Codex reads the key from $${KEY_ENV} — export it before starting codex.`,
52
+ 'Switch providers per run with: codex --config model_provider=<id>',
53
+ `If the model rejects requests, try wire_api = "responses" in ${shortenHome(file)} — the gateway only supports it when the upstream does.`,
54
+ ],
55
+ tryCommand: target.defaultModel ? `codex --model ${target.defaultModel}` : 'codex',
56
+ });
57
+ },
58
+ async remove(opts) {
59
+ const file = this.configPath(opts);
60
+ const before = await readIfExists(file);
61
+ if (before === null)
62
+ return result(this.id, []);
63
+ const doc = parseToml(before);
64
+ deleteIn(doc, ['model_providers', opts.providerId]);
65
+ if (doc.model_provider === opts.providerId)
66
+ delete doc.model_provider;
67
+ const change = await writeFileSafely(file, stringifyToml(doc), { dryRun: opts.dryRun });
68
+ return result(this.id, [change], { notes: [`Removed provider "${opts.providerId}" from ${shortenHome(file)}.`] });
69
+ },
70
+ };
71
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/adapters/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Adapter } from '../types.js';
2
+ /**
3
+ * Continue (VS Code / JetBrains). `models` is a list, not a map, so we replace
4
+ * entries whose `provider` is ours and keep everything else the user added.
5
+ */
6
+ export declare const continueAdapter: Adapter;
7
+ //# sourceMappingURL=continue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"continue.d.ts","sourceRoot":"","sources":["../../src/adapters/continue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,aAAa,CAAC;AAUtE;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,OAqE7B,CAAC"}