@parall/daemon 1.30.0 → 1.32.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 +17 -11
- package/bundle/package.json +1 -0
- package/bundle/parall-claude-agent.js +27671 -337
- package/bundle/parall-codex-agent.js +27715 -375
- package/bundle/parall-daemon.js +30421 -1355
- package/bundle/parall-openclaw-agent.js +51 -26
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +122 -72
- package/dist/clip-runtime/clip-installer.d.ts +44 -0
- package/dist/clip-runtime/clip-installer.d.ts.map +1 -0
- package/dist/clip-runtime/clip-installer.js +501 -0
- package/dist/clip-runtime/clip-provider.d.ts +76 -0
- package/dist/clip-runtime/clip-provider.d.ts.map +1 -0
- package/dist/clip-runtime/clip-provider.js +402 -0
- package/dist/clip-runtime/index.d.ts +7 -0
- package/dist/clip-runtime/index.d.ts.map +1 -0
- package/dist/clip-runtime/index.js +5 -0
- package/dist/clip-runtime/ipc.d.ts +94 -0
- package/dist/clip-runtime/ipc.d.ts.map +1 -0
- package/dist/clip-runtime/ipc.js +98 -0
- package/dist/clip-runtime/manifest.d.ts +74 -0
- package/dist/clip-runtime/manifest.d.ts.map +1 -0
- package/dist/clip-runtime/manifest.js +181 -0
- package/dist/clip-runtime/process-manager.d.ts +57 -0
- package/dist/clip-runtime/process-manager.d.ts.map +1 -0
- package/dist/clip-runtime/process-manager.js +354 -0
- package/dist/clip-runtime/process.d.ts +59 -0
- package/dist/clip-runtime/process.d.ts.map +1 -0
- package/dist/clip-runtime/process.js +350 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +36 -19
- package/dist/filesystem.d.ts +1 -1
- package/dist/filesystem.d.ts.map +1 -1
- package/dist/filesystem.js +51 -53
- package/dist/index.js +46 -14
- package/dist/runtimes.d.ts +10 -8
- package/dist/runtimes.d.ts.map +1 -1
- package/dist/runtimes.js +63 -95
- package/dist/supervisor.d.ts +12 -3
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +272 -71
- package/dist/updater-manifest.d.ts +41 -0
- package/dist/updater-manifest.d.ts.map +1 -0
- package/dist/updater-manifest.js +94 -0
- package/dist/updater.d.ts +60 -0
- package/dist/updater.d.ts.map +1 -0
- package/dist/updater.js +427 -0
- package/dist/workspace.d.ts +2 -2
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +112 -112
- package/package.json +6 -6
package/dist/workspace.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { spawn } from
|
|
2
|
-
import { createHash } from
|
|
3
|
-
import * as fs from
|
|
4
|
-
import * as path from
|
|
5
|
-
import { clearAllProviderCreds } from
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { clearAllProviderCreds } from './runtimes.js';
|
|
6
6
|
const OUTPUT_TAIL_LIMIT = 32 * 1024;
|
|
7
7
|
const DEFAULT_SETUP_TIMEOUT_SEC = 600;
|
|
8
8
|
export async function prepareWorkspace(opts) {
|
|
@@ -13,7 +13,7 @@ export async function prepareWorkspace(opts) {
|
|
|
13
13
|
return plan.workspaceDir;
|
|
14
14
|
}
|
|
15
15
|
let forceSetup = false;
|
|
16
|
-
if (prior?.status ===
|
|
16
|
+
if (prior?.status === 'ready' && prior.config_hash === plan.configHash) {
|
|
17
17
|
if (await verifyExistingWorkspace(plan, opts.log)) {
|
|
18
18
|
return plan.workspaceDir;
|
|
19
19
|
}
|
|
@@ -21,9 +21,9 @@ export async function prepareWorkspace(opts) {
|
|
|
21
21
|
}
|
|
22
22
|
await opts.client.reportAgentWorkspaceState(opts.agentId, {
|
|
23
23
|
config_hash: plan.configHash,
|
|
24
|
-
status:
|
|
24
|
+
status: 'preparing',
|
|
25
25
|
});
|
|
26
|
-
let outputTail =
|
|
26
|
+
let outputTail = '';
|
|
27
27
|
try {
|
|
28
28
|
await ensureWorkspace(plan, opts.log);
|
|
29
29
|
if (shouldRunSetup(plan.workspace.setup, prior, plan.configHash, forceSetup)) {
|
|
@@ -31,19 +31,21 @@ export async function prepareWorkspace(opts) {
|
|
|
31
31
|
}
|
|
32
32
|
await opts.client.reportAgentWorkspaceState(opts.agentId, {
|
|
33
33
|
config_hash: plan.configHash,
|
|
34
|
-
status:
|
|
34
|
+
status: 'ready',
|
|
35
35
|
output_tail: outputTail || null,
|
|
36
36
|
});
|
|
37
37
|
return plan.workspaceDir;
|
|
38
38
|
}
|
|
39
39
|
catch (err) {
|
|
40
40
|
const message = err instanceof Error ? err.message : String(err);
|
|
41
|
-
await opts.client
|
|
41
|
+
await opts.client
|
|
42
|
+
.reportAgentWorkspaceState(opts.agentId, {
|
|
42
43
|
config_hash: plan.configHash,
|
|
43
|
-
status:
|
|
44
|
+
status: 'failed',
|
|
44
45
|
last_error: message,
|
|
45
46
|
output_tail: outputTail || null,
|
|
46
|
-
})
|
|
47
|
+
})
|
|
48
|
+
.catch((reportErr) => {
|
|
47
49
|
opts.log.warn(`workspace state report failed after setup error: ${String(reportErr)}`);
|
|
48
50
|
});
|
|
49
51
|
throw err;
|
|
@@ -56,19 +58,17 @@ function buildWorkspacePlan(input, defaultWorkspaceDir, serverConfigHash) {
|
|
|
56
58
|
// The server is the source of truth for config_hash. Go and TypeScript JSON
|
|
57
59
|
// normalization can differ in harmless ways (field order, omitted defaults),
|
|
58
60
|
// so use the server-computed pending/ready row hash whenever it is present.
|
|
59
|
-
const configHash = serverConfigHash || createHash(
|
|
60
|
-
.update(JSON.stringify(config))
|
|
61
|
-
.digest("hex");
|
|
61
|
+
const configHash = serverConfigHash || createHash('sha256').update(JSON.stringify(config)).digest('hex');
|
|
62
62
|
return { config, workspace, workspaceDir, defaultWorkspaceDir, customWorkspaceField, configHash };
|
|
63
63
|
}
|
|
64
64
|
function normalizeConfig(input, defaultWorkspaceDir) {
|
|
65
65
|
const cfg = input ? JSON.parse(JSON.stringify(input)) : {};
|
|
66
66
|
cfg.workspace_path = cfg.workspace_path?.trim() || undefined;
|
|
67
67
|
if (!cfg.workspace && cfg.workspace_path) {
|
|
68
|
-
cfg.workspace = { mode:
|
|
68
|
+
cfg.workspace = { mode: 'local_path', path: cfg.workspace_path };
|
|
69
69
|
}
|
|
70
70
|
if (!cfg.workspace) {
|
|
71
|
-
cfg.workspace = { mode:
|
|
71
|
+
cfg.workspace = { mode: 'default' };
|
|
72
72
|
}
|
|
73
73
|
const ws = cfg.workspace;
|
|
74
74
|
ws.mode = (ws.mode?.trim() || undefined);
|
|
@@ -78,26 +78,26 @@ function normalizeConfig(input, defaultWorkspaceDir) {
|
|
|
78
78
|
ws.git.ref = ws.git.ref?.trim() || undefined;
|
|
79
79
|
ws.git.target_path = ws.git.target_path?.trim() || undefined;
|
|
80
80
|
}
|
|
81
|
-
ws.mode ||= ws.git ?
|
|
82
|
-
if (ws.mode ===
|
|
83
|
-
ws.mode =
|
|
81
|
+
ws.mode ||= ws.git ? 'git' : ws.path ? 'local_path' : 'default';
|
|
82
|
+
if (ws.mode === 'local_path' && !ws.path) {
|
|
83
|
+
ws.mode = 'default';
|
|
84
84
|
}
|
|
85
|
-
if (ws.mode ===
|
|
85
|
+
if (ws.mode === 'git') {
|
|
86
86
|
ws.git ||= {};
|
|
87
87
|
if (!ws.git.target_path && ws.path) {
|
|
88
88
|
ws.git.target_path = ws.path;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
if (ws.mode ===
|
|
91
|
+
if (ws.mode === 'default') {
|
|
92
92
|
ws.path = undefined;
|
|
93
93
|
ws.git = undefined;
|
|
94
94
|
}
|
|
95
|
-
if (ws.mode ===
|
|
95
|
+
if (ws.mode === 'local_path') {
|
|
96
96
|
ws.git = undefined;
|
|
97
97
|
}
|
|
98
98
|
if (ws.setup) {
|
|
99
99
|
ws.setup.command = ws.setup.command?.trim();
|
|
100
|
-
ws.setup.run_on ||=
|
|
100
|
+
ws.setup.run_on ||= 'first_attach';
|
|
101
101
|
if (!ws.setup.command) {
|
|
102
102
|
ws.setup = undefined;
|
|
103
103
|
}
|
|
@@ -108,19 +108,19 @@ function normalizeConfig(input, defaultWorkspaceDir) {
|
|
|
108
108
|
return cfg;
|
|
109
109
|
}
|
|
110
110
|
function resolveWorkspaceDir(workspace, defaultWorkspaceDir) {
|
|
111
|
-
if (workspace.mode ===
|
|
111
|
+
if (workspace.mode === 'local_path') {
|
|
112
112
|
if (!workspace.path)
|
|
113
113
|
return { workspaceDir: defaultWorkspaceDir };
|
|
114
114
|
return {
|
|
115
|
-
workspaceDir: requireAbsolute(workspace.path,
|
|
116
|
-
customWorkspaceField:
|
|
115
|
+
workspaceDir: requireAbsolute(workspace.path, 'workspace.path'),
|
|
116
|
+
customWorkspaceField: 'workspace.path',
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
-
if (workspace.mode ===
|
|
119
|
+
if (workspace.mode === 'git') {
|
|
120
120
|
return workspace.git?.target_path
|
|
121
121
|
? {
|
|
122
|
-
workspaceDir: requireAbsolute(workspace.git.target_path,
|
|
123
|
-
customWorkspaceField:
|
|
122
|
+
workspaceDir: requireAbsolute(workspace.git.target_path, 'workspace.git.target_path'),
|
|
123
|
+
customWorkspaceField: 'workspace.git.target_path',
|
|
124
124
|
}
|
|
125
125
|
: { workspaceDir: defaultWorkspaceDir };
|
|
126
126
|
}
|
|
@@ -128,19 +128,19 @@ function resolveWorkspaceDir(workspace, defaultWorkspaceDir) {
|
|
|
128
128
|
}
|
|
129
129
|
async function ensureWorkspace(plan, log) {
|
|
130
130
|
const ws = plan.workspace;
|
|
131
|
-
if (ws.mode ===
|
|
131
|
+
if (ws.mode === 'default') {
|
|
132
132
|
fs.mkdirSync(plan.workspaceDir, { recursive: true });
|
|
133
133
|
assertWritableWorkspaceDir(plan.workspaceDir);
|
|
134
134
|
return;
|
|
135
135
|
}
|
|
136
|
-
if (ws.mode ===
|
|
136
|
+
if (ws.mode === 'local_path') {
|
|
137
137
|
assertSafeCustomWorkspacePath(plan);
|
|
138
138
|
let st;
|
|
139
139
|
try {
|
|
140
140
|
st = fs.statSync(plan.workspaceDir);
|
|
141
141
|
}
|
|
142
142
|
catch (err) {
|
|
143
|
-
if (isNodeError(err) && err.code ===
|
|
143
|
+
if (isNodeError(err) && err.code === 'ENOENT') {
|
|
144
144
|
throw new Error(`workspace path does not exist: ${plan.workspaceDir}`);
|
|
145
145
|
}
|
|
146
146
|
throw err;
|
|
@@ -152,17 +152,17 @@ async function ensureWorkspace(plan, log) {
|
|
|
152
152
|
assertWritableWorkspaceDir(plan.workspaceDir);
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
|
-
if (ws.mode ===
|
|
155
|
+
if (ws.mode === 'git') {
|
|
156
156
|
const remote = ws.git?.remote?.trim();
|
|
157
157
|
if (!remote)
|
|
158
|
-
throw new Error(
|
|
158
|
+
throw new Error('workspace git remote is required');
|
|
159
159
|
if (plan.customWorkspaceField) {
|
|
160
160
|
assertSafeCustomWorkspacePath(plan);
|
|
161
161
|
}
|
|
162
162
|
if (!fs.existsSync(plan.workspaceDir)) {
|
|
163
163
|
fs.mkdirSync(path.dirname(plan.workspaceDir), { recursive: true });
|
|
164
164
|
assertWritableWorkspaceDir(path.dirname(plan.workspaceDir));
|
|
165
|
-
await runCommand(
|
|
165
|
+
await runCommand('git', ['clone', remote, plan.workspaceDir], process.cwd());
|
|
166
166
|
}
|
|
167
167
|
else {
|
|
168
168
|
const st = fs.statSync(plan.workspaceDir);
|
|
@@ -177,17 +177,17 @@ async function ensureWorkspace(plan, log) {
|
|
|
177
177
|
await ensureGitRemote(plan.workspaceDir, remote);
|
|
178
178
|
}
|
|
179
179
|
if (ws.git?.ref) {
|
|
180
|
-
const dirty = await commandOutput(
|
|
180
|
+
const dirty = await commandOutput('git', ['status', '--porcelain'], plan.workspaceDir);
|
|
181
181
|
if (dirty.trim()) {
|
|
182
182
|
throw new Error(`workspace git tree has local changes; refusing checkout: ${plan.workspaceDir}`);
|
|
183
183
|
}
|
|
184
|
-
await runCommand(
|
|
184
|
+
await runCommand('git', ['fetch', 'origin', '--prune'], plan.workspaceDir);
|
|
185
185
|
await checkoutGitRef(plan.workspaceDir, ws.git.ref);
|
|
186
186
|
}
|
|
187
187
|
log.info(`workspace ready: git ${remote} -> ${plan.workspaceDir}`);
|
|
188
188
|
return;
|
|
189
189
|
}
|
|
190
|
-
throw new Error(`unsupported workspace mode: ${ws.mode ??
|
|
190
|
+
throw new Error(`unsupported workspace mode: ${ws.mode ?? '(empty)'}`);
|
|
191
191
|
}
|
|
192
192
|
async function verifyExistingWorkspace(plan, log) {
|
|
193
193
|
try {
|
|
@@ -203,14 +203,14 @@ async function verifyExistingWorkspace(plan, log) {
|
|
|
203
203
|
assertSafeCustomWorkspacePath(plan, fs.realpathSync(plan.workspaceDir));
|
|
204
204
|
}
|
|
205
205
|
assertWritableWorkspaceDir(plan.workspaceDir);
|
|
206
|
-
if (plan.workspace.mode ===
|
|
206
|
+
if (plan.workspace.mode === 'git') {
|
|
207
207
|
await ensureGitWorktree(plan.workspaceDir);
|
|
208
208
|
const remote = plan.workspace.git?.remote?.trim();
|
|
209
209
|
if (remote) {
|
|
210
210
|
await ensureGitRemote(plan.workspaceDir, remote);
|
|
211
211
|
}
|
|
212
212
|
if (plan.workspace.git?.ref) {
|
|
213
|
-
const head = (await commandOutput(
|
|
213
|
+
const head = (await commandOutput('git', ['rev-parse', 'HEAD'], plan.workspaceDir)).trim();
|
|
214
214
|
const expected = await resolveConfiguredGitRef(plan.workspaceDir, plan.workspace.git.ref);
|
|
215
215
|
if (head !== expected) {
|
|
216
216
|
log.warn(`workspace ready state ignored: git HEAD ${head} does not match ${plan.workspace.git.ref} (${expected})`);
|
|
@@ -226,13 +226,13 @@ async function verifyExistingWorkspace(plan, log) {
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
async function ensureGitWorktree(cwd) {
|
|
229
|
-
const result = await commandOutput(
|
|
230
|
-
if (result.trim() !==
|
|
229
|
+
const result = await commandOutput('git', ['rev-parse', '--is-inside-work-tree'], cwd);
|
|
230
|
+
if (result.trim() !== 'true') {
|
|
231
231
|
throw new Error(`workspace path exists but is not a git worktree: ${cwd}`);
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
async function ensureGitRemote(cwd, expectedRemote) {
|
|
235
|
-
const actualRemote = (await commandOutput(
|
|
235
|
+
const actualRemote = (await commandOutput('git', ['remote', 'get-url', 'origin'], cwd)).trim();
|
|
236
236
|
if (actualRemote !== expectedRemote) {
|
|
237
237
|
throw new Error(`workspace git remote mismatch: expected ${expectedRemote}, got ${actualRemote}`);
|
|
238
238
|
}
|
|
@@ -240,23 +240,23 @@ async function ensureGitRemote(cwd, expectedRemote) {
|
|
|
240
240
|
async function checkoutGitRef(cwd, ref) {
|
|
241
241
|
const remoteCommit = await resolveRemoteGitRef(cwd, ref);
|
|
242
242
|
if (remoteCommit) {
|
|
243
|
-
await runCommand(
|
|
243
|
+
await runCommand('git', ['checkout', '-B', ref, remoteCommit], cwd);
|
|
244
244
|
return;
|
|
245
245
|
}
|
|
246
246
|
const targetCommit = await resolveGitCommit(cwd, ref);
|
|
247
|
-
await runCommand(
|
|
247
|
+
await runCommand('git', ['checkout', '--detach', targetCommit], cwd);
|
|
248
248
|
}
|
|
249
249
|
async function resolveConfiguredGitRef(cwd, ref) {
|
|
250
250
|
const remote = await resolveRemoteGitRef(cwd, ref);
|
|
251
|
-
return remote || await resolveGitCommit(cwd, ref);
|
|
251
|
+
return remote || (await resolveGitCommit(cwd, ref));
|
|
252
252
|
}
|
|
253
253
|
async function resolveRemoteGitRef(cwd, ref) {
|
|
254
|
-
return (await tryGitOutput(
|
|
254
|
+
return (await tryGitOutput('git', ['rev-parse', '--verify', `refs/remotes/origin/${ref}^{commit}`], cwd)).trim();
|
|
255
255
|
}
|
|
256
256
|
async function resolveGitCommit(cwd, ref) {
|
|
257
257
|
const candidates = [`refs/tags/${ref}^{commit}`, `${ref}^{commit}`];
|
|
258
258
|
for (const candidate of candidates) {
|
|
259
|
-
const commit = await tryGitOutput(
|
|
259
|
+
const commit = await tryGitOutput('git', ['rev-parse', '--verify', candidate], cwd);
|
|
260
260
|
if (commit.trim()) {
|
|
261
261
|
return commit.trim();
|
|
262
262
|
}
|
|
@@ -268,14 +268,14 @@ function shouldRunSetup(setup, prior, configHash, forceSetup = false) {
|
|
|
268
268
|
return false;
|
|
269
269
|
if (forceSetup)
|
|
270
270
|
return true;
|
|
271
|
-
if (setup.run_on ===
|
|
272
|
-
return prior?.config_hash !== configHash || prior.status ===
|
|
271
|
+
if (setup.run_on === 'config_change') {
|
|
272
|
+
return (prior?.config_hash !== configHash || prior.status === 'pending' || prior.status === 'failed');
|
|
273
273
|
}
|
|
274
|
-
return prior?.status !==
|
|
274
|
+
return prior?.status !== 'ready' || !prior.prepared_at;
|
|
275
275
|
}
|
|
276
276
|
async function runSetup(cwd, command, timeoutSec) {
|
|
277
277
|
const effectiveTimeoutSec = timeoutSec && timeoutSec > 0 ? timeoutSec : DEFAULT_SETUP_TIMEOUT_SEC;
|
|
278
|
-
return runCommand(process.env.SHELL ||
|
|
278
|
+
return runCommand(process.env.SHELL || '/bin/sh', ['-lc', command], cwd, effectiveTimeoutSec * 1000, scrubSetupEnv());
|
|
279
279
|
}
|
|
280
280
|
function scrubSetupEnv() {
|
|
281
281
|
const env = { ...process.env };
|
|
@@ -291,12 +291,12 @@ async function tryGitOutput(cmd, args, cwd) {
|
|
|
291
291
|
return await runCommand(cmd, args, cwd);
|
|
292
292
|
}
|
|
293
293
|
catch {
|
|
294
|
-
return
|
|
294
|
+
return '';
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
function runCommand(cmd, args, cwd, timeoutMs = 120_000, env = process.env) {
|
|
298
298
|
return new Promise((resolve, reject) => {
|
|
299
|
-
let tail =
|
|
299
|
+
let tail = '';
|
|
300
300
|
let timedOut = false;
|
|
301
301
|
let settled = false;
|
|
302
302
|
let timeoutTimer = null;
|
|
@@ -317,29 +317,29 @@ function runCommand(cmd, args, cwd, timeoutMs = 120_000, env = process.env) {
|
|
|
317
317
|
clearTimeout(killTimer);
|
|
318
318
|
fn();
|
|
319
319
|
};
|
|
320
|
-
const child = spawn(cmd, args, { cwd, env, stdio: [
|
|
320
|
+
const child = spawn(cmd, args, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
321
321
|
timeoutTimer = setTimeout(() => {
|
|
322
322
|
timedOut = true;
|
|
323
|
-
child.kill(
|
|
323
|
+
child.kill('SIGTERM');
|
|
324
324
|
killTimer = setTimeout(() => {
|
|
325
|
-
child.kill(
|
|
325
|
+
child.kill('SIGKILL');
|
|
326
326
|
}, 5_000);
|
|
327
327
|
}, timeoutMs);
|
|
328
|
-
child.stdout?.on(
|
|
329
|
-
child.stderr?.on(
|
|
330
|
-
child.once(
|
|
328
|
+
child.stdout?.on('data', append);
|
|
329
|
+
child.stderr?.on('data', append);
|
|
330
|
+
child.once('error', (err) => {
|
|
331
331
|
settle(() => reject(err));
|
|
332
332
|
});
|
|
333
|
-
child.once(
|
|
333
|
+
child.once('close', (code, signal) => {
|
|
334
334
|
if (timedOut) {
|
|
335
|
-
settle(() => reject(new Error(`command timed out after ${timeoutMs}ms: ${cmd} ${args.join(
|
|
335
|
+
settle(() => reject(new Error(`command timed out after ${timeoutMs}ms: ${cmd} ${args.join(' ')}\n${tail}`)));
|
|
336
336
|
return;
|
|
337
337
|
}
|
|
338
338
|
if (code === 0) {
|
|
339
339
|
settle(() => resolve(tail));
|
|
340
340
|
}
|
|
341
341
|
else {
|
|
342
|
-
settle(() => reject(new Error(`command failed (${code ?? signal}): ${cmd} ${args.join(
|
|
342
|
+
settle(() => reject(new Error(`command failed (${code ?? signal}): ${cmd} ${args.join(' ')}\n${tail}`)));
|
|
343
343
|
}
|
|
344
344
|
});
|
|
345
345
|
});
|
|
@@ -366,75 +366,75 @@ function assertSafeCustomWorkspacePath(plan, candidate = plan.workspaceDir) {
|
|
|
366
366
|
function assertWritableWorkspaceDir(dir) {
|
|
367
367
|
fs.accessSync(dir, fs.constants.R_OK | fs.constants.W_OK | fs.constants.X_OK);
|
|
368
368
|
const probe = path.join(dir, `.parall-workspace-check-${process.pid}-${Date.now()}`);
|
|
369
|
-
const fd = fs.openSync(probe,
|
|
369
|
+
const fd = fs.openSync(probe, 'wx', 0o600);
|
|
370
370
|
fs.closeSync(fd);
|
|
371
371
|
fs.unlinkSync(probe);
|
|
372
372
|
}
|
|
373
373
|
function workspacePathDenyReason(value) {
|
|
374
|
-
if (value ===
|
|
375
|
-
return
|
|
376
|
-
if ([
|
|
377
|
-
return
|
|
374
|
+
if (value === '/')
|
|
375
|
+
return 'the filesystem root';
|
|
376
|
+
if (['/tmp', '/private/tmp', '/var/tmp', '/Users', '/home'].includes(value)) {
|
|
377
|
+
return 'a shared or home root directory';
|
|
378
378
|
}
|
|
379
379
|
for (const root of [
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
380
|
+
'/Applications',
|
|
381
|
+
'/bin',
|
|
382
|
+
'/boot',
|
|
383
|
+
'/dev',
|
|
384
|
+
'/etc',
|
|
385
|
+
'/Library',
|
|
386
|
+
'/private/etc',
|
|
387
|
+
'/private/var/db',
|
|
388
|
+
'/proc',
|
|
389
|
+
'/root',
|
|
390
|
+
'/run',
|
|
391
|
+
'/sbin',
|
|
392
|
+
'/System',
|
|
393
|
+
'/sys',
|
|
394
|
+
'/usr',
|
|
395
|
+
'/var/db',
|
|
396
|
+
'/var/root',
|
|
397
397
|
]) {
|
|
398
398
|
if (value === root || value.startsWith(`${root}/`)) {
|
|
399
|
-
return
|
|
399
|
+
return 'a system directory';
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
|
-
const parts = value.split(
|
|
403
|
-
if (parts.includes(
|
|
404
|
-
return
|
|
405
|
-
if (parts.length === 2 && (parts[0] ===
|
|
406
|
-
return
|
|
402
|
+
const parts = value.split('/').filter(Boolean);
|
|
403
|
+
if (parts.includes('.git'))
|
|
404
|
+
return 'a git metadata directory';
|
|
405
|
+
if (parts.length === 2 && (parts[0] === 'Users' || parts[0] === 'home')) {
|
|
406
|
+
return 'a home directory';
|
|
407
407
|
}
|
|
408
|
-
if (parts.length >= 3 && (parts[0] ===
|
|
408
|
+
if (parts.length >= 3 && (parts[0] === 'Users' || parts[0] === 'home')) {
|
|
409
409
|
const homeChild = parts[2];
|
|
410
410
|
if ([
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
411
|
+
'.aws',
|
|
412
|
+
'.azure',
|
|
413
|
+
'.claude',
|
|
414
|
+
'.codex',
|
|
415
|
+
'.config',
|
|
416
|
+
'.docker',
|
|
417
|
+
'.gnupg',
|
|
418
|
+
'.kube',
|
|
419
|
+
'.local',
|
|
420
|
+
'.npm',
|
|
421
|
+
'.ssh',
|
|
422
|
+
'.parall-agent',
|
|
423
|
+
'.parall-daemon',
|
|
424
|
+
'Library',
|
|
425
425
|
].includes(homeChild)) {
|
|
426
|
-
return
|
|
426
|
+
return 'a credential or application state directory';
|
|
427
427
|
}
|
|
428
428
|
}
|
|
429
|
-
return
|
|
429
|
+
return '';
|
|
430
430
|
}
|
|
431
431
|
function isAncestorPath(parent, child) {
|
|
432
432
|
const relative = path.relative(parent, child);
|
|
433
|
-
return relative !==
|
|
433
|
+
return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);
|
|
434
434
|
}
|
|
435
435
|
function toPolicyPath(value) {
|
|
436
|
-
return path.resolve(value).split(path.sep).join(
|
|
436
|
+
return path.resolve(value).split(path.sep).join('/');
|
|
437
437
|
}
|
|
438
438
|
function isNodeError(err) {
|
|
439
|
-
return err instanceof Error &&
|
|
439
|
+
return err instanceof Error && 'code' in err;
|
|
440
440
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/daemon",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.32.0",
|
|
4
4
|
"description": "Parall local agent runtime — daemon supervisor + bridge runtimes, bundled as standalone JS files",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@parall/agent-core": "1.
|
|
33
|
-
"@parall/
|
|
34
|
-
"@parall/
|
|
35
|
-
"@parall/
|
|
36
|
-
"@parall/
|
|
32
|
+
"@parall/agent-core": "1.32.0",
|
|
33
|
+
"@parall/claude-agent": "1.32.0",
|
|
34
|
+
"@parall/codex-agent": "1.32.0",
|
|
35
|
+
"@parall/openclaw-agent": "1.32.0",
|
|
36
|
+
"@parall/sdk": "1.32.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^22.0.0",
|