@parall/daemon 1.29.2 → 1.30.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.
@@ -0,0 +1,7 @@
1
+ import type { FilesystemEntry } from "@parall/sdk";
2
+ export declare function browseDenyReason(value: string): string;
3
+ export declare function listDirectory(dirPath: string): Promise<{
4
+ entries: FilesystemEntry[];
5
+ error?: string;
6
+ }>;
7
+ //# sourceMappingURL=filesystem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../src/filesystem.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAqCnD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAkBtD;AAoBD,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA8C5G"}
@@ -0,0 +1,118 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import * as os from "os";
4
+ const MAX_ENTRIES = 200;
5
+ const SYSTEM_DIR_PREFIXES = [
6
+ "/Applications",
7
+ "/bin",
8
+ "/boot",
9
+ "/dev",
10
+ "/etc",
11
+ "/Library",
12
+ "/private",
13
+ "/proc",
14
+ "/root",
15
+ "/run",
16
+ "/sbin",
17
+ "/System",
18
+ "/sys",
19
+ "/usr",
20
+ "/var",
21
+ ];
22
+ const CREDENTIAL_DIR_NAMES = new Set([
23
+ ".aws",
24
+ ".azure",
25
+ ".claude",
26
+ ".codex",
27
+ ".config",
28
+ ".docker",
29
+ ".gnupg",
30
+ ".kube",
31
+ ".npm",
32
+ ".ssh",
33
+ ".parall-agent",
34
+ ".parall-daemon",
35
+ ]);
36
+ export function browseDenyReason(value) {
37
+ const normalized = path.resolve(value).split(path.sep).join("/");
38
+ if (normalized === "/")
39
+ return "";
40
+ for (const prefix of SYSTEM_DIR_PREFIXES) {
41
+ if (normalized === prefix || normalized.startsWith(`${prefix}/`)) {
42
+ return "a system directory";
43
+ }
44
+ }
45
+ const parts = normalized.split("/").filter(Boolean);
46
+ for (const part of parts) {
47
+ if (CREDENTIAL_DIR_NAMES.has(part)) {
48
+ return "a credential or application state directory";
49
+ }
50
+ }
51
+ return "";
52
+ }
53
+ function syntheticRoots() {
54
+ const roots = [];
55
+ const platform = os.platform();
56
+ const candidates = platform === "darwin"
57
+ ? ["/Users", os.homedir()]
58
+ : ["/home", os.homedir()];
59
+ for (const dir of [...new Set(candidates)]) {
60
+ try {
61
+ fs.accessSync(dir, fs.constants.R_OK);
62
+ roots.push({ name: dir, type: "dir" });
63
+ }
64
+ catch {
65
+ // not accessible
66
+ }
67
+ }
68
+ return roots;
69
+ }
70
+ export async function listDirectory(dirPath) {
71
+ const resolved = path.resolve(dirPath);
72
+ const normalized = resolved.split(path.sep).join("/");
73
+ if (normalized === "/") {
74
+ return { entries: syntheticRoots() };
75
+ }
76
+ const deny = browseDenyReason(normalized);
77
+ if (deny) {
78
+ return { entries: [], error: `Access denied: ${deny}` };
79
+ }
80
+ let realPath;
81
+ try {
82
+ realPath = fs.realpathSync(resolved);
83
+ }
84
+ catch (err) {
85
+ const code = err.code;
86
+ if (code === "ENOENT")
87
+ return { entries: [], error: "Directory not found" };
88
+ return { entries: [], error: "Permission denied" };
89
+ }
90
+ const realDeny = browseDenyReason(realPath.split(path.sep).join("/"));
91
+ if (realDeny) {
92
+ return { entries: [], error: `Access denied: ${realDeny}` };
93
+ }
94
+ let dirents;
95
+ try {
96
+ dirents = fs.readdirSync(realPath, { withFileTypes: true });
97
+ }
98
+ catch (err) {
99
+ const code = err.code;
100
+ if (code === "ENOENT")
101
+ return { entries: [], error: "Directory not found" };
102
+ if (code === "EACCES" || code === "EPERM")
103
+ return { entries: [], error: "Permission denied" };
104
+ return { entries: [], error: `Failed to read directory: ${code ?? String(err)}` };
105
+ }
106
+ const entries = [];
107
+ for (const d of dirents) {
108
+ if (!d.isDirectory())
109
+ continue;
110
+ if (d.name.startsWith("."))
111
+ continue;
112
+ entries.push({ name: d.name, type: "dir" });
113
+ if (entries.length >= MAX_ENTRIES)
114
+ break;
115
+ }
116
+ entries.sort((a, b) => a.name.localeCompare(b.name));
117
+ return { entries };
118
+ }
@@ -49,6 +49,7 @@ export declare class DaemonSupervisor {
49
49
  private fetchAttachedAgent;
50
50
  private handleAgentAttached;
51
51
  private handleAgentDetached;
52
+ private handleFilesystemBrowse;
52
53
  private handleWorkspaceSetupRequested;
53
54
  private scheduleWorkspaceSetupRetry;
54
55
  private clearWorkspaceSetupRetry;
@@ -1 +1 @@
1
- {"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAA2N,MAAM,aAAa,CAAC;AACpQ,OAAO,EACL,KAAK,kBAAkB,EAMxB,MAAM,aAAa,CAAC;AAiBrB;;;;GAIG;AACH,iBAAS,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAa3E;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAyB5B;;;;;;;;;;GAUG;AACH,qBAAa,gBAAgB;IAazB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqC;IAC/E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,WAAW,CAA6B;gBAG7B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,aAAa;IAGrC,yEAAyE;IACnE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiF7C,sEAAsE;IAChE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAiCb,kBAAkB;YAqClB,aAAa;IAqD3B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;YA6CX,kBAAkB;YAqBlB,mBAAmB;YAgBnB,mBAAmB;YAmBnB,6BAA6B;IAqC3C,OAAO,CAAC,2BAA2B;IAYnC,OAAO,CAAC,wBAAwB;YAUlB,oBAAoB;YAcpB,kBAAkB;YAYlB,eAAe;YAcf,UAAU;YAoBV,cAAc;IA2D5B,OAAO,CAAC,UAAU;YA0EJ,cAAc;IAyB5B,OAAO,CAAC,0BAA0B;CA8BnC"}
1
+ {"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../src/supervisor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,YAAY,EAA6P,MAAM,aAAa,CAAC;AAEtS,OAAO,EACL,KAAK,kBAAkB,EAMxB,MAAM,aAAa,CAAC;AAiBrB;;;;GAIG;AACH,iBAAS,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAa3E;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAyB5B;;;;;;;;;;GAUG;AACH,qBAAa,gBAAgB;IAazB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqC;IAC/E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,WAAW,CAA6B;gBAG7B,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,aAAa;IAGrC,yEAAyE;IACnE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAsF7C,sEAAsE;IAChE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAiCb,kBAAkB;YAqClB,aAAa;IAqD3B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;YA6CX,kBAAkB;YAqBlB,mBAAmB;YAgBnB,mBAAmB;YAmBnB,sBAAsB;YAwBtB,6BAA6B;IAqC3C,OAAO,CAAC,2BAA2B;IAYnC,OAAO,CAAC,wBAAwB;YAUlB,oBAAoB;YAcpB,kBAAkB;YAYlB,eAAe;YAcf,UAAU;YAoBV,cAAc;IA2D5B,OAAO,CAAC,UAAU;YA0EJ,cAAc;IAyB5B,OAAO,CAAC,0BAA0B;CA8BnC"}
@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import { ParallWs } from "@parall/sdk";
5
+ import { listDirectory } from "./filesystem.js";
5
6
  import { agentClaudeCredentialsFileFor, agentClaudeHomeFor, agentStateDirFor, agentWorkspaceDirFor, sharedClaudeCredentialsFileFor, } from "./config.js";
6
7
  import { assertAgentKey, getRuntimeAdapter } from "./runtimes.js";
7
8
  import { prepareWorkspace } from "./workspace.js";
@@ -117,6 +118,10 @@ export class DaemonSupervisor {
117
118
  this.log.info(`WS: workspace setup requested for agent ${data.agent_id}`);
118
119
  void this.handleWorkspaceSetupRequested(data.agent_id);
119
120
  });
121
+ this.ws.on("machine.filesystem.browse", (data) => {
122
+ this.log.info(`WS: filesystem browse requested: ${data.path}`);
123
+ void this.handleFilesystemBrowse(data.request_id, data.path);
124
+ });
120
125
  this.ws.on("machine.stop", (data) => {
121
126
  this.log.info(`WS: machine.stop received (reason=${data.reason ?? "none"})`);
122
127
  void this.stop();
@@ -343,6 +348,31 @@ export class DaemonSupervisor {
343
348
  await this.terminateChild(state);
344
349
  this.children.delete(agentId);
345
350
  }
351
+ async handleFilesystemBrowse(requestId, dirPath) {
352
+ try {
353
+ const result = await listDirectory(dirPath);
354
+ await this.client.postBrowseResponse(requestId, {
355
+ request_id: requestId,
356
+ path: dirPath,
357
+ entries: result.entries,
358
+ error: result.error,
359
+ });
360
+ }
361
+ catch (err) {
362
+ this.log.warn(`filesystem browse failed: ${String(err)}`);
363
+ try {
364
+ await this.client.postBrowseResponse(requestId, {
365
+ request_id: requestId,
366
+ path: dirPath,
367
+ entries: [],
368
+ error: String(err),
369
+ });
370
+ }
371
+ catch {
372
+ // best-effort
373
+ }
374
+ }
375
+ }
346
376
  async handleWorkspaceSetupRequested(agentId) {
347
377
  if (this.spawningAgents.has(agentId)) {
348
378
  this.log.info(`agent ${agentId}: workspace setup already in progress; queueing one restart`);
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAA4C,YAAY,EAAE,MAAM,aAAa,CAAC;AAiBzG,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,aAAa,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CA6ClF"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAA4C,YAAY,EAAE,MAAM,aAAa,CAAC;AAiBzG,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,GAAG,EAAE,aAAa,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8ClF"}
package/dist/workspace.js CHANGED
@@ -8,7 +8,8 @@ const DEFAULT_SETUP_TIMEOUT_SEC = 600;
8
8
  export async function prepareWorkspace(opts) {
9
9
  const prior = opts.attached.workspace_state;
10
10
  const plan = buildWorkspacePlan(opts.attached.daemon_config, opts.defaultWorkspaceDir, prior?.config_hash);
11
- if (!opts.attached.daemon_config && !prior) {
11
+ const hasConfig = opts.attached.daemon_config && Object.keys(opts.attached.daemon_config).length > 0;
12
+ if (!hasConfig && !prior) {
12
13
  return plan.workspaceDir;
13
14
  }
14
15
  let forceSetup = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/daemon",
3
- "version": "1.29.2",
3
+ "version": "1.30.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/sdk": "1.29.2",
33
- "@parall/codex-agent": "1.29.2",
34
- "@parall/claude-agent": "1.29.2",
35
- "@parall/agent-core": "1.29.2",
36
- "@parall/openclaw-agent": "1.29.2"
32
+ "@parall/agent-core": "1.30.0",
33
+ "@parall/sdk": "1.30.0",
34
+ "@parall/claude-agent": "1.30.0",
35
+ "@parall/codex-agent": "1.30.0",
36
+ "@parall/openclaw-agent": "1.30.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.0.0",