@agentbean/daemon 0.1.27 → 0.1.29

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.
@@ -51,6 +51,9 @@ function promptWithAttachments(prompt, attachments) {
51
51
  .join('\n');
52
52
  return `${prompt}\n\n用户随消息附加了以下本地文件,请在需要时读取并使用:\n${list}`;
53
53
  }
54
+ function promptWithWorkspaceOutput(prompt, outputDir) {
55
+ return `${prompt}\n\n如果本次任务会生成图片、文档、数据或其他文件,请把最终产物保存到这个 AgentBean 输出目录:\n${outputDir}\n保存后在回复中说明文件名即可,系统会自动同步并在聊天中展示预览。`;
56
+ }
54
57
  export class AgentInstance {
55
58
  config;
56
59
  adapter;
@@ -96,7 +99,7 @@ export class AgentInstance {
96
99
  let archivedFiles = [];
97
100
  try {
98
101
  const downloadedAttachments = await downloadAttachments({ serverUrl, token, run, attachments: req.attachments });
99
- const prompt = promptWithAttachments(req.prompt, downloadedAttachments);
102
+ const prompt = promptWithWorkspaceOutput(promptWithAttachments(req.prompt, downloadedAttachments), run.outputDir);
100
103
  const rawBody = await this.adapter.ask({
101
104
  prompt,
102
105
  history: req.history ?? [],
@@ -50,6 +50,9 @@ function promptWithAttachments(prompt, attachments) {
50
50
  .join('\n');
51
51
  return `${prompt}\n\n用户随消息附加了以下本地文件,请在需要时读取并使用:\n${list}`;
52
52
  }
53
+ function promptWithWorkspaceOutput(prompt, outputDir) {
54
+ return `${prompt}\n\n如果本次任务会生成图片、文档、数据或其他文件,请把最终产物保存到这个 AgentBean 输出目录:\n${outputDir}\n保存后在回复中说明文件名即可,系统会自动同步并在聊天中展示预览。`;
55
+ }
53
56
  export function createConnection(cfg, adapter) {
54
57
  let socket = null;
55
58
  let heartbeatTimer = null;
@@ -112,7 +115,7 @@ export function createConnection(cfg, adapter) {
112
115
  run,
113
116
  attachments: req.attachments,
114
117
  });
115
- const prompt = promptWithAttachments(req.prompt, downloadedAttachments);
118
+ const prompt = promptWithWorkspaceOutput(promptWithAttachments(req.prompt, downloadedAttachments), run.outputDir);
116
119
  const rawBody = await adapter.ask({
117
120
  prompt,
118
121
  history: req.history ?? [],
@@ -1,7 +1,7 @@
1
1
  import { io } from 'socket.io-client';
2
2
  import { execFile } from 'node:child_process';
3
3
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
4
- import { join } from 'node:path';
4
+ import { basename, isAbsolute, join } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
  import { promisify } from 'node:util';
7
7
  import { logger } from './log.js';
@@ -29,6 +29,50 @@ function agentSlug(name) {
29
29
  function scannedAgentId(deviceId, name) {
30
30
  return `scan-${deviceId}-${agentSlug(name)}`;
31
31
  }
32
+ function normalizeAdapterKind(kind) {
33
+ const normalized = (kind ?? '').trim().toLowerCase().replace(/[_\s]+/g, '-');
34
+ if (normalized === 'claude' || normalized === 'claude-code')
35
+ return 'claude-code';
36
+ if (normalized === 'codex' || normalized === 'codex-cli')
37
+ return 'codex';
38
+ if (normalized === 'kimi' || normalized === 'kimi-cli')
39
+ return 'kimi-cli';
40
+ return normalized;
41
+ }
42
+ function runtimeScoreForCustomAgent(runtime, custom) {
43
+ if (!runtime.installed || !runtime.command?.trim())
44
+ return 0;
45
+ const runtimeCommand = runtime.command.trim();
46
+ const customCommand = custom.command?.trim() ?? '';
47
+ if (runtimeCommand && customCommand && runtimeCommand === customCommand)
48
+ return 100;
49
+ const runtimeBase = basename(runtimeCommand).toLowerCase();
50
+ const customBase = customCommand ? basename(customCommand).toLowerCase() : '';
51
+ if (runtimeBase && customBase && runtimeBase === customBase)
52
+ return 90;
53
+ const runtimeKind = normalizeAdapterKind(runtime.adapterKind);
54
+ const customKind = normalizeAdapterKind(custom.adapterKind);
55
+ if (runtimeKind === 'kimi-cli' && customKind === 'codex' && customCommand.toLowerCase().includes('kimi'))
56
+ return 85;
57
+ if (runtimeKind && customKind && runtimeKind === customKind)
58
+ return 70;
59
+ return 0;
60
+ }
61
+ export function resolveCustomAgentRuntime(custom, runtimes) {
62
+ const configured = custom.command?.trim() ?? '';
63
+ const configuredAbsoluteExists = configured && isAbsolute(configured) && existsSync(configured);
64
+ const bestRuntime = [...runtimes]
65
+ .map((runtime) => ({ runtime, score: runtimeScoreForCustomAgent(runtime, custom) }))
66
+ .filter((candidate) => candidate.score > 0)
67
+ .sort((a, b) => b.score - a.score)[0]?.runtime;
68
+ if (configuredAbsoluteExists) {
69
+ return { command: configured, runtime: bestRuntime };
70
+ }
71
+ if (bestRuntime?.command?.trim()) {
72
+ return { command: bestRuntime.command.trim(), runtime: bestRuntime };
73
+ }
74
+ return { command: configured };
75
+ }
32
76
  export function nativeDirectoryPickerCommands(platform = process.platform) {
33
77
  if (platform === 'darwin') {
34
78
  return [{ command: 'osascript', args: ['-e', 'POSIX path of (choose folder with prompt "选择项目目录")'] }];
@@ -184,10 +228,12 @@ export function createDeviceDaemon(cfg, agents) {
184
228
  const httpBase = cfg.server.url.replace(/\/agent$/, '');
185
229
  let firstConnect = true;
186
230
  const systemInfo = collectSystemInfo();
231
+ let latestRuntimes = [];
187
232
  const publicAgents = Array.from(agents.values())
188
233
  .filter((a) => a.visibility === 'public')
189
234
  .map((a) => a.publicMeta);
190
235
  function emitRegister(sock, payload) {
236
+ latestRuntimes = payload.runtimes.filter((runtime) => runtime.installed);
191
237
  if (payload.runtimes.length > 0) {
192
238
  sock.emit('device:register-runtimes', { runtimes: payload.runtimes }, (ack) => {
193
239
  if (!ack?.ok)
@@ -323,8 +369,9 @@ export function createDeviceDaemon(cfg, agents) {
323
369
  });
324
370
  socket.on('dispatch', (req) => {
325
371
  let agent = agents.get(req.agentId);
326
- if (!agent && req.customAgent) {
372
+ if (req.customAgent) {
327
373
  const custom = req.customAgent;
374
+ const resolvedRuntime = resolveCustomAgentRuntime(custom, latestRuntimes);
328
375
  const entry = {
329
376
  id: custom.id,
330
377
  name: custom.name,
@@ -332,7 +379,7 @@ export function createDeviceDaemon(cfg, agents) {
332
379
  category: 'executor-hosted',
333
380
  adapter: {
334
381
  kind: custom.adapterKind,
335
- command: custom.command,
382
+ command: resolvedRuntime.command,
336
383
  args: custom.args ?? [],
337
384
  cwd: custom.cwd ?? undefined,
338
385
  workspace: custom.cwd ?? undefined,
@@ -343,7 +390,14 @@ export function createDeviceDaemon(cfg, agents) {
343
390
  try {
344
391
  agent = new AgentInstance(entry, pickAdapter(entry.adapter));
345
392
  agents.set(req.agentId, agent);
346
- logger.info({ agentId: req.agentId, kind: entry.adapter.kind, cwd: entry.adapter.cwd }, 'custom agent instance created for dispatch');
393
+ logger.info({
394
+ agentId: req.agentId,
395
+ kind: entry.adapter.kind,
396
+ command: entry.adapter.command,
397
+ configuredCommand: custom.command,
398
+ runtimeCommand: resolvedRuntime.runtime?.command,
399
+ cwd: entry.adapter.cwd,
400
+ }, 'custom agent instance created for dispatch');
347
401
  }
348
402
  catch (err) {
349
403
  logger.warn({ agentId: req.agentId, err: errorMessage(err) }, 'failed to create custom dispatch agent');
@@ -51,7 +51,7 @@ function normalizeCandidatePath(raw) {
51
51
  function extractMentionedFiles(reply, workspace, dispatchStart) {
52
52
  const candidates = new Set();
53
53
  const markdownLinkRe = /!?\[[^\]]*]\(([^)\s]+)\)/g;
54
- const plainPathRe = /(?:^|[\s"'`(<])((?:~?\/|\.{1,2}\/)?[\w@%+=:,./-]+\.(?:png|jpe?g|gif|webp|svg|pdf|txt|csv|json|md|mp4|mov|zip))(?:$|[\s"'`)>.,;:])/gim;
54
+ const plainPathRe = /(?:^|[\s"'`(<::])((?:~?\/|\.{1,2}\/)?[\w@%+=:,./-]+\.(?:png|jpe?g|gif|webp|svg|pdf|txt|csv|json|md|mp4|mov|zip))(?:$|[\s"'`)>.,;::])/gim;
55
55
  let match;
56
56
  while ((match = markdownLinkRe.exec(reply)) !== null) {
57
57
  const normalized = normalizeCandidatePath(match[1]);
package/dist/scanner.js CHANGED
@@ -285,14 +285,14 @@ async function checkOpenClawGateway() {
285
285
  return null;
286
286
  const status = await run(path, ["gateway", "status"]);
287
287
  const running = status.includes("running") || status.includes("✓");
288
- if (running) {
289
- const agentId = parseOpenClawAgentId(await run(path, ["agents", "list", "--json"])) ?? "main";
288
+ const agentId = parseOpenClawAgentId(await run(path, ["agents", "list", "--json"]));
289
+ if (running || agentId) {
290
290
  return {
291
291
  category: "agentos-hosted",
292
292
  name: "OpenClaw-Agent",
293
293
  adapterKind: "openclaw",
294
294
  command: path,
295
- args: ["agent", "--agent", agentId],
295
+ args: ["agent", "--agent", agentId ?? "main"],
296
296
  source: "gateway",
297
297
  };
298
298
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentbean/daemon",
3
3
  "private": false,
4
- "version": "0.1.27",
4
+ "version": "0.1.29",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "bin": {