impel-cli 0.16.5 → 0.17.1

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