impel-cli 0.16.4 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,1031 +1,310 @@
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
- ## Gateway-only vendor package
29
-
30
- White-labelled gateway launchers import the deliberately narrow
31
- `impel-cli/gateway` subpath. It exports only `createGatewayCli`; the resulting
32
- CLI accepts `setup`, `auth`, `claude`, `codex`, `status`, `token`, help, and
33
- version. Tasks, tenants, PAT minting, apps, agents, skills, MCP, updates, and
34
- other Impel control-plane commands are not part of this package surface.
35
-
36
- ```js
37
- import { createGatewayCli } from "impel-cli/gateway";
38
-
39
- const cli = createGatewayCli({ brand, entrypoint, version });
40
- const exitCode = await cli.main(process.argv.slice(2));
41
- if (exitCode) process.exitCode = exitCode;
42
- ```
1
+ # impel-cli
2
+
3
+ `impel-cli` prepares isolated Claude and Codex workspaces for every Impel
4
+ tenant you can access. It keeps tenant histories, settings, credentials, and
5
+ desktop app state separate from one another and from your personal Claude and
6
+ Codex profiles.
43
7
 
44
- The caller supplies a validated branding document, its own executable path,
45
- and its package version. Gateway profile behavior and the command allowlist
46
- remain in `impel-cli`, so vendor CLIs do not copy or fork those implementations.
8
+ The normal lifecycle has two commands:
47
9
 
48
- ## Start here
10
+ ```sh
11
+ impel setup
12
+ impel update
13
+ ```
49
14
 
50
- You do not need GitHub access to install Impel. You need an Impel account and a
51
- computer with Node.js installed. Node.js includes the small installer tool
52
- called `npm` that installs Impel for you.
15
+ ## Install
53
16
 
54
- ### 1. Install Node.js and npm
17
+ Install the public npm package once per machine:
55
18
 
56
- If `node --version` and `npm --version` already print version numbers, skip to
57
- the next step.
19
+ ```sh
20
+ npm install --global impel-cli
21
+ ```
58
22
 
59
- - **Mac or Windows:** visit [nodejs.org/download](https://nodejs.org/en/download),
60
- choose the **LTS** installer, and accept the default installer options.
61
- - **Linux:** follow Node's [official package-manager instructions](https://nodejs.org/en/download/package-manager)
62
- for your distribution.
23
+ Node.js 18 or newer is required.
63
24
 
64
- After installing Node.js, close and reopen Terminal (Mac/Linux) or PowerShell
65
- (Windows), then check that it worked:
25
+ Then run setup and paste an Impel Personal Access Token when prompted:
66
26
 
67
27
  ```sh
68
- node --version
69
- npm --version
28
+ impel setup
70
29
  ```
71
30
 
72
- Both commands should print a version number. Impel needs Node.js 18 or newer.
31
+ Setup discovers every tenant available to the PAT and prepares all supported
32
+ surfaces for each tenant. You do not need to repeat setup tenant by tenant.
73
33
 
74
- ### 2. Install Impel
34
+ ## What setup does
75
35
 
76
- In Terminal or PowerShell, run:
36
+ `impel setup`:
37
+
38
+ 1. Stores the PAT in the private Impel config file.
39
+ 2. Fetches the complete live tenant list.
40
+ 3. Preserves the current CLI tenant when it is still accessible, or selects the
41
+ control-plane default.
42
+ 4. Creates isolated Claude and Codex CLI profiles for every supported tenant.
43
+ 5. Installs or repairs supported tenant desktop apps on macOS and Windows.
44
+ 6. Registers tenant apps with Finder/Spotlight or Windows Start/Search.
45
+ 7. Verifies every supported tenant surface and prints a tenant-sorted summary.
46
+
47
+ Use `--tenant` only to choose the default for CLI launches. It does not limit
48
+ which tenants setup prepares:
77
49
 
78
50
  ```sh
79
- npm install --global impel-cli
51
+ impel setup --tenant acme
52
+ ```
53
+
54
+ Useful setup options:
55
+
56
+ ```sh
57
+ impel setup --pat <pat> # non-interactive credential input
58
+ impel setup --skip-apps # prepare CLI profiles only
59
+ impel setup --skip-clis # Windows: do not install missing vendor CLIs
60
+ impel setup --no-recovery # disable local and assisted recovery for this run
80
61
  ```
81
62
 
82
- This downloads the public Impel command-line app. It does **not** give anyone
83
- access to your Impel account, workspace, or data.
63
+ Putting a PAT on a command line can leave it in shell history. Prefer the
64
+ interactive prompt for ordinary use.
84
65
 
85
- ### 3. Connect your Impel account
66
+ ## Open apps
86
67
 
87
- 1. Open [Impel gateway settings](https://www.useimpel.com/settings/gateway).
88
- 2. Create a Personal Access Token and copy it somewhere safe.
89
- 3. Back in Terminal or PowerShell, run:
68
+ After setup, launch the tenant-specific apps through the operating system:
90
69
 
91
- ```sh
92
- impel setup
93
- ```
70
+ - macOS: Finder, Spotlight, or `~/Applications`
71
+ - Windows: Start or Search under the Impel program group
94
72
 
95
- 4. Paste the token when asked. The input is hidden, and Impel stores it only in
96
- your local account configuration.
97
- 5. Choose your organization when prompted and let the setup finish.
73
+ Names include the tenant, such as `Impel Claude (Acme)` and
74
+ `Impel ChatGPT (Acme)`. The selected CLI tenant does not affect which tenant an
75
+ app opens. Tenant variants can remain installed side by side.
98
76
 
99
- Your token is the key to your account. Never paste it into a chat, ticket, or
100
- shared document.
77
+ Linux does not have managed desktop apps. Use the isolated CLI commands there.
101
78
 
102
- ### 4. Start working
79
+ ## Use the CLI
80
+
81
+ Select the tenant used by CLI launches:
103
82
 
104
83
  ```sh
105
- impel claude # open an isolated Impel Claude Code session
106
- impel codex # open an isolated Impel Codex session
107
- impel status # check your connection and configuration
108
- impel update # install the latest Impel CLI later
84
+ impel tenant list
85
+ impel tenant current
86
+ impel tenant use acme
109
87
  ```
110
88
 
111
- `impel setup` keeps your normal Claude and Codex profiles separate. On Mac and
112
- Windows it also prepares the supported isolated desktop experiences. On Windows,
113
- it installs missing official vendor command-line tools with the vendors'
114
- checksum-verifying native installers, so Claude Code does not inherit its newer
115
- Node.js requirement from the deprecated npm package. On Linux, it prepares the
116
- isolated command-line workflow.
117
-
118
- If setup or the CLI self-update fails, Impel first runs deterministic local
119
- repairs for known failure fingerprints — offline, with no upload. If the
120
- failure persists, an interactive terminal offers assisted recovery (use
121
- `impel setup --repair` or `impel update --repair` to opt in explicitly): a
122
- bounded local loop where a gateway-hosted model selects typed diagnostic and
123
- repair tools and the CLI validates, approves, and executes them on this
124
- machine. Before the first upload, the CLI prints the exact sanitized payload.
125
- Free-form model commands are impossible by construction; repairs limited to
126
- Impel-owned files need one approval per session, while vendor installers and
127
- PATH edits always confirm individually. Recovery only reports success after
128
- the failed steps re-pass their local health checks, and it stops after 12
129
- model turns or 10 minutes. Use `--no-recovery` or
130
- `IMPEL_DISABLE_INSTALL_RECOVERY=1` to keep recovery off.
131
-
132
- ### If something does not work
133
-
134
- - **`npm` or `node` is not recognized:** install Node.js from the link above,
135
- then close and reopen Terminal or PowerShell.
136
- - **`impel` is not recognized:** close and reopen the terminal, then run
137
- `npm install --global impel-cli` again.
138
- - **Your token is rejected:** create a fresh token in Impel gateway settings and
139
- run `impel setup` again.
140
- - **You need a different organization:** run `impel tenant list`, then
141
- `impel tenant use <organization>`.
142
-
143
- The npm package is public so Impel users do not need access to the private
144
- GitHub repository. The package contains no account credentials or service
145
- secrets; the Impel gateway and control plane still enforce your token, product
146
- access, scopes, and organization membership for every protected action.
147
-
148
- ## Command reference
89
+ Then launch either client:
149
90
 
91
+ ```sh
92
+ impel claude
93
+ impel codex
150
94
  ```
151
- impel setup [--pat <pat>] [--tenant <org>] [--skip-apps] [--skip-clis]
152
- [--repair|--no-recovery]
153
- Guided setup: token, tenant, platform clients, verify
154
- (closes running Claude/ChatGPT apps, updates the vendor
155
- apps, then installs the vendored Impel apps on macOS;
156
- runs official native vendor CLI installers on Windows)
157
- impel update [--check] [--skip-apps] [--repair|--no-recovery]
158
- Update EVERYTHING: reinstall the CLI from npm, then
159
- cascade to `app update all`, `skills sync all`, and
160
- `agents sync all`
161
- impel auth [--pat <pat>] [--gateway <url>] [--app <url>]
162
- Store your Impel PAT + URLs
163
- impel pat create --label <label> [options] Mint a personal PAT through the control plane
164
- impel pat create --agent <agent-id> [options]
165
- Mint a PAT associated with an org agent
166
- impel pat revoke <token-id> --yes Irreversibly revoke one of your PATs
167
- impel token [--tenant <org>] Print a selected-tenant bearer
168
- impel mcp Run the local MCP bridge used by Claude/Codex
169
- impel claude [claude args...] Launch isolated Impel Claude Code
170
- impel codex [codex args...] Launch isolated Impel Codex
171
-
172
- impel tasks list [--org <org>] List tickets
173
- impel tasks get <id> Read one ticket
174
- impel tasks create --title <title> [...] Create a ticket
175
- impel tasks update <id> [...] Update a ticket
176
- impel tasks delete <id> --yes Delete a ticket
177
-
178
- impel tenant list List available organizations
179
- impel tenant current Print the selected organization
180
- impel tenant use <org> [--launch <target>] Select a tenant; optionally launch claude, codex, or apps
181
- impel doctor [--tenant <org>|--all-tenants] Run synthetic provider, routing, and latency checks
182
-
183
- impel use gateway [claude|codex|all] Switch to the Impel gateway (default: all)
184
- impel on [claude|codex|all] Alias for `impel use gateway`
185
- impel use account [claude|codex|all] Revert to your own login (default: all)
186
- impel off [claude|codex|all] Alias for `impel use account`
187
-
188
- impel skills sync [claude|codex|all] Sync Bifrost shared skills into managed clients (default: all)
189
- impel agents sync [claude|codex|all] Sync explicit tenant agents into native clients
190
-
191
- impel status Launcher readiness + native mode + gateway reachability
192
- impel app install [target] [--tenant <org>] Install isolated apps for one tenant
193
- impel app update [target] [--tenant <org>] [--force]
194
- `all` without --tenant updates every installed tenant;
195
- --force performs a clean full rebuild
196
- impel app refresh [target] [--tenant <org>] Configs/catalog/skills/agents only; safe while apps run
197
- (--stale-only: no-op unless 6h+ since last sync)
198
- impel app status [target] [--tenant <org>] Show tenant launcher/vendor status
199
- impel app open [target] [--tenant <org>] Install/configure a tenant on first use, then launch
200
- it (later opens are immediate when nothing changed;
201
- a detached stale-only refresh keeps things current)
202
- impel app uninstall [target] [--tenant <org>] [--keep-data]
203
- Remove one tenant's Impel-managed app files
204
- impel help Show help
205
- impel --version Show version
206
-
207
- For app commands, `target` is `claude`, `chatgpt`/`codex`, or `all` (the
208
- default). `impel app update all` without `--tenant` updates every locally
209
- installed tenant. Other app commands, a specific update target, or an explicit
210
- `--tenant` operate on one organization and default to the current selection.
211
-
212
- # `impel setup` no longer takes a per-tool target and never flips native
213
- # configs; flip a native tool explicitly with `impel use gateway [claude|codex]`.
95
+
96
+ Arguments after the command are passed to the vendor CLI:
97
+
98
+ ```sh
99
+ impel claude --model opus
100
+ impel codex exec "review this repository"
214
101
  ```
215
102
 
216
- ### `impel auth [--pat <pat>] [--gateway <url>] [--app <url>]`
103
+ Changing the selected tenant does not install, remove, or rewrite any other
104
+ tenant. It changes only the default used by CLI launches and selected-tenant
105
+ workspace commands.
217
106
 
218
- Stores your PAT, gateway base URL, and app/control-plane URL in `~/.config/impel/config.json`
219
- (created with mode `0600`, directory `0700`).
107
+ ## Update
220
108
 
221
- - If `--pat` isn't given, you're prompted for it (input is masked).
222
- - If `--gateway` isn't given, it falls back to (in order): the gateway URL you
223
- already had stored, the `IMPEL_GATEWAY_URL` environment variable, then the
224
- built-in default **`https://gateway.useimpel.com`**.
225
- - If `--app` isn't given, it falls back to (in order): the app URL you already
226
- had stored, the `IMPEL_APP_URL` environment variable, then the built-in
227
- default **`https://www.useimpel.com`**.
228
- - Trailing slashes on the gateway URL are stripped automatically.
109
+ Run one command for routine maintenance:
229
110
 
230
111
  ```sh
231
- impel auth --pat impel_pat_xxxxxxxxxxxx --gateway https://gateway.useimpel.com --app https://www.useimpel.com
112
+ impel update
232
113
  ```
233
114
 
234
- ### `impel pat create ...` / `impel pat revoke ...`
115
+ Update:
235
116
 
236
- Mints a new PAT through the app/control-plane API, authenticated by the PAT
237
- already stored by `impel auth`. Identity remains the minting authority: the
238
- control plane verifies the current owner, target-org membership, requested
239
- scopes, product access, and (when applicable) agent access before it requests a
240
- new secret. The gateway never mints or verifies PATs.
117
+ 1. Checks and installs the latest `impel-cli` package from npm.
118
+ 2. Continues under the freshly installed build.
119
+ 3. Fetches the current live tenant list.
120
+ 4. Fully prepares tenants added since the previous run.
121
+ 5. Repairs and upgrades existing tenants in place.
122
+ 6. Re-registers operating-system launch entries.
123
+ 7. Reports local tenants that are no longer accessible without deleting them.
241
124
 
242
- Use `--label` for a personal token or `--agent` for a token associated with an
243
- org agent. Agent PATs are still personal credentials: they act as you, are
244
- org-scoped, and use the same stable `Agent · <agent-id>` association as the agent
245
- setup UI.
125
+ An update preserves histories, browser state, preferences, permissions, stable
126
+ app identity, and user-authored settings outside Impel-managed blocks. Desktop
127
+ bundles are rebuilt only when missing, corrupt, or stale. An app is closed only
128
+ when its bundle must be replaced, and only an app closed by the update is
129
+ reopened afterward.
130
+
131
+ Useful update options:
246
132
 
247
133
  ```sh
248
- impel pat create --label "Rahul's laptop"
249
- impel pat create --agent support-agent --org acme
250
- impel pat create --label ci-automation --scopes codex-gateway,tasks --ttl-days 30 --json
134
+ impel update --check # inspect npm availability without changing state
135
+ impel update --skip-apps # reconcile CLI profiles only
136
+ impel update --no-recovery # disable recovery for this run
251
137
  ```
252
138
 
253
- `--org` defaults to the selected tenant (`--tenant` is an alias). `--scopes`
254
- accepts `claude-code-gateway`, `codex-gateway`, and `tasks`; when omitted, the
255
- control plane chooses the defaults allowed by your access tier. `--ttl-days`
256
- accepts 1–365 and defaults to 90 days.
257
-
258
- The new secret is returned once on stdout and is deliberately **not** written to
259
- the config file. To rotate this CLI onto it, explicitly run
260
- `impel auth --pat <new-pat>` after copying it. `--json` includes the raw secret,
261
- so treat redirected output as a credential.
139
+ "Latest" means the latest npm CLI plus the exact vendor builds and profile
140
+ schema verified by that CLI. It does not mean cloning an untested moving vendor
141
+ release.
262
142
 
263
- Revoke a token by the token ID printed when it was created:
143
+ ## Status and diagnosis
264
144
 
265
145
  ```sh
266
- impel pat revoke personal1 --yes
267
- impel pat revoke personal1 --yes --json
146
+ impel status
147
+ impel doctor
148
+ impel doctor --all-tenants
268
149
  ```
269
150
 
270
- Revocation is immediate and irreversible, so `--yes` is required. A full PAT is
271
- also accepted for recovery from older mint output, but using the token ID avoids
272
- putting a secret in shell history. The CLI extracts the ID without echoing the
273
- secret, authenticates with the currently stored PAT, and leaves the local config
274
- unchanged. If you revoke the stored PAT itself, authenticate again with a live PAT
275
- before running another protected command.
276
-
277
- ### `impel update`
278
-
279
- One command brings everything current, in dependency order: the CLI itself
280
- (`npm install -g impel-cli@latest` from npm, with no GitHub credentials),
281
- then — re-executing the freshly installed build — `impel app update all`
282
- (update every locally installed tenant and rebuild only stale vendored Impel apps)
283
- followed by `impel skills sync all` and `impel agents sync all` across every
284
- native and isolated CLI profile. Each tenant's app step syncs that tenant's app
285
- profiles; the dedicated steps exclude apps, so every profile is synced once.
286
-
287
- Most updates are in place — the desktop apps are **not** closed. A vendored
288
- bundle is only rebuilt (and therefore only quit) when the bundle-building logic
289
- or the pinned vendor version actually changes, not on every CLI release, so a
290
- routine update just rewrites configs, skills, and agents while the apps keep
291
- running. When a rebuild genuinely is required, `impel app update` closes only
292
- the app whose bundle it will swap and **reopens it afterward**, so the swap
293
- lands without you relaunching by hand. Apps you had already closed stay closed.
294
-
295
- Update discovery compares the installed package version with npm's public
296
- `latest` metadata, cached for 6 hours in
297
- `~/.config/impel/update-check.json`. It does not invoke Git or contact GitHub.
298
-
299
- You'll also hear about updates without asking: `impel claude`, `impel codex`,
300
- `impel status`, and `impel app open` print a one-line notice when the cached
301
- remote is ahead, and refresh the cache in a detached background process when
302
- it's stale (TTY-only, never blocks a launch; set `IMPEL_SKIP_UPDATE_CHECK=1`
303
- to silence). The Impel desktop apps keep themselves current too: their token
304
- helper kicks off a detached `impel app refresh --stale-only` on use, which
305
- re-syncs configs, the model catalog, skills, and native agents at most every six
306
- hours — safe while the apps are running because it never swaps an app bundle.
307
-
308
- ### `impel tasks ...` (aliases `impel task`, `impel tickets`, `impel ticket`)
309
-
310
- CRUDs Impel tickets through the app/control-plane API using the stored PAT. New
311
- PATs need the `tasks` scope and the user must be a **Workspace member**. Gateway
312
- members intentionally cannot access tasks or other workspace features, even if
313
- an older PAT still contains the scope.
151
+ `impel status` is read-only. It shows authentication, the current CLI tenant,
152
+ and every tenant's local Claude, Codex, and desktop readiness. Its repair path
153
+ is `impel update`.
154
+
155
+ `impel doctor` sends explicit synthetic provider checks. Those requests may be
156
+ billable, so doctor never runs implicitly.
157
+
158
+ ## Workspace tasks
159
+
160
+ Workspace members with a PAT carrying the `tasks` scope can use Impel tickets:
314
161
 
315
162
  ```sh
316
163
  impel tasks list --scope visible
317
164
  impel tasks get IMP-123
318
- impel tasks create --title "Fix flaky login test" --priority high --labels bug,engineering
319
- impel tasks update IMP-123 --progress review --assignee rahulvbrahmal@gmail.com
320
- impel tasks update IMP-123 --description-file brief.md --description-mode replace
165
+ impel tasks create --title "Harden setup verification"
166
+ impel tasks update IMP-123 --progress review
321
167
  impel tasks delete IMP-123 --yes
322
168
  ```
323
169
 
324
- Common options: `--org <org>` targets a specific org (default: the PAT's org),
325
- `--json` prints the raw API response, and `--app <url>` overrides the app URL for
326
- one command.
327
-
328
- ### Tenant selection
329
-
330
- `impel auth` discovers every organization the PAT owner currently belongs to.
331
- The default is `impel` when available, otherwise the first organization slug in
332
- deterministic order. Change it with `impel tenant use <org>`.
333
-
334
- The selected tenant controls all gateway-backed surfaces together: subscription
335
- accounts, usage attribution, model catalog, and specialist discovery. The
336
- gateway re-verifies both the underlying PAT and current organization membership;
337
- editing the local config cannot grant access to another tenant.
338
-
339
- Isolated CLI state is kept separately under
340
- `~/.config/impel/cli/tenants/<org>/`, so Claude/Codex history and MCP state do
341
- not bleed between tenants. `impel tenant use <org> --launch claude|codex|apps`
342
- selects and launches in one command.
343
-
344
- On macOS, desktop app state is isolated under
345
- `~/.config/impel/apps/tenants/<org>/`. Every installed tenant keeps its own app
346
- bundles in `~/Applications`, with names such as `Impel Claude (Acme)` and
347
- `Impel ChatGPT (Acme)`, stable tenant-specific bundle identifiers, and fixed
348
- tenant profile/auth paths. Different tenants can stay open concurrently.
349
- `impel app open --tenant <org>` installs and configures a missing tenant variant
350
- on first use, then launches it directly without changing the globally selected
351
- tenant. Later opens use the existing isolated app and profile. Switching tenants
352
- never deletes local history or projects; each tenant's preserved profile reopens
353
- independently. The first-use setup still requires an authenticated user PAT and
354
- live membership in the requested organization; it does not create a PAT per
355
- tenant.
356
- Windows Claude Desktop keeps each
357
- tenant under `%LOCALAPPDATA%\Claude-3p\Impel\<org>` and launches the unchanged
358
- signed vendor executable with that profile selected. Windows ChatGPT keeps its
359
- Codex and browser state under the same tenant-scoped Impel apps root used on
360
- macOS and launches a staged copy of the unchanged signed Store payload.
361
-
362
- ### Product access
363
-
364
- Impel has two product access levels, independent of organization membership and
365
- provider-pool administration:
366
-
367
- - **Workspace member** — the complete Impel workspace plus the gateway,
368
- tenant-scoped inference, and specialist agents.
369
- - **Gateway member** — the gateway setup/configuration surface, tenant selector,
370
- inference, and specialist agents only. Tasks, workspace rooms, notifications,
371
- and other collaboration features are unavailable.
372
-
373
- The control plane resolves this entitlement live. The CLI caches it only for
374
- display; the control plane and gateway enforce it again on every protected
375
- operation. Changing the selected tenant never changes the user's product access.
376
- Provider launches and `impel doctor` also preflight the PAT's live
377
- `claude-code-gateway` / `codex-gateway` scopes, so an older single-provider PAT
378
- gets a direct rotation instruction instead of a misleading model-capacity error.
379
-
380
- ### `impel doctor`
381
-
382
- Runs a real streaming request through each selected provider, so it consumes a
383
- small amount of provider capacity. Each request contains only a generated
384
- synthetic marker and asks the model to acknowledge that marker exactly. The
385
- report verifies:
386
-
387
- - live PAT, entitlement, and tenant membership resolution,
388
- - the tenant-scoped model catalog,
389
- - a selected subscription account on the response,
390
- - the gateway-owned request ID and echoed client correlation ID,
391
- - HTTP, bounded SSE parsing, provider response ID, and successful terminal event,
392
- - exact request/response acknowledgement, and
393
- - headers, first-byte, time-to-first-token (TTFT), and total latency.
394
-
395
- It never prints the PAT or any real prompt content. The default TTFT warning
396
- budgets are 6 seconds for Claude and 5 seconds for Codex. A budget miss is shown
397
- as a warning; add `--strict-latency` to make it fail the command. Connectivity,
398
- routing, account selection, or acknowledgement errors always produce a non-zero
399
- exit code.
400
-
401
- Organizations may intentionally have only one provider pool. When the live
402
- catalog reports an exact `no_seat` state for a requested provider, the doctor
403
- reports that provider as skipped instead of manufacturing a failed request. The
404
- tenant can pass when another requested provider completes successfully. Expired
405
- credentials, exhausted capacity, rate limits, ambiguous catalog responses, and
406
- tenants with no usable requested provider still fail closed.
407
-
408
- Use `--gateway <origin>` for a one-run data-plane override. Doctor resolves the
409
- gateway in this order: `--gateway`, `IMPEL_GATEWAY_URL`, the saved gateway, then
410
- the canonical default. The override must be a bare HTTP or HTTPS origin and is
411
- never written back to the stored authentication config.
170
+ Use `--json` when exact machine-readable fields matter. Gateway-only members
171
+ cannot use task CRUD.
172
+
173
+ ## PAT lifecycle
174
+
175
+ Minting and revocation remain explicit account operations:
412
176
 
413
177
  ```sh
414
- impel doctor
415
- impel doctor --attempts 2 --strict-latency
416
- impel doctor --all-tenants --attempts 2 --json > readiness.json
417
- impel doctor --tenant creador --providers claude
418
- impel doctor --gateway https://gateway.useimpel.com
178
+ impel pat create --label laptop
179
+ impel pat create --agent <agent-id> --org acme
180
+ impel pat revoke <token-id> --yes
419
181
  ```
420
182
 
421
- `--attempts` is capped at five per provider and a single invocation is capped at
422
- 50 total probes to prevent an accidental high-volume run. JSON output is one
423
- sanitized report suitable for CI or a rollout checklist.
424
-
425
- ### `impel token`
426
-
427
- Prints **only** a bearer derived from the stored PAT and selected tenant to
428
- stdout, nothing else. Non-zero exit and a
429
- stderr message if you haven't run `impel auth` yet.
430
-
431
- This is the contract both Claude Code's `apiKeyHelper` and Codex's
432
- command-backed provider auth expect: a command that is invoked on demand and
433
- whose stdout is the bearer token. Managed Codex auth and MCP entries call the
434
- current Node executable plus Impel entry point directly, avoiding npm `.cmd`
435
- resolution on Windows. Rotating your PAT is still one command
436
- (`impel auth --pat ...`) and every tool picks it up on its next call/refresh.
437
-
438
- ### Specialist delegation over MCP
439
-
440
- Gateway mode also installs an `impel` stdio MCP server in Claude Code and
441
- Codex, including the isolated Impel ChatGPT profile. The server runs the Impel
442
- MCP entry point, reads the PAT from the owner-only Impel
443
- config, and proxies JSON-RPC to `<gateway>/mcp`; the PAT is never written into
444
- either client's MCP configuration. Available specialists are discovered at
445
- runtime, so publishing a new opted-in specialist does not require another CLI
446
- release. The same authenticated catalog is rendered into native Claude Code
447
- and Codex agent definitions. Each definition carries only the direct
448
- `impel mcp --tenant <org>` subprocess invocation, never the PAT itself.
449
-
450
- ### Isolated CLI launchers
451
-
452
- `impel claude [args...]` and `impel codex [args...]` launch the installed vendor
453
- CLIs with every argument, the current working directory, stdin/stdout/stderr,
454
- and the vendor exit code passed through unchanged. They require `impel auth`
455
- and load gateway authentication plus the Impel MCP server from private,
456
- Impel-only profiles:
457
-
458
- - Claude: `~/.config/impel/cli/tenants/<org>/claude` via `CLAUDE_CONFIG_DIR`
459
- - Codex: `~/.config/impel/cli/tenants/<org>/codex` via `CODEX_HOME`
460
-
461
- The normal `claude` and `codex` commands still use `~/.claude`, `~/.claude.json`,
462
- and `~/.codex`. The launchers do not read, copy, modify, or remove those native
463
- profiles, their credentials, or their conversation history. Project-level
464
- files and the working directory remain available exactly as they are in the
465
- normal CLIs.
466
-
467
- The isolated launchers also add an Impel-only instruction that checks the live
468
- tenant specialist catalog before non-trivial work. When one available
469
- specialist clearly matches, the client starts one idempotent specialist run,
470
- waits for it to finish, and uses its result instead of repeating the task. It
471
- continues normally when no specialist matches or delegation is unavailable.
472
- Claude receives this through `--append-system-prompt`; Codex receives it through
473
- the invocation-local `developer_instructions` override. Neither instruction is
474
- written to or loaded by the native `claude` or `codex` commands.
475
-
476
- The isolated Codex launcher uses Codex's embedded code-mode runtime so MCP tools
477
- keep working when a vendor install does not include the optional standalone
478
- `codex-code-mode-host` companion. Only the four specialist-control tools are
479
- pre-approved for non-interactive delegation; other MCP tools retain Codex's
480
- normal approval behavior. Its Codex-only instruction also resolves exact Impel
481
- specialist tools through `tool_search` when the client defers MCP tools instead
482
- of showing them directly. These settings apply only to `impel codex`.
483
-
484
- The isolated Codex profile disables Codex shell snapshots and explicitly keeps
485
- the default `KEY`/`SECRET`/`TOKEN` environment filter enabled. Before every
486
- launch, `impel` removes derived `shell_snapshots`, drops inherited direct-auth
487
- variables such as `CODEX_ACCESS_TOKEN`, and enforces owner-only permissions on
488
- the managed profile and credential/config files. It also sweeps inactive tenant
489
- and pre-tenancy Impel CLI profiles, so switching organizations cannot strand an
490
- older snapshot. These safeguards apply only to Impel-owned `CODEX_HOME`
491
- directories; native `~/.codex` remains untouched.
492
-
493
- On Windows, `impel setup` installs only missing command-line tools through the
494
- vendors' current native PowerShell installers. Claude Code's npm installation is
495
- deprecated and its current package requires Node.js 22, while Impel itself still
496
- supports Node.js 18 or newer. The native path keeps that valid Impel setup
497
- working. Codex's installer is run non-interactively. Both installers download
498
- platform-specific native releases and verify their published SHA-256 checksums.
499
- Use `--skip-clis` to prepare the profiles without installing missing vendor
500
- CLIs. Each tool is installed independently, so a failure from one vendor does
501
- not prevent the other tool from being installed and configured.
502
-
503
- The launchers and skill sync still resolve executables through `PATH`/`PATHEXT`,
504
- including existing global npm `.cmd` and `.bat` shims. Windows environment
505
- references such as `%NVM_SYMLINK%` are expanded during lookup, and the standard
506
- npm-global, Claude native-installer, Codex standalone-installer, and Windows
507
- PowerShell directories are checked even when the parent PowerShell PATH is
508
- stale. Candidates must also pass a bounded `--version` probe, so a stale or
509
- half-removed npm shim cannot hide a healthy native install later in the search
510
- path. Existing batch-shim arguments are forwarded through an explicitly
511
- escaped `cmd.exe` invocation without Node shell mode; NULs and line breaks,
512
- which cannot be represented safely through a batch shim, are rejected before
513
- anything is launched. Managed Codex auth and MCP subprocesses bypass batch
514
- shims entirely by calling Node directly.
515
-
516
- If a native installer fails, the wizard identifies the affected tool, preserves
517
- the other tool's successful install, and prints that vendor's exact official
518
- PowerShell command to retry. If an installer exits successfully but its command
519
- cannot be verified, close and reopen PowerShell, retry the printed command, and
520
- run `impel setup` again. Setup exits non-zero and says it is incomplete until
521
- both launchers are ready—it does not print a success banner after a failed
522
- installation.
523
-
524
- Windows receives the complete CLI workflow: setup/auth, tenant selection,
525
- isolated Claude and Codex launches, native profile switching, tasks, skills,
526
- status, doctor, self-update, and both isolated desktop apps. `impel app install
527
- claude` installs/updates Anthropic's signed per-user app through winget, writes
528
- the selected tenant's 3P gateway configuration beneath Anthropic's reserved
529
- `%LOCALAPPDATA%\Claude-3p\Impel\<tenant>` root, and leaves the executable and
530
- normal Claude profile unchanged. `impel app install codex` installs/updates
531
- OpenAI's `OpenAI.Codex` Store package, stages its unchanged signed app payload
532
- inside Impel's shared app cache, and writes separate Codex and Chromium
533
- profiles. `impel app open claude` and `impel app open codex` install a missing
534
- official app and configure the selected tenant on first use, then launch the
535
- isolated profile without changing either normal vendor profile.
536
-
537
- For Claude, the Impel PAT is supplied only to the launched child process as an
538
- authentication token and disappears when that process exits; it is never
539
- written into the isolated Claude profile. The launcher also recognizes
540
- Claude's standard native-installer location at `~/.local/bin/claude` when that
541
- directory is not present in the parent process's `PATH`.
183
+ A newly minted PAT is shown once. It is not silently made active. Run
184
+ `impel setup` and provide the new PAT when rotating this machine. Revocation is
185
+ immediate and irreversible.
186
+
187
+ `impel auth --pat <pat>` remains available for existing automation that only
188
+ needs to store a credential. It does not provision tenants. Interactive users
189
+ should prefer setup because setup also reconciles the complete tenant list.
190
+
191
+ ## Recovery
192
+
193
+ Setup and update include bounded installation recovery by default:
194
+
195
+ 1. Known deterministic repairs run locally first.
196
+ 2. If verification still fails, the CLI prints the exact sanitized diagnostic
197
+ envelope before assisted diagnosis.
198
+ 3. The recovery model can select only strict, typed tools. It cannot run shell
199
+ text.
200
+ 4. Read operations and repairs limited to Impel-owned state run automatically.
201
+ 5. Vendor installers and user PATH edits require individual interactive
202
+ confirmation and are declined without a TTY.
203
+ 6. A recovery is accepted as fixed only after fresh local health checks pass.
204
+
205
+ Tenant failures are recovered serially with a tool context and checkpoint
206
+ bound to the exact tenant. PATs, bearer tokens, home paths, emails, and terminal
207
+ control sequences are removed before any diagnostic request.
208
+
209
+ Disable all recovery for one run with `--no-recovery`, or globally for a
210
+ process with:
542
211
 
543
212
  ```sh
544
- impel claude
545
- impel claude -p "summarize this repository" --output-format json
546
- impel codex
547
- impel codex exec --json "review the current diff"
213
+ IMPEL_DISABLE_INSTALL_RECOVERY=1 impel update
548
214
  ```
549
215
 
550
- `impel use gateway` remains available for users who intentionally want to
551
- switch their native profiles in place; the isolated launchers are the safer
552
- default when personal and Impel sessions should coexist.
553
-
554
- Every Impel-managed Claude configuration uses the same strict sandbox defaults
555
- with unrestricted outbound network access:
556
- the isolated `impel claude` profile, each tenant's Claude Desktop Code profile,
557
- the native profile while `impel use gateway claude` is active, and branded
558
- gateway-only CLI profiles. App config version 15 also pins managed Claude
559
- Desktop profiles to third-party deployment mode, enables the 3P Chat tab so
560
- Code's "Back to Claude" route has a valid destination, and removes the unusable
561
- Claude.ai sign-in path. Existing tenant profiles refresh onto these policies
562
- after the CLI update.
563
-
564
- ### `impel use gateway [claude|codex|all]` (alias `impel on`)
565
-
566
- Turns **on** gateway mode for the given tool (default `all`). Requires `impel auth`
567
- first.
568
-
569
- **Claude Code** — merges into `~/.claude/settings.json` (creating it if needed,
570
- leaving existing permission rules, hooks, other env vars, and custom sandbox
571
- filesystem/network rules intact). Gateway mode enables Claude's strict auto-allow sandbox by
572
- default: Bash runs inside the OS sandbox without routine prompts, the
573
- `dangerouslyDisableSandbox` escape hatch is disabled, and supported platforms
574
- fail closed if isolation cannot initialize. Native Windows keeps startup
575
- available because Claude's sandbox is supported only on macOS, Linux, and WSL2.
576
- A global `WebFetch(domain:*)` allow rule opens every outbound destination for Bash commands
577
- while retaining filesystem isolation. Explicit deny rules remain authoritative.
578
- On macOS, `enableWeakerNetworkIsolation` lets Go-based network tools reach the
579
- system TLS trust service while remaining sandboxed.
580
-
581
- ```json
582
- {
583
- "apiKeyHelper": "impel token",
584
- "env": {
585
- "ANTHROPIC_BASE_URL": "https://gateway.useimpel.com/anthropic"
586
- },
587
- "permissions": {
588
- "allow": ["WebFetch(domain:*)"]
589
- },
590
- "sandbox": {
591
- "enabled": true,
592
- "failIfUnavailable": true,
593
- "autoAllowBashIfSandboxed": true,
594
- "allowUnsandboxedCommands": false,
595
- "excludedCommands": [],
596
- "enableWeakerNetworkIsolation": true
597
- }
598
- }
599
- ```
216
+ See [docs/assisted-install-recovery.md](docs/assisted-install-recovery.md) for
217
+ the security model and tool registry.
600
218
 
601
- **Codex** merges into `~/.codex/config.toml`. Codex supports pointing
602
- `model_provider` at a custom `[model_providers.<id>]` table, plus a
603
- **command-backed** bearer token via a `[model_providers.<id>.auth]` sub-table
604
- (`command` + `args`, re-invoked on a timer and on auth retries — the same shape
605
- as Claude Code's `apiKeyHelper`), so we reuse `impel token` there too instead of
606
- writing the PAT into `config.toml` or `~/.codex/auth.json`. The block written
607
- (fenced by markers, so everything else in your `config.toml` — other
608
- `model_providers`, `mcp_servers`, and unrelated sandbox policy — is left alone).
609
- Codex defaults to the `workspace-write` filesystem sandbox with unrestricted
610
- direct outbound access; its optional network proxy stays disabled:
611
-
612
- ```toml
613
- model_provider = "impel"
614
-
615
- # >>> impel-cli managed block (model_providers.impel) >>>
616
- # Generated by `impel use gateway codex`. Safe to re-run; do not hand-edit
617
- # the lines between the markers above/below, they'll be overwritten.
618
- sandbox_mode = "workspace-write"
619
- sandbox_workspace_write.network_access = true
620
-
621
- [model_providers.impel]
622
- name = "Impel Gateway"
623
- base_url = "https://gateway.useimpel.com/chatgpt_passthrough/backend-api/codex"
624
- wire_api = "responses"
625
-
626
- [model_providers.impel.auth]
627
- command = "impel"
628
- args = ["token"]
629
- timeout_ms = 5000
630
- refresh_interval_ms = 300000
631
- # <<< impel-cli managed block <<<
632
- ```
219
+ ## Personal profiles
633
220
 
634
- Before overwriting anything, `impel` **backs up** what was there (any prior
635
- `apiKeyHelper`, `ANTHROPIC_BASE_URL`, Claude sandbox/permission policy, and the
636
- prior Codex `model_provider`, sandbox mode, and workspace network values) into
637
- `~/.config/impel/config.json`, so `impel use account`
638
- can restore it exactly. If `config.toml` already has a `[model_providers.impel]` table that
639
- wasn't written by this tool, `impel` refuses to touch the file and tells you to
640
- resolve it manually rather than risk emitting invalid/duplicate TOML.
641
-
642
- ### `impel use account [claude|codex|all]` (alias `impel off`)
643
-
644
- Turns gateway mode **off** and restores your normal account-based setup. It
645
- removes **only** the Impel-added bits, never your own config:
646
-
647
- - **Claude Code** — removes `apiKeyHelper` only if it's `"impel token"`, and
648
- `env.ANTHROPIC_BASE_URL` only if it equals `<gateway>/anthropic`. Then restores
649
- whatever those keys held before gateway mode (from the backup). If there was
650
- nothing before, they're deleted (and an emptied `env` is dropped). It also
651
- removes only the Impel-added global `WebFetch(domain:*)` allow rule.
652
- - **Codex** — removes the marker-fenced `[model_providers.impel]` block, and
653
- resets `model_provider` **only if it currently reads `"impel"`** — back to its
654
- backed-up prior value, or, if there was none, removes the line entirely so
655
- Codex falls back to its built-in default. The prior
656
- `sandbox_mode` and `sandbox_workspace_write.network_access` values are
657
- restored. All other tables are untouched.
658
-
659
- `impel use account` doesn't require the PAT and is safe to run repeatedly (if a
660
- tool is already in account mode it just says so and changes nothing).
661
-
662
- ### `impel skills sync [claude|codex|all]`
663
-
664
- Syncs Impel's **Bifrost shared skills** into your managed Claude Code and Codex
665
- clients. The gateway publishes a public, per-client plugin marketplace whose
666
- bundled plugin — `bifrost-all-skills` — contains every served skill:
667
-
668
- - Claude Code registers its manifest URL:
669
- `<gateway>/api/skills/serve/claude-code/.claude-plugin/marketplace.json`
670
- - Codex registers the Git source `<gateway>/api/skills/serve/codex`; its
671
- marketplace manifest lives at `<source>/.agents/plugins/marketplace.json`.
672
-
673
- The sync is idempotent: it registers the marketplace, ensures `bifrost-all-skills`
674
- is installed, then runs the client's refresh commands so an already-installed
675
- plugin picks up a newer bundle. It reads each marketplace's registered `name`
676
- from the served `marketplace.json` to pin installs and refreshes. Codex receives
677
- the Git source rather than the nested manifest URL because its CLI clones the
678
- source before discovering `.agents/plugins/marketplace.json`.
679
-
680
- You rarely need to run it by hand — it runs **automatically** at the end of every
681
- install/update path:
682
-
683
- - `impel use gateway ...` / `impel setup ...` (native `~/.claude` / `~/.codex`)
684
- - `impel app install` / `impel app update` (each isolated desktop app profile)
685
-
686
- `impel skills sync` also covers the isolated `impel claude` / `impel codex` CLI
687
- profiles and installed desktop apps when they exist. Skill syncing is
688
- best-effort: if a client CLI, its `plugin` subcommand, or the gateway is
689
- unavailable, it logs a warning and moves on — it never fails the parent command.
690
- Set `IMPEL_SKIP_SKILL_SYNC=1` to disable it entirely (offline/CI).
691
-
692
- ### `impel agents sync [claude|codex|all]`
693
-
694
- Fetches the selected organization's explicit native-agent catalog through the
695
- authenticated Impel MCP endpoint, then generates native custom-agent files for
696
- every managed profile. This catalog is separate from automatic specialists: it
697
- may include truthfully declared write-capable agents, but those agents require
698
- explicit user selection before the server will start a run.
699
-
700
- - Claude Code / Claude Desktop: `<CLAUDE_CONFIG_DIR>/agents/<agent-name>.md`
701
- - Codex / ChatGPT Codex mode: `<CODEX_HOME>/agents/impel-<tenant>-<agent-id>.toml`
702
-
703
- Each generated agent is bound to one exact `agentId` and
704
- `scopeParam`. It verifies that the exact agent is still available, starts one
705
- idempotent run, polls the durable run to a terminal state, and returns
706
- `result.finalText` without repeating the work. The files include a tenant-bound
707
- MCP subprocess definition so they also work in isolated desktop profiles. They
708
- never contain the PAT; the subprocess reads it from Impel's owner-only config
709
- when invoked.
710
-
711
- Visible agent names come from the registry title. Codex keeps that title as the
712
- custom-agent name; Claude uses the lowercase, hyphenated form required by its
713
- native agent schema and Desktop's `@` picker requires the Markdown filename to
714
- match that name. Codex filenames remain stable tenant-and-agent identifiers.
715
- Duplicate or built-in-reserved titles receive a deterministic disambiguator.
716
-
717
- The sync writes atomically and records a manifest beside the generated files.
718
- It removes only stale filenames from that manifest, so user-authored agents are
719
- never deleted. Automatic refreshes are best-effort and retain the last good
720
- catalog when the gateway is unavailable; the explicit command exits non-zero
721
- on failure. Set `IMPEL_SKIP_AGENT_SYNC=1` to keep the current generated catalog
722
- without contacting the gateway.
723
-
724
- Start a new task after syncing. Claude Code detects updates to an existing
725
- `agents/` directory within a few seconds, but if the sync created that directory
726
- for the first time while Claude was already running, restart Claude once.
727
- In Claude Desktop, selecting an agent on the first turn binds that Code session
728
- to the agent. Follow-up `@` menus continue to offer that same bound agent and
729
- insert a normal mention chip. Other agents remain available from a new task,
730
- because the running Claude session cannot safely switch agents mid-session.
731
-
732
- Invocation uses the clients' native agent behavior:
221
+ Impel sessions no longer route native personal `~/.claude` or `~/.codex`
222
+ profiles through Impel. Setup and update use tenant-isolated profiles only.
733
223
 
734
- ```text
735
- # Claude Code / Claude Desktop Code tab: guaranteed explicit selection
736
- @research-agent investigate the dependency change
224
+ For users of an older release, the first setup or update performs a one-time
225
+ cleanup migration. It removes only exact Impel-owned values and marker blocks,
226
+ restores stored prior values when available, and preserves user-edited or
227
+ unrelated settings. The old account-restore aliases remain as hidden cleanup
228
+ compatibility for one release, but no command can enable native routing.
737
229
 
738
- # Claude blocking CLI
739
- claude --agent research-agent -p "investigate the dependency change"
230
+ ## Platform behavior
740
231
 
741
- # Codex CLI / ChatGPT desktop Codex task
742
- Explicitly use the configured Research Agent for this request and wait for it.
743
- ```
232
+ ### macOS
744
233
 
745
- Claude exposes native custom agents in its `@` typeahead. Current Codex releases
746
- load the generated standalone definitions directly from `$CODEX_HOME/agents/`
747
- but do not provide an `@agent`
748
- mention target; named spawning remains model-mediated and may also be limited by
749
- the active MultiAgentV2 tool schema. Use `/agent` in Codex to inspect spawned
750
- agent threads.
234
+ - Tenant apps live in `~/Applications`.
235
+ - LaunchServices registration is non-launching and idempotent.
236
+ - Existing bundle fingerprints and stable signing identity prevent unnecessary
237
+ rebuilds and permission churn.
238
+ - Shared vendor downloads and bundle swaps are serialized.
751
239
 
752
- ### `impel status`
240
+ ### Windows
753
241
 
754
- Shows whether the isolated CLI launchers are ready, and whether each native
755
- tool profile is currently in **GATEWAY** or **ACCOUNT** mode (detected from
756
- whether the Impel markers / base URL are present), plus:
242
+ - Missing official Claude Code and Codex CLIs can be installed during setup.
243
+ - Signed vendor desktop executables remain unchanged.
244
+ - Tenant profiles and Start entries are isolated per tenant.
245
+ - Shared package-manager/vendor preparation runs once per product.
246
+ - System-level installer and PATH changes require confirmation.
757
247
 
758
- - the configured gateway URL,
759
- - whether a PAT is stored (masked, e.g. `impel_pat_ab12...wxyz`),
760
- - the live Workspace member / Gateway member entitlement when available,
761
- - a best-effort reachability check: a `POST` to `<gateway>/anthropic/v1/messages`
762
- with a 5s timeout. Any HTTP response (including a 4xx) counts as "reachable" —
763
- this checks the network path and gateway routing, not the request itself. A
764
- `401`/`403` is called out separately since it usually just means the stored PAT
765
- was rejected, not that the gateway is down.
248
+ ### Linux
766
249
 
767
- Codex CLI and the Codex IDE extension / desktop app share the same
768
- `~/.codex/config.toml`, so they always report the same mode.
250
+ - Every tenant receives isolated CLI profiles.
251
+ - Managed desktop apps are unavailable.
252
+ - Missing vendor CLIs are reported rather than installed through an unverified
253
+ mechanism.
769
254
 
770
- ## Isolated Impel desktop apps
255
+ ## Local state
771
256
 
772
- ### Windows Claude and ChatGPT Desktop
257
+ Main paths:
773
258
 
774
- ```powershell
775
- impel app open claude
776
- impel app status claude
777
- impel app open codex
778
- impel app status codex
259
+ ```text
260
+ ~/.config/impel/config.json
261
+ ~/.config/impel/cli/tenants/<tenant>/
262
+ ~/.config/impel/apps/tenants/<tenant>/
779
263
  ```
780
264
 
781
- The Windows path does not clone, patch, or re-sign Claude. It locates either
782
- Anthropic's standard Squirrel installation or its signed MSIX package, then
783
- launches the vendor executable with the supported `CLAUDE_USER_DATA_DIR`
784
- override. Anthropic's MSIX manifest explicitly reserves `%LOCALAPPDATA%\Claude-3p`
785
- for unvirtualized third-party profiles, so each Impel tenant lives below that
786
- root. The app's normal first-party profile is not read or changed. `impel setup`
787
- installs this profile by default; use `--skip-apps` to omit it.
788
-
789
- For ChatGPT/Codex Desktop, Windows installs the official Microsoft Store product
790
- `9PLM9XGG6VKS` (`OpenAI.Codex`). Windows blocks other programs from directly
791
- executing Electron inside `C:\Program Files\WindowsApps`, so Impel copies the
792
- vendor's complete `app` payload, unchanged, into a shared managed cache. It
793
- then launches that copy with a separate `CODEX_HOME` and
794
- `CODEX_ELECTRON_USER_DATA_PATH` (plus Electron `--user-data-dir` as a legacy
795
- fallback). The normal Store app, `%USERPROFILE%\.codex`, and the normal ChatGPT
796
- browser profile are not read or changed. `chatgpt` and `codex` are equivalent
797
- app-target names.
798
-
799
- ### macOS Claude and ChatGPT
800
-
801
- The first `impel app open --tenant <org>` (or an explicit `impel app install`)
802
- creates tenant-specific apps such as
803
- `Impel Claude (acme).app` and `Impel ChatGPT (acme).app` in `~/Applications`.
804
- Their visible bundle names use the organization display name, for example
805
- `Impel Claude (Acme)` and `Impel ChatGPT (Acme)`. Each tenant/product pair has
806
- a stable, unique bundle identifier and gateway configuration:
807
-
808
- - Impel Claude is an APFS-cloned vendored copy of the official app with its own
809
- bundle and helper identities. Its LaunchServices environment sets both the
810
- vendor-supported `CLAUDE_USER_DATA_DIR` and Claude Code's
811
- `CLAUDE_CONFIG_DIR` to the tenant-private profile. This isolates browser
812
- state, sessions, plugins, MCP servers, skills, and native `@` agents from
813
- `~/.claude`, while the app writes the 3P gateway
814
- `configLibrary` below `~/.config/impel/apps/tenants/<org>/claude`. A new
815
- isolated profile defaults to the Code app while preserving any app selection
816
- the user makes afterward. A narrowly version-checked compatibility patch
817
- connects Claude's native plan-usage meter to the authenticated aggregate
818
- Claude subscription pool exposed by the Impel gateway.
819
- - Impel ChatGPT wraps an APFS-cloned official app while preserving the nested
820
- OpenAI-signed bundle and executable. A wrapper-owned startup preload pins
821
- Electron's application name before the native menu is created, so macOS
822
- shows `Impel ChatGPT (<org>)` without changing OpenAI's signed code. Its outer
823
- resource root contains the narrowly patched renderer ASAR used at runtime.
824
- The wrapper sets `CODEX_HOME`,
825
- passes a separate Chromium `--user-data-dir`, and writes its provider config
826
- and model catalog below `~/.config/impel/apps/tenants/<org>/chatgpt`. It also supplies a
827
- process-scoped, Codex-compatible wrapper around the Impel PAT so native
828
- `/status` requests can read the organization's aggregated subscription pool
829
- from the gateway. Its generated Codex config uses the same snapshot and
830
- subprocess-environment hardening as `impel codex`; install, update, and every
831
- direct app launch also remove older snapshots for the selected profile, while
832
- app install/update/open sweeps inactive tenant and legacy Impel app profiles.
833
- - Neither app changes `~/.claude`, `~/.claude.json`, `~/.codex`, or the
834
- normal vendor app's browser/profile data.
835
-
836
- Open several tenants without changing the current selection. A missing variant
837
- is installed and configured automatically before it opens:
265
+ The config directory is owner-only and the config file is written with mode
266
+ `0600` where supported. Generated client profiles use tenant-bound helper
267
+ invocations; PAT material is not embedded into shell entries.
838
268
 
839
- ```sh
840
- impel app open all --tenant acme
841
- impel app open all --tenant globex
269
+ Losing membership does not delete local tenant data. A future cleanup feature
270
+ must remain explicit and destructive-state aware.
271
+
272
+ ## Public command surface
273
+
274
+ ```text
275
+ impel setup
276
+ impel update
277
+ impel claude
278
+ impel codex
279
+ impel tenant list|current|use
280
+ impel tasks ...
281
+ impel pat ...
282
+ impel status
283
+ impel doctor
842
284
  ```
843
285
 
844
- Updates with `--tenant` and all uninstalls touch only the named tenant variant.
845
- `impel app update all` without `--tenant` discovers every locally installed
846
- tenant and updates each sequentially; it does not install variants for tenants
847
- that have never been installed. The older global `Impel Claude.app` /
848
- `Impel ChatGPT.app` launcher is removed after the first successful
849
- tenant-specific rebuild.
850
-
851
- `impel app update` compares the installed bundle's build fingerprint, signing
852
- mode, pinned vendor version, identity, profile paths, and ASAR compatibility
853
- hash before touching it. The build fingerprint tracks the bundle-BUILDING logic
854
- (asar patches, plist rewrites, signing), not the CLI version, so a CLI release
855
- that doesn't change bundle bytes leaves installed bundles — and their macOS
856
- permission grants — alone. A current bundle is not closed, cloned, patched, or
857
- codesigned; its config and model catalog are still refreshed. If an interrupted
858
- update left a machine in a bad state, `impel app update --force` removes the
859
- selected launcher and performs a clean rebuild while sweeping abandoned
860
- `.tmp-*` and `.previous-*` staging directories.
861
-
862
- Skills plugin subprocesses run asynchronously, so the TTY progress indicator
863
- continues animating while Claude or Codex refreshes a marketplace. Skill status
864
- lines temporarily clear and redraw the spinner instead of overwriting it.
865
-
866
- The official Claude and ChatGPT apps remain separate installations and keep
867
- their normal accounts, updater, profiles, and bundle identities. Each CLI
868
- release pins exact, tested vendor builds (v0.10.0 pins Claude `1.20186.9` and
869
- ChatGPT `26.707.72221`) and their archive SHA-256 digests. The installer reuses
870
- an existing official app only when its version exactly matches; otherwise it
871
- downloads the pinned signed archive into Impel's managed vendor cache under
872
- `~/.config/impel/vendor`, verifies its digest, bundle version, layout, and code
873
- signature, and only then rebuilds the isolated app. It never clones a moving
874
- Homebrew or vendor “latest” release, and it never downgrades or replaces the
875
- user's normal vendor app. A future app version requires a new tested CLI release
876
- and pin update. Config refreshes do not download or replace apps. If the model
877
- endpoint is temporarily unavailable, the installer retains functionality using
878
- the CLI's bundled catalog.
879
-
880
- For ChatGPT/Codex, the gateway controls which models and capabilities are
881
- enabled while the installer merges those entries with the vendor app's native
882
- catalog. The Impel copy relaxes the vendor renderer's ChatGPT-account-only Fast
883
- gate because gateway auth supplies the entitlement instead. The outer wrapper
884
- is then signed locally (see below) without replacing the nested OpenAI signing
885
- identity. This preserves model-specific instructions and exposes
886
- the same reasoning-effort and service-tier controls as the normal app, including
887
- Ultra where supported and Fast only on models that advertise the `priority`
888
- tier. Inference uses an explicit non-WebSocket Impel model provider so Codex
889
- cannot fall back to the vendor's hard-coded `chatgpt.com` transport with an
890
- Impel PAT. Config refreshes replace only the Impel-managed gateway block; plugins,
891
- desktop preferences, projects, and other settings written by the isolated app
892
- are kept.
893
-
894
- Impel Claude and the outer Impel ChatGPT wrapper are signed locally after their
895
- bundle identities and launch wrappers are installed. The nested ChatGPT bundle
896
- retains OpenAI's signature.
897
-
898
- By default the CLI signs each rebuild with a **stable per-machine identity** — a
899
- self-signed certificate created once in a dedicated keychain under
900
- `~/.config/impel/codesign` (no Apple Developer account is involved). The
901
- certificate is trusted locally for the code-signing policy only; it is not a
902
- TLS or document-signing authority. macOS ties
903
- every privacy (TCC) grant — microphone, screen recording, folder access,
904
- Automation, accessibility — to the app's code-signing designated requirement. A
905
- stable identity keeps that requirement constant across rebuilds, so the grants
906
- you approve survive `impel app update` the way they do for a normal app instead
907
- of re-prompting after every rebuild. The one-time switch from the old ad-hoc
908
- signature to the stable identity triggers a single final round of prompts. If
909
- the local signing toolchain is unavailable the CLI logs a notice and falls back
910
- to ad-hoc signing (the previous behavior). Two environment overrides are
911
- available: `IMPEL_CODESIGN_ADHOC=1` forces ad-hoc signing, and
912
- `IMPEL_CODESIGN_IDENTITY=<name>` signs with a specific existing identity (e.g. a
913
- real `Developer ID Application: …`).
914
-
915
- Older Claude profiles already encrypt browser data with the macOS login
916
- Keychain item named `Claude Safe Storage`. Impel keeps that namespace immutable
917
- so an update never makes existing cookies or storage unreadable. Because each
918
- tenant app has a distinct bundle identity, macOS asks once before that app may
919
- use the legacy item: enter the Mac login password and choose **Always Allow**.
920
- Choosing **Allow** grants access for only that launch and therefore prompts
921
- again next time. The stable Impel signature keeps a successful Always Allow
922
- grant valid across later app updates. Clean profiles instead start with a
923
- persisted tenant-specific Safe Storage namespace, so they never request access
924
- to the shared vendor item. Impel does not weaken or silently rewrite Keychain
925
- access lists.
926
-
927
- The Claude 3P config necessarily contains the PAT because that app accepts a
928
- gateway API key rather than a token-helper command. It is stored in the same
929
- owner-only Impel config tree as the primary credential. ChatGPT/Codex uses a
930
- command-backed helper and does not copy the PAT into TOML. The installable app
931
- also reads that helper into its own process environment for Codex account-status
932
- compatibility; the value disappears when the app exits.
933
-
934
- ## Switching back and forth
935
-
936
- The whole point: flipping between the gateway and your own account is one
937
- command, per tool, and always reversible.
286
+ Profile helpers, content synchronization, desktop maintenance, and transport
287
+ commands are internal implementation details and intentionally absent from
288
+ normal help.
938
289
 
939
- ```sh
940
- impel on # both tools -> gateway
941
- impel off # both tools -> your own account (restores prior config exactly)
290
+ ## Library export
291
+
292
+ The package continues to expose `impel-cli/gateway` for approved branded
293
+ vendor integrations. Its CLI allowlist is deliberately narrow and does not
294
+ expose control-plane administration commands.
942
295
 
943
- impel on codex # just Codex -> gateway, leave Claude Code alone
944
- impel off claude # just Claude Code -> account
296
+ ## Development
945
297
 
946
- impel status # see where each tool stands right now
298
+ The project is dependency-free at runtime and uses Node's built-in test runner:
299
+
300
+ ```sh
301
+ npm test
302
+ npm pack --dry-run
947
303
  ```
948
304
 
949
- How the clean revert is guaranteed: `impel on` records the pre-existing values
950
- (your own `apiKeyHelper` / `ANTHROPIC_BASE_URL` / Claude sandbox and permission
951
- policy, and Codex's prior `model_provider`, sandbox mode, and workspace network
952
- value) into
953
- `~/.config/impel/config.json` **before** overwriting them,
954
- and only ever captures a real *account-mode* value (re-running `impel on` never
955
- clobbers the backup with Impel's own values). `impel off` removes only keys that
956
- still point at Impel and restores those backups — so a full `on` → `off`
957
- round-trip leaves `settings.json` and `config.toml` byte-for-byte back to where
958
- they started, with all your unrelated keys and tables intact.
959
-
960
- If you'd rather revert by hand: delete `apiKeyHelper`, the Impel
961
- `ANTHROPIC_BASE_URL`, and the Impel-managed sandbox keys from
962
- `~/.claude/settings.json`, plus the Impel-added `WebFetch(domain:*)` allow rule; delete the block between the
963
- `# >>> impel-cli managed block ... >>>` / `# <<< impel-cli managed block <<<`
964
- markers in `~/.codex/config.toml`, reset `model_provider`, and restore or remove
965
- `sandbox_mode` and `sandbox_workspace_write.network_access`; and
966
- `rm -rf ~/.config/impel` to forget the PAT + gateway entirely.
967
-
968
- ## Codex compatibility route
969
-
970
- The installable app uses an explicit Codex model provider at
971
- `<gateway>/chatgpt_passthrough/backend-api/codex`, producing the request path
972
- `/chatgpt_passthrough/backend-api/codex/responses`. The provider disables
973
- WebSockets because the vendor's built-in ChatGPT provider derives that URL from
974
- `chatgpt.com` rather than the configured account-service base. The gateway
975
- preserves the request body while replacing the Impel PAT with the selected
976
- ChatGPT subscription credential.
977
-
978
- The installable Impel ChatGPT app also points Codex's account client at
979
- `<gateway>/chatgpt_passthrough/backend-api`. The gateway exposes a virtual Impel
980
- pool identity and `GET .../wham/usage`, aggregating each primary and secondary
981
- subscription window across all active Codex seats with equal-seat weighting.
982
- This lets the native `/status` surface show pool capacity without exposing any
983
- provider account identity or OAuth credential.
984
-
985
- Before every launch, the wrapper refreshes Codex's supported personal-access-token
986
- login record from the tenant helper. That keeps the desktop app in native
987
- ChatGPT-account mode, allowing `/status` to request the aggregate five-hour and
988
- weekly windows instead of treating Impel as an API-key-only provider.
989
-
990
- The gateway also exposes `POST /v1/responses`, but that is the conventional
991
- OpenAI Responses front door for SDK/Eve traffic and may normalize the body
992
- (including enforcing `store: false`). The CLI deliberately does not use it.
993
-
994
- ## Design notes
995
-
996
- - **Zero dependencies.** Arg parsing, JSON/TOML merging, masked prompts, and the
997
- reachability check are all hand-rolled against Node stdlib (`fs`, `os`,
998
- `path`, `readline`, global `fetch`). No `chalk`, no `commander`, no `inquirer`.
999
- - **The PAT lives in exactly one place:** `~/.config/impel/config.json` (mode
1000
- `0600`). Both Claude Code and Codex fetch it on demand by shelling out to
1001
- `impel token` rather than copying it into their own config files — one token,
1002
- one rotation path, no stale copies.
1003
- - **The `config.toml` merge is intentionally narrow**, not a general TOML
1004
- parser: it touches the top-level `model_provider`, sandbox mode, the
1005
- workspace-write network toggle, and its own fenced `[model_providers.impel*]` block, leaving unrelated
1006
- tables (other providers, MCP servers, sandbox policy, etc.) untouched.
1007
-
1008
- ## Sources consulted for the Codex config
1009
-
1010
- - https://developers.openai.com/codex/config-reference
1011
- - https://developers.openai.com/codex/config-advanced
1012
- - https://developers.openai.com/codex/auth
1013
- - https://developers.openai.com/codex/ide
1014
-
1015
- ## Sources consulted for the Claude sandbox config
1016
-
1017
- - https://code.claude.com/docs/en/settings#sandbox-settings
1018
- - https://code.claude.com/docs/en/sandboxing#configure-the-sandbox-for-your-organization
1019
- - https://github.com/anthropics/claude-code/blob/v2.1.214/examples/settings/settings-bash-sandbox.json
1020
-
1021
- ## Sources consulted for concurrent tenant apps
1022
-
1023
- - Electron v43.1.0 release: https://github.com/electron/electron/releases/tag/v43.1.0
1024
- - Electron applies `--user-data-dir` before app JavaScript:
1025
- https://github.com/electron/electron/blob/b5c102b3f0f7e9c5be064ba337a3547a83cf7d09/shell/app/electron_main_delegate.cc#L247-L256
1026
- - Electron scopes its single-instance lock to the resolved user-data directory:
1027
- https://github.com/electron/electron/blob/b5c102b3f0f7e9c5be064ba337a3547a83cf7d09/shell/browser/api/electron_api_app.cc#L1049-L1073
1028
- - Electron macOS bundle rebranding requirements:
1029
- https://github.com/electron/electron/blob/b5c102b3f0f7e9c5be064ba337a3547a83cf7d09/docs/tutorial/application-distribution.md#L79-L96
1030
- - Apple `CFBundleIdentifier` reference:
1031
- https://developer.apple.com/documentation/bundleresources/information-property-list/cfbundleidentifier
305
+ Tests use temporary homes, injected process runners, fake catalogs, and local
306
+ HTTP fixtures. They must not require a live PAT, launch or close a real app,
307
+ run a real package manager, or modify native personal profiles.
308
+
309
+ CI runs on Ubuntu, macOS, and Windows with supported Node versions. See
310
+ [RELEASING.md](RELEASING.md) for the npm release process.