@foxden-app/foxclaw 0.3.0 → 0.3.2

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
@@ -51,7 +51,6 @@ FoxClaw(狸爪)的目标很直接:让你用手机控制本机的 Codex,
51
51
  ```bash
52
52
  npm install -g @foxden-app/foxclaw
53
53
  foxclaw init
54
- $EDITOR ~/.foxclaw/.env
55
54
  foxclaw doctor
56
55
  foxclaw start
57
56
  ```
@@ -61,11 +60,12 @@ pnpm 用户:
61
60
  ```bash
62
61
  pnpm add -g @foxden-app/foxclaw
63
62
  foxclaw init
64
- $EDITOR ~/.foxclaw/.env
65
63
  foxclaw doctor
66
64
  foxclaw start
67
65
  ```
68
66
 
67
+ `foxclaw init` 会创建 `~/.foxclaw/.env`,并在终端里提示填写 Telegram bot token、Telegram 数字用户 ID 和默认工作目录。任何一项都可以直接回车跳过,之后再用 `$EDITOR ~/.foxclaw/.env` 手动修改。
68
+
69
69
  跑 `doctor` 或 `start` 之前先把 `.env` 填好。私聊模式最小配置:
70
70
 
71
71
  ```dotenv
@@ -139,6 +139,14 @@ systemctl --user status foxclaw.service
139
139
  journalctl --user -u foxclaw.service -f
140
140
  ```
141
141
 
142
+ 也可以直接用包装命令:
143
+
144
+ ```bash
145
+ foxclaw status
146
+ foxclaw restart
147
+ foxclaw stop
148
+ ```
149
+
142
150
  需要前台调试时:
143
151
 
144
152
  ```bash
@@ -157,26 +165,9 @@ foxclaw serve
157
165
 
158
166
  可通过 `STORE_PATH`、`LOCK_PATH`、`CODEX_APP_SERVER_STATE_PATH`、`CODEX_APP_SERVER_LOG_PATH` 覆盖。
159
167
 
160
- ## 从 telegram-codex-app-bridge 迁移
161
-
162
- FoxClaw 最初 fork 自 `Gan-Xing/telegram-codex-app-bridge`,继续以 MIT License 分发。
163
-
164
- 升级已有安装:
165
-
166
- ```bash
167
- systemctl --user disable --now telegram-codex-app-bridge.service 2>/dev/null || true
168
- test -e ~/.foxclaw || cp -a ~/.telegram-codex-app-bridge ~/.foxclaw
169
- foxclaw start
170
- ```
171
-
172
- macOS launchd 用户先卸载旧 plist:
173
-
174
- ```bash
175
- launchctl unload ~/Library/LaunchAgents/com.ganxing.telegram-codex-app-bridge.plist 2>/dev/null || true
176
- foxclaw start
177
- ```
168
+ ## 鸣谢
178
169
 
179
- 旧目录不会被自动读取。如果要保留已有的绑定、线程缓存、审批和状态数据,手动复制一次即可。
170
+ FoxClaw 最初基于 `Gan-Xing/telegram-codex-app-bridge` fork 演进而来,继续以 MIT License 分发。感谢原项目对 Telegram 与 Codex 本地桥接思路的探索。
180
171
 
181
172
  ## Telegram 设置
182
173
 
@@ -284,7 +275,7 @@ foxclaw weixin-login
284
275
 
285
276
  ## 故障排查
286
277
 
287
- `doctor` 报错、Telegram 没回复、服务日志看不懂、重启行为异常、迁移出问题——都看 [故障排查](./docs/zh/troubleshooting.md)。
278
+ `doctor` 报错、Telegram 没回复、服务日志看不懂、重启行为异常——都看 [故障排查](./docs/zh/troubleshooting.md)。
288
279
 
289
280
  ## 运维命令
290
281
 
@@ -292,6 +283,8 @@ foxclaw weixin-login
292
283
  foxclaw doctor
293
284
  foxclaw status
294
285
  foxclaw start
286
+ foxclaw restart
287
+ foxclaw stop
295
288
  foxclaw uninstall-systemd
296
289
  ```
297
290
 
package/README_EN.md CHANGED
@@ -51,7 +51,6 @@ The minimum install needs only a Telegram bot token, your numeric Telegram user
51
51
  ```bash
52
52
  npm install -g @foxden-app/foxclaw
53
53
  foxclaw init
54
- $EDITOR ~/.foxclaw/.env
55
54
  foxclaw doctor
56
55
  foxclaw start
57
56
  ```
@@ -61,12 +60,13 @@ pnpm users:
61
60
  ```bash
62
61
  pnpm add -g @foxden-app/foxclaw
63
62
  foxclaw init
64
- $EDITOR ~/.foxclaw/.env
65
63
  foxclaw doctor
66
64
  foxclaw start
67
65
  ```
68
66
 
69
- Edit `.env` before running `doctor` or `start`. Minimum private-chat config:
67
+ `foxclaw init` creates `~/.foxclaw/.env` and prompts for the Telegram bot token, your numeric Telegram user id, and the default workspace. Press Enter on any field to skip it and edit later with `$EDITOR ~/.foxclaw/.env`.
68
+
69
+ Fill `.env` before running `doctor` or `start`. Minimum private-chat config:
70
70
 
71
71
  ```dotenv
72
72
  TG_BOT_TOKEN=123456:telegram-token
@@ -139,6 +139,14 @@ systemctl --user status foxclaw.service
139
139
  journalctl --user -u foxclaw.service -f
140
140
  ```
141
141
 
142
+ You can also use the wrapper commands:
143
+
144
+ ```bash
145
+ foxclaw status
146
+ foxclaw restart
147
+ foxclaw stop
148
+ ```
149
+
142
150
  For foreground debugging:
143
151
 
144
152
  ```bash
@@ -157,26 +165,9 @@ Default runtime files are stored under `~/.foxclaw`:
157
165
 
158
166
  Override with `STORE_PATH`, `LOCK_PATH`, `CODEX_APP_SERVER_STATE_PATH`, and `CODEX_APP_SERVER_LOG_PATH`.
159
167
 
160
- ## Migrating From telegram-codex-app-bridge
161
-
162
- FoxClaw was originally forked from `Gan-Xing/telegram-codex-app-bridge` and remains distributed under the MIT License.
163
-
164
- When upgrading an existing local install:
165
-
166
- ```bash
167
- systemctl --user disable --now telegram-codex-app-bridge.service 2>/dev/null || true
168
- test -e ~/.foxclaw || cp -a ~/.telegram-codex-app-bridge ~/.foxclaw
169
- foxclaw start
170
- ```
171
-
172
- For launchd installs, unload the old plist if present:
173
-
174
- ```bash
175
- launchctl unload ~/Library/LaunchAgents/com.ganxing.telegram-codex-app-bridge.plist 2>/dev/null || true
176
- foxclaw start
177
- ```
168
+ ## Acknowledgements
178
169
 
179
- The old runtime directory is not read automatically. Copy it once if you want to keep existing bindings, cached thread lists, approvals, and status data.
170
+ FoxClaw originally evolved from a fork of `Gan-Xing/telegram-codex-app-bridge` and remains distributed under the MIT License. Thanks to that project for exploring the Telegram-to-local-Codex bridge pattern.
180
171
 
181
172
  ## Telegram Setup
182
173
 
@@ -284,7 +275,7 @@ This repo ships a Codex skill at [`skills/foxclaw`](./skills/foxclaw). Use it wh
284
275
 
285
276
  ## Troubleshooting
286
277
 
287
- See [Troubleshooting](./docs/troubleshooting.md) for `doctor` failures, Telegram no-reply cases, service logs, reboot behavior, and migration issues.
278
+ See [Troubleshooting](./docs/troubleshooting.md) for `doctor` failures, Telegram no-reply cases, service logs, and reboot behavior.
288
279
 
289
280
  ## Operations
290
281
 
@@ -292,6 +283,8 @@ See [Troubleshooting](./docs/troubleshooting.md) for `doctor` failures, Telegram
292
283
  foxclaw doctor
293
284
  foxclaw status
294
285
  foxclaw start
286
+ foxclaw restart
287
+ foxclaw stop
295
288
  foxclaw uninstall-systemd
296
289
  ```
297
290
 
package/dist/main.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import process from 'node:process';
5
+ import { createInterface } from 'node:readline/promises';
5
6
  import { spawnSync } from 'node:child_process';
6
7
  import { fileURLToPath } from 'node:url';
7
8
  import { APP_HOME, DEFAULT_ENV_PATH, DEFAULT_LOG_PATH, DEFAULT_STATUS_PATH, loadConfig, loadEnv, } from './config.js';
@@ -13,7 +14,7 @@ const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
13
14
  const entryPoint = fileURLToPath(import.meta.url);
14
15
  async function main() {
15
16
  if (command === 'init') {
16
- initConfig();
17
+ await initConfig();
17
18
  return;
18
19
  }
19
20
  if (command === 'install-systemd') {
@@ -23,7 +24,16 @@ async function main() {
23
24
  }
24
25
  if (command === 'start') {
25
26
  requireNode24(command);
26
- startService();
27
+ startService('start');
28
+ return;
29
+ }
30
+ if (command === 'restart') {
31
+ requireNode24(command);
32
+ startService('restart');
33
+ return;
34
+ }
35
+ if (command === 'stop') {
36
+ stopService();
27
37
  return;
28
38
  }
29
39
  if (command === 'uninstall-systemd') {
@@ -134,22 +144,182 @@ async function runServeCli() {
134
144
  throw error;
135
145
  }
136
146
  }
137
- function initConfig() {
147
+ async function initConfig() {
138
148
  const envPath = process.env.FOXCLAW_ENV?.trim() || DEFAULT_ENV_PATH;
139
149
  fs.mkdirSync(path.dirname(envPath), { recursive: true });
140
- if (fs.existsSync(envPath)) {
150
+ const existed = fs.existsSync(envPath);
151
+ if (existed) {
141
152
  console.log(`Config already exists: ${envPath}`);
153
+ }
154
+ else {
155
+ const examplePath = path.join(packageRoot, '.env.example');
156
+ fs.copyFileSync(examplePath, envPath);
157
+ console.log(`Created ${envPath}`);
158
+ }
159
+ if (!canPromptForInit()) {
160
+ console.log(`Edit it manually, then run: foxclaw doctor`);
142
161
  return;
143
162
  }
144
- const examplePath = path.join(packageRoot, '.env.example');
145
- fs.copyFileSync(examplePath, envPath);
146
- console.log(`Created ${envPath}`);
147
- console.log('Edit it, then run: foxclaw doctor');
163
+ await configureEnvInteractively(envPath, existed);
164
+ }
165
+ function canPromptForInit() {
166
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.argv.includes('--no-input') && !process.argv.includes('--skip-prompts'));
167
+ }
168
+ async function configureEnvInteractively(envPath, existed) {
169
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
170
+ try {
171
+ if (existed) {
172
+ const updateExisting = (await rl.question('Update Telegram/workspace setup fields now? [y/N]: ')).trim().toLowerCase();
173
+ if (updateExisting !== 'y' && updateExisting !== 'yes') {
174
+ console.log(`Edit it manually, then run: foxclaw doctor`);
175
+ return;
176
+ }
177
+ }
178
+ else {
179
+ console.log('Interactive setup. Press Enter to skip any field and edit it later.');
180
+ }
181
+ const updates = {};
182
+ const skipped = [];
183
+ const warnings = [];
184
+ const token = sanitizeEnvInput(await rl.question('Telegram bot token (TG_BOT_TOKEN): '));
185
+ if (token) {
186
+ updates.TG_BOT_TOKEN = token;
187
+ if (!/^\d+:[A-Za-z0-9_-]+$/.test(token)) {
188
+ warnings.push('TG_BOT_TOKEN does not look like a standard Telegram bot token.');
189
+ }
190
+ }
191
+ else {
192
+ skipped.push('TG_BOT_TOKEN');
193
+ }
194
+ const userId = sanitizeEnvInput(await rl.question('Telegram numeric user ID (TG_ALLOWED_USER_ID): '));
195
+ if (userId) {
196
+ if (/^\d+$/.test(userId)) {
197
+ updates.TG_ALLOWED_USER_ID = userId;
198
+ }
199
+ else {
200
+ skipped.push('TG_ALLOWED_USER_ID');
201
+ warnings.push('TG_ALLOWED_USER_ID must be numeric; use the Id from @userinfobot, not @username.');
202
+ }
203
+ }
204
+ else {
205
+ skipped.push('TG_ALLOWED_USER_ID');
206
+ }
207
+ const cwdDefault = defaultInitCwd();
208
+ const cwdPrompt = cwdDefault
209
+ ? `Default Codex workspace (DEFAULT_CWD) [${cwdDefault}]: `
210
+ : 'Default Codex workspace (DEFAULT_CWD): ';
211
+ const cwdAnswer = sanitizeEnvInput(await rl.question(cwdPrompt));
212
+ const cwd = cwdAnswer ? normalizeUserPath(cwdAnswer) : cwdDefault;
213
+ if (cwd) {
214
+ updates.DEFAULT_CWD = cwd;
215
+ warnings.push(...validateDefaultCwd(cwd));
216
+ }
217
+ else {
218
+ skipped.push('DEFAULT_CWD');
219
+ }
220
+ const codexBin = resolveCommand('codex');
221
+ if (codexBin) {
222
+ updates.CODEX_CLI_BIN = codexBin;
223
+ }
224
+ const updatedKeys = Object.keys(updates);
225
+ if (updatedKeys.length > 0) {
226
+ writeEnvUpdates(envPath, updates);
227
+ console.log(`Saved ${updatedKeys.join(', ')} to ${envPath}`);
228
+ }
229
+ else {
230
+ console.log('No setup fields changed.');
231
+ }
232
+ for (const warning of warnings) {
233
+ console.log(`[WARN] ${warning}`);
234
+ }
235
+ if (skipped.length > 0) {
236
+ console.log(`Skipped ${skipped.join(', ')}. Edit later: ${editorCommand(envPath)}`);
237
+ }
238
+ console.log('Next: foxclaw doctor');
239
+ }
240
+ finally {
241
+ rl.close();
242
+ }
243
+ }
244
+ function sanitizeEnvInput(value) {
245
+ return value.replace(/[\r\n]/g, '').trim();
246
+ }
247
+ function defaultInitCwd() {
248
+ const cwd = path.resolve(process.cwd());
249
+ if (isUnsafeDefaultCwd(cwd))
250
+ return null;
251
+ try {
252
+ if (fs.statSync(cwd).isDirectory())
253
+ return cwd;
254
+ }
255
+ catch {
256
+ return null;
257
+ }
258
+ return null;
259
+ }
260
+ function normalizeUserPath(value) {
261
+ const expanded = value === '~' || value.startsWith('~/')
262
+ ? path.join(process.env.HOME || '', value.slice(2))
263
+ : value;
264
+ return path.resolve(expanded);
148
265
  }
149
- function startService() {
266
+ function validateDefaultCwd(cwd) {
267
+ const warnings = [];
268
+ if (!path.isAbsolute(cwd)) {
269
+ warnings.push('DEFAULT_CWD should be an absolute path.');
270
+ }
271
+ if (isUnsafeDefaultCwd(cwd)) {
272
+ warnings.push('DEFAULT_CWD points at a very broad directory; use a project/workspace folder for the first install.');
273
+ }
274
+ try {
275
+ if (!fs.statSync(cwd).isDirectory()) {
276
+ warnings.push('DEFAULT_CWD exists but is not a directory.');
277
+ }
278
+ }
279
+ catch {
280
+ warnings.push('DEFAULT_CWD does not exist yet; create it or edit the path before starting.');
281
+ }
282
+ return warnings;
283
+ }
284
+ function isUnsafeDefaultCwd(cwd) {
285
+ const resolved = path.resolve(cwd);
286
+ const home = process.env.HOME ? path.resolve(process.env.HOME) : '';
287
+ return resolved === path.parse(resolved).root || resolved === home || resolved === '/home' || resolved === '/Users';
288
+ }
289
+ function writeEnvUpdates(envPath, updates) {
290
+ let text = fs.readFileSync(envPath, 'utf8');
291
+ const newline = text.includes('\r\n') ? '\r\n' : '\n';
292
+ for (const [key, value] of Object.entries(updates)) {
293
+ const line = `${key}=${formatEnvValue(value)}`;
294
+ const pattern = new RegExp(`^${escapeRegExp(key)}=.*$`, 'm');
295
+ if (pattern.test(text)) {
296
+ text = text.replace(pattern, line);
297
+ }
298
+ else {
299
+ if (text && !text.endsWith('\n') && !text.endsWith('\r\n')) {
300
+ text += newline;
301
+ }
302
+ text += `${line}${newline}`;
303
+ }
304
+ }
305
+ fs.writeFileSync(envPath, text);
306
+ }
307
+ function formatEnvValue(value) {
308
+ const cleaned = value.replace(/[\r\n]/g, '').trim();
309
+ if (!/[\s#"\\]/.test(cleaned))
310
+ return cleaned;
311
+ return `"${cleaned.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
312
+ }
313
+ function escapeRegExp(value) {
314
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
315
+ }
316
+ function editorCommand(envPath) {
317
+ return `${process.env.EDITOR?.trim() || '$EDITOR'} ${envPath}`;
318
+ }
319
+ function startService(action) {
150
320
  if (!runDoctorChecks()) {
151
321
  console.error('');
152
- console.error('Fix the failed checks above, then run: foxclaw start');
322
+ console.error(`Fix the failed checks above, then run: foxclaw ${action}`);
153
323
  process.exit(1);
154
324
  }
155
325
  if (process.platform === 'darwin') {
@@ -158,6 +328,13 @@ function startService() {
158
328
  }
159
329
  installSystemd();
160
330
  }
331
+ function stopService() {
332
+ if (process.platform === 'darwin') {
333
+ stopLaunchd();
334
+ return;
335
+ }
336
+ stopSystemd();
337
+ }
161
338
  function runDoctorChecks() {
162
339
  const configuredCodexBin = process.env.CODEX_CLI_BIN;
163
340
  const checks = [
@@ -263,6 +440,15 @@ function uninstallSystemd() {
263
440
  spawnChecked('systemctl', ['--user', 'daemon-reload']);
264
441
  console.log(`Removed ${unitPath}`);
265
442
  }
443
+ function stopSystemd() {
444
+ if (!hasCommand('systemctl')) {
445
+ console.error('systemctl not found');
446
+ process.exit(1);
447
+ }
448
+ const unitName = 'foxclaw.service';
449
+ spawnChecked('systemctl', ['--user', 'stop', unitName]);
450
+ console.log(`Stopped ${unitName}`);
451
+ }
266
452
  function installLaunchd() {
267
453
  if (process.platform !== 'darwin') {
268
454
  console.error('launchd install is only available on macOS');
@@ -320,6 +506,19 @@ ${foxclawEnvXml}
320
506
  spawnChecked('launchctl', ['load', plist]);
321
507
  console.log(`Installed ${plist}`);
322
508
  }
509
+ function stopLaunchd() {
510
+ if (process.platform !== 'darwin') {
511
+ console.error('launchd stop is only available on macOS');
512
+ process.exit(1);
513
+ }
514
+ const plist = path.join(process.env.HOME || '', 'Library', 'LaunchAgents', 'app.foxden.foxclaw.plist');
515
+ if (!fs.existsSync(plist)) {
516
+ console.error(`launchd plist not found: ${plist}`);
517
+ process.exit(1);
518
+ }
519
+ spawnChecked('launchctl', ['unload', plist]);
520
+ console.log(`Stopped ${plist}`);
521
+ }
323
522
  function buildServicePath(nodeDir) {
324
523
  const parts = [
325
524
  path.join(process.env.HOME || '', '.local', 'bin'),
@@ -425,13 +624,18 @@ void main().catch((error) => {
425
624
  process.exit(1);
426
625
  });
427
626
  function hasCommand(commandName) {
627
+ return Boolean(resolveCommand(commandName));
628
+ }
629
+ function resolveCommand(commandName) {
428
630
  try {
429
631
  const which = process.platform === 'win32' ? 'where' : 'which';
430
- const result = spawnSync(which, [commandName], { stdio: 'ignore' });
431
- return result.status === 0;
632
+ const result = spawnSync(which, [commandName], { encoding: 'utf8' });
633
+ if (result.status !== 0)
634
+ return null;
635
+ return result.stdout.split(/\r?\n/).map(line => line.trim()).find(Boolean) ?? null;
432
636
  }
433
637
  catch {
434
- return false;
638
+ return null;
435
639
  }
436
640
  }
437
641
  function hasConfiguredCodexBin(binPath) {
@@ -38,7 +38,7 @@ TG_ALLOWED_USER_ID=<paste numeric Telegram user id here>
38
38
  DEFAULT_CWD=<paste absolute working directory here>
39
39
 
40
40
  Tasks:
41
- 1. Inspect the machine first. If a FoxClaw or old telegram-codex-app-bridge service already exists, report it before changing services.
41
+ 1. Inspect the machine first. If a FoxClaw service already exists, report it before changing services.
42
42
  2. Ensure Node.js 24+ is available. If not, install or activate Node 24 with nvm.
43
43
  3. Ensure the Codex CLI exists and is logged in. If login is required, stop and tell me exactly what I need to do.
44
44
  4. Install or update FoxClaw with npm install -g @foxden-app/foxclaw@latest.
@@ -48,33 +48,10 @@ Tasks:
48
48
  8. Ask me to send /help and /status to the Telegram bot.
49
49
  9. Verify the final state:
50
50
  - foxclaw.service is active/enabled on Linux
51
- - old telegram-codex-app-bridge.service is inactive/disabled if present
52
51
  - foxclaw status works
53
52
  10. Report the commands used, the final status, and the log command I should use if something stops working. Redact TG_BOT_TOKEN and never print the full token or full .env content.
54
53
  ```
55
54
 
56
- ## Migration Prompt For Old Installs
57
-
58
- Use this when the target computer is still running `telegram-codex-app-bridge`:
59
-
60
- ```text
61
- Migrate this machine from telegram-codex-app-bridge to FoxClaw.
62
-
63
- New package:
64
- @foxden-app/foxclaw
65
-
66
- Please:
67
- 1. Inspect the current install method, service file, and runtime directory before changing anything.
68
- 2. Stop and disable telegram-codex-app-bridge.service if it exists.
69
- 3. If ~/.foxclaw does not exist and ~/.telegram-codex-app-bridge exists, copy ~/.telegram-codex-app-bridge to ~/.foxclaw.
70
- 4. Install or update FoxClaw with npm install -g @foxden-app/foxclaw@latest.
71
- 5. Run foxclaw init if ~/.foxclaw/.env does not exist, then verify ~/.foxclaw/.env.
72
- 6. Run foxclaw doctor.
73
- 7. Install or restart the FoxClaw service with foxclaw start.
74
- 8. Verify foxclaw.service is active and telegram-codex-app-bridge.service is inactive/disabled.
75
- 9. Report final status and any blockers. Redact TG_BOT_TOKEN and never print the full token or full .env content.
76
- ```
77
-
78
55
  ## Safety Notes
79
56
 
80
57
  - Do not paste bot tokens into public issue trackers or public chat logs.
@@ -123,7 +123,7 @@ npm install -g @foxden-app/foxclaw
123
123
  foxclaw init
124
124
  ```
125
125
 
126
- This creates the config file at `~/.foxclaw/.env`.
126
+ This creates the config file at `~/.foxclaw/.env` and prompts for the Telegram bot token, your numeric Telegram user id, and the default workspace.
127
127
 
128
128
  If you prefer pnpm:
129
129
 
@@ -134,7 +134,7 @@ foxclaw init
134
134
 
135
135
  ## 7. Fill In `.env`
136
136
 
137
- Open `.env` in a simple editor:
137
+ If you filled the token, user id, and workspace during `foxclaw init`, go straight to the first check. If you skipped a field or want to change anything, open `.env` in a simple editor:
138
138
 
139
139
  ```bash
140
140
  nano ~/.foxclaw/.env
@@ -259,13 +259,13 @@ foxclaw status
259
259
  Restart Linux service after changing `.env`:
260
260
 
261
261
  ```bash
262
- foxclaw start
262
+ foxclaw restart
263
263
  ```
264
264
 
265
- Stop Linux service:
265
+ Stop the service:
266
266
 
267
267
  ```bash
268
- systemctl --user stop foxclaw.service
268
+ foxclaw stop
269
269
  ```
270
270
 
271
271
  Uninstall Linux service:
@@ -93,7 +93,7 @@ Check these in order:
93
93
  5. Restart after changing `.env`:
94
94
 
95
95
  ```bash
96
- foxclaw start
96
+ foxclaw restart
97
97
  ```
98
98
 
99
99
  If running foreground mode, stop with `Ctrl+C` and run `foxclaw serve` again.
@@ -129,23 +129,17 @@ If FoxClaw is still running, it may consume the update before you inspect it.
129
129
 
130
130
  If Telegram reports conflicts or the same bot behaves strangely, two processes may be polling the same bot token.
131
131
 
132
- Check old and new services:
132
+ Check the service and any extra foreground processes:
133
133
 
134
134
  ```bash
135
135
  systemctl --user is-active foxclaw.service
136
- systemctl --user is-active telegram-codex-app-bridge.service 2>/dev/null || true
136
+ pgrep -af foxclaw
137
137
  ```
138
138
 
139
- Stop the old service:
139
+ Stop the extra process or service, then restart FoxClaw:
140
140
 
141
141
  ```bash
142
- systemctl --user disable --now telegram-codex-app-bridge.service
143
- ```
144
-
145
- Then restart FoxClaw:
146
-
147
- ```bash
148
- foxclaw start
142
+ foxclaw restart
149
143
  ```
150
144
 
151
145
  ## Codex Or App-Server Fails
@@ -219,18 +213,3 @@ macOS launchd starts FoxClaw when you log in after running:
219
213
  ```bash
220
214
  foxclaw start
221
215
  ```
222
-
223
- ## Migrating From The Old Project Name
224
-
225
- If this machine still runs `telegram-codex-app-bridge`, migrate once:
226
-
227
- ```bash
228
- systemctl --user disable --now telegram-codex-app-bridge.service 2>/dev/null || true
229
- test -e ~/.foxclaw || cp -a ~/.telegram-codex-app-bridge ~/.foxclaw
230
- npm install -g @foxden-app/foxclaw@latest
231
- foxclaw init
232
- foxclaw doctor
233
- foxclaw start
234
- ```
235
-
236
- If `~/.foxclaw/.env` already exists, `foxclaw init` leaves it untouched.
@@ -100,7 +100,7 @@ Both install the same published npm package. Use one global package manager cons
100
100
 
101
101
  ### 1.6 Fill In The Config
102
102
 
103
- The default config file is `~/.foxclaw/.env`:
103
+ `foxclaw init` creates the default config file at `~/.foxclaw/.env` and prompts for the Telegram bot token, your numeric Telegram user id, and the default workspace. Press Enter on any field to skip it, then edit manually if needed:
104
104
 
105
105
  ```bash
106
106
  $EDITOR ~/.foxclaw/.env
@@ -146,6 +146,7 @@ journalctl --user -u foxclaw.service -f
146
146
  On macOS, `foxclaw start` manages launchd. For foreground debugging, stop the background service and run:
147
147
 
148
148
  ```bash
149
+ foxclaw stop
149
150
  foxclaw serve
150
151
  ```
151
152
 
@@ -37,7 +37,7 @@ TG_ALLOWED_USER_ID=<把 Telegram 数字用户 ID 粘贴在这里>
37
37
  DEFAULT_CWD=<把绝对工作目录粘贴在这里>
38
38
 
39
39
  任务:
40
- 1. 先检查机器环境。如果已经存在 FoxClaw 或旧的 telegram-codex-app-bridge 服务,先报告再改服务。
40
+ 1. 先检查机器环境。如果已经存在 FoxClaw 服务,先报告再改服务。
41
41
  2. 确保 Node.js 24+ 可用;如果没有,请用 nvm 安装或切到 Node 24。
42
42
  3. 确保 Codex CLI 存在并且已经登录。如果需要登录,停下来告诉我具体要执行什么。
43
43
  4. 用 npm install -g @foxden-app/foxclaw@latest 安装或升级 FoxClaw。
@@ -47,33 +47,10 @@ DEFAULT_CWD=<把绝对工作目录粘贴在这里>
47
47
  8. 让我在 Telegram bot 里发送 /help 和 /status。
48
48
  9. 验证最终状态:
49
49
  - Linux 上 foxclaw.service 处于 active/enabled
50
- - 如果存在旧 telegram-codex-app-bridge.service,它应当 inactive/disabled
51
50
  - foxclaw status 可以正常输出
52
51
  10. 汇报执行过的命令、最终状态和后续看日志的命令。请隐藏 TG_BOT_TOKEN,不要打印完整 token 或完整 .env。
53
52
  ```
54
53
 
55
- ## 旧项目迁移提示词
56
-
57
- 如果目标机器还在跑 `telegram-codex-app-bridge`,用这段:
58
-
59
- ```text
60
- 请把这台机器从 telegram-codex-app-bridge 迁移到 FoxClaw。
61
-
62
- 新发布包:
63
- @foxden-app/foxclaw
64
-
65
- 请执行:
66
- 1. 修改前先检查现有安装方式、服务文件和运行目录。
67
- 2. 如果存在 telegram-codex-app-bridge.service,停止并禁用它。
68
- 3. 如果 ~/.foxclaw 不存在而 ~/.telegram-codex-app-bridge 存在,把旧目录复制到 ~/.foxclaw。
69
- 4. 用 npm install -g @foxden-app/foxclaw@latest 安装或升级 FoxClaw。
70
- 5. 如果 ~/.foxclaw/.env 不存在,运行 foxclaw init;然后检查配置是否完整。
71
- 6. 运行 foxclaw doctor。
72
- 7. 用 foxclaw start 安装或重启 FoxClaw 服务。
73
- 8. 验证 foxclaw.service 正常运行,旧 telegram-codex-app-bridge.service 已停止或禁用。
74
- 9. 汇报最终状态和阻塞点。请隐藏 TG_BOT_TOKEN,不要打印完整 token 或完整 .env。
75
- ```
76
-
77
54
  ## 安全注意事项
78
55
 
79
56
  - 不要把 bot token 粘贴到公开 issue、公开聊天或代码仓库。
@@ -121,7 +121,7 @@ npm install -g @foxden-app/foxclaw
121
121
  foxclaw init
122
122
  ```
123
123
 
124
- 这会创建默认配置文件 `~/.foxclaw/.env`。
124
+ 这会创建默认配置文件 `~/.foxclaw/.env`,并提示你填写 Telegram bot token、Telegram 数字用户 ID 和默认工作目录。
125
125
 
126
126
  如果你用 pnpm:
127
127
 
@@ -132,7 +132,7 @@ foxclaw init
132
132
 
133
133
  ## 7. 填写 `.env`
134
134
 
135
- 用简单编辑器打开配置:
135
+ 如果刚才在 `foxclaw init` 里已经填好了 token、用户 ID 和工作目录,可以直接进入下一步检查。如果你选择了跳过,或者想改配置,用简单编辑器打开:
136
136
 
137
137
  ```bash
138
138
  nano ~/.foxclaw/.env
@@ -257,13 +257,13 @@ foxclaw status
257
257
  修改 `.env` 后重启:
258
258
 
259
259
  ```bash
260
- foxclaw start
260
+ foxclaw restart
261
261
  ```
262
262
 
263
- 停止 Linux 服务:
263
+ 停止服务:
264
264
 
265
265
  ```bash
266
- systemctl --user stop foxclaw.service
266
+ foxclaw stop
267
267
  ```
268
268
 
269
269
  卸载 Linux 服务:
@@ -279,20 +279,11 @@ npm install -g @foxden-app/foxclaw@latest
279
279
  foxclaw start
280
280
  ```
281
281
 
282
- ## 从旧项目名迁移
283
-
284
- 如果这台机器仍在运行 `telegram-codex-app-bridge`,迁移一次即可:
282
+ 如果 `~/.foxclaw/.env` 已经存在,`foxclaw init` 会先询问是否更新 Telegram 和工作目录相关字段,其它配置保持不变。
285
283
 
286
- ```bash
287
- systemctl --user disable --now telegram-codex-app-bridge.service 2>/dev/null || true
288
- test -e ~/.foxclaw || cp -a ~/.telegram-codex-app-bridge ~/.foxclaw
289
- npm install -g @foxden-app/foxclaw@latest
290
- foxclaw init
291
- foxclaw doctor
292
- foxclaw start
293
- ```
284
+ ## 鸣谢
294
285
 
295
- 如果 `~/.foxclaw/.env` 已经存在,`foxclaw init` 不会覆盖它。
286
+ FoxClaw 最初基于 `Gan-Xing/telegram-codex-app-bridge` fork 演进而来。感谢原项目对 Telegram 与 Codex 本地桥接思路的探索。
296
287
 
297
288
  ## 下一步
298
289
 
@@ -94,7 +94,7 @@ CODEX_CLI_BIN=/absolute/path/to/codex
94
94
  5. 修改 `.env` 后重启:
95
95
 
96
96
  ```bash
97
- foxclaw start
97
+ foxclaw restart
98
98
  ```
99
99
 
100
100
  如果正在前台运行,先 `Ctrl+C` 停止,再重新运行 `foxclaw serve`。
@@ -130,23 +130,17 @@ CODEX_CLI_BIN=/absolute/path/to/codex
130
130
 
131
131
  如果 Telegram 报 conflict,或者同一个 bot 行为异常,通常是两个进程在轮询同一个 bot token。
132
132
 
133
- 检查新旧服务:
133
+ 检查服务和额外的前台进程:
134
134
 
135
135
  ```bash
136
136
  systemctl --user is-active foxclaw.service
137
- systemctl --user is-active telegram-codex-app-bridge.service 2>/dev/null || true
137
+ pgrep -af foxclaw
138
138
  ```
139
139
 
140
- 停掉旧服务:
140
+ 停掉多余进程或服务后,重启 FoxClaw:
141
141
 
142
142
  ```bash
143
- systemctl --user disable --now telegram-codex-app-bridge.service
144
- ```
145
-
146
- 然后重启 FoxClaw:
147
-
148
- ```bash
149
- foxclaw start
143
+ foxclaw restart
150
144
  ```
151
145
 
152
146
  ## Codex 或 app-server 异常
@@ -100,7 +100,7 @@ foxclaw init
100
100
 
101
101
  ### 1.6 填写配置
102
102
 
103
- 默认配置文件是 `~/.foxclaw/.env`:
103
+ `foxclaw init` 会创建默认配置文件 `~/.foxclaw/.env`,并提示填写 Telegram bot token、Telegram 数字用户 ID 和默认工作目录。任何一项都可以直接回车跳过,之后再手动编辑:
104
104
 
105
105
  ```bash
106
106
  $EDITOR ~/.foxclaw/.env
@@ -146,6 +146,7 @@ journalctl --user -u foxclaw.service -f
146
146
  macOS 上 `foxclaw start` 会管理 launchd。前台排障时,先停后台服务,再运行:
147
147
 
148
148
  ```bash
149
+ foxclaw stop
149
150
  foxclaw serve
150
151
  ```
151
152
 
package/package.json CHANGED
@@ -1,65 +1,67 @@
1
- {
2
- "name": "@foxden-app/foxclaw",
3
- "version": "0.3.0",
4
- "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
- "type": "module",
6
- "main": "dist/main.js",
7
- "bin": {
8
- "foxclaw": "dist/main.js"
9
- },
10
- "files": [
11
- "dist",
12
- "docs",
13
- "scripts",
14
- "skills",
15
- ".env.example",
16
- "README.md",
17
- "README_EN.md",
18
- "LICENSE"
19
- ],
20
- "private": false,
21
- "repository": {
22
- "type": "git",
23
- "url": "git+https://github.com/foxden-app/foxclaw.git"
24
- },
25
- "homepage": "https://github.com/foxden-app/foxclaw#readme",
26
- "bugs": {
27
- "url": "https://github.com/foxden-app/foxclaw/issues"
28
- },
29
- "publishConfig": {
30
- "access": "public"
31
- },
32
- "engines": {
33
- "node": ">=24"
34
- },
35
- "scripts": {
36
- "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
37
- "build": "npm run clean && tsc -p tsconfig.build.json",
38
- "dev": "tsx src/main.ts serve",
39
- "serve": "node dist/main.js serve",
40
- "start-service": "node dist/main.js start",
41
- "weixin-login": "node dist/main.js weixin-login",
42
- "status": "node dist/main.js status",
43
- "doctor": "node dist/main.js doctor",
44
- "init": "node dist/main.js init",
45
- "install-systemd": "node dist/main.js install-systemd",
46
- "uninstall-systemd": "node dist/main.js uninstall-systemd",
47
- "install-launchd": "node dist/main.js install-launchd",
48
- "typecheck": "tsc --noEmit",
49
- "lint": "eslint .",
50
- "test": "node --test --import tsx \"src/**/*.test.ts\"",
51
- "prepack": "npm run build"
52
- },
53
- "dependencies": {
54
- "dotenv": "^16.6.1",
55
- "qrcode-terminal": "^0.12.0"
56
- },
57
- "devDependencies": {
58
- "@eslint/js": "^9.39.0",
59
- "@types/node": "^24.12.0",
60
- "eslint": "^9.39.0",
61
- "tsx": "^4.19.3",
62
- "typescript": "5.9.3",
63
- "typescript-eslint": "^8.58.0"
64
- }
65
- }
1
+ {
2
+ "name": "@foxden-app/foxclaw",
3
+ "version": "0.3.2",
4
+ "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
+ "type": "module",
6
+ "main": "dist/main.js",
7
+ "bin": {
8
+ "foxclaw": "dist/main.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "docs",
13
+ "scripts",
14
+ "skills",
15
+ ".env.example",
16
+ "README.md",
17
+ "README_EN.md",
18
+ "LICENSE"
19
+ ],
20
+ "private": false,
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/foxden-app/foxclaw.git"
24
+ },
25
+ "homepage": "https://github.com/foxden-app/foxclaw#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/foxden-app/foxclaw/issues"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "engines": {
33
+ "node": ">=24"
34
+ },
35
+ "scripts": {
36
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
37
+ "build": "npm run clean && tsc -p tsconfig.build.json",
38
+ "dev": "tsx src/main.ts serve",
39
+ "serve": "node dist/main.js serve",
40
+ "start-service": "node dist/main.js start",
41
+ "stop-service": "node dist/main.js stop",
42
+ "restart-service": "node dist/main.js restart",
43
+ "weixin-login": "node dist/main.js weixin-login",
44
+ "status": "node dist/main.js status",
45
+ "doctor": "node dist/main.js doctor",
46
+ "init": "node dist/main.js init",
47
+ "install-systemd": "node dist/main.js install-systemd",
48
+ "uninstall-systemd": "node dist/main.js uninstall-systemd",
49
+ "install-launchd": "node dist/main.js install-launchd",
50
+ "typecheck": "tsc --noEmit",
51
+ "lint": "eslint .",
52
+ "test": "node --test --import tsx \"src/**/*.test.ts\"",
53
+ "prepack": "npm run build"
54
+ },
55
+ "dependencies": {
56
+ "dotenv": "^16.6.1",
57
+ "qrcode-terminal": "^0.12.0"
58
+ },
59
+ "devDependencies": {
60
+ "@eslint/js": "^9.39.0",
61
+ "@types/node": "^24.12.0",
62
+ "eslint": "^9.39.0",
63
+ "tsx": "^4.19.3",
64
+ "typescript": "5.9.3",
65
+ "typescript-eslint": "^8.58.0"
66
+ }
67
+ }