@foxden-app/foxclaw 0.3.10 → 0.3.12

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/.env.example CHANGED
@@ -29,12 +29,15 @@ TELEGRAM_PREVIEW_THROTTLE_MS=800
29
29
  THREAD_LIST_LIMIT=10
30
30
  CODEX_CLI_BIN=/absolute/path/to/codex
31
31
 
32
- # Optional: proxy for ChatGPT/Codex backend requests when FoxClaw runs as a service.
32
+ # Optional: standard HTTP(S) proxy for Telegram and ChatGPT/Codex backend requests.
33
33
  # Put these in the same env file that `foxclaw start` installs into systemd/launchd.
34
+ # FoxClaw passes them to the service and enables Node's env proxy support.
34
35
  # HTTP_PROXY=http://127.0.0.1:7890
35
36
  # HTTPS_PROXY=http://127.0.0.1:7890
36
37
  # ALL_PROXY=socks5://127.0.0.1:7891
37
38
  # NO_PROXY=127.0.0.1,localhost
39
+ # Optional Linux-only fallback when a service must run through proxychains4.
40
+ # FOXCLAW_PROXYCHAINS_CONF=/absolute/path/to/proxychains.conf
38
41
 
39
42
  # Weixin (iLink): run `foxclaw weixin-login` once, then enable:
40
43
  # WX_ENABLED=true
package/dist/main.js CHANGED
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url';
8
8
  import { APP_HOME, DEFAULT_ENV_PATH, DEFAULT_LOG_PATH, DEFAULT_STATUS_PATH, getLoadedEnvPath, loadConfig, loadEnv, } from './config.js';
9
9
  import { acquireProcessLock, LockHeldError } from './lock.js';
10
10
  import { readRuntimeStatus, writeRuntimeStatus } from './runtime.js';
11
+ import { refreshFoxclawExecStartDropIns, removeFoxclawExecStartDropIns } from './systemd.js';
11
12
  const rawCommand = process.argv[2];
12
13
  const command = rawCommand || 'serve';
13
14
  loadEnv();
@@ -23,6 +24,7 @@ const PROXY_ENV_KEYS = [
23
24
  'all_proxy',
24
25
  'no_proxy',
25
26
  ];
27
+ const STANDARD_NODE_PROXY_ENV_KEYS = ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy'];
26
28
  async function main() {
27
29
  if (isVersionCommand(command)) {
28
30
  console.log(readPackageVersion());
@@ -492,9 +494,38 @@ function runDoctorChecks() {
492
494
  passed = false;
493
495
  }
494
496
  warnIfProxyEnvMissingFromLoadedEnv();
497
+ warnIfProxyConfigNeedsAttention();
495
498
  warnIfInstalledServiceNodeLooksWrong();
496
499
  return passed;
497
500
  }
501
+ function warnIfProxyConfigNeedsAttention() {
502
+ const proxychainsConf = process.env.FOXCLAW_PROXYCHAINS_CONF?.trim() || '';
503
+ if (proxychainsConf) {
504
+ if (process.platform !== 'linux') {
505
+ console.log('[WARN] FOXCLAW_PROXYCHAINS_CONF is only used by systemd on Linux.');
506
+ }
507
+ else if (!fs.existsSync(proxychainsConf)) {
508
+ console.log(`[WARN] FOXCLAW_PROXYCHAINS_CONF does not exist: ${proxychainsConf}`);
509
+ }
510
+ else if (!hasCommand('proxychains4')) {
511
+ console.log('[WARN] proxychains4 is not available, but FOXCLAW_PROXYCHAINS_CONF is set.');
512
+ }
513
+ else {
514
+ console.log(`[OK] proxychains config exists: ${proxychainsConf}`);
515
+ }
516
+ return;
517
+ }
518
+ const proxyKeys = PROXY_ENV_KEYS.filter((key) => proxyEnvValue(key));
519
+ if (proxyKeys.length === 0) {
520
+ return;
521
+ }
522
+ if (hasStandardNodeProxyEnv()) {
523
+ console.log(`[OK] service proxy env configured: ${proxyKeys.join(', ')}`);
524
+ return;
525
+ }
526
+ console.log('[WARN] Only ALL_PROXY/all_proxy is configured. Node service proxying works best with HTTP_PROXY/HTTPS_PROXY.');
527
+ console.log('[WARN] For SOCKS-only hosts, set FOXCLAW_PROXYCHAINS_CONF=/absolute/path/to/proxychains.conf and run foxclaw restart.');
528
+ }
498
529
  function warnIfProxyEnvMissingFromLoadedEnv() {
499
530
  const envPath = serviceEnvPath();
500
531
  const proxyUpdates = detectMissingProxyEnv(envPath);
@@ -518,7 +549,7 @@ function warnIfInstalledServiceNodeLooksWrong() {
518
549
  return;
519
550
  }
520
551
  const execStart = text.match(/^ExecStart=(.+)$/m)?.[1]?.trim();
521
- const nodePath = execStart ? systemdUnescape(execStart.split(/\s+/)[0] ?? '') : '';
552
+ const nodePath = execStart ? extractNodePathFromExecStart(execStart) : '';
522
553
  if (!nodePath) {
523
554
  return;
524
555
  }
@@ -537,6 +568,14 @@ function warnIfInstalledServiceNodeLooksWrong() {
537
568
  console.log(`[WARN] installed service node is older than 24: ${nodePath}${version ? ` (${version})` : ''}`);
538
569
  console.log('[WARN] Run foxclaw start from a Node 24 shell to refresh the service unit.');
539
570
  }
571
+ function extractNodePathFromExecStart(execStart) {
572
+ const tokens = execStart.split(/\s+/).map(systemdUnescape).filter(Boolean);
573
+ const directNode = tokens[0] || '';
574
+ if (path.basename(directNode) === 'node') {
575
+ return directNode;
576
+ }
577
+ return tokens.find((token) => path.basename(token) === 'node') || directNode;
578
+ }
540
579
  function installSystemd() {
541
580
  if (!hasCommand('systemctl')) {
542
581
  console.error('systemctl not found (need systemd)');
@@ -550,9 +589,16 @@ function installSystemd() {
550
589
  const nodeBin = process.execPath;
551
590
  const nodeDir = path.dirname(nodeBin);
552
591
  const pathValue = buildServicePath(nodeDir);
592
+ const proxychainsConf = process.env.FOXCLAW_PROXYCHAINS_CONF?.trim() || '';
593
+ const proxychainsBin = proxychainsConf ? resolveCommand('proxychains4') || '/usr/bin/proxychains4' : '';
594
+ const nodeProxyArgs = !proxychainsConf && hasStandardNodeProxyEnv() ? ' --use-env-proxy' : '';
595
+ const execStart = proxychainsConf
596
+ ? `${systemdEscape(proxychainsBin)} -f ${systemdEscape(proxychainsConf)} ${systemdEscape(nodeBin)} ${systemdEscape(entryPoint)} serve`
597
+ : `${systemdEscape(nodeBin)}${nodeProxyArgs} ${systemdEscape(entryPoint)} serve`;
553
598
  fs.mkdirSync(userSystemdDir, { recursive: true });
554
599
  fs.mkdirSync(configDir, { recursive: true });
555
600
  fs.mkdirSync(path.join(APP_HOME, 'logs'), { recursive: true });
601
+ const escapedEntryPoint = systemdEscape(entryPoint);
556
602
  fs.writeFileSync(unitPath, `[Unit]
557
603
  Description=FoxClaw local Codex execution bridge
558
604
  Documentation=https://github.com/foxden-app/foxclaw
@@ -564,12 +610,13 @@ StartLimitBurst=5
564
610
  [Service]
565
611
  Type=simple
566
612
  WorkingDirectory=${systemdEscape(configDir)}
613
+ EnvironmentFile=-${systemdEscape(envPath)}
567
614
  Environment=HOME=${systemdEscape(process.env.HOME || '')}
568
615
  Environment=USER=${systemdEscape(process.env.USER || '')}
569
616
  Environment=LOGNAME=${systemdEscape(process.env.LOGNAME || process.env.USER || '')}
570
617
  Environment=PATH=${systemdEscape(pathValue)}
571
618
  Environment=FOXCLAW_ENV=${systemdEscape(envPath)}
572
- ExecStart=${systemdEscape(nodeBin)} ${systemdEscape(entryPoint)} serve
619
+ ExecStart=${execStart}
573
620
  Restart=always
574
621
  RestartSec=10
575
622
  TimeoutStopSec=45
@@ -578,6 +625,24 @@ KillMode=process
578
625
  [Install]
579
626
  WantedBy=default.target
580
627
  `);
628
+ if (proxychainsConf) {
629
+ console.log(`[OK] systemd proxychains enabled: ${proxychainsConf}`);
630
+ }
631
+ else if (nodeProxyArgs) {
632
+ console.log('[OK] systemd Node env proxy enabled');
633
+ }
634
+ if (proxychainsConf) {
635
+ const dropInUpdates = removeFoxclawExecStartDropIns(userSystemdDir, unitName);
636
+ for (const update of dropInUpdates) {
637
+ console.log(`[OK] removed stale FoxClaw ExecStart override: ${update.path}`);
638
+ }
639
+ }
640
+ else {
641
+ const dropInUpdates = refreshFoxclawExecStartDropIns(userSystemdDir, unitName, escapedEntryPoint);
642
+ for (const update of dropInUpdates) {
643
+ console.log(`[OK] updated FoxClaw ExecStart override: ${update.path}`);
644
+ }
645
+ }
581
646
  spawnChecked('systemctl', ['--user', 'daemon-reload']);
582
647
  spawnChecked('systemctl', ['--user', 'enable', unitName]);
583
648
  const restarted = spawnSync('systemctl', ['--user', 'restart', unitName], { stdio: 'inherit' });
@@ -619,6 +684,9 @@ function installLaunchd() {
619
684
  const plist = path.join(home, 'Library', 'LaunchAgents', 'app.foxden.foxclaw.plist');
620
685
  const envPath = serviceEnvPath();
621
686
  const configDir = path.dirname(envPath);
687
+ const nodeProxyArgs = hasStandardNodeProxyEnv() ? ['--use-env-proxy'] : [];
688
+ const nodeProxyArgXml = nodeProxyArgs.map((arg) => ` <string>${xmlEscape(arg)}</string>`).join('\n');
689
+ const proxyEnvXml = buildLaunchdProxyEnvironmentXml();
622
690
  fs.mkdirSync(path.dirname(plist), { recursive: true });
623
691
  fs.mkdirSync(configDir, { recursive: true });
624
692
  fs.mkdirSync(path.join(APP_HOME, 'logs'), { recursive: true });
@@ -631,7 +699,7 @@ function installLaunchd() {
631
699
  <key>ProgramArguments</key>
632
700
  <array>
633
701
  <string>${xmlEscape(process.execPath)}</string>
634
- <string>${xmlEscape(entryPoint)}</string>
702
+ ${nodeProxyArgXml ? `${nodeProxyArgXml}\n` : ''} <string>${xmlEscape(entryPoint)}</string>
635
703
  <string>serve</string>
636
704
  </array>
637
705
  <key>WorkingDirectory</key>
@@ -648,6 +716,7 @@ function installLaunchd() {
648
716
  <string>${xmlEscape(process.env.LOGNAME || process.env.USER || '')}</string>
649
717
  <key>FOXCLAW_ENV</key>
650
718
  <string>${xmlEscape(envPath)}</string>
719
+ ${proxyEnvXml}
651
720
  </dict>
652
721
  <key>RunAtLoad</key>
653
722
  <true/>
@@ -663,6 +732,9 @@ function installLaunchd() {
663
732
  spawnSync('launchctl', ['unload', plist], { stdio: 'ignore' });
664
733
  spawnChecked('launchctl', ['load', plist]);
665
734
  console.log(`Installed ${plist}`);
735
+ if (nodeProxyArgs.length > 0) {
736
+ console.log('[OK] launchd Node env proxy enabled');
737
+ }
666
738
  }
667
739
  function stopLaunchd() {
668
740
  if (process.platform !== 'darwin') {
@@ -693,6 +765,23 @@ function buildServicePath(nodeDir) {
693
765
  function serviceEnvPath() {
694
766
  return path.resolve(process.env.FOXCLAW_ENV?.trim() || getLoadedEnvPath() || DEFAULT_ENV_PATH);
695
767
  }
768
+ function hasStandardNodeProxyEnv() {
769
+ return STANDARD_NODE_PROXY_ENV_KEYS.some((key) => Boolean(proxyEnvValue(key)));
770
+ }
771
+ function proxyEnvValue(key) {
772
+ return process.env[key]?.trim() || '';
773
+ }
774
+ function buildLaunchdProxyEnvironmentXml() {
775
+ const entries = [];
776
+ for (const key of PROXY_ENV_KEYS) {
777
+ const value = proxyEnvValue(key);
778
+ if (!value)
779
+ continue;
780
+ entries.push(` <key>${xmlEscape(key)}</key>`);
781
+ entries.push(` <string>${xmlEscape(value)}</string>`);
782
+ }
783
+ return entries.length > 0 ? `${entries.join('\n')}\n` : '';
784
+ }
696
785
  function spawnChecked(commandName, args) {
697
786
  const result = spawnSync(commandName, args, { stdio: 'inherit' });
698
787
  if (result.status !== 0) {
@@ -0,0 +1,14 @@
1
+ export interface SystemdDropInUpdate {
2
+ path: string;
3
+ replacements: number;
4
+ }
5
+ export declare function refreshFoxclawExecStartDropIns(userSystemdDir: string, unitName: string, escapedEntryPoint: string): SystemdDropInUpdate[];
6
+ export declare function removeFoxclawExecStartDropIns(userSystemdDir: string, unitName: string): SystemdDropInUpdate[];
7
+ export declare function refreshFoxclawExecStartText(text: string, escapedEntryPoint: string): {
8
+ text: string;
9
+ replacements: number;
10
+ };
11
+ export declare function removeFoxclawExecStartText(text: string): {
12
+ text: string;
13
+ replacements: number;
14
+ };
@@ -0,0 +1,103 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ const FOXCLAW_MAIN_PATH_RE = /\S*(?:\.pnpm\/@foxden-app\+foxclaw@[^/\s]+\/node_modules\/@foxden-app\/foxclaw|node_modules\/@foxden-app\/foxclaw)\/dist\/main\.js/g;
4
+ export function refreshFoxclawExecStartDropIns(userSystemdDir, unitName, escapedEntryPoint) {
5
+ const dropInDir = path.join(userSystemdDir, `${unitName}.d`);
6
+ let names;
7
+ try {
8
+ names = fs.readdirSync(dropInDir).filter((name) => name.endsWith('.conf')).sort();
9
+ }
10
+ catch {
11
+ return [];
12
+ }
13
+ const updates = [];
14
+ for (const name of names) {
15
+ const filePath = path.join(dropInDir, name);
16
+ let before = '';
17
+ try {
18
+ before = fs.readFileSync(filePath, 'utf8');
19
+ }
20
+ catch {
21
+ continue;
22
+ }
23
+ const { text, replacements } = refreshFoxclawExecStartText(before, escapedEntryPoint);
24
+ if (replacements === 0 || text === before) {
25
+ continue;
26
+ }
27
+ fs.writeFileSync(filePath, text, 'utf8');
28
+ updates.push({ path: filePath, replacements });
29
+ }
30
+ return updates;
31
+ }
32
+ export function removeFoxclawExecStartDropIns(userSystemdDir, unitName) {
33
+ const dropInDir = path.join(userSystemdDir, `${unitName}.d`);
34
+ let names;
35
+ try {
36
+ names = fs.readdirSync(dropInDir).filter((name) => name.endsWith('.conf')).sort();
37
+ }
38
+ catch {
39
+ return [];
40
+ }
41
+ const updates = [];
42
+ for (const name of names) {
43
+ const filePath = path.join(dropInDir, name);
44
+ let before = '';
45
+ try {
46
+ before = fs.readFileSync(filePath, 'utf8');
47
+ }
48
+ catch {
49
+ continue;
50
+ }
51
+ const { text, replacements } = removeFoxclawExecStartText(before);
52
+ if (replacements === 0 || text === before) {
53
+ continue;
54
+ }
55
+ if (isEmptyServiceDropIn(text)) {
56
+ fs.rmSync(filePath, { force: true });
57
+ }
58
+ else {
59
+ fs.writeFileSync(filePath, text, 'utf8');
60
+ }
61
+ updates.push({ path: filePath, replacements });
62
+ }
63
+ return updates;
64
+ }
65
+ export function refreshFoxclawExecStartText(text, escapedEntryPoint) {
66
+ let replacements = 0;
67
+ const lines = text.split(/(\r?\n)/);
68
+ const refreshed = lines.map((part) => {
69
+ if (!part.startsWith('ExecStart=') || part.trim() === 'ExecStart=') {
70
+ return part;
71
+ }
72
+ return part.replace(FOXCLAW_MAIN_PATH_RE, () => {
73
+ replacements += 1;
74
+ return escapedEntryPoint;
75
+ });
76
+ });
77
+ return { text: refreshed.join(''), replacements };
78
+ }
79
+ export function removeFoxclawExecStartText(text) {
80
+ const hasFoxclawExecStart = text
81
+ .split(/\r?\n/)
82
+ .some((line) => line.startsWith('ExecStart=') && FOXCLAW_MAIN_PATH_RE.test(line));
83
+ if (!hasFoxclawExecStart) {
84
+ return { text, replacements: 0 };
85
+ }
86
+ let replacements = 0;
87
+ const lines = text.split(/(\r?\n)/);
88
+ const cleaned = lines.map((part) => {
89
+ if (!part.startsWith('ExecStart=')) {
90
+ return part;
91
+ }
92
+ replacements += 1;
93
+ return '';
94
+ });
95
+ return { text: cleaned.join(''), replacements };
96
+ }
97
+ function isEmptyServiceDropIn(text) {
98
+ const meaningfulLines = text
99
+ .split(/\r?\n/)
100
+ .map((line) => line.trim())
101
+ .filter(Boolean);
102
+ return meaningfulLines.length === 0 || (meaningfulLines.length === 1 && meaningfulLines[0] === '[Service]');
103
+ }
@@ -203,12 +203,28 @@ ALL_PROXY=socks5://127.0.0.1:20170
203
203
  NO_PROXY=127.0.0.1,localhost
204
204
  ```
205
205
 
206
+ When `HTTP_PROXY` or `HTTPS_PROXY` is configured, FoxClaw passes those variables to systemd/launchd explicitly and starts Node with `--use-env-proxy`. Do not rely on proxy variables from the current shell; service processes do not inherit them automatically.
207
+
206
208
  Restart FoxClaw after editing. The restart also restarts the managed Codex app-server so the new proxy environment takes effect:
207
209
 
208
210
  ```bash
209
211
  foxclaw restart
210
212
  ```
211
213
 
214
+ If a Linux host must use `proxychains4` to reach Telegram or ChatGPT, do not hand-write a systemd drop-in that overrides `ExecStart`. Add this to the FoxClaw env file instead:
215
+
216
+ ```dotenv
217
+ FOXCLAW_PROXYCHAINS_CONF=/home/wuya/.proxychains-rt.conf
218
+ ```
219
+
220
+ Then run:
221
+
222
+ ```bash
223
+ foxclaw restart
224
+ ```
225
+
226
+ FoxClaw writes proxychains into the main service and removes stale FoxClaw `ExecStart` overrides, so later upgrades still only need the normal `pnpm install -g` and `foxclaw restart`.
227
+
212
228
  ## Service Starts With The Wrong Node Version
213
229
 
214
230
  The systemd installer records the absolute path of the Node process that is currently running FoxClaw. It does not rely on systemd loading `nvm.sh` or any other shell init script. Whether you use nvm, fnm, asdf, mise, Volta, Homebrew, or system Node, run `foxclaw start` from a Node 24+ shell and the service will keep using that Node 24+ path.
@@ -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
- `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. If the current shell has proxy variables such as `HTTP_PROXY`, `HTTPS_PROXY`, or `ALL_PROXY`, it also asks whether to save them into the FoxClaw config so the service-side Codex app-server uses the same network. Press Enter on any field to skip it, then edit manually if needed:
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. If the current shell has proxy variables such as `HTTP_PROXY`, `HTTPS_PROXY`, or `ALL_PROXY`, it also asks whether to save them into the FoxClaw config. When `HTTP_PROXY` or `HTTPS_PROXY` is configured, FoxClaw passes it to systemd/launchd explicitly and enables Node's env proxy support. Press Enter on any field to skip it, then edit manually if needed:
104
104
 
105
105
  ```bash
106
106
  $EDITOR ~/.foxclaw/.env
@@ -204,12 +204,28 @@ ALL_PROXY=socks5://127.0.0.1:20170
204
204
  NO_PROXY=127.0.0.1,localhost
205
205
  ```
206
206
 
207
+ 只要配置了 `HTTP_PROXY` 或 `HTTPS_PROXY`,FoxClaw 安装 systemd/launchd 时会把这些变量显式传给服务,并给 Node 加上 `--use-env-proxy`。不要依赖“当前 shell 里有代理变量”,服务进程不会自动继承它们。
208
+
207
209
  改完后重启 FoxClaw。重启会同时重启托管的 Codex app-server,让新代理生效:
208
210
 
209
211
  ```bash
210
212
  foxclaw restart
211
213
  ```
212
214
 
215
+ 如果这台 Linux 机器必须用 `proxychains4` 才能访问 Telegram 或 ChatGPT,不要手写 systemd drop-in 覆盖 `ExecStart`。在 FoxClaw env 文件里写:
216
+
217
+ ```dotenv
218
+ FOXCLAW_PROXYCHAINS_CONF=/home/wuya/.proxychains-rt.conf
219
+ ```
220
+
221
+ 然后运行:
222
+
223
+ ```bash
224
+ foxclaw restart
225
+ ```
226
+
227
+ FoxClaw 会把 proxychains 写进主 service,并清理旧的 FoxClaw `ExecStart` 覆盖,后续升级仍然只需要正常 `pnpm install -g` 和 `foxclaw restart`。
228
+
213
229
  ## 服务用了错误的 Node 版本
214
230
 
215
231
  systemd 安装脚本会记录当时正在运行的 Node 绝对路径,不依赖 systemd 去加载 `nvm.sh` 或其它 shell 初始化脚本。无论你用 nvm、fnm、asdf、mise、Volta、Homebrew 还是系统 Node,原则都是:从 Node 24+ 的 shell 里执行 `foxclaw start`,服务之后就固定使用这个 Node 24+ 路径。
@@ -100,7 +100,7 @@ foxclaw init
100
100
 
101
101
  ### 1.6 填写配置
102
102
 
103
- `foxclaw init` 会创建默认配置文件 `~/.foxclaw/.env`,并提示填写 Telegram bot token、Telegram 数字用户 ID 和默认工作目录。如果当前 shell 里有 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 等代理变量,它也会询问是否写入 FoxClaw 配置,让服务里的 Codex app-server 使用同样的网络。任何一项都可以直接回车跳过,之后再手动编辑:
103
+ `foxclaw init` 会创建默认配置文件 `~/.foxclaw/.env`,并提示填写 Telegram bot token、Telegram 数字用户 ID 和默认工作目录。如果当前 shell 里有 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 等代理变量,它也会询问是否写入 FoxClaw 配置。配置了 `HTTP_PROXY` `HTTPS_PROXY` 后,FoxClaw 会在安装服务时显式传给 systemd/launchd,并启用 Node 的 env proxy。任何一项都可以直接回车跳过,之后再手动编辑:
104
104
 
105
105
  ```bash
106
106
  $EDITOR ~/.foxclaw/.env
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -184,6 +184,7 @@ Use this checklist when the user asks for standard closing actions, release wrap
184
184
  - If the user has a pnpm global FoxClaw install, prefer `pnpm add -g <repo-path>` so the global `foxclaw` points at the local repo.
185
185
  - Rebuild before restarting because local linked installs run `dist/main.js`.
186
186
  - Refresh systemd with the existing service env path, for example `FOXCLAW_ENV=<existing-env> <node24> dist/main.js install-systemd`. Do not run `install-systemd` from the repo without `FOXCLAW_ENV`, because it may rewrite the service to use the repo `.env`.
187
+ - Do not create systemd drop-ins that override FoxClaw `ExecStart`. For Linux hosts that require proxychains, set `FOXCLAW_PROXYCHAINS_CONF=/absolute/path/to/proxychains.conf` in the FoxClaw env file and rerun `foxclaw restart`.
187
188
  - For macOS launchd, use the launchd install/start path from this skill and verify with `node dist/main.js status`.
188
189
  - Verify the running service reports the expected FoxClaw version in `status`.
189
190
  - If `doctor` fails only because `DEFAULT_CWD` is missing, report that separately; do not treat it as evidence that the service update failed.