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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskforce-loop-engineering",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "private": false,
5
5
  "description": "OpenClaw-native loop engineering CLI, templates, and skill for verifiable agent work loops.",
6
6
  "type": "module",
@@ -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-openclaw-install-'));
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 systemdEscape(value) {
26
- return String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
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="${systemdEscape(root)}"\nExecStart="${systemdEscape(process.execPath)}" "${systemdEscape(path.join(root, 'scripts', 'loops', 'openclaw-loop.mjs'))}" scheduler-tick --json\n`;
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 }) {