@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 +10 -0
- package/dist/main.js +37 -4
- package/dist/systemd.d.ts +7 -0
- package/dist/systemd.js +17 -0
- package/package.json +1 -1
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
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
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;
|