@parall/daemon 1.44.0 → 1.45.0
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/bundle/manifest.json +9 -9
- package/bundle/parall-claude-agent.js +336 -92
- package/bundle/parall-codex-agent.js +964 -1342
- package/bundle/parall-daemon.js +866 -311
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +229 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/runtime-bin-resolver.d.ts +7 -1
- package/dist/runtime-bin-resolver.d.ts.map +1 -1
- package/dist/runtime-bin-resolver.js +57 -22
- package/dist/runtimes.d.ts +15 -4
- package/dist/runtimes.d.ts.map +1 -1
- package/dist/runtimes.js +60 -5
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +4 -1
- package/dist/win-lifecycle.d.ts +96 -0
- package/dist/win-lifecycle.d.ts.map +1 -0
- package/dist/win-lifecycle.js +229 -0
- package/dist/win-service.d.ts +119 -0
- package/dist/win-service.d.ts.map +1 -0
- package/dist/win-service.js +226 -0
- package/package.json +6 -6
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
/**
|
|
3
|
+
* Windows service (Task Scheduler) artifact generation. Everything here is a
|
|
4
|
+
* pure function from paths to strings so the whole surface unit-tests on any
|
|
5
|
+
* platform; cli.ts owns filesystem writes and schtasks invocations.
|
|
6
|
+
*
|
|
7
|
+
* The chain at run time:
|
|
8
|
+
* Task Scheduler → wscript.exe //B launcher.vbs
|
|
9
|
+
* → vbs Run("node.exe launcher.js", 0, True) — window hidden, waits,
|
|
10
|
+
* propagates the exit code via WScript.Quit
|
|
11
|
+
* → launcher.js sets PRLL_DAEMON_MANAGED=1, writes the pidfile,
|
|
12
|
+
* redirects stdio to the log file, then imports the daemon bundle
|
|
13
|
+
* IN-PROCESS (overlay-first) — so the pid in the pidfile IS the
|
|
14
|
+
* daemon and a self-update exit(42) is the task's own exit code,
|
|
15
|
+
* which RestartOnFailure turns into a restart on the new overlay.
|
|
16
|
+
*/
|
|
17
|
+
export const WIN_TASK_NAME = 'ParallDaemon';
|
|
18
|
+
export function winServicePaths(home) {
|
|
19
|
+
const root = path.join(home, '.parall-daemon');
|
|
20
|
+
const serviceDir = path.join(root, 'service');
|
|
21
|
+
const logDir = path.join(root, 'logs');
|
|
22
|
+
return {
|
|
23
|
+
serviceDir,
|
|
24
|
+
launcherCjs: path.join(serviceDir, 'parall-daemon-launcher.cjs'),
|
|
25
|
+
legacyLauncherJs: path.join(serviceDir, 'parall-daemon-launcher.js'),
|
|
26
|
+
launcherVbs: path.join(serviceDir, 'parall-daemon-launcher.vbs'),
|
|
27
|
+
taskXml: path.join(serviceDir, 'task.xml'),
|
|
28
|
+
pidFile: path.join(root, 'daemon.pid'),
|
|
29
|
+
logDir,
|
|
30
|
+
logFile: path.join(logDir, 'parall-daemon.log'),
|
|
31
|
+
overlayEntry: path.join(root, 'bundle', 'current', 'parall-daemon.js'),
|
|
32
|
+
runningMarker: path.join(root, 'bundle', 'daemon-running'),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* VBScript string literal: wrap in quotes, escape embedded quotes by
|
|
37
|
+
* doubling. NTFS forbids `"` in file names, so real paths never exercise
|
|
38
|
+
* the escape — it exists as defense-in-depth for arbitrary strings.
|
|
39
|
+
*/
|
|
40
|
+
export function vbsQuote(value) {
|
|
41
|
+
return `"${value.replaceAll('"', '""')}"`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Encode a generated artifact as UTF-16LE with a BOM. wscript reads BOM-less
|
|
45
|
+
* files in the ANSI codepage (mangling non-ASCII profile paths) and schtasks'
|
|
46
|
+
* canonical XML form is UTF-16 — both .vbs and task.xml go through this.
|
|
47
|
+
*/
|
|
48
|
+
export function encodeUtf16LeBom(content) {
|
|
49
|
+
return Buffer.from('\ufeff' + content, 'utf16le');
|
|
50
|
+
}
|
|
51
|
+
export function xmlEscape(value) {
|
|
52
|
+
return value
|
|
53
|
+
.replaceAll('&', '&')
|
|
54
|
+
.replaceAll('<', '<')
|
|
55
|
+
.replaceAll('>', '>')
|
|
56
|
+
.replaceAll('"', '"')
|
|
57
|
+
.replaceAll("'", ''');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The hidden-window launcher. `Run(cmd, 0, True)`: 0 hides the console
|
|
61
|
+
* window (a console app started by an interactive scheduled task would
|
|
62
|
+
* otherwise flash/persist one), True waits and yields the exit code, and
|
|
63
|
+
* WScript.Quit hands that code to Task Scheduler so RestartOnFailure sees
|
|
64
|
+
* daemon crashes and self-update exit(42)s.
|
|
65
|
+
*
|
|
66
|
+
* Caller must write this file as UTF-16LE with a BOM: wscript reads BOM-less
|
|
67
|
+
* files in the ANSI codepage, which mangles non-ASCII (e.g. Chinese
|
|
68
|
+
* user-profile) paths.
|
|
69
|
+
*/
|
|
70
|
+
export function buildLauncherVbs(p) {
|
|
71
|
+
const runCommand = `"${p.nodeExe}" "${p.launcherCjs}"`;
|
|
72
|
+
return [
|
|
73
|
+
"' generated by `parall-daemon service install` - do not edit",
|
|
74
|
+
'Set sh = CreateObject("WScript.Shell")',
|
|
75
|
+
`code = sh.Run(${vbsQuote(runCommand)}, 0, True)`,
|
|
76
|
+
'WScript.Quit code',
|
|
77
|
+
'',
|
|
78
|
+
].join('\r\n');
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The in-process daemon launcher. Embedded paths go through JSON.stringify,
|
|
82
|
+
* which handles backslashes and quotes; the file itself is plain UTF-8 (Node
|
|
83
|
+
* always reads JS as UTF-8, so non-ASCII paths round-trip).
|
|
84
|
+
*
|
|
85
|
+
* The content is CommonJS and the artifact is written as `.cjs` so an
|
|
86
|
+
* ancestor package.json with "type": "module" (users do npm-init their home
|
|
87
|
+
* directories) can never flip it into ESM scope.
|
|
88
|
+
*/
|
|
89
|
+
export function buildLauncherCjs(p) {
|
|
90
|
+
return `// generated by \`parall-daemon service install\` - do not edit
|
|
91
|
+
'use strict';
|
|
92
|
+
const fs = require('node:fs');
|
|
93
|
+
const path = require('node:path');
|
|
94
|
+
const { pathToFileURL } = require('node:url');
|
|
95
|
+
|
|
96
|
+
// Must be set before the daemon loads: the bootstrap reads it at import time
|
|
97
|
+
// to enable supervised self-update.
|
|
98
|
+
process.env.PRLL_DAEMON_MANAGED = '1';
|
|
99
|
+
|
|
100
|
+
const OVERLAY_ENTRY = ${JSON.stringify(p.overlayEntry)};
|
|
101
|
+
const NPM_ENTRY = ${JSON.stringify(p.npmEntry)};
|
|
102
|
+
const LOG_FILE = ${JSON.stringify(p.logFile)};
|
|
103
|
+
const PID_FILE = ${JSON.stringify(p.pidFile)};
|
|
104
|
+
|
|
105
|
+
fs.mkdirSync(path.dirname(LOG_FILE), { recursive: true });
|
|
106
|
+
try {
|
|
107
|
+
if (fs.statSync(LOG_FILE).size > 20 * 1024 * 1024) {
|
|
108
|
+
fs.renameSync(LOG_FILE, LOG_FILE + '.old');
|
|
109
|
+
}
|
|
110
|
+
} catch {}
|
|
111
|
+
|
|
112
|
+
// Synchronous writes so an abrupt exit (self-update exit(42), crash) cannot
|
|
113
|
+
// lose the log tail. The daemon is not chatty enough for this to matter.
|
|
114
|
+
const logFd = fs.openSync(LOG_FILE, 'a');
|
|
115
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
116
|
+
stream.write = (chunk, encoding, callback) => {
|
|
117
|
+
try {
|
|
118
|
+
fs.writeSync(
|
|
119
|
+
logFd,
|
|
120
|
+
Buffer.isBuffer(chunk)
|
|
121
|
+
? chunk
|
|
122
|
+
: Buffer.from(chunk, typeof encoding === 'string' ? encoding : 'utf8'),
|
|
123
|
+
);
|
|
124
|
+
} catch {}
|
|
125
|
+
const done = typeof encoding === 'function' ? encoding : callback;
|
|
126
|
+
if (done) done();
|
|
127
|
+
return true;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fs.writeFileSync(PID_FILE, String(process.pid));
|
|
132
|
+
process.on('exit', () => {
|
|
133
|
+
try {
|
|
134
|
+
if (fs.readFileSync(PID_FILE, 'utf8').trim() === String(process.pid)) {
|
|
135
|
+
fs.unlinkSync(PID_FILE);
|
|
136
|
+
}
|
|
137
|
+
} catch {}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Overlay-first, mirroring the launchd/systemd wrapper semantics.
|
|
141
|
+
const entry = fs.existsSync(OVERLAY_ENTRY) ? OVERLAY_ENTRY : NPM_ENTRY;
|
|
142
|
+
// The daemon derives bridge-bundle siblings and self-update mode from its
|
|
143
|
+
// entry path - argv[1] must point at the real bundle entry, not this file.
|
|
144
|
+
process.argv[1] = entry;
|
|
145
|
+
import(pathToFileURL(entry).href).catch((err) => {
|
|
146
|
+
process.stderr.write('launcher: failed to load ' + entry + ': ' + ((err && err.stack) || err) + '\\n');
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Task Scheduler task definition.
|
|
153
|
+
*
|
|
154
|
+
* Element-order ground rules (Microsoft Task Scheduler schema docs):
|
|
155
|
+
* - trigger children (triggerBaseType) are an xs:sequence and MUST appear as
|
|
156
|
+
* StartBoundary → EndBoundary → Enabled → Repetition → ExecutionTimeLimit
|
|
157
|
+
* (learn.microsoft.com .../taskschedulerschema-timetrigger-triggergroup-element);
|
|
158
|
+
* - Settings children (settingsType) are an xs:all — order-insensitive;
|
|
159
|
+
* - RestartOnFailure Count is xs:unsignedByte (1–255)
|
|
160
|
+
* (.../taskschedulerschema-count-restarttype-element).
|
|
161
|
+
*
|
|
162
|
+
* - LogonTrigger: start at user logon (no admin, no stored password:
|
|
163
|
+
* InteractiveToken + omitted UserId registers for the calling user).
|
|
164
|
+
* - TimeTrigger PT1H: revival tick (StartBoundary is a fixed date safely in
|
|
165
|
+
* the past — a boundary in the future would silently disarm the trigger on
|
|
166
|
+
* a machine whose clock is behind) — if RestartOnFailure's count is ever
|
|
167
|
+
* exhausted the daemon still comes back within the hour; IgnoreNew makes
|
|
168
|
+
* it a no-op while an instance is running. `stop` disables the whole task,
|
|
169
|
+
* so this trigger never resurrects an intentionally stopped daemon.
|
|
170
|
+
* - ExecutionTimeLimit PT0S: no 72h default kill.
|
|
171
|
+
* - RestartOnFailure PT1M/99: crash or self-update exit(42) restarts in
|
|
172
|
+
* ~1 minute (PT1M is the schema minimum interval; 99 stays well inside the
|
|
173
|
+
* unsignedByte cap). Note this makes Windows self-update restarts ~1min,
|
|
174
|
+
* vs ~5s under launchd.
|
|
175
|
+
*
|
|
176
|
+
* Caller must write this as UTF-16LE with BOM to match the declared
|
|
177
|
+
* encoding — the canonical form schtasks itself produces on export.
|
|
178
|
+
*/
|
|
179
|
+
export function buildTaskXml(p) {
|
|
180
|
+
const args = `//B //Nologo "${p.launcherVbs}"`;
|
|
181
|
+
return `<?xml version="1.0" encoding="UTF-16"?>
|
|
182
|
+
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
183
|
+
<RegistrationInfo>
|
|
184
|
+
<Description>Parall Daemon (BYOC machine supervisor)</Description>
|
|
185
|
+
</RegistrationInfo>
|
|
186
|
+
<Triggers>
|
|
187
|
+
<LogonTrigger>
|
|
188
|
+
<Enabled>true</Enabled>
|
|
189
|
+
</LogonTrigger>
|
|
190
|
+
<TimeTrigger>
|
|
191
|
+
<StartBoundary>2020-01-01T00:00:00</StartBoundary>
|
|
192
|
+
<Enabled>true</Enabled>
|
|
193
|
+
<Repetition>
|
|
194
|
+
<Interval>PT1H</Interval>
|
|
195
|
+
</Repetition>
|
|
196
|
+
</TimeTrigger>
|
|
197
|
+
</Triggers>
|
|
198
|
+
<Principals>
|
|
199
|
+
<Principal id="Author">
|
|
200
|
+
<LogonType>InteractiveToken</LogonType>
|
|
201
|
+
<RunLevel>LeastPrivilege</RunLevel>
|
|
202
|
+
</Principal>
|
|
203
|
+
</Principals>
|
|
204
|
+
<Settings>
|
|
205
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
206
|
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
207
|
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
208
|
+
<AllowHardTerminate>true</AllowHardTerminate>
|
|
209
|
+
<StartWhenAvailable>true</StartWhenAvailable>
|
|
210
|
+
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
211
|
+
<Enabled>true</Enabled>
|
|
212
|
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
213
|
+
<RestartOnFailure>
|
|
214
|
+
<Interval>PT1M</Interval>
|
|
215
|
+
<Count>99</Count>
|
|
216
|
+
</RestartOnFailure>
|
|
217
|
+
</Settings>
|
|
218
|
+
<Actions Context="Author">
|
|
219
|
+
<Exec>
|
|
220
|
+
<Command>${xmlEscape(p.wscriptExe)}</Command>
|
|
221
|
+
<Arguments>${xmlEscape(args)}</Arguments>
|
|
222
|
+
</Exec>
|
|
223
|
+
</Actions>
|
|
224
|
+
</Task>
|
|
225
|
+
`;
|
|
226
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/daemon",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0",
|
|
4
4
|
"description": "Parall local agent runtime — daemon supervisor + bridge runtimes, bundled as standalone JS files",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@aws-sdk/client-s3": "3.984.0",
|
|
34
34
|
"@pinixai/bb-browser-pro": "0.15.0",
|
|
35
|
-
"@parall/agent-core": "1.
|
|
36
|
-
"@parall/
|
|
37
|
-
"@parall/
|
|
38
|
-
"@parall/
|
|
39
|
-
"@parall/openclaw-agent": "1.
|
|
35
|
+
"@parall/agent-core": "1.45.0",
|
|
36
|
+
"@parall/sdk": "1.45.0",
|
|
37
|
+
"@parall/codex-agent": "1.45.0",
|
|
38
|
+
"@parall/claude-agent": "1.45.0",
|
|
39
|
+
"@parall/openclaw-agent": "1.45.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^22.0.0",
|