taskforce-loop-engineering 0.8.4 → 0.8.5
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
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.5 - 2026-08-08
|
|
4
|
+
|
|
5
|
+
- Fixed generated systemd scheduler units so `WorkingDirectory` and `ExecStart` use unquoted, byte-safe systemd path escapes.
|
|
6
|
+
- Added real `systemd-analyze verify` coverage for scheduler paths containing spaces and non-ASCII characters.
|
|
7
|
+
|
|
3
8
|
## 0.8.4 - 2026-08-07
|
|
4
9
|
|
|
5
10
|
- Reclaim queue locks immediately when their recorded owner PID no longer
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import { spawn } from 'node:child_process';
|
|
|
4
4
|
import { tmpdir } from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
|
|
7
|
-
const root = await mkdtemp(path.join(tmpdir(), 'loop
|
|
7
|
+
const root = await mkdtemp(path.join(tmpdir(), 'loop openclaw 安装-'));
|
|
8
8
|
const deliveryCapture = path.join(root, 'delivery.json');
|
|
9
9
|
const mockOpenClaw = path.join(root, 'mock-openclaw.mjs');
|
|
10
10
|
const mockSystemctl = path.join(root, 'mock-systemctl.mjs');
|
|
@@ -66,6 +66,14 @@ const timerFile = path.join(process.env.XDG_CONFIG_HOME, 'systemd/user/openclaw-
|
|
|
66
66
|
const service = await readFile(serviceFile, 'utf8');
|
|
67
67
|
const timer = await readFile(timerFile, 'utf8');
|
|
68
68
|
if (!service.includes('scheduler-tick') || !timer.includes('OnUnitActiveSec=1min')) throw new Error('scheduler systemd units were not installed');
|
|
69
|
+
if (service.includes('WorkingDirectory="') || service.includes('ExecStart="') || !service.includes('\\x20') || !service.includes('\\xe5\\xae\\x89\\xe8\\xa3\\x85')) throw new Error('scheduler service paths were not encoded with systemd path escapes');
|
|
70
|
+
const systemdVerify = await new Promise((resolve) => {
|
|
71
|
+
const child = spawn('systemd-analyze', ['verify', serviceFile, timerFile], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
72
|
+
let stdout = ''; let stderr = ''; child.stdout.on('data', (chunk) => { stdout += chunk; }); child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
73
|
+
child.on('error', (error) => resolve({ code: 127, stdout, stderr: `${stderr}${error.message}` }));
|
|
74
|
+
child.on('close', (code) => resolve({ code, stdout, stderr }));
|
|
75
|
+
});
|
|
76
|
+
if (systemdVerify.code !== 0) throw new Error(`systemd rejected generated scheduler units: ${systemdVerify.stderr || systemdVerify.stdout}`);
|
|
69
77
|
const installSystemctlCalls = await readFile(systemctlCapture, 'utf8');
|
|
70
78
|
if (!installSystemctlCalls.includes('["--user","enable","--now","openclaw-loop-test-tasks-scheduler.timer"]')) throw new Error('scheduler timer was not enabled');
|
|
71
79
|
const schedulerTick = await new Promise((resolve) => {
|
|
@@ -22,8 +22,12 @@ function parseArgs(argv) {
|
|
|
22
22
|
return out;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function
|
|
26
|
-
return String(value)
|
|
25
|
+
function systemdEscapePath(value) {
|
|
26
|
+
return [...Buffer.from(String(value))]
|
|
27
|
+
.map((byte) => /[A-Za-z0-9/_.:-]/.test(String.fromCharCode(byte))
|
|
28
|
+
? String.fromCharCode(byte)
|
|
29
|
+
: `\\x${byte.toString(16).padStart(2, '0')}`)
|
|
30
|
+
.join('');
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
function safeId(value, label) {
|
|
@@ -140,7 +144,7 @@ if (command === 'route') {
|
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
function schedulerServiceSource({ root, queue }) {
|
|
143
|
-
return `[Unit]\nDescription=Taskforce Loop Engineering scheduler for ${queue}\nAfter=default.target\n\n[Service]\nType=oneshot\nWorkingDirectory
|
|
147
|
+
return `[Unit]\nDescription=Taskforce Loop Engineering scheduler for ${queue}\nAfter=default.target\n\n[Service]\nType=oneshot\nWorkingDirectory=${systemdEscapePath(root)}\nExecStart=${systemdEscapePath(process.execPath)} ${systemdEscapePath(path.join(root, 'scripts', 'loops', 'openclaw-loop.mjs'))} scheduler-tick --json\n`;
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
function schedulerTimerSource({ queue }) {
|