@agentbean/daemon 0.1.12 → 0.1.14

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.
@@ -1,3 +1,5 @@
1
+ import { writeFileSync } from 'node:fs';
2
+ import { basename, join } from 'node:path';
1
3
  import { logger } from './log.js';
2
4
  import { uploadArtifact } from './uploader.js';
3
5
  import { postProcess } from './post-process.js';
@@ -16,6 +18,39 @@ function errorMessage(err) {
16
18
  catch { }
17
19
  return 'unknown error';
18
20
  }
21
+ function safeFilename(value) {
22
+ return basename(value).replace(/[^a-zA-Z0-9._-]/g, '-').replace(/^-+|-+$/g, '') || 'attachment';
23
+ }
24
+ async function downloadAttachments(input) {
25
+ const downloaded = [];
26
+ for (const attachment of input.attachments ?? []) {
27
+ const sep = attachment.downloadUrl.includes('?') ? '&' : '?';
28
+ const url = `${input.serverUrl}${attachment.downloadUrl}${sep}token=${encodeURIComponent(input.token)}`;
29
+ try {
30
+ const resp = await fetch(url);
31
+ if (!resp.ok) {
32
+ logger.warn({ id: attachment.id, status: resp.status }, 'attachment download rejected');
33
+ continue;
34
+ }
35
+ const bytes = Buffer.from(await resp.arrayBuffer());
36
+ const localPath = join(input.run.inputDir, `${attachment.id}-${safeFilename(attachment.filename)}`);
37
+ writeFileSync(localPath, bytes);
38
+ downloaded.push({ ...attachment, localPath });
39
+ }
40
+ catch (err) {
41
+ logger.warn({ id: attachment.id, err: err?.message }, 'attachment download failed');
42
+ }
43
+ }
44
+ return downloaded;
45
+ }
46
+ function promptWithAttachments(prompt, attachments) {
47
+ if (attachments.length === 0)
48
+ return prompt;
49
+ const list = attachments
50
+ .map((file) => `- ${file.filename} (${file.mimeType}, ${file.sizeBytes} bytes): ${file.localPath}`)
51
+ .join('\n');
52
+ return `${prompt}\n\n用户随消息附加了以下本地文件,请在需要时读取并使用:\n${list}`;
53
+ }
19
54
  export class AgentInstance {
20
55
  config;
21
56
  adapter;
@@ -58,8 +93,10 @@ export class AgentInstance {
58
93
  });
59
94
  let archivedFiles = [];
60
95
  try {
96
+ const downloadedAttachments = await downloadAttachments({ serverUrl, token, run, attachments: req.attachments });
97
+ const prompt = promptWithAttachments(req.prompt, downloadedAttachments);
61
98
  const rawBody = await this.adapter.ask({
62
- prompt: req.prompt,
99
+ prompt,
63
100
  history: req.history ?? [],
64
101
  systemPrompt: this.config.adapter.systemPrompt,
65
102
  workspace: projectWorkspace,
@@ -1,4 +1,6 @@
1
1
  import { io } from 'socket.io-client';
2
+ import { writeFileSync } from 'node:fs';
3
+ import { basename, join } from 'node:path';
2
4
  import { logger } from './log.js';
3
5
  import { uploadArtifact } from './uploader.js';
4
6
  import { postProcess } from './post-process.js';
@@ -16,6 +18,38 @@ function errorMessage(err) {
16
18
  catch { }
17
19
  return 'unknown error';
18
20
  }
21
+ function safeFilename(value) {
22
+ return basename(value).replace(/[^a-zA-Z0-9._-]/g, '-').replace(/^-+|-+$/g, '') || 'attachment';
23
+ }
24
+ async function downloadAttachments(input) {
25
+ const downloaded = [];
26
+ for (const attachment of input.attachments ?? []) {
27
+ const sep = attachment.downloadUrl.includes('?') ? '&' : '?';
28
+ const url = `${input.serverUrl}${attachment.downloadUrl}${sep}token=${encodeURIComponent(input.token)}`;
29
+ try {
30
+ const resp = await fetch(url);
31
+ if (!resp.ok) {
32
+ logger.warn({ id: attachment.id, status: resp.status }, 'attachment download rejected');
33
+ continue;
34
+ }
35
+ const localPath = join(input.run.inputDir, `${attachment.id}-${safeFilename(attachment.filename)}`);
36
+ writeFileSync(localPath, Buffer.from(await resp.arrayBuffer()));
37
+ downloaded.push({ ...attachment, localPath });
38
+ }
39
+ catch (err) {
40
+ logger.warn({ id: attachment.id, err: err?.message }, 'attachment download failed');
41
+ }
42
+ }
43
+ return downloaded;
44
+ }
45
+ function promptWithAttachments(prompt, attachments) {
46
+ if (attachments.length === 0)
47
+ return prompt;
48
+ const list = attachments
49
+ .map((file) => `- ${file.filename} (${file.mimeType}, ${file.sizeBytes} bytes): ${file.localPath}`)
50
+ .join('\n');
51
+ return `${prompt}\n\n用户随消息附加了以下本地文件,请在需要时读取并使用:\n${list}`;
52
+ }
19
53
  export function createConnection(cfg, adapter) {
20
54
  let socket = null;
21
55
  let heartbeatTimer = null;
@@ -69,8 +103,16 @@ export function createConnection(cfg, adapter) {
69
103
  });
70
104
  let archivedFiles = [];
71
105
  try {
106
+ const httpBase = cfg.server.url.replace(/\/agent$/, '');
107
+ const downloadedAttachments = await downloadAttachments({
108
+ serverUrl: httpBase,
109
+ token: cfg.server.token,
110
+ run,
111
+ attachments: req.attachments,
112
+ });
113
+ const prompt = promptWithAttachments(req.prompt, downloadedAttachments);
72
114
  const rawBody = await adapter.ask({
73
- prompt: req.prompt,
115
+ prompt,
74
116
  history: req.history ?? [],
75
117
  systemPrompt: cfg.adapter.systemPrompt,
76
118
  workspace: cfg.adapter.workspace,
@@ -84,7 +126,6 @@ export function createConnection(cfg, adapter) {
84
126
  finishAgentWorkspaceRun(run, { replyText, files: archivedFiles, status: 'completed' });
85
127
  const artifactIds = [];
86
128
  if (archivedFiles.length > 0) {
87
- const httpBase = cfg.server.url.replace(/\/agent$/, '');
88
129
  for (const file of archivedFiles) {
89
130
  try {
90
131
  const result = await uploadArtifact({
package/dist/scanner.js CHANGED
@@ -4,6 +4,16 @@ import { join } from "node:path";
4
4
  import { createHash } from "node:crypto";
5
5
  import * as os from "node:os";
6
6
  import { logger } from "./log.js";
7
+ function readDaemonVersion() {
8
+ try {
9
+ const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
10
+ return typeof packageJson.version === "string" ? packageJson.version : "unknown";
11
+ }
12
+ catch {
13
+ return "unknown";
14
+ }
15
+ }
16
+ const DAEMON_VERSION = readDaemonVersion();
7
17
  function isExecutableFile(path) {
8
18
  try {
9
19
  return existsSync(path) && statSync(path).isFile();
@@ -374,5 +384,6 @@ export function collectSystemInfo() {
374
384
  totalMemoryGB: Math.round((totalMem / 1024 / 1024 / 1024) * 10) / 10,
375
385
  freeMemoryGB: Math.round((freeMem / 1024 / 1024 / 1024) * 10) / 10,
376
386
  nodeVersion: process.version,
387
+ daemonVersion: DAEMON_VERSION,
377
388
  };
378
389
  }
@@ -49,6 +49,7 @@ export function beginAgentWorkspaceRun(input) {
49
49
  const teamDir = ensureDir(join(rootDir(), 'teams', teamId));
50
50
  const agentDir = ensureDir(join(teamDir, 'agents', agentId));
51
51
  const runDir = ensureDir(join(agentDir, 'runs', runId));
52
+ const inputDir = ensureDir(join(runDir, 'inputs'));
52
53
  const outputDir = ensureDir(join(runDir, 'outputs'));
53
54
  const intermediateDir = ensureDir(join(runDir, 'intermediates'));
54
55
  const logDir = ensureDir(join(runDir, 'logs'));
@@ -72,7 +73,7 @@ export function beginAgentWorkspaceRun(input) {
72
73
  createdAt: new Date().toISOString(),
73
74
  files: [],
74
75
  });
75
- return { teamId: input.teamId, agentId: input.agentId, runId: input.runId, agentDir, runDir, outputDir, intermediateDir, logDir };
76
+ return { teamId: input.teamId, agentId: input.agentId, runId: input.runId, agentDir, runDir, inputDir, outputDir, intermediateDir, logDir };
76
77
  }
77
78
  export function workspaceEnv(run) {
78
79
  return {
@@ -80,6 +81,7 @@ export function workspaceEnv(run) {
80
81
  AGENTBEAN_AGENT_ID: run.agentId,
81
82
  AGENTBEAN_RUN_ID: run.runId,
82
83
  AGENTBEAN_WORKSPACE: run.agentDir,
84
+ AGENTBEAN_INPUT_DIR: run.inputDir,
83
85
  AGENTBEAN_OUTPUT_DIR: run.outputDir,
84
86
  AGENTBEAN_INTERMEDIATE_DIR: run.intermediateDir,
85
87
  AGENT_BEAN_OUTPUT_DIRS: [run.outputDir, run.intermediateDir].join(','),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentbean/daemon",
3
3
  "private": false,
4
- "version": "0.1.12",
4
+ "version": "0.1.14",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "bin": {