codex-to-im 1.0.21 → 1.0.23

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
@@ -159,7 +159,7 @@ codex-to-im stop
159
159
  - `/t 0`:切换到当前聊天的临时线程。
160
160
  - `/new`:在当前正式会话目录下新建线程。
161
161
  - `/new <路径或项目名>`:按指定目录新建线程。
162
- - `/mode <ask|code>`:切换运行模式。
162
+ - `/mode <ask|code|plan>`:切换运行模式。
163
163
  - `/reasoning <1-5>`:切换思考级别。
164
164
  - `/model`:查看当前模型和可选模型。
165
165
  - `/model <模型名>`:切换当前 IM 会话模型。
package/README_EN.md CHANGED
@@ -2,89 +2,62 @@
2
2
 
3
3
  [中文版](README.md)
4
4
 
5
- `codex-to-im` is a local bridge app that connects Codex desktop sessions to IM channels such as Feishu/Lark and Weixin.
5
+ `codex-to-im` is a local bridge app that connects Codex to IM channels such as Feishu/Lark and Weixin.
6
6
 
7
- The product is no longer centered around a Codex skill. The main path is:
7
+ Its main path is not to modify Codex itself, but to:
8
8
 
9
- 1. Install `codex-to-im`
10
- 2. Open the local web workbench
11
- 3. Create one or more channel instances in the workbench
12
- 4. Start the bridge in the background
13
- 5. Bind real desktop Codex threads to Feishu or Weixin chats
9
+ 1. start a local web workbench and bridge on your machine
10
+ 2. create and configure one or more channel instances in the workbench
11
+ 3. bind desktop Codex sessions to IM chats
12
+ 4. continue the same conversation, switch threads, and inspect status from IM
14
13
 
15
- Optional: if you want Codex to know it can send local files or images back to IM without relying on bridge-injected prompt text, install the bundled `codex-to-im` skill from the workbench.
14
+ ## Core Capabilities
16
15
 
17
- ## Project Origin
16
+ - Shared desktop threads: bind a thread currently used in Codex Desktop to IM and continue the same conversation there.
17
+ - IM remote control: inspect current status, switch threads, create threads, change mode, change reasoning effort, switch model, stop the current task, and inspect history from IM.
18
+ - Local web workbench: central place for configuration, channel login, logs, session management, and binding management.
19
+ - Feishu streaming cards: Feishu can show streaming shared-thread responses and tool progress updates.
20
+ - Attachment send-back: send local images or files back to Feishu; if you want Codex to actively use that capability, install the bundled `codex-to-im` skill.
21
+ - Local-first: services, config, logs, and the bridge all run on the local machine; LAN access to the web console is optional.
18
22
 
19
- The current codebase is a consolidated continuation of two earlier repositories:
23
+ ## Supported Channels
20
24
 
21
- - `Claude-to-IM`
22
- - `Claude-to-IM-skill`
25
+ - Feishu: supports multiple bot instances, connectivity testing, shared threads, streaming cards, image sending, and file sending.
26
+ - Weixin: supports multiple instances, QR login, shared threads, and text feedback.
23
27
 
24
- `codex-to-im` is based on those two projects and has been reworked toward a single-package local app and shared-thread workflow.
28
+ Each channel instance can have its own alias, for example:
25
29
 
26
- Windows host installation guide: [docs/install-windows.md](docs/install-windows.md)
30
+ - `Feishu Main`
31
+ - `Feishu Backup`
32
+ - `Weixin Work`
27
33
 
28
- ## What It Includes
34
+ These aliases only distinguish different chat entry points. They do not change Codex session semantics.
29
35
 
30
- - Local background bridge service
31
- - Local web workbench for configuration, testing, logs, and bindings
32
- - Multi-instance Feishu bot setup and connectivity testing
33
- - Multi-instance Weixin login flow
34
- - Desktop session discovery from `~/.codex/sessions`
35
- - Web-side binding updates for IM chats
36
-
37
- ## Install
36
+ ## Quick Start
38
37
 
39
38
  ### Prerequisites
40
39
 
41
40
  - Node.js 20+
42
- - If you use the `codex` or `auto` runtime, complete Codex authentication under the same OS user account
43
-
44
- `codex-to-im` now ships with the required `@openai/codex-sdk` / Codex CLI platform dependency, so you do not need to install a separate global Codex CLI just to run the bridge.
41
+ - Codex login state or API credentials available under the current OS user
45
42
 
46
- You still need Codex credentials to be available for the current user. Any of these is sufficient:
43
+ Any of the following is sufficient:
47
44
 
48
45
  - a logged-in Codex Desktop App
49
- - an existing Codex CLI login state
46
+ - a logged-in Codex CLI
50
47
  - `CTI_CODEX_API_KEY`, `CODEX_API_KEY`, or `OPENAI_API_KEY`
51
48
 
52
- If the machine does not have any Codex login state yet, the simplest path is still to install the global CLI once and log in:
53
-
54
- ```bash
55
- npm install -g @openai/codex
56
- codex auth login
57
- ```
58
-
59
- ### Global install
49
+ ### Install
60
50
 
61
51
  ```bash
62
52
  npm install -g codex-to-im
63
53
  ```
64
54
 
65
- ### Local development
66
-
67
- ```bash
68
- npm install
69
- npm run build
70
- ```
71
-
72
- Windows maintenance note:
73
-
74
- - The repo includes [patch-codex-sdk-windows-hide.js](scripts/patch-codex-sdk-windows-hide.js), which applies a conservative postinstall patch to `@openai/codex-sdk`.
75
- - This exists because on Windows the SDK may spawn the bundled Codex CLI without `windowsHide`, causing a black console window to flash for each IM-triggered run.
76
- - When upgrading `@openai/codex-sdk`, verify that the spawn block still matches; if upstream fixes this natively, remove the patch instead of carrying it forward.
77
-
78
- ## Run
79
-
80
- Start the local app:
55
+ ### Start
81
56
 
82
57
  ```bash
83
58
  codex-to-im
84
59
  ```
85
60
 
86
- This launches the local workbench and opens it in your browser.
87
-
88
61
  If you only want the background bridge without opening the UI:
89
62
 
90
63
  ```bash
@@ -93,7 +66,7 @@ codex-to-im start
93
66
 
94
67
  ### Boot Autostart on Windows
95
68
 
96
- The bridge can be registered as a Windows boot task. The Web UI remains on-demand and is still opened manually with `codex-to-im`.
69
+ The current implementation can register the **bridge** as a Windows boot task. The UI is still opened on demand with `codex-to-im`.
97
70
 
98
71
  ```powershell
99
72
  codex-to-im autostart status
@@ -104,161 +77,121 @@ codex-to-im autostart uninstall
104
77
  Notes:
105
78
 
106
79
  - `codex-to-im autostart install` and `codex-to-im autostart uninstall` must be run from an **elevated Administrator PowerShell / terminal**.
107
- - Installation prompts for the current Windows account password so the startup task can be created.
80
+ - Installation prompts for the current Windows account password so the boot task can be created.
108
81
  - Autostart only launches the bridge; it does not open the Web UI.
109
- - Running `codex-to-im` manually later only starts the UI if needed and will not duplicate the bridge.
110
- - The current implementation uses Windows Task Scheduler and does not require WinSW, NSSM, or PM2.
111
- - The Web UI is read-only for autostart status; enable/disable it from the administrator terminal commands above.
82
+ - Running `codex-to-im` manually later only starts the UI if needed and does not duplicate the bridge.
83
+ - The current implementation uses the built-in Windows Task Scheduler and does not depend on WinSW or NSSM.
84
+ - The web workbench only shows autostart status; enable or disable it from the administrator commands above.
112
85
 
113
- By default the workbench runs at:
86
+ By default the local workbench opens at:
114
87
 
115
88
  ```text
116
89
  http://127.0.0.1:4781
117
90
  ```
118
91
 
119
- If that port is already occupied, the app automatically finds an available local port and prints the actual address to the terminal when starting.
120
-
121
- By default, the web workbench only accepts local access.
122
-
123
- If you want to open it from your phone or another device on the same LAN, enable `允许局域网访问 Web 控制台` in the `配置` page. When enabled:
124
-
125
- - the workbench shows detected LAN URLs
126
- - the workbench displays an access token
127
- - LAN devices see a login page before they can view or modify settings
128
- - you can also copy a ready-to-use login link that includes `?token=...`
129
-
130
- If you forget the current address, run:
92
+ If you want to inspect the current address or service state:
131
93
 
132
94
  ```bash
133
95
  codex-to-im url
134
- ```
135
-
136
- Check the current local service state:
137
-
138
- ```bash
139
96
  codex-to-im status
140
97
  ```
141
98
 
142
- Stop the background UI and bridge:
99
+ If you want to stop the local UI and bridge:
143
100
 
144
101
  ```bash
145
102
  codex-to-im stop
146
103
  ```
147
104
 
148
- ## Main Workflow
149
-
150
- 1. Open the workbench
151
- 2. Create a Feishu or Weixin channel instance in the workbench
152
- 3. Give the instance an alias such as `Feishu Main` or `Weixin Work`
153
- 4. Save config and test connectivity
154
- 5. Start the bridge
155
- 6. Open the desktop sessions section
156
- 7. Bind a Feishu or Weixin chat to the target thread
157
- 8. Continue the same Codex thread from IM
158
-
159
- If LAN access is enabled, the easiest path is to copy the LAN login link from the local workbench and open it on your phone or another device on the same network.
160
-
161
- Useful commands:
105
+ ## Typical Workflows
162
106
 
163
- - `/` / `/status` shows the current session
164
- - `/h` / `/help` shows help
165
- - `/t` / `/threads` lists the most recent 10 desktop threads, `/t all` / `/threads all` lists up to 200 of them, `/t n 100` / `/threads n 100` lists the most recent 100 desktop threads (also capped at 200), and `/t 1` / `/thread 1` binds the first one
166
- - `/n` / `/new` creates a new thread in the current formal session directory; these IM-created threads are only guaranteed to continue inside IM and will not automatically appear in the Codex Desktop thread list
167
- - `/n proj1` / `/new proj1` creates a new project session under the default workspace root
168
- - `/m` / `/mode` shows or changes the current mode; options: `code` / `plan` / `ask`
169
- - `/r` / `/reasoning` shows or changes the current reasoning effort; options: `1|2|3|4|5`
170
- - `/his` / `/history` shows the summarized history, and `/his raw` / `/history raw` shows raw history
171
- - `/t 0` / `/thread 0` enters a temporary draft thread that does not pollute the main work thread
172
- - `1 / 2 / 3` or `/perm ...` handles permission prompts
173
- - N is configurable in the web workbench under the basic settings panel
174
- - The workbench command guide shows both short commands and compatible original commands
107
+ ### 1. Take over a desktop thread
175
108
 
176
- If you enable Feishu streaming response cards, the Feishu app must have the required permissions published first, at minimum:
109
+ After creating a Feishu or Weixin channel instance in the web workbench, start the bridge.
110
+ Then send:
177
111
 
178
- - `cardkit:card:write`
179
- - `cardkit:card:read`
180
- - `im:message:update`
181
-
182
- If those permissions are missing, the bridge log will usually show `99991672` with `cardkit:card:write`, and the bridge falls back to a final-result message.
183
-
184
- Also note that under the current `codex` runtime, the `Codex CLI / SDK` typically emits the assistant text only when the `agent_message` item is completed, not as token-level deltas. In practice that means Feishu "streaming cards" currently behave more like:
185
-
186
- - early `Thinking / Tool Progress` updates
187
- - final response text written into the card at completion
188
-
189
- So character-by-character text streaming is not guaranteed in the current implementation.
190
-
191
- If creating a new session fails with `Not inside a trusted directory`, either:
192
-
193
- - switch to a trusted project with `/new /absolute/path` or `/new proj1`, or
194
- - enable `Allow Codex outside trusted Git repos` in the basic settings and restart the bridge
195
-
196
- The configuration page also includes Codex runtime controls:
197
-
198
- - `Default workspace root`
199
- - parent directory used for `/new proj1`
200
- - falls back to `~/cx2im` when left empty, expanded for the current OS
201
- - `Codex filesystem permission`
202
- - `read-only`, `workspace-write`, or `danger-full-access`
203
- - default: `workspace-write`
204
- - `Codex reasoning effort`
205
- - global default reasoning level
206
- - can be overridden per IM session with `/reasoning`
207
- - official runtime levels are `minimal`, `low`, `medium`, `high`, `xhigh`
208
- - IM numeric aliases are `1=minimal`, `2=low`, `3=medium`, `4=high`, `5=xhigh`
112
+ ```text
113
+ /t
114
+ ```
209
115
 
210
- The primary persisted configuration now lives in:
116
+ to list the latest 10 desktop threads. Send:
211
117
 
212
- - `~/.codex-to-im/config.v2.json`
118
+ ```text
119
+ /t all
120
+ ```
213
121
 
214
- The legacy `config.env` file is still written as a compatibility snapshot, but it no longer fully represents multi-instance channel setup.
122
+ to list up to 200 desktop threads. Then use:
215
123
 
216
- If you are using `codex-to-im` on your own development machine for real coding work, the more aggressive recommended setup is:
124
+ ```text
125
+ /t 1
126
+ ```
217
127
 
218
- - set `Codex filesystem permission` to `danger-full-access`
219
- - set `Codex reasoning effort` to `xhigh`
128
+ to switch to the selected thread.
220
129
 
221
- This is closer to a full-power `code` workflow. It fits a controlled local project, but is not a good default for unknown repositories or higher-risk environments.
130
+ ### 2. Continue from IM
222
131
 
223
- The channel pages also expose a “Use Markdown for bridge feedback” switch:
224
- - enabled by default for Feishu
225
- - disabled by default for WeChat
226
- - affects text sent through the bridge, including normal replies, shared-thread mirror messages, and system feedback such as `/h`, `/status`, and `/threads`
132
+ Once the binding is established, send normal messages to continue the current thread.
133
+ If the same shared thread is also used on desktop, its output is mirrored back to IM.
227
134
 
228
- Each channel instance can have its own alias. The alias only identifies which IM entry point handled the chat; it does not change Codex session semantics or model behavior.
135
+ ### 3. Create a new IM thread
229
136
 
230
- ## Update
137
+ ```text
138
+ /new
139
+ ```
231
140
 
232
- On Windows, `npm update -g codex-to-im` can fail with `EBUSY` if the background UI or bridge is still running from the global install directory.
141
+ This creates a new thread under the working directory of the current formal session.
142
+ If there is no formal session yet, or the current session is temporary, the command fails.
233
143
 
234
- Recommended update flow:
144
+ You can also specify a directory explicitly:
235
145
 
236
- ```bash
237
- codex-to-im stop
238
- npm update -g codex-to-im
239
- codex-to-im
146
+ ```text
147
+ /new my-project
148
+ /new D:\work\my-project
240
149
  ```
241
150
 
242
- ## Repo Layout
151
+ ## Common Commands
152
+
153
+ - `/` or `/status`: inspect the current session, thread, model, mode, reasoning effort, and shared-mirror status.
154
+ - `/t`: list the latest 10 desktop threads.
155
+ - `/t all`: list up to 200 desktop threads.
156
+ - `/t n 100`: list the latest 100 desktop threads, capped at 200.
157
+ - `/t 1`: switch to desktop thread 1.
158
+ - `/t 0`: switch to the temporary thread for the current chat.
159
+ - `/new`: create a new thread under the current formal session directory.
160
+ - `/new <path or project name>`: create a new thread under the specified directory.
161
+ - `/mode <ask|code|plan>`: change the runtime mode.
162
+ - `/reasoning <1-5>`: change the reasoning effort.
163
+ - `/model`: inspect the current model and available models.
164
+ - `/model <model name>`: change the model for the current IM session.
165
+ - `/history`: inspect the current thread history summary.
166
+ - `/stop`: stop the current task.
167
+ - `/unbind`: remove the binding between the current chat and the session.
168
+
169
+ ## Key Settings
170
+
171
+ Common settings in the workbench include:
172
+
173
+ - Default workspace root: used for relative paths such as `/new my-project`.
174
+ - Codex filesystem permission: for example `workspace-write` or `danger-full-access`.
175
+ - Codex reasoning effort: `1-5`.
176
+ - Default model: chosen from the models available on the local machine.
177
+ - Use Markdown for feedback: controls whether bridge text feedback is sent through markdown rendering.
178
+ - Allow LAN access to the Web console: useful when opening the workbench from a phone or another device on the same LAN.
179
+ - Channel instances: you can create multiple Feishu or Weixin bot/account entry points and assign an alias to each instance.
180
+
181
+ The primary config file is:
243
182
 
244
- - `src/ui-server.ts` — local workbench UI and HTTP API
245
- - `src/service-manager.ts` — bridge and UI lifecycle management
246
- - `src/desktop-sessions.ts` — desktop thread discovery from Codex session files
247
- - `src/session-bindings.ts` — binding summaries and web-side binding updates
248
- - `src/lib/bridge/` — bridge runtime and IM channel routing
249
- - `docs/` — PRD and shared-thread design docs
183
+ - `~/.codex-to-im/config.v2.json`
250
184
 
251
- ## Development
185
+ The compatibility `config.env` file is still kept as a snapshot and fallback for older tooling, but it no longer fully represents multi-instance channel configuration.
252
186
 
253
- ```bash
254
- npm run typecheck
255
- npm run build
256
- ```
187
+ ## Current Boundaries
257
188
 
258
- ## Status
189
+ - Threads created with `/new` are only guaranteed to continue inside IM; they are not guaranteed to automatically appear in the Codex Desktop thread list.
190
+ - One session can only be bound to one chat at a time, and that exclusivity also applies across Feishu and Weixin.
191
+ - Feishu attachments currently support images and files; videos are currently sent as files and are not guaranteed to render with native preview.
192
+ - `/t` shows only the latest 10 desktop threads by default; `/t all` is capped at 200, and `/t n 100` is also capped at 200.
259
193
 
260
- Current product direction:
194
+ ## More Docs
261
195
 
262
- - Standalone local app first
263
- - Web workbench first
264
- - Shared Codex thread model first
196
+ - Windows installation guide: [docs/install-windows.md](docs/install-windows.md)
197
+ - Chinese version: [README.md](README.md)
package/SECURITY.md CHANGED
@@ -4,8 +4,6 @@
4
4
 
5
5
  Credentials are stored in `~/.codex-to-im/config.env` and the local runtime data under `~/.codex-to-im/`.
6
6
 
7
- The app still falls back to `~/.claude-to-im/` on machines that already have legacy data, but new installs should treat `~/.codex-to-im/` as the primary home.
8
-
9
7
  This repository never stores secrets in source control.
10
8
 
11
9
  ## Log Redaction
@@ -30,9 +28,7 @@ If a token is rotated or suspected to be exposed:
30
28
  3. Restart the bridge from the workbench
31
29
  4. Review recent logs under `~/.codex-to-im/logs/`
32
30
 
33
- ## Legacy Data
34
-
35
- If you upgraded from an older `claude-to-im` install, check both of these locations during diagnosis:
31
+ ## Home Directory
36
32
 
37
- - `~/.codex-to-im/`
38
- - `~/.claude-to-im/`
33
+ Current builds read and write only `~/.codex-to-im/`.
34
+ If a legacy home directory from older releases still exists on a machine, treat it as historical leftover data rather than an active runtime home.
package/dist/cli.mjs CHANGED
@@ -15,15 +15,9 @@ import { fileURLToPath } from "node:url";
15
15
  import fs from "node:fs";
16
16
  import os from "node:os";
17
17
  import path from "node:path";
18
- var LEGACY_CTI_HOME = path.join(os.homedir(), ".claude-to-im");
19
18
  var DEFAULT_CTI_HOME = path.join(os.homedir(), ".codex-to-im");
20
19
  var DEFAULT_WORKSPACE_ROOT = path.join(os.homedir(), "cx2im");
21
- function resolveDefaultCtiHome() {
22
- if (fs.existsSync(DEFAULT_CTI_HOME)) return DEFAULT_CTI_HOME;
23
- if (fs.existsSync(LEGACY_CTI_HOME)) return LEGACY_CTI_HOME;
24
- return DEFAULT_CTI_HOME;
25
- }
26
- var CTI_HOME = process.env.CTI_HOME || resolveDefaultCtiHome();
20
+ var CTI_HOME = process.env.CTI_HOME || DEFAULT_CTI_HOME;
27
21
  var CONFIG_PATH = path.join(CTI_HOME, "config.env");
28
22
  var CONFIG_V2_PATH = path.join(CTI_HOME, "config.v2.json");
29
23
  function parseEnvFile(content) {
@@ -61,6 +55,7 @@ var uiStatusFile = path2.join(runtimeDir, "ui-server.json");
61
55
  var uiPort = 4781;
62
56
  var bridgeAutostartTaskName = "CodexToIMBridge";
63
57
  var bridgeAutostartLauncherFile = path2.join(runtimeDir, "bridge-autostart.ps1");
58
+ var npmUninstallLogFile = path2.join(runtimeDir, "npm-uninstall.log");
64
59
  var WINDOWS_HIDE = process.platform === "win32" ? { windowsHide: true } : {};
65
60
  function ensureDirs() {
66
61
  fs2.mkdirSync(runtimeDir, { recursive: true });
@@ -179,6 +174,68 @@ function ensureBridgeAutostartLauncher() {
179
174
  function parsePowerShellJson(raw) {
180
175
  return JSON.parse(raw);
181
176
  }
177
+ function buildDeferredGlobalNpmUninstallLaunch(options = {}) {
178
+ const packageName = options.packageName || "codex-to-im";
179
+ const logPath = options.logPath || npmUninstallLogFile;
180
+ const delayMs = options.delayMs ?? 1500;
181
+ const platform = options.platform || process.platform;
182
+ const npmCommand = options.npmCommand || (platform === "win32" ? "npm.cmd" : "npm");
183
+ const command = options.nodePath || process.execPath;
184
+ const cwd = options.cwd || os2.homedir();
185
+ const script = [
186
+ "const { spawn } = require('node:child_process');",
187
+ "const fs = require('node:fs');",
188
+ `const logPath = ${JSON.stringify(logPath)};`,
189
+ `const npmCommand = ${JSON.stringify(npmCommand)};`,
190
+ `const npmArgs = ['uninstall', '-g', ${JSON.stringify(packageName)}];`,
191
+ `const childCwd = ${JSON.stringify(cwd)};`,
192
+ `const delayMs = ${JSON.stringify(delayMs)};`,
193
+ "const writeLog = (message) => {",
194
+ " try { fs.appendFileSync(logPath, String(message).endsWith('\\n') ? String(message) : String(message) + '\\n'); } catch {}",
195
+ "};",
196
+ "setTimeout(() => {",
197
+ " let fd;",
198
+ " try { fd = fs.openSync(logPath, 'a'); } catch (error) { writeLog(error); process.exit(1); return; }",
199
+ " const child = spawn(npmCommand, npmArgs, { cwd: childCwd, detached: false, stdio: ['ignore', fd, fd], windowsHide: true });",
200
+ " child.on('error', (error) => { writeLog(error); process.exit(1); });",
201
+ " child.on('close', (code) => { process.exit(typeof code === 'number' ? code : 0); });",
202
+ "}, delayMs);"
203
+ ].join("\n");
204
+ return {
205
+ command,
206
+ args: ["-e", script],
207
+ npmCommand,
208
+ logPath,
209
+ delayMs
210
+ };
211
+ }
212
+ async function launchDeferredGlobalNpmUninstall() {
213
+ ensureDirs();
214
+ const launch = buildDeferredGlobalNpmUninstallLaunch();
215
+ fs2.writeFileSync(
216
+ launch.logPath,
217
+ [
218
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] Scheduling global uninstall.`,
219
+ `${launch.npmCommand} uninstall -g codex-to-im`,
220
+ ""
221
+ ].join("\n"),
222
+ "utf-8"
223
+ );
224
+ await new Promise((resolve, reject) => {
225
+ const child = spawn(launch.command, launch.args, {
226
+ cwd: os2.homedir(),
227
+ detached: true,
228
+ stdio: "ignore",
229
+ ...WINDOWS_HIDE
230
+ });
231
+ child.once("error", reject);
232
+ child.once("spawn", () => {
233
+ child.unref();
234
+ resolve();
235
+ });
236
+ });
237
+ return launch;
238
+ }
182
239
  function getUiServerUrl(port = uiPort) {
183
240
  return `http://127.0.0.1:${port}`;
184
241
  }
@@ -396,6 +453,27 @@ async function uninstallBridgeAutostart() {
396
453
  }
397
454
  return await getBridgeAutostartStatus();
398
455
  }
456
+ async function uninstallCodexToImPackage() {
457
+ const autostartBefore = await getBridgeAutostartStatus();
458
+ if (process.platform === "win32" && autostartBefore.installed) {
459
+ await ensureWindowsAdminSession();
460
+ }
461
+ const ui = await stopUiServer();
462
+ const bridge = await stopBridge();
463
+ const autostart = autostartBefore.installed ? await uninstallBridgeAutostart() : autostartBefore;
464
+ if (autostart.installed) {
465
+ throw new Error(`\u672A\u80FD\u5220\u9664\u5F00\u673A\u81EA\u542F\u52A8\u4EFB\u52A1 ${autostart.taskName}\uFF0C\u5DF2\u53D6\u6D88 npm \u5168\u5C40\u5378\u8F7D\u3002`);
466
+ }
467
+ const launch = await launchDeferredGlobalNpmUninstall();
468
+ return {
469
+ ui,
470
+ bridge,
471
+ autostart,
472
+ npmCommand: launch.npmCommand,
473
+ logPath: launch.logPath,
474
+ scheduled: true
475
+ };
476
+ }
399
477
  async function ensureUiServerRunning() {
400
478
  ensureDirs();
401
479
  const current = getUiServerStatus();
@@ -573,6 +651,19 @@ async function main() {
573
651
  );
574
652
  return;
575
653
  }
654
+ case "uninstall": {
655
+ const result = await uninstallCodexToImPackage();
656
+ process.stdout.write(
657
+ [
658
+ `Stopped services. UI running=${result.ui.running ? "yes" : "no"}, Bridge running=${result.bridge.running ? "yes" : "no"}`,
659
+ result.autostart.installed ? `Bridge autostart still installed: ${result.autostart.taskName}` : "Bridge autostart removed.",
660
+ `Global npm uninstall scheduled via ${result.npmCommand}.`,
661
+ `Log: ${result.logPath}`,
662
+ "\u5F53\u524D\u547D\u4EE4\u9000\u51FA\u540E\uFF0C\u540E\u53F0\u4F1A\u7EE7\u7EED\u6267\u884C\u5168\u5C40\u5378\u8F7D\u3002"
663
+ ].join("\n") + "\n"
664
+ );
665
+ return;
666
+ }
576
667
  case "status": {
577
668
  const ui = getUiServerStatus();
578
669
  const bridge = getBridgeStatus();
@@ -607,6 +698,7 @@ async function main() {
607
698
  return;
608
699
  }
609
700
  case "install": {
701
+ await ensureWindowsAdminSession();
610
702
  const password = await promptHidden("\u8BF7\u8F93\u5165\u5F53\u524D Windows \u767B\u5F55\u5BC6\u7801\uFF08\u7528\u4E8E\u521B\u5EFA\u5F00\u673A\u542F\u52A8\u4EFB\u52A1\uFF09: ");
611
703
  const status = await installBridgeAutostart(password);
612
704
  process.stdout.write(`Bridge autostart installed. Task: ${status.taskName}
@@ -627,7 +719,7 @@ async function main() {
627
719
  }
628
720
  }
629
721
  default:
630
- process.stdout.write("Usage: codex-to-im [start|open|url|stop|status|autostart]\n");
722
+ process.stdout.write("Usage: codex-to-im [start|open|url|stop|status|autostart|uninstall]\n");
631
723
  }
632
724
  }
633
725
  main().catch((error) => {