@foxden-app/foxclaw 0.5.13 → 0.5.14

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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable FoxClaw changes are listed here. Each release note is bilingual so GitHub Releases and the npm package are useful to both Chinese and English readers.
4
4
 
5
+ ## 0.5.14 - 2026-06-08
6
+
7
+ ### 中文
8
+ - Linux `foxclaw start` / `foxclaw restart` / `install-systemd` 现在如果检测到自己正运行在 `foxclaw.service` cgroup 内,会通过一次性的 `systemd-run --user` helper 在服务外执行重启,避免命令执行者被自己重启时杀掉导致半截输出或不确定状态。
9
+ - 继续保留 systemd 作为稳定守护层:主 service 仍由 `Restart=always`、`KillMode=control-group` 和 user linger 保活;重启编排则交给短生命周期 helper,避免再引入一个更脆弱的常驻 Node 守护进程。
10
+
11
+ ### English
12
+ - On Linux, `foxclaw start`, `foxclaw restart`, and `install-systemd` now detect when they are running inside the `foxclaw.service` cgroup and delegate the actual restart to a one-shot `systemd-run --user` helper outside that cgroup, avoiding half-written output or uncertain state when the caller would otherwise kill itself.
13
+ - systemd remains the stable supervisor with `Restart=always`, `KillMode=control-group`, and user linger; restart orchestration moves to a short-lived helper instead of adding another long-running Node watchdog.
14
+
5
15
  ## 0.5.13 - 2026-06-08
6
16
 
7
17
  ### 中文
package/dist/main.js CHANGED
@@ -10,7 +10,7 @@ import { fileURLToPath } from 'node:url';
10
10
  import { APP_HOME, DEFAULT_CODEX_TELEGRAM_HOME, DEFAULT_ENV_PATH, DEFAULT_LOG_PATH, DEFAULT_STATUS_PATH, getLoadedEnvPath, loadConfig, loadEnv, } from './config.js';
11
11
  import { acquireProcessLock, LockHeldError } from './lock.js';
12
12
  import { readRuntimeStatus, writeRuntimeStatus } from './runtime.js';
13
- import { buildFoxclawSystemdUnitText, refreshFoxclawExecStartDropIns, removeFoxclawExecStartDropIns } from './systemd.js';
13
+ import { buildFoxclawSystemdUnitText, buildSystemdRestartHelperArgs, cgroupContainsSystemdUnit, refreshFoxclawExecStartDropIns, removeFoxclawExecStartDropIns, } from './systemd.js';
14
14
  import { createSelfUpdateRuntime, inferPnpmHomeFromEntryPoint, performSelfUpdate, readSelfUpdateStatus, writeSelfUpdateStatus, } from './update.js';
15
15
  const rawCommand = process.argv[2];
16
16
  const command = rawCommand || 'serve';
@@ -1305,13 +1305,46 @@ function installSystemd() {
1305
1305
  ensureSystemdUserLingerEnabled();
1306
1306
  spawnChecked('systemctl', ['--user', 'daemon-reload']);
1307
1307
  spawnChecked('systemctl', ['--user', 'enable', unitName]);
1308
+ restartSystemdUnit(unitName);
1309
+ console.log(`Installed ${unitPath}`);
1310
+ console.log(`Status: systemctl --user status ${unitName}`);
1311
+ console.log(`Logs: journalctl --user -u ${unitName} -f`);
1312
+ }
1313
+ function restartSystemdUnit(unitName) {
1314
+ if (isCurrentProcessInSystemdUnit(unitName)) {
1315
+ const systemdRun = resolveCommand('systemd-run');
1316
+ if (systemdRun) {
1317
+ const helperUnitName = `foxclaw-restart-${process.pid}-${Date.now()}`;
1318
+ const result = spawnSync(systemdRun, buildSystemdRestartHelperArgs({
1319
+ unitName,
1320
+ helperUnitName,
1321
+ delaySeconds: 1,
1322
+ }), { stdio: 'inherit' });
1323
+ if (result.status === 0) {
1324
+ console.log(`[OK] scheduled ${unitName} restart via transient systemd helper: ${helperUnitName}`);
1325
+ return;
1326
+ }
1327
+ console.log('[WARN] transient systemd restart helper failed; falling back to direct restart.');
1328
+ }
1329
+ else {
1330
+ console.log('[WARN] systemd-run not found; falling back to direct restart.');
1331
+ }
1332
+ }
1308
1333
  const restarted = spawnSync('systemctl', ['--user', 'restart', unitName], { stdio: 'inherit' });
1309
1334
  if (restarted.status !== 0) {
1310
1335
  spawnChecked('systemctl', ['--user', 'start', unitName]);
1311
1336
  }
1312
- console.log(`Installed ${unitPath}`);
1313
- console.log(`Status: systemctl --user status ${unitName}`);
1314
- console.log(`Logs: journalctl --user -u ${unitName} -f`);
1337
+ }
1338
+ function isCurrentProcessInSystemdUnit(unitName) {
1339
+ if (process.platform !== 'linux') {
1340
+ return false;
1341
+ }
1342
+ try {
1343
+ return cgroupContainsSystemdUnit(fs.readFileSync('/proc/self/cgroup', 'utf8'), unitName);
1344
+ }
1345
+ catch {
1346
+ return false;
1347
+ }
1315
1348
  }
1316
1349
  function ensureSystemdUserLingerEnabled() {
1317
1350
  if (process.platform !== 'linux') {
package/dist/systemd.d.ts CHANGED
@@ -11,7 +11,14 @@ export interface FoxclawSystemdUnitTextOptions {
11
11
  pathValue: string;
12
12
  execStart: string;
13
13
  }
14
+ export interface SystemdRestartHelperOptions {
15
+ unitName: string;
16
+ helperUnitName: string;
17
+ delaySeconds: number;
18
+ }
14
19
  export declare function buildFoxclawSystemdUnitText(options: FoxclawSystemdUnitTextOptions): string;
20
+ export declare function cgroupContainsSystemdUnit(cgroupText: string, unitName: string): boolean;
21
+ export declare function buildSystemdRestartHelperArgs(options: SystemdRestartHelperOptions): string[];
15
22
  export declare function refreshFoxclawExecStartDropIns(userSystemdDir: string, unitName: string, escapedEntryPoint: string): SystemdDropInUpdate[];
16
23
  export declare function removeFoxclawExecStartDropIns(userSystemdDir: string, unitName: string): SystemdDropInUpdate[];
17
24
  export declare function refreshFoxclawExecStartText(text: string, escapedEntryPoint: string): {
package/dist/systemd.js CHANGED
@@ -29,6 +29,23 @@ KillMode=control-group
29
29
  WantedBy=default.target
30
30
  `;
31
31
  }
32
+ export function cgroupContainsSystemdUnit(cgroupText, unitName) {
33
+ const escapedUnitName = unitName.replace(/-/g, '\\x2d');
34
+ return cgroupText
35
+ .split(/\r?\n/)
36
+ .some((line) => line.includes(`/${unitName}`) || line.includes(`/${escapedUnitName}`));
37
+ }
38
+ export function buildSystemdRestartHelperArgs(options) {
39
+ return [
40
+ '--user',
41
+ '--collect',
42
+ `--unit=${options.helperUnitName}`,
43
+ '--description=Restart FoxClaw service outside foxclaw.service cgroup',
44
+ '/bin/sh',
45
+ '-lc',
46
+ `sleep ${Math.max(0, options.delaySeconds)}; exec systemctl --user restart ${options.unitName}`,
47
+ ];
48
+ }
32
49
  export function refreshFoxclawExecStartDropIns(userSystemdDir, unitName, escapedEntryPoint) {
33
50
  const dropInDir = path.join(userSystemdDir, `${unitName}.d`);
34
51
  let names;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.5.13",
3
+ "version": "0.5.14",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",