@agentbean/daemon 0.1.32 → 0.1.34

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.
@@ -27,11 +27,22 @@ function buildPrompt(input, systemPrompt) {
27
27
  }
28
28
  const ANSI_RE = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
29
29
  const BOX_ONLY_RE = /^[\s─━═╭╮╰╯│┃┌┐└┘├┤┬┴┼]+$/;
30
+ function stripEchoedQueryPreamble(lines) {
31
+ const initIdx = lines.findIndex((line) => {
32
+ const trimmed = line.trim();
33
+ return trimmed === 'Initializing agent...' || trimmed === 'Initializing agent…';
34
+ });
35
+ if (initIdx < 0)
36
+ return lines;
37
+ if (!lines.slice(0, initIdx).some((line) => line.trim().startsWith('Query:')))
38
+ return lines;
39
+ return lines.slice(initIdx + 1);
40
+ }
30
41
  export function extractHermesReply(output) {
31
- const lines = output
42
+ const lines = stripEchoedQueryPreamble(output
32
43
  .replace(ANSI_RE, '')
33
44
  .replace(/\r\n?/g, '\n')
34
- .split('\n');
45
+ .split('\n'));
35
46
  let boxStart = -1;
36
47
  for (let i = lines.length - 1; i >= 0; i -= 1) {
37
48
  if (lines[i]?.trim().startsWith('╭')) {
@@ -106,7 +106,7 @@ export class AgentInstance {
106
106
  systemPrompt: this.config.adapter.systemPrompt,
107
107
  workspace: projectWorkspace,
108
108
  sandboxProfilePath: req.sandboxed && isSandboxAvailable()
109
- ? generateSandboxProfile(this.id, this.config.adapter.command)
109
+ ? generateSandboxProfile(this.id, this.config.adapter.command, [run.runDir])
110
110
  : undefined,
111
111
  env: { ...(this.config.adapter.env ?? {}), ...workspaceEnv(run) },
112
112
  }, ctl.signal);
@@ -83,7 +83,17 @@ export function resolveCustomAgentRuntime(custom, runtimes) {
83
83
  }
84
84
  export function nativeDirectoryPickerCommands(platform = process.platform) {
85
85
  if (platform === 'darwin') {
86
- return [{ command: 'osascript', args: ['-e', 'POSIX path of (choose folder with prompt "选择项目目录")'] }];
86
+ return [{
87
+ command: 'osascript',
88
+ args: [
89
+ '-e',
90
+ 'tell application "Finder" to activate',
91
+ '-e',
92
+ 'delay 0.2',
93
+ '-e',
94
+ 'POSIX path of (choose folder with prompt "选择项目目录" default location (path to home folder))',
95
+ ],
96
+ }];
87
97
  }
88
98
  if (platform === 'win32') {
89
99
  return [{
@@ -195,6 +205,8 @@ export function createDeviceSocketOptions(input) {
195
205
  directoryPicker: true,
196
206
  },
197
207
  },
208
+ transports: ['websocket', 'polling'],
209
+ rememberUpgrade: true,
198
210
  reconnection: true,
199
211
  reconnectionDelay: 1_000,
200
212
  reconnectionDelayMax: 10_000,
package/dist/index.js CHANGED
@@ -153,7 +153,8 @@ Options:
153
153
  console.error('Error: --server-url is required with --invite.');
154
154
  process.exit(1);
155
155
  }
156
- const auth = await runInviteMode(serverUrl, values.invite);
156
+ const inviteDeviceId = values['device-id'] ?? await getDeviceId();
157
+ const auth = await runInviteMode(serverUrl, values.invite, inviteDeviceId);
157
158
  serverUrl = auth.serverUrl;
158
159
  token = auth.token;
159
160
  networkId = auth.networkId ?? networkId;
@@ -238,7 +239,7 @@ export function socketErrorMessage(err) {
238
239
  .map((value) => value.trim());
239
240
  return [...new Set(details)].join(': ') || 'unknown socket error';
240
241
  }
241
- async function runInviteMode(serverUrl, inviteCode) {
242
+ async function runInviteMode(serverUrl, inviteCode, deviceId) {
242
243
  const { io } = await import('socket.io-client');
243
244
  const { execFile } = await import('node:child_process');
244
245
  const baseUrl = normalizeBaseUrl(serverUrl);
@@ -265,7 +266,7 @@ async function runInviteMode(serverUrl, inviteCode) {
265
266
  clearTimeout(connectTimer);
266
267
  logger.info('invite mode: connected, validating invite code');
267
268
  console.log('Connected. Validating invite code...');
268
- socket.emit('auth:invite:validate', { code: inviteCode }, (res) => {
269
+ socket.emit('auth:invite:validate', { code: inviteCode, deviceId }, (res) => {
269
270
  if (!res?.ok) {
270
271
  fail(new Error(res?.error ?? 'invalid invite code'));
271
272
  return;
package/dist/sandbox.js CHANGED
@@ -21,16 +21,21 @@ export function isSandboxAvailable() {
21
21
  return false;
22
22
  }
23
23
  }
24
- export function generateSandboxProfile(agentId, runtimePath) {
24
+ export function generateSandboxProfile(agentId, runtimePath, writableDirs = []) {
25
25
  const workspaceDir = getWorkspaceDir(agentId);
26
26
  const runtimeDir = runtimePath.includes('/') ? dirname(runtimePath) : '/usr/bin';
27
27
  const profilePath = `/tmp/agentbean-sandbox-${agentId}.sb`;
28
+ const extraWritableRules = writableDirs
29
+ .filter(Boolean)
30
+ .map((dir) => `(allow file-read* file-write*
31
+ (subpath "${escapeSchemeString(dir)}"))`)
32
+ .join('\n');
28
33
  const profile = `(version 1)
29
34
  (allow file-read* file-write*
30
35
  (subpath "${escapeSchemeString(workspaceDir)}"))
31
36
  (allow file-read* file-write*
32
37
  (subpath "/tmp"))
33
- (allow file-read*
38
+ ${extraWritableRules ? `${extraWritableRules}\n` : ''}(allow file-read*
34
39
  (subpath "${escapeSchemeString(runtimeDir)}"))
35
40
  (allow file-read*
36
41
  (subpath "/bin")
@@ -34,6 +34,14 @@ function uniqueDestination(dir, filename) {
34
34
  }
35
35
  return candidate;
36
36
  }
37
+ function fileNamePreference(path) {
38
+ const name = basename(path).toLowerCase();
39
+ if (/^ig_[a-f0-9]{32,}\.(png|jpe?g|gif|webp)$/i.test(name))
40
+ return 0;
41
+ if (/^(image|output|generated)[._-]?\d*\.(png|jpe?g|gif|webp)$/i.test(name))
42
+ return 1;
43
+ return 2;
44
+ }
37
45
  function escapeRegExp(value) {
38
46
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
39
47
  }
@@ -94,12 +102,14 @@ export function workspaceEnv(run) {
94
102
  }
95
103
  export function archiveOutputFiles(run, files) {
96
104
  const archived = [];
97
- const seen = new Set();
105
+ const candidates = new Map();
106
+ const hashOrder = [];
107
+ const seenPaths = new Set();
98
108
  for (const file of files) {
99
109
  const abs = isAbsolute(file) ? file : resolve(file);
100
- if (seen.has(abs))
110
+ if (seenPaths.has(abs))
101
111
  continue;
102
- seen.add(abs);
112
+ seenPaths.add(abs);
103
113
  let st;
104
114
  try {
105
115
  st = statSync(abs);
@@ -109,6 +119,21 @@ export function archiveOutputFiles(run, files) {
109
119
  catch {
110
120
  continue;
111
121
  }
122
+ const hash = fileHash(abs);
123
+ const current = candidates.get(hash);
124
+ if (!current) {
125
+ candidates.set(hash, { abs, hash });
126
+ hashOrder.push(hash);
127
+ }
128
+ else if (fileNamePreference(abs) > fileNamePreference(current.abs)) {
129
+ candidates.set(hash, { abs, hash });
130
+ }
131
+ }
132
+ for (const hash of hashOrder) {
133
+ const candidate = candidates.get(hash);
134
+ if (!candidate)
135
+ continue;
136
+ const abs = candidate.abs;
112
137
  const alreadyInRun = relative(run.runDir, abs);
113
138
  const archivedPath = alreadyInRun && !alreadyInRun.startsWith('..') && !isAbsolute(alreadyInRun)
114
139
  ? abs
@@ -121,7 +146,7 @@ export function archiveOutputFiles(run, files) {
121
146
  archivedPath,
122
147
  relativePath: relative(run.agentDir, archivedPath),
123
148
  pathKind: 'output',
124
- sha256: fileHash(archivedPath),
149
+ sha256: candidate.hash,
125
150
  sizeBytes,
126
151
  });
127
152
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentbean/daemon",
3
3
  "private": false,
4
- "version": "0.1.32",
4
+ "version": "0.1.34",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "bin": {