impel-cli 0.7.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 ADDED
@@ -0,0 +1,695 @@
1
+ # impel
2
+
3
+ A tiny, dependency-free CLI for macOS, Linux, and Windows that switches your
4
+ local **Claude Code** and **Codex** between two modes, per tool, reversibly:
5
+
6
+ - **gateway mode** — route through Impel's custom gateway, authenticated with an
7
+ Impel Personal Access Token (PAT).
8
+ - **account mode** — your own normal Anthropic / OpenAI login.
9
+
10
+ The gateway accepts `Authorization: Bearer impel_pat_...` directly. `impel` stores
11
+ that PAT locally and hands it to both tools through the same mechanism they
12
+ already support for external auth commands (`apiKeyHelper` for Claude Code,
13
+ `model_providers.<id>.auth.command` for Codex) — so the token is never written
14
+ into either tool's own config file.
15
+
16
+ - Claude Code talks to `<gateway>/anthropic`
17
+ - Codex uses `<gateway>/chatgpt_passthrough/backend-api/codex` as its provider
18
+ base and posts to the resulting `.../responses` compatibility route.
19
+
20
+ It can also install **isolated Impel desktop experiences**. On macOS, Impel
21
+ Claude and Impel ChatGPT are separate vendored app copies. On Windows, Impel
22
+ Claude launches Anthropic's signed vendor executable with its supported 3P
23
+ profile root, while Impel ChatGPT stages OpenAI's signed Electron payload out
24
+ of the protected Store directory and launches it with tenant-specific Codex and
25
+ browser profiles. Each approach keeps the user's normal app profile and
26
+ signed-in account untouched.
27
+
28
+ ## Install
29
+
30
+ Requires Node.js 18 or newer. The same command works on macOS, Linux, and
31
+ Windows (PowerShell):
32
+
33
+ ```sh
34
+ npm install --global impel-cli
35
+ ```
36
+
37
+ The installable package is intentionally public so Impel users do not need
38
+ access to the private GitHub repository. It contains only this dependency-free
39
+ CLI—no credentials, service secrets, or customer data. Installing it grants no
40
+ Impel access: every protected operation still requires an Impel PAT and the
41
+ gateway/control plane re-checks the user's live product access, scopes, and
42
+ tenant membership.
43
+
44
+ Releases are published from the private repository with npm Trusted Publishing:
45
+ the registry accepts a short-lived OIDC credential only from the pinned release
46
+ workflow, so no long-lived npm token is stored in GitHub. npm also verifies the
47
+ published tarball's advertised SHA-512 integrity during installation.
48
+
49
+ Maintainers can exercise the Windows-specific paths from a Linux Docker engine
50
+ with the real Windows Node binary and Wine's `cmd.exe` compatibility layer:
51
+
52
+ ```sh
53
+ docker build --platform linux/amd64 -f test/docker/windows-wine.Dockerfile -t impel-cli-windows-test .
54
+ docker run --rm --platform linux/amd64 impel-cli-windows-test
55
+ ```
56
+
57
+ The repository CI additionally runs the complete suite on `windows-latest`;
58
+ that native job owns the hostile percent/caret npm-shim case that Wine does not
59
+ faithfully emulate.
60
+
61
+ ## Quickstart
62
+
63
+ ```sh
64
+ impel setup # token → tenant → platform clients → verify
65
+ impel claude # launch Claude Code with an isolated Impel profile
66
+ impel codex # launch Codex with an isolated Impel profile
67
+ impel update # later: update the CLI, apps, and skills in one command
68
+ ```
69
+
70
+ `impel setup` walks through everything below in one command (create your PAT at
71
+ `https://www.useimpel.com/settings/gateway`, paste it at the secure prompt, and
72
+ the wizard does the rest). On Windows it installs either missing official vendor
73
+ CLI from npm, prepares both tenant-isolated CLI profiles, installs the signed
74
+ Claude and ChatGPT desktop apps with separate tenant profiles, and syncs skills.
75
+ On macOS it installs the isolated desktop apps. The individual commands remain
76
+ for piecemeal use:
77
+
78
+ ```sh
79
+ impel auth # paste your PAT when prompted
80
+ impel tenant list # see every organization available to this user
81
+ impel tenant use impel # select the tenant for accounts, specialists, and history
82
+ impel claude # launch Claude Code with an isolated Impel profile
83
+ impel codex # launch Codex with an isolated Impel profile
84
+ impel status # isolated-launcher readiness, native mode, and reachability
85
+ impel doctor # make one synthetic request to each provider and measure routing/latency
86
+
87
+ impel app install # install both isolated desktop app profiles
88
+ impel app update # update vendor apps, configs, and supported models
89
+ impel app open claude # open Claude Desktop with its isolated Impel profile
90
+ impel app open codex # open ChatGPT/Codex Desktop with its isolated Impel profile
91
+
92
+ # The normal `claude` and `codex` commands keep using your personal profiles.
93
+ ```
94
+
95
+ Fresh `impel claude` profiles start on **Claude Opus 4.8 with 1M context** and
96
+ also register **Claude Fable 5** in `/model`. You can choose a different
97
+ persistent default there, or override one session with `--model`.
98
+
99
+ ## Commands
100
+
101
+ ```
102
+ impel setup [--pat <pat>] [--tenant <org>] [--skip-apps] [--skip-clis]
103
+ Guided setup: token, tenant, platform clients, verify
104
+ (closes running Claude/ChatGPT apps, updates the vendor
105
+ apps, then installs the vendored Impel apps on macOS;
106
+ installs missing vendor CLIs on Windows)
107
+ impel update [--check] [--skip-apps] Update EVERYTHING: reinstall the CLI from npm, then
108
+ cascade to `app update all` and `skills sync all`
109
+ impel auth [--pat <pat>] [--gateway <url>] [--app <url>]
110
+ Store your Impel PAT + URLs
111
+ impel token [--tenant <org>] Print a selected-tenant bearer
112
+ impel mcp Run the local MCP bridge used by Claude/Codex
113
+ impel claude [claude args...] Launch isolated Impel Claude Code
114
+ impel codex [codex args...] Launch isolated Impel Codex
115
+
116
+ impel tasks list [--org <org>] List tickets
117
+ impel tasks get <id> Read one ticket
118
+ impel tasks create --title <title> [...] Create a ticket
119
+ impel tasks update <id> [...] Update a ticket
120
+ impel tasks delete <id> --yes Delete a ticket
121
+
122
+ impel tenant list List available organizations
123
+ impel tenant current Print the selected organization
124
+ impel tenant use <org> [--launch <target>] Select a tenant; optionally launch claude, codex, or apps
125
+ impel doctor [--tenant <org>|--all-tenants] Run synthetic provider, routing, and latency checks
126
+
127
+ impel use gateway [claude|codex|all] Switch to the Impel gateway (default: all)
128
+ impel on [claude|codex|all] Alias for `impel use gateway`
129
+ impel use account [claude|codex|all] Revert to your own login (default: all)
130
+ impel off [claude|codex|all] Alias for `impel use account`
131
+
132
+ impel skills sync [claude|codex|all] Sync Bifrost shared skills into managed clients (default: all)
133
+
134
+ impel status Launcher readiness + native mode + gateway reachability
135
+ impel app install [claude|chatgpt|codex|all] Install isolated apps
136
+ impel app update [claude|chatgpt|codex|all] Refresh apps, configs, and models
137
+ impel app refresh [claude|chatgpt|codex|all] Configs/catalog/skills only; safe while apps run
138
+ (--stale-only: no-op unless 6h+ since last sync)
139
+ impel app status [claude|chatgpt|codex|all] Show launcher/vendor status
140
+ impel app open [claude|chatgpt|codex|all] Launch isolated instances (fast path: opens
141
+ immediately when nothing changed since last open;
142
+ a detached stale-only refresh keeps things current)
143
+ impel app uninstall [target] [--keep-data] Remove Impel-managed app files
144
+ impel help Show help
145
+ impel --version Show version
146
+
147
+ # `impel setup` no longer takes a per-tool target and never flips native
148
+ # configs; flip a native tool explicitly with `impel use gateway [claude|codex]`.
149
+ ```
150
+
151
+ ### `impel auth [--pat <pat>] [--gateway <url>] [--app <url>]`
152
+
153
+ Stores your PAT, gateway base URL, and app/control-plane URL in `~/.config/impel/config.json`
154
+ (created with mode `0600`, directory `0700`).
155
+
156
+ - If `--pat` isn't given, you're prompted for it (input is masked).
157
+ - If `--gateway` isn't given, it falls back to (in order): the gateway URL you
158
+ already had stored, the `IMPEL_GATEWAY_URL` environment variable, then the
159
+ built-in default **`https://gateway.useimpel.com`**.
160
+ - If `--app` isn't given, it falls back to (in order): the app URL you already
161
+ had stored, the `IMPEL_APP_URL` environment variable, then the built-in
162
+ default **`https://www.useimpel.com`**.
163
+ - Trailing slashes on the gateway URL are stripped automatically.
164
+
165
+ ```sh
166
+ impel auth --pat impel_pat_xxxxxxxxxxxx --gateway https://gateway.useimpel.com --app https://www.useimpel.com
167
+ ```
168
+
169
+ ### `impel update`
170
+
171
+ One command brings everything current, in dependency order: the CLI itself
172
+ (`npm install -g impel-cli@latest` from npm, with no GitHub credentials),
173
+ then — re-executing the freshly installed build — `impel app update all`
174
+ (close running apps, update the vendor apps, rebuild the vendored Impel apps)
175
+ and `impel skills sync all` across every managed profile.
176
+
177
+ Update discovery compares the installed package version with npm's public
178
+ `latest` metadata, cached for 6 hours in
179
+ `~/.config/impel/update-check.json`. It does not invoke Git or contact GitHub.
180
+
181
+ You'll also hear about updates without asking: `impel claude`, `impel codex`,
182
+ `impel status`, and `impel app open` print a one-line notice when the cached
183
+ remote is ahead, and refresh the cache in a detached background process when
184
+ it's stale (TTY-only, never blocks a launch; set `IMPEL_SKIP_UPDATE_CHECK=1`
185
+ to silence). The Impel desktop apps keep themselves current too: their token
186
+ helper kicks off a detached `impel app refresh --stale-only` on use, which
187
+ re-syncs configs, the model catalog, and skills at most every 6 hours — safe
188
+ while the apps are running because it never swaps an app bundle.
189
+
190
+ ### `impel tasks ...` (aliases `impel task`, `impel tickets`, `impel ticket`)
191
+
192
+ CRUDs Impel tickets through the app/control-plane API using the stored PAT. New
193
+ PATs need the `tasks` scope and the user must be a **Workspace member**. Gateway
194
+ members intentionally cannot access tasks or other workspace features, even if
195
+ an older PAT still contains the scope.
196
+
197
+ ```sh
198
+ impel tasks list --scope visible
199
+ impel tasks get IMP-123
200
+ impel tasks create --title "Fix flaky login test" --priority high --labels bug,engineering
201
+ impel tasks update IMP-123 --progress review --assignee rahulvbrahmal@gmail.com
202
+ impel tasks update IMP-123 --description-file brief.md --description-mode replace
203
+ impel tasks delete IMP-123 --yes
204
+ ```
205
+
206
+ Common options: `--org <org>` targets a specific org (default: the PAT's org),
207
+ `--json` prints the raw API response, and `--app <url>` overrides the app URL for
208
+ one command.
209
+
210
+ ### Tenant selection
211
+
212
+ `impel auth` discovers every organization the PAT owner currently belongs to.
213
+ The default is `impel` when available, otherwise the first organization slug in
214
+ deterministic order. Change it with `impel tenant use <org>`.
215
+
216
+ The selected tenant controls all gateway-backed surfaces together: subscription
217
+ accounts, usage attribution, model catalog, and specialist discovery. The
218
+ gateway re-verifies both the underlying PAT and current organization membership;
219
+ editing the local config cannot grant access to another tenant.
220
+
221
+ Isolated CLI state is kept separately under
222
+ `~/.config/impel/cli/tenants/<org>/`, so Claude/Codex history and MCP state do
223
+ not bleed between tenants. `impel tenant use <org> --launch claude|codex|apps`
224
+ selects and launches in one command.
225
+
226
+ On macOS, desktop app state is isolated under
227
+ `~/.config/impel/apps/tenants/<org>/`. The app bundle in `~/Applications` is a
228
+ launcher for the currently opened tenant; `impel app open` refreshes that
229
+ launcher, the tenant-scoped bearer, model catalog, and specialist MCP arguments
230
+ before starting a new isolated instance. Windows Claude Desktop keeps each
231
+ tenant under `%LOCALAPPDATA%\Claude-3p\Impel\<org>` and launches the unchanged
232
+ signed vendor executable with that profile selected. Windows ChatGPT keeps its
233
+ Codex and browser state under the same tenant-scoped Impel apps root used on
234
+ macOS and launches a staged copy of the unchanged signed Store payload.
235
+
236
+ ### Product access
237
+
238
+ Impel has two product access levels, independent of organization membership and
239
+ provider-pool administration:
240
+
241
+ - **Workspace member** — the complete Impel workspace plus the gateway,
242
+ tenant-scoped inference, and specialist agents.
243
+ - **Gateway member** — the gateway setup/configuration surface, tenant selector,
244
+ inference, and specialist agents only. Tasks, workspace rooms, notifications,
245
+ and other collaboration features are unavailable.
246
+
247
+ The control plane resolves this entitlement live. The CLI caches it only for
248
+ display; the control plane and gateway enforce it again on every protected
249
+ operation. Changing the selected tenant never changes the user's product access.
250
+ Provider launches and `impel doctor` also preflight the PAT's live
251
+ `claude-code-gateway` / `codex-gateway` scopes, so an older single-provider PAT
252
+ gets a direct rotation instruction instead of a misleading model-capacity error.
253
+
254
+ ### `impel doctor`
255
+
256
+ Runs a real streaming request through each selected provider, so it consumes a
257
+ small amount of provider capacity. Each request contains only a generated
258
+ synthetic marker and asks the model to acknowledge that marker exactly. The
259
+ report verifies:
260
+
261
+ - live PAT, entitlement, and tenant membership resolution,
262
+ - the tenant-scoped model catalog,
263
+ - a selected subscription account on the response,
264
+ - the gateway-owned request ID and echoed client correlation ID,
265
+ - HTTP, bounded SSE parsing, provider response ID, and successful terminal event,
266
+ - exact request/response acknowledgement, and
267
+ - headers, first-byte, time-to-first-token (TTFT), and total latency.
268
+
269
+ It never prints the PAT or any real prompt content. The default TTFT warning
270
+ budgets are 6 seconds for Claude and 5 seconds for Codex. A budget miss is shown
271
+ as a warning; add `--strict-latency` to make it fail the command. Connectivity,
272
+ routing, account selection, or acknowledgement errors always produce a non-zero
273
+ exit code.
274
+
275
+ Organizations may intentionally have only one provider pool. When the live
276
+ catalog reports an exact `no_seat` state for a requested provider, the doctor
277
+ reports that provider as skipped instead of manufacturing a failed request. The
278
+ tenant can pass when another requested provider completes successfully. Expired
279
+ credentials, exhausted capacity, rate limits, ambiguous catalog responses, and
280
+ tenants with no usable requested provider still fail closed.
281
+
282
+ Use `--gateway <origin>` for a one-run data-plane override. Doctor resolves the
283
+ gateway in this order: `--gateway`, `IMPEL_GATEWAY_URL`, the saved gateway, then
284
+ the canonical default. The override must be a bare HTTP or HTTPS origin and is
285
+ never written back to the stored authentication config.
286
+
287
+ ```sh
288
+ impel doctor
289
+ impel doctor --attempts 2 --strict-latency
290
+ impel doctor --all-tenants --attempts 2 --json > readiness.json
291
+ impel doctor --tenant creador --providers claude
292
+ impel doctor --gateway https://gateway.useimpel.com
293
+ ```
294
+
295
+ `--attempts` is capped at five per provider and a single invocation is capped at
296
+ 50 total probes to prevent an accidental high-volume run. JSON output is one
297
+ sanitized report suitable for CI or a rollout checklist.
298
+
299
+ ### `impel token`
300
+
301
+ Prints **only** a bearer derived from the stored PAT and selected tenant to
302
+ stdout, nothing else. Non-zero exit and a
303
+ stderr message if you haven't run `impel auth` yet.
304
+
305
+ This is the contract both Claude Code's `apiKeyHelper` and Codex's
306
+ command-backed provider auth expect: a command that is invoked on demand and
307
+ whose stdout is the bearer token. Managed Codex auth and MCP entries call the
308
+ current Node executable plus Impel entry point directly, avoiding npm `.cmd`
309
+ resolution on Windows. Rotating your PAT is still one command
310
+ (`impel auth --pat ...`) and every tool picks it up on its next call/refresh.
311
+
312
+ ### Specialist delegation over MCP
313
+
314
+ Gateway mode also installs an `impel` stdio MCP server in Claude Code and
315
+ Codex, including the isolated Impel ChatGPT profile. The server runs the Impel
316
+ MCP entry point, reads the PAT from the owner-only Impel
317
+ config, and proxies JSON-RPC to `<gateway>/mcp`; the PAT is never written into
318
+ either client's MCP configuration. Available specialists are discovered at
319
+ runtime, so publishing a new opted-in specialist does not require another CLI
320
+ release.
321
+
322
+ ### Isolated CLI launchers
323
+
324
+ `impel claude [args...]` and `impel codex [args...]` launch the installed vendor
325
+ CLIs with every argument, the current working directory, stdin/stdout/stderr,
326
+ and the vendor exit code passed through unchanged. They require `impel auth`
327
+ and load gateway authentication plus the Impel MCP server from private,
328
+ Impel-only profiles:
329
+
330
+ - Claude: `~/.config/impel/cli/tenants/<org>/claude` via `CLAUDE_CONFIG_DIR`
331
+ - Codex: `~/.config/impel/cli/tenants/<org>/codex` via `CODEX_HOME`
332
+
333
+ The normal `claude` and `codex` commands still use `~/.claude`, `~/.claude.json`,
334
+ and `~/.codex`. The launchers do not read, copy, modify, or remove those native
335
+ profiles, their credentials, or their conversation history. Project-level
336
+ files and the working directory remain available exactly as they are in the
337
+ normal CLIs.
338
+
339
+ The isolated launchers also add an Impel-only instruction that checks the live
340
+ tenant specialist catalog before non-trivial work. When one available
341
+ specialist clearly matches, the client starts one idempotent specialist run,
342
+ waits for it to finish, and uses its result instead of repeating the task. It
343
+ continues normally when no specialist matches or delegation is unavailable.
344
+ Claude receives this through `--append-system-prompt`; Codex receives it through
345
+ the invocation-local `developer_instructions` override. Neither instruction is
346
+ written to or loaded by the native `claude` or `codex` commands.
347
+
348
+ The isolated Codex launcher uses Codex's embedded code-mode runtime so MCP tools
349
+ keep working when a vendor install does not include the optional standalone
350
+ `codex-code-mode-host` companion. Only the four specialist-control tools are
351
+ pre-approved for non-interactive delegation; other MCP tools retain Codex's
352
+ normal approval behavior. Its Codex-only instruction also resolves exact Impel
353
+ specialist tools through `tool_search` when the client defers MCP tools instead
354
+ of showing them directly. These settings apply only to `impel codex`.
355
+
356
+ The isolated Codex profile disables Codex shell snapshots and explicitly keeps
357
+ the default `KEY`/`SECRET`/`TOKEN` environment filter enabled. Before every
358
+ launch, `impel` removes derived `shell_snapshots`, drops inherited direct-auth
359
+ variables such as `CODEX_ACCESS_TOKEN`, and enforces owner-only permissions on
360
+ the managed profile and credential/config files. It also sweeps inactive tenant
361
+ and pre-tenancy Impel CLI profiles, so switching organizations cannot strand an
362
+ older snapshot. These safeguards apply only to Impel-owned `CODEX_HOME`
363
+ directories; native `~/.codex` remains untouched.
364
+
365
+ On Windows, `impel setup` ensures the official `@anthropic-ai/claude-code` and
366
+ `@openai/codex` packages are available, installing only the missing package(s)
367
+ with the same npm installation that supplies Impel. Use `--skip-clis` to prepare
368
+ the profiles without installing missing vendor CLIs. The launchers and skill
369
+ sync resolve native executables through `PATH`/`PATHEXT`, including global npm
370
+ `.cmd` and `.bat` shims. Windows environment references such as `%NVM_SYMLINK%`
371
+ are expanded during lookup, and the standard npm-global, Claude native-installer,
372
+ and Codex standalone-installer directories are checked even when the parent
373
+ PowerShell PATH is stale. Arguments are forwarded through an explicitly escaped
374
+ `cmd.exe` invocation without Node shell mode; NULs and line breaks, which cannot
375
+ be represented safely through a batch shim, are rejected before anything is
376
+ launched. Managed Codex auth and MCP subprocesses bypass batch shims entirely by
377
+ calling Node directly.
378
+
379
+ If npm itself fails during setup, the wizard prints the native spawn error and
380
+ the exact vendor-package command to retry in PowerShell. If npm succeeds but the
381
+ new commands remain undiscoverable, close and reopen PowerShell; then use
382
+ `npm prefix --global` to identify the directory that must be on the user PATH.
383
+ Setup exits non-zero and says it is incomplete until both launchers are ready—it
384
+ does not print a success banner after a failed installation.
385
+
386
+ Windows receives the complete CLI workflow: setup/auth, tenant selection,
387
+ isolated Claude and Codex launches, native profile switching, tasks, skills,
388
+ status, doctor, self-update, and both isolated desktop apps. `impel app install
389
+ claude` installs/updates Anthropic's signed per-user app through winget, writes
390
+ the selected tenant's 3P gateway configuration beneath Anthropic's reserved
391
+ `%LOCALAPPDATA%\Claude-3p\Impel\<tenant>` root, and leaves the executable and
392
+ normal Claude profile unchanged. `impel app install codex` installs/updates
393
+ OpenAI's `OpenAI.Codex` Store package, stages its unchanged signed app payload
394
+ inside Impel's shared app cache, and writes separate Codex and Chromium
395
+ profiles. `impel app open claude` and `impel app open codex` launch those
396
+ isolated profiles without changing either normal vendor profile.
397
+
398
+ For Claude, the Impel PAT is supplied only to the launched child process as an
399
+ authentication token and disappears when that process exits; it is never
400
+ written into the isolated Claude profile. The launcher also recognizes
401
+ Claude's standard native-installer location at `~/.local/bin/claude` when that
402
+ directory is not present in the parent process's `PATH`.
403
+
404
+ ```sh
405
+ impel claude
406
+ impel claude -p "summarize this repository" --output-format json
407
+ impel codex
408
+ impel codex exec --json "review the current diff"
409
+ ```
410
+
411
+ `impel use gateway` remains available for users who intentionally want to
412
+ switch their native profiles in place; the isolated launchers are the safer
413
+ default when personal and Impel sessions should coexist.
414
+
415
+ ### `impel use gateway [claude|codex|all]` (alias `impel on`)
416
+
417
+ Turns **on** gateway mode for the given tool (default `all`). Requires `impel auth`
418
+ first.
419
+
420
+ **Claude Code** — merges into `~/.claude/settings.json` (creating it if needed,
421
+ leaving every other key — permissions, hooks, other env vars — untouched):
422
+
423
+ ```json
424
+ {
425
+ "apiKeyHelper": "impel token",
426
+ "env": {
427
+ "ANTHROPIC_BASE_URL": "https://gateway.useimpel.com/anthropic"
428
+ }
429
+ }
430
+ ```
431
+
432
+ **Codex** — merges into `~/.codex/config.toml`. Codex supports pointing
433
+ `model_provider` at a custom `[model_providers.<id>]` table, plus a
434
+ **command-backed** bearer token via a `[model_providers.<id>.auth]` sub-table
435
+ (`command` + `args`, re-invoked on a timer and on auth retries — the same shape
436
+ as Claude Code's `apiKeyHelper`), so we reuse `impel token` there too instead of
437
+ writing the PAT into `config.toml` or `~/.codex/auth.json`. The block written
438
+ (fenced by markers, so everything else in your `config.toml` — other
439
+ `model_providers`, `mcp_servers`, sandbox policy — is left alone):
440
+
441
+ ```toml
442
+ model_provider = "impel"
443
+
444
+ # >>> impel-cli managed block (model_providers.impel) >>>
445
+ # Generated by `impel use gateway codex`. Safe to re-run; do not hand-edit
446
+ # the lines between the markers above/below, they'll be overwritten.
447
+ [model_providers.impel]
448
+ name = "Impel Gateway"
449
+ base_url = "https://gateway.useimpel.com/chatgpt_passthrough/backend-api/codex"
450
+ wire_api = "responses"
451
+
452
+ [model_providers.impel.auth]
453
+ command = "impel"
454
+ args = ["token"]
455
+ timeout_ms = 5000
456
+ refresh_interval_ms = 300000
457
+ # <<< impel-cli managed block <<<
458
+ ```
459
+
460
+ Before overwriting anything, `impel` **backs up** what was there (any prior
461
+ `apiKeyHelper` / `ANTHROPIC_BASE_URL`, and the prior Codex `model_provider`
462
+ value) into `~/.config/impel/config.json`, so `impel use account` can restore it
463
+ exactly. If `config.toml` already has a `[model_providers.impel]` table that
464
+ wasn't written by this tool, `impel` refuses to touch the file and tells you to
465
+ resolve it manually rather than risk emitting invalid/duplicate TOML.
466
+
467
+ ### `impel use account [claude|codex|all]` (alias `impel off`)
468
+
469
+ Turns gateway mode **off** and restores your normal account-based setup. It
470
+ removes **only** the Impel-added bits, never your own config:
471
+
472
+ - **Claude Code** — removes `apiKeyHelper` only if it's `"impel token"`, and
473
+ `env.ANTHROPIC_BASE_URL` only if it equals `<gateway>/anthropic`. Then restores
474
+ whatever those keys held before gateway mode (from the backup). If there was
475
+ nothing before, they're deleted (and an emptied `env` is dropped).
476
+ - **Codex** — removes the marker-fenced `[model_providers.impel]` block, and
477
+ resets `model_provider` **only if it currently reads `"impel"`** — back to its
478
+ backed-up prior value, or, if there was none, removes the line entirely so
479
+ Codex falls back to its built-in default. All other tables are untouched.
480
+
481
+ `impel use account` doesn't require the PAT and is safe to run repeatedly (if a
482
+ tool is already in account mode it just says so and changes nothing).
483
+
484
+ ### `impel skills sync [claude|codex|all]`
485
+
486
+ Syncs Impel's **Bifrost shared skills** into your managed Claude Code and Codex
487
+ clients. The gateway publishes a public, per-client plugin marketplace whose
488
+ bundled plugin — `bifrost-all-skills` — contains every served skill:
489
+
490
+ - Claude Code registers its manifest URL:
491
+ `<gateway>/api/skills/serve/claude-code/.claude-plugin/marketplace.json`
492
+ - Codex registers the Git source `<gateway>/api/skills/serve/codex`; its
493
+ marketplace manifest lives at `<source>/.agents/plugins/marketplace.json`.
494
+
495
+ The sync is idempotent: it registers the marketplace, ensures `bifrost-all-skills`
496
+ is installed, then runs the client's refresh commands so an already-installed
497
+ plugin picks up a newer bundle. It reads each marketplace's registered `name`
498
+ from the served `marketplace.json` to pin installs and refreshes. Codex receives
499
+ the Git source rather than the nested manifest URL because its CLI clones the
500
+ source before discovering `.agents/plugins/marketplace.json`.
501
+
502
+ You rarely need to run it by hand — it runs **automatically** at the end of every
503
+ install/update path:
504
+
505
+ - `impel use gateway ...` / `impel setup ...` (native `~/.claude` / `~/.codex`)
506
+ - `impel app install` / `impel app update` (each isolated desktop app profile)
507
+
508
+ `impel skills sync` also covers the isolated `impel claude` / `impel codex` CLI
509
+ profiles and installed desktop apps when they exist. Skill syncing is
510
+ best-effort: if a client CLI, its `plugin` subcommand, or the gateway is
511
+ unavailable, it logs a warning and moves on — it never fails the parent command.
512
+ Set `IMPEL_SKIP_SKILL_SYNC=1` to disable it entirely (offline/CI).
513
+
514
+ ### `impel status`
515
+
516
+ Shows whether the isolated CLI launchers are ready, and whether each native
517
+ tool profile is currently in **GATEWAY** or **ACCOUNT** mode (detected from
518
+ whether the Impel markers / base URL are present), plus:
519
+
520
+ - the configured gateway URL,
521
+ - whether a PAT is stored (masked, e.g. `impel_pat_ab12...wxyz`),
522
+ - the live Workspace member / Gateway member entitlement when available,
523
+ - a best-effort reachability check: a `POST` to `<gateway>/anthropic/v1/messages`
524
+ with a 5s timeout. Any HTTP response (including a 4xx) counts as "reachable" —
525
+ this checks the network path and gateway routing, not the request itself. A
526
+ `401`/`403` is called out separately since it usually just means the stored PAT
527
+ was rejected, not that the gateway is down.
528
+
529
+ Codex CLI and the Codex IDE extension / desktop app share the same
530
+ `~/.codex/config.toml`, so they always report the same mode.
531
+
532
+ ## Isolated Impel desktop apps
533
+
534
+ ### Windows Claude and ChatGPT Desktop
535
+
536
+ ```powershell
537
+ impel app install claude
538
+ impel app open claude
539
+ impel app status claude
540
+ impel app install codex
541
+ impel app open codex
542
+ impel app status codex
543
+ ```
544
+
545
+ The Windows path does not clone, patch, or re-sign Claude. It locates either
546
+ Anthropic's standard Squirrel installation or its signed MSIX package, then
547
+ launches the vendor executable with the supported `CLAUDE_USER_DATA_DIR`
548
+ override. Anthropic's MSIX manifest explicitly reserves `%LOCALAPPDATA%\Claude-3p`
549
+ for unvirtualized third-party profiles, so each Impel tenant lives below that
550
+ root. The app's normal first-party profile is not read or changed. `impel setup`
551
+ installs this profile by default; use `--skip-apps` to omit it.
552
+
553
+ For ChatGPT/Codex Desktop, Windows installs the official Microsoft Store product
554
+ `9PLM9XGG6VKS` (`OpenAI.Codex`). Windows blocks other programs from directly
555
+ executing Electron inside `C:\Program Files\WindowsApps`, so Impel copies the
556
+ vendor's complete `app` payload, unchanged, into a shared managed cache. It
557
+ then launches that copy with a separate `CODEX_HOME` and
558
+ `CODEX_ELECTRON_USER_DATA_PATH` (plus Electron `--user-data-dir` as a legacy
559
+ fallback). The normal Store app, `%USERPROFILE%\.codex`, and the normal ChatGPT
560
+ browser profile are not read or changed. `chatgpt` and `codex` are equivalent
561
+ app-target names.
562
+
563
+ ### macOS Claude and ChatGPT
564
+
565
+ `impel app install` creates `Impel Claude.app` and `Impel ChatGPT.app` in
566
+ `~/Applications`. They have separate bundle identities and gateway
567
+ configuration:
568
+
569
+ - Impel Claude is an APFS-cloned vendored copy of the official app with its own
570
+ bundle and helper identities. Its LaunchServices environment sets the
571
+ vendor-supported `CLAUDE_USER_DATA_DIR` and writes the 3P gateway `configLibrary` below
572
+ `~/.config/impel/apps/claude`. A narrowly version-checked compatibility patch
573
+ connects Claude's native plan-usage meter to the authenticated aggregate
574
+ Claude subscription pool exposed by the Impel gateway.
575
+ - Impel ChatGPT is an APFS-cloned vendored copy of the official app. Its wrapper
576
+ sets `CODEX_HOME`, passes a separate Chromium `--user-data-dir`, and writes its
577
+ provider config and model catalog below `~/.config/impel/apps/chatgpt`. It also
578
+ supplies a process-scoped, Codex-compatible wrapper around the Impel PAT so
579
+ native `/status` requests can read the organization's aggregated subscription
580
+ pool from the gateway. Its generated Codex config uses the same snapshot and
581
+ subprocess-environment hardening as `impel codex`; install, update, and every
582
+ direct app launch also remove older snapshots for the selected profile, while
583
+ app install/update/open sweeps inactive tenant and legacy Impel app profiles.
584
+ - Neither app changes `~/.claude`, `~/.claude.json`, `~/.codex`, or the
585
+ normal vendor app's browser/profile data.
586
+
587
+ The official Claude and ChatGPT apps remain separate installations and keep
588
+ their normal accounts, updater, profiles, and bundle identities. The installer
589
+ uses an existing official app when found. If one is missing and
590
+ Homebrew is available, it installs the corresponding cask into
591
+ `~/Applications`. `impel app update` upgrades Homebrew-managed vendor apps,
592
+ rebuilds the isolated apps, reapplies version-checked compatibility patches,
593
+ rewrites versioned configs, and fetches the PAT-scoped model catalog from
594
+ `<gateway>/v1/models`. If a vendor update changes a required renderer contract,
595
+ the update fails without replacing the working Impel copy. If the model endpoint
596
+ is temporarily unavailable, the installer retains functionality using the CLI's
597
+ bundled catalog.
598
+
599
+ For ChatGPT/Codex, the gateway controls which models and capabilities are
600
+ enabled while the installer merges those entries with the vendor app's native
601
+ catalog. The Impel copy relaxes the vendor renderer's ChatGPT-account-only Fast
602
+ gate because gateway auth supplies the entitlement instead. The copy is then
603
+ ad-hoc signed locally. This preserves model-specific instructions and exposes
604
+ the same reasoning-effort and service-tier controls as the normal app, including
605
+ Ultra where supported and Fast only on models that advertise the `priority`
606
+ tier. Inference uses an explicit non-WebSocket Impel model provider so Codex
607
+ cannot fall back to the vendor's hard-coded `chatgpt.com` transport with an
608
+ Impel PAT. Config refreshes replace only the Impel-managed gateway block; plugins,
609
+ desktop preferences, projects, and other settings written by the isolated app
610
+ are kept.
611
+
612
+ Both vendored copies are ad-hoc signed locally after their bundle identities and
613
+ launch wrappers are installed. The Claude 3P config necessarily contains the PAT because that app accepts a
614
+ gateway API key rather than a token-helper command. It is stored in the same
615
+ owner-only Impel config tree as the primary credential. ChatGPT/Codex uses a
616
+ command-backed helper and does not copy the PAT into TOML. The installable app
617
+ also reads that helper into its own process environment for Codex account-status
618
+ compatibility; the value disappears when the app exits.
619
+
620
+ ## Switching back and forth
621
+
622
+ The whole point: flipping between the gateway and your own account is one
623
+ command, per tool, and always reversible.
624
+
625
+ ```sh
626
+ impel on # both tools -> gateway
627
+ impel off # both tools -> your own account (restores prior config exactly)
628
+
629
+ impel on codex # just Codex -> gateway, leave Claude Code alone
630
+ impel off claude # just Claude Code -> account
631
+
632
+ impel status # see where each tool stands right now
633
+ ```
634
+
635
+ How the clean revert is guaranteed: `impel on` records the pre-existing values
636
+ (your own `apiKeyHelper` / `ANTHROPIC_BASE_URL`, and Codex's prior
637
+ `model_provider`) into `~/.config/impel/config.json` **before** overwriting them,
638
+ and only ever captures a real *account-mode* value (re-running `impel on` never
639
+ clobbers the backup with Impel's own values). `impel off` removes only keys that
640
+ still point at Impel and restores those backups — so a full `on` → `off`
641
+ round-trip leaves `settings.json` and `config.toml` byte-for-byte back to where
642
+ they started, with all your unrelated keys and tables intact.
643
+
644
+ If you'd rather revert by hand: delete `apiKeyHelper` + the Impel
645
+ `ANTHROPIC_BASE_URL` from `~/.claude/settings.json`; delete the block between the
646
+ `# >>> impel-cli managed block ... >>>` / `# <<< impel-cli managed block <<<`
647
+ markers in `~/.codex/config.toml` and reset `model_provider`; and
648
+ `rm -rf ~/.config/impel` to forget the PAT + gateway entirely.
649
+
650
+ ## Codex compatibility route
651
+
652
+ The installable app uses an explicit Codex model provider at
653
+ `<gateway>/chatgpt_passthrough/backend-api/codex`, producing the request path
654
+ `/chatgpt_passthrough/backend-api/codex/responses`. The provider disables
655
+ WebSockets because the vendor's built-in ChatGPT provider derives that URL from
656
+ `chatgpt.com` rather than the configured account-service base. The gateway
657
+ preserves the request body while replacing the Impel PAT with the selected
658
+ ChatGPT subscription credential.
659
+
660
+ The installable Impel ChatGPT app also points Codex's account client at
661
+ `<gateway>/chatgpt_passthrough/backend-api`. The gateway exposes a virtual Impel
662
+ pool identity and `GET .../wham/usage`, aggregating each primary and secondary
663
+ subscription window across all active Codex seats with equal-seat weighting.
664
+ This lets the native `/status` surface show pool capacity without exposing any
665
+ provider account identity or OAuth credential.
666
+
667
+ Before every launch, the wrapper refreshes Codex's supported personal-access-token
668
+ login record from the tenant helper. That keeps the desktop app in native
669
+ ChatGPT-account mode, allowing `/status` to request the aggregate five-hour and
670
+ weekly windows instead of treating Impel as an API-key-only provider.
671
+
672
+ The gateway also exposes `POST /v1/responses`, but that is the conventional
673
+ OpenAI Responses front door for SDK/Eve traffic and may normalize the body
674
+ (including enforcing `store: false`). The CLI deliberately does not use it.
675
+
676
+ ## Design notes
677
+
678
+ - **Zero dependencies.** Arg parsing, JSON/TOML merging, masked prompts, and the
679
+ reachability check are all hand-rolled against Node stdlib (`fs`, `os`,
680
+ `path`, `readline`, global `fetch`). No `chalk`, no `commander`, no `inquirer`.
681
+ - **The PAT lives in exactly one place:** `~/.config/impel/config.json` (mode
682
+ `0600`). Both Claude Code and Codex fetch it on demand by shelling out to
683
+ `impel token` rather than copying it into their own config files — one token,
684
+ one rotation path, no stale copies.
685
+ - **The `config.toml` merge is intentionally narrow**, not a general TOML
686
+ parser: it only ever touches a single top-level `model_provider` line and its
687
+ own fenced `[model_providers.impel*]` block, leaving unrelated tables (other
688
+ providers, MCP servers, sandbox policy, etc.) untouched.
689
+
690
+ ## Sources consulted for the Codex config
691
+
692
+ - https://developers.openai.com/codex/config-reference
693
+ - https://developers.openai.com/codex/config-advanced
694
+ - https://developers.openai.com/codex/auth
695
+ - https://developers.openai.com/codex/ide