impel-cli 0.18.15 → 0.18.16

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
@@ -98,8 +98,58 @@ Names include the tenant, such as `Impel Claude (Acme)` and
98
98
  `Impel ChatGPT (Acme)`. The selected CLI tenant does not affect which tenant an
99
99
  app opens. Tenant variants can remain installed side by side.
100
100
 
101
+ Impel-managed apps do not run the vendors' in-app auto-updaters. The generated
102
+ Claude policy disables its updater, and the ChatGPT launcher disables Sparkle
103
+ before starting the pinned app. Run `impel update` to move managed apps to the
104
+ next exact vendor builds reviewed and shipped with the CLI. This does not
105
+ change the update settings of native personal Claude or ChatGPT installations.
106
+
101
107
  Linux does not have managed desktop apps. Use the isolated CLI commands there.
102
108
 
109
+ ## Experimental managed Cursor
110
+
111
+ On macOS, a hidden experiment can prepare an isolated Cursor runtime for the
112
+ selected Impel tenant:
113
+
114
+ ```sh
115
+ impel experimental cursor prepare
116
+ impel experimental cursor open [workspace]
117
+ impel experimental cursor status
118
+ ```
119
+
120
+ The experiment never modifies `/Applications/Cursor.app` or the native Cursor
121
+ profile. It verifies the installed stable app's Developer ID signature, mirrors
122
+ it into the selected tenant's Impel-managed directory, enables Cursor's bundled
123
+ local-agent runtime with fail-closed fixed-width patches, verifies extension
124
+ integrity and signing, and prepares a tenant-only home, user-data directory,
125
+ and extension directory. Cursor's local-mode profile-import service is disabled
126
+ so it cannot inspect or copy the native Cursor profile. The managed bundle
127
+ recomputes Cursor's signed-source checksum table after its reviewed patches, and
128
+ the launcher uses Cursor's process-lifetime secret store because no persistent
129
+ Cursor account or provider secret belongs in this tenant runtime.
130
+
131
+ The native Cursor installation continues to update through Cursor. Every
132
+ `prepare` inspects it again and atomically rebuilds the managed copy
133
+ when a compatible version or commit changes. If Cursor changes any reviewed
134
+ local-agent, provider-priority, or model-transport source contract, Impel keeps
135
+ the previous managed generation intact and refuses the update until the patch is
136
+ reviewed. `experimental cursor open` performs this prepare step before every
137
+ launch. A real packaged GUI battle test proves that Cursor 3.13.25 can run
138
+ Agent through the selected tenant's gateway with Cursor's own non-secret smoke
139
+ identity. The logical `default` model is resolved by the gateway to its
140
+ registered Cursor default; explicit gateway models continue to use Responses.
141
+
142
+ This is not complete hosted-Cursor parity or an offline Cursor.
143
+ The managed local transport supports Agent chat, edits, terminal tools, MCP,
144
+ vision, reasoning, and gateway model discovery, and projects the live tenant
145
+ catalog into Cursor's model picker, but the current vendor build explicitly
146
+ disables hosted-only features such as Tab completion, cloud Background Agents,
147
+ shared chats/canvases, Bugbot, and server-side repository indexing in local
148
+ mode. Enabling those paths would either send work to Cursor-hosted models or
149
+ require additional unpublished Cursor protocols, so the experiment does not
150
+ claim or silently emulate them. See
151
+ [`docs/experimental-managed-cursor.md`](docs/experimental-managed-cursor.md).
152
+
103
153
  ## Use the CLI
104
154
 
105
155
  Select the tenant used by CLI launches:
@@ -179,6 +229,11 @@ impel update --no-recovery # disable recovery for this run
179
229
  schema verified by that CLI. It does not mean cloning an untested moving vendor
180
230
  release.
181
231
 
232
+ Stable installations follow npm's `latest` tag. Prerelease installations made
233
+ from `impel-cli@next` remain on the `next` channel during `impel update`. If npm
234
+ metadata is temporarily unavailable, update preserves the installed CLI build
235
+ and continues tenant reconciliation without attempting a downgrade.
236
+
182
237
  ## Status and diagnosis
183
238
 
184
239
  ```sh
@@ -0,0 +1,205 @@
1
+ # Experimental managed Cursor
2
+
3
+ This hidden macOS experiment runs the stable Cursor desktop Agent against the
4
+ selected Impel tenant without a real Cursor account or Cursor-routed BYOK. It is
5
+ validated against Cursor 3.13.25 and remains separate from the public
6
+ `impel setup` / `impel update` state machine:
7
+
8
+ ```sh
9
+ impel experimental cursor prepare
10
+ impel experimental cursor status
11
+ impel experimental cursor open [workspace-or-Cursor-args...]
12
+ ```
13
+
14
+ ## Why standard BYOK is insufficient
15
+
16
+ Cursor's supported BYOK mode still sends provider keys and requests through
17
+ Cursor's backend, applies only to chat models, and keeps Tab on Cursor-hosted
18
+ models. It cannot provide direct, tenant-scoped Impel inference.
19
+
20
+ The stable desktop bundle also contains a dormant local-agent runtime with
21
+ OpenAI Responses, Chat Completions, Anthropic Messages, `/models` discovery,
22
+ tools, streaming, reasoning, vision, and MCP support. Impel enables only the
23
+ exact reviewed local-mode signatures and locks provider resolution to its
24
+ process environment.
25
+
26
+ ## Managed authentication
27
+
28
+ Cursor's packaged Agent UI considers a user logged in only when both an access
29
+ token and refresh token exist. The same stable bundle contains an unconditional
30
+ test command that creates:
31
+
32
+ - an unsigned JWT with issuer `cursor-smoke-test`, audience `cursor`, and
33
+ scope `smoke-test`;
34
+ - the literal refresh token `fake-refresh-token-for-testing`.
35
+
36
+ Impel reproduces that vendor-owned, non-secret identity in the tenant-only
37
+ `state.vscdb` and also passes the JWT through Cursor's
38
+ `--override-cursor-auth-token` process flag. It never asks for, reads, or
39
+ stores a real Cursor credential. The source markers, storage keys, JWT contract,
40
+ and command-line flags are part of the fail-closed vendor compatibility check.
41
+
42
+ The fake identity unlocks the local Agent UI. Cursor-hosted account, model,
43
+ analytics, and hosted-feature requests remain unauthenticated and cannot use
44
+ the fake token for service access. The Impel PAT-derived tenant credential is
45
+ given only to the bundled local provider:
46
+
47
+ ```text
48
+ CURSOR_LOCAL_AGENT_BASE_URL=<gateway>/experimental/openai/v1
49
+ CURSOR_LOCAL_AGENT_API_KEY=<tenant-scoped Impel credential>
50
+ IMPEL_TENANT_ID=<tenant>
51
+ ```
52
+
53
+ ## Update and isolation contract
54
+
55
+ For every prepare or open, Impel:
56
+
57
+ 1. Finds the installed stable `Cursor.app`.
58
+ 2. Verifies its complete Apple signature, Cursor bundle identifier, and vendor
59
+ Developer ID team.
60
+ 3. Reads the exact version and commit from `product.json`.
61
+ 4. Verifies every reviewed local-mode, authentication, provider-priority,
62
+ model-transport, and profile-import source contract.
63
+ 5. Verifies Cursor's extension-integrity table, then updates exactly the hashes
64
+ affected by the reviewed fixed-width patches.
65
+ 6. Recomputes Cursor's `product.json` checksum table after every reviewed
66
+ managed patch, so Cursor's own installation-integrity service verifies the
67
+ resulting managed bundle.
68
+ 7. Assigns a tenant-specific packaged application name and uses tenant-only
69
+ `HOME`, XDG, user-data, and extension roots. This isolates Cursor's
70
+ hardcoded `~/.cursor` agents, rules, plugins, worktrees, and logs in addition
71
+ to the normal VS Code profile state.
72
+ 8. Disables local-mode personal-data import so the managed app cannot inspect,
73
+ copy, or relaunch from the native Cursor profile.
74
+ 9. Clears inherited OpenAI, Anthropic, Google, AWS/Bedrock, Azure OpenAI,
75
+ Cursor, Node, Electron, and portable-profile overrides.
76
+ 10. Uses Cursor's built-in process-lifetime secret store. The managed runtime
77
+ has no real Cursor login or persistent provider key, while its gateway
78
+ bearer remains process-scoped in the launch environment.
79
+ 11. Re-signs every Electron process bundle with the reviewed entitlements and
80
+ verifies the complete managed copy.
81
+ 12. Atomically replaces the tenant generation only after every check passes.
82
+
83
+ Tenant state lives below:
84
+
85
+ ```text
86
+ ~/.config/impel/apps/tenants/<tenant>/cursor/
87
+ runtime/current/Cursor.app
88
+ runtime/current/manifest.json
89
+ home/
90
+ user-data/
91
+ extensions/
92
+ ```
93
+
94
+ The launcher rejects user-supplied `--user-data-dir`, `--extensions-dir`,
95
+ and `--override-cursor-auth-token`. The native Cursor profile and the
96
+ operator's `~/.cursor`, `~/.claude`, and `~/.codex` roots are never read or
97
+ modified. The isolated `HOME` means shell, Git, and extension state that
98
+ normally lives in a home directory must be configured separately for this
99
+ tenant runtime.
100
+
101
+ The managed copy disables self-updates. The signed native installation remains
102
+ Cursor's update authority, and every `open` re-runs `prepare`, adopting a
103
+ compatible stable vendor update immediately. If an update changes any reviewed
104
+ contract, Impel preserves the previous generation and refuses the new build
105
+ until its compatibility signatures and tests are reviewed.
106
+
107
+ ## Gateway contract
108
+
109
+ The gateway must enable `IMPEL_EXPERIMENTAL_CROSS_APP_MODELS` and serve:
110
+
111
+ ```text
112
+ GET /experimental/openai/v1/models
113
+ POST /experimental/openai/v1/chat/completions
114
+ POST /experimental/openai/v1/responses
115
+ ```
116
+
117
+ Cursor 3.12 initially submits Agent turns with its logical model `default`
118
+ over Chat Completions, independently of the discovered catalog. The gateway
119
+ maps that alias only on the experimental Cursor route to the exact registered
120
+ Cursor default (`gpt-5.6-sol` today). Bifrost then performs its typed
121
+ Chat-to-Responses conversion, including streaming response conversion, tools,
122
+ reasoning, and multimodal messages. The converted request follows the normal
123
+ pooled Codex path with `store:false`, tenant attribution, usage metering, and
124
+ server-owned provider authentication.
125
+
126
+ Explicit local-provider model requests can use Responses directly. The
127
+ authenticated catalog is filtered by PAT scopes and live tenant pool readiness
128
+ and advertises tools, streaming, reasoning, vision, context, modalities, and
129
+ both protocol markers required by Cursor's bundled parser.
130
+
131
+ Reasoning effort is a per-model, per-selection parameter rather than a global
132
+ Cursor setting. The gateway catalog owns each model's allowed levels and
133
+ default. Impel projects those values into Cursor's `Reasoning` selector, and the
134
+ local Responses transport sends the selected value as `reasoning.effort`.
135
+ Bifrost then converts that typed field for the selected Codex or Claude
136
+ subscription provider. Models that advertise no reasoning levels receive no
137
+ effort selector.
138
+
139
+ ## Battle-test result and parity boundary
140
+
141
+ The gated packaged-GUI contract now proves all of the following against the
142
+ real signed stable app and patched managed copy:
143
+
144
+ - the native Cursor profile is unreadable and its sentinel remains unchanged;
145
+ - the onboarding and hosted-login wall are absent;
146
+ - the real `agent-exec` and local-agent runtime extensions activate;
147
+ - authenticated `GET /models` reaches the loopback gateway;
148
+ - the tenant catalog is reconciled into Cursor's named-model picker without
149
+ overwriting non-Impel model preferences;
150
+ - a real Agent submission sends `model: "default"` to Chat Completions with
151
+ the tenant bearer;
152
+ - the unique streamed gateway result is rendered in the Agent UI;
153
+ - no personal provider credential or native profile is used.
154
+
155
+ The direct runtime contract separately proves model discovery and a Responses
156
+ stream with the intended model and bearer.
157
+
158
+ The gateway conversion contract separately pins tool-call IDs across the next
159
+ turn's tool result, fragmented parallel argument streams, reasoning deltas,
160
+ `tool_calls` termination, vision input, and `store:false` after conversion.
161
+
162
+ | Cursor surface | Managed local mode |
163
+ | --- | --- |
164
+ | Editor, terminal, source control, extensions | Retained with tenant-isolated state |
165
+ | Agent chat, edits, terminal tools, MCP | Works through the Impel gateway |
166
+ | Streaming, reasoning, vision, tool calls | Preserved by Cursor and Bifrost's typed conversion |
167
+ | Model selection | Gateway owns `default`; concrete tenant models are projected into Cursor's named-model picker |
168
+ | Tab completion | Unavailable; Cursor disables CPP/Tab in local mode |
169
+ | Cloud Background Agents | Unavailable; hosted Cursor service |
170
+ | Shared chats/canvases, Bugbot, code tour | Unavailable; hosted Cursor services |
171
+ | Server-side repository indexing/search | Unavailable; hosted Cursor service |
172
+
173
+ This provides direct Impel inference for the local Agent with normal Cursor
174
+ editor updates. It cannot provide complete hosted-Cursor parity because Tab,
175
+ cloud agents, collaboration, Bugbot, and hosted indexing use unpublished
176
+ Cursor services rather than the bundled local-provider protocol. The app may
177
+ still contact Cursor domains for updates, extensions, and unavailable hosted
178
+ features; model inference is the gateway-owned boundary.
179
+
180
+ The tenant gateway bearer is process-scoped because Cursor's current local
181
+ provider contract resolves it from the launch environment. Treat extensions
182
+ installed into this isolated Cursor profile as trusted: a same-user extension
183
+ or process can inspect or reuse that gateway-only bearer. A short-lived,
184
+ route-bound desktop capability would be a future hardening, but it is not part
185
+ of the current PAT contract.
186
+
187
+ ## Validation
188
+
189
+ The ordinary suite uses fake bundles and credentials. Validate the installed
190
+ stable bundle and the direct local-provider transport with:
191
+
192
+ ```sh
193
+ IMPEL_VALIDATE_INSTALLED_CURSOR_LOCAL=1 node --test --test-name-pattern='installed stable Cursor' test/cursor-local.test.js
194
+ ```
195
+
196
+ The packaged GUI battle test uses only a disposable HOME, user-data directory,
197
+ extension directory, workspace, process-lifetime secret store, loopback HTTP
198
+ server, and loopback debugger:
199
+
200
+ ```sh
201
+ IMPEL_VALIDATE_INSTALLED_CURSOR_GUI=1 node --test --test-name-pattern='managed Cursor GUI' test/cursor-local.test.js
202
+ ```
203
+
204
+ Neither test reads or writes the operator's native Cursor profile or uses live
205
+ provider credentials.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impel-cli",
3
- "version": "0.18.15",
3
+ "version": "0.18.16",
4
4
  "description": "Prepare isolated Claude and Codex workspaces for every accessible Impel tenant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,7 @@
12
12
  },
13
13
  "files": [
14
14
  "bin",
15
+ "docs/experimental-managed-cursor.md",
15
16
  "src",
16
17
  "README.md"
17
18
  ],